fixed bad shape of markers (1x4) in several cases and added tests#3105
Conversation
6df7b11 to
9b8394a
Compare
modules/aruco/src/aruco.cpp
Outdated
| _detectedCorners.create((int)finalAcceptedCorners.size(), 1, CV_32FC2); | ||
| for(unsigned int i = 0; i < finalAcceptedCorners.size(); i++) { | ||
| _detectedCorners.create(4, 1, CV_32FC2, i, true); | ||
| _detectedCorners.create(1, 4, CV_32FC2, i, true); |
There was a problem hiding this comment.
Why?
Below we have 4,1 again: _rejectedCorners.create(4, 1, ...)
BTW, all usages from aruco module:
$ grep -Rn '.create(4, 1,' ./modules/aruco/
./modules/aruco/src/aruco.cpp:584: out.create(4, 1, CV_32FC2, i);
./modules/aruco/src/aruco.cpp:591: out.create(4, 1, CV_32FC2, i);
./modules/aruco/src/aruco.cpp:598: out.create(4, 1, CV_32FC2, i);
./modules/aruco/src/aruco.cpp:696: _objPoints.create(4, 1, CV_32FC3);
./modules/aruco/src/aruco.cpp:1402: _detectedCorners.create(4, 1, CV_32FC2, i, true);
./modules/aruco/src/aruco.cpp:1420: _rejectedCorners.create(4, 1, CV_32FC2, i, true);
./modules/aruco/src/charuco.cpp:822: _diamondCorners.create(4, 1, CV_32FC2, i, true);
There was a problem hiding this comment.
It is surprising that the dimension of the matrices out[i], _objPoints[i], _detectedCorners[i], _rejectedCorners[i], _diamondCorners[i] is: (1 (rows) , 4 (cols)). But create with (4 (rows), 1 (cols)) was used.

Flag allowTransposed is ignored in this case (same values for true/false).
Looks like a bug in void _OutputArray::create(int _rows, int _cols, int mtype, int i, bool allowTransposed, int fixedDepthMask) const.
There was a problem hiding this comment.
Such changes affecting API are considered as a "breaking change".
So proper and complete change should have:
- clear justification
- consistent solution (fix 1 place only and left 6 others is not a good way)
- adapt coding rules to avoid such/similar problems in the future.
surprising
There is allowTransposed parameter on this line.
There was a problem hiding this comment.
This strange code change is removed. Now refineDetectedMarkers() for create OutputArrayOfArrays corners and rejectedCorners uses _copyVector2Output(), just like detectMarkers().
It's strange that before detectMarkers() and refineDetectedMarkers() created OutputArrayOfArrays corners and rejectedCorners in different ways.
|
|
||
| aruco::detectMarkers(img, dico, corners, ids, detectorParams, rejectedPoints); | ||
| ASSERT_FALSE(corners.empty()); | ||
| EXPECT_EQ(Size(4, 1), corners[0].size()); |
There was a problem hiding this comment.
EXPECT_EQ(Size(4, 1), corners[0].size());
Why do you think that Size(1, 4) is wrong?
There was a problem hiding this comment.
Size(1, 4) is not wrong (because it's just four 2d-points) , but corners[i] must be the same size in all cases to avoid undefined behavior.
I also think that InputOutputArrayOfArrays should not be used to pass array of pointers. You suggested replacing the InputOutputArrayOfArrays with a InputOutputArray with ndarray shape [npoints, 4, 2]. I'll try to do that.
There was a problem hiding this comment.
Let me clarify:
In all tests and all examples in OpenCV detectMarkers and refineDetectedMarkers return corners with Size(4, 1) (for corners[i]). It is strange if this condition is violated for no reason.
8f02cad to
211d636
Compare
211d636 to
d72a91f
Compare
|
@alalek, could you recheck PR? I changed the description of the PR. Code changes are made more clear. PR description has been improved. |
| _detectedCorners.clear(); | ||
| _detectedIds.clear(); | ||
|
|
||
| // parse output | ||
| Mat(finalAcceptedIds).copyTo(_detectedIds); | ||
|
|
||
| _detectedCorners.create((int)finalAcceptedCorners.size(), 1, CV_32FC2); | ||
| for(unsigned int i = 0; i < finalAcceptedCorners.size(); i++) { | ||
| _detectedCorners.create(4, 1, CV_32FC2, i, true); | ||
| for(int j = 0; j < 4; j++) { | ||
| _detectedCorners.getMat(i).ptr< Point2f >()[j] = | ||
| finalAcceptedCorners[i].ptr< Point2f >()[j]; | ||
| } | ||
| } | ||
| _copyVector2Output(finalAcceptedCorners, _detectedCorners); | ||
|
|
||
| // recalculate _rejectedCorners based on alreadyIdentified | ||
| vector< Mat > finalRejected; | ||
| vector<vector<Point2f> > finalRejected; | ||
| for(unsigned int i = 0; i < alreadyIdentified.size(); i++) { | ||
| if(!alreadyIdentified[i]) { | ||
| finalRejected.push_back(_rejectedCorners.getMat(i).clone()); | ||
| } | ||
| } | ||
|
|
||
| _rejectedCorners.clear(); | ||
| _rejectedCorners.create((int)finalRejected.size(), 1, CV_32FC2); | ||
| for(unsigned int i = 0; i < finalRejected.size(); i++) { | ||
| _rejectedCorners.create(4, 1, CV_32FC2, i, true); | ||
| for(int j = 0; j < 4; j++) { | ||
| _rejectedCorners.getMat(i).ptr< Point2f >()[j] = | ||
| finalRejected[i].ptr< Point2f >()[j]; | ||
| } | ||
| } | ||
| _copyVector2Output(finalRejected, _rejectedCorners); |
There was a problem hiding this comment.
Now, refineDetectedMarkers() uses _copyVector2Output() to copy, just as detectMarkers() does.
Merge with extra: opencv/opencv_extra#935
Fixes opencv/opencv#14014
Problem:
detectMarkers()andrefineDetectedMarkers()returnsOutputArrayOfArrays cornersandrejectedCornerswith different dimensions (for similar cases).detectMarkers()always returnscornersandrejectedCornersasvector<Mat>, with Mat dimension 4x1.refineDetectedMarkers()works differently, if it finds a new marker. Then the dimension of Mats inside thevector<Mat>changes from 4x1 to 1x4.detectMarkers()andrefineDetectedMarkers()createcornersandrejectedCornerswith different ways.vector<vector<Point2f> >forcornersandrejectedCorners. Also, the current tests only check the number of refined corners (dimension and value are not checked).In this PR
refineDetectedMarkers()for createOutputArrayOfArrays cornersandrejectedCornersuses_copyVector2Output(), just likedetectMarkers().opencv_extra=fix_refineDetectedMarkers_shapePull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
Patch to opencv_extra has the same branch name.