Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Gdip_ImageSearch


  • Please log in to reply
63 replies to this topic
Croq
  • Members
  • 1 posts
  • Last active: Sep 16 2013 01:32 AM
  • Joined: 13 Feb 2013

Guys, I have been messing w/this for about 4 hours. I am running AHK_L 32, I even uninstalled and re-installed multiple times. All I get for my X and Y is -1 -1.

 

here is my code, can someone PLEASE help:

 

#SingleInstance, Force
#NoEnv
SetBatchLines, -1

#include gdip.ahk   ; download from here http://www.autohotkey.com/forum/topic32238.html 


If !pToken := Gdip_Startup()
{
   MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
   ExitApp
}

file1=c:\croq.png      ;filepath to image to search in image 
file2=c:\screenFollowMe.png       ;filepath to imagefile 


pBitmapneedle:=Gdip_CreateBitmapFromFile(file1)
pBitmapHaystack:=Gdip_CreateBitmapFromFile(file2)

Gdip_ImageSearch( pBitmapHayStack,pBitmapNeedle,  x,  y)


msgbox x coord =%x% y coord= %y%




Gdip_DisposeImage(pBitmaphaystack)
Gdip_DisposeImage(pBitmapneedle)
Gdip_Shutdown(pToken)
ExitApp

Gdip_ImageSearch(pBitmapHayStack="Screen", pBitmapNeedle="", ByRef x="", ByRef y="", sx1="", sy1="", sx2="", sy2="", Variation=0, Trans=0, w=0, h=0, sd=0)
{
    /*
    pBitmapHayStack:
        If "Screen" - screenshots screen 0 to be searched
        Else pointer to a Gdip bitmap object
    pBitmapNeedle:
        If filename - file is loaded
        Else pointer to a Gdip bitmap object
    x:
        The found X value of the needle image in the haystack image
    Y:
        The found Y value of the needle image in the haystack image
    sx1:
        The left bound search box for the haystack image (0 base)
    sy1:
        The upper bound search box for the haystack image (0 base)
    sx2:
        The right bound search box for the haystack image (0 base)
    sy2:
        The lower bound search box for the haystack image (0 base)
    */
    static _ImageSearch1, _ImageSearch2, _PixelAverage, Ptr, PtrA
    
    if !_ImageSearch1
    {
        Ptr := A_PtrSize ? "UPtr" : "UInt"
        , PtrA := A_PtrSize ? "UPtr*" : "UInt*"
        
        MCode_ImageSearch1 := "8B4C243483EC10803900538B5C2434555657750C80790100750680790200744B8B54243885D27E478B7C2430478BEA908B7424"
        . "3485F67E2C8BC78D9B000000008A50013A510275128A103A5101750B8A50FF3A117504C640020083C0044E75E08B54243803FB4D75C7EB048B5424388B"
        . "4424488944241C3B4424500F8D800200008B4C243C8BF90FAFF88D4402FF0FAFC1897C2418894424148DA424000000008B742444897424543B74244C0F"
        . "8D300200008B74245C83FE0175648B6C243033D2453B5424380F8D5402000033F6397424347E428B4C24548B5C242C8D0C8F8BC58D4C19018A58013A59"
        . "01750E8A183A1975088A58FF3A59FF740A807802000F85B60100004683C10483C0043B7424347CD38B5C2440037C243C4203EBEBA383FE020F85860000"
        . "008BF88BC14A8BCBF7D8F7D98BF20FAFF38B5C243089442410894C24488D6C1E01EB068D9B0000000083FAFF0F8EC701000033F6397424347E468B4C24"
        . "548B5C242C8D0C8F8BC58D4C19018A58013A5901750E8A183A1975088A58FF3A59FF740A807802000F85290100004683C10483C0043B7424347CD38B44"
        . "24108B4C244803F84A03E9EBA283FE030F85840000004A8BFAF7D90FAFFB8BF3F7DE8BE8894C2410897424488D490083FAFF0F8E470100008B44243448"
        . "83F8FF7E518B7424308B5C242C8D0C878D4C31018B74245403F08D74B5008D741E018A59013A5E01750E8A193A1E75088A59FF3A5EFF740A807902000F"
        . "859B0000004883EE0483E90483F8FF7FD48B7424488B4C241003E94A03FEEB9583FE040F85DC00000033ED896C24488D9B00000000395424480F8DC600"
        . "00008B4424344883F8FF7E4D8B4C24308B74242C8D5485008D4C0A018B54245403D08D14978D7432018A51013A5601750E8A113A1675088A51FF3A56FF"
        . "74068079020075224883EE0483E90483F8FF7FD88B5424388B4C243CFF44244803F903EBEB958B5C24408B4424548B7C24188B5424388B4C243C403B44"
        . "244C894424548B4424140F8CD0FDFFFF8B74241C4603C103F98974241C89442414897C24183B7424500F8C9FFDFFFF8B4C24248B5424285F5E5DC701FF"
        . "FFFFFFC702FFFFFFFF83C8FF5B83C410C38B4424248B4C24548B5424285F89088B4424185E5D890233C05B83C410C3"
        
        MCode_ImageSearch2 := "8B4C24348B54241883EC2C803900538B5C2450555657750C80790100750680790200744E8B6C244C85D27E4A8D7D0189542470"
        . "8B74245085F67E298BC78D49008A50013A510275128A103A5101750B8A50FF3A117504C640020083C0044E75E08B6C244C03FBFF4C247075C78B542454"
        . "EB048B6C244C8B442464894424183B44246C0F8D0C0400008B7C24588B74247C8BCF0FAFC8894C24148B8C248000000003C88D4402FF0FAFCF0FAFC789"
        . "4424108B442474894C2430EB068D9B000000008B4C2460894C24643B4C24680F8DA40300008BD30FAF94248000000003D58D0CB20FB611894C24348B4C"
        . "246003CE8B7424308D0C8E034C244889542438894C242CEB048B4C242C0FB6098D34013BD67F062BC83BD17D0E8B4C2434807903000F85350300008B4C"
        . "247883F9010F85AD0000008B742414C7442470000000008D4D018B542470894C2424897424283B5424540F8D5C03000033ED396C24507E698B5424648D"
        . "14968B7424488D7432018BFF0FB656010FB679018D1C023BFB7F2E2BD03BFA7C280FB6160FB6398D1C023BFB7F1B2BD03BFA7C150FB656FF0FB679FF8D"
        . "1C023BFB7F062BD03BFA7D0A807902000F85930200004583C60483C1043B6C24507CAC8B5C245C8B7424288B4C242403742458FF44247003CBE962FFFF"
        . "FF83F9020F85C60000008B5424108B4C2454498954241C8BD78BF3F7DAF7DE8BF90FAFFB8D7C2F018954242889742424897C2420894C247083F9FF0F8E"
        . "9402000033ED396C24507E798B74241C8B5424648B4C24208D14968B7424488D7432018BFF0FB656010FB679018D1C023BFB7F2E2BD03BFA7C280FB616"
        . "0FB6398D1C023BFB7F1B2BD03BFA7C150FB656FF0FB679FF8D1C023BFB7F062BD03BFA7D0A807902000F85C30100004583C60483C1043B6C24507CAC8B"
        . "7424248B5424288B4C24700154241C4901742420E964FFFFFF83F9030F85D80000008B5424548B4C24104A894C24208BF78BCAF7DE0FAFCB8BFBF7DF89"
        . "742428897C2424894C241C8954247083FAFF0F8EC90100008B6C24504D83FDFF0F8E8B0000008B7424208D14A98B4C244C8D4C0A018B54246403D58D14"
        . "968B7424488D543201EB068D9B000000000FB672010FB679018D1C063BFB7F2E2BF03BFE7C280FB6320FB6398D1C063BFB7F1B2BF03BFE7C150FB672FF"
        . "0FB679FF8D1C063BFB7F062BF03BFE7D0A807902000F85E30000004D83EA0483E90483FDFF7FAD8B7C24248B7424288B4C241C8B542470017424204A03"
        . "CFE94AFFFFFF83F9040F851B0100008B74241433C9894C2470894C24288B4C2470897424243B4C24540F8DFB0000008B6C24504D83FDFF7E738B542428"
        . "8D0CAA8B54244C8D4C11018B54246403D58D14968B7424488D5432010FB672010FB679018D1C063BFB7F2E2BF03BFE7C280FB6320FB6398D1C063BFB7F"
        . "1B2BF03BFE7C150FB672FF0FB679FF8D1C063BFB7F062BF03BFE7D068079020075254D83EA0483E90483FDFF7FB18B5C245C8B74242403742458FF4424"
        . "70015C2428E95CFFFFFF8B5C245C8B6C244C8B7C24588B5424388B4C24648344242C0441894C24643B4C24680F8C91FCFFFF8B74247CFF4424188B4C24"
        . "18017C2410017C2414017C24303B4C246C0F8C2CFCFFFF8B4424408B4C24445F5EC700FFFFFFFF5DC701FFFFFFFF83C8FF5B83C42CC38B5424408B4424"
        . "648B4C24445F89028B5424145E5D891133C05B83C42CC3"
        
        MCode_PixelAverage := "83EC488B4C24608B54246433C0890189028944243C894424388B44245453992BC2558B6C246456578BF88B442468992BC28B54"
        . "24688D5D148D73148D4E14D1FF897E04D1F8894104894308897E088951088B542464897D0C89430C89560C8B54246889510C8B542464897D1089561089"
        . "41108BC52BD92BC18BFE8D51042BF9C74424340100000089542410895C24408944242C897C2430EB178DA424000000008B5424108B5C24408B44242C8B"
        . "7C24308B328B0C1333ED896C2468896C2464896C246C897424443BCE0F8D030100008B0410894424148B0417894424188BC10FAF4424602BF189442438"
        . "8974243C8BFF8B7424148B4C241833DB33FF896C241C896C2420896C2424896C24283BF10F8DA00000002BCE83F9027C538B54245C8D0CB08D4C11018B"
        . "5424182BD683EA02D1EA428D34560FB6410103F80FB60103D80FB641FF0144241C0FB64105014424280FB64104014424240FB641030144242083C1084A"
        . "75CF8B4424388B54241033ED3B7424187D1E8D0CB0034C245C0FB67102017424680FB671010FB60901742464014C246C8B4C24248B7424200374241C03"
        . "CB014C24648B4C24280174246C03CF014C246803442460FF4C243C894424380F852AFFFFFF8B5C24408B44242C8B7C24308B34138B3C172B3C108B0A8B"
        . "4424682BCE0FAFCF33D2F7F133D2896C24688BF88B442464F7F133D28BD88B44246CF7F18D57198954246C8D4B1983C7E783C3E7897C243C894C243889"
        . "5C24288D50198954242483C0E733D2894424203B7424440F8DAE0000008B4424108B4C242C8B0C01894C24148B4C24308B1C018BC60FAF442460895C24"
        . "1C894424648D49008B7C24143BFB7D6B8B4C245C8D04B88D4C080280790100744A0FB6018BDA3B44246C7D063B44243C7F01420FB641FF3B4424387D06"
        . "3B4424287F01420FB641FE3B4424247D063B4424207F01428BC22BC33B4424687E0C897C245089742454894424688B5C241C4783C1043BFB7CA48B4424"
        . "640344246046894424643B7424440F8C7AFFFFFF3B5424487E208B4424708B4C2450895424488B54243489088B4424548954244C8B54247489028B4424"
        . "348344241004408944243483F8050F8C7DFDFFFF8B44244C5F5E5D5B83C448C3"
        
        VarSetCapacity(_ImageSearch1, StrLen(MCode_ImageSearch1)//2, 0)
        Loop % StrLen(MCode_ImageSearch1)//2      ;%
            NumPut("0x" . SubStr(MCode_ImageSearch1, (2*A_Index)-1, 2), _ImageSearch1, A_Index-1, "uchar")
        MCode_ImageSearch1 := ""
        
        VarSetCapacity(_ImageSearch2, 1233, 0)
        Loop % StrLen(MCode_ImageSearch2)//2      ;%
            NumPut("0x" . SubStr(MCode_ImageSearch2, (2*A_Index)-1, 2), _ImageSearch2, A_Index-1, "uchar")
        MCode_ImageSearch2 := ""
        
        VarSetCapacity(_PixelAverage, StrLen(MCode_PixelAverage)//2, 0)
        Loop % StrLen(MCode_PixelAverage)//2      ;%
            NumPut("0x" . SubStr(MCode_PixelAverage, (2*A_Index)-1, 2), _PixelAverage, A_Index-1, "uchar")
        MCode_PixelAverage := ""
        
        , DllCall("VirtualProtect", Ptr, &_ImageSearch1, Ptr, VarSetCapacity(_ImageSearch1), "uint", 0x40, PtrA, 0)
        , DllCall("VirtualProtect", Ptr, &_ImageSearch2, Ptr, VarSetCapacity(_ImageSearch2), "uint", 0x40, PtrA, 0)
        , DllCall("VirtualProtect", Ptr, &_PixelAverage, Ptr, VarSetCapacity(_PixelAverage), "uint", 0x40, PtrA, 0)
    }
    
    ;Alows the MCode to be setup before imagesearch is really needed
    if (pBitmapHayStack = "")
        return -1
    else if (pBitmapHayStack = "Screen"){
        ;Capture the screen
        Dump_HayStack := True
        pBitmapHayStack := Gdip_BitmapFromScreen()
    }
    
    if (pBitmapNeedle = "")
        return -1
    else if (FileExist(pBitmapNeedle)){
        ;Load the image from the HD
        Dump_Needle := True
        pBitmapNeedle := Gdip_CreateBitmapFromFile(pBitmapNeedle)
    }
    
    if (Variation > 255 || Variation < 0)
        return -2
    
    Gdip_GetImageDimensions(pBitmapHayStack, hWidth, hHeight)
    , Gdip_GetImageDimensions(pBitmapNeedle, nWidth, nHeight)
    
    if !(hWidth && hHeight && nWidth && nHeight)
        return -3
    if (nWidth > hWidth || nHeight > hHeight)
        return -4
    
    ;Sets/corrects resize variables
    w := (w < -1) ? 0 : w
    , h := (h < -1) ? 0 : h
    
    ;Resizes the needle image if w/h are set
    ;What a pain...
    if (w || h){
        ;Creates a resized needle image and set pBitmapNeedle to the bitmap
        if (w = -1)
            sH := h, sW := nWidth / nHeight, sW := Round(sH * sW)
        else if (h = -1)
            sW := w, sH := nHeight / nWidth, sH := Round(sW * sH)
        else
            sW := w, sH := h
        
        pTempBitmap := Gdip_CreateBitmap(sW, sH)
        , pG := Gdip_GraphicsFromImage(pTempBitmap)

        , Gdip_SetInterpolationMode(pG, 7)
        , Gdip_DrawImage(pG, pBitmapNeedle, 0, 0, sW, sH, 0, 0, nWidth, nHeight)
        , Gdip_DeleteGraphics(pG)
        
        , pBitmapNeedle := pTempBitmap
        , Gdip_GetImageDimensions(pBitmapNeedle, nWidth, nHeight)
        
        if !(nWidth && nHeight){
            Gdip_DisposeImage(pBitmapNeedle)
            if (Dump_HayStack)
                Gdip_DisposeImage(pBitmapHayStack)
            return -5
        }
        if (nWidth > hWidth || nHeight > hHeight){
            Gdip_DisposeImage(pBitmapNeedle)
            if (Dump_HayStack)
                Gdip_DisposeImage(pBitmapHayStack)
            return -6
        }
    }
    
    ;Sets/corrects search box and needle scan direction
    sx1 := (sx1 = "") ? 0 : sx1
    , sy1 := (sy1 = "") ? 0 : sy1
    , sx2 := (sx2 = "") ? hWidth : (sx2 - nWidth + 1)
    , sy2 := (sy2 = "") ? hHeight : (sy2 - nHeight + 1)
    , sd := (sd < 0 || sd > 4) ? 1 : sd
    
    if (sx1 < 0 || sy1 < 0){
        if (w || h || Dump_Needle)
            Gdip_DisposeImage(pBitmapNeedle)
        if (Dump_HayStack)
            Gdip_DisposeImage(pBitmapHayStack)
        return -7
    }
    
    ;Detects to-small search boxes
    if ((sx2 - sx1) < 1 || (sy2 - sy1) < 1){
        if (w || h || Dump_Needle)
            Gdip_DisposeImage(pBitmapNeedle)
        if (Dump_HayStack)
            Gdip_DisposeImage(pBitmapHayStack)
        return -8
    }
    
    ;Prevents searching to close to the edges: it can't match 20 pixels in a 19 pixel search area
    if (sx2 > (hWidth - nWidth + 1))
        sx2 := (hWidth - nWidth) + 1
    if (sy2 > (hHeight - nHeight + 1))
        sy2 := (hHeight - nHeight) + 1
    
    ;Detects invalid search boxes
    if (sx2 < sx1 || sy2 < sy1){
        if (w || h || Dump_Needle)
            Gdip_DisposeImage(pBitmapNeedle)
        if (Dump_HayStack)
            Gdip_DisposeImage(pBitmapHayStack)
        return -9
    }
    
    if (sx2 = 0)
        sx2 := 1
    if (sy2 = 0)
        sy2 := 1
    
    ;If Trans is used and the needle hasen't already been copied through scaling or loading
    ;create a copy because it might be modified by the imagesearch code
    if (!w && !h && Trans && !Dump_Needle)
        pBitmapNeedle := Gdip_CloneBitmapArea(pBitmapNeedle, 0, 0, nWidth, nHeight)        
    
    ;Stride2/Scan02/BitmapData2 are used because the needle is the second image eventhough it's used first
    E := Gdip_LockBits(pBitmapNeedle, 0, 0, nWidth, nHeight, Stride2, Scan02, BitmapData2)
    if (E){
        if (w || h || Trans || Dump_Needle)
            Gdip_DisposeImage(pBitmapNeedle)
        if (Dump_HayStack)
            Gdip_DisposeImage(pBitmapHayStack)
        return -12
    }
    
    ;Averages the needle in 4 chunks counting the number of pixels(R, G & B) that arn't +- 25 of the average color
    ;and sets the search code to scan that corner first when matching the image
    ;Also sets a "Check this pixel first" location for the haystack instead of always starting at the corner of the image (0/0, w/h, ...)
    if (sd = 0 And nWidth >= 20 And nHeight >= 20){
        VarSetCapacity(TempData, 5*4*4, 0) ;5 entires at 4 entires each at 4 bytes each
        , sd := DllCall(&_PixelAverage, Ptr, Scan02, "Int", Stride2, "Int", nWidth, "Int", nHeight, Ptr, &TempData
        , "UInt*", suX, "UInt*", suY, "cdecl int")
        , VarSetCapacity(TempData, 0)
    } else
        sd := sd = 0 ? 1 : sd
    
    ;Sets the default search-first location for variation searches if none was set yet
    suX := (suX = "" || suX = -1) ? 0 : suX
    , suY := (suY = "" || suY = -1) ? 0 : suY
    
    if (Gdip_LockBits(pBitmapHayStack, 0, 0, hWidth, hHeight, Stride1, Scan01, BitmapData1)){
        if (w || h || Trans || Dump_Needle)
            Gdip_DisposeImage(pBitmapNeedle)
        if (Dump_HayStack)
            Gdip_DisposeImage(pBitmapHayStack)
        return -12
    }
    
    ;The dllcall parameters are the same for easier C code modification even though they arn't all used on the _ImageSearch1 version
    x := 0, y := 0
    , E := DllCall((Variation = 0 ? &_ImageSearch1 : &_ImageSearch2), "int*", x, "int*", y, Ptr, Scan01, Ptr, Scan02, "int", nWidth
    , "int", nHeight, "int", Stride1, "int", Stride2, "int", sx1, "int", sy1, "int", sx2, "int", sy2, Ptr, &Trans, "int", Variation
    , "int", sd, "int", suX, "int", suY, "cdecl int")
    
    Gdip_UnlockBits(pBitmapHayStack, BitmapData1), Gdip_UnlockBits(pBitmapNeedle, BitmapData2)
    
    if (w || h || Trans || Dump_Needle)
        Gdip_DisposeImage(pBitmapNeedle)
    
    if (Dump_HayStack)
        Gdip_DisposeImage(pBitmapHayStack)
    
    return (E = "") ? -13 : E
}

 

 



Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010

Guys, I have been messing w/this for about 4 hours. I am running AHK_L 32, I even uninstalled and re-installed multiple times. All I get for my X and Y is -1 -1.

 

here is my code, can someone PLEASE help:

 

 

Can you upload the images (as a zip so they don't lose quality) so I can try them on my computer?



guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011


		if (Needle[(4*x2)+(y2*Stride2)+2] == HayStack[(4*tx)+(ty*Stride1)+2]
		&& Needle[(4*x2)+(y2*Stride2)+1] == HayStack[(4*tx)+(ty*Stride1)+1]
		&& Needle[(4*x2)+(y2*Stride2)] == HayStack[(4*tx)+(ty*Stride1)]
		|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
			tx++;
		else
			goto NoMatch;


why are you comparing four offsets? tic's original only compares the first without any offset addition.

in ahk...
NumGet(Scan01+0, (x*4)+(y*Stride1))       ;// works and returns a full ARGB color
NumGet(Scan01+0, (x*4)+(y*Stride1) + 1)    ;// gives something different i dont know what
i'm trying to understand

edit/
oh ok i see, you are using char whereas tic was using int, and ahk NumGet defaults to uint
nevermind happy.png

The MCode nolonger directly compares alpha values - it now sees transparent pixels (alpha of 0) on the needle as match-anything on the haystack image - all other alpha values on the needle and haystack are ignored

do you think ^^this^^ slows the search down? since now you have to do 4 comparisons whereas tic's original only does one

Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010

do you think ^^this^^ slows the search down? since now you have to do 4 comparisons whereas tic's original only does one

 

What slow down? The code should exceute the same as or maybe 1 MS slower then the original code that Tic posted for no-variance searches.

 

For variance searches Tic's original code already does the 4 comparisons.

 

 

The 4 comparisons are needed to match a transparent pixel (A of 0) or a pixel that matches the color you're looking for (RGB).



guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
right yeah ok i understand, thanks.
i am just trying to figure out ways to improve performance since the multiple image search is not gonna be acceptable. i've tried abstracting out the LockBits like i posted earlier but that only gave me 15% improvement. now i tried multithreaidng with ahk.dll but thats only giving me maybe 60% improvement. Uberi suggested i try a different search algorithm so thats probably what i'll do next

Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010

right yeah ok i understand, thanks.
i am just trying to figure out ways to improve performance since the multiple image search is not gonna be acceptable. i've tried abstracting out the LockBits like i posted earlier but that only gave me 15% improvement. now i tried multithreaidng with ahk.dll but thats only giving me maybe 60% improvement. Uberi suggested i try a different search algorithm so thats probably what i'll do next

 

 

Do you need variance? That can speed up the search by quite a lot.

 

Your problem is you're searching images for images using the CPU which isn't built do that efficiently.



guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011

Do you need variance? That can speed up the search by quite a lot.

i am not using variance but it doesnt look like ImageSearch1 allows for it anyway

Your problem is you're searching images for images using the CPU which isn't built do that efficiently.

how else can we do it? use the GPU for the processing?

MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009

Dear friends,
 
I've been trying to tweak this function recently.
After some conversation with guest3456, I decided to implement new things into the current/latest version of Gdip_ImageSearch().
(I commented some parts of the original function, you can search for "{MF}" to find those)
I was also interested in finding a fast way to retrieve all instances of an image on screen.
 
So, here they are: Gdip_FastImageSearch() and Gdip_ImageSearchList() [ previously Gdip_ImgSearchList() ]
These are not definitive versions, please give me some time to fix those!

 

These functions are licensed under http://creativecommo...nses/by-sa/3.0/

However, I deliberately and explicitly waive compliance with the "Share Alike" condition exclusively for tic, Rseding91 and guest3456.


Please note some variables in Gdip_ImageSearchList() are hardcoded on purpose.
As I said, this is a not a definitive version. I still have to do a few tests/changes...

 

 

* EDIT *

New link: https://github.com/M...dip_ImageSearch

Commit before the unification (09/march/2013): https://github.com/M...ded388f4ada2f4e


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
Thanks MasterFocus

I have tested your functions against RSeding's with those haystack and needles images you posted.

funcs used:
RSeding ImageSearch
RSeding Multi Img Search
MasterFocus ImgSearch and Multi

Results with 100 loops:
 

RSeding ImageSearch and Multi
time = 5085 ms

MasterFocus ImageSearch and Multi
time = 562 ms

RSeding imgsearch with MasterFocus multi
time = 749 ms

 
Conclusions:

1. Your new ImageSearch function seems to benchmark slightly better than RSeding's, due to the fact that you are saving the LockedBits and therefore do not need to lock and unlock within each call. This is as i mentioned with my example in this post, and the results seem to be similar. I was benchmarking about 15% faster without any loops, yours seems to be around 25% faster with 100 loops.
 
2. My hunch was correct: The significant difference is the speedup from your multiple image search function Gdip_ImgSearchList(). You are getting magnitudes better results. As you can see from only swapping in your multiple image list function, RSeding's imgsearch drops from 5100ms to 700ms. Clearly your algorithm is better. From a quick study, it seems even with two while loops, you are only iterating 16 times through this haystack (the number of found needles), while RSeding's multi img search iterates 242 times (the height of the haystack). Since he is searching row by row individually, I guess there are a lot of rows with no images, and the slowdown comes from having to do all the processing on those extraneous row failures instead of letting the MCode search them all within one call. RSeding originally wrote that multi-search quickly in IRC as we brainstormed, so I bet he would've noticed this if he had spent more time on it
 
test code:
Spoiler


MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009

I suppose the main reason is that, in Rseding91's code, it all starts assuming SX2 := Haystack_Width and SY2 := Needle_Height.

But why constinuously crop the search area so small if Gdip_ImageSearch() was already meant to iterate through each pixel?

 

I'll probably be very busy until the 20th (wednesday).

However, I intend to try further improvements by adding an option to lock the haystack and/or the needle.


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


TLM
  • Administrators
  • 3864 posts
  • Last active:
  • Joined: 21 Aug 2006

MasterFocus, guest3456.. Hey guys, here testing....


Posted Image

don't duplicate, iterate!


MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009

I managed to allow locking haystack/needle/both successfully.

I was experiencing a memory leak, but I found out Dump_Haystack and Dump_Needle had to be changed.

Once again, thank you very much guest3456 for testing some stuff with me on IRC.

 

* EDIT *

 

New link: https://github.com/M...dip_ImageSearch

Commit before the unification (09/march/2013): https://github.com/M...ded388f4ada2f4e


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009

Dear friends,

 

I have spent a few hours on IRC talking to guest3456 about standards and optimizations.

A few things have been considered:

    1) Users working with tic's GDI+ wrapper/library mostly deal with Bitmaps (pBitmap), not BitmapDatas (LockedBits stuff)

    2) I couldn't find one single situation on the forum where a person used ImageSearch's resize feature

    3) Even if someone needs to resize stuff, if they are already using GDIP, it will involve manipulating Bitmaps anyway

    4) A user-friendly function, which allows many kinds of input (not only Bitmaps), doesn't follow the standard in item 1 *

    5) Too many validations slow things down for multiple searches, specially if the same haystack and/or needle is involved

* Don't get me wrong, I do enjoy creating user-friendly functions, but implementing so many extra features (like item 3) goes against items 1 and 5

 

I may not be properly presenting all things we discussed, but those are the main arguments for what I decided to implement.

 

Finally, I have divided the whole thing into 3 functions:

    - Gdip_LockedBitsSearch(): contains most of the core stuff (like the MCode) and is what really finds a match

    - Gdip_MultiGdip_LockedBitsSearch(): implements my algorithm for multiple searches, using a BitmapData to do so

    - Gdip_ImageSearch(): see details below

 

Gdip_ImageSearch() is intended to be used by most users, as it accepts Bitmaps as input. Most initial validations are now located here.

It will store in 2 ByRef variables the list of matching coordinates and the number of instances matched.

I have also added a parameter to specify the maximum instances you want to find.

The current default is 0 (find all instances), but I'm pondering about setting it to 1.

 

As you may have noticed, the core stuff (MCode and such) is now part of an "internal" function, while Gdip_ImageSearch() is something else.

As this may lead to some confusion, I'm renaming the folder before uploading it all to GitHub.

 

New link: https://github.com/M...dip_ImageSearch

Commit before the unification: https://github.com/M...ded388f4ada2f4e

 

 

Note: I'll eventually add a proper header for each function documenting the parameters/usage


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
MasterFocus,

you should set the OutputList to empty "" at the start of the Multi function. otherwise if the same variable is used, the list will just append instead of starting fresh

check the `LIST` variable here:
#Include GDIP.ahk
#Include Gdip_ImageSearch.ahk

gdipToken := Gdip_Startup()

bmpHaystack := Gdip_CreateBitmapFromFile("haystack.png")
bmpNeedle := Gdip_CreateBitmapFromFile("needle.png")

ERR := Gdip_ImageSearch(bmpHaystack,bmpNeedle,LIST,COUNT)
MsgBox, % "Error: " ERR "`tCount: " COUNT "`n`n" LIST

ERR := Gdip_ImageSearch(bmpHaystack,bmpNeedle,LIST,COUNT)
MsgBox, % "Error: " ERR "`tCount: " COUNT "`n`n" LIST

ERR := Gdip_ImageSearch(bmpHaystack,bmpNeedle,LIST,COUNT)
MsgBox, % "Error: " ERR "`tCount: " COUNT "`n`n" LIST

Gdip_DisposeImage(bmpHaystack)
Gdip_DisposeImage(bmpNeedle)
Gdip_Shutdown(gdipToken)
fix:
Gdip_MultiLockedBitsSearch(hStride,hScan,hWidth,hHeight,nStride,nScan,nWidth,nHeight
,ByRef OutputList="",OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans=0
,SearchDirection=0,Instances=0,LineDelim="`n",CoordDelim=",")
{
    OutputList := ""    ;// <-------------------------------- FIX --
    OutputCount := 0 + (!Instances)
    ...
two final things:
1. in the TestScript.ahk in your repo, you forget to dispose the images happy.png
2. this comment is backwards i think:
sd += !sd ; becomes 0 if its 1, and stays unaltered otherwise
should say, "becomes 1 if it's 0"

MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009

if the same variable is used, the list will just append instead of starting fresh

"It's not a bug, it's an undocumented feature!" hahaha tongue.png Just kidding, you are right.

Wow, I can't believe I forgot to dispose the images. I suppose calling Gdip_Shutdown() cleaned up the resource, so I didn't even notice.

 

Thanks for pointing those things out. I'll fix them right now.

 

Just a reminder: I do intend to extend my Multi function, adding the remaining 3 search directions.

Currently, the "search direction" parameters is just passed to the internal MCode function, while the Multi function itself uses "L->R, T->B".

 

EDIT: I have also a few ideas, like implementing the search direction for the haystack instead of the needle, and some other improvements to the ImageSearch C code. I'm currently having my final exams, but I'll eventually update things when I have some time.


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.