霍夫变换实现球体追踪opencv

小编有个群193369905,里面分享的均是机器视觉的资料, 最近很多朋友问我如何去追踪一个乒乓球,然后利用PID算法来保证活动板的平衡,于是我利用树莓派和arduino实现了这个小实验,本文提出一种基于图像的圆形目标实时跟踪方法,用以解决圆形目标由远及近运动时跟踪稳定性不高的问题。然后将球体的中心坐标通过串口送给电机,利用电机来控制活动板的平衡。前篇博客我已经很好的讲解过了camshif原理和代码了,camshift代码可直接见上篇博客。霍夫变换检测圆的代码,并返回中心坐标和半径的代码我先贴出来吧。

#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{  
  bool flag=1;
   VideoCapture cap(0);
   if (!cap.isOpened())
    return -1;
    Mat frame,midimage,dst;
    while(flag)
    {
      cap>>frame;
      cvtColor(frame,midimage,CV_BGR2GRAY);
      blur(midimage,midimage,Size(9,9));
      //threshold(midimage,dst,96,255,THRESH_BINARY);
      vector<Vec3f>circles;
      HoughCircles(midimage,circles,CV_HOUGH_GRADIENT,1.2,10,200,100,0,0);
      for(size_t i=0;i<circles.size();i++)
      {
        Point center(cvRound(circles[i][0]),cvRound(circles[i][1]));
        int radius=cvRound(circles[i][2]);
        circle(frame,center,3,Scalar(255,255,255),-1,8,0);
        circle(frame,center,radius,Scalar(255,255,255),3,8,0);
        cout << cvRound(circles[i][0]) << "\t" << cvRound(circles[i][1]) << "\t"   
             << cvRound(circles[i][2]) << endl;
      }
    imshow("xiaorun",frame);
    if(waitKey(20)>=0)
        break;
         }
    return 0;

}

   计算机视觉应用中,目标跟踪一直是研究的热点内容之一。而在目标跟踪研究中,最值得关心的就是跟踪算法的实时性和准确性。为此,相关学者提出了很多不同的方法,其中应用最为广泛的是Gary R Bradski提出的基于自适应均值漂移(Continuously adaptive mean shift,CamShift)算法。CamShift算法依靠视频图像的色彩信息来实现对目标的跟踪,其运算速度快且比较适合不太复杂的背景下的目标跟踪,因此适合室内移动机器人对目标的跟踪。
  而对于基于视觉的室内移动机器人目标跟踪,仅仅依靠CamShift这种依靠颜色模型的跟踪方法,无法在照度不均的情况下获得圆形目标物的精确位置信息。而随机Hough圆变换具有对圆形目标识别精度高且速度快的特点。为此,本文提出采用CamShift算法与随机Hough变换结合的方法以实现对球形目标的快速、准确跟踪。
  1 算法原理
  1.1 CamShift算法介绍
  CamShift算法实际上是连续自适应的MeanShift算法的简称。该算法降低了由光照亮度变化对跟踪效果的影响,并且利用基于像素颜色概率分布信息进行目标跟踪的方法,使算法的效率比较高。CamShift算法采用基于概率密度的梯度攀升来寻找局部最优的思想对每一帧图像运用MeanShift算法做处理。根据前一帧图像的搜索结果自适应调整搜索窗的大小,从而实现对当前图像中目标的定位。
  算法主要步骤如下。
  1)首先将当前帧图像由RGB空间转换到HSV空间,并将当前图像整幅都作为搜索范围。
  2)设定搜索窗口(SearchWindow)大小与位置的初始值并计算窗口内的色度(Hue)分量概率分布直方图。
  3)对当前图像的每一个像素采用其颜色的概率值进行替换,从而得到颜色概率分布图。
  4)在颜色概率分布图中选取搜索窗。
  5)计算零阶矩:
  计算一阶矩:
  计算搜索窗的质心:
  6)调整搜索窗大小,窗口宽度为;长度为1.2w。
  7)将SearchWindow的中心移动到质心位置,当移动距离超过了设定的阈值时,则跳转到步骤5),当SearchWindow的中心与质心间的距离小于一个预设值或者迭代次数达到最大时,停止计算。
  8)在下一帧输入的图像中用上一步中的结果再一次计算搜索窗口的参数,然后跳转到步骤2)继续目标搜索。
  1.2 随机Hough圆变换介绍
  Hough变换是一种在图像中寻找直线、圆以及其他简单形状的方法。其基本思想是将原始图像中的曲线或者直线检测问题转变成在参数空间中聚类求峰值问题。由于圆形包含3个自由参数,需要在三维空间中投票求峰值,其计算量很大且对内存消耗大,算法运算时间长。因此XU等人提出了随机Hough变换(Randomized Hough Transform,RHT),使用了3个新的操作机制,即在图像空间中的随机抽样、参数空间中的动态链接列表以及连接图像空间和参数空间的收敛映射,从而加快了运算速度和提高了内存的利用率。然而随机采样会引入大量的无效累积和无效采样,在处理复杂背景和较大图像时对算法识别性能有较大的影响。因此有学者提出了改进的RHT算法,但不可否认的是在处理背景简单尤其是小图像的识别时,随机Hough变换运算速度很快,识别精度高。
  1.3 基于CamShift与RHT的球体跟踪方法
  由于CamShift算法是运用物体颜色信息和聚类的方式跟踪视频序列中的运动目标,因此算法具有较高的执行效率。而在双目移动机器人运用视差法计算球体目标三维坐标时,需要输入球体在图像中的二维坐标位置,二维坐标位置的误差过大会影响目标的定位及机器人姿态的调整。但是仅仅依靠CamShift算法返回的搜索区域的质心无法较精确的识别圆形目标的圆心及半径,考虑针对小图像运用随机Hough圆变换的具有高识别精度和运算速度快的特点,因此将CamShift和RHT两种算法结合并运用在简单的室内环境下对球形目标的跟踪上。在前文阐述的CamShift算法步骤中,步骤5)、6)、7)其实就是MeanShift算法的主要步骤,则CamShift与RHT结合的球体跟踪识别方法主要步骤表述如下。
  1)确定初始目标的位置及区域并计算目标的H分量直方图。
  2)利用直方图计算反向投影图。
  3)采用MeanShift算法在上一步的反向投影图中进行迭代搜索,当计算结果收敛或达到最大迭代次数时停止计算。
  4)利用MeanShift算法返回的搜索窗口的中心位置,取输入帧图像在周围的局部图像保存为子图像(SubImage),子图像的高度(subheight)和宽度度(subwidth)与搜索窗的宽度和长度相等。
  采用随机Hough圆变换对SubImage进行圆形特征检测。
  在子图像中若识别出圆形特征则返回其在原始图像当中的位置,假设子图像中圆心坐标为,原始图像中的圆心位置由如下坐标变换公式求得:
  5)将步骤3)中获得的新的搜索窗口参数带入到步骤2)中,继续下一帧图像的目标位置搜索。
  贴出利用camshift和霍夫变换的图片球体检测的代码吧,自行copy吧

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/video/tracking.hpp"
#include <tuple>

using namespace cv;
using namespace std;

//This is used to obtain a window inside the image,
//and cut it around the image borders.
Rect GetROI(Size const imageSize, Point const center, int windowSize)
{
    Point topLeft (center.x - windowSize / 2, center.y - windowSize / 2);

     if (topLeft.x + windowSize > imageSize.width || topLeft.y + windowSize >
         imageSize.height)
    {
         windowSize = 
         min(imageSize.width - topLeft.x, imageSize.height - topLeft.y);
    } 
    return Rect(topLeft.x, topLeft.y, windowSize, windowSize);
}

//This is used to find pixels that likely belong to the circular object
//we wish to track.
Mat HistogramBackProjectionForTracking(Mat const& image, Rect const window)
{
     const int sizes[] = {256,256,256};
     float rRange[] = {0,255};
     float gRange[] = {0,255};
     float bRange[] = {0,255};
     const float *ranges[] = {rRange,gRange,bRange};
     const int channels[] = {0, 1, 2};

     Mat roi = image(window);

     Mat hist;
     if (image.channels() == 3)
      calcHist(&roi, 1, channels, Mat(), hist, 3, sizes, ranges);
     else
      calcHist(&roi, 1, &channels[0], Mat(), hist, 1, &sizes[0], &ranges[0]);

     Mat backproj;
     calcBackProject(&image, 1, channels, hist, backproj, ranges);
     return backproj;
}

//Return a new circle by using CAMSHIFT to track the object around the initial circle.
tuple<Point, int> HoughShift(Point const center, int const radius, Mat const& image)
{
    Mat backproj = HistogramBackProjectionForTracking(image, 
    GetROI(image.size(),center, radius));

     //Fill holes:
     cv::dilate(backproj, backproj, cv::Mat(), cv::Point(-1,-1));
     cv::dilate(backproj, backproj, cv::Mat(), cv::Point(-1,-1));

    const int windowTrackingSize = 4 * radius;
    RotatedRect track = CamShift(backproj, GetROI(image.size(), center,
    TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));

    return make_tuple(track.center, (track.size.width + track.size.height )/ 4);
}

int main(int argc, char** argv)
{
     Mat image = cv::imread("image.jpg");

     Mat before, after; image.copyTo(before); image.copyTo(after);
     Mat gray; cv::cvtColor(image, gray, CV_BGR2GRAY);

     std::vector<cv::Vec3f> circles;
     HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 2, gray.cols / 3, 20, 40,
     gray.cols / 20, gray.cols / 5);

     for (int i = 0; i < circles.size(); ++i)
     {
         auto circle = HoughShift(Point(circles[i][0], circles[i][1]), 
         circles[i][2], image);

         circle(before, Point(circles[i][0], circles[i][1]), circles[i][2], 
         Scalar(128, 128, 30),  2);
         circle(after, get<0>(circle), get<1>(circle), Scalar(255, 0 , 0), 2);
     }

     imshow("Initial Circles", before);
     imshow("Refined Circles", after);
     waitKey(-1);

     return 0;
}

那么如何结合二者,在摄像头中实时的追踪球体呢,可以发邮件到1039463596@qq.com,小博看到立马会回复的

<think>嗯,用户想在树莓派上用OpenCV做乒乓球识别,这个问题看起来挺有意思的。首先,我需要理清楚树莓派的硬件限制,毕竟树莓派的处理能力不如台式机,所以得考虑优化算法,不能太耗资源。然后,OpenCV的物体检测方法有很多种,比如颜色分割、轮廓检测、霍夫变换之类的,得选适合乒乓球的方法。 首先,乒乓球通常是白色或橙色的,颜色比较单一,可能可以用颜色阈值分割。但光照条件会影响颜色识别,可能需要自适应阈值或者HSV颜色空间。比如,把图像转换到HSV空间,然后根据乒乓球的颜色范围来提取区域。不过实际场景中可能会有其他物体干扰,可能需要结合形状分析。 接下来是形状分析。乒乓球的轮廓应该接近圆形,所以可以用霍夫圆检测。不过霍夫圆检测在树莓派上可能会比较慢,尤其是图像分辨率高的时候。可能需要先缩小图像尺寸,或者用一些优化手段,比如限制检测的区域或者半径范围。另外,动态调整参数也很重要,比如在摄像头实时处理时,根据实际情况调整霍夫变换的参数。 然后是运动检测,因为乒乓球在运动中,可以考虑帧间差分或者光流法来捕捉运动物体,这样可以减少需要处理的区域,提高效率。不过运动检测可能会受到摄像头抖动或者背景变化的影响,可能需要结合其他方法。 硬件配置方面,树莓派的摄像头模块需要调整合适的分辨率和帧率,比如用640x480的分辨率,30fps,这样在性能和实时性之间取得平衡。另外,可能需要外接光源来保证光线充足,减少环境光变化的影响。 代码实现的话,大致流程应该是:读取摄像头图像,预处理(比如转HSV、高斯模糊),颜色分割,形态学操作去除噪声,霍夫圆检测,然后筛选出符合条件的圆作为乒乓球。如果检测不到,可能需要调整参数或者结合其他方法,比如模板匹配或者机器学习模型,不过后者在树莓派上可能资源不够,可以考虑用轻量级的模型,比如MobileNet之类的。 另外,用户提到的霍夫变换球体追踪的结合,可能需要先检测候选区域,再用霍夫变换来找圆,这样可以提高效率。例如,先用颜色分割得到可能的区域,然后在那些区域里做霍夫变换,而不是全图检测,这样计算量会小很多。 可能遇到的问题包括:检测速度不够快,导致延迟;误检其他圆形物体;光照变化导致颜色分割失效。针对这些,可以优化算法步骤,比如降低图像分辨率,使用ROI(感兴趣区域),或者加入自适应阈值处理。另外,可以考虑多线程处理,把图像采集和处理分开,提高帧率。 最后,测试和调试阶段需要实际运行代码,观察效果,逐步调整参数。可能需要用不同的场景来测试,确保鲁棒性。如果效果不好,可能需要结合多种方法,比如颜色+形状+运动检测,综合判断乒乓球的位置。</think>### 树莓派+OpenCV实现乒乓球识别的关键步骤 #### 1. 硬件准备与配置 - 树莓派4B及以上型号(推荐4GB内存) - 官方摄像头模块(CSI接口)或USB摄像头 - 配置OpenCV4.5+(建议使用轻量化编译版本) - 安装`picamera`库控制CSI摄像头: ```python from picamera.array import PiRGBArray from picamera import PiCamera ``` #### 2. 核心识别流程 ```python import cv2 import numpy as np # 初始化摄像头 camera = PiCamera() camera.resolution = (640, 480) raw_capture = PiRGBArray(camera, size=(640, 480)) for frame in camera.capture_continuous(raw_capture, format="bgr", use_video_port=True): image = frame.array # 预处理 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) blur = cv2.GaussianBlur(hsv, (11,11), 0) # 颜色阈值分割(以白色乒乓球为例) lower = np.array([0,0,200]) # H:0-180, S:0-50, V:200-255 upper = np.array([180,50,255]) mask = cv2.inRange(blur, lower, upper) # 形态学操作 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 霍夫圆检测[^1] circles = cv2.HoughCircles(mask, cv2.HOUGH_GRADIENT, 1, 50, param1=50, param2=30, minRadius=10, maxRadius=50) # 绘制检测结果 if circles is not None: circles = np.round(circles[0, :]).astype("int") for (x, y, r) in circles: cv2.circle(image, (x, y), r, (0,255,0), 4) # 显示结果 cv2.imshow("Detection", image) raw_capture.truncate(0) if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() ``` #### 3. 优化技巧 1. **分辨率控制**:使用$640\times480$或更低分辨率 2. **ROI限制**:仅在球桌区域进行检测 3. **动态参数调整**:根据检测结果自动调整霍夫变换参数 4. **多帧验证**:连续3帧检测到同一位置才确认为有效目标 #### 4. 常见问题解决方案 | 问题现象 | 解决方法 | |---------|----------| | 漏检小球 | 降低`param2`阈值或扩大颜色范围 | | 误检噪点 | 增大`minRadius`值或增强形态学滤波 | | 延迟严重 | 改用灰度空间处理或降低分辨率 |
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiao__run

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值