OpenCV2学习笔记(四):两种图像分割方法比较

此次研究两种图像分割法,分别是基于形态学的分水岭算法和基于图割理论的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();
}
时间: 2024-09-28 14:24:17

OpenCV2学习笔记(四):两种图像分割方法比较的相关文章

Java学习笔记——线程两种常用的创建调用方法

这是两种开发中常用的线程使用方法,匿名对象调用即可,很简单,掌握即可 <span style="font-size:18px;">class ThreadDemo { public static void main(String[] args) { new Thread() { public void run() { //coding here } }.start(); Runnable r = new Runnable() { public void run() { //

LIS学习笔记(两种算法)

2017-09-02 10:34:21 writer:pprp 最长上升子序列,具体分析看代码:O(n^2)的做法,dp的思想 代码如下: /* @theme:LIS最长上升子序列 @writer:pprp @begin:10:00 @end:10:15 @declare复杂度为O(n^2) @error:dp[i] = MAX(dp[j]+1,dp[i]),dp[i] = 1初始化为1 @date:2017/9/2 */ #include <bits/stdc++.h> using name

16jquery学习笔记-------动画----两种层显示和隐藏slide、fade

1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head> 3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 4 <title></title> 5 <script src="jquery-1.8.3.js"><

uboot学习之二----主Makefile学习之四----两种编译方法:原地编译和单独输出文件夹编译

第57-123行: 57 # 58 # U-boot build supports producing a object files to the separate external 59 # directory. Two use cases are supported: 60 # 61 # 1) Add O= to the make command line 62 # 'make O=/tmp/build all' 63 # 64 # 2) Set environement variable

Caliburn.Micro学习笔记(四)----IHandle&lt;T&gt;实现多语言功能

Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能 说一下IHandle<T>实现多语言功能 因为Caliburn.Micro是基于MvvM的UI与codebehind分离, binding可以是双向的所以我们想动态的实现多语言切换很是方便今天我做一个小demo给大家提供一个思路 先看一下效果 点击英文  变成英文状态点chinese就会变成中文                          源码的下载地址在文章的最下边 多语言用的是资源文件建

NLTK学习笔记(四):自然语言处理的一些算法研究

自然语言处理中算法设计有两大部分:分而治之 和 转化 思想.一个是将大问题简化为小问题,另一个是将问题抽象化,向向已知转化.前者的例子:归并排序:后者的例子:判断相邻元素是否相同(与排序). 这次总结的自然语言中常用的一些基本算法,算是入个门了. 递归 使用递归速度上会受影响,但是便于理解算法深层嵌套对象.而一些函数式编程语言会将尾递归优化为迭代. 如果要计算n个词有多少种组合方式?按照阶乘定义:n! = n*(n-1)*...*1 def func(wordlist): length = le

【Unity 3D】学习笔记四十一:关节

关节 关节组件可以添加至多个游戏对象中,而添加关节的游戏对象将通过关节连接在一起并且感觉连带的物理效果.需要注意的是:关节必须依赖于刚体组件. 关节介绍 关节一共分为5大类:链条关节,固定关节,弹簧关节,角色关节和可配置关节. 链条关节(hinge joint):将两个物体以链条的形式绑在一起,当力量大于链条的固定力矩时,两个物体就会产生相互的拉力. 固定关节(fixed joint):将两个物体永远以相对的位置固定在一起,即使发生物理改变,它们之间的相对位置也将不变. 弹簧关节(spring

【Unity 3D】学习笔记四十三:布料

布料 布料是特殊的组件,它可以变化成任意形状,比如说:随风飘的旗子,窗帘等 创建布料的方法有两种:创建布料对象,在游戏对象中添加布料组件.前者通过hierarchy视图中选择create--cloth即可,创建后,系统会自动将互动布料组件(interactive clothe)与布料渲染组件(cloth renderer)添加值该对象中.后者是在导航菜单中选component--physics--interactive cloth菜单项即可. 交互布料组件是由网格组成的布料,只要用于布料的逻辑判

Go语言学习笔记(四) [array、slices、map]

日期:2014年7月22日 一.array[数组] 1.定义:array 由 [n]<type> 定义,n 标示 array 的长度,而 <type> 标示希望存储的内容的类型. 例如: var arr[10] int arr[0] = 1 arr[1] = 2 数组值类型的:将一个数组赋值给 另一个数组,会复制所有的元素.另外,当向函数内传递一个数组的时候,它将获得一个数组的副本,而不是数组的指针. 2.数组的复合声明.a :=[3]int{1,2,3}或简写为a:=[...]i