问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

opencv实现一个人脸检测功能,编译出的android程序有多大

发布网友 发布时间:2022-04-14 20:49

我来回答

2个回答

懂视网 时间:2022-04-15 01:10

【原文:http://www.cnblogs.com/mikewolf2002/p/3474188.html】 前面一篇文章中提到,我们在一副脸部图像上选取76个特征点来描述脸部形状特征,本文中我们会把这些特征点映射到一个标准形状模型。 通常,脸部形状特征点能够参数化分解为两个变量,一个是全

【原文:http://www.cnblogs.com/mikewolf2002/p/3474188.html】

前面一篇文章中提到,我们在一副脸部图像上选取76个特征点来描述脸部形状特征,本文中我们会把这些特征点映射到一个标准形状模型。

通常,脸部形状特征点能够参数化分解为两个变量,一个是全局的刚体变化,一个是局部的变形。全局的刚体变化主要是指脸部能够在图像中移动,旋转,缩放,局部的变形则是指脸部的表情变化,不同人脸的特征等等。

形状模型类主要成员如下:

class shape_model
{ //2d linear shape model
public:
Mat p; //parameter vector (kx1) CV_32F,参数向量
Mat V; //shape basis (2nxk) CV_32F, line subspace,线性子空间
Mat e; //parameter variance (kx1) CV_32F 参数方差
Mat C; //connectivity (cx2) CV_32S 连通性

//把一个点集投影到一个可信的脸部形状空间
void calc_params(const vector &pts, //points to compute parameters from
const Mat weight = Mat(), //weight of each point (nx1) CV_32F 点集的权重
const float c_factor = 3.0); //clamping factor

//该函数用人脸模型V和e,把向量p转化为点集
vector calc_shape(); //shape described by parameters @p

...

void train(const vector > &p, //N-example shapes
const vector &con = vector(), //point-connectivity
const float frac = 0.95, //fraction of variation to retain
const int kmax = 10); //maximum number of modes to retain
...

}

本文中,我们通过Procrustes analysis来处理特征点,移去全局刚性变化,Procrustes analysis算法可以参考:http://en.wikipedia.org/wiki/Procrustes_analysis

在数学上,Procruster analysis就是寻找一个标准形状,然后把所有其它特征点数据都和标准形状对齐,对齐的时候采用最小平方距离,用迭代的方法不断逼近。下面通过代码来了解如何实现Procrustes analysis。

//Procrustes分析的基本思想是最小化所有形状到平均形状的距离和
Mat shape_model::procrustes(const Mat &X,
const int itol, //最大迭代次数
const float ftol //精度

)
{

X矩阵就是多副样本图像76个特征点组成的矩阵,共152行,列数为图像的个数,每列表示一个样本图像特征点的x,y坐标。
int N = X.cols,n = X.rows/2;

//remove centre of mass
//所有的形状向量(特征)对齐到原点,即每个向量的分量减去其平均值,每列是一个形状向量。
Mat P = X.clone();
for(int i = 0; i < N; i++)
{
Mat p = P.col(i); //第i个向量
float mx = 0,my = 0;
for(int j = 0; j < n; j++) //x,y分别计算得到平均值。
{
mx += p.fl(2*j);
my += p.fl(2*j+1);
}
mx /= n; my /= n;
for(int j = 0; j < n; j++)
{
p.fl(2*j) -= mx;
p.fl(2*j+1) -= my;
}
}


//optimise scale and rotation
Mat C_old;
for(int iter = 0; iter < itol; iter++)
{

注意下边的一行代码,会把每个图像对齐到原点特征点x,y分别加起来,求平均值,得到一个152*1的矩阵,然后对该矩阵进行归一化处理。
Mat C = P*Mat::ones(N,1,CV_32F)/N; //计算形状变换后的平均值
normalize(C,C); //canonical shape (对x-进行标准化处理)
if(iter > 0) //converged?//收敛?当两个标准形状或者标准形状的误差小于某一值这里是ftol则,停止迭代。
{

norm函数默认是矩阵各元素平方和的根范式
if(norm(C,C_old) < ftol)
break;
}
C_old = C.clone(); //remember current estimate//记下当前的矩阵,和下一次进行比较
for(int i = 0; i < N; i++)
{

rot_scale_align函数求每副图像的特征点向量和平均向量满足最小乘法时候的旋转矩阵。即求得a,b值组成的旋转矩阵。

该函数的代码:

Mat shape_model::rot_scale_align(const Mat &src, const Mat &dst)
 {
 //construct linear system
 int n = src.rows/2;
 float a=0,b=0,d=0;
 for(int i = 0; i < n; i++)
 {
 d += src.fl(2*i) * src.fl(2*i ) + src.fl(2*i+1) * src.fl(2*i+1); //x*x+y*y
 a += src.fl(2*i) * dst.fl(2*i ) + src.fl(2*i+1) * dst.fl(2*i+1);
 b += src.fl(2*i) * dst.fl(2*i+1) - src.fl(2*i+1) * dst.fl(2*i );
 }
 a /= d; 
 b /= d;
 //solved linear system
 return (Mat_(2,2) << a,-b,b,a);
 }


Mat R = this->rot_scale_align(P.col(i),C); //求两个形状之间的误差满足最小二乘时的旋转矩阵。即相当于两个形状"最靠近"时,需要的旋转的仿射矩阵
for(int j = 0; j < n; j++)
{ //apply similarity transform//应用相似变换,这对形状中的每一个点,应用仿射矩阵。 变化后,该特征点向量会靠近平均特征向量。之后经过反复迭代,直到平均向量和上次比较变化很小时,退出迭代。
float x = P.fl(2*j,i),y = P.fl(2*j+1,i);
P.fl(2*j ,i) = R.fl(0,0)*x + R.fl(0,1)*y;
P.fl(2*j+1,i) = R.fl(1,0)*x + R.fl(1,1)*y;
}
}
}
return P;
}

通过Procrustes analysis对齐的特征向量,我们要用一个统一的矩阵把平移和旋转统一起来表示(成为线性表示),然后把该矩阵追加到局部变形空间,注意对该矩阵表示,我们最后进行了史密斯正交处理。

我们通过函数 calc_rigid_basis得到该矩阵表示:

Mat shape_model::calc_rigid_basis(const Mat &X)
{
//compute mean shape
int N = X.cols,n = X.rows/2;
Mat mean = X*Mat::ones(N,1,CV_32F)/N;

//construct basis for similarity transform
Mat R(2*n,4,CV_32F);
for(int i = 0; i < n; i++)
{
R.fl(2*i,0) = mean.fl(2*i );
R.fl(2*i+1,0) = mean.fl(2*i+1);
R.fl(2*i,1) = -mean.fl(2*i+1);
R.fl(2*i+1,1) = mean.fl(2*i );
R.fl(2*i,2) = 1.0;
R.fl(2*i+1,2) = 0.0;
R.fl(2*i,3) = 0.0;
R.fl(2*i+1,3) = 1.0;
}
//Gram-Schmidt orthonormalization
for(int i = 0; i < 4; i++)
{
Mat r = R.col(i);
for(int j = 0; j < i; j++)
{
Mat b = R.col(j);
r -= b*(b.t()*r);
}
normalize(r,r);
}
return R;
}

下面我们看看train函数的实现:

两篇参考的翻译:http://blog.csdn.net/raby_gyl/article/details/13148193

http://blog.csdn.net/raby_gyl/article/details/13024867

该函数的输入为n个样本图像的采样特征点,该点集会被首先转化为行152,列为样本数量的矩阵表示,另外还有连通性点集索引,以及方差的置信区间以及保留模型的最大数量。

void train(const vector > &p, //N-example shapes
const vector &con = vector(), //point-connectivity
const float frac = 0.95, //fraction of variation to retain
const int kmax = 10) //maximum number of modes to retain
{
//vectorize points
Mat X = this->pts2mat(points);

N是样本的数目,n是76,表示76个特征点。
int N = X.cols,n = X.rows/2;

//align shapes
Mat Y = this->procrustes(X);

//compute rigid transformation 计算得到刚体变化矩阵R
Mat R = this->calc_rigid_basis(Y);

脸部局部变形我们用一个线性模型表示,主要的思想如下图所示:一个有N个面部特征组成面部形状,被建模成一个2N维空间的点。我们要尽量找到一个低维的超平面,在这个平面内,所有的面部形状都在里面,由于这个超平面只是2N维空间的子集,占用刚少的空间,处理起来更快。可以想得到,如果这个子空间来自于一个人,则子空间的点,表现这个人的各种表情变化。前面的教程中,我们知道PCA算法能够找到低维子空间,但PCA算法需要指定子空间的维数,在启发式算法中有时候这个值很难选择。在本程序中,我们通过SVD算法来模拟PCA算法。

//compute non-rigid transformation

Y是152*1294的矩阵,它是procrustes分析的结果,R是刚体变化矩阵152*4,它的转置就是4*152
Mat P = R.t()*Y; //原始的位置
Mat dY = Y - R*P; //dy变量的每一列表示减去均值的Procrustes对齐形状,投影刚体运动

奇异值分解SVD有效的应用到形状数据的协方差矩阵(即,dY.t()*dY),OpenCV的SVD类的w成员存储着数据变化性的主要方向的变量,从最大到最小排序。一个选择子空间维数的普通方法是选择保存数据总能量分数frac的方向最小集(即占总能量的比例为frac),这是通过svd.w记录表示的,因为这些记录是从最大的到最小的排序的,它充分地用来评估子空间,通过用变化性方向的最大值k来评估能量。他们自己的方向存储在SVD类的u成员内。svd.w和svd.u成分一般分别被成为特征值和特征矢量。
SVD svd(dY*dY.t());
int m = min(min(kmax,N-1),n-1);
float vsum = 0;
for(int i = 0; i < m; i++)
vsum += svd.w.fl(i);
float v = 0;
int k = 0;

达到了95%的主成分量,退出,frac=0.95
for(k = 0; k < m; k++)
{
v += svd.w.fl(k);
if(v/vsum >= frac){k++; break;}
}
if(k > m) k = m;

取前k个特征向量
Mat D = svd.u(Rect(0,0,k,2*n));

把全局刚体运动和局部变形运动结合起来,注意V的第一列是缩放,第三、四列分别是x,y偏移。

//combine bases
V.create(2*n,4+k,CV_32F);
Mat Vr = V(Rect(0,0,4,2*n)); //刚体子空间
R.copyTo(Vr); //非刚体子空间
Mat Vd = V(Rect(4,0,k,2*n));
D.copyTo(Vd);

最后我们要注意的一点是如何约束子空间坐标,以使得子空间内的面部形状都是有效的。在下面的图中,我们可以看到,对于子空间内的图像,如果在某个方向改变坐标值,当坐标值小时候,它仍是一个脸的形状,但是变化值大时候,就不知道是什么玩意了。防止出现这种情况的最简单方法,就是把变化的值clamp在一个范围内,通常是现在± 3 的范围,这样可以cover到99.7%的脸部变化。clamping的值通过下面的代码计算:

//compute variance (normalized wrt scale)


Mat Q = V.t()*X; //把数据投影到子空间
for(int i = 0; i < N; i++) //normalize coordinates w.r.t scale
{ //用第一个坐标缩放,防止太大的缩放值影响脸部识别
float v = Q.fl(0,i);
Mat q = Q.col(i);
q /= v;
}
e.create(4+k,1,CV_32F);
pow(Q,2,Q);
for(int i = 0; i < 4+k; i++)
{
if(i < 4)
e.fl(i) = -1; //no clamping for rigid coefficients
else
e.fl(i) = Q.row(i).dot(Mat::ones(1,N,CV_32F))/(N-1);
}
//store connectivity
if(con.size() > 0)
{ //default connectivity
int m = con.size();
C.create(m,2,CV_32F);
for(int i = 0; i < m; i++)
{
C.at(i,0) = con[i][0];
C.at(i,1) = con[i][1];
}
}
else
{ //user-specified connectivity
C.create(n,2,CV_32S);
for(int i = 0; i < n-1; i++)
{
C.at(i,0) = i; C.at(i,1) = i+1;
}
C.at(n-1,0) = n-1; C.at(n-1,1) = 0;
}
}

工程文件:FirstOpenCV40,

程序的运行参数为:annotations.yaml shapemodle.yaml

程序执行后,可以看到我们只保留了14个模型。

我们也可以使用下面的运行参数:annotations.yaml shapemodle.yaml –mirror

这时候,每副图像的特征点,会生成一个y轴对称的镜像特征点集,这时训练的采样数目翻倍,为5828。

在工程文件FirstOpenCV41中,我们可视化了生成的模型,会连续显示14个模型的不同姿态:

热心网友 时间:2022-04-14 22:18

  在Python下用起来OpenCV很爽,代码很简洁,很清晰易懂。使用的是Haar特征的分类器,训练之后得到的数据存在一个xml中。下面我们就来详细谈谈。   模式识别课上老师留了个实验,在VC++环境下利用OpenCV库编程实现人脸检测与跟踪。   然后就开始下载opencv和vs2012,再然后,配置了好几次还是配置不成功,这里不得不吐槽下微软,软件做这么大,这么难用真的好吗?   于是就尝试了一下使用python完成实验任务,大概过程就是这样子的:   首先,配置运行环境:   下载opencv和python的比较新的版本,推荐opencv2.4.X和python2.7.X。   直接去官网下载就ok了,python安装时一路next就行,下载的opencv.exe文件运行后基本上是一个解压的过程,自己选择一个解压路径(尽量不要出现中文),然后就坐等解压完成。   然后从opencv解压后的路径中找(D:My DocumentsDownloads)opencvbuildpython2.7x86,()里面的部分是你自己的安装路径,其中x86对应32位的机器,x64代表64位的机器,当然要按照你机器的实际情况选择了。将这个路径里面的cv2.pyd拷贝至python2.7的模块路径C:Python27Libsite-packages里,python2.7默认安装在C盘跟目录下。   此时打开python,在cmd下输入python,或者直接打开“所有程序->active state active python->Python Interactive Shell”都行。   接下来输入import cv2,出错了对不对?为什么呢?   这是因为没有安装numpy这个python模块,去numpy的官网下载一个比较新的版本,因为最新的版本一般都是源代码,需要去命令行中安装,比较麻烦,推荐找一个exe文件。注意,在官网给出的链接中,切记看完全名称,后面一般都会提示这个模块在哪个python版本下安装时比较和谐,选择你自己安装的python版本对应的numpy模块。下载完成后安装时看一下该模块给出的python路径对不对,对的话然后next就行了,不对的话可能就是你的python是2.7,却下了numpy for python 3.0.   这时再去import一下cv2,如果什么也没有输出的话就是import成功了。   简直比vs下的配置简单了好几个数量级,对不对?   配置好环境后,跟着opencv嗨起来!   然后在pythonwin或idle(python gui)下新建一个py文件,输入以下代码:   ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import cv2 import numpy as np cv2.namedWindow("test") cap=cv2.VideoCapture(0) success,frame=cap.read() classifier=cv2.CascadeClassifier("haarcascade_frontalface_alt.xml") #确保此xml文件与该py文件在一个文件夹下,否则将这里改为绝对路径,此xml文件可在D:My DocumentsDownloadsopencvsourcesdatahaarcascades下找到。 while success:   success,frame=cap.read()   size=frame.shape[:2]   image=np.zeros(size,dtype=np.float16)   image=cv2.cvtColor(frame,cv2.cv.CV_BGR2GRAY)   cv2.equalizeHist(image,image)   divisor=8   h,w=size   minSize=(w/divisor,h/divisor)   faceRects=classifier.detectMultiScale(image,1.2,2,cv2.CASCADE_SCALE_IMAGE,minSize)   if len(faceRects)>0:     for faceRect in faceRects:       x,y,w,h=faceRect       cv2.circle(frame,(x+w/2,y+h/2),min(w/2,h/2),(255,0,0))       cv2.circle(frame,(x+w/4,y+h/4),min(w/8,h/8),(255,0,0))       cv2.circle(frame,(x+3*w/4,y+h/4),min(w/8,h/8),(255,0,0))       cv2.rectangle(frame,(x+3*w/8,y+3*h/4),(x+5*w/8,y+7*h/8),(255,0,0))   cv2.imshow("test",frame)   key=cv2.waitKey(10)   c=chr(key&255)   if c in ['q','Q',chr(27)]:     break cv2.destroyWindow("test")   为什么没有注释,你恐怕知道下雨天,dir()和help()更配呦。   这段代码的功能就是对计算机摄像头拍到的视频加以处理,使其显示并追踪人脸。下图是运行效果:   最后再说一句,这个过程说起来简单,但很容易出错,希望大家能自己找到错误的原因,并解决错误。如果自己解决不了的话,不妨把问题贴在里,大家来共同解决,共同进步。   以上所述就是本文的全部内容了,希望大家能够喜欢。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
显卡降价矿难了!分享几个检测矿卡的实用软件 怎么分辨二手显卡刷bios 下文教你 怎么识别矿卡 鉴别矿卡的方法介绍 怎么设置小度在家回家视频通话? 贵阳砂岩雕塑厂有哪些 西安市长安二中附近有老年公寓没 来西安这么久了第一次租到性价比高的房子,松鼠公寓良心推荐_西安... 陕西省自强中等专业学校2024年学费多少 宝鸡市区有什么比较好的楼盘 乔安如何远程监控 李子还有芒果要怎么存放才不会熟得快? word新建样式字体设为楷体、小四、加宽1磅改怎么做 海灵菇是章鱼的吸盘吗? 无锡移动宽带 续费 安装的无锡移动宽带,充500元话费送一年宽带的那种,眼看就要到期了,要怎么办? 江苏移动宽带续费是不是都要最低消费28元 我的下巴上长了一颗痣怎么能彻底祛除啊? 甲下色素痣怎么解决? 我想问一下我要怎么做才能减少身上的痣啊? 高分求助:如何取痣? 华为matex手机售后服务电话 下巴有痣怎么办 下巴有痣怎么去 演员脸下的痦子是怎么做的? program japussy; 这中代码用什么编辑 PDF文件是用什么软件编辑的,初学者应从哪几方面入手,有没有相关的教程. android studio的编辑程序在哪 在VC++中编辑windows程序: 我在添加Menu(菜单)资源,默认的该菜单资源ID为:IDR_MENU1 您好,您会JavaScript吗?我想问问一款叫mcpe js编辑器的软件使用的语言是... 农行掌上银行转账费用怎么收取的? 蛟龙号深潜多少千米 群众路线的主要内容是﹖ 十九大党章规定,党的群众路线是什么 为什么不建一个电鳗发电站 为什么电鳗不能用来发电呢? 石家庄在线医生妇科免费咨询 有没有专业的大夫在线解答的 微信红包怎么可以转到支付宝上 借呗申请额度后说审核中,等二十四小时通知你,但过一会点进去看又显已订阅开通通知,这是什么情况? 微信红包钱可转入支付宝账号吗? 为什么支付宝借呗关了后重新打开还要邀约是什么意思? 借呗今天邀请我开通,但是过了一会又说开通预订通知,怎么回事? 我只是提示我开通借呗开通订阅通知是不是就可以开通了?是不是低于600芝麻分就不会出额度? 借呗出现关注生活号,开通通知我是什么意思? 谁知道宽带用哪家比较好 借呗已经有开通通知了,但显示申请火爆,12点申请还是申请火爆 支付宝借呗说我有笔额度可申请点进去说要24小时后短信通知不知道什么意思? 手机号吊销了会不会没有? qq号,是否会自动吊销 QQ空间的许愿树活动怎么参与抽奖啊我怎么找不到啊 手机号吊销微信会不会作废? 我的手机号由于欠费吊消了不能用手机号登录微信请问用什么办法加上微信谢谢?