Hough Transform opencv

Hough transform not only can find a straight line in the picture, you can also find circles, ellipses, triangles and so on, as long as you can define a linear equation, circular equation and so on.

Have to say, and now a variety of quality online blog really not, a bunch of articles online, chaos TM Xia Xie, fraught. Itself and that they do not understand very clearly, we do not read the source code of the algorithm, written Yunshanwuzhao and ever more ignorant force.

Hough transform the idea itself is very straightforward. In this article we will explain to the Hough transform linear idea of the algorithm.

Hough transform

Think about how we express two-dimensional plane in a straight line.

There are two expressions:

  • Cartesian coordinate system (also called a Cartesian coordinate system)
  • Polar coordinate system (also called spherical coordinates)

The first is expressed in the most common orthogonal coordinate system: y = ax + b of the form.
The second is expressed in polar coordinates:
We linear equation in a Cartesian coordinate system with r, theta linear equation to express . the slope and intercept

expressed in polar coordinate is obtained: r = xcosθ + ysinθ

Suppose an image of a pixel with coordinates (x, y). Through which we can draw many straight lines in the Cartesian coordinates.
Conversion to the next r-θ coordinate system, we can draw a curve also r = xcosθ + ysinθ is in the x, y are given numbers, θ and r are unknown

on this curve corresponding to each of a [theta] r, represents a straight line common to these is that they have a straight line passing through the coordinates (x, y) of pixels.


For each pixel in the image, I can draw a curve to express the myriad of straight lines passing through the point. What that represents the intersection of the curve of it? Obviously, representing at the intersection (θ, r) represent i.e., a straight line passing through the pixel a, another pixel through the points B, C .... pixel

How kind called "a straight line to find the figure"

Back to our question, we wanted to find a line in the image. What does it mean?
Many blog said, means to find a straight line, as much as possible through each pixel.

I just draw a straight line on TM image , can not pass through many pixels it?
in fact, it should be a straight line to find out as much as possible through "effective pixels." this is why we must do first Hough transform before the cause of edge detection. after canny after detection (do not know the reference to an article ), the resulting image matrix, only at the edges of its pixel grayscale value is relatively large, that is reflected in the white bright spots on the image, the non-edge, its gray values is 0, reflecting on the image is black. these represent edge pixels is effective pixels.

That is: If I can find such a straight line through a lot of valid pixels (this is what we need to adjust the reference threshold), then I say I find in the image of a straight line. Similarly, looking round to find. or find any triangle shape is a reason.

For example, the following diagram

you can not find a straight line through a lot of white spots. So the figure does not exist in a straight line.

Hough transformation process

  • canny edge detection to extract an edge
  • For each pixel of the edge image,
    the pseudo-code is as follows
for (every pixel)
{
    if(pixel is effective edge pixel)
    {
        for(int theta = 0; theta < 360; theta++)
        {
            r=xcosθ+ysinθ;//x,y为pixel坐标
            accum(theta,r) += 1; //(theta,r)所代表的直线经过的像素点数量加1
        }
    }
}

for(every element in accum)
{
    if (count of (theta,r) > thershold)
    {
        find line (theta,r)
    }
}

opencv example

houghlines api


wherein, double rho, double theta, ultimately determine how many combinations of (theta, r) of the decision may be the case where each pixel through the line. The smaller this value, the finer the particle size, calculates the required also greater. generally take rho = 1, i.e., one pixel takes one of .theta.
Here is an example of extracting a straight line in the picture parking space

import sys
import math
import cv2 as cv
import numpy as np
def test():
    src = cv.imread("/home/sc/disk/keepgoing/opencv_test/houghtest.jpg")
    src = cv.GaussianBlur(src, (3, 3), 0)
    gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    
    dst = cv.Canny(src, 150, 300, None, 3)
    lines = cv.HoughLines(dst, 1, np.pi / 180, 150, None, 0, 0)
    
    # Copy edges to the images that will display the results in BGR
    cdst = cv.cvtColor(dst, cv.COLOR_GRAY2BGR)
    cdstP = np.copy(cdst)
    
    lines = cv.HoughLines(dst, 1, np.pi / 180, 200, None, 0, 0)
    
    if lines is not None:
        for i in range(0, len(lines)):
            rho = lines[i][0][0]
            theta = lines[i][0][1]
            a = math.cos(theta)
            b = math.sin(theta)
            x0 = a * rho
            y0 = b * rho
            pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
            pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
            cv.line(cdst, pt1, pt2, (0,0,255), 3, cv.LINE_AA)
    
    
    cv.imshow("origin",src)
    cv.imshow("dst1",dst)
    cv.imshow("dst2",cdst)
    if 27 == cv.waitKey():
        cv.destroyAllWindows()

test()

opencv source code interpretation

Official achieve opencv

static void
HoughLinesStandard( InputArray src, OutputArray lines, int type,
                    float rho, float theta,
                    int threshold, int linesMax,
                    double min_theta, double max_theta )
{
    CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Internal error");

    Mat img = src.getMat();

    int i, j;
    float irho = 1 / rho;

    CV_Assert( img.type() == CV_8UC1 );
    CV_Assert( linesMax > 0 );

    const uchar* image = img.ptr();
    int step = (int)img.step;
    int width = img.cols;
    int height = img.rows;

    int max_rho = width + height;
    int min_rho = -max_rho;

    CV_CheckGE(max_theta, min_theta, "max_theta must be greater than min_theta");

    int numangle = cvRound((max_theta - min_theta) / theta);
    int numrho = cvRound(((max_rho - min_rho) + 1) / rho);

#if defined HAVE_IPP && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_HOUGH
    if (type == CV_32FC2 && CV_IPP_CHECK_COND)
    {
        IppiSize srcSize = { width, height };
        IppPointPolar delta = { rho, theta };
        IppPointPolar dstRoi[2] = {{(Ipp32f) min_rho, (Ipp32f) min_theta},{(Ipp32f) max_rho, (Ipp32f) max_theta}};
        int bufferSize;
        int nz = countNonZero(img);
        int ipp_linesMax = std::min(linesMax, nz*numangle/threshold);
        int linesCount = 0;
        std::vector<Vec2f> _lines(ipp_linesMax);
        IppStatus ok = ippiHoughLineGetSize_8u_C1R(srcSize, delta, ipp_linesMax, &bufferSize);
        Ipp8u* buffer = ippsMalloc_8u_L(bufferSize);
        if (ok >= 0) {ok = CV_INSTRUMENT_FUN_IPP(ippiHoughLine_Region_8u32f_C1R, image, step, srcSize, (IppPointPolar*) &_lines[0], dstRoi, ipp_linesMax, &linesCount, delta, threshold, buffer);};
        ippsFree(buffer);
        if (ok >= 0)
        {
            lines.create(linesCount, 1, CV_32FC2);
            Mat(linesCount, 1, CV_32FC2, &_lines[0]).copyTo(lines);
            CV_IMPL_ADD(CV_IMPL_IPP);
            return;
        }
        setIppErrorStatus();
    }
#endif


    Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 );
    std::vector<int> _sort_buf;
    AutoBuffer<float> _tabSin(numangle);
    AutoBuffer<float> _tabCos(numangle);
    int *accum = _accum.ptr<int>();
    float *tabSin = _tabSin.data(), *tabCos = _tabCos.data();

    // create sin and cos table
    createTrigTable( numangle, min_theta, theta,
                     irho, tabSin, tabCos);

    // stage 1. fill accumulator
    for( i = 0; i < height; i++ )
        for( j = 0; j < width; j++ )
        {
            if( image[i * step + j] != 0 )
                for(int n = 0; n < numangle; n++ )
                {
                    int r = cvRound( j * tabCos[n] + i * tabSin[n] );
                    r += (numrho - 1) / 2;
                    accum[(n+1) * (numrho+2) + r+1]++;
                }
        }

    // stage 2. find local maximums
    findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );

    // stage 3. sort the detected lines by accumulator value
    std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));

    // stage 4. store the first min(total,linesMax) lines to the output buffer
    linesMax = std::min(linesMax, (int)_sort_buf.size());
    double scale = 1./(numrho+2);

    lines.create(linesMax, 1, type);
    Mat _lines = lines.getMat();
    for( i = 0; i < linesMax; i++ )
    {
        LinePolar 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 = static_cast<float>(min_theta) + n * theta;
        if (type == CV_32FC2)
        {
            _lines.at<Vec2f>(i) = Vec2f(line.rho, line.angle);
        }
        else
        {
            CV_DbgAssert(type == CV_32FC3);
            _lines.at<Vec3f>(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
        }
    }
}

stage1 core logic i.e., the effective pixel one by one traverse, linear various statistics (theta, r) ​​passing through the representative pixel bit number

Mat _accum = Mat :: zeros (( numangle + 2), (numrho + 2), CV_32SC1);
can see the number of points the number of matrix statistics line passing through the (2 + numangle) x (numrho + 2) that our incoming double rho, double theta related. the smaller the value, the greater the number of the corresponding linear search of us .

achieve opencv's probably some considerations on the project, this is less certain, such as why there should (2 + numangle) x (numrho + 2) instead of numangle x numrho

int max_rho = width + height;
int min_rho = -max_rho;

Why is w + h, but not with the square root of the diagonal length requirements.
Want to know friends can leave a message to tell me.

// stage 2. find local maximums

static void
findLocalMaximums( int numrho, int numangle, int threshold,
                   const int *accum, std::vector<int>& sort_buf )
{
    for(int r = 0; r < numrho; r++ )
        for(int 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.push_back(base);
        }
}

Find local maximum count. Similar to non-maximal suppression. Further refinement of the detected straight line, the straight line is similar to the local take only the most accurate.

// stage 3. sort the detected lines by accumulator value
sorted by the number of accum size

The First Stage 4. Store // min (Total, linesMax) lines to Output Buffer The
n-pieces to the output lines before storage Buffer.

Guess you like

Origin www.cnblogs.com/sdu20112013/p/11614836.html