エッジ検出

カラー画像から隣り合っているピクセルの濃淡の違いを検出して、エッジ画像を作成します。

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

エッジを検出する

  1. エッジ画像は、二値画像と共に画像を解析するときに役に立ちます。
    顔の画像解析に使用する Face Class は Binary Mode に掲載しています。
    IMAGE AI Object では、画像の幅は8ビットの境界に合わせましたが、今回は画像サイズは問いません。
    二値画像では Dib.hBmpDC は使いませんでしたが、エッジの検出では隣接するピクセルの値を調べるために参照します。
  2. Main.cpp の AppInit() から OpenFile() で画像ファイルを選択します。
    InitDib() で DibStruct を初期化して下さい。
    エッジを検出するときに Dib.hBmpDC を参照するので、入力した画像を転送します。
    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.hBmpDC, 0, 0);
        Face->ShowDib(Face->Dib.hMaskDC, 0, 0);
        return true;
    }
    
  3. WM_PAINT では ShowMask() で Dib.hMaskDC の画像を描画します。
    描画する画像は、入力した画像または、Edge() 関数でエッジを検出した画像です。
            case WM_PAINT:          // Dib の画像を描画
                hdc = BeginPaint(hWnd,&ps);
                if (Face)   Face->ShowMask(hdc,0,0);
                EndPaint(hWnd, &ps);
                break;
    
  4. WM_LBUTTONDOWN では、val をアップしながら Edge() 関数を呼びます。
    Edge() 関数では Dib.hBmpDC を参照しながら Dib.hMaskDC にエッジを検出した画像を作成します。
    int     val= 5000;      // 閾値
    
            case WM_LBUTTONDOWN:    // 左ボタンをクリック
                val= val+1000;
                Face->Show(Face->Dib.hBmpDC, 0, 0);
                Face->Edge(val);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  5. WM_RBUTTONDOWN では、val を最初の値に戻して入力した画像に設定します。
            case WM_RBUTTONDOWN:    // 右ボタンをクリック
                val= 5000;
                Face->ShowDib(Face->Dib.hMaskDC, 0, 0);
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    
  6. 画像から隣り合っているピクセルの濃淡の違いを検出して、エッジ画像を作成します。
    エッジの検出は Sobel(ソーベル、またはゾーベル)フィルタと呼ばれる濃淡の違いを検出する手法があります。
    濃淡の違いを調べるピクセルを中心において、8方向の濃さを調べます。
    横方向の濃さを次のウエイトを掛けて演算します。
    1 0 -1
    2 0 -2
    1 0 -1
    同様に縦方向の濃さを次のウエイトを掛けて演算します。
    1 2 1
    0 0 0
    -1 -2 -1
    ウエイトの配列と、方向を決めるベクトルを Face.h で定義しています。
        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 };
    
  7. 濃淡の違いを検出して、エッジ画像を作成する Edge() 関数です。
    指定された座標のピクセルと隣接するピクセルの濃淡の違いを Sobel() 関数で調べます。
    両端のピクセルは対象から外します。
    // カラー画像(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;
            }
    }
    
  8. 横方向と縦方向の濃さを調べる Sobel() 関数です。
    三原色それぞれの濃淡の違いを調べます。
    // 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;
    }
    
  9. C#でも同様のプログラム エッジ検出 を作成しています。
    実行速度が気になるのですが、こちらの方が解り易いかも知れません。

[Next Chapter ↓] Mark Rect
[Previous Chapter ↑] Binary Mode

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