打开chip.sln
,修改图片地址
检查环境没错的话,直接运行项目
即可,会生成这几张图片
运行show.exe
,用滚轮进行缩放 对检测有问题的部分和结果进行修改 比如这里就是 136+5
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<opencv2\imgproc\types_c.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("P:/Item/Opencv/chip/chip/pic_13.jpg");
//高斯滤波
Mat gaussian;
GaussianBlur(src, gaussian, Size(3, 3), 0);
//imshow("gaussian", gaussian);
imwrite("gaussian.jpg", gaussian);
//转灰度图
Mat gray;
cvtColor(gaussian, gray, CV_BGR2GRAY);
//imshow("gray", gray);
imwrite("gray.jpg", gray);
//边缘检测
Mat canny;
Canny(gray, canny, 100, 255);
//imshow("canny", canny);
imwrite("canny.jpg", 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);
imwrite("morphology.jpg", 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,1, Scalar(255, 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);
imwrite("result.jpg", 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;
}
import cv2
# 全局变量
g_window_name = "img" # 窗口名
g_window_wh = [800, 600] # 窗口宽高
g_location_win = [0, 0] # 相对于大图,窗口在图片中的位置
location_win = [0, 0] # 鼠标左键点击时,暂存g_location_win
g_location_click, g_location_release = [0, 0], [0, 0] # 相对于窗口,鼠标左键点击和释放的位置
g_zoom, g_step = 1, 0.1 # 图片缩放比例和缩放系数
g_image_original = cv2.imread("result.jpg") # 原始图片,建议大于窗口宽高(800*600)
g_image_zoom = g_image_original.copy() # 缩放后的图片
g_image_show = g_image_original[g_location_win[1]:g_location_win[1] + g_window_wh[1], g_location_win[0]:g_location_win[0] + g_window_wh[0]] # 实际显示的图片
# 矫正窗口在图片中的位置
# img_wh:图片的宽高, win_wh:窗口的宽高, win_xy:窗口在图片的位置
def check_location(img_wh, win_wh, win_xy):
for i in range(2):
if win_xy[i] < 0:
win_xy[i] = 0
elif win_xy[i] + win_wh[i] > img_wh[i] and img_wh[i] > win_wh[i]:
win_xy[i] = img_wh[i] - win_wh[i]
elif win_xy[i] + win_wh[i] > img_wh[i] and img_wh[i] < win_wh[i]:
win_xy[i] = 0
# print(img_wh, win_wh, win_xy)
# 计算缩放倍数
# flag:鼠标滚轮上移或下移的标识, step:缩放系数,滚轮每步缩放0.1, zoom:缩放倍数
def count_zoom(flag, step, zoom):
if flag > 0: # 滚轮上移
zoom += step
if zoom > 1 + step * 20: # 最多只能放大到3倍
zoom = 1 + step * 20
else: # 滚轮下移
zoom -= step
if zoom < step: # 最多只能缩小到0.1倍
zoom = step
zoom = round(zoom, 2) # 取2位有效数字
return zoom
# OpenCV鼠标事件
def mouse(event, x, y, flags, param):
global g_location_click, g_location_release, g_image_show, g_image_zoom, g_location_win, location_win, g_zoom
if event == cv2.EVENT_LBUTTONDOWN: # 左键点击
g_location_click = [x, y] # 左键点击时,鼠标相对于窗口的坐标
location_win = [g_location_win[0], g_location_win[1]] # 窗口相对于图片的坐标,不能写成location_win = g_location_win
elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): # 按住左键拖曳
g_location_release = [x, y] # 左键拖曳时,鼠标相对于窗口的坐标
h1, w1 = g_image_zoom.shape[0:2] # 缩放图片的宽高
w2, h2 = g_window_wh # 窗口的宽高
show_wh = [0, 0] # 实际显示图片的宽高
if w1 < w2 and h1 < h2: # 图片的宽高小于窗口宽高,无法移动
show_wh = [w1, h1]
g_location_win = [0, 0]
elif w1 >= w2 and h1 < h2: # 图片的宽度大于窗口的宽度,可左右移动
show_wh = [w2, h1]
g_location_win[0] = location_win[0] + g_location_click[0] - g_location_release[0]
elif w1 < w2 and h1 >= h2: # 图片的高度大于窗口的高度,可上下移动
show_wh = [w1, h2]
g_location_win[1] = location_win[1] + g_location_click[1] - g_location_release[1]
else: # 图片的宽高大于窗口宽高,可左右上下移动
show_wh = [w2, h2]
g_location_win[0] = location_win[0] + g_location_click[0] - g_location_release[0]
g_location_win[1] = location_win[1] + g_location_click[1] - g_location_release[1]
check_location([w1, h1], [w2, h2], g_location_win) # 矫正窗口在图片中的位置
g_image_show = g_image_zoom[g_location_win[1]:g_location_win[1] + show_wh[1], g_location_win[0]:g_location_win[0] + show_wh[0]] # 实际显示的图片
elif event == cv2.EVENT_MOUSEWHEEL: # 滚轮
z = g_zoom # 缩放前的缩放倍数,用于计算缩放后窗口在图片中的位置
g_zoom = count_zoom(flags, g_step, g_zoom) # 计算缩放倍数
w1, h1 = [int(g_image_original.shape[1] * g_zoom), int(g_image_original.shape[0] * g_zoom)] # 缩放图片的宽高
w2, h2 = g_window_wh # 窗口的宽高
g_image_zoom = cv2.resize(g_image_original, (w1, h1), interpolation=cv2.INTER_AREA) # 图片缩放
show_wh = [0, 0] # 实际显示图片的宽高
if w1 < w2 and h1 < h2: # 缩放后,图片宽高小于窗口宽高
show_wh = [w1, h1]
cv2.resizeWindow(g_window_name, w1, h1)
elif w1 >= w2 and h1 < h2: # 缩放后,图片高度小于窗口高度
show_wh = [w2, h1]
cv2.resizeWindow(g_window_name, w2, h1)
elif w1 < w2 and h1 >= h2: # 缩放后,图片宽度小于窗口宽度
show_wh = [w1, h2]
cv2.resizeWindow(g_window_name, w1, h2)
else: # 缩放后,图片宽高大于窗口宽高
show_wh = [w2, h2]
cv2.resizeWindow(g_window_name, w2, h2)
g_location_win = [int((g_location_win[0] + x) * g_zoom / z - x), int((g_location_win[1] + y) * g_zoom / z - y)] # 缩放后,窗口在图片的位置
check_location([w1, h1], [w2, h2], g_location_win) # 矫正窗口在图片中的位置
# print(g_location_win, show_wh)
g_image_show = g_image_zoom[g_location_win[1]:g_location_win[1] + show_wh[1], g_location_win[0]:g_location_win[0] + show_wh[0]] # 实际的显示图片
cv2.imshow(g_window_name, g_image_show)
# 主函数
if __name__ == "__main__":
# 设置窗口
cv2.namedWindow(g_window_name, cv2.WINDOW_NORMAL)
# 设置窗口大小,只有当图片大于窗口时才能移动图片
cv2.resizeWindow(g_window_name, g_window_wh[0], g_window_wh[1])
cv2.moveWindow(g_window_name, 700, 100) # 设置窗口在电脑屏幕中的位置
# 鼠标事件的回调函数
cv2.setMouseCallback(g_window_name, mouse)
cv2.waitKey() # 不可缺少,用于刷新图片,等待鼠标操作
cv2.destroyAllWindows()