【DIY贴片机】基于opencv识别定位电子元件
识别效果:
操作步骤:
一、识别图像中目标数量
1.1加载图片
1.2滤波
1.3转灰度
1.4边缘检测
1.5二值化
1.6形态学操作
1.7画轮廓、最小外接矩形
二、确定每个目标位置与角度
2.1缩放最小外接矩形
2.2计算四个目标位置平均灰度值,平均灰度值最大的为1脚位置
2.3标记出1脚
2.4计算旋转角度(0-360度)
代码如下:
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
float seekFirstFoot(Mat image, RotatedRect box_min, Size size);
int compare_min(double val1, double val2, double val3, double val4);
int compare_max(double val1, double val2, double val3, double val4);
Point cp(Point pos1, Point pos2, Point pos3, Point pos4);
void main()
{
Mat src = imread("F:\\opencv_program\\IC检测素材\\微信图片_20210315144948.jpg");
//高斯滤波
Mat gaussian;
GaussianBlur(src, gaussian, Size(3, 3), 0);
//imshow("gaussian", gaussian);
//转灰度图
Mat gray;
cvtColor(gaussian, gray, CV_BGR2GRAY);
//imshow("gray", gray);
//边缘检测
Mat canny;
Canny(gray, canny, 50, 255);
//imshow("canny", canny);
//二值化
Mat binImag;
threshold(gray, binImag, 100, 255, THRESH_BINARY);
//inRange(gray, Scalar(0, 70, 70), Scalar(10, 255, 255), binImag);
bitwise_not(binImag, binImag);
//imshow("binImag", binImag);
//形态学操作
Mat morphology;
Mat kernel = getStructuringElement(CV_SHAPE_RECT, Size(3, 3));
morphologyEx(binImag, morphology, MORPH_OPEN, kernel, Point(-1, -1), 4);
//imshow("morphology", morphology);
//发现轮廓
vector<vector<Point>> contours;
findContours(morphology, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
cout << "目标数量:" << contours.size() << endl;
vector<Rect> bounding(contours.size()); //外接矩形
vector<RotatedRect> box(contours.size()); //定义最小外接矩形集合
Point2f rect[4];
Point2f recr_min[4];
//画轮廓、计算角度
for (int i = 0; i < contours.size(); i++)
{
bounding[i] = boundingRect(Mat(contours[i])); //计算外接矩形
box[i] = minAreaRect(Mat(contours[i])); //计算每个轮廓的最小外接矩形
//printf("最小外接圆旋转角度:%f \r\n", box[i].angle);
rectangle(src, bounding[i], Scalar(255, 0, 0)); //绘制外接矩形
circle(src, Point(box[i].center.x, box[i].center.y), 4, Scalar(0, 0, 255), -1); //绘制最小外接矩形中心
box[i].points(rect);
for (int j = 0; j < 4; j++)
{
line(src, rect[j], rect[(j + 1) % 4], Scalar(255, 0, 255), 3); //绘制最小外接矩形的四条直线
}
//drawContours(src,contours,i,Scalar(255,0,0)); //绘制轮廓
//cout << box[i].size << endl;
cout << "目标【" << i << "】";
cout << "坐标:" << box[i].center;
string str = to_string(i);
putText(src, str, Point(int(box[i].center.x), int(box[i].center.y)), FONT_HERSHEY_SIMPLEX, 2, Scalar(0, 255, 0));
//计算旋转角度
float zoom = 0.63; //缩放比例
RotatedRect box_min(Point(box[i].center.x, box[i].center.y), Size(box[i].size.width * zoom, box[i].size.height * zoom), box[i].angle); //缩放最小外接旋转矩形
seekFirstFoot(src, box_min, Size(35, 35)); //根据传入的旋转矩形,计算矩形四个点在size范围内的平均灰度,返回最大平均灰度值所在点与X轴的角度,(0-360度)
}
imshow("src", src);
waitKey(0);
}
float seekFirstFoot(Mat image, RotatedRect box_min, Size size)
{
Point2f rect_min[4];
box_min.points(rect_min);
Point2f rect_micro[4];
double m[4]; //可能为丝印四个点的灰度均值
Point pos[4]; //可能为丝印的四个点左上角坐标
int target_index; //丝印点下标
for (int k = 0; k < 4; k++)
{
//line(image, rect_min[k], rect_min[(k + 1) % 4], Scalar(255, 100, 255), 1); //绘制矩形的四条直线
//cout << "坐标【" << k << "】x=" << rect_min[k].x << "y=" << rect_min[k].y << endl;
}
for (int i = 0; i < 4; i++)
{
RotatedRect rect_micro(Point(rect_min[i].x, rect_min[i].y), size, 0);
Point2f rect_micro_pos[4];
rect_micro.points(rect_micro_pos);
for (int j = 0; j < 4; j++)
{
//line(image, rect_micro_pos[j], rect_micro_pos[(j + 1) % 4], Scalar(0, 255, 0), 1); //绘制矩形的四条直线
//putText(image, "1", (rect_micro_pos[j], rect_micro_pos[(j + 1) % 4]), (0, 255, 0));
//putText(image, "q", Point(int(rect_micro_pos[j].x), int(rect_micro_pos[j].y)), FONT_HERSHEY_SIMPLEX,2, Scalar(0, 255, 0));
//cout << "【1】 X=" << int(rect_micro_pos[j].x) << " Y=" << int(rect_micro_pos[j].y) << endl;
}
pos[i] = cp(rect_micro_pos[0], rect_micro_pos[1], rect_micro_pos[2], rect_micro_pos[3]); //返回最小矩形左上方坐标,
Rect rect_temp(int(pos[i].x), int(pos[i].y), size.width, size.height);
Mat InputImage = image(rect_temp);
//imshow("InputImage", InputImage);
Mat mat_mean, mat_stddev;
meanStdDev(InputImage, mat_mean, mat_stddev);//求灰度图像的均值、均方差
m[i] = mat_mean.at<double>(0, 0);
}
target_index = compare_max(m[0], m[1], m[2], m[3]);
Rect mask(int(pos[target_index].x), int(pos[target_index].y), size.width, size.height);
rectangle(image, mask, Scalar(100, 100, 255));
//计算旋转角度
Point p1(box_min.center.x, box_min.center.y);
Point p2(pos[target_index]);
double numble = 180 / CV_PI * (atan2(p2.y - p1.y, p2.x - p1.x));
if (numble > 0)
{
numble = 360 - abs(numble);
}
if (numble < 0)
{
numble = abs(numble);
}
cout << "角度 = " << numble << endl;
return 0;
}
//比较四个数,返回最小数的下标
int compare_min(double val1, double val2, double val3, double val4)
{
if (val1 <= val2 && val1 <= val3 && val1 <= val4)
{
return 0;
}
if (val2 <= val1 && val2 <= val3 && val2 <= val4)
{
return 1;
}
if (val3 <= val2 && val3 <= val1 && val3 <= val4)
{
return 2;
}
if (val4 <= val1 && val4 <= val3 && val4 <= val2)
{
return 3;
}
}
//比较四个数,返回最大数的下标
int compare_max(double val1, double val2, double val3, double val4)
{
if (val1 >= val2 && val1 >= val3 && val1 >= val4)
{
return 0;
}
if (val2 >= val1 && val2 >= val3 && val2 >= val4)
{
return 1;
}
if (val3 >= val2 && val3 >= val1 && val3 >= val4)
{
return 2;
}
if (val4 >= val1 && val4 >= val3 && val4 >= val2)
{
return 3;
}
}
//比较矩形的四个点,返回矩形左上方的点坐标
Point cp(Point pos1, Point pos2, Point pos3, Point pos4)
{
int add1 = pos1.x + pos1.y;
int add2 = pos2.x + pos2.y;
int add3 = pos3.x + pos3.y;
int add4 = pos4.x + pos4.y;
int temp = compare_min(add1, add2, add3, add4);
if (temp == 0)
return pos1;
if (temp == 1)
return pos2;
if (temp == 2)
return pos3;
if (temp == 3)
return pos4;
}
原图像: