Posted at 2010/02/27T23:16:17Z.
Face Detector
また意味の分からない物を作った。上のは顔認識のデモ。
下記のエントリーのアルゴリズムを移植して作った。
Face detection in pure PHP (without OpenCV) - Maurice Bloggue
HTML5のお陰でクライアントサイドで画像処理ができるし、上記の顔認識プログラムはピュアPHPでライブラリ使ってるわけじゃないからJavaScriptでもできた。
もちろんIEは無視。
Math.roundで丸めた。FaceDetector.detect(img);で、imgにはnew Image()などで生成された画像オブジェクトを指定する。{x:(X座標),y:(Y座標),w:(顔のサイズ)}の形式。
FaceDetector={
detection_data:null,
_extimg:function (img,size){
var cvs=document.createElement("canvas");
cvs.width=size.width;
cvs.height=size.height;
var g=cvs.getContext("2d");
g.drawImage(img,0,0,size.width,size.height);
var inp=g.getImageData(0,0,size.width,size.height);
return {
get:function (x,y){
var base=(y*inp.width+x)*4;
return {r:inp.data[base],g:inp.data[base+1],b:inp.data[base+2]};
},
width:inp.width,
height:inp.height
};
},
detect:function (img){
function getimgstats(c)
{
var w=c.width;
var h=c.height;
var iis=compute_ii(c,w,h);
return iis;
}
function compute_ii(c,w,h)
{
var iiw=w+1;
var iih=h+1;
var ii={};
var ii2={};
for(var i=0;i<iiw;i++)ii[i]=ii2[i]=0;
for(var i=1;i<iiw;i++)
{
ii[i*iiw]=0;
ii2[i*iiw]=0;
var rowsum=0;
var rowsum2=0;
for(var j=1;j<iih;j++)
{
var rgb=c.get(j,i);
var gray=(0.2989*rgb.r+0.587*rgb.g+0.114*rgb.b)>>0;
rowsum+=gray;
rowsum2+=gray*gray;
var iiabove=(i-1)*iiw+j;
var iithis=i*iiw+j;
ii[iithis]=ii[iiabove]+rowsum;
ii2[iithis]=ii2[iiabove]+rowsum2;
}
}
return {ii:ii,ii2:ii2,w:w,h:h};
}
function detect_b2s(iis)
{
var sw=iis.w/20;
var sh=iis.h/20;
var startScale=(sh<sw)?sh:sw;
var scaleUpdate=1/1.2;
for(var scale=startScale;scale>1;scale*=scaleUpdate)
{
var w=(20*scale)>>0;
var endx=iis.w-w-1;
var endy=iis.h-w-1;
var step=Math.max(scale,2)>>0;
var invarea=1/(w*w);
for(var y=0;y<endy;y+=step)
{
for(var x=0;x<endx;x+=step)
{
var passed=detect_sub(x,y,scale,iis,w,iis.w+1,invarea);
if(passed)return {x:x,y:y,w:w};
}
}
}
return null;
}
function detect_sub(x,y,scale,iis,w,iiw,invarea)
{
var mean=(iis.ii[(y+w)*iiw+x+w]+iis.ii[y*iiw+x]-iis.ii[(y+w)*iiw+x]-iis.ii[y*iiw+x+w])*invarea;
var vnorm=(iis.ii2[(y+w)*iiw+x+w]+iis.ii2[y*iiw+x]-iis.ii2[(y+x)*iiw+x]-iis.ii2[y*iiw+x+w])*invarea-mean*mean;
vnorm=(vnorm>1)?Math.sqrt(vnorm):1;
for(var i_stage in FaceDetector.detection_data)
{
var stage=FaceDetector.detection_data[i_stage];
var trees=stage[0];
var stageThresh=stage[1];
var stageSum=0;
for(var i_tree in trees)
{
var tree=trees[i_tree];
var currentNode=tree[0];
var treeSum=0;
while(currentNode!=null)
{
var vals=currentNode[0];
var nodeTresh=vals[0];
var leftval=vals[1];
var rightval=vals[2];
var leftidx=vals[3];
var rightidx=vals[4];
var rects=currentNode[1];
var rectSum=0;
for(var i_rect in rects)
{
var s=scale;
var rect=rects[i_rect];
var rx=(rect[0]*s+x)>>0;
var ry=(rect[1]*s+y)>>0;
var rw=(rect[2]*s)>>0;
var rh=(rect[3]*s)>>0;
var wt=rect[4];
var rSum=(iis.ii[(ry+rh)*iiw+rx+rw]+iis.ii[ry*iiw+rx]-iis.ii[(ry+rh)*iiw+rx]-iis.ii[ry*iiw+rx+rw])*wt;
rectSum+=rSum;
}
rectSum*=invarea;
currentNode=null;
if(rectSum>=nodeTresh*vnorm)
{
if(rightidx==-1)treeSum=rightval;
else currentNode=tree[rightidx];
}else{
if(leftidx==-1)treeSum=leftval;
else currentNode=tree[leftidx];
}
}
stageSum+=treeSum;
}
if(stageSum+0.08<stageThresh)return false;
}
return true;
}
var ratio=0;
var size={width:img.width,height:img.height};
var diff={width:320-size.width,height:240-size.height};
if(diff.width>diff.height)
{
ratio=size.width/320;
}else{
ratio=size.height/240;
}
if(ratio!=0)
{
size.width/=ratio;
size.height/=ratio;
}
var c=FaceDetector._extimg(img,size);
var stats=getimgstats(c);
var face=detect_b2s(stats);
if(face==null)return null;
if(ratio!=0)
{
face.x*=ratio;
face.y*=ratio;
face.w*=ratio;
}
return face;
}
};
出来る人は高速化してねー!俺の技術力じゃ無理。
まぁ、これが何に使えるかって言う話だけどね。