使用Python统计抗疫未填报同学排名
辅导员老师每天都会发当天没有填“抗疫小助手”同学名单的截图。有一天突发奇想,能不能统计一下没填的同学的名单,然后排序看一看哪位能够勇夺第一名呢?
本文章仅为娱乐及学习目的。
说干就干。统计的核心过程就是把老师发的截图集中保存下来,然后将其中的名字识别出来即可。保存截图就是一个体力活,只需要每天老师发截图的时候动动小手保存下来就可以。保存下来的截图如下图所示。
老师的截图的示例如下图所示。
其中,打码的第一列是名字,第二列是学号。我们需要的只是名字,并且由于老师用的手机是固定的,每张图中的名字的位置都相同,颜色也都相同。通过计算CSS,得知姓名一栏占用了网页最左侧的10%宽度。这样,我们只需要将图像最左侧的一竖条提取出来,并通过颜色范围将字提取出来,即可得到只含有名字的图片。
得到这个图片之后,如何把它们转换成可以供程序处理的文字呢?答案很简单,OCR。在此我们可以使用本地的OCR,也可以使用云端的OCR。为了简单起见,这里使用了百度云AI开放平台的通用文字识别服务。为了精准起见,这里使用的是高精度版。对于免费用户,每天有500
次的免费额度且有2 Qps
的限制,但这已经足够我们使用的了。为了使用Python语言调用百度云的API,可以使用百度云官方的Python API。使用pip
安装好后,向百度申请API Key
即可正常使用。
识别过程中,首先读入图片,然后经过opencv
的处理生成一个元组,之后同样使用opencv
将其编码成jpg格式,通过API发送到百度进行识别即可获得图片中所有人的名字。
最后,我们只需要使用matplotlib
来绘制图表即可。再次不再赘述。(matplotlib
用的很少,画出来的图能正常显示就行了,别要求太多……)
第一名居然超出第二名18次,果然是优秀的同学(虽然我并不认识)。
如下是所有代码。
from aip import AipOcr
import cv2, numpy as np, matplotlib
import os, time
from matplotlib import pyplot as plt
font = {'family': 'MicroSoft YaHei'}
matplotlib.rc('font', **font) # 使pyplot支持中文
plot_num = 30
APP_ID = 'xxxxxxxxx'
API_KEY = 'xxxxxxxxx'
SECRET_KEY = 'xxxxxxxxx'
font_color = np.array([35, 46, 204])
font_horizontal_ratio = 0.1 # 测量CSS之后得出姓名栏10%的比例
folder = r'screenshots'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
name_count = dict()
filelist = os.listdir(folder)
for file in filelist:
image = cv2.imread(folder+'\\'+file) # 读入图片
size = image.shape
fnt = cv2.inRange(image, font_color*0.9, font_color*1.1) # 提取文字
cropped = fnt[:, 0:int(size[0]*font_horizontal_ratio)] # 裁剪图片
_, img_encoded = cv2.imencode('.jpg',cropped) # 编码jpg
res = client.basicAccurate(img_encoded)
print('[', file, ']', end=' ')
try:
for name_dict in res['words_result']:
name = name_dict['words']
if name in name_count:
name_count[name] = name_count[name] + 1 # 累计出现次数
else:
name_count[name] = 1 # 新增
print(name, end=' ')
except ValueError:
print('识别错误', end='')
pass
print('')
time.sleep(0.5)
print('总人数: {}'.format(name_count.__len__()))
print(name_count)
name_sorted = np.array(sorted(name_count.items(), key=lambda d: d[1], reverse=True))
col_name = np.flip(name_sorted[0:plot_num,0])
col_times = np.flip(name_sorted[0:plot_num,1])
plt.figure(dpi=144) # 设置图形大小
plt.barh(range(plot_num), col_times, height=0.3, color='orange') # 绘制横着的条形图,横着的用height控制线条宽度
plt.yticks(range(plot_num), col_name)
plt.grid(alpha=0.3) # 添加网格
plt.show()