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

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

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

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

Python

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

Q&A

解決済

1回答

7156閲覧

輪郭抽出 openCV 初心者

退会済みユーザー

退会済みユーザー

総合スコア0

OpenCV

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

Python

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

0グッド

1クリップ

投稿2020/07/26 05:51

編集2020/07/26 08:59

人物の輪郭抽出を行いたいのですが、
画像の周りを抽出されたり、よくわからないところで抽出されたりします。
以下のコードで実行しました。

import cv2 from matplotlib import pyplot as plt # カラー画像の読み込み img = cv2.imread('image5.jpg', 1) # グレースケール化 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 単純二値化 ret, img_binary = cv2.threshold(img_gray, 60, 255, cv2.THRESH_BINARY) # 輪郭抽出 contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 輪郭を元画像に描画 img_contour = cv2.drawContours(img, contours, -1, (0, 255, 0), 5) # ここから画像描画 fig = plt.figure() ax1 = fig.add_subplot(111) ax1.imshow(cv2.cvtColor(img_contour, cv2.COLOR_BGR2RGB)) ax1.axis('off') plt.show() plt.close()

元画像

実行結果の写真

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

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

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

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

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

tiitoi

2020/07/26 08:57 編集

> 元画像を添付してください 輪郭抽出は2値画像の白の領域を囲むものなので、まず人の部分が白、背景部分が黒の2値画像を作成する必要があります。 img_binary を保存してみて確認してみてください。その部分がうまくいってないのではないでしょうか。
退会済みユーザー

退会済みユーザー

2020/07/26 08:59

img_binary を保存するにはどうすればよろしいのでしょうか。
guest

回答1

0

ベストアンサー

img_binary を保存するにはどうすればよろしいのでしょうか。

cv2.imwrite() で保存できます。

python

1cv2.imwrite("img_binary.png", img_binary)

これを見てみると、2値化がうまくいっていませんね。

イメージ説明

↓イメージですが、このような感じで2値化したい対象が白、そうでない背景は黒の画像ができていないと、findContours() はうまくいきません。

イメージ説明

追記

そうなんですね。領域抽出がよいのですが、背景を除去することが可能ならそれでも良いのですができるのでしょうか。質問とずれてしまいすいません。おわかりでしたら教えていただきたいです。

輪郭抽出のアプローチでやる場合、まず人と背景をある程度きれいに2値化することは必要です。2値化は大津の手法など使えば、もう少しきれいになります。

背景除去の場合は以下の手順になります。

  1. グレースケール化
  2. 2値化
  3. 輪郭抽出
  4. 一番大きい輪郭が人と仮定
  5. 輪郭内部を255、それ以外を0としたマスク画像を作成
  6. 元画像にアルファチャンネルを追加
  7. マスク画像の値が0 (背景) の画素の値を255にして透過

python

1import cv2 2import numpy as np 3from IPython import display 4 5 6def imshow(img): 7 """ndarray 配列をインラインで Notebook 上に表示する。 8 """ 9 ret, encoded = cv2.imencode(".jpg", img) 10 display.display(display.Image(encoded)) 11 12 13img = cv2.imread("person.jpg") 14 15# グレースケールに変換する。 16img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 17 18# 大津の手法で2値化する。 19ret, img_binary = cv2.threshold( 20 img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU 21) 22imshow(img_binary) 23 24# 輪郭を抽出する。 25contours, hierarchy = cv2.findContours( 26 img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE 27) 28 29# 最大の輪郭を取り出す。 30max_contour = max(contours, key=lambda x: cv2.contourArea(x)) 31 32# 輪郭を描画する。 33img_contour = cv2.drawContours(img.copy(), [max_contour], -1, (0, 255, 0), 2) 34imshow(img_contour) 35 36mask = cv2.drawContours( 37 np.zeros_like(img_binary), contours, -1, color=255, thickness=-1 38) 39 40# アルファチャンネル追加 41dst = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA) 42 43# mask の値が0のピクセルは透過する。 44dst[mask == 0] = 0 45 46cv2.imwrite("dst.png", dst)

イメージ説明

照明の影響等もあるので、多少背景も残ってしまいます。
きれいに2値化することは1つの課題であり、2値化方法は Adaptive Thresholding など他にも方法はあるので、いろいろな手法を検討してみてください。

画像のしきい値処理 — OpenCV-Python Tutorials 1 documentation

もし撮影環境が変更可能であるならば、グリーンバックの背景を使うなど、2値化しやすいように撮影環境を工夫してください。テレビや映画の背景合成はこの方法でやっています。

Amazon.co.jp : グリーンバック

グラフカットを使ったアプローチ

全自動でなく、対話式でよければ、グラフカットという方法があります。

GrabCutを使った対話的前景領域抽出 — OpenCV-Python Tutorials 1 documentation

↓ PowerPoint についてる背景削除のやつです。

イメージ説明

ディープラーニングを使った別のアプローチ

任意背景できれいに人の部分だけ抽出したいのであれば、自分であれば、ディープラーニングのセマンティックセグメンテーションモデルを使います。
詳細な解説はここでするには余白が足りないので、Pytorch を使ったコードの提示のみにとどめておきます。

python

1import cv2 2import numpy as np 3import torch 4import torchvision 5from PIL import Image 6from torchvision import models as models 7from torchvision import transforms as transforms 8 9device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 10PERSON_ID = 1 11 12# モデルを作成する。 13model = models.detection.maskrcnn_resnet50_fpn(pretrained=True).to(device) 14model.eval() 15 16# 画像を読み込む。 17img = Image.open("person2.jpg") 18img_tensor = transforms.functional.to_tensor(img).to(device) 19 20# 推論する。 21output = model([img_tensor])[0] 22 23# ラベル 人 のマスクを取り出す。 24person_scores = output["masks"][output["labels"] == PERSON_ID, 0] 25person_score = person_scores.max(dim=0)[0] 26 27mask = person_score >= 0.7 28mask = mask.cpu().detach().numpy() 29 30# アルファチャンネル追加 31dst = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGRA) 32imshow(dst) 33 34# mask の値が0のピクセルは透過する。 35dst[mask == 0] = 0 36imshow(dst)

イメージ説明

イメージ説明

イメージ説明

投稿2020/07/26 09:03

編集2020/07/26 10:15
tiitoi

総合スコア21956

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

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

退会済みユーザー

退会済みユーザー

2020/07/26 09:10

そうなんですね。領域抽出がよいのですが、背景を除去することが可能ならそれでも良いのですができるのでしょうか。質問とずれてしまいすいません。おわかりでしたら教えていただきたいです。
tiitoi

2020/07/26 10:20

> 背景を除去することが可能ならそれでも良いのですができるのでしょうか。 どのくらいきれいに背景除去するかによりますが、輪郭抽出でもある程度は可能です。輪郭抽出でキレイに除去したいのであれば、グリーンバック背景を使うなど、撮影環境の工夫も必要です。
退会済みユーザー

退会済みユーザー

2020/07/27 04:46

いろいろな方法の提案ありがとうございます。Pytorch を使った方法を実際にやってみたのですが、 No module named 'torch  このようにエラーがでてしまいインポートできないのですがどうすればよいのでしょうか。 基本的なことを聞いてしまいすいません。
tiitoi

2020/07/27 04:58

モジュールのインストールが必要です。 pip install torch torchvision
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問