由于我本专业是光电信息科学与工程,最近课设要做一个人脸识别。对于我这种半途转前端的on9仔,当然是不会做啦。通过查找资料发现,可以利用opencv这个开源的计算机视觉和机器学习软件库来实现,我就想着上网CTRL+CV
一段代码交差啦哈哈哈😅
灵光一闪
竟然可以做到人脸识别,那岂不是可以在我的项目的登录系统中加入人脸登录,那样面试的时候又有东西可吹了😁
废话不多,直接开干。
思路
- 前端注册时上传自己的大头照,后端通过OpenCV训练
- 登录时前端通过tracking.js识别出自己的人脸,再利用canvas转正base64的图片上传给后端,后端拿这张图片进行识别
技术栈
- 前端:Vue2 + tracking.js
- 后端:Python + OpenCV + Flask
不会Python的小伙伴不要担心,我也不会哈哈哈🤣🤣,只用调用opencv的库就ok了
人脸识别示范
我这里用的是这篇文章的代码用Python进行人脸识别[包括源代码],里面有源码和打开教程(本来打算直接就用来交差的)
可以点我这里下载源代码 人脸识别代码,或者文章后面有我封装好的代码
打开后,在终端运行以下命令,安装依赖
pip install numpy opencv-python
pip install dlib
pip install face_recognition
然后执行一下main.py文件,就可以看到运行的结果了,
总结一下
通过Python
利用OpenCV
先训练train
文件夹下的图片,然后我们拿test
文件夹下的图片用来测试,OpenCV
会通过train
下最像的图片,放回该图片的文件名(人名)
前端实践
注册
这一部分只要把用户名(最后识别出来的名字)和图片上传给后端
注意 照片一定要清晰的大头照,不然OpenCV
训练不了会报错
<form
method="post"
action="http://localhost:3000/register/"
enctype="multipart/form-data"
target="hideIframe1"
>
<h3>注册</h3>
<input type="text" placeholder="账号" name="username" />
<input type="file" name="photo" />
<button style="margin-top: 10px" type="submit">注册账号</button>
</form>
tracking.js
tracking.js
的安装网上说不建议用nmp,我们可以到官网上下载tracking.js然后把文件放在src/assets
目录下文件夹更名为 tracking 方便引入
之后我们只要在需要的页面上引用
import "@/assets/tracking/build/data/face-min.js";
人脸登录
这一步,就是调用摄像头,识别出人脸,把人脸图片上传到后端,后端会返回识别后的结果
注意 我这里只是本地调试,上线需要使用有https的域名 否则无法调用摄像头
<div class="video-box">
<video id="video" width="320" height="240" preload autoplay loop muted></video>
<canvas id="canvas" width="320" height="240"></canvas>
</div>
<canvas id="screenshotCanvas" width="320" height="240"></canvas>
// 初始化设置
async init() {
this.video = document.getElementById("video");
this.screenshotCanvas = document.getElementById("screenshotCanvas");
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
// 固定写法
let tracker = new window.tracking.ObjectTracker("face");
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
window.tracking.track("#video", tracker, {
camera: true,
});
// 我这里写了个sleep函数等两秒再执行,避免还没反应过来,摄像头一打开就识别上传了
await this.sleep(2000);
let _this = this;
tracker.on("track", function (event) {
// 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height);
event.data.forEach(function (rect) {
context.strokeStyle = "#0764B7";
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
// 上传图片
_this.uploadLock && _this.screenshotAndUpload();
});
});
},
// 上传图片
async screenshotAndUpload() {
// 上锁避免重复发送请求
this.uploadLock = false;
// 绘制当前帧图片转换为base64格式
let canvas = this.screenshotCanvas;
let video = this.video;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
console.log("camvas", canvas);
// 将图片保存为png格式
let base64Img = canvas.toDataURL("image/png");
// 使用 base64Img 请求接口即可
// console.log("base64Img:", base64Img);
//我后端地址是localhost:3000,我这里用了webpack代理解决跨域
axios.post("/api/check", {
data: base64Img,
}).then((res) => {
。。。。。。
});
// 请求接口成功以后打开锁
// await this.sleep(1500);
// this.uploadLock = true;
},
到这一步前端的工作就全部完成了
后端实践
Flask开启服务器
我真的不会Python
😅😅,但是思路上后端要做的事情都一样,我先上网搜了下发现可以利用Flask开启一个本地的服务器
from flask import Flask, request
app = Flask(__name__)
@app.route("/test/")
def test():
return "hello world"
if __name__ == '__main__':
app.run(host='127.0.0.1', port=3000)
运行起来,在浏览器访问localhost:3000/tset/
,就可以看到hello word
这样我们只需要写两个接口注册和识别
注册
我们只需要把前端传来的图片存在本地的,并以username
命名图片
@app.route('/register/', methods=['POST'])
def register():
img = request.files.get('photo') # 从post请求中获取图片数据
username = request.form.get('username', '')
suffix = '.' + img.filename.split('.')[-1] # 获取文件后缀名
basedir = os.path.abspath(os.path.dirname(__file__)) # 获取当前文件路径
photo = '/static/uploads/' + username + suffix # 拼接相对路径,并把username作为图片名字
img_path = basedir + photo # 拼接图片完整保存路径,
img.save(img_path) # 保存图片
return {'msg': 'ok'}
识别
这里我们拿到的是前端传来base64格式的图片,我们要把它转成图片并保存在本地(temp.png)
@app.route('/check', methods=['POST'])
def check():
src = request.data
print(src)
data = str(src).split(',')[1][:-3]
img_data = base64.b64decode(data)
with open("./test/temp.png", 'wb') as f:
f.write(img_data)
这里利用OpenCV识别这张图片,然后把结果返回
return {'msg': 'ok'}
重点来了
由于我真的一点也不会Python,我的想法很简单,打算把main.py里的代码包装成一个函数最后把识别出来的数组return,我在/check接口中接收。
我咨询了一下我同学一些语法问题,他是视觉识别
这一块的大佬(10多个省奖),他听了后觉得代码写的非常的不优雅,于是乎他把所有main.py的代码封装成一个类,并把全部代码写入一个文件中,代码如下
完整后端代码
- face_distances这个值越小说明精度越高,大于这个值就返回
["no match"]
- 识别完放回一个的数组
msg:['Chris]
import face_recognition as fr
import cv2
import numpy as np
import os, base64
from flask import Flask, request, Response, render_template
app=Flask(__name__)
class face:
def __init__(self, train_path):
self.known_names = []
self.known_name_encodings = []
self.train_path = train_path
def init(self):
images = os.listdir(self.train_path)
for _ in images:
image = fr.load_image_file(self.train_path + _)
image_path = self.train_path + _
encoding = fr.face_encodings(image)[0]
self.known_name_encodings.append(encoding)
self.known_names.append(os.path.splitext(os.path.basename(image_path))[0].capitalize())
def add(self, img_path):
image = fr.load_image_file(img_path)
encoding = fr.face_encodings(image)[0]
self.known_name_encodings.append(encoding)
self.known_names.append(os.path.splitext(os.path.basename(img_path))[0].capitalize())
# print(self.known_names)
def compare(self, img_path):
image = img_path
image = cv2.imread(image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
face_locations = fr.face_locations(image)
face_encodings = fr.face_encodings(image, face_locations)
names = []
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
matches = fr.compare_faces(self.known_name_encodings, face_encoding)
name = ""
face_distances = fr.face_distance(self.known_name_encodings, face_encoding)
print(face_distances)
if min(face_distances) > 0.45:
// face_distances这个值越小说明精度越高,大于这个值就返回["no match"]
return ["no match"]
best_match = np.argmin(face_distances)
if matches[best_match]:
name = self.known_names[best_match]
names.append(name)
cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
cv2.rectangle(image, (left, bottom - 15), (right, bottom), (0, 0, 255), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(image, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
print(names)
return names
# cv2.imshow("Result", image)
# cv2.imwrite("./output.jpg", image)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
@app.route('/register/', methods=['POST'])
def register():
global recognition
img = request.files.get('photo') # 从post请求中获取图片数据
username = request.form.get('username', '')
suffix = '.' + img.filename.split('.')[-1] # 获取文件后缀名
basedir = os.path.abspath(os.path.dirname(__file__)) # 获取当前文件路径
photo = '/train/' + username + suffix # 拼接相对路径
img_path = basedir + photo # 拼接图片完整保存路径,时间戳命名文件防止重复
img.save(img_path) # 保存图片
recognition.add(img_path)
return {'msg': 'ok'}
@app.route('/check', methods=['POST'])
def check():
global recognition
src = request.data
data = str(src).split(',')[1][:-3]
img_data = base64.b64decode(data)
with open("./test/temp.png", 'wb') as f:
f.write(img_data)
names = recognition.compare("./test/temp.png")
// 放回一个识别的数组
return {'msg': names}
if __name__ == "__main__":
recognition = face("./train/")
recognition.init()
app.run(host='127.0.0.1', port=3000)
到这一步后端所有的工作也做完啦
演示
登录
我英文名叫Chris,照片就放简历的大头照哈哈哈
识别
成功后提示,接下来操作... (自由发挥)
整个人脸登录已完成,恭喜自己成为CV程序猿哈哈哈
END
- 感想
可能很多小伙伴会说,为什么不自己写一个人脸识别的算法呀,你都根本不懂Python
,这只是调库。我想说对于我们这种小白,我们可以站在巨人的肩膀
上,利用市面上各种开源的库
,来结合自己的需求,完成公司的项目。我们先锻炼自己的编码思维,再对自己感兴趣的领域进行专研。✊✊
- 谢谢大家的支持,希望这篇文章可以帮助到有需要的小伙伴,有各种问题可以评论区留言或者私信我🤞🤞