pythonc++opencvhough-transformhoughlinesp

OpenCV Probabilistic Hough Line Transform giving different results with C++ and Python?


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:

Input Edge Image

Python HoughLinesP() output:

Python HoughLinesP() output

C++ 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.


Solution

  • Explanation & Fix

    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.

    Example

    Using the example listed in the openCV documentation of HoughLinesP, you can see that this fixes the issue.

    C++ version

    (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:

    C++ result

    Incorrect Python version

    (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:

    Wrong Python result

    Corrected python version

    (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):

    Fixed Python result