回答編集履歴

4

d

2019/06/25 14:31

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -279,3 +279,47 @@
279
279
 
280
280
 
281
281
  実際、取り組んでくうちにいろいろ課題が出てくるとは思いますが、基本的な方針については回答に記載されていると思いますので、あとは宿題とします。
282
+
283
+
284
+
285
+ ## 追記
286
+
287
+
288
+
289
+ > 実際のケースに近い画像で試してみたところ、赤ペンやマッキーで囲んだ枠をしっかり認識していました。また赤鉛筆では薄いためか厳しく、
290
+
291
+
292
+
293
+ 原因は以下です。
294
+
295
+
296
+
297
+ 線が細いため、2値化した時点で枠線に隙間が空いてしまっています。
298
+
299
+
300
+
301
+ ![イメージ説明](8d076c7354a32f72f7a42f2436b078b2.png)
302
+
303
+
304
+
305
+ これに対して、 findcontours() を行うと以下のようにそれぞれ別の輪郭あると検出されてしまいます。
306
+
307
+ そのため、複数に分割されてしまっている輪郭を1つの輪郭として統合するような処理が必要となると思います。
308
+
309
+
310
+
311
+ ![イメージ説明](c4827070a037411902f83f8d9a1252e9.png)
312
+
313
+
314
+
315
+ > こちらの環境のためかエラーメッセージが出る箇所もあったので追記しました。
316
+
317
+
318
+
319
+ おそらく、質問者さんの環境の OpenCV はバージョン3 なのだと思います。
320
+
321
+ findcontours() の返り値が OpenCV 3 はタプル3つだったのが、OpenCV 4 ではタプル2つに変更されました。
322
+
323
+
324
+
325
+ [OpenCV - findContours() による輪郭抽出](http://pynote.hatenablog.com/entry/opencv-findcontours)

3

d

2019/06/25 14:31

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -179,3 +179,103 @@
179
179
 
180
180
 
181
181
  結果
182
+
183
+
184
+
185
+ ## funa さんのアドバイスを受けて追記
186
+
187
+
188
+
189
+ > 簡便な方法として,drawContoursの直前で凸包に変えてしまえば,とりあえず「一部切れたけども連続はしている(=1つのContourではある)」的なパターンに関しては救えそうな気もしますが…
190
+
191
+
192
+
193
+ 囲った線が一箇所途切れている場合は、funa さんのアドバイス通りに輪郭を cv2.convexHull() で凸包にすることで対応できたので、参考までに追記しておきます。
194
+
195
+
196
+
197
+ ```python
198
+
199
+ import cv2
200
+
201
+
202
+
203
+ # 画像を読み込む。
204
+
205
+ img = cv2.imread("sample.png")
206
+
207
+
208
+
209
+ # HSV 色空間に変換する。
210
+
211
+ hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
212
+
213
+
214
+
215
+ # 2値化で赤の枠線を抽出する。
216
+
217
+ binary = cv2.inRange(hsv, (170, 0, 0), (180, 255, 255))
218
+
219
+
220
+
221
+ # OPENING でノイズを消す。
222
+
223
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
224
+
225
+ eroded = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
226
+
227
+
228
+
229
+ # 輪郭を抽出する。
230
+
231
+ contours, hierarchy = cv2.findContours(
232
+
233
+ eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
234
+
235
+ )
236
+
237
+
238
+
239
+ # 誤検出の輪郭を消す。
240
+
241
+ contours = list(filter(lambda x: cv2.contourArea(x) > 100, contours))
242
+
243
+
244
+
245
+ # 輪郭の凸包に置き換える。
246
+
247
+ convex_hulls = list(map(cv2.convexHull, contours))
248
+
249
+
250
+
251
+ # 検出された輪郭内部を (255, 255, 255) で塗りつぶす。
252
+
253
+ mask = np.zeros_like(img)
254
+
255
+ cv2.drawContours(mask, convex_hulls, -1, color=(255, 255, 255), thickness=-1)
256
+
257
+
258
+
259
+ # 元画像でマスクの値が (255, 255, 255) の画素以外を白に置換する。
260
+
261
+ img = np.where(mask == 255, img, 255)
262
+
263
+
264
+
265
+ # 結果を保存する。
266
+
267
+ cv2.imwrite("test.png", img)
268
+
269
+ ```
270
+
271
+
272
+
273
+ 2箇所以上途切れていたりする場合は findContours() の輪郭が分割されてしまうので、統合する処理を入れる必要がありそうです。
274
+
275
+
276
+
277
+ ![イメージ説明](707e6992185ce25592853ffaafd0559f.png)
278
+
279
+
280
+
281
+ 実際、取り組んでくうちにいろいろ課題が出てくるとは思いますが、基本的な方針については回答に記載されていると思いますので、あとは宿題とします。

2

d

2019/06/25 12:12

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  * 2.1. HSV 色空間に変換する。
22
22
 
23
- * 2.2. 赤色とそれ以外を cv2.inRange() で2値化する。
23
+ * 2.2. 赤色とそれ以外を cv2.inRange() で2値化する。(赤以外の場合は要パラメータ調整)
24
24
 
25
25
  [OpenCV - inRange による範囲指定で2値化する方法について](http://pynote.hatenablog.com/entry/opencv-inrange)
26
26
 

1

d

2019/06/24 17:22

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -59,6 +59,16 @@
59
59
 
60
60
 
61
61
  ## サンプルコード
62
+
63
+
64
+
65
+ 以下の環境で確認
66
+
67
+
68
+
69
+ * OpenCV 4
70
+
71
+ * Python 3.7
62
72
 
63
73
 
64
74