转自:Tiger & Pi

http://blog.163.com/my_645/blog/static/369785222013310619742/

Watershed就是传说中的分水岭算法, 它将一幅图像看成是一块有湖泊和山川组成的地形。 图像灰度值大的像素对应海拔高的山地, 灰度值低的像素对应于海拔低的盆地。Watershed分割是模拟湖水上涨并在湖泊相遇处筑坝的过程。一般水是从湖泊的最低处灌进去,最低点对应于图像的局部最低点。 但确定局部最低点的自动话算法得到的结果往往不尽如人意, 所以常常要手动指定marker点。

函数原型

void cvWatershed(Iplimage *src_image, CvArr* markers)

下面的代码通过交互式的方式演示了watershed的过程,先在图像的不同区域画线,然后按w键运行算法。 下图左边是原图和指定的markers, 右边是分割的mask图像。

/************************************************************************/

/* 下面这一堆代码真正的分水岭代码在cvWatershed( img0, markers )函数里面,其它

归纳起来为:实现鼠标标记图像,制作markers标记图像,实现颜色填充效果。 */

/************************************************************************/

//

// ch9_watershed image

// This is an exact copy of the watershed.cpp demo in the OpenCV ../samples/c directory

//

// Think about using a morphologically eroded foreground and background segmented image as the template

// for the watershed algorithm to segment objects by color and edges for collecting

//

#include "stdio.h"

#include "cv.h"

#include "highgui.h"

#include

#include

IplImage* marker_mask = 0;

IplImage* markers = 0;

IplImage* img0 = 0, *img = 0, *img_gray = 0, *wshed = 0;

CvPoint prev_pt = {-1,-1};

void on_mouse(int event, int x, int y, int flags, void* param)

{

if(!img)

return;

if(event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON))

// 如果鼠标左键弹起或鼠标左键没有按下

prev_pt = cvPoint(-1,-1);

else if(event == CV_EVENT_LBUTTONDOWN)

// 如果鼠标左键按下

prev_pt = cvPoint(x,y);

else if(event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))

// 如果鼠标移动且左键按下

{

CvPoint pt = cvPoint(x,y);

if( prev_pt.x < 0 )

prev_pt = pt;

cvLine(marker_mask, prev_pt, pt, cvScalarAll(255), 5, 8, 0);

// 实际标记 marker_mask 才会被算法用到

cvLine(img, prev_pt, pt, cvScalarAll(255), 5, 8, 0);

// img标记只便于用户观察

prev_pt = pt;

cvShowImage("image", img);

}

}

int main()

{

printf( "Hot keys: \n"

"\tESC - quit the program\n"

"\tr - restore the original image\n"

"\tw or ENTER - run watershed algorithm\n"

"\t\t(before running it, roughly mark the areas on the image)\n"

"\t (before that, roughly outline several markers on the image)\n");

char* filename = "lena.BMP";

CvRNG rng = cvRNG(-1);

// 定义一个随机化生成器并初始化为-1,配合下面cvRandInt(&rng)生成随机数,现在知道为什么会变颜色了吧

if((img0 = cvLoadImage(filename,1)) == 0)

return 0;

cvNamedWindow("image", 1);

cvNamedWindow("watershed transform", 1);

img = cvCloneImage(img0);

// 用于显示的原图像

img_gray = cvCloneImage(img0);

// 用于和分割出的颜色块进行混合

wshed = cvCreateImage(cvGetSize(img), 8, 3);

// 用于存储分割出的颜色块和最后的效果图

marker_mask = cvCreateImage(cvGetSize(img), 8, 1);

// 用于记录用户标记区域的画布,并在此基础上制作用于分水岭算法使用的markers

markers = cvCreateImage(cvGetSize(img), IPL_DEPTH_32S, 1);

cvCvtColor(img, marker_mask, CV_BGR2GRAY);

cvCvtColor(marker_mask, img_gray, CV_GRAY2BGR);

cvZero(marker_mask);

cvZero( wshed );

cvShowImage( "image", img );

cvShowImage( "watershed transform", wshed );

cvSetMouseCallback("image", on_mouse, 0);

// 从这儿开始实现鼠标标记功能,具体可查ICVL

for(;;)

{

char c = cvWaitKey();

if( c == 27 )

break;

if( c == 'r' )

{

cvZero(marker_mask);

cvCopy(img0, img);

cvShowImage("image", img);

}

if(c == 'w')

{

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* contours = 0;

int comp_count = 1;

// 粗看以为这是记录轮廓数目呢,其实不然,他将把每个轮廓设为同一像素值

cvFindContours( marker_mask, storage, &contours, sizeof(CvContour),

CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

cvZero( markers );

for( ; contours != 0; contours = contours->h_next, comp_count++)

{

cvDrawContours( markers, contours, cvScalarAll(comp_count+1),

cvScalarAll(comp_count+1), -1, -1, 8, cvPoint(0,0) );

}

// 上面这些最后得到markers 将会是一些数值块,每个轮廓区域内都有同一像素值

// 到此时Watershed 终于得到了它如饥似渴的 markers 这个markers 中记录了刚刚

// 用户用鼠标勾勒的感兴趣区域

CvMat* color_tab;

color_tab = cvCreateMat(1, comp_count, CV_8UC3);

// 构造一个一维8bit无符号3通道元素类型的矩阵,用来记录一些随机的颜色

for(int i = 0; i < comp_count; i++)

{

uchar* ptr = color_tab->data.ptr + i*3;

ptr[0] = (uchar)(cvRandInt(&rng)%180 + 50);

ptr[1] = (uchar)(cvRandInt(&rng)%180 + 50);

ptr[2] = (uchar)(cvRandInt(&rng)%180 + 50);

}

{// 千呼万唤始出来的cvWatershed

double t = (double)cvGetTickCount();

cvWatershed(img0, markers);

t = (double)cvGetTickCount() - t;

printf( "exec time = %gms\n", t/(cvGetTickFrequency()*1000.) );

// 上面的t用来计算此算法运行时间

/************************************************************************/

/* markers中包含了一些用户感兴趣的区域,每个区域用1、2、3。。一些像素值标注,经过

此算法后,markers会变成什么样呢?要知道markers中标注的只是用户用鼠标轻描淡写的

一些区域,把这些区域想像成一些湖泊,如果只有一个区域,则代表整幅图将会被这一个

湖泊淹没,上面color_tab 正是用来记录每个湖泊的颜色。如果用户标注了两个区域,则

湖泊会沿着这两个区域蔓延,直到把图片分成两个湖泊,这两个湖泊不是无规律的,而是

尽可能把图像的轮廓分隔开。如标注多个区域,则将形成多种颜色的湖泊,此算法会把把

每个湖泊的分水岭赋为 -1,即用来分隔这些湖泊,下面图片展示了这些湖泊把整幅图都分

隔开了 */

/************************************************************************/

}

// paint the watershed image

for(int i = 0; i < markers->height; i++)

for(int j = 0; j < markers->width; j++)

{

int idx = CV_IMAGE_ELEM( markers, int, i, j );

// idx得到了markers 在(i, j)坐标的的像素值,这个值对应color_tab中的一种颜色

// 因为markers 中的像素值就是用1-comp_count 的像素值标注的

uchar* dst = &CV_IMAGE_ELEM( wshed, uchar, i, j*3 );

// dst得到了wshed图像 (i, j)像素数据的首地址,因为乘3是因为3通道

if( idx == -1 )

// 在wshed图像中将markers 中得到的分水岭标记为白色,原先-1将显示黑色

dst[0] = dst[1] = dst[2] = (uchar)255;

else if( idx <= 0 || idx > comp_count )

dst[0] = dst[1] = dst[2] = (uchar)0; // should not get here

else

{

uchar* ptr = color_tab->data.ptr + (idx-1)*3;

// 指向idx 所对应的颜色通道,这些颜色是上面随机生成的

dst[0] = ptr[0]; dst[1] = ptr[1]; dst[2] = ptr[2];

// 把对应的像素值赋给wshed 图像

}

}

cvAddWeighted( wshed, 0.5, img_gray, 0.5, 0, wshed );

// 可以注释掉看下效果

cvShowImage( "watershed transform", wshed );

cvReleaseMemStorage( &storage );

cvReleaseMat( &color_tab );

}

}

return 1;

}

opencv图像分割合成_OpenCV图像分割-watershed相关推荐

  1. opencv图像分割合成_opencv 金字塔图像分割

    我所知的opencv中分割函数:watershed(只是看看效果,不能返回每类pixel类属),cvsegmentImage,cvPyrSegmentation(返回pixel类属) 金字塔分割原理篇 ...

  2. 基于边缘的图像分割——分水岭算法(watershed)算法分析(附opencv源码分析)

    最近需要做一个图像分割的程序,查了opencv的源代码,发现opencv里实现的图像分割一共有两个方法,watershed和mean-shift算法.这两个算法的具体实现都在segmentation. ...

  3. Python+OpenCV:基于分水岭算法的图像分割(Image Segmentation with Watershed Algorithm)

    Python+OpenCV:基于分水岭算法的图像分割(Image Segmentation with Watershed Algorithm) ############################ ...

  4. 传统图像分割——分水岭算法(watershed)

    传统图像分割--分水岭算法(watershed) 文章目录 传统图像分割--分水岭算法(watershed) 前言 一.什么是分水岭算法? 二.经典的分水岭求解算法 1.定义 2.算法流程 总结 前言 ...

  5. python医学图像分割_医学图像分割如何入门?

    既然是入门,搞明白下文术语的意思就入门了,把术语搜索搜索就差不多了. 提升嘛,就把相关[最原始]论文看看,用opencv或者scimage跑跑算法看看效果. 看论文一定是看最原始论文就够了,至于对原始 ...

  6. OpenCV分水岭分割函数:watershed()介绍

    OpenCV分水岭分割函数:watershed()介绍

  7. cv2.threshold() 阈值:使用Python,OpenCV进行简单的图像分割

    图像分割有多种形式. 聚类.压缩.边缘检测.区域增长.图分区.分水岭等等:(Clustering. Compression. Edge detection. Region-growing. Graph ...

  8. OpenCV(26)图像分割 -- 距离变换与分水岭算法(硬币检测、扑克牌检测、车道检测)

    目录 一.基础理论 1.思想 2.原理 二.分水岭实战:硬币 步骤归纳 1.把原图像转二值图 2.开运算去噪 3.确定背景区域(膨胀)(得到背景/最大连通域) 4.确定前景区域(距离变换) (分离)( ...

  9. python图像分割算法_Opencv(二)—图像分割之分水岭算法!

    做图像处理时,我们可能会遇到一个问题:我们只需要图片的一部分区域,如何把图片中的某部分区域提取出来 或者 图像想要的区域用某种颜色(与其它区域颜色不一致)标记起来 ,以上描述的问题在像处理领域称为 图 ...

最新文章

  1. jvm在不同系统中的最大内存空间地址
  2. OpenCV使用霍夫变换进行寻线的实例(附完整代码)
  3. [家里蹲大学数学杂志]第045期布朗运动矩的计算
  4. nodejs 定时 mysql_nodejs 使用 mysql
  5. Java 线程池框架核心代码分析
  6. android自动布局优先级,自动布局AutoLayout
  7. mean shift 图像分割(一、二、三)
  8. 是时候该了解一波Protocol Buffers了[Java]
  9. 卸载列表信息——Uninstall注册表
  10. android9.0 framewrok.jar push到system/framework不起作用,解决方式
  11. 十大ERP系统排行榜—2022年
  12. docker搭建LDAP统一用户认证
  13. 【FPGA黑金开发板】Verilog HDL那些事儿--串口模块(十一)
  14. 美团外卖离线数仓建设实践
  15. android语音到账,支付宝到账语音包
  16. 如何在word2003中打开word2007格式的文件
  17. java.lang.ClassNotFoundException: org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEnd
  18. 图计算思维与实践 (一)概览
  19. 一篇文章带你快速上手Airtest和Poco
  20. 所需即所获:IDE = _plugins_ + vim

热门文章

  1. DIRECT3D状态详解
  2. 微信小程序 web-view 在ios显示空白页面
  3. 织梦如何去掉dedecms
  4. 从输入 URL 到页面加载完成的过程中都发生了什么事情?
  5. 版本更新 | 极狐 GitLab 15.2 发布飞书通知机器人、多层史诗调整至专业版、实时 Wiki 图表预览和全新设计的合并请求报告
  6. 联想 thindBook 13s G2 ITL笔记本开不了机问题
  7. 百度ECharts插件 立体地图阴影实现带轨迹连接
  8. luogu P2887 [USACO07NOV]防晒霜Sunscreen
  9. 汽车行业中XCP协议和A2L文件–XCP概述
  10. 纳米数据,足球篮球实时数据比分,体育赛事比分接口代码,实时数据推送演示