这是一个基于EasyOCR的图片文字识别GUI程序,主要功能如下:点击"选择图片"加载要识别的图片,等待自动识别完成,在右侧查看识别结果。识别完成后支持复制选中的文本和导出结果到文件。导出格式支持:TXT格式纯文本内容,CSV格式包含文本和置信度。
适用场景:文档扫描识别、图片文字提取、批量文字处理、数据录入辅助。
首先,创建了OCR程序的主界面布局,采用双栏设计,分为左侧图片区域和右侧结果区域,右侧结果区域采用树形结构显示文本和置信度两列,固定显示30行,自动添加滚动条。并设置了功能按钮区和状态栏,
def create_widgets(self):
"""创建界面组件"""
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧图片显示区域
left_frame = ttk.Frame(main_frame, width=600)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.img_label = ttk.Label(left_frame, text="图片预览区域")
self.img_label.pack(pady=5)
self.canvas = tk.Canvas(left_frame, bg='#f0f0f0', bd=2, relief='groove')
self.canvas.pack(fill=tk.BOTH, expand=True)
ttk.Button(left_frame, text="选择图片", command=self.load_image).pack(pady=10)
# 右侧结果区域
right_frame = ttk.Frame(main_frame, width=600)
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
ttk.Label(right_frame, text="识别结果", style='Title.TLabel').pack(pady=5)
# 修改Treeview配置
self.result_tree = ttk.Treeview(right_frame,
columns=('text', 'confidence'),
show='tree headings', # 显示树形结构和表头
height=30 # 设置显示行数
)
# 配置列
self.result_tree.heading('text', text='识别文本')
self.result_tree.heading('confidence', text='置信度')
self.result_tree.column('text', width=400, anchor='w') # 左对齐
self.result_tree.column('confidence', width=100, anchor='center') # 居中
# 添加滚动条
scrollbar = ttk.Scrollbar(right_frame, orient="vertical",
command=self.result_tree.yview)
self.result_tree.configure(yscrollcommand=scrollbar.set)
# 正确布局
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.result_tree.pack(fill=tk.BOTH, expand=True)
# 在右侧结果区域添加功能按钮
button_frame = ttk.Frame(right_frame)
button_frame.pack(fill=tk.X, pady=5)
ttk.Button(button_frame, text="复制选中",
command=self.copy_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="复制全部",
command=self.copy_all).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="导出结果",
command=self.export_results).pack(side=tk.LEFT, padx=5)
# 底部状态栏
self.status_var = tk.StringVar()
status_bar = ttk.Label(self.root, textvariable=self.status_var, relief='sunken')
status_bar.pack(fill=tk.X, side=tk.BOTTOM)
接下来,实现了图片加载和显示功能。选择图片文件后自动加载并缩放,居中显示预览,自动开始文字识别。
图片加载方法 load_image():支持多种图片格式,并自动开始识别。
def load_image(self):
# 打开文件选择对话框
path = filedialog.askopenfilename(
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")]
)
# 显示加载状态
self.status_var.set(f"正在加载图片: {os.path.basename(path)}...")
# 加载并处理图片
self.original_image = Image.open(path)
self.show_image() # 显示图片
self.process_ocr() # 开始OCR识别
图片显示方法 show_image():智能缩放,保持图片比例,适应画布大小,高质量缩放算法。
def show_image(self):
# 获取画布尺寸
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
# 计算缩放比例
ratio = min(canvas_width/img_width, canvas_height/img_height)
new_size = (int(img_width*ratio), int(img_height*ratio))
# 缩放并显示
resized = self.original_image.resize(new_size, Image.LANCZOS)
# 更新Canvas
self.canvas.delete("all")
self.canvas.create_image(
canvas_width//2,
canvas_height//2,
anchor=tk.CENTER,
image=self.tk_image
)
下面,实现了OCR文字识别的核心处理逻辑。
预处理检查:检查图片是否已加载、将PIL图像转换为numpy数组。
if self.original_image is None:
self.status_var.set("请先加载图片")
return
# 转换图片为numpy数组
img_array = np.array(self.original_image)
OCR识别处理:使用EasyOCR引擎、逐字识别模式(非段落)、实时状态更新。
# 执行OCR识别
self.status_var.set("正在识别文字...")
results = self.reader.readtext(img_array, paragraph=False)
结果处理与显示:识别结果包含文本位置(bbox)、识别内容(text)和置信度(confidence)。
for i, (bbox, text, confidence) in enumerate(results, 1):
if text.strip(): # 只显示非空文本
self.result_tree.insert('', 'end',
text=f"文本 {i}", # 显示序号
values=(text, f"{confidence:.2%}"))
ocr识别完整代码如下(EasyOCR默认会尝试使用GPU来加速OCR识别过程,可修改代码优化为CPU模式 gpu=False # 避免GPU检测警告)。
def process_ocr(self):
"""执行OCR识别并显示结果"""
if self.original_image is None:
self.status_var.set("请先加载图片")
return
# 转换图片为numpy数组
img_array = np.array(self.original_image)
# 清空旧结果
self.result_tree.delete(*self.result_tree.get_children())
try:
# 执行OCR识别
self.status_var.set("正在识别文字...")
self.root.update()
results = self.reader.readtext(img_array, paragraph=False)
if not results:
self.status_var.set("未能识别出任何文字")
return
# 直接显示识别结果
for i, (bbox, text, confidence) in enumerate(results, 1):
if text.strip(): # 只显示非空文本
self.result_tree.insert('', 'end',
text=f"文本 {i}", # 显示序号
values=(text, f"{confidence:.2%}"))
self.status_var.set(f"识别完成,共找到{len(results)}个文本块")
except Exception as e:
print(f"OCR处理错误: {str(e)}")
self.status_var.set(f"识别失败: {str(e)}")
下面实现功能区复制文本、复制全部和导出结果三个按钮功能。
复制选中文本 copy_selected():获取选中项、提取文本内容、合并为字符串,最后复制到剪贴板。
def copy_selected(self):
"""复制选中的文本"""
selected_items = self.result_tree.selection()
texts = []
for item in selected_items:
text = self.result_tree.item(item)['values'][0]
texts.append(text)
# 复制到剪贴板
text = '\n'.join(texts)
self.root.clipboard_clear()
self.root.clipboard_append(text)
复制全部文本 copy_all():
def copy_all(self):
"""复制所有识别文本"""
all_items = self.result_tree.get_children()
texts = [self.result_tree.item(item)['values'][0] for item in all_items]
导出结果 export_results():支持两种格式TXT格式:纯文本和CSV格式:包含置信度。
def export_results(self):
"""导出识别结果到文件"""
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[
("文本文件", "*.txt"),
("CSV文件", "*.csv")
]
)
整个程序完整代码如下:
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from PIL import Image, ImageTk
import easyocr
import numpy as np
import os
class OCRApp:
def __init__(self, root):
self.root = root
self.root.title("智能文字识别系统")
self.root.geometry("1200x800")
# 初始化EasyOCR阅读器(默认中英文)
self.reader = easyocr.Reader(['ch_sim','en'])
# 创建界面组件
self.create_widgets()
self.setup_styles()
def setup_styles(self):
"""配置界面样式"""
style = ttk.Style()
style.configure('TButton', font=('微软雅黑', 10))
style.configure('TLabel', font=('微软雅布', 10))
style.configure('Title.TLabel', font=('微软雅黑', 12, 'bold'))
def create_widgets(self):
"""创建界面组件"""
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧图片显示区域
left_frame = ttk.Frame(main_frame, width=600)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.img_label = ttk.Label(left_frame, text="图片预览区域")
self.img_label.pack(pady=5)
self.canvas = tk.Canvas(left_frame, bg='#f0f0f0', bd=2, relief='groove')
self.canvas.pack(fill=tk.BOTH, expand=True)
ttk.Button(left_frame, text="选择图片", command=self.load_image).pack(pady=10)
# 右侧结果区域
right_frame = ttk.Frame(main_frame, width=600)
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
ttk.Label(right_frame, text="识别结果", style='Title.TLabel').pack(pady=5)
# 修改Treeview配置
self.result_tree = ttk.Treeview(right_frame,
columns=('text', 'confidence'),
show='tree headings', # 显示树形结构和表头
height=30 # 设置显示行数
)
# 配置列
self.result_tree.heading('text', text='识别文本')
self.result_tree.heading('confidence', text='置信度')
self.result_tree.column('text', width=400, anchor='w') # 左对齐
self.result_tree.column('confidence', width=100, anchor='center') # 居中
# 添加滚动条
scrollbar = ttk.Scrollbar(right_frame, orient="vertical",
command=self.result_tree.yview)
self.result_tree.configure(yscrollcommand=scrollbar.set)
# 正确布局
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.result_tree.pack(fill=tk.BOTH, expand=True)
# 在右侧结果区域添加功能按钮
button_frame = ttk.Frame(right_frame)
button_frame.pack(fill=tk.X, pady=5)
ttk.Button(button_frame, text="复制选中",
command=self.copy_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="复制全部",
command=self.copy_all).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="导出结果",
command=self.export_results).pack(side=tk.LEFT, padx=5)
# 底部状态栏
self.status_var = tk.StringVar()
status_bar = ttk.Label(self.root, textvariable=self.status_var, relief='sunken')
status_bar.pack(fill=tk.X, side=tk.BOTTOM)
def load_image(self):
"""加载图片文件"""
path = filedialog.askopenfilename(
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")]
)
if not path:
return
try:
# 显示加载状态
self.status_var.set(f"正在加载图片: {os.path.basename(path)}...")
self.root.update()
# 打开并显示图片
self.original_image = Image.open(path)
self.show_image()
# 开始识别
self.status_var.set("正在识别文字...")
self.root.update()
self.process_ocr()
except Exception as e:
messagebox.showerror("错误", f"无法加载图片: {str(e)}")
self.status_var.set("就绪")
def show_image(self):
"""在Canvas上显示图片"""
# 计算缩放比例
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
img_width, img_height = self.original_image.size
ratio = min(canvas_width/img_width, canvas_height/img_height)
new_size = (int(img_width*ratio), int(img_height*ratio))
# 缩放图片
resized = self.original_image.resize(new_size, Image.LANCZOS)
self.tk_image = ImageTk.PhotoImage(resized)
# 更新Canvas
self.canvas.delete("all")
self.canvas.create_image(
canvas_width//2,
canvas_height//2,
anchor=tk.CENTER,
image=self.tk_image
)
def process_ocr(self):
"""执行OCR识别并显示结果"""
if self.original_image is None:
self.status_var.set("请先加载图片")
return
# 转换图片为numpy数组
img_array = np.array(self.original_image)
# 清空旧结果
self.result_tree.delete(*self.result_tree.get_children())
try:
# 执行OCR识别
self.status_var.set("正在识别文字...")
self.root.update()
results = self.reader.readtext(img_array, paragraph=False)
if not results:
self.status_var.set("未能识别出任何文字")
return
# 直接显示识别结果
for i, (bbox, text, confidence) in enumerate(results, 1):
if text.strip(): # 只显示非空文本
self.result_tree.insert('', 'end',
text=f"文本 {i}", # 显示序号
values=(text, f"{confidence:.2%}"))
self.status_var.set(f"识别完成,共找到{len(results)}个文本块")
except Exception as e:
print(f"OCR处理错误: {str(e)}")
self.status_var.set(f"识别失败: {str(e)}")
def copy_selected(self):
"""复制选中的文本"""
selected_items = self.result_tree.selection()
if not selected_items:
self.status_var.set("请先选择要复制的文本")
return
texts = []
for item in selected_items:
text = self.result_tree.item(item)['values'][0] # 获取文本列的值
texts.append(text)
# 复制到剪贴板
text = '\n'.join(texts)
self.root.clipboard_clear()
self.root.clipboard_append(text)
self.status_var.set(f"已复制 {len(texts)} 条文本到剪贴板")
def copy_all(self):
"""复制所有识别文本"""
all_items = self.result_tree.get_children()
texts = []
for item in all_items:
text = self.result_tree.item(item)['values'][0]
texts.append(text)
if not texts:
self.status_var.set("没有可复制的文本")
return
# 复制到剪贴板
text = '\n'.join(texts)
self.root.clipboard_clear()
self.root.clipboard_append(text)
self.status_var.set(f"已复制全部 {len(texts)} 条文本到剪贴板")
def export_results(self):
"""导出识别结果到文件"""
if not self.result_tree.get_children():
self.status_var.set("没有可导出的内容")
return
# 选择保存路径
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[
("文本文件", "*.txt"),
("CSV文件", "*.csv"),
("所有文件", "*.*")
]
)
if not file_path:
return
try:
with open(file_path, 'w', encoding='utf-8') as f:
# 获取所有结果
all_items = self.result_tree.get_children()
# 根据文件类型选择导出格式
if file_path.endswith('.csv'):
# CSV格式(包含置信度)
import csv
writer = csv.writer(f)
writer.writerow(["文本", "置信度"]) # 写入表头
for item in all_items:
values = self.result_tree.item(item)['values']
writer.writerow(values)
else:
# 纯文本格式
for item in all_items:
text = self.result_tree.item(item)['values'][0]
f.write(text + '\n')
self.status_var.set(f"已导出结果到: {file_path}")
except Exception as e:
messagebox.showerror("导出错误", str(e))
if __name__ == "__main__":
root = tk.Tk()
app = OCRApp(root)
root.mainloop()