3Dモデルの断面といった2次元画像の重心を求めることで、類似断面の形状ずれ等を求めることが可能です。重心は自分で計算することも可能ですが画像処理ライブラリであるOpenCVを使うと簡単に算出できます。ここではPython/OpenCVにて重心を計算する方法を紹介します。
こんにちは。wat(@watlablog)です。ここではOpenCVによる重心計算を学びます!
画像の重心を求めるメリット
重心(Center of gravity)とは、物体の各部に働いている重力の作用と等価な合力が作用する点を意味します。
密度一定の物体の場合、重心は図形そのものから求められる図心と一致します。学校の授業では三角形や平行四辺形の重心を作図で求めた記憶がある人も多いと思います。
ただし学校では重心を求めることのメリットまでは詳しく教えてくれないと思いますので、Pythonでコーディングする前に少し触れておきましょう。
複数画像間の位置合わせや計測
今回は画像内のオブジェクトを対象にしますが、画像処理を使って重心を求めることで複数画像間の位置出しに使えるというメリットがあります。
例えば下図のように同一オブジェクトがずれた状態で描かれた複数枚の画像に対して重心を求めれば、重心座標を基準に画像切り出しを行いオブジェクトを整列できます。さらに重心座標を基準に移動量の測定も可能となります。
重心ずれを測定できる
例えば機械設計者の場合ですが、回転体の回転中心と重心を同時に計測できれば重心ずれを把握することができ、静アンバランスの検討ができます。
回転中心の計算は「最小二乗法で円をカーブフィットするPythonプログラム」の記事を参考にすればできそうです。
他にも回転体に関してはアンバランス関係の検討で何か重心測定が使えるかも知れません。
一般に機械設計者が使うCADソフトは重心を計算する機能をデフォルトで持っています。そのためCADモデルに対して画像から重心を計算する方法を使う場面はあまりないと思います。
ここではプログラミング的に計算できるようにしておくことでDIYエンジニアリングが捗るという程度を目指しましょう。
正直やってみたかっただけ。
OpenCVで重心を計算するPythonコード
動作環境
この記事のコードは以下の環境で動作確認しました。
Python | Python 3.9.6 |
---|---|
PyCharm (IDE) | PyCharm CE 2020.1 |
opencv-python | 4.2.0.34 |
サンプル画像
本コードでは以下の画像内オブジェクトの重心を求めてみました。ダウンロード可能にしておきますので、ちょっと動かして動作を確認したい時にお使いください。
全コード(コピペ用)
詳細を説明する前に、コピペで動作する全コードを以下に示します。
OpenCVの画像表示をdisplay()関数で行っていますが、何かキーを押すと消えて次に進みますので注意してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import cv2 import os def surface_cog(filename): ''' 画像を二値化して重心を求める関数 ''' # 画像をグレースケールで読み込み img = cv2.imread(filename, 0) display(img) # 二値化(図形部を白にする) ret, img_binary = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY_INV) display(img_binary) # 重心を計算 m = cv2.moments(img_binary) cog_x = int(m["m10"] / m["m00"]) cog_y = int(m["m01"] / m["m00"]) print(cog_x, cog_y) # 重心点を画像に描画 center = (cog_x, cog_y) radius = 5 color = (100, 0, 0) cv2.circle(img_binary, center, radius, color, thickness=5) display(img_binary) # 保存フォルダが無い時に新規作成 name, ext = os.path.splitext(filename) save_dir = name if os.path.exists(save_dir): pass else: os.mkdir(save_dir) # 重心を描画した画像を保存 save_path = os.path.join(*[save_dir, 'COG_' + name + ext]) cv2.imwrite(save_path, img_binary) return def display(img): ''' 画像を表示(ウィンドウサイズを指定してOpenCVで確認)''' h, w = img.shape w_name = 'img' cv2.imshow(w_name, img) cv2.namedWindow(w_name, cv2.WINDOW_NORMAL) window_size = 600 cv2.resizeWindow(w_name, window_size, int(window_size * h / w)) cv2.waitKey() return if __name__ == '__main__': filename = 'triangle.png' #filename = 'triangle2.png' #filename = 'parallelogram.png' #filename = 'circle.png' #filename = 'circle-with-hole.png' #filename = 'hiyoko.png' surface_cog(filename) |
グレースケール化→二値化
cv2.imreadの第二引数に0を指定することで、カラー画像をグレースケール化して読み込みます。
そして二値化処理を行いますが、この時cv2.THRESH_BINARY_INVを使って色が付いている部分を白にします。これが下準備です。
重心計算
重心はcv2.momentsで行います。詳しい説明は公式ページにありますが、このメソッドでMoment行列を全て計算し、必要な要素を抽出して重心(公式ページでmass centerと記載)を式(1)により求めます。
コードを実行すると下図のように重心点が画像上に描画されます。
重心が正しく計算されているかは作図することで確認可能です。下図はtriangle2.pngに補助線を描いてみた結果ですが、正しく重心点が描画されているとわかります。
全図の結果を以下に示します。穴が空いていても、複雑な形状でも問題なく重心が得られました。
まとめ
このページではOpenCVのmomentsを使って図形の重心を求める方法を紹介しました。
とは言え、本当に短いコードで2次元図形から重心を計算することができたためシンプルな記事となりました。
この後は3Dモデルの重心を求められないか…等のネタを考えてみる予定です。