目录 第八章、图像轮廓与图像分割修复 8.1、查找并绘制轮廓 8.1.1、寻找轮廓:findContours()函数 8.1.2、绘制轮廓:drawContours()函数 8.2、寻找物体的凸包 8.2.1、凸包 8.2.2、寻找凸包 8.2.4、寻找和绘制物体的凸包 8.3、使用多边形将轮廓包围 8.3.1、返回外部矩形边界:boundingRect 8.3.2、寻找最小包围矩形:minAreaRect 8.3.3、寻找最小包围圆形minEnclosingCircle()函数 8.3.4、用椭圆拟合二维点集:fitEllipse 8.3.5、逼近多边形曲线:approxPolyDP()函数 8.3.6、创建包围轮廓的矩形边界 8.3.7、创建包围轮廓的圆形边界 8.3.8、使用多边形包围轮廓 8.4、图像的矩 8.4.1、矩的计算:moments()函数 8.4.2、计算轮廓面积:contourArea()函数 8.4.3、计算轮廓长度:arcLengeh()函数 8.5、分水岭算法 8.5.1、实现分水岭算法:watershed()算法 8.5.2、分水岭算法 8.6、图像修补 8.6.1、实现图像修补:inpaint()函数 8.6.2、图像修补示例 8.7、本章小结
第八章、图像轮廓与图像分割修复
8.1、查找并绘制轮廓
8.1.1、寻找轮廓:findContours()函数
void findContours(InputOutArray image, OutputArrayOfArrays
contours,OutputArray hierarchy, int mode,
int method,Point offset=Point()) 1:输入图像且需为 8位单通道图像。 图像的非零像素被视为 1, 0像素值被 保留为 0,所以图像为二进制。 我们可以使用 compareO、 inrangeO、 thresholdO、 adaptivethresholdO、 cannyO等函数由灰度图或彩色图创建二进 制图像。 此函数会在提取图像轮廓的同时修改图像的内容;2:检测到的轮廓、函数 调用后的运算结果存在这里。每个轮廓存储为一个点向量,即用 point类型 的 vector 表示;3:可选的输出向量,包含图像的 拓扑信息。其作为轮廓数量的表示,包含了许多元素。每个轮廓contours[ i ] 对应4个hierarchy 元素 hierarchy[ i ][ 0 ]~hierarchy[ i ][ 3 ], 分别表示后一 个轮廓、前一个轮廓、父轮廓、内嵌轮廊的索引编号。 如果没有对应项, 对应的 hierarchy[i]值设置为负数。4:轮廓检索模式5:轮廓的近似办法6:每个轮廓点的可选偏移量,默认值Point()。对 ROI 图像中找出的轮廓, 并要在整个图像中进行分析时, 这个 参数便可排上用场。 findContours 经常与 drawContours 配合使用一使用用 findContoursO函数检测 到图像的轮廓后,便可以用 drawContoursO函数将检测到的轮廓绘制出来。 8.1.2、绘制轮廓:drawContours()函数
void drawContours(InputOutputArray image,
InputArrayOfArrays contours,int contourTdx,
const Scalar& color, int thickness=1,
int lineType=8, InputArray hierarchy=noArray(),
int maxLevel=INT_MAX,Point offset=Point()) 1:目标图像;2:所有的输入轮廓,每个轮廓存储为一个点向量;3:轮廓绘制的指示变量,如果为负值,则绘制所有轮廓;4:轮廓的颜色;5:轮廓线条的粗细度,有默认值1。若为负值,便会绘制在轮廓的内部;6:线条的类型,默认值为8,7:可选的层次结构信息,默认noArray();8:用于绘制轮廓的最大等级,默认INT_MAX;9:可选的轮廓偏移参数,用指定的偏移量offset =(dx,dy)偏移需要绘制的轮廓,有默认值Point() 8.1.3、基础示例程序:轮廓查找
int main(int argc, char **argv)
{
Mat srcImage = imread("1.jpg");
imshow("原始图",srcImage);
Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols,CV_8UC3);
//取阈值大于199的部分
srcImage=srcImage>199;
imshow("取阈值后的图像",srcImage);
//定义轮廓和层次结构
vector
vector
//查找轮廓
findContours(srcImage,contours;hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
//遍历所有顶层的轮廓,以随机颜色绘制出每个连接组件颜色
int index=0;
for(;index>=0;index=hierarchy[index[0]])
{
Scalar color(rand()&255,rand()&255,rand()&255);
drawContours(dstImage,contours,index,color,FILLED,8,hierarchy);
}
imshow("轮廓图",dstImage);
waitKey(0);
return 0;
}
8.2、寻找物体的凸包
8.2.1、凸包
给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。理解物体形状或轮廓的一种比较有用的方法便是计算凸包,然后计算凸缺陷。很多复杂物体的特性能很好的被这种缺陷表现出来。 8.2.2、寻找凸包
void convexHull(InputArray points, OutputArray hull,
bool clockwise=false, bool returnPoints=true) 1:输入的二维点集,可以是Mat或vector;2:输出参数,函数调用后找到的凸包;3:操作方向标识符,当为真时,输出的凸包为顺时针方向,反之为逆时针。4:操作标识符,默认为真,返回各凸包的各个点,否则返回凸包各点的指数。
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat image(600,600,CV_8UC3);
RNG& rng = theRng();
while(1)
{
char key;//键值
int count = (unsigned)rng%100+3;//随机生成点的数量
vector
//随机生成点坐标
for(int i = 0;i { Point point; point.x = rng.uniform(image.cols/4, image.cols*3/4); point.y = rng.uniform(image.rows/4, image.rows*3/4); points.push_back(point); } //检测凸包 vector convexHull (Mat(points),hull,true); //绘制出随机颜色的点 image = Scalar::all(0); for(int i=0;i { circle(image,points[i],3,Scalar(rng.uniform(0,255), rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA); } //准备参数 int hullcount = (int)hull.size();//凸包的边数 Point point()=points[hull[hullcount-1]];//连接凸包边的坐标点 //绘制凸包的边 for(int i=0;i { Point point = points[hull[i]]; line(image,point0,point,Scalar(255,255,255),2,LINE_AA); point0= point; } imshow("凸包检测示例",image); key=(char)waitKey(); if(key==27 || key=='q' || key == 'Q') break; } return 0; } 8.2.4、寻找和绘制物体的凸包 #include #include #include 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_secImage.clone(); Mat g_thresholdImage_output; vector vector static void ShowHelpText(); void on_ThreshChange(int,void *); int main() { g_srcImage = imread("1.jpg"); 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); //遍历每个轮廓,寻找其凸包 vector for(unsigned int i = 0; 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 { 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 drawContours(drawing, hull, i, color,1,8, vector } imshow(WINDOW_NAME2,drawing); } 8.3、使用多边形将轮廓包围 8.3.1、返回外部矩形边界:boundingRect Rect boundingRect(InputArray points) 唯一的一个参数是输入的二维点集 8.3.2、寻找最小包围矩形:minAreaRect RotateRect minAreaRect(InputArray points) 唯一的一个参数是输入的二维点集 8.3.3、寻找最小包围圆形minEnclosingCircle()函数 void minEnclosingCircle(InputArray points, Point2f& center,float& radius) 1:输入的二维点集;2:圆的输出圆心;3:圆的输出半径 8.3.4、用椭圆拟合二维点集:fitEllipse RotatedRect fitEllipse(InputArray points) 唯一的一个参数是输入的二维点集 8.3.5、逼近多边形曲线:approxPolyDP()函数 void approxPolyDP(InputArray curve,OutputArray approxCurve, double epsilon, bool closed) 1:输入的二维点集;2:多边形逼近的结果;3:逼近的精度,为原始曲线和即近似曲线间的最大值;4:为真则近似的曲线为封闭曲线 8.3.6、创建包围轮廓的矩形边界 #include #include #include using namespace cv; using namespace std; int main() { Mat image(600,600,CV_8U3C); RNG& rng = theRNG(); while (1) { //参数初始化 int count = rng.uniform(3,103); vector //随机生成点坐标 for(int i=0;i { Point point; point.x = rng.uniform(image.cols/4,image.cols*3/4); point.y = rng.uniform(image.rows/4,image.rows*3/4); points.push_back(point); } //对给定的2D点集,寻找最小面积的包围矩形 RotatedRect box = minAreaRect(Mat(points)); Point2f vertex[4]; box.point(vertex); //绘制出随机颜色的点 image = Scalar::all(0); for(int i=0;i { circle(image,points[i],3,Scalar(rng.uniform(0,255), rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA); } //绘制出最小面积的包围矩形 for(int i=0;i<4;i++) { line(image,vertex[i],vertex[(i+1)%4], Scalar(100,200,211),2,LINE_AA); } imshow("矩形包围示例",image); key=(char)waitKey(); if(key==27 || key=='q' || key == 'Q') break; } return 0; } 8.3.7、创建包围轮廓的圆形边界 #include #include #include using namespace cv; using namespace std; int main() { Mat image(600,600,CV_8U3C); RNG& rng = theRNG(); while (1) { //参数初始化 int count = rng.uniform(3,103); vector //随机生成点坐标 for(int i=0;i { Point point; point.x = rng.uniform(image.cols/4,image.cols*3/4); point.y = rng.uniform(image.rows/4,image.rows*3/4); points.push_back(point); } //对给定的2D点集,寻找最小面积的包围圆 Point2f center; float radius = 0; minEnclosingCircle(Mat(points),center,radius); //绘制出随机颜色的点 image = Scalar::all(0); for(int i=0;i { circle(image,points[i],3,Scalar(rng.uniform(0,255), rng.uniform(0,255),rng.uniform(0,255)),FILLED,LINE_AA); } //绘制出最小面积的包围圆 circle(image,center,cvRound(radius),Scalar(rng.uniform(0, 255),rng.uniform(0,255),rng.uniform(0,255)),2,LINE_AA); imshow("圆形包围示例",image); key=(char)waitKey(); if(key==27 || key=='q' || key == 'Q') break; } return 0; } 8.3.8、使用多边形包围轮廓 #include #include #include 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); void on_ContoursChange(int , void*); static void ShowHelpText(); int main() { system("color 1A"); g_srcImage = imread("1.jpg"); 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_ContoursChange); on_ContoursChange(0,0); waitKey(0); return 0; } void on_ContoursChange(int, void *) { Mat threshold_output; vector vector //检测边缘 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 vector vector for(unsigned int i=0;i { //指定精度逼近多边形曲线 approxPolyDP(Mat(contours[i]),contours_poly[i], 3,true); //计算点集最外面的矩形边界 boundRect[i]=boundingRect(Mat(contours_poly[i])); //对给定的2D点集,寻找最小面积的包围圆形 minEnclosingCircle(contours_poly[i],center[i], radius[i]); } //绘制多边形轮廓+包围的矩形框+圆形框 Mat drawing = Mat::zeros(threshold_output.size(),CV_8UC3); for(int unsigned i = 0; 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 rectangle(drawing,boundRect[i].t1(),boundRect[i].br(), color,2,8,0);// 绘制矩形 circle(drawing,center[i],(int)radius[i],color,2,8,0);//绘制圆 namedWindow(WINDOW_NAME1,Window_AUTOSIZE); imshow(WINDOW_NAME2,drawing); } } 8.4、图像的矩 矩函数在模式识别、目标分类、目标识别和方位估计、图像编码与重构应用。一个从数字图形中计算出来的矩集,描述了图像形状的全局特征,并提供了大量点的关于该图像不同类型的几何特性信息(大小、位置、方向、形状)。一阶矩与形状有关,二阶矩显示曲线围绕直线平均值的扩展程度,三阶矩则是关于平均值的对称性的测量。由二阶和三阶矩可以导出7组不变矩。 8.4.1、矩的计算:moments()函数 Moments moments(InputArray array, bool binaryImage=false) 1:输入参数;2:默认为false,为true时所有非零像素为1 8.4.2、计算轮廓面积:contourArea()函数 double contourArea(InputArray contour,bool oriented=false) 1:输入参数;2:面向区域标识符,true时返回一个带符号的面积值,正负值取决于轮廓的方向,根据这个特性可以根据面积的符号来确定轮廓的位置。这个参数有默认值false, 表示以绝对值返回,不带符号。 8.4.3、计算轮廓长度:arcLengeh()函数 double arcLength(InputArray curve,bool closed) 1:输入数据;2:用于指示曲线是否封闭的标识符,默认值closed曲线关闭 8.4.4、查找和绘制图像轮廓矩 #include #include #include 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 g_cannyMat_output; vector vector void on_ContoursChange(int , void*); static void ShowHelpText(); int main() { system("color 1A"); 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_ContoursChange); on_ContoursChange(0,0); waitKey(0); return 0; } void on_ContoursChange(int, void *) { //检测边缘 Canny(g_grayImage,g_cannyMat_output, g_nThresh,g_nThresh*2,3); //找出轮廓 findContours(threshold_output, contours,hierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0)); //计算矩 vector for(unsigned int i=0;i { mu[i]=moments(g_vContours[i],false); } //计算中心矩 vector for(unsigned int i=0;i { mc[i] =Point2f(static_cast (mu[i].m10/mu[i].m00),static_cast (mu[i].m01/mu[i].m00)) } //绘制轮廓 Mat drawing = Mat::zeros(g_cannyMat_output.size(),CV_8UC3); for(unsigned int i=0;i { Scalar color= Scalar(g_rng.uniform(0,255), g_rng.uniform(0,255),g_rng.uniform(0,255));//设置随机颜色 drawContours(drawing, g_vContours0,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 { printf(">通过m00计算出轮廓【%d】的面积:(m_00)=%.2f \n OpenCV 函数计算的面积=%0.2f,长度: %0.2f, \n\n",i,mu[i].m00, contourArea(g_vContours[i]),arcLength(g_vContours[i],true)); Sclar 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); } } 8.5、分水岭算法 此算法可以将图像中的边缘转化为”山脉“,将均匀区域转化为”山谷“,有助于分割目标。分水岭算法,是一种基于拓扑理论的数学形态学的分割方法,基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,形成分水岭。 分水岭的计算过程是一个迭代标注过程。分为两个步骤:排序和淹没。首先对每个像素的灰度级进行从低到高的排序,然后在从低到高实现淹没的过程中,对每一个局部极小值在h阶高度的影响域采用先进先出结构进行判断和标注。分水岭得到的是输入图像的集水盆图像,表示的是输入图像的极大值点。 分水岭算法首先计算灰度图像的梯度;然后从指定点开始持续灌注盆地直到这些区域连成一片。基于这样产生的标记就可以把区域合并到一起,合并后的区域又通过聚集的方式进行分割。 8.5.1、实现分水岭算法:watershed()算法 此函数实现的算法是基于标记的分割算法的一部分;在把图像传给函数之前,需要大致勾画标记出图像中的期望进行分割的区域,被标记为正指数。所以每一个预取都会被标记为像素值1、2、3等,表示成为一个或多个连接组件。这些标记的值可以使用findContours函数和drawContours函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的”种子“,而没有标记清楚的区域被置0。 void watershed(InputArray image, InputOutputArray markers) 1:输入图像;2:函数运算后的结果。 8.5.2、分水岭算法 #include #include #include using namespace cv; using namespace std; #define WINDOW_NAME "【程序窗口】" Mat g_maskImage,g_srcImage; Point prevPt(-1,-1); static void on_Mouse(int event, int x, int y, int flags,void *); static void ShowHelpText() int main(int argc, char**argv) { g_srcImage = imread("1.jpg",1); imshow(WINDOW_NAME,g_srcImage); Mat srcImage,grayImage; g_srcImage.copyTo(srcImage); cvtColor( g_srcImage, g_maskImage,COLOR_BGR2GRAY); cvtColor( g_maskImage, grayImage,COLOR_BGR2GRAY); g_maskImage = Scalar::all(0); //设置鼠标回调函数 setMouseCallback(WINDOW_NAME,on_Mouse,0); while(1) { int c= waitKey(0); if((char) c ==27) { cout<<"程序退出。。。。。。。\n"; break; } //恢复原图 if((char) c =='2') { g_maskImage = Sclar::all(0); srcImage.copyTo(g_srcImage); imshow("image",g_srcImage); } //进行处理 if((char)c == '1' || (char)c == ' ') { int i,j,compCount=0; vector vector //寻找轮廓 findContours(g_maskImage,contours,hierarchy, CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE); //轮廓为空时处理 if(contours.empty()) continue; //复制掩膜 Mat maskImage(g_maskImage.size(),CV_32S); maskImage = Scalar::all(0); //循环绘制出轮廓 for(int index = 0; index>=0;index=hierarchy[index][0], compCount++) drawContours(maskImage,contours,index, Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX); //compCount为0时的处理 if(compCount ==0) continue; //生成随机颜色 vector for(i=0;i { int b = theRNG().uniform(0,255); int q = theRNG().uniform(0,255); int r = theRNG().uniform(0,255); colorTab.push_back(Vec3b((uchar)b, (uchar)g,(uchar)r)); } //计算处理时间并输出到窗口 double dTime = (double) getTickCount(); watershed(srcImage,maskImage); dTime = (double )getTickCount()-dTime; printf("\t处理时间= %gms\n", dTime*1000./getTickFrequency()); //双层循环,将分水岭图像遍历存入watershedImage Mat watershedImage(maskImage.size(),CV_8UC3); for(int i=0;i { for(int j=0;j { int index = maskImage.at if(index==-1) watershedImage.at Vec3b(255,255,255); else if(index<=0 || index>compCount) watershedImage.at Vec3b(0,0,0); else watershedImage.at colorTab[index-1]; } //混合灰度图和分水岭效果图并显示 watershedImage = watershedImage*0.5+grayImage*0.5; imshow("watershed transform",watershedImage); } } return 0; } return 0; } static void on_Mouse(int event, int x,int y ,int flags, void*) { //处理鼠标不在窗口中的情况 if(x<0 || x>=g_srcImage.cols ||y<0||y>=g_srcImage.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(g_maskImage,prevPt,pt,Scalar::all(255),5,8,0); line(g_srcImage,prevPt,pt,Scalar::all(255),5,8,0); prevPt = pt; imshow(WINDOW_NAME,g_srcImage); } } static void ShowHelpText() { printf("\n\n\n\t欢迎来到分水岭示例程序···\n\n"); printf("\t当前opencv版本:"CV_VERSION); printf("\n\n\t按键操作说明:\n\n" // "\t\t 【esc】---退出程序" "\t\t 【1】or 【space】 ---运行的分水岭分割算法" "\t\t 【2】 ---恢复原图" // "\t\t 【3】 ---第3种映射" "\t\t 【esc】 ---退出程序") } 8.6、图像修补 利用那些已经被破坏区域的地缘,即边缘的颜色和结构,繁殖和混合到 损坏的图像中,以达到图像修补的问的。 8.6.1、实现图像修补:inpaint()函数 void inpaint(InputArray src,InputArray inpaintMask, OutputArray dst,double inpaintRadius,int flags) 1:输入图像;2:修复掩膜,其中的非零像素表示要修补的区域;3:函数调用后的运算结果存在这里;4:需要修补的每个点的圆形邻域, 为修复算法的参考半径;5:修补方法的标识符 8.6.2、图像修补示例 #include #include #include using namespace cv; using namespace std; #define WINDOW_NAME1 "【原始图】" #define WINDOW_NAME2 "【修补后的图】" Mat srcImage1,inpaintMask; Point previousPoint(-1,-1); static void on_Mouse(int event, int x, int y, int flags,void *); static void ShowHelpText() int main(int argc, char**argv) { Mat srcImage = imread("1.jpg",-1); srcImage1 = srcImage.clone(); inpaintMask = Mat::zeros(srcImage1.size(),CV_8U); imshow(WINDOW_NAME1,srcImage1); //设置鼠标回调函数 setMouseCallback(WINDOW_NAME1,on_Mouse,0); while(1) { int c= (char) waitKey(); if((char) c ==27) { cout<<"程序退出。。。。。。。\n"; break; } //恢复原图 if((char) c =='2') { inpaintMask = Scalar::all(0); srcImage.copyTo(srcImage1); imshow(WINDOW_NAME1, srcImage1); } //进行处理 if((char)c == '1' || (char)c == ' ') { Mat inpaintedImage; inpaint(srcImage1,inpaintMask,inpaintedImage,3 INPAINT_TELEA); imshow(WINDOW_NAME2, inpaintedImage); } return 0; } return 0; } 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); } } static void ShowHelpText() { printf("\n\n\n\t欢迎来到图像修补示例程序···\n\n"); printf("\t当前opencv版本:"CV_VERSION); printf("\n\n\t按键操作说明:\n\n" "\t\t 【esc】---退出程序" "\t\t 【1】or 【space】 ---进行图像修复操作" "\t\t 【鼠标左键】 ---在图像上绘制白色线条" // "\t\t 【3】 ---第3种映射" //"\t\t 【esc】 ---退出程序") } 8.7、本章小结 参考链接
发表评论