vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
message(STATUS "HAVE_FASTCV status ${HAVE_FASTCV}")
if(HAVE_FASTCV)
set(the_description "Qualcomm FastCV accelerated functions")
ocv_define_module(fastcv opencv_core opencv_geometry opencv_imgproc opencv_features opencv_video WRAP python java)
ocv_module_include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}/include"
${FastCV_INCLUDE_PATH})
ocv_target_link_libraries(${the_module} ${FASTCV_LIBRARY})
ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_FASTCV=1)
ocv_install_3rdparty_licenses(FastCV "${OpenCV_BINARY_DIR}/3rdparty/fastcv/LICENSE")
else()
ocv_module_disable(fastcv)
endif()
+6
View File
@@ -0,0 +1,6 @@
FastCV extension for OpenCV
===========================
This module provides wrappers for several FastCV functions not covered by the corresponding HAL in OpenCV or have implementation incompatible with OpenCV.
Please note that:
1. This module supports ARM architecture only. This means that CMake script will not configure or build under x86 platform.
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_HPP
#define OPENCV_FASTCV_HPP
#include <opencv2/core.hpp>
#include "opencv2/fastcv/arithm.hpp"
#include "opencv2/fastcv/bilateralFilter.hpp"
#include "opencv2/fastcv/blur.hpp"
#include "opencv2/fastcv/channel.hpp"
#include "opencv2/fastcv/cluster.hpp"
#include "opencv2/fastcv/draw.hpp"
#include "opencv2/fastcv/edges.hpp"
#include "opencv2/fastcv/fast10.hpp"
#include "opencv2/fastcv/fft.hpp"
#include "opencv2/fastcv/histogram.hpp"
#include "opencv2/fastcv/hough.hpp"
#include "opencv2/fastcv/ipptransform.hpp"
#include "opencv2/fastcv/moments.hpp"
#include "opencv2/fastcv/mser.hpp"
#include "opencv2/fastcv/pyramid.hpp"
#include "opencv2/fastcv/remap.hpp"
#include "opencv2/fastcv/scale.hpp"
#include "opencv2/fastcv/shift.hpp"
#include "opencv2/fastcv/smooth.hpp"
#include "opencv2/fastcv/thresh.hpp"
#include "opencv2/fastcv/tracking.hpp"
#include "opencv2/fastcv/warp.hpp"
#include "opencv2/fastcv/allocator.hpp"
#include "opencv2/fastcv/dsp_init.hpp"
#include "opencv2/fastcv/sad_dsp.hpp"
#include "opencv2/fastcv/thresh_dsp.hpp"
#include "opencv2/fastcv/fft_dsp.hpp"
#include "opencv2/fastcv/edges_dsp.hpp"
#include "opencv2/fastcv/blur_dsp.hpp"
#include "opencv2/fastcv/color.hpp"
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
* @{
* @}
*/
#endif // OPENCV_FASTCV_HPP
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_ALLOCATOR_HPP
#define OPENCV_FASTCV_ALLOCATOR_HPP
#include <opencv2/core.hpp>
#include <set>
#include <mutex>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Resource manager for FastCV allocations.
* This class manages active allocations.
*/
class QcResourceManager {
public:
static QcResourceManager& getInstance();
void addAllocation(void* ptr);
void removeAllocation(void* ptr);
private:
QcResourceManager() = default;
std::set<void*> activeAllocations;
std::mutex resourceMutex;
};
/**
* @brief Qualcomm's custom allocator.
* This allocator uses Qualcomm's memory management functions.
*
* Note: The userdata field of cv::UMatData is used to store the file descriptor (fd) of the allocated memory.
*
*/
class QcAllocator : public cv::MatAllocator {
public:
QcAllocator();
~QcAllocator();
cv::UMatData* allocate(int dims, const int* sizes, int type, void* data0, size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const CV_OVERRIDE;
bool allocate(cv::UMatData* u, cv::AccessFlag accessFlags, cv::UMatUsageFlags usageFlags) const CV_OVERRIDE;
void deallocate(cv::UMatData* u) const CV_OVERRIDE;
};
/**
* @brief Gets the default Qualcomm's allocator.
* This function returns a pointer to the default Qualcomm's allocator, which is optimized
* for use with DSP.
*
* @return Pointer to the default FastCV allocator.
*/
CV_EXPORTS cv::MatAllocator* getQcAllocator();
//! @}
} // namespace fastcv
} // namespace cv
#endif // OPENCV_FASTCV_ALLOCATOR_HPP
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_ARITHM_HPP
#define OPENCV_FASTCV_ARITHM_HPP
#include <opencv2/core.hpp>
#define FCV_CMP_EQ(val1,val2) (fabs(val1 - val2) < FLT_EPSILON)
#define FCV_OPTYPE(depth,op) ((depth<<3) + op)
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Matrix multiplication of two int8_t type matrices
* uses signed integer input/output whereas cv::gemm uses floating point input/output
* matmuls8s32 provides enhanced speed on Qualcomm's processors
* @param src1 First source matrix of type CV_8S
* @param src2 Second source matrix of type CV_8S
* @param dst Resulting matrix of type CV_32S
*/
CV_EXPORTS_W void matmuls8s32(InputArray src1, InputArray src2, OutputArray dst);
//! @}
//! @addtogroup fastcv
//! @{
/**
* @brief Arithmetic add and subtract operations for two matrices
* It is optimized for Qualcomm's processors
* @param src1 First source matrix, can be of type CV_8U, CV_16S, CV_32F.
* Note: CV_32F not supported for subtract
* @param src2 Second source matrix of same type and size as src1
* @param dst Resulting matrix of type as src mats
* @param op type of operation - 0 for add and 1 for subtract
*/
CV_EXPORTS_W void arithmetic_op(InputArray src1, InputArray src2, OutputArray dst, int op);
//! @}
//! @addtogroup fastcv
//! @{
/**
* @brief Matrix multiplication of two float type matrices
* R = a*A*B + b*C where A,B,C,R are matrices and a,b are constants
* It is optimized for Qualcomm's processors
* @param src1 First source matrix of type CV_32F
* @param src2 Second source matrix of type CV_32F with same rows as src1 cols
* @param dst Resulting matrix of type CV_32F
* @param alpha multiplying factor for src1 and src2
* @param src3 Optional third matrix of type CV_32F to be added to matrix product
* @param beta multiplying factor for src3
*/
CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, OutputArray dst, float alpha = 1.0,
InputArray src3 = noArray(), float beta = 0.0);
//! @}
//! @addtogroup fastcv
//! @{
/**
* @brief Integral of a YCbCr420 image.
* Note: Input height should be multiple of 2. Input width and stride should be multiple of 16.
* Output stride should be multiple of 8.
* It is optimized for Qualcomm's processors
* @param Y Input Y component of 8UC1 YCbCr420 image.
* @param CbCr Input CbCr component(interleaved) of 8UC1 YCbCr420 image.
* @param IY Output Y integral of CV_32S one channel, size (Y height + 1)*(Y width + 1)
* @param ICb Output Cb integral of CV_32S one channel, size (Y height/2 + 1)*(Y width/2 + 1)
* @param ICr Output Cr integral of CV_32S one channel, size (Y height/2 + 1)*(Y width/2 + 1)
*/
CV_EXPORTS_W void integrateYUV(InputArray Y, InputArray CbCr, OutputArray IY, OutputArray ICb, OutputArray ICr);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_ARITHM_HPP
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_BILATERALFILTER_HPP
#define OPENCV_FASTCV_BILATERALFILTER_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Applies Bilateral filter to an image considering d-pixel diameter of each pixel's neighborhood.
This filter does not work inplace.
* @param _src Intput image with type CV_8UC1
* @param _dst Destination image with same type as _src
* @param d kernel size (can be 5, 7 or 9)
* @param sigmaColor Filter sigma in the color space.
Typical value is 50.0f.
Increasing this value means increasing the influence of the neighboring pixels of more different color to the smoothing result.
* @param sigmaSpace Filter sigma in the coordinate space.
Typical value is 1.0f.
Increasing this value means increasing the influence of farther neighboring pixels within the kernel size distance to the smoothing result.
* @param borderType border mode used to extrapolate pixels outside of the image
*/
CV_EXPORTS_W void bilateralFilter( InputArray _src, OutputArray _dst, int d,
float sigmaColor, float sigmaSpace,
int borderType = BORDER_DEFAULT );
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_BILATERALFILTER_HPP
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_BLUR_HPP
#define OPENCV_FASTCV_BLUR_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
*/
//! @addtogroup fastcv
//! @{
/**
* @brief Gaussian blur with sigma = 0 and square kernel size. The way of handling borders is different with cv::GaussianBlur,
* leading to slight variations in the output.
* @param _src Intput image with type CV_8UC1
* @param _dst Output image with type CV_8UC1
* @param kernel_size Filer kernel size. One of 3, 5, 11
* @param blur_border If set to true, border is blurred by 0-padding adjacent values.(A variant of the constant border)
* If set to false, borders up to half-kernel width are ignored (e.g. 1 pixel in the 3x3 case).
*
* @sa GaussianBlur
*/
CV_EXPORTS_W void gaussianBlur(InputArray _src, OutputArray _dst, int kernel_size = 3, bool blur_border = true);
/**
* @brief NxN correlation with non-separable kernel. Borders up to half-kernel width are ignored
* @param _src Intput image with type CV_8UC1
* @param _dst Output image with type CV_8UC1, CV_16SC1 or CV_32FC1
* @param ddepth The depth of output image
* @param _kernel Filer kernel data
*
* @sa Filter2D
*/
CV_EXPORTS_W void filter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernel);
/**
* @brief NxN correlation with separable kernel. If srcImg and dstImg point to the same address and srcStride equals to dstStride,
* it will do in-place. Borders up to half-kernel width are ignored.
* The way of handling overflow is different with OpenCV, this function will do right shift for
* the intermediate results and final result.
* @param _src Intput image with type CV_8UC1
* @param _dst Output image with type CV_8UC1, CV_16SC1
* @param ddepth The depth of output image
* @param _kernelX Filer kernel data in x direction
* @param _kernelY Filer kernel data in Y direction (For CV_16SC1, the kernelX and kernelY should be same)
*
* @sa sepFilter2D
*/
CV_EXPORTS_W void sepFilter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernelX, InputArray _kernelY);
//! @}
//! @addtogroup fastcv
//! @{
/**
* @brief Calculates the local subtractive and contrastive normalization of the image.
* Each pixel of the image is normalized by the mean and standard deviation of the patch centred at the pixel.
* It is optimized for Qualcomm's processors.
* @param _src Input image, should have one channel CV_8U or CV_32F
* @param _dst Output array, should be one channel, CV_8S if src of type CV_8U, or CV_32F if src of CV_32F
* @param pSize Patch size for mean and std dev calculation
* @param useStdDev If 1, bot mean and std dev will be used for normalization, if 0, only mean used
*/
CV_EXPORTS_W void normalizeLocalBox(InputArray _src, OutputArray _dst, Size pSize, bool useStdDev);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_BLUR_HPP
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_BLUR_DSP_HPP
#define OPENCV_FASTCV_BLUR_DSP_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
namespace dsp {
//! @addtogroup fastcv
//! @{
/**
* @brief Filter an image with non-separable kernel
* @param _src Intput image with type CV_8UC1, src size should be greater than 176*144
* @param _dst Output image with type CV_8UC1, CV_16SC1 or CV_32FC1
* @param ddepth The depth of output image
* @param _kernel Filer kernel data
*/
CV_EXPORTS void filter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernel);
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_BLUR_DSP_HPP
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_CHANNEL_HPP
#define OPENCV_FASTCV_CHANNEL_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Creates one multi-channel mat out of several single-channel CV_8U mats.
* Optimized for Qualcomm's processors
* @param mv input vector of matrices to be merged; all the matrices in mv must be of CV_8UC1 and have the same size
* Note: numbers of mats can be 2,3 or 4.
* @param dst output array of depth CV_8U and same size as mv[0]; The number of channels
* will be the total number of matrices in the matrix array
*/
CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst);
//! @}
//! @addtogroup fastcv
//! @{
/**
* @brief Splits an CV_8U multi-channel mat into several CV_8UC1 mats
* Optimized for Qualcomm's processors
* @param src input 2,3 or 4 channel mat of depth CV_8U
* @param mv output vector of size src.channels() of CV_8UC1 mats
*/
CV_EXPORTS_W void split(InputArray src, OutputArrayOfArrays mv);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_CHANNEL_HPP
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_CLUSTER_HPP
#define OPENCV_FASTCV_CLUSTER_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Clusterizes N input points in D-dimensional space into K clusters
* Accepts 8-bit unsigned integer points
* Provides faster execution time than cv::kmeans on Qualcomm's processors
* @param points Points array of type 8u, each row represets a point.
* Size is N rows by D columns, can be non-continuous.
* @param clusterCenters Initial cluster centers array of type 32f, each row represents a center.
* Size is K rows by D columns, can be non-continuous.
* @param newClusterCenters Resulting cluster centers array of type 32f, each row represents found center.
* Size is set to be K rows by D columns.
* @param clusterSizes Resulting cluster member counts array of type uint32, size is set to be 1 row by K columns.
* @param clusterBindings Resulting points indices array of type uint32, each index tells to which cluster the corresponding point belongs to.
* Size is set to be 1 row by numPointsUsed columns.
* @param clusterSumDists Resulting distance sums array of type 32f, each number is a sum of distances between each cluster center to its belonging points.
* Size is set to be 1 row by K columns
* @param numPointsUsed Number of points to clusterize starting from 0 to numPointsUsed-1 inclusively. Sets to N if negative.
*/
CV_EXPORTS_W void clusterEuclidean(InputArray points, InputArray clusterCenters, OutputArray newClusterCenters,
OutputArray clusterSizes, OutputArray clusterBindings, OutputArray clusterSumDists,
int numPointsUsed = -1);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_CLUSTER_HPP
@@ -0,0 +1,43 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_FASTCV_COLOR_HPP
#define OPENCV_FASTCV_COLOR_HPP
#include <opencv2/core.hpp>
namespace cv
{
namespace fastcv
{
enum ColorConversionCodes {
// FastCV-specific color conversion codes (avoid collision with OpenCV core)
COLOR_YUV2YUV444sp_NV12 = 156, //!< FastCV: YCbCr420PseudoPlanar to YCbCr444PseudoPlanar
COLOR_YUV2YUV422sp_NV12 = 157, //!< FastCV: YCbCr420PseudoPlanar to YCbCr422PseudoPlanar
COLOR_YUV422sp2YUV444sp = 158, //!< FastCV: YCbCr422PseudoPlanar to YCbCr444PseudoPlanar
COLOR_YUV422sp2YUV_NV12 = 159, //!< FastCV: YCbCr422PseudoPlanar to YCbCr420PseudoPlanar
COLOR_YUV444sp2YUV422sp = 160, //!< FastCV: YCbCr444PseudoPlanar to YCbCr422PseudoPlanar
COLOR_YUV444sp2YUV_NV12 = 161, //!< FastCV: YCbCr444PseudoPlanar to YCbCr420PseudoPlanar
COLOR_YUV2RGB565_NV12 = 162, //!< FastCV: YCbCr420PseudoPlanar to RGB565
COLOR_YUV422sp2RGB565 = 163, //!< FastCV: YCbCr422PseudoPlanar to RGB565
COLOR_YUV422sp2RGB = 164, //!< FastCV: YCbCr422PseudoPlanar to RGB888
COLOR_YUV422sp2RGBA = 165, //!< FastCV: YCbCr422PseudoPlanar to RGBA8888
COLOR_YUV444sp2RGB565 = 166, //!< FastCV: YCbCr444PseudoPlanar to RGB565
COLOR_YUV444sp2RGB = 167, //!< FastCV: YCbCr444PseudoPlanar to RGB888
COLOR_YUV444sp2RGBA = 168, //!< FastCV: YCbCr444PseudoPlanar to RGBA8888
COLOR_RGB2YUV_NV12 = 169, //!< FastCV: RGB888 to YCbCr420PseudoPlanar
COLOR_RGB5652YUV444sp = 170, //!< FastCV: RGB565 to YCbCr444PseudoPlanar
COLOR_RGB5652YUV422sp = 171, //!< FastCV: RGB565 to YCbCr422PseudoPlanar
COLOR_RGB5652YUV_NV12 = 172, //!< FastCV: RGB565 to YCbCr420PseudoPlanar
COLOR_RGB2YUV444sp = 173, //!< FastCV: RGB888 to YCbCr444PseudoPlanar
COLOR_RGB2YUV422sp = 174, //!< FastCV: RGB888 to YCbCr422PseudoPlanar
};
CV_EXPORTS_W void cvtColor(InputArray src, OutputArray dst, int code);
}}; //cv::fastcv namespace end
#endif // OPENCV_FASTCV_COLOR_HPP
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_DRAW_HPP
#define OPENCV_FASTCV_DRAW_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Draw convex polygon
This function fills the interior of a convex polygon with the specified color.
Requires the width and stride to be multple of 8.
* @param img Image to draw on. Should have up to 4 8-bit channels
* @param pts Array of polygon points coordinates. Should contain N two-channel or 2*N one-channel 32-bit integer elements
* @param color Color of drawn polygon stored as B,G,R and A(if supported)
*/
CV_EXPORTS_W void fillConvexPoly(InputOutputArray img, InputArray pts, Scalar color);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_DRAW_HPP
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_DSP_INIT_HPP
#define OPENCV_FASTCV_DSP_INIT_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
namespace dsp {
//! @addtogroup fastcv
//! @{
/**
* @brief Initializes the FastCV DSP environment.
*
* This function sets up the necessary environment and resources for the DSP to operate.
* It must be called once at the very beginning of the use case or program to ensure that
* the DSP is properly initialized before any DSP-related operations are performed.
*
* @note This function must be called at the start of the use case or program, before any
* DSP-related operations.
*
* @return int Returns 0 on success, and a non-zero value on failure.
*/
CV_EXPORTS int fcvdspinit();
/**
* @brief Deinitializes the FastCV DSP environment.
*
* This function releases the resources and environment set up by the 'fcvdspinit' function.
* It should be called before the use case or program exits to ensure that all DSP resources
* are properly cleaned up and no memory leaks occur.
*
* @note This function must be called at the end of the use case or program, after all DSP-related
* operations are complete.
*/
CV_EXPORTS void fcvdspdeinit();
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_DSP_INIT_HPP
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_EDGES_HPP
#define OPENCV_EDGES_HPP
#include "opencv2/core/mat.hpp"
namespace cv {
namespace fastcv {
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
*/
//! @addtogroup fastcv
//! @{
/**
* @brief Creates a 2D gradient image from source luminance data without normalization.
* Calculate X direction 1 order derivative or Y direction 1 order derivative or both at the same time, .
* @param _src Input image with type CV_8UC1
* @param _dx Buffer to store horizontal gradient. Must be (dxyStride)*(height) bytes in size.
* If NULL, the horizontal gradient will not be calculated.
* @param _dy Buffer to store vertical gradient. Must be (dxyStride)*(height) bytes in size.
* If NULL, the vertical gradient will not be calculated
* @param kernel_size Sobel kernel size, support 3x3, 5x5, 7x7
* @param borderType Border type, support BORDER_CONSTANT, BORDER_REPLICATE
* @param borderValue Border value for constant border
*/
CV_EXPORTS_W void sobel(InputArray _src, OutputArray _dx, OutputArray _dy, int kernel_size, int borderType, int borderValue);
/**
* @brief Creates a 2D gradient image from source luminance data without normalization.
* This function computes central differences on 3x3 neighborhood and then convolves the result with Sobel kernel,
* borders up to half-kernel width are ignored.
* @param _src Input image with type CV_8UC1
* @param _dst If _dsty is given, buffer to store horizontal gradient, otherwise, output 8-bit image of |dx|+|dy|.
* Size of buffer is (srcwidth)*(srcheight) bytes
* @param _dsty (Optional)Buffer to store vertical gradient. Must be (srcwidth)*(srcheight) in size.
* @param ddepth The depth of output image CV_8SC1,CV_16SC1,CV_32FC1,
* @param normalization If do normalization for the result
*/
CV_EXPORTS_W void sobel3x3u8(InputArray _src, OutputArray _dst, OutputArray _dsty = noArray(), int ddepth = CV_8U,
bool normalization = false);
//! @}
}
}
#endif
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_EDGES_DSP_HPP
#define OPENCV_FASTCV_EDGES_DSP_HPP
#include "opencv2/core/mat.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
*/
//! @addtogroup fastcv
//! @{
/**
* @brief Canny edge detector applied to a 8 bit grayscale image
* @param _src Input image with type CV_8UC1
* @param _dst Output 8-bit image containing the edge detection results
* @param lowThreshold First threshold
* @param highThreshold Second threshold
* @param apertureSize The Sobel kernel size for calculating gradient. Supported sizes are 3, 5 and 7.
* @param L2gradient L2 Gradient or L1 Gradient
*/
CV_EXPORTS void Canny(InputArray _src, OutputArray _dst, int lowThreshold, int highThreshold, int apertureSize = 3, bool L2gradient = false);
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif //OPENCV_FASTCV_EDGES_DSP_HPP
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_FAST10_HPP
#define OPENCV_FASTCV_FAST10_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Extracts FAST10 corners and scores from the image based on the mask.
* The mask specifies pixels to be ignored by the detector
* designed for corner detection on Qualcomm's processors, provides enhanced speed.
*
* @param src 8-bit grayscale image
* @param mask Optional mask indicating which pixels should be omited from corner dection.
Its size should be k times image width and height, where k = 1/2, 1/4 , 1/8 , 1, 2, 4 and 8
For more details see documentation to `fcvCornerFast9InMaskScoreu8` function in FastCV
* @param coords Output array of CV_32S containing interleave x, y positions of detected corners
* @param scores Optional output array containing the scores of the detected corners.
The score is the highest threshold that can still validate the detected corner.
A higher score value indicates a stronger corner feature.
For example, a corner of score 108 is stronger than a corner of score 50
* @param barrier FAST threshold. The threshold is used to compare difference between intensity value
of the central pixel and pixels on a circle surrounding this pixel
* @param border Number for pixels to ignore from top,bottom,right,left of the image. Defaults to 4 if it's below 4
* @param nmsEnabled Enable non-maximum suppresion to prune weak key points
*/
CV_EXPORTS_W void FAST10(InputArray src, InputArray mask, OutputArray coords, OutputArray scores, int barrier, int border, bool nmsEnabled);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_FAST10_HPP
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_FFT_HPP
#define OPENCV_FASTCV_FFT_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Computes the 1D or 2D Fast Fourier Transform of a real valued matrix.
For the 2D case, the width and height of the input and output matrix must be powers of 2.
For the 1D case, the height of the matrices must be 1, while the width must be a power of 2.
Accepts 8-bit unsigned integer array, whereas cv::dft accepts floating-point or complex array.
* @param src Input array of CV_8UC1. The dimensions of the matrix must be powers of 2 for the 2D case,
and in the 1D case, the height must be 1, while the width must be a power of 2.
* @param dst The computed FFT matrix of type CV_32FC2. The FFT Re and Im coefficients are stored in different channels.
Hence the dimensions of the dst are (srcWidth, srcHeight)
*/
CV_EXPORTS_W void FFT(InputArray src, OutputArray dst);
/**
* @brief Computes the 1D or 2D Inverse Fast Fourier Transform of a complex valued matrix.
For the 2D case, The width and height of the input and output matrix must be powers of 2.
For the 1D case, the height of the matrices must be 1, while the width must be a power of 2.
* @param src Input array of type CV_32FC2 containing FFT Re and Im coefficients stored in separate channels.
The dimensions of the matrix must be powers of 2 for the 2D case, and in the 1D case, the height must be 1,
while the width must be a power of 2.
* @param dst The computed IFFT matrix of type CV_8U. The matrix is real valued and has no imaginary components.
Hence the dimensions of the dst are (srcWidth , srcHeight)
*/
CV_EXPORTS_W void IFFT(InputArray src, OutputArray dst);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_FFT_HPP
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_FFT_DSP_HPP
#define OPENCV_FASTCV_FFT_DSP_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
namespace dsp {
//! @addtogroup fastcv
//! @{
/**
* @brief Computes the 1D or 2D Fast Fourier Transform of a real valued matrix.
For the 2D case, the width and height of the input and output matrix must be powers of 2.
For the 1D case, the height of the matrices must be 1, while the width must be a power of 2.
* @param src Input array of CV_8UC1. The dimensions of the matrix must be powers of 2 for the 2D case,
and in the 1D case, the height must be 1, while the width must be a power of 2.
* @param dst The computed FFT matrix of type CV_32FC2. The FFT Re and Im coefficients are stored in different channels.
Hence the dimensions of the dst are (srcWidth, srcHeight)
*/
CV_EXPORTS void FFT(InputArray src, OutputArray dst);
/**
* @brief Computes the 1D or 2D Inverse Fast Fourier Transform of a complex valued matrix.
For the 2D case, The width and height of the input and output matrix must be powers of 2.
For the 1D case, the height of the matrices must be 1, while the width must be a power of 2.
* @param src Input array of type CV_32FC2 containing FFT Re and Im coefficients stored in separate channels.
The dimensions of the matrix must be powers of 2 for the 2D case, and in the 1D case, the height must be 1,
while the width must be a power of 2.
* @param dst The computed IFFT matrix of type CV_8U. The matrix is real valued and has no imaginary components.
Hence the dimensions of the dst are (srcWidth , srcHeight)
*/
CV_EXPORTS void IFFT(InputArray src, OutputArray dst);
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_FFT_DSP_HPP
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_HISTOGRAM_HPP
#define OPENCV_FASTCV_HISTOGRAM_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Calculates histogram of input image. This function implements specific use case of
* 256-bin histogram calculation for 8u single channel images in an optimized way.
* @param _src Intput image with type CV_8UC1
* @param _hist Output histogram of type int of 256 bins
*/
CV_EXPORTS_W void calcHist( InputArray _src, OutputArray _hist );
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_HISTOGRAM_HPP
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_HOUGH_HPP
#define OPENCV_FASTCV_HOUGH_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Performs Hough Line detection
*
* @param src Input 8-bit image containing binary contour. Width and step should be divisible by 8
* @param lines Output array containing detected lines in a form of (x1, y1, x2, y2) where all numbers are 32-bit floats
* @param threshold Controls the minimal length of a detected line. Value must be between 0.0 and 1.0
* Values close to 1.0 reduces the number of detected lines. Values close to 0.0
* detect more lines, but may be noisy. Recommended value is 0.25.
*/
CV_EXPORTS_W void houghLines(InputArray src, OutputArray lines, double threshold = 0.25);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_HOUGH_HPP
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_IPPTRANSFORM_HPP
#define OPENCV_FASTCV_IPPTRANSFORM_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief This function performs 8x8 forward discrete Cosine transform on input image
* accepts input of type 8-bit unsigned integer and produces output of type 16-bit signed integer
* provides faster execution time than cv::dct on Qualcomm's processor
* @param src Input image of type CV_8UC1
* @param dst Output image of type CV_16SC1
*/
CV_EXPORTS_W void DCT(InputArray src, OutputArray dst);
/**
* @brief This function performs 8x8 inverse discrete Cosine transform on input image
* provides faster execution time than cv::dct in inverse case on Qualcomm's processor
* @param src Input image of type CV_16SC1
* @param dst Output image of type CV_8UC1
*/
CV_EXPORTS_W void IDCT(InputArray src, OutputArray dst);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_IPPTRANSFORM_HPP
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_MOMENTS_HPP
#define OPENCV_FASTCV_MOMENTS_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Calculates all of the moments up to the third order of the image pixels' intensities
* The results are returned in the structure cv::Moments. This function cv::fastcv::moments()
* calculate the moments using floating point calculations whereas cv::moments() calculate moments using double.
* @param _src Input image with type CV_8UC1, CV_32SC1, CV_32FC1
* @param binary If true, assumes the image to be binary (0x00 for black, 0xff for white), otherwise assumes the image to be
* grayscale.
*/
CV_EXPORTS cv::Moments moments(InputArray _src, bool binary);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_MOMENTS_HPP
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_MSER_HPP
#define OPENCV_FASTCV_MSER_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief MSER blob detector for grayscale images
*
*/
class CV_EXPORTS_W FCVMSER
{
public:
/**
* @brief Structure containing additional information about found contour
*
*/
struct ContourData
{
uint32_t variation; //!< Variation of a contour from previous grey level
int32_t polarity; //!< Polarity for a contour. This value is 1 if this is a MSER+ region, -1 if this is a MSER- region.
uint32_t nodeId; //!< Node ID for a contour
uint32_t nodeCounter; //!< Node counter for a contour
};
/**
* @brief Creates MSER detector
*
* @param imgSize Image size. Image width has to be greater than 50, and image height has to be greater than 5.
* @param numNeighbors Number of neighbors in contours, can be 4 or 8
* @param delta Delta to be used in MSER algorithm (the difference in grayscale values
within which the region is stable ).
Typical value range [0.8 8], typical value 2
* @param minArea Minimum area (number of pixels) of a mser contour.
Typical value range [10 50], typical value 30
* @param maxArea Maximum area (number of pixels) of a mser contour.
Typical value 14400 or 0.25*width*height
* @param maxVariation Maximum variation in grayscale between 2 levels allowed.
Typical value range [0.1 1.0], typical value 0.15
* @param minDiversity Minimum diversity in grayscale between 2 levels allowed.
Typical value range [0.1 1.0], typical value 0.2
* @return Feature detector object ready for detection
*/
CV_WRAP static Ptr<FCVMSER> create( const cv::Size& imgSize,
int numNeighbors = 4,
int delta = 2,
int minArea = 30,
int maxArea = 14400,
float maxVariation = 0.15f,
float minDiversity = 0.2f);
/**
* @brief This is an overload for detect() function
*
* @param src Source image of type CV_8UC1. Image width has to be greater than 50, and image height has to be greater than 5.
Pixels at the image boundary are not processed. If boundary pixels are important
for a particular application, please consider padding the input image with dummy
pixels of one pixel wide.
* @param contours Array containing found contours
*/
CV_WRAP virtual void detect(InputArray src, std::vector<std::vector<Point>>& contours) = 0;
/**
* @brief This is an overload for detect() function
*
* @param src Source image of type CV_8UC1. Image width has to be greater than 50, and image height has to be greater than 5.
Pixels at the image boundary are not processed. If boundary pixels are important
for a particular application, please consider padding the input image with dummy
pixels of one pixel wide.
* @param contours Array containing found contours
* @param boundingBoxes Array containing bounding boxes of found contours
*/
CV_WRAP virtual void detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes) = 0;
/**
* @brief Runs MSER blob detector on the grayscale image
*
* @param src Source image of type CV_8UC1. Image width has to be greater than 50, and image height has to be greater than 5.
Pixels at the image boundary are not processed. If boundary pixels are important
for a particular application, please consider padding the input image with dummy
pixels of one pixel wide.
* @param contours Array containing found contours
* @param boundingBoxes Array containing bounding boxes of found contours
* @param contourData Array containing additional information about found contours
*/
virtual void detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes,
std::vector<ContourData>& contourData) = 0;
CV_WRAP virtual cv::Size getImgSize() = 0;
CV_WRAP virtual int getNumNeighbors() = 0;
CV_WRAP virtual int getDelta() = 0;
CV_WRAP virtual int getMinArea() = 0;
CV_WRAP virtual int getMaxArea() = 0;
CV_WRAP virtual float getMaxVariation() = 0;
CV_WRAP virtual float getMinDiversity() = 0;
virtual ~FCVMSER() {}
};
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_MSER_HPP
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_PYRAMID_HPP
#define OPENCV_FASTCV_PYRAMID_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Creates a gradient pyramid from an image pyramid
* Note: The borders are ignored during gradient calculation.
* @param pyr Input pyramid of 1-channel 8-bit images. Only continuous images are supported.
* @param dx Horizontal Sobel gradient pyramid of the same size as pyr
* @param dy Verical Sobel gradient pyramid of the same size as pyr
* @param outType Type of output data, can be CV_8S, CV_16S or CV_32F
*/
CV_EXPORTS_W void sobelPyramid(InputArrayOfArrays pyr, OutputArrayOfArrays dx, OutputArrayOfArrays dy, int outType = CV_8S);
/**
* @brief Builds an image pyramid of float32 arising from a single
original image - that are successively downscaled w.r.t. the
pre-set levels. This API supports both ORB scaling and scale down by half.
*
* @param src Input single-channel image of type 8U or 32F
* @param pyr Output array containing nLevels downscaled image copies
* @param nLevels Number of pyramid levels to produce
* @param scaleBy2 to scale images 2x down or by a factor of 1/(2)^(1/4) which is approximated as 0.8408964 (ORB downscaling),
* ORB scaling is not supported for float point images
* @param borderType how to process border, the options are BORDER_REFLECT (maps to FASTCV_BORDER_REFLECT),
* BORDER_REFLECT_101 (maps to FASTCV_BORDER_REFLECT_V2) and BORDER_REPLICATE (maps to FASTCV_BORDER_REPLICATE).
* Other border types are mapped to FASTCV_BORDER_UNDEFINED(border pixels are ignored). Currently, borders only
* supported for downscaling by half, ignored for ORB scaling. Also ignored for float point images
* @param borderValue what value should be used to fill border, ignored for float point images
*/
CV_EXPORTS_W void buildPyramid(InputArray src, OutputArrayOfArrays pyr, int nLevels, bool scaleBy2 = true,
int borderType = cv::BORDER_REFLECT, uint8_t borderValue = 0);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_PYRAMID_HPP
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_REMAP_HPP
#define OPENCV_FASTCV_REMAP_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Applies a generic geometrical transformation to a greyscale CV_8UC1 image.
* @param src The first input image data, type CV_8UC1
* @param dst The output image data, type CV_8UC1
* @param map1 Floating-point CV_32FC1 matrix with each element as the column coordinate of the mapped location in the source image
* @param map2 Floating-point CV_32FC1 matrix with each element as the row coordinate of the mapped location in the source image.
* @param interpolation Only INTER_NEAREST and INTER_LINEAR interpolation is supported
* @param borderValue constant pixel value
*/
CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
InputArray map1, InputArray map2,
int interpolation, int borderValue=0);
/**
* @brief Applies a generic geometrical transformation to a 4-channel CV_8UC4 image with bilinear or nearest neighbor interpolation
* @param src The first input image data, type CV_8UC4
* @param dst The output image data, type CV_8UC4
* @param map1 Floating-point CV_32FC1 matrix with each element as the column coordinate of the mapped location in the source image
* @param map2 Floating-point CV_32FC1 matrix with each element as the row coordinate of the mapped location in the source image.
* @param interpolation Only INTER_NEAREST and INTER_LINEAR interpolation is supported
*/
CV_EXPORTS_W void remapRGBA( InputArray src, OutputArray dst,
InputArray map1, InputArray map2, int interpolation);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_REMAP_HPP
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_SAD_HPP
#define OPENCV_FASTCV_SAD_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
namespace dsp {
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
*/
//! @addtogroup fastcv
//! @{
/**
* @brief Sum of absolute differences of an image against an 8x8 template.
* @param _patch The first input image data, type CV_8UC1
* @param _src The input image data, type CV_8UC1
* @param _dst The output image data, type CV_16UC1
*/
CV_EXPORTS void sumOfAbsoluteDiffs(cv::InputArray _patch, cv::InputArray _src, cv::OutputArray _dst);
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_SAD_HPP
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_SCALE_HPP
#define OPENCV_FASTCV_SCALE_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @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);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_SCALE_HPP
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_SHIFT_HPP
#define OPENCV_FASTCV_SHIFT_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Applies the meanshift procedure and obtains the final converged position.
This function applies the meanshift procedure to an original image (usually a probability image)
and obtains the final converged position. The converged position search will stop either it has reached
the required accuracy or the maximum number of iterations. Moments used in the algorithm are calculated
in floating point.
This function isn't bit-exact with cv::meanShift but provides improved latency on Snapdragon processors.
* @param src 8-bit, 32-bit int or 32-bit float grayscale image which is usually a probability image
* computed based on object histogram
* @param rect Initial search window position which also returns the final converged window position
* @param termCrit The criteria used to finish the MeanShift which consists of two termination criteria:
* 1) epsilon: required accuracy; 2) max_iter: maximum number of iterations
* @return Iteration number at which the loop stopped
*/
CV_EXPORTS_W int meanShift(InputArray src, Rect& rect, TermCriteria termCrit);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_SHIFT_HPP
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_SMOOTH_HPP
#define OPENCV_FASTCV_SMOOTH_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Recursive Bilateral Filtering
Different from traditional bilateral filtering, here the smoothing is actually performed in gradient domain.
The algorithm claims that it's more efficient than the original bilateral filtering in both image quality and computation.
See algorithm description in the paper Recursive Bilateral Filtering, ECCV2012 by Prof Yang Qingxiong
This function isn't bit-exact with cv::bilateralFilter but provides improved latency on Snapdragon processors.
* @param src Input image, should have one CV_8U channel
* @param dst Output array having one CV_8U channel
* @param sigmaColor Sigma in the color space, the bigger the value the more color difference is smoothed by the algorithm
* @param sigmaSpace Sigma in the coordinate space, the bigger the value the more distant pixels are smoothed
*/
CV_EXPORTS_W void bilateralRecursive(cv::InputArray src, cv::OutputArray dst, float sigmaColor = 0.03f, float sigmaSpace = 0.1f);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_SMOOTH_HPP
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_THRESH_HPP
#define OPENCV_FASTCV_THRESH_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Binarizes a grayscale image based on a pair of threshold values. The binarized image will be in the two values
* selected by user
* this function provides improved latency on Snapdragon processor.
* @param src 8-bit grayscale image
* @param dst Output image of the same size and type as input image, can be the same as input image
* @param lowThresh The lower threshold value for binarization
* @param highThresh The higher threshold value for binarization
* @param trueValue The value assigned to the destination pixel if the source is within the range inclusively defined by the
* pair of threshold values
* @param falseValue The value assigned to the destination pixel if the source is out of the range inclusively defined by the
* pair of threshold values
*/
CV_EXPORTS_W void thresholdRange(InputArray src, OutputArray dst, int lowThresh, int highThresh, int trueValue, int falseValue);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_THRESH_HPP
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_THRESH_DSP_HPP
#define OPENCV_FASTCV_THRESH_DSP_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
namespace dsp {
//! @addtogroup fastcv
//! @{
/**
* @brief Binarizes a grayscale image using Otsu's method.
* Sets the pixel to max(255) if it's value is greater than the threshold;
* else, set the pixel to min(0). The threshold is searched that minimizes
* the intra-class variance (the variance within the class).
*
* @param _src Input 8-bit grayscale image. Size of buffer is srcStride*srcHeight bytes.
* @param _dst Output 8-bit binarized image. Size of buffer is dstStride*srcHeight bytes.
* @param type Threshold type that can be either 0 or 1.
* NOTE: For threshold type=0, the pixel is set as
* maxValue if it's value is greater than the threshold; else, it is set as zero.
* For threshold type=1, the pixel is set as zero if it's
* value is greater than the threshold; else, it is set as maxValue.
*/
CV_EXPORTS void thresholdOtsu(InputArray _src, OutputArray _dst, bool type);
//! @}
} // dsp::
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_THRESH_DSP_HPP
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_TRACKING_HPP
#define OPENCV_FASTCV_TRACKING_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace fastcv {
//! @addtogroup fastcv
//! @{
/**
* @brief Calculates sparse optical flow using Lucas-Kanade algorithm
* accepts 8-bit unsigned integer image
* Provides faster execution time on Qualcomm's processor
* @param src Input single-channel image of type 8U, initial motion frame
* @param dst Input single-channel image of type 8U, final motion frame, should have the same size and stride as initial frame
* @param srcPyr Pyramid built from intial motion frame
* @param dstPyr Pyramid built from final motion frame
* @param ptsIn Array of initial subpixel coordinates of starting points, should contain 32F 2D elements
* @param ptsOut Output array of calculated final points, should contain 32F 2D elements
* @param ptsEst Input array of estimations for final points, should contain 32F 2D elements, can be empty
* @param statusVec Output array of int32 values indicating status of each feature, can be empty
* @param winSize Size of window for optical flow searching. Width and height ust be odd numbers. Suggested values are 5, 7 or 9
* @param termCriteria Termination criteria containing max number of iterations, max epsilon and stop condition
*/
CV_EXPORTS_W void trackOpticalFlowLK(InputArray src, InputArray dst,
InputArrayOfArrays srcPyr, InputArrayOfArrays dstPyr,
InputArray ptsIn, OutputArray ptsOut, InputArray ptsEst,
OutputArray statusVec, cv::Size winSize = cv::Size(7, 7),
cv::TermCriteria termCriteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
/* maxIterations */ 7, /* maxEpsilon */ 0.03f * 0.03f));
/**
* @brief Overload for v1 of the LK tracking function
*
* @param src Input single-channel image of type 8U, initial motion frame
* @param dst Input single-channel image of type 8U, final motion frame, should have the same size and stride as initial frame
* @param srcPyr Pyramid built from intial motion frame
* @param dstPyr Pyramid built from final motion frame
* @param srcDxPyr Pyramid of Sobel derivative by X of srcPyr
* @param srcDyPyr Pyramid of Sobel derivative by Y of srcPyr
* @param ptsIn Array of initial subpixel coordinates of starting points, should contain 32F 2D elements
* @param ptsOut Output array of calculated final points, should contain 32F 2D elements
* @param statusVec Output array of int32 values indicating status of each feature, can be empty
* @param winSize Size of window for optical flow searching. Width and height ust be odd numbers. Suggested values are 5, 7 or 9
* @param maxIterations Maximum number of iterations to try
*/
CV_EXPORTS_W void trackOpticalFlowLK(InputArray src, InputArray dst,
InputArrayOfArrays srcPyr, InputArrayOfArrays dstPyr,
InputArrayOfArrays srcDxPyr, InputArrayOfArrays srcDyPyr,
InputArray ptsIn, OutputArray ptsOut,
OutputArray statusVec, cv::Size winSize = cv::Size(7, 7), int maxIterations = 7);
//! @}
} // fastcv::
} // cv::
#endif // OPENCV_FASTCV_TRACKING_HPP
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_WARP_HPP
#define OPENCV_WARP_HPP
#include <opencv2/imgproc.hpp>
namespace cv {
namespace fastcv {
/**
* @defgroup fastcv Module-wrapper for FastCV hardware accelerated functions
*/
//! @addtogroup fastcv
//! @{
/**
* @brief Transform an image using perspective transformation, same as cv::warpPerspective but not bit-exact.
* @param _src Input 8-bit image.
* @param _dst Output 8-bit image.
* @param _M0 3x3 perspective transformation matrix.
* @param dsize Size of the output image.
* @param interpolation Interpolation method. Only cv::INTER_NEAREST, cv::INTER_LINEAR and cv::INTER_AREA are supported.
* @param borderType Pixel extrapolation method. Only cv::BORDER_CONSTANT, cv::BORDER_REPLICATE and cv::BORDER_TRANSPARENT
* are supported.
* @param borderValue Value used in case of a constant border.
*/
CV_EXPORTS_W void warpPerspective(InputArray _src, OutputArray _dst, InputArray _M0, Size dsize, int interpolation, int borderType,
const Scalar& borderValue);
/**
* @brief Perspective warp two images using the same transformation. Bi-linear interpolation is used where applicable.
* For example, to warp a grayscale image and an alpha image at the same time, or warp two color channels.
* @param _src1 First input 8-bit image. Size of buffer is src1Stride*srcHeight bytes.
* @param _src2 Second input 8-bit image. Size of buffer is src2Stride*srcHeight bytes.
* @param _dst1 First warped output image (correspond to src1). Size of buffer is dst1Stride*dstHeight bytes, type CV_8UC1
* @param _dst2 Second warped output image (correspond to src2). Size of buffer is dst2Stride*dstHeight bytes, type CV_8UC1
* @param _M0 The 3x3 perspective transformation matrix (inversed map)
* @param dsize The output image size
*/
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);
//! @}
}
}
#endif
+32
View File
@@ -0,0 +1,32 @@
/*
* 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<tuple<Size, int>> IntegrateYUVPerfTest;
PERF_TEST_P(IntegrateYUVPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8U) // image depth
)
)
{
cv::Size srcSize = get<0>(GetParam());
int depth = get<1>(GetParam());
cv::Mat Y(srcSize, depth), CbCr(srcSize.height/2, srcSize.width, depth);
cv::Mat IY, ICb, ICr;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, Y, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, CbCr, Scalar::all(0), Scalar::all(255));
TEST_CYCLE() cv::fastcv::integrateYUV(Y, CbCr, IY, ICb, ICr);
SANITY_CHECK_NOTHING();
}
} // namespace
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<float /*sigmaColor*/, float /*sigmaSpace*/> BilateralRecursivePerfParams;
typedef perf::TestBaseWithParam<BilateralRecursivePerfParams> BilateralRecursivePerfTest;
PERF_TEST_P(BilateralRecursivePerfTest, run,
::testing::Combine(::testing::Values(0.01f, 0.03f, 0.1f, 1.f, 5.f),
::testing::Values(0.01f, 0.05f, 0.1f, 1.f, 5.f))
)
{
auto p = GetParam();
float sigmaColor = std::get<0>(p);
float sigmaSpace = std::get<1>(p);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
Mat dst;
while(next())
{
startTimer();
cv::fastcv::bilateralRecursive(src, dst, sigmaColor, sigmaSpace);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef std::tuple<float /*sigmaColor*/, float /*sigmaSpace*/, cv::Size, int > BilateralPerfParams;
typedef perf::TestBaseWithParam<BilateralPerfParams> BilateralPerfTest;
PERF_TEST_P(BilateralPerfTest, run,
::testing::Combine(::testing::Values(0.01f, 0.03f, 0.1f, 1.f, 5.f),
::testing::Values(0.01f, 0.05f, 0.1f, 1.f, 5.f),
::testing::Values(Size(8, 8), Size(640, 480), Size(800, 600)),
::testing::Values(5, 7, 9))
)
{
auto p = GetParam();
float sigmaColor = std::get<0>(p);
float sigmaSpace = std::get<1>(p);
cv::Size size = std::get<2>(p);
int d = get<3>(p);
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
Mat dst;
while (next())
{
startTimer();
cv::fastcv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef perf::TestBaseWithParam<tuple<Size, int, int, bool>> GaussianBlurPerfTest;
PERF_TEST_P(GaussianBlurPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8U,CV_16S,CV_32S), // image depth
::testing::Values(3, 5), // kernel size
::testing::Values(true,false) // blur border
)
)
{
cv::Size srcSize = get<0>(GetParam());
int depth = get<1>(GetParam());
int ksize = get<2>(GetParam());
bool border = get<3>(GetParam());
// For some cases FastCV not support, so skip them
if((ksize!=5) && (depth!=CV_8U))
throw ::perf::TestBase::PerfSkipTestException();
cv::Mat src(srcSize, depth);
cv::Mat dst;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
while (next())
{
startTimer();
cv::fastcv::gaussianBlur(src, dst, ksize, border);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef perf::TestBaseWithParam<tuple<Size, int, int>> Filter2DPerfTest;
PERF_TEST_P(Filter2DPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8U,CV_16S,CV_32F), // dst image depth
::testing::Values(3, 5, 7, 9, 11) // kernel size
)
)
{
cv::Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int ksize = get<2>(GetParam());
cv::Mat src(srcSize, CV_8U);
cv::Mat kernel;
cv::Mat dst;
switch (ddepth)
{
case CV_8U:
case CV_16S:
{
kernel.create(ksize,ksize,CV_8S);
break;
}
case CV_32F:
{
kernel.create(ksize,ksize,CV_32F);
break;
}
default:
break;
}
cv::randu(src, 0, 256);
cv::randu(kernel, INT8_MIN, INT8_MAX);
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
while (next())
{
startTimer();
cv::fastcv::filter2D(src, dst, ddepth, kernel);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef perf::TestBaseWithParam<tuple<Size, int, int>> SepFilter2DPerfTest;
PERF_TEST_P(SepFilter2DPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8U,CV_16S), // dst image depth
::testing::Values(3, 5, 7, 9, 11, 13, 15, 17) // kernel size
)
)
{
cv::Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int ksize = get<2>(GetParam());
cv::Mat src(srcSize, ddepth);
cv::Mat kernel(1, ksize, ddepth);
cv::Mat dst;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, kernel, Scalar::all(INT8_MIN), Scalar::all(INT8_MAX));
while (next())
{
startTimer();
cv::fastcv::sepFilter2D(src, dst, ddepth, kernel, kernel);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef perf::TestBaseWithParam<tuple<Size, int, Size, int>> NormalizeLocalBoxPerfTest;
PERF_TEST_P(NormalizeLocalBoxPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8U,CV_32F), // src image depth
::testing::Values(Size(3,3),Size(5,5)), // patch size
::testing::Values(0,1) // use std dev or not
)
)
{
cv::Size srcSize = get<0>(GetParam());
int depth = get<1>(GetParam());
Size sz = get<2>(GetParam());
bool useStdDev = get<3>(GetParam());
cv::Mat src(srcSize, depth);
cv::Mat dst;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
TEST_CYCLE() cv::fastcv::normalizeLocalBox(src, dst, sz, useStdDev);
SANITY_CHECK_NOTHING();
}
} // namespace
+73
View File
@@ -0,0 +1,73 @@
/*
* 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<tuple<Size, int, int>> Filter2DPerfTest_DSP;
PERF_TEST_P(Filter2DPerfTest_DSP, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p), // image size
::testing::Values(CV_8U,CV_16S,CV_32F), // dst image depth
::testing::Values(3, 5, 7) // kernel size
)
)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
cv::Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int ksize = get<2>(GetParam());
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(srcSize, CV_8U);
cv::Mat kernel;
cv::Mat dst;
kernel.allocator = cv::fastcv::getQcAllocator();
dst.allocator = cv::fastcv::getQcAllocator();
switch (ddepth)
{
case CV_8U:
case CV_16S:
{
kernel.create(ksize,ksize,CV_8S);
break;
}
case CV_32F:
{
kernel.create(ksize,ksize,CV_32F);
break;
}
default:
break;
}
cv::randu(src, 0, 256);
cv::randu(kernel, INT8_MIN, INT8_MAX);
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
while (next())
{
startTimer();
cv::fastcv::dsp::filter2D(src, dst, ddepth, kernel);
stopTimer();
}
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
SANITY_CHECK_NOTHING();
}
} // namespace
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<int /* nPts */, int /*nDims*/, int /*nClusters*/> ClusterEuclideanPerfParams;
typedef perf::TestBaseWithParam<ClusterEuclideanPerfParams> ClusterEuclideanPerfTest;
PERF_TEST_P(ClusterEuclideanPerfTest, run,
::testing::Combine(::testing::Values(100, 1000, 10000), // nPts
::testing::Values(2, 10, 32), // nDims
::testing::Values(5, 10, 16)) // nClusters
)
{
auto p = GetParam();
int nPts = std::get<0>(p);
int nDims = std::get<1>(p);
int nClusters = std::get<2>(p);
Mat points(nPts, nDims, CV_8U);
Mat clusterCenters(nClusters, nDims, CV_32F);
Mat trueMeans(nClusters, nDims, CV_32F);
Mat stddevs(nClusters, nDims, CV_32F);
std::vector<int> trueClusterSizes(nClusters, 0);
std::vector<int> trueClusterBindings(nPts, 0);
std::vector<float> trueSumDists(nClusters, 0);
cv::RNG& rng = cv::theRNG();
for (int i = 0; i < nClusters; i++)
{
Mat mean(1, nDims, CV_64F), stdev(1, nDims, CV_64F);
rng.fill(mean, cv::RNG::UNIFORM, 0, 256);
rng.fill(stdev, cv::RNG::UNIFORM, 5.f, 16);
int lo = i * nPts / nClusters;
int hi = (i + 1) * nPts / nClusters;
for (int d = 0; d < nDims; d++)
{
rng.fill(points.col(d).rowRange(lo, hi), cv::RNG::NORMAL,
mean.at<double>(d), stdev.at<double>(d));
}
float sd = 0;
for (int j = lo; j < hi; j++)
{
Mat pts64f;
points.row(j).convertTo(pts64f, CV_64F);
sd += cv::norm(mean, pts64f, NORM_L2);
trueClusterBindings.at(j) = i;
trueClusterSizes.at(i)++;
}
trueSumDists.at(i) = sd;
// let's shift initial cluster center a bit
Mat(mean + stdev * 0.5).copyTo(clusterCenters.row(i));
mean.copyTo(trueMeans.row(i));
stdev.copyTo(stddevs.row(i));
}
while(next())
{
Mat newClusterCenters;
std::vector<int> clusterSizes, clusterBindings;
std::vector<float> clusterSumDists;
startTimer();
cv::fastcv::clusterEuclidean(points, clusterCenters, newClusterCenters, clusterSizes, clusterBindings, clusterSumDists);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef perf::TestBaseWithParam<tuple<Size, int, int, int>> SobelPerfTest;
PERF_TEST_P(SobelPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(3,5,7), // kernel size
::testing::Values(BORDER_CONSTANT, BORDER_REPLICATE), // border type
::testing::Values(0) // border value
)
)
{
Size srcSize = get<0>(GetParam());
int ksize = get<1>(GetParam());
int border = get<2>(GetParam());
int borderValue = get<3>(GetParam());
cv::Mat dx, dy, src(srcSize, CV_8U);
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
while (next())
{
startTimer();
cv::fastcv::sobel(src,dx,dy,ksize,border,borderValue);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef perf::TestBaseWithParam<tuple<Size, int, int>> Sobel3x3u8PerfTest;
PERF_TEST_P(Sobel3x3u8PerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(CV_8S, CV_16S, CV_32F), // image depth
::testing::Values(0, 1) // normalization
)
)
{
Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int normalization = get<2>(GetParam());
cv::Mat dx, dy, src(srcSize, CV_8U);
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
if((normalization ==0) && (ddepth == CV_8S))
throw ::perf::TestBase::PerfSkipTestException();
while (next())
{
startTimer();
cv::fastcv::sobel3x3u8(src, dx, dy, ddepth, normalization);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} //namespace
+56
View File
@@ -0,0 +1,56 @@
/*
* 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<tuple<Size, int, pair<int, int>, bool>> CannyPerfTest;
PERF_TEST_P(CannyPerfTest, run,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // image size
::testing::Values(3, 5, 7), // aperture size
::testing::Values(make_pair(0, 50), make_pair(100, 150), make_pair(50, 150)), // low and high thresholds
::testing::Values(false, true) // L2gradient
)
)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
cv::Size srcSize = get<0>(GetParam());
int apertureSize = get<1>(GetParam());
auto thresholds = get<2>(GetParam());
bool L2gradient = get<3>(GetParam());
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(srcSize, CV_8UC1);
cv::Mat dst;
dst.allocator = cv::fastcv::getQcAllocator();
cv::randu(src, 0, 256);
int lowThreshold = thresholds.first;
int highThreshold = thresholds.second;
while (next())
{
startTimer();
cv::fastcv::dsp::Canny(src, dst, lowThreshold, highThreshold, apertureSize, L2gradient);
stopTimer();
}
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
SANITY_CHECK_NOTHING();
}
} //namespace
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<bool /*useScores*/, int /*barrier*/, int /*border*/, bool /*nmsEnabled*/> FAST10PerfParams;
typedef perf::TestBaseWithParam<FAST10PerfParams> FAST10PerfTest;
PERF_TEST_P(FAST10PerfTest, run,
::testing::Combine(::testing::Bool(), // useScores
::testing::Values(10, 30, 50), // barrier
::testing::Values( 4, 10, 32), // border
::testing::Bool() // nonmax suppression
)
)
{
auto p = GetParam();
bool useScores = std::get<0>(p);
int barrier = std::get<1>(p);
int border = std::get<2>(p);
bool nmsEnabled = std::get<3>(p);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
std::vector<int> coords, scores;
while(next())
{
coords.clear();
scores.clear();
startTimer();
cv::fastcv::FAST10(src, noArray(), coords, useScores ? scores : noArray(), barrier, border, nmsEnabled);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef perf::TestBaseWithParam<cv::Size> FFTExtPerfTest;
PERF_TEST_P_(FFTExtPerfTest, forward)
{
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat dst;
while(next())
{
startTimer();
cv::fastcv::FFT(src, dst);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FFTExtPerfTest, inverse)
{
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat fwd, back;
cv::fastcv::FFT(src, fwd);
while(next())
{
startTimer();
cv::fastcv::IFFT(fwd, back);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, FFTExtPerfTest,
::testing::Values(Size(8, 8), Size(128, 128), Size(32, 256), Size(512, 512),
Size(32, 1), Size(512, 1)));
/// DCT ///
typedef perf::TestBaseWithParam<cv::Size> DCTExtPerfTest;
PERF_TEST_P_(DCTExtPerfTest, forward)
{
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat dst, ref;
while(next())
{
startTimer();
cv::fastcv::DCT(src, dst);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(DCTExtPerfTest, inverse)
{
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat fwd, back;
cv::fastcv::DCT(src, fwd);
while(next())
{
startTimer();
cv::fastcv::IDCT(fwd, back);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, DCTExtPerfTest,
::testing::Values(Size(8, 8), Size(128, 128), Size(32, 256), Size(512, 512)));
} // namespace
+85
View File
@@ -0,0 +1,85 @@
/*
* 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<cv::Size> FFT_DSPExtPerfTest;
PERF_TEST_P_(FFT_DSPExtPerfTest, forward)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat dst;
dst.allocator = cv::fastcv::getQcAllocator();
while (next())
{
startTimer();
cv::fastcv::dsp::FFT(src, dst);
stopTimer();
}
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FFT_DSPExtPerfTest, inverse)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
Size size = GetParam();
RNG& rng = cv::theRNG();
Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat fwd, back;
fwd.allocator = cv::fastcv::getQcAllocator();
back.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::dsp::FFT(src, fwd);
while (next())
{
startTimer();
cv::fastcv::dsp::IFFT(fwd, back);
stopTimer();
}
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, FFT_DSPExtPerfTest,
::testing::Values(Size(256, 256), Size(512, 512)));
} // namespace
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef tuple<cv::Size /*imgSize*/, int /*nPts*/, int /*channels*/> FillConvexPerfParams;
typedef perf::TestBaseWithParam<FillConvexPerfParams> FillConvexPerfTest;
PERF_TEST_P(FillConvexPerfTest, randomDraw, Combine(
testing::Values(Size(640, 480), Size(512, 512), Size(1920, 1080)),
testing::Values(4, 64, 1024),
testing::Values(1, 2, 3, 4)
))
{
auto p = GetParam();
Size imgSize = std::get<0>(p);
int nPts = std::get<1>(p);
int channels = std::get<2>(p);
cv::RNG rng = cv::theRNG();
std::vector<Point> allPts, contour;
for (int i = 0; i < nPts; i++)
{
allPts.push_back(Point(rng() % imgSize.width, rng() % imgSize.height));
}
cv::convexHull(allPts, contour);
Scalar color(rng() % 256, rng() % 256, rng() % 256);
Mat img(imgSize, CV_MAKE_TYPE(CV_8U, channels), Scalar(0));
while(next())
{
img = 0;
startTimer();
cv::fastcv::fillConvexPoly(img, contour, color);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(FillConvexPerfTest, circle, Combine(
testing::Values(Size(640, 480), Size(512, 512), Size(1920, 1080)),
testing::Values(4, 64, 1024),
testing::Values(1, 2, 3, 4)
))
{
auto p = GetParam();
Size imgSize = std::get<0>(p);
int nPts = std::get<1>(p);
int channels = std::get<2>(p);
cv::RNG rng = cv::theRNG();
float r = std::min(imgSize.width, imgSize.height) / 2 * 0.9f;
float angle = CV_PI * 2.0f / (float)nPts;
std::vector<Point2i> contour;
for (int i = 0; i < nPts; i++)
{
Point2f pt(r * cos((float)i * angle),
r * sin((float)i * angle));
contour.push_back({ imgSize.width / 2 + int(pt.x),
imgSize.height / 2 + int(pt.y)});
}
Scalar color(rng() % 256, rng() % 256, rng() % 256);
Mat img(imgSize, CV_MAKE_TYPE(CV_8U, channels), Scalar(0));
while(next())
{
img = 0;
startTimer();
cv::fastcv::fillConvexPoly(img, contour, color);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<cv::Size> HistogramPerfParams;
typedef perf::TestBaseWithParam<HistogramPerfParams> HistogramPerfTest;
PERF_TEST_P(HistogramPerfTest, run,
testing::Values(perf::szQVGA, perf::szVGA, perf::sz720p, perf::sz1080p)
)
{
auto p = GetParam();
cv::Size size = std::get<0>(p);
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
Mat hist(1, 256, CV_32SC1);
while (next())
{
startTimer();
cv::fastcv::calcHist(src, hist);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<std::string /* file name */, double /* threshold */ > HoughLinesPerfParams;
typedef perf::TestBaseWithParam<HoughLinesPerfParams> HoughLinesPerfTest;
PERF_TEST_P(HoughLinesPerfTest, run,
::testing::Combine(::testing::Values("cv/shared/pic5.png",
"stitching/a1.png",
"cv/shared/pic5.png",
"cv/shared/pic1.png"), // images
::testing::Values(0.05, 0.25, 0.5, 0.75, 5) // threshold
)
)
{
auto p = GetParam();
std::string fname = std::get<0>(p);
double thrld = std::get<1>(p);
cv::Mat src = imread(cvtest::findDataFile(fname), cv::IMREAD_GRAYSCALE);
// make it aligned by 8
cv::Mat withBorder;
int bpix = ((src.cols & 0xfffffff8) + 8) - src.cols;
cv::copyMakeBorder(src, withBorder, 0, 0, 0, bpix, BORDER_REFLECT101);
src = withBorder;
while(next())
{
std::vector<cv::Vec4f> lines;
startTimer();
cv::fastcv::houghLines(src, lines, thrld);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+13
View File
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
static void initFastCVTests()
{
cvtest::registerGlobalSkipTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
}
CV_PERF_TEST_MAIN(imgproc, initFastCVTests())
+73
View File
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<int /*rows1*/, int /*cols1*/, int /*cols2*/> MatMulPerfParams;
typedef perf::TestBaseWithParam<MatMulPerfParams> MatMulPerfTest;
typedef std::tuple<int /*rows1*/, int /*cols1*/, int /*cols2*/, float> MatMulGemmPerfParams;
typedef perf::TestBaseWithParam<MatMulGemmPerfParams> MatMulGemmPerfTest;
PERF_TEST_P(MatMulPerfTest, run,
::testing::Combine(::testing::Values(8, 16, 128, 256), // rows1
::testing::Values(8, 16, 128, 256), // cols1
::testing::Values(8, 16, 128, 256)) // cols2
)
{
auto p = GetParam();
int rows1 = std::get<0>(p);
int cols1 = std::get<1>(p);
int cols2 = std::get<2>(p);
RNG& rng = cv::theRNG();
Mat src1(rows1, cols1, CV_8SC1), src2(cols1, cols2, CV_8SC1);
cvtest::randUni(rng, src1, Scalar::all(-128), Scalar::all(128));
cvtest::randUni(rng, src2, Scalar::all(-128), Scalar::all(128));
Mat dst;
while(next())
{
startTimer();
cv::fastcv::matmuls8s32(src1, src2, dst);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(MatMulGemmPerfTest, run,
::testing::Combine(::testing::Values(8, 16, 128, 256), // rows1
::testing::Values(8, 16, 128, 256), // cols1
::testing::Values(8, 16, 128, 256), // cols2
::testing::Values(2.5, 5.8)) // alpha
)
{
auto p = GetParam();
int rows1 = std::get<0>(p);
int cols1 = std::get<1>(p);
int cols2 = std::get<2>(p);
float alpha = std::get<3>(p);
RNG& rng = cv::theRNG();
Mat src1(rows1, cols1, CV_32FC1), src2(cols1, cols2, CV_32FC1);
cvtest::randUni(rng, src1, Scalar::all(-128.0), Scalar::all(128.0));
cvtest::randUni(rng, src2, Scalar::all(-128.0), Scalar::all(128.0));
Mat dst;
while (next())
{
startTimer();
cv::fastcv::gemm(src1, src2, dst, alpha, noArray(), 0);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<cv::Size, MatType, int /*iterations*/, float /*epsilon*/, Size /*winSize*/> MeanShiftPerfParams;
typedef perf::TestBaseWithParam<MeanShiftPerfParams> MeanShiftPerfTest;
PERF_TEST_P(MeanShiftPerfTest, run,
::testing::Combine(::testing::Values(Size(128, 128), Size(640, 480), Size(800, 600)),
::testing::Values(CV_8U, CV_32S, CV_32F), // type
::testing::Values(2, 10, 100), // nIterations
::testing::Values(0.01f, 0.1f, 1.f, 10.f), // epsilon
::testing::Values(Size(8, 8), Size(13, 48), Size(64, 64)) // window size
)
)
{
auto p = GetParam();
cv::Size size = std::get<0>(p);
MatType type = std::get<1>(p);
int iters = std::get<2>(p);
float eps = std::get<3>(p);
Size winSize = std::get<4>(p);
RNG& rng = cv::theRNG();
const int nPts = 20;
Mat ptsMap(size, CV_8UC1, Scalar(255));
for(size_t i = 0; i < nPts; ++i)
{
ptsMap.at<uchar>(rng() % size.height, rng() % size.width) = 0;
}
Mat distTrans(size, CV_8UC1);
cv::distanceTransform(ptsMap, distTrans, DIST_L2, DIST_MASK_PRECISE);
Mat vsrc = 255 - distTrans;
Mat src;
vsrc.convertTo(src, type);
Point startPt(rng() % (size.width - winSize.width),
rng() % (size.height - winSize.height));
Rect startRect(startPt, winSize);
cv::TermCriteria termCrit( TermCriteria::EPS + TermCriteria::MAX_ITER, iters, eps);
Rect window = startRect;
while(next())
{
startTimer();
cv::fastcv::meanShift(src, window, termCrit);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
// we use such nested structure to combine test values
typedef std::tuple< std::tuple<bool /* useBboxes */, bool /* useContourData */>,
int /* numNeighbors */, std::string /*file path*/> MSERPerfParams;
typedef perf::TestBaseWithParam<MSERPerfParams> MSERPerfTest;
PERF_TEST_P(MSERPerfTest, run,
::testing::Combine(::testing::Values(std::tuple<bool, bool> { true, false},
std::tuple<bool, bool> {false, false},
std::tuple<bool, bool> { true, true}
), // useBboxes, useContourData
::testing::Values(4, 8), // numNeighbors
::testing::Values("cv/shared/baboon.png", "cv/mser/puzzle.png")
)
)
{
auto p = GetParam();
bool useBboxes = std::get<0>(std::get<0>(p));
bool useContourData = std::get<1>(std::get<0>(p));
int numNeighbors = std::get<1>(p); // 4 or 8
std::string imgPath = std::get<2>(p);
cv::Mat src = imread(cvtest::findDataFile(imgPath), cv::IMREAD_GRAYSCALE);
uint32_t delta = 2;
uint32_t minArea = 256;
uint32_t maxArea = (int)src.total()/4;
float maxVariation = 0.15f;
float minDiversity = 0.2f;
cv::Ptr<cv::fastcv::FCVMSER> mser;
mser = cv::fastcv::FCVMSER::create(src.size(), numNeighbors, delta, minArea, maxArea,
maxVariation, minDiversity);
while(next())
{
std::vector<std::vector<Point>> contours;
std::vector<cv::Rect> bboxes;
std::vector<cv::fastcv::FCVMSER::ContourData> contourData;
startTimer();
if (useBboxes)
{
if (useContourData)
{
mser->detect(src, contours, bboxes, contourData);
}
else
{
mser->detect(src, contours, bboxes);
}
}
else
{
mser->detect(src, contours);
}
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __FASTCV_EXT_PERF_PRECOMP_HPP__
#define __FASTCV_EXT_PERF_PRECOMP_HPP__
#include <opencv2/ts.hpp>
#include <opencv2/geometry.hpp>
#include <opencv2/features.hpp>
#include <opencv2/fastcv.hpp>
namespace opencv_test {
using namespace perf;
} // namespace
#define CV_TEST_TAG_FASTCV_SKIP_DSP "fastcv_skip_dsp"
#endif
+76
View File
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<bool /*useFloat*/, int /*nLevels*/, bool /*scaleBy2*/> PyramidTestParams;
class PyramidTest : public ::perf::TestBaseWithParam<PyramidTestParams> { };
PERF_TEST_P(PyramidTest, checkAllVersions, // version, useFloat, nLevels
::testing::Values(
PyramidTestParams { true, 2, true}, PyramidTestParams { true, 3, true}, PyramidTestParams { true, 4, true},
PyramidTestParams {false, 2, true}, PyramidTestParams {false, 3, true}, PyramidTestParams {false, 4, true},
PyramidTestParams {false, 2, false}, PyramidTestParams {false, 3, false}, PyramidTestParams {false, 4, false}
))
{
auto par = GetParam();
bool useFloat = std::get<0>(par);
int nLevels = std::get<1>(par);
bool scaleBy2 = std::get<2>(par);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
if (useFloat)
{
cv::Mat f;
src.convertTo(f, CV_32F);
src = f;
}
while(next())
{
std::vector<cv::Mat> pyr;
startTimer();
cv::fastcv::buildPyramid(src, pyr, nLevels, scaleBy2);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef std::tuple<MatType, size_t> SobelPyramidTestParams;
class SobelPyramidTest : public ::perf::TestBaseWithParam<SobelPyramidTestParams> {};
PERF_TEST_P(SobelPyramidTest, checkAllTypes,
::testing::Combine(::testing::Values(CV_8S, CV_16S, CV_32F),
::testing::Values(3, 6)))
{
auto p = GetParam();
int type = std::get<0>(p);
size_t nLevels = std::get<1>(p);
// NOTE: test files should be manually loaded to folder on a device, for example like this:
// adb push fastcv/misc/bilateral_recursive/ /sdcard/testdata/fastcv/bilateral/
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
std::vector<cv::Mat> pyr;
cv::fastcv::buildPyramid(src, pyr, nLevels);
while(next())
{
std::vector<cv::Mat> pyrDx, pyrDy;
startTimer();
cv::fastcv::sobelPyramid(pyr, pyrDx, pyrDy, type);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<cv::Size /*srcSize*/> SumOfAbsDiffsPerfParams;
typedef perf::TestBaseWithParam<SumOfAbsDiffsPerfParams> SumOfAbsDiffsPerfTest;
PERF_TEST_P(SumOfAbsDiffsPerfTest, run,
::testing::Values(cv::Size(640, 480), // VGA
cv::Size(1280, 720), // 720p
cv::Size(1920, 1080)) // 1080p
)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
// Initialize FastCV DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
auto p = GetParam();
cv::Size srcSize = std::get<0>(p);
RNG& rng = cv::theRNG();
cv::Mat patch, src;
patch.allocator = cv::fastcv::getQcAllocator(); // Use FastCV allocator for patch
src.allocator = cv::fastcv::getQcAllocator(); // Use FastCV allocator for src
patch.create(8, 8, CV_8UC1);
src.create(srcSize, CV_8UC1);
cvtest::randUni(rng, patch, cv::Scalar::all(0), cv::Scalar::all(255));
cvtest::randUni(rng, src, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat dst;
dst.allocator = cv::fastcv::getQcAllocator(); // Use FastCV allocator for dst
while(next())
{
startTimer();
cv::fastcv::dsp::sumOfAbsoluteDiffs(patch, src, dst);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+66
View File
@@ -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
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<cv::Size, bool /*type*/> ThresholdOtsuPerfParams;
typedef perf::TestBaseWithParam<ThresholdOtsuPerfParams> ThresholdOtsuPerfTest;
PERF_TEST_P(ThresholdOtsuPerfTest, run,
::testing::Combine(::testing::Values(Size(320, 240), Size(640, 480), Size(1280, 720), Size(1920, 1080)),
::testing::Values(false, true) // type
)
)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
auto p = GetParam();
cv::Size size = std::get<0>(p);
bool type = std::get<1>(p);
RNG& rng = cv::theRNG();
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
cv::Mat dst;
dst.allocator = cv::fastcv::getQcAllocator();
while (next())
{
startTimer();
cv::fastcv::dsp::thresholdOtsu(src, dst, type);
stopTimer();
}
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
SANITY_CHECK_NOTHING();
}
} // namespace
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<cv::Size, int /*lowThresh*/, int /*highThresh*/, int /*trueValue*/, int /*falseValue*/> ThresholdRangePerfParams;
typedef perf::TestBaseWithParam<ThresholdRangePerfParams> ThresholdRangePerfTest;
PERF_TEST_P(ThresholdRangePerfTest, run,
::testing::Combine(::testing::Values(Size(8, 8), Size(640, 480), Size(800, 600)),
::testing::Values(0, 15, 128, 255), // lowThresh
::testing::Values(0, 15, 128, 255), // highThresh
::testing::Values(0, 15, 128, 255), // trueValue
::testing::Values(0, 15, 128, 255) // falseValue
)
)
{
auto p = GetParam();
cv::Size size = std::get<0>(p);
int loThresh = std::get<1>(p);
int hiThresh = std::get<2>(p);
int trueValue = std::get<3>(p);
int falseValue = std::get<4>(p);
int lowThresh = std::min(loThresh, hiThresh);
int highThresh = std::max(loThresh, hiThresh);
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256));
Mat dst;
while(next())
{
startTimer();
cv::fastcv::thresholdRange(src, dst, lowThresh, highThresh, trueValue, falseValue);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
typedef std::tuple<int /*winSize*/, bool /*useSobelPyramid*/, bool /*useInitialEstimate*/ > TrackingTestParams;
class TrackingTest : public ::perf::TestBaseWithParam<TrackingTestParams> {};
PERF_TEST_P(TrackingTest, checkAllVersions,
::testing::Combine(::testing::Values(5, 7, 9), // window size
::testing::Bool(), // useSobelPyramid
::testing::Bool() // useInitialEstimate
))
{
auto par = GetParam();
int winSz = std::get<0>(par);
bool useSobelPyramid = std::get<1>(par);
bool useInitialEstimate = std::get<2>(par);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
double ang = 5.0 * CV_PI / 180.0;
cv::Matx33d tr = {
cos(ang), -sin(ang), 1,
sin(ang), cos(ang), 2,
0, 0, 1
};
cv::Matx33d orig {
1, 0, -(double)src.cols / 2,
0, 1, -(double)src.rows / 2,
0, 0, 1
};
cv::Matx33d back {
1, 0, (double)src.cols / 2,
0, 1, (double)src.rows / 2,
0, 0, 1
};
cv::Matx23d trans = (back * tr * orig).get_minor<2, 3>(0, 0);
cv::Mat dst;
cv::warpAffine(src, dst, trans, src.size());
int nLevels = 4;
std::vector<cv::Mat> srcPyr, dstPyr;
cv::buildPyramid(src, srcPyr, nLevels - 1);
cv::buildPyramid(dst, dstPyr, nLevels - 1);
cv::Matx23f transf = trans;
int nPts = 32;
std::vector<cv::Point2f> ptsIn, ptsEst, ptsExpected;
for (int i = 0; i < nPts; i++)
{
cv::Point2f p { (((float)cv::theRNG())*0.5f + 0.25f) * src.cols,
(((float)cv::theRNG())*0.5f + 0.25f) * src.rows };
ptsIn.push_back(p);
ptsExpected.push_back(transf * cv::Vec3f(p.x, p.y, 1.0));
ptsEst.push_back(p);
}
cv::TermCriteria termCrit;
termCrit.type = cv::TermCriteria::COUNT | cv::TermCriteria::EPS;
termCrit.maxCount = 7;
termCrit.epsilon = 0.03f * 0.03f;
std::vector<cv::Mat> srcDxPyr, srcDyPyr;
if (useSobelPyramid)
{
cv::fastcv::sobelPyramid(srcPyr, srcDxPyr, srcDyPyr, CV_8S);
}
while(next())
{
std::vector<int32_t> statusVec(nPts);
std::vector<cv::Point2f> ptsOut(nPts);
startTimer();
if (useSobelPyramid)
{
cv::fastcv::trackOpticalFlowLK(src, dst, srcPyr, dstPyr, srcDxPyr, srcDyPyr,
ptsIn, ptsOut, statusVec, {winSz, winSz});
}
else
{
cv::fastcv::trackOpticalFlowLK(src, dst, srcPyr, dstPyr, ptsIn, ptsOut, (useInitialEstimate ? ptsEst : noArray()),
statusVec, {winSz, winSz}, termCrit);
}
stopTimer();
}
SANITY_CHECK_NOTHING();
}
} // namespace
+236
View File
@@ -0,0 +1,236 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "perf_precomp.hpp"
namespace opencv_test {
static void getInvertMatrix(Mat& src, Size dstSize, Mat& M)
{
RNG& rng = cv::theRNG();
Point2f s[4], d[4];
s[0] = Point2f(0,0);
d[0] = Point2f(0,0);
s[1] = Point2f(src.cols-1.f,0);
d[1] = Point2f(dstSize.width-1.f,0);
s[2] = Point2f(src.cols-1.f,src.rows-1.f);
d[2] = Point2f(dstSize.width-1.f,dstSize.height-1.f);
s[3] = Point2f(0,src.rows-1.f);
d[3] = Point2f(0,dstSize.height-1.f);
float buffer[16];
Mat tmp( 1, 16, CV_32FC1, buffer );
rng.fill( tmp, 1, Scalar::all(0.), Scalar::all(0.1) );
for(int i = 0; i < 4; i++ )
{
s[i].x += buffer[i*4]*src.cols/2;
s[i].y += buffer[i*4+1]*src.rows/2;
d[i].x += buffer[i*4+2]*dstSize.width/2;
d[i].y += buffer[i*4+3]*dstSize.height/2;
}
cv::getPerspectiveTransform( s, d ).convertTo( M, M.depth() );
// Invert the perspective matrix
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,
::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p))
{
cv::Size dstSize = GetParam();
cv::Mat img = imread(cvtest::findDataFile("cv/shared/baboon.png"));
Mat src(img.rows, img.cols, CV_8UC1);
cvtColor(img,src,cv::COLOR_BGR2GRAY);
cv::Mat dst1, dst2, matrix;
matrix.create(3,3,CV_32FC1);
getInvertMatrix(src, dstSize, matrix);
while (next())
{
startTimer();
cv::fastcv::warpPerspective2Plane(src, src, dst1, dst2, matrix, dstSize);
stopTimer();
}
SANITY_CHECK_NOTHING();
}
typedef perf::TestBaseWithParam<tuple<Size, int, int>> WarpPerspectivePerfTest;
PERF_TEST_P(WarpPerspectivePerfTest, run,
::testing::Combine( ::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p),
::testing::Values(INTER_NEAREST, INTER_LINEAR, INTER_AREA),
::testing::Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_TRANSPARENT)))
{
cv::Size dstSize = get<0>(GetParam());
int interplation = get<1>(GetParam());
int borderType = get<2>(GetParam());
cv::Scalar borderValue = Scalar::all(100);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
EXPECT_FALSE(src.empty());
cv::Mat dst, matrix, ref;
matrix.create(3, 3, CV_32FC1);
getInvertMatrix(src, dstSize, matrix);
while (next())
{
startTimer();
cv::fastcv::warpPerspective(src, dst, matrix, dstSize, interplation, borderType, borderValue);
stopTimer();
}
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<uint8_t>(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
+111
View File
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
QcResourceManager& QcResourceManager::getInstance() {
static QcResourceManager instance;
return instance;
}
void QcResourceManager::addAllocation(void* ptr) {
std::lock_guard<std::mutex> lock(resourceMutex);
activeAllocations.insert(ptr);
CV_LOG_DEBUG(NULL, cv::format("Active Allocations: %zu", activeAllocations.size()));
}
void QcResourceManager::removeAllocation(void* ptr) {
std::lock_guard<std::mutex> lock(resourceMutex);
activeAllocations.erase(ptr);
CV_LOG_DEBUG(NULL, cv::format("Active Allocations: %zu", activeAllocations.size()));
}
QcAllocator::QcAllocator()
{
}
QcAllocator::~QcAllocator()
{
}
cv::UMatData* QcAllocator::allocate(int dims, const int* sizes, int type,
void* data0, size_t* step, cv::AccessFlag flags,
cv::UMatUsageFlags usageFlags) const
{
CV_UNUSED(flags);
CV_UNUSED(usageFlags);
size_t total = CV_ELEM_SIZE(type);
for( int i = dims-1; i >= 0; i-- )
{
if( step )
{
if( data0 && step[i] != cv::Mat::AUTO_STEP )
{
CV_Assert(total <= step[i]);
total = step[i];
}
else
step[i] = total;
}
total *= sizes[i];
}
int fd = -1;
uchar* data = data0 ? (uchar*)data0 : (uchar*)fcvHwMemAlloc(total, 16, &fd);
cv::UMatData* u = new cv::UMatData(this);
u->data = u->origdata = data;
u->size = total;
if(data0)
u->flags |= cv::UMatData::USER_ALLOCATED;
// Store FD in userdata (cast to void*)
if (fd >= 0)
u->userdata = reinterpret_cast<void*>(static_cast<intptr_t>(fd));
// Add to active allocations
cv::fastcv::QcResourceManager::getInstance().addAllocation(data);
return u;
}
bool QcAllocator::allocate(cv::UMatData* u, cv::AccessFlag accessFlags, cv::UMatUsageFlags usageFlags) const
{
CV_UNUSED(accessFlags);
CV_UNUSED(usageFlags);
return u != nullptr;
}
void QcAllocator::deallocate(cv::UMatData* u) const
{
if(!u)
return;
CV_Assert(u->urefcount == 0);
CV_Assert(u->refcount == 0);
if( !(u->flags & cv::UMatData::USER_ALLOCATED) )
{
fcvHwMemFree(u->origdata);
// Remove from active allocations
cv::fastcv::QcResourceManager::getInstance().removeAllocation(u->origdata);
u->origdata = 0;
}
delete u;
}
cv::MatAllocator* getQcAllocator()
{
static cv::MatAllocator* allocator = new QcAllocator;
return allocator;
}
}
}
+208
View File
@@ -0,0 +1,208 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void matmuls8s32(InputArray _src1, InputArray _src2, OutputArray _dst)
{
INITIALIZATION_CHECK;
CV_Assert(!_src1.empty() && _src1.type() == CV_8SC1);
CV_Assert(_src1.cols() <= 131072);
CV_Assert(_src1.step() % 8 == 0);
CV_Assert(_src1.cols() == _src2.rows());
Mat src1 = _src1.getMat();
CV_Assert(!_src2.empty() && _src2.type() == CV_8SC1);
CV_Assert(_src2.step() % 8 == 0);
Mat src2 = _src2.getMat();
_dst.create(_src1.rows(), _src2.cols(), CV_32SC1);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvMatrixMultiplys8s32((const int8_t*)src1.data, src1.cols, src1.rows, src1.step,
(const int8_t*)src2.data, src2.cols, src2.step,
(int32_t*)dst.data, dst.step);
}
void arithmetic_op(InputArray _src1, InputArray _src2, OutputArray _dst, int op)
{
CV_Assert(!_src1.empty() && (_src1.depth() == CV_8U || _src1.depth() == CV_16S || _src1.depth() == CV_32F));
CV_Assert(!_src2.empty() && _src2.type() == _src1.type());
CV_Assert(_src2.size() == _src1.size());
Mat src1 = _src1.getMat();
Mat src2 = _src2.getMat();
_dst.create(_src1.rows(), _src1.cols(), _src1.type());
Mat dst = _dst.getMat();
INITIALIZATION_CHECK;
fcvConvertPolicy policy = FASTCV_CONVERT_POLICY_SATURATE;
int nStripes = cv::getNumThreads();
int func = FCV_OPTYPE(_src1.depth(), op);
switch(func)
{
case FCV_OPTYPE(CV_8U, 0):
cv::parallel_for_(cv::Range(0, src1.rows), [&](const cv::Range &range){
int rangeHeight = range.end - range.start;
const uchar* yS1 = src1.data + static_cast<size_t>(range.start)*src1.step[0];
const uchar* yS2 = src2.data + static_cast<size_t>(range.start)*src2.step[0];
uchar* yD = dst.data + static_cast<size_t>(range.start)*dst.step[0];
fcvAddu8(yS1, src1.cols, rangeHeight, src1.step[0],
yS2, src2.step[0], policy, yD, dst.step[0]);
}, nStripes);
break;
case FCV_OPTYPE(CV_16S, 0):
cv::parallel_for_(cv::Range(0, src1.rows), [&](const cv::Range &range){
int rangeHeight = range.end - range.start;
const short* yS1 = (short*)src1.data + static_cast<size_t>(range.start)*(src1.step[0]/sizeof(short));
const short* yS2 = (short*)src2.data + static_cast<size_t>(range.start)*(src2.step[0]/sizeof(short));
short* yD = (short*)dst.data + static_cast<size_t>(range.start)*(dst.step[0]/sizeof(short));
fcvAdds16_v2(yS1, src1.cols, rangeHeight, src1.step[0],
yS2, src2.step[0], policy, yD, dst.step[0]);
}, nStripes);
break;
case FCV_OPTYPE(CV_32F, 0):
cv::parallel_for_(cv::Range(0, src1.rows), [&](const cv::Range &range){
int rangeHeight = range.end - range.start;
const float* yS1 = (float*)src1.data + static_cast<size_t>(range.start)*(src1.step[0]/sizeof(float));
const float* yS2 = (float*)src2.data + static_cast<size_t>(range.start)*(src2.step[0]/sizeof(float));
float* yD = (float*)dst.data + static_cast<size_t>(range.start)*(dst.step[0]/sizeof(float));
fcvAddf32(yS1, src1.cols, rangeHeight, src1.step[0],
yS2, src2.step[0], yD, dst.step[0]);
}, nStripes);
break;
case FCV_OPTYPE(CV_8U, 1):
cv::parallel_for_(cv::Range(0, src1.rows), [&](const cv::Range &range){
int rangeHeight = range.end - range.start;
const uchar* yS1 = src1.data + static_cast<size_t>(range.start)*src1.step[0];
const uchar* yS2 = src2.data + static_cast<size_t>(range.start)*src2.step[0];
uchar* yD = dst.data + static_cast<size_t>(range.start)*dst.step[0];
fcvSubtractu8(yS1, src1.cols, rangeHeight, src1.step[0],
yS2, src2.step[0], policy, yD, dst.step[0]);
}, nStripes);
break;
case FCV_OPTYPE(CV_16S, 1):
cv::parallel_for_(cv::Range(0, src1.rows), [&](const cv::Range &range){
int rangeHeight = range.end - range.start;
const short* yS1 = (short*)src1.data + static_cast<size_t>(range.start)*(src1.step[0]/sizeof(short));
const short* yS2 = (short*)src2.data + static_cast<size_t>(range.start)*(src2.step[0]/sizeof(short));
short* yD = (short*)dst.data + static_cast<size_t>(range.start)*(dst.step[0]/sizeof(short));
fcvSubtracts16(yS1, src1.cols, rangeHeight, src1.step[0],
yS2, src2.step[0], policy, yD, dst.step[0]);
}, nStripes);
break;
default:
CV_Error(cv::Error::StsBadArg, cv::format("op type is not supported"));
break;
}
}
void gemm(InputArray _src1, InputArray _src2, OutputArray _dst, float alpha, InputArray _src3, float beta)
{
CV_Assert(!_src1.empty() && _src1.type() == CV_32FC1);
CV_Assert(_src1.cols() == _src2.rows());
Mat src1 = _src1.getMat();
CV_Assert(!_src2.empty() && _src2.type() == CV_32FC1);
Mat src2 = _src2.getMat();
bool isSrc3 = !_src3.empty();
Mat src3 = _src3.getMat();
_dst.create(_src1.rows(), _src2.cols(), CV_32FC1);
Mat dst = _dst.getMat();
CV_Assert(!FCV_CMP_EQ(alpha,0));
cv::Mat dst_temp1, dst_temp2;
float *dstp = NULL;
bool inplace = false;
size_t dst_stride;
fcvStatus status = FASTCV_SUCCESS;
int n = src1.cols, m = src1.rows, k = src2.cols;
INITIALIZATION_CHECK;
if(src1.data == dst.data || src2.data == dst.data || (isSrc3 && (src3.data == dst.data)))
{
dst_temp1 = cv::Mat(m, k, CV_32FC1);
dstp = dst_temp1.ptr<float>();
inplace = true;
dst_stride = dst_temp1.step[0];
}
else
{
dstp = (float32_t*)dst.data;
dst_stride = dst.step[0];
}
float32_t *dstp1 = dstp;
status = fcvMatrixMultiplyf32_v2((float32_t*)src1.data, n, m, src1.step[0], (float32_t*)src2.data, k,
src2.step[0], dstp, dst_stride);
bool isAlpha = !(FCV_CMP_EQ(alpha,0) || FCV_CMP_EQ(alpha,1));
if(isAlpha && status == FASTCV_SUCCESS)
{
status = fcvMultiplyScalarf32(dstp, k, m, dst_stride, alpha, dstp1, dst_stride);
}
if(isSrc3 && (!FCV_CMP_EQ(beta,0)) && status == FASTCV_SUCCESS)
{
cv::Mat dst3 = cv::Mat(m, k, CV_32FC1);
if(!FCV_CMP_EQ(beta,1))
{
status = fcvMultiplyScalarf32((float32_t*)src3.data, k, m, src3.step[0], beta, (float32_t*)dst3.data, dst3.step[0]);
if(status == FASTCV_SUCCESS)
fcvAddf32_v2(dstp, k, m, dst_stride, (float32_t*)dst3.data, dst3.step[0], dstp1, dst_stride);
}
else
fcvAddf32_v2(dstp, k, m, dst_stride, (float32_t*)src3.data, src3.step[0], dstp1, dst_stride);
}
if(inplace == true)
{
dst_temp1(cv::Rect(0, 0, k, m)).copyTo(dst(cv::Rect(0, 0, k, m)));
}
}
void integrateYUV(InputArray _Y, InputArray _CbCr, OutputArray _IY, OutputArray _ICb, OutputArray _ICr)
{
CV_Assert(!_Y.empty() && !_CbCr.empty());
CV_Assert(_Y.type() == _CbCr.type() && _Y.type() == CV_8UC1);
Mat Y = _Y.getMat();
Mat CbCr = _CbCr.getMat();
int Ywidth = Y.cols;
int Yheight = Y.rows;
INITIALIZATION_CHECK;
_IY.create(Yheight + 1, Ywidth + 1, CV_32SC1);
_ICb.create(Yheight/2 + 1, Ywidth/2 + 1, CV_32SC1);
_ICr.create(Yheight/2 + 1, Ywidth/2 + 1, CV_32SC1);
Mat IY_ = _IY.getMat();
Mat ICb_ = _ICb.getMat();
Mat ICr_ = _ICr.getMat();
fcvIntegrateImageYCbCr420PseudoPlanaru8(Y.data, CbCr.data, Ywidth, Yheight, Y.step[0],
CbCr.step[0], (uint32_t*)IY_.data, (uint32_t*)ICb_.data, (uint32_t*)ICr_.data,
IY_.step[0], ICb_.step[0], ICr_.step[0]);
}
} // fastcv::
} // cv::
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class FcvFilterLoop_Invoker : public cv::ParallelLoopBody
{
public:
FcvFilterLoop_Invoker(cv::Mat src_, size_t src_step_, cv::Mat dst_, size_t dst_step_, int width_, int height_,
int bdr_, int knl_, float32_t sigma_color_, float32_t sigma_space_) :
cv::ParallelLoopBody(), src_step(src_step_), dst_step(dst_step_), width(width_), height(height_),
bdr(bdr_), knl(knl_), sigma_color(sigma_color_), sigma_space(sigma_space_), src(src_), dst(dst_)
{ }
virtual void operator()(const cv::Range& range) const CV_OVERRIDE
{
int height_ = range.end - range.start;
int width_ = width;
cv::Mat src_;
int n = knl/2;
src_ = cv::Mat(height_ + 2 * n, width_ + 2 * n, CV_8U);
if (range.start == 0 && range.end == height)
{
cv::copyMakeBorder(src(cv::Rect(0, 0, width, height)), src_, n, n, n, n, bdr);
}
else if (range.start == 0)
{
cv::copyMakeBorder(src(cv::Rect(0, 0, width_, height_ + n)), src_, n, 0, n, n, bdr);
}
else if (range.end == (height))
{
cv::copyMakeBorder(src(cv::Rect(0, range.start - n, width_, height_ + n)), src_, 0, n, n, n, bdr);
}
else
{
cv::copyMakeBorder(src(cv::Rect(0, range.start - n, width_, height_ + 2 * n)), src_, 0, 0, n, n, bdr);
}
cv::Mat dst_padded = cv::Mat(height_ + 2*n, width_ + 2*n, CV_8U);
auto func = (knl == 5) ? fcvBilateralFilter5x5u8_v3 :
(knl == 7) ? fcvBilateralFilter7x7u8_v3 :
(knl == 9) ? fcvBilateralFilter9x9u8_v3 :
nullptr;
func(src_.data, width_ + 2 * n, height_ + 2 * n, width_ + 2 * n,
dst_padded.data, width_ + 2 * n, sigma_color, sigma_space, 0);
cv::Mat dst_temp1 = dst_padded(cv::Rect(n, n, width_, height_));
cv::Mat dst_temp2 = dst(cv::Rect(0, range.start, width_, height_));
dst_temp1.copyTo(dst_temp2);
}
private:
const size_t src_step;
const size_t dst_step;
const int width;
const int height;
const int bdr;
const int knl;
float32_t sigma_color;
float32_t sigma_space;
int ret;
cv::Mat src;
cv::Mat dst;
FcvFilterLoop_Invoker(const FcvFilterLoop_Invoker &); // = delete;
const FcvFilterLoop_Invoker& operator= (const FcvFilterLoop_Invoker &); // = delete;
};
void bilateralFilter( InputArray _src, OutputArray _dst, int d,
float sigmaColor, float sigmaSpace,
int borderType )
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty());
int type = _src.type();
CV_Assert(type == CV_8UC1);
CV_Assert(d == 5 || d == 7 || d == 9);
Size size = _src.size();
_dst.create( size, type );
Mat src = _src.getMat();
Mat dst = _dst.getMat();
CV_Assert(src.data != dst.data);
if( sigmaColor <= 0 )
sigmaColor = 1;
if( sigmaSpace <= 0 )
sigmaSpace = 1;
int nStripes = (src.rows / 20 == 0) ? 1 : (src.rows / 20);
cv::parallel_for_(cv::Range(0, src.rows),
FcvFilterLoop_Invoker(src, src.step, dst, dst.step, src.cols, src.rows, borderType, d, sigmaColor, sigmaSpace), nStripes);
}
}
}
+386
View File
@@ -0,0 +1,386 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class FcvGaussianBlurLoop_Invoker : public ParallelLoopBody
{
public:
FcvGaussianBlurLoop_Invoker(const Mat& _src, Mat& _dst, int _ksize, fcvBorderType _fcvBorder, int _fcvBorderValue) :
ParallelLoopBody(), src(_src),dst(_dst), ksize(_ksize), fcvBorder(_fcvBorder), fcvBorderValue(_fcvBorderValue)
{
width = src.cols;
height = src.rows;
halfKsize = ksize / 2;
fcvFuncType = FCV_MAKETYPE(ksize, src.depth());
}
virtual void operator()(const Range& range) const CV_OVERRIDE
{
int topLines = 0;
int rangeHeight = range.end-range.start;
int paddedHeight = rangeHeight;
if(range.start != 0)
{
topLines += halfKsize;
paddedHeight += halfKsize;
}
if(range.end != height)
{
paddedHeight += halfKsize;
}
const Mat srcPadded = src(Rect(0, range.start - topLines, width, paddedHeight));
Mat dstPadded = Mat(paddedHeight, width, dst.depth());
if (fcvFuncType == FCV_MAKETYPE(3,CV_8U))
fcvFilterGaussian3x3u8_v4(srcPadded.data, width, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step, fcvBorder, 0);
else if (fcvFuncType == FCV_MAKETYPE(5,CV_8U))
fcvFilterGaussian5x5u8_v3(srcPadded.data, width, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step, fcvBorder, 0);
else if (fcvFuncType == FCV_MAKETYPE(5,CV_16S))
fcvFilterGaussian5x5s16_v3((int16_t*)srcPadded.data, width, paddedHeight, srcPadded.step, (int16_t*)dstPadded.data,
dstPadded.step, fcvBorder, 0);
else if (fcvFuncType == FCV_MAKETYPE(5,CV_32S))
fcvFilterGaussian5x5s32_v3((int32_t*)srcPadded.data, width, paddedHeight, srcPadded.step, (int32_t*)dstPadded.data,
dstPadded.step, fcvBorder, 0);
else if (fcvFuncType == FCV_MAKETYPE(11,CV_8U))
fcvFilterGaussian11x11u8_v2(srcPadded.data, width, rangeHeight, srcPadded.step, dstPadded.data, dstPadded.step, fcvBorder);
// Only copy center part back to output image and ignore the padded lines
Mat temp1 = dstPadded(Rect(0, topLines, width, rangeHeight));
Mat temp2 = dst(Rect(0, range.start, width, rangeHeight));
temp1.copyTo(temp2);
}
private:
const Mat& src;
Mat& dst;
int width;
int height;
const int ksize;
int halfKsize;
int fcvFuncType;
fcvBorderType fcvBorder;
int fcvBorderValue;
FcvGaussianBlurLoop_Invoker(const FcvGaussianBlurLoop_Invoker &); // = delete;
const FcvGaussianBlurLoop_Invoker& operator= (const FcvGaussianBlurLoop_Invoker &); // = delete;
};
void gaussianBlur(InputArray _src, OutputArray _dst, int kernel_size, bool blur_border)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && CV_MAT_CN(_src.type()) == 1);
Size size = _src.size();
int type = _src.type();
_dst.create( size, type );
Mat src = _src.getMat();
Mat dst = _dst.getMat();
int nThreads = getNumThreads();
int nStripes = (nThreads > 1) ? ((src.rows > 60) ? 3 * nThreads : 1) : 1;
fcvBorderType fcvBorder = blur_border ? FASTCV_BORDER_ZERO_PADDING : FASTCV_BORDER_UNDEFINED;
if (((type == CV_8UC1) && ((kernel_size == 3) || (kernel_size == 5) || (kernel_size == 11))) ||
((type == CV_16SC1) && (kernel_size == 5)) ||
((type == CV_32SC1) && (kernel_size == 5)))
{
parallel_for_(Range(0, src.rows), FcvGaussianBlurLoop_Invoker(src, dst, kernel_size, fcvBorder, 0), nStripes);
}
else
CV_Error(cv::Error::StsBadArg, cv::format("Src type %d, kernel size %d is not supported", type, kernel_size));
}
class FcvFilter2DLoop_Invoker : public ParallelLoopBody
{
public:
FcvFilter2DLoop_Invoker(const Mat& _src, Mat& _dst, const Mat& _kernel) :
ParallelLoopBody(), src(_src), dst(_dst), kernel(_kernel)
{
width = src.cols;
height = src.rows;
ksize = kernel.size().width;
halfKsize = ksize/2;
}
virtual void operator()(const Range& range) const CV_OVERRIDE
{
int topLines = 0;
int rangeHeight = range.end-range.start;
int paddedHeight = rangeHeight;
if(range.start >= halfKsize)
{
topLines += halfKsize;
paddedHeight += halfKsize;
}
if(range.end <= height-halfKsize)
{
paddedHeight += halfKsize;
}
const Mat srcPadded = src(Rect(0, range.start - topLines, width, paddedHeight));
Mat dstPadded = Mat(paddedHeight, width, dst.depth());
if (dst.depth() == CV_8U)
fcvFilterCorrNxNu8((int8_t*)kernel.data, ksize, 0, srcPadded.data, width, paddedHeight, srcPadded.step,
dstPadded.data, dstPadded.step);
else if (dst.depth() == CV_16S)
fcvFilterCorrNxNu8s16((int8_t*)kernel.data, ksize, 0, srcPadded.data, width, paddedHeight, srcPadded.step,
(int16_t*)dstPadded.data, dstPadded.step);
else if (dst.depth() == CV_32F)
fcvFilterCorrNxNu8f32((float32_t*)kernel.data, ksize, srcPadded.data, width, paddedHeight, srcPadded.step,
(float32_t*)dstPadded.data, dstPadded.step);
// Only copy center part back to output image and ignore the padded lines
Mat temp1 = dstPadded(Rect(0, topLines, width, rangeHeight));
Mat temp2 = dst(Rect(0, range.start, width, rangeHeight));
temp1.copyTo(temp2);
}
private:
const Mat& src;
Mat& dst;
const Mat& kernel;
int width;
int height;
int ksize;
int halfKsize;
FcvFilter2DLoop_Invoker(const FcvFilter2DLoop_Invoker &); // = delete;
const FcvFilter2DLoop_Invoker& operator= (const FcvFilter2DLoop_Invoker &); // = delete;
};
void filter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernel)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
Mat kernel = _kernel.getMat();
Size ksize = kernel.size();
CV_Assert(ksize.width == ksize.height);
CV_Assert(ksize.width % 2 == 1);
_dst.create(_src.size(), ddepth);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
int nThreads = getNumThreads();
int nStripes = (nThreads > 1) ? ((src.rows > 60) ? 3 * nThreads : 1) : 1;
switch (ddepth)
{
case CV_8U:
case CV_16S:
{
CV_Assert(CV_MAT_DEPTH(kernel.type()) == CV_8S);
parallel_for_(Range(0, src.rows), FcvFilter2DLoop_Invoker(src, dst, kernel), nStripes);
break;
}
case CV_32F:
{
CV_Assert(CV_MAT_DEPTH(kernel.type()) == CV_32F);
parallel_for_(Range(0, src.rows), FcvFilter2DLoop_Invoker(src, dst, kernel), nStripes);
break;
}
default:
{
CV_Error(cv::Error::StsBadArg, cv::format("Kernel Size:%d, Dst type:%s is not supported", ksize.width,
depthToString(ddepth)));
break;
}
}
}
class FcvSepFilter2DLoop_Invoker : public ParallelLoopBody
{
public:
FcvSepFilter2DLoop_Invoker(const Mat& _src, Mat& _dst, const Mat& _kernelX, const Mat& _kernelY) :
ParallelLoopBody(), src(_src), dst(_dst), kernelX(_kernelX), kernelY(_kernelY)
{
width = src.cols;
height = src.rows;
kernelXSize = kernelX.size().width;
kernelYSize = kernelY.size().width;
halfKsize = kernelXSize/2;
}
virtual void operator()(const Range& range) const CV_OVERRIDE
{
int topLines = 0;
int rangeHeight = range.end-range.start;
int paddedHeight = rangeHeight;
if(range.start >= halfKsize)
{
topLines += halfKsize;
paddedHeight += halfKsize;
}
if(range.end <= height-halfKsize)
{
paddedHeight += halfKsize;
}
const Mat srcPadded = src(Rect(0, range.start - topLines, width, paddedHeight));
Mat dstPadded = Mat(paddedHeight, width, dst.depth());
switch (dst.depth())
{
case CV_8U:
{
fcvFilterCorrSepMxNu8((int8_t*)kernelX.data, kernelXSize, (int8_t*)kernelY.data, kernelYSize, 0, srcPadded.data,
width, paddedHeight, srcPadded.step, dstPadded.data, dstPadded.step);
break;
}
case CV_16S:
{
std::vector<int16_t> tmpImage(width * (paddedHeight + kernelXSize - 1));
switch (kernelXSize)
{
case 9:
{
fcvFilterCorrSep9x9s16_v2((int16_t*)kernelX.data, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
case 11:
{
fcvFilterCorrSep11x11s16_v2((int16_t*)kernelX.data, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
case 13:
{
fcvFilterCorrSep13x13s16_v2((int16_t*)kernelX.data, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
case 15:
{
fcvFilterCorrSep15x15s16_v2((int16_t*)kernelX.data, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
case 17:
{
fcvFilterCorrSep17x17s16_v2((int16_t*)kernelX.data, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
default:
{
fcvFilterCorrSepNxNs16((int16_t*)kernelX.data, kernelXSize, (int16_t*)srcPadded.data, width, paddedHeight,
srcPadded.step, tmpImage.data(), (int16_t*)dstPadded.data, dstPadded.step);
break;
}
}
break;
}
default:
{
CV_Error(cv::Error::StsBadArg, cv::format("Dst type:%s is not supported", depthToString(dst.depth())));
break;
}
}
// Only copy center part back to output image and ignore the padded lines
Mat temp1 = dstPadded(Rect(0, topLines, width, rangeHeight));
Mat temp2 = dst(Rect(0, range.start, width, rangeHeight));
temp1.copyTo(temp2);
}
private:
const Mat& src;
Mat& dst;
int width;
int height;
const Mat& kernelX;
const Mat& kernelY;
int kernelXSize;
int kernelYSize;
int halfKsize;
FcvSepFilter2DLoop_Invoker(const FcvSepFilter2DLoop_Invoker &); // = delete;
const FcvSepFilter2DLoop_Invoker& operator= (const FcvSepFilter2DLoop_Invoker &); // = delete;
};
void sepFilter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernelX, InputArray _kernelY)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && (_src.type() == CV_8UC1 || _src.type() == CV_16SC1));
_dst.create(_src.size(), ddepth);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
Mat kernelX = _kernelX.getMat();
Mat kernelY = _kernelY.getMat();
int nThreads = getNumThreads();
int nStripes = (nThreads > 1) ? ((src.rows > 60) ? 3 * nThreads : 1) : 1;
switch (ddepth)
{
case CV_8U:
{
cv::parallel_for_(cv::Range(0, src.rows), FcvSepFilter2DLoop_Invoker(src, dst, kernelX, kernelY), nStripes);
break;
}
case CV_16S:
{
CV_Assert(CV_MAT_DEPTH(src.type()) == CV_16S);
CV_Assert(kernelX.size() == kernelY.size());
// kernalX and kernelY shhould be same.
Mat diff;
absdiff(kernelX, kernelY, diff);
CV_Assert(countNonZero(diff) == 0);
cv::parallel_for_(cv::Range(0, src.rows), FcvSepFilter2DLoop_Invoker(src, dst, kernelX, kernelY), nStripes);
break;
}
default:
{
CV_Error(cv::Error::StsBadArg, cv::format("Dst type:%s is not supported", depthToString(ddepth)));
break;
}
}
}
void normalizeLocalBox(InputArray _src, OutputArray _dst, Size pSize, bool useStdDev)
{
CV_Assert(!_src.empty());
int type = _src.type();
CV_Assert(type == CV_8UC1 || type == CV_32FC1);
Size size = _src.size();
int dst_type = type == CV_8UC1 ? CV_8SC1 : CV_32FC1;
_dst.create(size, dst_type);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
if(type == CV_8UC1)
fcvNormalizeLocalBoxu8(src.data, src.cols, src.rows, src.step[0],
pSize.width, pSize.height, useStdDev, (int8_t*)dst.data, dst.step[0]);
else if(type == CV_32FC1)
fcvNormalizeLocalBoxf32((float*)src.data, src.cols, src.rows, src.step[0],
pSize.width, pSize.height, useStdDev, (float*)dst.data, dst.step[0]);
}
} // fastcv::
} // cv::
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
void filter2D(InputArray _src, OutputArray _dst, int ddepth, InputArray _kernel)
{
CV_Assert(
!_src.empty() &&
_src.type() == CV_8UC1 &&
IS_FASTCV_ALLOCATED(_src.getMat()) &&
IS_FASTCV_ALLOCATED(_kernel.getMat())
);
Mat kernel = _kernel.getMat();
Size ksize = kernel.size();
CV_Assert(ksize.width == ksize.height);
CV_Assert(ksize.width % 2 == 1);
_dst.create(_src.size(), ddepth);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
switch (ddepth)
{
case CV_8U:
{
if(ksize.width == 3)
fcvFilterCorr3x3s8_v2Q((int8_t*)kernel.data, src.data, src.cols, src.rows, src.step, dst.data, dst.step);
else
fcvFilterCorrNxNu8Q((int8_t*)kernel.data, ksize.width, 0, src.data, src.cols, src.rows, src.step, dst.data, dst.step);
break;
}
case CV_16S:
{
fcvFilterCorrNxNu8s16Q((int8_t*)kernel.data, ksize.width, 0, src.data, src.cols, src.rows, src.step, (int16_t*)dst.data, dst.step);
break;
}
case CV_32F:
{
fcvFilterCorrNxNu8f32Q((float32_t*)kernel.data, ksize.width, src.data, src.cols, src.rows, src.step, (float32_t*)dst.data, dst.step);
break;
}
default:
{
CV_Error(cv::Error::StsBadArg, cv::format("Kernel Size:%d, Dst type:%s is not supported", ksize.width,
depthToString(ddepth)));
}
}
}
} // dsp::
} // fastcv::
} // cv::
+143
View File
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void merge(InputArrayOfArrays _mv, OutputArray _dst)
{
CV_Assert(!_mv.empty());
std::vector<cv::Mat> mv;
_mv.getMatVector(mv);
int count = mv.size();
CV_Assert(!mv.empty());
CV_Assert(count == 2 || count == 3 || count == 4);
CV_Assert(!mv[0].empty());
CV_Assert(mv[0].dims <= 2);
for(int i = 0; i < count; i++ )
{
CV_Assert(mv[i].size == mv[0].size && mv[i].step[0] == mv[0].step[0] && mv[i].type() == CV_8UC1);
}
_dst.create(mv[0].size, CV_MAKE_TYPE(CV_8U,count));
Mat dst = _dst.getMat();
INITIALIZATION_CHECK;
int nStripes = cv::getNumThreads();
switch(count)
{
case 2:
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0];
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0];
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0];
fcvChannelCombine2Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yD, dst.step[0]);
}, nStripes);
break;
case 3:
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0];
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0];
const uchar* yS3 = mv[2].data + static_cast<size_t>(range.start) * mv[2].step[0];
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0];
fcvChannelCombine3Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yS3, mv[2].step[0], yD, dst.step[0]);
}, nStripes);
break;
case 4:
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0];
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0];
const uchar* yS3 = mv[2].data + static_cast<size_t>(range.start) * mv[2].step[0];
const uchar* yS4 = mv[3].data + static_cast<size_t>(range.start) * mv[3].step[0];
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0];
fcvChannelCombine4Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yS3, mv[2].step[0], yS4, mv[3].step[0], yD, dst.step[0]);
}, nStripes);
break;
default:
CV_Error(cv::Error::StsBadArg, cv::format("count is not supported"));
break;
}
}
void split(InputArray _src, OutputArrayOfArrays _mv)
{
CV_Assert(!_src.empty());
Mat src = _src.getMat();
int depth = src.depth(), cn = src.channels();
CV_Assert(depth == CV_8U && (cn == 2 || cn == 3 || cn == 4));
CV_Assert(src.dims <= 2);
_mv.create(cn, 1, depth);
for( int k = 0; k < cn; k++ )
{
_mv.create(src.size, depth, k);
}
std::vector<cv::Mat> mv(cn);
_mv.getMatVector(mv);
INITIALIZATION_CHECK;
int nStripes = cv::getNumThreads();
if(src.rows * src.cols < 640 * 480)
if(cn == 3 || cn == 4)
nStripes = 1;
if(cn == 2)
{
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0];
uchar* y1D = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0];
uchar* y2D = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0];
fcvDeinterleaveu8(yS, src.cols, height_, src.step[0], y1D, mv[0].step[0], y2D, mv[1].step[0]);
}, nStripes);
}
else if(cn == 3)
{
for(int i=0; i<cn; i++)
{
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0];
uchar* yD = mv[i].data + static_cast<size_t>(range.start) * mv[i].step[0];
fcvChannelExtractu8(yS, src.cols, height_, src.step[0], NULL, 0, NULL, 0, (fcvChannelType)i, (fcvImageFormat)FASTCV_RGB, yD, mv[i].step[0]);
}, nStripes);
}
}
else if(cn == 4)
{
for(int i=0; i<cn; i++)
{
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){
int height_ = range.end - range.start;
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0];
uchar* yD = mv[i].data + static_cast<size_t>(range.start) * mv[i].step[0];
fcvChannelExtractu8(yS, src.cols, height_, src.step[0], NULL, 0, NULL, 0, (fcvChannelType)i, (fcvImageFormat)FASTCV_RGBX, yD, mv[i].step[0]);
}, nStripes);
}
}
}
} // fastcv::
} // cv::
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void clusterEuclidean(InputArray _points, InputArray _clusterCenters, OutputArray _newClusterCenters,
OutputArray _clusterSizes, OutputArray _clusterBindings, OutputArray _clusterSumDists,
int numPointsUsed)
{
INITIALIZATION_CHECK;
CV_Assert(!_points.empty() && _points.type() == CV_8UC1);
int nPts = _points.rows();
int nDims = _points.cols();
int ptsStride = _points.step();
CV_Assert(!_clusterCenters.empty() && _clusterCenters.depth() == CV_32F);
int nClusters = _clusterCenters.rows();
int clusterCenterStride = _clusterCenters.step();
CV_Assert(_clusterCenters.cols() == nDims);
CV_Assert(numPointsUsed <= nPts);
if (numPointsUsed < 0)
{
numPointsUsed = nPts;
}
_newClusterCenters.create(nClusters, nDims, CV_32FC1);
_clusterSizes.create(1, nClusters, CV_32SC1);
_clusterBindings.create(1, numPointsUsed, CV_32SC1);
_clusterSumDists.create(1, nClusters, CV_32FC1);
Mat points = _points.getMat();
Mat clusterCenters = _clusterCenters.getMat();
Mat newClusterCenters = _newClusterCenters.getMat();
Mat clusterSizes = _clusterSizes.getMat();
Mat clusterBindings = _clusterBindings.getMat();
Mat clusterSumDists = _clusterSumDists.getMat();
int result = fcvClusterEuclideanu8(points.data,
nPts,
nDims,
ptsStride,
numPointsUsed,
nClusters,
(float32_t*)clusterCenters.data,
clusterCenterStride,
(float32_t*)newClusterCenters.data,
(uint32_t*)clusterSizes.data,
(uint32_t*)clusterBindings.data,
(float32_t*)clusterSumDists.data);
if (result)
{
CV_Error(cv::Error::StsInternal, cv::format("Failed to clusterize, error code: %d", result));
}
}
} // fastcv::
} // cv::
+372
View File
@@ -0,0 +1,372 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
namespace cv { namespace fastcv {
static void fastcvColorWrapper(const Mat& src, Mat& dst, int code);
inline double heightFactor(int fmt /*420 / 422 / 444*/)
{
switch (fmt)
{
case 420: return 1.5; // YUV420 has 1.5× rows
case 422: return 2.0; // YUV422 have 2× rows
case 444: return 2.0; // YUV444 have 3× rows
default: return 1.0; // packed RGB565/RGB888 → no extra plane
}
}
inline void getFormats(int code, int& srcFmt, int& dstFmt)
{
switch (code)
{
case COLOR_YUV2YUV444sp_NV12: srcFmt=420; dstFmt=444; break;
case COLOR_YUV2YUV422sp_NV12: srcFmt=420; dstFmt=422; break;
case COLOR_YUV422sp2YUV444sp: srcFmt=422; dstFmt=444; break;
case COLOR_YUV422sp2YUV_NV12: srcFmt=422; dstFmt=420; break;
case COLOR_YUV444sp2YUV422sp: srcFmt=444; dstFmt=422; break;
case COLOR_YUV444sp2YUV_NV12: srcFmt=444; dstFmt=420; break;
case COLOR_YUV2RGB565_NV12: srcFmt=420; dstFmt=565; break;
case COLOR_YUV422sp2RGB565: srcFmt=422; dstFmt=565; break;
case COLOR_YUV422sp2RGB: srcFmt=422; dstFmt=888; break;
case COLOR_YUV422sp2RGBA:srcFmt=422; dstFmt=8888; break;
case COLOR_YUV444sp2RGB565: srcFmt=444; dstFmt=565; break;
case COLOR_YUV444sp2RGB: srcFmt=444; dstFmt=888; break;
case COLOR_YUV444sp2RGBA:srcFmt=444; dstFmt=8888; break;
case COLOR_RGB5652YUV444sp: srcFmt=565; dstFmt=444; break;
case COLOR_RGB5652YUV422sp: srcFmt=565; dstFmt=422; break;
case COLOR_RGB5652YUV_NV12: srcFmt=565; dstFmt=420; break;
case COLOR_RGB2YUV444sp: srcFmt=888; dstFmt=444; break;
case COLOR_RGB2YUV422sp: srcFmt=888; dstFmt=422; break;
case COLOR_RGB2YUV_NV12: srcFmt=888; dstFmt=420; break;
default:
CV_Error(Error::StsBadArg, "Unknown FastCV color-code");
}
}
void cvtColor( InputArray _src, OutputArray _dst, int code)
{
switch( code )
{
case COLOR_YUV2YUV444sp_NV12:
case COLOR_YUV2YUV422sp_NV12:
case COLOR_YUV422sp2YUV444sp:
case COLOR_YUV422sp2YUV_NV12:
case COLOR_YUV444sp2YUV422sp:
case COLOR_YUV444sp2YUV_NV12:
case COLOR_YUV2RGB565_NV12:
case COLOR_YUV422sp2RGB565:
case COLOR_YUV422sp2RGB:
case COLOR_YUV422sp2RGBA:
case COLOR_YUV444sp2RGB565:
case COLOR_YUV444sp2RGB:
case COLOR_YUV444sp2RGBA:
case COLOR_RGB5652YUV444sp:
case COLOR_RGB5652YUV422sp:
case COLOR_RGB5652YUV_NV12:
case COLOR_RGB2YUV444sp:
case COLOR_RGB2YUV422sp:
case COLOR_RGB2YUV_NV12:
fastcvColorWrapper(_src.getMat(), _dst.getMatRef(), code);
break;
default:
CV_Error( cv::Error::StsBadFlag, "Unknown/unsupported color conversion code" );
}
}
void fastcvColorWrapper(const Mat& src, Mat& dst, int code)
{
CV_Assert(src.isContinuous());
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 16 == 0);
const uint32_t width = static_cast<uint32_t>(src.cols);
int srcFmt, dstFmt;
getFormats(code, srcFmt, dstFmt);
const double hFactorSrc = heightFactor(srcFmt);
CV_Assert(std::fmod(src.rows, hFactorSrc) == 0.0);
const uint32_t height = static_cast<uint32_t>(src.rows / hFactorSrc); // Y-plane height we pass to FastCV
const uint8_t* srcY = src.data;
const size_t srcYBytes = static_cast<size_t>(src.step) * height;
const uint8_t* srcC = srcY + srcYBytes;
const uint32_t srcStride = static_cast<uint32_t>(src.step);
const int dstRows = static_cast<int>(height * heightFactor(dstFmt)); // 1.5·H or 2·H
int dstType = CV_8UC1; // default for planar/semi-planar YUV formats (1 byte per pixel)
switch (dstFmt)
{
case 420: case 422: case 444:
dstType = CV_8UC1;
break;
case 565: // RGB565 16-bit packed RGB, 2 bytes per pixel
dstType = CV_8UC2;
break;
case 888: // RGB888 3 bytes per pixel
dstType = CV_8UC3;
break;
case 8888: // RGBA8888 4 bytes per pixel
dstType = CV_8UC4;
break;
default:
CV_Error(cv::Error::StsBadArg, "Unsupported destination pixel format for FastCV");
}
dst.create(dstRows, width, dstType);
CV_Assert(dst.isContinuous());
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 16 == 0);
uint8_t* dstY = dst.data;
uint8_t* dstC = dstY + static_cast<size_t>(dst.step) * height; // offset by Y-plane bytes
const uint32_t dstStride = static_cast<uint32_t>(dst.step);
switch(code)
{
case COLOR_YUV2YUV444sp_NV12:
{
fcvColorYCbCr420PseudoPlanarToYCbCr444PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV2YUV422sp_NV12:
{
fcvColorYCbCr420PseudoPlanarToYCbCr422PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV422sp2YUV444sp:
{
fcvColorYCbCr422PseudoPlanarToYCbCr444PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV422sp2YUV_NV12:
{
fcvColorYCbCr422PseudoPlanarToYCbCr420PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV444sp2YUV422sp:
{
fcvColorYCbCr444PseudoPlanarToYCbCr422PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV444sp2YUV_NV12:
{
fcvColorYCbCr444PseudoPlanarToYCbCr420PseudoPlanaru8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB5652YUV444sp:
{
fcvColorRGB565ToYCbCr444PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB5652YUV422sp:
{
fcvColorRGB565ToYCbCr422PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB5652YUV_NV12:
{
fcvColorRGB565ToYCbCr420PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB2YUV444sp:
{
fcvColorRGB888ToYCbCr444PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB2YUV422sp:
{
fcvColorRGB888ToYCbCr422PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_RGB2YUV_NV12:
{
fcvColorRGB888ToYCbCr420PseudoPlanaru8(
srcY,
width, height,
srcStride,
dstY, dstC,
dstStride, dstStride
);
}
break;
case COLOR_YUV2RGB565_NV12:
{
fcvColorYCbCr420PseudoPlanarToRGB565u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV422sp2RGB565:
{
fcvColorYCbCr422PseudoPlanarToRGB565u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV422sp2RGB:
{
fcvColorYCbCr422PseudoPlanarToRGB888u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV422sp2RGBA:
{
fcvColorYCbCr422PseudoPlanarToRGBA8888u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV444sp2RGB565:
{
fcvColorYCbCr444PseudoPlanarToRGB565u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV444sp2RGB:
{
fcvColorYCbCr444PseudoPlanarToRGB888u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
case COLOR_YUV444sp2RGBA:
{
fcvColorYCbCr444PseudoPlanarToRGBA8888u8(
srcY, srcC,
width, height,
srcStride, srcStride,
dstY,
dstStride
);
}
break;
default:
CV_Error(cv::Error::StsBadArg, "Unsupported FastCV color code");
}
}
}} // namespace cv::fastcv
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
//CHANGE FASTCV Q6 INIT
int fcvdspinit()
{
FastCvDspContext& context = FastCvDspContext::getContext();
if (context.isInitialized()) {
CV_LOG_INFO(NULL, "FastCV DSP already initialized, skipping initialization");
return 0;
}
if (!context.initialize()) {
CV_LOG_ERROR(NULL, "Failed to initialize FastCV DSP");
return -1;
}
CV_LOG_INFO(NULL, "FastCV DSP initialized successfully");
return 0;
}
void fcvdspdeinit()
{
// Deinitialize the DSP environment
FastCvDspContext& context = FastCvDspContext::getContext();
if (!context.isInitialized()) {
CV_LOG_INFO(NULL, "FastCV DSP already deinitialized, skipping deinitialization");
return;
}
if (!context.deinitialize()) {
CV_LOG_ERROR(NULL, "Failed to deinitialize FastCV DSP");
}
CV_LOG_INFO(NULL, "FastCV DSP deinitialized successfully");
}
} // namespace dsp
} // namespace fastcv
} // namespace cv
+125
View File
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void sobel3x3u8(cv::InputArray _src, cv::OutputArray _dst, cv::OutputArray _dsty, int ddepth, bool normalization)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
Size size = _src.size();
_dst.create(size, ddepth);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
if (_dsty.needed())
{
_dsty.create(size, ddepth);
Mat dsty = _dsty.getMat();
switch(ddepth)
{
case CV_8S:
if (normalization)
fcvImageGradientSobelPlanars8_v2(src.data, src.cols, src.rows, src.step, (int8_t*)dst.data,
(int8_t*)dsty.data, dst.step);
else
CV_Error(cv::Error::StsBadArg,
cv::format("Depth: %d should do normalization, make sure the normalization parameter is true", ddepth));
break;
case CV_16S:
if (normalization)
fcvImageGradientSobelPlanars16_v2(src.data, src.cols, src.rows, src.step, (int16_t*)dst.data,
(int16_t*)dsty.data, dst.step);
else
fcvImageGradientSobelPlanars16_v3(src.data, src.cols, src.rows, src.step, (int16_t*)dst.data,
(int16_t*)dsty.data, dst.step);
break;
case CV_32F:
if (normalization)
fcvImageGradientSobelPlanarf32_v2(src.data, src.cols, src.rows, src.step, (float32_t*)dst.data,
(float32_t*)dsty.data, dst.step);
else
fcvImageGradientSobelPlanarf32_v3(src.data, src.cols, src.rows, src.step, (float32_t*)dst.data,
(float32_t*)dsty.data, dst.step);
break;
default:
CV_Error(cv::Error::StsBadArg, cv::format("depth: %d is not supported", ddepth));
break;
}
}
else
{
fcvFilterSobel3x3u8_v2(src.data, src.cols, src.rows, src.step, dst.data, dst.step);
}
}
void sobel(cv::InputArray _src, cv::OutputArray _dx, cv::OutputArray _dy, int kernel_size, int borderType, int borderValue)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
Size size = _src.size();
_dx.create( size, CV_16SC1);
_dy.create( size, CV_16SC1);
Mat src = _src.getMat();
Mat dx = _dx.getMat();
Mat dy = _dy.getMat();
fcvStatus status = FASTCV_SUCCESS;
fcvBorderType fcvBorder;
switch (borderType)
{
case cv::BorderTypes::BORDER_CONSTANT:
{
fcvBorder = fcvBorderType::FASTCV_BORDER_CONSTANT;
break;
}
case cv::BorderTypes::BORDER_REPLICATE:
{
fcvBorder = fcvBorderType::FASTCV_BORDER_REPLICATE;
break;
}
default:
{
CV_Error(cv::Error::StsBadArg, cv::format("Border type: %d is not supported", borderType));
break;
}
}
switch (kernel_size)
{
case 3:
status = fcvFilterSobel3x3u8s16(src.data, src.cols, src.rows, src.step, (int16_t*)dx.data, (int16_t*)dy.data,
dx.step, fcvBorder, borderValue);
break;
case 5:
status = fcvFilterSobel5x5u8s16(src.data, src.cols, src.rows, src.step, (int16_t*)dx.data, (int16_t*)dy.data,
dx.step, fcvBorder, borderValue);
break;
case 7:
status = fcvFilterSobel7x7u8s16(src.data, src.cols, src.rows, src.step, (int16_t*)dx.data, (int16_t*)dy.data,
dx.step, fcvBorder, borderValue);
break;
default:
CV_Error(cv::Error::StsBadArg, cv::format("Kernel size %d is not supported", kernel_size));
break;
}
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
void Canny(InputArray _src, OutputArray _dst, int lowThreshold, int highThreshold, int apertureSize, bool L2gradient)
{
CV_Assert(
!_src.empty() &&
lowThreshold <= highThreshold &&
IS_FASTCV_ALLOCATED(_src.getMat())
);
int type = _src.type();
CV_Assert(type == CV_8UC1);
CV_Assert(_src.step() % 8 == 0);
Size size = _src.size();
_dst.create(size, type);
Mat src = _src.getMat();
CV_Assert(src.step >= (size_t)src.cols);
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 8 == 0);
Mat dst = _dst.getMat();
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 8 == 0);
CV_Assert(dst.step >= (size_t)src.cols);
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
fcvNormType norm;
if (L2gradient)
norm = FASTCV_NORM_L2;
else
norm = FASTCV_NORM_L1;
int16_t* gx = (int16_t*)fcvHwMemAlloc(src.cols * src.rows * sizeof(int16_t), 16);
int16_t* gy = (int16_t*)fcvHwMemAlloc(src.cols * src.rows * sizeof(int16_t), 16);
uint32_t gstride = 2 * src.cols;
fcvStatus status = fcvFilterCannyu8Q((uint8_t*)src.data, src.cols, src.rows, src.step, apertureSize, lowThreshold, highThreshold, norm, (uint8_t*)dst.data, dst.step, gx, gy, gstride);
fcvHwMemFree(gx);
fcvHwMemFree(gy);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // dsp::
} // fastcv::
} // cv::
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void FAST10(InputArray _src, InputArray _mask, OutputArray _coords, OutputArray _scores, int barrier, int border, bool nmsEnabled)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.cols() <= 2048);
CV_Assert(_src.step() % 8 == 0);
// segfaults at border <= 3, fixing it
border = std::max(4, border);
CV_Assert(_src.cols() > 2*border);
CV_Assert(_src.rows() > 2*border);
Mat src = _src.getMat();
Mat mask;
if (!_mask.empty())
{
CV_Assert(_mask.type() == CV_8UC1);
float kw = (float)src.cols / (float)_mask.cols();
float kh = (float)src.rows / (float)_mask.rows();
float eps = std::numeric_limits<float>::epsilon();
if (std::abs(kw - kh) > eps)
{
CV_Error(cv::Error::StsBadArg, "Mask proportions do not correspond to image proportions");
}
bool sizeFits = false;
for (int k = -3; k <= 3; k++)
{
if (std::abs(kw - std::pow(2.f, (float)k)) < eps)
{
sizeFits = true;
break;
}
}
if (!sizeFits)
{
CV_Error(cv::Error::StsBadArg, "Mask size do not correspond to image size divided by k from -3 to 3");
}
mask = _mask.getMat();
}
CV_Assert(_coords.needed());
const int maxCorners = 32768;
Mat coords(1, maxCorners * 2, CV_32SC1);
AutoBuffer<uint32_t> tempBuf;
Mat scores;
if (_scores.needed())
{
scores.create(1, maxCorners, CV_32SC1);
tempBuf.allocate(maxCorners * 3 + src.rows + 1);
}
uint32_t nCorners = maxCorners;
if (!mask.empty())
{
if (!scores.empty())
{
fcvCornerFast10InMaskScoreu8(src.data, src.cols, src.rows, src.step,
barrier, border,
(uint32_t*)coords.data, (uint32_t*)scores.data, maxCorners, &nCorners,
mask.data, mask.cols, mask.rows,
nmsEnabled,
tempBuf.data());
}
else
{
fcvCornerFast10InMasku8(src.data, src.cols, src.rows, src.step,
barrier, border,
(uint32_t*)coords.data, maxCorners, &nCorners,
mask.data, mask.cols, mask.rows);
}
}
else
{
if (!scores.empty())
{
fcvCornerFast10Scoreu8(src.data, src.cols, src.rows, src.step,
barrier, border,
(uint32_t*)coords.data, (uint32_t*)scores.data, maxCorners, &nCorners,
nmsEnabled,
tempBuf.data());
}
else
{
fcvCornerFast10u8(src.data, src.cols, src.rows, src.step,
barrier, border,
(uint32_t*)coords.data, maxCorners, &nCorners);
}
}
_coords.create(1, nCorners*2, CV_32SC1);
coords(Range::all(), Range(0, nCorners*2)).copyTo(_coords);
if (_scores.needed())
{
scores(Range::all(), Range(0, nCorners)).copyTo(_scores);
}
}
} // fastcv::
} // cv::
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
static bool isPow2(int x)
{
return x && (!(x & (x - 1)));
}
void FFT(InputArray _src, OutputArray _dst)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(isPow2(_src.rows()) || _src.rows() == 1);
CV_Assert(isPow2(_src.cols()));
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
_dst.create(_src.rows(), _src.cols(), CV_32FC2);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvStatus status = fcvFFTu8(src.data, src.cols, src.rows, src.step,
(float*)dst.data, dst.step);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
void IFFT(InputArray _src, OutputArray _dst)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_32FC2);
CV_Assert(isPow2(_src.rows()) || _src.rows() == 1);
CV_Assert(isPow2(_src.cols()));
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
_dst.create(_src.rows(), _src.cols(), CV_8UC1);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvStatus status = fcvIFFTf32((const float*)src.data, src.cols * 2, src.rows, src.step,
dst.data, dst.step);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
static bool isPow2(int x)
{
return x && (!(x & (x - 1)));
}
void FFT(InputArray _src, OutputArray _dst)
{
CV_Assert(
!_src.empty() &&
_src.type() == CV_8UC1 &&
IS_FASTCV_ALLOCATED(_src.getMat())
);
CV_Assert(isPow2(_src.rows()) || _src.rows() == 1);
CV_Assert(isPow2(_src.cols()));
CV_Assert(_src.step() % 8 == 0);
CV_Assert(static_cast<unsigned long>(_src.rows() * _src.cols()) > MIN_REMOTE_BUF_SIZE);
Mat src = _src.getMat();
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 8 == 0);
_dst.create(_src.rows(), _src.cols(), CV_32FC2);
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 8 == 0);
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
fcvStatus status = fcvFFTu8Q(src.data, src.cols, src.rows, src.step,
(float*)dst.data, dst.step);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
}
}
void IFFT(InputArray _src, OutputArray _dst)
{
CV_Assert(
!_src.empty() &&
_src.type() == CV_32FC2 &&
IS_FASTCV_ALLOCATED(_src.getMat())
);
CV_Assert(isPow2(_src.rows()) || _src.rows() == 1);
CV_Assert(isPow2(_src.cols()));
CV_Assert(_src.step() % 8 == 0);
CV_Assert(static_cast<unsigned long>(_src.rows() * _src.cols() * sizeof(float32_t)) > MIN_REMOTE_BUF_SIZE);
Mat src = _src.getMat();
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 8 == 0);
_dst.create(_src.rows(), _src.cols(), CV_8UC1);
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 8 == 0);
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
fcvStatus status = fcvIFFTf32Q((const float*)src.data, src.cols * 2, src.rows, src.step,
dst.data, dst.step);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // dsp::
} // fastcv::
} // cv::
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void fillConvexPoly(InputOutputArray _img, InputArray _pts, Scalar color)
{
INITIALIZATION_CHECK;
CV_Assert(!_img.empty() && _img.depth() == CV_8U && _img.channels() <= 4);
CV_Assert(_img.cols() % 8 == 0);
CV_Assert(_img.step() % 8 == 0);
Mat img = _img.getMat();
CV_Assert(!_pts.empty() && (_pts.type() == CV_32SC1 || _pts.type() == CV_32SC2));
CV_Assert(_pts.isContinuous());
CV_Assert(_pts.total() * _pts.channels() % 2 == 0);
Mat pts = _pts.getMat();
uint32_t nPts = pts.total() * pts.channels() / 2;
Vec4b coloru8 = color;
fcvFillConvexPolyu8(nPts, (const uint32_t*)pts.data,
img.channels(), coloru8.val,
img.data, img.cols, img.rows, img.step);
}
} // fastcv::
} // cv::
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class FcvHistogramLoop_Invoker : public cv::ParallelLoopBody
{
public:
FcvHistogramLoop_Invoker(const uchar * src_data_, size_t src_step_, int width_, int height_, int32_t* gl_hist_, int stripeHeight_, cv::Mutex* histogramLock, int nStripes_):
cv::ParallelLoopBody(), src_data(src_data_), src_step(src_step_), width(width_), height(height_), gl_hist(gl_hist_), stripeHeight(stripeHeight_), histogramLock_(histogramLock), nStripes(nStripes_)
{
}
virtual void operator()(const cv::Range& range) const CV_OVERRIDE
{
int height_ = stripeHeight;
if(range.end == nStripes)
height_ += (height % nStripes);
const uchar* yS = src_data;
int32_t l_hist[256] = {0};
fcvImageIntensityHistogram(yS, src_step, 0, range.start, width, height_, l_hist);
cv::AutoLock lock(*histogramLock_);
for( int i = 0; i < 256; i++ )
gl_hist[i] += l_hist[i];
}
private:
const uchar * src_data;
const size_t src_step;
const int width;
const int height;
int32_t *gl_hist;
int ret;
int stripeHeight;
cv::Mutex* histogramLock_;
int nStripes;
FcvHistogramLoop_Invoker(const FcvHistogramLoop_Invoker &); // = delete;
const FcvHistogramLoop_Invoker& operator= (const FcvHistogramLoop_Invoker &); // = delete;
};
void calcHist( InputArray _src, OutputArray _hist )
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty());
int type = _src.type();
CV_Assert(type == CV_8UC1);
_hist.create( cv::Size(256, 1), CV_32SC1 );
Mat src = _src.getMat();
Mat hist = _hist.getMat();
for( int i = 0; i < 256; i++ )
hist.ptr<int>()[i] = 0;
cv::Mutex histogramLockInstance;
int nStripes = cv::getNumThreads();
int stripeHeight = src.rows / nStripes;
cv::parallel_for_(cv::Range(0, nStripes),
FcvHistogramLoop_Invoker(src.data, src.step[0], src.cols, src.rows, hist.ptr<int>(), stripeHeight, &histogramLockInstance, nStripes), nStripes);
}
} // fastcv::
} // cv::
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void houghLines(InputArray _src, OutputArray _lines, double threshold)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
const uint32_t maxLines = 16384;
cv::Mat lines(1, maxLines, CV_32FC4);
uint32_t nLines = maxLines;
fcvHoughLineu8(src.data, src.cols, src.rows, src.step,
(float)threshold, maxLines, &nLines, (fcvLine*)lines.data);
_lines.create(1, nLines, CV_32FC4);
lines(Range::all(), Range(0, nLines)).copyTo(_lines);
}
} // fastcv::
} // cv::
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void DCT(InputArray _src, OutputArray _dst)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
_dst.create(_src.rows(), _src.cols(), CV_16SC1);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvDCTu8(src.data, src.cols, src.rows, src.step, (short*)dst.data, dst.step);
}
void IDCT(InputArray _src, OutputArray _dst)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_16SC1);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
_dst.create(_src.rows(), _src.cols(), CV_8UC1);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvIDCTs16((const short*)src.data, src.cols, src.rows, src.step, dst.data, dst.step);
}
} // fastcv::
} // cv::
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
cv::Moments moments(InputArray _src, bool binary)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty());
int type = _src.type();
CV_Assert(type == CV_8UC1 || type == CV_32SC1 || type == CV_32FC1);
Size size = _src.size();
Mat src = _src.getMat();
cv::Moments m;
fcvMoments mFCV;
fcvStatus status = FASTCV_SUCCESS;
if(binary)
{
cv::Mat src_binary(size, CV_8UC1);
cv::compare( src, 0, src_binary, cv::CMP_NE );
fcvImageMomentsu8(src_binary.data, src_binary.cols,
src_binary.rows, src_binary.step[0], &mFCV, binary);
}
else
{
switch(type)
{
case CV_8UC1:
fcvImageMomentsu8(src.data, src.cols, src.rows, src.step[0], &mFCV, binary);
break;
case CV_32SC1:
fcvImageMomentss32(src.ptr<int>(), src.cols, src.rows, src.step[0], &mFCV, binary);
break;
case CV_32FC1:
fcvImageMomentsf32(src.ptr<float>(), src.cols, src.rows, src.step[0], &mFCV, binary);
break;
}
}
if (status != FASTCV_SUCCESS)
{
CV_Error( cv::Error::StsError, cv::format("Error occurred!") );
return m;
}
m.m00 = mFCV.m00; m.m10 = mFCV.m10; m.m01 = mFCV.m01;
m.m20 = mFCV.m20; m.m11 = mFCV.m11; m.m02 = mFCV.m02;
m.m30 = mFCV.m30; m.m21 = mFCV.m21; m.m12 = mFCV.m12;
m.m03 = mFCV.m03; m.mu02 = mFCV.mu02; m.m03 = mFCV.mu03;
m.mu11 = mFCV.mu11; m.mu12 = mFCV.mu12; m.mu20 = mFCV.mu20;
m.mu21 = mFCV.mu21; m.mu30 = mFCV.mu30;
float32_t inv_m00 = 1.0/mFCV.m00;
float32_t inv_sqrt_m00 = mFCV.inv_sqrt_m00;
float32_t s2 = inv_m00 * inv_m00, s3 = s2 * inv_sqrt_m00;
m.nu20 = mFCV.mu20 * s2; m.nu11 = mFCV.mu11 * s2;
m.nu02 = mFCV.mu02 * s2; m.nu30 = mFCV.mu30 * s3;
m.nu21 = mFCV.mu21 * s3; m.nu12 = mFCV.mu12 * s3;
m.nu03 = mFCV.mu03 * s3;
return m;
}
} // fastcv::
} // cv::
+260
View File
@@ -0,0 +1,260 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class MSER_Impl CV_FINAL : public cv::fastcv::FCVMSER
{
public:
explicit MSER_Impl(cv::Size imgSize,
int numNeighbors,
int delta,
int minArea,
int maxArea,
float maxVariation,
float minDiversity);
~MSER_Impl() CV_OVERRIDE;
cv::Size getImgSize() CV_OVERRIDE { return imgSize; };
int getNumNeighbors() CV_OVERRIDE { return numNeighbors; };
int getDelta() CV_OVERRIDE { return delta; };
int getMinArea() CV_OVERRIDE { return minArea; };
int getMaxArea() CV_OVERRIDE { return maxArea; };
float getMaxVariation() CV_OVERRIDE { return maxVariation; };
float getMinDiversity() CV_OVERRIDE { return minDiversity; };
void detect(InputArray src, std::vector<std::vector<Point>>& contours) CV_OVERRIDE;
void detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes) CV_OVERRIDE;
void detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes,
std::vector<ContourData>& contourData) CV_OVERRIDE;
void detectRegions(InputArray src,
std::vector<std::vector<Point>>& contours,
std::vector<cv::Rect>& boundingBoxes,
std::vector<ContourData>& contourData,
bool useBoundingBoxes = true,
bool useContourData = true);
cv::Size imgSize;
int numNeighbors;
int delta;
int minArea;
int maxArea;
float maxVariation;
float minDiversity;
void *mserHandle;
};
MSER_Impl::MSER_Impl(cv::Size _imgSize,
int _numNeighbors,
int _delta,
int _minArea,
int _maxArea,
float _maxVariation,
float _minDiversity)
{
CV_Assert(_imgSize.width > 50);
CV_Assert(_imgSize.height > 5);
CV_Assert(_numNeighbors == 4 || _numNeighbors == 8);
INITIALIZATION_CHECK;
this->imgSize = _imgSize;
this->numNeighbors = _numNeighbors;
this->delta = _delta;
this->minArea = _minArea;
this->maxArea = _maxArea;
this->maxVariation = _maxVariation;
this->minDiversity = _minDiversity;
auto initFunc = (this->numNeighbors == 4) ? fcvMserInit : fcvMserNN8Init;
if (!initFunc(this->imgSize.width, this->imgSize.height, this->delta, this->minArea, this->maxArea,
this->maxVariation, this->minDiversity, &this->mserHandle))
{
CV_Error(cv::Error::StsInternal, "Failed to initialize MSER");
}
}
MSER_Impl::~MSER_Impl()
{
fcvMserRelease(mserHandle);
}
void MSER_Impl::detectRegions(InputArray _src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes,
std::vector<ContourData>& contourData, bool useBoundingBoxes, bool useContourData)
{
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.size() == this->imgSize);
Mat src = _src.getMat();
bool usePointsArray = (this->numNeighbors == 8);
//bufSize for pts and bboxes
const uint32_t maxContours = 16384;
uint32_t numContours;
std::vector<uint32_t> numPointsInContour(maxContours);
std::vector<uint16_t> rectArray;
rectArray.resize(4 * maxContours); // xMin, xMax, yMax, yMin
uint32_t pointsArraySize = src.total() * 30; // Recommended typical size
std::vector<uint16_t> pointsArray;
std::vector<uint32_t> contourStartingPoints;
uint32_t pathArraySize = src.total() * 4; // Recommended size
std::vector<uint16_t> pathArray;
if (usePointsArray)
{
pointsArray.resize(pointsArraySize);
}
else
{
contourStartingPoints.resize(maxContours);
pathArray.resize(pathArraySize);
}
std::vector<uint32_t> contourVariation(maxContours), contourNodeId(maxContours), contourNodeCounter(maxContours);
std::vector<int8_t> contourPolarity(maxContours);
int mserRetcode = -1;
if (this->numNeighbors == 4)
{
mserRetcode = fcvMserExtu8_v3(mserHandle, src.data, src.cols, src.rows, src.step,
maxContours, &numContours,
rectArray.data(),
contourStartingPoints.data(),
numPointsInContour.data(),
pathArraySize, pathArray.data(),
contourVariation.data(), contourPolarity.data(), contourNodeId.data(), contourNodeCounter.data());
CV_LOG_INFO(NULL, "fcvMserExtu8_v3");
}
else
{
if (useContourData)
{
mserRetcode = fcvMserExtNN8u8(mserHandle, src.data, src.cols, src.rows, src.step,
maxContours, &numContours,
rectArray.data(),
numPointsInContour.data(), pointsArraySize, pointsArray.data(),
contourVariation.data(), contourPolarity.data(), contourNodeId.data(), contourNodeCounter.data());
CV_LOG_INFO(NULL, "fcvMserExtNN8u8");
}
else
{
mserRetcode = fcvMserNN8u8(mserHandle, src.data, src.cols, src.rows, src.step,
maxContours, &numContours,
rectArray.data(),
numPointsInContour.data(), pointsArraySize, pointsArray.data());
CV_LOG_INFO(NULL, "fcvMserNN8u8");
}
}
if (mserRetcode != 1)
{
CV_Error(cv::Error::StsInternal, "Failed to run MSER");
}
contours.clear();
contours.reserve(numContours);
if (useBoundingBoxes)
{
boundingBoxes.clear();
boundingBoxes.reserve(numContours);
}
if (useContourData)
{
contourData.clear();
contourData.reserve(numContours);
}
int ptCtr = 0;
for (uint32_t i = 0; i < numContours; i++)
{
std::vector<Point> contour;
contour.reserve(numPointsInContour[i]);
for (uint32_t j = 0; j < numPointsInContour[i]; j++)
{
Point pt;
if (usePointsArray)
{
uint32_t idx = (ptCtr + j) * 2;
pt = Point {pointsArray[idx + 0], pointsArray[idx + 1]};
}
else
{
uint32_t idx = contourStartingPoints[i] + j * 2;
pt = Point {pathArray[idx + 0], pathArray[idx + 1]};
}
contour.push_back(pt);
}
contours.push_back(contour);
ptCtr += numPointsInContour[i];
if (useBoundingBoxes)
{
uint16_t xMin = rectArray[i * 4 + 0];
uint16_t xMax = rectArray[i * 4 + 1];
uint16_t yMax = rectArray[i * 4 + 2];
uint16_t yMin = rectArray[i * 4 + 3];
// +1 is because max limit in cv::Rect() is exclusive
cv::Rect bbox(Point {xMin, yMin},
Point {xMax + 1, yMax + 1});
boundingBoxes.push_back(bbox);
}
if (useContourData)
{
ContourData data;
data.variation = contourVariation[i];
data.polarity = contourPolarity[i];
data.nodeId = contourNodeId[i];
data.nodeCounter = contourNodeCounter[i];
contourData.push_back(data);
}
}
}
void MSER_Impl::detect(InputArray src, std::vector<std::vector<Point>> &contours)
{
std::vector<cv::Rect> boundingBoxes;
std::vector<ContourData> contourData;
this->detectRegions(src, contours, boundingBoxes, contourData, /*useBoundingBoxes*/ false, /*useContourData*/ false);
}
void MSER_Impl::detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes)
{
std::vector<ContourData> contourData;
this->detectRegions(src, contours, boundingBoxes, contourData, /*useBoundingBoxes*/ true, /*useContourData*/ false);
}
void MSER_Impl::detect(InputArray src, std::vector<std::vector<Point>>& contours, std::vector<cv::Rect>& boundingBoxes,
std::vector<ContourData>& contourData)
{
this->detectRegions(src, contours, boundingBoxes, contourData, /*useBoundingBoxes*/ true, /*useContourData*/ true);
}
Ptr<FCVMSER> FCVMSER::create(const cv::Size& imgSize,
int numNeighbors,
int delta,
int minArea,
int maxArea,
float maxVariation,
float minDiversity)
{
CV_Assert(numNeighbors > 0 && delta >= 0 && minArea >= 0 && maxArea >= 0);
return makePtr<MSER_Impl>(imgSize, numNeighbors, delta, minArea, maxArea, maxVariation, minDiversity);
}
} // fastcv::
} // cv::
+185
View File
@@ -0,0 +1,185 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OPENCV_FASTCV_PRECOMP_HPP
#define OPENCV_FASTCV_PRECOMP_HPP
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "opencv2/core/private.hpp"
#include "opencv2/core/utils/logger.hpp"
#include <opencv2/fastcv.hpp>
#include <map>
#include <atomic>
#include "fastcv.h"
#include "fastcvDsp.h"
namespace cv {
namespace fastcv {
#define INITIALIZATION_CHECK \
{ \
if (!FastCvContext::getContext().isInitialized) \
{ \
CV_Error(cv::Error::StsBadArg, cv::format("Set mode failed!")); \
} \
CV_INSTRUMENT_REGION(); \
}
#define FCV_KernelSize_SHIFT 3
#define FCV_MAKETYPE(ksize,depth) ((ksize<<FCV_KernelSize_SHIFT) + depth)
#define MIN_REMOTE_BUF_SIZE 176*144*sizeof(uint8_t)
const std::map<fcvStatus, std::string> fcvStatusStrings =
{
{ FASTCV_SUCCESS, "Success"},
{ FASTCV_EFAIL, "General failure"},
{ FASTCV_EUNALIGNPARAM, "Unaligned pointer parameter"},
{ FASTCV_EBADPARAM, "Bad parameters"},
{ FASTCV_EINVALSTATE, "Called at invalid state"},
{ FASTCV_ENORES, "Insufficient resources, memory, thread"},
{ FASTCV_EUNSUPPORTED, "Unsupported feature"},
{ FASTCV_EHWQDSP, "Hardware QDSP failed to respond"},
{ FASTCV_EHWGPU, "Hardware GPU failed to respond"},
};
struct FastCvContext
{
public:
// initialize at first call
// Defines a static local variable context. Variable is created only once.
static FastCvContext& getContext()
{
static FastCvContext context;
return context;
}
FastCvContext()
{
if (fcvSetOperationMode(FASTCV_OP_CPU_PERFORMANCE) != 0)
{
CV_LOG_WARNING(NULL, "Failed to switch FastCV operation mode");
isInitialized = false;
}
else
{
CV_LOG_INFO(NULL, "FastCV Operation Mode Switched");
isInitialized = true;
}
}
bool isInitialized;
};
namespace dsp {
struct FastCvDspContext;
#define IS_FASTCV_ALLOCATED(mat) \
((mat.allocator == cv::fastcv::getQcAllocator()) ? true : \
(CV_Error(cv::Error::StsBadArg, cv::format("Matrix '%s' not allocated with FastCV allocator. " \
"Please ensure that the matrix is created using " \
"cv::fastcv::getQcAllocator().", #mat)), false))
#define FASTCV_CHECK_DSP_INIT() \
if (!FastCvDspContext::getContext().isInitialized() && \
fcvdspinit() != 0) \
{ \
CV_Error(cv::Error::StsError, "Failed to initialize DSP"); \
}
struct FastCvDspContext
{
private:
mutable cv::Mutex initMutex;
std::atomic<bool> isDspInitialized{false};
std::atomic<uint64_t> initializationCount{0};
std::atomic<uint64_t> deInitializationCount{0};
static FastCvDspContext& getInstanceImpl() {
static FastCvDspContext context;
return context;
}
public:
static FastCvDspContext& getContext() {
return getInstanceImpl();
}
FastCvDspContext(const FastCvDspContext&) = delete;
FastCvDspContext& operator=(const FastCvDspContext&) = delete;
bool initialize() {
cv::AutoLock lock(initMutex);
if (isDspInitialized.load(std::memory_order_acquire)) {
CV_LOG_INFO(NULL, "FastCV DSP already initialized, skipping initialization");
return true;
}
CV_LOG_INFO(NULL, "Initializing FastCV DSP");
if (fcvQ6Init() == 0) {
isDspInitialized.store(true, std::memory_order_release);
initializationCount++;
CV_LOG_DEBUG(NULL, cv::format("FastCV DSP initialized (init count: %lu, deinit count: %lu)",
initializationCount.load(), deInitializationCount.load()));
return true;
}
CV_LOG_ERROR(NULL, "FastCV DSP initialization failed");
return false;
}
bool deinitialize() {
cv::AutoLock lock(initMutex);
if (!isDspInitialized.load(std::memory_order_acquire)) {
CV_LOG_DEBUG(NULL, "FastCV DSP already deinitialized, skipping deinitialization");
return true;
}
CV_LOG_INFO(NULL, "Deinitializing FastCV DSP");
try {
fcvQ6DeInit();
isDspInitialized.store(false, std::memory_order_release);
deInitializationCount++;
CV_LOG_DEBUG(NULL, cv::format("FastCV DSP deinitialized (init count: %lu, deinit count: %lu)",
initializationCount.load(), deInitializationCount.load()));
return true;
}
catch (...) {
CV_LOG_ERROR(NULL, "Exception occurred during FastCV DSP deinitialization");
return false;
}
}
bool isInitialized() const {
return isDspInitialized.load(std::memory_order_acquire);
}
uint64_t getDspInitCount() const {
return initializationCount.load(std::memory_order_acquire);
}
uint64_t getDspDeInitCount() const {
return deInitializationCount.load(std::memory_order_acquire);
}
const cv::Mutex& getInitMutex() const {
return initMutex;
}
private:
FastCvDspContext() = default;
};
} // namespace dsp
} // namespace fastcv
} // namespace cv
#endif // OPENCV_FASTCV_PRECOMP_HPP
+183
View File
@@ -0,0 +1,183 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void sobelPyramid(InputArrayOfArrays _pyr, OutputArrayOfArrays _dx, OutputArrayOfArrays _dy, int outType)
{
INITIALIZATION_CHECK;
CV_Assert(_pyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_pyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_pyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_dx.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_dx.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_dx.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_dy.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_dy.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_dy.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
std::vector<cv::Mat> pyr;
_pyr.getMatVector(pyr);
size_t nLevels = pyr.size();
CV_Assert(!pyr.empty());
// this should be smaller I guess
CV_Assert(nLevels > 0 && nLevels < 16);
for (size_t i = 0; i < nLevels; i++)
{
// fcvPyramidLeved does not support other cases
CV_Assert(pyr[i].isContinuous());
CV_Assert(pyr[i].type() == CV_8UC1);
}
CV_Assert(outType == CV_8S || outType == CV_16S || outType == CV_32F);
std::vector<fcvPyramidLevel> lpyr;
for (size_t i = 0; i < nLevels; i++)
{
fcvPyramidLevel lev;
lev.width = pyr[i].cols;
lev.height = pyr[i].rows;
lev.ptr = pyr[i].data;
lpyr.push_back(lev);
}
std::vector<fcvPyramidLevel> ldx(nLevels), ldy(nLevels);
int pyrElemSz = (outType == CV_8S ) ? 1 :
(outType == CV_16S) ? 2 :
(outType == CV_32F) ? 4 : 0;
int retCodex = fcvPyramidAllocate(ldx.data(), pyr[0].cols, pyr[0].rows, pyrElemSz, nLevels, 1);
if (retCodex != 0)
{
CV_Error(cv::Error::StsInternal, cv::format("fcvPyramidAllocate returned code %d", retCodex));
}
int retCodey = fcvPyramidAllocate(ldy.data(), pyr[0].cols, pyr[0].rows, pyrElemSz, nLevels, 1);
if (retCodey != 0)
{
CV_Error(cv::Error::StsInternal, cv::format("fcvPyramidAllocate returned code %d", retCodey));
}
int returnCode = -1;
switch (outType)
{
case CV_8S: returnCode = fcvPyramidSobelGradientCreatei8 (lpyr.data(), ldx.data(), ldy.data(), nLevels);
break;
case CV_16S: returnCode = fcvPyramidSobelGradientCreatei16(lpyr.data(), ldx.data(), ldy.data(), nLevels);
break;
case CV_32F: returnCode = fcvPyramidSobelGradientCreatef32(lpyr.data(), ldx.data(), ldy.data(), nLevels);
break;
default:
break;
}
if (returnCode != 0)
{
CV_Error(cv::Error::StsInternal, cv::format("FastCV returned code %d", returnCode));
}
// resize arrays of Mats
_dx.create(1, nLevels, /* type does not matter here */ -1, -1);
_dy.create(1, nLevels, /* type does not matter here */ -1, -1);
for (size_t i = 0; i < nLevels; i++)
{
cv::Mat dx((int)ldx[i].height, (int)ldx[i].width, outType, (uchar*)ldx[i].ptr);
_dx.create(pyr[i].size(), outType, i);
dx.copyTo(_dx.getMat(i));
cv::Mat dy((int)ldy[i].height, (int)ldy[i].width, outType, (uchar*)ldy[i].ptr);
_dy.create(pyr[i].size(), outType, i);
dy.copyTo(_dy.getMat(i));
}
fcvPyramidDelete(ldx.data(), nLevels, 0);
fcvPyramidDelete(ldy.data(), nLevels, 0);
}
void buildPyramid(InputArray _src, OutputArrayOfArrays _pyr, int nLevels, bool scaleBy2, int borderType, uint8_t borderValue)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && (_src.type() == CV_8UC1 || _src.type() == CV_32FC1));
CV_Assert(_src.step() % 8 == 0);
cv::Mat src = _src.getMat();
bool useFloat = src.depth() == CV_32F;
int bytesPerPixel = useFloat ? 4 : 1;
CV_Assert(_pyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_pyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_pyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
// this should be smaller I guess
CV_Assert(nLevels > 0 && nLevels < 16);
if (useFloat && !scaleBy2)
{
CV_Error( cv::Error::StsBadArg, "ORB scale is not supported for float images (fcvPyramidCreatef32_v2)");
}
fcvPyramidScale scaleOption = scaleBy2 ? FASTCV_PYRAMID_SCALE_HALF : FASTCV_PYRAMID_SCALE_ORB;
fcvBorderType borderOption;
switch (borderType)
{
case cv::BORDER_REFLECT: borderOption = FASTCV_BORDER_REFLECT; break;
case cv::BORDER_REFLECT_101: borderOption = FASTCV_BORDER_REFLECT_V2; break;
case cv::BORDER_REPLICATE: borderOption = FASTCV_BORDER_REPLICATE; break;
default: borderOption = FASTCV_BORDER_UNDEFINED; break;
}
std::vector<fcvPyramidLevel_v2> lpyrSrc2(nLevels);
int alignment = 8;
if (useFloat)
{
// use version 2
CV_Assert(fcvPyramidAllocate_v2(lpyrSrc2.data(), src.cols, src.rows, src.step, bytesPerPixel, nLevels, 0) == 0);
CV_Assert(fcvPyramidCreatef32_v2((const float*)src.data, src.cols, src.rows, src.step, nLevels, lpyrSrc2.data()) == 0);
}
else
{
// use version 4
fcvStatus statusAlloc = fcvPyramidAllocate_v3(lpyrSrc2.data(), src.cols, src.rows, src.step,
bytesPerPixel, alignment, nLevels, scaleOption, 0);
if (statusAlloc != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(statusAlloc) ? fcvStatusStrings.at(statusAlloc) : "unknown";
CV_Error( cv::Error::StsInternal, "fcvPyramidAllocate_v3 error: " + s);
}
fcvStatus statusPyr = fcvPyramidCreateu8_v4(src.data, src.cols, src.rows, src.step, nLevels, scaleOption,
lpyrSrc2.data(), borderOption, borderValue);
if (statusPyr != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(statusPyr) ? fcvStatusStrings.at(statusPyr) : "unknown";
CV_Error( cv::Error::StsInternal, "fcvPyramidCreateu8_v4 error: " + s);
}
}
// create vector
_pyr.create(nLevels, 1, src.type(), -1);
for (int i = 0; i < nLevels; i++)
{
cv::Mat m = cv::Mat((uint32_t)lpyrSrc2[i].height, (uint32_t)lpyrSrc2[i].width,
src.type(), (void*)lpyrSrc2[i].ptr, (size_t)lpyrSrc2[i].stride);
_pyr.create(m.size(), m.type(), i);
m.copyTo(_pyr.getMat(i));
}
fcvPyramidDelete_v2(lpyrSrc2.data(), nLevels, 1);
}
} // namespace fastcv
} // namespace cv
+146
View File
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class RemapParallel : public cv::ParallelLoopBody {
public:
RemapParallel(int src_type, const uint8_t* src, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcStride, uint8_t* dst,
uint32_t dstWidth, uint32_t dstHeight, uint32_t dstStride, const float32_t* __restrict mapX,
const float32_t* __restrict mapY, uint32_t mapStride, fcvInterpolationType interpolation, uint8_t borderValue)
: src_type_(src_type), src_(src), srcWidth_(srcWidth), srcHeight_(srcHeight), srcStride_(srcStride), dst_(dst), dstWidth_(dstWidth),
dstHeight_(dstHeight), dstStride_(dstStride), mapX_(mapX), mapY_(mapY), mapStride_(mapStride),
fcvInterpolation_(interpolation), borderValue_(borderValue) {}
void operator()(const cv::Range& range) const override {
CV_UNUSED(srcHeight_);
CV_UNUSED(dstHeight_);
int rangeHeight = range.end-range.start;
fcvStatus status = FASTCV_SUCCESS;
if(src_type_==CV_8UC1)
{
status = fcvRemapu8_v2(src_ + range.start*srcStride_, srcWidth_, rangeHeight, srcStride_, dst_ + range.start*dstStride_,
srcWidth_, rangeHeight, dstStride_, mapX_, mapY_, mapStride_, fcvInterpolation_, FASTCV_BORDER_CONSTANT, borderValue_);
}
else if(src_type_==CV_8UC4)
{
if(fcvInterpolation_ == FASTCV_INTERPOLATION_TYPE_BILINEAR)
{
fcvRemapRGBA8888BLu8(src_ + range.start*srcStride_, srcWidth_, rangeHeight, srcStride_, dst_ + range.start*dstStride_, dstWidth_, rangeHeight,
dstStride_, mapX_, mapY_, mapStride_);
}
else if(fcvInterpolation_ == FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR)
{
fcvRemapRGBA8888NNu8(src_ + range.start*srcStride_, srcWidth_, rangeHeight, srcStride_, dst_ + range.start*dstStride_, dstWidth_, rangeHeight,
dstStride_, mapX_, mapY_, mapStride_);
}
}
if(status!=FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
private:
int src_type_;
const uint8_t* src_;
uint32_t srcWidth_;
uint32_t srcHeight_;
uint32_t srcStride_;
uint8_t* dst_;
uint32_t dstWidth_;
uint32_t dstHeight_;
uint32_t dstStride_;
const float32_t* __restrict mapX_;
const float32_t* __restrict mapY_;
uint32_t mapStride_;
fcvInterpolationType fcvInterpolation_;
uint8_t borderValue_;
};
void remap(cv::InputArray _src, cv::OutputArray _dst, cv::InputArray _map1, cv::InputArray _map2,
int interpolation, int borderValue)
{
INITIALIZATION_CHECK;
CV_Assert(_src.type() == CV_8UC1);
CV_Assert(_map1.type()==CV_32FC1);
CV_Assert(interpolation == cv::InterpolationFlags::INTER_NEAREST || interpolation == cv::InterpolationFlags::INTER_LINEAR);
CV_Assert(!_map1.empty() && !_map2.empty());
CV_Assert(_map1.size() == _map2.size());
CV_Assert(borderValue >= 0 && borderValue < 256);
Size size = _map1.size();
int type = _src.type();
_dst.create( size, type);
Mat src = _src.getMat();
Mat map1 = _map1.getMat();
Mat map2 = _map2.getMat();
Mat dst = _dst.getMat();
CV_Assert(map1.step == map2.step);
fcvStatus status = FASTCV_SUCCESS;
fcvInterpolationType fcvInterpolation;
if(interpolation==cv::InterpolationFlags::INTER_NEAREST)
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR;
else
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_BILINEAR;
cv::parallel_for_(cv::Range(0, src.rows), RemapParallel(CV_8UC1, src.data, src.cols, src.rows, src.step, dst.data, dst.cols, dst.rows, dst.step,
(float32_t*)map1.data, (float32_t*)map2.data, map1.step, fcvInterpolation, borderValue), (src.cols*src.rows)/(double)(1 << 16));
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
void remapRGBA(cv::InputArray _src, cv::OutputArray _dst, cv::InputArray _map1, cv::InputArray _map2, int interpolation)
{
INITIALIZATION_CHECK;
CV_Assert(_src.type() == CV_8UC4);
CV_Assert(_map1.type()==CV_32FC1);
CV_Assert(interpolation == cv::InterpolationFlags::INTER_NEAREST || interpolation == cv::InterpolationFlags::INTER_LINEAR);
CV_Assert(!_map1.empty() && !_map2.empty());
CV_Assert(_map1.size() == _map2.size());
Size size = _map1.size();
int type = _src.type();
_dst.create( size, type);
Mat src = _src.getMat();
Mat map1 = _map1.getMat();
Mat map2 = _map2.getMat();
Mat dst = _dst.getMat();
CV_Assert(map1.step == map2.step);
fcvStatus status = FASTCV_SUCCESS;
fcvInterpolationType fcvInterpolation;
if(interpolation==cv::InterpolationFlags::INTER_NEAREST)
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR;
else
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_BILINEAR;
cv::parallel_for_(cv::Range(0, src.rows), RemapParallel(CV_8UC4, src.data, src.cols, src.rows, src.step, dst.data, dst.cols, dst.rows, dst.step,
(float32_t*)map1.data, (float32_t*)map2.data, map1.step, fcvInterpolation, 0), (src.cols*src.rows)/(double)(1 << 16) );
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
void sumOfAbsoluteDiffs(cv::InputArray _patch, cv::InputArray _src, cv::OutputArray _dst)
{
cv::Mat patch = _patch.getMat();
cv::Mat src = _src.getMat();
// Check if matrices are allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(patch));
CV_Assert(IS_FASTCV_ALLOCATED(src));
CV_Assert(!_src.empty() && "src is empty");
CV_Assert(_src.type() == CV_8UC1 && "src type is not CV_8UC1");
CV_Assert(_src.step() * _src.rows() > MIN_REMOTE_BUF_SIZE && "src buffer size is too small");
CV_Assert(!_patch.empty() && "patch is empty");
CV_Assert(_patch.type() == CV_8UC1 && "patch type is not CV_8UC1");
CV_Assert(_patch.size() == cv::Size(8, 8) && "patch size is not 8x8");
cv::Size size = _src.size();
_dst.create(size, CV_16UC1);
cv::Mat dst = _dst.getMat();
CV_Assert(((intptr_t)src.data & 0x7) == 0 && "src data is not 8-byte aligned");
CV_Assert(((intptr_t)dst.data & 0x7) == 0 && "dst data is not 8-byte aligned");
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
fcvSumOfAbsoluteDiffs8x8u8_v2Q((uint8_t*)patch.data, patch.step, (uint8_t*)src.data, src.cols, src.rows, src.step, (uint16_t*)dst.data, dst.step);
}
} // dsp::
} // fastcv::
} // cv::
+77
View File
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void resizeDown(cv::InputArray _src, cv::OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y)
{
fcvStatus status = FASTCV_SUCCESS;
Size ssize = _src.size();
CV_Assert(!_src.empty() );
CV_Assert( _src.type() == CV_8UC1 || _src.type() == CV_8UC2 );
if( dsize.empty() )
{
CV_Assert(inv_scale_x > 0);
CV_Assert(inv_scale_y > 0);
dsize = Size(saturate_cast<int>(ssize.width*inv_scale_x),
saturate_cast<int>(ssize.height*inv_scale_y));
CV_Assert( !dsize.empty() );
}
else
{
inv_scale_x = static_cast<double>(dsize.width) / ssize.width;
inv_scale_y = static_cast<double>(dsize.height) / ssize.height;
CV_Assert(inv_scale_x > 0);
CV_Assert(inv_scale_y > 0);
}
CV_Assert(dsize.width <= ssize.width && dsize.height <= ssize.height);
CV_Assert(dsize.width * 20 > ssize.width);
CV_Assert(dsize.height * 20 > ssize.height);
INITIALIZATION_CHECK;
Mat src = _src.getMat();
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
// Alignment checks
CV_Assert(reinterpret_cast<uintptr_t>(src.data) % 16 == 0);
CV_Assert(reinterpret_cast<uintptr_t>(dst.data) % 16 == 0);
if(src.type() == CV_8UC2)
{
fcvScaleDownMNInterleaveu8((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.cols, dst.rows, dst.step);
}
else if (src.cols/dst.cols == 4 && src.rows/dst.rows == 4 && src.cols % dst.cols == 0 && src.rows % dst.rows == 0)
{
CV_Assert(src.rows % 4 == 0);
status = (fcvStatus)fcvScaleDownBy4u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.step);
}
else if (src.cols/dst.cols == 2 && src.rows/dst.rows == 2 && src.cols % dst.cols == 0 && src.rows % dst.rows == 0)
{
CV_Assert(src.rows % 2 == 0);
status = (fcvStatus)fcvScaleDownBy2u8_v2((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.step);
}
else
{
fcvScaleDownMNu8((const uint8_t*)src.data, src.cols, src.rows, src.step, (uint8_t*)dst.data, dst.cols, dst.rows, dst.step);
}
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
int meanShift(InputArray _src, Rect& rect, TermCriteria termCrit)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && (_src.type() == CV_8UC1 || _src.type() == CV_32SC1 || _src.type() == CV_32FC1));
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
fcvRectangleInt window;
window.x = rect.x;
window.y = rect.y;
window.width = rect.width;
window.height = rect.height;
fcvTermCriteria criteria;
criteria.epsilon = (termCrit.type & TermCriteria::EPS) ? termCrit.epsilon : 0;
criteria.max_iter = (termCrit.type & TermCriteria::COUNT) ? termCrit.maxCount : 1024;
uint32_t nIterations = 0;
if (src.depth() == CV_8U)
{
nIterations = fcvMeanShiftu8(src.data, src.cols, src.rows, src.step,
&window, criteria);
}
else if (src.depth() == CV_32S)
{
nIterations = fcvMeanShifts32((const int *)src.data, src.cols, src.rows, src.step,
&window, criteria);
}
else if (src.depth() == CV_32F)
{
nIterations = fcvMeanShiftf32((const float*)src.data, src.cols, src.rows, src.step,
&window, criteria);
}
rect.x = window.x;
rect.y = window.y;
rect.width = window.width;
rect.height = window.height;
return nIterations;
}
} // fastcv::
} // cv::
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void bilateralRecursive(cv::InputArray _src, cv::OutputArray _dst, float sigmaColor, float sigmaSpace)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.step() % 8 == 0);
Size size = _src.size();
int type = _src.type();
_dst.create(size, type);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat src = _src.getMat();
Mat dst = _dst.getMat();
fcvStatus status = fcvBilateralFilterRecursiveu8(src.data, src.cols, src.rows, src.step,
dst.data, dst.step, sigmaColor, sigmaSpace);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
void thresholdRange(InputArray _src, OutputArray _dst, int lowThresh, int highThresh, int trueValue, int falseValue)
{
INITIALIZATION_CHECK;
CV_Assert(lowThresh >= 0 && lowThresh < 256);
CV_Assert(highThresh >= 0 && highThresh < 256);
CV_Assert(falseValue >= 0 && falseValue < 256);
CV_Assert(trueValue >= 0 && trueValue < 256);
CV_Assert(lowThresh <= highThresh);
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
_dst.create(_src.size(), CV_8UC1);
// in case of fixed layout array we cannot fix this on our side, can only fail if false
CV_Assert(_dst.step() % 8 == 0);
Mat dst = _dst.getMat();
fcvStatus status = fcvFilterThresholdRangeu8_v2(src.data, src.cols, src.rows, src.step,
dst.data, dst.step,
lowThresh, highThresh, trueValue, falseValue);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
} // fastcv::
} // cv::
+55
View File
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
namespace dsp {
void thresholdOtsu(InputArray _src, OutputArray _dst, bool type)
{
CV_Assert(
!_src.empty() &&
_src.type() == CV_8UC1 &&
IS_FASTCV_ALLOCATED(_src.getMat())
);
CV_Assert((_src.step() * _src.rows()) > MIN_REMOTE_BUF_SIZE);
CV_Assert(_src.cols() % 8 == 0);
CV_Assert(_src.step() % 8 == 0);
Mat src = _src.getMat();
CV_Assert(((uintptr_t)src.data & 0x7) == 0);
_dst.create(_src.size(), CV_8UC1);
CV_Assert(_dst.step() % 8 == 0);
CV_Assert(_dst.cols() % 8 == 0);
Mat dst = _dst.getMat();
// Check if dst is allocated by the QcAllocator
CV_Assert(IS_FASTCV_ALLOCATED(dst));
CV_Assert(((uintptr_t)dst.data & 0x7) == 0);
if (src.data == dst.data) {
CV_Assert(src.step == dst.step);
}
// Check DSP initialization status and initialize if needed
FASTCV_CHECK_DSP_INIT();
fcvThreshType threshType;
if (type)
threshType = FCV_THRESH_BINARY_INV;
else
threshType = FCV_THRESH_BINARY;
fcvFilterThresholdOtsuu8Q(src.data, src.cols, src.rows, src.step, dst.data, dst.step, threshType);
}
} // dsp::
} // fastcv::
} // cv::
+269
View File
@@ -0,0 +1,269 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
static void trackOpticalFlowLKInternal(InputArray _src, InputArray _dst,
InputArrayOfArrays _srcPyr, InputArrayOfArrays _dstPyr,
InputArrayOfArrays _srcDxPyr, InputArrayOfArrays _srcDyPyr,
InputArray _ptsIn, OutputArray _ptsOut, InputArray _ptsEst,
OutputArray _statusVec, cv::Size winSize,
cv::TermCriteria termCriteria)
{
INITIALIZATION_CHECK;
CV_Assert(winSize.width % 2 == 1 && winSize.height % 2 == 1);
CV_Assert(!_src.empty() && _src.type() == CV_8UC1);
CV_Assert(!_dst.empty() && _dst.type() == CV_8UC1);
CV_Assert(_src.size() == _dst.size());
CV_Assert(_src.step() % 8 == 0);
CV_Assert(_dst.step() == _src.step());
cv::Mat src = _src.getMat(), dst = _dst.getMat();
CV_Assert(_srcPyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_srcPyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_srcPyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_dstPyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_dstPyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_dstPyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_srcPyr.size() == _dstPyr.size());
int nLevels = _srcPyr.size().area();
std::vector<cv::Mat> srcPyr, dstPyr;
_srcPyr.getMatVector(srcPyr);
_dstPyr.getMatVector(dstPyr);
cv::Size imSz = src.size();
for (int i = 0; i < nLevels; i++)
{
const cv::Mat& s = srcPyr[i];
const cv::Mat& d = dstPyr[i];
CV_Assert(!s.empty() && s.type() == CV_8UC1);
CV_Assert(!d.empty() && d.type() == CV_8UC1);
CV_Assert(s.size() == imSz);
CV_Assert(d.size() == imSz);
imSz.width /= 2; imSz.height /= 2;
}
bool useDxDy = !_srcDxPyr.empty() && !_srcDyPyr.empty();
int version = useDxDy ? 1 : 3;
std::vector<cv::Mat> srcDxPyr, srcDyPyr;
if (version == 1)
{
CV_Assert(_srcDxPyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_srcDxPyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_srcDxPyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_srcDyPyr.kind() == _InputArray::KindFlag::STD_ARRAY_MAT ||
_srcDyPyr.kind() == _InputArray::KindFlag::STD_VECTOR_MAT ||
_srcDyPyr.kind() == _InputArray::KindFlag::STD_VECTOR_UMAT);
CV_Assert(_srcDxPyr.size() == _srcDyPyr.size());
_srcDxPyr.getMatVector(srcDxPyr);
_srcDyPyr.getMatVector(srcDyPyr);
imSz = src.size();
for (int i = 0; i < nLevels; i++)
{
const cv::Mat& dx = srcDxPyr[i];
const cv::Mat& dy = srcDyPyr[i];
CV_Assert(!dx.empty() && dx.type() == CV_8SC1);
CV_Assert(!dy.empty() && dy.type() == CV_8SC1);
CV_Assert(dx.size() == imSz);
CV_Assert(dy.size() == imSz);
imSz.width /= 2; imSz.height /= 2;
}
}
std::vector<fcvPyramidLevel> lpyrSrc1, lpyrDst1, lpyrDxSrc, lpyrDySrc;
std::vector<fcvPyramidLevel_v2> lpyrSrc2, lpyrDst2;
for (int i = 0; i < nLevels; i++)
{
fcvPyramidLevel lsrc1, ldst1;
fcvPyramidLevel_v2 lsrc2, ldst2;
lsrc1.width = srcPyr[i].cols;
lsrc1.height = srcPyr[i].rows;
lsrc1.ptr = srcPyr[i].data;
lsrc2.width = srcPyr[i].cols;
lsrc2.height = srcPyr[i].rows;
lsrc2.stride = srcPyr[i].step;
lsrc2.ptr = srcPyr[i].data;
ldst1.width = dstPyr[i].cols;
ldst1.height = dstPyr[i].rows;
ldst1.ptr = dstPyr[i].data;
ldst2.width = dstPyr[i].cols;
ldst2.height = dstPyr[i].rows;
ldst2.stride = dstPyr[i].step;
ldst2.ptr = dstPyr[i].data;
lpyrSrc1.push_back(lsrc1); lpyrDst1.push_back(ldst1);
lpyrSrc2.push_back(lsrc2); lpyrDst2.push_back(ldst2);
if (version == 1)
{
fcvPyramidLevel ldx, ldy;
CV_Assert(srcDxPyr[i].isContinuous());
ldx.width = srcDxPyr[i].cols;
ldx.height = srcDxPyr[i].rows;
ldx.ptr = srcDxPyr[i].data;
CV_Assert(srcDyPyr[i].isContinuous());
ldy.width = srcDyPyr[i].cols;
ldy.height = srcDyPyr[i].rows;
ldy.ptr = srcDyPyr[i].data;
lpyrDxSrc.push_back(ldx); lpyrDySrc.push_back(ldy);
}
}
CV_Assert(!_ptsIn.empty() && (_ptsIn.type() == CV_32FC1 || _ptsIn.type() == CV_32FC2));
CV_Assert(_ptsIn.isContinuous());
CV_Assert(_ptsIn.total() * _ptsIn.channels() % 2 == 0);
cv::Mat ptsIn = _ptsIn.getMat();
int nPts = ptsIn.total() * ptsIn.channels() / 2;
bool useInitialEstimate;
cv::Mat ptsEst;
const float32_t* ptsEstData;
if (!_ptsEst.empty())
{
CV_Assert(_ptsEst.type() == CV_32FC1 || _ptsEst.type() == CV_32FC2);
CV_Assert(_ptsEst.isContinuous());
int estElems = _ptsEst.total() * _ptsEst.channels();
CV_Assert(estElems % 2 == 0);
CV_Assert(estElems / 2 == nPts);
ptsEst = _ptsEst.getMat();
ptsEstData = (const float32_t*)ptsEst.data;
useInitialEstimate = true;
}
else
{
useInitialEstimate = false;
ptsEstData = (const float32_t*)ptsIn.data;
}
CV_Assert(_ptsOut.needed());
_ptsOut.create(1, nPts, CV_32FC2);
cv::Mat ptsOut = _ptsOut.getMat();
cv::Mat statusVec;
if (!_statusVec.empty())
{
_statusVec.create(1, nPts, CV_32SC1);
statusVec = _statusVec.getMat();
}
else
{
statusVec = cv::Mat(1, nPts, CV_32SC1);
}
fcvTerminationCriteria termCrit;
if (termCriteria.type & cv::TermCriteria::COUNT)
{
if (termCriteria.type & cv::TermCriteria::EPS)
{
termCrit = FASTCV_TERM_CRITERIA_BOTH;
}
else
{
termCrit = FASTCV_TERM_CRITERIA_ITERATIONS;
}
}
else
{
if (termCriteria.type & cv::TermCriteria::EPS)
{
termCrit = FASTCV_TERM_CRITERIA_EPSILON;
}
else
{
CV_Error(cv::Error::StsBadArg, "Incorrect termination criteria");
}
}
int maxIterations = termCriteria.maxCount;
double maxEpsilon = termCriteria.epsilon;
fcvStatus status = FASTCV_SUCCESS;
if (version == 3)
{
status = fcvTrackLKOpticalFlowu8_v3(src.data, dst.data, src.cols, src.rows, src.step,
lpyrSrc2.data(), lpyrDst2.data(),
(const float32_t*)ptsIn.data,
ptsEstData,
(float32_t*)ptsOut.data,
(int32_t*)statusVec.data,
nPts,
winSize.width, winSize.height,
nLevels,
termCrit, maxIterations, maxEpsilon,
useInitialEstimate);
}
else // if (version == 1)
{
CV_Assert(src.isContinuous() && dst.isContinuous());
// Obsolete parameters, set to 0
float maxResidue = 0, minDisplacement = 0, minEigenvalue = 0;
int lightingNormalized = 0;
fcvTrackLKOpticalFlowu8(src.data, dst.data, src.cols, src.rows,
lpyrSrc1.data(), lpyrDst1.data(),
lpyrDxSrc.data(), lpyrDySrc.data(),
(const float32_t*)ptsIn.data,
(float32_t*)ptsOut.data,
(int32_t*)statusVec.data,
nPts,
winSize.width, winSize.height,
maxIterations,
nLevels,
maxResidue, minDisplacement, minEigenvalue, lightingNormalized);
}
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error( cv::Error::StsInternal, "FastCV error: " + s);
}
}
void trackOpticalFlowLK(InputArray _src, InputArray _dst,
InputArrayOfArrays _srcPyr, InputArrayOfArrays _dstPyr,
InputArray _ptsIn, OutputArray _ptsOut, InputArray _ptsEst,
OutputArray _statusVec, cv::Size winSize,
cv::TermCriteria termCriteria)
{
trackOpticalFlowLKInternal(_src, _dst, _srcPyr, _dstPyr, noArray(), noArray(),
_ptsIn, _ptsOut, _ptsEst,
_statusVec, winSize,
termCriteria);
}
void trackOpticalFlowLK(InputArray _src, InputArray _dst,
InputArrayOfArrays _srcPyr, InputArrayOfArrays _dstPyr,
InputArrayOfArrays _srcDxPyr, InputArrayOfArrays _srcDyPyr,
InputArray _ptsIn, OutputArray _ptsOut,
OutputArray _statusVec, cv::Size winSize, int maxIterations)
{
trackOpticalFlowLKInternal(_src, _dst, _srcPyr, _dstPyr,
_srcDxPyr, _srcDyPyr,
_ptsIn, _ptsOut, cv::noArray(),
_statusVec, winSize,
{cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
maxIterations, /* maxEpsilon */ 0.03f * 0.03f});
}
} // fastcv::
} // cv::
+12
View File
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
} // namespace fastcv
} // namespace cv
+320
View File
@@ -0,0 +1,320 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "precomp.hpp"
namespace cv {
namespace fastcv {
class FcvWarpPerspectiveLoop_Invoker : public cv::ParallelLoopBody
{
public:
FcvWarpPerspectiveLoop_Invoker(const Mat& _src1, const Mat& _src2, Mat& _dst1, Mat& _dst2,
const float * _M, fcvInterpolationType _interpolation = FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR,
fcvBorderType _borderType = fcvBorderType::FASTCV_BORDER_UNDEFINED, const int _borderValue = 0)
: ParallelLoopBody(), src1(_src1), src2(_src2), dst1(_dst1), dst2(_dst2), M(_M), interpolation(_interpolation),
borderType(_borderType), borderValue(_borderValue)
{}
virtual void operator()(const cv::Range& range) const CV_OVERRIDE
{
uchar* dst1_ptr = dst1.data + range.start * dst1.step;
int rangeHeight = range.end - range.start;
float rangeMatrix[9];
rangeMatrix[0] = M[0];
rangeMatrix[1] = M[1];
rangeMatrix[2] = M[2]+range.start*M[1];
rangeMatrix[3] = M[3];
rangeMatrix[4] = M[4];
rangeMatrix[5] = M[5]+range.start*M[4];
rangeMatrix[6] = M[6];
rangeMatrix[7] = M[7];
rangeMatrix[8] = M[8]+range.start*M[7];
if ((src2.empty()) || (dst2.empty()))
{
fcvWarpPerspectiveu8_v5(src1.data, src1.cols, src1.rows, src1.step, src1.channels(), dst1_ptr, dst1.cols, rangeHeight,
dst1.step, rangeMatrix, interpolation, borderType, borderValue);
}
else
{
uchar* dst2_ptr = dst2.data + range.start * dst2.step;
fcv2PlaneWarpPerspectiveu8(src1.data, src2.data, src1.cols, src1.rows, src1.step, src2.step, dst1_ptr, dst2_ptr,
dst1.cols, rangeHeight, dst1.step, dst2.step, rangeMatrix);
}
}
private:
const Mat& src1;
const Mat& src2;
Mat& dst1;
Mat& dst2;
const float* M;
fcvInterpolationType interpolation;
fcvBorderType borderType;
int borderValue;
FcvWarpPerspectiveLoop_Invoker(const FcvWarpPerspectiveLoop_Invoker &); // = delete;
const FcvWarpPerspectiveLoop_Invoker& operator= (const FcvWarpPerspectiveLoop_Invoker &); // = delete;
};
void warpPerspective2Plane(InputArray _src1, InputArray _src2, OutputArray _dst1, OutputArray _dst2, InputArray _M0,
Size dsize)
{
INITIALIZATION_CHECK;
CV_Assert(!_src1.empty() && _src1.type() == CV_8UC1);
CV_Assert(!_src2.empty() && _src2.type() == CV_8UC1);
CV_Assert(!_M0.empty());
Mat src1 = _src1.getMat();
Mat src2 = _src2.getMat();
_dst1.create(dsize, src1.type());
_dst2.create(dsize, src2.type());
Mat dst1 = _dst1.getMat();
Mat dst2 = _dst2.getMat();
Mat M0 = _M0.getMat();
CV_Assert((M0.type() == CV_32F || M0.type() == CV_64F) && M0.rows == 3 && M0.cols == 3);
float matrix[9];
Mat M(3, 3, CV_32F, matrix);
M0.convertTo(M, M.type());
int nThreads = getNumThreads();
int nStripes = nThreads > 1 ? 2*nThreads : 1;
cv::parallel_for_(cv::Range(0, dsize.height),
FcvWarpPerspectiveLoop_Invoker(src1, src2, dst1, dst2, matrix), nStripes);
}
void warpPerspective(InputArray _src, OutputArray _dst, InputArray _M0, Size dsize, int interpolation, int borderType,
const Scalar& borderValue)
{
Mat src = _src.getMat();
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
Mat M0 = _M0.getMat();
CV_Assert((M0.type() == CV_32F || M0.type() == CV_64F) && M0.rows == 3 && M0.cols == 3);
float matrix[9];
Mat M(3, 3, CV_32F, matrix);
M0.convertTo(M, M.type());
// Do not support inplace case
CV_Assert(src.data != dst.data);
// Only support CV_8U
CV_Assert(src.depth() == CV_8U);
INITIALIZATION_CHECK;
fcvBorderType fcvBorder;
uint8_t fcvBorderValue = 0;
fcvInterpolationType fcvInterpolation;
switch (borderType)
{
case BORDER_CONSTANT:
{
// Border value should be same
CV_Assert((borderValue[0] == borderValue[1]) &&
(borderValue[0] == borderValue[2]) &&
(borderValue[0] == borderValue[3]));
fcvBorder = fcvBorderType::FASTCV_BORDER_CONSTANT;
fcvBorderValue = static_cast<uint8_t>(borderValue[0]);
break;
}
case BORDER_REPLICATE:
{
fcvBorder = fcvBorderType::FASTCV_BORDER_REPLICATE;
break;
}
case BORDER_TRANSPARENT:
{
fcvBorder = fcvBorderType::FASTCV_BORDER_UNDEFINED;
break;
}
default:
CV_Error(cv::Error::StsBadArg, cv::format("Border type:%d is not supported", borderType));
}
switch(interpolation)
{
case INTER_NEAREST:
{
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR;
break;
}
case INTER_LINEAR:
{
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_BILINEAR;
break;
}
case INTER_AREA:
{
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_AREA;
break;
}
default:
CV_Error(cv::Error::StsBadArg, cv::format("Interpolation type:%d is not supported", interpolation));
}
int nThreads = cv::getNumThreads();
int nStripes = nThreads > 1 ? 2*nThreads : 1;
// placeholder
Mat tmp;
cv::parallel_for_(cv::Range(0, dsize.height),
FcvWarpPerspectiveLoop_Invoker(src, tmp, dst, tmp, matrix, fcvInterpolation, fcvBorder, fcvBorderValue), nStripes);
}
void warpAffine(InputArray _src, OutputArray _dst, InputArray _M, Size dsize,
int interpolation, int borderValue)
{
INITIALIZATION_CHECK;
CV_Assert(!_src.empty());
CV_Assert(!_M.empty());
Mat src = _src.getMat();
Mat M = _M.getMat();
CV_CheckEQ(M.rows, 2, "Affine Matrix must have 2 rows");
CV_Check(M.cols, M.cols == 2 || M.cols == 3, "Affine Matrix must be 2x2 or 2x3");
if (M.rows == 2 && M.cols == 2)
{
CV_CheckTypeEQ(src.type(), CV_8UC1, "2x2 matrix transformation only supports CV_8UC1");
// Check if src is a ROI
Size wholeSize;
Point ofs;
src.locateROI(wholeSize, ofs);
bool isROI = (wholeSize.width > src.cols || wholeSize.height > src.rows);
Mat fullImage;
Point2f center;
if (isROI)
{
center.x = ofs.x + src.cols / 2.0f;
center.y = ofs.y + src.rows / 2.0f;
CV_Check(center.x, center.x >= 0 && center.x < wholeSize.width, "ROI center X is outside full image bounds");
CV_Check(center.y, center.y >= 0 && center.y < wholeSize.height, "ROI center Y is outside full image bounds");
size_t offset = ofs.y * src.step + ofs.x * src.elemSize();
fullImage = Mat(wholeSize, src.type(), src.data - offset);
}
else
{
// Use src as is, center at image center
fullImage = src;
center.x = src.cols / 2.0f;
center.y = src.rows / 2.0f;
CV_LOG_WARNING(NULL, "2x2 matrix with non-ROI input. Using image center for patch extraction.");
}
float affineMatrix[4] = {
M.at<float>(0, 0), M.at<float>(0, 1),
M.at<float>(1, 0), M.at<float>(1, 1)};
float position[2] = {center.x, center.y};
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
dst.step = dst.cols * src.elemSize();
int status = fcvTransformAffineu8_v2(
(const uint8_t *)fullImage.data,
fullImage.cols, fullImage.rows, fullImage.step,
position,
affineMatrix,
(uint8_t *)dst.data,
dst.cols, dst.rows, dst.step);
if (status != 0)
{
CV_Error(Error::StsInternal, "FastCV patch extraction failed");
}
return;
}
// Validate 2x3 matrix for standard transformation
CV_CheckEQ(M.cols, 3, "Matrix must be 2x3 for standard affine transformation");
CV_Check(src.type(), src.type() == CV_8UC1 || src.type() == CV_8UC3, "Standard transformation supports CV_8UC1 or CV_8UC3");
float32_t affineMatrix[6] = {
M.at<float>(0, 0), M.at<float>(0, 1), M.at<float>(0, 2),
M.at<float>(1, 0), M.at<float>(1, 1), M.at<float>(1, 2)};
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
if (src.channels() == 1)
{
fcvStatus status;
fcvInterpolationType fcvInterpolation;
switch (interpolation)
{
case cv::InterpolationFlags::INTER_NEAREST:
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_NEAREST_NEIGHBOR;
break;
case cv::InterpolationFlags::INTER_LINEAR:
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_BILINEAR;
break;
case cv::InterpolationFlags::INTER_AREA:
fcvInterpolation = FASTCV_INTERPOLATION_TYPE_AREA;
break;
default:
CV_Error(cv::Error::StsBadArg, "Unsupported interpolation type");
}
status = fcvTransformAffineClippedu8_v3(
(const uint8_t *)src.data, src.cols, src.rows, src.step,
affineMatrix,
(uint8_t *)dst.data, dst.cols, dst.rows, dst.step,
NULL,
fcvInterpolation,
FASTCV_BORDER_CONSTANT,
borderValue);
if (status != FASTCV_SUCCESS)
{
std::string s = fcvStatusStrings.count(status) ? fcvStatusStrings.at(status) : "unknown";
CV_Error(cv::Error::StsInternal, "FastCV error: " + s);
}
}
else if (src.channels() == 3)
{
CV_LOG_INFO(NULL, "warpAffine: 3-channel images use bicubic interpolation internally.");
std::vector<uint32_t> dstBorder;
try
{
dstBorder.resize(dsize.height * 2);
}
catch (const std::bad_alloc &)
{
CV_Error(Error::StsNoMem, "Failed to allocate border array");
}
fcv3ChannelTransformAffineClippedBCu8(
(const uint8_t *)src.data, src.cols, src.rows, src.step[0],
affineMatrix,
(uint8_t *)dst.data, dst.cols, dst.rows, dst.step[0],
dstBorder.data());
}
}
} // fastcv::
} // cv::
+125
View File
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef std::tuple<int /*rows1*/, int /*cols1*/, int /*cols2*/> MatMulTestParams;
class MatMulTest : public ::testing::TestWithParam<MatMulTestParams> {};
typedef std::tuple<Size, int /*depth*/, int /*op type*/> ArithmOpTestParams;
class ArithmOpTest : public ::testing::TestWithParam<ArithmOpTestParams> {};
TEST_P(MatMulTest, accuracy)
{
auto p = GetParam();
int rows1 = std::get<0>(p);
int cols1 = std::get<1>(p);
int cols2 = std::get<2>(p);
RNG& rng = cv::theRNG();
Mat src1(rows1, cols1, CV_8SC1), src2(cols1, cols2, CV_8SC1);
cvtest::randUni(rng, src1, Scalar::all(-128), Scalar::all(128));
cvtest::randUni(rng, src2, Scalar::all(-128), Scalar::all(128));
Mat dst;
cv::fastcv::matmuls8s32(src1, src2, dst);
Mat fdst;
dst.convertTo(fdst, CV_32F);
Mat fsrc1, fsrc2;
src1.convertTo(fsrc1, CV_32F);
src2.convertTo(fsrc2, CV_32F);
Mat ref;
cv::gemm(fsrc1, fsrc2, 1.0, noArray(), 0, ref, 0);
double normInf = cvtest::norm(ref, fdst, cv::NORM_INF);
double normL2 = cvtest::norm(ref, fdst, cv::NORM_L2);
EXPECT_EQ(normInf, 0);
EXPECT_EQ(normL2, 0);
if (cvtest::debugLevel > 0 && (normInf > 0 || normL2 > 0))
{
std::ofstream of(cv::format("out_%d_%d_%d.txt", rows1, cols1, cols2));
of << ref << std::endl;
of << dst << std::endl;
of.close();
}
}
TEST_P(ArithmOpTest, accuracy)
{
auto p = GetParam();
Size sz = std::get<0>(p);
int depth = std::get<1>(p);
int op = std::get<2>(p);
RNG& rng = cv::theRNG();
Mat src1(sz, depth), src2(sz, depth);
cvtest::randUni(rng, src1, Scalar::all(0), Scalar::all(128));
cvtest::randUni(rng, src2, Scalar::all(0), Scalar::all(128));
Mat dst;
cv::fastcv::arithmetic_op(src1, src2, dst, op);
Mat ref;
if(op == 0)
cv::add(src1, src2, ref);
else if(op == 1)
cv::subtract(src1, src2, ref);
double normInf = cvtest::norm(ref, dst, cv::NORM_INF);
double normL2 = cvtest::norm(ref, dst, cv::NORM_L2);
EXPECT_EQ(normInf, 0);
EXPECT_EQ(normL2, 0);
}
typedef testing::TestWithParam<tuple<Size>> IntegrateYUVTest;
TEST_P(IntegrateYUVTest, accuracy)
{
auto p = GetParam();
Size srcSize = std::get<0>(p);
int depth = CV_8U;
cv::Mat Y(srcSize, depth), CbCr(srcSize.height/2, srcSize.width, depth);
cv::Mat IY, ICb, ICr;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, Y, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, CbCr, Scalar::all(0), Scalar::all(255));
cv::fastcv::integrateYUV(Y, CbCr, IY, ICb, ICr);
CbCr = CbCr.reshape(2,0);
std::vector<cv::Mat> ref;
cv::fastcv::split(CbCr, ref);
cv::Mat IY_ref, ICb_ref, ICr_ref;
cv::integral(Y,IY_ref,CV_32S);
cv::integral(ref[0],ICb_ref,CV_32S);
cv::integral(ref[1],ICr_ref,CV_32S);
EXPECT_EQ(IY_ref.at<int>(IY_ref.rows - 1, IY_ref.cols - 1), IY.at<int>(IY.rows - 1, IY.cols - 1));
EXPECT_EQ(ICb_ref.at<int>(ICb_ref.rows - 1, ICb_ref.cols - 1), ICb.at<int>(ICb.rows - 1, ICb.cols - 1));
EXPECT_EQ(ICr_ref.at<int>(ICr_ref.rows - 1, ICr_ref.cols - 1), ICr.at<int>(ICr.rows - 1, ICr.cols - 1));
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, MatMulTest,
::testing::Combine(::testing::Values(8, 16, 128, 256), // rows1
::testing::Values(8, 16, 128, 256), // cols1
::testing::Values(8, 16, 128, 256))); // cols2
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ArithmOpTest,
::testing::Combine(::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p), // sz
::testing::Values(CV_8U, CV_16S), // depth
::testing::Values(0,1))); // op type
INSTANTIATE_TEST_CASE_P(FastCV_Extension, IntegrateYUVTest,
Values(perf::szVGA, perf::sz720p, perf::sz1080p)); // sz
}} // namespaces opencv_test, ::
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef testing::TestWithParam<tuple<cv::Size,int,int>> fcv_bilateralFilterTest;
TEST_P(fcv_bilateralFilterTest, accuracy)
{
cv::Size size = get<0>(GetParam());
int d = get<1>(GetParam());
double sigmaColor = get<2>(GetParam());
double sigmaSpace = sigmaColor;
RNG& rng = cv::theRNG();
Mat src(size, CV_8UC1);
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cv::Mat dst;
cv::fastcv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);
EXPECT_FALSE(dst.empty());
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, fcv_bilateralFilterTest, Combine(
::testing::Values(Size(8, 8), Size(640, 480), Size(800, 600)),
::testing::Values(5, 7, 9),
::testing::Values(1., 10.)
));
}
}
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef testing::TestWithParam<tuple<Size, int, int, bool>> GaussianBlurTest;
TEST_P(GaussianBlurTest, accuracy)
{
cv::Size srcSize = get<0>(GetParam());
int depth = get<1>(GetParam());
int ksize = get<2>(GetParam());
bool border = get<3>(GetParam());
// For some cases FastCV not support, so skip them
if((ksize!=5) && (depth!=CV_8U))
return;
cv::Mat src(srcSize, depth);
cv::Mat dst,ref;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cv::fastcv::gaussianBlur(src, dst, ksize, border);
if(depth == CV_32S)
src.convertTo(src, CV_32F);
cv::GaussianBlur(src,ref,Size(ksize,ksize),0,0,border);
ref.convertTo(ref,depth);
cv::Mat difference;
cv::absdiff(dst, ref, difference);
int num_diff_pixels = cv::countNonZero(difference);
EXPECT_LT(num_diff_pixels, (src.rows+src.cols)*ksize);
}
typedef testing::TestWithParam<tuple<Size, int, int>> Filter2DTest;
TEST_P(Filter2DTest, accuracy)
{
Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int ksize = get<2>(GetParam());
cv::Mat src(srcSize, CV_8U);
cv::Mat kernel;
cv::Mat dst, ref;
switch (ddepth)
{
case CV_8U:
case CV_16S:
{
kernel.create(ksize,ksize,CV_8S);
break;
}
case CV_32F:
{
kernel.create(ksize,ksize,CV_32F);
break;
}
default:
return;
}
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, kernel, Scalar::all(INT8_MIN), Scalar::all(INT8_MAX));
cv::fastcv::filter2D(src, dst, ddepth, kernel);
cv::filter2D(src, ref, ddepth, kernel);
cv::Mat difference;
dst.convertTo(dst, CV_8U);
ref.convertTo(ref, CV_8U);
cv::absdiff(dst, ref, difference);
int num_diff_pixels = cv::countNonZero(difference);
EXPECT_LT(num_diff_pixels, (src.rows+src.cols)*ksize);
}
typedef testing::TestWithParam<tuple<Size, int>> SepFilter2DTest;
TEST_P(SepFilter2DTest, accuracy)
{
Size srcSize = get<0>(GetParam());
int ksize = get<1>(GetParam());
cv::Mat src(srcSize, CV_8U);
cv::Mat kernel(1,ksize,CV_8S);
cv::Mat dst,ref;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, kernel, Scalar::all(INT8_MIN), Scalar::all(INT8_MAX));
cv::fastcv::sepFilter2D(src, dst, CV_8U, kernel, kernel);
cv::sepFilter2D(src,ref,CV_8U,kernel,kernel);
cv::Mat difference;
cv::absdiff(dst, ref, difference);
int num_diff_pixels = cv::countNonZero(difference);
EXPECT_LT(num_diff_pixels, (src.rows+src.cols)*ksize);
}
typedef testing::TestWithParam<tuple<int>> NormalizeLocalBoxTest;
TEST_P(NormalizeLocalBoxTest, accuracy)
{
bool use_stddev = get<0>(GetParam());
cv::Mat src, dst;
src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
cv::fastcv::normalizeLocalBox(src, dst, Size(5,5), use_stddev);
Scalar s = cv::mean(dst);
if(use_stddev)
EXPECT_LT(s[0],1);
else
EXPECT_LT(s[0],50);
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, GaussianBlurTest, Combine(
/*image size*/ ::testing::Values(perf::szVGA, perf::sz720p, perf::sz1080p),
/*image depth*/ ::testing::Values(CV_8U,CV_16S,CV_32S),
/*kernel size*/ ::testing::Values(3, 5),
/*blur border*/ ::testing::Values(true,false)
));
INSTANTIATE_TEST_CASE_P(FastCV_Extension, Filter2DTest, Combine(
/*image sie*/ Values(perf::szVGA, perf::sz720p, perf::sz1080p),
/*dst depth*/ Values(CV_8U,CV_16S,CV_32F),
/*kernel size*/ Values(3, 5, 7, 9, 11)
));
INSTANTIATE_TEST_CASE_P(FastCV_Extension, SepFilter2DTest, Combine(
/*image size*/ Values(perf::szVGA, perf::sz720p, perf::sz1080p),
/*kernel size*/ Values(3, 5, 7, 9, 11)
));
INSTANTIATE_TEST_CASE_P(FastCV_Extension, NormalizeLocalBoxTest, Values(0,1));
}} // namespaces opencv_test, ::
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef testing::TestWithParam<tuple<Size, int, int>> Filter2DTest_DSP;
TEST_P(Filter2DTest_DSP, accuracy)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
int ksize = get<2>(GetParam());
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator();
src.create(srcSize, CV_8U);
cv::Mat kernel;
cv::Mat dst, ref;
kernel.allocator = cv::fastcv::getQcAllocator();
dst.allocator = cv::fastcv::getQcAllocator();
switch (ddepth)
{
case CV_8U:
case CV_16S:
{
kernel.create(ksize,ksize,CV_8S);
break;
}
case CV_32F:
{
kernel.create(ksize,ksize,CV_32F);
break;
}
default:
return;
}
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cvtest::randUni(rng, kernel, Scalar::all(INT8_MIN), Scalar::all(INT8_MAX));
cv::fastcv::dsp::filter2D(src, dst, ddepth, kernel);
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
cv::filter2D(src, ref, ddepth, kernel);
cv::Mat difference;
dst.convertTo(dst, CV_8U);
ref.convertTo(ref, CV_8U);
cv::absdiff(dst, ref, difference);
int num_diff_pixels = cv::countNonZero(difference);
EXPECT_LT(num_diff_pixels, (src.rows+src.cols)*ksize);
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, Filter2DTest_DSP, Combine(
/*image size*/ Values(perf::szVGA, perf::sz720p),
/*dst depth*/ Values(CV_8U,CV_16S,CV_32F),
/*kernel size*/ Values(3, 5, 7, 9, 11)
));
}} // namespaces opencv_test, ::
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef std::tuple<Size, int> ChannelMergeTestParams;
class ChannelMergeTest : public ::testing::TestWithParam<ChannelMergeTestParams> {};
typedef std::tuple<Size, int> ChannelSplitTestParams;
class ChannelSplitTest : public ::testing::TestWithParam<ChannelSplitTestParams> {};
TEST_P(ChannelMergeTest, accuracy)
{
int depth = CV_8UC1;
Size sz = std::get<0>(GetParam());
int count = std::get<1>(GetParam());
std::vector<Mat> src_mats;
RNG& rng = cv::theRNG();
for(int i = 0; i < count; i++)
{
Mat tmp(sz, depth);
src_mats.push_back(tmp);
cvtest::randUni(rng, src_mats[i], Scalar::all(0), Scalar::all(127));
}
Mat dst;
cv::fastcv::merge(src_mats, dst);
Mat ref;
cv::merge(src_mats, ref);
double normInf = cvtest::norm(ref, dst, cv::NORM_INF);
EXPECT_EQ(normInf, 0);
}
TEST_P(ChannelSplitTest, accuracy)
{
Size sz = std::get<0>(GetParam());
int cn = std::get<1>(GetParam());
std::vector<Mat> dst_mats(cn), ref_mats(cn);
RNG& rng = cv::theRNG();
Mat src(sz, CV_MAKE_TYPE(CV_8U,cn));
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(127));
cv::fastcv::split(src, dst_mats);
cv::split(src, ref_mats);
for(int i=0; i<cn; i++)
{
double normInf = cvtest::norm(ref_mats[i], dst_mats[i], cv::NORM_INF);
EXPECT_EQ(normInf, 0);
}
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ChannelMergeTest,
::testing::Combine(::testing::Values(perf::szODD, perf::szVGA, perf::sz720p, perf::sz1080p), // sz
::testing::Values(2,3,4))); // count
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ChannelSplitTest,
::testing::Combine(::testing::Values(perf::szODD, perf::szVGA, perf::sz720p, perf::sz1080p), // sz
::testing::Values(2,3,4))); // cn
}} // namespaces opencv_test, ::
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
// nPts, nDims, nClusters
typedef std::tuple<int, int, int> ClusterEuclideanTestParams;
class ClusterEuclideanTest : public ::testing::TestWithParam<ClusterEuclideanTestParams> {};
TEST_P(ClusterEuclideanTest, accuracy)
{
auto p = GetParam();
int nPts = std::get<0>(p);
int nDims = std::get<1>(p);
int nClusters = std::get<2>(p);
Mat points(nPts, nDims, CV_8U);
Mat clusterCenters(nClusters, nDims, CV_32F);
Mat trueMeans(nClusters, nDims, CV_32F);
Mat stddevs(nClusters, nDims, CV_32F);
std::vector<int> trueClusterSizes(nClusters, 0);
std::vector<int> trueClusterBindings(nPts, 0);
std::vector<float> trueSumDists(nClusters, 0);
cv::RNG& rng = cv::theRNG();
for (int i = 0; i < nClusters; i++)
{
Mat mean(1, nDims, CV_64F), stdev(1, nDims, CV_64F);
rng.fill(mean, cv::RNG::UNIFORM, 0, 256);
rng.fill(stdev, cv::RNG::UNIFORM, 5.f, 16);
int lo = i * nPts / nClusters;
int hi = (i + 1) * nPts / nClusters;
for (int d = 0; d < nDims; d++)
{
rng.fill(points.col(d).rowRange(lo, hi), cv::RNG::NORMAL,
mean.at<double>(d), stdev.at<double>(d));
}
float sd = 0;
for (int j = lo; j < hi; j++)
{
Mat pts64f;
points.row(j).convertTo(pts64f, CV_64F);
sd += cv::norm(mean, pts64f, NORM_L2);
trueClusterBindings.at(j) = i;
trueClusterSizes.at(i)++;
}
trueSumDists.at(i) = sd;
// let's shift initial cluster center a bit
Mat(mean + stdev * 0.5).copyTo(clusterCenters.row(i));
mean.copyTo(trueMeans.row(i));
stdev.copyTo(stddevs.row(i));
}
Mat newClusterCenters;
std::vector<int> clusterSizes, clusterBindings;
std::vector<float> clusterSumDists;
cv::fastcv::clusterEuclidean(points, clusterCenters, newClusterCenters, clusterSizes, clusterBindings, clusterSumDists);
if (cvtest::debugLevel > 0 && nDims == 2)
{
Mat draw(256, 256, CV_8UC3, Scalar(0));
for (int i = 0; i < nPts; i++)
{
int x = std::rint(points.at<uchar>(i, 0));
int y = std::rint(points.at<uchar>(i, 1));
draw.at<Vec3b>(y, x) = Vec3b::all(128);
}
for (int i = 0; i < nClusters; i++)
{
float cx = trueMeans.at<double>(i, 0);
float cy = trueMeans.at<double>(i, 1);
draw.at<Vec3b>(cy, cx) = Vec3b(0, 255, 0);
float sx = stddevs.at<double>(i, 0);
float sy = stddevs.at<double>(i, 1);
cv::ellipse(draw, Point(cx, cy), Size(sx, sy), 0, 0, 360, Scalar(255, 0, 0));
float ox = clusterCenters.at<float>(i, 0);
float oy = clusterCenters.at<float>(i, 1);
draw.at<Vec3b>(oy, ox) = Vec3b(0, 0, 255);
float nx = newClusterCenters.at<float>(i, 0);
float ny = newClusterCenters.at<float>(i, 1);
draw.at<Vec3b>(ny, nx) = Vec3b(255, 255, 0);
}
cv::imwrite(cv::format("draw_%d_%d_%d.png", nPts, nDims, nClusters), draw);
}
{
std::vector<double> diffs;
for (int i = 0; i < nClusters; i++)
{
double cs = std::abs((trueClusterSizes[i] - clusterSizes[i]) / double(trueClusterSizes[i]));
diffs.push_back(cs);
}
double normL2 = cv::norm(diffs, NORM_L2) / nClusters;
EXPECT_LT(normL2, 0.392);
}
{
Mat bindings8u, trueBindings8u;
Mat(clusterBindings).convertTo(bindings8u, CV_8U);
Mat(trueClusterBindings).convertTo(trueBindings8u, CV_8U);
double normH = cv::norm(bindings8u, trueBindings8u, NORM_HAMMING) / nPts;
EXPECT_LT(normH, 0.66);
}
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ClusterEuclideanTest,
::testing::Combine(::testing::Values(100, 1000, 10000), // nPts
::testing::Values(2, 10, 32), // nDims
::testing::Values(5, 10, 16))); // nClusters
}} // namespaces opencv_test, ::
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static inline void fillRandom8U(cv::Mat& m)
{
cv::RNG& rng = cv::theRNG();
rng.fill(m, cv::RNG::UNIFORM, 0, 256);
}
TEST(Fastcv_cvtColor, YUV420_to_YUV422_and_back_roundtrip)
{
const cv::Size sz(640, 480);
cv::Mat bgr(sz, CV_8UC3);
fillRandom8U(bgr);
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
cv::Mat yuv420_before;
yuv420_before.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(rgb, yuv420_before, cv::fastcv::COLOR_RGB2YUV_NV12);
cv::Mat yuv422;
yuv422.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv420_before, yuv422, cv::fastcv::COLOR_YUV2YUV422sp_NV12);
cv::Mat yuv422_to_bgr;
cv::Mat yuv420_after;
yuv420_after.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv422, yuv420_after, cv::fastcv::COLOR_YUV422sp2YUV_NV12);
ASSERT_EQ(yuv420_before.size(), yuv420_after.size());
ASSERT_EQ(yuv420_before.type(), yuv420_after.type());
double maxDiff = cv::norm(yuv420_before, yuv420_after, cv::NORM_INF);
std::cout << "Max difference YUV420 before vs after = " << maxDiff << std::endl;
EXPECT_LE(maxDiff, 1.0);
}
TEST(Fastcv_cvtColor, YUV444_to_YUV420_and_back_roundtrip)
{
const cv::Size sz(640, 480);
cv::Mat bgr(sz, CV_8UC3);
fillRandom8U(bgr);
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
cv::Mat yuv444_initial;
yuv444_initial.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(rgb, yuv444_initial, cv::fastcv::COLOR_RGB2YUV444sp);
cv::Mat yuv420;
yuv420.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv444_initial, yuv420, cv::fastcv::COLOR_YUV444sp2YUV_NV12);
cv::Mat yuv444_final;
yuv444_final.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv420, yuv444_final, cv::fastcv::COLOR_YUV2YUV444sp_NV12);
ASSERT_EQ(yuv444_initial.size(), yuv444_final.size());
ASSERT_EQ(yuv444_initial.type(), yuv444_final.type());
double maxDiff = cv::norm(yuv444_initial, yuv444_final, cv::NORM_INF);
std::cout << "Max difference YUV444 before vs after roundtrip = " << maxDiff << std::endl;
EXPECT_LE(maxDiff, 2.0);
}
TEST(Fastcv_cvtColor, YUV444_to_YUV422_and_back_roundtrip)
{
const cv::Size sz(640, 480);
cv::Mat bgr(sz, CV_8UC3);
fillRandom8U(bgr);
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
cv::Mat yuv444_initial;
yuv444_initial.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(rgb, yuv444_initial, cv::fastcv::COLOR_RGB2YUV444sp);
cv::Mat yuv422;
yuv422.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv444_initial, yuv422, cv::fastcv::COLOR_YUV444sp2YUV422sp);
cv::Mat yuv444_final;
yuv444_final.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv422, yuv444_final, cv::fastcv::COLOR_YUV422sp2YUV444sp);
ASSERT_EQ(yuv444_initial.size(), yuv444_final.size());
ASSERT_EQ(yuv444_initial.type(), yuv444_final.type());
double maxDiff = cv::norm(yuv444_initial, yuv444_final, cv::NORM_INF);
std::cout << "Max difference YUV444 before vs after roundtrip = " << maxDiff << std::endl;
EXPECT_LE(maxDiff, 2.0);
}
TEST(Fastcv_cvtColor, YUV444_to_RGB565_and_back_roundtrip)
{
const cv::Size sz(640, 480);
cv::Mat bgr(sz, CV_8UC3);
fillRandom8U(bgr);
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
cv::Mat yuv444_initial;
yuv444_initial.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(rgb, yuv444_initial, cv::fastcv::COLOR_RGB2YUV444sp);
cv::Mat rgb565(sz, CV_8UC2);
rgb565.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(yuv444_initial, rgb565, cv::fastcv::COLOR_YUV444sp2RGB565);
cv::Mat yuv444_roundtrip;
yuv444_roundtrip.allocator = cv::fastcv::getQcAllocator();
cv::fastcv::cvtColor(rgb565, yuv444_roundtrip, cv::fastcv::COLOR_RGB5652YUV444sp);
ASSERT_EQ(yuv444_initial.size(), yuv444_roundtrip.size());
ASSERT_EQ(yuv444_initial.type(), yuv444_roundtrip.type());
double maxDiff = cv::norm(yuv444_initial, yuv444_roundtrip, cv::NORM_INF);
std::cout << "Max difference YUV444 after RGB565 roundtrip = " << maxDiff << std::endl;
EXPECT_LE(maxDiff, 2.0);
}
}} // namespace opencv_test
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef testing::TestWithParam<tuple<Size, int, int, int>> Sobel;
typedef testing::TestWithParam<tuple<Size, int>> Sobel3x3u8;
TEST_P(Sobel,accuracy)
{
Size srcSize = get<0>(GetParam());
int ksize = get<1>(GetParam());
int border = get<2>(GetParam());
int borderValue = get<3>(GetParam());
cv::Mat dx, dy, src(srcSize, CV_8U), refx, refy;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cv::fastcv::sobel(src, dx, dy, ksize, border, borderValue);
cv::Sobel(src, refx, CV_16S, 1, 0, ksize, 1.0, 0.0, border);
cv::Sobel(src, refy, CV_16S, 0, 1, ksize, 1.0, 0.0, border);
cv::Mat difference_x, difference_y;
cv::absdiff(dx, refx, difference_x);
cv::absdiff(dy, refy, difference_y);
int num_diff_pixels_x = cv::countNonZero(difference_x);
int num_diff_pixels_y = cv::countNonZero(difference_y);
EXPECT_LT(num_diff_pixels_x, src.size().area()*0.1);
EXPECT_LT(num_diff_pixels_y, src.size().area()*0.1);
}
TEST_P(Sobel3x3u8,accuracy)
{
Size srcSize = get<0>(GetParam());
int ddepth = get<1>(GetParam());
cv::Mat dx, dy, src(srcSize, CV_8U), refx, refy;
RNG& rng = cv::theRNG();
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255));
cv::fastcv::sobel3x3u8(src, dx, dy, ddepth, 0);
cv::Sobel(src, refx, ddepth, 1, 0);
cv::Sobel(src, refy, ddepth, 0, 1);
cv::Mat difference_x, difference_y;
cv::absdiff(dx, refx, difference_x);
cv::absdiff(dy, refy, difference_y);
int num_diff_pixels_x = cv::countNonZero(difference_x);
int num_diff_pixels_y = cv::countNonZero(difference_y);
EXPECT_LT(num_diff_pixels_x, src.size().area()*0.1);
EXPECT_LT(num_diff_pixels_y, src.size().area()*0.1);
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, Sobel, Combine(
/*image size*/ Values(perf::szVGA, perf::sz720p, perf::sz1080p),
/*kernel size*/ Values(3,5,7),
/*border*/ Values(BORDER_CONSTANT, BORDER_REPLICATE),
/*border value*/ Values(0)
));
INSTANTIATE_TEST_CASE_P(FastCV_Extension, Sobel3x3u8, Combine(
/*image size*/ Values(perf::szVGA, perf::sz720p, perf::sz1080p),
/*dst depth*/ Values(CV_16S, CV_32F)
));
}
}
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
TEST(DSP_CannyTest, accuracy)
{
applyTestTag(CV_TEST_TAG_FASTCV_SKIP_DSP);
//Initialize DSP
int initStatus = cv::fastcv::dsp::fcvdspinit();
ASSERT_EQ(initStatus, 0) << "Failed to initialize FastCV DSP";
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator();
cv::imread(cvtest::findDataFile("cv/detectors_descriptors_evaluation/planar/box_in_scene.png"), src, cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(src.empty()) << "Could not read the image file.";
cv::Mat dst;
dst.allocator = cv::fastcv::getQcAllocator();
int lowThreshold = 0;
int highThreshold = 150;
cv::fastcv::dsp::Canny(src, dst, lowThreshold, highThreshold, 3, true);
//De-Initialize DSP
cv::fastcv::dsp::fcvdspdeinit();
EXPECT_FALSE(dst.empty());
EXPECT_EQ(src.size(), dst.size());
}
}
}
+65
View File
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
typedef std::tuple<bool /*useScores*/, int /*barrier*/, int /*border*/, bool /*nmsEnabled*/> Fast10TestParams;
class Fast10Test : public ::testing::TestWithParam<Fast10TestParams> {};
TEST_P(Fast10Test, accuracy)
{
auto p = GetParam();
bool useScores = std::get<0>(p);
int barrier = std::get<1>(p);
int border = std::get<2>(p);
bool nmsEnabled = std::get<3>(p);
cv::Mat src = imread(cvtest::findDataFile("cv/shared/baboon.png"), cv::IMREAD_GRAYSCALE);
std::vector<int> coords, scores;
cv::fastcv::FAST10(src, noArray(), coords, useScores ? scores : noArray(), barrier, border, nmsEnabled);
std::vector<KeyPoint> ocvKeypoints;
int thresh = barrier;
cv::FAST(src, ocvKeypoints, thresh, nmsEnabled, FastFeatureDetector::DetectorType::TYPE_9_16 );
if (useScores)
{
ASSERT_EQ(scores.size() * 2, coords.size());
}
Mat ptsMap(src.size(), CV_8U, Scalar(255));
for(size_t i = 0; i < coords.size() / 2; ++i)
{
ptsMap.at<uchar>(coords[2*i + 1], coords[2*i + 0]) = 0;
}
Mat distTrans(src.size(), CV_8U);
cv::distanceTransform(ptsMap, distTrans, DIST_L2, DIST_MASK_PRECISE);
Mat refPtsMap(src.size(), CV_8U, Scalar(255));
for(size_t i = 0; i < ocvKeypoints.size(); ++i)
{
refPtsMap.at<uchar>(ocvKeypoints[i].pt) = 0;
}
Mat refDistTrans(src.size(), CV_8U);
cv::distanceTransform(refPtsMap, refDistTrans, DIST_L2, DIST_MASK_PRECISE);
double normInf = cvtest::norm(refDistTrans, distTrans, cv::NORM_INF);
double normL2 = cvtest::norm(refDistTrans, distTrans, cv::NORM_L2) / src.size().area();
EXPECT_LT(normInf, 129.7);
EXPECT_LT(normL2, 0.067);
}
INSTANTIATE_TEST_CASE_P(FastCV_Extension, Fast10Test,
::testing::Combine(::testing::Bool(), // useScores
::testing::Values(10, 30, 50), // barrier
::testing::Values( 4, 10, 32), // border
::testing::Bool() // nonmax suppression
));
}} // namespaces opencv_test, ::

Some files were not shown because too many files have changed in this diff Show More