Skip to content

Commit 713ce63

Browse files
Kumatarothewoz
authored andcommitted
Merge pull request opencv#26022 from Kumataro:fix26016
Imgproc: use double to determine whether the corners points are within src opencv#26022 close opencv#26016 Related opencv/opencv_contrib#3778 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] 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 - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
1 parent 665228b commit 713ce63

File tree

5 files changed

+78
-8
lines changed

5 files changed

+78
-8
lines changed

modules/core/include/opencv2/core/types.hpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,14 @@ template<typename _Tp> class Rect_
475475
template<typename _Tp2> operator Rect_<_Tp2>() const;
476476

477477
//! checks whether the rectangle contains the point
478-
bool contains(const Point_<_Tp>& pt) const;
478+
/*! @warning After OpenCV 4.11.0, when calling Rect.contains() with cv::Point2f / cv::Point2d point, point should not convert/round to int.
479+
* ```
480+
* Rect_<int> r(0,0,500,500); Point_<float> pt(250.0f, 499.9f);
481+
* r.contains(pt) returns false.(OpenCV 4.10.0 or before)
482+
* r.contains(pt) returns true. (OpenCV 4.11.0 or later)
483+
* ```
484+
*/
485+
template<typename _Tp2> inline bool contains(const Point_<_Tp2>& pt) const;
479486

480487
_Tp x; //!< x coordinate of the top-left corner
481488
_Tp y; //!< y coordinate of the top-left corner
@@ -1861,12 +1868,29 @@ Rect_<_Tp>::operator Rect_<_Tp2>() const
18611868
return Rect_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y), saturate_cast<_Tp2>(width), saturate_cast<_Tp2>(height));
18621869
}
18631870

1864-
template<typename _Tp> inline
1865-
bool Rect_<_Tp>::contains(const Point_<_Tp>& pt) const
1871+
template<typename _Tp> template<typename _Tp2> inline
1872+
bool Rect_<_Tp>::contains(const Point_<_Tp2>& pt) const
18661873
{
18671874
return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height;
18681875
}
1869-
1876+
// See https://github.com/opencv/opencv/issues/26016
1877+
template<> template<> inline
1878+
bool Rect_<int>::contains(const Point_<double>& pt) const
1879+
{
1880+
// std::numeric_limits<int>::digits is 31.
1881+
// std::numeric_limits<double>::digits is 53.
1882+
// So conversion int->double does not lead to accuracy errors.
1883+
const Rect_<double> _rect(static_cast<double>(x), static_cast<double>(y), static_cast<double>(width), static_cast<double>(height));
1884+
return _rect.contains(pt);
1885+
}
1886+
template<> template<> inline
1887+
bool Rect_<int>::contains(const Point_<float>& _pt) const
1888+
{
1889+
// std::numeric_limits<float>::digits is 24.
1890+
// std::numeric_limits<double>::digits is 53.
1891+
// So conversion float->double does not lead to accuracy errors.
1892+
return contains(Point_<double>(static_cast<double>(_pt.x), static_cast<double>(_pt.y)));
1893+
}
18701894

18711895
template<typename _Tp> static inline
18721896
Rect_<_Tp>& operator += ( Rect_<_Tp>& a, const Point_<_Tp>& b )

modules/core/test/test_misc.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,22 @@ TYPED_TEST_P(Rect_Test, Overflows) {
908908
EXPECT_EQ(R(), R(20, 0, 10, 10) & R(0, num_lowest, 10, 10));
909909
EXPECT_EQ(R(), R(num_lowest, 0, 10, 10) & R(0, num_lowest, 10, 10));
910910
}
911-
REGISTER_TYPED_TEST_CASE_P(Rect_Test, Overflows);
911+
912+
// See https://github.com/opencv/opencv/issues/26016
913+
// Rect_<int>.contains(Point_<float/double>) needs template specialization.
914+
// This is test for a point on the edge and its nearest points.
915+
template<typename T> T cv_nexttoward(T v, T v2);
916+
template<> int cv_nexttoward<int>(int v, int v2) { CV_UNUSED(v); return v2; }
917+
template<> float cv_nexttoward<float>(float v, float v2) { return std::nextafter(v,v2); }
918+
template<> double cv_nexttoward<double>(double v, double v2) { return std::nexttoward(v,v2); }
919+
TYPED_TEST_P(Rect_Test, OnTheEdge) {
920+
Rect_<int> rect(0,0,500,500);
921+
TypeParam h = static_cast<TypeParam>(rect.height);
922+
ASSERT_TRUE ( rect.contains( Point_<TypeParam>(250, cv_nexttoward(h, h - 1))));
923+
ASSERT_FALSE( rect.contains( Point_<TypeParam>(250, cv_nexttoward(h, h ))));
924+
ASSERT_FALSE( rect.contains( Point_<TypeParam>(250, cv_nexttoward(h, h + 1))));
925+
}
926+
REGISTER_TYPED_TEST_CASE_P(Rect_Test, Overflows, OnTheEdge);
912927

913928
typedef ::testing::Types<int, float, double> RectTypes;
914929
INSTANTIATE_TYPED_TEST_CASE_P(Negative_Test, Rect_Test, RectTypes);

modules/features2d/src/keypoint.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ struct RoiPredicate
9696

9797
bool operator()( const KeyPoint& keyPt ) const
9898
{
99-
return !r.contains( keyPt.pt );
99+
// workaround for https://github.com/opencv/opencv/issues/26016
100+
// To keep its behaviour, keyPt.pt casts to Point_<int>.
101+
return !r.contains( Point_<int>(keyPt.pt) );
100102
}
101103

102104
Rect r;

modules/imgproc/test/test_cornersubpix.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,31 @@ TEST(Imgproc_CornerSubPix, out_of_image_corners)
6565
ASSERT_TRUE(Rect(0, 0, image.cols, image.rows).contains(corners.front()));
6666
}
6767

68+
// See https://github.com/opencv/opencv/issues/26016
69+
TEST(Imgproc_CornerSubPix, corners_on_the_edge)
70+
{
71+
cv::Mat image(500, 500, CV_8UC1);
72+
cv::Size win(1, 1);
73+
cv::Size zeroZone(-1, -1);
74+
cv::TermCriteria criteria;
75+
76+
std::vector<cv::Point2f> cornersOK1 = { cv::Point2f(250, std::nextafter(499.5f, 499.5f - 1.0f)) };
77+
EXPECT_NO_THROW( cv::cornerSubPix(image, cornersOK1, win, zeroZone, criteria) ) << cornersOK1;
78+
79+
std::vector<cv::Point2f> cornersOK2 = { cv::Point2f(250, 499.5f) };
80+
EXPECT_NO_THROW( cv::cornerSubPix(image, cornersOK2, win, zeroZone, criteria) ) << cornersOK2;
81+
82+
std::vector<cv::Point2f> cornersOK3 = { cv::Point2f(250, std::nextafter(499.5f, 499.5f + 1.0f)) };
83+
EXPECT_NO_THROW( cv::cornerSubPix(image, cornersOK3, win, zeroZone, criteria) ) << cornersOK3;
84+
85+
std::vector<cv::Point2f> cornersOK4 = { cv::Point2f(250, std::nextafter(500.0f, 500.0f - 1.0f)) };
86+
EXPECT_NO_THROW( cv::cornerSubPix(image, cornersOK4, win, zeroZone, criteria) ) << cornersOK4;
87+
88+
std::vector<cv::Point2f> cornersNG1 = { cv::Point2f(250, 500.0f) };
89+
EXPECT_ANY_THROW( cv::cornerSubPix(image, cornersNG1, win, zeroZone, criteria) ) << cornersNG1;
90+
91+
std::vector<cv::Point2f> cornersNG2 = { cv::Point2f(250, std::nextafter(500.0f, 500.0f + 1.0f)) };
92+
EXPECT_ANY_THROW( cv::cornerSubPix(image, cornersNG2, win, zeroZone, criteria) ) << cornersNG2;
93+
}
94+
6895
}} // namespace

modules/stitching/test/test_matchers.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ TEST(SurfFeaturesFinder, CanFindInROIs)
6767
int tl_rect_count = 0, br_rect_count = 0, bad_count = 0;
6868
for (const auto &keypoint : roi_features.keypoints)
6969
{
70-
if (rois[0].contains(keypoint.pt))
70+
// Workaround for https://github.com/opencv/opencv/issues/26016
71+
// To keep its behaviour, keypoint.pt casts to Point_<int>.
72+
if (rois[0].contains(Point_<int>(keypoint.pt)))
7173
tl_rect_count++;
72-
else if (rois[1].contains(keypoint.pt))
74+
else if (rois[1].contains(Point_<int>(keypoint.pt)))
7375
br_rect_count++;
7476
else
7577
bad_count++;

0 commit comments

Comments
 (0)