Jump to content

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

Gdip_PixelSearch


  • Please log in to reply
9 replies to this topic
tic
  • Members
  • 1926 posts
  • Last active: Oct 30 2014 06:18 PM
  • Joined: 22 Apr 2007
Example of Gdip_PixelSearch. Requires the gdi+ library

pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile("image.png")
if !Gdip_PixelSearch(pBitmap, ARGB := 0xff0118d9, x, y)
	MsgBox, Pixel %ARGB% found at (%x%, %y%)
else
	MsgBox, Pixel %ARGB% not found
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
return

;#####################################################################################

Gdip_PixelSearch(pBitmap, ARGB, ByRef x, ByRef y)
{
	static _PixelSearch
	if !_PixelSearch
	{
		MCode_PixelSearch := "8B44241099535583E2035603C233F6C1F80239742418577E388B7C24148B6C24248B5424188D1C85000000008D64240033C085"
		. "D27E108BCF3929743183C00183C1043BC27CF283C60103FB3B74241C7CDF8B4424288B4C242C5F5EC700FFFFFFFF5DC701FFFFFFFF83C8FF5BC38B4C2"
		. "4288B54242C5F890189325E5D33C05BC3"

		VarSetCapacity(_PixelSearch, StrLen(MCode_PixelSearch)//2)
		Loop % StrLen(MCode_PixelSearch)//2      ;%
			NumPut("0x" SubStr(MCode_PixelSearch, (2*A_Index)-1, 2), _PixelSearch, A_Index-1, "char")
	}
	Gdip_GetImageDimensions(pBitmap, Width, Height)
	if !(Width && Height)
		return -1

	if (E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1))
		return -2

	x := y := 0
	E := DllCall(&_PixelSearch, "uint", Scan01, "int", Width, "int", Height, "int", Stride1, "uint", ARGB, "int*", x, "int*", y)
	Gdip_UnlockBits(pBitmap, BitmapData1)
	return (E = "") ? -3 : E
}

Here is the c++

int Gdip_PixelSearch(unsigned int * HayStack, int w, int h, int Stride, int ARGB, int * x, int * y)
{
	int offset = Stride/4;
	for (int y1 = 0; y1 < h; ++y1)
	{
		for (int x1 = 0; x1 < w; ++x1)
		{
			if (HayStack[x1+(y1*offset)] == ARGB)
			{
				x[0] = x1; y[0] = y1;
				return 0;
			}
		}
	}
	x[0] = -1; y[0] = -1;
	return -1;
}


Spawnova
  • Members
  • 210 posts
  • Last active: Oct 10 2014 11:32 PM
  • Joined: 29 Jun 2011
thanks!

Sven
  • Members
  • 22 posts
  • Last active: Apr 05 2012 08:03 AM
  • Joined: 03 Aug 2011
Hey tic,

how exactly did you convert your C++ functions to machine code? When using MCodeGen on your C++ functions I always get code that doesn't work. For example the pixelsearch function posted in your answer above returns
MCode(Gdip_PixelSearch@@YAHPAIHHHHPAH1@Z, "558BEC8B4514995383E2035603C233F6C1F802397510577E298B55088BF8C1E70233C039450C7E128BCA8B193B5D1874234083C1043B450C7CF04603D73B75107CDF8B4D1C83C8FF89018B4D2089015F5E5B5DC38B4D1C89018B4520893033C0EBED")

PS:
Never mind, got it sorted. After reading the forums on this matter for a couple of days, it turns out all I had to do was to set the compiler to compile .dll files in two locations, namely in General and in Code Generation.



PPS:
For the last couple of hours I've tried to extend your Gdip_Pixelsearch C++ code to fit my needs, but I seem to keep getting it wrong. What I need is something that lists all the pixels in the haystack that have the specified color or are a close (user definable between 0 and 255 shades) match to that color.

The following is based on Gdip_Imagesearch2 which I tried changing for my purposes, but I kind of lost track of what is what. Any aid you could give me would help.

int Gdip_PixelSearchEx(unsigned char * HayStack, int w, int h, int Stride, int ARGB, int * x, int * y, int * v)
{
   int tx = 1, ty = 1, sx = 1, sy = 1, h2 = 1, w2 = 1, ph, pp, Ah, Rh, Gh, Bh, Ap, Rp, Gp, Bp;
   for (int y1 = sy; y1 < h; ++y1)
   {
      for (int x1 = sx; x1 < w; ++x1)
      {
         ty = y1;
         for (int y2 = 0; y2 < h2; ++y2)
         {
            tx = x1;
            for (int x2 = 0; x2 < w2; ++x2)
            {
               ph = (4*tx)+(ty*Stride);
			   pp = 1; //(4*x2)+(y2*Stride2);

               Ah = HayStack[3+ph]; Ap = ARGB[3+pp];
               if (Ap > Ah+v || Ap < Ah-v) goto NoMatch;

               Rh = HayStack[2+ph]; Rp = ARGB[2+pp];
               if (Rp > Rh+v || Rp < Rh-v) goto NoMatch;

               Gh = HayStack[1+ph]; Gp = ARGB[1+pp];
               if (Gp > Gh+v || Gp < Gh-v) goto NoMatch;

               Bh = HayStack[ph]; Bp = ARGB[pp];
               if (Bp > Bh+v || Bp < Bh-v) goto NoMatch;
               
               tx++;
            }
            ty++;
         }
         x[0] = x1; y[0] = y1;
         return 0;
         NoMatch:
         continue;
      }
   }
   x[0] = -1; y[0] = -1;
   return -1;
}

Please don't laugh, even if the above code looks stupid to you :p

tic
  • Members
  • 1926 posts
  • Last active: Oct 30 2014 06:18 PM
  • Joined: 22 Apr 2007
Sorry Sven. Must have missed this post. To list all of the pixels that match a given colour, the correct way would be to have 2 machine code functions. The first one counts the number of times that pixel is present. The VarSetCapacity in AHK with that returned value. Then You can call the 2nd machine code function to place the X and Y coordinates into this "array" that has been created with VarSetCapacity

Wicked
  • Members
  • 504 posts
  • Last active: Jun 14 2014 06:04 PM
  • Joined: 07 Jun 2008
I would very much like to see what you came up with, Sven! :).

tic
  • Members
  • 1926 posts
  • Last active: Oct 30 2014 06:18 PM
  • Joined: 22 Apr 2007

I would very much like to see what you came up with, Sven! :).


Well here's the 2 functions you'd need. Neither compiled or tested and didn't add variation yet

int Gdip_PixelSearch(unsigned int * HayStack, int w, int h, int Stride, int ARGB)
{
   int offset = Stride/4, count = 0;
   for (int y1 = 0; y1 < h; ++y1)
   {
      for (int x1 = 0; x1 < w; ++x1)
      {
         if (HayStack[x1+(y1*offset)] == ARGB)
         {
            count++;
         }
      }
   }
   return count;
}

int Gdip_PixelSearch(unsigned int * HayStack, int w, int h, int Stride, int ARGB, unsigned int * x, unsigned int * y)
{
   int offset = Stride/4, count = 0;
   for (int y1 = 0; y1 < h; ++y1)
   {
      for (int x1 = 0; x1 < w; ++x1)
      {
         if (HayStack[x1+(y1*offset)] == ARGB)
         {
            x[count] = x1;
            y[count] = y1;
            count++;
         }
      }
   }
   return 0;
}

Call the first function. Use the return value to VarSetCapacity to x and y, then call the second function to fill the arrays of x and y coordinates

Wicked
  • Members
  • 504 posts
  • Last active: Jun 14 2014 06:04 PM
  • Joined: 07 Jun 2008
Is there a reason that, on the same image, your original (first post) Gdip_PixelSearch will return -3 on some and 0 on most? It's not to do with AHK version, because it's compiled. Any ideas what could cause this?

tic
  • Members
  • 1926 posts
  • Last active: Oct 30 2014 06:18 PM
  • Joined: 22 Apr 2007
Well....0 means it was successful, and -3 means that something went wrong in the machine code. An error with the machine code could be that it is trying to access out of bounds memory. A reason for this could be that the bitmap is not in the correct pixel format. Compare pixel formats between a working image and one that is failing:

Gdip_GetImagePixelFormat()


Wicked
  • Members
  • 504 posts
  • Last active: Jun 14 2014 06:04 PM
  • Joined: 07 Jun 2008
Would it matter if both images are being retrieved from on-screen? I checked the image my PC is capturing and the one theirs is and they're exact to the pixel.

If the format was incorrect, how would I fix this?

Wicked
  • Members
  • 504 posts
  • Last active: Jun 14 2014 06:04 PM
  • Joined: 07 Jun 2008
Hey again, tic! If it's not too much trouble, would it be possible to get Gdip_PixelSearch() with variation included?