虽然在前面在第二次升级时就已经通过json配置文件支持将ocr识别结果txt保存到指定的文件夹里了,但由于指定待识别文件夹时文件夹里面可能包含多个不同的子文件夹、不同的子文件夹里面可能包含同名的图像文件,而原来的方式是直接把所有的txt全部放在json文件指定的一个文件夹中,当不同文件夹中存在同名的图像文件时,会存在txt文件覆盖的情况,虽然几率很小但是开发小伙伴们反映确实出现了这种情况,那么就需要改进,最好是改进为下面的方式:在指定的文件夹下面按照原图像文件的目录结构新建相同的文件夹结构并存放txt文件,也即在json指定的txt保存路径下重新按照待识别图像文件夹的结构,完全重新新建相同的文件夹结果,这样就可以完全避免由于大文件夹中的文件重名而带来的识别结果txt文件覆盖从而丢失的情况发生了,升级后的代码如下所示:
#!/home/super/miniconda3/bin/python#encoding=utf-8#author: superchao1982, 50903556@#帮助信息strhelp='''img2txt is one program to get ocr texts from image or pdf files!default threshold is 0.1;default langpath is '/home/langdata' for linux and 'C:\ocr\langdata' for win;default remove char is '| _^~`&';default path storing the ocr texts are the same directory with images;default settings above can be changed in the file 'config.json' which stored in langpath;contents in config.json like:{"threshold": 0.1,"batchsize": 2,"workernum": 4,"maximgsize": 1000,"allowlist": "","langpath": "/home/langdata","removechar": " _^~`&""txtpath": ""}------------------------------------e.g../img2txt.py img1.jpg jmg2.jpg #follow by one or more image files./img2txt.py ./img1 ./img home/usr/Document/img #follow by one or more directory contain image files./img2txt.py --help #output the help info./img2txt.py --config #generate the default config.json file in the langpath------------------------------------'''import sysimport jsonimport osimport pdf2imageimport numpy as np#------------------默认参数设置----------------------threshold=0.1 #(default = 0.1)阈值batchsize=2 # (default = 1) - batch_size>1 will make EasyOCR faster but use more memoryworkernum=4 # (default = 0) - Number thread used in of dataloadermaximgsize=1000 #(default = 1000) - Max image width & height when using pdfallowlist='' # (string) - Force EasyOCR to recognize only subset of charactersremovechar='| _^~`&'#待删除无效字符txtpath='' #ocr识别后同名txt文件存放的位置:空表示同一目录,点表示相对目录,其他表示绝对目录#根据系统设置默认的语言包路径if sys.platform.lower().startswith('linux'):langpath='/home/langdata'elif sys.platform.lower().startswith('win'):langpath='C:\ocr\langdata'else:print('Error: Unknow System!')sys.exit()#配置参数字典config={"threshold": threshold,"batchsize": batchsize,"workernum": workernum,"maximgsize": maximgsize,"allowlist": allowlist,"langpath": langpath,"removechar": removechar,"txtpath": txtpath}#------------------命令行参数处理----------------------#首先对输入的命令行参数进行处理,在加载ocr包之前排查的好处是避免临处理时出错白白浪费时间for i in range(1,len(sys.argv)):#获取命令行参数:argv[0]表示可执行文件本身if sys.argv[i] in ['-h', '--help']:print(strhelp)sys.exit()elif sys.argv[i] in ['-c', '--config']:#保存字典到文件try:with open(os.path.join(langpath,'config.json'), 'w') as jsonfile:json.dump(config, jsonfile, ensure_ascii=False,indent=4)print('Genrerating config.json success! ---> ', os.path.join(langpath,'config.json'))except(Exception) as e:print('\tSaving config file config.json Error: ', e)#输出异常错误sys.exit()else:#check the image file or directory is valid-提前校验,免得浪费时间加载easyocr模型if not os.path.exists(sys.argv[i]):print(sys.argv[i], ' is invalid, please input the correct file or directory path!')sys.exit()#检查语言包路径是否正确check the langpath is validif not os.path.exists(langpath):print('Error: Invalid langpath! Checking the path again!')sys.exit()#判断是否存在配置文件config.json,存在就使用,格式如下:configfile=os.path.join(langpath,'config.json')if os.path.exists(configfile):try:with open(configfile, 'r') as jsonfile:configdict=json.load(jsonfile)threshold=configdict['threshold']batchsize=configdict['batchsize']workernum=configdict['workernum']maximgsize=configdict['maximgsize']langpath=configdict['langpath']allowlist=configdict['allowlist']removechar=configdict['removechar']txtpath=configdict['txtpath']print('using the config in ', configfile)except(Exception) as e:print('\tReading config file ', configfile ,' Error: ', e)#输出异常错误print('\tCheck the json file, or remove the config.json file to use defaulting configs!')sys.exit()else:print('\tusing the default config in ', langpath)print(configdict)#如果用户在config.json中指定的txt文件保存路径不存在就生成一个if len(txtpath)>0 and not os.path.exists(txtpath):print('txtpath in config.json is not exists, generating ', txtpath, '!\n')try:os.system('mkdir '+txtpath)except(Exception) as e:print('\tMaking txt directory Error: ', e)#输出异常错误print('\tPlease input a legal txtpath in the config.json file and try again!\n')sys.exit()#------------------开始OCR识别----------------------import easyocrocrreader=easyocr.Reader(['ch_sim', 'en'], model_storage_directory=langpath)#Linux: r'/home/langdata', Windows: r'C:\ocr\langdata'for ind in range(1,len(sys.argv)):#获取命令行参数:argv[0]表示可执行文件本身argpath=sys.argv[ind]#如果是文件...if os.path.isfile(argpath):paper=''#获取文件后缀名filext=os.path.splitext(argpath)[-1]if filext.upper() not in ['.JPG','.JPEG','.PNG','.BMP','.PDF']:#转换为大写后再比对print('\t', argpath, ' 不是有效图片格式(jpg/jpeg/png/bmp/pdf)!')continueif filext.upper() in['.PDF']:#如果是pdf文档images=pdf2image.convert_from_path(argpath)#将pdf文档转换为图像序列for i in range(len(images)):#如果图片尺寸过大,缩小到特定尺寸,避免内存崩溃ratio=max(images[i].width, images[i].height)/maximgsizeif ratio>1:images[i]=images[i].resize((round(images[i].width/ratio),round(images[i].height/ratio)))result = ocrreader.readtext(np.asarray(images[i]),batch_size=batchsize,workers=workernum)for w in result:if w[2]>threshold:#设置一定的置信度阈值paper = paper+w[1]else:result = ocrreader.readtext(argpath,batch_size=batchsize,workers=workernum)for w in result:if w[2]>threshold:#设置一定的置信度阈值paper = paper+w[1]#print(paper)for item in removechar:paper=paper.replace(item, '')paper=paper.replace('\r', '')paper=paper.replace('\n', '')#记录当前文件的识别结果,保存为同名的txt文件if(len(txtpath)>0):#如果设置了txt文件目录basename=os.path.basename(argpath)+'.txt'#与原文件同名的txt文件(不含目录仅文件名)txtfilename=os.path.join(txtpath, basename)else:txtfilename=os.path.splitext(argpath)[0]+'.txt'#与原文件同名的txt文件(包括目录)print('saving file ---> ', txtfilename)#保存的文件名字try:with open(txtfilename, 'w') as txtfile:txtfile.write(paper)except(Exception) as e:print('\t', txtfilename, ' Saving txt File Error: ', e)#输出异常错误continue#如果是文件夹...if os.path.isdir(argpath):for root, _, filenames in os.walk(argpath):for imgfile in filenames:paper=''filext=os.path.splitext(imgfile)[-1]#文件后缀名if filext.upper() not in ['.JPG','.JPEG','.PNG','.BMP','.PDF']:print('\t', imgfile, '的后缀名不是有效的图像格式,跳过该文件!')continueimgfilepath=os.path.join(root, imgfile)#文件绝对路径if filext.upper() in['.PDF']:#如果是pdf文档images=pdf2image.convert_from_path(imgfilepath)#将pdf文档转换为图像序列for i in range(len(images)):#如果图片尺寸过大,缩小到特定尺寸,避免内存崩溃ratio=max(images[i].width, images[i].height)/maximgsizeif ratio>1:images[i]=images[i].resize((round(images[i].width/ratio),round(images[i].height/ratio)))result = ocrreader.readtext(np.asarray(images[i]),batch_size=batchsize,workers=workernum)for w in result:if w[2]>threshold:#设置一定的置信度阈值paper = paper+w[1]else:result = ocrreader.readtext(imgfilepath,batch_size=batchsize,workers=workernum)for w in result:if w[2]>threshold:#设置一定的置信度阈值paper = paper+w[1]#print(paper)for item in removechar:paper=paper.replace(item, '')paper=paper.replace('\r', '')paper=paper.replace('\n', '')#记录当前文件的识别结果,保存为同名的txt文件basename=os.path.splitext(imgfile)[0]+'.txt'#与原文件同名的txt文件(不包括目录)if(len(txtpath)>0):#如果设置了txt文件目录#原来的方式是直接把所有的txt全部放在指定的一个文件夹中,当不同文件夹中存在同名的图像文件时,会存在txt文件覆盖的情况#txtfilename=os.path.join(txtpath, basename)#拼接得到txt文件的绝对路径#下面的方式是在指定的文件夹下面按照原图像文件的目录结构新建相同的文件夹结构并存放txt文件relativeimgpath=imgfilepath.lstrip(argpath)#图片绝对路径左减去命令行指定的路径argpath得到图像文件的内部相对路径newtxtpath=os.path.join(txtpath,relativeimgpath)#指定txt文件路径+图像内部相对路径(还带有图像文件名和后缀名)basedir=os.path.dirname(newtxtpath)#去掉图像文件名和后缀名if not os.path.exists(basedir):#上面的新文件路径不一定存在try:os.system('mkdir '+basedir)#新建文件夹except(Exception) as e:print('\tMaking txt directory Error: ', e)#输出异常错误print('\tTxt file will be storded in the image file directory!')txtfilename=os.path.join(root, basename)#路径+txt文件名txtfilename=os.path.join(basedir, basename)#新路径+txt文件名else:txtfilename=os.path.join(root, basename)#路径+txt文件名print('saving file ---> ', txtfilename)#保存的文件名字try:with open(txtfilename, 'w') as txtfile:txtfile.write(paper)except(Exception) as e:print('\t', txtfilename, ' Saving txt File Error: ', e)#输出异常错误continue
如果觉得《基于Python的离线OCR图片文字识别(四)——支持txt文件指定路径保存》对你有帮助,请点赞、收藏,并留下你的观点哦!