ビットマップ画像の画像処理

ビットマップ画像を読取り、画像処理を行った結果を表示するためのウィンドウを作成する例です。下記の例では、元画像を読み取った後、画像の濃淡化(グレイスケールへの変換)、2値化、色の反転をメニューで選択し、実行できるようになっています。多少、複雑な画像処理の例として、2値化では最大尤度しきい値選定法を用いています(プログラム内容: opthmdthr.cpp )。中でも、ダイナミックプログラミングを用いた大津のしきい値選定法というアルゴリズムを採用しています(詳細は論文等を調べてもらうことになりますが。。)。

元画像   処理後の画像
(最大尤度しきい値選定法による2値化)
 

実行ファイルのダウンロード: winbip.exe

コンパイルオプション:
 Gami[84]% gcc -o winbip winbip.cpp opthmdthr.cpp -lgdi32 -lcomctl32 -lcomdlg32 -mwindows -e_mainCRTStartup

 ダウンロード

ビットマップ画像の画像処理(winbip.cpp)
//-----------------------------------------------------------------
// winbip.cpp:
//     ビットマップ画像の画像処理
//                 Last Update: <2004/11/28 21:01:32 A.Murakami>
//-----------------------------------------------------------------
#include <stdio.h>
#include <windows.h>
#include <wingdi.h>
#include <commctrl.h>
#include "opthmdthr.h"// 2値化: 最大尤度しきい値選定法

#define ID_IP   1000   // 画像処理
#define ID_OPEN 1001   // ファイルオープン

//-----------------------------------------------------------------
BOOL InitApp(HINSTANCE hInst, LPCSTR szClassName);
BOOL InitInstance(HINSTANCE hInst,LPCSTR szClassName,
                  LPSTR lpsCmdLine,int nCmdShow);
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
void GetWinPos(HWND hWnd,UINT *x,UINT *y);
UINT OpenFile(HWND hWnd, LPTSTR lpFname);
UINT reLoadBMP(LPCTSTR lpFname);
UINT LoadBMP(LPCTSTR lpFname,LPBITMAPINFO& biBuf,LPBYTE& lpBuf);
void execIP(DWORD ipmode);
void toGray(void);
void toBin(void);
void revColor(void);
//-----------------------------------------------------------------
HINSTANCE hAppInst; HWND hAppWnd;
UINT iWidth,iHeight,iLength,bmpLoad;
LPBYTE lpOrgBMP,lpBMP;
LPBITMAPINFO lpInfo;
//-----------------------------------------------------------------

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR
                   lpsCmdLine, int nCmdShow)
{
    LPCTSTR szClassName = "winbip";// 初期化
    bmpLoad = 0; iWidth = iHeight = 200;
    // ウィンドウ・クラスの登録
    if(!hPrevInst){
        if(!InitApp(hCurInst,szClassName)) return 0;
        hAppInst = hCurInst;
    }
    // ウィンドウ作成 
    if(!InitInstance(hCurInst,szClassName,lpsCmdLine,nCmdShow)){
        return 0;
    }
    // メッセージループ
    MSG msg;
    while(GetMessage(&msg,NULL,0,0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
//-----------------------------------------------------------------
// ウィンドウ・クラスの登録
//-----------------------------------------------------------------
BOOL InitApp(HINSTANCE hInst, LPCSTR szClassName)
{
    WNDCLASSEX wc;
    wc.cbSize      = sizeof(wc);
    wc.style       = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc; // プロシージャ名
    wc.cbClsExtra  = 0;
    wc.cbWndExtra  = 0;
    wc.hInstance   = hInst;   // インスタンス
    wc.hIcon       = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor     = LoadCursor(NULL,IDC_ARROW);
    wc.hIconSm     = LoadIcon(NULL,IDI_APPLICATION);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;   // メニュー名 
    wc.lpszClassName = (LPCSTR)szClassName;
    return (RegisterClassEx(&wc));
}
//-----------------------------------------------------------------
// ウィンドウの生成
//-----------------------------------------------------------------
BOOL InitInstance(HINSTANCE hInst,LPCSTR szClassName,
                  LPSTR lpsCmdLine,int nCmdShow)
{
    LPTSTR lpStr,ifile;
    //--------------------------------------------------
    // ビットマップファイルオープン 
    //--------------------------------------------------
    if(strlen(lpsCmdLine)) {
        ifile = lpsCmdLine;
        // ビットマップの読込み 
        if(!LoadBMP(ifile,lpInfo,lpOrgBMP)){
            MessageBox(NULL,"読込み失敗",NULL,MB_OK); return 0;
        }
        // 複製 
        lpBMP=(LPBYTE)GlobalAlloc(GPTR,iLength*iHeight);
        CopyMemory(lpBMP,lpOrgBMP,iLength*iHeight);
    }
    // ビットマップの情報表示
    if(bmpLoad) wsprintf(lpStr,"%s %d*%d Pixel %dBit",
                         ifile,iWidth,iHeight,
                         lpInfo->bmiHeader.biBitCount);
    else wsprintf(lpStr,"Winbip ");
    //--------------------------------------------------
    // ウィンドウの作成 
    //--------------------------------------------------
    HWND hWnd;
    hWnd = CreateWindow(szClassName, "BMP画像処理", // タイトルバー
                        WS_OVERLAPPEDWINDOW, // ウィンドウ
                        CW_USEDEFAULT, // X座標
                        CW_USEDEFAULT, // Y座標
                        15+iWidth,     // 幅
                        70+iHeight,    // 高さ
                        NULL,          // 親ウィンドウのハンドル
                        NULL,          // メニューハンドル 
                        hInst,         // インスタンスハンドル
                        NULL);
    if(!hWnd) return FALSE;
    // ドラッグ&ドロップ受入 
    DragAcceptFiles(hWnd,TRUE);
    // ウィンドウを表示 
    ShowWindow(hWnd,nCmdShow);
    // 再描画
    UpdateWindow(hWnd);
    // タイトル
    SetWindowText(hWnd,(LPSTR)lpStr);
    // ウィンドウ設定 
    hAppWnd = hWnd;
    return TRUE;
}
//-----------------------------------------------------------------
// メッセージ情報
//-----------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
                         WPARAM wParam, LPARAM lParam)
{
    static HWND hwB1,hwB2,hwSel;
    UINT s;  TCHAR szFn[MAX_PATH];
    HDC hdc; HDROP hDrop; PAINTSTRUCT ps;
    switch(iMsg){
    case WM_CHAR:
        if(wParam=='q') SendMessage(hwnd,WM_DESTROY,NULL,NULL); break;
    case WM_CREATE:
        // ボタン作成 
        hwB1=CreateWindow("Button","開く", WS_CHILD | WS_VISIBLE,
                          5,5,60,25,hwnd,(HMENU)ID_OPEN,hAppInst,NULL); 
        // ボタン作成
        hwB2=CreateWindow("Button","実行", WS_CHILD |
                          WS_VISIBLE,65,5,70,25,hwnd,
                          (HMENU)ID_IP,hAppInst,NULL);
        // 選択メニュー
        InitCommonControls();
        hwSel=CreateWindow("COMBOBOX",NULL,
                           WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,
                           150,5,115,90,hwnd,(HMENU)0,hAppInst,NULL);
        SendMessage(hwSel,CB_ADDSTRING,0,(LPARAM)"濃淡化");
        SendMessage(hwSel,CB_ADDSTRING,0,(LPARAM)"2値化");
        SendMessage(hwSel,CB_ADDSTRING,0,(LPARAM)"色反転");
        SendMessage(hwSel,CB_SETCURSEL,0,1);
        return 0;
    case WM_COMMAND:
        switch(LOWORD(wParam)){
        case 'q':     // 終了
            SendMessage(hwnd,WM_DESTROY,NULL,NULL); break;
        case ID_OPEN: // 読み込みボタン
            if(OpenFile(hwnd,szFn)){
                reLoadBMP(szFn); // 再読込み
            }
            break;
        case ID_IP:   // 画像処理ボタン 
            s=SendMessage(hwSel,CB_GETCURSEL,0,0);
            execIP(s);
            break;
        }
        return 0;
    case WM_DROPFILES: // ファイルがドロップされた時の処理
        // HDROPを取得
        hDrop=(HDROP)wParam;
        DragQueryFile(hDrop,0,szFn,MAX_PATH);
        DragFinish(hDrop);
        // 再読込み
        reLoadBMP(szFn);
        return 0;
    case WM_PAINT:
        if(bmpLoad){
            hdc=BeginPaint(hwnd,&ps);
            StretchDIBits(hdc,4,35,iWidth,iHeight,
                          0,0,iWidth,iHeight,lpBMP,lpInfo,
                          DIB_RGB_COLORS,SRCCOPY); // DIBを画面に描画 
            EndPaint(hwnd,&ps);
            return 0;
        }
        break;
    case WM_DESTROY: // メモリの解放
        GlobalFree(lpInfo);
        GlobalFree(lpOrgBMP);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd,iMsg,wParam,lParam);
}

//-----------------------------------------------------------------
// ウィンドウ位置の取得
//-----------------------------------------------------------------
void GetWinPos(HWND hWnd,UINT *x,UINT *y)
{
    WINDOWPLACEMENT wndpl;
    GetWindowPlacement(hWnd,&wndpl);
    *x = wndpl.rcNormalPosition.left;
    *y = wndpl.rcNormalPosition.top;
    return;
}
//-----------------------------------------------------------------
// ビットマップファイルの選択
//-----------------------------------------------------------------
UINT OpenFile(HWND hWnd, LPTSTR lpFname)
{
    static OPENFILENAME ofn;
    TCHAR szFn[MAX_PATH],szFt[MAX_PATH];
    FillMemory(szFn,MAX_PATH,0);
    // ファイル[開く]ダイアログ設定
    ofn.lStructSize=sizeof(OPENFILENAME);
    ofn.hwndOwner         = hWnd;
    ofn.hInstance         = NULL;
    ofn.lpstrFilter       =
        "Bitmap(*.bmp)\0*.bmp\0すべてのファイル(*.*)\0*.*\0\0";
    ofn.lpstrCustomFilter = NULL;
    ofn.nMaxCustFilter    = 0;
    ofn.nFilterIndex      = 1;    // フィルタの初期位置
    ofn.lpstrFile         = szFn; // ファイル名用文字列バッファ
    ofn.nMaxFile          = 255;  // 文字列バッファのサイズ
    ofn.lpstrFileTitle    = szFt; // タイトル用文字列バッファ
    ofn.nMaxFileTitle     = 255;  // 文字列バッファのサイズ
    ofn.lpstrInitialDir   = NULL;
    ofn.lpstrTitle        = "Bitmap";
    ofn.Flags             = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.nFileOffset       = 0;
    ofn.nFileExtension    = 0;
    ofn.lpstrDefExt       = NULL;
    ofn.lCustData         = 0;
    ofn.lpfnHook          = NULL;
    ofn.lpTemplateName    = NULL;
    // ダイアログを開く
    if(!GetOpenFileName(&ofn)) return FALSE;
    wsprintf(lpFname,szFn);
    return TRUE;
}
//-----------------------------------------------------------------
// ビットマップの再読込み
//-----------------------------------------------------------------
UINT reLoadBMP(LPCTSTR lpFname)
{
    UINT wx,wy,s; TCHAR lpStr[256];
    // メモリの解放
    GlobalFree(lpInfo);
    GlobalFree(lpOrgBMP);
    GlobalFree(lpBMP);
    // ビットマップの読込み
    if(LoadBMP(lpFname,lpInfo,lpOrgBMP)){
        // 複製
        lpBMP=(LPBYTE)GlobalAlloc(GPTR,iLength*iHeight);
        CopyMemory(lpBMP,lpOrgBMP,iLength*iHeight);
        // ウィンドウ幅の変更
        GetWinPos(hAppWnd,&wx,&wy);
        MoveWindow(hAppWnd,wx,wy,15+iWidth,iHeight+70,TRUE);
        // タイトル
        wsprintf(lpStr,"%s %d*%d Pixel %dBit",
                 lpFname,iWidth,iHeight,lpInfo->bmiHeader.biBitCount);
        SetWindowText(hAppWnd,(LPSTR)lpStr);
        return TRUE;
    }
    return FALSE;
}
//-----------------------------------------------------------------
// ビットマップの読込み
//-----------------------------------------------------------------
UINT LoadBMP(LPCTSTR lpFname,LPBITMAPINFO& biBuf,LPBYTE& lpBuf)
{
    HANDLE hdlBmp;           // ファイルのハンドル
    BITMAPFILEHEADER bfhBmp; // ビットマップファイル情報の構造体
    DWORD dwdFileSize=0;     // 読み込んだサイズ
    DWORD dwdPixelsSize=0;   // 色情報のサイズ
    // ファイルを開く
    hdlBmp=CreateFile(lpFname,GENERIC_READ,0,NULL,OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,NULL);
    if(hdlBmp==INVALID_HANDLE_VALUE){
        MessageBox(NULL,"読込みに失敗しました",NULL,MB_OK);
        return FALSE;
    }
    // ファイル情報部分を読み込む
    ReadFile(hdlBmp,&bfhBmp,sizeof(BITMAPFILEHEADER),&dwdFileSize,NULL);
    // ビットマップ情報部分を読み込む
    biBuf=(LPBITMAPINFO)GlobalAlloc(GPTR,sizeof(BITMAPINFO));
    ReadFile(hdlBmp,biBuf,sizeof(BITMAPINFOHEADER),&dwdFileSize,NULL);
    // 色情報を読み込む
    dwdPixelsSize=((biBuf->bmiHeader.biWidth+3)&0xfffffffc) *
        biBuf->bmiHeader.biHeight*3;
    lpBuf=(LPBYTE)GlobalAlloc(GPTR,dwdPixelsSize);
    ReadFile(hdlBmp,lpBuf,dwdPixelsSize,&dwdFileSize,NULL);
    // ファイルを閉じる
    CloseHandle(hdlBmp);
    // 24ビットの BMPでない場合、処理を終了する
    if(bfhBmp.bfType!='M'*256+'B' || biBuf->bmiHeader.biBitCount!=24){
        // メモリ領域を解放
        GlobalFree(biBuf);
        GlobalFree(lpBuf);
        MessageBox(NULL,"ファイルの種類が不正です",NULL,MB_OK);
        // 処理を中断
        return FALSE;
    }
    // 正常終了
    iWidth=(biBuf->bmiHeader.biWidth+3) & 0xfffffffc;
    iHeight=biBuf->bmiHeader.biHeight;
    // バッファの1ラインの長さを計算
    if((iWidth*3)%4==0) iLength=iWidth*3;
    else iLength=iWidth*3+(4-(iWidth*3)%4);
    // ビットマップの読込み完了
    bmpLoad = 1;
    return TRUE;
}
//-----------------------------------------------------------------
// 画像処理実行
//-----------------------------------------------------------------
void execIP(DWORD ipmode)
{
    switch(ipmode){
    case 0: toGray();   break;
    case 1: toBin();    break;
    case 2: revColor(); break;
    }
}
//-----------------------------------------------------------------
// 濃淡画像化
//-----------------------------------------------------------------
void toGray(void)
{
    if(!bmpLoad) return;
    BYTE r,g,b,gray;
    for(int i=0;i<iHeight;i++) for(int j=0;j<iWidth;j++) {
        b=lpOrgBMP[j*3+i*iLength];
        g=lpOrgBMP[j*3+i*iLength+1];
        r=lpOrgBMP[j*3+i*iLength+2];
        gray=(BYTE)(r*0.299+g*0.587+b*0.144);
        FillMemory(lpBMP+j*3+i*iLength,3,gray);
    }
    FlushViewOfFile(lpBMP,0);
    InvalidateRgn(hAppWnd,NULL,FALSE);
    UpdateWindow(hAppWnd); // 再描画
}
//-----------------------------------------------------------------
// 2値化
//-----------------------------------------------------------------
void toBin(void)
{
    if(!bmpLoad) return;
    BYTE r,g,b,gray;
    int isize,hist[256],thr[10];
    FillMemory(hist,sizeof(int)*256,0);
    // ヒストグラムの作成[濃淡化]
    for(int i=0;i<iHeight;i++) for(int j=0;j<iWidth;j++) {
        b=lpOrgBMP[j*3+i*iLength];
        g=lpOrgBMP[j*3+i*iLength+1];
        r=lpOrgBMP[j*3+i*iLength+2];
        gray=(BYTE)(r*0.299+g*0.587+b*0.144);
        FillMemory(lpBMP+j*3+i*iLength,3,gray);
        hist[gray]++;
    }
    // [2..]値化最適しきい値の計算
    isize = iWidth*iHeight;
    calc_opthmdthr(2,256,isize,hist,thr);
    
    // しきい値の表示
    char buf[256];
    wsprintf(buf,"最適しきい値 = %d,%d,%d\n",thr[0],thr[1],thr[2]);
    MessageBox(NULL,buf,NULL,MB_OK);
    
    for(int i=0;i<iHeight;i++) for(int j=0;j<iWidth;j++) {
        gray=lpBMP[j*3+i*iLength];
        if(gray>thr[1]){
            FillMemory(lpBMP+j*3+i*iLength,3,255);
        } else {
            FillMemory(lpBMP+j*3+i*iLength,3,0);
        }
        hist[gray]++;
    }
    FlushViewOfFile(lpBMP,0);
    InvalidateRgn(hAppWnd,NULL,FALSE);
    UpdateWindow(hAppWnd); // 再描画
}
//-----------------------------------------------------------------
// ビットマップの色を反転
//-----------------------------------------------------------------
void revColor(void)
{
    if(!bmpLoad) return;
    // 反転処理
    for(int i=0;i<iHeight;i++) for(int j=0;j<iWidth;j++) {
        lpBMP[j*3+i*iLength]   = 255-lpOrgBMP[j*3+i*iLength];
        lpBMP[j*3+i*iLength+1] = 255-lpOrgBMP[j*3+i*iLength+1];
        lpBMP[j*3+i*iLength+2] = 255-lpOrgBMP[j*3+i*iLength+2];
    }
    FlushViewOfFile(lpBMP,0);
    InvalidateRgn(hAppWnd,NULL,FALSE);
    UpdateWindow(hAppWnd); // 再描画
}
inserted by FC2 system