![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Алгоритм преобразования монохромного изображения [D7, WinXP]
Штотоносец © (2005-03-11 20:28:00)Не знаю как точно сказать. Вообщем есть монохромное изображение и надо его преобразовать в 2, 4, 8 и т.д. цветное, т.е. градацией серого. Во. Вроде понятно объяснил :)
Fenik (2005-03-15 19:39:00) [1]Блин, да это ж почти тоже самое, что и я первый раз писал "грубый вариант". -))
Fenik (2005-03-15 19:19:00) [2]> Штотоносец
Кстати, я проверил ситуации (b-a*c)>(a/2) никогда не возникает.
Поэтому всё заполнение таблицы сводится к
A := 255/(Value-1);
for X := 0 to 255 do
ByteTable[X] := Round(A * Round(X/A));
Тест я перезакачал. Можешь проверить.
Вот такая оптимизация. :))
Fenik (2005-03-15 18:53:00) [3]Упс, опечаточка, вместо строки -
else ByteTable[X] := Round( X)
нужно
else ByteTable[X] := Round( B)
В тесте тоже исправить.
Это я перед отправкой решил подправить, понимаешь.. :))
Fenik (2005-03-15 17:44:00) [4]> Штотоносец (14.03.05 23:21) [37]
> Остается теперь оптимизацией заняться =)
procedureSetGrayScale(Bitmap: TBitmap; Value: Byte);
varPixel: pRGBTriple;
X, Y: Integer;
A, A_2, B: Double;
ByteTable: array[Byte] ofByte;
begin
Bitmap.PixelFormat := pf24Bit;
ifValue > 1 then begin
A := 255/(Value-1);
A_2 := A/2;
forX := 0 to255 do begin
B := A * Round(X/A);
if(X - B) > A_2
thenByteTable[X] := Round(A*Round(X/(A+1)))
elseByteTable[X] := Round(X);
end;
end
else
FillChar(ByteTable, SizeOf(ByteTable), 0);
forY := 0 toBitmap.Height - 1 do begin
Pixel := Bitmap.ScanLine[Y];
forX := 0 toBitmap.Width - 1 do begin
withPixel^ do begin
rgbtBlue := ByteTable[rgbtBlue];
rgbtGreen := ByteTable[rgbtGreen];
rgbtRed := ByteTable[rgbtRed];
end;
Inc(Pixel);
end;
end;
end;
Тестирование - http://fenik.nm.ru/Gray.zip
Но этот алгоритм не умный, так сказать. Так как - [11].
Возьми например какртинку http://fenik.nm.ru/UGADAY.JPGи установи 3 цвета. В итоге лажа. Нужен умный алгоритм, который будет анализировать распределение цветов и в соответствии с этим подбирать опорные точки.
dmk (2005-03-15 00:52:00) [5]Так черещз блокнот скопируй и шрифт поменяй.
Все читается =)
Штотоносец (2005-03-14 23:55:00) [6]
dmk © (14.03.05 23:49) [38]
Ну а вдруг ему интересно?! =)
Да вообщем интересно, но с такими коментариями, код сложнее воспринимается =)
dmk (2005-03-14 23:49:00) [7]2 Fenik
Я не волшебник, я только учусъ (C) Золушка
Ну а вдруг ему интересно?! =)
Штотоносец (2005-03-14 23:21:00) [8]YurikGL © (14.03.05 21:33) [30]
Вот ты мой код даже не глянул =) я именно это и пытался реализовать. И сейчас я его доделал, и он теперь работает правильно с любым количеством цветности. Вот последний вариант:
procedure Tfrm_Main.GetGrayScale(Value:Integer);
var
i,w,h:integer;
a,c:Real;
b:byte;
begin
w:=0;
h:=0;
a:=255/value;
for i:=1 to Length(ArrByteImg) do begin
b:=ArrByteImg[i-1];
c:=Round(b/a);
if (b-a*c)>(a/2) then c:=Round(b/(a+1));
b:=Round(c*a);
Image2.Canvas.Pixels[w,h]:=b shl 16 + b shl 8 + b;
inc(w);
if w=Image2.Width then begin
w:=0;
inc(h);
end;
end;
end;
Остается теперь оптимизацией заняться =)
Fenik (2005-03-14 22:25:00) [9]>dmk ©
Блин, ну ты даешь. Человеку нужен алгоритм, красивый четкий наглядный код. А не секреты оптимизации графики инструментами встроенного ассемблера. Учится он ещё, понимаешь? =)
dmk (2005-03-14 22:02:00) [10]Procedure ConvertBitmapScLineToRgb(ImgLineAddr:DWord; DBufferLineAddr:DWord; NumberBits:DWord; StartBit:Byte);
var
SrcAddr: DWord;
DestAddr: DWord;
BitsOffs: DWord; //Ñìåùåíèå â ìàññèâå RGBBits
FBitsLen: DWord; //Äëèíà êîïèðóåìîé ÿ÷åéêè
FBitsStart: DWord; //Íà÷àëî â ÿ÷åéêå
FTotalBits: DWord;
FFirstBitsLen: DWord;
FLastBitsLen: DWord;
FBitsForCycle: DWord;
begin
//Àäðåñ ñêàí-ëèíèè â èçîáðàæåíèè
SrcAddr := ImgLineAddr;
//Àäðåñ ñêàí-ëèíèè â êîíå÷íîì áóôåðå
DestAddr := DBufferLineAddr;
//Êîë-âî áèòîâ â ñêàí-ëèíèè
FTotalBits := NumberBits;
//Ðàñ÷åò ñìåùåíèÿ â áàéòå, ò.ê. ïèêñåëü ìîæåò íà÷èíàòñÿ â áàéòå ãäå óãîäíî
FBitsStart := StartBit;
//Äëÿ öèêëà áèòîâ íåò
FBitsForCycle := 0;
//Äëÿ ïîñëåäíåãî áàéòà òîæå íåò
FLastBitsLen := 0;
//Åñëè äëèíà ìåíüøå âîñüìè ïèêñåëîâ(áàéòà)
If FTotalBits < 8 then
begin
//åñëè íà÷àëüíûé áèò íå íóëåâîé
If StartBit 0 then
begin
//Ñ÷èòàåì, ÷òî äëèíà öåïî÷êè â ïåðâîì áàéòå
//ðàâíà îñòàâøèìñÿ áèòàì
FFirstBitsLen := 8 - StartBit;
//Íó à åñëè äëèíà îñòàâøèõñÿ áèòîâ
//ìåíüøå äëèíû âõîäíîé öåïî÷êè
//òî ñáðàñûâàåì äëèíó ïåðâîé öåïî÷êè
//ê âõîäíîìó êîë-âó
If FFirstBitsLen > FTotalBits then FFirstBitsLen := FTotalBits;
//Åñëè êîë-âî áèòîâ â ïåðâîì áàéòå
//ìåíüøå ÷åì âñåãî, òî
//ïðîèñõîäèò ïåðåõîä â ñëåäóþùèé áàéò
If FFirstBitsLen < FTotalBits then
FLastBitsLen := FTotalBits - FFirstBitsLen;
end//If aX 0 then
else
begin
//Äëèíà ïåðâîé öåïî÷êè
FFirstBitsLen := FTotalBits;
end;
end//If FTotalBits < 8
else//Åñëè êîë-âî áèòîâ áîëüøå 8
dmk (2005-03-14 22:02:00) [11]If FBitsForCycle 0 then
begin
While FBitsForCycle 0 do
begin
//Ñìåùåíèå â ìàññèâå RGBBits
asm
mov eax, SrcAddr
mov al, [eax] //×èòàåì ïèêñåë
and eax, $000000FF //Î÷èñòèì ñòàðøèå 3 áàéòà ðåãèñòðà EAX
and al, gvIndexedMask //Îòêëþ÷åí ëè êàíàë? Òåïåðü â AL 8 ïèêñåëîâ èçîáðàæåíèÿ
shl eax, 5 //Óìíîæàåì íà 32, ò.ê. ýëåìåíò ìàññèâà - ýòî 32 áàéòà
mov BitsOffs, eax
end;
MoveBytes(RgbBits + BitsOffs, DestAddr, 32);
Inc(SrcAddr);
Inc(DestAddr,32);
Dec(FBitsForCycle,8);
end;//While
end;
If FLastBitsLen 0 then
begin
//Ñìåùåíèå â ìàññèâå RGBBits
asm
mov eax, SrcAddr
mov al, [eax] //×èòàåì ïèêñåë
and eax, $000000FF //Î÷èñòèì ñòàðøèå 3 áàéòà ðåãèñòðà EAX
and al, gvIndexedMask //Îòêëþ÷åí ëè êàíàë? Òåïåðü â AL 8 ïèêñåëîâ èçîáðàæåíèÿ
shl eax, 5 //Óìíîæàåì íà 32, ò.ê. ýëåìåíò ìàññèâà - ýòî 32 áàéòà
mov BitsOffs, eax
end;
MoveBytes(RgbBits + BitsOffs, DestAddr, (FLastBitsLen shl 2));
end;//If
end;
dmk (2005-03-14 22:02:00) [12]begin
//åñëè íà÷àëüíûé áèò íå íóëåâîé
If StartBit 0 then
begin
//Ñ÷èòàåì, ÷òî äëèíà öåïî÷êè â ïåðâîì áàéòå
//ðàâíà îñòàâøèìñÿ áèòàì
FFirstBitsLen := 8 - StartBit;
//Ïîñìîòðèì. Ñêîëêî æå áèòîâ îñòàëîñü?
Dec(FTotalBits,FFirstBitsLen);
//Åñëè îñòàëîñü 8 èëè ìåíüøå òî
If FTotalBits = 8
If FFirstBitsLen 0 then
begin
//êîë-âî áèòîâ ðàâíî 8 - ïîïðàâêà íà íà÷àëî áèòà
FBitsLen := FFirstBitsLen;
//Ñìåùåíèå â ìàññèâå RGBBits
asm
mov eax, SrcAddr
mov al, [eax] //×èòàåì ïèêñåë
and eax, $000000FF //Î÷èñòèì ñòàðøèå 3 áàéòà ðåãèñòðà EAX
and al, gvIndexedMask //Îòêëþ÷åí ëè êàíàë? Òåïåðü â AL 8 ïèêñåëîâ èçîáðàæåíèÿ
shl eax, 5 //Óìíîæàåì íà 32, ò.ê. ýëåìåíò ìàññèâà - ýòî 32 áàéòà
mov BitsOffs, eax
end;
//Êîïèðóåì RGB áàéòû â êîíå÷íûé áóôåð
MoveBytes(RgbBits + BitsOffs + (FBitsStart shl 2), DestAddr, FBitsLen shl 2);
//Ñìåùàåìñÿ ê ñëåäóþùåìó áàéòó â èçîáðàæåíèè
Inc(SrcAddr);
//Ñìåùàåìñÿ ê ñëåäóþùèì áèòàì â ìàññèâå RGBBits
Inc(DestAddr,FBitsLen shl 2);
end;
dmk (2005-03-14 21:58:00) [13]А если хочешь с RGB работать, что крайне непонятно для меня,
сначала конвертни Bitmap-строку в RGB. Также сначала
создай RGB-представление битов.
//Ïðîöåäóðà ñîçäàåò ìàññèâ îòîáðàæàþùèé áèòû â RGB ïðåäñòàâëåíèè
//Ïðèìåð: (10001111) = ($00000000 $00FFFFFF $00FFFFFF $00FFFFFF $00000000 $00000000 $00000000 $00000000)
//Èñïîëüçóåòñÿ äëÿ ïðîðèñîâêè ïðè áèòíîñòè 1 Bpp
//Ìàññèâ çàíèìàåò 8Êá
Procedure TImageStream.InitRgbMapOfBits;
var
Counter: Byte; //Ñ÷åò÷èê áàéòîâ
bc: Byte; //Ñ÷åò÷èê áèòîâ
begin
For Counter := 0 to 255 do
begin
For bc := 0 to 7 do
If Bit(Counter, bc) then
FRGBBitsArray[Counter].Cell[7 - bc] := clWhite else
FRGBBitsArray[Counter].Cell[7 - bc] := clBlack;
end;
//Àäðåñ íà÷àëà ìàññèâà
FRGBBitsAddr := DWord(Addr(FRGBBitsArray[0].Cell[0]));
RgbBits := FRgbBitsAddr;
end;
Fenik (2005-03-14 21:40:00) [14]>YurikGL © (14.03.05 21:33) [30]
>Я в [11] пример привел, который методом деления "в лоб" не решается.
Я понимаю и полностью поддерживаю.
Поэтому и написал "грубый вариант". Просто как пример.
Fenik (2005-03-14 21:33:00) [15]Ну всё, завалили парня кодом :)))
YurikGL (2005-03-14 21:33:00) [16]
> Fenik (12.03.05 19:38) [22]
Я в [11] пример привел, который методом деления "в лоб" не решается.
Автору:
Посмотри http://www.megaputer.ru/content/production/pa/_polyanalyst_info.html#5_05
Алгоритм кластеризации в твоем случае, примерно такой:
Пусть нам надо представить все в 4-х цветах. Тогда, делим всю область пополам т.е. цвета меньше 127,5 и больше 127,5 т.е. граница между двумя кластерами будет 127.5. Далее сдвигаем эту границу в сторону с большим количеством точек до тех пор, когда точек с обоих сторон не станет поровну. Далее, каждый получившийся кластер делим аналогичным способом пополам. После этого у нас в каждом из 4-х кластеров поровну точек (или почти поровну). Берем за новые цвета средние арифметические значений точек в каждом кластере и заменяем точки каждого кластера полученным значением.
Конечно, будут и пограничные значения и разное количество точек в кластерах, но общий алгоритм может быть такой. Ситуацию [11] он должен обработать корректно. Хотя существуют ситуации, которые и этот алгоритм обработает некорректно. Например значения
1,1,1,1,1,1, 200,201,202. разделить на 4 цвета. Для учета таких ситуаций надо еще много чего считать.
Хотя в университете нам вроде давали гораздо более оптимальные алогоритмы, но это надо в лекциях рыться :-)
dmk (2005-03-14 21:23:00) [17]Вот тебе еще немного кода. Преобразование BiLevel в Grayscale.
Преобразует 8 BiLevel-строк в 1 Grayscale.
Кстати! Потрясная скорость!
Только для этого надо сначала подсчитать
кол-во бит в кубике 8х8 и занести их в массив из
которого эта процедура выборку делает.
Я делаю это в секции Initialization
function Bit(TestByte, TestBit:Integer):boolean;
asm
bt eax, edx
setc al
and eax, 0FFh
end;
Function BitsCount(v:byte):byte;
var
c: Byte;
res: Integer;
begin
Res := 0;
For c := 0 to 7 do If Bit(v,c) then Inc(Res, 32);
If Res > 255 then Res := 255;
Result := Byte(Res);
end;
procedure InitHalphtoneBits;
var
Counter:Byte;
begin
For Counter := 0 to 255 do HBitsArray[Counter] := BitsCount(Counter);
HBitsArray[0] := 0;
HBitsArray[255] := 255;
HBitsArrayAddr := DWord(Addr(HBitsArray[0])); //Ìàññèâ ïîäñ÷èòàííûõ çíà÷åíèé
end;
procedure TImageStream.HalftoneLine(InputAddress, OutputAddress, Len:DWord);
var
FStart: DWord;
FPos: DWord;
FNewStart: DWord;
FNewPos: DWord;
//...
FLineSize: DWord;
FLinePos: DWord;
//...
FSum: DWord;
FHMem: DWord;
begin
FStart := InputAddress;
FHMem := HBitsArrayAddr; //Ìàññèâ ïîäñ÷èòàííûõ çíà÷åíèé
FLineSize := Len;
//...
FNewStart := OutputAddress;
FNewPos := 0;
FPos := 0;
//...
asm
push edi
push esi
push ebx
mov edx, FStart {Íà÷àëüíûé àäðåñ â bitmap èçîáðàæåíèè}
add edx, FPos {Ïîçèöèÿ â bitmap èçîáðàæåíèè}
mov edi, FNewStart {Íà÷àëüíûé àäðåñ â grayscale èçîáðàæåíèè}
add edi, FNewPos {Ïîçèöèÿ â grayscale èçîáðàæåíèè}
mov esi, FHMem {Ñòàðòîâûé àäðåñ â ìàññèâå ïîäñ÷èòàííûõ çíà÷åíèé}
mov ecx, FLineSize {äëèíà ñêàí-ëèíèè}
mov ebx, ecx
mov FLinePos, 0
@@Cyc: mov FSum, 0
xor eax, eax
{0}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx, ebx
{1}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{2}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{3}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{4}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{5}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{6}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
add edx,ebx
{7}mov al, [edx]
mov al, [esi+eax]
add FSum, eax
mov eax, FSum
shr ax, 3
mov [edi], al //Çàïèñü ñóììû â grayscale èçîáðàæåíèå
inc edi
mov edx, FStart //Èñõîäíûé àäðåñ
add edx, FPos
inc FLinePos
add edx, FLinePos
loop @@Cyc
//...
pop ebx
pop esi
pop edi
end;
end;
Fenik (2005-03-14 19:34:00) [18]Прости, не хотел обидеть. Там вообще-то смайлы.. :)
Вместо Canvas.Pixels целесообразней использовать ScanLine. Это первое. (Выигрыш в скорости офигенный)
А второе - это че за лабуда? :))))) Шутю.
Ты можешь объяснить, зачем тебе понадобилсся какой-то массив пикселей ArrByteImg?
Вообще прокомментируй свой код.
Потом посмотри внимательно на мой и подумай, что в нем тебе не понятно. Короче - смотри, думай и спрашивай.
Штотоносец (2005-03-14 19:24:00) [19]Fenik [25]
Послушай, я не лабы пишу и мне завтра это не здавать, я для себя пишу, так сказать для общего развития, поэтому хочеться самому =)
Если хочешь помочь, так лучше укажи на ошибки, объясни, если не влом, а то; "Это че за лабуда?". Ты типа лох, я тебе все дал, а ты типа, даже просто код скопировать не можешь. Так можно инерпретировать твои слова. А это обидно.
Fenik (2005-03-14 19:13:00) [20]Это че за лабуда? :))
Я тебе процедуру дал? Дал.
Ты скорость работы сравнивал со своим произведением? Сомневаюсь.
Короче, не парься и возьми мою функцию. Правь в ней первоначальное заполнение массива по своему усмотрению и всё. Для двух цветов там как тебе надо работает.
Не забудь только Bitmap.PixelFormat := pf24Bit;