目录 第八章、图像轮廓与图像分割修复 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> contours;

vector hierarchy;

//查找轮廓

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 points;

//随机生成点坐标

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 hull;

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> g_vContours;

vector g_vHierarchy;

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> hull (g_vContours.size());

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(),0,Point());

drawContours(drawing, hull, i, color,1,8,

vector(),0,Point());

}

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 points;

//随机生成点坐标

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 points;

//随机生成点坐标

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> contours;

vector hierarchy;

//检测边缘

threshold(g_grayImage,threshold_output,

g_nThresh,255,THRESH_BINARY);

//找出轮廓

findContours(threshold_output, contours,hierarchy,

RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));

//多边形逼近轮廓+获取矩形和圆形边界框

vector> contours_poly(contours.size());

vector boundRect(contours.size());

vectorcenter(contours.size());

vector radius(contours.size());

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(),0,Point());//绘制轮廓

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> g_vContours;

vector g_vHierarchy;

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));

//计算矩

vectormu(g_vContours.size());

for(unsigned int i=0;i

{

mu[i]=moments(g_vContours[i],false);

}

//计算中心矩

vector mc(g_vContours.size());

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> contours;

vector hierarchy;

//寻找轮廓

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 colorTab;

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(i,j);

if(index==-1)

watershedImage.at(i,j)=

Vec3b(255,255,255);

else if(index<=0 || index>compCount)

watershedImage.at(i,j)=

Vec3b(0,0,0);

else

watershedImage.at(i,j)=

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、本章小结

参考链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。