🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
OpenCV

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

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

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

Q&A

解決済

4回答

1719閲覧

面積(画素数)を指定して動画からその面積(画素数)を持つ特定色のものを認識したい

退会済みユーザー

退会済みユーザー

総合スコア0

OpenCV

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

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

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

0グッド

0クリップ

投稿2019/11/18 06:45

編集2019/12/03 03:36

下記のコードは動画から特定色を検出し、その検出した特定色の一番面積の大きいところを四角で描画するものです。
このコードを指定した面積のみ特定色を取り出せるように変更したいのですが、どのようにコードを書けばよいでしょうか。

import cv2 import numpy as np def find_rect_of_target_color(image): hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL) h = hsv[:, :, 0] s = hsv[:, :, 1] v = hsv[:, :, 2] mask = np.zeros(h.shape, dtype=np.uint8) mask[(h > 240) & ((100 < s) & (s < 200)) & (v > 180)] = 255 mask, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) rects = [] for contour in contours: approx = cv2.convexHull(contour) rect = cv2.boundingRect(approx) rects.append(np.array(rect)) return rects capture = cv2.VideoCapture(0) while cv2.waitKey(30) < 0: _, frame = capture.read() rects = find_rect_of_target_color(frame) if len(rects) > 0: rect = max(rects, key=(lambda x: x[2] * x[3])) cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 0, 255), thickness=2) cv2.imshow('red', frame) capture.release() cv2.destroyAllWindows()

・試したコード

import cv2 import numpy as np def find_rect_of_target_color(image): hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL) h = hsv[:, :, 0] s = hsv[:, :, 1] v = hsv[:, :, 2] mask = np.zeros(h.shape, dtype=np.uint8) mask[(h > 240) & ((100 < s) & (s < 200)) & (v > 180)] = 255 mask, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) rects = [] for contour in contours: approx = cv2.convexHull(contour) rect = cv2.boundingRect(approx) rects.append(np.array(rect)) return rects capture = cv2.VideoCapture(0) while cv2.waitKey(30) < 0: _, frame = capture.read() rects = find_rect_of_target_color(frame) for x, y, w, h in rects: area = w * h if area < 100: continue elif area >500: continue cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), thickness=2) cv2.imshow('red', frame) capture.release() cv2.destroyAllWindows()

・面積

-*- coding: utf-8 -*- import cv2 import numpy as np img = cv2.imread("/home/pi/picture/alarm9-1.png") hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL) h, s, v = cv2.split(hsv) mask = np.zeros_like(h) mask[(h > 240) & ((100 < s) & (s < 200)) & (v > 180)] = 255 # 輪郭を抽出する。 mask, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 各輪郭の面積を出力する。(デバッグ用) print(list(map(cv2.contourArea, contours))) cv2.waitKey(0) cv2.destroyAllWindows()

・出力結果

[1.5, 0.0, 0.0, 3.0, 2.5, 0.0, 0.5, 7.5, 49.0, 12.5, 0.0, 3.5, 0.0, 0.0, 0.0, 0.0, 4.5, 0.0, 0.0, 3.5, 19.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.5, 0.0, 0.5, 1.0, 3.5, 5052.0, 8.5, 5.5, 2.0, 4.0, 4.0, 15.0, 25.5, 2.0, 8.5, 26.0, 5.5, 18.5, 2.0, 4.0, 44.5, 13.5, 33.0, 2.0, 2.0, 22.5, 7.0, 6.0, 5.5, 4.0, 14.5, 8.5, 2.0, 4.0, 2.0, 831.5, 4.5, 0.0, 0.0, 2.0, 4.0, 10.0, 6.0, 2.0, 6.0, 9.5, 2.0, 2.0, 17.0, 14.0, 12.0, 4.0, 2.0, 61.5, 4.0, 4.0, 23.0, 14.5, 4.0, 2.0, 11.5, 2.0, 2.0, 7.5, 43.5, 4.0, 13.0, 4.0, 2.0, 11.0, 4.0, 2.0, 6.0, 4.0, 2.0, 5.5, 6.0, 11.5]

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

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

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

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

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

thkana

2019/11/18 14:23

> その検出した特定色の一番面積の大きいところを四角で描画するものです。 本当にそうですか?
退会済みユーザー

退会済みユーザー

2019/11/20 01:09

if len(rects) > 0: rect = max(rects, key=(lambda x: x[2] * x[3])) cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 0, 255), thickness=2) この部分がそうだと思うのですが違うのですか?
退会済みユーザー

退会済みユーザー

2019/11/21 16:20

そのboundingrectで矩形をもと求めてから rect = max(rects, key=(lambda x: x[2] * x[3])) cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 0, 255), thickness=2) の処理を行うことで最大の領域を持つものを視覚で描画するのだと思っているのですが、この考えは違いますかね?
coco_bauer

2019/11/25 09:54

「指定した面積のみ特定色を取り出せるように変更したい」というのは、 『画像で使われている全ての色のリストを作って、それらの色について、その色の一番面積の大きいところの面積が指定した面積に等しいか否かを判断して、等しければ、その色を出力する』ようになれば良いということでしょうか? 質問の文章から得たイメージは上記のような感じなのですが、これは質問の内容に一致していますか?
退会済みユーザー

退会済みユーザー

2019/11/26 15:33

いいえ、そういうわけではありません。 今のプログラムではHSVを設定してその値(色)の範囲内にあるものを認識して、その領域が最も大きいものを四角で描画するものです。 これをHSVを設定して特定職を認識するのはそのままで、「領域が最も大きいもの」ではなく、「面積(500から800など)を指定して」その大きさの範囲内にあるものを認識して四角で描画できるようにしたいんです。 説明下手ですみません。
coco_bauer

2019/11/27 00:04

面積とは、特定の色の画素数という事ですか??? 質問の中で用語の定義を明確にしないと、訳が判りません。
退会済みユーザー

退会済みユーザー

2019/11/27 03:33

そういうことです。 わかりづらくてすみませんでした...
退会済みユーザー

退会済みユーザー

2019/11/27 04:00

質問のところに、試しに書いたコードとその結果を載せました。 見ていただけますでしょうか。
guest

回答4

0

ベストアンサー

輪郭の面積は cv2.contourArea() 関数で計算できるので、cv2.findContours() で抽出された輪郭のうち、指定した範囲の面積を持つ輪郭を抽出したい場合は、組み込み関数 filter() を使って、以下のように抽出すればよいです。
この場合、1000 ~ 5000 の面積をもつ輪郭が抽出されます。

python

1contours = list(filter(lambda x: 1000 <= cv2.contourArea(x) <= 5000, contours))

この面積の範囲の値は画像によって変える必要があるので、print(list(map(cv2.contourArea, contours))) で各輪郭の面積を出力して、それを元に決めればよいと思います。

コード全体

python

1import cv2 2import numpy as np 3 4 5def find_rect_of_target_color(img): 6 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 7 h, s, v = cv2.split(hsv) 8 9 mask = np.zeros_like(h) 10 mask[(h > 170)] = 255 11 12 # 輪郭を抽出する。 13 _, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 14 15 # 各輪郭の面積を出力する。(デバッグ用) 16 print(list(map(cv2.contourArea, contours))) 17 18 # 指定した範囲の面積を持つ輪郭のみ、抽出する。 19 contours = list(filter(lambda x: 1000 <= cv2.contourArea(x) <= 5000, contours)) 20 21 # 輪郭に外接する長方形を取得する。 22 rects = map(cv2.boundingRect, contours) 23 24 return rects 25 26 27img = cv2.imread("sample.png") 28 29rects = find_rect_of_target_color(img) 30 31for x, y, w, h in rects: 32 cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), thickness=2) 33 34cv2.imwrite("result.png", img)

イメージ説明
入力画像

イメージ説明
出力画像

※1: mask[(h > 170)] = 255 の部分は抽出したい色に応じて変更してください。
※2: 1000 <= cv2.contourArea(x) <= 5000 の部分は抽出したい面積の範囲に応じて変更してください。

動画が流れない問題について

動画が流れるわけでもなく、どのキーを押しても反応しなくなりました。

cv2.imshow() が while ループの外にあるので、動画は表示されません。
ループの中に入れるべきだと思います。

python

1while cv2.waitKey(30) < 0: 2 _, frame = capture.read() 3 rects = find_rect_of_target_color(frame) 4 if len(rects) > 0: 5 rect = max(rects, key=(lambda x: x[2] * x[3])) 6 cv2.rectangle(frame, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 0, 255), thickness=2) 7 8 cv2.imshow('red', frame)

投稿2019/12/02 04:31

編集2019/12/02 04:34
tiitoi

総合スコア21956

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

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

退会済みユーザー

退会済みユーザー

2019/12/02 08:33

まずは、検出できるか試すために各HSVを設定し、抽出する面積を大きくして試してみたのですが、画像内の赤色の部分に枠が表示されませんでした。 いままでは同じHSVの範囲で検出できていたのですが、どうしてでしょうか。 ※書き直したコードを追記しておきました。
tiitoi

2019/12/02 09:08 編集

回答のコードの cv2.COLOR_BGR2HSV を cv2.COLOR_BGR2HSV_FULL に変えてください
退会済みユーザー

退会済みユーザー

2019/12/02 10:58

ありがとうございます。認識できました あと、コメントの編集前に分けて行うと良いと書いてあったので試してみて、画像の二値化はできましたがprint(list(map(cv2.contourArea, contours)))を使った各輪郭の面積の求め方がよくわかりませんでした。どのようにコードを書けば良いのですか。
tiitoi

2019/12/02 11:26

contours は輪郭のリストになっていて、リストの要素から、cv2.contourArea() に面積を求めたい輪郭を1つ渡すと面積を返すようになっています。 すべての輪郭に cv2.contourArea() を適用するために map() を使っています。 map() に不慣れのようであれば、 [cv2.contourArea(c) for c in contours] と同じ意味なので、こちらでもよいです。 こうすると各輪郭の面積のリストができます。
退会済みユーザー

退会済みユーザー

2019/12/03 03:55

mapのままでできました。 結果とコードを質問に追記したのですがあってますか? 結果では一番大きい数字が5052.0で次に831.5なのですがこれらがこれらを参考にして面積を設定すればよいのですか?
tiitoi

2019/12/03 03:56

それでよいです。 認識したい輪郭の面積以外は無視されるように面積の範囲を設定してみてください
退会済みユーザー

退会済みユーザー

2019/12/03 05:11

ありがとうございました! もう1つ質問があって、小さいものを認識するときは面積の設定がシビアになって同じところに複数個枠が表示されそうだと思ったのですが、その中でも最も大きな物を認識できるようにはできるでしょうか...
tiitoi

2019/12/03 05:29

面積で下限、上限で filter() したあと、max で面積が最大の輪郭を選択すればいいのではないでしょうか contours = max(contours, key=lambda x: cv2.contourArea(x))
退会済みユーザー

退会済みユーザー

2019/12/05 05:07

ありがとうございます! 何とかできました!
guest

0

とりあえず静止画でそのコードが調べているものを表示してみました。
どう考えますか。

Python

1import cv2 2import numpy as np 3 4def find_rect_of_target_color(image): 5 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL) 6 h = hsv[:, :, 0] 7 s = hsv[:, :, 1] 8 v = hsv[:, :, 2] 9 mask = np.zeros(h.shape, dtype=np.uint8) 10 mask[(h > 240) & ((100 < s) & (s < 200)) & (v > 180)] = 255 11 mask, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 12 rects = [] 13 for contour in contours: 14 approx = cv2.convexHull(contour) 15 rect = cv2.boundingRect(approx) 16 rects.append(np.array(rect)) 17 return rects 18 19img = cv2.imread(r'd:\testdata\areatest2.png') 20rects=find_rect_of_target_color(img) 21if len(rects) > 0: 22 rect = max(rects, key=(lambda x: x[2] * x[3])) 23 cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), (0, 255, 0), thickness=2) 24cv2.imshow("test", img) 25cv2.waitKey(0) 26cv2.destroyAllWindows()

テスト用に入力した画像(ペイントでマウスぐりぐりしたのでナンですが)
入力画像
で、こういうことになりました。
出力画像
あなたのいう面積ってなんですか、少なくとも(プログラミング抜きにしても)普通にいう面積とは違うもので、この検出方法でいいんですか、という話。

投稿2019/12/01 12:34

thkana

総合スコア7703

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

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

退会済みユーザー

退会済みユーザー

2019/12/02 03:37

確かにこれは想定と違いました。 説明しづらいですが、私の考えているのは、赤色が占める面積が最も大きいものだと思います。 ですが、そしてその範囲を四角で囲みたいです。
guest

0

cv2.imshow('red', frame) のインデントが間違っています。
Whileの中にないため動画が表示されません。

投稿2019/11/27 06:10

Pedgin

総合スコア6

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

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

退会済みユーザー

退会済みユーザー

2019/11/27 07:35

ありがとうございます。インデントを直したら写りました。 ちなみにこのコード(2番めのコード)はちゃんと面積の範囲を設定して四角で描画するようになっているでしょうか。
Pedgin

2019/11/27 08:02 編集

はい、boundingRectのw,hから画素数を求め、ifでフィルタしているので正しいです。
coco_bauer

2019/11/27 08:18

質問者に「面積とは、特定の色の画素数という事ですか?」と問うたところ「そういうことです。」との返事がありました。特定の色の画素の集まりに外接する四角形内部の面積(画素数)と、四角形の中に含まれる特定の色の画素数は一致しない可能性が高そうに思われます。(特定の色の領域の形状は、長方形に限られているというような条件でもない限りは) どうも、質問者の意図を把握するのは諦めたほうが良さそうです。
Pedgin

2019/11/27 09:30 編集

質問者さんの意図(コード)を汲むのであれば、w * hから前者の「特定の色の画素の集まりに外接する四角形内部の面積(画素数)」をフィルタリングすることになりますね。
退会済みユーザー

退会済みユーザー

2019/11/29 05:12

cocoさん このようなことに関しては本当に初心者なのでいろいろ見当違いなことを言ってしまってると思います。 申し訳ありません。
guest

0

findContoursで領域を取得する部分までできているなら
各領域の面積を cv.contourArea で求めて,それが所望の値か否かをチェックすればよいのではないでしょうか.

投稿2019/11/27 04:36

fana

総合スコア11990

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問