OpenCV图像分割-watershed

转自: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 <stdio.h>
#include <stdlib.h>

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;
}

转载于:https://www.cnblogs.com/April1314/p/3406988.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/421432.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyEclipse下安装MyBatis Generator代码反向生成工具

在http://mybatis.googlecode.com/svn/sub-projects/generator/trunk/eclipse/UpdateSite/下载 features/plugins/里面所有的jar包&#xff0c;新建一个mybatis-generator文件夹&#xff0c;把features跟plugins都丢到mybatis-generator文件夹中&#xff0c;把mybatis-generato…

linux的rootkit工具包,免费Linux杀毒软件Anti-Virus分享

ClamAVClamAV是一个免费的、开源的、通用的Linux系统杀毒工具包。它被用于检测木马&#xff0c;病毒&#xff0c;恶意软件和其他恶意威胁。是邮件网关扫描软件的标准;它支持几乎所有的邮件文件格式。它的主要功能有&#xff1a;它是跨平台的&#xff0c;适用于Linux、Windows和…

无插件,无com组件,利用EXCEL、WORD模板做数据导出(一)

本次随笔主要讲述着工作中是如何解决数据导出的&#xff0c;对于数据导出到excel在日常工作中大家还是比较常用的&#xff0c;那导出到word呢&#xff0c;改如何处理呢&#xff0c;简单的页面导出问题应该不大&#xff0c;但是如果是标准的公文导出呢&#xff0c;要保证其基本格…

linux提示链接层次太多,嵌入式linuxmusic播放器

VLC music player流媒体客户端软件层次结构流媒体网络协议流视频协议是为了在客户端机和服务器之间进行通信而设计的标准化协议。根据它们的功能&#xff0c;网络上传输的流视频相关的协议分为三类。网络层协议&#xff1a;网络层协议提供了基本的网络服务支持。IP就是网络上流…

vc的UI编程PngTextButton控件的适用情况

控件继承自Cbutton。重写了其中的一些方法。适用ui类型&#xff1a;带图片和文字的类型的按钮&#xff0c;其中图片在前面&#xff0c;文字在后面。如下图 文件下载 转载于:https://www.cnblogs.com/songtzu/p/3415601.html

如何使用一个库中不存在的函数

Windows是一个不断发展的系统&#xff0c;很多新的 API 在操作系统更新时更新&#xff0c;而MASM32开发包一般在很长一段时间内都无法及时更新&#xff0c;如果需要在编程中使用新添加的API函数。就得自己动手来更手库文件&#xff0c;下面我就给大家介绍一下如何通过手动的方法…

清除浮动的7种方法

使用display&#xff1a;inline-block会出现的情况&#xff1a; 1.使块元素在一行显示 2.使内嵌支持宽高 3.换行被解析了 4.不设置的时候宽度由内容撑开 5.在IE6,7下步支持块标签 由于inline-block属性换行的时候被解析&#xff08;有间隙&#xff09;故解决方法使用浮动float:…

背后的故事之 - 快乐的Lambda表达式(二)

快乐的Lambda表达式 上一篇 背后的故事之 - 快乐的Lambda表达式&#xff08;一&#xff09;我们由浅入深的分析了一下Lambda表达式。知道了它和委托以及普通方法的区别&#xff0c;并且通过测试对比他们之间的性能&#xff0c;然后我们通过IL代码深入了解了Lambda表达式&#x…

linux用vsc写c语言,vscode写c语言(windows)

用vscode学习c语言。记录vscode配置c语言编译环境。1.安装vscode(版本1.27)2.安装c/c扩展。配置环境变量&#xff0c;以WIN10为例 &#xff0c;此电脑-属性-高级系统设置-环境变量-系统变量-path-添加一条D:\Program Files\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw3…

linux识别UDF文件系统吗,Linux Kernel UDF文件系统本地缓冲区溢出漏洞

发布日期&#xff1a;2012-07-04更新日期&#xff1a;2012-07-05受影响系统&#xff1a;Linux kernel 3.3.x描述&#xff1a;--------------------------------------------------------------------------------BUGTRAQ ID: 54279Linux Kernel是Linux操作系统的内核。Linux K…

程序编译时书写Makefile注意事项一例

在进行程序编译时&#xff0c;可能需要指定一些库的库的路径、头文件的路径&#xff0c;分别使用的参数选项是-L和-I&#xff0c;需要注意的是&#xff1a;需要确保-L和-I后边的内容不为空&#xff0c;否则会出现意想不到的错误&#xff0c;而这种错误比较难以发现&#xff0c;…

把一个数组分为多个数组

/*int[]arr{1,2,5,213,75,42,64,48,21,44,22}; 分为3个数组 第一个数组满足全是偶数 第二个数组满足全是3的倍数 第三个数组满足其他*/ public class E{public static void main(String[]args){int[]arr{1,2,5,213,75,42,64,48,21,44,22};int b0,c0,d0;//遍历数组,得到每个数组…

数组的增删

public class A {public static void main(String[] args) {int[] arr { 1, 2, 5, 23, 64, 9, 87, 99 };// 要求删除23int index -1;int num 23;for (int i 0; i < arr.length; i) {if (num arr[i]) {index i;}}for (int i index 1; i < arr.length; i) {arr[i -…

获取Android版本信息和电话信息

Android的版本信息可以通过android.os.Build获得&#xff0c;电话信息可以通过TelephonyManager获得&#xff0c;代码如下&#xff1a; private void get_infor(){sdk android.os.Build.VERSION.SDK;;release android.os.Build.VERSION.RELEASE;user android.os.Build.USER;…

简单的二维数组

/*找人,找到输出在第几楼第几号房间&#xff08;考虑没有找到的情况&#xff09;*/ /*{{"凤姐","蔡除坤","张洁"},{"姚鸣","芙蓉姐姐"},{"马尔克死","打仲马","肝塞特","高死你"}}…

用c语言程序编写一份试卷,C语言程序设计试题

C语言程序设计试题俗话说&#xff1a;“一份耕耘&#xff0c;一分收获。”耕耘就得付出一定的代价&#xff0c;没有那含辛如苦的“耕耘”&#xff0c;哪能领会到甜人心田的收获?以下是小编为大家搜索整理的C语言程序设计试题&#xff0c;希望能给大家带来帮助!更多精彩内容请及…