9.3Otsu阈值分割
基本概念
在OpenCV中,Otsu阈值分割是一种全局阈值分割方法,但它会自动选择一个最佳的阈值来分割图像,这个阈值是通过最小化类内方差或等价地最大化类间方差来确定的。OpenCV提供了cv::threshold
函数来实现这一功能,其中可以指定cv::THRESH_OTSU
作为阈值类型之一。
OpenCV中的Otsu阈值分割
要使用Otsu阈值分割,你需要将cv::threshold
函数的thresh
参数设置为0(因为Otsu算法会自动计算阈值),并将maxval
参数设置为当像素值超过(或小于,取决于threshType
)阈值时应该赋予的新值,然后将threshType
设置为cv::THRESH_BINARY | cv::THRESH_OTSU
。注意,虽然cv::THRESH_OTSU
是一个标志,但你需要将它与实际的阈值类型(如cv::THRESH_BINARY
)组合使用。
Otsu阈值分割是一种广泛使用的全局阈值选择方法,它自动选择一个阈值来将图像分成两部分:前景和背景。这种方法基于最大类间方差的原则来寻找最优的单阈值,从而将图像二值化。
函数原型
对于cv::threshold
函数中与Otsu相关的部分,其原型可以简化为:
double cv::threshold(InputArray src, OutputArray dst, double thresh, double maxval, int thresholdType)但当你使用Otsu阈值时,thresh参数实际上会被忽略,因为它将由算法自动计算。你主要关心的是返回的阈值(如果thresholdType包含cv::THRESH_OTSU),它可以通过函数的返回值获得。
Otsu's二值化方法是一种自动计算图像最佳阈值的技术,它常用于图像分割。这种方法的目标是找到一个阈值,使得图像分割成前景和背景两部分之后,这两部分之间的类间方差最大。简而言之,Otsu's方法试图找到一个全局阈值,将图像像素分为两个类(前景和背景),使得它们之间的差异最大化。
在OpenCV中,你可以使用cv::threshold
函数来应用Otsu's阈值分割。
以下是如何在C++中使用OpenCV实现Otsu's阈值分割的步骤:
步骤 1: 包含必要的头文件
首先,你需要包含OpenCV的头文件。#include <opencv2/opencv.hpp>
using namespace cv;步骤 2: 加载图像
加载你要处理的灰度图像。如果你有一个彩色图像,需要先将其转换为灰度图像。
Mat src = imread("path_to_your_image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {std::cout << "Error opening image" << std::endl;return -1;
}步骤 3: 应用Otsu's阈值分割
使用cv::threshold函数设置threshold参数为0,并且传递THRESH_BINARY + THRESH_OTSU标志。int thresholdValue = 0;
Mat dst;
threshold(src, dst, thresholdValue, 255, THRESH_BINARY + THRESH_OTSU);这里,thresholdValue设置为0是因为Otsu's算法会根据图像的直方图自动计算最佳阈值。255是超过阈值后的最大值,对于黑白二值化来说通常就是白色。步骤 4: 显示结果
显示原始图像和处理后的图像。
namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", src);namedWindow("Otsu's Binarization", WINDOW_NORMAL);
imshow("Otsu's Binarization", dst);waitKey(0);
完整示例代码1
以下是完整的示例代码:
#include <opencv2/opencv.hpp>
#include <iostream>int main(int argc, char** argv) {cv::Mat src = cv::imread("path_to_your_image.jpg", cv::IMREAD_GRAYSCALE);if (src.empty()) {std::cout << "Error opening image" << std::endl;return -1;}int thresholdValue = 0;cv::Mat dst;cv::threshold(src, dst, thresholdValue, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);cv::namedWindow("Original Image", cv::WINDOW_NORMAL);cv::imshow("Original Image", src);cv::namedWindow("Otsu's Binarization", cv::WINDOW_NORMAL);cv::imshow("Otsu's Binarization", dst);cv::waitKey(0);return 0;
}
运行结果1
Otsu 阈值分割的实现步骤
1. 导入必要的头文件: 需要包含 OpenCV 的核心模块头文件。
#include <opencv2/opencv.hpp>
using namespace cv;2. 读取图像: 使用 imread 函数从磁盘读取图像,并将其转换为灰度图,因为 Otsu 阈值分割通常应用于灰度图像。Mat src = imread("path/to/image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {std::cout << "Error: Image not found or unable to read the image." << std::endl;return -1;
}3. 应用 Otsu 阈值分割: 使用 threshold 函数进行 Otsu 阈值分割。这里需要注意的是,需要将 THRESH_OTSU 标志添加到 threshold 函数中,以便启用 Otsu 方法。double thresholdValue;
// 选择一个初始阈值(这里设置为0),并让OpenCV计算最佳阈值
threshold(src, src, 0, 255, THRESH_BINARY + THRESH_OTSU, &thresholdValue);
THRESH_OTSU 标志告诉 threshold 函数使用 Otsu 的算法来计算最佳阈值。thresholdValue 是一个输出参数,它将返回计算出的最佳阈值。4. 显示结果: 可以通过 imshow 显示原始图像和处理后的图像,并等待用户按键退出。namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", src);namedWindow("Otsu Threshold Result", WINDOW_NORMAL);
imshow("Otsu Threshold Result", src);waitKey(0); // Wait for a keystroke in the window
5. 获取并打印阈值(可选): 如果需要查看计算出的最佳阈值,可以在程序中打印出来。
std::cout << "The optimal threshold value is: " << thresholdValue << std::endl;
完整的示例代码2
#include "pch.h"#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {// 读取图像Mat src = imread("654.png", IMREAD_GRAYSCALE);if (src.empty()) {cout << "Error: Image not found or unable to read the image." << endl;return -1;}// 应用 Otsu 阈值分割double thresholdValue;Mat dst;thresholdValue=threshold(src, dst, 0, 255, THRESH_BINARY + THRESH_OTSU);// 显示结果namedWindow("Original Image", WINDOW_NORMAL);imshow("Original Image", src);namedWindow("Otsu Threshold Result", WINDOW_NORMAL);imshow("Otsu Threshold Result", dst);// 打印阈值cout << "The optimal threshold value is: " << thresholdValue << endl;waitKey(0); // Wait for a keystroke in the windowreturn 0;
}注意事项
•Otsu 阈值分割适用于双峰直方图的图像,即图像中前景和背景有明显的灰度差异。
•如果图像的直方图没有明显的双峰分布,Otsu 阈值分割可能不会得到满意的结果。
•对于光照不均匀或背景复杂的图像,可能需要结合其他预处理技术(如直方图均衡化)来改善分割效果。
运行结果2
示例代码3
下面是一个使用OpenCV C++ API进行Otsu阈值分割的示例代码:
#include <opencv2/opencv.hpp>
#include <iostream> using namespace cv;
using namespace std; int main() { // 加载图像 Mat src = imread("path_to_image.jpg", IMREAD_GRAYSCALE); if (src.empty()) { cout << "Could not open or find the image!\n"; return -1; } // 创建输出图像 Mat dst; // 应用Otsu阈值分割 // 注意:thresh被设置为0,因为Otsu会自动计算它 // maxval是超过阈值时要赋予的新像素值 // THRESH_BINARY | THRESH_OTSU表示使用Otsu算法进行二值化 double threshVal = threshold(src, dst, 0, 255, THRESH_BINARY | THRESH_OTSU); // 输出计算得到的阈值 cout << "Calculated threshold value with Otsu's method: " << threshVal << endl; // 显示结果 imshow("Original Image", src); imshow("Otsu Thresholding", dst); waitKey(0); return 0;
}
在这个示例中,我们加载了一个灰度图像,并使用Otsu算法对其进行了二值化处理。计算得到的阈值通过threshold
函数的返回值获得,并打印到控制台上。然后,我们显示了原始图像和分割后的图像。
运行结果3
请注意,Otsu阈值分割适用于具有明显双峰直方图的图像,即图像的前景和背景在像素值上差异较大,且它们的分布相对独立。如果图像的直方图不满足这些条件,Otsu算法可能无法提供最佳结果。