imgproc: contours C-API cleanup

pull/25431/head
Maksim Shabunin 8 months ago
parent 5c1fbc2d0f
commit 3def7d09bc
  1. 30
      modules/imgproc/include/opencv2/imgproc/detail/legacy.hpp
  2. 2041
      modules/imgproc/src/contours.cpp
  3. 272
      modules/imgproc/src/geometry.cpp
  4. 120
      modules/imgproc/test/test_contours_new.cpp

@ -1,30 +0,0 @@
// 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
#ifndef OPENCV_IMGPROC_DETAIL_LEGACY_HPP
#define OPENCV_IMGPROC_DETAIL_LEGACY_HPP
#include "opencv2/imgproc.hpp"
namespace cv {
#ifdef __OPENCV_BUILD
CV_EXPORTS void findContours_legacy(InputArray _image,
OutputArrayOfArrays _contours,
OutputArray _hierarchy,
int mode,
int method,
Point offset = Point());
CV_EXPORTS void findContours_legacy(InputArray image,
OutputArrayOfArrays contours,
int mode,
int method,
Point offset = Point());
#endif
} // namespace cv
#endif // OPENCV_IMGPROC_DETAIL_LEGACY_HPP

File diff suppressed because it is too large Load Diff

@ -39,7 +39,9 @@
//
//M*/
#include "precomp.hpp"
#include "opencv2/core/hal/intrin.hpp"
using namespace cv;
double cv::pointPolygonTest( InputArray _contour, Point2f pt, bool measureDist )
{
@ -533,3 +535,273 @@ float cv::intersectConvexConvex( InputArray _p1, InputArray _p2, OutputArray _p1
}
return (float)fabs(area);
}
static Rect maskBoundingRect( const Mat& img )
{
CV_Assert( img.depth() <= CV_8S && img.channels() == 1 );
Size size = img.size();
int xmin = size.width, ymin = -1, xmax = -1, ymax = -1, i, j, k;
for( i = 0; i < size.height; i++ )
{
const uchar* _ptr = img.ptr(i);
const uchar* ptr = (const uchar*)alignPtr(_ptr, 4);
int have_nz = 0, k_min, offset = (int)(ptr - _ptr);
j = 0;
offset = MIN(offset, size.width);
for( ; j < offset; j++ )
if( _ptr[j] )
{
if( j < xmin )
xmin = j;
if( j > xmax )
xmax = j;
have_nz = 1;
}
if( offset < size.width )
{
xmin -= offset;
xmax -= offset;
size.width -= offset;
j = 0;
for( ; j <= xmin - 4; j += 4 )
if( *((int*)(ptr+j)) )
break;
for( ; j < xmin; j++ )
if( ptr[j] )
{
xmin = j;
if( j > xmax )
xmax = j;
have_nz = 1;
break;
}
k_min = MAX(j-1, xmax);
k = size.width - 1;
for( ; k > k_min && (k&3) != 3; k-- )
if( ptr[k] )
break;
if( k > k_min && (k&3) == 3 )
{
for( ; k > k_min+3; k -= 4 )
if( *((int*)(ptr+k-3)) )
break;
}
for( ; k > k_min; k-- )
if( ptr[k] )
{
xmax = k;
have_nz = 1;
break;
}
if( !have_nz )
{
j &= ~3;
for( ; j <= k - 3; j += 4 )
if( *((int*)(ptr+j)) )
break;
for( ; j <= k; j++ )
if( ptr[j] )
{
have_nz = 1;
break;
}
}
xmin += offset;
xmax += offset;
size.width += offset;
}
if( have_nz )
{
if( ymin < 0 )
ymin = i;
ymax = i;
}
}
if( xmin >= size.width )
xmin = ymin = 0;
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
}
// Calculates bounding rectangle of a point set or retrieves already calculated
static Rect pointSetBoundingRect( const Mat& points )
{
int npoints = points.checkVector(2);
int depth = points.depth();
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));
int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i;
bool is_float = depth == CV_32F;
if( npoints == 0 )
return Rect();
#if CV_SIMD // TODO: enable for CV_SIMD_SCALABLE, loop tail related.
const int64_t* pts = points.ptr<int64_t>();
if( !is_float )
{
v_int32 minval, maxval;
minval = maxval = v_reinterpret_as_s32(vx_setall_s64(*pts)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
for( i = 1; i <= npoints - VTraits<v_int32>::vlanes()/2; i+= VTraits<v_int32>::vlanes()/2 )
{
v_int32 ptXY2 = v_reinterpret_as_s32(vx_load(pts + i));
minval = v_min(ptXY2, minval);
maxval = v_max(ptXY2, maxval);
}
minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval))));
maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval))));
if( i <= npoints - VTraits<v_int32>::vlanes()/4 )
{
v_int32 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + i))));
minval = v_min(ptXY, minval);
maxval = v_max(ptXY, maxval);
i += VTraits<v_int64>::vlanes()/2;
}
for(int j = 16; j < VTraits<v_uint8>::vlanes(); j*=2)
{
minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval))));
maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval))));
}
xmin = v_get0(minval);
xmax = v_get0(maxval);
ymin = v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval))));
ymax = v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval))));
#if CV_SIMD_WIDTH > 16
if( i < npoints )
{
v_int32x4 minval2, maxval2;
minval2 = maxval2 = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i))));
for( i++; i < npoints; i++ )
{
v_int32x4 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i))));
minval2 = v_min(ptXY, minval2);
maxval2 = v_max(ptXY, maxval2);
}
xmin = min(xmin, v_get0(minval2));
xmax = max(xmax, v_get0(maxval2));
ymin = min(ymin, v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval2)))));
ymax = max(ymax, v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval2)))));
}
#endif // CV_SIMD
}
else
{
v_float32 minval, maxval;
minval = maxval = v_reinterpret_as_f32(vx_setall_s64(*pts)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
for( i = 1; i <= npoints - VTraits<v_float32>::vlanes()/2; i+= VTraits<v_float32>::vlanes()/2 )
{
v_float32 ptXY2 = v_reinterpret_as_f32(vx_load(pts + i));
minval = v_min(ptXY2, minval);
maxval = v_max(ptXY2, maxval);
}
minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval))));
maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval))));
if( i <= npoints - VTraits<v_float32>::vlanes()/4 )
{
v_float32 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + i))));
minval = v_min(ptXY, minval);
maxval = v_max(ptXY, maxval);
i += VTraits<v_float32>::vlanes()/4;
}
for(int j = 16; j < VTraits<v_uint8>::vlanes(); j*=2)
{
minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval))));
maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval))));
}
xmin = cvFloor(v_get0(minval));
xmax = cvFloor(v_get0(maxval));
ymin = cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval)))));
ymax = cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval)))));
#if CV_SIMD_WIDTH > 16
if( i < npoints )
{
v_float32x4 minval2, maxval2;
minval2 = maxval2 = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i))));
for( i++; i < npoints; i++ )
{
v_float32x4 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i))));
minval2 = v_min(ptXY, minval2);
maxval2 = v_max(ptXY, maxval2);
}
xmin = min(xmin, cvFloor(v_get0(minval2)));
xmax = max(xmax, cvFloor(v_get0(maxval2)));
ymin = min(ymin, cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval2))))));
ymax = max(ymax, cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval2))))));
}
#endif
}
#else
const Point* pts = points.ptr<Point>();
Point pt = pts[0];
if( !is_float )
{
xmin = xmax = pt.x;
ymin = ymax = pt.y;
for( i = 1; i < npoints; i++ )
{
pt = pts[i];
if( xmin > pt.x )
xmin = pt.x;
if( xmax < pt.x )
xmax = pt.x;
if( ymin > pt.y )
ymin = pt.y;
if( ymax < pt.y )
ymax = pt.y;
}
}
else
{
Cv32suf v;
// init values
xmin = xmax = CV_TOGGLE_FLT(pt.x);
ymin = ymax = CV_TOGGLE_FLT(pt.y);
for( i = 1; i < npoints; i++ )
{
pt = pts[i];
pt.x = CV_TOGGLE_FLT(pt.x);
pt.y = CV_TOGGLE_FLT(pt.y);
if( xmin > pt.x )
xmin = pt.x;
if( xmax < pt.x )
xmax = pt.x;
if( ymin > pt.y )
ymin = pt.y;
if( ymax < pt.y )
ymax = pt.y;
}
v.i = CV_TOGGLE_FLT(xmin); xmin = cvFloor(v.f);
v.i = CV_TOGGLE_FLT(ymin); ymin = cvFloor(v.f);
// because right and bottom sides of the bounding rectangle are not inclusive
// (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil
v.i = CV_TOGGLE_FLT(xmax); xmax = cvFloor(v.f);
v.i = CV_TOGGLE_FLT(ymax); ymax = cvFloor(v.f);
}
#endif
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
}
cv::Rect cv::boundingRect(InputArray array)
{
CV_INSTRUMENT_REGION();
Mat m = array.getMat();
return m.depth() <= CV_8U ? maskBoundingRect(m) : pointSetBoundingRect(m);
}

@ -4,9 +4,6 @@
#include "test_precomp.hpp"
#include "opencv2/ts/ocl_test.hpp"
#include "opencv2/imgproc/detail/legacy.hpp"
#define CHECK_OLD 1
namespace opencv_test { namespace {
@ -197,38 +194,6 @@ TEST_P(Imgproc_FindContours_Modes1, rectangle)
}
}
}
#if CHECK_OLD
if (method != 0) // old doesn't support chain codes
{
if (mode != RETR_FLOODFILL)
{
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t j = 0; j < contours.size(); ++j)
{
SCOPED_TRACE(format("contour %zu", j));
EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
}
else
{
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t j = 0; j < contours.size(); ++j)
{
SCOPED_TRACE(format("contour %zu", j));
EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
}
}
#endif
}
}
@ -276,18 +241,6 @@ TEST_P(Imgproc_FindContours_Modes1, small)
{
findContours(img32s, contours, hierarchy, mode, method);
ASSERT_EQ(pts.size() * 2 + extra_contours_32s, contours.size());
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t i = 0; i < contours.size(); ++i)
{
SCOPED_TRACE(format("contour %zu", i));
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
#endif
}
}
else
@ -301,18 +254,6 @@ TEST_P(Imgproc_FindContours_Modes1, small)
{
findContours(img, contours, hierarchy, mode, method);
ASSERT_EQ(pts.size(), contours.size());
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t i = 0; i < contours.size(); ++i)
{
SCOPED_TRACE(format("contour %zu", i));
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
#endif
}
}
}
@ -357,18 +298,6 @@ TEST_P(Imgproc_FindContours_Modes1, deep)
{
findContours(img32s, contours, hierarchy, mode, method);
ASSERT_EQ(2 * NUM, contours.size());
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t i = 0; i < contours.size(); ++i)
{
SCOPED_TRACE(format("contour %zu", i));
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
#endif
}
}
else
@ -383,18 +312,6 @@ TEST_P(Imgproc_FindContours_Modes1, deep)
{
findContours(img, contours, hierarchy, mode, method);
ASSERT_EQ(expected_count, contours.size());
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours.size(), contours_o.size());
for (size_t i = 0; i < contours.size(); ++i)
{
SCOPED_TRACE(format("contour %zu", i));
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
#endif
}
}
}
@ -475,18 +392,6 @@ TEST_P(Imgproc_FindContours_Modes2, new_accuracy)
{
EXPECT_EQ(0., diff1);
}
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours(img, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours_o.size(), contours.size());
for (size_t i = 0; i < contours_o.size(); ++i)
{
SCOPED_TRACE(format("contour = %zu", i));
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
#endif
}
TEST_P(Imgproc_FindContours_Modes2, approx)
@ -530,18 +435,6 @@ TEST_P(Imgproc_FindContours_Modes2, approx)
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, mode, method);
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
ASSERT_EQ(contours_o.size(), contours.size());
for (size_t i = 0; i < contours_o.size(); ++i)
{
SCOPED_TRACE(format("c = %d, contour = %zu", c, i));
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
#endif
// TODO: check something
}
}
@ -587,19 +480,6 @@ TEST(Imgproc_FindContours, link_runs)
imshow("img", img);
waitKey(0);
}
#if CHECK_OLD
vector<vector<Point>> contours_o;
vector<Vec4i> hierarchy_o;
findContours_legacy(img, contours_o, hierarchy_o, 0, 5); // CV_LINK_RUNS method
ASSERT_EQ(contours_o.size(), contours.size());
for (size_t i = 0; i < contours_o.size(); ++i)
{
SCOPED_TRACE(format("contour = %zu", i));
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
}
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
#endif
}
}} // namespace opencv_test

Loading…
Cancel
Save