From 78adab13e9de0a6c532d2103f2e9b7ff3d2f5caf Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Wed, 12 Nov 2025 23:44:41 +0800 Subject: [PATCH] Merge pull request #27479 from fengyuentau:4x/imgproc/boundingRect-simd imgproc: supports CV_SIMD_SCALABLE in pointSetBoundingRect #27479 ### 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 - [ ] 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 --- modules/imgproc/src/geometry.cpp | 185 ++++++++++--------------------- 1 file changed, 61 insertions(+), 124 deletions(-) diff --git a/modules/imgproc/src/geometry.cpp b/modules/imgproc/src/geometry.cpp index c7a52b2f46..020a636835 100644 --- a/modules/imgproc/src/geometry.cpp +++ b/modules/imgproc/src/geometry.cpp @@ -710,171 +710,108 @@ static Rect pointSetBoundingRect( const Mat& points ) int depth = points.depth(); CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S)); - int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i; + int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i = 0; bool is_float = depth == CV_32F; if( npoints == 0 ) return Rect(); -#if CV_SIMD // TODO: enable for CV_SIMD_SCALABLE, loop tail related. 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 - for( i = 1; i <= npoints - VTraits::vlanes()/2; i+= VTraits::vlanes()/2 ) + 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); } - 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::vlanes()/4 ) + 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++) { - v_int32 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + 2 * i)))); - minval = v_min(ptXY, minval); - maxval = v_max(ptXY, maxval); - i += VTraits::vlanes()/2; + 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]); } - for(int j = 16; j < VTraits::vlanes(); j*=2) +#endif + for( ; i < npoints; i++ ) { - 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)))); + 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); } - 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 + 2 * i)))); - for( i++; i < npoints; i++ ) - { - v_int32x4 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * 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 { 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 - for( i = 1; i <= npoints - VTraits::vlanes()/2; i+= VTraits::vlanes()/2 ) + 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); } - 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::vlanes()/4 ) + 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++) { - v_float32 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + 2 * i)))); - minval = v_min(ptXY, minval); - maxval = v_max(ptXY, maxval); - i += VTraits::vlanes()/4; - } - for(int j = 16; j < VTraits::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 + 2 * i)))); - for( i++; i < npoints; i++ ) - { - v_float32x4 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * 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)))))); + 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 - } -#else - const Point* pts = points.ptr(); - Point pt = pts[0]; - - if( !is_float ) - { - xmin = xmax = pt.x; - ymin = ymax = pt.y; - - for( i = 1; i < npoints; i++ ) + for( ; i < npoints; i++ ) { - pt = pts[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]); - 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; + 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 - { - 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); }