1、阈值分割
阈值分割法是一种基于区域的图像分割技术。其基本原理是:通过设定不同的特征阈值,把图像象素点分为若干类。根据图像阈值化算法所依据的信息源,可将阈值化方法分为五类:1) 基于聚类的方法:数据聚类中,总的数据集被划分为属性相似的子类,例如将灰度级聚类成为两部分:前景物体部分和背景部分。2) 基于直方图的方法:在直方图的峰、谷和直方图的圆滑曲线上进行分析。3) 基于熵的方法:熵方法将区域分为背景区域和前景区域,前景区域通常是物体部分(在一些热红外图像中,背景部分是物体) 。该方法是通过最小化一个熵函数来实现的,交叉熵函数包含了介于原图和其二值图像之间的保留信息。4) 基于空间方法:使用概率密度函数模型,考虑全局范围内的像素之间的相似关系。5) 基于自适应方法:局部方法不能决定单一的阈值,自适应阈值依赖于局部图像特点。
这里,我们仅结合OpenCV中的API函数 threshold 来介绍一下阈值化。threshold函数原型如下:
double threshold(InputArray src, OutputArray dst, double thresh,
double maxVal, int thresholdType)
Parameters:
第一个参数: 输入的灰度图像的地址。
第二个参数: 输出图像的地址。
第三个参数: 进行阈值操作时阈值的大小。
第四个参数: 设定的最大灰度值(该参数运用在二进制与反二进制阈值操作中)。
第五个参数: 阈值的类型。
最后一个参数是阈值化类型,函数一共提供了五种类型(图片来自opencv官网)。
1)二进制阈值化,参数值为0.
很好理解,像素值大于阈值设为255,反正设为0;
2)反二进制阈值化
与二进制阈值化类似,只不过大于阈值设为0,反之设为255;
3)截断阈值化
图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变;
4)阈值化为0
大于阈值的像素点不进行任何改变,其余灰度值全部变为0;
5)反阈值化为0
与阈值化类似,大于阈值的像素设为0,其余不做任何改变。
此外,OpenCV中应用极为广泛的阈值化API函数为adaptiveThreshold(自适应阈值化函数),详细用法参考OpenCV文档。
2、Trackbar
OpenCV提供了API函数createTrackbar,这使我们在设置参数的时候可以很方便的同程序交互。creatTrackbar函数原型如下:
int createTrackbar(const string& trackbarname,
const string& winname, int* value,
int count, TrackbarCallback onChange=0, )
第一个参数,const string&类型的trackbarname,表示轨迹条的名字,用来代表我们创建的轨迹条。
第二个参数,const string&类型的winname,填窗口的名字,表示这个轨迹条会依附到哪个窗口上,即对应namedWindow(),创建窗口时填的某一个窗口名。
第三个参数,int* 类型的value,一个指向整型的指针,表示滑块的位置。并且在创建时,滑块的初始位置就是是由该变量当前的值。
第四个参数,int类型的count,表示滑块可以达到的最大位置的值。PS:滑块最小的位置的值始终为0。
第五个参数,TrackbarCallback类型的onChange,首先注意他有默认值0。这是一个指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并且这个函数的原型必须为void Foo(int,void*);其中第一个参数是轨迹条的位置,第二个参数是用户数据(看下面的第六个参数)。如果回调是NULL指针,表示没有回调函数的调用,仅第三个参数value有变化。
第六个参数,void*类型的userdata,他也有默认值0。这个参数是用户传给回调函数的数据,用来处理轨迹条事件。如果使用的第三个参数value实参是全局变量的话,完全可以不去管这个userdata参数。
Demo程序如下:
#include <iostream>
#include <cstring>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace std;
using namespace cv;
// 全局变量定义及赋值
int threshold_type = 3;
int const max_type = 4; //阈值化类型
int threshold_Bvalue = 0; //B通道阈值设定
int threshold_Gvalue = 0; //G通道阈值设定
int threshold_Rvalue = 0; //R通道阈值设定
int const max_value = 255;
int const max_BINARY_value = 255;
Mat src, dst;
//创建一个图像向量
vector<Mat> planes;
char* window_name = "Threshold Func";
char* trackbar_type = "TrackbarType"; //0: Binary 1: Binary Inverted 2: Truncate
//3: To Zero 4: To Zero Inverted
char* trackbar_Bvalue = "B_Value";
char* trackbar_Gvalue = "G_Value";
char* trackbar_Rvalue = "R_Value";
/// 自定义函数声明
void Threshold_Func( int, void* );
int main()
{
//加载一幅图片
src = imread( "test.jpg", 1 );
// 将图片转换成灰度图片
//cvtColor( src, src_gray, CV_RGB2GRAY );
//将多通道图像分割为若干单通道图像
split(src, planes);
// 创建一个窗口显示图片
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
// 创建滑动条来控制阈值
createTrackbar( trackbar_type,
window_name, &threshold_type,
max_type, Threshold_Func );
createTrackbar( trackbar_Bvalue,
window_name, &threshold_Bvalue,
max_value, Threshold_Func );
createTrackbar( trackbar_Gvalue,
window_name, &threshold_Gvalue,
max_value, Threshold_Func );
createTrackbar( trackbar_Rvalue,
window_name, &threshold_Rvalue,
max_value, Threshold_Func );
// 初始化自定义的阈值函数
Threshold_Func( 0, 0 );
// 等待用户按键。如果是ESC健则退出等待过程。
while(true)
{
int c;
c = waitKey( 20 );
if( (char)c == 27 )
{ break; }
}
}
//自定义的阈值函数
void Threshold_Func( int, void* )
{
/* 0: 二进制阈值
1: 反二进制阈值
2: 截断阈值
3: 0阈值
4: 反0阈值
*/
dst.create(src.size(),src.type());
vector<Mat> thredplanes;
split(dst,thredplanes);
threshold( planes[0], thredplanes[0], threshold_Bvalue, max_BINARY_value,threshold_type );
threshold( planes[1], thredplanes[1], threshold_Gvalue, max_BINARY_value,threshold_type );
threshold( planes[2], thredplanes[2], threshold_Rvalue, max_BINARY_value,threshold_type );
//将三个单通道图像重新合并为一个三通道图像
merge(thredplanes,dst);
//显示dst图像
imshow( window_name, dst );
}
运行结果:
程序说明:
1)先读取一副图片,如果是图片颜色类型是BGR三通道类型,则分离为三个单通道图像。
vector<Mat> planes; //创建图像向量,用来存放src分割后的单通道图像
split(src, planes); //分割原始图像为若干单通道图像,split函数原型为
//void split(const Mat& mtx, vector<Mat>& mv)
//对偶运算为void merge(const vector<Mat>& mv, OutputArray dst)
2)创建一个窗口来显示该图片可以检验转换结果
3)创建滑动条。
第一个滑动条作用:选择阈值类型:二进制,反二进制,截断,0,反0。
二、三、四滑动条作用:分别选择BGR通道(分割后)阈值的大小。
4)等待用户拖动滚动条来输入阈值类型以及阈值的大小,或者是用户键入ESC健退出程序。
3、Nao红球识别
我们在Nao机器人的远程环境下进行红球识别,光照等外部环境对识别的结果有很大影响,所以一般将BGR颜色空间转换到HSV空间。无论如何选择颜色空间,进行多通道的阈值分割是保证后续识别的重要步骤。此外,用Nao机器人摄像头获取图像时,白平衡及曝光等参数的设定也至关重要,如果用滑动条的方式去寻找合适的参数,也不失为一种高效的方法!