html5与EmguCV前后端实现——人脸识别篇(一)

  上个月因为出差的关系,断更了很久,为了补偿大家长久的等待,送上一个新的系列,之前几个系列也会抽空继续更新。

  大概半年多前吧,因为工作需要,我开始研究图像识别技术。OpenCV在这方面已经有了很多技术积累,在html5领域也很早就有了这方面的Demo。但是一番学习下来,我发现基本上这方面的文章大都比较零散片面,而且很多关键的代码可能已经老化不能正常使用了。所以这个系列的文章中,我将对html5与EmguCV的整体开发过程做一个整理,逐步介绍怎么使用html5技术和EmguCV类库实现各种看上去高大上的识别技术。

  本文,我会以人脸识别为例,引入html+EmguCV的基本架构(如下图)。

  

  前端没有问题,在浏览器中用html5技术调用摄像头,使用video和canvas标签配合各种dom进行渲染。值得一提的是,因为这里有大量的图像数据交互传递,所以需要建立websocket来与后端服务器进行交互。

    后端的话,其实我开始使用的是PHP技术,但是发现openCV的安装略纠结,于是乎转投微软阵营。这里我使用了framework4.5+EmguCV,微软在frameworks4.5中已经集成了websocket的服务端套字,我们可以很方便地使用它,差不多就和之前版本中写Ajax的处理文件一样方便。关于EmguCV,其实就是OpenCV在c#中的再封装,可以访问OpenCV相关网站获取更多信息。

  接下来,我们快速地浏览下关键代码。

html部分:

<div>
       <div id=‘frame‘ style="position:relative;">
           <video style=‘position:absolute;top:0px;left:0px;z-index:2;‘ id="live" width="320" height="240" autoplay ></video>
           <canvas style=‘position:absolute;top:242px;left:0px; z-index:170;‘ width="320" id="canvasFace" height="240" ></canvas>
           <canvas style=‘position:absolute;top:242px;left:0px; z-index:11;‘   width="320" id="canvas" height="240" ></canvas>
         </div>
</div>

  这里主要起作用的DOM是1个video标签和2个Canvas标签。Video标签主要用来获取摄像头的数据流,两个Canvas标签分别用来绘制Video中的内容和计算出来的头像的位置。

Javascript部分:

 1 $(function(){
 2     var video = $(‘#live‘).get()[0],
 3     canvas = $(‘#canvas‘),
 4     ctx=canvas.get()[0].getContext(‘2d‘),
 5     canvasFace =$(‘#canvasFace‘),
 6     ctx2= canvasFace.get()[0].getContext(‘2d‘),
 7     canSend=true;
 8
 9     ctx2.strokeStyle="#EEEE00";
10     ctx2.fillStyle=‘rgba(0,0,0,0.0)‘;
11     ctx2.lineWidth=3;
12
13     navigator.webkitGetUserMedia({ "video": true },function(stream){
14         video.src = webkitURL.createObjectURL(stream);
15         startWS();
16     },function(err){
17         console.log(‘err‘);
18     });
19
20     //x,y,w,h
21     var _draw =function(pArr){
22         var _obj = $.fromJson(pArr);
23
24         ctx2.clearRect(0,0,320,240);
25
26         if($.isArray(_obj)){
27             for(var i=0,l=_obj.length;i<l;i++ ){
28                 ctx2.strokeRect(_obj[i].X,_obj[i].Y,_obj[i].W,_obj[i].H);
29             }
30         }
31     };
32
33     var startWS=function(){
34         var ws = new WebSocket("ws://10.168.1.1/Cloud/WSHandler.ashx");
35         ws.onopen = function(){
36             console.log(‘Opened WS!‘);
37         };
38         ws.onmessage=function(msg){
39             _draw(msg.data);
40             canSend = true;
41         };
42         ws.onclose=function(msg){
43             console.log(‘socket close!‘);
44         };
45         var timer = setInterval(function(){
46             ctx.drawImage(video,0,0,320,240);
47             if(ws.readyState == WebSocket.OPEN && canSend){
48                 canSend = false;
49                 var data =canvas.get()[0].toDataURL(‘image/jpeg‘,1.0),
50                 newblob = dataURItoBlob(data);
51                 ws.send(newblob);
52             }
53         },60);
54     };
55 });

  这段JS代码中,大家需要注意替换ws文件的地址。至于Canvas绘图,websocket,Camera调用等细节,在后续文章中会有详解。

  可以看到websocket在向服务器提交数据时候,需要对DataURL的数据进行封装,下面就附上这个函数(与早期版本不同)。

dataURItoBlob函数:

 1 function dataURItoBlob(dataURI) {
 2     var byteString = atob(dataURI.split(‘,‘)[1]),
 3             mimeString = dataURI.split(‘,‘)[0].split(‘:‘)[1].split(‘;‘)[0],
 4             ab = new ArrayBuffer(byteString.length),
 5                ia = new Uint8Array(ab);
 6     for (var i = 0; i < byteString.length; i++) {
 7                 ia[i] = byteString.charCodeAt(i);
 8         }
 9         return new Blob([ab],{type: mimeString});
10 }

  前端的代码大致就这样了,后端Coding前,先大致说下怎么部署EmguCV。假设我们把解压好的EmguCV文件夹拷贝到了C盘,那么环境变量Path为C:\Emgu\emgucv-windows-universal-cuda 2.9.0.1922\bin;在新建项目的时候,还需要把用到的DLL等文件拷贝到项目的输出目录。

后端代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;
using Emgu.CV.CvEnum;
using Emgu.CV.GPU;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace Cloud
{
    public class WSHandler : IHttpHandler
    {
        private static HaarCascade haar;
        private static string hasLocation;
        private static string phy;
        private int _maxBufferSize = 256 * 1024;

        public void ProcessRequest(HttpContext context)
        {
            if (context.IsWebSocketRequest)
            {
                phy = context.Request.PhysicalApplicationPath;
                hasLocation = context.Request.PhysicalApplicationPath + "haarcascade_frontalface_alt2.xml";
                context.AcceptWebSocketRequest(ProcessWSChat);
            }
        }

        private async Task ProcessWSChat(AspNetWebSocketContext context)
        {
            try
            {
                WebSocket socket = context.WebSocket;
                haar = new HaarCascade(hasLocation);

                byte[] receiveBuffer = new byte[_maxBufferSize];
                ArraySegment<byte> buffer = new ArraySegment<byte>(receiveBuffer);

                while (socket.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None); //.ConfigureAwait(continueOnCapturedContext: false);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await socket.CloseAsync(
                            result.CloseStatus.GetValueOrDefault(),
                            result.CloseStatusDescription,
                            CancellationToken.None);
                        break;
                    }

                    int offset = result.Count;

                    while (result.EndOfMessage == false)
                    {
                        result = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer, offset, _maxBufferSize - offset), CancellationToken.None);
                        offset += result.Count;
                    }

                    if (result.MessageType == WebSocketMessageType.Binary && offset!=0)
                    {

                        ArraySegment<byte> newbuff = new ArraySegment<byte>(Encoding.UTF8.GetBytes(FaceDetection(receiveBuffer, offset)));
                        await socket.SendAsync(newbuff, WebSocketMessageType.Text, true, CancellationToken.None);

                    }                    

                }
            }
            catch (Exception e) {
                var err = e.Message;
            }
        }

        private static string FaceDetection(byte[] data,int plength)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("[");

            Image<Bgr, byte> nextFrame = new Image<Bgr, byte>(ByteToBitmap(data, plength));

            if (nextFrame != null)
            {
                Image<Gray, Byte> grayframe = nextFrame.Convert<Gray, Byte>();
                var faces = grayframe.DetectHaarCascade(
                                haar, 1.4, 4,
                                HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
                                new Size(nextFrame.Width / 8, nextFrame.Height / 8)
                                )[0];

                foreach (var face in faces)
                {
                    sb.AppendFormat("{{X:{0},Y:{1},W:{2},H:{3}}},",face.rect.X, face.rect.Y,face.rect.Width,face.rect.Height);
                }

                if (sb[sb.Length - 1] == ‘,‘) {
                    sb.Remove(sb.Length-1,1);
                }
            }

            sb.Append("]");

            return sb.ToString();
        }

        private int _ii = 0;

        private static byte[] BitmapToByte(Bitmap b)
        {
            MemoryStream ms = new MemoryStream();
            //b.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            byte[] bytes = ms.GetBuffer();
            ms.Close();
            return bytes;
        }
        private static Bitmap ByteToBitmap(byte[] datas,int pLength)
        {
            MemoryStream ms1 = new MemoryStream(datas, 0, pLength);
            Bitmap bm = (Bitmap)Bitmap.FromStream(ms1);

            //
            bm.Save(phy + "test", ImageFormat.Bmp);

            ms1.Close();
            return bm;
        } 

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

  这里有几点需要注意:

1)因为我们websocket传输的是图片,数据流较大,一般需要轮训多次接收。

2)如果你用的图片较大,还需要修改接收图片的数组的尺寸。

3)你需要一个xml文件用来人脸识别,EmguCV已经内置,也可以自己去网上找。

   人脸识别Demo的关键代码基本就都在这里了,关于各个技术的实现细节,我会在之后的同系列文章中一一阐述。

 转发请注明出处 http://www.cnblogs.com/Arthus/p/3804037.html

  

html5与EmguCV前后端实现——人脸识别篇(一)

时间: 2024-10-29 05:58:11

html5与EmguCV前后端实现——人脸识别篇(一)的相关文章

关于大型网站技术演进的思考(十六)--网站静态化处理—前后端分离—下(8)

我第一次听说nodejs技术大概是在2009年年末,不过我真正认真在网络上进一步了解nodejs还是在2010年年中,当时对nodejs的认识和我现在对nodejs的认识有着天壤的区别,开始想了解nodejs我只是为了感慨谷歌公司开发的V8引擎居然如此强大,它不仅仅可以作为chrome浏览器的javascript内核运行平台,居然还能为服务端使用javascript语言作为平台,通过对nodejs的了解让我认识到chrome浏览器是如此的优秀,但是如此相对的是我并不认为javascript作为服

【转】关于大型网站技术演进的思考(十六)--网站静态化处理—前后端分离—下(8)

我第一次听说nodejs技术大概是在2009年年末,不过我真正认真在网络上进一步了解nodejs还是在2010年年中,当时对nodejs的认识和我现在对nodejs的认识有着天壤的区别,开始想了解nodejs我只是为了感慨谷歌公司开发的V8引擎居然如此强大,它不仅仅可以作为chrome浏览器的javascript内核运行平台,居然还能为服务端使用javascript语言作为平台,通过对nodejs的了解让我认识到chrome浏览器是如此的优秀,但是如此相对的是我并不认为javascript作为服

基于HTML5 的人脸识别活体认证

近几年,人脸识别技术在身份认证领域的应用已经有了较多应用,例如:支付宝.招行的取款.养老金领取等方面,但在杜绝假冒.认证安全性等方面,目前还是一个比较需要进一步解决的课题,特别是在移动端的活体认证技术方面. 本文介绍了在HTML5 环境下可以采用clmtrackr.js 检测工具,结合人脸模型,实现人脸的跟踪检测.同时采用动作识别实现活体认证. 但本方案只能够在Firefox 或者Chrome中使用.并且只适合研究学习,实际场景中不太理想,需要进一步优化才能够应用. 如果有人有相关的技术,可以推

Asp.net+WebSocket+Emgucv实时人脸识别

上个月在网上看到一个用web实现简单AR效果的文章,然后自己一路折腾,最后折腾出来一个 Asp.net+WebSocket+Emgucv实时人脸识别的东西,网上也有不少相关资料,有用winform的也有asp.net的.其实人脸识别技术早就成熟了,就是没机会接触这方面.百度了一下 找到好多,JqueryFaceDetection,face++,face core,opencv,emgucv等等,这些我都折腾了一遍,并不能很好的满足我的需求,我就是想像手机QQ里边的拍照的时候能识别到人脸并且对图像

百度人脸识别SDK学习

之前看到同事说人脸识别多么高大上之类的, 我就好奇搜索了一下, 本人是小白级别,喜欢用百度多一点,所以就使用了百度的人脸识别SDK进行研究.不得不说百度提供的完档很详细,在学习过程中很少出现不能解决的问题, 所以本人也偷个懒,把sdk文档复制下来. 注:貌似有个bug,我在百度语音中菜单下创建的人脸识别,然后获取API_key和Secret_key, 在学习尝试过程中, 或多或少有请求量, 但是报表中却没有任何记录, 难道是bug吗? 我在想是不是可以无限制的调用了,作为尝试, 没去批量去测试,

基于百度AI开放平台的人脸识别及语音合成

基于百度AI的人脸识别及语音合成课题 课题需求 (1)人脸识别 在Web界面上传人的照片,后台使用Java技术接收图片,然后对图片进行解码,调用云平台接口识别人脸特征,接收平台返回的人员年龄.性别.颜值等信息,将信息返回到Web界面进行显示. (2)人脸比对 在Web界面上传两张人的照片,后台使用Java技术接收图片,然后对图片进行解码,调用云平台接口比对照片信息,返回相似度. (3)语音识别 在Web页面上传语音文件,判断语音文件格式,如果不是wav格式进行转码处理,然后调用平台接口进行识别,

基于javaweb人脸识别注册登录系统

---恢复内容开始--- 现在是2019年,人脸识别技术已经相当成熟了,百度自2017年发布人脸识别技术,已经被广泛应用,不管从现在的iphoneX掀起的面部解锁到手机应用端的各种人脸认证,这一技术已经悄然升息的方便了我们的生活,但是在web端注册登录缺很少用到刷脸登录,第一个最主要的原因可能是安全隐私方面人们对大数据时代的误解.不多废话,下面通过调用百度api来实现人脸注册及登录, Web端人脸识别主要有三个技术思路: 1.前端的人脸识别,例如使用Tensorflow.js, 2.后台人脸识别

OpenCV 和 Dlib 人脸识别基础

00 环境配置 Anaconda 安装 1 下载 https://repo.anaconda.com/archive/ 考虑到兼容性问题,推荐下载Anaconda3-5.2.0版本. 2 安装 3 测试 在键盘按 Win + R, 输入 cmd,回车,将会打开cmd窗口,输入 activate base, 如下所示,表明anaconda环境系统变量无误. IDE PyCharm的安装 自行百度搜索下载并破解. http://idea.lanyus.com/ OpenCV安装 C:\Users\A

FaceNet--Google的人脸识别

引入 随着深度学习的出现,CV领域突破很多,甚至掀起了一股CV界的创业浪潮,当次风口浪尖之时,Google岂能缺席.特贡献出FaceNet再次刷新LFW上人脸验证的效果记录. 本文是阅读FaceNet论文的笔记,所有配图均来自于论文. 转载请注明:http://blog.csdn.net/stdcoutzyx/article/details/46687471 FaceNet 与其他的深度学习方法在人脸上的应用不同,FaceNet并没有用传统的softmax的方式去进行分类学习,然后抽取其中某一层