1、大津OTSU算法
最大类间方差法是1979年由日本学者大津(Nobuyuki Otsu)提出的,是一种自适应阈值确定的方法,又叫大津法,简称OTSU,是一种基于全局的二值化算法,它是根据图像的灰度特性,将图像分为前景和背景两个部分。当取最佳阈值时,两部分之间的差别应该是最大的,在OTSU算法中所采用的衡量差别的标准就是较为常见的最大类间方差。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大,当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小,当所取阈值的分割使类间方差最大时就意味着错分概率最小。
2、Image Threshold
If the intensity of a pixel in the input image is greater than a threshold, the corresponding output pixel is marked as white (foreground), and if the input pixel intensity intensity is less than or equal to the threshold, the output pixel location is marked black (background).
Image thresholding is used in many applications as a pre-processing step. For example, you may use it in medical image processing to reveal tumor in a mammogram or localize a natural disaster in satellite images.
A problem with simple thresholding is that you have to manually specify the threshold value. We can manually check how good a threshold is by trying different values but it is tedious and it may break down in the real world.
So, we need a way to automatically determine the threshold. The Otsu’s technique named after its creator Nobuyuki Otsu is a good example of auto thresholding.
Before we jump into the details of the technique let’s understand how image thresholding relates to image segmentation.
3、Otsu thresholding
To improve on the segmentation, we next investigated a smarter thresholding approach: Otsu’s method. Otsu thresholding assumes that there are two classes of pixels in the image which we wish to separate. Additionally, Otsu’s method assumes that the two classes are separated by a roughly bimodal intensity histogram. The algorithm will then find the optimal threshold that separates the two classes via an exhaustive search for the threshold that minimizes the variance between the two classes. Our golf course images seemed to fit the assumptions of the algorithm: the two classes of pixels are “trees” and “the rest” and the intuition for our bimodal intensity histogram is “trees are dark”, “the rest is light”.
Using Otsu’s method for thresholding worked much better than the simple thresholding: large parts of the playable rough are now included in the segmented mask. There are some small false-positives such as the masked pixels in the tree crowns on the right upper side of the image. However, those small areas of noise were easily fixed via morphological operations such as opening, erosion and dilation.
One area where we noted that Otsu’s method broke down is in an image that contains shadows such as the example below. Otsu’s method operates on grayscale images so it can’t distinguish the deep dark green color of the tree canopy from the dark shadows of a tree. This is very visible in the upper center of the picture where shadows on the right end of the horizontal tree line are being included. However, for our golf course image segmentation, these shadows should be excluded.
4、大津OTSU算法的源代码(两个版本)
using System;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
namespace Legalsoft.Truffer.ImageTools
{
public static partial class BinarizationHelper
{
#region 灰度图像二值化 全局算法 大津OTSU 算法(两种代码)
/// <summary>
/// 图像二值化的大津算法
/// Otsu's Thresholding
/// https://blog.csdn.net/weixin_35811044/article/details/85255924
/// </summary>
/// <param name="data">灰度化的图片数据</param>
/// <returns></returns>
public static int Otsu_Threshold(byte[,] data)
{
int height = data.GetLength(0);
int width = data.GetLength(1);
int[] histv = Gray_Histogram(data);
double[] histogram = Histogram_Normalize(histv);
int pixelNumb = height * width;
double[] variances = new double[256];
for (int T = 0; T < histogram.Length; T++)
{
double n1 = 0, n2 = 0;
double total1 = 0, total2 = 0;
double aver1 = 0, aver2 = 0;
double w1 = 0, w2 = 0;
double ft1 = 0, ft2 = 0;
for (int i = 0; i < T; i++)
{
n1 += histogram[i];
total1 += histogram[i] * i;
}
for (int j = T; j < variances.Length; j++)
{
n2 += histogram[j];
total2 += histogram[j] * j;
}
w1 = n1 / pixelNumb;
w2 = n2 / pixelNumb;
aver1 = (n1 == 0) ? 0 : (total1 / n1);
aver2 = (n2 == 0) ? 0 : (total2 / n2);
for (int i = 0; i < T; i++)
{
ft1 += (Math.Pow((i - aver1), 2) * histogram[i]);
}
for (int j = T; j < 256; j++)
{
ft2 += (Math.Pow((j - aver2), 2) * histogram[j]);
}
ft1 = (n1 == 0) ? 0 : (ft1 / n1);
ft2 = (n2 == 0) ? 0 : (ft2 / n2);
variances[T] = w1 * ft1 + w2 * ft2;
}
double min = variances[0];
int threshold = 0;
for (int i = 1; i < variances.Length; i++)
{
if (variances[i] < min)
{
min = variances[i];
threshold = i;
}
}
return threshold;
}
/// <summary>
/// 大津算法之二
/// </summary>
/// <param name="histogram"></param>
/// <returns></returns>
public static int Ostu_Threshold_Second(int[] histogram)
{
int left = Histogram_Left(histogram);
int right = Histogram_Right(histogram);
int amount = Histogram_Sum(histogram);
double PixelIntegral = Histogram_Sum(histogram, 1);
int Threshold = 0;
double PixelBack = 0.0;
double PixelIntegralBack = 0.0;
double SigmaB = -1.0;
for (int i = left; i <= right; i++)
{
PixelBack = PixelBack + histogram[i];
double PixelFore = amount - PixelBack;
double OmegaBack = (double)PixelBack / (double)amount;
double OmegaFore = (double)PixelFore / (double)amount;
PixelIntegralBack += histogram[i] * (double)i;
double PixelIntegralFore = PixelIntegral - PixelIntegralBack;
double MicroBack = (double)PixelIntegralBack / PixelBack;
double MicroFore = (double)PixelIntegralFore / PixelFore;
double Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);
if (Sigma > SigmaB)
{
SigmaB = Sigma;
Threshold = i;
}
}
return Threshold;
}
/// <summary>
/// 图像二值化的大津算法
/// </summary>
/// <param name="data">灰度化的图片数据</param>
public static void Otsu_Algorithm(byte[,] data)
{
int threshold = Otsu_Threshold(data);
Threshold_Algorithm(data, threshold);
}
#endregion
}
}