CGDipSnapShot - GDI replacement for PixelGetColor

Post your working scripts, libraries and tools for AHK v1.1 and older
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

30 Oct 2017, 11:44

It appears as if CGDipSnapShot is gradually increasing the amount of memory it uses, the longer the program is running. The main loop of my program calls the following functions. It's memory usage increases from an initial ~7MB until it eventually lags so badly that it has to be restarted.

The program [initially] runs so much quicker than with the original pixelgetcolor code, but unfortunately I won't be able to use it if I can't get this issue fixed. I would be very grateful if you could offer any advice.

(Also, during the checkseatstatus function, I had to give the two uses of the CGDipSnapShot different names (originally they both used the variable "snap", now they have been changed to "snap1" and "snap2") even though they are used one after the other, because originally the second use was returning a result of zero each time the red colour component was read. NB: Parts of the code have been revised since I made those name changes- in case that may be relevant.)

Thank you!

Code: Select all

;------------------------------------------------------------------------
;------------------------------------------------------------------------
;------------------------CGdipSnapShot Functions-------------------------
;------------------------------------------------------------------------
;------------------------------------------------------------------------

;Average Colour Function
;Checks 100* pixels within the rectangle to find the average colour
;(*as long as dimensions are greater than 20, else it's one check per pixel per dimension <= 20)
AveCol(x,y,w,h)
{
	blue := 0
	green := 0
	red := 0
	snap := new CGdipSnapshot(x,y,w,h)
	snap.TakeSnapShot
	
	if (w > 20) then
	{
		loopw := 10
	} else {
		loopw := w
	}
	
	if (h > 20) then
	{
		looph := 10
	} else {
		looph := h
	}
	
	stepw := floor(w/10)
	steph := floor(h/10)
	
	count1 := 0
	Loop %loopw%
	{
		count2 := 0
		Loop %looph%
		{
			blue := blue + snap.Pixelsnap[count1,count2].b
			green := green + snap.Pixelsnap[count1,count2].g
			red := red + snap.Pixelsnap[count1,count2].r
			count2 := count2 + steph
		}
		count1 := count1 + stepw
	}
	Blue := (blue/(loopw*looph))
	Green := (green/(loopw*looph))
	Red := (red/(loopw*looph))
	;return Blue + Green + Red
	return [Blue,Green,Red]
}




CheckBattery(Tablex,Tabley,Tablewidth,Tableheight)
{
	Global head
	
	x := Tablex + (0.919 * TableWidth)
	y := Tabley + Head + (0.015 * (TableHeight-Head))		
	w := ((0.948 - 0.919) * TableWidth)
	h := ((0.019 - 0.015) * (TableHeight-Head))
	
	x1 := Tablex + (0.467 * TableWidth)
	y1 := Tabley + Head + (0.947 * (TableHeight-Head))
	
	colourcheck := AveCol(x,y,w,h)
	blue := colourcheck[1]
	green := colourcheck[2]
	red := colourcheck[3]
	if ((abs(blue-45)<25) and (abs(green-70)<25) and (abs(red-25)<15)) then
	{
		BatteryShown := 1
		GreenShown := 1
	} else {
		BatteryShown := 0
		GreenShown := 0
	}

	colourcheck := AveCol(x1,y1,1,1)
	blue := colourcheck[1]
	green := colourcheck[2]
	red := colourcheck[3]	
	if ((abs(Blue-158)<20) and (abs(Green-223)<20) and (abs(Red-254)<20)) or ((abs(Blue-52)<10) and (abs(Green-39)<10) and (abs(Red-31)<10)) then
	{
		;Logo (or results page) showing, so Battery not showing
	} else {
		BatteryShown := 1
	}
	
	return [greenshown,batteryshown]
}



FeltColourCheck(x,y,w,h)
{
	colourcheck := avecol(x,y,w,h)
	blue := colourcheck[1]
	green := colourcheck[2]
	red := colourcheck[3]
	tabcol := 0
	if ((abs(blue-80)<40) and (abs(green-45)<40) and (abs(red-0)<30)) then
	{
		TabCol := 1
	}
	if ((abs(blue-40)<20) and (abs(green-40)<20) and (abs(red-40)<20) and (abs(blue-red)<5)) then
	{
		TabCol := 2
	}
	if ((abs(blue-130)<40) and (abs(green-70)<25) and (abs(red-50)<20)) then
	{
		TabCol := 3
	}
	if ((abs(blue-40)<20) and (abs(green-85)<25) and (abs(red-17)<30)) then
	{
		TabCol := 4
	}
	;msgbox %blue%, %green%, %red%
	return tabcol
}



CheckSeatStatus(x,y,w,h,x1,y1,t)
{
	Global head
	fx := x + ((x1 - 0.033) * w)
	fy := y + head + ((y1 - 0.013) * (h - head))
	fw := (0.033 + 0.033) * w
	fh := (0.013 + 0.013) * (h - head)
	
	colourcheck := AveCol(fx,fy,fw,fh)
	blue := colourcheck[1]
	green := colourcheck[2]
	red := colourcheck[3]
	
	filled := 1
	open := 0
	balance := 0
	
	if ((abs(blue-120)<45) and (abs(green-75)<25) and (abs(red-0)<22) and (t = 1)) then
	{
		filled := 0
	}
	if ((abs(blue-35)<20) and (abs(green-35)<20) and (abs(red-35)<20) and (abs(blue-red)<10) and (t = 2)) then
	{
		filled := 0
	}
	if ((abs(blue-115)<25) and (abs(green-70)<25) and (abs(red-50)<20) and (t = 3)) then
	{
		filled := 0
	}
	if ((abs(blue-40)<15) and (abs(green-75)<20) and (abs(red- 20)<10) and (t = 4)) then           
	{
		filled := 0
	}
	
	if (filled = 0) then
	{
		;perfrom one more filled check, on the balance/writing under avatar, in case avatar colour is similar to background
		qx := x + ((x1 - 0.025) * w)
		qy := y + head + ((y1 + 0.055) * (h - head))
		qw := (0.025 + 0.025) * w
		qh := (0.060 - 0.055) * (h - head)

		snap1 := new CGdipSnapshot(qx,qy,qw,qh)
		snap1.TakeSnapShot
		count1 := 0
		Loop %qw%
		{
			count2 := 0
			Loop %qh%
			{
				red := snap1.Pixelsnap[count1,count2].r
				if (red > 100) then 
				balance := balance + 1
				count2 := count2 + 1
			}
			count1 := count1 + 1
		}
		if (balance > 1) then
		{
			filled := 1
		}
	}
	
	if (filled < 2) then
	{
		;check for Open seat
		ox := x + ((x1 - 0.033) * w)
		oy := y + head + ((y1 - 0.003) * (h - head))
		ow := (0.033 + 0.033) * w
		oh := (0.003 + 0.003) * (h - head)

		snap2 := new CGdipSnapshot(ox,oy,ow,oh)
		snap2.TakeSnapShot
		count1 := 0
		Loop %ow%
		{
			count2 := 0
			Loop %oh%
			{
				red := snap2.Pixelsnap[count1,count2].r
				if ((t = 1) and (red > 20)) then 
				{
					open := open + 1
				}
				if ((t = 2) and (red > 48)) then
				{
					open := open + 1
				}
				if ((t = 3) and (red > 65)) then
				{
					open := open + 1
				}
				if ((t = 4) and (red > 37)) then
				{
					open := open + 1
				}
				count2 := count2 + 1
			}
			count1 := count1 + 1
		}
	}
	return [filled,open]
}
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: CGDipSnapShot - GDI replacement for PixelGetColor

30 Oct 2017, 15:47

Hmm, it is entirely possible that I messed up and when the snap variable goes out of scope, the memory is not freed up. (Probably due to a self-reference).

In order to work around this, you could do try to re-use the same snapshot object, just re-take the snapshot
At the moment, you create a new snapshot each time the function is called.
You could maybe do something like

Code: Select all

snap := new CGdipSnapshot(x,y,w,h)
AveCol(x,y,w,h, snap)
{
	blue := 0
	green := 0
	red := 0
	snap.TakeSnapShot() 
This would also make your code quicker, as you do not need to new up the snapshot each time.

Btw, your sample code uses snap.TakeSnapShot when it should use snap.TakeSnapShot()

I will try and get around to looking into whether snapshot objects get properly garbage collected, thanks for the heads up.
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

30 Oct 2017, 18:29

Thanks very much for the prompt response! I'll make the change you suggested and see how it goes.

(I hadn't noticed the missing "()" as it still worked despite that. However I now just tested using an infinite loop and undefined, and then off-screen, variabless, and whereas it continued to work fine with the correct syntax, with the parenthesis omitted it caused the program to crash.)
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

30 Oct 2017, 21:03

OK, so I've spent a little time trying to understand everything better but I'm still having a problem: Forgive me if I've missed something, but I don't understand how I can re-use the same snapshot object. Each time the AveCol function is called, different co-ordinates and dimensions of the area to snapshot are used, so wouldn't I need to create a new snapshot each time?

As I was unable to successfully make the change you mentioned, I reverted to the code as it stood, other than now using the correct syntax of snap.TakeSnapShot(), and even added in a further check to verify coordinates are valid, but I'm still seeing a steady increase in memory usage until the program slows to such a pace that it needs to be restarted.

Help! :)

EDIT: I was looking through the other AHK program I wrote last summer, which also included CGdipSnapShot, as I recalled an issue I had back then. I found this code and comment:

Code: Select all

;Take a peek else next snap won't update
DummyPeek := snap.PixelSnap[5, 5].r
IIRC, often the initial attempt to read a colour component resulted in a value of zero (similar to the comment I made in my last post) and so I initiated a "dummy" read to get around this. I don't know if this is in any way related to my current issue, but I thought I'd mention it in case it may be.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: CGDipSnapShot - GDI replacement for PixelGetColor

31 Oct 2017, 07:22

Are there a fixed number of different regions that you want to examine using AveCol?

What I meant is, that if you want to examine x5 y5 w100 h100 multiple times, then it is wasteful creating a new instance each time. Every time you want to take a snapshot of the same region, you should use the same instance and re-snapshot.

I had a quick look at the code re: the bloating issue, and all my classes seem to free their memory.
However, I did find some issue where GDI+ did not like me creating a new token each time you newed up a CGDipSnapshot class.

So I altered the code so that all CGDipSnapshot classes share the same GDI+ token.
This should also increase performance.

Regarding the "snap not updating" issue, do you mean if you re-snapshot or something? Could you maybe clarify a little?
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

01 Nov 2017, 12:25

No, there aren't really any fixed regions, but it's good to know that I don't have to create a new snap if I do happen to have a fixed region that I need to examine at some future point.

Thanks very much for making the change to the code. I'll try it out and report back.

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

I can only be vague with the issue where the snap was returning a zero value each time. It was from over a year ago, and I just recall that the first pixelsnap read returned a zero result each time, and that the next read produced the actually result, so I had to add a "dummy peek" to get around the problem. I don't remember the precise conditions though. I just wondered if it was similar to the problem I mentioned a few posts back: "Also, during the checkseatstatus function, I had to give the two uses of the CGDipSnapShot different names (originally they both used the variable "snap", now they have been changed to "snap1" and "snap2") even though they are used one after the other, because originally the second use was returning a result of zero each time the red colour component was read..." I'll let you know if I experience it again.
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

05 Nov 2017, 06:36

Hi.

Just reporting back that since you made the alterations, everything seems to be working perfectly! Thanks very much for quickly fixing it.
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

18 Nov 2017, 23:24

Hi again, evilC

Is it possible for SnapShot to work with an image file, rather than just from the screen?

For example, if one had previously saved an image with SnapShot and wished to re-examine it, or- in my case- with an image captured from a window that is hidden.

Thanks.
+MeleaB+
Posts: 12
Joined: 26 Oct 2017, 18:46

Re: CGDipSnapShot - GDI replacement for PixelGetColor

23 Nov 2017, 02:35

I think there are still memory leakage issues caused from Snapshot.

When I tried using code such as that shown below, it appears that Snapshot was causing a conflict as I was having to create a new token each time I wanted to create and save a bitmap. So, I couldn't use the code as shown, and instead had to use pToken := gdip_startup() and Gdip_Shutdown(pToken) EACH TIME I wanted to perform the task. That wasn't a long-term solution of course and so I proceeded to replace all of my routines that used SnapShot with those from the GDI+ library and it now works as required. I'm learning as I go with AHK so there's plenty I still don't fully grasp. If I'm missing something then I'd be grateful if you could explain how else I could have resolved the memory leak.

Code: Select all

pToken := gdip_startup()
.
.
file=%a_scriptdir%\PMData\thumbnail%thumbnail_tableno%.png
thumbnail_id := tableid[thumbnail_tableno]
pBitmap := Gdip_BitmapFromHWND(thumbnail_id)
Gdip_SaveBitmapToFile(pBitmap,file)
Gdip_DisposeImage(pBitmap)
.
.
Gdip_Shutdown(pToken)
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: CGDipSnapShot - GDI replacement for PixelGetColor

13 May 2019, 07:27

Can I search a pixel using an image on a disc?
robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: CGDipSnapShot - GDI replacement for PixelGetColor

29 Jun 2019, 10:36

I have the same question, does it work with an image file as well ?
I solved this issue on my own. It was a silly question ;-).

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.
jstirle11
Posts: 3
Joined: 08 Feb 2023, 23:08

Re: CGDipSnapShot - GDI replacement for PixelGetColor

08 Feb 2023, 23:16

I need your help, how can I use this code only with CGDipSnapShot ?

Code: Select all

loop{
KeyWait, Space, D
PixelSearch, X, Y, 316, 235, 321, 243, 0xFFFF00, 40, Fast RGB
If (ErrorLevel = 0){
SendInput {Lbutton}
Sleep, 265
}
 
 
}
return

[Mod edit: Added [code][/code] tags. Please use them yourself when posting code.]
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: CGDipSnapShot - GDI replacement for PixelGetColor

09 Feb 2023, 11:41

jstirle11 wrote:
08 Feb 2023, 23:16
I need your help, how can I use this code only with CGDipSnapShot ?
There was no included function previously to do this, but I just added a new function "PixelSearch" to the library.
You can now do what you wish like this:

Code: Select all

#include CGdipSnapshot.ahk

; Create a snapshot object specifying your search area
snap := new CGdipSnapshot(316, 235, 321, 243)

; Create a colour object with the colour you wish to search for
col := new CColor(0xFFFF00)

Loop {
	KeyWait, Space, D
	; Take a snapshot of the specified search area
	snap.TakeSnapshot()
	; Search the snapshot for the colour, with tolerance of 40
	res := snap.PixelSearch(col, 40)
	; Check the result
	; res.result will be true if found. If found, res.x will be the x coordinate found at, res.y will be the y coordinate found at
	if (res.result){
		SendInput {Lbutton}
	}
	Sleep, 265
}
jstirle11
Posts: 3
Joined: 08 Feb 2023, 23:08

Re: CGDipSnapShot - GDI replacement for PixelGetColor

10 Feb 2023, 10:34

@evilC
Thanks, is something needed to have a quick read? is the color 0xFFFF00 already saved inside PixelSearch?
no need to save anything
jstirle11
Posts: 3
Joined: 08 Feb 2023, 23:08

Re: CGDipSnapShot - GDI replacement for PixelGetColor

26 Feb 2023, 00:01

@evilC
how to use 2 snaps with only 1 color, search 2 areas with the same color?

"snap := new CGdipSnapshot(316, 235, 321, 243)"
"snap2 := new CGdipSnapshot(314, 232, 350, 233)"
"col := new CColor(0xFFFF00)"

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 113 guests