2

When i run this code on my desktop pc it runs fine. but when i run it op my laptop something goes wrong when i set the bounding box for the image grab to the bounding box of the windows calculator and the screen recording of the window and places it self a little up and to the left.

import cv2
import numpy as np
from PIL import ImageGrab
import win32gui

def windowGrab(window_title=None):
    if window_title:
        global hwnd
        hwnd = win32gui.FindWindow(None, window_title)
        if hwnd:
            win32gui.SetForegroundWindow(hwnd)
        else:
            print("window not found")

windowGrab("Calculator")

while True:
    left_x, top_y, right_x, bottom_y = win32gui.GetWindowRect(hwnd)
    screen = np.array(ImageGrab.grab( bbox = (left_x, top_y, right_x, bottom_y ) ) )
    cv2.imshow('window', screen)
    if cv2.waitKey(25) & 0xFF == ord('q'):
        cv2.destroyAllWindows()
        break
karlphillip
  • 92,053
  • 36
  • 243
  • 426
Ethan Rork
  • 23
  • 7
  • 1
    Ethan, upvote the answers that were helpful. And in case one solved your problem, click on the checkbox near it to select it as the official answer. By doing these things you are helping to keep the site organized and easier for future visitors to find their answers. – karlphillip Feb 06 '20 at 23:36
  • 1
    It feels so good to spend hours helping someone and they get your code and run off into the sunset. Dude, come back here and provide some feedback for the community that helped you: up vote all answers that were helpful and click on the checkbox near the answer that solved your problem. – karlphillip Feb 07 '20 at 17:01

1 Answers1

2

Here are the problems that I faced while testing your code:

  • Displaying the calculator window wasn't working correctly.
  • win32gui.FindWindow()wasn't finding the correct window so I ended up replacing it. For some reason on Windows 10, win32gui.EnumWindows() lists 2 windows for one Calculator app: one of them has negative width/height values.
  • win32gui.GetWindowRect() returns the incorrect position and dimensions of the window. It seems to think my resolution is 1280x720. This probably happens because DPI scaling is being used.
  • ImageGrab.grab() has trouble taking a screenshot of the app using coordinates and dimensions from the real monitor resolution space (1920x1080).

enter image description here

Obs: this application won't work if the target window is minimized.

Source code:

import cv2
import numpy as np
import sys

import ctypes
import ctypes.wintypes
from ctypes.wintypes import HWND, RECT, DWORD
from ctypes import *

import win32gui
import win32con

from PIL import ImageGrab


# global variables
dwmapi = ctypes.WinDLL("dwmapi")
APP_NAME = ''
win_hwnd = -1


def callback(hwnd, extra):
    wnd_name = win32gui.GetWindowText(hwnd)

    if (wnd_name == APP_NAME):
        rect = win32gui.GetWindowRect(hwnd)
        x = rect[0]
        y = rect[1]
        w = rect[2] - x
        h = rect[3] - y

        #print("Name: %s" % wnd_name)
        #print("\tLocation: (%d, %d)" % (x, y))
        #print("\t    Size: (%d, %d)" % (w, h))

        if (x >= 0 and y >= 0):
            global win_hwnd
            win_hwnd = hwnd


def windowGrab(window_title=None):
    global APP_NAME, win_hwnd
    APP_NAME = window_title

    if (window_title is None) or (len(window_title) == 0):
        print('!!! window_title == None')
        sys.exit(-1)

    # try to find a window with matching title and valid coordinates
    win32gui.EnumWindows(callback, None)

    # check if it has focus
    if (win_hwnd != win32gui.GetForegroundWindow()):
        print('not focused')
        win32gui.SetActiveWindow(win_hwnd)
        win32gui.SetForegroundWindow(win_hwnd)


# main()
windowGrab("Calculator")

# workaround to allow ImageGrab to capture the whole screen
user32 = ctypes.windll.user32
user32.SetProcessDPIAware()

# get monitor resolution
screen_w = ctypes.windll.user32.GetSystemMetrics(0)
screen_h = ctypes.windll.user32.GetSystemMetrics(1)
print('screen_w=', screen_w, 'screen_h=', screen_h)

# loop
while True:
    # retrieve size and position of the window
    rect = RECT()
    DWMWA_EXTENDED_FRAME_BOUNDS = 9
    dwmapi.DwmGetWindowAttribute(HWND(win_hwnd), DWORD(DWMWA_EXTENDED_FRAME_BOUNDS), ctypes.byref(rect), ctypes.sizeof(rect))

    x = rect.left
    y = rect.top
    w = rect.right- x
    h = rect.bottom - y
    print('x=', x, 'y=', y, 'w=', w, 'h=', h)

    if (w == 0 or h == 0):
        continue

    # take a full screenshot of the desktop
    full_screen = np.array(ImageGrab.grab( bbox= (0, 0, screen_w, screen_h) ))
    if (full_screen is None):
        continue

    # crop window area from the screenshot
    cropped_rgb = full_screen[y : y+h, x : x+w]

    # convert from RGB to BGR order so that colors are displayed correctly
    cropped_bgr = cv2.cvtColor(cropped_rgb, cv2.COLOR_RGB2BGR)

    cv2.imshow('window', cropped_bgr)
    key = cv2.waitKey(25)
    if (key & 0xFF) == ord('q'):
        break

cv2.destroyAllWindows()
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • 1
    I found this very helpful for a similar but separate issue @Karlphillip thank you –  Mar 19 '21 at 22:47