teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

4

d

2019/06/25 14:31

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -138,4 +138,26 @@
138
138
 
139
139
  ![イメージ説明](707e6992185ce25592853ffaafd0559f.png)
140
140
 
141
- 実際、取り組んでくうちにいろいろ課題が出てくるとは思いますが、基本的な方針については回答に記載されていると思いますので、あとは宿題とします。
141
+ 実際、取り組んでくうちにいろいろ課題が出てくるとは思いますが、基本的な方針については回答に記載されていると思いますので、あとは宿題とします。
142
+
143
+ ## 追記
144
+
145
+ > 実際のケースに近い画像で試してみたところ、赤ペンやマッキーで囲んだ枠をしっかり認識していました。また赤鉛筆では薄いためか厳しく、
146
+
147
+ 原因は以下です。
148
+
149
+ 線が細いため、2値化した時点で枠線に隙間が空いてしまっています。
150
+
151
+ ![イメージ説明](8d076c7354a32f72f7a42f2436b078b2.png)
152
+
153
+ これに対して、 findcontours() を行うと以下のようにそれぞれ別の輪郭あると検出されてしまいます。
154
+ そのため、複数に分割されてしまっている輪郭を1つの輪郭として統合するような処理が必要となると思います。
155
+
156
+ ![イメージ説明](c4827070a037411902f83f8d9a1252e9.png)
157
+
158
+ > こちらの環境のためかエラーメッセージが出る箇所もあったので追記しました。
159
+
160
+ おそらく、質問者さんの環境の OpenCV はバージョン3 なのだと思います。
161
+ findcontours() の返り値が OpenCV 3 はタプル3つだったのが、OpenCV 4 ではタプル2つに変更されました。
162
+
163
+ [OpenCV - findContours() による輪郭抽出](http://pynote.hatenablog.com/entry/opencv-findcontours)

3

d

2019/06/25 14:31

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -88,4 +88,54 @@
88
88
 
89
89
  ![イメージ説明](c4593ff289107267cefa6617437a335c.png)
90
90
 
91
- 結果
91
+ 結果
92
+
93
+ ## funa さんのアドバイスを受けて追記
94
+
95
+ > 簡便な方法として,drawContoursの直前で凸包に変えてしまえば,とりあえず「一部切れたけども連続はしている(=1つのContourではある)」的なパターンに関しては救えそうな気もしますが…
96
+
97
+ 囲った線が一箇所途切れている場合は、funa さんのアドバイス通りに輪郭を cv2.convexHull() で凸包にすることで対応できたので、参考までに追記しておきます。
98
+
99
+ ```python
100
+ import cv2
101
+
102
+ # 画像を読み込む。
103
+ img = cv2.imread("sample.png")
104
+
105
+ # HSV 色空間に変換する。
106
+ hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
107
+
108
+ # 2値化で赤の枠線を抽出する。
109
+ binary = cv2.inRange(hsv, (170, 0, 0), (180, 255, 255))
110
+
111
+ # OPENING でノイズを消す。
112
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
113
+ eroded = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
114
+
115
+ # 輪郭を抽出する。
116
+ contours, hierarchy = cv2.findContours(
117
+ eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
118
+ )
119
+
120
+ # 誤検出の輪郭を消す。
121
+ contours = list(filter(lambda x: cv2.contourArea(x) > 100, contours))
122
+
123
+ # 輪郭の凸包に置き換える。
124
+ convex_hulls = list(map(cv2.convexHull, contours))
125
+
126
+ # 検出された輪郭内部を (255, 255, 255) で塗りつぶす。
127
+ mask = np.zeros_like(img)
128
+ cv2.drawContours(mask, convex_hulls, -1, color=(255, 255, 255), thickness=-1)
129
+
130
+ # 元画像でマスクの値が (255, 255, 255) の画素以外を白に置換する。
131
+ img = np.where(mask == 255, img, 255)
132
+
133
+ # 結果を保存する。
134
+ cv2.imwrite("test.png", img)
135
+ ```
136
+
137
+ 2箇所以上途切れていたりする場合は findContours() の輪郭が分割されてしまうので、統合する処理を入れる必要がありそうです。
138
+
139
+ ![イメージ説明](707e6992185ce25592853ffaafd0559f.png)
140
+
141
+ 実際、取り組んでくうちにいろいろ課題が出てくるとは思いますが、基本的な方針については回答に記載されていると思いますので、あとは宿題とします。

2

d

2019/06/25 12:12

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -9,7 +9,7 @@
9
9
  * 1. 画像を読み込む。
10
10
  * 2. 枠線部分のみを2値化で抽出する。
11
11
  * 2.1. HSV 色空間に変換する。
12
- * 2.2. 赤色とそれ以外を cv2.inRange() で2値化する。
12
+ * 2.2. 赤色とそれ以外を cv2.inRange() で2値化する。(赤以外の場合は要パラメータ調整)
13
13
  [OpenCV - inRange による範囲指定で2値化する方法について](http://pynote.hatenablog.com/entry/opencv-inrange)
14
14
 
15
15
  * 2.3. ノイズをモルフォロジー変換で消す。

1

d

2019/06/24 17:22

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -30,6 +30,11 @@
30
30
 
31
31
  ## サンプルコード
32
32
 
33
+ 以下の環境で確認
34
+
35
+ * OpenCV 4
36
+ * Python 3.7
37
+
33
38
  ```python
34
39
  import cv2
35
40