import cv2
import numpy as np
#-----------找光斑函数----------------
def Find_red(frame):
x = 0
y = 0
# 进行高斯模糊
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
# 转换颜色空间到HSV
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# ---------白纸 白板上的红色激光阈值
lower_red = np.array([133, 12, 0])
upper_red = np.array([179, 255, 255])
# 对图片进行二值化处理
mask = cv2.inRange(hsv, lower_red, upper_red)
# 腐蚀操作
mask = cv2.erode(mask, None, iterations=2)
# 膨胀操作,先腐蚀后膨胀以滤除噪声
mask = cv2.dilate(mask, None, iterations=2)
cv2.imshow('mask', mask)
# 寻找图中轮廓
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
# 如果存在至少一个轮廓则进行如下操作
if len(cnts) > 0:
# 找到面积最大的轮廓
c = max(cnts, key=cv2.contourArea)
# 使用最小外接圆圈出面积最大的轮廓
((x, y), radius) = cv2.minEnclosingCircle(c)
print('光斑位置:', x, y)
# 计算轮廓的矩
M = cv2.moments(c)
# 计算轮廓的重心
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))# 通过矩计算中心xy坐标
# 只处理尺寸足够大的轮廓
if radius > 5:
# 画出最小外接圆
cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2)
# 画出重心
cv2.circle(frame, center, 2, (0, 0, 255), -1)
# cv2.imshow('frame', frame)
return x, y
#---------绘制需要的边缘到空白图像-----------
def find_Contours(original_image, img, edge_img1, cunt):
co = []
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# 遍历边缘,仅绘制满足面积条件的边缘
for contour in contours:
# 找到面积最大的轮廓
c = max(contours, key=cv2.contourArea)
# 对轮廓进行多边形近似,减少点数
epsilon = 0.02 * cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, epsilon, True)
# 确保近似后的轮廓是四边形
if len(approx) == 4:
# 获取矩形的四个角坐标
co = approx.reshape(-1, 2)
# 在原图上绘制四个角点
for corner in co:
x, y = corner
cv2.circle(original_image, (x, y), 5, (0, 255, 0), -1)
area = cv2.contourArea(contour)
if 5000 < area < 9500: # 面积大小 根据实际情况调
# 将满足条件的边缘绘制在空白图像上
cv2.drawContours(edge_img1, [contour], -1, 255, 1)
break
return co
def get_distance(dian):
distances = []
for i in range(len(dian)):
for j in range(i + 1, len(dian)):
x1, y1 = dian[i]
x2, y2 = dian[j]
distance = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
distances.append(distance)
return distances
def Get_linspa(data1, data2):
X = []
Y = []
# 将两个点作为数据点,创建 NumPy 数组
x_data = np.array([data1[0], data2[0]])
y_data = np.array([data1[1], data2[1]])
# 使用 np.polyfit 进行线性回归计算
coefficients = np.polyfit(x_data, y_data, 1)
# 得到拟合直线的斜率和截距
slope = coefficients[0]
intercept = coefficients[1]
# 使用斜率和截距来求出直线上的其他点的坐标
x_values_top = np.linspace(start=data1[0], stop=data2[0], num=10)
y_values_top = slope * x_values_top + intercept
# 输出拟合直线的斜率和截距
print(x_values_top)
print("斜率:", slope)
print("截距:", intercept)
# 输出拟合直线上的其他点的坐标
print("拟合直线上的点坐标:")
for x, y in zip(x_values_top, y_values_top):
print("({:.2f}, {:.2f})".format(x, y))
X.append(x)
Y.append(y)
return X, Y, slope
def control_system(big_, small_, rx, ry, state=0):
#得到内外矩形框四角坐标
left_top1 = big_[0] #外边框
left_down1 = big_[1]
right_down1 = big_[2]
right_top1 = big_[3]
left_top2 = small_[0] #内边框
left_down2 = small_[1]
right_down2 = small_[2]
right_top2 = small_[3]
left_top = (left_top2+left_top1)/2 #两框之间位置的四个角
left_down = (left_down2+left_down1)/2
right_down = (right_down2+right_down1)/2
right_top = (right_top2+right_top1)/2
#拟合黑线之间的四条边 得到的xy和斜率
left_outx, left_outy, left_slope = Get_linspa(left_top, left_down)
down_outx, down_outy, down_slope = Get_linspa(left_down, right_down)
right_outx, right_outy, right_slope = Get_linspa(right_down, right_top)
right_outx.reverse()#逆排序
right_outy.reverse()
top_outx, top_outy, top_slope = Get_linspa(right_top, left_top)
print(left_outx, left_outy)
print(down_outx, down_outy)
print(right_outx, right_outy)
print(top_outx, top_outy)
return left_top, left_down, right_down, right_top, left_slope, down_slope, right_slope, top_slope
def Canny_detect():
out_d = []
in_d = []
flag_findbleak = 1
# 打开摄像头
capture = cv2.VideoCapture(1, cv2.CAP_DSHOW) # 0:本地摄像头 1:外接摄像头
while(True):
# 1、按帧读取视频
ret, frame = capture.read() # frame为每一帧的图像
# 检查图像是否成功加载
if ret is False:
print("Error: Failed to load the image.")
else:
# 检测矩形框 两个
if flag_findbleak == 1:
#-------边缘提取
# 3、高斯滤波 Canny边缘算子
img = cv2.GaussianBlur(frame, (3, 3), 0) # 用高斯平滑处理原图像降噪。
canny = cv2.Canny(img, threshold1=100, threshold2=250, L2gradient=True) # 得到边缘
# 4. 查找轮廓
#方法1 5. 绘制满足面积条件的边缘
cunt = 0#判断是大还是小边缘
edge_img1 = np.zeros_like(canny)# 创建空白图像
big_ = find_Contours(img, canny, edge_img1, cunt)#外边缘
print('大---',big_) #左上 左下 右下 右上
cunt = cunt+1
canny2 = canny - edge_img1 #原始边缘图减去大方框
small_ = find_Contours(img, canny2, edge_img1, cunt) #再找一个方框(内边缘)
print('小----', small_)
# 计算每个点之间的距离
big_dis = get_distance(big_)
small_dis = get_distance(small_)
#
print("每个点之间的距离:")
print(big_dis)
print(small_dis)
if len(big_dis)>0 and len(small_dis)>0:
if big_dis != small_dis:
out_d = big_
in_d = small_
# flag_findbleak = 0
# cv2.imwrite('photo.jpg', img)
# cv2.imshow("canny", canny)
cv2.imshow("img", img)
#开始检测光斑
elif flag_findbleak == 0:
img = cv2.GaussianBlur(frame, (3, 3), 0) # 用高斯平滑处理原图像降噪。
# 光斑的xy坐标
red_x, red_y = Find_red(img)
# 黑框里面的四个角坐标
left_top, left_down, right_down, right_top, left_slope, down_slope, right_slope, top_slope = control_system(out_d, in_d, red_x, red_y, 0)
cv2.imshow("find_red", img)
if red_x!=0 and red_y!=0:
with open('data.txt', 'w') as file:
file.write('red_xy:')
file.write(str(red_x)+', ')
file.write(str(red_y))
file.write('\n' + 'left_top_xy:')
for item in left_top:
file.write(str(item) + '\n')
file.write('left_down_xy:')
for item in left_down:
file.write(str(item) + '\n')
file.write('right_down_xy:')
for item in right_down:
file.write(str(item) + '\n')
file.write('right_top_xy:')
for item in right_top:
file.write(str(item) + '\n')
file.write('left_slope:'+str(left_slope)+' ')
file.write('down_slope:' + str(down_slope)+' ')
file.write('right_slope:' + str(right_slope)+' ')
file.write('top_slope:' + str(top_slope)+' ')
break
# esc键退出(设置读帧间隔时间)
c = cv2.waitKey(5)
if c == 27: # ESC
break
if __name__ == "__main__":
i = 0
Canny_detect()
cv2.waitKey(0)
cv2.destroyAllWindows()
2023电赛E题OpenCV获取矩形框和光斑坐标
于 2023-08-07 13:59:14 首次发布