質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.13%

C++でOpenCVのプログラミングをしたときの関数が文法上良くわからない

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,789

watarusugimoto

score 49

Win32APIを用いてデスクトップキャプチャした画像をOpenCVのMat型に変換し、OpenCVで解析したい。

Win32APIを用いてデスクトップキャプチャを連続で行い、動画としてデスクトップの映像が見れるようにプログラミングを組みました。メモリデバイスコンテキストにビットマップを対応させ、画像をメモリに保存するところまではプログラミングが完成しています。
次にやりたいこととして、このメモリに保存されている画像データを、OpenCVのMat型変数の中に格納したいと考えています。
そこで、参考となるサイトとして、山本ワールドというサイトの「OpenCV Mat型とGDI BITMAPの相互変換サンプル」というコードを参考にさせて頂きました。

理解できないコードの抜粋

C++の文法の話になるかもしれませんが、void operator()(HBITMAP hbmp)というように、()が連続して続くような関数は良いのか、または、何か決まった文法があるのかがわかりません。分からないのでひたすら調べているのですが、単にoperator()までが関数名なだけなのか、このように()を2回繰り返すのが文法上意味があるのかどうなのかがわかりません。

    void operator()(HBITMAP hbmp){
        BITMAP bmp;
        GetObject(hbmp, sizeof(bmp), &bmp);
        if (mat==0 || (mat && (bmp.bmHeight!=height || bmp.bmWidth!=width)) ){
            delete mat;
            mat = new Mat(bmp.bmHeight, bmp.bmWidth, CV_8UC(bmp.bmBitsPixel / 8));
        }
        width = bmp.bmWidth;
        height = bmp.bmHeight;
        size_t sz = bmp.bmWidth*bmp.bmHeight*(bmp.bmBitsPixel / 8);
        memcpy(mat->data, bmp.bmBits, sz);
    }

該当のソースコード全体

//    OpenCV Mat型とGDI BITMAPの相互変換サンプル
//
//    test.jpgファイルをMat型に読み込み、GDI BITMAPに変換してウィンドウに描画
//    GDI BITMAPをMat型に変換してOpenCVでウィンドウを開いて表示
//
//    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
//    LIBファイルは本ソースのプラグマで設定しているので、CV_INST_DIR CV_INSt_DIR_SUBマクロでLIBファイルのパスを設定する必要がある。
//        本ソースは、c:\opencvメジャー番号.マイナー番号.サブマイナー番号\opencv\build を想定
//    動作確認
//        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 BitBlt");


//    Mat型をメモリデバイスコンテキストに変換するクラス

struct Mat2MemHDC{
    BITMAPINFO bmi;
    HBITMAP hbmp;
    BYTE *pBits;
    HDC memHDC;

    Mat2MemHDC(){
        pBits = 0;
    }
    ~Mat2MemHDC(){
        if (pBits){
            DeleteObject(hbmp);
            DeleteDC(memHDC);
        }
    }
    //    Mat型の画像とメモリデバイスコンテキストに変換する
    bool operator()(HDC hdc, Mat img){
        //    横幅を4の倍数に合わせる
        int sx = img.cols;
        if (sx % 4){
            sx = (sx + 4) & 0xfffffffc;
        }
        if (pBits == NULL || (pBits && (sx != bmi.bmiHeader.biWidth || img.rows != bmi.bmiHeader.biHeight)) ){
                DeleteObject(hbmp);
                DeleteDC(memHDC);
                bmi.bmiHeader.biSize = sizeof(bmi);
                bmi.bmiHeader.biWidth = sx;
                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 = CreateDIBSection(NULL, &bmi, 0, (void**)&pBits, NULL, 0);
                if (pBits == NULL){    //    メモリ不足
                    return false;
                }
                memHDC = CreateCompatibleDC(hdc);
                SelectObject(memHDC, hbmp);
            }

        //        imgの横幅が4の倍数でない場合があるので、1ラインずつコピーする
        for (int y = 0; y < height(); y++){
            memcpy(pBits + y*width()*img.channels(), img.data + y*img.cols*img.channels(), img.cols*img.channels());
            //    右横の余白を白にする
            BYTE* p = pBits + (y*width()+img.cols)*img.channels();
            for (int x = img.cols; x < width(); x++){
                for (int n = 0; n < img.channels(); n++){
                    *p++ = 0xff;
                }
            }
        }
        return true;
    }
    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);
    }
};

//    ビットマップをMat型に変換するクラス

struct MemHDC2Mat{
    Mat* mat;
    int width, height;
    MemHDC2Mat(){
        mat = 0;
    }
    void operator()(HBITMAP hbmp){
        BITMAP bmp;
        GetObject(hbmp, sizeof(bmp), &bmp);
        if (mat==0 || (mat && (bmp.bmHeight!=height || bmp.bmWidth!=width)) ){
            delete mat;
            mat = new Mat(bmp.bmHeight, bmp.bmWidth, CV_8UC(bmp.bmBitsPixel / 8));
        }
        width = bmp.bmWidth;
        height = bmp.bmHeight;
        size_t sz = bmp.bmWidth*bmp.bmHeight*(bmp.bmBitsPixel / 8);
        memcpy(mat->data, bmp.bmBits, sz);
    }
    ~MemHDC2Mat(){
        if (mat)
            delete mat;
    }
};


Mat2MemHDC mat2memHdc;
MemHDC2Mat memHDC2Mat;

Mat img;

char* fileName = "test.jpg";

// ウィンドウを作成/閉じる/移動等のメッセージにより起動される関数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);


int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPreInst, char* 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にロードする
        hdc = GetDC(hWnd);
        mat2memHdc(hdc, img);    //    Mat型をメモリデバイスコンテキストに変換する
        memHDC2Mat(mat2memHdc.hbmp);

        namedWindow("OpenCV namedWindow", CV_WINDOW_AUTOSIZE);
        imshow("OpenCV namedWindow", *memHDC2Mat.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;
}

試したこと

現在、C++の文法書を見直してそのような関数が定義できるのか、確認中ですが納得のいくような答えが出せていません。クラスについての知識があまりないので、その部分を見直しているのですが答えを出せていません。
このoperatorという関数についてどう考えれば良いのか教えて頂けると助かります。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

こんにちは。

operator()は、()演算子という意味です。
なんじゃそりゃ?って感じですね。()は関数を呼び出すと言う「演算子」なのです。
ここの解説が分かり易いと思います。

他にも、+演算子を定義するoperator+など多数あります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/30 18:14

    素早い回答をありがとうございます。
    助かります。

    キャンセル

  • 2016/12/30 20:25

    ありがとうございます。自分が求めていたものでした。
    何となくわかりました。
    あと一点、教えて欲しいのですが、このソースコードを読んだ時に、
    何故オーバーロードを使っているのでしょうか?
    何を意図してオーバーロードを使っているのかが良く理解できていません。
    宜しければ教えて下さい。

    キャンセル

  • 2016/12/30 20:38 編集

    私もよく分からなかったです。普通にメソッドで処理して良いし、その方が分かり易いと思います。

    operator()を定義するケースは、ファンクタ(関数オブジェクト)を作る時が多いです。
    https://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88
    ですが、ご提示のソースは戻り値がvoidなのでファンクタ的な使い方ではないです。
    個人的にはあまり()演算子で処理しない方が良いケースのように感じます。

    キャンセル

  • 2016/12/30 20:56

    理解していないままの質問なので、余計に分かりませんでした。
    この後またじっくり勉強してみたいと思います。
    ありがとうございました。

    キャンセル

0

()演算子のオーバーロードですね。

C++ではoperator+operator=で、+=の挙動をユーザ(プログラマ)自身が定義することが出来ます。
operator()も同様です。

参考: C++マニアック,オペレータ(演算子)のオーバーロード,operator overload

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/30 18:14

    素早い回答をありがとうございます。
    助かります。

    キャンセル

  • 2016/12/30 20:25

    ありがとうございます。自分が求めていたものでした。
    何となくわかりました。
    あと一点、教えて欲しいのですが、このソースコードを読んだ時に、
    何故オーバーロードを使っているのでしょうか?
    何を意図してオーバーロードを使っているのかが良く理解できていません。
    宜しければ教えて下さい。

    キャンセル

  • 2016/12/30 20:46 編集

    挙動に関しては参考にされているサイトで詳しく書いてあります。

    設計者では無いので設計の意図に関しては分かりかねます。
    推測ですが、定義しているクラス自体が変換用のクラスですので、それを強調するためかと感じました。
    single responsibility principleを意識したのかもしれませんし、サンプルプログラムとあるので特別な意味は無いかもしれません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る