## Introduction

In the previous tutorial, we learned how to use the OpenCV function **Canny() **in order to detect any edges in an image. Now we will learn another useful technique for objects detection – Hough line detection. We will use the Canny edge detection in this tutorial as a preparation step before we apply the Hough line detection algorithm. We will see two variants of this algorithm – the second one will be more accurate in detecting lines in the images.

## Hough line detection

Here I will give just some brief explanation of the Hough line detection theory. You can find more information here, here and from this video.

The basic idea is to present a line in the **Polar coordinate system **using the equation:

r = xcosθ + ysinθ

We should work with a binary image, which has all the edges extracted. Then we go through all the points in the image and for each one of them, we plot the graphic from the above formula for 0 < θ < 2π. Thus, we will get a sinusoid in the plane (θ – r). For example, we find one edge point that stays on point

x0=80 and y0=16. We plot the sinusoid. Then we get another point at x1=84, y1=16 and for that point we plot another sinusoid. We do this for all the edge points.

As a result, we will see in our graphic that there are points where the sinusoids intersect. For example we will see that at ( θ,r) = (0.5, 18) there are many sinusoid lines that intersect at that point. That point will be a point, through which a line passes. We will draw a line, which has the ( θ,r) = (0.5, 18) coordinates.

Again, more information is available in the above links. Check them to get the concept better.

## Implementing Hough line detection

All the theory and logic, which I mention in the above section is available in OpenCV as ready-to-use functions. We will see how to use them. Following now is the source code:

#### The code

// Apply the Hough Line Transform operation to an image void applyHoughLineTransform() { // Path to the input image std::string l_pathToInputImage{ "../Resources/table.jpg" }; // Create an object to hold the image data of the first image Mat l_image; // Read the image date from a file with no change to color scheme l_image = imread(l_pathToInputImage, IMREAD_GRAYSCALE); // Check if we have read the first image data correctly if (!l_image.data) { std::cout << "No image data \n"; return; } // Create the output image matrices Mat l_noiseRemovedImage{}; Mat l_outputImageCanny{}; Mat l_outputHough{}; // Remove any noise GaussianBlur(l_image, l_noiseRemovedImage, Size(3, 3), 0, 0, BORDER_DEFAULT); // Create the Canny constants to play with constexpr int c_kernelSize = 3; constexpr double c_lowerThreshold = 30.0; constexpr double c_upperThreshold = c_lowerThreshold * 3.0; // Apply the canny edge detection Canny(l_noiseRemovedImage, l_outputImageCanny, c_lowerThreshold, c_upperThreshold, c_kernelSize, false); // Clone the Canny result to another matrix, where the lines will be drawn // It should be converted to BGR color in order to draw the lines on top cvtColor(l_outputImageCanny, l_outputHough, COLOR_GRAY2BGR); // Create constants and variables to hold the results from the Hough transform constexpr double c_distanceResolution = 1; constexpr double c_angleResolution = CV_PI / 180; constexpr int c_threshold = 200; std::vector<Vec2f> l_foundLines; // Apply the Hough Line Transform HoughLines(l_outputImageCanny, l_foundLines, c_distanceResolution, c_angleResolution, c_threshold); // Draw the result lines for (size_t i = 0; i < l_foundLines.size(); ++i) { // Calculate the needed values and points coordinates constexpr int l_lineThickness = 2; float l_rho = l_foundLines.at(i)[0]; float l_theta = l_foundLines.at(i)[1]; double l_cosTheta = std::cos(l_theta); double l_sinTheta = sin(l_theta); double l_initXPoint = l_rho * l_cosTheta; double l_initYPoint = l_rho * l_sinTheta; Point l_startPoint{ cvRound(l_initXPoint + 1000 * (-l_sinTheta)), cvRound(l_initYPoint + 1000 * (l_cosTheta)) }; Point l_endPoint{ cvRound(l_initXPoint - 1000 * (-l_sinTheta)), cvRound(l_initYPoint - 1000 * (l_cosTheta)) }; // Draw the line line(l_outputHough, l_startPoint, l_endPoint, Scalar(0, 255, 0), l_lineThickness, LINE_AA); } // Display the input image namedWindow("Input", WINDOW_NORMAL); cv::imshow("Input", l_image); // Display the Canny result image namedWindow("Result Canny", WINDOW_NORMAL); cv::imshow("Result Canny", l_outputImageCanny); // Display the Hough Transform result image namedWindow("Result Hough Transform", WINDOW_NORMAL); cv::imshow("Result Hough Transform", l_outputHough); }

We have the standard implementation of loading an image as a first step. Then we create three matrices, which will hold the image with the removed noise, the output of the Canny edge detection and the final result of Hough line detection algorithm. The first thing I do is to remove any noise from the image, using the Gaussian blur algorithm. Previously we saw how to use it. Next, we apply the Canny edge detection algorithm. We also saw this algorithm in a previous tutorial. This algorithm will find all the edges in the image and will create one binary matrix. It is what we need in order to apply the Hough transform.

We make one copy of the result matrix and transform it to BGR color. We will use this copy in order to draw the detected lines on top of it.

##### The OpenCV function

After we have detected the edges, it is time to apply the Hough line transformation. We set some constants and then we call the **HoughLines()** function. The function takes the input image as a first argument and one vector of point coordinates as a second argument. It will save all the lines it finds in that vector. Actually, it will save the ( θ,r) points. Then the next two arguments are the resolution of the distance and the angle, which the algorithm will used when calculating the sinusoids. The fifth argument is the threshold or the value, which will determine if a point has enough intersections in order to consider it a line point. We leave the rest of the arguments with their default values.

So far, we have the ( θ,r) points of the lines. Next thing we do is to calculate the start and end of each line from these points. The next several lines do this calculation. Finally, we draw the line on top of the image. We have seen how to use the **line()** function previously.

#### The results

Once we are done with all the calculations and line drawings, we display the results. Following is what I get on my side. I display the input image, the Canny edge detection result and the Hough line detection result. With green lines, I have marked all the lines that we have found. If I change the threshold value, the result will also change.

## Implementing the probabilistic Hough line detection

As you have seen from the previous image, the result lines start from the left or from the top ends, even though the actual lines are not starting from there. Now, we will see another function from OpenCV, which outputs the extremes of the detected lines, thus getting the exact beginning and end of the given line. Most of the time, we will need to get the exact line with its exact starting and ending points. Let us see the code.

#### The code

// Apply the Hough Line Transform operation to an image void applyHoughProbabilisticLineTransform() { // Path to the input image std::string l_pathToInputImage{ "../Resources/building.jpg" }; // Create an object to hold the image data of the first image Mat l_image; // Read the image date from a file with no change to color scheme l_image = imread(l_pathToInputImage, IMREAD_GRAYSCALE); // Check if we have read the first image data correctly if (!l_image.data) { std::cout << "No image data \n"; return; } // Create the output image matrices Mat l_noiseRemovedImage{}; Mat l_outputImageCanny{}; Mat l_outputHough{}; // Remove any noise GaussianBlur(l_image, l_noiseRemovedImage, Size(3, 3), 0, 0, BORDER_DEFAULT); // Create the Canny constants to play with constexpr int c_kernelSize = 3; constexpr double c_lowerThreshold = 30.0; constexpr double c_upperThreshold = c_lowerThreshold * 3.0; // Apply the canny edge detection Canny(l_noiseRemovedImage, l_outputImageCanny, c_lowerThreshold, c_upperThreshold, c_kernelSize, false); // Clone the Canny result to another matrix, where the lines will be drawn // It should be converted to BGR color in order to draw the lines on top cvtColor(l_outputImageCanny, l_outputHough, COLOR_GRAY2BGR); // Create constants and variables to hold the results from the Hough transform constexpr double c_distanceResolution = 1; constexpr double c_angleResolution = CV_PI / 180; constexpr int c_threshold = 120; std::vector<Vec4i> l_foundLines; // Apply the Hough Line Transform HoughLinesP(l_outputImageCanny, l_foundLines, c_distanceResolution, c_angleResolution, c_threshold); // Draw the result lines for (size_t i = 0; i < l_foundLines.size(); ++i) { // Calculate the needed values and points coordinates constexpr int l_lineThickness = 2; Point l_startPoint{ l_foundLines.at(i)[0], l_foundLines.at(i)[1] }; Point l_endPoint{ l_foundLines.at(i)[2], l_foundLines.at(i)[3] }; // Draw the line line(l_outputHough, l_startPoint, l_endPoint, Scalar(0, 255, 0), l_lineThickness, LINE_AA); } // Display the input image namedWindow("Input", WINDOW_NORMAL); cv::imshow("Input", l_image); // Display the Canny result image namedWindow("Result Canny", WINDOW_NORMAL); cv::imshow("Result Canny", l_outputImageCanny); // Display the Hough Transform result image namedWindow("Result Hough Transform", WINDOW_NORMAL); cv::imshow("Result Hough Transform", l_outputHough); }

The logic is similar to the previous example so I will not go into too much detail. We load image, clear any noise from it and apply the Canny edge detection mechanism.

In order to apply the probabilistic Hough line detection algorithm we call the **HoughLinesP()** function. The first five arguments are the same as with the normal **HoughLines()** function. The last two arguments are different but we will use their default values. One difference with the normal **HoughLines() **is that we use vector of four integers structure, which will hold the X and Y coordinates of the start and end point.

Drawing the resulting lines is also different. We directly get the coordinates from the result vector and draw a line using the **line() **function. There are no calculations involved.

#### The result

Finally, we display the results – the input image, the result from the Canny edge detection and the result from the probabilistic Hough line detection. As you can see from the image below, the lines are not starting from the very left or top ends. You can see that not all lines are detected and colored in green. If you change the threshold, you will also change this and you can get more or less lines. Try to play with it.

Look also into the result from another image, which does not have so straight lines.

## Implementing the Hough line detection from a set of points

I want to show one more example with yet another OpenCV function, which is able to detect lines based on a set of points. Let us look at the code:

void applyHoughLineTransformOnSetOfPoints() { Mat l_transformResult{}; std::vector<Vec3d> l_outputLines{}; // The set of points to apply the Hough Line transform on std::vector<Vec2f> l_inputPoints { { 0.0f, 369.0f }, { 10.0f, 364.0f }, { 20.0f, 358.0f }, { 30.0f, 352.0f }, { 40.0f, 346.0f }, { 50.0f, 341.0f }, { 60.0f, 335.0f }, { 70.0f, 329.0f }, { 80.0f, 323.0f }, { 90.0f, 318.0f }, { 100.0f, 312.0f }, { 110.0f, 306.0f }, { 120.0f, 300.0f }, { 130.0f, 295.0f }, { 140.0f, 289.0f }, { 150.0f, 284.0f }, { 160.0f, 277.0f }, { 170.0f, 271.0f }, { 180.0f, 266.0f }, { 190.0f, 260.0f } }; // The required constants constexpr int c_maxLines = 20; constexpr int c_threshold = 2; constexpr double c_distanceMin = 0.0; constexpr double c_distanceMax = 360.0; constexpr double c_distanceStep = 1.0; constexpr double c_angleMin = 0.0; constexpr double c_angleMax = CV_PI / 2.0; constexpr double c_angleStep = CV_PI / 180.0; // Apply the Hough Line transform HoughLinesPointSet(l_inputPoints, l_transformResult, c_maxLines, c_threshold, c_distanceMin, c_distanceMax, c_distanceStep, c_angleMin, c_angleMax, c_angleStep); // Copy the results l_transformResult.copyTo(l_outputLines); // Draw the result lines Mat l_result{ 600, 400, CV_8UC3, Scalar{255, 255, 255} }; for (size_t i = 0; i < l_outputLines.size(); ++i) { // Calculate the needed values and points coordinates constexpr int l_lineThickness = 2; double l_rho = l_outputLines.at(i)[1]; double l_theta = l_outputLines.at(i)[2]; double l_cosTheta = std::cos(l_theta); double l_sinTheta = sin(l_theta); double l_initXPoint = l_rho * l_cosTheta; double l_initYPoint = l_rho * l_sinTheta; Point l_startPoint{ cvRound(l_initXPoint + 1000 * (-l_sinTheta)), cvRound(l_initYPoint + 1000 * (l_cosTheta)) }; Point l_endPoint{ cvRound(l_initXPoint - 1000 * (-l_sinTheta)), cvRound(l_initYPoint - 1000 * (l_cosTheta)) }; // Draw the line line(l_result, l_startPoint, l_endPoint, Scalar(0, 255, 0), l_lineThickness, LINE_AA); } // Display the input image namedWindow("Output", WINDOW_NORMAL); cv::imshow("Output", l_result); }

I am creating one matrix to hold the results from the OpenCV function, one vector of points where I will keep the results in form of three values – votes, rho and theta. Then I provide a vector of input points (I have taken them from the OpenCV documentation). Next, I have some constants, which the OpenCV needs.

I am calling the **HoughLinesPointSet() **function, which will detect all the lines based on the provided points. The first argument is the points set. Next, I have the matrix, where I will keep the results. The third argument is the maximum count of Hough lines I am interested in. The fourth argument is the threshold of votes, which is taken into account before adding any line to the results. Next, I have the minimum, maximum and step for the rho and the minimum, maximum and step values for the theta.

After the function is done processing, I will copy the results from the matrix into a vector of three values – votes, rho and theta. Next thing is to create one matrix, where I will draw the lines. As we did previously, we calculate the lines start and end points from the rho and theta values and draw the lines. Finally, we display the result. This is how it looks on my side.

## Conclusion

In this tutorial, we saw one very important algorithm in the image-processing world – the Hough line detection algorithm. There was a short info on how the algorithm works and two examples using ready OpenCV functions. Revise the provided links in order to get more information about this algorithm.

The code from the examples is available in GitHub and attached as an archive below.

In the next tutorial, we will see another implementation of the Hough algorithm, which will allow us to detect circles.

### OpenCV Training

Passionate developer, loving husband and caring father. As an introvert programming is my life. I work as a senior software engineer in a big international company. My hobbies include playing computer games (mostly World of Warcraft), watching TV series and hiking.