Mark Rect

検索するパラメータを設定して Land Mark を検出します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

Land Mark

  1. 顔画像の識別は、果物のように色やサイズで識別することは出来ないので、パーツの相対位置や大きさや特徴を利用します。
    パーツの位置や特徴を検出するのに、口や目や眉毛の座標を表す Land Mark を使用します。
    このプログラムでランドマークを検出する検索パラメータ(矩形領域)を設定します。
    Face Image でサイズを整えた画像(鼻の座標を中心に 256*256 のサイズ)を入力します。
    鼻の座標は 128,128 で眉間の座標は 128,192 に調整されています。
    ランドマークの検出は「二値画像または、エッジ検出の画像」を使うのですが、今回はエッジ検出の画像を使います。
  2. プログラムの操作法
    1. プログラムを起動して 256*256 にサイズに整えた画像を入力します。
      画像に重ねて、検索パラメータの矩形が表示されます。
    2. 左クリックで閾値をアップしながら、エッジ検出画像の最適値を求めます。
    3. 右クリックで画像に重ねてランドマークが描画されます。
    4. 座標が合えば良いのですが、合わない時は検索パラメータを修正して矩形を調整します。
    5. 別の画像でも試して、最適なパラメータ(矩形領域)を設定して下さい。
  3. このプログラムで探したランドマークの検出パラメータです。
    「下唇と口の左右と左目と右目の内側」の検索パラメータを設定してみました。
    座標は、左下を起点にした BMP 座標系です。(ディスプレイ系とは上下が反転)
        int     mkt[5][5] =             //Mark 検索パラメータ(BMP 座標)
        { { 123,55,133,95,0 },
        { 72,80,96,120,2 },{ 160,80,184,120,3 },
        { 98,172,112,202,3 },{ 144,172,158,202,2 } };
    
    パラメータ 説明
    123,55 矩形領域の左下座標
    133,95 矩形領域の右上座標
    0 検索の方向(0:上, 1:下, 2:右, 3:左)
  4. Main.cpp の AppInit() から OpenFile() で画像ファイル(256*256 で切り出したカラー画像)を選択します。
    InitDib() で DibStruct を初期化して下さい。
    DrawRect() 関数で検索パラメータの矩形を画像に重ねて表示します。
    DrawRect() 関数は Binary Mode に掲載しています。
    WM_PAINT で描画する画像は Dib.hMaskDC の画像です。
    // 画像をロードして、24bit DIB を作成
    LRESULT  AppInit(HWND hwnd)
    {
        Face= new FACE(hwnd);
        if (Face->OpenFile()==false)    return false;
        Face->Adjust(false);
        Face->InitDib();
        Face->Show(Face->Dib.hBmpDC, 0, 0);
        Face->DrawRect();
        Face->ShowDib(Face->Dib.hMaskDC, 0, 0);
        return true;
    }
    
  5. WM_PAINT では ShowMask() で Dib.hMaskDC の画像を描画します。
            case WM_PAINT:          // Dib.hMaskDC の画像を描画
                hdc = BeginPaint(hWnd,&ps);
                if (Face)   Face->ShowMask(hdc,0,0);
                EndPaint(hWnd, &ps);
                break;
    
  6. WM_LBUTTONDOWN では、閾値をアップしながら Edge() 関数でマスク画像を作成します。
    画像を確認しながら、適当なところで止めて下さい。
            case WM_LBUTTONDOWN:    // 左ボタンをクリック
                val= val+1000;
                Face->Show(Face->Dib.hBmpDC, 0, 0);
                Face->Edge(val);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  7. WM_RBUTTONDOWN では検索パラメータ(矩形)とマスク画像を使ってランドマークを検出します。
    表示された座標を確認して、最適な値になるように検索パラメータを設定して下さい。
            case WM_RBUTTONDOWN:    // 右ボタンをクリック
                Face->Show(Face->Dib.hBmpDC, 0, 0);
                Face->LandMark();
                Face->ShowDib(Face->Dib.hMaskDC, 0, 0);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  8. マークを検出する LandMK() 関数です。
    検索の方向が 0(上),1(下) のときはX座標は中央から左右交互に、2(右).3(左)のときはY座標は中央から上下交互に調べます。
    見つけたマークの座標を POINT pt; に格納します。
    // hMask の画像から t[5] の範囲で Land Mark を検出する
    // t[0],t[1]=左下座標(X,Y),  t[2],t[3]=右上座標(X,Y)
    void  FACE::LandMK(int t[5])
    {   int w,h,cx,cy,x,y,x1,x2,y1,y2;
    
        pt.x = pt.y = 0;
        w= t[2]-t[0];
        h= t[3]-t[1];
        cx= t[0]+w/2;
        cy= t[1]+h/2;
    
        switch(t[4])
        {   case 0:     //上方向
                for(y=0; y<h; y++)
                    for(x=0; x<w/2; x++)
                    {
                        y1= t[1]+y;
                        x1= cx+x;
                        x2= cx-x;
                        if (MASK(y1,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y1;
                            return;
                        }
                        if (MASK(y1,x2,0)==0)
                        {   pt.x= x2;
                            pt.y= y1;
                            return;
                        }
                    }
                break;
            case 1:     //下方向
                for(y=0; y<h; y++)
                    for(x=0; x<w/2; x++)
                    {
                        y1= t[3]-y;
                        x1= cx+x;
                        x2= cx-x;
                        if (MASK(y1,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y1;
                            return;
                        }
                        if (MASK(y1,x2,0)==0)
                        {   pt.x= x2;
                            pt.y= y1;
                            return;
                        }
                    }
                break;
            case 2:     //右方向
                for(x=0; x<w; x++)
                    for(y=0; y<h/2; y++)
                    {
                        x1= t[0]+x;
                        y1= cy+y;
                        y2= cy-y;
                        if (MASK(y1,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y1;
                            return;
                        }
                        if (MASK(y2,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y2;
                            return;
                        }
                    }
                break;
            case 3:     //左方向
                for(x=0; x<w; x++)
                    for(y=0; y<h/2; y++)
                    {
                        x1= t[2]-x;
                        y1= cy+y;
                        y2= cy-y;
                        if (MASK(y1,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y1;
                            return;
                        }
                        if (MASK(y2,x1,0)==0)
                        {   pt.x= x1;
                            pt.y= y2;
                            return;
                        }
                    }
                break;
        }
    }
    

[Next Chapter ↓] 顔画像の識別
[Previous Chapter ↑] エッジ検出

超初心者のプログラム入門(Win32API C++)