Перетворення векторного у векторний

Таке перетворення не є надто складним, але можливе виникнення двох проблем. Перша, пов‘язана з відмінностями у кількості та типі об‘єктів або їх параметрів. Наприклад, деякі формати підтримують тільки прості елементи зображення, типу кола, або прямокутників. Потужні формати можуть підтримувати набагато більше додаткових складних елементів (наприклад градієнти, сплайни, шрифти, криві Безьє). Друга проблема виникає через те, що різною може бути система виміру, її точність, тощо.

Перетворення метафайлів у метафайли

Оскільки метафайли містять растрові та векторні дані, при їх перетворенні виникають такі ж проблеми які виникають при перетворенні растрового у растровий та векторного у векторний. (див. два попередніх пункти).

Перетворення векторного чи метафайлового у растровий

Таке перетворення називають раструванням. Суть полягає у тому, що вихідне зображення розбивається на рядки та стовпчики (з заданою щільністю у dpi), що утворюють пікселі, які залишається записати в результуючому (растровому) форматі. При цьому, може виникнути ефект сходинок на похилих лініях та дугах, який можна приховати за допомогою невеличкого розмиття (anti-aliasing).

Перетворення растрового та метафайлового у векторний

Перетворення растрового та метафайлового у векторний є дуже складною операцією. Не завжди можна досягти гарних результатів, тому що при такому перетворенні використовуються складні алгоритми обробки зображень, еврістичні методи пошуку та виділення в однорідних растрових даних ліній, прямокутників, градієнтів, тощо. Ще однією проблемою проблемою є точна передача кольорів. Растрові дані, як правило, повнокольорові (24 bit), а векторні підтримують лише декілька кольорів, що може призвести до втрати кольорів у результуючому забраженні.

 Найпоширенішими програмами, що можуть робити подібне перетворення, є Adobe Streamline та Corel Trace.

Перетворення растрового та векторного у метафайл

Таке перетворення не мусить викликати багато проблем, тому що метафайли добре підтримують растрові та векторні дані. Але формат зберігання растрових чи векторних даних може бути різним, тому виникають такі ж проблеми як і при перетворенні растрового у растровий та векторного у векторний.

Приклад програми перетворення

Розглянемо перетворення двох растрових форматів на конкретному прикладі створення програми для перетворення графічного зображення з формату PCX на Microsoft BMP третьої версії. Уся обробка графічних файлів була написана на мові Паскаль з нуля, без використання бібліотек, які в великій кількості можна знайти в Інтернет. На вході програми – файл у форматі PCX, на виході – BMP. Основний принцип роботи програми такий: зчитується заголовок файлу PCX, на його основі створюється заголовок файлу BMP, зчитуються дані кожного пікселя і записуються у формат BMP. Через те, що у форматі BMP рядки даних йдуть знизу догори (у PCX та інших форматах – навпаки), то спочатку записується тимчасовий файл з перевернутим зображенням, а потім цей тимчасовий файл перевертається, щоб отримати нормальний файл BMP, який можна відкривати й використовувати в інших програмах. Оскільки при написанні програми єдиною метою було зрозуміти головний принцип перетворення, то створена програма не є універсальною. Тому вона працює тільки з попередньо заданими іменами файлів і перетворює файли лише з 24 бітами на піксель. Інтерфейс програми вибрано найпростішим для того, щоб концентрувати увагу на самому перетворенні, а не реалізації інтерфейсу. Також, не було приділено уваги анлізу помилок. Розглянемо формати файлів PCX та BMP докладніше, перш ніж перейти до аналізу самої програми.

Формат PCX

PCX – один з форматів, що часто використовується, через свою простоту. Спочатку він був розроблений фірмою Zsoft для використання у програмі PC Paintbrush для MS-DOS. Дані зображення кодуються за допомогою одного з варіантів алгоритму RLE, який незважаючи на простоту та швидкісь, не є ефективним. Цей формат може містити зображення з 4, 8 та 24 бітами на піксель. Далі ми будемо розглядати особливості 24 бітового формату.

Перші 128 байтів файлу містять заголовок:

 

Typedef struct _PcxHeader {  
BYTE Id; Ідентикатор PCX (0Ah)
BYTE Version; Версія
BYTE Format; Кодування (1 - RLE)
BYTE BitsPixelPlane; Кількість бітів на піксель
WORD Xmin; Ліва мінімальна координата
WORD Ymin; Верхня мінімальна координата
WORD Xmax; Права максимальна координата
WORD Ymax; Нижня максимальна координата
WORD Hdpi; Горизонтальна роздільна здатність
WORD Vdpi; Вертикальна роздільна здатність
BYTE EgaPalette[48]; Палітра
BYTE Reserved; Зарезервовано
BYTE NumberOfPlanes; Кількість площин
WORD BytesLinePlane; Кількість байтів на рядок
WORD PaletteInfo; Тип палітри
WORD HScreenSize; Розмір екрану по горизонталі
WORD VScreenSize; Розмір екрану по вертикалі
BYTE Filler[54]; Зарезервовано
} PCXHEADER;  

 

Поле Id містить 0Ah, це означає, що даний файл направді є файлом формату PCX. Натепер підтримується лише один тип кодування – RLE, якому відповідає значення 1. Назви інших полів говорять самі за себе.

Дані у форматі PCX стискаються за допомогою простого алгоритму групового кодування, у якому група байтів, що повторюються, замінюється двома байтами: кількістю значень та самим значенням. Перший байт цієї двобайтової групи містить у двох старших бітах по одиниці, а у шести менших – кількість значень, які повторюються, що визначає максимальну довжину – 63 пікселя. Другий байт містить восьмибітове значення, що на виході повторюється стільки разів, скільки було задано у молодших шести бітах першого байту. Далі йде наступна пара кількість/значення. Якщо старші два біти першого байту нулі, то цей байт містить саме значення, а кількість повторень вважається 1. Отримані дані кодують колір послідовних пікселів, по три на піксель у форматі RGB. Але дані записуються не триплетами, а спочатку усі червоні компоненти для одного рядка растру, потім так само зелені, а потім сині. При цьому палітра не використовується.

Формат BMP

Формат BMP був створений компанією Microsoft спеціально для зберігання растрових даних у системі Windows. Дані заголовку та й його розмір змінювалися й доповнювалися з випуском нових версій цієї операційної системи. Ми будемо розглядати формат BMP версії три. Перший 14-байтовий заголовок однаковий для усіх версій:

 

Typedef struct BITMAPFILEHEADER {  
UINT16 type; Тип файлу – 4D42h ("BM")
UINT32 size; Розмір файлу в байтах
INT16 xHotspot; Резерв
INT16 yHotspot; Резерв
UINT32 offsetToBits; Початок даних зображення
} BITMAPFILEHEADER;  

 

У версії три далі йде 40-байтовий додатковий заголовок:

 

Typedef struct BITMAPHEADER {  
UINT32 size; Розмір цього заголовку у байтах
INT32 width; Ширина зображення в пікселях
INT32 height; Висота зображення в пікселях
UINT16 numBitPlanes; Кількість площин
UINT16 numBitsPerPlane; Кількість бітів на піксель
UINT32 compressionScheme; Тип стиснення
UINT32 sizeOfImageData; Розмір растру у байтах
UINT32 xResolution; Горизонтальна роздільна здатність у пікселях на метр
UINT32 yResolution; Вертикальна роздільна здатність у пікселях на метр
UINT32 numColorsUsed; Кількість кольорів у зображенні
UINT32 numImportantColors; Кількість важливих кольорів
}  

 

Після заголовків йдуть дані 24-бітового зображення у форматі RGB, але на відміну від PCX, площини не використовуються і триплети кольорів йдуть один за одним.

Опис програми

Розглянемо програму перетворення растрового зображення з формату PCX у BMP рядок за рядком. Повний лістінг програми див. у додатку 1.

uses
crt;

Додатковий стандартний модуль crt використовується для позиціонування текстового курсора та очищення рядку.

Далі визначаються змінні:

var
PCXF, BMPF: file;

Нетипізовані файли для зчитування та запису інформації.

PCXheader: array [0..127] of byte;

Масив для зберігання заголовку PCX файлу.

BMPheader: array [0..53] of byte;

Масив для зберігання заголовку BMP файлу.

i, result, curx, HGT: integer;

Змінна циклу, результат зчитування та запису файлів і умова виходу з циклу, вказівник на поточний елемент у масиві buf, висота зображення у рядках (або пікселах).

buf: array [0..1959] of byte;

Масив для зберігання даних одного рядка. Максимальне значення індексу масиву вибрано довільно, але так, щоб можна було перетворити тестовий файл.

b, val: byte;

Дуплети даних формату PCX запакованого за алгоритмом RLE.

size, pos1, BPL: longint;

Рахівник кількості перетворених пікселів, кількості рядків, що були перевернуті з тимчасового файлу перевернутого BMP та кількість пікселів на рядок. BPL*3 – реальний об‘єм потрібного буфера для зберігання одного рядку.

Begin
assign(PCXF, 'dollar.pcx');
reset(PCXF, 1);
assign(BMPF, 'dollar.tmp');
rewrite(BMPF, 1);

Відкриваються два файли – для зчитування з PCX та запису у TMP.

blockread(PCXF, PCXheader, 128);

Зчитується заголовок PCX в масив PCXheader розміром 128 байт:

for i:=0 to 53 do begin
BMPheader[i]:=0;
end;

Майже всі дані новоствореного заголовку BMP міститимуть нулі.

writeln('Width: ', PCXheader[9]*256+PCXheader[8]+1);
HGT:= PCXheader[11]*256+PCXheader[10]+1;
writeln('Height: ', HGT);

На екран виводяться ширина та висота зображення, взяті з заголовку PCX файлу, при чому висота нам ще знадобиться. Формула використовується для перетворення двох байтових значень у одне слово.

BPL:= PCXheader[67]*256+PCXheader[66];

Визначається кількість байтів на рядок. Далі створюється заголовок формату BMP, поки що в пам‘яті.

BMPheader[0]:= $42; {header word}
BMPheader[1]:= $4D;

Заголовок всього формату BMP – "BM".

BMPheader[10]:= 54;

Початок даних – 14 байтів заголовку №1 + 40 байтів заголовку №2.

BMPheader[14]:= 40; {header size}

Розмір заголовку 2 та одночасно ідентифікатор версії – три.

BMPheader[18]:= PCXheader[8]+1;
BMPheader[19]:= PCXheader[9];
BMPheader[22]:= PCXheader[10]+1;
BMPheader[23]:= PCXheader[11];

Записується ширина та висота зображення.

BMPheader[26]:= 1; {planes}

Кількість площин.

BMPheader[28]:= 24; {bpp}

Бітів на піксель.

blockwrite(BMPF, BMPheader, 54);

Створенй заголовок записується на диск.

result:=1;
size:=0;
curx:=0;
while result=1 do begin

Ініціалізуються рахівники, та змінна виходу з циклу, при закінченні даних файла.

blockread(PCXF, b, 1, result);

Читається один байт, причому, якщо зчитування неможливе (кінець файлу), result прийме значення нуль, і виконається умова виходу з циклу.

if (b and $C0) = $C0 then begin
b:=b and $3F;
    blockread(PCXF, val, 1, result);
end else begin
val:=b;
b:=1;
end;

Стандартний алгоритм розпаковки RLE-закодованих даних формату PCX. Тут обробляється зчитаний байт та якщо потрібно зчитується наступний, і на виході маємо b – кількість повторів, val – значення, яке потрібно повторити.

gotoxy(1, wherey); write(size); clreol; inc(size);

Для індикаціі того, що програма щось робить, а не зависла, на екран виводиться кількість вже перетворених пікселів, яка (кількість) після цього інкрементується.

for i:=1 to b do begin
buf[curx]:=val;
inc(curx);
end;

Відбувається запис у буфер значення b val разів.

if (curx=BPL*3) then begin
for i:=0 to BPL-1 do begin
   blockwrite(BMPF, buf[i+2*BPL], 1);
   blockwrite(BMPF, buf[i+BPL], 1);
   blockwrite(BMPF, buf[i+000], 1);
end;
curx:=0;
end;

Коли ми зчитали увесь рядок у буфер, час його записати. Але через те, що RGB дані у PCX форматі розбиті на площини, запис йде з трьох точок буферу, що поступово зміщуються. Зауважте обернений формат запису триплетів у форматі BMP, спочатку йде B, потім G, а потім R. У кінці ми переводимо вказівник знову на початок буферу.

end;
writeln;
close(PCXF);
close(BMPF);

Закінчили читати файл й записувати тимчасовий.

Починаємо перевертання тимчасового файлу у нормальний формат BMP.

assign(PCXF, 'dollar.tmp');
reset(PCXF, 1);
assign(BMPF, 'dollar.bmp');
rewrite(BMPF, 1);

Спочатку, як завжди, відкриємо файли. Врахуйте, що тимчасовий файл відкривається змінною, що раніше відповідала PCX файлу. Так зроблено для мінімізації кількості змінних.

blockread(PCXF, BMPheader, 54);
blockwrite(BMPF, BMPheader, 54);

Зчитаємо й запишемо заголовок без змін. Будемо зчитувати файл по рядкам з кінця, а записуватимемо з початку.

for i:=1 to HGT do begin

Як не дивно, зчитувати рядки потрібно стільки раз, скільки у нас рядків.

pos1:= filesize(PCXF)-(BPL*3*i);

Визначається позиція кожного рядку у файлі: від кінця файлу віднімаємо i рядків довжиною BPL*3 (*3 тому, що RGB).

gotoxy(1, wherey); write(pos1); clreol;

Просто виводимо цю позицію на екран.

seek(PCXF, pos1);
blockread(PCXF, buf, BPL*3);
blockwrite(BMPF, buf, BPL*3);
end;

Переходимо до визначеной позиції, зчитуємо і записуємо рядок зображення.

close(PCXF);
close(BMPF);
writeln;
end.

У кінці бажано позакривати все, що відкривали. Можна було б також витерти тимчасовий файл.















































Висновки

Завданням даної роботи було дослідження основних принципів перетворення форматів графічних файлів. У роботі наведено різні класифікації графічних форматів. Розглянуто можливі галузі застосування цих форматів, як то: Internet, друковані видання, електроні презентації. Також наведено детальний опис усіх можливих варіантів перетворення одного формату в інший, зазначено типові проблеми, які при цьому виникають. Крім того створено програму перетворення для двох конкретних растрових форматів. Під час роботи над програмою ретельно досліджено будову форматів PCX та BMP, описано алгоритм їхнього перетворення, пояснено реалізацію алгоритму на мові Pascal.

Література

1. Д. Мюррей, У. Райпер "Энциклопедия форматов графических файлов", BHV, Киев, 1997.

2. Р. Водески "Графика для Web", Диалектика, Киев, 1998.

3. ftp://ftp.mv.com/pub/ddj/1994/1194.09/bmp.zip

4. ftp://x2ftp.oulu.fi/pub/msdos/programming/formats

5. ftp://telva.ccu.uniovi.es/pub/graphics/file.formats

6. Журнал "ЧИП", 9/97, стр. 58, "Графические форматы BMP, GIF, JPEG, PNG и FLASHPIX"

 

Додаток 1

Повний текст програми перетворення.

Uses
crt;
var
PCXF, BMPF: file;
PCXheader: array [0..127] of byte;
BMPheader: array [0..53] of byte;
i, result, curx, HGT: integer;
buf: array [0..1959] of byte;
b, val: byte;
size, pos1, BPL: longint;
begin
assign(PCXF, 'dollar.pcx');
reset(PCXF, 1);
assign(BMPF, 'dollar.tmp');
rewrite(BMPF, 1);

blockread(PCXF, PCXheader, 128);
for i:=0 to 53 do begin
BMPheader[i]:=0;
end;

writeln('W: ', PCXheader[9]*256+PCXheader[8]+1);
HGT:= PCXheader[11]*256+PCXheader[10]+1;
writeln('H: ', HGT);
BPL:= PCXheader[67]*256+PCXheader[66];

BMPheader[0]:= $42; {header word}
BMPheader[1]:= $4D;
BMPheader[10]:= 54;
BMPheader[14]:= 40; {header size}
BMPheader[18]:= PCXheader[8]+1;
BMPheader[19]:= PCXheader[9];
BMPheader[22]:= PCXheader[10]+1;
BMPheader[23]:= PCXheader[11];
BMPheader[26]:= 1; {planes}
BMPheader[28]:= 24; {bpp}
blockwrite(BMPF, BMPheader, 54);
result:=1;
size:=0;
curx:=0;
while result=1 do begin
blockread(PCXF, b, 1, result);
if (b and $C0) = $C0 then begin
   b:=b and $3F;
blockread(PCXF, val, 1, result);
end else begin
val:=b;
b:=1;
end;
gotoxy(1, wherey); write(size); clreol; inc(size);
for i:=1 to b do begin
buf[curx]:=val;
inc(curx);
end;
if (curx=BPL*3) then begin
for i:=0 to BPL-1 do begin
   blockwrite(BMPF, buf[i+2*BPL], 1);
   blockwrite(BMPF, buf[i+BPL], 1);
   blockwrite(BMPF, buf[i+000], 1);
end;
curx:=0;
end;
end;
writeln;
close(PCXF);
close(BMPF);

assign(PCXF, 'dollar.tmp');
reset(PCXF, 1);
assign(BMPF, 'dollar.bmp');
rewrite(BMPF, 1);
blockread(PCXF, BMPheader, 54);
blockwrite(BMPF, BMPheader, 54);
for i:=1 to HGT do begin
pos1:= filesize(PCXF)-(BPL*3*i);
gotoxy(1, wherey); write(pos1); clreol;
seek(PCXF, pos1);
blockread(PCXF, buf, BPL*3);
blockwrite(BMPF, buf, BPL*3);
end;
close(PCXF);
close(BMPF);
writeln;
end.

 













































































Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: