Image Filter everything but a range of similar colors Topic is solved
Image Filter everything but a range of similar colors
I was reading the following thread:
viewtopic.php?style=1&f=76&t=98341
where XTRA posted this nice ColorFilter-Function that takes pBitmap and replaces everything but a certain color. Thanks for that, works great and is very fast.
I was wondering if anyone knows a way to have it replace everything but "a certain color and similar colors"? XTRA's code is over my head for me to modify it, and I understand it involves machine code to be fast...
Thanks in advance. Simon
viewtopic.php?style=1&f=76&t=98341
where XTRA posted this nice ColorFilter-Function that takes pBitmap and replaces everything but a certain color. Thanks for that, works great and is very fast.
I was wondering if anyone knows a way to have it replace everything but "a certain color and similar colors"? XTRA's code is over my head for me to modify it, and I understand it involves machine code to be fast...
Thanks in advance. Simon
Re: Image Filter everything but a range of similar colors
Call this line:
Multiple times. Use a different TargetColor for each call. No machine code needed, just plain AutoHotkey is good to modify this code!
Code: Select all
DllCall(ColorFilterMCode, "uint", scan, "int", w, "int", h, "int", Stride, "uint", TargetColor, "uint", ReplaceColor, "cdecl")
Re: Image Filter everything but a range of similar colors
An issue with that approach is that if you want to use the equivalent of just 10 shades of allowable variation, it results in a large number of different color codes (almost 10000). That's because the range gets applied per RGB component, so a range of 10 results in 21 x 21 x 21 = 9261 different shades (21 each because it's the nominal itself plus 10 in either direction). So you'd have to loop through that DLL call 9261 times. And a range of 10 isn't even that wide of a range. That range is barely even noticeable to a human eye, and it goes up exponentially as you widen it. Just making it range of 20 would result in 68921 different hues to check. I'm guessing it wouldn't be very fast to loop that many times.
A modification of @Xtra's C function would just have it check to see if each component is within the allowable range instead of being equal to the specified color. It wouldn't even need to loop.
A modification of @Xtra's C function would just have it check to see if each component is within the allowable range instead of being equal to the specified color. It wouldn't even need to loop.
Re: Image Filter everything but a range of similar colors
thanks @iseahound and @boiler for your answers
Yeah, calling the DLL a thousand times is probably not the fastest way to do it. But i'll give it a try.
@Xtra 's code is great, but it has machine code. He showed the sourcecode in his thread and I could convert this to my needs, but I have no clue how to convert that into machine code... or do you guys?
Greets Simon
Yeah, calling the DLL a thousand times is probably not the fastest way to do it. But i'll give it a try.
@Xtra 's code is great, but it has machine code. He showed the sourcecode in his thread and I could convert this to my needs, but I have no clue how to convert that into machine code... or do you guys?
Greets Simon
Re: Image Filter everything but a range of similar colors
Xtra had used an online compiler that is no longer working, but there are other methods as described in this Mcode tutorial.
Re: Image Filter everything but a range of similar colors
Looping this function will not work. After the first call there is only 2 colors the TargetColor and ReplaceColor.
This function replaces all but the TargetColor
Re: Image Filter everything but a range of similar colors
Thanks. Interesting tutorial... This looks like an awful lot of crazily complicated work for a simple guy like me. Hmmm... I'll see what I can doboiler wrote: ↑10 Aug 2022, 09:09Xtra had used an online compiler that is no longer working, but there are other methods as described in this Mcode tutorial.
@iseahound: Do you think, your ImagePut-Library could be used for the job?
Greets Simon
Re: Image Filter everything but a range of similar colors
Yes you would just do something like:
Just to be clear, the DllCall is the fastest part of the code.
Code: Select all
pic := ImagePutBuffer("myimage.png")
pic.ColorKey(0xFFFF0000, 0x00000000) ; Repeat this as many times as you need.
Re: Image Filter everything but a range of similar colors
viewtopic.php?t=51100#p225689
Only the 64bit version posted in the linked post worked for me when i tested it last. But it does have variation option.
The 32bit version of Tics works.
Only the 64bit version posted in the linked post worked for me when i tested it last. But it does have variation option.
The 32bit version of Tics works.
Re: Image Filter everything but a range of similar colors
@Xtra
Is this the source code? I feel like you want a working 32 and 64 bit version, just describe what the function does and I'll have something posted later today
Alright I added a new updated version into the old thread.
viewtopic.php?f=76&t=51100&p=478687#p478687
Works on 32 bit unicode and 64 bit Windows 11. I refactored the C code so it should perform much faster, as you can see by the length of the base64 code.
Is this the source code? I feel like you want a working 32 and 64 bit version, just describe what the function does and I'll have something posted later today
Code: Select all
int Gdip_FilterColor(unsigned char * Bitmap, int w, int h, int Stride, unsigned int Color, unsigned int ReplaceColor, int v)
{
unsigned int p, A1, R1, G1, B1, A2, R2, G2, B2, tA, tR, tG, tB;
A1 = (Color & 0xff000000) >> 24;
R1 = (Color & 0x00ff0000) >> 16;
G1 = (Color & 0x0000ff00) >> 8;
B1 = Color & 0x000000ff;
A2 = (ReplaceColor & 0xff000000) >> 24;
R2 = (ReplaceColor & 0x00ff0000) >> 16;
G2 = (ReplaceColor & 0x0000ff00) >> 8;
B2 = ReplaceColor & 0x000000ff;
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
p = (4*x)+(y*Stride);
tA = Bitmap[3+p];
tR = Bitmap[2+p];
tG = Bitmap[1+p];
tB = Bitmap[p];
if ((tA <= A1+v && tA >= A1-v) && (tR <= R1+v && tR >= R1-v) && (tG <= G1+v && tG >= G1-v) && (tB <= B1+v && tB >= B1-v))
{
Bitmap[3+p] = A2;
Bitmap[2+p] = R2;
Bitmap[1+p] = G2;
Bitmap[p] = B2;
}
}
}
return 0;
}
viewtopic.php?f=76&t=51100&p=478687#p478687
Works on 32 bit unicode and 64 bit Windows 11. I refactored the C code so it should perform much faster, as you can see by the length of the base64 code.
Re: Image Filter everything but a range of similar colors Topic is solved
Hello @iseahound and @Xtra
Well, I finally solved my problem using your functions. Works like a charm and it is really really fast. Here's my code:
Thanks a ton, you made my day. Spitzi
Well, I finally solved my problem using your functions. Works like a charm and it is really really fast. Here's my code:
Code: Select all
#include Gdip_All_latest.ahk
#SingleInstance Force
#NoEnv
start := A_TickCount
FilterImage("C:\Temp\example5.png")
duration := A_TickCount - start
MsgBox, Time:%duration%
start := A_TickCount
FilterImageFaster("C:\Temp\example4.png")
duration := A_TickCount - start
MsgBox, Time:%duration%
return
^<::
Reload
return
; Filters an image with grayscale stuff and colored text on it to become an image with just the text in white/black
; using standard GDIP functions and iterating through pixels
; every grey pixel (R=G=B ) becomes black
; every color pixel (nor R=G=B) becomes white
FilterImage(imagePath) {
token := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile(imagePath)
Gdip_GetImageDimensions(pBitmap, Width, Height)
x := 1
while (x < Width) {
y := 1
while (y < Height) {
ARGB := Gdip_GetPixel(pBitmap, x, y)
Gdip_FromARGB(ARGB, A, R, G, B)
if ((R==G) && (R==B)) {
Gdip_SetPixel(pBitmap, x, y,"0xFF000000")
} else {
Gdip_SetPixel(pBitmap, x, y,"0xFFFFFFFF")
}
y += 1
}
x += 1
; ToolTip, x:%x%
}
Gdip_Savebitmaptofile(pBitmap, "C:\Temp\filtered.png")
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(token)
}
; same thing, but using the incredibly fast Functions with Mcode of iseahound and xtra
FilterImageFaster(imagePath) {
token := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile(imagePath)
Gdip_FilterColor(pBitmap, 0xFFfcbf45, 0xFF00FF00, 50) ; everything I want is now green
Gdip_ColorFilter(pBitmap, 0xFF00FF00, 0xFF000000) ; leave the green stuff, rest black
Gdip_ColorFilter(pBitmap, 0xFF000000, 0xFFFFFFFF) ; green stuff is now white
Gdip_Savebitmaptofile(pBitmap, "C:\Temp\filtered.png")
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(token)
}
; Filters everything away except pixels of key-Color and variations of it, those are replaced by value
; Courtesy of iseahound
; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=51100&p=478687#p478687
; -----------------------------------------------------------------------
Gdip_FilterColor(pBitmap, key, value, variation) {
; Get Bitmap width and height.
DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
; Create a pixel buffer.
VarSetCapacity(Rect, 16, 0) ; sizeof(Rect) = 16
NumPut( width, Rect, 8, "uint") ; Width
NumPut( height, Rect, 12, "uint") ; Height
VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0) ; sizeof(BitmapData) = 24, 32
DllCall("gdiplus\GdipBitmapLockBits"
, "ptr", pBitmap
, "ptr", &Rect
, "uint", 3 ; ImageLockMode.ReadWrite
, "int", 0x26200A ; Format32bppArgb
, "ptr", &BitmapData)
Scan0 := NumGet(BitmapData, 16, "ptr")
; C source code - https://godbolt.org/z/3Gf341hsb
static code := 0
if !code {
b64 := (A_PtrSize == 4)
? "VYnlVlNSikUQi3UIilUcik0gil0kiEX3ikUUiEX2ikUYiEX1O3UMcy2KRgI4RfdyIDpF9nIbikYBOEX1chM40HIPigY4wXIJONhyBYtFKIkGg8YE685YW15dww=="
: "VlNEilQkOESKXCRAilwkSECKdCRQSInISDnQczGKSAJBOMhyI0Q4yXIeikgBQTjKchZEONlyEYoIOMtyC0A48XIGi0wkWIkISIPABOvKW17D"
s64 := StrLen(RTrim(b64, "=")) * 3 // 4
code := DllCall("GlobalAlloc", "uint", 0, "uptr", s64, "ptr")
DllCall("crypt32\CryptStringToBinary", "str", b64, "uint", 0, "uint", 0x1, "ptr", code, "uint*", s64, "ptr", 0, "ptr", 0)
DllCall("VirtualProtect", "ptr", code, "ptr", s64, "uint", 0x40, "uint*", op:=0)
}
v := abs(variation)
r := ((key & 0xFF0000) >> 16)
g := ((key & 0xFF00) >> 8)
b := ((key & 0xFF))
; When doing pointer arithmetic, *Scan0 + 1 is actually adding 4 bytes.
DllCall(code, "ptr", Scan0, "ptr", Scan0 + 4*width*height
, "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)
, "uint", value)
; Write pixels to bitmap.
DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", &BitmapData)
return pBitmap
}
; Filters everything away except pixels of TargetColor
; Courtesy of Xtra
; https://www.autohotkey.com/boards/viewtopic.php?style=1&t=98341#p436534
; -----------------------------------------------------------------------
Gdip_ColorFilter(pBitmap, TargetColor, ReplaceColor)
{
static ColorFilterMCode
if (ColorFilterMCode = "")
{
if (A_PtrSize = 4)
mCode := ""
. "2,x86:VVdWU4PsBItUJCSLTCQoi1wkLItsJByNQgOF0g9JwotU"
. "JCCByQAAAP/B+AKBywAAAP+F0n44he1+NIt0JBjB4ALB5QKJBC"
. "Qx/420JgAAAACNFC6J8DsIdAKJGIPABDnQdfODxwEDNCQ5fCQg"
. "deKDxAQxwFteX13D"
else
mCode := ""
. "2,x64:VlNEi1QkQEGNWQNFhclBD0nZRItMJDhBgcoAAAD/wfsC"
. "QYHJAAAA/0WFwH5QhdJ+TI1C/0hj20Ux20jB4wJIjTSFBAAAAG"
. "YuDx+EAAAAAABIjRQOSInIZg8fhAAAAAAARDsIdANEiRBIg8AE"
. "SDnQde9Bg8MBSAHZRTnYddMxwFtew5CQkJA="
ColorFilterMCode := MCode(mCode)
}
Gdip_GetImageDimensions(pBitmap, w, h)
Gdip_LockBits(pBitmap, 0, 0, w, h, stride, scan, bitmapData)
DllCall(ColorFilterMCode, "uint", scan, "int", w, "int", h, "int", Stride, "uint", TargetColor, "uint", ReplaceColor, "cdecl")
Gdip_UnlockBits(pBitmap, bitmapData)
}
MCode(mcode)
{
static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m))
return
if (!DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0))
return
p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
if (c="x64")
DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
if (DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0))
return p
DllCall("GlobalFree", "ptr", p)
}