摘要: 本文深入探讨了 Otsu 二值化算法,详细阐述其原理,包括类间方差的计算与阈值确定机制。分别给出了该算法在 C#、Python 和 C++ 中的实现代码示例,并对代码进行了详细注释与分析。此外,还探讨了 Otsu 二值化算法在图像分割、目标检测、字符识别等多个领域的广泛应用,展示了其在计算机视觉与图像处理领域的重要地位与价值。
一、引言
在计算机视觉与图像处理领域,图像二值化是一项基础且关键的操作。它将图像中的像素根据一定的规则分为两类,通常为前景(目标)和背景,从而简化图像分析与处理过程。Otsu 二值化算法作为一种自动确定阈值的方法,在众多应用场景中表现出色,无需手动设定阈值,能够根据图像自身的特性自适应地找到最佳阈值,广泛应用于图像分割、目标识别、文档处理等多个领域。
二、Otsu 二值化算法原理
(一)灰度直方图基础
图像的灰度直方图是理解 Otsu 算法的重要基础。灰度直方图描述了图像中各个灰度级出现的频率。对于一幅灰度图像,其灰度值通常在 0(黑色)到 255(白色)之间。通过统计每个灰度值在图像中出现的像素数量,可构建灰度直方图。
设图像的灰度级为 L(对于 8 位灰度图像,L=256),图像中灰度值为i的像素数量为ni ,总像素数量为 。
。
(二)类间方差公式推导
Otsu 算法的核心是最大化类间方差。假设通过阈值t将图像像素分为两类C0(灰度值范围为0到t)和C1(灰度值范围为t+1到L-1)。
两类的概率分别为:


两类的均值分别为:


图像的总均值为:

类间方差公式为:

通过遍历所有可能的阈值t(从0到L-2),计算对应的类间方差 ,使得
 ,使得 最大的阈值t即为 Otsu 算法所求的最佳阈值。
最大的阈值t即为 Otsu 算法所求的最佳阈值。
三、Otsu 二值化算法在 C# 中的实现
(一)代码示例
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
class OtsuBinarization
{
    public static Image<Gray, byte> OtsuBinarize(Image<Gray, byte> grayImage)
    {
        // 获取图像的宽度和高度
        int width = grayImage.Width;
        int height = grayImage.Height;
        // 计算灰度直方图
        int[] histogram = new int[256];
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                byte pixelValue = grayImage[y, x].Intensity;
                histogram[pixelValue]++;
            }
        }
        // 总像素数量
        int totalPixels = width * height;
        // 初始化类间方差和最佳阈值
        double maxVariance = 0;
        int optimalThreshold = 0;
        // 计算类间方差并寻找最佳阈值
        double sum = 0;
        double sumB = 0;
        double wB = 0;
        double wF = 0;
        for (int t = 0; t < 256; t++)
        {
            sum += t * histogram[t];
        }
        for (int t = 0; t < 256; t++)
        {
            wB += histogram[t];
            if (wB == 0)
            {
                continue;
            }
            wF = totalPixels - wB;
            if (wF == 0)
            {
                break;
            }
            sumB += t * histogram[t];
            double meanB = sumB / wB;
            double meanF = (sum - sumB) / wF;
            double variance = wB * wF * (meanB - meanF) * (meanB - meanF);
            if (variance > maxVariance)
            {
                maxVariance = variance;
                optimalThreshold = t;
            }
        }
        // 根据最佳阈值进行二值化
        Image<Gray, byte> binaryImage = new Image<Gray, byte>(width, height);
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                byte pixelValue = grayImage[y, x].Intensity;
                if (pixelValue <= optimalThreshold)
                {
                    binaryImage[y, x] = new Gray(0);
                }
                else
                {
                    binaryImage[y, x] = new Gray(255);
                }
            }
        }
        return binaryImage;
    }
}
(二)代码分析
- 首先获取输入灰度图像的宽度和高度,用于后续遍历图像像素和计算相关参数。
- 构建一个长度为 256 的数组 histogram来存储灰度直方图。通过两层循环遍历图像的每个像素,将像素的灰度值作为索引,增加对应histogram数组元素的值,从而统计每个灰度级的像素数量。
- 计算图像的总像素数量 totalPixels,用于后续计算各类的概率。
- 初始化类间方差 maxVariance为 0,最佳阈值optimalThreshold为 0。同时初始化一些用于计算类间方差的变量,如sum(用于计算图像总灰度值之和)、sumB(背景类灰度值之和)、wB(背景类像素数量占比)、wF(前景类像素数量占比)。
- 计算图像总灰度值之和 sum,通过遍历灰度直方图,将每个灰度值乘以其对应的像素数量并累加。
- 主循环遍历所有可能的阈值 t(从 0 到 255)。在循环内:- 首先计算背景类像素数量占比 wB和前景类像素数量占比wF。
- 计算背景类灰度值之和 sumB,并进一步计算背景类均值meanB和前景类均值meanF。
- 根据类间方差公式计算当前阈值 t下的类间方差variance。
- 如果当前方差大于已记录的最大方差 maxVariance,则更新最大方差和最佳阈值。
 
- 首先计算背景类像素数量占比 
- 最后,根据最佳阈值 optimalThreshold对图像进行二值化。再次遍历图像像素,如果像素灰度值小于等于最佳阈值,则将其在二值化图像中设为黑色(0),否则设为白色(255)。
四、Otsu 二值化算法在 Python 中的实现
(一)代码示例
import cv2
import numpy as np
def otsu_binarization(image):
    # 将图像转换为灰度图像
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 计算灰度直方图
    histogram = np.zeros(256)
    height, width = gray_image.shape
    for y in range(height):
        for x in range(width):
            pixel_value = gray_image[y, x]
            histogram[pixel_value] += 1
    # 总像素数量
    total_pixels = height * width
    # 初始化类间方差和最佳阈值
    max_variance = 0
    optimal_threshold = 0
    # 计算类间方差并寻找最佳阈值
    sum_gray = 0
    sum_back = 0
    w_back = 0
    w_fore = 0
    for t in range(256):
        sum_gray += t * histogram[t]
    for t in range(256):
        w_back += histogram[t]
        if w_back == 0:
            continue
        w_fore = total_pixels - w_back
        if w_fore == 0:
            break
        sum_back += t * histogram[t]
        mean_back = sum_back / w_back
        mean_fore = (sum_gray - sum_back) / w_fore
        variance = w_back * w_fore * (mean_back - mean_fore) ** 2
        if variance > max_variance:
            max_variance = variance
            optimal_threshold = t
    # 根据最佳阈值进行二值化
    binary_image = np.zeros((height, width), dtype=np.uint8)
    for y in range(height):
        for x in range(width):
            pixel_value = gray_image[y, x]
            if pixel_value <= optimal_threshold:
                binary_image[y, x] = 0
            else:
                binary_image[y, x] = 255
    return binary_image
(二)代码分析
- 利用 cv2.cvtColor函数将输入图像转换为灰度图像,方便后续处理。
- 构建一个长度为 256 的 histogram数组来存储灰度直方图。通过两层循环遍历灰度图像的每个像素,统计每个灰度级的像素数量。
- 计算图像的总像素数量 total_pixels。
- 初始化类间方差 max_variance为 0,最佳阈值optimal_threshold为 0,以及一些用于计算类间方差的变量sum_gray(图像总灰度值之和)、sum_back(背景类灰度值之和)、w_back(背景类像素数量占比)、w_fore(前景类像素数量占比)。
- 计算图像总灰度值之和 sum_gray,通过遍历灰度直方图,将每个灰度值乘以其对应的像素数量并累加。
- 主循环遍历所有可能的阈值 t(从 0 到 255)。在循环内:- 计算背景类像素数量占比 w_back和前景类像素数量占比w_fore。
- 计算背景类灰度值之和 sum_back,进而计算背景类均值mean_back和前景类均值mean_fore。
- 根据类间方差公式计算当前阈值 t下的类间方差variance。
- 如果当前方差大于已记录的最大方差 max_variance,则更新最大方差和最佳阈值。
 
- 计算背景类像素数量占比 
- 最后,根据最佳阈值 optimal_threshold对图像进行二值化。通过两层循环遍历图像像素,如果像素灰度值小于等于最佳阈值,则在二值化图像中设为黑色(0),否则设为白色(255)。
五、Otsu 二值化算法在 C++ 中的实现
(一)代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
cv::Mat otsuBinarization(cv::Mat grayImage)
{
    // 计算灰度直方图
    int histogram[256] = { 0 };
    int height = grayImage.rows;
    int width = grayImage.cols;
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            histogram[grayImage.at<uchar>(y, x)]++;
        }
    }
    // 总像素数量
    int totalPixels = height * width;
    // 初始化类间方差和最佳阈值
    double maxVariance = 0;
    int optimalThreshold = 0;
    // 计算类间方差并寻找最佳阈值
    double sum = 0;
    double sumB = 0;
    double wB = 0;
    double wF = 0;
    for (int t = 0; t < 256; t++)
    {
        sum += t * histogram[t];
    }
    for (int t = 0; t < 256; t++)
    {
        wB += histogram[t];
        if (wB == 0)
        {
            continue;
        }
        wF = totalPixels - wB;
        if (wF == 0)
        {
            break;
        }
        sumB += t * histogram[t];
        double meanB = sumB / wB;
        double meanF = (sum - sumB) / wF;
        double variance = wB * wF * (meanB - meanF) * (meanB - meanF);
        if (variance > maxVariance)
        {
            maxVariance = variance;
            optimalThreshold = t;
        }
    }
    // 根据最佳阈值进行二值化
    cv::Mat binaryImage(height, width, CV_8UC1);
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            if (grayImage.at<uchar>(y, x) <= optimalThreshold)
            {
                binaryImage.at<uchar>(y, x) = 0;
            }
            else
            {
                binaryImage.at<uchar>(y, x) = 255;
            }
        }
    }
    return binaryImage;
}
(二)代码分析
- 首先计算输入灰度图像的灰度直方图。通过两层循环遍历图像的每个像素,将像素的灰度值作为索引,增加对应 histogram数组元素的值。
- 计算图像的总像素数量 totalPixels。
- 初始化类间方差 maxVariance为 0,最佳阈值optimalThreshold为 0,以及一些用于计算类间方差的变量sum(图像总灰度值之和)、sumB(背景类灰度值之和)、wB(背景类像素数量占比)、wF(前景类像素数量占比)。
- 计算图像总灰度值之和 sum,通过遍历灰度直方图,将每个灰度值乘以其对应的像素数量并累加。
- 主循环遍历所有可能的阈值 t(从 0 到 255)。在循环内:- 计算背景类像素数量占比 wB和前景类像素数量占比wF。
- 计算背景类灰度值之和 sumB,并进一步计算背景类均值meanB和前景类均值meanF。
- 根据类间方差公式计算当前阈值 t下的类间方差variance。
- 如果当前方差大于已记录的最大方差 maxVariance,则更新最大方差和最佳阈值。
 
- 计算背景类像素数量占比 
- 最后,根据最佳阈值 optimalThreshold对图像进行二值化。通过两层循环遍历图像像素,如果像素灰度值小于等于最佳阈值,则在二值化图像中设为黑色(0),否则设为白色(255)。
六、Otsu 二值化算法的应用
(一)图像分割
在图像分割中,Otsu 二值化算法可用于将目标物体从背景中分离出来。例如在医学图像中,将病变组织与正常组织分离,或者在工业检测中,将产品与背景区分开来。通过自动确定阈值,能够适应不同图像的光照、对比度等变化,提高分割的准确性和稳定性。
(二)目标检测
在目标检测任务中,二值化后的图像可以突出目标物体的轮廓,便于后续的特征提取与目标识别。例如在人脸识别系统中,先对图像进行 Otsu 二值化,然后提取人脸的边缘特征,与数据库中的模板进行匹配,从而实现人脸检测与识别。
(三)字符识别
在光学字符识别(OCR)领域,Otsu 二值化发挥着极为关键的作用。无论是印刷体文字还是手写体文字的识别,都需要将文字从复杂的背景图像中清晰地分离出来,以提取文字的笔画、结构等特征信息。通过对包含文字的图像进行 Otsu 二值化处理,可以有效地将文字像素与背景像素区分开来,得到仅包含黑白两种像素值的二值图像。在这种二值图像中,文字部分通常呈现为前景(白色像素),背景则被转换为黑色像素,从而为后续的字符分割、特征提取以及分类识别等操作提供了便利且高质量的图像基础。例如,在文档数字化处理过程中,大量的纸质文档需要被扫描并转换为可编辑的电子文本,Otsu 二值化能够快速准确地处理扫描图像中的文字部分,极大地提高了 OCR 系统的识别准确率和效率。
(四)交通监控与智能驾驶
在交通监控系统中,Otsu 二值化算法被广泛应用于车辆检测、车牌识别等任务。对于交通场景图像,通过二值化可以突出车辆的轮廓、形状以及车牌区域等关键信息。在车辆检测方面,能够快速确定图像中车辆的位置和大致范围,为交通流量统计、违章行为监测等提供基础数据支持。而在车牌识别中,二值化后的车牌图像更易于进行字符分割和识别,有助于实现高效准确的车牌号码读取,这对于智能交通管理系统的运行至关重要。在智能驾驶领域,Otsu 二值化算法也可用于对道路图像进行预处理,例如识别道路标识、区分道路与周围环境等,为自动驾驶车辆的决策和导航提供重要的视觉信息依据,保障自动驾驶过程的安全性和可靠性。
(五)工业视觉检测
工业生产线上,产品的质量检测往往依赖于机器视觉技术,Otsu 二值化算法是其中常用的图像处理手段之一。在零部件的表面缺陷检测中,如金属制品表面的划痕、裂纹检测,或者电子元器件的外观瑕疵检测等任务中,通过对采集到的产品图像进行二值化处理,可以凸显出缺陷部分与正常产品表面的差异。由于缺陷区域通常在灰度值或纹理特征上与正常区域有所不同,Otsu 二值化能够根据图像自身的灰度分布自动确定合适的阈值,将缺陷部分清晰地分离出来,便于后续通过形态学分析、特征提取等方法进一步判断缺陷的类型、大小和位置等信息,从而实现对产品质量的快速、准确检测,有效提高工业生产的自动化程度和产品质量控制水平。
(六)生物特征识别
除了人脸识别,在其他生物特征识别领域如指纹识别、虹膜识别等方面,Otsu 二值化也有着重要的应用。在指纹识别中,指纹图像经过二值化后,指纹的纹路细节更加突出,便于提取指纹的特征点,如端点、分叉点等,这些特征点构成了指纹的独特标识,用于与数据库中的指纹模板进行匹配识别。对于虹膜识别,二值化可以增强虹膜图像的对比度,突出虹膜的纹理结构,有助于提取虹膜的纹理特征,进而实现高精度的身份验证。通过 Otsu 二值化算法对生物特征图像进行预处理,能够提高生物特征提取的准确性和稳定性,提升整个生物特征识别系统的性能和可靠性,在安防监控、门禁系统等众多领域发挥着保障安全和身份识别的重要作用。
(七)遥感图像分析
在遥感图像处理与分析中,Otsu 二值化算法可用于土地利用分类、目标物提取等任务。例如,在对卫星遥感图像进行分析时,通过二值化可以将不同地物类型(如水体、植被、建筑用地等)进行初步分离。由于不同地物在遥感图像中的灰度特征存在差异,Otsu 二值化能够自动确定合适的阈值,将图像中的像素划分为不同类别,从而为进一步的精确分类和信息提取提供基础。在目标物提取方面,比如从遥感图像中提取特定的建筑物、道路等目标,二值化后的图像可以简化目标物的轮廓和区域,便于采用形态学操作、区域生长等方法进行目标物的精确提取和分析,为城市规划、资源监测、环境评估等领域提供有力的技术支持和数据依据。
综上所述,Otsu 二值化算法以其自动确定阈值的优势,在众多计算机视觉和图像处理领域得到了广泛而深入的应用,为提高各种图像处理任务的效率和准确性发挥着不可或缺的作用。随着技术的不断发展,其在新兴领域和复杂应用场景中的潜力也将不断被挖掘和拓展。



















