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 4 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
40 changes: 40 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,46 @@ 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 Performs an affine transformation on an input image using a provided transformation matrix.
*
* This function performs two types of operations based on the transformation matrix:
*
* 1. Standard Affine Transformation (2x3 matrix):
* - Transforms the entire input image using the affine matrix
* - Supports both CV_8UC1 and CV_8UC3 types
*
* 2. Patch Extraction with Transformation (2x2 matrix):
* - Extracts and transforms a patch from the input image
* - Only supports CV_8UC1 type
* - If input is a ROI: patch is extracted from ROI center in the original image
* - If input is full image: patch is extracted from image center
*
* @param _src Input image. Supported formats:
* - CV_8UC1: 8-bit single-channel
* - CV_8UC3: 8-bit three-channel - only for 2x3 matrix
* @param _dst Output image. Will have the same type as src and size specified by dsize
* @param _M 2x2/2x3 affine transformation matrix (inversed map)
* @param dsize Output size:
* - For 2x3 matrix: Size of the output image
* - For 2x2 matrix: Size of the extracted patch
* @param interpolation Interpolation method. Only applicable for 2x3 transformation with CV_8UC1 input.
* Options:
* - INTER_NEAREST: Nearest-neighbor interpolation
* - INTER_LINEAR: Bilinear interpolation (default)
* - INTER_AREA: Area-based interpolation
* - INTER_CUBIC: Bicubic interpolation
* Note: CV_8UC3 input always use bicubic interpolation internally
* @param borderValue Constant pixel value for border pixels. Only applicable for 2x3 transformations
* with single-channel input.
*
* @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 = INTER_LINEAR,
int borderValue = 0);

//! @}

}
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
140 changes: 140 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,121 @@ 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::warpAffine(src, dst, inverseAffine, sz);
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);

// Create ROI with top-left at the specified position
cv::Rect roiRect(static_cast<int>(position.x), static_cast<int>(position.y), patchSize.width, patchSize.height);

// Ensure ROI is within image bounds
roiRect = roiRect & cv::Rect(0, 0, src.cols, src.rows);
cv::Mat roi = src(roiRect);

cv::Mat patch;

while (next())
{
startTimer();
cv::fastcv::warpAffine(roi, patch, affine, 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