はじめに
画像から物体を検出したり、認識したりする際に、画像の特徴量を数値化することが重要になります。
OpenCVのcv2.moments関数はそのためのツールで、画像のモーメント(重心、面積など)を計算することができます。
本投稿ではcv2.moments関数の使い方と応用例を紹介します。
モーメントとは
モーメントとは、画像の統計的な特徴量のことです。
例えば、物体の重心座標(x, y)、面積、回転角度などがモーメントから求められます。
モーメントは物体の不変特徴量なので、同じモーメントを持つ場合は同一の物体であると判断できます。
cv2.moments関数
モーメントはcv2.moments関数を使って、簡単に計算することができます。cv2.moments関数の引数と戻り値について説明します。
引数
| 名称 | 説明 |
|---|---|
| 入力画像(必須) | モーメントを計算する入力画像。データ型はnp.int32 または np.float32です。 |
| binaryImage(オプション) | True の場合、ゼロ以外のすべての画素値は 1 として扱われます。 このパラメータは画像のみに使用されます。 (デフォルト:False) |
戻り値
戻り値はディクショナリー型で、以下のようなモーメントの値となります。
{
'm00': 0次の積率モーメント(輪郭などが囲む領域の面積),
'm10': x方向の1次の積率モーメント(輪郭の重心におけるx座標の期待値),
'm01': y方向の1次の積率モーメント(輪郭の重心におけるy座標の期待値),
'm20': x方向の2次の中心モーメント(輪郭の重心からのx軸方向の分散),
'm11': xy方向共分散(輪郭の重心からのx方向とy方向の分散の共分散),
'm02': y方向2次モーメント(輪郭の重心からのy方向の分散),
'm30': x方向3次モーメント,
'm21': xy方向2次共分散:輪郭の重心からのx軸方向とy軸方向の2次モーメントの共分散,
'm12': yx方向2次共分散:輪郭の重心からのy軸方向とx軸方向の2次モーメントの共分散,
'm03': y方向3次モーメント
}
各モーメントの値は以下のように解釈されます。
m00: 0次モーメントは、画像の面積に相当します。m10とm01: 1次モーメントは、画像の重心のx座標とy座標を表します。m20、m11、m02: 2次モーメントは、画像の回転角度を求めるのに使用されます。- その他のモーメントは、より高次の特徴量を表します。
これらのモーメントから、以下のような画像の特徴量が計算できます。
- 面積: 0次の積率モーメント
m00が面積となります。
面積 =m00 - 重心座標: 1次の積率モーメント
m10、m01から次の式で重心座標が計算できます
(x, y) = (m10/m00,m01/m00) - 楕円の長半径と短半径: 0次、2次のモーメントから、楕円の長半径と短半径が求められます。
x方向半径 = sqrt(4 *m20/m00)
y方向半径 = sqrt(4 *m02/m00) - 画像の回転角: 2次のモーメントから画像の回転角度を求められます。
回転角 = 0.5 *atan2(2 *m11,m20-m02) - Huモーメント: 2次、3次のモーメントから、物体の形状を表す7つのHuモーメント(不変モーメント)が計算できます。これらは、スケール、平行移動、回転に対して不変な特徴量です。
特に重心座標と面積は、物体検出や認識などの多くのアプリケーションで重要な役割を果たします。
モーメントを利用することで、画像の基本的な統計量を簡単に得ることができます。
オプションのbinaryImage=Trueを指定した場合、入力画像はあらかじめ2値化されている必要があります。2値画像のモーメントを計算することで、より単純化された形状の特徴量が得られます。
使い方
以下にサンプルコードを示します。
import cv2
import math
# 画像を読み込む
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 2値化する
ret, thresh = cv2.threshold(img, 127, 255, 0)
# モーメントを計算する
M = cv2.moments(thresh)
# 面積を出力
area = M['m00']
print('面積: ', area)
# モーメントから重心座標を計算する
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
print('重心座標: ({}, {})'.format(cx, cy))
# モーメントから回転角度を計算する
angle = math.degrees(0.5 * math.atan(2 * M['m11'] / (M['m20'] - M['m02'])))
print('回転角度: {} degrees'.format(angle))
このコードでは、まず画像をグレースケールで読み込んで二値化(閾値127で二値化)しています。
2値画像からcv2.moments関数でモーメントMを計算し、そのモーメントから重心座標(cx, cy)を求めています。
グレースケールで画像を読み込む方法と、二値化について下記を参考にしてください。
【Python・OpenCV】基礎を解説! 画像の読み込み方法(cv2.imread)
【Python・OpenCV】二値化による閾値処理を行うには(cv2.threshold)
おわりに
OpenCVのcv2.moments関数とモーメントの活用方法についてご紹介しました。モーメントからは、画像の面積、重心座標、楕円の長短半径など、様々な特徴量が計算できることが分かりました。
これらのモーメント特徴量は、物体検出・認識、トラッキングなどのさまざまなプロセスで利用できます。
モーメントは画像の基礎的な統計量を表すシンプルな指標ですが、上手く活用すれば強力な性能を発揮してくれます。
単体で使うよりも、他の手法と組み合わせて使うことで、より高度な画像解析が可能になります。例えばモーメントから得られる重心座標を特徴点として使い、他の局所特徴量と組み合わせれば物体認識の精度が向上します。
画像処理の能力を高めるためには、色々な手法を組み合わせて使うことが重要です。モーメントもその有力な選択肢の1つとなるでしょう。
ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。
参考リンク
■(広告)OpenCVの参考書としてどうぞ!■