基于EasyOCR的图片文字识别系统(Python)

这是一个基于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() 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

席子哥哥的代码库

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值