Search code examples
opencvhough-transform

Search for lines with a small range of angles in OpenCV


I'm using the Hough transform in OpenCV to detect lines. However, I know in advance that I only need lines within a very limited range of angles (about 10 degrees or so). I'm doing this in a very performance sensitive setting, so I'd like to avoid the extra work spent detecting lines at other angles, lines I know in advance I don't care about.

I could extract the Hough source from OpenCV and just hack it to take min_rho and max_rho parameters, but I'd like a less fragile approach (have to manually update my code w/ each OpenCV update, etc.).

What's the best approach here?


Solution

  • Well, i've modified the icvHoughlines function to go for a certain range of angles. I'm sure there's cleaner ways that plays with memory allocation as well, but I got a speed gain going from 100ms to 33ms for a range of angle going from 180deg to 60deg, so i'm happy with that.

    Note that this code also outputs the accumulator value. Also, I only output 1 line because that fit my purposes but there was no gain really there.

    static void
    icvHoughLinesStandard2( const CvMat* img, float rho, float theta,
                           int threshold, CvSeq *lines, int linesMax )
    {
        cv::AutoBuffer<int> _accum, _sort_buf;
        cv::AutoBuffer<float> _tabSin, _tabCos;
    
        const uchar* image;
        int step, width, height;
        int numangle, numrho;
        int total = 0;
        float ang;
        int r, n;
        int i, j;
        float irho = 1 / rho;
        double scale;
    
        CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );
    
        image = img->data.ptr;
        step = img->step;
        width = img->cols;
        height = img->rows;
    
        numangle = cvRound(CV_PI / theta);
        numrho = cvRound(((width + height) * 2 + 1) / rho);
    
        _accum.allocate((numangle+2) * (numrho+2));
        _sort_buf.allocate(numangle * numrho);
        _tabSin.allocate(numangle);
        _tabCos.allocate(numangle);
        int *accum = _accum, *sort_buf = _sort_buf;
        float *tabSin = _tabSin, *tabCos = _tabCos;
    
        memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );
    
        // find n and ang limits (in our case we want 60 to 120
        float limit_min = 60.0/180.0*PI;
        float limit_max = 120.0/180.0*PI;
    
        //num_steps = (limit_max - limit_min)/theta;
        int start_n = floor(limit_min/theta);
        int stop_n = floor(limit_max/theta);
    
        for( ang = limit_min, n = start_n; n < stop_n; ang += theta, n++ )
        {
            tabSin[n] = (float)(sin(ang) * irho);
            tabCos[n] = (float)(cos(ang) * irho);
        }
    
    
    
        // stage 1. fill accumulator
        for( i = 0; i < height; i++ )
            for( j = 0; j < width; j++ )
            {
                if( image[i * step + j] != 0 )
                            //
            for( n = start_n; n < stop_n; n++ )
                    {
                        r = cvRound( j * tabCos[n] + i * tabSin[n] );
                        r += (numrho - 1) / 2;
                        accum[(n+1) * (numrho+2) + r+1]++;
                    }
            }
    
    
    
        int max_accum = 0;
        int max_ind = 0;
    
        for( r = 0; r < numrho; r++ )
        {
            for( n = start_n; n < stop_n; n++ )
            {
                int base = (n+1) * (numrho+2) + r+1;
                if (accum[base] > max_accum)
                {
                    max_accum = accum[base];
                    max_ind = base;
                }
            }
        }   
    
        CvLinePolar2 line;
        scale = 1./(numrho+2);
        int idx = max_ind;
        n = cvFloor(idx*scale) - 1;
        r = idx - (n+1)*(numrho+2) - 1;
        line.rho = (r - (numrho - 1)*0.5f) * rho;
        line.angle = n * theta;
        line.votes = accum[idx];
        cvSeqPush( lines, &line );
    
    }