Skip to content

Add warpAffine and resizeDown APIs in FastCV Extension #3936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: 4.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions modules/fastcv/include/opencv2/fastcv/scale.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand All @@ -15,20 +15,18 @@ namespace fastcv {
//! @{

/**
* @brief Down-scale the image by averaging each 2x2 pixel block.
* This function is not bit-exact with cv::resize but provides faster execution time on Qualcomm's processor.
* @param _src The first input image data, type CV_8UC1, src height must be a multiple of 2
* @param _dst The output image data, type CV_8UC1
*/
CV_EXPORTS_W void resizeDownBy2(cv::InputArray _src, cv::OutputArray _dst);

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

//! @}

Expand Down
51 changes: 51 additions & 0 deletions modules/fastcv/include/opencv2/fastcv/warp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,57 @@ CV_EXPORTS_W void warpPerspective(InputArray _src, OutputArray _dst, InputArray
CV_EXPORTS_W void warpPerspective2Plane(InputArray _src1, InputArray _src2, OutputArray _dst1, OutputArray _dst2,
InputArray _M0, Size dsize);

/**
* @brief Applies an affine transformation to a grayscale image using a 2x3 matrix.
* @param _src Input grayscale image of type 'CV_8UC1'.
* @param _dst Output image after transformation.
* @param _M 2x3 affine transformation matrix.
* @param dsize Size of the output image.
* @param interpolation Interpolation method, Supported methods include:
* 'INTER_NEAREST': Nearest neighbor interpolation.
* 'INTER_LINEAR': Bilinear interpolation.
* 'INTER_AREA': Area interpolation.
* @param borderValue Constant pixel value for border pixels.
*
* @note The affine matrix follows the inverse mapping convention, applied to destination coordinates
* to produce corresponding source coordinates.
* @note The function uses 'FASTCV_BORDER_CONSTANT' for border handling, with the specified 'borderValue'.
*/
CV_EXPORTS_W void warpAffine(InputArray _src, OutputArray _dst, InputArray _M, Size dsize, int interpolation, int borderValue);

/**
* @brief Applies an affine transformation on a 3-color channel image using a 2x3 matrix with bicubic interpolation.
* Pixels that would be sampled from outside the source image are not modified in the target image.
* The left-most and right-most pixel coordinates of each scanline are written to dstBorder.
* @param _src Input image (3-channel RGB). Size of buffer is src.step[0] * src.rows bytes.
* WARNING: data should be 128-bit aligned.
* @param _dst Warped output image (3-channel RGB). Size of buffer is dst.step[0] * dst.rows bytes.
* WARNING: data should be 128-bit aligned.
* @param _M 2x3 perspective transformation matrix. The matrix stored in affineMatrix is using row major ordering:
* | a11, a12, a13 |
* | a21, a22, a23 |
* The affine matrix follows the inverse mapping convention.
* @param dsize The output image size.
* @param _dstBorder Output array receiving the x-coordinates of left-most and right-most pixels for each scanline.
* The format of the array is: l0,r0,l1,r1,l2,r2,... where l0 is the left-most pixel coordinate in scanline 0
* and r0 is the right-most pixel coordinate in scanline 0.
* The buffer must therefore be 2 * dsize.height integers in size.
* NOTE: data should be 128-bit aligned.
*/
CV_EXPORTS_W void warpAffine3Channel(InputArray _src, OutputArray _dst, InputArray _M, Size dsize, OutputArray _dstBorder);

/**
* @brief Warps a patch centered at a specified position in the input image using an affine transformation.
* @param src Input grayscale image of type 'CV_8UC1'.
* @param position Position in the image where the patch is centered.
* @param affine 2x2 affine transformation matrix of type 'CV_32FC1'.
* @param patch Output image patch after transformation.
* @param patchSize Size of the output patch.
*
* @return Returns 0 if the transformation is valid.
*/
CV_EXPORTS_W void warpAffineROI(InputArray _src, const cv::Point2f& position, InputArray _affine, OutputArray _patch, Size patchSize);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, the API is redundant. OpenCV cv::Mat has locateRoi method that may extract details of the original matrix, if roi is provided as a parameter. You can just handle the case in existing warpAffine.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in warpAffineROI, we use fcvTransformAffineu8_v2 which uses 2x2 Affine transformation and also uses fixed point precision, that's why it was added separately


//! @}

}
Expand Down
66 changes: 66 additions & 0 deletions modules/fastcv/perf/perf_scale.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "perf_precomp.hpp"

namespace opencv_test {

typedef perf::TestBaseWithParam<std::tuple<Size, int>> ResizePerfTest;

PERF_TEST_P(ResizePerfTest, run, ::testing::Combine(
::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(2, 4) // resize factor
))
{
Size size = std::get<0>(GetParam());
int factor = std::get<1>(GetParam());

cv::Mat inputImage(size, CV_8UC1);
cv::randu(inputImage, cv::Scalar::all(0), cv::Scalar::all(255));

cv::Mat resized_image;
Size dsize(inputImage.cols / factor, inputImage.rows / factor);

while (next())
{
startTimer();
cv::fastcv::resizeDown(inputImage, resized_image, dsize, 0, 0);
stopTimer();
}

SANITY_CHECK_NOTHING();
}

typedef perf::TestBaseWithParam<std::tuple<Size, double, double, int>> ResizeByMnPerfTest;

PERF_TEST_P(ResizeByMnPerfTest, run, ::testing::Combine(
::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(0.35, 0.65), // inv_scale_x
::testing::Values(0.35, 0.65), // inv_scale_y
::testing::Values(CV_8UC1, CV_8UC2) // data type
))
{
Size size = std::get<0>(GetParam());
double inv_scale_x = std::get<1>(GetParam());
double inv_scale_y = std::get<2>(GetParam());
int type = std::get<3>(GetParam());

cv::Mat inputImage(size, type);
cv::randu(inputImage, cv::Scalar::all(0), cv::Scalar::all(255));

Size dsize;
cv::Mat resized_image;

while (next())
{
startTimer();
cv::fastcv::resizeDown(inputImage, resized_image, dsize, inv_scale_x, inv_scale_y);
stopTimer();
}

SANITY_CHECK_NOTHING();
}

} // namespace
132 changes: 132 additions & 0 deletions modules/fastcv/perf/perf_warp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ static void getInvertMatrix(Mat& src, Size dstSize, Mat& M)
invert(M,M);
}

static cv::Mat getInverseAffine(const cv::Mat& affine)
{
// Extract the 2x2 part
cv::Mat rotationScaling = affine(cv::Rect(0, 0, 2, 2));

// Invert the 2x2 part
cv::Mat inverseRotationScaling;
cv::invert(rotationScaling, inverseRotationScaling);

// Extract the translation part
cv::Mat translation = affine(cv::Rect(2, 0, 1, 2));

// Compute the new translation
cv::Mat inverseTranslation = -inverseRotationScaling * translation;

// Construct the inverse affine matrix
cv::Mat inverseAffine = cv::Mat::zeros(2, 3, CV_32F);
inverseRotationScaling.copyTo(inverseAffine(cv::Rect(0, 0, 2, 2)));
inverseTranslation.copyTo(inverseAffine(cv::Rect(2, 0, 1, 2)));

return inverseAffine;
}

typedef perf::TestBaseWithParam<Size> WarpPerspective2PlanePerfTest;

PERF_TEST_P(WarpPerspective2PlanePerfTest, run,
Expand Down Expand Up @@ -93,4 +116,113 @@ PERF_TEST_P(WarpPerspectivePerfTest, run,
SANITY_CHECK_NOTHING();
}

typedef TestBaseWithParam< tuple<MatType, Size> > WarpAffine3ChannelPerf;

PERF_TEST_P(WarpAffine3ChannelPerf, run, Combine(
Values(CV_8UC3),
Values( szVGA, sz720p, sz1080p)
))
{
Size sz, szSrc(512, 512);
int dataType;
dataType = get<0>(GetParam());
sz = get<1>(GetParam());

cv::Mat src(szSrc, dataType), dst(sz, dataType);

cvtest::fillGradient(src);

//Affine matrix
float angle = 30.0; // Rotation angle in degrees
float scale = 2.2; // Scale factor
cv::Mat affine = cv::getRotationMatrix2D(cv::Point2f(100, 100), angle, scale);

// Compute the inverse affine matrix
cv::Mat inverseAffine = getInverseAffine(affine);

// Create the dstBorder array
Mat dstBorder;

declare.in(src).out(dst);

while (next())
{
startTimer();
cv::fastcv::warpAffine3Channel(src, dst, inverseAffine, sz, dstBorder);
stopTimer();
}

SANITY_CHECK_NOTHING();
}

typedef perf::TestBaseWithParam<std::tuple<cv::Size, cv::Point2f, cv::Mat>> WarpAffineROIPerfTest;

PERF_TEST_P(WarpAffineROIPerfTest, run, ::testing::Combine(
::testing::Values(cv::Size(50, 50), cv::Size(100, 100)), // patch size
::testing::Values(cv::Point2f(50.0f, 50.0f), cv::Point2f(100.0f, 100.0f)), // position
::testing::Values((cv::Mat_<float>(2, 2) << 1, 0, 0, 1), // identity matrix
(cv::Mat_<float>(2, 2) << cos(CV_PI), -sin(CV_PI), sin(CV_PI), cos(CV_PI))) // rotation matrix
))
{
cv::Size patchSize = std::get<0>(GetParam());
cv::Point2f position = std::get<1>(GetParam());
cv::Mat affine = std::get<2>(GetParam());

cv::Mat src = cv::imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
cv::Mat patch;

while (next())
{
startTimer();
cv::fastcv::warpAffineROI(src, position, affine, patch, patchSize);
stopTimer();
}

SANITY_CHECK_NOTHING();
}

typedef TestBaseWithParam<tuple<int, int> > WarpAffinePerfTest;

PERF_TEST_P(WarpAffinePerfTest, run, ::testing::Combine(
::testing::Values(cv::InterpolationFlags::INTER_NEAREST, cv::InterpolationFlags::INTER_LINEAR, cv::InterpolationFlags::INTER_AREA),
::testing::Values(0, 255) // Black and white borders
))
{
// Load the source image
cv::Mat src = cv::imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(src.empty());

// Generate random values for the affine matrix
std::srand(std::time(0));
float angle = static_cast<float>(std::rand() % 360); // Random angle between 0 and 360 degrees
float scale = static_cast<float>(std::rand() % 200) / 100.0f + 0.5f; // Random scale between 0.5 and 2.5
float tx = static_cast<float>(std::rand() % 100) - 50; // Random translation between -50 and 50
float ty = static_cast<float>(std::rand() % 100) - 50; // Random translation between -50 and 50
float radians = angle * CV_PI / 180.0;
cv::Mat affine = (cv::Mat_<float>(2, 3) << scale * cos(radians), -scale * sin(radians), tx,
scale * sin(radians), scale * cos(radians), ty);

// Compute the inverse affine matrix
cv::Mat inverseAffine = getInverseAffine(affine);

// Define the destination size
cv::Size dsize(src.cols, src.rows);

// Define the output matrix
cv::Mat dst;

// Get the parameters
int interpolation = std::get<0>(GetParam());
int borderValue = std::get<1>(GetParam());

while (next())
{
startTimer();
cv::fastcv::warpAffine(src, dst, inverseAffine, dsize, interpolation, borderValue);
stopTimer();
}

SANITY_CHECK_NOTHING();
}

} //namespace
Loading
Loading