Why is OpenCV's imshow function displaying a blank output when I use a custom screen capture class?

I am using low-level windows functions in conjunction with OpenCV to capture a window. I could use ImageGrab.grab(), but I am trying to make my capture faster. I created a class called Window_Capture that runs all of the functions necessary to capture the screen. My issue lies within the member function: get_screenshot(). Whenever I capture the screen, OpenCV’s imshow displays a seemingly blank output. I currently use Opera as the browser window I am trying to display, and I have tried using other browser windows, but all of them produce the same blank output. What am I doing wrong?

Window Capture Class:

import win32gui, win32ui, win32con, win32api
from ctypes import windll
import numpy as np
import cv2

class WindowNotFoundException(Exception):
    def __init__(self, window_name, hwnd=None, debug=False):
        self.window_name = window_name
        self.hwnd = hwnd
        self.debug = debug
        super().__init__()

    def __str__(self):
        if self.debug:
            string = f"Window '{self.window_name}' not found. Hwnd: {self.hwnd}"
        else:
            string = f"Window '{self.window_name}' not found."
        return string

class Window_Capture:
    
    w = 0
    h = 0
    cropped_x = 0
    cropped_y = 0
    offset_x = 0
    offset_y = 0
    hwnd = None

    #constructor
    def __init__(self, window_name=None):
        #find the handle for the window we want to capture
        #if no window name is given, capture the entire screen
        if window_name is None:
            self.hwnd = win32gui.GetDesktopWindow()
        else:
            self.hwnd = win32gui.FindWindow(None, window_name)
        
        if not self.hwnd:
            raise WindowNotFoundException(window_name, self.hwnd, debug=True)

        #get the window size
        window_rect = win32gui.GetWindowRect(self.hwnd)
        self.w = window_rect[2] - window_rect[0]
        self.h = window_rect[3] - window_rect[1]

        #account for the window border and titlebar and cut them off
        border_pixels = 1 # was 8
        titlebar_pixels = 10 # was 30
        self.w = self.w - border_pixels # was (border_pixels * 2)
        # border_pixels = 8
        # titlebar_pixels = 30
        # self.w = self.w - (border_pixels * 2)
        self.h = self.h - titlebar_pixels - border_pixels
        self.cropped_x = border_pixels
        self.cropped_y = titlebar_pixels

        #set the cropped coordinates offset
        self.offset_x = window_rect[0] + self.cropped_x
        self.offset_y = window_rect[1] + self.cropped_y

        #capture a screenshot of a window
    def get_screenshot(self):
        #get the window's device context
        hwndDC = win32gui.GetWindowDC(self.hwnd)
        
        #create a device context for the entire screen
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        
        #create a memory device context for the entire screen
        saveDC = mfcDC.CreateCompatibleDC()
        
        #create a bitmap object
        saveBitMap = win32ui.CreateBitmap()
        
        #set the bitmap object to the size of the window
        saveBitMap.CreateCompatibleBitmap(mfcDC, self.w, self.h)
        
        #copy the window's device context into the bitmap object
        saveDC.SelectObject(saveBitMap)
        
        #copy the window's device context into the memory device context
        result = windll.user32.PrintWindow(self.hwnd, saveDC.GetSafeHdc(), 0)
        
        #if the window was successfully captured
        if result != 0:
            print("[+] Window captured")
            
            #copy the bitmap object into a numpy array
            bmpinfo = saveBitMap.GetInfo()
            bmpstr = saveBitMap.GetBitmapBits(True)
            img = np.fromstring(bmpstr, dtype='uint8')
            img.shape = (bmpinfo['bmHeight'], bmpinfo['bmWidth'], 4)
            
            #crop the image to remove the window border and titlebar
            img = img[self.cropped_y:self.cropped_y+self.h, self.cropped_x:self.cropped_x+self.w]
            
            #convert the image to RGB
            img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGB)
        
        #if the window was not successfully captured
        else:
            print("[-] Failed to capture window")
            img = None
        
        #clean up
        win32gui.DeleteObject(saveBitMap.GetHandle())
        saveDC.DeleteDC()
        mfcDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hwndDC)
        return img

Main function:

window = Window_Capture('Google - Opera')

def Main():
    while(True):
        screen = window.get_screenshot()
        print(screen.shape)
        cv2.imshow('window', cv2.cvtColor(screen, cv2.COLOR_BGR2RGB))
        if cv2.waitKey(25) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            break

Extra Info:
screen.shape returns (13, 197, 3)

A screenshot of what imshow is displaying

A screenshot of what  is displaying

you haven’t checked that you actually HAVE any data. don’t blame imshow/OpenCV when you don’t know what your data is.

crosspost: