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
Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010

EDIT: removed a msgbox from the code I had for other purposes

I've played around with the code and managed to make the imagesearch with variance faster and in some cases depending on the needle much much faster.

I made the following changes:
 

  • Moved the pointer math into the if statements
  • Changed the if statements from: "if (N[#] > H[#]+V || N[#] < H[#]-V) nomatch" to:
  • "if (N[#] < H[#]+V && N[#] < H[#]-V) continue checking"

Added the option to change the search direction on the needle

 

The huge speed improvement comes from the needle search direction option. The default method is left->right, top->bottom. The new option lets you specify which scan direction you want to use:

 

  • 0 : auto detect best scan direction
  • 1 : left->right, top->bottom
  • 2 : left->right, bottom->top
  • 3 : right->left, bottom->top
  • 4 : right->left, top->bottom

 

The way auto detect works is:

 

  • If auto-detect was set and variation was set and needle width >= 20 and needle height >= 20
  • Copy the needle into 4 split images
  • Average the A, R, G, B values for a given corner
  • Find the number of pixels that do not fall +- 20 pixels of the average A, R, G, B values
  • Scan from the corner with the highest unique pixel count first (1, 2, 3, 4)


In my testing the auto-detect takes maybe 10-20 milliseconds to run (not worth counting) on a
420x280 image. The speed improvement went from 47~ seconds to return no-match down to 500~ MS to return a no-match.


Gdip_ImageSearch(pBitmapHayStack, pBitmapNeedle, ByRef x, ByRef y, Variation=0, sx="", sy="", w="", h="", sd=0)
{
static _ImageSearch1, _ImageSearch2, _PixelSearch1, _PixelSearch2
if !_ImageSearch1
{
MCode_ImageSearch1 := "83EC108B44242C9983E20303C28BC88B4424309983E20303C253C1F80255894424148B44244056C1F9023B44244C578944244"
. "80F8DCA0000008B7C24348D148D000000000FAFC88B442444895424148B54242403C88D1C8A8B4C244C895C24183BC1894424407D7A895C24108D6424"
. "008B6C2428C744243C000000008D6424008B44243C3B4424380F8D9400000033C985FF7E178BD58BF38B063B02752283C10183C20483C6043BCF7CED8"
. "B44241C035C24148344243C0103C003C003E8EBC08B4424408B5C24108B4C244C83C00183C3043BC189442440895C24107C928B4424448B5424488B5C"
. "2418035C241483C2013B54245089542448895C24180F8C5DFFFFFF8B5424548B4424585F5EC702FFFFFFFF5DC700FFFFFFFF83C8FF5B83C410C38B4C2"
. "4548B5424408B4424585F89118B4C24445E5D890833C05B83C410C3"

VarSetCapacity(_ImageSearch1, StrLen(MCode_ImageSearch1)//2)
Loop % StrLen(MCode_ImageSearch1)//2 ;%
NumPut("0x" SubStr(MCode_ImageSearch1, (2*A_Index)-1, 2), _ImageSearch1, A_Index-1, "char")
}

if !_ImageSearch2
{
MCode_ImageSearch2 := "83EC208B44244853555657894424183B4424600F8D3E0400008B5C244C8B7C24508B6C24388BCB0FAFC8894C24148B4C24488D"
. "7401FF8B4424640FAFF38B4C24548974242CEB098DA424000000008BFF894C24103B4C245C0F8DDD0300008BFF8B5424708B4C244883FA010F85DB0000"
. "008B742414C7442458000000008D55028954242489742428394C24580F8DE403000033DB395C24440F8E910000008B4C24108D0C8E8B7424348D4C3102"
. "8D49000FB671010FB67A018D2C063BFD0F8D510300002BF03BFE0F8E470300000FB6310FB63A8D2C063BFD0F8D360300002BF03BFE0F8E2C0300000FB6"
. "71FF0FB67AFF8D2C063BFD0F8D190300002BF03BFE0F8E0F0300000FB671FE0FB67AFE8D2C063BFD0F8DFC0200002BF03BFE0F8EF20200004383C10483"
. "C2043B5C24447C818B7424288B5424240374244CFF442458035424508B4C2448E934FFFFFF83FA020F85F4000000498974241C8BD38BF7F7DAF7DE8BD9"
. "0FAFDF8D7C2B028954242889742424897C2420894C245883F9FF0F8EF202000033DB395C24440F8EAB0000008B7424108B4C241C8B5424208D0CB18B74"
. "24348D4C3102EB078DA424000000000FB671010FB67A018D2C063BFD0F8D510200002BF03BFE0F8E470200000FB6310FB63A8D2C063BFD0F8D36020000"
. "2BF03BFE0F8E2C0200000FB671FF0FB67AFF8D2C063BFD0F8D190200002BF03BFE0F8E0F0200000FB671FE0FB67AFE8D2C063BFD0F8DFC0100002BF03B"
. "FE0F8EF20100004383C10483C2043B5C24447C818B7424248B5424288B4C24580154241C4901742420E92EFFFFFF83FA030F85FC0000008D51FF897424"
. "208BCA8BF30FAFCFF7DEF7DF89742428897C2424894C241C8954245883FAFF0F8EF90100008B5C24444B83FBFF0F8EB30000008B7424208D14998B4C24"
. "1003CB8D0C8E8B7424348D542A028D4C3102EB078DA424000000000FB671010FB67A018D2C063BFD0F8D510100002BF03BFE0F8E470100000FB6310FB6"
. "3A8D2C063BFD0F8D360100002BF03BFE0F8E2C0100000FB671FF0FB67AFF8D2C063BFD0F8D190100002BF03BFE0F8E0F0100000FB671FE0FB67AFE8D2C"
. "063BFD0F8DFC0000002BF03BFE0F8EF20000004B83E90483EA0483FBFF7F828B7C24248B7424288B4C241C8B542458017424208B6C24384A03CFE91EFF"
. "FFFF83FA040F851F0100008B74241433FF897C2458897C242489742428394C24580F8D030100008B5C24444B83FBFF7E768B4C241003CB8D0C8E8B7424"
. "348D149F8D542A028D4C31020FB671010FB67A018D2C063BFD7D702BF03BFE7E6A0FB6310FB63A8D2C063BFD7D5D2BF03BFE7E570FB671FF0FB67AFF8D"
. "2C063BFD7D482BF03BFE7E420FB671FE0FB67AFE8D2C063BFD7D332BF03BFE7E2D4B83E90483EA0483FBFF7FA28B7424288B7C24240374244CFF442458"
. "037C24508B4C24488B6C2438E94DFFFFFF8B4C24108B74242C8B7C24508B5C244C8B6C243841894C24103B4C245C0F8C29FCFFFF8B4C2454FF4424188B"
. "542418015C241403F38974242C3B5424600F8CF9FBFFFF8B4C24688B54246C5F5E5DC701FFFFFFFFC702FFFFFFFF83C8FF5B83C420C38B4424688B4C24"
. "108B54246C5F89088B4424145E5D890233C05B83C420C3"

VarSetCapacity(_ImageSearch2, StrLen(MCode_ImageSearch2)//2)
Loop % StrLen(MCode_ImageSearch2)//2 ;%
NumPut("0x" SubStr(MCode_ImageSearch2, (2*A_Index)-1, 2), _ImageSearch2, A_Index-1, "char")
}

if !_PixelSearch1
{
PixelAverage1 := "8B4C24148B44240833D2891189510489510889510C89511089511489511889511C3BC27E5B538B5C2414558B6C240C565783C502894"
. "424248B7C241C3BFA7E338BF50FB646019901011151040FB6069901410811510C0FB646FF990141101151140FB646FE9901411811511C03F34F75D133D"
. "283C504FF4C242475BC5F5E5D5BC3"
, PixelAverage2 := "83EC1C8B4C243053568B318D5E1983C6E7897424088B7108578D7E1983C6E7897424148B71108B4918897C24108D7E1983C6E7897"
. "4241C8D711983C1E7894C24248B4C243033C033D2897C24188974242085C90F8E8C0000008B74242C8B7C243483C6028974243C894C24308D64240085F"
. "F7E600FB64E013BCB7F063B4C240C7D0683C00183D2000FB60E3B4C24107F063B4C24147D0683C00183D2000FB64EFF3B4C24187F063B4C241C7D0683C"
. "00183D2000FB64EFE3B4C24207F063B4C24247D0683C00183D200037424384F75A48B7C24348B74243C83C604FF4C24308974243C758B5F5E5B83C41CC3"

VarSetCapacity(_PixelSearch1, StrLen(PixelAverage1)//2)
Loop % StrLen(PixelAverage1)//2 ;%
NumPut("0x" SubStr(PixelAverage1, (2*A_Index)-1, 2), _PixelSearch1, A_Index-1, "char")

VarSetCapacity(_PixelSearch2, StrLen(PixelAverage2)//2)
Loop % StrLen(PixelAverage2)//2 ;%
NumPut("0x" SubStr(PixelAverage2, (2*A_Index)-1, 2), _PixelSearch2, A_Index-1, "char")
}

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

sx := (sx = "") ? 0 : sx
, sy := (sy = "") ? 0 : sy
, w := (w = "") ? hWidth-sx : w
, h := (h = "") ? hHeight-sy : h
, sd := (sd < 0 Or sd > 4) ? 1 : sd

if (sx+w > hWidth-nWidth)
w := hWidth-sx-nWidth+1

if (sy+h > hHeight-nHeight)
h := hHeight-sy-nHeight+1

if (sd = 0 And Variation And nWidth >= 20 And nHeight >= 20){
pBitmap_Needle_S1 := Gdip_CloneBitmapArea(pBitmapNeedle, 0, 0, Floor(nWidth / 2), Floor(nHeight / 2))
, pBitmap_Needle_S2 := Gdip_CloneBitmapArea(pBitmapNeedle, 0, Floor(nHeight / 2), Floor(nWidth / 2), Ceil(nHeight / 2))
, pBitmap_Needle_S3 := Gdip_CloneBitmapArea(pBitmapNeedle, Floor(nWidth / 2), Floor(nHeight / 2), Ceil(nWidth / 2), Ceil(nHeight / 2))
, pBitmap_Needle_S4 := Gdip_CloneBitmapArea(pBitmapNeedle, Floor(nWidth / 2), 0, Ceil(nWidth / 2), Floor(nHeight / 2))
, Last_Score := 0
, sd := 1

loop, 4
{
Gdip_GetImageDimensions(pBitmap_Needle_S%A_Index%, Sector_Width, Sector_Height)
if !(Sector_Width && Sector_Height)
return -3

if (Gdip_LockBits(pBitmap_Needle_S%A_Index%, 0, 0, Sector_Width, Sector_Height, Sector_Stride, Sector_Scan, Sector_BitmapData))
return -5

VarSetCapacity(Sector_Averages, 8 * 4, 0)
, DllCall(&_PixelSearch1, "UInt", Sector_Scan, "Int", Sector_Width, "Int", Sector_Height, "Int", Sector_Stride, "UInt", &Sector_Averages)

loop, 4
N := NumGet(Sector_Averages, (A_Index - 1) * 8, "Int64") // (Sector_Width * Sector_Height)
, NumPut(N, Sector_Averages, (A_Index - 1) * 8, "Int64")

Sector_Score := DllCall(&_PixelSearch2, "UInt", Sector_Scan, "Int", Sector_Width, "Int", Sector_Height, "Int", Sector_Stride, "UInt", &Sector_Averages, "cdecl int64")
, Gdip_UnlockBits(pBitmap_Needle_S%A_Index%, Sector_BitmapData)
, Gdip_DisposeImage(pBitmap_Needle_S%A_Index%)

if (Sector_Score > Last_Score)
sd := A_Index, Last_Score := Sector_Score
}
}

E1 := Gdip_LockBits(pBitmapHayStack, 0, 0, hWidth, hHeight, Stride1, Scan01, BitmapData1)
, E2 := Gdip_LockBits(pBitmapNeedle, 0, 0, nWidth, nHeight, Stride2, Scan02, BitmapData2)
if (E1 || E2)
return -5

x := y := 0
if (Variation = 0)
{
E := DllCall(&_ImageSearch1, "uint", Scan01, "uint", Scan02, "int", hWidth, "int", hHeight, "int", nWidth, "int", nHeight, "int", Stride1
, "int", Stride2, "int", sx, "int", sy, "int", w, "int", h, "int*", x, "int*", y)
}
else
{
E := DllCall(&_ImageSearch2, "uint", Scan01, "uint", Scan02, "int", hWidth, "int", hHeight, "int", nWidth, "int", nHeight, "int", Stride1
, "int", Stride2, "int", sx, "int", sy, "int", w, "int", h, "int", Variation, "int*", x, "int*", y, "int", sd)
}
Gdip_UnlockBits(pBitmapHayStack, BitmapData1), Gdip_UnlockBits(pBitmapNeedle, BitmapData2)
return (E = "") ? -6 : E
}


The new C code is as follows:

int Gdip_ImageSearch2(unsigned char * HayStack, unsigned char * Needle, int w1, int h1, int w2, int h2, int Stride1, int Stride2, int sx, int sy, int w, int h, int v, int * x, int * y, int sd)
{
int tx, ty, ph, pn, Ah, Rh, Gh, Bh, An, Rn, Gn, Bn;
int y1, y2, x1, x2;

for (y1 = sy; y1 < h; ++y1)
{
for (x1 = sx; x1 < w; ++x1)
{
if (sd == 1){
ty = y1;
for (y2 = 0; y2 < h2; ++y2)
{
tx = x1;
for (x2 = 0; x2 < w2; ++x2)
{
if (Needle[(4*x2)+(y2*Stride2)+3] < HayStack[(4*tx)+(ty*Stride1)+3]+v
&& Needle[(4*x2)+(y2*Stride2)+3] > HayStack[(4*tx)+(ty*Stride1)+3]-v
&& Needle[(4*x2)+(y2*Stride2)+2] < HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] > HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] < HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] > HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] < HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] > HayStack[(4*tx)+(ty*Stride1)]-v)
tx++;
else
goto NoMatch;
}
ty++;
}
} else if (sd == 2){
ty = y1 + h2 - 1;
for (y2 = h2 - 1; y2 > -1; --y2)
{
tx = x1;
for (x2 = 0; x2 < w2; ++x2)
{
if (Needle[(4*x2)+(y2*Stride2)+3] < HayStack[(4*tx)+(ty*Stride1)+3]+v
&& Needle[(4*x2)+(y2*Stride2)+3] > HayStack[(4*tx)+(ty*Stride1)+3]-v
&& Needle[(4*x2)+(y2*Stride2)+2] < HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] > HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] < HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] > HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] < HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] > HayStack[(4*tx)+(ty*Stride1)]-v)
tx++;
else
goto NoMatch;
}
ty--;
}
} else if (sd == 3){
ty = y1 + h2 - 1;
for (y2 = h2 - 1; y2 > -1; --y2)
{
tx = x1 + w2 - 1;
for (x2 = w2 - 1; x2 > -1; --x2)
{
if (Needle[(4*x2)+(y2*Stride2)+3] < HayStack[(4*tx)+(ty*Stride1)+3]+v
&& Needle[(4*x2)+(y2*Stride2)+3] > HayStack[(4*tx)+(ty*Stride1)+3]-v
&& Needle[(4*x2)+(y2*Stride2)+2] < HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] > HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] < HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] > HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] < HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] > HayStack[(4*tx)+(ty*Stride1)]-v)
tx--;
else
goto NoMatch;
}
ty--;
}
} else if (sd == 4){
ty = y1;
for (y2 = 0; y2 < h2; ++y2)
{
tx = x1 + w2 - 1;
for (x2 = w2 - 1; x2 > -1; --x2)
{
if (Needle[(4*x2)+(y2*Stride2)+3] < HayStack[(4*tx)+(ty*Stride1)+3]+v
&& Needle[(4*x2)+(y2*Stride2)+3] > HayStack[(4*tx)+(ty*Stride1)+3]-v
&& Needle[(4*x2)+(y2*Stride2)+2] < HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] > HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] < HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] > HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] < HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] > HayStack[(4*tx)+(ty*Stride1)]-v)
tx--;
else
goto NoMatch;
}
ty++;
}
}

x[0] = x1; y[0] = y1;
return 0;
NoMatch:;
}
}

x[0] = -1; y[0] = -1;
return -1;
}

int PixelAverage1(unsigned char * Needle, int w, int h, int Stride, long long int * ARGB_A)
{
int x, y;

ARGB_A[0] = 0;
ARGB_A[1] = 0;
ARGB_A[2] = 0;
ARGB_A[3] = 0;

for (x = 0; x < w; x++)
{
for (y = 0; y < h; y++)
{
ARGB_A[0] += Needle[(4*x)+(y*Stride)+3];
ARGB_A[1] += Needle[(4*x)+(y*Stride)+2];
ARGB_A[2] += Needle[(4*x)+(y*Stride)+1];
ARGB_A[3] += Needle[(4*x)+(y*Stride)];
}
}
}

long long int PixelAverage2(unsigned char * Needle, int w, int h, int Stride, long long int * ARGB_A)
{
int x, y, A0H, A0L, A1H, A1L, A2H, A2L, A3H, A3L;
long long int c = 0;

A0H = ARGB_A[0] + 25;
A0L = ARGB_A[0] - 25;

A1H = ARGB_A[1] + 25;
A1L = ARGB_A[1] - 25;

A2H = ARGB_A[2] + 25;
A2L = ARGB_A[2] - 25;

A3H = ARGB_A[3] + 25;
A3L = ARGB_A[3] - 25;

for (x = 0; x < w; x++)
{
for (y = 0; y < h; y++)
{
if (Needle[(4*x)+(y*Stride)+3] > A0H || Needle[(4*x)+(y*Stride)+3] < A0L)
c ++;

if (Needle[(4*x)+(y*Stride)+2] > A1H || Needle[(4*x)+(y*Stride)+2] < A1L)
c ++;

if (Needle[(4*x)+(y*Stride)+1] > A2H || Needle[(4*x)+(y*Stride)+1] < A2L)
c ++;

if (Needle[(4*x)+(y*Stride)] > A3H || Needle[(4*x)+(y*Stride)] < A3L)
c ++;
}
}

return c;
}


tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Wow....I'll need to have a proper look through this. There are some bugs in my original code that I know of and need to fix, but I am surprised this code works...especially using:

long long int

without the need for the ASM to use temporary pointers.

Thanks for the contribution!

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

Wow....I'll need to have a proper look through this. There are some bugs in my original code that I know of and need to fix, but I am surprised this code works...especially using:

long long int

without the need for the ASM to use temporary pointers.

Thanks for the contribution!


long long int works fine for addition and subtraction in everything i've ever done in AHK MCode. Anything else and the compiler tries to use __allmul/__alldiv/and so on which can't be linked into the ASM from inside AHK so it fails.


What bugs do you mean? I looked at the code for a long time trying to make it faster and I didn't find any bugs. The only two things I found to be slightly off was the "w" and "h" paramaters don't act like they do in built in ahk imagesearch and the Gdip image search also matches alpha whereas the built in ahk imagesearch doesn't.

I was also reading the other people's posts and I think I could add the *Trans option without to much trouble if it's still wanted.

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

More changes and speed improvements, also new features and a few bug fixes happy.png

Total list of modifications over the original Gdip_ImageSearch:
 

  • FIXED: "w" and "h" paramaters, they now work as they do in AHK scaling the needle image - they used to act as the width and height of the search box
  • FIXED: "Variation" paramater, it is now used correctly - it used to calculate off by one in the MCode
  • ADDED: "x2" and "y2" paramaters, they act exactily as the AHK imagesearch paramaters work
  • ADDED: "Trans" paramater, it acts exactily as the AHK imagesearch paramater works except it only accepts numbers (FF00FF is invalid - must be 0xFF00FF or decimal)
  • ADDED: "sd" paramater, it determines which way the needle is scanned through (0 = auto detect | 1 = left->right top->bottom | 2 = left->right bottom->top | 3 = right->left bottom->top | 4 = right->left top->bottom) NOTE: manually entering the best "sd" paramater for a needle will give slightly better performance (10-20 MS) than using auto (0) unless the needle is very large (> 500x500) and mostly the same color
  • ADDED: Auto search-pixel-first internal code - it finds a unique pixel that doesn't fall within +- 25 of the average pixel color and checks that pixel against the haystack image first before checking the rest

 

  • CHANGES: Moved the C pointer math into the if statements and removed the temporary variables they used to use
  • CHANGES: Changed the if in the C pointer math from: "if (N[#] > H[#]+V || N[#] < H[#]-V) nomatch" to: "if (N[#] < H[#]+V && N[#] < H[#]-V) continue checking"
  • CHANGES: When Trans is used the function makes an internal copy of the needle and C code now converts any pixel in the needle image that matches it to be truly transparent
  • CHANGES: 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
  • CHANGES: The Gdip_ImageSearch function now frees the RAM used by the MCode hex variables after it converts them into binary variables
  • CHANGES: The Gdip_ImageSearch function will only search within the search coordinates where a complete needle match is possible - it won't search for a 20 pixel needle when it only has 19 pixels worth of space to search

All of those modifications add features and or speed up the search process by huge amounts.

These are my results from the test I ran using 350x280~ images:
 

  • At a 98%~ match on 50%+ of the haystack using 50 Variation and it took 190~ MS to fail finding the image
  • At a 100% match using 50 variation setting "sd" to auto it took 98~ MS to find the image
  • At a 100% match using 50 variation manually setting the "sd" value it took 68 MS to find the image

Using the built in AHk image search it took 800x - 900x longer to do the same searches.


The AHK code:

Gdip_ImageSearch(pBitmapHayStack, pBitmapNeedle, ByRef x, ByRef y, sx1="", sy1="", sx2="", sy2="", Variation=0, Trans=0, w=0, h=0, sd=0)
{
static _ImageSearch1, _ImageSearch2, _PixelSearch1, _PixelSearch2

if !_ImageSearch1
{
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"

VarSetCapacity(_ImageSearch1, StrLen(MCode_ImageSearch1)//2)
Loop % StrLen(MCode_ImageSearch1)//2 ;%
NumPut("0x" SubStr(MCode_ImageSearch1, (2*A_Index)-1, 2), _ImageSearch1, A_Index-1, "char")
MCode_ImageSearch1 := ""

VarSetCapacity(_ImageSearch2, StrLen(MCode_ImageSearch2)//2)
Loop % StrLen(MCode_ImageSearch2)//2 ;%
NumPut("0x" SubStr(MCode_ImageSearch2, (2*A_Index)-1, 2), _ImageSearch2, A_Index-1, "char")
MCode_ImageSearch2 := ""
}

if !_PixelSearch1
{
MCode_PixelAverage1 := "8B4C24148B44240833D2891189510489510889510C8951108951143BC27E54538B5C2414558B6C240C565745894424248B7C2"
. "41C3BFA7E2E8BF58D9B000000000FB646019901011151040FB6069901410811510C0FB646FF9901411011511403F34F75DC33D283C504FF4C242475C15"
. "F5E5D5BC3"

MCode_PixelAverage2 := "83EC288B4C243C568B31578D7E1983C6E7897424148B71088B4910897C24108D7E1983C6E733C08974241C8D711983C1E733D"
. "2894424288944242C897C241889742420894C242489442444394424380F8EDF0000008B7C24348B4C243C83C70253897C24105533DB895C241085C90F8"
. "EA0000000807F01000F84810000000FB60F8BF08BEA3B4C24187D063B4C241C7F0683C00183D2000FB64FFF3B4C24207D063B4C24247F0683C00183D20"
. "00FB64FFE3B4C24287D063B4C242C7F0683C00183D2008BC82BCE8BF21BF578327F0583F901722B3B7424347C257F063B4C2430721D8B5C24508B6C244"
. "C892B8B5C24108B6C2454895D00894C2430897424348B4C2444037C244843895C24103BD90F8C60FFFFFF8B74244C8B7C24144683C7048974244C897C2"
. "4143B7424400F8C34FFFFFF5D5B5F5E83C428C3"

VarSetCapacity(_PixelSearch1, StrLen(MCode_PixelAverage1)//2)
Loop % StrLen(MCode_PixelAverage1)//2 ;%
NumPut("0x" SubStr(MCode_PixelAverage1, (2*A_Index)-1, 2), _PixelSearch1, A_Index-1, "char")
MCode_PixelAverage1 := ""

VarSetCapacity(_PixelSearch2, StrLen(MCode_PixelAverage2)//2)
Loop % StrLen(MCode_PixelAverage2)//2 ;%
NumPut("0x" SubStr(MCode_PixelAverage2, (2*A_Index)-1, 2), _PixelSearch2, A_Index-1, "char")
MCode_PixelAverage2 := ""
}

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)
return -5
}
if (nWidth > hWidth || nHeight > hHeight){
Gdip_DisposeImage(pBitmapNeedle)
return -6
}
}

;Sets/corrects search box and needle scan direction
sx1 := (sx1 = "") ? 0 : sx1
, sy1 := (sy1 = "") ? 0 : sy1
, sx2 := (sx2 = "") ? hWidth-sx1 : sx2
, sy2 := (sy2 = "") ? hHeight-sy1 : sy2
, sd := (sd < 0 || sd > 4) ? 1 : sd

if (sx1 < 0 || sy1 < 0){
if (w || h)
Gdip_DisposeImage(pBitmapNeedle)
return -7
}

;Detects to-small search boxes
if ((sx2 - sx1) < nWidth || (sy2 - sy1) < nHeight){
if (w || h)
Gdip_DisposeImage(pBitmapNeedle)
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))
sx2 := hWidth - nWidth
if (sy2 > (hHeight - nHeight))
sy2 := hHeight - nHeight

;Detects invalid search boxes
if (sx2 <= sx1 || sy2 <= sy1){
if (w || h)
Gdip_DisposeImage(pBitmapNeedle)
return -9
}

;Averages the needle in 4 chunks counting the number of pixels(R, G &  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){
;Split the needle into 4 corners
pBitmap_Needle_S1 := Gdip_CloneBitmapArea(pBitmapNeedle, 0, 0, Floor(nWidth / 2), Floor(nHeight / 2))
, pBitmap_Needle_S2 := Gdip_CloneBitmapArea(pBitmapNeedle, 0, Floor(nHeight / 2), Floor(nWidth / 2), Ceil(nHeight / 2))
, pBitmap_Needle_S3 := Gdip_CloneBitmapArea(pBitmapNeedle, Floor(nWidth / 2), Floor(nHeight / 2), Ceil(nWidth / 2), Ceil(nHeight / 2))
, pBitmap_Needle_S4 := Gdip_CloneBitmapArea(pBitmapNeedle, Floor(nWidth / 2), 0, Ceil(nWidth / 2), Floor(nHeight / 2))
, Last_Score := 0
, sd := 1

;Loop through the corners one at a time
loop, 4
{
Gdip_GetImageDimensions(pBitmap_Needle_S%A_Index%, Sector_Width, Sector_Height)
if !(Sector_Width && Sector_Height){
;Cleanup
if (w || h)
Gdip_DisposeImage(pBitmapNeedle)
Loop, 4
Gdip_DisposeImage(pBitmap_Needle_S%A_Index%)

return -10
}

if (Gdip_LockBits(pBitmap_Needle_S%A_Index%, 0, 0, Sector_Width, Sector_Height, Sector_Stride, Sector_Scan, Sector_BitmapData)){
;Cleanup
if (w || h)
Gdip_DisposeImage(pBitmapNeedle)
Loop, 4
Gdip_DisposeImage(pBitmap_Needle_S%A_Index%)

return -11
}

;Average the R G B values of the pixels in this corner of the image
VarSetCapacity(Sector_Averages, 8 * 3, 0)
, DllCall(&_PixelSearch1, "UInt", Sector_Scan, "Int", Sector_Width, "Int", Sector_Height, "Int", Sector_Stride, "UInt", &Sector_Averages)

loop, 3
N := NumGet(Sector_Averages, (A_Index - 1) * 8, "Int64") // (Sector_Width * Sector_Height)
, NumPut(N, Sector_Averages, (A_Index - 1) * 8, "Int64")

;Find the total number of R G B values that do not fall within +- 25 of the averages
;Also find a pixel that does not fall within +- 25 of the R G B averages to use as a search-first location for the needle
uX := -1, uY := -1
, Sector_Score := DllCall(&_PixelSearch2, "UInt", Sector_Scan, "Int", Sector_Width, "Int", Sector_Height, "Int", Sector_Stride, "UInt", &Sector_Averages, "int*", uX, "int*", uY, "cdecl int64")
, Gdip_UnlockBits(pBitmap_Needle_S%A_Index%, Sector_BitmapData)
, Gdip_DisposeImage(pBitmap_Needle_S%A_Index%)

;Remember the corner with the highest rating
if (Sector_Score > Last_Score){
sd := A_Index, Last_Score := Sector_Score

;Records the x/y of a unique pixel (does not fall within +- 25 of the average pixel color
;None of this is used if Variation is not set
if (Variation && uX != -1 && uY != -1){
if (A_Index = 1)
suX := uX, suY := uY
else if (A_Index = 2)
suX := uX, suY := Floor(nHeight / 2) + uY
else if (A_Index = 3)
suX := Floor(nWidth / 2) + uX, suY := Floor(nHeight / 2) + uY
else if (A_Index = 4)
suX := Floor(nWidth / 2) + uX, suY := uY
}
}
}
}

if (sd = 0)
sd := 1

;Sets the default search-first location for variation searches if none was set yet
suX := (suX = "") ? 0 : suX
, suY := (suY = "") ? 0 : suY

;If Trans is used and the needle hasn't already been copied through scaling create a copy because it might be modified by the imagesearch code
if (!w && !h && Trans)
pBitmapNeedle := Gdip_CloneBitmapArea(pBitmapNeedle, 0, 0, nWidth, nHeight)

E1 := Gdip_LockBits(pBitmapHayStack, 0, 0, hWidth, hHeight, Stride1, Scan01, BitmapData1)
, E2 := Gdip_LockBits(pBitmapNeedle, 0, 0, nWidth, nHeight, Stride2, Scan02, BitmapData2)
if (E1 || E2){
if (w || h || Trans)
Gdip_DisposeImage(pBitmapNeedle)
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 := y := 0
, E := DllCall(((Variation = 0) ? &_ImageSearch1 : &_ImageSearch2), "int*", x, "int*", y, "uint", Scan01, "uint", Scan02, "int", nWidth, "int", nHeight, "int", Stride1
, "int", Stride2, "int", sx1, "int", sy1, "int", sx2, "int", sy2, "int*", Trans, "int", Variation, "int", sd, "int", suX, "int", suY, "cdecl int")

Gdip_UnlockBits(pBitmapHayStack, BitmapData1), Gdip_UnlockBits(pBitmapNeedle, BitmapData2)

if (w || h || Trans)
Gdip_DisposeImage(pBitmapNeedle)

return (E = "") ? -13 : E
}


The C code:
int Gdip_ImageSearch1(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, unsigned char * Trans, int v, int sd, int suX, int suY)
{
int y1, y2, x1, x2, tx, ty;

if (Trans[0] || Trans[1] || Trans[2])
{
for (y1 = 0; y1 < nh; y1++)
{
for (x1 = 0; x1 < nw; x1++)
{
if (Needle[(4*x1)+(y1*Stride2)+2] == Trans[2]
&& Needle[(4*x1)+(y1*Stride2)+1] == Trans[1]
&& Needle[(4*x1)+(y1*Stride2)] == Trans[0])
Needle[(4*x1)+(y1*Stride2)+3] = 0;
}
}
}

for (y1 = sy1; y1 < sy2; y1++)
{
for (x1 = sx1; x1 < sx2; x1++)
{
if (sd == 1){
ty = y1;
for (y2 = 0; y2 < nh; y2++)
{
tx = x1;
for (x2 = 0; x2 < nw; x2++)
{
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;
}
ty++;
}
} else if (sd == 2){
ty = y1 + nh - 1;
for (y2 = nh - 1; y2 > -1; y2--)
{
tx = x1;
for (x2 = 0; x2 < nw; x2++)
{
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;
}
ty--;
}
} else if (sd == 3){
ty = y1 + nh - 1;
for (y2 = nh - 1; y2 > -1; y2--)
{
tx = x1 + nw - 1;
for (x2 = nw - 1; x2 > -1; x2--)
{
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;
}
ty--;
}
} else if (sd == 4){
ty = y1;
for (y2 = 0; y2 < nh; y2++)
{
tx = x1 + nw - 1;
for (x2 = nw - 1; x2 > -1; x2--)
{
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;
}
ty++;
}
}

Foundx[0] = x1; Foundy[0] = y1;
return 0;
NoMatch:;
}
}

Foundx[0] = -1; Foundy[0] = -1;
return -1;
}

int Gdip_ImageSearch2(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, unsigned char * Trans, int v, int sd, int suX, int suY)
{
int y1, y2, x1, x2, tx, ty;

if (Trans[0] || Trans[1] || Trans[2])
{
for (y1 = 0; y1 < nh; y1++)
{
for (x1 = 0; x1 < nw; x1++)
{
if (Needle[(4*x1)+(y1*Stride2)+2] == Trans[2]
&& Needle[(4*x1)+(y1*Stride2)+1] == Trans[1]
&& Needle[(4*x1)+(y1*Stride2)] == Trans[0])
Needle[(4*x1)+(y1*Stride2)+3] = 0;
}
}
}

for (y1 = sy1; y1 < sy2; y1++)
{
for (x1 = sx1; x1 < sx2; x1++)
{
if (Needle[(4*suX)+(suY*Stride2)] <= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]+v
&& Needle[(4*suX)+(suY*Stride2)] >= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]-v
|| Needle[(4*suX)+(suY*Stride2)+3] == 0)
{
if (sd == 1){
ty = y1;
for (y2 = 0; y2 < nh; y2++)
{
tx = x1;
for (x2 = 0; x2 < nw; x2++)
{
if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
tx++;
else
goto NoMatch;
}
ty++;
}
} else if (sd == 2){
ty = y1 + nh - 1;
for (y2 = nh - 1; y2 > -1; y2--)
{
tx = x1;
for (x2 = 0; x2 < nw; x2++)
{
if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
tx++;
else
goto NoMatch;
}
ty--;
}
} else if (sd == 3){
ty = y1 + nh - 1;
for (y2 = nh - 1; y2 > -1; y2--)
{
tx = x1 + nw - 1;
for (x2 = nw - 1; x2 > -1; x2--)
{
if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
tx--;
else
goto NoMatch;
}
ty--;
}
} else if (sd == 4){
ty = y1;
for (y2 = 0; y2 < nh; y2++)
{
tx = x1 + nw - 1;
for (x2 = nw - 1; x2 > -1; x2--)
{
if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
tx--;
else
goto NoMatch;
}
ty++;
}
}
} else
continue;

Foundx[0] = x1; Foundy[0] = y1;
return 0;
NoMatch:;
}
}

Foundx[0] = -1; Foundy[0] = -1;
return -1;
}

int PixelAverage1(unsigned char * Needle, int w, int h, int Stride, long long int * RGB_A)
{
int x, y;

RGB_A[0] = 0;
RGB_A[1] = 0;
RGB_A[2] = 0;

for (x = 0; x < w; x++)
{
for (y = 0; y < h; y++)
{
RGB_A[0] += Needle[(4*x)+(y*Stride)+2];
RGB_A[1] += Needle[(4*x)+(y*Stride)+1];
RGB_A[2] += Needle[(4*x)+(y*Stride)];
}
}
}

long long int PixelAverage2(unsigned char * Needle, int w, int h, int Stride, long long int * RGB_A, int * uX, int * uY)
{
int x, y, A0H, A0L, A1H, A1L, A2H, A2L;
long long int c = 0, lc = 0, ld = 0;

A0H = RGB_A[0] + 25;
A0L = RGB_A[0] - 25;

A1H = RGB_A[1] + 25;
A1L = RGB_A[1] - 25;

A2H = RGB_A[2] + 25;
A2L = RGB_A[2] - 25;

for (x = 0; x < w; x++)
{
for (y = 0; y < h; y++)
{
if (Needle[(4*x)+(y*Stride)+3] == 0)
continue;

lc = c;

if (Needle[(4*x)+(y*Stride)+2] >= A0H || Needle[(4*x)+(y*Stride)+2] <= A0L)
c ++;

if (Needle[(4*x)+(y*Stride)+1] >= A1H || Needle[(4*x)+(y*Stride)+1] <= A1L)
c ++;

if (Needle[(4*x)+(y*Stride)] >= A2H || Needle[(4*x)+(y*Stride)] <= A2L)
c ++;

if (c - lc >= 1 && c - lc >= ld){
uX[0] = x;
uY[0] = y;
ld = c - lc;
}
}
}

return c;
}



EDIT: fixed a dllcall mistake that made the average color code return false values.

Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010
More changes and speed improvements (again).

I merged the 2 PixelSearch functions from the last version I posted into one MCode function and removed most of that code from the AHK function.

The AHK code:
Gdip_ImageSearch(pBitmapHayStack="", pBitmapNeedle="", ByRef x="", ByRef y="", sx1="", sy1="", sx2="", sy2="", Variation=0, Trans=0, w=0, h=0, sd=0)
{
	static _ImageSearch1, _ImageSearch2, _PixelAverage
	
	if !_ImageSearch1
	{
		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", "UInt", &_ImageSearch1, "UInt", VarSetCapacity(_ImageSearch1), "uint", 0x40, "uint*", 0)
		, DllCall("VirtualProtect", "UInt", &_ImageSearch2, "UInt", VarSetCapacity(_ImageSearch2), "uint", 0x40, "uint*", 0)
		, DllCall("VirtualProtect", "UInt", &_PixelAverage, "UInt", VarSetCapacity(_PixelAverage), "uint", 0x40, "uint*", 0)
	}
	
	;Alows the MCode to be setup before a imagesearch is really needed
	if (pBitmapHayStack = "")
		return
	
	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)
			return -5
		}
		if (nWidth > hWidth || nHeight > hHeight){
			Gdip_DisposeImage(pBitmapNeedle)
			return -6
		}
	}
	
	;Sets/corrects search box and needle scan direction
	sx1 := (sx1 = "") ? 0 : sx1
	, sy1 := (sy1 = "") ? 0 : sy1
	, sx2 := (sx2 = "") ? hWidth-sx1 : sx2
	, sy2 := (sy2 = "") ? hHeight-sy1 : sy2
	, sd := (sd < 0 || sd > 4) ? 1 : sd
	
	if (sx1 < 0 || sy1 < 0){
		if (w || h)
			Gdip_DisposeImage(pBitmapNeedle)
		return -7
	}
	
	;Detects to-small search boxes
	if ((sx2 - sx1) < nWidth || (sy2 - sy1) < nHeight){
		if (w || h)
			Gdip_DisposeImage(pBitmapNeedle)
		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))
		sx2 := hWidth - nWidth
	if (sy2 > (hHeight - nHeight))
		sy2 := hHeight - nHeight
	
	;Detects invalid search boxes
	if (sx2 <= sx1 || sy2 <= sy1){
		if (w || h)
			Gdip_DisposeImage(pBitmapNeedle)
		return -9
	}
	
	;If Trans is used and the needle hasn't already been copied through scaling create a copy because it might be modified by the imagesearch code
	if (!w && !h && Trans)
		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)
			Gdip_DisposeImage(pBitmapNeedle)
		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)
		, sd := DllCall(&_PixelAverage, "UInt", Scan02, "Int", Stride2, "Int", nWidth, "Int", nHeight, "UInt", &TempData, "UInt*", suX
		, "UInt*", suY, "cdecl int")
		, VarSetCapacity(TempData, 0)
	}
	
	if (sd = 0)
		sd := 1
	
	;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
	
	E := Gdip_LockBits(pBitmapHayStack, 0, 0, hWidth, hHeight, Stride1, Scan01, BitmapData1)
	if (E){
		if (w || h || Trans)
			Gdip_DisposeImage(pBitmapNeedle)
		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, "uint", Scan01, "uint", Scan02, "int", nWidth
	, "int", nHeight, "int", Stride1, "int", Stride2, "int", sx1, "int", sy1, "int", sx2, "int", sy2, "Uint", &Trans, "int", Variation
	, "int", sd, "int", suX, "int", suY, "cdecl int")
	, Gdip_UnlockBits(pBitmapHayStack, BitmapData1), Gdip_UnlockBits(pBitmapNeedle, BitmapData2)
	
	if (w || h || Trans)
		Gdip_DisposeImage(pBitmapNeedle)
	
	return (E = "") ? -13 : E
}

The C code:
int Gdip_ImageSearch1(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, unsigned char * Trans, int v, int sd, int suX, int suY)
{
	int y1, y2, x1, x2, tx, ty;
	
	if (Trans[0] || Trans[1] || Trans[2])
	{
		for (y1 = 0; y1 < nh; y1++)
		{
			for (x1 = 0; x1 < nw; x1++)
			{
				if (Needle[(4*x1)+(y1*Stride2)+2] == Trans[2]
				&& Needle[(4*x1)+(y1*Stride2)+1] == Trans[1]
				&& Needle[(4*x1)+(y1*Stride2)] == Trans[0])
					Needle[(4*x1)+(y1*Stride2)+3] = 0;
			}
		}
	}
	
	for (y1 = sy1; y1 < sy2; y1++)
	{
		for (x1 = sx1; x1 < sx2; x1++)
		{
			if (sd == 1){
				ty = y1;
				for (y2 = 0; y2 < nh; y2++)
				{
					tx = x1;
					for (x2 = 0; x2 < nw; x2++)
					{
						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;
					}
					ty++;
				}
			} else if (sd == 2){
				ty = y1 + nh - 1;
				for (y2 = nh - 1; y2 > -1; y2--)
				{
					tx = x1;
					for (x2 = 0; x2 < nw; x2++)
					{
						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;
					}
					ty--;
				}
			} else if (sd == 3){
				ty = y1 + nh - 1;
				for (y2 = nh - 1; y2 > -1; y2--)
				{
					tx = x1 + nw - 1;
					for (x2 = nw - 1; x2 > -1; x2--)
					{
						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;
					}
					ty--;
				}
			} else if (sd == 4){
				ty = y1;
				for (y2 = 0; y2 < nh; y2++)
				{
					tx = x1 + nw - 1;
					for (x2 = nw - 1; x2 > -1; x2--)
					{
						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;
					}
					ty++;
				}
			}
			
			Foundx[0] = x1; Foundy[0] = y1;
			return 0;
			NoMatch:;
		}
	}
	
	Foundx[0] = -1; Foundy[0] = -1;
	return -1;
}

int Gdip_ImageSearch2(int * Foundx, int * Foundy, unsigned char * HayStack, unsigned char * Needle, int nw, int nh, int Stride1, int Stride2, int sx1, int sy1, int sx2, int sy2, unsigned char * Trans, int v, int sd, int suX, int suY)
{
	int y1, y2, x1, x2, tx, ty;
	
	if (Trans[0] || Trans[1] || Trans[2])
	{
		for (y1 = 0; y1 < nh; y1++)
		{
			for (x1 = 0; x1 < nw; x1++)
			{
				if (Needle[(4*x1)+(y1*Stride2)+2] == Trans[2]
				&& Needle[(4*x1)+(y1*Stride2)+1] == Trans[1]
				&& Needle[(4*x1)+(y1*Stride2)] == Trans[0])
					Needle[(4*x1)+(y1*Stride2)+3] = 0;
			}
		}
	}
	
	for (y1 = sy1; y1 < sy2; y1++)
	{
		for (x1 = sx1; x1 < sx2; x1++)
		{
			if (Needle[(4*suX)+(suY*Stride2)] <= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]+v
			&& Needle[(4*suX)+(suY*Stride2)] >= HayStack[(4*(x1 + suX))+((y1 + suY)*Stride1)]-v
			|| Needle[(4*suX)+(suY*Stride2)+3] == 0)
			{
				if (sd == 1){
					ty = y1;
					for (y2 = 0; y2 < nh; y2++)
					{
						tx = x1;
						for (x2 = 0; x2 < nw; x2++)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx++;
							else
								goto NoMatch;
						}
						ty++;
					}
				} else if (sd == 2){
					ty = y1 + nh - 1;
					for (y2 = nh - 1; y2 > -1; y2--)
					{
						tx = x1;
						for (x2 = 0; x2 < nw; x2++)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx++;
							else
								goto NoMatch;
						}
						ty--;
					}
				} else if (sd == 3){
					ty = y1 + nh - 1;
					for (y2 = nh - 1; y2 > -1; y2--)
					{
						tx = x1 + nw - 1;
						for (x2 = nw - 1; x2 > -1; x2--)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx--;
							else
								goto NoMatch;
						}
						ty--;
					}
				} else if (sd == 4){
					ty = y1;
					for (y2 = 0; y2 < nh; y2++)
					{
						tx = x1 + nw - 1;
						for (x2 = nw - 1; x2 > -1; x2--)
						{
							if (Needle[(4*x2)+(y2*Stride2)+2] <= HayStack[(4*tx)+(ty*Stride1)+2]+v
							&& Needle[(4*x2)+(y2*Stride2)+2] >= HayStack[(4*tx)+(ty*Stride1)+2]-v
							&& Needle[(4*x2)+(y2*Stride2)+1] <= HayStack[(4*tx)+(ty*Stride1)+1]+v
							&& Needle[(4*x2)+(y2*Stride2)+1] >= HayStack[(4*tx)+(ty*Stride1)+1]-v
							&& Needle[(4*x2)+(y2*Stride2)] <= HayStack[(4*tx)+(ty*Stride1)]+v
							&& Needle[(4*x2)+(y2*Stride2)] >= HayStack[(4*tx)+(ty*Stride1)]-v
							|| Needle[(4*x2)+(y2*Stride2)+3] == 0)
								tx--;
							else
								goto NoMatch;
						}
						ty++;
					}
				}
			} else
				continue;
			
			Foundx[0] = x1; Foundy[0] = y1;
			return 0;
			NoMatch:;
		}
	}
	
	Foundx[0] = -1; Foundy[0] = -1;
	return -1;
}

int PixelAverageScan(unsigned char * Needle, int Stride, int w, int h, int * Temp, int * suX, int * suY)
{
	unsigned int R, G, B;
	int x, y, tx, ty;
	int RL, GL, BL;
	int RH, GH, BH;
	int Count, Corner;
	int ScanDirection = 0;
	int LastDifference, LastCount;
	int LargestCount = 0;
	int * SX, * SY, * SW, * SH;
	
	suX[0] = 0;
	suY[0] = 0;
	
	SX = Temp;
	SY = SX + 5;
	SW = SY + 5;
	SH = SW + 5;
	
	SW[1] = w / 2;
	SH[1] = h / 2;
	
	SY[2] = h / 2;
	SW[2] = w / 2;
	SH[2] = h;
	
	SX[3] = w / 2;
	SY[3] = h / 2;
	SW[3] = w;
	SH[3] = h;
	
	SX[4] = w / 2;
	SW[4] = w;
	SH[4] = h / 2;
	
	//The maximum possible safe image size is 67371264 total pixels
	//Larger images can be used but they may give false average color readings
	for (Corner = 1; Corner < 5; Corner++)
	{
		R = 0;
		G = 0;
		B = 0;
		for (y = SY[Corner]; y < SH[Corner]; y++)
		{
			for (x = SX[Corner]; x < SW[Corner]; x++)
			{
				R += Needle[(4*x)+(y*Stride)+2];
				G += Needle[(4*x)+(y*Stride)+1];
				B += Needle[(4*x)+(y*Stride)];
			}
		}
		
		R = R / ((SW[Corner] - SX[Corner]) * (SH[Corner] - SY[Corner]));
		G = G / ((SW[Corner] - SX[Corner]) * (SH[Corner] - SY[Corner]));
		B = B / ((SW[Corner] - SX[Corner]) * (SH[Corner] - SY[Corner]));
		
		RH = R + 25;
		RL = R - 25;
		GH = G + 25;
		GL = G - 25;
		BH = B + 25;
		BL = B - 25;
		
		Count = 0;
		LastDifference = 0;
		for (y = SY[Corner]; y < SH[Corner]; y++)
		{
			for (x = SX[Corner]; x < SW[Corner]; x++)
			{
				if (Needle[(4*x)+(y*Stride)+3] == 0)
					continue;
				
				LastCount = Count;
				
				if (Needle[(4*x)+(y*Stride)+2] >= RH || Needle[(4*x)+(y*Stride)+2] <= RL)
					Count ++;
				
				if (Needle[(4*x)+(y*Stride)+1] >= GH || Needle[(4*x)+(y*Stride)+1] <= GL)
					Count ++;
				
				if (Needle[(4*x)+(y*Stride)] >= BH || Needle[(4*x)+(y*Stride)] <= BL)
					Count ++;
				
				
				if ((Count - LastCount) > LastDifference)
				{
					tx = x;
					ty = y;
					LastDifference = Count - LastCount;
				}
			}
		}
		
		if (Count > LargestCount)
		{
			LargestCount = Count;
			ScanDirection = Corner;
			suX[0] = tx;
			suY[0] = ty;
		}
	}
	
	return ScanDirection;
}


Rseding91
  • Members
  • 703 posts
  • Last active: Apr 02 2016 05:05 AM
  • Joined: 07 Jun 2010
Slight edit (not worth a repost): I fixed a bug with small image searches in the above 2 posts.

if (sd = 0)
	sd := 1


RobS1990
  • Members
  • 25 posts
  • Last active: May 13 2015 03:53 AM
  • Joined: 15 Oct 2012
wow - I just did a test comparing this to AHK built-in imagesearch, really great work!

I have a noob question though. What do I put in the ByRef x and ByRef y parameters? What do they represent? Thanks!

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

wow - I just did a test comparing this to AHK built-in imagesearch, really great work!

I have a noob question though. What do I put in the ByRef x and ByRef y parameters? What do they represent? Thanks!



Those are the found x and found y values. The location the imagesearch finds the to-be-searched image.

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

i'm using the original script in the first post

 

it seems to return once one match is found in the haystack. suppose i want to return ALL matches? is that possible? is there some way to iterate through all matches?



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

Fixed: sx1/sy1/sx2/sy2 values not limiting the search area as they should
Added: "Screen" option for the haystack image to screenshot the screen and search that
Added: Load-image-from-HDD option for the needle image (pass a full filepath + name)
 
The C code is still the same as my last post here so I won't repost it.
 
New AHK code:

 

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
i'm using the original script in the first post

 

it seems to return once one match is found in the haystack. suppose i want to return ALL matches? is that possible? is there some way to iterate through all matches?

 

You can use this code with the latest version of the Gdip_ImageSearch that I just posted to find all instances of a image in another:

 

 

 

Gdip_GetImageDimensions(pBitmap_HayStack, Haystack_Width, Haystack_Height)
Gdip_GetImageDimensions(pBitmap_Needle, Needle_Width, Needle_Height)
 
SX1 := 0
SY1 := 0
SX2 := Haystack_Width
SY2 := Needle_Height
Loop
{
 E := Gdip_ImageSearch(pBitmap_HayStack, pBitmap_Needle, FoundX, FoundY, SX1, SY1, SX2, SY2)
; MsgBox % E
 If (FoundX > 0 And FoundY > 0){
  FoundLocations .= "X: " FoundX ", Y: " FoundY "`r`n"
  
  If ((FoundX + 1 + Needle_Width) > Haystack_Width){
   SX1 := 0
   SY1 ++
   SY2 ++
  } Else
   SX1 := FoundX + 1
 } Else {
  SX1 := 0
  SY1 ++
  SY2 ++
 }
 
 If (SX1 + Needle_Width > SX2 Or SY1 + Needle_Height > Haystack_Height)
  Break
}
MsgBox % "Found locations: `n`n" FoundLocations



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

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

Rseding,

 

this part of your code is probably no good:

 

    ;If Trans is used and the needle hasent 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)

 

 

when i have used CloneBitmapArea in this same way, by overwriting the previous pBitmap by assigning to the same variable, i would end up with memory leaks because the previous original pBitmapNeedle was never Disposed. it was easy to notice the leak because i was looping and cloning many many times

 

in my code, ive had to use a new variable so that i could dispose the previous:

 

   pBitmap1 := Gdip_CreateBitmapFromHBITMAP(hBMP)
   Gdip_GetImageDimensions(pBitmap1, pbWidth, pbHeight)
   pBitmap2 := Gdip_CloneBitmapArea(pBitmap1, 0, 0, pbWidth, pbHeight)
   Gdip_DisposeImage(pBitmap1)

 

 

i have no use for the Trans parameter so i did not test your function. but it might be something to look into



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

Rseding,

 

this part of your code is probably no good:

 

    ;If Trans is used and the needle hasent 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)

 

 

when i have used CloneBitmapArea in this same way, by overwriting the previous pBitmap by assigning to the same variable, i would end up with memory leaks because the previous original pBitmapNeedle was never Disposed. it was easy to notice the leak because i was looping and cloning many many times

 

in my code, ive had to use a new variable so that i could dispose the previous:

 

   pBitmap1 := Gdip_CreateBitmapFromHBITMAP(hBMP)
   Gdip_GetImageDimensions(pBitmap1, pbWidth, pbHeight)
   pBitmap2 := Gdip_CloneBitmapArea(pBitmap1, 0, 0, pbWidth, pbHeight)
   Gdip_DisposeImage(pBitmap1)

 

 

i have no use for the Trans parameter so i did not test your function. but it might be something to look into

 

 

The function will clean up the new pointer it creates if it does create one. You (the user of the function) are responsible for the original pointer passed to the function.



guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
Rseding,
 
I have tried to optimize the multiple image search code, by changing the inputs to use the locked bitmap data struct, instead of the pBitmap. However, I'm not seeing the improvements that I had hoped for. My only conclusion is that perhaps the locking/unlocking of the bits does not take as much time as I had thought, and that most of the time is consumed in the C code function. 
 
If you have any better ideas on how to optimize for multiple searches, please advise.
 
Here's how I've changed it:
f9::
   mousegetpos, , , win
   pBitmapHayStack := Gdip_BitmapFromHWND(win)
   pBitmapNeedle := Gdip_CreateBitmapFromFile("myimage.png")
   
   Gdip_GetImageDimensions(pBitmapHayStack, hWidth, hHeight)
   CreateRect(HaystackRect, 0, 0, hWidth, hHeight)
   VarSetCapacity(BitmapDataHaystack, 21, 0)
   nE := DllCall("Gdiplus\GdipBitmapLockBits", "uint", pBitmapHayStack, "uint", &HaystackRect, "uint", 3, "int", 0x26200a, "uint", &BitmapDataHaystack)

   Gdip_GetImageDimensions(pBitmapNeedle, nWidth, nHeight)
   CreateRect(NeedleRect, 0, 0, nWidth, nHeight)
   VarSetCapacity(BitmapDataNeedle, 21, 0)
   hE := DllCall("Gdiplus\GdipBitmapLockBits", "uint", pBitmapNeedle, "uint", &NeedleRect, "uint", 3, "int", 0x26200a, "uint", &BitmapDataNeedle)

   if (!hE) && (!nE) {
      allimgs := FindAllLockedImageOccurances(BitmapDataHaystack, BitmapDataNeedle)
   }

   DllCall("Gdiplus\GdipBitmapUnlockBits", "uint", pBitmapHayStack, "uint", &BitmapDataHaystack)
   DllCall("Gdiplus\GdipBitmapUnlockBits", "uint", pBitmapNeedle, "uint", &BitmapDataNeedle)
   Gdip_DisposeImage(pBitmapHayStack)
   Gdip_DisposeImage(pBitmapNeedle)
   
   msgbox, %allimgs%
return





FindAllLockedImageOccurances(ByRef BitmapDataHayStack, ByRef BitmapDataNeedle)
{
   Haystack_Width := NumGet(BitmapDataHayStack, 0)
   , Haystack_Height := NumGet(BitmapDataHayStack, 4)
   Needle_Width := NumGet(BitmapDataNeedle, 0)
   , Needle_Height := NumGet(BitmapDataNeedle, 4)

   SX1 := 0
   SY1 := 0
   SX2 := Haystack_Width
   SY2 := Needle_Height
   
   Loop
   {
       E := Gdip_LockedImageSearch(BitmapDataHayStack, BitmapDataNeedle, FoundX, FoundY, SX1, SY1, SX2, SY2)
       
       If (FoundX > 0 And FoundY > 0) {
           thispoint := ""
           listAdd(thispoint, FoundX, ",")
           listAdd(thispoint, FoundY, ",")
           listAdd(thislist, thispoint, "|")
           
           If ((FoundX + 1 + Needle_Width) > Haystack_Width) {
               SX1 := 0
               SY1++
               SY2++
           } Else
               SX1 := FoundX + 1
       } Else {
           SX1 := 0
           SY1++
           SY2++
       }
       
       If (SX1 + Needle_Width > SX2 Or SY1 + Needle_Height > Haystack_Height)
           Break
   }
   
   return thislist
}



listAdd( byRef list, item, del="," ) {
   list:=( list!="" ? ( list . del . item ) : item )
   return list
}




Gdip_LockedImageSearch(ByRef BitmapDataHayStack="", ByRef BitmapDataNeedle="", ByRef x="", ByRef y="", sx1="", sy1="", sx2="", sy2="", Variation=0, sd=0)
{
   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)
   }
   
   if (BitmapDataHayStack = "") || (BitmapDataNeedle = "") {
      ; msgbox, fail -1
      return -1
   }
   
   if (Variation > 255 || Variation < 0)
      return -2
   
   hWidth := NumGet(BitmapDataHayStack, 0)
   , hHeight := NumGet(BitmapDataHayStack, 4)
   nWidth := NumGet(BitmapDataNeedle, 0)
   , nHeight := NumGet(BitmapDataNeedle, 4)
   
   if !(hWidth && hHeight && nWidth && nHeight)
      return -3
   if (nWidth > hWidth || nHeight > hHeight)
      return -4
   
   ;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
   
   ;Detects too-small search boxes and invalid search boxes
   if ((sx2 - sx1) < 1 || (sy2 - sy1) < 1)
      return -8
   
   ;Prevents searching to close to the edges: it cant 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
   
   if (sx2 = 0)
      sx2 := 1
   if (sy2 = 0)
      sy2 := 1
   
   hStride := NumGet(BitmapDataHayStack, 8)
   , hScan0 := NumGet(BitmapDataHayStack, 16)
   nStride := NumGet(BitmapDataNeedle, 8)
   , nScan0 := NumGet(BitmapDataNeedle, 16)

   ;Averages the needle in 4 chunks counting the number of pixels(R, G & B) that arent +- 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, nScan0, "Int", nStride, "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
   
   ;The dllcall parameters are the same for easier C code modification even though they arent all used on the _ImageSearch1 version
   x := 0, y := 0, Trans := 0
   , E := DllCall((Variation = 0 ? &_ImageSearch1 : &_ImageSearch2), "int*", x, "int*", y, Ptr, hScan0, Ptr, nScan0, "int", nWidth
   , "int", nHeight, "int", hStride, "int", nStride, "int", sx1, "int", sy1, "int", sx2, "int", sy2, Ptr, &Trans, "int", Variation
   , "int", sd, "int", suX, "int", suY, "cdecl int")

   return (E = "") ? -13 : E
}