Reputation: 229361
If my windows is in 32-bit color depth mode, then the following code gets a nice PIL Image from a window:
def image_grab_native(window):
hwnd = win32gui.GetDesktopWindow()
left, top, right, bot = get_rect(window)
w = right - left
h = bot - top
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0, 0), (w, h), mfcDC, (left, top), win32con.SRCCOPY)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer(
'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
return im
However, when running in 16-bit mode, I get the error:
>>> image_grab_native(win)
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
image_grab_native(win)
File "C:\claudiu\bumhunter\finderbot\ezpoker\utils\win32.py", line 204, in image_grab_native
bmpstr, 'raw', 'BGRX', 0, 1)
File "c:\python25\lib\site-packages\PIL\Image.py", line 1808, in frombuffer
return apply(fromstring, (mode, size, data, decoder_name, args))
File "c:\python25\lib\site-packages\PIL\Image.py", line 1747, in fromstring
im.fromstring(data, decoder_name, args)
File "c:\python25\lib\site-packages\PIL\Image.py", line 575, in fromstring
raise ValueError("not enough image data")
ValueError: not enough image data
How should I form the frombuffer
call to work in 16-bit mode? Also how can I make this function work in any bit depth mode, instead of say having to pass it as a parameter?
UPDATE: From this question I learned I must use "BGR;16" instead of "BGRX" for the 2nd mode parameter. It takes a correct picture, either specifying stride or not. The problem is that the pixel values are slightly off on some values:
x y native ImageGrab
280 0 (213, 210, 205) (214, 211, 206)
280 20 (156, 153, 156) (156, 154, 156)
280 40 (213, 210, 205) (214, 211, 206)
300 0 (213, 210, 205) (214, 211, 206)
just a sample of values taken from the same window. the screenshots look identical to the naked eye, but i have to do some pixel manipulation.. also the reason I want to use the native approach at all is that it's a bit faster and it behaves better when running inside virtual machines with dual monitors.. (yes pretty randomly complicated I know).
Upvotes: 0
Views: 5149
Reputation: 308176
For the stride
parameter, you need to give the row size in bytes. Your pixels are 16 bits each so you might naively assume stride = 2*bmpinfo['bmWidth']
; unfortunately Windows adds padding to make the stride an even multiple of 32 bits. That means you'll have to round it to the next highest multiple of 4: stride = (stride + 3) / 4) * 4
.
The documentation doesn't mention a 16-bit raw format so you'll have to check the Unpack.c module to see what's available.
The final thing you'll notice is that Windows likes to make its bitmaps upside down.
Edit: Your final little problem is easily explained - the conversion from 16 bit to 24 bit is not precisely defined, and an off-by-one difference between two different conversions is perfectly normal. It wouldn't be hard to adjust the data after you've converted it, as I'm sure the differences are constant based on the value.
Upvotes: 2