Fastest Pixel Search - 1.6x faster than built in PixelSearch
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
I can add it - Variations are just RGB offsets, right? I was always concerned about proper color spaces, it seems like most people don't care and it would slow down the code.
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Yes, you would just add a check to see if each pixel's r/g/b component is within that range on either side (i.e., plus or minus that number) of the reference color's corresponding component.
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Higher is better.
Normal PixelSearch - https://godbolt.org/z/9v7vzf5az
Variation PixelSearch - https://godbolt.org/z/oocoPndE8
New PixelSearch with variation
New updated function
I don't know why the variation beats the normal pixelsearch.---------------------------
Benchmark.ahk
---------------------------
Built-in PixelSearch 25.39 fps
Custom PixelSearch 37.31 fps
Built-in PixelSearch with variation 24.44 fps
Custom PixelSearch with variation 38.13 fps
---------------------------
OK
---------------------------
Normal PixelSearch - https://godbolt.org/z/9v7vzf5az
Variation PixelSearch - https://godbolt.org/z/oocoPndE8
New PixelSearch with variation
Code: Select all
CoordMode, Mouse, Screen
if xy := px2(0x54C845, 50)
MouseMove xy[1], xy[2]
px2(color, variation := 3) {
static hdc, hbm, obm, pBits
if !hdc {
; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
VarSetCapacity(bi, 40, 0) ; sizeof(bi) = 40
NumPut( 40, bi, 0, "uint") ; Size
NumPut(A_ScreenWidth, bi, 4, "uint") ; Width
NumPut(-A_ScreenHeight, bi, 8, "int") ; Height - Negative so (0, 0) is top-left.
NumPut( 1, bi, 12, "ushort") ; Planes
NumPut( 32, bi, 14, "ushort") ; BitCount / BitsPerPixel
hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")
}
; Retrieve the device context for the screen.
static sdc := DllCall("GetDC", "ptr", 0, "ptr")
; Copies a portion of the screen to a new device context.
DllCall("gdi32\BitBlt"
, "ptr", hdc, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight
, "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY
; C source code - https://godbolt.org/z/oocoPndE8
static bin := 0
if !bin {
code := (A_PtrSize == 4)
? "VYnlVlNRikUQilUcik0gil0ki3UIiEX3ikUUiEX2ikUYiEX1O3UMcyiKRgI6Rfd3GzpF9nIWikYBOkX1dw440HIKigY4yHcEONhzCIPGBOvTi3UMWonwW15dww=="
: "VlNEilQkOESKXCRAilwkSECKdCRQSInISDnQcyuKSAJEOMF3HUQ4yXIYikgBRDjRdxBEONlyC4oIONl3BUA48XMJSIPABOvQSInQW17D"
size := StrLen(RTrim(code, "=")) * 3 // 4
bin := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
DllCall("VirtualProtect", "ptr", bin, "ptr", size, "uint", 0x40, "uint*", old:=0)
DllCall("crypt32\CryptStringToBinary", "str", code, "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size, "ptr", 0, "ptr", 0)
}
v := variation
r := ((color & 0xFF0000) >> 16)
g := ((color & 0xFF00) >> 8)
b := ((color & 0xFF))
; When doing pointer arithmetic, *Scan0 + 1 is actually adding 4 bytes.
byte := DllCall(bin, "ptr", pBits, "ptr", pBits + (4 * A_ScreenWidth * A_ScreenHeight)
, "uchar", Min(r+v, 255)
, "uchar", Max(r-v, 0)
, "uchar", Min(g+v, 255)
, "uchar", Max(g-v, 0)
, "uchar", Min(b+v, 255)
, "uchar", Max(b-v, 0)
, "ptr")
if (byte == pBits + (4 * A_ScreenWidth * A_ScreenHeight))
return False
offset := (byte - pBits) // 4
return [mod(offset, A_ScreenWidth), offset // A_ScreenWidth]
}
Code: Select all
px(color) {
static hdc, hbm, obm, pBits
if !hdc {
; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
VarSetCapacity(bi, 40, 0) ; sizeof(bi) = 40
NumPut( 40, bi, 0, "uint") ; Size
NumPut(A_ScreenWidth, bi, 4, "uint") ; Width
NumPut(-A_ScreenHeight, bi, 8, "int") ; Height - Negative so (0, 0) is top-left.
NumPut( 1, bi, 12, "ushort") ; Planes
NumPut( 32, bi, 14, "ushort") ; BitCount / BitsPerPixel
hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")
}
; Retrieve the device context for the screen.
static sdc := DllCall("GetDC", "ptr", 0, "ptr")
; Copies a portion of the screen to a new device context.
DllCall("gdi32\BitBlt"
, "ptr", hdc, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight
, "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY
; C source code - https://godbolt.org/z/9v7vzf5az
static bin := 0
if !bin {
code := (A_PtrSize == 4)
? "VYnli1UMi00Qi0UIOdBzCTkIdAeDwATr84nQXcM="
: "SInISDnQcwtEOQB0CUiDwATr8EiJ0MM="
size := StrLen(RTrim(code, "=")) * 3 // 4
bin := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
DllCall("VirtualProtect", "ptr", bin, "ptr", size, "uint", 0x40, "uint*", old:=0)
DllCall("crypt32\CryptStringToBinary", "str", code, "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size, "ptr", 0, "ptr", 0)
}
; Lift color to 32-bits if first 8 bits are zero.
(!(color >> 24)) && color |= 0xFF000000
; Pass the width * height, but the size is returned due to C interpreting Scan0 as a integer pointer.
; So when doing pointer arithmetic, *Scan0 + 1 is actually adding 4 bytes.
byte := DllCall(bin, "ptr", pBits, "ptr", pBits + (4 * A_ScreenWidth * A_ScreenHeight), "uint", color, "ptr")
if (byte == pBits + A_ScreenWidth * A_ScreenHeight * 4)
return False
offset := (byte - pBits) // 4
return [mod(offset, A_ScreenWidth), offset // A_ScreenWidth]
}
- Attachments
-
- PixelSearch Benchmark.zip
- Please run the benchmark yourself and see what you get.
- (29.02 KiB) Downloaded 241 times
Last edited by iseahound on 22 May 2022, 15:24, edited 7 times in total.
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
I just want to thank TeaDrinker for this line of code:
It's so obvious and so lovely. I used their coding style to help improve my script.
Code: Select all
; Lift color to 32-bits if first 8 bits are zero.
(!(color >> 24)) && color |= 0xFF000000
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Nice...thank you!
And if that is not too much to ask, is it possible you could add search specific area function like built-in pixel search?
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Not at the moment. I think it should be straightforward to modify the function to do this. My main purpose was to optimize the pixel search assembly, and I've accomplished that.
Anyone is free to make their own modifications!
Edit: Oh I just remembered you can actually do this using ImagePut.
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
Code: Select all
pic := ImagePutBuffer([0, 200, 500, 500])
xy := pic.PixelSearch2(0x1235FF, 3) ; variation of 3.
ImageShow(pic) ; or pic.show()
MouseMove xy[1], xy[2]
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Thanks, this tool helps me achieve my goal, and it's fast!iseahound wrote: ↑17 May 2022, 21:29Not at the moment. I think it should be straightforward to modify the function to do this. My main purpose was to optimize the pixel search assembly, and I've accomplished that.
Anyone is free to make their own modifications!
Edit: Oh I just remembered you can actually do this using ImagePut.
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
Code: Select all
pic := ImagePutBuffer([0, 200, 500, 500]) xy := pic.PixelSearch2(0x1235FF, 3) ; variation of 3. ImageShow(pic) ; or pic.show() MouseMove xy[1], xy[2]
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
I want to verify if the green color in the middle is there. But it throws pixel not found.
I don't know how to create the searched pixel. I take the color with idropper.
I don't know how to create the searched pixel. I take the color with idropper.
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Code: Select all
CoordMode, Mouse, Screen
a::
if xy := px(0x19AE82)
MouseMove xy[1], xy[2]
return
px(color) {
static hdc, hbm, obm, pBits
if !hdc {
; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
VarSetCapacity(bi, 40, 0) ; sizeof(bi) = 40
NumPut( 40, bi, 0, "uint") ; Size
NumPut(A_ScreenWidth, bi, 4, "uint") ; Width
NumPut(-A_ScreenHeight, bi, 8, "int") ; Height - Negative so (0, 0) is top-left.
NumPut( 1, bi, 12, "ushort") ; Planes
NumPut( 32, bi, 14, "ushort") ; BitCount / BitsPerPixel
hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")
}
; Retrieve the device context for the screen.
static sdc := DllCall("GetDC", "ptr", 0, "ptr")
; Copies a portion of the screen to a new device context.
DllCall("gdi32\BitBlt"
, "ptr", hdc, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight
, "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY
; C source code - https://godbolt.org/z/9v7vzf5az
static bin := 0
if !bin {
code := (A_PtrSize == 4)
? "VYnli1UMi00Qi0UIOdBzCTkIdAeDwATr84nQXcM="
: "SInISDnQcwtEOQB0CUiDwATr8EiJ0MM="
size := StrLen(RTrim(code, "=")) * 3 // 4
bin := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
DllCall("VirtualProtect", "ptr", bin, "ptr", size, "uint", 0x40, "uint*", old:=0)
DllCall("crypt32\CryptStringToBinary", "str", code, "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size, "ptr", 0, "ptr", 0)
}
; Lift color to 32-bits if first 8 bits are zero.
(!(color >> 24)) && color |= 0xFF000000
; Pass the width * height, but the size is returned due to C interpreting Scan0 as a integer pointer.
; So when doing pointer arithmetic, *Scan0 + 1 is actually adding 4 bytes.
byte := DllCall(bin, "ptr", pBits, "ptr", pBits + (4 * A_ScreenWidth * A_ScreenHeight), "uint", color, "ptr")
if (byte == pBits + A_ScreenWidth * A_ScreenHeight * 4)
return False
offset := (byte - pBits) // 4
return [mod(offset, A_ScreenWidth), offset // A_ScreenWidth]
}
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
I am very sorry. I wanted to find out if this pixel exists in the png file. My fault, I didn't explain correctly.iseahound wrote: ↑22 May 2022, 15:23It shouldn't throw, use the newer version on the above post.Code: Select all
CoordMode, Mouse, Screen a:: if xy := px(0x19AE82) MouseMove xy[1], xy[2] return px(color) { static hdc, hbm, obm, pBits if !hdc { ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr") VarSetCapacity(bi, 40, 0) ; sizeof(bi) = 40 NumPut( 40, bi, 0, "uint") ; Size NumPut(A_ScreenWidth, bi, 4, "uint") ; Width NumPut(-A_ScreenHeight, bi, 8, "int") ; Height - Negative so (0, 0) is top-left. NumPut( 1, bi, 12, "ushort") ; Planes NumPut( 32, bi, 14, "ushort") ; BitCount / BitsPerPixel hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr") obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr") } ; Retrieve the device context for the screen. static sdc := DllCall("GetDC", "ptr", 0, "ptr") ; Copies a portion of the screen to a new device context. DllCall("gdi32\BitBlt" , "ptr", hdc, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY ; C source code - https://godbolt.org/z/9v7vzf5az static bin := 0 if !bin { code := (A_PtrSize == 4) ? "VYnli1UMi00Qi0UIOdBzCTkIdAeDwATr84nQXcM=" : "SInISDnQcwtEOQB0CUiDwATr8EiJ0MM=" size := StrLen(RTrim(code, "=")) * 3 // 4 bin := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr") DllCall("VirtualProtect", "ptr", bin, "ptr", size, "uint", 0x40, "uint*", old:=0) DllCall("crypt32\CryptStringToBinary", "str", code, "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size, "ptr", 0, "ptr", 0) } ; Lift color to 32-bits if first 8 bits are zero. (!(color >> 24)) && color |= 0xFF000000 ; Pass the width * height, but the size is returned due to C interpreting Scan0 as a integer pointer. ; So when doing pointer arithmetic, *Scan0 + 1 is actually adding 4 bytes. byte := DllCall(bin, "ptr", pBits, "ptr", pBits + (4 * A_ScreenWidth * A_ScreenHeight), "uint", color, "ptr") if (byte == pBits + A_ScreenWidth * A_ScreenHeight * 4) return False offset := (byte - pBits) // 4 return [mod(offset, A_ScreenWidth), offset // A_ScreenWidth] }
I mimicked your work with ImagePutBuffer. It found the blue color in the png file you provided. But I failed when I used my own file and green color.
viewtopic.php?f=6&t=101819#p456737
I'm interested in the png file, not the screen coordinates. I should have explained that.
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Code: Select all
#include ImagePut.ahk
pic := ImagePutBuffer("GreenDot.png")
pic.show()
if xy := pic.PixelSearch(0x19AE82)
MouseMove xy[1], xy[2]
MsgBox % "The color of the found pixel is: " pic[xy*]
Just to explain pic[xy*], this uses the unpacking operator * as a shorthand for pic[xy[1], xy[2]]
EDIT: I reread your question and it seems a simpler snippet would suffice:
Code: Select all
#include ImagePut.ahk
filepath := "GreenDot.png"
if ImagePutBuffer(filepath).PixelSearch(0x19AE82)
MsgBox Green Pixel Found
else
MsgBox not found!
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Thank you so much. I'll try when I get home. Both examples will work for me.
I forgot to use this part. That's why my attempts failed. "pic.show()" I understand why my attempt failed.
I think the second example is the ideal one for me.
I forgot to use this part. That's why my attempts failed. "pic.show()" I understand why my attempt failed.
I think the second example is the ideal one for me.
-
- Posts: 13
- Joined: 29 May 2022, 16:20
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
@iseahound
how can I add specific region to search?
how can I add specific region to search?
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Most people would use ImagePut for that.
https://github.com/iseahound/ImagePut
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
https://github.com/iseahound/ImagePut
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
Code: Select all
pic := ImagePutBuffer([100, 100, 50, 50]) ; Load image
pic.show() ; or ImageShow(pic) ; Show image
if xy := pic.PixelSearch(0xFFFFFF) ; Get [x, y] of 0xFFFFFF
MouseMove xy[1], xy[2] ; Move cursor
else Reload ; Restart
-
- Posts: 1
- Joined: 27 Nov 2022, 16:53
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
@iseahound Please tell me if there is a V2 AutoHotkey version for your updated 'px' function from the top of this thread.
Thank you!
neutrowave
Thank you!
neutrowave
Re: Fastest Pixel Search - 1.6x faster than built in PixelSearch
Again my v2 compatible work is found in:
https://github.com/iseahound/ImagePut/blob/master/ImagePut.ahk
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
px() was only provided as a proof of concept.
https://github.com/iseahound/ImagePut/blob/master/ImagePut.ahk
https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch
px() was only provided as a proof of concept.