要求:
- 检验对象性质:光纤,直径在100µm级。
- 检验精度要求:不合格率与手工检测偏差不超过5%,每一检测项目(同心度、角度等)与手工测量值基本吻合,每一检测项目测量值与手工测量值之差的平均值趋近于零。
- 检验内容要求:同心度、角度、曲率半径等。
-
-
-
测量流程简介
-
-
- 图像处理算法
-
图像预处理
-
主函数中的图像预处理程序包括图像增强、平滑处理、二值化等操作,并且在最后判断是否按空格键保存图片并且执行measure函数。
函数具体操作流程如下:
- 首先从摄像头获取一帧图像,对于图像以大孔径(99,99)做高斯模糊,获取图像的明暗特征,再转换为灰度图像。
- 反转图像像素值,并将其加权与原始图像叠加,即与原图像作差,用作增强效果,清除背景噪声。
- 对图像做局部均值滤波,进一步平滑图片,清除个别突出的噪点,减少噪点干扰。
- 根据图像亮度均值计算阈值,以当前平均灰度的0.9对图像二值化,进行二分类处理。
- 在处理好的图像中遍历所有像素点,计算出重心X和Y的坐标,所有白色像素坐标相加求均值即为光纤位置。
- 对第1步截取的原始图像做矩形截取,截取重心周围的一个匡准区域,输出非失真、光照均匀的标准化图像数据。
- 循环重复第3、4步操作,使用高斯滤波来模糊图像,并使用二进制反转生成Gauss图像。用addWeighted函数将原始图像与Gauss图像进行加权叠加,强化图像边缘和深浅度,清除随机噪声。
- 使用threshold函数将图像二值化,并对阈值进行参数化调整。
- 判断是否按下空格键,如果按下则保存图片,执行measure函数。
- 该段函数先通过高斯模糊、作差、中值滤波等操作尽量消除背景噪声影响,获取图像的明暗特征。二值化后遍历计算所有像素点找取光纤位置。截取了包含光纤端面部分的图像后,再次通过高斯模糊以及图像作差的操作强化光纤端面图像的边缘和深浅度,以便后续的边缘提取及边缘拟合的操作。二值化操作后判断是否保存图片并执行自行定义的measure函数。
-
轮廓拟合
-
轮廓拟合程序包含在自行定义的measure函数中,该段函数先以8个方向作最大切线,以各个切点的中心点作为圆心寻找一个无论放大缩小所包含的点数都几乎不变的圆。重复上述操作直至圆心基本不变,此时的(X,Y)即为圆心的不精确解,R为半径。在(X,Y)基础上添加随机扰动以确定最优解。将此最优解圆上的点作为参数进行霍夫变换得到的光纤包层边缘的的拟合椭圆。重复类似的操作步骤以得到纤芯边缘的拟合椭圆。
函数具体操作流程如下:
1. 对程序载入的灰度图像进行二值化处理,并在其中寻找亮度超过一定阈值的像素点,将它们存储在一个vector中。首先,程序遍历整张图片的所有像素,将亮度值高于100的像素点设为255,其余设为0。程序根据阈值得到所有二值化后值为255的像素点的坐标信息并存储到points_0向量中;然后,将points_0赋值给points_all,表示最初的识别结果即为全部的亮点;最后,向量r_0用于存储points_0中每个点到图像原点的距离。
2. 接着是一个基于霍夫变换的圆检测算法。该算法首先通过迭代求圆心坐标和半径,将所有满足半径要求内的点加入最终输出结果points_all中。
程序中的变量X和Y为圆心坐标,R为圆半径,R_k为半径增加倍率,num_R为各个半径下圆内点数,R_为上一轮循环的半径,dR为每次半径增加值,num_R为在细调环节用于判断循环的上一轮中心点数。
具体实现过程如下:
①首先找到所有点集在0°、90°、180°、270°方向上的最大最小值,即x和y坐标的极值以及两个斜线上的距离的极值;然后以这八个方向的切点计算出圆心坐标X和Y。
②接着,对于每一个点,计算其与圆心之间的距离,并与半径阈值进行比较,得到当前半径下圆内点数目num。
③按照半径增加倍率R_k逐步增加半径,计算每个半径下的圆内点数目num_R,并将其存放在num_R数组中。
④判断在增加半径后圆内点数目是否发生变化。当相邻两个半径对应的圆内点数目差值小于设定值时,说明找到了更合适的圆,更新圆半径、points_all和num_R。
⑤如果已经进行了一次微调,但是圆内点数仍未发生变化,则以(X,Y)为圆心的不精确解,R为半径。
⑥最终输出圆心坐标X、Y,圆半径R以及圆内点数目。
3. 然后是一个用于对圆进行优化的算法,其主要思路是在原圆心坐标附近随机生成一些点,添加随机扰动,并计算这些点到圆心的距离,将这些点中距离小于当前圆半径的点加入新的点集points_new。然后通过迭代求得新的圆心坐标和半径,并将满足半径要求内的点加入最终输出结果points_all中。
程序中的变量dx和dy为随机生成的偏移量,x和y为根据原圆心坐标和偏移量计算得出的新圆心坐标,right为优化类型,1表示快速优化,2表示慢速优化。优化类型不同会影响生成的偏移量的大小。当超过一段时间未能找到更优解时,则认为当前的圆已是最优解并退出循环。
具体实现过程如下:
①第一行代码使用C++标准库中的chrono头文件,获取当前系统时间,并将其转换为time_t类型的值。
②在一个while循环中,程序不断检测键盘是否按下回车键(ASCII码为13),如果按下则跳出循环。该循环用于在一定时间内对圆进行拟合优化。
③根据当前时间与上一次记录的时间差,分别给(dx,dy)赋予不同的随机值,然后计算得到圆心坐标(x,y)。其中, dx和dy是随机数,rand()函数返回一个随机数,用于给dx和dy添加一定的扰动。
④根据优化类型right的不同,选择不同的半径增加倍率和步长。优化类型有两种,一种是快速,另一种是慢速。
⑤计算所有点到圆心的距离,并将这些距离存储在r_0对象中。
⑥遍历r_0对象中的元素,统计出当前圆半径下圆内的点数,并将这些数量存储在num变量中。
⑦根据优化类型right的不同,选择不同的细调方式。
4. 然后是对圆心坐标和半径的迭代过程,根据不同的优化类型计算新的半径值,如果当前新半径内点数目与上一轮循环的点数差小于设定值,则认为找到了合适的圆,更新圆心坐标和半径。最后输出最终的圆心坐标X、Y,圆半径R以及圆内点数目。
该部分代码中,num为当前半径下圆内点数目,num_R为在细调环节用于判断循环的上一轮中心点数,R为当前圆半径。当优化类型为1时,每次半径减小0.02;当优化类型为2时,每次半径减小0.001。
具体实现过程如下:
①根据优化类型right的不同,选择不同的细调方式。当right等于1时,圆半径R每次减小0.02;当right等于2时,圆半径R每次减小0.001。
②遍历所有点到圆心的距离r_0,统计出当前圆半径下圆内的点数,并将这些数量存储在num变量中。
③根据上一次记录的圆内点数num_R与当前圆内点数num的差值,如果小于设定值,则认为拟合圆完成,更新圆心坐标(X,Y)和半径R,并将结果输出。
④最后面有一行代码,用于记录当前时间,方便计算运行时间。
5. 最后是对检测到的圆形进行优化得到最后的两个拟合椭圆。首先根据当前圆心坐标X、Y和半径R筛选出在圆内的点集points_all,然后根据points_all拟合椭圆box_1。接着再根据纤芯半径对全图点集points_0进行筛选,得到新的点集points_all,并根据points_all重新计算圆心坐标和半径R,直到圆内点数目num超过10个或时间超过10秒为止。最后,再次根据points_all筛选点集,直到点集内点数目大于设定值,得到最终的优化椭圆box_2。
该部分代码通过不断迭代优化圆形,提高了椭圆拟合精度和稳定性,同时还能够适用于纤芯半径不同的光纤检测场景。其中,利用随机偏移量dx和dy生成新的圆心坐标x和y,再通过计算点到圆心的距离r_0进行半径优化;利用时间戳t和判断条件控制循环时间和循环退出条件。
具体实现过程如下:
①清空点集points_all,将所有距离圆心在圆内的点加入到points_all中。将全图点集points_0重置为points_all,并重新计算每个点到圆心的距离r_0。
②初始化最小半径r_min为0.1,通过逐步增加r_min的方式,筛选出符合条件的点集points_all,直至点集中的点数大于15。输出点集points_all的大小和全图点集points_0的大小。
③使用points_all拟合椭圆矩阵box_1,包括圆心的x、y坐标、宽度、高度以及旋转角度,即为拟合得到的包层轮廓,用于后续计算操作。
④清空点集points_all,将所有距离圆心在纤芯内的点加入到points_all中。将全图点集points_0按照距离圆心的升序排列,并重新计算每个点到圆心的距离r_0。
⑤从R=1开始,不断增加圆半径R,统计当前半径下圆内点的数量num,直到num大于20。此时记录下当前R值,并记录时间t。在10秒内,或者圆内点数num大于10时停止循环。每次随机生成dx和dy,并根据当前点(x,y)和dx、dy更新新的点坐标。计算新的点与圆心的距离r_0,并统计距离半径为R+0.0005范围内的点数num。如果num小于20,说明圆半径过小,增加圆半径R。如果num大于等于20,则保持当前R不变。
⑥输出新的圆心坐标X、Y,圆半径R和圆内点数num,并记录时间t。将所有距离圆心在圆环内的点加入到points_all中,直至点集中的点数大于等于12。输出点集points_all的大小和全图点集points_0的大小。
⑦使用points_all拟合椭圆矩阵box_2,包括圆心的x、y坐标、宽度、高度以及旋转角度,即为拟合得到的纤芯轮廓,用于后续计算操作。
-
-
测量计算
-
这段代码主要是可视化处理结果和输出分析数据,在图像中显示已经得到的椭圆形状,并且计算出椭圆度、平均半径和同心度等工程参数值,以便进一步进行物品的分类和性质分析。具体来说:
1. 如果原始图片只有一个灰度通道,则将其转换为三通道RGB模式,方便后续的可视化操作。
2. 可选地,在原图中添加生成随机圆周所对应的圆形并输出。
3. 在原图中标注出所有检测到的目标点的位置。
4. 在原图中绘制第一个最佳拟合椭圆矩阵box_1(包层),通过红色线条展示出它的轮廓,在椭圆边缘位置添加文字说明,包括椭圆度和角度信息。
5. 在原图中绘制通过第二次筛选后得到的椭圆矩阵box_2(纤芯),同样以红色字体在椭圆边缘位置添加文字说明,包括椭圆度和角度信息。
6. 针对当前图像,分别输出包层椭圆度、纤芯椭圆度、包层平均半径、纤芯平均半径以及同心度等特征数据,提供给其他统计模块进行更准确的处理。
7. 通过resize()函数将图像增大两倍,并在程序中打开窗口以便用户查看结果。
8. 最后,当用户按下一个键盘上的任意键时,程序结束并关闭图像窗口。
#include <opencv2/opencv.hpp>
#include <opencv.hpp>
#include <opencv2\face.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2\imgproc\types_c.h"
#include <opencv2/imgproc/imgproc_c.h>
#include<iostream>
#include<fstream>
#include<istream>
#include <iomanip>
#include <conio.h>
#include <windows.h>
#include <cstdio>
#include <Windows.h>
#include <winuser.h>
#include "cmath"
#include <time.h>
using namespace std;
using namespace cv;
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
double Short(double x, double y)
{
if (x <= y)
{
return x;
}
else
{
return y;
}
}
double Long(double x, double y)
{
if (x >= y)
{
return x;
}
else
{
return y;
}
}
void measure(Mat src)
{
cout << "START" << endl;
Mat se = getStructuringElement(MORPH_CROSS, Size(5, 5), Point(-1, -1));
erode(src, src, se);
srand((unsigned int)_getpid());
cout << "随机设置" << endl;
//Mat src = imread("1.jpg", 0);
//threshold(src, src, 40, 255, 1);
vector<Point> points_0;//全图点集
for (int x = 10; x < src.cols - 10; x++)
{
for (int y = 10; y < src.rows - 10; y++)
{
if (src.at<uchar>(Point(x, y)) < 100)
{
points_0.push_back(Point(x, y));
}
}
}
vector<Point> points_all = points_0;//用于识别的最大点集,也是输出的最终结果bb
vector<double> r_0;//全图距离与points_0对应
cout << "全图点集" << endl;
double X = 0, Y = 0;//圆心
double R = 500;//圆半径
int R_k = 0;//圆半径增加倍率
vector<double> num_R;//各R下圆内点数
double R_ = 700;//用于判断循环的上一轮R值
double dR = 0.1;//半径增加值
int num_R_ = 0;//在细调环节用于判断循环的上一轮中心点数
for (int n = 0; /*n <= 50*/; n++)
{
cout << n << endl;
if (n > 2 && (R_ - R) < 5)
{
break;
}
double x_0 = 0, x_180 = 9999;
double y_90 = 0, y_270 = 9999;
double d_fmax = 0, d_fmin = 9999, d_zmax = 0, d_zmin = 9999;
for (int i = 0; i < points_all.size(); i++)
{
x_0 = Long(x_0, points_all[i].x);
y_90 = Long(y_90, points_all[i].y);
x_180 = Short(x_180, points_all[i].x);
y_270 = Short(y_270, points_all[i].y);
d_fmax = Long(d_fmax, points_all[i].x + points_all[i].y);
d_fmin = Short(d_fmin, points_all[i].x + points_all[i].y);
d_zmax = Long(d_zmax, points_all[i].y - points_all[i].x);
d_zmin = Short(d_zmin, points_all[i].y - points_all[i].x);
}
double d_f = (d_fmax + d_fmin) / 2;
double d_z = (d_zmax + d_zmin) / 2;
X = (x_0 + x_180 + (d_z + d_f) / 2) / 3 + 0.02 * double(100 - rand() % 100);
Y = (y_90 + y_270 + (d_z + d_f) / 2) / 3 + 0.02 * double(100 - rand() % 100);
r_0.clear();
for (int i = 0; i < points_0.size(); i++)
{
r_0.push_back(sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)));
}
num_R.clear();
int num = 0;
for (R_k = 0; 250 + R_k * dR < 500; R_k++)
{
num = 0;
for (int i = 0; i < r_0.size(); i++)
{
if (r_0[i] <= 150 + dR * R_k)
{
num++;
}
}
num_R.push_back(num);
}
for (int i = 0; i < num_R.size() - 1; i++)
{
//cout << " " << i << " " << num_R[i + 1] - num_R[i] << endl;
if (num_R[i + 1] - num_R[i] < 50)
{
R_ = R;
R = 250 + (i + 0) * dR;
points_all.clear();
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R)
{
points_all.push_back(points_0[i]);
}
}
cout << n << " X=" << setw(7) << setiosflags(ios::left) << X << " Y=" << setw(7) << setiosflags(ios::left) << Y
<< " R=" << setw(7) << setiosflags(ios::left) << R << " NUM+=" << num_R[i + 1] << " NUM=" << num_R[i] << endl;
num_R_ = num_R[i];
break;
}
}
/*circle(src, Point(X, Y), R, Scalar(255, 255, 0), 1, CV_AA, 0);
imshow("1", src);
waitKey(0);*/
}
cout << endl;
long t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
double dx, dy, x, y;
int right = 1;//优化类型 1:快速 2:慢速
while (1)
{
if (KEY_DOWN(13))
{
break;
}
if (long(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t) % 2 == 0 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t < 4)
{
dx = double(10000.0 - rand() % 20000);
dy = double(10000.0 - rand() % 20000);
x = X + 0.0005 * dx;
y = Y + 0.0005 * dy;
}
else if (long(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t) % 2 == 1 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t < 4)
{
dx = double(50000.0 - rand() % 100000);
dy = double(50000.0 - rand() % 100000);
x = X + 0.0005 * dx;
y = Y + 0.0005 * dy;
}
else if (long(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t) % 2 == 1 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t >= 4 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t < 9)
{
dx = double(200.0 - rand() % 400);
dy = double(200.0 - rand() % 400);
x = X + 0.00005 * dx;
y = Y + 0.00005 * dy;
right = 2;
}
else if (long(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t) % 2 == 0 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t >= 4 &&
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t < 9)
{
dx = double(10000.0 - rand() % 20000);
dy = double(10000.0 - rand() % 20000);
if (dx < 1500)
{
dx = dx * 2;
}
if (dy < 1500)
{
dy = dy * 2;
}
x = X + 0.0005 * dx;
y = Y + 0.0005 * dy;
right = 2;
}
else
{
break;
}
r_0.clear();
for (int i = 0; i < points_0.size(); i++)
{
r_0.push_back(sqrt((points_0[i].x - x) * (points_0[i].x - x) + (points_0[i].y - y) * (points_0[i].y - y)));
}
int num = 0;
//#pragma omp parallel for num_threads(18) reduction(+:num)
if (right == 1)
{
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R - 0.1)
{
num++;
}
}
if (num_R_ - num < 30)
{
R = R - 0.1;
X = x;
Y = y;
cout << "X=" << setw(9) << setiosflags(ios::left) << X << " Y=" << setw(9) << setiosflags(ios::left)
<< Y << " R=" << setw(9) << setiosflags(ios::left) << R << " NUM_=" << num << endl;
t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
num_R_ = num;
}
}
if (right == 2)
{
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R - 0.03)
{
num++;
}
}
if (num_R_ - num < 30)
{
R = R - 0.03;
X = x;
Y = y;
cout << "X=" << setw(9) << setiosflags(ios::left) << X << " Y=" << setw(9) << setiosflags(ios::left)
<< Y << " R=" << setw(9) << setiosflags(ios::left) << R << " NUM_=" << num << endl;
t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
num_R_ = num;
}
}
}
points_all.clear();
for (int i = 0; i < points_0.size(); i++)
{
if (sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) <= R)
{
points_all.push_back(points_0[i]);
}
}
points_0 = points_all;//重置全图点集
r_0.clear();
for (int i = 0; i < points_0.size(); i++)
{
r_0.push_back(sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)));//重置全图半径
}
double r_min = 0.1;
while (1)
{
points_all.clear();
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R && r_0[i] > R - r_min)
{
points_all.push_back(points_0[i]);
}
}
if (points_all.size() > 100)
{
break;
}
r_min += 0.05;
}
cout << points_all.size() << "/" << points_0.size() << endl;
RotatedRect box_1 = fitEllipse(points_all);
points_all.clear();
for (int i = 0; i < points_0.size(); i++)
{
if (sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) <= R / 5)
{
points_all.push_back(points_0[i]);
}
}
points_0 = points_all;//重置全图点集按纤芯半径
r_0.clear();
for (int i = 0; i < points_0.size(); i++)
{
r_0.push_back(sqrt((points_0[i].x - x) * (points_0[i].x - x) + (points_0[i].y - y) * (points_0[i].y - y)));
}
int num = 0;
for (R = 1; num <= 20; R += 0.02)
{
num = 0;
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R)
{
num++;
}
}
}
R = R - 1;
t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
while (1)
{
if (KEY_DOWN(/*13*/'Q') || (std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - t > 10 && num > 10))
{
break;
}
dx = double(10000.0 - rand() % 20000);
dy = double(10000.0 - rand() % 20000);
x = X + 0.0003 * dx;
y = Y + 0.0003 * dy;
r_0.clear();
for (int i = 0; i < points_0.size(); i++)
{
r_0.push_back(sqrt((points_0[i].x - x) * (points_0[i].x - x) + (points_0[i].y - y) * (points_0[i].y - y)));
}
num = 0;
for (int i = 0; i < points_0.size(); i++)
{
if (r_0[i] <= R + 0.005)
{
num++;
}
}
if (num < 20)
{
R = R + 0.005;
X = x;
Y = y;
t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
cout << "X=" << setw(9) << setiosflags(ios::left) << X << " Y=" << setw(9) << setiosflags(ios::left)
<< Y << " R=" << setw(9) << setiosflags(ios::left) << R << " NUM_=" << num << endl;
}
}
points_all.clear();
for (int i = 0; i < points_0.size(); i++)
{
if (sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) <= R &&
sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) >= R - 0.5)
{
points_all.push_back(points_0[i]);
}
}
while (points_all.size() < 50)
{
for (int i = 0; i < points_0.size(); i++)
{
if (sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) <= R + 0.01 &&
sqrt((points_0[i].x - X) * (points_0[i].x - X) + (points_0[i].y - Y) * (points_0[i].y - Y)) >= R)
{
points_all.push_back(points_0[i]);
}
}
R = R + 0.01;
}
cout << points_all.size() << "/" << points_0.size() << endl;
RotatedRect box_2 = fitEllipse(points_all);
if (src.channels() <= 1)
{
cvtColor(src, src, COLOR_GRAY2BGR);
}
//circle(src, Point(X, Y), R, Scalar(255, 255, 0), 0.5, CV_AA, 0);
/*for (int i = 0; i < points_all.size(); i++)
{
circle(src, points_all[i], 1, Scalar(250, 250, 0), -1, CV_AA, 0);
}*/
ellipse(src, box_1, Scalar(20, 25, 255), 2, CV_AA);
//putText(src, to_string(box_1.size.height / box_1.size.width) /*+ "-" + to_string(sin(CV_PI*box.angle/180))*/,
// Point(box_1.center.x + box_1.size.height / 2 * sin(CV_PI * box_1.angle / 180) * 1.01,
// box_1.center.y - box_1.size.height / 2 * cos(CV_PI * box_1.angle / 180)) * 1.01,
// FONT_HERSHEY_SIMPLEX, 0.3, Scalar(50, 50, 180), 1, 8);
ellipse(src, box_2, Scalar(20, 255, 25), 2, CV_AA);
//putText(src, to_string(box_2.size.height / box_2.size.width) /*+ "-" + to_string(sin(CV_PI*box.angle/180))*/,
// Point(box_2.center.x + box_2.size.height / 2 * sin(CV_PI * box_2.angle / 180) * 1.01,
// box_2.center.y - box_2.size.height / 2 * cos(CV_PI * box_2.angle / 180)) * 1.01,
// FONT_HERSHEY_SIMPLEX, 0.3, Scalar(50, 50, 180), 1, 8);
cout << "包层椭圆度:" << box_1.size.height / box_1.size.width << endl;
cout << "纤芯椭圆度:" << box_2.size.height / box_2.size.width << endl;
//分辨率:3840 X 2160
//CCD尺寸:6.16 X 4.62
//物镜倍率:10X ? 实测为8.904
/*cout << "纤芯平均半径1:" << (box_2.size.height + box_2.size.width) * 125 / (box_1.size.height + box_1.size.width) << endl;
cout << "纤芯平均半径2:" << (box_2.size.height + box_2.size.width) * 6.16 * 1000 / 3840 / 2 / 10 << endl;
cout << "纤芯平均半径3:" << (box_2.size.height + box_2.size.width) * 4.62 * 1000 / 2160 / 2 / 10 << endl;
cout << "纤芯平均半径4:" << ((box_2.size.height + box_2.size.width) * 4.62 * 1000 / 2160 +
(box_2.size.height + box_2.size.width) * 6.16 * 1000 / 3840) / 2 / 2 / 10 << endl;*/
cout << "纤芯平均半径:" << (box_2.size.height + box_2.size.width) * 7.7 * 1.12309 * 1000 / 4406 / 2 / 10 << endl << endl;
/*cout << "包层平均半径1:" << (box_1.size.height + box_1.size.width) * 6.16 * 1000 / 3840 / 2 / 10 << endl;
cout << "包层平均半径2:" << (box_1.size.height + box_1.size.width) * 4.62 * 1000 / 2160 / 2 / 10 << endl;
cout << "包层平均半径3:" << ((box_1.size.height + box_1.size.width) * 4.62 * 1000 / 2160 +
(box_1.size.height + box_1.size.width) * 6.16 * 1000 / 3840) / 2 / 2 / 10 << endl;*/
cout << "包层平均半径:" << (box_1.size.height + box_1.size.width) * 7.7 * 1.12309 * 1000 / 4406 / 2 / 10 << endl << endl;
cout << "同心度(以平均半径计):" << sqrt((box_1.center.x - box_2.center.x) * (box_1.center.x - box_2.center.x) +
(box_1.center.y - box_2.center.y) * (box_1.center.y - box_2.center.y)) * 400 /
(box_1.size.height + box_1.size.width + box_2.size.height + box_2.size.width) << "%" << endl;
resize(src, src, src.size() / 2, 0);
imshow("1", src);
waitKey(500);
waitKey(0);
}
int main()
{
int vedio = 1;
VideoCapture capture(vedio);
capture.set(CAP_PROP_FRAME_HEIGHT, 2160);
capture.set(CAP_PROP_FRAME_WIDTH, 3840);
Mat src, src_1;
Mat src_show, dst_1, dst_2;
Mat Gauss;
//capture >> src;
//namedWindow("1", WINDOW_AUTOSIZE);
//waitKey(1);
//HWND hwnd= FindWindow(NULL, "1");
//SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION);
//SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED | WS_EX_TOOLWINDOW | 0x00080000);
//imshow("1", src);
//SetWindowPos(hwnd, NULL, 0, 0, src.cols, src.rows, /*SWP_NOSIZE*/NULL);
double t = 0;
int FPS_0 = 0;
capture >> src;
//resize(src, src, src.size() / 4, 0);
if (src.channels() > 1)
{
cvtColor(src, src, COLOR_BGR2GRAY);
}
int n = 0;
double km = 1.45, km_0;
int state = 0;
int m = 0;
START:
while (1)
{
t = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
capture >> src_1;
resize(src_1, src_1, src_1.size() / 4, INTER_LINEAR);
putText(src_1, to_string(FPS_0), Point(5, src_1.size().height - 5), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 1, 8);
imshow("1", src_1);
waitKey(1);
if (KEY_DOWN(13))
{
waitKey(100);
break;
}
FPS_0 = int(1000) / int(chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count() - t);
}
n = 0;
while (1)
{
if (KEY_DOWN(13))
{
waitKey(100);
goto START;
}
n++;
t = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
capture >> src_1;
//resize(src_1, src_1, src_1.size() / 4, 0);
/*if (KEY_DOWN(13))
{
waitKey(100);
while (1)
{
capture >> src_1;
Mat src_t;
resize(src_1, src_t, src_1.size() / 5, 0);
imshow("1", src_t);
waitKey(1);
if (KEY_DOWN(13))
{
waitKey(100);
break;
}
}
}*/
if (src_1.channels() > 1)
{
cvtColor(src_1, src_1, COLOR_BGR2GRAY);
}
addWeighted(src, 0.7, src_1, 0.3, 0, src);
resize(src, src_show, src.size(), INTER_LINEAR);
GaussianBlur(src_show, Gauss, cv::Size(99, 99), 0, 0);
bitwise_not(Gauss, Gauss);
addWeighted(src_show, 1, Gauss, -0.4, 0, src_show);
medianBlur(src_show, src_show, 7);
//cout << mean(src_show)[0] << " ";
if (n == 15)
{
cout << endl;
}
//cout << "test0 " << n << endl;
km_0 = km;
m = 0;
while (n > 15)
{
m++;
Mat src_k;
resize(src_show, src_k, src_show.size(), 0);
threshold(src_k, src_k, mean(src_k)[0] * km_0, 255, 0);
//resize(src_k, src_k, src_k.size() / 4, 0);
//imshow("2", src_k);
if (m > 150)
{
km = 1.45;
break;
}
else if (mean(src_k)[0] < 0.005)
{
km_0 = km_0 * 0.99;
}
else if (mean(src_k)[0] > 0.01)
{
km_0 = km_0 * 1.01;
}
else
{
state = 0;
km = km_0;
break;
}
}
if (m > 8)
{
state = 1;
}
//cout << m << " " << km << endl;
//waitKey(1);
//cout << "test1.1 " << n << endl;
//medianBlur(src_show, src_show, 7);
medianBlur(src_show, src_show, 7);
threshold(src_show, src_show, mean(src_show)[0] * km, 255, 0);
/*Mat src_k;
resize(src_show, src_k, src_show.size()/4, 0);
imshow("2", src_k);*/
//cout << mean(src_show)[0] << " " << km << endl;
//cout << "test1 " << n << endl;
double X = 0, Y = 0, N = 0;
for (int x = 100; x < src_show.cols - 100; x++)
{
for (int y = 100; y < src_show.rows - 100; y++)
{
if (src_show.at<uchar>(Point(x, y)) > 240)
{
X = X + x;
Y = Y + y;
N++;
}
}
}
X = X / N;
Y = Y / N;
//cout << "test2 " << n << endl;
resize(src, src_show, src.size(), INTER_LINEAR);
int d_ = 500;
src_show = src_show(Rect(Long(X - d_, 0), Long(Y - d_, 0),
Short(src_show.size().width * 2 - X * 2, d_ * 2), Short(src_show.size().height * 2 - Y * 2, d_ * 2)));
GaussianBlur(src_show, Gauss, cv::Size(55, 55), 0, 0);
bitwise_not(Gauss, Gauss);
addWeighted(src_show, 1, Gauss, -0.7, 0, src_show);
medianBlur(src_show, src_show, 3);
threshold(src_show, dst_1, double(mean(src_show)[0]) * 0.9, 255, 0);
threshold(src_show, dst_2, double(mean(src_show)[0]) * 5, 255, 0);
addWeighted(dst_1, 1, dst_2, -1, 0, src_show);
/*resize(dst_1, dst_1, dst_1.size() / 4, 0);
imshow("2", dst_1);
resize(dst_2, dst_2, dst_2.size() / 4, 0);
imshow("3", dst_2);*/
//cout << "test3 " << n << endl;
if (KEY_DOWN(VK_SPACE))
{
waitKey(50);
measure(src_show);
}
if (src_1.channels() <= 1)
{
cvtColor(src_show, src_show, COLOR_GRAY2BGR);
}
resize(src_show, src_show, src_show.size() / 4, 0);
if (state == 1)
{
putText(src_show, "light!", Point(5, src_show.size().height - 25), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 1, 8);
}
if (state == 2)
{
putText(src_show, "过暗!", Point(5, src_show.size().height - 25), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 1, 8);
}
putText(src_show, to_string(FPS_0), Point(5, src_show.size().height - 5), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 1, 8);
imshow("1", src_show);
//imwrite(to_string(t) + ".jpg", src);
waitKey(1);
FPS_0 = int(1000) / int(chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count() - t);
//cout << n++ << endl;
}
}
运行结果:
我选用了断面情况极为恶劣的光纤,测试结果良好
此处额外贴上用于相机矫正的代码:
矫正时使用透明网格版。
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2\imgproc\types_c.h"
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/core.hpp>
#include<iostream>
#include<fstream>
#include<istream>
#include <iomanip>
#include <conio.h>
#include <windows.h>
#include <cstdio>
#include <Windows.h>
#include <winuser.h>
#include "cmath"
using namespace std;
using namespace cv;
double Short(double x, double y)
{
if (x <= y)
{
return x;
}
else
{
return y;
}
}
double Long(double x, double y)
{
if (x >= y)
{
return x;
}
else
{
return y;
}
}
void setCursorPosition(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
Mat AreaGrow(Mat mat, double x, double y)
{
double num = 0, num_0, num_h = 0;
num_0 = mat.cols * mat.rows;
int cornersSeed_x = round(mat.cols * y);
int cornersSeed_y = round(mat.rows * x);
Point cornersSeed = Point(cornersSeed_x, cornersSeed_y);
Mat growArea = Mat::zeros(mat.size(), CV_8UC1);
growArea = 1;
growArea.at<uchar>(Point(cornersSeed.x, cornersSeed.y)) = mat.at<uchar>(Point(cornersSeed.x, cornersSeed.y));
Point waitSeed;
vector<Point> seedVector;
seedVector.push_back(cornersSeed);
int direct[36][2] = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} ,
{-3,-3}, {0,-3}, {3,-3}, {3,0}, {3,3}, {0,3}, {-3,3}, {-3,0},
{5,1},{5,3},{5,5},{3,5},{1,5},{-1,5},{-3,5},{-5,5},{-5,3},
{-5,1},{-5,-1},{-5,-3},{-5,-5},{-3,-5},{-1,-5},{1,-5},{3,-5},{5,-5},{5,-3},{5,-1} };
while (!seedVector.empty())
{
Point seed = seedVector.back();
seedVector.pop_back();
for (int i = 0; i < 36; i++)
{
waitSeed.x = seed.x + direct[i][0];
waitSeed.y = seed.y + direct[i][1];
if (waitSeed.x < 0 || waitSeed.y < 0 || waitSeed.x >(mat.cols - 1) || (waitSeed.y > mat.rows - 1))
{
continue;
}
if (growArea.at<uchar>(Point(waitSeed.x, waitSeed.y)) == 1)
{
if (mat.at<uchar>(Point(waitSeed.x, waitSeed.y)) > (mat.at<uchar>(Point(seed.x, seed.y)) - 16) &&
mat.at<uchar>(Point(waitSeed.x, waitSeed.y)) < (mat.at<uchar>(Point(seed.x, seed.y)) + 16) &&
mat.at<uchar>(Point(waitSeed.x, waitSeed.y)) > (mat.at<uchar>(Point(cornersSeed.x, cornersSeed.y)) - 96) &&
mat.at<uchar>(Point(waitSeed.x, waitSeed.y)) < (mat.at<uchar>(Point(cornersSeed.x, cornersSeed.y)) + 96))
{
growArea.at<uchar>(Point(waitSeed.x, waitSeed.y)) = 255;
seedVector.push_back(waitSeed);
}
else
{
growArea.at<uchar>(Point(waitSeed.x, waitSeed.y)) = 0;
}
num++;
}
}
if (num * 100 / num_0 > num_h + 0.01)
{
num_h = num * 100 / num_0;
}
}
return growArea;
}
int main()
{
int BOARDSIZE[2] = { 31,15 };
vector<Point3f> obj_world_pts;
for (int i = 0; i < BOARDSIZE[1]; i++)
{
for (int j = 0; j < BOARDSIZE[0]; j++)
{
obj_world_pts.push_back(Point3f(j, i, 0));
}
}
vector<vector<Point3f>> objpoints_img;
vector<vector<Point2f>> images_points;
vector<String> images_path;
string image_path = "*.jpg";
glob(image_path, images_path);
cout << images_path.size() << endl << endl;
int page = 0;
Mat src;
double bigger = 1;
Mat turntangle, _turntangle;
page_turn:
if (page >= images_path.size())
{
fstream oFile;
oFile.open("out.DATA", ios::out);
Mat cameraMatrix, distCoeffs, R, T;
calibrateCamera(objpoints_img, images_points, src.size(), cameraMatrix, distCoeffs, R, T);
cout << "内参:" << endl;
cout << cameraMatrix << endl;
oFile << setprecision(25) << cameraMatrix << endl;
cout << endl;
cout << "相机畸变:" << endl;
cout << distCoeffs << endl;
oFile << distCoeffs << endl;
while (1)
{
//
}
return(0);
}
cout << page + 1 << " " << images_path[page] << " " << endl;
src = imread(images_path[page]);
//src = imread("1.jpg", -1);
//resize(src, src, src.size(), INTER_LINEAR);
if (src.channels() > 1)
{
cvtColor(src, src, COLOR_BGR2GRAY);
}
threshold(src, src, 0, 255, THRESH_OTSU);
Mat se = getStructuringElement(MORPH_CROSS, Size(11, 11), Point(-1, -1));
erode(src, src, se);
se = getStructuringElement(MORPH_CROSS, Size(3, 3), Point(-1, -1));
for (int i = 0; i < 69; i++)
{
dilate(src, src, se);
}
//imwrite("缓存1.jpg", src);
vector<Point2f> corners;
int maxcorners = 5000;
double qualityLevel = 0.08;
double minDistance = 70;
int blockSize = 18;
double k = 0.16;
goodFeaturesToTrack(src, corners, maxcorners, qualityLevel, minDistance, Mat(), blockSize, false, k);
TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::EPS, 60, 0.0001);
//cornerSubPix(src, corners, cv::Size(5, 5), cv::Size(-1, -1), criteria);
cvtColor(src, src, COLOR_GRAY2BGR);
//resize(src, src, src.size(), INTER_LINEAR);
/*for (int i = 0; i < corners.size(); i++)
{
circle(src, corners[i], 5, Scalar(0, 0, 255), -1, CV_AA, 0);
}*/
vector<vector<Point2f>> corners_order;
vector<Point2f> corners_level;
vector<Point2f> corners_vertical;
vector<Point2f> corners_line;
Point2f corner;
double dis_to_l_u_min = sqrt(corners[0].x * corners[0].x + corners[0].y * corners[0].y);
for (int i = 1; i < corners.size(); i++)
{
if (dis_to_l_u_min > sqrt(corners[i].x * corners[i].x + corners[i].y * corners[i].y))
{
dis_to_l_u_min = sqrt(corners[i].x * corners[i].x + corners[i].y * corners[i].y);
corner = corners[i];
}
}
corners_vertical.push_back(corner);
corners_vertical[0].x += 100;
corners_vertical[0].y += 170;
dis_to_l_u_min = 999999;
for (int i = 0; i < corners.size(); i++)
{
if (dis_to_l_u_min > sqrt((corners[i].x - corners_vertical[0].x) * (corners[i].x - corners_vertical[0].x)
+ (corners[i].y - corners_vertical[0].y) * (corners[i].y - corners_vertical[0].y))
&& corners[i].x > corners_vertical[0].x
&& corners[i].y > corners_vertical[0].y)
{
dis_to_l_u_min = sqrt((corners[i].x - corners_vertical[0].x) * (corners[i].x - corners_vertical[0].x)
+ (corners[i].y - corners_vertical[0].y) * (corners[i].y - corners_vertical[0].y));
corner = corners[i];
}
}
corners_vertical.clear();
corners_vertical.push_back(corner);
for (int i = 0; i < corners.size(); i++)
{
for (int j = 1; j < corners.size() - 1; j++)
{
double d1 = sqrt((corners[i].x - corners_vertical[0].x) * (corners[i].x - corners_vertical[0].x)
+ (corners[i].y - corners_vertical[0].y) * (corners[i].y - corners_vertical[0].y));
double d2 = sqrt((corners[j].x - corners_vertical[0].x) * (corners[j].x - corners_vertical[0].x)
+ (corners[j].y - corners_vertical[0].y) * (corners[j].y - corners_vertical[0].y));
double d3 = sqrt((corners[i].x - corners[j].x) * (corners[i].x - corners[j].x)
+ (corners[i].y - corners[j].y) * (corners[i].y - corners[j].y));
/*if ((d1 + d3) < d2 * 1.001 && d1 > 1000 && d2 > 1000 && d3 > 1000 && d2<(corners[j].x - corners_vertical[0].x) * 1.001
&& corners[i].x > corners_vertical[0].x && corners[j].y > corners_vertical[0].y)
{
circle(src, corners[i], 10, Scalar(255, 0, 0), -1, CV_AA, 0);
circle(src, corners[j], 10, Scalar(255, 0, 0), -1, CV_AA, 0);
goto tangle;
}*/
if (d1 > 10 && d2 > 10 && d3 > d1 && d3 > d2 && d1 < 140 && d2 < 140 && d3<(d1 + d2) * 0.85
&& corners[i].x > corners_vertical[0].x && corners[j].y > corners_vertical[0].y
&& (corners[i].y - corners_vertical[0].y) < (corners[i].x - corners_vertical[0].x))
{
double k = double(corners[i].y - corners_vertical[0].y) / double(corners[i].x - corners_vertical[0].x);
turntangle = getRotationMatrix2D(corners_vertical[0], atan(k) * 180 / CV_PI, 1);
_turntangle = getRotationMatrix2D(corners_vertical[0], atan(-1 * k) * 180 / CV_PI, 1);
/*circle(src, corners[i], 10, Scalar(255, 0, 0), -1, CV_AA, 0);
circle(src, corners[j], 10, Scalar(255, 0, 0), -1, CV_AA, 0);*/
//warpAffine(src, src, turntangle, src.size());
goto tangle;
}
}
}
tangle:
transform(corners, corners, turntangle);
corners.push_back(Point2f(-1, -1));
while (corners[corners.size() - 1] == Point2f(-1, -1))
{
for (int i = 0; i < corners.size(); i++)
{
double d1 = sqrt((corners[i].x - corners_vertical[corners_vertical.size() - 1].x) * (corners[i].x - corners_vertical[corners_vertical.size() - 1].x)
+ (corners[i].y - corners_vertical[corners_vertical.size() - 1].y) * (corners[i].y - corners_vertical[corners_vertical.size() - 1].y));
if (d1 < 140 && corners[i].y > corners_vertical[corners_vertical.size() - 1].y
&& abs(corners[i].x - corners_vertical[corners_vertical.size() - 1].x) < corners[i].y - corners_vertical[corners_vertical.size() - 1].y
&& abs(corners[i].x - corners_vertical[0].x) < corners[i].y - corners_vertical[corners_vertical.size() - 1].y)
{
corners_vertical.push_back(corners[i]);
break;
}
if (corners[i] == Point2f(-1, -1))
{
corners.pop_back();
}
}
}
//corners_vertical.pop_back();
corners_vertical.pop_back();
//transform(corners_vertical, corners_vertical, _turntangle);
/*for (int i = 0; i < corners_vertical.size(); i++)
{
circle(src, corners_vertical[i], 12, Scalar(20 * i + 50, 50, 255 - 20 * i), -1, CV_AA, 0);
}*/
for (int y = 0; y < corners_vertical.size(); y++)
{
corners.push_back(Point2f(-1, -1));
corners_level.clear();
corners_level.push_back(corners_vertical[y]);
while (corners[corners.size() - 1] == Point2f(-1, -1))
{
for (int i = 0; i < corners.size(); i++)
{
double d1 = sqrt((corners[i].x - corners_level[corners_level.size() - 1].x) * (corners[i].x - corners_level[corners_level.size() - 1].x)
+ (corners[i].y - corners_level[corners_level.size() - 1].y) * (corners[i].y - corners_level[corners_level.size() - 1].y));
if (d1 < 140 && d1 > 10 && corners[i].x > corners_level[corners_level.size() - 1].x
&& abs(corners[i].y - corners_level[corners_level.size() - 1].y) < corners[i].x - corners_level[corners_level.size() - 1].x
&& abs(corners[i].y - corners_level[0].y) < corners[i].x - corners_level[corners_level.size() - 1].x)
{
corners_level.push_back(corners[i]);
break;
}
if (corners[i] == Point2f(-1, -1))
{
corners.pop_back();
}
}
}
corners_order.push_back(corners_level);
/*for (int i = 0; i < corners_order[y].size(); i++)
{
circle(src, corners_order[y][i], 12, Scalar(10 * i + 50, 255 - 20 * y, 255 - 10 * i), -1, CV_AA, 0);
}*/
}
while (corners_order.size() > 15)
{
corners_order.pop_back();
}
for (int i = 0; i < corners_order.size(); i++)
{
while (corners_order[i].size() > 31)
{
corners_order[i].pop_back();
}
}
for (int i = 0; i < corners_order.size(); i++)
{
transform(corners_order[i], corners_order[i], _turntangle);
for (int j = 0; j < corners_order[i].size(); j++)
{
corners_line.push_back(corners_order[i][j]);
}
}
cout << obj_world_pts.size() << endl;
cout << corners_line.size() << endl;
if (obj_world_pts.size() == corners_line.size())
{
images_points.push_back(corners_line);
objpoints_img.push_back(obj_world_pts);
}
/*int minx = 999, miny;
miny = corners_order.size();
for (int i = 0; i < corners_order.size(); i++)
{
minx = Short(minx, corners_order[i].size());
}
cout << minx << " " << miny << endl;*/
//for (int i = 0; i < corners_order.size(); i++)
//{
// for (int j = 0; j < corners_order[i].size(); j++)
// {
// circle(src, corners_order[i][j], 12, Scalar(10 * j + 50, 255 - 20 * i, 255 - 10 * j), -1, CV_AA, 0);
// putText(src, to_string(i) + "," + to_string(j), corners_order[i][j], FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
// }
//}
//resize(src, src, src.size()/3, INTER_LINEAR);
//imshow("缓存2.jpg", src);
imwrite("缓存2.jpg", src);
//waitKey(0);
//for (int j = 0; j < corners_line.size(); j++)
//{
// circle(src, corners_line[j], 12, Scalar(0.5 * j + 10, 60, 255 - 0.5 * j), -1, CV_AA, 0);
// putText(src, to_string(j), corners_line[j], FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
//}
//resize(src, src, src.size() / 3, INTER_LINEAR);
//imshow("缓存2.jpg", src);
imwrite("缓存2.jpg", src);
//waitKey(0);
page++;
goto page_turn;
}