Skip to content

add estimateAffine3D overload that implements Umeyama's algorithm#19689

Merged
opencv-pushbot merged 1 commit intoopencv:masterfrom
andy-held:umeyama
May 25, 2021
Merged

add estimateAffine3D overload that implements Umeyama's algorithm#19689
opencv-pushbot merged 1 commit intoopencv:masterfrom
andy-held:umeyama

Conversation

@andy-held
Copy link
Copy Markdown
Contributor

@andy-held andy-held commented Mar 7, 2021

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 other license that is incompatible with OpenCV
  • The PR is proposed to proper branch
  • There is reference to original bug report and related work
  • 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

Added an estimateAffine3D overload that implements Umeyama's algorithm. Compared to the existing function, this has the advantage that it can optimize scale, will always return a rotation matrix and can force the rotation to not be a reflection, but has the disadvantage that it is not as robust against outliers.

I would appreciate feedback regarding the function signature my Mat use, as I am not as versed with it.

Mat u,d,vt;
SVD::compute(cov, d, u, vt, SVD::MODIFY_A | SVD::FULL_UV);

CV_CheckGE(countNonZero(d), 2, "Points cannot be colinear");
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 check may throw exception, which is not a good idea for a numerical algorithm. Instead, it should provide some reasonable response, i.e. return empty matrix or maybe one of possible solutions (if there are many).

Copy link
Copy Markdown
Contributor Author

@andy-held andy-held Mar 29, 2021

Choose a reason for hiding this comment

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

If the points are collinear the problem is underconstrained and there is an infinite number of solutions. However it is common to throw on malformed arguments, for example if the input matrices are not the correct dimension.
E.g. CV_CheckEQ(to.checkVector(3), count, "Point sets need to have the same size"); I would consider this to be a similar case.

I would suggest extending the documentation to saying that co-linear points are not valid. Returning a possible solution would involve matching two two point sets, of which at least one lies on a line. So we would need to choose a random plane to project to to do so. It would involve quite some heuristics and would be pretty hard for the user to unravel if it goes wrong.

BTW.: I tried what happens when a collinear set of points is passed to the existing RANSAC implementation:

import cv2
import numpy as np

a = np.array([[1., 1, 1, 1],[0, 0, 0, 0],[0, 0, 0, 0]]).T

r, o, i = cv2.estimateAffine3D(a, a)
print(o, r)

returns:

[[0.5 0.  0.  0.5]
 [0.  0.  0.  0. ]
 [0.  0.  0.  0. ]] 1

Should I maybe open an issue, at least this should probably not report a successful execution.

@vpisarev
Copy link
Copy Markdown
Contributor

@andy-held, thank you for the contribution.
The use of cv::Mat is fine, even though it makes the algorithm several times slower than it could be, especially on small datasets, where the overhead of the matrix operations might be a bit high.
Also, you mentioned that the algorithm is not robust to outliers, since it does not include RANSAC. I wonder if the algorithm can be refactored and be used inside RANSAC loop? Now many points does it need at minimum?

@andy-held
Copy link
Copy Markdown
Contributor Author

I wasn't sure about the use of CV_OUT double* _scale as kind of a flag. If nullptr is given here the scale will not be estimated. A different approach would be to have Mat estimateAffine3D(InputArray _from, InputArray _to, CV_OUT double* _scale, int flags) and pass estimate_scale and force_rotation in the flags.

It could be used as core of the RANSAC loop, it needs 4 points at least. The advantage compared to the linear equation solving is that the resulting transformation is guaranteed to have an orthonormal rotation matrix (and can also force it to not be a reflection).

@asenyaev
Copy link
Copy Markdown
Contributor

asenyaev commented Apr 8, 2021

jenkins cn please retry a build

@andy-held
Copy link
Copy Markdown
Contributor Author

andy-held commented Apr 28, 2021

I thought about my last statement and looked it up, I was wrong. The algorithm needs only 3 points, as it only calculates a transformation with 7 degrees of freedom. I adjusted my code accordingly and also noted it in the comments explicitely.

@vpisarev vpisarev self-requested a review May 25, 2021 06:49
@opencv-pushbot opencv-pushbot merged commit cc712a1 into opencv:master May 25, 2021
@alalek alalek mentioned this pull request Jun 4, 2021
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.

5 participants