基于Vue+Flask的PyTorch人脸匹配、识别、对齐的实现

实验目标

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版权协议,转载请附上原文出处链接和本声明。