OpenCV中通过滑动条阈值分割多通道图像

谁说我不能喝 提交于 2019-12-02 13:39:03

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机器人摄像头获取图像时,白平衡及曝光等参数的设定也至关重要,如果用滑动条的方式去寻找合适的参数,也不失为一种高效的方法!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!