首页 > 解决方案 > 有没有办法将 Tkinter 画布的内容捕获到图像中?

问题描述

我正在尝试使用卷积神经网络构建手写数字识别系统。到目前为止,我已经开发了一个包含画布的 Tkinter 窗口,你可以在上面手写一个数字。在我点击“识别”按钮后,我希望程序拍摄该数字(即画布)的图像并通过神经网络运行以预测该数字。以下是我在网上找到的一些代码的实现:

from tkinter import *
import win32gui
from PIL import ImageGrab, Image

cv = Canvas(width=400, height=400, bg='white', cursor='cross')
cv.grid(row=0, column=0, sticky=W)

hwnd = cv.winfo_id()
cv_rect = win32gui.GetWindoRect(hwnd)
img = ImageGrab.grab(cv_rect)

此代码能够抓取图像。但是,图像不是画布,而是屏幕上的其他位置,通常在画布的西北部。输出看起来像这样:

在此处输入图像描述

但是,如果我将 Tkinter 窗口从计算机屏幕的左上角移开,图像甚至不会捕获 Tkinter 窗口的左上角,如上图所示。

为了进一步了解,这是我在其中构建此 gui 的文件的完整代码。

import numpy as np
from tkinter import *
from PIL import Image, ImageGrab
import win32gui
import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib
import win32api

cnn = tf.keras.models.load_model('\\Users\\hamza\\Documents\\VS Code\\mnist.h5py')

#---------------------------------------------------------------
'''
(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()

X_test = X_test.reshape(-1, 28, 28, 1)

X_test = X_test.astype('float32')

X_test /= 255

y_test = tf.keras.utils.to_categorical(y_test)

y_pred = cnn.predict(X_test)
print(y_pred[3700])
print(y_test[3700])

test_eval = cnn.evaluate(X_test, y_test, verbose=1)

print('Test loss:', test_eval[0])
print('Test accuracy:', test_eval[1])
'''

from pyscreenshot import grab
import pygetwindow as pgw
'''
im = grab(bbox=(500, 200, 800, 400))
im.show()
'''


def predict(img):
    #img = img.resize((28, 28))
    img = img.convert('L')
    img = np.array(img)
    #img.reshape(28, 28)
    plt.imshow(img, cmap=matplotlib.cm.binary, interpolation='nearest')
    plt.show()
    #img = img.reshape(1, 28, 28, 1)/255
    #pred = cnn.predict(img)[0]
    #print(pred)
    #print('---------------------')
    #return np.argmax(pred), max(pred)

def classify():
    '''
    hwnd = cv.winfo_id()
    print(hwnd)
    cv_rect = win32gui.GetWindowRect(hwnd)
    img = ImageGrab.grab(cv_rect)
    '''
    win = pgw.getWindowsWithTitle('tk')[0]
    winleft = win.left+9
    wintop = win.top+7
    winright = win.right-9
    winbottom = win.bottom-9
    final_rect = (winleft,wintop,winright,winbottom)
    img = ImageGrab.grab(final_rect)
    
    digit, accuracy = predict(img)
    print(digit)
    print(accuracy)
    #label.configure(str(digit) + ', ' + str(int(accuracy*100)) + '%')
        
def clear():
    cv.delete('all')
    
def draw(event):
    x = event.x
    y = event.y
    r = 8
    cv.create_oval(x-r, y-r, x+r, y+r, fill='black')

root = Tk()

cv = Canvas(width=400, height=400, bg='white', cursor='cross')
cv.grid(row=0, column=0, pady=2, sticky=W)

label = Label(text='Welcome', font=('Helvetica', 32))
label.grid(row=0, column=1, padx=2, pady=2)

clear_button = Button(text='Clear', command=clear)
clear_button.grid(row=1, column=0, pady=2)

recognize_button = Button(text='Recognize', command=classify)
recognize_button.grid(row=1, column=1, padx=2, pady=2)
        
cv.bind("<B1-Motion>", draw)

mainloop()

有人,请帮忙。

标签: pythontkintercanvas

解决方案


我在我的 GUI 中使用了类似的功能,我一开始也使用win32gui过,但意识到这对于这个小目的来说太过分了,相反,只需PyGetWindow使用PIL.

首先安装模块,在终端中这样说。

pip install PyGetWindow
pip install Pillow

然后,此代码将在 3 秒后自动截取屏幕截图

from tkinter import *
from win32gui import FindWindow, GetWindowRect
import pygetwindow as gw
from PIL import ImageGrab

def ss():
    win = gw.getWindowsWithTitle('Trial')[0]
    winleft = win.left+9
    wintop = win.top+38 #change 38 to 7 to not capture the titlebar
    winright = win.right-9
    winbottom = win.bottom-9
    final_rect = (winleft,wintop,winright,winbottom)
    img = ImageGrab.grab(final_rect)
    img.save('Required Image.png')
#making the tkinter window
root = Tk()
root.title('Trial')

cv = Canvas(width=400, height=400, bg='white', cursor='cross')
cv.grid(row=0, column=0, sticky=W)

root.after(3000,ss)

root.mainloop()

在这里,win = gw.getWindowsWithTitle('Title of the window')[0]您必须调用您的窗口名称,就像您设置的窗口名称一样,root.title('Hello World')然后'Title of the window'将替换为'Hello World'。或者默认情况下它会 tkinter 标题将是'tk'.

或者,如果您仍然不愿意使用win32gui,请更换您的ss()to,

def ss():
    win = FindWindow(None, 'Title of the window')
    rect = GetWindowRect(win)
    list_rect = list(rect)
    list_frame = [-9, -38, 9, 9]
    final_rect = tuple((map(lambda x,y:x-y,list_rect,list_frame))) #subtracting two lists

    img = ImageGrab.grab(bbox=final_rect)
    img.save('Image.png')

额外提示:

  • 为什么我要从像素中减去一些数量?这是因为,windows 有像窗口阴影效果这样的装饰,它们也是窗口的一部分,将包含在屏幕截图中,所以我用它来去除那些额外的像素,我想它会让你的神经网络更加高效,无需查看垃圾物品。

这是我得到的图像:

在此处输入图像描述

希望这对您有所帮助,如果有任何错误或疑问,请告诉我。

额外参考 - ImageGrab 没有抓取精确的 bbox 图像

干杯


推荐阅读