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部分:
- $(function(){
- var video = $('#live').get()[0],
- canvas = $('#canvas'),
- ctx=canvas.get()[0].getContext('2d'),
- canvasFace =$('#canvasFace'),
- ctx2= canvasFace.get()[0].getContext('2d'),
- canSend=true;
- ctx2.strokeStyle="#EEEE00";
- ctx2.fillStyle='rgba(0,0,0,0.0)';
- ctx2.lineWidth=3;
- navigator.webkitGetUserMedia({ "video": true },function(stream){
- video.src = webkitURL.createObjectURL(stream);
- startWS();
- },function(err){
- console.log('err');
- });
- //x,y,w,h
- var _draw =function(pArr){
- var _obj = $.fromJson(pArr);
- ctx2.clearRect(0,0,320,240);
- if($.isArray(_obj)){
- for(var i=0,l=_obj.length;i<l;i++ ){
- ctx2.strokeRect(_obj[i].X,_obj[i].Y,_obj[i].W,_obj[i].H);
- }
- }
- };
- var startWS=function(){
- var ws = new WebSocket("ws://10.168.1.1/Cloud/WSHandler.ashx");
- ws.onopen = function(){
- console.log('Opened WS!');
- };
- ws.onmessage=function(msg){
- _draw(msg.data);
- canSend = true;
- };
- ws.onclose=function(msg){
- console.log('socket close!');
- };
- var timer = setInterval(function(){
- ctx.drawImage(video,0,0,320,240);
- if(ws.readyState == WebSocket.OPEN && canSend){
- canSend = false;
- var data =canvas.get()[0].toDataURL('image/jpeg',1.0),
- newblob = dataURItoBlob(data);
- ws.send(newblob);
- }
- },60);
- };
- });
这段JS代码中,大家需要注意替换ws文件的地址。至于Canvas绘图,websocket,Camera调用等细节,在后续文章中会有详解。
可以看到websocket在向服务器提交数据时候,需要对DataURL的数据进行封装,下面就附上这个函数(与早期版本不同)。
dataURItoBlob函数:
- function dataURItoBlob(dataURI) {
- var byteString = atob(dataURI.split(',')[1]),
- mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0],
- ab = new ArrayBuffer(byteString.length),
- ia = new Uint8Array(ab);
- for (var i = 0; i < byteString.length; i++) {
- ia[i] = byteString.charCodeAt(i);
- }
- return new Blob([ab],{type: mimeString});
- }
前端的代码大致就这样了,后端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 = * ;
- 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!=)
- {
- 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, ,
- HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
- new Size(nextFrame.Width / , nextFrame.Height / )
- )[];
- 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 - ] == ',') {
- sb.Remove(sb.Length-,);
- }
- }
- sb.Append("]");
- return sb.ToString();
- }
- private int _ii = ;
- 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, , 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前后端实现——人脸识别篇(一)的更多相关文章
- 前后端token机制 识别用户登录信息
Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. 那么,Token有什么作用?又是什么原理呢? Token一般用在两个地方: 1)防止表单重复提交. 2)anti c ...
- OpenCV+python 人脸识别
首先给大家推荐一本书:机器学习算法原理与编程实践 本文内容全部转载于书中,相当于一个读书笔记了吧 绪论 1992年麻省理工学院通过实验对比了基于结构特征的方法与基于模版匹配的方法,发现模版匹配的方法要 ...
- 百度人脸识别java html5
1.前端thymeleaf+h5 index.html 人脸识别+定位,用的百度sdk <!DOCTYPE html> <html xmlns="http://www ...
- 基于HTML5 的人脸识别活体认证
近几年,人脸识别技术在身份认证领域的应用已经有了较多应用,例如:支付宝.招行的取款.养老金领取等方面,但在杜绝假冒.认证安全性等方面,目前还是一个比较需要进一步解决的课题,特别是在移动端的活体认证技术 ...
- Asp.net+WebSocket+Emgucv实时人脸识别
上个月在网上看到一个用web实现简单AR效果的文章,然后自己一路折腾,最后折腾出来一个 Asp.net+WebSocket+Emgucv实时人脸识别的东西,网上也有不少相关资料,有用winform的也 ...
- 基于 HTML5 的人脸识别技术
基于 HTML5 的人脸识别技术 https://github.com/auduno/headtrackr/
- 使用 HTML5, javascript, webrtc, websockets, Jetty 和 OpenCV 实现基于 Web 的人脸识别
这是一篇国外的文章,介绍如何通过 WebRTC.OpenCV 和 WebSocket 技术实现在 Web 浏览器上的人脸识别,架构在 Jetty 之上. 实现的效果包括: 还能识别眼睛 人脸识别的核心 ...
- 人脸识别技术大总结(1):Face Detection & Alignment
http://blog.jobbole.com/85783/ 首页 最新文章 IT 职场 前端 后端 移动端 数据库 运维 其他技术 - 导航条 - 首页 最新文章 IT 职场 前端 - Ja ...
- JWT 在前后端分离中的应用与实践
关于前后端分离 前后端分离是一个很有趣的议题,它不仅仅是指前后端工程师之间的相互独立的合作分工方式,更是前后端之间开发模式与交互模式的模块化.解耦化.计算机世界的经验告诉我们,对于复杂的事物,模块化总 ...
随机推荐
- 2013=11=12 SQL 实验
--22. 查询选修课程成绩至少有一门在80分以上的学生学号: select distinct sno from sc where grade>80 go --23. 查询选修课程成绩均在80分 ...
- Selenium IDE整理
安装 Step1: 下载Firefox浏览器 http://www.firefox.com.cn/ Step2: 安装Selenium IDE插件 http://seleniumhq.org/down ...
- Object -C NSNumber -- 笔记
// // main.m // NSNumber // // Created by facial on 24/8/15. // Copyright (c) 2015 facial_huo. A ...
- 【ArcGIS 10.2新特性】Geodatabase 10.2 常见问题
地理数据库技术一直以来都是ArcGIS的基础技术.为充分使用ArcGIS的全部功能则需要把数据存储在Geodatabase当中.Geodatabase是一个综合性的信息模型,它可以支持存储几乎任意类型 ...
- 百度2014校园招聘算法——给出一组数据A=[a_0, a_1, a-2, ... a_n](当中n可变),打印出该数值元素的全部组合。
VC++ void StringTest(CString source, CStringArray& dest) { if(source.IsEmpty()) { } else { CStri ...
- linux 启动network后报错:device eth0 does not seem to be present, delaying initialization
问题背景: 在vsphere client中部署ovf模板后启动linux 的network后提示:device eth0 does not seem to be present, delaying ...
- Mysql大小写敏感的问题 --转
一.1 CREATE TABLE NAME(name VARCHAR(10)); 对这个表,缺省情况下,下面两个查询的结果是一样的: SELECT * FROM TABLE NAME WHERE na ...
- Apache调优
1.调整文件描述符大小,默认Linux系统的文件描述符是1024,对于squid等一些服务来说 在高负载的情况下这些文件描述符是远远不够的,所以在部署该类服务器时修改文件 ...
- java的练习
import java.awt.GridLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.s ...
- spring-data-solr官方学习文档介绍
spring-data-solr文档介绍如下: 通过http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd(spring ...