2
votes

In Matlab, by combining hough transform, houghpeaks and houghlines, it is possible to show the detected lines in the original image.

This can be show in the following image (produced by using a sample code from Matlab's help for houghlines). The green lines are detected. The blue one is the longest one:

output from example code by Mathoworks

I ran cv::HoughLines on a simple synthetic image I generated (several squares etc.). The image is attached here:

my sample image

The relevant code portion is:

cv::vector<cv::Vec2f> lines;
cv::HoughLines(I_BW, lines, 1, CV_PI/180,200);
cv::Mat linesMat ( lines, true );

Viewing the linesMat matrix (I converted it to this form so I can use image watch to view the data) while running a for loop to add a red line over the edge image I see that rho and theta are ordered by the length of the longest line in the image.

My output is:

Showing the 8 strongest lines

The lines are the entire width (or height) of the image. How can I just show the actual lines like in the Matlab example? I can work my way back from rho+theta to x and y, but then I have to somehow link them to the detected edges etc. - maybe there's a simple way to do this which I'm missing?

Thanks!

2

2 Answers

6
votes

taoufik is correct and I upvoted his answer. I actually found the answer in the OpenCV 2 Computer Vision Application Programming Cookbook before I read his comment.

I'm selecting my answer as the answer, because it is more complete for future reference.

@taoufik - thanks again, man!

I'll post a code snippet that may be of use to other people (based on a solution I found in the cookbook. I just wrote the short version, not the elegant class implementation from the cookbook).

I also add here a small function I wrote that computes the CDF used to find the high and low threshold for the canny edge detector in the Matlab implementation of Canny which gives good results. Normally I also do a Gaussian blur prior the edge detection (as appears within the canny.m in Matlab) but the attached image is synthetically perfect (no noise) so it's redundant here. I chose a high minimum vote value ('threshold') so only the 4 long lines will be found.

We'll start with the code in the main function:

cv::Mat image = cv::imread("shapes.jpg");
int bins = 256;
cv::Mat cdf = getGrayCDF(image,bins);
cv::Mat diffy = cdf>0.7;
cv::Mat NonZero_Locations;   // output, locations of non-zero pixels 
cv::findNonZero(diffy, NonZero_Locations);
double highThreshold = double((NonZero_Locations.at<cv::Point>(0).y))/bins;
double lowThreshold = 0.4*highThreshold;
cv::Mat contours;
// cv::GaussianBlur( image, contours, cv::Size(7,7),2 ); // NOT REQUIRED HERE. Syhnthetic image
cv::Canny( image, contours, lowThreshold*bins, highThreshold*bins);
std::vector<cv::Vec4i> lines;
double rho = 1; // delta_rho resolution
double theta = CV_PI/180; // delta_theta resolution
int threshold = 300; // threshold number of votes , I SET A HIGH VALUE TO FIND ONLY THE LONG LINES
double minLineLength = 0; // min length for a line
double maxLineGap = 2; // max allowed gap along the line
cv::HoughLinesP(contours,lines, rho, theta, threshold, minLineLength, maxLineGap); // running probabilistic hough line
if (image.channels()!=3) {cv::cvtColor(image,image,CV_GRAY2BGR);} // so we can see the red lines
int line_thickness = 2;
cv::Scalar color=cv::Scalar(0,0,255);
std::vector<cv::Vec4i>::const_iterator iterator_lines = lines.begin();
while (iterator_lines!=lines.end()) {
    cv::Point pt1((*iterator_lines)[0],(*iterator_lines)[1]);
    cv::Point pt2((*iterator_lines)[2],(*iterator_lines)[3]);
    cv::line( image, pt1, pt2, color, line_thickness);
    ++iterator_lines;
}
cv::imshow("found lines", image); cvWaitKey(0); cv::destroyWindow("found lines");

And I'll end with my function for computing a simple grayscale cumulative distribution function:

cv::Mat getGrayCDF(cv::Mat Input, int histSize){
cv::Mat InputGray = Input.clone();
if (InputGray.channels()!=1) {cv::cvtColor(Input,InputGray,CV_BGR2GRAY);}
float range[] = { 0, histSize  } ;
const float* histRange = { range };
bool uniform = true; bool accumulate = false;
cv::Mat hist;
calcHist( &InputGray, 1, 0, cv::Mat(), hist, 1, &histSize , &histRange, uniform, accumulate );
for (int i = 1; i < hist.rows; i++) {
    float* data = hist.ptr<float>(0);
    data[i] += data[i-1];
}
return hist/(InputGray.total()); // WE NOW HAVE A *NORMALIZED* COMPUTED CDF!
}

My solution for the above given snippet is:

shapes with 4 highlighted lines

Hope you find this useful!

3
votes

What you're looking for is the Probabilistic Hough transform. In OpenCV it is available under HoughLinesP().