3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【画像処理】Numpyで大津の二値化(判別分析法)

Posted at

Numpyで画像の二値化を実装してみます。

まず、二値化する画像を読み込み、グレースケール画像に変換します。

original_image = plt.imread(image_name)
if np.issubdtype(original_image.dtype, np.floating):
  original_image = (original_image * 255).astype(np.uint8)
gray_image = (0.2116 * original_image[:,:,0] + 0.7152 * original_image[:,:,1] + 0.0722 * original_image[:,:,2]).astype(np.uint8)
plt.imshow(gray_image, cmap='gray')

GrayImage.png

任意の閾値で二値化をする関数を実装してみます。これはnumpy.whereを用いると簡単に実装できます。

def thresholding(image, threshold):
  return np.where(image <= threshold, 0, 255)
  
threshold_image = thresholding(gray_image, 128)
plt.imshow(threshold_image, cmap='gray')

ThresholdImage.png


次に大津の二値化(判別分析法)を実装してみます。

大津の二値化ではクラス間分散$\sigma_b^2$とクラス内分散$\sigma_w^2$の比である分離度$\frac{\sigma_b^2}{\sigma_w^2}$が最大になる閾値で二値化を行います。

黒画素クラスの平均と分散を$m_1$と$\sigma_1^2$、白画素クラスの平均と分散を$m_2$と$\sigma_2^2$とし、黒画素クラスと白画素クラスの所属する画素数をそれぞれ$\omega_1$、$\omega_2$とすると、クラス間分散$\sigma_b^2$とクラス内分散$\sigma_w^2$はそれぞれ以下のようになります。

\sigma_w^2=\frac{\omega_1\sigma_1^2+\omega_2\sigma_2^2}{\omega_1\omega_2} \\
\sigma_b^2=\frac{\omega_1\omega_2(m_1-m_2)^2}{(\omega_1+\omega_2)^2}

全分散$\sigma_t^2$は閾値によらず一定で、クラス間分散$\sigma_b^2$とクラス内分散$\sigma_w^2$と以下のように関係にあります。

\sigma_t^2=\sigma_w^2 + \sigma_b^2

このことから分離度$\frac{\sigma_b^2}{\sigma_w^2}$は次のようになり、クラス間分散$\sigma_b^2$が最大のときに分離度$\frac{\sigma_b^2}{\sigma_w^2}$も最大になることがわかります。

\frac{\sigma_b^2}{\sigma_w^2}=\frac{\sigma_b^2}{\sigma_t^2-\sigma_b^2}

実装としては、すべての閾値に対してクラス間分散$\sigma_b^2$を計算して、クラス間分散$\sigma_b^2$が最大となるとき閾値で二値化を行います。クラス間分散$\sigma_b^2$の分母は一定なので、実装では計算を省略します。

def otsu_thresholding(image):
  between_class_variance = np.zeros(256)
  for i in range(np.min(image), np.max(image)):
    class1 = image <= i 
    class2 = ~class1
    average1 = np.average(image[class1])
    average2 = np.average(image[class2])
    num1 = np.count_nonzero(class1)
    num2 = np.count_nonzero(class2)
    between_class_variance[i] = (num1 * num2 * (average1 - average2) ** 2)
  threshold = np.argmax(between_class_variance)
  return np.where(image <= threshold, 0, 255)

plt.imshow(otsu_thresholding(gray_image), cmap='gray')

OtsuTresholdImage.png


実装したコードはGoogle Colaboratoryに置いてあります。

3
2
0

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
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?