エッジ検出

グレースケールに変換してエッジを検出します。

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

ImageAI Class Ver-3

  1. ImageAI Class Ver-3 ではマスク画像を使います。
    画像解析に使用する専用の ImageAI Class 3 のヘッダーファイル ImageAI.h です。
    DibStruct に hMask を追加して、マスクを作成する Set_Mask() 関数を追加しています。
    //★ IMAGE_AI_3 Class Header File  前田 稔
    #ifndef     _ImageAI
    #define     _ImageAI
    #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_Width+x)*3+c])
    #define     MASK(y,x,c) (Dib.Mask[(y*m_Width+x)*3+c])
    #define     GRAY(y,x)   (Gray[y*m_Width+x])
    
    typedef struct
    {   HBITMAP         hBmp;
        HDC             hBmpDC;
        LPBYTE          Data;           // 24bit DIB の BITMAP DATA
        HBITMAP         hMask;
        HDC             hMaskDC;
        LPBYTE          Mask;           // 24bit Mask の BITMAP DATA
    }   DibStruct;
    
    class IMAGEAI
    { protected:
        HWND        hWnd;
        HBITMAP     hBmp;               //画像の HBITMAP
        HDC         hBmpDC;             //画像の HDC
    
      public:
        long        m_Width;            //画像の幅
        long        m_Height;           //画像の高さ
        DibStruct   Dib;                //RGB 画像解析の構造体
        char        szFile[MAX_PATH];   //オープンするファイル名(パス付き)
        LPBYTE      Gray;               //Gray Image 
        byte        low[3];             //Color の閾値
        byte        high[3];
        int         rgb[3];             //RGB Color(%)
        int         width, height;      //物体の幅と高さ       
    
        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 };
    
        IMAGEAI(HWND hWnd);             //Constructor
        virtual ~IMAGEAI();             //Destructor
        HRESULT     LoadBmp(LPSTR szBitmap);
    
        //BitBlt() で描画する
        HRESULT     Show(HDC hdc,long x,long y,long w,long h,long xd,long yd);
        HRESULT     Show(HDC hdc,long x,long y)
                    {  return Show(hdc,x,y,m_Width,m_Height,0,0);  }
    
        //StretchBlt() で描画する
        HRESULT     Show(HDC hdc,long x,long y,long w,long h,long sx,long sy,long sw,long sh);
        HRESULT     Show(HDC hdc,POINTS beg,POINTS end);
    
        //Dib で加工した画像を描画する
        HRESULT     ShowDib(HDC hdc, long x, long y);
        HRESULT     ShowMask(HDC hdc, long x, long y);
        HRESULT     AndMask(HDC hdc, long x, long y);
    
        //Dib Function
        void        InitDib();
        void        Grayscale();
        void        BinMode(WORD val);
        void        Sobel(WORD wd);
        void        SetRGB(int mode);
        void        Light(WORD val);
        void        Del_Color(int x, int y, WORD wd);
        void        Sel_Color(int x, int y, WORD wd);
        void        Sum_Color();
        void        Get_Size();
        void        Set_Mask(WORD xp, WORD yp, WORD wd);
        void        SetLimit(int x, int y, WORD wd);
        bool        ChkPic(int x, int y);
        void        Pixcel(int x, int y);
        bool        OutPic(int x, int y);
        int         Coef(int x, int y, int tbl[9]);
        void        GetPixel(WORD x, WORD y);
    
        BOOL        OpenFile();
        void        Adjust(BOOL flag=TRUE);
    };
    
    #endif
    
  2. ImageAI Class のプログラムファイル ImageAI.cpp のソースコードです。
    // IMAGE_AI_3 Object Class Library  前田 稔
    #include    "ImageAI.h"
    
    void  Debug(char *msg, WORD v)
    {
        char    str[80];
        wsprintf(str, "%s=%d\n", msg, v);
        OutputDebugString(str);
    }
    void  Debug(char *msg, int v)
    {
        char    str[80];
        wsprintf(str, "%s=%d\n", msg, v);
        OutputDebugString(str);
    }
    void  Debug(int sum[3])
    {
        char    str[80];
        wsprintf(str, "B:%d  G:%d  R:%d\n", sum[0], sum[1], sum[2]);
        OutputDebugString(str);
    }
    void  VarMsg(HWND hwnd, char *msg, WORD n)
    {   char    str[80];
        wsprintf(str,"%s=%d\n",msg,n);
        SetWindowText(hwnd,(LPCSTR)str);
    }
    
    // IMAGE Object Class 関数
    // コンストラクタ(オブジェクトの初期化)
    IMAGEAI::IMAGEAI(HWND hwnd)
    {   hWnd= hwnd;
        hBmp= NULL;
        hBmpDC= NULL;
        Dib.hBmp = NULL;
        Dib.hBmpDC = NULL;
        Dib.hMask = NULL;
        Dib.hMaskDC = NULL;
        Gray = NULL;
        m_Width = m_Height = 0;
        szFile[0]= '\0';
    }
    
    // デストラクタ(オブジェクトの終了)
    IMAGEAI::~IMAGEAI()
    {
        if (Gray)   delete Gray;
        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  IMAGEAI::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;
        return TRUE;
    }
    
    //★ 画像を描画する
    HRESULT  IMAGEAI::Show(HDC hdc,long x,long y,long w,long h,long xd,long yd)
    {   if (hBmpDC==NULL)   return FALSE;
        BitBlt(hdc,x,y,w,h,hBmpDC,xd,yd,SRCCOPY);
        return TRUE;
    }
    
    HRESULT  IMAGEAI::Show(HDC hdc,long x,long y,long w,long h,long sx,long sy,long sw,long sh)
    {   if (hBmpDC==NULL)   return FALSE;
        StretchBlt(hdc,x,y,w,h,hBmpDC,sx,sy,sw,sh,SRCCOPY);
        return TRUE;
    }
    
    HRESULT  IMAGEAI::Show(HDC hdc,POINTS beg,POINTS end)
    {   if (hBmpDC==NULL)   return FALSE;
        StretchBlt(hdc,0,0,m_Width,m_Height,hBmpDC,beg.x,beg.y,end.x-beg.x,end.y-beg.y,SRCCOPY);
        return TRUE;
    }
    
    // Dib 構造体を使って処理した画像を描画する 
    HRESULT  IMAGEAI::ShowDib(HDC hdc, long x, long y)
    {
        if (Dib.hBmpDC == NULL) return FALSE;
        BitBlt(hdc, x, y, m_Width, m_Height, Dib.hBmpDC, 0, 0, SRCCOPY);
        return TRUE;
    }
    
    // Mask 画像を描画する 
    HRESULT  IMAGEAI::ShowMask(HDC hdc, long x, long y)
    {
        if (Dib.hMaskDC == NULL)    return FALSE;
        BitBlt(hdc, x, y, m_Width, m_Height, Dib.hMaskDC, 0, 0, SRCCOPY);
        return TRUE;
    }
    
    // 画像の上から Mask 画像を重ねる 
    HRESULT  IMAGEAI::AndMask(HDC hdc, long x, long y)
    {
        if (Dib.hMaskDC == NULL)    return FALSE;
        BitBlt(hdc, x, y, m_Width, m_Height, Dib.hMaskDC, 0, 0, SRCAND);
        return TRUE;
    }
    
    //★ BMP 画像に合わせて 24bit Dib を初期化する
    void  IMAGEAI::InitDib()
    {   HDC      hDC;
        LPBITMAPINFO    Info;   //BITMAP 構造体のヘッダー
    
        hDC = GetDC(hWnd);
        Info = (LPBITMAPINFO)GlobalAlloc(GPTR, sizeof(BITMAPINFO));
        Info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        Info->bmiHeader.biWidth = m_Width;
        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);
    }
    
    // Image 画像を Dib.hBmp に転送して Grayscale に変換する
    void  IMAGEAI::Grayscale()
    {   int     x, y;
        WORD    wk;
    
        Show(Dib.hBmpDC, 0, 0);
        if (Gray)   delete Gray;
        Gray = new BYTE[m_Height*m_Width];
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
            {
                wk= (MAP(y,x,2)*2 + MAP(y,x,1)*4 + MAP(y,x,0)) / 7;
                MAP(y,x,0)= (BYTE)wk;
                MAP(y,x,1)= (BYTE)wk;
                MAP(y,x,2)= (BYTE)wk;
                GRAY(y,x)= (BYTE)wk;
            }
    }
    
    // Image 画像を Dib.hBmp に転送して Binary Mode に変換する
    void  IMAGEAI::BinMode(WORD val)
    {   int     x, y;
        WORD    wk;
    
        Show(Dib.hBmpDC, 0, 0);
    VarMsg(hWnd,"WORD VAL:",val);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
            {
                wk= (MAP(y,x,2)*2 + MAP(y,x,1)*4 + MAP(y,x,0)) / 7;
                if (wk<val)
                {   MAP(y,x,0)= 0;
                    MAP(y,x,1)= 0;
                    MAP(y,x,2)= 0;
                }
                else
                {   MAP(y,x,0)= 255;
                    MAP(y,x,1)= 255;
                    MAP(y,x,2)= 255;
                }
            }
    }
    
    // Image 画像を Dib.hBmp に転送して mode の色に設定する関数
    void  IMAGEAI::SetRGB(int mode)
    {   int     x, y;
    
        Show(Dib.hBmpDC, 0, 0);
        // mode の色に設定する
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
            {
                if ((mode & 1) == 0)    MAP(y,x,0) = 0;
                if ((mode & 2) == 0)    MAP(y,x,1) = 0;
                if ((mode & 4) == 0)    MAP(y,x,2) = 0;
            }
    }
    
    // Image 画像を Dib.hBmp に転送して val(100~600) の明るさに設定する関数
    void  IMAGEAI::Light(WORD val)
    {   int     x, y, cp;
        WORD    wk;
    
        Show(Dib.hBmpDC, 0, 0);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
                for(cp=0; cp<3; cp++)
                {
                    wk= MAP(y,x,cp);
                    wk= (wk*val)/100;
                    if (wk>255) wk= 255;
                    MAP(y,x,cp)= (BYTE)wk;
                }
    }
    
    // Dib 画像から Y,X で指定された色を削除する関数
    void  IMAGEAI::Del_Color(int xp, int yp, WORD wd)
    {   int     x, y;
    
        Show(Dib.hBmpDC, 0, 0);
        SetLimit(xp,yp,wd);
    VarMsg(hWnd,"WORD WD:",wd);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
            {
                if (ChkPic(x,y))
                {   MAP(y,x,0)= 0;
                    MAP(y,x,1)= 0;
                    MAP(y,x,2)= 0;
                }
            }
    }
    
    // Dib 画像から X,Y で指定された色を抽出する関数
    void  IMAGEAI::Sel_Color(int xp, int yp, WORD wd)
    {   int     x, y;
    
        Show(Dib.hBmpDC, 0, 0);
        SetLimit(xp,yp,wd);
    VarMsg(hWnd,"WORD WD:",wd);
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
            {
                if (ChkPic(x,y)==false)
                {   MAP(y,x,0)= 0;
                    MAP(y,x,1)= 0;
                    MAP(y,x,2)= 0;
                }
            }
    }
    
    // Dib 画像の RGB の使用率を求める(画像全体の色調)
    void  IMAGEAI::Sum_Color()
    {   int     x, y, cp;
        int     sum[3],wk;
    
        sum[0]=sum[1]=sum[2]= 0;
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
                for (cp=0; cp<3; cp++)
                {   sum[cp] += MAP(y,x,cp);  }
        wk= sum[0]+sum[1]+sum[2];
        for(cp=0; cp<3; cp++)
        {   rgb[cp] = sum[cp]*100/wk;  }
    Debug(rgb);
    }
    
    // Dib 画像の幅と高さを求める
    void  IMAGEAI::Get_Size()
    {   int     x, y, s, e;
        bool    sw;
    
        sw= true;
        for(y=0; y<m_Height && sw; y++)
            for(x=0; x<m_Width && sw; x++)
            {   if (MAP(y,x,0)!=0 || MAP(y,x,1)!=0 || MAP(y,x,2)!=0)
                {   s= y;
                    sw= false;
                }
            }
        sw= true;
        for(y=m_Height-1; y>=0 && sw; y--)
            for(x=0; x<m_Width && sw; x++)
            {   if (MAP(y,x,0)!=0 || MAP(y,x,1)!=0 || MAP(y,x,2)!=0)
                {   e= y;
                    sw= false;
                }
            }
        height= e-s;
        sw= true;
        for(x=0; x<m_Width && sw; x++)
            for(y=0; y<m_Height && sw; y++)
            {   if (MAP(y,x,0)!=0 || MAP(y,x,1)!=0 || MAP(y,x,2)!=0)
                {   s= x;
                    sw= false;
                }
            }
        sw= true;
        for(x=m_Width-1; x>=0 && sw; x--)
            for(y=0; y<m_Height && sw; y++)
            {   if (MAP(y,x,0)!=0 || MAP(y,x,1)!=0 || MAP(y,x,2)!=0)
                {   e= x;
                    sw= false;
                }
            }
        width= e-s;
    Debug("Width",width);
    Debug("Height",height);
    }
    
    // Dib.Data の果物を抽出して Mask を設定
    void  IMAGEAI::Set_Mask(WORD xp, WORD yp, WORD wd)
    {   int     x, y, cp, xw, yw;
        xw = xp;
        yw = m_Height - yp;
        for(y=0; y<m_Height; y++)
            for(x=0; x<m_Width; x++)
                for(cp=0; cp<3; cp++)
                {   MASK(y, x, cp) = 0;  }
        Show(Dib.hBmpDC, 0, 0);
        SetLimit(xw, yw, wd);
    VarMsg(hWnd,"WORD WD:",wd);
        Pixcel(xw,yw);  
    }
    
    // Dib.hBmp から X,Y の色を取得して low[3], high[3] を設定する(wd 幅)
    void  IMAGEAI::SetLimit(int x, int y, WORD wd)
    {   int     cp;
        WORD    wk;
    
        for(cp = 0; cp<3; cp++)
        {
            wk= MAP(y,x,cp);
            if (wk>wd)  low[cp]= wk-wd;
            else    low[cp]= 0;
            wk+= wd;
            if (wk>255) wk= 255;
            high[cp]= (BYTE)wk;
        }
    }
    
    // X,Y のピクセルが low[3] から high[3] の範囲かチエックする 
    bool  IMAGEAI::ChkPic(int x, int y)
    {   if (MAP(y,x,0)<low[0])  return false;
        if (MAP(y,x,0)>high[0]) return false;
        if (MAP(y,x,1)<low[1])  return false;
        if (MAP(y,x,1)>high[1]) return false;
        if (MAP(y,x,2)<low[2])  return false;
        if (MAP(y,x,2)>high[2]) return false;
        return true;
    }
    
    // 隣接するピクセルを再帰でテスト 
    void  IMAGEAI::Pixcel(int x, int y)
    {
        if (OutPic(x,y)==false) return;
        MASK(y, x, 0) = 255;
        MASK(y, x, 1) = 255;
        MASK(y, x, 2) = 255;
        Pixcel(x+1,y);
        Pixcel(x,y+1);
        Pixcel(x-1,y);
        Pixcel(x,y-1);
    }
    
    // Dib.Data[X,Y] が low[3]~high[3] の範囲外のとき false 
    bool  IMAGEAI::OutPic(int x, int y)
    {
        if (x<0 || y<0) return false;
        if (x>=m_Width || y>=m_Height)  return false;
        if (Dib.Mask[(y*m_Width+x)*3]==255) return false;
        if (MAP(y,x,0)<low[0])  return false;
        if (MAP(y,x,0)>high[0]) return false;
        if (MAP(y,x,1)<low[1])  return false;
        if (MAP(y,x,1)>high[1]) return false;
        if (MAP(y,x,2)<low[2])  return false;
        if (MAP(y,x,2)>high[2]) return false;
        return true;
    }
    
    // グレースケールデータからエッジ抽出して Mask を作成
    void  IMAGEAI::Sobel(WORD wd)
    {
        int     x,y,fx,fy;
        BYTE    ans;
    
        if (Gray == NULL)   return;
        VarMsg(hWnd, "WORD WD:", wd);
        for(y=1; y<m_Height-1; y++)
            for(x=1; x<m_Width-1; x++)
            {   ans = 255;
                fx = Coef(x,y,hCoef);
                fy = Coef(x,y,vCoef);
                if (fx*fx+fy*fy > wd)   ans = 0;
                MASK(y,x,0)= ans;
                MASK(y,x,1)= ans;
                MASK(y,x,2)= ans;
            }
    }
    
    int  IMAGEAI::Coef(int x, int y, int tbl[9])
    {
        int     ans, i, dx, dy;
        ans = 0;
        for (i = 0; i<9; i++)
        {
            dx = x + vect[i][0];
            dy = y + vect[i][1];
            ans += GRAY(dy, dx)*tbl[i];
        }
        return ans;
    }
    
    // Pixel の値を確認する
    void  IMAGEAI::GetPixel(WORD x, WORD y)
    {
        int     xw, yw, cp;
        WORD    col[3];
        char    str[80];
        xw = x;
        yw = m_Height - y;
        for (cp = 0; cp<3; cp++)
        {   col[cp] = MAP(yw, xw, cp);  }
        wsprintf(str, "[Y:%d X:%d][%d, %d] B:%d  G:%d  R:%d", y, x, yw, xw, col[0], col[1], col[2]);
        SetWindowText(hWnd, (LPCSTR)str);
    }
    
    // Window Size を画像に合わす
    void  IMAGEAI::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  IMAGEAI::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;
    }
    
  3. Pixcel() 関数は「再帰で多重に呼び出される」ので、スタック領域が不足するようです。
    [プロジェクト/Mainのプロパティ/リンカー/システム/スタックのサイズの設定] からサイズを 10000000 に設定して下さい。
    今回の BMP サイズであれば、スタックは 10000000(10MB) あれば足りるようです。
    ビルド/ソリューションのリビルドで再コンパイルしてテストして下さい。
    再起で抽出の詳細は「超初心者のプログラム入門(C/C++)」から「*で囲まれた中を塗りつぶす」を参照して下さい。

エッジを検出する

  1. BMP 画像の横幅(Width)は【8の倍数】に合わせて下さい。
    画像から隣り合っているピクセルの濃淡の違いを検出して、エッジ画像を作成します。
    エッジの検出は Sobel(ソーベル、またはゾーベル)フィルタと呼ばれる濃淡の違いを検出する方法があります。
    濃淡の違いを調べるピクセルを中心において、8方向の濃さを調べます。
    横方向の濃さを次のウエイトを掛けて演算します。
    1 0 -1
    2 0 -2
    1 0 -1
    同様に縦方向の濃さを次のウエイトを掛けて演算します。
    1 2 1
    0 0 0
    -1 -2 -1
    ウエイトの配列と、方向を決めるベクトルを ImageAI.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 };
    
  2. 横方向と縦方向の濃さを計算する Coef() 関数です。
    グレースケールに変換した値を元に隣接するピクセルとの濃淡を計算します。
    int  IMAGEAI::Coef(int x, int y, int tbl[9])
    {
        int     ans, i, dx, dy;
        ans = 0;
        for (i = 0; i<9; i++)
        {
            dx = x + vect[i][0];
            dy = y + vect[i][1];
            ans += GRAY(dy, dx)*tbl[i];
        }
        return ans;
    }
    
  3. グレースケールデータからエッジを抽出して Mask を作成する Sobel() 関数です。
    wd が閾値で値が大きくなるほど黒い部分が少なくなります。
    fx,fy が隣接するピクセルとの濃さで、負の値になることがあるので二乗してマイナスを消します。
    // グレースケールデータからエッジ抽出して Mask を作成
    void  IMAGEAI::Sobel(WORD wd)
    {
        int     x,y,fx,fy;
        BYTE    ans;
    
        if (Gray == NULL)   return;
        VarMsg(hWnd, "WORD WD:", wd);
        for(y=1; y<m_Height-1; y++)
            for(x=1; x<m_Width-1; x++)
            {   ans = 255;
                fx = Coef(x,y,hCoef);
                fy = Coef(x,y,vCoef);
                if (fx*fx+fy*fy > wd)   ans = 0;
                MASK(y,x,0)= ans;
                MASK(y,x,1)= ans;
                MASK(y,x,2)= ans;
            }
    }
    
  4. Main.cpp の AppInit() から OpenFile() で画像ファイルを選択します。
    InitDib() で DibStruct を初期化して下さい。
    WM_PAINT では Dib.hMaskDC の画像を描画するので Show(ImageAI->Dib.hMaskDC, 0, 0) で画像を転送します。
    Grayscale() 関数で Dib.hBmpDC に画像を転送してグレースケールに変換します。
    グレースケールに変換は Color ⇒ Grayscale を参照して下さい。
    // 画像をロードして、24bit DIB を作成
    LRESULT  AppInit(HWND hwnd)
    {
        ImageAI= new IMAGEAI(hwnd);
        if (ImageAI->OpenFile()==false) return false;
        ImageAI->Adjust(false);
        ImageAI->InitDib();
        ImageAI->Show(ImageAI->Dib.hMaskDC, 0, 0);
        ImageAI->Grayscale();
        return true;
    }
    
  5. WM_PAINT では ShowMask() で Dib.hMaskDC の画像を描画します。
            case WM_PAINT:
                hdc = BeginPaint(hWnd,&ps);
                if (ImageAI)
                {   ImageAI->ShowMask(hdc, 0, 0);  }
                EndPaint(hWnd, &ps);
                break;
    
  6. WM_LBUTTONDOWN では、閾値アップしながらエッジ抽出します。
    WORD        wd= 300;                // 閾値(幅)
    
            case WM_LBUTTONDOWN:    // 左ボタンをクリック
                wd= wd+50;
                ImageAI->Sobel(wd);
                InvalidateRect(hWnd,NULL,FALSE);
                break;
    
  7. WM_RBUTTONDOWN: では、新しい画像ファイルを入力します。
            case WM_RBUTTONDOWN:    // 右ボタンをクリック
                wd= 300;
                if (ImageAI->OpenFile() == false)   return false;
                ImageAI->Adjust(false);
                ImageAI->InitDib();
                ImageAI->Show(ImageAI->Dib.hMaskDC, 0, 0);
                ImageAI->Grayscale();
                InvalidateRect(hWnd, NULL, FALSE);
                break;
    

[Next Chapter ↓] Mask 画像を使う
[Previous Chapter ↑] Color&Size

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