## Loop problem ? Topic is solved

Ask gaming related questions
Ahk-parrenti

### Loop problem ?  Topic is solved

Hello to all,

I have a new project. My lack of knowledge prevents me from completing it.
So I need you!
PS: I did a lot of research, I do not come here without having done anything and wait for you to do it for me,
i'm here to learn / understand.

The project :
I will try to be clear. Imagine, you're facing your screen, and there is a letter or a number
displayed. This number (or letter) changes quickly for another. And you want every time
as this number changes, a precise action is taken.

Constraints :
Well yes, there is. Otherwise, it would not even be a challenge for a beginner like me
1) We know the color of this number: white. BUT the background constantly changes color!
2) The time:
- The number changes quickly and the interval is random. To give yourself a point of reference: let's say that it changes every 50ms on average.
- My program should detect the number change and start the action in less than 11ms. (It can be a little more)

What I did:
- after studying:
I can forget the "search for images" since the background changes constantly, my image on the screen is never the same;
I can forget the "image recognition", already because it is not very good and because it is too slow for it to recognize I think,
So we go for a good handmade script.

Well, I have the idea to make the change of number detection by looking at certain pixels. Say 3 pixels, arranged at key locations.
On all the possible numbers, there will ALWAYS be at least 1 pixel that will change to white or lose its white color!

If I had to explain to you what my program should do:
1) I'm looking at 3 pixels
2) I keep in mind who are white, who are not white.
3) I look at my 3 pixels, and I constantly compare their current colors to those I have in memory.
4) As soon as I notice that one of them (or more) has lost its white or recovered a white value
5) I keep in memory the new colors for these 3 pixels (related to the white always).
5) I'm doing a specific action !!
6) we go back on the 3)

Well, here is the script that I could perform for now: (ie, for the need of the test, the color listened is the black, and the reaction is to write "ab")

///

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.

VarPix1 := false
VarPix2 := false
VarPix3 := false

MyRoutine:
PixelGetColor, Pixel1, 1060, 448
PixelGetColor, Pixel2, 1076, 361
PixelGetColor, Pixel3, 1087, 454

if (Pixel1 = 0x000000) && (VarPix1 = false)
{
VarPix1 := !VarPix1
}
else if (Pixel1 != 0x000000) && (VarPix1 = true)
{
VarPix1 := !VarPix1
}
else if (Pixel2 = 0x000000) && (VarPix2 = false)
{
VarPix2 := !VarPix2
}
else if (Pixel2 != 0x000000) && (VarPix2 = true)
{
VarPix2 := !VarPix2
}
else if (Pixel3 = 0x000000) && (VarPix3 = false)
{
VarPix3 := !VarPix3
}
else if (Pixel3 != 0x000000) && (VarPix3 = true)
{
VarPix3 := !VarPix3
}
{
send, ab
}

Goto, MyRoutine

///

The problem with this script is that:
1) It looks like, from time to time, between 2 numbers, the script sends to much "ab".
So :
- either because it sends me as much as "ab" that pixels change by number.
- or if my script seems to you clean, it is that the color of the screen changes, briefly, without I realize it, between 2 numbers from time to time.
2) It is surely not optimized; there must be simpler and faster for the program to run, but with my little knowledge : that's all I can do.

Can someone tell me if my code is "ok"? And also tell me how it can be optimized ?
If you would have adopted another strategy for the basic project: which one?
evilC
Posts: 4539
Joined: 27 Feb 2014, 12:30

### Re: Loop problem ?

Bear in mind that with normal AHK commands, you have no guarantees that each PixelGetColor check is actually on the same frame of animation.
You might want to try out my CGDipSnapshot library, which allows you take one snapshot of part of the screen, then do pixel inspection on the snapshot, ensuring all your checks happen on the same frame.

Regarding your original code, nest your IF statements.
You don't need to be constantly checking if Pixel1 is 0 or not - do it once and put other checks inside

Code: Select all

``````if (Pixel1 = 0x000000) && (VarPix1 = false)
{
[...]
}
else if (Pixel1 != 0x000000) && (VarPix1 = true)
{
[...]
}
else if (Pixel2 = 0x000000) && (VarPix2 = false)
{
[...]
}

``````
It would be simpler to do:

Code: Select all

``````if (Pixel1 = 0x000000)
{
if (VarPix1 = false)
{
[...]
}
else if (VarPix2 = false)
{
[...]
}
}
else
{
; Pixel1 is not 0x0000
if (VarPix1 = true)
{
[...]
}
else
{
[...]
}
}
``````
Ahk-apprenti
Posts: 8
Joined: 19 Apr 2018, 20:00

### Re: Loop problem ?

Hi evilC,

It's great to be able to interact with you. An expert in picture/sec, it's right what I needed!

What i understand/don't understand :
For the history of "frames of animation", you put in the problem a data that I had not throught at all. Thank you !
It raises some new questions for me!

Take as an example a number change from 8 to 7.
It is possible that my pixelsearchS do not analyze the same number, on the same loop, for 2 cases:

1) Because of the refresh of my high screen (144hz). It seems to me that the refresh of the screen is like this: the first pixel line from the top displays the new image, then the second line of pixels etc ... at the bottom of the screen: the new image is complete.
So for a very short period of time, I have an image, imperceptible to the naked eye, on the screen that is a mix of 8 and 7. If my script have got the time to perform a full loop at this precise moment and on this image, then at this loop
he will detect a change of number on the top pixels (he will see the top of the 7), but none of the bottom of the 7 because the bottom will be still 8. He will launch the action.
At next loop it will detect a change of number on the bottom, it will now see the bottom of the 7: it returns one more action !!
=> The script first parses a hybrid image 7/8 and performs an action, then reanalyzes a new image (full 7) and restarts the action.

2) Or assume that 8 turns into 7 instantaneously, so ahk can not do anything during this too short interval. It could still be that:
the script starts on 8, it sees no change on the top pixels of the 8 and it continues to analyze the 8 from top to bottom, boom 8 instantly turns into 7, the script continues and he realizes before the end of the script that the bottom has become a 7.
It launches the action, at the next round of loop, it analyzes the top of 7 and sees changes (since the valor of the pixels before were on the top of 8) and it launches the action again ...
=> the image changes in the middle of the script, only the last pixels of the script realized it and starts the action, the next loop is the pixels of the beginning of script that will launch the action...

Ok, well they're two probabilities and I need to deal with it. But im not sure about the benefits of your library because :

Case A It is possible that the snapshot is made at the time of the change of image, the script will analyze, during the loop 1, a number : half 8 half 7, next loop: new snapshot of the figure 7 only this time, Even problem:
two actions launched while only one real change of number.

Case B On this possibility, snapshot would be useful since it would force the script to scan either 8 integer or 7 integer. But I lose in reactivity, I suppose, for 2 reasons:

- Let's say that 8 is on the screen, boom snapshot, and right after this moment it becomes 7, my script analyzes a past image (the 8) and sees that there is no change. For the duration of the 8 ghost check,
7 is present and no action is taken, at the next loop, re-snapshot and the script analyzes and it is only at the end of the 2nd loop that action will have been taken. So, at worst, I have the time of 2 loops to wait for an action to be taken on 7. (i dont know how many time this is represent)

- I could pixelearch more pixel in my initial script, but I took the lead so that the script can with a minimum of pixel to realize the change of number! Why ? The speed of data.
Actually, the program "flash" only 3 pixels. There with snapshot, it has to flash a lot more pixels. If I take a square of 300, it will have to analyze and store 90 000 pixels ! Wow. We are no longer
on the same reaction time ! The processing time of the information must be extended!

My questions in brief :
- In the end, do you think that the time of refreshing an image can be problematic? I do not know how long it takes and if it can play on the script (half number + half number).
- Finally, if I use snapshot, these calculations (300px square) will be done very quickly anyway and all will be less than 5 ms?

Another way ?
Finally, a good idea would be: since we know that it is during this period of refreshment some errors are created. I would have to create a script that will detect the start of a refresh screen, cancel the actual loop,
pause the loop, and when the refresh is finished, it launches a new loop!

For the optimization of the script, I look and apply the changes tonight. Again, thank you for your help
Ahk-apprenti
Posts: 8
Joined: 19 Apr 2018, 20:00

### Re: Loop problem ?

evilC,

I looked at your code quickly. I do not think it's suited to what I want to do, or I do not understand it.

=> VarPix2 is my "Var" of information that makes sure that I only send my action once when Pixel2 changes its value (white /! White).
So, VarPix2 totally does not care about Pixel1's color. And here, it dont even look and check for Pixel2.

VarPix1 and Pixel1 are a mechanism for listen/send reaction about one pixel.
VarPix2 and Pixel2 are a mechanism for listen/send reaction about an another pixel.

(PS: I can not take a single pixel as a reference. For example, on a count of 10 to 0: a pixel can alternatively turn white then non-white on the numbers from 9 to 6. But stay blank on the 6 and 5. So, the change of digit will not be detected.)
swagfag
Posts: 2116
Joined: 11 Jan 2017, 17:59

### Re: Loop problem ?

Code: Select all

``````let's say that it changes every 50ms on average.
- My program should detect the number change and start the action in less than 11ms. (It can be a little more)``````
this part stood out to me. one pixelgetcolor call is fast(i5 4690k@4.6, between 3-11ms), two calls not so much(30ms), three calls(50ms on average)

so before u code all this shit up, id suggest you benchmark all methods of querying pixel data and figure out if the approach is at all viable or not
Ahk-apprenti
Posts: 8
Joined: 19 Apr 2018, 20:00

### Re: Loop problem ?

@swag fag : Indeed, if your ms numbers are right, I must take this data in my method too ! lol Decidly it's more and more complex

I do not know all methods of querying pixel data. (I will continue to search) If you have some tips, I'm listening.
But I do not see how I could see the speed of one method via another. How do you know its delay for pixelgetcolor ?
I have already started to rewrite my script with the evilC's method. He gave me a method, I have to try it!

@evilC : I could read this on a thread:
"- For even more performance, you can capture the content of a screen memory (SnapShot, takes about the same time as or PixelSearch PixelGetColor) then do all the research you want in this snapshots (very fast functions : nearly 1000 times as fast as the native PixelGetColor)."
Can you confirm this delay? So capture 300 pix and search in it, it's faster than 3 pixelgetcolors ?
It should solve the Case B.

ATM, I still do not know for the case A, if the refresh delay of the screen is a problem or not (half 7 - half 8).
swagfag
Posts: 2116
Joined: 11 Jan 2017, 17:59

### Re: Loop problem ?

Code: Select all

``````#NoEnv
#WinActivateForce
#SingleInstance, Force
#Warn, ClassOverwrite
SendMode, Input
SetBatchLines, -1
SetTitleMatchMode, 2
SetWorkingDir, %A_ScriptDir%

QPC := new QueryPerformanceCounter(1000000)

x::QPC.benchmarkFunction(Func("pix"))
z::ExitApp

pix() {
Loop, 3 {
PixelGetColor, Pixel1, 1060, 448
}
}

class QueryPerformanceCounter {
static precisionConversion := {1 : "second"
, 10 : "decisecond"
, 100 : "centisecond"
, 1000 : "millisecond"
, 1000000 : "microsecond"
, 1000000000 : "nanosecond"
, 1000000000000 : "picosecond"}

benchmarkFunction(functionObject := "") {
counterStart := this._getTimeStamp()
functionObject.Call()
elapsedTime := this._elapsedTimeSince(counterStart)
MsgBox, % "'" . functionObject.Name . "' executed in: " . elapsedTime . " " . this.precisionConversion[this.precision] . "(s)."
}
__New(precision := 1000) {
this.frequency := this._getFrequency()
this.precision := precision
}

_getFrequency() {
DllCall("QueryPerformanceFrequency", "Int64*", frequency)
return frequency
}

_getTimestamp() {
DllCall("QueryPerformanceCounter", "Int64*", timestamp)
return timestamp
}

_elapsedTimeSince(oldTime) {
currentTime := this._getTimestamp()
elapsedTime := (currentTime - oldTime) * this.precision
elapsedTime /= this.frequency ;division performed separately to preserve precision/rounding
return elapsedTime
}

}``````
other methods include pixelsearch, imagesearch, gdip_imagesearch, gdip lockbits
neverlevel
Posts: 60
Joined: 13 Apr 2016, 22:02

### Re: Loop problem ?

does AHK care what your screens refresh rate is? isnt it checking what your computer is telling your screen to be? as in..if you didnt HAVE a monitor..it would still work