此次研究两种图像分割法,分别是基于形态学的分水岭算法和基于图割理论的GrabCut算法。OpenCV均提供了两张算法或其变种。鉴于研究所需,记录一些知识点,开发平台为OpenCV2.4.9+Qt5.3.2。
一、使用分水岭算法进行图像分割
分水岭变换是一种常用的图像处理算法,在网上很容易搜到详细的原理分析。简单来说,这是一种基于拓扑理论的数学形态学的图像分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭算法简单,因此存在一些缺陷,如容易导致图像的过度分割。OpenCV提供了该算法的改进版本,即使用预定义的一组标记来引导对图像的分割,该算法是通过cv::watershed函数来实现的。
#ifndef WATERSHEDSEGMENTATION_H
#define WATERSHEDSEGMENTATION_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
class WaterShedSegmentation
{
public:
void setMarkers(const cv::Mat &markerImage); // 将原图像转换为整数图像
cv::Mat process(const cv::Mat &image); // // 分水岭算法实现
// 以下是两种简化结果的特殊方法
cv::Mat getSegmentation();
cv::Mat getWatersheds();
private:
cv::Mat markers; // 用于非零像素点的标记
};
#endif // WATERSHEDSEGMENTATION_H
#include "watershedsegmentation.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
void WaterShedSegmentation::setMarkers(const cv::Mat &markerImage) // 将原图像转换为整数图像
{
markerImage.convertTo(markers,CV_32S);
}
cv::Mat WaterShedSegmentation::process(const cv::Mat &image)
{
// 使用分水岭算法
cv::watershed(image,markers);
return markers;
}
// 以下是两种简化结果的特殊方法
// 以图像的形式返回分水岭结果
cv::Mat WaterShedSegmentation::getSegmentation()
{
cv::Mat tmp;
// 所有像素值高于255的标签分割均赋值为255
markers.convertTo(tmp,CV_8U);
return tmp;
}
cv::Mat WaterShedSegmentation::getWatersheds()
{
cv::Mat tmp;
markers.convertTo(tmp,CV_8U,255,255);
return tmp;
}
main函数:
#include <QCoreApplication>
#include "watershedsegmentation.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Read input image
cv::Mat image= cv::imread("c:/gray.jpg");
if (!image.data)
return 0;
// Display the image9
cv::namedWindow("Original Image");
cv::imshow("Original Image",image);
// Get the binary map
cv::Mat binary;
binary= cv::imread("c:/gray.jpg",0);
// Display the binary image
cv::namedWindow("Binary Image");
cv::imshow("Binary Image",binary);
// 移除
cv::Mat fg;
cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),6);
// Display the foreground image
cv::namedWindow("Foreground Image");
cv::imshow("Foreground Image",fg);
// Identify image pixels without objects
cv::Mat bg;
cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),6);
cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);
// Display the background image
cv::namedWindow("Background Image");
cv::imshow("Background Image",bg);
// Show markers image
cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
markers= fg+bg;
cv::namedWindow("Markers");
cv::imshow("Markers",markers);
// Create watershed segmentation object
WaterShedSegmentation segmenter;
// Set markers and process
segmenter.setMarkers(markers);
segmenter.process(image);
// Display segmentation result
cv::namedWindow("Segmentation");
cv::imshow("Segmentation",segmenter.getSegmentation());
// Display watersheds
cv::namedWindow("Watersheds");
cv::imshow("Watersheds",segmenter.getWatersheds());
// Open another image
image= cv::imread("../tower.jpg");
// Identify background pixels
cv::Mat imageMask(image.size(),CV_8U,cv::Scalar(0));
cv::rectangle(imageMask,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255),3);
// Identify foreground pixels (in the middle of the image)
cv::rectangle(imageMask,cv::Point(image.cols/2-10,image.rows/2-10),
cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1),10);
// Set markers and process
segmenter.setMarkers(imageMask);
segmenter.process(image);
// Display the image with markers
cv::rectangle(image,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255,255,255),3);
cv::rectangle(image,cv::Point(image.cols/2-10,image.rows/2-10),
cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1,1,1),10);
cv::namedWindow("Image with marker");
cv::imshow("Image with marker",image);
// Display watersheds
cv::namedWindow("Watersheds of foreground object");
cv::imshow("Watersheds of foreground object",segmenter.getWatersheds());
// Open another image
image= cv::imread("../tower.jpg");
// define bounding rectangle
cv::Rect rectangle(50,70,image.cols-150,image.rows-180);
cv::Mat result; // segmentation result (4 possible values)
cv::Mat bgModel,fgModel; // the models (internally used)
// GrabCut segmentation
cv::grabCut(image, // input image
result, // segmentation result
rectangle,// rectangle containing foreground
bgModel,fgModel, // models
1, // number of iterations
cv::GC_INIT_WITH_RECT); // use rectangle
// Get the pixels marked as likely foreground
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
// Generate output image
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
// draw rectangle on original image
cv::rectangle(image, rectangle, cv::Scalar(255,255,255),1);
cv::namedWindow("Image");
cv::imshow("Image",image);
// display result
cv::namedWindow("Segmented Image");
cv::imshow("Segmented Image",foreground);
// Open another image
image= cv::imread("../group.jpg");
// define bounding rectangle
cv::Rect rectangle2(10,100,380,180);
cv::Mat bkgModel,fgrModel; // the models (internally used)
// GrabCut segmentation
cv::grabCut(image, // input image
result, // segmentation result
rectangle2,bkgModel,fgrModel,5,cv::GC_INIT_WITH_RECT);
// Get the pixels marked as likely foreground
// cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
result= result&1;
foreground.create(image.size(),CV_8UC3);
foreground.setTo(cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
// draw rectangle on original image
cv::rectangle(image, rectangle2, cv::Scalar(255,255,255),1);
cv::namedWindow("Image 2");
cv::imshow("Image 2",image);
// display result
cv::namedWindow("Foreground objects");
cv::imshow("Foreground objects",foreground);
return 0;
return a.exec();
}
原文地址:http://blog.csdn.net/liyuefeilong/article/details/43895525