Ask Your Question

HoughLinesP vastly different in Python and C++

asked 2015-02-17 07:31:03 -0500

NebuK gravatar image

updated 2015-02-17 09:14:33 -0500

Hi all,

I’ve spent some time to debug a pole detector i wanted to build on basis of the probabilistic line transform. The results i got from my python code, using Opencv 2.4.x as well as 3.0.0-beta, always looked just so bad. So i made what i hope to be a kind of self-contained example…



import cv2
import numpy as np
import sys
src = cv2.imread(sys.argv[1], 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 x1,y1,x2,y2 in lines[0]:
    cv2.line(color_dst, (x1,y1), (x2,y2), (0,255,0), 3, 8)
cv2.imwrite("test_py.jpg", color_dst)


#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc/types_c.h>
#include <iostream>

using namespace cv;

int main(int argc, char** argv)
    Mat src, dst, color_dst;
    if( argc != 2 || !(src=imread(argv[1], 0)).data)
        return -1;

    Canny( src, dst, 50, 200, 3 );
    cvtColor( dst, color_dst, CV_GRAY2BGR );
    std::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( "test_cpp.jpg", color_dst );
    return 0;


I’ve taken the example image from the OpenCV docs for HoughLinesP, as this looked to me like a good tasting ground…

Input Image

Input image

C++ Result 2.4.9+3.0.0-beta (expected)

Result of the above using the C++ version

Python 2.7.9 OpenCV 2.4.9 (broken?)

Result of the above using the Python version

Python 2.7.9 OpenCV 3.0.0-beta (totally broken?)

image description

As you can see, in Python with OpenCV 2.4.9 (3.0.0-beta looks worse!), the resulting lines are more, shorter, generally more clutter-y as compared with the C++ version. I’ve also checked the Canny output (visually), and it looks like it’s the same. Did i make any mistake? If not, how can this happen? I thought that python bindings are generated using an automated wrapper generator (swig?), so the code run in the background should be the same, right?

Any hints or tips on this would be greatly appreciated!

edit retag flag offensive close merge delete

1 answer

Sort by » oldest newest most voted

answered 2015-02-17 16:23:55 -0500

berak gravatar image

updated 2015-02-18 01:38:34 -0500

why do the c++ and the python solution differ ?

 >>> help(cv2.HoughLinesP)
Help on built-in function HoughLinesP:

    HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) -> lines

if you stare hard enough at it, you see the 'lines' argument in the middle. if you omit that, you will have to call each following argument by name like:

lines = cv2.HoughLinesP(dst, 1, np.pi/180, 80, minLineLength=30, maxLineGap=10)

this will already give you the same number of lines / same image as in c++

why do the 2.4 and the 3.0 (python)version differ ?

the 2.4 version is using c-api code under the hood, a CvSeq is used to keep the lines, this gets translated into a 1 row, n cols Mat, resulting in a (numpy)shape like (1,681,4), while the 3.0 version wraps a vector<Vec4i> into a n rows, 1 col Mat , resulting in (681,1,4) shape. rows and cols swapped, that's it.

in the end, this code:

for x1,y1,x2,y2 in lines[0]:
     cv2.line(color_dst, (x1,y1), (x2,y2), (0,255,0), 3, 8)

hits the 1 row in 2.4 correctly, and iterates over cols, while for 3.0, you'd have to use something like:

for l in lines:
    for x1,y1,x2,y2 in l:
        cv2.line(color_dst, (x1,y1), (x2,y2), (0,255,0), 3, 8)

(btw, turns out , the in the 3.0 samples is broken in the very same way ! ..)

edit flag offensive delete link more



this now works as expected for me. I was rather dumb to overlook that parameter. Thats what you get for intense Cargo-Culting!

Thanks a lot!

NebuK gravatar imageNebuK ( 2015-02-18 05:16:32 -0500 )edit

can't blame you for overlooking this.

it's rather weird, that the python wrapper-generator re-orders the arguments, and inserts an additional one for the output before the 'optional' args.

this is probably a remainder of the arcane c-api, where you had to pre-allocate special opencv types for this, which is no more needed using numpy types nowadays.

berak gravatar imageberak ( 2015-02-18 05:21:11 -0500 )edit

Question Tools

1 follower


Asked: 2015-02-17 07:29:17 -0500

Seen: 2,224 times

Last updated: Feb 18 '15