【问题标题】:How can I get the pixel data from an area of the screen (much) faster in AHK?如何在 AHK 中更快地(更快)从屏幕区域获取像素数据?
【发布时间】:2017-11-21 03:39:27
【问题描述】:

我知道对于某些事情,用 C++ 编写某些事情会更好,但我真的希望能够在 AHK 中做到这一点:

我希望能够从屏幕的 100x300 区域检索像素数据,但是 PixelGetColor 太慢了。这是一个测试,证明每个像素大约需要 0.02 秒,大约需要 11.5 小时才能从整个 1920 x 1080 屏幕获取像素数据。

在测试中,从屏幕的 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 或其他一些函数,我可以使用它来比这更快地从屏幕的一小块区域获取像素数据。

【问题讨论】:

  • 也许拍摄一个屏幕截图,然后遍历图像以构建 2x2 像素颜色数组。不确定 AHK 是否能够实现这样的目标。我只是一直使用 PixelGetColor。
  • 任何人都可以验证if this code example works for them? 我打算尝试使用它作为解决方案,但示例代码没有保存屏幕截图。
  • 了解到我需要使用 ansi 32 位版本的 ahk 才能工作。仍在为此制定整体解决方案..
  • @ChickenFeet 如果您使用正确的 DLL,它可以:)

标签: autohotkey


【解决方案1】:

我找到了一种比 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.

【讨论】:

  • 要考虑的另一件事可能不是计算 RGB 值,而是保留 ARGB 值并使用这些值。无论如何,A 组件将始终为 0x00
  • @Forivin 谢谢,是的,当您对 160000 次循环迭代执行计算时,每次省略一个字符串转换可能会产生很大的不同。
  • @Forivin 实际上你的建议让它快了 160 倍而不是 100 倍,哇。
  • 您可以直接使用 New_Index_X 而不是定义变量 tick。在许多情况下,= 比 := 快。不过,我不确定它是否真的对你有帮助。
  • 如果我没记错你有一个小错误,你的 ARGB 转换是反向的,红色是蓝色,蓝色是红色。我没有深入研究它,只是注意到它与本机 getpixel 返回的相反
【解决方案2】:

要将命令之间的延迟降至最低,您还应该使用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

【讨论】:

  • 看我的回答。我找到了一种将 xD 速度提高 103 倍的方法(运行我的代码进行基准测试)
猜你喜欢
  • 2018-04-16
  • 2018-10-21
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
相关资源
最近更新 更多