指针仪表检测算法代码

#include <iostream>
#include<opencv2/opencv.hpp>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/photo.hpp"
#include<math.h>
#include<Windows.h>
#include<cstdio>

#define PI acos(-1)

using namespace std;
using namespace cv;


int hmin = 0, hmax = 180, smin = 0, smax = 255, vmin = 90, vmax = 255;
int g_nStructElementSize = 3;
int g_nGaussianBlurValue = 6;
int c;
int l;
Point a[3];

Point origion_point;
Point distortion;
Point origionBigin_End;
Point outputBigin_End;

Mat H;

Vec4d get_line;
Vec4d origion_line;
Vec4d other_line;

/*
*	左击鼠标操作
*/
int i = 1;
int j = 1;
struct userdata {
	Mat im;
	vector<Point2f> points;
};

struct userdata1 {
	Mat im1;
	vector<Point2f> points1;
};

//打开文件
string openFile()
{
	char szFileName[MAX_PATH] = { 0 };
	OPENFILENAME openFileName = { 0 };
	openFileName.lStructSize = sizeof(OPENFILENAME);
	openFileName.nMaxFile = MAX_PATH;
	openFileName.lpstrFilter = "图片文件(*.jpg)\0*.jpg\0所有文件(*.*)\0\0";
	openFileName.lpstrFile = szFileName;
	openFileName.nFilterIndex = 1;
	openFileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	if (::GetOpenFileName(&openFileName))
	{
		//::MessageBoxA(NULL, openFileName.lpstrFile, "", MB_OK);
		//cout << openFileName.lpstrFile << endl;
	}
	return openFileName.lpstrFile;
}

Mat Distortion_correction(Mat &input)

{
	Mat re_img;
	cv::flip(input, re_img, 1);
	imshow("re_img", re_img);
	Mat result = Mat::zeros(610, 590, CV_8UC3);

	//相当于将图像透射到这个尺寸大小的平面上,而建立的平面
	vector<Point2f> obj;
	obj.push_back(Point2f(150, 150));//原点
	obj.push_back(Point2f(450, 150));
	obj.push_back(Point2f(450, 450));
	obj.push_back(Point2f(150, 450));

	/*
	*	修改#if的值切换手动抓取点或使用默认Point
	*/

#if 1
	//将输出点写入下方方便接下来操作,
	//图像翻转后
	//vector<Point2f> constant_data;
	//constant_data.push_back(Point2f(538, 287));//原点
	//constant_data.push_back(Point2f(732, 285));
	//constant_data.push_back(Point2f(712, 418));
	//constant_data.push_back(Point2f(542, 419));
	vector<Point2f> constant_data;
	constant_data.push_back(Point2f(324, 286));//原点
	constant_data.push_back(Point2f(1059, 281));
	constant_data.push_back(Point2f(983, 784));
	constant_data.push_back(Point2f(341, 785));

	//图像翻转前
	vector<Point2f> constant_data2;
	constant_data2.push_back(Point2f(269, 285));//原点
	constant_data2.push_back(Point2f(458, 290));
	constant_data2.push_back(Point2f(457, 419));
	constant_data2.push_back(Point2f(287, 416));
	H = findHomography(constant_data, obj, RANSAC);

	//应用仿射变换,可以恢复出原图
	warpPerspective(re_img, result, H, result.size());
	//cout << "H" << H << endl;
	imshow("矫正后图像", result);
#else
	Mat dst = re_img.clone();

	userdata data;
	data.im = dst;
	imshow("dst", dst);
	setMouseCallback("dst", mouseHandle, &data);
	waitKey(0);

	//findHomography找到两个平面之间的转换矩阵
	//RANSAC筛选误匹配特征
	Mat H = findHomography(data.points, obj, RANSAC);
	//应用仿射变换,可以恢复出原图
	warpPerspective(re_img, result, H, result.size());
	//cout << "H" << H << endl;
	imshow("矫正后图像", result);

#endif // 0

	//waitKey(0);
	return result;
}


//鼠标左键单击操作
void mouseHandle(int event, int x, int y, int flags, void* ptr)
{
	if (event == EVENT_LBUTTONDOWN)
	{
		userdata *data = (userdata*)ptr;
		circle(data->im, Point(x, y), 5, Scalar(0, 0, 255), -1);
		cv::imshow("dst", data->im);
		if (data->points.size() < 4)
		{
			//i++;
			//cout << "第" << i++ << "个点坐标" << Point(x, y) << endl;
			data->points.push_back(Point2f(x, y));
		}
	}
}

//鼠标右键单击操作
void mouseHandleRight(int event, int x, int y, int flags, void* ptr)
{
	if (event == EVENT_RBUTTONDOWN)
	{
		userdata1 *data1 = (userdata1*)ptr;
		circle(data1->im1, Point(x, y), 5, Scalar(0, 255, 0), -1);
		cv::imshow("dst1", data1->im1);
		if (data1->points1.size() < 3)
		{
			//i++;
			data1->points1.push_back(Point2f(x, y));
			a[j] = Point2f(x, y);
			cout << "第" << j++ << "个点坐标" << Point(x, y) << endl;
		}
	}
}

//细化算法:将指针细化
void Thining_Pavlidis(cv::Mat& src, cv::Mat& dst)
{
	if (src.type() != CV_8UC1)
	{
		printf("只能处理二值或灰度图像\n");
		return;
	}
	//非原地操作时候,copy src到dst
	if (dst.data != src.data)
	{
		src.copyTo(dst);
	}

	char erase, n[8];
	unsigned char bdr1, bdr2, bdr4, bdr5;
	short k, b;
	unsigned long i, j;

	int width, height;
	width = dst.cols;
	height = dst.rows;

	//把不能于0的值转化为1,便于后面处理
	for (i = 0; i < height; i++)
	{
		for (j = 0; j < width; j++)
		{
			if (dst.at<uchar>(i, j) != 0)
			{
				dst.at<uchar>(i, j) = 1;
			}
			//图像边框像素值为0
			if (i == 0 || i == (height - 1) || j == 0 || j == (width - 1))
				dst.at<uchar>(i, j) = 0;
		}
	}

	erase = 1;
	width = width - 1;
	height = height - 1;
	uchar* img;
	int step = dst.step;
	while (erase)
	{
		img = dst.data;
		//第一个循环,取得前景轮廓,轮廓用2表示
		for (i = 1; i < height; i++)
		{
			img += step;
			for (j = 1; j < width; j++)
			{
				uchar* p = img + j;

				if (p[0] != 1)
					continue;

				n[0] = p[1];
				n[1] = p[-step + 1];
				n[2] = p[-step];
				n[3] = p[-step - 1];
				n[4] = p[-1];
				n[5] = p[step - 1];
				n[6] = p[step];
				n[7] = p[step + 1];

				//bdr1是2进制表示的p0...p6p7排列,10000011,p0=1,p6=p7=1
				bdr1 = 0;
				for (k = 0; k < 8; k++)
				{
					if (n[k] >= 1)
						bdr1 |= 0x80 >> k;
				}
				//内部点,p0, p2, p4, p6都是为1, 非边界点,所以继续循环
				//0xaa 10101010
				//  0   1   0
				//  1         1
				//   0   1    0

				if ((bdr1 & 0xaa) == 0xaa)
					continue;
				//不是内部点,则是边界点,对于边界点,我们标记为2,是轮廓
				p[0] = 2;

				b = 0;

				for (k = 0; k <= 7; k++)
				{
					b += bdr1 & (0x80 >> k);
				}
				//在边界点中,等于1,则是端点,等于0,则是孤立点,此时标记3
				if (b <= 1)
					p[0] = 3;

				//此条件说明p点是中间点,如果移去会引起断裂
				// 0x70        0x7         0x88      0xc1        0x1c      0x22      0x82     0x1      0xa0     0x40     0x28    0x10       0xa      0x4
				// 0 0 0     0  1  1     1  0   0    0   0   0    1  1  0    0   0   1  0  0  1  0 0 0    0  0  0   0 0 0    1  0  0   0  0  0  1  0  1   0 1 0
				// 1    0     0      1     0       0    0        1    1      0    0        0  0      0  0    1    0      0   0    0    0      0   1      0  0     0    0    0
				// 1 1 0     0  0  0     0  0   1    0   1   1    0  0  0    1   0   1  0  0  1  0 0 0    1  0  1   0 1 0    1  0  0   0  0  0  0  0  0   0 0 0
				if ((bdr1 & 0x70) != 0 && (bdr1 & 0x7) != 0 && (bdr1 & 0x88) == 0)
					p[0] = 3;
				else if ((bdr1 && 0xc1) != 0 && (bdr1 & 0x1c) != 0 && (bdr1 & 0x22) == 0)
					p[0] = 3;
				else if ((bdr1 & 0x82) == 0 && (bdr1 & 0x1) != 0)
					p[0] = 3;
				else if ((bdr1 & 0xa0) == 0 && (bdr1 & 0x40) != 0)
					p[0] = 3;
				else if ((bdr1 & 0x28) == 0 && (bdr1 & 0x10) != 0)
					p[0] = 3;
				else if ((bdr1 & 0xa) == 0 && (bdr1 & 0x4) != 0)
					p[0] = 3;
			}
		}
		//printf("------------------------------\n");
		//PrintMat(dst);
		img = dst.data;
		for (i = 1; i < height; i++)
		{
			img += step;
			for (j = 1; j < width; j++)
			{
				uchar* p = img + j;

				if (p[0] == 0)
					continue;

				n[0] = p[1];
				n[1] = p[-step + 1];
				n[2] = p[-step];
				n[3] = p[-step - 1];
				n[4] = p[-1];
				n[5] = p[step - 1];
				n[6] = p[step];
				n[7] = p[step + 1];

				bdr1 = bdr2 = 0;

				//bdr1是2进制表示的当前点p的8邻域连通情况,hdr2是当前点周围轮廓点的连接情况
				for (k = 0; k <= 7; k++)
				{
					if (n[k] >= 1)
						bdr1 |= 0x80 >> k;
					if (n[k] >= 2)
						bdr2 |= 0x80 >> k;
				}

				//相等,就是周围全是值为2的像素,继续
				if (bdr1 == bdr2)
				{
					p[0] = 4;
					continue;
				}

				//p0不为2,继续
				if (p[0] != 2) continue;
				//=4都是不可删除的轮廓点
				//     0x80       0xa     0x40        0x1      0x30   0x6
				//   0 0 0      1  0 1    0  0  0    0  0  0   0 0 0   0 1 1
				//   0    0      0     0    0      0    0      1   1    0   0    0
				//   0 0 1      0  0 0    0  1  0    0  0  0   1 0 0   0 0 0

				if (
					(bdr2 & 0x80) != 0 && (bdr1 & 0xa) == 0 &&
					//    ((bdr1&0x40)!=0 &&(bdr1&0x1)!=0 ||     ((bdr1&0x40)!=0 ||(bdr1 & 0x1)!=0) &&(bdr1&0x30)!=0 &&(bdr1&0x6)!=0 )
					(((bdr1 & 0x40) != 0 || (bdr1 & 0x1) != 0) && (bdr1 & 0x30) != 0 && (bdr1 & 0x6) != 0)
					)
				{
					p[0] = 4;
				}
				//
				else if ((bdr2 & 0x20) != 0 && (bdr1 & 0x2) == 0 &&
					//((bdr1&0x10)!=0 && (bdr1&0x40)!=0 || ((bdr1&0x10)!=0 || (bdr1&0x40)!=0) &&    (bdr1&0xc)!=0 && (bdr1&0x81)!=0)
					(((bdr1 & 0x10) != 0 || (bdr1 & 0x40) != 0) && (bdr1 & 0xc) != 0 && (bdr1 & 0x81) != 0)
					)
				{
					p[0] = 4;
				}

				else if ((bdr2 & 0x8) != 0 && (bdr1 & 0x80) == 0 &&
					//((bdr1&0x4)!=0 && (bdr1&0x10)!=0 || ((bdr1&0x4)!=0 || (bdr1&0x10)!=0) &&(bdr1&0x3)!=0 && (bdr1&0x60)!=0)
					(((bdr1 & 0x4) != 0 || (bdr1 & 0x10) != 0) && (bdr1 & 0x3) != 0 && (bdr1 & 0x60) != 0)
					)
				{
					p[0] = 4;
				}

				else if ((bdr2 & 0x2) != 0 && (bdr1 & 0x20) == 0 &&
					//((bdr1&0x1)!=0 && (bdr1&0x4)!=0 ||((bdr1&0x1)!=0 || (bdr1&0x4)!=0) &&(bdr1&0xc0)!=0 && (bdr1&0x18)!=0)
					(((bdr1 & 0x1) != 0 || (bdr1 & 0x4) != 0) && (bdr1 & 0xc0) != 0 && (bdr1 & 0x18) != 0)
					)
				{
					p[0] = 4;
				}
			}
		}
		//printf("------------------------------\n");
		//PrintMat(dst);
		img = dst.data;
		for (i = 1; i < height; i++)
		{
			img += step;
			for (j = 1; j < width; j++)
			{
				uchar* p = img + j;

				if (p[0] != 2)
					continue;

				n[0] = p[1];
				n[1] = p[-step + 1];
				n[2] = p[-step];
				n[3] = p[-step - 1];
				n[4] = p[-1];
				n[5] = p[step - 1];
				n[6] = p[step];
				n[7] = p[step + 1];

				bdr4 = bdr5 = 0;
				for (k = 0; k <= 7; k++)
				{
					if (n[k] >= 4)
						bdr4 |= 0x80 >> k;
					if (n[k] >= 5)
						bdr5 |= 0x80 >> k;
				}
				//值为4和5的像素
				if ((bdr4 & 0x8) == 0)
				{
					p[0] = 5;
					continue;
				}
				if ((bdr4 & 0x20) == 0 && bdr5 == 0)
				{
					p[0] = 5;
					continue;
				}
			}
		}
		erase = 0;
		//printf("------------------------------\n");
		//PrintMat(dst);
		img = dst.data;
		for (i = 1; i < height; i++)
		{
			img += step;
			for (j = 1; j < width; j++)
			{
				uchar* p = img + j;
				if (p[0] == 2 || p[0] == 5)
				{
					erase = 1;
					p[0] = 0;
				}
			}
		}
		//printf("------------------------------\n");
		//PrintMat(dst);
		//printf("------------------------\n");
	}
}

void Thining_Rosenfeld(cv::Mat& src, cv::Mat& dst)
{
	if (src.type() != CV_8UC1)
	{
		printf("只能处理二值或灰度图像\n");
		return;
	}
	//非原地操作时候,copy src到dst
	if (dst.data != src.data)
	{
		src.copyTo(dst);
	}

	int i, j, n;
	int width, height;
	//之所以减1,是方便处理8邻域,防止越界
	width = src.cols - 1;
	height = src.rows - 1;
	int step = src.step;
	int  p2, p3, p4, p5, p6, p7, p8, p9;
	uchar* img;
	bool ifEnd;
	cv::Mat tmpimg;
	int dir[4] = { -step, step, 1, -1 };

	while (1)
	{
		//分四个子迭代过程,分别对应北,南,东,西四个边界点的情况
		ifEnd = false;
		for (n = 0; n < 4; n++)
		{
			dst.copyTo(tmpimg);
			img = tmpimg.data;
			for (i = 1; i < height; i++)
			{
				img += step;
				for (j = 1; j < width; j++)
				{
					uchar* p = img + j;
					//如果p点是背景点或者且为方向边界点,依次为北南东西,继续循环
					if (p[0] == 0 || p[dir[n]] > 0) continue;
					p2 = p[-step] > 0 ? 1 : 0;
					p3 = p[-step + 1] > 0 ? 1 : 0;
					p4 = p[1] > 0 ? 1 : 0;
					p5 = p[step + 1] > 0 ? 1 : 0;
					p6 = p[step] > 0 ? 1 : 0;
					p7 = p[step - 1] > 0 ? 1 : 0;
					p8 = p[-1] > 0 ? 1 : 0;
					p9 = p[-step - 1] > 0 ? 1 : 0;
					//8 simple判定
					int is8simple = 1;
					if (p2 == 0 && p6 == 0)
					{
						if ((p9 == 1 || p8 == 1 || p7 == 1) && (p3 == 1 || p4 == 1 || p5 == 1))
							is8simple = 0;
					}
					if (p4 == 0 && p8 == 0)
					{
						if ((p9 == 1 || p2 == 1 || p3 == 1) && (p5 == 1 || p6 == 1 || p7 == 1))
							is8simple = 0;
					}
					if (p8 == 0 && p2 == 0)
					{
						if (p9 == 1 && (p3 == 1 || p4 == 1 || p5 == 1 || p6 == 1 || p7 == 1))
							is8simple = 0;
					}
					if (p4 == 0 && p2 == 0)
					{
						if (p3 == 1 && (p5 == 1 || p6 == 1 || p7 == 1 || p8 == 1 || p9 == 1))
							is8simple = 0;
					}
					if (p8 == 0 && p6 == 0)
					{
						if (p7 == 1 && (p3 == 9 || p2 == 1 || p3 == 1 || p4 == 1 || p5 == 1))
							is8simple = 0;
					}
					if (p4 == 0 && p6 == 0)
					{
						if (p5 == 1 && (p7 == 1 || p8 == 1 || p9 == 1 || p2 == 1 || p3 == 1))
							is8simple = 0;
					}
					int adjsum;
					adjsum = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
					//判断是否是邻接点或孤立点,0,1分别对于那个孤立点和端点
					if (adjsum != 1 && adjsum != 0 && is8simple == 1)
					{
						dst.at<uchar>(i, j) = 0; //满足删除条件,设置当前像素为0
						ifEnd = true;
					}
				}
			}
		}

		//printf("\n");
		//PrintMat(dst);
		//PrintMat(dst);
		//已经没有可以细化的像素了,则退出迭代
		if (!ifEnd) break;
	}
}

//圆形ROI区域绘制
Mat circle_ROI(Mat& image)
{
	Mat img = image.clone();
	Mat mask1 = Mat::ones(img.size(), CV_8UC3);
	Mat imghsv;
	cvtColor(img, imghsv, COLOR_BGR2HSV);//RGB to HSV
	//imshow("hsv", imghsv);

	Mat mask;
	inRange(imghsv, Scalar(hmin, smin, vmin), Scalar(hmax, smax, vmax), mask);//filter red color
	//imshow("mask", mask);

	Mat out2;
	Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
	erode(mask, out2, element); //erode
	//imshow("腐蚀", out2);

	Mat gaussian;
	GaussianBlur(mask, gaussian, Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);//模糊化
	//imshow("高斯滤波", gaussian);

//#pragma region 检测圆
//	vector<Vec3f> circles;
//	int dp = 1;					//分辨率的反比
//	//int	min_dist = erode_img.row / 16;	//分辨率的反比
//	int param_1 = 255;	//内部Canny边缘检测器的上限阈值
//	int param_2 = 23;	//中心检测阈值
//	int min_radius = 200;	//要检测的最小半径。如果未知,则将零置为默认值
//	int max_radius = 350;	//要检测的最大半径。如果未知,则将零置为默认值
//	HoughCircles(out2, circles, HOUGH_GRADIENT, dp, mask.rows / 16, param_1, param_2, min_radius, max_radius);
//	cout << circles.size() << endl;
//	for (size_t i = 0; i < circles.size(); i++)
//	{
//		Vec3i c = circles[i];
//		circle(img, Point(c[0], c[1]), c[2], Scalar(0, 0, 255), 3, LINE_AA);
//		circle(img, Point(c[0], c[1]), 2, Scalar(0, 255, 0), 3, LINE_AA);
//		//cout << "Circle" << c << endl;
//		//Mat circleROI = imCrop(circle(imCrop, Point(c[0], c[1]), c[2], Scalar(0, 0, 255)));
//	}
//
//	imshow("detected circles", img);
//#pragma endregion

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	Mat imgcontours;
	Point2f center;
	float radius;
	Mat dst = Mat::zeros(img.size(), CV_8UC3);

	findContours(gaussian, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
	double maxarea = 0;
	int maxareaidx = 0;
	for (int index = contours.size() - 1; index >= 0; index--)// find the maxarea return contour index
	{
		double tmparea = fabs(contourArea(contours[index]));
		if (tmparea > maxarea)
		{
			maxarea = tmparea;
			maxareaidx = index;
		}
	}
	RNG rng(0);
	double thickness = 0.5;	// 绘制轮廓线条的宽度
	int lineType = 1;	//线的连通性
	int maxLevel = 0;	//绘制轮廓的最高级别。若为0,则绘制指定轮廓;若为1,则绘制该轮廓和所有嵌套轮廓(nested contours);若为2,则绘制该轮廓、嵌套轮廓(nested contours)/子轮廓和嵌套-嵌套轮廓(all the nested-to-nested contours)/孙轮廓,等等。该参数只有在层级结构时才用到。
	for (int i = 0; i < contours.size(); i++)
	{
		//Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		Scalar color = Scalar(0, 255, 0);
		drawContours(dst, contours, i, color, thickness, lineType, hierarchy, maxLevel, Point(0, 0));
	}
	//imshow("contour", dst);
	minEnclosingCircle(contours[maxareaidx], center, radius);//using index ssearching the min circle
	circle(mask1, center, 250, Scalar(255, 255, 255), -1);
	//circle(img, center, 300, Scalar(255, 0, 0), 3);	//using contour index to drawing circle
	cvtColor(mask1, mask1, COLOR_BGR2GRAY);
	//imshow("掩模圆", mask1);
	//mask1 = mask1 * 255;
	Mat resimg;

	Mat image_mask = Mat(image.size(), CV_8UC3, Scalar(0, 0, 0));
	bitwise_or(image, image, resimg, mask1);
	imshow("绘制圆形", resimg);

	return resimg;
}

//直方图均衡化
Mat equlizeHistImg(Mat& input)
{
	Mat result;
	equalizeHist(input, result);
	cv::imshow("直方图均衡化", result);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\直方图均衡化.jpg", result);
	return result;
}

//直线通过中心位置的圆的关系x,y为圆心, x1,y1,x2,y2为hough变换检测出的直线
bool linePassCenterCircle(int x, int y, Vec4i lines)
{
	bool result = false;
	int circleR = 40;
	double A = lines[3] - lines[1];
	double B = lines[0] - lines[2];
	double C = (lines[2] * lines[1]) - (lines[3] * lines[0]);
	double delta = sqrt((A*A) + (B*B));
	double compare = (((A*x) + (B*y) + C) / delta) - circleR;
	if (compare < 0)			//小于0时直线与圆相交
	{
		cout << "直线与圆相交" << endl;
		return true;
	}
	else
	{
		cout << "直线与圆相离" << endl;
		return false;
	}
}

//	指针检测
Vec4i detection_line(Mat &input)
{
	Mat kernel = getStructuringElement(MORPH_CROSS, Size(9, 9));

	Mat Dist = Distortion_correction(input);
	int width = Dist.cols;
	int height = Dist.rows;
	Mat circle_img = circle_ROI(Dist);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\绘制圆形ROI.jpg", circle_img);
	Mat gray;
	cvtColor(circle_img, gray, COLOR_BGR2GRAY);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\灰度化.jpg", gray);

	Mat hist_img = equlizeHistImg(gray);
	Mat NOT;
	bitwise_not(hist_img, NOT);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\取反.jpg", NOT);
	//imshow("取反", NOT);
	cvtColor(NOT, NOT, COLOR_GRAY2BGR);
	//imshow("转彩图", NOT);
	//二次画圆去掉取反之后的白色区域以及二次定位
	NOT = circle_ROI(NOT);
	cvtColor(NOT, NOT, COLOR_BGR2GRAY);
	Mat Blur;
	medianBlur(NOT, Blur, 3);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\中值滤波.jpg", Blur);
	//imshow("中值滤波", Blur);
	Mat erode_img;
	erode(Blur, erode_img, kernel);
	cv::imshow("腐蚀", erode_img);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\腐蚀.jpg", erode_img);
	Mat open_img;
	morphologyEx(erode_img, open_img, MORPH_CLOSE, kernel);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\开运算.jpg", open_img);
	//cv::imshow("开运算", open_img);
	Mat two;
	threshold(open_img, two, 210, 255, THRESH_BINARY);
	cv::imshow("二值化", two);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\二值化.jpg", two);

	//canny
	Mat canny;
	Canny(two, canny, 150, 250);
	cv::imshow("canny", canny);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\canny.jpg", canny);

	//判断指针指向
	Mat img4 = two(Rect(two.cols*0.13, two.rows*0.2, two.cols*0.75, two.rows*0.65));
	int nRow = img4.rows;
	int nCol = img4.cols;
	c = 0;
	l = 0;
	//遍历ROI区域内的像素,以中线作为区分,算出区域内白色像素所占的数量。
	for (size_t i = 0; i < nRow; i++)
	{
		for (size_t j = 0; j < nCol; j++)
		{
			if (img4.at<uchar>(i, j) > 210)
			{
				if (j < img4.cols / 2)
				{
					l++;
				}
				else if (img4.cols / 2 < j)
				{
					c++;
				}
			}
		}
	}
	if (l < c)
	{
		cout << "指针指向左边" << endl;
	}
	else
	{
		cout << "指针指向右边" << endl;
	}

	Mat thining_img;
	Thining_Rosenfeld(two, thining_img);
	cv::imshow("细化操作", thining_img);
	//imwrite("C:\\Users\\SF1\\Desktop\\报告\\细化.jpg", thining_img);
#pragma region HoughLines直线检测算法
	//MessageBox(NULL, "HoughLinesP", "Tips", MB_OK);
	vector<Vec4i> lines;
	double thita;
	int threshold = 50;
	int miniLineLenth = 100;
	int maxLineGrap = 150;
	HoughLinesP(thining_img, lines, 1, CV_PI / 180, threshold, miniLineLenth, maxLineGrap);
	if (lines.size() == 0)
	{
		//MessageBox(NULL, "未检测到直线,正在尝试新的检测方法", "提示", MB_OK);
		HoughLinesP(thining_img, lines, 1, CV_PI / 180, 45, 30, maxLineGrap);
		for (int i = 0; i < lines.size(); i++)
		{
			Vec4i l = lines[i];
			if (linePassCenterCircle(width / 2, height / 2, l))
			{
				get_line = lines[i];
				line(Dist, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255, 255, 0), 3, LINE_AA);
				thita = tan((l[0] - l[2]) / (l[1] - l[3]));
			}
			else
			{
				cout << "未检测到直线!!" << endl;
			}
			thita = (thita * 180) / CV_PI;
			cout << "角度为:" << thita << endl;
		}
	}
	else
	{
		for (int i = 0; i < lines.size(); i++)
		{
			Vec4i l = lines[i];
			if (linePassCenterCircle(width / 2, height / 2, l))
			{
				get_line = lines[i];
				line(Dist, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 3, LINE_AA);
				thita = tan((l[0] - l[2]) / (l[1] - l[3]));
			}
			else
			{
				cout << "未检测到直线!!" << endl;
			}
			thita = (thita * 180) / CV_PI;
			cout << "角度为:" << thita << endl;
		}
	}
	cv::imshow("Line", Dist);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\Hough1.jpg", Dist);
#pragma endregion
	return get_line;
}

//刻度检测
void scale_detection()
{
	Mat img = imread("C:\\Users\\SF1\\Desktop\\Test\\1999_1202_044107_923.jpg");
	Mat resize_img;
	vector<Vec4i> lines;
	Mat kernel = getStructuringElement(MORPH_CROSS, Size(3, 3));
	Mat kernel_erode = getStructuringElement(MORPH_CROSS, Size(2, 3));
	Mat kernel_black = getStructuringElement(MORPH_CROSS, Size(7, 7));
	Size dsize = Size(1000, 700);
	resize(img, resize_img, dsize, 0, 0, INTER_AREA);
	//ROI图
	Rect ROI = selectROI(resize_img, false, false);
	Mat imCrop = resize_img(ROI);
	//绘制圆形区域
	Mat img_circle_roi = circle_ROI(imCrop);

	Mat gray_img;
	cvtColor(img_circle_roi, gray_img, COLOR_BGR2GRAY);
	Mat equalize_img = equlizeHistImg(gray_img);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\刻度均衡化.jpg", equalize_img);
	Mat guass;
	GaussianBlur(equalize_img, guass, Size(3, 3), 0, 0);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\高斯.jpg", guass);
	Mat erode_img;
	erode(guass, erode_img, kernel_erode);
	//dilate(erode_img, erode_img, kernel_erode);
	cv::imshow("erode", erode_img);
	//黑帽处理
	Mat Black;
	morphologyEx(erode_img, Black, MORPH_BLACKHAT, kernel_black);
	cv::imshow("black", Black);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\黑帽.jpg", Black);
	Mat two;
	Mat dst = Mat::zeros(imCrop.size(), CV_8UC3);
	//Mat dst = imCrop.clone();

	adaptiveThreshold(Black, two, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -7);
	cv::imshow("two", two);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\自适应二值化.jpg", two);

	//描绘轮廓
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(two, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	cout << "contours.size():" << contours.size() << endl;
	RNG rng(0);
	double thickness = -1;	// 绘制轮廓线条的宽度,如果为负值或CV_FILLED表示填充轮廓内部,
	int lineType = 4;	//线的连通性
	int maxLevel = 0;	//绘制轮廓的最高级别。若为0,则绘制指定轮廓;若为1,则绘制该轮廓和所有嵌套轮廓(nested contours);若为2,则绘制该轮廓、嵌套轮廓(nested contours)/子轮廓和嵌套-嵌套轮廓(all the nested-to-nested contours)/孙轮廓,等等。该参数只有在层级结构时才用到。
	for (int i = 0; i < contours.size(); i++)
	{
		//Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		Scalar color = Scalar(0, 255, 0);
		double area1 = contourArea(contours[i]);
		RotatedRect minRect = minAreaRect(Mat(contours[i]));
		Point2f rect_point[4];
		minRect.points(rect_point);
		if (5 <= area1 && area1 <= 75)
		{
			cout << "第" << i << "轮廓面积= " << area1 << endl;
			drawContours(dst, contours, i, color, thickness, lineType, hierarchy, maxLevel, Point(0, 0));
			获取轮廓角度
			//Point2f* pos = new Point2f();
			//double dOrient = getOrientation(contours[i], *pos, dst);
			//for (int j = 0; j < 4; j++)
			//{
			//	line(dst, rect_point[j], rect_point[(j + 1) % 4], Scalar(0, 0, 255), 2);	//绘制矩形
			//}
		}
	}
	Mat and_img;
	//bitwise_and(dst, erode_img, and_img);
	cv::imshow("contour", dst);
	//cv::imwrite("C:\\Users\\SF1\\Desktop\\报告\\轮廓.jpg", dst);
	Mat thining1;
	//Thining(dst, thining1);
	cv::Canny(dst, thining1, 150, 200);
	cv::imshow("thining", thining1);
	//Mat fitline;
	//fitLine(thining1, fitline, DIST_L2, 0, 0.01, 0.01);
#pragma region HoughLines直线检测算法
#if 0
	vector<Vec2f> lines1;
	int threshold_lines = 10;
	MessageBox(NULL, "HoughLines", "Tips", MB_OK);
	cv::HoughLines(output, lines1, 1, CV_PI / 180, threshold_lines);
	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a * rho, y0 = b * rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(imCrop, pt1, pt2, Scalar(0, 0, 255), 2);
		imshow("HoughLines", resize_img);
	}
#else
	MessageBox(NULL, "HoughLinesP", "Tips", MB_OK);
	int threshold = 10;
	int miniLineLenth = 20;
	int maxLineGrap = 100;
	try
	{
		HoughLinesP(thining1, lines, 1, PI / 180, threshold, miniLineLenth, maxLineGrap);
		if (lines.size() == 0)
		{
			MessageBox(NULL, "未检测到直线", "提示", MB_OK);
		}
		else
		{
			for (size_t i = 0; i < lines.size(); i++)
			{
				Vec4i l = lines[i];
				line(imCrop, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 1, LINE_AA);
			}
			imshow("Line", resize_img);
		}
	}
	catch (const std::exception&)
	{
		cout << "HoughLineP异常" << endl;
	}
#endif

#pragma endregion
	waitKey(0);
}

// 右键标定点
void signPoint(Mat &image)
{
	Mat dst1 = image.clone();
	cv::imshow("dst1", dst1);
	userdata1 data1;
	data1.im1 = dst1;
	cv::setMouseCallback("dst1", mouseHandleRight, &data1);
	waitKey(0);
}

//起始到终点角度测量
//起始到终点角度测量
float biginToEnd(Point2d center, Point2d point1, Point2d point2)
{


	float bigin_end_angle;
	float a = sqrt(((point1.x - center.x)*(point1.x - center.x)) + ((point1.y - center.y)*(point1.y - center.y)));
	float b = sqrt(((point2.x - center.x)*(point2.x - center.x)) + ((point2.y - center.y)*(point2.y - center.y)));
	float c = sqrt(((point2.x - point1.x)*(point2.x - point1.x)) + ((point2.y - point1.y)*(point2.y - point1.y)));
	float cosx = ((b*b) + (a*a) - (c*c)) / (2 * a * b);
	float angle = (acos(cosx) * 180) / CV_PI;

	float k1, k2;
	float data_x1;
	float data_y1;
	float data_x2;
	float data_y2;
	data_x1 = center.x - point1.x;
	data_y1 = center.y - point1.y;
	data_x2 = center.x - point2.x;
	data_y2 = center.y - point2.y;
	if ((data_y1 != 0) && (data_y2 != 0))
	{

		bigin_end_angle = 360 - angle;
		cout << "起始点到终点的夹角" << bigin_end_angle << endl;
	}
	else {
		cout << "分子为零!!" << endl;
	}

	return bigin_end_angle;
}

//求两直线交点
Point lineAndLine(Mat &input)
{
	Mat Dist = Distortion_correction(input);
	//直线一
	double x1 = origion_line[0];
	double y1 = origion_line[1];
	double x2 = origion_line[2];
	double y2 = origion_line[3];

	//直线二
	double x3 = other_line[0];
	double y3 = other_line[1];
	double x4 = other_line[2];
	double y4 = other_line[3];

	double x, y;
	double k, k1;

	k = (y2 - y1) / (x2 - x1);
	k1 = (y4 - y3) / (x4 - x3);
	x = ((k*x1) - (k1*x3) - y1 + y3) / (k - k1);
	y = (k * (x - x1)) + y1;
	cout << "x: " << x;
	cout << "    y: " << y << endl;

	//绘制交点
	a[0] = Point(x, y);
	//cout << a[0] << endl;
	return a[0];
}

bool verdict(Point2d point1, Point2d center, Point2d point2)
{
	float linex1 = point1.x - center.x;
	float linex2 = point2.x - center.x;
	float liney1 = point1.y - center.y;
	float liney2 = point2.y - center.y;
	float D = (linex1*liney2) - (linex2*liney1);

	if (D < 0)
	{
		return true;
		cout << "点在右下方" << endl;
	}
	else
	{
		return false;
		cout << "点在左上方" << endl;
	}
}

//三个点求两直线角度 center为中间点
float twoLineCorner(Point point1, Point center, int x2, int y2, Point endPoint)
{
	float corner;
	float k1;
	float k2;
	float data_x1;
	float data_y1;
	float data_x2;
	float data_y2;
	data_x1 = center.x - point1.x;
	data_y1 = center.y - point1.y;
	data_x2 = center.x - x2;
	data_y2 = center.y - y2;
	k1 = data_y1 / data_x1;
	k2 = data_y2 / data_x2;
#if 0
	//tan正切计算
	double tanx = (k2 - k1) / (1 + (k1*k2));
	corner = atan(tanx) * 180 / CV_PI; x2
		cout << "两直线的夹角为: " << corner << endl;
#else

	//三角形计算方式

	float a = sqrt(((point1.x - center.x)*(point1.x - center.x)) + ((point1.y - center.y)*(point1.y - center.y)));
	float b = sqrt(((x2 - center.x)*(x2 - center.x)) + ((y2 - center.y)*(y2 - center.y)));
	float c = sqrt(((x2 - point1.x)*(x2 - point1.x)) + ((y2 - point1.y)*(y2 - point1.y)));
	float cosx = ((b*b) + (a*a) - (c*c)) / (2 * a * b);
	corner = (acos(cosx) * 180) / CV_PI;
	//点三为检测出的点
	//a中存储180度斜线线段起始段和终点段的值
	//向量法区分点的位置
	float linex1 = point1.x - center.x;
	float linex2 = x2 - center.x;
	float liney1 = point1.y - center.y;
	float liney2 = y2 - center.y;
	float D = (linex1*liney2) - (linex2*liney1);

	if (D < 0)
	{
		if ((verdict(point1, center, Point(other_line[0], other_line[1])) == true) && (verdict(Point(other_line[0], other_line[1]), center, endPoint) == true))
		{
			//cout << "指针在非指向区域!!" << endl;
			corner;
		}
		else
		{
			//cout << "指针在刻度区域内" << endl;
			if (corner > 90)
			{
				//cout << "点在右下方" << endl;
				corner = (180 - corner) + 180;
				corner;
			}
			else
			{
				//cout << "点在右下方" << endl;
				corner = corner + 180;
			}
		}
	}
	else
	{
		//cout << "点在左上方" << endl;
	}

	//Point2d p1(95, 220);
	//Point2d p2(464, 556);
	//double tmpx = (p1.x - p2.x) / (p1.y - p2.y) * (y2 - p2.y) + p2.x;
	//if ((((p2.x - p1.x)*(y2 - p1.y)) - ((p2.y - p1.y)*(x2 - p1.x))) > 0)
	//{
	//	if (corner < 90)
	//	{
	//		cout << "点在左下方" << endl;
	//		corner = (180 - corner) + 180;
	//	}
	//	else
	//	{
	//		corner = corner + 180;
	//	}
	//}
	//else
	//{
	//	cout << "点在右上方" << endl;
	//}
	//corner = acos(fabs(1 + (k1 * k2)) / sqrt(1 + (k1 * k1))*sqrt(1 + (k2 * k2)));
	//corner = (corner * 180) / CV_PI;
	//bigin_end_angle = atan(abs((k2 - k1) / (1 + k1 * k2)));
	cout << "两直线的夹角为:" << corner << endl;

	向量法求角度
	//double va_x = x2 - x1;
	//double va_y = y2 - y1;
	//double vb_x = x2 - x3;
	//double vb_y = y2 - y3;
	//double Value = (va_x*vb_x) + (va_y*vb_y);
	//double va_val = sqrt(va_x * va_x + va_y * va_y);  // 向量a的模
	//double vb_val = sqrt(vb_x * vb_x + vb_y * vb_y);  // 向量b的模
	//double cosValue = Value / (va_val * vb_val);      // 余弦公式
	acos的输入参数范围必须在[-1, 1]之间,否则会"domain error"
	对输入参数作校验和处理
	//if (cosValue < -1 && cosValue > -2)
	//	cosValue = -1;
	//else if (cosValue > 1 && cosValue < 2)
	//	cosValue = 1;
	//corner = acos(fabs(1 + (k1 * k2)) / sqrt(1 + (k1 * k1))*sqrt(1 + (k2 * k2)));
	//corner = (corner * 180) / CV_PI;
	//bigin_end_angle = atan(abs((k2 - k1) / (1 + k1 * k2)));
	//cout << "两直线的夹角为:" << corner << endl;
#endif // 0

	return corner;
}

/*求圆心坐标两点加一条直线方法
*	point1, point2 为起始点到终点的四个点
*	line2为中垂线
*	line3为Hough检测到的四个指针坐标
*/
Point circleCenter(Point2d point2, Point2d point1, Vec4d line3)
{
	//指针所在直线斜率为
	float k3 = float(line3[3] - line3[1]) / float(line3[2] - line3[0]);
	if ((point2.y - point1.y) == 0)
	{
		cout << "标定的起始点和终点为一条直线上的点" << endl;
		int line_k0_centerPoint_x = (point1.x + point2.x) / 2;		//当标定点与y轴平行时的直线
		int y = float((k3*(line_k0_centerPoint_x - line3[0])) + line3[1]);
		return Point(line_k0_centerPoint_x, y);
	}
	else
	{
		//求起始点到终点直线的斜率
		float k1 = float(point2.y - point1.y) / float(point2.x - point1.x);
		//垂线line2的斜率为
		float k2 = float(-1) / float(k1);
		//垂线line2与line1相交点为(x,y)
		Point line2(float((point2.x + point1.x) / 2), float((point2.y + point1.y) / 2));
		cout << "起始点与终点的中点坐标为: (" << line2.x << ", " << line2.y << ")" << endl;
		//指针与中垂线的交点为
		float x = float((k2*line2.x) - (k3*line3[0]) - line2.y + line3[1]) / float(k2 - k3);
		float y = float(k3 * (x - line3[0])) + float(line3[1]);
		x = fabs(x);
		y = fabs(y);
		cout << "圆心位置为x: " << x << "  y: " << y << endl;
		return Point(x, y);
	}
}

/*
*	计算矫正前后坐标
*	@param	Mat 镜像后图像
*	@param	Point	需要矫正的坐标点
*	@param	H	矫正坐标
*/
Point correct(Mat image, Point point1, Mat H)
{
	Point outPutPoint;
	float width = float(977) / float(image.rows);
	float height = float(1339) / float(image.cols);
	int x, y;
	x = point1.x*height;
	y = point1.y*width;
	outPutPoint.x = 2 * (image.cols / 2) - x;
	outPutPoint.y = y;



	//输入坐标计算矫正后点位坐标
	origionBigin_End = outPutPoint;
	if (origionBigin_End.x != 0 && origionBigin_End.y != 0)
	{
		outputBigin_End.x = ((H.at<double>(0, 0) * origionBigin_End.x) + (H.at<double>(0, 1) * origionBigin_End.y) + (H.at<double>(0, 2)))
			/ ((H.at<double>(2, 0) * origionBigin_End.x) + (H.at<double>(2, 1) * origionBigin_End.y) + (H.at<double>(2, 2)));
		outputBigin_End.y = ((H.at<double>(1, 0) * origionBigin_End.x) + (H.at<double>(1, 1) * origionBigin_End.y) + (H.at<double>(1, 2)))
			/ ((H.at<double>(2, 0) * origionBigin_End.x) + (H.at<double>(2, 1) * origionBigin_End.y) + (H.at<double>(2, 2)));
		cout << "outputBigin_End  " << outputBigin_End << endl;
	}

	return outputBigin_End;
}


int main()
{
	string path = openFile();
	Mat img = imread(path);

	//抓取仪表所在位置
	Mat imgRoi = img(Rect(754, 819, 1339, 977));

	//手动标定仪表最大最小值
	signPoint(imgRoi);

	//图像矫正
	Mat img2 = Distortion_correction(imgRoi);

	//指针检测并返回检测到直线坐标
	other_line = detection_line(imgRoi);

	//计算仪表最大值点坐标转换
	Point endPoint = correct(imgRoi, a[1], H);

	//仪表最小值点坐标转换
	Point biginPoint = correct(imgRoi, a[2], H);
	//cv::imshow("img", imgRoi);

	//求圆心
	Point center = circleCenter(endPoint, biginPoint, other_line);

	//计算仪表最小值到最大值的角度
	float min_max = biginToEnd(center, endPoint, biginPoint);
	float twoLine;
	if (l < c)
	{
		//cout << "指针指向左边" << endl;
		twoLine = twoLineCorner(biginPoint, center, other_line[0], other_line[1], endPoint);
		line(img2, center, biginPoint, Scalar(255, 0, 0), 3, LINE_AA);
		line(img2, center, Point(other_line[0], other_line[1]), Scalar(0, 0, 255), 3, LINE_AA);
	}
	else
	{
		twoLine = twoLineCorner(biginPoint, center, other_line[2], other_line[3], endPoint);
		line(img2, center, Point(other_line[2], other_line[3]), Scalar(0, 0, 255), 3, LINE_AA);
		//cout << "指针指向右边" << endl;
	}
	line(img2, biginPoint, center, Scalar(0, 255, 0), 3, LINE_AA);
	circle(img2, Point(center.x, center.y), 10, Scalar(0, 0, 255), -1);
	circle(img2, endPoint, 7, Scalar(15, 242, 235), -1);
	circle(img2, biginPoint, 7, Scalar(15, 242, 235), -1);
	cv::imshow("点", img2);
	float kedu = float(twoLine / min_max) * 60;
	cout << "刻度值为:" << kedu << endl;

	cv::waitKey(0);
}```

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值