画像認識について
Posted: 2012年7月20日(金) 21:07
VC++を使って画像認識をしようと思い、下記のURLにある肌色と左右対称性を用いた顔の検出というプログラムを使ってやってみましたが、いまいちやり方が良く分かりませんでした。
もし、下記のソースにmain文をつけたらどのようなプログラムになりますか?
分かる人いたら教えてください
よろしくお願いします
もし、下記のソースにmain文をつけたらどのようなプログラムになりますか?
分かる人いたら教えてください
よろしくお願いします
private: System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
Graphics^ gr=e->Graphics;
int X0=10,X1=340,Y0=10,Y1=260;
int FACES_MAX=20; //検出する顔の数の上限を20個とする
int F_WIDTH=23,F_HEIGHT=29; //フレームのサイズ
int i,j,p,q,r,g,b,d,n;
int x,y,xx,yy;
int x_max,y_max,counter_max;
int skin_count;
Color color1,color2;
int candidate_count; //フレームと肌色によって検出した顔の数(対称性チェック前)
int diff; //対称性を調べるための差分
array<Point^>^ candidate=gcnew array<Point^>(FACES_MAX); //顔の候補の座標位置を示す
array<int>^ shift=gcnew array<int>{0,-1,1,-2,2,-3,3,-4,4}; //対称性を調べるときの左右のシフト量
Bitmap^ bmap_skin=gcnew Bitmap(WIDTH,HEIGHT); //肌色を検出した画像
Bitmap^ bmap_gray=gcnew Bitmap(WIDTH,HEIGHT); //肌色部をグレイ化した画像
//原画像を読み込む
Bitmap^ bmap_src=gcnew Bitmap("faces.JPG");
//原画像bmap_srcを左上に表示する
gr->DrawImage(bmap_src,X0,Y0);
//肌色を検出した白黒画像bmap_skinとグレイ化画像bmap_grayを得る
for(y=0;y<HEIGHT;y++)
for(x=0;x<WIDTH;x++){
color1=bmap_src->GetPixel(x,y);
r=color1.R;
g=color1.G;
b=color1.B;
d=(2*r+4*g+b)/7;
if((r>g*1.1 && r<g*2.4) && (r>b*1.1 && r<b*2.4) && r>90){ //肌色(「易しい画像認識(1)」参照)
bmap_skin->SetPixel(x,y,Color::White);
bmap_gray->SetPixel(x,y,Color::FromArgb(d,d,d));
}
else{ //肌色以外
bmap_skin->SetPixel(x,y,Color::Black);
bmap_gray->SetPixel(x,y,Color::Black);
}
}
//肌色画像bmap_skinとグレイ画像bmap_grayを右上と左下に表示する
gr->DrawImage(bmap_skin,X1,Y0);
gr->DrawImage(bmap_gray,X0,Y1);
//白黒画像bmap_skinを二次元データにする
array<int,2>^ pixel_data=gcnew array<int,2>(WIDTH,HEIGHT);
pixel_data=getPixelDataFromBitmap(bmap_skin);
//白黒画像bmap_skinの積分型二次元データを得る
array<int,2>^ integral_image=gcnew array<int,2>(WIDTH,HEIGHT);
integral_image=createIntegralImageFromPixelData(pixel_data);
//積分型二次元データintegral_imageを用いて肌色のピクセル数を求めcount[x,y]に入れる
array<int,2>^ count=gcnew array<int,2>(WIDTH,HEIGHT);
for(y=0;y<HEIGHT-F_HEIGHT;y++)
for(x=0;x<WIDTH-F_WIDTH;x++)
count[x,y]=0;
for(y=0;y<HEIGHT-F_HEIGHT;y++)
for(x=0;x<WIDTH-F_WIDTH;x++){
skin_count=integral_image[x,y]+integral_image[x+F_WIDTH,y+F_HEIGHT]-integral_image[x+F_WIDTH,y]-integral_image[x,y+F_HEIGHT];
if(skin_count>267) count[x,y]=skin_count; //フレーム内ピクセル数の40%を超えれば
}
for(n=0;n<FACES_MAX;n++){
//肌色のカウント数最大のフレームの位置x_max,y_maxを求める
counter_max=0;
for(y=0;y<HEIGHT;y++)
for(x=0;x<WIDTH;x++){
if(count[x,y]>counter_max){
counter_max=count[x,y];
x_max=x;
y_max=y;
}
}
if(counter_max<400) break; //フレーム内ピクセル数の60%未満は顔と認識しない
//座標位置を格納する
candidate[n]=gcnew Point(x_max,y_max);
//周辺を削除する
for(p=-15;p<=15;p++)
for(q=-15;q<=15;q++){
xx=x_max+q;
if(xx<0) xx=0;
if(xx>=WIDTH) xx=WIDTH-1;
yy=y_max+p;
if(yy<0) yy=0;
if(yy>=HEIGHT) yy=HEIGHT-1;
count[xx,yy]=0;
}
//白黒画像bmap_skin上に顔候補のフレームを描く
gr->DrawRectangle(Pens::Red,X1+x_max,Y0+y_max,F_WIDTH,F_HEIGHT);
}
candidate_count=n;
//------------------------- 対称検出処理 -----------------------------
for(i=0;i<candidate_count;i++){
x=candidate[i]->X;
y=candidate[i]->Y;
for(j=0;j<9;j++){
xx=x+shift[j];
diff=0;
for(q=0;q<F_HEIGHT;q++)
for(p=5;p<F_WIDTH/2;p++){ //左右各5ピクセルは調べない
color1=bmap_gray->GetPixel(xx+p,y+q); //左から
color2=bmap_gray->GetPixel(xx+F_WIDTH-1-p,y+q); //右から
diff+=Math::Abs(color1.R-color2.R);
}
if(diff<5700){ //ピクセル1対当たり平均濃度差約30未満を顔と判定
//グレイ画像bmap_gray上に新たな判定結果のフレームを描く
gr->DrawRectangle(Pens::Lime,X0+xx,Y1+y,F_WIDTH,F_HEIGHT);
break;
}
}
}
}
//画像をピクセルデータ化する(「易しい画像処理(9)」を参考)
private: array<int,2>^ getPixelDataFromBitmap(Bitmap^ bmap){
int width=bmap->Width;
int height=bmap->Height;
Color color1;
array<int,2>^ p_data=gcnew array<int,2>(width,height);
for(int j=0;j<height;j++)
for(int i=0;i<width;i++){
color1=bmap->GetPixel(i,j);
if(color1.R<128) p_data[i,j]=0; //黒は0に
else p_data[i,j]=1; //白は1に
}
return p_data;
}
//ピクセルデータから積分形二次元配列データを求める(「易しい画像処理(9)」より)
private: array<int,2>^ createIntegralImageFromPixelData(array<int,2>^ p_data){
array<int,2>^ integral=gcnew array<int,2>(WIDTH,HEIGHT);
int sum=0;
//一番上の行
for(int i=1;i<WIDTH;i++){
sum+=p_data[i,0];
integral[i,0]=sum;
}
//二番目以降の行
for(int j=1;j<HEIGHT;j++){
sum=0;
for(int i=0;i<WIDTH;i++){
sum+=p_data[i,j];
integral[i,j]=integral[i,j-1]+sum;
}
}
return integral;
}