mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 08:13:04 +04:00
Merge branch 4.x
This commit is contained in:
@@ -76,6 +76,19 @@ TEST(Imgproc_ApproxPoly, bad_epsilon)
|
||||
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
|
||||
}
|
||||
|
||||
TEST(Imgproc_ApproxPoly, distace_between_point_and_segment)
|
||||
{
|
||||
vector<Point2f> inputPoints = {
|
||||
{ {0.f, 0.f}, {4.f, 2.f}, {11.f, 1.f}, {8.f, 0.f} }
|
||||
};
|
||||
std::vector<Point2f> result;
|
||||
approxPolyDP(inputPoints, result, 1.9, false);
|
||||
vector<Point2f> expectedResult = {
|
||||
{ {0.f, 0.f}, {11.f, 1.f}, {8.f, 0.f} }
|
||||
};
|
||||
ASSERT_EQ(result, expectedResult);
|
||||
}
|
||||
|
||||
struct ApproxPolyN: public testing::Test
|
||||
{
|
||||
void SetUp()
|
||||
|
||||
@@ -291,4 +291,33 @@ namespace opencv_test { namespace {
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
// Regression test for issue #28254
|
||||
// Out-of-bounds read in AVX2 bilateralFilter 32f path with BORDER_CONSTANT
|
||||
TEST(Imgproc_BilateralFilter, regression_28254_oob_read)
|
||||
{
|
||||
// Create a 64x64 CV_32FC1 image with values in range [100, 200]
|
||||
// Image must be large enough (width >= 32) to trigger SIMD/AVX2 code path.
|
||||
// Values are set so BORDER_CONSTANT padding (default 0) is outside the range,
|
||||
// which triggers the out-of-bounds condition in the LUT access.
|
||||
cv::Mat src(64, 64, CV_32FC1);
|
||||
cv::randu(src, 100.0f, 200.0f);
|
||||
cv::Mat dst;
|
||||
|
||||
// Parameters that trigger the bug
|
||||
int d = -1;
|
||||
double sigmaColor = 2.7;
|
||||
double sigmaSpace = 44.5;
|
||||
int borderType = cv::BORDER_CONSTANT;
|
||||
|
||||
// This should not crash or trigger AddressSanitizer
|
||||
EXPECT_NO_THROW(
|
||||
cv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace, borderType)
|
||||
);
|
||||
|
||||
// Verify output is valid
|
||||
EXPECT_FALSE(dst.empty());
|
||||
EXPECT_EQ(dst.size(), src.size());
|
||||
EXPECT_EQ(dst.type(), src.type());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -233,5 +233,20 @@ TEST(Imgproc_DrawContours, MatListOfMatIntScalarInt)
|
||||
EXPECT_EQ(nz, 0);
|
||||
}
|
||||
|
||||
TEST(Imgproc_Moments, degenerateContours)
|
||||
{
|
||||
std::vector<cv::Point> c1;
|
||||
c1.push_back(cv::Point(10,10));
|
||||
cv::Moments m1 = cv::moments(c1, false);
|
||||
EXPECT_EQ(m1.m00, 0);
|
||||
|
||||
std::vector<cv::Point> c2;
|
||||
c2.push_back(cv::Point(0,0));
|
||||
c2.push_back(cv::Point(5,5));
|
||||
c2.push_back(cv::Point(10,10));
|
||||
cv::Moments m2 = cv::moments(c2, false);
|
||||
EXPECT_EQ(m2.m00, 0);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
|
||||
@@ -304,9 +304,11 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
|
||||
vector<int> hull_ind;
|
||||
vector<Vec4i> defects;
|
||||
|
||||
#if 0 // deprecated behavior
|
||||
// first, check the original contour as-is, without intermediate fillPoly/drawContours.
|
||||
convexHull(contour_, hull_ind, false, false);
|
||||
EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception );
|
||||
#endif
|
||||
|
||||
int scale = 20;
|
||||
contour_ *= (double)scale;
|
||||
@@ -319,10 +321,12 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
|
||||
findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
convexHull(contours[0], hull_ind, false, false);
|
||||
|
||||
#if 0 // deprecated behavior
|
||||
// the original contour contains self-intersections,
|
||||
// therefore convexHull does not return a monotonous sequence of points
|
||||
// and therefore convexityDefects throws an exception
|
||||
EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception );
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
// one way to eliminate the contour self-intersection in this particular case is to apply dilate(),
|
||||
@@ -1049,7 +1053,7 @@ TEST_P(minEnclosingTriangle_Modes, accuracy)
|
||||
const Mat midPoint = (cur + next) / 2;
|
||||
EXPECT_TRUE(isPointOnHull(hull, midPoint));
|
||||
|
||||
// at least one of hull edges must be on tirangle edge
|
||||
// at least one of hull edges must be on triangle edge
|
||||
hasEdgeOnHull = hasEdgeOnHull || isEdgeOnHull(hull, cur, next);
|
||||
}
|
||||
EXPECT_TRUE(hasEdgeOnHull);
|
||||
@@ -1279,10 +1283,60 @@ INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
|
||||
testing::Values(
|
||||
std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f),
|
||||
std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f),
|
||||
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f),
|
||||
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301041f),
|
||||
std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
|
||||
));
|
||||
|
||||
typedef testing::TestWithParam<tuple<tuple<std::vector<Point>, Mat>, bool> > convexHull_monotonous;
|
||||
TEST_P(convexHull_monotonous, self_intersecting_contour)
|
||||
{
|
||||
std::vector<Point> contour = get<0>(get<0>(GetParam()));
|
||||
Mat ref = get<1>(get<0>(GetParam())).clone();
|
||||
bool clockwise = get<1>(GetParam());
|
||||
if (!clockwise)
|
||||
{
|
||||
std::reverse(ref.begin<int>(), ref.end<int>());
|
||||
}
|
||||
|
||||
Mat indices;
|
||||
convexHull(contour, indices, clockwise, false);
|
||||
|
||||
Point minLoc;
|
||||
minMaxLoc(indices, nullptr, nullptr, &minLoc);
|
||||
std::rotate(indices.begin<int>(), indices.begin<int>() + minLoc.y, indices.end<int>());
|
||||
|
||||
minMaxLoc(ref, nullptr, nullptr, &minLoc);
|
||||
std::rotate(ref.begin<int>(), ref.begin<int>() + minLoc.y, ref.end<int>());
|
||||
|
||||
ASSERT_EQ( cvtest::norm(indices, ref, NORM_INF), 0) << indices;
|
||||
}
|
||||
INSTANTIATE_TEST_CASE_P(Imgproc, convexHull_monotonous,
|
||||
testing::Combine(
|
||||
testing::Values(
|
||||
std::make_tuple(
|
||||
std::vector<Point>{
|
||||
Point(3, 2), Point(3, 4), Point(2, 5), Point(1, 5),
|
||||
Point(2, 5), Point(3, 4), Point(6, 4), Point(6, 2)
|
||||
},
|
||||
(Mat_<int>(5, 1) << 0, 3, 4, 6, 7)
|
||||
),
|
||||
std::make_tuple(
|
||||
std::vector<Point>{
|
||||
Point(3, -2), Point(3, -4), Point(2, -5), Point(1, -5),
|
||||
Point(2, -5), Point(3, -4), Point(6, -4), Point(6, -2)
|
||||
},
|
||||
(Mat_<int>(5, 1) << 3, 0, 7, 6, 4)
|
||||
),
|
||||
std::make_tuple(
|
||||
std::vector<Point>{
|
||||
Point(1, 1), Point(1, 0), Point(0, 0), Point(1, 0), Point(0, 1)
|
||||
},
|
||||
(Mat_<int>(4, 1) << 0, 1, 2, 4)
|
||||
)
|
||||
),
|
||||
testing::Bool()
|
||||
));
|
||||
|
||||
}} // namespace
|
||||
|
||||
/* End of file. */
|
||||
|
||||
@@ -1281,4 +1281,28 @@ TEST(Drawing, contours_filled)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for LINE_4 vs LINE_8 connectivity behavior
|
||||
// Regression test for issue #26413
|
||||
TEST(Drawing, line_connectivity_regression_26413)
|
||||
{
|
||||
Mat img4(10, 10, CV_8UC1, Scalar(0));
|
||||
Mat img8(10, 10, CV_8UC1, Scalar(0));
|
||||
|
||||
// Draw a diagonal line from (0,0) to (9,9)
|
||||
// LINE_4 (4-connected) should produce staircase pattern (no diagonals)
|
||||
// LINE_8 (8-connected) should produce diagonal steps
|
||||
line(img4, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_4);
|
||||
line(img8, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_8);
|
||||
|
||||
int count4 = countNonZero(img4);
|
||||
int count8 = countNonZero(img8);
|
||||
|
||||
// LINE_8 for a 10-pixel diagonal should have exactly 10 pixels
|
||||
EXPECT_EQ(10, count8) << "LINE_8 diagonal from (0,0) to (9,9) should have 10 pixels";
|
||||
|
||||
// LINE_4 for a 10-pixel diagonal should have approximately 19 pixels
|
||||
// (needs both horizontal and vertical steps)
|
||||
EXPECT_GT(count4, 15) << "LINE_4 diagonal should have significantly more pixels due to staircase";
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -509,7 +509,8 @@ int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx )
|
||||
EXPECT_LE(e, eps); // never true
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
|
||||
for(int i = 0; i < (int)std::min((unsigned int)(cornersQuality.size()), (unsigned int)(cornersQuality.size())); i++) {
|
||||
int min_size = (int)std::min(cornersQuality.size(), RefcornersQuality.size());
|
||||
for(int i = 0; i < min_size; i++) {
|
||||
if (std::abs(cornersQuality[i] - RefcornersQuality[i]) > eps * std::max(cornersQuality[i], RefcornersQuality[i]))
|
||||
printf("i = %i Quality %2.6f Quality ref %2.6f\n", i, cornersQuality[i], RefcornersQuality[i]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
Mat CropMid(InputArray src, int w, int h)
|
||||
{
|
||||
Mat mat = src.getMat();
|
||||
return mat(Rect(mat.cols / 2 - w / 2, mat.rows / 2 - h / 2, w, h));
|
||||
}
|
||||
|
||||
Mat GenerateTestImage(Size size)
|
||||
{
|
||||
Mat image = Mat::zeros(size.height * 2, size.width * 2, CV_32F);
|
||||
rectangle(image,
|
||||
Point(static_cast<int>(size.width * 0.1), static_cast<int>(size.height * 0.1)),
|
||||
Point(static_cast<int>(size.width * 0.9), static_cast<int>(size.height * 0.9)),
|
||||
Scalar(1),
|
||||
-1);
|
||||
return image;
|
||||
}
|
||||
|
||||
void TestPhaseCorrelationIterative(const Size& size, const double maxShift)
|
||||
{
|
||||
const auto iters = std::max(201., maxShift * 10 + 1);
|
||||
const Point2d shiftOffset(-maxShift * 0.5, -maxShift * 0.5);
|
||||
Mat image1 = GenerateTestImage(size);
|
||||
Mat crop1 = CropMid(image1, size.width, size.height);
|
||||
Mat image2 = image1.clone();
|
||||
|
||||
std::vector<double> pcErrors;
|
||||
std::vector<double> ipcErrors;
|
||||
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
const auto shift =
|
||||
Point2d(maxShift * i / (iters - 1), maxShift * i / (iters - 1)) + shiftOffset;
|
||||
const Mat Tmat = (Mat_<double>(2, 3) << 1., 0., shift.x, 0., 1., shift.y);
|
||||
warpAffine(image1, image2, Tmat, image2.size());
|
||||
Mat crop2 = CropMid(image2, size.width, size.height);
|
||||
const auto ipcshift = phaseCorrelateIterative(crop1, crop2);
|
||||
const auto pcshift = phaseCorrelate(crop1, crop2);
|
||||
|
||||
pcErrors.push_back(
|
||||
0.5 * std::abs(pcshift.x - shift.y) + 0.5 * std::abs(pcshift.y - shift.x));
|
||||
ipcErrors.push_back(
|
||||
0.5 * std::abs(ipcshift.x - shift.y) + 0.5 * std::abs(ipcshift.y - shift.x));
|
||||
|
||||
// error should be low
|
||||
EXPECT_NEAR(ipcshift.x - shift.x, 0.0, 0.1);
|
||||
EXPECT_NEAR(ipcshift.y - shift.y, 0.0, 0.1);
|
||||
}
|
||||
|
||||
cv::Scalar pcMean, pcStddev, ipcMean, ipcStddev;
|
||||
meanStdDev(ipcErrors, ipcMean, ipcStddev);
|
||||
meanStdDev(pcErrors, pcMean, pcStddev);
|
||||
|
||||
// average error should be low
|
||||
ASSERT_LT(ipcMean[0], 0.03);
|
||||
// average error should be less than non-iterative average error
|
||||
ASSERT_LT(ipcMean[0], pcMean[0]);
|
||||
// error stddev should be less than non-iterative error stddev
|
||||
ASSERT_LT(ipcStddev[0], pcStddev[0]);
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, 256x128_accuracy)
|
||||
{
|
||||
TestPhaseCorrelationIterative(Size(256, 128), 1);
|
||||
}
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_1)
|
||||
{
|
||||
TestPhaseCorrelationIterative(Size(64, 64), 1);
|
||||
}
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_16)
|
||||
{
|
||||
TestPhaseCorrelationIterative(Size(64, 64), 16);
|
||||
}
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, 0x0_image)
|
||||
{
|
||||
ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(0, 0), 1));
|
||||
}
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, 1x1_image)
|
||||
{
|
||||
ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(1, 1), 1));
|
||||
}
|
||||
|
||||
TEST(Imgproc_PhaseCorrelationIterative, accuracy_real_img)
|
||||
{
|
||||
Mat img = imread(cvtest::TS::ptr()->get_data_path() + "shared/airplane.png", IMREAD_GRAYSCALE);
|
||||
if (img.empty())
|
||||
return;
|
||||
img.convertTo(img, CV_64FC1);
|
||||
|
||||
const int xLen = 256;
|
||||
const int yLen = 256;
|
||||
const int xShift = 40;
|
||||
const int yShift = 14;
|
||||
|
||||
Mat roi1 = img(Rect(xShift, yShift, xLen, yLen));
|
||||
Mat roi2 = img(Rect(0, 0, xLen, yLen));
|
||||
|
||||
const Point2d ipcShift = phaseCorrelateIterative(roi1, roi2);
|
||||
|
||||
ASSERT_NEAR(ipcShift.x, (double)xShift, 1.);
|
||||
ASSERT_NEAR(ipcShift.y, (double)yShift, 1.);
|
||||
}
|
||||
|
||||
}} // namespace opencv_test
|
||||
@@ -311,5 +311,18 @@ TEST_P(StackBlur_GaussianBlur, compare)
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgproc, StackBlur_GaussianBlur, testing::Values(CV_8U, CV_16S, CV_16U, CV_32F));
|
||||
|
||||
TEST(Imgproc_StackBlur, regression_28233)
|
||||
{
|
||||
Mat src1(1, 1, CV_8UC1, Scalar(123));
|
||||
Mat dst1;
|
||||
EXPECT_NO_THROW(stackBlur(src1, dst1, Size(9, 1)));
|
||||
EXPECT_EQ(dst1.at<uchar>(0, 0), 123);
|
||||
|
||||
Mat src2(3, 3, CV_8UC1, Scalar(50));
|
||||
Mat dst2;
|
||||
EXPECT_NO_THROW(stackBlur(src2, dst2, Size(11, 11)));
|
||||
EXPECT_EQ(dst2.at<uchar>(1, 1), 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user