search
LoginSignup
4

posted at

updated at

Organization

【OpenCV+matplotlib】画像の重心と慣性主軸を表示する方法

概要

OpenCVとmatplotlibで画像の重心と慣性主軸をプロットする方法を紹介します。
画像の重心と慣性主軸の表示するのに関連する記事が少なかったため、備忘録も兼ねて記事にしました。
Google Colabで作成したコードは、こちらにあります。

重心と慣性主軸

画像の重心と慣性主軸は、画像のモーメントを使って表すことができます。画像のモーメントの定義については、cv::Moments Class Referenceを参照されたい。
画像の重心は、

x = \frac{m_{10}}{m_{00}},\ y = \frac{m_{01}}{m_{00}}

と表される。
画像の慣性主軸は、重心を通りその傾きを$\tan \theta$とすると

\begin{align}
\tan 2\theta &=2\frac{mu_{11}}{mu_{20}-mu_{02}}\\
\theta &= \frac{1}{2} \arctan \left(2\frac{mu_{11}}{mu_{20}-mu_{02}}\right)\\
\tan \theta &= \tan \left\{\frac{1}{2} \arctan \left(2\frac{mu_{11}}{mu_{20}-mu_{02}}\right) \right\} 
\end{align}

と表される。ただし、$m_{**}$は空間モーメント、$mu_{**}$は中心モーメントを意味する。

手順

  1. 各種インポート
  2. サンプル画像、重心、慣性主軸の関数を用意
  3. グラフに表示

1. 各種インポート

各種インポート
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy.stats import multivariate_normal
import cv2

実行時の各種バージョン

Name Version
numpy 1.21.6
matplotlib 3.2.2
scipy 1.7.3
opencv-python 4.6.0.66

2. サンプル画像、重心、慣性主軸の関数を用意

サンプル画像
def get_sample_image():
    x, y = np.mgrid[-25:26:, -50:51:]
    pos = np.dstack((x, y))
    mean = np.array([0, 0])  # 分布の平均
    cov = np.array([[40, 40], [40, 180]])  # 分布の分散共分散行列
    rv = multivariate_normal(mean, cov)
    image = rv.pdf(pos)
    return image
重心
def get_gravity_coord(image):
    M = cv2.moments(image)
    gravity_x = int(M["m10"] / M["m00"])
    gravity_y = int(M["m01"] / M["m00"])
    return gravity_x, gravity_y
慣性主軸
def get_inertia_slope(image):
    M = cv2.moments(image)
    inertia_theta = 0.5*np.arctan2(2*M["mu11"], M["mu20"]-M["mu02"])
    inertia_slope = np.tan(inertia_theta)
    return inertia_slope

3. グラフに表示

サンプル画像の確認

サンプル画像の確認
image = get_sample_image()
fig, ax = plt.subplots(1, 1, figsize=(8, 4))
cbar = ax.imshow(image, cmap=cm.CMRmap, origin="lower")
ax.set_xlabel("x")
ax.set_ylabel("y")
fig.colorbar(cbar, shrink=0.8)
plt.show()

出力結果
image.png

重心と完成主軸をグラフに表示

重心と完成主軸をグラフに表示
gravity_x, gravity_y = get_gravity_coord(image)
inertia_slope = get_inertia_slope(image)
x = np.arange(101)
y = inertia_slope*(x-gravity_x) + gravity_y  # 傾きから直線をプロット

fig, ax = plt.subplots(1, 1, figsize=(8, 4))
ax.scatter(gravity_x, gravity_y, label="gravity")
ax.plot(x, y, label="principal_axis_of_inertia")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xlim(0, 100)
ax.set_ylim(0, 50)
ax.legend()
plt.show()

出力結果
image.png

重心と慣性主軸を画像に重ねて表示

重心と慣性主軸を画像に重ねて表示
fig, ax = plt.subplots(1, 1, figsize=(8, 4))
cbar = ax.imshow(image, cmap=cm.CMRmap, origin="lower")
ax.scatter(gravity_x, gravity_y, label="gravity")
ax.plot(x, y, label="principal_axis_of_inertia")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xlim(0, 100)
ax.set_ylim(0, 50)
fig.colorbar(cbar, shrink=0.8)
ax.legend()
plt.show()

出力結果
image.png

まとめ

OpenCVとmatplotlibで画像の重心と慣性主軸をプロットする方法を紹介しました。誰かの役に立てれば幸いです。

参考資料

Stack Overflow, Extend part of an object

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4