1
0
mirror of https://github.com/opencv/opencv_contrib.git synced 2025-10-23 18:09:25 +08:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2025-07-28 10:07:59 +03:00
7 changed files with 611 additions and 4 deletions

View File

@@ -19,20 +19,24 @@ if(OCV_DEPENDENCIES_FOUND)
if(NOT ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
ocv_check_windows_crt_linkage()
set(target_libs ${target_libs} ${CUDA_LIBRARIES})
set(test_cudev_cuda_options "")
if(CUDA_VERSION VERSION_LESS "11.0")
# Windows version does not support --std option
if(UNIX OR APPLE)
ocv_update(OPENCV_CUDA_OPTIONS_opencv_test_cudev "-std=c++11")
list(APPEND test_cudev_cuda_options "-std=c++11")
endif()
else()
if(CUDA_VERSION VERSION_LESS "12.8")
ocv_update(OPENCV_CUDA_OPTIONS_opencv_test_cudev "-std=c++14")
list(APPEND test_cudev_cuda_options "-std=c++14")
else()
ocv_update(OPENCV_CUDA_OPTIONS_opencv_test_cudev "-std=c++17")
list(APPEND test_cudev_cuda_options "-std=c++17")
if(WIN32)
list(APPEND test_cudev_cuda_options "-Xcompiler=/Zc:preprocessor")
endif()
endif()
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wdeprecated-declarations)
endif()
CUDA_ADD_EXECUTABLE(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} OPTIONS ${OPENCV_CUDA_OPTIONS_opencv_test_cudev})
CUDA_ADD_EXECUTABLE(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} OPTIONS ${test_cudev_cuda_options})
else()
ocv_add_executable(${the_target} ${OPENCV_TEST_${the_module}_SOURCES})
endif()

View File

@@ -37,6 +37,7 @@
#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

View File

@@ -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

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

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

View File

@@ -99,6 +99,8 @@ struct ParamDesc {
PluginConfigT config;
size_t nireq = 1;
bool ensure_named_tensors = false;
};
// NB: Just helper to avoid code duplication.
@@ -205,6 +207,24 @@ public:
return *this;
}
/** @brief Ensures the model has named tensors.
This function is used to ensure that all tensors in the model have names.
It goes through all input and output nodes of the model and sets the names
if they are not set. This is neccessary for models with nameless tensors.
If a tensor does not have a name, it will be assigned a default name
based on the producer node's friendly name. If the producer node has multiple
outputs, the name will be in the form "node_name:N", where N is the output index.
@param flag If true, then it guarantees that all tensors will have names.
@return reference to this parameter structure.
*/
Params<Net>& cfgEnsureNamedTensors(bool flag = true) {
m_desc.ensure_named_tensors = flag;
return *this;
}
/** @brief Specifies tensor layout for an input layer.
The function is used to set tensor layout for an input layer.
@@ -524,6 +544,12 @@ public:
return *this;
}
/** @see ov::Params::cfgEnsureNamedTensors. */
Params& cfgEnsureNamedTensors(bool flag = true) {
m_desc.ensure_named_tensors = flag;
return *this;
}
/** @see ov::Params::cfgInputTensorLayout. */
Params& cfgInputTensorLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")

View File

@@ -69,6 +69,27 @@ ov::Core cv::gapi::ov::wrap::getCore() {
? create_OV_Core_pointer() : create_OV_Core_instance();
}
static std::string make_default_tensor_name(const ov::Output<const ov::Node>& output) {
auto default_name = output.get_node()->get_friendly_name();
if (output.get_node()->get_output_size() > 1) {
default_name += ':' + std::to_string(output.get_index());
}
return default_name;
}
static void ensureNamedTensors(std::shared_ptr<ov::Model> model) {
for (auto& input : model->inputs()) {
if (input.get_names().empty()) {
input.set_names({make_default_tensor_name(input)});
}
}
for (auto& output : model->outputs()) {
if (output.get_names().empty()) {
output.set_names({make_default_tensor_name(output)});
}
}
}
static ov::AnyMap toOV(const ParamDesc::PluginConfigT &config) {
return {config.begin(), config.end()};
}
@@ -236,6 +257,10 @@ struct OVUnit {
.read_model(desc.model_path, desc.bin_path);
GAPI_Assert(model);
if (params.ensure_named_tensors) {
ensureNamedTensors(model);
}
if (params.num_in == 1u && params.input_names.empty()) {
params.input_names = { model->inputs().begin()->get_any_name() };
}