一、截图与文本识别小工具的制作
- 工具设计
该截图与文本识别小工具的设计主要包括以下几个部分:
- 截图模块:负责截取屏幕内容,并将截取的图片保存到本地文件夹内。
- OCR模块:调用Paddle OCR库,对截取的图片进行文本识别,并将识别结果以文本形式输出。
- 用户界面:提供简洁明了的操作界面,方便用户进行截图和文本识别操作。
- 技术实现
- 截图模块的实现:使用第三方截图库tkinter与PIL进行截图操作,可以用鼠标自由选择截屏范围,并保存截取的图片。
- OCR模块的实现:引入Paddle OCR库,然后调用Paddle OCR的识别进行文本识别,在第一次识别中会自动下载轻量化的OCR模型库。识别结果会输出到用户界面,结果包括txt文本。
- 用户界面的实现:使用图形用户界面(GUI)框架Tkinter设计简洁明了的操作界面,包括截图按钮、识别按钮、结果显示框等。通过信号或事件监听器实现用户界面的交互逻辑。
- 部署与测试
- 部署:条件较复杂,由于是非联网、非接口的,离线OCR模型库。需要利用机载的GPU或者CPU进行计算,要求具备计算能力在3.5以上的GPU或其它,并且安装有Paddle适配版本的CUDA与Cudnn软件,且安装有满足Paddle需求的第三方库,详情见官方文档“Paddle的Windows部署教程”。
- 测试:在多种场景下对工具进行测试,包括不同字体、大小、布局和背景的文本识别效果测试,以及截图和识别功能的稳定性和性能测试。对于中文的识别效果较好,英文也适用,但缺乏其他语种的切换功能。
二、使用示例
假设用户需要识别一张图片中的文字内容,可以使用该截图与文本识别小工具进行如下操作:
- 打开工具并点击截图按钮,截取需要识别的图片区域,然后自主选择保存位置。
图1 工具界面示意图
- 点击识别按钮,会自动识别本次截屏图片,等待工具调用Paddle OCR进行文本识别。
- 识别完成后,工具会将识别结果以文本形式显示在结果显示框中。用户可以根据需要对识别结果进行复制、编辑或导出等操作。
图2 识字结果示意图
三、缺点与不足
- 识别语种少,仅支持中文、英文和数字,后续将不断优化出切换不同语种功能的按钮。
- 由于调用的OCRC超轻量级模型,大小仅8.6M,所以在识别文字方面,置信度会偏低,时而会出现不准确现象。
- 调用模型简单,所以对图片缺乏预处理,对图片要求较高,常常会出现输出为0的情况。
- 由于使用机载的GPU核心计算,由于算力的不同,在识字时会出现不同程度的卡顿属于正常现象,但也无法避免。
- 由于截屏算法的底层逻辑有所问题,导致Windows系统在笔记本或其他设备下,在文本缩放不为100%时,截图会出现偏差BUG,后续也将会优化解决。
完整代码如下:
#程序中部分代码非原创,参考自CSDN“【Python】用Tkinter实现一个简单的任意区域截图软件”一文中截图功能的实现
#调用paddleOCR代码文字识别部分参考自Paddle官方“十分钟掌握PaddleOCR使用”一文
#AI工具“文心一言”对代码完善亦有帮助
import os
import tkinter
import tkinter.filedialog
import time
from PIL import ImageGrab
from paddleocr import PaddleOCR
a=[]
b=[]
class FreeCapture():
def __init__(self, root, img):
# 变量X和Y用来记录鼠标左键按下的位置
self.X = tkinter.IntVar(value=0)
self.Y = tkinter.IntVar(value=0)
# 屏幕尺寸
screenWidth = root.winfo_screenwidth()
screenHeight = root.winfo_screenheight()
# 创建顶级组件容器
self.top = tkinter.Toplevel(root, width=screenWidth, height=screenHeight)
# 不显示最大化、最小化按钮
self.top.overrideredirect(True)
self.canvas = tkinter.Canvas(self.top, bg='white', width=screenWidth, height=screenHeight)
# 显示全屏截图,在全屏截图上进行区域截图
self.image = tkinter.PhotoImage(file=img)
self.canvas.create_image(screenWidth // 2, screenHeight // 2, image=self.image)
self.lastDraw = None
# 鼠标左键按下的位置
def onLeftButtonDown(event):
self.X.set(event.x)
self.Y.set(event.y)
# 开始截图
self.sel = True
self.canvas.bind('<Button-1>', onLeftButtonDown)
def onLeftButtonMove(event):
# 鼠标左键移动,显示选取的区域
if not self.sel:
return
try: # 删除刚画完的图形,要不然鼠标移动的时候是黑乎乎的一片矩形
self.canvas.delete(self.lastDraw)
except Exception as e:
pass
self.lastDraw = self.canvas.create_rectangle(self.X.get(), self.Y.get(), event.x, event.y, outline='green')
def onLeftButtonUp(event):
# 获取鼠标左键抬起的位置,保存区域截图
self.sel = False
try:
self.canvas.delete(self.lastDraw)
except Exception as e:
pass
time.sleep(0.5)
# 考虑鼠标左键从右下方按下而从左上方抬起的截图
left, right = sorted([self.X.get(), event.x])
top, bottom = sorted([self.Y.get(), event.y])
pic = ImageGrab.grab((left + 1, top + 1, right, bottom))
# 弹出保存截图对话框
fileName = tkinter.filedialog.asksaveasfilename(title='保存截图', filetypes=[('image', '*.jpg *.png')],
defaultextension='.jpg')
if fileName:
pic.save(fileName)
a.append(fileName)
self.top.destroy()
self.canvas.bind('<B1-Motion>', onLeftButtonMove) # 按下左键
self.canvas.bind('<ButtonRelease-1>', onLeftButtonUp) # 抬起左键
# 让canvas充满窗口,并随窗口自动适应大小
self.canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES)
def screenShot():
""" 自由截屏的函数 (button按钮的事件)
"""
# print("test")
root.state('icon') # 最小化主窗体
time.sleep(0.2)
im = ImageGrab.grab()
# 暂存全屏截图
im.save('temp.png')
im.close()
# 进行自由截屏
w = FreeCapture(root, 'temp.png')
button_screenShot.wait_window(w.top)
# 截图结束,恢复主窗口
root.state('normal')
os.remove('temp.png')
def f():
ocr = PaddleOCR(use_angle_cls=True, lang='ch') # 你可以修改 'lang' 参数来适应不同的语言
image_path = a[-1]
result = ocr.ocr(image_path)
s=""
for line in result:
line_text = ''.join([word_info[1][0] for word_info in line])
print(line_text)
b.append(line_text)
a.pop(-1)
main()
b.clear()
result=""
def append_to_text(text_widget, text):
"""在Text组件的末尾追加文本"""
text_widget.insert(tkinter.END, text + '\n') # \n用于在文本末尾添加新行
def main():
root = tkinter.Tk()
root.title("识字结果")
# 创建一个Text组件作为结果输出框
result_text = tkinter.Text(root, height=10, width=50) # 你可以根据需要调整高度和宽度
result_text.pack(pady=20) # 使用pack布局管理器放置Text组件,并添加一些垂直填充
# 示例:向Text组件中追加一些文本
my_list_str = list(map(str, b))
# 使用join()方法将列表转换为字符串
result = ', '.join(my_list_str)
append_to_text(result_text, result)
# 开始tkinter的主事件循环
root.mainloop()
####
root = tkinter.Tk()
root.title('截屏与识图工具by Aloha')
# 指定窗口的大小
root.geometry('500x250')
# 不允许改变窗口大小
root.resizable(False, False)
# ================== 布置截屏按钮 ====================================
button_screenShot = tkinter.Button(root, text='截屏', command=screenShot)
button_screenShot.place(relx=0.2, rely=0.35, relwidth=0.25, relheight=0.25)
button_screenShot = tkinter.Button(root, text='识字', command=f)
button_screenShot.place(relx=0.6, rely=0.35, relwidth=0.25, relheight=0.25)
# ================== 完 =============================================
label = tkinter.Label(root, text="截屏要求Windows文本缩放为100%才可正常截屏,识字利用GPU运算卡顿正常现象")
label.pack(side=tkinter.BOTTOM, fill=tkinter.X, pady=10)
try:
root.mainloop()
except:
root.destroy()