Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV: get Hough accumulator value?

Tags:

opencv

Is it possible to get the accumulator value along with rho and theta from a Hough transform?

I ask because I'd like to differentiate between lines which are "well defined" (ie, which have a high accumulator value) and lines which aren't as well defined.

Thanks!

like image 574
David Wolever Avatar asked Jul 16 '10 14:07

David Wolever


People also ask

What is the accumulator value in Hough transform?

The Hough transform algorithm uses an array, called an accumulator, to detect the existence of a line y = mx + b. The dimension of the accumulator is equal to the number of unknown parameters of the Hough transform problem.

What is cv2 HoughLinesP?

Everything explained above is encapsulated in the OpenCV function, cv2. HoughLines(). It simply returns an array of (r, 0) values. r is measured in pixels and 0 is measured in radians.

What does Hough lines return?

HoughLines returns lines in Polar coordinate system. Converting them to Cartesian coordinate system helps in calculating points on the line and point of intersection between two lines. The Cartesian line equations can be used to find points on the edge of the image that lie on the line detected by cv2.

What is multi scale Hough transform?

The multi-scale version adds the following, to form an iterative process: Instead of applying a single resolution for ρ, srn parameter determined the divisor for the distance resolution. Similarly instead of applying a single resolution for θ, stn parameter determined the divisor for the angle resolution.


2 Answers

Ok, so looking at the cvhough.cpp file, the structure CvLinePolar is only defined by rho and angle.

This is all that is passed back as a result of our call to HoughLines. I am in the process of modifying the c++ file and see if i can get the votes out.

Update oct 26: just realized these are not really answers but more like questions. apparently frowned upon. I found some instructions on recompiling OpenCV. I guess we'll have to go in the code and modify it and recompile. How to install OpenCV 2.0 on win32

update Oct 27: well, i failed at compiling the dlls for OpenCV with my new code so I ended up copy-pasting the specific parts I want to modify into my own files. I like to add new functions so to avoid overloading the already defined functions. There are 4 main things you need to copy over: 1- some random defines

#define hough_cmp_gt(l1,l2) (aux[l1] > aux[l2])
static CV_IMPLEMENT_QSORT_EX( icvHoughSortDescent32s, int, hough_cmp_gt, const int* )

2- redefining the struct for line parameters

typedef struct CvLinePolar2
{
    float rho;
    float angle;
    float votes;
}
CvLinePolar2;

3- the main function that was modified

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) );

    for( ang = 0, n = 0; n < numangle; 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 = 0; n < numangle; n++ )
                {
                    r = cvRound( j * tabCos[n] + i * tabSin[n] );
                    r += (numrho - 1) / 2;
                    accum[(n+1) * (numrho+2) + r+1]++;
                }
        }

    // stage 2. find local maximums
    for( r = 0; r < numrho; r++ )
        for( n = 0; n < numangle; n++ )
        {
            int base = (n+1) * (numrho+2) + r+1;
            if( accum[base] > threshold &&
                accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
                accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
                sort_buf[total++] = base;
        }

    // stage 3. sort the detected lines by accumulator value
    icvHoughSortDescent32s( sort_buf, total, accum );

    // stage 4. store the first min(total,linesMax) lines to the output buffer
    linesMax = MIN(linesMax, total);
    scale = 1./(numrho+2);
    for( i = 0; i < linesMax; i++ )
    {
        CvLinePolar2 line;
        int idx = sort_buf[i];
        int n = cvFloor(idx*scale) - 1;
        int 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 );
    }

    cvFree( (void**)&sort_buf );
    cvFree( (void**)&accum );
    cvFree( (void**)&tabSin );
    cvFree( (void**)&tabCos);

}

4- the function that calls that function

CV_IMPL CvSeq*
cvHoughLines3( CvArr* src_image, void* lineStorage, int method,
               double rho, double theta, int threshold,
               double param1, double param2 )
{
    CvSeq* result = 0;

    CvMat stub, *img = (CvMat*)src_image;
    CvMat* mat = 0;
    CvSeq* lines = 0;
    CvSeq lines_header;
    CvSeqBlock lines_block;
    int lineType, elemSize;
    int linesMax = INT_MAX;
    int iparam1, iparam2;

    img = cvGetMat( img, &stub );

    if( !CV_IS_MASK_ARR(img))
        CV_Error( CV_StsBadArg, "The source image must be 8-bit, single-channel" );

    if( !lineStorage )
        CV_Error( CV_StsNullPtr, "NULL destination" );

    if( rho <= 0 || theta <= 0 || threshold <= 0 )
        CV_Error( CV_StsOutOfRange, "rho, theta and threshold must be positive" );

    if( method != CV_HOUGH_PROBABILISTIC )
    {
        lineType = CV_32FC3;
        elemSize = sizeof(float)*3;
    }
    else
    {
        lineType = CV_32SC4;
        elemSize = sizeof(int)*4;
    }

    if( CV_IS_STORAGE( lineStorage ))
    {
        lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize, (CvMemStorage*)lineStorage );
    }
    else if( CV_IS_MAT( lineStorage ))
    {
        mat = (CvMat*)lineStorage;

        if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 && mat->cols != 1) )
            CV_Error( CV_StsBadArg,
            "The destination matrix should be continuous and have a single row or a single column" );

        if( CV_MAT_TYPE( mat->type ) != lineType )
            CV_Error( CV_StsBadArg,
            "The destination matrix data type is inappropriate, see the manual" );

        lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize, mat->data.ptr,
                                         mat->rows + mat->cols - 1, &lines_header, &lines_block );
        linesMax = lines->total;
        cvClearSeq( lines );
    }
    else
        CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" );

    iparam1 = cvRound(param1);
    iparam2 = cvRound(param2);

    switch( method )
    {
    case CV_HOUGH_STANDARD:
          icvHoughLinesStandard2( img, (float)rho,
                (float)theta, threshold, lines, linesMax );
          break;

    default:
        CV_Error( CV_StsBadArg, "Unrecognized method id" );
    }

    if( mat )
    {
        if( mat->cols > mat->rows )
            mat->cols = lines->total;
        else
            mat->rows = lines->total;
    }
    else
        result = lines;

    return result;
}

And i guess you could uninstall opencv so it takes off all those automatic path setting and recompile it yourself using the CMake method and then the OpenCV is really whatever you make it.

like image 62
Denis Avatar answered Nov 16 '22 03:11

Denis


Although this is an old question, I had the same problem, so I might as well put up my solution. The threshold in houghlines() returns 1 for any point that cleared the threshold for votes. The solution is to run houghlines() for every threshold value (until there are no more votes) and add up the votes in another array. In python (maybe with other languages too) when you have no more votes, it throws an error, so use try/except.

Here is an example in python. The array I used was for rho values of -199 to 200 with a max vote of less than 100. You can play around with those constants to suit your needs. You may need to add a line to convert the source image to grayscale.

import matplotlib.pyplot as plt

import cv2

import math



############ make houghspace array ############

houghspace = []

c = 0

height = 400

while c <= height:

    houghspace.append([])

    cc = 0

    while cc <= 180:

        houghspace[c].append(0)

        cc += 1

    c+=1



############ do transform ############


degree_tick = 1 #by how many degrees to check 

total_votes = 1 #votes counter

highest_vote = 0 #highest vote in the array



while total_votes < 100:

    img = cv2.imread('source.pgm')

    edges = cv2.Canny(img,50,150,apertureSize = 3)

    lines = cv2.HoughLines(edges,1,math.pi*degree_tick/180,total_votes)



    try:

        for rho,theta in lines[0]:





            a = math.cos(theta)

            b = math.sin(theta)

            x1 = int((a*rho) + 1000*(-b))

            y1 = int((b*rho) + 1000*(a))

            x2 = int((a*rho) - 1000*(-b))

            y2 = int((b*rho) - 1000*(a))

            cv2.line(img,(x1,y1),(x2,y2),(50,200,255),2)

        #################add votes into the array################

        deradian = 180/math.pi #used to convert to degrees

        for rho,theta in lines[0]:

            degree = int(round(theta*deradian))

            rho_pos = int(rho - 200) 

            houghspace[rho_pos][degree] += 1 
    #when lines[0] has no votes, it throws an error which is caught here

    except:     

        total_votes = 999 #exit loop


    highest_vote = total_votes

    total_votes += 1
    del lines



########### loop finished ###############################
print highest_vote



#############################################################

################### plot the houghspace ###################


maxy = 200 #used to offset the y-axis

miny = -200 #used to offset the y-axis

#the main graph

fig = plt.figure(figsize=(10, 5))

ax = fig.add_subplot(111)

ax.set_title('Houghspace')

plt.imshow(houghspace, cmap='gist_stern')

ax.set_aspect('equal')

plt.yticks([0,-miny,maxy-miny], [miny,0,maxy])

#the legend
cax = fig.add_axes([0, 0.1, 0.78, 0.8])

cax.get_xaxis().set_visible(False)

cax.get_yaxis().set_visible(False)

cax.patch.set_alpha(0)

cax.set_frame_on(False)

plt.colorbar(orientation='vertical')

#plot

plt.show()
like image 38
Norbles Avatar answered Nov 16 '22 04:11

Norbles