Skip to content

Commit 1e4d4e0

Browse files
Merge pull request #3936 from CodeLinaro:apreetam_6thPost
Add warpAffine and resizeDown APIs in FastCV Extension #3936 - Added warpAffine function to apply affine transformations. 2x3 affine transformations for both CV_8UC1 and CV_8UC3 input 2x2 matrix-based patch extraction for grayscale images, with ROI. - Deprecated resizeDownBy2 and resizeDownBy4 functions. - Introduced resizeDown function to down-scale images using specified scaling factors or dimensions, supporting both single-channel (CV_8UC1) and two-channel (CV_8UC2) images. ### 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 - [ ] 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
1 parent 6e8ce30 commit 1e4d4e0

File tree

8 files changed

+575
-94
lines changed

8 files changed

+575
-94
lines changed

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

Lines changed: 13 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,18 @@ 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+
*
21+
* @param _src The input image data, type CV_8UC1 or CV_8UC2.
22+
* @param _dst The output image data, type CV_8UC1 or CV_8UC2.
23+
* @param dsize The desired size of the output image. If empty, it is calculated using inv_scale_x and inv_scale_y.
24+
* @param inv_scale_x The inverse scaling factor for the width. If dsize is provided, this parameter is ignored.
25+
* @param inv_scale_y The inverse scaling factor for the height. If dsize is provided, this parameter is ignored.
26+
*
27+
* @note If dsize is not specified, inv_scale_x and inv_scale_y must be strictly positive.
28+
*/
29+
CV_EXPORTS_W void resizeDown(cv::InputArray _src, cv::OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y);
3230

3331
//! @}
3432

modules/fastcv/include/opencv2/fastcv/warp.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,46 @@ CV_EXPORTS_W void warpPerspective(InputArray _src, OutputArray _dst, InputArray
4444
CV_EXPORTS_W void warpPerspective2Plane(InputArray _src1, InputArray _src2, OutputArray _dst1, OutputArray _dst2,
4545
InputArray _M0, Size dsize);
4646

47+
/**
48+
* @brief Performs an affine transformation on an input image using a provided transformation matrix.
49+
*
50+
* This function performs two types of operations based on the transformation matrix:
51+
*
52+
* 1. Standard Affine Transformation (2x3 matrix):
53+
* - Transforms the entire input image using the affine matrix
54+
* - Supports both CV_8UC1 and CV_8UC3 types
55+
*
56+
* 2. Patch Extraction with Transformation (2x2 matrix):
57+
* - Extracts and transforms a patch from the input image
58+
* - Only supports CV_8UC1 type
59+
* - If input is a ROI: patch is extracted from ROI center in the original image
60+
* - If input is full image: patch is extracted from image center
61+
*
62+
* @param _src Input image. Supported formats:
63+
* - CV_8UC1: 8-bit single-channel
64+
* - CV_8UC3: 8-bit three-channel - only for 2x3 matrix
65+
* @param _dst Output image. Will have the same type as src and size specified by dsize
66+
* @param _M 2x2/2x3 affine transformation matrix (inversed map)
67+
* @param dsize Output size:
68+
* - For 2x3 matrix: Size of the output image
69+
* - For 2x2 matrix: Size of the extracted patch
70+
* @param interpolation Interpolation method. Only applicable for 2x3 transformation with CV_8UC1 input.
71+
* Options:
72+
* - INTER_NEAREST: Nearest-neighbor interpolation
73+
* - INTER_LINEAR: Bilinear interpolation (default)
74+
* - INTER_AREA: Area-based interpolation
75+
* - INTER_CUBIC: Bicubic interpolation
76+
* Note: CV_8UC3 input always use bicubic interpolation internally
77+
* @param borderValue Constant pixel value for border pixels. Only applicable for 2x3 transformations
78+
* with single-channel input.
79+
*
80+
* @note The affine matrix follows the inverse mapping convention, applied to destination coordinates
81+
* to produce corresponding source coordinates.
82+
* @note The function uses 'FASTCV_BORDER_CONSTANT' for border handling, with the specified 'borderValue'.
83+
*/
84+
CV_EXPORTS_W void warpAffine(InputArray _src, OutputArray _dst, InputArray _M, Size dsize, int interpolation = INTER_LINEAR,
85+
int borderValue = 0);
86+
4787
//! @}
4888

4989
}

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/perf/perf_warp.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,29 @@ static void getInvertMatrix(Mat& src, Size dstSize, Mat& M)
3939
invert(M,M);
4040
}
4141

42+
static cv::Mat getInverseAffine(const cv::Mat& affine)
43+
{
44+
// Extract the 2x2 part
45+
cv::Mat rotationScaling = affine(cv::Rect(0, 0, 2, 2));
46+
47+
// Invert the 2x2 part
48+
cv::Mat inverseRotationScaling;
49+
cv::invert(rotationScaling, inverseRotationScaling);
50+
51+
// Extract the translation part
52+
cv::Mat translation = affine(cv::Rect(2, 0, 1, 2));
53+
54+
// Compute the new translation
55+
cv::Mat inverseTranslation = -inverseRotationScaling * translation;
56+
57+
// Construct the inverse affine matrix
58+
cv::Mat inverseAffine = cv::Mat::zeros(2, 3, CV_32F);
59+
inverseRotationScaling.copyTo(inverseAffine(cv::Rect(0, 0, 2, 2)));
60+
inverseTranslation.copyTo(inverseAffine(cv::Rect(2, 0, 1, 2)));
61+
62+
return inverseAffine;
63+
}
64+
4265
typedef perf::TestBaseWithParam<Size> WarpPerspective2PlanePerfTest;
4366

4467
PERF_TEST_P(WarpPerspective2PlanePerfTest, run,
@@ -93,4 +116,121 @@ PERF_TEST_P(WarpPerspectivePerfTest, run,
93116
SANITY_CHECK_NOTHING();
94117
}
95118

119+
typedef TestBaseWithParam< tuple<MatType, Size> > WarpAffine3ChannelPerf;
120+
121+
PERF_TEST_P(WarpAffine3ChannelPerf, run, Combine(
122+
Values(CV_8UC3),
123+
Values( szVGA, sz720p, sz1080p)
124+
))
125+
{
126+
Size sz, szSrc(512, 512);
127+
int dataType;
128+
dataType = get<0>(GetParam());
129+
sz = get<1>(GetParam());
130+
131+
cv::Mat src(szSrc, dataType), dst(sz, dataType);
132+
133+
cvtest::fillGradient(src);
134+
135+
//Affine matrix
136+
float angle = 30.0; // Rotation angle in degrees
137+
float scale = 2.2; // Scale factor
138+
cv::Mat affine = cv::getRotationMatrix2D(cv::Point2f(100, 100), angle, scale);
139+
140+
// Compute the inverse affine matrix
141+
cv::Mat inverseAffine = getInverseAffine(affine);
142+
143+
// Create the dstBorder array
144+
Mat dstBorder;
145+
146+
declare.in(src).out(dst);
147+
148+
while (next())
149+
{
150+
startTimer();
151+
cv::fastcv::warpAffine(src, dst, inverseAffine, sz);
152+
stopTimer();
153+
}
154+
155+
SANITY_CHECK_NOTHING();
156+
}
157+
158+
typedef perf::TestBaseWithParam<std::tuple<cv::Size, cv::Point2f, cv::Mat>> WarpAffineROIPerfTest;
159+
160+
PERF_TEST_P(WarpAffineROIPerfTest, run, ::testing::Combine(
161+
::testing::Values(cv::Size(50, 50), cv::Size(100, 100)), // patch size
162+
::testing::Values(cv::Point2f(50.0f, 50.0f), cv::Point2f(100.0f, 100.0f)), // position
163+
::testing::Values((cv::Mat_<float>(2, 2) << 1, 0, 0, 1), // identity matrix
164+
(cv::Mat_<float>(2, 2) << cos(CV_PI), -sin(CV_PI), sin(CV_PI), cos(CV_PI))) // rotation matrix
165+
))
166+
{
167+
cv::Size patchSize = std::get<0>(GetParam());
168+
cv::Point2f position = std::get<1>(GetParam());
169+
cv::Mat affine = std::get<2>(GetParam());
170+
171+
cv::Mat src = cv::imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
172+
173+
// Create ROI with top-left at the specified position
174+
cv::Rect roiRect(static_cast<int>(position.x), static_cast<int>(position.y), patchSize.width, patchSize.height);
175+
176+
// Ensure ROI is within image bounds
177+
roiRect = roiRect & cv::Rect(0, 0, src.cols, src.rows);
178+
cv::Mat roi = src(roiRect);
179+
180+
cv::Mat patch;
181+
182+
while (next())
183+
{
184+
startTimer();
185+
cv::fastcv::warpAffine(roi, patch, affine, patchSize);
186+
stopTimer();
187+
}
188+
189+
SANITY_CHECK_NOTHING();
190+
}
191+
192+
typedef TestBaseWithParam<tuple<int, int> > WarpAffinePerfTest;
193+
194+
PERF_TEST_P(WarpAffinePerfTest, run, ::testing::Combine(
195+
::testing::Values(cv::InterpolationFlags::INTER_NEAREST, cv::InterpolationFlags::INTER_LINEAR, cv::InterpolationFlags::INTER_AREA),
196+
::testing::Values(0, 255) // Black and white borders
197+
))
198+
{
199+
// Load the source image
200+
cv::Mat src = cv::imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
201+
ASSERT_FALSE(src.empty());
202+
203+
// Generate random values for the affine matrix
204+
std::srand(std::time(0));
205+
float angle = static_cast<float>(std::rand() % 360); // Random angle between 0 and 360 degrees
206+
float scale = static_cast<float>(std::rand() % 200) / 100.0f + 0.5f; // Random scale between 0.5 and 2.5
207+
float tx = static_cast<float>(std::rand() % 100) - 50; // Random translation between -50 and 50
208+
float ty = static_cast<float>(std::rand() % 100) - 50; // Random translation between -50 and 50
209+
float radians = angle * CV_PI / 180.0;
210+
cv::Mat affine = (cv::Mat_<float>(2, 3) << scale * cos(radians), -scale * sin(radians), tx,
211+
scale * sin(radians), scale * cos(radians), ty);
212+
213+
// Compute the inverse affine matrix
214+
cv::Mat inverseAffine = getInverseAffine(affine);
215+
216+
// Define the destination size
217+
cv::Size dsize(src.cols, src.rows);
218+
219+
// Define the output matrix
220+
cv::Mat dst;
221+
222+
// Get the parameters
223+
int interpolation = std::get<0>(GetParam());
224+
int borderValue = std::get<1>(GetParam());
225+
226+
while (next())
227+
{
228+
startTimer();
229+
cv::fastcv::warpAffine(src, dst, inverseAffine, dsize, interpolation, borderValue);
230+
stopTimer();
231+
}
232+
233+
SANITY_CHECK_NOTHING();
234+
}
235+
96236
} //namespace

0 commit comments

Comments
 (0)