OpenCV3学习笔记(八)---------图像轮廓与图像分割修复

1、【查找并绘制轮廓】

寻找轮廓:findContours()函数-------用于在二值图像中寻找轮廓

void findContours(
InputOutputArray image,                //输入图像
OutputArrayOfArrays contours,    //检测到的轮廓、函数调用后的运算结果存在这里
OutputArray hierarchy,           //可选的输出向量,包含图像的拓普信息
int mode,                        //轮廓检索模式
int method,                      //为轮廓的近似办法
Point offset=Point()             //每个轮廓点的可选偏移量
);

参数四:可选的轮廓检索模式
①RETR_EXTERNAL----表示只检测最外面的轮廓。对所有轮廓,设置hierarchy[i][2]=hierarchy[i][3]=-1
②RETR_LIST------提取所有轮廓,并且放置在list中。检测的轮廓不建立等级关系
③RETR_CCOMP-----提取所有轮廓,并且将其组织为双层结构(顶层为连通域的外围边界;次层为孔的内层边界)
④RETR_TREE------提取所有轮廓,并重新建立网状的轮廓结构
参数五:可选的轮廓的近似办法
①CHAIN_APPROX_NONE------获取每个轮廓的每个像素,相邻的两点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
②CHAIN_APPROX_SIMPLE-----压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标(eg:矩形轮廓只需4个点来保存轮廓)
③CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS-----使用Teh-Chinl链逼近算法中的一个

绘制找轮廓:drawdContours()函数--------用于在图像中绘制外部或内部轮廓

void drawdContours(
InputOutputArray image,         //输入图像
InputArrayOfArrays contours,    //所有的输入轮廓
int contourIdx,                 //轮廓绘制的指示变量
const Scalar& color,            //轮廓的颜色
int thickness=1,                //轮廓线条的粗细度
int lineType=8,                 //线条的类型(默认8---8连通线性;4;LINE_AA----抗锯齿线型)
InputArray hierarchy=noArray(), //可选的层次结构信息(默认noArray())
int maxLevel=INT_MAX,           //表示用于轮廓绘制的最大等级(默认INT_MAX)
Point offset=Point()            //每个轮廓点的可选偏移量
);

查找并绘制轮廓综合示例代码:

//-----------------------【查找并绘制轮廓】-----------------------

#include <opencv2/opencv.hpp>
#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, g_grayImage;
int g_nThresh = 80, g_nThresh_max = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;

//--------------------【全局函数声明部分】----------------
//    描述:全局函数声明
//--------------------------------------------------------
static void ShowHelpText();
void on_ThreshChange(int,void*);

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc,char** argv) {
	
	//改变console的颜色
	system("color 1F");

	//显示欢迎和帮助文档
	ShowHelpText();

	//载入原图
	g_srcImage = imread("juan.jpg");
	if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }

	//转换灰度并模糊化降噪
	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("canny阈值",WINDOW_NAME1,&g_nThresh,g_nThresh_max,on_ThreshChange);
	on_ThreshChange(0,0);

	waitKey (0);
	return 0;
}

//--------------------------【on_ThreshChange()函数】----------------------
//     描述:回调函数
//-----------------------------------------------------------------------
void on_ThreshChange(int,void*) {

	//用Canny算子检测边缘
	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));

	//绘出轮廓
	Mat drawing = Mat::zeros(g_cannyMat_output.size(),CV_8UC3);
	for (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());
	}

	//显示图像
	imshow(WINDOW_NAME2,drawing);
}

//--------------------------【ShowHelpText()函数】----------------------
//     描述:输出一些帮助信息
//-----------------------------------------------------------------------
static void ShowHelpText() {
	//输出一些帮助信息
	printf("\n\n\n\t欢迎来到【在图形中寻找轮廓】示例程序~\n\n");
	printf("\t当前使用的opencv版本为opencv3.4.1");
	printf("\n\n\t按键操作说明:\n\n"
		"\n\n\t按键盘任意键-退出程序\n\n"
		"\t\t滑动滚动条改变阈值\n\n");

}

2、【凸包】

给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它是包含点集中所有点的。
寻找凸包:convexHull()函数---用于寻找图像点集中的凸包

void convexHull(
InputArray points,    //输入的二维点集
OutputArray hull,     //输出参数,函数调用后找到的凸包
bool clockwise=false, //操作方向标识符(为真时,凸包为顺时针方像。否则逆时针)
bool returnPoints=true//操作标识符(默认true--返回各凸包的各个点,否则返回凸包个点的指数)
);

寻找和绘制物体的凸包示例代码:

//-------------------------【寻找和绘制物体的凸包】------------------
#include <opencv2/opencv.hpp>
#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, g_grayImage;
int g_nThresh =50, 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;

//--------------------【全局函数声明部分】----------------
//    描述:全局函数声明
//--------------------------------------------------------
void on_ThreshChange(int, void*);

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {

	//载入原图
	g_srcImage = imread("juan.jpg");
	if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }

	//转换灰度并模糊化降噪
	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;
}

//--------------------------【on_ThreshChange()函数】----------------------
//     描述:回调函数
//-----------------------------------------------------------------------
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);
}

3、【使用多边形将轮廓包围】

返回外部矩形边界:boundingRect()函数----计算并返回指定点集最外面的矩形边界

Rect boundingRect(InputArray points);//参数:输入的二维点集

寻找最小包围矩形:minAreaRect()函数----用于对给定的2D点集,寻找可旋转的最小面积的包围矩形

RotatedRect minAreaRect(InputArray points);//参数:输入的二维点集

寻找最小包围圆形:minEnclosingCircle()函数----利用一种迭代算法对给定的2D点集,去寻找面积最小的可包围他们的圆形

void minEnclosingCircle(InputArray points,Point2f& center,float& radius);//参数:输入的二维点集、圆心、圆半径

用椭圆拟合二维点集:fitEllipse()函数

RotatedRect fitEllipse(InputArray points);

逼近多边形曲线:approxPolyDP()函数
 

void approxPolyDP(
InputArray curve,   //输入的二维点集
OutputArray approxCurve,//多边形逼近的结果
double epsilon, //逼近的精度
bool closed  //为真则近似的曲线为封闭曲线,否则不封闭
);

 使用多边形包围轮廓示例代码:

//----------------【使用多边形包围轮廓】-----------------

#include <opencv2/opencv.hpp>
#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, g_grayImage;
int g_nThresh = 50, g_maxThresh = 255;
RNG g_rng(12345);//随机数生成器

//--------------------【全局函数声明部分】----------------
//    描述:全局函数声明
//--------------------------------------------------------
void on_ContoursChange(int, void*);
static void ShowHelpText();

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {

	//改变console字体颜色
	system("color 1A");

	//载入原图
	g_srcImage = imread("juan.jpg",1);
	if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }

	//转换灰度并模糊化(平滑)降噪
	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;
}

//--------------------------【on_ContoursChange()函数】----------------------
//     描述:回调函数
//-----------------------------------------------------------------------
void on_ContoursChange(int, void*) {

	//定义一些参数
	Mat threshold_output;
	vector<vector<Point>> contours;
	vector<Vec4i> 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<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]));//计算点集的最外面矩形边界
		minEnclosingCircle(contours_poly[i],center[i],radius[i]);//对给定的2D点集,寻找最小面积的包围圆形

	}

	//绘制多边形轮廓+包围的矩形框+圆形框

	Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
	for (unsigned int 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);
}

4、【查找和绘制图像轮廓矩】

一个从一幅数字图像中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量的关于该图像不同类型的几何特性信息(大小、位置、方向及形状)。
一阶矩与形状有关;
二阶矩显示曲线围绕直线平均值的扩展程度;
三阶矩是关于平均值的对称性的测量。
由二阶矩和三阶矩可以导出一组共7个不变矩阵
不变矩是图像的统计特征,满足平移、伸缩、旋转均不变的不变性。

矩的计算:moments()函数------用于计算多边形和光栅形状的最高达三阶的所有矩。
矩用来计算形状的重心、面积,主轴和其它形状特征。

Moments moments(InputArray array,bool binaryImage=false);

参数一:输入参数,可以是光栅图像(单通道,8位或浮点的二维数组)或二维数组。
参数二:默认值false。若此参数取true,则所有非零像素为1.此参数仅对于图像使用。

计算轮廓面积:contourArea()函数------用于计算整个轮廓或部分轮廓的面积

double contourArea(InputArray contour,bool oriented=false);

参数一:输入的向量,二维点
参数二:面向区域标识符。为true时返回一个带符号的面积值,其正负取决于轮廓的方向。根据这个特性
我们可以根据面积的符号来确定轮廓的位置。为false时,以绝对值返回,不带符号。

计算轮廓长度:arcLength()函数-----用于计算封闭轮廓的周长或曲线的长度。

double srcLength(InputArray curve,bool closed);

参数一:输入的二维点集
参数二:一个用于指定曲线是否封闭的标识符(默认closed---表示曲线封闭)

查找和绘制图像轮廓矩综合示例代码: 

//--------------------【查找和绘制图像轮廓矩】----------------------

#include <opencv2/opencv.hpp>
#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, g_grayImage;
int g_nThresh = 100, 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();

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {

	//改变console字体颜色
	system("color 1E");

	//载入原图
	g_srcImage = imread("lenna.jpg", 1);
	if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }

	//转换灰度并模糊化(平滑)降噪
	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;
}

//--------------------------【on_ThreshChange()函数】----------------------
//     描述:回调函数
//-----------------------------------------------------------------------
void on_ThreshChange(int, void*) {

	//使用Canny检测边缘
	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);//绘制圆
	}
}

5、【分水岭算法】

分水岭算法---是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学
上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域
称为集水盆,而集水盆的边界则形成分水岭。
分水岭表示的是输入图像的极大值点。
首先计算灰度图像的梯度,然后开始从用户指定点开始持续“灌注”盆地直到这些区域连成片。
基于这样产生的标记就可以把区域合并到一起,合并后的区域又通聚集的方式进行分割,好像
被“填充”起来一样。

实现分水岭算法:watershed()函数---- 一种基于标记的分割算法。
在把图像传给函数之前,我们需要大致勾画标记处图像中的期望进行分割的区域,它们被标记为正指数。

void watershed(InputArray image,InputOutArray markers);

参数一:输入图像
参数二:函数调用后的运算结果存入地,输入或输出32位单通道图像的标记结果。
 

 分水岭算法综合示例:

//-----------------------【分水岭算法】-------------------

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//--------------------【宏定义部分】-----------------
//    描述:定义一些辅助宏
//---------------------------------------------------
#define WINDOW_NAME "【原始图窗口】"      //为窗口标题定义的宏

//--------------------【全局变量声明部分】----------------
//    描述:全局变量声明
//--------------------------------------------------------
Mat g_maskImage, g_srcImage;
Point prevPt(-1,-1);

//--------------------【全局函数声明部分】----------------
//    描述:全局函数声明
//--------------------------------------------------------
static void ShowHelpText();
static void on_Mouse(int event,int x,int y,int flags,void*);

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {

	//【1】载入原图
	g_srcImage = imread("lenna.jpg");
	if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }
	imshow(WINDOW_NAME, g_srcImage);
	Mat srcIamge, grayImage;
	g_srcImage.copyTo(srcIamge);
	cvtColor(g_srcImage,g_maskImage,COLOR_BGR2GRAY);
	cvtColor(g_maskImage, grayImage,COLOR_GRAY2BGR);
	g_maskImage = Scalar::all(0);
	
	//【2】设置鼠标回调函数
	setMouseCallback(WINDOW_NAME,on_Mouse,0);

	//【3】轮询按键,进行处理
	while (1) {
	
		//获取键值
		int c = waitKey(0);

		//若按键键值为ESC时,退出
		if ((char)c == 27)
			break;
		
		//按键键值为2时,恢复原图
		if ((char)c == '2') {
			g_maskImage = Scalar::all(0);
			srcIamge.copyTo(g_srcImage);
			imshow("image",g_srcImage);
		}

		//按键键值为1或者空格时,进行处理
		if ((char)c == '1'|| (char)c == ' ') {
			//定义一些参数
			int i, j, compCount = 0;
			vector<vector<Point>> contours;
			vector<Vec4i> 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为零时的处理
			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 dTime = (double)getTickCount();
			watershed(srcIamge,maskImage);
			dTime=(double)getTickCount()-dTime;
			printf("\t处理时间=%gms\n",dTime*1000./getTickFrequency()); 

			//双层循环,将分水岭图像遍历存储到watersheImage中
			Mat watershedImage(maskImage.size(),CV_8UC3);
			for (i = 0; i < maskImage.rows;i++) 
				for (j = 0; j < maskImage.cols;j++) {
					int index = maskImage.at<int>(i, j);
					if (index == -1)
						watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
					else if(index<=0||index>compCount)
						watershedImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
					else
						watershedImage.at<Vec3b>(i, j) = colorTab[index-1];
				}
				//混合灰度图像和分水岭效果图并显示最终的窗口
				watershedImage = watershedImage * 0.5 + grayImage * 0.5;
				imshow("watershed transform", watershedImage);
			}			
		}
		return 0;
	}


//--------------------------【on_Mouse()函数】----------------------
//     描述:鼠标消息回调函数
//-----------------------------------------------------------------------
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);
	}
}
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;
Vec3b RandomColor(int value);

int main(int argc, char* argv) {
	
Mat image = imread("lenna.jpg", 1);
	imshow("原图像", image);

	//灰度化,滤波,Canny边缘检测  
	Mat imageGray;
	cvtColor(image, imageGray, CV_RGB2GRAY);//灰度转换  
	GaussianBlur(imageGray, imageGray, Size(5, 5), 2);
	imshow("灰度图像", imageGray);
	Canny(imageGray, imageGray, 80, 150);
	imshow("canny图像", imageGray);

	//查找轮廓  
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(imageGray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	Mat imageContours = Mat::zeros(image.size(), CV_8UC1);
	Mat marks(image.size(), CV_32S);
	marks = Scalar::all(0);
	int index = 0;
	int compCount = 0;
	for (; index >= 0; index = hierarchy[index][0], compCount++)
	{
		//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点  
		drawContours(marks, contours, index, Scalar::all(compCount + 1), 1, 8, hierarchy);
		drawContours(imageContours, contours, index, Scalar(255), 1, 8, hierarchy);
	}

	//显示传入的矩阵marks  
	Mat marksShows;
	convertScaleAbs(marks, marksShows);
	imshow("marksShow", marksShows);
	imshow("轮廓", imageContours);
	watershed(image, marks);

	//分水岭算法之后的矩阵marks 

	Mat afterWatershed;
	convertScaleAbs(marks, afterWatershed);
	imshow("分水岭处理过的图片", afterWatershed);
	
//对每一个区域进行颜色填充  
	Mat PerspectiveImage = Mat::zeros(image.size(), CV_8UC3);
	for (int i = 0; i < marks.rows; i++)
	{
		for (int j = 0; j < marks.cols; j++)
		{
			int index = marks.at<int>(i, j);
			if (marks.at<int>(i, j) == -1)
			{
				PerspectiveImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
			}
			else
			{
				PerspectiveImage.at<Vec3b>(i, j) = RandomColor(index);
			}
		}
	}
	imshow("颜色填充后的图像", PerspectiveImage);

	//分割并填充颜色的结果跟原始图像融合  
	Mat wshed;
	addWeighted(image, 0.4, PerspectiveImage, 0.6, 0, wshed);
	imshow("组合图片", wshed);

	waitKey();
	return 0;
}

Vec3b RandomColor(int value) {
	value = value % 255;
	RNG rng;
	int aa = rng.uniform(0, value);
	int bb = rng.uniform(0, value);
	int cc = rng.uniform(0, value);
	return Vec3b(aa, bb, cc);
}

 6、【图像修补】


图像修复就是利用那些已经被破坏区域的边缘,即边缘的颜色和结构,繁殖和混合到损坏的图像中,已达到图像修补的目的。

实现图像修补:inpaint()函数----用来从扫描的照片中清除灰尘和划痕,或者从静态图像或视频中去除不需要的物体。
 

void inpaint(
InputArray src,          //输入图像
InputArray inpaintMask,  //修复掩模。其中的非零像素表示需要修补的区域
OutputArray dst,         //修复的结果图
double inpaintRadius,    //需要修补的每个点的圆形邻域,为修复算法的参考半径
int flags                //修补方法的标识符(INPAINT_NS---基于Navier-Stokes方程的方法,或INPAINT_TELEA---Alexandru Telea)
);

图像修补综合示例代码:

//-----------------------【图像修补】----------------------

#include <opencv2/opencv.hpp>
#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_NAME1 "【原始图窗口】"      //为窗口标题定义的宏
#define WINDOW_NAME2 "【修补后的图像窗口】"      //为窗口标题定义的宏

//--------------------【全局变量声明部分】----------------
//    描述:全局变量声明
//--------------------------------------------------------
Mat srcImage1, inpaintMask;
Point previousPoint(-1,-1);//原来的点坐标

//--------------------【全局函数声明部分】----------------
//    描述:全局函数声明
//--------------------------------------------------------
void On_Mouse(int event,int x,int y,int flags,void*);
static void ShowHelpText();

//--------------------【main()函数】-----------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {

	//改变console字体颜色
	system("color 1A");

	//载入原图
	Mat srcImage = imread("juan.jpg", 1);
	if (!srcImage.data) { printf("读取图片错误 \n"); return false; }

	srcImage1 = srcImage.clone();
	inpaintMask = Mat::zeros(srcImage1.size(),CV_8U);

    //显示原图
	imshow(WINDOW_NAME1,srcImage1);
	namedWindow("yanma");
	imshow("yanma", inpaintMask);
	//设置鼠标回调函数
	setMouseCallback(WINDOW_NAME1,On_Mouse,0);

	//轮询按键,根据不同的按键进行处理
	while (1)
	{
		//获取按键键值
		char c = (char)waitKey();

		//按键为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;
}

//--------------------------【On_Mouse()函数】----------------------
//     描述:相应鼠标消息的回调函数
//-----------------------------------------------------------------------
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),1,8,0);
		line(srcImage1, previousPoint, pt, Scalar::all(255), 1, 8, 0);
		imshow(WINDOW_NAME1,srcImage1);
	}
		
}

 

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值