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

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

新規登録して質問してみよう
ただいま回答率
85.50%
OpenCV

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

C++

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

Q&A

解決済

2回答

785閲覧

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

leliel1193

総合スコア9

OpenCV

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

C++

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

0グッド

1クリップ

投稿2018/07/17 12:15

前提・実現したいこと

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

該当のソースコード

C++

1 2cv::Rect box; 3 4bool drawing_box = false; 5 6double R, G, B; 7COLORREF clr; 8 9 10void mouse_callback(int event, int x, int y, int flags, void* param) { 11 cv::Mat* image = static_cast<cv::Mat*>(param); 12 HDC hdc; 13 HWND hWnd; 14 hWnd = GetDesktopWindow(); 15 hdc = GetWindowDC(hWnd); 16 17 switch (event) { 18 19 case cv::EVENT_LBUTTONDOWN: 20 drawing_box = true; 21 box = cv::Rect(x, y, 0, 0); 22 std::cout << x << "," << y <<"\n"; 23 clr = GetPixel(hdc,x, y); 24 ReleaseDC(hWnd, hdc); 25 R = GetRValue(clr); 26 G = GetGValue(clr); 27 B = GetBValue(clr); 28 std::cout << "RGB = " << R << " " << G << " " << B <<"\n"; 29 break; 30 31 } 32} 33 34int main(int argc, char *argv[]) 35 36{ HDC hdc; 37 HWND hWnd; 38 hWnd = GetDesktopWindow(); 39 hdc = GetWindowDC(hWnd); 40 41 42 43 44 cv::Mat out(cv::Size(768, 1024), CV_8UC3, cv::Scalar::all(0)); 45 46 47 box = cv::Rect(-1, -1, 0, 0); 48 cv::String name = "img"; 49 cv::String Output = "imgs"; 50 // 画像の読み込み 51 cv::Mat src_img = cv::imread("C:\data\DSCN0048.jpg",1); 52 53 cv::namedWindow(name, CV_WINDOW_AUTOSIZE); 54 55 56 57 cv::Mat temp = src_img.clone(); 58 cv::Mat res_img = src_img.clone(); 59 60 cv::setMouseCallback(name, mouse_callback, (void *)&src_img); 61 62 63 // 領域選択の処理(Escキーを押したら次の処理へ続く) 64 bool roop = true; 65 while (roop) { 66 67 src_img.copyTo(temp); 68 cv::imshow(name, temp); 69 70 71 if (cv::waitKey(15) == 27) 72 roop = false; 73 74 75 76 } 77 78 79 //以下の処理が分からないところ 80   81 for (int y = 0; y > temp.rows; y++) 82 { 83 for (int x = 0; x > temp.cols; x++) 84 { 85 86 COLORREF color[768][1024]; 87 cv::Mat RR[768][1024]; 88 cv::Mat GG[768][1024]; 89 cv::Mat BB[768][1024]; 90 color[y][x] = { GetPixel(hdc,x,y) }; 91 RR[y][x] = GetRValue(color[y][x]) ; 92 GG[y][x] = GetGValue(color[y][x]) ; 93 BB[y][x] = GetBValue(color[y][x]) ; 94 95 if (RR[y][x] == R && GG[y][x] == G && BB[y][x] == B) 96 { 97 res_img.at<cv::Vec3b>(y, x) = { 0,0,255 }; 98 } 99 ReleaseDC(hWnd, hdc); 100 101 } 102 } 103 104//ここまで 105 106 //出力処理 107 cv::namedWindow(Output, CV_WINDOW_AUTOSIZE); 108 cv::imshow(Output, res_img); 109 110 cv::waitKey(0); 111 112 cv::destroyAllWindows(); 113 114 return 0; 115} 116

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

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

//テスト 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 05:18

編集2018/07/20 05:19
fana

総合スコア11634

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

leliel1193

2018/07/20 11:07

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

2018/07/20 11:14

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

2018/07/20 11:35

(R,G,B)3次元空間上での距離(の2乗)を評価しています.(なので,閾値も2乗した値を使用). わかりにくければ,例えばR成分を無視してGとBしか判定に用いない場合を考えてみるとわかりやすいかと.色を(G,B)平面上の点と考えれば,2つの色がどれくらい離れているかは,2点間の距離として考えることができ,それは sqrt( dG^2 + dB^2 ) ですよね. 勉強… C++側は主に「Effective C++」系の本(言語文法の本ではないです.文法面は適当にググって身に着けた気がします),OpenCVはリファレンスと必要に応じた検索,とかでしょうか.
leliel1193

2018/07/23 05:16

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

0

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

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

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

投稿2018/07/17 13:16

MasahikoHirata

総合スコア3747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

leliel1193

2018/07/17 13:24

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

2018/07/17 15:46

閾値を設けて判定するところまで想定済みなのであれば,問題点はどこにあるのでしょうか? (「同じような色」というのをどう定義するのか? とかそういう方面?) OpenCVを用いていて色をデバイスコンテキスト経由で取得する理由がよくわかりませんが,ループ内にReleaseDC()があるところに危険な雰囲気を感じます.
leliel1193

2018/07/17 17:44

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

2018/07/18 01:05 編集

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

2018/07/18 01:04

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

2018/07/18 02:16

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

2018/07/18 04:44

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

2018/07/20 03:46

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問