目标
在本教程中,您将学习如何:
- 使用 OpenCV 函数 cv::threshold 执行基本阈值操作
理论依据
注意
下面的解释属于 Bradski 和 Kaehler 的 Learning OpenCV 一书
阈值?
- 最简单的分割方法
- 应用示例:分离出与要分析的对象相对应的图像区域。这种分离基于对象像素和背景像素之间的强度变化。
- 为了将我们感兴趣的像素与其他像素区分开来(最终会被拒绝),我们对每个像素强度值与阈值(根据要解决的问题确定)进行比较。
- 一旦我们正确地分离了重要的像素,我们就可以为它们设置一个确定的值来识别它们(即我们可以为它们分配一个值(0)(黑色)、(255)(白色)或任何适合您需求的值)。
- .
阈值的类型
- OpenCV 提供了函数 cv::threshold 来执行阈值操作。
- 我们可以使用此函数实现 (5) 类型的阈值操作。我们将在以下小节中解释它们。
- 为了说明这些阈值过程是如何工作的,让我们假设我们有一个源图像,其像素的强度值为(src(x,y))下图描绘了这一点。水平蓝线表示阈值 (thresh)(固定)。
阈值二进制
-
此阈值操作可以表示为:
- 因此,如果像素src(x,y) 的强度高于 (thresh),则新像素强度设置为 (MaxVal)否则,像素设置为(0)。
阈值二进制,反转
-
此阈值操作可以表示为:
- 如果像素src(x,y) 的强度高于 thresh,则新像素强度设置为0.否则,它设置为MaxVal
截断
-
此阈值操作可以表示为:
- 像素的最大强度值为 thresh,如果src(x,y)大于此,则其值将被像素的最大强度值为。见下图:
阈值为零
-
此操作可以表示为:
- 如果 src(x,y)低于thresh,则新像素值将设置为 0。
阈值为零,倒置
-
此操作可以表示为:
- 如果src(x,y)大于thresh,则新像素值将设置为0。
演示代码
C++
教程代码如下所示。您也可以从这里下载
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>using namespace cv;
using std::cout;int threshold_value = 0;
int threshold_type = 3;
int const max_value = 255;
int const max_type = 4;
int const max_binary_value = 255;Mat src, src_gray, dst;
const char* window_name = "Threshold Demo";const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
const char* trackbar_value = "Value";static void Threshold_Demo( int, void* )
{/* 0: Binary1: Binary Inverted2: Threshold Truncated3: Threshold to Zero4: Threshold to Zero Inverted*/threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type );imshow( window_name, dst );
}int main( int argc, char** argv )
{String imageName("stuff.jpg"); // by defaultif (argc > 1){imageName = argv[1];}src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an imageif (src.empty()){cout << "Cannot read the image: " << imageName << std::endl;return -1;}cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to GraynamedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display resultscreateTrackbar( trackbar_type,window_name, &threshold_type,max_type, Threshold_Demo ); // Create a Trackbar to choose type of ThresholdcreateTrackbar( trackbar_value,window_name, &threshold_value,max_value, Threshold_Demo ); // Create a Trackbar to choose Threshold valueThreshold_Demo( 0, 0 ); // Call the function to initializewaitKey();return 0;
}
解释
C++
让我们检查一下程序的一般结构:
- 加载图像。如果是 BGR,我们将其转换为灰度。为此,请记住,我们可以使用函数 cv::cvtColor : :
String imageName("stuff.jpg"); // by defaultif (argc > 1){imageName = argv[1];}src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an imageif (src.empty()){cout << "Cannot read the image: " << imageName << std::endl;return -1;}cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray
- 创建一个窗口来显示结果
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results
- 创建2个跟踪栏供用户输入:
- 阈值类型:二进制、归零等...
- 阈值
createTrackbar( trackbar_type,window_name, &threshold_type,max_type, Threshold_Demo ); // Create a Trackbar to choose type of ThresholdcreateTrackbar( trackbar_value,window_name, &threshold_value,max_value, Threshold_Demo ); // Create a Trackbar to choose Threshold value
- 等到用户输入阈值、阈值类型(或程序退出)
- 每当用户更改任何 Trackbar 的值时,都会调用函数 Threshold_Demo(Java 中的 update):
static void Threshold_Demo( int, void* )
{/* 0: Binary1: Binary Inverted2: Threshold Truncated3: Threshold to Zero4: Threshold to Zero Inverted*/threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type );imshow( window_name, dst );
}
如您所见,函数 cv::threshold 被调用。我们在 C++ 代码中给出 (5)个参数:
- src_gray:我们的输入图像
- dst:目标(输出)图像
- threshold_value:进行阈值操作所依据的 \(thresh\) 值
- max_BINARY_value:用于二进制阈值操作的值(用于设置所选像素)
- threshold_type:(5) 阈值操作之一。它们列在上面函数的注释部分。
结果
- 编译此程序后,运行它,将图像的路径作为参数。例如,对于输入图像,如下所示:
- 首先,我们尝试使用反转的二进制阈值来阈值图像。我们预计比 \(thresh\) 更亮的像素会变暗,这就是我们实际发生的情况,正如我们在下面的快照中看到的那样(请注意,从原始图像中可以看出,与图像相比,狗狗的舌头和眼睛特别明亮,这反映在输出图像中)。
- 现在我们尝试将阈值设置为零。这样,我们预计最暗的像素(低于阈值)将完全变为黑色,而值大于阈值的像素将保持其原始值。这由输出图像的以下快照验证:
参考文献:
1、《Basic Thresholding Operations》-----Ana Huamán