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

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

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

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

3156閲覧

cv2で輪郭を近似したいがepsilonが決まらない

TakaKan

総合スコア10

OpenCV

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/04/12 02:41

編集2020/04/12 05:11

前提・実現したいこと

下の画像に何角形がどのくらい含まれているのかを知るために、opencvで輪郭を近似し、何角形かを判別したいです。
よろしくお願いいたします。

イメージ説明

発生している問題

コード自体は問題なく動いているのですが、例えば6角形と数えてほしいものを20角形と言われてしまったりと思い通りの結果にはなっていません。
そこで、

Python

1 approx_cnt = cv2.approxPolyDP(cnt, epsilon=0.005 * arclen, closed=True)

の、「epsilon」を変更すれば良いのではないかと考えたのですが、まずその考えはあっているでしょうか?
また、適切なepsilonを決定する方法を教えていただきたいです。(数値をいじってみたのですが、近似されすぎてしまったりもします)

該当のソースコード

付近のコードも載せておきます。

Python

1for i, cnt in enumerate(contours): 2 # 輪郭の周囲の長さを計算する。 3 arclen = cv2.arcLength(cnt, True) 4 # 輪郭を近似する。 5 approx_cnt = cv2.approxPolyDP(cnt, epsilon=0.005 * arclen, closed=True) 6 approx_contours.append(approx_cnt) 7 # 元の輪郭及び近似した輪郭の点の数を表示する。 8 print('contour {}: {} -> {}'.format(i, len(cnt), len(approx_cnt)))

試したこと

epsilonの数値をいじってみましたが、良い決定方法が分からず思い通りの結果にはなりませんでした。

補足情報(FW/ツールのバージョンなど)

Python3

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

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

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

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

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

tiitoi

2020/04/12 04:19

画像を質問欄に貼れますか?
TakaKan

2020/04/12 05:12

失礼いたしました。追加しました よろしくお願いいたします。
fana

2020/04/13 01:54

> epsilon=0.005 * arclen なぜこのような方法で与えているのか? という背景(というか考えというか)を示す方が良いのではないでしょうか. リファレンスを見るとepsilonの説明は "This is the maximum distance between the original curve and its approximation"とのことです.(私なら,これ読んだら最初は0.5だとか1.0とかそのあたりの値を試すかなぁ,という気がします)
guest

回答2

0

ベストアンサー

「質問への追記・修正、ベストアンサー選択の依頼」にも書きましたが,
リファレンスを見るとepsilonの説明は "This is the maximum distance between the original curve and its approximation"とのことです.
この値を周長に比例させて増減させることの有用性(意図,なぜそうしているのか)がわかりません.
まずは定数を与えてみることから始めてはどうでしょうか.

とりあえず epsilon=1,2,3,... と実際にやってみました.
一応コード(C++)も示しますが,このコードは単に epsilon=3 でやっただけです.
下図はその結果(epsilon=3)です.各Contourの中央ら辺にapproxPolyDP後の要素数(頂点数)を赤色で描画しています.

イメージ説明

結果としては,良さそうなやつもあれば,残念なやつもがありますが… 少なくとも

20角形

みたいな大きくかけ離れた結果と比べるとマシなのではないかと.

  • epsilonの値としては1や2では,画素レベルのギザギザの影響を抑制するには足りない感じでした.
  • このデータの場合,4とか5あたりでもよいのかもしれません

C++

1int main() 2{ 3 //画像読込(グレースケール) 4 cv::Mat Src = cv::imread( "Areas.png", CV_LOAD_IMAGE_GRAYSCALE ); 5 if( Src.empty() )return 0; 6 //2値化 7 cv::threshold( Src, Src, 128, 255, cv::THRESH_BINARY ); 8 9 //結果描画用画像バッファを用意 10 cv::Mat Draw; 11 cv::cvtColor( Src, Draw, CV_GRAY2BGR ); 12 13 //findContours 14 std::vector< std::vector< cv::Point> > Contours; 15 cv::findContours( Src, Contours, cv::RETR_LIST ,cv::CHAIN_APPROX_NONE ); 16 17 //approxPolyDPして結果を描画 18 for( auto &Contour : Contours ) 19 { 20 //てきとーにでかすぎるやつは無視 21 if( cv::contourArea( Contour ) > 50*50 )continue; 22 23 //approxPolyDP 24 cv::approxPolyDP( Contour, Contour, 3, true ); 25 {//approxPolyDP結果の点の個数をてきとーに描画してみる 26 int Gx=0, Gy=0; 27 for( const auto &p : Contour ){ Gx+=p.x; Gy+=p.y; } 28 Gx/=Contour.size(); 29 Gy/=Contour.size(); 30 31 std::stringstream SS; 32 SS << Contour.size(); 33 cv::putText( Draw, SS.str(), cv::Point(Gx-2,Gy+2), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0,0,255) ); 34 } 35 } 36 cv::imshow( "Draw", Draw ); 37 if( cv::waitKey() == 's' ){ cv::imwrite( "result.png", Draw ); } 38 39 return 0; 40}

投稿2020/04/14 05:22

編集2020/04/14 05:31
fana

総合スコア11658

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

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

fana

2020/04/14 05:36

epsilonの大きさはそこそこに留めて * 距離が近すぎる2頂点があれば1個に統合する * ある頂点を取っ払っても面積がそう変わらない(近似する前の大元のContourの面積との比較になるかな?)なら取っ払う …的な後処理を自前で一段かましても良いかもしれません.
TakaKan

2020/04/23 04:49

コードまでご丁寧にありがとうございました。 無事、実行できました。参考にしていた本がepsilonを弧の長さから決定していたのでそれに従っていましたが、確かに定数でも問題ないですよね。ありがとうございました
fana

2020/04/23 05:11

> 参考にしていた本がepsilonを弧の長さから決定していた 可能であれば,そういう決め方の意味(意図)を掴むようにすると良いです(「参考にする」とはそういうところまでも含む). 「大きさが異なる相似形な形状に対して同様な近似結果形状を得たい」とかそういう雰囲気の話に対しての決め方かな?と推測します. 例えば「ある形状を近似すると四角形になるとして,その形状をサイズ2倍とか10倍とかに拡大したものを近似してもやはり(元のサイズで近似した結果の四角形をサイズ2倍とか10倍した)四角形になる」ような結果を求めているような. 今回の画像では領域の大きさのばらつきが言うほどでもない感じですから,定数で行けると考えました.
fana

2020/04/23 05:20

この「とりあえず定数でやってみたらepsilonとしては3とか4で行けるかも感」という結果を元に, > epsilon=0.005 * arclen の「0.005」の部分の値を調整してやれば,領域サイズに対する自動調整が有効に働くのかもしれません.
TakaKan

2020/04/23 06:48

アドバイスありがとうございます。 そこまで深く考えながら本で学習をしていませんでした。 今後はそういったところまで意識していこうと思います。 大変ためになるコメントありがとうございました
guest

0

画像を見ると 二値画像に見えますが、どうでしょうか。
もし、そうならば、グレー画像にして、平滑処理(ぼかし)を入れるとどうでしょうか? 結構、二値画像って誤認識率が高いように感じています。(細かな凸凹を検出するため)

参考として。

投稿2020/04/12 11:56

pepperleaf

総合スコア6383

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

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

TakaKan

2020/04/12 13:48 編集

回答ありがとうございます。 ご指摘の通り、二値化をした画像です。最初はグレースケールのみで試したのですが、その結果画像の外枠の4点のみが抽出されてしまい、次にグレースケール画像の色を反転させて試しても同じ結果となったため、色反転を行った後にさらに二値を行っています。 また、平滑処理も行ってみたのですが、外枠の4点しか検出されませんでした。 また、findContoursの第二因数はRETR_LISTを基本的には用いましたが、おそらく全ての第二因数で実験を行いました。その結果も外枠のみの抽出となってしまいました。 元の画像はオレンジ背景に黒色の線が入ったものです。よろしくお願いいたします。
pepperleaf

2020/04/12 13:58

直線検出、円検出等を行った時に、平滑化、二値化等を試しましたが、二値化で、良い結果を得たことはありませんでした。(かえってノイズが増える) 元画像の問題もありますが、平滑化した結果の表示を見たり(させたり)しながら、最適な方法を探しました。検出結果が外枠の4点のみというのは、こちらのパラメータが適切でないと思います。 自分自身が模索中のため、適切な方法の提示ができませんが、参考として。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問