当前位置:   article > 正文

【opencv】示例-watershed.cpp 通过用户的鼠标输入分水岭算法可以使得图像的不同部分分割开来...

【opencv】示例-watershed.cpp 通过用户的鼠标输入分水岭算法可以使得图像的不同部分分割开来...

2a446bf7532ee2a9a16b8652c0ae2237.png

38b86a5797da3c194d8c423bca1fc3d2.png

  1. #include <opencv2/core/utility.hpp> // 引入OpenCV的核心工具包
  2. #include "opencv2/imgproc.hpp" // 引入OpenCV的图片处理模块
  3. #include "opencv2/imgcodecs.hpp" // 引入OpenCV的图像编解码模块
  4. #include "opencv2/highgui.hpp" // 引入OpenCV的高层GUI(图形用户界面)模块
  5. #include <cstdio> // 引入标准输入输出头文件
  6. #include <iostream> // 引入输入输出流头文件
  7. using namespace cv; // 使用cv命名空间,避免每次调用OpenCV库函数时都要加cv::前缀
  8. using namespace std; // 使用std命名空间,避免每次调用标准库函数时都要加std::前缀
  9. static void help(char** argv) // help函数,用于显示程序使用方法
  10. {
  11. cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n"
  12. "Usage:\n" << argv[0] <<" [image_name -- default is fruits.jpg]\n" << endl;
  13. cout << "Hot keys: \n"
  14. "\tESC - quit the program\n"
  15. "\tr - restore the original image\n"
  16. "\tw or SPACE - run watershed segmentation algorithm\n"
  17. "\t\t(before running it, *roughly* mark the areas to segment on the image)\n"
  18. "\t (before that, roughly outline several markers on the image)\n";
  19. }
  20. Mat markerMask, img; // 定义标记掩码和图像矩阵
  21. Point prevPt(-1, -1); // 定义前一个点,初始化为(-1, -1),代表未初始化状态
  22. static void onMouse(int event, int x, int y, int flags, void*) // onMouse函数,用于处理鼠标事件
  23. {
  24. // 检查鼠标点击是否超出图像范围,如果超出则返回
  25. if(x < 0 || x >= img.cols || y < 0 || y >= img.rows)
  26. return;
  27. // 根据不同的鼠标事件进行处理
  28. if(event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
  29. prevPt = Point(-1,-1); // 如果鼠标左键弹起,重置前一个点
  30. else if(event == EVENT_LBUTTONDOWN)
  31. prevPt = Point(x,y); // 如果鼠标左键按下,记录当前点为前一个点
  32. else if(event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
  33. {
  34. Point pt(x, y); // 记录当前移动点
  35. if(prevPt.x < 0)
  36. prevPt = pt;
  37. // 画线在标记掩码和图像上,用于标记区域
  38. line(markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
  39. line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);
  40. prevPt = pt; // 更新前一个点
  41. imshow("image", img); // 显示当前图像
  42. }
  43. }
  44. int main(int argc, char** argv) // 主函数
  45. {
  46. // 解析命令行参数
  47. cv::CommandLineParser parser(argc, argv, "{help h | | }{ @input | fruits.jpg | }");
  48. if(parser.has("help")) // 如果输入了帮助参数
  49. {
  50. help(argv); // 显示帮助信息
  51. return 0; // 退出程序
  52. }
  53. string filename = samples::findFile(parser.get<string>("@input")); // 获取输入的图像文件名
  54. Mat img0 = imread(filename, IMREAD_COLOR), imgGray; // 读取图像文件
  55. if(img0.empty()) // 如果图像为空
  56. {
  57. cout << "Couldn't open image "; // 显示错误信息
  58. help(argv); // 显示帮助信息
  59. return 0; // 退出程序
  60. }
  61. help(argv); // 显示帮助信息
  62. namedWindow("image", 1); // 创建一个窗口
  63. img0.copyTo(img); // 复制图像
  64. cvtColor(img, markerMask, COLOR_BGR2GRAY); // 将图像转换为灰度图,并存储到标记掩码中
  65. cvtColor(markerMask, imgGray, COLOR_GRAY2BGR); // 将标记掩码转换回BGR格式,存储到imgGray中
  66. markerMask = Scalar::all(0); // 将标记掩码初始化为0
  67. imshow("image", img); // 显示图像
  68. setMouseCallback("image", onMouse, 0); // 设置鼠标回调函数
  69. for(;;) // 无限循环,等待用户操作
  70. {
  71. char c = (char)waitKey(0); // 等待按键
  72. if(c == 27) // 如果按下ESC键
  73. break; // 退出循环
  74. if(c == 'r') // 如果按下r键
  75. {
  76. markerMask = Scalar::all(0); // 重置标记掩码
  77. img0.copyTo(img); // 复制原始图像到img
  78. imshow("image", img); // 显示图像
  79. }
  80. // 如果按下'w'键或者空格键
  81. if( c == 'w' || c == ' ' )
  82. {
  83. int i, j, compCount = 0; // 定义循环计数变量和组件计数器
  84. vector<vector<Point> > contours; // 定义轮廓集合的向量
  85. vector<Vec4i> hierarchy; // 定义层次结构向量
  86. // 在标记掩码上寻找轮廓
  87. findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  88. // 如果没有找到轮廓,跳过后续流程
  89. if( contours.empty() )
  90. continue;
  91. Mat markers(markerMask.size(), CV_32S); // 定义标记矩阵
  92. markers = Scalar::all(0); // 初始化标记矩阵为0
  93. int idx = 0;
  94. // 用轮廓构建标记矩阵
  95. for( ; idx >= 0; idx = hierarchy[idx][0], compCount++ )
  96. drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
  97. // 如果没有轮廓,跳过后续流程
  98. if( compCount == 0 )
  99. continue;
  100. vector<Vec3b> colorTab; // 定义颜色表向量
  101. for( i = 0; i < compCount; i++ ) // 为每个组件分配随机颜色
  102. {
  103. int b = theRNG().uniform(0, 255); // 蓝色分量
  104. int g = theRNG().uniform(0, 255); // 绿色分量
  105. int r = theRNG().uniform(0, 255); // 红色分量
  106. // 将随机颜色添加到颜色表
  107. colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
  108. }
  109. double t = (double)getTickCount(); // 获取执行前的时间
  110. watershed( img0, markers ); // 执行分水岭算法
  111. t = (double)getTickCount() - t; // 获取执行后的时间,并计算执行时间
  112. printf( "execution time = %gms\n", t*1000./getTickFrequency() ); // 打印执行时间
  113. Mat wshed(markers.size(), CV_8UC3); // 定义分水岭图像矩阵
  114. // 为分水岭图像上色
  115. for( i = 0; i < markers.rows; i++ )
  116. for( j = 0; j < markers.cols; j++ )
  117. {
  118. int index = markers.at<int>(i,j); // 获取当前位置的标记值
  119. if( index == -1 ) // 如果标记为-1,表示边界,使用白色显示
  120. wshed.at<Vec3b>(i,j) = Vec3b(255,255,255);
  121. else if( index <= 0 || index > compCount ) // 如果标记非法,使用黑色显示
  122. wshed.at<Vec3b>(i,j) = Vec3b(0,0,0);
  123. else // 否则,使用分配的颜色
  124. wshed.at<Vec3b>(i,j) = colorTab[index - 1];
  125. }
  126. // 将分水岭图像与原始灰度图像混合显示
  127. wshed = wshed*0.5 + imgGray*0.5;
  128. imshow( "watershed transform", wshed ); // 显示分水岭转换后的图像
  129. }
  130.     }
  131.     return 0// 程序正常退出
  132. }

这段代码演示了如何使用OpenCV实现分水岭分割算法,包括图像的加载、处理和显示等步骤。首先,通过命令行参数获取输入的图像名,然后读取图像并对其进行分水岭算法的预处理,包括将图像转为灰度图并进行标记。通过鼠标事件,用户可以在图像上标记出想要分割的区域。最后,执行分水岭算法并显示分割结果。整个程序还包括了一些基本的用户交互,如按键操作来控制程序的行为。

findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

db6803770c0fbe61416418a677643726.png

drawContours(markers, contours, idx, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);

31d80f4cbff7062b5bdb2532cb739e64.png

watershed(img0, markers);

2e026eb3873b3df6050329c3aec6ca97.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/434589
推荐阅读
相关标签
  

闽ICP备14008679号