因为要对生成的深度图像和深度视频进行处理,所以最近研究着如何对图像进行滤波和分割,查阅了很多深度视频处理的论文,决定先用meanshift 算法进行图像的聚类分割,通过分割划定区域,然后对生成深度图进行空洞补全和边缘修复,具体细节操作还在研究中。若有做相关方面的同仁可以私信交流!共同进步。
下面介绍下meanshift 算法函数的应用。
Meanshift不仅可以用于图像滤波,视频跟踪,还可以用于图像分割。
pyrMeanShiftFiltering可知,这里是将meanshift算法和图像金字塔相结合用来分割的,所以其参数列表中就有一个专门定义所需金字塔层数的变量。
PyrMeanShiftFiltering函数解释:
void cvPyrMeanShiftFiltering( const CvArr* src, CvArr* dst, double sp, double sr, int max_level=1, CvTermCriteria termcrit=cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,5,1));
- src
- 输入的8-比特,3-信道图象.
- dst
- 和源图象相同大小,相同格式的输出图象.
- sp
- The spatial window radius.
- 空间窗的半径
- sr
- The color window radius.
- 色彩窗的半径
- max_level
- Maximum level of the pyramid for the segmentation.
达到将图像与背景分离的目的。 简单来说,基于Mean Shift的图像分割过程就是首先利用Mean Shift算法对图像中的像素进行聚类,
即把收敛到同一点的起始点归为一类,然后把这一类的标号赋给这些起始点,同时把包含像素点太少的类去掉。
然后,采用阈值化分割的方法对图像进行二值化处理 基于Mean Shift的图像分割算法将图像中灰度值相近的像素点聚类为一个灰度级,
因此,经过Mean Shift算法分割后的图像中的灰度级较该算处理有所减少。
一般而言一副图像的特征点至少可以提取出5维,即(x,y,r,g,b),众所周知,meanshift经常用来寻找模态点,即密度最大的点。所以这里同样可以用它来寻找这5维空间的模态点,由于不同的点最终会收敛到不同的峰值,所以这些点就形成了一类,这样就完成了图像分割的目的,有点聚类的意思在里面。
有一点需要注意的是图像像素的变化范围和坐标的变化范围是不同的,所以我们在使用窗口对这些数据点进行模态检测时,需要使用不同的窗口半径。因此在opencv自带的meanshift分割函数pyrMeanShiftFiltering()函数中,就专门有2个参数供选择空间搜索窗口半径和颜色窗口搜索半径的。
// meanshift9.4.cpp : 定义控制台应用程序的入口点。
//#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
Mat src, dst;
int spr = 10, scr = 10, maxPryLevel = 3;
//const Scalar& colorDiff=Scalar::all(1);
void meanshift_seg(int, void *)
{
//调用meanshift图像金字塔进行分割
pyrMeanShiftFiltering(src, dst, spr,scr, maxPryLevel);
RNG rng = theRNG();
Mat mask(dst.rows + 2, dst.cols + 2, CV_8UC1, Scalar::all(0));
for (int i = 0; i<dst.rows; i++) //opencv图像等矩阵也是基于0索引的
for (int j = 0; j<dst.cols; j++)
if (mask.at<uchar>(i + 1, j + 1) == 0)
{
Scalar newcolor(rng(256), rng(256), rng(256));
floodFill(dst, mask, Point(j, i), newcolor, 0, Scalar::all(1), Scalar::all(1));//注意这里的 Point(j, i)的位置不要搞错,否则滑动条将不能正常化动
//原因是Point中的参数为x,y,对应到图片的矩阵里是列、行,而这段代码中遍历像素点时用的i,j,表示 //需要调换行、列,因此两者正好相反, 对于Mat的对象src,src.at(y.x)与src.at(Point(x,y))是等价的
// floodFill(dst,mask,Point(i,j),newcolor,0,colorDiff,colorDiff);}
imshow("dst", dst);
}
int main(int argc, uchar* argv[])
{
namedWindow("src", WINDOW_AUTOSIZE);
namedWindow("dst", WINDOW_AUTOSIZE);
src = imread("stuff.jpg");
CV_Assert(!src.empty());
spr = 24;
scr = 30;
maxPryLevel = 3;
//虽然createTrackbar函数的参数onChange函数要求其2个参数形式为onChange(int,void*)
//但是这里是系统响应函数,在使用createTrackbar函数时,其调用的函数可以不用写参数,甚至
//括号都不用写,但是其调用函数的实现过程中还是需要满足(int,void*)2个参数类型
createTrackbar("spatialRad", "dst", &spr, 80, meanshift_seg);
createTrackbar("colorRad", "dst", &scr, 80, meanshift_seg);
createTrackbar("maxPryLevel", "dst", &maxPryLevel, 5, meanshift_seg);
// meanshift_seg(0,0);
imshow("src", src);
/*char c=(char)waitKey();
if(27==c)
return 0;*/
imshow("dst", src);
waitKey();//无限等待用户交互响应
// while(1);//这里不能用while(1)的原因是需要等待用户的交互,而while(1)没有该功能。虽然2者都有无限等待的作用。
return 0;
}