Skip to content

Commit 17398ec

Browse files
committed
Add resizeDown API, performance tests, and accuracy tests
1 parent eebd897 commit 17398ec

File tree

4 files changed

+151
-94
lines changed

4 files changed

+151
-94
lines changed

modules/fastcv/include/opencv2/fastcv/scale.hpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
2+
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -15,20 +15,15 @@ namespace fastcv {
1515
//! @{
1616

1717
/**
18-
* @brief Down-scale the image by averaging each 2x2 pixel block.
19-
* This function is not bit-exact with cv::resize but provides faster execution time on Qualcomm's processor.
20-
* @param _src The first input image data, type CV_8UC1, src height must be a multiple of 2
21-
* @param _dst The output image data, type CV_8UC1
22-
*/
23-
CV_EXPORTS_W void resizeDownBy2(cv::InputArray _src, cv::OutputArray _dst);
24-
25-
/**
26-
* @brief Down-scale the image by averaging each 4x4 pixel block.
27-
* This function is not bit-exact with cv::resize but provides faster execution time on Qualcomm's processor.
28-
* @param _src The first input image data, type CV_8UC1, src height must be a multiple of 4
29-
* @param _dst The output image data, type CV_8UC1
30-
*/
31-
CV_EXPORTS_W void resizeDownBy4(cv::InputArray _src, cv::OutputArray _dst);
18+
* @brief Down-scales the image using specified scaling factors or dimensions.
19+
* This function supports both single-channel (CV_8UC1) and two-channel (CV_8UC2) images.
20+
* @param _src The input image data, type CV_8UC1 or CV_8UC2.
21+
* @param _dst The output image data, type CV_8UC1 or CV_8UC2.
22+
* @param dsize The desired size of the output image. If empty, it is calculated using inv_scale_x and inv_scale_y.
23+
* @param inv_scale_x The inverse scaling factor for the width. If dsize is provided, this parameter is ignored.
24+
* @param inv_scale_y The inverse scaling factor for the height. If dsize is provided, this parameter is ignored.
25+
*/
26+
CV_EXPORTS_W void resizeDown(cv::InputArray _src, cv::OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y);
3227

3328
//! @}
3429

modules/fastcv/perf/perf_scale.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "perf_precomp.hpp"
7+
8+
namespace opencv_test {
9+
10+
typedef perf::TestBaseWithParam<std::tuple<Size, int>> ResizePerfTest;
11+
12+
PERF_TEST_P(ResizePerfTest, run, ::testing::Combine(
13+
::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
14+
::testing::Values(2, 4) // resize factor
15+
))
16+
{
17+
Size size = std::get<0>(GetParam());
18+
int factor = std::get<1>(GetParam());
19+
20+
cv::Mat inputImage(size, CV_8UC1);
21+
cv::randu(inputImage, cv::Scalar::all(0), cv::Scalar::all(255));
22+
23+
cv::Mat resized_image;
24+
Size dsize(inputImage.cols / factor, inputImage.rows / factor);
25+
26+
while (next())
27+
{
28+
startTimer();
29+
cv::fastcv::resizeDown(inputImage, resized_image, dsize, 0, 0);
30+
stopTimer();
31+
}
32+
33+
SANITY_CHECK_NOTHING();
34+
}
35+
36+
typedef perf::TestBaseWithParam<std::tuple<Size, double, double, int>> ResizeByMnPerfTest;
37+
38+
PERF_TEST_P(ResizeByMnPerfTest, run, ::testing::Combine(
39+
::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
40+
::testing::Values(0.35, 0.65), // inv_scale_x
41+
::testing::Values(0.35, 0.65), // inv_scale_y
42+
::testing::Values(CV_8UC1, CV_8UC2) // data type
43+
))
44+
{
45+
Size size = std::get<0>(GetParam());
46+
double inv_scale_x = std::get<1>(GetParam());
47+
double inv_scale_y = std::get<2>(GetParam());
48+
int type = std::get<3>(GetParam());
49+
50+
cv::Mat inputImage(size, type);
51+
cv::randu(inputImage, cv::Scalar::all(0), cv::Scalar::all(255));
52+
53+
Size dsize;
54+
cv::Mat resized_image;
55+
56+
while (next())
57+
{
58+
startTimer();
59+
cv::fastcv::resizeDown(inputImage, resized_image, dsize, inv_scale_x, inv_scale_y);
60+
stopTimer();
61+
}
62+
63+
SANITY_CHECK_NOTHING();
64+
}
65+
66+
} // namespace

modules/fastcv/src/scale.cpp

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
2+
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -8,55 +8,68 @@
88
namespace cv {
99
namespace fastcv {
1010

11-
void resizeDownBy2(cv::InputArray _src, cv::OutputArray _dst)
11+
void resizeDown(cv::InputArray _src, cv::OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y)
1212
{
13-
INITIALIZATION_CHECK;
14-
15-
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
16-
17-
Mat src = _src.getMat();
18-
CV_Assert((src.cols & 1)==0 && (src.rows & 1)==0);
19-
20-
int type = _src.type();
21-
cv::Size dsize(src.cols / 2, src.rows / 2);
22-
23-
_dst.create(dsize, type);
24-
25-
Mat dst = _dst.getMat();
13+
fcvStatus status = FASTCV_SUCCESS;
14+
Size ssize = _src.size();
2615

27-
fcvStatus status = (fcvStatus)fcvScaleDownBy2u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data,
28-
src.cols/2);
16+
CV_Assert(!_src.empty() );
17+
CV_Assert( _src.type() == CV_8UC1 || _src.type() == CV_8UC2 );
2918

30-
if (status != FASTCV_SUCCESS)
19+
if( dsize.empty() )
3120
{
32-
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
33-
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
21+
CV_Assert(inv_scale_x > 0);
22+
CV_Assert(inv_scale_y > 0);
23+
dsize = Size(saturate_cast<int>(ssize.width*inv_scale_x),
24+
saturate_cast<int>(ssize.height*inv_scale_y));
25+
CV_Assert( !dsize.empty() );
26+
}
27+
else
28+
{
29+
inv_scale_x = static_cast<double>(dsize.width) / ssize.width;
30+
inv_scale_y = static_cast<double>(dsize.height) / ssize.height;
31+
CV_Assert(inv_scale_x > 0);
32+
CV_Assert(inv_scale_y > 0);
3433
}
35-
}
36-
37-
void resizeDownBy4(cv::InputArray _src, cv::OutputArray _dst)
38-
{
39-
INITIALIZATION_CHECK;
40-
41-
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
4234

43-
Mat src = _src.getMat();
44-
CV_Assert((src.cols & 3)==0 && (src.rows & 3)==0);
35+
CV_Assert(dsize.width <= ssize.width && dsize.height <= ssize.height);
4536

46-
int type = _src.type();
47-
cv::Size dsize(src.cols / 4, src.rows / 4);
37+
CV_Assert(dsize.width * 20 > ssize.width);
38+
CV_Assert(dsize.height * 20 > ssize.height);
4839

49-
_dst.create(dsize, type);
40+
INITIALIZATION_CHECK;
5041

42+
Mat src = _src.getMat();
43+
_dst.create(dsize, src.type());
5144
Mat dst = _dst.getMat();
5245

53-
fcvStatus status = (fcvStatus)fcvScaleDownBy4u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step,
54-
(uint8_t*)dst.data, src.cols/4);
46+
// Alignment checks
47+
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 16 == 0);
48+
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 16 == 0);
49+
50+
if(src.type() == CV_8UC2)
51+
{
52+
fcvScaleDownMNInterleaveu8((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.cols, dst.rows, dst.step);
53+
}
54+
else if (src.cols/dst.cols == 4 && src.rows/dst.rows == 4 && src.cols % dst.cols == 0 && src.rows % dst.rows == 0)
55+
{
56+
CV_Assert(src.rows % 4 == 0);
57+
status = (fcvStatus)fcvScaleDownBy4u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.step);
58+
}
59+
else if (src.cols/dst.cols == 2 && src.rows/dst.rows == 2 && src.cols % dst.cols == 0 && src.rows % dst.rows == 0)
60+
{
61+
CV_Assert(src.rows % 2 == 0);
62+
status = (fcvStatus)fcvScaleDownBy2u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.step);
63+
}
64+
else
65+
{
66+
fcvScaleDownMNu8((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.cols, dst.rows, dst.step);
67+
}
5568

5669
if (status != FASTCV_SUCCESS)
5770
{
5871
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
59-
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
72+
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
6073
}
6174
}
6275

modules/fastcv/test/test_scale.cpp

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,27 @@
11
/*
2-
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
2+
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
#include "test_precomp.hpp"
77

88
namespace opencv_test { namespace {
99

10-
class ResizeBy2Test : public ::testing::TestWithParam<cv::Size> {};
11-
class ResizeBy4Test : public ::testing::TestWithParam<cv::Size> {};
12-
1310
TEST(resizeDownBy2, accuracy)
1411
{
1512
cv::Mat inputImage = cv::imread(cvtest::findDataFile("cv/shared/box_in_scene.png"), cv::IMREAD_GRAYSCALE);
1613

17-
Size dsize;
1814
cv::Mat resized_image;
1915

20-
cv::fastcv::resizeDownBy2(inputImage, resized_image);
16+
cv::fastcv::resizeDown(inputImage, resized_image, cv::Size(inputImage.cols / 2, inputImage.rows / 2), 0, 0);
2117

2218
EXPECT_FALSE(resized_image.empty());
2319

2420
cv::Mat resizedImageOpenCV;
2521
cv::resize(inputImage, resizedImageOpenCV, cv::Size(inputImage.cols / 2, inputImage.rows / 2), 0, 0, INTER_AREA);
2622

27-
// Calculate the maximum difference
2823
double maxVal = cv::norm(resized_image, resizedImageOpenCV, cv::NORM_INF);
2924

30-
// Assert if the difference is acceptable (max difference should be less than 10)
3125
CV_Assert(maxVal < 10 && "Difference between images is too high!");
3226
}
3327

@@ -38,67 +32,56 @@ TEST(resizeDownBy4, accuracy)
3832
Size dsize;
3933
cv::Mat resized_image;
4034

41-
cv::fastcv::resizeDownBy4(inputImage, resized_image);
35+
cv::fastcv::resizeDown(inputImage, resized_image, dsize, 0.25, 0.25);
4236

4337
EXPECT_FALSE(resized_image.empty());
4438

4539
cv::Mat resizedImageOpenCV;
4640
cv::resize(inputImage, resizedImageOpenCV, cv::Size(inputImage.cols / 4, inputImage.rows / 4), 0, 0, INTER_AREA);
4741

48-
// Calculate the maximum difference
4942
double maxVal = cv::norm(resized_image, resizedImageOpenCV, cv::NORM_INF);
5043

51-
// Assert if the difference is acceptable (max difference should be less than 10)
5244
CV_Assert(maxVal < 10 && "Difference between images is too high!");
5345
}
5446

55-
TEST_P(ResizeBy2Test, ResizeBy2) {
56-
57-
//Size size = get<0>(GetParam());
58-
Size size = GetParam();
59-
cv::Mat inputImage(size, CV_8UC1);
60-
randu(inputImage, Scalar::all(0), Scalar::all(255)); // Fill with random values
47+
TEST(resizeDownMN, accuracy)
48+
{
49+
cv::Mat inputImage = cv::imread(cvtest::findDataFile("cv/cascadeandhog/images/class57.png"), cv::IMREAD_GRAYSCALE);
6150

62-
Size dsize;
6351
cv::Mat resized_image;
6452

65-
// Resize the image by a factor of 2
66-
cv::fastcv::resizeDownBy2(inputImage, resized_image);
53+
cv::fastcv::resizeDown(inputImage, resized_image, cv::Size(800, 640), 0, 0);
6754

68-
// Check if the output size is correct
69-
EXPECT_EQ(resized_image.size().width, size.width * 0.5);
70-
EXPECT_EQ(resized_image.size().height, size.height * 0.5);
55+
EXPECT_FALSE(resized_image.empty());
56+
57+
cv::Mat resizedImageOpenCV;
58+
cv::resize(inputImage, resizedImageOpenCV, cv::Size(800, 640), 0, 0, INTER_LINEAR);
59+
60+
double maxVal = cv::norm(resized_image, resizedImageOpenCV, cv::NORM_INF);
61+
62+
CV_Assert(maxVal < 78 && "Difference between images is too high!");
7163
}
7264

73-
TEST_P(ResizeBy4Test, ResizeBy4) {
65+
TEST(resizeDownInterleaved, accuracy)
66+
{
67+
cv::Mat inputImage = cv::Mat::zeros(512, 512, CV_8UC2);
68+
cv::randu(inputImage, cv::Scalar(0), cv::Scalar(255));
7469

75-
//Size size = get<0>(GetParam());
76-
Size size = GetParam();
77-
cv::Mat inputImage(size, CV_8UC1);
78-
randu(inputImage, Scalar::all(0), Scalar::all(255)); // Fill with random values
7970

8071
Size dsize;
8172
cv::Mat resized_image;
8273

83-
// Resize the image by a factor of 4
84-
cv::fastcv::resizeDownBy4(inputImage, resized_image);
74+
cv::fastcv::resizeDown(inputImage, resized_image, dsize, 0.500, 0.125);
8575

86-
// Check if the output size is correct
87-
EXPECT_EQ(resized_image.size().width, size.width * 0.25);
88-
EXPECT_EQ(resized_image.size().height, size.height * 0.25);
89-
}
76+
EXPECT_FALSE(resized_image.empty());
9077

91-
INSTANTIATE_TEST_CASE_P(
92-
ResizeTests,
93-
ResizeBy2Test,
94-
::testing::Values(cv::Size(640, 480), cv::Size(1280, 720), cv::Size(1920, 1080)
95-
));
9678

97-
INSTANTIATE_TEST_CASE_P(
98-
ResizeTests,
99-
ResizeBy4Test,
100-
::testing::Values(cv::Size(640, 480), cv::Size(1280, 720), cv::Size(1920, 1080)
101-
));
79+
cv::Mat resizedImageOpenCV;
80+
cv::resize(inputImage, resizedImageOpenCV, dsize, 0.500, 0.125, INTER_AREA);
10281

82+
double maxVal = cv::norm(resized_image, resizedImageOpenCV, cv::NORM_INF);
83+
84+
CV_Assert(maxVal < 10 && "Difference between images is too high!");
85+
}
10386

10487
}} // namespaces opencv_test, ::

0 commit comments

Comments
 (0)