How to deal with HoughTransform2DLinesImageFilter GetLines() method in python

(Nick) #1

Dear All,

I’m trying to use HoughTransform2DLinesImageFilter in python. However, I cannot understand how to use the output of the GetLines() method.
AFAIU, the filter should return a list o the lines detected: how do I iterate over this list? How do access to the parameters of the lines?

Thanks

(Matt McCormick) #2

Hello @nick,

Welcome to the ITK community! :snake: :link: :slight_smile:

Could you please share a reproducible version of your code / data? We will be able to help with the specifics, then.

Thanks!
Matt

(Nick) #3

Dear @matt.mccormick thank you for your welcome and your reply.

What I’m trying to do, is to apply the hough transform for lines to an image and get the lines found by the algorithm.

Here is a sample of the code I’m using

houghF = itk.HoughTransform2DLinesImageFilter[itk.F, itk.F].New()
houghF.SetInput(edges)
houghF.SetAngleResolution(100)
houghF.SetNumberOfLines(2)
houghF.Update()
DetectedLines=houghF.GetLines()
I expected the var “DetectedLines” to be a list/array of 2 entries. However, it is ‘SwigPyObject’.
My question is: how do I access to the lines contained in the var “DetectedLines”?

Thanks

(Nick) #4

Dear All,

I’m still not able to understand my previous question… anyone could give me a hint on how to continue with this filter?

thanks

(Niels Dekker) #5

Hi @nick

I just looked at your question, together with my LKEB colleague Patrick de Koning, who has experience using the SWIG Python wrapper of ITK. Unfortunately, we haven’t found the answer to your question. :slightly_frowning_face:

DetectedLines wraps a C++ reference to an std::list of LineSpatialObject pointers. I can imagine that the wrapping would have been easier if std::vector was used, instead of std::list. For example, the Python wrapper of ITK’s VectorContainer does provide full access to the underlying std::vector.

However, SWIG does support std::list so I don’t really know why it does not work: https://github.com/swig/swig/blob/rel-4.0.0/Lib/python/std_list.i

Bradley @blowekamp do you have a clue?

(Francois Budin) #6

I created an issue on the issue tracker and will submit a tentative patch soon: https://github.com/InsightSoftwareConsortium/ITK/issues/918

2 Likes
(Francois Budin) #7

I created a PR here. I need to update the test with an image that would give a reasonable result for this filter. @nick: Do you have any simple line image that you could share that we could integrate as a test in ITK?

1 Like
(Nick) #8

Thank you guys for your effort!
@fbudin I have no realistic line image that I can share. I can generate binary lines like this line. Do you think that this will be useful as a basic test?

Thanks

(Francois Budin) #9

That’s perfect for the simple example that I wanted to implement. It is just to check that it is possible to use this filter in Python.

(Francois Budin) #10

FYI, I updated the PR using the image you provided to add a very simple test that verifies that we can access the output of the filter in Python: https://github.com/InsightSoftwareConsortium/ITK/pull/920

1 Like
(Nick) #11

@fbudin Thank you for your work.

This morning I downloaded the latest build, typing
python -m pip install itk --upgrade --no-index -f https://github.com/InsightSoftwareConsortium/ITKPythonPackage/releases/tag/latest

I tried to run the code I sent above.
However, when I try to access to DetectedLines[0] I got a segfault…

Is there something I’m missing?

Thanks

P.S.

I’m using python 3.6 on a 64 Ubuntu 16.04

(Niels Dekker) #12

Just curious… were you able to query the number of detected lines? Something like DetectedLines.size()?

(Nick) #13

@Niels_Dekker: I still get a segfault when I try to get the number of lines (both with lines.size() and len(lines) ).

(Niels Dekker) #14

That’s a pity!

My personal interest is more towards the “circles” version, HoughTransform2DCirclesImageFilter. Could you possible check if that one has the same problem, with GetCircles?

(Nick) #15

@Niels_Dekker. I did a simple test with the HoughTransform2DCirclesImageFilter and I still get segfault.
Could you try to reproduce this?

(Francois Budin) #16

Interesting. I just installed the latest package using the command you posted to make sure I was reproducing your steps. I tried both with python2.7 and python3.6. It worked for both. I ran the test that I added in ITK here using the line image that is available in this thread.

(Nick) #17

Dear @fbudin @Niels_Dekker

I investigated the problem I little more: as you pointed out the example works, the segfault comes when running the code inside a function.
I attached a little snippet that reproduces this issue.test.py (536 Bytes)

Could you reproduce it? Is there something wrong in what I’m doing?

Thanks

(Niels Dekker) #18

Ah, I see:

def RunHough(itkimage):
    hough = itk.HoughTransform2DLinesImageFilter[itk.F, itk.F].New()
    hough.SetInput(itkimage)
    ...
    hough.Update()
    return hough.GetLines()

# generate an empty image with one spot
...
lines2=RunHough(itkimage)
print(len(lines2))  // <=== segfault here

Looks like the problem here is that the list of lines is owned by the hough object. The hough object could be destroyed when the RunHough function has returned, and in that case, the list has gone, too.

The most obvious fix to me would be to change the return type in C++:

I think your problem would be fixed by returning the list of lines by value, instead of by reference (&).

By the way, I would prefer to return an std::vector, instead of an std::list.

(Nick) #19

Dear @Niels_Dekker,

I think that you are right: that doesn’t happen for the images (e.g. the accumulator) as they are handled with Smart Pointer.
I’m not sure if modify the method to return a copy of the object is a good idea: I think it could break some code developed by other users… is there a way in python to return a copy of this kind of objects?

Thanks

(Niels Dekker) #20

At the client side: sure, you could create a new Python list inside your RunHough function, and copy each line object (which is if fact a smart pointer) into your list, which you will then return. That should work just fine.

At the Python wrapper side: it might be possible to do return “by value” for GetLines(), even when the C++ code says “by reference”. But personally I wouldn’t really like to introduce such an inconsistency between the C++ API and the Python wrapping.

At the C++ side: theoretically it might break some C++ user code when the list of lines is returned by value, instead of by reference, but I don’t expect that it will affect many users. What particular use case do you think about?