问题
I understand that for some things it would be better to write certain things in C++, but I'd really like to be able to do this in AHK:
I want to be able to retrieve the pixel data from a 100x300 area of the screen, however PixelGetColor
is way too slow. Here's a test demonstrating that it takes about 0.02 seconds per pixel, which is roughly 11.5 hours to get the pixel data from an entire 1920 x 1080 screen.
In the test, it'll take about 4-5 seconds just to get the pixel data from a 15 x 15 area of the screen.
width := 15 ; 1920
height := 15 ; 1080
searchResolution := 1 ; 3
columns := width / searchResolution
rows := height / searchResolution
resultRows := {}
columnCounter := 0
rowCounter := 0
resultCounter := 0
start := getTimestamp()
loop, %columns%
{
resultRows[columnCounter] := {}
loop, %rows%
{
PixelGetColor, pixelColor, columnCounter, rowCounter
resultRows[columnCounter][rowCounter] := pixelColor
rowCounter += searchResolution
resultCounter += 1
}
columnCounter += searchResolution
rowCounter := 0
}
end := getTimestamp()
MsgBox % "Finished! It took " . (end - start) / 1000 .
" seconds to record pixel data from a " .
width . " x " . height . " area of the screen (" . resultCounter . " pixels)."
getTimestamp()
{
DllCall("QueryPerformanceCounter", "Int64*", timestamp)
DllCall("QueryPerformanceFrequency", "Int64*", frequency)
return Round(timestamp * 1000 / frequency)
}
If you'd like the version which includes debug logging and exporting of the data to an XML file for inspection, it's here.
Is there any faster way to get pixel data from a portion of the screen?
PixelSearch
searches very large areas of the screen very quickly, I'm not sure why PixelGetColor
would be so very slow in comparison. There must be some .dll
or some other function I can use to get pixel data from a small area of the screen much faster than this.
回答1:
I found a way to do it 103 times faster than Forivin's solution :D
SetBatchLines, -1
CoordMode, Pixel, screen
FileDelete, Log.txt
searchSpace := 400
jumpSize := 1 ; how many units to skip each interval
total := Round(((searchSpace * searchSpace) / jumpSize), 0)
startTimer := getTimestamp()
getPixelMapSlow(searchSpace, jumpSize)
endTimer := getTimestamp()
duration := endTimer - startTimer
rate := total / duration
FileAppend, % "[getPixelMapSlow] Retrieved " . total . " pixels from bitmap, duration: " . duration . "ms at at a rate of " . rate . " pixels/ms.`n", Log.txt
searchSpace := 400
jumpSize := 1 ; how many units to skip each interval
total := Round(((searchSpace * searchSpace) / jumpSize), 0)
startTimer := getTimestamp()
getPixelMapFast(searchSpace, jumpSize)
endTimer := getTimestamp()
duration := endTimer - startTimer
rate := total / duration
FileAppend, % "[getPixelMapFast] Retrieved " . total . " pixels from bitmap, duration: " . duration . "ms at at a rate of " . rate . " pixels/ms.`n", Log.txt
getPixelMapFast(searchSpace, jumpSize){
width := 1920
height := 1080
centerX := width / 2
centerY := height / 2
searchSpacehalf := searchSpace / 2
searchCounterX := 0
searchCounterY := 0
pixelMap := {}
pBitmap := Gdip_BitmapFromScreen((centerX - searchSpacehalf) . "|" . (centerY - searchSpacehalf) . "|" . searchSpace . "|" . searchSpace)
E1 := Gdip_LockBits(pBitmap, 0, 0, Gdip_GetImageWidth(pBitmap), Gdip_GetImageHeight(pBitmap), Stride, Scan0, BitmapData)
Loop, %searchSpace%
{
tick := A_Index * jumpSize
if (tick < searchSpace) {
New_Index_X := tick
Loop, %searchSpace%
{
tick := A_Index * jumpSize
if (tick < searchSpace) {
New_Index_Y := tick
color1ARGB := Gdip_GetLockBitPixel(Scan0, New_Index_X, New_Index_Y, Stride)
SetFormat, Integer, H
color1RGB := 0x00ffffff & color1ARGB
SetFormat, Integer, D
if (!pixelMap[New_Index_X]){
pixelMap[New_Index_X] := {}
}
pixelMap[New_Index_X][New_Index_Y] := color1RGB
}
}
}
}
Gdip_UnlockBits(pBitmap, BitmapData)
Gdip_DisposeImage(pBitmap)
return pixelMap
}
getPixelMapSlow(searchSpace, jumpSize){
width := 1920
height := 1080
centerX := width / 2
centerY := height / 2
searchSpacehalf := searchSpace / 2
searchCounterX := 0
searchCounterY := 0
pixelMap := {}
pBitmap := Gdip_BitmapFromScreen((centerX - searchSpacehalf) . "|" . (centerY - searchSpacehalf) . "|" . searchSpace . "|" . searchSpace)
Loop, %searchSpace%
{
tick := A_Index * jumpSize
if (tick < searchSpace) {
New_Index_X := tick
Loop, %searchSpace%
{
tick := A_Index * jumpSize
if (tick < searchSpace) {
New_Index_Y := tick
color1ARGB := Gdip_GetPixel(pBitmap, New_Index_X, New_Index_Y)
if (!pixelMap[New_Index_X]){
pixelMap[New_Index_X] := {}
}
color1RGB := ARGBtoRGB(color1ARGB)
pixelMap[New_Index_X][New_Index_Y] := color1RGB
}
}
}
}
Gdip_DisposeImage(pBitmap)
return pixelMap
}
ARGBtoRGB( ARGB ) {
VarSetCapacity( RGB,6,0 )
DllCall( "msvcrt.dll\sprintf", Str,RGB, Str,"%06X", UInt,ARGB<<8 )
Return "0x" RGB
}
getTimestamp()
{
DllCall("QueryPerformanceCounter", "Int64*", timestamp)
DllCall("QueryPerformanceFrequency", "Int64*", frequency)
return Round(timestamp * 1000 / frequency)
}
Of course include the relevant functions of the AHK Gdip library (found on Github) in your code for this to work.
Log:
[getPixelMapSlow] Retrieved 160000 pixels from bitmap, duration: 33161ms at at a rate of 4.824945 pixels/ms.
[getPixelMapFast] Retrieved 160000 pixels from bitmap, duration: 321ms at at a rate of 498.442368 pixels/ms.
回答2:
To reduce delays between commands to a minimum you should also use SetBatchLines, -1
. This alone can give you a significant performance boost.
I think you have already figured out the rest.
But in case anyone else stumbles across this question. Here is how you can do it with GDI+:
SetBatchLines, -1
#Include Gdip.ahk
pToken := Gdip_Startup()
; Screen area ("X|Y|Width|Height")
pBitmap := Gdip_BitmapFromScreen("500|600|300|100")
; Read RGB color from pixel x290 y65
ARGB := Gdip_GetPixel( pBitmap, 290, 65 )
pixelColor := ARGBtoRGB( ARGB )
MsgBox, % pixelColor
; Read RGB color from pixel x167 y90
ARGB := Gdip_GetPixel( pBitmap, 167, 90 )
pixelColor := ARGBtoRGB( ARGB )
MsgBox, % pixelColor
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
ARGBtoRGB( ARGB ) {
VarSetCapacity( RGB,6,0 )
DllCall( "msvcrt.dll\sprintf", Str,RGB, Str,"%06X", UInt,ARGB<<8 )
Return "0x" RGB
}
The code is mostly what I already posted in another answer right here.
来源:https://stackoverflow.com/questions/44621796/how-can-i-get-the-pixel-data-from-an-area-of-the-screen-much-faster-in-ahk