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

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

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

Anacondaは、Python本体とPythonで利用されるライブラリを一括でインストールできるパッケージです。環境構築が容易になるため、Python開発者間ではよく利用されており、商用目的としても利用できます。

OpenCV

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

Python

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

Q&A

解決済

1回答

2906閲覧

Python OpenCV

miurakana

総合スコア5

Anaconda

Anacondaは、Python本体とPythonで利用されるライブラリを一括でインストールできるパッケージです。環境構築が容易になるため、Python開発者間ではよく利用されており、商用目的としても利用できます。

OpenCV

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

Python

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

0グッド

0クリップ

投稿2020/02/18 11:57

編集2020/02/19 11:38

実現したいこと
困っています
一番中心に近い輪郭を切り取るプログラムを作りたいと思っています。

発生している問題・エラーメッセージ

特になし

該当のソースコード

Python

1import cv2 2import os 3import matplotlib.pyplot as plt 4from matplotlib.patches import Polygon 5from matplotlib.patches import Rectangle 6import numpy as np 7from PIL import Image 8from PIL import ImageDraw 9from PIL import ImageFont 10 11 12 13img = cv2.imread("img234.jpg")#images12.jpg") 14 15# グレースケールに変換する。 16grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 17 18""" 19ret, binaryImg = cv2.threshold(grayImg,90,255,cv2.THRESH_BINARY) 20 #print(binaryImg) 21 22neiborhood8 = np.array([[1, 1, 1],[1, 1, 1],[1, 1, 1]],np.uint8) 23erodeImg = cv2.erode(binaryImg,neiborhood8,iterations =10) 24dilateImg = cv2.dilate(erodeImg,neiborhood8,iterations = 10) 25""" 26#2値化 27#ret,thresh = cv2.threshold(grayImg,60,255,0) 28 29#反転 30#thresh= cv2.bitwise_not(thresh) 31 32retval, im_bw = cv2.threshold(grayImg, 120, 255, cv2.THRESH_BINARY_INV )#+ cv2.THRESH_OTSU) 33 34# 輪郭の検出 35contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 36 37 38def draw_contours(ax, img, contours): 39# 抽出した輪郭を描画する。 40 ax.imshow(img, cmap='gray') 41 ax.axis('off') 42 for i, cnt in enumerate(contours): 43 cnt = np.squeeze(cnt, axis=1) # (NumPoints, 1, 2) -> (NumPoints, 2) 44 # 輪郭の点同士を結ぶ線を描画する。 45 ax.add_patch(Polygon(cnt, color='b', fill=None, lw=2)) 46 # 輪郭の点を描画する。 47 #ax.plot(cnt[:, 0], cnt[:, 1], 'ro', mew=0, ms=3) 48 # 輪郭の番号を描画する。 49 #ax.text(cnt[0][0], cnt[0][1], i, color='orange', size='20') 50fig,ax = plt.subplots(figsize=(6, 6)) 51ax.set_title("cv2.RETR_CCOMP") 52 #print(con,tours) 53 #print(cnt) 54draw_contours(ax, img, contours) 55 56# 元画像の幅と高さを取得 57srcHeight, srcWidth, srcColor = img.shape 58print("width: %d, height: %d" % (srcHeight, srcWidth)) 59# 面積を取得 60srcArea = srcWidth * srcHeight 61print("area: %d" % srcArea) 62# 全面積の0.1倍(10%)以上の輪郭だけ保存する 63thresholdArea = srcArea * 0.05 64 65number = 1 66maxArea = 0 67maxImage = None 68for cnt in contours: 69 # 輪郭に外接する長方形を取得する。 70 x, y, width, height = cv2.boundingRect(cnt) 71 # 長方形を描画する。 72 ax.add_patch( 73 Rectangle(xy=(x, y), width=width, height=height, color="g", fill=None, lw=2) 74 ) 75 print(" sub-area: %d" % (width * height)) 76 if (width * height >= thresholdArea): 77 img0 = img[y:(y+height), x:(x+width)] 78 cv2.imwrite("output_%d.jpg" % number, img0) 79 number += 1 80 if (width * height > maxArea): 81 maxArea = width * height 82 maxImage = img0 83 84cv2.imwrite("max.jpg", maxImage) 85 86#size = tuple([img.shape[0], img.shape[1]]) 87#print(size) 88#img1 = img[y:(y+height), x:(x+width)] 89 90#cv2.imwrite("out_sample126.jpg", img) 91#cv2.imshow('thresh', im_bw) 92#cv2.waitKey(0) 93#cv2.destroyAllWindows() 94plt.imshow(img) 95plt.show()

試したこと

一番面積が大きい輪郭を保存した

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

OpenCV 3.4.0

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

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

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

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

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

pepperleaf

2020/02/18 12:04

質問の主旨は何でしょうか? > 一番中心に近い輪郭を切り取る 中心に近いをどのように定義するかだと思います。 また、 > 一番面積が大きい輪郭 多分、面積の閾値も必要でしょうか。
miurakana

2020/02/18 12:08

回答ありがとうございます ペットボトルが5本並んでいる場合は、3本目が中心となります 6本だと3本目か4本目となります
pepperleaf

2020/02/18 12:13

具体的な方法はすぐに提示できないですが、、、、 輪郭が抽出できたのですから、それぞれの重心なりの座標を求めれば、良いのては? 荒っぽくは、輪郭のX, Y の最大/最小から、その中点を真ん中とするとか。ノイズの少ない図形ではそれでもできそうです。
miurakana

2020/02/18 12:18

回答ありがとうございます 入力画像がラズパイのパイカメラで撮影するためノイズの多い場合も考慮したいと思います 具体的な方法が来るまで待ちたいと思います。
tiitoi

2020/02/18 14:26

中心というのは画像の中心の座標ということでよろしいでしょうか?
miurakana

2020/02/19 01:08

tiitoiさん 回答ありがとうございます。 座標というより画像内の中心のペットボトルの全体を切り抜きたいと思っています
tiitoi

2020/02/19 02:59

画像を質問に添付できますか?
miurakana

2020/02/19 04:09

回答ありがとうございます 上記が5つの画像になります
tiitoi

2020/02/19 04:15

画像が添付できていません。 質問欄編集の「画像の挿入ボタン」から添付できます。
guest

回答1

0

ベストアンサー

cv2.moments() で輪郭の重心を求めて、その重心と画像の中心との距離が最も近い輪郭を選べばいいと思います。

Python+OpenCVで重心を求める | OpenCV画像解析入門

python

1import cv2 2import numpy as np 3 4# 画像を読み込む。 5img = cv2.imread("sample.jpg") 6 7# グレースケールに変換する。 8gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 9 10retval, binary = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV) 11 12# 輪郭の検出 13contours, hierarchy = cv2.findContours( 14 binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE 15) 16 17# 各輪郭の重心と画像の中心との距離を計算する。 18h, w = img.shape[:2] 19img_center = np.array([w / 2, h / 2]) 20 21contours = list(filter(lambda x: cv2.contourArea(x) > 100, contours)) 22 23dists = [] 24for cnt in contours: 25 # 輪郭の重心を計算する。 26 M = cv2.moments(cnt) 27 cx = int(M["m10"] / M["m00"]) 28 cy = int(M["m01"] / M["m00"]) 29 cnt_center = np.array([cx, cy]) 30 # 画像の中心との距離を計算する。 31 dist = np.linalg.norm(img_center - cnt_center) 32 33 dists.append(dist) 34 35# 中心から最も距離が近い輪郭を取得する。 36idx = np.argmin(dists) 37target_cnt = contours[idx] 38 39# その輪郭の外接矩形を取得する。 40x, y, w, h = cv2.boundingRect(target_cnt) 41cropped = img[y : y + h, x : x + w] 42 43cv2.imwrite("result.png", cropped)

イメージ説明

入力画像

イメージ説明

切り抜き画像

投稿2020/02/19 10:14

編集2020/02/20 02:46
tiitoi

総合スコア21956

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

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

miurakana

2020/02/19 10:41

赤い輪郭部分を切り取るにはどうしたらいいのでしょうか?
tiitoi

2020/02/19 18:12

cv2.boundingRect() でその赤の輪郭の外接矩形を取得して、 x, y, w, h = cv2.boundingRect(target_cnt) その矩形の部分を切り抜いて保存すればよいです。 cropped = img[y:y + h, x:x + w]
miurakana

2020/02/19 23:50

cx = int(M["m10"] / M["m00"])ここでエラーが発生します float division by zero
miurakana

2020/02/20 01:05

エラー→float division by zero
tiitoi

2020/02/20 02:25 編集

貼っていただいた5本の缶の画像で回答のコードをそのままコピペして実行してそのエラーが発生しましたか? こちらでは今一度試しましたが、記載いただいたエラーが再現しません。
miurakana

2020/02/20 02:38

回答ありがとうございます そのままの画像はできました 撮影した場合だと取れないのですが、どうしたらよろしいでしょうか? 上記の画像をA4でカラープリントしたものを撮影します
tiitoi

2020/02/20 02:48

カメラで撮影した映像は、照明の影響等により、貼っていただいた5本の缶の画像のように綺麗に2値化できないかもしれないです。その場合に、極小の輪郭が抽出されてしまい、それの中心を求めようとした際にゼロ除算が発生していると思われます。 一応、回答のコードに小さい輪郭は排除するコードを追加しましたが、どうでしょうか。
tiitoi

2020/02/20 02:56 編集

これでもうまくいかない場合は、 ・ 2値化の方法を調整する。 → 2値化がうまくいっていないと、その後の輪郭抽出もうまくいきません → 2値化後の画像を cv2.imwrite() を出力して、結果を確認してください → 線が切れていたりして物体が1つの輪郭として認識されない場合は必要に応じてモルフォロジー変換 ・誤検出された輪郭を除外する処理を追加する → 輪郭の面積などで判断、誤検出を除ける条件を考える 画像処理は、このように実際の画像を元に調整していく作業が必須ですし、その撮影環境というのは質問者さんしか試せないわけですから、その環境で問題なく動作するコードというのはここでは示せません。 少なくとも回答は、コード付きで「輪郭の中心を求めて、画像の中心に最も近い輪郭を選択し、切り取る具体的な方法」の提示にはなっていると思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問