Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Алгоритм преобразования монохромного изображения [D7, WinXP]


Страницы:   1 2 3

Штотоносец ©   (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;


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо