基于靶标的二维视觉定位补偿

一、前言

省略了相机的内外参数标定,默认相机没有畸变,对如下场景进行视觉定位。

场景设定如下:

1、相机镜头垂直水平面固定,也就是说本场景只能进行二维平面的定位补偿,默认Z轴坐标等于0;

2、相机完成标定,即像素坐标能够转换为世界坐标;

3、靶标是如图的一张白纸,白纸上有一个黑色圆形和一个黑色三角形;

定位补偿的想法:

不论是二维平面定位还是三维空间定位,目的都是想要获取目标的位置信息,而位置信息不仅包含了x,y,z=0,还包含旋转角度。当然,在二维平面中旋转角度只有一个,在三维空间中旋转角度有三个。

1、设定模板:

也就是在之前我们设定的场景中,把靶标放在你想放的位置上,然后拍一张照。假设就是上图所示。

因为我们已经建立了像素坐标系,以图像的左上角为原点。所以,进行图像处理得到黑色圆的质点坐标X_coordinate=[x0,y0](名字无所谓),这个坐标就是模板坐标。当然为了简单,我们设定模板的时候,最好让向量AB能够和X轴平行且方向相同,因为这样的话AB向量和X轴的夹角就是0°,默认为靶标没有发生旋转。

2、计算偏移量:

当模板坐标确定后,如果靶标发生偏移,则再次识别圆,确定圆的质点坐标[x1,y1]。则相对模板靶标的偏移量就是[x1-x0,y1-y0]。

3、计算旋转角度:

知道了坐标的偏移量还不行啊,因为我们不知道靶标往那个方向偏移了,所以要确定靶标相对模板的旋转角度。

在x轴上任意取一点[112,0],与原点[0,0],可以构成一个向量X_Particle = np.array([112,0])。然后找到圆的质点和三角形的质点,求出AB向量。最后根据向量夹角的公式就能算出旋转角度。

注意:向量的夹角范围是0到180,所以要根据向量AB的方向来判断。这里我判断如果向量AB的方向和Y轴方向一致,则夹角为正;否则夹角为否。

二、完整代码

import cv2
import numpy as np


#定义线程函数——
def PositionParticle(frame):
    gauss_img = cv2.GaussianBlur(frame,(3,3),0)
    gray_img = cv2.cvtColor(gauss_img,cv2.COLOR_RGB2GRAY)
    _,er_img = cv2.threshold(gray_img,90,255,cv2.THRESH_BINARY_INV)
    cv2.imshow('er',er_img)
    #提取轮廓
    contours,_ = cv2.findContours(er_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    #提取质点
    Particle = []
    for contour in contours:
        contour_ = np.squeeze(contour)
        x = np.int(np.sum(contour_,axis=0)[0] / len(contour_))
        y = np.int(np.sum(contour_,axis=0)[1] / len(contour_))
        Particle.append([x,y])
    #debug
    print(Particle)

    #霍夫圆检测——找到图像中的圆——鲁棒性真差
    circles = cv2.HoughCircles(gray_img,cv2.HOUGH_GRADIENT,1,10,param1=100,param2=35)
    #debug
    print(circles)
    ######################求方向向量###鲁棒性差#############
    Particle = np.array(Particle)
    #定义方向向量的起始点A和终止点B
    A,B = [],[]
    for circle in circles[0,:]:
        i = np.array(circle[:2])
        for j in Particle:
            #判断已求质心中,那个是圆的——是圆的就赋值给A点
            if np.abs(np.sum(j-i)) < 5:
                B = j
            else:
                A = j
    Poisiton = np.array(B) - np.array(A)
    return Poisiton,B
#坐标域判断函数
def Coordinate(x_particle,mark_particle):
    if mark_particle[1] >= 0:
        angle = np.arccos(x_particle.dot(mark_particle) /
                          (np.sqrt(x_particle.dot(x_particle)) * np.sqrt(mark_particle.dot(mark_particle)))) * 180 / np.pi
    else:
        angle = np.arccos(x_particle.dot(mark_particle) /
                          (np.sqrt(x_particle.dot(x_particle)) * np.sqrt(mark_particle.dot(mark_particle)))) * (-180) / np.pi
    return angle



if __name__ == '__main__':
    #定义一个X轴的方向向量
    X_Particle = np.array([112,2])
    #定义模板坐标
    X_coordinate = np.array([285,85])
    #debug
    print(X_Particle)
    video = cv2.VideoCapture(1)
    while video.isOpened():
        ok,frame = video.read()
        if not ok:
            print('video open error')
            break
        else:
            cv2.imshow('src',frame)
            key = cv2.waitKey(5)
            #定义键盘事件
            if key & 0XFF == 27:    #Esc事件
                break
            if key & 0XFF == 32:    #Space事件
                Mark_Particle,Circle = PositionParticle(frame)
                #debug
                print(Mark_Particle)
                #角度偏差
                angle = Coordinate(X_Particle,Mark_Particle)
                print(angle)
                #坐标偏差
                lass = Circle - X_coordinate
                print(lass)


    cv2.destroyAllWindows()

三、代码解释

首先说明,代码能够运行,但是这就是一个练手的demo,不要期望精度有多高。另外,圆的识别也是一个问题,每次圆的质点坐标都有1到2个像素的偏差。

在PositionParticle(frame)函数中,先使用轮廓提取图像中的圆和三角形最外层轮廓;根据每一个轮廓的点,能求出轮廓对应的质点,但是不能确定那个是圆的质点,那个是三角形的质点。所以在进行霍夫圆检测(如果识别不到圆,就把param2参数设小点),然后让两个质点分别和检测出的圆心做距离比较(距离可以设置为圆半径的五分之一),距离越小的应该就是圆的质点;最后就能求得AB向量。

函数Coordinate(x_particle,mark_particle),使用mark_particle[1]判断就是AB向量的y值,当mark_particle[1]大于等于0时,AB向量和x轴夹角就是正的;当其小于0时,AB向量和x轴夹角就是否的。

原创文章 24 获赞 45 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weilixin88/article/details/103498777
今日推荐