Python - Screenshot of background/inactive window - python

I am attempting to take fast screenshots ready for processing with PIL/Numpy (~0.01s per screenshot) with Python 3.6. Ideally the window would not need to be in the foreground, i.e. even when another window is covering it, the screenshot is still successful.
So far I've modified the code for python 3 from this question: Python Screenshot of inactive window PrintWindow + win32gui
However, all it gets is black images.
import win32gui
import win32ui
from ctypes import windll
from PIL import Image
hwnd = win32gui.FindWindow(None, 'Calculator')
# Get window bounds
left, top, right, bot = win32gui.GetWindowRect(hwnd)
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)
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
print(result)
bmp_info = saveBitMap.GetInfo()
bmp_str = saveBitMap.GetBitmapBits(True)
print(bmp_str)
im = Image.frombuffer(
'RGB',
(bmp_info['bmWidth'], bmp_info['bmHeight']),
bmp_str, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
if result == 1:
im.save("screenshot.png")

This code worked for me with applications in background, not minimized.
import win32gui
import win32ui
def background_screenshot(hwnd, width, height):
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0,0),(width, height) , dcObj, (0,0), win32con.SRCCOPY)
dataBitMap.SaveBitmapFile(cDC, 'screenshot.bmp')
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
hwnd = win32gui.FindWindow(None, windowname)
background_screenshot(hwnd, 1280, 780)

Related

python screengrab problem (on yolov5, pytorch)

im trying to get a screenshot of a window as fast and then inference on yolov5
it works but sometimes it doesnt detect very well compared to using detect.py on the same image. i think its probably because of img shape or array but i dont know where or how to edit those to make it work. can anyone help me with this please?
import torch
import numpy as np
import win32gui
import win32ui
import win32con
w = 800 # set this
h = 600 # set this
bmpfilenamename = "color.bmp" #set this
windowname = 'put windowname'
def screenshot():
hwnd = win32gui.FindWindow(None, windowname)
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)
#save the screenshot
#dataBitMap.SaveBitmapFile(cDC, bmpfilenamename)
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.frombuffer(signedIntsArray, dtype='uint8')
img.shape = (h,w,4)
# Free Resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
#img = img[..., ::-1]
#img = np.ascontiguousarray(img)
return img
#load
model = torch.hub.load('./', 'custom', path='yolov5s.pt', source='local')
#inference
test = screenshot()
results = model(test)
boxes = results.pandas().xyxy[0]
print (boxes)
edit : i figured you can do it by changing the code from this
results = model(test)
to this
results = model(cv.cvtColor(test, cv.COLOR_BGR2RGB))
but isnt this code supposed to do the same? for some reason this one wont work
img[: ,: ,::-1]

Taking a screenshot of everything but 1 window

Ok, so I want to continuously screenshot the main screen (at something like 30 images per second), but I want my PyQt5 app to be invisible on the screenshot image. The app should stay on the screen at the same time (I am drawing things on the app based on the screenshot of the image)
Is there a way for my PyQt5 app to be invisible to my screenshots ?
Here is the function I currently use to take a screenshot :
def screenshot_screen(self, coords=None):
hwnd = win32gui.GetDesktopWindow()
if coords != None:
x0, y0, x1, y1 = coords
else:
x0, y0, x1, y1 = win32gui.GetWindowRect(hwnd)
width = x1 - x0
height = y1 - y0
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (width, height), dcObj, (x0+1, y0+1), win32con.SRCCOPY)
im = dataBitMap.GetBitmapBits(True)
img = np.frombuffer(im, dtype='uint8')
img.shape = (height, width, 4)
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
return img
To be clear, the yellow rects are from my pyqt5 app, I would want them drawing like they are on the right, but I wouldn't want them on the left window that shows the screenshot that is being taken:

Screenshot function does not work on other machines

I am trying to take window screenshot while the windows is in background. The code runs perfectly on my system(HP, win7). The same code fails to give proper output on another system(win7, Lenovo and Dell). It does give the screenshot, but it is not clear. There is black color wherever there is empty area in the window.
Click here to see image
def capture_screen(win_name,outloc,imagename,h):
hwnd = win32gui.FindWindow(None,win_name)
# Get window bounds
left, top, right, bot = win32gui.GetWindowRect(hwnd)
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)
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
#print(result)
bmp_info = saveBitMap.GetInfo()
bmp_str = saveBitMap.GetBitmapBits(True)
#print(bmp_str)
im = Image.frombuffer('RGB',(bmp_info['bmWidth'], bmp_info['bmHeight']),
bmp_str, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
if result == 1:
im.save(outloc+imagename)
To capture any hidden window (Win32, UWP, ...), you can use DWM (DwmRegisterThumbnail )

Fastest way to take screenshot of a window

def take_screenshot(hwnd):
left, top, right, bot = win32gui.GetClientRect(hwnd)
#left, top, right, bot = win32gui.GetWindowRect(hwnd)
width = right - left
height = bot - top
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (width, height), dcObj, (0, 0), win32con.SRCCOPY)
im = dataBitMap.GetBitmapBits(True)
img = np.frombuffer(im, dtype='uint8')
img.shape = (height,width,4)
cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
return img[:,:,:3]
I'm using this code at the moment. Is there any way to make it faster= I want to be able to capture 1080 at 60 fps live. Can I use my gpu for this problem? I have gtx 1070.

Screenshot of inactive window PrintWindow + win32gui

After hours of googling I managed to "write" this:
import win32gui
from ctypes import windll
hwnd = win32gui.FindWindow(None, 'Steam')
hdc = win32gui.GetDC(hwnd)
hdcMem = win32gui.CreateCompatibleDC(hdc)
hbitmap = win32ui.CreateBitmap()
hbitmap = win32gui.CreateCompatibleBitmap(hdcMem, 500, 500)
win32gui.SelectObject(hdcMem, hbitmap)
windll.user32.PrintWindow(hwnd, hdcMem, 0)
Is this a correct way to do this and how would I save an image?
After lots of searching and trying various different methods, the following worked for me.
import win32gui
import win32ui
from ctypes import windll
import Image
hwnd = win32gui.FindWindow(None, 'Calculator')
# Change the line below depending on whether you want the whole window
# or just the client area.
#left, top, right, bot = win32gui.GetClientRect(hwnd)
left, top, right, bot = win32gui.GetWindowRect(hwnd)
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)
# Change the line below depending on whether you want the whole window
# or just the client area.
#result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)
print result
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)
if result == 1:
#PrintWindow Succeeded
im.save("test.png")

Categories