opencv第八章

找轮廓
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main( int argc, char** argv ){
	Mat srcImage=imread("1.jpg", 0);//载入原始图,且必须以二值图模式载入
	imshow("原始图",srcImage);
	Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);//初始化结果图
	srcImage = srcImage > 119;//取大于119的部分(注意载入时已经是二值图)
	imshow( "取阈值后的原始图", srcImage );
	vector<vector<Point> > contours;//定义轮廓
	vector<Vec4i> 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);
}

轮廓检测可以使用findContours函数,检测步骤是:

1.  使用拉普拉斯或Canny等边缘检测算子处理图像,获得仅包含边界的二值图像

2.  使用findContorus方法,获取图像所有的边界连续像素序列,并保存在contours向量中

3.  标示出contours向量中所有的轮廓序列

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
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<Point> points; //点值
		for(int i = 0; i < count; 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<int> hull;//检测凸包
		convexHull(Mat(points), hull, true);//输入,输出,默认真(假只返回凸包点数)
		image = Scalar::all(0);//绘制出随机颜色的点
		for(int i = 0; i < count; 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 point0 = points[hull[hullcount-1]];//连接凸包边的坐标点
		for(int  i = 0; i < hullcount; i++ ){//绘制凸包的边
			Point point = points[hull[i]];
			line(image, point0, point, Scalar(255, 255, 255), 2, LINE_AA);
			point0 = point;
		}
		imshow("凸包检测示例", image);//显示效果图
		key = (char)waitKey();//按下ESC,Q,或者q,程序退出
		if( key == 27 || key == 'q' || key == 'Q' )
			break;
	}
	return 0;
}

凸包原理请见ACM

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main(){
	Mat image(600, 600, CV_8UC3);//初始化变量
	RNG& rng = theRNG();//初始化随机值
	while(1){//参数初始化
		int count = rng.uniform(3, 103);//随机生成点的数量
		vector<Point> points;//点值
		for(int  i = 0; i < count; 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);
		}
		RotatedRect box = minAreaRect(Mat(points));//对给定的 2D 点集,寻找最小面积的包围矩形!!!全文关键!
		Point2f vertex[4];
		box.points(vertex);
		image = Scalar::all(0);//绘制出随机颜色的点
		for( int i = 0; i < count; 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 );//显示窗口
		char key = (char)waitKey();	//按下ESC,Q,或者q,程序退出
		if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'
			break;
	}
	return 0;
}
关于最小矩形覆盖的算法:
Graham’s Scan法构建凸包,时间复杂度O(nlogn)
利用旋转卡壳的思想,最小矩形至少存在一条边与凸包共线。所以直接枚举底边,利用旋转卡壳确定其余三个点即可
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main(  ){//初始化变量和随机值
	Mat image(600, 600, CV_8UC3);
	RNG& rng = theRNG();
	while(1){//循环,按下ESC,Q,q键程序退出,否则有键按下便一直更新
		int count = rng.uniform(3, 103);//随机生成点的数量
		vector<Point> points;//点值
		for(int  i = 0; i < count; 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);
		}
		Point2f center;//对给定的 2D 点集,寻找最小面积的包围圆
		float radius = 0;
		minEnclosingCircle(Mat(points), center, radius);//全文关键,算法解释见下文
		image = Scalar::all(0);//绘制出随机颜色的点
		for( int i = 0; i < count; 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 );//显示窗口
		char key = (char)waitKey();//按下ESC,Q,或者q,程序退出
		if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'
			break;
	}
	return 0;
}

矩的定义不难理解,就是每个点与均值之差的K次方和;然后是周长,把点集读一次就不是周长了吗?面积的话,把轮廓点集打上标记,然后任取一个内部点开始深搜就可以知道有多少个内部点了

分水岭:说得简单点,就是从灰度最低(或者用户自义的点)开始填充,填到梯度忽然变大的分割线(边缘)

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "【程序窗口1】"        //为窗口标题定义的宏
#define WINDOW_NAME2 "【分水岭算法效果图】"        //为窗口标题定义的宏
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* );
int main( int argc, char** argv ){
	//【1】载入原图并显示,初始化掩膜和灰度图
	g_srcImage = imread("1.jpg", 1);
	imshow( WINDOW_NAME1, g_srcImage );
	Mat srcImage,grayImage;
	g_srcImage.copyTo(srcImage);
	cvtColor(g_srcImage, g_maskImage, COLOR_BGR2GRAY);
	cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);
	g_maskImage = Scalar::all(0);
	//【2】设置鼠标回调函数
	setMouseCallback( WINDOW_NAME1, 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);
			srcImage.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, RETR_CCOMP, 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( srcImage, maskImage );
			dTime = (double)getTickCount() - dTime;
			printf( "\t处理时间 = %gms\n", dTime*1000./getTickFrequency() );
			//双层循环,将分水岭图像遍历存入watershedImage中
			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( WINDOW_NAME2, watershedImage );
		}
	}
	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_NAME1, g_srcImage);
	}
}
图像修补
#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 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 ){
	//载入原始图并进行掩膜的初始化
	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;
}

图像修补算法思路:

对待修补区域,一层一层地往内填补,收缩,每补一个点,就计算其邻域各点的梯度,利用梯度求出该点预想值,然后综合邻域各点对其预想值(平均或加权平均)得到填充的值

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值