ここでは、OpenCVに頼らず、自分でヒストグラムとしきい値を計算して大津法を実装してみましょう。
目次
🔧 ステップ構成(手作業で大津法を実装)
- 画像をグレースケールで読み込み
- ヒストグラムを計算
- 正規化されたヒストグラム(確率分布)を求める
- 全てのしきい値 tt に対して、クラス間分散を計算
- 最大の分散を与える tt を見つける → それが最適なしきい値!
✅ Python実装コード(完全自作)
import cv2
import numpy as np
import matplotlib.pyplot as plt
# グレースケール画像を読み込む
img = cv2.imread('sample.jpg', 0)
# 画像サイズ
height, width = img.shape
total_pixels = height * width
# ① ヒストグラムを作成(256階調)
hist = np.zeros(256)
for value in img.flatten():
hist[value] += 1
# ② 正規化ヒストグラム(確率)
prob = hist / total_pixels
# ③ 累積和と平均
omega = np.cumsum(prob) # 重み(累積分布)
mu = np.cumsum(prob * np.arange(256)) # 累積平均
# ④ 全体平均
mu_total = mu[-1]
# ⑤ クラス間分散を計算
sigma_b_squared = (mu_total * omega - mu)**2 / (omega * (1 - omega) + 1e-6) # ゼロ除算回避のため +ε
# ⑥ 最大となるしきい値を探す
optimal_threshold = np.argmax(sigma_b_squared)
# ⑦ 二値化
binary = (img > optimal_threshold).astype(np.uint8) * 255
# 表示
print(f"最適なしきい値(Otsu法): {optimal_threshold}")
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.title("Original")
plt.imshow(img, cmap='gray')
plt.subplot(1, 3, 2)
plt.title("Histogram")
plt.plot(hist)
plt.subplot(1, 3, 3)
plt.title("Otsu Binarized")
plt.imshow(binary, cmap='gray')
plt.tight_layout()
plt.show()
🔍 補足ポイント
| 項目 | 説明 |
|---|---|
np.arange(256) | 輝度0〜255の配列 |
mu_total * omega - mu | クラス間の差(全体平均とクラス平均の差) |
np.argmax() | 最大値のインデックスを取得(=最適しきい値) |
📌 実行のための準備
opencv-pythonとmatplotlibをインストール:
pip install opencv-python matplotlib
sample.jpgは適当なグレースケール画像(カラーでもOK)でOK。
必要であれば、カラー画像 → グレースケール → 大津法適用 → 結果保存までのワークフローも作れます!
試してみた画像があれば、アップロードしてくれてもOKです!
