Opencv分水岭算法学习

分水岭算法可以将图像中的边缘转化成“山脉”,将均匀区域转化为“山谷”,这样有助于分割目标。

分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中的每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响区域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。

分水岭的计算过程是一个迭代标注过程。分水岭计算分成两个步骤:一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高的排序,然后在从低到高实现淹没的过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点即为分水岭。显然,分水岭表示的是输入图像的极大值点。

简而言之,分水岭算法首先计算灰度图的梯度,这对图像中的“山谷”或没有纹理的“盆地”(亮度值低的点)的形成是很有效的,也对“山头”或图像中有主导线段的“山脉”(山脊对应的边缘)的形成有效。然后开始从用户指定点(或者算法得到点)开始持续“灌注”盆地直到这些区域连成一片。基于这样产生的标记就可以把区域合并到0一起,合并后的区域又通过聚集的方式进行分割,好像图像被“填充”起来一样。

实现分水岭算法–watershed函数

函数watershed实现的分水岭算法是基于标记的分割算法中的一种。在把图像传给函数之前,需要大致勾画标记出图像中的期望进行分割的区域,它们被标记为正指数,所以,每一个区域都会被标记为像素值1、2、3等,表示成为一个或者多个连接组件,这些标记的值可以使用findContours函数和drawContours函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的“种子”,而没有标记清楚的区域,被置为0,在函数的输出中,每一个标记中的像素被设置为“种子”的值,而区域间的值被设置为-1。
void watershed(inputArray,intputOutputArray markers)
*第一个参数,输入图像,需为8位三通道的彩色图像。
*第二个参数,函数调用后的运算结果存在这里,输入/输入32位单通道图像的标记结果。

#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>

using namespace cv;
using namespace std;
//宏定义
#define WINDOW_NAME "image[procedure window]"

//全局变量声明
Mat g_srcImage,g_maskImage;
Point prevPt(-1,-1);

//全局函数声明
static void on_Mouse(int event,int x,int y,int flags,void*);

//主函数
int main()
{
  //载入源图像
  g_srcImage=imread("/Users/new/Desktop/1.jpg");
  if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}

  //显示源图像
  imshow(WINDOW_NAME,g_srcImage);
  Mat srcImage,grayImage;
  g_srcImage.copyTo(srcImage);
  //灰度化
  cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY);
  //imshow("image[mask]",g_maskImage);
  cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);
  //imshow("image[gray]",grayImage);
  //掩膜图像初始化为0
  g_maskImage=Scalar::all(0);

  //设置鼠标回调函数
  setMouseCallback(WINDOW_NAME, on_Mouse,0);

  //轮询按键处理
  while(1)
  {
    //获取键值
    int c=waitKey(0);
    //若按键为ESC时,退出
    if((char)c == 27)
      break;
    //若按键为2时,恢复原图
    if((char)c=='2')
    {
      g_maskImage=Scalar::all(0);
      srcImage.copyTo(g_srcImage);
      imshow("image",g_srcImage);
    }
    //若按键为1,则进行处理
    if((char)c=='1')
    {
      //定义一些参数
      int i,j,compCount=0;
      vector<vector<Point>>contours;
      vector<Vec4i> hierarchy;
      //寻找轮廓
      findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE);
      //轮廓为空时的处理
      if(contours.empty())
        continue;
      //复制掩膜
      Mat maskImage(g_maskImage.size(),CV_32S);
      maskImage=Scalar::all(0);

      //循环绘制轮廓
      for(int index=0;index>=0;index=hierarchy[index][0],++compCount)
        drawContours(maskImage, contours, index, Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);
        //compCount为零时的处理
        if(compCount==0)
          continue;

        //生成随机颜色
        vector<Vec3b>colorTab;
        for(int i=0;i<compCount;++i)
        {
          int b=theRNG().uniform(0, 255);
          int g=theRNG().uniform(0, 255);
          int r=theRNG().uniform(0, 255);

          colorTab.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r));
        }
        //计算处理时间并输出到窗口中
        double dTime=(double)getTickCount();
        //进行分水岭算法
        watershed(srcImage, maskImage);
        dTime=(double)getTickCount()-dTime;
        printf("\t 处理时间=%gms\n",dTime*1000./getTickFrequency());
        //双层循环,将分水岭图像遍历存入watershedImage中
        Mat watershedImage(maskImage.size(),CV_8UC3);
        for(i=0;i<maskImage.rows;++i)
          for(j=0;j<maskImage.cols;++j)
          {
            int index=maskImage.at<int>(i,j);
            if(index==-1)
              watershedImage.at<Vec3b>(i,j)=Vec3b(255,255,255);//图像变白色
            else if(index<=0||index>compCount)
              watershedImage.at<Vec3b>(i,j)=Vec3b(0,0,0);//图像变黑色
            else
              watershedImage.at<Vec3b>(i,j)=colorTab[index-1];
          }
        //混合灰度图和分水岭效果图并显示最终的窗口
        watershedImage=watershedImage*0.5+grayImage*0.5;
        imshow("image[watershed]",watershedImage);
    }
  }
    return 0;
}

//回调函数定义
void on_Mouse(int event,int x,int y,int flags,void*)
{
  //处理鼠标不在窗口中的情况
  if(x<0||x>=g_srcImage.cols||y<0||y>=g_srcImage.rows)
    return;

  //处理鼠标左键相关消息
  if(event==EVENT_LBUTTONUP||!(flags & EVENT_FLAG_LBUTTON))//按下左键
    prevPt=Point(-1,-1);
  else if(event==EVENT_LBUTTONDOWN)//松开左键
    prevPt=Point(x,y);//鼠标所指的位置

  //鼠标左键按下并移动,绘制出白色线条
  else if(event==EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  {
    Point pt(x,y);
    if(prevPt.x<0)//如果指出去了,返回
      prevPt=pt;
    line(g_maskImage, prevPt, pt, Scalar::all(255),2,8,0);//画白线
    line(g_srcImage,prevPt,pt,Scalar::all(255),2,8,0);//画白线
    prevPt=pt;
    imshow(WINDOW_NAME, g_srcImage);

  }
}


Opencv技巧

(1)计算算法运行时间:

//计算处理时间并输出到窗口中
        double dTime=(double)getTickCount();
        //进行分水岭算法
        watershed(srcImage, maskImage);
        dTime=(double)getTickCount()-dTime;
        printf("\t 处理时间=%gms\n",dTime*1000./getTickFrequency());

(2)改变图像某点像素值:Mat类中的at方法对于获取图像矩阵某点的RGB值或者改变某点的值很方便,

对于单通道的图像:image.at<uchar>(i, j)
对于RGB通道的图像:image.at<Vec3b>(i, j)[0]
         image.at<Vec3b>(i, j)[1]
         image.at<Vec3b>(i, j)[2]

(3)Point(-1,-1)解析:由于卷积过程,图像矩阵要进行填充,Point(-1,-1)即代表卷积开始的位置,这决定了不填充时的结果A处于填充后结果B的位置的那个部分,从(-1,-1)开始卷积的结果是A处于B的正中间那块位置。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • OpenCV图像分割中的分水岭算法原理与应用详解
  • Opencv实现用于图像分割分水岭算法
(0)

相关推荐

  • OpenCV图像分割中的分水岭算法原理与应用详解

    图像分割是按照一定的原则,将一幅图像分为若干个互不相交的小局域的过程,它是图像处理中最为基础的研究领域之一.目前有很多图像分割方法,其中分水岭算法是一种基于区域的图像分割算法,分水岭算法因实现方便,已经在医疗图像,模式识别等领域得到了广泛的应用. 1.传统分水岭算法基本原理 分水岭比较经典的计算方法是L.Vincent于1991年在PAMI上提出的[1].传统的分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一像素的灰度值表示该点的海

  • Opencv实现用于图像分割分水岭算法

    目标 • 使用分水岭算法基于掩模的图像分割 • 学习函数: cv2.watershed() 原理   任何一幅灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷.我们向每一个山谷中灌不同颜色的水,随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝.不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没.我们构建好的堤坝就是对图像的分割.这就是分水岭算法的背后哲理.   但是这种方法通常都会得到过度分割的结果

  • Opencv分水岭算法学习

    分水岭算法可以将图像中的边缘转化成"山脉",将均匀区域转化为"山谷",这样有助于分割目标. 分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中的每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响区域慢慢向外扩展,

  • OpenCV半小时掌握基本操作之分水岭算法

    [OpenCV]⚠️高手勿入! 半小时学会基本操作 ⚠️ 分水岭算法 概述 OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天小白就带大家一起携手走进 OpenCV 的世界. 分水岭算法 分水岭算法 (Watershed Algorithm) 是一种图像区域分割算法. 在分割的过程中, 分水岭算法会把跟临近像素间的相似性作为重要的根据. 分水岭分割流程: 读取图片 转换成灰度图 二值化 距离变换 寻找种子 生成 Marker 分水岭变换 距离变换 距离变换 (Distan

  • python opencv之分水岭算法示例

    本文介绍了python opencv之分水岭算法示例,分享给大家,具体如下: 目标 使用分水岭算法对基于标记的图像进行分割 使用函数cv2.watershed() 原理: 灰度图像可以被看成拓扑平面,灰度值高的区域可以看出山峰,灰度值低的区域可以看成是山谷.向每一个山谷当中灌不同颜色的水.水位升高,不同山谷的水会汇合,为防止不同山谷的水汇合,小在汇合处建立起堤坝.然后继续灌水,然后再建立堤坝,直到山峰都掩模.构建好的堤坝就是图像的分割. 此方法通常会得到过渡分割的结果,因为图像中的噪声以及其他因

  • C++中实现OpenCV图像分割与分水岭算法

    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征. API介绍 void watershed( InputArray image, InputOutputArray markers ); 参数说明: image: 必须是一个8bit 3通道彩色图像矩阵序列 markers: 在执行分水岭函数watershed之前,必须对第二个参数markers

  • OpenCV图像分割之分水岭算法与图像金字塔算法详解

    目录 前言 一.使用分水岭算法分割图像 1.cv2.distanceTransform()函数 2.cv2.connectedComponents()函数 3.cv2.watershed()函数 二.图像金字塔 1.高斯金字塔向下采样 2.高斯金字塔向上采样 3.拉普拉斯金字塔 4.应用图像金字塔实现图像的分割和融合 前言 主要介绍OpenCV中的分水岭算法.图像金字塔对图像进行分割的方法. 一.使用分水岭算法分割图像 分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表

  • python数字图像处理之骨架提取与分水岭算法

    骨架提取与分水岭算法也属于形态学处理范畴,都放在morphology子模块内. 1.骨架提取 骨架提取,也叫二值图像细化.这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示. morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize()函数和medial_axis()函数.我们先来看Skeletonize()函数. 格式为:skimage.morphology.skeletonize(image) 输入和输出都是一幅二值图像. 例1: from s

  • Python 深入了解opencv图像分割算法

    使用 OpenCV 函数 cv::filter2D 执行一些拉普拉斯滤波以进行图像锐化 使用 OpenCV 函数 cv::distanceTransform 以获得二值图像的派生(derived)表示,其中每个像素的值被替换为其到最近背景像素的距离 使用 OpenCV 函数 cv::watershed 将图像中的对象与背景隔离 加载源图像并检查它是否加载没有任何问题,然后显示它: # Load the image parser = argparse.ArgumentParser(descript

  • Opencv Hough算法实现图片中直线检测

    本文实例为大家分享了Opencv Hough算法实现直线检测的具体代码,供大家参考,具体内容如下 (1)载入需检测的图及显示原图 Mat g_srcImage = imread("C:\\Users\\lenovo\\Pictures\\Saved Pictures\\Q.jpg"); //图片所放路径 imshow("[原始图]", g_srcImage); (2)为显示不同的效果图而设置滑动条 namedWindow("[效果图]", 1);

随机推荐