图像分割指的是将数字图像细分为多个图像子区域的过程,在OpenCv中实现了三种跟图像分割相关的算法,它们分别是:金字塔分割算法,分水岭分割算法以及均值漂移分割算法。它们的使用过程都很简单,刚开始学习opencv,先记录一下我对金字塔分割原理的理解吧。
金字塔分割算法
金字塔分割算法由cvPrySegmentation所实现,该函数的使用还是比较简单;需要注意的是图像的尺寸以及金字塔的层数,图像的宽度和高度必须能被2整除,能够被2整除的次数决定了金字塔的最大层数。下面的代码演示了如何校验金字塔层数:
/// <summary> /// 当改变金字塔分割的参数“金字塔层数”时,对参数进行校验 /// </summary> private void txtPSLevel_TextChanged(object sender, EventArgs e){ int level = int.Parse(txtPSLevel.Text); if (level < 1 || imageSource.Width % (int)(Math.Pow(2, level - 1)) != 0 || imageSource.Height % (int)(Math.Pow(2, level - 1)) != 0){ MessageBox.Show(this, "输入的金字塔层数不符合要求,计算结果可能会无效。", "金字塔层数错误!") }
opencv中有分装好的函数库,
void pyrMeanShiftFiltering( IplImage* src, IplImage* dst,CvMemStorage* storage, CvSeq** comp,int level, double threshold1, double threshold2 );
src: 输入图像;dst: 输出图像;storage ;Storage:存储连通部件的序列结果 ;comp :分割部件的输出序列指针 components.
level:建立金字塔的最大层数 ;threshold1 :建立连接的错误阈值 ;threshold2 :分割簇的错误阈值 。
函数 cvPyrSegmentation 实现了金字塔方法的图像分割。
金字塔建立到 level 指定的最大层数。
如果 p(c(a),c(b))<threshold1,则在层 i 的象素点 a 和它的相邻层的父亲象素 b 之间的连接被建立起来, 定义好连接部件后,它们被加入到某些簇中。
如果p(c(A),c(B))<threshold2,则任何两个分割 A 和 B 属于同一簇。
如果输入图像只有一个通道,那么 p(c1,c2)=|c1-c2|. 如果输入图像有单个通道(红、绿、兰),那幺 p(c1,c2)=0,3·(c1r-c2r)+0,59·(c1g-c2g)+0,11·(c1b-c2b) . 每一个簇可以有多个连接部件。
图像 src 和 dst 应该是 8-比特、单通道 或 3-通道图像,且大小一样 Threshold1,Threshold2的解读:金字塔分割先通过p(c(a),c(b))<threshold1在像素a,b...中找连通域,也就是所谓的连接部件A,B...
第二步通过p(c(A),c(B))<threshold2判断两个联通与是否属于同一个簇,簇即使我们最后得到的分割结果。
这是一个测试例子:
#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 spatialRad , colorRad , maxPryLevel; //空间,颜色,金字塔层数 void meanshift_seg() { ////调用meanshift图像金字塔进行分割 pyrMeanShiftFiltering(src, dst, spatialRad, colorRad, 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)); } imshow("结果图像", dst); } void meanshift_seg_s(int i, void *) //控制滑动块 { spatialRad = i; meanshift_seg(); } void meanshift_seg_c(int i, void *) { colorRad = i; meanshift_seg(); } void meanshift_seg_m(int i, void *) { maxPryLevel = i; meanshift_seg(); } int main(int argc, uchar* argv[]) { namedWindow("载入原图像", WINDOW_AUTOSIZE); namedWindow("结果图像", WINDOW_AUTOSIZE); src = imread("../pict1.jpg"); CV_Assert(!src.empty()); spatialRad = 10; colorRad = 10; maxPryLevel = 1; createTrackbar("空间度", "结果图像", &spatialRad, 80, meanshift_seg_s); createTrackbar("色度", "结果图像", &colorRad, 255, meanshift_seg_c); createTrackbar("金字塔层级", "结果图像", &maxPryLevel, 5, meanshift_seg_m); imshow("载入原图像", src); imshow("结果图像", src); waitKey(); return 0; }结果图:
可以调节,当金字塔层级越高时候,切割效果越明显。好了,今天就不写了,我去上课了,后面继续。。。
原文地址:http://blog.csdn.net/u012942818/article/details/45840389