[Delphi] obsahuje Bitmapa jinou Bitmapu ??

C++, C#, Visual Basic, Delphi, Perl a ostatní

Moderátor: Moderátoři Živě.cz

Odeslat příspěvekod dyžon 10. 2. 2017 14:39

Zdravím,
potřeboval bych prosím nějakou rychlou funkcičku, která by mi zjistila,
jestli jedna velká Bitmapa obsahuje jinou malou Bitmapu(20 x 30 px).
našel jsem krásny řešení, ale bohužel je to v C.
Kód: Vybrat vše
private Rectangle searchBitmap(Bitmap smallBmp, Bitmap bigBmp, double tolerance)
{
    BitmapData smallData =
      smallBmp.LockBits(new Rectangle(0, 0, smallBmp.Width, smallBmp.Height),
               System.Drawing.Imaging.ImageLockMode.ReadOnly,
               System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    BitmapData bigData =
      bigBmp.LockBits(new Rectangle(0, 0, bigBmp.Width, bigBmp.Height),
               System.Drawing.Imaging.ImageLockMode.ReadOnly,
               System.Drawing.Imaging.PixelFormat.Format24bppRgb);

    int smallStride = smallData.Stride;
    int bigStride = bigData.Stride;

    int bigWidth = bigBmp.Width;
    int bigHeight = bigBmp.Height - smallBmp.Height + 1;
    int smallWidth = smallBmp.Width * 3;
    int smallHeight = smallBmp.Height;

    Rectangle location = Rectangle.Empty;
    int margin = Convert.ToInt32(255.0 * tolerance);

    unsafe
    {
        byte* pSmall = (byte*)(void*)smallData.Scan0;
        byte* pBig = (byte*)(void*)bigData.Scan0;

        int smallOffset = smallStride - smallBmp.Width * 3;
        int bigOffset = bigStride - bigBmp.Width * 3;

        bool matchFound = true;

        for (int y = 0; y < bigHeight; y++)
        {
            for (int x = 0; x < bigWidth; x++)
            {
                byte* pBigBackup = pBig;
                byte* pSmallBackup = pSmall;

                //Look for the small picture.
                for (int i = 0; i < smallHeight; i++)
                {
                    int j = 0;
                    matchFound = true;
                    for (j = 0; j < smallWidth; j++)
                    {
                        //With tolerance: pSmall value should be between margins.
                        int inf = pBig[0] - margin;
                        int sup = pBig[0] + margin;
                        if (sup < pSmall[0] || inf > pSmall[0])
                        {
                            matchFound = false;
                            break;
                        }

                        pBig++;
                        pSmall++;
                    }

                    if (!matchFound) break;

                    //We restore the pointers.
                    pSmall = pSmallBackup;
                    pBig = pBigBackup;

                    //Next rows of the small and big pictures.
                    pSmall += smallStride * (1 + i);
                    pBig += bigStride * (1 + i);
                }

                //If match found, we return.
                if (matchFound)
                {
                    location.X = x;
                    location.Y = y;
                    location.Width = smallBmp.Width;
                    location.Height = smallBmp.Height;
                    break;
                }
                //If no match found, we restore the pointers and continue.
                else
                {
                    pBig = pBigBackup;
                    pSmall = pSmallBackup;
                    pBig += 3;
                }
            }

            if (matchFound) break;

            pBig += bigOffset;
        }
    }

    bigBmp.UnlockBits(bigData);
    smallBmp.UnlockBits(smallData);

    return location;
}

dokázal by to prosím někdo přepsat pro Delphi ??
nebo mi pomoct s napsáním vlastní ?
napadá mě jen jít ve velké Bitmapě pixel po pixelu, vždycky zkopírovat RecTangle (20 x 30 px) a porovat ho s malou Bitmapou.
ale to je podle mě strašně neohrabany a navíc na dlouho.
já bych potřeboval něco, co bude maximálně rychly.
děkuji za nápady.
AMD FX-6300; Gigabyte 970A-DS3P; DDR3 8192MBytes; AMD Radeon HD 6700 Series
dyžon
Junior
Uživatelský avatar

Odeslat příspěvekod satikcz 10. 2. 2017 15:28

V podstatě je to tak, v tý velký jedeš pixel po pixelu a porovnáváš odpovídající pixely z tý malý (nic nekopíruješ).

Jakmile narazíš na rozdílnej pixel, hned jdeš na další pixel v tý velký bitmapě, to to znatelně urychlí.
3x AOC AG271QG (2560x1440, IPS, 165Hz, GSync), MSI RTX 2080Ti Trio, Intel Core i7 8700K@5GHz+EKWB L360, ASUS Maximus X, 32GB G.SKILL TridentZ@3466 MHz, Samsung EVO 840 500 GB + Crucial BX 500 GB + Kingston UV400 1TB, Seasonic P-860 Platinum, FD Define S
satikcz
Junior
Uživatelský avatar

Odeslat příspěvekod Nargon 10. 2. 2017 16:04

Je to jak píše satikcz, je to zcela tupé prohledávání. Porovnáváš barvy pixelů ve velkém obrázku s pixely v malém obrázku. Žádný speciální algoritmus na to asi ani není.

Ten kód je řešení pro C# .NET tam je nutný takovýto "složitý" zápis z výkonnostních důvodů, protože "standartní funkce" na vrácení barvy pixelu na souřadnici X,Y je nějak pokryplená. Zjistil jsem to sám když jsem taky chtěl něco zjistit z obrázku. Asi jsem zjišťoval průměrnou barvu, a vím že i pro celkem malý obrázek (cca 1M pixelů) to bylo strašně pomalé. Vypočítat průměr pro R,G,B z 1M pixelů trvalo minuty nebo možná desítky minut, prostě něco neskutečně pomalého a to jsem v kódu žádný problém neměl. Sečíst 3x1M hodnot je snadné a nakonec to ještě vydělit. Ale problém byl ve funkci GetPixel(x,y) která byla pomalá. Po přepsání na podobný zápis, který si přímo šahal na paměť se výpočet zrychlil na zlomek vteřiny. Stačilo obejít jednu problémovou funkci.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod worrapS 10. 2. 2017 16:21

Nargon: metoda GetPixel není nijak "pokryplená", je prostě objektová. Vytvořit milion objektů není zadarmo.

Tvůj problém je v počítačovém vidění popsán jako "template match". Existují různé algoritmy s rozdílnými vlastnostmi, které ho řeší (např. konvoluční maska, SIFT apod.). Většina moderních přístupů je implementována v knihovně OpenCV a jejích klonech pro různé jazyky. Doporučuju ji využít a nepatlat se s vlastním naivním řešením.
Pracovat s počítači je docela otrava. Vůbec, pracovat je otrava. Možná to nakonec s těmi počítači nebude až tak zlé.
worrapS
Junior
Uživatelský avatar

Odeslat příspěvekod dyžon 11. 2. 2017 11:28

bohužel OpenCV se mi nepodařilo do Delphi dostat, pořád chybí nějaky dll, ktery jsem nenašel nikde ke stažení, tak jsem začal smolit vlastní funkci.
místo GetPixel používám ScanLine,
ale narazil jsem hned na problém:
Kód: Vybrat vše
function TForm1.NajdiBmp(velka, mala: TBitMap): Boolean;
type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;
var radekMala, radekVelka: PRGBTripleArray;
    x, y: Integer;
    pomocnaBmp: TBitMap;
begin
  result:= False;
    pomocnaBmp:=TBitMap.Create;
    pomocnaBmp.Height:= mala.Height;
    pomocnaBmp.Width:= mala.Width;
    pomocnaBmp.PixelFormat:= pf24bit;

  mala.PixelFormat:= pf24bit;
  velka.PixelFormat:= pf24bit;
  radekMala:= mala.ScanLine[0];
  for x := 0 to velka.Width - mala.Width do begin
    for y := 0 to velka.Height - mala.Height do begin
      BitBlt(PomocnaBmp.Canvas.Handle, 0, 0, pomocnaBmp.Width, PomocnaBmp.Height,
             velka.Canvas.Handle, x, y, SRCCOPY);
      radekVelka:= pomocnaBmp.ScanLine[0];
      if radekMala = radekVelka then
        begin
          // pokud je 1. řádek stejnej.
           showmessage('našel.');
        end else
        begin
          // pokud nesedí skočí dál.
        end;
    end;  // konec For y
  end;  //konec For x
end;

divný je, že ikdyž dám stejnou BitMapu do velké i malé, tak ten první řádek shodnej není.
co je špatně ?
AMD FX-6300; Gigabyte 970A-DS3P; DDR3 8192MBytes; AMD Radeon HD 6700 Series
dyžon
Junior
Uživatelský avatar

Odeslat příspěvekod dyžon 11. 2. 2017 13:05

tak už jsem na to asi přišel, chyba je v porovnání.
místo radekMala a radekVelka : PRGBTripleArray jsem použil Pointer

Kód: Vybrat vše
var radekMala, radekVelka: Pointer;
  PixelSize: Byte;
     ......
  PixelSize:= 3;  //pf24bit
      ......
  if CompareMem(radekMala, radekVelka, scanptr2, mala.Width*PixelSize) then

našel jsem na netu ještě toto,
ale nepodařilo se mi zjistit, jakou knihovnu používá Region.FindRegion :-(
AMD FX-6300; Gigabyte 970A-DS3P; DDR3 8192MBytes; AMD Radeon HD 6700 Series
dyžon
Junior
Uživatelský avatar

Odeslat příspěvekod worrapS 11. 2. 2017 15:21

"Toto" je z návodu pro nástroj firmy SmartBear. Ti prodávají různá řešení pro automatizaci testů, čili nic co by sis mohl jednoduše stáhnout a použít zadarmo. Jaké řešení pro CV používají vnitřně nevím, ale vsadím boty že OpenCV.
Pracovat s počítači je docela otrava. Vůbec, pracovat je otrava. Možná to nakonec s těmi počítači nebude až tak zlé.
worrapS
Junior
Uživatelský avatar

Odeslat příspěvekod dyžon 12. 2. 2017 09:20

aha, tak díky.
no já bych rád použil OpenCV, ale když stahnu OpenCV 3.2, a podle instrukci zkusím nainstalovat, tak hned první balík <PROJECT_ROOT>\source\component\DelphiXX\OpenCVXXX.dpk mi vyhodí krpu, že nemám v PC opencv_core2413.dll
podařilo se mi ho nakonec stáhnout, zkopíroval jsem ho do Windows\System32\, dokonce jsem přidal do Delphi i další složku s jeho umístěním (Tools-Options-Delphi Options-Library-Library path)
ale když ho chci nainstalovat, tak pořád stejný, v PC chybí opencv_core2413.dll
zoufalství ... :-(
AMD FX-6300; Gigabyte 970A-DS3P; DDR3 8192MBytes; AMD Radeon HD 6700 Series
dyžon
Junior
Uživatelský avatar

Odeslat příspěvekod Nargon 13. 2. 2017 11:36

A stahuješ/instaluješ OpenCV? http://opencv.org/ já bych si tipnul že stahuješ jen nějakou pomocnou knihovnu, která je určena pro delphi, ale ke své funkci potřebuje i původní OpenCV, který ti tam stále chybí.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod satikcz 19. 2. 2017 14:42

Nargon píše:Ten kód je řešení pro C# .NET tam je nutný takovýto "složitý" zápis z výkonnostních důvodů, protože "standartní funkce" na vrácení barvy pixelu na souřadnici X,Y je nějak pokryplená. Zjistil jsem to sám když jsem taky chtěl něco zjistit z obrázku. Asi jsem zjišťoval průměrnou barvu, a vím že i pro celkem malý obrázek (cca 1M pixelů) to bylo strašně pomalé. Vypočítat průměr pro R,G,B z 1M pixelů trvalo minuty nebo možná desítky minut, prostě něco neskutečně pomalého a to jsem v kódu žádný problém neměl. Sečíst 3x1M hodnot je snadné a nakonec to ještě vydělit. Ale problém byl ve funkci GetPixel(x,y) která byla pomalá. Po přepsání na podobný zápis, který si přímo šahal na paměť se výpočet zrychlil na zlomek vteřiny. Stačilo obejít jednu problémovou funkci.

Není pokryplená, jen každej getpixel/setpixel lockuje a unlockuje znova tu bitmapu, u scanline to uděláš jen na začátku jednou a na konci.
3x AOC AG271QG (2560x1440, IPS, 165Hz, GSync), MSI RTX 2080Ti Trio, Intel Core i7 8700K@5GHz+EKWB L360, ASUS Maximus X, 32GB G.SKILL TridentZ@3466 MHz, Samsung EVO 840 500 GB + Crucial BX 500 GB + Kingston UV400 1TB, Seasonic P-860 Platinum, FD Define S
satikcz
Junior
Uživatelský avatar

Odeslat příspěvekod Brcer 5. 5. 2017 00:48

Nejjednodušší je udělat funkci v assembleru. Do jednoho registru načíst první kontrolní longword(pixel) hledaného obrazu a potom prohledávat původní obraz dokud nenajde stejné číslo. Když nenajde, neobsahuje, když ano pokračovat dalším pixelem. Assembler je to nejrychlejší co lze použít a pro tuto úlohu to nejlepší.
Brcer
Kolemjdoucí


Kdo je online

Uživatelé procházející toto fórum: Žádní registrovaní uživatelé a 0 návštevníků