很烦截图诶,老是让我去查谁没交。名字在图片上,吸取上次的经验,我把“查查谁没交截图”做成了全自动😅

顺便把剪切图片这种烦人的工作也加了上去😑


大概的思路就是将所有图片放在一个文件夹下,对这个文件夹下所有图片进行一个处理。

先进行OCR文字识别,把名字识别出来,然后用花名册对它进行遍历查找。

再顺便写了个批量剪切图片,就可以把证件照啥的一下全剪成200x200啥的。多是一件美事。


OCR识别

对于OCR识别,大致有两种方式。一是在线识别操作,二是本地离线模型包,离线识别。

免费在线识别有很多,随便一搜就有很多。但是无一例外,都是一张一张上传识别,不符合我们设计程序的思路。

大厂的OCR技术倒是有API接口,百度,腾讯,阿里……但无一例外,免费次数有限,而且我们操作的某些图片不太适合进行上传。

所以在这里,我选择了开源的PaddleOCR项目来作为轮子

同样,开源OCR项目也有很多。但Paddle项目的文档很全,且包很小,轻量CPU仅9MB。

Github地址:https://github.com/PaddlePaddle/PaddleOCR

Gitee地址:https://gitee.com/paddlepaddle/PaddleOCR

官网地址:https://www.paddlepaddle.org.cn/

🍕在python中安装PaddleOCR

从百度源下载cpu版本paddle库

1
python -m pip install paddlepaddle==2.2.0 -i https://mirror.baidu.com/pypi/simpl

安装shapely

https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely去找到shapelywhl包进行安装

在这里需要注意,你该如何选择对应的whl包?

执行命令:

1
pip debug --verbose

查看当前你支持的whl包,去下载对应的包

下载后将whl包放在工程目录下(其他地方cd过去也行),执行:

1
pip install xxxxxxxx.whl

如果遇到错误,很可能是不支持的whl包。你下载了错误的whl包

安装其他依赖

在项目下新建requirements.txt文件,里面填写官方文档内的内容

然后在该目录下执行:

1
pip3 install -r requirements.txt

最后安装PaddleOCR

执行命令:

1
pip install "paddleocr>=2.2.0"

好了,不出意外的话你已经安装好了所有的拓展包.

不出意外的话就要出意外

以下是我遇到的部分问题

报错: error: Microsoft Visual C++ 14.0 is required.

安装Levenshtein

同样进入https://www.lfd.uci.edu/~gohlke/pythonlibs/#python-levenshtein

找到对应的whl包下载,pip安装它。

这里有详细文档

报错: ‘NoneType’ object has no attribute ‘array_interface

极有可能是因为你的文件路径包含了中文,python的安装路径也是一样,不能包含中文

在期间我还遇到了很多奇奇怪怪的错误,大部分都可以在百度-必应联合大学下找到答案。

部分问题文档

自己动手,丰衣足食

下载cpu解析模型包

如果不需要大型模型包,可以忽略本步骤。paddleocr可以自行检测是否有模型,如果没有就下载轻量级模型包。

🍕python程序运行

可以参照此文档检验自己的配置是否正确

我直接贴我处理完后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# -*- coding: utf-8 -*-
import cv2
import re
import os
import numpy as np
import pandas as pd
from paddleocr import PaddleOCR
from PIL import Image, ImageDraw, ImageFont

# deal with OCR_img
# API OCR_name

def OCR_name(img_path,orginal_name):

new_name = "test.png"
os.rename(img_path+orginal_name,img_path+new_name) # 将图片改个名字,不清楚为什么传入中文会报错

orginal_img_path = img_path # 存储路径
orginal_img_name = orginal_name
img_path = img_path+new_name # 替换以下用到的文件绝对路径

font = cv2.FONT_HERSHEY_SIMPLEX

# Paddleocr目前支持中英文、英文、法语、德语、韩语、日语,可以通过修改lang参数进行切换
# 参数依次为`ch`, `en`, `french`, `german`, `korean`, `japan`。
ocr = PaddleOCR(use_angle_cls=True, lang="ch", use_gpu=False,
rec_model_dir='./inference/ch_ppocr_server_v2.0_rec_infer/',
cls_model_dir='./inference/ch_ppocr_mobile_v2.0_cls_infer/',
det_model_dir='./inference/ch_ppocr_server_v2.0_det_infer/') # need to run only once to download and load model into memory

first_name = ['李', '王', '张', '刘', '陈', '杨', '赵', '黄', '周', '吴', '徐', '孙', '胡', '朱', '高', '林', '何', '郭', '马', '罗', '梁',
'宋', '郑', '谢', '韩', '唐', '冯', '于', '董', '萧', '程', '曹', '袁', '邓', '许', '傅', '沈', '曾', '彭', '吕', '苏', '卢',
'蒋', '蔡', '贾', '丁', '魏', '薛', '叶', '阎', '余', '潘', '杜', '戴', '夏', '钟', '汪', '田', '任', '姜', '范', '方', '石',
'姚', '谭', '廖', '邹', '熊', '金', '陆', '郝', '孔', '白', '崔', '康', '毛', '邱', '秦', '江', '史', '顾', '侯', '邵', '孟',
'龙', '万', '段', '漕', '钱', '汤', '尹', '黎', '易', '常', '武', '乔', '贺', '赖', '龚', '文', '盧']


def putText_Chinese(img, strText, pos, color, fontSize):
fontpath = "./doc/fonts/simfang.ttf" # 宋体路径
font = ImageFont.truetype(fontpath, fontSize)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text(pos, strText, font=font, fill=color)
img = np.array(img_pil)
return img


def first_str(s):
s = s.replace(" ", "") # 防止空格
return [ch for ch in s]

print('---------------PaddleOCR Start---------------------')

img = cv2.imread(img_path)
#cv2.imshow(img_path, img) # show original picture
result = ocr.ocr(img_path, cls=True)

data = []
for line in result:
data.append(line[1][0])
name=""
pt1 = ((int)(line[0][0][0]), (int)(line[0][0][1]))
pt2 = ((int)(line[0][1][0]), (int)(line[0][1][1]))
pt3 = ((int)(line[0][2][0]), (int)(line[0][2][1]))
pt4 = ((int)(line[0][3][0]), (int)(line[0][3][1]))
cv2.line(img, pt1, pt2, (0, 0, 255), 1, cv2.LINE_AA)
cv2.line(img, pt2, pt3, (0, 0, 255), 1, cv2.LINE_AA)
cv2.line(img, pt3, pt4, (0, 0, 255), 1, cv2.LINE_AA)
cv2.line(img, pt1, pt4, (0, 0, 255), 1, cv2.LINE_AA)

img = putText_Chinese(img, line[1][0], (pt1[0], pt1[1] - 35), (255, 0, 255), 50)
value = re.findall("'(.+?)'", str(line[1]))
# value[0] is comlete string
# first_str(value[0]) is modify string

if first_str(value[0])[0] in first_name:
name = value[0]
print(value)
break # jump out of the loop and rename
# print("failly! please inspect model!")

data = pd.DataFrame(data)

if name != "":
name = name + ".png"
os.rename(img_path, orginal_img_path+name)
else:
os.rename(img_path, orginal_img_path+orginal_img_name)

#cv2.imwrite("show_"+name+".png", img) # 输出版面分析图片
#cv2.imshow("模型展示"+name+".png", img) # 展示图片

cv2.waitKey()
cv2.destroyAllWindows()

在这里我为了配合其他代码块简洁。将OCR识别区域写为了一个函数

需要传入2个参数,一个是文件夹路径(img_path),另一个是原名称(orginal_name)

部分代码解释
  1. 直接传入中文名称会有奇奇怪怪的问题,在程序开始部分对其重命名为test。在识别成功会被重命名,识别失败会被重命名为原名称。
  2. 代码中直接判断了百家姓规则,将符合规则的字符串作为姓名把图片重命名。
  3. 其中注释掉了展示版面分析部分,即第81,82行。

OCR识别部分完结🍕

图片剪切

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# -*- coding: utf-8 -*-
import os
from PIL import Image

# cut picture
# need path of picture and place,value

def cut_picture(file_in, width, height, value):
if value=="mediary": #居中缩放
# dont't cut picture, entire shrink to specify pixel of picture
image = Image.open(file_in)
resized_image = image.resize((width, height), Image.ANTIALIAS)
resized_image.save(file_in)
else:
img = Image.open(file_in)
_width, _height = img.size
print(_width,_height, "==>",width,height)
# 图像从左上角建立坐标系,四个值依次为距左的像素,距顶的像素,从左开始剪切到距离左多少的像素,从顶开始剪切到那里的像素。
# 即(左,上,右,底),构成一个矩形
if value=="leftTop":
crop = (0, 0, width, height)
elif value=="rightTop":
crop = (_width-width, 0, _width, height)
elif value=="bottomRight":
crop = (_width-width, _height-height, _width, _height)
elif value=="bottomLeft":
crop = (0, _height-height, width, _height)
elif value=="middle":
crop = (int((_width-width)/2), int((_height-height)/2), int((_width-width)/2+width), int((_height-height)/2+height))
cropped = img.crop(crop) # (left, upper, right, lower)
cropped.save(file_in)

使用了PIL库直接操作了图片,没有什么特别的地方。

主要是剪切的坐标系需要注意,是从左上角开始。

同样写为了一个函数,需要传入(file_in, width, height, value),分别是:图片绝对路径,保留的宽度,高度,基点位置。

单文件版本见我的吾爱贴子


文件查缺少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import re
import os

# who is missing
# need excel file and folder path

def without(txt_path,folder_path):
file = open(txt_path, encoding="utf-8")
body=[]
for line in file:
body.append(line.replace('\n', '')) # 添加到列表
print("\n已读取", body[0], "等", len(body), "人")
listtwo = []
a = 0
filePath = folder_path
list = os.listdir(filePath)
if len(list) == len(body): # 当文件数量等于名字列表时
print("相同!")
input("按下回车退出!")
else:
print("\n人数不匹配!(", len(list), "/", len(body), ")人", "缺少", len(body) - len(list), "人\n")
for i in range(len(list)):
marry = list[i].replace(".jpg", "").replace(".png", "").replace(".docx", "").replace(".pptx", "").replace(
".xlsx", "") # 将后缀去掉
listtwo.append(marry)
# count = body.count(marry)
for v in range(len(body)): # 将文件夹内名字与列表匹配
if body[v] in listtwo:
pass # 匹配成功不处理
else:
print("不存在:", body[v]) # 输出不匹配的
a = a + 1
if a != len(body) - len(list):
print("\n返回人数不一样,请检查你的文件内名字是否有错别字!\n")
else:
print("\n数据返回完毕!")

传入2个参数,一个是花名册.txt,将名单每行一个存入txt文件。另一个是需要遍历查找的文件夹路径。

自动查找缺少谁。

1
without("花名册.txt","D:\Desktop\\test2\\")


遍历文件夹*

上述前俩功能,都需要去处理文件夹下所有图片。

所以此功能是它们的驱动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
import os

# deal with folder
# input folder_path

form_name = [".jpg",".png",".jpeg"] #判断的图片后缀

def find_folder(folder_path, value, width, height,cut_vaule):
folder_name = os.listdir(folder_path)
if value=="ocr":
for v in range(len(folder_name)):
if form_name[0] in folder_name[v] or form_name[1] in folder_name[v] or form_name[2] in folder_name[v]:
# print(folder_name[v])
OCR_name(folder_path,folder_name[v])
else:
print("不符合的类型", folder_name[v])
elif value=="cut":
for v in range(len(folder_name)):
cut_picture(str(folder_path+folder_name[v]), width, height, cut_vaule) # (path,width,height,value)

在此函数的驱动下,OCR识别与cut_picture俩个函数功能只需要传入一个即可。

1
2
3
find_folder("D:\Desktop\hh"+"\\","ocr",0,0,0) # OCR识别,传入文件夹路径即可

find_folder("D:\Desktop\\02-副本\\","cut",200,500,"middle") # Cut图片,传入文件夹路径,宽,高,基点位置

Ui绘制

很多朋友说程序黑乎乎的Dos窗口很丑。于是我用PYQT5画了一个简单的Ui界面。

但,还没将他们关联好。

未完待续…