Opencv大津二值化算法

Opencv大津二值化算法,也被称作最大类间方差法,是一种可以自动确定二值化中阈值的算法,从类内方差和类间方差的比值计算得来:

  • 小于阈值 t 的类记作 0,大于阈值 t 的类记作 1;
  • $$w0$$ 和 $$w1$$ 是被阈值 t 分开的两个类中的像素数占总像素数的比率(满足 w0+w1=1);
  • $$S0^2$$, $$S1^2$$ 是这两个类中像素值的方差;
  • $$M0$4, $$M1$$ 是这两个类的像素值的平均值;

也就是说:

类内方差:Sw^2 = w0 * S0^2 + w1 * S1^2

类间方差:Sb^2 = w0 * (M0 – Mt)^2 + w1 * (M1 – Mt)^2 = w0 * w1 * (M0 – M1) ^2

图像所有像素的方差:St^2 = Sw^2 + Sb^2 = (const)

根据以上的式子,我们用以下的式子计算分离度:

分离度X = Sb^2 / Sw^2 = Sb^2 / (St^2 – Sb^2)

也就是说:

argmax_{t} X = argmax_{t} Sb^2

换言之,如果使 Sb^2 = w0 * w1 * (M0 – M1) ^2 最大,就可以得到最好的二值化阈值 t。

pyhton实现:

import cv2
import numpy as np

# Read image
img = cv2.imread("imori.jpg").astype(np.float)

H, W, C = img.shape

# Grayscale
out = 0.2126 * img[..., 2] + 0.7152 * img[..., 1] + 0.0722 * img[..., 0]
out = out.astype(np.uint8)

# Determine threshold of Otsu's binarization
max_sigma = 0
max_t = 0

for _t in range(1, 255):
    v0 = out[np.where(out < _t)]
    m0 = np.mean(v0) if len(v0) > 0 else 0.
    w0 = len(v0) / (H * W)
    v1 = out[np.where(out >= _t)]
    m1 = np.mean(v1) if len(v1) > 0 else 0.
    w1 = len(v1) / (H * W)
    sigma = w0 * w1 * ((m0 - m1) ** 2)
    if sigma > max_sigma:
        max_sigma = sigma
        max_t = _t

# Binarization
print("threshold >>", max_t)
th = max_t
out[out < th] = 0
out[out >= th] = 255

# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()

C++实现:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <math.h>

int main(int argc, const char* argv[]){
  cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR);

  int width = img.rows;
  int height = img.cols;

  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);

  // gray
  int val = 0;
  for (int j = 0; j < height; j++){
    for (int i = 0; i < width; i++){
      val = (int)((float)img.at<cv::Vec3b>(j,i)[0] * 0.0722 + \
          (float)img.at<cv::Vec3b>(j,i)[1] * 0.7152 + \
          (float)img.at<cv::Vec3b>(j,i)[2] * 0.2126);
      out.at<uchar>(j,i) = (uchar)val;
    }
  }

  // determine threshold
  double w0 = 0, w1 = 0;
  double m0 = 0, m1 = 0;
  double max_sb = 0, sb = 0;
  int th = 0;

  for (int t = 0; t < 255; t++){
    w0 = 0;
    w1 = 0;
    m0 = 0;
    m1 = 0;
    for (int j = 0; j < height; j++){
      for (int i = 0; i < width; i++){
    val = (int)(out.at<uchar>(j,i));

    if (val < t){
      w0++;
      m0 += val;
    } else {
      w1++;
      m1 += val;
    }
      }
    }

    m0 /= w0;
    m1 /= w1;
    w0 /= (height * width);
    w1 /= (height * width);
    sb = w0 * w1 * pow((m0 - m1), 2);

    if(sb > max_sb){
      max_sb = sb;
      th = t;
    }

  }

  // binalization
  for (int j = 0; j < height; j++){
    for (int i = 0; i < width; i++){
      val = (int)(out.at<uchar>(j,i));
      if (val < th){
    val = 0;
      } else {
    val = 255;
      }
      out.at<uchar>(j,i) = (uchar)val;
    }
  }

  std::cout << "threshold >> " << th << std::endl;

  //cv::imwrite("out.jpg", out);
  cv::imshow("answer", out);
  cv::waitKey(0);
  cv::destroyAllWindows();

  return 0;

}

输入:

Opencv大津二值化算法

输出:

Opencv大津二值化算法

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程