OpenCV库中watershed函数(分水岭算法)的详细使用例程

#

声明:如果有写的不对的地方欢迎指正!

一、分水岭算法

关于分水岭算法的具体原理我就不说了,网上搜一下很多。OpenCV中的watershed函数实现的分水岭算法是基于“标记”的分割算法,用于解决传统的分水岭算法过度分割的问题。试想,一副图片中肯定有N多个“山谷”,它们中的很多是我们不想要的。
对于标记的原则,我总结是:你认为它们属于一个区域,就用标记将它们连接起来,对于另一个区域,再用另一个标记连接。就像这样:
图片中我认为有三个区域,所以做了三个标记。看到这里,你就可以把文章结尾的代码和图片拷到你的工程中试一试效果了。

二、代码分析

要想watershed函数,我们先要做一些准备工作:

1. 做标记

 做标记的原则在上面已经说过了,具体对应代码中on_Mouse函数里面的内容,这是一个鼠标事件回调函数。


    
    
  1. else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  2. {
  3. Point pt(x, y);
  4. if (previousPoint.x < 0)
  5. previousPoint = pt;
  6. //绘制白色线条
  7. line(inpaintMask, previousPoint, pt, Scalar::all( 255), 5, 8, 0);
  8. line(srcImage1, previousPoint, pt, Scalar::all( 255), 5, 8, 0);
  9. previousPoint = pt;
  10. imshow(WINDOW_NAME1, srcImage1);
  11. }

在鼠标左键点按并移动时画线,其中的maskImage是一个CV_8UC1类型的掩模,绘制完的结果就是黑色背景上有几条线(标记),srcImage用于实时显示我们做标记的结果。

2. 寻找轮廓

对我们做过标记的maskImage寻找轮廓,这部分代码写在if ((char)c == ‘1’)中,findContours函数这里不展开说明。

     
     
  1. vector< vector<Point>> contours;
  2. vector<Vec4i> hierarchy;
  3. findContours(maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

3. 绘制轮廓

这里我们声明了一个CV_32S类型的Mat用于绘制轮廓,然后作为watershed的第二个参数传入。对于drawContours()函数的color参数,我们用的是Scalar::all(index + 1),也就是1,2,3这样的数,后面的代码我们会根据这些数绘制可以显示的图像。

     
     
  1. for (int index = 0; index < contours.size(); index++)
  2. drawContours( maskWaterShed, contours, index, Scalar::all( index + 1), -1, 8, hierarchy, INT_MAX);

4. 分水岭算法分割

下面就是调用OpenCV中的watershed函数进行分割
watershed(srcImage_, maskWaterShed);
     
     
注意它的两个参数:srcImage_是没做任何修改的原图,CV_8UC3类型;
maskWaterShed声明为CV_32S类型(32位单通道),全部元素为0,然后作为drawContours的第一个参数传入
(第3步),在上面绘制轮廓,最后作为watershed的参数。另外,参数maskWaterShed是InputOutputArray类型,
作为输入,也作为输出保存函数调用的结果。
经过watershed函数的处理,不同区域间的值被置为-1(边界)没有标记清楚的区域被置为0,其他每个区域
的值保持不变:1,2,…,contours.size()。

5. 绘制结果图像

由于watershed的结果中只有-1,0,1,2这样的数,不能直接显示,所以我们还要做进一步的处理将结果显示出来

     
     
  1. Mat resImage = Mat(srcImage.size(), CV_8UC3);   // 声明一个最后要显示的图像
  2. for ( int i = 0; i < maskImage.rows; i++)
  3. {
  4. for ( int j = 0; j < maskImage.cols; j++)
  5. { // 根据经过watershed处理过的maskWaterShed来绘制每个区域的颜色
  6. int index = maskWaterShed.at< int>(i, j);   // 这里的maskWaterShed是经过watershed处理的
  7. if (index == -1)   // 区域间的值被置为-1(边界)
  8. resImage.at<Vec3b>(i, j) = Vec3b( 255, 255, 255);
  9. else if (index <= 0 || index > contours.size())   // 没有标记清楚的区域被置为0
  10. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  11. else   // 其他每个区域的值保持不变:1,2,…,contours.size()
  12. resImage.at<Vec3b>(i, j) = colorTab[index - 1];   // 然后把这些区域绘制成不同颜色
  13. }
  14. }
  15. imshow( “resImage”, resImage);
显示出来是这样

我们用三个标记图片分成了三个区域,每个区域用不同的颜色表示,区域间用白色的线隔开。
我们也可以用

     
     
  1. addWeighted(resImage, 0.3, srcImage_, 0.7, 0, resImage);
  2. imshow( “分水岭结果”, resImage);
将它和原图做加权相加,结果是这样:
或者将某个区域作为前景显示出来,另外两个区域作为背景显示为黑色,对应代码在if ((char)c == ‘0’)中,这里只贴出结果

多次点按【0】键还可以显示不同前景。

三、代码和原图


     
     
  1. #include <iostream>
  2. #include <opencv2\opencv.hpp>
  3. using namespace std;
  4. using namespace cv;
  5. Mat srcImage, srcImage_, maskImage;
  6. Mat maskWaterShed; // watershed()函数的参数
  7. Point clickPoint; // 鼠标点下去的位置
  8. void on_Mouse(int event, int x, int y, int flags, void*);
  9. void helpText();
  10. int main(int argc, char** argv)
  11. {
  12. /* 操作提示 */
  13. helpText();
  14. srcImage = imread( “fly.jpg”);
  15. srcImage_ = srcImage.clone(); // 程序中srcImage会被改变,所以这里做备份
  16. maskImage = Mat(srcImage.size(), CV_8UC1); // 掩模,在上面做标记,然后传给findContours
  17. maskImage = Scalar::all( 0);
  18. int areaCount = 1; // 计数,在按【0】时绘制每个区域
  19. imshow( “在图像中做标记”, srcImage);
  20. setMouseCallback( “在图像中做标记”, on_Mouse, 0);
  21. while ( true)
  22. {
  23. int c = waitKey( 0);
  24. if (( char)c == 27) // 按【ESC】键退出
  25. break;
  26. if (( char)c == ‘2’) // 按【2】恢复原图
  27. {
  28. maskImage = Scalar::all( 0);
  29. srcImage = srcImage_.clone();
  30. imshow( “在图像中做标记”, srcImage);
  31. }
  32. if (( char)c == ‘1’) // 按【1】处理图片
  33. {
  34. vector< vector<Point>> contours;
  35. vector<Vec4i> hierarchy;
  36. findContours(maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  37. if (contours.size() == 0) // 如果没有做标记,即没有轮廓,则退出该if语句
  38. break;
  39. cout << contours.size() << “个轮廓” << endl;
  40. maskWaterShed = Mat(maskImage.size(), CV_32S);
  41. maskWaterShed = Scalar::all( 0);
  42. /* 在maskWaterShed上绘制轮廓 */
  43. for ( int index = 0; index < contours.size(); index++)
  44. drawContours(maskWaterShed, contours, index, Scalar::all(index + 1), -1, 8, hierarchy, INT_MAX);
  45. /* 如果imshow这个maskWaterShed,我们会发现它是一片黑,原因是在上面我们只给它赋了1,2,3这样的值,通过代码80行的处理我们才能清楚的看出结果 */
  46. watershed(srcImage_, maskWaterShed); // 注释一
  47. vector<Vec3b> colorTab; // 随机生成几种颜色
  48. for ( int i = 0; i < contours.size(); i++)
  49. {
  50. int b = theRNG().uniform( 0, 255);
  51. int g = theRNG().uniform( 0, 255);
  52. int r = theRNG().uniform( 0, 255);
  53. colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
  54. }
  55. Mat resImage = Mat(srcImage.size(), CV_8UC3); // 声明一个最后要显示的图像
  56. for ( int i = 0; i < maskImage.rows; i++)
  57. {
  58. for ( int j = 0; j < maskImage.cols; j++)
  59. { // 根据经过watershed处理过的maskWaterShed来绘制每个区域的颜色
  60. int index = maskWaterShed.at< int>(i, j); // 这里的maskWaterShed是经过watershed处理的
  61. if (index == -1) // 区域间的值被置为-1(边界)
  62. resImage.at<Vec3b>(i, j) = Vec3b( 255, 255, 255);
  63. else if (index <= 0 || index > contours.size()) // 没有标记清楚的区域被置为0
  64. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  65. else // 其他每个区域的值保持不变:1,2,…,contours.size()
  66. resImage.at<Vec3b>(i, j) = colorTab[index - 1]; // 然后把这些区域绘制成不同颜色
  67. }
  68. }
  69. imshow( “resImage”, resImage);
  70. addWeighted(resImage, 0.3, srcImage_, 0.7, 0, resImage);
  71. imshow( “分水岭结果”, resImage);
  72. }
  73. if (( char)c == ‘0’) // 多次点按【0】依次显示每个被分割的区域,需要先按【1】处理图像
  74. {
  75. Mat resImage = Mat(srcImage.size(), CV_8UC3); // 声明一个最后要显示的图像
  76. for ( int i = 0; i < maskImage.rows; i++)
  77. {
  78. for ( int j = 0; j < maskImage.cols; j++)
  79. {
  80. int index = maskWaterShed.at< int>(i, j);
  81. if (index == areaCount)
  82. resImage.at<Vec3b>(i, j) = srcImage_.at<Vec3b>(i, j);
  83. else
  84. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  85. }
  86. }
  87. imshow( “分水岭结果”, resImage);
  88. areaCount++;
  89. if (areaCount == 4)
  90. areaCount = 1;
  91. }
  92. }
  93. return 0;
  94. }
  95. void on_Mouse(int event, int x, int y, int flags, void*)
  96. {
  97. // 如果鼠标不在窗口中则返回
  98. if (x < 0 || x >= srcImage.cols || y < 0 || y >= srcImage.rows)
  99. return;
  100. // 如果鼠标左键被按下,获取鼠标当前位置;当鼠标左键按下并且移动时,绘制白线;
  101. if (event == EVENT_LBUTTONDOWN)
  102. {
  103. clickPoint = Point(x, y);
  104. }
  105. else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  106. {
  107. Point point(x, y);
  108. line(maskImage, clickPoint, point, Scalar::all( 255), 5, 8, 0);
  109. line(srcImage, clickPoint, point, Scalar::all( 255), 5, 8, 0);
  110. clickPoint = point;
  111. imshow( “在图像中做标记”, srcImage);
  112. }
  113. }
  114. void helpText()
  115. {
  116. cout << “先用鼠标在图片窗口中标记出大致的区域” << endl;
  117. cout << “如果想把图片分割为N个区域,就要做N个标记” << endl;
  118. cout << “键盘按键【1】 - 运行的分水岭分割算法” << endl;
  119. cout << “键盘按键【2】 - 恢复原始图片” << endl;
  120. cout << “键盘按键【0】 - 依次分割每个区域(必须先按【1】)” << endl;
  121. cout << “键盘按键【ESC】 - 退出程序” << endl << endl;
  122. }
  123. /* 注释一:watershed(srcImage_, maskWaterShed);
  124. * 注意它的两个参数
  125. * srcImage_是没做任何修改的原图,CV_8UC3类型
  126. * maskWaterShed声明为CV_32S类型(32位单通道),且全部元素为0
  127. * 然后作为drawContours的第一个参数传入,在上面绘制轮廓
  128. * 最后作为watershed的参数
  129. * 另外,watershed的第二个参数maskWaterShed是InputOutputArray类型
  130. * 即作为输入,也作为输出保存函数调用的结果
  131. */


        </div>
            </div> 欢迎使用Markdown编辑器写博客

本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:

  • Markdown和扩展Markdown简洁的语法
  • 代码块高亮
  • 图片链接和图片上传
  • LaTex数学公式
  • UML序列图和流程图
  • 离线写博客
  • 导入导出Markdown文件
  • 丰富的快捷键

快捷键

  • 加粗 Ctrl + B
  • 斜体 Ctrl + I
  • 引用 Ctrl + Q
  • 插入链接 Ctrl + L
  • 插入代码 Ctrl + K
  • 插入图片 Ctrl + G
  • 提升标题 Ctrl + H
  • 有序列表 Ctrl + O
  • 无序列表 Ctrl + U
  • 横线 Ctrl + R
  • 撤销 Ctrl + Z
  • 重做 Ctrl + Y

Markdown及扩展

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]

使用简单的符号标识不同的标题,将某些文字标记为粗体或者斜体,创建一个链接等,详细语法参考帮助?。

本编辑器支持 Markdown Extra ,  扩展了很多好用的功能。具体请参考Github.

表格

Markdown Extra 表格语法:

项目 价格
Computer $1600
Phone $12
Pipe $1

可以使用冒号来定义对齐方式:

项目 价格 数量
Computer 1600 元 5
Phone 12 元 12
Pipe 1 元 234

定义列表

Markdown Extra 定义列表语法:
项目1
项目2
定义 A
定义 B
项目3
定义 C

定义 D

定义D内容

代码块

代码块语法遵循标准markdown代码,例如:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None
class SomeClass:
    pass
>>> message = '''interpreter
... prompt'''

脚注

生成一个脚注1.

扫描二维码关注公众号,回复: 2974742 查看本文章

目录

[TOC]来生成目录:

数学公式

使用MathJax渲染LaTex 数学公式,详见math.stackexchange.com.

  • 行内公式,数学公式为: Γ ( n ) = ( n 1 ) ! n N
  • 块级公式:

x = b ± b 2 4 a c 2 a

更多LaTex语法请参考 这儿.

UML 图:

可以渲染序列图:

Created with Raphaël 2.1.2 张三 张三 李四 李四 嘿,小四儿, 写博客了没? 李四愣了一下,说: 忙得吐血,哪有时间写。

或者流程图:

Created with Raphaël 2.1.2 开始 我的操作 确认? 结束 yes no
  • 关于 序列图 语法,参考 这儿,
  • 关于 流程图 语法,参考 这儿.

离线写博客

即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。

用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。

博客发表后,本地缓存将被删除。 

用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。

注意:虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱

浏览器兼容

  1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下问题
    1. 不支持离线功能
    2. IE9不支持文件导入导出
    3. IE10不支持拖拽文件导入


  1. 这里是 脚注内容.

声明:如果有写的不对的地方欢迎指正!

一、分水岭算法

关于分水岭算法的具体原理我就不说了,网上搜一下很多。OpenCV中的watershed函数实现的分水岭算法是基于“标记”的分割算法,用于解决传统的分水岭算法过度分割的问题。试想,一副图片中肯定有N多个“山谷”,它们中的很多是我们不想要的。
对于标记的原则,我总结是:你认为它们属于一个区域,就用标记将它们连接起来,对于另一个区域,再用另一个标记连接。就像这样:
图片中我认为有三个区域,所以做了三个标记。看到这里,你就可以把文章结尾的代码和图片拷到你的工程中试一试效果了。

二、代码分析

要想watershed函数,我们先要做一些准备工作:

1. 做标记

 做标记的原则在上面已经说过了,具体对应代码中on_Mouse函数里面的内容,这是一个鼠标事件回调函数。


  
  
  1. else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  2. {
  3. Point pt(x, y);
  4. if (previousPoint.x < 0)
  5. previousPoint = pt;
  6. //绘制白色线条
  7. line(inpaintMask, previousPoint, pt, Scalar::all( 255), 5, 8, 0);
  8. line(srcImage1, previousPoint, pt, Scalar::all( 255), 5, 8, 0);
  9. previousPoint = pt;
  10. imshow(WINDOW_NAME1, srcImage1);
  11. }

在鼠标左键点按并移动时画线,其中的maskImage是一个CV_8UC1类型的掩模,绘制完的结果就是黑色背景上有几条线(标记),srcImage用于实时显示我们做标记的结果。

2. 寻找轮廓

对我们做过标记的maskImage寻找轮廓,这部分代码写在if ((char)c == ‘1’)中,findContours函数这里不展开说明。

   
   
  1. vector< vector<Point>> contours;
  2. vector<Vec4i> hierarchy;
  3. findContours(maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

3. 绘制轮廓

这里我们声明了一个CV_32S类型的Mat用于绘制轮廓,然后作为watershed的第二个参数传入。对于drawContours()函数的color参数,我们用的是Scalar::all(index + 1),也就是1,2,3这样的数,后面的代码我们会根据这些数绘制可以显示的图像。

   
   
  1. for (int index = 0; index < contours.size(); index++)
  2. drawContours( maskWaterShed, contours, index, Scalar::all( index + 1), -1, 8, hierarchy, INT_MAX);

4. 分水岭算法分割

下面就是调用OpenCV中的watershed函数进行分割
watershed(srcImage_, maskWaterShed);
   
   
注意它的两个参数:srcImage_是没做任何修改的原图,CV_8UC3类型;
maskWaterShed声明为CV_32S类型(32位单通道),全部元素为0,然后作为drawContours的第一个参数传入
(第3步),在上面绘制轮廓,最后作为watershed的参数。另外,参数maskWaterShed是InputOutputArray类型,
作为输入,也作为输出保存函数调用的结果。
经过watershed函数的处理,不同区域间的值被置为-1(边界)没有标记清楚的区域被置为0,其他每个区域
的值保持不变:1,2,…,contours.size()。

5. 绘制结果图像

由于watershed的结果中只有-1,0,1,2这样的数,不能直接显示,所以我们还要做进一步的处理将结果显示出来

   
   
  1. Mat resImage = Mat(srcImage.size(), CV_8UC3);   // 声明一个最后要显示的图像
  2. for ( int i = 0; i < maskImage.rows; i++)
  3. {
  4. for ( int j = 0; j < maskImage.cols; j++)
  5. { // 根据经过watershed处理过的maskWaterShed来绘制每个区域的颜色
  6. int index = maskWaterShed.at< int>(i, j);   // 这里的maskWaterShed是经过watershed处理的
  7. if (index == -1)   // 区域间的值被置为-1(边界)
  8. resImage.at<Vec3b>(i, j) = Vec3b( 255, 255, 255);
  9. else if (index <= 0 || index > contours.size())   // 没有标记清楚的区域被置为0
  10. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  11. else   // 其他每个区域的值保持不变:1,2,…,contours.size()
  12. resImage.at<Vec3b>(i, j) = colorTab[index - 1];   // 然后把这些区域绘制成不同颜色
  13. }
  14. }
  15. imshow( “resImage”, resImage);
显示出来是这样

我们用三个标记图片分成了三个区域,每个区域用不同的颜色表示,区域间用白色的线隔开。
我们也可以用

   
   
  1. addWeighted(resImage, 0.3, srcImage_, 0.7, 0, resImage);
  2. imshow( “分水岭结果”, resImage);
将它和原图做加权相加,结果是这样:
或者将某个区域作为前景显示出来,另外两个区域作为背景显示为黑色,对应代码在if ((char)c == ‘0’)中,这里只贴出结果

多次点按【0】键还可以显示不同前景。

三、代码和原图


   
   
  1. #include <iostream>
  2. #include <opencv2\opencv.hpp>
  3. using namespace std;
  4. using namespace cv;
  5. Mat srcImage, srcImage_, maskImage;
  6. Mat maskWaterShed; // watershed()函数的参数
  7. Point clickPoint; // 鼠标点下去的位置
  8. void on_Mouse(int event, int x, int y, int flags, void*);
  9. void helpText();
  10. int main(int argc, char** argv)
  11. {
  12. /* 操作提示 */
  13. helpText();
  14. srcImage = imread( “fly.jpg”);
  15. srcImage_ = srcImage.clone(); // 程序中srcImage会被改变,所以这里做备份
  16. maskImage = Mat(srcImage.size(), CV_8UC1); // 掩模,在上面做标记,然后传给findContours
  17. maskImage = Scalar::all( 0);
  18. int areaCount = 1; // 计数,在按【0】时绘制每个区域
  19. imshow( “在图像中做标记”, srcImage);
  20. setMouseCallback( “在图像中做标记”, on_Mouse, 0);
  21. while ( true)
  22. {
  23. int c = waitKey( 0);
  24. if (( char)c == 27) // 按【ESC】键退出
  25. break;
  26. if (( char)c == ‘2’) // 按【2】恢复原图
  27. {
  28. maskImage = Scalar::all( 0);
  29. srcImage = srcImage_.clone();
  30. imshow( “在图像中做标记”, srcImage);
  31. }
  32. if (( char)c == ‘1’) // 按【1】处理图片
  33. {
  34. vector< vector<Point>> contours;
  35. vector<Vec4i> hierarchy;
  36. findContours(maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  37. if (contours.size() == 0) // 如果没有做标记,即没有轮廓,则退出该if语句
  38. break;
  39. cout << contours.size() << “个轮廓” << endl;
  40. maskWaterShed = Mat(maskImage.size(), CV_32S);
  41. maskWaterShed = Scalar::all( 0);
  42. /* 在maskWaterShed上绘制轮廓 */
  43. for ( int index = 0; index < contours.size(); index++)
  44. drawContours(maskWaterShed, contours, index, Scalar::all(index + 1), -1, 8, hierarchy, INT_MAX);
  45. /* 如果imshow这个maskWaterShed,我们会发现它是一片黑,原因是在上面我们只给它赋了1,2,3这样的值,通过代码80行的处理我们才能清楚的看出结果 */
  46. watershed(srcImage_, maskWaterShed); // 注释一
  47. vector<Vec3b> colorTab; // 随机生成几种颜色
  48. for ( int i = 0; i < contours.size(); i++)
  49. {
  50. int b = theRNG().uniform( 0, 255);
  51. int g = theRNG().uniform( 0, 255);
  52. int r = theRNG().uniform( 0, 255);
  53. colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
  54. }
  55. Mat resImage = Mat(srcImage.size(), CV_8UC3); // 声明一个最后要显示的图像
  56. for ( int i = 0; i < maskImage.rows; i++)
  57. {
  58. for ( int j = 0; j < maskImage.cols; j++)
  59. { // 根据经过watershed处理过的maskWaterShed来绘制每个区域的颜色
  60. int index = maskWaterShed.at< int>(i, j); // 这里的maskWaterShed是经过watershed处理的
  61. if (index == -1) // 区域间的值被置为-1(边界)
  62. resImage.at<Vec3b>(i, j) = Vec3b( 255, 255, 255);
  63. else if (index <= 0 || index > contours.size()) // 没有标记清楚的区域被置为0
  64. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  65. else // 其他每个区域的值保持不变:1,2,…,contours.size()
  66. resImage.at<Vec3b>(i, j) = colorTab[index - 1]; // 然后把这些区域绘制成不同颜色
  67. }
  68. }
  69. imshow( “resImage”, resImage);
  70. addWeighted(resImage, 0.3, srcImage_, 0.7, 0, resImage);
  71. imshow( “分水岭结果”, resImage);
  72. }
  73. if (( char)c == ‘0’) // 多次点按【0】依次显示每个被分割的区域,需要先按【1】处理图像
  74. {
  75. Mat resImage = Mat(srcImage.size(), CV_8UC3); // 声明一个最后要显示的图像
  76. for ( int i = 0; i < maskImage.rows; i++)
  77. {
  78. for ( int j = 0; j < maskImage.cols; j++)
  79. {
  80. int index = maskWaterShed.at< int>(i, j);
  81. if (index == areaCount)
  82. resImage.at<Vec3b>(i, j) = srcImage_.at<Vec3b>(i, j);
  83. else
  84. resImage.at<Vec3b>(i, j) = Vec3b( 0, 0, 0);
  85. }
  86. }
  87. imshow( “分水岭结果”, resImage);
  88. areaCount++;
  89. if (areaCount == 4)
  90. areaCount = 1;
  91. }
  92. }
  93. return 0;
  94. }
  95. void on_Mouse(int event, int x, int y, int flags, void*)
  96. {
  97. // 如果鼠标不在窗口中则返回
  98. if (x < 0 || x >= srcImage.cols || y < 0 || y >= srcImage.rows)
  99. return;
  100. // 如果鼠标左键被按下,获取鼠标当前位置;当鼠标左键按下并且移动时,绘制白线;
  101. if (event == EVENT_LBUTTONDOWN)
  102. {
  103. clickPoint = Point(x, y);
  104. }
  105. else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  106. {
  107. Point point(x, y);
  108. line(maskImage, clickPoint, point, Scalar::all( 255), 5, 8, 0);
  109. line(srcImage, clickPoint, point, Scalar::all( 255), 5, 8, 0);
  110. clickPoint = point;
  111. imshow( “在图像中做标记”, srcImage);
  112. }
  113. }
  114. void helpText()
  115. {
  116. cout << “先用鼠标在图片窗口中标记出大致的区域” << endl;
  117. cout << “如果想把图片分割为N个区域,就要做N个标记” << endl;
  118. cout << “键盘按键【1】 - 运行的分水岭分割算法” << endl;
  119. cout << “键盘按键【2】 - 恢复原始图片” << endl;
  120. cout << “键盘按键【0】 - 依次分割每个区域(必须先按【1】)” << endl;
  121. cout << “键盘按键【ESC】 - 退出程序” << endl << endl;
  122. }
  123. /* 注释一:watershed(srcImage_, maskWaterShed);
  124. * 注意它的两个参数
  125. * srcImage_是没做任何修改的原图,CV_8UC3类型
  126. * maskWaterShed声明为CV_32S类型(32位单通道),且全部元素为0
  127. * 然后作为drawContours的第一个参数传入,在上面绘制轮廓
  128. * 最后作为watershed的参数
  129. * 另外,watershed的第二个参数maskWaterShed是InputOutputArray类型
  130. * 即作为输入,也作为输出保存函数调用的结果
  131. */


        </div>
            </div> 欢迎使用Markdown编辑器写博客

本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:

  • Markdown和扩展Markdown简洁的语法
  • 代码块高亮
  • 图片链接和图片上传
  • LaTex数学公式
  • UML序列图和流程图
  • 离线写博客
  • 导入导出Markdown文件
  • 丰富的快捷键

猜你喜欢

转载自blog.csdn.net/jumencibaliang92/article/details/81514766