flask人脸识别图书系统(下)
四、flask蓝图(blue_print)改写后端的实现
用蓝图技术来修改这个程序,首先分析一下,目前后端完成了两个功能,上传人脸头像和验证人脸头像,这两个功能可以设计成两个蓝图开发中的两个模块,上传人脸头像这里叫myupload,可以在pycharm建立一个flask蓝图项目mylantu,再建一个python包文件app,将所有的模块应用都集中一个包文件中管理,这样比较有序。接着建立第一个后端功能的python包文件myupload上传头像,在myupload包文件下建立一个views视图文件,将原来实现的upload代码复制到这里来,再加上Blueprint语句初始化这个蓝图应用upload,做upload这个蓝图应用的路由规则,对应的方法中实现原来upload中写入的代码。
代码如下。
from flask import Blueprint,render_template,request
from app import mongo
import base64
import pickle
import face_recognition
from bson.binary import Binary
upload=Blueprint("upload",__name__,template_folder="templates")
@upload.route("/",methods=["GET","POST"])
def upload_face():
if request.method=="POST":
imgdata=request.form.get("myimg")
imgdata=base64.b64decode(imgdata)
with open("a.png","wb") as f:
f.write(imgdata)
faceimg=face_recognition.load_image_file("a.png")
facedata=face_recognition.face_encodings(faceimg)[0]
print(facedata)
binary_data = Binary(pickle.dumps(facedata,protocol=-1),subtype=128)
mongo.db.myface.insert_one({'face':binary_data})
return {"result":"OK"}
return render_template("video.html")
myupload模块的包结构如下。
完成了myupload的功能逻辑模块,继续进行验证人脸头像进行登陆的功能模块开发。在原来建立的包文件夹app下面继续建立mylogin包文件夹,也是在mylogin包文件夹下建立views文件,将原来的checkface()方法中的代码复制到这里,再加上Blueprint语句初始化这个蓝图应用login,做login这个蓝图应用的路由规则,对应的方法中实现原来checkface中写入的代码。
代码如下。
from flask import Blueprint,request,render_template
import base64
import face_recognition
from app import mongo
import pickle
login=Blueprint("login",__name__,template_folder="templates")
@login.route("/",methods=["GET","POST"])
def login_home():
if request.method == "POST":
imgdata = request.form.get("myimg")
imgdata = base64.b64decode(imgdata)
with open("b.png", "wb") as f:
f.write(imgdata)
faceimg = face_recognition.load_image_file("b.png")
facedata = face_recognition.face_encodings(faceimg)[0]
faces=mongo.db.myface.find()
for fa in faces:
'''
取出的数据是myface数据库中的每一条记录face的"face"键对应的值
'''
facedata_orign=pickle.loads(fa["face"])
res=face_recognition.compare_faces([facedata],facedata_orign)
print(res)
if res[0]:
return {"result":"this is true face"}
else:
return {"result":"not you face"}
return render_template("check_face.html")
两个后端基本功能完成以后,在建立app包文件的init.py文件中写入flask初始化的相关语句。
from flask import Flask
from flask_pymongo import PyMongo
apps=Flask(__name__,template_folder="templates",static_folder="static")
apps.config["MONGO_DBNAME"]="myface"
apps.config["MONGO_URI"]="mongodb://localhost:27017/myface"
mongo= PyMongo(apps)
完成上面的语句后,还要在当前app应用中注册上面完成的两个后端功能应用。注意,这段注册后端功能的应用代码和导包都要在mongo=PyMongo(apps)后面,不然会报错,原因是这两个模块中的views中都要调用这个mongo变量,如果mongo变量没有产生,就不能被导包和调用。
from app.mylogin.views import login
from app.myupload.views import upload
apps.register_blueprint(login,url_prefix="/login")
apps.register_blueprint(upload,url_prefix="/upload")
最后完成manage.py调用app初始化的应用run()方法即可.
from app import apps
if __name__=="__main__":
apps.run()
至此,后端结合前端的flask人脸登录图书系统flask蓝图技术实现完成。
五、图书管理系统数据分析
1、根据书名和分类,找出适合表达书籍的主题的词汇
在数据分析中,主题词的提取也是很重要的数据分析的内容,一个主题词往往反应出书名属于哪个领域。
对于一些专业的词汇,用jieba分词进行分词化时,也需要加载用户自定义的分词文件,具体代码如下。
jieba.load_userdict("library_new_words.txt")
对于书籍主题词的提取,可以读取图书目录,对图书来讲,主要是分类号,不同的分类号表示了不同的书籍类型,可以对分类号进行前去空格处理,也对分类号对应的书名做前后去空格处理,接着可以提取图书分类号中的主码,主码可以表征当前图书分类的所属类别,具体图书分类的主码可以参见《中国图书馆图书分类法》简表,这张简表也是一般大型图书馆对图书分类的参考,也是对图书管理行业的专业的知识体现。
对数据进行了格式上面的处理及重要信息的提取后,就可以按照数据分析的思路:去重、去空、去异常等操作。这里的数据可以去掉空值行,去掉重复的数据,对于重复的数据只保留第一个,对于文学作品来说,主题词一般和题目息息相关,这里可以不考虑文学作品的主题词提取。根据《中国图书馆图书分类法》简表可以知道,文学对应的分类主码是I,相当于在图书分类号中把首字母为“I”的图书信息过滤掉。最后再将图书目录信息表的数据重置相关的索引号。代码如下。
book = pd.DataFrame(pd.read_excel(dataPath+'\\图书目录.xlsx'))
book=book.loc[:,['书名','图书分类号']] #
book['图书分类号']=book['图书分类号'].str.strip() #去掉图书分类号前后的空格
book['书名']=book['书名'].str.strip() #去掉书名前后的空格
book['图书分类号']=book['图书分类号'].str.extract('([A-Z]+)', expand=False) #提取图书分类主码
book=book[book['书名'].notnull() & book['图书分类号'].notnull()] #去掉空值行
book=book.drop_duplicates(['图书分类号','书名']) #去掉重复的,保留第1个
book=book.loc[~ book['图书分类号'].str.startswith('I')] #去掉文学作品,文学作品不参加主题词筛选
book=book.reset_index(drop=True) #重置索引
因为最终要达到的目的一个主题词反映的书名属于哪一个领域。可以用图书分类号的主码和书名的分化结合起来,jieba分词提取主题词函数jieba.analyse.extract_tags()后变成了横向的维度,表明了这种分类的书有哪些主题词,也就是这些主题词能表示哪一种分类的书。对于横向维度的数据有很多的值是None,因为是以最长的主题词来进行横向式的扩展的。通过stack()堆叠技术可以将横向的数据维度进行纵向化,并且将数据维度中是None值的清除掉,紧接着把索引值重置,语义化一下将此列名列更改为“词条”。
将上面的逻辑语句分步执地如下:
1、把数据集中先把书名这一列删除。
book.drop('书名', axis=1)
2、从书名中提取关键词。
jieba.analyse.extract_tags(x) for x in book['书名']
3、将书名中的“关键词”列表转换成DataFrame和图书分类号形成数据表。
book.drop('书名', axis=1).join(pd.DataFrame(jieba.analyse.extract_tags(x) for x in book['书名'])
4、stack()方法将横向的DataFrame数据表堆叠起来。
book.drop('书名', axis=1).join(pd.DataFrame(jieba.analyse.extract_tags(x) for x in book['书名']).stack()
5、将堆叠后的竖向DataFrame数据表重置索引。
book.drop('书名', axis=1).join(pd.DataFrame(jieba.analyse.extract_tags(x) for x in book['书名']).stack().reset_index(level=1, drop=True)
6、最后将此列更名为“词条”。
book.drop('书名', axis=1).join(pd.DataFrame(jieba.analyse.extract_tags(x) for x in book['书名']).stack().reset_index(level=1, drop=True).rename('词条'))
表达书籍的主题词汇这个问题实际上可以理解成tf-idf统计词频逆词频的问题,这里就需要进行各种量值的统计。现对各种统计量的变量值含义进行统计。
(1)dw_count:为词条在图书分类中出现的次数。
(2)dw_total为图书分类中所有的词条数目。相同图书分类具有相同的值。相同词条重复个数。
(3)d_total为图书分类总数。只有一个值,不计重复个数。
(4)d_count为包含词条的图书分类数。相同词条具有相同的值。
(5)w_total:词条在图书分类中出现的总次数,重复计数。
(6)cumsum: 相同词条的记录,按词条在分类中出现次数降序排列时,词条出现次数dw_count的累加和。
(7)rank:相同词条的记录,按词条在分类中出现次数降序排列时的顺序号,出现频次最多的分类的rank值为1。
(8)tf:词频,词条在图书分类中出现的次数占该分类中所有词条的比例。
(9)cumtf: 词频的累加值,同一词条最后行的累加值总是为1。
(10)tfidf:tf和idf的乘积值。
程序逻辑中先假定词条在图书分类中初始化的初值为1。代码如下。
words['dw_count']=1
将前面分步讨论的词条提取步骤的结果按第一索引值"图书分类号"和第二索引值“词条”进行分组聚合,然后对分组出的结果进行求和。然后进行重置索引号,这样就实现了对应图书分类号中的词条数量统计。代码如下。
words=words.groupby(['图书分类号','词条']).sum()
words.reset_index(inplace=True)
图书每个分类号下面的所有词条数,可以按“图书分类号”统计,求出sum总和,更新到dw_total字段中。
words['dw_total']=words.groupby('图书分类号')['dw_count'].transform('sum')
图书分类总数就是对书籍列表的describe()方法统计后的每一个不同的“图书分类号”。可以用unique()方法展示图书分类号所有可能的取值。
d_total=book.describe()['图书分类号']['unique']
包含该词条的图书分类数,可以按“词条”进行分组,求出其count值,然后更新到d_count中。
words['d_count']=words.groupby('词条')['dw_count'].transform('count')
词条在图书分类中出现的总次数,可以按“词条”分组,求出其sum值,然后更新到w_total中。
words['w_total']=words.groupby('词条')['dw_count'].transform('sum')
紧接着按第一索引值"词条"和第二索引值"dwcount",也就是对词条分组的条件下,再按词条图书分类中的次数来分组。cumsum()函数的方法是相同词条的记录,按词条在分类中出现次数降序排列时,词条出现次数dwcount的累加和。
words=words.sort_values(['词条','dw_count'],ascending=[True, False])
words['cumsum']=words.groupby('词条')['dw_count'].cumsum()
用rank()函数的作用是相同词条的记录,按词条在分类中出现次数降序排列时的顺序号,出现频次最多。
words['rank']=words.groupby('词条')['cumsum'].rank()
根据tf(词频)的计算公式,如下。
由上面的公式,前面某个词在文章中出现的次数使用dwcount的变量来保存的,文章的总词数是由wtotal的变量来保存的。这样tf(词频)的计算代码如下。
words['tf']=words['dw_count']/words['w_total']
根据idf(逆词频)的计算公式,如下。
由上面的公式,前面语料库的文档总数使用dtotal变量来保存,包含该词的文档数由dcount变量来保存。这样idf(逆词频)的计算代码如下。
words["idf"]=(1+d_total/words['d_count']).transform(math.log)
tf和idf的计算就是把tf和idf的值进行相乘即可。代码如下。
words['tfidf'] = words['tf']*(1+d_total/words['d_count']).transform(math.log)
这样可以输出词条文件。
words.to_excel(dataPath+'\\words.xlsx',index=None)
接下来,可以按照一定的规则去筛选出主题词,比如规定包含该词的文档数大于等于3定为主题词,小于3等于2的话如果文章的总词数达到15以上,或者小于3等于1的话如果文章总词数达到6以上,都可以定为主题词。
topicWords=words.loc[((words['d_count']>=3) & (words['rank']==3) & (words['cumtf']>0.85) & (words['cumsum']>20)) \
| ((words['d_count']==2) & (words['w_total']>15)) \
| ((words['d_count']==1) & (words['w_total']>6))]
这样导出文档,就是图书馆收藏书目的主题词列表。
topicWords.to_excel(dataPath+'\\TopicWords_detail.xls',index=None)
topicWords = topicWords.loc[:,['词条']]
topicWords.to_excel(dataPath+'\\TopicWords.xls',index=None)
代码的github地址:https://github.com/wawacode/flask_over_face_recognition
项目对应的视频地址:
flask人脸登录图书系统4-facerecognition模块实现化妆功能
https://www.bilibili.com/video/BV19i4y1K7bz/
flask人脸登录图书系统5-图书爬虫技术分享
https://www.bilibili.com/video/BV1fr4y1P7fq/
flask人脸登录图书系统6-图书管理数据分析
https://www.bilibili.com/video/BV1Ez4y127RX/