因为现在在做的项目里牵涉到图像分割,这两天一直在找各种资料。终于可以更新了!
先补充点基础知识:
数字图像的质量取决于层次(Hierarchy)、对比度、清晰度。
层次越多视觉效果就越好。
对比度=最大亮度/最小亮度。
在对图像的研究和应用中,人们往往仅对图像中的某些部分感兴趣,这些部分一般称为目标或前景。这就是图像分割的意义啦!
概念:
图像分割就是指根据图像的灰度、颜色、纹理和形状等特征把图像划分成若干互不交叠的区域,并使这些特征在同一区域内呈现出相似性。
换句话说图像分割是基于亮度值的不连续性和相似性。
-不连续性是基于亮度的不连续变化分割图像,如边缘
-根据制定的准则将图像分割为相似的区域,如阈值处理、区域生长、区域分离和合并。
目的:
为了辨识和分析目标,图像分割就是要要把图像分成各有特性的区域并提取出感兴趣目标。
四大类方法:
1、基于阈值的分割方法
以像素性质的分布进行阈值处理。
要有一个可以比较的阈值,可以提前选取好,或者算法自动选取。这个方法主要是基于图像的灰度特征,把图像中每个像素的灰度值都跟阈值比较,得到比较结果后进行分类,相似灰度的像素在一个类中。所以此方法最重要的就是如何选取一个好的阈值。
2、基于边缘的分割方法
边缘处有很多具有明显差异的灰度不连续处,常用间断检测、边缘连接和边界检测。
3、基于区域的分割方法
直接搜寻区域进行分割。主要有种子区域生长法、区域分裂合并法和分水岭法。
4、基于图论的分割方法
此类方法把图像分割问题与图的最小割(min cut)问题相关联。首先将图像映射为带权无向图G=<V,E>,图中每个节点N∈V对应于图像中的每个像素,每条边∈E连接着一对相邻的像素,边的权值表示了相邻像素之间在灰度、颜色或纹理方面的非负相似度。而对图像的一个分割s就是对图的一个剪切,被分割的每个区域C∈S对应着图中的一个子图。而分割的最优原则就是使划分后的子图在内部保持相似度最大,而子图之间的相似度保持最小。基于图论的分割方法的本质就是移除特定的边,将图划分为若干子图从而实现分割。目前所了解到的基于图论的方法有GraphCut,GrabCut和Random Walk等。
今天先给大家介绍经典的图像分割算法-分水岭
函数原型:
void watershed( InputArray image, InputOutputArray markers );
输入的是一个32位的有符号整数标记图,每个非零的像素表示一个标记。我们将图像中已知属于某个区域的像素进行标记。基于这个初始标记,分水岭算法开始确定其他像素的归属区域。
原理:将图像看做拓扑结构的地图,那么均匀区域对应的是被陡峭地区包围的平坦盆地。但这个算法存在过分割问题。
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> //#include "watershedSegmenter.h" class watershedSegmenter { private: //用来表示标记(图) cv::Mat markers; public: void setMarkers(const cv::Mat & markerImage){ //watershed()的输入参数必须为一个32位有符号的标记,所以要先进行转换 markerImage.convertTo(markers,CV_32S); } //执行watershed() cv::Mat process(const cv::Mat &image){ cv::watershed(image,markers); return markers; } //以图像形式返回结果 cv::Mat getSegmentation(){ cv::Mat temp; //从32S到8u(0-255)会进行饱和运算,所以像素高于255 的一律复制为255 markers.convertTo(temp,CV_8U); return temp; } //以图像形式返回分水岭 cv::Mat getWatersheds(){ cv::Mat temp; markers.convertTo(temp,CV_8U,255,255); return temp; } }; int main() { // Read input image 原图 cv::Mat image= cv::imread("E://group.jpg",1); if (!image.data) return 0; // Display the image cv::namedWindow("Original Image"); cv::imshow("Original Image",image); //二值图-像素反转 cv::Mat binary; binary=cv::imread("E://binary.jpg",0); cv::namedWindow("Binary Image"); cv::imshow("Binary Image",binary); //二值图像获得前景。腐蚀,移除噪点与微小物体 cv::Mat foreground; cv::erode(binary,foreground,cv::Mat(),cv::Point(-1,-1),6); cv::namedWindow("foreground Image"); cv::imshow("foreground Image",foreground); //膨胀二值图获取背景 cv::Mat background; cv::dilate(binary,background,cv::Mat(),cv::Point(-1,-1),6); cv::threshold(background,background,1,128,cv::THRESH_BINARY_INV); cv::namedWindow("background Image"); cv::imshow("background Image",background); //形成标记图 cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0)); markers=foreground+background; cv::namedWindow("Markers"); cv::imshow("Markers",markers); //创建分水岭分割对象 watershedSegmenter segmenters; //设置标记并处理 segmenters.setMarkers(markers); segmenters.process(image); cv::namedWindow("Segmenters"); cv::imshow("Segmenters",segmenters.getSegmentation()); cv::namedWindow("watershed"); cv::imshow("watershed",segmenters.getSegmentation()); cv::waitKey(0); }