Skip to content

Add python binding for LineIterator#24261

Closed
LaurentBerger wants to merge 27 commits intoopencv:4.xfrom
LaurentBerger:lineiterator
Closed

Add python binding for LineIterator#24261
LaurentBerger wants to merge 27 commits intoopencv:4.xfrom
LaurentBerger:lineiterator

Conversation

@LaurentBerger
Copy link
Copy Markdown
Contributor

@LaurentBerger LaurentBerger commented Sep 12, 2023

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • The PR is proposed to the proper branch
  • There is a reference to the original bug report and related work LineIterator poorly supported in bindings #21307
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

Copy link
Copy Markdown
Contributor

@asmorkalov asmorkalov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposal looks good! Please remove not related renaming in the class interface. Such changes does not bring value, but makes merges 4.x->5.x and conflict resolution harder.

Copy link
Copy Markdown
Contributor

@opencv-alalek opencv-alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It make sense to add simple Python test to avoid regressions.
Does Python types work as expected?

@LaurentBerger
Copy link
Copy Markdown
Contributor Author

LaurentBerger commented Sep 14, 2023

It make sense to add simple Python test to avoid regressions.

add a test in /modules/python/test?

Does Python types work as expected?

What do you mean?
getPos() and Next() return a tuple
h = cv.LineIterator_create(np.array([30, 400]), np.array([200, 90]))
h = cv.LineIterator_create([30, 400], [200, 90])
h = cv.LineIterator_create((30, 400), (200, 90))
works

May be Next() method should become next()?

@asmorkalov
Copy link
Copy Markdown
Contributor

add a test in /modules/python/test? - yes.
Most probably alalek means Python type stubs. They should be generated automatically with bindings generator script.


/** @brief next pixel. Use for python binding.
*/
CV_WRAP Point Next() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use next() - OpenCV uses camelCase naming convention for functions

Comment on lines +264 to +265
Ptr<LineIterator> lineiterator = makePtr<LineIterator>(pt1, pt2, connectivity, leftToRight);
return lineiterator;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Ptr<LineIterator> lineiterator = makePtr<LineIterator>(pt1, pt2, connectivity, leftToRight);
return lineiterator;
return makePtr<LineIterator>(pt1, pt2, connectivity, leftToRight);

img = cv.imread(cv.samples.findFile('butterfly.jpg'))
pt1 = (30, 40)
pt2 = (200, 90)
h = cv.LineIterator_create(pt1, pt2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to use .create notation instead of _create

Suggested change
h = cv.LineIterator_create(pt1, pt2)
h = cv.LineIterator.create(pt1, pt2)

cv.destroyAllWindows()
pt1 = (40, 80)
pt2 = (30, 200)
h = cv.LineIterator_create(pt1, pt2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
h = cv.LineIterator_create(pt1, pt2)
h = cv.LineIterator.create(pt1, pt2)

@param leftToRight If true, the line is traversed from the leftmost endpoint to the rightmost
endpoint. Otherwise, the line is traversed from \p pt1 to \p pt2.
*/
LineIterator( const Mat& img, Point pt1, Point pt2,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the point of renaming img argument to src?
It is unnecessary change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before PR there was an attribute Mat img in LineIterator. Many warning were generated parameter img hide attribute . i change parameter name. attribute img was unused in class. I delete it after first or second review.
After two changes it is now unnecessary

@endcode
*/
class CV_EXPORTS LineIterator
class CV_EXPORTS_W LineIterator
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need create function. Have you tried to use CV_EXPORTS_W_SIMPLE and add CV_WRAP to the appropriate constructor?

Copy link
Copy Markdown
Contributor Author

@LaurentBerger LaurentBerger Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it does not work without static method create

h = cv.LineIterator.create(pt1, pt2)
pt = h.pos()

error is

[ WARN:0@0.057] global samples.cpp:61 cv::samples::findFile cv::samples::findFile('butterfly.jpg') => 'C:\lib\opencv\samples/data\butterfly.jpg'
Traceback (most recent call last):
  File "C:\lib\opencv\samples\python\lineiterator.py", line 8, in <module>
    pt = h.pos()
cv2.error: Unknown C++ exception from OpenCV code

I floolowed guide line (https://docs.opencv.org/4.x/da/d49/tutorial_py_bindings_basics.html)
For large classes also, CV_EXPORTS_W is used. To extend class methods, CV_WRAP is used. Similarly, CV_PROP is used for class fields.

Small classes/structs are extended using CV_EXPORTS_W_SIMPLE. These structs are passed by value to C++ functions. Examples are KeyPoint, Match etc. Their methods are extended by CV_WRAP and fields are extended by CV_PROP_RW.

Now code is with CV_EXPORTS_W_SIMPLE and with static method create

@@ -0,0 +1,32 @@
#!/usr/bin/python
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#!/usr/bin/env python is more usable

#!/usr/bin/python

'''
This example illustrates how to use LineIterator to get point coordinates on a line
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example

This line is not needed (at least not accurate)

import cv2 as cv
import numpy as np
import sys
import math
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

math

Where is it used?


pt = h.pos()
while min(pt1[0], pt2[0]) <= pt[0] <= max(pt1[0], pt2[0]) and \
min(pt1[1], pt2[1]) <= pt[1] <= max(pt1[1], pt2[1]):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we improve stop condition?


Could we provide a "native" Python iterator?

for pt in cv.LineIterator(pt1, pt2):
    ... process pt ...

See how to write Python (3+ only) code here: modules/core/misc/python/package (but for imgproc).

Python allows to replace modules's entity.
Redefine class (create new) or redefine __iter__/__next__ methods:

def LineIterator__iter__(self):
    ... reset ...
    return self

def LineIterator__next__(self):
    if self.next():
      return self.pos
    else:
      raise StopIteration

cv2.LineIterator.__iter__ = LineIterator__iter__
cv2.LineIterator.__next__ = LineIterator__next__

Copy link
Copy Markdown
Contributor Author

@LaurentBerger LaurentBerger Sep 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem when import cv2 :
cv2.LineIterator.iter = LineIterator__iter__
TypeError: cannot set 'iter' attribute of immutable type 'cv2.LineIterator'

file init.py content in imgproc/python/package/lineiteraor/

import cv2

def LineIterator__iter__(self):
    return self

def LineIterator__next__(self):
    if self.next():
        return self.pos
    else:
        raise StopIteration

cv2.LineIterator.__iter__ = LineIterator__iter__
cv2.LineIterator.__next__ = LineIterator__next__


/** @brief next pixel. Use for python binding.
*/
CV_WRAP Point next() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool next() interface is more appropriate for iterators.

@@ -0,0 +1,18 @@
import cv2 as cv

class LineIteratorB(cv.LineIterator):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for derived class.

Please use LineIteratorWrapper or LineIteratorForPython. `B looks as unclear name.

super().__init__(pt1, pt2)

def __iter__(self):
return self
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check:

line = cv.LineIterator(p1, p2)
print('first pass...')
for pt in line:
  print(pt)
print('second pass...')
for pt in line:
  print(pt)

Passes should print the same elements.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments above - reset part should be implemented.

def LineIterator__iter__(self):
    ... reset ...
    return self

BTW, another case from Python iter world:

line = cv.LineIterator(p1, p2)
it1 = iter(line)
it2 = iter(line)
... it1 and it2 should not interfere on each other (must be independent) ...

Perhaps we could not derive. May be we should use some kind of proxy pattern.


LineIteratorB.__module__ = cv.__name__
cv.LineIterator = LineIteratorB
cv._registerMatType(LineIteratorB)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cv._registerMatType(LineIteratorB)

Remove that.

count = count + 1
if count == 10:
self.assertEqual(p.pos(), (10.0, 10.0))
break
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets check for stop condition instead of break:

for ...
    ...
    self.assertLess(count, 102)
self.assertEqual(count, 101)

BTW, 101, 102 could be adjusted.

*/
CV_WRAP bool next() {
this->operator++();
return true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It never stops.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must change c++ class


pt = h.pos()
pt_tl, pt_br = boundaries_rect(pt1, pt2)
while pt_in_rect(pt, pt_tl, pt_br):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make it simple.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change in c++ class

@LaurentBerger
Copy link
Copy Markdown
Contributor Author

LaurentBerger commented Sep 21, 2023

@opencv-alalek something is weird
result with test_lineiterator.py in PR
#https://pullrequest.opencv.org/buildbot/builders/precommit_linux64/builds/104652
result without test_lineiterator.py in PR everything is green
#https://pullrequest.opencv.org/buildbot/builders/precommit_linux64/builds/104654
https://pullrequest.opencv.org/buildbot/builders/precommit_windows64/builds/102841
https://pullrequest.opencv.org/buildbot/builders/precommit_docs/builds/104438
https://pullrequest.opencv.org/buildbot/builders/precommit_linux64_no_opt/builds/103032
append test_lineiterator.py result with test_lineiterator.py in PR
now everything is red
Problem in python 2-3 test

In python 2 test_lineiterator (test_lineiterator.lineiterator_test) ... ERROR how to disable python2 test

in python3
test_lineiterator (test_lineiterator.lineiterator_test) ... ok Linux x64 Debug
for https://pullrequest.opencv.org/buildbot/builders/precommit_linux64_no_opt/builds/103034/steps/test_python3/logs/stdio
and for linux x64
In linux x64 a test failed est_arithm_op_with_saturation (test_misc.Arguments) ... /build/precommit_linux64/.container/job_2165934: line 19: 40186 Segmentation fault (core dumped) /app/bin/buildenv python3 ../4.x/opencv/modules/python/test/test.py --repo ../4.x/opencv --data /opt/build/python_test_data -v
for win64 python2 failed and test_arithm_op_with_saturation (test_misc.Arguments) ... program finished with exit code -1073741819

How my test can kill other testt?

still waiting an answer @opencv-alalek
@LaurentBerger

@asmorkalov
Copy link
Copy Markdown
Contributor

@opencv-alalek please take a look.

@LaurentBerger
Copy link
Copy Markdown
Contributor Author

@opencv-alalek please take a look.

I proposed to close this PR

@asmorkalov
Copy link
Copy Markdown
Contributor

@LaurentBerger Thanks a lot for the contribution. Feel free to close PR, if you treat it as not successful, or does not have a chance to finish the work.

@LaurentBerger
Copy link
Copy Markdown
Contributor Author

Thanks a lot for the contribution. Feel free to close PR, if you treat it as not successful, or does not have a chance to finish the work.

the work is finished but buildbot is wrong. All my unit tests are good but other tests failed with timeout

@LaurentBerger LaurentBerger mentioned this pull request Dec 17, 2023
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants