【Python・OpenCV】画像の特徴量:モーメント(cv2.moment)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】画像の特徴量:モーメント(cv2.moment)

2024-04-25

はじめに

画像から物体を検出したり、認識したりする際に、画像の特徴量を数値化することが重要になります。
OpenCVのcv2.moments関数はそのためのツールで、画像のモーメント(重心、面積など)を計算することができます。
本投稿ではcv2.moments関数の使い方と応用例を紹介します。

(広告) OpenCV関連書籍をAmazonで探す

モーメントとは

モーメントとは、画像の統計的な特徴量のことです。
例えば、物体の重心座標(x, y)、面積、回転角度などがモーメントから求められます。
モーメントは物体の不変特徴量なので、同じモーメントを持つ場合は同一の物体であると判断できます。

cv2.moments関数

モーメントはcv2.moments関数を使って、簡単に計算することができます。
cv2.moments関数の引数と戻り値について説明します。

cv2.moments(入力画像[, binaryImage])

引数

名称説明
入力画像(必須)モーメントを計算する入力画像。データ型は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次モーメントは、画像の面積に相当します。
  • m10m01: 1次モーメントは、画像の重心のx座標とy座標を表します。
  • m20m11m02: 2次モーメントは、画像の回転角度を求めるのに使用されます。
  • その他のモーメントは、より高次の特徴量を表します。

これらのモーメントから、以下のような画像の特徴量が計算できます。

  1. 面積: 0次の積率モーメント m00 が面積となります。
    面積 = m00
  2. 重心座標: 1次の積率モーメント m10m01 から次の式で重心座標が計算できます
    (x, y) = (m10/m00m01/m00)
  3. 楕円の長半径と短半径: 0次、2次のモーメントから、楕円の長半径と短半径が求められます。
    x方向半径 = sqrt(4 * m20 / m00 )
    y方向半径 = sqrt(4 * m02 / m00 )
  4. 画像の回転角: 2次のモーメントから画像の回転角度を求められます。
    回転角 = 0.5 *atan2(2 * m11, m20 - m02)
  5. 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)

メモ

重心座標は以下のような意味を持つため、特徴点としてよく利用されます。

  1. 物体の位置特定 重心座標は、画像内の物体がどこに位置しているかを示します。物体検出やトラッキングの際に、物体の位置を特定するのに役立ちます。
  2. 物体の向き推定 複数の物体が存在する場合、それぞれの重心座標から物体の向きを大まかに推定することができます。
  3. 特徴点としての利用 重心座標自体を物体の特徴点として利用できます。特徴点マッチングなどの手法と組み合わせることで、物体認識に役立ちます。
  4. 領域分割の基準 画像を領域に分割する際の基準点として、重心座標を用いることができます。

おわりに

OpenCVのcv2.moments関数とモーメントの活用方法についてご紹介しました。モーメントからは、画像の面積、重心座標、楕円の長短半径など、様々な特徴量が計算できることが分かりました。

これらのモーメント特徴量は、物体検出・認識、トラッキングなどのさまざまなプロセスで利用できます。
モーメントは画像の基礎的な統計量を表すシンプルな指標ですが、上手く活用すれば強力な性能を発揮してくれます。
単体で使うよりも、他の手法と組み合わせて使うことで、より高度な画像解析が可能になります。例えばモーメントから得られる重心座標を特徴点として使い、他の局所特徴量と組み合わせれば物体認識の精度が向上します。

画像処理の能力を高めるためには、色々な手法を組み合わせて使うことが重要です。モーメントもその有力な選択肢の1つとなるでしょう。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

参考リンク

■(広告)OpenCVの参考書としてどうぞ!■

-Python, プログラミング, 画像処理
-