Programowanie w języku Delphi » FAQ

Jak porównać dwie bitmapy

function GetDifference(Img1, Img2:TImage; var XStart, XStop, YStart, YStrop:integer):boolean;
var
  Pixel1:^TrgbTriple;
  Pixel2:^TrgbTriple;
  x, y:integer;
  TheSame:boolean;
begin
  Img1.Picture.Bitmap.PixelFormat:=pf24bit;
  Img2.Picture.Bitmap.PixelFormat:=pf24bit;
  TheSame:=true;
  for y:=0 to Img1.Picture.Bitmap.Height -1  do
  begin
    Pixel1:=Img1.Picture.Bitmap.ScanLine[y];
    Pixel2:=Img2.Picture.Bitmap.ScanLine[y];
 
    for x:=0 to Img1.Picture.Bitmap.Width-1 do
    begin
      if  (Pixel1.RgbtRed<>Pixel2.RgbtRed) or
          (Pixel1.RgbtGreen<>Pixel2.RgbtGreen)or
          (Pixel1.RgbtBlue<>Pixel2.RgbtBlue) then
      begin
        if TheSame or (XStart>X) then XStart:=X;
        if TheSame or (XStop<X) then XStop:=X ;
        if TheSame or (YStart>Y) then YStart:=Y;
        if TheSame or (YStrop<Y) then YStrop:=Y;
        TheSame:=false;
      end;
 
      inc(Pixel1);
      inc(Pixel2);
    end;
    Result:=not TheSame;
  end;
end;

Kod pochodzi autorstwa Piotrkadp, pochodzący z tego wątku na forum. I jeszcze wersja dla C++Builder przetłumaczona przez AklimX-a:

bool GetDifference(TImage Img1, TImage Img2, int &XStart, int &XStop, int &YStart, int &YStrop)
{
    TrgbTriple* Pixel1, *Pixel2;
    int x, y;
    bool TheSame;
 
    Img1->Picture->Bitmap->PixelFormat = pf24bit;
    Img2->Picture->Bitmap->PixelFormat = pf24bit;
    TheSame = true;
 
    for(y=0; y<Img1->Picture->Bitmap->Height; ++y)
    {
        Pixel1 =Img1->Picture->Bitmap->ScanLine[y];
        Pixel2 =Img2->Picture->Bitmap->ScanLine[y];
 
        for( x=0; x<Img1->Picture->Bitmap->Width; ++x)
        {
 
            if( (Pixel1->RgbtRed!=Pixel2->RgbtRed) ||
                (Pixel1->gbtGreen!=Pixel2->RgbtGreen)||
                (Pixel1->RgbtBlue!=Pixel2->RgbtBlue) )
            {
 
            if ((TheSame) || (XStart>X)) XStart=X;
            if ((TheSame) || (XStop <X)) XStop =X;
            if ((TheSame) || (YStart>Y)) YStart=Y;
            if ((TheSame) || (YStrop<Y)) YStrop=Y;
            TheSame=false;
            }
        ++Pixel1;
        ++Pixel2;
        }
    }
    return !TheSame;
}

5 komentarzy

_Paweł_ 2008-01-09 22:05

Jesem kompletnym laikiem jeśli chodzi mi o programowanie. Potrzebny jest mi jednak program wykrywający zmiany na monitorze (np. odświeżenie i zmiana zawartości strony www). Nie wiem jak to zrobić- możnaby się pokusić (o ile nie ma prostszego rozwiązania) tworzenie co pewien czas screenów, i późniejsze porównywanie ich jako bitmapy (ale niestety nawet nie umiem wykorzystać podanego kodu). Prosiłbym o szczegółową pomoc. Z góry dziękuję

_Paweł_ 2008-01-09 22:02

Jesem kompletnym laikiem jeśli chodzi mi o programowanie. Potrzebny jest mi jednak program wykrywający zmiany na monitorze (np. odświeżenie i zmiana zawartości strony www). Nie wiem jak to zrobić- możnaby się pokusić (o ile nie ma prostszego rozwiązania) tworzenie co pewien czas screenów, i późniejsze porównywanie ich jako bitmapy (ale niestety nawet nie umiem wykorzystać podanego kodu). Prosiłbym o szczegółową pomoc. Z góry dziękuję

Szczawik 2007-09-28 15:40

Przyznam, że dziwi mnie to. Przecież porównanie czy zwiększanie (adresów) trwa tyle samo dla danych 1 i 4 bajtowych, a w drugim jest po prostu więcej operacji.. Na pierwszy rzut oka - bez sensu.

Piotrekdp 2007-09-28 12:28

napisałem procedury tak :

function GetDifference(const Bitmap1, Bitmap2:TBitmap; var XStart, XStop, YStart, YStrop:integer):boolean;
var
  Pixel1,Pixel2:^DWORD;
  x, y:integer;
  TheSame:boolean;
begin
 Bitmap1.PixelFormat:=pf32bit;
 Bitmap2.PixelFormat:=pf32bit;
  TheSame:=true;
  for y:=0 to Bitmap1.Height -1  do
  begin
    Pixel1:=Bitmap1.ScanLine[y];
    Pixel2:=Bitmap2.ScanLine[y];
 
    for x:=0 to Bitmap1.Width-1 do
    begin
      if  (Pixel1^<>Pixel2^) then
      begin
        if TheSame or (XStart>X) then XStart:=X;
        if TheSame or (XStop<X) then XStop:=X ;
        if TheSame or (YStart>Y) then YStart:=Y;
        if TheSame or (YStrop<Y) then YStrop:=Y;
        TheSame:=false;
      end;
      inc(Pixel1);
      inc(Pixel2);
    end;
    Result:=not TheSame;
  end;
end;
 
 
function GetDifference2(const Bitmap1, Bitmap2:TBitmap; var XStart, XStop, YStart, YStrop:integer):boolean;
var
  Pixel1,Pixel2:^TRGBQuad;
  x, y:integer;
  TheSame:boolean;
begin
  Bitmap1.PixelFormat:=pf32bit;
  Bitmap2.PixelFormat:=pf32bit;
  TheSame:=true;
  for y:=0 to Bitmap1.Height -1  do
  begin
    Pixel1:=Bitmap1.ScanLine[y];
    Pixel2:=Bitmap2.ScanLine[y];
 
    for x:=0 to Bitmap2.Width-1 do
    begin
      if  (Pixel1.RgbRed<>Pixel2.RgbRed) or
          (Pixel1.RgbGreen<>Pixel2.RgbGreen)or
          (Pixel1.RgbBlue<>Pixel2.RgbBlue) or
          (Pixel1.rgbReserved<>Pixel2.rgbReserved) then
      begin
        if TheSame or (XStart>X) then XStart:=X;
        if TheSame or (XStop<X) then XStop:=X ;
        if TheSame or (YStart>Y) then YStart:=Y;
        if TheSame or (YStrop<Y) then YStrop:=Y;
        TheSame:=false;
      end;
      inc(Pixel1);
      inc(Pixel2);
    end;
    Result:=not TheSame;
  end;
end;

Właczyłem test na wykonanie  10 000 razy na Bitmapie o wymiarach
500 X 500 pixeli no i procedura 2  z or ' ami okazała się szybsza za każdym razem o jakieś 1,7 sekundy
 test przeprowadzany 20 krotnie na
Komputerze
 AMD Athlon(tm) XP 1800+
1.50 GHz, 256 MB RAM System 32 bitowy

Do postu Szczawik'a:
  też mnie to zdziwiło ale być może czytając jako DWORD procesor porównuje wszystkie 3 bajty natomiast w or'owych wyskakuje dalej zaraz po pierwszym bajcie niezgodnym i może stąd ta roznica  ale to tylko być może :) nie wiem nie znam się , nie orientuje się :)

Szczawik 2007-09-27 13:32

Po pierwsze ustawienie 32bit pozwoliłoby na wykonanie jednego porównania na DWORD zamiast trzech z'or'owanych porównań na byte (po to w ogóle stworzono bmp 32bit, zanim jeszcze wprowadzono kanał alpha). Trzeba jednak pamiętać, że w tym wszystkim najbardziej czasochłonną operacją nie jest znalezienie różnic, ale zmiana głębi kolorów. Gdy bitmapy są już w głębi docelowej, wtedy można porównywać szybkość.

Pod drugie, skoro znamy kolejność przechodzenia w osi Y (tu rosnąco), to przy wykryciu różnicy nie trzeba sprawdzać, czy (StopY<Y), bo zawsze jest co najwyżej równe.

Przy odliczaniu pętlą w dół po obu osiach i skracając operacje w pętli mamy większe szanse na objęcie kodu przez cache procesora, a zatem być może nawet większy zysk szybkości.