opencv8-图像分割-分水岭算法
因为现在在做的项目里牵涉到图像分割,这两天一直在找各种资料。终于可以更新了!
先补充点基础知识:
数字图像的质量取决于层次(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);
}