Search code examples
c++opencvkalman-filter

Use Kalman Filter to filt noise without delay opencv c++


My task is to detect an orange ball in video. I detected by thresholding image on HSV colorspace and bounding box. Then I have center and radius of ball, with unit is pixel.

When ball is static, I expect center and radius will be static too, but reality, it has noise. I use Kalman Filter to filter noise and it works well. But it delay in real-time. I try to optimize covariance parameters but not work.

So could anyone help me static center and radius when ball is static and without delay?


Solution

  • Are you sure it is the Kalman Filter witch is causing the delay. Otherwise you can try this lazy filter witch only is noise rejecting but blazingly fast. My suspicion however it is the HSV conversion.

    class noiseFilter
    {
    private:
        cv::Point2f ptLast;
        float ptMaxTol;
    public:
        noiseFilter(float maxTol = 1.5f)
        {
            ptMaxTol = maxTol * maxTol;             // we do the pow(2) here so we don't have to do a square root on every update
            ptLast = cv::Point2f(0.0f, 0.0f);
        }
        cv::Point2f update(cv::Point2f &ptNew)      // update filter with new found point
        {
            float dist = pDistance2(ptLast, ptNew);
            if (dist > ptMaxTol) ptLast = ptNew;    // update only if distance is more than threshold
            return ptLast;
        }
        cv::Point2f getResult()                     // get result of filter
        {
            return ptLast;
        }
    private:
        // calculate distance between 2 point without doing a sqrt
        float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
        {
            float dx = p1.x - p2.x;
            float dy = p1.y - p2.y;
            return (dx * dx + dy * dy);
        }
    };
    
    int main()
    {
        cv::Point2f pt;
        noiseFilter filter(2.1f);           // initialize filter wit max 2.1 pixels noise rejection.
        int x = 100, y = 120;
    
        for (int i = 0; i < 100; i++)
        {
            // generate some noise with 2 pixels variation
            pt.x = ((rand() % 200) - 100) * 0.01f + x;
            pt.y = ((rand() % 200) - 100) * 0.01f + y;
    
            cv::Point2f pts = filter.update(pt);
            printf("input x=%6.2f y=%6.2f  output x=%6.2f y=%6.2f\r\n", pt.x, pt.y, pts.x, pts.y);
    
            // do som random big update on random intervals
            if ((rand() % 50) == 1) {
                x += 15;
                printf("big update on X\r\n");
            }
            if ((rand() % 50) == 1){
                y += 25;
                printf("big update on Y\r\n");
            }
    
        }
        return 0;
    }
    

    Below a noise filter with smoothing. Works on slow and fast moving objects.

    class noiseFilterSmooth
    {
    private:
        static const int maxHist = 10;
        cv::Point2f ptLast;
        float ptMaxTol;
        cv::Point2f hist[maxHist];
        int histHead,histSize;
    public:
        noiseFilterSmooth(float maxTol = 1.5f)
        {
            histHead = histSize = 0;
            ptMaxTol = maxTol * maxTol;             // we do the pow(2) here so we don't have to do a square root on every update
            ptLast = cv::Point2f(0.0f, 0.0f);
        }
        cv::Point2f& update(cv::Point2f &ptNew)     // update filter with new found point
        {
            float dist = pDistance2(ptLast, ptNew);
            if (dist > ptMaxTol)  histSize = histHead = 0;      // reset smoothing filter if distance is more than threshold
            // update smoothing filter with last result
            hist[histHead] = ptNew;                 // update smoothing filter with last 
            histHead = (histHead + 1) % maxHist;
            if (histSize < maxHist) histSize++;
            return getResult();
        }
        cv::Point2f& getResult()                        // get result of filter
        {
            float sumx = 0, sumy = 0;
            for (int i = 0; i < histSize; i++)
            {
                sumx += hist[i].x;
                sumy += hist[i].y;
            }
            ptLast.x = sumx / histSize;
            ptLast.y = sumy / histSize;
            return ptLast;
        }
    private:
        // calculate distance between 2 point without doing a sqrt
        float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
        {
            float dx = p1.x - p2.x;
            float dy = p1.y - p2.y;
            return (dx * dx + dy * dy);
        }
    };