实验目标
1. 安装PyCharm
2. 实现一个Flask网页 连接数据库 将单词表打印在网页中
3. 再实现一个Flask网页 将人脸识别的内容放入到网页中 具体来说需要包含以下内容:
3.1 判断人脸是否匹配+显示两张图的人脸特征向量
3.2 人脸对齐和人脸识别
实验内容
1. 安装PyCharm
进入官网安装PyCharm
建议安装专业版,因为在创建flask项目比较方便
http://www.jetbrains.com/pycharm/download/#section=windows.
2. 网页展示单词表
2.1 后端
首先将单词表放入MySQL中,后端通过pymysql获取数据,前端从后端获取数据
@app.route('/getallword', methods=['get'])
def getallword():
sql = "SELECT * FROM `map_enword`"
res = appbk_sql.mysql_com(sql)
return jsonify(res)
2.2 Vue中实现
<script setup>
import Axios from 'axios'
import { ref, onMounted } from "vue"
const get_all_words_api = "/api/getallword"
const text = ref("")
onMounted(() => {
Axios.get(get_all_words_api).then((response) => {
text.value = response.data
})
})
</script>
<template>
<div>
<header-box section_class="tile color transparent-white">
<template v-slot:title>
<h1><a><strong>英语词汇表</strong></a></h1>
</template>
<template v-slot:content>
<div>
{{text}}
</div>
</template>
</header-box>
</div>
</template>
<style scoped>
a {
color: #fff;
}
img {
margin: 0 auto;
}
</style>
2.3 结果展示
3. Flask+Vue实现人脸匹配
3.1 人脸匹配的Python文件
import cv2
import torch
from facenet_pytorch import MTCNN, InceptionResnetV1
# 获得人脸特征向量
def load_known_faces(dstImgPath, mtcnn, resnet, device):
aligned = []
knownImg = cv2.imread(dstImgPath) # 读取图片
face = mtcnn(knownImg) # 使用mtcnn检测人脸,返回【人脸数组】
if face is not None:
aligned.append(face[0])
aligned = torch.stack(aligned).to(device)
with torch.no_grad():
known_faces_emb = resnet(aligned).detach().cpu() # 使用resnet模型获取人脸对应的特征向量
return known_faces_emb, knownImg
# 计算人脸特征向量间的欧氏距离,设置阈值,判断是否为同一个人脸
def match_faces(faces_emb, known_faces_emb, threshold):
isExistDst = False
distance = (known_faces_emb[0] - faces_emb[0]).norm().item()
if(distance < threshold):
isExistDst = True
return distance, isExistDst
def match_faces_main(file, func):
if func == "func1":
with open('./static/test1.jpg', 'wb') as f:
f.write(file.read())
return {'url': '/api/static/test1.jpg', 'func': '1', 'data': []}
else:
with open('./static/test2.jpg', 'wb') as f:
f.write(file.read())
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
mtcnn = MTCNN(min_face_size=12, thresholds=[0.2, 0.2, 0.3], keep_all=True, device=device)
resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device)
MatchThreshold = 0.8
known_faces_emb, _ = load_known_faces('./static/test1.jpg', mtcnn, resnet, device) # 已知人物图
faces_emb, img = load_known_faces('./static/test2.jpg', mtcnn, resnet, device) # 待检测人物图
distance, isExistDst = match_faces(faces_emb, known_faces_emb, MatchThreshold) # 人脸匹配
if isExistDst:
str1 = "两张图的余弦距离为 %.2f" % distance
str2 = '由于距离小于设定的阈值%.1f,因此两张图片中的人物匹配' % MatchThreshold
else:
str1 = "两张图的余弦距离为%.2f" % distance
str2 = '由于距离大于设定的阈值 %.1f,因此两张图片中的人物不匹配' % MatchThreshold
s1 = "已知人物特征向量为" + str(known_faces_emb)
s2 = "已知人物特征向量为" + str(faces_emb)
res = {
'url': '/api/static/test2.jpg',
'func': '2',
'match': str(isExistDst),
'data': [str1, str2],
'data2': [s1, s2]
}
return res
3.2 Flask接口定义
@app.route('/matchface', methods=['POST'])
def matchface():
file = request.files.get("img")
func = request.form.get("func")
if file is not None:
res = match_faces_main(file, func)
return jsonify(res)
else:
return '上传文件失败'
3.3 人脸匹配vue页面实现
<script setup>
import Axios from 'axios'
import { reactive, ref } from "vue"
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
const get_img_api = "/api/matchface"
const ImgUrlList = reactive({ data: [] })
const image_is_null = ref(true)
const height = ref(500)
const width = ref(500)
const data = ref([])
const data2 = ref([])
const tzxl = ref(false)
const uploadFile = (formData) => {
$("#loader").fadeIn(300);
$(".mask").fadeIn(300)
Axios.post(get_img_api, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
}).then((response) => {
ImgUrlList.data.push(response.data.url)
image_is_null.value = false
data.value = response.data.data
data2.value = response.data.data2
let func = response.data.func
let match = response.data.match
if (func == '2') {
if (maxqtch == "True") {
ElMessage({
message: `匹配成功`,
type: 'success',
})
}
else {
ElMessage({
message: `匹配失败`,
type: 'error',
})
}
}
$("#loader").fadeOut(300);
$(".mask").fadeOut(300)
})
}
const uploadImg1 = (e) => {
var file = e.target.files[0]
if (!/\.jpg$/.test(e.target.value)) {
ElMessage({
message: `图片类型必须是jpg类型`,
type: 'error',
})
return false
}
let formData = new FormData();
formData.append('img', file);
formData.append('func', 'func1');
ElMessage({
message: `正在收集人物信息`,
type: 'success',
})
ImgUrlList.data = []
uploadFile(formData);
}
const uploadImg2 = (e) => {
var file = e.target.files[0]
if (!/\.jpg$/.test(e.target.value)) {
ElMessage({
message: `图片类型必须是jpg类型`,
type: 'error',
})
return false
}
let formData = new FormData();
formData.append('img', file);
formData.append('func', 'func2');
ElMessage({
message: `正在检测`,
type: 'success',
})
uploadFile(formData);
}
</script>
<template>
<div>
<header-box section_class="tile color transparent-white">
<template v-slot:title>
<h1><a><strong>人脸匹配</strong></a></h1>
</template>
<template v-slot:content>
<div>
<el-button round style="margin-right:30px">
<label class="btn" for="teacherIntroductionVideo1" style="color:#000">上传已知人物jpg图片</label>
</el-button>
<input type="file" style="display:none;" id="teacherIntroductionVideo1" accept="image/jpg" @change="uploadImg1($event)">
<el-button round>
<label class="btn" for="teacherIntroductionVideo2" style="color:#000">上传待检测jpg图片</label>
</el-button>
<input type="file" style="display:none;" id="teacherIntroductionVideo2" accept="image/jpg" @change="uploadImg2($event)">
<el-empty v-show="image_is_null" description="快快输入文字测试一下吧~" />
<div style="margin:10px" v-show="!image_is_null">
<img v-for="(item,index) in ImgUrlList.data" :key="index" :src="item" alt="" :width="width" />
</div>
<div style="margin:10px" v-show="!image_is_null">
<p v-for="(item,index) in data" :key="index" style="font-size:22px">{{item}}</p>
</div>
<div style="margin:10px" v-show="!image_is_null">
<el-button :icon="Search" round @click="tzxl=true">显示特征向量</el-button>
</div>
<div style="margin:10px" v-show="tzxl">
<p v-for="(item,index) in data2" :key="index" style="font-size:15px">{{item}}</p>
</div>
</div>
</template>
</header-box>
</div>
</template>
<style scoped>
a {
color: #fff;
}
img {
margin: 0 auto;
}
</style>
3.4 结果展示
3.4.1 匹配结果
分别输入刘亦菲和刘诗诗的照片会显示:不匹配,符合预期
3.4.2 人脸特征显示
4. Flask+Vue人脸识别和人脸对齐
两种人脸识别算法:
- OpenCV人脸检测
- face_recognition人脸检测
人脸对齐算法:基于Dlib人脸对齐
以上算法可参考我之前写的博客(链接如下):基于OpenCV和Dlib+fr的人脸检测以及基于Dlib人脸对齐_CocoLoveOreo的博客-CSDN博客_人脸检测与对齐
4.1 后端接口定义
@app.route('/OpenCV1', methods=['POST'])
def OpenCV1():
file = request.files.get("img")
func = request.form.get("func")
if file is not None:
if func == "opencv1":
res = opencv1(file)
return jsonify(res)
elif func == "opencv2":
res = opencv2(file)
return jsonify(res)
else:
res = opencv3(file)
return jsonify(res)
else:
return '上传文件失败'
4.2 Vue页面编写
<script setup>
import Axios from 'axios'
import { ref } from "vue"
import { ElMessage } from 'element-plus'
import { Edit } from '@element-plus/icons-vue'
const get_img_api = "/api/OpenCV1"
const image_is_null = ref(true)
const height = ref(500)
const width = ref(500)
const ImgUrl = ref("")
const ImgUrlYuan = ref("")
const func = ref("opencv1")
const python_alert_show = ref(false)
const value = ref([])
const options = [{
value: 'opencv1',
label: 'OpenCV人脸检测',
},
{
value: 'opencv2',
label: 'face_recognition人脸检测',
},
{
value: 'opencv3',
label: '基于Dlib人脸对齐 ',
}]
const change_func = () => {
func.value = value._value[0]
python_alert_show.value = false
}
const uploadFile = (formData) => {
$("#loader").fadeIn(300);
$(".mask").fadeIn(300)
Axios.post(get_img_api, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
}).then((response) => {
ImgUrl.value = response.data.url
ImgUrlYuan.value = "/api/static/test.jpg"
let size = response.data.size
height.value = size[0] / size[1] * 500
image_is_null.value = false
$("#loader").fadeOut(300);
$(".mask").fadeOut(300)
})
}
const uploadImg = (e) => {
var file = e.target.files[0]
if (!/\.jpg$/.test(e.target.value)) {
ElMessage({
message: `图片类型必须是jpg类型`,
type: 'error',
})
return false
}
let formData = new FormData();
formData.append('img', file);
formData.append('func', func.value);
ElMessage({
message: `正在检测`,
type: 'success',
})
uploadFile(formData);
}
</script>
<template>
<div>
<header-box section_class="tile color transparent-white">
<template v-slot:title>
<h1><a><strong>人脸检测</strong></a></h1>
</template>
<template v-slot:content>
<div>
<el-button icon="Search" round>
<label class="btn" for="teacherIntroductionVideo" style="color:#000">上传jpg图片</label>
</el-button>
<el-button type="primary" :icon="Edit" circle @click="python_alert_show=true" />
<input type="file" style="display:none;" id="teacherIntroductionVideo" accept="image/jpg" @change="uploadImg($event)">
<el-empty v-show="image_is_null" description="快快输入文字测试一下吧~" />
<div style="margin:10px">
<img v-show="!image_is_null" :src="ImgUrlYuan" alt="" :width="width" :height="height" />
<img v-show="!image_is_null" :src="ImgUrl" alt="" :width="width" :height="height" />
</div>
</div>
</template>
</header-box>
<el-dialog v-model="python_alert_show" title="请选择人脸识别算法" width="30%">
<el-cascader v-model="value" :options="options" />
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="change_func">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style scoped>
a {
color: #fff;
}
img {
margin: 0 auto;
}
</style>
4.3 Vue页面结果展示
基于face_recognition的人脸检测:
基于Dlib的人脸对齐:
版权声明:本文为acccis原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。