利用django实现在线人脸识别

发布于:2022-11-13 ⋅ 阅读:(752) ⋅ 点赞:(0)

利用课余时间写了一个基于的django图像处理例子,数据库是mysql,很多代码还不成熟,关于django的使用主要是在视频:2022 B站最详细django3教程(django从入门到实践)_哔哩哔哩_bilibili 上进行的。我认为这是最好的django学习视频了。

一、项目简介

1、项目包括:

(1)用户登录,注册

(2)上传npz数据集

(3)对数据集基于sklearn交叉验证或数据集分割后验证

(4)上传符合数据集标准的样本进行识别

(5)利用摄像头采集人脸信息

(6)利用dlib进行人脸识别(比较慢,有待优化)

2、例子涉及知识:

  1. django
  2. bootstrap
  3. dlib人脸识别
  4. sklearn
  5. 摄像头视频采集
  6. echarts (这个对项目没什么用,纯练习)

3、参考文档:

[1] js开启摄像头_安和同学的博客-CSDN博客_js开启摄像头

[2] Python 3 利用 Dlib 实现摄像头实时人脸识别 - coneypo - 博客园

[3] web调用摄像头拍照并上传到服务器_淋雨一直走~的博客-CSDN博客_摄像头拍照上传服务器

[4] Detecting Face Features with Python

参考了很多文档,这里未一一列出,感谢各位大佬的辛苦付出。

本文源码:https://github.com/huangzhiyu2018/OnlineAiPicture

4、项目截图:

 

 

 

 

 

 

 

 

 

为了总结过去的经验,列出主要操作

二、html模板嵌套

1、首先定义一个母版,在里面留出适当的block,例如:

{% load static %}

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.css' %}">

    <!--额外css填入位置-->

    {% block css %} {% endblock %}

    <title>      

        {% block title %} {% endblock %}

    </title>

</head>

<body>

    <div class="container"> 

            <!--内容填入位置-->            

                {% block contain %} {% endblock %}           

        </div>

    <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->

    <script src="{%static 'js/jquery-3.6.0.min.js' %}"></script>

    <!-- 最新的 Bootstrap4 核心 JavaScript 文件 -->

    <script src="{%static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.js'%}"></script>

    <!--额外js填入位置-->

    {% block js %} {% endblock %}

</body>

</html>

2、其它模板可以引用这个母版,达到所有模板都具有相同的布局:

{% extends 'master.html' %}

{% block contain %}

写入当前模板需要的内容

{% endblock %}

3、继续继承

更有意思的是还可以上(2)基础上进一步进行嵌套,再次嵌套时需要在(2)的文档中也设定响应的block,其过程与第一次嵌套一样。

三、人脸采集

人脸采集时控制摄像头,并把摄像头采集到的图片上传到服务器,基本思路,先打开摄像头,然后拍照,最后ajax提交到后台进行保存。页面布局以及功能如下:

 

正在上传…重新上传取消

1、打开摄像头

前台有<video id="video" ></video>标签,利用javascript打开,这是参考的博客:https://blog.csdn.net/amdd9582/article/details/95501402

function playfunction(){

        // 开启摄像

        document.getElementById('play').onclick = () => {

            let constraints = {

                // video属性设置

                video: {

                    width: 300,

                    height: 300

                },

                // audio属性设置,无声

                audio: false

            }

            navigator.mediaDevices.getUserMedia(constraints)

            .then(mediaStream => {

                // 成功返回promise对象,接收一个mediaStream参数与video标签进行对接

                document.getElementById('video').srcObject = mediaStream

                document.getElementById('video').play()

                mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[0];    

                click_start_vedio=1;//记录开启视频

            })

            // 失败就失败了

        }

    }

为了能够关闭摄像头,我定义了一个全局变量:mediaStreamTrack

2、拍照

点击id为take的按钮,进行拍照,

function photofunction(){

        // 拍照、canvas绘制        

                document.getElementById('take').onclick = () => {

                    if (click_start_vedio==1){

                        let ctx = document.getElementById("canvas").getContext('2d')

                        ctx.drawImage(document.getElementById("video"), 0, 0, 300, 300)        

                        click_scrap=1;

                        $("#img_result").removeAttr("src");//每次拍照后页面识别结果都清空

                    }else{

                        alert("请点击开始摄像按钮");

                    }               

             }        

   }

3、关闭摄像头

利用全局变量mediaStreamTrack进行关闭摄像头

    function closevideofunction(){

        $("#close").on({

            "click":function(){              

                if(mediaStreamTrack){

                    mediaStreamTrack.stop();

                    mediaStreamTrack=[];

                    console.log("stop");                    

                    click_start_vedio=0;

                }else{

                    console.log("mediaStreamTrack is empty")

                }

                 

            }

        })

    }

4、异步上传

需要把canvas的内容先转成字符流,再把字符流转成文件进行上传,两个函数:

function dataURItoBlob(base64Data) {

        var byteString;

        if (base64Data.split(',')[0].indexOf('base64') >= 0)

            byteString = atob(base64Data.split(',')[1]);

        else

            byteString = unescape(base64Data.split(',')[1]);

        var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];

        var ia = new Uint8Array(byteString.length);

        for (var i = 0; i < byteString.length; i++) {

            ia[i] = byteString.charCodeAt(i);

        }

        return new Blob([ia], {type: mimeString});

    };

    //blob转换为file

    function blobToFile(theBlob, fileName) {

        theBlob.lastModifiedDate = new Date();

        theBlob.name = fileName;

        return theBlob;

    };

由于后台是利用modelform进行处理的,上传的图片和姓名信息直接存入到了mysql数据库,这里异步时把相关信息都携带了,代码中为防止无视频直接上传,用标志变量来确定是否已经点击了拍照按钮。代码如下:

 function uploadfunction(){

    $("#upload").on({

        "click":function(){  

            if(click_start_vedio==0){

                alert("首先点击开启摄像,然后点击拍照");

                return;

            }

            var personName=$("#person_name").val();

            if (personName){

                if(click_scrap==1){

                click_scrap=0

                canvas = document.getElementById('canvas');

                   

                //生成图片格式base64包括:jpgpng格式

                var base64Data = canvas.toDataURL("image/jpeg", 1.0);               

                //封装blob对象

                var blob = dataURItoBlob(base64Data);   

                var file = blobToFile(blob, personName);   

                //利用FormData进行传值

                var form = document.getElementById("controlform");               

              // 用表单来初始化

                var formdata = new FormData(form);          

                formdata.append("person_name",personName);

                //必须是file字段的添加,防止汉语名称不识别,这里用英文名称

                formdata.append("file",blob,"person.jpg");                      

                $.ajax({

                    url: ajaxurl,

                    type:"post",

                    data:formdata,

                    processData: false,//用于对data参数进行序列化处理 这里必须false

                    contentType: false, //必须

                    success:function(res){

                      if(res.status){

                        var valide="图片中没有脸部信息"

                        if (res.valide){

                            valide="图片有效"

                        }

                        $("#pic_result").html("文件:"+res.path+"上传成功!<br>"+valide)

                        $("#img_result").attr("src","/media/"+res.path)

                        if (show_result_alert){//这是为了图像识别显示对话框用的

                            if(res.valide)

                            {

                                alert("当前人物为:"+res.person+"\n 可能人物:"+res.min_person)

                            }else{

                                alert(valide);

                            }

                        }

                      }else{                    

                        console.log(res.errors)

                      }

   

                    }

         

                  }

                  )

                }else{

                    alert("必须点击拍照!");

                }

            }else{

                alert("必须输入姓名")

            }          

        },

    })    

   }

5、采集人脸和人脸识别的模板

整个模板仍然是人脸采集和人脸识别模板的母版,里面包括了被人脸识别的两个block

{% block table %}    {% endblock %} {% block exjs%}{% endblock %}

文件名为:scrapture.html其完整代码如下:

{% extends 'master.html' %}

{% block css %}

    <style>    

        video, canvas {

            width: 300px;

            height: 300px;

            border: 5px solid #000;

            border-radius: 10px;

            margin-left: 5px;

        }

    </style>

{% endblock %}

{% block contain %}

<div class="container">

    <div class="panel panel-primary">

        <div class="panel-heading">

          <h3 class="panel-title">采集人脸图像</h3>

        </div>

        <div class="panel-body" >

            <div class="panel panel-default" style="margin:10px 10px">

                <div class="panel-heading">

                  <h3 class="panel-title">采集面部</h3>

                </div>

                <div class="panel-body">

                    <div class="col-md-6">                    

                        <video id="video" ></video>

                     </div>

                     <!-- 尽量在canvas标签上设置宽高 -->

                     <div class="col-md-6">

                       <canvas id="canvas" width="300px" height="300px"></canvas>

                     </div>

                </div>

            </div>

           

             <div class="col-md-6">  

                    <div class="panel panel-default" >

                        <div class="panel-heading">

                           <h3 class="panel-title">控制面板</h3>

                        </div>

                        <div class="panel-body" style="margin:10px 10px">                            

                           <button id="play" class="btn btn-success" >开启摄像</button>                        

                           <button id="close" class="btn btn-danger " >关闭视频</button>                              

                            <form method="post" novalidate id="controlform" enctype="multipart/form-data">

                                <div class="form-group" id="input_person">                        

                                    <label class="col-sm-2" for="person_name">人物姓名:</label>

                                    <input type="text" class="col-sm-2"  id="person_name" placeholder="Tom" value="第一人" >                                                  

                                </div>

                                <div class="form-group">                            

                                    <input id="take" class="btn btn-success col-sm-2" value="拍照"></input>

                                    <input id="upload" class="btn btn-info col-sm-2" value="采集"></input>                              

                                   

                                   

                                </div>

                            </form>

                           

                           

                        </div>

                    </div>

               

                </div>      

             <!--加上一个处理结果显示情况-->

             <div class="col-md-6" id="result_panel">  

                <div class="panel panel-default" style="margin:10px 10px">

                    <div class="panel-heading">

                    <h3 class="panel-title">采集结果</h3>

                    </div>

                    <div class="panel-body" style="margin:10px 10px">

                    <img style="height:300px" id="img_result"></img>

                    </div>

                </div>

            </div>  

            </div>        

        </div>

        <div class="panel-footer" id="footer">

            <p class="list-group-item-text" id="pic_result"></p>

            <a href="/facerecon/list/" style="align:right">采集结果列表 </a>

        </div>      

    </div>

    {% block table %}

    {% endblock %}

</div>  

{% endblock %}

{% block js %}

<script type="text/javascript">

    var click_start_vedio=0;

    var click_scrap=0;

    var ajaxurl;

    var show_result_alert=0;

     var  mediaStreamTrack;

     // 获取媒体方法(旧方法)

    function closevideofunction(){

        $("#close").on({

            "click":function(){              

                if(mediaStreamTrack){

                    mediaStreamTrack.stop();

                    mediaStreamTrack=[];

                    console.log("stop");                    

                    click_start_vedio=0;

                }else{

                    console.log("mediaStreamTrack is empty")

                }

                 

            }

        })

    }

    function playfunction(){

        // 开启摄像

        document.getElementById('play').onclick = () => {

            let constraints = {

                // video属性设置

                video: {

                    width: 300,

                    height: 300

                },

                // audio属性设置,无声

                audio: false

            }

            navigator.mediaDevices.getUserMedia(constraints)

            .then(mediaStream => {

                // 成功返回promise对象,接收一个mediaStream参数与video标签进行对接

                document.getElementById('video').srcObject = mediaStream

                document.getElementById('video').play()

                mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[0];    

                click_start_vedio=1;//记录开启视频

            })

            // 失败就失败了

        }

    }

   function photofunction(){

        // 拍照、canvas绘制        

                document.getElementById('take').onclick = () => {

                    if (click_start_vedio==1){

                        let ctx = document.getElementById("canvas").getContext('2d')

                        ctx.drawImage(document.getElementById("video"), 0, 0, 300, 300)        

                        click_scrap=1;

                        $("#img_result").removeAttr("src");

                    }else{

                        alert("请点击开始摄像按钮");

                    }

               

             }

       

   }

   

   function dataURItoBlob(base64Data) {

        var byteString;

        if (base64Data.split(',')[0].indexOf('base64') >= 0)

            byteString = atob(base64Data.split(',')[1]);

        else

            byteString = unescape(base64Data.split(',')[1]);

        var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];

        var ia = new Uint8Array(byteString.length);

        for (var i = 0; i < byteString.length; i++) {

            ia[i] = byteString.charCodeAt(i);

        }

        return new Blob([ia], {type: mimeString});

    };

    //blob转换为file

    function blobToFile(theBlob, fileName) {

        theBlob.lastModifiedDate = new Date();

        theBlob.name = fileName;

        return theBlob;

    };

   function uploadfunction(){

    $("#upload").on({

        "click":function(){  

            if(click_start_vedio==0){

                alert("首先点击开启摄像,然后点击拍照");

                return;

            }

            var personName=$("#person_name").val();

            if (personName){

                if(click_scrap==1){

                click_scrap=0

                canvas = document.getElementById('canvas');

                   

                //生成图片格式base64包括:jpgpng格式

                var base64Data = canvas.toDataURL("image/jpeg", 1.0);

               

                //封装blob对象

                var blob = dataURItoBlob(base64Data);

   

                var file = blobToFile(blob, personName);

   

                //利用FormData进行传值

                var form = document.getElementById("controlform");

               

              // 用表单来初始化

                var formdata = new FormData(form);          

                formdata.append("person_name",personName);

                //必须是file字段的添加,防止汉语名称不识别,这里用英文名称

                formdata.append("file",blob,"person.jpg");  

                   

                //没有问题            

                $.ajax({

                    url: ajaxurl,

                    type:"post",

                    data:formdata,

                    processData: false,//用于对data参数进行序列化处理 这里必须false

                    contentType: false, //必须

                    success:function(res){

                      if(res.status){

                        var valide="图片中没有脸部信息"

                        if (res.valide){

                            valide="图片有效"

                        }

                        $("#pic_result").html("文件:"+res.path+"上传成功!<br>"+valide)

                        //console.log(res.path)

                        $("#img_result").attr("src","/media/"+res.path)

                        if (show_result_alert){

                            if(res.valide)

                            {

                                alert("当前人物为:"+res.person+"\n 可能人物:"+res.min_person)

                            }else{

                                alert(valide);

                            }

                        }

                      }else{                    

                        console.log(res.errors)

                      }

   

                    }

         

                  }

                  )

                }else{

                    alert("必须点击拍照!");

                }

               

            }else{

                alert("必须输入姓名")

            }          

             

           

        },

    })    

   }

   

   

</script>

{% block exjs%}

{% endblock %}

{% endblock %}

6、人脸采集模板

人脸采集模板是继承了上面的模板,只是更改了提交按钮的post地址

{% extends 'scrapture.html' %}

{% block exjs%}

<script>

    $(document).ready(function(){

        playfunction();

        photofunction();

        uploadfunction();  

        initparams();    

    })

    function initparams(){

        //$("#input_person").hide()

        ajaxurl="/facerecon/scrap/"

    }

</script>

{% endblock%}

7、人脸采集数据模型

a) 采集人脸文件上传到后台media/persons路径,信息存入到数据库,数据库model如下:

class PersonInfor(models.Model):

    """动态上传人员信息表

    Args:

        models (_type_): _description_

    """      

    #上传的文件路径,在persons路径

    file=models.FileField(verbose_name="文件",max_length=125,upload_to="persons/",default="")  

    #图片对应的人名

    person_name=models.CharField(max_length=30,verbose_name='姓名',default="Tom")    

    #上传时间

    create_time = models.DateField(verbose_name='上传时间')

    #采集者

    upload_user=models.CharField(max_length=30,verbose_name='上传者')

    #是否是有效的人物图片,只有一张图片人脸的是有效的

    valide_choice = ((0, ""), (1, ""))

    isvalide=models.SmallIntegerField(choices=valide_choice,verbose_name="有效",default=0)

    #保存到一个表中,去掉一个表,允许为空

    rects=models.TextField(verbose_name='面部位置',blank=True)

    landmarks=models.TextField(verbose_name='面部标注',blank=True)

    #每个rect对应一个面部描述,由dlib_face_recognition_resnet_model_v1.dat模型获得

    descriptions=models.TextField(verbose_name='面部描述',blank=True)

    #显示信息用

    def __str__(self):

        return f"姓名:{self.person_name} 文件:{self.file}"

8、view中的人脸采集模型

首先构建一个modelform类

class PersonInforModelForm(BootrapModelForm):

    """针对上传人物图片的模型

    Args:

        BootrapModelForm (_type_): _description_

    """

    class Meta:

        model=models.PersonInfor        

        exclude=["upload_user","create_time","isvalide","rects","landmarks","descriptions"]

        bootstrap_exclude_fileds=["file"]  

    def clean_file(self):

        file = self.cleaned_data['file']

        ext = file.name.split('.')[-1].lower()

        if ext not in ["bmp","jpg","png"]:

            raise forms.ValidationError("仅支持bmp,jpg,png 类型文件")

        return file

9、应用bootstrap的modelform

这里的BootrapModelForm是按照参考视频的写法写的一个能够应用bootstrap的类,定义如下:

from django import forms

class Bootstrap:

    bootstrap_exclude_fileds=[]

    def __init__(self,*args,**kwargs):

        super().__init__(*args,**kwargs)        

        for name,field in self.fields.items():

            #不需要bootstrap的字段

            if name in self.bootstrap_exclude_fileds:

                continue

            if  field.widget.attrs:

                field.widget.attrs['class']='form-control'

                field.widget.attrs['placeholder']=field.label

            else:

                field.widget.attrs={"class":"form-control",'placeholder':field.label}            

class BootrapModelForm(Bootstrap,forms.ModelForm):

    pass

class BootrapForm(Bootstrap,forms.Form):

    pass

   

10、后台处理

其次在定义好modelform类之后,可以直接读取前段异步到后台的post内容,form= PersonInforModelForm(request.POST,request.FILES)完整的处理函数如下,代码中dlibcompute是自定义的利用dlib处理图片的模块,请参考源码

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt

def scrapy_image(request):

    if request.method=="POST":

        #传入信息

        form= PersonInforModelForm(request.POST,request.FILES)  

             

        if form.is_valid():

           

            form.instance.create_time=datetime.datetime.now()

            #form.instance.upload_user=request.session.info.userName    

            info=request.session.get("info")

            if info:                

                form.instance.upload_user=info["userName"]

            else:

                form.instance.upload_user="test"                

           

             #先保存,否则没有图片

            form.save()

            #得到图片绝对路径          

            absolute_file_path = os.path.join('media',form.instance.file.name)            

            #print(form.instance.file.name)

            #print(absolute_file_path)

            #得到人脸信息

            rects,image=dlibcompute.find_person_rect(absolute_file_path)  

           

            #讲数据转成字符序列

            s=pickle.dumps(rects)

            #print(len(rects))        

            isValide=0

            if len(rects)==1:

                isValide=1

            models.PersonInfor.objects.filter(file=form.instance.file.name).update(isvalide=isValide,rects=s)

           

            return JsonResponse({"status":True,"path":form.instance.file.name,"valide":isValide})

        else:            

            return JsonResponse({"status":False,"errors":form.errors})

               

       

   

    return render(request,"scraperimage.html")

四、人脸识别

人脸采集的思路是:从摄像头采集图片,对图片得到图片描述信息,把描述信息与存放到数据库中的采集图片信息进行欧式距离计算,设定一个阈值,如果小于这个值就是当前人物,记录最小的距离,这是可能的人物。

1、人脸识别模板

打开、拍照、关闭摄像头都是利用上节5中的scrapture.html文件,在人脸识别模板去掉人物姓名项,利用js隐藏响应的模块即可。在识别模块下发加入已经采集的图片信息,并更换提交按钮的url,以便后台采用不同的处理方式。模板代码如下:

{% extends 'scrapture.html' %}

{% block table %}

<div class="panel panel-default" style="margin:10px 10px">

    <div class="panel-heading" >

        <span class="glyphicon glyphicon-th-large" aria-hidden="true"></span>人物信息

    </div>

    <div class="panel-body">

   

        <table class="table table-bordered">                      

            <thead>

              <tr>

                <th>ID</th>

                <th姓名</th>                

                <th>图片</th>    

                <th>姓名</th>

                <th>上传时间</th>

                <th>是否有效</th>              

              </tr>

            </thead>

            <tbody>

            {% for obj in querySet%}

              <tr>

                <th scope="row">{{ obj.id }}</th>

                <td><img src="/media/{{obj.file}}" style="height:100px"></img></td>  

                <td>{{obj.person_name}}</td>                

                <td>{{ obj.create_time |date:"Ymd"}}</td>  

                <td>{{ obj.get_isvalide_display }}</td>

               

              </tr>

             {% endfor %}

            </tbody>

          </table>

    </div>

  </div>

{% endblock %}

{% block exjs%}

<script>

    $(document).ready(function(){

        playfunction();

        photofunction();

        uploadfunction();  

        initparams();    

       

        closevideofunction();

    })

    function initparams(){

        show_result_alert=1;

        $("#input_person").hide();

        $("#input_person").val("test");//用测试的名字进行        

        $("#result_panel").hide();

        //$("#footer").hide();//隐藏,用对话框提示有效性

        $("#upload").val("识别");

        ajaxurl="/facerecon/recon/";

    }

</script>

{% endblock%}

2、识别后台代码

后台识别时,首先查看当前上传的图片是否有人脸信息,如果有再看原来人脸采集数据表中人脸描述字段是否已经计算,如果没有计算就重新计算原采集人脸的描述信息;如果采集的人脸都已经计算描述,就比较欧氏距离。

@csrf_exempt

def face_recon(request):

    """人脸识别

    Args:

        request (_type_): _description_

    """

    if request.method=="GET":

        #只显示有效信息

        querySet = models.PersonInfor.objects.filter(isvalide=1)

        return render(request,"personrecon.html",{"querySet":querySet})

    #提交图片后进行识别

    form= PersonInforModelForm(request.POST,request.FILES)  

    if form.is_valid():

        file = request.FILES.get("file")  

        #返回两个路径一个用于显示,一个用于读取

        url_file_name,absolute_file_path = handle_uploaded_file(file)

       

        #print(url_file_name,absolute_file_path)

       

        rects,image=dlibcompute.find_person_rect(absolute_file_path)  

       

        #print(len(rects))

       

        if len(rects)!=1:

            return JsonResponse({"status":True,"path":url_file_name,"valide":0,"person":"无法识别"})

       

        #合适的人脸数据信息,对人脸数据进行读取

        baseDir=os.path.join(settings.BASE_DIR,r"picturerec\utils")

       

        detector=dlibcompute.get_front_face_dector()

        landmarks_predictor=dlibcompute.get_point_5_infor(baseDir)

        face_encoder=dlibcompute.get_face_encodeing(baseDir)  

         

        #print("*"*10)

       

        #dlibcompute.face_encodings

        #先把所有在人物信息表中的数据进行计算,如果计算过了就不必计算了

        compute_person_description(detector=detector,landmarks_predictor=landmarks_predictor,face_encoder=face_encoder)

        #计算当前照片的的描述和标志点

        descriptions,_=dlibcompute.face_dector_encodings(image,detector=detector,landmarks_predictor=landmarks_predictor,face_encoder=face_encoder)        

        findperson,min_person=find_person(descriptions)        

        #有效的提交进行图片处理

        return JsonResponse({"status":True,"path":url_file_name,"valide":1,"person":findperson,"min_person":min_person})    

    else:            

        return JsonResponse({"status":False,"errors":form.errors})  

3、计算原采集人脸的描述信息

def compute_person_description(detector,landmarks_predictor,face_encoder):

    #默认是blank,判断用""

    querySet = models.PersonInfor.objects.filter(descriptions="",isvalide=1)

   

    #print("query sets lin 148=",len(querySet))    

    for temp in querySet:

        #对于所有没有计算描述的图片计算描述信息,已经计算的不用

        absolute_file_path = os.path.join('media', str(temp.file))

        description,landmarks=dlibcompute.face_encoding_by_file(absolute_file_path,detector,landmarks_predictor,face_encoder)  

        if description is None:

            continue    

        s_description=pickle.dumps(description)

        s_landmarks=pickle.dumps(landmarks)

        models.PersonInfor.objects.filter(id=temp.id).update(landmarks=s_landmarks,descriptions=s_description)

这里注意的是,存入采集数据库的描述信息是以二进制形式保存的numpy.ndarray,存入时利用pickle.dumps进行的。

4、识别当前人是人脸采集中的哪一位

PersonInfor中存放有当前采集人脸图片的描述信息,这些信息是numpy.ndarray形式,是利用pickle.dumps存入,读取时,我采用的方法是,首先利用ast.literal_eval转成数组然后利用pickle.loads转成ndarray,

L1=ast.literal_eval(temp.descriptions)        

np_description=pickle.loads(L1)

函数find_person是根据当前识别图片的描述与数据库中存放的描述信息进行对比,然后找到当前人是哪一个如:

def find_person(temp_description):

    if temp_description is None:

        print("temp_description is None")

        return "temp_description is None"

    if len(temp_description)==0:

        print("temp_description is empty")

       

    querySet = models.PersonInfor.objects.filter(isvalide=1,descriptions__isnull=False)#得到数据集,必须要求描述不空

    thread=0.4

    find_person=""

    min_value_person=""

    min_value=10000

    for temp in querySet:

        #record_description=(temp.descriptions)

        L1=ast.literal_eval(temp.descriptions)        

        np_description=pickle.loads(L1)

        if len(np_description)==0:

            print(f"description is zero {temp.id}:{temp.person_name}")            

            continue

        print(len(np_description[0]))

        d=distancecompute.Euclidean_distance(np_description,temp_description)

        if d<min_value:

            min_value=d

            #搜索

            min_value_person=f"{temp.person_name}"

            if d<thread:

                find_person=f"{temp.person_name}"

       

           

    return find_person,min_value_person

五、人脸定位与描述

1、人脸位置监测

利用dlib进行人脸定位和人脸信息的描述,利用dlib可以快速的对人脸进行定位,并根据适当的模型得到人脸上的标志点。

detector = dlib.get_frontal_face_detector()

返回人脸监测器,给监测器传入图片信息可以得到当前图片的人脸位置信息,这些信息是多个矩形信息,图片中有几张脸就有几个矩形,矩形是左上角和右下角坐标存储的。

face_locations = detector(face_image, number_of_times_to_upsample)

2、标志位的获得

根据得到人脸位置,可以利用dlib.shape_predictor产生的预测器得到人脸标志点,dlib.shape_predictor需要传入训练好的模型,这个模型有两种,一种是5个标志点,对应文件是shape_predictor_5_face_landmarks.dat,另一种是68个点,对应文件是shape_predictor_68_face_landmarks.dat,这68个点排列位置时固定的,如图

 

正在上传…重新上传取消

如果模型文件路径为shape_5_file,那么参数预测的代码如下:

pose_predictor_5_point = dlib.shape_predictor(shape_5_file)

得到预测器,传入图片和detector得到的人脸位置,就可以得到人脸的标志点坐标,这个坐标是相对于图片的,左上角为0,0,向右,下为正对应横纵坐标。

如果图片face_image所有脸位置是face_locations,下面landmarks_predictor标志位的预测器,下列代码得到所有脸(这是一张图)的标志位。

raw_landmarks = [landmarks_predictor(face_image, face_location) for face_location in face_locations]#

3、描述向量的获得

利用dlib.face_recognition_model_v1可以得到人脸描述器,需要传入模型,这里用的是dlib_face_recognition_resnet_model_v1.dat,把文件名传入后得到描述器,调用描述器的compute_face_descriptor函数可以得到图像的描述信息,这个信息是128个元素的ndarray数字,compute_face_descriptor传入参数是图像,标志位向量。

项目中dlibcompute模块中函数就是计算当前图片的所有描述信息,并以ndarray形式返回。

def face_encodings(face_image,face_locations,landmarks_predictor, face_encoder, num_jitters=1):

    """返回图像中每个人脸的 128D 描述符"""

    if landmarks_predictor and face_encoder:       

        # 检测面部特征点

        raw_landmarks = [landmarks_predictor(face_image, face_location) for face_location in face_locations]

        # 使用每个检测到的特征点计算每个检测到的人脸的编码,返回numpy的列表,方便转换

        return np.array([np.array(face_encoder.compute_face_descriptor(face_image, raw_landmark_set, num_jitters)) for

                raw_landmark_set in raw_landmarks]),np.array(raw_landmarks)

       

    else:

        return None,None


网站公告

今日签到

点亮在社区的每一天
去签到