I know i'm VERY late but maybe itll help someone in the future as this took WAY to long to find. This is a MASSIVE increase in speed. For some reason numpy.asarray() and alot of the other numpy array creation methods are VERY slow. All my findings are mostly based off Adam S answer.
the line
bmparray = numpy.array(bitmap.GetBitmapBits()).astype(numpy.uint8)
on my machine take about 0.25 seconds which is not very nice. To fix this we can avoid creating an array here.
bmpinfo = bitmap.GetInfo()
bmpbits = bitmap.GetBitmapBits(True)
pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpbits, 'raw', 'BGRX', 0, 1)
pil_array = numpy.array(pil_im)
img = cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)
here is my optimized version of the code, it can take ~55 screenshots in 1 seconds, as appose to the old methods getting ~6 per second:
import win32gui
import win32ui
import win32con
import numpy, cv2
from PIL import Image
def convert(bitmap) -> cv2.Mat:
bmpinfo = bitmap.GetInfo()
bmpbits = bitmap.GetBitmapBits(True)
pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpbits, 'raw', 'BGRX', 0, 1)
pil_array = numpy.array(pil_im)
return cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)
def take_screen_shot():
# https://stackoverflow.com/questions/3586046/fastest-way-to-take-a-screenshot-with-python-on-windows
w = 1920 # set this
h = 1080 # set this
hwnd = win32gui.FindWindow(None, "filler")
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, w, h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0,0),(w, h) , dcObj, (0,0), win32con.SRCCOPY)
image = convert(dataBitMap)
# Free Resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
return image
Edit:
Okay I got curious and ended up using GPT3 to assist to see if could come up with the same answer (with some assistance) and holy smokes did it make a difference.
using this code:
def take_screenshot():
# Find the handle for the window named "filler"
hwnd = win32gui.FindWindow(None, "filler")
# Get the dimensions of the window
left, top, right, bottom = win32gui.GetClientRect(hwnd)
w = right - left # set size here if you want
h = bottom - top # set size here if you want
# Create a device context (DC) for the window
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
dc = mfcDC.CreateCompatibleDC()
# Create a bitmap object and select it into the device context
bmp = win32ui.CreateBitmap()
bmp.CreateCompatibleBitmap(mfcDC, w, h)
dc.SelectObject(bmp)
# Copy the contents of the window into the bitmap object
dc.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)
# Convert the bitmap object to a numpy array
signedIntsArray = bmp.GetBitmapBits(True)
img = np.frombuffer(signedIntsArray, dtype='uint8')
img.shape = (h, w, 4)
# Clean up
dc.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
win32gui.DeleteObject(bmp.GetHandle())
return img
this code allowed me personally to take screenshots at ~100 per second