使用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()