From 252403bbf2fc560007c2c9057db5a9a151e99dd7 Mon Sep 17 00:00:00 2001 From: Andrei Tamas Date: Tue, 12 Aug 2025 14:34:42 +0300 Subject: [PATCH] Merge pull request #27600 from atamas19:ov_output_clamping G-API: Implement cfgClampOutputs option to OpenVINO Params #27600 Added the option `cfgClampOutputs` to control where output clamping is performed for OpenVINO models. When enabled, output values are clamped in the PrePostProcessor stage instead of by the device or plugin. This provides a consistent and standardized clamping method across devices, helping to maintain accuracy regardless of device-specific clamping behavior. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../gapi/include/opencv2/gapi/infer/ov.hpp | 28 ++++++++++++++++ modules/gapi/src/backends/ov/govbackend.cpp | 33 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/modules/gapi/include/opencv2/gapi/infer/ov.hpp b/modules/gapi/include/opencv2/gapi/infer/ov.hpp index 9515744cac..3673ff53a2 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ov.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ov.hpp @@ -66,6 +66,8 @@ struct ParamDesc { LayerVariantAttr> scale_values; LayerVariantAttr interpolation; + + bool clamp_outputs = false; }; struct CompiledModel { @@ -356,6 +358,24 @@ public: return *this; } + /** @brief Enables or disables clamping of model outputs in the PrePostProcessor. + + By default, output values are clamped to the valid range for the output precision + by the device or plugin. Enabling this option moves clamping to the PrePostProcessor stage. + + @note This feature is only available with OpenVINO 2025.2 and newer. + + @param flag If true, clamping is performed in the PrePostProcessor; + otherwise, it is handled by the device or plugin. + @return reference to this parameter structure. + */ + Params& + cfgClampOutputs(bool flag = true) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "clamp outputs") + .clamp_outputs = std::move(flag); + return *this; + } + /** @brief Specifies the new shape for input layers. The function is used to set new shape for input layers. @@ -625,6 +645,14 @@ public: return *this; } + /** @see ov::Params::cfgClampOutputs. */ + Params& + cfgClampOutputs(bool flag = true) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "clamp outputs") + .clamp_outputs = std::move(flag); + return *this; + } + /** @see ov::Params::cfgReshape. */ Params& cfgReshape(std::vector new_shape) { detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index dbaba382db..4ea1c1cc0f 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -147,6 +147,25 @@ static int toCV(const ov::element::Type &type) { return -1; } +static inline std::pair get_CV_type_range(int cv_type) { + switch (cv_type) { + case CV_8U: + return { static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()) }; + case CV_32S: + return { static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()) }; + case CV_32F: + return { static_cast(std::numeric_limits::lowest()), + static_cast(std::numeric_limits::max()) }; + case CV_16F: + return { -65504.0, 65504.0 }; + default: + GAPI_Error("OV Backend: Unsupported data type"); + } + return {0.0, 0.0}; +} + static void copyFromOV(const ov::Tensor &tensor, cv::Mat &mat) { const auto total = mat.total() * mat.channels(); if (toCV(tensor.get_element_type()) != mat.depth() || @@ -1052,6 +1071,20 @@ public: if (explicit_out_tensor_prec) { m_ppp.output(output_name).tensor() .set_element_type(toOV(*explicit_out_tensor_prec)); + + if (m_model_info.clamp_outputs) { + #if INF_ENGINE_RELEASE >= 2025020000 + auto clamp_range = get_CV_type_range(*explicit_out_tensor_prec); + m_ppp.output(output_name).postprocess() + .clamp(clamp_range.first, clamp_range.second); + #else + static bool warned = false; + if (!warned) { + GAPI_LOG_WARNING(NULL, "cfgClampOutputs is enabled, but not supported in this OpenVINO version. Clamping will be ignored."); + warned = true; + } + #endif // INF_ENGINE_RELEASE >= 2025020000 + } } } }