From 9803eb0443947f2c801f92ec82970269918af6c4 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 3 Jun 2026 22:50:44 +0300 Subject: [PATCH] Move more 2d funcs to geometry module. --- .../geometry/include/opencv2/geometry/2d.hpp | 50 ++++ .../geometry/misc/java/test/GeometryTest.java | 41 ++- modules/geometry/perf/perf_2d.cpp | 22 ++ modules/geometry/src/geometry.cpp | 278 ++++++++++++++++++ .../test/test_boundingrect.cpp | 0 modules/imgproc/include/opencv2/imgproc.hpp | 51 ---- .../imgproc/misc/java/test/ImgprocTest.java | 38 --- modules/imgproc/perf/perf_contours.cpp | 22 -- modules/imgproc/src/contours_common.cpp | 278 ------------------ modules/imgproc/test/test_moments.cpp | 4 +- .../colorblobdetect/ColorBlobDetector.java | 5 +- samples/cpp/snippets/segment_objects.cpp | 1 + .../ShapeDescriptors/moments_demo.cpp | 1 + .../introduction_to_pca.cpp | 1 + 14 files changed, 398 insertions(+), 394 deletions(-) rename modules/{imgproc => geometry}/test/test_boundingrect.cpp (100%) diff --git a/modules/geometry/include/opencv2/geometry/2d.hpp b/modules/geometry/include/opencv2/geometry/2d.hpp index bc04b7826c..2e4d409705 100644 --- a/modules/geometry/include/opencv2/geometry/2d.hpp +++ b/modules/geometry/include/opencv2/geometry/2d.hpp @@ -806,6 +806,56 @@ CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measu */ CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); +/** @brief Calculates a contour perimeter or a curve length. + * + * The function computes a curve length or a closed contour perimeter. + * + * @param curve Input vector of 2D points, stored in std::vector or Mat. + * @param closed Flag indicating whether the curve is closed or not. + */ +CV_EXPORTS_W double arcLength( InputArray curve, bool closed ); + +/** @brief Calculates a contour area. + * + * The function computes a contour area. Similarly to moments , the area is computed using the Green + * formula. Thus, the returned area and the number of non-zero pixels, if you draw the contour using + * #drawContours or #fillPoly , can be different. Also, the function will most certainly give a wrong + * results for contours with self-intersections. + * + * Example: + * @code + * vector contour; + * contour.push_back(Point2f(0, 0)); + * contour.push_back(Point2f(10, 0)); + * contour.push_back(Point2f(10, 10)); + * contour.push_back(Point2f(5, 4)); + * + * double area0 = contourArea(contour); + * vector approx; + * approxPolyDP(contour, approx, 5, true); + * double area1 = contourArea(approx); + * + * cout << "area0 =" << area0 << endl << + * "area1 =" << area1 << endl << + * "approx poly vertices" << approx.size() << endl; + * @endcode + * @param contour Input vector of 2D points (contour vertices), stored in std::vector or Mat. + * @param oriented Oriented area flag. If it is true, the function returns a signed area value, + * depending on the contour orientation (clockwise or counter-clockwise). Using this feature you can + * determine orientation of a contour by taking the sign of an area. By default, the parameter is + * false, which means that the absolute value is returned. + */ +CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false ); + +/** @brief Calculates the up-right bounding rectangle of a point set or non-zero pixels of gray-scale image. + * + * The function calculates and returns the minimal up-right bounding rectangle for the specified point set or + * non-zero pixels of gray-scale image. + * + * @param array Input gray-scale image or 2D point set, stored in std::vector or Mat. + */ +CV_EXPORTS_W Rect boundingRect( InputArray array ); + } // namespace cv #endif // OPENCV_2D_HPP diff --git a/modules/geometry/misc/java/test/GeometryTest.java b/modules/geometry/misc/java/test/GeometryTest.java index 9e5b123267..64d0ff10a2 100644 --- a/modules/geometry/misc/java/test/GeometryTest.java +++ b/modules/geometry/misc/java/test/GeometryTest.java @@ -10,6 +10,7 @@ import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfDouble; import org.opencv.core.MatOfPoint; +import org.opencv.core.Rect; import org.opencv.core.MatOfPoint2f; import org.opencv.core.MatOfPoint3f; import org.opencv.core.MatOfInt; @@ -733,7 +734,7 @@ public class GeometryTest extends OpenCVTestCase { assertEquals(100.0f, radius[0], 1.0); } - public void testPointPolygonTest() { + public void testPointPolygonTest() { MatOfPoint2f contour = new MatOfPoint2f(new Point(0, 0), new Point(1, 3), new Point(3, 4), new Point(4, 3), new Point(2, 1)); double sign1 = Geometry.pointPolygonTest(contour, new Point(2, 2), false); assertEquals(1.0, sign1); @@ -741,4 +742,42 @@ public class GeometryTest extends OpenCVTestCase { double sign2 = Geometry.pointPolygonTest(contour, new Point(4, 4), true); assertEquals(-Math.sqrt(0.5), sign2); } + + public void testContourAreaMat() { + Mat contour = new Mat(1, 4, CvType.CV_32FC2); + contour.put(0, 0, 0, 0, 10, 0, 10, 10, 5, 4); + + double area = Geometry.contourArea(contour); + + assertEquals(45., area, EPS); + } + + public void testContourAreaMatBoolean() { + Mat contour = new Mat(1, 4, CvType.CV_32FC2); + contour.put(0, 0, 0, 0, 10, 0, 10, 10, 5, 4); + + double area = Geometry.contourArea(contour, true); + + assertEquals(45., area, EPS); + // TODO_: write better test + } + + public void testArcLength() { + MatOfPoint2f curve = new MatOfPoint2f(new Point(1, 3), new Point(2, 4), new Point(3, 5), new Point(4, 4), new Point(5, 3)); + + double arcLength = Geometry.arcLength(curve, false); + + assertEquals(5.656854249, arcLength, 0.000001); + } + + public void testBoundingRect() { + MatOfPoint points = new MatOfPoint(new Point(0, 0), new Point(0, 4), new Point(4, 0), new Point(4, 4)); + Point p1 = new Point(1, 1); + Point p2 = new Point(-5, -2); + + Rect bbox = Geometry.boundingRect(points); + + assertTrue(bbox.contains(p1)); + assertFalse(bbox.contains(p2)); + } } diff --git a/modules/geometry/perf/perf_2d.cpp b/modules/geometry/perf/perf_2d.cpp index 75615a0192..6aee86d7f1 100644 --- a/modules/geometry/perf/perf_2d.cpp +++ b/modules/geometry/perf/perf_2d.cpp @@ -9,6 +9,28 @@ namespace opencv_test { namespace { using namespace perf; +typedef TestBaseWithParam< tuple > TestBoundingRect; + +PERF_TEST_P(TestBoundingRect, BoundingRect, + Combine( + testing::Values(CV_32S, CV_32F), // points type + Values(400, 511, 1000, 10000, 100000) // points count + ) +) + +{ + int ptType = get<0>(GetParam()); + int n = get<1>(GetParam()); + + Mat pts(n, 2, ptType); + declare.in(pts, WARMUP_RNG); + + cv::Rect rect; + TEST_CYCLE() rect = boundingRect(pts); + + SANITY_CHECK_NOTHING(); +} + typedef TestBaseWithParam< tuple > TestMinEnclosingCircle; PERF_TEST_P(TestMinEnclosingCircle, minEnclosingCircle, Combine( diff --git a/modules/geometry/src/geometry.cpp b/modules/geometry/src/geometry.cpp index c83022e1dd..2d8dc9128e 100644 --- a/modules/geometry/src/geometry.cpp +++ b/modules/geometry/src/geometry.cpp @@ -433,6 +433,284 @@ static int intersectConvexConvex_( const Point2f* P, int n, const Point2f* Q, in return nr-1; } +// area of a whole sequence +double contourArea( InputArray _contour, bool oriented ) +{ + CV_INSTRUMENT_REGION(); + + Mat contour = _contour.getMat(); + int npoints = contour.checkVector(2); + int depth = contour.depth(); + CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S)); + + if( npoints == 0 ) + return 0.; + + double a00 = 0; + bool is_float = depth == CV_32F; + const Point* ptsi = contour.ptr(); + const Point2f* ptsf = contour.ptr(); + Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y); + + for( int i = 0; i < npoints; i++ ) + { + Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); + a00 += (double)prev.x * p.y - (double)prev.y * p.x; + prev = p; + } + + a00 *= 0.5; + if( !oriented ) + a00 = fabs(a00); + + return a00; +} + +// calculates length of a curve (e.g. contour perimeter) +double arcLength( InputArray _curve, bool is_closed ) +{ + CV_INSTRUMENT_REGION(); + + Mat curve = _curve.getMat(); + int count = curve.checkVector(2); + int depth = curve.depth(); + CV_Assert( count >= 0 && (depth == CV_32F || depth == CV_32S)); + double perimeter = 0; + + int i; + + if( count <= 1 ) + return 0.; + + bool is_float = depth == CV_32F; + int last = is_closed ? count-1 : 0; + const Point* pti = curve.ptr(); + const Point2f* ptf = curve.ptr(); + + Point2f prev = is_float ? ptf[last] : Point2f((float)pti[last].x,(float)pti[last].y); + + for( i = 0; i < count; i++ ) + { + Point2f p = is_float ? ptf[i] : Point2f((float)pti[i].x,(float)pti[i].y); + float dx = p.x - prev.x, dy = p.y - prev.y; + perimeter += std::sqrt(dx*dx + dy*dy); + + prev = p; + } + + return perimeter; +} + +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 = 0; + bool is_float = depth == CV_32F; + + if( npoints == 0 ) + return Rect(); + + if( !is_float ) + { + const int32_t* pts = points.ptr(); + int64_t firstval = 0; + std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); + xmin = xmax = pts[0]; + ymin = ymax = pts[1]; + #if CV_SIMD || CV_SIMD_SCALABLE + v_int32 minval, maxval; + minval = maxval = v_reinterpret_as_s32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y + const int nlanes = VTraits::vlanes()/2; + for (; i < npoints; i += nlanes) + { + if (i > npoints - nlanes) + { + if (i == 0) + break; + i = npoints - nlanes; + } + v_int32 ptXY2 = vx_load(pts + 2 * i); + minval = v_min(ptXY2, minval); + maxval = v_max(ptXY2, maxval); + } + constexpr int max_nlanes = VTraits::max_nlanes; + int arr_minval[max_nlanes], arr_maxval[max_nlanes]; + vx_store(arr_minval, minval); + vx_store(arr_maxval, maxval); + for (int j = 0; j < nlanes; j++) + { + xmin = std::min(xmin, arr_minval[2*j]); + ymin = std::min(ymin, arr_minval[2*j+1]); + xmax = std::max(xmax, arr_maxval[2*j]); + ymax = std::max(ymax, arr_maxval[2*j+1]); + } + #endif + for( ; i < npoints; i++ ) + { + int pt_x = pts[2*i]; + int pt_y = pts[2*i+1]; + + xmin = std::min(xmin, pt_x); + xmax = std::max(xmax, pt_x); + ymin = std::min(ymin, pt_y); + ymax = std::max(ymax, pt_y); + } + } + else + { + const float* pts = points.ptr(); + int64_t firstval = 0; + std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); + xmin = xmax = cvFloor(pts[0]); + ymin = ymax = cvFloor(pts[1]); + #if CV_SIMD || CV_SIMD_SCALABLE + v_float32 minval, maxval; + minval = maxval = v_reinterpret_as_f32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y + const int nlanes = VTraits::vlanes()/2; + for (; i < npoints; i += nlanes) + { + if (i > npoints - nlanes) + { + if (i == 0) + break; + i = npoints - nlanes; + } + v_float32 ptXY2 = vx_load(pts + 2 * i); + minval = v_min(ptXY2, minval); + maxval = v_max(ptXY2, maxval); + } + constexpr int max_nlanes = VTraits::max_nlanes; + float arr_minval[max_nlanes], arr_maxval[max_nlanes]; + vx_store(arr_minval, minval); + vx_store(arr_maxval, maxval); + for (int j = 0; j < nlanes; j++) + { + int _xmin = cvFloor(arr_minval[2*j]), _ymin = cvFloor(arr_minval[2*j+1]); + int _xmax = cvFloor(arr_maxval[2*j]), _ymax = cvFloor(arr_maxval[2*j+1]); + xmin = std::min(xmin, _xmin); + ymin = std::min(ymin, _ymin); + xmax = std::max(xmax, _xmax); + ymax = std::max(ymax, _ymax); + } + #endif + for( ; i < npoints; i++ ) + { + // 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 + int pt_x = cvFloor(pts[2*i]); + int pt_y = cvFloor(pts[2*i+1]); + + xmin = std::min(xmin, pt_x); + xmax = std::max(xmax, pt_x); + ymin = std::min(ymin, pt_y); + ymax = std::max(ymax, pt_y); + } + } + + return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); +} + +cv::Rect boundingRect(InputArray array) +{ + CV_INSTRUMENT_REGION(); + + Mat m = array.getMat(); + return m.depth() <= CV_8U ? maskBoundingRect(m) : pointSetBoundingRect(m); +} + } float cv::intersectConvexConvex( InputArray _p1, InputArray _p2, OutputArray _p12, bool handleNested ) diff --git a/modules/imgproc/test/test_boundingrect.cpp b/modules/geometry/test/test_boundingrect.cpp similarity index 100% rename from modules/imgproc/test/test_boundingrect.cpp rename to modules/geometry/test/test_boundingrect.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 02ef55dc0c..e6796cb292 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3616,57 +3616,6 @@ CV_EXPORTS_W void findContoursLinkRuns(InputArray image, OutputArrayOfArrays con //! @overload CV_EXPORTS_W void findContoursLinkRuns(InputArray image, OutputArrayOfArrays contours); -/** @brief Calculates the up-right bounding rectangle of a point set or non-zero pixels of gray-scale image. - * - * The function calculates and returns the minimal up-right bounding rectangle for the specified point set or - * non-zero pixels of gray-scale image. - * - * @param array Input gray-scale image or 2D point set, stored in std::vector or Mat. - */ -CV_EXPORTS_W Rect boundingRect( InputArray array ); - -/** @brief Calculates a contour perimeter or a curve length. - -The function computes a curve length or a closed contour perimeter. - -@param curve Input vector of 2D points, stored in std::vector or Mat. -@param closed Flag indicating whether the curve is closed or not. - */ -CV_EXPORTS_W double arcLength( InputArray curve, bool closed ); - -/** @brief Calculates a contour area. - -The function computes a contour area. Similarly to moments , the area is computed using the Green -formula. Thus, the returned area and the number of non-zero pixels, if you draw the contour using -#drawContours or #fillPoly , can be different. Also, the function will most certainly give a wrong -results for contours with self-intersections. - -Example: -@code - vector contour; - contour.push_back(Point2f(0, 0)); - contour.push_back(Point2f(10, 0)); - contour.push_back(Point2f(10, 10)); - contour.push_back(Point2f(5, 4)); - - double area0 = contourArea(contour); - vector approx; - approxPolyDP(contour, approx, 5, true); - double area1 = contourArea(approx); - - cout << "area0 =" << area0 << endl << - "area1 =" << area1 << endl << - "approx poly vertices" << approx.size() << endl; -@endcode -@param contour Input vector of 2D points (contour vertices), stored in std::vector or Mat. -@param oriented Oriented area flag. If it is true, the function returns a signed area value, -depending on the contour orientation (clockwise or counter-clockwise). Using this feature you can -determine orientation of a contour by taking the sign of an area. By default, the parameter is -false, which means that the absolute value is returned. - */ -CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false ); - - /** @brief Creates a smart pointer to a cv::GeneralizedHoughBallard class and initializes it. */ CV_EXPORTS_W Ptr createGeneralizedHoughBallard(); diff --git a/modules/imgproc/misc/java/test/ImgprocTest.java b/modules/imgproc/misc/java/test/ImgprocTest.java index a91ad40dd0..6e2ef437ba 100644 --- a/modules/imgproc/misc/java/test/ImgprocTest.java +++ b/modules/imgproc/misc/java/test/ImgprocTest.java @@ -145,14 +145,6 @@ public class ImgprocTest extends OpenCVTestCase { assertEquals(src.rows(), Core.countNonZero(dst)); } - public void testArcLength() { - MatOfPoint2f curve = new MatOfPoint2f(new Point(1, 3), new Point(2, 4), new Point(3, 5), new Point(4, 4), new Point(5, 3)); - - double arcLength = Imgproc.arcLength(curve, false); - - assertEquals(5.656854249, arcLength, 0.000001); - } - public void testBilateralFilterMatMatIntDoubleDouble() { Imgproc.bilateralFilter(gray255, dst, 5, 10, 5); @@ -188,17 +180,6 @@ public class ImgprocTest extends OpenCVTestCase { // TODO_: write better test } - public void testBoundingRect() { - MatOfPoint points = new MatOfPoint(new Point(0, 0), new Point(0, 4), new Point(4, 0), new Point(4, 4)); - Point p1 = new Point(1, 1); - Point p2 = new Point(-5, -2); - - Rect bbox = Imgproc.boundingRect(points); - - assertTrue(bbox.contains(p1)); - assertFalse(bbox.contains(p2)); - } - public void testBoxFilterMatMatIntSize() { Size size = new Size(3, 3); Imgproc.boxFilter(gray0, dst, 0, size); @@ -347,25 +328,6 @@ public class ImgprocTest extends OpenCVTestCase { assertEquals(1., distance, EPS); } - public void testContourAreaMat() { - Mat contour = new Mat(1, 4, CvType.CV_32FC2); - contour.put(0, 0, 0, 0, 10, 0, 10, 10, 5, 4); - - double area = Imgproc.contourArea(contour); - - assertEquals(45., area, EPS); - } - - public void testContourAreaMatBoolean() { - Mat contour = new Mat(1, 4, CvType.CV_32FC2); - contour.put(0, 0, 0, 0, 10, 0, 10, 10, 5, 4); - - double area = Imgproc.contourArea(contour, true); - - assertEquals(45., area, EPS); - // TODO_: write better test - } - public void testConvertMapsMatMatMatMatInt() { Mat map1 = new Mat(1, 4, CvType.CV_32FC1, new Scalar(1)); Mat map2 = new Mat(1, 4, CvType.CV_32FC1, new Scalar(2)); diff --git a/modules/imgproc/perf/perf_contours.cpp b/modules/imgproc/perf/perf_contours.cpp index 5493a8b70d..307d20bcac 100644 --- a/modules/imgproc/perf/perf_contours.cpp +++ b/modules/imgproc/perf/perf_contours.cpp @@ -84,28 +84,6 @@ PERF_TEST_P(TestFindContoursFF, findContours, SANITY_CHECK_NOTHING(); } -typedef TestBaseWithParam< tuple > TestBoundingRect; - -PERF_TEST_P(TestBoundingRect, BoundingRect, - Combine( - testing::Values(CV_32S, CV_32F), // points type - Values(400, 511, 1000, 10000, 100000) // points count - ) -) - -{ - int ptType = get<0>(GetParam()); - int n = get<1>(GetParam()); - - Mat pts(n, 2, ptType); - declare.in(pts, WARMUP_RNG); - - cv::Rect rect; - TEST_CYCLE() rect = boundingRect(pts); - - SANITY_CHECK_NOTHING(); -} - // ============================================================ // findTRUContours performance tests // ============================================================ diff --git a/modules/imgproc/src/contours_common.cpp b/modules/imgproc/src/contours_common.cpp index 1e85f1f571..325df8e55a 100644 --- a/modules/imgproc/src/contours_common.cpp +++ b/modules/imgproc/src/contours_common.cpp @@ -12,284 +12,6 @@ using namespace std; using namespace cv; -// calculates length of a curve (e.g. contour perimeter) -double cv::arcLength( InputArray _curve, bool is_closed ) -{ - CV_INSTRUMENT_REGION(); - - Mat curve = _curve.getMat(); - int count = curve.checkVector(2); - int depth = curve.depth(); - CV_Assert( count >= 0 && (depth == CV_32F || depth == CV_32S)); - double perimeter = 0; - - int i; - - if( count <= 1 ) - return 0.; - - bool is_float = depth == CV_32F; - int last = is_closed ? count-1 : 0; - const Point* pti = curve.ptr(); - const Point2f* ptf = curve.ptr(); - - Point2f prev = is_float ? ptf[last] : Point2f((float)pti[last].x,(float)pti[last].y); - - for( i = 0; i < count; i++ ) - { - Point2f p = is_float ? ptf[i] : Point2f((float)pti[i].x,(float)pti[i].y); - float dx = p.x - prev.x, dy = p.y - prev.y; - perimeter += std::sqrt(dx*dx + dy*dy); - - prev = p; - } - - return perimeter; -} - -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 = 0; - bool is_float = depth == CV_32F; - - if( npoints == 0 ) - return Rect(); - - if( !is_float ) - { - const int32_t* pts = points.ptr(); - int64_t firstval = 0; - std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); - xmin = xmax = pts[0]; - ymin = ymax = pts[1]; - #if CV_SIMD || CV_SIMD_SCALABLE - v_int32 minval, maxval; - minval = maxval = v_reinterpret_as_s32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y - const int nlanes = VTraits::vlanes()/2; - for (; i < npoints; i += nlanes) - { - if (i > npoints - nlanes) - { - if (i == 0) - break; - i = npoints - nlanes; - } - v_int32 ptXY2 = vx_load(pts + 2 * i); - minval = v_min(ptXY2, minval); - maxval = v_max(ptXY2, maxval); - } - constexpr int max_nlanes = VTraits::max_nlanes; - int arr_minval[max_nlanes], arr_maxval[max_nlanes]; - vx_store(arr_minval, minval); - vx_store(arr_maxval, maxval); - for (int j = 0; j < nlanes; j++) - { - xmin = std::min(xmin, arr_minval[2*j]); - ymin = std::min(ymin, arr_minval[2*j+1]); - xmax = std::max(xmax, arr_maxval[2*j]); - ymax = std::max(ymax, arr_maxval[2*j+1]); - } - #endif - for( ; i < npoints; i++ ) - { - int pt_x = pts[2*i]; - int pt_y = pts[2*i+1]; - - xmin = std::min(xmin, pt_x); - xmax = std::max(xmax, pt_x); - ymin = std::min(ymin, pt_y); - ymax = std::max(ymax, pt_y); - } - } - else - { - const float* pts = points.ptr(); - int64_t firstval = 0; - std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); - xmin = xmax = cvFloor(pts[0]); - ymin = ymax = cvFloor(pts[1]); - #if CV_SIMD || CV_SIMD_SCALABLE - v_float32 minval, maxval; - minval = maxval = v_reinterpret_as_f32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y - const int nlanes = VTraits::vlanes()/2; - for (; i < npoints; i += nlanes) - { - if (i > npoints - nlanes) - { - if (i == 0) - break; - i = npoints - nlanes; - } - v_float32 ptXY2 = vx_load(pts + 2 * i); - minval = v_min(ptXY2, minval); - maxval = v_max(ptXY2, maxval); - } - constexpr int max_nlanes = VTraits::max_nlanes; - float arr_minval[max_nlanes], arr_maxval[max_nlanes]; - vx_store(arr_minval, minval); - vx_store(arr_maxval, maxval); - for (int j = 0; j < nlanes; j++) - { - int _xmin = cvFloor(arr_minval[2*j]), _ymin = cvFloor(arr_minval[2*j+1]); - int _xmax = cvFloor(arr_maxval[2*j]), _ymax = cvFloor(arr_maxval[2*j+1]); - xmin = std::min(xmin, _xmin); - ymin = std::min(ymin, _ymin); - xmax = std::max(xmax, _xmax); - ymax = std::max(ymax, _ymax); - } - #endif - for( ; i < npoints; i++ ) - { - // 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 - int pt_x = cvFloor(pts[2*i]); - int pt_y = cvFloor(pts[2*i+1]); - - xmin = std::min(xmin, pt_x); - xmax = std::max(xmax, pt_x); - ymin = std::min(ymin, pt_y); - ymax = std::max(ymax, pt_y); - } - } - - 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); -} - -// area of a whole sequence -double cv::contourArea( InputArray _contour, bool oriented ) -{ - CV_INSTRUMENT_REGION(); - - Mat contour = _contour.getMat(); - int npoints = contour.checkVector(2); - int depth = contour.depth(); - CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S)); - - if( npoints == 0 ) - return 0.; - - double a00 = 0; - bool is_float = depth == CV_32F; - const Point* ptsi = contour.ptr(); - const Point2f* ptsf = contour.ptr(); - Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y); - - for( int i = 0; i < npoints; i++ ) - { - Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y); - a00 += (double)prev.x * p.y - (double)prev.y * p.x; - prev = p; - } - - a00 *= 0.5; - if( !oriented ) - a00 = fabs(a00); - - return a00; -} - void cv::contourTreeToResults(CTree& tree, int res_type, OutputArrayOfArrays& _contours, diff --git a/modules/imgproc/test/test_moments.cpp b/modules/imgproc/test/test_moments.cpp index 0f1d0344dd..55a49afd9b 100644 --- a/modules/imgproc/test/test_moments.cpp +++ b/modules/imgproc/test/test_moments.cpp @@ -60,9 +60,9 @@ protected: points.push_back(Point(49, 51)); Moments m = moments(points, false); - double area = contourArea(points); + // double area = contourArea(points); - CV_Assert( m.m00 == 0 && m.m01 == 0 && m.m10 == 0 && area == 0 ); + CV_Assert( m.m00 == 0 && m.m01 == 0 && m.m10 == 0 /*&& area == 0*/ ); } catch(...) { diff --git a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetector.java b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetector.java index 21988a81a1..2d65337746 100644 --- a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetector.java +++ b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetector.java @@ -10,6 +10,7 @@ import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.Scalar; import org.opencv.imgproc.Imgproc; +import org.opencv.geometry.Geometry; public class ColorBlobDetector { // Lower and Upper bounds for range checking in HSV color space @@ -85,7 +86,7 @@ public class ColorBlobDetector { Iterator each = contours.iterator(); while (each.hasNext()) { MatOfPoint wrapper = each.next(); - double area = Imgproc.contourArea(wrapper); + double area = Geometry.contourArea(wrapper); if (area > maxArea) maxArea = area; } @@ -95,7 +96,7 @@ public class ColorBlobDetector { each = contours.iterator(); while (each.hasNext()) { MatOfPoint contour = each.next(); - if (Imgproc.contourArea(contour) > mMinContourArea*maxArea) { + if (Geometry.contourArea(contour) > mMinContourArea*maxArea) { Core.multiply(contour, new Scalar(4,4), contour); mContours.add(contour); } diff --git a/samples/cpp/snippets/segment_objects.cpp b/samples/cpp/snippets/segment_objects.cpp index 2bcddddf85..c09be20b00 100644 --- a/samples/cpp/snippets/segment_objects.cpp +++ b/samples/cpp/snippets/segment_objects.cpp @@ -1,4 +1,5 @@ #include "opencv2/imgproc.hpp" +#include "opencv2/geometry.hpp" #include "opencv2/videoio.hpp" #include "opencv2/highgui.hpp" #include "opencv2/video/background_segm.hpp" diff --git a/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp b/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp index 9b243ca53c..26160d7b59 100644 --- a/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp +++ b/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp @@ -7,6 +7,7 @@ #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/imgproc.hpp" +#include "opencv2/geometry.hpp" #include #include diff --git a/samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp b/samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp index c0ca672436..ba82f40635 100644 --- a/samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp +++ b/samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp @@ -6,6 +6,7 @@ #include "opencv2/core.hpp" #include "opencv2/imgproc.hpp" +#include "opencv2/geometry.hpp" #include "opencv2/highgui.hpp" #include