#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);
}```
指针仪表检测算法代码
最新推荐文章于 2024-03-28 18:15:43 发布