2020电赛G题图像OPENMV实现

2020电赛G题图像OPENMV实现

平面部分

1.目标要求:识别出正方形、圆形、三角形的形状、边长、颜色。
2.算法思路:

  • 图像预处理,选取双边滤波,去除一些噪点并保留边缘特征(实测中改变效果并没有很明显)。
  • 先对圆形和正方形进行判别,采用霍夫圆检测与矩形检测,查找此区域LAB的值与RGB三色的LAB进行比较,如果符合则置标志位。
  • 若没检测到,则查找色块,并判断色块的Solidity(色块占外接矩形比重),外接矩形长宽比,圆度,像素大小等条件综合判断是否为三角形。
  • 数据统计并清洗,在几个时间周期内统计视野中检测到最多的结果将其发送出去。(若检测不稳定,则需要减小值)
while True:
    clock.tick()
    fs_cnt = fs_cnt + 1
    img = sensor.snapshot().rotation_corr(z_rotation=180)
    img.lens_corr(1.8)
    img.bilateral(1)
    #img.draw_cross(80, 60, color=(255,255,255))

    # 初始化
    x_location = 0
    y_location = 0
    shape = 0     # 0没检测到1圆形2正方形3三角形4篮球5足球6排球
    color = 0     # 0没检测到1圆形2正方形3三角形4篮球5足球6排球
    sidelen = 0   # 单位cm
    area = 0



    #识别圆形
    for c in img.find_circles(threshold=2000, x_margin=10, y_margin=10, r_margin=10,
                              r_min=5, r_max=30, r_step=2):
        area = (c.x() - c.r(), c.y() - c.r(), 2 * c.r(), 2 * c.r())
        # area为识别到的圆的区域,即圆的外接矩形框
        statistics = img.get_statistics(roi=area)  # 像素颜色统计
        #print(statistics)

        #红色圆
        if 0<statistics.l_mode()<100 and 20<statistics.a_mode()<127 and 10<statistics.b_mode()<127:
            color = 1
            sidelen =2 * c.r()
            shape = 1
            x_location = c.x()
            y_location = c.y()

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = 3.14*c.r()*c.r()
            color_list[shape] = color

            img.draw_cross(c.x(), c.y(), size=5, color=(255,0,255))
            img.draw_circle(c.x(), c.y(), c.r(), color=(255, 0, 255))

        #绿色圆
        if 0<statistics.l_mode()<100 and -128<statistics.a_mode()<0 and 0<statistics.b_mode()<127:
            color = 2
            sidelen =2 * c.r()
            shape = 1
            x_location = c.x()
            y_location = c.y()

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = 3.14*c.r()*c.r()
            color_list[shape] = color

            img.draw_cross(c.x(), c.y(), size=5, color=(255,0,255))
            img.draw_circle(c.x(), c.y(), c.r(), color=(255, 0, 255))





        #蓝色圆
        if 0<statistics.l_mode()<100 and -128<statistics.a_mode()<127 and -128<statistics.b_mode()<-2:
            color =  3
            sidelen =2 * c.r()
            shape = 1
            x_location = c.x()
            y_location = c.y()

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = 3.14*c.r()*c.r()
            color_list[shape] = color

            img.draw_cross(c.x(), c.y(), size=5, color=(255,0,255))
            img.draw_circle(c.x(), c.y(), c.r(), color=(255, 0, 255))



    #矩形识别
    for d in img.find_rects(threshold=15500):
        area = (d[0], d[1], d[2], d[3])
        statistics1 = img.get_statistics(roi=area)  # 像素颜色统计
        print(statistics1)

        xq = 0
        xq = d[2] / d[3]
        coners = d.corners()
        sb = ((abs(coners[0][0] - coners[1][0]))**2 + (abs(coners[0][1]-coners[1][1])**2))**0.5
        if 0<statistics1.l_mode()<100 and 20<statistics1.a_mode()<127 and 10<statistics1.b_mode()<127 and 0.8<xq<1.2:
            color = 1
            x_location = int(d[0] + d[2] / 2)
            y_location = int(d[1] + d[3] / 2)
            shape = 2
            sidelen = sb
            img.draw_rectangle(d.rect(), color=(255, 255, 0))
            img.draw_cross(x_location, y_location, color=(255,255,0))

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = d[2]*d[3]
            color_list[shape] = color

        if 0<statistics1.l_mode()<100 and -128<statistics1.a_mode()<-23 and -128<statistics1.b_mode()<127 and 0.8<xq<1.2:
            color = 2
            x_location = int(d[0] + d[2] / 2)
            y_location = int(d[1] + d[3] / 2)
            shape = 2
            sidelen = sb
            img.draw_rectangle(d.rect(), color=(255, 255, 0))
            img.draw_cross(x_location, y_location, color=(255,255,0))

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = d[2]*d[3]
            color_list[shape] = color

        if 0<statistics1.l_mode()<100 and -128<statistics1.a_mode()<127 and -128<statistics1.b_mode()<-2 and 0.8<xq<1.2:
            color = 3
            x_location = int(d[0] + d[2] / 2)
            y_location = int(d[1] + d[3] / 2)
            shape = 2
            sidelen = sb
            img.draw_rectangle(d.rect(), color=(255, 255, 0))
            img.draw_cross(x_location, y_location, color=(255,255,0))

            x_list[shape] = x_location
            y_list[shape] = y_location
            len_list[shape] = sidelen
            area_list[shape] = d[2]*d[3]
            color_list[shape] = color

    #三角
    for threshold_index in range(3):
        blob1 = img.find_blobs([thresholds[threshold_index]], pixels_threshold=40)
        xt = 0
        if len(blob1)!=0 & shape == 0:
            for b in blob1:
                Solidty = b.solidity()
                Roundness = b.roundness()
                pixels = b.pixels()
                xt = b.w()/b.h()
                print(Solidty, Roundness, pixels, xt)
                if  0.3<= Solidty <=0.815 and 0.5< xt <1.5 and Roundness < 0.9405 and pixels < 265:
                    shape = 3
                    color = threshold_index + 1
                    sidelen = (4*pixels/((3)**0.5))**0.5


                    x_location = int(b.x() + 0.5 * b.w())
                    y_location = int(b.y() + 0.5 * b.h())
                    img.draw_rectangle(b.x(), b.y(), b.w(), b.h(), color=(0, 255, 255))
                    img.draw_cross(x_location, y_location, color=(0,255,255))

                    x_list[shape] = x_location
                    y_list[shape] = y_location
                    len_list[shape] = sidelen
                    area_list[shape] = b.pixels()
                    color_list[shape] = color
                    #print(sidelen)



############################################################
# 数据清洗

    shapeList[shape] += 1

    # if x_location!= 0 | y_location != 0:
    if fs_cnt%3 == 0 :
        fs_cnt = 0
        shape_result = shapeList.index(max(shapeList))
        shapeList = [0, 0, 0, 0, 0, 0, 0]
        x_location = x_list[shape_result]
        y_location = y_list[shape_result]
        sidelen  = int(len_list[shape_result]/144.5*100*10)
        area = int(area_list[shape_result])
        color = color_list[shape_result]

        #print(shape)
        #print(sidelen)
        #print(area)
        #print(x_location, y_location)


        #x_draw = int(x_location - 0.5*sidelen)
        #y_draw = int(y_location - 0.5*sidelen)


        if (x_location != 0 or y_location != 0) and sidelen != 0:

            img.draw_string(x_location,y_location,str(shape_result), color=(255,165,0),scale=1.5)

            #img.draw_string(x_draw,y_draw,str(x_location&','&y_location), color=(0,0,0), scale=1)
            #print(sidelen)
            data = bytearray([0x5a, 0x78, x_location, y_location, shape_result, sidelen, color, 0xb3])
            print(data[6])
            #print(data)
            uart.write(data)  # 发送

立体部分

  • 球体由于在普通镜头内是一团黑,换了可调焦镜头镜头对其进行识别。
  • 球形定位,中心点比较难识别,所以我们剑走偏锋,采用一个非常规的方法进行球形位置定位。让舵机云台在左右30度水平方向扫描(题目内高度并不会变,只需要改变x轴),当遇到距离突变的时候停下来,这个点就是球类的位置(此原理有点类似于激光雷达的感觉)。
  • 此时镜头就有球类的整体轮廓可以进行识别了。
  • 对图像先进行gamma变换来增强图像的亮度与对比度,这样球类的颜色就能比较突出。
  • 简单写下大致代码
# 篮球
    for blob_bask in img.find_blobs([basketball_thresholds],pixels_thresholds=1000):

        pixel_bask =  blob_bask.pixels()
        if pixel_bask >200:
            x_location = int(blob_bask[0]+0.5*blob_bask[2])
            y_location = int(blob_bask[1]+0.5*blob_bask[3])

            Radius = int(0.5*blob_bask[2]+0.5*blob_bask[3])

            img.draw_circle(x_location,y_location,Radius,color=(255,0,0))
            img.draw_cross(x_location,y_location,color=(255,0,0))
            shape = 4
            color = 4
            sidelen = 24
        # 排球
        #if shape == 0 :
    for c in img.find_circles(threshold = 2000, x_margin = 10, y_margin = 10, r_margin = 10,r_min = 6, r_max = 100, r_step = 2):

        vball_yellow =  img.find_blobs([volleyball_yellow_thresholds], invert=False)
        #vball_blue = img.find_blobs([volleyball_blue_thresholds],invert=False)

        #img.binary(volleyball_thresholds)
        ##bin_vball = img.find_rects(threshold = 20000)
        vball = len(vball_yellow) #+ len(vball_blue)

        if vball > MyVolleyball.yellow_cnt :
            shape = 6
            color = 6
            sidelen = 22
            x_location = c.x()
            y_location = c.y()

            x_list[shape] = x_location
            y_list[shape] = y_location
            img.draw_circle(x_location, y_location, c.r(), color=(255,255,0))
            img.draw_cross(x_location, y_location, color=(255,255,0))
####################################################################
        if shape == 0 :
            for football in img.find_circles(threshold = 1400, x_margin = 10, y_margin = 10, r_margin = 10,r_min = 6, r_max = 100, r_step = 2):
                fball = img.find_blobs([football_threshold])
                if len(fball) > 3:
                    shape = 5
                    color = 5
                    sidelen = 22
                    x_location = c.x()
                    y_location = c.y()
                    img.draw_circle(x_location, y_location, c.r(), color=(0,0,0))
       if x_location !=0 and y_location!= 0 and shape != 0:
	        print(shape)
	        #img.draw_
	        img.draw_string(x_location,y_location,str(shape), color=(255,165,0),scale=1.5)
	
	        #img.draw_string(x_draw,y_draw,str(x_location&','&y_location), color=(0,0,0), scale=1)
	
	        data = bytearray([0x5a, 0x5a, x_location, y_location, shape, sidelen, color, 0xb3])
	        print(data[4],data[5],data[6])
	        uart.write(data)  # 发送

    print("FPS %f" % clock.fps())

测实际物体边长

根据小孔成像原理,拟合出实际边长与像素变长的关系,采用工具MATLAB进行曲线拟合。

通信协议

 data = bytearray([0x5a, 0x78, x_location, y_location, shape_result, sidelen, color, 0xb3])

x_location,y_location 为色块中心坐标,sidelen 为边长。

需要改进的问题

1.存在误判,在三角形与圆形中会存在误判,是因为用的不是可调焦距的镜头,若对三角形进行三线段检测,会有太多杂物出现在视野中影响判断。
2.在三角形的识别中,镜头正对着图形对容易识别不到,所以在目标锁定的时候,先让舵机上抬一个角度就解决问题了。

参考资料

1.2020 电设G题 图像题解
2.opencv/openmv识别三角形思路(识别多边形)
3.一种基于opencv的分辨检测圆形,三角形,矩形的思路

你可能感兴趣的:(2020电赛G题图像OPENMV实现)