赞
踩
- #include <opencv2/core/utility.hpp> // 引入OpenCV的核心工具包
- #include "opencv2/imgproc.hpp" // 引入OpenCV的图片处理模块
- #include "opencv2/imgcodecs.hpp" // 引入OpenCV的图像编解码模块
- #include "opencv2/highgui.hpp" // 引入OpenCV的高层GUI(图形用户界面)模块
-
-
- #include <cstdio> // 引入标准输入输出头文件
- #include <iostream> // 引入输入输出流头文件
-
-
- using namespace cv; // 使用cv命名空间,避免每次调用OpenCV库函数时都要加cv::前缀
- using namespace std; // 使用std命名空间,避免每次调用标准库函数时都要加std::前缀
-
-
- static void help(char** argv) // help函数,用于显示程序使用方法
- {
- cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n"
- "Usage:\n" << argv[0] <<" [image_name -- default is fruits.jpg]\n" << endl;
-
-
- cout << "Hot keys: \n"
- "\tESC - quit the program\n"
- "\tr - restore the original image\n"
- "\tw or SPACE - run watershed segmentation algorithm\n"
- "\t\t(before running it, *roughly* mark the areas to segment on the image)\n"
- "\t (before that, roughly outline several markers on the image)\n";
- }
- Mat markerMask, img; // 定义标记掩码和图像矩阵
- Point prevPt(-1, -1); // 定义前一个点,初始化为(-1, -1),代表未初始化状态
-
-
- static void onMouse(int event, int x, int y, int flags, void*) // onMouse函数,用于处理鼠标事件
- {
- // 检查鼠标点击是否超出图像范围,如果超出则返回
- if(x < 0 || x >= img.cols || y < 0 || y >= img.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(markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
- line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);
- prevPt = pt; // 更新前一个点
- imshow("image", img); // 显示当前图像
- }
- }
-
-
- int main(int argc, char** argv) // 主函数
- {
- // 解析命令行参数
- cv::CommandLineParser parser(argc, argv, "{help h | | }{ @input | fruits.jpg | }");
- if(parser.has("help")) // 如果输入了帮助参数
- {
- help(argv); // 显示帮助信息
- return 0; // 退出程序
- }
- string filename = samples::findFile(parser.get<string>("@input")); // 获取输入的图像文件名
- Mat img0 = imread(filename, IMREAD_COLOR), imgGray; // 读取图像文件
-
-
- if(img0.empty()) // 如果图像为空
- {
- cout << "Couldn't open image "; // 显示错误信息
- help(argv); // 显示帮助信息
- return 0; // 退出程序
- }
- help(argv); // 显示帮助信息
- namedWindow("image", 1); // 创建一个窗口
-
-
- img0.copyTo(img); // 复制图像
- cvtColor(img, markerMask, COLOR_BGR2GRAY); // 将图像转换为灰度图,并存储到标记掩码中
- cvtColor(markerMask, imgGray, COLOR_GRAY2BGR); // 将标记掩码转换回BGR格式,存储到imgGray中
- markerMask = Scalar::all(0); // 将标记掩码初始化为0
- imshow("image", img); // 显示图像
- setMouseCallback("image", onMouse, 0); // 设置鼠标回调函数
-
-
- for(;;) // 无限循环,等待用户操作
- {
- char c = (char)waitKey(0); // 等待按键
-
-
- if(c == 27) // 如果按下ESC键
- break; // 退出循环
-
-
- if(c == 'r') // 如果按下r键
- {
- markerMask = Scalar::all(0); // 重置标记掩码
- img0.copyTo(img); // 复制原始图像到img
- imshow("image", img); // 显示图像
- }
-
-
- // 如果按下'w'键或者空格键
- if( c == 'w' || c == ' ' )
- {
- int i, j, compCount = 0; // 定义循环计数变量和组件计数器
- vector<vector<Point> > contours; // 定义轮廓集合的向量
- vector<Vec4i> hierarchy; // 定义层次结构向量
-
- // 在标记掩码上寻找轮廓
- findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
-
- // 如果没有找到轮廓,跳过后续流程
- if( contours.empty() )
- continue;
- Mat markers(markerMask.size(), CV_32S); // 定义标记矩阵
- markers = Scalar::all(0); // 初始化标记矩阵为0
- int idx = 0;
- // 用轮廓构建标记矩阵
- for( ; idx >= 0; idx = hierarchy[idx][0], compCount++ )
- drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
-
- // 如果没有轮廓,跳过后续流程
- if( compCount == 0 )
- continue;
-
- vector<Vec3b> colorTab; // 定义颜色表向量
- for( 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 t = (double)getTickCount(); // 获取执行前的时间
- watershed( img0, markers ); // 执行分水岭算法
- t = (double)getTickCount() - t; // 获取执行后的时间,并计算执行时间
- printf( "execution time = %gms\n", t*1000./getTickFrequency() ); // 打印执行时间
-
- Mat wshed(markers.size(), CV_8UC3); // 定义分水岭图像矩阵
-
- // 为分水岭图像上色
- for( i = 0; i < markers.rows; i++ )
- for( j = 0; j < markers.cols; j++ )
- {
- int index = markers.at<int>(i,j); // 获取当前位置的标记值
- if( index == -1 ) // 如果标记为-1,表示边界,使用白色显示
- wshed.at<Vec3b>(i,j) = Vec3b(255,255,255);
- else if( index <= 0 || index > compCount ) // 如果标记非法,使用黑色显示
- wshed.at<Vec3b>(i,j) = Vec3b(0,0,0);
- else // 否则,使用分配的颜色
- wshed.at<Vec3b>(i,j) = colorTab[index - 1];
- }
-
- // 将分水岭图像与原始灰度图像混合显示
- wshed = wshed*0.5 + imgGray*0.5;
- imshow( "watershed transform", wshed ); // 显示分水岭转换后的图像
- }
- }
- return 0; // 程序正常退出
- }
这段代码演示了如何使用OpenCV实现分水岭分割算法,包括图像的加载、处理和显示等步骤。首先,通过命令行参数获取输入的图像名,然后读取图像并对其进行分水岭算法的预处理,包括将图像转为灰度图并进行标记。通过鼠标事件,用户可以在图像上标记出想要分割的区域。最后,执行分水岭算法并显示分割结果。整个程序还包括了一些基本的用户交互,如按键操作来控制程序的行为。
findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
drawContours(markers, contours, idx, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);
watershed(img0, markers);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。