I was working on a project using OpenCV, Python that uses Probabilistic Hough Line Transform function "HoughLinesP" in some part of the project. My code worked just fine and there was no problem. Then I thought of converting the same code to C++.
After converting the code to C++, the output is not the same as that of the Python code. After long hours of debugging, I found out that everything else works fine but the "HoughLinesP" function is giving different output in the case of C++. The input to this function in both the languages is the same and the values of parameters are also the same but the output from it is different.
Can someone explain me why is this happening and any possible fixes for it?
Also, I have checked the version of OpenCV for both the languages, and it is the same:
4.5.0 dev
Also, I have tried playing with the values passed to the C++ code, but I am not able to obtain similar results.
Input Edge Image:
Python HoughLinesP() output:
C++ HoughLinesP() output:
Following are the codes in each language: Python:
Lines = cv2.HoughLinesP(EdgeImage, 1, np.pi / 180, 50, 10, 15)
C++:
std::vector<cv::Vec4i> Lines;
cv::HoughLinesP(EdgeImage, Lines, 1, CV_PI / 180, 50, 10, 15);
It would be a great help if anyone could suggest something.
The problem arises because in the Python version you are not setting the arguments that you think you are setting. In contrast to some other functions for which the argument lists are adapted in the Python interface, HoughLinesP
does not only return the lines but also still takes a parameter lines
for the line output. You can see that in the help for HoughLinesP
:
import cv2
help(cv2.HoughLinesP)
which gives you (ellipsis mine):
Help on built-in function HoughLinesP:
HoughLinesP(...)
HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) -> lines
. @brief Finds line segments in a binary image using the probabilistic Hough transform.
.
...
. @param lines Output vector of lines. Each line is represented by a 4-element vector
. \f$(x_1, y_1, x_2, y_2)\f$ , where \f$(x_1,y_1)\f$ and \f$(x_2, y_2)\f$ are the ending points of each detected
. line segment.
...
So basically, in your python example you pass 10
as lines
instead of as minLineLength
. To fix this, you can either pass an empty array as lines
or you can pass the parameters as keyword arguments:
Lines = cv2.HoughLinesP(EdgeImage, rho=1, theta=np.pi/180,
threshold=50, minLineLength=10, maxLineGap=15)
Doing that should make your Python version's output match the C++ version's.
Alternatively, if you are happy with the results of the Python version, you have to leave out parameter lines
(i.e. only setting minLineLength
to 15 and using the default of 0 for maxLineGap
[see docs]):
std::vector<cv::Vec4i> Lines;
cv::HoughLinesP(EdgeImage, Lines, 1, CV_PI / 180, 50, 15);
This should then reproduce your Python version.
Using the example listed in the openCV documentation of HoughLinesP
, you can see that this fixes the issue.
(Taken from openCV documentation listed above and adapted to save image instead.)
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src, dst, color_dst;
if( argc != 3 || !(src=imread(argv[1], 0)).data)
return -1;
Canny( src, dst, 50, 200, 3 );
cvtColor( dst, color_dst, COLOR_GRAY2BGR );
vector<Vec4i> lines;
HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
line( color_dst, Point(lines[i][0], lines[i][1]),
Point( lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
}
imwrite( argv[2], color_dst );
return 0;
}
If you compile this and run it over the example picture provided in the docs, you get the following result:
(Basically, just the translated C++ version without the lines parameter.)
import argparse
import cv2
import numpy as np
parser = argparse.ArgumentParser()
parser.add_argument("input_file", type=str)
parser.add_argument("output_file", type=str)
args = parser.parse_args()
src = cv2.imread(args.input_file, 0)
dst = cv2.Canny(src, 50., 200., 3)
color_dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
lines = cv2.HoughLinesP(dst, 1., np.pi/180., 80, 30, 10.)
for this_line in lines:
cv2.line(color_dst,
(this_line[0][0], this_line[0][1]),
(this_line[0][2], this_line[0][3]),
[0, 0, 255], 3, 8)
cv2.imwrite(args.output_file, color_dst)
Running this gives the following (different) result:
(Fixed by passing keyword args instead)
import argparse
import cv2
import numpy as np
parser = argparse.ArgumentParser()
parser.add_argument("input_file", type=str)
parser.add_argument("output_file", type=str)
args = parser.parse_args()
src = cv2.imread(args.input_file, 0)
dst = cv2.Canny(src, 50., 200., 3)
color_dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
lines = cv2.HoughLinesP(dst, rho=1., theta=np.pi/180.,
threshold=80, minLineLength=30, maxLineGap=10.)
for this_line in lines:
cv2.line(color_dst,
(this_line[0][0], this_line[0][1]),
(this_line[0][2], this_line[0][3]),
[0, 0, 255], 3, 8)
cv2.imwrite(args.output_file, color_dst)
This gives the correct result (i.e. the same result as the C++ version):