数字形态学(OpenCV)
形态学
形态学(morphology)是生物学的一个分支,其关注的是动植物的形态和结构等。这里用同一词语表示图像处理的数学形态学的内容。数学形态学的语言是集合论,其中的集合表示图像的对象,如:二值化图像中,所有白色像素的集合是该图像的一个完整形态学描述。集合中每两个分量提供一个像素的坐标,第三个分量则对应于其离散灰度值。更高维度空间的集合可以包含其他的图像属性,譬如颜色和随时间变化的分量。除了基本的集合定义之外,集合的反射和平移的概念在形态学中用得也很广泛。一个集合B的反射表示B',定义如下:B'={w|w=-b,b∈B}。如果B是描述图像中物体的像素的集合(二维点),则B'是B中(x,y)坐标被(-x,-y)替代的点的集合。集合B按照点z=(z1,z2)表示(B)z的平移定义:(B)z={c|c=b+z,b∈B}。图像表示如下:
集合反射操作类似于空间卷积中执行的翻转(旋转)操作。在形态学中集合的反射和平移用来表达基于结构元(SE)的操作:研究一幅图像中感兴趣特性所用的小集合或子图像。对于结构元的操作可以使用下图做一些简单的介绍:
当我们使用“结构元包含在几何中”这样的术语时,我们明确地指出A和B的元素完全重叠。
形态学梯度运算:
#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv; int main() { Mat image = imread("1.jpg"); imshow("【原始图】形态学梯度", image); Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); morphologyEx(image, image, MORPH_GRADIENT, element); imshow("【效果图】形态学梯度", image); waitKey(0); return 0; }
形态学综合示例:
#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; Mat g_srcImage, g_dstImage;//原始图和效果图 int g_nElementShape = MORPH_RECT;//元素结构的形状 //变量接收的TrackBar位置参数 int g_nMaxIterationNum = 10; int g_nOpenCloseNum = 0; int g_nErodeDilateNum = 0; int g_nTopBlackHatNum = 0; static void on_OpenClose(int, void*);//回调函数 static void on_ErodeDilate(int, void*);//回调函数 static void on_TopBlackHat(int, void*);//回调函数 int main() { g_srcImage = imread("1.jpg"); if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; } imshow("【原始图】", g_srcImage); namedWindow("【开运算/闭运算】", 1); namedWindow("【腐蚀/膨胀】", 1); namedWindow("【顶帽/黑帽】", 1); //参数赋值 g_nOpenCloseNum = 9; g_nErodeDilateNum = 9; g_nTopBlackHatNum = 2; //分别为三个窗口创建滚动条 createTrackbar("迭代值", "【开运算/闭运算】", &g_nOpenCloseNum, g_nMaxIterationNum * 2 + 1, on_OpenClose); createTrackbar("迭代值", "【腐蚀/膨胀】", &g_nErodeDilateNum, g_nMaxIterationNum * 2 + 1, on_ErodeDilate); createTrackbar("迭代值", "【顶帽/黑帽】", &g_nTopBlackHatNum, g_nMaxIterationNum * 2 + 1, on_TopBlackHat); //轮询获取按键信息 while (1) { int c; //执行回调函数 on_OpenClose(g_nOpenCloseNum, 0); on_ErodeDilate(g_nErodeDilateNum, 0); on_TopBlackHat(g_nTopBlackHatNum, 0); //获取按键 c = waitKey(0); //按下键盘按键Q或者ESC,程序退出 if ((char)c == 'q' || (char)c == 27) break; //按下键盘按键1,使用椭圆(Elliptic)结构元素结构元素MORPH_ELLIPSE if ((char)c == 49)//键盘按键1的ASII码为49 g_nElementShape = MORPH_ELLIPSE; //按下键盘按键2,使用矩形(Rectangle)结构元素MORPH_RECT else if ((char)c == 50)//键盘按键2的ASII码为50 g_nElementShape = MORPH_RECT; //按下键盘按键3,使用十字形(Cross-shaped)结构元素MORPH_CROSS else if ((char)c == 51)//键盘按键3的ASII码为51 g_nElementShape = MORPH_CROSS; //按下键盘按键space,在矩形、椭圆、十字形结构元素中循环 else if ((char)c == ' ') g_nElementShape = (g_nElementShape + 1) % 3; } return 0; } static void on_OpenClose(int, void*) { //偏移量的定义 int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量 int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值 //自定义核 Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset)); //进行操作 if (offset < 0) //此句代码的OpenCV2版为: //morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element); //此句代码的OpenCV3版为: morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element); else //此句代码的OpenCV2版为: //morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element); //此句代码的OpenCV3版为: morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element); //显示图像 imshow("【开运算/闭运算】", g_dstImage); } static void on_ErodeDilate(int, void*) { //偏移量的定义 int offset = g_nErodeDilateNum - g_nMaxIterationNum; //偏移量 int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值 //自定义核 Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset)); //进行操作 if (offset < 0) erode(g_srcImage, g_dstImage, element); else dilate(g_srcImage, g_dstImage, element); //显示图像 imshow("【腐蚀/膨胀】", g_dstImage); } static void on_TopBlackHat(int, void*) { //偏移量的定义 int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量 int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值 //自定义核 Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset)); //进行操作 if (offset < 0) morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT, element); else morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element); //显示图像 imshow("【顶帽/黑帽】", g_dstImage); }
填充:
#include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv; int main() { Mat src = imread("1.jpg"); imshow("【原始图】", src); Rect ccomp; floodFill(src, Point(50, 300), Scalar(155, 255, 55), &ccomp, Scalar(20, 20, 20), Scalar(20, 20, 20)); imshow("【效果图】", src); waitKey(0); return 0; }
图像金字塔
#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; #define WINDOW_NAME "【程序窗口】" //为窗口标题定义的宏 static void ShowHelpText(); Mat g_srcImage, g_dstImage, g_tmpImage; int main() { g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的测试图像,且其尺寸需被2的N次方整除,N为可以缩放的次数 if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; } namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE); imshow(WINDOW_NAME, g_srcImage); g_tmpImage = g_srcImage; g_dstImage = g_tmpImage; int key = 0; ShowHelpText(); while (1) { key = waitKey();//读取键值到key变量中 //根据key变量的值,进行不同的操作 switch (key) { //======================【程序退出相关键值处理】======================= case 27://按键ESC return 0; break; case 'q'://按键Q return 0; break; //======================【图片放大相关键值处理】======================= case 'a'://按键A按下,调用pyrUp函数 pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2)); printf(">检测到按键【A】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n"); break; case 'w'://按键W按下,调用resize函数 resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2)); printf(">检测到按键【W】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n"); break; case '1'://按键1按下,调用resize函数 resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2)); printf(">检测到按键【1】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n"); break; case '3': //按键3按下,调用pyrUp函数 pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2)); printf(">检测到按键【3】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n"); break; //======================【图片缩小相关键值处理】======================= case 'd': //按键D按下,调用pyrDown函数 pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2)); printf(">检测到按键【D】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n"); break; case 's': //按键S按下,调用resize函数 resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2)); printf(">检测到按键【S】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n"); break; case '2'://按键2按下,调用resize函数 resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2)); printf(">检测到按键【2】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n"); break; case '4': //按键4按下,调用pyrDown函数 pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2)); printf(">检测到按键【4】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n"); break; } imshow(WINDOW_NAME, g_dstImage); g_tmpImage = g_dstImage; } return 0; } static void ShowHelpText() { //输出一些帮助信息 printf("\n\n\n\t欢迎来到OpenCV图像金字塔和resize示例程序~\n\n"); printf("\n\n\t按键操作说明: \n\n" "\t\t键盘按键【ESC】或者【Q】- 退出程序\n" "\t\t键盘按键【1】或者【W】- 进行基于【resize】函数的图片放大\n" "\t\t键盘按键【2】或者【S】- 进行基于【resize】函数的图片缩小\n" "\t\t键盘按键【3】或者【A】- 进行基于【pyrUp】函数的图片放大\n" "\t\t键盘按键【4】或者【D】- 进行基于【pyrDown】函数的图片缩小\n" ); }
基本阈值操作
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace cv; using namespace std; #define WINDOW_NAME "【程序窗口】" //为窗口标题定义的宏 int g_nThresholdValue = 100; int g_nThresholdType = 3; Mat g_srcImage, g_grayImage, g_dstImage; static void ShowHelpText();//输出帮助文字 void on_Threshold(int, void*);//回调函数 int main() { ShowHelpText(); g_srcImage = imread("1.jpg"); if (!g_srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; } imshow("原始图", g_srcImage); cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY); namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE); createTrackbar("模式", WINDOW_NAME, &g_nThresholdType, 4, on_Threshold); createTrackbar("参数值", WINDOW_NAME, &g_nThresholdValue, 255, on_Threshold); on_Threshold(0, 0); while (1) { int key; key = waitKey(20); if ((char)key == 27) { break; } } } void on_Threshold(int, void*) { //调用阈值函数 threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, g_nThresholdType); //更新效果图 imshow(WINDOW_NAME, g_dstImage); } static void ShowHelpText() { //输出欢迎信息和OpenCV版本 printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION); printf("\n\n ----------------------------------------------------------------------------\n"); //输出一些帮助信息 printf("\n\t欢迎来到【基本阈值操作】示例程序~\n\n"); printf("\n\t按键操作说明: \n\n" "\t\t键盘按键【ESC】- 退出程序\n" "\t\t滚动条模式0- 二进制阈值\n" "\t\t滚动条模式1- 反二进制阈值\n" "\t\t滚动条模式2- 截断阈值\n" "\t\t滚动条模式3- 反阈值化为0\n" "\t\t滚动条模式4- 阈值化为0\n"); }
轮廓
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; using namespace std; #define WINDOW_NAME1 "【原始图窗口】" //为窗口标题定义的宏 #define WINDOW_NAME2 "【效果图窗口】" //为窗口标题定义的宏 Mat g_srcImage; Mat g_grayImage; int g_nThresh = 50; int g_maxThresh = 255; RNG g_rng(12345); Mat srcImage_copy = g_srcImage.clone(); Mat g_thresholdImage_output; vector<vector<Point> > g_vContours; vector<Vec4i> g_vHierarchy; static void ShowHelpText(); void on_ThreshChange(int, void*); void ShowHelpText(); int main() { ShowHelpText(); g_srcImage = imread("1.jpg", 1); cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); blur(g_grayImage, g_grayImage, Size(3, 3)); namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE); imshow(WINDOW_NAME1, g_srcImage); createTrackbar(" 阈值:", WINDOW_NAME1, &g_nThresh, g_maxThresh, on_ThreshChange); on_ThreshChange(0, 0);//调用一次进行初始化 waitKey(0); return(0); } void on_ThreshChange(int, void*) { // 对图像进行二值化,控制阈值 threshold(g_grayImage, g_thresholdImage_output, g_nThresh, 255, THRESH_BINARY); // 寻找轮廓 findContours(g_thresholdImage_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); // 遍历每个轮廓,寻找其凸包 vector<vector<Point> >hull(g_vContours.size()); for (unsigned int i = 0; i < g_vContours.size(); i++) { convexHull(Mat(g_vContours[i]), hull[i], false); } // 绘出轮廓及其凸包 Mat drawing = Mat::zeros(g_thresholdImage_output.size(), CV_8UC3); for (unsigned int i = 0; i < g_vContours.size(); i++) { Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)); drawContours(drawing, g_vContours, i, color, 1, 8, vector<Vec4i>(), 0, Point()); drawContours(drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point()); } imshow(WINDOW_NAME2, drawing); } void ShowHelpText() { printf("当前使用的OpenCV版本为:" CV_VERSION); }
绘制包围轮廓的矩形和圆
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; using namespace std; #define WINDOW_NAME1 "【原始图窗口】" //为窗口标题定义的宏 #define WINDOW_NAME2 "【效果图窗口】" //为窗口标题定义的宏 Mat g_srcImage; Mat g_grayImage; int g_nThresh = 50;//阈值 int g_nMaxThresh = 255;//阈值最大值 RNG g_rng(12345);//随机数生成器 void on_ContoursChange(int, void*); static void ShowHelpText(); int main() { ShowHelpText(); //【1】载入3通道的原图像 g_srcImage = imread("1.jpg", 1); if (!g_srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; } //【2】得到原图的灰度图像并进行平滑 cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); blur(g_grayImage, g_grayImage, Size(3, 3)); //【3】创建原始图窗口并显示 namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE); imshow(WINDOW_NAME1, g_srcImage); //【4】设置滚动条并调用一次回调函数 createTrackbar(" 阈值:", WINDOW_NAME1, &g_nThresh, g_nMaxThresh, on_ContoursChange); on_ContoursChange(0, 0); waitKey(0); return(0); } void on_ContoursChange(int, void*) { //定义一些参数 Mat threshold_output; vector<vector<Point>> contours; vector<Vec4i> hierarchy; // 使用Threshold检测边缘 threshold(g_grayImage, threshold_output, g_nThresh, 255, THRESH_BINARY); // 找出轮廓 findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); // 多边形逼近轮廓 + 获取矩形和圆形边界框 vector<vector<Point> > contours_poly(contours.size()); vector<Rect> boundRect(contours.size()); vector<Point2f>center(contours.size()); vector<float>radius(contours.size()); //一个循环,遍历所有部分,进行本程序最核心的操作 for (unsigned int i = 0; i < contours.size(); i++) { approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);//用指定精度逼近多边形曲线 boundRect[i] = boundingRect(Mat(contours_poly[i]));//计算点集的最外面(up-right)矩形边界 minEnclosingCircle(contours_poly[i], center[i], radius[i]);//对给定的 2D点集,寻找最小面积的包围圆形 } // 绘制多边形轮廓 + 包围的矩形框 + 圆形框 Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3); for (int unsigned i = 0; i < contours.size(); i++) { Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//随机设置颜色 drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());//绘制轮廓 rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);//绘制矩形 circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);//绘制圆 } // 显示效果图窗口 namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE); imshow(WINDOW_NAME2, drawing); } static void ShowHelpText() { printf("当前使用的OpenCV版本为:" CV_VERSION); printf("欢迎来到【创建包围轮廓的矩形和圆形边界框】示例程序\n"); printf("按键操作说明: \n" "键盘按键【ESC】- 退出程序\n" "滑动滚动条 - 改变阈值\n"); }
轮廓面积与中心距
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; using namespace std; #define WINDOW_NAME1 "【原始图】" //为窗口标题定义的宏 #define WINDOW_NAME2 "【图像轮廓】" //为窗口标题定义的宏 Mat g_srcImage; Mat g_grayImage; int g_nThresh = 100; int g_nMaxThresh = 255; RNG g_rng(12345); Mat g_cannyMat_output; vector<vector<Point> > g_vContours; vector<Vec4i> g_vHierarchy; void on_ThreshChange(int, void*); static void ShowHelpText(); int main(int argc, char** argv) { ShowHelpText(); // 读入原图像, 返回3通道图像数据 g_srcImage = imread("1.jpg", 1); // 把原图像转化成灰度图像并进行平滑 cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); blur(g_grayImage, g_grayImage, Size(3, 3)); // 创建新窗口 namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE); imshow(WINDOW_NAME1, g_srcImage); //创建滚动条并进行初始化 createTrackbar(" 阈值", WINDOW_NAME1, &g_nThresh, g_nMaxThresh, on_ThreshChange); on_ThreshChange(0, 0); waitKey(0); return(0); } void on_ThreshChange(int, void*) { // 使用Canndy检测边缘 Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3); // 找到轮廓 findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); // 计算矩 vector<Moments> mu(g_vContours.size()); for (unsigned int i = 0; i < g_vContours.size(); i++) { mu[i] = moments(g_vContours[i], false); } // 计算中心矩 vector<Point2f> mc(g_vContours.size()); for (unsigned int i = 0; i < g_vContours.size(); i++) { mc[i] = Point2f(static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00)); } // 绘制轮廓 Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3); for (unsigned int i = 0; i < g_vContours.size(); i++) { Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//随机生成颜色值 drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());//绘制外层和内层轮廓 circle(drawing, mc[i], 4, color, -1, 8, 0);;//绘制圆 } // 显示到窗口中 namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE); imshow(WINDOW_NAME2, drawing); // 通过m00计算轮廓面积并且和OpenCV函数比较 printf("\t 输出内容: 面积和轮廓长度\n"); for (unsigned int i = 0; i < g_vContours.size(); i++) { printf(" >通过m00计算出轮廓[%d]的面积: (M_00) = %.2f \n OpenCV函数计算出的面积=%.2f , 长度: %.2f \n\n", i, mu[i].m00, contourArea(g_vContours[i]), arcLength(g_vContours[i], true)); Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)); drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point()); circle(drawing, mc[i], 4, color, -1, 8, 0); } } void ShowHelpText() { printf("当前使用的OpenCV版本为:" CV_VERSION); }
分水岭算法
#include <opencv2/core/utility.hpp> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include <cstdio> #include <iostream> using namespace cv; using namespace std; static void help(char** argv) { cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n" "Usage:\n" << argv[0] << " [image_name -- default is 1.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); static void onMouse(int event, int x, int y, int flags, void*) { 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 | 1.jpg | }"); if (parser.has("help")) { help(argv); return 0; } string filename = samples::findFile(parser.get<string>("@input")); Mat img0 = imread(filename, 1), 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); markerMask = Scalar::all(0); imshow("image", img); setMouseCallback("image", onMouse, 0); for (;;) { char c = 'w'; c = (char)waitKey(0); if (c == 27) break; if (c == 'r') { markerMask = Scalar::all(0); img0.copyTo(img); imshow("image", img); } 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); 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); // paint the watershed image for (i = 0; i < markers.rows; i++) for (j = 0; j < markers.cols; j++) { int index = markers.at<int>(i, j); if (index == -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; }
修补图像
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/photo/photo.hpp" #include <iostream> using namespace cv; using namespace std; #define WINDOW_NAME0 "【原始图参考】" //为窗口标题定义的宏 #define WINDOW_NAME1 "【原始图】" //为窗口标题定义的宏 #define WINDOW_NAME2 "【修补后的效果图】" //为窗口标题定义的宏 Mat srcImage0, srcImage1, inpaintMask; Point previousPoint(-1, -1);//原来的点坐标 static void ShowHelpText() { printf("当前使用的OpenCV版本为:" CV_VERSION); printf("\n欢迎来到【图像修复】示例程序\n"); printf("请在进行图像修复操作之前,在【原始图】窗口中进行适量的绘制" "\n按键操作说明: \n" "【鼠标左键】-在图像上绘制白色线条\n" "键盘按键【ESC】- 退出程序\n" "键盘按键【1】或【SPACE】-进行图像修复操作 \n"); } static void On_Mouse(int event, int x, int y, int flags, void*) { //鼠标左键弹起消息 if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) previousPoint = Point(-1, -1); //鼠标左键按下消息 else if (event == EVENT_LBUTTONDOWN) previousPoint = Point(x, y); //鼠标按下并移动,进行绘制 else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) { Point pt(x, y); if (previousPoint.x < 0) previousPoint = pt; //绘制白色线条 line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0); line(srcImage1, previousPoint, pt, Scalar::all(255), 5, 8, 0); previousPoint = pt; imshow(WINDOW_NAME1, srcImage1); } } int main(int argc, char** argv) { //显示帮助文字 ShowHelpText(); //载入原始图并进行掩膜的初始化 Mat srcImage = imread("1.jpg", -1); if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; } srcImage0 = srcImage.clone(); srcImage1 = srcImage.clone(); inpaintMask = Mat::zeros(srcImage1.size(), CV_8U); //显示原始图参考 imshow(WINDOW_NAME0, srcImage0); //显示原始图 imshow(WINDOW_NAME1, srcImage1); //设置鼠标回调消息 setMouseCallback(WINDOW_NAME1, On_Mouse, 0); //轮询按键,根据不同的按键进行处理 while (1) { //获取按键键值 char c = (char)waitKey(); //键值为ESC,程序退出 if (c == 27) break; //键值为2,恢复成原始图像 if (c == '2') { inpaintMask = Scalar::all(0); srcImage.copyTo(srcImage1); imshow(WINDOW_NAME1, srcImage1); } //键值为1或者空格,进行图像修补操作 if (c == '1' || c == ' ') { Mat inpaintedImage; inpaint(srcImage1, inpaintMask, inpaintedImage, 3, INPAINT_TELEA); imshow(WINDOW_NAME2, inpaintedImage); } } return 0; }
--------------------------continue----------------------------------------
【zlc】