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

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

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

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

C++

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

Q&A

3回答

2108閲覧

OpenCV2 C++ オセロの画像の白と黒を文字にして出力

shinto_0619

総合スコア6

OpenCV

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

C++

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

0グッド

0クリップ

投稿2021/08/05 12:25

黒と白の数を数えてどちらの方が多いかまではできたのですが一つ一つ白黒を出力していく方法がわかりません。

入力画像
イメージ説明
出力例
イメージ説明
このように出力したいです

c++

1 2//略 3 4int main(int argc, const char * argv[]){ 5 //画像変数の宣言 6 cv::Mat src_img, gray_img, dst_img, bin_img, bin_img2, tmp_img; 7 //変数宣言 8 int ku, si; 9 10 std::vector<std::vector<cv::Point>> contours; 11 std::vector<std::vector<cv::Point>> contours2; 12 13 //1. 入力画像をカラーで入力 14 src_img = cv::imread(FILE_NAME, cv::IMREAD_COLOR); 15 if (src_img.empty()) {//入力失敗の場合 16 fprintf(stderr, "Cannot read image file: %s.\n", FILE_NAME); 17 return (-1); 18 } 19 dst_img = cv::Mat::zeros(src_img.size(), CV_8UC1); 20 21 //2. グレースケール画像 22 cv::cvtColor(src_img, gray_img, cv::COLOR_BGR2GRAY); 23 //3. 二値化 24 cv::threshold(gray_img, bin_img, TH, MAX_VAL, cv::THRESH_BINARY_INV); 25 cv::threshold(gray_img, bin_img2, TH2, MAX_VAL, cv::THRESH_BINARY); 26 27 //4. 膨張縮小処理 28 cv::dilate(bin_img, bin_img, cv::Mat(), cv::Point(-1,-1), COUNT); 29 cv::erode(bin_img, bin_img, cv::Mat(), cv::Point(-1,-1), COUNT*2); 30 cv::dilate(bin_img, bin_img, cv::Mat(), cv::Point(-1,-1), COUNT); 31 32 cv::dilate(bin_img2, bin_img2, cv::Mat(), cv::Point(-1,-1), COUNT); 33 cv::erode(bin_img2, bin_img2, cv::Mat(), cv::Point(-1,-1), COUNT*2); 34 cv::dilate(bin_img2, bin_img2, cv::Mat(), cv::Point(-1,-1), COUNT); 35 36 37 //5.黒のコマの数を検出 38 tmp_img = bin_img.clone(); 39 cv::findContours(tmp_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); 40 std::cout << "黒 = " << contours.size() << std::endl; 41 42 ku = contours.size(); 43 44 //6.白のコマの数を検出 45 tmp_img = bin_img2.clone(); 46 cv::findContours(tmp_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); 47 std::cout << "白 = " << contours.size() << std::endl; 48 49 si = contours.size(); 50 51 //7.どちらが勝ったかの測定 52 if(ku>si){ 53 std::cout << "黒の勝ち\n"; 54 }else if(si<ku){ 55 std::cout << "白の勝ち\n"; 56 }else{ 57 std::cout << "引き分け\n"; 58 } 59 60 return 0; 61}

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

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

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

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

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

guest

回答3

0

まず,その絵だけを見ると,何故に findContours なんて方法をとっているのか? というあたりから疑問です.
(何らかの意図や前提条件的な理由等があるのであれば,質問文内に明記してください)


(入力画像に対する相応の事前知識を用いて良いならば,ですが,)
示されているような入力画像においては
「画像上での各マスの中央位置」というのは簡単に求めることができる(≒「既知」と言える)のではないのでしょうか?
あとは単にそれぞれの場所の色を相応に見てやればよいだけでしょう.


以下の例では,マスの中央っぽい場所の1pixelだけを見ていますが,必要に応じて少し広い範囲を見て判断する等すれば良いでしょう.
ShowImg は単に見ている場所を視覚化するための物で,処理には無関係)

C++

1int main() 2{ 3 cv::Mat SrcImg = cv::imread( "Othello.png", cv::IMREAD_GRAYSCALE ); 4 if( SrcImg.empty() )return 0; 5 6 cv::Mat ShowImg = cv::Mat::zeros( SrcImg.size(), CV_8UC3 ); 7 cv::cvtColor( SrcImg, ShowImg, cv::COLOR_GRAY2BGR ); 8 9 cv::threshold( SrcImg, SrcImg, 127, 255, cv::THRESH_BINARY ); 10 const double UnitW = SrcImg.cols / 8.0; 11 const double UnitH = SrcImg.rows / 8.0; 12 for( int y=0; y<8; ++y ) 13 { 14 int PosY = cvRound( (y+0.5)*UnitH ); 15 for( int x=0; x<8; ++x ) 16 { 17 int PosX = cvRound( (x+0.5)*UnitW ); 18 bool IsWhite =( SrcImg.at<unsigned char>(PosY,PosX) > 127 ); 19 std::cout << ( IsWhite ? "白" : "黒" ); 20 cv::circle( ShowImg, cv::Point(PosX,PosY), 3, cv::Scalar(0,0,255), -1 ); 21 } 22 std::cout << std::endl; 23 } 24 cv::imshow( "ShowImg", ShowImg ); 25 cv::waitKey(); 26 return 0; 27}

イメージ説明

投稿2021/08/06 01:05

編集2021/08/06 06:17
fana

総合スコア11660

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

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

0

オセロ盤の各マス(正方形)を検出することから出発すれば,
既存の「投稿 2021/08/18 17:25」の回答とは異なり,
回転具合だけでなく「マスの辺の長さ ≒ 隣接するコマの中心間距離の理想値」も同時に推定できる.
(→その推定結果情報の用途としては該回答と同じ)

背景にある程度ゴミがある画像
イメージ説明
…を入力とした結果がコレ↓
(画像中央に,{推定した長さで,推定した方向を向いた}線分を黄色で描画してある)
イメージ説明

C++

1int main() 2{ 3 cv::Mat ColorImg = cv::imread( "Othello_with_Noise.png" ); 4 if( ColorImg.empty() )return 0; 5 6 //contour検出 7 std::vector<std::vector<cv::Point>> Contours; 8 { 9 cv::Mat GrayImg; 10 cv::cvtColor( ColorImg, GrayImg, cv::COLOR_BGR2GRAY ); 11 cv::threshold( GrayImg, GrayImg, 80, 255, cv::THRESH_BINARY ); 12 cv::findContours(GrayImg, Contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); 13 } 14 //※ColorImgを以降,結果描画用途に使いまわす 15 ColorImg *= 0.4; 16 cv::drawContours( ColorImg, Contours, -1, cv::Scalar(0,0,255) ); //検出されたcontour群を赤で描画 17 18 //マスのサイズと回転角度の推定 19 int Angle_Deg; 20 int GridSize; 21 { 22 cv::Mat VotingSpace = cv::Mat::zeros( 90, std::min( ColorImg.rows, ColorImg.cols )/8, CV_8UC1 ); 23 const double SizeRateThresh = 0.9; 24 for( int i=0; i<Contours.size(); /*NOP*/ ) 25 { 26 auto rect = cv::minAreaRect( Contours[i] ); 27 if( (rect.size.width < rect.size.height*SizeRateThresh) || (rect.size.height < rect.size.width*SizeRateThresh) ) 28 { Contours.erase( Contours.begin()+i ); continue; } 29 30 int iSize = cvRound( (rect.size.width + rect.size.height)*0.5f ); 31 int iAngle = cvRound( rect.angle ); 32 if( iAngle<0 )iAngle+=360; 33 if( iAngle>=360 )iAngle-=360; 34 iAngle = iAngle % 90; 35 VotingSpace.at<unsigned char>( iAngle, iSize )++; 36 ++i; 37 } 38 cv::drawContours( ColorImg, Contours, -1, cv::Scalar(0,255,0) ); //投票に参加したcontourを緑で描画 39 40 cv::Point maxIdx; 41 cv::GaussianBlur( VotingSpace, VotingSpace, cv::Size(3,3), 0 ); 42 cv::minMaxLoc( VotingSpace, NULL, NULL, NULL, &maxIdx ); 43 Angle_Deg = maxIdx.y; 44 GridSize = maxIdx.x; 45 } 46 //結果表示 47 std::cout << "Estimated Grid Size = " << GridSize << std::endl; 48 std::cout << "Estimated Rotation[deg] = " << Angle_Deg << std::endl; 49 { //推定結果(マスのサイズと回転角度)を可視化する方法として,画像のど真ん中に線分として引いてみる 50 int cx = ColorImg.cols/2; 51 int cy = ColorImg.rows/2; 52 double Angle_Rad = RadFromDeg( Angle_Deg ); 53 cv::line( ColorImg, cv::Point(cx,cy), cv::Point( cx+cvRound(GridSize*cos(Angle_Rad)), cy+cvRound(GridSize*sin(Angle_Rad)) ), cv::Scalar(0,255,255), 3 ); 54 } 55 cv::imshow( "ColorImg", ColorImg ); 56 cv::waitKey(); 57 return 0; 58}

投稿2021/08/24 07:21

編集2021/08/27 05:13
fana

総合スコア11660

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

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

fana

2021/08/24 07:28 編集

既存の「投稿 2021/08/18 17:25」の回答 と比較して,ゴミに対する耐性がちょっとだけあるかな,と. 示した簡素な実装は,大多数のコマがマスの内側に収まっていることを前提としている. (結果例で緑で囲まれていないマスのように)コマがグリッド線をちょっと隠しているような場所も扱えるようにするなら,ちょっとだけcontour検出を工夫すればよい. 例えば,前処理にモルフォロジを入れるだけでもちょっとした切れ目は塞げるだろうし, コマと盤の下地との間に境界ができるように二値化するのでもよいだろう.
guest

0

質問コードの状況(findContoursでcontour群を見つけたけど,ここからどうするか?)から出発する形で方法を考えるならば……

検出した各contourの間の相対的な位置関係に着目すれば,
(方向の近さと距離の近さをパラメタとする適当な評価関数をでっちあげて用いる等することで)
「このcontourの右隣はこのcontourで…」といった隣接関係を推定することができ,8x8 のグリッド状の並びを推定できるはずだ.
→それができたら後は白黒を表示すればいい.

「隣接contourがあるハズの方向」については,画像が無回転なのだという前提を用いてよいならば自明であるし,
下図のように単純な回転がある程度の話ならば,輝度勾配の主たる方向から回転量を推定できる(オセロのコマは丸いから^^).

イメージ説明

てきとーに回転させた画像から実際に簡素な処理で輝度勾配から推定した方向を赤線で重畳した.
この程度の推定精度でも「隣はどれだろう?」に用いるには十分であろう.

投稿2021/08/18 08:25

fana

総合スコア11660

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

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

fana

2021/08/18 08:31 編集

* とりあえずこのような話にすれば,マスのサイズは既知情報でなくてもよい. * 回転を考慮する場合,最終的に白黒の並びを表示する際に左上のマスってどれだよ?って話が出てくるだろうから,無回転前提なのだと思うが. --- OpenCVタグのこれ系の質問は全く応答なしで放置される率が妙に高いように感じるな.
fana

2021/08/18 08:34

輝度勾配を使うとなると「背景部分が邪魔になるじゃん」とかなんとかいう話も考えるべきだろうけども, 本件はどう見ても人工的な綺麗な画像を相手にしている話だろうから,そういう話は考慮しないものとした.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問