如何在 AHK 中更快地从屏幕区域获取像素数据?
How can I get the pixel data from an area of the screen (much) faster in AHK?
我知道对于某些事情,用 C++ 编写某些东西会更好,但我真的很想能够在 AHK 中做到这一点:
我希望能够从屏幕的 100x300 区域检索像素数据,但是 PixelGetColor
太慢了。这是一个测试,表明每个像素大约需要 0.02 秒,从整个 1920 x 1080 屏幕获取像素数据大约需要 11.5 小时。
在测试中,从屏幕的 15 x 15 区域 获取像素数据大约需要 4-5 秒。
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)
}
如果您想要包含调试日志记录和将数据导出到 XML 文件以供检查的版本,它是 here。
有没有更快的方法从屏幕的一部分获取像素数据?
PixelSearch
非常快速地搜索屏幕的很大区域,我不确定为什么 PixelGetColor
相比之下会这么慢。必须有一些 .dll
或其他一些函数可以用来从屏幕的一小块区域获取像素数据,速度比这快得多。
要将命令之间的延迟降至最低,您还应该使用 SetBatchLines, -1
。仅此一项就可以显着提升性能。
我想你已经想好了剩下的。
但以防万一其他人偶然发现了这个问题。以下是使用 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
}
代码大部分是我已经发布的 in another answer right here。
我找到了比 Forivin 的解决方案快 103 倍的方法 :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)
}
当然要在您的代码中包含 AHK Gdip 库(在 Github 上找到)的相关函数,这样才能正常工作。
日志:
[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.
我知道对于某些事情,用 C++ 编写某些东西会更好,但我真的很想能够在 AHK 中做到这一点:
我希望能够从屏幕的 100x300 区域检索像素数据,但是 PixelGetColor
太慢了。这是一个测试,表明每个像素大约需要 0.02 秒,从整个 1920 x 1080 屏幕获取像素数据大约需要 11.5 小时。
在测试中,从屏幕的 15 x 15 区域 获取像素数据大约需要 4-5 秒。
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)
}
如果您想要包含调试日志记录和将数据导出到 XML 文件以供检查的版本,它是 here。
有没有更快的方法从屏幕的一部分获取像素数据?
PixelSearch
非常快速地搜索屏幕的很大区域,我不确定为什么 PixelGetColor
相比之下会这么慢。必须有一些 .dll
或其他一些函数可以用来从屏幕的一小块区域获取像素数据,速度比这快得多。
要将命令之间的延迟降至最低,您还应该使用 SetBatchLines, -1
。仅此一项就可以显着提升性能。
我想你已经想好了剩下的。
但以防万一其他人偶然发现了这个问题。以下是使用 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
}
代码大部分是我已经发布的 in another answer right here。
我找到了比 Forivin 的解决方案快 103 倍的方法 :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)
}
当然要在您的代码中包含 AHK Gdip 库(在 Github 上找到)的相关函数,这样才能正常工作。
日志:
[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.