山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他OpenCVを使用してjpgファイル(画像の横幅は4の倍数に限定)を読み込みGDIで表示する
概要
OpenCVのMat型を使用してtest.jpgファイルを読み込み、Mat型のイメージへのポインタを直接メモリデバイスコンテキストに変換し、GDIによりウィンドウに表示するプログラムである。
Mat型とメモリデバイスコンテキストでは同じアドレスを共有することとなるのでOpen CV及びGDI両方から描画ができる。
内部的には1ピクセルあたり3byte割り当てられている。
ウィンドウの処理等はWindowsの流儀で記述できるので楽である。
test.jpgの画像の横幅は4の倍数でなければならない4の倍数でない場合画像がずれる。これはOpenCVがファイルから読み込んだ画像を必ず1ピクセルあたり24bitカラーで表現すること、WindowsのBITMAPの1行あたりのバイト数は4の倍数でなければならないという制限からくる。読み込み元の画像にモノクロ(1ピクセルあたり1bit)やグレースケール(1ピクセルあたり8bit)を用いても同じである。では画像の横サイズが4の倍数と4の倍数でない場合の違いを示す。
画像サイズ320(4の倍数)*240
画像サイズ319(4の倍数でない)*239
Visual C++ を対象としておりOpenCVのインストールが必要である。
Mat型とメモリデバイスコンテキストでは同じアドレスを共有することとなるのでOpen CV及びGDI両方から描画ができる。
内部的には1ピクセルあたり3byte割り当てられている。
ウィンドウの処理等はWindowsの流儀で記述できるので楽である。
test.jpgの画像の横幅は4の倍数でなければならない4の倍数でない場合画像がずれる。これはOpenCVがファイルから読み込んだ画像を必ず1ピクセルあたり24bitカラーで表現すること、WindowsのBITMAPの1行あたりのバイト数は4の倍数でなければならないという制限からくる。読み込み元の画像にモノクロ(1ピクセルあたり1bit)やグレースケール(1ピクセルあたり8bit)を用いても同じである。では画像の横サイズが4の倍数と4の倍数でない場合の違いを示す。
画像サイズ320(4の倍数)*240
画像サイズ319(4の倍数でない)*239
Visual C++ を対象としておりOpenCVのインストールが必要である。
テスト環境
コンパイラ
Visual C++ 2008 Express(32bit)/Standard(32/64bit) with OpenCV2.3.1Visual C++ 2013 Express 32bit/64bit with OpenCV2.4.10
実行環境
Windows 7 32/64bitOpenCVのインストール及び環境設定
プログラムソースの概要
#include の後の行からusing namespace cv;の手前までは、Visual C++のバージョン及びRelease/Debug 32bit/64bit 静的リンク/動的リンクに合わせてロードするLIBのファイル名を自動的に作成するために記述しています。
固定された環境であれば、簡素に記述可能です。
例えば Visual C++ 2013 32bit Release 動的リンクであれば、以下のように簡素に記述できます。
グローバル変数fileName変数で開くjpgファイルのファイル名を指定しています。
imread関数でjpgファイルをMat型の変数imgにロードします。
imgのメンバー変数のdataがNULLの場合は、ロードに失敗しているのでプログラムを終了させます。
GetDC APIによりウィンドウのデバイスコンテキストハンドルを取得します。
mat2memHDC構造体の()関数によりウィンドウと互換性のあるメモリデバイスコンテキストを作成します。
Release APIによりウィンドウのデバイスコンテキストハンドルを解放します。
InvalidateRect APIによりウィンドウの再描画を要求します。
mat2memHDC構造体のBitBlt関数によりメモリからウィンドウへイメージを転送します。
固定された環境であれば、簡素に記述可能です。
例えば Visual C++ 2013 32bit Release 動的リンクであれば、以下のように簡素に記述できます。
#pragma comment(lib, "c:/opencv2.4.10/opencv/build/vc12/x86/lib/opencv_core2410.lib")
#pragma comment(lib, "c:/opencv2.4.10/opencv/build/vc12/x86/lib/opencv_highgui2410.lib")
using namespace cv; により名前空間をcvにします。これを定義しない場合は、OpenCVの関数を使う場合には関数名の前にcv::を付加する必要があります。
_tWinMain
ウィンドウを開きます。WndProc
WM_CREATE
ウィンドウの初期化時に呼び出されます。グローバル変数fileName変数で開くjpgファイルのファイル名を指定しています。
imread関数でjpgファイルをMat型の変数imgにロードします。
imgのメンバー変数のdataがNULLの場合は、ロードに失敗しているのでプログラムを終了させます。
GetDC APIによりウィンドウのデバイスコンテキストハンドルを取得します。
mat2memHDC構造体の()関数によりウィンドウと互換性のあるメモリデバイスコンテキストを作成します。
Release APIによりウィンドウのデバイスコンテキストハンドルを解放します。
InvalidateRect APIによりウィンドウの再描画を要求します。
WM_PAINT
ウィンドウの再描画が必要な場合に呼び出されます。mat2memHDC構造体のBitBlt関数によりメモリからウィンドウへイメージを転送します。
WM_DESTROY
ウィンドウが閉じるときに呼び出されます。Mat2MemHDC構造体
Open CVのMat型をGDIで使用するために定義しています。
struct Mat2MemHDC{
BITMAPINFO bmi;
HBITMAP hbmp;
BYTE *pBits;
HDC memHDC;
// Mat型の画像とメモリデバイスコンテキストに変換する
void operator()(HDC hdc, Mat& img);
int width(void);
int height(void);
void BitBlt(HDC hdc,int dx,int dy,DWORD rop);
};
operator()(HDC hdc, Mat img);
デバイスコンテキストハンドルとMat型を与えて呼び出すと指定したデバイスコンテキストと互換性のあるメモリデバイスコンテキストを作成し、ビットマップデータのアドレスはMat型のメンバであるdataと同じにします。BitBlt
指定されたデバイスコンテキストにビットマップを転送します。ソースコード
// OpenCVでtest.jpgをMat型に読み込み、Mat型のデータを直接メモリHDCに変換してGDIにより表示
// Mat型のメモリを直接メモリデバイスで使用しているのでMatで使用するイメージの横幅は4の倍数である必要がある。
// 仮に4の倍数でない場合、画像がずれて表示される
//
// Open CV 2.3.1/2.4.10 サポート
// 例えばVisual C++ 2013でOpen CV 2.4.10でコンパイルする場合の設定は以下の通りとなる
// VCのインクルードディレクトリに C:\opencv2.4.10\opencv\build\include; を付加
// 動的リンクで作成した場合は、サンプルの実行には環境変数PATHに以下のフォルダーを登録する必要がある。
// win64 C:\opencv2.4.10\opencv\build\x64\vc12\bin
// win32 C:\opencv2.4.10\opencv\build\x86\vc12\bin
// 動作確認
// Visual C++ 2008 Standard Release 64bit 動的/静的 OpenCV 2.3.1
// Visual C++ 2008 Standard Debug 64bit 動的/静的 OpenCV 2.3.1
// Visual C++ 2008 Standard Release 32bit 動的/静的 OpenCV 2.3.1
// Visual C++ 2008 Standard Debug 32bit 動的/静的 OpenCV 2.3.1
// Visual C++ 2013 Express Debug 32bit 動的/静的 OpenCV 2.4.10
// Visual C++ 2013 Express Release 32bit 動的/静的 OpenCV 2.4.10
// Visual C++ 2013 Express Debug 64bit 動的/静的 OpenCV 2.4.10
// Visual C++ 2013 Express Release 64bit 動的/静的 OpenCV 2.4.10
#include <windows.h>
#include <opencv2/opencv.hpp>
#include <tchar.h>
#include <commctrl.h>
#ifdef _DLL // 動的リンク
#define CV_LINK_MODE "/lib/"
#else // 静的リンク
#define CV_LINK_MODE "/staticlib/"
#endif
// バージョン取得
#define CV_VERSION_STR CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION)
#define CV_INST_SUB_DIR "opencv" ## CVAUX_STR(CV_MAJOR_VERSION) ## "." CVAUX_STR(CV_MINOR_VERSION) ## "." CVAUX_STR(CV_SUBMINOR_VERSION)
#define CV_INST_DIR "c:/" ## CV_INST_SUB_DIR ## "/opencv/build"
// 32bit/64bit ランタイムのリンクモード等に応じてLIBファイルのフォルダー名(CV_LIB_DIR)を作成
#ifdef _WIN64
#ifdef _DLL
#define CV_LIB_DIR CV_INST_DIR ## "/x64/" ## CV_MS_VER ## "/lib/"
#else
#define CV_LIB_DIR CV_INST_DIR ## "/x64/" ## CV_MS_VER ## "/staticlib/"
#endif
#else
#ifdef _DLL
#define CV_LIB_DIR CV_INST_DIR ## "/x86/" ## CV_MS_VER ## "/lib/"
#else
#define CV_LIB_DIR CV_INST_DIR ## "/x86/" ## CV_MS_VER ## "/staticlib/"
#endif
#endif
#if _MSC_VER==1500
#define VCVER 2008
#define CV_MS_VER "vc9"
#endif
#if _MSC_VER==1600
#define VCVER 2010
#define CV_MS_VER "vc10"
#endif
#if _MSC_VER==1700
#define VCVER 2012
#define CV_MS_VER "vc11"
#endif
#if _MSC_VER==1800
#define VCVER 2008
#define CV_MS_VER "vc12"
#endif
#ifdef _DEBUG // デバック
#define CV_EXT_STR "d.lib"
#else // リリース
#define CV_EXT_STR ".lib"
#endif
//
#pragma comment(lib, CV_LIB_DIR "opencv_core" CV_VERSION_STR CV_EXT_STR)
#pragma comment(lib, CV_LIB_DIR "opencv_highgui" CV_VERSION_STR CV_EXT_STR)
#ifdef _DLL // 動的リンク
#else
#pragma comment(lib,"comctl32.lib")
#pragma comment(linker, "/nodefaultlib:\"msvcprt" CV_EXT_STR "\"")
#if CV_MAJOR_VERSION==2 && CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION==10
#pragma comment(lib, CV_LIB_DIR "IlmImf" CV_EXT_STR )
#endif
#pragma comment(lib, CV_LIB_DIR "libjasper" CV_EXT_STR )
#pragma comment(lib, CV_LIB_DIR "libjpeg" CV_EXT_STR )
#pragma comment(lib, CV_LIB_DIR "libpng" CV_EXT_STR )
#pragma comment(lib, CV_LIB_DIR "libtiff" CV_EXT_STR )
#pragma comment(lib, CV_LIB_DIR "zlib" CV_EXT_STR )
#endif
using namespace cv;
// ウィンドウプロシージャー
TCHAR* szClassName = _TEXT("GDI with OpenCV");
// Mat型をメモリデバイスコンテキストに変換するクラス
struct Mat2MemHDC{
BITMAPINFO bmi;
HBITMAP hbmp;
BYTE *pBits;
HDC memHDC;
// Mat型の画像とメモリデバイスコンテキストに変換する
void operator()(HDC hdc, Mat& img){
bmi.bmiHeader.biSize = sizeof(bmi);
bmi.bmiHeader.biWidth = img.cols;
bmi.bmiHeader.biHeight = -img.rows; // 上下反転
bmi.bmiHeader.biBitCount = img.channels() * 8;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
hbmp = CreateCompatibleBitmap(hdc , img.cols , img.rows);
SetDIBits(hdc,hbmp , 0, img.rows, img.data,&bmi, DIB_RGB_COLORS);
memHDC = CreateCompatibleDC(hdc);
SelectObject(memHDC, hbmp);
pBits = (BYTE*)img.data; // Mat型のメンバdataにイメージが保存されている
}
int width(void){
return bmi.bmiHeader.biWidth;
}
int height(void){
return -bmi.bmiHeader.biHeight;
}
void BitBlt(HDC hdc,int dx,int dy,DWORD rop){
::BitBlt(hdc, 0, 0, width(), height(), memHDC, dx, dy, rop);
}
};
Mat2MemHDC mat2memHdc;
Mat img;
char* fileName = "test.jpg";
// ウィンドウを作成/閉じる/移動等のメッセージにより起動される関数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPreInst, TCHAR* CmdLine, int nCmdShow){
HWND hWnd;
MSG lpMsg;
WNDCLASS myProg;
if (!hPreInst) {
myProg.style = CS_HREDRAW | CS_VREDRAW;
myProg.lpfnWndProc = WndProc;
myProg.cbClsExtra = 0;
myProg.cbWndExtra = 0;
myProg.hInstance = hPreInst;
myProg.hIcon = NULL;
myProg.hCursor = LoadCursor(NULL, IDC_ARROW);
myProg.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
myProg.lpszMenuName = NULL;
myProg.lpszClassName = szClassName;
if (!RegisterClass(&myProg))
return FALSE;
}
hWnd = CreateWindow(szClassName,
szClassName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hPreInst,
NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&lpMsg, NULL, 0, 0)){
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
return int(lpMsg.wParam);
}
// ウィンドウを作成/閉じる/移動等のメッセージにより起動される関数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
HDC hdc;
PAINTSTRUCT ps;
switch (msg) {
case WM_CREATE:
img = imread(fileName); // jpgファイルをimgにロードする
if(img.data==NULL){
MessageBox(0,_TEXT("test.jpgが開けません"),_TEXT("エラー"),MB_OK);
SendMessage(hWnd , WM_DESTROY , 0 , 0 );
}
hdc = GetDC(hWnd);
mat2memHdc(hdc, img); // Mat型をメモリデバイスコンテキストに変換する
ReleaseDC(hWnd, hdc);
InvalidateRect(hWnd, 0, TRUE);
break;
case WM_PAINT: // ウィンドウの描画が必要な場合に呼び出される。
hdc = BeginPaint(hWnd, &ps);
mat2memHdc.BitBlt(hdc, 0, 0,SRCCOPY); // メモリデバイスコンテキストをウィンドウに表示
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: // ウィンドウを閉じる場合に呼び出される。
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0L;
}
Copyright (C) 2012 山本ワールド All Rights Reserved.