图像识别(10)——UVC预览+曝光滑动调节+Canny阈值调节+圆心打点

博主QQ:1356438802

QQ群:473383394——UVC&OpenCV473383394


平台:Win7 64bits + Visual Studio 2012 + OpenCV 2.4.10


将《图像识别(7)——静态图片识别LED灯+Canny阈值调节+圆心打点》与《图像识别(9)——UVC预览+曝光滑动调节》两份代码结合,就可以得到动态视频的LED光点捕捉,并且绘制圆心点。

//--------------------------------------【程序说明】-------------------------------------------
//		程序说明:《OpenCV3编程入门》OpenCV2版书本配套示例程序07
//		程序描述:使用VideoCapture类调用摄像头读入视频并显示
//		开发测试所用操作系统: Windows 7 64bit
//		开发测试所用IDE版本:Visual Studio 2010
//		开发测试所用OpenCV版本:	2.4.9
//		2014年03月 Created by @浅墨_毛星云
//		2014年11月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------
//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//-------------------------------------------------------------------------------------------------
#include <opencv2\opencv.hpp>  
using namespace cv;  
using namespace std;
#define WINDOW_NAME1 "【原始图窗口】"			//为窗口标题定义的宏 
#define WINDOW_NAME2 "【LED轨迹图】"			//为窗口标题定义的宏 
#define TRACK_BAR
//【1】从摄像头读入视频
VideoCapture capture;
Mat frame;  //定义一个Mat变量,用于存储每一帧的图像
double time0;
int exposure = -10;
Mat g_srcImage; 
Mat g_grayImage;
int g_nThresh = 80;
int g_nThresh_max = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;
#ifdef TRACK_BAR
//滑动条回调函数
void onChange(int pos, void* userdata);
void on_ThreshChange(int, void* );
#endif
//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main( )  
{  
	//对给定的 2D 点集,寻找最小面积的包围圆
	Point2f center;
	float radius = 0;
	//用来绘制圆心点
	Mat drawing;
	bool is_new_draw = true;
	frame = imread("1.jpg", 0);
	// 创建窗口
	namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
	namedWindow( WINDOW_NAME2, CV_WINDOW_AUTOSIZE );
	imshow( WINDOW_NAME1, frame );
	imshow( WINDOW_NAME2, frame );  //显示当前帧
	waitKey(1000);
	capture.open(0);
	if(false == capture.isOpened())
	{
		return -1;
	}	
	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);
	exposure = capture.get(CV_CAP_PROP_EXPOSURE);
	//显示曝光值
	cout << ">设置后: 曝光值= " << exposure << endl;
	double width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
	double height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
	//显示尺寸
	cout << ">宽:= " << width << ";  高: =" << height << endl;
#ifdef TRACK_BAR
	exposure = 50 - exposure;
	createTrackbar("曝光值", WINDOW_NAME1, &exposure, 100, onChange);
	onChange(0, 0);
	createTrackbar( "canny阈值", WINDOW_NAME2, &g_nThresh, g_nThresh_max, on_ThreshChange );
	on_ThreshChange( 0, 0 );
#endif
	//【2】循环显示每一帧
	while(1)  
	{  
		
		time0 = static_cast<double>(getTickCount( ));//记录起始时间
		capture >> frame;  //读取当前帧
		//若视频播放完成,退出循环
		if (frame.empty())
		{
			break;
		}
		imshow(WINDOW_NAME1, frame);  //显示当前帧
		//====================获取LED圆心======================
		// 转成灰度并模糊化降噪
		cvtColor( frame, g_grayImage, CV_BGR2GRAY );
		blur( g_grayImage, g_grayImage, Size(3,3) );
		// 用Canny算子检测边缘
		Canny( g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh*2, 3 );
		// 寻找轮廓
		findContours( g_cannyMat_output, g_vContours, g_vHierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
		// 绘出轮廓或圆心
		if(true == is_new_draw)
		{
			//drawing = Mat::zeros( g_cannyMat_output.size(), CV_8UC3 );
			drawing = Mat( g_cannyMat_output.size(), CV_8UC3, Scalar(255, 255, 255));
			is_new_draw = false;
		}
		Scalar color = Scalar(0, 0, 255 );//红色
		for( int i = 0; i < g_vContours.size(); i++ )
		{
			
			//drawContours( drawing, g_vContours, i, color, 1, 8, g_vHierarchy, 0, Point() );
			//对给定的 2D 点集,寻找最小面积的包围圆
			minEnclosingCircle(Mat(g_vContours[i]), center, radius);
			//画出圆心
			circle( drawing, center, 1, color, CV_FILLED, CV_AA );
			//绘制出最小面积的包围圆
			//circle(drawing, center, cvRound(radius), color, 1, CV_AA);
		}
		// 显示效果图
		imshow( WINDOW_NAME2, drawing );
		//显示帧率
		cout << ">帧率= " << getTickFrequency() / (getTickCount() - time0) << endl;
		char c = (char)waitKey(10);
		if( c == 27 )
			break;
	}  
	return 0;     
}  
#ifdef TRACK_BAR
void onChange(int pos, void* userdata)
{
	exposure = 50 - exposure;	
	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);
}
//-----------------------------------【on_ThreshChange( )函数】------------------------------  
//      描述:回调函数
//----------------------------------------------------------------------------------------------  
void on_ThreshChange(int, void* )
{
	;
}
#endif

上面的代码,如果曝光值调的比较高,会检测到多个轮廓,于是会得到多个圆心点。所以我们需要把曝光值调整到一个合适的值,让画面中只剩下LED光点,那么这个时候其实就只有一个轮廓,也就只有一个圆形点,我们可以修改代码,只打印第一个圆心点:

//--------------------------------------【程序说明】-------------------------------------------
//		程序说明:《OpenCV3编程入门》OpenCV2版书本配套示例程序07
//		程序描述:使用VideoCapture类调用摄像头读入视频并显示
//		开发测试所用操作系统: Windows 7 64bit
//		开发测试所用IDE版本:Visual Studio 2010
//		开发测试所用OpenCV版本:	2.4.9
//		2014年03月 Created by @浅墨_毛星云
//		2014年11月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------
//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//-------------------------------------------------------------------------------------------------
#include <opencv2\opencv.hpp>  
using namespace cv;  
using namespace std;
#define WINDOW_NAME1 "【原始图窗口】"			//为窗口标题定义的宏 
#define WINDOW_NAME2 "【LED轨迹图】"			//为窗口标题定义的宏 
#define TRACK_BAR
//【1】从摄像头读入视频
VideoCapture capture;
Mat frame;  //定义一个Mat变量,用于存储每一帧的图像
double time0;
int exposure = -10;
Mat g_srcImage; 
Mat g_grayImage;
int g_nThresh = 80;
int g_nThresh_max = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;
#ifdef TRACK_BAR
//滑动条回调函数
void onChange(int pos, void* userdata);
void on_ThreshChange(int, void* );
#endif
//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main( )  
{  
	//对给定的 2D 点集,寻找最小面积的包围圆
	Point2f prev_center;
	Point2f center;
	float radius = 0;
	//用来绘制圆心点
	Mat drawing;
	//drawing = Mat::zeros( g_cannyMat_output.size(), CV_8UC3 );
	//drawing = Mat( g_cannyMat_output.size(), CV_8UC3, Scalar(255, 255, 255));
	//当前摄像头的分辨率最大就是640x480
	drawing = Mat( Size(640, 480), CV_8UC3, Scalar(255, 255, 255));
	Scalar color = Scalar(0, 0, 255 );//红色
	frame = imread("1.jpg", 0);
	// 创建窗口
	namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
	namedWindow( WINDOW_NAME2, CV_WINDOW_AUTOSIZE );
	imshow( WINDOW_NAME1, frame );
	imshow( WINDOW_NAME2, frame );  //显示当前帧
	waitKey(1000);
	capture.open(0);
	if(false == capture.isOpened())
	{
		return -1;
	}	
	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);
	exposure = capture.get(CV_CAP_PROP_EXPOSURE);
	//显示曝光值
	cout << ">设置后: 曝光值= " << exposure << endl;
	double width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
	double height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
	//显示尺寸
	cout << ">宽:= " << width << ";  高: =" << height << endl;
#ifdef TRACK_BAR
	exposure = 50 - exposure;
	createTrackbar("曝光值", WINDOW_NAME1, &exposure, 100, onChange);
	onChange(0, 0);
	createTrackbar( "canny阈值", WINDOW_NAME2, &g_nThresh, g_nThresh_max, on_ThreshChange );
	on_ThreshChange( 0, 0 );
#endif
	//【2】循环显示每一帧
	while(1)  
	{  
		
		time0 = static_cast<double>(getTickCount( ));//记录起始时间
		capture >> frame;  //读取当前帧
		//若视频播放完成,退出循环
		if (frame.empty())
		{
			break;
		}
		imshow(WINDOW_NAME1, frame);  //显示当前帧
		//====================获取LED圆心======================
		// 转成灰度并模糊化降噪
		cvtColor( frame, g_grayImage, CV_BGR2GRAY );
		blur( g_grayImage, g_grayImage, Size(3,3) );
		// 用Canny算子检测边缘
		Canny( g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh*2, 3 );
		// 寻找轮廓
		findContours( g_cannyMat_output, g_vContours, g_vHierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
		// 绘出轮廓或圆心
		//for( int i = 0; i < g_vContours.size(); i++ )
		if(g_vContours.size() > 0)
		{
			
			//drawContours( drawing, g_vContours, i, color, 1, 8, g_vHierarchy, 0, Point() );
			//先只考虑对第一个轮廓
			//对给定的 2D 点集,寻找最小面积的包围圆
			minEnclosingCircle(Mat(g_vContours[0]), center, radius);
			//画出圆心
			circle( drawing, center, 1, color, CV_FILLED, CV_AA );
			//绘制出最小面积的包围圆
			//circle(drawing, center, cvRound(radius), color, 1, CV_AA);
		}
		// 显示效果图
		imshow( WINDOW_NAME2, drawing );
		//显示帧率
		cout << ">帧率= " << getTickFrequency() / (getTickCount() - time0) << endl;
		char c = (char)waitKey(10);
		if( c == 27 )
			break;
	}  
	return 0;     
}  
#ifdef TRACK_BAR
void onChange(int pos, void* userdata)
{
	exposure = 50 - exposure;	
	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);
}
//-----------------------------------【on_ThreshChange( )函数】------------------------------  
//      描述:回调函数
//----------------------------------------------------------------------------------------------  
void on_ThreshChange(int, void* )
{
	;
}
#endif


再增加一个鼠标双击回调函数,清空所有圆心点,重新打印:


//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//-----------------------------------------------------------------------------------------
#include <opencv2\opencv.hpp>  
using namespace cv;  
using namespace std;

#define WINDOW_NAME1 "【原始图窗口】"			//为窗口标题定义的宏 
#define WINDOW_NAME2 "【LED轨迹图】"			//为窗口标题定义的宏 

//-----------------------------------------------------------------------------------------
// 全局变量
//-----------------------------------------------------------------------------------------

//从摄像头读入视频
VideoCapture capture;

//计算帧率
double time0;

//曝光值
int exposure = -10;			

//作为绘制圆心点的画布
Mat drawing;

//用于存储摄像头的每一帧图像
Mat g_srcImage; 

//用于存储灰度化处理后的图像
Mat g_grayImage;

//用于存储canny算法的结果
Mat g_cannyMat_output;

//canny阈值和最大值
int g_nThresh = 80;
int g_nThresh_max = 255;

//轮廓向量集
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;


//-----------------------------------------------------------------------------------------
// 局部函数声明
//-----------------------------------------------------------------------------------------
//曝光值变化回调函数
void onExposureChange(int pos, void* userdata);

//canny阈值变化回调函数
void onThreshChange(int, void* );

//鼠标回调函数
void onMouseCallback(int event, int x, int y, int flags, void* userdata);



//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//----------------------------------------------------------------------------------------------
int main( )  
{  
	Point2f prev_center;			//上一次计算出来的中心点
	Point2f center;					//当前计算的中心点
	float radius = 0;				//最小包围圆的半径
	bool first_detect = true;	
	
	//当前摄像头的分辨率最大就是640x480
	drawing = Mat( Size(640, 480), CV_8UC3, Scalar(255, 255, 255));
	Scalar color = Scalar(0, 0, 255 );//红色

	//先加载一张图片,以灰度模式加载
	g_srcImage = imread("1.jpg", 0);
	if (g_srcImage.empty())
	{
		return -1;
	}

	// 创建窗口
	namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
	namedWindow( WINDOW_NAME2, CV_WINDOW_AUTOSIZE );
	imshow( WINDOW_NAME1, g_srcImage );
	imshow( WINDOW_NAME2, g_srcImage );  //显示当前帧
	waitKey(1000);

	//打开摄像头
	capture.open(0);
	if(false == capture.isOpened())
	{
		return -1;
	}	

	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);

	exposure = capture.get(CV_CAP_PROP_EXPOSURE);
	//显示曝光值
	cout << ">设置后: 曝光值= " << exposure << endl;

	double width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
	double height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);

	//显示尺寸
	cout << ">宽:= " << width << ";  高: =" << height << endl;


	exposure = 50 - exposure;
	createTrackbar("曝光值", WINDOW_NAME1, &exposure, 100, onExposureChange);
	onExposureChange(0, 0);

	createTrackbar( "canny阈值", WINDOW_NAME2, &g_nThresh, g_nThresh_max, onThreshChange );
	onThreshChange( 0, 0 );


	//鼠标回调函数——双击清空图片
	setMouseCallback(WINDOW_NAME2, onMouseCallback);

	//循环处理每一帧
	while(1)  
	{  
		
		time0 = static_cast<double>(getTickCount( ));//记录起始时间

		capture >> g_srcImage;  //读取当前帧
		//若视频播放完成,退出循环
		if (g_srcImage.empty())
		{
			break;
		}

		imshow(WINDOW_NAME1, g_srcImage);  //显示当前帧

		//====================获取LED圆心======================
		// 转成灰度并模糊化降噪
		cvtColor( g_srcImage, g_grayImage, CV_BGR2GRAY );
		blur( g_grayImage, g_grayImage, Size(3,3) );

		// 用Canny算子检测边缘
		Canny( g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh*2, 3 );

		// 寻找轮廓
		findContours( g_cannyMat_output, g_vContours, g_vHierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

		// 绘出轮廓或圆心
		//for( int i = 0; i < g_vContours.size(); i++ )
		if(g_vContours.size() > 0)
		{
			
			//先只考虑对第一个轮廓
			//对给定的 2D 点集,寻找最小面积的包围圆
			minEnclosingCircle(Mat(g_vContours[0]), center, radius);

			if(true == first_detect)
			{
				prev_center = center;
				first_detect = false;
			}

			//用当前的圆心和前一次检测的圆心画线
			line(drawing, center, prev_center, color, 1, CV_AA);

			//保存当前的圆心,下次用
			prev_center = center;
			
		}

		// 显示效果图
		imshow( WINDOW_NAME2, drawing );

		//显示帧率
		cout << ">帧率= " << getTickFrequency() / (getTickCount() - time0) << endl;

		char c = (char)waitKey(10);
		if( c == 27 )
			break;
	}  

	return 0;     
}  

//将Mat中的每个元素设置为某个数值
void setMatInt(Mat & input_image, uchar val)
{
	//行数
	int rows = input_image.rows;
	
	//列数x通道数=每一行的元素个数
	int cols = input_image.cols * input_image.channels();

	for(int i = 0; i< rows; i++)
	{
		uchar *pdata = input_image.ptr<uchar>(i);
		for(int j = 0; j < cols; j++)
		{
			pdata[j] = val;
		}
	}

}


//曝光值变化回调函数
void onExposureChange(int pos, void* userdata)
{
	exposure = 50 - exposure;	
	//设置曝光值
	capture.set(CV_CAP_PROP_EXPOSURE, exposure);
}

void onThreshChange(int, void* )
{
	;
}

//鼠标回调函数
void onMouseCallback(int event, int x, int y, int flags, void* userdata)
{
	if(event == EVENT_LBUTTONDBLCLK)
	{
		setMatInt(drawing, 255);
	}
}


这样就比较完美了!


代码整理打包上传:

1. 基于《Win7 64bits + Visual Studio 2012 + OpenCV 2.4.10 》的代码

http://download.csdn.net/detail/luoyouren/9736553


2. 基于《Win7 64bits + Qt 5.3.0 MinGW 32bit + OpenCV 2.4.10 》的代码

http://download.csdn.net/detail/luoyouren/9736660











V4L2的demo capture.c是官方示例程序。 capture.c 程序中的 process_image 函数: capture.c 程序主要是用来演示怎样使用 v4l2 接口,并没有对采集到的视频帧数据做任何实际的处理,仅仅用 process_image 函数表示了处理图像的代码位置。 process_image 函数只有一个参数,就是存储视频帧的内存的地址指针,但是在真正的应用中,通常还需要知道该指针指向的数据的大小。 因此可以修改函数,改成 void process_image ( const void * p, int len ) ,但是每次调用 process_image 的时候,第 2 个参数该传递什么值? 考虑到程序中对 buffer 的定义 struct buffer { void * start; size_t length}; 如果将 buffer.length 作为第 2 个参数传递到修改后的 process_image 函数中,这样做是不正确的。 process_image 需要的第二个参数应该是每帧图像的大小,仔细阅读代码后会发现, buffer.length 并不一定就等于图像帧的大小。 (buffer 的大小,还需要考虑其他的一些因素,比如内存对齐等 )。 capture.c只是一个示例程序,仅仅是演示怎样使用v4l2中最基本的接口。尤其是在main函数中的那几个函数调用,表明了在使用v4l2时的最基本的一个流程,包括 open_device,init_device,start_capturing,mainloop,stop_capturing,uninit_device,close_device。在写程序的时候,可以充分的利用这几个基本模块,把他们分散在不同的代码位置上,灵活的调用,有兴趣的可以看一下gstreamer中v4l2src的源代码或者其他的大型程序的相关部分。 总之一句话,capture.c仅仅是一个演示程序,不要局限于它的代码结构,要灵活的使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值