ToGrayscale() : Converts GDI bitmap to Greyscale

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

ToGrayscale() : Converts GDI bitmap to Greyscale

18 Sep 2020, 16:50

  • Screen shot of demo
    ToGrayscale.PNG
    ToGrayscale.PNG (74.63 KiB) Viewed 4352 times
     

    The function: Along with a demo

    Code: Select all

    ToGrayscale(sBM) {                            ; By SKAN on CR7J/D39L @ tiny.cc/tograyscale
      Local  ; Original ver. GDI_GrayscaleBitmap() @ https://autohotkey.com/board/topic/82794-
    
      P8:=(A_PtrSize=8),  VarSetCapacity(BM,P8? 32:24, 0)
      DllCall("GetObject", "Ptr",sBM, "Int",P8? 32:24, "Ptr",&BM)
      W := NumGet(BM,4,"Int"), H := NumGet(BM,8,"Int")
      sDC := DllCall( "CreateCompatibleDC", "Ptr",0, "Ptr")
    
      DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",sDC, "Ptr",sBM, "Ptr"))
    
      tBM := DllCall( "CopyImage", "Ptr"
           , DllCall( "CreateBitmap", "Int",1, "Int",1, "Int",0x1, "Int",8, "Ptr",0, "Ptr")
           , "Int",0, "Int",W, "Int",H, "Int",0x2008, "Ptr")
    
      tDC := DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
      DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",tDC, "Ptr",tBM, "Ptr"))
    
      Loop % (255, n:=0x000000, VarSetCapacity(RGBQUAD256,256*4,0))
            Numput(n+=0x010101, RGBQUAD256, A_Index*4, "Int")
      DllCall("SetDIBColorTable", "Ptr",tDC, "Int",0, "Int",256, "Ptr",&RGBQUAD256)
    
      DllCall("BitBlt",   "Ptr",tDC, "Int",0, "Int",0, "Int",W, "Int",H
                        , "Ptr",sDC, "Int",0, "Int",0, "Int",0x00CC0020)
    
    Return % (tBM, DllCall("DeleteDC", "Ptr",sDC), DllCall("DeleteDC", "Ptr",tDC))
    }
    ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    ; Demo follows
    
    #NoEnv
    #SingleInstance, Force
    SetWorkingDir, %A_ScriptDir%
    
    If !FileExist("bluebottlefly.jpg")
       URLDownloadToFile, https://www.autohotkey.com/boards/download/file.php?id=10624
                        , bluebottlefly.jpg
    
    hBM1 := LoadPicture("bluebottlefly.jpg")
    hBM2 := ToGrayscale(hBM1)
    Gui, Margin, 24, 24
    Gui, Add, Picture,,    HBITMAP:%hBM1%
    Gui, Add, Picture,x+m, HBITMAP:%hBM2%
    Gui, Show,, ToGrayscale()
    Return
    
 
 
FsGrayscale()
 
 
My Scripts and Functions: V1  V2
iseahound
Posts: 1445
Joined: 13 Aug 2016, 21:04
Contact:

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

19 Sep 2020, 08:23

Wow. Are the calculations from RGB to Greyscale done by windows itself? I know that RGB channels have different color weights, but I'm not seeing how that is accounted for in the code. (Although your sample looks very good.)

Code: Select all

      ; Returns a value between 0-255 when given an RGB value. 
      grayscale(sRGB) {
         static rY := 0.212655
         static gY := 0.715158
         static bY := 0.072187

         c1 := 255 & ( sRGB >> 16 )
         c2 := 255 & ( sRGB >> 8 )
         c3 := 255 & ( sRGB )

         loop 3 {
            c%A_Index% := c%A_Index% / 255
            c%A_Index% := (c%A_Index% <= 0.04045) ? c%A_Index%/12.92 : ((c%A_Index%+0.055)/(1.055))**2.4
         }

         v := rY*c1 + gY*c2 + bY*c3
         v := (v <= 0.0031308) ? v * 12.92 : 1.055*(v**(1.0/2.4))-0.055
         return Round(v*255)
      }
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

19 Sep 2020, 08:58

iseahound wrote:Wow. Are the calculations from RGB to Greyscale done by windows itself?
Yes. Windows uses Nearest match method.
The magic is here:
 

Code: Select all

  Loop % (255, n:=0x000000, VarSetCapacity(RGBQUAD256,256*4,0))
        Numput(n+=0x010101, RGBQUAD256, A_Index*4, "Int")
  DllCall("SetDIBColorTable", "Ptr",tDC, "Int",0, "Int",256, "Ptr",&RGBQUAD256)
 
I create a 256 color table with 256 shades of gray and windows matches nearest color in the table,
probably based on luminosity.
 
Try replacing n+=0x010101 with n+=0x000101 and you will see a Cyan-scaled image instead of GrayScale.
I conceived/devised this method. :)

:thumbup:
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

19 Sep 2020, 17:49

iseahound wrote: (Although your sample looks very good.)
I had never compared the results with an another program.
I did today. Thanks. I saved hBitmap with GDIP [ SavePicture() ]
 
  • Adobe Photoshop 2020
    photoshop.png
    photoshop.png (99.64 KiB) Viewed 4243 times
     
  • ToGrayscale
    tograyscale.png
    tograyscale.png (77.11 KiB) Viewed 4243 times
     
  • Original / source
    Image
     
My Scripts and Functions: V1  V2
blue83
Posts: 157
Joined: 11 Apr 2018, 06:38

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 00:45

Hi @SKAN ,

Great and useful function.

One question, can we use this script to convert image in black and white using treshold like in python?

And maybe take one picture and create image augumentation to make few images from one image for use in training object detection.

Thanks,
blue
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 05:14

blue83 wrote: Great and useful function.
 
Thank you :)
 
blue83 wrote: can we use this script to convert image in black and white using treshold like in python?
 
No built-in method in GDI for it. The returned hBitmap has DIB section. We may get a pointer to the bits and do the conversion manually,
which is usually best done with machine code.
 
blue83 wrote: And maybe take one picture and create image augumentation to make few images from one image for use in training object detection.
 
Its above my skillset. I had to Bing to get the faintest idea on what you're talking about. :(


:thumbup: :)
blue83
Posts: 157
Joined: 11 Apr 2018, 06:38

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 05:27

Haha, thank you anyway :)

Most OCR works better with grayscale image so this is great function.

Thanks,
blue
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 15:01

@SKAN, thanks for another great addition. I don't think anyone else has such an arsenal of small but very useful scripts.
By the way, for me on both Win10 and Win7, AHK 1.1.33.02, it works great with 32-bit, but for 64-bit only the first image appears.

32-bit:
grayscale2.PNG
grayscale2.PNG (95.1 KiB) Viewed 4170 times
64-bit:
grayscale1.PNG
grayscale1.PNG (83.3 KiB) Viewed 4170 times
Regards,
burque505
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 15:22

burque505 wrote: @SKAN, thanks for another great addition. I don't think anyone else has such an arsenal of small but very useful scripts.
 
Thanks :)
 
burque505 wrote: By the way, for me on both Win10 and Win7, AHK 1.1.33.02, it works great with 32-bit, but for 64-bit only the first image appears.
 
Thanks for the report. Fixed.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

20 Sep 2020, 15:38

thanks, very useful and clever solution!
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

21 Sep 2020, 06:03

blue83 wrote: can we use this script to convert image in black and white using treshold like in python?
blue83 also wrote: 
Most OCR works better with grayscale image so this is great function.
 
Oh! OCR.
It seems simple to add threshold parameter to this function.
But I'm not familiar with python. Do you have a link/screenshot to show how threshold affect the original image
blue83
Posts: 157
Joined: 11 Apr 2018, 06:38

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

21 Sep 2020, 07:18

Hi @SKAN ,

Here it is.

https://techtutorialsx.com/2019/04/13/python-opencv-converting-image-to-black-and-white/

Also if there is anything to change image brightness?

Thanks,
blue
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

21 Sep 2020, 08:00

@blue83 Thanks for the link. I will look into it.
iseahound
Posts: 1445
Joined: 13 Aug 2016, 21:04
Contact:

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

21 Sep 2020, 13:31

You're right about the nearest neighbor. I didn't notice at first, but it doesn't seem to be true greyscale. I can see topological rings of grey in the blurred background of the butterfly picture that aren't present in the photoshop equivalent. You could randomize noise and dither the photo to prevent such occurrences, but I think the converting each channel independently and then forming a weighed average has a better visual perception to greyscale.

My greyscale function only converts a single pixel :(
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

13 Nov 2020, 21:46

iseahound wrote:
21 Sep 2020, 13:31
You're right about the nearest neighbor. I didn't notice at first, but it doesn't seem to be true greyscale.
 
It is a true grayscale. You don't see any colors in it. :D
 
iseahound wrote:
21 Sep 2020, 13:31
I can see topological rings of grey in the blurred background of the butterfly picture that aren't present in the photoshop equivalent.
 
I thought the difference is negligible, but no.. you're right. Color matching is botchy. Here is the proof:
 
image.png
image.png (7.06 KiB) Viewed 3911 times
 

Code: Select all

#NoEnv
#Warn
#SingleInstance, Force
SetBatchLines -1

; Creating 32 bit 256x1 color bitmap with all shades of gray
VarSetCapacity(BMBITS, 256*4, 0),  pBits := &BMBITS+4,  Gray := 0
Loop 255
 pBits := NumPut(Gray += 0x010101, pBits+0, "Int")
hBM1 := DllCall("CreateBitmap", "Int",256, "Int",1, "Int",0x1, "Int",32, "Ptr",&BMBITS, "Ptr")
hBM1 := DllCall("CopyImage", "Ptr",hBM1, Int,0, "Int",256, "Int",48, "Int",0x2008, "Ptr")

; Converting above bitmap to 8bit 256 color grayscale
hBM2 := ToGrayscale(hBM1)

Gui, Font, s9, Segoe UI
Gui, Margin, 24, 24
Gui, Add, Picture, xm ym  Border,          HBITMAP:%hBM1%
Gui, Add, Text,    xm y+0 wp    h24 0x201, 32 bit color bitmap with 256 shades of Gray
Gui, Add, Picture, xm y+m Border,          HBITMAP:%hBM2%
Gui, Add, Text,    xm y+0 wp    h24 0x201, Converted to 8 bit Grayscale with ToGrayscale()

Gui, Show,, ToGrayscale() demo
Return                                                      ; end of auto-execute section4

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ToGrayscale(sBM) {                            ; By SKAN on CR7J/D39J @ tiny.cc/tograyscale
  Local  ; Original ver. GDI_GrayscaleBitmap() @ https://autohotkey.com/board/topic/82794-
  VarSetCapacity(BM,24,0),  DllCall("GetObject", "Ptr",sBM, "Int",24, "Ptr",&BM)
  W := NumGet(BM,4,"Int"), H := NumGet(BM,8,"Int")
  sDC := DllCall( "CreateCompatibleDC", "Ptr",0, "Ptr")
  DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",sDC, "Ptr",sBM, "Ptr"))

  tBM := DllCall( "CopyImage", "Ptr"
       , DllCall( "CreateBitmap", "Int",1, "Int",1, "Int",0x1, "Int",8, "Ptr",0, "Ptr")
       , "Int",0, "Int",W, "Int",H, "Int",0x2008, "Ptr")

  tDC := DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
  DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",tDC, "Ptr",tBM, "Ptr"))

  Loop % (255, n:=0x000000, VarSetCapacity(RGBQUAD256,256*4,0))
        Numput(n+=0x010101, RGBQUAD256, A_Index*4, "Int")
  DllCall("SetDIBColorTable", "Ptr",tDC, "Int",0, "Int",256, "Ptr",&RGBQUAD256)

  DllCall("BitBlt",   "Ptr",tDC, "Int",0, "Int",0, "Int",W, "Int",H
                    , "Ptr",sDC, "Int",0, "Int",0, "Int",0x00CC0020)

Return (tBM, DllCall("DeleteDC", "Ptr",sDC), DllCall("DeleteDC", "Ptr",tDC))
}
 
iseahound wrote:
21 Sep 2020, 13:31
I think the converting each channel independently and then forming a weighed average has a better visual perception to greyscale.
My greyscale function only converts a single pixel :(
 
Ok. I will post a different version that does conversion with machine code.

:thumbup:
User avatar
Xtra
Posts: 2750
Joined: 02 Oct 2015, 12:15

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

13 Nov 2020, 23:19

SKAN wrote:
13 Nov 2020, 21:46
Ok. I will post a different version that does conversion with machine code.
@SKAN i did this a few months back and just posted it here:
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=83208
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

14 Nov 2020, 03:51

Xtra wrote:
13 Nov 2020, 23:19
@SKAN i did this a few months back and just posted it here:
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=83208
Hi @Xtra,
Thanks for sharing your Gdip_GrayScale(). :thumbup:
This topic deals with GDI bitmap, So I will comment on your topic instead.
User avatar
Xtra
Posts: 2750
Joined: 02 Oct 2015, 12:15

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

14 Nov 2020, 19:05

@SKAN it will be interesting to see what you come up with. As always thanks for sharing. :thumbup:
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: ToGrayscale() : Converts GDI bitmap to Greyscale

24 Dec 2020, 12:14

Hello @SKAN.
Is it possible to use this to greyscale icon inside an imagelist?

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: guest3456 and 147 guests