Closed librix closed 9 months ago
我尝试使用你的代码原文,它运行正常,并没有复现异常。
针对你的问题,我有几点想法:
ocr.ret.kill()
所引起,这只是表象。根本原因是 GetOcrApi()
执行异常,导致 ocr 对象的 self.ret 属性未构造,因此无法调用 self.ret.kill()
。GetOcrApi()
执行异常,大概率是传入的路径(即 ./PaddleOCR-json_v.1.3.0/PaddleOCR-json.exe
) 有问题。这是个相对路径,它的正确性依赖于你python代码的工作目录。可能第一次执行时,工作目录确实是正确的。但是后续操作中,你代码的某些模块修改了当前工作目录,或者受到多线程异步机制的影响,导致第二次执行OCR时相对路径不正确。print("当前工作目录:", os.getcwd())
GetOcrApi
的第一个路径就行了,不用改argument
的config_path
。
ocr = GetOcrApi("绝对路径/PaddleOCR-json.exe", argument)
另外,不建议你每执行一次OCR,就要 GetOcrApi 构造一个ocr对象然后kill掉。因为引擎初始化需要一定时间。
更高效的流程是,程序开始时用 GetOcrApi 构造一个对象,然后之后每次调用都使用这同一个对象。这样可以避免重复初始化。直到要退出程序时,才kill掉该对象。
或者当需要更改参数(模型库路径)时,再删掉旧对象,构造新对象。
是的,您的直觉非常准确。
我回顾了代码,因为糟糕的代码水平,在使用函数修改图片的时候,为了方便保存操作,发现 os.chdir(outerpath)
改变了 os.getcwd()
的值。
os.chdir(outerpath) # 改变 工作路径
#切割文件名: base.son.....
base1 = str(os.path.basename(srcfile)[:-4])
date2 = str(time.strftime("%Y%m%d%H%M%S", time.gmtime()))
Suffix = str(os.path.basename(srcfile).split(".")[-1])
outfile = base1 +"_"+ date2
for id, img_c in enumerate(img_cropped):
# print(len(enumerate(img_cropped)))
outfile_i = outfile + "_" + "{:02d}".format(id) + "." + Suffix
cv2.imwrite(outfile_i, img_c)
print("write pic in ", outfile_i)
print(os.path.basename(srcfile) + " splits 完毕。")
感谢你的慷慨~
关于您的「用 GetOcrApi 构造一个对象」建议,我觉得很有道理,正在修改中~
关于您的「用 GetOcrApi 构造一个对象」建议,我觉得很有道理,正在修改中~
因为程序启动比较慢,所以新建了一个进程:
import tbpu
from PPOCR_api import GetOcrApi
import threading
def OCR_text_Thread(srcfile):
argument = {'config_path': "models/config_chinese.txt"}
ocr = GetOcrApi("./PaddleOCR-json_v.1.3.0/PaddleOCR-json.exe", argument)
res = ocr.run(srcfile)
# ocr.exit()
if res['code'] == 100:
textBlocksNew = tbpu.run_merge_line_h_m_paragraph(res['data'])
s = ""
for i in range(len(textBlocksNew)):
s1 = textBlocksNew[i]['text']
s += s1
print(s)
clipboard.copy(s)
ocr.exit()
return
def OCR_text(srcfile):
print(srcfile)
my_thread = threading.Thread(target=OCR_text_Thread, args=(srcfile,))
my_thread.start()
if __name__ == '__main__':
isOCR = False
while 1:
if not isOCR:
print("当前工作目录:", os.getcwd())
OCR_text(srcfile)
isOCR = True
因为识别过程也很慢,因此识别过程也独立进程,设立两个进程: 一个初始化Thread,一个正常Thread2,Thread2调用Thread1产生的ocr对象:
import tbpu
from PPOCR_api import GetOcrApi
import threading
def OCR_text_Thread1(srcfile):
global ocr
argument = {'config_path': "models/config_chinese.txt"}
ocr = GetOcrApi("./PaddleOCR-json_v.1.3.0/PaddleOCR-json.exe", argument)
res = ocr.run(srcfile)
# ocr.exit()
if res['code'] == 100:
textBlocksNew = tbpu.run_merge_line_h_m_paragraph(res['data'])
s = ""
for i in range(len(textBlocksNew)):
s1 = textBlocksNew[i]['text']
s += s1
print(s)
clipboard.copy(s)
return
def OCR_text_init(srcfile):
my_thread = threading.Thread(target=OCR_text_Thread1, args=(srcfile,))
my_thread.start()
#######################################################
def OCR_text_Thread2(srcfile):
global ocr
res = ocr.run(srcfile)
# ocr.exit()
if res['code'] == 100:
textBlocksNew = tbpu.run_merge_line_h_m_paragraph(res['data'])
s = ""
for i in range(len(textBlocksNew)):
s1 = textBlocksNew[i]['text']
s += s1
print(s)
clipboard.copy(s)
return
def OCR_text(srcfile):
my_thread = threading.Thread(target=OCR_text_Thread2, args=(srcfile,))
my_thread.start()
if __name__ == '__main__':
print_hi('World!')
isOCR = False
isOCRinit = False
while 1:
if not isOCRinit:
print("当前工作目录:", os.getcwd())
OCR_text_init(srcfile)
isOCRinit = True
isOCR = True
if not isOCR:
print("当前工作目录:", os.getcwd())
OCR_text(srcfile)
isOCR = True
cv2.destroyAllWindows() # close the window
global ocr
ocr.exit()
这样似乎速度有所提高。
但是,偶尔,比如切换图片太快,会出现这个问题:
Exception in thread Thread-2 (OCR_text_Thread2):
Traceback (most recent call last):
File "F:\language\miniconda3\lib\threading.py", line 1016, in _bootstrap_inner
self.run()
File "F:\language\miniconda3\lib\threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "E:\MyCode\python\picture-crop\main - v0.3.py", line 458, in OCR_text_Thread2
res = ocr.run(srcfile)
NameError: name 'ocr' is not defined. Did you mean: 'oct'?
另外 ocr.exit()
放在哪里会比较合适呢?
我尝试了一些方法,感觉这样是比较好的结果:
import tbpu
from PPOCR_api import GetOcrApi
import threading
def OCR_text_get_word(srcfile):
global ocr
try:
res = ocr.run(srcfile)
# ocr.exit()
if res['code'] == 100:
textBlocksNew = tbpu.run_merge_line_h_m_paragraph(res['data'])
s = ""
for i in range(len(textBlocksNew)):
s1 = textBlocksNew[i]['text']
s += s1
print(s)
clipboard.copy(s)
except Exception as e:
print(f' OCR_text_get_word 失败: {e}')
def OCR_text_Thread_init(srcfile):
global ocr
argument = {'config_path': "models/config_chinese.txt"}
ocr = GetOcrApi("./PaddleOCR-json_v.1.3.0/PaddleOCR-json.exe", argument)
OCR_text_get_word(srcfile)
return
def OCR_text_init(srcfile):
print(srcfile)
my_thread = threading.Thread(target=OCR_text_Thread_init, args=(srcfile,))
my_thread.start()
def OCR_text(srcfile):
print(srcfile)
my_thread = threading.Thread(target=OCR_text_get_word, args=(srcfile,))
my_thread.start()
if __name__ == '__main__':
print_hi('World!')
isOCR = False
isOCRinit = False
while 1:
if not isOCRinit:
print("当前工作目录:", os.getcwd())
OCR_text_init(srcfile)
isOCRinit = True
isOCR = True
if not isOCR:
print("当前工作目录:", os.getcwd())
OCR_text(srcfile)
isOCR = True
cv2.destroyAllWindows() # close the window
global ocr
if 'ocr' in locals():
ocr.ret.kill()
但是会爆出 OCR_text_get_word 失败: name 'ocr' is not defined
的错误。
而将try
改成判断if 'ocr' in locals():
, OCR 引擎则不会执行。
关于 ocr.exit()
我补充一点,刚刚忘了。这玩意它在一般情况下是不需要手动调用的,因为在程序退出时或者ocr对象析构的时候会自动调用。请阅读文档 这部分 。
然后,关于你的新代码,我提一些建议:
ocr
)应该隐藏在对应的子线程中,没有必要作为全局变量暴露到外界和主线程。以下是一个简单的示例,可以参考一下。
import threading
import os
from PPOCR_api import GetOcrApi
class TaskClass:
def __init__(self):
self.ocr_api = None # ocr对象
self.task_thread = None # 工作线程
self.task_list = [] # 工作队列,存放待处理的path
self.task_lock = threading.RLock() # 工作队列的异步锁
# 调用一次该方法,执行一次OCR
def run(self, path):
# 上锁→加入待处理队列→解锁
self.task_lock.acquire()
self.task_list.append(path)
self.task_lock.release()
# 工作线程已经在运行了,就无需重复建立线程
if self.task_thread:
return
# 未在运行,则建立线程
self.task_thread = threading.Thread(target=self.__task_thread_run)
self.task_thread.start()
# 工作线程内容
def __task_thread_run(self):
# 未初始化引擎,则初始化
if not self.ocr_api:
exe_path = r"./PaddleOCR-json_v.1.3.0/PaddleOCR-json.exe"
self.ocr_api = GetOcrApi(exe_path)
print("OCR初始化完成!")
# 循环,直到任务队列为空
while True:
self.task_lock.acquire()
if len(self.task_list) == 0:
print("所有任务处理完毕!")
self.task_lock.release()
self.task_thread = None
return
# 从任务队列中取一个任务来执行
path = self.task_list.pop(0)
self.task_lock.release()
print(f"当前任务路径:{path}")
res = self.ocr_api.run(path)
print(f"当前任务结果:{res}")
Task = TaskClass() # 模块单例
from task import Task
Task.run("图片1")
Task.run("图片2")
Task.run("图片3")
最大的问题是我还无法理解面向对象~感谢您的思路,我会继续尝试~~
想请问一下进程该如何结束。
我使用如下代码创建子进程:
某图片路径为:
Z:\0722pz\0680_20230727110609_00.jpg
单次执行可成功。但是二次执行会爆出找不到文件的错误,重启重新识别正常:
ocr.ret.kill()
似乎不起作用。该如何结束进程?