mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29547 from alessio-antochi:fix-approxpoly-test
Rework accuracy test for legacy C-API cvApproxPoly and CV_PerimeterTest - #29547 ### 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 - [ ] 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 **Details:** This PR restores the deleted C-API `cvApproxPoly` tests (part of #24957). I used `cv::pointPolygonTest` to make sure every original point is within the allowed `epsilon` distance to the final approximated polygon rather than calculate the distance to the corresponding line. **Update:** Restored `CV_PerimeterTest` *TEST(Imgproc_ContourPerimeter, accuracy)*. Created a parameterized test for open/closed curves with int/float type shapes. I also added a simple 10x10 square test.
This commit is contained in:
committed by
GitHub
parent
0627765f01
commit
16e83222dc
@@ -43,21 +43,58 @@
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//
|
||||
// TODO!!!:
|
||||
// check_slice (and/or check) seem(s) to be broken, or this is a bug in function
|
||||
// (or its inability to handle possible self-intersections in the generated contours).
|
||||
//
|
||||
// At least, if // return TotalErrors;
|
||||
// is uncommented in check_slice, the test fails easily.
|
||||
// So, now (and it looks like since 0.9.6)
|
||||
// we only check that the set of vertices of the approximated polygon is
|
||||
// a subset of vertices of the original contour.
|
||||
//
|
||||
|
||||
// Tests that every point on the original polygon is
|
||||
// within epsilon distance to the approximated polygon.
|
||||
TEST(Geometry_ApproxPoly, accuracy)
|
||||
{
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
const int polygon_count = 30;
|
||||
|
||||
for (int i = 0; i < polygon_count; ++i)
|
||||
{
|
||||
// create a random noisy polygon
|
||||
vector<Point> curve;
|
||||
Point center {rng.uniform(100, 400), rng.uniform(100, 400)};
|
||||
double base_radius = rng.uniform(20.0, 100.0);
|
||||
int points_count = rng.uniform(20, 100);
|
||||
|
||||
for (int p = 0; p < points_count; ++p)
|
||||
{
|
||||
double angle = p * (2 * CV_PI) / points_count;
|
||||
double radius = base_radius + rng.uniform(-10.0, 10.0);
|
||||
curve.push_back(Point(cvRound(center.x + radius * cos(angle)),
|
||||
cvRound(center.y + radius * sin(angle))));
|
||||
}
|
||||
|
||||
Rect bound = boundingRect(curve);
|
||||
double max_diameter = sqrt(bound.width * bound.width + bound.height * bound.height);
|
||||
const int epsilons_count = 10;
|
||||
double eps_step = max_diameter / epsilons_count;
|
||||
|
||||
// test different epsilon values
|
||||
for (double eps = eps_step; eps < max_diameter; eps += eps_step)
|
||||
{
|
||||
vector<Point> approx_curve;
|
||||
approxPolyDP(curve, approx_curve, eps, true);
|
||||
|
||||
EXPECT_LE(approx_curve.size(), curve.size());
|
||||
EXPECT_GT(approx_curve.size(), 0U);
|
||||
|
||||
// every point on original curve should be within epsilon distance to the new polygon
|
||||
const double tol = std::max(1e-3, 16 * FLT_EPSILON * max_diameter);
|
||||
for (const auto& pt : curve)
|
||||
{
|
||||
const double min_dist = std::abs(cv::pointPolygonTest(approx_curve, Point2f(pt), true));
|
||||
EXPECT_LE(min_dist, eps + tol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Tests to make sure that unreasonable epsilon (error)
|
||||
//values never get passed to the Douglas-Peucker algorithm.
|
||||
TEST(Imgproc_ApproxPoly, bad_epsilon)
|
||||
TEST(Geometry_ApproxPoly, bad_epsilon)
|
||||
{
|
||||
std::vector<Point2f> inputPoints;
|
||||
inputPoints.push_back(Point2f(0.0f, 0.0f));
|
||||
@@ -76,7 +113,7 @@ TEST(Imgproc_ApproxPoly, bad_epsilon)
|
||||
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
|
||||
}
|
||||
|
||||
TEST(Imgproc_ApproxPoly, distace_between_point_and_segment)
|
||||
TEST(Geometry_ApproxPoly, distance_between_point_and_segment)
|
||||
{
|
||||
vector<Point2f> inputPoints = {
|
||||
{ {0.f, 0.f}, {4.f, 2.f}, {11.f, 1.f}, {8.f, 0.f} }
|
||||
|
||||
97
modules/geometry/test/test_arclength.cpp
Normal file
97
modules/geometry/test/test_arclength.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef testing::TestWithParam<bool> Geometry_ArcLength;
|
||||
|
||||
template<typename T>
|
||||
static double refArcLength(const std::vector<T>& curve, bool is_closed)
|
||||
{
|
||||
double expected_length = 0.0;
|
||||
size_t npoints = curve.size();
|
||||
T prev_pt = curve[is_closed ? npoints - 1 : 0];
|
||||
int start_idx = is_closed ? 0 : 1;
|
||||
for (size_t i = start_idx; i < npoints; i++)
|
||||
{
|
||||
T pt = curve[i];
|
||||
expected_length += cv::norm(pt - prev_pt);
|
||||
prev_pt = pt;
|
||||
}
|
||||
|
||||
return expected_length;
|
||||
}
|
||||
|
||||
// Test arcLength accuracy
|
||||
// Covers both open/closed curves and int/float coordinates
|
||||
TEST_P(Geometry_ArcLength, accuracy_float)
|
||||
{
|
||||
const bool is_closed = GetParam();
|
||||
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
|
||||
// generate random points
|
||||
const int npoints = rng.uniform(3, 100);
|
||||
vector<Point2f> curvef;
|
||||
|
||||
for (int i = 0; i < npoints; i++)
|
||||
{
|
||||
Point2f pt(rng.uniform(-1000.f, 1000.f), rng.uniform(-1000.f, 1000.f));
|
||||
curvef.push_back(pt);
|
||||
}
|
||||
|
||||
double length = cv::arcLength(curvef, is_closed);
|
||||
double expected_length = refArcLength(curvef, is_closed);
|
||||
|
||||
EXPECT_NEAR(expected_length, length, FLT_EPSILON * 100 * expected_length);
|
||||
}
|
||||
|
||||
TEST_P(Geometry_ArcLength, accuracy_int)
|
||||
{
|
||||
const bool is_closed = GetParam();
|
||||
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
|
||||
// generate random points
|
||||
const int npoints = rng.uniform(3, 100);
|
||||
vector<Point2i> curvei;
|
||||
|
||||
for (int i = 0; i < npoints; i++)
|
||||
{
|
||||
Point2f pt(rng.uniform(-1000.f, 1000.f), rng.uniform(-1000.f, 1000.f));
|
||||
curvei.push_back(Point2i((int)pt.x, (int)pt.y));
|
||||
}
|
||||
|
||||
double length = cv::arcLength(curvei, is_closed);
|
||||
double expected_length = refArcLength(curvei, is_closed);
|
||||
|
||||
EXPECT_NEAR(expected_length, length, FLT_EPSILON * expected_length);
|
||||
}
|
||||
|
||||
// Simple test with a manually defined shape
|
||||
TEST(Geometry_ArcLength, simple_square)
|
||||
{
|
||||
// create a 10x10 square
|
||||
vector<Point2f> square;
|
||||
square.push_back(Point2f(0, 0));
|
||||
square.push_back(Point2f(10, 0));
|
||||
square.push_back(Point2f(10, 10));
|
||||
square.push_back(Point2f(0, 10));
|
||||
|
||||
// closed perimeter
|
||||
EXPECT_DOUBLE_EQ(cv::arcLength(square, true), 40.0);
|
||||
|
||||
// open perimeter (missing the edge from (0,10) to (0,0))
|
||||
EXPECT_DOUBLE_EQ(cv::arcLength(square, false), 30.0);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Geometry,
|
||||
Geometry_ArcLength,
|
||||
testing::Bool()
|
||||
);
|
||||
|
||||
}} // namespace opencv_test
|
||||
Reference in New Issue
Block a user