python - 有没有办法将 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()
有人,请帮忙。
解决方案
我在我的 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 图像
干杯