I’m trying to capture sceen using win32 for opencv but when i start it gives me an error about the CreateCompatibleDC function.
That’s the first time i touch this library so i don’t really know how to fix this.
import numpy as np
import win32gui,win32ui,win32con
class WindowCapture:
# properties
w = 0
h = 0
hwnd = None
cropped_x = 0
cropped_y = 0
offset_x = 0
offset_y = 0
# constructor
def __init__(self, window_name):
# find the handle for the window we want to capture
self.hwnd = win32gui.FindWindow(None, window_name)
if not self.hwnd:
raise Exception('Window not found: {}'.format(window_name))
# 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 = 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 so we can translate screenshot
# images into actual screen positions
self.offset_x = window_rect[0] + self.cropped_x
self.offset_y = window_rect[1] + self.cropped_y
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
#dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
# drop the alpha channel, or cv.matchTemplate() will throw an error like:
# error: (-215:Assertion failed) (depth == CV_8U || depth == CV_32F) && type == _templ.type()
# && _img.dims() <= 2 in function 'cv::matchTemplate'
img = img[...,:3]
# make image C_CONTIGUOUS to avoid errors that look like:
# File ... in draw_rectangles
# TypeError: an integer is required (got type tuple)
# see the discussion here:
# https://github.com/opencv/opencv/issues/14866#issuecomment-580207109
img = np.ascontiguousarray(img)
return img
# find the name of the window you're interested in.
# once you have it, update window_capture()
# https://stackoverflow.com/questions/55547940/how-to-get-a-list-of-the-name-of-every-open-window
def list_window_names(self):
def winEnumHandler(hwnd, ctx):
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd))
win32gui.EnumWindows(winEnumHandler, None)
# translate a pixel position on a screenshot image to a pixel position on the screen.
# pos = (x, y)
# WARNING: if you move the window being captured after execution is started, this will
# return incorrect coordinates, because the window position is only calculated in
# the __init__ constructor.
def get_screen_position(self, pos):
return (pos[0] + self.offset_x, pos[1] + self.offset_y)
That’s what i wrote, referring to Learn Code By Gaming video
But when start, here’s the error:
File "C:Xwindowcapture.py", line 49, in get_screenshot
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
win32ui.error: CreateCompatibleDC failed
I think the same error as this thread; Python win32ui.error: CreateCompatibleDC failed
It’s been a few months since I took a look at this code because I couldn’t get past this error.
I’m using source_needle_images
as the artefacts I’m trying to find, and haystack
as the image I’m trying to find them in. My needles are currently two jpg’s however, in future, this may be dozens of jpg’s and my haystack is an application window named wincap
The eventual intention is to «do something» if anything whatsoever from source_needle_images
is found within the application window.
This code is from my main.py, and I’m using two functions search
and windowcapture
windowcapture
captures the application window, search
performs the OCR.
from windowcapture import WindowCapture
from search import Search
# the window to capture
wincap = WindowCapture('P')
# load needle images and start the matching process
source_needle_images = glob.glob(r'C:\\\*.jpg')
search = Search(source_needle_images)
loop_time = time()
while(True):
# get an updated image of the background
haystack_img = wincap.get_screenshot()
# display the processed image
points = search.find(haystack_img, 0.85, 'rectangles')
This is the section of code causing issue;
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
#dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
and this is the error in question;
Traceback (most recent call last):
File "c:\\\main.py", line 23, in <module>
haystack_img = wincap.get_screenshot()
File "c:\\\windowcapture.py", line 52, in get_screenshot
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
win32ui.error: CreateCompatibleDC failed
Updated with requested code:
# 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 Exception('Window not found: {}'.format(window_name))
#python #memory-leaks #pywin32 #win32gui #win32-process
Вопрос:
Я загружаю окно opencv в цикле с помощью этой конкретной процедуры захвата экрана окна ниже.
ПРОБЛЕМА: после сотен циклов в цикле он внезапно выходит из строя в любой из двух ТОЧЕК СБОЯ, отмеченных ниже в коде.
Я подозреваю возможную утечку памяти, но, если я не ошибаюсь, я удаляю и освобождаю то, что требуется, а также (повторно)выбираю объект, прежде чем удалить его.
(Причина, по которой я использую этот метод, заключается в том, что для меня важно иметь возможность захватить конкретное окно, даже если оно неактивно и находится в фоновом режиме, и я не обнаружил, что какой-либо другой модуль/метод действительно работает.)
Что я упускаю из виду?
import win32gui
import win32ui
from PIL import Image
import numpy as np
import cv2
while True:
target_window = win32gui.FindWindow(None, ("Analytics dashboard - Google Chrome"))
hwndDC = win32gui.GetWindowDC(target_window)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC() #### <-- RANDOM FAIL POINT 1: win32ui.error: CreateCompatibleDC failed
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
saveDC.SelectObject(saveBitMap)
result = windll.user32.PrintWindow(target_window, saveDC.GetSafeHdc(), 3)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
mfcDC.DeleteDC() #### <-- RANDOM FAIL POINT 2: win32ui.error: DeleteDC failed
saveDC.DeleteDC()
win32gui.DeleteObject(saveBitMap.GetHandle())
win32gui.ReleaseDC(target_window, hwndDC)
image = cv2.cvtColor(np.array(screen_image), cv2.IMREAD_ANYCOLOR)
Комментарии:
1. Убедитесь
target_window
, что это не0
так . Перейдите в диспетчер задач и следите за «использованием GDI» (по умолчанию он не отображается, вам нужно перейти в раздел «Сведения» и выбрать GDI. Посмотрите, увеличиваются ли объекты GDI. Это проблема, если она превышает 1000 или 2000 дескрипторов.2. @Barmak Shemirani : спасибо за ваш быстрый ответ! Проверено, target_window никогда не равен 0, всегда правильно его получает., также при сбое. Я также проверил объекты GDI и действительно увеличил, как вы указали, запустив приведенный выше код. Вопрос, почему, так как, на мой взгляд, я удаляю, освобождаю и выбираю объект правильно. Могу я спросить, не могли бы вы взять код и любезно протестировать его рядом с собой, чтобы убедиться, что вы можете обнаружить проблему ?
3. У меня нет
windll
cv2
расширения и, я прокомментировал их и запустил ваш код, не смог увидеть утечку GDI. Это не превышает 3. Возможно, у вас есть утечки GDI в другом месте.4. @Barmak Shemirani : ЕЩЕ раз большое спасибо за ваш ответ. Следуя вашему комментарию, я тоже провел дополнительный тест на своей стороне, и я могу подтвердить, что CreateCompatibleDC или DeleteDC завершаются неудачей ТОЛЬКО при добавлении строки PrintWindow. Итак, нет, мы могли бы сузить проблему до окна печати. Я пытался найти здесь какое-либо решение, но ничего не смог найти. Могу я спросить, есть ли у вас какие-либо идеи, как это решить ?
Ответ №1:
Я попробовал использовать приведенный выше код, ctypes.windll.user32.PrintWindow
и утечек GDI не было. PrintWindow
третьим аргументом должно быть PW_CLIENTONLY
(1), или есть вариант без документов PW_RENDERFULLCONTENT
(2). Недокументированный код ненадежен. Я не знаю, к чему относится константа (3).
Если Chrome является верхним окном, вам следует просто сделать снимок экрана рабочего стола. Это было бы совместимо.
Это может помочь, если вы удалите часть кода вне цикла, по крайней мере, это будет более эффективно.
import ctypes
import win32gui
import win32ui
import win32con
from PIL import Image
hdesktop = win32gui.GetDesktopWindow()
(l, r, width, height) = win32gui.GetClientRect(hdesktop)
hdc = win32gui.GetWindowDC(hdesktop)
dc = win32ui.CreateDCFromHandle(hdc)
memdc = dc.CreateCompatibleDC()
bitmap = win32ui.CreateBitmap()
bitmap.CreateCompatibleBitmap(dc, width, height)
memdc.SelectObject(bitmap)
while True:
hwnd = win32gui.FindWindow("Chrome_WidgetWin_1", None)
if hwnd == 0:
break
result = ctypes.windll.user32.PrintWindow(hwnd, memdc.GetSafeHdc(), 2)
if result == 1:
bytes = bitmap.GetBitmapBits(True)
img = Image.frombuffer('RGB', (width, height), bytes, 'raw', 'BGRX', 0, 1)
img.save("file.bmp")
#break
dc.DeleteDC()
memdc.DeleteDC()
win32gui.DeleteObject(bitmap.GetHandle())
win32gui.ReleaseDC(hwnd, hdc)
Вы также можете добавить ctypes.windll.shcore.SetProcessDpiAwareness(2)
сверху
Комментарии:
1. Большое вам спасибо, что уделили мне время. Я полностью понимаю ваши предложения, но, к сожалению, это приводит к черному экрану. Ранее я тоже столкнулся с этой проблемой с моим кодом, поэтому я использовал только целевое окно hwnd, не получая сначала рабочий стол, а также 3-ю константу, которую вы упомянули, ДОЛЖНО быть 3 (хотя я не знаю, почему), иначе по какой-то причине она всегда дает черный экран. Тем не менее, я стараюсь принять предложенные вами изменения и вернуться к вам с результатом.
2. Chrome иногда реагирует на
PW_RENDERFULLCONTENT
(2
), иногда нет. Я думаю , что если вы поставите3
или3000000
, код изменит его на2
. Другая программа может изменить его0
или вернуть значение false. Не существует стандарта для обработки недокументированных констант.3. Это заняло некоторое время, но я рад подтвердить, что ваше предложение вывести из цикла большую часть кода сработало как нельзя лучше ! БОЛЬШОЕ спасибо вам за ваше время и помощь!! Я принял ответ и тоже проголосовал.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
hey there, I’m having difficulty figuring out how to get my template to match the game recording for my code. This is the test code that I’m trying to run, and it just doesn’t seem to work. I do realize I haven’t run the exact code that you put on your videos, but that is because I can’t seem to get it to window/video capture for the game I’m playing, (which is a little frustrating).
Nevertheless, it works for still image and it works fine videocapturing when I don’t try to matchTemplate but the two don’t seem to want to merge.
import numpy as np
from PIL import ImageGrab
import cv2
import time
import threading
def screen_record():
last_time = time.time()
while(True):
# 800x600 windowed mode for GTA 5, at the top left position of your main screen.
# 40 px accounts for title bar.
printscreen = np.array(ImageGrab.grab(bbox=(0,40,800,640)))
print('loop took {} seconds'.format(time.time()-last_time))
last_time = time.time()
img_gray = cv2.imshow('window',cv2.cvtColor(printscreen, cv2.COLOR_BGR2GRAY))
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
eyes_hungry = cv2.imshow('hungry2.jpg',0)
last_time = time.time()
res = cv2.matchTemplate(img_gray, eyes_hungry, cv2.TM_CCOEFF_NORMED)
threshhold = 0.8
loc = np.where(res >= threshhold)
for pt in zip(*loc[::-1]):
w, h = template.shape[::-1]
cv2.rectangle(img_gray, pt, (pt[0]+w, pt[1]+h), (0,255,255), 1)
screen_record()
this is the error or a variation on the same error I get when I try running it:
F:DesktopNeneat-python>grabscreen.py
loop took 0.0657804012298584 seconds
Traceback (most recent call last):
File "F:DesktopNeneat-pythongrabscreen.py", line 29, in <module>
screen_record()
File "F:DesktopNeneat-pythongrabscreen.py", line 22, in screen_record
res = cv2.matchTemplate(img_gray, eyes_hungry, cv2.TM_CCOEFF_NORMED)
cv2.error: OpenCV(4.5.3) C:UsersrunneradminAppDataLocalTemppip-req-build-sn_xpupmopencvmodulesimgprocsrctemplmatch.cpp:588: error: (-215:Assertion failed) corr.rows <= img.rows + templ.rows - 1 && corr.cols <= img.cols + templ.cols - 1 in function 'cv::crossCorr'
if you or anyone might be able to help, I’d really appreciate it, I’ve been banging my head on the wall for a few days now.
Thank you for reading.