1. 目标
在本文中,将学习如何:
- 使用 OpenCV 函数cv::threshold执行基本阈值操作
2. 理论
-
笔记
下面的解释来自Bradski 和 Kaehler的《Learning OpenCV》一书。
3. 阈值
-
最简单的分割方法
-
应用示例:分离出感兴趣的图像区域。这种分离基于对象像素和背景像素之间的强度变化。
-
为了感兴趣的像素与其余像素区分开来,将每个像素强度值与阈值(根据要解决的问题确定)进行比较。
-
一旦正确地分离了重要的像素,就可以给它们设置一个确定的值来识别它们(即可以为它们分配一个值0(黑色的),255(白色)或任何满足需要的值)。
3.1 阈值类型
-
OpenCV 提供函数cv::threshold来执行阈值操作。
-
使用此函数可以完成5种阈值操作类型。将在以下小节中解释它们。
-
为了说明这些阈值处理是如何工作的,假设有一个源图像,其中的像素具有强度值src(x,y). 下图描述了这一点。水平蓝线代表阈值。
3.1.1 阈值二值化
-
这个阈值操作可以表示为:
-
所以,如果像素的强度src(x,y)高于阈值,则新的像素强度设置为maxVal. 否则,像素设置为0.
3.1.2 反向阈值二值化
- 这个阈值操作可以表示为:
-
如果像素的强度src(x,y)高于阈值,则新的像素强度设置为0. 否则设置为maxVal.
3.1.3 截断
-
这个阈值操作可以表示为:
-
像素的最大强度值为阈值, 如果src(x,y)比阈值大,则其值被截断。见下图:
3.1.4 阈值为零
-
该操作可以表示为:
-
如果src(x,y)低于阈值,新的像素值将被设置为0.
3.1.5 反向阈值为零
-
该操作可以表示为:
-
如果src(x,y)大于时间阈值,新的像素值将被设置为0.
4. 代码
代码如下所示。可以从这里下载
#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: Binary
1: Binary Inverted
2: Threshold Truncated
3: Threshold to Zero
4: 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 default
if (argc > 1)
{
imageName = argv[1];
}
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
if (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
createTrackbar( trackbar_type,
window_name, &threshold_type,
max_type, Threshold_Demo ); // Create a Trackbar to choose type of Threshold
createTrackbar( trackbar_value,
window_name, &threshold_value,
max_value, Threshold_Demo ); // Create a Trackbar to choose Threshold value
Threshold_Demo( 0, 0 ); // Call the function to initialize
waitKey();
return 0;
}
5. 代码解释
下边查看以下程序的主干:
- 加载图像。如果是 BGR,将其转换为灰度。使用函数cv::cvtColor:
String imageName("stuff.jpg"); // by default
if (argc > 1)
{
imageName = argv[1];
}
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
if (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 Threshold
createTrackbar( trackbar_value,
window_name, &threshold_value,
max_value, Threshold_Demo ); // Create a Trackbar to choose Threshold value
- 等到用户输入阈值,阈值的类型(或直到程序退出)
- 每当用户更改任何 Trackbars 的值时,都会调用函数Threshold_Demo(Java 中的update ):
static void Threshold_Demo( int, void* )
{
/* 0: Binary
1: Binary Inverted
2: Threshold Truncated
3: Threshold to Zero
4: Threshold to Zero Inverted
*/
threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type );
imshow( window_name, dst );
}
函数cv::threshold被调用,这个函数有5个参数:
- src_gray : 输入图像
- dst:目标(输出)图像
- threshold_value:阈值
- max_BINARY_value:与二值化阈值操作一起使用的值(用于设置所选像素)
- threshold_type:5种阈值操作。它们列在上面函数的注释部分。
6. 结果
-
编译并运行,将图像的路径作为参数。例如,对于输入图像:
-
首先,尝试使用反向二值阈值。比阈值高的像素会变暗,在下面的快照中所见(从原始图像中可以看出,与图像相比,小狗的舌头和眼睛特别亮,所以会变暗,这反映在输出图像中)。
-
现在尝试将阈值设为零。有了这个,最暗的像素(低于阈值)将完全变黑,而值大于阈值的像素将保持其原始值。这通过以下输出图像的快照得到验证: