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

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

ただいまの
回答率

90.03%

初めてのOpenCV

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 56

Kanikama_h

score 7

やりたいこと

以下の輪郭抽出して背景を透過した画像の
①RGB値(比率)?の取得
②画像の大きさpx値として表示
③輪郭抽出した中身の画像の重心を取得したい
イメージ説明

書いたコード

import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread(r"C:\Users\hirayama\Desktop\python_test\Python.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 2値化する。
thresh, binary = cv2.threshold(gray, 230, 255, cv2.THRESH_BINARY_INV)

# 輪郭を抽出する。
contours, hierarchy = cv2.findContours(
    binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)

# マスクを作成する
mask = np.zeros_like(binary)

# 輪郭内部(透明化しない画素)を255で塗りつぶす。
for cnt in contours:
    cv2.drawContours(mask, contours, -1, color=255, thickness=-1)

# RGBAに変換する
rgba = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)

# マスクをアルファチャンネルに設定する。
rgba[..., 3] = mask


# すべての輪郭を描画
#cv2.drawContours(img, contours, -1, color=(0, 255, 0), thickness=2)

# 保存する。
cv2.imwrite(r"C:\Users\hirayama\Desktop\python_test\rgba_Python.png", rgba)

# BGR値の取得
BGR = rgba[100, 100]
print(BGR)

# 画像の大きさ(px)
print(rgba.size)

# 表示
cv2.imshow("img", rgba)
cv2.waitKey(0)

実行結果

BGR値の取得
BGR = rgba[100, 100]
print(BGR)

画像の大きさ(px)
print(rgba.size)

[255 255 255   0]
7060548

のような値で全部255っておかしいかですよね。たぶん。重心の求め方に関してはmoments関数を使うことしかわからなくて実装できません。

解決してほしいこと

コードをレビューしていただきたいです。また、実行結果からRGB値の値が正しいのか、他のやりかたがあるのか教えてほしいです。
最後に、全然分からないので、contoursした画像の重心を求めるコードを教えていただけるとうれしいです。
超初心者ですので、失礼がありましたら、申し訳ありません。

追記

重心のコードを書いてみましたが、はたして、輪郭内部の重心であっているのでしょうか?

# 重心を求める
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print((cx, cy))


実行結果
{'m00': 3630.0, 'm10': 2613283.6666666665, 'm01': 3195271.1666666665, 'm20': 1883025588.0, 'm11': 2299330490.583333, 'm02': 2818133066.5, 'm30': 1358054429772.7002, 'm21': 1656066366000.6167, 'm12': 2027034542877.3167, 'm03': 2490427057352.85, 'mu20': 1689080.433394909, 'mu11': -986300.1662378311, 'mu02': 5527604.094666958, 'mu30': 8119675.634277344, 'mu21': -27959950.409425497, 'mu12': -39319440.03291702, 'mu03': 62396420.001464844, 'nu20': 0.1281849625780653, 'nu11': -0.07485069828547161, 'nu02': 0.41949199695428796, 'nu30': 0.01022756192602152, 'nu21': -0.03521841723007951, 'nu12': -0.049526856237396895, 'nu03': 0.07859467277646077}

(719, 880)

輪郭内部の画像の重心を取得できたのかわかりません
別の書き方を教えてほしいです。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

全部255っておかしいかですよね。

おかしくないです。RGBA=(255, 255, 255, 0) ということは、白でアルファチャンネルが0の画素を意味します。
実際、画像の (100, 100) の位置の画素は白になってます。(アルファチャンネル0なので透過されてますが)

画像の重心を求めるコードを教えていただけるとうれしいです。

cv2.moments() に輪郭を渡して帰ってくるモーメントを使って、重心は以下のように計算できます。 

# 各輪郭の重心を計算する。
for i, cnt in enumerate(contours):
    M = cv2.moments(cnt)
    cx = int(M["m10"] / M["m00"])
    cy = int(M["m01"] / M["m00"])
    print(f"contour {i}: ({cx}, {cy})")

領域(輪郭)の特徴 — OpenCV-Python Tutorials 1 documentation

輪郭抽出した画像の中身だけの画像の指定方法を教えていただけませんか?

import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread(r"sample.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 2値化する。
thresh, binary = cv2.threshold(gray, 230, 255, cv2.THRESH_BINARY_INV)

# 輪郭を抽出する。
contours, hierarchy = cv2.findContours(
    binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)

# マスクを作成する
mask = np.zeros_like(binary)

# 輪郭内部(透明化しない画素)を255で塗りつぶす。
cv2.drawContours(mask, contours, -1, color=255, thickness=-1)

# RGBAに変換する
rgba = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)

# マスクをアルファチャンネルに設定する。
rgba[..., 3] = mask

### 追加したコード

# すべての輪郭を構成する点
all_points = np.concatenate(contours).reshape(-1, 2)

# x, y の最小値、最大値を探す。
xmin, ymin, xmax, ymax = (
    all_points[:, 0].min(),
    all_points[:, 1].min(),
    all_points[:, 0].max(),
    all_points[:, 1].max(),
)
# その範囲でクロップする。
cropped = rgba[ymin:ymax + 1, xmin:xmax + 1]
print(cropped.shape)  # (419, 852, 4)

# 保存する。
cv2.imwrite(r"cropped.png", cropped)

# BGR値の取得
print(cropped[200, 200])  # [167 116  55 255]

# 画像の大きさ(px)
h, w = cropped.shape[:2]
print(w, h)  # 624 852

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/08 16:41

    重心の回答ありがとうございます!!
    本当に助かります!!!
    私のミスというか知識不足で、画像の中身のRGB値を取得するつもりだったのですがすべて(100,100)で指定していました。
    輪郭抽出した画像の中身だけの画像の指定方法を教えていただけませんか?

    キャンセル

  • 2019/11/08 16:54

    contours の点の中で一番左上の点と一番右下の点を探して、その範囲で画像をクロップすればよいです。
    コードを追記しました。

    キャンセル

  • 2019/11/09 00:14

    ありがとうございます!!

    キャンセル

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

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る