このページではpython-openCVを使って、以下の画像のように画像内にある物体の重心位置を計算しプロットする方法をご紹介していこうと思います。
(白いプロットが重心位置です。)
基本的な処理の流れとしては、
①ベース画像を読み込み
②①の画像を二値化処理
③②の画像から輪郭を抽出する
④輪郭の内部を塗りつぶす
⑤塗りつぶした画像から白い部分の重心を計算
という流れで実施していきます。
それではさっそくやっていきましょう!
※Python(無料のプログラミング言語)のインストール~実行方法までは以下で解説しています。
追加で必要なopenCVについても以下に参考記事を載せておきます。
ライブラリのインポート
今回はopenCVとnumpyをつかいます。
まずはインポートから。
import cv2
import numpy as np
ベース画像の読み込み
次はベースとなる画像を読み込みます。
今回は以下の画像を使用します。
※プログラム実行フォルダにmouse.jpgとして保存してあります。
読み込む方法は以下の通り。
※カラー画像であっても白黒画像として読み込みます。
file_name='mouse.jpg'
img=cv2.imread(file_name,cv2.IMREAD_GRAYSCALE)
h,w=img.shape[:2]
これで変数imgに画像の輝度値データが格納されています。
h,wは重心算出する際に用いる画像を作成するために使います。
読み込んだ画像を二値化する
次は先ほど読み込んだ画像を二値化します。
thresh, img_thresh = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY)
cv2.imwrite('img_thresh.jpg',img_thresh)
(今回は閾値60で二値化しています。)
これでimg_threshという変数の中に二値化された画像の輝度値データが格納されています。
ここまでを順番に実行すると実行フォルダに以下の画像が出力されています。
二値化画像から輪郭を抽出する
次は先ほど出力した画像から輪郭を抽出していきます。
輪郭抽出方法は以下の通りです。
contours, hierarchy = cv2.findContours(img_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
これで物体の最も外側の輪郭だけを抽出してくれます。
こいつを実行するとcountoursという変数の中に、輪郭の座標情報が格納されています。
今回は物体が一つしかないので輪郭も一つしか抽出していませんが、もし複数物体がある画像を使用している場合は、以下のコードを実行して何個の物体を認識しているかを確認しておきましょう。
print(len(contours))
取り出した輪郭の内部を塗りつぶした画像を作成する
重心を算出するためには、輪郭内部を塗りつぶしただけの画像が必要になります。
ここではその画像を作成していきます。
以下のコードで作成します。
black_img=np.zeros((h,w),np.uint8)
cv2.drawContours(black_img, contours, 0, 255, -1)
cv2.imwrite('img_thresh2.jpg',black_img)
※cv2.drawContoursの中の3つめの引数である0は、先ほど抽出した輪郭変数であるcountoursの中の何番目の輪郭を使用するかを指定しています。
今回は輪郭が一つしかないので、0番目を指定していますが、もし複数の輪郭を検出している際は、あなたが重心算出したい物体は何番目の輪郭として認識されているのかを確認する必要があります。
ですので、この引数を0,1,2・・・と変えながら実行していき、できあがった画像をみながらあなたが重心計算をしたい物体を探しましょう。
とりあえず私の場合はここまで実行すると以下の画像が出力されていました。
これで重心を算出する準備は完了です。
物体の重心を算出する
最後は物体の重心算出です。
M = cv2.moments(black_img, False)
x,y= int(M["m10"]/M["m00"]) , int(M["m01"]/M["m00"])
print('mom=('+str(x)+','+str(y)+')')
cv2.circle(img, (x,y), 10, 255, -1)
cv2.imwrite('img_mom.jpg',img)
こいつを実行すると以下が出力されていました。
mom=(402,328)
これが今回使用した画像内にある物体のx,y座標です。
また、以下の重心にプロット点をうった画像も出力されています。
重心算出のコード全体紹介
ここまでに使用したコードを一連でご紹介しておきます。
#ライブラリインポート
import cv2
import numpy as np
#ベース画像の読み込み
file_name='mouse.jpg'
img=cv2.imread(file_name,cv2.IMREAD_GRAYSCALE)
h,w=img.shape[:2]
#二値化処理
thresh, img_thresh = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY)
cv2.imwrite('img_thresh.jpg',img_thresh)
#輪郭抽出
contours, hierarchy = cv2.findContours(img_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))
#塗りつぶし画像の作成
black_img=np.zeros((h,w),np.uint8)
cv2.drawContours(black_img, contours, 0, 255, -1)
cv2.imwrite('img_thresh2.jpg',black_img)
#重心計算
M = cv2.moments(black_img, False)
x,y= int(M["m10"]/M["m00"]) , int(M["m01"]/M["m00"])
print('mom=('+str(x)+','+str(y)+')')
cv2.circle(img, (x,y), 10, 255, -1)
cv2.imwrite('img_mom.jpg',img)
おわりに
というわけで今回はpython-openCVを使って、画像内にある物体の重心を算出する方法を順をおって解説しました。
画像処理の際にぜひご活用ください。
このように、私のブログでは様々なスキルを紹介しています。
今は仕事中で時間がないかもしれませんが、ぜひ通勤時間中などに他の記事も読んでいただけると嬉しいです。
⇒興味をもった方は【ヒガサラ】で検索してみてください。
確実にスキルアップできるはずです。
最後に、この記事が役に立ったという方は、ぜひ応援よろしくお願いします。
↓ 応援ボタン
にほんブログ村
それではまた!
Follow @HigashiSalary
コメント
hとwの変数にはどの値を入れればいいのでしょうか?
ご質問ありがとうございます(^^)
すみません、コードが抜けてましたね。
hとwはベース画像の縦、横のピクセルサイズになります。
最終項目のコード全文にはh,wの指定を載せておりますのでそちらをご確認ください。
大変参考になりました。質問ですが、こちらのプログラムに塗りつぶした形の輪郭を元画像に描画するようなコードを導入したい場合、どのようなコードを導入するのがよいでしょうか。
また、重心位置をプロットする点はほかの色に変更することは可能でしょうか。
素人質問で恐縮ですが、よろしくお願いいたします。
ご質問ありがとうございます(^^)
輪郭の描写については全く同じことを以下で実施していますので参考にしてみてください。
重心プロットの色は以下のようにBGR指定すると可能です。
cv2.circle(img,(x,y),radius=5,color=(0,255,0),thickness=2)
こちらも詳細は以下をご欄ください。
ご回答ありがとうございました。重心点の色について変更を加えたところ、白黒でしか表示できませんでした。これは画像を白黒で読み込んだために起こるのでしょうか。またほかに挙げられる要因にどのようなものがありますか。
はい、そうだと思います。
対策としては、
①一度モノクロ画像を保存する
②モノクロ画像をカラー画像として読み込む
③カラーで円を描く
です。
cv2.imwrite(‘test.jpg’,img)
img=cv2.imread(‘test.jpg’)
cv2.circle(img,(x,y),radius=5,color=(0,255,0),thickness=2)
cv2.imwrite(‘test2.jpg’,img)
こんな流れで実施してみてください。
test2.jpgに緑の円が描かれるのではないかと思います。
色をつけられました!沢山のアドバイスありがとうございました。