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

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

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

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

Python

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

Q&A

解決済

1回答

4151閲覧

矩形内に直線が入っていた場合、出力1を出す

uriuri

総合スコア47

OpenCV

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

Python

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

0グッド

0クリップ

投稿2018/09/13 11:24

編集2018/09/14 03:48

イメージ説明
イメージ説明

python

1import cv2 2import math 3import numpy as np 4 5img_src = cv2.imread("test.jpg") 6 7gauss = cv2.GaussianBlur(img_src, (11, 11), 0) 8gray = cv2.cvtColor(gauss, cv2.COLOR_BGR2GRAY) 9ret,th1 = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) 10edges = cv2.Canny(th1, 50, 150) 11 12img, contours, hierarchy = cv2.findContours(th1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1) 13for i in range(0, len(contours)): 14#for cnt in contours: 15 cnt = contours[i] 16 area = cv2.contourArea(cnt) 17 #print(area) 18 19 if 500 < area < 5000: 20 rect = cv2.minAreaRect(cnt) 21 box = cv2.boxPoints(rect) 22 23 (cx, cy), (width, height), angle = rect 24 diag = np.linalg.norm([width, height]) 25 #print(width, height, diag) 26 27 A = width 28 B = height 29 C = diag 30 31 if A < B: 32 min = A 33 else: 34 min = B 35 if C < min: 36 min = C 37 print("min", min) 38 if 5 < min < 25: 39 # 40 cv2.drawContours(img_src, [box.astype(int)], -1, (0, 255, 0), 2) 41 42def within(line, rect): 43 44 p1, p2 = tuple(line[:2]), tuple(line[2:]) 45 #print("p1",p1) 46 #print("p2",p2) 47 #print("line",line) 48 return cv2.pointPolygonTest(box, p1, False) >= 0 and \ 49 cv2.pointPolygonTest(box, p2, False) >= 0 50 51LSD = cv2.createLineSegmentDetector() 52lines, width, prec, nfa = LSD.detect(edges) 53if lines is not None: 54 lines = np.squeeze(lines,axis=1) 55 for line in lines: 56 p1, p2 = tuple(line[:2]), tuple(line[2:]) 57 print("line",line) 58 59 if within(line, rect): 60 61 cv2.line(img_src, p1, p2, (0, 0, 255), 2) 62 63 #else: 64 65 # cv2.line(img_src, p1, p2, (255, 0, 0), 2) 66 67 68 69cv2.imshow('src', img_src) 70cv2.waitKey() 71cv2.destroyAllWindows() 72

今回、上記の処理をカメラを用いてリアルタイムで処理したのですが、矩形検出と直線検出が別処理で行われてしまいました。
理想としては矩形内に直線が含まれていた時、認識したいものとして出力1を出すようなコードを目指しています

編集部分

python

1def contains_lines(rect, lines): 2 3 cnt = 0 4 for line in lines: 5 p1, p2 = tuple(line[:2]), tuple(line[2:]) 6 if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ 7 cv2.pointPolygonTest(rect, p2, False) >= 0: 8 cnt += 1 9 10 return cnt == 2

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

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

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

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

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

oikashinoa

2018/09/13 11:57

テストに使った画像もUPすると話早いと思います
guest

回答1

0

ベストアンサー

線を含む長方形というのは1本でも含めばよいのですか?
その仮定で条件を満たす長方形を返す処理を関数化しました。

python

1import cv2 2import matplotlib.pyplot as plt 3import numpy as np 4 5 6def contains_lines(rect, lines): 7 8 cnt = 0 9 for line in lines: 10 p1, p2 = tuple(line[:2]), tuple(line[2:]) 11 if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ 12 cv2.pointPolygonTest(rect, p2, False) >= 0: 13 cnt += 1 # 線が長方形内に含まれる場合 14 print('cnt', cnt) # cnt 8 15 return cnt == 2 # 2本だけ含まれる場合 16 17def detect(img): 18 dst = [] # 線を1本でも含む長方形一覧 19 20 # 2値化する。 21 gauss = cv2.GaussianBlur(img, (11, 11), 0) 22 gray = cv2.cvtColor(gauss, cv2.COLOR_BGR2GRAY) 23 _, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) 24 25 # 線を検出する。 26 edges = cv2.Canny(binary, 50, 150) 27 LSD = cv2.createLineSegmentDetector() 28 lines, width, prec, nfa = LSD.detect(edges) 29 if lines is None: 30 return # 線が存在しない場合 31 lines = np.squeeze(lines, axis=1) # (NumLines, 1, 4) -> (NumLines, 4) 32 33 # 輪郭を検出する。 34 _, contours, _ = cv2.findContours( 35 binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1) 36 for cnt in contours: 37 area = cv2.contourArea(cnt) 38 if not 500 < area < 5000: 39 continue # 面積が小さすぎるまたは大きすぎる場合 40 41 rotated_rect = cv2.minAreaRect(cnt) 42 rect = cv2.boxPoints(rotated_rect) 43 (cx, cy), (width, height), angle = rotated_rect 44 print('center: ({:.1f}, {:.1f}), size: ({:.1f}, {:.1f}),' 45 'angle: {:.1f}'.format(cx, cy, width, height, angle)) 46 47 min_edge = min(width, height) # 短辺 48 if not 5 < min_edge < 25: 49 continue # 長方形の短辺が短すぎるまたはすぎる場合 50 51 if contains_lines(rect, lines): 52 dst.append(rect) 53 54 return dst 55 56# main 57###################################### 58img = cv2.imread("test.jpg") 59rects = detect(img) 60for rect in rects: 61 cv2.drawContours(img, [rect.astype(int)], -1, (0, 255, 0), 2) 62 print(rect) 63 64plt.imshow(img) 65plt.axis('off') 66plt.show()

イメージ説明

この部分の意図は長方形の短辺が短すぎたり、長過ぎたりしたものを除くという意図だと理解していますが、
ピタゴラスの定理より対角線が長方形の幅、高さより、短いということはありえないので、対角線を考慮する必要性はないと思います。(w^2 <= w^2 + h^2, h^2 <= w^2 + h^2 より)

A = width B = height C = diag if A < B: min = A else: min = B if C < min: min = C if 5 < min < 25:

投稿2018/09/13 12:09

編集2018/09/14 04:15
tiitoi

総合スコア21954

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

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

uriuri

2018/09/13 14:57

わざわざ綺麗にまとめていただき、ありがとうございます。 この処理をカメラで用いて実行することもできました。 矩形内に直線が2本ある場合の解釈として for line in lines: p1, p2 = tuple(line[:2]), tuple(line[2:]) if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ cv2.pointPolygonTest(rect, p2, False) >= 0: return True の部分の cv2.pointPolygonTest(rect, p1, False) >= 1にすることであっていますでしょうか。
tiitoi

2018/09/13 15:22

cv2.pointPolygonTest() >= 0 は、点が内側か外側かを見ているだけなので違います。(内側なら1、境界線上なら0、外側なら-1を返す。) 2本以上としたいなら、内側に含まれる線の数を数えてください。 ```python def contains_lines(rect, lines): """長方形 rect が線 lines を1本でも含むかどうか """ cnt = 0 for line in lines: p1, p2 = tuple(line[:2]), tuple(line[2:]) if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ cv2.pointPolygonTest(rect, p2, False) >= 0: cnt += 1 if cnt >= 2: return True return False ```
uriuri

2018/09/13 15:48

理解できていなくてすみません 内側に含まれる線の数を2本にしたい場合は ```python def contains_lines(rect, lines): """長方形 rect が線 lines を1本でも含むかどうか """ cnt = 0 for line in lines: p1, p2 = tuple(line[:2]), tuple(line[2:]) if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ cv2.pointPolygonTest(rect, p2, False) >= 0: cnt += 1 if cnt >= 2: 提示されたものが2本以上含むとき→2本にしたい場合、>を除くことで2本のときにTrueを返すということでいいでしょうか
tiitoi

2018/09/13 15:57

上記だと2本含まれた時点で retrun True なので、3本以上の場合も True が返ります。 2本未満でも3本以上も駄目で2本だけにしたい場合はまず全部の線が含まれるか調べてから、最後に含まれた数をチェックします。 ``` cnt = 0 for line in lines: p1, p2 = tuple(line[:2]), tuple(line[2:]) if cv2.pointPolygonTest(rect, p1, False) >= 0 and \ cv2.pointPolygonTest(rect, p2, False) >= 0: cnt += 1 return cnt == 2 ```
uriuri

2018/09/14 03:47

test.jpgで2本含む場合ためしたのですが検出されませんでした。 申し訳ないのですがコードを追記しますので添削お願いします
tiitoi

2018/09/14 04:21

まず以下のどちらかなのかで意味がかわってきます。 cnt が長方形内に含まれた線の数とした場合 * 2本含む場合: cnt == 2 * 2本以上含む場合: cnt >= 2 2本だけ含む場合という仮定のコードを編集して書きました。 提示された画像は8本の線が含まれるので、結果としては長方形は検出対象外となります。 次にご提示いただいたコードは、意図した通りに動きません。 1. 線の始点終点が含まれるかどうか調べる。 2. 含まれる場合は cnt += 1 する。 3. return cnt == 2 する。 つまり、cnt == 2 は絶対に起こりえないので、かならず False が返ります。 (なぜかわからない場合は、よく考えてみてください。)
uriuri

2018/09/14 04:51

提示した今回のコードではcnt +=1と同じインデントにreturnがあったためループがcnt=1で毎回返り値がでてしまいFalseとなっていたということでしょうか。 for文と同じインデントにreturn cnt==2を置きました。しかし今回の画像は8本の直線を含むため検出できなかったと判断しreturn cnt >=2を行ったところうまくいきました。 2本含む時の場合の指定として cnt += 1 if cnt ==2: return cnt == 2 これで検出されました。 理解としてまちがっていたらごめんなさい。
tiitoi

2018/09/14 04:56

``` 提示した今回のコードではcnt +=1と同じインデントにreturnがあったためループがcnt=1で毎回返り値がでてしまいFalseとなっていたということでしょうか。 ``` その理解であっています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問