吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 797|回复: 25
上一主题 下一主题
收起左侧

[Python 原创] 紧跟潮流 也写个 OCR 图像识别 (调用本地 http )

  [复制链接]
跳转到指定楼层
楼主
pyjiujiu 发表于 2024-11-16 10:12 回帖奖励
本帖最后由 pyjiujiu 于 2024-11-17 18:14 编辑

更新修复一个小bug,详细在文章末尾

**编写说明:
1 调用的是 Umi-OCR 的http 服务,所以需要先安装这个软件(开源的)


老实说 软件本身已经可以支持OCR的操作,为何多此一举,理由如下
* http 服务可以面向局域网,调用接口通过路由器,还是有需求的
* 原软件比较花哨,看着晕,操作也不够友好(我的脚本 更简洁,适合一般任务)



           

2 脚本说明
* 支持 「打开图片」,「拖入图片」,「剪贴板粘贴」(ctrl+V 即可)三种输入方式
其中 打开图片 需要手动按识别按钮"operate"外,剪贴板和拖入 会自动识别


* 默认仅显示三个常用设置,点击「更多选项」还有三个选项
其中“忽略区域” 不能真正修改,待以后规划,“数据格式” 是http 返回的信息多与少,界面上没影响


* 需要安装额外的包
tkinterdnd2  #为了支持拖入图片
PIL                # 为了支持剪贴板拾图(python3 -m pip install --upgrade Pillow


---分割线---


*umi 原软件除后台运行外,还需确保 http服务打开 (默认是打开的)





---分割线---
**下面是代码


[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import filedialog, messagebox,scrolledtext
from tkinter import ttk
from tkinterdnd2 import TkinterDnD, DND_FILES

import json
import base64
import pathlib
import requests

import io
from PIL import ImageGrab  

import ctypes 
ctypes.windll.shcore.SetProcessDpiAwareness(2)

url_base = 'http://127.0.0.1:1224/'
url_options = url_base + 'api/ocr/get_options'
url_ocr = url_base + "api/ocr"

def test_http(url_base):
    '''test the server'''
    try:
        response = requests.get(url=url_base)
        response.raise_for_status()
    except Exception as e:
        messagebox.showwarning("warning","服务未连通,请确保软件或服务端已经打开")

def img_base64(img_file):
    if isinstance(img_file,io.BytesIO):
        img_bytes = img_file.getvalue()
    else:
        img_bytes = pathlib.Path(img_file).read_bytes()
    return base64.b64encode(img_bytes).decode('utf-8')

def to_ocr(url,options:dict,img:str | io.BytesIO):
    data = {
    "base64":img_base64(img), 
    "options": options
    }
    headers = {"Content-Type": "application/json"} 
    data_str = json.dumps(data)
    response = requests.post(url,data=data_str, headers=headers)  #也可用 json 参数
    response.raise_for_status()
    res_dict = json.loads(response.text)
    return res_dict  

class OptTranslator:
    '''en --> zh'''
    __slots__=('ocr_language','tbpu_parser','opt_name')
    def __init__(self) -> None:
        self.opt_name:dict = {"ocr.language":"语言:","ocr.cls":"对正文字:","ocr.limit_side_len":"限制边长:","tbpu.parser":"排版:","tbpu.ignoreArea":"忽略区域:","data.format":"数据格式:",}
        self.ocr_language:dict={"简体中文":"models/config_chinese.txt",
                        "English":"models/config_en.txt",
                            "繁體中文":"models/config_chinese_cht(v2).txt",
                            "日本語":"models/config_japan.txt",
                            "한국어":"models/config_korean.txt",
                            "Русский":"models/config_cyrillic.txt"}
        self.tbpu_parser:dict = {"单栏-按自然段换行":"single_para",
                            "单栏-总是换行":"single_line",
                            "多栏-按自然段换行":"multi_para",
                            "多栏-总是换行":"multi_line",
                            "多栏-无换行":"multi_none",
                            "单栏-按自然段换行":"single_para",
                            "单栏-总是换行":"single_line",
                            "单栏-无换行":"single_none",
                            "单栏-保留缩进":"single_code",
                            "不做处理":"none"}

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("OCR")
        self.root.geometry("520x450")
        self.file_path_label = tk.Label(root, text="File Path:")
        self.file_path_label.grid(row=0, column=0, sticky="w", padx=10, pady=10)
        self.file_path_entry = tk.Entry(root, width=50)
        self.file_path_entry.grid(row=0, column=1, padx=10, pady=10)
        self.open_button = tk.Button(root, text="Open", command=self.open_file)
        self.open_button.grid(row=0, column=2, padx=10, pady=10)
        self.root.drop_target_register(DND_FILES)
        self.root.dnd_bind('<<Drop>>', self.on_file_drop)
        self.clipboard = None
        self.root.bind("<Control-v>", self.on_paste)
        self.arg_vars = [tk.StringVar() for _ in range(6)]
        self.arg_comboboxes = []  
        self.arg_comboboxes_label = []
        self.OptTranslator = OptTranslator()
        self.option_list = [{'opt_name':'ocr.language','value':[ ("models/config_chinese.txt","简体中文"),
                                                                ("models/config_en.txt","English"),
                                                                ("models/config_chinese_cht(v2).txt","繁體中文"),
                                                                ("models/config_japan.txt","日本語"),
                                                                ("models/config_korean.txt","&#54620;&#44397;&#50612;"),
                                                                ("models/config_cyrillic.txt","Русский")]},
                            {'opt_name':'ocr.limit_side_len','value':("960", "2880", "4320", "999999")},
                            {'opt_name':'tbpu.parser','value':[
                                                                ("single_para","单栏-按自然段换行"),
                                                                ("single_line","单栏-总是换行"),
                                                                ("multi_para","多栏-按自然段换行"),
                                                                ("multi_line","多栏-总是换行"),
                                                                ("multi_none","多栏-无换行"),
                                                                ("single_para","单栏-按自然段换行"),
                                                                ("single_line","单栏-总是换行"),
                                                                ("single_none","单栏-无换行"),
                                                                ("single_code","单栏-保留缩进"),
                                                                ("none","不做处理")]},
                            {'opt_name':'ocr.cls','value':("false", "true")},
                            {'opt_name':'tbpu.ignoreArea','value':[""]},  #修复原始版本 "[]" -> "" ,旧版不会报错,新版2.1.4会报错
                            {'opt_name':'data.format','value':("text","dict")}]
        for i in range(6):
            arg_label = tk.Label(root, text=self.OptTranslator.opt_name[self.option_list[i]['opt_name']])
            arg_combobox = ttk.Combobox(root, textvariable=self.arg_vars[i], values=self.option_list[i]['value'], state="readonly", width=20)
            if i < 3:
                arg_label.grid(row=i+1, column=0, sticky="w", padx=10, pady=2)
                arg_combobox.grid(row=i+1, column=1, padx=10, pady=2)
            _value_dict = self.option_list[i]
            if  _value_dict['opt_name'] =='tbpu.parser' or _value_dict['opt_name']== 'ocr.language':
                arg_combobox['value'] = [item[1] for item in _value_dict['value']]
            else:
                arg_combobox['value'] = _value_dict['value']
            arg_combobox.current(0)
            self.arg_comboboxes.append(arg_combobox)
            self.arg_comboboxes_label.append(arg_label)
            
        #operate 按钮(识别)
        self.operate_button = tk.Button(root, text="Operate", command=self.operate)
        self.operate_button.grid(row=7, column=1, pady=20)
        #更多选项 按钮
        self.hide_toggle = tk.Button(root, text="更多选项", command=self.visible_toggle)
        self.hide_toggle.grid(row=7, column=0, pady=20)
        self.result_text =scrolledtext.ScrolledText(root, width=70, height=14,wrap=tk.WORD)
        self.result_text.grid(row=8, column=0, columnspan=3, padx=10, pady=10)
        #测试 http 是否开启
        test_http(url_base)
        
    def visible_toggle(self):
        for i,(label,combo) in enumerate(zip(self.arg_comboboxes_label[3:],self.arg_comboboxes[3:])):
            if label.grid_info() or combo.grid_info() :  
                label.grid_forget()
                combo.grid_forget()
            else:
                label.grid(row=i+1+3, column=0, sticky="w", padx=10, pady=2)
                combo.grid(row=i+1+3, column=1, padx=10, pady=2)
    def open_file(self):
        """Open file dialog to select a file."""
        file_path = filedialog.askopenfilename()
        if file_path:
            self.file_path_entry.delete(0, tk.END)
            self.file_path_entry.insert(0, file_path)
    def on_file_drop(self, event):
        """Handles the drag-and-drop event."""
        file_path = event.data.strip('{}')
        self.file_path_entry.delete(0, tk.END)
        self.file_path_entry.insert(0, file_path)
        if pathlib.Path(file_path).is_file():
            try:
                self.operate()
            except Exception as e:
                print(e)
                
    def on_paste(self,event):
        try:
            image = ImageGrab.grabclipboard()
            if image:
                img_data =  io.BytesIO()
                image.convert('RGB').save(img_data,'PNG')
                self.clipboard = img_data
                self.operate(clipboard=True)
                self.clipboard = None
            else:
                messagebox.showinfo("Clipboard", "No image found in clipboard.")
        except Exception as e:
            messagebox.showerror("Error", f"An error occurred: {e}")
            
    def operate(self,clipboard=False):
        """Run the operation and display results."""
        file_path = self.file_path_entry.get()
        if clipboard:
            file_path = self.clipboard
        args = [var.get() for var in self.arg_vars]
        options = {}
        for index,box in enumerate(self.arg_comboboxes):
            key = self.option_list[index]['opt_name']
            value = box.get()
            if key == 'ocr.language' or key == 'tbpu.parser':
                value = getattr(self.OptTranslator,key.replace('.','_'))[value]
            options[key] = value
        result = to_ocr(url_ocr,options=options,img=file_path)
        self.result_text.delete(1.0, tk.END)  
        self.result_text.insert(tk.END, result['data'])
if __name__=="__main__":
    root = TkinterDnD.Tk()
    app = App(root)
    root.mainloop()



脚本附加说明:
因为图片识别 时长比较短,故不再加入多线程(tkinter 本身是单线程的),用起来会有一点点运行的时间感受,这是正常的


---分割线---
最后,个人典型的使用场景是,截图,然后打开脚本界面,ctrl+v,结果就出来了

希望对大家有帮助,欢迎交流




---版本迭代 记录---
*11-17 修复参数传递值,代码第108行,为{'opt_name':'tbpu.ignoreArea','value':[""]} (旧版Umi软件不报错,但最新版2.1.4会)

测试3.JPG (107.21 KB, 下载次数: 2)

测试3.JPG

免费评分

参与人数 6吾爱币 +12 热心值 +5 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
feilon5555 + 1 + 1 用心讨论,共获提升!
ZzcZackery + 1 谢谢@Thanks!
愷龍 + 2 + 1 谢谢@Thanks!
guihai3 + 1 用心讨论,共获提升!
superworker2022 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
小小/ng 发表于 2024-11-16 14:18
可以搞个油猴脚本,自动识别验证码
推荐
 楼主| pyjiujiu 发表于 2024-11-18 21:49 |楼主
feilon5555 发表于 2024-11-16 12:12
怎么识别生成表格啊

看我最新回复,GOT-OCR 模型可以解决表格生成 的问题

但是成熟的脚本,要等一等,还在消化中
推荐
forever96 发表于 2024-11-16 12:57
推荐
dinosaur18 发表于 2024-11-16 10:37
ocr是练手神器
沙发
superworker2022 发表于 2024-11-16 10:34
下载学习,也复现一下~
4#
qiu520 发表于 2024-11-16 10:41
太厉害了吧,佩服
5#
z297171662 发表于 2024-11-16 10:53
这个现在的确很多。。。
6#
Nuaza 发表于 2024-11-16 11:30
这个确实有用,感谢楼主分享
7#
LuoBoYIng 发表于 2024-11-16 11:43
感谢大佬分享
8#
feilon5555 发表于 2024-11-16 12:12
怎么识别生成表格啊
9#
shi147517631 发表于 2024-11-16 12:54
这个确实有用,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 17:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表