Python实战项目:flask人脸识别图书系统(上)

    科技2022-08-08  114

    flask人脸识别图书系统(上)

    涉及内容:爬虫&开发&数据分析

    a、前端界面的技术——> jquery/bootstrap

    b、后面逻辑——>flask,前后端半分离技术,使用模块 flask 蓝图(blue_print)/

    c、收集的图书数据进行数据分析——Pandas模块,结合matplotlib画图

    d、从网上抓取关于书的名字、价格——>requests爬取京东技术,需要js接口的解析。

    一、前端功能的实现

    前端共实现2个需求:A、打开摄像头,B、拍照

    (1)用前端打开摄像头功能可以使用前端的video标签去显示获取到的视频,通过调用navigator对象中mediaDevices媒体对象的getUserMedia()方法根据摄像头区域的宽度和高度来获取内容,如果获取到内容用then方法显示其中内容并播放。

    (2)用前端打开拍照功能使用HTML5的canvas画布标签,画布一般需要提前指明画布的画图方式,画平面图用“2d”的方法,通过drawImage方法在指定的位置画出视屏中显示的图片。

    代码如下。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.1/css/bootstrap.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.1/js/bootstrap.js"></script> <style> video,canvas{ border:2px solid blue } </style> </head> <body> <header class="col-xs-12 navbar navbar-default"> <div class="navbar-header"> <a class="navbar-brand" href="#"> flask图书系统登陆界面 </a> </div> </header> <main div="col-xs-12"> <div class="col-xs-6"> <video id="video" width="100%" height="400"></video> </div> <div class="col-xs-6"> <canvas id="canvas" width="100%" height="400"></canvas> </div> <div class="col-xs-6"> <button onclick="openvideo()" class="btn btn-primary">打开摄像头</button> </div> <div class="col-xs-6"> <button onclick="getPic()" class="btn btn-primary">拍照</button> </div> </main> <script> $("#canvas").width($("#video").width()) $("#canvas").height($("#video").height()) function openvideo(){ /*获取前端的视频标签*/ video=document.getElementById("video") /*定义一个video设备的字典,显示宽高*/ cons={ video:{width:video.width,height:video.height} } /*navigator导航的对象,它可以找到一些硬件mediaDevices,*/ /*getUserMedia取出video摄像头这个设备*/ pros=navigator.mediaDevices.getUserMedia(cons) /*取出后做的内容,用then来接,把内容放在res参数中,与video进行关联*/ pros.then(function(res){ video.srcObject=res video.play() }) } function getPic(){ /*获取前端的视频标签和画布的标签*/ video=document.getElementById("video") canvas=document.getElementById("canvas") /*用canvas画图,画的是平面的2d图形*/ ctx=canvas.getContext("2d") /*调用canvas画布当中的drawImage画图函数*/ /*drawImage四个参数:画图位置和宽高*/ /*画的目标是视频中的内容*/ ctx.drawImage(video,0,0,video.width,video.height) } </script> </body> </html>

    运行结果如下。

    二、后端功能的实现

    前端共实现2个需求:

    A、接收前端上传的人脸图片,存储到数据库中,目的是对以后登录者进行对比

    B、下次再上传图片,把数据库中存储的人脸取出来,与上传的图片进行对比,如果是正确的内容,就实现登陆或者允许使用电脑这类的操作,这里允许访问图书馆系统的图书列表页。

    技术实现:

    数据库采用mongodb,flask连接mongodb,需要安装flask-pymongo模块。

    安装方法: pip3 install flask-pymongo

    人脸识别模块,采用face-recognition,face-recognition的安装,需要安装dlib模块,再安装face-recognition。注意,如果要安装dlib模块,就必安装cmake,并且设置cmake的环境变量。针对cmake可以通过安装visual studio软件来完成这样的操作,必须保证visual studio版本是2015以上的版本。如图所示。

    安装visual studio安装成功后,设置cmake的环境变量。如图所示。

    设置cmake路径后,就可以安装face-recognition。

    pip3 install face-recognition

    安装过程中,visual studio版本比较占空间,可以安装完face-recognition后,卸裁visual studio。

    先用flask实现hello world,相当地实现了flask的基本结构。

    from flask import Flask app=Flask(__name__) @app.route("/hello") def index(): return "Hello World!" if __name__=="__main__": app.run()

    设置另外一个路由规则,显示上传脸部拍照数据的界面。

    from flask import Flask,render_template app=Flask(__name__,template_folder="templates",static_folder="static") @app.route("/upload_face") def index(): return render_template("upload_face.html") @app.route("/hello") def index(): return "Hello World!" if __name__=="__main__": app.run()

    上传脸部数据界面发生post请求时,就将数据上传到服务器后台。可以用form.data.get()来接收指定标签的内容数据,接着需要将内容数据用base64模块转让化成base64编码,然后可以存储到一个图片文件中。存储到mongo数据库时,需要facerecognition模块中的loadimagefile去加载文,再用facerecognition模块中faceencodings对人脸进行编码,需要注意的是,这里如果因为一些明暗度的问题,可能会出现找不到脸的情况,前端拍照的结果要注意亮度,尽量能够分辩出人脸,不然faceencoding模块解析出来的数据会为[],这样存储到数据库mongodb就会报错。存储到mongodb数据库需要设置flask的app一些参数,app.config["MONGODBNAME"]="myface"设置mongodb数据库连接的connection的名称,app.config["MONGOURI"]="mongodb://localhost:27017/myface" 语句是mongodb数据库的连接地址。定义mongo变量,用PyMongo实例化,并把app做为参数。因为facerecognition解析后的数据是二维数组,保存这样的数据需要二进制转化后进行存储数据库mongodb的操作。bson.binary模块中的Binary类就可以将二维数组转化成二进制,pickle模块dump()方法可以facerecognition处理的结果进行二进制可写。把facerecognition处理的结果转成二进制后就可以调用insertone方法存储到mongodb数据库中。代码如下。

    from flask import Flask,request,render_template import base64 import face_recognition from bson.binary import Binary import pickle from flask_pymongo import PyMongo app=Flask(__name__,template_folder="templates",static_folder="static") ''' 连接mongodb,为app设置的内容 ''' app.config["MONGO_DBNAME"]="myface" app.config["MONGO_URI"]="mongodb://localhost:27017/myface" ''' 将app应用与mongodb产生联系,使用PyMongo(app) ''' mongo=PyMongo(app) @app.route("/hello",methods=["GET","POST"]) def hello(): return "hello world" @app.route("/",methods=["GET","POST"]) def uploadface(): ''' 功能:接收前端上传来的人脸图片,完成数据库的保存 1、取上传来的数据的参数 request.form.get("myimg") 2、上传的数据是base64格式,调用后端的base64进行解码 3、存到后台变成图片 4、调用face_reconginition的load_image-file读取文件 5、调用 face-recognition的face_encodings对脸部进行编码 6、利用bson和pickle模块组合把脸部编码数据变成128位bitdata数据 7、利用mongo.db.myface.insert_one插入数据,存储到mongodb里面 上面是逻辑,但逻辑发生在post方式上,不发生在get, 限定一下上面逻辑的发生条件,不是POST方式,就是GET,GET请求页面 :return: ''' 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")

    前端上传图片数据的实现。一般前端都使用ajax技术上传数据到后台,前面已经实现了前端canvas标签中显示拍照的数据,现在只需调用toDataURL()方法将数据转成数据,利用$.post提交即可。需要注意的是,转成后的数据前22个字符表示的是编码格式,需要截取22位字符后面的数据进行提交到后台服务器。完善后前端getPic() 函数的代码如下。

    function getPic(){ /*获取前端的视频标签和画布的标签*/ video=document.getElementById("video") canvas=document.getElementById("canvas") /*用canvas画图,画的是平面的2d图形*/ ctx=canvas.getContext("2d") /*调用canvas画布当中的drawImage画图函数*/ /*drawImage四个参数:画图位置和宽高*/ /*画的目标是视频中的内容*/ ctx.drawImage(video,0,0,300,300) /*ajax只能上传的是数据,不是图片,只能把图片转成数据*/ /*toDataURL把canvas画的图片变成数据*/ data=canvas.toDataURL("image/png",0.5) console.log(data) /*下面一句代码的意思,这个canvas转成的data数据前面的22位只是编码格式,不是图片数据*/ data=data.substring(22) /*图片数据只能post方式来提交*/ $.post("/",{"myimg":data},function(res){ console.log(res) }) }

    至此,接收前端上传的人脸图片,存储到数据库的功能已经编码结束。

    下一步,可以再产生一个人脸登录页面,布局可以在上传人脸界面的结构上另外增加一个登陆按钮就可以,为了验证脸部数据的不同,可以再加上一个上传图片的功能,上传图片功能可以实现服务器后端接收的图片不是摄像头拍照的图片。在一个人调试的时候很方便。代码如下。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.1/css/bootstrap.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.1/js/bootstrap.js"></script> <style> video,canvas{ border:2px solid blue } div{ margin-top:30px; } </style> </head> <body> <header class="col-xs-12 navbar navbar-default"> <div class="navbar-header"> <a class="navbar-brand" href="#"> flask图书系统登陆界面 </a> </div> </header> <main div="col-xs-12"> <div class="col-xs-6"> <video id="video" width="100%" height="400"></video> </div> <div class="col-xs-6"> <canvas id="canvas" width="100%" height="400"></canvas> </div> <div class="col-xs-6"> <button onclick="openvideo()" class="btn btn-primary">打开摄像头</button> </div> <div class="col-xs-6"> <button onclick="getPic()" class="btn btn-primary">拍照登陆入口</button> </div> <div class="col-xs-6"> <button onclick="openvideo()" class="btn btn-primary">上传图片</button> </div> <div class="col-xs-6"> <button onclick="getPic()" class="btn btn-primary">上传图片登陆入口</button> </div> </main> <script> $("#canvas").width($("#video").width()) $("#canvas").height($("#video").height()) function openvideo(){ /*获取前端的视频标签*/ video=document.getElementById("video") /*定义一个video设备的字典,显示宽高*/ cons={ video:{width:video.width,height:video.height} } /*navigator导航的对象,它可以找到一些硬件mediaDevices,*/ /*getUserMedia取出video摄像头这个设备*/ pros=navigator.mediaDevices.getUserMedia(cons) /*取出后做的内容,用then来接,把内容放在res参数中,与video进行关联*/ pros.then(function(res){ video.srcObject=res video.play() }) } function uploadface(){ /*获取上传文件的元素,获取文件的相关信息*/ file=document.getElementById("file").files[0] console.log(file) /*定义javascript读文件的函数*/ reader=new FileReader() /*readAsDataURL把读取的内容变成数据*/ reader.readAsDataURL(file) /*onloadend是读取结束*/ reader.onloadend=function(){ /*reader的result内容中接收结果*/ res=reader.result res=res.substring(22) $.post("/upload_face",{"myimg":res},function(res){ console.log(res) }) } } function getPic(){ /*获取前端的视频标签和画布的标签*/ video=document.getElementById("video") canvas=document.getElementById("canvas") /*用canvas画图,画的是平面的2d图形*/ ctx=canvas.getContext("2d") /*调用canvas画布当中的drawImage画图函数*/ /*drawImage四个参数:画图位置和宽高*/ /*画的目标是视频中的内容*/ ctx.drawImage(video,0,0,300,300) /*ajax只能上传的是数据,不是图片,只能把图片转成数据*/ /*toDataURL把canvas画的图片变成数据*/ data=canvas.toDataURL("image/png",0.5) console.log(data) /*下面一句代码的意思,这个canvas转成的data数据前面的22位只是编码格式,不是图片数据*/ data=data.substring(22) /*图片数据只能post方式来提交*/ $.post("/upload_face",{"myimg":data},function(res){ console.log(res) }) } </script> </body> </html>

    继续完成下面的逻辑,把数据库中存储的人脸取出来,与上传的图片进行对比,如果是正确的内容,就实现登陆或者允许使用电脑这类的操作。

    在后端实现这样的逻辑,也是需要在flask中建立一个新的请求路由,然后也是接收上传过来的图像数据,继续base64模块的编码转换,存储成一个新的图片,使用facerecognition人脸识别模块读取存储的新的图片,再进行faceencodings编码,调用mongodb中的find()方法找出数据库中所有的人脸数据,如果有一张脸的数据恰好就是当前上传的人脸的数据,就可以根据前后端分离的原理,返回前端json数据{"result":"人脸识别正确"},如果错误,就返回{"result":"是没有权限登陆的人脸"},注意从数据库中取出的每一个人脸数据都需要用pickle模块的loads方法转化成二维数组,这样就可以调用facerecognition中的facecompare方法进行人脸数据的比对。代码如下。

    @app.route("/check",methods=["GET","POST"]) def checkface(): ''' 功能:接收前端上传来的人脸图片,完成数据库的保存 1\取上传来的数据的参数 request.form.get("myimg") 2\上传的数据是base64格式,调用后端的base64进行解码 3\存到后台变成图片,取另一个名称 4\调用face_reconginition的load_image_file读取文件 5\调用 face-recognition的face_encodings对脸部进行编码 6\利用pickle模块把脸部的编码从数据库中提取出来 7\将数据库提取的脸部数据与当前用户在上传的脸部数据进行对比, 8\如果对比成功,返回ok,如果对比失败,返回:"not you face" 上面是逻辑,但逻辑发生在post方式上,不发生在get, 限定一下上面逻辑的发生条件,不是POST方式,就是GET,GET请求页面 :return: ''' 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")

    代码的github地址:https://github.com/wawacode/flask_over_face_recognition

    项目对应的视频地址:

    flask人脸登录图书系统1-前端人脸登陆的拍照效果 https://www.bilibili.com/video/BV1Xy4y1Y7GK/ flask人脸登录图书系统2-后台接收ajax图片数据人脸识别并存储到mongodb中 https://www.bilibili.com/video/BV1by4y1E7fS/ flask人脸登录图书系统3-后台实现人脸比对逻辑 https://www.bilibili.com/video/BV1T5411E76x/

    Processed: 0.016, SQL: 8