爱板网论坛

查看: 287|回复: 2

[大赛作品提交] 如何用树莓派追踪一个小气球

[复制链接]

22

主题

0

好友

873

积分

举人

Rank: 4

  • TA的每日心情
    慵懒
    2017-12-22 00:01
  • 签到天数: 97 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2017-12-26 22:48:07 |显示全部楼层
    本帖最后由 ky123 于 2017-12-27 09:13 编辑

    感谢e络盟官方提供的助赛基金。
    上个帖子我介绍了学习RGB转HSV颜色空间转换的过程,里面提到可以通过对应的阈值来滤掉背景,保留对应的色域目标。但是我哪里使用的阈值是直接写到程序中的,人观察到的物体,不可能直接指导他颜色对应的阈值范围,需要不断尝试。这样子操作就十分麻烦了,需要不断改写程序参数,再运行测试。
    这两天刚好学习到了cv2.creatTrackbar()函数,就是建立滑条,通过调节滑动条来设置颜色的阈值。定次滑动都会调用回调函数,回调函数通常都会含有一个默认参数,就是滑动条的位置不生产,只搬运,我是一个辛勤的劳动人民。感谢万能的互联网,我受益匪浅。

    首先,创建两个窗口,一个用来显示上限阈值,另一个用来显示下限阈值。

    然后,创建六个滑条,分别对应H、S、V的上下限阈值。

    接着,运行程序,滑动滑条,然后程序会读取滑条数值,赋到阈值变量值。

    最后,阈值滤波,杂物归于背景,只有目标留在画面中。

    code如下:

    #-*-coding:utf-8-*-

    import cv2.cv

    import cv2.cv as cv

    import numpy as np


    # get video from USB camera

    cap = cv2.VideoCapture(0)


    #Set the size of video as 640*480

    cap.set(3,640)

    cap.set(4,480)


    def nothing(x):

        pass


    cv2.namedWindow('HSV')

    cv2.namedWindow('camrea')

    cv2.namedWindow('lower_hsv')

    cv2.namedWindow('upper_hsv')


    cv2.createTrackbar('hmin', 'lower_hsv',100,179,nothing)

    cv2.createTrackbar('smin', 'lower_hsv',100,255,nothing)

    cv2.createTrackbar('vmin', 'lower_hsv',100,255,nothing)


    cv2.createTrackbar('hmax', 'upper_hsv',100,179,nothing)

    cv2.createTrackbar('smax', 'upper_hsv',100,255,nothing)

    cv2.createTrackbar('vmax', 'upper_hsv',100,255,nothing)


    while(1):

        ret, frame = cap.read()

        #converting to HSV

        hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)


        lowh = cv2.getTrackbarPos('hmin','lower_hsv')

        lows = cv2.getTrackbarPos('smin','lower_hsv')

        lowv = cv2.getTrackbarPos('vmin','lower_hsv')


        uph = cv2.getTrackbarPos('hmax','upper_hsv')

        ups = cv2.getTrackbarPos('smax','upper_hsv')

        upv = cv2.getTrackbarPos('vmax','upper_hsv')


        lower_threshold = np.array([lowh,lows,lowv])

        upper_threshold = np.array([uph,ups,upv])


        mask = cv2.inRange(hsv,lower_threshold,upper_threshold)

        hsv = cv2.bitwise_and(frame,frame,mask=mask)


        cv2.imshow('HSV',hsv)   

        cv2.imshow('camrea',frame)

        #Hold the video windos

        cv2.waitKey(1)


    cap.release()

    cv2.destroyAllWindows()
    图片1.png

    ============================================================

    接下来是主题了,如何利用还没发霉的树莓派来追踪运动物体。

    我在网上泡了小半个月,总算了解到了一点知识。现在比较常用的方法有两种,一种是通过物体的轮廓形状来识别最终,另外一种这是通过提取颜色特征值来识别追踪。接下来我以这个蓝色的小气球为例,说一下我的学习经验。
    图片2.png

    轮廓形状识别:

    轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。大概步骤如下:

    采集每一帧图像,进行RGB转HSV空间颜色变化

    寻找合适的阈值,滤去杂物,只保留气球

    对图像进行腐蚀膨胀,然后做闭运算。

    利用 Hough 变换在图像中找圆

    圈出气球,显示到屏幕上。

    程序如下:

    #-*-coding:utf-8-*-

    import cv2.cv

    #import cv2.cv as cv

    import numpy as np


    # get video from USB camera

    cap = cv2.VideoCapture(0)


    #Set the size of video as 640*480

    cap.set(3,640)

    cap.set(4,480)


    def nothing(x):

       pass


    cv2.namedWindow('HSV')

    cv2.namedWindow('camrea')

    cv2.namedWindow('lower_hsv')

    cv2.namedWindow('upper_hsv')


    cv2.createTrackbar('hmin', 'lower_hsv',100,179,nothing)

    cv2.createTrackbar('smin', 'lower_hsv',100,255,nothing)

    cv2.createTrackbar('vmin', 'lower_hsv',100,255,nothing)


    cv2.createTrackbar('hmax', 'upper_hsv',100,179,nothing)

    cv2.createTrackbar('smax', 'upper_hsv',100,255,nothing)

    cv2.createTrackbar('vmax', 'upper_hsv',100,255,nothing)


    while(1):

        ret, frame = cap.read()

        #converting to HSV

        hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)


        lowh = cv2.getTrackbarPos('hmin','lower_hsv')

        lows = cv2.getTrackbarPos('smin','lower_hsv')

        lowv = cv2.getTrackbarPos('vmin','lower_hsv')


        uph = cv2.getTrackbarPos('hmax','upper_hsv')

        ups = cv2.getTrackbarPos('smax','upper_hsv')

        upv = cv2.getTrackbarPos('vmax','upper_hsv')


        lower_threshold = np.array([lowh,lows,lowv])

        upper_threshold = np.array([uph,ups,upv])


        mask = cv2.inRange(hsv,lower_threshold,upper_threshold)

    eroding = cv2.erode(mask, None, iterations=2)  

    dilating = cv2.dilate(eroding, None, iterations=2)

        closing = cv2.morphologyEx(dilating, cv2.MORPH_CLOSE, None)  #闭运算

        closing = cv2.GaussianBlur(closing,(5,5),0)

            

    circles = cv2.HoughCircles(closing,cv.CV_HOUGH_GRADIENT,2,120,param1=120,param2=50,minRadius=10,maxRadius=0)

            

        #Draw Circles

        if circles is not None:

                for i in circles[0,:]:

                    # If the ball is far, draw it in green

                    if int(round(i[2])) < 30:

                        cv2.circle(frame,(int(round(i[0])),int(round(i[1]))),int(round(i[2])),(0,255,0),5)

                        cv2.circle(frame,(int(round(i[0])),int(round(i[1]))),2,(0,255,0),10)

        # else draw it in red

                    elif int(round(i[2])) > 35:

                        cv2.circle(frame,(int(round(i[0])),int(round(i[1]))),int(round(i[2])),(0,0,255),5)

                        cv2.circle(frame,(int(round(i[0])),int(round(i[1]))),2,(0,0,255),10)


        hsv = cv2.bitwise_and(frame,frame,mask=mask)


        cv2.imshow('HSV',hsv)   

        cv2.imshow('camrea',frame)

        #Hold the video windos

        cv2.waitKey(1)


    cap.release()

    cv2.destroyAllWindows()
    图片3.png


    里面比较重要的就是Hough 变换了。霍夫圆变换累加平面是一个三维累加容器(x,y,r),x,y确定圆心,r确定半径(不懂没关系,我也不懂)。OpenCV通过一个比较灵活的霍夫梯度法来解决圆变换问题,反正就是一个函数的事,拿来就是用。霍夫圆变换的函数为:


    CvSeq* cvHoughCircles( CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1=100, double param2=100, int min_radius=0, int max_radius=0 );


    image:输入 8-bit、单通道灰度图像.

    circle_storage:检测到的圆存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回)或者是包含圆参数的特殊类型的具有单行/单列的CV_32FC3型矩阵(CvMat*). 矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的圆。如果 circle_storage 是矩阵,而实际圆的数目超过矩阵尺寸,那么最大可能数目的圆被返回每个圆由三个浮点数表示:圆心坐标(x,y)和半径.

    method:Hough 变换方式,目前只支持CV_HOUGH_GRADIENT, which is basically 21HT, described in [Yuen03].

    dp:累加器图像的分辨率。这个参数允许创建一个比输入图像分辨率低的累加器。(这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。

    min_dist:该参数是让算法能明显区分的两个不同圆之间的最小距离。

    param1:用于Canny的边缘阀值上限,下限被置为上限的一半。

    param2:累加器的阀值。

    min_radius:最小圆半径。

    max_radius:最大圆半径。


    查找轮廓的函数会修改原始图像。如果之后想继续使用原始图像,应该将原始图像储存到其他变量中。在OpenCV中,查找轮廓就像在黑色背景中超白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。就效果来看,确实可以实现气球追踪了。但是,还是需要说一下但是,因为建立圆存储仓,需要大量的内存,这就导致了我的树莓派运行起来其实很吃力,我是640*480的像素,延时差不多一秒多了,效果仿佛十年前的山寨手机打开了摄像头。而且追踪点很不稳定,圆心一直在跳,简单的处理确实没办法达到很好的效果。


    提取颜色特征值识别:

    这个就比较简单了,我以前用单片机的时候遇到颜色识别问题,都是用这种方法,压力不大,很简单,历遍全图,阈值滤波,提取色块轮廓,圈起来,就ok了,直接看code就能理解:

    while(1):

        ret, frame = cap.read()

        #converting to HSV

        hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)


        lowh = cv2.getTrackbarPos('hmin','lower_hsv')

        lows = cv2.getTrackbarPos('smin','lower_hsv')

        lowv = cv2.getTrackbarPos('vmin','lower_hsv')


        uph = cv2.getTrackbarPos('hmax','upper_hsv')

        ups = cv2.getTrackbarPos('smax','upper_hsv')

        upv = cv2.getTrackbarPos('vmax','upper_hsv')


        lower_threshold = np.array([lowh,lows,lowv])

        upper_threshold = np.array([uph,ups,upv])


        mask = cv2.inRange(hsv,lower_threshold,upper_threshold)

    eroding = cv2.erode(mask, None, iterations=2)  

    dilating = cv2.dilate(eroding, None, iterations=2)

    outline = cv2.findContours(dilating.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]

            

    center = None

    if len(outline) > 0:

         c = max(outline, key = cv2.contourArea)

         ((x, y), radius) = cv2.minEnclosingCircle(c)  

         M = cv2.moments(c)

      center = (int(M["m10"]/M["m00"]), int(M["m01"]/M["m00"]))

      if radius > 10:

          cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2)

       cv2.circle(frame, center, 5, (0, 0, 255), -1)


        hsv = cv2.bitwise_and(frame,frame,mask=mask)


        cv2.imshow('HSV',hsv)   

        cv2.imshow('camrea',frame)

        #Hold the video windos

        cv2.waitKey(1)
    图片4.png


    结论:就效果和效率来说,我还是偏向根据颜色来追踪物体了,反正就是好用不贵,所以我树莓派的项目就采用这个颜色特征值识别的算法。
    源码: 源码.zip (2.62 KB, 下载次数: 1)
    平台:树莓派三代+USB摄像头+openCV
    图片5.png

    演示视频:






    回复

    使用道具 举报

    114

    主题

    63

    好友

    2万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    该用户从未签到

    分区版主职务勋章

    发表于 2017-12-27 17:21:12 |显示全部楼层
    手机发帖,代码怎么搞定的
    回复

    使用道具 举报

    37

    主题

    15

    好友

    935

    积分

    版主

    Rank: 7Rank: 7Rank: 7

  • TA的每日心情
    开心
    7 小时前
  • 签到天数: 48 天

    连续签到: 17 天

    [LV.5]常住居民I

    发表于 2017-12-28 19:08:40 |显示全部楼层
    什么算法呀,计算量有点大,气球动的快了,小圆圈跟踪不过来了,稍微有点延迟
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /3 下一条

    手机版|爱板网 |网站地图  

    GMT+8, 2018-1-16 21:30 , Processed in 0.552185 second(s), 14 queries , Memcache On.

    苏公网安备 32059002001056号

    Powered by Discuz!

    回顶部