2009年6月27日土曜日

WMで顔認識その4

恥ずかしいけど、顔認識プログラムのソースを公開しときます。

http://sites.google.com/site/windowsmobilesmile/

顔認識部分に関してはサンプルソースのほぼそのままです

フェイスマーク画像から、合成用のマスク画像を作成に以下の用にしました

bool LoadFaceImage(LPTSTR fpath, LPTSTR mpath)

{

if(fpath[0]!=0)

{

char cpath[MAX_PATH];

WideCharToMultiByte(CP_ACP,0,fpath,-1,cpath,sizeof(cpath),0,0);

face_img = cvLoadImage (cpath, CV_LOAD_IMAGE_COLOR);

if(mpath[0]!=0)

{

WideCharToMultiByte(CP_ACP,0,mpath,-1,cpath,sizeof(cpath),0,0);

mask_img = cvLoadImage (cpath, CV_LOAD_IMAGE_GRAYSCALE);

}



if(face_img && !mask_img)

{

IplImage *tmp = 0;

mask_img = cvCreateImage(cvGetSize(face_img),IPL_DEPTH_8U, 1);

#if 1

tmp = cvCreateImage(cvGetSize(face_img),IPL_DEPTH_8U, 3);

cvCvtColor (face_img, tmp, CV_BGR2HSV);

uchar sh = tmp->imageData[0];

uchar ss = tmp->imageData[1];

uchar sv = tmp->imageData[2];

CvScalar lower = { sh -16, ss -32, sv -32, 0},upper = { sh + 16, ss + 32, sv + 32 , 0};

cvInRangeS(tmp, lower, upper, mask_img);

cvReleaseImage(&tmp);

#else

uchar sb = face_img->imageData[0];

uchar sg = face_img->imageData[1];

uchar sr = face_img->imageData[2];

CvScalar lower = { sb -32, sg -32, sr -32, 0},upper = { sb + 32, sg + 32, sr + 32 , 0};

mask_img = cvCreateImage(cvGetSize(face_img),IPL_DEPTH_8U, 1);

cvInRangeS(face_img, lower, upper, mask_img);

#endif



tmp = cvCreateImage(cvGetSize(face_img),IPL_DEPTH_8U, 1);

cvNot(mask_img,tmp);

cvReleaseImage(&mask_img);

mask_img = tmp;



}

}

return true;

}




(1)フェイスマーク画像をRGBからHSVに変換
cvCvtColor (face_img, tmp, CV_BGR2HSV);

(2)左端の画素のHSV値を取得
uchar sh = tmp->imageData[0];
uchar ss = tmp->imageData[1];
uchar sv = tmp->imageData[2];

(3)誤差範囲を指定して左端の画素と同じ色の画素を0、その他を255とするマスク画像を作成
CvScalar lower = { sh -16, ss -32, sv -32, 0},upper = { sh + 16, ss + 32, sv + 32 , 0};
cvInRangeS(tmp, lower, upper, mask_img);
cvReleaseImage(&tmp);

RGBのまま処理をするばあいは(1)のHSV化を省略する。
誤差範囲を調整して抜け具合を調整する


フェイスマークを画像に合成するコード

bool DrawFaceMark(CvRect *r, int idx)

{

if(face_img==0)

{

CvPoint center;

int radius;

center.x = cvRound (r->x + r->width * 0.5);

center.y = cvRound (r->y + r->height * 0.5);

radius = cvRound ((r->width + r->height) * 0.25);

cvCircle (src_img, center, radius, colors[idx % 8],-1, 8, 0);

}

else

{

CvSize sz;

sz.width = r->width;

sz.height = r->height;

IplImage *rfimg = cvCreateImage(sz, IPL_DEPTH_8U, 3);

IplImage *rmimg = cvCreateImage(sz, IPL_DEPTH_8U, 1);

IplImage *trimg = cvCreateImage(sz, IPL_DEPTH_8U, 1);

cvResize(face_img, rfimg, 1);

cvResize(mask_img, rmimg, 1);

cvThreshold (rmimg, trimg, 90, 255, CV_THRESH_BINARY);

cvReleaseImage (&rmimg);

rmimg = trimg;





cvSetImageROI (src_img, *r);

cvCopy(rfimg,src_img,rmimg);



cvReleaseImage (&rfimg);

cvReleaseImage (&rmimg);

}

cvResetImageROI(src_img);



return true;

}


(1) r がフェイスマークの位置を示す構造体。rサイズの画像を3枚作る
CvSize sz;
sz.width = r->width;
sz.height = r->height;
IplImage *rfimg = cvCreateImage(sz, IPL_DEPTH_8U, 3);
IplImage *rmimg = cvCreateImage(sz, IPL_DEPTH_8U, 1);
IplImage *trimg = cvCreateImage(sz, IPL_DEPTH_8U, 1);

(2) フェイスマーク画像、マスク画像を(1)の画像にリサイズする
cvResize(face_img, rfimg, 1);
cvResize(mask_img, rmimg, 1);

(3)リサイズしたマスク画像を適当なしきい値で2値化する。(元のマスク画像は2値化されているけど、リサイズで境界部分に中間値ができる)
cvThreshold (rmimg, trimg, 90, 255, CV_THRESH_BINARY);
cvReleaseImage (&rmimg);
rmimg = trimg;

(3)元画像の置き換える部分を指定する
cvSetImageROI (src_img, *r);

(4)マスクを指定して元画像にリサイズしたフェイスマスク画像をコピーする
cvCopy(rfimg,src_img,rmimg);

(5)作った画像を開放して、(3)で指定した範囲を解除する
cvReleaseImage (&rfimg);
cvReleaseImage (&rmimg);
cvResetImageROI(src_img);

ROIを指定すると、画像の一部を書き換えることが出来る

0 件のコメント: