由于我们的实验场地并不是一根纯黑线,中间的交线会对识别造成一定的影响,从而导致无人机在交线处飞偏,所以就想识别交点坐标,保证无人机始终在直线上飞行,最后的实验结果也很理想。
识别交线代码是在网上看到的一段代码,后来发现它的算法思路很新奇,且应用过程中发现它的用武之地还蛮多的。
算法思路:
这是一张二值化后的图,对应像素值:白色:255,黑色:0。 我用数值表示为:
这是一张5*7大小的二值图,白色像素值为255,黑色像素值为0,我的目的是要求得(3,4)这个交点坐标。以求X坐标为例,先将每一列的值相加存入数组得到 [255, 255, 1785, 255, 255] ,可以看到出现了一个最大值1785,然后将相邻的列相减(从第二列开始,后一列减去前一列)的值存入另一个数组得到 [0,1530,-1530,0] ,可以看到出现了一个极大值和极小值,而这两个值对应的索引(列号)就是我们需要的值。取得极大值的索引:2; 极小值的索引: 3 。对应二值图可以看到,2就是我们需要求的X坐标的左邻列,3+1 就是X坐标的右邻列。最后求的 X = (2+4)/2 = 3 。同理,Y= (3 +5 )/2 = 4 。
代码:
import cv2
import numpy as np
import imutils
frame = cv2.imread("Cross 2.jpg")
frame = imutils.resize(frame, width=160) #调整图片大小
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
ret, th1 = cv2.threshold(gray, 80, 255, cv2.THRESH_BINARY_INV) #转化为二值图
mask = cv2.dilate(th1, None, iterations=1) #膨胀一下,补足一些残缺部分
ROImask = mask[0:120, 50:110]
cnts2 = cv2.findContours(ROImask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts2 = cnts2[0] if imutils.is_cv2() else cnts2[1]
if (len(cnts2) > 0):
#取得面积最大的轮廓并重绘(减少噪音干扰)
area = [cv2.contourArea(i) for i in cnts2]
area = np.array(area)
index = np.argmax(area)
img = np.zeros(ROImask.shape)
im = cv2.drawContours(img, cnts2, index, 255, -1)
#求交点X坐标 (可以封装成函数)
x_acc = np.sum(im, axis=0)
x_diff = np.diff(x_acc)
x_index1 = np.argmax(x_diff)
x_index2 = np.argmin(x_diff) + 1
x_max = max(x_diff)
x_min = min(x_diff)
if (x_max < 1000 and x_min > -1000):
Postion_x = 80 #图像中点
else:
Postion_x = (x_index1 + x_index2) // 2 + 50 #ROImask取的范围在[50,110],所以+50
# 求交点Y坐标
y_acc = np.sum(im, axis=1)
y_diff = np.diff(y_acc)
y_index1 = np.argmax(y_diff)
y_index2 = np.argmin(y_diff) + 1
y_max = max(y_diff)
y_min = min(y_diff)
if (y_max < 1000 and y_min > -1000):
Postion_y = 60 #图像中点
else:
Postion_y = (y_index1 + y_index2) // 2
else:
Postion_x = 80
Postion_y = 60
cv2.circle(frame, (Postion_x, Postion_y), 4, (0, 0, 255), -1) #画出交点
print('Postion_x,Postion_y', Postion_x, Postion_y)
cv2.imshow('mask', mask)
cv2.imshow('frame', frame)
cv2.waitKey(0)
结果图:
代码说明:
ROImask = mask[0:120, 50:110]
cnts2 = cv2.findContours(ROImask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts2 = cnts2[0] if imutils.is_cv2() else cnts2[1]
只取了二值图中间一部分[50,110]来求解,因为无人机在飞行起点与终点,左右两边直线外的图像会带来干扰,索性只取了中间一段来求交点。当无人机飞出了终点,也就是cnts2没有轮廓点了,可以做额外的动作,比如降落。
if (x_max < 1000 and x_min > -1000):
Postion_x = 80
else:
Postion_x = (x_index1 + x_index2) // 2 + 50
这里做了一个最大最小值判断,如果图像上不存在交点,只是一根单纯的直线。例如下图,它只能找到直线的中点Y坐标。
这时X坐标应为图像的中点(或者往某一方向飞行所需的定值)。所以,用这个算法巡线也完全OK。( // 2 为取整。)
缺陷:飞行时需将无人机摆正(不用完全与直线重合),因为算法是对像素的操作,如果无人机与直线倾斜的角度大了,得到的图像存在一定角度,算法就失效了。
其它应用:
1、 绕矩形框飞行,正如一个矩形框的拐角,可以作为条件判断来控制无人机的飞行方向。
2、在交点处悬停
源码下载:
https://github.com/LNanL/drone
矩形框代码没有测飞过,因为没有场地。
来源:https://blog.csdn.net/LNL_LNL/article/details/100119625