基于仿射变换对摄像头捕捉的图像,利用opencv实现对表盘和指针的识别

源码已经上传[https://gitee.com/facebaby_L/instrument_recognition.git]https://gitee.com/facebaby_L/instrument_recognition.git)

核心函数
//边缘检测
void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )
  • InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。

  • OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和类型。

  • double类型的threshold1,第一个滞后性阈值【低阈值】。值越大,找到的边缘越少

  • double类型的threshold2,第二个滞后性阈值【高阈值】。

  • int类型的apertureSize,表示应用Sobel算子的孔径大小,其有默认值3。

  • bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false。

    低于阈值1的像素点会被认为不是边缘;
    高于阈值2的像素点会被认为是边缘;
    在阈值1和阈值2之间的像素点,若与第2步得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘。

//霍夫圆检测
void HoughCircles( CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1=100, double param2=100, int min_radius=0, int max_radius=0 );
  • image:输入 8-比特、单通道灰度图像
  • circle_storage:检测到的矩阵,3个参数为圆心坐标(x,y)和半径
  • method:Hough 变换方式,目前只支持CV_HOUGH_GRADIENT
  • dp:累加器图像的分辨率。这个参数允许创建一个比输入图像分辨率低的累加器。(这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。
  • min_dist:该参数是让算法能明显区分的两个不同圆之间的最小距离。
  • param1:用于Canny的边缘阀值上限,下限被置为上限的一半。
  • param2:累加器的阀值。
  • min_radius:最小圆半径。
  • max_radius:最大圆半径。
//霍夫直线检测
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
  • image:8位的单通道二进制图像。
  • lines:经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的端点。
  • rho:以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。
  • theta:以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • threshold:累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • minLineLength:有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
  • maxLineGap:有默认值0,允许将同一行点与点之间连接起来的最大的距离。
//找到两个平面之间的转换矩阵,返回值为矩阵。
Mat findHomography(InputArray srcPoints,InputArray dstPoints,int method = 0)
  • srcPoints :源平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector类型
  • dstPoints :目标平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector类型
  • method :计算单应矩阵所使用的方法。不同的方法对应不同的参数,具体如下:0 - 利用所有点的常规方法,RANSAC - RANSAC-基于RANSAC的鲁棒算法,LMEDS - 最小中值鲁棒算法,RHO - PROSAC-基于PROSAC的鲁棒算法。
//透视变换
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR)
  • src:输入的图像
  • dst:输出的图像
  • M:透视变换的矩阵
  • dsize:输出图像的大小
  • INTER_LINEAR:输出图像的插值方法

如果知道输出图像的四点坐标,也可以调用GetPerspectiveTransform

//由四对点计算透射变换
perspectiveTransform(vector obj_corners1(4), vector scene_corners1(4), InputArray M)
  • obj_corners1(4):模板图角点坐标。
  • scene_corners1(4):待测图角点坐标
  • M:转换矩阵
准备

因为不论是霍夫圆还是霍夫直线检测,都要先进行边缘提取,那么就涉及到一个参数选择问题,为了更好的选择参数,我们利用如下的参数选取代码,来确定得到最佳边缘分割的参数。

#include   
#include   
#include   
#include 

using namespace std;
using namespace cv;

// Define the Mat parameters
Mat g_srcImage, g_dstImage, g_midImage;
// Define a vector to collect all detected lines
vector<Vec4i> g_lines;
int g_nthreshold = 100;

static void showImage1(int, void*);
static void showImage2(int, void*);
int thred1 = 23;
int thred2 = 55;
// 23 55
int main()
{
	// Load origin image   
	g_srcImage = imread("2.jpg");
	// Show origin image
	namedWindow("Dst Image", 1);
	imshow("Origin Image", g_srcImage);
	// Create a Tracebar
	createTrackbar("threshold1", "Dst Image", &thred1, 500, showImage1);
	createTrackbar("threshold2", "Dst Image", &thred2, 500, showImage2);
	// Canny Detect and Gray am image  
	Canny(g_srcImage, g_midImage, thred1, thred2, 3);
	cvtColor(g_midImage, g_dstImage, CV_GRAY2BGR);
	showImage1(thred1, 0);
	showImage2(thred2, 0);
	// Show the dst image
	imshow("Dst Image", g_dstImage);
	waitKey(0);
	return 0;
}

static void showImage1(int thred1, void*) {
	// Canny Detection
	Canny(g_srcImage, g_midImage, thred1, thred2, 3);
	imshow("Dst Image", g_midImage);
}

static void showImage2(int thred2, void*) {
	// Canny Detection
	Canny(g_srcImage, g_midImage, thred1, thred2, 3);
	imshow("Dst Image", g_midImage);
}

基于仿射变换对摄像头捕捉的图像,利用opencv实现对表盘和指针的识别_第1张图片

代码

首先上不加摄像头,直接识别模板的代码。

#include
#include
#include
#include 
#include "math.h"

using namespace std;
using namespace cv;

int main()
{
	Mat orgImage,rayImage,canImage;
	orgImage = imread("1.jpg");
	if (orgImage.empty())
	{
		cout << "could not find Image" << endl;
		return -1;
	}
	imshow("【原始图】", orgImage);

	cvtColor(orgImage, rayImage, CV_BGR2GRAY);//转化边缘检测后的图为灰度图
	GaussianBlur(rayImage, rayImage, Size(9, 9), 2, 2);
	vector<Vec3f> circles;
	// 霍夫圆
	HoughCircles(rayImage, circles, CV_HOUGH_GRADIENT, 1, rayImage.rows, 100, 200, 0, 0);


	//霍夫直线   canny边缘提取
	Canny(orgImage, canImage, 250, 200, 3);
	vector<Vec4f> lines;

	//'1'生成极坐标时候的像素扫描步长,'CV_PI/180'生成极坐标时候的角度步长,'20'最小直线长度,'5'最大间隔(能构成一条直线) 
	HoughLinesP(canImage, lines, 1, CV_PI / 180, 100, 20, 5);

	Point center(cvRound(circles[0][0]), cvRound(circles[0][1]));
	Point destination;
	int radius = cvRound(circles[0][2]);
	float length = 0.0;
	float max_length = 0.0;

	// 找出所有直线中最长的一条
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4f plines = lines[i];  // 一个plines里边是四个点一条直线 
		if (plines[0] > center.x - radius && plines[0] < center.x + radius &&
			plines[1] > center.y - radius && plines[1] < center.y + radius &&
			plines[2] > center.x - radius && plines[2] < center.x + radius &&
			plines[3] > center.y - radius && plines[3] < center.y + radius)
		{
			// line(orgImage, Point(plines[0],plines[1]), Point(plines[2], plines[3]), color, 1, LINE_AA);
			length = sqrt((plines[2] - plines[0])*(plines[2] - plines[0]) + (plines[3] - plines[1])*(plines[3] - plines[1]));
			if (length > max_length)
			{
				max_length = length;
				if ((plines[0]-center.x)*(plines[0] - center.x)+ (plines[1] - center.y)*(plines[1] - center.y)
					>(plines[2] - center.x)*(plines[2] - center.x) + (plines[3] - center.y)*(plines[3] - center.y))
				{
					destination.x = plines[0];
					destination.y = plines[1];
				}
				else
				{
					destination.x = plines[2];
					destination.y = plines[3];
				}
			}
		}
	}
	line(orgImage, center, destination, Scalar(0, 0, 255), 3, LINE_AA);

	//绘制圆心
	circle(orgImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
	//绘制圆轮廓
	circle(orgImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);


	imshow("【效果图】", orgImage);
	waitKey(0);
	return 0;
}
结果

利用模板匹配进行摄像头识别代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 


using namespace std;
using namespace cv;

double pi = 3.1415926;
double dtheta = 100 / pi;// 一弧度的分度值
double return_degree(Point starting_Point, Point ending_Point, double dtheta);


int main()
{
	VideoCapture capture(0);
	//namedWindow("识别结果", WINDOW_NORMAL);

	Mat srcImage1 = imread("1.jpg"), resVideo, procVideo;

	std::vector<cv::KeyPoint> keypoints_object1, keypoints_scene;//vector模板类,存放任意类型的动态数组
	cv::Mat descriptors_object1, descriptors_scene;

	while (true)
	{
		capture >> resVideo;
		imshow("【原始图】", resVideo);

		cv::Ptr<cv::Feature2D>surf_f2d = cv::xfeatures2d::SURF::create(500);//选择SURF特征
		surf_f2d->detectAndCompute(srcImage1, cv::Mat(), keypoints_object1, descriptors_object1);//检测关键点并计算描述符
		surf_f2d->detectAndCompute(resVideo, cv::Mat(), keypoints_scene, descriptors_scene);

		cv::FlannBasedMatcher matcher;//基于Flann的描述符匹配器
		std::vector<cv::DMatch> matches1;
		matcher.match(descriptors_object1, descriptors_scene, matches1);//从查询集中查找每个描述符的最佳匹配

		double max_dist1 = 0, min_dist1 = 100;//最小距离和最大距离
		for (int i = 0; i < descriptors_object1.rows; i++)//计算出关键点之间距离的最大值和最小值
		{
			double dist = matches1[i].distance;
			if (dist < min_dist1) min_dist1 = dist;
			if (dist > max_dist1) max_dist1 = dist;
		}

		std::vector<cv::DMatch> good_matches1, good_matches2, nomatches;//存下匹配距离小于3*min_dist的点对
		for (int i = 0; i < descriptors_object1.rows; i++)
		{
			if (matches1[i].distance < 2 * min_dist1)
			{
				good_matches1.push_back(matches1[i]);
			}
		}

		std::vector<cv::Point2f> obj1, obj2, scene;
		for (unsigned int i = 0; i < good_matches1.size(); i++)//从匹配成功的匹配对中获取关键点
		{
			obj1.push_back(keypoints_object1[good_matches1[i].queryIdx].pt);
			scene.push_back(keypoints_scene[good_matches1[i].trainIdx].pt);
		}

		if (true/*good_matches1.size()>50*/) {
			//std::vector obj_corners1(4), scene_corners1(4);//从待测图片中获取角点
			//obj_corners1[0] = cvPoint(0, 0);
			//obj_corners1[1] = cvPoint(srcImage1.cols, 0);
			//obj_corners1[2] = cvPoint(srcImage1.cols, srcImage1.rows);
			//obj_corners1[3] = cvPoint(0, srcImage1.rows);

			//Mat Homography1 = findHomography(obj1, scene, RANSAC);//计算透视变换
			//perspectiveTransform(obj_corners1, scene_corners1, Homography1);//进行透视变换

			Mat Homography1 = findHomography(scene, obj1, RANSAC);//计算透视变换
			warpPerspective(resVideo, resVideo, Homography1, resVideo.size(), INTER_LINEAR);//待测图片线性插值扭正
			imshow("【扭正图】", resVideo);																	//			drawMatches(srcImage1, keypoints_object1, resVideo, keypoints_scene,//绘制出匹配到的关键点
																				//				good_matches1, resVideo, cv::Scalar::all(-1), cv::Scalar::all(-1),
																				//				std::vector(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);


			//line(resVideo, scene_corners1[0] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[1] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
			//line(resVideo, scene_corners1[1] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[2] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
			//line(resVideo, scene_corners1[2] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[3] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
			//line(resVideo, scene_corners1[3] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[0] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);

			cvtColor(resVideo, procVideo, COLOR_BGR2GRAY);
			GaussianBlur(procVideo, procVideo, Size(9, 9), 2, 2);
			medianBlur(procVideo, procVideo, 7);

			vector<Vec3f> circles;
			HoughCircles(procVideo, circles, CV_HOUGH_GRADIENT, 1, procVideo.rows/10, 10, 100, 0, 0);

			if (!circles.empty())
			{
				Canny(procVideo, procVideo, 50, 80, 3); // Canny边缘提取,test测试最佳系数
				vector<Vec4f>lines;
				//imshow("边缘检测",procVideo);

				HoughLinesP(procVideo, lines, 1, CV_PI / 180, 50, 30, 5); // 霍夫直线

				Point center(cvRound(circles[0][0]), cvRound(circles[0][1]));
				Point destination;
				double radius = cvRound(circles[0][2]);
				double length = 0.0;
				double max_length = 0.0;

				//指针识别
				for (size_t i = 0; i < lines.size(); i++)
				{
					Vec4f plines = lines[i];  // 一个plines里边是四个点一条直线 
					if (plines[0] > center.x - radius && plines[0] < center.x + radius &&
						plines[1] > center.y - radius && plines[1] < center.y + radius &&
						plines[2] > center.x - radius && plines[2] < center.x + radius &&
						plines[3] > center.y - radius && plines[3] < center.y + radius)
					{
						// line(orgImage, Point(plines[0],plines[1]), Point(plines[2], plines[3]), color, 1, LINE_AA);
						length = sqrt((plines[2] - plines[0])*(plines[2] - plines[0]) + (plines[3] - plines[1])*(plines[3] - plines[1]));
						if (length > max_length)
						{
							max_length = length;
							if ((plines[0] - center.x)*(plines[0] - center.x) + (plines[1] - center.y)*(plines[1] - center.y)
		> (plines[2] - center.x)*(plines[2] - center.x) + (plines[3] - center.y)*(plines[3] - center.y))
							{
								destination.x = plines[0];
								destination.y = plines[1];
							}
							else
							{
								destination.x = plines[2];
								destination.y = plines[3];
							}
						}
					}
				}

				// 求夹角
				double degree = return_degree(center, destination, dtheta);
				string s = to_string(degree);

				// 绘制指针
				line(resVideo, center, destination, Scalar(0, 0, 255), 3, LINE_AA);

				// 绘制圆心
				circle(resVideo, center, 3, Scalar(0, 255, 0), -1, 8, 0);

				// 绘制圆轮廓
				circle(resVideo, center, radius, Scalar(155, 50, 255), 3, 8, 0);

				/*line(resVideo, scene_corners1[0], scene_corners1[1], cv::Scalar(255, 0, 123), 4);
				line(resVideo, scene_corners1[1], scene_corners1[2], cv::Scalar(255, 0, 123), 4);
				line(resVideo, scene_corners1[2], scene_corners1[3], cv::Scalar(255, 0, 123), 4);
				line(resVideo, scene_corners1[3], scene_corners1[0], cv::Scalar(255, 0, 123), 4);*/

				// 显示数值
				putText(resVideo, s, Point(resVideo.rows / 3, resVideo.cols / 2), FONT_HERSHEY_PLAIN, 2, (0, 255, 255), 1, 8);

				imshow("【效果图】", resVideo);
			}
				waitKey(1);
		}
	}
	return 0;
}

double return_degree(Point starting_Point, Point ending_Point, double dtheta)
{
	double k = abs((double)(ending_Point.y - starting_Point.y) / (double)(ending_Point.x - starting_Point.x));
	double theta = atan(k);
	if (starting_Point.x > ending_Point.x && starting_Point.y > ending_Point.y)
	{
		return (10 + theta*dtheta);
	}
	else if (starting_Point.x < ending_Point.x && starting_Point.y < ending_Point.y)
	{
		return (10 + (pi - theta)*dtheta);
	}
	else if (starting_Point.x < ending_Point.x && starting_Point.y < ending_Point.y)
	{
		return (10 + (pi + theta)*dtheta);
	}
	else if (starting_Point.x > ending_Point.x && starting_Point.y < ending_Point.y)
	{
		return (10 - theta*dtheta);
	}
}

结果
基于仿射变换对摄像头捕捉的图像,利用opencv实现对表盘和指针的识别_第2张图片

你可能感兴趣的:(OpenCV)