|
|
@ -1,6 +1,7 @@ |
|
|
|
from selectAreaWin import SelectAreaWin # 子窗口 |
|
|
|
from asset import iconPngBase64, getHelpText # 资源 |
|
|
|
from asset import IconPngBase64, GetHelpText # 资源 |
|
|
|
from callingOCR import CallingOCR # OCR调用接口 |
|
|
|
from config import Config |
|
|
|
|
|
|
|
import os |
|
|
|
import time |
|
|
@ -9,12 +10,12 @@ import threading # 线程 |
|
|
|
from PIL import Image |
|
|
|
import tkinter as tk |
|
|
|
import tkinter.filedialog |
|
|
|
from tkinter import ttk |
|
|
|
from tkinter import Variable, ttk |
|
|
|
from windnd import hook_dropfiles # 文件拖拽 |
|
|
|
from pyperclip import copy as pyperclipCopy # 剪贴板 |
|
|
|
from webbrowser import open as webOpen # “关于”面板打开项目网址 |
|
|
|
|
|
|
|
ProjectVer = "1.1" # 版本号 |
|
|
|
ProjectVer = "1.2.0" # 版本号 |
|
|
|
ProjectName = f"Umi-OCR 批量图片转文字 v{ProjectVer}" # 名称 |
|
|
|
ProjectWeb = "https://github.com/hiroi-sora/Umi-OCR" |
|
|
|
|
|
|
@ -22,14 +23,15 @@ ProjectWeb = "https://github.com/hiroi-sora/Umi-OCR" |
|
|
|
class Win: |
|
|
|
def __init__(self): |
|
|
|
self.imgDict = {} # 当前载入的图片信息字典,key为表格组件id。必须为有序字典,python3.6以上默认是。 |
|
|
|
self.areaInfo = None # 特殊处理区域数据 |
|
|
|
# self.areaInfo = None # 特殊处理区域数据 |
|
|
|
self.isRunning = 0 # 0未在运行,1正在运行,2正在停止 |
|
|
|
|
|
|
|
def initWin(): # 初始化主窗口 |
|
|
|
# 1.初始化主窗口 |
|
|
|
def initWin(): |
|
|
|
self.win = tk.Tk() |
|
|
|
self.win.title(ProjectName) |
|
|
|
# 窗口大小与位置 |
|
|
|
w, h = 360, 520 # 窗口初始大小与最小大小 |
|
|
|
w, h = 360, 500 # 窗口初始大小与最小大小 |
|
|
|
ws, hs = self.win.winfo_screenwidth(), self.win.winfo_screenheight() |
|
|
|
x, y = round(ws/2 - w/2), round(hs/2 - h/2) # 初始位置,屏幕正中 |
|
|
|
self.win.minsize(w, h) # 最小大小 |
|
|
@ -37,20 +39,57 @@ class Win: |
|
|
|
self.win.protocol("WM_DELETE_WINDOW", self.onClose) # 窗口关闭 |
|
|
|
# 图标 |
|
|
|
self.iconImg = tkinter.PhotoImage( |
|
|
|
data=iconPngBase64) # 载入图标,base64转 |
|
|
|
data=IconPngBase64) # 载入图标,base64转 |
|
|
|
self.win.iconphoto(False, self.iconImg) # 设置窗口图标 |
|
|
|
initWin() |
|
|
|
|
|
|
|
# 2.初始化变量、配置项 |
|
|
|
def initVar(): |
|
|
|
self.cfgVar = { # 设置项tk变量 |
|
|
|
# 输出文件设置 |
|
|
|
"isOutputFile": tk.BooleanVar(), # T时输出内容写入本地文件 |
|
|
|
"outputFilePath": tk.StringVar(), # 输出文件目录 |
|
|
|
"outputFileName": tk.StringVar(), # 输出文件名称 |
|
|
|
# 输出格式设置 |
|
|
|
"isOutputDebug": tk.BooleanVar(), # T时输出调试信息 |
|
|
|
"isIgnoreNoText": tk.BooleanVar(), # T时忽略(不输出)没有文字的图片信息 |
|
|
|
"outputStyle": tk.IntVar(), # 1:纯文本,2:Markdown |
|
|
|
# 识别器设置 |
|
|
|
"ocrToolPath": tk.StringVar(), # 识别器路径 |
|
|
|
"imageSuffix": tk.StringVar(), # 图片后缀 |
|
|
|
} |
|
|
|
Config.initValue(self.cfgVar) # 初始化设置项 |
|
|
|
|
|
|
|
# 面板值改变时,更新到配置值,并写入本地 |
|
|
|
self.saveTimer = None # 计时器,改变面板值一段时间后写入本地 |
|
|
|
|
|
|
|
def configSave(): # 保存值的事件 |
|
|
|
Config.save() |
|
|
|
self.saveTimer = None |
|
|
|
|
|
|
|
def valueChange(key): # 值改变的事件 |
|
|
|
Config.update(key) # 更新配置项 |
|
|
|
if Config.isSaveItem(key): |
|
|
|
if self.saveTimer: # 计时器已存在,则停止已存在的 |
|
|
|
self.win.after_cancel(self.saveTimer) |
|
|
|
self.saveTimer = None |
|
|
|
self.saveTimer = self.win.after(200, configSave) |
|
|
|
for key in self.cfgVar: |
|
|
|
self.cfgVar[key].trace( # 跟踪值改变事件 |
|
|
|
"w", lambda *e, key=key: valueChange(key)) |
|
|
|
initVar() |
|
|
|
|
|
|
|
# 3. 初始化组件 |
|
|
|
def initTop(): # 顶部按钮 |
|
|
|
tk.Frame(self.win, height=5).pack(side='top') |
|
|
|
vFrame1 = tk.Frame(self.win) |
|
|
|
vFrame1.pack(side='top', fill="x", padx=5) |
|
|
|
fr = tk.Frame(self.win) |
|
|
|
fr.pack(side='top', fill="x", padx=5) |
|
|
|
# 右侧按钮 |
|
|
|
self.btnRun = tk.Button( |
|
|
|
vFrame1, text='开始任务', width=12, height=2, command=self.run) |
|
|
|
fr, text='开始任务', width=12, height=2, command=self.run) |
|
|
|
self.btnRun.pack(side='right', padx=5) |
|
|
|
# 左侧文本和进度条 |
|
|
|
vFrame2 = tk.Frame(vFrame1) |
|
|
|
vFrame2 = tk.Frame(fr) |
|
|
|
vFrame2.pack(side='top', fill='x') |
|
|
|
labelUse = tk.Label(vFrame2, text="使用说明", |
|
|
|
fg="gray", cursor="hand2") |
|
|
@ -62,35 +101,33 @@ class Win: |
|
|
|
self.labelFractions.pack(side='right') |
|
|
|
self.labelTime = tk.Label(vFrame2, text="0s") # 已用时间 12.3s |
|
|
|
self.labelTime.pack(side='right', padx=5) |
|
|
|
self.progressbar = ttk.Progressbar(vFrame1) |
|
|
|
self.progressbar = ttk.Progressbar(fr) |
|
|
|
self.progressbar.pack(side='top', padx=5, fill="x") |
|
|
|
initTop() |
|
|
|
|
|
|
|
# 初始化选项卡组件 |
|
|
|
self.notebook = ttk.Notebook(self.win) |
|
|
|
self.notebook = ttk.Notebook(self.win) # 初始化选项卡组件 |
|
|
|
self.notebook.pack(expand=True, fill=tk.BOTH) # 填满父组件 |
|
|
|
|
|
|
|
def initTab1(): # 表格卡 |
|
|
|
tabFrame = tk.Frame(self.notebook) # 选项卡主容器 |
|
|
|
self.notebook.add(tabFrame, text=f'{"处理列表": ^10s}') |
|
|
|
vFrame1 = tk.Frame(tabFrame) |
|
|
|
vFrame1.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Button(vFrame1, text=' 浏览 ', command=self.openFileWin).pack( |
|
|
|
# 顶栏 |
|
|
|
fr1 = tk.Frame(tabFrame) |
|
|
|
fr1.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Button(fr1, text=' 浏览 ', command=self.openFileWin).pack( |
|
|
|
side='left', padx=5) |
|
|
|
tk.Label(vFrame1, text="或直接拖入").pack(side='left') |
|
|
|
tk.Button(vFrame1, text='清空表格', width=12, command=self.clearTable).pack( |
|
|
|
tk.Label(fr1, text="或直接拖入").pack(side='left') |
|
|
|
tk.Button(fr1, text='清空表格', width=12, command=self.clearTable).pack( |
|
|
|
side='right') |
|
|
|
tk.Button(vFrame1, text='移除选中图片', width=12, command=self.delImgList).pack( |
|
|
|
tk.Button(fr1, text='移除选中图片', width=12, command=self.delImgList).pack( |
|
|
|
side='right', padx=5) |
|
|
|
|
|
|
|
vFrame2 = tk.Frame(tabFrame) |
|
|
|
vFrame2.pack(side='top', fill='both') |
|
|
|
|
|
|
|
columns = ['name', 'time', 'score'] |
|
|
|
# 表格主体 |
|
|
|
fr2 = tk.Frame(tabFrame) |
|
|
|
fr2.pack(side='top', fill='both') |
|
|
|
self.table = ttk.Treeview( |
|
|
|
master=vFrame2, # 父容器 |
|
|
|
master=fr2, # 父容器 |
|
|
|
height=50, # 表格显示的行数,height行 |
|
|
|
columns=columns, # 显示的列 |
|
|
|
columns=['name', 'time', 'score'], # 显示的列 |
|
|
|
show='headings', # 隐藏首列 |
|
|
|
) |
|
|
|
hook_dropfiles(self.table, func=self.draggedImages) # 注册文件拖入 |
|
|
@ -101,144 +138,180 @@ class Win: |
|
|
|
self.table.column('name', minwidth=40) |
|
|
|
self.table.column('time', width=20, minwidth=20) |
|
|
|
self.table.column('score', width=30, minwidth=30) |
|
|
|
scroll = tk.Scrollbar( # 绑定滚动条 |
|
|
|
vFrame2, orient='vertical', command=self.table.yview) |
|
|
|
scroll.pack(side="left", fill='y') |
|
|
|
self.table["yscrollcommand"] = scroll.set |
|
|
|
vbar = tk.Scrollbar( # 绑定滚动条 |
|
|
|
fr2, orient='vertical', command=self.table.yview) |
|
|
|
vbar.pack(side="left", fill='y') |
|
|
|
self.table["yscrollcommand"] = vbar.set |
|
|
|
initTab1() |
|
|
|
|
|
|
|
def initTab2(): # 输出卡 |
|
|
|
self.tabFrameOutput = tk.Frame(self.notebook) # 选项卡主容器 |
|
|
|
self.notebook.add(self.tabFrameOutput, text=f'{"识别内容": ^10s}') |
|
|
|
vFrame1 = tk.Frame(self.tabFrameOutput) |
|
|
|
vFrame1.pack(side='top', fill='x', pady=2) |
|
|
|
fr1 = tk.Frame(self.tabFrameOutput) |
|
|
|
fr1.pack(side='top', fill='x', pady=2) |
|
|
|
self.isAutoRoll = tk.IntVar() |
|
|
|
self.isAutoRoll.set(1) |
|
|
|
tk.Checkbutton(vFrame1, variable=self.isAutoRoll, text="自动滚动到底部").pack( |
|
|
|
tk.Checkbutton(fr1, variable=self.isAutoRoll, text="自动滚动到底部").pack( |
|
|
|
side='left') |
|
|
|
tk.Button(vFrame1, text='清空版面', width=12, |
|
|
|
tk.Button(fr1, text='清空版面', width=12, |
|
|
|
command=lambda: self.textOutput.delete('1.0', tk.END)).pack(side='right') |
|
|
|
tk.Button(vFrame1, text='复制到剪贴板', width=12, |
|
|
|
tk.Button(fr1, text='复制到剪贴板', width=12, |
|
|
|
command=lambda: pyperclipCopy(self.textOutput.get("1.0", tk.END))).pack(side='right', padx=5) |
|
|
|
vFrame2 = tk.Frame(self.tabFrameOutput) |
|
|
|
vFrame2.pack(side='top', fill='both') |
|
|
|
scroll = tk.Scrollbar( # 滚动条 |
|
|
|
vFrame2, orient='vertical') |
|
|
|
scroll.pack(side="right", fill='y') |
|
|
|
self.textOutput = tk.Text(vFrame2, height=500, width=500) |
|
|
|
fr2 = tk.Frame(self.tabFrameOutput) |
|
|
|
fr2.pack(side='top', fill='both') |
|
|
|
vbar = tk.Scrollbar(fr2, orient='vertical') # 滚动条 |
|
|
|
vbar.pack(side="right", fill='y') |
|
|
|
self.textOutput = tk.Text(fr2, height=500, width=500) |
|
|
|
self.textOutput.pack(fill='both', side="left") |
|
|
|
scroll["command"] = self.textOutput.yview |
|
|
|
self.textOutput["yscrollcommand"] = scroll.set |
|
|
|
vbar["command"] = self.textOutput.yview |
|
|
|
self.textOutput["yscrollcommand"] = vbar.set |
|
|
|
initTab2() |
|
|
|
|
|
|
|
def initTab3(): # 设置卡 |
|
|
|
tabFrame = tk.Frame(self.notebook) # 选项卡主容器 |
|
|
|
self.notebook.add(tabFrame, text=f'{"设置": ^10s}') |
|
|
|
self.labelOptionTips = tk.Label(tabFrame, fg="red") # 提示 |
|
|
|
self.labelOptionTips.pack() |
|
|
|
# 输出文件设置 |
|
|
|
vFrameOutFile = tk.LabelFrame(tabFrame, text="输出设置") |
|
|
|
vFrameOutFile.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
vFrameO1 = tk.Frame(vFrameOutFile) |
|
|
|
vFrameO1.pack(side='top', fill='x', pady=2) |
|
|
|
self.isOutputFile = tk.IntVar() |
|
|
|
self.isOutputFile.set(1) |
|
|
|
tk.Checkbutton(vFrameO1, variable=self.isOutputFile, text="将识别内容写入txt文件").pack( |
|
|
|
side='left') |
|
|
|
self.isOutputDebug = tk.IntVar() |
|
|
|
self.isOutputDebug.set(0) |
|
|
|
tk.Checkbutton(vFrameO1, variable=self.isOutputDebug, text="输出调试信息").pack( |
|
|
|
side='left', padx=23) |
|
|
|
tk.Label(vFrameOutFile, fg="gray", text="下面两项为空时,默认输出到第一张图片所在的文件夹").pack( |
|
|
|
side='top', padx=5) |
|
|
|
vFrameO2 = tk.Frame(vFrameOutFile) |
|
|
|
vFrameO2.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Label(vFrameO2, text="输出目录: ").pack( |
|
|
|
side='left', padx=5) |
|
|
|
self.enOutPath = tk.Entry(vFrameO2) |
|
|
|
self.enOutPath.pack(side='top', fill="x", padx=5) |
|
|
|
vFrameO3 = tk.Frame(vFrameOutFile) |
|
|
|
vFrameO3.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Label(vFrameO3, text="输出文件名:").pack(side='left', padx=5) |
|
|
|
self.enOutName = tk.Entry(vFrameO3) |
|
|
|
self.enOutName.pack(side='top', fill="x", padx=5) |
|
|
|
# 忽略区域 |
|
|
|
vFrameArea = tk.LabelFrame(tabFrame, text="忽略图片中某些区域内的文字") |
|
|
|
vFrameArea.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
vFrameA1 = tk.Frame(vFrameArea) |
|
|
|
vFrameA1.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Button(vFrameA1, text='添加忽略区域', |
|
|
|
command=self.openSelectArea).pack(side="left", padx=5) |
|
|
|
tk.Button(vFrameA1, text='清空所有区域', |
|
|
|
command=self.clearArea).pack(side="left") |
|
|
|
self.areaLabel = tk.Label(vFrameA1, text="待添加", padx=5) |
|
|
|
self.areaLabel.pack(side="right") |
|
|
|
self.canvasHeight = 140 # 画板高度不变,宽度根据选区回传数据调整 |
|
|
|
self.canvas = tk.Canvas(vFrameArea, width=249, height=self.canvasHeight, |
|
|
|
bg="black") |
|
|
|
self.canvas.pack(side='top') |
|
|
|
# 识别器exe与图片后缀设置 |
|
|
|
vFrameEXE = tk.LabelFrame(tabFrame, text="识别器设置 [切换多国语言和不同格式图片]") |
|
|
|
vFrameEXE.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
vFrame5 = tk.Frame(vFrameEXE) |
|
|
|
vFrame5.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Label(vFrame5, text="识别器路径:").pack( |
|
|
|
side='left', padx=5) |
|
|
|
self.enEXE = tk.Entry(vFrame5) |
|
|
|
self.enEXE.pack(side='top', fill="x", padx=5) |
|
|
|
self.enEXE.insert(0, "PaddleOCR-json\PaddleOCR_json.exe") |
|
|
|
vFrame4 = tk.Frame(vFrameEXE) |
|
|
|
vFrame4.pack(side='top', fill='x', pady=2) |
|
|
|
tk.Label(vFrame4, text="图片后缀: ").pack( |
|
|
|
side='left', padx=5) |
|
|
|
self.enInSuffix = tk.Entry(vFrame4) |
|
|
|
self.enInSuffix.pack(side='top', fill="x", padx=5) |
|
|
|
self.enInSuffix.insert( |
|
|
|
0, ".jpg .jpe .jpeg .jfif .png .webp .bmp .tif .tiff") |
|
|
|
# 关于面板,隐藏在设置选项卡默认大小的下方外面 |
|
|
|
tk.Frame(tabFrame, height=20).pack() |
|
|
|
vFrameAbout = tk.LabelFrame( |
|
|
|
tabFrame, text="关于") |
|
|
|
vFrameAbout.pack(side='top', fill='both', padx=5, ipady=10) |
|
|
|
tk.Label(vFrameAbout, image=self.iconImg).pack() # 图标 |
|
|
|
tk.Label(vFrameAbout, text=ProjectName, fg="gray").pack() |
|
|
|
labelWeb = tk.Label(vFrameAbout, text=ProjectWeb, cursor="hand2", |
|
|
|
fg="deeppink") |
|
|
|
labelWeb.pack() # 文字 |
|
|
|
labelWeb.bind( # 绑定鼠标左键点击,打开网页 |
|
|
|
'<Button-1>', self.openProjectWeb) |
|
|
|
tk.Frame(tabFrame, height=150).pack() |
|
|
|
tk.Label(tabFrame, text="真没有啦!", fg="gray").pack() |
|
|
|
|
|
|
|
def initOptFrame(): # 初始化可滚动画布 及 内嵌框架 |
|
|
|
optVbar = tk.Scrollbar( |
|
|
|
tabFrame, orient="vertical") # 创建滚动条 |
|
|
|
optVbar.pack(side="right", fill="y") |
|
|
|
self.optCanvas = tk.Canvas( |
|
|
|
tabFrame, highlightthickness=0) # 创建画布,用于承载框架。highlightthickness取消高亮边框 |
|
|
|
self.optCanvas.pack(side="left", fill="both", |
|
|
|
expand="yes") # 填满父窗口 |
|
|
|
self.optCanvas["yscrollcommand"] = optVbar.set # 绑定滚动条 |
|
|
|
optVbar["command"] = self.optCanvas.yview |
|
|
|
self.optFrame = tk.Frame(self.optCanvas) # 容纳设置项的框架 |
|
|
|
self.optFrame.pack() |
|
|
|
self.optCanvas.create_window( # 框架塞进画布 |
|
|
|
(0, 0), window=self.optFrame, anchor="nw") |
|
|
|
self.labelOptionTips = tk.Label(self.optFrame, fg="red") |
|
|
|
self.labelOptionTips.pack() |
|
|
|
initOptFrame() |
|
|
|
|
|
|
|
LabelFramePadY = 3 # 每个区域上下间距 |
|
|
|
|
|
|
|
def initArea(): # 忽略区域设置 |
|
|
|
self.areaLabel = tk.LabelFrame( |
|
|
|
self.optFrame, text="忽略图片中某些区域内的文字") |
|
|
|
self.areaLabel.pack(side='top', fill='x', |
|
|
|
ipady=2, pady=LabelFramePadY, padx=4) |
|
|
|
self.areaLabel.grid_columnconfigure(0, minsize=4) |
|
|
|
tk.Button(self.areaLabel, text='添加区域', |
|
|
|
command=self.openSelectArea).grid(column=1, row=0, sticky="w") |
|
|
|
tk.Button(self.areaLabel, text='清空区域', |
|
|
|
command=self.clearArea).grid(column=1, row=1, sticky="w") |
|
|
|
self.areaLabel.grid_rowconfigure(2, minsize=10) |
|
|
|
# tk.Button(self.areaLabel, text='读取预设', |
|
|
|
# command=self.showTest).grid(column=1, row=3, sticky="w") |
|
|
|
# tk.Button(self.areaLabel, text='保存预设', |
|
|
|
# command=self.showTest).grid(column=1, row=4, sticky="w") |
|
|
|
self.areaLabel.grid_columnconfigure(2, minsize=4) |
|
|
|
self.canvasHeight = 140 # 画板高度不变,宽度根据选区回传数据调整 |
|
|
|
self.canvas = tk.Canvas(self.areaLabel, width=249, height=self.canvasHeight, |
|
|
|
bg="black") |
|
|
|
self.canvas.grid(column=3, row=0, rowspan=10) |
|
|
|
initArea() |
|
|
|
|
|
|
|
def initOutFile(): # 输出文件设置 |
|
|
|
frameOutFile = tk.LabelFrame(self.optFrame, text="输出设置") |
|
|
|
frameOutFile.pack(side='top', fill='x', |
|
|
|
ipady=2, pady=LabelFramePadY, padx=4) |
|
|
|
|
|
|
|
fr1 = tk.Frame(frameOutFile) |
|
|
|
fr1.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
tk.Checkbutton(fr1, variable=self.cfgVar["isOutputFile"], |
|
|
|
text="将识别内容写入本地文件").grid(column=0, row=0, columnspan=2, sticky="w") |
|
|
|
tk.Checkbutton(fr1, text="输出调试信息", variable=self.cfgVar["isOutputDebug"] |
|
|
|
).grid(column=0, row=1, sticky="w") |
|
|
|
tk.Checkbutton(fr1, text="忽略无文字的图片", variable=self.cfgVar["isIgnoreNoText"], |
|
|
|
).grid(column=1, row=1, sticky="w") |
|
|
|
tk.Radiobutton(fr1, text='纯文本.txt文件', value=1, variable=self.cfgVar["outputStyle"], |
|
|
|
).grid(column=0, row=2, sticky="w") |
|
|
|
tk.Radiobutton(fr1, text='Markdown风格.md文件', value=2, variable=self.cfgVar["outputStyle"], |
|
|
|
).grid(column=1, row=2, sticky="w") |
|
|
|
tk.Label(fr1, fg="gray", |
|
|
|
text="下面两项为空时,默认输出到第一张图片所在的文件夹" |
|
|
|
).grid(column=0, row=3, columnspan=2, sticky="nsew") |
|
|
|
|
|
|
|
fr2 = tk.Frame(frameOutFile) |
|
|
|
fr2.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
tk.Label(fr2, text="输出目录:").grid(column=0, row=3, sticky="w") |
|
|
|
enOutPath = tk.Entry( |
|
|
|
fr2, textvariable=self.cfgVar["outputFilePath"]) |
|
|
|
enOutPath.grid(column=1, row=3, sticky="nsew") |
|
|
|
fr2.grid_rowconfigure(4, minsize=2) # 第二行拉开间距 |
|
|
|
tk.Label(fr2, text="输出文件名:").grid(column=0, row=5, sticky="w") |
|
|
|
enOutName = tk.Entry( |
|
|
|
fr2, textvariable=self.cfgVar["outputFileName"]) |
|
|
|
enOutName.grid(column=1, row=5, sticky="nsew") |
|
|
|
fr2.grid_columnconfigure(1, weight=1) # 第二列自动扩充 |
|
|
|
initOutFile() |
|
|
|
|
|
|
|
def initOcrUI(): # 识别器exe与图片后缀设置 |
|
|
|
frameOCR = tk.LabelFrame( |
|
|
|
self.optFrame, text="识别器设置 [切换多国语言和不同格式图片]") |
|
|
|
frameOCR.pack(side='top', fill='x', ipady=2, |
|
|
|
pady=LabelFramePadY, padx=4) |
|
|
|
|
|
|
|
fr1 = tk.Frame(frameOCR) |
|
|
|
fr1.pack(side='top', fill='x', pady=2, padx=5) |
|
|
|
|
|
|
|
tk.Label(fr1, text="识别器路径:").grid(column=0, row=0, sticky="w") |
|
|
|
enEXE = tk.Entry(fr1, textvariable=self.cfgVar["ocrToolPath"]) |
|
|
|
enEXE.grid(column=1, row=0, sticky="nsew") |
|
|
|
|
|
|
|
tk.Label(fr1, text="图片后缀:").grid(column=0, row=2, sticky="w") |
|
|
|
enInSuffix = tk.Entry( |
|
|
|
fr1, textvariable=self.cfgVar["imageSuffix"]) |
|
|
|
enInSuffix.grid(column=1, row=2, sticky="nsew") |
|
|
|
fr1.grid_columnconfigure(1, weight=1) |
|
|
|
fr1.grid_rowconfigure(1, minsize=2) |
|
|
|
initOcrUI() |
|
|
|
|
|
|
|
def initAbout(): # 关于面板 |
|
|
|
frameAbout = tk.LabelFrame( |
|
|
|
self.optFrame, text="关于") |
|
|
|
frameAbout.pack(side='top', fill='x', ipady=2, |
|
|
|
pady=LabelFramePadY, padx=4) |
|
|
|
tk.Label(frameAbout, image=self.iconImg).pack() # 图标 |
|
|
|
tk.Label(frameAbout, text=ProjectName, fg="gray").pack() |
|
|
|
labelWeb = tk.Label(frameAbout, text=ProjectWeb, cursor="hand2", |
|
|
|
fg="deeppink") |
|
|
|
labelWeb.pack() # 文字 |
|
|
|
labelWeb.bind( # 绑定鼠标左键点击,打开网页 |
|
|
|
'<Button-1>', self.openProjectWeb) |
|
|
|
initAbout() |
|
|
|
|
|
|
|
def initOptFrameWH(): # 初始化框架的宽高 |
|
|
|
self.optFrame.update() # 强制刷新 |
|
|
|
rH = self.optFrame.winfo_height() # 由组件撑起的 框架高度 |
|
|
|
self.optCanvas.config(scrollregion=(0, 0, 0, rH)) # 画布内高度为框架高度 |
|
|
|
self.optFrame.pack_propagate(False) # 禁用框架自动宽高调整 |
|
|
|
self.optFrame["height"] = rH # 手动还原高度。一次性设置,之后无需再管。 |
|
|
|
self.optCanvasWidth = 1 # 宽度则是随窗口大小而改变。 |
|
|
|
|
|
|
|
def onCanvasResize(event): # 绑定画布大小改变事件 |
|
|
|
cW = event.width-3 # 当前 画布宽度 |
|
|
|
if not cW == self.optCanvasWidth: # 若与上次不同: |
|
|
|
self.optFrame["width"] = cW # 修改设置页 框架宽度 |
|
|
|
self.optCanvasWidth = cW |
|
|
|
self.optCanvas.bind( # 绑定画布大小改变事件。只有画布组件前台显示时才会触发,减少性能占用 |
|
|
|
'<Configure>', onCanvasResize) |
|
|
|
|
|
|
|
def onCanvasMouseWheel(event): # 绑定画布中滚轮滚动事件 |
|
|
|
self.optCanvas.yview_scroll( |
|
|
|
1 if event.delta < 0 else -1, "units") |
|
|
|
self.optCanvas.bind_all("<MouseWheel>", onCanvasMouseWheel) |
|
|
|
initOptFrameWH() |
|
|
|
|
|
|
|
# self.notebook.select(tabFrame) |
|
|
|
|
|
|
|
initTab3() |
|
|
|
|
|
|
|
self.win.mainloop() |
|
|
|
|
|
|
|
def openSelectArea(self): # 打开选择区域 |
|
|
|
if not self.isRunning == 0: |
|
|
|
return |
|
|
|
defaultPath = "" |
|
|
|
if self.imgDict: |
|
|
|
defaultPath = next(iter(self.imgDict.values()))["path"] |
|
|
|
self.win.attributes("-disabled", 1) # 禁用父窗口 |
|
|
|
SelectAreaWin(self.closeSelectArea, defaultPath) |
|
|
|
|
|
|
|
def closeSelectArea(self, info=None): # 关闭选择区域,获取选择区域数据 |
|
|
|
self.win.attributes("-disabled", 0) # 启用父窗口 |
|
|
|
if not info: |
|
|
|
return |
|
|
|
self.areaInfo = info |
|
|
|
self.areaLabel["text"] = f"生效分辨率:{info[0][0]}x{info[0][1]}" |
|
|
|
self.canvas.delete(tk.ALL) # 清除画布 |
|
|
|
scale = self.canvasHeight / info[0][1] # 显示缩放比例 |
|
|
|
width = int(self.canvasHeight * (info[0][0] / info[0][1])) |
|
|
|
self.canvas["width"] = width |
|
|
|
areaColor = ["red", "green", "gold1"] |
|
|
|
for i in range(3): |
|
|
|
for a in info[1][i]: |
|
|
|
x0, y0 = a[0][0]*scale, a[0][1]*scale, |
|
|
|
x1, y1 = a[1][0]*scale, a[1][1]*scale, |
|
|
|
self.canvas.create_rectangle( |
|
|
|
x0, y0, x1, y1, fill=areaColor[i]) # 绘制新图 |
|
|
|
# 加载图片 =============================================== |
|
|
|
|
|
|
|
def draggedImages(self, paths): # 拖入图片 |
|
|
|
if not self.isRunning == 0: |
|
|
@ -251,51 +324,87 @@ class Win: |
|
|
|
def openFileWin(self): # 打开选择文件窗 |
|
|
|
if not self.isRunning == 0: |
|
|
|
return |
|
|
|
suf = self.enInSuffix.get() # 许可后缀 |
|
|
|
suf = Config.get("imageSuffix") # 许可后缀 |
|
|
|
paths = tk.filedialog.askopenfilenames( |
|
|
|
title='选择图片', filetypes=[('图片', suf)]) |
|
|
|
self.addImagesList(paths) |
|
|
|
|
|
|
|
def addImagesList(self, paths): |
|
|
|
suf = self.enInSuffix.get().split() # 许可后缀 |
|
|
|
def addImagesList(self, paths): # 添加一批图片列表 |
|
|
|
suf = Config.get("imageSuffix").split() # 许可后缀列表 |
|
|
|
|
|
|
|
def addImage(path): # 添加一张图片。传入路径,许可后缀。 |
|
|
|
path = path.replace("/", "\\") # 浏览是左斜杠,拖入是右斜杠;需要统一 |
|
|
|
if suf and os.path.splitext(path)[1].lower() not in suf: |
|
|
|
return # 需要判别许可后缀 且 文件后缀不在许可内,不添加。 |
|
|
|
# 检测是否重复 |
|
|
|
for key, value in self.imgDict.items(): |
|
|
|
if value["path"] == path: |
|
|
|
return |
|
|
|
# 检测是否可用 |
|
|
|
try: |
|
|
|
s = Image.open(path).size |
|
|
|
except Exception as e: |
|
|
|
tk.messagebox.showwarning( |
|
|
|
"遇到了一点小问题", f"图片载入失败。图片地址:\n{path}\n\n错误信息:\n{e}") |
|
|
|
return |
|
|
|
# 计算路径 |
|
|
|
p = os.path.abspath(os.path.join(path, os.pardir)) # 父文件夹 |
|
|
|
if not Config.get("outputFilePath"): # 初始化输出路径 |
|
|
|
Config.set("outputFilePath", p) |
|
|
|
if not Config.get("outputFileName"): # 初始化输出文件名 |
|
|
|
n = f"[转文字]_{os.path.basename(p)}" |
|
|
|
Config.set("outputFileName", n) |
|
|
|
# 加入待处理列表 |
|
|
|
name = os.path.basename(path) # 带后缀的文件名 |
|
|
|
tableInfo = (name, "", "") |
|
|
|
id = self.table.insert('', 'end', values=tableInfo) # 添加到表格组件中 |
|
|
|
dictInfo = {"name": name, "path": path, "size": s} |
|
|
|
self.imgDict[id] = (dictInfo) # 添加到字典中 |
|
|
|
|
|
|
|
for path in paths: # 遍历拖入的所有路径 |
|
|
|
if os.path.isdir(path): # 若是目录 |
|
|
|
subFiles = os.listdir(path) # 遍历子文件 |
|
|
|
for s in subFiles: |
|
|
|
self.addImage(path+"\\"+s, suf) # 添加 |
|
|
|
addImage(path+"\\"+s) # 添加 |
|
|
|
elif os.path.isfile(path): # 若是文件: |
|
|
|
self.addImage(path, suf) # 直接添加 |
|
|
|
addImage(path) # 直接添加 |
|
|
|
|
|
|
|
def addImage(self, path, okSuf=None): # 添加一张图片。传入路径,许可后缀。 |
|
|
|
path = path.replace("/", "\\") # 浏览是左斜杠,拖入是右斜杠;需要统一 |
|
|
|
if okSuf and os.path.splitext(path)[1].lower() not in okSuf: |
|
|
|
return # 需要判别许可后缀 且 文件后缀不在许可内,不添加。 |
|
|
|
# 检测是否重复 |
|
|
|
for key, value in self.imgDict.items(): |
|
|
|
if value["path"] == path: |
|
|
|
return |
|
|
|
# 检测是否可用 |
|
|
|
try: |
|
|
|
s = Image.open(path).size |
|
|
|
except Exception as e: |
|
|
|
tk.messagebox.showwarning( |
|
|
|
"遇到了一点小问题", f"图片载入失败。图片地址:\n{path}\n\n错误信息:\n{e}") |
|
|
|
# 忽略区域 =============================================== |
|
|
|
|
|
|
|
def openSelectArea(self): # 打开选择区域 |
|
|
|
if not self.isRunning == 0: |
|
|
|
return |
|
|
|
# 计算路径 |
|
|
|
p = os.path.abspath(os.path.join(path, os.pardir)) # 父文件夹 |
|
|
|
if not self.enOutPath.get(): # 初始化输出路径 |
|
|
|
self.enOutPath.delete('0', tk.END) |
|
|
|
self.enOutPath.insert(0, p) |
|
|
|
if not self.enOutName.get(): # 初始化输出文件名 |
|
|
|
n = f"[转文字]_{os.path.basename(p)}.txt" |
|
|
|
self.enOutName.delete('0', tk.END) |
|
|
|
self.enOutName.insert(0, n) |
|
|
|
# 加入待处理列表 |
|
|
|
name = os.path.basename(path) # 带后缀的文件名 |
|
|
|
tableInfo = (name, "", "") |
|
|
|
id = self.table.insert('', 'end', values=tableInfo) # 添加到表格组件中 |
|
|
|
dictInfo = {"name": name, "path": path, "size": s} |
|
|
|
self.imgDict[id] = (dictInfo) # 添加到字典中 |
|
|
|
defaultPath = "" |
|
|
|
if self.imgDict: |
|
|
|
defaultPath = next(iter(self.imgDict.values()))["path"] |
|
|
|
self.win.attributes("-disabled", 1) # 禁用父窗口 |
|
|
|
SelectAreaWin(self.closeSelectArea, defaultPath) |
|
|
|
|
|
|
|
def closeSelectArea(self): # 关闭选择区域,获取选择区域数据 |
|
|
|
self.win.attributes("-disabled", 0) # 启用父窗口 |
|
|
|
area = Config.get("ignoreArea") |
|
|
|
if not area: |
|
|
|
return |
|
|
|
self.areaLabel["text"] = f"忽略区域 生效分辨率:{area['size'][0]}x{area['size'][1]}" |
|
|
|
self.canvas.delete(tk.ALL) # 清除画布 |
|
|
|
scale = self.canvasHeight / area['size'][1] # 显示缩放比例 |
|
|
|
width = int(self.canvasHeight * (area['size'][0] / area['size'][1])) |
|
|
|
self.canvas["width"] = width |
|
|
|
areaColor = ["red", "green", "gold1"] |
|
|
|
for i in range(3): |
|
|
|
for a in area['area'][i]: |
|
|
|
x0, y0 = a[0][0]*scale, a[0][1]*scale, |
|
|
|
x1, y1 = a[1][0]*scale, a[1][1]*scale, |
|
|
|
self.canvas.create_rectangle( |
|
|
|
x0, y0, x1, y1, fill=areaColor[i]) # 绘制新图 |
|
|
|
|
|
|
|
def clearArea(self): # 清空忽略区域 |
|
|
|
Config.set("ignoreArea", None) |
|
|
|
self.areaLabel["text"] = "忽略图片中某些区域内的文字" |
|
|
|
self.canvas.delete(tk.ALL) # 清除画布 |
|
|
|
self.canvas["width"] = int(self.canvasHeight * (16/9)) |
|
|
|
|
|
|
|
# 表格操作 =============================================== |
|
|
|
|
|
|
|
def clearTable(self): # 清空表格 |
|
|
|
if not self.isRunning == 0: |
|
|
@ -304,19 +413,13 @@ class Win: |
|
|
|
self.labelPercentage["text"] = "0%" |
|
|
|
self.labelFractions["text"] = "0/0" |
|
|
|
self.labelTime["text"] = "0s" |
|
|
|
self.enOutPath.delete('0', tk.END) |
|
|
|
self.enOutName.delete('0', tk.END) |
|
|
|
Config.set("outputFilePath", "") |
|
|
|
Config.set("outputFileName", "") |
|
|
|
self.imgDict = {} |
|
|
|
chi = self.table.get_children() |
|
|
|
for i in chi: |
|
|
|
self.table.delete(i) # 表格组件移除 |
|
|
|
|
|
|
|
def clearArea(self): # 清空忽略区域 |
|
|
|
self.areaInfo = None |
|
|
|
self.areaLabel["text"] = "待添加" |
|
|
|
self.canvas.delete(tk.ALL) # 清除画布 |
|
|
|
self.canvas["width"] = int(self.canvasHeight * (16/9)) |
|
|
|
|
|
|
|
def delImgList(self): # 图片列表中删除选中 |
|
|
|
if not self.isRunning == 0: |
|
|
|
return |
|
|
@ -344,14 +447,16 @@ class Win: |
|
|
|
if not self.imgDict: |
|
|
|
return |
|
|
|
# 检测识别器存在 |
|
|
|
exePath = self.enEXE.get() |
|
|
|
if not os.path.exists(exePath): |
|
|
|
ocrToolPath = Config.get("ocrToolPath") |
|
|
|
if not os.path.exists(ocrToolPath): |
|
|
|
tk.messagebox.showerror( |
|
|
|
'遇到了一点小问题', f'未在以下地址找到识别器!\n{exePath}') |
|
|
|
'遇到了一点小问题', f'未在以下地址找到识别器!\n{ocrToolPath}') |
|
|
|
return |
|
|
|
# 创建输出文件 |
|
|
|
if self.isOutputFile.get() == 1: |
|
|
|
outPath = self.enOutPath.get() + "\\" + self.enOutName.get() |
|
|
|
if Config.get("isOutputFile"): |
|
|
|
suffix = ".txt" if Config.get("outputStyle") == 1 else ".md" |
|
|
|
outPath = Config.get("outputFilePath") + \ |
|
|
|
"\\" + Config.get("outputFileName")+suffix |
|
|
|
try: |
|
|
|
if os.path.exists(outPath): # 文件存在 |
|
|
|
os.remove(outPath) # 删除文件 |
|
|
@ -381,19 +486,48 @@ class Win: |
|
|
|
|
|
|
|
async def run_(self): # 异步,执行任务 |
|
|
|
self.labelPercentage["text"] = "初始化" |
|
|
|
isOutputFile = self.isOutputFile.get() # 是否输出文件 |
|
|
|
isOutputDebug = self.isOutputDebug.get() # 是否输出调试 |
|
|
|
areaInfo = self.areaInfo |
|
|
|
isOutputFile = Config.get("isOutputFile") # 是否输出文件 |
|
|
|
isOutputDebug = Config.get("isOutputDebug") # 是否输出调试 |
|
|
|
isIgnoreNoText = Config.get("isIgnoreNoText") # 是否忽略无字图片 |
|
|
|
outputStyle = Config.get("outputStyle") # 输出风格 |
|
|
|
areaInfo = Config.get("ignoreArea") |
|
|
|
if isOutputFile: |
|
|
|
outPath = self.enOutPath.get() + "\\" + self.enOutName.get() # 输出文件路径 |
|
|
|
suffix = ".txt" if outputStyle == 1 else ".md" |
|
|
|
outPath = Config.get("outputFilePath") + \ |
|
|
|
"\\" + Config.get("outputFileName")+suffix |
|
|
|
|
|
|
|
def output(outStr, type_): # 输出字符串 |
|
|
|
""" |
|
|
|
debug :调试信息 |
|
|
|
text :正文 |
|
|
|
name :文件名 |
|
|
|
none :不做修改 |
|
|
|
""" |
|
|
|
# 写入输出面板,无需格式 |
|
|
|
self.textOutput.insert(tk.END, f"\n{outStr}\n") |
|
|
|
if self.isAutoRoll.get(): # 需要自动滚动 |
|
|
|
self.textOutput.see(tk.END) |
|
|
|
|
|
|
|
def output(str_): # 输出字符串 |
|
|
|
# 写入本地文件,按照格式 |
|
|
|
if isOutputFile: |
|
|
|
if outputStyle == 1: # 纯文本风格 |
|
|
|
if type_ == "debug": |
|
|
|
outStr = f"```\n{outStr}```\n" |
|
|
|
elif type_ == "name": |
|
|
|
outStr = f"\n\n≦ {outStr} ≧\n" |
|
|
|
elif outputStyle == 2: # markdown风格 |
|
|
|
if type_ == "debug": |
|
|
|
outStr = f"```\n{outStr}```\n" |
|
|
|
elif type_ == "text": |
|
|
|
outList = outStr.split("\n") |
|
|
|
outStr = "" |
|
|
|
for i in outList: |
|
|
|
outStr += f"> {i} \n" |
|
|
|
elif type_ == "name": |
|
|
|
path = outStr.replace(" ", "%20") |
|
|
|
outStr = f"---\n\n[{outStr}]({path})\n" |
|
|
|
with open(outPath, "a", encoding='utf-8') as f: # 追加写入本地文件 |
|
|
|
f.write(str_) |
|
|
|
self.textOutput.insert(tk.END, str_) # 1写入输出面板 |
|
|
|
if self.isAutoRoll.get(): # 需要自动滚动 |
|
|
|
self.textOutput.see(tk.END) |
|
|
|
f.write(outStr) |
|
|
|
|
|
|
|
def close(): # 关闭所有异步相关的东西 |
|
|
|
del self.ocr # 关闭OCR进程 |
|
|
@ -401,31 +535,35 @@ class Win: |
|
|
|
self.setRunning(0) |
|
|
|
self.labelPercentage["text"] = "已终止" |
|
|
|
|
|
|
|
def getText(oget, img): # 分析一张图转出的文字 |
|
|
|
def analyzeText(oget, img): # 分析一张图转出的文字 |
|
|
|
def isInBox(aPos0, aPos1, bPos0, bPos1): # 检测框左上、右下角,待测者左上、右下角 |
|
|
|
return bPos0[0] >= aPos0[0] and bPos0[1] >= aPos0[1] and bPos1[0] <= aPos1[0] and bPos1[1] <= aPos1[1] |
|
|
|
|
|
|
|
def isIden(): # 是否识别区域模式 |
|
|
|
if areaInfo[1][1]: # 需要检测 |
|
|
|
if areaInfo["area"][1]: # 需要检测 |
|
|
|
for o in oget: # 遍历每一个文字块 |
|
|
|
for a in areaInfo[1][1]: # 遍历每一个检测块 |
|
|
|
for a in areaInfo["area"][1]: # 遍历每一个检测块 |
|
|
|
if isInBox(a[0], a[1], (o["box"][0], o["box"][1]), (o["box"][4], o["box"][5])): |
|
|
|
return True |
|
|
|
text = "" |
|
|
|
textDebug = "" # 调试信息 |
|
|
|
score = 0 # 平均置信度 |
|
|
|
scoreNum = 0 |
|
|
|
if not areaInfo or not areaInfo[0][0] == img["size"][0] or not areaInfo[0][1] == img["size"][1]: |
|
|
|
|
|
|
|
# 无需忽略区域 |
|
|
|
if not areaInfo or not areaInfo["size"][0] == img["size"][0] or not areaInfo["size"][1] == img["size"][1]: |
|
|
|
|
|
|
|
for i in oget: |
|
|
|
text += i["text"]+"\n" |
|
|
|
score += i["score"] |
|
|
|
scoreNum += 1 |
|
|
|
# 判断,是忽略模式2 |
|
|
|
|
|
|
|
# 忽略模式2 |
|
|
|
elif isIden(): |
|
|
|
fn = 0 # 记录忽略的数量 |
|
|
|
for o in oget: |
|
|
|
flag = True |
|
|
|
for a in areaInfo[1][2]: # 遍历每一个检测块 |
|
|
|
for a in areaInfo["area"][2]: # 遍历每一个检测块 |
|
|
|
if isInBox(a[0], a[1], (o["box"][0], o["box"][1]), (o["box"][4], o["box"][5])): |
|
|
|
flag = False # 踩到任何一个块,GG |
|
|
|
break |
|
|
@ -436,12 +574,14 @@ class Win: |
|
|
|
else: |
|
|
|
fn += 1 |
|
|
|
if isOutputDebug: |
|
|
|
textDebug = f"〔忽略模式2:忽略{fn}条〕\n" |
|
|
|
else: # 否则,忽略模式1 |
|
|
|
textDebug = f"忽略模式2:忽略{fn}条\n" |
|
|
|
|
|
|
|
# 忽略模式1 |
|
|
|
else: |
|
|
|
fn = 0 # 记录忽略的数量 |
|
|
|
for o in oget: |
|
|
|
flag = True |
|
|
|
for a in areaInfo[1][0]: # 遍历每一个检测块 |
|
|
|
for a in areaInfo["area"][0]: # 遍历每一个检测块 |
|
|
|
if isInBox(a[0], a[1], (o["box"][0], o["box"][1]), (o["box"][4], o["box"][5])): |
|
|
|
flag = False # 踩到任何一个块,GG |
|
|
|
break |
|
|
@ -452,30 +592,29 @@ class Win: |
|
|
|
else: |
|
|
|
fn += 1 |
|
|
|
if isOutputDebug: |
|
|
|
textDebug = f"〔忽略模式1:忽略{fn}条〕\n" |
|
|
|
if text and not scoreNum == 0: |
|
|
|
text = textDebug+text |
|
|
|
textDebug = f"忽略模式1:忽略{fn}条\n" |
|
|
|
|
|
|
|
if text and not scoreNum == 0: # 区域内有文本,计算置信度 |
|
|
|
score /= scoreNum |
|
|
|
score = str(score) |
|
|
|
score = str(score) # 转文本 |
|
|
|
else: |
|
|
|
text = textDebug+"所有文字在忽略范围内\n" |
|
|
|
score = "全部忽略" |
|
|
|
return text, score |
|
|
|
score = "1" # 区域内没有文本,置信度为1 |
|
|
|
return text, textDebug, score |
|
|
|
|
|
|
|
# 开始 |
|
|
|
startStr = f"\n任务开始时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}\n" |
|
|
|
startStr = f"任务开始时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}\n" |
|
|
|
output(startStr, "text") |
|
|
|
if isOutputDebug: |
|
|
|
if areaInfo: |
|
|
|
startStr += f"忽略区域:开启\n适用分辨率:{areaInfo[0]}\n" |
|
|
|
startStr += f"忽略区域1:{areaInfo[1][0]}\n" |
|
|
|
startStr += f"识别区域:{areaInfo[1][1]}\n" |
|
|
|
startStr += f"忽略区域2:{areaInfo[1][2]}\n" |
|
|
|
startStr = f'忽略区域:开启\n适用分辨率:{areaInfo["size"]}\n' |
|
|
|
startStr += f'忽略区域1:{areaInfo["area"][0]}\n' |
|
|
|
startStr += f'识别区域:{areaInfo["area"][1]}\n' |
|
|
|
startStr += f'忽略区域2:{areaInfo["area"][2]}\n' |
|
|
|
else: |
|
|
|
startStr += f"忽略区域:关闭\n" |
|
|
|
output(startStr) |
|
|
|
startStr = f"忽略区域:关闭\n" |
|
|
|
output(startStr, "debug") |
|
|
|
# 创建OCR进程 |
|
|
|
exe = self.enEXE.get() |
|
|
|
self.ocr = CallingOCR(exe) |
|
|
|
self.ocr = CallingOCR(Config.get("ocrToolPath")) |
|
|
|
# 初始化UI |
|
|
|
for key in self.imgDict.keys(): # 清空表格参数 |
|
|
|
self.table.set(key, column='time', value="") |
|
|
@ -512,9 +651,11 @@ class Win: |
|
|
|
value=needTimeStr[:4]) # 时间写入表格 |
|
|
|
# 分析数据 |
|
|
|
dataStr = "" |
|
|
|
textDebug = "" |
|
|
|
if oget['code'] == 100: # 成功 |
|
|
|
numOK += 1 |
|
|
|
dataStr, score = getText(oget['data'], value) # 获取文字 |
|
|
|
dataStr, textDebug, score = analyzeText( |
|
|
|
oget['data'], value) # 获取文字 |
|
|
|
elif oget['code'] == 101: # 无文字 |
|
|
|
numNON += 1 |
|
|
|
score = "无文字" |
|
|
@ -523,28 +664,34 @@ class Win: |
|
|
|
dataStr = "识别失败" # 不管开不开输出调试,都要输出报错 |
|
|
|
dataStr += f",错误码:{oget['code']}\n错误信息:{str(oget['data'])}\n" |
|
|
|
score = "失败" |
|
|
|
writeStr = f'\n\n≦ {value["name"]} ≧\n' |
|
|
|
if isOutputDebug: # 输出调试 |
|
|
|
writeStr += f"〔识别耗时:{needTimeStr}s 置信度:{score}〕\n" |
|
|
|
writeStr += dataStr # 输出内容 |
|
|
|
self.table.set(key, column='score', |
|
|
|
value=score[:4]) # 写入表格 |
|
|
|
output(writeStr) |
|
|
|
|
|
|
|
# 写入表格 |
|
|
|
self.table.set(key, column='score', value=score[:4]) |
|
|
|
# 格式化输出 |
|
|
|
if isIgnoreNoText and not dataStr: |
|
|
|
continue # 忽略无字图片 |
|
|
|
output(value["name"], "name") |
|
|
|
if isOutputDebug: |
|
|
|
output( |
|
|
|
f"识别耗时:{needTimeStr}s 置信度:{score}\n{textDebug}", "debug") |
|
|
|
output(dataStr, "text") |
|
|
|
except Exception as e: |
|
|
|
tk.messagebox.showerror( |
|
|
|
'遇到了亿点小问题', f'图片识别异常:\n{value["name"]}\n异常信息:\n{e}') |
|
|
|
# print(f'出问题了:{value["name"]}\n{e}') |
|
|
|
# 结束 |
|
|
|
endTime = time.time() |
|
|
|
endStr = f"\n\n\n任务结束时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(endTime))}\n" |
|
|
|
output("\n\n---\n", "none") |
|
|
|
endStr = f"任务结束时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(endTime))}\n" |
|
|
|
output(endStr, "text") |
|
|
|
if isOutputDebug: |
|
|
|
endStr += f"任务耗时(秒): {endTime-startTime}\n" |
|
|
|
endStr += f"单张平均耗时: {(endTime-startTime)/allNum}\n" |
|
|
|
endStr = f"任务耗时(秒): {endTime-startTime}\n" |
|
|
|
if not allNum == 0: |
|
|
|
endStr += f"单张平均耗时: {(endTime-startTime)/allNum}\n" |
|
|
|
endStr += f"共计图片数量: {numOK+numNON+numERR}\n" |
|
|
|
endStr += f"识别正常 的图片数量: {numOK}\n" |
|
|
|
endStr += f"未识别到文字 的图片数量:{numNON}\n" |
|
|
|
endStr += f"识别失败 的图片数量: {numERR}\n\n" |
|
|
|
output(endStr) |
|
|
|
endStr += f"识别失败 的图片数量: {numERR}\n" |
|
|
|
output(endStr, "debug") |
|
|
|
close() # 完成后关闭 |
|
|
|
self.labelPercentage["text"] = "完成!" |
|
|
|
|
|
|
@ -559,7 +706,7 @@ class Win: |
|
|
|
if not tkinter.messagebox.askokcancel('提示', '将清空输出面板。要继续吗?'): |
|
|
|
return |
|
|
|
self.textOutput.delete('1.0', tk.END) |
|
|
|
self.textOutput.insert(tk.END, getHelpText(ProjectWeb)) |
|
|
|
self.textOutput.insert(tk.END, GetHelpText(ProjectWeb)) |
|
|
|
|
|
|
|
def openProjectWeb(self, e=None): # 打开项目网页 |
|
|
|
webOpen(ProjectWeb) |
|
|
@ -579,6 +726,10 @@ class Win: |
|
|
|
else: |
|
|
|
self.win.after(50, self.waitClose) # 等待关闭,50ms轮询一次是否已结束子进程 |
|
|
|
|
|
|
|
# def showTest(self): |
|
|
|
# tk.messagebox.showwarning( |
|
|
|
# "警告", "此功能尚未完工。") |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
Win() |
|
|
|