先描述一下使用场景:
我要识别一个红灯,已经把目标准确的提取出来了,二值图像中白色为目标物,现在要算出二值图中的白色像素点的坐标。因为之后需要移植到树莓派,所以需要高效的方法。
需要找出白色中心点
-
第一想到的肯定就是for for 了,然而效率太低,320*240迭代下来花费10ms。优点效果好,稳定在目标物的中心。
优化的方法:
1、在不丢失目标的情况下缩小图像进行扫描
2、内循环根据因子迭代,不满足则下一层。也就是在行扫描的时候,如果满足则条件设为后一个像素,不满足则进行下一行扫描。(问题:如果目标区域不连续,该行扫描不完就跳出了)
#传统扫描 :耗时10ms 约占总时间1/3,显然很浪费。优点:在图像处理算法准确的时候坐标精确
#优化方式:1、在不丢失目标的情况下缩小图像进行扫描 2、内循环根据因子迭代,不满足则下一层
def get_Target_Location(image):
size = image.shape
#print size
h = size[0]
w = size[1]
num_x = 0
num_y = 0
x=0
y=0
num = 0
#tt = cv2.getTickCount()
for i in range(w):
for j in range(h):
if image.item(j,i) & 255: #(x,y) = (j,i)
num_x += i
num_y += j
num += 1
if num > 0:
x = int(num_x/num)
y = int(num_y/num)
#print (cv2.getTickCount() - tt)/cv2.getTickFrequency() # 传统扫描:耗时10ms 约占总时间1/3,显然很浪费。优点:在图像处理算法准确的时候坐标精确
return x,y,num
-
第二种方法,粗略扫描(两点法),从左上角开始扫描,也是一行一行的扫描,当发现有满足的像素时,跳出并记录该点的x。继续在该行从行最大扫描到记录的x点,发现有满足的即结束循环。算出两次记录的平均值,即为目标中点。若找y,则一列一列的扫描,记录两次y算中点。 耗时: 0.5 - 2ms
问题:1、目标物对称才好确定。 2、两点波动性大 。 3、区域不连续仍然不好确定
def get_mid_x(image):
size = image.shape
h = size[0]
w = size[1]
flag_back = 0
x = 0
y = 0
ttt = cv2.getTickCount()
for i in range(0,h,3):
for j in range(w):
if image.item(i, j) & 255:
flag_back = 1
y = i
x = j
break
if flag_back:
break
for k in range(w,x):
if image.item(y, k) & 255:
x = (k+x)/2
return x,y,1
print (cv2.getTickCount() - ttt)/cv2.getTickFrequency() # 粗略扫描2:耗时 0.5 - 2ms 缺点:目标较近时,波动大
return x, y, 0
def get_mid_y(image): #不需要找y了
size = image.shape
h = size[0]
w = size[1]
flag_back = 0
x = 0
y = 0
ttt = cv2.getTickCount()
for i in range(0,w,3):
for j in range(h):
if image.item(j, i) & 255:
flag_back = 1
y = j
x = i
break
if flag_back:
break
for k in range(h,y):
if image.item(k, x) & 255:
y = (k+y)/2
return x,y,1
print (cv2.getTickCount() - ttt)/cv2.getTickFrequency() # 粗略扫描:耗时 0.5 - 2ms 缺点:目标较近时,波动大
return x, y, 0
-
结合上两种,只找第一点,然后根据自己的需要(我所需要的是x),进行该行扫描,扫描完成后判断该行满足要求像素多少,若数量达到要求则完成,否则继续扫描下一行直到该行目标像素满足要求。最后取均值得出x的中点。耗时 1 - 3ms..该方法解决了两点不稳定性,并且有效缩短了时间。但所制定的满足要求像素的数量需要确定好。
- 增加Y轴的值,即得出目标物的长和宽。在第k行找到x后,从图像最下面的x坐标(x,height)开始扫描,到我们得到x的坐标的点(x,k),如果找到满足目标的像素值(255)则跳出循环,中间的则为高度。称之为十字扫描法(效果不错)。
-
def get_mid_X(image, min_p): size = image.shape h = size[0] w = size[1] flag_back = 0 sum_x = 0 num = 0 x = 0 y = 0 y_num = 0 # tt = cv2.getTickCount() for i in range(0, h, 3): for j in range(w): if image.item(i, j) & 255: sum_x += j num += 1 else: if num > min_p: break if num > min_p: x = int(sum_x / num) y = i break if x > 0: for k in range(1, h - y): if image.item(h - k, x) & 255: y_num = h - k - y break # print (cv2.getTickCount() - ttt)/cv2.getTickFrequency() # 粗略扫描1:耗时 2ms左右 缺点:目标较近时,波动大 # print num return x, y, y_num
效果图:分别对应三种。图二即使加了高斯滤波抖动还是比较大
代码链接(包括树莓派端实现的自动寻灯小车代码)