Before we can apply the Hough transform to images, we must first learn what a Hough space is, which we shall do through an example.
When working with photos, we can consider the image to be a 2d matrix with x and y coordinates, with a line defined as y = mx + b
.
But in Hough Space,instead of representing that identical line as m
vs b
, I can express it as m-b
, which means that the characterisation of a line in image space will be a single point at m-b
in Hough space.
However, we have a problem: we can't describe a vertical line with y = mx + b
because the slope is infinite. As a result, we require a more efficient method of parametrization, polar coordinates (rho and theta).
What occurs when we take numerous points around a line and change into our Hough space is an essential insight.
A single dot in image space corresponds to a curve in Hough space, with the exception that points within a line in image space are represented by several curves with a single touchpoint.
Finding the spots where a set of curves intersects will be our goal.
In an image, the Hough transform is a feature extraction method for detecting simple objects like circles and lines.
A "simple" shape is one that has only a few parameters to represent it. A line, for example, can be represented by two parameters (slope and intercept), while a circle can be represented by three parameters (centre coordinates and radius) (x, y, r). When it comes to finding such shapes in a picture, the Hough transform excels.
The Hough transform's key advantage is that it is unaffected by occlusion.
Let's look at an example of how the Hough transform works.
A line in image space can be described with two variables, as you may know. Consider the following scenario:
Parameters in the Cartesian coordinate system: (m,b).
Parameters in the Polar coordinate system: (r,θ)
We shall express lines in the Polar system for Hough Transforms. Hence, a line equation can be represented as:
r=xcosθ+ysinθ
r0=x0⋅cosθ+y0⋅sinθ
Each pair (r,θ) denotes one of the lines that passes by (x0,y0).
For instance, for x0=8 and y0=6 we get the following graphic (on a plane θ - r):
Only points with r>0 and 0<θ<2π. are considered.
For example, if we continue with the previous example and plot two more points: x1=4, y1=9 and x2=12, y2=3, we get:
The three plots come together at a single location (0.925,9.6); these coordinates are the parameters (,r), or the line that connects (x0,y0), (x1,y1), and (x2,y2).
What does all of the foregoing imply? It means that the number of intersections between curves can be used to detect a line in general. The more curves that connect, the more points there are on the line represented by that intersection. In general, we can set a minimum number of intersections required to detect a line as a threshold.
The Hough Line Transform accomplishes this. It maintains note of where the curves of each point in the image cross. If the number of intersections exceeds a certain threshold, it is declared as a line with the intersection point's parameters (θ,r).
...
//Reading Image
if(argc <= 1) {
std::cerr << "!!! ERROR !!!\n";
std::cerr << "Please provide Image Path\n";
std::cerr << "<program> <input_image>\n";
return -1;
}
cv::Mat src = cv::imread(argv[1]);
cv::Mat img(src);
cv::cvtColor(img,img,cv::COLOR_BGR2GRAY);
showImg(img,"input Image");
if(img.empty()){
std::cerr << "!!! ERROR !!!\n";
std::cerr << "Error while Opening the Image\n";
return -1;
}
...
....
auto cannyEdgeDetection = [](cv::Mat& input, cv::Mat& output,double minThresh,double maxThresh) {
cv::blur(input,output,cv::Size(5,5));
cv::Canny(input,output,minThresh,maxThresh);
};
....
Note: In the example below I have used Probabilistic Hough Line Transform Function which is a more efficient implementation of Hough Line transform then Standar Hough Line Transform.
houghLinesP
function takes the following argument:
....
std::vector<cv::Vec4i>* houghLineP(cv::Mat& canny_img,double thresh){
auto linesP = new std::vector<cv::Vec4i>;
cv::HoughLinesP(canny_img,*linesP,1,CV_PI/180,thresh,10,10);
return linesP;
}
....
#include <iostream>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <algorithm>
std::vector<cv::Vec4i>* houghLineP(cv::Mat& canny_img,double thresh){
auto linesP = new std::vector<cv::Vec4i>;
cv::HoughLinesP(canny_img,*linesP,1,CV_PI/180,thresh,10,10);
return linesP;
}
int main(int argc,char** argv) {
// Defining Functions
auto showImg = [](cv::Mat& img,const std::string& win_name){
cv::imshow(win_name,img);
};
auto cannyEdgeDetection = [](cv::Mat& input, cv::Mat& output,
double minThresh,double maxThresh) {
cv::blur(input,output,cv::Size(5,5));
cv::Canny(input,output,minThresh,maxThresh);
};
//Reading Image
if(argc <= 1) {
std::cerr << "!!! ERROR !!!\n";
std::cerr << "Please provide Image Path\n";
std::cerr << "<program> <input_image>\n";
return -1;
}
cv::Mat src = cv::imread(argv[1]);
cv::Mat img(src);
cv::cvtColor(img,img,cv::COLOR_BGR2GRAY);
showImg(img,"input Image");
if(img.empty()){
std::cerr << "!!! ERROR !!!\n";
std::cerr << "Error while Opening the Image\n";
return -1;
}
// Finding Line Using Hough Detection
cv::Mat dst;
cannyEdgeDetection(img,dst,180,250); // Tweak Argument as per need
showImg(dst,"Canny Ouput");
auto *phtld = houghLineP(dst,120); //Probablistic Hough Transform
cv::Scalar blue(255,0,0); //for Probablistic Hough Transform
std::for_each(phtld->cbegin(),phtld->cend(),[&src,&blue](const auto &line){
// Drawing Line
auto l = line;
cv::line(src,cv::Point(l[0],l[1]),cv::Point(l[2],l[3]),blue,2,cv::LINE_AA);
});
showImg(src,"Hough Lines");
cv::waitKey();
// Cleaning
delete phtld;
return 0;
}