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

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

ただいまの
回答率

90.52%

  • C++

    3469questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • OpenCV

    1092questions

    OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++、OpenCVを用いた画像処理

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 310

leliel1193

score 1

 前提・実現したいこと

画像検出のプログラムを書いているのですが全くできません。
内容は、「読み込んだ画像のある1点をクリックすると、そのクリックした点と同じような色部分を検出する」
というものなのですが、どのように書けばよいのか見当が付きません。
画像を読み込んで、MauseCallBack()とGetPixel()を用いてクリックした点のRGB値は格納できていると思うのですが、その後の処理が分かりません。
プログラミング初心者なので、読みづらいコード、意味不明なところが多々ございますが、ご教授のほうよろしくお願いします。

 該当のソースコード

cv::Rect box;

bool drawing_box = false;

double R, G, B;
COLORREF clr;


void mouse_callback(int event, int x, int y, int flags, void* param) {
    cv::Mat* image = static_cast<cv::Mat*>(param);
    HDC hdc;
    HWND hWnd;
    hWnd = GetDesktopWindow();
    hdc = GetWindowDC(hWnd);

    switch (event) {

    case cv::EVENT_LBUTTONDOWN:
        drawing_box = true;
        box = cv::Rect(x, y, 0, 0);
        std::cout << x << "," << y <<"\n";
        clr = GetPixel(hdc,x, y);
        ReleaseDC(hWnd, hdc);
        R = GetRValue(clr);
        G = GetGValue(clr);
        B = GetBValue(clr);
        std::cout << "RGB = " << R << " " << G << " " << B <<"\n";
        break;

    }
}

int main(int argc, char *argv[])

{    HDC hdc;
     HWND hWnd;
     hWnd = GetDesktopWindow();
     hdc = GetWindowDC(hWnd);




    cv::Mat out(cv::Size(768, 1024), CV_8UC3, cv::Scalar::all(0));


    box = cv::Rect(-1, -1, 0, 0);
    cv::String name = "img";
    cv::String Output = "imgs";
    // 画像の読み込み
    cv::Mat src_img = cv::imread("C:\\data\\DSCN0048.jpg",1);

    cv::namedWindow(name, CV_WINDOW_AUTOSIZE);



    cv::Mat temp = src_img.clone();
    cv::Mat res_img = src_img.clone();

    cv::setMouseCallback(name, mouse_callback, (void *)&src_img);


    // 領域選択の処理(Escキーを押したら次の処理へ続く)
    bool roop = true;
    while (roop) {

        src_img.copyTo(temp);
        cv::imshow(name, temp);


        if (cv::waitKey(15) == 27)
            roop = false;



    }


    //以下の処理が分からないところ
  
    for (int y = 0; y > temp.rows; y++)
    {
        for (int x = 0; x > temp.cols; x++)
        {

                COLORREF color[768][1024];
                cv::Mat RR[768][1024];
                cv::Mat GG[768][1024];
                cv::Mat BB[768][1024];
                color[y][x] = { GetPixel(hdc,x,y) };
                RR[y][x] = GetRValue(color[y][x]) ;
                GG[y][x] = GetGValue(color[y][x]) ;
                BB[y][x] = GetBValue(color[y][x]) ;

                if (RR[y][x] == R && GG[y][x] == G && BB[y][x] == B)
                {
                    res_img.at<cv::Vec3b>(y, x) = { 0,0,255 };
                }
                ReleaseDC(hWnd, hdc);

        }
    }

//ここまで

   //出力処理
    cv::namedWindow(Output, CV_WINDOW_AUTOSIZE);
    cv::imshow(Output, res_img);

    cv::waitKey(0);

    cv::destroyAllWindows();

    return 0;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+2

とりあえず自前で全画素走査する簡素なコードを書いてみました.

//テスト
class Test
{
private:
    const cv::Mat &m_rSrcImg;
    cv::Mat m_ResultImg;    //クリックした位置の色に似ている箇所検出結果用の画像バッファ
    const std::string m_SrcWndName;
    const std::string m_ResultWndName;
    int m_SqThresh; //閾値(2乗値)

public:
    Test(
        const cv::Mat &rSrcImg, //処理対象画像
        const std::string &rSrcWndName, //処理対象画像表示&クリックする用のウィンドウ名
        const std::string &rResultWndName   //処理結果画像表示用ウィンドウ名
    )
        : m_rSrcImg( rSrcImg )
        , m_SrcWndName( rSrcWndName )
        , m_ResultWndName( rResultWndName )
        , m_SqThresh( 16*16 )   //※てきとーに閾値を決めてます
    {
        cv::imshow( rSrcWndName, rSrcImg );
        cv::setMouseCallback( rSrcWndName,  MouseCallback, this );
        m_ResultImg = cv::Mat::zeros( rSrcImg.rows, rSrcImg.cols, CV_8UC3 );
    }

    ~Test()
    {
        cv::destroyWindow( m_SrcWndName );
        cv::destroyWindow( m_ResultWndName );
    }

private:
    Test( const Test & );
    Test &operator=( const Test & );

    //CV用マウスコールバック
    static void MouseCallback( int event, int x, int y, int flags, void* param )
    {
        if( event != cv::EVENT_LBUTTONDOWN )return;
        ( ( Test* )param )->OnClick( x, y );
    }

    //マウス左クリック時処理
    void OnClick( int x, int y )    //引数はクリックされた座標
    {
        if( x<0 || y<0 || x>=m_rSrcImg.cols  ||  y>=m_rSrcImg.rows )return;

        //クリック位置の色を取得
        cv::Vec3b SelectedColor = m_rSrcImg.at<cv::Vec3b>( y, x );

        //全画素操作して,クリック位置と色が似ているかどうかチェック
        for( int y=0; y<m_rSrcImg.rows; ++y )
        {
            const cv::Vec3b *pSrcPix = m_rSrcImg.ptr< cv::Vec3b >( y );
            cv::Vec3b *pResultPix = m_ResultImg.ptr< cv::Vec3b >( y );
            for( int x=0; x<m_rSrcImg.cols; ++x, ++pSrcPix, ++pResultPix )
            {
                //(R,G,B)の差からてきとーに評価
                int dB = (int)( (*pSrcPix)[0] ) - (int)( SelectedColor[0] );
                int dG = (int)( (*pSrcPix)[1] ) - (int)( SelectedColor[1] );
                int dR = (int)( (*pSrcPix)[2] ) - (int)( SelectedColor[2] );

                bool IsSameColor = ( (dB*dB + dG*dG + dR*dR) <= m_SqThresh );

                //判定結果描画:似ている色な箇所は白,そうでない箇所は黒にしてます
                *pResultPix = ( IsSameColor   ?   cv::Vec3b( 255,255,255 )   :   cv::Vec3b( 0,0,0 ) );
            }
        }
        //結果表示
        cv::imshow( m_ResultWndName, m_ResultImg );
    }
};

//main
int main(void)
{
    //入力画像読込
    cv::Mat SrcImg = cv::imread( "test.png" );
    if( SrcImg.empty() )return 0;

    //処理
    Test T( SrcImg, "Src", "Result" );

    //キー押下すると終了
    cv::waitKey();
    return 0;
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/20 20:07

    丁寧なコメントまでつけていただきありがとうございます。
    プログラムのほう実行してみました。ひとつだけ質問があるのですが、
    bool IsSameColor = ((dB*dB + dG*dG + dR*dR) <= m_SqThresh)
    の処理部分が少し分かりません。
    クリックしたRGB値と各ピクセルRGB値の差を足し合わせて、閾値の評価ができるのでしょうか。
    初歩的な質問で申し訳ありません。

    キャンセル

  • 2018/07/20 20:14

    あと余談ですが、C++やOpenCVはどのような書籍(あるいはサイト)でどのように勉強していますか。
    まだまだ理解が浅いので(特にクラスの概念)、もっと勉強していかなきゃいけないと実感しているのですが……
    もしよろしければ教えていただいてもよろしいですか。

    キャンセル

  • 2018/07/20 20:35

    (R,G,B)3次元空間上での距離(の2乗)を評価しています.(なので,閾値も2乗した値を使用).
    わかりにくければ,例えばR成分を無視してGとBしか判定に用いない場合を考えてみるとわかりやすいかと.色を(G,B)平面上の点と考えれば,2つの色がどれくらい離れているかは,2点間の距離として考えることができ,それは sqrt( dG^2 + dB^2 ) ですよね.

    勉強… C++側は主に「Effective C++」系の本(言語文法の本ではないです.文法面は適当にググって身に着けた気がします),OpenCVはリファレンスと必要に応じた検索,とかでしょうか.

    キャンセル

  • 2018/07/23 14:16

    なるほど、RGBを座標空間と考えて評価をしていたということですね。分かりやすい説明ありがとうございます。
    勉強のほうも教えていただきありがとうございます。色々参考にしながら力をつけれるようがんばりたいと思います。

    キャンセル

+2

同じ色の識別という観点から。完全一致は難しい(わずかなズレでも別と判断)なのである程度の範囲(閾値)との比較を行うのがよろしいかと。

ソースを見る限りではマウスの操作が理解できているのであれば、画像の特定位置の色を取得して、判断になるかと。しかしながら、マウスの操作を待でなく単純ループであれば、処理は1回だけと思います。

ループを抜け出す仕掛けを用意して、マウス操作を待つべきですね。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 22:24

    回答ありがとうございます。
    最終的にはもちろん閾値を設けて検出を行いたいのですが、それ以前のプログラムがぼろぼろだと自覚していたため、質問した時点では閾値の話には触れていませんでした。
    私のイメージでは取得した色を画像内のピクセルすべて走査して比較することでしか検出できないと思っており、そうすると2重ループを用いないといけないと思っていたのですが……
    1度の処理で検出ができるということでしょうか。

    キャンセル

  • 2018/07/18 00:46

    閾値を設けて判定するところまで想定済みなのであれば,問題点はどこにあるのでしょうか?
    (「同じような色」というのをどう定義するのか? とかそういう方面?)

    OpenCVを用いていて色をデバイスコンテキスト経由で取得する理由がよくわかりませんが,ループ内にReleaseDC()があるところに危険な雰囲気を感じます.

    キャンセル

  • 2018/07/18 02:44

    閾値判定はthreshold関数を使うんですよね?
    お恥ずかしい話、コメントアウトしたループ文の部分を自分ではどうしたら良いか全く分からず、質問した次第です汗
    デバイスコンテキストは、GetPixel関数を使うために必要とMSDNに書いてあったので用いていました。。。
    デバイスコンテキストについてはあまり詳しく分かっていないで使っていました。

    キャンセル

  • 2018/07/18 09:56 編集

    thresholdのような既存関数を駆使する形でも,自前のループで画素毎に判定するのでも,どちらでもよいのではないでしょうか.(方法論を模索しているような段階では,後者の方がやりやすいと思う)
    (R,G,B)3要素全てについて,a==bという判定(現状)だと条件が厳しすぎる場合,簡単には,「|a-b|<=閾値」とか「(a-b)^2<=閾値」とかにして「同じ」と見なす範囲に幅を持たせてやればどうでしょうか.
    あるいは,「(R,G,B)3次元空間上での2点間の距離<=閾値」とかでも良いでしょう.
    (RGBの値でいろいろ処理してみてもどうにも所望の「同じような色」にならない,という場合は,判定を行う色空間を変えてみたりとか……)

    キャンセル

  • 2018/07/18 10:04

    色については,(デバイスコンテキストを用いる理由が特別無いのであれば)cv::Matが画像データなのだから,atとかptrとかそこらへんのメソッドで直に取得すれば事足りるのではないかと.

    キャンセル

  • 2018/07/18 11:16

    fana殿 同意です。まずはat辺りで試してみて、高速化が必要な場合Matの中を直接アクセスする方法が良いかと。質問者は比較の方法を知りたいと理解していますが、比較した部分をどのように出力するかが提示されていないので思案中。(別ウインドウで一致部分だけを表示とか?)
    またRGBでの比較よりHSVに変換しての方が色別では濃淡の範囲などを考えると実用的では?

    キャンセル

  • 2018/07/18 13:44

    色空間については「"同じような色"って何だろう?」なる求められている事柄次第なので何とも.「Lab(L*a*b*)での色差が人間の感覚に近い」とか何とか聞いたことがありますが,個人的には「そうか?」って感じだったりしますし.

    キャンセル

  • 2018/07/20 12:46

    お二人とも連絡が遅くなって申し訳ありません。
    今、atを用いて画素を直に獲得しabs()<=閾値を用いてとりあえず1ピクセルでプログラムを組んでいるところです。私が知りたいのは、
    ・貼り付けたソースファイルを軸にプログラムが完成できるかどうか
    ・クリックしてRGB値を取得した後の処理方法(コード)
    です。
    色については説明不足だったのですが、今のところは精度よりとりあえずプログラム自体が正しく動作するほうが重要なのでRGBで良いかなという感じです。もちろん、将来的にはRGB⇒HSV、Labに変換してからの処理のほうが実用的で良いというのは理解しています。

    キャンセル

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

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

関連した質問

  • 受付中

    Win32APIでBMPファイルを読み込む方法

    #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(H

  • 解決済

    OpenCvSharp3で重心を求めたいです

    以下のように、ラベリングを実行するプログラムはできたのですが、ここから重心の座標や面積を求めるプログラムを作成したいです。どなたか、教えていただけますか? 最終的な目標としては

  • 解決済

    visual studio 出力の文字化け

    文字化けをなくしたいです Visual studio 2017を使っています。 猫でもわかるプログラミングの通りにしていたら、下の写真のように文字化けしてしまいました。 宜しくお願

  • 解決済

    OpenCVを使った笑顔検出での問題

    前提・実現したいこと OpenCVで元からあるサンプルコード「smiledetect.cpp」を用いて笑顔検出のシステムを作っています。 検出機能を実装中に以下の問題が発生しました

  • 解決済

    Win32で自作のダイアログエディタで上下関係をはっきりしたい

    こんにちは。  前提・実現したいこと 今、Win32 APIでRisohEditorという名前のリソースエディタを作っています。 RisohEditor https://g

  • 解決済

    特定環境でAlphaBlendが表示されない

     前提・実現したいこと GDI+で画像を表示後、その上にAlphaBlendで黒い半透明のマスクを描画し、 かつ、特定の矩形のみマスクしないように表示したいと思っています。 ※全体

  • 解決済

    BitBltによる画像表示について

    現在C++でGUIアプリケーションを作成しているのですが、ウィンドウに画像を表示させたいです。 調べると、画像を表示するにはメッセージループのWM_PAINTの箇所に該当する記述を

  • 解決済

    WinAPIマルチスレッドにおけるOpenGLのメッセージループ

     前提・実現したいこと C言語でシンプルにWinAPIでマルチスレッドを作成しOpenGLのメッセージループを回すというプログラムを書きたいと思ったのですが表示されません。スレッド

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

  • C++

    3469questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • OpenCV

    1092questions

    OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。