Reading pixels in an image

Started by bobert, February 05, 2014, 07:54:20 AM

Previous topic - Next topic

bobert

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!

Bob


td

You could use the WinBatch binary buffer functions to read the image into memory and search for pixels you want to change.  Once found you can use one of the BinaryPoke functions to change the pixels value.  This approach would require a bit of understanding of the structure of bitmap files so that you don't corrupt the file meta data.

Another approach would be to download and install the ImageMagick COM object.  It can be found here:

http://www.imagemagick.org/script/binary-releases.php#windows

The tool is a free, powerful image editing COM object that plays well with WinBatch's COM subsystem.  There are several example of its use in the Tech Database.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

I should have mentioned that changing all of one color in a bitmap to another color  involves finding the color you want to change in the bitmap file's color table and changing that single entry. When I get a chance and if someone else hasn't offered simpler approach, I will try and post an example.

[Correction: the single entry approach applies to 4 and 8 bit color depth bitmaps like the one attached above.  Bitmaps with greater color depth generally have the color values stored at each pixel location instead.]
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

bobert

Just to clarify I do not want to manipulate the image in any way. I just need to know if a specific pixel is color A or B.

I looked at image magick initially but did not see any way to get a color value for an individual pixel or dump the file into an HTML table which would be ideal as I could then turn the table into an array in winbatch.

Any code samples that I could re purpose would be great and thanks for the quick response.


td

Quote from: bobert on February 05, 2014, 10:07:00 AM
Just to clarify I do not want to manipulate the image in any way. I just need to know if a specific pixel is color A or B.

The hard part is identifying the location of and gaining access to a given pixel in the image.  Once that's done, whether or not you modify it is almost trival.

Quote
I looked at image magick initially but did not see any way to get a color value for an individual pixel or dump the file into an HTML table which would be ideal as I could then turn the table into an array in winbatch.

The task of loading a bitmaps pixels into an array is supposedly a three liner with ImageMajickObject but I have never gotten the technique to work in WinBatch.  I suspect I overstated the bit about ImageMajickObject being WinBatch friendly. It is for most tasks but not all.

Quote
Any code samples that I could re purpose would be great and thanks for the quick response.

I think a few GUI function calls using DllCall may be the way to go.  If I get a chance, I will post something.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

....IFICantBYTE

Perhaps display the picture in a dialog or known window and location, then use the GetPixel API in GDI32.DLL feeding it the x,y pixel location you are interested in to return the colour?

See article ID:  W14581 in the Tech Database for the GetPixel API Dll calls.

Also related vaguely to this idea:
W15978
W16227

Regards,
....IFICantBYTE

Nothing sucks more than that moment during an argument when you realize you're wrong. :)

td

The GDI  GetPixel function is one solution but it can be used without having to display the image in a dialog.  It would take maybe three or 4 DllCalls with various functions to get the job done and it's not all the difficult.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

bobert

Yes, I had considered displaying the image in the uppermost left hand corner of the screen and using GetPixel to return the color of x,y but that would not allow the script to run while the system was being used so it would be a last resort. If there are functions that would allow that to happen without tying up the use of the keyboard and mouse while the script is running that would be perfect.

JTaylor

I believe there is code in the Tech Data using the Binary functions for doing stuff with bmp files.   

Jim

bobert

I have looked and did not find anything that would return pixel information from a BMP although there is plenty of stuff that will insert into or otherwise modify a BMP.

JTaylor

But if you know how to get it into a Binary buffer then couldn't you look at/compare the appropriate areas to see what you want?  Here is a link to the layout of bitmap files.

http://en.wikipedia.org/wiki/BMP_file_format


Jim

td

Quote from: bobert on February 06, 2014, 06:39:12 AM
I have looked and did not find anything that would return pixel information from a BMP although there is plenty of stuff that will insert into or otherwise modify a BMP.

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.

Code (winbatch) Select

; 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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Here is one 'GDI' way to create a region the size of the bitmap.
Code (winbatch) Select

; 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)

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

bobert

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)