【从零学习OpenCV 4】分割图像——Mean-Shift分割算法
经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。 |
Mean-Shift算法又被称为均值漂移法,是一种基于颜色空间分布的图像分割算法。该算法的输出是一个经过滤色的“分色”图像,其颜色会变得渐变,并且细纹纹理会变得平缓。
在Mean-Shift算法中每个像素点用一个五维的向量表示,前两个量是像素点在图像中的坐标,后三个量是每个像素点的颜色分量(蓝、绿、红)。在颜色分布的峰值处开始,通过滑动窗口不断寻找属于同一类的像素点并统一像素点的像素值。滑动窗口由半径和颜色幅度构成,半径决定了滑动窗口的范围,即坐标的范围,颜色幅度决定了半径内像素点分类的标准。这样通过不断地移动滑动窗口,实现基于像素点颜色的图像分割。由于分割后同一类像素点具有相同像素值,因此Mean-Shift算法的输出结果是一个颜色渐变、纹理平缓的图像。
OpenCV 4中提供了实现Mean-Shift算法分割图像的pyrMeanShiftFiltering()函数,该函数的函数原型在代码清单8-23中给出。
代码清单8-23 pyrMeanShiftFiltering()函数原型
void cv::pyrMeanShiftFiltering(InputArray src,
OutputArray dst,
double sp,
double sr,
int maxLevel = 1,
TermCriteria termcrit =
TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 5, 1)
8. )
src:待分割的输入图像,必须是三通道CU_8U的彩色图像 dst:分割后的输出图像,与输入图像具有相同的尺寸和数据类型 sp:滑动窗口的半径 sr:滑动窗口颜色幅度 maxLevel:分割金字塔缩放层数 termcrit:迭代算法终止条件。
该函数基于彩色图像的像素值实现对图像的分割,函数的输出结果是经过颜色分布平滑的图像。经过该函数分割后的图像具有较少的纹理信息,可以利用边缘检测函数Canny()以及连通域查找函数findContours()进行进一步细化分类和处理。函数前两个参数是待分割的输入图像和分割后的输出图像,两个图像具有相同的尺寸并且必须是CV_8U的三通道彩色图像。第三个参数为滑动窗口的半径,第四个参数为滑动窗口的颜色幅度。第五个参数为分割金字塔缩放层数,当参数大于1时构建maxLevel + 1层高斯金字塔。该算法首先在尺寸最小的图像层中进行分类,之后将结果传播到尺寸较大的图像层,并且仅在颜色与上一层颜色差异大于滑动窗口颜色幅度的像素上再次进行分类,从而使得颜色区域的边界更清晰。当分割金字塔缩放层数为0时表示直接在整个原始图像时进行均值平移分割。函数最后一个参数表示算法迭代停止的条件,该参数的数据类型是TermCriteria,该数据类型是OpenCV 4中用于表示迭代算法终止条件的数据类型,在所有涉及到迭代条件的函数中都有该参数,用于表示在满足某些条件时函数将停止迭代并输出结果。TermCriteria变量可以通过TermCriteria()函数进行赋值,该函数的函数原型在代码清单8-24中给出。
代码清单8-24 TermCriteria()函数原型
cv::TermCriteria::TermCriteria(int type,
int maxCount,
double epsilon
)
type:终止条件的类型标志,可以选择的参数及含义在表8-6中给出。 maxCount:最大迭代次数或者元素数。 epsilon:迭代算法停止时需要满足的精度或者参数变化。
该函数可以表示迭代算法的终止条件,主要分为满足迭代次数和满足计算精度两种。函数第一个参数是终止条件的类型标志,其可选参数在表8-6中给出,这几个标志可以互相结合使用,需要注意的是,由于该参数在TermCriteria类中,因此在使用时需要在变量前面添类名前缀。函数第二个参数表示最大迭代次数,在epsilon== TermCriteria::COUNT时发挥作用。函数第三个参数表示停止迭代时需要满足的计算精度,在epsilon== TermCriteria::EPS时发挥作用。
表8-6 TermCriteria()函数中终止条件的类型标志可选参数及含义
标志参数 | 简记 | 含义 |
---|---|---|
TermCriteria::COUNT | 1 | 迭代次数达到设定值才停止迭代 |
TermCriteria::MAX_ITER | 1 | 同上 |
TermCriteria::EPS | 2 | 当计算的精度满足要求时停止迭代 |
为了了解pyrMeanShiftFiltering()函数的使用方法以及分割效果,在代码清单8-25中给出了利用该函数进行图像分割的示例程序。程序中对图像连续进行两次处理,并对分割结果提取Canny边缘,比较分割前后对图像边缘的影响,发现经过多次分割处理后的图像边缘明显变少,图像分割区域也更加整齐和平滑,程序的运行结果在图8-15、图8-16和图8-17中给出。
码清单8-25 myPyrMeanShiftFiltering.cpp利用均值漂移法分割图像
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("coins.png");
if (!img.data)
{
cout << "读取图像错误,请确认图像文件是否正确" << endl;
return -1;
}
//分割处理
Mat result1, result2;
TermCriteria T10 = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 0.1);
pyrMeanShiftFiltering(img, result1, 20, 40, 2, T10); //第一次分割
pyrMeanShiftFiltering(result1, result2, 20, 40, 2, T10); //第一次分割的结果再次分割
//显示分割结果
imshow("img", img);
imshow("result1", result1);
imshow("result2", result2);
//对图像提取Canny边缘
Mat imgCanny, result1Canny, result2Canny;
Canny(img, imgCanny, 150, 300);
Canny(result1, result1Canny, 150, 300);
Canny(result2, result2Canny, 150, 300);
//显示边缘检测结果
imshow("imgCanny", imgCanny);
imshow("result1Canny", result1Canny);
imshow("result2Canny", result2Canny);
waitKey(0);
return 0;
}
图8-15 myPyrMeanShiftFiltering.cpp程序中原图及Canny边缘
图8-16 myPyrMeanShiftFiltering.cpp程序中处理一次图像及Canny边缘
图8-17 myPyrMeanShiftFiltering.cpp程序中处理两次图像及Canny边缘