概要
OpenCVで検出した輪郭から面積・周囲長・重心などの幾何学的特徴量を計算し、外接矩形や最小外接円を描画する方法を解説する。
1. ライブラリのインポートと画像の読み込み
import cv2
import numpy as np
img = cv2.imread("input.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
-
cv2.cvtColor()でグレースケールに変換する
2. 二値化と輪郭検出
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
-
cv2.RETR_EXTERNAL: 最外部の輪郭のみ取得 -
cv2.CHAIN_APPROX_SIMPLE: 輪郭点を圧縮して格納
3. 輪郭の面積を求める
for cnt in contours:
area = cv2.contourArea(cnt)
print(f"面積: {area}")
-
cv2.contourArea(contour): 輪郭で囲まれた領域の面積をピクセル単位で返す - 面積が0の場合、輪郭が直線状であることを意味する
4. 輪郭の周囲長を求める
for cnt in contours:
perimeter = cv2.arcLength(cnt, closed=True)
print(f"周囲長: {perimeter}")
-
cnt: 輪郭の点群 -
closed=True: 輪郭が閉じた曲線であることを指定(始点と終点を結ぶ) -
closed=False: 開いた曲線として計算する
5. 重心を求める
for cnt in contours:
M = cv2.moments(cnt)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
cv2.circle(img, (cx, cy), 5, (0, 0, 255), -1)
print(f"重心: ({cx}, {cy})")
-
cv2.moments(): 輪郭のモーメントを辞書形式で返す -
m00: 面積(ゼロ次モーメント) -
m10 / m00: 重心のx座標 -
m01 / m00: 重心のy座標 -
m00 == 0の場合はゼロ除算を避ける
6. 外接矩形を描画する
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
-
cv2.boundingRect(): 輪郭を囲む最小の軸平行な矩形を返す - 戻り値は
(x, y, w, h)の4要素タプル
7. 最小外接円を描画する
for cnt in contours:
(cx, cy), radius = cv2.minEnclosingCircle(cnt)
center = (int(cx), int(cy))
radius = int(radius)
cv2.circle(img, center, radius, (255, 0, 0), 2)
-
cv2.minEnclosingCircle(): 輪郭を囲む最小の円を返す - 戻り値は中心座標
(x, y)と半径radius
8. 回転矩形を描画する
for cnt in contours:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int32(box)
cv2.drawContours(img, [box], 0, (0, 255, 255), 2)
-
cv2.minAreaRect(): 輪郭を囲む最小面積の回転矩形を返す - 戻り値は
((cx, cy), (w, h), angle)のタプル -
cv2.boxPoints(): 回転矩形の4頂点座標を取得する -
np.int32(): 描画用に整数座標に変換する
9. 表示して確認
import cv2
import numpy as np
img = cv2.imread("input.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, closed=True)
M = cv2.moments(cnt)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
cv2.circle(img, (cx, cy), 5, (0, 0, 255), -1)
print(f"面積: {area:.1f}, 周囲長: {perimeter:.1f}, 重心: ({cx}, {cy})")
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Contour Properties", img)
cv2.waitKey(0)
cv2.destroyAllWindows()