Binary Mode

カラー画像を二値画像に変換します。

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

Face Class

  1. 顔の画像解析に使用する専用の Face Class のヘッダーファイル Face.h です。
    //★ Face Class Header File  前田 稔
    #ifndef     _Face
    #define     _Face
    #include    <windows.h>
    #include    <math.h>
    #pragma     once
    #define     SAFE_DELDC(p)   { if (p) { DeleteDC (p);   (p)=NULL; } }
    #define     SAFE_DELOBJ(p)  { if (p) { DeleteObject(p); (p)=NULL; } }
    #define     MAP(y,x,c)  (Dib.Data[(y*m_Wbyte+x)*3+c])
    #define     MASK(y,x,c) (Dib.Mask[(y*m_Wbyte+x)*3+c])
    
    typedef struct
    {   HBITMAP         hBmp;
        HDC             hBmpDC;
        LPBYTE          Data;           // 24bit DIB の BITMAP DATA
        HBITMAP         hMask;
        HDC             hMaskDC;
        LPBYTE          Mask;           // 24bit Mask の BITMAP DATA
    }   DibStruct;
    
    class FACE
    { protected:
        HWND        hWnd;
        HBITMAP     hBmp;               //画像の HBITMAP
        HDC         hBmpDC;             //画像の HDC
    
      public:
        int         m_Width;            //画像の幅
        int         m_Height;           //画像の高さ
        int         m_Wbyte;            //幅のバイト数
        DibStruct   Dib;                //RGB 画像解析の構造体
        char        szFile[MAX_PATH];   //オープンするファイル名(パス付き)
    
        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 } };
        POINT   pt_t[5];                //Mark Table
        POINT   pt;                     //Mark Point
    
        int     vect[9][2] =            //エッジ検出テーブル
        { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,0}, {0,1}, {1,-1}, {1,0}, {1,1} };
        int     hCoef[9] = { 1, 0, -1,  2, 0, -2,  1, 0, -1 };
        int     vCoef[9] = { 1, 2, 1,  0, 0, 0,  -1, -2, -1 };
    
        FACE(HWND hWnd);                //Constructor
        virtual ~FACE();                //Destructor
        HRESULT     LoadBmp(LPSTR szBitmap);
    
        //入力画像を BitBlt() で描画(転送)する
        HRESULT     Show(HDC hdc,int x,int y,int w,int h,int xd,int yd);
        HRESULT     Show(HDC hdc,int x,int y)
                    {  return Show(hdc,x,y,m_Width,m_Height,0,0);  }
    
        //Dib.hBmpDC を BitBlt() で転送する
        HRESULT     ShowDib(HDC hdc, int x, int y);
    
        //Dib.hMaskDC を BitBlt() で転送する
        HRESULT     ShowMask(HDC hdc, int x, int y);
    
        //Dib Function
        void        InitDib();
        void        Mark(int xp, int yp);
        void        DrawRect();
        void        Rect(int t[5]);
        void        BinMode(int val);
        void        Edge(int val);
        BOOL        Sobel(int x, int y, int val);
        void        LandMark();
        void        LandMK(int t[5]);
    
        BOOL        OpenFile();
        void        Adjust(BOOL flag=TRUE);
    };
    
    #endif
    
  2. Face Class のプログラムファイル Face.cpp のソースコードです。
    // Face Class  Program File  前田 稔
    #include    "Face.h"
    
    void  Debug(char *msg, int v)
    {   char    str[80];
        wsprintf(str, "%s=%d\n", msg, v);
        OutputDebugString(str);
    }
    void  Debug(int x, int y)
    {   char    str[80];
        wsprintf(str, "x:%d  y:%d\n", x, y);
        OutputDebugString(str);
    }
    void  VarMsg(HWND hwnd, char *msg, int n)
    {   char    str[80];
        wsprintf(str,"%s=%d\n",msg,n);
        SetWindowText(hwnd,(LPCSTR)str);
    }
    void  VarMsg(HWND hwnd, int x, int y)
    {   char    str[80];
        wsprintf(str, "x:%d  y:%d", x, y);
        SetWindowText(hwnd, (LPCSTR)str);
    }
    
    // FaceCor Class 関数
    // コンストラクタ(オブジェクトの初期化)
    FACE::FACE(HWND hwnd)
    {   hWnd= hwnd;
        hBmp= NULL;
        hBmpDC= NULL;
        Dib.hBmp = NULL;
        Dib.hBmpDC = NULL;
        Dib.hMask = NULL;
        Dib.hMaskDC = NULL;
        m_Width = m_Height = 0;
        szFile[0]= '\0';
    }
    
    // デストラクタ(オブジェクトの終了)
    FACE::~FACE()
    {   SAFE_DELDC(Dib.hBmpDC);
        SAFE_DELDC(Dib.hMaskDC);
        SAFE_DELDC(hBmpDC);
        SAFE_DELOBJ(Dib.hBmp);
        SAFE_DELOBJ(Dib.hMask);
        SAFE_DELOBJ(hBmp);
    }
    
    //★ BMP File のロード
    HRESULT  FACE::LoadBmp(LPSTR szBitmap)
    {   HDC         hdc;
        BITMAP      bmp;
    
        SAFE_DELDC(Dib.hBmpDC);
        SAFE_DELDC(Dib.hMaskDC);
        SAFE_DELDC(hBmpDC);
        SAFE_DELOBJ(Dib.hBmp);
        SAFE_DELOBJ(Dib.hMask);
        SAFE_DELOBJ(hBmp);
        m_Width = m_Height = 0;
    
        hBmp= (HBITMAP)LoadImage(NULL,szBitmap,IMAGE_BITMAP,0,0,
                                 LR_LOADFROMFILE|LR_CREATEDIBSECTION);
        if (hBmp==NULL)
        {   MessageBox(NULL,szBitmap,"Load BMP Error",MB_OK);
            return  FALSE;
        }
        hdc= GetDC(hWnd);
        hBmpDC= CreateCompatibleDC(hdc);
        SelectObject(hBmpDC, hBmp);
    
        // サイズを保存
        GetObject(hBmp,sizeof(BITMAP),&bmp);
        m_Width= bmp.bmWidth;
        m_Height= bmp.bmHeight;
        m_Wbyte = (m_Width + 7) & 0xfffffff8;
        return TRUE;
    }
    
    //★ BMP 画像に合わせて 24bit Dib を初期化する
    void  FACE::InitDib()
    {   HDC      hDC;
        LPBITMAPINFO    Info;   // BITMAP 構造体のヘッダー
    
        hDC = GetDC(hWnd);
        Info = (LPBITMAPINFO)GlobalAlloc(GPTR, sizeof(BITMAPINFO));
        Info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        Info->bmiHeader.biWidth = m_Wbyte;
        Info->bmiHeader.biHeight = m_Height;
        Info->bmiHeader.biPlanes = 1;
        Info->bmiHeader.biBitCount = 24;        // 24 bit
        Info->bmiHeader.biCompression = BI_RGB; // 非圧縮
    
        Dib.hBmp = CreateDIBSection(hDC, Info, 0, (void**)&Dib.Data, NULL, 0);
        Dib.hBmpDC = CreateCompatibleDC(hDC);
        SelectObject(Dib.hBmpDC, Dib.hBmp);
        Dib.hMask = CreateDIBSection(hDC, Info, 0, (void**)&Dib.Mask, NULL, 0);
        Dib.hMaskDC = CreateCompatibleDC(hDC);
        SelectObject(Dib.hMaskDC, Dib.hMask);
    
        GlobalFree(Info);
        ReleaseDC(hWnd, hDC);
    }
    
    //★ 入力画像を描画(転送)する
    HRESULT  FACE::Show(HDC hdc,int x,int y,int w,int h,int xd,int yd)
    {   if (hBmpDC==NULL)   return FALSE;
        BitBlt(hdc,x,y,w,h,hBmpDC,xd,yd,SRCCOPY);
        return TRUE;
    }
    
    // Dib.hBmpDC を BitBlt() で転送する
    HRESULT  FACE::ShowDib(HDC hdc, int x, int y)
    {   if (Dib.hBmpDC==NULL)   return FALSE;
        BitBlt(hdc, x, y, m_Width, m_Height, Dib.hBmpDC, 0, 0, SRCCOPY);
        return TRUE;
    }
    
    // Dib.hMaskDC を BitBlt() で転送する
    HRESULT  FACE::ShowMask(HDC hdc, int x, int y)
    {   if (Dib.hMaskDC==NULL)  return FALSE;
        BitBlt(hdc, x, y, m_Width, m_Height, Dib.hMaskDC, 0, 0, SRCCOPY);
        return TRUE;
    }
    
    // Dib.hBmp にマークを表示する(x,y: BMP 座標)
    void  FACE::Mark(int x, int y)
    {   int     xw, yw, i;
    
        if (x==0 || y==0)   return;
    VarMsg(hWnd, x, y);
        for (i = 0; i<9; i++)
        {   xw = x + vect[i][0];
            yw = y + vect[i][1];
            MAP(yw, xw, 0) = 0;
            MAP(yw, xw, 1) = 0;
            MAP(yw, xw, 2) = 0;
            xw += vect[i][0];
            yw += vect[i][1];
            MAP(yw, xw, 0) = 0;
            MAP(yw, xw, 1) = 0;
            MAP(yw, xw, 2) = 0;
        }
    }
    
    // Dib.hBmp に検索矩形を表示する(BMP座標⇒ディスプレイ座標)
    void  FACE::DrawRect()
    {   int     t[5];
        for(int i=0; i<5; i++)
        {   t[0]= mkt[i][0];
            t[1]= (m_Height-1) - mkt[i][1];
            t[2]= mkt[i][2];
            t[3]= (m_Height-1) - mkt[i][3];
            t[4]= mkt[i][4];
            Rect(t);
        }
    }
    
    // Dib.hBmpDC に矩形を描画(ディスプレイ座標で受け取る)
    void  FACE::Rect(int t[5])
    {   HPEN    hPen;
        int     old;
    
        old= SetROP2(Dib.hBmpDC, R2_NOT);
        hPen = CreatePen(PS_SOLID,1,RGB(0,0,0));
        SelectObject(Dib.hBmpDC, hPen);
        MoveToEx(Dib.hBmpDC, t[0], t[1], NULL);
        LineTo(Dib.hBmpDC, t[2], t[1]);
        LineTo(Dib.hBmpDC, t[2], t[3]);
        LineTo(Dib.hBmpDC, t[0], t[3]);
        LineTo(Dib.hBmpDC, t[0], t[1]);
        DeleteObject(hPen);
        SetROP2(Dib.hBmpDC, old);
    }
    
    // Image 画像を Dib.hMaskDC に転送して Binary Mode に変換する
    void  FACE::BinMode(int val)
    {   int     x, y;
        int     wk;
    
        if (Dib.hMaskDC == NULL)    return;
        Show(Dib.hMaskDC, 0, 0);
    VarMsg(hWnd,"VAL:",val);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Wbyte; x++)
            {   wk= (MASK(y,x,2)*2 + MASK(y,x,1)*4 + MASK(y,x,0)) / 7;
                if (wk<val)
                {   MASK(y,x,0)= 0;
                    MASK(y,x,1)= 0;
                    MASK(y,x,2)= 0;
                }
                else
                {   MASK(y,x,0)= 255;
                    MASK(y,x,1)= 255;
                    MASK(y,x,2)= 255;
                }
            }
    }
    
    // カラー画像(Dib.hBmpDC)を参照して Dib.hMask にエッジ画像を作成
    void  FACE::Edge(int val)
    {   int     x, y;
        int     ans;
    
        if (Dib.hMaskDC == NULL)    return;
    VarMsg(hWnd, "VAL:", val);
        for (y = 1; y<m_Height - 1; y++)
            for (x = 1; x<m_Wbyte - 1; x++)
            {
                ans = 255;
                if (Sobel(x, y, val))   ans = 0;
                MASK(y, x, 0) = ans;
                MASK(y, x, 1) = ans;
                MASK(y, x, 2) = ans;
            }
    }
    
    // Dib.hBmpDC の周辺のピクセルを調べてエッジを検出する
    BOOL  FACE::Sobel(int x, int y, int val)
    {   int     fx, fy, c, i, dx, dy;
    
        for(c=0; c<3; c++)
        {   fx = 0;
            for (i = 0; i<9; i++)
            {   dx = x + vect[i][0];
                dy = y + vect[i][1];
                fx += MAP(dy,dx,c)*hCoef[i];
            }
            fy = 0;
            for (i = 0; i<9; i++)
            {   dx = x + vect[i][0];
                dy = y + vect[i][1];
                fy += MAP(dy,dx,c)*vCoef[i];
            }
            if (fx*fx+fy*fy > val)  return true;
        }
        return false;
    }
    
    // Mark 検索パラメータを使って pt_t[5] にマーク座標を格納する
    void  FACE::LandMark()
    {   for(int n=0; n<5; n++)
        {   LandMK(mkt[n]);
            pt_t[n]= pt;
            Mark(pt.x, pt.y);
        }
        Mark(128, 128);
        Mark(128, 192);
    }
    
    // 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;
        }
    }
    
    // Window Size を画像に合わす
    void  FACE::Adjust(BOOL flag)
    {   RECT    rc;
        int     x,y;
    
        GetWindowRect(hWnd,&rc);
        x= rc.left;
        y= rc.top;
        rc.right= x + m_Width;
        rc.bottom= y + m_Height;
        AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,flag);
        MoveWindow(hWnd,x,y,rc.right-rc.left,rc.bottom-rc.top,TRUE);
    }
    
    // OPENFILENAME 構造体で画像ファイルを Load する
    BOOL  FACE::OpenFile()
    {   OPENFILENAME    ofn;
    
        szFile[0]= '\0';
        memset(&ofn,0,sizeof(OPENFILENAME));
        ofn.lStructSize= sizeof(OPENFILENAME);
        ofn.hwndOwner= hWnd;
        ofn.lpstrFilter= "bmp(*.bmp)\0*.bmp\0All files(*.*)\0*.*\0\0";
        ofn.lpstrFile= szFile;
        ofn.lpstrFileTitle= NULL;
        ofn.nMaxFile= MAX_PATH;
        ofn.Flags= OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
        ofn.lpstrDefExt= "gif";
        ofn.lpstrTitle= "ファイルオープン!";
        if (GetOpenFileName(&ofn)!=TRUE)    return FALSE;
        LoadBmp(szFile);
        return TRUE;
    }
    

二値画像に変換

  1. 二値画像は画像を解析するときに役に立ちます。
    IMAGE AI Object では、画像の幅は8ビットの境界に合わせましたが、今回は画像サイズは問いません。
    画像は Dib.hMaskDC に直接転送して変換するので Dib.hBmpDC は使いません。
    Main.cpp の AppInit() から OpenFile() で画像ファイルを選択します。
    InitDib() で DibStruct を初期化して下さい。
    WM_PAINT では Dib.hMaskDC から描画するので入力した画像を 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.hMaskDC, 0, 0);
        return true;
    }
    
  2. WM_PAINT では ShowMask() で Dib.hMaskDC の画像を描画します。
    描画する画像は、入力した画像または、BinMode() 関数で二値に変換された画像です。
            case WM_PAINT:          // Dib の画像を描画
                hdc = BeginPaint(hWnd,&ps);
                if (Face)   Face->ShowMask(hdc,0,0);
                EndPaint(hWnd, &ps);
                break;
    
  3. WM_LBUTTONDOWN では、val をアップしながら二値画像に変換します。
    int     val= 30;        // 閾値
    
            case WM_LBUTTONDOWN:    // 左ボタンをクリック
                val= val+2;
                Face->BinMode(val);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  4. WM_RBUTTONDOWN では、val を最初の値に戻して入力した画像に設定します。
            case WM_RBUTTONDOWN:    // 右ボタンをクリック
                val= 30;
                Face->Show(Face->Dib.hMaskDC, 0, 0);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  5. カラー画像を二値画像に変換する BinMode() 関数です。
    入力したカラー画像を Dib.hMask に転送して、白黒二値の画像に変換します。
    RGB を単純に加えて3で割る方法では、色に対する明るさが考慮されていません。
    そこでウエイトを掛けて演算をしています。
    val は二値画像の閾値(しきいち)で値が大きくなるほど、黒のピクセルが増えます。
    渡された閾値をタイトルバーに表示しています。
    // Image 画像を Dib.hMaskDC に転送して Binary Mode に変換する
    void  FACE::BinMode(int val)
    {   int     x, y;
        int     wk;
    
        if (Dib.hMaskDC == NULL)    return;
        Show(Dib.hMaskDC, 0, 0);
    VarMsg(hWnd,"VAL:",val);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Wbyte; x++)
            {   wk= (MASK(y,x,2)*2 + MASK(y,x,1)*4 + MASK(y,x,0)) / 7;
                if (wk<val)
                {   MASK(y,x,0)= 0;
                    MASK(y,x,1)= 0;
                    MASK(y,x,2)= 0;
                }
                else
                {   MASK(y,x,0)= 255;
                    MASK(y,x,1)= 255;
                    MASK(y,x,2)= 255;
                }
            }
    }
    

[Next Chapter ↓] エッジ検出
[Previous Chapter ↑] Face Image

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