WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

DllCall Information

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Reading Pixels in an Image

 Keywords: Read get Pixel Image Bitmap bmp GDI GetPixel LoadImageA GetDC CreateCompatibleDC SelectObject 

;***************************************************************************
;**                     GetPixel to CSV
;**    Get specified number of pixels from upper left corner of image
;**
;** Purpose: Get specified number of pixels and store result in CSV file
;** Inputs:  Bitmap, Number of Rows and columns to get from Bitmap
;** Outputs: CSV File
;** Revisions: 1.0
;***************************************************************************

#DefineFunction Byte2Hex (Byte)
   HexChars="0123456789abcdef"
   h1=StrSub(HexChars,1+(Byte>>4),1)
   h2=StrSub(HexChars,1+(Byte&15),1)
   Return (StrCat(h1,h2))
#EndFunction

#DefineFunction GetPixelColor (strBmpFile,x,y)
   hGdi32  = DllLoad("gdi32.dll")
   hUser32 = DllLoad("user32.dll")
   LR_LOADFROMFILE     = 16
   IMAGE_BITMAP        = 0
   LR_CREATEDIBSECTION = 8192
   hBm=DllCall(hUser32,long:"LoadImageA",long:0,lpstr:strBmpFile,long:IMAGE_BITMAP,long:0,long:0,long:LR_CREATEDIBSECTION|LR_LOADFROMFILE)
   ; Get a DC.
   hWnd=DllCall(hUser32,long:"GetDesktopWindow")
   hDc=DllCall(hUser32,long:"GetDC",long:hWnd)
   ; Create a compatible memory DC.
   hBmDc=DllCall(hGdi32,long:"CreateCompatibleDC",long:hDc)
   ; Select the bitmap into the DC.
   hOldBm = DllCall(hGdi32,long:"SelectObject",long:hBmDc,long:hBm)

   ; Create a clipping region that includes the bitmap.
   nStrctSize = 12
   hStrctBuff = BinaryAlloc(nStrctSize)
   BinaryPoke4(hStrctBuff, 0, nStrctSize)
   BinaryPoke2(hStrctBuff,10, 0) ; Set bit count to 0.
   DllCall(hGdi32,long:"GetDIBits",long:hBmDc, long:hBm, long:0, long:0, long:0,lpbinary:hStrctBuff, long:0)
   nX        = 0
   nY        = 0
   nWidth  = BinaryPeek2(hStrctBuff,4) ; width is the 2 bytes after structure size.
   nHeight = BinaryPeek2(hStrctBuff,6) ; height is the 2 bytes after width.
   BinaryFree(hStrctBuff)

   hRgn = DllCall(hGdi32,long:"CreateRectRgn", long:nX, long:nY,long:nWidth,long:nHeight)

   ; Set the dc's region
   DllCall(hGdi32,long:"SelectClipRgn", long:hBmDc, long:hRgn)

   ; Get the pixel color value at position 0,0
   Pixel =    DllCall(hGdi32,long:"GetPixel", long:hBmDc, long:x,long:y)
   Red=StrFixCharsL((Pixel) & 255,"0",3)
   Grn=StrFixCharsL((Pixel >> 8) & 255,"0",3)
   Blu=StrFixCharsL((Pixel >> 16) & 255,"0",3)
   Hex=StrCat (Byte2Hex(Red),Byte2Hex(Grn),Byte2Hex(Blu))

   ;; Cleanup
   DllCall(hUser32,long:"ReleaseDC",long:hWnd,long:hDc)
   DllCall(hGdi32,long:"DeleteObject",long:hRgn)
   DllCall(hGdi32,long:"DeleteObject",long:hBm)
   DllCall(hGdi32,long:"DeleteDC",long:hBmDc)
   DllFree(hGdi32)
   DllFree(hUser32)

   Return (StrCat(Red,"|",Grn,"|",Blu,"|",Hex))
#EndFunction

inputrow = 10 ; Number of pixel Rows
inputcol = 20 ; Number of pixel Columns
bitmap = "C:\Temp\Pictures\background.bmp"

row=0
col=0
array=ArrDimension(inputrow,inputcol)
ArrInitialize(array, 0)
While row < inputrow
   While col < inputcol
      pixcol=GetPixelColor(bitmap,col,row)
      array[row,col]=pixcol
      col=col+1
   EndWhile
   row=row+1
   col=0
EndWhile
ArrayFilePutCSV("c:\temp\test.csv", array, ",",@TRUE,0)
Exit


Question:

I have scoured the web for a solution to this problem and come to the conclusion that doing it winbatch is probably my best bet as I do not want to learn another language if at all possible. I need to be able to read a pixel in an image and determine if it is one of two colors. Turning the image into an array storing the color value for each pixel seems like the best option but I am somewhat stumped on how to get the pixel info into an array using winbatch.

Here is an example o better illustrate the requirement:

There is a 2x2 pixel BMP file represented below (I have attached a sample BMP as well)

X=black
0=white

0X
00
How can winbatch create an array that will represent the pixel/colors similar to this:
FFFFFF,010101
FFFFFF,FFFFFF
It does not matter how the colors are represented as long as they can be compared to determine if they are color A or B. Dumping the image to a CSV or HTML table would also work but I cannot find a way to do so without employing another language.

Any help you can provide will be greatly appreciated.

Thanks in advance!

Answer:

The GDI GetPixel function is one solution but it can be used without having to display the image in a dialog. Using binary buffers to obtain pixel values it certainly a viable approach. It is probably the better approach if you can make some general assumptions about the format of a the bitmaps you are inspecting. The problem with the approach is that there are a lot of variations in bitmap format, some involve padding of each row and the like.

Here is a very rough example of how to obtain pixel values using the Windows GDI API. It is not all that simple either. It lacks error handling and has not been properly tested. Use at your own risk.

; Get dlls.
hGdi32  = DllLoad("gdi32.dll")
hUser32 = DllLoad("user32.dll")

; Load a bitmap from a file.
strBmpFile = "C:\temp\test.bmp"
LR_LOADFROMFILE     = 16
IMAGE_BITMAP        = 0
LR_CREATEDIBSECTION = 8192
hBm=DllCall(huser32,long:"LoadImageA",long:0,lpstr:strBmpFile,long:IMAGE_BITMAP,long:0,long:0,long:LR_CREATEDIBSECTION|LR_LOADFROMFILE)

; Get a DC.
hWnd=DllCall(hUser32,long:"GetDesktopWindow")
hDc=DllCall(hUser32,long:"GetDC",long:hWnd)

; Create a compatible memory DC.
hBmDc=DllCall(hGdi32,long:"CreateCompatibleDC",long:hDc)

; Select the bitmap into the DC.
hOldBm = DllCall(hGdi32,long:"SelectObject",long:hBmDc,long:hBm)

; Create a clipping region that includes the bitmap.
hRect = BinaryAlloc(16)
DllCall(hUser32, long:"GetClientRect", long:hWnd, lpbinary:hRect)
nX        = BinaryPeek4(hRect,0)
nY        = BinaryPeek4(hRect,4)
nWidth  = BinaryPeek4(hRect,8)
nHeight = BinaryPeek4(hRect,12)
BinaryFree(hRect)
hRgn = DllCall(hGdi32,long:"CreateRectRgn", long:nX, long:nY,long:nWidth,long:nHeight)

; Set the dc's region
DllCall(hGdi32,long:"SelectClipRgn", long:hBmDc, long:hRgn)

; Get the pixel color value at position 0,0
ColorRef =    DllCall(hGdi32,long:"GetPixel", long:hBmDc, long:0,long:0)
Message("Color Value", "Color at 0,0 = ": ColorRef)

;; Cleanup
DllCall(hUser32,long:"ReleaseDC",long:hWnd,long:hDc)
DllCall(hGdi32,long:"DeleteObject",long:hRgn)
DllCall(hGdi32,long:"DeleteObject",long:hBm)
DllCall(hGdi32,long:"DeleteDC",long:hBmDc)
DllFree(hGdi32)
DllFree(hUser32)
The example could be improved by obtain the size of the bitmap and the setting the clipping region to that size. It does appear that the example works without using a clipping region at all but MSFT documents that a clipping region is necessary in order for the GetPixel function to work properly.

User Reply:

Thanks for all the advice and suggestions. In the end I decided that running it on a dedicated VM, setting the wallpaper to the tiled image and using the GetPixelColor function was the quickest solution. Below is the code I used if anyone is interested. It places the color information of each pixel into an array and then dumps it into a CSV.
#DefineFunction Byte2Hex (Byte)
   HexChars="0123456789abcdef"
   h1=StrSub(HexChars,1+(Byte>>4),1)
   h2=StrSub(HexChars,1+(Byte&15),1)
Return (StrCat(h1,h2))
#EndFunction

#DefineFunction GetPixelColor (x,y)
   HDC=DllCall(StrCat(DirWindows(1),"USER32.DLL"),long:"GetDC",lpnull)
   Pixel=DllCall (StrCat(DirWindows(1),"GDI32.DLL"),long:"GetPixel",long:HDC,long:x,long:y)
   Red=StrFixCharsL((Pixel) & 255,"0",3)
   Grn=StrFixCharsL((Pixel >> 8) & 255,"0",3)
   Blu=StrFixCharsL((Pixel >> 16) & 255,"0",3)
   Hex=StrCat (Byte2Hex(Red),Byte2Hex(Grn),Byte2Hex(Blu))
   DllCall (StrCat(DirWindows(1),"USER32.DLL"),long:"ReleaseDC",lpnull,long:hdc)
Return (StrCat(Red,"|",Grn,"|",Blu,"|",Hex))
#EndFunction

row=0
col=0
array=ArrDimension(17,81)
ArrInitialize(array, 0)

Wallpaper("c:\image.bmp", @TRUE)

While row < 17
   While col < 81
      pixcol=GetPixelColor(col,row)
      array[row,col]=pixcol
      col=col+1
   EndWhile
   row=row+1
   col=0
EndWhile

ArrayFilePutCSV("c:\test.txt", array, @TAB,@TRUE,0)

Article ID:   W17795
Filename:   Reading Pixels in an Image .txt
File Created: 2014:02:12:09:30:14
Last Updated: 2014:02:12:09:30:14