From 2b8094f91573ee1be55ba20f5fc6c507f546ea0d Mon Sep 17 00:00:00 2001 From: lgx Date: Mon, 2 May 2022 20:42:41 +0800 Subject: [PATCH 001/313] fix a blur3x3 function --- 3rdparty/carotene/src/blur.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/carotene/src/blur.cpp b/3rdparty/carotene/src/blur.cpp index 798cce5a71..21689a2bd3 100644 --- a/3rdparty/carotene/src/blur.cpp +++ b/3rdparty/carotene/src/blur.cpp @@ -391,9 +391,9 @@ void blur3x3(const Size2D &size, s32 cn, } else if (borderType == BORDER_MODE_REFLECT101) { - tcurr = vsetq_lane_u16(vgetq_lane_u16(tcurr, 3),tcurr, 5); tcurr = vsetq_lane_u16(vgetq_lane_u16(tcurr, 4),tcurr, 6); tcurr = vsetq_lane_u16(vgetq_lane_u16(tcurr, 5),tcurr, 7); + tcurr = vsetq_lane_u16(vgetq_lane_u16(tcurr, 3),tcurr, 5); } else { From a3d6994afaaa546b328bfc0f56e2430f61878348 Mon Sep 17 00:00:00 2001 From: xiong-jie-y Date: Thu, 26 May 2022 01:12:51 +0900 Subject: [PATCH 002/313] Add stateful kernel to python G-API --- .../include/opencv2/gapi/python/python.hpp | 14 ++- modules/gapi/misc/python/pyopencv_gapi.hpp | 106 +++++++++++++++++- .../src/backends/python/gpythonbackend.cpp | 41 ++++++- 3 files changed, 148 insertions(+), 13 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/python/python.hpp b/modules/gapi/include/opencv2/gapi/python/python.hpp index 0e20bbbb59..72ed6f5850 100644 --- a/modules/gapi/include/opencv2/gapi/python/python.hpp +++ b/modules/gapi/include/opencv2/gapi/python/python.hpp @@ -31,17 +31,26 @@ struct GPythonContext const cv::GArgs &ins; const cv::GMetaArgs &in_metas; const cv::GTypesInfo &out_info; + + bool m_isStateful; + + GArg m_state; }; using Impl = std::function; +using Setup = std::function; class GAPI_EXPORTS GPythonKernel { public: GPythonKernel() = default; - GPythonKernel(Impl run); + GPythonKernel(Impl run, Setup setup); cv::GRunArgs operator()(const GPythonContext& ctx); + + bool m_isStateful = false; + Setup m_setup = nullptr; + private: Impl m_run; }; @@ -51,7 +60,8 @@ class GAPI_EXPORTS GPythonFunctor : public cv::gapi::GFunctor public: using Meta = cv::GKernel::M; - GPythonFunctor(const char* id, const Meta &meta, const Impl& impl); + GPythonFunctor(const char* id, const Meta& meta, const Impl& impl, + const Setup& setup = nullptr); GKernelImpl impl() const override; gapi::GBackend backend() const override; diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 7b760920e7..0812b283b7 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -660,7 +660,7 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, // NB: Doesn't increase reference counter (false), // because PyObject already have ownership. // In case exception decrement reference counter. - cv::detail::PyObjectHolder args(PyTuple_New(ins.size()), false); + cv::detail::PyObjectHolder args(PyTuple_New(ctx.m_isStateful ? ins.size() + 1: ins.size()), false); for (size_t i = 0; i < ins.size(); ++i) { // NB: If meta is monostate then object isn't associated with G-TYPE. @@ -690,6 +690,12 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, } ++in_idx; } + + if (ctx.m_isStateful) + { + PyTuple_SetItem(args.get(), ins.size(), pyopencv_from(ctx.m_state)); + } + // NB: Doesn't increase reference counter (false). // In case PyObject_CallObject return NULL, do nothing in destructor. cv::detail::PyObjectHolder result( @@ -736,6 +742,80 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, return outs; } +static cv::GArg setup_py(cv::detail::PyObjectHolder out_meta, const cv::GMetaArgs& meta, + const cv::GArgs& gargs) +{ + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GArg out; + + try + { + // NB: Doesn't increase reference counter (false), + // because PyObject already have ownership. + // In case exception decrement reference counter. + cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false); + size_t idx = 0; + for (auto&& m : meta) + { + switch (m.index()) + { + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), idx, pyopencv_from(gargs[idx])); + break; + case cv::GMetaArg::index_of(): + util::throw_error( + std::logic_error("GFrame isn't supported for custom operation")); + break; + } + ++idx; + } + + // PyTuple_SetItem(args.get(), idx, pyopencv_from(gmarg)); + + // NB: Doesn't increase reference counter (false). + // In case PyObject_CallObject return NULL, do nothing in destructor. + cv::detail::PyObjectHolder result(PyObject_CallObject(out_meta.get(), args.get()), false); + + if (PyErr_Occurred()) + { + PyErr_PrintEx(0); + PyErr_Clear(); + throw std::logic_error("Python kernel failed with error!"); + } + // NB: In fact it's impossible situation, because errors were handled above. + GAPI_Assert(result.get() && "Python kernel returned NULL!"); + + if (!pyopencv_to(result.get(), out, ArgInfo("arg", false))) + { + util::throw_error(std::logic_error("Unsupported output meta type")); + } + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + return out; +} + static GMetaArg get_meta_arg(PyObject* obj) { cv::GMetaArg arg; @@ -860,6 +940,10 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec "Python kernel should contain run, please use cv.gapi.kernel to define kernel"); return NULL; } + PyObject* setup = nullptr; + if (PyObject_HasAttrString(user_kernel, "setup")) { + setup = PyObject_GetAttrString(user_kernel, "setup"); + } std::string id; if (!pyopencv_to(id_obj, id, ArgInfo("id", false))) @@ -869,10 +953,22 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec } using namespace std::placeholders; - gapi::python::GPythonFunctor f(id.c_str(), - std::bind(run_py_meta , cv::detail::PyObjectHolder{out_meta}, _1, _2), - std::bind(run_py_kernel, cv::detail::PyObjectHolder{run} , _1)); - pkg.include(f); + + if (setup) + { + gapi::python::GPythonFunctor f( + id.c_str(), std::bind(run_py_meta, cv::detail::PyObjectHolder{out_meta}, _1, _2), + std::bind(run_py_kernel, cv::detail::PyObjectHolder{run}, _1), + std::bind(setup_py, cv::detail::PyObjectHolder{setup}, _1, _2)); + pkg.include(f); + } + else + { + gapi::python::GPythonFunctor f( + id.c_str(), std::bind(run_py_meta, cv::detail::PyObjectHolder{out_meta}, _1, _2), + std::bind(run_py_kernel, cv::detail::PyObjectHolder{run}, _1)); + pkg.include(f); + } } return pyopencv_from(pkg); } diff --git a/modules/gapi/src/backends/python/gpythonbackend.cpp b/modules/gapi/src/backends/python/gpythonbackend.cpp index 4361bab75d..4bfdc7d3ec 100644 --- a/modules/gapi/src/backends/python/gpythonbackend.cpp +++ b/modules/gapi/src/backends/python/gpythonbackend.cpp @@ -6,15 +6,19 @@ #include // zip_range, indexed +#include "compiler/gmodel.hpp" +#include #include // throw_error #include #include "api/gbackend_priv.hpp" #include "backends/common/gbackend.hpp" -cv::gapi::python::GPythonKernel::GPythonKernel(cv::gapi::python::Impl run) - : m_run(run) +cv::gapi::python::GPythonKernel::GPythonKernel(cv::gapi::python::Impl run, + cv::gapi::python::Setup setup) + : m_run(run), m_setup(setup) { + m_isStateful = (setup != nullptr); } cv::GRunArgs cv::gapi::python::GPythonKernel::operator()(const cv::gapi::python::GPythonContext& ctx) @@ -23,9 +27,10 @@ cv::GRunArgs cv::gapi::python::GPythonKernel::operator()(const cv::gapi::python: } cv::gapi::python::GPythonFunctor::GPythonFunctor(const char* id, - const cv::gapi::python::GPythonFunctor::Meta &meta, - const cv::gapi::python::Impl& impl) - : gapi::GFunctor(id), impl_{GPythonKernel{impl}, meta} + const cv::gapi::python::GPythonFunctor::Meta& meta, + const cv::gapi::python::Impl& impl, + const cv::gapi::python::Setup& setup) + : gapi::GFunctor(id), impl_{GPythonKernel{impl, setup}, meta} { } @@ -68,6 +73,7 @@ class GPythonExecutable final: public cv::gimpl::GIslandExecutable virtual cv::RMat allocate(const cv::GMatDesc&) const override { return {}; } virtual bool canReshape() const override { return true; } + virtual void handleNewStream() override; virtual void reshape(ade::Graph&, const cv::GCompileArgs&) override { // Do nothing here } @@ -80,6 +86,7 @@ public: cv::gimpl::GModel::ConstGraph m_gm; cv::gapi::python::GPythonKernel m_kernel; ade::NodeHandle m_op; + cv::GArg m_node_state; cv::GTypesInfo m_out_info; cv::GMetaArgs m_in_metas; @@ -153,6 +160,15 @@ static void writeBack(cv::GRunArg& arg, cv::GRunArgP& out) } } +void GPythonExecutable::handleNewStream() +{ + if (!m_kernel.m_isStateful) + return; + + m_node_state = m_kernel.m_setup(cv::gimpl::GModel::collectInputMeta(m_gm, m_op), + m_gm.metadata(m_op).get().args); +} + void GPythonExecutable::run(std::vector &&input_objs, std::vector &&output_objs) { @@ -165,8 +181,15 @@ void GPythonExecutable::run(std::vector &&input_objs, std::back_inserter(inputs), std::bind(&packArg, std::ref(m_res), _1)); + cv::gapi::python::GPythonContext ctx{inputs, m_in_metas, m_out_info, false}; + + // For stateful kernel add state to its execution context + if (m_kernel.m_isStateful) + { + ctx.m_state = m_node_state; + ctx.m_isStateful = true; + } - cv::gapi::python::GPythonContext ctx{inputs, m_in_metas, m_out_info}; auto outs = m_kernel(ctx); for (auto&& it : ade::util::zip(outs, output_objs)) @@ -225,6 +248,12 @@ GPythonExecutable::GPythonExecutable(const ade::Graph& g, m_op = *it; m_kernel = cag.metadata(m_op).get().kernel; + // If kernel is stateful then prepare storage for its state. + if (m_kernel.m_isStateful) + { + m_node_state = cv::GArg{ }; + } + // Ensure this the only op in the graph if (std::any_of(it+1, nodes.end(), is_op)) { From ea2527c2d18aa7237c90f562d698f63c80e37660 Mon Sep 17 00:00:00 2001 From: xiong-jie-y Date: Fri, 20 May 2022 20:49:15 +0900 Subject: [PATCH 003/313] Add python bindings for G-API onnx --- modules/gapi/CMakeLists.txt | 1 + .../opencv2/gapi/infer/bindings_onnx.hpp | 43 +++++++++++ .../gapi/include/opencv2/gapi/infer/onnx.hpp | 33 +++++++++ modules/gapi/misc/python/pyopencv_gapi.hpp | 1 + modules/gapi/misc/python/shadow_gapi.hpp | 1 + .../misc/python/test/test_gapi_infer_onnx.py | 74 +++++++++++++++++++ .../gapi/src/backends/onnx/bindings_onnx.cpp | 24 ++++++ .../gapi/src/backends/onnx/gonnxbackend.cpp | 32 +++++++- 8 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp create mode 100644 modules/gapi/misc/python/test/test_gapi_infer_onnx.py create mode 100644 modules/gapi/src/backends/onnx/bindings_onnx.cpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 9b97a1b92a..2138df57d7 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -176,6 +176,7 @@ set(gapi_srcs # Python bridge src/backends/ie/bindings_ie.cpp + src/backends/onnx/bindings_onnx.cpp src/backends/python/gpythonbackend.cpp # OpenVPL Streaming source diff --git a/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp new file mode 100644 index 0000000000..d539f6007d --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.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_GAPI_INFER_BINDINGS_ONNX_HPP +#define OPENCV_GAPI_INFER_BINDINGS_ONNX_HPP + +#include // GKernelPackage +#include // Params +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include + +#include + +namespace cv { +namespace gapi { +namespace onnx { + +// NB: Used by python wrapper +// This class can be marked as SIMPLE, because it's implemented as pimpl +class GAPI_EXPORTS_W_SIMPLE PyParams { +public: + GAPI_WRAP + PyParams() = default; + + GAPI_WRAP + PyParams(const std::string& tag, const std::string& model_path); + + GBackend backend() const; + std::string tag() const; + cv::util::any params() const; + +private: + std::shared_ptr> m_priv; +}; + +GAPI_EXPORTS_W PyParams params(const std::string& tag, const std::string& model_path); + +} // namespace onnx +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_BINDINGS_ONNX_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp index 16fc42eb63..90e04a2477 100644 --- a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp @@ -17,6 +17,7 @@ #include // GAPI_EXPORTS #include // GKernelPackage +#include // Generic namespace cv { namespace gapi { @@ -67,6 +68,8 @@ struct ParamDesc { std::vector normalize; //!< Vector of bool values that enabled or disabled normalize of input data. std::vector names_to_remap; //!< Names of output layers that will be processed in PostProc function. + + bool is_generic; }; } // namespace detail @@ -103,6 +106,7 @@ public: desc.model_path = model; desc.num_in = std::tuple_size::value; desc.num_out = std::tuple_size::value; + desc.is_generic = false; }; /** @brief Specifies sequence of network input layers names for inference. @@ -277,6 +281,35 @@ protected: detail::ParamDesc desc; }; +/* +* @brief This structure provides functions for generic network type that +* fill inference parameters. +* @see struct Generic +*/ +template<> +class Params { +public: + /** @brief Class constructor. + + Constructs Params based on input information and sets default values for other + inference description parameters. + + @param tag string tag of the network for which these parameters are intended. + @param model_path path to model file (.onnx file). + */ + Params(const std::string& tag, const std::string& model_path) + : desc{model_path, 0u, 0u, {}, {}, {}, {}, {}, {}, {}, {}, {}, true}, m_tag(tag) {} + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::onnx::backend(); } + std::string tag() const { return m_tag; } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) +protected: + detail::ParamDesc desc; + std::string m_tag; +}; + } // namespace onnx } // namespace gapi } // namespace cv diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 7b760920e7..9327752f2f 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -14,6 +14,7 @@ using gapi_GKernelPackage = cv::GKernelPackage; using gapi_GNetPackage = cv::gapi::GNetPackage; using gapi_ie_PyParams = cv::gapi::ie::PyParams; +using gapi_onnx_PyParams = cv::gapi::onnx::PyParams; using gapi_wip_IStreamSource_Ptr = cv::Ptr; using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback; using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback; diff --git a/modules/gapi/misc/python/shadow_gapi.hpp b/modules/gapi/misc/python/shadow_gapi.hpp index 802f4397a0..cf81335e0b 100644 --- a/modules/gapi/misc/python/shadow_gapi.hpp +++ b/modules/gapi/misc/python/shadow_gapi.hpp @@ -79,5 +79,6 @@ namespace streaming namespace detail { gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params); + gapi::GNetParam GAPI_EXPORTS_W strip(gapi::onnx::PyParams params); } // namespace detail } // namespace cv diff --git a/modules/gapi/misc/python/test/test_gapi_infer_onnx.py b/modules/gapi/misc/python/test/test_gapi_infer_onnx.py new file mode 100644 index 0000000000..443c6d4396 --- /dev/null +++ b/modules/gapi/misc/python/test/test_gapi_infer_onnx.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +import numpy as np +import cv2 as cv +import os +import sys +import unittest + +from tests_common import NewOpenCVTests + + +try: + + if sys.version_info[:2] < (3, 0): + raise unittest.SkipTest('Python 2.x is not supported') + + CLASSIFICATION_MODEL_PATH = "onnx_models/vision/classification/squeezenet/model/squeezenet1.0-9.onnx" + + testdata_required = bool(os.environ.get('OPENCV_DNN_TEST_REQUIRE_TESTDATA', False)) + + class test_gapi_infer(NewOpenCVTests): + def find_dnn_file(self, filename, required=None): + if not required: + required = testdata_required + return self.find_file(filename, [os.environ.get('OPENCV_DNN_TEST_DATA_PATH', os.getcwd()), + os.environ['OPENCV_TEST_DATA_PATH']], + required=required) + + def test_onnx_classification(self): + model_path = self.find_dnn_file(CLASSIFICATION_MODEL_PATH) + + if model_path is None: + raise unittest.SkipTest("Missing DNN test file") + + in_mat = cv.imread( + self.find_file("cv/dpm/cat.png", + [os.environ.get('OPENCV_TEST_DATA_PATH')])) + + g_in = cv.GMat() + g_infer_inputs = cv.GInferInputs() + g_infer_inputs.setInput("data_0", g_in) + g_infer_out = cv.gapi.infer("squeeze-net", g_infer_inputs) + g_out = g_infer_out.at("softmaxout_1") + + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + + net = cv.gapi.onnx.params("squeeze-net", model_path) + try: + out_gapi = comp.apply(cv.gin(in_mat), cv.gapi.compile_args(cv.gapi.networks(net))) + except cv.error as err: + if err.args[0] == "G-API has been compiled without ONNX support": + raise unittest.SkipTest("G-API has been compiled without ONNX support") + else: + raise + + self.assertEqual((1, 1000, 1, 1), out_gapi.shape) + + +except unittest.SkipTest as e: + + message = str(e) + + class TestSkip(unittest.TestCase): + def setUp(self): + self.skipTest('Skip tests: ' + message) + + def test_skip(): + pass + + pass + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/gapi/src/backends/onnx/bindings_onnx.cpp b/modules/gapi/src/backends/onnx/bindings_onnx.cpp new file mode 100644 index 0000000000..c43b86e0c8 --- /dev/null +++ b/modules/gapi/src/backends/onnx/bindings_onnx.cpp @@ -0,0 +1,24 @@ +// 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 + +cv::gapi::onnx::PyParams::PyParams(const std::string& tag, + const std::string& model_path) + : m_priv(std::make_shared>(tag, model_path)) {} + +cv::gapi::GBackend cv::gapi::onnx::PyParams::backend() const { + return m_priv->backend(); +} + +std::string cv::gapi::onnx::PyParams::tag() const { return m_priv->tag(); } + +cv::util::any cv::gapi::onnx::PyParams::params() const { + return m_priv->params(); +} + +cv::gapi::onnx::PyParams cv::gapi::onnx::params( + const std::string& tag, const std::string& model_path) { + return {tag, model_path}; +} diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.cpp b/modules/gapi/src/backends/onnx/gonnxbackend.cpp index af1f7f8948..a99d38654f 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.cpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.cpp @@ -735,7 +735,8 @@ void ONNXCompiled::extractMat(ONNXCallContext &ctx, const size_t in_idx, Views& } } -void ONNXCompiled::setOutput(int i, cv::Mat &m) { +void ONNXCompiled::setOutput(int i, cv::Mat &m) +{ // FIXME: No need in double-indexing? out_data[i] = m; } @@ -1133,9 +1134,34 @@ namespace { // FIXME: Introduce a DNNBackend interface which'd specify // the framework for this??? GONNXModel gm(gr); - const auto &np = gm.metadata(nh).get(); - const auto &pp = cv::util::any_cast(np.opaque); + auto &np = gm.metadata(nh).get(); + auto &pp = cv::util::any_cast(np.opaque); const auto &ki = cv::util::any_cast(ii.opaque); + + GModel::Graph model(gr); + auto& op = model.metadata(nh).get(); + if (pp.is_generic) { + auto& info = cv::util::any_cast(op.params); + + for (const auto& a : info.in_names) + { + pp.input_names.push_back(a); + } + // Adding const input is necessary because the definition of input_names + // includes const input. + for (const auto& a : pp.const_inputs) + { + pp.input_names.push_back(a.first); + } + pp.num_in = info.in_names.size(); + + for (const auto& a : info.out_names) + { + pp.output_names.push_back(a); + } + pp.num_out = info.out_names.size(); + } + gm.metadata(nh).set(ONNXUnit{pp}); gm.metadata(nh).set(ONNXCallable{ki.run}); gm.metadata(nh).set(CustomMetaFunction{ki.customMetaFunc}); From f1328c7395178f8b9bf39bde093866f49996239b Mon Sep 17 00:00:00 2001 From: catree Date: Sun, 19 Jun 2022 22:59:50 +0200 Subject: [PATCH 004/313] Add a small exercise to show the warping of the homography transformations step-by-step. --- doc/tutorials/features2d/homography/homography.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/tutorials/features2d/homography/homography.markdown b/doc/tutorials/features2d/homography/homography.markdown index e5009798c3..b8c17c0856 100644 --- a/doc/tutorials/features2d/homography/homography.markdown +++ b/doc/tutorials/features2d/homography/homography.markdown @@ -416,6 +416,12 @@ The homography matrices are similar. If we compare the image 1 warped using both Visually, it is hard to distinguish a difference between the result image from the homography computed from the camera displacement and the one estimated with @ref cv::findHomography function. +#### Exercise + +This demo shows you how to compute the homography transformation from two camera poses. Try to perform the same operations, but by computing N inter homography this time. Instead of computing one homography to directly warp the source image to the desired camera viewpoint, perform N warping operations to the see the different transformations operating. + +You should get something similar to this video: + ### Demo 4: Decompose the homography matrix {#tutorial_homography_Demo4} OpenCV 3 contains the function @ref cv::decomposeHomographyMat which allows to decompose the homography matrix to a set of rotations, translations and plane normals. From 44c2519d7562374f7741f2a9e2d3be3c96db7e57 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 23 Jun 2022 15:09:17 +0200 Subject: [PATCH 005/313] issues-22141 --- modules/videoio/src/cap_ffmpeg_impl.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 98913ed839..3e8099262d 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1510,10 +1510,6 @@ bool CvCapture_FFMPEG::grabFrame() ret = got_picture ? 0 : -1; #endif if (ret >= 0) { - //picture_pts = picture->best_effort_timestamp; - if( picture_pts == AV_NOPTS_VALUE_ ) - picture_pts = picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_ && picture->CV_FFMPEG_PTS_FIELD != 0 ? picture->CV_FFMPEG_PTS_FIELD : picture->pkt_dts; - valid = true; } else if (ret == AVERROR(EAGAIN)) { continue; @@ -1526,8 +1522,11 @@ bool CvCapture_FFMPEG::grabFrame() } } - if (valid) + if (valid) { + if( picture_pts == AV_NOPTS_VALUE_ ) + picture_pts = picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_ && picture->CV_FFMPEG_PTS_FIELD != 0 ? picture->CV_FFMPEG_PTS_FIELD : picture->pkt_dts; frame_number++; + } if (!rawMode && valid && first_frame_number < 0) first_frame_number = dts_to_frame_number(picture_pts); From 79731cb0ff790481e48d4a6170b60f9683d76de6 Mon Sep 17 00:00:00 2001 From: "@lizhiyu3" Date: Tue, 5 Jul 2022 15:43:52 +0800 Subject: [PATCH 006/313] fix the bug when src*2 < dst --- modules/imgproc/src/pyramids.cpp | 22 ++++++++++++---- modules/imgproc/test/test_pyramid.cpp | 37 ++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 0261dd4de3..e8227b614c 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -983,13 +983,25 @@ pyrUp_( const Mat& _src, Mat& _dst, int) row0 = rows[0]; row1 = rows[1]; row2 = rows[2]; dsts[0] = dst0; dsts[1] = dst1; - x = PyrUpVecV(rows, dsts, dsize.width); - for( ; x < dsize.width; x++ ) + if (dst0 != dst1) { - T t1 = castOp((row1[x] + row2[x])*4); - T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); - dst1[x] = t1; dst0[x] = t0; + x = PyrUpVecV(rows, dsts, dsize.width); + for( ; x < dsize.width; x++ ) + { + T t1 = castOp((row1[x] + row2[x])*4); + T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); + dst1[x] = t1; dst0[x] = t0; + } } + else + { + for(x = 0; x < dsize.width; x++ ) + { + T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); + dst0[x] = t0; + } + } + } if (dsize.height > ssize.height*2) diff --git a/modules/imgproc/test/test_pyramid.cpp b/modules/imgproc/test/test_pyramid.cpp index 343d7a2321..cdf72b7850 100644 --- a/modules/imgproc/test/test_pyramid.cpp +++ b/modules/imgproc/test/test_pyramid.cpp @@ -8,12 +8,41 @@ namespace opencv_test { namespace { TEST(Imgproc_PyrUp, pyrUp_regression_22184) { - Mat src(100, 100, CV_16UC3, Scalar::all(255)); - Mat dst(100 * 2 + 1, 100 * 2 + 1, CV_16UC3, Scalar::all(0)); + Mat src(100,100,CV_16UC3,Scalar(255,255,255)); + Mat dst(100 * 2 + 1, 100 * 2 + 1, CV_16UC3, Scalar(0,0,0)); pyrUp(src, dst, Size(dst.cols, dst.rows)); - double min_val = 0; + double min_val; minMaxLoc(dst, &min_val); ASSERT_GT(cvRound(min_val), 0); } -}} // namespace +TEST(Imgproc_PyrUp, pyrUp_regression_22193) +{ + Mat src(13, 13,CV_16UC3,Scalar(0,0,0)); + { + int swidth = src.cols; + int sheight = src.rows; + int cn = src.channels(); + int count = 0; + for (int y = 0; y < sheight; y++) + { + ushort *src_c = src.ptr(y); + for (int x = 0; x < swidth * cn; x++) + { + src_c[x] = (count++) % 10; + } + } + } + Mat dst(src.cols * 2 - 1, src.rows * 2 - 1, CV_16UC3, Scalar(0,0,0)); + pyrUp(src, dst, Size(dst.cols, dst.rows)); + + { + ushort *dst_c = dst.ptr(dst.rows - 1); + ASSERT_EQ(dst_c[0], 6); + ASSERT_EQ(dst_c[1], 6); + ASSERT_EQ(dst_c[2], 1); + } +} + +} +} From c54ccaac31491ffa0f670e4e12e962dc5326e0b7 Mon Sep 17 00:00:00 2001 From: "@lizhiyu3" Date: Tue, 5 Jul 2022 15:50:33 +0800 Subject: [PATCH 007/313] change test number --- modules/imgproc/test/test_pyramid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_pyramid.cpp b/modules/imgproc/test/test_pyramid.cpp index cdf72b7850..e02e5e343d 100644 --- a/modules/imgproc/test/test_pyramid.cpp +++ b/modules/imgproc/test/test_pyramid.cpp @@ -16,7 +16,7 @@ TEST(Imgproc_PyrUp, pyrUp_regression_22184) ASSERT_GT(cvRound(min_val), 0); } -TEST(Imgproc_PyrUp, pyrUp_regression_22193) +TEST(Imgproc_PyrUp, pyrUp_regression_22194) { Mat src(13, 13,CV_16UC3,Scalar(0,0,0)); { From 01226cb8ac19a664f3752c3ec3bff246943bec44 Mon Sep 17 00:00:00 2001 From: "@lizhiyu3" Date: Tue, 5 Jul 2022 15:43:52 +0800 Subject: [PATCH 008/313] fix the bug when src*2 < dst --- modules/imgproc/src/pyramids.cpp | 140 ++++++++++++++++++++++++-- modules/imgproc/test/test_pyramid.cpp | 37 ++++++- 2 files changed, 167 insertions(+), 10 deletions(-) diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 0261dd4de3..a5f8a71c2c 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -82,6 +82,8 @@ template int PyrDownVecV(T1**, T2*, int) { return 0; } template int PyrUpVecV(T1**, T2**, int) { return 0; } +template int PyrUpVecVOneRow(T1**, T2*, int) { return 0; } + #if CV_SIMD template<> int PyrDownVecH(const uchar* src, int* row, int width) @@ -717,6 +719,120 @@ template <> int PyrUpVecV(float** src, float** dst, int width) return x; } +template <> int PyrUpVecVOneRow(int** src, uchar* dst, int width) +{ + int x = 0; + const int *row0 = src[0], *row1 = src[1], *row2 = src[2]; + + for( ; x <= width - v_uint8::nlanes; x += v_uint8::nlanes) + { + v_int16 v_r00 = v_pack(vx_load(row0 + x), vx_load(row0 + x + v_int32::nlanes)), + v_r01 = v_pack(vx_load(row0 + x + 2 * v_int32::nlanes), vx_load(row0 + x + 3 * v_int32::nlanes)), + v_r10 = v_pack(vx_load(row1 + x), vx_load(row1 + x + v_int32::nlanes)), + v_r11 = v_pack(vx_load(row1 + x + 2 * v_int32::nlanes), vx_load(row1 + x + 3 * v_int32::nlanes)), + v_r20 = v_pack(vx_load(row2 + x), vx_load(row2 + x + v_int32::nlanes)), + v_r21 = v_pack(vx_load(row2 + x + 2 * v_int32::nlanes), vx_load(row2 + x + 3 * v_int32::nlanes)); + v_int16 v_2r10 = v_r10 + v_r10, v_2r11 = (v_r11 + v_r11); + v_store(dst + x, v_rshr_pack_u<6>(v_r00 + v_r20 + (v_2r10 + v_2r10 + v_2r10), v_r01 + v_r21 + (v_2r11 + v_2r11 + v_2r11))); + } + if(x <= width - v_uint16::nlanes) + { + v_int16 v_r00 = v_pack(vx_load(row0 + x), vx_load(row0 + x + v_int32::nlanes)), + v_r10 = v_pack(vx_load(row1 + x), vx_load(row1 + x + v_int32::nlanes)), + v_r20 = v_pack(vx_load(row2 + x), vx_load(row2 + x + v_int32::nlanes)); + v_int16 v_2r10 = v_r10 + v_r10; + v_rshr_pack_u_store<6>(dst + x, v_r00 + v_r20 + (v_2r10 + v_2r10 + v_2r10)); + x += v_uint16::nlanes; + } + typedef int CV_DECL_ALIGNED(1) unaligned_int; + for (; x <= width - v_int32x4::nlanes; x += v_int32x4::nlanes) + { + v_int32 v_r00 = vx_load(row0 + x), + v_r10 = vx_load(row1 + x), + v_r20 = vx_load(row2 + x); + v_int32 v_2r10 = v_r10 + v_r10; + v_int16 d = v_pack(v_r00 + v_r20 + (v_2r10 + v_2r10 + v_2r10), (v_r10 + v_r20) << 2); + *(unaligned_int*)(dst + x) = v_reinterpret_as_s32(v_rshr_pack_u<6>(d, vx_setzero_s16())).get0(); + } + vx_cleanup(); + + return x; +} + +template <> int PyrUpVecVOneRow(int** src, short* dst, int width) +{ + int x = 0; + const int *row0 = src[0], *row1 = src[1], *row2 = src[2]; + + for( ; x <= width - v_int16::nlanes; x += v_int16::nlanes) + { + v_int32 v_r00 = vx_load(row0 + x), + v_r01 = vx_load(row0 + x + v_int32::nlanes), + v_r10 = vx_load(row1 + x), + v_r11 = vx_load(row1 + x + v_int32::nlanes), + v_r20 = vx_load(row2 + x), + v_r21 = vx_load(row2 + x + v_int32::nlanes); + v_store(dst + x, v_rshr_pack<6>(v_r00 + v_r20 + ((v_r10 << 1) + (v_r10 << 2)), v_r01 + v_r21 + ((v_r11 << 1) + (v_r11 << 2)))); + } + if(x <= width - v_int32::nlanes) + { + v_int32 v_r00 = vx_load(row0 + x), + v_r10 = vx_load(row1 + x), + v_r20 = vx_load(row2 + x); + v_rshr_pack_store<6>(dst + x, v_r00 + v_r20 + ((v_r10 << 1) + (v_r10 << 2))); + x += v_int32::nlanes; + } + vx_cleanup(); + + return x; +} + +template <> int PyrUpVecVOneRow(int** src, ushort* dst, int width) +{ + int x = 0; + const int *row0 = src[0], *row1 = src[1], *row2 = src[2]; + + for( ; x <= width - v_uint16::nlanes; x += v_uint16::nlanes) + { + v_int32 v_r00 = vx_load(row0 + x), + v_r01 = vx_load(row0 + x + v_int32::nlanes), + v_r10 = vx_load(row1 + x), + v_r11 = vx_load(row1 + x + v_int32::nlanes), + v_r20 = vx_load(row2 + x), + v_r21 = vx_load(row2 + x + v_int32::nlanes); + v_store(dst + x, v_rshr_pack_u<6>(v_r00 + v_r20 + ((v_r10 << 1) + (v_r10 << 2)), v_r01 + v_r21 + ((v_r11 << 1) + (v_r11 << 2)))); + } + if(x <= width - v_int32::nlanes) + { + v_int32 v_r00 = vx_load(row0 + x), + v_r10 = vx_load(row1 + x), + v_r20 = vx_load(row2 + x); + v_rshr_pack_u_store<6>(dst + x, v_r00 + v_r20 + ((v_r10 << 1) + (v_r10 << 2))); + x += v_int32::nlanes; + } + vx_cleanup(); + + return x; +} + +template <> int PyrUpVecVOneRow(float** src, float* dst, int width) +{ + int x = 0; + const float *row0 = src[0], *row1 = src[1], *row2 = src[2]; + + v_float32 v_6 = vx_setall_f32(6.0f), v_scale = vx_setall_f32(1.f/64.f); + for( ; x <= width - v_float32::nlanes; x += v_float32::nlanes) + { + v_float32 v_r0 = vx_load(row0 + x), + v_r1 = vx_load(row1 + x), + v_r2 = vx_load(row2 + x); + v_store(dst + x, v_scale * (v_muladd(v_6, v_r1, v_r0) + v_r2)); + } + vx_cleanup(); + + return x; +} + #endif template @@ -963,7 +1079,7 @@ pyrUp_( const Mat& _src, Mat& _dst, int) if (dsize.width > ssize.width*2) { - row[(_dst.cols-1) * cn + x] = row[dx + cn]; + row[(_dst.cols-1) + x] = row[dx + cn]; } } @@ -983,12 +1099,24 @@ pyrUp_( const Mat& _src, Mat& _dst, int) row0 = rows[0]; row1 = rows[1]; row2 = rows[2]; dsts[0] = dst0; dsts[1] = dst1; - x = PyrUpVecV(rows, dsts, dsize.width); - for( ; x < dsize.width; x++ ) + if (dst0 != dst1) { - T t1 = castOp((row1[x] + row2[x])*4); - T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); - dst1[x] = t1; dst0[x] = t0; + x = PyrUpVecV(rows, dsts, dsize.width); + for( ; x < dsize.width; x++ ) + { + T t1 = castOp((row1[x] + row2[x])*4); + T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); + dst1[x] = t1; dst0[x] = t0; + } + } + else + { + x = PyrUpVecVOneRow(rows, dst0, dsize.width); + for( ; x < dsize.width; x++ ) + { + T t0 = castOp(row0[x] + row1[x]*6 + row2[x]); + dst0[x] = t0; + } } } diff --git a/modules/imgproc/test/test_pyramid.cpp b/modules/imgproc/test/test_pyramid.cpp index 343d7a2321..e02e5e343d 100644 --- a/modules/imgproc/test/test_pyramid.cpp +++ b/modules/imgproc/test/test_pyramid.cpp @@ -8,12 +8,41 @@ namespace opencv_test { namespace { TEST(Imgproc_PyrUp, pyrUp_regression_22184) { - Mat src(100, 100, CV_16UC3, Scalar::all(255)); - Mat dst(100 * 2 + 1, 100 * 2 + 1, CV_16UC3, Scalar::all(0)); + Mat src(100,100,CV_16UC3,Scalar(255,255,255)); + Mat dst(100 * 2 + 1, 100 * 2 + 1, CV_16UC3, Scalar(0,0,0)); pyrUp(src, dst, Size(dst.cols, dst.rows)); - double min_val = 0; + double min_val; minMaxLoc(dst, &min_val); ASSERT_GT(cvRound(min_val), 0); } -}} // namespace +TEST(Imgproc_PyrUp, pyrUp_regression_22194) +{ + Mat src(13, 13,CV_16UC3,Scalar(0,0,0)); + { + int swidth = src.cols; + int sheight = src.rows; + int cn = src.channels(); + int count = 0; + for (int y = 0; y < sheight; y++) + { + ushort *src_c = src.ptr(y); + for (int x = 0; x < swidth * cn; x++) + { + src_c[x] = (count++) % 10; + } + } + } + Mat dst(src.cols * 2 - 1, src.rows * 2 - 1, CV_16UC3, Scalar(0,0,0)); + pyrUp(src, dst, Size(dst.cols, dst.rows)); + + { + ushort *dst_c = dst.ptr(dst.rows - 1); + ASSERT_EQ(dst_c[0], 6); + ASSERT_EQ(dst_c[1], 6); + ASSERT_EQ(dst_c[2], 1); + } +} + +} +} From e59cff47d404cc41ce3a4c9cc7a407bdbf0bda90 Mon Sep 17 00:00:00 2001 From: "@lizhiyu3" Date: Wed, 20 Jul 2022 17:03:09 +0800 Subject: [PATCH 009/313] fix the right border --- modules/imgproc/src/pyramids.cpp | 2 +- modules/imgproc/test/test_pyramid.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 5727e46255..9b4ad5d840 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -1079,7 +1079,7 @@ pyrUp_( const Mat& _src, Mat& _dst, int) if (dsize.width > ssize.width*2) { - row[(_dst.cols-1) + x] = row[dx + cn]; + row[(_dst.cols-1) * cn + x] = row[dx + cn]; } } diff --git a/modules/imgproc/test/test_pyramid.cpp b/modules/imgproc/test/test_pyramid.cpp index e02e5e343d..0556ce6fa6 100644 --- a/modules/imgproc/test/test_pyramid.cpp +++ b/modules/imgproc/test/test_pyramid.cpp @@ -11,7 +11,7 @@ TEST(Imgproc_PyrUp, pyrUp_regression_22184) Mat src(100,100,CV_16UC3,Scalar(255,255,255)); Mat dst(100 * 2 + 1, 100 * 2 + 1, CV_16UC3, Scalar(0,0,0)); pyrUp(src, dst, Size(dst.cols, dst.rows)); - double min_val; + double min_val = 0; minMaxLoc(dst, &min_val); ASSERT_GT(cvRound(min_val), 0); } From 3390da6bebd87f7183cc81833508ee06cedc7f4b Mon Sep 17 00:00:00 2001 From: Giles Payne Date: Tue, 2 Aug 2022 21:00:22 +0900 Subject: [PATCH 010/313] Fix for frame stride wider than frame width for 1080p issue --- modules/videoio/src/cap_android_camera.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/videoio/src/cap_android_camera.cpp b/modules/videoio/src/cap_android_camera.cpp index 18fc604367..3da1b61488 100644 --- a/modules/videoio/src/cap_android_camera.cpp +++ b/modules/videoio/src/cap_android_camera.cpp @@ -181,6 +181,7 @@ class AndroidCameraCapture : public IVideoCapture std::shared_ptr captureSession; CaptureSessionState sessionState = CaptureSessionState::INITIALIZING; int32_t frameWidth = 0; + int32_t frameStride = 0; int32_t frameHeight = 0; int32_t colorFormat; std::vector buffer; @@ -307,8 +308,11 @@ public: AImage_getPlaneData(image.get(), 1, &uPixel, &uLen); AImage_getPlaneData(image.get(), 2, &vPixel, &vLen); AImage_getPlanePixelStride(image.get(), 1, &uvPixelStride); + int32_t yBufferLen = yLen; - if ( (uvPixelStride == 2) && (uPixel == vPixel + 1) && (yLen == frameWidth * frameHeight) && (uLen == ((yLen / 2) - 1)) && (vLen == uLen) ) { + if ( (uvPixelStride == 2) && (uPixel == vPixel + 1) && (yLen == (yStride * (frameHeight - 1)) + frameWidth) && (uLen == (uvStride * ((frameHeight / 2) - 1)) + frameWidth - 1) && (uvStride == yStride) && (vLen == uLen) ) { + frameStride = yStride; + yBufferLen = frameStride * frameHeight; colorFormat = COLOR_FormatYUV420SemiPlanar; if (fourCC == FOURCC_UNKNOWN) { fourCC = FOURCC_NV21; @@ -326,8 +330,8 @@ public: } buffer.clear(); - buffer.insert(buffer.end(), yPixel, yPixel + yLen); - buffer.insert(buffer.end(), vPixel, vPixel + yLen / 2); + buffer.insert(buffer.end(), yPixel, yPixel + yBufferLen); + buffer.insert(buffer.end(), vPixel, vPixel + yBufferLen / 2); return true; } @@ -336,8 +340,8 @@ public: if (buffer.empty()) { return false; } - Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data()); if (colorFormat == COLOR_FormatYUV420Planar) { + Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data()); switch (fourCC) { case FOURCC_BGR: cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_YV12); @@ -356,18 +360,20 @@ public: break; } } else if (colorFormat == COLOR_FormatYUV420SemiPlanar) { + Mat yuv(frameHeight + frameHeight/2, frameStride, CV_8UC1, buffer.data()); + Mat tmp = (frameWidth == frameStride) ? yuv : yuv(Rect(0, 0, frameWidth, frameHeight + frameHeight / 2)); switch (fourCC) { case FOURCC_BGR: - cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_NV21); + cv::cvtColor(tmp, out, cv::COLOR_YUV2BGR_NV21); break; case FOURCC_RGB: - cv::cvtColor(yuv, out, cv::COLOR_YUV2RGB_NV21); + cv::cvtColor(tmp, out, cv::COLOR_YUV2RGB_NV21); break; case FOURCC_GRAY: - cv::cvtColor(yuv, out, cv::COLOR_YUV2GRAY_NV21); + cv::cvtColor(tmp, out, cv::COLOR_YUV2GRAY_NV21); break; case FOURCC_NV21: - yuv.copyTo(out); + tmp.copyTo(out); break; default: LOGE("Unexpected FOURCC value: %d", fourCC); From 4b05765174fa2a0750aa8acc252329d6df73c2f1 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Tue, 2 Aug 2022 17:18:42 +0300 Subject: [PATCH 011/313] Initial fix to try and return a valid fourcc when _opencv_avcodec_get_name fails. --- modules/videoio/src/cap_ffmpeg_impl.hpp | 20 +++++++++++++++++--- modules/videoio/test/test_ffmpeg.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index d02b045a19..caeae31e1f 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1699,7 +1699,7 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const return (double)av_get_picture_type_char(picture->pict_type); case CAP_PROP_FPS: return get_fps(); - case CAP_PROP_FOURCC: + case CAP_PROP_FOURCC: { codec_id = video_st->CV_FFMPEG_CODEC_FIELD->codec_id; codec_tag = (double) video_st->CV_FFMPEG_CODEC_FIELD->codec_tag; @@ -1709,12 +1709,26 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const } codec_fourcc = _opencv_avcodec_get_name(codec_id); - if(!codec_fourcc || strlen(codec_fourcc) < 4 || strcmp(codec_fourcc, "unknown_codec") == 0) + if (!codec_fourcc || strcmp(codec_fourcc, "unknown_codec") == 0 || strlen(codec_fourcc) != 4) { - return codec_tag; + const struct AVCodecTag* fallback_tags[] = { + // APIchanges: + // 2012-01-31 - dd6d3b0 - lavf 54.01.0 + // Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags(). + avformat_get_riff_video_tags(), +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100) && defined LIBAVFORMAT_VERSION_MICRO && LIBAVFORMAT_VERSION_MICRO >= 100 + // APIchanges: ffmpeg only + // 2014-01-19 - 1a193c4 - lavf 55.25.100 - avformat.h + // Add avformat_get_mov_video_tags() and avformat_get_mov_audio_tags(). + avformat_get_mov_video_tags(), +#endif + codec_bmp_tags, // fallback for avformat < 54.1 + NULL }; + return av_codec_get_tag(fallback_tags, codec_id); } return (double) CV_FOURCC(codec_fourcc[0], codec_fourcc[1], codec_fourcc[2], codec_fourcc[3]); + } case CAP_PROP_SAR_NUM: return _opencv_ffmpeg_get_sample_aspect_ratio(ic->streams[video_stream]).num; case CAP_PROP_SAR_DEN: diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 115705760c..f59631cfc4 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -399,7 +399,32 @@ const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = { INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties)); +typedef tuple ffmpeg_get_fourcc_param_t; +typedef testing::TestWithParam ffmpeg_get_fourcc; +TEST_P(ffmpeg_get_fourcc, check_short_codecs) +{ + const VideoCaptureAPIs api = CAP_FFMPEG; + if (!videoio_registry::hasBackend(api)) + throw SkipTestException("Backend was not found"); + const string fileName = get<0>(GetParam()); + const string fourcc = get<1>(GetParam()); + VideoCapture cap(findDataFile(fileName), api); + if (!cap.isOpened()) + throw SkipTestException("Video stream is not supported"); + ASSERT_EQ(fourccToString(cap.get(CAP_PROP_FOURCC)), fourcc); +} + +const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] = +{ + ffmpeg_get_fourcc_param_t("../cv/tracking/faceocc2/data/faceocc2.webm", "VP80"), + ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", "vp09"), + ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libaom-av1.mp4", "av01"), + ffmpeg_get_fourcc_param_t("video/big_buck_bunny.h265", "hevc"), + ffmpeg_get_fourcc_param_t("video/big_buck_bunny.h264", "h264") +}; + +INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_get_fourcc, testing::ValuesIn(ffmpeg_get_fourcc_param)); // related issue: https://github.com/opencv/opencv/issues/15499 TEST(videoio, mp4_orientation_meta_auto) From 7ce83f2a9548549d2a6718de598f7f338b0cb90e Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:41:06 +0300 Subject: [PATCH 012/313] Skip new test until pr built into windows ffmpeg dll. --- modules/videoio/test/test_ffmpeg.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index f59631cfc4..5001bcf796 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -408,11 +408,16 @@ TEST_P(ffmpeg_get_fourcc, check_short_codecs) if (!videoio_registry::hasBackend(api)) throw SkipTestException("Backend was not found"); const string fileName = get<0>(GetParam()); - const string fourcc = get<1>(GetParam()); + const string fourcc_string = get<1>(GetParam()); VideoCapture cap(findDataFile(fileName), api); if (!cap.isOpened()) throw SkipTestException("Video stream is not supported"); - ASSERT_EQ(fourccToString(cap.get(CAP_PROP_FOURCC)), fourcc); + const double fourcc = cap.get(CAP_PROP_FOURCC); +#ifdef _WIN32 // handle old FFmpeg backend + if(!fourcc && fileName == "../cv/tracking/faceocc2/data/faceocc2.webm") + throw SkipTestException("Feature not yet supported by Windows FFmpeg shared library!"); +#endif + ASSERT_EQ(fourccToString(fourcc), fourcc_string); } const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] = From f0d29cd33c5d2b8f6c9c5c3177cbb3a359ee6b33 Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Mon, 8 Aug 2022 02:09:54 +0000 Subject: [PATCH 013/313] Add more universal intrinsic implementations for RVV. --- .../core/include/opencv2/core/hal/intrin.hpp | 16 +- .../opencv2/core/hal/intrin_rvv_scalable.hpp | 695 ++++++++++++++++++ modules/core/test/test_intrin_utils.hpp | 248 ++++++- 3 files changed, 950 insertions(+), 9 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index a04de3a12d..c12140bbf8 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -537,7 +537,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_setall_f32(float v) { return VXPREFIX(_setall_f32)(v); } inline v_int64 vx_setall_s64(int64 v) { return VXPREFIX(_setall_s64)(v); } inline v_uint64 vx_setall_u64(uint64 v) { return VXPREFIX(_setall_u64)(v); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_setall_f64(double v) { return VXPREFIX(_setall_f64)(v); } #endif //! @} @@ -554,7 +554,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_setzero_f32() { return VXPREFIX(_setzero_f32)(); } inline v_int64 vx_setzero_s64() { return VXPREFIX(_setzero_s64)(); } inline v_uint64 vx_setzero_u64() { return VXPREFIX(_setzero_u64)(); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_setzero_f64() { return VXPREFIX(_setzero_f64)(); } #endif //! @} @@ -571,7 +571,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_load(const float * ptr) { return VXPREFIX(_load)(ptr); } inline v_int64 vx_load(const int64 * ptr) { return VXPREFIX(_load)(ptr); } inline v_uint64 vx_load(const uint64 * ptr) { return VXPREFIX(_load)(ptr); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_load(const double * ptr) { return VXPREFIX(_load)(ptr); } #endif //! @} @@ -588,7 +588,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_load_aligned(const float * ptr) { return VXPREFIX(_load_aligned)(ptr); } inline v_int64 vx_load_aligned(const int64 * ptr) { return VXPREFIX(_load_aligned)(ptr); } inline v_uint64 vx_load_aligned(const uint64 * ptr) { return VXPREFIX(_load_aligned)(ptr); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_load_aligned(const double * ptr) { return VXPREFIX(_load_aligned)(ptr); } #endif //! @} @@ -605,7 +605,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_load_low(const float * ptr) { return VXPREFIX(_load_low)(ptr); } inline v_int64 vx_load_low(const int64 * ptr) { return VXPREFIX(_load_low)(ptr); } inline v_uint64 vx_load_low(const uint64 * ptr) { return VXPREFIX(_load_low)(ptr); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_load_low(const double * ptr) { return VXPREFIX(_load_low)(ptr); } #endif //! @} @@ -622,7 +622,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_load_halves(const float * ptr0, const float * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } inline v_int64 vx_load_halves(const int64 * ptr0, const int64 * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } inline v_uint64 vx_load_halves(const uint64 * ptr0, const uint64 * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_load_halves(const double * ptr0, const double * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } #endif //! @} @@ -639,7 +639,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_lut(const float* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } inline v_int64 vx_lut(const int64 * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } inline v_uint64 vx_lut(const uint64 * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_lut(const double* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } #endif //! @} @@ -656,7 +656,7 @@ namespace CV__SIMD_NAMESPACE { inline v_float32 vx_lut_pairs(const float* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } inline v_int64 vx_lut_pairs(const int64 * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } inline v_uint64 vx_lut_pairs(const uint64 * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } -#if CV_SIMD_64F +#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F inline v_float64 vx_lut_pairs(const double* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } #endif //! @} diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 30c7524699..728112bc99 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -284,6 +284,64 @@ inline v_float64 v_reinterpret_as_f64(const v_float32& v) \ } #endif +//////////// Extract ////////////// + +#define OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(_Tpvec, _Tp, suffix, vl) \ +template \ +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b, int i = s) \ +{ \ + return vslideup(vslidedown(v_setzero_##suffix(), a, i, vl), b, VTraits<_Tpvec>::vlanes() - i, vl); \ +} \ +template inline _Tp v_extract_n(_Tpvec v, int i = s) \ +{ \ + return vmv_x(vslidedown(v_setzero_##suffix(), v, i, vl)); \ +} + + +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_uint8, uchar, u8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_int8, schar, s8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_uint16, ushort, u16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_int16, short, s16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_uint32, unsigned int, u32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_int32, int, s32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_uint64, uint64, u64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT_INTEGER(v_int64, int64, s64, VTraits::vlanes()) + +#define OPENCV_HAL_IMPL_RVV_EXTRACT_FP(_Tpvec, _Tp, suffix, vl) \ +template \ +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b, int i = s) \ +{ \ + return vslideup(vslidedown(v_setzero_##suffix(), a, i, vl), b, VTraits<_Tpvec>::vlanes() - i, vl); \ +} \ +template inline _Tp v_extract_n(_Tpvec v, int i = s) \ +{ \ + return vfmv_f(vslidedown(v_setzero_##suffix(), v, i, vl)); \ +} + +OPENCV_HAL_IMPL_RVV_EXTRACT_FP(v_float32, float, f32, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_EXTRACT_FP(v_float64, double, f64, VTraits::vlanes()) +#endif + +#define OPENCV_HAL_IMPL_RVV_EXTRACT(_Tpvec, _Tp, vl) \ +inline _Tp v_extract_highest(_Tpvec v) \ +{ \ + return v_extract_n(v, vl-1); \ +} + +OPENCV_HAL_IMPL_RVV_EXTRACT(v_uint8, uchar, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_int8, schar, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_uint16, ushort, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_int16, short, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_uint32, unsigned int, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_int32, int, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_uint64, uint64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_int64, int64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_EXTRACT(v_float32, float, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_EXTRACT(v_float64, double, VTraits::vlanes()) +#endif + ////////////// Load/Store ////////////// #define OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(_Tpvec, _nTpvec, _Tp, hvl, vl, width, suffix, vmv) \ @@ -387,6 +445,9 @@ OPENCV_HAL_IMPL_RVV_LUT(v_int16, short, m2) OPENCV_HAL_IMPL_RVV_LUT(v_int32, int, m1) OPENCV_HAL_IMPL_RVV_LUT(v_int64, int64_t, mf2) OPENCV_HAL_IMPL_RVV_LUT(v_float32, float, m1) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_LUT(v_float64, double, mf2) +#endif inline v_uint8 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } inline v_uint8 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } @@ -401,6 +462,219 @@ inline v_uint64 v_lut(const uint64* tab, const int* idx) { return v_reinterpret_ inline v_uint64 v_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } inline v_uint64 v_lut_quads(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_quads((const int64_t*)tab, idx)); } +////////////// Pack boolean //////////////////// +/* TODO */ + +////////////// Arithmetics ////////////// +#define OPENCV_HAL_IMPL_RVV_BIN_OP(_Tpvec, ocv_intrin, rvv_intrin) \ +inline _Tpvec v_##ocv_intrin(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return rvv_intrin(a, b, VTraits<_Tpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, add, vsaddu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, sub, vssubu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, div, vdivu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, add, vsadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, sub, vssub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, div, vdiv) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, add, vsaddu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, sub, vssubu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, div, vdivu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, add, vsadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, sub, vssub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, div, vdiv) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, add, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, sub, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, mul, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, div, vdivu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, add, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, sub, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, mul, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, div, vdiv) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, add, vfadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, sub, vfsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, mul, vfmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, div, vfdiv) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, add, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, sub, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, mul, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, div, vdivu) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, add, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, sub, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, mul, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, div, vdiv) + +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, add, vfadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, sub, vfsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, mul, vfmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, div, vfdiv) +#endif + +#define OPENCV_HAL_IMPL_RVV_BIN_MADD(_Tpvec, rvv_add) \ +template \ +inline _Tpvec v_add(_Tpvec f1, _Tpvec f2, Args... vf) { \ + return v_add(rvv_add(f1, f2, VTraits<_Tpvec>::vlanes()), vf...); \ +} +#define OPENCV_HAL_IMPL_RVV_BIN_MMUL(_Tpvec, rvv_mul) \ +template \ +inline _Tpvec v_mul(_Tpvec f1, _Tpvec f2, Args... vf) { \ + return v_mul(rvv_mul(f1, f2, VTraits<_Tpvec>::vlanes()), vf...); \ +} +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_uint8, vsaddu) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_int8, vsadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_uint16, vsaddu) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_int16, vsadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_uint32, vadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_int32, vadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_float32, vfadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_uint64, vadd) +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_int64, vadd) + +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_uint32, vmul) +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_int32, vmul) +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_float32, vfmul) +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_uint64, vmul) +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_int64, vmul) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_BIN_MADD(v_float64, vfadd) +OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_float64, vfmul) +#endif + +#define OPENCV_HAL_IMPL_RVV_MUL_EXPAND(_Tpvec, _Tpwvec, _TpwvecM2, suffix, wmul) \ +inline void v_mul_expand(const _Tpvec& a, const _Tpvec& b, _Tpwvec& c, _Tpwvec& d) \ +{ \ + _TpwvecM2 temp = wmul(a, b, VTraits<_Tpvec>::vlanes()); \ + c = vget_##suffix##m1(temp, 0); \ + d = vget_##suffix##m1(temp, 1); \ +} + +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_uint8, v_uint16, vuint16m2_t, u16, vwmulu) +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int8, v_int16, vint16m2_t, i16, vwmul) +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_uint16, v_uint32, vuint32m2_t, u32, vwmulu) +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int16, v_int32, vint32m2_t, i32, vwmul) +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_uint32, v_uint64, vuint64m2_t, u64, vwmulu) +OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int32, v_int64, vint64m2_t, i64, vwmul) + + +inline v_int16 v_mul_hi(const v_int16& a, const v_int16& b) +{ + return vmulh(a, b, VTraits::vlanes()); +} +inline v_uint16 v_mul_hi(const v_uint16& a, const v_uint16& b) +{ + return vmulhu(a, b, VTraits::vlanes()); +} + +////////////// Arithmetics (wrap)////////////// +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, add_wrap, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, add_wrap, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, add_wrap, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, add_wrap, vadd) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, sub_wrap, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, sub_wrap, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, sub_wrap, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, sub_wrap, vsub) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, mul_wrap, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, mul_wrap, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, mul_wrap, vmul) +OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, mul_wrap, vmul) + +//////// Saturating Multiply //////// +// TODO + +////////////// Bitwise logic ////////////// + +#define OPENCV_HAL_IMPL_RVV_LOGIC_OP(_Tpvec, vl) \ +inline _Tpvec v_and(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vand(a, b, vl); \ +} \ +inline _Tpvec v_or(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vor(a, b, vl); \ +} \ +inline _Tpvec v_xor(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vxor(a, b, vl); \ +} \ +inline _Tpvec v_not (const _Tpvec& a) \ +{ \ + return vnot(a, vl); \ +} + +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int64, VTraits::vlanes()) + +#define OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(op, vl) \ +inline v_float32 v_##op (const v_float32& a, const v_float32& b) \ +{ \ + return vreinterpret_v_i32m1_f32m1(v##op(vreinterpret_v_f32m1_i32m1(a), vreinterpret_v_f32m1_i32m1(b), vl)); \ +} +OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(and, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(or, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(xor, VTraits::vlanes()) + +inline v_float32 v_not(const v_float32& a) +{ + return vreinterpret_v_i32m1_f32m1(vnot(vreinterpret_v_f32m1_i32m1(a), VTraits::vlanes())); +} + +#if CV_SIMD_SCALABLE_64F +#define OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(op, vl) \ +inline v_float64 v_##op (const v_float64& a, const v_float64& b) \ +{ \ + return vreinterpret_v_i64m1_f64m1(v##op(vreinterpret_v_f64m1_i64m1(a), vreinterpret_v_f64m1_i64m1(b), vl)); \ +} +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(and, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(or, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(xor, VTraits::vlanes()) +inline v_float64 v_not (const v_float64& a) +{ + return vreinterpret_v_i64m1_f64m1(vnot(vreinterpret_v_f64m1_i64m1(a), VTraits::vlanes())); +} +#endif + +////////////// Bitwise shifts ////////////// + +#define OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(_Tpvec, vl) \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ \ + return _Tpvec(vsll(a, uint8_t(n), vl)); \ +} \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ \ + return _Tpvec(vsrl(a, uint8_t(n), vl)); \ +} + +#define OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(_Tpvec, vl) \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ \ + return _Tpvec(vsll(a, uint8_t(n), vl)); \ +} \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ \ + return _Tpvec(vsra(a, uint8_t(n), vl)); \ +} + +OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int64, VTraits::vlanes()) + +////////////// Comparison ////////////// +// TODO ////////////// Min/Max ////////////// @@ -433,6 +707,363 @@ OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_min, vfmin, VTraits::vlanes OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_max, vfmax, VTraits::vlanes()) #endif +////////////// Reduce ////////////// + +#define OPENCV_HAL_IMPL_RVV_REDUCE_SUM(_Tpvec, _wTpvec, _nwTpvec, scalartype, wsuffix, vl, red) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + _nwTpvec zero = vmv_v_x_##wsuffix##m1(0, vl); \ + _nwTpvec res = vmv_v_x_##wsuffix##m1(0, vl); \ + res = v##red(res, a, zero, vl); \ + return (scalartype)v_get0(res); \ +} +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint8, v_uint16, vuint16m1_t, unsigned, u16, VTraits::vlanes(), wredsumu) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int8, v_int16, vint16m1_t, int, i16, VTraits::vlanes(), wredsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint16, v_uint32, vuint32m1_t, unsigned, u32, VTraits::vlanes(), wredsumu) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int16, v_int32, vint32m1_t, int, i32, VTraits::vlanes(), wredsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint32, v_uint64, vuint64m1_t, unsigned, u64, VTraits::vlanes(), wredsumu) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int32, v_int64, vint64m1_t, int, i64, VTraits::vlanes(), wredsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint64, v_uint64, vuint64m1_t, uint64, u64, VTraits::vlanes(), redsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int64, v_int64, vint64m1_t, int64, i64, VTraits::vlanes(), redsum) + +#define OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(_Tpvec, _wTpvec, _nwTpvec, scalartype, wsuffix, vl) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + _nwTpvec zero = vfmv_v_f_##wsuffix##m1(0, vl); \ + _nwTpvec res = vfmv_v_f_##wsuffix##m1(0, vl); \ + res = vfredosum(res, a, zero, vl); \ + return (scalartype)v_get0(res); \ +} +OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float32, v_float32, vfloat32m1_t, float, f32, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float64, v_float64, vfloat64m1_t, double, f64, VTraits::vlanes()) +#endif + +#define OPENCV_HAL_IMPL_RVV_REDUCE(_Tpvec, func, scalartype, suffix, vl, red) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpvec res = _Tpvec(v##red(a, a, a, vl)); \ + return (scalartype)v_get0(res); \ +} + +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint8, min, uchar, u8, VTraits::vlanes(), redminu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int8, min, schar, i8, VTraits::vlanes(), redmin) +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint16, min, ushort, u16, VTraits::vlanes(), redminu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int16, min, short, i16, VTraits::vlanes(), redmin) +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint32, min, unsigned, u32, VTraits::vlanes(), redminu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int32, min, int, i32, VTraits::vlanes(), redmin) +OPENCV_HAL_IMPL_RVV_REDUCE(v_float32, min, float, f32, VTraits::vlanes(), fredmin) +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint8, max, uchar, u8, VTraits::vlanes(), redmaxu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int8, max, schar, i8, VTraits::vlanes(), redmax) +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint16, max, ushort, u16, VTraits::vlanes(), redmaxu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int16, max, short, i16, VTraits::vlanes(), redmax) +OPENCV_HAL_IMPL_RVV_REDUCE(v_uint32, max, unsigned, u32, VTraits::vlanes(), redmaxu) +OPENCV_HAL_IMPL_RVV_REDUCE(v_int32, max, int, i32, VTraits::vlanes(), redmax) +OPENCV_HAL_IMPL_RVV_REDUCE(v_float32, max, float, f32, VTraits::vlanes(), fredmax) + +//TODO: v_reduce_sum4 + +////////////// Square-Root ////////////// + +inline v_float32 v_sqrt(const v_float32& x) +{ + return vfsqrt(x, VTraits::vlanes()); +} + +inline v_float32 v_invsqrt(const v_float32& x) +{ + v_float32 one = v_setall_f32(1.0f); + return v_div(one, v_sqrt(x)); +} + +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_sqrt(const v_float64& x) +{ + return vfsqrt(x, VTraits::vlanes()); +} + +inline v_float64 v_invsqrt(const v_float64& x) +{ + v_float64 one = v_setall_f64(1.0f); + return v_div(one, v_sqrt(x)); +} +#endif + +inline v_float32 v_magnitude(const v_float32& a, const v_float32& b) +{ + v_float32 x = vfmacc(vfmul(a, a, VTraits::vlanes()), b, b, VTraits::vlanes()); + return v_sqrt(x); +} + +inline v_float32 v_sqr_magnitude(const v_float32& a, const v_float32& b) +{ + return v_float32(vfmacc(vfmul(a, a, VTraits::vlanes()), b, b, VTraits::vlanes())); +} + +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_magnitude(const v_float64& a, const v_float64& b) +{ + v_float64 x = vfmacc(vfmul(a, a, VTraits::vlanes()), b, b, VTraits::vlanes()); + return v_sqrt(x); +} + +inline v_float64 v_sqr_magnitude(const v_float64& a, const v_float64& b) +{ + return vfmacc(vfmul(a, a, VTraits::vlanes()), b, b, VTraits::vlanes()); +} +#endif + +////////////// Multiply-Add ////////////// + +inline v_float32 v_fma(const v_float32& a, const v_float32& b, const v_float32& c) +{ + return vfmacc(c, a, b, VTraits::vlanes()); +} +inline v_int32 v_fma(const v_int32& a, const v_int32& b, const v_int32& c) +{ + return vmacc(c, a, b, VTraits::vlanes()); +} + +inline v_float32 v_muladd(const v_float32& a, const v_float32& b, const v_float32& c) +{ + return v_fma(a, b, c); +} + +inline v_int32 v_muladd(const v_int32& a, const v_int32& b, const v_int32& c) +{ + return v_fma(a, b, c); +} + +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_fma(const v_float64& a, const v_float64& b, const v_float64& c) +{ + return vfmacc_vv_f64m1(c, a, b, VTraits::vlanes()); +} + +inline v_float64 v_muladd(const v_float64& a, const v_float64& b, const v_float64& c) +{ + return v_fma(a, b, c); +} +#endif + +////////////// Check all/any ////////////// + +#define OPENCV_HAL_IMPL_RVV_CHECK_ALLANY(_Tpvec, vl) \ +inline bool v_check_all(const _Tpvec& a) \ +{ \ + return vcpop(vmslt(a, 0, vl), vl) == vl; \ +} \ +inline bool v_check_any(const _Tpvec& a) \ +{ \ + return vcpop(vmslt(a, 0, vl), vl) != 0; \ +} + +OPENCV_HAL_IMPL_RVV_CHECK_ALLANY(v_int8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_CHECK_ALLANY(v_int16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_CHECK_ALLANY(v_int32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_CHECK_ALLANY(v_int64, VTraits::vlanes()) + + +inline bool v_check_all(const v_uint8& a) +{ return v_check_all(v_reinterpret_as_s8(a)); } +inline bool v_check_any(const v_uint8& a) +{ return v_check_any(v_reinterpret_as_s8(a)); } + +inline bool v_check_all(const v_uint16& a) +{ return v_check_all(v_reinterpret_as_s16(a)); } +inline bool v_check_any(const v_uint16& a) +{ return v_check_any(v_reinterpret_as_s16(a)); } + +inline bool v_check_all(const v_uint32& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_uint32& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } + +inline bool v_check_all(const v_float32& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_float32& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } + +inline bool v_check_all(const v_uint64& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_uint64& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } + +#if CV_SIMD_SCALABLE_64F +inline bool v_check_all(const v_float64& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_float64& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } +#endif + +////////////// abs ////////////// + +#define OPENCV_HAL_IMPL_RVV_ABSDIFF(_Tpvec, abs) \ +inline _Tpvec v_##abs(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return v_sub(v_max(a, b), v_min(a, b)); \ +} + +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_uint8, absdiff) +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_uint16, absdiff) +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_uint32, absdiff) +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_float32, absdiff) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_float64, absdiff) +#endif +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_int8, absdiffs) +OPENCV_HAL_IMPL_RVV_ABSDIFF(v_int16, absdiffs) + +#define OPENCV_HAL_IMPL_RVV_ABSDIFF_S(_Tpvec, _rTpvec, width) \ +inline _rTpvec v_absdiff(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vnclipu(vreinterpret_u##width##m2(vwsub_vv(v_max(a, b), v_min(a, b), VTraits<_Tpvec>::vlanes())), 0, VTraits<_Tpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_ABSDIFF_S(v_int8, v_uint8, 16) +OPENCV_HAL_IMPL_RVV_ABSDIFF_S(v_int16, v_uint16, 32) +OPENCV_HAL_IMPL_RVV_ABSDIFF_S(v_int32, v_uint32, 64) + +#define OPENCV_HAL_IMPL_RVV_ABS(_Tprvec, _Tpvec, suffix) \ +inline _Tprvec v_abs(const _Tpvec& a) \ +{ \ + return v_absdiff(a, v_setzero_##suffix()); \ +} + +OPENCV_HAL_IMPL_RVV_ABS(v_uint8, v_int8, s8) +OPENCV_HAL_IMPL_RVV_ABS(v_uint16, v_int16, s16) +OPENCV_HAL_IMPL_RVV_ABS(v_uint32, v_int32, s32) +OPENCV_HAL_IMPL_RVV_ABS(v_float32, v_float32, f32) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_ABS(v_float64, v_float64, f64) +#endif + + +#define OPENCV_HAL_IMPL_RVV_REDUCE_SAD(_Tpvec, scalartype) \ +inline scalartype v_reduce_sad(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return v_reduce_sum(v_absdiff(a, b)); \ +} + +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_uint8, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_int8, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_uint16, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_int16, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_uint32, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_int32, unsigned) +OPENCV_HAL_IMPL_RVV_REDUCE_SAD(v_float32, float) + +////////////// Select ////////////// + +#define OPENCV_HAL_IMPL_RVV_SELECT(_Tpvec, vl) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vmerge(vmsne(mask, 0, vl), b, a, vl); \ +} + +OPENCV_HAL_IMPL_RVV_SELECT(v_uint8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SELECT(v_uint16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SELECT(v_uint32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SELECT(v_int8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SELECT(v_int16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SELECT(v_int32, VTraits::vlanes()) + +inline v_float32 v_select(const v_float32& mask, const v_float32& a, const v_float32& b) \ +{ \ + return vmerge(vmfne(mask, 0, VTraits::vlanes()), b, a, VTraits::vlanes()); \ +} + +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_select(const v_float64& mask, const v_float64& a, const v_float64& b) \ +{ \ + return vmerge(vmfne(mask, 0, VTraits::vlanes()), b, a, VTraits::vlanes()); \ +} +#endif + +////////////// Rotate shift ////////////// + +#define OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(_Tpvec, suffix, vl) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + return vslidedown(vmv_v_x_##suffix##m1(0, vl), a, n, vl); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + return vslideup(vmv_v_x_##suffix##m1(0, vl), a, n, vl); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ return a; } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup(vslidedown(vmv_v_x_##suffix##m1(0, vl), a, n, vl), b, VTraits<_Tpvec>::vlanes() - n, vl); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup(vslidedown(vmv_v_x_##suffix##m1(0, vl), b, VTraits<_Tpvec>::vlanes() - n, vl), a, n, vl); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ CV_UNUSED(b); return a; } + +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_uint8, u8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_int8, i8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_uint16, u16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_int16, i16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_uint32, u32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_int32, i32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_uint64, u64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_ROTATE_INTEGER(v_int64, i64, VTraits::vlanes()) + +#define OPENCV_HAL_IMPL_RVV_ROTATE_FP(_Tpvec, suffix, vl) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + return vslidedown(vfmv_v_f_##suffix##m1(0, vl), a, n, vl); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + return vslideup(vfmv_v_f_##suffix##m1(0, vl), a, n, vl); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ return a; } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup(vslidedown(vfmv_v_f_##suffix##m1(0, vl), a, n, vl), b, VTraits<_Tpvec>::vlanes() - n, vl); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup(vslidedown(vfmv_v_f_##suffix##m1(0, vl), b, VTraits<_Tpvec>::vlanes() - n, vl), a, n, vl); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ CV_UNUSED(b); return a; } + +OPENCV_HAL_IMPL_RVV_ROTATE_FP(v_float32, f32, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_ROTATE_FP(v_float64, f64, VTraits::vlanes()) +#endif + +////////////// Convert to float ////////////// +// TODO + +//////////// Broadcast ////////////// + +#define OPENCV_HAL_IMPL_RVV_BROADCAST(_Tpvec, suffix) \ +template inline _Tpvec v_broadcast_element(_Tpvec v, int i = s) \ +{ \ + return v_setall_##suffix(v_extract_n(v, i)); \ +} \ +inline _Tpvec v_broadcast_highest(_Tpvec v) \ +{ \ + return v_setall_##suffix(v_extract_n(v, VTraits<_Tpvec>::vlanes()-1)); \ +} + +OPENCV_HAL_IMPL_RVV_BROADCAST(v_uint32, u32) +OPENCV_HAL_IMPL_RVV_BROADCAST(v_int32, s32) +OPENCV_HAL_IMPL_RVV_BROADCAST(v_float32, f32) + +////////////// Transpose4x4 ////////////// +// TODO + +////////////// Reverse ////////////// +// TODO //////////// Value reordering //////////// @@ -475,6 +1106,61 @@ inline v_int32 v_load_expand_q(const schar* ptr) return vwcvt_x(vwcvt_x(vle8_v_i8mf4(ptr, VTraits::vlanes()), VTraits::vlanes()), VTraits::vlanes()); } +//////////// PopCount ////////// +// TODO + +//////////// SignMask //////////// +#define OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(_Tpvec) \ +inline int v_signmask(const _Tpvec& a) \ +{ \ + uint8_t ans[4] = {0}; \ + vsm(ans, vmslt(a, 0, VTraits<_Tpvec>::vlanes()), VTraits<_Tpvec>::vlanes()); \ + return *(reinterpret_cast(ans)); \ +} \ +inline int v_scan_forward(const _Tpvec& a) \ +{ \ + return (int)vfirst(vmslt(a, 0, VTraits<_Tpvec>::vlanes()), VTraits<_Tpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(v_int8) +OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(v_int16) +OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(v_int32) +OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(v_int64) + +inline int64 v_signmask(const v_uint8& a) +{ return v_signmask(v_reinterpret_as_s8(a)); } +inline int64 v_signmask(const v_uint16& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } +inline int v_signmask(const v_uint32& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_float32& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_uint64& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } +#if CV_SIMD_SCALABLE_64F +inline int v_signmask(const v_float64& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } +#endif + +//////////// Scan forward //////////// +inline int v_scan_forward(const v_uint8& a) +{ return v_scan_forward(v_reinterpret_as_s8(a)); } +inline int v_scan_forward(const v_uint16& a) +{ return v_scan_forward(v_reinterpret_as_s16(a)); } +inline int v_scan_forward(const v_uint32& a) +{ return v_scan_forward(v_reinterpret_as_s32(a)); } +inline int v_scan_forward(const v_float32& a) +{ return v_scan_forward(v_reinterpret_as_s32(a)); } +inline int v_scan_forward(const v_uint64& a) +{ return v_scan_forward(v_reinterpret_as_s64(a)); } +#if CV_SIMD_SCALABLE_64F +inline int v_scan_forward(const v_float64& a) +{ return v_scan_forward(v_reinterpret_as_s64(a)); } +#endif + +//////////// Pack triplets //////////// +// TODO + ////// FP16 support /////// @@ -484,6 +1170,15 @@ inline v_float32 v_load_expand(const float16_t* ptr) return vundefined_f32m1(); } +////////////// Rounding ////////////// +// TODO + +//////// Dot Product //////// +// TODO + +//////// Fast Dot Product //////// +// TODO + inline void v_cleanup() {} CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index 763702bf38..d3ced9df87 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -1737,7 +1737,33 @@ void test_hal_intrin_uint8() // typedef v_uint8 R; TheTest() .test_loadstore() + .test_expand() + .test_expand_q() + .test_addsub() + .test_arithm_wrap() + .test_mul_expand() + .test_logic() .test_min_max() + .test_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() + .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() +#if 0 // not implemented in rvv backend yet. + .test_interleave() + .test_mul() + .test_cmp() + .test_dotprod_expand() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() + .test_pack_u<1>().test_pack_u<2>().test_pack_u<3>().test_pack_u<8>() + .test_pack_b() + .test_unpack() + .test_reverse() + .test_popcount() +#endif ; } @@ -1747,7 +1773,33 @@ void test_hal_intrin_int8() // typedef v_int8 R; TheTest() .test_loadstore() + .test_expand() + .test_expand_q() + .test_addsub() + .test_arithm_wrap() + .test_mul_expand() + .test_logic() .test_min_max() + .test_absdiff() + .test_absdiffs() + .test_abs() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() + .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() +#if 0 + .test_interleave() + .test_mul() + .test_cmp() + .test_dotprod_expand() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() + .test_unpack() + .test_reverse() + .test_popcount() +#endif ; } @@ -1759,7 +1811,34 @@ void test_hal_intrin_uint16() // typedef v_uint16 R; TheTest() .test_loadstore() + .test_expand() + .test_addsub() + .test_arithm_wrap() + .test_mul_expand() + .test_mul_hi() + .test_shift<1>() + .test_shift<8>() + .test_logic() .test_min_max() + .test_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() + .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() +#if 0 + .test_interleave() + .test_mul() + .test_cmp() + .test_dotprod_expand() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() + .test_pack_u<1>().test_pack_u<2>().test_pack_u<7>().test_pack_u<16>() + .test_unpack() + .test_reverse() + .test_popcount() +#endif ; } @@ -1769,7 +1848,36 @@ void test_hal_intrin_int16() // typedef v_int16 R; TheTest() .test_loadstore() + .test_expand() + .test_addsub() + .test_arithm_wrap() + .test_mul_expand() + .test_mul_hi() + .test_shift<1>() + .test_shift<8>() + .test_logic() .test_min_max() + .test_absdiff() + .test_absdiffs() + .test_abs() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() + .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() +#if 0 + .test_interleave() + .test_mul() + .test_cmp() + .test_dotprod() + .test_dotprod_expand() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() + .test_unpack() + .test_reverse() + .test_popcount() +#endif ; } @@ -1781,7 +1889,33 @@ void test_hal_intrin_uint32() // typedef v_uint32 R; TheTest() .test_loadstore() + .test_expand() + .test_addsub() + .test_mul() + .test_mul_expand() + .test_shift<1>() + .test_shift<8>() + .test_logic() .test_min_max() + .test_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() + .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() + .test_extract_highest() + .test_broadcast_highest() +#if 0 + .test_interleave() + .test_cmp() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() + .test_unpack() + .test_reverse() + .test_transpose() + .test_popcount() +#endif ; } @@ -1791,7 +1925,36 @@ void test_hal_intrin_int32() // typedef v_int32 R; TheTest() .test_loadstore() + .test_expand() + .test_addsub() + .test_mul() + .test_abs() + .test_shift<1>().test_shift<8>() + .test_dotprod_expand_f64() + .test_logic() .test_min_max() + .test_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() + .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() + .test_extract_highest() + .test_broadcast_highest() +#if 0 + .test_interleave() + .test_cmp() + .test_dotprod() + .test_reduce() + .test_reduce_sad() + .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() + .test_unpack() + .test_reverse() + .test_float_cvt32() + .test_float_cvt64() + .test_transpose() + .test_popcount() +#endif ; } @@ -1803,7 +1966,20 @@ void test_hal_intrin_uint64() // typedef v_uint64 R; TheTest() .test_loadstore() + .test_addsub() + .test_shift<1>().test_shift<8>() + .test_logic() + .test_extract<0>().test_extract<1>() + .test_rotate<0>().test_rotate<1>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() ; +#if 0 + #if CV_SIMD_64F + .test_cmp64() + #endif + .test_reverse() +#endif } void test_hal_intrin_int64() @@ -1812,7 +1988,21 @@ void test_hal_intrin_int64() // typedef v_int64 R; TheTest() .test_loadstore() + .test_addsub() + .test_shift<1>().test_shift<8>() + .test_logic() + .test_extract<0>().test_extract<1>() + .test_rotate<0>().test_rotate<1>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() + .test_cvt64_double() ; +#if 0 + #if CV_SIMD_64F + .test_cmp64() + #endif + .test_reverse() +#endif } //============= Floating point ===================================================================== @@ -1822,18 +2012,61 @@ void test_hal_intrin_float32() // typedef v_float32 R; TheTest() .test_loadstore() + .test_addsub() + .test_mul() + .test_div() + .test_sqrt_abs() .test_min_max() + .test_float_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() + .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() + .test_extract_highest() + .test_broadcast_highest() +#if 0 + .test_interleave() + .test_interleave_2channel() + .test_cmp() + .test_reduce() + .test_reduce_sad() + .test_unpack() + .test_float_math() + .test_float_cvt64() + .test_matmul() + .test_transpose() + .test_reverse() + .test_reduce_sum4() +#endif ; } void test_hal_intrin_float64() { DUMP_ENTRY(v_float64); -#if CV_SIMD_64F +#if CV_SIMD_SCALABLE_64F // typedef v_float64 R; TheTest() .test_loadstore() + .test_addsub() + .test_mul() + .test_div() + .test_sqrt_abs() .test_min_max() + .test_float_absdiff() + .test_mask() + .test_extract<0>().test_extract<1>() + .test_rotate<0>().test_rotate<1>() + .test_extract_n<0>().test_extract_n<1>() + .test_extract_highest() +#if 0 + .test_cmp() + .test_unpack() + .test_float_cvt32() + .test_float_math() + .test_reverse() +#endif ; #endif @@ -1874,6 +2107,7 @@ void test_hal_intrin_uint8() .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() #if CV_SIMD_WIDTH == 32 .test_pack<9>().test_pack<10>().test_pack<13>().test_pack<15>() @@ -1914,6 +2148,7 @@ void test_hal_intrin_int8() .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() ; } @@ -1951,6 +2186,7 @@ void test_hal_intrin_uint16() .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() ; } @@ -1988,6 +2224,7 @@ void test_hal_intrin_int16() .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() ; } @@ -2022,6 +2259,8 @@ void test_hal_intrin_uint32() .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() .test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_extract_highest() + .test_broadcast_highest() .test_transpose() ; } @@ -2058,6 +2297,8 @@ void test_hal_intrin_int32() .test_float_cvt32() .test_float_cvt64() .test_transpose() + .test_extract_highest() + .test_broadcast_highest() ; } @@ -2079,6 +2320,7 @@ void test_hal_intrin_uint64() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() ; } @@ -2099,6 +2341,7 @@ void test_hal_intrin_int64() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() .test_cvt64_double() ; @@ -2134,6 +2377,8 @@ void test_hal_intrin_float32() .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() .test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_extract_highest() + .test_broadcast_highest() #if CV_SIMD_WIDTH == 32 .test_extract<4>().test_extract<5>().test_extract<6>().test_extract<7>() .test_rotate<4>().test_rotate<5>().test_rotate<6>().test_rotate<7>() @@ -2163,6 +2408,7 @@ void test_hal_intrin_float64() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_highest() //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() #if CV_SIMD_WIDTH == 32 .test_extract<2>().test_extract<3>() From e7e814fa8ce0f3f67c2f432035336ef4015bb979 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Wed, 10 Aug 2022 19:52:44 +0800 Subject: [PATCH 014/313] remove asymmetric padding checks --- modules/dnn/src/layers/convolution_layer.cpp | 8 ----- modules/dnn/src/onnx/onnx_importer.cpp | 38 -------------------- 2 files changed, 46 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index c2960d5aeb..88ee7ee42c 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -101,10 +101,6 @@ public: if (kernel_size.size() == 2) { kernel = Size(kernel_size[1], kernel_size[0]); stride = Size(strides[1], strides[0]); - for (int i = 0; i < pads_begin.size(); i++) { - if (pads_begin[i] != pads_end[i]) - CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer"); - } pad = Size(pads_begin[1], pads_begin[0]); dilation = Size(dilations[1], dilations[0]); @@ -163,10 +159,6 @@ public: } getConvPoolPaddings(inpShape, kernel_size, strides, padMode, pads_begin, pads_end); if (pads_begin.size() == 2) { - for (int i = 0; i < pads_begin.size(); i++) { - if (pads_begin[i] != pads_end[i]) - CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer"); - } pad = Size(pads_begin[1], pads_begin[0]); } fusedWeights = false; diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index e99fbb319e..4f797c1627 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2067,44 +2067,6 @@ void ONNXImporter::parseConv(LayerParams& layerParams, const opencv_onnx::NodePr int outCn = layerParams.blobs.empty() ? outShapes[node_proto.input(1)][0] : layerParams.blobs[0].size[0]; layerParams.set("num_output", outCn); - // Check for asymmetric padding in Conv2D - if (layerParams.has("pad")) - { - bool asymmetricPadding = false; - DictValue pads = layerParams.get("pad"); - const int dims = pads.size() / 2; - for (int i = 0; i < dims; ++i) - { - if (pads.get(i) != pads.get(i + dims)) - { - asymmetricPadding = true; - break; - } - } - if (asymmetricPadding && pads.size() == 4) // [pad_t, pad_l, pad_b, pad_r] - { - layerParams.erase("pad"); - // No paddings required for N, C axis - std::vector paddings(4, 0); - // Add paddings for H, W axis - for (int i = 0; i < dims; ++i) - { - paddings.push_back(pads.get(i)); - paddings.push_back(pads.get(dims + i)); - } - LayerParams padLp; - padLp.name = layerParams.name + "/pad"; - padLp.type = "Padding"; - padLp.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size())); - - opencv_onnx::NodeProto proto; - proto.add_input(node_proto.input(0)); - proto.add_output(padLp.name); - - addLayer(padLp, proto); - node_proto.set_input(0, padLp.name); - } - } addLayer(layerParams, node_proto); } From 2fb652ce09c5f46d108c219d4b13cb86681e7e95 Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 12 Aug 2022 01:44:30 +0000 Subject: [PATCH 015/313] Add testcase for continuous mul and add. --- modules/core/test/test_intrin_utils.hpp | 31 ++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index d3ced9df87..2398d308b9 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -578,16 +578,18 @@ template struct TheTest TheTest & test_addsub() { - Data dataA, dataB; + Data dataA, dataB, dataC; dataB.reverse(); - R a = dataA, b = dataB; + dataA[1] = static_cast(std::numeric_limits::max()); + R a = dataA, b = dataB, c = dataC; - Data resC = v_add(a, b), resD = v_sub(a, b); + Data resD = v_add(a, b), resE = v_add(a, b, c), resF = v_sub(a, b); for (int i = 0; i < VTraits::vlanes(); ++i) { SCOPED_TRACE(cv::format("i=%d", i)); - EXPECT_EQ(saturate_cast(dataA[i] + dataB[i]), resC[i]); - EXPECT_EQ(saturate_cast(dataA[i] - dataB[i]), resD[i]); + EXPECT_EQ(saturate_cast(dataA[i] + dataB[i]), resD[i]); + EXPECT_EQ(saturate_cast(dataA[i] + dataB[i] + dataC[i]), resE[i]); + EXPECT_EQ(saturate_cast(dataA[i] - dataB[i]), resF[i]); } return *this; @@ -614,16 +616,18 @@ template struct TheTest TheTest & test_mul() { - Data dataA, dataB; + Data dataA, dataB, dataC; dataA[1] = static_cast(std::numeric_limits::max()); dataB.reverse(); - R a = dataA, b = dataB; + R a = dataA, b = dataB, c = dataC; - Data resC = v_mul(a, b); + Data resD = v_mul(a, b); + Data resE = v_mul(a, b, c); for (int i = 0; i < VTraits::vlanes(); ++i) { SCOPED_TRACE(cv::format("i=%d", i)); - EXPECT_EQ(saturate_cast(dataA[i] * dataB[i]), resC[i]); + EXPECT_EQ(saturate_cast(dataA[i] * dataB[i]), resD[i]); + EXPECT_EQ(saturate_cast(dataA[i] * dataB[i] * dataC[i]), resE[i]); } return *this; @@ -1741,6 +1745,7 @@ void test_hal_intrin_uint8() .test_expand_q() .test_addsub() .test_arithm_wrap() + .test_mul() .test_mul_expand() .test_logic() .test_min_max() @@ -1752,7 +1757,6 @@ void test_hal_intrin_uint8() .test_extract_highest() #if 0 // not implemented in rvv backend yet. .test_interleave() - .test_mul() .test_cmp() .test_dotprod_expand() .test_reduce() @@ -1777,6 +1781,7 @@ void test_hal_intrin_int8() .test_expand_q() .test_addsub() .test_arithm_wrap() + .test_mul() .test_mul_expand() .test_logic() .test_min_max() @@ -1790,7 +1795,6 @@ void test_hal_intrin_int8() .test_extract_highest() #if 0 .test_interleave() - .test_mul() .test_cmp() .test_dotprod_expand() .test_reduce() @@ -1814,6 +1818,7 @@ void test_hal_intrin_uint16() .test_expand() .test_addsub() .test_arithm_wrap() + .test_mul() .test_mul_expand() .test_mul_hi() .test_shift<1>() @@ -1828,7 +1833,6 @@ void test_hal_intrin_uint16() .test_extract_highest() #if 0 .test_interleave() - .test_mul() .test_cmp() .test_dotprod_expand() .test_reduce() @@ -1851,6 +1855,7 @@ void test_hal_intrin_int16() .test_expand() .test_addsub() .test_arithm_wrap() + .test_mul() .test_mul_expand() .test_mul_hi() .test_shift<1>() @@ -1867,7 +1872,7 @@ void test_hal_intrin_int16() .test_extract_highest() #if 0 .test_interleave() - .test_mul() + .test_cmp() .test_dotprod() .test_dotprod_expand() From 80c82e10aa7e9a5d227fe4a2cad0e409c278d6d3 Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 12 Aug 2022 01:45:44 +0000 Subject: [PATCH 016/313] Update implementations on arithmetics. --- .../opencv2/core/hal/intrin_rvv_scalable.hpp | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 728112bc99..5b3f1677e9 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -474,36 +474,26 @@ inline _Tpvec v_##ocv_intrin(const _Tpvec& a, const _Tpvec& b) \ OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, add, vsaddu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, sub, vssubu) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint8, div, vdivu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, add, vsadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, sub, vssub) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_int8, div, vdiv) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, add, vsaddu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, sub, vssubu) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, div, vdivu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, add, vsadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, sub, vssub) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, div, vdiv) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, add, vadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, sub, vsub) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, mul, vmul) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint32, div, vdivu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, add, vadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, sub, vsub) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, mul, vmul) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_int32, div, vdiv) OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, add, vfadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, sub, vfsub) OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, mul, vfmul) OPENCV_HAL_IMPL_RVV_BIN_OP(v_float32, div, vfdiv) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, add, vadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, sub, vsub) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, mul, vmul) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint64, div, vdivu) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, add, vadd) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, sub, vsub) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, mul, vmul) -OPENCV_HAL_IMPL_RVV_BIN_OP(v_int64, div, vdiv) #if CV_SIMD_SCALABLE_64F OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, add, vfadd) @@ -514,12 +504,12 @@ OPENCV_HAL_IMPL_RVV_BIN_OP(v_float64, div, vfdiv) #define OPENCV_HAL_IMPL_RVV_BIN_MADD(_Tpvec, rvv_add) \ template \ -inline _Tpvec v_add(_Tpvec f1, _Tpvec f2, Args... vf) { \ +inline _Tpvec v_add(const _Tpvec& f1, const _Tpvec& f2, const Args&... vf) { \ return v_add(rvv_add(f1, f2, VTraits<_Tpvec>::vlanes()), vf...); \ } #define OPENCV_HAL_IMPL_RVV_BIN_MMUL(_Tpvec, rvv_mul) \ template \ -inline _Tpvec v_mul(_Tpvec f1, _Tpvec f2, Args... vf) { \ +inline _Tpvec v_mul(const _Tpvec& f1, const _Tpvec& f2, const Args&... vf) { \ return v_mul(rvv_mul(f1, f2, VTraits<_Tpvec>::vlanes()), vf...); \ } OPENCV_HAL_IMPL_RVV_BIN_MADD(v_uint8, vsaddu) @@ -535,8 +525,6 @@ OPENCV_HAL_IMPL_RVV_BIN_MADD(v_int64, vadd) OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_uint32, vmul) OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_int32, vmul) OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_float32, vfmul) -OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_uint64, vmul) -OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_int64, vmul) #if CV_SIMD_SCALABLE_64F OPENCV_HAL_IMPL_RVV_BIN_MADD(v_float64, vfadd) OPENCV_HAL_IMPL_RVV_BIN_MMUL(v_float64, vfmul) @@ -555,8 +543,6 @@ OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int8, v_int16, vint16m2_t, i16, vwmul) OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_uint16, v_uint32, vuint32m2_t, u32, vwmulu) OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int16, v_int32, vint32m2_t, i32, vwmul) OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_uint32, v_uint64, vuint64m2_t, u64, vwmulu) -OPENCV_HAL_IMPL_RVV_MUL_EXPAND(v_int32, v_int64, vint64m2_t, i64, vwmul) - inline v_int16 v_mul_hi(const v_int16& a, const v_int16& b) { @@ -582,7 +568,20 @@ OPENCV_HAL_IMPL_RVV_BIN_OP(v_uint16, mul_wrap, vmul) OPENCV_HAL_IMPL_RVV_BIN_OP(v_int16, mul_wrap, vmul) //////// Saturating Multiply //////// -// TODO +#define OPENCV_HAL_IMPL_RVV_MUL_SAT(_Tpvec, _clip, _wmul) \ +inline _Tpvec v_mul(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _clip(_wmul(a, b, VTraits<_Tpvec>::vlanes()), 0, VTraits<_Tpvec>::vlanes()); \ +} \ +template \ +inline _Tpvec v_mul(const _Tpvec& a1, const _Tpvec& a2, const Args&... va) { \ + return v_mul(_clip(_wmul(a1, a2, VTraits<_Tpvec>::vlanes()), 0, VTraits<_Tpvec>::vlanes()), va...); \ +} + +OPENCV_HAL_IMPL_RVV_MUL_SAT(v_uint8, vnclipu, vwmulu) +OPENCV_HAL_IMPL_RVV_MUL_SAT(v_int8, vnclip, vwmul) +OPENCV_HAL_IMPL_RVV_MUL_SAT(v_uint16, vnclipu, vwmulu) +OPENCV_HAL_IMPL_RVV_MUL_SAT(v_int16, vnclip, vwmul) ////////////// Bitwise logic ////////////// From e65ad44b32136dcec1cb4ae5b249f56b41496949 Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 12 Aug 2022 14:12:52 +0000 Subject: [PATCH 017/313] Remove redundant intrinsics. --- .../opencv2/core/hal/intrin_rvv_scalable.hpp | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 5b3f1677e9..396a2d68a5 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -612,34 +612,7 @@ OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int32, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint64, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int64, VTraits::vlanes()) -#define OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(op, vl) \ -inline v_float32 v_##op (const v_float32& a, const v_float32& b) \ -{ \ - return vreinterpret_v_i32m1_f32m1(v##op(vreinterpret_v_f32m1_i32m1(a), vreinterpret_v_f32m1_i32m1(b), vl)); \ -} -OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(and, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(or, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_FLT32_BIT_OP(xor, VTraits::vlanes()) -inline v_float32 v_not(const v_float32& a) -{ - return vreinterpret_v_i32m1_f32m1(vnot(vreinterpret_v_f32m1_i32m1(a), VTraits::vlanes())); -} - -#if CV_SIMD_SCALABLE_64F -#define OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(op, vl) \ -inline v_float64 v_##op (const v_float64& a, const v_float64& b) \ -{ \ - return vreinterpret_v_i64m1_f64m1(v##op(vreinterpret_v_f64m1_i64m1(a), vreinterpret_v_f64m1_i64m1(b), vl)); \ -} -OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(and, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(or, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(xor, VTraits::vlanes()) -inline v_float64 v_not (const v_float64& a) -{ - return vreinterpret_v_i64m1_f64m1(vnot(vreinterpret_v_f64m1_i64m1(a), VTraits::vlanes())); -} -#endif ////////////// Bitwise shifts ////////////// @@ -663,11 +636,9 @@ template inline _Tpvec v_shr(const _Tpvec& a) \ return _Tpvec(vsra(a, uint8_t(n), vl)); \ } -OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint8, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint16, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint32, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_UNSIGNED_SHIFT_OP(v_uint64, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int8, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int16, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int32, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int64, VTraits::vlanes()) @@ -697,10 +668,6 @@ OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int32, v_min, vmin, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int32, v_max, vmax, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float32, v_min, vfmin, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float32, v_max, vfmax, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint64, v_min, vminu, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_uint64, v_max, vmaxu, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int64, v_min, vmin, VTraits::vlanes()) -OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_int64, v_max, vmax, VTraits::vlanes()) #if CV_SIMD_SCALABLE_64F OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_min, vfmin, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_max, vfmax, VTraits::vlanes()) @@ -722,8 +689,6 @@ OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint16, v_uint32, vuint32m1_t, unsigned, u32, V OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int16, v_int32, vint32m1_t, int, i32, VTraits::vlanes(), wredsum) OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint32, v_uint64, vuint64m1_t, unsigned, u64, VTraits::vlanes(), wredsumu) OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int32, v_int64, vint64m1_t, int, i64, VTraits::vlanes(), wredsum) -OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint64, v_uint64, vuint64m1_t, uint64, u64, VTraits::vlanes(), redsum) -OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int64, v_int64, vint64m1_t, int64, i64, VTraits::vlanes(), redsum) #define OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(_Tpvec, _wTpvec, _nwTpvec, scalartype, wsuffix, vl) \ inline scalartype v_reduce_sum(const _Tpvec& a) \ @@ -734,9 +699,6 @@ inline scalartype v_reduce_sum(const _Tpvec& a) \ return (scalartype)v_get0(res); \ } OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float32, v_float32, vfloat32m1_t, float, f32, VTraits::vlanes()) -#if CV_SIMD_SCALABLE_64F -OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(v_float64, v_float64, vfloat64m1_t, double, f64, VTraits::vlanes()) -#endif #define OPENCV_HAL_IMPL_RVV_REDUCE(_Tpvec, func, scalartype, suffix, vl, red) \ inline scalartype v_reduce_##func(const _Tpvec& a) \ From f572ae3474ae7961636178c51b0d3d7c0ca43d0a Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 12 Aug 2022 14:13:26 +0000 Subject: [PATCH 018/313] add missing test cases(v_abs) --- modules/core/test/test_intrin_utils.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index 2398d308b9..4af3998c3e 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -703,7 +703,7 @@ template struct TheTest for (int i = 0; i < VTraits::vlanes(); ++i) { SCOPED_TRACE(cv::format("i=%d", i)); - R_type ssub = dataA[i] - dataB[i] < std::numeric_limits::min() ? std::numeric_limits::min() : dataA[i] - dataB[i]; + R_type ssub = dataA[i] - dataB[i] < std::numeric_limits::lowest() ? std::numeric_limits::lowest() : dataA[i] - dataB[i]; EXPECT_EQ((u_type)std::abs(ssub), resC[i]); } @@ -2018,6 +2018,7 @@ void test_hal_intrin_float32() TheTest() .test_loadstore() .test_addsub() + .test_abs() .test_mul() .test_div() .test_sqrt_abs() @@ -2057,6 +2058,7 @@ void test_hal_intrin_float64() .test_addsub() .test_mul() .test_div() + .test_abs() .test_sqrt_abs() .test_min_max() .test_float_absdiff() @@ -2364,6 +2366,7 @@ void test_hal_intrin_float32() .test_addsub() .test_mul() .test_div() + .test_abs() .test_cmp() .test_sqrt_abs() .test_min_max() @@ -2401,6 +2404,7 @@ void test_hal_intrin_float64() .test_addsub() .test_mul() .test_div() + .test_abs() .test_cmp() .test_sqrt_abs() .test_min_max() From 99683e958a1255de2928fd7742c8b8a799dfb7c0 Mon Sep 17 00:00:00 2001 From: Kian Eliasi Date: Fri, 12 Aug 2022 19:26:59 +0430 Subject: [PATCH 019/313] Fix the example in py_calib3d/py_calibration --- .../py_calib3d/py_calibration/py_calibration.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown b/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown index bba7b90b9f..182f1c845b 100644 --- a/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown +++ b/doc/py_tutorials/py_calib3d/py_calibration/py_calibration.markdown @@ -127,7 +127,7 @@ for fname in images: objpoints.append(objp) corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria) - imgpoints.append(corners) + imgpoints.append(corners2) # Draw and display the corners cv.drawChessboardCorners(img, (7,6), corners2, ret) From 0cdff4672561ae125cba32a9590d9ec30a92bdb6 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Sun, 14 Aug 2022 17:47:48 +0800 Subject: [PATCH 020/313] tune for opencl --- modules/dnn/src/layers/convolution_layer.cpp | 5 ++++- modules/dnn/src/ocl4dnn/include/ocl4dnn.hpp | 7 ++++--- .../dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp | 13 +++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 88ee7ee42c..6334b40538 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -1807,7 +1807,10 @@ public: config.in_shape = shape(inputs[0]); config.out_shape = shape(outputs[0]); config.kernel = kernel; - config.pad = pad; + // pads_begin: 0 - pad_top, 1 - pad_left + // pads_end: 0 - pad_bottom, 1 - pad_right + std::vector pads = {int(pads_begin[0]), int(pads_end[0]), int(pads_begin[1]), int(pads_end[1])}; + config.pads = pads; config.stride = stride; config.dilation = dilation; if (inputs[0].dims != 4 && inputs[0].dims != umat_blobs[0].dims) diff --git a/modules/dnn/src/ocl4dnn/include/ocl4dnn.hpp b/modules/dnn/src/ocl4dnn/include/ocl4dnn.hpp index bf5fba71a1..b965ba4af1 100644 --- a/modules/dnn/src/ocl4dnn/include/ocl4dnn.hpp +++ b/modules/dnn/src/ocl4dnn/include/ocl4dnn.hpp @@ -55,17 +55,18 @@ struct OCL4DNNConvConfig { OCL4DNNConvConfig() : kernel(1, 1), - pad(0, 0), stride(1, 1), dilation(1, 1), group(1), bias_term(false), use_half(false) - {} + { + pads = {0, 0, 0, 0}; + } MatShape in_shape; MatShape out_shape; Size kernel; - Size pad; + std::vector pads; // [pad_top, pad_bottom, pad_left, pad_right] Size stride; Size dilation; int group; // = 1; diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index e2129be536..90cc2108d6 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -181,8 +181,11 @@ OCL4DNNConvSpatial::OCL4DNNConvSpatial(OCL4DNNConvConfig config) // assumption: spatial dimension is 2. kernel_h_ = config.kernel.height; kernel_w_ = config.kernel.width; - pad_h_ = config.pad.height; - pad_w_ = config.pad.width; + // pads: [pad_top, pad_bottom, pad_left, pad_right] + pad_h_ = config.pads[0]; // pad_top + pad_bottom_ = config.pads[1]; + pad_w_ = config.pads[2]; // pad_left + pad_right_ = config.pads[3]; stride_h_ = config.stride.height; stride_w_ = config.stride.width; dilation_h_ = config.dilation.height; @@ -194,12 +197,6 @@ OCL4DNNConvSpatial::OCL4DNNConvSpatial(OCL4DNNConvConfig config) output_w_ = config.out_shape[dims - spatial_dims + 1]; bottom_dim_ = channels_ * width_ * height_; top_dim_ = num_output_ * output_w_ * output_h_; - int Ph = (output_h_ - 1) * stride_h_ + (dilation_h_ * (kernel_h_ - 1) + 1) - height_; - int Pw = (output_w_ - 1) * stride_w_ + (dilation_w_ * (kernel_w_ - 1) + 1) - width_; - Ph = (Ph > 0) ? Ph : 0; - Pw = (Pw > 0) ? Pw : 0; - pad_right_ = (Pw + 1) / 2; - pad_bottom_ = (Ph + 1) / 2; cache_path_ = utils::getConfigurationParameterString("OPENCV_OCL4DNN_CONFIG_PATH", ""); dwconv_ = (num_output_ == channels_ && channels_ == group_); From 7ffb1037583ed7c4e21632a59b7f1aa653ae00e3 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 15 Aug 2022 10:15:12 +0300 Subject: [PATCH 021/313] QRcode, change INTER_LINEAR to INTER_LINEAR_EXACT fix python test_detect_and_decode_multi fix python test_detect_and_decode_multi, sort QR in multiDetect/multiDecode enable tests with "version_5_up.jpg", "version_5_top.jpg" remove lambda --- .../misc/python/test/test_qrcode_detect.py | 12 +++---- .../objdetect/perf/perf_qrcode_pipeline.cpp | 31 ++++++++++++++----- modules/objdetect/src/qrcode.cpp | 14 ++++----- modules/objdetect/test/test_qrcode.cpp | 2 +- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/modules/objdetect/misc/python/test/test_qrcode_detect.py b/modules/objdetect/misc/python/test/test_qrcode_detect.py index 8a95c8bce5..0237900572 100644 --- a/modules/objdetect/misc/python/test/test_qrcode_detect.py +++ b/modules/objdetect/misc/python/test/test_qrcode_detect.py @@ -43,10 +43,10 @@ class qrcode_detector_test(NewOpenCVTests): retval, decoded_data, points, straight_qrcode = detector.detectAndDecodeMulti(img) self.assertTrue(retval) self.assertEqual(len(decoded_data), 6) - self.assertEqual(decoded_data[0], "TWO STEPS FORWARD") - self.assertEqual(decoded_data[1], "EXTRA") - self.assertEqual(decoded_data[2], "SKIP") - self.assertEqual(decoded_data[3], "STEP FORWARD") - self.assertEqual(decoded_data[4], "STEP BACK") - self.assertEqual(decoded_data[5], "QUESTION") + self.assertTrue("TWO STEPS FORWARD" in decoded_data) + self.assertTrue("EXTRA" in decoded_data) + self.assertTrue("SKIP" in decoded_data) + self.assertTrue("STEP FORWARD" in decoded_data) + self.assertTrue("STEP BACK" in decoded_data) + self.assertTrue("QUESTION" in decoded_data) self.assertEqual(points.shape, (6, 4, 2)) diff --git a/modules/objdetect/perf/perf_qrcode_pipeline.cpp b/modules/objdetect/perf/perf_qrcode_pipeline.cpp index 9e7960d819..efd7add204 100644 --- a/modules/objdetect/perf/perf_qrcode_pipeline.cpp +++ b/modules/objdetect/perf/perf_qrcode_pipeline.cpp @@ -55,6 +55,10 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, decode) typedef ::perf::TestBaseWithParam< std::string > Perf_Objdetect_QRCode_Multi; +static inline bool compareCorners(const Point2f& corner1, const Point2f& corner2) { + return corner1.x == corner2.x ? corner1.y < corner2.y : corner1.x < corner2.x; +} + PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti) { const std::string name_current_image = GetParam(); @@ -66,9 +70,14 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti) std::vector corners; QRCodeDetector qrcode; TEST_CYCLE() ASSERT_TRUE(qrcode.detectMulti(src, corners)); + sort(corners.begin(), corners.end(), compareCorners); SANITY_CHECK(corners); } +static inline bool compareQR(const pair& v1, const pair& v2) { + return v1.first < v2.first; +} + #ifdef HAVE_QUIRC PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) { @@ -91,15 +100,21 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) ASSERT_FALSE(decoded_info[i].empty()); } } - std::vector < std::vector< uint8_t > > decoded_info_uint8_t; - for(size_t i = 0; i < decoded_info.size(); i++) - { - std::vector< uint8_t > tmp(decoded_info[i].begin(), decoded_info[i].end()); - decoded_info_uint8_t.push_back(tmp); + ASSERT_EQ(decoded_info.size(), straight_barcode.size()); + vector > result; + for (size_t i = 0ull; i < decoded_info.size(); i++) { + result.push_back(make_pair(decoded_info[i], straight_barcode[i])); } - SANITY_CHECK(decoded_info_uint8_t); - SANITY_CHECK(straight_barcode); - + sort(result.begin(), result.end(), compareQR); + vector > decoded_info_sort; + vector straight_barcode_sort; + for (size_t i = 0ull; i < result.size(); i++) { + vector tmp(result[i].first.begin(), result[i].first.end()); + decoded_info_sort.push_back(tmp); + straight_barcode_sort.push_back(result[i].second); + } + SANITY_CHECK(decoded_info_sort); + SANITY_CHECK(straight_barcode_sort); } #endif diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index d720f1b80b..0a4ac5a80c 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -136,7 +136,7 @@ void QRDetect::init(const Mat& src, double eps_vertical_, double eps_horizontal_ const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); } else if (min_side > 512.0) { @@ -524,7 +524,7 @@ bool QRDetect::localization() const int height = cvRound(bin_barcode.size().height * coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); for (size_t i = 0; i < localization_points.size(); i++) { @@ -537,7 +537,7 @@ bool QRDetect::localization() const int height = cvRound(bin_barcode.size().height / coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); for (size_t i = 0; i < localization_points.size(); i++) { @@ -2764,7 +2764,7 @@ void QRDetectMulti::init(const Mat& src, double eps_vertical_, double eps_horizo const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); } else if (min_side > 512.0) { @@ -3121,7 +3121,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector& tmp_localizatio const int height = cvRound(bin_barcode.size().height * coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); } else if (purpose == ZOOMING) @@ -3130,7 +3130,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector& tmp_localizatio const int height = cvRound(bin_barcode.size().height / coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); } else @@ -3148,7 +3148,7 @@ void QRDetectMulti::findQRCodeContours(vector& tmp_localization_points, const int width = cvRound(bin_barcode.size().width); const int height = cvRound(bin_barcode.size().height); Size new_size(width, height); - resize(bar, bar, new_size, 0, 0, INTER_LINEAR); + resize(bar, bar, new_size, 0, 0, INTER_LINEAR_EXACT); blur(bar, blur_image, Size(3, 3)); threshold(blur_image, threshold_output, 50, 255, THRESH_BINARY); diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index b5680387cb..0f50b77b36 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -11,7 +11,7 @@ std::string qrcode_images_name[] = { "version_2_down.jpg", "version_2_left.jpg", "version_2_right.jpg", "version_2_up.jpg", "version_2_top.jpg", "version_3_down.jpg", "version_3_left.jpg", "version_3_right.jpg", "version_3_up.jpg", "version_3_top.jpg", "version_4_down.jpg", "version_4_left.jpg", "version_4_right.jpg", "version_4_up.jpg", "version_4_top.jpg", - "version_5_down.jpg", "version_5_left.jpg"/*"version_5_right.jpg"*/, + "version_5_down.jpg", "version_5_left.jpg"/*"version_5_right.jpg"*/, "version_5_up.jpg", "version_5_top.jpg", "russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg" // version_5_right.jpg DISABLED after tile fix, PR #22025 }; From 189f64726437a3756329890ea75c8ca5fde46bcf Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Wed, 17 Aug 2022 14:38:38 +0000 Subject: [PATCH 022/313] Add implementation for zip, transpose, interleave, reverse and combine. --- .../opencv2/core/hal/intrin_rvv_scalable.hpp | 150 +++++++++++++++++- 1 file changed, 147 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 396a2d68a5..7452ad91ad 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #ifndef CV_RVV_MAX_VLEN #define CV_RVV_MAX_VLEN 1024 @@ -1020,11 +1021,26 @@ OPENCV_HAL_IMPL_RVV_BROADCAST(v_uint32, u32) OPENCV_HAL_IMPL_RVV_BROADCAST(v_int32, s32) OPENCV_HAL_IMPL_RVV_BROADCAST(v_float32, f32) -////////////// Transpose4x4 ////////////// -// TODO ////////////// Reverse ////////////// -// TODO +#define OPENCV_HAL_IMPL_RVV_REVERSE(_Tpvec, width) \ +inline _Tpvec v_reverse(const _Tpvec& a) \ +{ \ + vuint##width##m1_t vidx = vrsub(vid_v_u##width##m1(VTraits<_Tpvec>::vlanes()), VTraits<_Tpvec>::vlanes()-1, VTraits<_Tpvec>::vlanes()); \ + return vrgather(a, vidx, VTraits<_Tpvec>::vlanes()); \ +} +OPENCV_HAL_IMPL_RVV_REVERSE(v_uint8, 8) +OPENCV_HAL_IMPL_RVV_REVERSE(v_int8, 8) +OPENCV_HAL_IMPL_RVV_REVERSE(v_uint16, 16) +OPENCV_HAL_IMPL_RVV_REVERSE(v_int16, 16) +OPENCV_HAL_IMPL_RVV_REVERSE(v_uint32, 32) +OPENCV_HAL_IMPL_RVV_REVERSE(v_int32, 32) +OPENCV_HAL_IMPL_RVV_REVERSE(v_float32, 32) +OPENCV_HAL_IMPL_RVV_REVERSE(v_uint64, 64) +OPENCV_HAL_IMPL_RVV_REVERSE(v_int64, 64) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_REVERSE(v_float64, 64) +#endif //////////// Value reordering //////////// @@ -1067,6 +1083,134 @@ inline v_int32 v_load_expand_q(const schar* ptr) return vwcvt_x(vwcvt_x(vle8_v_i8mf4(ptr, VTraits::vlanes()), VTraits::vlanes()), VTraits::vlanes()); } + +/* void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) + a0 = {A1 A2 A3 A4} + a1 = {B1 B2 B3 B4} +--------------- + {A1 B1 A2 B2} and {A3 B3 A4 B4} +*/ + +#define OPENCV_HAL_IMPL_RVV_ZIP(_Tpvec, _wTpvec, suffix, width, width2, convert2um2, convert2um1) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) { \ + _wTpvec temp = vreinterpret_##suffix##m2(convert2um2( \ + vor(vzext_vf2(convert2um1(a0), VTraits<_Tpvec>::vlanes()*2), \ + vreinterpret_u##width2##m2(vslide1up(vreinterpret_u##width##m2(vzext_vf2(convert2um1(a1), VTraits<_Tpvec>::vlanes()*2)), 0, VTraits<_Tpvec>::vlanes()*2)), \ + VTraits<_Tpvec>::vlanes()))); \ + b0 = vget_##suffix##m1(temp, 0); \ + b1 = vget_##suffix##m1(temp, 1); \ +} +OPENCV_HAL_IMPL_RVV_ZIP(v_uint8, vuint8m2_t, u8, 8, 16, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_ZIP(v_int8, vint8m2_t, i8, 8, 16, vreinterpret_u8m2, vreinterpret_u8m1) +OPENCV_HAL_IMPL_RVV_ZIP(v_uint16, vuint16m2_t, u16, 16, 32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_ZIP(v_int16, vint16m2_t, i16, 16, 32, vreinterpret_u16m2, vreinterpret_u16m1) +OPENCV_HAL_IMPL_RVV_ZIP(v_uint32, vuint32m2_t, u32, 32, 64, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_ZIP(v_int32, vint32m2_t, i32, 32, 64, vreinterpret_u32m2, vreinterpret_u32m1) +OPENCV_HAL_IMPL_RVV_ZIP(v_float32, vfloat32m2_t, f32, 32, 64, vreinterpret_u32m2, vreinterpret_u32m1) + +#define OPENCV_HAL_IMPL_RVV_UNPACKS(_Tpvec, width) \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup(a, b, VTraits<_Tpvec>::vlanes()/2, VTraits<_Tpvec>::vlanes());\ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return vslideup( \ + vslidedown(a, a, VTraits<_Tpvec>::vlanes()/2, VTraits<_Tpvec>::vlanes()), \ + vslidedown(b, b, VTraits<_Tpvec>::vlanes()/2, VTraits<_Tpvec>::vlanes()), \ + VTraits<_Tpvec>::vlanes()/2, \ + VTraits<_Tpvec>::vlanes()); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + c = v_combine_low(a, b); \ + d = v_combine_high(a, b); \ +} + +OPENCV_HAL_IMPL_RVV_UNPACKS(v_uint8, 8) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_int8, 8) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_uint16, 16) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_int16, 16) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_uint32, 32) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_int32, 32) +OPENCV_HAL_IMPL_RVV_UNPACKS(v_float32, 32) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_UNPACKS(v_float64, 64) +#endif + +static uint64_t idx_interleave_pairs[] = { \ + 0x0705060403010200, 0x0f0d0e0c0b090a08, 0x1715161413111210, 0x1f1d1e1c1b191a18, \ + 0x2725262423212220, 0x2f2d2e2c2b292a28, 0x3735363433313230, 0x3f3d3e3c3b393a38, \ + 0x4745464443414240, 0x4f4d4e4c4b494a48, 0x5755565453515250, 0x5f5d5e5c5b595a58, \ + 0x6765666463616260, 0x6f6d6e6c6b696a68, 0x7775767473717270, 0x7f7d7e7c7b797a78}; + +static uint64_t idx_interleave_quads[] = { \ + 0x0703060205010400, 0x0f0b0e0a0d090c08, 0x1713161215111410, 0x1f1b1e1a1d191c18, \ + 0x2723262225212420, 0x2f2b2e2a2d292c28, 0x3733363235313430, 0x3f3b3e3a3d393c38, \ + 0x4743464245414440, 0x4f4b4e4a4d494c48, 0x5753565255515450, 0x5f5b5e5a5d595c58, \ + 0x6763666265616460, 0x6f6b6e6a6d696c68, 0x7773767275717470, 0x7f7b7e7a7d797c78}; + +#define OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ_NOEXPEND(_Tpvec, func) \ +inline _Tpvec v_interleave_##func(const _Tpvec& vec) { \ + CV_CheckLE(VTraits<_Tpvec>::vlanes(), VTraits<_Tpvec>::max_nlanes, "RVV implementation only supports VLEN in the range [128, 1024]"); \ + vuint8m1_t vidx = vundefined_u8m1();\ + vidx = vreinterpret_u8m1(vle64_v_u64m1(idx_interleave_##func, 16)); \ + return vrgather(vec, vidx, VTraits::vlanes()); \ +} +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ_NOEXPEND(v_uint8, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ_NOEXPEND(v_int8, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ_NOEXPEND(v_uint8, quads) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ_NOEXPEND(v_int8, quads) + +#define OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(_Tpvec, width, vzext_vfx, func) \ +inline _Tpvec v_interleave_##func(const _Tpvec& vec) { \ + CV_CheckLE(VTraits<_Tpvec>::vlanes(), VTraits<_Tpvec>::max_nlanes, "RVV implementation only supports VLEN in the range [128, 1024]"); \ + vuint##width##m1_t vidx = vundefined_u##width##m1();\ + vidx = vget_u##width##m1(vzext_vfx(vreinterpret_u8m1(vle64_v_u64m1(idx_interleave_##func, 16)), VTraits::vlanes()), 0); \ + return vrgather(vec, vidx, VTraits<_Tpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_uint16, 16, vzext_vf2, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_int16, 16, vzext_vf2, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_uint32, 32, vzext_vf4, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_int32, 32, vzext_vf4, pairs) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_float32, 32, vzext_vf4, pairs) + +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_uint16, 16, vzext_vf2, quads) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_int16, 16, vzext_vf2, quads) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_uint32, 32, vzext_vf4, quads) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_int32, 32, vzext_vf4, quads) +OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_float32, 32, vzext_vf4, quads) + +////////////// Transpose4x4 ////////////// +#define OPENCV_HAL_IMPL_RVV_ZIP4(_Tpvec, _wTpvec, suffix, convert2u, convert) \ +static inline void v_zip4(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) { \ + int vl = 4; \ + _wTpvec temp = vreinterpret_##suffix##m2(convert2u( \ + vor(vzext_vf2(convert(a0), vl), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(convert(a1), vl)), 0, vl*2)), \ + vl))); \ + b0 = vget_##suffix##m1(temp, 0); \ + b1 = vget_##suffix##m1(vrgather(temp, vadd(vid_v_u32m2(vl), 4, vl)/*{4,5,6,7} */, vl) ,0); \ +} + +OPENCV_HAL_IMPL_RVV_ZIP4(v_uint32, vuint32m2_t, u32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_ZIP4(v_int32, vint32m2_t, i32, vreinterpret_u32m2, vreinterpret_u32m1) +OPENCV_HAL_IMPL_RVV_ZIP4(v_float32, vfloat32m2_t, f32, vreinterpret_u32m2, vreinterpret_u32m1) + +#define OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, const _Tpvec& a2, const _Tpvec& a3, _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) { \ + _Tpvec t0,t1,t2,t3= vundefined_##suffix##m1(); \ + v_zip4(a0, a2, t0, t2); \ + v_zip4(a1, a3, t1, t3); \ + v_zip4(t0, t1, b0, b1); \ + v_zip4(t2, t3, b2, b3); \ +} + +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_uint32, u32) +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_int32, i32) +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_float32, f32) + //////////// PopCount ////////// // TODO From 8dc332721ffce54c750f1a259690cc36c2b126b5 Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Wed, 17 Aug 2022 14:39:23 +0000 Subject: [PATCH 023/313] Add testcases for interleave_p&q and enable others testcases. --- modules/core/test/test_intrin_utils.hpp | 120 +++++++++++++++++------- 1 file changed, 87 insertions(+), 33 deletions(-) diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index 4af3998c3e..cb6ac5d901 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -503,6 +503,38 @@ template struct TheTest return *this; } + TheTest & test_interleave_pq() + { + Data dataA; + R a = dataA; + Data resP = v_interleave_pairs(a); + Data resQ = v_interleave_quads(a); + for (int i = 0; i < VTraits::vlanes()/4; ++i) + { + SCOPED_TRACE(cv::format("i=%d", i)); + EXPECT_EQ(resP[4*i], dataA[4*i ]); + EXPECT_EQ(resP[4*i + 1], dataA[4*i+2]); + EXPECT_EQ(resP[4*i + 2], dataA[4*i+1]); + EXPECT_EQ(resP[4*i + 3], dataA[4*i+3]); + } + for (int i = 0; i < VTraits::vlanes(); ++i) + { + printf("%d%s", (int)resQ[i], i == VTraits::vlanes()-1 ? "\n" : " "); + } + for (int i = 0; i < VTraits::vlanes()/8; ++i) + { + SCOPED_TRACE(cv::format("i=%d", i)); + EXPECT_EQ(resQ[8*i], dataA[8*i ]); + EXPECT_EQ(resQ[8*i + 1], dataA[8*i+4]); + EXPECT_EQ(resQ[8*i + 2], dataA[8*i+1]); + EXPECT_EQ(resQ[8*i + 3], dataA[8*i+5]); + EXPECT_EQ(resQ[8*i + 4], dataA[8*i+2]); + EXPECT_EQ(resQ[8*i + 5], dataA[8*i+6]); + EXPECT_EQ(resQ[8*i + 6], dataA[8*i+3]); + EXPECT_EQ(resQ[8*i + 7], dataA[8*i+7]); + } + return *this; + } // float32x4 only TheTest & test_interleave_2channel() @@ -1577,19 +1609,27 @@ template struct TheTest v_transpose4x4(a, b, c, d, e, f, g, h); - Data res[4] = {e, f, g, h}; - // for (int i = 0; i < VTraits::vlanes(); i += 4) - // { - int i = 0; - for (int j = 0; j < 4; ++j) - { - SCOPED_TRACE(cv::format("i=%d j=%d", i, j)); - EXPECT_EQ(dataA[i + j], res[j][i]); - EXPECT_EQ(dataB[i + j], res[j][i + 1]); - EXPECT_EQ(dataC[i + j], res[j][i + 2]); - EXPECT_EQ(dataD[i + j], res[j][i + 3]); - } - // } + // Data res[4] = {e, f, g, h}; // Generates incorrect data in certain RVV case. + Data res0 = e, res1 = f, res2 = g, res3 = h; + EXPECT_EQ(dataA[0], res0[0]); + EXPECT_EQ(dataB[0], res0[1]); + EXPECT_EQ(dataC[0], res0[2]); + EXPECT_EQ(dataD[0], res0[3]); + + EXPECT_EQ(dataA[1], res1[0]); + EXPECT_EQ(dataB[1], res1[1]); + EXPECT_EQ(dataC[1], res1[2]); + EXPECT_EQ(dataD[1], res1[3]); + + EXPECT_EQ(dataA[2], res2[0]); + EXPECT_EQ(dataB[2], res2[1]); + EXPECT_EQ(dataC[2], res2[2]); + EXPECT_EQ(dataD[2], res2[3]); + + EXPECT_EQ(dataA[3], res3[0]); + EXPECT_EQ(dataB[3], res3[1]); + EXPECT_EQ(dataC[3], res3[2]); + EXPECT_EQ(dataD[3], res3[3]); return *this; } @@ -1741,6 +1781,7 @@ void test_hal_intrin_uint8() // typedef v_uint8 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_expand_q() .test_addsub() @@ -1755,6 +1796,8 @@ void test_hal_intrin_uint8() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_unpack() + .test_reverse() #if 0 // not implemented in rvv backend yet. .test_interleave() .test_cmp() @@ -1764,8 +1807,6 @@ void test_hal_intrin_uint8() .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() .test_pack_u<1>().test_pack_u<2>().test_pack_u<3>().test_pack_u<8>() .test_pack_b() - .test_unpack() - .test_reverse() .test_popcount() #endif ; @@ -1777,6 +1818,7 @@ void test_hal_intrin_int8() // typedef v_int8 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_expand_q() .test_addsub() @@ -1793,6 +1835,8 @@ void test_hal_intrin_int8() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_unpack() + .test_reverse() #if 0 .test_interleave() .test_cmp() @@ -1800,8 +1844,6 @@ void test_hal_intrin_int8() .test_reduce() .test_reduce_sad() .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() - .test_unpack() - .test_reverse() .test_popcount() #endif ; @@ -1815,6 +1857,7 @@ void test_hal_intrin_uint16() // typedef v_uint16 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_addsub() .test_arithm_wrap() @@ -1831,6 +1874,8 @@ void test_hal_intrin_uint16() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_unpack() + .test_reverse() #if 0 .test_interleave() .test_cmp() @@ -1839,8 +1884,6 @@ void test_hal_intrin_uint16() .test_reduce_sad() .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() .test_pack_u<1>().test_pack_u<2>().test_pack_u<7>().test_pack_u<16>() - .test_unpack() - .test_reverse() .test_popcount() #endif ; @@ -1852,6 +1895,7 @@ void test_hal_intrin_int16() // typedef v_int16 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_addsub() .test_arithm_wrap() @@ -1870,6 +1914,8 @@ void test_hal_intrin_int16() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_unpack() + .test_reverse() #if 0 .test_interleave() @@ -1879,8 +1925,6 @@ void test_hal_intrin_int16() .test_reduce() .test_reduce_sad() .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() - .test_unpack() - .test_reverse() .test_popcount() #endif ; @@ -1894,6 +1938,7 @@ void test_hal_intrin_uint32() // typedef v_uint32 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_addsub() .test_mul() @@ -1910,15 +1955,15 @@ void test_hal_intrin_uint32() .test_broadcast_element<0>().test_broadcast_element<1>() .test_extract_highest() .test_broadcast_highest() + .test_unpack() + .test_transpose() + .test_reverse() #if 0 .test_interleave() .test_cmp() .test_reduce() .test_reduce_sad() .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() - .test_unpack() - .test_reverse() - .test_transpose() .test_popcount() #endif ; @@ -1930,6 +1975,7 @@ void test_hal_intrin_int32() // typedef v_int32 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_expand() .test_addsub() .test_mul() @@ -1946,6 +1992,9 @@ void test_hal_intrin_int32() .test_broadcast_element<0>().test_broadcast_element<1>() .test_extract_highest() .test_broadcast_highest() + .test_unpack() + .test_transpose() + .test_reverse() #if 0 .test_interleave() .test_cmp() @@ -1953,11 +2002,8 @@ void test_hal_intrin_int32() .test_reduce() .test_reduce_sad() .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() - .test_unpack() - .test_reverse() .test_float_cvt32() .test_float_cvt64() - .test_transpose() .test_popcount() #endif ; @@ -1978,12 +2024,12 @@ void test_hal_intrin_uint64() .test_rotate<0>().test_rotate<1>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_reverse() ; #if 0 #if CV_SIMD_64F .test_cmp64() #endif - .test_reverse() #endif } @@ -2001,12 +2047,12 @@ void test_hal_intrin_int64() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() .test_cvt64_double() + .test_reverse() ; #if 0 #if CV_SIMD_64F .test_cmp64() #endif - .test_reverse() #endif } @@ -2017,6 +2063,7 @@ void test_hal_intrin_float32() // typedef v_float32 R; TheTest() .test_loadstore() + .test_interleave_pq() .test_addsub() .test_abs() .test_mul() @@ -2031,18 +2078,18 @@ void test_hal_intrin_float32() .test_broadcast_element<0>().test_broadcast_element<1>() .test_extract_highest() .test_broadcast_highest() + .test_unpack() + .test_transpose() + .test_reverse() #if 0 .test_interleave() .test_interleave_2channel() .test_cmp() .test_reduce() .test_reduce_sad() - .test_unpack() .test_float_math() .test_float_cvt64() .test_matmul() - .test_transpose() - .test_reverse() .test_reduce_sum4() #endif ; @@ -2067,12 +2114,12 @@ void test_hal_intrin_float64() .test_rotate<0>().test_rotate<1>() .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() + .test_reverse() #if 0 .test_cmp() .test_unpack() .test_float_cvt32() .test_float_math() - .test_reverse() #endif ; @@ -2091,6 +2138,7 @@ void test_hal_intrin_uint8() TheTest() .test_loadstore() .test_interleave() + .test_interleave_pq() .test_expand() .test_expand_q() .test_addsub() @@ -2132,6 +2180,7 @@ void test_hal_intrin_int8() TheTest() .test_loadstore() .test_interleave() + .test_interleave_pq() .test_expand() .test_expand_q() .test_addsub() @@ -2169,6 +2218,7 @@ void test_hal_intrin_uint16() TheTest() .test_loadstore() .test_interleave() + .test_interleave_pq() .test_expand() .test_addsub() .test_arithm_wrap() @@ -2205,6 +2255,7 @@ void test_hal_intrin_int16() TheTest() .test_loadstore() .test_interleave() + .test_interleave_pq() .test_expand() .test_addsub() .test_arithm_wrap() @@ -2245,6 +2296,7 @@ void test_hal_intrin_uint32() TheTest() .test_loadstore() .test_interleave() + // .test_interleave_pq() //not implemented in AVX .test_expand() .test_addsub() .test_mul() @@ -2279,6 +2331,7 @@ void test_hal_intrin_int32() TheTest() .test_loadstore() .test_interleave() + // .test_interleave_pq() //not implemented in AVX .test_expand() .test_addsub() .test_mul() @@ -2363,6 +2416,7 @@ void test_hal_intrin_float32() .test_loadstore() .test_interleave() .test_interleave_2channel() + // .test_interleave_pq() //not implemented in AVX .test_addsub() .test_mul() .test_div() From b9a1039566a60175fad8dd646e598b2e31ff1f4a Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Thu, 18 Aug 2022 08:01:09 +0000 Subject: [PATCH 024/313] Remove the test log in test_interleave_pq. --- modules/core/test/test_intrin_utils.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index cb6ac5d901..ac05768c35 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -517,10 +517,6 @@ template struct TheTest EXPECT_EQ(resP[4*i + 2], dataA[4*i+1]); EXPECT_EQ(resP[4*i + 3], dataA[4*i+3]); } - for (int i = 0; i < VTraits::vlanes(); ++i) - { - printf("%d%s", (int)resQ[i], i == VTraits::vlanes()-1 ? "\n" : " "); - } for (int i = 0; i < VTraits::vlanes()/8; ++i) { SCOPED_TRACE(cv::format("i=%d", i)); From 7e5d012f755ffd599729b5d17d01a8f6950ebe2f Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Sun, 21 Aug 2022 12:06:46 +0530 Subject: [PATCH 025/313] cmake: Enable pkgconfig support for mingw --- CMakeLists.txt | 4 +++- cmake/OpenCVGenPkgconfig.cmake | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43b6023242..3eac4b5dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -641,7 +641,7 @@ ocv_cmake_hook(POST_COMPILER_OPTIONS) # ---------------------------------------------------------------------------- # CHECK FOR SYSTEM LIBRARIES, OPTIONS, ETC.. # ---------------------------------------------------------------------------- -if(UNIX) +if(UNIX OR MINGW) if(NOT APPLE_FRAMEWORK OR OPENCV_ENABLE_PKG_CONFIG) if(CMAKE_CROSSCOMPILING AND NOT DEFINED ENV{PKG_CONFIG_LIBDIR} AND NOT DEFINED ENV{PKG_CONFIG_SYSROOT_DIR} AND NOT OPENCV_ENABLE_PKG_CONFIG @@ -673,6 +673,8 @@ if(UNIX) # no need to link to system libs with emscripten elseif(QNXNTO) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} m) + elseif(MINGW) + set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} pthread) else() set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} dl m pthread rt) endif() diff --git a/cmake/OpenCVGenPkgconfig.cmake b/cmake/OpenCVGenPkgconfig.cmake index 43d6a87e74..8d36b74f09 100644 --- a/cmake/OpenCVGenPkgconfig.cmake +++ b/cmake/OpenCVGenPkgconfig.cmake @@ -103,7 +103,7 @@ add_custom_target(gen-pkgconfig ALL SOURCES "${CMAKE_BINARY_DIR}/unix-install/${ add_dependencies(developer_scripts gen-pkgconfig) -if(UNIX AND NOT ANDROID) +if((UNIX AND NOT ANDROID) OR MINGW) install(FILES ${CMAKE_BINARY_DIR}/unix-install/${OPENCV_PC_FILE_NAME} DESTINATION ${OPENCV_LIB_INSTALL_PATH}/pkgconfig COMPONENT dev) endif() From 5e92bf8e411f538b45c80aac842a9c1ff50f8150 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Mon, 22 Aug 2022 10:51:29 +0800 Subject: [PATCH 026/313] support silu activation in darknet --- modules/dnn/src/darknet/darknet_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/src/darknet/darknet_io.cpp b/modules/dnn/src/darknet/darknet_io.cpp index 520f3c94be..54140f8dc0 100644 --- a/modules/dnn/src/darknet/darknet_io.cpp +++ b/modules/dnn/src/darknet/darknet_io.cpp @@ -229,7 +229,7 @@ namespace cv { activation_param.set("negative_slope", 0.1f); activation_param.type = "ReLU"; } - else if (type == "swish") + else if (type == "swish" || type == "silu") // swish is an extension of silu. { activation_param.type = "Swish"; } From 1fb8d60fd227bcd1aa9a08614c978a890610a198 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Mon, 22 Aug 2022 11:14:59 +0800 Subject: [PATCH 027/313] remove whitespace --- modules/videoio/include/opencv2/videoio.hpp | 8 ++++---- .../src/cap_obsensor/obsensor_stream_channel_msmf.hpp | 2 +- .../src/cap_obsensor/obsensor_uvc_stream_channel.cpp | 2 +- samples/cpp/videocapture_obsensor.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 3a4b5995f1..bbe392266f 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -659,21 +659,21 @@ enum { CAP_PROP_IMAGES_BASE = 18000, @{ */ //! OBSENSOR data given from image generator -enum VideoCaptureOBSensorDataType{ +enum VideoCaptureOBSensorDataType{ CAP_OBSENSOR_DEPTH_MAP = 0, //!< Depth values in mm (CV_16UC1) CAP_OBSENSOR_BGR_IMAGE = 1, //!< Data given from BGR stream generator CAP_OBSENSOR_IR_IMAGE = 2 //!< Data given from IR stream generator(CV_16UC1) }; //! OBSENSOR stream generator -enum VideoCaptureOBSensorGenerators{ +enum VideoCaptureOBSensorGenerators{ CAP_OBSENSOR_DEPTH_GENERATOR = 1 << 29, CAP_OBSENSOR_IMAGE_GENERATOR = 1 << 28, - CAP_OBSENSOR_IR_GENERATOR = 1 << 27, + CAP_OBSENSOR_IR_GENERATOR = 1 << 27, CAP_OBSENSOR_GENERATORS_MASK = CAP_OBSENSOR_DEPTH_GENERATOR + CAP_OBSENSOR_IMAGE_GENERATOR + CAP_OBSENSOR_IR_GENERATOR }; -//!OBSENSOR properties +//!OBSENSOR properties enum VideoCaptureOBSensorProperties{ // INTRINSIC CAP_PROP_OBSENSOR_INTRINSIC_FX=26001, diff --git a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.hpp b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.hpp index 2891ff488a..401989950d 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.hpp +++ b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_msmf.hpp @@ -42,7 +42,7 @@ #include #include //IKsTopologyInfo #include //IKsControl -#include +#include namespace cv { namespace obsensor { diff --git a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp index ff038f8330..aa2b7f9dd0 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp +++ b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp @@ -61,7 +61,7 @@ StreamType parseUvcDeviceNameToStreamType(const std::string& devName) std::string uvcDevName = devName; for (size_t i = 0; i < uvcDevName.length(); i++) { - uvcDevName[i] = (char)tolower(uvcDevName[i]); + uvcDevName[i] = (char)tolower(uvcDevName[i]); } if (uvcDevName.find(" depth") != std::string::npos) { diff --git a/samples/cpp/videocapture_obsensor.cpp b/samples/cpp/videocapture_obsensor.cpp index 6af73de1d1..98eb9f479f 100644 --- a/samples/cpp/videocapture_obsensor.cpp +++ b/samples/cpp/videocapture_obsensor.cpp @@ -17,7 +17,7 @@ int main() double cx = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CX); double cy = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CY); std::cout << "obsensor camera intrinsic params: fx=" << fx << ", fy=" << fy << ", cx=" << cx << ", cy=" << cy << std::endl; - + Mat image; Mat depthMap; Mat adjDepthMap; From 6432f029968b7d039f17f9b3f032fa9d320cfbc5 Mon Sep 17 00:00:00 2001 From: MENG Yu Date: Sat, 6 Aug 2022 01:52:33 +0800 Subject: [PATCH 028/313] select correct MediaType in MSMF backend. --- modules/videoio/src/cap_msmf.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index d3002a5151..0b46be1ad9 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -347,6 +347,12 @@ struct MediaType } return false; } + bool VideoIsAvailable() const + { + return ((subType == MFVideoFormat_RGB32) || + (subType == MFVideoFormat_RGB24) || + (subType == MFVideoFormat_YUY2)); + } }; void printFormat(std::ostream& out, const GUID& fmt) @@ -627,7 +633,7 @@ public: { if (i->second.majorType == MFMediaType_Video) { - if (best.second.isEmpty() || i->second.VideoIsBetterThan(best.second, newType)) + if (best.second.isEmpty() || (i->second.VideoIsBetterThan(best.second, newType) && i->second.VideoIsAvailable())) { best = *i; } From ef570e4e135e5c7259f194b2c4534ddc470db06a Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Mon, 22 Aug 2022 17:42:22 +0300 Subject: [PATCH 029/313] Suppress warnings in carotene on macOS ARM64 for 3.4 branch --- 3rdparty/carotene/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/3rdparty/carotene/CMakeLists.txt b/3rdparty/carotene/CMakeLists.txt index 3d49a2def6..091990d722 100644 --- a/3rdparty/carotene/CMakeLists.txt +++ b/3rdparty/carotene/CMakeLists.txt @@ -27,6 +27,10 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() endif() +if(APPLE AND CV_CLANG AND WITH_NEON) + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-unused-function -Wno-c++11-extensions) +endif() + add_library(carotene_objs OBJECT EXCLUDE_FROM_ALL ${carotene_headers} ${carotene_sources} From a001ab3a445b8e1f8f4004a816c1a0232a90bd05 Mon Sep 17 00:00:00 2001 From: Daisuke Mizobuchi Date: Wed, 15 Jun 2022 10:35:42 +0900 Subject: [PATCH 030/313] V4L2: Add multi-planar capture support Devices which only support multi-planar capture cannot be processed as single-planar. Add multi-planar support to v4l driver. --- modules/videoio/src/cap_v4l.cpp | 335 +++++++++++++++++++++++--------- 1 file changed, 244 insertions(+), 91 deletions(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index 2fc41ce05e..bbc05efadc 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -286,6 +286,12 @@ typedef uint32_t __u32; #define MAX_V4L_BUFFERS 10 #define DEFAULT_V4L_BUFFERS 4 +// types of memory in 'special' buffer +enum { + MEMORY_ORIG = 0, // Image data in original format. + MEMORY_RGB = 1, // Image data converted to RGB format. +}; + // if enabled, then bad JPEG warnings become errors and cause NULL returned instead of image #define V4L_ABORT_BADJPEG @@ -317,17 +323,27 @@ static const char* decode_ioctl_code(unsigned long ioctlCode) return "unknown"; } +struct Memory +{ + void * start; + size_t length; + + Memory() : start(NULL), length(0) {} +}; + /* Device Capture Objects */ /* V4L2 structure */ struct Buffer { - void * start; - size_t length; + Memory memories[VIDEO_MAX_PLANES]; + v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + // Total number of bytes occupied by data in the all planes (payload) + __u32 bytesused; // This is dequeued buffer. It used for to put it back in the queue. // The buffer is valid only if capture->bufferIndex >= 0 v4l2_buffer buffer; - Buffer() : start(NULL), length(0) + Buffer() { buffer = v4l2_buffer(); } @@ -374,6 +390,7 @@ struct CvCaptureCAM_V4L CV_FINAL : public CvCapture v4l2_format form; v4l2_requestbuffers req; v4l2_buf_type type; + unsigned char num_planes; timeval timestamp; @@ -430,6 +447,7 @@ CvCaptureCAM_V4L::CvCaptureCAM_V4L() : fps(0), convert_rgb(0), frame_allocated(false), returnFrame(false), channelNumber(-1), normalizePropRange(false), type(V4L2_BUF_TYPE_VIDEO_CAPTURE), + num_planes(0), havePendingFrame(false) { frame = cvIplImage(); @@ -472,15 +490,24 @@ bool CvCaptureCAM_V4L::isOpened() const bool CvCaptureCAM_V4L::try_palette_v4l2() { form = v4l2_format(); - form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - form.fmt.pix.pixelformat = palette; - form.fmt.pix.field = V4L2_FIELD_ANY; - form.fmt.pix.width = width; - form.fmt.pix.height = height; + form.type = type; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + form.fmt.pix_mp.pixelformat = palette; + form.fmt.pix_mp.field = V4L2_FIELD_ANY; + form.fmt.pix_mp.width = width; + form.fmt.pix_mp.height = height; + } else { + form.fmt.pix.pixelformat = palette; + form.fmt.pix.field = V4L2_FIELD_ANY; + form.fmt.pix.width = width; + form.fmt.pix.height = height; + } if (!tryIoctl(VIDIOC_S_FMT, &form, true)) { return false; } + if (V4L2_TYPE_IS_MULTIPLANAR(type)) + return palette == form.fmt.pix_mp.pixelformat; return palette == form.fmt.pix.pixelformat; } @@ -534,12 +561,15 @@ bool CvCaptureCAM_V4L::try_init_v4l2() return false; } - if ((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) + if ((capability.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) == 0) { /* Nope. */ - CV_LOG_INFO(NULL, "VIDEOIO(V4L2:" << deviceName << "): not supported - device is unable to capture video (missing V4L2_CAP_VIDEO_CAPTURE)"); + CV_LOG_INFO(NULL, "VIDEOIO(V4L2:" << deviceName << "): not supported - device is unable to capture video (missing V4L2_CAP_VIDEO_CAPTURE or V4L2_CAP_VIDEO_CAPTURE_MPLANE)"); return false; } + + if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; return true; } @@ -603,7 +633,7 @@ bool CvCaptureCAM_V4L::setFps(int value) return false; v4l2_streamparm streamparm = v4l2_streamparm(); - streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + streamparm.type = type; streamparm.parm.capture.timeperframe.numerator = 1; streamparm.parm.capture.timeperframe.denominator = __u32(value); if (!tryIoctl(VIDIOC_S_PARM, &streamparm) || !tryIoctl(VIDIOC_G_PARM, &streamparm)) @@ -652,12 +682,21 @@ bool CvCaptureCAM_V4L::convertableToRgb() const void CvCaptureCAM_V4L::v4l2_create_frame() { - CV_Assert(form.fmt.pix.width <= (uint)std::numeric_limits::max()); - CV_Assert(form.fmt.pix.height <= (uint)std::numeric_limits::max()); - CvSize size = {(int)form.fmt.pix.width, (int)form.fmt.pix.height}; + CvSize size; int channels = 3; int depth = IPL_DEPTH_8U; + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + CV_Assert(form.fmt.pix_mp.width <= (uint)std::numeric_limits::max()); + CV_Assert(form.fmt.pix_mp.height <= (uint)std::numeric_limits::max()); + size = {(int)form.fmt.pix_mp.width, (int)form.fmt.pix_mp.height}; + } else { + CV_Assert(form.fmt.pix.width <= (uint)std::numeric_limits::max()); + CV_Assert(form.fmt.pix.height <= (uint)std::numeric_limits::max()); + size = {(int)form.fmt.pix.width, (int)form.fmt.pix.height}; + } + if (!convert_rgb) { switch (palette) { case V4L2_PIX_FMT_BGR24: @@ -689,9 +728,19 @@ void CvCaptureCAM_V4L::v4l2_create_frame() default: channels = 1; if(bufferIndex < 0) - size = cvSize(buffers[MAX_V4L_BUFFERS].length, 1); - else - size = cvSize(buffers[bufferIndex].buffer.bytesused, 1); + size = cvSize(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, 1); + else { + __u32 bytesused = 0; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + __u32 data_offset; + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + data_offset = buffers[bufferIndex].planes[n_planes].data_offset; + bytesused += buffers[bufferIndex].planes[n_planes].bytesused - data_offset; + } + } else + bytesused = buffers[bufferIndex].buffer.bytesused; + size = cvSize(bytesused, 1); + } break; } } @@ -723,7 +772,7 @@ bool CvCaptureCAM_V4L::initCapture() /* Find Window info */ form = v4l2_format(); - form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + form.type = type; if (!tryIoctl(VIDIOC_G_FMT, &form)) { @@ -743,18 +792,27 @@ bool CvCaptureCAM_V4L::initCapture() /* try to set framerate */ setFps(fps); - unsigned int min; - /* Buggy driver paranoia. */ - min = form.fmt.pix.width * 2; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + // TODO: add size adjustment if needed + } else { + unsigned int min; - if (form.fmt.pix.bytesperline < min) - form.fmt.pix.bytesperline = min; + min = form.fmt.pix.width * 2; - min = form.fmt.pix.bytesperline * form.fmt.pix.height; + if (form.fmt.pix.bytesperline < min) + form.fmt.pix.bytesperline = min; - if (form.fmt.pix.sizeimage < min) - form.fmt.pix.sizeimage = min; + min = form.fmt.pix.bytesperline * form.fmt.pix.height; + + if (form.fmt.pix.sizeimage < min) + form.fmt.pix.sizeimage = min; + } + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) + num_planes = form.fmt.pix_mp.num_planes; + else + num_planes = 1; if (!requestBuffers()) return false; @@ -800,7 +858,7 @@ bool CvCaptureCAM_V4L::requestBuffers(unsigned int buffer_number) req = v4l2_requestbuffers(); req.count = buffer_number; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.type = type; req.memory = V4L2_MEMORY_MMAP; if (!tryIoctl(VIDIOC_REQBUFS, &req)) { @@ -824,34 +882,56 @@ bool CvCaptureCAM_V4L::createBuffers() size_t maxLength = 0; for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) { v4l2_buffer buf = v4l2_buffer(); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_plane mplanes[VIDEO_MAX_PLANES]; + size_t length; + off_t offset; + buf.type = type; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + buf.m.planes = mplanes; + buf.length = VIDEO_MAX_PLANES; + } if (!tryIoctl(VIDIOC_QUERYBUF, &buf)) { CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QUERYBUF: errno=" << errno << " (" << strerror(errno) << ")"); return false; } - buffers[n_buffers].length = buf.length; - buffers[n_buffers].start = - mmap(NULL /* start anywhere */, - buf.length, - PROT_READ /* required */, - MAP_SHARED /* recommended */, - deviceHandle, buf.m.offset); + CV_Assert(1 <= num_planes && num_planes <= VIDEO_MAX_PLANES); + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + length = buf.m.planes[n_planes].length; + offset = buf.m.planes[n_planes].m.mem_offset; + } else { + length = buf.length; + offset = buf.m.offset; + } - if (MAP_FAILED == buffers[n_buffers].start) { - CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed mmap(" << buf.length << "): errno=" << errno << " (" << strerror(errno) << ")"); - return false; + buffers[n_buffers].memories[n_planes].length = length; + buffers[n_buffers].memories[n_planes].start = + mmap(NULL /* start anywhere */, + length, + PROT_READ /* required */, + MAP_SHARED /* recommended */, + deviceHandle, offset); + if (MAP_FAILED == buffers[n_buffers].memories[n_planes].start) { + CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed mmap(" << length << "): errno=" << errno << " (" << strerror(errno) << ")"); + return false; + } } - maxLength = maxLength > buf.length ? maxLength : buf.length; + + maxLength = maxLength > length ? maxLength : length; } if (maxLength > 0) { - buffers[MAX_V4L_BUFFERS].start = malloc(maxLength); - buffers[MAX_V4L_BUFFERS].length = maxLength; + maxLength *= num_planes; + buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start = malloc(maxLength); + buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length = maxLength; + buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start = malloc(maxLength); + buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].length = maxLength; } - return buffers[MAX_V4L_BUFFERS].start != 0; + return (buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start != 0) && + (buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start != 0); } /** @@ -933,8 +1013,13 @@ bool CvCaptureCAM_V4L::open(const char* _deviceName) bool CvCaptureCAM_V4L::read_frame_v4l2() { v4l2_buffer buf = v4l2_buffer(); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_plane mplanes[VIDEO_MAX_PLANES]; + buf.type = type; buf.memory = V4L2_MEMORY_MMAP; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + buf.m.planes = mplanes; + buf.length = VIDEO_MAX_PLANES; + } while (!tryIoctl(VIDIOC_DQBUF, &buf)) { int err = errno; @@ -951,12 +1036,33 @@ bool CvCaptureCAM_V4L::read_frame_v4l2() } CV_Assert(buf.index < req.count); - CV_Assert(buffers[buf.index].length == buf.length); + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) + CV_Assert(buffers[buf.index].memories[n_planes].length == buf.m.planes[n_planes].length); + } else + CV_Assert(buffers[buf.index].memories[MEMORY_ORIG].length == buf.length); //We shouldn't use this buffer in the queue while not retrieve frame from it. buffers[buf.index].buffer = buf; bufferIndex = buf.index; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + __u32 offset = 0; + + buffers[buf.index].buffer.m.planes = buffers[buf.index].planes; + memcpy(buffers[buf.index].planes, buf.m.planes, sizeof(mplanes)); + + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + __u32 bytesused; + bytesused = buffers[buf.index].planes[n_planes].bytesused - + buffers[buf.index].planes[n_planes].data_offset; + offset += bytesused; + } + buffers[buf.index].bytesused = offset; + } else + buffers[buf.index].bytesused = buffers[buf.index].buffer.bytesused; + //set timestamp in capture struct to be timestamp of most recent frame timestamp = buf.timestamp; return true; @@ -1042,10 +1148,15 @@ bool CvCaptureCAM_V4L::grabFrame() bufferIndex = -1; for (__u32 index = 0; index < req.count; ++index) { v4l2_buffer buf = v4l2_buffer(); + v4l2_plane mplanes[VIDEO_MAX_PLANES]; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.type = type; buf.memory = V4L2_MEMORY_MMAP; buf.index = index; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + buf.m.planes = mplanes; + buf.length = VIDEO_MAX_PLANES; + } if (!tryIoctl(VIDIOC_QBUF, &buf)) { CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QBUF (buffer=" << index << "): errno=" << errno << " (" << strerror(errno) << ")"); @@ -1534,35 +1645,51 @@ static int sonix_decompress(int width, int height, unsigned char *inp, unsigned void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer) { - cv::Size imageSize(form.fmt.pix.width, form.fmt.pix.height); + cv::Size imageSize; + unsigned char *start; + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + __u32 offset = 0; + start = (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start; + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + __u32 data_offset, bytesused; + data_offset = currentBuffer.planes[n_planes].data_offset; + bytesused = currentBuffer.planes[n_planes].bytesused - data_offset; + memcpy(start + offset, (char *)currentBuffer.memories[n_planes].start + data_offset, + std::min(currentBuffer.memories[n_planes].length, (size_t)bytesused)); + offset += bytesused; + } + + imageSize = cv::Size(form.fmt.pix_mp.width, form.fmt.pix_mp.height); + } else { + start = (unsigned char*)currentBuffer.memories[MEMORY_ORIG].start; + + imageSize = cv::Size(form.fmt.pix.width, form.fmt.pix.height); + } // Not found conversion switch (palette) { case V4L2_PIX_FMT_YUV411P: yuv411p_to_rgb24(imageSize.width, imageSize.height, - (unsigned char*)(currentBuffer.start), - (unsigned char*)frame.imageData); + start, (unsigned char*)frame.imageData); return; case V4L2_PIX_FMT_SBGGR8: bayer2rgb24(imageSize.width, imageSize.height, - (unsigned char*)currentBuffer.start, - (unsigned char*)frame.imageData); + start, (unsigned char*)frame.imageData); return; case V4L2_PIX_FMT_SN9C10X: sonix_decompress_init(); sonix_decompress(imageSize.width, imageSize.height, - (unsigned char*)currentBuffer.start, - (unsigned char*)buffers[MAX_V4L_BUFFERS].start); + start, (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start); bayer2rgb24(imageSize.width, imageSize.height, - (unsigned char*)buffers[MAX_V4L_BUFFERS].start, + (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start, (unsigned char*)frame.imageData); return; case V4L2_PIX_FMT_SGBRG8: sgbrg2rgb24(imageSize.width, imageSize.height, - (unsigned char*)currentBuffer.start, - (unsigned char*)frame.imageData); + start, (unsigned char*)frame.imageData); return; default: break; @@ -1571,69 +1698,69 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer) cv::Mat destination(imageSize, CV_8UC3, frame.imageData); switch (palette) { case V4L2_PIX_FMT_YVU420: - cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination, + cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination, COLOR_YUV2BGR_YV12); return; case V4L2_PIX_FMT_YUV420: - cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination, + cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination, COLOR_YUV2BGR_IYUV); return; case V4L2_PIX_FMT_NV12: - cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination, + cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination, COLOR_YUV2RGB_NV12); return; case V4L2_PIX_FMT_NV21: - cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination, + cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination, COLOR_YUV2RGB_NV21); return; #ifdef HAVE_JPEG case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: - CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): decoding JPEG frame: size=" << currentBuffer.buffer.bytesused); - cv::imdecode(Mat(1, currentBuffer.buffer.bytesused, CV_8U, currentBuffer.start), IMREAD_COLOR, &destination); + CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): decoding JPEG frame: size=" << currentBuffer.bytesused); + cv::imdecode(Mat(1, currentBuffer.bytesused, CV_8U, start), IMREAD_COLOR, &destination); return; #endif case V4L2_PIX_FMT_YUYV: - cv::cvtColor(cv::Mat(imageSize, CV_8UC2, currentBuffer.start), destination, COLOR_YUV2BGR_YUYV); + cv::cvtColor(cv::Mat(imageSize, CV_8UC2, start), destination, COLOR_YUV2BGR_YUYV); return; case V4L2_PIX_FMT_UYVY: - cv::cvtColor(cv::Mat(imageSize, CV_8UC2, currentBuffer.start), destination, COLOR_YUV2BGR_UYVY); + cv::cvtColor(cv::Mat(imageSize, CV_8UC2, start), destination, COLOR_YUV2BGR_UYVY); return; case V4L2_PIX_FMT_RGB24: - cv::cvtColor(cv::Mat(imageSize, CV_8UC3, currentBuffer.start), destination, COLOR_RGB2BGR); + cv::cvtColor(cv::Mat(imageSize, CV_8UC3, start), destination, COLOR_RGB2BGR); return; case V4L2_PIX_FMT_Y16: { - cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start); - cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 256); + cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start); + cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 256); cv::cvtColor(temp, destination, COLOR_GRAY2BGR); return; } case V4L2_PIX_FMT_Y12: { - cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start); - cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 16); + cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start); + cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 16); cv::cvtColor(temp, destination, COLOR_GRAY2BGR); return; } case V4L2_PIX_FMT_Y10: { - cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start); - cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 4); + cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start); + cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 4); cv::cvtColor(temp, destination, COLOR_GRAY2BGR); return; } case V4L2_PIX_FMT_GREY: - cv::cvtColor(cv::Mat(imageSize, CV_8UC1, currentBuffer.start), destination, COLOR_GRAY2BGR); + cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_GRAY2BGR); break; case V4L2_PIX_FMT_XBGR32: case V4L2_PIX_FMT_ABGR32: - cv::cvtColor(cv::Mat(imageSize, CV_8UC4, currentBuffer.start), destination, COLOR_BGRA2BGR); + cv::cvtColor(cv::Mat(imageSize, CV_8UC4, start), destination, COLOR_BGRA2BGR); break; case V4L2_PIX_FMT_BGR24: default: - memcpy((char *)frame.imageData, (char *)currentBuffer.start, - std::min(frame.imageSize, (int)currentBuffer.buffer.bytesused)); + memcpy((char *)frame.imageData, start, + std::min(frame.imageSize, (int)currentBuffer.bytesused)); break; } } @@ -1904,9 +2031,15 @@ double CvCaptureCAM_V4L::getProperty(int property_id) const { switch (property_id) { case cv::CAP_PROP_FRAME_WIDTH: - return form.fmt.pix.width; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) + return form.fmt.pix_mp.width; + else + return form.fmt.pix.width; case cv::CAP_PROP_FRAME_HEIGHT: - return form.fmt.pix.height; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) + return form.fmt.pix_mp.height; + else + return form.fmt.pix.height; case cv::CAP_PROP_FOURCC: return palette; case cv::CAP_PROP_FORMAT: @@ -1922,7 +2055,7 @@ double CvCaptureCAM_V4L::getProperty(int property_id) const case cv::CAP_PROP_FPS: { v4l2_streamparm sp = v4l2_streamparm(); - sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sp.type = type; if (!tryIoctl(VIDIOC_G_PARM, &sp)) { CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): Unable to get camera FPS"); return -1; @@ -2063,9 +2196,14 @@ void CvCaptureCAM_V4L::releaseBuffers() { releaseFrame(); - if (buffers[MAX_V4L_BUFFERS].start) { - free(buffers[MAX_V4L_BUFFERS].start); - buffers[MAX_V4L_BUFFERS].start = 0; + if (buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start) { + free(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start); + buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start = 0; + } + + if (buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start) { + free(buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start); + buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start = 0; } bufferIndex = -1; @@ -2076,11 +2214,14 @@ void CvCaptureCAM_V4L::releaseBuffers() v4l_buffersRequested = false; for (unsigned int n_buffers = 0; n_buffers < MAX_V4L_BUFFERS; ++n_buffers) { - if (buffers[n_buffers].start) { - if (-1 == munmap(buffers[n_buffers].start, buffers[n_buffers].length)) { - CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed munmap(): errno=" << errno << " (" << strerror(errno) << ")"); - } else { - buffers[n_buffers].start = 0; + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + if (buffers[n_buffers].memories[n_planes].start) { + if (-1 == munmap(buffers[n_buffers].memories[n_planes].start, + buffers[n_buffers].memories[n_planes].length)) { + CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed munmap(): errno=" << errno << " (" << strerror(errno) << ")"); + } else { + buffers[n_buffers].memories[n_planes].start = 0; + } } } } @@ -2100,7 +2241,6 @@ bool CvCaptureCAM_V4L::streaming(bool startStream) return !startStream; } - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; bool result = tryIoctl(startStream ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type); if (result) { @@ -2133,13 +2273,26 @@ IplImage *CvCaptureCAM_V4L::retrieveFrame(int) } else { // for mjpeg streams the size might change in between, so we have to change the header // We didn't allocate memory when not convert_rgb, but we have to recreate the header - CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): buffer input size=" << currentBuffer.buffer.bytesused); - if (frame.imageSize != (int)currentBuffer.buffer.bytesused) + CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): buffer input size=" << currentBuffer.bytesused); + if (frame.imageSize != (int)currentBuffer.bytesused) v4l2_create_frame(); - frame.imageData = (char *)buffers[MAX_V4L_BUFFERS].start; - memcpy(buffers[MAX_V4L_BUFFERS].start, currentBuffer.start, - std::min(buffers[MAX_V4L_BUFFERS].length, (size_t)currentBuffer.buffer.bytesused)); + frame.imageData = (char *)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + __u32 offset = 0; + for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) { + __u32 data_offset, bytesused; + data_offset = currentBuffer.planes[n_planes].data_offset; + bytesused = currentBuffer.planes[n_planes].bytesused - data_offset; + memcpy((unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start + offset, + (char *)currentBuffer.memories[n_planes].start + data_offset, + std::min(currentBuffer.memories[n_planes].length, (size_t)bytesused)); + offset += bytesused; + } + } else { + memcpy(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start, currentBuffer.memories[MEMORY_ORIG].start, + std::min(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, (size_t)currentBuffer.buffer.bytesused)); + } } //Revert buffer to the queue if (!tryIoctl(VIDIOC_QBUF, &buffers[bufferIndex].buffer)) From a1d752bfc041ad126ad78fe7fa9e412fda6c44e6 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Wed, 24 Aug 2022 17:30:32 +0100 Subject: [PATCH 031/313] OneVPL fixes --- modules/gapi/samples/onevpl_infer_single_roi.cpp | 4 ++-- .../onevpl/accelerators/accel_policy_va_api.cpp | 16 +++++++++------- .../onevpl/cfg_param_device_selector.cpp | 5 ++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/gapi/samples/onevpl_infer_single_roi.cpp b/modules/gapi/samples/onevpl_infer_single_roi.cpp index 4734b12f3f..811008b168 100644 --- a/modules/gapi/samples/onevpl_infer_single_roi.cpp +++ b/modules/gapi/samples/onevpl_infer_single_roi.cpp @@ -499,11 +499,11 @@ int main(int argc, char *argv[]) { std::tie(dx11_dev, dx11_ctx) = create_device_with_ctx(intel_adapter.get()); gpu_accel_device = cv::util::make_optional( cv::gapi::wip::onevpl::create_dx11_device( - reinterpret_cast(dx11_dev.get()), + reinterpret_cast(dx11_dev.release()), "GPU")); gpu_accel_ctx = cv::util::make_optional( cv::gapi::wip::onevpl::create_dx11_context( - reinterpret_cast(dx11_ctx.get()))); + reinterpret_cast(dx11_ctx.release()))); #endif // HAVE_D3D11 #endif // HAVE_DIRECTX #ifdef __linux__ diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp index ca5f1de94c..0ded066137 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp @@ -42,7 +42,14 @@ VPLVAAPIAccelerationPolicy::VPLVAAPIAccelerationPolicy(device_selector_ptr_t sel GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) } +#else // __linux__ +VPLVAAPIAccelerationPolicy::VPLVAAPIAccelerationPolicy(device_selector_ptr_t selector) : + VPLAccelerationPolicy(selector) { + GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); +} +#endif // __linux__ +#if defined(HAVE_VA) || defined(HAVE_VA_INTEL) VPLVAAPIAccelerationPolicy::~VPLVAAPIAccelerationPolicy() { vaTerminate(va_handle); GAPI_LOG_INFO(nullptr, "destroyed"); @@ -90,12 +97,7 @@ cv::MediaFrame::AdapterPtr VPLVAAPIAccelerationPolicy::create_frame_adapter(pool return cpu_dispatcher->create_frame_adapter(key, params); } -#else // __linux__ - -VPLVAAPIAccelerationPolicy::VPLVAAPIAccelerationPolicy(device_selector_ptr_t selector) : - VPLAccelerationPolicy(selector) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); -} +#else // defined(HAVE_VA) || defined(HAVE_VA_INTEL) VPLVAAPIAccelerationPolicy::~VPLVAAPIAccelerationPolicy() = default; @@ -128,7 +130,7 @@ cv::MediaFrame::AdapterPtr VPLVAAPIAccelerationPolicy::create_frame_adapter(pool const FrameConstructorArgs &) { GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); } -#endif // __linux__ +#endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp index 68d4fce1b9..f4f89a196a 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp @@ -31,12 +31,11 @@ #endif // HAVE_DIRECTX #ifdef __linux__ +#include +#include #if defined(HAVE_VA) || defined(HAVE_VA_INTEL) #include "va/va.h" #include "va/va_drm.h" - -#include -#include #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) #endif // __linux__ From 2dd3408caa7ad66781837107caeddd634189cc51 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Thu, 25 Aug 2022 14:27:18 +0300 Subject: [PATCH 032/313] change resize interpolation to enable tests in arm, disable close_5 --- modules/objdetect/test/test_qrcode.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index 0f50b77b36..62bec2fc68 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -3,6 +3,7 @@ // of this distribution and at http://opencv.org/license.html. #include "test_precomp.hpp" +#include "opencv2/imgproc.hpp" namespace opencv_test { namespace { @@ -16,8 +17,9 @@ std::string qrcode_images_name[] = { // version_5_right.jpg DISABLED after tile fix, PR #22025 }; +// Todo: fix corner align in big QRs to enable close_5.png std::string qrcode_images_close[] = { - "close_1.png", "close_2.png", "close_3.png", "close_4.png", "close_5.png" + "close_1.png", "close_2.png", "close_3.png", "close_4.png"//, "close_5.png" }; std::string qrcode_images_monitor[] = { "monitor_1.png", "monitor_2.png", "monitor_3.png", "monitor_4.png", "monitor_5.png" @@ -87,7 +89,7 @@ TEST(Objdetect_QRCode_Close, generate_test_data) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); #ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); @@ -125,7 +127,7 @@ TEST(Objdetect_QRCode_Monitor, generate_test_data) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); #ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); @@ -313,7 +315,7 @@ TEST_P(Objdetect_QRCode_Close, regression) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); std::vector corners; std::string decoded_info; QRCodeDetector qrcode; @@ -380,7 +382,7 @@ TEST_P(Objdetect_QRCode_Monitor, regression) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); std::vector corners; std::string decoded_info; QRCodeDetector qrcode; From e1305e8d05d2654d4c9edd3a0198c458b735088a Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Thu, 25 Aug 2022 18:17:03 +0300 Subject: [PATCH 033/313] Fixed a warning in case of typename on macOS ARM64 --- 3rdparty/carotene/CMakeLists.txt | 2 +- 3rdparty/carotene/src/add_weighted.cpp | 36 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/3rdparty/carotene/CMakeLists.txt b/3rdparty/carotene/CMakeLists.txt index 091990d722..ebcdf1a9f6 100644 --- a/3rdparty/carotene/CMakeLists.txt +++ b/3rdparty/carotene/CMakeLists.txt @@ -28,7 +28,7 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() if(APPLE AND CV_CLANG AND WITH_NEON) - ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-unused-function -Wno-c++11-extensions) + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-unused-function) endif() add_library(carotene_objs OBJECT EXCLUDE_FROM_ALL diff --git a/3rdparty/carotene/src/add_weighted.cpp b/3rdparty/carotene/src/add_weighted.cpp index 1f89fb5372..6559b9fe53 100644 --- a/3rdparty/carotene/src/add_weighted.cpp +++ b/3rdparty/carotene/src/add_weighted.cpp @@ -109,9 +109,9 @@ template <> struct wAdd vgamma = vdupq_n_f32(_gamma + 0.5); } - void operator() (const typename VecTraits::vec128 & v_src0, - const typename VecTraits::vec128 & v_src1, - typename VecTraits::vec128 & v_dst) const + void operator() (const VecTraits::vec128 & v_src0, + const VecTraits::vec128 & v_src1, + VecTraits::vec128 & v_dst) const { float32x4_t vs1 = vcvtq_f32_s32(v_src0); float32x4_t vs2 = vcvtq_f32_s32(v_src1); @@ -121,9 +121,9 @@ template <> struct wAdd v_dst = vcvtq_s32_f32(vs1); } - void operator() (const typename VecTraits::vec64 & v_src0, - const typename VecTraits::vec64 & v_src1, - typename VecTraits::vec64 & v_dst) const + void operator() (const VecTraits::vec64 & v_src0, + const VecTraits::vec64 & v_src1, + VecTraits::vec64 & v_dst) const { float32x2_t vs1 = vcvt_f32_s32(v_src0); float32x2_t vs2 = vcvt_f32_s32(v_src1); @@ -153,9 +153,9 @@ template <> struct wAdd vgamma = vdupq_n_f32(_gamma + 0.5); } - void operator() (const typename VecTraits::vec128 & v_src0, - const typename VecTraits::vec128 & v_src1, - typename VecTraits::vec128 & v_dst) const + void operator() (const VecTraits::vec128 & v_src0, + const VecTraits::vec128 & v_src1, + VecTraits::vec128 & v_dst) const { float32x4_t vs1 = vcvtq_f32_u32(v_src0); float32x4_t vs2 = vcvtq_f32_u32(v_src1); @@ -165,9 +165,9 @@ template <> struct wAdd v_dst = vcvtq_u32_f32(vs1); } - void operator() (const typename VecTraits::vec64 & v_src0, - const typename VecTraits::vec64 & v_src1, - typename VecTraits::vec64 & v_dst) const + void operator() (const VecTraits::vec64 & v_src0, + const VecTraits::vec64 & v_src1, + VecTraits::vec64 & v_dst) const { float32x2_t vs1 = vcvt_f32_u32(v_src0); float32x2_t vs2 = vcvt_f32_u32(v_src1); @@ -197,17 +197,17 @@ template <> struct wAdd vgamma = vdupq_n_f32(_gamma + 0.5); } - void operator() (const typename VecTraits::vec128 & v_src0, - const typename VecTraits::vec128 & v_src1, - typename VecTraits::vec128 & v_dst) const + void operator() (const VecTraits::vec128 & v_src0, + const VecTraits::vec128 & v_src1, + VecTraits::vec128 & v_dst) const { float32x4_t vs1 = vmlaq_f32(vgamma, v_src0, valpha); v_dst = vmlaq_f32(vs1, v_src1, vbeta); } - void operator() (const typename VecTraits::vec64 & v_src0, - const typename VecTraits::vec64 & v_src1, - typename VecTraits::vec64 & v_dst) const + void operator() (const VecTraits::vec64 & v_src0, + const VecTraits::vec64 & v_src1, + VecTraits::vec64 & v_dst) const { float32x2_t vs1 = vmla_f32(vget_low(vgamma), v_src0, vget_low(valpha)); v_dst = vmla_f32(vs1, v_src1, vget_low(vbeta)); From 7eaec9dd227e13944c82d62c8da4e875d7aa89d6 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Fri, 26 Aug 2022 10:04:44 +0800 Subject: [PATCH 034/313] load fp16 as fp32 and align fp16 and double in onnx_graph_simplifie --- .../dnn/src/onnx/onnx_graph_simplifier.cpp | 66 ++++++++++++++++++- modules/dnn/test/test_onnx_importer.cpp | 5 ++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp index c6e54d6a92..c787c0a321 100644 --- a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp +++ b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp @@ -18,6 +18,17 @@ CV__DNN_INLINE_NS_BEGIN extern bool DNN_DIAGNOSTICS_RUN; +static int isLittleEndianCPU() +{ + int x = 7; + char *ptr = (char *)&x; + + if(ptr[0] == 0) + return 0; + else + return 1; +} + // This wrapper can behave differently for fake input nodes and real graph nodes. class ONNXNodeWrapper : public ImportNodeWrapper { @@ -767,11 +778,64 @@ Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto) Mat(sizes, CV_32FC1, val).copyTo(blob); } } + else if (datatype == opencv_onnx::TensorProto_DataType_FLOAT16) + { + // FIXME, for now, we only load FP16 Tensor as FP32 Mat, full support for FP16 is required in the future. + CV_LOG_ONCE_WARNING(NULL, "DNN: load FP16 model as FP32 model, and it takes twice the FP16 RAM requirement."); + + // ONNX saves float 16 data in two format: int32 and raw_data. + // Link: https://github.com/onnx/onnx/issues/4460#issuecomment-1224373746 + if (!tensor_proto.int32_data().empty()) + { + const int offset = isLittleEndianCPU() ? 0 : 1; + const ::google::protobuf::RepeatedField field = tensor_proto.int32_data(); + + AutoBuffer aligned_val; + size_t sz = tensor_proto.int32_data().size(); + aligned_val.allocate(sz); + float16_t* bufPtr = aligned_val.data(); + + float16_t *fp16Ptr = (float16_t *)field.data(); + for (int i = 0; i < sz; i++) + { + bufPtr[i] = fp16Ptr[i*2 + offset]; + } + Mat(sizes, CV_16FC1, bufPtr).convertTo(blob, CV_32FC1); + } + else + { + char* val = const_cast(tensor_proto.raw_data().c_str()); +#if CV_STRONG_ALIGNMENT + // Aligned pointer is required. + AutoBuffer aligned_val; + if (!isAligned(val)) + { + size_t sz = tensor_proto.raw_data().size(); + aligned_val.allocate(divUp(sz, sizeof(float16_t))); + memcpy(aligned_val.data(), val, sz); + val = (char*)aligned_val.data(); + } +#endif + Mat(sizes, CV_16FC1, val).convertTo(blob, CV_32FC1); + } + } else if (datatype == opencv_onnx::TensorProto_DataType_DOUBLE) { const ::google::protobuf::RepeatedField field = tensor_proto.double_data(); CV_Assert(!field.empty()); - Mat(sizes, CV_64FC1, (void*)field.data()).convertTo(blob, CV_32FC1); + char* val = (char *)field.data(); +#if CV_STRONG_ALIGNMENT + // Aligned pointer is required. + AutoBuffer aligned_val; + if (!isAligned(val)) + { + size_t sz = tensor_proto.raw_data().size(); + aligned_val.allocate(divUp(sz, sizeof(double))); + memcpy(aligned_val.data(), val, sz); + val = (char*)aligned_val.data(); + } +#endif + Mat(sizes, CV_64FC1, val).convertTo(blob, CV_32FC1); } else if (datatype == opencv_onnx::TensorProto_DataType_INT32) { diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 578e0442b2..eb1db3396b 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -2098,6 +2098,11 @@ TEST_P(Test_ONNX_nets, MobileNet_v2) testONNXModels("mobilenetv2", pb, default_l1, default_lInf, true); } +TEST_P(Test_ONNX_nets, MobileNet_v2_FP16) +{ + testONNXModels("mobilenetv2_fp16", npy, default_l1, default_lInf, true); +} + TEST_P(Test_ONNX_nets, LResNet100E_IR) { applyTestTag( From 7e2c8cc9f4c49a3afeee084a2f01f7bac1d9a2dc Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 26 Aug 2022 07:06:51 +0000 Subject: [PATCH 035/313] Add remaining intrinsics. --- .../opencv2/core/hal/intrin_rvv_scalable.hpp | 827 +++++++++++++++++- modules/core/test/test_intrin_utils.hpp | 443 +--------- 2 files changed, 833 insertions(+), 437 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 7452ad91ad..463c010d71 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -464,7 +464,27 @@ inline v_uint64 v_lut_pairs(const uint64* tab, const int* idx) { return v_reinte inline v_uint64 v_lut_quads(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_quads((const int64_t*)tab, idx)); } ////////////// Pack boolean //////////////////// -/* TODO */ +inline v_uint8 v_pack_b(const v_uint16& a, const v_uint16& b) +{ + return vnsrl(vset(vlmul_ext_u16m2(a),1,b), 0, VTraits::vlanes()); +} + +inline v_uint8 v_pack_b(const v_uint32& a, const v_uint32& b, + const v_uint32& c, const v_uint32& d) +{ + + return vnsrl(vnsrl(vset(vset(vset(vlmul_ext_u32m4(a),1,b),2,c),3,d), 0, VTraits::vlanes()), 0, VTraits::vlanes()); +} + +inline v_uint8 v_pack_b(const v_uint64& a, const v_uint64& b, const v_uint64& c, + const v_uint64& d, const v_uint64& e, const v_uint64& f, + const v_uint64& g, const v_uint64& h) +{ + return vnsrl(vnsrl(vnsrl( + vset(vset(vset(vset(vset(vset(vset(vlmul_ext_u64m8(a), + 1,b),2,c),3,d),4,e),5,f),6,g),7,h), + 0, VTraits::vlanes()), 0, VTraits::vlanes()), 0, VTraits::vlanes()); +} ////////////// Arithmetics ////////////// #define OPENCV_HAL_IMPL_RVV_BIN_OP(_Tpvec, ocv_intrin, rvv_intrin) \ @@ -645,7 +665,65 @@ OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int32, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_SIGNED_SHIFT_OP(v_int64, VTraits::vlanes()) ////////////// Comparison ////////////// -// TODO +#define OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, op, intrin, suffix, vl) \ +inline _Tpvec v_##op(const _Tpvec& a, const _Tpvec& b) \ +{ \ + uint64_t ones = -1; \ + return vmerge(intrin(a, b, vl), vmv_v_x_##suffix##m1(0, vl), ones, vl); \ +} + +#define OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, op, intrin, suffix, vl) \ +inline _Tpvec v_##op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + union { uint64 u; double d; } ones; ones.u = -1; \ + return _Tpvec(vfmerge(intrin(a, b, vl), vfmv_v_f_##suffix##m1(0, vl), ones.d, vl)); \ +} //TODO + +#define OPENCV_HAL_IMPL_RVV_UNSIGNED_CMP(_Tpvec, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, eq, vmseq, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, ne, vmsne, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, lt, vmsltu, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, gt, vmsgtu, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, le, vmsleu, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, ge, vmsgeu, suffix, vl) + +#define OPENCV_HAL_IMPL_RVV_SIGNED_CMP(_Tpvec, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, eq, vmseq, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, ne, vmsne, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, lt, vmslt, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, gt, vmsgt, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, le, vmsle, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_INT_CMP_OP(_Tpvec, ge, vmsge, suffix, vl) + +#define OPENCV_HAL_IMPL_RVV_FLOAT_CMP(_Tpvec, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, eq, vmfeq, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, ne, vmfne, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, lt, vmflt, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, gt, vmfgt, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, le, vmfle, suffix, vl) \ +OPENCV_HAL_IMPL_RVV_FLOAT_CMP_OP(_Tpvec, ge, vmfge, suffix, vl) + + +OPENCV_HAL_IMPL_RVV_UNSIGNED_CMP(v_uint8, u8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_CMP(v_uint16, u16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_CMP(v_uint32, u32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_UNSIGNED_CMP(v_uint64, u64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_CMP(v_int8, i8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_CMP(v_int16, i16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_CMP(v_int32, i32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_SIGNED_CMP(v_int64, i64, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_FLOAT_CMP(v_float32, f32, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_FLOAT_CMP(v_float64, f64, VTraits::vlanes()) +#endif + +inline v_float32 v_not_nan(const v_float32& a) +{ return v_eq(a, a); } + +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_not_nan(const v_float64& a) +{ return v_eq(a, a); } +#endif ////////////// Min/Max ////////////// @@ -674,6 +752,95 @@ OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_min, vfmin, VTraits::vlanes OPENCV_HAL_IMPL_RVV_BIN_FUNC(v_float64, v_max, vfmax, VTraits::vlanes()) #endif +////////////// Transpose4x4 ////////////// +#define OPENCV_HAL_IMPL_RVV_ZIP4(_Tpvec, _wTpvec, suffix, convert2u, convert) \ +inline void v_zip4(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) { \ + int vl = 4; \ + _wTpvec temp = vreinterpret_##suffix##m2(convert2u( \ + vor(vzext_vf2(convert(a0), vl), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(convert(a1), vl)), 0, vl*2)), \ + vl))); \ + b0 = vget_##suffix##m1(temp, 0); \ + b1 = vget_##suffix##m1(vrgather(temp, vadd(vid_v_u32m2(vl), 4, vl)/*{4,5,6,7} */, vl) ,0); \ +} + +OPENCV_HAL_IMPL_RVV_ZIP4(v_uint32, vuint32m2_t, u32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_ZIP4(v_int32, vint32m2_t, i32, vreinterpret_u32m2, vreinterpret_u32m1) +OPENCV_HAL_IMPL_RVV_ZIP4(v_float32, vfloat32m2_t, f32, vreinterpret_u32m2, vreinterpret_u32m1) + +#if 0 +// this is v_zip4 and v_tranpose4x4 for scalable VLEN, costs more instruction than current 128-bit only version. +inline void v_zip4(const v_float32& a0, const v_float32& a1, v_float32& b0, v_float32& b1) { + vuint64m1_t vid1 = vid_v_u64m1(VTraits::vlanes()); + vuint16m1_t t1 = vreinterpret_u16m1(vid1); + vuint16m1_t t2 = vslide1up(t1, 0, VTraits::vlanes()); + vuint16m1_t t3 = vslide1up(t2, 0, VTraits::vlanes()); + vuint16m1_t t4 = vslide1up(t3, 0, VTraits::vlanes()); + t1 = vor( + vor(t1, t2, VTraits::vlanes()), + vor(t3, t4, VTraits::vlanes()), + VTraits::vlanes() + ); + vuint32m2_t vidx0 = vwmulu(t1, 4, VTraits::vlanes()); + vidx0 = vadd(vidx0, vid_v_u32m2(VTraits::vlanes()), VTraits::vlanes()); + vuint32m2_t vidx1 = vadd(vidx0, 4, VTraits::vlanes()); + vfloat32m2_t temp = vreinterpret_f32m2(vreinterpret_u32m2( + vor(vzext_vf2(vreinterpret_u32m1(a0), VTraits::vlanes()), + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(vreinterpret_u32m1(a1), VTraits::vlanes())), 0, VTraits::vlanes()*2)), + VTraits::vlanes()))); + b0 = vlmul_trunc_f32m1(vrgather(temp, vidx0, VTraits::vlanes())); + b1 = vlmul_trunc_f32m1(vrgather(temp, vidx1, VTraits::vlanes())); +} + +inline void v_transpose4x4(const v_float32& a0, const v_float32& a1, const v_float32& a2, const v_float32& a3,\ + v_float32& b0, v_float32& b1, v_float32& b2, v_float32& b3) { \ + vuint64m2_t vid1 = vid_v_u64m2(VTraits::vlanes()); + vuint16m2_t t1 = vreinterpret_u16m2(vid1); + vuint16m2_t t2 = vslide1up(t1, 0, VTraits::vlanes()); + vuint16m2_t t3 = vslide1up(t2, 0, VTraits::vlanes()); + vuint16m2_t t4 = vslide1up(t3, 0, VTraits::vlanes()); + t1 = vor( + vor(t1, t2, VTraits::vlanes()), + vor(t3, t4, VTraits::vlanes()), + VTraits::vlanes() + ); + vuint16m2_t vidx0 = vmul(t1, 12, VTraits::vlanes()); + vidx0 = vadd(vidx0, vid_v_u16m2(VTraits::vlanes()), VTraits::vlanes()); + vuint16m2_t vidx1 = vadd(vidx0, 4, VTraits::vlanes()); + vuint16m2_t vidx2 = vadd(vidx0, 8, VTraits::vlanes()); + vuint16m2_t vidx3 = vadd(vidx0, 12, VTraits::vlanes()); + vuint32m2_t tempA = vreinterpret_u32m2( \ + vor(vzext_vf2(vreinterpret_u32m1(a0), VTraits::vlanes()), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(vreinterpret_u32m1(a2), VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes())); \ + vuint32m2_t tempB = vreinterpret_u32m2( \ + vor(vzext_vf2(vreinterpret_u32m1(a1), VTraits::vlanes()), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(vreinterpret_u32m1(a3), VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes())); \ + vfloat32m4_t temp = vreinterpret_f32m4(vreinterpret_u32m4( \ + vor(vzext_vf2(tempA, VTraits::vlanes()), \ + vreinterpret_u64m4(vslide1up(vreinterpret_u32m4(vzext_vf2(tempB, VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes()))); \ + b0 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx0, VTraits::vlanes())); + b1 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx1, VTraits::vlanes())); + b2 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx2, VTraits::vlanes())); + b3 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx3, VTraits::vlanes())); +} +#endif + +#define OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, const _Tpvec& a2, const _Tpvec& a3, _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) { \ + _Tpvec t0,t1,t2,t3; \ + v_zip4(a0, a2, t0, t2); \ + v_zip4(a1, a3, t1, t3); \ + v_zip4(t0, t1, b0, b1); \ + v_zip4(t2, t3, b2, b3); \ +} + +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_uint32, u32) +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_int32, i32) +OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_float32, f32) + ////////////// Reduce ////////////// #define OPENCV_HAL_IMPL_RVV_REDUCE_SUM(_Tpvec, _wTpvec, _nwTpvec, scalartype, wsuffix, vl, red) \ @@ -690,6 +857,9 @@ OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint16, v_uint32, vuint32m1_t, unsigned, u32, V OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int16, v_int32, vint32m1_t, int, i32, VTraits::vlanes(), wredsum) OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint32, v_uint64, vuint64m1_t, unsigned, u64, VTraits::vlanes(), wredsumu) OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int32, v_int64, vint64m1_t, int, i64, VTraits::vlanes(), wredsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_uint64, v_uint64, vuint64m1_t, uint64, u64, VTraits::vlanes(), redsum) +OPENCV_HAL_IMPL_RVV_REDUCE_SUM(v_int64, v_int64, vint64m1_t, int64, i64, VTraits::vlanes(), redsum) + #define OPENCV_HAL_IMPL_RVV_REDUCE_SUM_FP(_Tpvec, _wTpvec, _nwTpvec, scalartype, wsuffix, vl) \ inline scalartype v_reduce_sum(const _Tpvec& a) \ @@ -723,7 +893,56 @@ OPENCV_HAL_IMPL_RVV_REDUCE(v_uint32, max, unsigned, u32, VTraits::vlan OPENCV_HAL_IMPL_RVV_REDUCE(v_int32, max, int, i32, VTraits::vlanes(), redmax) OPENCV_HAL_IMPL_RVV_REDUCE(v_float32, max, float, f32, VTraits::vlanes(), fredmax) -//TODO: v_reduce_sum4 +inline v_float32 v_reduce_sum4(const v_float32& a, const v_float32& b, + const v_float32& c, const v_float32& d) +{ + // 0000 1111 2222 3333 .... + vuint64m2_t vid1 = vid_v_u64m2(VTraits::vlanes()); + vuint16m2_t t1 = vreinterpret_u16m2(vid1); + vuint16m2_t t2 = vslide1up(t1, 0, VTraits::vlanes()); + vuint16m2_t t3 = vslide1up(t2, 0, VTraits::vlanes()); + vuint16m2_t t4 = vslide1up(t3, 0, VTraits::vlanes()); + t1 = vor( + vor(t1, t2, VTraits::vlanes()), + vor(t3, t4, VTraits::vlanes()), + VTraits::vlanes() + ); + + // index for transpose4X4 + vuint16m2_t vidx0 = vmul(t1, 12, VTraits::vlanes()); + vidx0 = vadd(vidx0, vid_v_u16m2(VTraits::vlanes()), VTraits::vlanes()); + vuint16m2_t vidx1 = vadd(vidx0, 4, VTraits::vlanes()); + vuint16m2_t vidx2 = vadd(vidx0, 8, VTraits::vlanes()); + vuint16m2_t vidx3 = vadd(vidx0, 12, VTraits::vlanes()); + + // zip + vuint32m2_t tempA = vreinterpret_u32m2( \ + vor(vzext_vf2(vreinterpret_u32m1(a), VTraits::vlanes()), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(vreinterpret_u32m1(c), VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes())); \ + vuint32m2_t tempB = vreinterpret_u32m2( \ + vor(vzext_vf2(vreinterpret_u32m1(b), VTraits::vlanes()), \ + vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(vreinterpret_u32m1(d), VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes())); \ + vfloat32m4_t temp = vreinterpret_f32m4(vreinterpret_u32m4( \ + vor(vzext_vf2(tempA, VTraits::vlanes()), \ + vreinterpret_u64m4(vslide1up(vreinterpret_u32m4(vzext_vf2(tempB, VTraits::vlanes())), 0, VTraits::vlanes())), \ + VTraits::vlanes()))); + + // transpose + vfloat32m1_t b0 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx0, VTraits::vlanes())); + vfloat32m1_t b1 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx1, VTraits::vlanes())); + vfloat32m1_t b2 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx2, VTraits::vlanes())); + vfloat32m1_t b3 = vlmul_trunc_f32m1(vrgatherei16(temp, vidx3, VTraits::vlanes())); + + // vector add + v_float32 res = vfadd( + vfadd(b0, b1, VTraits::vlanes()), + vfadd(b2, b3, VTraits::vlanes()), + VTraits::vlanes() + ); + return res; +} ////////////// Square-Root ////////////// @@ -1003,7 +1222,47 @@ OPENCV_HAL_IMPL_RVV_ROTATE_FP(v_float64, f64, VTraits::vlanes()) #endif ////////////// Convert to float ////////////// -// TODO +inline v_float32 v_cvt_f32(const v_int32& a) +{ + return vfcvt_f_x_v_f32m1(a, VTraits::vlanes()); +} + +#if CV_SIMD_SCALABLE_64F +inline v_float32 v_cvt_f32(const v_float64& a) +{ + return vfncvt_f(vlmul_ext_f64m2(a), VTraits::vlanes()); +} + +inline v_float32 v_cvt_f32(const v_float64& a, const v_float64& b) +{ + return vfncvt_f(vset(vlmul_ext_f64m2(a),1,b), VTraits::vlanes()); +} + +inline v_float64 v_cvt_f64(const v_int32& a) +{ + return vget_f64m1(vfwcvt_f(a, VTraits::vlanes()), 0); +} + +inline v_float64 v_cvt_f64_high(const v_int32& a) +{ + return vget_f64m1(vfwcvt_f(a, VTraits::vlanes()), 1); +} + +inline v_float64 v_cvt_f64(const v_float32& a) +{ + return vget_f64m1(vfwcvt_f(a, VTraits::vlanes()), 0); +} + +inline v_float64 v_cvt_f64_high(const v_float32& a) +{ + return vget_f64m1(vfwcvt_f(a, VTraits::vlanes()), 1); +} + +inline v_float64 v_cvt_f64(const v_int64& a) +{ + return vfcvt_f(a, VTraits::vlanes()); +} +#endif //////////// Broadcast ////////////// @@ -1083,6 +1342,56 @@ inline v_int32 v_load_expand_q(const schar* ptr) return vwcvt_x(vwcvt_x(vle8_v_i8mf4(ptr, VTraits::vlanes()), VTraits::vlanes()), VTraits::vlanes()); } +#define OPENCV_HAL_IMPL_RVV_PACK(_Tpvec, _Tp, _wTpvec, hwidth, hsuffix, suffix, rshr, shr) \ +inline _Tpvec v_pack(const _wTpvec& a, const _wTpvec& b) \ +{ \ + return shr(vset(vlmul_ext_##suffix##m2(a), 1, b), 0, VTraits<_Tpvec>::vlanes()); \ +} \ +inline void v_pack_store(_Tp* ptr, const _wTpvec& a) \ +{ \ + vse##hwidth##_v_##hsuffix##mf2(ptr, shr(a, 0, VTraits<_Tpvec>::vlanes()), VTraits<_wTpvec>::vlanes()); \ +} \ +template inline \ +_Tpvec v_rshr_pack(const _wTpvec& a, const _wTpvec& b, int N = n) \ +{ \ + return rshr(vset(vlmul_ext_##suffix##m2(a), 1, b), N, VTraits<_Tpvec>::vlanes()); \ +} \ +template inline \ +void v_rshr_pack_store(_Tp* ptr, const _wTpvec& a, int N = n) \ +{ \ + vse##hwidth##_v_##hsuffix##mf2(ptr, rshr(a, N, VTraits<_Tpvec>::vlanes()), VTraits<_wTpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_PACK(v_uint8, uchar, v_uint16, 8, u8, u16, vnclipu, vnclipu) +OPENCV_HAL_IMPL_RVV_PACK(v_int8, schar, v_int16, 8, i8, i16, vnclip, vnclip) +OPENCV_HAL_IMPL_RVV_PACK(v_uint16, ushort, v_uint32, 16, u16, u32, vnclipu, vnclipu) +OPENCV_HAL_IMPL_RVV_PACK(v_int16, short, v_int32, 16, i16, i32, vnclip, vnclip) +OPENCV_HAL_IMPL_RVV_PACK(v_uint32, unsigned, v_uint64, 32, u32, u64, vnclipu, vnsrl) +OPENCV_HAL_IMPL_RVV_PACK(v_int32, int, v_int64, 32, i32, i64, vnclip, vnsra) + +#define OPENCV_HAL_IMPL_RVV_PACK_U(_Tpvec, _Tp, _wTpvec, _wTp, hwidth, width, hsuffix, suffix, rshr, cast, hvl, vl) \ +inline _Tpvec v_pack_u(const _wTpvec& a, const _wTpvec& b) \ +{ \ + return vnclipu(cast(vmax(vset(vlmul_ext_##suffix##m2(a), 1, b), 0, vl)), 0, vl); \ +} \ +inline void v_pack_u_store(_Tp* ptr, const _wTpvec& a) \ +{ \ + vse##hwidth##_v_##hsuffix##mf2(ptr, vnclipu(vreinterpret_u##width##m1(vmax(a, 0, vl)), 0, vl), hvl); \ +} \ +template inline \ +_Tpvec v_rshr_pack_u(const _wTpvec& a, const _wTpvec& b, int n = N) \ +{ \ + return vnclipu(cast(vmax(vset(vlmul_ext_##suffix##m2(a), 1, b), 0, vl)), n, vl); \ +} \ +template inline \ +void v_rshr_pack_u_store(_Tp* ptr, const _wTpvec& a, int n = N) \ +{ \ + vse##hwidth##_v_##hsuffix##mf2(ptr, vnclipu(vreinterpret_u##width##m1(vmax(a, 0, vl)), n, vl), hvl); \ +} + +OPENCV_HAL_IMPL_RVV_PACK_U(v_uint8, uchar, v_int16, short, 8, 16, u8, i16, vnclipu_wx_u8m1, vreinterpret_v_i16m2_u16m2, VTraits::vlanes(), VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_PACK_U(v_uint16, ushort, v_int32, int, 16, 32, u16, i32, vnclipu_wx_u16m1, vreinterpret_v_i32m2_u32m2, VTraits::vlanes(), VTraits::vlanes()) + /* void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) a0 = {A1 A2 A3 A4} @@ -1138,6 +1447,63 @@ OPENCV_HAL_IMPL_RVV_UNPACKS(v_float32, 32) OPENCV_HAL_IMPL_RVV_UNPACKS(v_float64, 64) #endif +#define OPENCV_HAL_IMPL_RVV_INTERLEAVED(_Tpvec, _Tp, suffix, width, hwidth, vl) \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \ +{ \ + a = vlse##width##_v_##suffix##m1(ptr , sizeof(_Tp)*2, VTraits::vlanes()); \ + b = vlse##width##_v_##suffix##m1(ptr+1, sizeof(_Tp)*2, VTraits::vlanes()); \ +}\ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \ +{ \ + a = vlse##width##_v_##suffix##m1(ptr , sizeof(_Tp)*3, VTraits::vlanes()); \ + b = vlse##width##_v_##suffix##m1(ptr+1, sizeof(_Tp)*3, VTraits::vlanes()); \ + c = vlse##width##_v_##suffix##m1(ptr+2, sizeof(_Tp)*3, VTraits::vlanes()); \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \ + v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + \ + a = vlse##width##_v_##suffix##m1(ptr , sizeof(_Tp)*4, VTraits::vlanes()); \ + b = vlse##width##_v_##suffix##m1(ptr+1, sizeof(_Tp)*4, VTraits::vlanes()); \ + c = vlse##width##_v_##suffix##m1(ptr+2, sizeof(_Tp)*4, VTraits::vlanes()); \ + d = vlse##width##_v_##suffix##m1(ptr+3, sizeof(_Tp)*4, VTraits::vlanes()); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vsse##width(ptr, sizeof(_Tp)*2, a, VTraits::vlanes()); \ + vsse##width(ptr+1, sizeof(_Tp)*2, b, VTraits::vlanes()); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vsse##width(ptr, sizeof(_Tp)*3, a, VTraits::vlanes()); \ + vsse##width(ptr+1, sizeof(_Tp)*3, b, VTraits::vlanes()); \ + vsse##width(ptr+2, sizeof(_Tp)*3, c, VTraits::vlanes()); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, const v_##_Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + vsse##width(ptr, sizeof(_Tp)*4, a, VTraits::vlanes()); \ + vsse##width(ptr+1, sizeof(_Tp)*4, b, VTraits::vlanes()); \ + vsse##width(ptr+2, sizeof(_Tp)*4, c, VTraits::vlanes()); \ + vsse##width(ptr+3, sizeof(_Tp)*4, d, VTraits::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint8, uchar, u8, 8, 4, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(int8, schar, i8, 8, 4, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint16, ushort, u16, 16, 8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(int16, short, i16, 16, 8, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint32, unsigned, u32, 32, 16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(int32, int, i32, 32, 16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(float32, float, f32, 32, 16, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint64, uint64, u64, 64, 32, VTraits::vlanes()) +OPENCV_HAL_IMPL_RVV_INTERLEAVED(int64, int64, i64, 64, 32, VTraits::vlanes()) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_INTERLEAVED(float64, double, f64, 64, 32, VTraits::vlanes()) +#endif + static uint64_t idx_interleave_pairs[] = { \ 0x0705060403010200, 0x0f0d0e0c0b090a08, 0x1715161413111210, 0x1f1d1e1c1b191a18, \ 0x2725262423212220, 0x2f2d2e2c2b292a28, 0x3735363433313230, 0x3f3d3e3c3b393a38, \ @@ -1182,37 +1548,69 @@ OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_uint32, 32, vzext_vf4, quads) OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_int32, 32, vzext_vf4, quads) OPENCV_HAL_IMPL_RVV_INTERLEAVED_PQ(v_float32, 32, vzext_vf4, quads) -////////////// Transpose4x4 ////////////// -#define OPENCV_HAL_IMPL_RVV_ZIP4(_Tpvec, _wTpvec, suffix, convert2u, convert) \ -static inline void v_zip4(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) { \ - int vl = 4; \ - _wTpvec temp = vreinterpret_##suffix##m2(convert2u( \ - vor(vzext_vf2(convert(a0), vl), \ - vreinterpret_u64m2(vslide1up(vreinterpret_u32m2(vzext_vf2(convert(a1), vl)), 0, vl*2)), \ - vl))); \ - b0 = vget_##suffix##m1(temp, 0); \ - b1 = vget_##suffix##m1(vrgather(temp, vadd(vid_v_u32m2(vl), 4, vl)/*{4,5,6,7} */, vl) ,0); \ -} - -OPENCV_HAL_IMPL_RVV_ZIP4(v_uint32, vuint32m2_t, u32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) -OPENCV_HAL_IMPL_RVV_ZIP4(v_int32, vint32m2_t, i32, vreinterpret_u32m2, vreinterpret_u32m1) -OPENCV_HAL_IMPL_RVV_ZIP4(v_float32, vfloat32m2_t, f32, vreinterpret_u32m2, vreinterpret_u32m1) - -#define OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(_Tpvec, suffix) \ -inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, const _Tpvec& a2, const _Tpvec& a3, _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) { \ - _Tpvec t0,t1,t2,t3= vundefined_##suffix##m1(); \ - v_zip4(a0, a2, t0, t2); \ - v_zip4(a1, a3, t1, t3); \ - v_zip4(t0, t1, b0, b1); \ - v_zip4(t2, t3, b2, b3); \ -} - -OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_uint32, u32) -OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_int32, i32) -OPENCV_HAL_IMPL_RVV_TRANSPOSE4x4(v_float32, f32) - //////////// PopCount ////////// -// TODO +static const unsigned char popCountTable[256] = +{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; +#define OPENCV_HAL_IMPL_RVV_HADD(_Tpvec, _Tpvec2, _Tm2, width, width2, suffix, add) \ +static inline _Tpvec2 v_hadd(_Tpvec a) { \ + vuint##width2##m1_t oneX2 = vmv_v_x_u##width2##m1(1, VTraits::vlanes()); \ + vuint##width##m1_t one = vreinterpret_u##width##m1(oneX2); \ + _Tm2 res = add(a, vslide1down(a, 0, VTraits::vlanes()), VTraits::vlanes()); \ + return vget_##suffix##m1(vcompress(vmseq(one, 1, VTraits::vlanes()), res, res, VTraits::vlanes()), 0); \ +} +OPENCV_HAL_IMPL_RVV_HADD(v_uint8, v_uint16, vuint16m2_t, 8, 16, u16, vwaddu_vv) +OPENCV_HAL_IMPL_RVV_HADD(v_uint16, v_uint32, vuint32m2_t, 16, 32, u32, vwaddu_vv) +OPENCV_HAL_IMPL_RVV_HADD(v_uint32, v_uint64, vuint64m2_t, 32, 64, u64, vwaddu_vv) +OPENCV_HAL_IMPL_RVV_HADD(v_int8, v_int16, vint16m2_t, 8, 16, i16, vwadd_vv) +OPENCV_HAL_IMPL_RVV_HADD(v_int16, v_int32, vint32m2_t, 16, 32, i32, vwadd_vv) +OPENCV_HAL_IMPL_RVV_HADD(v_int32, v_int64, vint64m2_t, 32, 64, i64, vwadd_vv) + +OPENCV_HAL_IMPL_RVV_HADD(vint32m2_t, v_int32, vint32m2_t, 16, 32, i32, vadd) +OPENCV_HAL_IMPL_RVV_HADD(vint64m2_t, v_int64, vint64m2_t, 32, 64, i64, vadd) + +inline v_uint8 v_popcount(const v_uint8& a) +{ + return vloxei8(popCountTable, a, VTraits::vlanes()); +} +inline v_uint16 v_popcount(const v_uint16& a) +{ + return v_hadd(v_popcount(vreinterpret_u8m1(a))); +} +inline v_uint32 v_popcount(const v_uint32& a) +{ + return v_hadd(v_hadd(v_popcount(vreinterpret_u8m1(a)))); +} + +inline v_uint8 v_popcount(const v_int8& a) +{ + return v_popcount(v_abs(a));\ +} +inline v_uint16 v_popcount(const v_int16& a) +{ + return v_popcount(v_abs(a));\ +} +inline v_uint32 v_popcount(const v_int32& a) +{ + return v_popcount(v_abs(a));\ +} + //////////// SignMask //////////// #define OPENCV_HAL_IMPL_RVV_SIGNMASK_OP(_Tpvec) \ @@ -1264,25 +1662,372 @@ inline int v_scan_forward(const v_float64& a) #endif //////////// Pack triplets //////////// -// TODO +// {A0, A1, A2, A3, B0, B1, B2, B3, C0 ...} --> {A0, A1, A2, B0, B1, B2, C0 ...} +// mask: {0,0,0,1, ...} -> {T,T,T,F, ...} +#define OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(_Tpvec, v_trunc) \ +inline _Tpvec v_pack_triplets(const _Tpvec& vec) { \ + size_t vl = vsetvlmax_e8m1(); \ + vuint32m1_t one = vmv_v_x_u32m1(1, vl/4); \ + vuint8m1_t zero = vmv_v_x_u8m1(0, vl); \ + vuint8m1_t mask = vreinterpret_u8m1(one); \ + return vcompress(vmseq(v_trunc(vslideup(zero, mask, 3, vl)), 0, vl), vec, vec, VTraits<_Tpvec>::vlanes()); \ +} + +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_uint8, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_int8, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_uint16, vlmul_trunc_u8mf2) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_int16, vlmul_trunc_u8mf2) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_uint32, vlmul_trunc_u8mf4) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_int32, vlmul_trunc_u8mf4) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_float32, vlmul_trunc_u8mf4) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_uint64, vlmul_trunc_u8mf8) +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_int64, vlmul_trunc_u8mf8) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_float64, vlmul_trunc_u8mf8) +#endif ////// FP16 support /////// +#if __riscv_zfh inline v_float32 v_load_expand(const float16_t* ptr) { - // TODO - return vundefined_f32m1(); + return vfwcvt_f(vle16_v_f16mf2((_Float16*)ptr, VTraits::vlanes()) ,VTraits::vlanes());; } +inline void v_pack_store(float16_t* ptr, const v_float32& v) +{ + vse16_v_f16mf2((_Float16*)ptr, vfncvt_f_f_w_f16mf2(v, VTraits::vlanes()), VTraits::vlanes()); +} +#else +inline v_float32 v_load_expand(const float16_t* ptr) +{ + float buf[32]; + for( int i = 0; i < VTraits::vlanes(); i++ ) buf[i] = (float)ptr[i]; + return v_load(buf); +} + +inline void v_pack_store(float16_t* ptr, const v_float32& v) +{ + float buf[32]; + v_store(buf, v); + for( int i = 0; i < VTraits::vlanes(); i++ ) ptr[i] = float16_t(buf[i]); +} +#endif ////////////// Rounding ////////////// -// TODO +inline v_int32 v_round(const v_float32& a) +{ + // return vfcvt_x(vfadd(a, 1e-6, VTraits::vlanes()), VTraits::vlanes()); + return vfcvt_x(a, VTraits::vlanes()); +} + +inline v_int32 v_floor(const v_float32& a) +{ + return vfcvt_x(vfsub(a, 0.5f - 1e-5, VTraits::vlanes()), VTraits::vlanes()); + // return vfcvt_x(a, VTraits::vlanes()); +} + +inline v_int32 v_ceil(const v_float32& a) +{ + return vfcvt_x(vfadd(a, 0.5f - 1e-5, VTraits::vlanes()), VTraits::vlanes()); +} + +inline v_int32 v_trunc(const v_float32& a) +{ + return vfcvt_rtz_x(a, VTraits::vlanes()); +} +#if CV_SIMD_SCALABLE_64F +inline v_int32 v_round(const v_float64& a) +{ + return vfncvt_x(vlmul_ext_f64m2(vfadd(a, 1e-6, VTraits::vlanes())), VTraits::vlanes()); +} + +inline v_int32 v_round(const v_float64& a, const v_float64& b) +{ + return vfncvt_x(vset(vlmul_ext_f64m2(vfadd(a, 1e-6, VTraits::vlanes())), 1, b), VTraits::vlanes()); +} + +inline v_int32 v_floor(const v_float64& a) +{ + return vfncvt_x(vlmul_ext_f64m2(vfsub(a, 0.5f - 1e-6, VTraits::vlanes())), VTraits::vlanes()); +} + +inline v_int32 v_ceil(const v_float64& a) +{ + return vfncvt_x(vlmul_ext_f64m2(vfadd(a, 0.5f - 1e-6, VTraits::vlanes())), VTraits::vlanes()); +} + +inline v_int32 v_trunc(const v_float64& a) +{ + return vfncvt_rtz_x(vlmul_ext_f64m2(a), VTraits::vlanes()); +} +#endif //////// Dot Product //////// -// TODO + +// 16 >> 32 +inline v_int32 v_dotprod(const v_int16& a, const v_int16& b) +{ + vint32m2_t temp1 = vwmul(a, b, VTraits::vlanes()); + return v_hadd(temp1); +} + +inline v_int32 v_dotprod(const v_int16& a, const v_int16& b, const v_int32& c) +{ + vint32m2_t temp1 = vwmul(a, b, VTraits::vlanes()); + return vadd(v_hadd(temp1), c, VTraits::vlanes()); +} + +// 32 >> 64 +inline v_int64 v_dotprod(const v_int32& a, const v_int32& b) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint32m1_t one32 = vreinterpret_u32m1(one64); \ + vbool32_t mask = vmseq(one32, 1, VTraits::vlanes()); \ + vint64m2_t temp1 = vwmul(a, b, VTraits::vlanes()); \ + vint64m2_t temp2 = vslide1down(temp1, 0, VTraits::vlanes()); + vint64m2_t res = vadd(temp1, temp2, VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vlmul_trunc_i64m1(res); \ +} +inline v_int64 v_dotprod(const v_int32& a, const v_int32& b, const v_int64& c) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint32m1_t one32 = vreinterpret_u32m1(one64); \ + vbool32_t mask = vmseq(one32, 1, VTraits::vlanes()); \ + vint64m2_t temp1 = vwmul(a, b, VTraits::vlanes()); \ + vint64m2_t temp2 = vslide1down(temp1, 0, VTraits::vlanes()); + vint64m2_t res = vadd(temp1, temp2, VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vadd(vlmul_trunc_i64m1(res), c, VTraits::vlanes()); \ +} + +// 8 >> 32 +inline v_uint32 v_dotprod_expand(const v_uint8& a, const v_uint8& b) +{ + vuint32m1_t one32 = vmv_v_x_u32m1(1, VTraits::vlanes()); \ + vuint8m1_t one8 = vreinterpret_u8m1(one32); \ + vbool8_t mask = vmseq(one8, 1, VTraits::vlanes()); \ + vuint16m2_t t0 = vwmulu(a, b, VTraits::vlanes()); \ + vuint16m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vuint16m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vuint16m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vuint32m4_t res = vadd(vwaddu_vv(t2, t3, VTraits::vlanes()), vwaddu_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vlmul_trunc_u32m1(res); +} + +inline v_uint32 v_dotprod_expand(const v_uint8& a, const v_uint8& b, + const v_uint32& c) +{ + vuint32m1_t one32 = vmv_v_x_u32m1(1, VTraits::vlanes()); \ + vuint8m1_t one8 = vreinterpret_u8m1(one32); \ + vbool8_t mask = vmseq(one8, 1, VTraits::vlanes()); \ + vuint16m2_t t0 = vwmulu(a, b, VTraits::vlanes()); \ + vuint16m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vuint16m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vuint16m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vuint32m4_t res = vadd(vwaddu_vv(t2, t3, VTraits::vlanes()), vwaddu_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vadd(vlmul_trunc_u32m1(res), c, VTraits::vlanes()); +} + +inline v_int32 v_dotprod_expand(const v_int8& a, const v_int8& b) +{ + vuint32m1_t one32 = vmv_v_x_u32m1(1, VTraits::vlanes()); \ + vuint8m1_t one8 = vreinterpret_u8m1(one32); \ + vbool8_t mask = vmseq(one8, 1, VTraits::vlanes()); \ + vint16m2_t t0 = vwmul(a, b, VTraits::vlanes()); \ + vint16m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vint16m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vint16m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vint32m4_t res = vadd(vwadd_vv(t2, t3, VTraits::vlanes()), vwadd_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vlmul_trunc_i32m1(res); +} + +inline v_int32 v_dotprod_expand(const v_int8& a, const v_int8& b, + const v_int32& c) +{ + vuint32m1_t one32 = vmv_v_x_u32m1(1, VTraits::vlanes()); \ + vuint8m1_t one8 = vreinterpret_u8m1(one32); \ + vbool8_t mask = vmseq(one8, 1, VTraits::vlanes()); \ + vint16m2_t t0 = vwmul(a, b, VTraits::vlanes()); \ + vint16m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vint16m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vint16m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vint32m4_t res = vadd(vwadd_vv(t2, t3, VTraits::vlanes()), vwadd_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vadd(vlmul_trunc_i32m1(res), c, VTraits::vlanes()); +} + + +// // 16 >> 64 +inline v_uint64 v_dotprod_expand(const v_uint16& a, const v_uint16& b) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint16m1_t one16 = vreinterpret_u16m1(one64); \ + vbool16_t mask = vmseq(one16, 1, VTraits::vlanes()); \ + vuint32m2_t t0 = vwmulu(a, b, VTraits::vlanes()); \ + vuint32m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vuint32m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vuint32m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vuint64m4_t res = vadd(vwaddu_vv(t2, t3, VTraits::vlanes()), vwaddu_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vlmul_trunc_u64m1(res); +} +inline v_uint64 v_dotprod_expand(const v_uint16& a, const v_uint16& b, const v_uint64& c) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint16m1_t one16 = vreinterpret_u16m1(one64); \ + vbool16_t mask = vmseq(one16, 1, VTraits::vlanes()); \ + vuint32m2_t t0 = vwmulu(a, b, VTraits::vlanes()); \ + vuint32m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vuint32m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vuint32m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vuint64m4_t res = vadd(vwaddu_vv(t2, t3, VTraits::vlanes()), vwaddu_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vadd(vlmul_trunc_u64m1(res), c, VTraits::vlanes()); +} + +inline v_int64 v_dotprod_expand(const v_int16& a, const v_int16& b) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint16m1_t one16 = vreinterpret_u16m1(one64); \ + vbool16_t mask = vmseq(one16, 1, VTraits::vlanes()); \ + vint32m2_t t0 = vwmul(a, b, VTraits::vlanes()); \ + vint32m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vint32m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vint32m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vint64m4_t res = vadd(vwadd_vv(t2, t3, VTraits::vlanes()), vwadd_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vlmul_trunc_i64m1(res); +} +inline v_int64 v_dotprod_expand(const v_int16& a, const v_int16& b, + const v_int64& c) +{ + vuint64m1_t one64 = vmv_v_x_u64m1(1, VTraits::vlanes()); \ + vuint16m1_t one16 = vreinterpret_u16m1(one64); \ + vbool16_t mask = vmseq(one16, 1, VTraits::vlanes()); \ + vint32m2_t t0 = vwmul(a, b, VTraits::vlanes()); \ + vint32m2_t t1= vslide1down(t0, 0, VTraits::vlanes()); + vint32m2_t t2= vslide1down(t1, 0, VTraits::vlanes()); + vint32m2_t t3= vslide1down(t2, 0, VTraits::vlanes()); + vint64m4_t res = vadd(vwadd_vv(t2, t3, VTraits::vlanes()), vwadd_vv(t0, t1, VTraits::vlanes()), VTraits::vlanes()); + res = vcompress(mask, res, res, VTraits::vlanes()); \ + return vadd(vlmul_trunc_i64m1(res), c, VTraits::vlanes()); +} + +// // 32 >> 64f +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_dotprod_expand(const v_int32& a, const v_int32& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64 v_dotprod_expand(const v_int32& a, const v_int32& b, + const v_float64& c) +{ return v_add(v_dotprod_expand(a, b) , c); } +#endif //////// Fast Dot Product //////// -// TODO +// 16 >> 32 +inline v_int32 v_dotprod_fast(const v_int16& a, const v_int16& b) +{ + v_int32 zero = v_setzero_s32(); + return vredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_int32 v_dotprod_fast(const v_int16& a, const v_int16& b, const v_int32& c) +{ + v_int32 zero = v_setzero_s32(); + return vredsum(zero, vwmul(a, b, VTraits::vlanes()), vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} + +// 32 >> 64 +inline v_int64 v_dotprod_fast(const v_int32& a, const v_int32& b) +{ + v_int64 zero = v_setzero_s64(); + return vredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_int64 v_dotprod_fast(const v_int32& a, const v_int32& b, const v_int64& c) +{ + v_int64 zero = v_setzero_s64(); + return vadd(vredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()) , vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} + + +// 8 >> 32 +inline v_uint32 v_dotprod_expand_fast(const v_uint8& a, const v_uint8& b) +{ + v_uint32 zero = v_setzero_u32(); + return vwredsumu(zero, vwmulu(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_uint32 v_dotprod_expand_fast(const v_uint8& a, const v_uint8& b, const v_uint32& c) +{ + v_uint32 zero = v_setzero_u32(); + return vadd(vwredsumu(zero, vwmulu(a, b, VTraits::vlanes()), zero, VTraits::vlanes()) , vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} +inline v_int32 v_dotprod_expand_fast(const v_int8& a, const v_int8& b) +{ + v_int32 zero = v_setzero_s32(); + return vwredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_int32 v_dotprod_expand_fast(const v_int8& a, const v_int8& b, const v_int32& c) +{ + v_int32 zero = v_setzero_s32(); + return vadd(vwredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()) , vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} + +// 16 >> 64 +inline v_uint64 v_dotprod_expand_fast(const v_uint16& a, const v_uint16& b) +{ + v_uint64 zero = v_setzero_u64(); + return vwredsumu(zero, vwmulu(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_uint64 v_dotprod_expand_fast(const v_uint16& a, const v_uint16& b, const v_uint64& c) +{ + v_uint64 zero = v_setzero_u64(); + return vadd(vwredsumu(zero, vwmulu(a, b, VTraits::vlanes()), zero, VTraits::vlanes()), vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} +inline v_int64 v_dotprod_expand_fast(const v_int16& a, const v_int16& b) +{ + v_int64 zero = v_setzero_s64(); + return vwredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()); +} +inline v_int64 v_dotprod_expand_fast(const v_int16& a, const v_int16& b, const v_int64& c) +{ + v_int64 zero = v_setzero_s64(); + return vadd(vwredsum(zero, vwmul(a, b, VTraits::vlanes()), zero, VTraits::vlanes()), vredsum(zero, c, zero, VTraits::vlanes()), VTraits::vlanes()); +} + +// 32 >> 64f +#if CV_SIMD_SCALABLE_64F +inline v_float64 v_dotprod_expand_fast(const v_int32& a, const v_int32& b) +{ return v_cvt_f64(v_dotprod_fast(a, b)); } +inline v_float64 v_dotprod_expand_fast(const v_int32& a, const v_int32& b, const v_float64& c) +{ return v_add(v_dotprod_expand_fast(a, b) , c); } +#endif + +// TODO: only 128 bit now. +inline v_float32 v_matmul(const v_float32& v, const v_float32& m0, + const v_float32& m1, const v_float32& m2, + const v_float32& m3) +{ + vfloat32m1_t res; + res = vfmul_vf_f32m1(m0, v_extract_n(v, 0), VTraits::vlanes()); + res = vfmacc_vf_f32m1(res, v_extract_n(v, 1), m1, VTraits::vlanes()); + res = vfmacc_vf_f32m1(res, v_extract_n(v, 2), m2, VTraits::vlanes()); + res = vfmacc_vf_f32m1(res, v_extract_n(v, 3), m3, VTraits::vlanes()); + return res; +} + +// TODO: only 128 bit now. +inline v_float32 v_matmuladd(const v_float32& v, const v_float32& m0, + const v_float32& m1, const v_float32& m2, + const v_float32& a) +{ + vfloat32m1_t res = vfmul_vf_f32m1(m0, v_extract_n(v,0), VTraits::vlanes()); + res = vfmacc_vf_f32m1(res, v_extract_n(v,1), m1, VTraits::vlanes()); + res = vfmacc_vf_f32m1(res, v_extract_n(v,2), m2, VTraits::vlanes()); + return vfadd(res, a, VTraits::vlanes()); +} inline void v_cleanup() {} @@ -1290,4 +2035,4 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } //namespace cv -#endif //OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP \ No newline at end of file +#endif //OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index ac05768c35..c1db4f49a5 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -1163,6 +1163,22 @@ template struct TheTest return *this; } + TheTest & test_pack_triplets() + { + Data dataA; + R a = dataA; + Data res = v_pack_triplets(a); + + for (int i = 0; i < VTraits::vlanes()/4; ++i) + { + SCOPED_TRACE(cv::format("i=%d", i)); + EXPECT_EQ(dataA[4*i], res[3*i]); + EXPECT_EQ(dataA[4*i+1], res[3*i+1]); + EXPECT_EQ(dataA[4*i+2], res[3*i+2]); + } + return *this; + } + template TheTest & test_pack_u() { @@ -1639,15 +1655,14 @@ template struct TheTest R a = dataA, b = dataB, c = dataC, d = dataD; Data res = v_reduce_sum4(a, b, c, d); - // for (int i = 0; i < VTraits::vlanes(); i += 4) - // { - int i = 0; + for (int i = 0; i < VTraits::vlanes(); i += 4) + { SCOPED_TRACE(cv::format("i=%d", i)); EXPECT_COMPARE_EQ(dataA.sum(i, 4), res[i]); EXPECT_COMPARE_EQ(dataB.sum(i, 4), res[i + 1]); EXPECT_COMPARE_EQ(dataC.sum(i, 4), res[i + 2]); EXPECT_COMPARE_EQ(dataD.sum(i, 4), res[i + 3]); - // } + } return *this; } @@ -1765,372 +1780,12 @@ template struct TheTest #endif }; -#if CV_SIMD_SCALABLE //Temporary -#define DUMP_ENTRY(type) printf("SIMD: %s\n", CV__TRACE_FUNCTION); - - +#define DUMP_ENTRY(type) printf("SIMD%d: %s\n", 8*VTraits::vlanes(), CV__TRACE_FUNCTION); //============= 8-bit integer ===================================================================== void test_hal_intrin_uint8() { DUMP_ENTRY(v_uint8); - // typedef v_uint8 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_expand_q() - .test_addsub() - .test_arithm_wrap() - .test_mul() - .test_mul_expand() - .test_logic() - .test_min_max() - .test_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() - .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_unpack() - .test_reverse() -#if 0 // not implemented in rvv backend yet. - .test_interleave() - .test_cmp() - .test_dotprod_expand() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() - .test_pack_u<1>().test_pack_u<2>().test_pack_u<3>().test_pack_u<8>() - .test_pack_b() - .test_popcount() -#endif - ; -} - -void test_hal_intrin_int8() -{ - DUMP_ENTRY(v_int8); - // typedef v_int8 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_expand_q() - .test_addsub() - .test_arithm_wrap() - .test_mul() - .test_mul_expand() - .test_logic() - .test_min_max() - .test_absdiff() - .test_absdiffs() - .test_abs() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() - .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_unpack() - .test_reverse() -#if 0 - .test_interleave() - .test_cmp() - .test_dotprod_expand() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<3>().test_pack<8>() - .test_popcount() -#endif - ; -} - -//============= 16-bit integer ===================================================================== - -void test_hal_intrin_uint16() -{ - DUMP_ENTRY(v_uint16); - // typedef v_uint16 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_addsub() - .test_arithm_wrap() - .test_mul() - .test_mul_expand() - .test_mul_hi() - .test_shift<1>() - .test_shift<8>() - .test_logic() - .test_min_max() - .test_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() - .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_unpack() - .test_reverse() -#if 0 - .test_interleave() - .test_cmp() - .test_dotprod_expand() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() - .test_pack_u<1>().test_pack_u<2>().test_pack_u<7>().test_pack_u<16>() - .test_popcount() -#endif - ; -} - -void test_hal_intrin_int16() -{ - DUMP_ENTRY(v_int16); - // typedef v_int16 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_addsub() - .test_arithm_wrap() - .test_mul() - .test_mul_expand() - .test_mul_hi() - .test_shift<1>() - .test_shift<8>() - .test_logic() - .test_min_max() - .test_absdiff() - .test_absdiffs() - .test_abs() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() - .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_unpack() - .test_reverse() -#if 0 - .test_interleave() - - .test_cmp() - .test_dotprod() - .test_dotprod_expand() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<7>().test_pack<16>() - .test_popcount() -#endif - ; -} - -//============= 32-bit integer ===================================================================== - -void test_hal_intrin_uint32() -{ - DUMP_ENTRY(v_uint32); - // typedef v_uint32 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_addsub() - .test_mul() - .test_mul_expand() - .test_shift<1>() - .test_shift<8>() - .test_logic() - .test_min_max() - .test_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() - .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>() - .test_broadcast_element<0>().test_broadcast_element<1>() - .test_extract_highest() - .test_broadcast_highest() - .test_unpack() - .test_transpose() - .test_reverse() -#if 0 - .test_interleave() - .test_cmp() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() - .test_popcount() -#endif - ; -} - -void test_hal_intrin_int32() -{ - DUMP_ENTRY(v_int32); - // typedef v_int32 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_expand() - .test_addsub() - .test_mul() - .test_abs() - .test_shift<1>().test_shift<8>() - .test_dotprod_expand_f64() - .test_logic() - .test_min_max() - .test_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() - .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>() - .test_broadcast_element<0>().test_broadcast_element<1>() - .test_extract_highest() - .test_broadcast_highest() - .test_unpack() - .test_transpose() - .test_reverse() -#if 0 - .test_interleave() - .test_cmp() - .test_dotprod() - .test_reduce() - .test_reduce_sad() - .test_pack<1>().test_pack<2>().test_pack<15>().test_pack<32>() - .test_float_cvt32() - .test_float_cvt64() - .test_popcount() -#endif - ; -} - -//============= 64-bit integer ===================================================================== - -void test_hal_intrin_uint64() -{ - DUMP_ENTRY(v_uint64); - // typedef v_uint64 R; - TheTest() - .test_loadstore() - .test_addsub() - .test_shift<1>().test_shift<8>() - .test_logic() - .test_extract<0>().test_extract<1>() - .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_reverse() - ; -#if 0 - #if CV_SIMD_64F - .test_cmp64() - #endif -#endif -} - -void test_hal_intrin_int64() -{ - DUMP_ENTRY(v_int64); - // typedef v_int64 R; - TheTest() - .test_loadstore() - .test_addsub() - .test_shift<1>().test_shift<8>() - .test_logic() - .test_extract<0>().test_extract<1>() - .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_cvt64_double() - .test_reverse() - ; -#if 0 - #if CV_SIMD_64F - .test_cmp64() - #endif -#endif -} - -//============= Floating point ===================================================================== -void test_hal_intrin_float32() -{ - DUMP_ENTRY(v_float32); - // typedef v_float32 R; - TheTest() - .test_loadstore() - .test_interleave_pq() - .test_addsub() - .test_abs() - .test_mul() - .test_div() - .test_sqrt_abs() - .test_min_max() - .test_float_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() - .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>() - .test_broadcast_element<0>().test_broadcast_element<1>() - .test_extract_highest() - .test_broadcast_highest() - .test_unpack() - .test_transpose() - .test_reverse() -#if 0 - .test_interleave() - .test_interleave_2channel() - .test_cmp() - .test_reduce() - .test_reduce_sad() - .test_float_math() - .test_float_cvt64() - .test_matmul() - .test_reduce_sum4() -#endif - ; -} - -void test_hal_intrin_float64() -{ - DUMP_ENTRY(v_float64); -#if CV_SIMD_SCALABLE_64F - // typedef v_float64 R; - TheTest() - .test_loadstore() - .test_addsub() - .test_mul() - .test_div() - .test_abs() - .test_sqrt_abs() - .test_min_max() - .test_float_absdiff() - .test_mask() - .test_extract<0>().test_extract<1>() - .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>() - .test_extract_highest() - .test_reverse() -#if 0 - .test_cmp() - .test_unpack() - .test_float_cvt32() - .test_float_math() -#endif - ; - -#endif -} - -#else - -#define DUMP_ENTRY(type) printf("SIMD%d: %s\n", 8*(int)sizeof(v_uint8), CV__TRACE_FUNCTION); -//============= 8-bit integer ===================================================================== - -void test_hal_intrin_uint8() -{ - DUMP_ENTRY(v_uint8); - typedef v_uint8 R; TheTest() .test_loadstore() .test_interleave() @@ -2157,9 +1812,10 @@ void test_hal_intrin_uint8() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_pack_triplets() + //.test_broadcast_element<0>().test_broadcast_element<1>() #if CV_SIMD_WIDTH == 32 .test_pack<9>().test_pack<10>().test_pack<13>().test_pack<15>() .test_pack_u<9>().test_pack_u<10>().test_pack_u<13>().test_pack_u<15>() @@ -2172,7 +1828,6 @@ void test_hal_intrin_uint8() void test_hal_intrin_int8() { DUMP_ENTRY(v_int8); - typedef v_int8 R; TheTest() .test_loadstore() .test_interleave() @@ -2199,9 +1854,10 @@ void test_hal_intrin_int8() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<8>().test_extract<15>() .test_rotate<0>().test_rotate<1>().test_rotate<8>().test_rotate<15>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_pack_triplets() + //.test_broadcast_element<0>().test_broadcast_element<1>() ; } @@ -2210,7 +1866,6 @@ void test_hal_intrin_int8() void test_hal_intrin_uint16() { DUMP_ENTRY(v_uint16); - typedef v_uint16 R; TheTest() .test_loadstore() .test_interleave() @@ -2238,16 +1893,16 @@ void test_hal_intrin_uint16() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_pack_triplets() + //.test_broadcast_element<0>().test_broadcast_element<1>() ; } void test_hal_intrin_int16() { DUMP_ENTRY(v_int16); - typedef v_int16 R; TheTest() .test_loadstore() .test_interleave() @@ -2277,9 +1932,10 @@ void test_hal_intrin_int16() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>() .test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_pack_triplets() + //.test_broadcast_element<0>().test_broadcast_element<1>() ; } @@ -2288,7 +1944,6 @@ void test_hal_intrin_int16() void test_hal_intrin_uint32() { DUMP_ENTRY(v_uint32); - typedef v_uint32 R; TheTest() .test_loadstore() .test_interleave() @@ -2312,18 +1967,18 @@ void test_hal_intrin_uint32() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() - .test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() .test_extract_highest() .test_broadcast_highest() .test_transpose() + .test_pack_triplets() ; } void test_hal_intrin_int32() { DUMP_ENTRY(v_int32); - typedef v_int32 R; TheTest() .test_loadstore() .test_interleave() @@ -2348,13 +2003,14 @@ void test_hal_intrin_int32() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() - .test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() .test_float_cvt32() .test_float_cvt64() .test_transpose() .test_extract_highest() .test_broadcast_highest() + .test_pack_triplets() ; } @@ -2363,7 +2019,6 @@ void test_hal_intrin_int32() void test_hal_intrin_uint64() { DUMP_ENTRY(v_uint64); - typedef v_uint64 R; TheTest() .test_loadstore() .test_addsub() @@ -2375,16 +2030,15 @@ void test_hal_intrin_uint64() .test_reverse() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + //.test_broadcast_element<0>().test_broadcast_element<1>() ; } void test_hal_intrin_int64() { DUMP_ENTRY(v_int64); - typedef v_int64 R; TheTest() .test_loadstore() .test_addsub() @@ -2396,9 +2050,9 @@ void test_hal_intrin_int64() .test_reverse() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + //.test_broadcast_element<0>().test_broadcast_element<1>() .test_cvt64_double() ; } @@ -2407,7 +2061,6 @@ void test_hal_intrin_int64() void test_hal_intrin_float32() { DUMP_ENTRY(v_float32); - typedef v_float32 R; TheTest() .test_loadstore() .test_interleave() @@ -2433,10 +2086,11 @@ void test_hal_intrin_float32() .test_reverse() .test_extract<0>().test_extract<1>().test_extract<2>().test_extract<3>() .test_rotate<0>().test_rotate<1>().test_rotate<2>().test_rotate<3>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() - .test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + .test_extract_n<0>().test_extract_n<1>() + .test_broadcast_element<0>().test_broadcast_element<1>() .test_extract_highest() .test_broadcast_highest() + .test_pack_triplets() #if CV_SIMD_WIDTH == 32 .test_extract<4>().test_extract<5>().test_extract<6>().test_extract<7>() .test_rotate<4>().test_rotate<5>().test_rotate<6>().test_rotate<7>() @@ -2448,7 +2102,6 @@ void test_hal_intrin_float64() { DUMP_ENTRY(v_float64); #if CV_SIMD_64F - typedef v_float64 R; TheTest() .test_loadstore() .test_addsub() @@ -2466,9 +2119,9 @@ void test_hal_intrin_float64() .test_reverse() .test_extract<0>().test_extract<1>() .test_rotate<0>().test_rotate<1>() - .test_extract_n<0>().test_extract_n<1>().test_extract_n() + .test_extract_n<0>().test_extract_n<1>() .test_extract_highest() - //.test_broadcast_element<0>().test_broadcast_element<1>().test_broadcast_element() + //.test_broadcast_element<0>().test_broadcast_element<1>() #if CV_SIMD_WIDTH == 32 .test_extract<2>().test_extract<3>() .test_rotate<2>().test_rotate<3>() @@ -2494,8 +2147,6 @@ void test_hal_intrin_float16() #endif } -#endif - /*#if defined(CV_CPU_DISPATCH_MODE_FP16) && CV_CPU_DISPATCH_MODE == FP16 void test_hal_intrin_float16() From bb64db98d89ae9806eb792cca42dd43c06ebd5e8 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Fri, 26 Aug 2022 17:57:25 +0800 Subject: [PATCH 036/313] Further optimization of Conv2D, fused Conv_Add_Activation, bring latest code from ficus OpConv.fx. (#22401) --- .../dnn/include/opencv2/dnn/all_layers.hpp | 3 + modules/dnn/src/dnn_common.hpp | 1 + modules/dnn/src/layers/convolution_layer.cpp | 42 +- .../fast_convolution.avx2.cpp | 91 +- .../fast_convolution/fast_convolution.cpp | 847 ++++++++---------- .../fast_convolution/fast_convolution.hpp | 39 +- .../fast_convolution.simd.hpp | 488 +++++----- .../fast_convolution/winograd_3x3s1_f63.cpp | 406 +++++++-- modules/dnn/src/net_impl_fuse.cpp | 174 +++- modules/dnn/test/test_caffe_importer.cpp | 2 +- modules/dnn/test/test_int8_layers.cpp | 2 +- modules/dnn/test/test_torch_importer.cpp | 4 +- 12 files changed, 1194 insertions(+), 905 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 263e48c760..66ba08710e 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -256,6 +256,9 @@ CV__DNN_INLINE_NS_BEGIN { public: static Ptr create(const LayerParams& params); + bool fusedActivation = false; + bool fusedAdd = false; + bool isConv2D = false; // Should be deleted after fastconv branch support Conv1D and Conv3D. }; class CV_EXPORTS ConvolutionLayerInt8 : public BaseConvolutionLayer diff --git a/modules/dnn/src/dnn_common.hpp b/modules/dnn/src/dnn_common.hpp index ae4d9c295e..b580b9f74b 100644 --- a/modules/dnn/src/dnn_common.hpp +++ b/modules/dnn/src/dnn_common.hpp @@ -13,6 +13,7 @@ namespace cv { namespace dnn { CV__DNN_INLINE_NS_BEGIN #define IS_DNN_OPENCL_TARGET(id) (id == DNN_TARGET_OPENCL || id == DNN_TARGET_OPENCL_FP16) +#define IS_DNN_CPU_TARGET(id) (id == DNN_TARGET_CPU) // TODO: add DNN_TARGET_CPU_FP16 Mutex& getInitializationMutex(); void initializeLayerFactory(); diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index c2960d5aeb..e6ef9f1e35 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -118,6 +118,9 @@ public: fusedWeights = false; fusedBias = false; + + if (kernel_size.size() == 2) + isConv2D = true; } virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE @@ -188,6 +191,9 @@ public: virtual bool tryFuse(Ptr& top) CV_OVERRIDE { + if (fusedAdd) // If the Conv layer has fused Add layer, it cannot fuse other layers. + return false; + Ptr blank_layer = top.dynamicCast(); if (blank_layer) return true; @@ -260,7 +266,6 @@ public: std::vector reluslope; Ptr activ; - Mat fastWeights; // Used to store weight params. It will be used for layer fusion and without memory alignment. Ptr fastConv2dImpl; #ifdef HAVE_OPENCL @@ -438,7 +443,6 @@ public: wm.copyTo(wm_aligned); wm = wm_aligned; } - fastWeights = blobs[0].reshape(1, numOutput); weightsMat = wm; } else @@ -584,11 +588,15 @@ public: } } #endif - return !activ.empty(); + fusedActivation = !activ.empty(); + return fusedActivation; } virtual bool tryFuse(Ptr& top) CV_OVERRIDE { + if (fusedAdd) // If the Conv layer has fused Add layer, it cannot fuse other layers. + return false; + #ifdef HAVE_CUDA if(IS_DNN_CUDA_TARGET(preferableTarget)) { @@ -634,26 +642,14 @@ public: if (weightsMat.data == blobs[0].data) weightsMat = weightsMat.clone(); - // If fastWeights is the same as weightsMat, we don't need to allocate more space for fastWeights. - bool sameFastWeights = false; - if (fastWeights.step1() == weightsMat.step1()) // If weightsMat is realigned, it is not the same as fastWeights. - sameFastWeights = true; - - if (!sameFastWeights && fastWeights.data == blobs[0].data) - fastWeights = fastWeights.clone(); - Mat originWeights = blobs[0].reshape(1, outCn); for (int i = 0; i < outCn; ++i) { double wi = w.at(i); weightsMultipliers[i] *= wi; cv::multiply(originWeights.row(i), weightsMultipliers[i], weightsMat.row(i)); - if (!sameFastWeights) - cv::multiply(originWeights.row(i), weightsMultipliers[i], fastWeights.row(i)); biasvec[i] *= wi; } - if (sameFastWeights) - fastWeights = weightsMat; } if (!b.empty()) @@ -1970,9 +1966,6 @@ public: if (blobs.empty()) { variableWeight = true; - if (fastWeights.data != inputs[1].data) - fastWeights = inputs[1].clone(); - Mat wm = inputs[1].reshape(1, outCn); if (wm.data != weightsMat.data) { @@ -2089,7 +2082,7 @@ public: { int nstripes = std::max(getNumThreads(), 1); - // Initialization of FastCovn2d + // Initialization of FastCovn2d, pack weight. if ((!fastConv2dImpl || variableWeight) && inputs[0].dims == 4) { int K = outputs[0].size[1]; @@ -2103,23 +2096,22 @@ public: int dilation_h = dilations[dilations.size() - 2]; int dilation_w = dilations.back(); - float* weightsPtr = fastWeights.ptr(); - CV_Assert(weightsPtr); - fastConv2dImpl = initFastConv2d(ngroups, K, C, Hk, Wk, stride_w, stride_h, - dilation_w, dilation_h, pads_begin, pads_end, weightsPtr, &biasvec[0]); + fastConv2dImpl = initFastConv2d(ngroups, K, C, Hk, Wk, stride_w, stride_h, dilation_w, + dilation_h, pads_begin, pads_end, weightsMat, &biasvec[0]); } if (fastConv2dImpl) { - runFastConv2d(inputs[0], outputs[0], fastConv2dImpl, nstripes, activ); + runFastConv2d(inputs[0], outputs[0], fastConv2dImpl, nstripes, activ, fusedAdd); return; } + //TODO: Add support of Conv1D and Conv3D to fastConv, and remove the old Conv branch. // Use only for Conv1D and Conv3D. + CV_Assert(!fusedAdd); ParallelConv::run(inputs[0], outputs[0], weightsMat, biasvec, reluslope, kernel_size, strides, pads_begin, pads_end, dilations, activ.get(), ngroups, nstripes); - } } diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp index 22580c580c..de1a2ef6b8 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp @@ -9,67 +9,67 @@ namespace cv { namespace opt_AVX2 { #if CV_TRY_AVX2 -void convBlock_AVX2(int k, const float *a, const float *b, - float *c, int ldc, const float *bias, - float minval, float maxval, bool ifActiv) +void convBlock_AVX2(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { -#if FAST_CONV_MR == 4 && FAST_CONV_NR == 24 - __m256 vminval = _mm256_set1_ps(minval), vmaxval = _mm256_set1_ps(maxval); - __m256 c0 = _mm256_set1_ps(bias[0]), c1 = c0, c2 = c0; - __m256 c3 = _mm256_set1_ps(bias[1]), c4 = c3, c5 = c3; - __m256 c6 = _mm256_set1_ps(bias[2]), c7 = c6, c8 = c6; - __m256 c9 = _mm256_set1_ps(bias[3]), c10 = c9, c11 = c9; +#if CONV_MR == 4 && CONV_NR == 24 + __m256 c00 = _mm256_set1_ps(0.f), c01 = c00, c02 = c00; + __m256 c10 = c00, c11 = c00, c12 = c00; + __m256 c20 = c00, c21 = c00, c22 = c00; + __m256 c30 = c00, c31 = c00, c32 = c00; __m256 a0 = _mm256_setzero_ps(), a1 = _mm256_setzero_ps(); __m256 b0 = _mm256_setzero_ps(), b1 = _mm256_setzero_ps(), b2 = _mm256_setzero_ps(); - for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR) + for (int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR) { a0 = _mm256_set1_ps(a[0]), a1 = _mm256_set1_ps(a[1]); b0 = _mm256_load_ps(b), b1 = _mm256_load_ps(b + 8), b2 = _mm256_load_ps(b + 16); - c0 = _mm256_fmadd_ps(b0, a0, c0); - c1 = _mm256_fmadd_ps(b1, a0, c1); - c2 = _mm256_fmadd_ps(b2, a0, c2); + c00 = _mm256_fmadd_ps(b0, a0, c00); + c01 = _mm256_fmadd_ps(b1, a0, c01); + c02 = _mm256_fmadd_ps(b2, a0, c02); - c3 = _mm256_fmadd_ps(b0, a1, c3); - a0 = _mm256_set1_ps(a[2]); - c4 = _mm256_fmadd_ps(b1, a1, c4); - c5 = _mm256_fmadd_ps(b2, a1, c5); + c10 = _mm256_fmadd_ps(b0, a1, c10); + c11 = _mm256_fmadd_ps(b1, a1, c11); + c12 = _mm256_fmadd_ps(b2, a1, c12); - c6 = _mm256_fmadd_ps(b0, a0, c6); - a1 = _mm256_set1_ps(a[3]); - c7 = _mm256_fmadd_ps(b1, a0, c7); - c8 = _mm256_fmadd_ps(b2, a0, c8); + a0 = _mm256_set1_ps(a[2]), a1 = _mm256_set1_ps(a[3]); - c9 = _mm256_fmadd_ps(b0, a1, c9); - c10 = _mm256_fmadd_ps(b1, a1, c10); - c11 = _mm256_fmadd_ps(b2, a1, c11); + c20 = _mm256_fmadd_ps(b0, a0, c20); + c21 = _mm256_fmadd_ps(b1, a0, c21); + c22 = _mm256_fmadd_ps(b2, a0, c22); + + c30 = _mm256_fmadd_ps(b0, a1, c30); + c31 = _mm256_fmadd_ps(b1, a1, c31); + c32 = _mm256_fmadd_ps(b2, a1, c32); } - if (ifActiv) + if (!init_c) { - c0 = _mm256_min_ps(_mm256_max_ps(c0, vminval), vmaxval); - c1 = _mm256_min_ps(_mm256_max_ps(c1, vminval), vmaxval); - c2 = _mm256_min_ps(_mm256_max_ps(c2, vminval), vmaxval); - c3 = _mm256_min_ps(_mm256_max_ps(c3, vminval), vmaxval); - c4 = _mm256_min_ps(_mm256_max_ps(c4, vminval), vmaxval); - c5 = _mm256_min_ps(_mm256_max_ps(c5, vminval), vmaxval); - c6 = _mm256_min_ps(_mm256_max_ps(c6, vminval), vmaxval); - c7 = _mm256_min_ps(_mm256_max_ps(c7, vminval), vmaxval); - c8 = _mm256_min_ps(_mm256_max_ps(c8, vminval), vmaxval); - c9 = _mm256_min_ps(_mm256_max_ps(c9, vminval), vmaxval); - c10 = _mm256_min_ps(_mm256_max_ps(c10, vminval), vmaxval); - c11 = _mm256_min_ps(_mm256_max_ps(c11, vminval), vmaxval); + c00 = _mm256_add_ps(c00, _mm256_load_ps(c)); + c01 = _mm256_add_ps(c01, _mm256_load_ps(c + 8)); + c02 = _mm256_add_ps(c02, _mm256_load_ps(c + 16)); + + c10 = _mm256_add_ps(c10, _mm256_load_ps(c + ldc)); + c11 = _mm256_add_ps(c11, _mm256_load_ps(c + ldc + 8)); + c12 = _mm256_add_ps(c12, _mm256_load_ps(c + ldc + 16)); + + c20 = _mm256_add_ps(c20, _mm256_load_ps(c + ldc*2)); + c21 = _mm256_add_ps(c21, _mm256_load_ps(c + ldc*2 + 8)); + c22 = _mm256_add_ps(c22, _mm256_load_ps(c + ldc*2 + 16)); + + c30 = _mm256_add_ps(c30, _mm256_load_ps(c + ldc*3)); + c31 = _mm256_add_ps(c31, _mm256_load_ps(c + ldc*3 + 8)); + c32 = _mm256_add_ps(c32, _mm256_load_ps(c + ldc*3 + 16)); } - _mm256_storeu_ps(c, c0); _mm256_storeu_ps(c+8, c1); _mm256_storeu_ps(c+16, c2); - _mm256_storeu_ps(c + ldc, c3); _mm256_storeu_ps(c + ldc + 8, c4); _mm256_storeu_ps(c + ldc + 16, c5); - _mm256_storeu_ps(c + ldc*2, c6); _mm256_storeu_ps(c + ldc*2 + 8, c7); _mm256_storeu_ps(c + ldc*2 + 16, c8); - _mm256_storeu_ps(c + ldc*3, c9); _mm256_storeu_ps(c + ldc*3 + 8, c10); _mm256_storeu_ps(c + ldc*3 + 16, c11); + _mm256_storeu_ps(c, c00), _mm256_storeu_ps(c+8, c01), _mm256_storeu_ps(c+16, c02); + _mm256_storeu_ps(c + ldc, c10), _mm256_storeu_ps(c + ldc + 8, c11), _mm256_storeu_ps(c + ldc + 16, c12); + _mm256_storeu_ps(c + ldc*2, c20), _mm256_storeu_ps(c + ldc*2 + 8, c21), _mm256_storeu_ps(c + ldc*2 + 16, c22); + _mm256_storeu_ps(c + ldc*3, c30), _mm256_storeu_ps(c + ldc*3 + 8, c31), _mm256_storeu_ps(c + ldc*3 + 16, c32); _mm256_zeroupper(); #else -#error "unsupported FAST_CONV_MR and/or FAST_CONV_NR in convBlock_AVX2." +#error "unsupported CONV_MR and/or CONV_NR in convBlock_AVX2." #endif } @@ -78,7 +78,6 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3) { - const int VECSZ = 8; __m256 vminval = _mm256_set1_ps(minval); __m256 vmaxval = _mm256_set1_ps(maxval); @@ -175,7 +174,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights { if (dy0 == 3) { - for (; x0 <= x1 - VECSZ; x0 += VECSZ) + for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -251,7 +250,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights } else { - for (; x0 <= x1 - VECSZ; x0 += VECSZ) + for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -277,7 +276,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights } else { - for (; x0 <= x1 - VECSZ; x0 += VECSZ) + for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) { int xi_ = x0 * stride_x - pad_left, k = 0; const float *inptr_xi = inptr + Wi * yi_ + xi_; diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index 139ea7f6fc..2af836339d 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -22,7 +22,7 @@ Ptr initFastConv2d( int dilation_x, int dilation_y, const std::vector& pads_begin, const std::vector& pads_end, - float* srcWeights, + InputArray _weightsMat, float* srcBias) { Ptr conv = makePtr(); @@ -43,33 +43,27 @@ Ptr initFastConv2d( conv->pad_bottom = pads_end[0]; conv->pad_left = pads_begin[1]; conv->pad_right = pads_end[1]; - - // store bias; append some zero's to make sure that - // we can always read FAST_CONV_MR elements starting from any valid index - { - int k = 0, nbias = K + FAST_CONV_MR-1; - conv->biasBuf.reserve(nbias); - float* biasBufPtr = conv->biasBuf.data(); - for(; k < K; k++) - biasBufPtr[k] = srcBias ? srcBias[k] : 0.f; - for(; k < nbias; k++) - biasBufPtr[k] = 0.f; - } + Mat weightsMat = _weightsMat.getMat(); + auto wShape = shape(weightsMat); + const size_t wstep = weightsMat.step1(); #if CV_NEON // For now, winograd is ARM platform only. - if (ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && dilation_x == 1 && dilation_y ==1 - && K >= 16 && C >= 16 ) + if (ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && + dilation_x == 1 && dilation_y ==1 && K >= 16 && C >= 16) conv->ifWinograd63 = true; #else conv->ifWinograd63 = false; #endif + float *srcWeights = (float *)weightsMat.data; if (ngroups > 1 && ngroups == K && ngroups == C) { // for depth-wise convolutions on NCHW data we just preserve the weights in KCHW layout, // but add some padding to make the weights array layout more SIMD-friendly int ksize = Hk*Wk; - int padded_ksize = ((ksize + FAST_VEC_NLANES-1)/FAST_VEC_NLANES)*FAST_VEC_NLANES; // this code aims to let memory fit with vector size. + + // this code aims to let memory fit with vector size. + int padded_ksize = ((ksize + FAST_VEC_NLANES-1) / FAST_VEC_NLANES) * FAST_VEC_NLANES; int nweights = C*padded_ksize; conv->weightsBuf.reserve(nweights); float* weightsBufPtr = conv->weightsBuf.data(); @@ -77,340 +71,80 @@ Ptr initFastConv2d( for(int c = 0; c < C; c++) { for (int k = 0; k < ksize; k++) - weightsBufPtr[c*padded_ksize + k] = srcWeights[c*ksize + k]; + weightsBufPtr[c*padded_ksize + k] = srcWeights[c*wstep + k]; } } else { // The weights are packed as - // ngroups x (ceil((K/ngroups)/FAST_CONV_MR)*FAST_CONV_MR) x (Cg*Hk*Wk) x FAST_CONV_MR tensor + // ngroups x (ceil((K/ngroups)/CONV_MR)*CONV_MR) x (Cg*Hk*Wk) x CONV_MR tensor int Kg = K/ngroups, Cg = max(C/ngroups, 1); - int Kg_aligned = ((Kg + FAST_CONV_MR - 1)/FAST_CONV_MR)*FAST_CONV_MR; - size_t nweights = ngroups*Kg_aligned*Cg*Hk*Wk; + int numStripsMR = (Kg + CONV_MR - 1) / CONV_MR; + int Kg_aligned = numStripsMR * CONV_MR; + int HkWkCg = Hk*Wk*Cg; + size_t nweights = ngroups*Kg_aligned*HkWkCg; conv->weightsBuf.reserve(nweights); float* weightsBufPtr = conv->weightsBuf.data(); memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0])); - float* packed_wptr = weightsBufPtr; - // pack the weight. - for(int g = 0; g < ngroups; g++) + // Pack the weight. + parallel_for_(Range(0, ngroups * numStripsMR), [&](const Range& r0){ + for (int gsi = r0.start; gsi < r0.end; gsi++) { - for(int k0 = 0; k0 < Kg_aligned; k0 += FAST_CONV_MR) - { - int dk = Kg - k0 < FAST_CONV_MR ? Kg - k0 : FAST_CONV_MR; - for(int c = 0; c < Cg; c++) + int g = gsi / numStripsMR; + int si = gsi - g * numStripsMR; + + int startK = si * CONV_MR; + CV_Assert(startK < Kg_aligned); + + float* packed_wptr = weightsBufPtr + HkWkCg * (startK + g * Kg_aligned); + int dk = Kg - startK < CONV_MR ? Kg - startK : CONV_MR; // check if we need zero padding. + + int k_idx = g*Kg + startK; + for(int yx = 0; yx < Hk*Wk; yx++) { + for(int c = 0; c < Cg; c++, packed_wptr += CONV_MR) { - for(int yx = 0; yx < Hk*Wk; yx++, packed_wptr += FAST_CONV_MR) - { - const float* wptr = srcWeights + ((g*Kg + k0)*Cg + c)*Hk*Wk + yx; - int k = 0; - for(; k < dk; k++, wptr += Cg*Hk*Wk) - packed_wptr[k] = *wptr; - for(; k < FAST_CONV_MR; k++) - packed_wptr[k] = 0.f; - } + const float* wptr = srcWeights + wstep * k_idx + c*Hk*Wk + yx; + int k = 0; + for(; k < dk; k++, wptr += wstep) + packed_wptr[k] = *wptr; + for(; k < CONV_MR; k++) + packed_wptr[k] = 0.f; } } - } + }}); // Prepare Weight for Winograd F(6x6, 3x3) if (conv->ifWinograd63) { - initWinograd63(conv, srcWeights, K, C); + initWinograd63(conv, weightsMat, K, C); } } + + // store bias; append some zero's to make sure that + // we can always read MR elements starting from any valid index + { + int k = 0, nbias = K + CONV_MR - 1; + conv->biasBuf.reserve(nbias); + float* biasBufPtr = conv->biasBuf.data(); + for(; k < K; k++) + biasBufPtr[k] = srcBias ? srcBias[k] : 0.f; + for(; k < nbias; k++) + biasBufPtr[k] = 0.f; + } return conv; } -static void packInput(float* inpbuf, const float* inptr, int* yxtab, int ksize, int Cg, int Hi, int Wi, int W0, - int pad_top, int pad_left, int stride_x, int stride_y, int yx0, int slice_len, - bool fast_1x1, bool partial0, bool s1d1p0, bool s1d1) -{ - const size_t inp_planesize = (size_t)Hi*Wi; - - if (fast_1x1) - { - /* - super-fast branch for 1x1 convolutions with sy=sx=1. - in this case each feature plane can be safely treated - as 1D array and we just extract next portion - of FAST_CONV_NR elements from each feature plane and - put it together. - */ - inptr += yx0; - if (!partial0) - { - // Make special branch where memcpy() is called with a constant buffer size. - // Compilers will likely unroll this loop properly. - for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += FAST_CONV_NR) - memcpy(inpbuf, inptr, FAST_CONV_NR * sizeof(inpbuf[0])); - } - else - { - for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += FAST_CONV_NR) - { - memcpy(inpbuf, inptr, slice_len * sizeof(inpbuf[0])); - memset(inpbuf + slice_len, 0, (FAST_CONV_NR - slice_len) * sizeof(inpbuf[0])); - } - } - } - else if (s1d1p0) - { - /* - slower, but still fast branch for sy=sx=1, dy=dx=1 and without padding, - in this case we copy data from input tensors by chunks. - */ - for (int c = 0; c < Cg; c++) - { - float *inpbuf_c = inpbuf + c * (FAST_CONV_NR * ksize); - const float *inptr_c = inptr + c * inp_planesize; - - for (int k = 0; k < ksize; k++) - { - int y0 = yx0 / W0, x0 = yx0 % W0; - int yi = y0 + yxtab[k * 2], xi = x0 + yxtab[k * 2 + 1]; - float *inpbuf_k = inpbuf_c + k * FAST_CONV_NR; - int xi_0 = yxtab[k * 2 + 1]; - - int i = 0; - for (; i < slice_len;) - { - const float *inptr_k = inptr_c + yi * Wi + xi; - int copy_len = std::min(slice_len - i, W0 - x0); - int di_z = (slice_len == i + copy_len) ? FAST_CONV_NR - slice_len : 0; - - memcpy(inpbuf_k + i, - inptr_k, - copy_len * sizeof(inpbuf_k[0])); - - memset(inpbuf_k + i + copy_len, - 0, di_z * sizeof(inpbuf_k[0])); - - i += copy_len; - x0 = 0; - xi = xi_0; - yi++; - } - } - } - } - else if (s1d1) - { - /* - slower, but still fast branch for sy=sx=1, dy=dx=1. - in this case we copy data from input tensors by chunks and - interleave the data in inpbuf with 0's - (that correspond to the padding elements) when necessary - */ - int y0 = yx0 / W0, x0 = yx0 % W0; - for (int c = 0; c < Cg; c++) - { - float *inpbuf_c = inpbuf + c * (FAST_CONV_NR * ksize); - const float *inptr_c = inptr + c * inp_planesize; - - for (int k = 0; k < ksize; k++) - { - int x0_tmp = x0; - - int xi_0 = yxtab[k * 2 + 1] - pad_left; - - int yi = y0 + yxtab[k * 2] - pad_top, xi = x0_tmp + xi_0; - float *inpbuf_k = inpbuf_c + k * FAST_CONV_NR; - - int i = 0; - for (; i < slice_len;) { - int copyLen = std::min(slice_len - i, W0 - x0_tmp); - - int di_z = (i + copyLen == slice_len) ? FAST_CONV_NR - slice_len - : 0; // The final padding. - // pad_top or pad bottom - if (yi < 0 || yi > Hi - 1) - { - memset(inpbuf_k + i, - 0, (copyLen + di_z) * sizeof(inpbuf_k[0])); - i += copyLen + di_z; - } - else - { - int x_pad_left = 0, x_pad_right = 0; - - // pad_left - if (xi < 0) - { - x_pad_left = std::min(-xi, copyLen); - xi = 0; - copyLen -= x_pad_left; - } - - memset(inpbuf_k + i, - 0, x_pad_left * sizeof(inpbuf_k[0])); - i += x_pad_left; - - // pad right - if (xi + copyLen > Wi) - { - if (xi > Wi) - { - x_pad_right = copyLen; - copyLen = 0; - } - else - { - x_pad_right = std::min(xi + copyLen - Wi, copyLen); - copyLen -= x_pad_right; - } - } - - CV_Assert(copyLen >= 0); - - const float *inptr_k = inptr_c + yi * Wi + xi; - memcpy(inpbuf_k + i, - inptr_k, - copyLen * sizeof(inpbuf_k[0])); - - i += copyLen; - - // pad_right and the final padding. - memset(inpbuf_k + i, - 0, (di_z + x_pad_right) * sizeof(inpbuf_k[0])); - i += x_pad_right + di_z; - } - - x0_tmp = 0; - xi = xi_0; - yi++; - } - } - } - } - else - { - int y0_ = yx0 / W0, x0_ = yx0 - y0_ * W0; - for (int k = 0; k < ksize; k++) - { - int dy = yxtab[k * 2], dx = yxtab[k * 2 + 1]; - int i = 0, y0 = y0_, x0 = x0_; - for (; i < FAST_CONV_NR;) - { - float *inpbuf_ki = inpbuf + k * FAST_CONV_NR + i; - int yi = y0 * stride_y + dy - pad_top; - int xi = x0 * stride_x + dx - pad_left; - - if ((unsigned) yi < (unsigned) Hi && - (unsigned) xi < (unsigned) Wi) - { - const float *inptr_ki = inptr + yi * Wi + xi; - if (i + 4 <= FAST_CONV_NR && x0 + 4 <= W0 && xi + stride_x * 4 <= Wi) - { - if (stride_x == 2) { - for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR * - ksize, inptr_ki += inp_planesize) - { - float t0 = inptr_ki[0], t1 = inptr_ki[2]; - float t2 = inptr_ki[4], t3 = inptr_ki[6]; - inpbuf_ki[0] = t0; - inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; - inpbuf_ki[3] = t3; - } - } - else - { - for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR * - ksize, inptr_ki += inp_planesize) - { - float t0 = inptr_ki[0], t1 = inptr_ki[stride_x]; - float t2 = inptr_ki[stride_x * 2], t3 = inptr_ki[stride_x * 3]; - inpbuf_ki[0] = t0; - inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; - inpbuf_ki[3] = t3; - } - } - i += 4; - x0 += 4; - } - else - { - for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR * - ksize, inptr_ki += inp_planesize) - *inpbuf_ki = *inptr_ki; - i++; - x0++; - } - } - else - { - for (int c = 0; c < Cg; c++, inpbuf_ki += FAST_CONV_NR * ksize) - inpbuf_ki[0] = 0.f; - i++; - x0++; - } - int mask = x0 >= W0; - y0 += mask; - x0 &= mask - 1; - } - } - } -} - -static void matMulCompute(float* outptr0, float* inpbuf_task, float* cbuf, const Ptr& conv, int HkWkCg, - int k0, int k1, int yx0, int yx1, size_t out_planesize, int g, int Kg, int Kg_aligned, - bool partial0, ActivationLayer*& activ, float minval, float maxval, bool ifMinMaxAct) -{ - int outstep0 = out_planesize; - - for (int k = k0; k < k1; k += FAST_CONV_MR, outptr0 += outstep0 * FAST_CONV_MR) - { - int dk = Kg - k < FAST_CONV_MR ? Kg - k : FAST_CONV_MR; - bool partial = partial0 || dk < FAST_CONV_MR; - float *outptr = outptr0; - - int outstep = outstep0; - if (partial) - { - outptr = cbuf; - outstep = FAST_CONV_NR; - } - - -#if CV_TRY_AVX2 - if (conv->useAVX2) - opt_AVX2::convBlock_AVX2( HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg, - inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k, - minval, maxval, ifMinMaxAct); - else -#endif -#if CV_TRY_NEON - if (conv->useNEON) - opt_NEON::convBlock_NEON(HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg, - inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k, - minval, maxval, ifMinMaxAct); - else -#endif - convBlock(HkWkCg, conv->weightsBuf.data() + (g * Kg_aligned + k) * HkWkCg, - inpbuf_task, outptr, outstep, conv->biasBuf.data() + Kg * g + k, - minval, maxval, ifMinMaxAct); - - // activation - if (activ) - activ->forwardSlice(outptr, outptr, yx1 - yx0, outstep, Kg * g + k, - Kg * g + k + dk); - - if (partial) - { - for (int i = 0; i < dk; i++) - memcpy(outptr0 + i * outstep0, cbuf + i * FAST_CONV_NR, - (yx1 - yx0) * sizeof(cbuf[0])); - } - } -} - -void runFastConv2d(InputArray _input, OutputArray _output, - const Ptr& conv, int ntasks, const Ptr& actLayer) +void runFastConv2d(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, + const Ptr& actLayer, bool fusedAdd) { Mat input = _input.getMat(); Mat output = _output.getMat(); + + Mat fusedAddMat; + if (fusedAdd) + fusedAddMat = _output.getMat(); + MatShape inputShape = shape(input); MatShape outputShape = shape(output); CV_Assert(inputShape.size() == 4 && outputShape.size() == 4); @@ -452,93 +186,69 @@ void runFastConv2d(InputArray _input, OutputArray _output, if (conv->ngroups > 1 && conv->ngroups == conv->K && conv->ngroups == conv->C) { + CV_Assert(fusedAddMat.empty()); // Depthwise-Convolution layer should not be followed by Add layer. return runDepthwise(input, output, conv, minval, maxval, activ, ifMinMaxAct); } #if CV_NEON - if ( conv->ifWinograd63 + if (conv->ifWinograd63 && inputShape[2] > 12 && inputShape[3] > 12 - && inputShape[2] < 120 && inputShape[3] < 120 ) + && inputShape[2] < 120 && inputShape[3] < 120 + ) { - // In general, for winograd branch, more cores will give better performance. - int maxNumThread = std::max(getNumThreads(), 1); - if (runWinograd63(input, output, conv, maxNumThread, minval, maxval, activ, ifMinMaxAct)) + if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) return; } #endif - float* inp = input.ptr(); - float* out = output.ptr(); - int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W] int K = conv->K, Hk = conv->Hk, Wk = conv->Wk; - int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups; // ngroups + int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups; int Cg = C/ngroups, Kg = K/ngroups; - int Kg_nblocks = (Kg + FAST_CONV_MR-1)/FAST_CONV_MR, Kg_aligned = Kg_nblocks*FAST_CONV_MR; // align to MR const size_t inp_planesize = (size_t)Hi*Wi; const size_t out_planesize = (size_t)H0*W0; - int pad_top = conv->pad_top, pad_bottom = conv->pad_bottom; + int pad_top = conv->pad_top; int pad_left = conv->pad_left; - int pad_right = conv->pad_right; int stride_y = conv->stride_y, stride_x = conv->stride_x; int dilation_y = conv->dilation_y, dilation_x = conv->dilation_x; int ksize = Hk * Wk; - bool s1d1 = stride_x == 1 && stride_y == 1 && dilation_x == 1 && dilation_y == 1; - bool s1d1p0 = s1d1 && pad_top == 0 && pad_left ==0 && pad_bottom == 0 && pad_right == 0; bool fast_1x1 = stride_x == 1 && stride_y == 1 && ksize == 1; int HkWkCg = Hk*Wk*Cg; - enum { VEC_ALIGN = 8, DFT_TYPE = CV_32F }; - size_t taskbufsize = FAST_CONV_NR*HkWkCg; // input buffer - size_t taskbufsizeOutput = FAST_CONV_NR * FAST_CONV_MR; - size_t inputbufsize = 0; - size_t outbufsize = ntasks * taskbufsizeOutput; + enum { VEC_ALIGN = 8, DFT_TYPE = CV_32F }; // Memory alignment. + int MAX_STRIPES = 2; // (56 + CONV_NR - 1)/CONV_NR; - int stripes_per_sample = (out_planesize + FAST_CONV_NR - 1)/FAST_CONV_NR; // align to NR - size_t hw_task = stripes_per_sample; - size_t hw_aligned = stripes_per_sample * FAST_CONV_NR; + // Friendly to L1 cache + const int K_BLOCK_SIZE = 32; + const int C_BLOCK_SIZE = 256; - bool separatedLoop = false; + int Kg_nblocks = (Kg + CONV_MR-1)/CONV_MR, Kg_aligned = Kg_nblocks * CONV_MR; - if (stripes_per_sample < 4 * ntasks) + int stripes_per_sample = (out_planesize + CONV_NR - 1) / CONV_NR; + + if (stripes_per_sample < ntasks * 4) { - // If stripes_per_sample is small, we parallelize on K (output channel). + MAX_STRIPES = 1; stripes_per_sample = 1; - - // Separated Parallelloop could save much time in packing input data. But it may cost more memory, we use it when batch size is 1. - if (N == 1) - { - separatedLoop = true; - inputbufsize = ngroups * hw_aligned * HkWkCg; - } - - if (!separatedLoop) - { - inputbufsize = taskbufsize * ntasks; - } } else - { - // If stripes_per_sample is big, we parallelize on H0*W0. Kg_nblocks = 1; - inputbufsize = taskbufsize * ntasks; - } int Kstripes = Kg_nblocks*stripes_per_sample; int nsubtasks = N*ngroups*Kstripes; - AutoBuffer inpbuf_all_, outputbuf_; - inputbufsize = alignSize(inputbufsize, VEC_ALIGN); - inpbuf_all_.allocate(inputbufsize + VEC_ALIGN); - float* inpbuf_all = alignPtr(inpbuf_all_.data(), (int)(VEC_ALIGN*sizeof(float))); + size_t stripesize = CONV_NR * ksize * Cg; + size_t taskbufsize = (stripesize + CONV_NR * K_BLOCK_SIZE) * MAX_STRIPES; + size_t totalbufsize = taskbufsize * ntasks; - outbufsize = alignSize(outbufsize, VEC_ALIGN); - outputbuf_.allocate(outbufsize + VEC_ALIGN); - float* output_buf = alignPtr(outputbuf_.data(), (int)(VEC_ALIGN*sizeof(float))); + AutoBuffer inpbuf_all_; + totalbufsize = alignSize(totalbufsize, VEC_ALIGN); + inpbuf_all_.allocate(totalbufsize + VEC_ALIGN); + float* inpbuf_all = alignPtr(inpbuf_all_.data(), (int)(VEC_ALIGN*sizeof(inpbuf_all_[0]))); std::vector ofstab_(Hk*Wk*3, 0); int* ofstab = ofstab_.data(); @@ -554,141 +264,306 @@ void runFastConv2d(InputArray _input, OutputArray _output, ofstab[k] = dy*Wi + dx; } - if (ksize == 1) - { - CV_Assert(pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0); - CV_Assert(stride_x != 1 || stride_y != 1 || (H0 == Hi && W0 == Wi)); - } + float* inp = input.ptr(); + float* out = output.ptr(); + float* fusedAddPtr0 = fusedAddMat.empty() ? 0 : fusedAddMat.ptr(); - if (separatedLoop) + parallel_for_(Range(0, ntasks), [&](const Range& r0) { + for (int task_id = r0.start; task_id < r0.end; task_id++) { - // For now this branch only handles batch size = 1. Maybe we could support batch size < 10 in the future. - // Pack Input data - parallel_for_(Range(0, ngroups * hw_task), [&](const Range& r0) + float* inpbuf_task = &inpbuf_all[taskbufsize * task_id]; + float* cbuf_task = inpbuf_task + stripesize * MAX_STRIPES; + + int ngs0 = (int)((size_t)nsubtasks * task_id / ntasks); + int ngs1 = (int)((size_t)nsubtasks * (task_id+1) / ntasks); + for (int subtask = ngs0; subtask < ngs1; ) { - for (int nhwi = r0.start; nhwi < r0.end; nhwi++) + int ng = subtask / Kstripes; + int kyx0 = subtask - ng * Kstripes; + int kyx1 = kyx0 + (ngs1 - subtask); + int n = ng / ngroups, g = ng % ngroups; // ng - n * ngroups; + size_t inp_plane_ofs = (size_t)(n * ngroups + g) * Cg * inp_planesize; + kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; + subtask += kyx1 - kyx0; + int k0, k1; + int yx0, yx_limit, yx_block_limit = 0; + + if (stripes_per_sample == 1) { - int g = nhwi/hw_task; - int hw_i = nhwi % hw_task; - int hw0 = hw_i * FAST_CONV_NR; - float* inpbuf = inpbuf_all + g * hw_aligned * HkWkCg + hw0 * HkWkCg; - const float* inptr = inp + g * Cg * inp_planesize; - bool partial0 = hw0 + FAST_CONV_NR > out_planesize? true: false; - int slice_len = FAST_CONV_NR; - - if (partial0) - slice_len = out_planesize - hw0; - - packInput(inpbuf, inptr, yxtab, ksize, Cg, Hi, Wi, W0, pad_top, pad_left, stride_x, stride_y, - hw0, slice_len, fast_1x1, partial0, s1d1p0, s1d1); + k0 = kyx0 * CONV_MR; + k1 = kyx1 * CONV_MR; + k1 = k1 <= Kg ? k1 : Kg; + yx0 = 0; + yx_limit = out_planesize; } - }); - - // Compute - parallel_for_(Range(0, ntasks), [&](const Range& r0) - { - for (int task_id = r0.start; task_id < r0.end; task_id++) + else { - float *cbuf = output_buf + task_id * taskbufsizeOutput; - int ngs0 = (int) ((size_t) nsubtasks * task_id / ntasks); - int ngs1 = (int) ((size_t) nsubtasks * (task_id + 1) / ntasks); - for (int subtask = ngs0; subtask < ngs1;) - { - int ng = subtask / Kstripes; - int kyx0 = subtask - ng * Kstripes; - int kyx1 = kyx0 + (ngs1 - subtask); - int n = ng / ngroups, g = ng - n * ngroups; - - CV_Assert(n <= 1); - - kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; // Guarantee that maximum kyx1 is Kstripes. - subtask += kyx1 - kyx0; - - int k0 = kyx0 * FAST_CONV_MR; - int k1 = kyx1 * FAST_CONV_MR; - k1 = k1 <= Kg ? k1 : Kg; - - - for (int yx0 = 0; yx0 < out_planesize; yx0 += FAST_CONV_NR) - { - float* inpbuf_task = inpbuf_all + g * hw_aligned * HkWkCg + yx0 * HkWkCg; - int yx1 = yx0 + FAST_CONV_NR; - yx1 = yx1 <= out_planesize ? yx1 : out_planesize; - int slice_len = yx1 - yx0; - bool partial0 = slice_len < FAST_CONV_NR; - - int outstep0 = out_planesize; - size_t outofs = ((n * ngroups + g) * Kg + k0) * outstep0 + yx0; - float *outptr0 = out + outofs; - - matMulCompute(outptr0, inpbuf_task, cbuf, conv, HkWkCg, k0, k1, yx0, yx1, out_planesize, g, - Kg, Kg_aligned, partial0, activ, minval, maxval, ifMinMaxAct); - } - } + k0 = 0; + k1 = Kg; + yx0 = kyx0 * CONV_NR; + yx_limit = kyx1 * CONV_NR; + yx_limit = yx_limit < out_planesize ? yx_limit : out_planesize; } - }); - } - else - { - parallel_for_(Range(0, ntasks), [&](const Range &r0) { - for (int task_id = r0.start; task_id < r0.end; task_id++) { - float *inpbuf_task = &inpbuf_all[taskbufsize * task_id]; - float *cbuf = output_buf + task_id * taskbufsizeOutput; - int ngs0 = (int) ((size_t) nsubtasks * task_id / ntasks); - int ngs1 = (int) ((size_t) nsubtasks * (task_id + 1) / ntasks); - for (int subtask = ngs0; subtask < ngs1;) + for (; yx0 < yx_limit; yx0 = yx_block_limit) + { + // step 1. extract part of input tensor and represent it in zigzag form + yx_block_limit = yx0 + CONV_NR * MAX_STRIPES; + yx_block_limit = yx_block_limit < yx_limit ? yx_block_limit : yx_limit; + + int nstripes = (yx_block_limit - yx0 + CONV_NR - 1) / CONV_NR; + int yx0_saved = yx0; + + CV_Assert(nstripes <= MAX_STRIPES); + + for (int stripe = 0; yx0 < yx_block_limit; stripe++, yx0 += CONV_NR) { - int ng = subtask / Kstripes; - int kyx0 = subtask - ng * Kstripes; - int kyx1 = kyx0 + (ngs1 - subtask); - int n = ng / ngroups, g = ng - n * ngroups; - size_t inp_plane_ofs = (size_t) (n * ngroups + g) * Cg * inp_planesize; - kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; // Guarantee that maximum kyx1 is Kstripes. - subtask += kyx1 - kyx0; - int k0, k1; - int yx0, yx_limit; + float* inpbuf = inpbuf_task + stripe * stripesize; + float* inptr = inp + inp_plane_ofs; - if (stripes_per_sample == 1) + /* + 1. pack the data. Copy the HkxWk CONV_NR-wide slices from + each feature plane of the input tensor to the input buffer. + */ + if (fast_1x1) { - k0 = kyx0 * FAST_CONV_MR; - k1 = kyx1 * FAST_CONV_MR; - k1 = k1 <= Kg ? k1 : Kg; - yx0 = 0; - yx_limit = out_planesize; + int slice_len = yx_block_limit - yx0; + bool partial = slice_len < CONV_NR; + // Superfast branch for 1x1 convolutions with sy=sx=1. + // in this case each feature plane can be safely treated + // as 1D array, and we just extract next portion + // of CONV_NR elements from each feature plane and + // put it together. + inptr += yx0; + if (!partial) + { + // Make special branch where memcpy() is called with a constant buffer size. + // Compilers will likely unroll this loop properly. + for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += CONV_NR) + memcpy(inpbuf, inptr, CONV_NR*sizeof(inpbuf[0])); + } + else + { + for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += CONV_NR) + { + memcpy(inpbuf, inptr, slice_len * sizeof(inpbuf[0])); + memset(inpbuf + slice_len, 0, (CONV_NR - slice_len) * sizeof(inpbuf[0])); + } + } } else { - k0 = 0; - k1 = Kg; - yx0 = kyx0 * FAST_CONV_NR; - yx_limit = kyx1 * FAST_CONV_NR; - yx_limit = yx_limit < out_planesize ? yx_limit : out_planesize; + int y0_ = yx0 / W0, x0_ = yx0 - y0_ * W0; + for (int k = 0; k < ksize; k++) + { + int dy = yxtab[k * 2], dx = yxtab[k * 2 + 1]; + int i = 0, y0 = y0_, x0 = x0_; + for (; i < CONV_NR;) + { + float *inpbuf_ki = inpbuf + k * CONV_NR * Cg + i; + int yi = y0 * stride_y + dy - pad_top; + int xi = x0 * stride_x + dx - pad_left; + + if ((unsigned) yi < (unsigned) Hi && (unsigned) xi < (unsigned) Wi) + { + const float *inptr_ki = inptr + yi * Wi + xi; + if (i + 8 <= CONV_NR && x0 + 8 <= W0 && xi + stride_x * 8 <= Wi) + { + if (stride_x == 1) + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + { + float t0 = inptr_ki[0], t1 = inptr_ki[1]; + float t2 = inptr_ki[2], t3 = inptr_ki[3]; + float t4 = inptr_ki[4], t5 = inptr_ki[5]; + float t6 = inptr_ki[6], t7 = inptr_ki[7]; + inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + inpbuf_ki[4] = t4; inpbuf_ki[5] = t5; + inpbuf_ki[6] = t6; inpbuf_ki[7] = t7; + } + } + else + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + { + float t0 = inptr_ki[0], t1 = inptr_ki[stride_x]; + float t2 = inptr_ki[stride_x*2], t3 = inptr_ki[stride_x*3]; + float t4 = inptr_ki[stride_x*4], t5 = inptr_ki[stride_x*5]; + float t6 = inptr_ki[stride_x*6], t7 = inptr_ki[stride_x*7]; + inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + inpbuf_ki[4] = t4; inpbuf_ki[5] = t5; + inpbuf_ki[6] = t6; inpbuf_ki[7] = t7; + } + } + i += 8; + x0 += 8; + } + else if (i + 4 <= CONV_NR && x0 + 4 <= W0 && xi + stride_x * 4 <= Wi) + { + if (stride_x == 1) + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + { + float t0 = inptr_ki[0], t1 = inptr_ki[1]; + float t2 = inptr_ki[2], t3 = inptr_ki[3]; + inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + } + } + else + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + { + float t0 = inptr_ki[0], t1 = inptr_ki[stride_x]; + float t2 = inptr_ki[stride_x*2], t3 = inptr_ki[stride_x*3]; + inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + } + } + i += 4; + x0 += 4; + } + else + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + *inpbuf_ki = *inptr_ki; + i++; + x0++; + } + } + else + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR) + inpbuf_ki[0] = 0.f; + i++; + x0++; + } + int mask = x0 >= W0; + y0 += mask; + x0 &= mask - 1; + } + } + } + } + + yx0 = yx0_saved; + float* weights = conv->weightsBuf.data() + g * Kg_aligned * HkWkCg; + const float* biasptr = conv->biasBuf.data() + Kg * g; + int ldc = nstripes * CONV_NR; + + // 2. do convolution, compute Kg x (yx_block_limit - yx0) part of the output tensor + for (int k0_block = k0; k0_block < k1; k0_block += K_BLOCK_SIZE) + { + int k1_block = k0_block + K_BLOCK_SIZE < k1 ? k0_block + K_BLOCK_SIZE : k1; + for (int c0 = 0; c0 < HkWkCg; c0 += C_BLOCK_SIZE) + { + int c1 = c0 + C_BLOCK_SIZE < HkWkCg ? c0 + C_BLOCK_SIZE : HkWkCg; + for (int stripe = 0; stripe < nstripes; stripe++) + { + float* wptr = weights + k0_block*HkWkCg + c0*CONV_MR; + const float* inptr = inpbuf_task + stripe*stripesize + c0 * CONV_NR; + float* cptr = cbuf_task + stripe * CONV_NR; + for (int k = k0_block; k < k1_block; k += CONV_MR, + wptr += HkWkCg * CONV_MR, cptr += CONV_MR * ldc) + { +#if CV_TRY_AVX2 + if (conv->useAVX2) + opt_AVX2::convBlock_AVX2(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); + else +#endif +#if CV_TRY_NEON + if (conv->useNEON) + opt_NEON::convBlock_NEON(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); + else +#endif + convBlock(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); + } + } } - for (; yx0 < yx_limit; yx0 += FAST_CONV_NR) + size_t outofs = ((n*ngroups + g) * Kg + k0_block) * out_planesize + yx0; + int out_width = yx_block_limit - yx0; + const float* cptr = cbuf_task; + + float* outptr = out + outofs; + const float* pbptr = fusedAddPtr0 ? fusedAddPtr0 + outofs : 0; + + for (int k = k0_block; k < k1_block; k++, + cptr += ldc, outptr += out_planesize, + pbptr += (pbptr ? out_planesize : 0)) { - float *inpbuf = inpbuf_task; - const float *inptr = inp + inp_plane_ofs; - int yx1 = yx0 + FAST_CONV_NR; - yx1 = yx1 <= yx_limit ? yx1 : yx_limit; - int slice_len = yx1 - yx0; - bool partial0 = slice_len < FAST_CONV_NR; - packInput(inpbuf, inptr, yxtab, ksize, Cg, Hi, Wi, W0, pad_top, pad_left, stride_x, stride_y, - yx0, slice_len, fast_1x1, partial0, s1d1p0, s1d1); + float biasval = biasptr[k]; + int j = 0; +#if CV_SIMD128 + v_float32x4 vbias = v_setall_f32(biasval), vmax = v_setall_f32(maxval), vmin = v_setall_f32(minval); + if (pbptr) + { + for (; j + 7 < out_width; j += 8) + { + v_float32x4 v0 = v_add(v_load(cptr + j), vbias); + v_float32x4 v1 = v_add(v_load(cptr + j + 4), vbias); + v0 = v_add(v0, v_load(pbptr + j)); + v1 = v_add(v1, v_load(pbptr + j + 4)); - // 2. do convolution, compute Kg x (yx1 - yx0) part of the output tensor - int outstep0 = out_planesize; - size_t outofs = ((n * ngroups + g) * Kg + k0) * outstep0 + yx0; - float *outptr0 = out + outofs; + if (ifMinMaxAct) + { + v0 = v_min(v_max(v0, vmin), vmax); + v1 = v_min(v_max(v1, vmin), vmax); + } - matMulCompute(outptr0, inpbuf_task, cbuf, conv, HkWkCg, k0, k1, yx0, yx1, out_planesize, g, - Kg, Kg_aligned, partial0, activ, minval, maxval, ifMinMaxAct); + v_store(outptr + j, v0); + v_store(outptr + j + 4, v1); + } + } + else + { + for (; j + 7 < out_width; j += 8) + { + v_float32x4 v0 = v_add(v_load(cptr + j), vbias); + v_float32x4 v1 = v_add(v_load(cptr + j + 4), vbias); + + if (ifMinMaxAct) + { + v0 = v_min(v_max(v0, vmin), vmax); + v1 = v_min(v_max(v1, vmin), vmax); + } + + v_store(outptr + j, v0); + v_store(outptr + j + 4, v1); + } + } +#endif + if (pbptr) { + for (; j < out_width; j++) + { + float v = cptr[j] + biasval; + v += pbptr[j]; + if (ifMinMaxAct) + v = std::min(std::max(v, minval), maxval); + outptr[j] = v; + } + } + else + { + for (; j < out_width; j++) + { + float v = cptr[j] + biasval; + + if (ifMinMaxAct) + v = std::min(std::max(v, minval), maxval); + outptr[j] = v; + } + } + + if (activ) + activ->forwardSlice(outptr, outptr, out_width, out_planesize, Kg * g + k, Kg * g + k + 1); } } } - }); + } } + }); } - -}} // namespace cv::dnn \ No newline at end of file +}} // namespace cv::dnn diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index c993781f5f..eda5c7ce26 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -7,20 +7,26 @@ #include "opencv2/core/hal/intrin.hpp" -#ifndef FAST_CONV_PRAM -#define FAST_CONV_PRAM +#ifndef CONV_PRAM +#define CONV_PRAM #if CV_NEON && CV_NEON_AARCH64 // 32 registers. -#define FAST_CONV_MR 4 -#define FAST_CONV_NR 28 +#define CONV_MR 4 +#define CONV_NR 28 enum { FAST_VEC_NLANES=4 }; #elif CV_NEON // 16 registers. -#define FAST_CONV_MR 4 -#define FAST_CONV_NR 12 +#define CONV_MR 4 +#define CONV_NR 12 enum { FAST_VEC_NLANES=4 }; #else // SIMD 128, AVX or AVX2 -#define FAST_CONV_MR 4 -#define FAST_CONV_NR 24 -enum { FAST_VEC_NLANES=4 }; +#define CONV_MR 4 +#define CONV_NR 24 + +#ifdef CV_AVX2 +enum { FAST_VEC_NLANES=8 }; // AVX2 +#else +enum { FAST_VEC_NLANES=4 }; // SIMD 128 +#endif + #endif #endif @@ -37,7 +43,6 @@ struct FastConv2d std::vector weightsBuf; // For generic Conv 2D std::vector weightsWino63Buf; // For Winograd F(6x6, 3x3). - std::vector biasBuf; bool ifWinograd63 = false; bool useAVX2 = checkHardwareSupport(CPU_AVX2); @@ -52,20 +57,20 @@ Ptr initFastConv2d( int dilation_x, int dilation_y, const std::vector& pads_begin, const std::vector& pads_end, - float* srcWeights, + InputArray weightsMat, float* srcBias); // It contains different computing branches, like winograd, 1x1 conv. -void runFastConv2d(InputArray _input, OutputArray _output, - const Ptr& conv, int ntasks, const Ptr& actLayer); +void runFastConv2d(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, + const Ptr& actLayer, bool fusedAdd); void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); // winograd init -void initWinograd63(Ptr& conv, float* src_weight, int K, int C); +void initWinograd63(Ptr& conv, InputArray weightsMat, int K, int C); -int runWinograd63(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); } // namespace dnn @@ -73,9 +78,7 @@ int runWinograd63(InputArray _input, OutputArray _output, const Ptr& namespace opt_AVX2 { #if CV_TRY_AVX2 -void convBlock_AVX2(int k, const float *a, const float *b, - float *c, int ldc, const float *bias, - float minval, float maxval, bool ifActiv); +void convBlock_AVX2(int np, const float* a, const float* b, float* c, int ldc, bool init_c); void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab, float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left, diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp index 94b08141e9..2e088a660b 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp @@ -11,140 +11,131 @@ namespace cv { namespace dnn { -void convBlock(int k, const float *a, const float *b, - float *c, int ldc, const float *bias, - float minval, float maxval, bool ifActiv) +void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { -#if CV_SIMD128 -#if FAST_CONV_MR == 4 && FAST_CONV_NR == 24 +#if 0 // CV_SIMD128 && CONV_MR == 4 && CONV_NR == 24 + v_float32x4 c0 = v_setzero_f32(), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0; + v_float32x4 c6 = v_setzero_f32(), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6; + v_float32x4 c12 = v_setzero_f32(), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12; + v_float32x4 c18 = v_setzero_f32(), c19 = c18, c20 = c18, c21 = c18, c22 = c18, c23 = c18; + + for (int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR) { - v_float32x4 c0 = v_setall_f32(bias[0]), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0; - v_float32x4 c6 = v_setall_f32(bias[1]), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6; - v_float32x4 c12 = v_setall_f32(bias[2]), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12; - v_float32x4 c18 = v_setall_f32(bias[3]), c19 = c18, c20 = c18, c21 = c18, c22 = c18, c23 = c18; + v_float32x4 a0 = v_setall_f32(a[0]); + v_float32x4 b0 = v_load(b), b1 = v_load(b + 4), b2 = v_load(b + 8); + v_float32x4 b3 = v_load(b + 12), b4 = v_load(b + 16), b5 = v_load(b + 20); - for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR) - { - v_float32x4 a0 = v_setall_f32(a[0]); - v_float32x4 b0 = v_load(b), b1 = v_load(b + 4), b2 = v_load(b + 8); - v_float32x4 b3 = v_load(b + 12), b4 = v_load(b + 16), b5 = v_load(b + 20); + c0 = v_fma(b0, a0, c0); + c1 = v_fma(b1, a0, c1); + c2 = v_fma(b2, a0, c2); + c3 = v_fma(b3, a0, c3); + c4 = v_fma(b4, a0, c4); + c5 = v_fma(b5, a0, c5); - c0 = v_fma(b0, a0, c0); - c1 = v_fma(b1, a0, c1); - c2 = v_fma(b2, a0, c2); - c3 = v_fma(b3, a0, c3); - c4 = v_fma(b4, a0, c4); - c5 = v_fma(b5, a0, c5); + a0 = v_setall_f32(a[1]); + c6 = v_fma(b0, a0, c6); + c7 = v_fma(b1, a0, c7); + c8 = v_fma(b2, a0, c8); + c9 = v_fma(b3, a0, c9); + c10 = v_fma(b4, a0, c10); + c11 = v_fma(b5, a0, c11); - a0 = v_setall_f32(a[1]); - c6 = v_fma(b0, a0, c6); - c7 = v_fma(b1, a0, c7); - c8 = v_fma(b2, a0, c8); - c9 = v_fma(b3, a0, c9); - c10 = v_fma(b4, a0, c10); - c11 = v_fma(b5, a0, c11); + a0 = v_setall_f32(a[2]); + c12 = v_fma(b0, a0, c12); + c13 = v_fma(b1, a0, c13); + c14 = v_fma(b2, a0, c14); + c15 = v_fma(b3, a0, c15); + c16 = v_fma(b4, a0, c16); + c17 = v_fma(b5, a0, c17); - a0 = v_setall_f32(a[2]); - c12 = v_fma(b0, a0, c12); - c13 = v_fma(b1, a0, c13); - c14 = v_fma(b2, a0, c14); - c15 = v_fma(b3, a0, c15); - c16 = v_fma(b4, a0, c16); - c17 = v_fma(b5, a0, c17); - - a0 = v_setall_f32(a[3]); - c18 = v_fma(b0, a0, c18); - c19 = v_fma(b1, a0, c19); - c20 = v_fma(b2, a0, c20); - c21 = v_fma(b3, a0, c21); - c22 = v_fma(b4, a0, c22); - c23 = v_fma(b5, a0, c23); - } - - if (ifActiv) { - v_float32x4 vmin = v_setall_f32(minval), vmax = v_setall_f32(maxval); - c0 = v_min(v_max(c0, vmin), vmax); - c1 = v_min(v_max(c1, vmin), vmax); - c2 = v_min(v_max(c2, vmin), vmax); - c3 = v_min(v_max(c3, vmin), vmax); - c4 = v_min(v_max(c4, vmin), vmax); - c5 = v_min(v_max(c5, vmin), vmax); - c6 = v_min(v_max(c6, vmin), vmax); - c7 = v_min(v_max(c7, vmin), vmax); - c8 = v_min(v_max(c8, vmin), vmax); - c9 = v_min(v_max(c9, vmin), vmax); - c10 = v_min(v_max(c10, vmin), vmax); - c11 = v_min(v_max(c11, vmin), vmax); - c12 = v_min(v_max(c12, vmin), vmax); - c13 = v_min(v_max(c13, vmin), vmax); - c14 = v_min(v_max(c14, vmin), vmax); - c15 = v_min(v_max(c15, vmin), vmax); - c16 = v_min(v_max(c16, vmin), vmax); - c17 = v_min(v_max(c17, vmin), vmax); - c18 = v_min(v_max(c18, vmin), vmax); - c19 = v_min(v_max(c19, vmin), vmax); - c20 = v_min(v_max(c20, vmin), vmax); - c21 = v_min(v_max(c21, vmin), vmax); - c22 = v_min(v_max(c22, vmin), vmax); - c23 = v_min(v_max(c23, vmin), vmax); - } - v_store(c, c0); - v_store(c + 4, c1); - v_store(c + 8, c2); - v_store(c + 12, c3); - v_store(c + 16, c4); - v_store(c + 20, c5); - - v_store(c + ldc, c6); - v_store(c + ldc + 4, c7); - v_store(c + ldc + 8, c8); - v_store(c + ldc + 12, c9); - v_store(c + ldc + 16, c10); - v_store(c + ldc + 20, c11); - - v_store(c + ldc * 2, c12); - v_store(c + ldc * 2 + 4, c13); - v_store(c + ldc * 2 + 8, c14); - v_store(c + ldc * 2 + 12, c15); - v_store(c + ldc * 2 + 16, c16); - v_store(c + ldc * 2 + 20, c17); - - v_store(c + ldc * 3, c18); - v_store(c + ldc * 3 + 4, c19); - v_store(c + ldc * 3 + 8, c20); - v_store(c + ldc * 3 + 12, c21); - v_store(c + ldc * 3 + 16, c22); - v_store(c + ldc * 3 + 20, c23); + a0 = v_setall_f32(a[3]); + c18 = v_fma(b0, a0, c18); + c19 = v_fma(b1, a0, c19); + c20 = v_fma(b2, a0, c20); + c21 = v_fma(b3, a0, c21); + c22 = v_fma(b4, a0, c22); + c23 = v_fma(b5, a0, c23); } -#endif + + if (!init_c) + { + c0 = v_add(c0, v_load(c)); + c1 = v_add(c1, v_load(c + 4)); + c2 = v_add(c2, v_load(c + 8)); + c3 = v_add(c3, v_load(c + 12)); + c4 = v_add(c4, v_load(c + 16)); + c5 = v_add(c5, v_load(c + 20)); + + c6 = v_add(c6 , v_load(c + ldc)); + c7 = v_add(c7 , v_load(c + ldc + 4)); + c8 = v_add(c8 , v_load(c + ldc + 8)); + c9 = v_add(c9 , v_load(c + ldc + 12)); + c10 = v_add(c10, v_load(c + ldc + 16)); + c11 = v_add(c11, v_load(c + ldc + 20)); + + c12 = v_add(c12, v_load(c + ldc*2)); + c13 = v_add(c13, v_load(c + ldc*2 + 4)); + c14 = v_add(c14, v_load(c + ldc*2 + 8)); + c15 = v_add(c15, v_load(c + ldc*2 + 12)); + c16 = v_add(c16, v_load(c + ldc*2 + 16)); + c17 = v_add(c17, v_load(c + ldc*2 + 20)); + + c18 = v_add(c18, v_load(c + ldc*3)); + c19 = v_add(c19, v_load(c + ldc*3 + 4)); + c20 = v_add(c20, v_load(c + ldc*3 + 8)); + c21 = v_add(c21, v_load(c + ldc*3 + 12)); + c22 = v_add(c22, v_load(c + ldc*3 + 16)); + c23 = v_add(c23, v_load(c + ldc*3 + 20)); + } + + v_store(c, c0); + v_store(c + 4, c1); + v_store(c + 8, c2); + v_store(c + 12, c3); + v_store(c + 16, c4); + v_store(c + 20, c5); + + v_store(c + ldc, c6); + v_store(c + ldc + 4, c7); + v_store(c + ldc + 8, c8); + v_store(c + ldc + 12, c9); + v_store(c + ldc + 16, c10); + v_store(c + ldc + 20, c11); + + v_store(c + ldc * 2, c12); + v_store(c + ldc * 2 + 4, c13); + v_store(c + ldc * 2 + 8, c14); + v_store(c + ldc * 2 + 12, c15); + v_store(c + ldc * 2 + 16, c16); + v_store(c + ldc * 2 + 20, c17); + + v_store(c + ldc * 3, c18); + v_store(c + ldc * 3 + 4, c19); + v_store(c + ldc * 3 + 8, c20); + v_store(c + ldc * 3 + 12, c21); + v_store(c + ldc * 3 + 16, c22); + v_store(c + ldc * 3 + 20, c23); #else - for (int i = 0; i < FAST_CONV_MR; i++) + float cbuf[CONV_MR * CONV_NR]; + memset(cbuf, 0, sizeof(cbuf)); + for( int p = 0; p < np; p++ ) { - float beta = bias[i]; - for (int j = 0; j < FAST_CONV_NR; j++) - c[i*ldc + j] = beta; - } - for (int p = 0; p < k; p++) - { - for (int i = 0; i < FAST_CONV_MR; i++) + for( int i = 0; i < CONV_MR; i++ ) { - float alpha = a[FAST_CONV_MR*p + i]; - for (int j = 0; j < FAST_CONV_NR; j++) - { - c[i*ldc+j] += b[FAST_CONV_NR*p + j]*alpha; - } + float ai = a[CONV_MR*p + i]; + for( int j = 0; j < CONV_NR; j++ ) + cbuf[i * CONV_NR+j] += b[CONV_NR*p + j] * ai; } } - if (ifActiv) - { - for (int i = 0; i < FAST_CONV_MR; i++) - { - for (int j = 0; j < FAST_CONV_NR; j++) - { - float v = c[i*ldc + j]; - v = std::min(std::max(v, minval), maxval); - c[i*ldc + j] = v; - } + if (!init_c) { + for(int i = 0; i < CONV_MR; i++) { + for(int j = 0; j < CONV_NR; j++) + c[i*ldc + j] += cbuf[i*CONV_NR + j]; + } + } else { + for(int i = 0; i < CONV_MR; i++) { + for(int j = 0; j < CONV_NR; j++) + c[i*ldc + j] = cbuf[i*CONV_NR + j]; } } #endif @@ -154,142 +145,122 @@ void convBlock(int k, const float *a, const float *b, namespace opt_NEON { #if CV_TRY_NEON -void convBlock_NEON(int k, const float *a, const float *b, - float *c, int ldc, const float *bias, - float minval, float maxval, bool ifActiv) +void convBlock_NEON(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { -#if CV_NEON_AARCH64 && FAST_CONV_MR == 4 && FAST_CONV_NR == 28 // AARCH64 +#if CONV_MR == 4 && CONV_NR == 28 // AARCH64 { - float32x4_t c0 = vdupq_n_f32(bias[0]), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0, c24 = c0; - float32x4_t c6 = vdupq_n_f32(bias[1]), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6, c25 = c6; - float32x4_t c12 = vdupq_n_f32(bias[2]), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12, c26 = c12; - float32x4_t c18 = vdupq_n_f32(bias[3]), c19 = c18, c20 = c18, c21 = c18, c22 = c18, c23 = c18, c27 = c18; + float32x4_t c00 = vdupq_n_f32(0.f), c01 = c00, c02 = c00, c03 = c00, c04 = c00, c05 = c00, c06 = c00; + float32x4_t c10 = vdupq_n_f32(0.f), c11 = c10, c12 = c10, c13 = c10, c14 = c10, c15 = c10, c16 = c10; + float32x4_t c20 = vdupq_n_f32(0.f), c21 = c20, c22 = c20, c23 = c20, c24 = c20, c25 = c20, c26 = c20; + float32x4_t c30 = vdupq_n_f32(0.f), c31 = c30, c32 = c30, c33 = c30, c34 = c30, c35 = c30, c36 = c30; - float32x4_t a0 = vdupq_n_f32(0.0f); - float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f); - - for (int p = 0; p < k; p++, a += FAST_CONV_MR) + for( int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR ) { - a0 = vld1q_f32(a); - b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8); - b += 12; + float32x4_t a0 = vld1q_f32(a), b0, b1, b2; + b0 = vld1q_f32(b); b1 = vld1q_f32(b + 4); b2 = vld1q_f32(b + 8); - c0 = vfmaq_laneq_f32(c0, b0, a0, 0); - c1 = vfmaq_laneq_f32(c1, b1, a0, 0); - c2 = vfmaq_laneq_f32(c2, b2, a0, 0); - c6 = vfmaq_laneq_f32(c6, b0, a0, 1); - c7 = vfmaq_laneq_f32(c7, b1, a0, 1); - c8 = vfmaq_laneq_f32(c8, b2, a0, 1); - c12 = vfmaq_laneq_f32(c12, b0, a0, 2); - c13 = vfmaq_laneq_f32(c13, b1, a0, 2); - c14 = vfmaq_laneq_f32(c14, b2, a0, 2); - c18 = vfmaq_laneq_f32(c18, b0, a0, 3); - c19 = vfmaq_laneq_f32(c19, b1, a0, 3); - c20 = vfmaq_laneq_f32(c20, b2, a0, 3); + c00 = vfmaq_laneq_f32(c00, b0, a0, 0); + c01 = vfmaq_laneq_f32(c01, b1, a0, 0); + c02 = vfmaq_laneq_f32(c02, b2, a0, 0); + c10 = vfmaq_laneq_f32(c10, b0, a0, 1); + c11 = vfmaq_laneq_f32(c11, b1, a0, 1); + c12 = vfmaq_laneq_f32(c12, b2, a0, 1); + c20 = vfmaq_laneq_f32(c20, b0, a0, 2); + c21 = vfmaq_laneq_f32(c21, b1, a0, 2); + c22 = vfmaq_laneq_f32(c22, b2, a0, 2); + c30 = vfmaq_laneq_f32(c30, b0, a0, 3); + c31 = vfmaq_laneq_f32(c31, b1, a0, 3); + c32 = vfmaq_laneq_f32(c32, b2, a0, 3); - b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8); - b += 12; + b0 = vld1q_f32(b + 12); b1 = vld1q_f32(b + 16); b2 = vld1q_f32(b + 20); - c3 = vfmaq_laneq_f32(c3, b0, a0, 0); - c4 = vfmaq_laneq_f32(c4, b1, a0, 0); - c5 = vfmaq_laneq_f32(c5, b2, a0, 0); + c03 = vfmaq_laneq_f32(c03, b0, a0, 0); + c04 = vfmaq_laneq_f32(c04, b1, a0, 0); + c05 = vfmaq_laneq_f32(c05, b2, a0, 0); + c13 = vfmaq_laneq_f32(c13, b0, a0, 1); + c14 = vfmaq_laneq_f32(c14, b1, a0, 1); + c15 = vfmaq_laneq_f32(c15, b2, a0, 1); + c23 = vfmaq_laneq_f32(c23, b0, a0, 2); + c24 = vfmaq_laneq_f32(c24, b1, a0, 2); + c25 = vfmaq_laneq_f32(c25, b2, a0, 2); + c33 = vfmaq_laneq_f32(c33, b0, a0, 3); + c34 = vfmaq_laneq_f32(c34, b1, a0, 3); + c35 = vfmaq_laneq_f32(c35, b2, a0, 3); - c9 = vfmaq_laneq_f32(c9, b0, a0, 1); - c10 = vfmaq_laneq_f32(c10, b1, a0, 1); - c11 = vfmaq_laneq_f32(c11, b2, a0, 1); - - c15 = vfmaq_laneq_f32(c15, b0, a0, 2); - c16 = vfmaq_laneq_f32(c16, b1, a0, 2); - c17 = vfmaq_laneq_f32(c17, b2, a0, 2); - - c21 = vfmaq_laneq_f32(c21, b0, a0, 3); - - b0 = vld1q_f32(b); - b += 4; - - c22 = vfmaq_laneq_f32(c22, b1, a0, 3); - c23 = vfmaq_laneq_f32(c23, b2, a0, 3); - - c24 = vfmaq_laneq_f32(c24, b0, a0, 0); - c25 = vfmaq_laneq_f32(c25, b0, a0, 1); + b0 = vld1q_f32(b + 24); + c06 = vfmaq_laneq_f32(c06, b0, a0, 0); + c16 = vfmaq_laneq_f32(c16, b0, a0, 1); c26 = vfmaq_laneq_f32(c26, b0, a0, 2); - c27 = vfmaq_laneq_f32(c27, b0, a0, 3); + c36 = vfmaq_laneq_f32(c36, b0, a0, 3); } - if (ifActiv) { - b0 = vdupq_n_f32(minval), b1 = vdupq_n_f32(maxval); - c0 = vminq_f32(vmaxq_f32(c0, b0), b1); - c1 = vminq_f32(vmaxq_f32(c1, b0), b1); - c2 = vminq_f32(vmaxq_f32(c2, b0), b1); - c3 = vminq_f32(vmaxq_f32(c3, b0), b1); - c4 = vminq_f32(vmaxq_f32(c4, b0), b1); - c5 = vminq_f32(vmaxq_f32(c5, b0), b1); - c6 = vminq_f32(vmaxq_f32(c6, b0), b1); - c7 = vminq_f32(vmaxq_f32(c7, b0), b1); - c8 = vminq_f32(vmaxq_f32(c8, b0), b1); - c9 = vminq_f32(vmaxq_f32(c9, b0), b1); - c10 = vminq_f32(vmaxq_f32(c10, b0), b1); - c11 = vminq_f32(vmaxq_f32(c11, b0), b1); - c12 = vminq_f32(vmaxq_f32(c12, b0), b1); - c13 = vminq_f32(vmaxq_f32(c13, b0), b1); - c14 = vminq_f32(vmaxq_f32(c14, b0), b1); - c15 = vminq_f32(vmaxq_f32(c15, b0), b1); - c16 = vminq_f32(vmaxq_f32(c16, b0), b1); - c17 = vminq_f32(vmaxq_f32(c17, b0), b1); - c18 = vminq_f32(vmaxq_f32(c18, b0), b1); - c19 = vminq_f32(vmaxq_f32(c19, b0), b1); - c20 = vminq_f32(vmaxq_f32(c20, b0), b1); - c21 = vminq_f32(vmaxq_f32(c21, b0), b1); - c22 = vminq_f32(vmaxq_f32(c22, b0), b1); - c23 = vminq_f32(vmaxq_f32(c23, b0), b1); - c24 = vminq_f32(vmaxq_f32(c24, b0), b1); - c25 = vminq_f32(vmaxq_f32(c25, b0), b1); - c26 = vminq_f32(vmaxq_f32(c26, b0), b1); - c27 = vminq_f32(vmaxq_f32(c27, b0), b1); + if (!init_c) + { + c00 = vaddq_f32(c00, vld1q_f32(c)); + c01 = vaddq_f32(c01, vld1q_f32(c + 4)); + c02 = vaddq_f32(c02, vld1q_f32(c + 8)); + c03 = vaddq_f32(c03, vld1q_f32(c + 12)); + c04 = vaddq_f32(c04, vld1q_f32(c + 16)); + c05 = vaddq_f32(c05, vld1q_f32(c + 20)); + c06 = vaddq_f32(c06, vld1q_f32(c + 24)); + + c10 = vaddq_f32(c10, vld1q_f32(c + ldc)); + c11 = vaddq_f32(c11, vld1q_f32(c + ldc + 4)); + c12 = vaddq_f32(c12, vld1q_f32(c + ldc + 8)); + c13 = vaddq_f32(c13, vld1q_f32(c + ldc + 12)); + c14 = vaddq_f32(c14, vld1q_f32(c + ldc + 16)); + c15 = vaddq_f32(c15, vld1q_f32(c + ldc + 20)); + c16 = vaddq_f32(c16, vld1q_f32(c + ldc + 24)); + + c20 = vaddq_f32(c20, vld1q_f32(c + ldc*2)); + c21 = vaddq_f32(c21, vld1q_f32(c + ldc*2 + 4)); + c22 = vaddq_f32(c22, vld1q_f32(c + ldc*2 + 8)); + c23 = vaddq_f32(c23, vld1q_f32(c + ldc*2 + 12)); + c24 = vaddq_f32(c24, vld1q_f32(c + ldc*2 + 16)); + c25 = vaddq_f32(c25, vld1q_f32(c + ldc*2 + 20)); + c26 = vaddq_f32(c26, vld1q_f32(c + ldc*2 + 24)); + + c30 = vaddq_f32(c30, vld1q_f32(c + ldc*3)); + c31 = vaddq_f32(c31, vld1q_f32(c + ldc*3 + 4)); + c32 = vaddq_f32(c32, vld1q_f32(c + ldc*3 + 8)); + c33 = vaddq_f32(c33, vld1q_f32(c + ldc*3 + 12)); + c34 = vaddq_f32(c34, vld1q_f32(c + ldc*3 + 16)); + c35 = vaddq_f32(c35, vld1q_f32(c + ldc*3 + 20)); + c36 = vaddq_f32(c36, vld1q_f32(c + ldc*3 + 24)); } - vst1q_f32(c, c0); - vst1q_f32(c + 4, c1); - vst1q_f32(c + 8, c2); - vst1q_f32(c + 12, c3); - vst1q_f32(c + 16, c4); - vst1q_f32(c + 20, c5); - vst1q_f32(c + 24, c24); - vst1q_f32(c + ldc, c6); - vst1q_f32(c + ldc + 4, c7); - vst1q_f32(c + ldc + 8, c8); - vst1q_f32(c + ldc + 12, c9); - vst1q_f32(c + ldc + 16, c10); - vst1q_f32(c + ldc + 20, c11); - vst1q_f32(c + ldc + 24, c25); + vst1q_f32(c, c00); vst1q_f32(c+4, c01); + vst1q_f32(c+8, c02); vst1q_f32(c+12, c03); + vst1q_f32(c+16, c04); vst1q_f32(c+20, c05); + vst1q_f32(c+24, c06); - vst1q_f32(c + ldc * 2, c12); - vst1q_f32(c + ldc * 2 + 4, c13); - vst1q_f32(c + ldc * 2 + 8, c14); - vst1q_f32(c + ldc * 2 + 12, c15); - vst1q_f32(c + ldc * 2 + 16, c16); - vst1q_f32(c + ldc * 2 + 20, c17); - vst1q_f32(c + ldc * 2 + 24, c26); + vst1q_f32(c+ldc, c10); vst1q_f32(c+ldc+4, c11); + vst1q_f32(c+ldc+8, c12); vst1q_f32(c+ldc+12, c13); + vst1q_f32(c+ldc+16, c14); vst1q_f32(c+ldc+20, c15); + vst1q_f32(c+ldc+24, c16); - vst1q_f32(c + ldc * 3, c18); - vst1q_f32(c + ldc * 3 + 4, c19); - vst1q_f32(c + ldc * 3 + 8, c20); - vst1q_f32(c + ldc * 3 + 12, c21); - vst1q_f32(c + ldc * 3 + 16, c22); - vst1q_f32(c + ldc * 3 + 20, c23); - vst1q_f32(c + ldc * 3 + 24, c27); + vst1q_f32(c+ldc*2, c20); vst1q_f32(c+ldc*2+4, c21); + vst1q_f32(c+ldc*2+8, c22); vst1q_f32(c+ldc*2+12, c23); + vst1q_f32(c+ldc*2+16, c24); vst1q_f32(c+ldc*2+20, c25); + vst1q_f32(c+ldc*2+24, c26); + + vst1q_f32(c+ldc*3, c30); vst1q_f32(c+ldc*3+4, c31); + vst1q_f32(c+ldc*3+8, c32); vst1q_f32(c+ldc*3+12, c33); + vst1q_f32(c+ldc*3+16, c34); vst1q_f32(c+ldc*3+20, c35); + vst1q_f32(c+ldc*3+24, c36); } -#elif (!defined(CV_NEON_AARCH64) || !CV_NEON_AARCH64) && FAST_CONV_MR == 4 && FAST_CONV_NR == 12 // ARMv7 +#elif CONV_MR == 4 && CONV_NR == 12 // ARMv7 { - float32x4_t c0 = vdupq_n_f32(bias[0]), c1 = c0, c2 = c0; - float32x4_t c3 = vdupq_n_f32(bias[1]), c4 = c3, c5 = c3; - float32x4_t c6 = vdupq_n_f32(bias[2]), c7 = c6, c8 = c6; - float32x4_t c9 = vdupq_n_f32(bias[3]), c10 = c9, c11 = c9; + float32x4_t c0 = vdupq_n_f32(0.f), c1 = c0, c2 = c0; + float32x4_t c3 = vdupq_n_f32(0.f), c4 = c3, c5 = c3; + float32x4_t c6 = vdupq_n_f32(0.f), c7 = c6, c8 = c6; + float32x4_t c9 = vdupq_n_f32(0.f), c10 = c9, c11 = c9; + float32x2_t a0 = vdup_n_f32(0.0f), a1 = a0; float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f); - for (int p = 0; p < k; p++, a += FAST_CONV_MR, b += FAST_CONV_NR) + for (int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR) { a0 = vld1_f32(a), a1 = vld1_f32(a+2); b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8); @@ -311,29 +282,32 @@ void convBlock_NEON(int k, const float *a, const float *b, c11 = vmlaq_lane_f32(c11, b2, a1, 1); } - if (ifActiv) + if (!init_c) { - b0 = vdupq_n_f32(minval), b1 = vdupq_n_f32(maxval); - c0 = vminq_f32(vmaxq_f32(c0, b0), b1); - c1 = vminq_f32(vmaxq_f32(c1, b0), b1); - c2 = vminq_f32(vmaxq_f32(c2, b0), b1); - c3 = vminq_f32(vmaxq_f32(c3, b0), b1); - c4 = vminq_f32(vmaxq_f32(c4, b0), b1); - c5 = vminq_f32(vmaxq_f32(c5, b0), b1); - c6 = vminq_f32(vmaxq_f32(c6, b0), b1); - c7 = vminq_f32(vmaxq_f32(c7, b0), b1); - c8 = vminq_f32(vmaxq_f32(c8, b0), b1); - c9 = vminq_f32(vmaxq_f32(c9, b0), b1); - c10 = vminq_f32(vmaxq_f32(c10, b0), b1); - c11 = vminq_f32(vmaxq_f32(c11, b0), b1); + c0 = vaddq_f32(c0, vld1q_f32(c)); + c1 = vaddq_f32(c1, vld1q_f32(c + 4)); + c2 = vaddq_f32(c2, vld1q_f32(c + 8)); + + c3 = vaddq_f32(c3, vld1q_f32(c + ldc)); + c4 = vaddq_f32(c4, vld1q_f32(c + ldc + 4)); + c5 = vaddq_f32(c5, vld1q_f32(c + ldc + 8)); + + c6 = vaddq_f32(c6, vld1q_f32(c + ldc * 2)); + c7 = vaddq_f32(c7, vld1q_f32(c + ldc * 2 + 4)); + c8 = vaddq_f32(c8, vld1q_f32(c + ldc * 2 + 8)); + + c9 = vaddq_f32(c9 , vld1q_f32(c + ldc * 3)); + c10 = vaddq_f32(c10, vld1q_f32(c + ldc * 3 + 4)); + c11 = vaddq_f32(c11, vld1q_f32(c + ldc * 3 + 8)); } - vst1q_f32(c, c0); vst1q_f32(c+4, c1); vst1q_f32(c+8, c2); - vst1q_f32(c + ldc, c3); vst1q_f32(c + ldc + 4, c4); vst1q_f32(c + ldc + 8, c5); - vst1q_f32(c + ldc*2, c6); vst1q_f32(c + ldc*2 + 4, c7); vst1q_f32(c + ldc*2 + 8, c8); - vst1q_f32(c + ldc*3, c9); vst1q_f32(c + ldc*3 + 4, c10); vst1q_f32(c + ldc*3 + 8, c11); + + vst1q_f32(c, c0), vst1q_f32(c+4, c1), vst1q_f32(c+8, c2); + vst1q_f32(c + ldc, c3), vst1q_f32(c + ldc + 4, c4), vst1q_f32(c + ldc + 8, c5); + vst1q_f32(c + ldc*2, c6), vst1q_f32(c + ldc*2 + 4, c7), vst1q_f32(c + ldc*2 + 8, c8); + vst1q_f32(c + ldc*3, c9), vst1q_f32(c + ldc*3 + 4, c10), vst1q_f32(c + ldc*3 + 8, c11); } -#else -#error "unsupported FAST_CONV_MR and/or FAST_CONV_NR in convBlock_NEON." +//#else +//#error "unsupported CONV_MR and/or CONV_NR in convBlock_NEON." #endif } #endif diff --git a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp index e841889007..fb668d63c6 100644 --- a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp +++ b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp @@ -37,6 +37,47 @@ enum }; #if CV_NEON + +#undef _FAST_CONV_T4x4 +#define _FAST_CONV_T4x4(a, b, c, d, tr0, tr1) \ + tr0 = vtrnq_f32(a, b); \ + tr1 = vtrnq_f32(c, d); \ + a = vcombine_f32(vget_low_f32(tr0.val[0]), vget_low_f32(tr1.val[0])); \ + b = vcombine_f32(vget_low_f32(tr0.val[1]), vget_low_f32(tr1.val[1])); \ + c = vcombine_f32(vget_high_f32(tr0.val[0]), vget_high_f32(tr1.val[0])); \ + d = vcombine_f32(vget_high_f32(tr0.val[1]), vget_high_f32(tr1.val[1])) + +// The input is the pack4 data, and the output is unpack4 data. +static void transpose12x4(float* src, float* dst, const int cn) +{ + float32x4_t r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, r10, r11; + float32x4x2_t tr0, tr1; + for (int i = 0; i < cn; i++, src += 48, dst += 48) + { + r00 = vld1q_f32(src); + r01 = vld1q_f32(src + 4); + r02 = vld1q_f32(src + 8); + r03 = vld1q_f32(src + 12); + r04 = vld1q_f32(src + 16); + r05 = vld1q_f32(src + 20); + r06 = vld1q_f32(src + 24); + r07 = vld1q_f32(src + 28); + r08 = vld1q_f32(src + 32); + r09 = vld1q_f32(src + 36); + r10 = vld1q_f32(src + 40); + r11 = vld1q_f32(src + 44); + + _FAST_CONV_T4x4(r00, r01, r02, r03, tr0, tr1); + _FAST_CONV_T4x4(r04, r05, r06, r07, tr0, tr1); + _FAST_CONV_T4x4(r08, r09, r10, r11, tr0, tr1); + + vst1q_f32(dst, r00), vst1q_f32(dst + 4, r04), vst1q_f32(dst + 8, r08); + vst1q_f32(dst + 12, r01), vst1q_f32(dst + 16, r05), vst1q_f32(dst + 20, r09); + vst1q_f32(dst + 24, r02), vst1q_f32(dst + 28, r06), vst1q_f32(dst + 32, r10); + vst1q_f32(dst + 36, r03), vst1q_f32(dst + 40, r07), vst1q_f32(dst + 44, r11); + } +} + static void winograd_trans_input_F63(float* src, float* dst, int Channle_div4, const int tiles, const int big_step, const int line_step, const int* ofstab0) { // const float itm[8][8] = { @@ -192,15 +233,14 @@ static void winograd_trans_input_F63(float* src, float* dst, int Channle_div4, c float* input0 = input_buf0 + 4 * tiles * r; // TODO! support tiles > 12 -//#if CV_NEON_AARCH64 -// for (; ti + 11 < tiles; ti += 12) -// { -// float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; -//// std::cout<<"ofstab0[ti * 2] = "< convLayer = ld.layerInstance.dynamicCast(); + + // Only Conv2D without fusion Activation supports this fusion, other-wise, we skip. + if (!convLayer->isConv2D || convLayer->fusedActivation) + break; + + // For now, there are currently two layers in OpenCV that run the Add operator. + Ptr nextNaryEltwiseLayer = nextData->layerInstance.dynamicCast(); + Ptr nextEltwiseLayer = nextData->layerInstance.dynamicCast(); + if (nextNaryEltwiseLayer.empty() && nextEltwiseLayer.empty()) + break; + + if (nextData->inputBlobsId.size() != 2) + break; + + if (!nextData->params.has("operation") || toLowerCase(nextData->params.get("operation")) != "add") + { + CV_LOG_DEBUG(NULL, "DNN/CPU: fusion with NaryEltwise or Eltwise Layer operation is not supported: " + << nextData->params.get("operation")); + break; + } + + // This optimization is for cases like + // some_layer conv + // | | + // +-- eltwise or (naryEltwise) --+ + // | + // activ + // This way all the element-wise computations + // (i.e. some_layer+conv) would be done at [conv] layer. + // So we need to replace [conv]'s output blob to [eltwise]'s one + // considering that [activ] is an in-place layer. + // Also we need to move all the consumers' references. + // To prevent memory collisions (i.e. when input of + // [conv] and output of [eltwise or naryEltwise] is the same blob) + // we allocate a new blob. + { + LayerData *naryOrEltwiseData = nextData; + + // Eltwise or NaryEltwise layer has two inputs. We need to determine which + // is a base convolution layer and which could be used as it's bias. + LayerData* biasLayerData = 0; + for (int i = 0; i < 2; ++i) + { + LayerData *downLayerData = &layers[naryOrEltwiseData->inputBlobsId[i].lid]; + CV_Assert(downLayerData); + // If the current downLayerData is skip, it means it is fused into the parent node. + while (downLayerData->skip) + { + if (downLayerData->inputBlobsId.size() == 1) + downLayerData = &layers[downLayerData->inputBlobsId[0].lid]; + else + { + downLayerData = 0; + break; + } + } + + if (downLayerData && ld.id == downLayerData->id) + { + biasLayerData = &layers[naryOrEltwiseData->inputBlobsId[1 - i].lid]; + break; + } + } + + // We check if biasLayerData is expected layer. + if (!biasLayerData) + break; + + // We check if the bias output shape and the ld output shape are the same. + MatShape biasOutShape = shape(biasLayerData->outputBlobs[0]); + MatShape ldOutShape = shape(ld.outputBlobs[0]); + if (biasOutShape != ldOutShape) + break; + + CV_Assert(biasLayerData); + { + // fuse naryEltwise layer + // bias must already be computed to fuse => bias layer must appear before convolution + if (biasLayerData->id < ld.id) + { + // conv + naryEltwise. + CV_Assert_N(biasLayerData->outputBlobs.size() == 1, ld.inputBlobs.size() == 1); + CV_Assert_N(biasLayerData->outputBlobsWrappers.size() == 1, ld.inputBlobsWrappers.size() == 1); + + printf_(("\tfused with %s\n", nextNaryEltwiseLayer->name.c_str())); + naryOrEltwiseData->skip = true; + + + CV_Assert_N(ld.outputBlobs.size() == 1, ld.outputBlobsWrappers.size() == 1); + // Note: Here's a trick. We set the output of conv as the output of biasLayer. + ld.outputBlobs[0] = ld.outputBlobs[0].clone(); + ld.outputBlobsWrappers[0] = wrap(ld.outputBlobs[0]); + + // Recursively modifies the output data of biasLayerData and its parent. + std::vector skipDataList; + skipDataList.push_back(biasLayerData); + + while (!skipDataList.empty()) + { + LayerData* skipData = skipDataList.back(); + skipDataList.pop_back(); + + CV_Assert(skipData->outputBlobs.size() == 1); + skipData->outputBlobs[0] = ld.outputBlobs[0]; + skipData->outputBlobsWrappers[0] = ld.outputBlobsWrappers[0]; + if (skipData->skip) + { + for (auto& inputLayerId : skipData->inputLayersId) + { + LayerData* inputld = &layers[inputLayerId]; + + if (inputld && inputld->outputBlobs.size() == 1) + skipDataList.push_back(inputld); + } + } + } + + naryOrEltwiseData->outputBlobs = ld.outputBlobs; + naryOrEltwiseData->outputBlobsWrappers = ld.outputBlobsWrappers; + + // set the fusedAdd flag in [Conv]; + convLayer->fusedAdd = true; + LayerData* finalData = naryOrEltwiseData; + /* After fused Conv + naryEltwise or eltwise, we can fuse activation if: + * => activation layer that follows is the only consumer of eltwise output + * => activation layer does not process multiple inputs + * => we do not require to keep the output of eltwise + */ + if (naryOrEltwiseData->consumers.size() == 1) + { + Ptr nextFusabeleActivLayer; + LayerData* nextAct = &layers[naryOrEltwiseData->consumers[0].lid]; + + if (nextData->outputBlobs.size() == 1) + nextFusabeleActivLayer = nextAct->layerInstance.dynamicCast(); + + if (!nextFusabeleActivLayer.empty()) + { + convLayer->setActivation(nextFusabeleActivLayer); + nextAct->skip = true; + + nextAct->outputBlobs = ld.outputBlobs; + nextAct->outputBlobsWrappers = ld.outputBlobsWrappers; + } + } + + // Move references of finalData (eltwise or activation) layer consumers to the newly allocated blob. + for (int i = 0; i < finalData->consumers.size(); ++i) + { + LayerData& consumer = layers[finalData->consumers[i].lid]; + for (int j = 0; j < consumer.inputBlobsId.size(); ++j) + { + if (consumer.inputBlobsId[j].lid == finalData->id) + { + consumer.inputBlobs[j] = &ld.outputBlobs[0]; + consumer.inputBlobsWrappers[j] = ld.outputBlobsWrappers[0]; + break; + } + } + } + } + } + } + break; + } + // OpenCL: fuse convolution layer followed by eltwise + relu // CUDA: fuse convolution layer followed by eltwise (and optional activation) while (nextData && @@ -398,7 +570,7 @@ void Net::Impl::fuseLayers(const std::vector& blobsToKeep_) // (i.e. some_layer+conv or some_layer*conv) // would be done at [conv] layer. So we need to // replace [conv]'s output blob to [eltwise]'s one. - // Also we need to move all the consumers' references. + // Also, we need to move all the consumers' references. // To prevent memory collisions (i.e. when input of // [conv] and output of [eltwise] is the same blob) // we allocate a new blob. diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index ddaa51dcbf..3ea8d17102 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -284,7 +284,7 @@ TEST(Reproducibility_SSD, Accuracy) Mat out = net.forward("detection_out"); Mat ref = blobFromNPY(_tf("ssd_out.npy")); - normAssertDetections(ref, out, "", FLT_MIN); + normAssertDetections(ref, out, "", 0.06); } typedef testing::TestWithParam > Reproducibility_MobileNet_SSD; diff --git a/modules/dnn/test/test_int8_layers.cpp b/modules/dnn/test/test_int8_layers.cpp index 54db5a39ea..1cafa22619 100644 --- a/modules/dnn/test/test_int8_layers.cpp +++ b/modules/dnn/test/test_int8_layers.cpp @@ -1029,7 +1029,7 @@ TEST_P(Test_Int8_nets, FasterRCNN_resnet50) Mat blob = blobFromImage(inp, 1.0, Size(800, 600), Scalar(), true, false); Mat ref = blobFromNPY(_tf("tensorflow/faster_rcnn_resnet50_coco_2018_01_28.detection_out.npy")); - float confThreshold = 0.5, scoreDiff = 0.05, iouDiff = 0.15; + float confThreshold = 0.8, scoreDiff = 0.05, iouDiff = 0.15; testDetectionNet(net, blob, ref, confThreshold, scoreDiff, iouDiff); } diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index 520887480d..bd9572748b 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -488,7 +488,7 @@ TEST_P(Test_Torch_nets, ENet_accuracy) // Due to numerical instability in Pooling-Unpooling layers (indexes jittering) // thresholds for ENet must be changed. Accuracy of results was checked on // Cityscapes dataset and difference in mIOU with Torch is 10E-4% - normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.552); + normAssert(ref, out, "", 0.0005, /*target == DNN_TARGET_CPU ? 0.453 : */0.552); normAssertSegmentation(ref, out); const int N = 3; @@ -496,7 +496,7 @@ TEST_P(Test_Torch_nets, ENet_accuracy) { net.setInput(inputBlob, ""); Mat out = net.forward(); - normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.552); + normAssert(ref, out, "", 0.0005, /*target == DNN_TARGET_CPU ? 0.453 : */0.552); normAssertSegmentation(ref, out); } } From ed9d4c0b2be42fa2b1025b6a2efa576d0728e1db Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Fri, 26 Aug 2022 21:29:56 +0300 Subject: [PATCH 037/313] G-API: Update ADE to v0.1.2a to fix new Windows warnings --- modules/gapi/cmake/DownloadADE.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gapi/cmake/DownloadADE.cmake b/modules/gapi/cmake/DownloadADE.cmake index 7a54f62cae..3157436369 100644 --- a/modules/gapi/cmake/DownloadADE.cmake +++ b/modules/gapi/cmake/DownloadADE.cmake @@ -1,7 +1,7 @@ set(ade_src_dir "${OpenCV_BINARY_DIR}/3rdparty/ade") -set(ade_filename "v0.1.2.zip") -set(ade_subdir "ade-0.1.2") -set(ade_md5 "561c1e28ccf27ad0557a18e251c22226") +set(ade_filename "v0.1.2a.zip") +set(ade_subdir "ade-0.1.2a") +set(ade_md5 "fa4b3e25167319cb0fa9432ef8281945") ocv_download(FILENAME ${ade_filename} HASH ${ade_md5} URL From 9638e34ab054b9499875ec9ecdde7b4cfa907677 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Sat, 27 Aug 2022 07:42:38 +0800 Subject: [PATCH 038/313] reuse WORDS_BIGENDIAN. --- modules/dnn/src/onnx/onnx_graph_simplifier.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp index c787c0a321..d115e5706f 100644 --- a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp +++ b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp @@ -18,17 +18,6 @@ CV__DNN_INLINE_NS_BEGIN extern bool DNN_DIAGNOSTICS_RUN; -static int isLittleEndianCPU() -{ - int x = 7; - char *ptr = (char *)&x; - - if(ptr[0] == 0) - return 0; - else - return 1; -} - // This wrapper can behave differently for fake input nodes and real graph nodes. class ONNXNodeWrapper : public ImportNodeWrapper { @@ -787,7 +776,10 @@ Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto) // Link: https://github.com/onnx/onnx/issues/4460#issuecomment-1224373746 if (!tensor_proto.int32_data().empty()) { - const int offset = isLittleEndianCPU() ? 0 : 1; + int offset = 0; +#ifdef WORDS_BIGENDIAN + offset = 1; +#endif const ::google::protobuf::RepeatedField field = tensor_proto.int32_data(); AutoBuffer aligned_val; From 3929e26276c247660dcd5f2eb5ced04ad35e8711 Mon Sep 17 00:00:00 2001 From: Berke Date: Fri, 10 Jun 2022 23:23:42 +0300 Subject: [PATCH 039/313] spng encoder/decoder added as optional png codec --- 3rdparty/libspng/CMakeLists.txt | 47 + 3rdparty/libspng/LICENSE | 25 + 3rdparty/libspng/spng.c | 6978 ++++++++++++++++++++ 3rdparty/libspng/spng.h | 537 ++ CMakeLists.txt | 11 +- cmake/OpenCVFindLibsGrfmt.cmake | 16 +- cmake/templates/cvconfig.h.in | 3 + modules/highgui/src/window_w32.cpp | 4 +- modules/imgcodecs/CMakeLists.txt | 8 +- modules/imgcodecs/perf/perf_png.cpp | 41 + modules/imgcodecs/src/grfmt_spng.cpp | 754 +++ modules/imgcodecs/src/grfmt_spng.hpp | 60 + modules/imgcodecs/src/grfmts.hpp | 1 + modules/imgcodecs/src/loadsave.cpp | 5 +- modules/imgcodecs/test/test_grfmt.cpp | 2 +- modules/imgcodecs/test/test_png.cpp | 221 +- modules/imgcodecs/test/test_read_write.cpp | 4 +- modules/imgproc/test/test_drawing.cpp | 2 +- 18 files changed, 8707 insertions(+), 12 deletions(-) create mode 100644 3rdparty/libspng/CMakeLists.txt create mode 100644 3rdparty/libspng/LICENSE create mode 100644 3rdparty/libspng/spng.c create mode 100644 3rdparty/libspng/spng.h create mode 100644 modules/imgcodecs/perf/perf_png.cpp create mode 100644 modules/imgcodecs/src/grfmt_spng.cpp create mode 100644 modules/imgcodecs/src/grfmt_spng.hpp diff --git a/3rdparty/libspng/CMakeLists.txt b/3rdparty/libspng/CMakeLists.txt new file mode 100644 index 0000000000..afd6d5fe40 --- /dev/null +++ b/3rdparty/libspng/CMakeLists.txt @@ -0,0 +1,47 @@ +# ---------------------------------------------------------------------------- +# CMake file for libspng. See root CMakeLists.txt +# +# ---------------------------------------------------------------------------- + +project(${SPNG_LIBRARY}) + +set(CURR_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") +set_property(GLOBAL PROPERTY SPNG_INCLUDE_DIR ${CURR_INCLUDE_DIR}) +ocv_include_directories(${ZLIB_INCLUDE_DIRS}) + +file(GLOB_RECURSE spng_headers RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "*.h") +file(GLOB_RECURSE spng_sources RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "*.c") + +message(STATUS "libspng will be used as PNG codec") + +# ---------------------------------------------------------------------------------- +# Define the library target: +# ---------------------------------------------------------------------------------- + +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) +endif(MSVC) + +add_library(${SPNG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${spng_headers} ${spng_sources}) +ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-variable) +target_link_libraries(${SPNG_LIBRARY} ${ZLIB_LIBRARIES}) + +set_target_properties(${SPNG_LIBRARY} + PROPERTIES OUTPUT_NAME ${SPNG_LIBRARY} + DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" + COMPILE_PDB_NAME ${SPNG_LIBRARY} + COMPILE_PDB_NAME_DEBUG "${SPNG_LIBRARY}${OPENCV_DEBUG_POSTFIX}" + ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH} + ) + +target_compile_definitions(${SPNG_LIBRARY} PUBLIC SPNG_STATIC) + +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${SPNG_LIBRARY} PROPERTIES FOLDER "3rdparty") +endif() + +if(NOT BUILD_SHARED_LIBS) + ocv_install_target(${SPNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL) +endif() + +ocv_install_3rdparty_licenses(${SPNG_LIBRARY} LICENSE) diff --git a/3rdparty/libspng/LICENSE b/3rdparty/libspng/LICENSE new file mode 100644 index 0000000000..f96574b80d --- /dev/null +++ b/3rdparty/libspng/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2018-2022, Randy +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/3rdparty/libspng/spng.c b/3rdparty/libspng/spng.c new file mode 100644 index 0000000000..6ed60f2d6c --- /dev/null +++ b/3rdparty/libspng/spng.c @@ -0,0 +1,6978 @@ +/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */ + +#define SPNG__BUILD + +#include "spng.h" + +#include +#include +#include +#include + +#define ZLIB_CONST + +#ifdef __FRAMAC__ + #define SPNG_DISABLE_OPT + #include "tests/framac_stubs.h" +#else + #ifdef SPNG_USE_MINIZ + #include + #else + #include + #endif +#endif + +#ifdef SPNG_MULTITHREADING + #include +#endif + +/* Not build options, edit at your own risk! */ +#define SPNG_READ_SIZE (8192) +#define SPNG_WRITE_SIZE SPNG_READ_SIZE +#define SPNG_MAX_CHUNK_COUNT (1000) + +#define SPNG_TARGET_CLONES(x) + +#ifndef SPNG_DISABLE_OPT + + #if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) + #define SPNG_X86 + + #if defined(__x86_64__) || defined(_M_X64) + #define SPNG_X86_64 + #endif + + #elif defined(__aarch64__) || defined(_M_ARM64) /* || defined(__ARM_NEON) */ + #define SPNG_ARM /* NOTE: only arm64 builds are tested! */ + #else + #pragma message "disabling SIMD optimizations for unknown target" + #define SPNG_DISABLE_OPT + #endif + + #if defined(SPNG_X86_64) && defined(SPNG_ENABLE_TARGET_CLONES) + #undef SPNG_TARGET_CLONES + #define SPNG_TARGET_CLONES(x) __attribute__((target_clones(x))) + #else + #define SPNG_TARGET_CLONES(x) + #endif + + #ifndef SPNG_DISABLE_OPT + static void defilter_sub3(size_t rowbytes, unsigned char *row); + static void defilter_sub4(size_t rowbytes, unsigned char *row); + static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev); + static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev); + static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev); + static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev); + + #if defined(SPNG_ARM) + static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width); + static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width); + #endif + #endif +#endif + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4244) +#endif + +#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(__BIG_ENDIAN__) + #define SPNG_BIG_ENDIAN +#else + #define SPNG_LITTLE_ENDIAN +#endif + +enum spng_state +{ + SPNG_STATE_INVALID = 0, + SPNG_STATE_INIT = 1, /* No PNG buffer/stream is set */ + SPNG_STATE_INPUT, /* Decoder input PNG was set */ + SPNG_STATE_OUTPUT = SPNG_STATE_INPUT, /* Encoder output was set */ + SPNG_STATE_IHDR, /* IHDR was read/written */ + SPNG_STATE_FIRST_IDAT, /* Encoded up to / reached first IDAT */ + SPNG_STATE_DECODE_INIT, /* Decoder is ready for progressive reads */ + SPNG_STATE_ENCODE_INIT = SPNG_STATE_DECODE_INIT, + SPNG_STATE_EOI, /* Reached the last scanline/row */ + SPNG_STATE_LAST_IDAT, /* Reached last IDAT, set at end of decode_image() */ + SPNG_STATE_AFTER_IDAT, /* */ + SPNG_STATE_IEND, /* Reached IEND */ +}; + +enum spng__internal +{ + SPNG__IO_SIGNAL = 1 << 9, + SPNG__CTX_FLAGS_ALL = (SPNG_CTX_IGNORE_ADLER32 | SPNG_CTX_ENCODER) +}; + +#define SPNG_STR(x) _SPNG_STR(x) +#define _SPNG_STR(x) #x + +#define SPNG_VERSION_STRING SPNG_STR(SPNG_VERSION_MAJOR) "." \ + SPNG_STR(SPNG_VERSION_MINOR) "." \ + SPNG_STR(SPNG_VERSION_PATCH) + +#define SPNG_GET_CHUNK_BOILERPLATE(chunk) \ + if(ctx == NULL) return 1; \ + int ret = read_chunks(ctx, 0); \ + if(ret) return ret; \ + if(!ctx->stored.chunk) return SPNG_ECHUNKAVAIL; \ + if(chunk == NULL) return 1 + +#define SPNG_SET_CHUNK_BOILERPLATE(chunk) \ + if(ctx == NULL || chunk == NULL) return 1; \ + if(ctx->data == NULL && !ctx->encode_only) return SPNG_ENOSRC; \ + int ret = read_chunks(ctx, 0); \ + if(ret) return ret + +/* Determine if the spng_option can be overriden/optimized */ +#define spng__optimize(option) (ctx->optimize_option & (1 << option)) + +struct spng_subimage +{ + uint32_t width; + uint32_t height; + size_t out_width; /* byte width based on output format */ + size_t scanline_width; +}; + +struct spng_text2 +{ + int type; + char *keyword; + char *text; + + size_t text_length; + + uint8_t compression_flag; /* iTXt only */ + char *language_tag; /* iTXt only */ + char *translated_keyword; /* iTXt only */ + + size_t cache_usage; + char user_keyword_storage[80]; +}; + +struct decode_flags +{ + unsigned apply_trns: 1; + unsigned apply_gamma: 1; + unsigned use_sbit: 1; + unsigned indexed: 1; + unsigned do_scaling: 1; + unsigned interlaced: 1; + unsigned same_layout: 1; + unsigned zerocopy: 1; + unsigned unpack: 1; +}; + +struct encode_flags +{ + unsigned interlace: 1; + unsigned same_layout: 1; + unsigned to_bigendian: 1; + unsigned progressive: 1; + unsigned finalize: 1; + + enum spng_filter_choice filter_choice; +}; + +struct spng_chunk_bitfield +{ + unsigned ihdr: 1; + unsigned plte: 1; + unsigned chrm: 1; + unsigned iccp: 1; + unsigned gama: 1; + unsigned sbit: 1; + unsigned srgb: 1; + unsigned text: 1; + unsigned bkgd: 1; + unsigned hist: 1; + unsigned trns: 1; + unsigned phys: 1; + unsigned splt: 1; + unsigned time: 1; + unsigned offs: 1; + unsigned exif: 1; + unsigned unknown: 1; +}; + +/* Packed sample iterator */ +struct spng__iter +{ + const uint8_t mask; + unsigned shift_amount; + const unsigned initial_shift, bit_depth; + const unsigned char *samples; +}; + +union spng__decode_plte +{ + struct spng_plte_entry rgba[256]; + unsigned char rgb[256 * 3]; + unsigned char raw[256 * 4]; + uint32_t align_this; +}; + +struct spng__zlib_options +{ + int compression_level; + int window_bits; + int mem_level; + int strategy; + int data_type; +}; + +typedef void spng__undo(spng_ctx *ctx); + +struct spng_ctx +{ + size_t data_size; + size_t bytes_read; + size_t stream_buf_size; + unsigned char *stream_buf; + const unsigned char *data; + + /* User-defined pointers for streaming */ + spng_read_fn *read_fn; + spng_write_fn *write_fn; + void *stream_user_ptr; + + /* Used for buffer reads */ + const unsigned char *png_base; + size_t bytes_left; + size_t last_read_size; + + /* Used for encoding */ + int user_owns_out_png; + unsigned char *out_png; + unsigned char *write_ptr; + size_t out_png_size; + size_t bytes_encoded; + + /* These are updated by read/write_header()/read_chunk_bytes() */ + struct spng_chunk current_chunk; + uint32_t cur_chunk_bytes_left; + uint32_t cur_actual_crc; + + struct spng_alloc alloc; + + enum spng_ctx_flags flags; + enum spng_format fmt; + + enum spng_state state; + + unsigned streaming: 1; + unsigned internal_buffer: 1; /* encoding to internal buffer */ + + unsigned inflate: 1; + unsigned deflate: 1; + unsigned encode_only: 1; + unsigned strict: 1; + unsigned discard: 1; + unsigned skip_crc: 1; + unsigned keep_unknown: 1; + unsigned prev_was_idat: 1; + + struct spng__zlib_options image_options; + struct spng__zlib_options text_options; + + spng__undo *undo; + + /* input file contains this chunk */ + struct spng_chunk_bitfield file; + + /* chunk was stored with spng_set_*() */ + struct spng_chunk_bitfield user; + + /* chunk was stored by reading or with spng_set_*() */ + struct spng_chunk_bitfield stored; + + /* used to reset the above in case of an error */ + struct spng_chunk_bitfield prev_stored; + + struct spng_chunk first_idat, last_idat; + + uint32_t max_width, max_height; + + size_t max_chunk_size; + size_t chunk_cache_limit; + size_t chunk_cache_usage; + uint32_t chunk_count_limit; + uint32_t chunk_count_total; + + int crc_action_critical; + int crc_action_ancillary; + + uint32_t optimize_option; + + struct spng_ihdr ihdr; + + struct spng_plte plte; + + struct spng_chrm_int chrm_int; + struct spng_iccp iccp; + + uint32_t gama; + + struct spng_sbit sbit; + + uint8_t srgb_rendering_intent; + + uint32_t n_text; + struct spng_text2 *text_list; + + struct spng_bkgd bkgd; + struct spng_hist hist; + struct spng_trns trns; + struct spng_phys phys; + + uint32_t n_splt; + struct spng_splt *splt_list; + + struct spng_time time; + struct spng_offs offs; + struct spng_exif exif; + + uint32_t n_chunks; + struct spng_unknown_chunk *chunk_list; + + struct spng_subimage subimage[7]; + + z_stream zstream; + unsigned char *scanline_buf, *prev_scanline_buf, *row_buf, *filtered_scanline_buf; + unsigned char *scanline, *prev_scanline, *row, *filtered_scanline; + + /* based on fmt */ + size_t image_size; /* may be zero */ + size_t image_width; + + unsigned bytes_per_pixel; /* derived from ihdr */ + unsigned pixel_size; /* derived from spng_format+ihdr */ + int widest_pass; + int last_pass; /* last non-empty pass */ + + uint16_t *gamma_lut; /* points to either _lut8 or _lut16 */ + uint16_t *gamma_lut16; + uint16_t gamma_lut8[256]; + unsigned char trns_px[8]; + union spng__decode_plte decode_plte; + struct spng_sbit decode_sb; + struct decode_flags decode_flags; + struct spng_row_info row_info; + + struct encode_flags encode_flags; +}; + +static const uint32_t spng_u32max = INT32_MAX; + +static const uint32_t adam7_x_start[7] = { 0, 4, 0, 2, 0, 1, 0 }; +static const uint32_t adam7_y_start[7] = { 0, 0, 4, 0, 2, 0, 1 }; +static const uint32_t adam7_x_delta[7] = { 8, 8, 4, 4, 2, 2, 1 }; +static const uint32_t adam7_y_delta[7] = { 8, 8, 8, 4, 4, 2, 2 }; + +static const uint8_t spng_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + +static const uint8_t type_ihdr[4] = { 73, 72, 68, 82 }; +static const uint8_t type_plte[4] = { 80, 76, 84, 69 }; +static const uint8_t type_idat[4] = { 73, 68, 65, 84 }; +static const uint8_t type_iend[4] = { 73, 69, 78, 68 }; + +static const uint8_t type_trns[4] = { 116, 82, 78, 83 }; +static const uint8_t type_chrm[4] = { 99, 72, 82, 77 }; +static const uint8_t type_gama[4] = { 103, 65, 77, 65 }; +static const uint8_t type_iccp[4] = { 105, 67, 67, 80 }; +static const uint8_t type_sbit[4] = { 115, 66, 73, 84 }; +static const uint8_t type_srgb[4] = { 115, 82, 71, 66 }; +static const uint8_t type_text[4] = { 116, 69, 88, 116 }; +static const uint8_t type_ztxt[4] = { 122, 84, 88, 116 }; +static const uint8_t type_itxt[4] = { 105, 84, 88, 116 }; +static const uint8_t type_bkgd[4] = { 98, 75, 71, 68 }; +static const uint8_t type_hist[4] = { 104, 73, 83, 84 }; +static const uint8_t type_phys[4] = { 112, 72, 89, 115 }; +static const uint8_t type_splt[4] = { 115, 80, 76, 84 }; +static const uint8_t type_time[4] = { 116, 73, 77, 69 }; + +static const uint8_t type_offs[4] = { 111, 70, 70, 115 }; +static const uint8_t type_exif[4] = { 101, 88, 73, 102 }; + +static inline void *spng__malloc(spng_ctx *ctx, size_t size) +{ + return ctx->alloc.malloc_fn(size); +} + +static inline void *spng__calloc(spng_ctx *ctx, size_t nmemb, size_t size) +{ + return ctx->alloc.calloc_fn(nmemb, size); +} + +static inline void *spng__realloc(spng_ctx *ctx, void *ptr, size_t size) +{ + return ctx->alloc.realloc_fn(ptr, size); +} + +static inline void spng__free(spng_ctx *ctx, void *ptr) +{ + ctx->alloc.free_fn(ptr); +} + +#if defined(SPNG_USE_MINIZ) +static void *spng__zalloc(void *opaque, size_t items, size_t size) +#else +static void *spng__zalloc(void *opaque, uInt items, uInt size) +#endif +{ + spng_ctx *ctx = opaque; + + if(size > SIZE_MAX / items) return NULL; + + size_t len = (size_t)items * size; + + return spng__malloc(ctx, len); +} + +static void spng__zfree(void *opqaue, void *ptr) +{ + spng_ctx *ctx = opqaue; + spng__free(ctx, ptr); +} + +static inline uint16_t read_u16(const void *src) +{ + const unsigned char *data = src; + + return (data[0] & 0xFFU) << 8 | (data[1] & 0xFFU); +} + +static inline uint32_t read_u32(const void *src) +{ + const unsigned char *data = src; + + return (data[0] & 0xFFUL) << 24 | (data[1] & 0xFFUL) << 16 | + (data[2] & 0xFFUL) << 8 | (data[3] & 0xFFUL); +} + +static inline int32_t read_s32(const void *src) +{ + int32_t ret = (int32_t)read_u32(src); + + return ret; +} + +static inline void write_u16(void *dest, uint16_t x) +{ + unsigned char *data = dest; + + data[0] = x >> 8; + data[1] = x & 0xFF; +} + +static inline void write_u32(void *dest, uint32_t x) +{ + unsigned char *data = dest; + + data[0] = (x >> 24); + data[1] = (x >> 16) & 0xFF; + data[2] = (x >> 8) & 0xFF; + data[3] = x & 0xFF; +} + +static inline void write_s32(void *dest, int32_t x) +{ + uint32_t n = x; + write_u32(dest, n); +} + +/* Returns an iterator for 1,2,4,8-bit samples */ +static struct spng__iter spng__iter_init(unsigned bit_depth, const unsigned char *samples) +{ + struct spng__iter iter = + { + .mask = (uint32_t)(1 << bit_depth) - 1, + .shift_amount = 8 - bit_depth, + .initial_shift = 8 - bit_depth, + .bit_depth = bit_depth, + .samples = samples + }; + + return iter; +} + +/* Returns the current sample unpacked, iterates to the next one */ +static inline uint8_t get_sample(struct spng__iter *iter) +{ + uint8_t x = (iter->samples[0] >> iter->shift_amount) & iter->mask; + + iter->shift_amount -= iter->bit_depth; + + if(iter->shift_amount > 7) + { + iter->shift_amount = iter->initial_shift; + iter->samples++; + } + + return x; +} + +static void u16_row_to_host(void *row, size_t size) +{ + uint16_t *px = row; + size_t i, n = size / 2; + + for(i=0; i < n; i++) + { + px[i] = read_u16(&px[i]); + } +} + +static void u16_row_to_bigendian(void *row, size_t size) +{ + uint16_t *px = (uint16_t*)row; + size_t i, n = size / 2; + + for(i=0; i < n; i++) + { + write_u16(&px[i], px[i]); + } +} + +static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n) +{ + uint32_t i; + for(i=0; i < n; i++) + { + memcpy(out + i * 4, row + i * 3, 3); + out[i*4+3] = 255; + } +} + +static unsigned num_channels(const struct spng_ihdr *ihdr) +{ + switch(ihdr->color_type) + { + case SPNG_COLOR_TYPE_TRUECOLOR: return 3; + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: return 2; + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: return 4; + case SPNG_COLOR_TYPE_GRAYSCALE: + case SPNG_COLOR_TYPE_INDEXED: + return 1; + default: return 0; + } +} + +/* Calculate scanline width in bits, round up to the nearest byte */ +static int calculate_scanline_width(const struct spng_ihdr *ihdr, uint32_t width, size_t *scanline_width) +{ + if(ihdr == NULL || !width) return SPNG_EINTERNAL; + + size_t res = num_channels(ihdr) * ihdr->bit_depth; + + if(res > SIZE_MAX / width) return SPNG_EOVERFLOW; + res = res * width; + + res += 15; /* Filter byte + 7 for rounding */ + + if(res < 15) return SPNG_EOVERFLOW; + + res /= 8; + + if(res > UINT32_MAX) return SPNG_EOVERFLOW; + + *scanline_width = res; + + return 0; +} + +static int calculate_subimages(struct spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + struct spng_ihdr *ihdr = &ctx->ihdr; + struct spng_subimage *sub = ctx->subimage; + + if(ihdr->interlace_method == 1) + { + sub[0].width = (ihdr->width + 7) >> 3; + sub[0].height = (ihdr->height + 7) >> 3; + sub[1].width = (ihdr->width + 3) >> 3; + sub[1].height = (ihdr->height + 7) >> 3; + sub[2].width = (ihdr->width + 3) >> 2; + sub[2].height = (ihdr->height + 3) >> 3; + sub[3].width = (ihdr->width + 1) >> 2; + sub[3].height = (ihdr->height + 3) >> 2; + sub[4].width = (ihdr->width + 1) >> 1; + sub[4].height = (ihdr->height + 1) >> 2; + sub[5].width = ihdr->width >> 1; + sub[5].height = (ihdr->height + 1) >> 1; + sub[6].width = ihdr->width; + sub[6].height = ihdr->height >> 1; + } + else + { + sub[0].width = ihdr->width; + sub[0].height = ihdr->height; + } + + int i; + for(i=0; i < 7; i++) + { + if(sub[i].width == 0 || sub[i].height == 0) continue; + + int ret = calculate_scanline_width(ihdr, sub[i].width, &sub[i].scanline_width); + if(ret) return ret; + + if(sub[ctx->widest_pass].scanline_width < sub[i].scanline_width) ctx->widest_pass = i; + + ctx->last_pass = i; + } + + return 0; +} + +static int check_decode_fmt(const struct spng_ihdr *ihdr, const int fmt) +{ + switch(fmt) + { + case SPNG_FMT_RGBA8: + case SPNG_FMT_RGBA16: + case SPNG_FMT_RGB8: + case SPNG_FMT_PNG: + case SPNG_FMT_RAW: + return 0; + case SPNG_FMT_G8: + case SPNG_FMT_GA8: + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) return 0; + else return SPNG_EFMT; + case SPNG_FMT_GA16: + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) return 0; + else return SPNG_EFMT; + default: return SPNG_EFMT; + } +} + +static int calculate_image_width(const struct spng_ihdr *ihdr, int fmt, size_t *len) +{ + if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL; + + size_t res = ihdr->width; + unsigned bytes_per_pixel; + + switch(fmt) + { + case SPNG_FMT_RGBA8: + case SPNG_FMT_GA16: + bytes_per_pixel = 4; + break; + case SPNG_FMT_RGBA16: + bytes_per_pixel = 8; + break; + case SPNG_FMT_RGB8: + bytes_per_pixel = 3; + break; + case SPNG_FMT_PNG: + case SPNG_FMT_RAW: + { + int ret = calculate_scanline_width(ihdr, ihdr->width, &res); + if(ret) return ret; + + res -= 1; /* exclude filter byte */ + bytes_per_pixel = 1; + break; + } + case SPNG_FMT_G8: + bytes_per_pixel = 1; + break; + case SPNG_FMT_GA8: + bytes_per_pixel = 2; + break; + default: return SPNG_EINTERNAL; + } + + if(res > SIZE_MAX / bytes_per_pixel) return SPNG_EOVERFLOW; + res = res * bytes_per_pixel; + + *len = res; + + return 0; +} + +static int calculate_image_size(const struct spng_ihdr *ihdr, int fmt, size_t *len) +{ + if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL; + + size_t res = 0; + + int ret = calculate_image_width(ihdr, fmt, &res); + if(ret) return ret; + + if(res > SIZE_MAX / ihdr->height) return SPNG_EOVERFLOW; + res = res * ihdr->height; + + *len = res; + + return 0; +} + +static int increase_cache_usage(spng_ctx *ctx, size_t bytes, int new_chunk) +{ + if(ctx == NULL || !bytes) return SPNG_EINTERNAL; + + if(new_chunk) + { + ctx->chunk_count_total++; + if(ctx->chunk_count_total < 1) return SPNG_EOVERFLOW; + + if(ctx->chunk_count_total > ctx->chunk_count_limit) return SPNG_ECHUNK_LIMITS; + } + + size_t new_usage = ctx->chunk_cache_usage + bytes; + + if(new_usage < ctx->chunk_cache_usage) return SPNG_EOVERFLOW; + + if(new_usage > ctx->chunk_cache_limit) return SPNG_ECHUNK_LIMITS; + + ctx->chunk_cache_usage = new_usage; + + return 0; +} + +static int decrease_cache_usage(spng_ctx *ctx, size_t usage) +{ + if(ctx == NULL || !usage) return SPNG_EINTERNAL; + if(usage > ctx->chunk_cache_usage) return SPNG_EINTERNAL; + + ctx->chunk_cache_usage -= usage; + + return 0; +} + +static int is_critical_chunk(struct spng_chunk *chunk) +{ + if(chunk == NULL) return 0; + if((chunk->type[0] & (1 << 5)) == 0) return 1; + + return 0; +} + +static int decode_err(spng_ctx *ctx, int err) +{ + ctx->state = SPNG_STATE_INVALID; + + return err; +} + +static int encode_err(spng_ctx *ctx, int err) +{ + ctx->state = SPNG_STATE_INVALID; + + return err; +} + +static inline int read_data(spng_ctx *ctx, size_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!bytes) return 0; + + if(ctx->streaming && (bytes > SPNG_READ_SIZE)) return SPNG_EINTERNAL; + + int ret = ctx->read_fn(ctx, ctx->stream_user_ptr, ctx->stream_buf, bytes); + + if(ret) + { + if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR; + + return ret; + } + + ctx->bytes_read += bytes; + if(ctx->bytes_read < bytes) return SPNG_EOVERFLOW; + + return 0; +} + +/* Ensure there is enough space for encoding starting at ctx->write_ptr */ +static int require_bytes(spng_ctx *ctx, size_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + if(ctx->streaming) + { + if(bytes > ctx->stream_buf_size) + { + size_t new_size = ctx->stream_buf_size; + + /* Start at default IDAT size + header + crc */ + if(new_size < (SPNG_WRITE_SIZE + 12)) new_size = SPNG_WRITE_SIZE + 12; + + if(new_size < bytes) new_size = bytes; + + void *temp = spng__realloc(ctx, ctx->stream_buf, new_size); + + if(temp == NULL) return encode_err(ctx, SPNG_EMEM); + + ctx->stream_buf = temp; + ctx->stream_buf_size = bytes; + ctx->write_ptr = ctx->stream_buf; + } + + return 0; + } + + if(!ctx->internal_buffer) return SPNG_ENODST; + + size_t required = ctx->bytes_encoded + bytes; + if(required < bytes) return SPNG_EOVERFLOW; + + if(required > ctx->out_png_size) + { + size_t new_size = ctx->out_png_size; + + /* Start with a size that doesn't require a realloc() 100% of the time */ + if(new_size < (SPNG_WRITE_SIZE * 2)) new_size = SPNG_WRITE_SIZE * 2; + + /* Prefer the next power of two over the requested size */ + while(new_size < required) + { + if(new_size / SIZE_MAX > 2) return encode_err(ctx, SPNG_EOVERFLOW); + + new_size *= 2; + } + + void *temp = spng__realloc(ctx, ctx->out_png, new_size); + + if(temp == NULL) return encode_err(ctx, SPNG_EMEM); + + ctx->out_png = temp; + ctx->out_png_size = new_size; + ctx->write_ptr = ctx->out_png + ctx->bytes_encoded; + } + + return 0; +} + +static int write_data(spng_ctx *ctx, const void *data, size_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!bytes) return 0; + + if(ctx->streaming) + { + if(bytes > SPNG_WRITE_SIZE) return SPNG_EINTERNAL; + + int ret = ctx->write_fn(ctx, ctx->stream_user_ptr, (void*)data, bytes); + + if(ret) + { + if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR; + + return encode_err(ctx, ret); + } + } + else + { + int ret = require_bytes(ctx, bytes); + if(ret) return encode_err(ctx, ret); + + memcpy(ctx->write_ptr, data, bytes); + + ctx->write_ptr += bytes; + } + + ctx->bytes_encoded += bytes; + if(ctx->bytes_encoded < bytes) return SPNG_EOVERFLOW; + + return 0; +} + +static int write_header(spng_ctx *ctx, const uint8_t chunk_type[4], size_t chunk_length, unsigned char **data) +{ + if(ctx == NULL || chunk_type == NULL) return SPNG_EINTERNAL; + if(chunk_length > spng_u32max) return SPNG_EINTERNAL; + + size_t total = chunk_length + 12; + + int ret = require_bytes(ctx, total); + if(ret) return ret; + + uint32_t crc = crc32(0, NULL, 0); + ctx->current_chunk.crc = crc32(crc, chunk_type, 4); + + memcpy(&ctx->current_chunk.type, chunk_type, 4); + ctx->current_chunk.length = (uint32_t)chunk_length; + + if(!data) return SPNG_EINTERNAL; + + if(ctx->streaming) *data = ctx->stream_buf + 8; + else *data = ctx->write_ptr + 8; + + return 0; +} + +static int trim_chunk(spng_ctx *ctx, uint32_t length) +{ + if(length > spng_u32max) return SPNG_EINTERNAL; + if(length > ctx->current_chunk.length) return SPNG_EINTERNAL; + + ctx->current_chunk.length = length; + + return 0; +} + +static int finish_chunk(spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + struct spng_chunk *chunk = &ctx->current_chunk; + + unsigned char *header; + unsigned char *chunk_data; + + if(ctx->streaming) + { + chunk_data = ctx->stream_buf + 8; + header = ctx->stream_buf; + } + else + { + chunk_data = ctx->write_ptr + 8; + header = ctx->write_ptr; + } + + write_u32(header, chunk->length); + memcpy(header + 4, chunk->type, 4); + + chunk->crc = crc32(chunk->crc, chunk_data, chunk->length); + + write_u32(chunk_data + chunk->length, chunk->crc); + + if(ctx->streaming) + { + const unsigned char *ptr = ctx->stream_buf; + uint32_t bytes_left = chunk->length + 12; + uint32_t len = 0; + + while(bytes_left) + { + ptr += len; + len = SPNG_WRITE_SIZE; + + if(len > bytes_left) len = bytes_left; + + int ret = write_data(ctx, ptr, len); + if(ret) return ret; + + bytes_left -= len; + } + } + else + { + ctx->bytes_encoded += chunk->length; + if(ctx->bytes_encoded < chunk->length) return SPNG_EOVERFLOW; + + ctx->bytes_encoded += 12; + if(ctx->bytes_encoded < 12) return SPNG_EOVERFLOW; + + ctx->write_ptr += chunk->length + 12; + } + + return 0; +} + +static int write_chunk(spng_ctx *ctx, const uint8_t type[4], const void *data, size_t length) +{ + if(ctx == NULL || type == NULL) return SPNG_EINTERNAL; + if(length && data == NULL) return SPNG_EINTERNAL; + + unsigned char *write_ptr; + + int ret = write_header(ctx, type, length, &write_ptr); + if(ret) return ret; + + if(length) memcpy(write_ptr, data, length); + + return finish_chunk(ctx); +} + +static int write_iend(spng_ctx *ctx) +{ + unsigned char iend_chunk[12] = { 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 }; + return write_data(ctx, iend_chunk, 12); +} + +static int write_unknown_chunks(spng_ctx *ctx, enum spng_location location) +{ + if(!ctx->stored.unknown) return 0; + + const struct spng_unknown_chunk *chunk = ctx->chunk_list; + + uint32_t i; + for(i=0; i < ctx->n_chunks; i++, chunk++) + { + if(chunk->location != location) continue; + + int ret = write_chunk(ctx, chunk->type, chunk->data, chunk->length); + if(ret) return ret; + } + + return 0; +} + +/* Read and check the current chunk's crc, + returns -SPNG_CRC_DISCARD if the chunk should be discarded */ +static inline int read_and_check_crc(spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + int ret; + ret = read_data(ctx, 4); + if(ret) return ret; + + ctx->current_chunk.crc = read_u32(ctx->data); + + if(ctx->skip_crc) return 0; + + if(ctx->cur_actual_crc != ctx->current_chunk.crc) + { + if(is_critical_chunk(&ctx->current_chunk)) + { + if(ctx->crc_action_critical == SPNG_CRC_USE) return 0; + } + else + { + if(ctx->crc_action_ancillary == SPNG_CRC_USE) return 0; + if(ctx->crc_action_ancillary == SPNG_CRC_DISCARD) return -SPNG_CRC_DISCARD; + } + + return SPNG_ECHUNK_CRC; + } + + return 0; +} + +/* Read and validate the current chunk's crc and the next chunk header */ +static inline int read_header(spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + int ret; + struct spng_chunk chunk = { 0 }; + + ret = read_and_check_crc(ctx); + if(ret) + { + if(ret == -SPNG_CRC_DISCARD) + { + ctx->discard = 1; + } + else return ret; + } + + ret = read_data(ctx, 8); + if(ret) return ret; + + chunk.offset = ctx->bytes_read - 8; + + chunk.length = read_u32(ctx->data); + + memcpy(&chunk.type, ctx->data + 4, 4); + + if(chunk.length > spng_u32max) return SPNG_ECHUNK_STDLEN; + + ctx->cur_chunk_bytes_left = chunk.length; + + if(is_critical_chunk(&chunk) && ctx->crc_action_critical == SPNG_CRC_USE) ctx->skip_crc = 1; + else if(ctx->crc_action_ancillary == SPNG_CRC_USE) ctx->skip_crc = 1; + else ctx->skip_crc = 0; + + if(!ctx->skip_crc) + { + ctx->cur_actual_crc = crc32(0, NULL, 0); + ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, chunk.type, 4); + } + + ctx->current_chunk = chunk; + + return 0; +} + +/* Read chunk bytes and update crc */ +static int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL; + if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */ + + int ret; + + ret = read_data(ctx, bytes); + if(ret) return ret; + + if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, ctx->data, bytes); + + ctx->cur_chunk_bytes_left -= bytes; + + return ret; +} + +/* read_chunk_bytes() + read_data() with custom output buffer */ +static int read_chunk_bytes2(spng_ctx *ctx, void *out, uint32_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL; + if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */ + + int ret; + uint32_t len = bytes; + + if(ctx->streaming && len > SPNG_READ_SIZE) len = SPNG_READ_SIZE; + + while(bytes) + { + if(len > bytes) len = bytes; + + ret = ctx->read_fn(ctx, ctx->stream_user_ptr, out, len); + if(ret) return ret; + + if(!ctx->streaming) memcpy(out, ctx->data, len); + + ctx->bytes_read += len; + if(ctx->bytes_read < len) return SPNG_EOVERFLOW; + + if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, out, len); + + ctx->cur_chunk_bytes_left -= len; + + out = (char*)out + len; + bytes -= len; + len = SPNG_READ_SIZE; + } + + return 0; +} + +static int discard_chunk_bytes(spng_ctx *ctx, uint32_t bytes) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!bytes) return 0; + + int ret; + + if(ctx->streaming) /* Do small, consecutive reads */ + { + while(bytes) + { + uint32_t len = SPNG_READ_SIZE; + + if(len > bytes) len = bytes; + + ret = read_chunk_bytes(ctx, len); + if(ret) return ret; + + bytes -= len; + } + } + else + { + ret = read_chunk_bytes(ctx, bytes); + if(ret) return ret; + } + + return 0; +} + +static int spng__inflate_init(spng_ctx *ctx, int window_bits) +{ + if(ctx->zstream.state) inflateEnd(&ctx->zstream); + + ctx->inflate = 1; + + ctx->zstream.zalloc = spng__zalloc; + ctx->zstream.zfree = spng__zfree; + ctx->zstream.opaque = ctx; + + if(inflateInit2(&ctx->zstream, window_bits) != Z_OK) return SPNG_EZLIB_INIT; + +#if ZLIB_VERNUM >= 0x1290 && !defined(SPNG_USE_MINIZ) + + int validate = 1; + + if(ctx->flags & SPNG_CTX_IGNORE_ADLER32) validate = 0; + + if(is_critical_chunk(&ctx->current_chunk)) + { + if(ctx->crc_action_critical == SPNG_CRC_USE) validate = 0; + } + else /* ancillary */ + { + if(ctx->crc_action_ancillary == SPNG_CRC_USE) validate = 0; + } + + if(inflateValidate(&ctx->zstream, validate)) return SPNG_EZLIB_INIT; + +#else /* This requires zlib >= 1.2.11 */ + #pragma message ("inflateValidate() not available, SPNG_CTX_IGNORE_ADLER32 will be ignored") +#endif + + return 0; +} + +static int spng__deflate_init(spng_ctx *ctx, struct spng__zlib_options *options) +{ + if(ctx->zstream.state) deflateEnd(&ctx->zstream); + + ctx->deflate = 1; + + z_stream *zstream = &ctx->zstream; + zstream->zalloc = spng__zalloc; + zstream->zfree = spng__zfree; + zstream->opaque = ctx; + zstream->data_type = options->data_type; + + int ret = deflateInit2(zstream, options->compression_level, Z_DEFLATED, options->window_bits, options->mem_level, options->strategy); + + if(ret != Z_OK) return SPNG_EZLIB_INIT; + + return 0; +} + +/* Inflate a zlib stream starting with start_buf if non-NULL, + continuing from the datastream till an end marker, + allocating and writing the inflated stream to *out, + leaving "extra" bytes at the end, final buffer length is *len. + + Takes into account the chunk size and cache limits. +*/ +static int spng__inflate_stream(spng_ctx *ctx, char **out, size_t *len, size_t extra, const void *start_buf, size_t start_len) +{ + int ret = spng__inflate_init(ctx, 15); + if(ret) return ret; + + size_t max = ctx->chunk_cache_limit - ctx->chunk_cache_usage; + + if(ctx->max_chunk_size < max) max = ctx->max_chunk_size; + + if(extra > max) return SPNG_ECHUNK_LIMITS; + max -= extra; + + uint32_t read_size; + size_t size = 8 * 1024; + void *t, *buf = spng__malloc(ctx, size); + + if(buf == NULL) return SPNG_EMEM; + + z_stream *stream = &ctx->zstream; + + if(start_buf != NULL && start_len) + { + stream->avail_in = (uInt)start_len; + stream->next_in = start_buf; + } + else + { + stream->avail_in = 0; + stream->next_in = NULL; + } + + stream->avail_out = (uInt)size; + stream->next_out = buf; + + while(ret != Z_STREAM_END) + { + ret = inflate(stream, Z_NO_FLUSH); + + if(ret == Z_STREAM_END) break; + + if(ret != Z_OK && ret != Z_BUF_ERROR) + { + ret = SPNG_EZLIB; + goto err; + } + + if(!stream->avail_out) /* Resize buffer */ + { + /* overflow or reached chunk/cache limit */ + if( (2 > SIZE_MAX / size) || (size > max / 2) ) + { + ret = SPNG_ECHUNK_LIMITS; + goto err; + } + + size *= 2; + + t = spng__realloc(ctx, buf, size); + if(t == NULL) goto mem; + + buf = t; + + stream->avail_out = (uInt)size / 2; + stream->next_out = (unsigned char*)buf + size / 2; + } + else if(!stream->avail_in) /* Read more chunk bytes */ + { + read_size = ctx->cur_chunk_bytes_left; + if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE; + + ret = read_chunk_bytes(ctx, read_size); + + if(ret) + { + if(!read_size) ret = SPNG_EZLIB; + + goto err; + } + + stream->avail_in = read_size; + stream->next_in = ctx->data; + } + } + + size = stream->total_out; + + if(!size) + { + ret = SPNG_EZLIB; + goto err; + } + + size += extra; + if(size < extra) goto mem; + + t = spng__realloc(ctx, buf, size); + if(t == NULL) goto mem; + + buf = t; + + (void)increase_cache_usage(ctx, size, 0); + + *out = buf; + *len = size; + + return 0; + +mem: + ret = SPNG_EMEM; +err: + spng__free(ctx, buf); + return ret; +} + +/* Read at least one byte from the IDAT stream */ +static int read_idat_bytes(spng_ctx *ctx, uint32_t *bytes_read) +{ + if(ctx == NULL || bytes_read == NULL) return SPNG_EINTERNAL; + if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT; + + int ret; + uint32_t len; + + while(!ctx->cur_chunk_bytes_left) + { + ret = read_header(ctx); + if(ret) return ret; + + if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT; + } + + if(ctx->streaming) + {/* TODO: estimate bytes to read for progressive reads */ + len = SPNG_READ_SIZE; + if(len > ctx->cur_chunk_bytes_left) len = ctx->cur_chunk_bytes_left; + } + else len = ctx->current_chunk.length; + + ret = read_chunk_bytes(ctx, len); + + *bytes_read = len; + + return ret; +} + +static int read_scanline_bytes(spng_ctx *ctx, unsigned char *dest, size_t len) +{ + if(ctx == NULL || dest == NULL) return SPNG_EINTERNAL; + + int ret = Z_OK; + uint32_t bytes_read; + + z_stream *zstream = &ctx->zstream; + + zstream->avail_out = (uInt)len; + zstream->next_out = dest; + + while(zstream->avail_out != 0) + { + ret = inflate(zstream, Z_NO_FLUSH); + + if(ret == Z_OK) continue; + + if(ret == Z_STREAM_END) /* Reached an end-marker */ + { + if(zstream->avail_out != 0) return SPNG_EIDAT_TOO_SHORT; + } + else if(ret == Z_BUF_ERROR) /* Read more IDAT bytes */ + { + ret = read_idat_bytes(ctx, &bytes_read); + if(ret) return ret; + + zstream->avail_in = bytes_read; + zstream->next_in = ctx->data; + } + else return SPNG_EIDAT_STREAM; + } + + return 0; +} + +static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c) +{ + int16_t p = a + b - c; + int16_t pa = abs(p - a); + int16_t pb = abs(p - b); + int16_t pc = abs(p - c); + + if(pa <= pb && pa <= pc) return a; + else if(pb <= pc) return b; + + return c; +} + +SPNG_TARGET_CLONES("default,avx2") +static void defilter_up(size_t bytes, unsigned char *row, const unsigned char *prev) +{ + size_t i; + for(i=0; i < bytes; i++) + { + row[i] += prev[i]; + } +} + +/* Defilter *scanline in-place. + *prev_scanline and *scanline should point to the first pixel, + scanline_width is the width of the scanline including the filter byte. +*/ +static int defilter_scanline(const unsigned char *prev_scanline, unsigned char *scanline, + size_t scanline_width, unsigned bytes_per_pixel, unsigned filter) +{ + if(prev_scanline == NULL || scanline == NULL || !scanline_width) return SPNG_EINTERNAL; + + size_t i; + scanline_width--; + + if(filter == 0) return 0; + +#ifndef SPNG_DISABLE_OPT + if(filter == SPNG_FILTER_UP) goto no_opt; + + if(bytes_per_pixel == 4) + { + if(filter == SPNG_FILTER_SUB) + defilter_sub4(scanline_width, scanline); + else if(filter == SPNG_FILTER_AVERAGE) + defilter_avg4(scanline_width, scanline, prev_scanline); + else if(filter == SPNG_FILTER_PAETH) + defilter_paeth4(scanline_width, scanline, prev_scanline); + else return SPNG_EFILTER; + + return 0; + } + else if(bytes_per_pixel == 3) + { + if(filter == SPNG_FILTER_SUB) + defilter_sub3(scanline_width, scanline); + else if(filter == SPNG_FILTER_AVERAGE) + defilter_avg3(scanline_width, scanline, prev_scanline); + else if(filter == SPNG_FILTER_PAETH) + defilter_paeth3(scanline_width, scanline, prev_scanline); + else return SPNG_EFILTER; + + return 0; + } +no_opt: +#endif + + if(filter == SPNG_FILTER_UP) + { + defilter_up(scanline_width, scanline, prev_scanline); + return 0; + } + + for(i=0; i < scanline_width; i++) + { + uint8_t x, a, b, c; + + if(i >= bytes_per_pixel) + { + a = scanline[i - bytes_per_pixel]; + b = prev_scanline[i]; + c = prev_scanline[i - bytes_per_pixel]; + } + else /* First pixel in row */ + { + a = 0; + b = prev_scanline[i]; + c = 0; + } + + x = scanline[i]; + + switch(filter) + { + case SPNG_FILTER_SUB: + { + x = x + a; + break; + } + case SPNG_FILTER_AVERAGE: + { + uint16_t avg = (a + b) / 2; + x = x + avg; + break; + } + case SPNG_FILTER_PAETH: + { + x = x + paeth(a,b,c); + break; + } + } + + scanline[i] = x; + } + + return 0; +} + +static int filter_scanline(unsigned char *filtered, const unsigned char *prev_scanline, const unsigned char *scanline, + size_t scanline_width, unsigned bytes_per_pixel, const unsigned filter) +{ + if(prev_scanline == NULL || scanline == NULL || scanline_width <= 1) return SPNG_EINTERNAL; + + if(filter > 4) return SPNG_EFILTER; + if(filter == 0) return 0; + + scanline_width--; + + uint32_t i; + for(i=0; i < scanline_width; i++) + { + uint8_t x, a, b, c; + + if(i >= bytes_per_pixel) + { + a = scanline[i - bytes_per_pixel]; + b = prev_scanline[i]; + c = prev_scanline[i - bytes_per_pixel]; + } + else /* first pixel in row */ + { + a = 0; + b = prev_scanline[i]; + c = 0; + } + + x = scanline[i]; + + switch(filter) + { + case SPNG_FILTER_SUB: + { + x = x - a; + break; + } + case SPNG_FILTER_UP: + { + x = x - b; + break; + } + case SPNG_FILTER_AVERAGE: + { + uint16_t avg = (a + b) / 2; + x = x - avg; + break; + } + case SPNG_FILTER_PAETH: + { + x = x - paeth(a,b,c); + break; + } + } + + filtered[i] = x; + } + + return 0; +} + +static int32_t filter_sum(const unsigned char *prev_scanline, const unsigned char *scanline, + size_t size, unsigned bytes_per_pixel, const unsigned filter) +{ + /* prevent potential over/underflow, bails out at a width of ~8M pixels for RGBA8 */ + if(size > (INT32_MAX / 128)) return INT32_MAX; + + uint32_t i; + int32_t sum = 0; + uint8_t x, a, b, c; + + for(i=0; i < size; i++) + { + if(i >= bytes_per_pixel) + { + a = scanline[i - bytes_per_pixel]; + b = prev_scanline[i]; + c = prev_scanline[i - bytes_per_pixel]; + } + else /* first pixel in row */ + { + a = 0; + b = prev_scanline[i]; + c = 0; + } + + x = scanline[i]; + + switch(filter) + { + case SPNG_FILTER_NONE: + { + break; + } + case SPNG_FILTER_SUB: + { + x = x - a; + break; + } + case SPNG_FILTER_UP: + { + x = x - b; + break; + } + case SPNG_FILTER_AVERAGE: + { + uint16_t avg = (a + b) / 2; + x = x - avg; + break; + } + case SPNG_FILTER_PAETH: + { + x = x - paeth(a,b,c); + break; + } + } + + sum += 128 - abs((int)x - 128); + } + + return sum; +} + +static unsigned get_best_filter(const unsigned char *prev_scanline, const unsigned char *scanline, + size_t scanline_width, unsigned bytes_per_pixel, const int choices) +{ + if(!choices) return SPNG_FILTER_NONE; + + scanline_width--; + + int i; + unsigned int best_filter = 0; + enum spng_filter_choice flag; + int32_t sum, best_score = INT32_MAX; + int32_t filter_scores[5] = { INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX }; + + if( !(choices & (choices - 1)) ) + {/* only one choice/bit is set */ + for(i=0; i < 5; i++) + { + if(choices == 1 << (i + 3)) return i; + } + } + + for(i=0; i < 5; i++) + { + flag = 1 << (i + 3); + + if(choices & flag) sum = filter_sum(prev_scanline, scanline, scanline_width, bytes_per_pixel, i); + else continue; + + filter_scores[i] = abs(sum); + + if(filter_scores[i] < best_score) + { + best_score = filter_scores[i]; + best_filter = i; + } + } + + return best_filter; +} + +/* Scale "sbits" significant bits in "sample" from "bit_depth" to "target" + + "bit_depth" must be a valid PNG depth + "sbits" must be less than or equal to "bit_depth" + "target" must be between 1 and 16 +*/ +static uint16_t sample_to_target(uint16_t sample, unsigned bit_depth, unsigned sbits, unsigned target) +{ + if(bit_depth == sbits) + { + if(target == sbits) return sample; /* No scaling */ + }/* bit_depth > sbits */ + else sample = sample >> (bit_depth - sbits); /* Shift significant bits to bottom */ + + /* Downscale */ + if(target < sbits) return sample >> (sbits - target); + + /* Upscale using left bit replication */ + int8_t shift_amount = target - sbits; + uint16_t sample_bits = sample; + sample = 0; + + while(shift_amount >= 0) + { + sample = sample | (sample_bits << shift_amount); + shift_amount -= sbits; + } + + int8_t partial = shift_amount + (int8_t)sbits; + + if(partial != 0) sample = sample | (sample_bits >> abs(shift_amount)); + + return sample; +} + +static inline void gamma_correct_row(unsigned char *row, uint32_t pixels, int fmt, const uint16_t *gamma_lut) +{ + uint32_t i; + + if(fmt == SPNG_FMT_RGBA8) + { + unsigned char *px; + for(i=0; i < pixels; i++) + { + px = row + i * 4; + + px[0] = gamma_lut[px[0]]; + px[1] = gamma_lut[px[1]]; + px[2] = gamma_lut[px[2]]; + } + } + else if(fmt == SPNG_FMT_RGBA16) + { + for(i=0; i < pixels; i++) + { + uint16_t px[4]; + memcpy(px, row + i * 8, 8); + + px[0] = gamma_lut[px[0]]; + px[1] = gamma_lut[px[1]]; + px[2] = gamma_lut[px[2]]; + + memcpy(row + i * 8, px, 8); + } + } + else if(fmt == SPNG_FMT_RGB8) + { + unsigned char *px; + for(i=0; i < pixels; i++) + { + px = row + i * 3; + + px[0] = gamma_lut[px[0]]; + px[1] = gamma_lut[px[1]]; + px[2] = gamma_lut[px[2]]; + } + } +} + +/* Apply transparency to output row */ +static inline void trns_row(unsigned char *row, + const unsigned char *scanline, + const unsigned char *trns, + unsigned scanline_stride, + struct spng_ihdr *ihdr, + uint32_t pixels, + int fmt) +{ + uint32_t i; + unsigned row_stride; + unsigned depth = ihdr->bit_depth; + + if(fmt == SPNG_FMT_RGBA8) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */ + + row_stride = 4; + for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) + { + if(!memcmp(scanline, trns, scanline_stride)) row[3] = 0; + } + } + else if(fmt == SPNG_FMT_RGBA16) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */ + + row_stride = 8; + for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) + { + if(!memcmp(scanline, trns, scanline_stride)) memset(row + 6, 0, 2); + } + } + else if(fmt == SPNG_FMT_GA8) + { + row_stride = 2; + + if(depth == 16) + { + for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride) + { + if(!memcmp(scanline, trns, scanline_stride)) memset(row + 1, 0, 1); + } + } + else /* depth <= 8 */ + { + struct spng__iter iter = spng__iter_init(depth, scanline); + + for(i=0; i < pixels; i++, row+=row_stride) + { + if(trns[0] == get_sample(&iter)) row[1] = 0; + } + } + } + else if(fmt == SPNG_FMT_GA16) + { + row_stride = 4; + + if(depth == 16) + { + for(i=0; i< pixels; i++, scanline+=scanline_stride, row+=row_stride) + { + if(!memcmp(scanline, trns, 2)) memset(row + 2, 0, 2); + } + } + else + { + struct spng__iter iter = spng__iter_init(depth, scanline); + + for(i=0; i< pixels; i++, row+=row_stride) + { + if(trns[0] == get_sample(&iter)) memset(row + 2, 0, 2); + } + } + } + else return; +} + +static inline void scale_row(unsigned char *row, uint32_t pixels, int fmt, unsigned depth, const struct spng_sbit *sbit) +{ + uint32_t i; + + if(fmt == SPNG_FMT_RGBA8) + { + unsigned char px[4]; + for(i=0; i < pixels; i++) + { + memcpy(px, row + i * 4, 4); + + px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8); + px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8); + px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8); + px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 8); + + memcpy(row + i * 4, px, 4); + } + } + else if(fmt == SPNG_FMT_RGBA16) + { + uint16_t px[4]; + for(i=0; i < pixels; i++) + { + memcpy(px, row + i * 8, 8); + + px[0] = sample_to_target(px[0], depth, sbit->red_bits, 16); + px[1] = sample_to_target(px[1], depth, sbit->green_bits, 16); + px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 16); + px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 16); + + memcpy(row + i * 8, px, 8); + } + } + else if(fmt == SPNG_FMT_RGB8) + { + unsigned char px[4]; + for(i=0; i < pixels; i++) + { + memcpy(px, row + i * 3, 3); + + px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8); + px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8); + px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8); + + memcpy(row + i * 3, px, 3); + } + } + else if(fmt == SPNG_FMT_G8) + { + for(i=0; i < pixels; i++) + { + row[i] = sample_to_target(row[i], depth, sbit->grayscale_bits, 8); + } + } + else if(fmt == SPNG_FMT_GA8) + { + for(i=0; i < pixels; i++) + { + row[i*2] = sample_to_target(row[i*2], depth, sbit->grayscale_bits, 8); + } + } +} + +/* Expand to *row using 8-bit palette indices from *scanline */ +static void expand_row(unsigned char *row, + const unsigned char *scanline, + const union spng__decode_plte *decode_plte, + uint32_t width, + int fmt) +{ + uint32_t i = 0; + unsigned char *px; + unsigned char entry; + const struct spng_plte_entry *plte = decode_plte->rgba; + +#if defined(SPNG_ARM) + if(fmt == SPNG_FMT_RGBA8) i = expand_palette_rgba8_neon(row, scanline, decode_plte->raw, width); + else if(fmt == SPNG_FMT_RGB8) + { + i = expand_palette_rgb8_neon(row, scanline, decode_plte->raw, width); + + for(; i < width; i++) + {/* In this case the LUT is 3 bytes packed */ + px = row + i * 3; + entry = scanline[i]; + px[0] = decode_plte->raw[entry * 3 + 0]; + px[1] = decode_plte->raw[entry * 3 + 1]; + px[2] = decode_plte->raw[entry * 3 + 2]; + } + return; + } +#endif + + if(fmt == SPNG_FMT_RGBA8) + { + for(; i < width; i++) + { + px = row + i * 4; + entry = scanline[i]; + px[0] = plte[entry].red; + px[1] = plte[entry].green; + px[2] = plte[entry].blue; + px[3] = plte[entry].alpha; + } + } + else if(fmt == SPNG_FMT_RGB8) + { + for(; i < width; i++) + { + px = row + i * 3; + entry = scanline[i]; + px[0] = plte[entry].red; + px[1] = plte[entry].green; + px[2] = plte[entry].blue; + } + } +} + +/* Unpack 1/2/4/8-bit samples to G8/GA8/GA16 or G16 -> GA16 */ +static void unpack_scanline(unsigned char *out, const unsigned char *scanline, uint32_t width, unsigned bit_depth, int fmt) +{ + struct spng__iter iter = spng__iter_init(bit_depth, scanline); + uint32_t i; + uint16_t sample, alpha = 65535; + + + if(fmt == SPNG_FMT_GA8) goto ga8; + else if(fmt == SPNG_FMT_GA16) goto ga16; + + /* 1/2/4-bit -> 8-bit */ + for(i=0; i < width; i++) out[i] = get_sample(&iter); + + return; + +ga8: + /* 1/2/4/8-bit -> GA8 */ + for(i=0; i < width; i++) + { + out[i*2] = get_sample(&iter); + out[i*2 + 1] = 255; + } + + return; + +ga16: + + /* 16 -> GA16 */ + if(bit_depth == 16) + { + for(i=0; i < width; i++) + { + memcpy(out + i * 4, scanline + i * 2, 2); + memcpy(out + i * 4 + 2, &alpha, 2); + } + return; + } + + /* 1/2/4/8-bit -> GA16 */ + for(i=0; i < width; i++) + { + sample = get_sample(&iter); + memcpy(out + i * 4, &sample, 2); + memcpy(out + i * 4 + 2, &alpha, 2); + } +} + +static int check_ihdr(const struct spng_ihdr *ihdr, uint32_t max_width, uint32_t max_height) +{ + if(ihdr->width > spng_u32max || !ihdr->width) return SPNG_EWIDTH; + if(ihdr->height > spng_u32max || !ihdr->height) return SPNG_EHEIGHT; + + if(ihdr->width > max_width) return SPNG_EUSER_WIDTH; + if(ihdr->height > max_height) return SPNG_EUSER_HEIGHT; + + switch(ihdr->color_type) + { + case SPNG_COLOR_TYPE_GRAYSCALE: + { + if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 || + ihdr->bit_depth == 4 || ihdr->bit_depth == 8 || + ihdr->bit_depth == 16) ) + return SPNG_EBIT_DEPTH; + + break; + } + case SPNG_COLOR_TYPE_TRUECOLOR: + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: + { + if( !(ihdr->bit_depth == 8 || ihdr->bit_depth == 16) ) + return SPNG_EBIT_DEPTH; + + break; + } + case SPNG_COLOR_TYPE_INDEXED: + { + if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 || + ihdr->bit_depth == 4 || ihdr->bit_depth == 8) ) + return SPNG_EBIT_DEPTH; + + break; + } + default: return SPNG_ECOLOR_TYPE; + } + + if(ihdr->compression_method) return SPNG_ECOMPRESSION_METHOD; + if(ihdr->filter_method) return SPNG_EFILTER_METHOD; + + if(ihdr->interlace_method > 1) return SPNG_EINTERLACE_METHOD; + + return 0; +} + +static int check_plte(const struct spng_plte *plte, const struct spng_ihdr *ihdr) +{ + if(plte == NULL || ihdr == NULL) return 1; + + if(plte->n_entries == 0) return 1; + if(plte->n_entries > 256) return 1; + + if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) + { + if(plte->n_entries > (1U << ihdr->bit_depth)) return 1; + } + + return 0; +} + +static int check_sbit(const struct spng_sbit *sbit, const struct spng_ihdr *ihdr) +{ + if(sbit == NULL || ihdr == NULL) return 1; + + if(ihdr->color_type == 0) + { + if(sbit->grayscale_bits == 0) return SPNG_ESBIT; + if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT; + } + else if(ihdr->color_type == 2 || ihdr->color_type == 3) + { + if(sbit->red_bits == 0) return SPNG_ESBIT; + if(sbit->green_bits == 0) return SPNG_ESBIT; + if(sbit->blue_bits == 0) return SPNG_ESBIT; + + uint8_t bit_depth; + if(ihdr->color_type == 3) bit_depth = 8; + else bit_depth = ihdr->bit_depth; + + if(sbit->red_bits > bit_depth) return SPNG_ESBIT; + if(sbit->green_bits > bit_depth) return SPNG_ESBIT; + if(sbit->blue_bits > bit_depth) return SPNG_ESBIT; + } + else if(ihdr->color_type == 4) + { + if(sbit->grayscale_bits == 0) return SPNG_ESBIT; + if(sbit->alpha_bits == 0) return SPNG_ESBIT; + + if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT; + if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT; + } + else if(ihdr->color_type == 6) + { + if(sbit->red_bits == 0) return SPNG_ESBIT; + if(sbit->green_bits == 0) return SPNG_ESBIT; + if(sbit->blue_bits == 0) return SPNG_ESBIT; + if(sbit->alpha_bits == 0) return SPNG_ESBIT; + + if(sbit->red_bits > ihdr->bit_depth) return SPNG_ESBIT; + if(sbit->green_bits > ihdr->bit_depth) return SPNG_ESBIT; + if(sbit->blue_bits > ihdr->bit_depth) return SPNG_ESBIT; + if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT; + } + + return 0; +} + +static int check_chrm_int(const struct spng_chrm_int *chrm_int) +{ + if(chrm_int == NULL) return 1; + + if(chrm_int->white_point_x > spng_u32max || + chrm_int->white_point_y > spng_u32max || + chrm_int->red_x > spng_u32max || + chrm_int->red_y > spng_u32max || + chrm_int->green_x > spng_u32max || + chrm_int->green_y > spng_u32max || + chrm_int->blue_x > spng_u32max || + chrm_int->blue_y > spng_u32max) return SPNG_ECHRM; + + return 0; +} + +static int check_phys(const struct spng_phys *phys) +{ + if(phys == NULL) return 1; + + if(phys->unit_specifier > 1) return SPNG_EPHYS; + + if(phys->ppu_x > spng_u32max) return SPNG_EPHYS; + if(phys->ppu_y > spng_u32max) return SPNG_EPHYS; + + return 0; +} + +static int check_time(const struct spng_time *time) +{ + if(time == NULL) return 1; + + if(time->month == 0 || time->month > 12) return 1; + if(time->day == 0 || time->day > 31) return 1; + if(time->hour > 23) return 1; + if(time->minute > 59) return 1; + if(time->second > 60) return 1; + + return 0; +} + +static int check_offs(const struct spng_offs *offs) +{ + if(offs == NULL) return 1; + + if(offs->unit_specifier > 1) return 1; + + return 0; +} + +static int check_exif(const struct spng_exif *exif) +{ + if(exif == NULL) return 1; + if(exif->data == NULL) return 1; + + if(exif->length < 4) return SPNG_ECHUNK_SIZE; + if(exif->length > spng_u32max) return SPNG_ECHUNK_STDLEN; + + const uint8_t exif_le[4] = { 73, 73, 42, 0 }; + const uint8_t exif_be[4] = { 77, 77, 0, 42 }; + + if(memcmp(exif->data, exif_le, 4) && memcmp(exif->data, exif_be, 4)) return 1; + + return 0; +} + +/* Validate PNG keyword */ +static int check_png_keyword(const char *str) +{ + if(str == NULL) return 1; + size_t len = strlen(str); + const char *end = str + len; + + if(!len) return 1; + if(len > 79) return 1; + if(str[0] == ' ') return 1; /* Leading space */ + if(end[-1] == ' ') return 1; /* Trailing space */ + if(strstr(str, " ") != NULL) return 1; /* Consecutive spaces */ + + uint8_t c; + while(str != end) + { + memcpy(&c, str, 1); + + if( (c >= 32 && c <= 126) || (c >= 161) ) str++; + else return 1; /* Invalid character */ + } + + return 0; +} + +/* Validate PNG text *str up to 'len' bytes */ +static int check_png_text(const char *str, size_t len) +{/* XXX: are consecutive newlines permitted? */ + if(str == NULL || len == 0) return 1; + + uint8_t c; + size_t i = 0; + while(i < len) + { + memcpy(&c, str + i, 1); + + if( (c >= 32 && c <= 126) || (c >= 161) || c == 10) i++; + else return 1; /* Invalid character */ + } + + return 0; +} + +/* Returns non-zero for standard chunks which are stored without allocating memory */ +static int is_small_chunk(uint8_t type[4]) +{ + if(!memcmp(type, type_plte, 4)) return 1; + else if(!memcmp(type, type_chrm, 4)) return 1; + else if(!memcmp(type, type_gama, 4)) return 1; + else if(!memcmp(type, type_sbit, 4)) return 1; + else if(!memcmp(type, type_srgb, 4)) return 1; + else if(!memcmp(type, type_bkgd, 4)) return 1; + else if(!memcmp(type, type_trns, 4)) return 1; + else if(!memcmp(type, type_hist, 4)) return 1; + else if(!memcmp(type, type_phys, 4)) return 1; + else if(!memcmp(type, type_time, 4)) return 1; + else if(!memcmp(type, type_offs, 4)) return 1; + else return 0; +} + +static int read_ihdr(spng_ctx *ctx) +{ + int ret; + struct spng_chunk *chunk = &ctx->current_chunk; + const unsigned char *data; + + chunk->offset = 8; + chunk->length = 13; + size_t sizeof_sig_ihdr = 29; + + ret = read_data(ctx, sizeof_sig_ihdr); + if(ret) return ret; + + data = ctx->data; + + if(memcmp(data, spng_signature, sizeof(spng_signature))) return SPNG_ESIGNATURE; + + chunk->length = read_u32(data + 8); + memcpy(&chunk->type, data + 12, 4); + + if(chunk->length != 13) return SPNG_EIHDR_SIZE; + if(memcmp(chunk->type, type_ihdr, 4)) return SPNG_ENOIHDR; + + ctx->cur_actual_crc = crc32(0, NULL, 0); + ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, data + 12, 17); + + ctx->ihdr.width = read_u32(data + 16); + ctx->ihdr.height = read_u32(data + 20); + ctx->ihdr.bit_depth = data[24]; + ctx->ihdr.color_type = data[25]; + ctx->ihdr.compression_method = data[26]; + ctx->ihdr.filter_method = data[27]; + ctx->ihdr.interlace_method = data[28]; + + ret = check_ihdr(&ctx->ihdr, ctx->max_width, ctx->max_height); + if(ret) return ret; + + ctx->file.ihdr = 1; + ctx->stored.ihdr = 1; + + if(ctx->ihdr.bit_depth < 8) ctx->bytes_per_pixel = 1; + else ctx->bytes_per_pixel = num_channels(&ctx->ihdr) * (ctx->ihdr.bit_depth / 8); + + ret = calculate_subimages(ctx); + if(ret) return ret; + + return 0; +} + +static void splt_undo(spng_ctx *ctx) +{ + struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1]; + + spng__free(ctx, splt->entries); + + decrease_cache_usage(ctx, sizeof(struct spng_splt)); + decrease_cache_usage(ctx, splt->n_entries * sizeof(struct spng_splt_entry)); + + splt->entries = NULL; + + ctx->n_splt--; +} + +static void text_undo(spng_ctx *ctx) +{ + struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1]; + + spng__free(ctx, text->keyword); + if(text->compression_flag) spng__free(ctx, text->text); + + decrease_cache_usage(ctx, text->cache_usage); + decrease_cache_usage(ctx, sizeof(struct spng_text2)); + + text->keyword = NULL; + text->text = NULL; + + ctx->n_text--; +} + +static void chunk_undo(spng_ctx *ctx) +{ + struct spng_unknown_chunk *chunk = &ctx->chunk_list[ctx->n_chunks - 1]; + + spng__free(ctx, chunk->data); + + decrease_cache_usage(ctx, chunk->length); + decrease_cache_usage(ctx, sizeof(struct spng_unknown_chunk)); + + chunk->data = NULL; + + ctx->n_chunks--; +} + +static int read_non_idat_chunks(spng_ctx *ctx) +{ + int ret; + struct spng_chunk chunk; + const unsigned char *data; + + ctx->discard = 0; + ctx->undo = NULL; + ctx->prev_stored = ctx->stored; + + while( !(ret = read_header(ctx))) + { + if(ctx->discard) + { + if(ctx->undo) ctx->undo(ctx); + + ctx->stored = ctx->prev_stored; + } + + ctx->discard = 0; + ctx->undo = NULL; + + ctx->prev_stored = ctx->stored; + chunk = ctx->current_chunk; + + if(!memcmp(chunk.type, type_idat, 4)) + { + if(ctx->state < SPNG_STATE_FIRST_IDAT) + { + if(ctx->ihdr.color_type == 3 && !ctx->stored.plte) return SPNG_ENOPLTE; + + ctx->first_idat = chunk; + return 0; + } + + if(ctx->prev_was_idat) + { + /* Ignore extra IDAT's */ + ret = discard_chunk_bytes(ctx, chunk.length); + if(ret) return ret; + + continue; + } + else return SPNG_ECHUNK_POS; /* IDAT chunk not at the end of the IDAT sequence */ + } + + ctx->prev_was_idat = 0; + + if(is_small_chunk(chunk.type)) + { + /* None of the known chunks can be zero length */ + if(!chunk.length) return SPNG_ECHUNK_SIZE; + + /* The largest of these chunks is PLTE with 256 entries */ + ret = read_chunk_bytes(ctx, chunk.length > 768 ? 768 : chunk.length); + if(ret) return ret; + } + + data = ctx->data; + + if(is_critical_chunk(&chunk)) + { + if(!memcmp(chunk.type, type_plte, 4)) + { + if(ctx->file.trns || ctx->file.hist || ctx->file.bkgd) return SPNG_ECHUNK_POS; + if(chunk.length % 3 != 0) return SPNG_ECHUNK_SIZE; + + ctx->plte.n_entries = chunk.length / 3; + + if(check_plte(&ctx->plte, &ctx->ihdr)) return SPNG_ECHUNK_SIZE; /* XXX: EPLTE? */ + + size_t i; + for(i=0; i < ctx->plte.n_entries; i++) + { + ctx->plte.entries[i].red = data[i * 3]; + ctx->plte.entries[i].green = data[i * 3 + 1]; + ctx->plte.entries[i].blue = data[i * 3 + 2]; + } + + ctx->file.plte = 1; + ctx->stored.plte = 1; + } + else if(!memcmp(chunk.type, type_iend, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) + { + if(chunk.length) return SPNG_ECHUNK_SIZE; + + ret = read_and_check_crc(ctx); + if(ret == -SPNG_CRC_DISCARD) ret = 0; + + return ret; + } + else return SPNG_ECHUNK_POS; + } + else if(!memcmp(chunk.type, type_ihdr, 4)) return SPNG_ECHUNK_POS; + else return SPNG_ECHUNK_UNKNOWN_CRITICAL; + } + else if(!memcmp(chunk.type, type_chrm, 4)) /* Ancillary chunks */ + { + if(ctx->file.plte) return SPNG_ECHUNK_POS; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.chrm) return SPNG_EDUP_CHRM; + + if(chunk.length != 32) return SPNG_ECHUNK_SIZE; + + ctx->chrm_int.white_point_x = read_u32(data); + ctx->chrm_int.white_point_y = read_u32(data + 4); + ctx->chrm_int.red_x = read_u32(data + 8); + ctx->chrm_int.red_y = read_u32(data + 12); + ctx->chrm_int.green_x = read_u32(data + 16); + ctx->chrm_int.green_y = read_u32(data + 20); + ctx->chrm_int.blue_x = read_u32(data + 24); + ctx->chrm_int.blue_y = read_u32(data + 28); + + if(check_chrm_int(&ctx->chrm_int)) return SPNG_ECHRM; + + ctx->file.chrm = 1; + ctx->stored.chrm = 1; + } + else if(!memcmp(chunk.type, type_gama, 4)) + { + if(ctx->file.plte) return SPNG_ECHUNK_POS; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.gama) return SPNG_EDUP_GAMA; + + if(chunk.length != 4) return SPNG_ECHUNK_SIZE; + + ctx->gama = read_u32(data); + + if(!ctx->gama) return SPNG_EGAMA; + if(ctx->gama > spng_u32max) return SPNG_EGAMA; + + ctx->file.gama = 1; + ctx->stored.gama = 1; + } + else if(!memcmp(chunk.type, type_sbit, 4)) + { + if(ctx->file.plte) return SPNG_ECHUNK_POS; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.sbit) return SPNG_EDUP_SBIT; + + if(ctx->ihdr.color_type == 0) + { + if(chunk.length != 1) return SPNG_ECHUNK_SIZE; + + ctx->sbit.grayscale_bits = data[0]; + } + else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 3) + { + if(chunk.length != 3) return SPNG_ECHUNK_SIZE; + + ctx->sbit.red_bits = data[0]; + ctx->sbit.green_bits = data[1]; + ctx->sbit.blue_bits = data[2]; + } + else if(ctx->ihdr.color_type == 4) + { + if(chunk.length != 2) return SPNG_ECHUNK_SIZE; + + ctx->sbit.grayscale_bits = data[0]; + ctx->sbit.alpha_bits = data[1]; + } + else if(ctx->ihdr.color_type == 6) + { + if(chunk.length != 4) return SPNG_ECHUNK_SIZE; + + ctx->sbit.red_bits = data[0]; + ctx->sbit.green_bits = data[1]; + ctx->sbit.blue_bits = data[2]; + ctx->sbit.alpha_bits = data[3]; + } + + if(check_sbit(&ctx->sbit, &ctx->ihdr)) return SPNG_ESBIT; + + ctx->file.sbit = 1; + ctx->stored.sbit = 1; + } + else if(!memcmp(chunk.type, type_srgb, 4)) + { + if(ctx->file.plte) return SPNG_ECHUNK_POS; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.srgb) return SPNG_EDUP_SRGB; + + if(chunk.length != 1) return SPNG_ECHUNK_SIZE; + + ctx->srgb_rendering_intent = data[0]; + + if(ctx->srgb_rendering_intent > 3) return SPNG_ESRGB; + + ctx->file.srgb = 1; + ctx->stored.srgb = 1; + } + else if(!memcmp(chunk.type, type_bkgd, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.bkgd) return SPNG_EDUP_BKGD; + + if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4) + { + if(chunk.length != 2) return SPNG_ECHUNK_SIZE; + + ctx->bkgd.gray = read_u16(data); + } + else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6) + { + if(chunk.length != 6) return SPNG_ECHUNK_SIZE; + + ctx->bkgd.red = read_u16(data); + ctx->bkgd.green = read_u16(data + 2); + ctx->bkgd.blue = read_u16(data + 4); + } + else if(ctx->ihdr.color_type == 3) + { + if(chunk.length != 1) return SPNG_ECHUNK_SIZE; + if(!ctx->file.plte) return SPNG_EBKGD_NO_PLTE; + + ctx->bkgd.plte_index = data[0]; + if(ctx->bkgd.plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX; + } + + ctx->file.bkgd = 1; + ctx->stored.bkgd = 1; + } + else if(!memcmp(chunk.type, type_trns, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.trns) return SPNG_EDUP_TRNS; + if(!chunk.length) return SPNG_ECHUNK_SIZE; + + if(ctx->ihdr.color_type == 0) + { + if(chunk.length != 2) return SPNG_ECHUNK_SIZE; + + ctx->trns.gray = read_u16(data); + } + else if(ctx->ihdr.color_type == 2) + { + if(chunk.length != 6) return SPNG_ECHUNK_SIZE; + + ctx->trns.red = read_u16(data); + ctx->trns.green = read_u16(data + 2); + ctx->trns.blue = read_u16(data + 4); + } + else if(ctx->ihdr.color_type == 3) + { + if(chunk.length > ctx->plte.n_entries) return SPNG_ECHUNK_SIZE; + if(!ctx->file.plte) return SPNG_ETRNS_NO_PLTE; + + memcpy(ctx->trns.type3_alpha, data, chunk.length); + ctx->trns.n_type3_entries = chunk.length; + } + + if(ctx->ihdr.color_type == 4 || ctx->ihdr.color_type == 6) return SPNG_ETRNS_COLOR_TYPE; + + ctx->file.trns = 1; + ctx->stored.trns = 1; + } + else if(!memcmp(chunk.type, type_hist, 4)) + { + if(!ctx->file.plte) return SPNG_EHIST_NO_PLTE; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.hist) return SPNG_EDUP_HIST; + + if( (chunk.length / 2) != (ctx->plte.n_entries) ) return SPNG_ECHUNK_SIZE; + + size_t k; + for(k=0; k < (chunk.length / 2); k++) + { + ctx->hist.frequency[k] = read_u16(data + k*2); + } + + ctx->file.hist = 1; + ctx->stored.hist = 1; + } + else if(!memcmp(chunk.type, type_phys, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.phys) return SPNG_EDUP_PHYS; + + if(chunk.length != 9) return SPNG_ECHUNK_SIZE; + + ctx->phys.ppu_x = read_u32(data); + ctx->phys.ppu_y = read_u32(data + 4); + ctx->phys.unit_specifier = data[8]; + + if(check_phys(&ctx->phys)) return SPNG_EPHYS; + + ctx->file.phys = 1; + ctx->stored.phys = 1; + } + else if(!memcmp(chunk.type, type_time, 4)) + { + if(ctx->file.time) return SPNG_EDUP_TIME; + + if(chunk.length != 7) return SPNG_ECHUNK_SIZE; + + struct spng_time time; + + time.year = read_u16(data); + time.month = data[2]; + time.day = data[3]; + time.hour = data[4]; + time.minute = data[5]; + time.second = data[6]; + + if(check_time(&time)) return SPNG_ETIME; + + ctx->file.time = 1; + + if(!ctx->user.time) ctx->time = time; + + ctx->stored.time = 1; + } + else if(!memcmp(chunk.type, type_offs, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.offs) return SPNG_EDUP_OFFS; + + if(chunk.length != 9) return SPNG_ECHUNK_SIZE; + + ctx->offs.x = read_s32(data); + ctx->offs.y = read_s32(data + 4); + ctx->offs.unit_specifier = data[8]; + + if(check_offs(&ctx->offs)) return SPNG_EOFFS; + + ctx->file.offs = 1; + ctx->stored.offs = 1; + } + else /* Arbitrary-length chunk */ + { + + if(!memcmp(chunk.type, type_exif, 4)) + { + if(ctx->file.exif) return SPNG_EDUP_EXIF; + + ctx->file.exif = 1; + + if(ctx->user.exif) goto discard; + + if(increase_cache_usage(ctx, chunk.length, 1)) return SPNG_ECHUNK_LIMITS; + + struct spng_exif exif; + + exif.length = chunk.length; + + exif.data = spng__malloc(ctx, chunk.length); + if(exif.data == NULL) return SPNG_EMEM; + + ret = read_chunk_bytes2(ctx, exif.data, chunk.length); + if(ret) + { + spng__free(ctx, exif.data); + return ret; + } + + if(check_exif(&exif)) + { + spng__free(ctx, exif.data); + return SPNG_EEXIF; + } + + ctx->exif = exif; + + ctx->stored.exif = 1; + } + else if(!memcmp(chunk.type, type_iccp, 4)) + {/* TODO: add test file with color profile */ + if(ctx->file.plte) return SPNG_ECHUNK_POS; + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->file.iccp) return SPNG_EDUP_ICCP; + if(!chunk.length) return SPNG_ECHUNK_SIZE; + + ctx->file.iccp = 1; + + uint32_t peek_bytes = 81 > chunk.length ? chunk.length : 81; + + ret = read_chunk_bytes(ctx, peek_bytes); + if(ret) return ret; + + unsigned char *keyword_nul = memchr(ctx->data, '\0', peek_bytes); + if(keyword_nul == NULL) return SPNG_EICCP_NAME; + + uint32_t keyword_len = keyword_nul - ctx->data; + + if(keyword_len > 79) return SPNG_EICCP_NAME; + + memcpy(ctx->iccp.profile_name, ctx->data, keyword_len); + + if(check_png_keyword(ctx->iccp.profile_name)) return SPNG_EICCP_NAME; + + if(chunk.length < (keyword_len + 2)) return SPNG_ECHUNK_SIZE; + + if(ctx->data[keyword_len + 1] != 0) return SPNG_EICCP_COMPRESSION_METHOD; + + ret = spng__inflate_stream(ctx, &ctx->iccp.profile, &ctx->iccp.profile_len, 0, ctx->data + keyword_len + 2, peek_bytes - (keyword_len + 2)); + + if(ret) return ret; + + ctx->stored.iccp = 1; + } + else if(!memcmp(chunk.type, type_text, 4) || + !memcmp(chunk.type, type_ztxt, 4) || + !memcmp(chunk.type, type_itxt, 4)) + { + if(!chunk.length) return SPNG_ECHUNK_SIZE; + + ctx->file.text = 1; + + if(ctx->user.text) goto discard; + + if(increase_cache_usage(ctx, sizeof(struct spng_text2), 1)) return SPNG_ECHUNK_LIMITS; + + ctx->n_text++; + if(ctx->n_text < 1) return SPNG_EOVERFLOW; + if(sizeof(struct spng_text2) > SIZE_MAX / ctx->n_text) return SPNG_EOVERFLOW; + + void *buf = spng__realloc(ctx, ctx->text_list, ctx->n_text * sizeof(struct spng_text2)); + if(buf == NULL) return SPNG_EMEM; + ctx->text_list = buf; + + struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1]; + memset(text, 0, sizeof(struct spng_text2)); + + ctx->undo = text_undo; + + uint32_t text_offset = 0, language_tag_offset = 0, translated_keyword_offset = 0; + uint32_t peek_bytes = 256; /* enough for 3 80-byte keywords and some text bytes */ + uint32_t keyword_len; + + if(peek_bytes > chunk.length) peek_bytes = chunk.length; + + ret = read_chunk_bytes(ctx, peek_bytes); + if(ret) return ret; + + data = ctx->data; + + const unsigned char *zlib_stream = NULL; + const unsigned char *peek_end = data + peek_bytes; + const unsigned char *keyword_nul = memchr(data, 0, chunk.length > 80 ? 80 : chunk.length); + + if(keyword_nul == NULL) return SPNG_ETEXT_KEYWORD; + + keyword_len = keyword_nul - data; + + if(!memcmp(chunk.type, type_text, 4)) + { + text->type = SPNG_TEXT; + + text->text_length = chunk.length - keyword_len - 1; + + text_offset = keyword_len; + + /* increment past nul if there is a text field */ + if(text->text_length) text_offset++; + } + else if(!memcmp(chunk.type, type_ztxt, 4)) + { + text->type = SPNG_ZTXT; + + if((peek_bytes - keyword_len) <= 2) return SPNG_EZTXT; + + if(keyword_nul[1]) return SPNG_EZTXT_COMPRESSION_METHOD; + + text->compression_flag = 1; + + text_offset = keyword_len + 2; + } + else if(!memcmp(chunk.type, type_itxt, 4)) + { + text->type = SPNG_ITXT; + + /* at least two 1-byte fields, two >=0 length strings, and one byte of (compressed) text */ + if((peek_bytes - keyword_len) < 5) return SPNG_EITXT; + + text->compression_flag = keyword_nul[1]; + + if(text->compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG; + + if(keyword_nul[2]) return SPNG_EITXT_COMPRESSION_METHOD; + + language_tag_offset = keyword_len + 3; + + const unsigned char *term; + term = memchr(data + language_tag_offset, 0, peek_bytes - language_tag_offset); + if(term == NULL) return SPNG_EITXT_LANG_TAG; + + if((peek_end - term) < 2) return SPNG_EITXT; + + translated_keyword_offset = term - data + 1; + + zlib_stream = memchr(data + translated_keyword_offset, 0, peek_bytes - translated_keyword_offset); + if(zlib_stream == NULL) return SPNG_EITXT; + if(zlib_stream == peek_end) return SPNG_EITXT; + + text_offset = zlib_stream - data + 1; + text->text_length = chunk.length - text_offset; + } + else return SPNG_EINTERNAL; + + + if(text->compression_flag) + { + /* cache usage = peek_bytes + decompressed text size + nul */ + if(increase_cache_usage(ctx, peek_bytes, 0)) return SPNG_ECHUNK_LIMITS; + + text->keyword = spng__calloc(ctx, 1, peek_bytes); + if(text->keyword == NULL) return SPNG_EMEM; + + memcpy(text->keyword, data, peek_bytes); + + zlib_stream = ctx->data + text_offset; + + ret = spng__inflate_stream(ctx, &text->text, &text->text_length, 1, zlib_stream, peek_bytes - text_offset); + + if(ret) return ret; + + text->text[text->text_length - 1] = '\0'; + text->cache_usage = text->text_length + peek_bytes; + } + else + { + if(increase_cache_usage(ctx, chunk.length + 1, 0)) return SPNG_ECHUNK_LIMITS; + + text->keyword = spng__malloc(ctx, chunk.length + 1); + if(text->keyword == NULL) return SPNG_EMEM; + + memcpy(text->keyword, data, peek_bytes); + + if(chunk.length > peek_bytes) + { + ret = read_chunk_bytes2(ctx, text->keyword + peek_bytes, chunk.length - peek_bytes); + if(ret) return ret; + } + + text->text = text->keyword + text_offset; + + text->text_length = chunk.length - text_offset; + + text->text[text->text_length] = '\0'; + text->cache_usage = chunk.length + 1; + } + + if(check_png_keyword(text->keyword)) return SPNG_ETEXT_KEYWORD; + + text->text_length = strlen(text->text); + + if(text->type != SPNG_ITXT) + { + language_tag_offset = keyword_len; + translated_keyword_offset = keyword_len; + + if(ctx->strict && check_png_text(text->text, text->text_length)) + { + if(text->type == SPNG_ZTXT) return SPNG_EZTXT; + else return SPNG_ETEXT; + } + } + + text->language_tag = text->keyword + language_tag_offset; + text->translated_keyword = text->keyword + translated_keyword_offset; + + ctx->stored.text = 1; + } + else if(!memcmp(chunk.type, type_splt, 4)) + { + if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS; + if(ctx->user.splt) goto discard; /* XXX: could check profile names for uniqueness */ + if(!chunk.length) return SPNG_ECHUNK_SIZE; + + ctx->file.splt = 1; + + /* chunk.length + sizeof(struct spng_splt) + splt->n_entries * sizeof(struct spng_splt_entry) */ + if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_splt), 1)) return SPNG_ECHUNK_LIMITS; + + ctx->n_splt++; + if(ctx->n_splt < 1) return SPNG_EOVERFLOW; + if(sizeof(struct spng_splt) > SIZE_MAX / ctx->n_splt) return SPNG_EOVERFLOW; + + void *buf = spng__realloc(ctx, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt)); + if(buf == NULL) return SPNG_EMEM; + ctx->splt_list = buf; + + struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1]; + + memset(splt, 0, sizeof(struct spng_splt)); + + ctx->undo = splt_undo; + + void *t = spng__malloc(ctx, chunk.length); + if(t == NULL) return SPNG_EMEM; + + splt->entries = t; /* simplifies error handling */ + data = t; + + ret = read_chunk_bytes2(ctx, t, chunk.length); + if(ret) return ret; + + uint32_t keyword_len = chunk.length < 80 ? chunk.length : 80; + + const unsigned char *keyword_nul = memchr(data, 0, keyword_len); + if(keyword_nul == NULL) return SPNG_ESPLT_NAME; + + keyword_len = keyword_nul - data; + + memcpy(splt->name, data, keyword_len); + + if(check_png_keyword(splt->name)) return SPNG_ESPLT_NAME; + + uint32_t j; + for(j=0; j < (ctx->n_splt - 1); j++) + { + if(!strcmp(ctx->splt_list[j].name, splt->name)) return SPNG_ESPLT_DUP_NAME; + } + + if( (chunk.length - keyword_len) <= 2) return SPNG_ECHUNK_SIZE; + + splt->sample_depth = data[keyword_len + 1]; + + uint32_t entries_len = chunk.length - keyword_len - 2; + if(!entries_len) return SPNG_ECHUNK_SIZE; + + if(splt->sample_depth == 16) + { + if(entries_len % 10 != 0) return SPNG_ECHUNK_SIZE; + splt->n_entries = entries_len / 10; + } + else if(splt->sample_depth == 8) + { + if(entries_len % 6 != 0) return SPNG_ECHUNK_SIZE; + splt->n_entries = entries_len / 6; + } + else return SPNG_ESPLT_DEPTH; + + if(!splt->n_entries) return SPNG_ECHUNK_SIZE; + + size_t list_size = splt->n_entries; + + if(list_size > SIZE_MAX / sizeof(struct spng_splt_entry)) return SPNG_EOVERFLOW; + + list_size *= sizeof(struct spng_splt_entry); + + if(increase_cache_usage(ctx, list_size, 0)) return SPNG_ECHUNK_LIMITS; + + splt->entries = spng__malloc(ctx, list_size); + if(splt->entries == NULL) + { + spng__free(ctx, t); + return SPNG_EMEM; + } + + data = (unsigned char*)t + keyword_len + 2; + + uint32_t k; + if(splt->sample_depth == 16) + { + for(k=0; k < splt->n_entries; k++) + { + splt->entries[k].red = read_u16(data + k * 10); + splt->entries[k].green = read_u16(data + k * 10 + 2); + splt->entries[k].blue = read_u16(data + k * 10 + 4); + splt->entries[k].alpha = read_u16(data + k * 10 + 6); + splt->entries[k].frequency = read_u16(data + k * 10 + 8); + } + } + else if(splt->sample_depth == 8) + { + for(k=0; k < splt->n_entries; k++) + { + splt->entries[k].red = data[k * 6]; + splt->entries[k].green = data[k * 6 + 1]; + splt->entries[k].blue = data[k * 6 + 2]; + splt->entries[k].alpha = data[k * 6 + 3]; + splt->entries[k].frequency = read_u16(data + k * 6 + 4); + } + } + + spng__free(ctx, t); + decrease_cache_usage(ctx, chunk.length); + + ctx->stored.splt = 1; + } + else /* Unknown chunk */ + { + ctx->file.unknown = 1; + + if(!ctx->keep_unknown) goto discard; + if(ctx->user.unknown) goto discard; + + if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_unknown_chunk), 1)) return SPNG_ECHUNK_LIMITS; + + ctx->n_chunks++; + if(ctx->n_chunks < 1) return SPNG_EOVERFLOW; + if(sizeof(struct spng_unknown_chunk) > SIZE_MAX / ctx->n_chunks) return SPNG_EOVERFLOW; + + void *buf = spng__realloc(ctx, ctx->chunk_list, ctx->n_chunks * sizeof(struct spng_unknown_chunk)); + if(buf == NULL) return SPNG_EMEM; + ctx->chunk_list = buf; + + struct spng_unknown_chunk *chunkp = &ctx->chunk_list[ctx->n_chunks - 1]; + + memset(chunkp, 0, sizeof(struct spng_unknown_chunk)); + + ctx->undo = chunk_undo; + + memcpy(chunkp->type, chunk.type, 4); + + if(ctx->state < SPNG_STATE_FIRST_IDAT) + { + if(ctx->file.plte) chunkp->location = SPNG_AFTER_PLTE; + else chunkp->location = SPNG_AFTER_IHDR; + } + else if(ctx->state >= SPNG_STATE_AFTER_IDAT) chunkp->location = SPNG_AFTER_IDAT; + + if(chunk.length > 0) + { + void *t = spng__malloc(ctx, chunk.length); + if(t == NULL) return SPNG_EMEM; + + ret = read_chunk_bytes2(ctx, t, chunk.length); + if(ret) + { + spng__free(ctx, t); + return ret; + } + + chunkp->length = chunk.length; + chunkp->data = t; + } + + ctx->stored.unknown = 1; + } + +discard: + ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); + if(ret) return ret; + } + + } + + return ret; +} + +/* Read chunks before or after the IDAT chunks depending on state */ +static int read_chunks(spng_ctx *ctx, int only_ihdr) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!ctx->state) return SPNG_EBADSTATE; + if(ctx->data == NULL) + { + if(ctx->encode_only) return 0; + else return SPNG_EINTERNAL; + } + + int ret = 0; + + if(ctx->state == SPNG_STATE_INPUT) + { + ret = read_ihdr(ctx); + + if(ret) return decode_err(ctx, ret); + + ctx->state = SPNG_STATE_IHDR; + } + + if(only_ihdr) return 0; + + if(ctx->state == SPNG_STATE_EOI) + { + ctx->state = SPNG_STATE_AFTER_IDAT; + ctx->prev_was_idat = 1; + } + + while(ctx->state < SPNG_STATE_FIRST_IDAT || ctx->state == SPNG_STATE_AFTER_IDAT) + { + ret = read_non_idat_chunks(ctx); + + if(!ret) + { + if(ctx->state < SPNG_STATE_FIRST_IDAT) ctx->state = SPNG_STATE_FIRST_IDAT; + else if(ctx->state == SPNG_STATE_AFTER_IDAT) ctx->state = SPNG_STATE_IEND; + } + else + { + switch(ret) + { + case SPNG_ECHUNK_POS: + case SPNG_ECHUNK_SIZE: /* size != expected size, SPNG_ECHUNK_STDLEN = invalid size */ + case SPNG_EDUP_PLTE: + case SPNG_EDUP_CHRM: + case SPNG_EDUP_GAMA: + case SPNG_EDUP_ICCP: + case SPNG_EDUP_SBIT: + case SPNG_EDUP_SRGB: + case SPNG_EDUP_BKGD: + case SPNG_EDUP_HIST: + case SPNG_EDUP_TRNS: + case SPNG_EDUP_PHYS: + case SPNG_EDUP_TIME: + case SPNG_EDUP_OFFS: + case SPNG_EDUP_EXIF: + case SPNG_ECHRM: + case SPNG_ETRNS_COLOR_TYPE: + case SPNG_ETRNS_NO_PLTE: + case SPNG_EGAMA: + case SPNG_EICCP_NAME: + case SPNG_EICCP_COMPRESSION_METHOD: + case SPNG_ESBIT: + case SPNG_ESRGB: + case SPNG_ETEXT: + case SPNG_ETEXT_KEYWORD: + case SPNG_EZTXT: + case SPNG_EZTXT_COMPRESSION_METHOD: + case SPNG_EITXT: + case SPNG_EITXT_COMPRESSION_FLAG: + case SPNG_EITXT_COMPRESSION_METHOD: + case SPNG_EITXT_LANG_TAG: + case SPNG_EITXT_TRANSLATED_KEY: + case SPNG_EBKGD_NO_PLTE: + case SPNG_EBKGD_PLTE_IDX: + case SPNG_EHIST_NO_PLTE: + case SPNG_EPHYS: + case SPNG_ESPLT_NAME: + case SPNG_ESPLT_DUP_NAME: + case SPNG_ESPLT_DEPTH: + case SPNG_ETIME: + case SPNG_EOFFS: + case SPNG_EEXIF: + case SPNG_EZLIB: + { + if(!ctx->strict && !is_critical_chunk(&ctx->current_chunk)) + { + ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); + if(ret) return decode_err(ctx, ret); + + if(ctx->undo) ctx->undo(ctx); + + ctx->stored = ctx->prev_stored; + + ctx->discard = 0; + ctx->undo = NULL; + + continue; + } + else return decode_err(ctx, ret); + + break; + } + default: return decode_err(ctx, ret); + } + } + } + + return ret; +} + +static int read_scanline(spng_ctx *ctx) +{ + int ret, pass = ctx->row_info.pass; + struct spng_row_info *ri = &ctx->row_info; + const struct spng_subimage *sub = ctx->subimage; + size_t scanline_width = sub[pass].scanline_width; + uint32_t scanline_idx = ri->scanline_idx; + + uint8_t next_filter = 0; + + if(scanline_idx == (sub[pass].height - 1) && ri->pass == ctx->last_pass) + { + ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width - 1); + } + else + { + ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width); + if(ret) return ret; + + next_filter = ctx->scanline[scanline_width - 1]; + if(next_filter > 4) ret = SPNG_EFILTER; + } + + if(ret) return ret; + + if(!scanline_idx && ri->filter > 1) + { + /* prev_scanline is all zeros for the first scanline */ + memset(ctx->prev_scanline, 0, scanline_width); + } + + if(ctx->ihdr.bit_depth == 16 && ctx->fmt != SPNG_FMT_RAW) u16_row_to_host(ctx->scanline, scanline_width - 1); + + ret = defilter_scanline(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, ri->filter); + if(ret) return ret; + + ri->filter = next_filter; + + return 0; +} + +static int update_row_info(spng_ctx *ctx) +{ + int interlacing = ctx->ihdr.interlace_method; + struct spng_row_info *ri = &ctx->row_info; + const struct spng_subimage *sub = ctx->subimage; + + if(ri->scanline_idx == (sub[ri->pass].height - 1)) /* Last scanline */ + { + if(ri->pass == ctx->last_pass) + { + ctx->state = SPNG_STATE_EOI; + + return SPNG_EOI; + } + + ri->scanline_idx = 0; + ri->pass++; + + /* Skip empty passes */ + while( (!sub[ri->pass].width || !sub[ri->pass].height) && (ri->pass < ctx->last_pass)) ri->pass++; + } + else + { + ri->row_num++; + ri->scanline_idx++; + } + + if(interlacing) ri->row_num = adam7_y_start[ri->pass] + ri->scanline_idx * adam7_y_delta[ri->pass]; + + return 0; +} + +int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len) +{ + if(ctx == NULL || out == NULL) return 1; + + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + + struct decode_flags f = ctx->decode_flags; + + struct spng_row_info *ri = &ctx->row_info; + const struct spng_subimage *sub = ctx->subimage; + + const struct spng_ihdr *ihdr = &ctx->ihdr; + const uint16_t *gamma_lut = ctx->gamma_lut; + unsigned char *trns_px = ctx->trns_px; + const struct spng_sbit *sb = &ctx->decode_sb; + const struct spng_plte_entry *plte = ctx->decode_plte.rgba; + struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->scanline); + + const unsigned char *scanline; + + const int pass = ri->pass; + const int fmt = ctx->fmt; + const size_t scanline_width = sub[pass].scanline_width; + const uint32_t width = sub[pass].width; + uint32_t k; + uint8_t r_8, g_8, b_8, a_8, gray_8; + uint16_t r_16, g_16, b_16, a_16, gray_16; + r_8=0; g_8=0; b_8=0; a_8=0; gray_8=0; + r_16=0; g_16=0; b_16=0; a_16=0; gray_16=0; + size_t pixel_size = 4; /* SPNG_FMT_RGBA8 */ + size_t pixel_offset = 0; + unsigned char *pixel; + unsigned processing_depth = ihdr->bit_depth; + + if(f.indexed) processing_depth = 8; + + if(fmt == SPNG_FMT_RGBA16) pixel_size = 8; + else if(fmt == SPNG_FMT_RGB8) pixel_size = 3; + + if(len < sub[pass].out_width) return SPNG_EBUFSIZ; + + int ret = read_scanline(ctx); + + if(ret) return decode_err(ctx, ret); + + scanline = ctx->scanline; + + for(k=0; k < width; k++) + { + pixel = (unsigned char*)out + pixel_offset; + pixel_offset += pixel_size; + + if(f.same_layout) + { + if(f.zerocopy) break; + + memcpy(out, scanline, scanline_width - 1); + break; + } + + if(f.unpack) + { + unpack_scanline(out, scanline, width, ihdr->bit_depth, fmt); + break; + } + + if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR) + { + if(ihdr->bit_depth == 16) + { + memcpy(&r_16, scanline + (k * 6), 2); + memcpy(&g_16, scanline + (k * 6) + 2, 2); + memcpy(&b_16, scanline + (k * 6) + 4, 2); + + a_16 = 65535; + } + else /* == 8 */ + { + if(fmt == SPNG_FMT_RGBA8) + { + rgb8_row_to_rgba8(scanline, out, width); + break; + } + + r_8 = scanline[k * 3]; + g_8 = scanline[k * 3 + 1]; + b_8 = scanline[k * 3 + 2]; + + a_8 = 255; + } + } + else if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) + { + uint8_t entry = 0; + + if(ihdr->bit_depth == 8) + { + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) + { + expand_row(out, scanline, &ctx->decode_plte, width, fmt); + break; + } + + entry = scanline[k]; + } + else /* < 8 */ + { + entry = get_sample(&iter); + } + + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) + { + pixel[0] = plte[entry].red; + pixel[1] = plte[entry].green; + pixel[2] = plte[entry].blue; + if(fmt == SPNG_FMT_RGBA8) pixel[3] = plte[entry].alpha; + + continue; + } + else /* RGBA16 */ + { + r_16 = plte[entry].red; + g_16 = plte[entry].green; + b_16 = plte[entry].blue; + a_16 = plte[entry].alpha; + + r_16 = (r_16 << 8) | r_16; + g_16 = (g_16 << 8) | g_16; + b_16 = (b_16 << 8) | b_16; + a_16 = (a_16 << 8) | a_16; + + memcpy(pixel, &r_16, 2); + memcpy(pixel + 2, &g_16, 2); + memcpy(pixel + 4, &b_16, 2); + memcpy(pixel + 6, &a_16, 2); + + continue; + } + } + else if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) + { + if(ihdr->bit_depth == 16) + { + memcpy(&r_16, scanline + (k * 8), 2); + memcpy(&g_16, scanline + (k * 8) + 2, 2); + memcpy(&b_16, scanline + (k * 8) + 4, 2); + memcpy(&a_16, scanline + (k * 8) + 6, 2); + } + else /* == 8 */ + { + r_8 = scanline[k * 4]; + g_8 = scanline[k * 4 + 1]; + b_8 = scanline[k * 4 + 2]; + a_8 = scanline[k * 4 + 3]; + } + } + else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) + { + if(ihdr->bit_depth == 16) + { + memcpy(&gray_16, scanline + k * 2, 2); + + if(f.apply_trns && ctx->trns.gray == gray_16) a_16 = 0; + else a_16 = 65535; + + r_16 = gray_16; + g_16 = gray_16; + b_16 = gray_16; + } + else /* <= 8 */ + { + gray_8 = get_sample(&iter); + + if(f.apply_trns && ctx->trns.gray == gray_8) a_8 = 0; + else a_8 = 255; + + r_8 = gray_8; g_8 = gray_8; b_8 = gray_8; + } + } + else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) + { + if(ihdr->bit_depth == 16) + { + memcpy(&gray_16, scanline + (k * 4), 2); + memcpy(&a_16, scanline + (k * 4) + 2, 2); + + r_16 = gray_16; + g_16 = gray_16; + b_16 = gray_16; + } + else /* == 8 */ + { + gray_8 = scanline[k * 2]; + a_8 = scanline[k * 2 + 1]; + + r_8 = gray_8; + g_8 = gray_8; + b_8 = gray_8; + } + } + + + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) + { + if(ihdr->bit_depth == 16) + { + r_8 = r_16 >> 8; + g_8 = g_16 >> 8; + b_8 = b_16 >> 8; + a_8 = a_16 >> 8; + } + + pixel[0] = r_8; + pixel[1] = g_8; + pixel[2] = b_8; + + if(fmt == SPNG_FMT_RGBA8) pixel[3] = a_8; + } + else if(fmt == SPNG_FMT_RGBA16) + { + if(ihdr->bit_depth != 16) + { + r_16 = r_8; + g_16 = g_8; + b_16 = b_8; + a_16 = a_8; + } + + memcpy(pixel, &r_16, 2); + memcpy(pixel + 2, &g_16, 2); + memcpy(pixel + 4, &b_16, 2); + memcpy(pixel + 6, &a_16, 2); + } + }/* for(k=0; k < width; k++) */ + + if(f.apply_trns) trns_row(out, scanline, trns_px, ctx->bytes_per_pixel, &ctx->ihdr, width, fmt); + + if(f.do_scaling) scale_row(out, width, fmt, processing_depth, sb); + + if(f.apply_gamma) gamma_correct_row(out, width, fmt, gamma_lut); + + /* The previous scanline is always defiltered */ + void *t = ctx->prev_scanline; + ctx->prev_scanline = ctx->scanline; + ctx->scanline = t; + + ret = update_row_info(ctx); + + if(ret == SPNG_EOI) + { + if(ctx->cur_chunk_bytes_left) /* zlib stream ended before an IDAT chunk boundary */ + {/* Discard the rest of the chunk */ + int error = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left); + if(error) return decode_err(ctx, error); + } + + ctx->last_idat = ctx->current_chunk; + } + + return ret; +} + +int spng_decode_row(spng_ctx *ctx, void *out, size_t len) +{ + if(ctx == NULL || out == NULL) return 1; + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + if(len < ctx->image_width) return SPNG_EBUFSIZ; + + const struct spng_ihdr *ihdr = &ctx->ihdr; + int ret, pass = ctx->row_info.pass; + unsigned char *outptr = out; + + if(!ihdr->interlace_method || pass == 6) return spng_decode_scanline(ctx, out, len); + + ret = spng_decode_scanline(ctx, ctx->row, ctx->image_width); + if(ret && ret != SPNG_EOI) return ret; + + uint32_t k; + unsigned pixel_size = 4; /* RGBA8 */ + if(ctx->fmt == SPNG_FMT_RGBA16) pixel_size = 8; + else if(ctx->fmt == SPNG_FMT_RGB8) pixel_size = 3; + else if(ctx->fmt == SPNG_FMT_G8) pixel_size = 1; + else if(ctx->fmt == SPNG_FMT_GA8) pixel_size = 2; + else if(ctx->fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) + { + if(ihdr->bit_depth < 8) + { + struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->row); + const uint8_t samples_per_byte = 8 / ihdr->bit_depth; + uint8_t sample; + + for(k=0; k < ctx->subimage[pass].width; k++) + { + sample = get_sample(&iter); + + size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass]; + + sample = sample << (iter.initial_shift - ioffset * ihdr->bit_depth % 8); + + ioffset /= samples_per_byte; + + outptr[ioffset] |= sample; + } + + return 0; + } + else pixel_size = ctx->bytes_per_pixel; + } + + for(k=0; k < ctx->subimage[pass].width; k++) + { + size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size; + + memcpy(outptr + ioffset, ctx->row + k * pixel_size, pixel_size); + } + + return 0; +} + +int spng_decode_chunks(spng_ctx *ctx) +{ + if(ctx == NULL) return 1; + if(ctx->encode_only) return SPNG_ECTXTYPE; + if(ctx->state < SPNG_STATE_INPUT) return SPNG_ENOSRC; + if(ctx->state == SPNG_STATE_IEND) return 0; + + return read_chunks(ctx, 0); +} + +int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags) +{ + if(ctx == NULL) return 1; + if(ctx->encode_only) return SPNG_ECTXTYPE; + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + + const struct spng_ihdr *ihdr = &ctx->ihdr; + + int ret = read_chunks(ctx, 0); + if(ret) return decode_err(ctx, ret); + + ret = check_decode_fmt(ihdr, fmt); + if(ret) return ret; + + ret = calculate_image_width(ihdr, fmt, &ctx->image_width); + if(ret) return decode_err(ctx, ret); + + if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */ + else ctx->image_size = ctx->image_width * ihdr->height; + + if( !(flags & SPNG_DECODE_PROGRESSIVE) ) + { + if(out == NULL) return 1; + if(!ctx->image_size) return SPNG_EOVERFLOW; + if(len < ctx->image_size) return SPNG_EBUFSIZ; + } + + uint32_t bytes_read = 0; + + ret = read_idat_bytes(ctx, &bytes_read); + if(ret) return decode_err(ctx, ret); + + if(bytes_read > 1) + { + int valid = read_u16(ctx->data) % 31 ? 0 : 1; + + unsigned flg = ctx->data[1]; + unsigned flevel = flg >> 6; + int compression_level = Z_DEFAULT_COMPRESSION; + + if(flevel == 0) compression_level = 0; /* fastest */ + else if(flevel == 1) compression_level = 1; /* fast */ + else if(flevel == 2) compression_level = 6; /* default */ + else if(flevel == 3) compression_level = 9; /* slowest, max compression */ + + if(valid) ctx->image_options.compression_level = compression_level; + } + + ret = spng__inflate_init(ctx, ctx->image_options.window_bits); + if(ret) return decode_err(ctx, ret); + + ctx->zstream.avail_in = bytes_read; + ctx->zstream.next_in = ctx->data; + + size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width; + + scanline_buf_size += 32; + + if(scanline_buf_size < 32) return SPNG_EOVERFLOW; + + ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size); + ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size); + + ctx->scanline = ctx->scanline_buf; + ctx->prev_scanline = ctx->prev_scanline_buf; + + struct decode_flags f = {0}; + + ctx->fmt = fmt; + + if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) f.indexed = 1; + + unsigned processing_depth = ihdr->bit_depth; + + if(f.indexed) processing_depth = 8; + + if(ihdr->interlace_method) + { + f.interlaced = 1; + ctx->row_buf = spng__malloc(ctx, ctx->image_width); + ctx->row = ctx->row_buf; + + if(ctx->row == NULL) return decode_err(ctx, SPNG_EMEM); + } + + if(ctx->scanline == NULL || ctx->prev_scanline == NULL) + { + return decode_err(ctx, SPNG_EMEM); + } + + f.do_scaling = 1; + if(f.indexed) f.do_scaling = 0; + + unsigned depth_target = 8; /* FMT_RGBA8, G8 */ + if(fmt == SPNG_FMT_RGBA16) depth_target = 16; + + if(flags & SPNG_DECODE_TRNS && ctx->stored.trns) f.apply_trns = 1; + else flags &= ~SPNG_DECODE_TRNS; + + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || + ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) flags &= ~SPNG_DECODE_TRNS; + + if(flags & SPNG_DECODE_GAMMA && ctx->stored.gama) f.apply_gamma = 1; + else flags &= ~SPNG_DECODE_GAMMA; + + if(flags & SPNG_DECODE_USE_SBIT && ctx->stored.sbit) f.use_sbit = 1; + else flags &= ~SPNG_DECODE_USE_SBIT; + + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16)) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA && + ihdr->bit_depth == depth_target) f.same_layout = 1; + } + else if(fmt == SPNG_FMT_RGB8) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR && + ihdr->bit_depth == depth_target) f.same_layout = 1; + + f.apply_trns = 0; /* not applicable */ + } + else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) + { + f.same_layout = 1; + f.do_scaling = 0; + f.apply_gamma = 0; /* for now */ + f.apply_trns = 0; + } + else if(fmt == SPNG_FMT_G8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) + { + if(ihdr->bit_depth == depth_target) f.same_layout = 1; + else if(ihdr->bit_depth < 8) f.unpack = 1; + + f.apply_trns = 0; + } + else if(fmt == SPNG_FMT_GA8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA && + ihdr->bit_depth == depth_target) f.same_layout = 1; + else if(ihdr->bit_depth <= 8) f.unpack = 1; + } + else if(fmt == SPNG_FMT_GA16 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA && + ihdr->bit_depth == depth_target) f.same_layout = 1; + else if(ihdr->bit_depth == 16) f.unpack = 1; + } + + /*if(f.same_layout && !flags && !f.interlaced) f.zerocopy = 1;*/ + + uint16_t *gamma_lut = NULL; + + if(f.apply_gamma) + { + float file_gamma = (float)ctx->gama / 100000.0f; + float max; + + unsigned lut_entries; + + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) + { + lut_entries = 256; + max = 255.0f; + + gamma_lut = ctx->gamma_lut8; + ctx->gamma_lut = ctx->gamma_lut8; + } + else /* SPNG_FMT_RGBA16 */ + { + lut_entries = 65536; + max = 65535.0f; + + ctx->gamma_lut16 = spng__malloc(ctx, lut_entries * sizeof(uint16_t)); + if(ctx->gamma_lut16 == NULL) return decode_err(ctx, SPNG_EMEM); + + gamma_lut = ctx->gamma_lut16; + ctx->gamma_lut = ctx->gamma_lut16; + } + + float screen_gamma = 2.2f; + float exponent = file_gamma * screen_gamma; + + if(FP_ZERO == fpclassify(exponent)) return decode_err(ctx, SPNG_EGAMA); + + exponent = 1.0f / exponent; + + unsigned i; + for(i=0; i < lut_entries; i++) + { + float c = pow((float)i / max, exponent) * max; + if(c > max) c = max; + + gamma_lut[i] = (uint16_t)c; + } + } + + struct spng_sbit *sb = &ctx->decode_sb; + + sb->red_bits = processing_depth; + sb->green_bits = processing_depth; + sb->blue_bits = processing_depth; + sb->alpha_bits = processing_depth; + sb->grayscale_bits = processing_depth; + + if(f.use_sbit) + { + if(ihdr->color_type == 0) + { + sb->grayscale_bits = ctx->sbit.grayscale_bits; + sb->alpha_bits = ihdr->bit_depth; + } + else if(ihdr->color_type == 2 || ihdr->color_type == 3) + { + sb->red_bits = ctx->sbit.red_bits; + sb->green_bits = ctx->sbit.green_bits; + sb->blue_bits = ctx->sbit.blue_bits; + sb->alpha_bits = ihdr->bit_depth; + } + else if(ihdr->color_type == 4) + { + sb->grayscale_bits = ctx->sbit.grayscale_bits; + sb->alpha_bits = ctx->sbit.alpha_bits; + } + else /* == 6 */ + { + sb->red_bits = ctx->sbit.red_bits; + sb->green_bits = ctx->sbit.green_bits; + sb->blue_bits = ctx->sbit.blue_bits; + sb->alpha_bits = ctx->sbit.alpha_bits; + } + } + + if(ihdr->bit_depth == 16 && fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8)) + {/* samples are scaled down by 8 bits in the decode loop */ + sb->red_bits -= 8; + sb->green_bits -= 8; + sb->blue_bits -= 8; + sb->alpha_bits -= 8; + sb->grayscale_bits -= 8; + + processing_depth = 8; + } + + /* Prevent infinite loops in sample_to_target() */ + if(!depth_target || depth_target > 16 || + !processing_depth || processing_depth > 16 || + !sb->grayscale_bits || sb->grayscale_bits > processing_depth || + !sb->alpha_bits || sb->alpha_bits > processing_depth || + !sb->red_bits || sb->red_bits > processing_depth || + !sb->green_bits || sb->green_bits > processing_depth || + !sb->blue_bits || sb->blue_bits > processing_depth) + { + return decode_err(ctx, SPNG_ESBIT); + } + + if(sb->red_bits == sb->green_bits && + sb->green_bits == sb->blue_bits && + sb->blue_bits == sb->alpha_bits && + sb->alpha_bits == processing_depth && + processing_depth == depth_target) f.do_scaling = 0; + + struct spng_plte_entry *plte = ctx->decode_plte.rgba; + + /* Pre-process palette entries */ + if(f.indexed) + { + uint8_t red, green, blue, alpha; + + uint32_t i; + for(i=0; i < 256; i++) + { + if(f.apply_trns && i < ctx->trns.n_type3_entries) + ctx->plte.entries[i].alpha = ctx->trns.type3_alpha[i]; + else + ctx->plte.entries[i].alpha = 255; + + red = sample_to_target(ctx->plte.entries[i].red, 8, sb->red_bits, 8); + green = sample_to_target(ctx->plte.entries[i].green, 8, sb->green_bits, 8); + blue = sample_to_target(ctx->plte.entries[i].blue, 8, sb->blue_bits, 8); + alpha = sample_to_target(ctx->plte.entries[i].alpha, 8, sb->alpha_bits, 8); + +#if defined(SPNG_ARM) + if(fmt == SPNG_FMT_RGB8 && ihdr->bit_depth == 8) + {/* Working with 3 bytes at a time is more of an ARM thing */ + ctx->decode_plte.rgb[i * 3 + 0] = red; + ctx->decode_plte.rgb[i * 3 + 1] = green; + ctx->decode_plte.rgb[i * 3 + 2] = blue; + continue; + } +#endif + plte[i].red = red; + plte[i].green = green; + plte[i].blue = blue; + plte[i].alpha = alpha; + } + + f.apply_trns = 0; + } + + unsigned char *trns_px = ctx->trns_px; + + if(f.apply_trns) + { + uint16_t mask = ~0; + if(ctx->ihdr.bit_depth < 16) mask = (1 << ctx->ihdr.bit_depth) - 1; + + if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16)) + { + if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR) + { + if(ihdr->bit_depth == 16) + { + memcpy(trns_px, &ctx->trns.red, 2); + memcpy(trns_px + 2, &ctx->trns.green, 2); + memcpy(trns_px + 4, &ctx->trns.blue, 2); + } + else + { + trns_px[0] = ctx->trns.red & mask; + trns_px[1] = ctx->trns.green & mask; + trns_px[2] = ctx->trns.blue & mask; + } + } + } + else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) // fmt == SPNG_FMT_GA8 && + { + if(ihdr->bit_depth == 16) + { + memcpy(trns_px, &ctx->trns.gray, 2); + } + else + { + trns_px[0] = ctx->trns.gray & mask; + } + } + } + + ctx->decode_flags = f; + + ctx->state = SPNG_STATE_DECODE_INIT; + + struct spng_row_info *ri = &ctx->row_info; + struct spng_subimage *sub = ctx->subimage; + + while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++; + + if(f.interlaced) ri->row_num = adam7_y_start[ri->pass]; + + unsigned pixel_size = 4; /* SPNG_FMT_RGBA8 */ + + if(fmt == SPNG_FMT_RGBA16) pixel_size = 8; + else if(fmt == SPNG_FMT_RGB8) pixel_size = 3; + else if(fmt == SPNG_FMT_G8) pixel_size = 1; + else if(fmt == SPNG_FMT_GA8) pixel_size = 2; + + int i; + for(i=ri->pass; i <= ctx->last_pass; i++) + { + if(!sub[i].scanline_width) continue; + + if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) sub[i].out_width = sub[i].scanline_width - 1; + else sub[i].out_width = (size_t)sub[i].width * pixel_size; + + if(sub[i].out_width > UINT32_MAX) return decode_err(ctx, SPNG_EOVERFLOW); + } + + /* Read the first filter byte, offsetting all reads by 1 byte. + The scanlines will be aligned with the start of the array with + the next scanline's filter byte at the end, + the last scanline will end up being 1 byte "shorter". */ + ret = read_scanline_bytes(ctx, &ri->filter, 1); + if(ret) return decode_err(ctx, ret); + + if(ri->filter > 4) return decode_err(ctx, SPNG_EFILTER); + + if(flags & SPNG_DECODE_PROGRESSIVE) + { + return 0; + } + + do + { + size_t ioffset = ri->row_num * ctx->image_width; + + ret = spng_decode_row(ctx, (unsigned char*)out + ioffset, ctx->image_width); + }while(!ret); + + if(ret != SPNG_EOI) return decode_err(ctx, ret); + + return 0; +} + +int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info) +{ + if(ctx == NULL || row_info == NULL || ctx->state < SPNG_STATE_DECODE_INIT) return 1; + + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + + *row_info = ctx->row_info; + + return 0; +} + +static int write_chunks_before_idat(spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + if(!ctx->encode_only) return SPNG_EINTERNAL; + if(!ctx->stored.ihdr) return SPNG_EINTERNAL; + + int ret; + uint32_t i; + size_t length; + const struct spng_ihdr *ihdr = &ctx->ihdr; + unsigned char *data = ctx->decode_plte.raw; + + ret = write_data(ctx, spng_signature, 8); + if(ret) return ret; + + write_u32(data, ihdr->width); + write_u32(data + 4, ihdr->height); + data[8] = ihdr->bit_depth; + data[9] = ihdr->color_type; + data[10] = ihdr->compression_method; + data[11] = ihdr->filter_method; + data[12] = ihdr->interlace_method; + + ret = write_chunk(ctx, type_ihdr, data, 13); + if(ret) return ret; + + if(ctx->stored.chrm) + { + write_u32(data, ctx->chrm_int.white_point_x); + write_u32(data + 4, ctx->chrm_int.white_point_y); + write_u32(data + 8, ctx->chrm_int.red_x); + write_u32(data + 12, ctx->chrm_int.red_y); + write_u32(data + 16, ctx->chrm_int.green_x); + write_u32(data + 20, ctx->chrm_int.green_y); + write_u32(data + 24, ctx->chrm_int.blue_x); + write_u32(data + 28, ctx->chrm_int.blue_y); + + ret = write_chunk(ctx, type_chrm, data, 32); + if(ret) return ret; + } + + if(ctx->stored.gama) + { + write_u32(data, ctx->gama); + + ret = write_chunk(ctx, type_gama, data, 4); + if(ret) return ret; + } + + if(ctx->stored.iccp) + { + uLongf dest_len = compressBound((uLong)ctx->iccp.profile_len); + + Bytef *buf = spng__malloc(ctx, dest_len); + if(buf == NULL) return SPNG_EMEM; + + ret = compress2(buf, &dest_len, (void*)ctx->iccp.profile, (uLong)ctx->iccp.profile_len, Z_DEFAULT_COMPRESSION); + + if(ret != Z_OK) + { + spng__free(ctx, buf); + return SPNG_EZLIB; + } + + size_t name_len = strlen(ctx->iccp.profile_name); + + length = name_len + 2; + length += dest_len; + + if(dest_len > length) return SPNG_EOVERFLOW; + + unsigned char *cdata = NULL; + + ret = write_header(ctx, type_iccp, length, &cdata); + + if(ret) + { + spng__free(ctx, buf); + return ret; + } + + memcpy(cdata, ctx->iccp.profile_name, name_len + 1); + cdata[name_len + 1] = 0; /* compression method */ + memcpy(cdata + name_len + 2, buf, dest_len); + + spng__free(ctx, buf); + + ret = finish_chunk(ctx); + if(ret) return ret; + } + + if(ctx->stored.sbit) + { + switch(ctx->ihdr.color_type) + { + case SPNG_COLOR_TYPE_GRAYSCALE: + { + length = 1; + + data[0] = ctx->sbit.grayscale_bits; + + break; + } + case SPNG_COLOR_TYPE_TRUECOLOR: + case SPNG_COLOR_TYPE_INDEXED: + { + length = 3; + + data[0] = ctx->sbit.red_bits; + data[1] = ctx->sbit.green_bits; + data[2] = ctx->sbit.blue_bits; + + break; + } + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: + { + length = 2; + + data[0] = ctx->sbit.grayscale_bits; + data[1] = ctx->sbit.alpha_bits; + + break; + } + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: + { + length = 4; + + data[0] = ctx->sbit.red_bits; + data[1] = ctx->sbit.green_bits; + data[2] = ctx->sbit.blue_bits; + data[3] = ctx->sbit.alpha_bits; + + break; + } + default: return SPNG_EINTERNAL; + } + + ret = write_chunk(ctx, type_sbit, data, length); + if(ret) return ret; + } + + if(ctx->stored.srgb) + { + ret = write_chunk(ctx, type_srgb, &ctx->srgb_rendering_intent, 1); + if(ret) return ret; + } + + ret = write_unknown_chunks(ctx, SPNG_AFTER_IHDR); + if(ret) return ret; + + if(ctx->stored.plte) + { + for(i=0; i < ctx->plte.n_entries; i++) + { + data[i * 3 + 0] = ctx->plte.entries[i].red; + data[i * 3 + 1] = ctx->plte.entries[i].green; + data[i * 3 + 2] = ctx->plte.entries[i].blue; + } + + ret = write_chunk(ctx, type_plte, data, ctx->plte.n_entries * 3); + if(ret) return ret; + } + + if(ctx->stored.bkgd) + { + switch(ctx->ihdr.color_type) + { + case SPNG_COLOR_TYPE_GRAYSCALE: + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: + { + length = 2; + + write_u16(data, ctx->bkgd.gray); + + break; + } + case SPNG_COLOR_TYPE_TRUECOLOR: + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: + { + length = 6; + + write_u16(data, ctx->bkgd.red); + write_u16(data + 2, ctx->bkgd.green); + write_u16(data + 4, ctx->bkgd.blue); + + break; + } + case SPNG_COLOR_TYPE_INDEXED: + { + length = 1; + + data[0] = ctx->bkgd.plte_index; + + break; + } + default: return SPNG_EINTERNAL; + } + + ret = write_chunk(ctx, type_bkgd, data, length); + if(ret) return ret; + } + + if(ctx->stored.hist) + { + length = ctx->plte.n_entries * 2; + + for(i=0; i < ctx->plte.n_entries; i++) + { + write_u16(data + i * 2, ctx->hist.frequency[i]); + } + + ret = write_chunk(ctx, type_hist, data, length); + if(ret) return ret; + } + + if(ctx->stored.trns) + { + if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) + { + write_u16(data, ctx->trns.gray); + + ret = write_chunk(ctx, type_trns, data, 2); + } + else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) + { + write_u16(data, ctx->trns.red); + write_u16(data + 2, ctx->trns.green); + write_u16(data + 4, ctx->trns.blue); + + ret = write_chunk(ctx, type_trns, data, 6); + } + else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) + { + ret = write_chunk(ctx, type_trns, ctx->trns.type3_alpha, ctx->trns.n_type3_entries); + } + + if(ret) return ret; + } + + if(ctx->stored.phys) + { + write_u32(data, ctx->phys.ppu_x); + write_u32(data + 4, ctx->phys.ppu_y); + data[8] = ctx->phys.unit_specifier; + + ret = write_chunk(ctx, type_phys, data, 9); + if(ret) return ret; + } + + if(ctx->stored.splt) + { + const struct spng_splt *splt; + unsigned char *cdata = NULL; + + uint32_t k; + for(i=0; i < ctx->n_splt; i++) + { + splt = &ctx->splt_list[i]; + + size_t name_len = strlen(splt->name); + length = name_len + 1; + + if(splt->sample_depth == 8) length += splt->n_entries * 6 + 1; + else if(splt->sample_depth == 16) length += splt->n_entries * 10 + 1; + + ret = write_header(ctx, type_splt, length, &cdata); + if(ret) return ret; + + memcpy(cdata, splt->name, name_len + 1); + cdata += name_len + 2; + cdata[-1] = splt->sample_depth; + + if(splt->sample_depth == 8) + { + for(k=0; k < splt->n_entries; k++) + { + cdata[k * 6 + 0] = splt->entries[k].red; + cdata[k * 6 + 1] = splt->entries[k].green; + cdata[k * 6 + 2] = splt->entries[k].blue; + cdata[k * 6 + 3] = splt->entries[k].alpha; + write_u16(cdata + k * 6 + 4, splt->entries[k].frequency); + } + } + else if(splt->sample_depth == 16) + { + for(k=0; k < splt->n_entries; k++) + { + write_u16(cdata + k * 10 + 0, splt->entries[k].red); + write_u16(cdata + k * 10 + 2, splt->entries[k].green); + write_u16(cdata + k * 10 + 4, splt->entries[k].blue); + write_u16(cdata + k * 10 + 6, splt->entries[k].alpha); + write_u16(cdata + k * 10 + 8, splt->entries[k].frequency); + } + } + + ret = finish_chunk(ctx); + if(ret) return ret; + } + } + + if(ctx->stored.time) + { + write_u16(data, ctx->time.year); + data[2] = ctx->time.month; + data[3] = ctx->time.day; + data[4] = ctx->time.hour; + data[5] = ctx->time.minute; + data[6] = ctx->time.second; + + ret = write_chunk(ctx, type_time, data, 7); + if(ret) return ret; + } + + if(ctx->stored.text) + { + unsigned char *cdata = NULL; + const struct spng_text2 *text; + const uint8_t *text_type_array[4] = { 0, type_text, type_ztxt, type_itxt }; + + for(i=0; i < ctx->n_text; i++) + { + text = &ctx->text_list[i]; + + const uint8_t *text_chunk_type = text_type_array[text->type]; + Bytef *compressed_text = NULL; + size_t keyword_len = 0; + size_t language_tag_len = 0; + size_t translated_keyword_len = 0; + size_t compressed_length = 0; + size_t text_length = 0; + + keyword_len = strlen(text->keyword); + text_length = strlen(text->text); + + length = keyword_len + 1; + + if(text->type == SPNG_ZTXT) + { + length += 1; /* compression method */ + } + else if(text->type == SPNG_ITXT) + { + if(!text->language_tag || !text->translated_keyword) return SPNG_EINTERNAL; + + language_tag_len = strlen(text->language_tag); + translated_keyword_len = strlen(text->translated_keyword); + + length += language_tag_len; + if(length < language_tag_len) return SPNG_EOVERFLOW; + + length += translated_keyword_len; + if(length < translated_keyword_len) return SPNG_EOVERFLOW; + + length += 4; /* compression flag + method + nul for the two strings */ + if(length < 4) return SPNG_EOVERFLOW; + } + + if(text->compression_flag) + { + ret = spng__deflate_init(ctx, &ctx->text_options); + if(ret) return ret; + + z_stream *zstream = &ctx->zstream; + uLongf dest_len = deflateBound(zstream, (uLong)text_length); + + compressed_text = spng__malloc(ctx, dest_len); + + if(compressed_text == NULL) return SPNG_EMEM; + + zstream->next_in = (void*)text->text; + zstream->avail_in = (uInt)text_length; + + zstream->next_out = compressed_text; + zstream->avail_out = dest_len; + + ret = deflate(zstream, Z_FINISH); + + if(ret != Z_STREAM_END) + { + spng__free(ctx, compressed_text); + return SPNG_EZLIB; + } + + compressed_length = zstream->total_out; + + length += compressed_length; + if(length < compressed_length) return SPNG_EOVERFLOW; + } + else + { + text_length = strlen(text->text); + + length += text_length; + if(length < text_length) return SPNG_EOVERFLOW; + } + + ret = write_header(ctx, text_chunk_type, length, &cdata); + if(ret) + { + spng__free(ctx, compressed_text); + return ret; + } + + memcpy(cdata, text->keyword, keyword_len + 1); + cdata += keyword_len + 1; + + if(text->type == SPNG_ITXT) + { + cdata[0] = text->compression_flag; + cdata[1] = 0; /* compression method */ + cdata += 2; + + memcpy(cdata, text->language_tag, language_tag_len + 1); + cdata += language_tag_len + 1; + + memcpy(cdata, text->translated_keyword, translated_keyword_len + 1); + cdata += translated_keyword_len + 1; + } + else if(text->type == SPNG_ZTXT) + { + cdata[0] = 0; /* compression method */ + cdata++; + } + + if(text->compression_flag) memcpy(cdata, compressed_text, compressed_length); + else memcpy(cdata, text->text, text_length); + + spng__free(ctx, compressed_text); + + ret = finish_chunk(ctx); + if(ret) return ret; + } + } + + if(ctx->stored.offs) + { + write_s32(data, ctx->offs.x); + write_s32(data + 4, ctx->offs.y); + data[8] = ctx->offs.unit_specifier; + + ret = write_chunk(ctx, type_offs, data, 9); + if(ret) return ret; + } + + if(ctx->stored.exif) + { + ret = write_chunk(ctx, type_exif, ctx->exif.data, ctx->exif.length); + if(ret) return ret; + } + + ret = write_unknown_chunks(ctx, SPNG_AFTER_PLTE); + if(ret) return ret; + + return 0; +} + +static int write_chunks_after_idat(spng_ctx *ctx) +{ + if(ctx == NULL) return SPNG_EINTERNAL; + + int ret = write_unknown_chunks(ctx, SPNG_AFTER_IDAT); + if(ret) return ret; + + return write_iend(ctx); +} + +/* Compress and write scanline to IDAT stream */ +static int write_idat_bytes(spng_ctx *ctx, const void *scanline, size_t len, int flush) +{ + if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL; + if(len > UINT_MAX) return SPNG_EINTERNAL; + + int ret = 0; + unsigned char *data = NULL; + z_stream *zstream = &ctx->zstream; + uint32_t idat_length = SPNG_WRITE_SIZE; + + zstream->next_in = scanline; + zstream->avail_in = (uInt)len; + + do + { + ret = deflate(zstream, flush); + + if(zstream->avail_out == 0) + { + ret = finish_chunk(ctx); + if(ret) return encode_err(ctx, ret); + + ret = write_header(ctx, type_idat, idat_length, &data); + if(ret) return encode_err(ctx, ret); + + zstream->next_out = data; + zstream->avail_out = idat_length; + } + + }while(zstream->avail_in); + + if(ret != Z_OK) return SPNG_EZLIB; + + return 0; +} + +static int finish_idat(spng_ctx *ctx) +{ + int ret = 0; + unsigned char *data = NULL; + z_stream *zstream = &ctx->zstream; + uint32_t idat_length = SPNG_WRITE_SIZE; + + while(ret != Z_STREAM_END) + { + ret = deflate(zstream, Z_FINISH); + + if(ret) + { + if(ret == Z_STREAM_END) break; + + if(ret != Z_BUF_ERROR) return SPNG_EZLIB; + } + + if(zstream->avail_out == 0) + { + ret = finish_chunk(ctx); + if(ret) return encode_err(ctx, ret); + + ret = write_header(ctx, type_idat, idat_length, &data); + if(ret) return encode_err(ctx, ret); + + zstream->next_out = data; + zstream->avail_out = idat_length; + } + } + + uint32_t trimmed_length = idat_length - zstream->avail_out; + + ret = trim_chunk(ctx, trimmed_length); + if(ret) return ret; + + return finish_chunk(ctx); +} + +static int encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) +{ + if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL; + + int ret, pass = ctx->row_info.pass; + uint8_t filter = 0; + struct spng_row_info *ri = &ctx->row_info; + const struct spng_subimage *sub = ctx->subimage; + struct encode_flags f = ctx->encode_flags; + unsigned char *filtered_scanline = ctx->filtered_scanline; + size_t scanline_width = sub[pass].scanline_width; + + if(len < scanline_width - 1) return SPNG_EINTERNAL; + + /* encode_row() interlaces directly to ctx->scanline */ + if(scanline != ctx->scanline) memcpy(ctx->scanline, scanline, scanline_width - 1); + + if(f.to_bigendian) u16_row_to_bigendian(ctx->scanline, scanline_width - 1); + const int requires_previous = f.filter_choice & (SPNG_FILTER_CHOICE_UP | SPNG_FILTER_CHOICE_AVG | SPNG_FILTER_CHOICE_PAETH); + + /* XXX: exclude 'requires_previous' filters by default for first scanline? */ + if(!ri->scanline_idx && requires_previous) + { + /* prev_scanline is all zeros for the first scanline */ + memset(ctx->prev_scanline, 0, scanline_width); + } + + filter = get_best_filter(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, f.filter_choice); + + if(!filter) filtered_scanline = ctx->scanline; + + filtered_scanline[-1] = filter; + + if(filter) + { + ret = filter_scanline(filtered_scanline, ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, filter); + if(ret) return encode_err(ctx, ret); + } + + ret = write_idat_bytes(ctx, filtered_scanline - 1, scanline_width, Z_NO_FLUSH); + if(ret) return encode_err(ctx, ret); + + /* The previous scanline is always unfiltered */ + void *t = ctx->prev_scanline; + ctx->prev_scanline = ctx->scanline; + ctx->scanline = t; + + ret = update_row_info(ctx); + + if(ret == SPNG_EOI) + { + int error = finish_idat(ctx); + if(error) encode_err(ctx, error); + + if(f.finalize) + { + error = spng_encode_chunks(ctx); + if(error) return encode_err(ctx, error); + } + } + + return ret; +} + +static int encode_row(spng_ctx *ctx, const void *row, size_t len) +{ + if(ctx == NULL || row == NULL) return SPNG_EINTERNAL; + + const int pass = ctx->row_info.pass; + + if(!ctx->ihdr.interlace_method || pass == 6) return encode_scanline(ctx, row, len); + + uint32_t k; + const unsigned pixel_size = ctx->pixel_size; + const unsigned bit_depth = ctx->ihdr.bit_depth; + + if(bit_depth < 8) + { + const unsigned samples_per_byte = 8 / bit_depth; + const uint8_t mask = (1 << bit_depth) - 1; + const unsigned initial_shift = 8 - bit_depth; + unsigned shift_amount = initial_shift; + + unsigned char *scanline = ctx->scanline; + const unsigned char *row_uc = row; + uint8_t sample; + + memset(scanline, 0, ctx->subimage[pass].scanline_width); + + for(k=0; k < ctx->subimage[pass].width; k++) + { + size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass]; + + sample = row_uc[ioffset / samples_per_byte]; + + sample = sample >> (initial_shift - ioffset * bit_depth % 8); + sample = sample & mask; + sample = sample << shift_amount; + + scanline[0] |= sample; + + shift_amount -= bit_depth; + + if(shift_amount > 7) + { + shift_amount = initial_shift; + scanline++; + } + } + + return encode_scanline(ctx, ctx->scanline, len); + } + + for(k=0; k < ctx->subimage[pass].width; k++) + { + size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size; + + memcpy(ctx->scanline + k * pixel_size, (unsigned char*)row + ioffset, pixel_size); + } + + return encode_scanline(ctx, ctx->scanline, len); +} + +int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) +{ + if(ctx == NULL || scanline == NULL) return SPNG_EINVAL; + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + if(len < (ctx->subimage[ctx->row_info.pass].scanline_width -1) ) return SPNG_EBUFSIZ; + + return encode_scanline(ctx, scanline, len); +} + +int spng_encode_row(spng_ctx *ctx, const void *row, size_t len) +{ + if(ctx == NULL || row == NULL) return SPNG_EINVAL; + if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI; + if(len < ctx->image_width) return SPNG_EBUFSIZ; + + return encode_row(ctx, row, len); +} + +int spng_encode_chunks(spng_ctx *ctx) +{ + if(ctx == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + if(ctx->state < SPNG_STATE_OUTPUT) return SPNG_ENODST; + if(!ctx->encode_only) return SPNG_ECTXTYPE; + + int ret = 0; + + if(ctx->state < SPNG_STATE_FIRST_IDAT) + { + if(!ctx->stored.ihdr) return SPNG_ENOIHDR; + + ret = write_chunks_before_idat(ctx); + if(ret) return encode_err(ctx, ret); + + ctx->state = SPNG_STATE_FIRST_IDAT; + } + else if(ctx->state == SPNG_STATE_FIRST_IDAT) + { + return 0; + } + else if(ctx->state == SPNG_STATE_EOI) + { + ret = write_chunks_after_idat(ctx); + if(ret) return encode_err(ctx, ret); + + ctx->state = SPNG_STATE_IEND; + } + else return SPNG_EOPSTATE; + + return 0; +} + +int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags) +{ + if(ctx == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + if(!ctx->encode_only) return SPNG_ECTXTYPE; + if(!ctx->stored.ihdr) return SPNG_ENOIHDR; + if( !(fmt == SPNG_FMT_PNG || fmt == SPNG_FMT_RAW) ) return SPNG_EFMT; + + int ret = 0; + const struct spng_ihdr *ihdr = &ctx->ihdr; + struct encode_flags *encode_flags = &ctx->encode_flags; + + if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED && !ctx->stored.plte) return SPNG_ENOPLTE; + + ret = calculate_image_width(ihdr, fmt, &ctx->image_width); + if(ret) return encode_err(ctx, ret); + + if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */ + else ctx->image_size = ctx->image_width * ihdr->height; + + if( !(flags & SPNG_ENCODE_PROGRESSIVE) ) + { + if(img == NULL) return 1; + if(!ctx->image_size) return SPNG_EOVERFLOW; + if(len != ctx->image_size) return SPNG_EBUFSIZ; + } + + ret = spng_encode_chunks(ctx); + if(ret) return encode_err(ctx, ret); + + ret = calculate_subimages(ctx); + if(ret) return encode_err(ctx, ret); + + if(ihdr->bit_depth < 8) ctx->bytes_per_pixel = 1; + else ctx->bytes_per_pixel = num_channels(ihdr) * (ihdr->bit_depth / 8); + + if(spng__optimize(SPNG_FILTER_CHOICE)) + { + /* Filtering would make no difference */ + if(!ctx->image_options.compression_level) + { + encode_flags->filter_choice = SPNG_DISABLE_FILTERING; + } + + /* Palette indices and low bit-depth images do not benefit from filtering */ + if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED || ihdr->bit_depth < 8) + { + encode_flags->filter_choice = SPNG_DISABLE_FILTERING; + } + } + + /* This is technically the same as disabling filtering */ + if(encode_flags->filter_choice == SPNG_FILTER_CHOICE_NONE) + { + encode_flags->filter_choice = SPNG_DISABLE_FILTERING; + } + + if(!encode_flags->filter_choice && spng__optimize(SPNG_IMG_COMPRESSION_STRATEGY)) + { + ctx->image_options.strategy = Z_DEFAULT_STRATEGY; + } + + ret = spng__deflate_init(ctx, &ctx->image_options); + if(ret) return encode_err(ctx, ret); + + size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width; + + scanline_buf_size += 32; + + if(scanline_buf_size < 32) return SPNG_EOVERFLOW; + + ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size); + ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size); + + if(ctx->scanline_buf == NULL || ctx->prev_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM); + + /* Maintain alignment for pixels, filter at [-1] */ + ctx->scanline = ctx->scanline_buf + 16; + ctx->prev_scanline = ctx->prev_scanline_buf + 16; + + if(encode_flags->filter_choice) + { + ctx->filtered_scanline_buf = spng__malloc(ctx, scanline_buf_size); + if(ctx->filtered_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM); + + ctx->filtered_scanline = ctx->filtered_scanline_buf + 16; + } + + struct spng_subimage *sub = ctx->subimage; + struct spng_row_info *ri = &ctx->row_info; + + ctx->fmt = fmt; + + z_stream *zstream = &ctx->zstream; + zstream->avail_out = SPNG_WRITE_SIZE; + + ret = write_header(ctx, type_idat, zstream->avail_out, &zstream->next_out); + if(ret) return encode_err(ctx, ret); + + if(ihdr->interlace_method) encode_flags->interlace = 1; + + if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW) ) encode_flags->same_layout = 1; + + if(ihdr->bit_depth == 16 && fmt != SPNG_FMT_RAW) encode_flags->to_bigendian = 1; + + if(flags & SPNG_ENCODE_FINALIZE) encode_flags->finalize = 1; + + while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++; + + if(encode_flags->interlace) ri->row_num = adam7_y_start[ri->pass]; + + ctx->pixel_size = 4; /* SPNG_FMT_RGBA8 */ + + if(fmt == SPNG_FMT_RGBA16) ctx->pixel_size = 8; + else if(fmt == SPNG_FMT_RGB8) ctx->pixel_size = 3; + else if(fmt == SPNG_FMT_G8) ctx->pixel_size = 1; + else if(fmt == SPNG_FMT_GA8) ctx->pixel_size = 2; + else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) ctx->pixel_size = ctx->bytes_per_pixel; + + ctx->state = SPNG_STATE_ENCODE_INIT; + + if(flags & SPNG_ENCODE_PROGRESSIVE) + { + encode_flags->progressive = 1; + + return 0; + } + + do + { + size_t ioffset = ri->row_num * ctx->image_width; + + ret = encode_row(ctx, (unsigned char*)img + ioffset, ctx->image_width); + + }while(!ret); + + if(ret != SPNG_EOI) return encode_err(ctx, ret); + + return 0; +} + +spng_ctx *spng_ctx_new(int flags) +{ + struct spng_alloc alloc = + { + .malloc_fn = malloc, + .realloc_fn = realloc, + .calloc_fn = calloc, + .free_fn = free + }; + + return spng_ctx_new2(&alloc, flags); +} + +spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags) +{ + if(alloc == NULL) return NULL; + if(flags != (flags & SPNG__CTX_FLAGS_ALL)) return NULL; + + if(alloc->malloc_fn == NULL) return NULL; + if(alloc->realloc_fn == NULL) return NULL; + if(alloc->calloc_fn == NULL) return NULL; + if(alloc->free_fn == NULL) return NULL; + + spng_ctx *ctx = alloc->calloc_fn(1, sizeof(spng_ctx)); + if(ctx == NULL) return NULL; + + ctx->alloc = *alloc; + + ctx->max_width = spng_u32max; + ctx->max_height = spng_u32max; + + ctx->max_chunk_size = spng_u32max; + ctx->chunk_cache_limit = SIZE_MAX; + ctx->chunk_count_limit = SPNG_MAX_CHUNK_COUNT; + + ctx->state = SPNG_STATE_INIT; + + ctx->crc_action_critical = SPNG_CRC_ERROR; + ctx->crc_action_ancillary = SPNG_CRC_DISCARD; + + const struct spng__zlib_options image_defaults = + { + .compression_level = Z_DEFAULT_COMPRESSION, + .window_bits = 15, + .mem_level = 8, + .strategy = Z_FILTERED, + .data_type = 0 /* Z_BINARY */ + }; + + const struct spng__zlib_options text_defaults = + { + .compression_level = Z_DEFAULT_COMPRESSION, + .window_bits = 15, + .mem_level = 8, + .strategy = Z_DEFAULT_STRATEGY, + .data_type = 1 /* Z_TEXT */ + }; + + ctx->image_options = image_defaults; + ctx->text_options = text_defaults; + + ctx->optimize_option = ~0; + ctx->encode_flags.filter_choice = SPNG_FILTER_CHOICE_ALL; + + ctx->flags = flags; + + if(flags & SPNG_CTX_ENCODER) ctx->encode_only = 1; + + return ctx; +} + +void spng_ctx_free(spng_ctx *ctx) +{ + if(ctx == NULL) return; + + if(ctx->streaming && ctx->stream_buf != NULL) spng__free(ctx, ctx->stream_buf); + + if(!ctx->user.exif) spng__free(ctx, ctx->exif.data); + + if(!ctx->user.iccp) spng__free(ctx, ctx->iccp.profile); + + uint32_t i; + + if(ctx->splt_list != NULL && !ctx->user.splt) + { + for(i=0; i < ctx->n_splt; i++) + { + spng__free(ctx, ctx->splt_list[i].entries); + } + spng__free(ctx, ctx->splt_list); + } + + if(ctx->text_list != NULL) + { + for(i=0; i< ctx->n_text; i++) + { + if(ctx->user.text) break; + + spng__free(ctx, ctx->text_list[i].keyword); + if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text); + } + spng__free(ctx, ctx->text_list); + } + + if(ctx->chunk_list != NULL && !ctx->user.unknown) + { + for(i=0; i< ctx->n_chunks; i++) + { + spng__free(ctx, ctx->chunk_list[i].data); + } + spng__free(ctx, ctx->chunk_list); + } + + if(ctx->deflate) deflateEnd(&ctx->zstream); + else inflateEnd(&ctx->zstream); + + if(!ctx->user_owns_out_png) spng__free(ctx, ctx->out_png); + + spng__free(ctx, ctx->gamma_lut16); + + spng__free(ctx, ctx->row_buf); + spng__free(ctx, ctx->scanline_buf); + spng__free(ctx, ctx->prev_scanline_buf); + spng__free(ctx, ctx->filtered_scanline_buf); + + spng_free_fn *free_func = ctx->alloc.free_fn; + + memset(ctx, 0, sizeof(spng_ctx)); + + free_func(ctx); +} + +static int buffer_read_fn(spng_ctx *ctx, void *user, void *data, size_t n) +{ + if(n > ctx->bytes_left) return SPNG_IO_EOF; + + (void)user; + (void)data; + ctx->data = ctx->data + ctx->last_read_size; + + ctx->last_read_size = n; + ctx->bytes_left -= n; + + return 0; +} + +static int file_read_fn(spng_ctx *ctx, void *user, void *data, size_t n) +{ + FILE *file = user; + (void)ctx; + + if(fread(data, n, 1, file) != 1) + { + if(feof(file)) return SPNG_IO_EOF; + else return SPNG_IO_ERROR; + } + + return 0; +} + +static int file_write_fn(spng_ctx *ctx, void *user, void *data, size_t n) +{ + FILE *file = user; + (void)ctx; + + if(fwrite(data, n, 1, file) != 1) return SPNG_IO_ERROR; + + return 0; +} + +int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size) +{ + if(ctx == NULL || buf == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + if(ctx->encode_only) return SPNG_ECTXTYPE; /* not supported */ + + if(ctx->data != NULL) return SPNG_EBUF_SET; + + ctx->data = buf; + ctx->png_base = buf; + ctx->data_size = size; + ctx->bytes_left = size; + + ctx->read_fn = buffer_read_fn; + + ctx->state = SPNG_STATE_INPUT; + + return 0; +} + +int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user) +{ + if(ctx == NULL || rw_func == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + + /* SPNG_STATE_OUTPUT shares the same value */ + if(ctx->state >= SPNG_STATE_INPUT) return SPNG_EBUF_SET; + + if(ctx->encode_only) + { + if(ctx->out_png != NULL) return SPNG_EBUF_SET; + + ctx->write_fn = rw_func; + ctx->write_ptr = ctx->stream_buf; + + ctx->state = SPNG_STATE_OUTPUT; + } + else + { + ctx->stream_buf = spng__malloc(ctx, SPNG_READ_SIZE); + if(ctx->stream_buf == NULL) return SPNG_EMEM; + + ctx->read_fn = rw_func; + ctx->data = ctx->stream_buf; + ctx->data_size = SPNG_READ_SIZE; + + ctx->state = SPNG_STATE_INPUT; + } + + ctx->stream_user_ptr = user; + + ctx->streaming = 1; + + return 0; +} + +int spng_set_png_file(spng_ctx *ctx, FILE *file) +{ + if(file == NULL) return 1; + + if(ctx->encode_only) return spng_set_png_stream(ctx, file_write_fn, file); + + return spng_set_png_stream(ctx, file_read_fn, file); +} + +void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error) +{ + int tmp = 0; + error = error ? error : &tmp; + *error = 0; + + if(ctx == NULL || !len) *error = SPNG_EINVAL; + + if(*error) return NULL; + + if(!ctx->encode_only) *error = SPNG_ECTXTYPE; + else if(!ctx->state) *error = SPNG_EBADSTATE; + else if(!ctx->internal_buffer) *error = SPNG_EOPSTATE; + else if(ctx->state < SPNG_STATE_EOI) *error = SPNG_EOPSTATE; + else if(ctx->state != SPNG_STATE_IEND) *error = SPNG_ENOTFINAL; + + if(*error) return NULL; + + ctx->user_owns_out_png = 1; + + *len = ctx->bytes_encoded; + + return ctx->out_png; +} + +int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height) +{ + if(ctx == NULL) return 1; + + if(width > spng_u32max || height > spng_u32max) return 1; + + ctx->max_width = width; + ctx->max_height = height; + + return 0; +} + +int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height) +{ + if(ctx == NULL || width == NULL || height == NULL) return 1; + + *width = ctx->max_width; + *height = ctx->max_height; + + return 0; +} + +int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_limit) +{ + if(ctx == NULL || chunk_size > spng_u32max || chunk_size > cache_limit) return 1; + + ctx->max_chunk_size = chunk_size; + + ctx->chunk_cache_limit = cache_limit; + + return 0; +} + +int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_limit) +{ + if(ctx == NULL || chunk_size == NULL || cache_limit == NULL) return 1; + + *chunk_size = ctx->max_chunk_size; + + *cache_limit = ctx->chunk_cache_limit; + + return 0; +} + +int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary) +{ + if(ctx == NULL) return 1; + if(ctx->encode_only) return SPNG_ECTXTYPE; + + if(critical > 2 || critical < 0) return 1; + if(ancillary > 2 || ancillary < 0) return 1; + + if(critical == SPNG_CRC_DISCARD) return 1; + + ctx->crc_action_critical = critical; + ctx->crc_action_ancillary = ancillary; + + return 0; +} + +int spng_set_option(spng_ctx *ctx, enum spng_option option, int value) +{ + if(ctx == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + + switch(option) + { + case SPNG_KEEP_UNKNOWN_CHUNKS: + { + ctx->keep_unknown = value ? 1 : 0; + break; + } + case SPNG_IMG_COMPRESSION_LEVEL: + { + ctx->image_options.compression_level = value; + break; + } + case SPNG_IMG_WINDOW_BITS: + { + ctx->image_options.window_bits = value; + break; + } + case SPNG_IMG_MEM_LEVEL: + { + ctx->image_options.mem_level = value; + break; + } + case SPNG_IMG_COMPRESSION_STRATEGY: + { + ctx->image_options.strategy = value; + break; + } + case SPNG_TEXT_COMPRESSION_LEVEL: + { + ctx->text_options.compression_level = value; + break; + } + case SPNG_TEXT_WINDOW_BITS: + { + ctx->text_options.window_bits = value; + break; + } + case SPNG_TEXT_MEM_LEVEL: + { + ctx->text_options.mem_level = value; + break; + } + case SPNG_TEXT_COMPRESSION_STRATEGY: + { + ctx->text_options.strategy = value; + break; + } + case SPNG_FILTER_CHOICE: + { + if(value & ~SPNG_FILTER_CHOICE_ALL) return 1; + ctx->encode_flags.filter_choice = value; + break; + } + case SPNG_CHUNK_COUNT_LIMIT: + { + if(value < 0) return 1; + if(value > (int)ctx->chunk_count_total) return 1; + ctx->chunk_count_limit = value; + break; + } + case SPNG_ENCODE_TO_BUFFER: + { + if(value < 0) return 1; + if(!ctx->encode_only) return SPNG_ECTXTYPE; + if(ctx->state >= SPNG_STATE_OUTPUT) return SPNG_EOPSTATE; + + if(!value) break; + + ctx->internal_buffer = 1; + ctx->state = SPNG_STATE_OUTPUT; + + break; + } + default: return 1; + } + + /* Option can no longer be overriden by the library */ + if(option < 32) ctx->optimize_option &= ~(1 << option); + + return 0; +} + +int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value) +{ + if(ctx == NULL || value == NULL) return 1; + if(!ctx->state) return SPNG_EBADSTATE; + + switch(option) + { + case SPNG_KEEP_UNKNOWN_CHUNKS: + { + *value = ctx->keep_unknown; + break; + } + case SPNG_IMG_COMPRESSION_LEVEL: + { + *value = ctx->image_options.compression_level; + break; + } + case SPNG_IMG_WINDOW_BITS: + { + *value = ctx->image_options.window_bits; + break; + } + case SPNG_IMG_MEM_LEVEL: + { + *value = ctx->image_options.mem_level; + break; + } + case SPNG_IMG_COMPRESSION_STRATEGY: + { + *value = ctx->image_options.strategy; + break; + } + case SPNG_TEXT_COMPRESSION_LEVEL: + { + *value = ctx->text_options.compression_level; + break; + } + case SPNG_TEXT_WINDOW_BITS: + { + *value = ctx->text_options.window_bits; + break; + } + case SPNG_TEXT_MEM_LEVEL: + { + *value = ctx->text_options.mem_level; + break; + } + case SPNG_TEXT_COMPRESSION_STRATEGY: + { + *value = ctx->text_options.strategy; + break; + } + case SPNG_FILTER_CHOICE: + { + *value = ctx->encode_flags.filter_choice; + break; + } + case SPNG_CHUNK_COUNT_LIMIT: + { + *value = ctx->chunk_count_limit; + break; + } + case SPNG_ENCODE_TO_BUFFER: + { + if(ctx->internal_buffer) *value = 1; + else *value = 0; + + break; + } + default: return 1; + } + + return 0; +} + +int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len) +{ + if(ctx == NULL || len == NULL) return 1; + + int ret = read_chunks(ctx, 1); + if(ret) return ret; + + ret = check_decode_fmt(&ctx->ihdr, fmt); + if(ret) return ret; + + return calculate_image_size(&ctx->ihdr, fmt, len); +} + +int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) +{ + if(ctx == NULL) return 1; + int ret = read_chunks(ctx, 1); + if(ret) return ret; + if(ihdr == NULL) return 1; + + *ihdr = ctx->ihdr; + + return 0; +} + +int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte) +{ + SPNG_GET_CHUNK_BOILERPLATE(plte); + + *plte = ctx->plte; + + return 0; +} + +int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns) +{ + SPNG_GET_CHUNK_BOILERPLATE(trns); + + *trns = ctx->trns; + + return 0; +} + +int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm) +{ + SPNG_GET_CHUNK_BOILERPLATE(chrm); + + chrm->white_point_x = (double)ctx->chrm_int.white_point_x / 100000.0; + chrm->white_point_y = (double)ctx->chrm_int.white_point_y / 100000.0; + chrm->red_x = (double)ctx->chrm_int.red_x / 100000.0; + chrm->red_y = (double)ctx->chrm_int.red_y / 100000.0; + chrm->blue_y = (double)ctx->chrm_int.blue_y / 100000.0; + chrm->blue_x = (double)ctx->chrm_int.blue_x / 100000.0; + chrm->green_x = (double)ctx->chrm_int.green_x / 100000.0; + chrm->green_y = (double)ctx->chrm_int.green_y / 100000.0; + + return 0; +} + +int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm) +{ + SPNG_GET_CHUNK_BOILERPLATE(chrm); + + *chrm = ctx->chrm_int; + + return 0; +} + +int spng_get_gama(spng_ctx *ctx, double *gamma) +{ + double *gama = gamma; + SPNG_GET_CHUNK_BOILERPLATE(gama); + + *gama = (double)ctx->gama / 100000.0; + + return 0; +} + +int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int) +{ + uint32_t *gama = gama_int; + SPNG_GET_CHUNK_BOILERPLATE(gama); + + *gama_int = ctx->gama; + + return 0; +} + +int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp) +{ + SPNG_GET_CHUNK_BOILERPLATE(iccp); + + *iccp = ctx->iccp; + + return 0; +} + +int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit) +{ + SPNG_GET_CHUNK_BOILERPLATE(sbit); + + *sbit = ctx->sbit; + + return 0; +} + +int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent) +{ + uint8_t *srgb = rendering_intent; + SPNG_GET_CHUNK_BOILERPLATE(srgb); + + *srgb = ctx->srgb_rendering_intent; + + return 0; +} + +int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text) +{ + if(ctx == NULL) return 1; + int ret = read_chunks(ctx, 0); + if(ret) return ret; + if(!ctx->stored.text) return SPNG_ECHUNKAVAIL; + if(n_text == NULL) return 1; + + if(text == NULL) + { + *n_text = ctx->n_text; + return 0; + } + + if(*n_text < ctx->n_text) return 1; + + uint32_t i; + for(i=0; i< ctx->n_text; i++) + { + text[i].type = ctx->text_list[i].type; + memcpy(&text[i].keyword, ctx->text_list[i].keyword, strlen(ctx->text_list[i].keyword) + 1); + text[i].compression_method = 0; + text[i].compression_flag = ctx->text_list[i].compression_flag; + text[i].language_tag = ctx->text_list[i].language_tag; + text[i].translated_keyword = ctx->text_list[i].translated_keyword; + text[i].length = ctx->text_list[i].text_length; + text[i].text = ctx->text_list[i].text; + } + + return ret; +} + +int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) +{ + SPNG_GET_CHUNK_BOILERPLATE(bkgd); + + *bkgd = ctx->bkgd; + + return 0; +} + +int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist) +{ + SPNG_GET_CHUNK_BOILERPLATE(hist); + + *hist = ctx->hist; + + return 0; +} + +int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys) +{ + SPNG_GET_CHUNK_BOILERPLATE(phys); + + *phys = ctx->phys; + + return 0; +} + +int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt) +{ + if(ctx == NULL) return 1; + int ret = read_chunks(ctx, 0); + if(ret) return ret; + if(!ctx->stored.splt) return SPNG_ECHUNKAVAIL; + if(n_splt == NULL) return 1; + + if(splt == NULL) + { + *n_splt = ctx->n_splt; + return 0; + } + + if(*n_splt < ctx->n_splt) return 1; + + memcpy(splt, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt)); + + return 0; +} + +int spng_get_time(spng_ctx *ctx, struct spng_time *time) +{ + SPNG_GET_CHUNK_BOILERPLATE(time); + + *time = ctx->time; + + return 0; +} + +int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks) +{ + if(ctx == NULL) return 1; + int ret = read_chunks(ctx, 0); + if(ret) return ret; + if(!ctx->stored.unknown) return SPNG_ECHUNKAVAIL; + if(n_chunks == NULL) return 1; + + if(chunks == NULL) + { + *n_chunks = ctx->n_chunks; + return 0; + } + + if(*n_chunks < ctx->n_chunks) return 1; + + memcpy(chunks, ctx->chunk_list, sizeof(struct spng_unknown_chunk)); + + return 0; +} + +int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs) +{ + SPNG_GET_CHUNK_BOILERPLATE(offs); + + *offs = ctx->offs; + + return 0; +} + +int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif) +{ + SPNG_GET_CHUNK_BOILERPLATE(exif); + + *exif = ctx->exif; + + return 0; +} + +int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr) +{ + SPNG_SET_CHUNK_BOILERPLATE(ihdr); + + if(ctx->stored.ihdr) return 1; + + ret = check_ihdr(ihdr, ctx->max_width, ctx->max_height); + if(ret) return ret; + + ctx->ihdr = *ihdr; + + ctx->stored.ihdr = 1; + ctx->user.ihdr = 1; + + return 0; +} + +int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte) +{ + SPNG_SET_CHUNK_BOILERPLATE(plte); + + if(!ctx->stored.ihdr) return 1; + + if(check_plte(plte, &ctx->ihdr)) return 1; + + ctx->plte.n_entries = plte->n_entries; + + memcpy(ctx->plte.entries, plte->entries, plte->n_entries * sizeof(struct spng_plte_entry)); + + ctx->stored.plte = 1; + ctx->user.plte = 1; + + return 0; +} + +int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns) +{ + SPNG_SET_CHUNK_BOILERPLATE(trns); + + if(!ctx->stored.ihdr) return SPNG_ENOIHDR; + + if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE) + { + ctx->trns.gray = trns->gray; + } + else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR) + { + ctx->trns.red = trns->red; + ctx->trns.green = trns->green; + ctx->trns.blue = trns->blue; + } + else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED) + { + if(!ctx->stored.plte) return SPNG_ETRNS_NO_PLTE; + if(trns->n_type3_entries > ctx->plte.n_entries) return 1; + + ctx->trns.n_type3_entries = trns->n_type3_entries; + memcpy(ctx->trns.type3_alpha, trns->type3_alpha, trns->n_type3_entries); + } + else return SPNG_ETRNS_COLOR_TYPE; + + ctx->stored.trns = 1; + ctx->user.trns = 1; + + return 0; +} + +int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm) +{ + SPNG_SET_CHUNK_BOILERPLATE(chrm); + + struct spng_chrm_int chrm_int; + + chrm_int.white_point_x = (uint32_t)(chrm->white_point_x * 100000.0); + chrm_int.white_point_y = (uint32_t)(chrm->white_point_y * 100000.0); + chrm_int.red_x = (uint32_t)(chrm->red_x * 100000.0); + chrm_int.red_y = (uint32_t)(chrm->red_y * 100000.0); + chrm_int.green_x = (uint32_t)(chrm->green_x * 100000.0); + chrm_int.green_y = (uint32_t)(chrm->green_y * 100000.0); + chrm_int.blue_x = (uint32_t)(chrm->blue_x * 100000.0); + chrm_int.blue_y = (uint32_t)(chrm->blue_y * 100000.0); + + if(check_chrm_int(&chrm_int)) return SPNG_ECHRM; + + ctx->chrm_int = chrm_int; + + ctx->stored.chrm = 1; + ctx->user.chrm = 1; + + return 0; +} + +int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int) +{ + SPNG_SET_CHUNK_BOILERPLATE(chrm_int); + + if(check_chrm_int(chrm_int)) return SPNG_ECHRM; + + ctx->chrm_int = *chrm_int; + + ctx->stored.chrm = 1; + ctx->user.chrm = 1; + + return 0; +} + +int spng_set_gama(spng_ctx *ctx, double gamma) +{ + SPNG_SET_CHUNK_BOILERPLATE(ctx); + + uint32_t gama = gamma * 100000.0; + + if(!gama) return 1; + if(gama > spng_u32max) return 1; + + ctx->gama = gama; + + ctx->stored.gama = 1; + ctx->user.gama = 1; + + return 0; +} + +int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma) +{ + SPNG_SET_CHUNK_BOILERPLATE(ctx); + + if(!gamma) return 1; + if(gamma > spng_u32max) return 1; + + ctx->gama = gamma; + + ctx->stored.gama = 1; + ctx->user.gama = 1; + + return 0; +} + +int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp) +{ + SPNG_SET_CHUNK_BOILERPLATE(iccp); + + if(check_png_keyword(iccp->profile_name)) return SPNG_EICCP_NAME; + if(!iccp->profile_len || iccp->profile_len > UINT_MAX) return 1; + + if(ctx->iccp.profile && !ctx->user.iccp) spng__free(ctx, ctx->iccp.profile); + + ctx->iccp = *iccp; + + ctx->stored.iccp = 1; + ctx->user.iccp = 1; + + return 0; +} + +int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit) +{ + SPNG_SET_CHUNK_BOILERPLATE(sbit); + + if(check_sbit(sbit, &ctx->ihdr)) return 1; + + if(!ctx->stored.ihdr) return 1; + + ctx->sbit = *sbit; + + ctx->stored.sbit = 1; + ctx->user.sbit = 1; + + return 0; +} + +int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent) +{ + SPNG_SET_CHUNK_BOILERPLATE(ctx); + + if(rendering_intent > 3) return 1; + + ctx->srgb_rendering_intent = rendering_intent; + + ctx->stored.srgb = 1; + ctx->user.srgb = 1; + + return 0; +} + +int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text) +{ + if(!n_text) return 1; + SPNG_SET_CHUNK_BOILERPLATE(text); + + uint32_t i; + for(i=0; i < n_text; i++) + { + if(check_png_keyword(text[i].keyword)) return SPNG_ETEXT_KEYWORD; + if(!text[i].length) return 1; + if(text[i].length > UINT_MAX) return 1; + if(text[i].text == NULL) return 1; + + if(text[i].type == SPNG_TEXT) + { + if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1; + } + else if(text[i].type == SPNG_ZTXT) + { + if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1; + + if(text[i].compression_method != 0) return SPNG_EZTXT_COMPRESSION_METHOD; + } + else if(text[i].type == SPNG_ITXT) + { + if(text[i].compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG; + if(text[i].compression_method != 0) return SPNG_EITXT_COMPRESSION_METHOD; + if(text[i].language_tag == NULL) return SPNG_EITXT_LANG_TAG; + if(text[i].translated_keyword == NULL) return SPNG_EITXT_TRANSLATED_KEY; + } + else return 1; + + } + + struct spng_text2 *text_list = spng__calloc(ctx, sizeof(struct spng_text2), n_text); + + if(!text_list) return SPNG_EMEM; + + if(ctx->text_list != NULL) + { + for(i=0; i < ctx->n_text; i++) + { + if(ctx->user.text) break; + + spng__free(ctx, ctx->text_list[i].keyword); + if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text); + } + spng__free(ctx, ctx->text_list); + } + + for(i=0; i < n_text; i++) + { + text_list[i].type = text[i].type; + /* Prevent issues with spng_text.keyword[80] going out of scope */ + text_list[i].keyword = text_list[i].user_keyword_storage; + memcpy(text_list[i].user_keyword_storage, text[i].keyword, strlen(text[i].keyword)); + text_list[i].text = text[i].text; + text_list[i].text_length = text[i].length; + + if(text[i].type == SPNG_ZTXT) + { + text_list[i].compression_flag = 1; + } + else if(text[i].type == SPNG_ITXT) + { + text_list[i].compression_flag = text[i].compression_flag; + text_list[i].language_tag = text[i].language_tag; + text_list[i].translated_keyword = text[i].translated_keyword; + } + } + + ctx->text_list = text_list; + ctx->n_text = n_text; + + ctx->stored.text = 1; + ctx->user.text = 1; + + return 0; +} + +int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd) +{ + SPNG_SET_CHUNK_BOILERPLATE(bkgd); + + if(!ctx->stored.ihdr) return 1; + + if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4) + { + ctx->bkgd.gray = bkgd->gray; + } + else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6) + { + ctx->bkgd.red = bkgd->red; + ctx->bkgd.green = bkgd->green; + ctx->bkgd.blue = bkgd->blue; + } + else if(ctx->ihdr.color_type == 3) + { + if(!ctx->stored.plte) return SPNG_EBKGD_NO_PLTE; + if(bkgd->plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX; + + ctx->bkgd.plte_index = bkgd->plte_index; + } + + ctx->stored.bkgd = 1; + ctx->user.bkgd = 1; + + return 0; +} + +int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist) +{ + SPNG_SET_CHUNK_BOILERPLATE(hist); + + if(!ctx->stored.plte) return SPNG_EHIST_NO_PLTE; + + ctx->hist = *hist; + + ctx->stored.hist = 1; + ctx->user.hist = 1; + + return 0; +} + +int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys) +{ + SPNG_SET_CHUNK_BOILERPLATE(phys); + + if(check_phys(phys)) return SPNG_EPHYS; + + ctx->phys = *phys; + + ctx->stored.phys = 1; + ctx->user.phys = 1; + + return 0; +} + +int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt) +{ + if(!n_splt) return 1; + SPNG_SET_CHUNK_BOILERPLATE(splt); + + uint32_t i; + for(i=0; i < n_splt; i++) + { + if(check_png_keyword(splt[i].name)) return SPNG_ESPLT_NAME; + if( !(splt[i].sample_depth == 8 || splt[i].sample_depth == 16) ) return SPNG_ESPLT_DEPTH; + } + + if(ctx->stored.splt && !ctx->user.splt) + { + for(i=0; i < ctx->n_splt; i++) + { + if(ctx->splt_list[i].entries != NULL) spng__free(ctx, ctx->splt_list[i].entries); + } + spng__free(ctx, ctx->splt_list); + } + + ctx->splt_list = splt; + ctx->n_splt = n_splt; + + ctx->stored.splt = 1; + ctx->user.splt = 1; + + return 0; +} + +int spng_set_time(spng_ctx *ctx, struct spng_time *time) +{ + SPNG_SET_CHUNK_BOILERPLATE(time); + + if(check_time(time)) return SPNG_ETIME; + + ctx->time = *time; + + ctx->stored.time = 1; + ctx->user.time = 1; + + return 0; +} + +int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks) +{ + if(!n_chunks) return 1; + SPNG_SET_CHUNK_BOILERPLATE(chunks); + + uint32_t i; + for(i=0; i < n_chunks; i++) + { + if(chunks[i].length > spng_u32max) return SPNG_ECHUNK_STDLEN; + if(chunks[i].length && chunks[i].data == NULL) return 1; + + switch(chunks[i].location) + { + case SPNG_AFTER_IHDR: + case SPNG_AFTER_PLTE: + case SPNG_AFTER_IDAT: + break; + default: return SPNG_ECHUNK_POS; + } + } + + if(ctx->stored.unknown && !ctx->user.unknown) + { + for(i=0; i < ctx->n_chunks; i++) + { + spng__free(ctx, ctx->chunk_list[i].data); + } + spng__free(ctx, ctx->chunk_list); + } + + ctx->chunk_list = chunks; + ctx->n_chunks = n_chunks; + + ctx->stored.unknown = 1; + ctx->user.unknown = 1; + + return 0; +} + +int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs) +{ + SPNG_SET_CHUNK_BOILERPLATE(offs); + + if(check_offs(offs)) return SPNG_EOFFS; + + ctx->offs = *offs; + + ctx->stored.offs = 1; + ctx->user.offs = 1; + + return 0; +} + +int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif) +{ + SPNG_SET_CHUNK_BOILERPLATE(exif); + + if(check_exif(exif)) return SPNG_EEXIF; + + if(ctx->exif.data != NULL && !ctx->user.exif) spng__free(ctx, ctx->exif.data); + + ctx->exif = *exif; + + ctx->stored.exif = 1; + ctx->user.exif = 1; + + return 0; +} + +const char *spng_strerror(int err) +{ + switch(err) + { + case SPNG_IO_EOF: return "end of stream"; + case SPNG_IO_ERROR: return "stream error"; + case SPNG_OK: return "success"; + case SPNG_EINVAL: return "invalid argument"; + case SPNG_EMEM: return "out of memory"; + case SPNG_EOVERFLOW: return "arithmetic overflow"; + case SPNG_ESIGNATURE: return "invalid signature"; + case SPNG_EWIDTH: return "invalid image width"; + case SPNG_EHEIGHT: return "invalid image height"; + case SPNG_EUSER_WIDTH: return "image width exceeds user limit"; + case SPNG_EUSER_HEIGHT: return "image height exceeds user limit"; + case SPNG_EBIT_DEPTH: return "invalid bit depth"; + case SPNG_ECOLOR_TYPE: return "invalid color type"; + case SPNG_ECOMPRESSION_METHOD: return "invalid compression method"; + case SPNG_EFILTER_METHOD: return "invalid filter method"; + case SPNG_EINTERLACE_METHOD: return "invalid interlace method"; + case SPNG_EIHDR_SIZE: return "invalid IHDR chunk size"; + case SPNG_ENOIHDR: return "missing IHDR chunk"; + case SPNG_ECHUNK_POS: return "invalid chunk position"; + case SPNG_ECHUNK_SIZE: return "invalid chunk length"; + case SPNG_ECHUNK_CRC: return "invalid chunk checksum"; + case SPNG_ECHUNK_TYPE: return "invalid chunk type"; + case SPNG_ECHUNK_UNKNOWN_CRITICAL: return "unknown critical chunk"; + case SPNG_EDUP_PLTE: return "duplicate PLTE chunk"; + case SPNG_EDUP_CHRM: return "duplicate cHRM chunk"; + case SPNG_EDUP_GAMA: return "duplicate gAMA chunk"; + case SPNG_EDUP_ICCP: return "duplicate iCCP chunk"; + case SPNG_EDUP_SBIT: return "duplicate sBIT chunk"; + case SPNG_EDUP_SRGB: return "duplicate sRGB chunk"; + case SPNG_EDUP_BKGD: return "duplicate bKGD chunk"; + case SPNG_EDUP_HIST: return "duplicate hIST chunk"; + case SPNG_EDUP_TRNS: return "duplicate tRNS chunk"; + case SPNG_EDUP_PHYS: return "duplicate pHYs chunk"; + case SPNG_EDUP_TIME: return "duplicate tIME chunk"; + case SPNG_EDUP_OFFS: return "duplicate oFFs chunk"; + case SPNG_EDUP_EXIF: return "duplicate eXIf chunk"; + case SPNG_ECHRM: return "invalid cHRM chunk"; + case SPNG_EPLTE_IDX: return "invalid palette (PLTE) index"; + case SPNG_ETRNS_COLOR_TYPE: return "tRNS chunk with incompatible color type"; + case SPNG_ETRNS_NO_PLTE: return "missing palette (PLTE) for tRNS chunk"; + case SPNG_EGAMA: return "invalid gAMA chunk"; + case SPNG_EICCP_NAME: return "invalid iCCP profile name"; + case SPNG_EICCP_COMPRESSION_METHOD: return "invalid iCCP compression method"; + case SPNG_ESBIT: return "invalid sBIT chunk"; + case SPNG_ESRGB: return "invalid sRGB chunk"; + case SPNG_ETEXT: return "invalid tEXt chunk"; + case SPNG_ETEXT_KEYWORD: return "invalid tEXt keyword"; + case SPNG_EZTXT: return "invalid zTXt chunk"; + case SPNG_EZTXT_COMPRESSION_METHOD: return "invalid zTXt compression method"; + case SPNG_EITXT: return "invalid iTXt chunk"; + case SPNG_EITXT_COMPRESSION_FLAG: return "invalid iTXt compression flag"; + case SPNG_EITXT_COMPRESSION_METHOD: return "invalid iTXt compression method"; + case SPNG_EITXT_LANG_TAG: return "invalid iTXt language tag"; + case SPNG_EITXT_TRANSLATED_KEY: return "invalid iTXt translated key"; + case SPNG_EBKGD_NO_PLTE: return "missing palette for bKGD chunk"; + case SPNG_EBKGD_PLTE_IDX: return "invalid palette index for bKGD chunk"; + case SPNG_EHIST_NO_PLTE: return "missing palette for hIST chunk"; + case SPNG_EPHYS: return "invalid pHYs chunk"; + case SPNG_ESPLT_NAME: return "invalid suggested palette name"; + case SPNG_ESPLT_DUP_NAME: return "duplicate suggested palette (sPLT) name"; + case SPNG_ESPLT_DEPTH: return "invalid suggested palette (sPLT) sample depth"; + case SPNG_ETIME: return "invalid tIME chunk"; + case SPNG_EOFFS: return "invalid oFFs chunk"; + case SPNG_EEXIF: return "invalid eXIf chunk"; + case SPNG_EIDAT_TOO_SHORT: return "IDAT stream too short"; + case SPNG_EIDAT_STREAM: return "IDAT stream error"; + case SPNG_EZLIB: return "zlib error"; + case SPNG_EFILTER: return "invalid scanline filter"; + case SPNG_EBUFSIZ: return "invalid buffer size"; + case SPNG_EIO: return "i/o error"; + case SPNG_EOF: return "end of file"; + case SPNG_EBUF_SET: return "buffer already set"; + case SPNG_EBADSTATE: return "non-recoverable state"; + case SPNG_EFMT: return "invalid format"; + case SPNG_EFLAGS: return "invalid flags"; + case SPNG_ECHUNKAVAIL: return "chunk not available"; + case SPNG_ENCODE_ONLY: return "encode only context"; + case SPNG_EOI: return "reached end-of-image state"; + case SPNG_ENOPLTE: return "missing PLTE for indexed image"; + case SPNG_ECHUNK_LIMITS: return "reached chunk/cache limits"; + case SPNG_EZLIB_INIT: return "zlib init error"; + case SPNG_ECHUNK_STDLEN: return "chunk exceeds maximum standard length"; + case SPNG_EINTERNAL: return "internal error"; + case SPNG_ECTXTYPE: return "invalid operation for context type"; + case SPNG_ENOSRC: return "source PNG not set"; + case SPNG_ENODST: return "PNG output not set"; + case SPNG_EOPSTATE: return "invalid operation for state"; + case SPNG_ENOTFINAL: return "PNG not finalized"; + default: return "unknown error"; + } +} + +const char *spng_version_string(void) +{ + return SPNG_VERSION_STRING; +} + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +/* The following SIMD optimizations are derived from libpng source code. */ + +/* +* PNG Reference Library License version 2 +* +* Copyright (c) 1995-2019 The PNG Reference Library Authors. +* Copyright (c) 2018-2019 Cosmin Truta. +* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. +* Copyright (c) 1996-1997 Andreas Dilger. +* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. +* +* The software is supplied "as is", without warranty of any kind, +* express or implied, including, without limitation, the warranties +* of merchantability, fitness for a particular purpose, title, and +* non-infringement. In no event shall the Copyright owners, or +* anyone distributing the software, be liable for any damages or +* other liability, whether in contract, tort or otherwise, arising +* from, out of, or in connection with the software, or the use or +* other dealings in the software, even if advised of the possibility +* of such damage. +* +* Permission is hereby granted to use, copy, modify, and distribute +* this software, or portions hereof, for any purpose, without fee, +* subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you +* must not claim that you wrote the original software. If you +* use this software in a product, an acknowledgment in the product +* documentation would be appreciated, but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must +* not be misrepresented as being the original software. +* +* 3. This Copyright notice may not be removed or altered from any +* source or altered source distribution. +*/ + +#if defined(SPNG_X86) + +#ifndef SPNG_SSE + #define SPNG_SSE 1 +#endif + +#if defined(__GNUC__) && !defined(__clang__) + #if SPNG_SSE == 3 + #pragma GCC target("ssse3") + #elif SPNG_SSE == 4 + #pragma GCC target("sse4.1") + #else + #pragma GCC target("sse2") + #endif +#endif + +/* SSE2 optimised filter functions + * Derived from filter_neon_intrinsics.c + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2016-2017 Glenn Randers-Pehrson + * Written by Mike Klein and Matt Sarett + * Derived from arm/filter_neon_intrinsics.c + * + * This code is derived from libpng source code. + * For conditions of distribution and use, see the disclaimer + * and license above. + */ + +#include +#include +#include + +/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). + * They're positioned like this: + * prev: c b + * row: a d + * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be + * whichever of a, b, or c is closest to p=a+b-c. + */ + +static __m128i load4(const void* p) +{ + int tmp; + memcpy(&tmp, p, sizeof(tmp)); + return _mm_cvtsi32_si128(tmp); +} + +static void store4(void* p, __m128i v) +{ + int tmp = _mm_cvtsi128_si32(v); + memcpy(p, &tmp, sizeof(int)); +} + +static __m128i load3(const void* p) +{ + uint32_t tmp = 0; + memcpy(&tmp, p, 3); + return _mm_cvtsi32_si128(tmp); +} + +static void store3(void* p, __m128i v) +{ + int tmp = _mm_cvtsi128_si32(v); + memcpy(p, &tmp, 3); +} + +static void defilter_sub3(size_t rowbytes, unsigned char *row) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + size_t rb = rowbytes; + + __m128i a, d = _mm_setzero_si128(); + + while(rb >= 4) + { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store3(row, d); + + row += 3; + rb -= 3; + } + + if(rb > 0) + { + a = d; d = load3(row); + d = _mm_add_epi8(d, a); + store3(row, d); + } +} + +static void defilter_sub4(size_t rowbytes, unsigned char *row) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + size_t rb = rowbytes+4; + + __m128i a, d = _mm_setzero_si128(); + + while(rb > 4) + { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store4(row, d); + + row += 4; + rb -= 4; + } +} + +static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + + size_t rb = rowbytes; + + const __m128i zero = _mm_setzero_si128(); + + __m128i b; + __m128i a, d = zero; + + while(rb >= 4) + { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), + _mm_set1_epi8(1))); + d = _mm_add_epi8(d, avg); + store3(row, d); + + prev += 3; + row += 3; + rb -= 3; + } + + if(rb > 0) + { + __m128i avg; + b = load3(prev); + a = d; d = load3(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a, b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store3(row, d); + } +} + +static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + size_t rb = rowbytes+4; + + const __m128i zero = _mm_setzero_si128(); + __m128i b; + __m128i a, d = zero; + + while(rb > 4) + { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store4(row, d); + + prev += 4; + row += 4; + rb -= 4; + } +} + +/* Returns |x| for 16-bit lanes. */ +#if (SPNG_SSE >= 3) && !defined(_MSC_VER) +__attribute__((target("ssse3"))) +#endif +static __m128i abs_i16(__m128i x) +{ +#if SPNG_SSE >= 3 + return _mm_abs_epi16(x); +#else + /* Read this all as, return x<0 ? -x : x. + * To negate two's complement, you flip all the bits then add 1. + */ + __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128()); + + /* Flip negative lanes. */ + x = _mm_xor_si128(x, is_negative); + + /* +1 to negative lanes, else +0. */ + x = _mm_sub_epi16(x, is_negative); + return x; +#endif +} + +/* Bytewise c ? t : e. */ +static __m128i if_then_else(__m128i c, __m128i t, __m128i e) +{ +#if SPNG_SSE >= 4 + return _mm_blendv_epi8(e, t, c); +#else + return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e)); +#endif +} + +static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + size_t rb = rowbytes; + const __m128i zero = _mm_setzero_si128(); + __m128i c, b = zero, + a, d = zero; + + while(rb >= 4) + { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa,pb,pc,smallest,nearest; + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + + pa = _mm_sub_epi16(b, c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a, c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa, pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d, d)); + + prev += 3; + row += 3; + rb -= 3; + } + + if(rb > 0) + { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa, pb, pc, smallest, nearest; + c = b; b = _mm_unpacklo_epi8(load3(prev), zero); + a = d; d = _mm_unpacklo_epi8(load3(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b, c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a, c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa, pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d, d)); + } +} + +static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + size_t rb = rowbytes+4; + + const __m128i zero = _mm_setzero_si128(); + __m128i pa, pb, pc, smallest, nearest; + __m128i c, b = zero, + a, d = zero; + + while(rb > 4) + { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b, c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a, c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa, pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store4(row, _mm_packus_epi16(d, d)); + + prev += 4; + row += 4; + rb -= 4; + } +} + +#endif /* SPNG_X86 */ + + +#if defined(SPNG_ARM) + +/* NEON optimised filter functions + * Derived from filter_neon_intrinsics.c + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2014,2016 Glenn Randers-Pehrson + * Written by James Yu , October 2013. + * Based on filter_neon.S, written by Mans Rullgard, 2011. + * + * This code is derived from libpng source code. + * For conditions of distribution and use, see the disclaimer + * and license in this file. + */ + +#define png_aligncast(type, value) ((void*)(value)) +#define png_aligncastconst(type, value) ((const void*)(value)) + +/* libpng row pointers are not necessarily aligned to any particular boundary, + * however this code will only work with appropriate alignment. mips/mips_init.c + * checks for this (and will not compile unless it is done). This code uses + * variants of png_aligncast to avoid compiler warnings. + */ +#define png_ptr(type,pointer) png_aligncast(type *,pointer) +#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer) + +/* The following relies on a variable 'temp_pointer' being declared with type + * 'type'. This is written this way just to hide the GCC strict aliasing + * warning; note that the code is safe because there never is an alias between + * the input and output pointers. + */ +#define png_ldr(type,pointer)\ + (temp_pointer = png_ptr(type,pointer), *temp_pointer) + + +#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_ARM64) + #include +#else + #include +#endif + +static void defilter_sub3(size_t rowbytes, unsigned char *row) +{ + unsigned char *rp = row; + unsigned char *rp_stop = row + rowbytes; + + uint8x16_t vtmp = vld1q_u8(rp); + uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp); + uint8x8x2_t vrp = *vrpt; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + for (; rp < rp_stop;) + { + uint8x8_t vtmp1, vtmp2; + uint32x2_t *temp_pointer; + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); + vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6); + vdest.val[1] = vadd_u8(vdest.val[0], vtmp1); + + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + vdest.val[2] = vadd_u8(vdest.val[1], vtmp2); + vdest.val[3] = vadd_u8(vdest.val[2], vtmp1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t, &vtmp); + vrp = *vrpt; + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } +} + +static void defilter_sub4(size_t rowbytes, unsigned char *row) +{ + unsigned char *rp = row; + unsigned char *rp_stop = row + rowbytes; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + for (; rp < rp_stop; rp += 16) + { + uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp)); + uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp); + uint8x8x4_t vrp = *vrpt; + uint32x2x4_t *temp_pointer; + uint32x2x4_t vdest_val; + + vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]); + vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]); + vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]); + + vdest_val = png_ldr(uint32x2x4_t, &vdest); + vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); + } +} + +static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) +{ + unsigned char *rp = row; + const unsigned char *pp = prev_row; + unsigned char *rp_stop = row + rowbytes; + + uint8x16_t vtmp; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + vtmp = vld1q_u8(rp); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + for (; rp < rp_stop; pp += 12) + { + uint8x8_t vtmp1, vtmp2, vtmp3; + + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + + uint32x2_t *temp_pointer; + + vtmp = vld1q_u8(pp); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); + vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6); + vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2); + vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); + + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6); + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2); + vdest.val[2] = vadd_u8(vdest.val[2], vtmp3); + + vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); + + vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2); + vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } +} + +static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) +{ + unsigned char *rp = row; + unsigned char *rp_stop = row + rowbytes; + const unsigned char *pp = prev_row; + + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + for (; rp < rp_stop; rp += 16, pp += 16) + { + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + uint32x2x4_t vdest_val; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]); + vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); + vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]); + vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); + vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]); + vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); + + vdest_val = png_ldr(uint32x2x4_t, &vdest); + vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); + } +} + +static uint8x8_t paeth_arm(uint8x8_t a, uint8x8_t b, uint8x8_t c) +{ + uint8x8_t d, e; + uint16x8_t p1, pa, pb, pc; + + p1 = vaddl_u8(a, b); /* a + b */ + pc = vaddl_u8(c, c); /* c * 2 */ + pa = vabdl_u8(b, c); /* pa */ + pb = vabdl_u8(a, c); /* pb */ + pc = vabdq_u16(p1, pc); /* pc */ + + p1 = vcleq_u16(pa, pb); /* pa <= pb */ + pa = vcleq_u16(pa, pc); /* pa <= pc */ + pb = vcleq_u16(pb, pc); /* pb <= pc */ + + p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */ + + d = vmovn_u16(pb); + e = vmovn_u16(p1); + + d = vbsl_u8(d, b, c); + e = vbsl_u8(e, a, d); + + return e; +} + +static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) +{ + unsigned char *rp = row; + const unsigned char *pp = prev_row; + unsigned char *rp_stop = row + rowbytes; + + uint8x16_t vtmp; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; + uint8x8_t vlast = vdup_n_u8(0); + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + vtmp = vld1q_u8(rp); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + for (; rp < rp_stop; pp += 12) + { + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + uint8x8_t vtmp1, vtmp2, vtmp3; + uint32x2_t *temp_pointer; + + vtmp = vld1q_u8(pp); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); + vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3); + vdest.val[1] = paeth_arm(vdest.val[0], vtmp2, vpp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[1], vtmp1); + + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6); + vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6); + vdest.val[2] = paeth_arm(vdest.val[1], vtmp3, vtmp2); + vdest.val[2] = vadd_u8(vdest.val[2], vtmp1); + + vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); + vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); + + vtmp = vld1q_u8(rp + 12); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; + + vdest.val[3] = paeth_arm(vdest.val[2], vtmp2, vtmp3); + vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); + + vlast = vtmp2; + + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); + rp += 3; + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); + rp += 3; + } +} + +static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row) +{ + unsigned char *rp = row; + unsigned char *rp_stop = row + rowbytes; + const unsigned char *pp = prev_row; + + uint8x8_t vlast = vdup_n_u8(0); + uint8x8x4_t vdest; + vdest.val[3] = vdup_n_u8(0); + + for (; rp < rp_stop; rp += 16, pp += 16) + { + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + uint32x2x4_t vdest_val; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; + + vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast); + vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); + vdest.val[1] = paeth_arm(vdest.val[0], vpp.val[1], vpp.val[0]); + vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]); + vdest.val[2] = paeth_arm(vdest.val[1], vpp.val[2], vpp.val[1]); + vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]); + vdest.val[3] = paeth_arm(vdest.val[2], vpp.val[3], vpp.val[2]); + vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); + + vlast = vpp.val[3]; + + vdest_val = png_ldr(uint32x2x4_t, &vdest); + vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0); + } +} + +/* NEON optimised palette expansion functions + * Derived from palette_neon_intrinsics.c + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 2017-2018 Arm Holdings. All rights reserved. + * Written by Richard Townsend , February 2017. + * + * This code is derived from libpng source code. + * For conditions of distribution and use, see the disclaimer + * and license in this file. + * + * Related: https://developer.arm.com/documentation/101964/latest/Color-palette-expansion + * + * The functions were refactored to iterate forward. + * + */ + +/* Expands a palettized row into RGBA8. */ +static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width) +{ + const uint32_t scanline_stride = 4; + const uint32_t row_stride = scanline_stride * 4; + const uint32_t count = width / scanline_stride; + const uint32_t *palette = (const uint32_t*)plte; + + if(!count) return 0; + + uint32_t i; + uint32x4_t cur; + for(i=0; i < count; i++, scanline += scanline_stride) + { + cur = vld1q_dup_u32 (palette + scanline[0]); + cur = vld1q_lane_u32(palette + scanline[1], cur, 1); + cur = vld1q_lane_u32(palette + scanline[2], cur, 2); + cur = vld1q_lane_u32(palette + scanline[3], cur, 3); + vst1q_u32((uint32_t*)(row + i * row_stride), cur); + } + + return count * scanline_stride; +} + +/* Expands a palettized row into RGB8. */ +static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width) +{ + const uint32_t scanline_stride = 8; + const uint32_t row_stride = scanline_stride * 3; + const uint32_t count = width / scanline_stride; + + if(!count) return 0; + + uint32_t i; + uint8x8x3_t cur; + for(i=0; i < count; i++, scanline += scanline_stride) + { + cur = vld3_dup_u8 (plte + 3 * scanline[0]); + cur = vld3_lane_u8(plte + 3 * scanline[1], cur, 1); + cur = vld3_lane_u8(plte + 3 * scanline[2], cur, 2); + cur = vld3_lane_u8(plte + 3 * scanline[3], cur, 3); + cur = vld3_lane_u8(plte + 3 * scanline[4], cur, 4); + cur = vld3_lane_u8(plte + 3 * scanline[5], cur, 5); + cur = vld3_lane_u8(plte + 3 * scanline[6], cur, 6); + cur = vld3_lane_u8(plte + 3 * scanline[7], cur, 7); + vst3_u8(row + i * row_stride, cur); + } + + return count * scanline_stride; +} + +#endif /* SPNG_ARM */ diff --git a/3rdparty/libspng/spng.h b/3rdparty/libspng/spng.h new file mode 100644 index 0000000000..5937d6c15d --- /dev/null +++ b/3rdparty/libspng/spng.h @@ -0,0 +1,537 @@ +/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */ +#ifndef SPNG_H +#define SPNG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC) + #if defined(SPNG__BUILD) + #define SPNG_API __declspec(dllexport) + #else + #define SPNG_API __declspec(dllimport) + #endif +#else + #define SPNG_API +#endif + +#if defined(_MSC_VER) + #define SPNG_CDECL __cdecl +#else + #define SPNG_CDECL +#endif + +#include +#include +#include + +#define SPNG_VERSION_MAJOR 0 +#define SPNG_VERSION_MINOR 7 +#define SPNG_VERSION_PATCH 3 + +enum spng_errno +{ + SPNG_IO_ERROR = -2, + SPNG_IO_EOF = -1, + SPNG_OK = 0, + SPNG_EINVAL, + SPNG_EMEM, + SPNG_EOVERFLOW, + SPNG_ESIGNATURE, + SPNG_EWIDTH, + SPNG_EHEIGHT, + SPNG_EUSER_WIDTH, + SPNG_EUSER_HEIGHT, + SPNG_EBIT_DEPTH, + SPNG_ECOLOR_TYPE, + SPNG_ECOMPRESSION_METHOD, + SPNG_EFILTER_METHOD, + SPNG_EINTERLACE_METHOD, + SPNG_EIHDR_SIZE, + SPNG_ENOIHDR, + SPNG_ECHUNK_POS, + SPNG_ECHUNK_SIZE, + SPNG_ECHUNK_CRC, + SPNG_ECHUNK_TYPE, + SPNG_ECHUNK_UNKNOWN_CRITICAL, + SPNG_EDUP_PLTE, + SPNG_EDUP_CHRM, + SPNG_EDUP_GAMA, + SPNG_EDUP_ICCP, + SPNG_EDUP_SBIT, + SPNG_EDUP_SRGB, + SPNG_EDUP_BKGD, + SPNG_EDUP_HIST, + SPNG_EDUP_TRNS, + SPNG_EDUP_PHYS, + SPNG_EDUP_TIME, + SPNG_EDUP_OFFS, + SPNG_EDUP_EXIF, + SPNG_ECHRM, + SPNG_EPLTE_IDX, + SPNG_ETRNS_COLOR_TYPE, + SPNG_ETRNS_NO_PLTE, + SPNG_EGAMA, + SPNG_EICCP_NAME, + SPNG_EICCP_COMPRESSION_METHOD, + SPNG_ESBIT, + SPNG_ESRGB, + SPNG_ETEXT, + SPNG_ETEXT_KEYWORD, + SPNG_EZTXT, + SPNG_EZTXT_COMPRESSION_METHOD, + SPNG_EITXT, + SPNG_EITXT_COMPRESSION_FLAG, + SPNG_EITXT_COMPRESSION_METHOD, + SPNG_EITXT_LANG_TAG, + SPNG_EITXT_TRANSLATED_KEY, + SPNG_EBKGD_NO_PLTE, + SPNG_EBKGD_PLTE_IDX, + SPNG_EHIST_NO_PLTE, + SPNG_EPHYS, + SPNG_ESPLT_NAME, + SPNG_ESPLT_DUP_NAME, + SPNG_ESPLT_DEPTH, + SPNG_ETIME, + SPNG_EOFFS, + SPNG_EEXIF, + SPNG_EIDAT_TOO_SHORT, + SPNG_EIDAT_STREAM, + SPNG_EZLIB, + SPNG_EFILTER, + SPNG_EBUFSIZ, + SPNG_EIO, + SPNG_EOF, + SPNG_EBUF_SET, + SPNG_EBADSTATE, + SPNG_EFMT, + SPNG_EFLAGS, + SPNG_ECHUNKAVAIL, + SPNG_ENCODE_ONLY, + SPNG_EOI, + SPNG_ENOPLTE, + SPNG_ECHUNK_LIMITS, + SPNG_EZLIB_INIT, + SPNG_ECHUNK_STDLEN, + SPNG_EINTERNAL, + SPNG_ECTXTYPE, + SPNG_ENOSRC, + SPNG_ENODST, + SPNG_EOPSTATE, + SPNG_ENOTFINAL, +}; + +enum spng_text_type +{ + SPNG_TEXT = 1, + SPNG_ZTXT = 2, + SPNG_ITXT = 3 +}; + +enum spng_color_type +{ + SPNG_COLOR_TYPE_GRAYSCALE = 0, + SPNG_COLOR_TYPE_TRUECOLOR = 2, + SPNG_COLOR_TYPE_INDEXED = 3, + SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4, + SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6 +}; + +enum spng_filter +{ + SPNG_FILTER_NONE = 0, + SPNG_FILTER_SUB = 1, + SPNG_FILTER_UP = 2, + SPNG_FILTER_AVERAGE = 3, + SPNG_FILTER_PAETH = 4 +}; + +enum spng_filter_choice +{ + SPNG_DISABLE_FILTERING = 0, + SPNG_FILTER_CHOICE_NONE = 8, + SPNG_FILTER_CHOICE_SUB = 16, + SPNG_FILTER_CHOICE_UP = 32, + SPNG_FILTER_CHOICE_AVG = 64, + SPNG_FILTER_CHOICE_PAETH = 128, + SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128) +}; + +enum spng_interlace_method +{ + SPNG_INTERLACE_NONE = 0, + SPNG_INTERLACE_ADAM7 = 1 +}; + +/* Channels are always in byte-order */ +enum spng_format +{ + SPNG_FMT_RGBA8 = 1, + SPNG_FMT_RGBA16 = 2, + SPNG_FMT_RGB8 = 4, + + /* Partially implemented, see documentation */ + SPNG_FMT_GA8 = 16, + SPNG_FMT_GA16 = 32, + SPNG_FMT_G8 = 64, + + /* No conversion or scaling */ + SPNG_FMT_PNG = 256, + SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */ +}; + +enum spng_ctx_flags +{ + SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */ + SPNG_CTX_ENCODER = 2 /* Create an encoder context */ +}; + +enum spng_decode_flags +{ + SPNG_DECODE_USE_TRNS = 1, /* Deprecated */ + SPNG_DECODE_USE_GAMA = 2, /* Deprecated */ + SPNG_DECODE_USE_SBIT = 8, /* Undocumented */ + + SPNG_DECODE_TRNS = 1, /* Apply transparency */ + SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */ + SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */ +}; + +enum spng_crc_action +{ + /* Default for critical chunks */ + SPNG_CRC_ERROR = 0, + + /* Discard chunk, invalid for critical chunks. + Since v0.6.2: default for ancillary chunks */ + SPNG_CRC_DISCARD = 1, + + /* Ignore and don't calculate checksum. + Since v0.6.2: also ignores checksums in DEFLATE streams */ + SPNG_CRC_USE = 2 +}; + +enum spng_encode_flags +{ + SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */ + SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */ +}; + +struct spng_ihdr +{ + uint32_t width; + uint32_t height; + uint8_t bit_depth; + uint8_t color_type; + uint8_t compression_method; + uint8_t filter_method; + uint8_t interlace_method; +}; + +struct spng_plte_entry +{ + uint8_t red; + uint8_t green; + uint8_t blue; + + uint8_t alpha; /* Reserved for internal use */ +}; + +struct spng_plte +{ + uint32_t n_entries; + struct spng_plte_entry entries[256]; +}; + +struct spng_trns +{ + uint16_t gray; + + uint16_t red; + uint16_t green; + uint16_t blue; + + uint32_t n_type3_entries; + uint8_t type3_alpha[256]; +}; + +struct spng_chrm_int +{ + uint32_t white_point_x; + uint32_t white_point_y; + uint32_t red_x; + uint32_t red_y; + uint32_t green_x; + uint32_t green_y; + uint32_t blue_x; + uint32_t blue_y; +}; + +struct spng_chrm +{ + double white_point_x; + double white_point_y; + double red_x; + double red_y; + double green_x; + double green_y; + double blue_x; + double blue_y; +}; + +struct spng_iccp +{ + char profile_name[80]; + size_t profile_len; + char *profile; +}; + +struct spng_sbit +{ + uint8_t grayscale_bits; + uint8_t red_bits; + uint8_t green_bits; + uint8_t blue_bits; + uint8_t alpha_bits; +}; + +struct spng_text +{ + char keyword[80]; + int type; + + size_t length; + char *text; + + uint8_t compression_flag; /* iTXt only */ + uint8_t compression_method; /* iTXt, ztXt only */ + char *language_tag; /* iTXt only */ + char *translated_keyword; /* iTXt only */ +}; + +struct spng_bkgd +{ + uint16_t gray; /* Only for gray/gray alpha */ + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t plte_index; /* Only for indexed color */ +}; + +struct spng_hist +{ + uint16_t frequency[256]; +}; + +struct spng_phys +{ + uint32_t ppu_x, ppu_y; + uint8_t unit_specifier; +}; + +struct spng_splt_entry +{ + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t alpha; + uint16_t frequency; +}; + +struct spng_splt +{ + char name[80]; + uint8_t sample_depth; + uint32_t n_entries; + struct spng_splt_entry *entries; +}; + +struct spng_time +{ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +struct spng_offs +{ + int32_t x, y; + uint8_t unit_specifier; +}; + +struct spng_exif +{ + size_t length; + char *data; +}; + +struct spng_chunk +{ + size_t offset; + uint32_t length; + uint8_t type[4]; + uint32_t crc; +}; + +enum spng_location +{ + SPNG_AFTER_IHDR = 1, + SPNG_AFTER_PLTE = 2, + SPNG_AFTER_IDAT = 8, +}; + +struct spng_unknown_chunk +{ + uint8_t type[4]; + size_t length; + void *data; + enum spng_location location; +}; + +enum spng_option +{ + SPNG_KEEP_UNKNOWN_CHUNKS = 1, + + SPNG_IMG_COMPRESSION_LEVEL, + SPNG_IMG_WINDOW_BITS, + SPNG_IMG_MEM_LEVEL, + SPNG_IMG_COMPRESSION_STRATEGY, + + SPNG_TEXT_COMPRESSION_LEVEL, + SPNG_TEXT_WINDOW_BITS, + SPNG_TEXT_MEM_LEVEL, + SPNG_TEXT_COMPRESSION_STRATEGY, + + SPNG_FILTER_CHOICE, + SPNG_CHUNK_COUNT_LIMIT, + SPNG_ENCODE_TO_BUFFER, +}; + +typedef void* SPNG_CDECL spng_malloc_fn(size_t size); +typedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size); +typedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size); +typedef void SPNG_CDECL spng_free_fn(void* ptr); + +struct spng_alloc +{ + spng_malloc_fn *malloc_fn; + spng_realloc_fn *realloc_fn; + spng_calloc_fn *calloc_fn; + spng_free_fn *free_fn; +}; + +struct spng_row_info +{ + uint32_t scanline_idx; + uint32_t row_num; /* deinterlaced row index */ + int pass; + uint8_t filter; +}; + +typedef struct spng_ctx spng_ctx; + +typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length); +typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length); + +typedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length); + +SPNG_API spng_ctx *spng_ctx_new(int flags); +SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags); +SPNG_API void spng_ctx_free(spng_ctx *ctx); + +SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size); +SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user); +SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file); + +SPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error); + +SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height); +SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height); + +SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size); +SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size); + +SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary); + +SPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value); +SPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value); + +SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len); + +/* Decode */ +SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags); + +/* Progressive decode */ +SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len); +SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len); +SPNG_API int spng_decode_chunks(spng_ctx *ctx); + +/* Encode/decode */ +SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info); + +/* Encode */ +SPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags); + +/* Progressive encode */ +SPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len); +SPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len); +SPNG_API int spng_encode_chunks(spng_ctx *ctx); + +SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr); +SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte); +SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns); +SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm); +SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int); +SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma); +SPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int); +SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp); +SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit); +SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent); +SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text); +SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd); +SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist); +SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys); +SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt); +SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time); +SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks); + +/* Official extensions */ +SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs); +SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif); + + +SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr); +SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte); +SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns); +SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm); +SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int); +SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma); +SPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma); +SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp); +SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit); +SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent); +SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text); +SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd); +SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist); +SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys); +SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt); +SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time); +SPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks); + +/* Official extensions */ +SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs); +SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif); + + +SPNG_API const char *spng_strerror(int err); +SPNG_API const char *spng_version_string(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SPNG_H */ diff --git a/CMakeLists.txt b/CMakeLists.txt index fa409f516c..1496c8ec05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,6 +327,9 @@ OCV_OPTION(WITH_OPENNI2 "Include OpenNI2 support" OFF OCV_OPTION(WITH_PNG "Include PNG support" ON VISIBLE_IF TRUE VERIFY HAVE_PNG) +OCV_OPTION(WITH_SPNG "Include SPNG support" OFF + VISIBLE_IF TRUE + VERIFY HAVE_SPNG) OCV_OPTION(WITH_GDCM "Include DICOM support" OFF VISIBLE_IF TRUE VERIFY HAVE_GDCM) @@ -1326,8 +1329,12 @@ if(WITH_WEBP OR HAVE_WEBP) status(" WEBP:" WEBP_FOUND THEN "${WEBP_LIBRARY} (ver ${WEBP_VERSION})" ELSE "build (ver ${WEBP_VERSION})") endif() -if(WITH_PNG OR HAVE_PNG) - status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION})" ELSE "build (ver ${PNG_VERSION})") +if(WITH_PNG OR HAVE_PNG OR WITH_SPNG) + if(WITH_SPNG) + status(" PNG:" "build-${SPNG_LIBRARY} (ver ${SPNG_VERSION})") + else() + status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION})" ELSE "build (ver ${PNG_VERSION})") + endif() endif() if(WITH_TIFF OR HAVE_TIFF) diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake index 00886cc131..4e8a1de17a 100644 --- a/cmake/OpenCVFindLibsGrfmt.cmake +++ b/cmake/OpenCVFindLibsGrfmt.cmake @@ -221,8 +221,21 @@ if(WITH_JASPER AND NOT HAVE_OPENJPEG) endif() endif() +if(WITH_SPNG) + set(SPNG_LIBRARY libspng CACHE INTERNAL "") + set(SPNG_LIBRARIES ${SPNG_LIBRARY}) + add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libspng") + set(SPNG_INCLUDE_DIR "${${SPNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "") + set(SPNG_DEFINITIONS "") + ocv_parse_header("${SPNG_INCLUDE_DIR}/spng.h" SPNG_VERSION_LINES SPNG_VERSION_MAJOR SPNG_VERSION_MINOR SPNG_VERSION_PATCH) + + set(HAVE_SPNG YES) + set(SPNG_VERSION "${SPNG_VERSION_MAJOR}.${SPNG_VERSION_MINOR}.${SPNG_VERSION_PATCH}") + message(STATUS "imgcodecs: PNG codec will use SPNG, version: ${SPNG_VERSION} ") +endif() + # --- libpng (optional, should be searched after zlib) --- -if(WITH_PNG) +if(NOT HAVE_SPNG AND WITH_PNG) if(BUILD_PNG) ocv_clear_vars(PNG_FOUND) else() @@ -254,6 +267,7 @@ if(WITH_PNG) set(PNG_VERSION "${PNG_LIBPNG_VER_MAJOR}.${PNG_LIBPNG_VER_MINOR}.${PNG_LIBPNG_VER_RELEASE}") endif() + # --- OpenEXR (optional) --- if(WITH_OPENEXR) ocv_clear_vars(HAVE_OPENEXR) diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index 6439d8b43f..633164c75c 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -106,6 +106,9 @@ /* PNG codec */ #cmakedefine HAVE_PNG +/* PNG codec */ +#cmakedefine HAVE_SPNG + /* Posix threads (pthreads) */ #cmakedefine HAVE_PTHREAD diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 76f320f19f..a28769d8b7 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -2155,7 +2155,7 @@ static void showSaveDialog(CvWindow& window) #endif ofn.hwndOwner = window.hwnd; ofn.lpstrFilter = -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) "Portable Network Graphics files (*.png)\0*.png\0" #endif "Windows bitmap (*.bmp;*.dib)\0*.bmp;*.dib\0" @@ -2181,7 +2181,7 @@ static void showSaveDialog(CvWindow& window) ofn.lpstrFile = szFileName; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN | OFN_NOCHANGEDIR; -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) ofn.lpstrDefExt = "png"; #else ofn.lpstrDefExt = "bmp"; diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index b9c4683aa9..ac201e7935 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -24,6 +24,12 @@ if(HAVE_WEBP) list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES}) endif() +if(HAVE_SPNG) + add_definitions(${SPNG_DEFINITIONS}) + ocv_include_directories(${SPNG_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${SPNG_LIBRARY}) +endif() + if(HAVE_PNG) add_definitions(${PNG_DEFINITIONS}) ocv_include_directories(${PNG_INCLUDE_DIR}) @@ -67,7 +73,7 @@ if(HAVE_OPENEXR) endif() endif() -if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) +if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR OR HAVE_SPNG) ocv_include_directories(${ZLIB_INCLUDE_DIRS}) list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) endif() diff --git a/modules/imgcodecs/perf/perf_png.cpp b/modules/imgcodecs/perf/perf_png.cpp new file mode 100644 index 0000000000..7893d841c4 --- /dev/null +++ b/modules/imgcodecs/perf/perf_png.cpp @@ -0,0 +1,41 @@ +// 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 "perf_precomp.hpp" + +namespace opencv_test +{ +using namespace perf; + +typedef perf::TestBaseWithParam PNG; + +PERF_TEST(PNG, decode) +{ + String filename = getDataPath("perf/2560x1600.png"); + + FILE *f = fopen(filename.c_str(), "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + vector file_buf((size_t)len); + EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f)); + fclose(f); f = NULL; + + TEST_CYCLE() imdecode(file_buf, IMREAD_UNCHANGED); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST(PNG, encode) +{ + String filename = getDataPath("perf/2560x1600.png"); + cv::Mat src = imread(filename); + + vector buf; + TEST_CYCLE() imencode(".png", src, buf); + + SANITY_CHECK_NOTHING(); +} + +} // namespace diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp new file mode 100644 index 0000000000..8bcbbe21c7 --- /dev/null +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -0,0 +1,754 @@ +// 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" + +#ifdef HAVE_SPNG + +/****************************************************************************************\ + This part of the file implements PNG codec on base of libspng library, + in particular, this code is based on example.c from libspng + (see 3rdparty/libspng/LICENSE for copyright notice) +\****************************************************************************************/ + +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE 0 +#endif +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif + +#include +#include + +#include "grfmt_spng.hpp" + +/* + * libspng does not support RGB -> Gray conversion. In order to decode colorful images as grayscale + * we need conversion functions. In the previous png implementation(grfmt_png), the author was set + * to particular values for rgb coefficients. OpenCV icvCvt_BGR2Gray function values does not match + * with these values. (png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 );) For this codec implementation, + * slightly modified versions are implemented in the below of this page. +*/ +void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step, + uchar *gray, int gray_step, + cv::Size size, int _swap_rb); + +void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step, + uchar *gray, int gray_step, + cv::Size size, int _swap_rb); + +void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step, + ushort *gray, int gray_step, + cv::Size size, int ncn, int _swap_rb); + +namespace cv +{ + +/////////////////////// SPngDecoder /////////////////// + +SPngDecoder::SPngDecoder() +{ + m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa"; + m_color_type = 0; + m_ctx = 0; + m_f = 0; + m_buf_supported = true; + m_buf_pos = 0; + m_bit_depth = 0; +} + +SPngDecoder::~SPngDecoder() +{ + close(); +} + +ImageDecoder SPngDecoder::newDecoder() const +{ + return makePtr(); +} + +void SPngDecoder::close() +{ + if (m_f) + { + fclose(m_f); + m_f = 0; + } + + if (m_ctx) + { + struct spng_ctx *ctx = (struct spng_ctx *)m_ctx; + spng_ctx_free(ctx); + m_ctx = 0; + } +} + +int SPngDecoder::readDataFromBuf(void *sp_ctx, void *user, void *dst, size_t size) +{ + /* + * typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length) + * Type definition for callback passed to spng_set_png_stream() for decoders. + * A read callback function should copy length bytes to dest and return 0 or SPNG_IO_EOF/SPNG_IO_ERROR on error. + */ + CV_UNUSED(sp_ctx); + SPngDecoder *decoder = (SPngDecoder *)(user); + CV_Assert(decoder); + + const Mat &buf = decoder->m_buf; + if (decoder->m_buf_pos + size > buf.cols * buf.rows * buf.elemSize()) + { + return SPNG_IO_ERROR; + } + memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size); + decoder->m_buf_pos += size; + + return 0; +} + +bool SPngDecoder::readHeader() +{ + volatile bool result = false; + close(); + + spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32); + + if (!ctx) + { + spng_ctx_free(ctx); + return false; + } + + m_ctx = ctx; + spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); + + if (!m_buf.empty()) + spng_set_png_stream((struct spng_ctx *)m_ctx, (spng_rw_fn *)readDataFromBuf, this); + else + { + m_f = fopen(m_filename.c_str(), "rb"); + if (m_f) + { + spng_set_png_file(ctx, m_f); + } + } + + if (!m_buf.empty() || m_f) + { + struct spng_ihdr ihdr; + int ret = spng_get_ihdr(ctx, &ihdr); + + if (ret == SPNG_OK) + { + m_width = static_cast(ihdr.width); + m_height = static_cast(ihdr.height); + m_color_type = ihdr.color_type; + m_bit_depth = ihdr.bit_depth; + + if (ihdr.bit_depth <= 8 || ihdr.bit_depth == 16) + { + int num_trans; + switch (ihdr.color_type) + { + case SPNG_COLOR_TYPE_TRUECOLOR: + case SPNG_COLOR_TYPE_INDEXED: + struct spng_trns trns; + num_trans = !spng_get_trns(ctx, &trns); + if (num_trans > 0) + m_type = CV_8UC4; + else + m_type = CV_8UC3; + break; + case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: + case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: + m_type = CV_8UC4; + break; + default: + m_type = CV_8UC1; + } + if (ihdr.bit_depth == 16) + m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type)); + result = true; + } + } + } + + return result; +} + +bool SPngDecoder::readData(Mat &img) +{ + volatile bool result = false; + bool color = img.channels() > 1; + + struct spng_ctx *png_ptr = (struct spng_ctx *)m_ctx; + + if (m_ctx && m_width && m_height) + { + int fmt = SPNG_FMT_PNG; + + struct spng_trns trns; + int have_trns = spng_get_trns((struct spng_ctx *)m_ctx, &trns); + + int decode_flags = 0; + if (have_trns == SPNG_OK) + { + decode_flags = SPNG_DECODE_TRNS; + } + if (img.channels() == 4) + { + if (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR || + m_color_type == SPNG_COLOR_TYPE_INDEXED || + m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) + fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8; + else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE) + fmt = m_bit_depth == 16 ? SPNG_FMT_GA16 : SPNG_FMT_GA8; + else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) + { + fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8; + } + else + fmt = SPNG_FMT_RGBA8; + } + if (img.channels() == 3) + { + fmt = SPNG_FMT_RGB8; + if ((m_color_type == SPNG_COLOR_TYPE_GRAYSCALE || m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) && + m_bit_depth == 16) + fmt = SPNG_FMT_RGB8; + else if (m_bit_depth == 16) + fmt = SPNG_FMT_PNG; + } + else if (img.channels() == 1) + { + if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth <= 8) + fmt = SPNG_FMT_G8; + else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth == 16) + { + if (img.depth() == CV_8U || img.depth() == CV_8S) + { + fmt = SPNG_FMT_RGB8; + } + else + { + fmt = SPNG_FMT_PNG; + } + } + else if (m_color_type == SPNG_COLOR_TYPE_INDEXED || + m_color_type == SPNG_COLOR_TYPE_TRUECOLOR) + { + if (img.depth() == CV_8U || img.depth() == CV_8S) + { + fmt = SPNG_FMT_RGB8; + } + else + { + fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8; + } + } + else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || fmt == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) + { + if (img.depth() == CV_8U || img.depth() == CV_8S) + { + fmt = SPNG_FMT_RGB8; + } + else + { + fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8; + } + } + else + fmt = SPNG_FMT_RGB8; + } + + size_t image_width, image_size = 0; + int ret = spng_decoded_image_size(png_ptr, fmt, &image_size); + struct spng_ihdr ihdr; + spng_get_ihdr(png_ptr, &ihdr); + + if (ret == SPNG_OK) + { + image_width = image_size / m_height; + + ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags); + if (ret == SPNG_OK) + { + struct spng_row_info row_info{}; + + // If user wants to read image as grayscale(IMREAD_GRAYSCALE), but image format is not + // decode image then convert to grayscale + if (!color && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA8 || fmt == SPNG_FMT_RGBA16)) + { + if (ihdr.interlace_method == 0) + { + AutoBuffer buffer; + buffer.allocate(image_width); + if (fmt == SPNG_FMT_RGB8) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer.data(), image_width); + spngCvt_BGR2Gray_8u_C3C1R( + buffer.data(), + 0, + img.data + row_info.row_num * img.step, + 0, Size(m_width, 1), 2); + } while (ret == SPNG_OK); + } + else if (fmt == SPNG_FMT_RGBA8) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer.data(), image_width); + spngCvt_BGRA2Gray_8u_C4C1R( + buffer.data(), + 0, + img.data + row_info.row_num * img.step, + 0, Size(m_width, 1), 2); + } while (ret == SPNG_OK); + } + else if (fmt == SPNG_FMT_RGBA16) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer.data(), image_width); + spngCvt_BGRA2Gray_16u_CnC1R( + reinterpret_cast(buffer.data()), 0, + reinterpret_cast(img.data + row_info.row_num * img.step), + 0, Size(m_width, 1), + 4, 2); + } while (ret == SPNG_OK); + } + } + else + { + AutoBuffer imageBuffer(image_size); + spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0); + int step = m_width * img.channels(); + if (fmt == SPNG_FMT_RGB8) + { + spngCvt_BGR2Gray_8u_C3C1R( + imageBuffer.data(), + step, + img.data, + step, Size(m_width, m_height), 2); + } + else if (fmt == SPNG_FMT_RGBA8) + { + spngCvt_BGRA2Gray_8u_C4C1R( + imageBuffer.data(), + step, + img.data, + step, Size(m_width, m_height), 2); + } + else if (fmt == SPNG_FMT_RGBA16) + { + spngCvt_BGRA2Gray_16u_CnC1R( + reinterpret_cast(imageBuffer.data()), step / 3, + reinterpret_cast(img.data), + step / 3, Size(m_width, m_height), + 4, 2); + } + } + } + else if (color) + { // RGB -> BGR, convert row by row if png is non-interlaced, otherwise convert image as one + int step = m_width * img.channels(); + AutoBuffer _buffer(m_height); + uchar **buffer = _buffer.data(); + for (int y = 0; y < m_height; y++) + { + buffer[y] = img.data + y * img.step; + } + if (img.channels() == 4 && m_bit_depth == 16) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); + if (ihdr.interlace_method == 0) + { + icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast(buffer[row_info.row_num]), 0, + reinterpret_cast(buffer[row_info.row_num]), 0, + Size(m_width, 1)); + } + } while (ret == SPNG_OK); + if (ihdr.interlace_method) + { + icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast(img.data), step * 2, reinterpret_cast(img.data), step * 2, Size(m_width, m_height)); + } + } + else if (img.channels() == 4) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); + if (ihdr.interlace_method == 0) + { + icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1)); + } + } while (ret == SPNG_OK); + if (ihdr.interlace_method) + { + icvCvt_RGBA2BGRA_8u_C4R(img.data, step, img.data, step, Size(m_width, m_height)); + } + } + else if (fmt == SPNG_FMT_PNG) + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); + if (ihdr.interlace_method == 0) + { + icvCvt_RGB2BGR_16u_C3R(reinterpret_cast(buffer[row_info.row_num]), 0, + reinterpret_cast(buffer[row_info.row_num]), 0, Size(m_width, 1)); + } + } while (ret == SPNG_OK); + if (ihdr.interlace_method) + { + icvCvt_RGB2BGR_16u_C3R(reinterpret_cast(img.data), step, + reinterpret_cast(img.data), step, Size(m_width, m_height)); + } + } + else + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); + if (ihdr.interlace_method == 0) + { + icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1)); + } + } while (ret == SPNG_OK); + if (ihdr.interlace_method) + { + icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height)); + } + } + } + else + { + do + { + ret = spng_get_row_info(png_ptr, &row_info); + if (ret) + break; + + ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width); + } while (ret == SPNG_OK); + } + } + + if (ret == SPNG_EOI) + { + struct spng_exif exif_s{}; + ret = spng_get_exif(png_ptr, &exif_s); + if (ret == SPNG_OK) + { + if (exif_s.data && exif_s.length > 0) + { + m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length); + } + } + result = true; + } + } + } + + return result; +} + +/////////////////////// SPngEncoder /////////////////// + +SPngEncoder::SPngEncoder() +{ + m_description = "Portable Network Graphics files (*.png)"; + m_buf_supported = true; +} + +SPngEncoder::~SPngEncoder() +{ +} + +bool SPngEncoder::isFormatSupported(int depth) const +{ + return depth == CV_8U || depth == CV_16U; +} + +ImageEncoder SPngEncoder::newEncoder() const +{ + return makePtr(); +} + +int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length) +{ + CV_UNUSED(ctx); + SPngEncoder *encoder = (SPngEncoder *)(user); + CV_Assert(encoder && encoder->m_buf); + size_t cursz = encoder->m_buf->size(); + encoder->m_buf->resize(cursz + length); + memcpy(&(*encoder->m_buf)[cursz], dst_src, length); + return 0; +} + +bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) +{ + int fmt; + spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER); + FILE *volatile f = 0; + int width = img.cols, height = img.rows; + int depth = img.depth(), channels = img.channels(); + volatile bool result = false; + + if (depth != CV_8U && depth != CV_16U) + return false; + + if (ctx) + { + struct spng_ihdr ihdr = {}; + ihdr.height = height; + ihdr.width = width; + int compression_level = -1; // Invalid value to allow setting 0-9 as valid + int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy + bool isBilevel = false; + + for (size_t i = 0; i < params.size(); i += 2) + { + if (params[i] == IMWRITE_PNG_COMPRESSION) + { + compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy + compression_level = params[i + 1]; + compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION); + } + if (params[i] == IMWRITE_PNG_STRATEGY) + { + compression_strategy = params[i + 1]; + compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED); + } + if (params[i] == IMWRITE_PNG_BILEVEL) + { + isBilevel = params[i + 1] != 0; + } + } + fmt = channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR + : SPNG_COLOR_TYPE_TRUECOLOR_ALPHA; + + ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16; + ihdr.color_type = fmt; + ihdr.interlace_method = SPNG_INTERLACE_NONE; + ihdr.filter_method = SPNG_FILTER_NONE; + ihdr.compression_method = 0; + spng_set_ihdr(ctx, &ihdr); + + if (m_buf) + { + spng_set_png_stream(ctx, (spng_rw_fn *)writeDataToBuf, this); + } + else + { + f = fopen(m_filename.c_str(), "wb"); + if (f) + spng_set_png_file(ctx, f); + } + + if (m_buf || f) + { + if (compression_level >= 0) + { + spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level); + } + else + { + spng_set_option(ctx, SPNG_FILTER_CHOICE, SPNG_FILTER_CHOICE_SUB); + spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, Z_BEST_SPEED); + } + spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy); + + int ret; + spng_encode_chunks(ctx); + ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE); + if (channels > 1) + { + int error; + if (ret == SPNG_OK) + { + if (depth == CV_16U) + { + AutoBuffer buff2; + buff2.allocate(height); + for (int y = 0; y < height; y++) + buff2[y] = reinterpret_cast(img.data + y * img.step); + + AutoBuffer _buffer; + _buffer.allocate(width * channels * 2); + for (int y = 0; y < height; y++) + { + if (channels == 3) + { + icvCvt_BGR2RGB_16u_C3R(buff2[y], 0, + _buffer.data(), 0, Size(width, 1)); + } + else if (channels == 4) + { + icvCvt_BGRA2RGBA_16u_C4R(buff2[y], 0, + _buffer.data(), 0, Size(width, 1)); + } + error = spng_encode_row(ctx, _buffer.data(), width * channels * 2); + if (error) + break; + } + } + else + { + AutoBuffer buff; + buff.allocate(height); + for (int y = 0; y < height; y++) + buff[y] = img.data + y * img.step; + + AutoBuffer _buffer; + _buffer.allocate(width * channels); + for (int y = 0; y < height; y++) + { + if (channels == 3) + { + icvCvt_BGR2RGB_8u_C3R(buff[y], 0, _buffer.data(), 0, Size(width, 1)); + } + else if (channels == 4) + { + icvCvt_BGRA2RGBA_8u_C4R(buff[y], 0, _buffer.data(), 0, Size(width, 1)); + } + error = spng_encode_row(ctx, _buffer.data(), width * channels); + if (error) + break; + } + } + if (error == SPNG_EOI) + { // success + spng_encode_chunks(ctx); + ret = SPNG_OK; + } + } + } + else + { + int error; + for (int y = 0; y < height; y++) + { + error = spng_encode_row(ctx, img.data + y * img.step, width * channels * (depth == CV_16U ? 2 : 1)); + if (error) + break; + } + if (error == SPNG_EOI) + { // success + spng_encode_chunks(ctx); + ret = SPNG_OK; + } + } + if (ret == SPNG_OK) + result = true; + } + } + + spng_ctx_free(ctx); + if (f) + fclose((FILE *)f); + + return result; +} + +} + +void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step, + uchar *gray, int gray_step, + cv::Size size, int _swap_rb) +{ + int i; + for (; size.height--; gray += gray_step) + { + double cBGR0 = 0.1140441895; + double cBGR2 = 0.2989807129; + if (_swap_rb) + std::swap(cBGR0, cBGR2); + for (i = 0; i < size.width; i++, bgr += 3) + { + int t = static_cast(cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]); + gray[i] = (uchar)t; + } + + bgr += bgr_step - size.width * 3; + } +} + +void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step, + uchar *gray, int gray_step, + cv::Size size, int _swap_rb) +{ + int i; + for (; size.height--; gray += gray_step) + { + double cBGR0 = 0.1140441895; + double cBGR2 = 0.2989807129; + if (_swap_rb) + std::swap(cBGR0, cBGR2); + for (i = 0; i < size.width; i++, bgra += 4) + { + int t = cBGR0 * bgra[0] + 0.5869750977 * bgra[1] + cBGR2 * bgra[2]; + gray[i] = (uchar)t; + } + + bgra += rgba_step - size.width * 4; + } +} + +void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step, + ushort *gray, int gray_step, + cv::Size size, int ncn, int _swap_rb) +{ + int i; + for (; size.height--; gray += gray_step) + { + double cBGR0 = 0.1140441895; + double cBGR2 = 0.2989807129; + if (_swap_rb) + std::swap(cBGR0, cBGR2); + for (i = 0; i < size.width; i++, bgr += ncn) + { + int t = cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]; + gray[i] = (ushort)t; + } + + bgr += bgr_step - size.width * ncn; + } +} + +#endif + +/* End of file. */ diff --git a/modules/imgcodecs/src/grfmt_spng.hpp b/modules/imgcodecs/src/grfmt_spng.hpp new file mode 100644 index 0000000000..648fa99ff6 --- /dev/null +++ b/modules/imgcodecs/src/grfmt_spng.hpp @@ -0,0 +1,60 @@ +// 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 _GRFMT_SPNG_H_ +#define _GRFMT_SPNG_H_ + +#ifdef HAVE_SPNG + +#include "grfmt_base.hpp" +#include "bitstrm.hpp" + +namespace cv +{ + +class SPngDecoder CV_FINAL : public BaseImageDecoder +{ +public: + + SPngDecoder(); + virtual ~SPngDecoder(); + + bool readData( Mat& img ) CV_OVERRIDE; + bool readHeader() CV_OVERRIDE; + void close(); + + ImageDecoder newDecoder() const CV_OVERRIDE; + +protected: + + static int readDataFromBuf(void* sp_ctx, void *user, void* dst, size_t size); + + int m_bit_depth; + void* m_ctx; + FILE* m_f; + int m_color_type; + size_t m_buf_pos; +}; + + +class SPngEncoder CV_FINAL : public BaseImageEncoder +{ +public: + SPngEncoder(); + virtual ~SPngEncoder(); + + bool isFormatSupported( int depth ) const CV_OVERRIDE; + bool write( const Mat& img, const std::vector& params ) CV_OVERRIDE; + + ImageEncoder newEncoder() const CV_OVERRIDE; + +protected: + static int writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length); +}; + +} + +#endif + +#endif/*_GRFMT_PNG_H_*/ diff --git a/modules/imgcodecs/src/grfmts.hpp b/modules/imgcodecs/src/grfmts.hpp index 496cb192d5..637538d223 100644 --- a/modules/imgcodecs/src/grfmts.hpp +++ b/modules/imgcodecs/src/grfmts.hpp @@ -49,6 +49,7 @@ #include "grfmt_pxm.hpp" #include "grfmt_pfm.hpp" #include "grfmt_tiff.hpp" +#include "grfmt_spng.hpp" #include "grfmt_png.hpp" #include "grfmt_jpeg2000.hpp" #include "grfmt_jpeg2000_openjpeg.hpp" diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index e9b6d0517c..290170e094 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -167,7 +167,10 @@ struct ImageCodecInitializer decoders.push_back( makePtr() ); encoders.push_back( makePtr() ); #endif - #ifdef HAVE_PNG + #ifdef HAVE_SPNG + decoders.push_back( makePtr() ); + encoders.push_back( makePtr() ); + #elif defined(HAVE_PNG) decoders.push_back( makePtr() ); encoders.push_back( makePtr() ); #endif diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index a6af4b92e2..bf52a9f896 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -211,7 +211,7 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) const string all_exts[] = { -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) ".png", #endif #ifdef HAVE_TIFF diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index f71fabc7e4..cdc7da39b2 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -5,7 +5,7 @@ namespace opencv_test { namespace { -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) TEST(Imgcodecs_Png, write_big) { @@ -186,6 +186,225 @@ const string exif_files[] = INSTANTIATE_TEST_CASE_P(ExifFiles, Imgcodecs_PNG_Exif, testing::ValuesIn(exif_files)); + +typedef testing::TestWithParam Imgcodecs_Png_PngSuite; + +TEST_P(Imgcodecs_Png_PngSuite, decode) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "pngsuite/" + GetParam() + ".png"; + const string xml_filename = root + "pngsuite/" + GetParam() + ".xml"; + FileStorage fs(xml_filename, FileStorage::READ); + EXPECT_TRUE(fs.isOpened()); + + Mat src = imread(filename, IMREAD_UNCHANGED); + Mat gt; + fs.getFirstTopLevelNode() >> gt; + + EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), src, gt); +} + +const string pngsuite_files[] = +{ + "basi0g01", + "basi0g02", + "basi0g04", + "basi0g08", + "basi0g16", + "basi2c08", + "basi2c16", + "basi3p01", + "basi3p02", + "basi3p04", + "basi3p08", + "basi4a08", + "basi4a16", + "basi6a08", + "basi6a16", + "basn0g01", + "basn0g02", + "basn0g04", + "basn0g08", + "basn0g16", + "basn2c08", + "basn2c16", + "basn3p01", + "basn3p02", + "basn3p04", + "basn3p08", + "basn4a08", + "basn4a16", + "basn6a08", + "basn6a16", + "bgai4a08", + "bgai4a16", + "bgan6a08", + "bgan6a16", + "bgbn4a08", + "bggn4a16", + "bgwn6a08", + "bgyn6a16", + "ccwn2c08", + "ccwn3p08", + "cdfn2c08", + "cdhn2c08", + "cdsn2c08", + "cdun2c08", + "ch1n3p04", + "ch2n3p08", + "cm0n0g04", + "cm7n0g04", + "cm9n0g04", + "cs3n2c16", + "cs3n3p08", + "cs5n2c08", + "cs5n3p08", + "cs8n2c08", + "cs8n3p08", + "ct0n0g04", + "ct1n0g04", + "cten0g04", + "ctfn0g04", + "ctgn0g04", + "cthn0g04", + "ctjn0g04", + "ctzn0g04", + "exif2c08", + "f00n0g08", + "f00n2c08", + "f01n0g08", + "f01n2c08", + "f02n0g08", + "f02n2c08", + "f03n0g08", + "f03n2c08", + "f04n0g08", + "f04n2c08", + "f99n0g04", + "g03n0g16", + "g03n2c08", + "g03n3p04", + "g04n0g16", + "g04n2c08", + "g04n3p04", + "g05n0g16", + "g05n2c08", + "g05n3p04", + "g07n0g16", + "g07n2c08", + "g07n3p04", + "g10n0g16", + "g10n2c08", + "g10n3p04", + "g25n0g16", + "g25n2c08", + "g25n3p04", + "oi1n0g16", + "oi1n2c16", + "oi2n0g16", + "oi2n2c16", + "oi4n0g16", + "oi4n2c16", + "oi9n0g16", + "oi9n2c16", + "pp0n2c16", + "pp0n6a08", + "ps1n0g08", + "ps1n2c16", + "ps2n0g08", + "ps2n2c16", + "s01i3p01", + "s01n3p01", + "s02i3p01", + "s02n3p01", + "s03i3p01", + "s03n3p01", + "s04i3p01", + "s04n3p01", + "s05i3p02", + "s05n3p02", + "s06i3p02", + "s06n3p02", + "s07i3p02", + "s07n3p02", + "s08i3p02", + "s08n3p02", + "s09i3p02", + "s09n3p02", + "s32i3p04", + "s32n3p04", + "s33i3p04", + "s33n3p04", + "s34i3p04", + "s34n3p04", + "s35i3p04", + "s35n3p04", + "s36i3p04", + "s36n3p04", + "s37i3p04", + "s37n3p04", + "s38i3p04", + "s38n3p04", + "s39i3p04", + "s39n3p04", + "s40i3p04", + "s40n3p04", + "tbbn0g04", + "tbbn2c16", + "tbbn3p08", + "tbgn2c16", + "tbgn3p08", + "tbrn2c08", + "tbwn0g16", + "tbwn3p08", + "tbyn3p08", + "tm3n3p02", + "tp0n0g08", + "tp0n2c08", + "tp0n3p08", + "tp1n3p08", + "z00n2c08", + "z03n2c08", + "z06n2c08", + "z09n2c08", +}; + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite, + testing::ValuesIn(pngsuite_files)); + +typedef testing::TestWithParam Imgcodecs_Png_PngSuite_Corrupted; + +TEST_P(Imgcodecs_Png_PngSuite_Corrupted, decode) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "pngsuite/" + GetParam() + ".png"; + + Mat src = imread(filename, IMREAD_UNCHANGED); + + // Corrupted files should not be read + EXPECT_TRUE(src.empty()); +} + +const string pngsuite_files_corrupted[] = { + "xc1n0g08", + "xc9n2c08", + "xcrn0g04", + "xcsn0g01", + "xd0n2c08", + "xd3n2c08", + "xd9n2c08", + "xdtn0g01", + "xhdn0g08", + "xlfn0g04", + "xs1n0g01", + "xs2n0g01", + "xs4n0g01", + "xs7n0g01", +}; + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted, + testing::ValuesIn(pngsuite_files_corrupted)); + #endif // HAVE_PNG }} // namespace diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 9dbd2e33c7..44c8becec8 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -28,7 +28,7 @@ const tuple images[] = #ifdef HAVE_JPEG make_tuple("../cv/imgproc/stuff.jpg", Size(640, 480)), #endif -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) make_tuple("../cv/shared/pic1.png", Size(400, 300)), #endif make_tuple("../highgui/readwrite/ordinary.bmp", Size(480, 272)), @@ -148,7 +148,7 @@ typedef string Ext; typedef testing::TestWithParam Imgcodecs_Image; const string exts[] = { -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) "png", #endif #ifdef HAVE_TIFF diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index feb75de8b8..1d19565288 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -71,7 +71,7 @@ void CV_DrawingTest::run( int ) { //imwrite( filename, testImg ); ts->printf( ts->LOG, "test image can not be read"); -#ifdef HAVE_PNG +#if defined(HAVE_PNG) || defined(HAVE_SPNG) ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); #else ts->printf( ts->LOG, "PNG image support is not available"); From a4b191a7e044de508603986e9a303bddbcb4783c Mon Sep 17 00:00:00 2001 From: Harvey Date: Mon, 29 Aug 2022 10:21:17 +0800 Subject: [PATCH 040/313] 4-bit_palette_color --- modules/imgcodecs/src/grfmt_tiff.cpp | 12 ++++++++++++ modules/imgcodecs/test/test_tiff.cpp | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index a2b55ea2ff..0ae60e6947 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -312,6 +312,18 @@ bool TiffDecoder::readHeader() result = true; break; } + case 4: + //support 4-bit palette. + if (photometric == PHOTOMETRIC_PALETTE) + { + CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_UINT || sample_format == SAMPLEFORMAT_INT, ""); + int depth = sample_format == SAMPLEFORMAT_INT ? CV_8S : CV_8U; + m_type = CV_MAKETYPE(depth, 3); + result = true; + } + else + CV_Error(cv::Error::StsError, "bitsperpixel value is 4 should be palette."); + break; case 8: { //Palette color, the value of the component is used as an index into the red, diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index b7b6b95d83..76a39c0fae 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -359,6 +359,16 @@ TEST(Imgcodecs_Tiff, read_palette_color_image) ASSERT_EQ(CV_8UC3, img.type()); } +TEST(Imgcodecs_Tiff, read_4_bit_palette_color_image) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/4-bit_palette_color.tif"; + + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC3, img.type()); +} + TEST(Imgcodecs_Tiff, readWrite_predictor) { /* see issue #21871 From 2959286eb552d785bc29049c9b33f6191bfc77ab Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Mon, 29 Aug 2022 02:51:26 +0000 Subject: [PATCH 041/313] tengine: supports conv with asymmetric padding --- modules/dnn/src/layers/convolution_layer.cpp | 24 ++++++++------- .../include/tengine_graph_convolution.hpp | 8 +++-- .../src/tengine_graph_convolution.cpp | 29 +++++++++---------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 6334b40538..ff135fe41b 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -2027,7 +2027,7 @@ public: } #ifdef HAVE_TENGINE - bool tengine_ret = false; ; + bool tengine_ret = false; std::vector teng_in, teng_out; inputs_arr.getMatVector(teng_in); @@ -2052,20 +2052,24 @@ public: /* tengine_init will run when first time. */ if(NULL == tengine_graph) { + // pads_begin: 0 - pad_top, 1 - pad_left + // pads_end: 0 - pad_bottom, 1 - pad_right + // pad_h0: pad_top, pad_h1: pad_bottom + // pad_w0: pad_left, pad_w1: pad_right tengine_graph = tengine_init(name.c_str(), input_, inch, ngroups, in_h, in_w, output_, out_b, outch, out_h, out_w, kernel_, kernel_size.size(), kernel.height, kernel.width, teg_bias, stride.height, stride.width, - pad.height, pad.width, dilation.height, dilation.width, + pads_begin[0], pads_end[0], pads_begin[1], pads_end[1], dilation.height, dilation.width, weightsMat.step1(), padMode, tengine_graph, nstripes); - /*printf("Init(%s): input=%p(%d %d %d %d ),output=%p(%d %d %d %d ),kernel=%p(%ld %d %d ), bias=%p ," - "stride(%d %d), pad(%d %d), dilation(%d %d) ,weightsMat=%ld, padMode=%s ,tengine_graph = %p \n", - name.c_str(),input_, inch, ngroups, in_h, in_w, - output_, out_b, outch, out_h, out_w, - kernel_, kernel_size.size(), kernel.height, kernel.width, - teg_bias, stride.height, stride.width, - pad.height, pad.width, dilation.height, dilation.width, - weightsMat.step1(), padMode.c_str() ,tengine_graph);*/ + // printf("Init(%s): input=%p(%d %d %d %d ),output=%p(%d %d %d %d ),kernel=%p(%ld %d %d ), bias=%p ," + // "stride(%d %d), pad(%d %d %d %d), dilation(%d %d) ,weightsMat=%ld, padMode=%s ,tengine_graph = %p \n", + // name.c_str(),input_, inch, ngroups, in_h, in_w, + // output_, out_b, outch, out_h, out_w, + // kernel_, kernel_size.size(), kernel.height, kernel.width, + // teg_bias, stride.height, stride.width, + // pads_begin[0], pads_end[0], pads_begin[1], pads_end[1], dilation.height, dilation.width, + // weightsMat.step1(), padMode.c_str() ,tengine_graph); } if(NULL != tengine_graph) { diff --git a/modules/dnn/src/tengine4dnn/include/tengine_graph_convolution.hpp b/modules/dnn/src/tengine4dnn/include/tengine_graph_convolution.hpp index c6b0495ab5..8ec99c9685 100644 --- a/modules/dnn/src/tengine4dnn/include/tengine_graph_convolution.hpp +++ b/modules/dnn/src/tengine4dnn/include/tengine_graph_convolution.hpp @@ -34,11 +34,15 @@ namespace cv { namespace dnn { +// pad_h0: pad_top +// pad_h1: pad_bottom +// pad_w0: pad_left +// pad_w1: pad_right teng_graph_t tengine_init(const char* name , float* input_, int inch, int group, int in_h, int in_w, float *output_, int out_b, int outch, int out_h, int out_w, float *kernel_,int kernel_s , int kernel_h, int kernel_w, - float *teg_bias, int stride_h,int stride_w, - int pad_h, int pad_w, int dilation_h, int dilation_w, + float *teg_bias, int stride_h, int stride_w, + int pad_h0, int pad_h1, int pad_w0, int pad_w1, int dilation_h, int dilation_w, size_t wstep, const std::string padMode , teng_graph_t& graph, int nstripes) ; bool tengine_forward(teng_graph_t& graph) ; diff --git a/modules/dnn/src/tengine4dnn/src/tengine_graph_convolution.cpp b/modules/dnn/src/tengine4dnn/src/tengine_graph_convolution.cpp index daadc32ad2..d35937006c 100644 --- a/modules/dnn/src/tengine4dnn/src/tengine_graph_convolution.cpp +++ b/modules/dnn/src/tengine4dnn/src/tengine_graph_convolution.cpp @@ -56,7 +56,7 @@ static int create_input_node(teng_graph_t graph, const char* node_name, int inch } static int create_conv_node(teng_graph_t graph, const char* node_name, const char* input_name, int in_h, int in_w, int out_h, int out_w, - int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, int pad_w, int inch, int outch, int group, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h0, int pad_h1, int pad_w0, int pad_w1, int inch, int outch, int group, int dilation_h, int dilation_w, int activation, std::string padMode) { node_t conv_node = teng_create_graph_node(graph, node_name, "Convolution"); @@ -107,15 +107,12 @@ static int create_conv_node(teng_graph_t graph, const char* node_name, const cha teng_release_graph_node(b_node); teng_release_graph_tensor(b_tensor); - int pad_h1 = pad_h; - int pad_w1 = pad_w; - if (!padMode.empty()) { if (padMode == "SAME") { - int out_h_temp = (in_h-kernel_h + 2*pad_h)/stride_h + 1; - int out_w_temp = (in_w-kernel_w + 2*pad_w)/stride_w + 1; + int out_h_temp = (in_h-kernel_h + 2*pad_h0)/stride_h + 1; + int out_w_temp = (in_w-kernel_w + 2*pad_w0)/stride_w + 1; if (out_h_temp < out_h) pad_h1 += 1; @@ -129,8 +126,8 @@ static int create_conv_node(teng_graph_t graph, const char* node_name, const cha teng_set_node_attr_int(conv_node, "kernel_w", &kernel_w); teng_set_node_attr_int(conv_node, "stride_h", &stride_h); teng_set_node_attr_int(conv_node, "stride_w", &stride_w); - teng_set_node_attr_int(conv_node, "pad_h0", &pad_h); - teng_set_node_attr_int(conv_node, "pad_w0", &pad_w); + teng_set_node_attr_int(conv_node, "pad_h0", &pad_h0); + teng_set_node_attr_int(conv_node, "pad_w0", &pad_w0); teng_set_node_attr_int(conv_node, "pad_h1", &pad_h1); teng_set_node_attr_int(conv_node, "pad_w1", &pad_w1); teng_set_node_attr_int(conv_node, "output_channel", &outch); @@ -149,7 +146,7 @@ static teng_graph_t create_conv_graph(const char* layer_name, float* input_data, float* output_data, int outch, int out_h, int out_w, int kernel_h, int kernel_w, int stride_h,int stride_w, - int pad_h, int pad_w, int dilation_h, int dilation_w, int activation, + int pad_h0, int pad_h1, int pad_w0, int pad_w1, int dilation_h, int dilation_w, int activation, float* teg_weight, float* teg_bias, std::string padMode, int nstripes) { node_t conv_node = NULL; @@ -188,7 +185,7 @@ static teng_graph_t create_conv_graph(const char* layer_name, float* input_data, } if (ok && create_conv_node(graph, conv_name, input_name, in_h, in_w, out_h, out_w, kernel_h, kernel_w, - stride_h, stride_w, pad_h, pad_w, inch, outch, group, dilation_h, dilation_w, activation, padMode) < 0) + stride_h, stride_w, pad_h0, pad_h1, pad_w0, pad_w1, inch, outch, group, dilation_h, dilation_w, activation, padMode) < 0) { CV_LOG_WARNING(NULL,"Tengine: create conv node failed." ); ok = false; @@ -289,8 +286,8 @@ static bool tengine_init_flag = false; teng_graph_t tengine_init(const char* layer_name, float* input_, int inch, int group, int in_h, int in_w, float *output_, int out_b, int outch, int out_h, int out_w, float *kernel_, int kernel_s ,int kernel_h, int kernel_w, - float *teg_bias, int stride_h,int stride_w, - int pad_h, int pad_w, int dilation_h, int dilation_w, + float *teg_bias, int stride_h, int stride_w, + int pad_h0, int pad_h1, int pad_w0, int pad_w1, int dilation_h, int dilation_w, size_t wstep, const std::string padMode, teng_graph_t &graph, int nstripes) { std::vector teg_weight_vec; @@ -299,9 +296,9 @@ teng_graph_t tengine_init(const char* layer_name, float* input_, int inch, int g // Do not using the activation fuse mode, just convolution only. int activation = -1; - if (!(kernel_s == 2 && kernel_h == kernel_w && pad_h == pad_w + if (!(kernel_s == 2 && kernel_h == kernel_w && dilation_h == dilation_w && stride_h == stride_w - && out_b == 1 && pad_h < 10)) // just for Conv2D + && out_b == 1 && pad_h0 < 10 && pad_h1 < 10 && pad_w0 < 10 && pad_w1 < 10)) // just for Conv2D { // printf("return : just for Conv2D\n"); return NULL; @@ -314,7 +311,7 @@ teng_graph_t tengine_init(const char* layer_name, float* input_, int inch, int g kernel_w, kernel_h, stride_w, stride_h, dilation_w, dilation_h, - pad_w, pad_h); + pad_h0, pad_h1, pad_w0, pad_w1); */ // weight if (kernel_inwh != wstep) @@ -342,7 +339,7 @@ teng_graph_t tengine_init(const char* layer_name, float* input_, int inch, int g graph = create_conv_graph(layer_name, input_, inch, group, in_h, in_w, output_, outch, out_h, out_w, kernel_h, kernel_w, stride_h,stride_w, - pad_h, pad_w, dilation_h, dilation_w, activation, + pad_h0, pad_h1, pad_w0, pad_w1, dilation_h, dilation_w, activation, teg_weight, teg_bias, padMode, nstripes); if(NULL == graph ) { From 2cd7e17b65b6bbff7541583d360ae3970d7e0a40 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Mon, 29 Aug 2022 17:15:35 +0800 Subject: [PATCH 042/313] replace v_add with + --- .../fast_convolution/fast_convolution.cpp | 13 ++--- .../fast_convolution.simd.hpp | 50 +++++++++---------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index 2af836339d..d62b6f230c 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -502,10 +502,11 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr { for (; j + 7 < out_width; j += 8) { - v_float32x4 v0 = v_add(v_load(cptr + j), vbias); - v_float32x4 v1 = v_add(v_load(cptr + j + 4), vbias); - v0 = v_add(v0, v_load(pbptr + j)); - v1 = v_add(v1, v_load(pbptr + j + 4)); + v_float32x4 v0 = v_load(cptr + j) + vbias; + v_float32x4 v1 = v_load(cptr + j + 4) + vbias; + + v0 += v_load(pbptr + j); + v1 += v_load(pbptr + j + 4); if (ifMinMaxAct) { @@ -521,8 +522,8 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr { for (; j + 7 < out_width; j += 8) { - v_float32x4 v0 = v_add(v_load(cptr + j), vbias); - v_float32x4 v1 = v_add(v_load(cptr + j + 4), vbias); + v_float32x4 v0 = v_load(cptr + j) + vbias; + v_float32x4 v1 = v_load(cptr + j + 4) + vbias; if (ifMinMaxAct) { diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp index 2e088a660b..7325cc375e 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp @@ -13,7 +13,7 @@ namespace dnn { void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { -#if 0 // CV_SIMD128 && CONV_MR == 4 && CONV_NR == 24 +#if CV_SIMD128 && CONV_MR == 4 && CONV_NR == 24 v_float32x4 c0 = v_setzero_f32(), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0; v_float32x4 c6 = v_setzero_f32(), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6; v_float32x4 c12 = v_setzero_f32(), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12; @@ -59,33 +59,33 @@ void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool i if (!init_c) { - c0 = v_add(c0, v_load(c)); - c1 = v_add(c1, v_load(c + 4)); - c2 = v_add(c2, v_load(c + 8)); - c3 = v_add(c3, v_load(c + 12)); - c4 = v_add(c4, v_load(c + 16)); - c5 = v_add(c5, v_load(c + 20)); + c0 += v_load(c); + c1 += v_load(c + 4); + c2 += v_load(c + 8); + c3 += v_load(c + 12); + c4 += v_load(c + 16); + c5 += v_load(c + 20); - c6 = v_add(c6 , v_load(c + ldc)); - c7 = v_add(c7 , v_load(c + ldc + 4)); - c8 = v_add(c8 , v_load(c + ldc + 8)); - c9 = v_add(c9 , v_load(c + ldc + 12)); - c10 = v_add(c10, v_load(c + ldc + 16)); - c11 = v_add(c11, v_load(c + ldc + 20)); + c6 += v_load(c + ldc); + c7 += v_load(c + ldc + 4); + c8 += v_load(c + ldc + 8); + c9 += v_load(c + ldc + 12); + c10 += v_load(c + ldc + 16); + c11 += v_load(c + ldc + 20); - c12 = v_add(c12, v_load(c + ldc*2)); - c13 = v_add(c13, v_load(c + ldc*2 + 4)); - c14 = v_add(c14, v_load(c + ldc*2 + 8)); - c15 = v_add(c15, v_load(c + ldc*2 + 12)); - c16 = v_add(c16, v_load(c + ldc*2 + 16)); - c17 = v_add(c17, v_load(c + ldc*2 + 20)); + c12 += v_load(c + ldc*2); + c13 += v_load(c + ldc*2 + 4); + c14 += v_load(c + ldc*2 + 8); + c15 += v_load(c + ldc*2 + 12); + c16 += v_load(c + ldc*2 + 16); + c17 += v_load(c + ldc*2 + 20); - c18 = v_add(c18, v_load(c + ldc*3)); - c19 = v_add(c19, v_load(c + ldc*3 + 4)); - c20 = v_add(c20, v_load(c + ldc*3 + 8)); - c21 = v_add(c21, v_load(c + ldc*3 + 12)); - c22 = v_add(c22, v_load(c + ldc*3 + 16)); - c23 = v_add(c23, v_load(c + ldc*3 + 20)); + c18 += v_load(c + ldc*3); + c19 += v_load(c + ldc*3 + 4); + c20 += v_load(c + ldc*3 + 8); + c21 += v_load(c + ldc*3 + 12); + c22 += v_load(c + ldc*3 + 16); + c23 += v_load(c + ldc*3 + 20); } v_store(c, c0); From 837e41f9a7acad9e300ba2693bfe8a3eda22c639 Mon Sep 17 00:00:00 2001 From: catree Date: Mon, 29 Aug 2022 23:55:00 +0200 Subject: [PATCH 043/313] Add -ws flag for cornerSubPix params (backported from 4.x). Add flags to set the camera intrinsic parameters as an initial guess (can allow converging to the correct camera intrinsic parameters). Add -imshow-scale flag to resize the image when displaying the results. Add -enable-k3 flag to enable or disable the estimation of the K3 distortion coefficient. --- samples/cpp/calibration.cpp | 86 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/samples/cpp/calibration.cpp b/samples/cpp/calibration.cpp index e827e4c26b..5d67600239 100644 --- a/samples/cpp/calibration.cpp +++ b/samples/cpp/calibration.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace cv; using namespace std; @@ -37,9 +38,6 @@ const char * usage = "\n" "\n"; - - - const char* liveCaptureHelp = "When the live video from camera is used as input, the following hot-keys may be used:\n" " , 'q' - quit the program\n" @@ -58,17 +56,24 @@ static void help(char** argv) " # of board views actually available)\n" " [-d=] # a minimum delay in ms between subsequent attempts to capture a next view\n" " # (used only for video capturing)\n" - " [-s=] # square size in some user-defined units (1 by default)\n" + " [-s=] # square size in some user-defined units (1 by default)\n" " [-o=] # the output filename for intrinsic [and extrinsic] parameters\n" " [-op] # write detected feature points\n" " [-oe] # write extrinsic parameters\n" " [-zt] # assume zero tangential distortion\n" - " [-a=] # fix aspect ratio (fx/fy)\n" + " [-a=] # fix aspect ratio (fx/fy)\n" " [-p] # fix the principal point at the center\n" " [-v] # flip the captured images around the horizontal axis\n" " [-V] # use a video file, and not an image list, uses\n" " # [input_data] string for the video file name\n" " [-su] # show undistorted images after calibration\n" + " [-ws=] # half of search window for cornerSubPix (11 by default)\n" + " [-fx=] # focal length in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-fy=] # focal length in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-cx=] # camera center point in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-cy=] # camera center point in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-imshow-scale # image resize scaling factor when displaying the results (must be >= 1)\n" + " [-enable-k3=<0/1> # to enable (1) or disable (0) K3 coefficient for the distortion model\n" " [input_data] # input data, one of the following:\n" " # - text file with a list of the images of the board\n" " # the text file can be generated with imagelist_creator\n" @@ -142,7 +147,6 @@ static bool runCalibration( vector > imagePoints, vector& reprojErrs, double& totalAvgErr) { - cameraMatrix = Mat::eye(3, 3, CV_64F); if( flags & CALIB_FIX_ASPECT_RATIO ) cameraMatrix.at(0,0) = aspectRatio; @@ -154,8 +158,7 @@ static bool runCalibration( vector > imagePoints, objectPoints.resize(imagePoints.size(),objectPoints[0]); double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, - distCoeffs, rvecs, tvecs, flags|CALIB_FIX_K4|CALIB_FIX_K5); - ///*|CALIB_FIX_K3*/|CALIB_FIX_K4|CALIB_FIX_K5); + distCoeffs, rvecs, tvecs, flags | CALIB_USE_LU); printf("RMS error reported by calibrateCamera: %g\n", rms); bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); @@ -166,7 +169,6 @@ static bool runCalibration( vector > imagePoints, return ok; } - static void saveCameraParams( const string& filename, Size imageSize, Size boardSize, float squareSize, float aspectRatio, int flags, @@ -199,7 +201,7 @@ static void saveCameraParams( const string& filename, if( flags != 0 ) { - sprintf( buf, "flags: %s%s%s%s", + snprintf( buf, sizeof(buf), "flags: %s%s%s%s", flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "", flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "", flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "", @@ -296,7 +298,7 @@ static bool runAndSave(const string& outputFilename, bool ok = runCalibration(imagePoints, imageSize, boardSize, patternType, squareSize, aspectRatio, flags, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, totalAvgErr); - printf("%s. avg reprojection error = %.2f\n", + printf("%s. avg reprojection error = %.7f\n", ok ? "Calibration succeeded" : "Calibration failed", totalAvgErr); @@ -312,11 +314,10 @@ static bool runAndSave(const string& outputFilename, return ok; } - int main( int argc, char** argv ) { Size boardSize, imageSize; - float squareSize, aspectRatio; + float squareSize, aspectRatio = 1; Mat cameraMatrix, distCoeffs; string outputFilename; string inputFilename = ""; @@ -339,7 +340,10 @@ int main( int argc, char** argv ) cv::CommandLineParser parser(argc, argv, "{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{o|out_camera_data.yml|}" - "{op||}{oe||}{zt||}{a|1|}{p||}{v||}{V||}{su||}" + "{op||}{oe||}{zt||}{a||}{p||}{v||}{V||}{su||}" + "{ws|11|}" + "{fx||}{fy||}{cx||}{cy||}" + "{imshow-scale|1|}{enable-k3|1|}" "{@input_data|0|}"); if (parser.has("help")) { @@ -362,12 +366,13 @@ int main( int argc, char** argv ) } squareSize = parser.get("s"); nframes = parser.get("n"); - aspectRatio = parser.get("a"); delay = parser.get("d"); writePoints = parser.has("op"); writeExtrinsics = parser.has("oe"); - if (parser.has("a")) + if (parser.has("a")) { flags |= CALIB_FIX_ASPECT_RATIO; + aspectRatio = parser.get("a"); + } if ( parser.has("zt") ) flags |= CALIB_ZERO_TANGENT_DIST; if ( parser.has("p") ) @@ -381,6 +386,24 @@ int main( int argc, char** argv ) cameraId = parser.get("@input_data"); else inputFilename = parser.get("@input_data"); + int winSize = parser.get("ws"); + cameraMatrix = Mat::eye(3, 3, CV_64F); + if (parser.has("fx") && parser.has("fy") && parser.has("cx") && parser.has("cy")) + { + cameraMatrix.at(0,0) = parser.get("fx"); + cameraMatrix.at(0,2) = parser.get("cx"); + cameraMatrix.at(1,1) = parser.get("fy"); + cameraMatrix.at(1,2) = parser.get("cy"); + flags |= CALIB_USE_INTRINSIC_GUESS; + std::cout << "Use the following camera matrix as an initial guess:\n" << cameraMatrix << std::endl; + } + int viewScaleFactor = parser.get("imshow-scale"); + bool useK3 = parser.get("enable-k3"); + std::cout << "Use K3 distortion coefficient? " << useK3 << std::endl; + if (!useK3) + { + flags |= CALIB_FIX_K3; + } if (!parser.check()) { help(argv); @@ -471,8 +494,8 @@ int main( int argc, char** argv ) } // improve the found corners' coordinate accuracy - if( pattern == CHESSBOARD && found) cornerSubPix( viewGray, pointbuf, Size(11,11), - Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 )); + if( pattern == CHESSBOARD && found) cornerSubPix( viewGray, pointbuf, Size(winSize,winSize), + Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.0001 )); if( mode == CAPTURING && found && (!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC) ) @@ -494,9 +517,9 @@ int main( int argc, char** argv ) if( mode == CAPTURING ) { if(undistortImage) - msg = format( "%d/%d Undist", (int)imagePoints.size(), nframes ); + msg = cv::format( "%d/%d Undist", (int)imagePoints.size(), nframes ); else - msg = format( "%d/%d", (int)imagePoints.size(), nframes ); + msg = cv::format( "%d/%d", (int)imagePoints.size(), nframes ); } putText( view, msg, textOrigin, 1, 1, @@ -510,8 +533,17 @@ int main( int argc, char** argv ) Mat temp = view.clone(); undistort(temp, view, cameraMatrix, distCoeffs); } + if (viewScaleFactor > 1) + { + Mat viewScale; + resize(view, viewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA); + imshow("Image View", viewScale); + } + else + { + imshow("Image View", view); + } - imshow("Image View", view); char key = (char)waitKey(capture.isOpened() ? 50 : 500); if( key == 27 ) @@ -552,9 +584,17 @@ int main( int argc, char** argv ) view = imread(imageList[i], 1); if(view.empty()) continue; - //undistort( view, rview, cameraMatrix, distCoeffs, cameraMatrix ); remap(view, rview, map1, map2, INTER_LINEAR); - imshow("Image View", rview); + if (viewScaleFactor > 1) + { + Mat rviewScale; + resize(rview, rviewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA); + imshow("Image View", rviewScale); + } + else + { + imshow("Image View", rview); + } char c = (char)waitKey(); if( c == 27 || c == 'q' || c == 'Q' ) break; From 1852d0b9b8d38c1d7c08127363b325b1a969a19f Mon Sep 17 00:00:00 2001 From: catree Date: Tue, 30 Aug 2022 00:45:47 +0200 Subject: [PATCH 044/313] Add flags to set the camera intrinsic parameters as an initial guess (can allow converging to the correct camera intrinsic parameters). Add -imshow-scale flag to resize the image when displaying the results. Add -enable-k3 flag to enable or disable the estimation of the K3 distortion coefficient. --- samples/cpp/calibration.cpp | 62 +++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/samples/cpp/calibration.cpp b/samples/cpp/calibration.cpp index 4f2bb8b8b5..1e8e149940 100644 --- a/samples/cpp/calibration.cpp +++ b/samples/cpp/calibration.cpp @@ -38,9 +38,6 @@ const char * usage = "\n" "\n"; - - - const char* liveCaptureHelp = "When the live video from camera is used as input, the following hot-keys may be used:\n" " , 'q' - quit the program\n" @@ -59,19 +56,25 @@ static void help(char** argv) " # of board views actually available)\n" " [-d=] # a minimum delay in ms between subsequent attempts to capture a next view\n" " # (used only for video capturing)\n" - " [-s=] # square size in some user-defined units (1 by default)\n" + " [-s=] # square size in some user-defined units (1 by default)\n" " [-o=] # the output filename for intrinsic [and extrinsic] parameters\n" " [-op] # write detected feature points\n" " [-oe] # write extrinsic parameters\n" " [-oo] # write refined 3D object points\n" " [-zt] # assume zero tangential distortion\n" - " [-a=] # fix aspect ratio (fx/fy)\n" + " [-a=] # fix aspect ratio (fx/fy)\n" " [-p] # fix the principal point at the center\n" " [-v] # flip the captured images around the horizontal axis\n" " [-V] # use a video file, and not an image list, uses\n" " # [input_data] string for the video file name\n" " [-su] # show undistorted images after calibration\n" - " [-ws=] # Half of search window for cornerSubPix (11 by default)\n" + " [-ws=] # half of search window for cornerSubPix (11 by default)\n" + " [-fx=] # focal length in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-fy=] # focal length in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-cx=] # camera center point in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-cy=] # camera center point in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n" + " [-imshow-scale # image resize scaling factor when displaying the results (must be >= 1)\n" + " [-enable-k3=<0/1> # to enable (1) or disable (0) K3 coefficient for the distortion model\n" " [-dt=] # actual distance between top-left and top-right corners of\n" " # the calibration grid. If this parameter is specified, a more\n" " # accurate calibration method will be used which may be better\n" @@ -151,7 +154,6 @@ static bool runCalibration( vector > imagePoints, vector& newObjPoints, double& totalAvgErr) { - cameraMatrix = Mat::eye(3, 3, CV_64F); if( flags & CALIB_FIX_ASPECT_RATIO ) cameraMatrix.at(0,0) = aspectRatio; @@ -170,7 +172,7 @@ static bool runCalibration( vector > imagePoints, iFixedPoint = boardSize.width - 1; rms = calibrateCameraRO(objectPoints, imagePoints, imageSize, iFixedPoint, cameraMatrix, distCoeffs, rvecs, tvecs, newObjPoints, - flags | CALIB_FIX_K3 | CALIB_USE_LU); + flags | CALIB_USE_LU); printf("RMS error reported by calibrateCamera: %g\n", rms); bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); @@ -191,7 +193,6 @@ static bool runCalibration( vector > imagePoints, return ok; } - static void saveCameraParams( const string& filename, Size imageSize, Size boardSize, float squareSize, float aspectRatio, int flags, @@ -346,7 +347,6 @@ static bool runAndSave(const string& outputFilename, return ok; } - int main( int argc, char** argv ) { Size boardSize, imageSize; @@ -375,6 +375,8 @@ int main( int argc, char** argv ) "{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{o|out_camera_data.yml|}" "{op||}{oe||}{zt||}{a||}{p||}{v||}{V||}{su||}" "{oo||}{ws|11|}{dt||}" + "{fx||}{fy||}{cx||}{cy||}" + "{imshow-scale|1|}{enable-k3|0|}" "{@input_data|0|}"); if (parser.has("help")) { @@ -419,6 +421,23 @@ int main( int argc, char** argv ) else inputFilename = parser.get("@input_data"); int winSize = parser.get("ws"); + cameraMatrix = Mat::eye(3, 3, CV_64F); + if (parser.has("fx") && parser.has("fy") && parser.has("cx") && parser.has("cy")) + { + cameraMatrix.at(0,0) = parser.get("fx"); + cameraMatrix.at(0,2) = parser.get("cx"); + cameraMatrix.at(1,1) = parser.get("fy"); + cameraMatrix.at(1,2) = parser.get("cy"); + flags |= CALIB_USE_INTRINSIC_GUESS; + std::cout << "Use the following camera matrix as an initial guess:\n" << cameraMatrix << std::endl; + } + int viewScaleFactor = parser.get("imshow-scale"); + bool useK3 = parser.get("enable-k3"); + std::cout << "Use K3 distortion coefficient? " << useK3 << std::endl; + if (!useK3) + { + flags |= CALIB_FIX_K3; + } float grid_width = squareSize * (boardSize.width - 1); bool release_object = false; if (parser.has("dt")) { @@ -554,8 +573,17 @@ int main( int argc, char** argv ) Mat temp = view.clone(); undistort(temp, view, cameraMatrix, distCoeffs); } + if (viewScaleFactor > 1) + { + Mat viewScale; + resize(view, viewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA); + imshow("Image View", viewScale); + } + else + { + imshow("Image View", view); + } - imshow("Image View", view); char key = (char)waitKey(capture.isOpened() ? 50 : 500); if( key == 27 ) @@ -596,9 +624,17 @@ int main( int argc, char** argv ) view = imread(imageList[i], 1); if(view.empty()) continue; - //undistort( view, rview, cameraMatrix, distCoeffs, cameraMatrix ); remap(view, rview, map1, map2, INTER_LINEAR); - imshow("Image View", rview); + if (viewScaleFactor > 1) + { + Mat rviewScale; + resize(rview, rviewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA); + imshow("Image View", rviewScale); + } + else + { + imshow("Image View", rview); + } char c = (char)waitKey(); if( c == 27 || c == 'q' || c == 'Q' ) break; From 2d837efba7f54a91a08b7997cf68dca126f18481 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Tue, 30 Aug 2022 09:50:29 +0800 Subject: [PATCH 045/313] add qgemm and squeeze op13 supported on ONNXImporter --- modules/dnn/src/onnx/onnx_importer.cpp | 110 ++++++++++++++++++++++-- modules/dnn/test/test_onnx_importer.cpp | 6 ++ 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index e99fbb319e..4814645858 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -192,6 +192,7 @@ private: void parseQSigmoid (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseQAvgPool (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseQConcat (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseQGemm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); // '???' domain or '???' layer type void parseCustomLayer (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); @@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N void ONNXImporter::parseSqueeze(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - CV_Assert_N(node_proto.input_size() == 1, layerParams.has("axes")); - DictValue axes_dict = layerParams.get("axes"); - MatShape inpShape = outShapes[node_proto.input(0)]; + CV_Assert(node_proto.input_size() <= 2); + MatShape inpShape = outShapes[node_proto.input(0)]; std::vector maskedAxes(inpShape.size(), false); - for (int i = 0; i < axes_dict.size(); ++i) + if (layerParams.has("axes")) { - int axis = axes_dict.getIntValue(i); - CV_CheckLE(axis, static_cast(inpShape.size()), "Squeeze axis"); - maskedAxes[axis] = inpShape[axis] == 1; + DictValue axes_dict = layerParams.get("axes"); + for (int i = 0; i < axes_dict.size(); ++i) + { + int axis = axes_dict.getIntValue(i); + CV_CheckLE(axis, static_cast(inpShape.size()), "Squeeze axis"); + maskedAxes[axis] = inpShape[axis] == 1; + } } + else if (node_proto.input_size() == 2) + { + if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) + { + Mat axesMat = getBlob(node_proto, 1); + if (axesMat.depth() == CV_32F) + axesMat.convertTo(axesMat, CV_32S); + size_t axesLen = axesMat.total(); + for (int i = 0; i < axesLen; i++) + { + int axis = axesMat.at(i); + CV_CheckLE(axis, static_cast(inpShape.size()), "Squeeze axis"); + maskedAxes[axis] = inpShape[axis] == 1; + } + } + else + CV_Error(Error::StsNotImplemented, cv::format("ONNX/Squeeze: doesn't support non-constant 'axes' input")); + } + MatShape outShape; for (int i = 0; i < inpShape.size(); ++i) { @@ -3260,6 +3283,78 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod addLayer(layerParams, node_proto); } +// A * B + C = Y, we require that the dimension of A is [m, k], and the dimension of B is [n, k]. +// And the dim of output Y is [m, n] +void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + int ninputs = node_proto.input_size(); + CV_Assert(ninputs == 8 || ninputs == 9); + + layerParams.type = "InnerProductInt8"; + + if (constBlobs.find(node_proto.input(3)) == constBlobs.end()) + CV_Error(Error::StsNotImplemented, "Variable weights is not supported"); + + Mat weights = getBlob(node_proto, 3); + + if (!layerParams.get("transB", 0)) + { + transpose(weights, weights); + } + + CV_Assert(layerParams.get("alpha", 1) == 1.0f); + CV_Assert(layerParams.get("transA", 0) == 0); + + int firstInpDims = outShapes[node_proto.input(0)].size(); + + Mat inp_sc = getBlob(node_proto, 1); + Mat inp_zp = getBlob(node_proto, 2); + + int outCn = weights.size[0]; + int secondInpDims = weights.dims; + + Mat w_scale = getBlob(node_proto, 4); + CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn); + bool per_channel = w_scale.total() == outCn; + Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at(0))); + + Mat w_zp = getBlob(node_proto, 5); + int8_t* ptrZp = w_zp.ptr(0); + + for (int i = 0; i < w_zp.total(); i++) + { + if (ptrZp[i] != (int8_t)0) + CV_Error(Error::StsUnsupportedFormat, "The zero-point non-zero case of W is not supported!"); + } + + Mat out_sc, bias; + out_sc = getBlob(node_proto, 7); + if (constBlobs.find(node_proto.input(6)) != constBlobs.end()) + bias = getBlob(node_proto, 6); + else + bias = Mat::zeros(1, outCn, CV_32S); + + Mat biasFused(1, outCn, CV_32S); + Mat outputMultiplier(1, outCn, CV_32F); + for (int i = 0; i < outCn; i++) + { + biasFused.at(i) = bias.at(i) - inp_zp.at(0)*(cv::sum(weights.row(i))[0]); + outputMultiplier.at(i) = (inp_sc.at(0) * wt_sc.at(i)) / out_sc.at(0); + } + + layerParams.type = "InnerProductInt8"; + layerParams.set("num_output", outCn); + layerParams.set("axis", firstInpDims - secondInpDims + 1); + layerParams.set("input_scale", inp_sc.at(0)); + layerParams.set("input_zeropoint", inp_zp.at(0)); + layerParams.set("per_channel", per_channel); + + layerParams.blobs.push_back(weights); + layerParams.blobs.push_back(biasFused); + layerParams.blobs.push_back(outputMultiplier); + addLayer(layerParams, node_proto); +} + void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { opencv_onnx::NodeProto node_proto = node_proto_; @@ -3654,6 +3749,7 @@ void ONNXImporter::buildDispatchMap_COM_MICROSOFT(int opset_version) dispatch["QLinearLeakyRelu"] = &ONNXImporter::parseQLeakyRelu; dispatch["QLinearSigmoid"] = &ONNXImporter::parseQSigmoid; dispatch["QLinearConcat"] = &ONNXImporter::parseQConcat; + dispatch["QGemm"] = &ONNXImporter::parseQGemm; domain_dispatch_map["com.microsoft"] = dispatch; } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 578e0442b2..2e68b77226 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1083,6 +1083,7 @@ TEST_P(Test_ONNX_layers, Squeeze) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); testONNXModels("squeeze"); + testONNXModels("squeeze_axes_op13"); } TEST_P(Test_ONNX_layers, ReduceL2) @@ -1775,6 +1776,11 @@ TEST_P(Test_ONNX_layers, Quantized_MatMul) testONNXModels("quantized_matmul_per_channel_weights", npy, 0.06, 0.22); } +TEST_P(Test_ONNX_layers, Quantized_Gemm) +{ + testONNXModels("quantized_gemm", npy); +} + TEST_P(Test_ONNX_layers, Quantized_MatMul_Variable_Weights) { // Unsupported From a31fb88fd08d9d55ab8deff8257633d094c6e538 Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Tue, 30 Aug 2022 22:48:29 +0300 Subject: [PATCH 046/313] G-API: Introduce GAbstractExecutor GExecutor is now a subclass of GAbstractExecutor. Other to come --- modules/gapi/CMakeLists.txt | 1 + modules/gapi/src/compiler/gcompiled.cpp | 3 +- modules/gapi/src/compiler/gcompiled_priv.hpp | 6 +- .../gapi/src/executor/gabstractexecutor.cpp | 25 ++++++ .../gapi/src/executor/gabstractexecutor.hpp | 80 +++++++++++++++++++ modules/gapi/src/executor/gexecutor.cpp | 11 +-- modules/gapi/src/executor/gexecutor.hpp | 54 ++----------- 7 files changed, 118 insertions(+), 62 deletions(-) create mode 100644 modules/gapi/src/executor/gabstractexecutor.cpp create mode 100644 modules/gapi/src/executor/gabstractexecutor.hpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 63373ad9c4..962256cc88 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -113,6 +113,7 @@ set(gapi_srcs src/compiler/passes/intrin.cpp # Executor + src/executor/gabstractexecutor.cpp src/executor/gexecutor.cpp src/executor/gtbbexecutor.cpp src/executor/gstreamingexecutor.cpp diff --git a/modules/gapi/src/compiler/gcompiled.cpp b/modules/gapi/src/compiler/gcompiled.cpp index 263878ce0d..df4411e832 100644 --- a/modules/gapi/src/compiler/gcompiled.cpp +++ b/modules/gapi/src/compiler/gcompiled.cpp @@ -14,11 +14,12 @@ #include "compiler/gcompiled_priv.hpp" #include "backends/common/gbackend.hpp" +#include "executor/gexecutor.hpp" // GCompiled private implementation //////////////////////////////////////////// void cv::GCompiled::Priv::setup(const GMetaArgs &_metaArgs, const GMetaArgs &_outMetas, - std::unique_ptr &&_pE) + std::unique_ptr &&_pE) { m_metas = _metaArgs; m_outMetas = _outMetas; diff --git a/modules/gapi/src/compiler/gcompiled_priv.hpp b/modules/gapi/src/compiler/gcompiled_priv.hpp index f21bfc80bc..3f873aba23 100644 --- a/modules/gapi/src/compiler/gcompiled_priv.hpp +++ b/modules/gapi/src/compiler/gcompiled_priv.hpp @@ -12,7 +12,7 @@ #include "opencv2/gapi/util/optional.hpp" #include "compiler/gmodel.hpp" -#include "executor/gexecutor.hpp" +#include "executor/gabstractexecutor.hpp" // NB: BTW, GCompiled is the only "public API" class which // private part (implementation) is hosted in the "compiler/" module. @@ -36,14 +36,14 @@ class GAPI_EXPORTS GCompiled::Priv // If we want to go autonomous, we might to do something with this. GMetaArgs m_metas; // passed by user GMetaArgs m_outMetas; // inferred by compiler - std::unique_ptr m_exec; + std::unique_ptr m_exec; void checkArgs(const cv::gimpl::GRuntimeArgs &args) const; public: void setup(const GMetaArgs &metaArgs, const GMetaArgs &outMetas, - std::unique_ptr &&pE); + std::unique_ptr &&pE); bool isEmpty() const; bool canReshape() const; diff --git a/modules/gapi/src/executor/gabstractexecutor.cpp b/modules/gapi/src/executor/gabstractexecutor.cpp new file mode 100644 index 0000000000..e22b2eeb7c --- /dev/null +++ b/modules/gapi/src/executor/gabstractexecutor.cpp @@ -0,0 +1,25 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + +#include "precomp.hpp" + +#include + +#include "executor/gabstractexecutor.hpp" + +cv::gimpl::GAbstractExecutor::GAbstractExecutor(std::unique_ptr &&g_model) + : m_orig_graph(std::move(g_model)) + , m_island_graph(GModel::Graph(*m_orig_graph).metadata() + .get().model) + , m_gm(*m_orig_graph) + , m_gim(*m_island_graph) +{ +} + +const cv::gimpl::GModel::Graph& cv::gimpl::GAbstractExecutor::model() const +{ + return m_gm; +} diff --git a/modules/gapi/src/executor/gabstractexecutor.hpp b/modules/gapi/src/executor/gabstractexecutor.hpp new file mode 100644 index 0000000000..1657142021 --- /dev/null +++ b/modules/gapi/src/executor/gabstractexecutor.hpp @@ -0,0 +1,80 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + + +#ifndef OPENCV_GAPI_GABSTRACT_EXECUTOR_HPP +#define OPENCV_GAPI_GABSTRACT_EXECUTOR_HPP + +#include // unique_ptr, shared_ptr + +#include // tuple, required by magazine +#include // required by magazine + +#include + +#include "backends/common/gbackend.hpp" + +namespace cv { +namespace gimpl { + +// Graph-level executor interface. +// +// This class specifies API for a "super-executor" which orchestrates +// the overall Island graph execution. +// +// Every Island (subgraph) execution is delegated to a particular +// backend and is done opaquely to the GExecutor. +// +// Inputs to a GExecutor instance are: +// - GIslandModel - a high-level graph model which may be seen as a +// "procedure" to execute. +// - GModel - a low-level graph of operations (from which a GIslandModel +// is projected) +// - GComputation runtime arguments - vectors of input/output objects +// +// Every GExecutor is responsible for +// a. Maintaining non-island (intermediate) data objects within graph +// b. Providing GIslandExecutables with input/output data according to +// their protocols +// c. Triggering execution of GIslandExecutables when task/data dependencies +// are met. +// +// By default G-API stores all data on host, and cross-Island +// exchange happens via host buffers (and CV data objects). +// +// Today's exchange data objects are: +// - cv::Mat, cv::RMat - for image buffers +// - cv::Scalar - for single values (with up to four components inside) +// - cv::detail::VectorRef - an untyped wrapper over std::vector +// - cv::detail::OpaqueRef - an untyped wrapper over T +// - cv::MediaFrame - for image textures and surfaces (e.g. in planar format) + +class GAbstractExecutor +{ +protected: + std::unique_ptr m_orig_graph; + std::shared_ptr m_island_graph; + + cv::gimpl::GModel::Graph m_gm; // FIXME: make const? + cv::gimpl::GIslandModel::Graph m_gim; // FIXME: make const? + +public: + explicit GAbstractExecutor(std::unique_ptr &&g_model); + virtual ~GAbstractExecutor() = default; + virtual void run(cv::gimpl::GRuntimeArgs &&args) = 0; + + virtual bool canReshape() const = 0; + virtual void reshape(const GMetaArgs& inMetas, const GCompileArgs& args) = 0; + + virtual void prepareForNewStream() = 0; + + const GModel::Graph& model() const; // FIXME: make it ConstGraph? +}; + +} // namespace gimpl +} // namespace cv + +#endif // OPENCV_GAPI_GABSTRACT_EXECUTOR_HPP diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp index 472abaaa14..6853c30d3e 100644 --- a/modules/gapi/src/executor/gexecutor.cpp +++ b/modules/gapi/src/executor/gexecutor.cpp @@ -16,11 +16,7 @@ #include "compiler/passes/passes.hpp" cv::gimpl::GExecutor::GExecutor(std::unique_ptr &&g_model) - : m_orig_graph(std::move(g_model)) - , m_island_graph(GModel::Graph(*m_orig_graph).metadata() - .get().model) - , m_gm(*m_orig_graph) - , m_gim(*m_island_graph) + : GAbstractExecutor(std::move(g_model)) { // NB: Right now GIslandModel is acyclic, so for a naive execution, // simple unrolling to a list of triggers is enough @@ -424,11 +420,6 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) } } -const cv::gimpl::GModel::Graph& cv::gimpl::GExecutor::model() const -{ - return m_gm; -} - bool cv::gimpl::GExecutor::canReshape() const { // FIXME: Introduce proper reshaping support on GExecutor level diff --git a/modules/gapi/src/executor/gexecutor.hpp b/modules/gapi/src/executor/gexecutor.hpp index 5d797ce604..67182fc8cc 100644 --- a/modules/gapi/src/executor/gexecutor.hpp +++ b/modules/gapi/src/executor/gexecutor.hpp @@ -8,58 +8,18 @@ #ifndef OPENCV_GAPI_GEXECUTOR_HPP #define OPENCV_GAPI_GEXECUTOR_HPP -#include // unique_ptr, shared_ptr - #include // tuple, required by magazine #include // required by magazine -#include - -#include "backends/common/gbackend.hpp" +#include "executor/gabstractexecutor.hpp" namespace cv { namespace gimpl { -// Graph-level executor interface. -// -// This class specifies API for a "super-executor" which orchestrates -// the overall Island graph execution. -// -// Every Island (subgraph) execution is delegated to a particular -// backend and is done opaquely to the GExecutor. -// -// Inputs to a GExecutor instance are: -// - GIslandModel - a high-level graph model which may be seen as a -// "procedure" to execute. -// - GModel - a low-level graph of operations (from which a GIslandModel -// is projected) -// - GComputation runtime arguments - vectors of input/output objects -// -// Every GExecutor is responsible for -// a. Maintaining non-island (intermediate) data objects within graph -// b. Providing GIslandExecutables with input/output data according to -// their protocols -// c. Triggering execution of GIslandExecutables when task/data dependencies -// are met. -// -// By default G-API stores all data on host, and cross-Island -// exchange happens via host buffers (and CV data objects). -// -// Today's exchange data objects are: -// - cv::Mat - for image buffers -// - cv::Scalar - for single values (with up to four components inside) -// - cv::detail::VectorRef - an untyped wrapper over std::vector -// - -class GExecutor +class GExecutor final: public GAbstractExecutor { protected: Mag m_res; - std::unique_ptr m_orig_graph; - std::shared_ptr m_island_graph; - - cv::gimpl::GModel::Graph m_gm; // FIXME: make const? - cv::gimpl::GIslandModel::Graph m_gim; // FIXME: make const? // FIXME: Naive executor details are here for now // but then it should be moved to another place @@ -85,14 +45,12 @@ protected: public: explicit GExecutor(std::unique_ptr &&g_model); - void run(cv::gimpl::GRuntimeArgs &&args); + void run(cv::gimpl::GRuntimeArgs &&args) override; - bool canReshape() const; - void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); + bool canReshape() const override; + void reshape(const GMetaArgs& inMetas, const GCompileArgs& args) override; - void prepareForNewStream(); - - const GModel::Graph& model() const; // FIXME: make it ConstGraph? + void prepareForNewStream() override; }; } // namespace gimpl From a122f0f24802113a0f250f3b71e2b90ed9763bad Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Tue, 30 Aug 2022 23:50:34 +0300 Subject: [PATCH 047/313] G-API: Introduce GAbstractStreamingExecutor Now GStreamingExecutor is its subclass; others to come --- modules/gapi/CMakeLists.txt | 1 + modules/gapi/src/compiler/gstreaming.cpp | 4 +- modules/gapi/src/compiler/gstreaming_priv.hpp | 8 +-- .../executor/gabstractstreamingexecutor.cpp | 23 +++++++++ .../executor/gabstractstreamingexecutor.hpp | 49 +++++++++++++++++++ .../gapi/src/executor/gstreamingexecutor.cpp | 10 +--- .../gapi/src/executor/gstreamingexecutor.hpp | 29 ++++------- 7 files changed, 91 insertions(+), 33 deletions(-) create mode 100644 modules/gapi/src/executor/gabstractstreamingexecutor.cpp create mode 100644 modules/gapi/src/executor/gabstractstreamingexecutor.hpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 962256cc88..edf4e588e0 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -114,6 +114,7 @@ set(gapi_srcs # Executor src/executor/gabstractexecutor.cpp + src/executor/gabstractstreamingexecutor.cpp src/executor/gexecutor.cpp src/executor/gtbbexecutor.cpp src/executor/gstreamingexecutor.cpp diff --git a/modules/gapi/src/compiler/gstreaming.cpp b/modules/gapi/src/compiler/gstreaming.cpp index e45e770427..c0bc0b1d1b 100644 --- a/modules/gapi/src/compiler/gstreaming.cpp +++ b/modules/gapi/src/compiler/gstreaming.cpp @@ -19,14 +19,14 @@ // GStreamingCompiled private implementation /////////////////////////////////// void cv::GStreamingCompiled::Priv::setup(const GMetaArgs &_metaArgs, const GMetaArgs &_outMetas, - std::unique_ptr &&_pE) + std::unique_ptr &&_pE) { m_metas = _metaArgs; m_outMetas = _outMetas; m_exec = std::move(_pE); } -void cv::GStreamingCompiled::Priv::setup(std::unique_ptr &&_pE) +void cv::GStreamingCompiled::Priv::setup(std::unique_ptr &&_pE) { m_exec = std::move(_pE); } diff --git a/modules/gapi/src/compiler/gstreaming_priv.hpp b/modules/gapi/src/compiler/gstreaming_priv.hpp index 1b559ba310..0fd5fc7b7f 100644 --- a/modules/gapi/src/compiler/gstreaming_priv.hpp +++ b/modules/gapi/src/compiler/gstreaming_priv.hpp @@ -9,7 +9,7 @@ #define OPENCV_GAPI_GSTREAMING_COMPILED_PRIV_HPP #include // unique_ptr -#include "executor/gstreamingexecutor.hpp" +#include "executor/gabstractstreamingexecutor.hpp" namespace cv { @@ -26,7 +26,7 @@ class GAPI_EXPORTS GStreamingCompiled::Priv { GMetaArgs m_metas; // passed by user GMetaArgs m_outMetas; // inferred by compiler - std::unique_ptr m_exec; + std::unique_ptr m_exec; // NB: Used by python wrapper to clarify input/output types GTypesInfo m_out_info; @@ -35,8 +35,8 @@ class GAPI_EXPORTS GStreamingCompiled::Priv public: void setup(const GMetaArgs &metaArgs, const GMetaArgs &outMetas, - std::unique_ptr &&pE); - void setup(std::unique_ptr &&pE); + std::unique_ptr &&pE); + void setup(std::unique_ptr &&pE); bool isEmpty() const; const GMetaArgs& metas() const; diff --git a/modules/gapi/src/executor/gabstractstreamingexecutor.cpp b/modules/gapi/src/executor/gabstractstreamingexecutor.cpp new file mode 100644 index 0000000000..07579a9a6f --- /dev/null +++ b/modules/gapi/src/executor/gabstractstreamingexecutor.cpp @@ -0,0 +1,23 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + +#include "precomp.hpp" + +#include + +#include "executor/gabstractstreamingexecutor.hpp" + +cv::gimpl::GAbstractStreamingExecutor::GAbstractStreamingExecutor(std::unique_ptr &&g_model, + const GCompileArgs &comp_args) + : m_orig_graph(std::move(g_model)) + , m_island_graph(GModel::Graph(*m_orig_graph).metadata() + .get().model) + , m_comp_args(comp_args) + , m_gim(*m_island_graph) + , m_desync(GModel::Graph(*m_orig_graph).metadata() + .contains()) +{ +} diff --git a/modules/gapi/src/executor/gabstractstreamingexecutor.hpp b/modules/gapi/src/executor/gabstractstreamingexecutor.hpp new file mode 100644 index 0000000000..952bbd01db --- /dev/null +++ b/modules/gapi/src/executor/gabstractstreamingexecutor.hpp @@ -0,0 +1,49 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + +#ifndef OPENCV_GAPI_GABSTRACT_STREAMING_EXECUTOR_HPP +#define OPENCV_GAPI_GABSTRACT_STREAMING_EXECUTOR_HPP + +#include // unique_ptr, shared_ptr + +#include + +#include "backends/common/gbackend.hpp" + +namespace cv { +namespace gimpl { + +class GAbstractStreamingExecutor +{ +protected: + std::unique_ptr m_orig_graph; + std::shared_ptr m_island_graph; + cv::GCompileArgs m_comp_args; + + cv::gimpl::GIslandModel::Graph m_gim; // FIXME: make const? + const bool m_desync; + +public: + explicit GAbstractStreamingExecutor(std::unique_ptr &&g_model, + const cv::GCompileArgs &comp_args); + virtual ~GAbstractStreamingExecutor() = default; + virtual void setSource(GRunArgs &&args) = 0; + virtual void start() = 0; + virtual bool pull(cv::GRunArgsP &&outs) = 0; + virtual bool pull(cv::GOptRunArgsP &&outs) = 0; + + using PyPullResult = std::tuple>; + virtual PyPullResult pull() = 0; + + virtual bool try_pull(cv::GRunArgsP &&outs) = 0; + virtual void stop() = 0; + virtual bool running() const = 0; +}; + +} // namespace gimpl +} // namespace cv + +#endif // OPENCV_GAPI_GABSTRACT_STREAMING_EXECUTOR_HPP diff --git a/modules/gapi/src/executor/gstreamingexecutor.cpp b/modules/gapi/src/executor/gstreamingexecutor.cpp index 557e5ceee4..66aad90697 100644 --- a/modules/gapi/src/executor/gstreamingexecutor.cpp +++ b/modules/gapi/src/executor/gstreamingexecutor.cpp @@ -1288,13 +1288,7 @@ public: // proper graph reshape and islands recompilation cv::gimpl::GStreamingExecutor::GStreamingExecutor(std::unique_ptr &&g_model, const GCompileArgs &comp_args) - : m_orig_graph(std::move(g_model)) - , m_island_graph(GModel::Graph(*m_orig_graph).metadata() - .get().model) - , m_comp_args(comp_args) - , m_gim(*m_island_graph) - , m_desync(GModel::Graph(*m_orig_graph).metadata() - .contains()) + : GAbstractStreamingExecutor(std::move(g_model), comp_args) { GModel::Graph gm(*m_orig_graph); // NB: Right now GIslandModel is acyclic, and all the below code assumes that. @@ -1862,7 +1856,7 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs) GAPI_Assert(false && "Unreachable code"); } -std::tuple> cv::gimpl::GStreamingExecutor::pull() +cv::gimpl::GAbstractStreamingExecutor::PyPullResult cv::gimpl::GStreamingExecutor::pull() { using RunArgs = cv::util::variant; bool is_over = false; diff --git a/modules/gapi/src/executor/gstreamingexecutor.hpp b/modules/gapi/src/executor/gstreamingexecutor.hpp index da27f6a646..cb63d4b7f0 100644 --- a/modules/gapi/src/executor/gstreamingexecutor.hpp +++ b/modules/gapi/src/executor/gstreamingexecutor.hpp @@ -12,7 +12,6 @@ // on concurrent_bounded_queue #endif -#include // unique_ptr, shared_ptr #include // thread #include #include @@ -26,9 +25,7 @@ template using QueueClass = cv::gapi::own::concurrent_bounded_queue< #endif // TBB #include "executor/last_value.hpp" -#include - -#include "backends/common/gbackend.hpp" +#include "executor/gabstractstreamingexecutor.hpp" namespace cv { namespace gimpl { @@ -104,7 +101,7 @@ public: // FIXME: Currently all GExecutor comments apply also // to this one. Please document it separately in the future. -class GStreamingExecutor final +class GStreamingExecutor final: public GAbstractStreamingExecutor { protected: // GStreamingExecutor is a state machine described as follows @@ -131,15 +128,9 @@ protected: RUNNING, } state = State::STOPPED; - std::unique_ptr m_orig_graph; - std::shared_ptr m_island_graph; - cv::GCompileArgs m_comp_args; cv::GMetaArgs m_last_metas; util::optional m_reshapable; - cv::gimpl::GIslandModel::Graph m_gim; // FIXME: make const? - const bool m_desync; - // FIXME: Naive executor details are here for now // but then it should be moved to another place struct OpDesc @@ -202,14 +193,14 @@ public: explicit GStreamingExecutor(std::unique_ptr &&g_model, const cv::GCompileArgs &comp_args); ~GStreamingExecutor(); - void setSource(GRunArgs &&args); - void start(); - bool pull(cv::GRunArgsP &&outs); - bool pull(cv::GOptRunArgsP &&outs); - std::tuple> pull(); - bool try_pull(cv::GRunArgsP &&outs); - void stop(); - bool running() const; + void setSource(GRunArgs &&args) override; + void start() override; + bool pull(cv::GRunArgsP &&outs) override; + bool pull(cv::GOptRunArgsP &&outs) override; + PyPullResult pull() override; + bool try_pull(cv::GRunArgsP &&outs) override; + void stop() override; + bool running() const override; }; } // namespace gimpl From 1f0bfc8d8350dc41c09e1151437d0164e94f69b4 Mon Sep 17 00:00:00 2001 From: lamm45 <96844552+lamm45@users.noreply.github.com> Date: Sun, 26 Jun 2022 19:13:12 -0400 Subject: [PATCH 048/313] Fix angle discretization in Hough transforms In some situations the last value was missing from the discrete theta values. Now, the last value is chosen such that it is close to the user-provided maximum theta, while the distance to pi remains always at least theta_step/2. This should avoid duplicate detections. A better way would probably be to use max_theta as is and adjust the resolution (theta_step) instead, such that the discretization would always be uniform (in a circular sense) when full angle range is used. --- modules/imgproc/include/opencv2/imgproc.hpp | 24 +++++++++++---------- modules/imgproc/src/hough.cpp | 22 ++++++++++++++----- modules/imgproc/test/test_houghlines.cpp | 11 ++++++++++ 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 4cb077f375..2250a09411 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2024,23 +2024,24 @@ transform. @param image 8-bit, single-channel binary source image. The image may be modified by the function. @param lines Output vector of lines. Each line is represented by a 2 or 3 element vector -\f$(\rho, \theta)\f$ or \f$(\rho, \theta, \textrm{votes})\f$ . \f$\rho\f$ is the distance from the coordinate origin \f$(0,0)\f$ (top-left corner of -the image). \f$\theta\f$ is the line rotation angle in radians ( -\f$0 \sim \textrm{vertical line}, \pi/2 \sim \textrm{horizontal line}\f$ ). +\f$(\rho, \theta)\f$ or \f$(\rho, \theta, \textrm{votes})\f$, where \f$\rho\f$ is the distance from +the coordinate origin \f$(0,0)\f$ (top-left corner of the image), \f$\theta\f$ is the line rotation +angle in radians ( \f$0 \sim \textrm{vertical line}, \pi/2 \sim \textrm{horizontal line}\f$ ), and \f$\textrm{votes}\f$ is the value of accumulator. @param rho Distance resolution of the accumulator in pixels. @param theta Angle resolution of the accumulator in radians. -@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +@param threshold %Accumulator threshold parameter. Only those lines are returned that get enough votes ( \f$>\texttt{threshold}\f$ ). -@param srn For the multi-scale Hough transform, it is a divisor for the distance resolution rho . +@param srn For the multi-scale Hough transform, it is a divisor for the distance resolution rho. The coarse accumulator distance resolution is rho and the accurate accumulator resolution is -rho/srn . If both srn=0 and stn=0 , the classical Hough transform is used. Otherwise, both these +rho/srn. If both srn=0 and stn=0, the classical Hough transform is used. Otherwise, both these parameters should be positive. @param stn For the multi-scale Hough transform, it is a divisor for the distance resolution theta. @param min_theta For standard and multi-scale Hough transform, minimum angle to check for lines. Must fall between 0 and max_theta. -@param max_theta For standard and multi-scale Hough transform, maximum angle to check for lines. -Must fall between min_theta and CV_PI. +@param max_theta For standard and multi-scale Hough transform, an upper bound for the angle. +Must fall between min_theta and CV_PI. The actual maximum angle in the accumulator may be slightly +less than max_theta, depending on the parameters min_theta and theta. */ CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, @@ -2068,7 +2069,7 @@ And this is the output of the above program in case of the probabilistic Hough t line segment. @param rho Distance resolution of the accumulator in pixels. @param theta Angle resolution of the accumulator in radians. -@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +@param threshold %Accumulator threshold parameter. Only those lines are returned that get enough votes ( \f$>\texttt{threshold}\f$ ). @param minLineLength Minimum line length. Line segments shorter than that are rejected. @param maxLineGap Maximum allowed gap between points on the same line to link them. @@ -2087,13 +2088,14 @@ The function finds lines in a set of points using a modification of the Hough tr @param lines Output vector of found lines. Each vector is encoded as a vector \f$(votes, rho, theta)\f$. The larger the value of 'votes', the higher the reliability of the Hough line. @param lines_max Max count of Hough lines. -@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +@param threshold %Accumulator threshold parameter. Only those lines are returned that get enough votes ( \f$>\texttt{threshold}\f$ ). @param min_rho Minimum value for \f$\rho\f$ for the accumulator (Note: \f$\rho\f$ can be negative. The absolute value \f$|\rho|\f$ is the distance of a line to the origin.). @param max_rho Maximum value for \f$\rho\f$ for the accumulator. @param rho_step Distance resolution of the accumulator. @param min_theta Minimum angle value of the accumulator in radians. -@param max_theta Maximum angle value of the accumulator in radians. +@param max_theta Upper bound for the angle value of the accumulator in radians. The actual maximum +angle may be slightly less than max_theta, depending on the parameters min_theta and theta_step. @param theta_step Angle resolution of the accumulator in radians. */ CV_EXPORTS_W void HoughLinesPointSet( InputArray point, OutputArray lines, int lines_max, int threshold, diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index 5fa24f4789..dda0307152 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -68,6 +68,18 @@ struct hough_cmp_gt const int* aux; }; +static inline int +computeNumangle( double min_theta, double max_theta, double theta_step ) +{ + int numangle = cvFloor((max_theta - min_theta) / theta_step) + 1; + // If the distance between the first angle and the last angle is + // approximately equal to pi, then the last angle will be removed + // in order to prevent a line to be detected twice. + if ( numangle > 1 && fabs(CV_PI - (numangle-1)*theta_step) < theta_step/2 ) + --numangle; + return numangle; +} + static void createTrigTable( int numangle, double min_theta, double theta_step, float irho, float *tabSin, float *tabCos ) @@ -130,7 +142,7 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type, CV_CheckGE(max_theta, min_theta, "max_theta must be greater than min_theta"); - int numangle = cvRound((max_theta - min_theta) / theta); + int numangle = computeNumangle(min_theta, max_theta, theta); int numrho = cvRound(((max_rho - min_rho) + 1) / rho); #if defined HAVE_IPP && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_HOUGH @@ -475,7 +487,7 @@ HoughLinesProbabilistic( Mat& image, int width = image.cols; int height = image.rows; - int numangle = cvRound(CV_PI / theta); + int numangle = computeNumangle(0.0, CV_PI, theta); int numrho = cvRound(((width + height) * 2 + 1) / rho); #if defined HAVE_IPP && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_HOUGH @@ -792,7 +804,7 @@ static bool ocl_HoughLines(InputArray _src, OutputArray _lines, double rho, doub } UMat src = _src.getUMat(); - int numangle = cvRound((max_theta - min_theta) / theta); + int numangle = computeNumangle(min_theta, max_theta, theta); int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); UMat pointsList; @@ -846,7 +858,7 @@ static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, dou } UMat src = _src.getUMat(); - int numangle = cvRound(CV_PI / theta); + int numangle = computeNumangle(0.0, CV_PI, theta); int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); UMat pointsList; @@ -956,7 +968,7 @@ void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, i int i; float irho = 1 / (float)rho_step; float irho_min = ((float)min_rho * irho); - int numangle = cvRound((max_theta - min_theta) / theta_step); + int numangle = computeNumangle(min_theta, max_theta, theta_step); int numrho = cvRound((max_rho - min_rho + 1) / rho_step); Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 ); diff --git a/modules/imgproc/test/test_houghlines.cpp b/modules/imgproc/test/test_houghlines.cpp index e90891274a..61b67d9873 100644 --- a/modules/imgproc/test/test_houghlines.cpp +++ b/modules/imgproc/test/test_houghlines.cpp @@ -329,6 +329,17 @@ TEST(HoughLinesPointSet, regression_21029) EXPECT_TRUE(lines.empty()); } +TEST(HoughLines, regression_21983) +{ + Mat img(200, 200, CV_8UC1, Scalar(0)); + line(img, Point(0, 100), Point(100, 100), Scalar(255)); + std::vector lines; + HoughLines(img, lines, 1, CV_PI/180, 90, 0, 0, 0.001, 1.58); + ASSERT_EQ(lines.size(), 1U); + EXPECT_EQ(lines[0][0], 100); + EXPECT_NEAR(lines[0][1], 1.57179642, 1e-4); +} + INSTANTIATE_TEST_CASE_P( ImgProc, StandartHoughLinesTest, testing::Combine(testing::Values( "shared/pic5.png", "../stitching/a1.png" ), testing::Values( 1, 10 ), testing::Values( 0.05, 0.1 ), From 51855448643131608e93d2d35f722182a79cde19 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 31 Aug 2022 11:57:48 +0300 Subject: [PATCH 049/313] Enabled EXIF orientation tests for libspng. --- modules/imgcodecs/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index ac201e7935..1572543aff 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -185,7 +185,7 @@ endif() if(TARGET opencv_test_imgcodecs AND HAVE_OPENEXR AND "$ENV{OPENCV_IO_ENABLE_OPENEXR}") ocv_target_compile_definitions(opencv_test_imgcodecs PRIVATE OPENCV_IMGCODECS_ENABLE_OPENEXR_TESTS=1) endif() -if(TARGET opencv_test_imgcodecs AND HAVE_PNG AND NOT (PNG_VERSION VERSION_LESS "1.6.31")) +if(TARGET opencv_test_imgcodecs AND ((HAVE_PNG AND NOT (PNG_VERSION VERSION_LESS "1.6.31")) OR HAVE_SPNG)) # details: https://github.com/glennrp/libpng/commit/68cb0aaee3de6371b81a4613476d9b33e43e95b1 ocv_target_compile_definitions(opencv_test_imgcodecs PRIVATE OPENCV_IMGCODECS_PNG_WITH_EXIF=1) endif() From c4a6e1fd4dcde1a1287395d6f15ea2a000f4192a Mon Sep 17 00:00:00 2001 From: ocpalo Date: Wed, 31 Aug 2022 20:39:19 +0300 Subject: [PATCH 050/313] decode chunks and calculate checksums --- modules/imgcodecs/src/grfmt_spng.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index 8bcbbe21c7..e3c61164e4 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -121,7 +121,6 @@ bool SPngDecoder::readHeader() } m_ctx = ctx; - spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); if (!m_buf.empty()) spng_set_png_stream((struct spng_ctx *)m_ctx, (spng_rw_fn *)readDataFromBuf, this); @@ -336,7 +335,7 @@ bool SPngDecoder::readData(Mat &img) else { AutoBuffer imageBuffer(image_size); - spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0); + ret = spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0); int step = m_width * img.channels(); if (fmt == SPNG_FMT_RGB8) { @@ -469,16 +468,17 @@ bool SPngDecoder::readData(Mat &img) if (ret == SPNG_EOI) { + ret = spng_decode_chunks(png_ptr); + if(ret == SPNG_OK) result = true; struct spng_exif exif_s{}; ret = spng_get_exif(png_ptr, &exif_s); if (ret == SPNG_OK) { if (exif_s.data && exif_s.length > 0) { - m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length); + result = m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length); } } - result = true; } } } From b69b1eae8f4bab282004ae6f3e9e4f3a8f0db1e2 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Fri, 2 Sep 2022 16:30:06 +0800 Subject: [PATCH 051/313] fix bug 22450 --- modules/dnn/src/layers/fast_convolution/fast_convolution.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index eda5c7ce26..ba85077f70 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -21,7 +21,7 @@ enum { FAST_VEC_NLANES=4 }; #define CONV_MR 4 #define CONV_NR 24 -#ifdef CV_AVX2 +#if CV_AVX2 enum { FAST_VEC_NLANES=8 }; // AVX2 #else enum { FAST_VEC_NLANES=4 }; // SIMD 128 From 337452b4c036311f5a0c77db79bf713cc84fc621 Mon Sep 17 00:00:00 2001 From: anton Date: Tue, 30 Aug 2022 18:43:37 +0200 Subject: [PATCH 052/313] changed names of permutations if Reshpe is in NHWC --- modules/dnn/src/tensorflow/tf_importer.cpp | 16 ++++++++++++---- modules/dnn/test/test_tf_importer.cpp | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 80a8b6dfc5..96e0af99ec 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -1097,6 +1097,9 @@ void TFImporter::parseReshape(tensorflow::GraphDef& net, const tensorflow::NodeD std::swap(*newShape.ptr(0, 1), *newShape.ptr(0, 2)); hasSwap = true; } + + bool changedType{false}; + if (inpLayout == DATA_LAYOUT_NHWC) { if (newShapeSize >= 2 || newShape.at(1) == 1) @@ -1110,23 +1113,28 @@ void TFImporter::parseReshape(tensorflow::GraphDef& net, const tensorflow::NodeD else { inpLayout = DATA_LAYOUT_NHWC; + changedType = newShapeSize == 4 && !hasSwap; } } } layerParams.set("dim", DictValue::arrayInt(newShape.ptr(), newShapeSize)); - int id = dstNet.addLayer(name, "Reshape", layerParams); - layer_id[name] = id; + std::string setName = changedType ? name + "/realReshape" : name; + + int id = dstNet.addLayer(setName, "Reshape", layerParams); + layer_id[setName] = id; // one input only connect(layer_id, dstNet, inpId, id, 0); - inpId = Pin(name); + inpId = Pin(setName); if ((inpLayout == DATA_LAYOUT_NHWC || inpLayout == DATA_LAYOUT_UNKNOWN || inpLayout == DATA_LAYOUT_PLANAR) && newShapeSize == 4 && !hasSwap) { int order[] = {0, 3, 1, 2}; // Transform back to OpenCV's NCHW. - addPermuteLayer(order, name + "/nchw", inpId); + + setName = changedType ? name : name + "/nchw"; + addPermuteLayer(order, setName, inpId); inpLayout = DATA_LAYOUT_NCHW; } diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index 72a8989f6a..97060df563 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -337,6 +337,12 @@ TEST_P(Test_TensorFlow_layers, eltwise_mul_vec) runTensorFlowNet("eltwise_mul_vec"); } +TEST_P(Test_TensorFlow_layers, tf_reshape_nhwc) +{ + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); + runTensorFlowNet("tf_reshape_nhwc"); +} TEST_P(Test_TensorFlow_layers, channel_broadcast) { From bf54a370e56f24da87c5789d3f013c815f731cd9 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Sun, 4 Sep 2022 20:15:53 +0100 Subject: [PATCH 053/313] Add tests for stateful kernel functionality --- .../python/test/test_gapi_sample_pipelines.py | 2 +- .../python/test/test_gapi_stateful_kernel.py | 96 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 modules/gapi/misc/python/test/test_gapi_stateful_kernel.py diff --git a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py index 7763579ebf..8e15d457d9 100644 --- a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py +++ b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py @@ -432,7 +432,7 @@ try: with self.assertRaises(Exception): create_op([cv.GMat, int], [cv.GMat]).on(cv.GMat()) - def test_stateful_kernel(self): + def test_state_in_class(self): @cv.gapi.op('custom.sum', in_types=[cv.GArray.Int], out_types=[cv.GOpaque.Int]) class GSum: @staticmethod diff --git a/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py b/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py new file mode 100644 index 0000000000..f24ceccabf --- /dev/null +++ b/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +import numpy as np +import cv2 as cv +import os +import sys +import unittest + +from tests_common import NewOpenCVTests + + +try: + + if sys.version_info[:2] < (3, 0): + raise unittest.SkipTest('Python 2.x is not supported') + + + class CounterState: + def __init__(self): + self.counter = 0 + + + @cv.gapi.op('stateful_counter', + in_types=[cv.GOpaque.Int], + out_types=[cv.GOpaque.Int]) + class GStatefulCounter: + """Accumulate state counter on every call""" + + @staticmethod + def outMeta(desc): + return cv.empty_gopaque_desc() + + + @cv.gapi.kernel(GStatefulCounter) + class GStatefulCounterImpl: + """Implementation for GStatefulCounter operation.""" + + @staticmethod + def setup(desc): + return CounterState() + + @staticmethod + def run(value, state): + state.counter += value + return state.counter + + + class gapi_sample_pipelines(NewOpenCVTests): + def test_stateful_kernel_single_instance(self): + g_in = cv.GOpaque.Int() + g_out = GStatefulCounter.on(g_in) + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + pkg = cv.gapi.kernels(GStatefulCounterImpl) + + nums = [i for i in range(10)] + acc = 0 + for v in nums: + acc = comp.apply(cv.gin(v), args=cv.gapi.compile_args(pkg)) + + self.assertEqual(sum(nums), acc) + + + def test_stateful_kernel_multiple_instances(self): + # NB: Every counter has his own independent state. + g_in = cv.GOpaque.Int() + g_out0 = GStatefulCounter.on(g_in) + g_out1 = GStatefulCounter.on(g_in) + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out0, g_out1)) + pkg = cv.gapi.kernels(GStatefulCounterImpl) + + nums = [i for i in range(10)] + acc0 = acc1 = 0 + for v in nums: + acc0, acc1 = comp.apply(cv.gin(v), args=cv.gapi.compile_args(pkg)) + + ref = sum(nums) + self.assertEqual(ref, acc0) + self.assertEqual(ref, acc1) + + +except unittest.SkipTest as e: + + message = str(e) + + class TestSkip(unittest.TestCase): + def setUp(self): + self.skipTest('Skip tests: ' + message) + + def test_skip(): + pass + + pass + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() From 22eb91a7e024bcc979d2cb11eb0835def56f6a69 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sat, 3 Sep 2022 11:49:00 +0300 Subject: [PATCH 054/313] Update hough.cpp --- modules/imgproc/src/hough.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index 40ac546a41..13e349b97e 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -2293,6 +2293,9 @@ static void HoughCircles( InputArray _image, OutputArray _circles, break; case HOUGH_GRADIENT_ALT: { + if( param2 >= 1 ) + CV_Error( Error::StsOutOfRange, "when using HOUGH_GRADIENT_ALT method, param2 parameter must be smaller than 1.0" ); + std::vector circles; Mat image = _image.getMat(); HoughCirclesAlt(image, circles, dp, minDist, minRadius, maxRadius, param1, param2); @@ -2320,7 +2323,7 @@ static void HoughCircles( InputArray _image, OutputArray _circles, } break; default: - CV_Error( Error::StsBadArg, "Unrecognized method id. Actually only CV_HOUGH_GRADIENT is supported." ); + CV_Error( Error::StsBadArg, "Unrecognized method id. Actually supported methods are HOUGH_GRADIENT and HOUGH_GRADIENT_ALT" ); } } From 2ac62bccec74b2316b847ade6fe5b2d738dc6afc Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 5 Sep 2022 16:53:08 +0000 Subject: [PATCH 055/313] videoio(test): fix build warning --- modules/videoio/test/test_ffmpeg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 5001bcf796..6f0f2f28b4 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -417,7 +417,7 @@ TEST_P(ffmpeg_get_fourcc, check_short_codecs) if(!fourcc && fileName == "../cv/tracking/faceocc2/data/faceocc2.webm") throw SkipTestException("Feature not yet supported by Windows FFmpeg shared library!"); #endif - ASSERT_EQ(fourccToString(fourcc), fourcc_string); + ASSERT_EQ(fourcc_string, fourccToString((int)fourcc)); } const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] = From 2e40b7f1138d523d128b6e3d939b63c85fec3139 Mon Sep 17 00:00:00 2001 From: alessandro faria Date: Mon, 5 Sep 2022 18:22:01 -0300 Subject: [PATCH 056/313] ADD weights yolov4 in models.yml --- ...olov3.txt => object_detection_classes_yolov4.txt} | 12 ++++++------ samples/dnn/models.yml | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) rename samples/data/dnn/{object_detection_classes_yolov3.txt => object_detection_classes_yolov4.txt} (90%) diff --git a/samples/data/dnn/object_detection_classes_yolov3.txt b/samples/data/dnn/object_detection_classes_yolov4.txt similarity index 90% rename from samples/data/dnn/object_detection_classes_yolov3.txt rename to samples/data/dnn/object_detection_classes_yolov4.txt index 941cb4e139..ca76c80b5b 100644 --- a/samples/data/dnn/object_detection_classes_yolov3.txt +++ b/samples/data/dnn/object_detection_classes_yolov4.txt @@ -1,8 +1,8 @@ person bicycle car -motorcycle -airplane +motorbike +aeroplane bus train truck @@ -55,12 +55,12 @@ pizza donut cake chair -couch -potted plant +sofa +pottedplant bed -dining table +diningtable toilet -tv +tvmonitor laptop mouse remote diff --git a/samples/dnn/models.yml b/samples/dnn/models.yml index 86e86925df..f6e9f0e41f 100644 --- a/samples/dnn/models.yml +++ b/samples/dnn/models.yml @@ -23,16 +23,16 @@ opencv_fd: # Might be used for all YOLOv2, TinyYolov2, YOLOv3, YOLOv4 and TinyYolov4 yolo: load_info: - url: "https://pjreddie.com/media/files/yolov3.weights" - sha1: "520878f12e97cf820529daea502acca380f1cb8e" - model: "yolov3.weights" - config: "yolov3.cfg" + url: "https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.weights" + sha1: "0143deb6c46fcc7f74dd35bf3c14edc3784e99ee" + model: "yolov4.weights" + config: "yolov4.cfg" mean: [0, 0, 0] scale: 0.00392 width: 416 height: 416 rgb: true - classes: "object_detection_classes_yolov3.txt" + classes: "object_detection_classes_yolov4.txt" sample: "object_detection" tiny-yolo-voc: From 7955469f7c3410b932b78c73410ad6d51b3def60 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Tue, 6 Sep 2022 08:52:35 +0100 Subject: [PATCH 057/313] Fix problem with handleNewStream functionality --- .../include/opencv2/gapi/python/python.hpp | 14 +- modules/gapi/misc/python/pyopencv_gapi.hpp | 120 ++++++++---------- .../python/test/test_gapi_stateful_kernel.py | 46 +++++++ .../src/backends/python/gpythonbackend.cpp | 31 ++--- 4 files changed, 113 insertions(+), 98 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/python/python.hpp b/modules/gapi/include/opencv2/gapi/python/python.hpp index 72ed6f5850..1857a938d5 100644 --- a/modules/gapi/include/opencv2/gapi/python/python.hpp +++ b/modules/gapi/include/opencv2/gapi/python/python.hpp @@ -32,9 +32,7 @@ struct GPythonContext const cv::GMetaArgs &in_metas; const cv::GTypesInfo &out_info; - bool m_isStateful; - - GArg m_state; + cv::optional m_state; }; using Impl = std::function; @@ -46,13 +44,9 @@ public: GPythonKernel() = default; GPythonKernel(Impl run, Setup setup); - cv::GRunArgs operator()(const GPythonContext& ctx); - - bool m_isStateful = false; - Setup m_setup = nullptr; - -private: - Impl m_run; + Impl run; + Setup setup = nullptr; + bool is_stateful = false; }; class GAPI_EXPORTS GPythonFunctor : public cv::gapi::GFunctor diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 0812b283b7..ce1dbf6a48 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -660,7 +660,8 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, // NB: Doesn't increase reference counter (false), // because PyObject already have ownership. // In case exception decrement reference counter. - cv::detail::PyObjectHolder args(PyTuple_New(ctx.m_isStateful ? ins.size() + 1: ins.size()), false); + cv::detail::PyObjectHolder args( + PyTuple_New(ctx.m_state.has_value() ? ins.size() + 1 : ins.size()), false); for (size_t i = 0; i < ins.size(); ++i) { // NB: If meta is monostate then object isn't associated with G-TYPE. @@ -691,9 +692,9 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, ++in_idx; } - if (ctx.m_isStateful) + if (ctx.m_state.has_value()) { - PyTuple_SetItem(args.get(), ins.size(), pyopencv_from(ctx.m_state)); + PyTuple_SetItem(args.get(), ins.size(), pyopencv_from(ctx.m_state.value())); } // NB: Doesn't increase reference counter (false). @@ -742,8 +743,45 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, return outs; } -static cv::GArg setup_py(cv::detail::PyObjectHolder out_meta, const cv::GMetaArgs& meta, - const cv::GArgs& gargs) +static void unpackMetasToTuple(const cv::GMetaArgs& meta, + const cv::GArgs& gargs, + cv::detail::PyObjectHolder& tuple) +{ + size_t idx = 0; + for (auto&& m : meta) + { + switch (m.index()) + { + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, pyopencv_from(gargs[idx])); + break; + case cv::GMetaArg::index_of(): + util::throw_error( + std::logic_error("GFrame isn't supported for custom operation")); + break; + } + ++idx; + } +} + +static cv::GArg setup_py(cv::detail::PyObjectHolder setup, + const cv::GMetaArgs& meta, + const cv::GArgs& gargs) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -756,42 +794,11 @@ static cv::GArg setup_py(cv::detail::PyObjectHolder out_meta, const cv::GMetaArg // because PyObject already have ownership. // In case exception decrement reference counter. cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false); - size_t idx = 0; - for (auto&& m : meta) - { - switch (m.index()) - { - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, - pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, - pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, - pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(gargs[idx])); - break; - case cv::GMetaArg::index_of(): - util::throw_error( - std::logic_error("GFrame isn't supported for custom operation")); - break; - } - ++idx; - } - - // PyTuple_SetItem(args.get(), idx, pyopencv_from(gmarg)); - - // NB: Doesn't increase reference counter (false). - // In case PyObject_CallObject return NULL, do nothing in destructor. - cv::detail::PyObjectHolder result(PyObject_CallObject(out_meta.get(), args.get()), false); + unpackMetasToTuple(meta, gargs, args); + // NB: Take an onwership because this state is "Python" type so it will be wrapped as-is + // into cv::GArg and stored in GPythonBackend. Object without ownership can't + // be dealocated outside this function. + cv::detail::PyObjectHolder result(PyObject_CallObject(setup.get(), args.get()), true); if (PyErr_Occurred()) { @@ -841,8 +848,8 @@ static cv::GMetaArgs get_meta_args(PyObject* tuple) } static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta, - const cv::GMetaArgs &meta, - const cv::GArgs &gargs) + const cv::GMetaArgs &meta, + const cv::GArgs &gargs) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -854,32 +861,7 @@ static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta, // because PyObject already have ownership. // In case exception decrement reference counter. cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false); - size_t idx = 0; - for (auto&& m : meta) - { - switch (m.index()) - { - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get(m))); - break; - case cv::GMetaArg::index_of(): - PyTuple_SetItem(args.get(), idx, pyopencv_from(gargs[idx])); - break; - case cv::GMetaArg::index_of(): - util::throw_error(std::logic_error("GFrame isn't supported for custom operation")); - break; - } - ++idx; - } + unpackMetasToTuple(meta, gargs, args); // NB: Doesn't increase reference counter (false). // In case PyObject_CallObject return NULL, do nothing in destructor. cv::detail::PyObjectHolder result( diff --git a/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py b/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py index f24ceccabf..9b3b614523 100644 --- a/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py +++ b/modules/gapi/misc/python/test/test_gapi_stateful_kernel.py @@ -78,6 +78,52 @@ try: self.assertEqual(ref, acc1) + def test_stateful_throw_setup(self): + @cv.gapi.kernel(GStatefulCounter) + class GThrowStatefulCounterImpl: + """Implementation for GStatefulCounter operation + that throw exception in setup method""" + + @staticmethod + def setup(desc): + raise Exception('Throw from setup method') + + @staticmethod + def run(value, state): + raise Exception('Unreachable') + + g_in = cv.GOpaque.Int() + g_out = GStatefulCounter.on(g_in) + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + pkg = cv.gapi.kernels(GThrowStatefulCounterImpl) + + with self.assertRaises(Exception): comp.apply(cv.gin(42), + args=cv.gapi.compile_args(pkg)) + + + def test_stateful_reset(self): + g_in = cv.GOpaque.Int() + g_out = GStatefulCounter.on(g_in) + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + pkg = cv.gapi.kernels(GStatefulCounterImpl) + + cc = comp.compileStreaming(args=cv.gapi.compile_args(pkg)) + + cc.setSource(cv.gin(1)) + cc.start() + for i in range(1, 10): + _, actual = cc.pull() + self.assertEqual(i, actual) + cc.stop() + + cc.setSource(cv.gin(2)) + cc.start() + for i in range(2, 10, 2): + _, actual = cc.pull() + self.assertEqual(i, actual) + cc.stop() + + except unittest.SkipTest as e: message = str(e) diff --git a/modules/gapi/src/backends/python/gpythonbackend.cpp b/modules/gapi/src/backends/python/gpythonbackend.cpp index 4bfdc7d3ec..0c65f4cba8 100644 --- a/modules/gapi/src/backends/python/gpythonbackend.cpp +++ b/modules/gapi/src/backends/python/gpythonbackend.cpp @@ -14,16 +14,10 @@ #include "api/gbackend_priv.hpp" #include "backends/common/gbackend.hpp" -cv::gapi::python::GPythonKernel::GPythonKernel(cv::gapi::python::Impl run, - cv::gapi::python::Setup setup) - : m_run(run), m_setup(setup) +cv::gapi::python::GPythonKernel::GPythonKernel(cv::gapi::python::Impl runf, + cv::gapi::python::Setup setupf) + : run(runf), setup(setupf), is_stateful(setup != nullptr) { - m_isStateful = (setup != nullptr); -} - -cv::GRunArgs cv::gapi::python::GPythonKernel::operator()(const cv::gapi::python::GPythonContext& ctx) -{ - return m_run(ctx); } cv::gapi::python::GPythonFunctor::GPythonFunctor(const char* id, @@ -162,11 +156,11 @@ static void writeBack(cv::GRunArg& arg, cv::GRunArgP& out) void GPythonExecutable::handleNewStream() { - if (!m_kernel.m_isStateful) + if (!m_kernel.is_stateful) return; - m_node_state = m_kernel.m_setup(cv::gimpl::GModel::collectInputMeta(m_gm, m_op), - m_gm.metadata(m_op).get().args); + m_node_state = m_kernel.setup(cv::gimpl::GModel::collectInputMeta(m_gm, m_op), + m_gm.metadata(m_op).get().args); } void GPythonExecutable::run(std::vector &&input_objs, @@ -181,16 +175,15 @@ void GPythonExecutable::run(std::vector &&input_objs, std::back_inserter(inputs), std::bind(&packArg, std::ref(m_res), _1)); - cv::gapi::python::GPythonContext ctx{inputs, m_in_metas, m_out_info, false}; + cv::gapi::python::GPythonContext ctx{inputs, m_in_metas, m_out_info, /*state*/{}}; - // For stateful kernel add state to its execution context - if (m_kernel.m_isStateful) + // NB: For stateful kernel add state to its execution context + if (m_kernel.is_stateful) { - ctx.m_state = m_node_state; - ctx.m_isStateful = true; + ctx.m_state = cv::optional(m_node_state); } - auto outs = m_kernel(ctx); + auto outs = m_kernel.run(ctx); for (auto&& it : ade::util::zip(outs, output_objs)) { @@ -249,7 +242,7 @@ GPythonExecutable::GPythonExecutable(const ade::Graph& g, m_kernel = cag.metadata(m_op).get().kernel; // If kernel is stateful then prepare storage for its state. - if (m_kernel.m_isStateful) + if (m_kernel.is_stateful) { m_node_state = cv::GArg{ }; } From ca7f964104727c4832ae499b6e57639b696a643a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 7 Mar 2022 22:26:20 +0000 Subject: [PATCH 058/313] dnn: use inheritance for OpenVINO net impl --- modules/dnn/include/opencv2/dnn/dnn.hpp | 11 +- modules/dnn/src/dnn_common.hpp | 12 + modules/dnn/src/dnn_params.cpp | 4 - modules/dnn/src/ie_ngraph.cpp | 14 -- modules/dnn/src/ie_ngraph.hpp | 3 - modules/dnn/src/layer_internals.hpp | 24 ++ modules/dnn/src/legacy_backend.cpp | 6 +- modules/dnn/src/net.cpp | 4 +- modules/dnn/src/net_impl.cpp | 79 ++---- modules/dnn/src/net_impl.hpp | 45 ++-- modules/dnn/src/net_impl_backend.cpp | 22 +- modules/dnn/src/net_openvino.cpp | 311 +++++++++++++++++++++++- modules/dnn/src/net_quantization.cpp | 9 +- modules/dnn/src/op_inf_engine.hpp | 2 + 14 files changed, 413 insertions(+), 133 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 6f03a8c32e..3382b27d8b 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -52,6 +52,11 @@ namespace cv { namespace dnn { + +namespace accessor { +class DnnNetAccessor; // forward declaration +} + CV__DNN_INLINE_NS_BEGIN //! @addtogroup dnn //! @{ @@ -840,8 +845,12 @@ CV__DNN_INLINE_NS_BEGIN */ CV_WRAP int64 getPerfProfile(CV_OUT std::vector& timings); - private: + struct Impl; + inline Impl* getImpl() const { return impl.get(); } + inline Impl& getImplRef() const { CV_DbgAssert(impl); return *impl.get(); } + friend class accessor::DnnNetAccessor; + protected: Ptr impl; }; diff --git a/modules/dnn/src/dnn_common.hpp b/modules/dnn/src/dnn_common.hpp index b580b9f74b..f5c3cce7ca 100644 --- a/modules/dnn/src/dnn_common.hpp +++ b/modules/dnn/src/dnn_common.hpp @@ -156,6 +156,18 @@ static inline std::string toString(const Mat& blob, const std::string& name = st CV__DNN_INLINE_NS_END + +namespace accessor { +class DnnNetAccessor +{ +public: + static inline Ptr& getImplPtrRef(Net& net) + { + return net.impl; + } +}; +} + }} // namespace #endif // __OPENCV_DNN_COMMON_HPP__ diff --git a/modules/dnn/src/dnn_params.cpp b/modules/dnn/src/dnn_params.cpp index 48e89c6fac..86a43db757 100644 --- a/modules/dnn/src/dnn_params.cpp +++ b/modules/dnn/src/dnn_params.cpp @@ -36,11 +36,7 @@ bool getParam_DNN_OPENCL_ALLOW_ALL_DEVICES() int getParam_DNN_BACKEND_DEFAULT() { static int PARAM_DNN_BACKEND_DEFAULT = (int)utils::getConfigurationParameterSizeT("OPENCV_DNN_BACKEND_DEFAULT", -#ifdef HAVE_INF_ENGINE - (size_t)DNN_BACKEND_INFERENCE_ENGINE -#else (size_t)DNN_BACKEND_OPENCV -#endif ); return PARAM_DNN_BACKEND_DEFAULT; } diff --git a/modules/dnn/src/ie_ngraph.cpp b/modules/dnn/src/ie_ngraph.cpp index d2bb2f189c..49b0345816 100644 --- a/modules/dnn/src/ie_ngraph.cpp +++ b/modules/dnn/src/ie_ngraph.cpp @@ -988,14 +988,6 @@ InferenceEngine::DataPtr ngraphDataOutputNode( return w.dataPtr; } -void forwardNgraph(const std::vector >& outBlobsWrappers, - Ptr& node, bool isAsync) -{ - CV_Assert(!node.empty()); - Ptr ieNode = node.dynamicCast(); - CV_Assert(!ieNode.empty()); - ieNode->net->forward(outBlobsWrappers, isAsync); -} void InfEngineNgraphNet::reset() { @@ -1192,12 +1184,6 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo } } -#else -void forwardNgraph(const std::vector >& outBlobsWrappers, - Ptr& node, bool isAsync) -{ - CV_Assert(false && "nGraph is not enabled in this OpenCV build"); -} #endif }} diff --git a/modules/dnn/src/ie_ngraph.hpp b/modules/dnn/src/ie_ngraph.hpp index 0d287a22a5..9ccc182fc8 100644 --- a/modules/dnn/src/ie_ngraph.hpp +++ b/modules/dnn/src/ie_ngraph.hpp @@ -158,9 +158,6 @@ private: #endif // HAVE_DNN_NGRAPH -void forwardNgraph(const std::vector >& outBlobsWrappers, - Ptr& node, bool isAsync); - }} // namespace cv::dnn diff --git a/modules/dnn/src/layer_internals.hpp b/modules/dnn/src/layer_internals.hpp index 9ded3543e1..e805ab53bb 100644 --- a/modules/dnn/src/layer_internals.hpp +++ b/modules/dnn/src/layer_internals.hpp @@ -112,6 +112,30 @@ struct LayerData return layerInstance; } + + void resetAllocation() + { + if (id == 0) + return; // skip "input" layer (assertion in Net::Impl::allocateLayers) + + layerInstance.release(); + outputBlobs.clear(); + inputBlobs.clear(); + internals.clear(); + + outputBlobsWrappers.clear(); + inputBlobsWrappers.clear(); + internalBlobsWrappers.clear(); + + backendNodes.clear(); + + skip = false; + flag = 0; + +#ifdef HAVE_CUDA + cudaD2HBackgroundTransfers.clear(); +#endif + } }; diff --git a/modules/dnn/src/legacy_backend.cpp b/modules/dnn/src/legacy_backend.cpp index fa9407aacd..431c597fab 100644 --- a/modules/dnn/src/legacy_backend.cpp +++ b/modules/dnn/src/legacy_backend.cpp @@ -75,11 +75,7 @@ Ptr wrapMat(int backendId, int targetId, cv::Mat& m) } else if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { -#ifdef HAVE_DNN_NGRAPH - return Ptr(new NgraphBackendWrapper(targetId, m)); -#else - CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of OpenVINO / Inference Engine + nGraph"); -#endif + CV_Assert(0 && "Internal error: DNN_BACKEND_INFERENCE_ENGINE_NGRAPH must be implemented through inheritance"); } else if (backendId == DNN_BACKEND_WEBNN) { diff --git a/modules/dnn/src/net.cpp b/modules/dnn/src/net.cpp index 33f22744b8..b3cf811a94 100644 --- a/modules/dnn/src/net.cpp +++ b/modules/dnn/src/net.cpp @@ -120,7 +120,7 @@ Net Net::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtyp CV_TRACE_FUNCTION(); CV_Assert(impl); CV_Assert(!empty()); - return impl->quantize(calibData, inputsDtype, outputsDtype, perChannel); + return impl->quantize(*this, calibData, inputsDtype, outputsDtype, perChannel); } // FIXIT drop from inference API @@ -146,7 +146,7 @@ void Net::setPreferableBackend(int backendId) CV_TRACE_FUNCTION(); CV_TRACE_ARG(backendId); CV_Assert(impl); - return impl->setPreferableBackend(backendId); + return impl->setPreferableBackend(*this, backendId); } void Net::setPreferableTarget(int targetId) diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index 24fb31f03e..dfab6de59a 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -30,6 +30,12 @@ std::string detail::NetImplBase::getDumpFileNameBase() const } +Net::Impl::~Impl() +{ + // nothing +} + + Net::Impl::Impl() { // allocate fake net input layer @@ -46,9 +52,8 @@ Net::Impl::Impl() netWasQuantized = false; fusion = true; isAsync = false; - preferableBackend = DNN_BACKEND_DEFAULT; + preferableBackend = (Backend)getParam_DNN_BACKEND_DEFAULT(); preferableTarget = DNN_TARGET_CPU; - skipInfEngineInit = false; hasDynamicShapes = false; } @@ -86,22 +91,10 @@ void Net::Impl::clear() } -void Net::Impl::setUpNet(const std::vector& blobsToKeep_) +void Net::Impl::validateBackendAndTarget() { CV_TRACE_FUNCTION(); - if (dumpLevel && networkDumpCounter == 0) - { - dumpNetworkToFile(); - } - - if (preferableBackend == DNN_BACKEND_DEFAULT) - preferableBackend = (Backend)getParam_DNN_BACKEND_DEFAULT(); -#ifdef HAVE_INF_ENGINE - if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE) - preferableBackend = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; // = getInferenceEngineBackendTypeParam(); -#endif - CV_Assert(preferableBackend != DNN_BACKEND_OPENCV || preferableTarget == DNN_TARGET_CPU || preferableTarget == DNN_TARGET_OPENCL || @@ -109,19 +102,6 @@ void Net::Impl::setUpNet(const std::vector& blobsToKeep_) CV_Assert(preferableBackend != DNN_BACKEND_HALIDE || preferableTarget == DNN_TARGET_CPU || preferableTarget == DNN_TARGET_OPENCL); -#ifdef HAVE_INF_ENGINE - if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) - { - CV_Assert( - (preferableTarget == DNN_TARGET_CPU && (!isArmComputePlugin() || preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)) || - preferableTarget == DNN_TARGET_OPENCL || - preferableTarget == DNN_TARGET_OPENCL_FP16 || - preferableTarget == DNN_TARGET_MYRIAD || - preferableTarget == DNN_TARGET_HDDL || - preferableTarget == DNN_TARGET_FPGA - ); - } -#endif #ifdef HAVE_WEBNN if (preferableBackend == DNN_BACKEND_WEBNN) { @@ -136,6 +116,20 @@ void Net::Impl::setUpNet(const std::vector& blobsToKeep_) CV_Assert(preferableBackend != DNN_BACKEND_TIMVX || preferableTarget == DNN_TARGET_NPU); + CV_Assert(preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && "Inheritance internal error"); +} + +void Net::Impl::setUpNet(const std::vector& blobsToKeep_) +{ + CV_TRACE_FUNCTION(); + + if (dumpLevel && networkDumpCounter == 0) + { + dumpNetworkToFile(); + } + + validateBackendAndTarget(); + if (!netWasAllocated || this->blobsToKeep != blobsToKeep_) { if (preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget)) @@ -813,12 +807,10 @@ void Net::Impl::forwardLayer(LayerData& ld) { forwardHalide(ld.outputBlobsWrappers, node); } -#ifdef HAVE_INF_ENGINE else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { - forwardNgraph(ld.outputBlobsWrappers, node, isAsync); + CV_Assert(preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && "Inheritance internal error"); } -#endif else if (preferableBackend == DNN_BACKEND_WEBNN) { forwardWebnn(ld.outputBlobsWrappers, node, isAsync); @@ -844,7 +836,7 @@ void Net::Impl::forwardLayer(LayerData& ld) #endif else { - CV_Error(Error::StsNotImplemented, "Unknown backend identifier"); + CV_Error(Error::StsNotImplemented, cv::format("Unknown backend identifier: %d", preferableBackend)); } } @@ -1369,30 +1361,7 @@ Mat Net::Impl::getBlob(String outputName) const AsyncArray Net::Impl::getBlobAsync(const LayerPin& pin) { CV_TRACE_FUNCTION(); -#ifdef HAVE_INF_ENGINE - if (!pin.valid()) - CV_Error(Error::StsObjectNotFound, "Requested blob not found"); - - LayerData& ld = layers[pin.lid]; - if ((size_t)pin.oid >= ld.outputBlobs.size()) - { - CV_Error(Error::StsOutOfRange, format("Layer \"%s\" produce only %d outputs, " - "the #%d was requested", - ld.name.c_str(), (int)ld.outputBlobs.size(), (int)pin.oid)); - } - if (preferableTarget != DNN_TARGET_CPU) - { - CV_Assert(!ld.outputBlobsWrappers.empty() && !ld.outputBlobsWrappers[pin.oid].empty()); - // Transfer data to CPU if it's require. - ld.outputBlobsWrappers[pin.oid]->copyToHost(); - } - CV_Assert(preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); - - Ptr wrapper = ld.outputBlobsWrappers[pin.oid].dynamicCast(); - return std::move(wrapper->futureMat); -#else CV_Error(Error::StsNotImplemented, "DNN: OpenVINO/nGraph backend is required"); -#endif // HAVE_INF_ENGINE } diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index 5f0563d3c3..269a46be79 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -38,7 +38,12 @@ struct Net::Impl : public detail::NetImplBase typedef std::map LayersShapesMap; typedef std::map MapIdToLayerData; + virtual ~Impl(); Impl(); + Impl(const Impl&) = delete; + + // Inheritance support + Ptr basePtr_; Ptr netInputLayer; std::vector blobsToKeep; @@ -49,7 +54,7 @@ struct Net::Impl : public detail::NetImplBase int preferableBackend; int preferableTarget; String halideConfigFile; - bool skipInfEngineInit; +// bool skipInfEngineInit; bool hasDynamicShapes; // Map host data to backend specific wrapper. std::map> backendWrappers; @@ -59,19 +64,22 @@ struct Net::Impl : public detail::NetImplBase bool netWasAllocated; bool netWasQuantized; bool fusion; - bool isAsync; + bool isAsync; // FIXIT: drop std::vector layersTimings; - bool empty() const; - void setPreferableBackend(int backendId); - void setPreferableTarget(int targetId); + virtual bool empty() const; + virtual void setPreferableBackend(Net& net, int backendId); + virtual void setPreferableTarget(int targetId); // FIXIT use inheritance - Ptr wrap(Mat& host); + virtual Ptr wrap(Mat& host); - void clear(); + virtual void clear(); + + + virtual void validateBackendAndTarget(); void setUpNet(const std::vector& blobsToKeep_ = std::vector()); @@ -118,7 +126,7 @@ struct Net::Impl : public detail::NetImplBase void setInputsNames(const std::vector& inputBlobNames); void setInputShape(const String& inputName, const MatShape& shape); - void setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean); + virtual void setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean); Mat getParam(int layer, int numParam) const; void setParam(int layer, int numParam, const Mat& blob); std::vector> getLayerInputs(int layerId) const; @@ -130,8 +138,7 @@ struct Net::Impl : public detail::NetImplBase int getLayersCount(const String& layerType) const; - // FIXIT use inheritance - void initBackend(const std::vector& blobsToKeep_); + virtual void initBackend(const std::vector& blobsToKeep_); void setHalideScheduler(const String& scheduler); #ifdef HAVE_HALIDE @@ -139,11 +146,6 @@ struct Net::Impl : public detail::NetImplBase void initHalideBackend(); #endif -#ifdef HAVE_DNN_NGRAPH - void addNgraphOutputs(LayerData& ld); - void initNgraphBackend(const std::vector& blobsToKeep_); -#endif - #ifdef HAVE_WEBNN void addWebnnOutputs(LayerData& ld); void initWebnnBackend(const std::vector& blobsToKeep_); @@ -183,11 +185,11 @@ struct Net::Impl : public detail::NetImplBase // TODO add getter void enableFusion(bool fusion_); - void fuseLayers(const std::vector& blobsToKeep_); + virtual void fuseLayers(const std::vector& blobsToKeep_); void allocateLayers(const std::vector& blobsToKeep_); - void forwardLayer(LayerData& ld); + virtual void forwardLayer(LayerData& ld); void forwardToLayer(LayerData& ld, bool clearFlags = true); @@ -243,22 +245,17 @@ struct Net::Impl : public detail::NetImplBase Mat getBlob(String outputName) const; #ifdef CV_CXX11 - AsyncArray getBlobAsync(const LayerPin& pin); + virtual AsyncArray getBlobAsync(const LayerPin& pin); AsyncArray getBlobAsync(String outputName); #endif // CV_CXX11 -#ifdef HAVE_INF_ENGINE - static - Net createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet); -#endif - string dump(bool forceAllocation = false) const; void dumpNetworkToFile() const; // FIXIT drop from inference API - Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel) /*const*/; + Net quantize(Net& net, InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel) /*const*/; void getInputDetails(std::vector& scales, std::vector& zeropoints) /*const*/; void getOutputDetails(std::vector& scales, std::vector& zeropoints) /*const*/; diff --git a/modules/dnn/src/net_impl_backend.cpp b/modules/dnn/src/net_impl_backend.cpp index e26126d86c..c9c61eb893 100644 --- a/modules/dnn/src/net_impl_backend.cpp +++ b/modules/dnn/src/net_impl_backend.cpp @@ -109,11 +109,7 @@ void Net::Impl::initBackend(const std::vector& blobsToKeep_) } else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { -#ifdef HAVE_DNN_NGRAPH - initNgraphBackend(blobsToKeep_); -#else - CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of OpenVINO"); -#endif + CV_Assert(0 && "Inheritance must be used with OpenVINO backend"); } else if (preferableBackend == DNN_BACKEND_WEBNN) { @@ -154,26 +150,30 @@ void Net::Impl::initBackend(const std::vector& blobsToKeep_) } -void Net::Impl::setPreferableBackend(int backendId) +void Net::Impl::setPreferableBackend(Net& net, int backendId) { if (backendId == DNN_BACKEND_DEFAULT) backendId = (Backend)getParam_DNN_BACKEND_DEFAULT(); + if (backendId == DNN_BACKEND_INFERENCE_ENGINE) + backendId = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; // = getInferenceEngineBackendTypeParam(); + if (netWasQuantized && backendId != DNN_BACKEND_OPENCV && backendId != DNN_BACKEND_TIMVX) { CV_LOG_WARNING(NULL, "DNN: Only default and TIMVX backends support quantized networks"); backendId = DNN_BACKEND_OPENCV; } -#ifdef HAVE_INF_ENGINE - if (backendId == DNN_BACKEND_INFERENCE_ENGINE) - backendId = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; -#endif - if (preferableBackend != backendId) { preferableBackend = backendId; clear(); +#ifdef HAVE_INF_ENGINE + if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + { + switchToOpenVINOBackend(net); + } +#endif } } diff --git a/modules/dnn/src/net_openvino.cpp b/modules/dnn/src/net_openvino.cpp index a546b0237d..77186f074c 100644 --- a/modules/dnn/src/net_openvino.cpp +++ b/modules/dnn/src/net_openvino.cpp @@ -17,11 +17,205 @@ CV__DNN_INLINE_NS_BEGIN #ifdef HAVE_INF_ENGINE +// TODO: use "string" target specifier +class NetImplOpenVINO CV_FINAL : public Net::Impl +{ +public: + typedef Net::Impl Base; + + // this default constructor is used with OpenVINO native loader + // TODO: dedicated Impl? + NetImplOpenVINO() + : Net::Impl() + { + preferableBackend = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; + } + + // constructor to derive execution implementation from the loaded network + explicit NetImplOpenVINO(const Ptr& basePtr) + : Net::Impl() + { + basePtr_ = basePtr; + init(); + } + + void init() + { + CV_TRACE_FUNCTION(); + CV_Assert(basePtr_); + Net::Impl& base = *basePtr_; + CV_Assert(!base.netWasAllocated); + CV_Assert(!base.netWasQuantized); + netInputLayer = base.netInputLayer; + blobsToKeep = base.blobsToKeep; + layers = base.layers; + for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) + { + LayerData& ld = it->second; + ld.resetAllocation(); + } + layerNameToId = base.layerNameToId; + outputNameToId = base.outputNameToId; + //blobManager = base.blobManager; + preferableBackend = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; //base.preferableBackend; + preferableTarget = base.preferableTarget; + hasDynamicShapes = base.hasDynamicShapes; + CV_Assert(base.backendWrappers.empty()); //backendWrappers = base.backendWrappers; + lastLayerId = base.lastLayerId; + netWasAllocated = base.netWasAllocated; + netWasQuantized = base.netWasQuantized; + fusion = base.fusion; + } + + + //bool isAsync; // FIXIT: drop + + + bool empty() const override + { + return Base::empty(); + } + void setPreferableBackend(Net& net, int backendId) override + { + if (backendId == DNN_BACKEND_INFERENCE_ENGINE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + return; // no-op + if (!basePtr_) + CV_Error(Error::StsError, "DNN: Can't switch backend of network created by OpenVINO"); + Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); + impl_ptr_ref = basePtr_; + return basePtr_->setPreferableBackend(net, backendId); + } + void setPreferableTarget(int targetId) override + { + if (preferableTarget != targetId) + { + preferableTarget = targetId; + clear(); + } + } + + Ptr wrap(Mat& host) override + { + return Ptr(new NgraphBackendWrapper(preferableTarget, host)); + } + + + void clear() override + { + Base::clear(); + } + + void validateBackendAndTarget() override + { + CV_TRACE_FUNCTION(); + + CV_Assert(preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); + CV_Check((int)preferableTarget, + preferableTarget == DNN_TARGET_CPU || + preferableTarget == DNN_TARGET_OPENCL || + preferableTarget == DNN_TARGET_OPENCL_FP16 || + preferableTarget == DNN_TARGET_MYRIAD || + preferableTarget == DNN_TARGET_HDDL || + preferableTarget == DNN_TARGET_FPGA, + "Unknown OpenVINO target" + ); + } + + //void setUpNet(const std::vector& blobsToKeep_ = std::vector()) override; + + + //void setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean) override; + + void addNgraphOutputs(LayerData& ld); + + void initBackend(const std::vector& blobsToKeep_) override; + + void fuseLayers(const std::vector& blobsToKeep_) override; + + //void allocateLayers(const std::vector& blobsToKeep_) override; + + void forwardLayer(LayerData& ld) override; + + AsyncArray getBlobAsync(const LayerPin& pin) override; + + //string dump(bool forceAllocation = false) const override; + + static + Net createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet); + +}; // NetImplOpenVINO + + +void NetImplOpenVINO::forwardLayer(LayerData& ld) +{ + CV_TRACE_FUNCTION(); + + Ptr layer = ld.layerInstance; + + if (!ld.skip) + { + auto it = ld.backendNodes.find(preferableBackend); + if (ld.id == 0 || // input layer + it == ld.backendNodes.end() // non-supported layer or its mode + ) + { + return Base::forwardLayer(ld); + } + + CV_Assert(it != ld.backendNodes.end()); + const Ptr& node = it->second; + CV_Assert(!node.empty()); + Ptr ieNode = node.dynamicCast(); + CV_Assert(!ieNode.empty()); + CV_Assert(ieNode->net); + + TickMeter tm; + tm.start(); + + ieNode->net->forward(ld.outputBlobsWrappers, isAsync); + + tm.stop(); + int64 t = tm.getTimeTicks(); + layersTimings[ld.id] = (t > 0) ? t : t + 1; // zero for skipped layers only + } + else + { + layersTimings[ld.id] = 0; + } + + ld.flag = 1; +} + +AsyncArray NetImplOpenVINO::getBlobAsync(const LayerPin& pin) +{ + CV_TRACE_FUNCTION(); + if (!pin.valid()) + CV_Error(Error::StsObjectNotFound, "Requested blob not found"); + + LayerData& ld = layers[pin.lid]; + if ((size_t)pin.oid >= ld.outputBlobs.size()) + { + CV_Error(Error::StsOutOfRange, format("Layer \"%s\" produce only %d outputs, " + "the #%d was requested", + ld.name.c_str(), (int)ld.outputBlobs.size(), (int)pin.oid)); + } + if (preferableTarget != DNN_TARGET_CPU) + { + CV_Assert(!ld.outputBlobsWrappers.empty() && !ld.outputBlobsWrappers[pin.oid].empty()); + // Transfer data to CPU if it's require. + ld.outputBlobsWrappers[pin.oid]->copyToHost(); + } + CV_Assert(preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); + + Ptr wrapper = ld.outputBlobsWrappers[pin.oid].dynamicCast(); + return std::move(wrapper->futureMat); +} + /** mark input pins as outputs from other subnetworks * FIXIT must be done by DNN engine not ngraph. */ -void Net::Impl::addNgraphOutputs(LayerData& ld) +void NetImplOpenVINO::addNgraphOutputs(LayerData& ld) { CV_TRACE_FUNCTION(); @@ -59,7 +253,7 @@ void Net::Impl::addNgraphOutputs(LayerData& ld) } } -void Net::Impl::initNgraphBackend(const std::vector& blobsToKeep_) +void NetImplOpenVINO::initBackend(const std::vector& blobsToKeep_) { CV_TRACE_FUNCTION(); CV_CheckEQ(preferableBackend, DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, ""); @@ -92,7 +286,7 @@ void Net::Impl::initNgraphBackend(const std::vector& blobsToKeep_) } } - if (skipInfEngineInit) + if (!basePtr_) // model is loaded by OpenVINO { Ptr node = layers[lastLayerId].backendNodes[preferableBackend]; CV_Assert(!node.empty()); @@ -399,10 +593,104 @@ void Net::Impl::initNgraphBackend(const std::vector& blobsToKeep_) } } -//} // Net::Impl + +#if 0 +#define printf_(args) printf args +#else +#define printf_(args) +#endif + +void NetImplOpenVINO::fuseLayers(const std::vector& blobsToKeep_) +{ + CV_TRACE_FUNCTION(); + + if(!fusion) + return; + + CV_Check((int)preferableBackend, preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, ""); + +#if 0 // FIXIT mode without fusion is broken due to unsupported layers and handling of "custom" nodes + return; +#endif + + // scan through all the layers. If there is convolution layer followed by the activation layer, + // we try to embed this activation into the convolution and disable separate execution of the activation + + // FIXIT replace by layersToKeep to avoid hacks like "LayerPin(lid, 0)" + std::set pinsToKeep(blobsToKeep_.begin(), + blobsToKeep_.end()); + for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) + { + int lid = it->first; + LayerData& ld = layers[lid]; + if (ld.skip) + { + printf_(("skipped %s: %s\n", ld.layerInstance->name.c_str(), ld.layerInstance->type.c_str())); + continue; + } + printf_(("analyzing %s: %s\n", ld.layerInstance->name.c_str(), ld.layerInstance->type.c_str())); + + // the optimization #1. try to fuse batch norm, scaling and/or activation layers + // with the current layer if they follow it. Normally, the are fused with the convolution layer, + // but some of them (like activation) may be fused with fully-connected, elemwise (+) and + // some other layers. + Ptr& currLayer = ld.layerInstance; + if (ld.consumers.size() == 1 && pinsToKeep.count(LayerPin(lid, 0)) == 0) + { + LayerData* nextData = &layers[ld.consumers[0].lid]; + LayerPin lpNext(ld.consumers[0].lid, 0); + while (nextData) + { + if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && pinsToKeep.count(lpNext) != 0) + { + CV_LOG_DEBUG(NULL, "DNN/IE: skip fusing with 'output' node: " << nextData->name << "@" << nextData->type); + break; + } + + /* we use `tryFuse` member of convolution layer to fuse eltwise later + * it's not intended to be fused here; hence, we stop when we encounter eltwise + */ + Ptr nextLayer = nextData->layerInstance; + if (currLayer->tryFuse(nextLayer)) + { + printf_(("\tfused with %s\n", nextLayer->name.c_str())); + nextData->skip = true; + ld.outputBlobs = layers[lpNext.lid].outputBlobs; + ld.outputBlobsWrappers = layers[lpNext.lid].outputBlobsWrappers; + if (nextData->consumers.size() == 1) + { + int nextLayerId = nextData->consumers[0].lid; + nextData = &layers[nextLayerId]; + lpNext = LayerPin(nextLayerId, 0); + } + else + { + nextData = 0; + break; + } + } + else + break; + } + } + } +} + + + +void switchToOpenVINOBackend(Net& net) +{ + CV_TRACE_FUNCTION(); + CV_LOG_INFO(NULL, "DNN: switching to OpenVINO backend..."); + Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); + Ptr openvino_impl_ptr = makePtr(impl_ptr_ref); + impl_ptr_ref = openvino_impl_ptr; +} + + /*static*/ -Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet) +Net NetImplOpenVINO::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet) { CV_TRACE_FUNCTION(); @@ -418,6 +706,10 @@ Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNe } Net cvNet; + Ptr openvino_impl_ptr = makePtr(); + NetImplOpenVINO& openvino_impl = *openvino_impl_ptr; + accessor::DnnNetAccessor::getImplPtrRef(cvNet) = openvino_impl_ptr; + cvNet.setInputsNames(inputsNames); // set empty input to determine input shapes @@ -432,7 +724,7 @@ Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNe { auto fake_node = std::make_shared(ngraph::element::f32, ngraph::Shape {}); Ptr backendNodeNGraph(new InfEngineNgraphNode(fake_node)); - backendNodeNGraph->net = Ptr(new InfEngineNgraphNet(*(cvNet.impl), ieNet)); + backendNodeNGraph->net = Ptr(new InfEngineNgraphNet(openvino_impl, ieNet)); backendNode = backendNodeNGraph; } @@ -450,7 +742,7 @@ Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNe LayerParams lp; int lid = cvNet.addLayer(it.first, "", lp); - LayerData& ld = cvNet.impl->layers[lid]; + LayerData& ld = openvino_impl.layers[lid]; { Ptr cvLayer(new NgraphBackendLayer(ieNet)); @@ -498,7 +790,6 @@ Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNe cvNet.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); - cvNet.impl->skipInfEngineInit = true; return cvNet; } #endif // HAVE_INF_ENGINE @@ -516,7 +807,7 @@ Net Net::readFromModelOptimizer(const String& xml, const String& bin) InferenceEngine::Core& ie = getCore(""); InferenceEngine::CNNNetwork ieNet = ie.ReadNetwork(xml, bin); - return Impl::createNetworkFromModelOptimizer(ieNet); + return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); #endif // HAVE_INF_ENGINE } @@ -560,7 +851,7 @@ Net Net::readFromModelOptimizer( CV_Error(Error::StsError, std::string("DNN: IE failed to load model: ") + e.what()); } - return Impl::createNetworkFromModelOptimizer(ieNet); + return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); #endif // HAVE_INF_ENGINE } diff --git a/modules/dnn/src/net_quantization.cpp b/modules/dnn/src/net_quantization.cpp index 8316687412..0add2d2d79 100644 --- a/modules/dnn/src/net_quantization.cpp +++ b/modules/dnn/src/net_quantization.cpp @@ -33,7 +33,7 @@ void getQuantizationParams(const Mat& src, std::vector& scales, std::vect } // FIXIT drop from inference API -Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel) +Net Net::Impl::quantize(Net& net, InputArrayOfArrays calibData, int inputsDtype, int outputsDtype, bool perChannel) { // Net can be quantized only once. if (netWasQuantized) @@ -47,7 +47,8 @@ Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outpu int prefTarget = preferableTarget; // Disable fusions and use CPU backend to quantize net - setPreferableBackend(DNN_BACKEND_OPENCV); + // FIXIT: we should not modify original network! + setPreferableBackend(net, DNN_BACKEND_OPENCV); setPreferableTarget(DNN_TARGET_CPU); enableFusion(false); @@ -163,7 +164,7 @@ Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outpu Net::Impl& dstNet = *(dstNet_.impl); dstNet.netWasQuantized = true; dstNet.setInputsNames(netInputLayer->outNames); - dstNet.setPreferableBackend(prefBackend); + dstNet.setPreferableBackend(dstNet_, prefBackend); dstNet.setPreferableTarget(prefTarget); dstNet.enableFusion(originalFusion); @@ -253,7 +254,7 @@ Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outpu } } // Restore FP32 Net's backend, target and fusion - setPreferableBackend(prefBackend); + setPreferableBackend(net, prefBackend); setPreferableTarget(prefTarget); enableFusion(originalFusion); return dstNet_; diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index 856441e71d..7f9f4bf47c 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -73,6 +73,8 @@ void infEngineBlobsToMats(const std::vector& blobs, CV__DNN_INLINE_NS_BEGIN +void switchToOpenVINOBackend(Net& net); + namespace openvino { // TODO: use std::string as parameter From 6cc0107693e1e634387d802929b01b240de33f5d Mon Sep 17 00:00:00 2001 From: Matej Jeglic Date: Tue, 6 Sep 2022 20:53:52 +0200 Subject: [PATCH 059/313] Save output file to local directory instead of desktop (for windows) --- doc/pattern_tools/svgfig.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/pattern_tools/svgfig.py b/doc/pattern_tools/svgfig.py index 4fa69806b4..de5da182ce 100755 --- a/doc/pattern_tools/svgfig.py +++ b/doc/pattern_tools/svgfig.py @@ -452,8 +452,6 @@ class SVG: def interpret_fileName(self, fileName=None): if fileName is None: fileName = _default_fileName - if re.search("windows", platform.system(), re.I) and not os.path.isabs(fileName): - fileName = _default_directory + os.sep + fileName return fileName def save(self, fileName=None, encoding="utf-8", compresslevel=None): From 4bec43cf798b7891b86df89237243f68e38db30c Mon Sep 17 00:00:00 2001 From: Matej Jeglic Date: Tue, 6 Sep 2022 21:08:37 +0200 Subject: [PATCH 060/313] Pythonify the 'interpret_fileName function' --- doc/pattern_tools/svgfig.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/pattern_tools/svgfig.py b/doc/pattern_tools/svgfig.py index de5da182ce..214be640bd 100755 --- a/doc/pattern_tools/svgfig.py +++ b/doc/pattern_tools/svgfig.py @@ -449,10 +449,9 @@ class SVG: return output - def interpret_fileName(self, fileName=None): - if fileName is None: - fileName = _default_fileName - return fileName + @staticmethod + def interpret_fileName(fileName=None): + return fileName or _default_fileName def save(self, fileName=None, encoding="utf-8", compresslevel=None): """Save to a file for viewing. Note that svg.save() overwrites the file named _default_fileName. From 7de7891c4d0427855f473fc296f34282f603cc8e Mon Sep 17 00:00:00 2001 From: Matej Jeglic Date: Wed, 7 Sep 2022 08:25:31 +0200 Subject: [PATCH 061/313] Removed intialization of _default_directory --- doc/pattern_tools/svgfig.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/doc/pattern_tools/svgfig.py b/doc/pattern_tools/svgfig.py index 214be640bd..37eaf77c83 100755 --- a/doc/pattern_tools/svgfig.py +++ b/doc/pattern_tools/svgfig.py @@ -34,18 +34,6 @@ try: except NameError: xrange = range # Python 3 - -if re.search("windows", platform.system(), re.I): - try: - import _winreg - _default_directory = _winreg.QueryValueEx(_winreg.OpenKey(_winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\Current Version\Explorer\Shell Folders"), "Desktop")[0] -# tmpdir = _winreg.QueryValueEx(_winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Environment"), "TEMP")[0] -# if tmpdir[0:13] != "%USERPROFILE%": -# tmpdir = os.path.expanduser("~") + tmpdir[13:] - except: - _default_directory = os.path.expanduser("~") + os.sep + "Desktop" - _default_fileName = "tmp.svg" _hacks = {} From 9074b3e98075903e6aac8cd491e66741305e850a Mon Sep 17 00:00:00 2001 From: Yulv-git Date: Sat, 30 Apr 2022 13:37:15 +0800 Subject: [PATCH 062/313] Fix some typos in platforms/, samples/, 3rdparty/. --- platforms/android/android.toolchain.cmake | 4 ++-- samples/cpp/tutorial_code/video/meanshift/camshift.cpp | 2 +- samples/cpp/tutorial_code/video/meanshift/meanshift.cpp | 2 +- samples/java/tutorial_code/video/meanshift/CamshiftDemo.java | 2 +- samples/java/tutorial_code/video/meanshift/MeanshiftDemo.java | 2 +- samples/winrt/ImageManipulations/MainPage.xaml.cpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/platforms/android/android.toolchain.cmake b/platforms/android/android.toolchain.cmake index 50b342c7a6..cc9ded5f62 100644 --- a/platforms/android/android.toolchain.cmake +++ b/platforms/android/android.toolchain.cmake @@ -399,7 +399,7 @@ if( NOT ANDROID_NDK ) __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) if( NOT ANDROID_STANDALONE_TOOLCHAIN ) - #try to find Android NDK in one of the the default locations + #try to find Android NDK in one of the default locations set( __ndkSearchPaths ) foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) @@ -413,7 +413,7 @@ if( NOT ANDROID_NDK ) message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) else() - #try to find Android standalone toolchain in one of the the default locations + #try to find Android standalone toolchain in one of the default locations __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) if( ANDROID_STANDALONE_TOOLCHAIN ) diff --git a/samples/cpp/tutorial_code/video/meanshift/camshift.cpp b/samples/cpp/tutorial_code/video/meanshift/camshift.cpp index 13965e623f..4f5d796072 100644 --- a/samples/cpp/tutorial_code/video/meanshift/camshift.cpp +++ b/samples/cpp/tutorial_code/video/meanshift/camshift.cpp @@ -58,7 +58,7 @@ int main(int argc, char **argv) calcHist(&hsv_roi, 1, channels, mask, roi_hist, 1, histSize, range); normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX); - // Setup the termination criteria, either 10 iteration or move by atleast 1 pt + // Setup the termination criteria, either 10 iteration or move by at least 1 pt TermCriteria term_crit(TermCriteria::EPS | TermCriteria::COUNT, 10, 1); while(true){ diff --git a/samples/cpp/tutorial_code/video/meanshift/meanshift.cpp b/samples/cpp/tutorial_code/video/meanshift/meanshift.cpp index 0e16442c6d..e812cb5db4 100644 --- a/samples/cpp/tutorial_code/video/meanshift/meanshift.cpp +++ b/samples/cpp/tutorial_code/video/meanshift/meanshift.cpp @@ -58,7 +58,7 @@ int main(int argc, char **argv) calcHist(&hsv_roi, 1, channels, mask, roi_hist, 1, histSize, range); normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX); - // Setup the termination criteria, either 10 iteration or move by atleast 1 pt + // Setup the termination criteria, either 10 iteration or move by at least 1 pt TermCriteria term_crit(TermCriteria::EPS | TermCriteria::COUNT, 10, 1); while(true){ diff --git a/samples/java/tutorial_code/video/meanshift/CamshiftDemo.java b/samples/java/tutorial_code/video/meanshift/CamshiftDemo.java index 6717446a7f..701851aaa7 100644 --- a/samples/java/tutorial_code/video/meanshift/CamshiftDemo.java +++ b/samples/java/tutorial_code/video/meanshift/CamshiftDemo.java @@ -35,7 +35,7 @@ class Camshift { Imgproc.calcHist(Arrays.asList(hsv_roi), channels, mask, roi_hist, histSize, range); Core.normalize(roi_hist, roi_hist, 0, 255, Core.NORM_MINMAX); - // Setup the termination criteria, either 10 iteration or move by atleast 1 pt + // Setup the termination criteria, either 10 iteration or move by at least 1 pt TermCriteria term_crit = new TermCriteria(TermCriteria.EPS | TermCriteria.COUNT, 10, 1); while (true) { diff --git a/samples/java/tutorial_code/video/meanshift/MeanshiftDemo.java b/samples/java/tutorial_code/video/meanshift/MeanshiftDemo.java index 5fbdd0efff..f07adb5bcb 100644 --- a/samples/java/tutorial_code/video/meanshift/MeanshiftDemo.java +++ b/samples/java/tutorial_code/video/meanshift/MeanshiftDemo.java @@ -34,7 +34,7 @@ class Meanshift { Imgproc.calcHist(Arrays.asList(hsv_roi), channels, mask, roi_hist, histSize, range); Core.normalize(roi_hist, roi_hist, 0, 255, Core.NORM_MINMAX); - // Setup the termination criteria, either 10 iteration or move by atleast 1 pt + // Setup the termination criteria, either 10 iteration or move by at least 1 pt TermCriteria term_crit = new TermCriteria(TermCriteria.EPS | TermCriteria.COUNT, 10, 1); while (true) { diff --git a/samples/winrt/ImageManipulations/MainPage.xaml.cpp b/samples/winrt/ImageManipulations/MainPage.xaml.cpp index bd897fcc0b..73febc241c 100644 --- a/samples/winrt/ImageManipulations/MainPage.xaml.cpp +++ b/samples/winrt/ImageManipulations/MainPage.xaml.cpp @@ -91,7 +91,7 @@ void MainPage::InvalidateSize() // We have different widths to use depending on the view state if (ApplicationView::Value != ApplicationViewState::Snapped) { - // Make us as big as the the left over space, factoring in the ListBox width, the ListBox margins. + // Make us as big as the left over space, factoring in the ListBox width, the ListBox margins. // and the LayoutRoot's margins InputSection->Width = ((availableWidth) - (layoutRootMarginLeft + layoutRootMarginRight + listBoxMarginLeft + listBoxMarginRight)); From c3b83b8354fb3a34d9dc3731ecd984d5106b5f2f Mon Sep 17 00:00:00 2001 From: Markus Heck Date: Thu, 8 Sep 2022 03:04:19 +0200 Subject: [PATCH 063/313] Tutorial for Generalized Hough Ballard and Guil Transform --- .../generalized_hough_ballard_guil.markdown | 177 ++++++++++++++++++ .../images/generalized_hough_image.jpg | Bin 0 -> 80497 bytes ...eralized_hough_less_perfect_result_img.jpg | Bin 0 -> 36256 bytes .../images/generalized_hough_mini_image.jpg | Bin 0 -> 39664 bytes .../generalized_hough_mini_template.jpg | Bin 0 -> 31486 bytes .../images/generalized_hough_result_img.jpg | Bin 0 -> 21864 bytes .../images/generalized_hough_template.jpg | Bin 0 -> 42838 bytes .../table_of_content_objdetect.markdown | 10 + doc/tutorials/objdetect/traincascade.markdown | 2 + .../generalizedHoughTransform.cpp | 100 ++++++++++ 10 files changed, 289 insertions(+) create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_image.jpg create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg create mode 100644 doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_template.jpg create mode 100644 samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown new file mode 100644 index 0000000000..6507e0f4a1 --- /dev/null +++ b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown @@ -0,0 +1,177 @@ +Object detection with Generalized Ballard and Guil Hough Transform {#tutorial_generalized_hough_ballard_guil} +================================================================== + +@tableofcontents + +@prev_tutorial{tutorial_traincascade} + +Goal +---- + +In this tutorial you will lern how to: + +- Use @ref cv::GeneralizedHoughBallard and @ref cv::GeneralizedHoughGuil to detect an object + +Example +------- + +### What does this program do? + +1. Load the image and template + +![image](images/generalized_hough_mini_image.jpg) +![template](images/generalized_hough_mini_template.jpg) + +2. Instantiate @ref cv::GeneralizedHoughBallard with the help of `createGeneralizedHoughBallard()` +3. Instantiate @ref cv::GeneralizedHoughGuil with the help of `createGeneralizedHoughGuil()` +4. Set the required parameters for both GeneralizedHough variants +5. Detect and show found results + +Note: + +- Both variants can't be instantiated directly. Using the create methods is required. +- Guil Hough is very slow. Calculating the results for the "mini" files used in this tutorial + takes only a few seconds. With image and template in a higher resolution, as shown below, + my notebook requires about 5 minutes to calculate a result. + +![image](images/generalized_hough_image.jpg) +![template](images/generalized_hough_template.jpg) + +### Code + +The complete code for this tutorial is shown below. +@include generalizedHoughTransform.cpp + +Explanation +----------- + +### Load image, template and setup variables + +```c++ +// load source images +Mat image = imread("images/generalized_hough_mini_image.jpg"); +Mat imgTemplate = imread("images/generalized_hough_mini_template.jpg"); + +// create grayscale image and template +Mat templ = Mat(imgTemplate.rows, imgTemplate.cols, CV_8UC1); +Mat grayImage; +cvtColor(imgTemplate, templ, COLOR_RGB2GRAY); +cvtColor(image, grayImage, COLOR_RGB2GRAY); + +// create variable for location, scale and rotation of detected templates +vector positionBallard, positionGuil; + +// template width and height +int w = templ.cols; +int h = templ.rows; +``` + +The position vectors will contain the matches the detectors will find. +Every entry contains four floating point values: +position vector + +- *[0]*: x coordinate of center point +- *[1]*: y coordinate of center point +- *[2]*: scale of detected object compared to template +- *[3]*: rotation of detected object in degree in relation to template + +An example could look as follows: `[200, 100, 0.9, 120]` + +### Setup parameters + +```c++ +// create ballard and set options +Ptr ballard = createGeneralizedHoughBallard(); +ballard->setMinDist(10); +ballard->setLevels(360); +ballard->setDp(2); +ballard->setMaxBufferSize(1000); +ballard->setVotesThreshold(40); + +ballard->setCannyLowThresh(30); +ballard->setCannyHighThresh(110); +ballard->setTemplate(templ); + + +// create guil and set options +Ptr guil = createGeneralizedHoughGuil(); +guil->setMinDist(10); +guil->setLevels(360); +guil->setDp(3); +guil->setMaxBufferSize(1000); + +guil->setMinAngle(0); +guil->setMaxAngle(360); +guil->setAngleStep(1); +guil->setAngleThresh(1500); + +guil->setMinScale(0.5); +guil->setMaxScale(2.0); +guil->setScaleStep(0.05); +guil->setScaleThresh(50); + +guil->setPosThresh(10); + +guil->setCannyLowThresh(30); +guil->setCannyHighThresh(110); + +guil->setTemplate(templ); +``` + +Finding the optimal values can end up in trial and error and depends on many factors, such as the image resolution. + +### Run detection + +```c++ +// execute ballard detection + ballard->detect(grayImage, positionBallard); +// execute guil detection + guil->detect(grayImage, positionGuil); +``` + +As mentioned above, this step will take some time, especially with larger images and when using Guil. + +### Draw results and show image + +```c++ +// draw ballard +for (vector::iterator iter = positionBallard.begin(); iter != positionBallard.end(); ++iter) { +RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), +Size2f(w * (*iter)[2], h * (*iter)[2]), +(*iter)[3]); +Point2f vertices[4]; +rRect.points(vertices); +for (int i = 0; i < 4; i++) +line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255, 0, 0), 6); +} + +// draw guil +for (vector::iterator iter = positionGuil.begin(); iter != positionGuil.end(); ++iter) { +RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), +Size2f(w * (*iter)[2], h * (*iter)[2]), +(*iter)[3]); +Point2f vertices[4]; +rRect.points(vertices); +for (int i = 0; i < 4; i++) +line(image, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0), 2); +} + +imshow("result_img", image); +waitKey(); +return EXIT_SUCCESS; +``` + +Result +------ + +![result image](images/generalized_hough_result_img.jpg) + +The blue rectangle shows the result of @ref cv::GeneralizedHoughBallard and the green rectangles the results of @ref +cv::GeneralizedHoughGuil. + +Getting perfect results like in this example is unlikely if the parameters are not perfectly adapted to the sample. +An example with less perfect parameters is shown below. +For the Ballard variant, only the center of the result is marked as a black dot on this image. The rectangle would be +the same as on the previous image. + +![less perfect result](images/generalized_hough_less_perfect_result_img.jpg) diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_image.jpg b/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..06f53823b2bbde793308cc03fd8dc406225b3061 GIT binary patch literal 80497 zcmZs?1B_)&*S~$*wr$(C&FN{|w(Xv_ZQIkfZQHgn)BnET=YI1(`SMj#Sy!#BTJ@`{ zvy;l+b-q@=b^(ae;!@%OpzlHpL<<0X?OF7Sxm%b508&!a07w7;00Te(0{$OL_--M7 zGyJ#B_^!x6AON6m3;tcPfx!NU%f1=sU#|OR%70^kznST~FWk3n{jSX4jQVYdzbgvx zfAaj3%OVW)fAXwlFLga6q|1vW)0Dw{Oe;R-aA^vAx|Lh4+(SP;*m%rTrkpE)PqJQ_|dt9)8{kY#T z;Qti^`Ckn6Ukv?U4ErztGwwfy$^`%r6h;7yezWm+{qJ7_GdCM6HxtMIuaAb2iJOrT z0Qmpf_wU62$mvk)9-aI`rd?ptfZ*2BJhndn~knLKJEE| z?9asrq@@Z{J1}Ldrr90|q4y)4pR^l23_S@(Ake}F-Kq4X?xXU06&SA{i>^%xW< zBCk&!)v0B5KHh&T$+s>B_X^b3F2eL{!P*5ziGzFMBxyI9Q-3HiSR%0ANG-H&!)4Z6 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

b7{Ol**)xz3)Q@v0fIRkr5qfvy5UJ2 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

H*ZXf@ipu)RJNS)~t}nZxUH-j!8JV(M@am|Yw0 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg b/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f6bf315a807ba6aa636ac9984f4a8719159b225 GIT binary patch literal 36256 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.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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg b/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bcc8b9e4140c8eb40a053bdc97d2a13c186d5b81 GIT binary patch literal 39664 zcmZs?1B_)&*S~$*wr$(C&FN{|w(Xv_ZQIkfZQHgn)BnET=YI1(`SMj#Sy!#BTJ@`{ zvy;l+b-q@=b^(ae;!@%OpzlHpL<<0X?U{j#xm%b508&!a07w7;00Te(0{$OL_--M7 zGyJ#B_^!x6AON6m3;tcPfx!NU%f1=sU#|OR%70^kznST~FWk3n{jSX4jQVYdzbgvx zfAaj3%OVW)fAXw_ZZ-~XMos`FAS3WUYJ0zBW&g{g-%K3- zA0_{&rTCYV!M+=D5CG6`!^q6S1QPcj<+I;Gp#SpWHzR&C#COc=HzWPa0Fduh`j;WU z87Tfgd4T?zaRT)J=tlhZ11J1P59T+6{Oc$DX3&3`@|(f_jbZ#|@PC=}n;{bZPcziN zjQFhs`rjDA?-=g{2!OsK03i9zaS8v?S@ylE@&9t&Hz)kdt=|mD`**%fjEq2e|F=89 zdH>lFLga6q|1vW)0Dw{Oe;R-aA^vAx|Lh4+(SP;*m%rTrkpE)PqJQ_|dt9)8{kY#T z;Qti^`Ckn6Ukv?U4ErztGwwfy$^`%r6h;7yezWm+{qJ7_GdCM6HxtMIuaAb2iJOrT z0Qmpf_wU62$mvk)9-aI`rd?ptfZ*2BJhndn~knLKJEE| z?9asrq@@Z{J1}Ldrr90|q4y)4pR^l23_S@(Ake}F-Kq4X?xXU06&SA{i>^%xW< zBCk&!)v0B5KHh&T$+s>B_X^b3F2eL{!P*5ziGzFMBxyI9Q-3HiSR%0ANG-H&!)4Z6 zg6#D-z8hMK6DECGAA2aPS(`O?JXteFmUuF$Q+0*#%;?L#xP%B}>*97cM6|Ox6TRFj_7*4!m^Y&^;vR4Yiu_ru{M9R+`~CS`5maWKFJ|o^N^#)`3`JsSKrI z_Q%=vEOnligL6nfyc27_7CqE03Be9u82EHac^$*A>!Xm07@LFdb90;wmX?*SV`Js> z96`Y^dLXPG)-3%>mW@T`7-VBBSB_iJXNr&*UxxDieL&h^_y zBd0U=a<#Z7$}>MIwwdMu+hob=B`klKvZw0=uID@z;L@+gX9V! z){<$=uEX~ivx1NpgAAfd_d!Gs76PCr1{+QmIylKi&nW!&Di9FavamqK!b6LIPNvGCWm@EB$a+ur_I{}+wzRe_Cx`EwS_Xbv)T*lFJ8(!#?QqyH+WVIAu z`zMV9ZI6w&zdCNg49FcS1VL+YiK!>cb91Fl9mEYFa4}b}(gRqr5TSW}`(4(W9_N?^ zH1k<-_t4n(EZ*GOnq`wI!nS=hk6qmo3UBHA96Y!3i)c866EB=Y9nh_&DEy+54nQ@x z!;86;^4>E`%IWOo552L7?i@PP6o2Hlt)7fHs}OxnPN1p~bPD>L3hcnaQ^<`ws;0sU z!&N|5q-YCqVy1H%+|;=^P6iYL1W;unifx?a5J(p9wQOv7vM;yx02BIgmsT}+PYoqz z=^&1q>fKeFDj_yI>@wtu`iU-0G!X~lpeCZ7O@b}(e>RWqi2lxoM++vpnjFi&;=vz- zIl^0(c^N*_W`Q$Ab=J}MAT-B`H5E*~6Rg)TGtr_NUt>ntt6M!0OFX_*8&(A)=wxK7 z9yU-_siosmpJzj<)E|ZXAU#xm89oS+k!9tSsB9CHWj%=|x=C7>&r5kG3Pmv|)Y3LM z=)FSCBRkDb(UTX+Yy48Rce|dGiuW+?dHq3d7&|M_T6+g`8-K`a zUwOO`$~tMX&gbhFYz1*)D!^ceA|F+73Z?+(z4%K*9j)KA8mpg;`4{oLJ`QGKa?Lyj z+gQ`u7Z8OI_OixD=yuM_ht1_K+ig`ZaRffH7Iwakm%@pA>N7m!dXu@AjSW0|oG$d1 z>{zJa+f03%{U#goO`=XAhs*ZZ-5Sv8=$)X=ds2N~YUsIoj>t!0pEI5U1bqXE4{^;A z;`)mB>Tlv#mQO3dGN|@Udi^;UxeS%4+@@t(fHEP}O$35DZ1A}&wHCbuGu1v91FjLw zqYT`+ZxWGG*1@A{i>4ml)2HWJ3ODg+@>g7tV#Dbi`8yHeVOlpR1}XX5Rs@7QyYdUK zd2_f7Q9VJ$YBQYRI0hqu#vc^U#KTe}1~Xt4CNlSDW#G?}iSi0e4@NWMiyNPIdbtMR z1OJUV5v@POO#|bv#0TPc#qOc3(EdiQn+`|J(Uj{cDJrpBZ^tTz42&VU$-w0y*|ZoJ zypNPfuF-UmJP8R+6^{8o+@n1@q!VUZ5P}Qu_jzXtj(>&PtbO7!zoG2C>}R;n9n-9N z{1ybWAosDNAMyd`wVBBu4i|gIYR=Dnu-S3GD5j(Wt+Nlrx$$Q;f;k8`An{I^dz4QQ z>=iDy=99xlQM~v;Qo~D?62pA*;>T04OO+*VZ{RHELk6hGR1SY=B`(ZC@Z-4o@eYRu z4I4EAKJxt-M&BOFh90_%byD35rb}G zik8|$q*vR#m5_JJZDB6f)AGa)+^)?sR$Xz1w@2QzjzkF_otV{iN^MZD){B!l$%E4z zeIRfq;uHJ(QF6E#A!66nWW-M^q?G1PnWX33c5l9Ye1Ql6@q_*IY0&TL#C1dvWN!UGujPIP*xURN zVH^ff>hjSQS}tsuo%1aR(EWKn)yP*oo-s03ukz@CX3RgSjh>bGnyC%)n&k z#=|o_^a3$5jrRHGSG5|5timV6prg2K-AhB$t{~ZcEA)z1GGH~2MHTJUTpGCWm8$9D zOX@V!`%PSYp4J2RD<&=vvoAQ9+|><0zRdE&Cmb1cyXN zpn|@{R0_il_+jKQFP~_-N_>5{S}PknA8HO|c!;sGu52i*@|~%Ou$hsWpx2Cj=@E`f zLxH7jD1`JgLVTT0zuXT`5n04>oLzWT(=N8%k&fO3EK0Cl0a(qIc9#KOO%;>V$$X~x zQNK;&-bceQhL8&In2qLM@ORc^UnA^M+z|q`W}c$Xj9!hl&EE`DcwAqSGtGtCIMVr7 zQFmv57lWz_&D{{8#I^kIa?C>v(eFHp`IzB9;vkC{y?r+v)cOMO+Doj?@+*f;28=Ot zK?3Lj<3Ntu4`mDwZT$sAE{JW&NzZy68h!kEir#U}0fH$M`=eV*;W#CwNkkL0v${{b_lT;KN0Utwd;m`Seg|hBy;ZR#7jqusRTuZD36o zEs=k$c$BN?&6h-S)L*A4#x3wdx~~)MEIkCO(m@}iew<1>$|43Kk%GDyo6(9K=X*-p z)O!GQ+X%flBWzxbRr{4Og{&;1yW;z|9jF6^Y(2JxH-CfIa&Wr$Op%d6Yyr>fM(Tv* zEY`LS_^ztr9Gm0OsALPss`Y-}?p*KuwN*E_98Ox=?HoyDE{5;z={-otolCFEH2%eY z_c8H+342#R_h+k@v27ViOhYF%U?pyIm`nhiP^YUGEX9PWr}Uud60RVZ@o>Z}x0DpY zezw*E-OnJ+t8_LZQUX4DkZF-T9hkDJnp6SSLMen{36ciIIr!FpBM{J(me2M@mXE@m z+$46S&!)_g1Wvl{)NkR@X!%@M71{Pex#EHzW2(UyTtV__y`K*K6UaLzLUSd56K62^ z7_*xrfd$qJA!kOycjz$l9{Lw^AxSF|pXZ}k>Zf6$7HI!WM|paQElHy7NDycXKDh~9 z4GYWQq%H;x4at7~puEzBq*t-lura;F@G}12;E?h|4HAby$1@LFy_I`E|7z|C^$j0X6%sq^;B-3eX|BO3f8f@+{1N){UbNzk(|P^{L3YudH#4HcX{ ziXdlx_?$Nv=Qo9$6t3L?oy1gjJ+~Mr&J?1en`1O>L5KUMfGb}cT(C52DOIYTc_Q@@ zy&^33U>?T3$eNqEC|oQ!FbAHG0j3>h@h5ZztwQVdqiL%cuXvT61QgLG1FeCd73nVE zOP10`%1CY@w)#09D@3J8V@H`%Ltqw- zeqo56)jd4si7!>l?^UG;M+(J8BJ~{|x-N#~^vnTdwj!wT*~)CBaLi5oo2x;sBbpV^ z=Idf67809|bvdTw&4w zN~wqDPn-6C%Uuhcf*r^gq);XV`NS%@tX2Cv|U(U`&0q3T zJ`C2!b-R`X5)N_!hf@rK(&>>|H@%i`F-2)-VY%_fPJb!2V+j7_gLx7>7aX8tKr(tO z=%qAaSg?=)zvD}b! zEZ2xQ15=R_bYY1*{d~4LIhCQGHuG)#KF*)4mVFHL#ck3hlk;m^l}`tE+yTcmC07tn zQgiF(dY5Xy5l4dmWNnPn+*odG;$ptzb;S>|NOY^HMbLLOoNw18e6i*md2yQkIl7*V zllvJk`UEmplGL&=wPs`NLq9Y*We>FliampE^f@<#FLtX^ks+G|s}Ed6v)nJ{kF~Z^ zSy+w#v&c(F=e^>YjbEc$D+C17eFrs^VVYdNzAWJ;ioV^q<=NxL0|uh;=ng--?f^6Q zXkkMW3w-1~`X~sm)mvC*YFG*d-u~$JXccPU>Mrq+jQVZl=ZO#{Vta!TB3&VW5bd9WLNd3LbY<(s!+IDcUw2Z&dua0gDC#o zCiQheJA5fPn{T5ZwPpAQgCfU_8?H%VO==`rCa`|;{%!~^X{7hfIu{e5{)NE%I%f#W zliY+FbFVIV6L#w|Ff0Wk=onEnrtD2v5IqS0ba(=L$LajDbnfCT28GjHZ3zxh1Bmr% z?ZxVuZ*bQ#%gL6s8+}0;Ee|3GhJ%f(qGp(G4bD;o2hWc*%K5KiJDT17mNxSsaZdnQ zsxKrJg_{1sv&*a%Y7|=#pZ`Um^`?H+j%XH-Lxj5ls2qHS!SZl?K?JG-UIP*$)clqM zWwpm44^0`5%?xZGeahDC{C-rw8ol@Gq-_oifm=?vngDTMVx(G#ZCvqqF7%#$vGmly z7TSZ@$e=Q;7R@K9&zziRb3M!&1XbUCxAWPWiGaf|_yYu8n|$d(;{C7aK^=y0GuDkE z;@!Lsm8KC9XPwEWwD1rT&Bk`~c3*lf;xCrNFM4EoO>ct1L0O3xB``ApwS^Ko=MJ6y zA1W|Wa!8ELp3dRd+i0}E-e56ih5I|D!p!<>Pqf%fmIE4P8Qz%Ane<=Ig*qN{$$F=F zMk8W$xiu>JnKW#npVnTMQCcLXQTq5qer?5s5lmBPGb`(lvhg;WoGvm zu8ZcP$D$XXl6IB_H^` zW9ZXi#vUqZUChGAqASNrBq!9=uSAzBP14u45zD{WuiWwqZ6Q(Mlb<1Nw8GR*N13pG zcI+Rt*(U)e4wuSuMbvR4t9xq?>d;EnWD35k|dAp9XNnWO?^Zxs;1>|;rj7@(f$70Zz2kbwHRXH($9Y6J-k>+i&*{O0s$fP#7NpBNJ|}2 z`Rl405TqZ{qxEb2fg33(;y7|y<&Qfy^5;19_3yjrlj$@oVjwnbR^@y_!-9f8gTW<= zW(S=`aZ@@;L8_M={eP_wm`pIP7JC_8c_8PNu>4igZ{!iKew4@kZnLux2l8S))R5OX&6=6q4aNkF2~k|wR;)11nc zhNgSaNo19KX#>8zj8Tcz3oM|p$U?KQ{SHel@TLKcSX6SylbytILtp$Zb)#%WsHpgGp}`}vXLCjCm?4e}bwAk>hMc_1kCW@};Tp$I z+0gFlStx!A1JndLBDQvKOyq3kdLEr`($~pk zdi7oDu5S0Fz02ILM_!B5xeq*2jQwJ)JNu6mhGcYD?;8bHJ%f)gQ^y?3lzKD?npd^T=ziJM$;^4Lh)(Kz7F5 zTquAzqMz+QWozDtyKd0^H5_BJ%u{&Dj&Mb(gyWq3^^DZaQ#@dHrzWrLs^4zS3+Km1 zwdCW^OBDf%S;yRPtY4=C#&3m*x`mdtHz!@;#hwdTA0ZkiO>zY%B0I!DvX!i5x;IIa z=ub)9$-~%FNBm8*n%+JKMYV#id&v_)9@qzKMUzflMyyVGv~R<5tY-U9v$<)sH`Mi^j&YN5q7`@yp?E*wb^lqqF^>;^!KiA$)BY-m; z?`)65%`g#*18K#xDY`ybaqL?lojp-~R|?@x)cGc4y&kf_-V?%%F&T4KztEg4wF!Fi zYoC^Z!5<1Dw@qqPOu|o%4hw(u!Opbnaqfi>7EqaV_?-0Y4yl&820}7E3AekMtK(D% z{cLk6nYfBF_Lw}|m|wrMo&$x%h&B!NpwNT3Y{1uHd3v59`6To%inPEDN_(H9$4%4N z7dA>CnAd#3EZ4&N0WEdo@1c|~!I4D=;olT_Jx`hoZ@Mlaag8#q;+MQi6a%iSc)*r@ z`DhwjXlqSU#;tJLu!Z;0qcQ`q7OP~vFr`tHZBo)&bi3o20OM2qyo2kAcp=9J;|Ap1 zFMhnIu>DbV2O^{Tg9^;f{6w`Qz++l4y&sGv@!_od5&E|fC`5FL54p*d{LroxYth0M zW7$n&zW4E`aG|ybQ(uzV@2Vs_We13e2cKM;5CCIG;D&ISX5VE(z#TW1v}+d&=HEe8 z-*ReU=c*B@1|Ow}%ruo>m0WQ0Pr!aE*EmI1Cm+kwi)gNm?zI(5sxRjU+jB*^{uk;H z@iJnPgh^5(@^;*n!YSAB$OS>tWiC{&M44JrDKqBT_hhh8pa!1BQqg~B;)87r4*hqT z18ft_GW!L>WybHcxq9O91FdExms?aNu1H0=D!O6rp#sH96ZTGk|;i6JaPQWSIDrhk^AgAu;a( z?0Qht2-#r$k8s89-1+RtFTyZ8N9!Bm5dcQdHn5aVwmO$NUvL>3EiBLNSpvpF;1)zFejB;({v2yK-?YHbU9T`7^jZf~>JuBK-sd z;EQP^K2FgB(z>0r6A{wxLF0o==v_1x{+)@|G zek3nSW?n?bnE~HP2+Uh=cvz+XrS;&5x2!%blg}3${>fXrko`K6h6#zy(=BYlSdk<~ z{DT9ePB&4g9C1(mw*C#MX!Hj$fg5-FND{+}Czmbr6re1}wk9R6s;N^u*C-oVi{d$Y zE0>&v_VYH~=4$#BuQ#v9m$7`x)6v3Qp0Fo%h(qu0;?PY3x9@J-9v#(53WY9#jWDdA zOF6KJC;Pv5?eT>(i>YoD{m{e4)xb(7>W?t# z&)1#$(>x!jYn86Wt-muiZ!{6?Iyg2lc;a*)q~+-a`isF-aO-1K0|ev_m27U0He>`% zDlA^^XBRUf(aKfBeZXpMGL4$eP07+PaDPO<($B*1&Md8JAnGy@p`Qa_#eNx$+Gu=hKU}p!&ow#wI1c_;kk&ea>bb^t zTS~BK!OOsHt58!L&KobVhp$5enAs@tiqz?$Q(ZI(3GU4ttNf}cy$#e?e0QCTBv@{j z;=gFFF_g#=6BR;)#?E&d{}{fQ?EAX6{9I$_)Iq)B%eb=@OK|`=KBR|#r8D@NB~cy9 zrF1FZQO%0)w@U9B4OnhPy^L(Wf$xSuDyrH|uoUg&WJ z!^ROjQ3;OL+D6+O3H0mMh*t`(=}dB2(nun&Htx#I-TvLJPIuIzK0=c(MFba0c#cc7 z@Ttma&5#$FwwYu8E$`{3WLl98X;*x)${o`42?ue69}v}JRbOEY9g2P8!V+Js*V{-h z_sd4lY{1l$olq7bM3%5;UOXXS@CP$L{>f^1r`+P&i=vkSOjD_B>b~%_2=hx5CQ8{o zTmrnDUI|e1cuypvTvO z4Wdljb0kIOv(eU_sZU_Y<^f)^{#sj3z`lQerqEZMMqcwznXW(2iTXEO6R3ToQK6rWwe|nS3~bv1SHiMWBvJzk}h!1h*DLO&A#H)gNGrQjfXP_~C5dh5;4FxeMP0x@a2%9!qWzH)AS))|q7O(L>p=EGho{4% ztz9LInhX_b3rZK0zqC^vT zX}w7@9})P3(t%J`%|jD9>jc0l%N?FsKvIcJ60sayY1OH!ut0lKY;sjwY>hcF+jdMk z+}b>>;FHeL998m2gn#9rdK&g4u9)a5{Cu~~L8t=umDoF*tu#W5Wfe2KOJ4!5xoC$m zuT6%iMz1G%x@K$$(5U-b;l!z-_uGq-UkW$sIbWwc6)+17Qa$t0G5V#|Gb;;#2m+lR zKz$Ac*MQQXa**>HBnKCHlqOtemt2qwdl4*p>c&`hj7aQcAr6}w^YGr=H~;Hoqs!%J zu#IM4ds%a5E*WI>PP&{D5SizNNJEJKXV5Dx4q@XwLrxCD;q)y|MqMd9ow_QnX7)c?dOKoV-uG@>$XJT}{&JlBSBt1;Z z4t&UzB0xKsV&0*%F{m|%?+I}QlCPydv*G87nw^iew!)3ipv3i`kRUw`cCn*}%QkNf zCf$b!UO{BM<9+r{``W7uw87t9Ubw8GG+Ve=erdoo+t%wy;<2pEoEvw;Jty{m-Wr$f zefEA>)ABkRVhYewN~nRWm8(z8G^dfrOXrY>S{u~t-HU6)oalPVDONSs1O5S>t zaq-Sy(-Qbz1h=kXS$PLc&Jt@$yclk}jEOIP@8>&=?m9tx1#+ON!xeVJ!j#0Qk)6f3i^+Fxbi&Wa+MH7SJpK-{pZ!VO z>uA@N-myUbYqB%;H#8-W*s|}vmX9862Z8L*YCm?=ZNL5zK2R-G8Ov2pP zVAn>TubheGbO+4gN5|*SQhtwzd8=zle;@2Cn>j#hDfNcZ`*RwlwLPRDP~nUv`YUOv z4N)=y9|{3yGTL-T?uGd{RXZ71Qs3xd=Oo;*3d(S#{Dn;K*12L5cwzLsZ>I!nE~Z&I zpRe4@mx>N$23O$IOFYp`@XW_HSwBr5c=c4ND^M7NwwRL_ObK>&2ryNi@eH;NQO9&p zE>&OBwBHgTuF?yxZ}^C9E%M;H%_iwFwlXQ9P5)C%Sm?H76)s8{oJQ2h;p#Gx_8k9Y z5+ZG*NvMho`wjp4g+?7#8~Vaxz(KU%sX>z*6-v$MR`Qn&T zOl}QQj`S8ND^(`0G^!6Ad;XZ?b54?b9MNwfnl2R@0j_BzHf8|zf$0 zdMZx&FX|Un56EkiLw#;VI(d$vr)fVob`j^|+B##idP_!Is2JUTHbUG-*E|4O{IGl+ z!`+jOP&y$W3*oynImq4=TQ`}U^-uN4-!Hz{O zF{wruJIX7*$kbom{U!NswOOG3B7T@1YgsF111F;fRjFDX6 z->CODD0IthPHEuxQ!gYRmMGPC;1&-IrGR~F>z2bl9RjM?;6!W$A#R&^;?~|z9n|Q! zl735YS7K=8#(#h$27TqL^;_L$#!WoIUr)=MBTWk{N_Ls&Efa+g>fG+Mh8AA4KG=@F zRFk@Pz58cv+1(*Pc%povhG20@IovV-z&h(1qD+M&i<({H^QOE&5uWKhC|)aSSqT^T zeBrAcF$DlWRg9Twit;OO4#DJ<(QT3SW>0Y}o3qT~H}2@hedM3R?Q0=-OX!V9$svu0 zmkw4g6)MjLU*wa}Ar;RUs|e@_#TtqxyUs{JbH*Qv5z^>fCFnBKP!oOJj4$2&rBv?} z13y(>)-SsF5Nh&0#YZV#=+d8?=bWh49C#bZn+#z*P(eEzK!ys>J!cU#fe%jB7Vw72q7Ud09MeSuQ9;;Re(ZvROHJv7? zqwWShO^i7@Ok11!%Hsk+2w-&3Wf;Vpp5X@VEtha=4XOHt9Vj$4T*}~9R2oSks%wz! zVQtZNN*t|HQDj}0XD4K~_L8}rEz`mG`1-G zl}6_YaK^MU3$%c>KED3(T+a>N>h7{5B39k`vUFj=xaW%Cu>#T^WT|TPY+#8bJ8DEK zeAw=g4J6}Gb*Na(-uf}iP2;a1#N3CTanw8RuJ$asUCkp%7jO4^t+--^*(# zyJu^w_l!^~bT959@PeS@s-O;!8By!=6OFYUpHD`}#GR7KH|bBt4h6r}#cWR=(6VGU z4Yjw^p9l-Gz!{Sbc=8-7fBE}>4Ro^)UF|Mp=s!(ijP-G%XlmgOkL6ZSG%D%ihu5KT zsmZ(Wc?A!zP7B~TTOUShUdd62-ULDGEP8??2ejGwdsoh3k_5Jdklb(?07kh&yEajY zj2vw;VfQVAj|O5j(9cF&85V-A=6gCOZ$6w$$M)D~P?{y>E7i>G1s zlV7S>AQ)k1h`_`us{NKc*VBg662ZpG^gam_Hc25o zI!_~s7}E!)eP5DKG{-hP<|+1wE})iN(mE1vh>&V4mdKC9bxQ^f`c_hDZ8*KwRSM$h z!+zq*S1K=;y9S<-h=yN?ksf~MT{{)oj5>-mJ^!uMStv}S9;7vYmV~%d-?*#uOQ1*6 zcz|dp*FdcgCo5Vo`77S|elsr#WUBm}F{Jukiy9y018~e)()rJTtDb&f= zUnl0mZ20Mr_U=c8arvqnv-3IG)5avXilbd=1erx#%t*@7!Dq9RX6xaFH$jYcUw~iX zu*~I&n|rGz-ZuY8_V8Wzp*RIUdft`N^#cOOvS5?T^N8t27)7AC$lBB6V}=-29H9MU zpGTWqa1%imrIIv!h%6sxX3n)e;DFaINLo$wMF*h=?k=u zL%rz~8n&yx`^6&VY0uyl9H?AK44M)YF%bkR+u`mm(45Sz_qJ9OV_(8&C7ydqzLdjO z<*AM!JUuppaYoL$89vB;8t-76Nn$;Z#zOgG2V&J)Oy@=(ml!6Po(*RHZFcI-jYO?w z0U>3l)MI~p-z5Aj%O4CwbOQKNw=)8Phrx+-DZrWv#An45*YL%MiJ`R$>2LSd{{B?4 z(21U8+Zd4aED!x~B;)9>wH%?ACNCqi^>`E9$$3DABurG&1?6tMO8sd$U`^y$keaD* zyXWWVky%<>DgenZ5?RHIAGE$pt^pDse*oS= zK6vp^54nYemNUM|`ZI;i@X7HT1y{Do1;>&Ps;#Ng=_#J0P7#GkE@2t=bRK{Qnb3ly z%*WJoNVtlMGd?_%^W2Xftys;2ZS+8eY)u?RwtmYN2+z5Q%~Sx3=As6f3p3Y36Ldx- zQTM?8Q_hFsqiYv$>D`iT1x`3sn>;)1zV`Qx3C_2jPoUgGJ{EYoK4u$esP6P1>tIqp zXDSoV2u)2F!ft+9I*OJqZ#(%Y7WZ#yE1j|n(B(VpVm{;k=H}6*MnnL?d^%?mvj>JH9ENRbFa9QHCS_?r!DE4}Vg_H6EHC7sl zPlScgOKpj_pq57F$#Yp5Wv~DG!761*9Hn()e7Md(P$NR32>Wg=AC=S96sb>eNGpkp zvdQW=MkDJj<=_a0d5&~n0hU0a#fN!2E^c~^I$$uC8*E9_lYJr7#e~^iXs0S%g(MYg zpFqML_g{MgPGdi2ctQrrN)5L$C>pY|A7 zJohT|SD;ZvCI90LtlN>x@aY#^Lqy6cOZGKudXmeYziIt7zPpKVW@SW&v^Sla9*aH*Mf4m%PZ(@!qbY8T;V+`eNdV#tynZZ>K%LdBxyOf;|6Ts zAwG;dk?HGGRgFwgicTF8R~KcJm9g$%ThAxMN#u&sZOYoD*S!V44O`LEJOGk3D|@O( z1`3tO{NE^rtHJUnN(aJh#G314!&xpc4u;}aFpY=?yWI&gjA-3u-oxKV%T6Y9tk&gT zX;d@QQAxfw2d!`lYT6MT49AjH2Ag;L1vPhXVW%EL`1^qfJy@4Z*oikhh%18q#}Ebj zhE?K{aQU&mH@&l=x{(ba2)wjOF8I@6*Z%6HdMvC{`(yOdonpnzy&BRMbqjfg0JOoK zi72>@Ih0^?f9Gi6(CQORX_Q+=4Y-eAu=!z&9Mf|f&4Ai$vpvp9V+mgo-9fU|Mlk82 z6)@AdS#BFDG4k6mQb^v`&9SAW3Mm@nvCrifYn(1d8ZoqVsveEVPZZ;`VQBy|6my|Q zz>BP(c8_{@+_?VJ!&WLP@J*`(JK?)^zPy`;&KzwB&>oEc;=wjwWXo?*2g zlv0{BQjV0cVst_ncM-u4vU5P8AAKAnbss(m+brnsW8F>4=_*WJ?M$kT z!7EFZ*-lwNzyk#BhQ#Th^)(lJG)8Z9Z5TA?mv0?X z#rUjz0GPd=&?W%YuCAq5iwCcni9t|&0uO`ZF%q(i)U4?=K8=-Ji}lK=IE@^ z$OPztVqgUl(Ja>-#Ol+;;M7%imiA(_)4a=b?zyWXI_4;|WeTfxl^KTWzQPITU2qm5 zK&GqR>6{TOw-5Oq)FKz9B=<9m678|NhPA7a+%8d#=K4j?td!AS$TN4WUO80s{s@yl zh4ZQz(3PaO+RtRp^FEOoFM0DsJQ}YtJfW-RBEVM2rGvI1VM(OQO`x_d-DYn9+=XS-@I=ag)z|k(vDA)r&wj~6-Wt;)<8Of1o&t}P9`~ix z))h9OhP*)=dxW)Km}q5=?p=Pix=S+Em2K)w!;0W@VNBs7p1WAwM|J*#(A7 zdF;`B{n%sHNwPi6pSmVxq*IU;y7R~i%&}2f^M_g(WZ+>b8I6uxnLgDzbgfM}P^UC@ z9*FAmD2U6l1Va8-=1u>6nSrV7vO_^qWWd`!(9FZG!99v0Cmxp0g&&Lo%QWE(0Tee8 zMv`U{WKLtPnDsl*18?}u{38K>eM_QYE-#mB=q>%4QTybHn|Irn-~Pf2Go%y9f*_q> zIfi`v_JYi+3Q)FgQN}pMf#0fb{;=++4^F=|g_cIDB#S}4av0Fdb07H$?#`5YsoWtg zc>ZlwX{}6#7yOBI3JPuaLE}5VN*yy|^m*A7KJxd6{}a9g#P20 zSq>bgDJT4_vn^zvvbmNNxmtrf7LX`vHH)0GGk#`;7@*=M;{w3S@xg*=Mj@8$utR

w&BI}?Jl>%y*v|67&7xl|Z4t$Fm#PBPwpNG|vC!=j4o7?*GfC?+e-nCeqBUiU6 zkb&w#Pl6r=F}iP+D(J(8-j}h260E0R z`27qyKuMpAKWO|I%a;=)bU?G;KOam0HOj}Qf~;Q*7ktq?^u(NtP)ZQj14UU-oO4#n zHbOf%*herrhI8Q;6!=|6{0qU8kt8reMvvsnwliaU$`wJb0z;v@FR17`UGYJ`j8{{R zz}yhA2Z1LOuFksH>5N51VKj~-h)4uhc7nM>Bo8T;oU4+p`ry_h{sea`d;V@}o~c#x zIn@$sPM)`Q$@`uj-KZ@L{m#%^#2`lVK0vmLDSlsrnZX&H@(D8&84EY zRn+Y-SS0vil2cKT0!&1ZGny#YUcxrdap1NcDyVejG2iL$fIKBRdJ{&Z)g(Hcsmc;r zZqqLGUNm*{>$B5W%)hZ__n}COUwFrAxQ|WcQ3OKGNbISUX=5~0^*xE%8R*1Co6X`L zCq4T2!-C3HR^6w$MC+G!**Y#9ZGuj6)}fs z&9F_a;cf`B60E$JVj8wK4cmxj*C)%$)k_wwzS~x7f~&X%>4ycvG$<)6UY-7tcF6U+ z3fe~L;hkz-+CDkz;OpC6hRM0xo;?|0++-(A%erHJSuARaymadDkP%{7yJDE8skvf} zdBH@syBxG>BGplX)y_Xu`GULpY}mcmP4b;vSQ;|py{w*Rd*XZN3O^zoNh$0jTvzW zYr===*hfAkEsSd;I=A+71B+`Vsh$?ON>{FfyhnrU({~wJKbi5TDAUJ^NXn#N25s}z zzR9qS4ps$r`InVg6N^)NWheUB@DBgc#Rfjsn8CJ0O`B>sR9Tgt2=!WSVWGiKRN@Y0 z?4RjQF+>{$9OX?j1e?`G>NZ*Fsg=HB^=~hCHv88;aIa?|1fk6Q=E&nR>QFKQnxg=G z1(;59Y#BH4;4dC9>mdmCPE{O>Dn6aK`f{>=?)U!1C(XN7I&n>A+B(0 zW*2%*^0}lJ!CZ>BU7s5ATUea>4}Iu$K8~usOlBk~S^bfGH5>6re#i87v{TRhRQ9P4%H8pN zbFVgw{=R(qvTD=bH#-*}oH6NaZPoc0*|(~Cor3Zx7HcAM3k$zqDfinQL-&i^7aYzn zku7vqs2-I!skOK-sh>^aYlsig2q!;^S{qWmlW68atXgXM!f1@w?E9Z}aLzYt%$TmW zRum>flwIZZcEf)N27p6)p~qq0C2#g!HGn?;93w{BaRuQW9wwRVdFWFNepR=5=pP4S~sc+iW{a7z>}z%Jjs^aq#P zN1Wt#wkB#Gnd4<1oc0GPaUunIUl0}Kn1@u@FQxbD3WmNqDt}d}QQHe7W=qq5UuWpiE0Hn!=yQiTqdM3AbhIr&k*0SBBSe4vm4d$l10TOgAzuGsz0A9%{5s z{sefv-(v+^0g(e|0acS;irrqZsMw5V)X zh4*+CFEpSWD5phrIFz|pT(D-5(t5pS*LjO%&zW;pTE*Vuy(^*H{cgsL$8p!>Bp5fS zu&fx631aOH@{j-w*I8{Sjs|6M#5BKhz|+`F*o&sgJ`?Yh0-|+o6)|puz*4fna-Xyl zvaynUU3p#!o|}NBSqMa8D-rG^lhY_YW%s}bBDi4xYCxmTb+MRM&r6cyU=6{Q`J2ls zm_j;|vX#KDSh_`AS?+*eT@8^QJ$Rps9_rZwq3I6CAQ;F^Mp>xw&v=tT>)+;W0G2pG&-&3-^!n? zKWsB))M+jN*`}A}JF@#e9zRSLWeh+G8Cb~VvQ&o;u3X3ou!3Bix{r?p0$50SPFCge zo=3>^UJLjvHOBEg$x&OMri9*JOro}%^?|<1*64p}6@RjebpCYuG2b=LAnHi~P6AO? z4jK3d7jFB}b+$zde~a8caSOflWQ}{W9w};>Vd*zv(3cUUzGza7qhY!Hn?!k_n!_f2~&XenCy5e?mr>HY?G4Dr);s;;I5>i~yg zzb@S_1hLiTF8?Q15Jm=(>qM5C^xhjnH7HGJ?t2@O0q8?m)IFe9Yg;TDzIC@z?9CZ{ z5633$@O<*JuY1{t2e(WN9C(1C(X_zaU{h_pDc38r#};{o%{4tN-`a0!odSid&Vg!yJ{F1+7-NnQjshD}k~>-^gu2R3~hyWM8-*awOJu3c1jj-3H5y}cXqmd({o zC-gju$v#rgn4kW$3WDrLro6ds)fSR@ri|5wfhtyUO(k%N=9J&(-a@N4R+#T2ogr+< z{BDw0(5_hdBI)Y<>!r46rQ;M3E2Utc6MLj&sQgm)pFDA{kvWLBpQTq85%w-*M3d;!UMmhb(xH|2PO?c0TvX?iAZ z=Y?*d-C0`|)az;{twm>ZLOd}>VZZu+NTnJ`a;cG)%@2NaVz5x1-Bwq6<{6syc5DN; z_A90nQ6_GxWXYrpm+@O)JCq*<*9aXN)_reQf0*{Onn@+cp6B@I>RZfVGtBH<->3Y@ zzu~S(xTYDfaO_l|Oe@wd&RmRa7Hs4LTGHBQNBpeEvOsiad{Sh+?$~N(`WlM^n zv6NE=pw(p0JiA03dqdy3*-!oj*9k4O2^PX)=P>4RI2=+dx}g7nThVIe{p2Y88Y$kf zcK9*+t+u3PFQrI@aR*`FIidvK+@bcPtV~H9lU~Sl;H;+!tFc7bm0-9UL+gH26AL7j zi#|TTsWb2%k)^BFUnAlf3hgb#@)DK%X5)BCG3Z5%8fP(B0*L`~X{t)XI zvdQ~_MrU)F-hF|7_nf=zJl}GeV)@IeeXAq#v-j@YnD*A;CwJv=a^{DtBkioJUtX+o z#cOBGhN=He_8`@zM!_j%`iyxs+(3TgS{ zLw6tHyHHhyH}XsJVxw!E=cfV+cxdXI9UQek4!H)37-#|!0;fc82skS&hpFP7w(K8o zv8WRByWHd!+0Ob~0tj^o{*m$hZ*5Pg9BT*qQY- zF&Pw-7cEe{K!!-}P4G+bkQF9<#w?M7 zqvEyi3sSiW!nZjiW1)>oO%{Z~K|)<~H}S;eGv(GSNV4YEO!rTauL*^GE~}}4 z$Jmpz^_a?V!+Lp0UDb+!mlm`o<3PW8@9)$v4Ut|jXGS9x^iLee%Ms!s&Mp4>p|_h- z{h|P$k+@g#aUaeuziHD!?=Xsu+pmN(%((NPJh{zJ*F z?mN`eGi6V)3Gp<}F4n}o-6MOi@dOT{sj)W*bE5}RVF@)w;K&av;|$32lB z-RUB=(ddk#;pH`H&c;vInQwH7cF(`6^T-JijC64cQ8vtdO9DSJOKma;+6L@vRA$`g ze!-~MDhDCPDt@U7YX4&aITxQ(%0QG2LoCz433$|9Y7AIG1=aGcuIfKi=zjD>J@c`` zY$%9A$RW#Je4fYPeTMUf(!B-LhyhA++YS7s%HO7IZd3?>@y5*857UyFzxzmBEZ)<< z#2O;f&F9-_pk{c_5wp81?9`4VWZY^~882j|(hX)|e`N2uVdTl_(|5(U?r-qRYNp=j zQ_!cnBF$%_QBXJZqIhaSvd72{u0zAXDBy?ROPhM>!tC62TWU8DL!D4&LcYshuyjXb z6Z?YX;K}xq8D?nGBYC9aYDTrz%N-SMiGGw1O(eUDJftB$_->dslg%%|d%Rxs8{%jp z7jIU22x;YNrnW`ZuMH@P`-RyZL0-!0cofo;Kkvm|DOP9Xd8rBj!<32PLa%y)j^l@t#`n?Zuj~8Yg^ycp7mK=n~bu;O;)E~~!qi0QUA`iLNDx1@& zXCh~(oSh+3Q2#*=BHEGXEus=3xO1CW&ZEmj7-|9Bk_-B--L&Nf7CM>T7OO};+C|`* zeiI{9QNUw`eyfuIM;XnsAKhA^nxJ}&njy7)NNldN6l!&vU%;QotRFIa=d5;*KW;GM zgF3nQ!Dulzg$}W6tqp~pHK!_$k3uP_MD6)O<7go9bRTwF`nIc4-}Rpo%~sBmOJq!G z|1?s3v!IYx0Tj;MT0P0%A`}!|4QuWS?@rAs_$bTi?LQtDzIQYIouf9Oc=9W`lXI+S zu92IWo1C;hkhqwqwYwXLT7cVG3fcZRt?&DJf=H3CbfIrYrgS?g=mGAWcJ`~yAa_PC zJ^a3Wv-3ysI+PbI`|uxM&4axg+l`#0GHC4?B^mdK? zq)xQeG!ne!9ATu8_5yCK=Z>j#YTKp&9IG#~J3Itt)FPdz+^#7OWHw~w==7~McQsoH zys#sJC>@XM>?r6Tw727S04tj;Y%gtkQ>DDIt3!-T9PBptn##A)+Tbr08Yu-_cFS4% zmNeFVfSvPY&Ip~aM4)Aw|Hih$3t90){0TZ-w(HiZEG`-g_r5SlZ@+6+y{J);UC(|*L(D;-lm~@ z*Hp-=OY&E=o3PSE7NMEXm^f^Yq!qV|tDtHX_}bH0Bl}_GkAML<{y|IU)i) zF1(!kB~MCeuQ&(nRUrkp)4kpp1HuAj-C8LxC78pwATCPDhWNaTF;Xax+S7T6-4rO%(jlIBElR$*bHo_ZKZ z5tfz4jWEvXtcsMEzA*@m7LVkokZY}Mi-v=$RtVjkY1rMe{I$oFM2c5K$G)<`y-1{C z2723t9_sUBebKJriJriXWKv zZLtu=1G>+!=%|Yc_sS)L-Kzv9rA9&q$bO7xt5-?fC=q&5?#+=y8KnUy0Sp4phh(ze( zR9HGR);xsI*G6wJ%@6Y}`3XUmiJY#mCzyTe1TUs52g@QFQ73k=@>CXz`lUmr)#nY+ z_s5=hi4Sm6p&v8Fawh_!s8E1R}$D2$<``XTTmD0nIMZdQd-Hn7TxY(KC$_m ze89kCeMf7w_KQZpZ2{k+9Jev0%#sjQ3sud;Rr$||D?&4~h7>g4?E60IkLmaulnyvY2C0xJlFWEsiLZ5sjwdKOQlxlwvr4bj_4pqwBz@(jA3O z`8;l`8Iz>UjVl$+Yt8>zkKcuX6CL8I8`lPOg*_d#P9ywn>sJ^ZLz#2R=2Iu{M?*4) zOZsbf8Im9Vt=~CgX$)Own#nrwqUogd6kAN^a*yFsxf}IKNxcKpG$38k@dk#jWbm;z zp5Iel=)!GPRrIVrP&#U-?2!lHn-E4$CEM`5moW@(Kr)J9q`gz5S5gy0?s=?Qoubx% zkyRHF4Pin^q2+_Kuoc|~wTG z;N{2PQlMC>AU(@|dt{%!z-AtbwhFd@3rgND@sNTj8D$qSd{ZeN<&R?ADpPd1B&;oj zve7@Oy{Dx?8637vm5i7tJH_mR+udc%8iC1)rip3)dQa(6&4aBuW~4|Na~nO(^I52k;Vp9soAY;X+W~yAIh4e*(=;IIBZoji;b3=q)Z?%N6qJOD`{-nIf@SP2j z1$9AH;G=kioseiM8^PzecU4nHxh1LUgZe+ypK=}Ki_Bsn(kS9eI21JA|LR-xVM>PM zJ#WrRl)uO85!<**W$ESdS$k6pJJ7&ZfoqO@$UuB2BS39}|Gh~kY0x|kf8qq;dE%=B zGGv=x(p;wTW)}kaOmJGSOj$?9rUlx8?47l04TOcb+3%0|SFSq~5WT(maa!MyXeYWq z8PTFyCt!RHg!kH{by!B_ORJAGZdifX7WA}R>s@`QW&55dCX({*8VAaOVY$1Ur`|l= zU~53}?F+As4RJ0(R%0QROfX-U@ID$+(5x|y8Pmeb+l6Ot9zHf64gmOH^H*NNS1v@P zzd1Dc9TLn;2?0QY{{TSwbsK!YI^B;yqaScfj2)u3K?rLrZ{Fmnucx5ex z_=mCbwFAVz@&vdoB$$`VG|ROk`NOR5s^`` z(2`LwQ4$f+@zOJ~v2$^8k<#)D^Kl5VaB^|H4g!IIfPjpIjE91P$3ad+&hdYoUU~ow zcwhnotSkipi2(tH0r4^bkb?IU2I6n}N0nDap`c-4;ouPvk-!a_(O)eE1qlrW0|O17 zLlAzi)TPiEFqq`*;;>k1;6SZ2Hb-DW0X(HdeeWCf*>fsR6PF+aL>yc^d;)43S~_|L zE^Z!PK7Ij7DQOv5Igq@Drk1vjuAaWBnYo3fm9>qlo4beS2QTm7kkC(I;Su0xACps3 z)6z3C3yX?NO3TVCDjOP`np;}i+B^FC2L^|RM@Gly<`)*1mcOm6ZvWWX-P=DnJoWG3j?edBs4S>G~BCR5Rjg)ieo^-kh8;LimSmHJ7ZCB z1j1uWBox&5B2aRwpT9A2nMK5*;@YOZcvbDMX8+$53;O@1+5fHBfAv}gkfFfmF9s9_ zAPQVvNK-Sb_C?au13%^E>CoE?lem(BlR1mT_)Nf^G?XV2@LWpbFD~K&nvyf}e=EXF z(Yq>6G6Mq1>JW;FKk#rl1j`h92)Y$dbA1wsbg+UAAfB$lPF987k5rt^ zd{2#S5&wHBzrd<^8rvcOc{Wp?Q!MDU2GFPH1pR%Nv4U^~Wh%~|i6Ldu(@lA60A;Aj zJvO;zJ~y_$^nZ4o=pk^5s_^_>;U>v{`Y|$);diY?$jAz2pNpgz8n}R_U6foOk{iAo z+{+IzTtpV55O?wNG{h$uNxYl)VCWm0{W1!R;=PT5M_@MN0V zF(qj$nW%feXK}u|gPb4d9g+V*+}1R}d+6iQ3xH^bc`D6c1u4cpscvHR38{29KnxB! zta5SJ=_j5R{>&-XfQS=Y|6~7LxCQ(U9D6H8?(T_|d&wjxS9=AWhgIo*^gP5$VUno4 zfGPwilT%s@e2_TBVDO7a3Ihypcv*Qp41@0#A4 zMmYnG_jD9Ni76R?r#a4*Bq9|>TDA28e3F(J6Q{a{!@tf)NEi{eIZ;Z&a;J_v6n(f> zM{@aT?%F{8bNK1E28E18#E&*i8acqpi+Ffj$$_?hUYB$o>rA`%s!|*77TOcGCRmGk zpoKJ3-@@8PMv$w0l9wx^6gpX*fOAr;XU-y6Dc>Xsv=qrH2Emn)e^s0>v7f|ckr;r> zrLG1pGE=wQ0B?vS50zml+5oYVgXWv!-Dq{DNBZ_!C5C;Ci7d3dUKpJ+88R$be_Z^1 z{uKPNrKKV`I;}y%pt4h!-qY1KJ0E0!0YL8>NBs8+ql^4xZAus;hK8J6k2QK-_Y(0v zi`+HjZm?)4Ld{jqWLDl_Xv;t(SKPia%VcNnY&nc>e?DaxmnC}F-6489n5(i>h2Y;f zDtDhp%1OFi8OJDy1n<^u3`#?2mFi z8#Ll6D*l*zgxE zgsEl9(?J@5?X;FD;i{^NbtI<*J*NthDGxraLMLf$z^e=R?3yGlhfvWMn75h)Sgh&k z&ISSe(oB7OfNMdyy+VmCQVHdanS2eEd`}Pu!y}6gO>9A75(m|MK2?0@+r>!~|8 z*@$lpXZ(|X$PX{uyC^4n)ZCX^T`t~|q?s#WkX*0eF>dX#Y_7DU6<0gZ^*5^K@t@<4AQcX3 zUsI<^bq}fL3K47Z+ft}wg*a(D1_q{4TW!1}&bDPjz z!lYm5aT~0tMR#E}9=tbE?oi}JHzFxxEstK&F34=xXIi*^c*Ql9l=HO#NCw#!46;I3pLdl8 z&js+M3OqruKc(n6=L5*|K^IAbmOnEWTT(++rg_ca?w1Mu>Wnt(ztc;dR1GrsoaOFs zP=@8Q`5_^i8j0~^l9Qg*-7RNul}zcTK4dyvsEFHmJFZbs(mIi}{m5;QrCon-JGr&M z!u*X(qhn{2qJ`=D+68l4%5i16;9-#}y;;^onwRLR(&?R>9-FMGjtl(O1!$-zXXrUY^x+0@~(5)YonsPH$v z)3uD_TiFBaxo8_CDI15Lh|FpVW^7xFHEL)w_K^nj*5bt+gS=(fq`q^0Hi%F?r34;m z2J>hdN#KjK;=Dr4w{^}Vg}L;gBrZ%i{m@!UqguEfF|7H%3$4ikYmy)RB<;1z2ZNpx zmQsjsKt$?oCmwdqY1OuBN2!CV!Gcb3B`%F0DlgIy#cr#u9d_H}M;UQUd}exS3{zNY zsl3dPr}NU2W)}G&v>9Y6YDxw`rAlx~?0QtPmaBZLc+S&)nRV zU6bjf_qs0t+6$oHam<*IKuCfnY2-*4z5KSspmDW!=XV+0gLcBQ1XWGN;+%V_By9#} zYkAoMHZ@nIhtHfkJ{Dr=QZ+$LD9xt~W!+S{F}4w5+k^$X%(1PlkH0!}ERKT*HNG?} zRC#C_1&YbS-4ZC-|T2=hzCNZXB*zYyF%YA~FNV}IK)#_cQB+7S_6qy4@-vs_( z9PI1Ij+GH{f;iS|(HF1!kn%p6xpY}=;d;^p^n)=q$%)Le#g^gyRzh%`Lkbk*IZxhL z%2)$0#WH+6E&%C8cF8E+{P=;AgKCoU1?(uclVf)$d}UBWo`Y)P;rr=4SC#Wk8Oi0( zQn!y_BmRmc(Jv#QT)Eu|JGeQn;*F-hK{5-uOVMh+k3wMZ9xqptX@gmVO`2kf?>$fM zS}a3BMy#?|LCt|tecpryvV%a?N6lLG&knTR2sC&ks3`1hQv#Z1@zPv0_68d&LS`k! z5uJkJ8m+0*JHwC_J%eS^-wcvsx~E?NqpCJ%Bn!Hy&wPTtqEq>xRT?xxdvkt`L#vh$ zl6tt6qM6+|jKOXLwspRK9*C!^phdFvr7CP6+2qO(LvM)|aBmS@F4y}7QY=QJ_uf9C z9q8DfnuRG|mc?AIWwS%U!w-G@YT#ZDs*&|cS74emWWp&fRpv!4yQmFpI!-Sb$tddq zlL=-25oTl=gfyV)aU$ei%J_jZDRPd5PdiuZsr|Xg!i7-Fybr7ClG9K*gHRTlH8{d# z`bV|Sy-B-Qu}tUtusx!W!r)1F!hFu<$4bkjE;C$j=P#+5Tb|lvahki*@<7m-Syl$g zJbEppHCEvu|4mdta8~YPawPh^T$wt7m_&bMkQ85)lmg+mt z%P!VtE@O3~P-H;Ay4rY2^+=#1)9_N-$tA0T=k4(`_guR7oEOOg#1vi$CFIHrFukJ+ zvgAy748uDJegT$ii+cxYlLi6j7hq*su=i0g+b8a#${Kq%`cI(l&KJfaHXjW|l7-AR z%7g`0R76mjG(IJeQq6|H&QM{Z3=k`MbKN)_^qFLc!xfw|CS;Bo;%Y`}!=w-iBs}Rp zH>>$UWF#^66VfJQ@gch=J-aY;*);GF-?09!^3;)0BZJ5Fe`f@~mT_vz?`jobA&|F- zymvVjIUGhhI}=-C(MKTXfNb#lA-3*MP)32Aq>3(HB{$hwCgv2 z2`SKO^NUMC+gFKJ=zzApqB&=HZs<>kmlK2MFgG)oMkq+)+07zJWI+`|&d*wj8X(6` zFDZ;ih;+hE-d2PuDK2UTrNZTw-j~$4?`>67IdO!O;)gsj#xnhAk^rvLr|5 zC_8OCw53i+PKdl3Ey5&^S>VXckL>F_zfw2P_xpO%N>RyiisHf7AZ*Qpd{uMLg%nDA z>+2ny8E%zEwKL{89JvU>=b8vrv1x_*Yyqnndh^rPVp%NtGix*4zODDPTo5vGR@TDI zPnLj!^~t%bur~KZEqcs9i?x0516FTjfI}E*Y#-;yrlQEKbq^AH>%ttcPgWQSzTu+- z-{TYpi@^|E6@ooli^RkLCL$~){vy$B8NLd<%>yN_=Y@6WOu&I|P_UUiA9jw!TE*MI zH4?C(hcELi=7PY-?rWw$lJi!_*V>tsm|5(Dr4IpnCF*Q5WH(i2YJrhVxMaFw6|?Lr zT11+`fqCB62=!V{4B|>qkspW|MsbPMo<9@@P6;1E_)l}G%_ZG|@?owDlj`?_tRSq4 zX|cna0cLD(4OcPL;z`JO_d&q_Wd(To0r?puDfv!ST}1|@C=JGrz_?GoovFPmH2Akc z>>b=()MX{ez%T|G+%A9&AOPq9AHZyE>gpt}s;c-OFy;T5-z>f2LI5z&{5sbEv;Kcu zf?{UwY6=En$iS_{Or2cZ!18;r%;V|i^eU%=Wo%O`V=#CIQ4N-vT);aBmcPB0oBSi6 zyvmk;j8)$Y!sy@2hNY z=MLVtzv)#&WOD~C4RB5ceh~q(00>Y5Q~@%;1aJqe0Xx7AU;dDy{ejnG0B!?L|Fx~F1t%C01t$n(2{4Aa@$&MU8jM*c z0l=U5mzT$amzO_>*f z;IR$QpikA#MVjDm`ahKhiMj)9Jf0hUq!VvXRAuf3q5;ZPCa5x}MYhv}v76>A&;9-tt= zFx!8yMk26_J6DU@-v^g(?)_Nv8kz8oVRzb~Rnb+!!xn~M-q-pZeE-~ny8e#PtJ-4J z700NKw{`pQeY6DHurpAJjwmjUnO_(`9HrFX%71lP-R>vbYCw-ObG1>&`*nL|F&w>A zSrB+5RsmiG%IRR(O%}W!??j%SER`Mgj-AF3>)XFx-9O->N%oc^LQfADztPgvHiO4z z03aicC+oJCM@)@qmk)}oSM}5O8+5D>AJ31zZA^Le!sD;*%t~3C=wgT?L6!hwC2ZzZ z(&U)+ z7n{kqXsp)0>qo@sV>$_PS$+rJ9ZQlpuI>D(O_ClpwO0VY7#qM~v)Gwx{_Vd1dAuTK zduKlJv1{XPDTPCd*Z|B2t}KtKKH;O=Vw zo#g0&%gsit;mF!hgIWm_-<7tyHB7x(P+~G57Gey|J{Ck55@X#>Lfj?s8|>yLW(wk# zg{kc=GkjLs5Jty{9Rwk%0_YKGIFVhqil#?VQCYrcmbn)}T4~5OJGF6nt!&3}v_F5F zq*XtBa69!QcY*No$$1knRNS5S%c-1Cx5z9@@w*xdu}BP^Xx0!<%3&mY{}8@D^%f(_l4<~ zazpbgt>wGb>z-Qut?^TpSoqOzoks&Kx>Tgm6c3k5FAUcvgFiGbiJuAm@!@qanhvwj z1>lkMSIr1Q>@JTgroHi(noLU)w4|%~-yh5;c(ZQ_P7q9`Ir4lpdh5yiX7xF2Y{=n&^5H1D}p{Rarv3zkn-VLU6|0R6q?K_u&MLig#PRd`r4bTH1__A{&0AlbhF1>3po*ifV1U;X@cP9F4wQf+B@l4`gw((j9K6R{;!7Ca(col?zfqVfXq@w50}=m?-lJbj5$6qR+>LKYK^ipM_xIjea2hb*a5yTwx~pFR zQliVH9ouXN+Ux_k@}2i=97tG#9nojP-~WV62X{%_oS}NbWVcQgu<%`~FW9;Lsf-K* zP90Cm(1w`;gY63W&UUb(>K_Kx=mgmz7BR+XiKTI=9zmo1#21T^M2j@8MeWqS=CM3j zx%67?85VgS?-{<~pZR*{;q&vUh46_-*A@jA3Bxj9>mDL8RBt2ePpHUiebTzSY0R=D ziXTPhimDRmTI_cDdQ$2$6g`)4OK7peofv1)kBa%Ng zp+1^YbMz1eno;BbYIiVPGTi%+gE%j$Hox~Qt_>0h7l0TyyOrlKUgdj&76I!wv=aA+~uY0I%3v4zi64sbZd#o9>8SSaiG3aG+C?{*pd4C9T*H4MCDmD z|5R4_=Q_3gjRvBN0$BqR*LY~k+fl#9Bs{g$W0c4mNWF`#N&HXlK1l;E_JxQnbkX{a zsWxr_Q5ps)-44|=S&IE$IJ8;BX1PF0QihK#_B`K85kxx#pq+8N=y-CftY|hg@15Qq zIj_vDsUlHlIhj>en)Q%Xqf47dy>7GmN9&h3(UgKERip1_kBW)!{Z)Tl=htf(AH{Zk z+bWrIT9aSm>P?endU97~o&4+}U(w&C`{yJHT2$gxf39TvJR_N4L`BggH_3Lyju?Cx z$mVtotxd06O)1)34>Wfr-f?xe+DXwQ5EQ8|c%tB1KZL?8*Ke*-a0n{gIq&Uvd^@&>DvP?Uzn;c1}f}fv#rNutWVx>jV7!|#j zRig6~zOL=}H z6t%My^N2h%Ziy$e_*R2~QJEzY*;d%kE$;Z4m-Mt1>>SRw!ICTIR@HP)Pn4`G>Xym! z+#$2LwrzuPJd-{b z1sj^~uc2Wfpur2^zm^6Y8tSj5*(un>)v!1yIgS4@Hsq_ZUx3o08+CdDCTH80HlYKP zv=zEP9y*YFNvX+9GPJ%22(D6#BK;IUEf|_K8e(12ND6+VHs|sG+}hbOJ(s;YW`9KM zB|SbK>Hp&wIw5dLan(5W#Cs0XIA$;8aK@e=JKAkf&R|tQ$VVy6BrU3n%J5yAQ_mGSiUZjO_e z(W5`4rk)p#jK5idZ_Ol%S{SpORlmPkil{TqPP|E5L@Y@=6W8cn3Y|X5|`Qmj^qADNe z{WKN@Qxr=W{JJXDGcTMiU*K1}H<1DDFXnB-dq+sBQ-+)j{81EPyT7ru)zdRi)iY19 zuJ6F5z{I30!f+`B&DFW@Nn@)?W2??nQnT`_M$$Qgc2W(m=?WVvDI;zeCMU^%_-20< z&ZN_h8>KXe)s64eRFi%)13r9$=wQbi8WIKp78V@oe@)7Ct9yuj87`uD;HD+>8E_A~Jz@4Hooepf8xT^G0QrCc?kII6no zPfxd%4TXb~RZI9e*!jI^4f*>TijMm74yI*Uo zifrCI-pL2ZYV0WWE*SS*+k@sO&Z<9xm+kjX6590nf9@bZkAFD1Ag-@yoZ7=MWUW-s z-Ms!8&Xb<#lM{eQXc*;}%ByMLA4#0^gcZ0Nr3_=sot^JlrVx*Y5{l^BSm}HrUh|n` zChOY6X+Drw3PYiBpFjy^B2+^m%3q_jG@T~}&8Jgx^Dh4M*Ve(o?Y@Q_D*iZnC90cj zY}2XGe)*@>)QB@@Pro30MUqNR9q*i8fCX6!p=)t@E zLL?V%(?PSB&PAc%Cq;BPeq>Kf&IqkPlB6OvISZpMFFxiE#v2GTTe7}1q=_|J ziq1T{yT+T9Igq0LJP(Lj-o4sK+{M5Bzf9>~aD#sCHl5(y_o1uk8{(fq>)Huglk!O8 z2pNExBkhF=qrdr-a8Gc3aS0c7n-?6A~~eo}ylvM{idTj)>0Bv+dSde~S_AM0g>SNh2BaYHAaLt#ymG1Gl`_I(Y4Se;{J zzr78bs2p@47=n3svQHjb;@w3>7x^nc?5mXb!jnO>HUM!X|A6d^Iz|q1RO)I5NK_NLDPA zNx|@+Y!vr$kJzHs{J}TlS8h+%=dLrKlQrXP#3q4{KR!JoWcEQ^Y{7TNRVAo!X2|V#=*U*3uWkZqcksQMw>)a-F2wJ*F!1bg z=ImGbo%H-eMg<~_M#P%I@EbEK>vRbuG+WHvC7+;d8dupE6N5jce048;0c!TX$Zo=M z!EqsqnQHbM@TyljIav+N2W5X}YSy|+@k6?>dBE;?ccIzRex{#WqJ=}0)nd~-e8j7m zWHdc@OLntE!L~{jA{#hr3|0G9*uIC(kS|8-2$}#`9QXW2GaS-)XUVqqrpOe@Lc~{x z0leC~0EOmvY{n!cnJ2KG~FM(c+vjjg+qhC+w~uy0RjWOa;X`k zI|mjd)SrXbDzc0Jc;KSshFnKMpf5@Dn;`w{tF0V_i+SZt_k|?M3IBm2qmJt=A*}h_ zB&mq#tT!o?vd(Mijxks)KC>Jg-#2o@T^2QT64;VTE3MLL;u^d}5MF>j-$E}g?xPB- z7htH{ED==mUgukQVzs}Nzyhg4V<+5oC)dt)GC>W@Zg;xKaQ>wP{R{AzMrp($O|TWKE=ba1uvEgfF|aUwH;74{Z<%T|%7cQ1 zx*J}Pf}m|ldw$i_#T-^1{Ipi`vAPdd`Anjn@~B{fP$GzXw)-&Y2BC^JX*QuVf;MnR zy{Ex1Ji0FUMzWm+iS1ni<=fD`^ewYy71mb)v!lwd{1NbCIdpn%EoEig%fpyu@8~>C|uAJ zn*lR7LkH58dnn>u)lE- zqZZC|kZMJ_Y-6~dYr1w|_K`CGn4j=m33I17I`c>%MCXu_5gRrD7QCyX!BVsf=`Sy?>#?uV(`yD?cM=brR;!-0G{S4s$Xw9vm#vJUSH zK_M#4Y6ZMhNz<;q0&dq^9|deUIA`WSvb=#Y!vjta67l-iXm_Iily7Dyko{>|okbHt zrxC{ca7`+*IZwD;Tfc=P1b1(Q@~FC_JC3l$p)@%qV7uHdW254-BTZD?bAJ>jKW7kR z=RHBdFgp!Ww_6U6CNzAJ`SGRWV=wP>UUjChTTO>T5P7*^G0h!&AGb?nSpQc^7b!)4 z+0Iq?K40;w7XW@YiUc+!%58{;(xeF|GKsm-#985Mo7V~njixUWwr`x=)Nc6%%Ay)w z!E+)g7OjEq1XGXDxF>a+;mak8(uC6+BA5u3Pw5j!0*gC;P}Im5@?R0(zJsUDZtySD{QkS1bn@xuL<#f~&nEO%%;ax6oN1b*v zL58Qs?TIi6IRs>0Erw=2wpoomek9OGV*5f!8$Z?~3Tg%_sy?6mv?*6H!0jZlxx-9D zKuyP5WZMK@Kd_e{qC;rLU zaF2|Hf$I9(AZJY1511As&K6lmtUXUA@7ai&gSjD@Ia~Y~L}|OOGoRNr{p{Jihpt%m zYiH;tcYUJnXw=<4T&dy*tazhmGMYFDVi^SVEhhlvj2YYUzqLcd zzLmaBud5esQpRLv#VVv9vIINCt5!fI0&H;#(?lC zW_pTxnf0UN)vwnzJ5jCq-(uQer_2pYe!a-Q#%p>3CcHcGs6IWZ{ed<$38|AB6W++9 zgm}D(k|#~aDngc?!@PL`V2j0>+tbcwgBU`c`srdZ;%exb7y#N&nw>*=nKdi-i*yMz%{N}-11jP00lZ#gi7IU%Ef!b^Y zM4)fat2d%%YwrS6T3y(XpAg}v?(v=+(|w>djKT|PuM%&CyhTmR4v$W9K}QCk8nL1p zFuC2zt%9JfxTNFq<4vs44TlWB$k2Z%V>v$?fk|R{b#G%i~vIHOi z8WR&;DM*dJb0h?+L=CYS1ga-&KeLm-AS9_oc!<&cj(jcv1EC>E&W;*sI9HA;6iGXP z5P%;%qe!Jn&>Mg>z_>C(_8E7Zk$8`8l<7-1YyZ^@0$!%HK_r=;=g$SzhJ8xil7D*n z`{J``Z2ww(rmh-0vT<{^!xR^)U?s~F=#0o@T+f|+FBPwO=mc`~i2P-eFHNTBROp}< z+%D_3ZtK+w01^SZ9u7LQC@MaXDjuGyLm+@MkXJ|yh!=+jJ2a#i;J66ba$tAn-*?Hu^mk$D9y(04#?*bwmj!qbZZfg&k@z>+O7MB&rtrE zT+}Syant1$DHZo=kY>}z$?*#|2>A&uOJ44nj=d^}%(HqM>YF7x%Q z>xV7TBcbl^aV*`>Z%14nudz_Eu*$YueNpQ@NQvy?yc{;`7~i)0eZFxDiF(?0Sj@)R85 z2<;?{$%6%kOz~cT(Hq}5uQ~s(G|M|JzpI4|XNCEN1;ZLJe((RG$fmuZ&Ju8ea)inz zO<9b(wU1#5lAT72LU*sOLJCsu{%RdH;?;A35X^Zz;-WZrCHz3rJ{a#q?%n+YP-kY-d^y5;+@TOhL02sUjfu3{*-68F5MgWe{q`+$bMrv~8HA%8=0end$((&+ zzfVO7FY#O`d=y$dNcc#ee>JBrF*CQ!=R~xOvw!>xlu`bja(_d0uQTs2008!((O;*W zn6YzU@A=zU zefLu5cj?_}BT+Y#p!kqyH;#38DJ1$C_PatFdD}O#=T*aCG`PzohR^tbw?at^1U%$u zqKnAa`^NgGDQ2j~dbnWgT`$5!7T*^@G{CRJZZ^g%+`cK7GQ-4y^v)laWLhnC&zD5X zyCc|gnOBt1%>6WeEiZ&XHm0rZ;+AH3PpOr1B{Zb<9`~N;nhNgp2NEtN@F-#@0@28w z=GTwMo889Q_Jh~*{|es|AnaByPT8^y{x`U7jgoOS9f-NRo=v*ncRIb6w)5=muK3yN zJ{0hqVc?exv=NYsIJnKacpqM`zYCFWd|73dGHf{UuQyPGW1k&C#gSP1X4rU5_e>M0 zKWsm7)NXpqENqj=^Ha%_Ekn5dczd+9jo*o$5b}GUr+~f=T!bMr$JCN|5o|d&(n3;93HuA2o@C=_$eY0oCA%+aIK5zfT03{Fs00II60RsgA0RR91000015g`CE zK~Z6GfsvuH!O`&X;s4qI2mt{A0Y4C-sQ1D>yzdidUsnb~;p=%`3~vxZ{{Xm^#Qbk1 ziCKKR_ryf49%3-w@?ykZBwa@FOC?*J0PbR|j8%r)`N`y0ks5VY@(DsF7=qma2<3105kQ#;5LW) z4hQ?-@Ay{~RH3wW4lrDv3B0MgT8&`AEAYu0QO@fG8Rb*AD7N1x@Mgql_sLCDipfzD zdN`m4y<7)LUF8yQ(-j0hF#*o5wTkE~z2us)<0kI?G4~t+l{6F5#tI4%-s2mrp8LQz z44kenxV}m~@r0rdXDpM2#-FAKDJ1=mE=8M#M*Sg0@76oQiBv<>s9U|VbCSEUVA}3# zW+pkoaL&}@{X%Y6VSdEQGN8Ob&qJXe<$x&}Ae7HD#8x2Pi@|O{1Q;y?@L(n>dB_zC zYpkS~Tub%IAMyLjJ#f=C4scOvAhG+L9Hnr+9R03xoi3Qz=r7jI};~-dn-Jn;$-*1 zHb{#E`M|zL@HpYWO2M5i1!9gcHl%2`NeKNCs-%*Mo7opF`<=M;gFVJO2t{0 z&p5LZaD`&O|m7%wk&#nv?O)QL2PM zpvTQ%9#=NUE4Uz^1_IOkDG8kePlp%*9e{)xj~a412%&30?N|e}%}1hmKoEUI_P{aL zn0MzOqLWfD&J=T??S!8)L(yhU=#2}4;9S$epr zZ9L?{!x3FhD;iD(&I?}FaZz#ylM@3z$-Gkp)ymdOGaeQkUgYw3oJvQjl%<|xHIR`i z?O*wb8aZm*v*<3NB}^fotBY7xd;E8iVnUB|@q{3m9++de>{-WG@%A)TgY+pf zgfu=`LD`{#0i&Lh--w53KqvwO>4md%kc|?{%A!CYMsg(Xg;+wrj7gz9jD7jQt%~iC zC*LJZyLP4y5fj0zmH_Oy)fWQZJ zr<%_Tw<;v|WcG8A)K1tggw#zvVv)2!gC_C_1t?_)V4=aUT)Td{Q)ZOXuoZYELMpIi zMAN82fefSfS4qE9S`f#Af6kmhoE*gWBpm4*{s;3f))MU=F;64DC* z#_Xnq1qB$8kcC5RV8CqBw$6jjqYb7C0;>@o521k|hGY{$jq*R!lA8m}Rs%*)KYutH zP%fa#NpTzh0CI6VshP=o`C`?I{{VjRnLgOfq)lSkZSQV!1oc&l91ls76@ZuM_E-LH#YCP67Zr$Jl8r0;Y>Qp|sUJ*LR1Q6UOp()ULMn6qW9;X4~ z$8&=J02Bh$US2tGkxz|B#!CP~5N!H?xD@EJdHUhix2k;LCvn%DP}s5|WHL|Z^@8 zV7yWZv(s)Ug*UYhAb%tK?UPhN8!=p2icOEs6&sUjkz^9eVoJf@O|;!3k{Ly(oXqz` z>$bd_BtIqK9Dqm+aBY)d$;K+gG#rw+#w^caW8_Bgf|-Hl(i z{c-Hbp`L@}YxAsTiF_x1xzKLveQ;0|P8xrDU|#HGoylZ8Z?{?;OQ9TANxl0I?}$P0{bMx>bc$qvy0gap(q8H~Gw zHt&8udec}oB#8u37&iz$*d>9|BxrOmw5g@TAf;V0#Oy{%;k*TCvLkSgY6;Y0*g*xO zMoh#9m{a$>1kk-t7_C4J&X0WXI9W>9oK^&4bv*w7UN8u#v^k>7F}X03OSwdd&ldTP zKY1a@xb86wH2UAc{rQ>^f1Aq<5LBb_fH>hHss8?qSVC{DN46-ifmRnD**ui-8~*^A zA~(?tk!mp6iQYnwyEtn#&J?H6)=qMq2?9Jag)ov|GY?2iZHuGp@SbpH01D)(da-@Q z+lpI#4$1Qi1Gzadz$JwxP)EEmJjcEOP-BSUATrkJ_`nG~Cdk9HU5)$RF;N55E9r=& zj^Vq?u@)rP>m4%EW)Dr`+kttWKX{NXx^Fh0P?`AS{^L6d>#uy+8sUHLMALH$Y5ig@ z0!pWk?|fLXLfviq#wZjK8e+Yt7QkWR9zVR`#kzGYQ5G2J#g#5NgBeuX_>3?=jAlwB z?}*e-ml&ewz2t5^!ZBJ%`!bA4VEes{i?TO?NgX`mP8gLt7W;$hIFGd(sUZugo&Ffc zT4drL)x&~p^lRq^Sl^_NrbRRf!;IS=Ka9Fm6Cv}8l_xX5f2KMo+}=LQZ9z+fmOM?e`i zlEZPYd&Q=!!xM~2i*;C>p>B)_qa)`7bpzJ18!oF3lie81mUhHu#r2I0B|x6H_Pq?Z z2MWq`QtHMwFN~H;0>|=HCZ0sgh9=q~Q2}ChOIM>Me9MXR{@_5zXkWGng^^vRY?%2m zS~}5hr}>LA3aUPjt^jC<6el$@Lz)l~>HRV-m?N^eF9LLO$dMX&)+=axzd3LKys*jt z05M>i&jt*lTEPI`0 zL()gkhgSQ27DhviC;;XLBKhM5MpzBNFU~yx9G8_kUeE6wsnNA8S9qr$C}e-H0MIyA zC+#@JUd&xqVRamyegyMA#ZL{E$P!6nz+dt{R$o^<;g#DfUvjr(IQE3|k! zTzDWs7n$}e*Bw&(hCm{^uUwjiN1PT}a04PHkDs^y+T&Q1#h<9|3srS^YV!303pZ{rq0EInWR<1T^YmsNl@b3QR) zBoLQxoQy3Tu5D$G;(xw!vjfhYgc6f6{NT<{44ymBX&B#(ka$562top_Ou#BnFq%GL zHo`hnQIAFN;<(~ej2Ocb&Pg;PX~A-cQ2_GSTvT-vG~Kf69@Ua2DEOa@PdCixC_s{N zh#D?~Ro((I8pOOI(86HM%?Z+Y*UV!61`I?BAHUZKWQ!jUUxy2-!P7GDd(qbzvkFH5 zITF|J{{S(C!hm5+$2;+fWVi&itRyW!35~?!jLgNk+(qktmzTcOBzzj<=N!W>?H&ha zO+r+{(=LO{ScyPMfFrpiPOkC5N{Q&&8xwptie6f=#AGIvW8J`@lc+Edj3Ob{Iv%#7 z)xmO1W^JZ1-ctyxF`#3@c*85S8;vlGsu zed15POSvHwJ1Hdfylz$C?UT+7BB{}=No(c9lq9Z^iF{YaFDRXZw%rGAL7cK7f>8pe zjxjCfRBp}8_dginON}QeyS?LB8Zsf`c&*3VC6NFoxH4gTtw**k$;e(V;sbHH@4aPb zFBs6=VX(;oB+SHl!6gZJ8rlq(Q>>3!5KNaf7HO)&$R;6oa40R*2_{Iw#ky3C>Kx69 zVp5PqM8cVawg~rRcf>F<$WRc;>gxsql*t zmchpNka!^&x0k=ZDJDuub-W^JVD))?ZhDknFJfu;utIwi|ryZba^#$ zMs8d#EkhCt%mO#3DYI%55fYGl>mD_El}#C4ymj71 z2rS7I^KFXaPpTcP0Wc6=dfTg#!>I zAZFzU1dv1sGGIY1uHIJ~CLT`fDjCV~`>+1wSg$Q>9lN~HAZF16#e>7!nuR9Vv2o}x z&I#q^D;Ez59pac$dk@DLi?Hl**e&|bC8ozuc%!krO{3J~4a!1jOcfzB+xo$cZ_`ddhh91+Z!=Mso zK-!Kzu8C_LZ34tDle><9(7^#bczJu@I2@$)-?kDNP5_i*6iT_}KDi~-dYi`9*9yTH zi#0vTiqJ~jN7Ia&7!Y`Mfta6$aZfOugk-`J9&E&uapGUzYmMC;ulL3mVqK7VH|54# z7JEOvF+g3Jw8)VXOL+DYv@z18o^_UL^^{C6mnIXVjF&-#`ea>TQ_z3D*iYC*Q|R}j z=-SRAS6DAxr{^G&9t`6&w3a21famzu1mO-fJ6+!R;4ch@=!tt?UoHS^MRG@@fNzMV z4F3Rn;PuPQPFiyHk&^&S^`>c-C%jfXa2;bF?ZW`&t?AZE*uJOhEj0&ODHmKVON%E5 z0b!!i5&OIp0mPkFb_s0$_0EXsEhkl!Ah*3@Au!Apjre;`TEV{f$~k_2SWs{@1`#y_ z!r_ID4AwAhIL}TI3D7UCID(Gp&U;1coIaEUSsw|J!S)xZ_TjpEsxmSa*@By4;!yoz z1XiWI8HK&l$?G^ar=tmAm`$zy@=WS{v69Mfu+9#H!s0%A#znFH^vO<@;)m;%H}z`( z}H44BR98@qrXXG>D9X2xmq$wAXsUAmQNs zb&{~sY#x3xOpSEpkWx2TtRuOXzlR8f5}hVpnGX!$j0e5WC_ZZ-~X7B&DSAS3WUYJ0zBW&g{g-%K3- zA0_{&rTCYV!M+=D5CG6`!^q6S1QPcj<+I;Gp#SpWHzR&C#COc=HzWPa0Fduh`j;WU z87Tfgd4T?zaRT)J=tlhZ11J1P59T+6{Oc$DX3&3`@|(f_jbZ#|@PC=}n;{bZPcziN zjQFhs`rjDA?-=g{2!OsK03i9zaS8v?S@ylE@&9t&Hz)kdt=|mD`**%fjEq2e|F=89 zdH>lFLga6q|1vW)0Dw{Oe;R-aA^vAx|Lh4+(SP;*m%rTrkpE)PqJQ_|dt9)8{kY#T z;Qti^`Ckn6Ukv?U4ErztGwwfy$^`%r6h;7yezWm+{qJ7_GdCM6HxtMIuaAb2iJOrT z0Qmpf_wU62$mvk)9-aI`rd?ptfZ*2BJhndn~knLKJEE| z?9asrq@@Z{J1}Ldrr90|q4y)4pR^l23_S@(Ake}F-Kq4X?xXU06&SA{i>^%xW< zBCk&!)v0B5KHh&T$+s>B_X^b3F2eL{!P*5ziGzFMBxyI9Q-3HiSR%0ANG-H&!)4Z6 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg b/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..782f730fbc5d6419cf9cd9a897319d5f21ef7788 GIT binary patch literal 21864 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%0AAmG%Ub%wY5p-+A+A3= z?D3@K3}<)c8a?eLYe-3Nx6&xbDbLerbkf$}N-r%#8_w}p(WKWT3!JIT3C#Rt?>#f^ zuCpsazK%*-Qe@0DN~YjT zcWLbZx_>5nf9e97JyzNM! z?flmHXYvOwtXf+Eh*LcBJYTqJ32N@CmJILrRDN*dAH{3AD#OiyKa;U zwpx8><)6yB=O^bqS~NPm#>K1_tW8kTlJ5<^kPOFRhf1E>KjqtaFwzSt=qKP+Ar5Oe z=kb=}mPE2pI*QF;u~Ae-Z2<85Uwxa1`7~I9(NToeWWD-ZK}TaIL@@Kn#IF;?d?G9X zflu|T5`D?Wsn>Y&#|`2=od)C-hE!ipl#=n6m)#jfBlE`DShJOWTqip$M1lc-}DOV z7_>I5(L>x#xJVATSN-u5u|TBuwTjO-6-$s1@5Br^BaqTrl0zb1N!-D`Vs|!**mffT z+~2X4_a2f~_ACvP8m!aC+De>l?e4_}C61bRPIz>XoX<&U3h&oCEaZef_m8|(Gt2Bx zC7Tfrm9}p3?YvEHb7}rb(T$LvFtki>Hf9A%og~kv61&#WVnH#)5Y&6VuW6!ESV*C- zHziCwZn6(bZ5i}%9O)#nIu9sXjyihX5IoGfJPp|yBkqQ&-d77L30~mg8DiBb4x5zL zeT3I=&PED>Jkl4qEMzU4M{XhSt3@!znH6>f+2lFI+)3NyuKoC&!l1B#kBsL%oaW{o z(e-_@(iG_zYwm7Q{VR`FR*|d(XvC#Y1Pv4bu$BUmFs~r_kK2YCr4Jol>l;g8A6jLi zc$}_k)+GS|k~^-$+t#7Yj6{~m$O)tVeUysku`Xx69ae8mOtQ58k`5@_(~K0)0hEYdfjGUorm z$nLbpdc&`uYH*UIu!+>)(vI^tAapWikVDXhmDyH%*3?`Qo9W(XFmfB5!+!Blb*x=*oodS6kh zRSr~?o06wO@Zj5k(XPVn`NsS#@Ac~A?rJ{)SK#X)OR`VNxpcWgLb9rogYu3pgpd;E z=*9*`H7MDmG})|lL?%ks9AlM<)*}aNtRK{0b}~vC2$nKmicnc@nkx}%lk+(H`Z?4r zY}9h7Pf2;Q=w>`NUu7QQ2I3F0pwtnzbfrCRep3(bQ1tH@%441w%ytRp_6P*-xKo0@ z1diWg{q$M7{o>^LxM}=D{{3)Bww_MU5EFP=4`XgtbOXgk^}dT zw@f;h6{wc6J1=@cRMpPGITDPt0?58AYKp#U%@=is?!z2<-*gwX$vLC5@&Ah~CH0v5 z2mq2b^luTq5VS^VJHMQdmtfi~!o5PKbYP@GR5b4f()taD>@j_-< zK#^plT^Kol2d|nyRmLTSN4mEDYwAdlZoinpNy<3gNjjiBuE^8#mqrrx42; zm*UFJlJ`5i5n0E`H{4$vnn;0@MgDObIc5dbjoeD0?WX(7nW_l%8`nda?Hmqrm9|C* zoY%kASSul3DPE1k#1Xxq23^yYKNa$LeL)x5+r>L#0V_6UAI?K~&C za&~kOxdVJ+KhH9Ph;jQcO(*IW=ULYu-&gRp3ZI2ocW;5vmd%U%gi7Hdumr%gHa3KgZ9=~ z*rLN~nH&d@8YC#oK+=c50XC-9U}g$c?bbp z!T6d`vxoXQgsr84PA&MVLA3nRHdU_$3j3=}SpQ6Uje!Gz8>+8RELuOe}SR_@0kD5^&*OY^{HE-Eb0fAh1Mg^&1d~ z!nX;?E(4#`o;dB39Svmj9*#C2dvSaNDdHhH{~%)i0Vt9V2N2)7691{^om`jbVyPKm zex>P^e599qO`&;Wy&Hmihs*FKlsnn#m#Z%S9?iJyu9!&k-+LeAU4u1G9{=}wy9O_V z5x=yR7mXHmnuFM`JT};7jr?^Jk4+Ri{}1n@>6H|%KpQ%;d#){MXs)lW^42F| z`TVAPU!I5@(M>)5H`a3aCw39~bNf>HpYhl({~k_>$cofw&PVbB=)KUBM7>B{!cmm Gp7|dcBBz)D literal 0 HcmV?d00001 diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_template.jpg b/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_template.jpg new file mode 100644 index 0000000000000000000000000000000000000000..393db734ef5f2870bf81dbe4c3c2658074e8c2ff GIT binary patch literal 42838 zcmZs?1B_)&*S~$*wr$(C&FN{|w(Xv_ZQIkfZQHgn)BnET=YI1(`SMj#Sy!#BTJ@`{ zvy;l+b-q@=b^(ae;!@%OpzlHpL<<0X?U`YVxm%b508&!a07w7;00Te(0{$OL_--M7 zGyJ#B_^!x6AON6m3;tcPfx!NU%f1=sU#|OR%70^kznST~FWk3n{jSX4jQVYdzbgvx zfAaj3%OVW)fAXwlFLga6q|1vW)0Dw{Oe;R-aA^vAx|Lh4+(SP;*m%rTrkpE)PqJQ_|dt9)8{kY#T z;Qti^`Ckn6Ukv?U4ErztGwwfy$^`%r6h;7yezWm+{qJ7_GdCM6HxtMIuaAb2iJOrT z0Qmpf_wU62$mvk)9-aI`rd?ptfZ*2BJhndn~knLKJEE| z?9asrq@@Z{J1}Ldrr90|q4y)4pR^l23_S@(Ake}F-Kq4X?xXU06&SA{i>^%xW< zBCk&!)v0B5KHh&T$+s>B_X^b3F2eL{!P*5ziGzFMBxyI9Q-3HiSR%0ANG-H&!)4Z6 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/objdetect/table_of_content_objdetect.markdown b/doc/tutorials/objdetect/table_of_content_objdetect.markdown index 0b019d88a5..6d646a8481 100644 --- a/doc/tutorials/objdetect/table_of_content_objdetect.markdown +++ b/doc/tutorials/objdetect/table_of_content_objdetect.markdown @@ -16,3 +16,13 @@ Ever wondered how your digital camera detects peoples and faces? Look here to fi - @subpage tutorial_traincascade This tutorial describes _opencv_traincascade_ application and its parameters. + +- @subpage tutorial_generalized_hough_ballard_guil + + Detect an object in a picture with the help of GeneralizedHoughBallard and GeneralizedHoughGuil. + + *Languages:* C++ + + *Compatibility:* \> OpenCV 3.4 + + *Author:* Markus Heck \ No newline at end of file diff --git a/doc/tutorials/objdetect/traincascade.markdown b/doc/tutorials/objdetect/traincascade.markdown index d78de2ec9a..7ff39b5a90 100644 --- a/doc/tutorials/objdetect/traincascade.markdown +++ b/doc/tutorials/objdetect/traincascade.markdown @@ -2,6 +2,8 @@ Cascade Classifier Training {#tutorial_traincascade} =========================== @prev_tutorial{tutorial_cascade_classifier} +@next_tutorial{tutorial_generalized_hough_ballard_guil} + Introduction ------------ diff --git a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp new file mode 100644 index 0000000000..3e0e095a9e --- /dev/null +++ b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp @@ -0,0 +1,100 @@ +/** + @file generalizedHoughTransform.cpp + @author Markus Heck + @brief Detects an object, given by a template, in an image using GeneralizedHoughBallard and GeneralizedHoughGuil. +*/ + +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" + +using namespace cv; +using namespace std; + +int main() { +// load source images + Mat image = imread("images/generalized_hough_mini_image.jpg"); + Mat imgTemplate = imread("images/generalized_hough_mini_template.jpg"); + +// create grayscale image and template + Mat templ = Mat(imgTemplate.rows, imgTemplate.cols, CV_8UC1); + Mat grayImage; + cvtColor(imgTemplate, templ, COLOR_RGB2GRAY); + cvtColor(image, grayImage, COLOR_RGB2GRAY); + +// create variable for location, scale and rotation of detected templates + vector positionBallard, positionGuil; + +// template width and height + int w = templ.cols; + int h = templ.rows; + +// create ballard and set options + Ptr ballard = createGeneralizedHoughBallard(); + ballard->setMinDist(10); + ballard->setLevels(360); + ballard->setDp(2); + ballard->setMaxBufferSize(1000); + ballard->setVotesThreshold(40); + + ballard->setCannyLowThresh(30); + ballard->setCannyHighThresh(110); + ballard->setTemplate(templ); + + +// create guil and set options + Ptr guil = createGeneralizedHoughGuil(); + guil->setMinDist(10); + guil->setLevels(360); + guil->setDp(3); + guil->setMaxBufferSize(1000); + + guil->setMinAngle(0); + guil->setMaxAngle(360); + guil->setAngleStep(1); + guil->setAngleThresh(1500); + + guil->setMinScale(0.5); + guil->setMaxScale(2.0); + guil->setScaleStep(0.05); + guil->setScaleThresh(50); + + guil->setPosThresh(10); + + guil->setCannyLowThresh(30); + guil->setCannyHighThresh(110); + + guil->setTemplate(templ); + + +// execute ballard detection + ballard->detect(grayImage, positionBallard); +// execute guil detection + guil->detect(grayImage, positionGuil); + + +// draw ballard + for (vector::iterator iter = positionBallard.begin(); iter != positionBallard.end(); ++iter) { + RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), + Size2f(w * (*iter)[2], h * (*iter)[2]), + (*iter)[3]); + Point2f vertices[4]; + rRect.points(vertices); + for (int i = 0; i < 4; i++) + line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255, 0, 0), 6); + } + +// draw guil + for (vector::iterator iter = positionGuil.begin(); iter != positionGuil.end(); ++iter) { + RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), + Size2f(w * (*iter)[2], h * (*iter)[2]), + (*iter)[3]); + Point2f vertices[4]; + rRect.points(vertices); + for (int i = 0; i < 4; i++) + line(image, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0), 2); + } + + imshow("result_img", image); + waitKey(); + return EXIT_SUCCESS; +} \ No newline at end of file From e2a9cff3dc1c595badc31cd80d152282a55afce6 Mon Sep 17 00:00:00 2001 From: Markus Heck Date: Thu, 8 Sep 2022 13:03:33 +0200 Subject: [PATCH 064/313] Include code snippets with doxygen notation and small fix in table_of_content_objdetect.markdown --- .../generalized_hough_ballard_guil.markdown | 98 ++----------------- .../table_of_content_objdetect.markdown | 8 +- .../generalizedHoughTransform.cpp | 10 ++ 3 files changed, 20 insertions(+), 96 deletions(-) diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown index 6507e0f4a1..b6f1479172 100644 --- a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown +++ b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown @@ -27,8 +27,7 @@ Example 4. Set the required parameters for both GeneralizedHough variants 5. Detect and show found results -Note: - +@note - Both variants can't be instantiated directly. Using the create methods is required. - Guil Hough is very slow. Calculating the results for the "mini" files used in this tutorial takes only a few seconds. With image and template in a higher resolution, as shown below, @@ -40,31 +39,14 @@ Note: ### Code The complete code for this tutorial is shown below. -@include generalizedHoughTransform.cpp +@include samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp Explanation ----------- ### Load image, template and setup variables -```c++ -// load source images -Mat image = imread("images/generalized_hough_mini_image.jpg"); -Mat imgTemplate = imread("images/generalized_hough_mini_template.jpg"); - -// create grayscale image and template -Mat templ = Mat(imgTemplate.rows, imgTemplate.cols, CV_8UC1); -Mat grayImage; -cvtColor(imgTemplate, templ, COLOR_RGB2GRAY); -cvtColor(image, grayImage, COLOR_RGB2GRAY); - -// create variable for location, scale and rotation of detected templates -vector positionBallard, positionGuil; - -// template width and height -int w = templ.cols; -int h = templ.rows; -``` +@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-load-and-setup The position vectors will contain the matches the detectors will find. Every entry contains four floating point values: @@ -79,87 +61,19 @@ An example could look as follows: `[200, 100, 0.9, 120]` ### Setup parameters -```c++ -// create ballard and set options -Ptr ballard = createGeneralizedHoughBallard(); -ballard->setMinDist(10); -ballard->setLevels(360); -ballard->setDp(2); -ballard->setMaxBufferSize(1000); -ballard->setVotesThreshold(40); - -ballard->setCannyLowThresh(30); -ballard->setCannyHighThresh(110); -ballard->setTemplate(templ); - - -// create guil and set options -Ptr guil = createGeneralizedHoughGuil(); -guil->setMinDist(10); -guil->setLevels(360); -guil->setDp(3); -guil->setMaxBufferSize(1000); - -guil->setMinAngle(0); -guil->setMaxAngle(360); -guil->setAngleStep(1); -guil->setAngleThresh(1500); - -guil->setMinScale(0.5); -guil->setMaxScale(2.0); -guil->setScaleStep(0.05); -guil->setScaleThresh(50); - -guil->setPosThresh(10); - -guil->setCannyLowThresh(30); -guil->setCannyHighThresh(110); - -guil->setTemplate(templ); -``` +@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-setup-parameters Finding the optimal values can end up in trial and error and depends on many factors, such as the image resolution. ### Run detection -```c++ -// execute ballard detection - ballard->detect(grayImage, positionBallard); -// execute guil detection - guil->detect(grayImage, positionGuil); -``` +@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-run As mentioned above, this step will take some time, especially with larger images and when using Guil. ### Draw results and show image -```c++ -// draw ballard -for (vector::iterator iter = positionBallard.begin(); iter != positionBallard.end(); ++iter) { -RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), -Size2f(w * (*iter)[2], h * (*iter)[2]), -(*iter)[3]); -Point2f vertices[4]; -rRect.points(vertices); -for (int i = 0; i < 4; i++) -line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255, 0, 0), 6); -} - -// draw guil -for (vector::iterator iter = positionGuil.begin(); iter != positionGuil.end(); ++iter) { -RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), -Size2f(w * (*iter)[2], h * (*iter)[2]), -(*iter)[3]); -Point2f vertices[4]; -rRect.points(vertices); -for (int i = 0; i < 4; i++) -line(image, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0), 2); -} - -imshow("result_img", image); -waitKey(); -return EXIT_SUCCESS; -``` +@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-draw-results Result ------ diff --git a/doc/tutorials/objdetect/table_of_content_objdetect.markdown b/doc/tutorials/objdetect/table_of_content_objdetect.markdown index 6d646a8481..9b8a09b4e8 100644 --- a/doc/tutorials/objdetect/table_of_content_objdetect.markdown +++ b/doc/tutorials/objdetect/table_of_content_objdetect.markdown @@ -19,10 +19,10 @@ Ever wondered how your digital camera detects peoples and faces? Look here to fi - @subpage tutorial_generalized_hough_ballard_guil - Detect an object in a picture with the help of GeneralizedHoughBallard and GeneralizedHoughGuil. - *Languages:* C++ - *Compatibility:* \> OpenCV 3.4 + *Compatibility:* \>= OpenCV 3.4 - *Author:* Markus Heck \ No newline at end of file + *Author:* Markus Heck + + Detect an object in a picture with the help of GeneralizedHoughBallard and GeneralizedHoughGuil. \ No newline at end of file diff --git a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp index 3e0e095a9e..2630dc1f6e 100644 --- a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp +++ b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp @@ -11,6 +11,7 @@ using namespace cv; using namespace std; int main() { + //! [generalized-hough-transform-load-and-setup] // load source images Mat image = imread("images/generalized_hough_mini_image.jpg"); Mat imgTemplate = imread("images/generalized_hough_mini_template.jpg"); @@ -27,7 +28,10 @@ int main() { // template width and height int w = templ.cols; int h = templ.rows; + //! [generalized-hough-transform-load-and-setup] + + //! [generalized-hough-transform-setup-parameters] // create ballard and set options Ptr ballard = createGeneralizedHoughBallard(); ballard->setMinDist(10); @@ -64,14 +68,18 @@ int main() { guil->setCannyHighThresh(110); guil->setTemplate(templ); + //! [generalized-hough-transform-setup-parameters] + //! [generalized-hough-transform-run] // execute ballard detection ballard->detect(grayImage, positionBallard); // execute guil detection guil->detect(grayImage, positionGuil); + //! [generalized-hough-transform-run] + //! [generalized-hough-transform-draw-results] // draw ballard for (vector::iterator iter = positionBallard.begin(); iter != positionBallard.end(); ++iter) { RotatedRect rRect = RotatedRect(Point2f((*iter)[0], (*iter)[1]), @@ -96,5 +104,7 @@ int main() { imshow("result_img", image); waitKey(); + //! [generalized-hough-transform-draw-results] + return EXIT_SUCCESS; } \ No newline at end of file From 9dc844a6e142eee62852c15194222acb758a5af3 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Fri, 9 Sep 2022 17:56:30 +0800 Subject: [PATCH 065/313] Merge pull request #22346 from fengyuentau:mat1d_part1 Changes separated from Mat 1D support in core #18594 (#22346) --- modules/core/src/arithm.cpp | 4 +- modules/core/src/minmax.cpp | 17 +++++-- modules/core/test/test_mat.cpp | 90 ++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 604b13101d..fbc4959253 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -168,7 +168,7 @@ static void binary_op( InputArray _src1, InputArray _src2, OutputArray _dst, if( dims1 <= 2 && dims2 <= 2 && kind1 == kind2 && sz1 == sz2 && type1 == type2 && !haveMask ) { - _dst.create(sz1, type1); + _dst.createSameSize(*psrc1, type1); CV_OCL_RUN(use_opencl, ocl_binary_op(*psrc1, *psrc2, _dst, _mask, bitwise, oclop, false)) @@ -1225,7 +1225,7 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op) if( kind1 == kind2 && src1.dims <= 2 && src2.dims <= 2 && src1.size() == src2.size() && src1.type() == src2.type() ) { int cn = src1.channels(); - _dst.create(src1.size(), CV_8UC(cn)); + _dst.createSameSize(src1, CV_8UC(cn)); Mat dst = _dst.getMat(); Size sz = getContinuousSize2D(src1, src2, dst, src1.channels()); BinaryFuncC cmpFn = getCmpFunc(depth1); diff --git a/modules/core/src/minmax.cpp b/modules/core/src/minmax.cpp index 96a7d027ad..d1ef797cda 100644 --- a/modules/core/src/minmax.cpp +++ b/modules/core/src/minmax.cpp @@ -1565,13 +1565,24 @@ void cv::minMaxLoc( InputArray _img, double* minVal, double* maxVal, { CV_INSTRUMENT_REGION(); - CV_Assert(_img.dims() <= 2); + int dims = _img.dims(); + CV_CheckLE(dims, 2, ""); minMaxIdx(_img, minVal, maxVal, (int*)minLoc, (int*)maxLoc, mask); if( minLoc ) - std::swap(minLoc->x, minLoc->y); + { + if (dims == 2) + std::swap(minLoc->x, minLoc->y); + else + minLoc->y = 0; + } if( maxLoc ) - std::swap(maxLoc->x, maxLoc->y); + { + if (dims == 2) + std::swap(maxLoc->x, maxLoc->y); + else + maxLoc->y = 0; + } } enum class ReduceMode diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 68de691260..9f4711a816 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2354,6 +2354,96 @@ TEST(Mat, regression_18473) EXPECT_EQ((int)5, (int)m.at(19, 49, 99)); } +// FITIT: remove DISABLE_ when 1D Mat is supported +TEST(Mat1D, DISABLED_basic) +{ + std::vector sizes { 100 }; + Mat m1(sizes, CV_8UC1, Scalar::all(5)); + m1.at(50) = 10; + EXPECT_FALSE(m1.empty()); + ASSERT_EQ(1, m1.dims); + ASSERT_EQ(1, m1.size.dims()); // hack map on .rows + EXPECT_EQ(Size(100, 1), m1.size()); + + { + SCOPED_TRACE("clone"); + Mat m = m1.clone(); + EXPECT_EQ(1, m.dims); + EXPECT_EQ(Size(100, 1), m.size()); + } + + { + SCOPED_TRACE("colRange()"); + Mat m = m1.colRange(Range(10, 30)); + EXPECT_EQ(1, m.dims); + EXPECT_EQ(Size(20, 1), m.size()); + } + + { + SCOPED_TRACE("reshape(1, 1)"); + Mat m = m1.reshape(1, 1); + EXPECT_EQ(1, m.dims); + EXPECT_EQ(Size(100, 1), m.size()); + } + + { + SCOPED_TRACE("reshape(1, 100)"); + Mat m = m1.reshape(1, 100); + EXPECT_EQ(2, m.dims); + EXPECT_EQ(Size(1, 100), m.size()); + } + + { + SCOPED_TRACE("reshape(1, {1, 100})"); + Mat m = m1.reshape(1, {1, 100}); + EXPECT_EQ(2, m.dims); + EXPECT_EQ(Size(100, 1), m.size()); + } + + { + SCOPED_TRACE("copyTo(std::vector)"); + std::vector dst; + m1.copyTo(dst); + EXPECT_EQ(100u, dst.size()); + } + + { + SCOPED_TRACE("copyTo(row2D)"); + Mat m(5, 100, CV_8UC1, Scalar::all(0)); + const Mat row2D = m.row(2); + EXPECT_NO_THROW(m1.copyTo(row2D)); + } + + { + SCOPED_TRACE("convertTo(row2D)"); + Mat m(5, 100, CV_32FC1, Scalar::all(0)); + const Mat row2D = m.row(2); + EXPECT_NO_THROW(m1.convertTo(row2D, CV_32FC1)); + } + + { + SCOPED_TRACE("CvMat"); + CvMat c_mat = cvMat(m1); + EXPECT_EQ(100, c_mat.cols); + EXPECT_EQ(1, c_mat.rows); + } + + { + SCOPED_TRACE("CvMatND"); + CvMatND c_mat = cvMatND(m1); + EXPECT_EQ(2, c_mat.dims); + EXPECT_EQ(100, c_mat.dim[0].size); + EXPECT_EQ(1, c_mat.dim[1].size); + } + + { + SCOPED_TRACE("minMaxLoc"); + Point pt; + minMaxLoc(m1, 0, 0, 0, &pt); + EXPECT_EQ(50, pt.x); + EXPECT_EQ(0, pt.y); + } +} TEST(Mat, ptrVecni_20044) { From 54089499516e81a6a2d261129451f79324a74a83 Mon Sep 17 00:00:00 2001 From: Markus Heck Date: Fri, 9 Sep 2022 15:27:11 +0200 Subject: [PATCH 066/313] fix typo and simplify example (grayscale template) --- .../generalized_hough_ballard_guil.markdown | 2 +- .../objectDetection/generalizedHoughTransform.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown index b6f1479172..c5dc2590af 100644 --- a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown +++ b/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown @@ -8,7 +8,7 @@ Object detection with Generalized Ballard and Guil Hough Transform {#tutorial_ge Goal ---- -In this tutorial you will lern how to: +In this tutorial you will learn how to: - Use @ref cv::GeneralizedHoughBallard and @ref cv::GeneralizedHoughGuil to detect an object diff --git a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp index 2630dc1f6e..dca52b1339 100644 --- a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp +++ b/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp @@ -12,14 +12,12 @@ using namespace std; int main() { //! [generalized-hough-transform-load-and-setup] -// load source images +// load source image and grayscale template Mat image = imread("images/generalized_hough_mini_image.jpg"); - Mat imgTemplate = imread("images/generalized_hough_mini_template.jpg"); + Mat templ = imread("images/generalized_hough_mini_template.jpg", IMREAD_GRAYSCALE); -// create grayscale image and template - Mat templ = Mat(imgTemplate.rows, imgTemplate.cols, CV_8UC1); +// create grayscale image Mat grayImage; - cvtColor(imgTemplate, templ, COLOR_RGB2GRAY); cvtColor(image, grayImage, COLOR_RGB2GRAY); // create variable for location, scale and rotation of detected templates From 4154bd06677c30475e2545cbee05906dd9a367cb Mon Sep 17 00:00:00 2001 From: wxsheng <78061310+gititgo@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:39:43 +0800 Subject: [PATCH 067/313] Add Loongson Advanced SIMD Extension support: -DCPU_BASELINE=LASX * Add Loongson Advanced SIMD Extension support: -DCPU_BASELINE=LASX * Add resize.lasx.cpp for Loongson SIMD acceleration * Add imgwarp.lasx.cpp for Loongson SIMD acceleration * Add LASX acceleration support for dnn/conv * Add CV_PAUSE(v) for Loongarch * Set LASX by default on Loongarch64 * LoongArch: tune test threshold for Core/HAL.mat_decomp/15 Co-authored-by: shengwenxue --- cmake/OpenCVCompilerOptimizations.cmake | 7 + cmake/OpenCVDetectCXXCompiler.cmake | 2 + cmake/checks/cpu_lasx.cpp | 23 + .../include/opencv2/core/cv_cpu_dispatch.h | 9 + .../core/include/opencv2/core/cv_cpu_helper.h | 21 + modules/core/include/opencv2/core/cvdef.h | 4 + .../core/include/opencv2/core/hal/intrin.hpp | 16 + .../include/opencv2/core/hal/intrin_lasx.hpp | 3236 +++++++++++++++++ modules/core/src/parallel_impl.cpp | 2 + modules/core/src/system.cpp | 6 + modules/core/test/test_hal_core.cpp | 4 + modules/dnn/CMakeLists.txt | 4 +- .../dnn/src/int8layers/convolution_layer.cpp | 18 +- .../src/int8layers/fully_connected_layer.cpp | 9 +- .../dnn/src/int8layers/layers_common.simd.hpp | 624 ++++ modules/dnn/src/layers/convolution_layer.cpp | 24 +- .../dnn/src/layers/fully_connected_layer.cpp | 9 +- modules/dnn/src/layers/layers_common.simd.hpp | 679 ++++ modules/imgproc/src/imgwarp.cpp | 7 + modules/imgproc/src/imgwarp.hpp | 7 + modules/imgproc/src/imgwarp.lasx.cpp | 98 + modules/imgproc/src/resize.cpp | 10 + modules/imgproc/src/resize.hpp | 9 + modules/imgproc/src/resize.lasx.cpp | 249 ++ 24 files changed, 5071 insertions(+), 6 deletions(-) create mode 100644 cmake/checks/cpu_lasx.cpp create mode 100644 modules/core/include/opencv2/core/hal/intrin_lasx.hpp create mode 100644 modules/imgproc/src/imgwarp.lasx.cpp create mode 100644 modules/imgproc/src/resize.lasx.cpp diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index cc0b5f8216..b1751e3987 100644 --- a/cmake/OpenCVCompilerOptimizations.cmake +++ b/cmake/OpenCVCompilerOptimizations.cmake @@ -50,6 +50,7 @@ list(APPEND CPU_ALL_OPTIMIZATIONS NEON VFPV3 FP16 NEON_DOTPROD) list(APPEND CPU_ALL_OPTIMIZATIONS MSA) list(APPEND CPU_ALL_OPTIMIZATIONS VSX VSX3) list(APPEND CPU_ALL_OPTIMIZATIONS RVV) +list(APPEND CPU_ALL_OPTIMIZATIONS LASX) list(REMOVE_DUPLICATES CPU_ALL_OPTIMIZATIONS) ocv_update(CPU_VFPV3_FEATURE_ALIAS "") @@ -380,6 +381,12 @@ elseif(RISCV) set(CPU_DISPATCH "RVV" CACHE STRING "${HELP_CPU_DISPATCH}") set(CPU_BASELINE "RVV" CACHE STRING "${HELP_CPU_BASELINE}") +elseif(LOONGARCH64) + ocv_update(CPU_LASX_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_lasx.cpp") + ocv_update(CPU_KNOWN_OPTIMIZATIONS "LASX") + ocv_update(CPU_LASX_FLAGS_ON "-mlasx") + set(CPU_BASELINE "LASX" CACHE STRING "${HELP_CPU_BASELINE}") + endif() # Helper values for cmake-gui diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake index 7f229cde96..8fe89b3fe0 100644 --- a/cmake/OpenCVDetectCXXCompiler.cmake +++ b/cmake/OpenCVDetectCXXCompiler.cmake @@ -100,6 +100,8 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips.*|MIPS.*)") set(MIPS 1) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv.*|RISCV.*)") set(RISCV 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(loongarch64.*|LOONGARCH64.*)") + set(LOONGARCH64 1) else() if(NOT OPENCV_SUPPRESS_MESSAGE_UNRECOGNIZED_SYSTEM_PROCESSOR) message(WARNING "OpenCV: unrecognized target processor configuration") diff --git a/cmake/checks/cpu_lasx.cpp b/cmake/checks/cpu_lasx.cpp new file mode 100644 index 0000000000..9d3b2a8725 --- /dev/null +++ b/cmake/checks/cpu_lasx.cpp @@ -0,0 +1,23 @@ +#include + +#if defined(__loongarch_asx) +# include +# define CV_LASX 1 +#endif + +#if defined CV_LASX +int test() +{ + const float src[] = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f }; + v8f32 val = (v8f32)__lasx_xvld((const float*)(src), 0); + return __lasx_xvpickve2gr_w(__lasx_xvftint_w_s (val), 7); +} +#else +#error "LASX is not supported" +#endif + +int main() +{ + printf("%d\n", test()); + return 0; +} diff --git a/modules/core/include/opencv2/core/cv_cpu_dispatch.h b/modules/core/include/opencv2/core/cv_cpu_dispatch.h index 12e4cb47b8..3235b6317e 100644 --- a/modules/core/include/opencv2/core/cv_cpu_dispatch.h +++ b/modules/core/include/opencv2/core/cv_cpu_dispatch.h @@ -172,6 +172,11 @@ # define CV_MSA 1 #endif +#ifdef CV_CPU_COMPILE_LASX +# include +# define CV_LASX 1 +#endif + #ifdef __EMSCRIPTEN__ # define CV_WASM_SIMD 1 # include @@ -370,3 +375,7 @@ struct VZeroUpperGuard { #ifndef CV_RVV # define CV_RVV 0 #endif + +#ifndef CV_LASX +# define CV_LASX 0 +#endif diff --git a/modules/core/include/opencv2/core/cv_cpu_helper.h b/modules/core/include/opencv2/core/cv_cpu_helper.h index 91b853de0c..41fc9d50fa 100644 --- a/modules/core/include/opencv2/core/cv_cpu_helper.h +++ b/modules/core/include/opencv2/core/cv_cpu_helper.h @@ -525,5 +525,26 @@ #endif #define __CV_CPU_DISPATCH_CHAIN_RVV(fn, args, mode, ...) CV_CPU_CALL_RVV(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_LASX +# define CV_TRY_LASX 1 +# define CV_CPU_FORCE_LASX 1 +# define CV_CPU_HAS_SUPPORT_LASX 1 +# define CV_CPU_CALL_LASX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_LASX_(fn, args) return (opt_LASX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_LASX +# define CV_TRY_LASX 1 +# define CV_CPU_FORCE_LASX 0 +# define CV_CPU_HAS_SUPPORT_LASX (cv::checkHardwareSupport(CV_CPU_LASX)) +# define CV_CPU_CALL_LASX(fn, args) if (CV_CPU_HAS_SUPPORT_LASX) return (opt_LASX::fn args) +# define CV_CPU_CALL_LASX_(fn, args) if (CV_CPU_HAS_SUPPORT_LASX) return (opt_LASX::fn args) +#else +# define CV_TRY_LASX 0 +# define CV_CPU_FORCE_LASX 0 +# define CV_CPU_HAS_SUPPORT_LASX 0 +# define CV_CPU_CALL_LASX(fn, args) +# define CV_CPU_CALL_LASX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_LASX(fn, args, mode, ...) CV_CPU_CALL_LASX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + #define CV_CPU_CALL_BASELINE(fn, args) return (cpu_baseline::fn args) #define __CV_CPU_DISPATCH_CHAIN_BASELINE(fn, args, mode, ...) CV_CPU_CALL_BASELINE(fn, args) /* last in sequence */ diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 21e3792162..95dc81fb46 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -279,6 +279,8 @@ namespace cv { #define CV_CPU_RVV 210 +#define CV_CPU_LASX 230 + // CPU features groups #define CV_CPU_AVX512_SKX 256 #define CV_CPU_AVX512_COMMON 257 @@ -336,6 +338,8 @@ enum CpuFeatures { CPU_RVV = 210, + CPU_LASX = 230, + CPU_AVX512_SKX = 256, //!< Skylake-X with AVX-512F/CD/BW/DQ/VL CPU_AVX512_COMMON = 257, //!< Common instructions AVX-512F/CD for all CPUs that support AVX-512 CPU_AVX512_KNL = 258, //!< Knights Landing with AVX-512F/CD/ER/PF diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index c12140bbf8..6eac27e763 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -231,8 +231,16 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; #elif CV_RVV && !defined(CV_FORCE_SIMD128_CPP) && !defined(CV_RVV_SCALABLE) #include "opencv2/core/hal/intrin_rvv.hpp" + #elif CV_RVV && !defined(CV_FORCE_SIMD128_CPP) && CV_RVV_SCALABLE #include "opencv2/core/hal/intrin_rvv_scalable.hpp" + +#elif CV_LASX + #if !defined(CV_FORCE_SIMD128_CPP) + #define CV_FORCE_SIMD128_CPP 1 + #endif +#include "opencv2/core/hal/intrin_cpp.hpp" + #else #include "opencv2/core/hal/intrin_cpp.hpp" @@ -267,6 +275,14 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; #endif +#if CV_LASX + +#define CV__SIMD_FORWARD 256 +#include "opencv2/core/hal/intrin_forward.hpp" +#include "opencv2/core/hal/intrin_lasx.hpp" + +#endif + //! @cond IGNORED namespace cv { diff --git a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp new file mode 100644 index 0000000000..37f2e3f81d --- /dev/null +++ b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp @@ -0,0 +1,3236 @@ +// 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_HAL_INTRIN_LASX_HPP +#define OPENCV_HAL_INTRIN_LASX_HPP + +#include +#include + +#define CV_SIMD256 1 +#define CV_SIMD256_64F 1 +#define CV_SIMD256_FP16 0 + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Utils //////////// + +inline __m256i _v256_setr_b(char v0, char v1, char v2, char v3, char v4, char v5, char v6, char v7, char v8, char v9, + char v10, char v11, char v12, char v13, char v14, char v15, char v16, char v17, char v18, char v19, + char v20, char v21, char v22, char v23, char v24, char v25, char v26, char v27, char v28, char v29, + char v30, char v31) +{ + return (__m256i)v32i8{ v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31 }; +} + +inline __m256i _v256_set_b(char v0, char v1, char v2, char v3, char v4, char v5, char v6, char v7, char v8, char v9, + char v10, char v11, char v12, char v13, char v14, char v15, char v16, char v17, char v18, char v19, + char v20, char v21, char v22, char v23, char v24, char v25, char v26, char v27, char v28, char v29, + char v30, char v31) +{ + return (__m256i)v32i8{ v31, v30, + v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, + v19, v18, v17, v16, v15, v14, v13, v12, v11, v10, + v9, v8, v7, v6, v5, v4, v3, v2, v1, v0 }; +} + +inline __m256i _v256_setr_h(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, short v12, short v13, short v14, short v15) +{ + return (__m256i)v16i16{ v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 }; +} + +inline __m256i _v256_setr_w(int v0, int v1, int v2, int v3, int v4, int v5, int v6, int v7) +{ + return (__m256i)v8i32{ v0, v1, v2, v3, v4, v5, v6, v7 }; +} + +inline __m256i _v256_set_w(int v0, int v1, int v2, int v3, int v4, int v5, int v6, int v7) +{ + return (__m256i)v8i32{ v7, v6, v5, v4, v3, v2, v1, v0 }; +} + +inline __m256i _v256_setall_w(int v0) +{ + return (__m256i)v8i32{ v0, v0, v0, v0, v0, v0, v0, v0 }; +} + +inline __m256i _v256_setr_d(int64 v0, int64 v1, int64 v2, int64 v3) +{ + return (__m256i)v4i64{ v0, v1, v2, v3 }; +} + +inline __m256i _v256_set_d(int64 v0, int64 v1, int64 v2, int64 v3) +{ + return (__m256i)v4i64{ v3, v2, v1, v0 }; +} + +inline __m256 _v256_setr_ps(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7) +{ + return (__m256)v8f32{ v0, v1, v2, v3, v4, v5, v6, v7 }; +} + +inline __m256 _v256_setall_ps(float f32) +{ + return (__m256)v8f32{ f32, f32, f32, f32, f32, f32, f32, f32 }; +} + +inline __m256d _v256_setr_pd(double v0, double v1, double v2, double v3) +{ + return (__m256d)v4f64{ v0, v1, v2, v3 }; +} + +inline __m256d _v256_setall_pd(double f64) +{ + return (__m256d)v4f64{ f64, f64, f64, f64 }; +} + +inline __m256i _lasx_packus_h(const __m256i& a, const __m256i& b) +{ + __m256i u8min = __lasx_xvreplgr2vr_h(0); + __m256i u8max = __lasx_xvreplgr2vr_h(255); + __m256i sat_a = __lasx_xvmax_h(a, u8min); + sat_a = __lasx_xvmin_h(sat_a, u8max); + __m256i sat_b = __lasx_xvmax_h(b, u8min); + sat_b = __lasx_xvmin_h(sat_b, u8max); + __m256i byteIndex = _v256_setr_b(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30); + return __lasx_xvshuf_b(sat_b, sat_a, byteIndex); +} + +inline __m256i _lasx_packs_h(const __m256i& a, const __m256i& b) +{ + __m256i s8min = __lasx_xvreplgr2vr_h(-128); + __m256i s8max = __lasx_xvreplgr2vr_h(127); + __m256i sat_a = __lasx_xvmax_h(a, s8min); + sat_a = __lasx_xvmin_h(sat_a, s8max); + __m256i sat_b = __lasx_xvmax_h(b, s8min); + sat_b = __lasx_xvmin_h(sat_b, s8max); + __m256i byteIndex = _v256_setr_b(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30); + return __lasx_xvshuf_b(sat_b, sat_a, byteIndex); +} + +inline __m256i _lasx_packus_w(const __m256i& a, const __m256i& b) +{ + __m256i u16min = __lasx_xvreplgr2vr_w(0); + __m256i u16max = __lasx_xvreplgr2vr_w(0xffff); + __m256i sat_a = __lasx_xvmax_w(a, u16min); + sat_a = __lasx_xvmin_w(sat_a, u16max); + __m256i sat_b = __lasx_xvmax_w(b, u16min); + sat_b = __lasx_xvmin_w(sat_b, u16max); + __m256i hwordIndex = _v256_setr_h(0, 2, 4, 6, 8, 10, 12, 14, + 0, 2, 4, 6, 8, 10, 12, 14); + return __lasx_xvshuf_h(hwordIndex, sat_b, sat_a); +} + +inline __m256i _lasx_packs_w(const __m256i& a, const __m256i& b) +{ + __m256i s16min = __lasx_xvreplgr2vr_w(-0x8000); + __m256i s16max = __lasx_xvreplgr2vr_w(0x7fff); + __m256i sat_a = __lasx_xvmax_w(a, s16min); + sat_a = __lasx_xvmin_w(sat_a, s16max); + __m256i sat_b = __lasx_xvmax_w(b, s16min); + sat_b = __lasx_xvmin_w(sat_b, s16max); + __m256i hwordIndex = _v256_setr_h(0, 2, 4, 6, 8, 10, 12, 14, + 0, 2, 4, 6, 8, 10, 12, 14); + return __lasx_xvshuf_h(hwordIndex, sat_b, sat_a); +} + +inline __m256i _v256_combine(const __m128i& lo, const __m128i& hi) +{ return __lasx_xvpermi_q(*((__m256i*)&lo), *((__m256i*)&hi), 0x02); } + +inline __m256 _v256_combine(const __m128& lo, const __m128& hi) +{ return __m256(__lasx_xvpermi_q(*((__m256i*)&lo), *((__m256i*)&hi), 0x02)); } + +inline __m256d _v256_combine(const __m128d& lo, const __m128d& hi) +{ return __m256d(__lasx_xvpermi_q(*((__m256i*)&lo), *((__m256i*)&hi), 0x02)); } + +inline __m256i _v256_shuffle_odd_64(const __m256i& v) +{ return __lasx_xvpermi_d(v, 0xd8); } + +inline __m256d _v256_shuffle_odd_64(const __m256d& v) +{ return __m256d(__lasx_xvpermi_d(*((__m256i*)&v), 0xd8)); } + +//LASX: only use for permute WITHOUT zero clearing +template +inline __m256i _v256_permute2x128(const __m256i& a, const __m256i& b) +{ return __lasx_xvpermi_q(a, b, imm); } + +template +inline __m256 _v256_permute2x128(const __m256& a, const __m256& b) +{ return __m256(__lasx_xvpermi_q(*((__m256i*)&a), *((__m256i*)&b), imm)); } + +template +inline __m256d _v256_permute2x128(const __m256d& a, const __m256d& b) +{ return __m256d(__lasx_xvpermi_q(*((__m256i*)&a), *((__m256i*)&b), imm)); } + +template +inline _Tpvec v256_permute2x128(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_v256_permute2x128(a.val, b.val)); } + +template +inline __m256i _v256_permute4x64(const __m256i& a) +{ return __lasx_xvpermi_d(a, imm); } + +template +inline __m256d _v256_permute4x64(const __m256d& a) +{ return __m256d(__lasx_xvpermi_d(*((__m256i*)&a), imm)); } + +template +inline _Tpvec v256_permute4x64(const _Tpvec& a) +{ return _Tpvec(_v256_permute4x64(a.val)); } + +inline __m128i _v256_extract_high(const __m256i& v) +{ __m256i temp256i = __lasx_xvpermi_q(v, v, 0x31); + return *((__m128i*)&temp256i); } + +inline __m128 _v256_extract_high(const __m256& v) +{ return __m128(_v256_extract_high(*((__m256i*)&v))); } + +inline __m128d _v256_extract_high(const __m256d& v) +{ return __m128d(_v256_extract_high(*((__m256i*)&v))); } + +inline __m128i _v256_extract_low(const __m256i& v) +{ return *((__m128i*)&v); } + +inline __m128 _v256_extract_low(const __m256& v) +{ return __m128(_v256_extract_low(*((__m256i*)&v))); } + +inline __m128d _v256_extract_low(const __m256d& v) +{ return __m128d(_v256_extract_low(*((__m256i*)&v))); } + +inline __m256i _v256_packs_epu32(const __m256i& a, const __m256i& b) +{ + const __m256i maxv = __lasx_xvreplgr2vr_w(65535); + __m256i am = __lasx_xvmin_wu(a, maxv); + __m256i bm = __lasx_xvmin_wu(b, maxv); + return _lasx_packus_w(am, bm); +} + +template +inline int _v256_extract_b(const __m256i& a) +{ + int des[1] = {0}; + __lasx_xvstelm_b(a, des, 0, i); + return des[0]; +} + +template +inline int _v256_extract_h(const __m256i& a) +{ + int des[1] = {0}; + __lasx_xvstelm_h(a, des, 0, i); + return des[0]; +} + +template +inline int _v256_extract_w(const __m256i& a) +{ + return __lasx_xvpickve2gr_w(a, i); +} + +template +inline int64 _v256_extract_d(const __m256i& a) +{ + return __lasx_xvpickve2gr_d(a, i); +} + +///////// Types //////////// + +struct v_uint8x32 +{ + typedef uchar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_uint8x32(__m256i v) : val(v) {} + v_uint8x32(uchar v0, uchar v1, uchar v2, uchar v3, + uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, + uchar v12, uchar v13, uchar v14, uchar v15, + uchar v16, uchar v17, uchar v18, uchar v19, + uchar v20, uchar v21, uchar v22, uchar v23, + uchar v24, uchar v25, uchar v26, uchar v27, + uchar v28, uchar v29, uchar v30, uchar v31) + { + val = _v256_setr_b((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6 , (char)v7, (char)v8, (char)v9, + (char)v10, (char)v11, (char)v12, (char)v13, (char)v14, (char)v15, + (char)v16, (char)v17, (char)v18, (char)v19, (char)v20, (char)v21, + (char)v22, (char)v23, (char)v24, (char)v25, (char)v26, (char)v27, + (char)v28, (char)v29, (char)v30, (char)v31); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint8x32() {} + + uchar get0() const { + uchar des[1] = {0}; + __lasx_xvstelm_b(val, des, 0, 0); + return des[0]; + } +}; + +struct v_int8x32 +{ + typedef schar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_int8x32(__m256i v) : val(v) {} + v_int8x32(schar v0, schar v1, schar v2, schar v3, + schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, + schar v12, schar v13, schar v14, schar v15, + schar v16, schar v17, schar v18, schar v19, + schar v20, schar v21, schar v22, schar v23, + schar v24, schar v25, schar v26, schar v27, + schar v28, schar v29, schar v30, schar v31) + { + val = _v256_setr_b(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, + v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int8x32() {} + + schar get0() const { + schar des[1] = {0}; + __lasx_xvstelm_b(val, des, 0, 0); + return des[0]; + } +}; + +struct v_uint16x16 +{ + typedef ushort lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_uint16x16(__m256i v) : val(v) {} + v_uint16x16(ushort v0, ushort v1, ushort v2, ushort v3, + ushort v4, ushort v5, ushort v6, ushort v7, + ushort v8, ushort v9, ushort v10, ushort v11, + ushort v12, ushort v13, ushort v14, ushort v15) + { + val = _v256_setr_h((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7, (short)v8, (short)v9, + (short)v10, (short)v11, (short)v12, (short)v13, (short)v14, (short)v15); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint16x16() {} + + ushort get0() const { + ushort des[1] = {0}; + __lasx_xvstelm_h(val, des, 0, 0); + return des[0]; + } +}; + +struct v_int16x16 +{ + typedef short lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_int16x16(__m256i v) : val(v) {} + v_int16x16(short v0, short v1, short v2, short v3, + short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, + short v12, short v13, short v14, short v15) + { + val = _v256_setr_h(v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int16x16() {} + + short get0() const { + short des[1] = {0}; + __lasx_xvstelm_h(val, des, 0, 0); + return des[0]; + } +}; + +struct v_uint32x8 +{ + typedef unsigned lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_uint32x8(__m256i v) : val(v) {} + v_uint32x8(unsigned v0, unsigned v1, unsigned v2, unsigned v3, + unsigned v4, unsigned v5, unsigned v6, unsigned v7) + { + val = _v256_setr_w((unsigned)v0, (unsigned)v1, (unsigned)v2, + (unsigned)v3, (unsigned)v4, (unsigned)v5, (unsigned)v6, (unsigned)v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint32x8() {} + + unsigned get0() const { return __lasx_xvpickve2gr_wu(val, 0); } +}; + +struct v_int32x8 +{ + typedef int lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_int32x8(__m256i v) : val(v) {} + v_int32x8(int v0, int v1, int v2, int v3, + int v4, int v5, int v6, int v7) + { + val = _v256_setr_w(v0, v1, v2, v3, v4, v5, v6, v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int32x8() {} + + int get0() const { return __lasx_xvpickve2gr_w(val, 0); } +}; + +struct v_float32x8 +{ + typedef float lane_type; + enum { nlanes = 8 }; + __m256 val; + + explicit v_float32x8(__m256 v) : val(v) {} + explicit v_float32x8(__m256i v) { val = *((__m256*)&v); } + v_float32x8(float v0, float v1, float v2, float v3, + float v4, float v5, float v6, float v7) + { + val = _v256_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_float32x8() {} + + float get0() const { + float des[1] = {0}; + __lasx_xvstelm_w(*((__m256i*)&val), des, 0, 0); + return des[0]; + } + + int get0toint() const { + int des[1] = {0}; + __lasx_xvstelm_w(*((__m256i*)&val), des, 0, 0); + return des[0]; + } +}; + +struct v_uint64x4 +{ + typedef uint64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_uint64x4(__m256i v) : val(v) {} + v_uint64x4(uint64 v0, uint64 v1, uint64 v2, uint64 v3) + { val = _v256_setr_d((int64)v0, (int64)v1, (int64)v2, (int64)v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_uint64x4() {} + + uint64 get0() const + { + return __lasx_xvpickve2gr_du(val, 0); + } +}; + +struct v_int64x4 +{ + typedef int64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_int64x4(__m256i v) : val(v) {} + v_int64x4(int64 v0, int64 v1, int64 v2, int64 v3) + { val = _v256_setr_d(v0, v1, v2, v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_int64x4() {} + + int64 get0() const + { + return __lasx_xvpickve2gr_d(val, 0); + } +}; + +struct v_float64x4 +{ + typedef double lane_type; + enum { nlanes = 4 }; + __m256d val; + + explicit v_float64x4(__m256d v) : val(v) {} + explicit v_float64x4(__m256i v) { val = *((__m256d*)&v); } + v_float64x4(double v0, double v1, double v2, double v3) + { val = _v256_setr_pd(v0, v1, v2, v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_float64x4() {} + + double get0() const { + double des[1] = {0}; + __lasx_xvstelm_d(*((__m256i*)&val), des, 0, 0); + return des[0]; + } + + int64 get0toint64() const { + int64 des[1] = {0}; + __lasx_xvstelm_d(*((__m256i*)&val), des, 0, 0); + return des[0]; + } +}; + +//////////////// Load and store operations /////////////// + +#define OPENCV_HAL_IMPL_LASX_LOADSTORE(_Tpvec, _Tp) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(__lasx_xvld(ptr, 0)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(__lasx_xvld(ptr, 0)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + __m128i v128 = __lsx_vld(ptr, 0); \ + return _Tpvec(*((__m256i*)&v128)); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + __m128i vlo = __lsx_vld(ptr0, 0); \ + __m128i vhi = __lsx_vld(ptr1, 0); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + __lasx_xvst(a.val, ptr, 0); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + __lasx_xvst(a.val, ptr, 0); \ + else \ + __lasx_xvst(a.val, ptr, 0); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { __lsx_vst(_v256_extract_low(a.val), ptr, 0); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { __lsx_vst(_v256_extract_high(a.val), ptr, 0); } + +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_uint8x32, uchar) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_int8x32, schar) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_uint16x16, ushort) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_int16x16, short) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_uint32x8, unsigned) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_int32x8, int) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_uint64x4, uint64) +OPENCV_HAL_IMPL_LASX_LOADSTORE(v_int64x4, int64) + + +#define OPENCV_HAL_IMPL_LASX_LOADSTORE_FLT(_Tpvec, _Tp, halfreg) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(__lasx_xvld(ptr, 0)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(__lasx_xvld(ptr, 0)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + __m128i v128 = __lsx_vld(ptr, 0); \ + return _Tpvec(*((__m256i*)&v128)); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + halfreg vlo = __lsx_vld(ptr0, 0); \ + halfreg vhi = __lsx_vld(ptr1, 0); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { __lasx_xvst(a.val, ptr, 0); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + __lasx_xvst(a.val, ptr, 0); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + __lasx_xvst(a.val, ptr, 0); \ + else \ + __lasx_xvst(a.val, ptr, 0); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { __lsx_vst(_v256_extract_low(a.val), ptr, 0); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { __lsx_vst(_v256_extract_high(a.val), ptr, 0); } + +OPENCV_HAL_IMPL_LASX_LOADSTORE_FLT(v_float32x8, float, __m128i) +OPENCV_HAL_IMPL_LASX_LOADSTORE_FLT(v_float64x4, double, __m128i) + + +inline __m256i _lasx_256_castps_si256(const __m256& v) +{ return __m256i(v); } + +inline __m256i _lasx_256_castpd_si256(const __m256d& v) +{ return __m256i(v); } + +#define OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, _Tpvecf, suffix, cast) \ + inline _Tpvec v_reinterpret_as_##suffix(const _Tpvecf& a) \ + { return _Tpvec(cast(a.val)); } + +#define OPENCV_HAL_IMPL_LASX_INIT(_Tpvec, _Tp, suffix, ssuffix, ctype_s) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(__lasx_xvreplgr2vr_d(0)); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(__lasx_xvreplgr2vr_##ssuffix((ctype_s)v)); } \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_float32x8, suffix, _lasx_256_castps_si256) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_float64x4, suffix, _lasx_256_castpd_si256) + +OPENCV_HAL_IMPL_LASX_INIT(v_uint8x32, uchar, u8, b, int) +OPENCV_HAL_IMPL_LASX_INIT(v_int8x32, schar, s8, b, int) +OPENCV_HAL_IMPL_LASX_INIT(v_uint16x16, ushort, u16, h, int) +OPENCV_HAL_IMPL_LASX_INIT(v_int16x16, short, s16, h, int) +OPENCV_HAL_IMPL_LASX_INIT(v_uint32x8, unsigned, u32, w, int) +OPENCV_HAL_IMPL_LASX_INIT(v_int32x8, int, s32, w, int) +OPENCV_HAL_IMPL_LASX_INIT(v_uint64x4, uint64, u64, d, long int) +OPENCV_HAL_IMPL_LASX_INIT(v_int64x4, int64, s64, d, long int) + + +inline __m256 _lasx_256_castsi256_ps(const __m256i &v) +{ return __m256(v); } + +inline __m256d _lasx_256_castsi256_pd(const __m256i &v) +{ return __m256d(v); } + +#define OPENCV_HAL_IMPL_LASX_INIT_FLT(_Tpvec, _Tp, suffix, zsuffix, cast) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(__lasx_xvreplgr2vr_d(0)); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(_v256_setall_##zsuffix(v)); } \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint8x32, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int8x32, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint16x16, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int16x16, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint32x8, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int32x8, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_uint64x4, suffix, cast) \ + OPENCV_HAL_IMPL_LASX_CAST(_Tpvec, v_int64x4, suffix, cast) + +OPENCV_HAL_IMPL_LASX_INIT_FLT(v_float32x8, float, f32, ps, _lasx_256_castsi256_ps) +OPENCV_HAL_IMPL_LASX_INIT_FLT(v_float64x4, double, f64, pd, _lasx_256_castsi256_pd) + +inline v_float32x8 v_reinterpret_as_f32(const v_float32x8& a) +{ return a; } +inline v_float32x8 v_reinterpret_as_f32(const v_float64x4& a) +{ return v_float32x8(_lasx_256_castps_si256(__m256(a.val))); } + +inline v_float64x4 v_reinterpret_as_f64(const v_float64x4& a) +{ return a; } +inline v_float64x4 v_reinterpret_as_f64(const v_float32x8& a) +{ return v_float64x4(_lasx_256_castpd_si256(__m256d(a.val))); } + + +//////////////// Variant Value reordering /////////////// + +// unpacks +#define OPENCV_HAL_IMPL_LASX_UNPACK(_Tpvec, suffix) \ + inline _Tpvec v256_unpacklo(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(__lasx_xvilvl_##suffix(__m256i(b.val), __m256i(a.val))); } \ + inline _Tpvec v256_unpackhi(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(__lasx_xvilvh_##suffix(__m256i(b.val), __m256i(a.val))); } + +OPENCV_HAL_IMPL_LASX_UNPACK(v_uint8x32, b) +OPENCV_HAL_IMPL_LASX_UNPACK(v_int8x32, b) +OPENCV_HAL_IMPL_LASX_UNPACK(v_uint16x16, h) +OPENCV_HAL_IMPL_LASX_UNPACK(v_int16x16, h) +OPENCV_HAL_IMPL_LASX_UNPACK(v_uint32x8, w) +OPENCV_HAL_IMPL_LASX_UNPACK(v_int32x8, w) +OPENCV_HAL_IMPL_LASX_UNPACK(v_uint64x4, d) +OPENCV_HAL_IMPL_LASX_UNPACK(v_int64x4, d) +OPENCV_HAL_IMPL_LASX_UNPACK(v_float32x8, w) +OPENCV_HAL_IMPL_LASX_UNPACK(v_float64x4, d) + + +// shuffle +// todo: emulate 64bit +#define OPENCV_HAL_IMPL_LASX_SHUFFLE(_Tpvec, intrin) \ + template \ + inline _Tpvec v256_shuffle(const _Tpvec& a) \ + { return _Tpvec(__lasx_xvshuf4i_##intrin(a.val, m)); } + +OPENCV_HAL_IMPL_LASX_SHUFFLE(v_uint32x8, w) +OPENCV_HAL_IMPL_LASX_SHUFFLE(v_int32x8, w) + +template +inline v_float32x8 v256_shuffle(const v_float32x8 &a) +{ return v_float32x8(__lasx_xvshuf4i_w(*((__m256i*)&a.val), m)); } + +template +inline v_float64x4 v256_shuffle(const v_float64x4 &a) +{ + int imm8 = m & 0b0001; //0 or 1 + if (m & 0x0b0010) imm8 |= 0b0100; + //else imm8 |= 0b0000; + if (m & 0x0b0100) imm8 |= 0b110000; //2 or 3 + else imm8 |= 0b100000; + if (m & 0x0b1000) imm8 |= 0b11000000; + else imm8 |= 0b10000000; + + return v_float64x4(__lasx_xvpermi_d(*((__m256i*)&a.val), imm8)); +} +template +inline void v256_zip(const _Tpvec& a, const _Tpvec& b, _Tpvec& ab0, _Tpvec& ab1) +{ + ab0 = v256_unpacklo(a, b); + ab1 = v256_unpackhi(a, b); +} + +template +inline _Tpvec v256_combine_diagonal(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(__lasx_xvpermi_q(a.val, b.val, 0x12)); } + +inline v_float32x8 v256_combine_diagonal(const v_float32x8& a, const v_float32x8& b) +{ return v_float32x8(__lasx_xvpermi_q(a.val, b.val, 0x12)); } + +inline v_float64x4 v256_combine_diagonal(const v_float64x4& a, const v_float64x4& b) +{ return v_float64x4(__lasx_xvpermi_q(a.val, b.val, 0x12)); } + +template +inline _Tpvec v256_alignr_128(const _Tpvec& a, const _Tpvec& b) +{ return v256_permute2x128<0x03>(a, b); } + +inline __m256i _v256_alignr_b(const __m256i &a, const __m256i &b, const int imm) +{ + if (imm == 8) { + return __lasx_xvshuf4i_d(b, a, 0x9); // b.d1 a.d0 b.d3 a.d2 + } else { + __m256i byteIndex = _v256_setr_b(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + return __lasx_xvshuf_b(a, b, __lasx_xvadd_b(__lasx_xvreplgr2vr_b(imm), byteIndex)); + } +} + +template +inline _Tpvec v256_alignr_64(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_v256_alignr_b(a.val, b.val, 8)); } +inline v_float64x4 v256_alignr_64(const v_float64x4& a, const v_float64x4& b) +{ return v_float64x4(__lasx_xvshuf4i_d(b.val, a.val, 0x9)); } // b.d1 a.d0 b.d3 a.d2 +// todo: emulate float32 + +template +inline _Tpvec v256_swap_halves(const _Tpvec& a) +{ return v256_permute2x128<1>(a, a); } + +template +inline _Tpvec v256_reverse_64(const _Tpvec& a) +{ return v256_permute4x64<0x1b>(a); } + + +// ZIP +#define OPENCV_HAL_IMPL_LASX_ZIP(_Tpvec) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x02>(a, b); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x13>(a, b); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { \ + _Tpvec a1b0 = v256_alignr_128(a, b); \ + c = v256_combine_diagonal(a, a1b0); \ + d = v256_combine_diagonal(a1b0, b); \ + } \ + inline void v_zip(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& ab0, _Tpvec& ab1) \ + { \ + _Tpvec ab0ab2, ab1ab3; \ + v256_zip(a, b, ab0ab2, ab1ab3); \ + v_recombine(ab0ab2, ab1ab3, ab0, ab1); \ + } + +OPENCV_HAL_IMPL_LASX_ZIP(v_uint8x32) +OPENCV_HAL_IMPL_LASX_ZIP(v_int8x32) +OPENCV_HAL_IMPL_LASX_ZIP(v_uint16x16) +OPENCV_HAL_IMPL_LASX_ZIP(v_int16x16) +OPENCV_HAL_IMPL_LASX_ZIP(v_uint32x8) +OPENCV_HAL_IMPL_LASX_ZIP(v_int32x8) +OPENCV_HAL_IMPL_LASX_ZIP(v_uint64x4) +OPENCV_HAL_IMPL_LASX_ZIP(v_int64x4) +OPENCV_HAL_IMPL_LASX_ZIP(v_float32x8) +OPENCV_HAL_IMPL_LASX_ZIP(v_float64x4) + +////////// Arithmetic, bitwise and comparison operations ///////// + +/** Arithmetics **/ +#define OPENCV_HAL_IMPL_LASX_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_uint8x32, __lasx_xvsadd_bu) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_uint8x32, __lasx_xvssub_bu) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_int8x32, __lasx_xvsadd_b) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_int8x32, __lasx_xvssub_b) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_uint16x16, __lasx_xvsadd_hu) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_uint16x16, __lasx_xvssub_hu) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_int16x16, __lasx_xvsadd_h) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_int16x16, __lasx_xvssub_h) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_uint32x8, __lasx_xvadd_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_uint32x8, __lasx_xvsub_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(*, v_uint32x8, __lasx_xvmul_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_int32x8, __lasx_xvadd_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_int32x8, __lasx_xvsub_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(*, v_int32x8, __lasx_xvmul_w) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_uint64x4, __lasx_xvadd_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_uint64x4, __lasx_xvsub_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_int64x4, __lasx_xvadd_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_int64x4, __lasx_xvsub_d) + +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_float32x8, __lasx_xvfadd_s) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_float32x8, __lasx_xvfsub_s) +OPENCV_HAL_IMPL_LASX_BIN_OP(*, v_float32x8, __lasx_xvfmul_s) +OPENCV_HAL_IMPL_LASX_BIN_OP(/, v_float32x8, __lasx_xvfdiv_s) +OPENCV_HAL_IMPL_LASX_BIN_OP(+, v_float64x4, __lasx_xvfadd_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(-, v_float64x4, __lasx_xvfsub_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(*, v_float64x4, __lasx_xvfmul_d) +OPENCV_HAL_IMPL_LASX_BIN_OP(/, v_float64x4, __lasx_xvfdiv_d) + +// saturating multiply 8-bit, 16-bit +inline v_uint8x32 operator * (const v_uint8x32& a, const v_uint8x32& b) +{ + v_uint16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_int8x32 operator * (const v_int8x32& a, const v_int8x32& b) +{ + v_int16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_uint16x16 operator * (const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i pl = __lasx_xvmul_h(a.val, b.val); + __m256i ph = __lasx_xvmuh_hu(a.val, b.val); + __m256i p0 = __lasx_xvilvl_h(ph, pl); + __m256i p1 = __lasx_xvilvh_h(ph, pl); + return v_uint16x16(_v256_packs_epu32(p0, p1)); +} +inline v_int16x16 operator * (const v_int16x16& a, const v_int16x16& b) +{ + __m256i pl = __lasx_xvmul_h(a.val, b.val); + __m256i ph = __lasx_xvmuh_h(a.val, b.val); + __m256i p0 = __lasx_xvilvl_h(ph, pl); + __m256i p1 = __lasx_xvilvh_h(ph, pl); + return v_int16x16(_lasx_packs_w(p0, p1)); +} +inline v_uint8x32& operator *= (v_uint8x32& a, const v_uint8x32& b) +{ a = a * b; return a; } +inline v_int8x32& operator *= (v_int8x32& a, const v_int8x32& b) +{ a = a * b; return a; } +inline v_uint16x16& operator *= (v_uint16x16& a, const v_uint16x16& b) +{ a = a * b; return a; } +inline v_int16x16& operator *= (v_int16x16& a, const v_int16x16& b) +{ a = a * b; return a; } + +/** Non-saturating arithmetics **/ + +#define OPENCV_HAL_IMPL_LASX_BIN_FUNC(func, _Tpvec, intrin) \ + inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_add_wrap, v_uint8x32, __lasx_xvadd_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_add_wrap, v_int8x32, __lasx_xvadd_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_add_wrap, v_uint16x16, __lasx_xvadd_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_add_wrap, v_int16x16, __lasx_xvadd_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_sub_wrap, v_uint8x32, __lasx_xvsub_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_sub_wrap, v_int8x32, __lasx_xvsub_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_sub_wrap, v_uint16x16, __lasx_xvsub_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_sub_wrap, v_int16x16, __lasx_xvsub_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_mul_wrap, v_uint16x16, __lasx_xvmul_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_mul_wrap, v_int16x16, __lasx_xvmul_h) + +inline v_uint8x32 v_mul_wrap(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i ad = __lasx_xvsrai_h(a.val, 8); + __m256i bd = __lasx_xvsrai_h(b.val, 8); + __m256i p0 = __lasx_xvmul_h(a.val, b.val); + __m256i p1 = __lasx_xvslli_h(__lasx_xvmul_h(ad, bd), 8); + + const __m256i b01 = __lasx_xvreplgr2vr_w(0xFF00FF00); + return v_uint8x32(__lasx_xvbitsel_v(p0, p1, b01)); +} +inline v_int8x32 v_mul_wrap(const v_int8x32& a, const v_int8x32& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +// Multiply and expand +inline void v_mul_expand(const v_uint8x32& a, const v_uint8x32& b, + v_uint16x16& c, v_uint16x16& d) +{ + v_uint16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x32& a, const v_int8x32& b, + v_int16x16& c, v_int16x16& d) +{ + v_int16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x16& a, const v_int16x16& b, + v_int32x8& c, v_int32x8& d) +{ + v_int16x16 vhi = v_int16x16(__lasx_xvmuh_h(a.val, b.val)); + + v_int16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_s32(v0); + d = v_reinterpret_as_s32(v1); +} + +inline void v_mul_expand(const v_uint16x16& a, const v_uint16x16& b, + v_uint32x8& c, v_uint32x8& d) +{ + v_uint16x16 vhi = v_uint16x16(__lasx_xvmuh_hu(a.val, b.val)); + + v_uint16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_u32(v0); + d = v_reinterpret_as_u32(v1); +} + +inline void v_mul_expand(const v_uint32x8& a, const v_uint32x8& b, + v_uint64x4& c, v_uint64x4& d) +{ + __m256i v0 = __lasx_xvmulwev_d_wu(a.val, b.val); + __m256i v1 = __lasx_xvmulwod_d_wu(a.val, b.val); + v_zip(v_uint64x4(v0), v_uint64x4(v1), c, d); +} + +inline v_int16x16 v_mul_hi(const v_int16x16& a, const v_int16x16& b) { return v_int16x16(__lasx_xvmuh_h(a.val, b.val)); } +inline v_uint16x16 v_mul_hi(const v_uint16x16& a, const v_uint16x16& b) { return v_uint16x16(__lasx_xvmuh_hu(a.val, b.val)); } + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_LASX_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, srai) \ + inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ + { return _Tpuvec(__lasx_xvsll_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ + { return _Tpsvec(__lasx_xvsll_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ + { return _Tpuvec(__lasx_xvsrl_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ + { return _Tpsvec(srai(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + template \ + inline _Tpuvec v_shl(const _Tpuvec& a) \ + { return _Tpuvec(__lasx_xvsll_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + template \ + inline _Tpsvec v_shl(const _Tpsvec& a) \ + { return _Tpsvec(__lasx_xvsll_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + template \ + inline _Tpuvec v_shr(const _Tpuvec& a) \ + { return _Tpuvec(__lasx_xvsrl_##suffix(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } \ + template \ + inline _Tpsvec v_shr(const _Tpsvec& a) \ + { return _Tpsvec(srai(a.val, __lasx_xvreplgr2vr_##suffix(imm))); } + +OPENCV_HAL_IMPL_LASX_SHIFT_OP(v_uint16x16, v_int16x16, h, __lasx_xvsra_h) +OPENCV_HAL_IMPL_LASX_SHIFT_OP(v_uint32x8, v_int32x8, w, __lasx_xvsra_w) + +inline __m256i _v256_srai_dx(const __m256i a, const __m256i shift) +{ + __m256i d = __lasx_xvreplgr2vr_d((int64)1 << 63); + __m256i r = __lasx_xvsrl_d(__lasx_xvadd_d(a, d), shift); + return __lasx_xvsub_d(r, __lasx_xvsrl_d(d, shift)); +} +OPENCV_HAL_IMPL_LASX_SHIFT_OP(v_uint64x4, v_int64x4, d, _v256_srai_dx) + + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_LASX_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_LASX_BIN_OP(&, _Tpvec, __lasx_xvand_##suffix) \ + OPENCV_HAL_IMPL_LASX_BIN_OP(|, _Tpvec, __lasx_xvor_##suffix) \ + OPENCV_HAL_IMPL_LASX_BIN_OP(^, _Tpvec, __lasx_xvxor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(__lasx_xvxor_##suffix(a.val, not_const)); } + +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_uint8x32, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_int8x32, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_uint16x16, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_int16x16, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_uint32x8, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_int32x8, v, __lasx_xvreplgr2vr_w(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_uint64x4, v, __lasx_xvreplgr2vr_d(-1)) +OPENCV_HAL_IMPL_LASX_LOGIC_OP(v_int64x4, v, __lasx_xvreplgr2vr_d(-1)) + +#define OPENCV_HAL_IMPL_LASX_FLOAT_BIN_OP(bin_op, _Tpvec, intrin, cast) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(*((__m256i*)(&a.val)), *((__m256i*)(&b.val)))); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { __m256i c = intrin(*((__m256i*)(&a.val)), *((__m256i*)(&b.val))); a.val = cast(c); return a; } + +#define OPENCV_HAL_IMPL_LASX_FLOAT_LOGIC_OP(_Tpvec, suffix, not_const, cast) \ + OPENCV_HAL_IMPL_LASX_FLOAT_BIN_OP(&, _Tpvec, __lasx_xvand_##suffix, cast) \ + OPENCV_HAL_IMPL_LASX_FLOAT_BIN_OP(|, _Tpvec, __lasx_xvor_##suffix, cast) \ + OPENCV_HAL_IMPL_LASX_FLOAT_BIN_OP(^, _Tpvec, __lasx_xvxor_##suffix, cast) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(__lasx_xvxor_##suffix(*((__m256i*)(&a.val)), not_const)); } + +OPENCV_HAL_IMPL_LASX_FLOAT_LOGIC_OP(v_float32x8, v, __lasx_xvreplgr2vr_w(-1), _lasx_256_castsi256_ps) +OPENCV_HAL_IMPL_LASX_FLOAT_LOGIC_OP(v_float64x4, v, __lasx_xvreplgr2vr_d(-1), _lasx_256_castsi256_pd) + +/** Select **/ +#define OPENCV_HAL_IMPL_LASX_SELECT(_Tpvec) \ + inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(__lasx_xvbitsel_v(b.val, a.val, mask.val)); } + +OPENCV_HAL_IMPL_LASX_SELECT(v_uint8x32) +OPENCV_HAL_IMPL_LASX_SELECT(v_int8x32) +OPENCV_HAL_IMPL_LASX_SELECT(v_uint16x16) +OPENCV_HAL_IMPL_LASX_SELECT(v_int16x16) +OPENCV_HAL_IMPL_LASX_SELECT(v_uint32x8) +OPENCV_HAL_IMPL_LASX_SELECT(v_int32x8) + +inline v_float32x8 v_select(const v_float32x8 &mask, const v_float32x8 &a, const v_float32x8 &b) +{ return v_float32x8(__lasx_xvbitsel_v(*((__m256i*)&b.val), *((__m256i*)&a.val), *((__m256i*)&mask.val))); } + +inline v_float64x4 v_select(const v_float64x4 &mask, const v_float64x4 &a, const v_float64x4 &b) +{ return v_float64x4(__lasx_xvbitsel_v(*((__m256i*)&b.val), *((__m256i*)&a.val), *((__m256i*)&mask.val))); } + +/** Comparison **/ +#define OPENCV_HAL_IMPL_LASX_CMP_OP_OV(_Tpvec) \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } \ + inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ + { return b > a; } \ + inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a < b); } \ + inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ + { return b >= a; } + +#define OPENCV_HAL_IMPL_LASX_CMP_OP_INT(_Tpuvec, _Tpsvec, suffix, usuffix) \ + inline _Tpuvec operator == (const _Tpuvec& a, const _Tpuvec& b) \ + { return _Tpuvec(__lasx_xvseq_##suffix(a.val, b.val)); } \ + inline _Tpuvec operator > (const _Tpuvec& a, const _Tpuvec& b) \ + { \ + return _Tpuvec(__lasx_xvslt_##usuffix(b.val, a.val)); \ + } \ + inline _Tpsvec operator == (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(__lasx_xvseq_##suffix(a.val, b.val)); } \ + inline _Tpsvec operator > (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(__lasx_xvslt_##suffix(b.val, a.val)); } \ + OPENCV_HAL_IMPL_LASX_CMP_OP_OV(_Tpuvec) \ + OPENCV_HAL_IMPL_LASX_CMP_OP_OV(_Tpsvec) + +OPENCV_HAL_IMPL_LASX_CMP_OP_INT(v_uint8x32, v_int8x32, b, bu) +OPENCV_HAL_IMPL_LASX_CMP_OP_INT(v_uint16x16, v_int16x16, h, hu) +OPENCV_HAL_IMPL_LASX_CMP_OP_INT(v_uint32x8, v_int32x8, w, wu) + +#define OPENCV_HAL_IMPL_LASX_CMP_OP_64BIT(_Tpvec, suffix) \ + inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(__lasx_xvseq_##suffix(a.val, b.val)); } \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } + +OPENCV_HAL_IMPL_LASX_CMP_OP_64BIT(v_uint64x4, d) +OPENCV_HAL_IMPL_LASX_CMP_OP_64BIT(v_int64x4, d) + +#define OPENCV_HAL_IMPL_LASX_CMP_FLT(bin_op, suffix, _Tpvec, ssuffix) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(__lasx_##suffix##_##ssuffix(a.val, b.val)); } + +#define OPENCV_HAL_IMPL_LASX_CMP_OP_FLT(_Tpvec, ssuffix) \ + OPENCV_HAL_IMPL_LASX_CMP_FLT(==, xvfcmp_ceq, _Tpvec, ssuffix) \ + OPENCV_HAL_IMPL_LASX_CMP_FLT(!=, xvfcmp_cne, _Tpvec, ssuffix) \ + OPENCV_HAL_IMPL_LASX_CMP_FLT(<, xvfcmp_clt, _Tpvec, ssuffix) \ + OPENCV_HAL_IMPL_LASX_CMP_FLT(<=, xvfcmp_cle, _Tpvec, ssuffix) + +OPENCV_HAL_IMPL_LASX_CMP_OP_FLT(v_float32x8, s) +OPENCV_HAL_IMPL_LASX_CMP_OP_FLT(v_float64x4, d) + +inline v_float32x8 operator > (const v_float32x8 &a, const v_float32x8 &b) +{ return v_float32x8(__lasx_xvfcmp_clt_s(b.val, a.val)); } + +inline v_float32x8 operator >= (const v_float32x8 &a, const v_float32x8 &b) +{ return v_float32x8(__lasx_xvfcmp_cle_s(b.val, a.val)); } + +inline v_float64x4 operator > (const v_float64x4 &a, const v_float64x4 &b) +{ return v_float64x4(__lasx_xvfcmp_clt_d(b.val, a.val)); } + +inline v_float64x4 operator >= (const v_float64x4 &a, const v_float64x4 &b) +{ return v_float64x4(__lasx_xvfcmp_cle_d(b.val, a.val)); } + +inline v_float32x8 v_not_nan(const v_float32x8& a) +{ return v_float32x8(__lasx_xvfcmp_cor_s(a.val, a.val)); } +inline v_float64x4 v_not_nan(const v_float64x4& a) +{ return v_float64x4(__lasx_xvfcmp_cor_d(a.val, a.val)); } + +/** min/max **/ +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_uint8x32, __lasx_xvmin_bu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_uint8x32, __lasx_xvmax_bu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_int8x32, __lasx_xvmin_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_int8x32, __lasx_xvmax_b) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_uint16x16, __lasx_xvmin_hu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_uint16x16, __lasx_xvmax_hu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_int16x16, __lasx_xvmin_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_int16x16, __lasx_xvmax_h) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_uint32x8, __lasx_xvmin_wu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_uint32x8, __lasx_xvmax_wu) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_int32x8, __lasx_xvmin_w) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_int32x8, __lasx_xvmax_w) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_float32x8, __lasx_xvfmin_s) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_float32x8, __lasx_xvfmax_s) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_min, v_float64x4, __lasx_xvfmin_d) +OPENCV_HAL_IMPL_LASX_BIN_FUNC(v_max, v_float64x4, __lasx_xvfmax_d) + +/** Rotate **/ +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_R = (16 - imm) & 0xFF}; + enum {IMM_R2 = (32 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _v256_permute2x128<0x21>(a.val, b.val); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_v256_alignr_b(a.val, swap, IMM_R)); + return v_uint8x32(_v256_alignr_b(swap, b.val, IMM_R2)); // imm < 32 +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _v256_permute2x128<0x03>(a.val, b.val); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_v256_alignr_b(swap, a.val, imm)); + return v_uint8x32(_v256_alignr_b(b.val, swap, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + enum {IMM_R = (16 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i vzero = __lasx_xvreplgr2vr_w(0); + __m256i swapz = __lasx_xvpermi_q(a.val, vzero, 0x20);; + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_v256_alignr_b(a.val, swapz, IMM_R)); + return v_uint8x32(__lasx_xvbsll_v(swapz, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i vzero = __lasx_xvreplgr2vr_w(0); + __m256i swapz = __lasx_xvpermi_q(vzero, a.val, 0x21);; + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_v256_alignr_b(swapz, a.val, imm)); + return v_uint8x32(__lasx_xvbsrl_v(swapz, IMM_L)); +} + +#define OPENCV_HAL_IMPL_LASX_ROTATE_CAST(intrin, _Tpvec, cast) \ + template \ + inline _Tpvec intrin(const _Tpvec& a, const _Tpvec& b) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a), \ + v_reinterpret_as_u8(b)); \ + return _Tpvec(cast(ret.val)); \ + } \ + template \ + inline _Tpvec intrin(const _Tpvec& a) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a)); \ + return _Tpvec(cast(ret.val)); \ + } + +#define OPENCV_HAL_IMPL_LASX_ROTATE(_Tpvec) \ + OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_left, _Tpvec, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_right, _Tpvec, OPENCV_HAL_NOP) + +OPENCV_HAL_IMPL_LASX_ROTATE(v_int8x32) +OPENCV_HAL_IMPL_LASX_ROTATE(v_uint16x16) +OPENCV_HAL_IMPL_LASX_ROTATE(v_int16x16) +OPENCV_HAL_IMPL_LASX_ROTATE(v_uint32x8) +OPENCV_HAL_IMPL_LASX_ROTATE(v_int32x8) +OPENCV_HAL_IMPL_LASX_ROTATE(v_uint64x4) +OPENCV_HAL_IMPL_LASX_ROTATE(v_int64x4) + +OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_left, v_float32x8, _lasx_256_castsi256_ps) +OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_right, v_float32x8, _lasx_256_castsi256_ps) +OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_left, v_float64x4, _lasx_256_castsi256_pd) +OPENCV_HAL_IMPL_LASX_ROTATE_CAST(v_rotate_right, v_float64x4, _lasx_256_castsi256_pd) + +/** Reverse **/ +inline v_uint8x32 v_reverse(const v_uint8x32 &a) +{ + static const __m256i perm = _v256_setr_b( + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + __m256i vec = __lasx_xvshuf_b(a.val, a.val, perm); + return v_uint8x32(__lasx_xvpermi_q(vec, vec, 1)); +} + +inline v_int8x32 v_reverse(const v_int8x32 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x16 v_reverse(const v_uint16x16 &a) +{ + static const __m256i perm = _v256_setr_b( + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + __m256i vec = __lasx_xvshuf_b(a.val, a.val, perm); + return v_uint16x16(__lasx_xvpermi_q(vec, vec, 1)); +} + +inline v_int16x16 v_reverse(const v_int16x16 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x8 v_reverse(const v_uint32x8 &a) +{ + static const __m256i perm = _v256_setr_w(7, 6, 5, 4, 3, 2, 1, 0); + return v_uint32x8(__lasx_xvperm_w(a.val, perm)); +} + +inline v_int32x8 v_reverse(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x8 v_reverse(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x4 v_reverse(const v_uint64x4 &a) +{ + return v_uint64x4(__lasx_xvpermi_d(a.val, 0x1b)); +} + +inline v_int64x4 v_reverse(const v_int64x4 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x4 v_reverse(const v_float64x4 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +////////// Reduce and mask ///////// + +/** Reduce **/ +// this function is return a[0]+a[1]+...+a[31] +inline unsigned v_reduce_sum(const v_uint8x32& a) +{ + __m256i t1 = __lasx_xvhaddw_hu_bu(a.val, a.val); + __m256i t2 = __lasx_xvhaddw_wu_hu(t1, t1); + __m256i t3 = __lasx_xvhaddw_du_wu(t2, t2); + return (unsigned)(((v4u64)t3)[0]+((v4u64)t3)[1]+((v4u64)t3)[2]+((v4u64)t3)[3]); +} +inline int v_reduce_sum(const v_int8x32& a) +{ + __m256i t1 = __lasx_xvhaddw_h_b(a.val, a.val); + __m256i t2 = __lasx_xvhaddw_w_h(t1, t1); + __m256i t3 = __lasx_xvhaddw_d_w(t2, t2); + return (int)(((v4i64)t3)[0]+((v4i64)t3)[1]+((v4i64)t3)[2]+((v4i64)t3)[3]); +} + + +#define OPENCV_HAL_IMPL_LASX_REDUCE_32(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i val = intrin(_v256_extract_low(a.val), _v256_extract_high(a.val)); \ + val = intrin(val, __lsx_vbsrl_v(val,8)); \ + val = intrin(val, __lsx_vbsrl_v(val,4)); \ + val = intrin(val, __lsx_vbsrl_v(val,2)); \ + val = intrin(val, __lsx_vbsrl_v(val,1)); \ + return (sctype)__lsx_vpickve2gr_w(val, 0); \ + } + +OPENCV_HAL_IMPL_LASX_REDUCE_32(v_uint8x32, uchar, min, __lsx_vmin_bu) +OPENCV_HAL_IMPL_LASX_REDUCE_32(v_int8x32, schar, min, __lsx_vmin_b) +OPENCV_HAL_IMPL_LASX_REDUCE_32(v_uint8x32, uchar, max, __lsx_vmax_bu) +OPENCV_HAL_IMPL_LASX_REDUCE_32(v_int8x32, schar, max, __lsx_vmax_b) + +#define OPENCV_HAL_IMPL_LASX_REDUCE_16(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, __lsx_vbsrl_v(v0, 8)); \ + v0 = intrin(v0, __lsx_vbsrl_v(v0, 4)); \ + v0 = intrin(v0, __lsx_vbsrl_v(v0, 2)); \ + return (sctype) __lsx_vpickve2gr_w(v0, 0); \ + } + +OPENCV_HAL_IMPL_LASX_REDUCE_16(v_uint16x16, ushort, min, __lsx_vmin_hu) +OPENCV_HAL_IMPL_LASX_REDUCE_16(v_int16x16, short, min, __lsx_vmin_h) +OPENCV_HAL_IMPL_LASX_REDUCE_16(v_uint16x16, ushort, max, __lsx_vmax_hu) +OPENCV_HAL_IMPL_LASX_REDUCE_16(v_int16x16, short, max, __lsx_vmax_h) + +#define OPENCV_HAL_IMPL_LASX_REDUCE_8(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, __lsx_vbsrl_v(v0, 8)); \ + v0 = intrin(v0, __lsx_vbsrl_v(v0, 4)); \ + return (sctype) __lsx_vpickve2gr_w(v0, 0); \ + } + +OPENCV_HAL_IMPL_LASX_REDUCE_8(v_uint32x8, unsigned, min, __lsx_vmin_wu) +OPENCV_HAL_IMPL_LASX_REDUCE_8(v_int32x8, int, min, __lsx_vmin_w) +OPENCV_HAL_IMPL_LASX_REDUCE_8(v_uint32x8, unsigned, max, __lsx_vmax_wu) +OPENCV_HAL_IMPL_LASX_REDUCE_8(v_int32x8, int, max, __lsx_vmax_w) + +#define OPENCV_HAL_IMPL_LASX_REDUCE_FLT(func, intrin) \ + inline float v_reduce_##func(const v_float32x8& a) \ + { \ + __m128 v0 = _v256_extract_low(a.val); \ + __m128 v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, __m128(__lsx_vpermi_w(*((__m128i*)&v0), *((__m128i*)&v0), 0x0e))); \ + v0 = intrin(v0, __m128(__lsx_vpermi_w(*((__m128i*)&v0), *((__m128i*)&v0), 0x01))); \ + float *fvalue = (float*)&v0; \ + return fvalue[0]; \ + } + +OPENCV_HAL_IMPL_LASX_REDUCE_FLT(min, __lsx_vfmin_s) +OPENCV_HAL_IMPL_LASX_REDUCE_FLT(max, __lsx_vfmax_s) + +inline int v_reduce_sum(const v_int32x8& a) +{ + __m256i t1 = __lasx_xvhaddw_d_w(a.val, a.val); + return (int)(((v4i64)t1)[0]+((v4i64)t1)[1]+((v4i64)t1)[2]+((v4i64)t1)[3]); +} + +inline unsigned v_reduce_sum(const v_uint32x8& a) +{ return v_reduce_sum(v_reinterpret_as_s32(a)); } + +inline int v_reduce_sum(const v_int16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline unsigned v_reduce_sum(const v_uint16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +inline float v_reduce_sum(const v_float32x8& a) +{ + float result = 0; + float *pa = (float*)&a; + for (int i = 0; i < 2; ++i) { + result += pa[i*4] + pa[i*4+1] + pa[i*4+2] + pa[i*4+3]; + } + return result; +} + +inline uint64 v_reduce_sum(const v_uint64x4& a) +{ + uint64 *pa = (uint64*)&a; + return pa[0] + pa[1] + pa[2] + pa[3]; +} +inline int64 v_reduce_sum(const v_int64x4& a) +{ + int64 *pa = (int64*)&a; + return pa[0] + pa[1] + pa[2] + pa[3]; +} +inline double v_reduce_sum(const v_float64x4& a) +{ + double *pa = (double*)&a; + return pa[0] + pa[1] + pa[2] + pa[3]; +} + +inline v_float32x8 v_reduce_sum4(const v_float32x8& a, const v_float32x8& b, + const v_float32x8& c, const v_float32x8& d) +{ + float *pa = (float*)&a; + float *pb = (float*)&b; + float *pc = (float*)&c; + float *pd = (float*)&d; + + float v0 = pa[0] + pa[1] + pa[2] + pa[3]; + float v1 = pb[0] + pb[1] + pb[2] + pb[3]; + float v2 = pc[0] + pc[1] + pc[2] + pc[3]; + float v3 = pd[0] + pd[1] + pd[2] + pd[3]; + float v4 = pa[4] + pa[5] + pa[6] + pa[7]; + float v5 = pb[4] + pb[5] + pb[6] + pb[7]; + float v6 = pc[4] + pc[5] + pc[6] + pc[7]; + float v7 = pd[4] + pd[5] + pd[6] + pd[7]; + return v_float32x8(v0, v1, v2, v3, v4, v5, v6, v7); +} + +inline unsigned v_reduce_sad(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i t0 = __lasx_xvabsd_bu(a.val, b.val); + __m256i t1 = __lasx_xvhaddw_hu_bu(t0, t0); + __m256i t2 = __lasx_xvhaddw_wu_hu(t1, t1); + __m256i t3 = __lasx_xvhaddw_du_wu(t2, t2); + return (unsigned)(((v4u64)t3)[0]+((v4u64)t3)[1]+((v4u64)t3)[2]+((v4u64)t3)[3]); +} +inline unsigned v_reduce_sad(const v_int8x32& a, const v_int8x32& b) +{ + __m256i t0 = __lasx_xvabsd_b(a.val, b.val); + __m256i t1 = __lasx_xvhaddw_hu_bu(t0, t0); + __m256i t2 = __lasx_xvhaddw_wu_hu(t1, t1); + __m256i t3 = __lasx_xvhaddw_du_wu(t2, t2); + return (unsigned)(((v4u64)t3)[0]+((v4u64)t3)[1]+((v4u64)t3)[2]+((v4u64)t3)[3]); +} +inline unsigned v_reduce_sad(const v_uint16x16& a, const v_uint16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_add_wrap(a - b, b - a), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x16& a, const v_int16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x8& a, const v_uint32x8& b) +{ + return v_reduce_sum(v_max(a, b) - v_min(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 m = a < b; + return v_reduce_sum(v_reinterpret_as_u32(((a - b) ^ m) - m)); +} +inline float v_reduce_sad(const v_float32x8& a, const v_float32x8& b) +{ + v_float32x8 a_b = a - b; + return v_reduce_sum(v_float32x8(*((__m256i*)&a_b.val) & __lasx_xvreplgr2vr_w(0x7fffffff))); +} + +/** Popcount **/ +inline v_uint8x32 v_popcount(const v_uint8x32& a) +{ + __m256i _popcnt_table = _v256_setr_b(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); + __m256i _popcnt_mask = __lasx_xvreplgr2vr_b(0x0F); + return v_uint8x32(__lasx_xvadd_b(__lasx_xvshuf_b(_popcnt_table, _popcnt_table, __lasx_xvand_v(a.val, _popcnt_mask)), + __lasx_xvshuf_b(_popcnt_table, _popcnt_table, __lasx_xvand_v(__lasx_xvsrli_h(a.val, 4), _popcnt_mask)))); +} +inline v_uint16x16 v_popcount(const v_uint16x16& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v_uint16x16(__lasx_xvreplgr2vr_h(0x00ff)); +} +inline v_uint32x8 v_popcount(const v_uint32x8& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v_uint32x8(__lasx_xvreplgr2vr_w(0x000000ff)); +} +inline v_uint64x4 v_popcount(const v_uint64x4& a) +{ + v_uint8x32 atemp = v_popcount(v_reinterpret_as_u8(a)); + uint8_t *pa = (uint8_t*)&atemp; + uint64 v[4]; + for (int i = 0; i < 4; ++i) { + v[i] = pa[i*8] + pa[i*8+1] + pa[i*8+2] + pa[i*8+3] + pa[i*8+4] + pa[i*8+5] + pa[i*8+6] + pa[i*8+7]; + } + return v_uint64x4(v[0], v[1], v[2], v[3]); +} +inline v_uint8x32 v_popcount(const v_int8x32& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x16 v_popcount(const v_int16x16& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x8 v_popcount(const v_int32x8& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x4 v_popcount(const v_int64x4& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +/** Mask **/ +#define OPENCV_HAL_IMPL_REINTERPRET_INT(ft, tt) \ +inline tt reinterpret_int(ft x) { union { ft l; tt i; } v; v.l = x; return v.i; } +OPENCV_HAL_IMPL_REINTERPRET_INT(uchar, schar) +OPENCV_HAL_IMPL_REINTERPRET_INT(schar, schar) +OPENCV_HAL_IMPL_REINTERPRET_INT(ushort, short) +OPENCV_HAL_IMPL_REINTERPRET_INT(short, short) +OPENCV_HAL_IMPL_REINTERPRET_INT(unsigned, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(int, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(float, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(uint64, int64) +OPENCV_HAL_IMPL_REINTERPRET_INT(int64, int64) +OPENCV_HAL_IMPL_REINTERPRET_INT(double, int64) + +inline int v_signmask(const v_int8x32& a) +{ + int mask = 0; + int8_t *pa = (int8_t*)&a; + for( int i = 0; i < 32; i++ ) + mask |= (reinterpret_int(pa[i]) < 0) << i; + return mask; +} +inline int v_signmask(const v_uint8x32& a) +{ return v_signmask(v_reinterpret_as_s8(a)); } + +inline int v_signmask(const v_int16x16& a) +{ return v_signmask(v_pack(a, a)) & 0xFFFF; } +inline int v_signmask(const v_uint16x16& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } + +inline int v_signmask(const v_int32x8& a) +{ + int mask = 0; + int *pa = (int*)&a; + for( int i = 0; i < 8; i++ ) + mask |= (pa[i] < 0) << i; + return mask; +} +inline int v_signmask(const v_uint32x8& a) +{ return v_signmask(*(v_int32x8*)(&a)); } + +inline int v_signmask(const v_int64x4& a) +{ + int mask = 0; + int64 *pa = (int64*)&a; + for( int i = 0; i < 4; i++ ) + mask |= (pa[i] < 0) << i; + return mask; +} +inline int v_signmask(const v_uint64x4& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } + +inline int v_signmask(const v_float32x8& a) +{ return v_signmask(*(v_int32x8*)(&a)); } + +inline int v_signmask(const v_float64x4& a) +{ return v_signmask(*(v_int64x4*)(&a)); } + +inline int v_scan_forward(const v_int8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +/** Checks **/ +#define OPENCV_HAL_IMPL_LASX_CHECK(_Tpvec, allmask) \ + inline bool v_check_all(const _Tpvec& a) { return v_signmask(a) == allmask; } \ + inline bool v_check_any(const _Tpvec& a) { return v_signmask(a) != 0; } +OPENCV_HAL_IMPL_LASX_CHECK(v_uint8x32, -1) +OPENCV_HAL_IMPL_LASX_CHECK(v_int8x32, -1) +OPENCV_HAL_IMPL_LASX_CHECK(v_uint32x8, 255) +OPENCV_HAL_IMPL_LASX_CHECK(v_int32x8, 255) +OPENCV_HAL_IMPL_LASX_CHECK(v_uint64x4, 15) +OPENCV_HAL_IMPL_LASX_CHECK(v_int64x4, 15) +OPENCV_HAL_IMPL_LASX_CHECK(v_float32x8, 255) +OPENCV_HAL_IMPL_LASX_CHECK(v_float64x4, 15) + +#define OPENCV_HAL_IMPL_LASX_CHECK_SHORT(_Tpvec) \ + inline bool v_check_all(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) == 0xaaaaaaaa; } \ + inline bool v_check_any(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) != 0; } +OPENCV_HAL_IMPL_LASX_CHECK_SHORT(v_uint16x16) +OPENCV_HAL_IMPL_LASX_CHECK_SHORT(v_int16x16) + +////////// Other math ///////// + +/** Some frequent operations **/ +#define OPENCV_HAL_IMPL_LASX_MULADD(_Tpvec, suffix) \ + inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(__lasx_xvfmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(__lasx_xvfmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_sqrt(const _Tpvec& x) \ + { return _Tpvec(__lasx_xvfsqrt_##suffix(x.val)); } \ + inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_fma(a, a, b * b); } \ + inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_sqrt(v_fma(a, a, b*b)); } + +OPENCV_HAL_IMPL_LASX_MULADD(v_float32x8, s) +OPENCV_HAL_IMPL_LASX_MULADD(v_float64x4, d) + +inline v_int32x8 v_fma(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return a * b + c; +} + +inline v_int32x8 v_muladd(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x8 v_invsqrt(const v_float32x8& x) +{ + v_float32x8 half = x * v_float32x8(0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5); + v_float32x8 t = v_float32x8(__lasx_xvfrsqrt_s(x.val)); + t *= v_float32x8(1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5) - ((t * t) * half); + return t; +} + +inline v_float64x4 v_invsqrt(const v_float64x4& x) +{ + return v_float64x4(1., 1., 1., 1.) / v_sqrt(x); +} + +/** Absolute values **/ +#define OPENCV_HAL_IMPL_LASX_ABS(_Tpvec, suffix) \ + inline v_u##_Tpvec v_abs(const v_##_Tpvec& x) \ + { return v_u##_Tpvec(__lasx_xvabsd_##suffix(x.val, __lasx_xvreplgr2vr_w(0))); } + +OPENCV_HAL_IMPL_LASX_ABS(int8x32, b) +OPENCV_HAL_IMPL_LASX_ABS(int16x16, h) +OPENCV_HAL_IMPL_LASX_ABS(int32x8, w) + +inline v_float32x8 v_abs(const v_float32x8& x) +{ return v_float32x8(*((__m256i*)&x) & __lasx_xvreplgr2vr_w(0x7fffffff)); } +inline v_float64x4 v_abs(const v_float64x4& x) +{ return v_float64x4(*((__m256i*)&x) & __lasx_xvreplgr2vr_d(0x7fffffffffffffff)); } + +/** Absolute difference **/ +inline v_uint8x32 v_absdiff(const v_uint8x32& a, const v_uint8x32& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x16 v_absdiff(const v_uint16x16& a, const v_uint16x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x8 v_absdiff(const v_uint32x8& a, const v_uint32x8& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x32 v_absdiff(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = v_sub_wrap(a, b); + v_int8x32 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} + +inline v_uint16x16 v_absdiff(const v_int16x16& a, const v_int16x16& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } + +inline v_uint32x8 v_absdiff(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 d = a - b; + v_int32x8 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +inline v_float32x8 v_absdiff(const v_float32x8& a, const v_float32x8& b) +{ return v_abs(a - b); } + +inline v_float64x4 v_absdiff(const v_float64x4& a, const v_float64x4& b) +{ return v_abs(a - b); } + +/** Saturating absolute difference **/ +inline v_int8x32 v_absdiffs(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = a - b; + v_int8x32 m = a < b; + return (d ^ m) - m; +} +inline v_int16x16 v_absdiffs(const v_int16x16& a, const v_int16x16& b) +{ return v_max(a, b) - v_min(a, b); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x8 v_round(const v_float32x8& a) +{ return v_int32x8(__lasx_xvftint_w_s(a.val)); } + +inline v_int32x8 v_round(const v_float64x4& a) +{ __m256i t = __lasx_xvftint_w_d(a.val, a.val); + return v_int32x8(__lasx_xvpermi_d(t, 0x88)); } + +inline v_int32x8 v_round(const v_float64x4& a, const v_float64x4& b) +{ + __m256i abi = __lasx_xvftint_w_d(b.val, a.val); + return v_int32x8(__lasx_xvpermi_d(abi, 0b11011000)); //3120 +} + +inline v_int32x8 v_trunc(const v_float32x8& a) +{ return v_int32x8(__lasx_xvftintrz_w_s(a.val)); } + +inline v_int32x8 v_trunc(const v_float64x4& a) +{ __m256i t = __lasx_xvftintrz_w_d(a.val, a.val); + return v_int32x8(__lasx_xvpermi_d(t, 0x88)); } + +inline v_int32x8 v_floor(const v_float32x8& a) +{ return v_int32x8(__lasx_xvftintrz_w_s(__m256(__lasx_xvfrintrm_s(a.val)))); } + +inline v_int32x8 v_floor(const v_float64x4& a) +{ return v_trunc(v_float64x4(__lasx_xvfrintrm_d(a.val))); } + +inline v_int32x8 v_ceil(const v_float32x8& a) +{ return v_int32x8(__lasx_xvftintrz_w_s(__m256(__lasx_xvfrintrp_s(a.val)))); } + +inline v_int32x8 v_ceil(const v_float64x4& a) +{ return v_trunc(v_float64x4(__lasx_xvfrintrp_d(a.val))); } + +/** To float **/ +inline v_float32x8 v_cvt_f32(const v_int32x8& a) +{ return v_float32x8(__lasx_xvffint_s_w(a.val)); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a) +{ return v_float32x8(__lasx_xvpermi_d(__lasx_xvfcvt_s_d(a.val, a.val), 0x88)); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a, const v_float64x4& b) +{ + __m256 abf = __lasx_xvfcvt_s_d(a.val, b.val); //warnning: order of a,b is diff from instruction xvfcvt.s.d + return v_float32x8(__lasx_xvpermi_d(abf, 0x8D)); +} + +inline v_float64x4 v_cvt_f64(const v_int32x8& a) +{ + __m256i alow = __lasx_xvpermi_d(a.val, 0x10); + return v_float64x4(__lasx_xvffintl_d_w(alow)); +} + +inline v_float64x4 v_cvt_f64_high(const v_int32x8& a) +{ + __m256i ahigh = __lasx_xvpermi_d(a.val, 0x32); + return v_float64x4(__lasx_xvffintl_d_w(ahigh)); +} + +inline v_float64x4 v_cvt_f64(const v_float32x8& a) +{ + __m256i alow = __lasx_xvpermi_d(a.val, 0x10); + return v_float64x4(__lasx_xvfcvtl_d_s((__m256)alow)); +} + +inline v_float64x4 v_cvt_f64_high(const v_float32x8& a) +{ + __m256i ahigh = __lasx_xvpermi_d(a.val, 0x32); + return v_float64x4(__lasx_xvfcvtl_d_s((__m256)ahigh)); +} + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x4 v_cvt_f64(const v_int64x4& v) +{ + // constants encoded as floating-point + __m256i magic_i_lo = __lasx_xvreplgr2vr_d(0x4330000000000000); + __m256i magic_i_hi32 = __lasx_xvreplgr2vr_d(0x4530000080000000); + __m256i magic_i_all = __lasx_xvreplgr2vr_d(0x4530000080100000); + __m256d magic_d_all = _lasx_256_castsi256_pd(magic_i_all); + + // Blend the 32 lowest significant bits of v with magic_int_lo + __m256i mask = _v256_set_w(0, -1, 0, -1, 0, -1, 0, -1); + __m256i v_lo = __lasx_xvbitsel_v(magic_i_lo, v.val, mask); + // Extract the 32 most significant bits of v + __m256i v_hi = __lasx_xvsrli_d(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = __lasx_xvxor_v(v_hi, magic_i_hi32); + // Compute in double precision + __m256d v_hi_dbl = __lasx_xvfsub_d(_lasx_256_castsi256_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m256d result = __lasx_xvfadd_d(v_hi_dbl, _lasx_256_castsi256_pd(v_lo)); + return v_float64x4(result); +} + +////////////// Lookup table access //////////////////// + +inline v_int8x32 v256_lut(const schar* tab, const int* idx) +{ + return v_int8x32(_v256_setr_b(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], + tab[idx[ 6]], tab[idx[ 7]], tab[idx[ 8]], tab[idx[ 9]], tab[idx[10]], tab[idx[11]], + tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]], tab[idx[16]], tab[idx[17]], + tab[idx[18]], tab[idx[19]], tab[idx[20]], tab[idx[21]], tab[idx[22]], tab[idx[23]], + tab[idx[24]], tab[idx[25]], tab[idx[26]], tab[idx[27]], tab[idx[28]], tab[idx[29]], + tab[idx[30]], tab[idx[31]])); +} +inline v_int8x32 v256_lut_pairs(const schar* tab, const int* idx) +{ + return v_int8x32(_v256_setr_h(*(const short*)(tab + idx[ 0]), *(const short*)(tab + idx[ 1]), *(const short*)(tab + idx[ 2]), + *(const short*)(tab + idx[ 3]), *(const short*)(tab + idx[ 4]), *(const short*)(tab + idx[ 5]), + *(const short*)(tab + idx[ 6]), *(const short*)(tab + idx[ 7]), *(const short*)(tab + idx[ 8]), + *(const short*)(tab + idx[ 9]), *(const short*)(tab + idx[10]), *(const short*)(tab + idx[11]), + *(const short*)(tab + idx[12]), *(const short*)(tab + idx[13]), *(const short*)(tab + idx[14]), + *(const short*)(tab + idx[15]))); +} +inline v_int8x32 v256_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x32(_v256_setr_w(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]), + *(const int*)(tab + idx[4]), *(const int*)(tab + idx[5]), + *(const int*)(tab + idx[6]), *(const int*)(tab + idx[7]))); +} +inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); } + +inline v_int16x16 v256_lut(const short* tab, const int* idx) +{ + return v_int16x16(_v256_setr_h(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], + tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], tab[idx[ 8]], tab[idx[ 9]], + tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], + tab[idx[15]])); +} +inline v_int16x16 v256_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x16(_v256_setr_w(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]), + *(const int*)(tab + idx[4]), *(const int*)(tab + idx[5]), + *(const int*)(tab + idx[6]), *(const int*)(tab + idx[7]) )); +} +inline v_int16x16 v256_lut_quads(const short* tab, const int* idx) +{ + return v_int16x16(_v256_setr_d(*(const long long int*)(tab + idx[0]), *(const long long int*)(tab + idx[1]), + *(const long long int*)(tab + idx[2]), *(const long long int*)(tab + idx[3]) )); + +} +inline v_uint16x16 v256_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_pairs((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_quads((const short *)tab, idx)); } + +inline v_int32x8 v256_lut(const int* tab, const int* idx) +{ + return v_int32x8(_v256_setr_w(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]), + *(const int*)(tab + idx[4]), *(const int*)(tab + idx[5]), + *(const int*)(tab + idx[6]), *(const int*)(tab + idx[7]) )); +} +inline v_int32x8 v256_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x8(_v256_setr_d(*(const long long int*)(tab + idx[0]), *(const long long int*)(tab + idx[1]), + *(const long long int*)(tab + idx[2]), *(const long long int*)(tab + idx[3]) )); +} +inline v_int32x8 v256_lut_quads(const int* tab, const int* idx) +{ + return v_int32x8(_v256_combine(__lsx_vld(tab + idx[0], 0), __lsx_vld(tab + idx[1], 0))); +} +inline v_uint32x8 v256_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_pairs((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_quads((const int *)tab, idx)); } + +inline v_int64x4 v256_lut(const int64* tab, const int* idx) +{ + return v_int64x4(_v256_setr_d(*(const long long int*)(tab + idx[0]), *(const long long int*)(tab + idx[1]), + *(const long long int*)(tab + idx[2]), *(const long long int*)(tab + idx[3]) )); +} +inline v_int64x4 v256_lut_pairs(const int64* tab, const int* idx) +{ + return v_int64x4(_v256_combine(__lsx_vld(tab + idx[0], 0), __lsx_vld(tab + idx[1], 0))); +} +inline v_uint64x4 v256_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut((const int64 *)tab, idx)); } +inline v_uint64x4 v256_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut_pairs((const int64 *)tab, idx)); } + +inline v_float32x8 v256_lut(const float* tab, const int* idx) +{ + return v_float32x8(_v256_setr_ps(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], + tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]])); +} +inline v_float32x8 v256_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_pairs((const int *)tab, idx)); } +inline v_float32x8 v256_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_quads((const int *)tab, idx)); } + +inline v_float64x4 v256_lut(const double* tab, const int* idx) +{ + return v_float64x4(_v256_setr_pd(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} +inline v_float64x4 v256_lut_pairs(const double* tab, const int* idx) +{ return v_float64x4(_v256_combine(__lsx_vld(tab + idx[0], 0), __lsx_vld(tab + idx[1], 0))); } + +inline v_int32x8 v_lut(const int* tab, const v_int32x8& idxvec) +{ + int *idx = (int*)&idxvec.val; + return v256_lut(tab, idx); +} + +inline v_uint32x8 v_lut(const unsigned* tab, const v_int32x8& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x8 v_lut(const float* tab, const v_int32x8& idxvec) +{ + const int *idx = (const int*)&idxvec.val; + return v256_lut(tab, idx); +} + +inline v_float64x4 v_lut(const double* tab, const v_int32x8& idxvec) +{ + const int *idx = (const int*)&idxvec.val; + return v256_lut(tab, idx); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x8& idxvec, v_float32x8& x, v_float32x8& y) +{ + const int *idx = (const int*)&idxvec.val; + __m128i xy01, xy45, xy23, xy67; + xy01 = __lsx_vld(tab + idx[0], 0); + xy01 = __lsx_vextrins_d(xy01, __lsx_vld(tab + idx[1], 0), 0x10); + xy45 = __lsx_vld(tab + idx[4], 0); + xy45 = __lsx_vextrins_d(xy45, __lsx_vld(tab + idx[5], 0), 0x10); + __m256i xy0145 = _v256_combine(xy01, xy45); + xy23 = __lsx_vld(tab + idx[2], 0); + xy23 = __lsx_vextrins_d(xy23, __lsx_vld(tab + idx[3], 0), 0x10); + xy67 = __lsx_vld(tab + idx[6], 0); + xy67 = __lsx_vextrins_d(xy67, __lsx_vld(tab + idx[7], 0), 0x10); + __m256i xy2367 = _v256_combine(xy23, xy67); + + __m256i xxyy0145 = __lasx_xvilvl_w(xy2367, xy0145); + __m256i xxyy2367 = __lasx_xvilvh_w(xy2367, xy0145); + + x = v_float32x8(__lasx_xvilvl_w(xxyy2367, xxyy0145)); + y = v_float32x8(__lasx_xvilvh_w(xxyy2367, xxyy0145)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x8& idxvec, v_float64x4& x, v_float64x4& y) +{ + //int CV_DECL_ALIGNED(32) idx[4]; + const int *idx = (const int*)&idxvec.val; + __m128i xy0 = __lsx_vld(tab + idx[0], 0); + __m128i xy2 = __lsx_vld(tab + idx[2], 0); + __m128i xy1 = __lsx_vld(tab + idx[1], 0); + __m128i xy3 = __lsx_vld(tab + idx[3], 0); + __m256i xy02 = _v256_combine(xy0, xy2); + __m256i xy13 = _v256_combine(xy1, xy3); + + x = v_float64x4(__lasx_xvilvl_d(xy13, xy02)); + y = v_float64x4(__lasx_xvilvh_d(xy13, xy02)); +} + +inline v_int8x32 v_interleave_pairs(const v_int8x32& vec) +{ + return v_int8x32(__lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0x0f0d0e0c0b090a08, 0x0705060403010200, 0x0f0d0e0c0b090a08, 0x0705060403010200))); +} +inline v_uint8x32 v_interleave_pairs(const v_uint8x32& vec) +{ return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x32 v_interleave_quads(const v_int8x32& vec) +{ + return v_int8x32(__lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0x0f0b0e0a0d090c08, 0x0703060205010400, 0x0f0b0e0a0d090c08, 0x0703060205010400))); +} +inline v_uint8x32 v_interleave_quads(const v_uint8x32& vec) +{ return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_interleave_pairs(const v_int16x16& vec) +{ + return v_int16x16(__lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0x0f0e0b0a0d0c0908, 0x0706030205040100, 0x0f0e0b0a0d0c0908, 0x0706030205040100))); +} +inline v_uint16x16 v_interleave_pairs(const v_uint16x16& vec) +{ return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x16 v_interleave_quads(const v_int16x16& vec) +{ + return v_int16x16(__lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0x0f0e07060d0c0504, 0x0b0a030209080100, 0x0f0e07060d0c0504, 0x0b0a030209080100))); +} +inline v_uint16x16 v_interleave_quads(const v_uint16x16& vec) +{ return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_interleave_pairs(const v_int32x8& vec) +{ + return v_int32x8(__lasx_xvshuf4i_w(vec.val, 0xd8)); +} +inline v_uint32x8 v_interleave_pairs(const v_uint32x8& vec) +{ return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_interleave_pairs(const v_float32x8& vec) +{ return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x32 v_pack_triplets(const v_int8x32& vec) +{ + __m256i vzero = __lasx_xvreplgr2vr_w(0); + __m256i t1 = __lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0xffffff0f0e0d0c0a, 0x0908060504020100, 0xffffff0f0e0d0c0a, 0x0908060504020100)); + __m256i t2 = __lasx_xvshuf_b(vzero, t1, + _v256_set_d(0x1211100c0b0a0908, 0x0706050403020100, 0x1211100c0b0a0908, 0x0706050403020100)); + return v_int8x32(__lasx_xvperm_w(t2, + _v256_set_d(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint8x32 v_pack_triplets(const v_uint8x32& vec) +{ return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_pack_triplets(const v_int16x16& vec) +{ + __m256i vzero = __lasx_xvreplgr2vr_w(0); + __m256i t1 = __lasx_xvshuf_b(vec.val, vec.val, + _v256_set_d(0xffff0f0e0d0c0b0a, 0x0908050403020100, 0xffff0f0e0d0c0b0a, 0x0908050403020100)); + __m256i t2 = __lasx_xvshuf_b(vzero, t1, + _v256_set_d(0x11100d0c0b0a0908, 0x0706050403020100, 0x11100d0c0b0a0908, 0x0706050403020100)); + return v_int16x16(__lasx_xvperm_w(t2, + _v256_set_d(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint16x16 v_pack_triplets(const v_uint16x16& vec) +{ return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_pack_triplets(const v_int32x8& vec) +{ + return v_int32x8(__lasx_xvperm_w(vec.val, + _v256_set_d(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint32x8 v_pack_triplets(const v_uint32x8& vec) +{ return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_pack_triplets(const v_float32x8& vec) +{ + return v_float32x8(__lasx_xvperm_w(*(__m256i*)(&vec.val), + _v256_set_d(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} + +////////// Matrix operations ///////// + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b) +{ return v_int32x8(__lasx_xvadd_w(__lasx_xvmulwev_w_h(a.val, b.val), __lasx_xvmulwod_w_h(a.val, b.val))); } + +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b) +{ + __m256i even = __lasx_xvmulwev_d_w(a.val, b.val); + __m256i odd = __lasx_xvmulwod_d_w(a.val, b.val); + return v_int64x4(__lasx_xvadd_d(even, odd)); +} +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i even_m = __lasx_xvreplgr2vr_w(0xFF00FF00); + __m256i even_a = __lasx_xvbitsel_v(a.val, __lasx_xvreplgr2vr_d(0), even_m); + __m256i odd_a = __lasx_xvsrli_h(a.val, 8); + + __m256i even_b = __lasx_xvbitsel_v(b.val, __lasx_xvreplgr2vr_d(0), even_m); + __m256i odd_b = __lasx_xvsrli_h(b.val, 8); + + __m256i prod0 = __lasx_xvadd_w(__lasx_xvmulwev_w_h(even_a, even_b), __lasx_xvmulwod_w_h(even_a, even_b)); + __m256i prod1 = __lasx_xvadd_w(__lasx_xvmulwev_w_h(odd_a, odd_b),__lasx_xvmulwod_w_h(odd_a, odd_b)); + return v_uint32x8(__lasx_xvadd_w(prod0, prod1)); +} +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b) +{ + __m256i even_a = __lasx_xvsrai_h(__lasx_xvbsll_v(a.val, 1), 8); + __m256i odd_a = __lasx_xvsrai_h(a.val, 8); + + __m256i even_b = __lasx_xvsrai_h(__lasx_xvbsll_v(b.val, 1), 8); + __m256i odd_b = __lasx_xvsrai_h(b.val, 8); + + __m256i prod0 = __lasx_xvadd_w(__lasx_xvmulwev_w_h(even_a, even_b), __lasx_xvmulwod_w_h(even_a, even_b)); + __m256i prod1 = __lasx_xvadd_w(__lasx_xvmulwev_w_h(odd_a, odd_b),__lasx_xvmulwod_w_h(odd_a, odd_b)); + return v_int32x8(__lasx_xvadd_w(prod0, prod1)); +} +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = __lasx_xvmul_h(a.val, b.val); + __m256i mulhi = __lasx_xvmuh_hu(a.val, b.val); + __m256i mul0 = __lasx_xvilvl_h(mulhi, mullo); + __m256i mul1 = __lasx_xvilvh_h(mulhi, mullo); + + __m256i p02 = __lasx_xvbitsel_v(mul0, __lasx_xvreplgr2vr_d(0), _v256_set_w(-1, 0, -1, 0, -1, 0, -1, 0)); + __m256i p13 = __lasx_xvsrli_d(mul0, 32); + __m256i p46 = __lasx_xvbitsel_v(mul1, __lasx_xvreplgr2vr_d(0), _v256_set_w(-1, 0, -1, 0, -1, 0, -1, 0)); + __m256i p57 = __lasx_xvsrli_d(mul1, 32); + + __m256i p15_ = __lasx_xvadd_d(p02, p13); + __m256i p9d_ = __lasx_xvadd_d(p46, p57); + + return v_uint64x4(__lasx_xvadd_d( + __lasx_xvilvl_d(p9d_, p15_), + __lasx_xvilvh_d(p9d_, p15_))); +} +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = __lasx_xvadd_w(__lasx_xvmulwev_w_h(a.val, b.val), __lasx_xvmulwod_w_h(a.val, b.val)); + __m256i sign = __lasx_xvsrai_w(prod, 31); + + __m256i lo = __lasx_xvilvl_w(sign, prod); + __m256i hi = __lasx_xvilvh_w(sign, prod); + + return v_int64x4(__lasx_xvadd_d(__lasx_xvilvl_d(hi, lo), __lasx_xvilvh_d(hi, lo))); +} +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b) +{ return v_dotprod(a, b); } +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod(a, b); } +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = __lasx_xvmul_h(a.val, b.val); + __m256i mulhi = __lasx_xvmuh_hu(a.val, b.val); + __m256i mul0 = __lasx_xvilvl_h(mulhi, mullo); + __m256i mul1 = __lasx_xvilvh_h(mulhi, mullo); + + __m256i p02 = __lasx_xvbitsel_v(mul0, __lasx_xvreplgr2vr_d(0), _v256_set_w(-1, 0, -1, 0, -1, 0, -1, 0)); + __m256i p13 = __lasx_xvsrli_d(mul0, 32); + __m256i p46 = __lasx_xvbitsel_v(mul1, __lasx_xvreplgr2vr_d(0), _v256_set_w(-1, 0, -1, 0, -1, 0, -1, 0)); + __m256i p57 = __lasx_xvsrli_d(mul1, 32); + + __m256i p15_ = __lasx_xvadd_d(p02, p13); + __m256i p9d_ = __lasx_xvadd_d(p46, p57); + + return v_uint64x4(__lasx_xvadd_d(p15_, p9d_)); +} +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = __lasx_xvadd_w(__lasx_xvmulwev_w_h(a.val, b.val), __lasx_xvmulwod_w_h(a.val, b.val)); + __m256i sign = __lasx_xvsrai_w(prod, 31); + __m256i lo = __lasx_xvilvl_w(sign, prod); + __m256i hi = __lasx_xvilvh_w(sign, prod); + return v_int64x4(__lasx_xvadd_d(lo, hi)); +} +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b, c); } + + +#define OPENCV_HAL_LASX_SPLAT2_PS(a, im) \ + v_float32x8(__lasx_xvpermi_w(a.val, a.val, im)) + +inline v_float32x8 v_matmul(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& m3) +{ + v_float32x8 v04 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0x55); + v_float32x8 v26 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0xAA); + v_float32x8 v37 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0xFF); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, v37 * m3))); +} + +inline v_float32x8 v_matmuladd(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& a) +{ + v_float32x8 v04 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0x55); + v_float32x8 v26 = OPENCV_HAL_LASX_SPLAT2_PS(v, 0xAA); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, a))); +} + + +#define OPENCV_HAL_IMPL_LASX_TRANSPOSE4x4(_Tpvec, cast_from, cast_to) \ + inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ + { \ + __m256i t0 = cast_from(__lasx_xvilvl_w(a1.val, a0.val)); \ + __m256i t1 = cast_from(__lasx_xvilvl_w(a3.val, a2.val)); \ + __m256i t2 = cast_from(__lasx_xvilvh_w(a1.val, a0.val)); \ + __m256i t3 = cast_from(__lasx_xvilvh_w(a3.val, a2.val)); \ + b0.val = cast_to(__lasx_xvilvl_d(t1, t0)); \ + b1.val = cast_to(__lasx_xvilvh_d(t1, t0)); \ + b2.val = cast_to(__lasx_xvilvl_d(t3, t2)); \ + b3.val = cast_to(__lasx_xvilvh_d(t3, t2)); \ + } + +OPENCV_HAL_IMPL_LASX_TRANSPOSE4x4(v_uint32x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_LASX_TRANSPOSE4x4(v_int32x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) + +inline void v_transpose4x4(const v_float32x8 &a0, const v_float32x8 &a1, + const v_float32x8 &a2, const v_float32x8 &a3, + v_float32x8 &b0, v_float32x8 &b1, v_float32x8 &b2, v_float32x8 &b3) +{ + __m256i t0 = __lasx_xvilvl_w(__m256i(a1.val), __m256i(a0.val)); + __m256i t1 = __lasx_xvilvl_w(__m256i(a3.val), __m256i(a2.val)); + __m256i t2 = __lasx_xvilvh_w(__m256i(a1.val), __m256i(a0.val)); + __m256i t3 = __lasx_xvilvh_w(__m256i(a3.val), __m256i(a2.val)); + b0.val = __m256(__lasx_xvilvl_d(t1, t0)); + b1.val = __m256(__lasx_xvilvh_d(t1, t0)); + b2.val = __m256(__lasx_xvilvl_d(t3, t2)); + b3.val = __m256(__lasx_xvilvh_d(t3, t2)); +} + +//////////////// Value reordering /////////////// + +/* Expand */ +#define OPENCV_HAL_IMPL_LASX_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(a.val); \ + b1.val = intrin(__lasx_xvpermi_q(a.val, a.val, 0x11)); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(a.val)); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(intrin(__lasx_xvpermi_q(a.val, a.val, 0x11))); } \ + inline _Tpwvec v256_load_expand(const _Tp* ptr) \ + { \ + __m128i a = __lsx_vld(ptr, 0); \ + return _Tpwvec(intrin(*((__m256i*)&a))); \ + } + +OPENCV_HAL_IMPL_LASX_EXPAND(v_uint8x32, v_uint16x16, uchar, __lasx_vext2xv_hu_bu) +OPENCV_HAL_IMPL_LASX_EXPAND(v_int8x32, v_int16x16, schar, __lasx_vext2xv_h_b) +OPENCV_HAL_IMPL_LASX_EXPAND(v_uint16x16, v_uint32x8, ushort, __lasx_vext2xv_wu_hu) +OPENCV_HAL_IMPL_LASX_EXPAND(v_int16x16, v_int32x8, short, __lasx_vext2xv_w_h) +OPENCV_HAL_IMPL_LASX_EXPAND(v_uint32x8, v_uint64x4, unsigned, __lasx_vext2xv_du_wu) +OPENCV_HAL_IMPL_LASX_EXPAND(v_int32x8, v_int64x4, int, __lasx_vext2xv_d_w) + +#define OPENCV_HAL_IMPL_LASX_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v256_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = __lsx_vld(ptr, 0); \ + return _Tpvec(intrin(*((__m256i*)&a))); \ + } + +OPENCV_HAL_IMPL_LASX_EXPAND_Q(v_uint32x8, uchar, __lasx_vext2xv_wu_bu) +OPENCV_HAL_IMPL_LASX_EXPAND_Q(v_int32x8, schar, __lasx_vext2xv_w_b) + +/* pack */ +// 16 +inline v_int8x32 v_pack(const v_int16x16& a, const v_int16x16& b) +{ return v_int8x32(_v256_shuffle_odd_64(_lasx_packs_h(a.val, b.val))); } + +inline v_uint8x32 v_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i t = __lasx_xvreplgr2vr_h(255); + __m256i a1 = __lasx_xvmin_hu(a.val, t); + __m256i b1 = __lasx_xvmin_hu(b.val, t); + return v_uint8x32(_v256_shuffle_odd_64(_lasx_packus_h(a1, b1))); +} + +inline v_uint8x32 v_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + return v_uint8x32(_v256_shuffle_odd_64(_lasx_packus_h(a.val, b.val))); +} + +inline void v_pack_store(schar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(uchar* ptr, const v_uint16x16& a) +{ + const __m256i m = __lasx_xvreplgr2vr_h(255); + __m256i am = __lasx_xvmin_hu(a.val, m); + am = _v256_shuffle_odd_64(_lasx_packus_h(am, am)); + v_store_low(ptr, v_uint8x32(am)); +} + +inline void v_pack_u_store(uchar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + +template inline +v_uint8x32 v_rshr_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + return v_pack_u(v_reinterpret_as_s16((a + delta) >> n), + v_reinterpret_as_s16((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x16& a) +{ + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + v_pack_u_store(ptr, v_reinterpret_as_s16((a + delta) >> n)); +} + +template inline +v_uint8x32 v_rshr_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int8x32 v_rshr_pack(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_store(ptr, (a + delta) >> n); +} + +// 32 +inline v_int16x16 v_pack(const v_int32x8& a, const v_int32x8& b) +{ return v_int16x16(_v256_shuffle_odd_64(_lasx_packs_w(a.val, b.val))); } + +inline v_uint16x16 v_pack(const v_uint32x8& a, const v_uint32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_v256_packs_epu32(a.val, b.val))); } + +inline v_uint16x16 v_pack_u(const v_int32x8& a, const v_int32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_lasx_packus_w(a.val, b.val))); } + +inline void v_pack_store(short* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x8& a) +{ + const __m256i m = __lasx_xvreplgr2vr_w(65535); + __m256i am = __lasx_xvmin_wu(a.val, m); + am = _v256_shuffle_odd_64(_lasx_packus_w(am, am)); + v_store_low(ptr, v_uint16x16(am)); +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + + +template inline +v_uint16x16 v_rshr_pack(const v_uint32x8& a, const v_uint32x8& b) +{ + // we assume that n > 0, and so the shifted 32-bit values can be treated as signed numbers. + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + return v_pack_u(v_reinterpret_as_s32((a + delta) >> n), + v_reinterpret_as_s32((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x8& a) +{ + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + v_pack_u_store(ptr, v_reinterpret_as_s32((a + delta) >> n)); +} + +template inline +v_uint16x16 v_rshr_pack_u(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int16x16 v_rshr_pack(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// 64 +// Non-saturating pack +inline v_uint32x8 v_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + __m256i a0 = __lasx_xvshuf4i_w(a.val, 0x08); + __m256i b0 = __lasx_xvshuf4i_w(b.val, 0x08); + __m256i ab = __lasx_xvilvl_d(b0, a0); + return v_uint32x8(_v256_shuffle_odd_64(ab)); +} + +inline v_int32x8 v_pack(const v_int64x4& a, const v_int64x4& b) +{ return v_reinterpret_as_s32(v_pack(v_reinterpret_as_u64(a), v_reinterpret_as_u64(b))); } + +inline void v_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + __m256i a0 = __lasx_xvshuf4i_w(a.val, 0x08); + v_store_low(ptr, v_uint32x8(_v256_shuffle_odd_64(a0))); +} + +inline void v_pack_store(int* ptr, const v_int64x4& b) +{ v_pack_store((unsigned*)ptr, v_reinterpret_as_u64(b)); } + +template inline +v_uint32x8 v_rshr_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +template inline +v_int32x8 v_rshr_pack(const v_int64x4& a, const v_int64x4& b) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x4& a) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// pack boolean +inline v_uint8x32 v_pack_b(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i ab = _lasx_packs_h(a.val, b.val); + return v_uint8x32(_v256_shuffle_odd_64(ab)); +} + +inline v_uint8x32 v_pack_b(const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d) +{ + __m256i ab = _lasx_packs_w(a.val, b.val); + __m256i cd = _lasx_packs_w(c.val, d.val); + + __m256i abcd = _v256_shuffle_odd_64(_lasx_packs_h(ab, cd)); + return v_uint8x32(__lasx_xvshuf4i_w(abcd, 0xd8)); +} + +inline v_uint8x32 v_pack_b(const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + const v_uint64x4& d, const v_uint64x4& e, const v_uint64x4& f, + const v_uint64x4& g, const v_uint64x4& h) +{ + __m256i ab = _lasx_packs_w(a.val, b.val); + __m256i cd = _lasx_packs_w(c.val, d.val); + __m256i ef = _lasx_packs_w(e.val, f.val); + __m256i gh = _lasx_packs_w(g.val, h.val); + + __m256i abcd = _lasx_packs_w(ab, cd); + __m256i efgh = _lasx_packs_w(ef, gh); + __m256i pkall = _v256_shuffle_odd_64(_lasx_packs_h(abcd, efgh)); + + __m256i rev = _v256_alignr_b(pkall, pkall, 8); + return v_uint8x32(__lasx_xvilvl_h(rev, pkall)); +} + +/* Recombine */ +// its up there with load and store operations + +/* Extract */ +#define OPENCV_HAL_IMPL_LASX_EXTRACT(_Tpvec) \ + template \ + inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ + { return v_rotate_right(a, b); } + +OPENCV_HAL_IMPL_LASX_EXTRACT(v_uint8x32) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_int8x32) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_uint16x16) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_int16x16) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_uint32x8) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_int32x8) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_uint64x4) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_int64x4) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_float32x8) +OPENCV_HAL_IMPL_LASX_EXTRACT(v_float64x4) + +template +inline uchar v_extract_n(v_uint8x32 a) +{ + return (uchar)_v256_extract_b(a.val); +} + +template +inline schar v_extract_n(v_int8x32 a) +{ + return (schar)v_extract_n(v_reinterpret_as_u8(a)); +} + +template +inline ushort v_extract_n(v_uint16x16 a) +{ + return (ushort)_v256_extract_h(a.val); +} + +template +inline short v_extract_n(v_int16x16 a) +{ + return (short)v_extract_n(v_reinterpret_as_u16(a)); +} + +template +inline uint v_extract_n(v_uint32x8 a) +{ + return (uint)_v256_extract_w(a.val); +} + +template +inline int v_extract_n(v_int32x8 a) +{ + return (int)v_extract_n(v_reinterpret_as_u32(a)); +} + +template +inline uint64 v_extract_n(v_uint64x4 a) +{ + return (uint64)_v256_extract_d(a.val); +} + +template +inline int64 v_extract_n(v_int64x4 v) +{ + return (int64)v_extract_n(v_reinterpret_as_u64(v)); +} + +template +inline float v_extract_n(v_float32x8 v) +{ + union { uint iv; float fv; } d; + d.iv = v_extract_n(v_reinterpret_as_u32(v)); + return d.fv; +} + +template +inline double v_extract_n(v_float64x4 v) +{ + union { uint64 iv; double dv; } d; + d.iv = v_extract_n(v_reinterpret_as_u64(v)); + return d.dv; +} + +template +inline v_uint32x8 v_broadcast_element(v_uint32x8 a) +{ + static const __m256i perm = __lasx_xvreplgr2vr_w((char)i); + return v_uint32x8(__lasx_xvperm_w(a.val, perm)); +} + +template +inline v_int32x8 v_broadcast_element(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_broadcast_element(v_reinterpret_as_u32(a))); } + +template +inline v_float32x8 v_broadcast_element(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_broadcast_element(v_reinterpret_as_u32(a))); } + + +///////////////////// load deinterleave ///////////////////////////// + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b ) +{ + __m256i ab0 = __lasx_xvld(ptr, 0); + __m256i ab1 = __lasx_xvld(ptr + 32, 0); + + const __m256i sh = _v256_setr_b(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15, + 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15); + __m256i p0 = __lasx_xvshuf_b(ab0, ab0, sh); + __m256i p1 = __lasx_xvshuf_b(ab1, ab1, sh); + __m256i pl = __lasx_xvpermi_q(p0, p1, 0x02); + __m256i ph = __lasx_xvpermi_q(p0, p1, 0x13); + __m256i a0 = __lasx_xvilvl_d(ph, pl); + __m256i b0 = __lasx_xvilvh_d(ph, pl); + a = v_uint8x32(a0); + b = v_uint8x32(b0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b ) +{ + __m256i ab0 = __lasx_xvld(ptr, 0); + __m256i ab1 = __lasx_xvld(ptr + 16, 0); + + const __m256i sh = _v256_setr_b(0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, + 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15); + __m256i p0 = __lasx_xvshuf_b(ab0, ab0, sh); + __m256i p1 = __lasx_xvshuf_b(ab1, ab1, sh); + __m256i pl = __lasx_xvpermi_q(p0, p1, 0x02); + __m256i ph = __lasx_xvpermi_q(p0, p1, 0x13); + __m256i a0 = __lasx_xvilvl_d(ph, pl); + __m256i b0 = __lasx_xvilvh_d(ph, pl); + a = v_uint16x16(a0); + b = v_uint16x16(b0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b ) +{ + __m256i ab0 = __lasx_xvld(ptr, 0); + __m256i ab1 = __lasx_xvld(ptr + 8, 0); + + //const int sh = 0+2*4+1*16+3*64; + __m256i p0 = __lasx_xvshuf4i_w(ab0, 0xD8); + __m256i p1 = __lasx_xvshuf4i_w(ab1, 0xD8); + __m256i pl = __lasx_xvpermi_q(p0, p1, 0x02); + __m256i ph = __lasx_xvpermi_q(p0, p1, 0x13); + __m256i a0 = __lasx_xvilvl_d(ph, pl); + __m256i b0 = __lasx_xvilvh_d(ph, pl); + a = v_uint32x8(a0); + b = v_uint32x8(b0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b ) +{ + __m256i ab0 = __lasx_xvld(ptr, 0); + __m256i ab1 = __lasx_xvld(ptr + 4, 0); + + __m256i pl = __lasx_xvpermi_q(ab0, ab1, 0x02); + __m256i ph = __lasx_xvpermi_q(ab0, ab1, 0x13); + __m256i a0 = __lasx_xvilvl_d(ph, pl); + __m256i b0 = __lasx_xvilvh_d(ph, pl); + a = v_uint64x4(a0); + b = v_uint64x4(b0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 32, 0); + __m256i bgr2 = __lasx_xvld(ptr + 64, 0); + + __m256i s02_low = __lasx_xvpermi_q(bgr0, bgr2, 0x02); + __m256i s02_high = __lasx_xvpermi_q(bgr0, bgr2, 0x13); + + const __m256i m0 = _v256_setr_b(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _v256_setr_b(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1); + + __m256i b0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_high, s02_low, m1), bgr1, m0); + __m256i r0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(bgr1, s02_low, m0), s02_high, m1); + + const __m256i + sh_b = _v256_setr_b(0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, + 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13), + sh_g = _v256_setr_b(1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, + 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14), + sh_r = _v256_setr_b(2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, + 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15); + b0 = __lasx_xvshuf_b(b0, b0, sh_b); + g0 = __lasx_xvshuf_b(g0, g0, sh_g); + r0 = __lasx_xvshuf_b(r0, r0, sh_r); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 16, 0); + __m256i bgr2 = __lasx_xvld(ptr + 32, 0); + + __m256i s02_low = __lasx_xvpermi_q(bgr0, bgr2, 0x02); + __m256i s02_high = __lasx_xvpermi_q(bgr0, bgr2, 0x13); + + const __m256i m0 = _v256_setr_b(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _v256_setr_b(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + __m256i b0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(bgr1, s02_low, m0), s02_high, m1); + __m256i r0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_high, s02_low, m1), bgr1, m0); + const __m256i sh_b = _v256_setr_b(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _v256_setr_b(2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, + 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13); + const __m256i sh_r = _v256_setr_b(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + b0 = __lasx_xvshuf_b(b0, b0, sh_b); + g0 = __lasx_xvshuf_b(g0, g0, sh_g); + r0 = __lasx_xvshuf_b(r0, r0, sh_r); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 8, 0); + __m256i bgr2 = __lasx_xvld(ptr + 16, 0); + + __m256i s02_low = __lasx_xvpermi_q(bgr0, bgr2, 0x02); + __m256i s02_high = __lasx_xvpermi_q(bgr0, bgr2, 0x13); + + __m256i m24 = _v256_set_w(0, 0, -1, 0, 0, -1, 0, 0); + __m256i m92 = _v256_set_w(-1, 0, 0, -1, 0, 0, -1, 0); + __m256i b0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_low, s02_high, m24), bgr1, m92); + __m256i g0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(s02_high, s02_low, m92), bgr1, m24); + __m256i r0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(bgr1, s02_low, m24), s02_high, m92); + + b0 = __lasx_xvshuf4i_w(b0, 0x6c); + g0 = __lasx_xvshuf4i_w(g0, 0xb1); + r0 = __lasx_xvshuf4i_w(r0, 0xc6); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 4, 0); + __m256i bgr2 = __lasx_xvld(ptr + 8, 0); + + __m256i s01 = __lasx_xvpermi_q(bgr0, bgr1, 0x12); // get bgr0 low 128 and bgr1 high 128 + __m256i s12 = __lasx_xvpermi_q(bgr1, bgr2, 0x12); + __m256i s20r = __lasx_xvpermi_d(__lasx_xvpermi_q(bgr2, bgr0, 0x12), 0x1b); + __m256i b0 = __lasx_xvilvl_d(s20r, s01); + __m256i g0 = _v256_alignr_b(s12, s01, 8); + __m256i r0 = __lasx_xvilvh_d(s12, s20r); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c, v_uint8x32& d ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 32, 0); + __m256i bgr2 = __lasx_xvld(ptr + 64, 0); + __m256i bgr3 = __lasx_xvld(ptr + 96, 0); + const __m256i sh = _v256_setr_b(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, + 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); + + __m256i p0 = __lasx_xvshuf_b(bgr0, bgr0, sh); + __m256i p1 = __lasx_xvshuf_b(bgr1, bgr1, sh); + __m256i p2 = __lasx_xvshuf_b(bgr2, bgr2, sh); + __m256i p3 = __lasx_xvshuf_b(bgr3, bgr3, sh); + + __m256i p01l = __lasx_xvilvl_w(p1, p0); + __m256i p01h = __lasx_xvilvh_w(p1, p0); + __m256i p23l = __lasx_xvilvl_w(p3, p2); + __m256i p23h = __lasx_xvilvh_w(p3, p2); + + __m256i pll = __lasx_xvpermi_q(p01l, p23l, 0x02); + __m256i plh = __lasx_xvpermi_q(p01l, p23l, 0x13); + __m256i phl = __lasx_xvpermi_q(p01h, p23h, 0x02); + __m256i phh = __lasx_xvpermi_q(p01h, p23h, 0x13); + + __m256i b0 = __lasx_xvilvl_w(plh, pll); + __m256i g0 = __lasx_xvilvh_w(plh, pll); + __m256i r0 = __lasx_xvilvl_w(phh, phl); + __m256i a0 = __lasx_xvilvh_w(phh, phl); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); + d = v_uint8x32(a0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c, v_uint16x16& d ) +{ + __m256i bgr0 = __lasx_xvld(ptr, 0); + __m256i bgr1 = __lasx_xvld(ptr + 16, 0); + __m256i bgr2 = __lasx_xvld(ptr + 32, 0); + __m256i bgr3 = __lasx_xvld(ptr + 48, 0); + const __m256i sh = _v256_setr_b(0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15, + 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15); + __m256i p0 = __lasx_xvshuf_b(bgr0, bgr0, sh); + __m256i p1 = __lasx_xvshuf_b(bgr1, bgr1, sh); + __m256i p2 = __lasx_xvshuf_b(bgr2, bgr2, sh); + __m256i p3 = __lasx_xvshuf_b(bgr3, bgr3, sh); + + __m256i p01l = __lasx_xvilvl_w(p1, p0); + __m256i p01h = __lasx_xvilvh_w(p1, p0); + __m256i p23l = __lasx_xvilvl_w(p3, p2); + __m256i p23h = __lasx_xvilvh_w(p3, p2); + + __m256i pll = __lasx_xvpermi_q(p01l, p23l, 0x02); + __m256i plh = __lasx_xvpermi_q(p01l, p23l, 0x13); + __m256i phl = __lasx_xvpermi_q(p01h, p23h, 0x02); + __m256i phh = __lasx_xvpermi_q(p01h, p23h, 0x13); + + __m256i b0 = __lasx_xvilvl_w(plh, pll); + __m256i g0 = __lasx_xvilvh_w(plh, pll); + __m256i r0 = __lasx_xvilvl_w(phh, phl); + __m256i a0 = __lasx_xvilvh_w(phh, phl); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); + d = v_uint16x16(a0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c, v_uint32x8& d ) +{ + __m256i p0 = __lasx_xvld(ptr, 0); + __m256i p1 = __lasx_xvld(ptr + 8, 0); + __m256i p2 = __lasx_xvld(ptr + 16, 0); + __m256i p3 = __lasx_xvld(ptr + 24, 0); + + __m256i p01l = __lasx_xvilvl_w(p1, p0); + __m256i p01h = __lasx_xvilvh_w(p1, p0); + __m256i p23l = __lasx_xvilvl_w(p3, p2); + __m256i p23h = __lasx_xvilvh_w(p3, p2); + + __m256i pll = __lasx_xvpermi_q(p01l, p23l, 0x02); + __m256i plh = __lasx_xvpermi_q(p01l, p23l, 0x13); + __m256i phl = __lasx_xvpermi_q(p01h, p23h, 0x02); + __m256i phh = __lasx_xvpermi_q(p01h, p23h, 0x13); + + __m256i b0 = __lasx_xvilvl_w(plh, pll); + __m256i g0 = __lasx_xvilvh_w(plh, pll); + __m256i r0 = __lasx_xvilvl_w(phh, phl); + __m256i a0 = __lasx_xvilvh_w(phh, phl); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); + d = v_uint32x8(a0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c, v_uint64x4& d ) +{ + __m256i bgra0 = __lasx_xvld(ptr, 0); + __m256i bgra1 = __lasx_xvld(ptr + 4, 0); + __m256i bgra2 = __lasx_xvld(ptr + 8, 0); + __m256i bgra3 = __lasx_xvld(ptr + 12, 0); + + __m256i l02 = __lasx_xvpermi_q(bgra0, bgra2, 0x02); + __m256i h02 = __lasx_xvpermi_q(bgra0, bgra2, 0x13); + __m256i l13 = __lasx_xvpermi_q(bgra1, bgra3, 0x02); + __m256i h13 = __lasx_xvpermi_q(bgra1, bgra3, 0x13); + + __m256i b0 = __lasx_xvilvl_d(l13, l02); + __m256i g0 = __lasx_xvilvh_d(l13, l02); + __m256i r0 = __lasx_xvilvl_d(h13, h02); + __m256i a0 = __lasx_xvilvh_d(h13, h02); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); + d = v_uint64x4(a0); +} + +///////////////////////////// store interleave ///////////////////////////////////// + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& x, const v_uint8x32& y, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = __lasx_xvilvl_b(y.val, x.val); + __m256i xy_h = __lasx_xvilvh_b(y.val, x.val); + + __m256i xy0 = __lasx_xvpermi_q(xy_h, xy_l, 0 + 2*16); + __m256i xy1 = __lasx_xvpermi_q(xy_h, xy_l, 1 + 3*16); + + __lasx_xvst(xy0, (__m256i*)ptr, 0); + __lasx_xvst(xy1, (__m256i*)ptr, 32*1); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& x, const v_uint16x16& y, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = __lasx_xvilvl_h(y.val, x.val); + __m256i xy_h = __lasx_xvilvh_h(y.val, x.val); + + __m256i xy0 = __lasx_xvpermi_q(xy_h, xy_l, 0 + 2*16); + __m256i xy1 = __lasx_xvpermi_q(xy_h, xy_l, 1 + 3*16); + + __lasx_xvst(xy0, (__m256i*)ptr, 0); + __lasx_xvst(xy1, (__m256i*)ptr, 16*2); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& x, const v_uint32x8& y, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = __lasx_xvilvl_w(y.val, x.val); + __m256i xy_h = __lasx_xvilvh_w(y.val, x.val); + + __m256i xy0 = __lasx_xvpermi_q(xy_h, xy_l, 0 + 2*16); + __m256i xy1 = __lasx_xvpermi_q(xy_h, xy_l, 1 + 3*16); + + __lasx_xvst(xy0, (__m256i*)ptr, 0); + __lasx_xvst(xy1, (__m256i*)ptr, 8*4); +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& x, const v_uint64x4& y, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = __lasx_xvilvl_d(y.val, x.val); + __m256i xy_h = __lasx_xvilvh_d(y.val, x.val); + + __m256i xy0 = __lasx_xvpermi_q(xy_h, xy_l, 0 + 2*16); + __m256i xy1 = __lasx_xvpermi_q(xy_h, xy_l, 1 + 3*16); + + __lasx_xvst(xy0, (__m256i*)ptr, 0); + __lasx_xvst(xy1, (__m256i*)ptr, 4*8); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, const v_uint8x32& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _v256_setr_b( + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5, + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5); + const __m256i sh_g = _v256_setr_b( + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10); + const __m256i sh_r = _v256_setr_b( + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15); + + __m256i b0 = __lasx_xvshuf_b(a.val, a.val, sh_b); + __m256i g0 = __lasx_xvshuf_b(b.val, b.val, sh_g); + __m256i r0 = __lasx_xvshuf_b(c.val, c.val, sh_r); + + const __m256i m0 = _v256_setr_b(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _v256_setr_b(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + + __m256i p0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(b0, g0, m0), r0, m1); + __m256i p1 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(g0, r0, m0), b0, m1); + __m256i p2 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(r0, b0, m0), g0, m1); + + __m256i bgr0 = __lasx_xvpermi_q(p1, p0, 0 + 2*16); + __m256i bgr1 = __lasx_xvpermi_q(p0, p2, 0 + 3*16); + __m256i bgr2 = __lasx_xvpermi_q(p2, p1, 1 + 3*16); + + __lasx_xvst(bgr0, (__m256i*)ptr, 0); + __lasx_xvst(bgr1, (__m256i*)ptr, 32); + __lasx_xvst(bgr2, (__m256i*)ptr, 64); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, const v_uint16x16& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _v256_setr_b( + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _v256_setr_b( + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5); + const __m256i sh_r = _v256_setr_b( + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + + __m256i b0 = __lasx_xvshuf_b(a.val, a.val, sh_b); + __m256i g0 = __lasx_xvshuf_b(b.val, b.val, sh_g); + __m256i r0 = __lasx_xvshuf_b(c.val, c.val, sh_r); + + const __m256i m0 = _v256_setr_b(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _v256_setr_b(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + + __m256i p0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(b0, g0, m0), r0, m1); + __m256i p1 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(g0, r0, m0), b0, m1); + __m256i p2 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(r0, b0, m0), g0, m1); + + __m256i bgr0 = __lasx_xvpermi_q(p2, p0, 0 + 2*16); + __m256i bgr2 = __lasx_xvpermi_q(p2, p0, 1 + 3*16); + + __lasx_xvst(bgr0, (__m256i*)ptr, 0); + __lasx_xvst(p1, (__m256i*)ptr, 16*2); + __lasx_xvst(bgr2, (__m256i*)ptr, 32*2); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, const v_uint32x8& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i b0 = __lasx_xvshuf4i_w(a.val, 0x6c); + __m256i g0 = __lasx_xvshuf4i_w(b.val, 0xb1); + __m256i r0 = __lasx_xvshuf4i_w(c.val, 0xc6); + + __m256i bitmask_1 = _v256_set_w(-1, 0, 0, -1, 0, 0, -1, 0); + __m256i bitmask_2 = _v256_set_w(0, 0, -1, 0, 0, -1, 0, 0); + + __m256i p0 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(b0, g0, bitmask_1), r0, bitmask_2); + __m256i p1 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(g0, r0, bitmask_1), b0, bitmask_2); + __m256i p2 = __lasx_xvbitsel_v(__lasx_xvbitsel_v(r0, b0, bitmask_1), g0, bitmask_2); + + __m256i bgr0 = __lasx_xvpermi_q(p1, p0, 0 + 2*16); + __m256i bgr2 = __lasx_xvpermi_q(p1, p0, 1 + 3*16); + + __lasx_xvst(bgr0, (__m256i*)ptr, 0); + __lasx_xvst(p2, (__m256i*)ptr, 8*4); + __lasx_xvst(bgr2, (__m256i*)ptr, 16*4); +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i s01 = __lasx_xvilvl_d(b.val, a.val); + __m256i s12 = __lasx_xvilvh_d(c.val, b.val); + __m256i s20 = __lasx_xvpermi_w(a.val, c.val, 0xe4); + + __m256i bgr0 = __lasx_xvpermi_q(s20, s01, 0 + 2*16); + __m256i bgr1 = __lasx_xvpermi_q(s01, s12, 0x30); + __m256i bgr2 = __lasx_xvpermi_q(s12, s20, 1 + 3*16); + + __lasx_xvst(bgr0, (__m256i*)ptr, 0); + __lasx_xvst(bgr1, (__m256i*)ptr, 4*8); + __lasx_xvst(bgr2, (__m256i*)ptr, 8*8); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, + const v_uint8x32& c, const v_uint8x32& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = __lasx_xvilvl_b(b.val, a.val); + __m256i bg1 = __lasx_xvilvh_b(b.val, a.val); + __m256i ra0 = __lasx_xvilvl_b(d.val, c.val); + __m256i ra1 = __lasx_xvilvh_b(d.val, c.val); + + __m256i bgra0_ = __lasx_xvilvl_h(ra0, bg0); + __m256i bgra1_ = __lasx_xvilvh_h(ra0, bg0); + __m256i bgra2_ = __lasx_xvilvl_h(ra1, bg1); + __m256i bgra3_ = __lasx_xvilvh_h(ra1, bg1); + + __m256i bgra0 = __lasx_xvpermi_q(bgra1_, bgra0_, 0 + 2*16); + __m256i bgra2 = __lasx_xvpermi_q(bgra1_, bgra0_, 1 + 3*16); + __m256i bgra1 = __lasx_xvpermi_q(bgra3_, bgra2_, 0 + 2*16); + __m256i bgra3 = __lasx_xvpermi_q(bgra3_, bgra2_, 1 + 3*16); + + __lasx_xvst(bgra0, (__m256i*)ptr, 0); + __lasx_xvst(bgra1, (__m256i*)ptr, 32); + __lasx_xvst(bgra2, (__m256i*)ptr, 64); + __lasx_xvst(bgra3, (__m256i*)ptr, 96); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, + const v_uint16x16& c, const v_uint16x16& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = __lasx_xvilvl_h(b.val, a.val); + __m256i bg1 = __lasx_xvilvh_h(b.val, a.val); + __m256i ra0 = __lasx_xvilvl_h(d.val, c.val); + __m256i ra1 = __lasx_xvilvh_h(d.val, c.val); + + __m256i bgra0_ = __lasx_xvilvl_w(ra0, bg0); + __m256i bgra1_ = __lasx_xvilvh_w(ra0, bg0); + __m256i bgra2_ = __lasx_xvilvl_w(ra1, bg1); + __m256i bgra3_ = __lasx_xvilvh_w(ra1, bg1); + + __m256i bgra0 = __lasx_xvpermi_q(bgra1_, bgra0_, 0 + 2*16); + __m256i bgra2 = __lasx_xvpermi_q(bgra1_, bgra0_, 1 + 3*16); + __m256i bgra1 = __lasx_xvpermi_q(bgra3_, bgra2_, 0 + 2*16); + __m256i bgra3 = __lasx_xvpermi_q(bgra3_, bgra2_, 1 + 3*16); + + __lasx_xvst(bgra0, (__m256i*)ptr, 0); + __lasx_xvst(bgra1, (__m256i*)ptr, 16*2); + __lasx_xvst(bgra2, (__m256i*)ptr, 32*2); + __lasx_xvst(bgra3, (__m256i*)ptr, 48*2); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = __lasx_xvilvl_w(b.val, a.val); + __m256i bg1 = __lasx_xvilvh_w(b.val, a.val); + __m256i ra0 = __lasx_xvilvl_w(d.val, c.val); + __m256i ra1 = __lasx_xvilvh_w(d.val, c.val); + + __m256i bgra0_ = __lasx_xvilvl_d(ra0, bg0); + __m256i bgra1_ = __lasx_xvilvh_d(ra0, bg0); + __m256i bgra2_ = __lasx_xvilvl_d(ra1, bg1); + __m256i bgra3_ = __lasx_xvilvh_d(ra1, bg1); + + __m256i bgra0 = __lasx_xvpermi_q(bgra1_, bgra0_, 0 + 2*16); + __m256i bgra2 = __lasx_xvpermi_q(bgra1_, bgra0_, 1 + 3*16); + __m256i bgra1 = __lasx_xvpermi_q(bgra3_, bgra2_, 0 + 2*16); + __m256i bgra3 = __lasx_xvpermi_q(bgra3_, bgra2_, 1 + 3*16); + + __lasx_xvst(bgra0, (__m256i*)ptr, 0); + __lasx_xvst(bgra1, (__m256i*)ptr, 8*4); + __lasx_xvst(bgra2, (__m256i*)ptr, 16*4); + __lasx_xvst(bgra3, (__m256i*)ptr, 24*4); +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, + const v_uint64x4& c, const v_uint64x4& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = __lasx_xvilvl_d(b.val, a.val); + __m256i bg1 = __lasx_xvilvh_d(b.val, a.val); + __m256i ra0 = __lasx_xvilvl_d(d.val, c.val); + __m256i ra1 = __lasx_xvilvh_d(d.val, c.val); + + __m256i bgra0 = __lasx_xvpermi_q(ra0, bg0, 0 + 2*16); + __m256i bgra1 = __lasx_xvpermi_q(ra1, bg1, 0 + 2*16); + __m256i bgra2 = __lasx_xvpermi_q(ra0, bg0, 1 + 3*16); + __m256i bgra3 = __lasx_xvpermi_q(ra1, bg1, 1 + 3*16); + + __lasx_xvst(bgra0, (__m256i*)ptr, 0); + __lasx_xvst(bgra1, (__m256i*)(ptr), 4*8); + __lasx_xvst(bgra2, (__m256i*)(ptr), 8*8); + __lasx_xvst(bgra3, (__m256i*)(ptr), 12*8); +} + + +#define OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1/*, mode*/); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, const _Tpvec0& c0, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1/*, mode*/); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1/*, mode*/); \ +} + +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_int8x32, schar, s8, v_uint8x32, uchar, u8) +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_int16x16, short, s16, v_uint16x16, ushort, u16) +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_int32x8, int, s32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_float32x8, float, f32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_int64x4, int64, s64, v_uint64x4, uint64, u64) +OPENCV_HAL_IMPL_LASX_LOADSTORE_INTERLEAVE(v_float64x4, double, f64, v_uint64x4, uint64, u64) + +// +// FP16 +// + +inline v_float32x8 v256_load_expand(const float16_t* ptr) +{ +#if CV_FP16 + //1-load128, 2-permi, 3-cvt + return v_float32x8(__lasx_xvfcvtl_s_h(__lasx_xvpermi_d(__lsx_vld((const __m128i*)ptr, 0), 0x10))); +#else + float CV_DECL_ALIGNED(32) buf[8]; + for (int i = 0; i < 8; i++) + buf[i] = (float)ptr[i]; + return v256_load_aligned(buf); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x8& a) +{ +#if CV_FP16 + __m256i ah = __lasx_xvfcvt_h_s(a.val, a.val); + __lsx_vst((_m128i)ah, ptr, 0); +#else + float CV_DECL_ALIGNED(32) buf[8]; + v_store_aligned(buf, a); + for (int i = 0; i < 8; i++) + ptr[i] = float16_t(buf[i]); +#endif +} + +// +// end of FP16 +// + +inline void v256_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_LASX_HPP diff --git a/modules/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index c118bcd3cb..708627c072 100644 --- a/modules/core/src/parallel_impl.cpp +++ b/modules/core/src/parallel_impl.cpp @@ -59,6 +59,8 @@ DECLARE_CV_PAUSE // https://github.com/riscv/riscv-isa-manual/issues/43 // # define CV_PAUSE(v) do { for (int __delay = (v); __delay > 0; --__delay) { asm volatile("pause"); } } while (0) # define CV_PAUSE(v) do { for (int __delay = (v); __delay > 0; --__delay) { asm volatile("nop"); } } while (0) +# elif defined __GNUC__ && defined __loongarch__ +# define CV_PAUSE(v) do { for (int __delay = (v); __delay > 0; --__delay) { asm volatile("nop"); } } while (0) # else # warning "Can't detect 'pause' (CPU-yield) instruction on the target platform. Specify CV_PAUSE() definition via compiler flags." # define CV_PAUSE(...) do { /* no-op: works, but not effective */ } while (0) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 4e7e1b7ea0..027072a5da 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -434,6 +434,8 @@ struct HWFeatures g_hwFeatureNames[CPU_AVX512_ICL] = "AVX512-ICL"; g_hwFeatureNames[CPU_RVV] = "RVV"; + + g_hwFeatureNames[CPU_LASX] = "LASX"; } void initialize(void) @@ -689,6 +691,10 @@ struct HWFeatures have[CV_CPU_RVV] = true; #endif + #if defined __loongarch_asx + have[CV_CPU_LASX] = true; + #endif + bool skip_baseline_check = false; #ifndef NO_GETENV if (getenv("OPENCV_SKIP_CPU_BASELINE_CHECK")) diff --git a/modules/core/test/test_hal_core.cpp b/modules/core/test/test_hal_core.cpp index 35fb977478..f9078e55f9 100644 --- a/modules/core/test/test_hal_core.cpp +++ b/modules/core/test/test_hal_core.cpp @@ -136,7 +136,11 @@ TEST_P(HAL, mat_decomp) int size = (hcase / 2) % 4; size = size == 0 ? 3 : size == 1 ? 4 : size == 2 ? 6 : 15; int nfunc = (hcase / 8); + #if CV_LASX + double eps = depth == CV_32F ? 1e-5 : 2e-10; + #else double eps = depth == CV_32F ? 1e-5 : 1e-10; + #endif if( size == 3 ) return; // TODO ??? diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index e0773d5214..08cda81819 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -8,8 +8,8 @@ endif() set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass") -ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV) -ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX) +ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX) +ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX LASX) ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js) diff --git a/modules/dnn/src/int8layers/convolution_layer.cpp b/modules/dnn/src/int8layers/convolution_layer.cpp index dfa58b09fe..320a18e5ab 100644 --- a/modules/dnn/src/int8layers/convolution_layer.cpp +++ b/modules/dnn/src/int8layers/convolution_layer.cpp @@ -579,13 +579,14 @@ public: bool is1x1_; bool useAVX2; bool useAVX512; + bool useLASX; int blk_size_cn; int inpZp, outZp; const std::vector* multiplier; ParallelConv() : input_(0), weights_(0), output_(0), ngroups_(0), nstripes_(0), - biasvec_(0), activLUT_(0), activ_(0), is1x1_(false), useAVX2(false), useAVX512(false) + biasvec_(0), activLUT_(0), activ_(0), is1x1_(false), useAVX2(false), useAVX512(false), useLASX(false) , blk_size_cn(0), inpZp(0), outZp(0), multiplier(0) {} @@ -641,6 +642,8 @@ public: p.useAVX2 = checkHardwareSupport(CPU_AVX2) && isConv2D; p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX && isConv2D; + p.useLASX = checkHardwareSupport(CPU_LASX) && isConv2D; + int kernel_d = isConv3D? kernel_size[0] : 1; int kernel_h = isConv1D? 1 : kernel_size[kernel_size.size() - 2]; int kernel_w = kernel_size.back(); @@ -837,6 +840,13 @@ public: stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, biasptr, multptr, inptr_, height, width, outptr_, out_d, outH, outW, inpZp, outZp); else + #endif + #if CV_TRY_LASX + if(useLASX) + opt_LASX::fastDepthwiseConv(wptr, kernel_h, kernel_w, + stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, + biasptr, multptr, inptr_, height, width, outptr_, out_d, outH, outW, inpZp, outZp); + else #endif { const int8_t w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], @@ -1210,6 +1220,12 @@ public: opt_AVX2::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, outShape, bsz, vsz, vsz_a, outZp, multptr, cn0 == 0, cn1 == inpCn); else + #endif + #if CV_TRY_LASX + if(useLASX) + opt_LASX::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, + outShape, bsz, vsz, vsz_a, outZp, multptr, cn0 == 0, cn1 == inpCn); + else #endif for( int i = 0; i < outCn; i += 2 ) { diff --git a/modules/dnn/src/int8layers/fully_connected_layer.cpp b/modules/dnn/src/int8layers/fully_connected_layer.cpp index dc759ebdbc..867f002dd4 100644 --- a/modules/dnn/src/int8layers/fully_connected_layer.cpp +++ b/modules/dnn/src/int8layers/fully_connected_layer.cpp @@ -226,7 +226,7 @@ public: { public: FullyConnected() : srcMat(0), weights(0), biasMat(0), outputMultiplier(0), activationLUT(0), activ(0), - dstMat(0), nstripes(0), outZp(0), useAVX2(false), useAVX512(false) {} + dstMat(0), nstripes(0), outZp(0), useAVX2(false), useAVX512(false), useLASX(false) {} static void run(const Mat& srcMat, const Mat& weights, const Mat& biasMat, const Mat& outputMultiplier, const Mat& activationLUT, Mat& dstMat, const ActivationLayerInt8* activ, int nstripes, int outZp) @@ -250,6 +250,7 @@ public: p.activ = !activationLUT.empty() ? activ : 0; p.useAVX2 = checkHardwareSupport(CPU_AVX2); p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX; + p.useLASX = checkHardwareSupport(CPU_LASX); parallel_for_(Range(0, nstripes), p, nstripes); } @@ -294,6 +295,11 @@ public: if( useAVX2 ) opt_AVX2::fastGEMM1T( sptr, wptr, wstep, biasptr, multptr, dptr, nw, vecsize, outZp ); else + #endif + #if CV_TRY_LASX + if( useLASX ) + opt_LASX::fastGEMM1T( sptr, wptr, wstep, biasptr, multptr, dptr, nw, vecsize, outZp ); + else #endif { int i = 0; @@ -349,6 +355,7 @@ public: int nstripes, outZp; bool useAVX2; bool useAVX512; + bool useLASX; }; void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE diff --git a/modules/dnn/src/int8layers/layers_common.simd.hpp b/modules/dnn/src/int8layers/layers_common.simd.hpp index bf6149e5c9..1b3ac7a4b8 100644 --- a/modules/dnn/src/int8layers/layers_common.simd.hpp +++ b/modules/dnn/src/int8layers/layers_common.simd.hpp @@ -633,5 +633,629 @@ void fastGEMM1T( const int8_t* vec, const int8_t* weights, } #endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_LASX + +inline __m256i _v256_fmadds8_s32(const __m256i& a, const __m256i& b, const __m256i& c) +{ + __m256i vzero = __lasx_xvreplgr2vr_d(0); + __m256i even_ab = __lasx_xvmaddwev_h_b(vzero, a, b); + __m256i madd_ab = __lasx_xvmaddwod_h_b(even_ab, a, b); + + __m256i even_madd_ab = __lasx_xvsrai_w(__lasx_xvslli_w(madd_ab, 16), 16); + __m256i odd_madd_ab = __lasx_xvsrai_w(madd_ab, 16); + + return __lasx_xvadd_w(__lasx_xvadd_w(even_madd_ab, odd_madd_ab), c); +} + +enum { FASCONV_BASE_VECSZ = 4 }; + +void fastConv( const int8_t* weights, size_t wstep, const int* bias, + const int8_t* rowbuf, int* output, const int* outShape, + int blockSize, int vecsize, int vecsize_aligned, int outZp, + const float* multiplier, bool initOutput, bool finalOutput ) +{ + int outCn = outShape[1]; + size_t outPlaneSize = outShape[2]*outShape[3]; + int CV_DECL_ALIGNED(16) maskbuf[FASCONV_BASE_VECSZ] = {0}; + int rsz = blockSize % FASCONV_BASE_VECSZ; + for( int i = 0; i < rsz; i++ ) + maskbuf[FASCONV_BASE_VECSZ - i - 1] = -1; + __m128i mask = __lsx_vld((const float*)maskbuf, 0); + + // now compute dot product of the weights + // and im2row-transformed part of the tensor + for( int i = 0; i < outCn; i += 3 ) + { + const int8_t* wptr0 = weights + i*wstep; + const int8_t* wptr1 = wptr0 + wstep; + const int8_t* wptr2 = wptr1 + wstep; + int* outptr0 = output + i*outPlaneSize; + int* outptr1 = outptr0 + outPlaneSize; + int* outptr2 = outptr1 + outPlaneSize; + int bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2]; + float mult0 = multiplier[i], mult1 = multiplier[i+1], mult2 = multiplier[i+2]; + + if( i+2 >= outCn ) + { + wptr2 = wptr1; + outptr2 = outptr1; + bias2 = bias1; + mult2 = mult1; + + if( i+1 >= outCn ) + { + wptr2 = wptr1 = wptr0; + outptr2 = outptr1 = outptr0; + bias2 = bias1 = bias0; + mult2 = mult1 = mult0; + } + } + int j = 0; + for( ; j < blockSize; j += FASCONV_BASE_VECSZ ) + { + bool tail = false; + if (j + FASCONV_BASE_VECSZ > blockSize) + { + if (j == 0) + break; + j = blockSize - FASCONV_BASE_VECSZ; + tail = true; + } + int k = 0; + const int8_t* rptr = rowbuf + j*vecsize_aligned; + + __m256i vs00 = __lasx_xvreplgr2vr_d(0), vs01 = __lasx_xvreplgr2vr_d(0), + vs02 = __lasx_xvreplgr2vr_d(0), vs03 = __lasx_xvreplgr2vr_d(0), + vs10 = __lasx_xvreplgr2vr_d(0), vs11 = __lasx_xvreplgr2vr_d(0), + vs12 = __lasx_xvreplgr2vr_d(0), vs13 = __lasx_xvreplgr2vr_d(0), + vs20 = __lasx_xvreplgr2vr_d(0), vs21 = __lasx_xvreplgr2vr_d(0), + vs22 = __lasx_xvreplgr2vr_d(0), vs23 = __lasx_xvreplgr2vr_d(0); + + for (; k < vecsize; k += 32, rptr += 32 ) + { + __m256i w0 = __lasx_xvld((const __m256i*)(wptr0 + k), 0); + __m256i w1 = __lasx_xvld((const __m256i*)(wptr1 + k), 0); + __m256i w2 = __lasx_xvld((const __m256i*)(wptr2 + k), 0); + __m256i r0 = __lasx_xvld((const __m256i*)(rptr), 0); + + vs00 = _v256_fmadds8_s32(w0, r0, vs00); + vs10 = _v256_fmadds8_s32(w1, r0, vs10); + vs20 = _v256_fmadds8_s32(w2, r0, vs20); + + r0 = __lasx_xvld((const __m256i*)(rptr + vecsize_aligned), 0); + vs01 = _v256_fmadds8_s32(w0, r0, vs01); + vs11 = _v256_fmadds8_s32(w1, r0, vs11); + vs21 = _v256_fmadds8_s32(w2, r0, vs21); + + r0 = __lasx_xvld((const __m256i*)(rptr + vecsize_aligned*2), 0); + vs02 = _v256_fmadds8_s32(w0, r0, vs02); + vs12 = _v256_fmadds8_s32(w1, r0, vs12); + vs22 = _v256_fmadds8_s32(w2, r0, vs22); + + r0 = __lasx_xvld((const __m256i*)(rptr + vecsize_aligned*3), 0); + vs03 = _v256_fmadds8_s32(w0, r0, vs03); + vs13 = _v256_fmadds8_s32(w1, r0, vs13); + vs23 = _v256_fmadds8_s32(w2, r0, vs23); + } + + /*t0*/ + __m256i vs00_hadd_w = __lasx_xvhaddw_d_w(vs00, vs00); + __m256i vs00_hadd_d = __lasx_xvhaddw_q_d(vs00_hadd_w, vs00_hadd_w); + + __m256i vs01_hadd_w = __lasx_xvhaddw_d_w(vs01, vs01); + __m256i vs01_hadd_d = __lasx_xvhaddw_q_d(vs01_hadd_w, vs01_hadd_w); + + __m256i vs02_hadd_w = __lasx_xvhaddw_d_w(vs02, vs02); + __m256i vs02_hadd_d = __lasx_xvhaddw_q_d(vs02_hadd_w, vs02_hadd_w); + + __m256i vs03_hadd_w = __lasx_xvhaddw_d_w(vs03, vs03); + __m256i vs03_hadd_d = __lasx_xvhaddw_q_d(vs03_hadd_w, vs03_hadd_w); + + __m256i vs01_vs00 = __lasx_xvpackev_w(vs01_hadd_d, vs00_hadd_d); + __m256i vs03_vs02 = __lasx_xvpackev_w(vs03_hadd_d, vs02_hadd_d); + __m256i t0 = __lasx_xvpackev_d(vs03_vs02, vs01_vs00); + + /*t1*/ + __m256i vs10_hadd_w = __lasx_xvhaddw_d_w(vs10, vs10); + __m256i vs10_hadd_d = __lasx_xvhaddw_q_d(vs10_hadd_w, vs10_hadd_w); + + __m256i vs11_hadd_w = __lasx_xvhaddw_d_w(vs11, vs11); + __m256i vs11_hadd_d = __lasx_xvhaddw_q_d(vs11_hadd_w, vs11_hadd_w); + + __m256i vs12_hadd_w = __lasx_xvhaddw_d_w(vs12, vs12); + __m256i vs12_hadd_d = __lasx_xvhaddw_q_d(vs12_hadd_w, vs12_hadd_w); + + __m256i vs13_hadd_w = __lasx_xvhaddw_d_w(vs13, vs13); + __m256i vs13_hadd_d = __lasx_xvhaddw_q_d(vs13_hadd_w, vs13_hadd_w); + + __m256i vs11_vs10 = __lasx_xvpackev_w(vs11_hadd_d, vs10_hadd_d); + __m256i vs13_vs12 = __lasx_xvpackev_w(vs13_hadd_d, vs12_hadd_d); + __m256i t1 = __lasx_xvpackev_d(vs13_vs12, vs11_vs10); + + /*t2*/ + __m256i vs20_hadd_w = __lasx_xvhaddw_d_w(vs20, vs20); + __m256i vs20_hadd_d = __lasx_xvhaddw_q_d(vs20_hadd_w, vs20_hadd_w); + + __m256i vs21_hadd_w = __lasx_xvhaddw_d_w(vs21, vs21); + __m256i vs21_hadd_d = __lasx_xvhaddw_q_d(vs21_hadd_w, vs21_hadd_w); + + __m256i vs22_hadd_w = __lasx_xvhaddw_d_w(vs22, vs22); + __m256i vs22_hadd_d = __lasx_xvhaddw_q_d(vs22_hadd_w, vs22_hadd_w); + + __m256i vs23_hadd_w = __lasx_xvhaddw_d_w(vs23, vs23); + __m256i vs23_hadd_d = __lasx_xvhaddw_q_d(vs23_hadd_w, vs23_hadd_w); + + __m256i vs21_vs20 = __lasx_xvpackev_w(vs21_hadd_d, vs20_hadd_d); + __m256i vs23_vs22 = __lasx_xvpackev_w(vs23_hadd_d, vs22_hadd_d); + __m256i t2 = __lasx_xvpackev_d(vs23_vs22, vs21_vs20); + + t0 = __lasx_xvadd_w(t0, __lasx_xvpermi_q(t0, t0, 1)); + t1 = __lasx_xvadd_w(t1, __lasx_xvpermi_q(t1, t1, 1)); + t2 = __lasx_xvadd_w(t2, __lasx_xvpermi_q(t2, t2, 1)); + + __m128i s0, s1, s2; + + if( initOutput ) + { + s0 = __lsx_vreplgr2vr_w(bias0); + s1 = __lsx_vreplgr2vr_w(bias1); + s2 = __lsx_vreplgr2vr_w(bias2); + } + else + { + s0 = __lsx_vld((__m128i*)(outptr0 + j), 0); + s1 = __lsx_vld((__m128i*)(outptr1 + j), 0); + s2 = __lsx_vld((__m128i*)(outptr2 + j), 0); + } + + s0 = __lsx_vadd_w(s0, *(__m128i*)&t0); + s1 = __lsx_vadd_w(s1, *(__m128i*)&t1); + s2 = __lsx_vadd_w(s2, *(__m128i*)&t2); + + if( finalOutput ) + { + __m128i voutzp = __lsx_vreplgr2vr_w(outZp); + __m128i outmin = __lsx_vreplgr2vr_w(-128), outmax = __lsx_vreplgr2vr_w(127); + __m256 v_mult0 = _v256_setall_ps(mult0); + __m256 v_mult1 = _v256_setall_ps(mult1); + __m256 v_mult2 = _v256_setall_ps(mult2); + + s0 = __lsx_vadd_w(voutzp, __lsx_vftint_w_s(__lsx_vfmul_s(__lsx_vffint_s_w(s0), *(__m128*)&v_mult0))); + s1 = __lsx_vadd_w(voutzp, __lsx_vftint_w_s(__lsx_vfmul_s(__lsx_vffint_s_w(s1), *(__m128*)&v_mult1))); + s2 = __lsx_vadd_w(voutzp, __lsx_vftint_w_s(__lsx_vfmul_s(__lsx_vffint_s_w(s2), *(__m128*)&v_mult2))); + + s0 = __lsx_vmin_w(__lsx_vmax_w(s0, outmin), outmax); + s1 = __lsx_vmin_w(__lsx_vmax_w(s1, outmin), outmax); + s2 = __lsx_vmin_w(__lsx_vmax_w(s2, outmin), outmax); + } + if( tail ) + { + s0 = __lsx_vbitsel_v(__lsx_vld((const float*)outptr0 + j, 0), s0, mask); + s1 = __lsx_vbitsel_v(__lsx_vld((const float*)outptr1 + j, 0), s1, mask); + s2 = __lsx_vbitsel_v(__lsx_vld((const float*)outptr2 + j, 0), s2, mask); + } + __lsx_vst(s0, (__m128i*)(outptr0 + j), 0); + __lsx_vst(s1, (__m128i*)(outptr1 + j), 0); + __lsx_vst(s2, (__m128i*)(outptr2 + j), 0); + } + + for( ; j <= blockSize - 2; j += 2 ) + { + const int8_t* rptr0 = rowbuf + j*vecsize_aligned; + const int8_t* rptr1 = rowbuf + (j+1)*vecsize_aligned; + int s00, s01, s10, s11, s20, s21; + + if( initOutput ) + { + s00 = s01 = bias0; + s10 = s11 = bias1; + s20 = s21 = bias2; + } + else + { + s00 = outptr0[j]; s01 = outptr0[j+1]; + s10 = outptr1[j]; s11 = outptr1[j+1]; + s20 = outptr2[j]; s21 = outptr2[j+1]; + } + + for( int k = 0; k < vecsize; k++ ) + { + int8_t w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; + int8_t r = rptr0[k]; + s00 += (int)w0*r; s10 += (int)w1*r; s20 += (int)w2*r; + r = rptr1[k]; + s01 += (int)w0*r; s11 += (int)w1*r; s21 += (int)w2*r; + } + + if( finalOutput ) + { + s00 = std::min(std::max(outZp + (int)std::round(s00*mult0), -128), 127); + s01 = std::min(std::max(outZp + (int)std::round(s01*mult0), -128), 127); + s10 = std::min(std::max(outZp + (int)std::round(s10*mult1), -128), 127); + s11 = std::min(std::max(outZp + (int)std::round(s11*mult1), -128), 127); + s20 = std::min(std::max(outZp + (int)std::round(s20*mult2), -128), 127); + s21 = std::min(std::max(outZp + (int)std::round(s21*mult2), -128), 127); + } + outptr0[j] = s00; + outptr0[j+1] = s01; + outptr1[j] = s10; + outptr1[j+1] = s11; + outptr2[j] = s20; + outptr2[j+1] = s21; + } + + for( ; j < blockSize; j++ ) + { + const int8_t* rptr0 = rowbuf + j*vecsize_aligned; + int s00, s10, s20; + + if( initOutput ) + { + s00 = bias0; + s10 = bias1; + s20 = bias2; + } + else + { + s00 = outptr0[j]; + s10 = outptr1[j]; + s20 = outptr2[j]; + } + + for( int k = 0; k < vecsize; k++ ) + { + int8_t w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; + int8_t r = rptr0[k]; + s00 += (int)w0*r; s10 += (int)w1*r; s20 += (int)w2*r; + } + + if( finalOutput ) + { + s00 = std::min(std::max(outZp + (int)std::round(s00*mult0), -128), 127); + s10 = std::min(std::max(outZp + (int)std::round(s10*mult1), -128), 127); + s20 = std::min(std::max(outZp + (int)std::round(s20*mult2), -128), 127); + } + outptr0[j] = s00; + outptr1[j] = s10; + outptr2[j] = s20; + } + } +} + +static inline void _v256_expand_mul_add(const __m256i& a, const __m256i& b, + __m256i& out0, __m256i& out1, __m256i& out2, __m256i& out3) +{ + __m256i a0 = __lasx_xvsllwil_h_b(__lasx_xvpermi_d(a, 0x10), 0); + __m256i a1 = __lasx_xvsllwil_h_b(__lasx_xvpermi_d(a, 0x32), 0); + + __m256i b0 = __lasx_xvsllwil_h_b(__lasx_xvpermi_d(b, 0x10), 0); + __m256i b1 = __lasx_xvsllwil_h_b(__lasx_xvpermi_d(b, 0x32), 0); + + __m256i a0b0 = __lasx_xvmul_h(a0, b0); + __m256i a1b1 = __lasx_xvmul_h(a1, b1); + + out0 = __lasx_xvadd_w(out0, __lasx_xvsllwil_w_h(__lasx_xvpermi_d(a0b0, 0x10), 0)); + out1 = __lasx_xvadd_w(out1, __lasx_xvsllwil_w_h(__lasx_xvpermi_d(a0b0, 0x32), 0)); + out2 = __lasx_xvadd_w(out2, __lasx_xvsllwil_w_h(__lasx_xvpermi_d(a1b1, 0x10), 0)); + out3 = __lasx_xvadd_w(out3, __lasx_xvsllwil_w_h(__lasx_xvpermi_d(a1b1, 0x32), 0)); +} + +static inline void _v256_load_deinterleave(const int8_t* ptr, __m256i& a, __m256i& b) +{ + __m256i t0 = __lasx_xvld((const __m256i*)ptr, 0); + __m256i t1 = __lasx_xvld((const __m256i*)ptr, 32*1); + + const __m256i sh = _v256_setr_b(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15, + 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15); + __m256i p0 = __lasx_xvshuf_b(t0, t0, sh); + __m256i p1 = __lasx_xvshuf_b(t1, t1, sh); + __m256i lo = __lasx_xvpermi_q(p0, p1, 0x02); + __m256i hi = __lasx_xvpermi_q(p0, p1, 0x13); + + a = __lasx_xvilvl_d(hi, lo); + b = __lasx_xvilvh_d(hi, lo); +} + +void fastDepthwiseConv( const int8_t* wptr, + int kernel_h, int kernel_w, + int stride_h, int stride_w, + int dilation_h, int dilation_w, + int pad_t, int pad_l, + const int* biasptr, const float* multptr, + const int8_t* inptr_, + int height, int width, + int* outptr_, + int out_d, int outH, int outW, + int inpZp, int outZp) +{ + const int8_t w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], + w10 = wptr[3], w11 = wptr[4], w12 = wptr[5], + w20_ = wptr[6], w21_ = wptr[7], w22_ = wptr[8]; + int outW1 = min(outW, (width - dilation_w*(kernel_w - 1) + pad_l)/stride_w); + float mult = multptr[out_d]; + int bias = biasptr[out_d]; + int biasCopy; + + for (int out_i = 0; out_i < outH; out_i++) + { + int in_i = out_i * stride_h - pad_t, out_j = 0; + const int8_t* imgptr0 = inptr_ + in_i*width; + const int8_t* imgptr1 = imgptr0 + dilation_h*width; + const int8_t* imgptr2 = imgptr0 + (dilation_h*2)*width; + int8_t w00 = w00_, w01 = w01_, w02 = w02_; + int8_t w20 = w20_, w21 = w21_, w22 = w22_; + int out; + biasCopy = bias; + if (in_i < 0) + { + biasCopy += inpZp * (w00 + w01 + w02); + w00 = w01 = w02 = 0; + imgptr0 = imgptr1; + } + else if (in_i + dilation_h*(kernel_h-1) >= height) + { + biasCopy += inpZp * (w20 + w21 + w22); + w20 = w21 = w22 = 0; + imgptr2 = imgptr1; + } + int* outptr = outptr_ + out_i*outW; + if (pad_l > 0) + { + out = (int)imgptr0[0]*w01 + (int)imgptr0[dilation_w]*w02 + + (int)imgptr1[0]*w11 + (int)imgptr1[dilation_w]*w12 + + (int)imgptr2[0]*w21 + (int)imgptr2[dilation_w]*w22 + + biasCopy + inpZp*(w00 + w10 + w20); + outptr[0] = std::min(std::max(outZp + (int)std::round(out*mult), -128), 127); + out_j = 1; + } + + if (stride_w == 1 || (stride_w == 2 && dilation_w == 1)) + { + const int VECSZ = 32; + __m256i vw00 = __lasx_xvreplgr2vr_b(w00), vw01 = __lasx_xvreplgr2vr_b(w01), vw02 = __lasx_xvreplgr2vr_b(w02), + vw10 = __lasx_xvreplgr2vr_b(w10), vw11 = __lasx_xvreplgr2vr_b(w11), vw12 = __lasx_xvreplgr2vr_b(w12), + vw20 = __lasx_xvreplgr2vr_b(w20), vw21 = __lasx_xvreplgr2vr_b(w21), vw22 = __lasx_xvreplgr2vr_b(w22); + __m256i vbias = __lasx_xvreplgr2vr_w(biasCopy), voutzp = __lasx_xvreplgr2vr_w(outZp), + outmin = __lasx_xvreplgr2vr_w(-128), outmax = __lasx_xvreplgr2vr_w(127); + __m256 vmult = _v256_setall_ps(mult); + __m256i vout0, vout1, vout2, vout3; + + if( stride_w == 1 ) + { + for( ; out_j < outW1; out_j += VECSZ ) + { + if (out_j + VECSZ > outW1) + { + if (out_j <= pad_l) + break; + out_j = outW1 - VECSZ; + } + int in_j = out_j * stride_w - pad_l; + __m256i v00 = __lasx_xvld((const __m256i*)(imgptr0 + in_j), 0), + v01 = __lasx_xvld((const __m256i*)(imgptr0 + in_j + dilation_w), 0), + v02 = __lasx_xvld((const __m256i*)(imgptr0 + in_j + dilation_w*2), 0), + v10 = __lasx_xvld((const __m256i*)(imgptr1 + in_j), 0), + v11 = __lasx_xvld((const __m256i*)(imgptr1 + in_j + dilation_w), 0), + v12 = __lasx_xvld((const __m256i*)(imgptr1 + in_j + dilation_w*2), 0), + v20 = __lasx_xvld((const __m256i*)(imgptr2 + in_j), 0), + v21 = __lasx_xvld((const __m256i*)(imgptr2 + in_j + dilation_w), 0), + v22 = __lasx_xvld((const __m256i*)(imgptr2 + in_j + dilation_w*2), 0); + + vout0 = vout1 = vout2 = vout3 = vbias; + _v256_expand_mul_add(v00, vw00, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v01, vw01, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v02, vw02, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v10, vw10, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v11, vw11, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v12, vw12, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v20, vw20, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v21, vw21, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v22, vw22, vout0, vout1, vout2, vout3); + + vout0 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout0), vmult))); + vout1 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout1), vmult))); + vout2 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout2), vmult))); + vout3 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout3), vmult))); + + vout0 = __lasx_xvmin_w(__lasx_xvmax_w(vout0, outmin), outmax); + vout1 = __lasx_xvmin_w(__lasx_xvmax_w(vout1, outmin), outmax); + vout2 = __lasx_xvmin_w(__lasx_xvmax_w(vout2, outmin), outmax); + vout3 = __lasx_xvmin_w(__lasx_xvmax_w(vout3, outmin), outmax); + + __lasx_xvst(vout0, (__m256i*)(outptr + out_j), 0); + __lasx_xvst(vout1, (__m256i*)(outptr + out_j), 8*4); + __lasx_xvst(vout2, (__m256i*)(outptr + out_j), 16*4); + __lasx_xvst(vout3, (__m256i*)(outptr + out_j), 24*4); + } + } + else + { + for( ; out_j < outW1; out_j += VECSZ ) + { + if (out_j + VECSZ > outW1) + { + if (out_j <= pad_l) + break; + out_j = outW1 - VECSZ; + } + int in_j = out_j * stride_w - pad_l; + __m256i v00, v01, v02, v10, v11, v12, v20, v21, v22, unused; + _v256_load_deinterleave(imgptr0 + in_j, v00, v01); + _v256_load_deinterleave(imgptr0 + in_j + 2, v02, unused); + _v256_load_deinterleave(imgptr1 + in_j, v10, v11); + _v256_load_deinterleave(imgptr1 + in_j + 2, v12, unused); + _v256_load_deinterleave(imgptr2 + in_j, v20, v21); + _v256_load_deinterleave(imgptr2 + in_j + 2, v22, unused); + + vout0 = vout1 = vout2 = vout3 = vbias; + _v256_expand_mul_add(v00, vw00, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v01, vw01, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v02, vw02, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v10, vw10, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v11, vw11, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v12, vw12, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v20, vw20, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v21, vw21, vout0, vout1, vout2, vout3); + _v256_expand_mul_add(v22, vw22, vout0, vout1, vout2, vout3); + + vout0 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout0), vmult))); + vout1 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout1), vmult))); + vout2 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout2), vmult))); + vout3 = __lasx_xvadd_w(voutzp, __lasx_xvftint_w_s(__lasx_xvfmul_s(__lasx_xvffint_s_w(vout3), vmult))); + + vout0 = __lasx_xvmin_w(__lasx_xvmax_w(vout0, outmin), outmax); + vout1 = __lasx_xvmin_w(__lasx_xvmax_w(vout1, outmin), outmax); + vout2 = __lasx_xvmin_w(__lasx_xvmax_w(vout2, outmin), outmax); + vout3 = __lasx_xvmin_w(__lasx_xvmax_w(vout3, outmin), outmax); + + __lasx_xvst(vout0, (__m256i*)(outptr + out_j), 0); + __lasx_xvst(vout1, (__m256i*)(outptr + out_j), 8*4); + __lasx_xvst(vout2, (__m256i*)(outptr + out_j), 16*4); + __lasx_xvst(vout3, (__m256i*)(outptr + out_j), 24*4); + } + } + } + + for (; out_j < outW1; out_j++) + { + int in_j = out_j * stride_w - pad_l; + out = (int)imgptr0[in_j]*w00 + (int)imgptr0[in_j + dilation_w]*w01 + (int)imgptr0[in_j + dilation_w*2]*w02 + + (int)imgptr1[in_j]*w10 + (int)imgptr1[in_j + dilation_w]*w11 + (int)imgptr1[in_j + dilation_w*2]*w12 + + (int)imgptr2[in_j]*w20 + (int)imgptr2[in_j + dilation_w]*w21 + (int)imgptr2[in_j + dilation_w*2]*w22 + biasCopy; + outptr[out_j] = std::min(std::max(outZp + (int)std::round(out*mult), -128), 127); + } + + for (; out_j < outW; out_j++ ) + { + int in_j0 = out_j * stride_w - pad_l, in_j1 = in_j0 + dilation_w, in_j2 = in_j0 + dilation_w*2; + int s0 = 1, s1 = 1, s2 = 1; + if (in_j0 >= width) + { + in_j0 = 0; + s0 = 0; + biasCopy += inpZp*(w00 + w10 + w20); + } + if (in_j1 >= width) + { + in_j1 = 0; + s1 = 0; + biasCopy += inpZp*(w01 + w11 + w21); + } + if (in_j2 >= width) + { + in_j2 = 0; + s2 = 0; + biasCopy += inpZp*(w02 + w12 + w22); + } + out = (int)imgptr0[in_j0]*w00*s0 + (int)imgptr0[in_j1]*w01*s1 + (int)imgptr0[in_j2]*w02*s2 + + (int)imgptr1[in_j0]*w10*s0 + (int)imgptr1[in_j1]*w11*s1 + (int)imgptr1[in_j2]*w12*s2 + + (int)imgptr2[in_j0]*w20*s0 + (int)imgptr2[in_j1]*w21*s1 + (int)imgptr2[in_j2]*w22*s2 + biasCopy; + outptr[out_j] = std::min(std::max(outZp + (int)std::round(out*mult), -128), 127); + } + } +} + +// dst = vec * weights^t + bias +void fastGEMM1T( const int8_t* vec, const int8_t* weights, + size_t wstep, const int* bias, const float* multiplier, + int* dst, int nvecs, int vecsize, int outZp ) +{ + int i = 0; + + for( ; i <= nvecs - 8; i += 8 ) + { + const int8_t* wptr = weights + i*wstep; + __m256i vs0 = __lasx_xvreplgr2vr_d(0), vs1 = __lasx_xvreplgr2vr_d(0), + vs2 = __lasx_xvreplgr2vr_d(0), vs3 = __lasx_xvreplgr2vr_d(0), + vs4 = __lasx_xvreplgr2vr_d(0), vs5 = __lasx_xvreplgr2vr_d(0), + vs6 = __lasx_xvreplgr2vr_d(0), vs7 = __lasx_xvreplgr2vr_d(0); + + __m128i voutzp = __lsx_vreplgr2vr_w(outZp); + __m128i outmin = __lsx_vreplgr2vr_w(-128), outmax = __lsx_vreplgr2vr_w(127); + + for( int k = 0; k < vecsize; k += 32, wptr += 32 ) + { + __m256i v = __lasx_xvld((const __m256i*)(vec + k), 0); + + vs0 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)wptr, 0), v, vs0); + vs1 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep), 0), v, vs1); + vs2 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*2), 0), v, vs2); + vs3 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*3), 0), v, vs3); + vs4 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*4), 0), v, vs4); + vs5 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*5), 0), v, vs5); + vs6 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*6), 0), v, vs6); + vs7 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)(wptr + wstep*7), 0), v, vs7); + } + + /*s0*/ + __m256i vs0_hadd_w = __lasx_xvhaddw_d_w(vs0, vs0); + __m256i vs0_hadd_d = __lasx_xvhaddw_q_d(vs0_hadd_w, vs0_hadd_w); + + __m256i vs1_hadd_w = __lasx_xvhaddw_d_w(vs1, vs1); + __m256i vs1_hadd_d = __lasx_xvhaddw_q_d(vs1_hadd_w, vs1_hadd_w); + + __m256i vs2_hadd_w = __lasx_xvhaddw_d_w(vs2, vs2); + __m256i vs2_hadd_d = __lasx_xvhaddw_q_d(vs2_hadd_w, vs2_hadd_w); + + __m256i vs3_hadd_w = __lasx_xvhaddw_d_w(vs3, vs3); + __m256i vs3_hadd_d = __lasx_xvhaddw_q_d(vs3_hadd_w, vs3_hadd_w); + + __m256i vs1_vs0 = __lasx_xvpackev_w(vs1_hadd_d, vs0_hadd_d); + __m256i vs3_vs2 = __lasx_xvpackev_w(vs3_hadd_d, vs2_hadd_d); + __m256i s0 = __lasx_xvpackev_d(vs3_vs2, vs1_vs0); + + /*s1*/ + __m256i vs4_hadd_w = __lasx_xvhaddw_d_w(vs4, vs4); + __m256i vs4_hadd_d = __lasx_xvhaddw_q_d(vs4_hadd_w, vs4_hadd_w); + + __m256i vs5_hadd_w = __lasx_xvhaddw_d_w(vs5, vs5); + __m256i vs5_hadd_d = __lasx_xvhaddw_q_d(vs5_hadd_w, vs5_hadd_w); + + __m256i vs6_hadd_w = __lasx_xvhaddw_d_w(vs6, vs6); + __m256i vs6_hadd_d = __lasx_xvhaddw_q_d(vs6_hadd_w, vs6_hadd_w); + + __m256i vs7_hadd_w = __lasx_xvhaddw_d_w(vs7, vs7); + __m256i vs7_hadd_d = __lasx_xvhaddw_q_d(vs7_hadd_w, vs7_hadd_w); + + __m256i vs5_vs4 = __lasx_xvpackev_w(vs5_hadd_d, vs4_hadd_d); + __m256i vs7_vs6 = __lasx_xvpackev_w(vs7_hadd_d, vs6_hadd_d); + __m256i s1 = __lasx_xvpackev_d(vs7_vs6, vs5_vs4); + + s0 = __lasx_xvadd_w(s0, __lasx_xvpermi_q(s0, s0, 1)); + s1 = __lasx_xvadd_w(s1, __lasx_xvpermi_q(s1, s1, 1)); + + __m128i t0 = __lsx_vadd_w(*(__m128i*)(&s0), __lsx_vld((__m128i*)(bias + i), 0)); + __m128i t1 = __lsx_vadd_w(*(__m128i*)(&s1), __lsx_vld((__m128i*)(bias + i), 4*4)); + + t0 = __lsx_vadd_w(voutzp, __lsx_vftint_w_s(__lsx_vfmul_s(__lsx_vffint_s_w(t0), (__m128)__lsx_vld(multiplier + i, 0)))); + t1 = __lsx_vadd_w(voutzp, __lsx_vftint_w_s(__lsx_vfmul_s(__lsx_vffint_s_w(t1), (__m128)__lsx_vld(multiplier + i, 4*4)))); + + t0 = __lsx_vmin_w(__lsx_vmax_w(t0, outmin), outmax); + t1 = __lsx_vmin_w(__lsx_vmax_w(t1, outmin), outmax); + + __lsx_vst(t0, (__m128i*)(dst + i), 0); + __lsx_vst(t1, (__m128i*)(dst + i), 4*4); + } + + for( ; i < nvecs; i++ ) + { + const int8_t* wptr = weights + i*wstep; + __m256i vs0 = __lasx_xvreplgr2vr_d(0); + + for( int k = 0; k < vecsize; k += 32, wptr += 32 ) + { + __m256i v = __lasx_xvld((const __m256i*)(vec + k), 0); + vs0 = _v256_fmadds8_s32(__lasx_xvld((const __m256i*)wptr, 0), v, vs0); + } + + __m256i s0_hadd_w = __lasx_xvhaddw_d_w(vs0, vs0); + int temp = ((v4i64)s0_hadd_w)[0] + ((v4i64)s0_hadd_w)[1] + ((v4i64)s0_hadd_w)[2] + ((v4i64)s0_hadd_w)[3]; + dst[i] = outZp + (int)std::round((temp + bias[i]) * multiplier[i]); + } + +} +#endif // CV_LASX + CV_CPU_OPTIMIZATION_NAMESPACE_END }} // namespace diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 7bb3014fc0..678a052c7c 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -986,12 +986,13 @@ public: bool useAVX2; bool useAVX512; bool useRVV; + bool useLASX; int blk_size_cn; ParallelConv() : input_(0), weights_(0), output_(0), ngroups_(0), nstripes_(0), biasvec_(0), reluslope_(0), activ_(0), is1x1_(false), useAVX(false), useAVX2(false), useAVX512(false), useRVV(false) - , blk_size_cn(0) + , useLASX(false), blk_size_cn(0) {} static void run( const Mat& input, Mat& output, const Mat& weights, @@ -1049,6 +1050,7 @@ public: p.useAVX2 = checkHardwareSupport(CPU_AVX2) && isConv2D; p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX && isConv2D; p.useRVV = checkHardwareSupport(CPU_RVV) && isConv2D; + p.useLASX = checkHardwareSupport(CPU_LASX) && isConv2D; int kernel_d = isConv3D? kernel_size[0] : 1; int kernel_h = isConv1D? 1 : kernel_size[kernel_size.size() - 2]; @@ -1256,6 +1258,13 @@ public: stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); else + #endif + #if CV_TRY_LASX + if(useLASX) + opt_LASX::fastDepthwiseConv(wptr, kernel_h, kernel_w, + stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, + biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); + else #endif { const float w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], @@ -1631,6 +1640,12 @@ public: opt_RVV::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, outShape, bsz, vsz, vsz_a, relu, cn0 == 0); else + #endif + #if CV_TRY_LASX + if(useLASX) + opt_LASX::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, + outShape, bsz, vsz, vsz_a, relu, cn0 == 0); + else #endif for( int i = 0; i < outCn; i += 2 ) { @@ -2437,6 +2452,7 @@ public: useAVX2 = checkHardwareSupport(CPU_AVX2); useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX; useRVV = checkHardwareSupport(CPU_RVV); + useLASX = checkHardwareSupport(CPU_LASX); } void operator()(const Range& range_) const CV_OVERRIDE @@ -2474,6 +2490,11 @@ public: opt_RVV::fastGEMM( aptr, astep, bptr, bstep, cptr, cstep, mmax, kmax, nmax ); } else + #endif + #if CV_TRY_LASX + if( useLASX ) + opt_LASX::fastGEMM( aptr, astep, bptr, bstep, cptr, cstep, mmax, kmax, nmax ); + else #endif for( m = 0; m < mmax; m += 2 ) { @@ -2574,6 +2595,7 @@ public: bool useAVX2; bool useAVX512; bool useRVV; + bool useLASX; }; class Col2ImInvoker : public cv::ParallelLoopBody diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 509f6cc177..71ca706ac4 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -173,7 +173,7 @@ public: class FullyConnected : public ParallelLoopBody { public: - FullyConnected() : srcMat(0), weights(0), biasMat(0), activ(0), dstMat(0), nstripes(0), useAVX(false), useAVX2(false), useAVX512(false), useRVV(false) {} + FullyConnected() : srcMat(0), weights(0), biasMat(0), activ(0), dstMat(0), nstripes(0), useAVX(false), useAVX2(false), useAVX512(false), useRVV(false), useLASX(false) {} static void run(const Mat& srcMat, const Mat& weights, const Mat& biasMat, Mat& dstMat, const ActivationLayer* activ, int nstripes) @@ -197,6 +197,7 @@ public: p.useAVX2 = checkHardwareSupport(CPU_AVX2); p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX; p.useRVV = checkHardwareSupport(CPU_RVV); + p.useLASX = checkHardwareSupport(CPU_LASX); parallel_for_(Range(0, nstripes), p, nstripes); } @@ -250,6 +251,11 @@ public: if( useRVV ) opt_RVV::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize); else + #endif + #if CV_TRY_LASX + if( useLASX ) + opt_LASX::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize); + else #endif { int i = 0; @@ -305,6 +311,7 @@ public: bool useAVX2; bool useAVX512; bool useRVV; + bool useLASX; }; #ifdef HAVE_OPENCL diff --git a/modules/dnn/src/layers/layers_common.simd.hpp b/modules/dnn/src/layers/layers_common.simd.hpp index fd88a3c3d2..f706abfa76 100644 --- a/modules/dnn/src/layers/layers_common.simd.hpp +++ b/modules/dnn/src/layers/layers_common.simd.hpp @@ -1343,5 +1343,684 @@ void fastDepthwiseConv( const float* wptr, #endif // CV_RVV +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_LASX + +enum { FASCONV_BASE_VECSZ = 4 }; + +void fastConv( const float* weights, size_t wstep, const float* bias, + const float* rowbuf, float* output, const int* outShape, + int blockSize, int vecsize, int vecsize_aligned, + const float* relu, bool initOutput ) +{ + int outCn = outShape[1]; + size_t outPlaneSize = outShape[2]*outShape[3]; + float r0 = 1.f, r1 = 1.f, r2 = 1.f; + __m256 t1 = _v256_setall_ps(1.f), t2 = _v256_setall_ps(0.f); + __m128 vr0 = *(__m128*)&t1, vr1 = vr0, vr2 = vr0, z = *(__m128*)&t2; + int CV_DECL_ALIGNED(16) maskbuf[FASCONV_BASE_VECSZ] = {0}; + int rsz = blockSize % FASCONV_BASE_VECSZ; + for( int i = 0; i < rsz; i++ ) + maskbuf[FASCONV_BASE_VECSZ - i - 1] = -1; + __m128i mask = __lsx_vld((const float*)maskbuf, 0); + + // now compute dot product of the weights + // and im2row-transformed part of the tensor + for( int i = 0; i < outCn; i += 3 ) + { + const float* wptr0 = weights + i*wstep; + const float* wptr1 = wptr0 + wstep; + const float* wptr2 = wptr1 + wstep; + float* outptr0 = output + i*outPlaneSize; + float* outptr1 = outptr0 + outPlaneSize; + float* outptr2 = outptr1 + outPlaneSize; + float bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2]; + + if( i+2 >= outCn ) + { + wptr2 = wptr1; + outptr2 = outptr1; + bias2 = bias1; + if( i+1 >= outCn ) + { + wptr2 = wptr1 = wptr0; + outptr2 = outptr1 = outptr0; + bias2 = bias1 = bias0; + } + } + + if( relu ) + { + r0 = relu[i]; r1 = relu[i+1]; r2 = relu[i+2]; + if( i+2 >= outCn ) + { + r2 = r1; + if( i+1 >= outCn ) + r2 = r1 = r0; + } + vr0 = _v256_extract_low(_v256_setall_ps(r0)); + vr1 = _v256_extract_low(_v256_setall_ps(r1)); + vr2 = _v256_extract_low(_v256_setall_ps(r2)); + } + + int j = 0; + for( ; j < blockSize; j += FASCONV_BASE_VECSZ ) + { + bool tail = false; + if (j + FASCONV_BASE_VECSZ > blockSize) + { + if (j == 0) + break; + j = blockSize - FASCONV_BASE_VECSZ; + tail = true; + } + int k = 0; + const float* rptr = rowbuf + j*vecsize_aligned; + + __m256i tmp; + __m256 vs00 = (__m256)__lasx_xvxor_v(tmp, tmp), vs01 = (__m256)__lasx_xvxor_v(tmp, tmp), + vs02 = (__m256)__lasx_xvxor_v(tmp, tmp), vs03 = (__m256)__lasx_xvxor_v(tmp, tmp), + vs10 = (__m256)__lasx_xvxor_v(tmp, tmp), vs11 = (__m256)__lasx_xvxor_v(tmp, tmp), + vs12 = (__m256)__lasx_xvxor_v(tmp, tmp), vs13 = (__m256)__lasx_xvxor_v(tmp, tmp), + vs20 = (__m256)__lasx_xvxor_v(tmp, tmp), vs21 = (__m256)__lasx_xvxor_v(tmp, tmp), + vs22 = (__m256)__lasx_xvxor_v(tmp, tmp), vs23 = (__m256)__lasx_xvxor_v(tmp, tmp); + + for (; k < vecsize; k += 8, rptr += 8 ) + { + __m256 w0 = (__m256)__lasx_xvld(wptr0 + k, 0); + __m256 w1 = (__m256)__lasx_xvld(wptr1 + k, 0); + __m256 w2 = (__m256)__lasx_xvld(wptr2 + k, 0); + __m256 r0 = (__m256)__lasx_xvld(rptr, 0); + + vs00 = __lasx_xvfmadd_s(w0, r0, vs00); + vs10 = __lasx_xvfmadd_s(w1, r0, vs10); + vs20 = __lasx_xvfmadd_s(w2, r0, vs20); + + r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned, 0); + vs01 = __lasx_xvfmadd_s(w0, r0, vs01); + vs11 = __lasx_xvfmadd_s(w1, r0, vs11); + vs21 = __lasx_xvfmadd_s(w2, r0, vs21); + + r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned*2, 0); + vs02 = __lasx_xvfmadd_s(w0, r0, vs02); + vs12 = __lasx_xvfmadd_s(w1, r0, vs12); + vs22 = __lasx_xvfmadd_s(w2, r0, vs22); + + r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned*3, 0); + vs03 = __lasx_xvfmadd_s(w0, r0, vs03); + vs13 = __lasx_xvfmadd_s(w1, r0, vs13); + vs23 = __lasx_xvfmadd_s(w2, r0, vs23); + } + + /*t0*/ + __m256 vs00_perm = (__m256)__lasx_xvpermi_d(vs00, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs00_add_2w = __lasx_xvfadd_s(vs00, vs00_perm); + __m256 tmp00_srl = (__m256)__lasx_xvsrli_d(vs00_add_2w, 32); + __m256 vs00_add_4w = __lasx_xvfadd_s(vs00_add_2w, tmp00_srl); + + __m256 vs01_perm = (__m256)__lasx_xvpermi_d(vs01, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs01_add_2w = __lasx_xvfadd_s(vs01, vs01_perm); + __m256 tmp01_srl = (__m256)__lasx_xvsrli_d(vs01_add_2w, 32); + __m256 vs01_add_4w = __lasx_xvfadd_s(vs01_add_2w, tmp01_srl); + + __m256 vs02_perm = (__m256)__lasx_xvpermi_d(vs02, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs02_add_2w = __lasx_xvfadd_s(vs02, vs02_perm); + __m256 tmp02_srl = (__m256)__lasx_xvsrli_d(vs02_add_2w, 32); + __m256 vs02_add_4w = __lasx_xvfadd_s(vs02_add_2w, tmp02_srl); + + __m256 vs03_perm = (__m256)__lasx_xvpermi_d(vs03, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs03_add_2w = __lasx_xvfadd_s(vs03, vs03_perm); + __m256 tmp03_srl = (__m256)__lasx_xvsrli_d(vs03_add_2w, 32); + __m256 vs03_add_4w = __lasx_xvfadd_s(vs03_add_2w, tmp03_srl); + + __m256i vs01_vs00 = __lasx_xvpackev_w((__m256i)vs01_add_4w, (__m256i)vs00_add_4w); + __m256i vs03_vs02 = __lasx_xvpackev_w((__m256i)vs03_add_4w, (__m256i)vs02_add_4w); + __m256 t0 = (__m256)__lasx_xvpackev_d(vs03_vs02, vs01_vs00); + + /*t1*/ + __m256 vs10_perm = (__m256)__lasx_xvpermi_d(vs10, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs10_add_2w = __lasx_xvfadd_s(vs10, vs10_perm); + __m256 tmp10_srl = (__m256)__lasx_xvsrli_d(vs10_add_2w, 32); + __m256 vs10_add_4w = __lasx_xvfadd_s(vs10_add_2w, tmp10_srl); + + __m256 vs11_perm = (__m256)__lasx_xvpermi_d(vs11, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs11_add_2w = __lasx_xvfadd_s(vs11, vs11_perm); + __m256 tmp11_srl = (__m256)__lasx_xvsrli_d(vs11_add_2w, 32); + __m256 vs11_add_4w = __lasx_xvfadd_s(vs11_add_2w, tmp11_srl); + + __m256 vs12_perm = (__m256)__lasx_xvpermi_d(vs12, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs12_add_2w = __lasx_xvfadd_s(vs12, vs12_perm); + __m256 tmp12_srl = (__m256)__lasx_xvsrli_d(vs12_add_2w, 32); + __m256 vs12_add_4w = __lasx_xvfadd_s(vs12_add_2w, tmp12_srl); + + __m256 vs13_perm = (__m256)__lasx_xvpermi_d(vs13, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs13_add_2w = __lasx_xvfadd_s(vs13, vs13_perm); + __m256 tmp13_srl = (__m256)__lasx_xvsrli_d(vs13_add_2w, 32); + __m256 vs13_add_4w = __lasx_xvfadd_s(vs13_add_2w, tmp13_srl); + + __m256i vs11_vs10 = __lasx_xvpackev_w((__m256i)vs11_add_4w, (__m256i)vs10_add_4w); + __m256i vs13_vs12 = __lasx_xvpackev_w((__m256i)vs13_add_4w, (__m256i)vs12_add_4w); + __m256 t1 = (__m256)__lasx_xvpackev_d(vs13_vs12, vs11_vs10); + + /*t2*/ + __m256 vs20_perm = (__m256)__lasx_xvpermi_d(vs20, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs20_add_2w = __lasx_xvfadd_s(vs20, vs20_perm); + __m256 tmp20_srl = (__m256)__lasx_xvsrli_d(vs20_add_2w, 32); + __m256 vs20_add_4w = __lasx_xvfadd_s(vs20_add_2w, tmp20_srl); + + __m256 vs21_perm = (__m256)__lasx_xvpermi_d(vs21, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs21_add_2w = __lasx_xvfadd_s(vs21, vs21_perm); + __m256 tmp21_srl = (__m256)__lasx_xvsrli_d(vs21_add_2w, 32); + __m256 vs21_add_4w = __lasx_xvfadd_s(vs21_add_2w, tmp21_srl); + + __m256 vs22_perm = (__m256)__lasx_xvpermi_d(vs22, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs22_add_2w = __lasx_xvfadd_s(vs22, vs22_perm); + __m256 tmp22_srl = (__m256)__lasx_xvsrli_d(vs22_add_2w, 32); + __m256 vs22_add_4w = __lasx_xvfadd_s(vs22_add_2w, tmp22_srl); + + __m256 vs23_perm = (__m256)__lasx_xvpermi_d(vs23, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs23_add_2w = __lasx_xvfadd_s(vs23, vs23_perm); + __m256 tmp23_srl = (__m256)__lasx_xvsrli_d(vs23_add_2w, 32); + __m256 vs23_add_4w = __lasx_xvfadd_s(vs23_add_2w, tmp23_srl); + + __m256i vs21_vs20 = __lasx_xvpackev_w((__m256i)vs21_add_4w, (__m256i)vs20_add_4w); + __m256i vs23_vs22 = __lasx_xvpackev_w((__m256i)vs23_add_4w, (__m256i)vs22_add_4w); + __m256 t2 = (__m256)__lasx_xvpackev_d(vs23_vs22, vs21_vs20); + + t0 = __lasx_xvfadd_s(t0, (__m256)__lasx_xvpermi_q(t0, t0, 1)); + t1 = __lasx_xvfadd_s(t1, (__m256)__lasx_xvpermi_q(t1, t1, 1)); + t2 = __lasx_xvfadd_s(t2, (__m256)__lasx_xvpermi_q(t2, t2, 1)); + + __m128 s0, s1, s2; + + if( initOutput ) + { + s0 = _v256_extract_low(_v256_setall_ps(bias0)); + s1 = _v256_extract_low(_v256_setall_ps(bias1)); + s2 = _v256_extract_low(_v256_setall_ps(bias2)); + } + else + { + s0 = (__m128)__lsx_vld(outptr0 + j, 0); + s1 = (__m128)__lsx_vld(outptr1 + j, 0); + s2 = (__m128)__lsx_vld(outptr2 + j, 0); + } + + s0 = __lsx_vfadd_s(s0, *(__m128*)&t0); + s1 = __lsx_vfadd_s(s1, *(__m128*)&t1); + s2 = __lsx_vfadd_s(s2, *(__m128*)&t2); + + if( relu ) + { + __m128i m0 = __lsx_vfcmp_clt_s(z, s0); + __m128i m1 = __lsx_vfcmp_clt_s(z, s1); + __m128i m2 = __lsx_vfcmp_clt_s(z, s2); + s0 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s0, vr0), (__m128i)s0, m0); + s1 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s1, vr1), (__m128i)s1, m1); + s2 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s2, vr2), (__m128i)s2, m2); + } + + if( tail ) + { + s0 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr0 + j, 0), (__m128i)s0, mask); + s1 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr1 + j, 0), (__m128i)s1, mask); + s2 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr2 + j, 0), (__m128i)s2, mask); + } + + __lsx_vst(s0, outptr0 + j, 0); + __lsx_vst(s1, outptr1 + j, 0); + __lsx_vst(s2, outptr2 + j, 0); + } + + for( ; j <= blockSize - 2; j += 2 ) + { + const float* rptr0 = rowbuf + j*vecsize_aligned; + const float* rptr1 = rowbuf + (j+1)*vecsize_aligned; + float s00, s01, s10, s11, s20, s21; + + if( initOutput ) + { + s00 = s01 = bias0; + s10 = s11 = bias1; + s20 = s21 = bias2; + } + else + { + s00 = outptr0[j]; s01 = outptr0[j+1]; + s10 = outptr1[j]; s11 = outptr1[j+1]; + s20 = outptr2[j]; s21 = outptr2[j+1]; + } + + for( int k = 0; k < vecsize; k++ ) + { + float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; + float r = rptr0[k]; + s00 += w0*r; s10 += w1*r; s20 += w2*r; + r = rptr1[k]; + s01 += w0*r; s11 += w1*r; s21 += w2*r; + } + + if( relu ) + { + s00 = s00 > 0.f ? s00 : s00*r0; + s01 = s01 > 0.f ? s01 : s01*r0; + s10 = s10 > 0.f ? s10 : s10*r1; + s11 = s11 > 0.f ? s11 : s11*r1; + s20 = s20 > 0.f ? s20 : s20*r2; + s21 = s21 > 0.f ? s21 : s21*r2; + } + + outptr0[j] = s00; + outptr0[j+1] = s01; + outptr1[j] = s10; + outptr1[j+1] = s11; + outptr2[j] = s20; + outptr2[j+1] = s21; + } + + for( ; j < blockSize; j++ ) + { + const float* rptr0 = rowbuf + j*vecsize_aligned; + float s00, s10, s20; + + if( initOutput ) + { + s00 = bias0; + s10 = bias1; + s20 = bias2; + } + else + { + s00 = outptr0[j]; + s10 = outptr1[j]; + s20 = outptr2[j]; + } + + for( int k = 0; k < vecsize; k++ ) + { + float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; + float r = rptr0[k]; + s00 += w0*r; s10 += w1*r; s20 += w2*r; + } + + if( relu ) + { + s00 = s00 > 0.f ? s00 : s00*r0; + s10 = s10 > 0.f ? s10 : s10*r1; + s20 = s20 > 0.f ? s20 : s20*r2; + } + + outptr0[j] = s00; + outptr1[j] = s10; + outptr2[j] = s20; + } + } +} + +static inline void _v256_load_deinterleave(const float* ptr, __m256& a, __m256& b) +{ + __m256 t0 = (__m256)__lasx_xvld(ptr, 0); + __m256 t1 = (__m256)__lasx_xvld(ptr, 8*4); + + __m256 lo = (__m256)__lasx_xvpermi_q(t0, t1, 2+0*16); + __m256 hi = (__m256)__lasx_xvpermi_q(t0, t1, 3+1*16); + + a = (__m256)__lasx_xvpermi_w(hi, lo, 0x88); + b = (__m256)__lasx_xvpermi_w(hi, lo, 0xdd); +} + +void fastDepthwiseConv( const float* wptr, + int kernel_h, int kernel_w, + int stride_h, int stride_w, + int dilation_h, int dilation_w, + int pad_t, int pad_l, + const float* biasptr, const float* relu, + const float* inptr_, + int height, int width, + float* outptr_, + int out_d, int outH, int outW ) +{ + const float w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], + w10 = wptr[3], w11 = wptr[4], w12 = wptr[5], + w20_ = wptr[6], w21_ = wptr[7], w22_ = wptr[8]; + int outW1 = min(outW, (width - dilation_w*(kernel_w - 1) + pad_l)/stride_w); + float relu_coeff = relu ? relu[out_d] : 1.f, bias = biasptr[out_d]; + + for (int out_i = 0; out_i < outH; out_i++) + { + int in_i = out_i * stride_h - pad_t, out_j = 0; + const float* imgptr0 = inptr_ + in_i*width; + const float* imgptr1 = imgptr0 + dilation_h*width; + const float* imgptr2 = imgptr0 + (dilation_h*2)*width; + float out, w00 = w00_, w01 = w01_, w02 = w02_; + float w20 = w20_, w21 = w21_, w22 = w22_; + if (in_i < 0) + { + w00 = w01 = w02 = 0.f; + imgptr0 = imgptr1; + } + else if (in_i + dilation_h*(kernel_h-1) >= height) + { + w20 = w21 = w22 = 0.f; + imgptr2 = imgptr1; + } + float* outptr = outptr_ + out_i*outW; + if (pad_l > 0) + { + out = imgptr0[0]*w01 + imgptr0[dilation_w]*w02 + + imgptr1[0]*w11 + imgptr1[dilation_w]*w12 + + imgptr2[0]*w21 + imgptr2[dilation_w]*w22 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[0] = out; + out_j = 1; + } + + if (stride_w == 1 || (stride_w == 2 && dilation_w == 1)) + { + const int VECSZ = 8; + __m256 vw00 = _v256_setall_ps(w00), vw01 = _v256_setall_ps(w01), vw02 = _v256_setall_ps(w02), + vw10 = _v256_setall_ps(w10), vw11 = _v256_setall_ps(w11), vw12 = _v256_setall_ps(w12), + vw20 = _v256_setall_ps(w20), vw21 = _v256_setall_ps(w21), vw22 = _v256_setall_ps(w22); + __m256 z = (__m256)__lasx_xvxor_v((__m256i)vw00, (__m256i)vw00), + vbias = _v256_setall_ps(bias), vrc = _v256_setall_ps(relu_coeff); + + if( stride_w == 1 ) + for( ; out_j < outW1; out_j += VECSZ ) + { + if (out_j + VECSZ > outW1 && out_j > pad_l) + out_j = outW1 - VECSZ; + int in_j = out_j * stride_w - pad_l; + __m256 v00 = (__m256)__lasx_xvld(imgptr0 + in_j, 0), + v01 = (__m256)__lasx_xvld(imgptr0 + in_j + dilation_w, 0), + v02 = (__m256)__lasx_xvld(imgptr0 + in_j + dilation_w*2, 0), + v10 = (__m256)__lasx_xvld(imgptr1 + in_j, 0), + v11 = (__m256)__lasx_xvld(imgptr1 + in_j + dilation_w, 0), + v12 = (__m256)__lasx_xvld(imgptr1 + in_j + dilation_w*2, 0), + v20 = (__m256)__lasx_xvld(imgptr2 + in_j, 0), + v21 = (__m256)__lasx_xvld(imgptr2 + in_j + dilation_w, 0), + v22 = (__m256)__lasx_xvld(imgptr2 + in_j + dilation_w*2, 0); + + __m256 vout0 = __lasx_xvfmadd_s(v00, vw00, vbias); + __m256 vout1 = __lasx_xvfmul_s(v01, vw01); + __m256 vout2 = __lasx_xvfmul_s(v02, vw02); + + vout0 = __lasx_xvfmadd_s(v10, vw10, vout0); + vout1 = __lasx_xvfmadd_s(v11, vw11, vout1); + vout2 = __lasx_xvfmadd_s(v12, vw12, vout2); + + vout0 = __lasx_xvfmadd_s(v20, vw20, vout0); + vout1 = __lasx_xvfmadd_s(v21, vw21, vout1); + vout2 = __lasx_xvfmadd_s(v22, vw22, vout2); + + vout0 = __lasx_xvfadd_s(__lasx_xvfadd_s(vout0, vout1), vout2); + if (relu) + { + __m256i m = __lasx_xvfcmp_clt_s(z, vout0); + vout0 = (__m256)__lasx_xvbitsel_v((__m256i)__lasx_xvfmul_s(vout0, vrc), (__m256i)vout0, m); + } + __lasx_xvst(vout0, outptr + out_j, 0); + } + else + for( ; out_j < outW1; out_j += VECSZ ) + { + if (out_j + VECSZ > outW1 && out_j > pad_l) + out_j = outW1 - VECSZ; + int in_j = out_j * stride_w - pad_l; + __m256 v00, v01, v02, v10, v11, v12, v20, v21, v22, unused; + _v256_load_deinterleave(imgptr0 + in_j, v00, v01); + _v256_load_deinterleave(imgptr0 + in_j + 2, v02, unused); + _v256_load_deinterleave(imgptr1 + in_j, v10, v11); + _v256_load_deinterleave(imgptr1 + in_j + 2, v12, unused); + _v256_load_deinterleave(imgptr2 + in_j, v20, v21); + _v256_load_deinterleave(imgptr2 + in_j + 2, v22, unused); + + __m256 vout0 = __lasx_xvfmadd_s(v00, vw00, vbias); + __m256 vout1 = __lasx_xvfmul_s(v01, vw01); + __m256 vout2 = __lasx_xvfmul_s(v02, vw02); + + vout0 = __lasx_xvfmadd_s(v10, vw10, vout0); + vout1 = __lasx_xvfmadd_s(v11, vw11, vout1); + vout2 = __lasx_xvfmadd_s(v12, vw12, vout2); + + vout0 = __lasx_xvfmadd_s(v20, vw20, vout0); + vout1 = __lasx_xvfmadd_s(v21, vw21, vout1); + vout2 = __lasx_xvfmadd_s(v22, vw22, vout2); + + vout0 = __lasx_xvfadd_s(__lasx_xvfadd_s(vout0, vout1), vout2); + if (relu) + { + __m256i m = __lasx_xvfcmp_clt_s(z, vout0); + vout0 = (__m256)__lasx_xvbitsel_v((__m256i)__lasx_xvfmul_s(vout0, vrc), (__m256i)vout0, m); + } + __lasx_xvst(vout0, outptr + out_j, 0); + } + } + + for (; out_j < outW1; out_j++) + { + int in_j = out_j * stride_w - pad_l; + out = imgptr0[in_j]*w00 + imgptr0[in_j + dilation_w]*w01 + imgptr0[in_j + dilation_w*2]*w02 + + imgptr1[in_j]*w10 + imgptr1[in_j + dilation_w]*w11 + imgptr1[in_j + dilation_w*2]*w12 + + imgptr2[in_j]*w20 + imgptr2[in_j + dilation_w]*w21 + imgptr2[in_j + dilation_w*2]*w22 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; + } + + for (; out_j < outW; out_j++ ) + { + int in_j0 = out_j * stride_w - pad_l, in_j1 = in_j0 + dilation_w, in_j2 = in_j0 + dilation_w*2; + float s0 = 1.f, s1 = 1.f, s2 = 1.f; + if (in_j0 >= width) + { + in_j0 = 0; + s0 = 0.f; + } + if (in_j1 >= width) + { + in_j1 = 0; + s1 = 0.f; + } + if (in_j2 >= width) + { + in_j2 = 0; + s2 = 0.f; + } + out = imgptr0[in_j0]*w00*s0 + imgptr0[in_j1]*w01*s1 + imgptr0[in_j2]*w02*s2 + + imgptr1[in_j0]*w10*s0 + imgptr1[in_j1]*w11*s1 + imgptr1[in_j2]*w12*s2 + + imgptr2[in_j0]*w20*s0 + imgptr2[in_j1]*w21*s1 + imgptr2[in_j2]*w22*s2 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; + } + } +} + +// dst = vec * weights^t + bias +void fastGEMM1T( const float* vec, const float* weights, + size_t wstep, const float* bias, + float* dst, int nvecs, int vecsize ) +{ + int i = 0; + __m256i v256_tmp; + + for( ; i <= nvecs - 8; i += 8 ) + { + const float* wptr = weights + i*wstep; + __m256 vs0 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), vs1 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), + vs2 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), vs3 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), + vs4 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), vs5 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), + vs6 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), vs7 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + + for( int k = 0; k < vecsize; k += 8, wptr += 8 ) + { + __m256 v = (__m256)__lasx_xvld(vec + k, 0); + + vs0 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr, 0), v, vs0); + vs1 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep, 0), v, vs1); + vs2 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*2, 0), v, vs2); + vs3 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*3, 0), v, vs3); + vs4 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*4, 0), v, vs4); + vs5 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*5, 0), v, vs5); + vs6 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*6, 0), v, vs6); + vs7 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr + wstep*7, 0), v, vs7); + } + + /*s0*/ + __m256 vs00_perm = (__m256)__lasx_xvpermi_d(vs0, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs00_add_2w = __lasx_xvfadd_s(vs0, vs00_perm); + __m256 tmp00_srl = (__m256)__lasx_xvsrli_d(vs00_add_2w, 32); + __m256 vs00_add_4w = __lasx_xvfadd_s(vs00_add_2w, tmp00_srl); + + __m256 vs01_perm = (__m256)__lasx_xvpermi_d(vs1, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs01_add_2w = __lasx_xvfadd_s(vs1, vs01_perm); + __m256 tmp01_srl = (__m256)__lasx_xvsrli_d(vs01_add_2w, 32); + __m256 vs01_add_4w = __lasx_xvfadd_s(vs01_add_2w, tmp01_srl); + + __m256 vs02_perm = (__m256)__lasx_xvpermi_d(vs2, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs02_add_2w = __lasx_xvfadd_s(vs2, vs02_perm); + __m256 tmp02_srl = (__m256)__lasx_xvsrli_d(vs02_add_2w, 32); + __m256 vs02_add_4w = __lasx_xvfadd_s(vs02_add_2w, tmp02_srl); + + __m256 vs03_perm = (__m256)__lasx_xvpermi_d(vs3, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs03_add_2w = __lasx_xvfadd_s(vs3, vs03_perm); + __m256 tmp03_srl = (__m256)__lasx_xvsrli_d(vs03_add_2w, 32); + __m256 vs03_add_4w = __lasx_xvfadd_s(vs03_add_2w, tmp03_srl); + + __m256i vs01_vs00 = __lasx_xvpackev_w((__m256i)vs01_add_4w, (__m256i)vs00_add_4w); + __m256i vs03_vs02 = __lasx_xvpackev_w((__m256i)vs03_add_4w, (__m256i)vs02_add_4w); + __m256 s0 = (__m256)__lasx_xvpackev_d(vs03_vs02, vs01_vs00); + + /*s1*/ + __m256 vs10_perm = (__m256)__lasx_xvpermi_d(vs4, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs10_add_2w = __lasx_xvfadd_s(vs4, vs10_perm); + __m256 tmp10_srl = (__m256)__lasx_xvsrli_d(vs10_add_2w, 32); + __m256 vs10_add_4w = __lasx_xvfadd_s(vs10_add_2w, tmp10_srl); + + __m256 vs11_perm = (__m256)__lasx_xvpermi_d(vs5, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs11_add_2w = __lasx_xvfadd_s(vs5, vs11_perm); + __m256 tmp11_srl = (__m256)__lasx_xvsrli_d(vs11_add_2w, 32); + __m256 vs11_add_4w = __lasx_xvfadd_s(vs11_add_2w, tmp11_srl); + + __m256 vs12_perm = (__m256)__lasx_xvpermi_d(vs6, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs12_add_2w = __lasx_xvfadd_s(vs6, vs12_perm); + __m256 tmp12_srl = (__m256)__lasx_xvsrli_d(vs12_add_2w, 32); + __m256 vs12_add_4w = __lasx_xvfadd_s(vs12_add_2w, tmp12_srl); + + __m256 vs13_perm = (__m256)__lasx_xvpermi_d(vs7, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs13_add_2w = __lasx_xvfadd_s(vs7, vs13_perm); + __m256 tmp13_srl = (__m256)__lasx_xvsrli_d(vs13_add_2w, 32); + __m256 vs13_add_4w = __lasx_xvfadd_s(vs13_add_2w, tmp13_srl); + + __m256i vs11_vs10 = __lasx_xvpackev_w((__m256i)vs11_add_4w, (__m256i)vs10_add_4w); + __m256i vs13_vs12 = __lasx_xvpackev_w((__m256i)vs13_add_4w, (__m256i)vs12_add_4w); + __m256 s1 = (__m256)__lasx_xvpackev_d(vs13_vs12, vs11_vs10); + + s0 = __lasx_xvfadd_s(s0, (__m256)__lasx_xvpermi_q(s0, s0, 1)); + s1 = __lasx_xvfadd_s(s1, (__m256)__lasx_xvpermi_q(s1, s1, 1)); + + s0 = __lasx_xvfadd_s(s0, (__m256)__lasx_xvld(bias + i, 0)); + s1 = __lasx_xvfadd_s(s1, (__m256)__lasx_xvld(bias + i, 4*4)); + + __lsx_vst(*(__m128*)&s0, dst + i, 0); + __lsx_vst(*(__m128*)&s1, dst + i, 4*4); + } + + float temp = 0.f; + for( ; i < nvecs; i++ ) + { + const float* wptr = weights + i*wstep; + __m256 vs0 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + + for( int k = 0; k < vecsize; k += 8, wptr += 8 ) + { + __m256 v = (__m256)__lasx_xvld(vec + k, 0); + vs0 = __lasx_xvfmadd_s((__m256)__lasx_xvld(wptr, 0), v, vs0); + } + + __m256i vs0_perm = __lasx_xvpermi_d(vs0, (2<<6) + (3<<4) + (0<<2) + 1); + __m256 vs0_add_2w = __lasx_xvfadd_s(vs0, (__m256)vs0_perm); + __m256i tmp_srl = __lasx_xvsrli_d(vs0_add_2w, 32); + __m256 vs0_add_4w = __lasx_xvfadd_s(vs0_add_2w, (__m256)tmp_srl); + temp = ((v8f32)vs0_add_4w)[0] + ((v8f32)vs0_add_4w)[4]; + dst[i] = temp + bias[i]; + } +} + + +void fastGEMM( const float* aptr, size_t astep, const float* bptr, + size_t bstep, float* cptr, size_t cstep, + int ma, int na, int nb ) +{ + int n = 0; + + for( ; n <= nb - 16; n += 16 ) + { + for( int m = 0; m < ma; m += 4 ) + { + const float* aptr0 = aptr + astep*m; + const float* aptr1 = aptr + astep*std::min(m+1, ma-1); + const float* aptr2 = aptr + astep*std::min(m+2, ma-1); + const float* aptr3 = aptr + astep*std::min(m+3, ma-1); + + float* cptr0 = cptr + cstep*m; + float* cptr1 = cptr + cstep*std::min(m+1, ma-1); + float* cptr2 = cptr + cstep*std::min(m+2, ma-1); + float* cptr3 = cptr + cstep*std::min(m+3, ma-1); + + __m256i v256_tmp; + __m256 d00 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), d01 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + __m256 d10 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), d11 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + __m256 d20 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), d21 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + __m256 d30 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp), d31 = (__m256)__lasx_xvxor_v(v256_tmp, v256_tmp); + + for( int k = 0; k < na; k++ ) + { + __m256 a0 = _v256_setall_ps(aptr0[k]); + __m256 a1 = _v256_setall_ps(aptr1[k]); + __m256 a2 = _v256_setall_ps(aptr2[k]); + __m256 a3 = _v256_setall_ps(aptr3[k]); + + __m256 b0 = (__m256)__lasx_xvld(bptr + k*bstep + n, 0); + __m256 b1 = (__m256)__lasx_xvld(bptr + k*bstep + n + 8, 0); + d00 = __lasx_xvfmadd_s(a0, b0, d00); + d01 = __lasx_xvfmadd_s(a0, b1, d01); + d10 = __lasx_xvfmadd_s(a1, b0, d10); + d11 = __lasx_xvfmadd_s(a1, b1, d11); + d20 = __lasx_xvfmadd_s(a2, b0, d20); + d21 = __lasx_xvfmadd_s(a2, b1, d21); + d30 = __lasx_xvfmadd_s(a3, b0, d30); + d31 = __lasx_xvfmadd_s(a3, b1, d31); + } + + __lasx_xvst(d00, cptr0 + n, 0); + __lasx_xvst(d01, cptr0 + n, 8*4); + __lasx_xvst(d10, cptr1 + n, 0); + __lasx_xvst(d11, cptr1 + n, 8*4); + __lasx_xvst(d20, cptr2 + n, 0); + __lasx_xvst(d21, cptr2 + n, 8*4); + __lasx_xvst(d30, cptr3 + n, 0); + __lasx_xvst(d31, cptr3 + n, 8*4); + } + } + + for( ; n < nb; n++ ) + { + for( int m = 0; m < ma; m++ ) + { + const float* aptr0 = aptr + astep*m; + float* cptr0 = cptr + cstep*m; + float d0 = 0.f; + + for( int k = 0; k < na; k++ ) + d0 += aptr0[k]*bptr[k*bstep + n]; + + cptr0[n] = d0; + } + } +} + +#endif // CV_LASX + CV_CPU_OPTIMIZATION_NAMESPACE_END }} // namespace diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index f57d05faee..c6dd063f73 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -2178,6 +2178,9 @@ public: #if CV_TRY_SSE4_1 bool useSSE4_1 = CV_CPU_HAS_SUPPORT_SSE4_1; #endif + #if CV_TRY_LASX + bool useLASX = CV_CPU_HAS_SUPPORT_LASX; + #endif int bh0 = std::min(BLOCK_SZ/2, dst.rows); int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, dst.cols); @@ -2241,6 +2244,10 @@ public: if ( useAVX2 ) x1 = opt_AVX2::warpAffineBlockline(adelta + x, bdelta + x, xy, alpha, X0, Y0, bw); #endif + #if CV_TRY_LASX + if ( useLASX ) + x1 = opt_LASX::warpAffineBlockline(adelta + x, bdelta + x, xy, alpha, X0, Y0, bw); + #endif #if CV_SIMD128 { v_int32x4 v__X0 = v_setall_s32(X0), v__Y0 = v_setall_s32(Y0); diff --git a/modules/imgproc/src/imgwarp.hpp b/modules/imgproc/src/imgwarp.hpp index 1f8f1c5d17..4b81b5e79d 100644 --- a/modules/imgproc/src/imgwarp.hpp +++ b/modules/imgproc/src/imgwarp.hpp @@ -61,6 +61,13 @@ int warpAffineBlockline(int *adelta, int *bdelta, short* xy, short* alpha, int X #endif } +namespace opt_LASX +{ +#if CV_TRY_LASX +int warpAffineBlockline(int *adelta, int *bdelta, short* xy, short* alpha, int X0, int Y0, int bw); +#endif +} + namespace opt_SSE4_1 { #if CV_TRY_SSE4_1 diff --git a/modules/imgproc/src/imgwarp.lasx.cpp b/modules/imgproc/src/imgwarp.lasx.cpp new file mode 100644 index 0000000000..f6bf2a13cb --- /dev/null +++ b/modules/imgproc/src/imgwarp.lasx.cpp @@ -0,0 +1,98 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +/* //////////////////////////////////////////////////////////////////// +// +// Geometrical transforms on images and matrices: rotation, zoom etc. +// +// */ + +#include "precomp.hpp" +#include "imgwarp.hpp" +#include "opencv2/core/hal/intrin.hpp" + +namespace cv +{ +namespace opt_LASX +{ + +int warpAffineBlockline(int *adelta, int *bdelta, short* xy, short* alpha, int X0, int Y0, int bw) +{ + const int AB_BITS = MAX(10, (int)INTER_BITS); + int x1 = 0; + __m256i fxy_mask = _v256_setall_w(INTER_TAB_SIZE - 1); + __m256i XX = _v256_setall_w(X0), YY = _v256_setall_w(Y0); + for (; x1 <= bw - 16; x1 += 16) + { + __m256i tx0, tx1, ty0, ty1; + tx0 = __lasx_xvadd_w(__lasx_xvld((const __m256i*)(adelta + x1), 0), XX); + ty0 = __lasx_xvadd_w(__lasx_xvld((const __m256i*)(bdelta + x1), 0), YY); + tx1 = __lasx_xvadd_w(__lasx_xvld((const __m256i*)(adelta + x1), 8*4), XX); + ty1 = __lasx_xvadd_w(__lasx_xvld((const __m256i*)(bdelta + x1), 8*4), YY); + + tx0 = __lasx_xvsrai_w(tx0, AB_BITS - INTER_BITS); + ty0 = __lasx_xvsrai_w(ty0, AB_BITS - INTER_BITS); + tx1 = __lasx_xvsrai_w(tx1, AB_BITS - INTER_BITS); + ty1 = __lasx_xvsrai_w(ty1, AB_BITS - INTER_BITS); + + __m256i fx_ = _lasx_packs_w(__lasx_xvand_v(tx0, fxy_mask), + __lasx_xvand_v(tx1, fxy_mask)); + __m256i fy_ = _lasx_packs_w(__lasx_xvand_v(ty0, fxy_mask), + __lasx_xvand_v(ty1, fxy_mask)); + tx0 = _lasx_packs_w(__lasx_xvsrai_w(tx0, INTER_BITS), + __lasx_xvsrai_w(tx1, INTER_BITS)); + ty0 = _lasx_packs_w(__lasx_xvsrai_w(ty0, INTER_BITS), + __lasx_xvsrai_w(ty1, INTER_BITS)); + fx_ = __lasx_xvsadd_h(fx_, __lasx_xvslli_h(fy_, INTER_BITS)); + fx_ = __lasx_xvpermi_d(fx_, (3 << 6) + (1 << 4) + (2 << 2) + 0); + + __lasx_xvst(__lasx_xvilvl_h(ty0, tx0), (__m256i*)(xy + x1 * 2), 0); + __lasx_xvst(__lasx_xvilvh_h(ty0, tx0), (__m256i*)(xy + x1 * 2), 16*2); + __lasx_xvst(fx_, (__m256i*)(alpha + x1), 0); + } + return x1; +} + +} +} +/* End of file. */ diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 90a05085e3..4ad64534be 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1098,6 +1098,16 @@ resizeNN( const Mat& src, Mat& dst, double fx, double fy ) opt_SSE4_1::resizeNN4_SSE4_1(range, src, dst, x_ofs, ify); } else +#endif +#if CV_TRY_LASX + if(CV_CPU_HAS_SUPPORT_LASX && ((pix_size == 2) || (pix_size == 4))) + { + if(pix_size == 2) + opt_LASX::resizeNN2_LASX(range, src, dst, x_ofs, ify); + else + opt_LASX::resizeNN4_LASX(range, src, dst, x_ofs, ify); + } + else #endif { resizeNNInvoker invoker(src, dst, x_ofs, ify); diff --git a/modules/imgproc/src/resize.hpp b/modules/imgproc/src/resize.hpp index 67cf5184af..1636e7585e 100644 --- a/modules/imgproc/src/resize.hpp +++ b/modules/imgproc/src/resize.hpp @@ -70,6 +70,15 @@ void resizeNN4_SSE4_1(const Range&, const Mat&, Mat&, int*, double); int VResizeLanczos4Vec_32f16u_SSE41(const float** src, ushort* dst, const float* beta, int width); #endif } + +namespace opt_LASX +{ +#if CV_TRY_LASX +void resizeNN2_LASX(const Range&, const Mat&, Mat&, int*, double); +void resizeNN4_LASX(const Range&, const Mat&, Mat&, int*, double); +#endif +} + } #endif /* End of file. */ diff --git a/modules/imgproc/src/resize.lasx.cpp b/modules/imgproc/src/resize.lasx.cpp new file mode 100644 index 0000000000..fece47087d --- /dev/null +++ b/modules/imgproc/src/resize.lasx.cpp @@ -0,0 +1,249 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +/* //////////////////////////////////////////////////////////////////// +// +// Geometrical transforms on images and matrices: rotation, zoom etc. +// +// */ + +#include "precomp.hpp" +#include "resize.hpp" +#include "opencv2/core/hal/intrin.hpp" + +namespace cv +{ +namespace opt_LASX +{ + +class resizeNNInvokerLASX4 CV_FINAL : + public ParallelLoopBody +{ +public: + resizeNNInvokerLASX4(const Mat& _src, Mat &_dst, int *_x_ofs, double _ify) : + ParallelLoopBody(), src(_src), dst(_dst), x_ofs(_x_ofs), + ify(_ify) + { + } + + virtual void operator() (const Range& range) const CV_OVERRIDE + { + Size ssize = src.size(), dsize = dst.size(); + int y, x; + int width = dsize.width; + int avxWidth = width - (width & 0x7); + if(((int64)(dst.data + dst.step) & 0x1f) == 0) + { + for(y = range.start; y < range.end; y++) + { + uchar* D = dst.data + dst.step*y; + uchar* Dstart = D; + int sy = std::min(cvFloor(y*ify), ssize.height-1); + const uchar* S = src.data + sy*src.step; +#ifdef CV_ICC +#pragma unroll(4) +#endif + for(x = 0; x < avxWidth; x += 8) + { + const __m256i CV_DECL_ALIGNED(64) *addr = (__m256i*)(x_ofs + x); + __m256i CV_DECL_ALIGNED(64) pixels = v256_lut_quads((schar *)S, (int *)addr).val; + __lasx_xvst(pixels, (int*)D, 0); + D += 32; + } + for(; x < width; x++) + { + *(int*)(Dstart + x*4) = *(int*)(S + x_ofs[x]); + } + } + } + else + { + for(y = range.start; y < range.end; y++) + { + uchar* D = dst.data + dst.step*y; + uchar* Dstart = D; + int sy = std::min(cvFloor(y*ify), ssize.height-1); + const uchar* S = src.data + sy*src.step; +#ifdef CV_ICC +#pragma unroll(4) +#endif + for(x = 0; x < avxWidth; x += 8) + { + const __m256i CV_DECL_ALIGNED(64) *addr = (__m256i*)(x_ofs + x); + __m256i CV_DECL_ALIGNED(64) pixels = v256_lut_quads((schar *)S, (int *)addr).val; + __lasx_xvst(pixels, (int*)D, 0); + D += 32; + } + for(; x < width; x++) + { + *(int*)(Dstart + x*4) = *(int*)(S + x_ofs[x]); + } + } + } + } + +private: + const Mat& src; + Mat& dst; + int* x_ofs; + double ify; + + resizeNNInvokerLASX4(const resizeNNInvokerLASX4&); + resizeNNInvokerLASX4& operator=(const resizeNNInvokerLASX4&); +}; + +class resizeNNInvokerLASX2 CV_FINAL : + public ParallelLoopBody +{ +public: + resizeNNInvokerLASX2(const Mat& _src, Mat &_dst, int *_x_ofs, double _ify) : + ParallelLoopBody(), src(_src), dst(_dst), x_ofs(_x_ofs), + ify(_ify) + { + } + + virtual void operator() (const Range& range) const CV_OVERRIDE + { + Size ssize = src.size(), dsize = dst.size(); + int y, x; + int width = dsize.width; + int avxWidth = width - (width & 0xf); + const __m256i CV_DECL_ALIGNED(64) shuffle_mask = _v256_set_b(15,14,11,10,13,12,9,8,7,6,3,2,5,4,1,0, + 15,14,11,10,13,12,9,8,7,6,3,2,5,4,1,0); + const __m256i CV_DECL_ALIGNED(64) permute_mask = _v256_set_w(7, 5, 3, 1, 6, 4, 2, 0); + if(((int64)(dst.data + dst.step) & 0x1f) == 0) + { + for(y = range.start; y < range.end; y++) + { + uchar* D = dst.data + dst.step*y; + uchar* Dstart = D; + int sy = std::min(cvFloor(y*ify), ssize.height-1); + const uchar* S = src.data + sy*src.step; + const uchar* S2 = S - 2; +#ifdef CV_ICC +#pragma unroll(4) +#endif + for(x = 0; x < avxWidth; x += 16) + { + const __m256i CV_DECL_ALIGNED(64) *addr = (__m256i*)(x_ofs + x); + __m256i CV_DECL_ALIGNED(64) pixels1 = v256_lut_quads((schar *)S, (int *)addr).val; + + const __m256i CV_DECL_ALIGNED(64) *addr2 = (__m256i*)(x_ofs + x + 8); + __m256i CV_DECL_ALIGNED(64) pixels2 = v256_lut_quads((schar *)S2, (int *)addr2).val; + + const __m256i h_mask = __lasx_xvreplgr2vr_w(0xFFFF0000); + __m256i CV_DECL_ALIGNED(64) unpacked = __lasx_xvbitsel_v(pixels1, pixels2, h_mask); + + __m256i CV_DECL_ALIGNED(64) bytes_shuffled = __lasx_xvshuf_b(unpacked, unpacked, shuffle_mask); + __m256i CV_DECL_ALIGNED(64) ints_permuted = __lasx_xvperm_w(bytes_shuffled, permute_mask); + __lasx_xvst(ints_permuted, (int*)D, 0); + D += 32; + } + for(; x < width; x++) + { + *(ushort*)(Dstart + x*2) = *(ushort*)(S + x_ofs[x]); + } + + } + } + else + { + for(y = range.start; y < range.end; y++) + { + uchar* D = dst.data + dst.step*y; + uchar* Dstart = D; + int sy = std::min(cvFloor(y*ify), ssize.height-1); + const uchar* S = src.data + sy*src.step; + const uchar* S2 = S - 2; +#ifdef CV_ICC +#pragma unroll(4) +#endif + for(x = 0; x < avxWidth; x += 16) + { + const __m256i CV_DECL_ALIGNED(64) *addr = (__m256i*)(x_ofs + x); + __m256i CV_DECL_ALIGNED(64) pixels1 = v256_lut_quads((schar *)S, (int *)addr).val; + + const __m256i CV_DECL_ALIGNED(64) *addr2 = (__m256i*)(x_ofs + x + 8); + __m256i CV_DECL_ALIGNED(64) pixels2 = v256_lut_quads((schar *)S2, (int *)addr2).val; + + const __m256i h_mask = __lasx_xvreplgr2vr_w(0xFFFF0000); + __m256i CV_DECL_ALIGNED(64) unpacked = __lasx_xvbitsel_v(pixels1, pixels2, h_mask); + + __m256i CV_DECL_ALIGNED(64) bytes_shuffled = __lasx_xvshuf_b(unpacked, unpacked, shuffle_mask); + __m256i CV_DECL_ALIGNED(64) ints_permuted = __lasx_xvperm_w(bytes_shuffled, permute_mask); + __lasx_xvst(ints_permuted, (int*)D, 0); + D += 32; + } + for(; x < width; x++) + { + *(ushort*)(Dstart + x*2) = *(ushort*)(S + x_ofs[x]); + } + } + } + } + +private: + const Mat& src; + Mat& dst; + int* x_ofs; + double ify; + + resizeNNInvokerLASX2(const resizeNNInvokerLASX2&); + resizeNNInvokerLASX2& operator=(const resizeNNInvokerLASX2&); +}; + +void resizeNN2_LASX(const Range& range, const Mat& src, Mat &dst, int *x_ofs, double ify) +{ + resizeNNInvokerLASX2 invoker(src, dst, x_ofs, ify); + parallel_for_(range, invoker, dst.total() / (double)(1 << 16)); +} + +void resizeNN4_LASX(const Range& range, const Mat& src, Mat &dst, int *x_ofs, double ify) +{ + resizeNNInvokerLASX4 invoker(src, dst, x_ofs, ify); + parallel_for_(range, invoker, dst.total() / (double)(1 << 16)); +} + +} +} +/* End of file. */ From 619e038de97906ace6fbc555e257ddae65cae6a5 Mon Sep 17 00:00:00 2001 From: Markus Heck Date: Sat, 10 Sep 2022 17:40:31 +0200 Subject: [PATCH 068/313] move tutorial to imgproc and example to ImgTrans --- .../generalized_hough_ballard_guil.markdown | 13 +++++++------ .../images/generalized_hough_image.jpg | Bin .../generalized_hough_less_perfect_result_img.jpg | Bin .../images/generalized_hough_mini_image.jpg | Bin .../images/generalized_hough_mini_template.jpg | Bin .../images/generalized_hough_result_img.jpg | Bin .../images/generalized_hough_template.jpg | Bin .../imgtrans/hough_circle/hough_circle.markdown | 2 +- .../imgproc/imgtrans/remap/remap.markdown | 2 +- .../imgproc/table_of_content_imgproc.markdown | 10 ++++++++++ .../objdetect/table_of_content_objdetect.markdown | 10 ---------- doc/tutorials/objdetect/traincascade.markdown | 1 - .../generalizedHoughTransform.cpp | 0 13 files changed, 19 insertions(+), 19 deletions(-) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown (81%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_image.jpg (100%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg (100%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg (100%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg (100%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg (100%) rename doc/tutorials/{objdetect => imgproc}/generalized_hough_ballard_guil/images/generalized_hough_template.jpg (100%) rename samples/cpp/tutorial_code/{objectDetection => ImgTrans}/generalizedHoughTransform.cpp (100%) diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown b/doc/tutorials/imgproc/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown similarity index 81% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown index c5dc2590af..edf18b3844 100644 --- a/doc/tutorials/objdetect/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown +++ b/doc/tutorials/imgproc/generalized_hough_ballard_guil/generalized_hough_ballard_guil.markdown @@ -3,7 +3,8 @@ Object detection with Generalized Ballard and Guil Hough Transform {#tutorial_ge @tableofcontents -@prev_tutorial{tutorial_traincascade} +@prev_tutorial{tutorial_hough_circle} +@next_tutorial{tutorial_remap} Goal ---- @@ -39,14 +40,14 @@ Example ### Code The complete code for this tutorial is shown below. -@include samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp +@include samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp Explanation ----------- ### Load image, template and setup variables -@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-load-and-setup +@snippet samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp generalized-hough-transform-load-and-setup The position vectors will contain the matches the detectors will find. Every entry contains four floating point values: @@ -61,19 +62,19 @@ An example could look as follows: `[200, 100, 0.9, 120]` ### Setup parameters -@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-setup-parameters +@snippet samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp generalized-hough-transform-setup-parameters Finding the optimal values can end up in trial and error and depends on many factors, such as the image resolution. ### Run detection -@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-run +@snippet samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp generalized-hough-transform-run As mentioned above, this step will take some time, especially with larger images and when using Guil. ### Draw results and show image -@snippet samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp generalized-hough-transform-draw-results +@snippet samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp generalized-hough-transform-draw-results Result ------ diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_image.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_image.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_image.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_image.jpg diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_less_perfect_result_img.jpg diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_mini_image.jpg diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_mini_template.jpg diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_result_img.jpg diff --git a/doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_template.jpg b/doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_template.jpg similarity index 100% rename from doc/tutorials/objdetect/generalized_hough_ballard_guil/images/generalized_hough_template.jpg rename to doc/tutorials/imgproc/generalized_hough_ballard_guil/images/generalized_hough_template.jpg diff --git a/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.markdown b/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.markdown index 497215794b..0a2ba8843e 100644 --- a/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.markdown +++ b/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.markdown @@ -2,7 +2,7 @@ Hough Circle Transform {#tutorial_hough_circle} ====================== @prev_tutorial{tutorial_hough_lines} -@next_tutorial{tutorial_remap} +@next_tutorial{tutorial_generalized_hough_ballard_guil} Goal ---- diff --git a/doc/tutorials/imgproc/imgtrans/remap/remap.markdown b/doc/tutorials/imgproc/imgtrans/remap/remap.markdown index ac2b879171..eb34be4bb1 100644 --- a/doc/tutorials/imgproc/imgtrans/remap/remap.markdown +++ b/doc/tutorials/imgproc/imgtrans/remap/remap.markdown @@ -1,7 +1,7 @@ Remapping {#tutorial_remap} ========= -@prev_tutorial{tutorial_hough_circle} +@prev_tutorial{tutorial_generalized_hough_ballard_guil} @next_tutorial{tutorial_warp_affine} Goal diff --git a/doc/tutorials/imgproc/table_of_content_imgproc.markdown b/doc/tutorials/imgproc/table_of_content_imgproc.markdown index b0a8b8260b..9545cd2155 100644 --- a/doc/tutorials/imgproc/table_of_content_imgproc.markdown +++ b/doc/tutorials/imgproc/table_of_content_imgproc.markdown @@ -173,6 +173,16 @@ In this section you will learn about the image processing (manipulation) functio Where we learn how to detect circles +- @subpage tutorial_generalized_hough_ballard_guil + + *Languages:* C++ + + *Compatibility:* \>= OpenCV 3.4 + + *Author:* Markus Heck + + Detect an object in a picture with the help of GeneralizedHoughBallard and GeneralizedHoughGuil. + - @subpage tutorial_remap *Languages:* C++, Java, Python diff --git a/doc/tutorials/objdetect/table_of_content_objdetect.markdown b/doc/tutorials/objdetect/table_of_content_objdetect.markdown index 9b8a09b4e8..0b019d88a5 100644 --- a/doc/tutorials/objdetect/table_of_content_objdetect.markdown +++ b/doc/tutorials/objdetect/table_of_content_objdetect.markdown @@ -16,13 +16,3 @@ Ever wondered how your digital camera detects peoples and faces? Look here to fi - @subpage tutorial_traincascade This tutorial describes _opencv_traincascade_ application and its parameters. - -- @subpage tutorial_generalized_hough_ballard_guil - - *Languages:* C++ - - *Compatibility:* \>= OpenCV 3.4 - - *Author:* Markus Heck - - Detect an object in a picture with the help of GeneralizedHoughBallard and GeneralizedHoughGuil. \ No newline at end of file diff --git a/doc/tutorials/objdetect/traincascade.markdown b/doc/tutorials/objdetect/traincascade.markdown index 7ff39b5a90..1528c02211 100644 --- a/doc/tutorials/objdetect/traincascade.markdown +++ b/doc/tutorials/objdetect/traincascade.markdown @@ -2,7 +2,6 @@ Cascade Classifier Training {#tutorial_traincascade} =========================== @prev_tutorial{tutorial_cascade_classifier} -@next_tutorial{tutorial_generalized_hough_ballard_guil} Introduction diff --git a/samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp b/samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp similarity index 100% rename from samples/cpp/tutorial_code/objectDetection/generalizedHoughTransform.cpp rename to samples/cpp/tutorial_code/ImgTrans/generalizedHoughTransform.cpp From cb1a1e9a5131614bce46463faa2aa95dbeddb80b Mon Sep 17 00:00:00 2001 From: Christine Poerschke <6458642+cpoerschke@users.noreply.github.com> Date: Sun, 11 Sep 2022 16:40:27 +0100 Subject: [PATCH 069/313] add explicit onChange callback in cocoa cvSetTrackbarPos --- modules/highgui/src/window_cocoa.mm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index ef3f828cf8..3f46d47de7 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -451,6 +451,9 @@ CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]]; if(slider) { [[slider slider] setIntValue:pos]; + if([slider respondsToSelector:@selector(handleSlider)]) { + [slider performSelector:@selector(handleSlider)]; + } } } [localpool5 drain]; @@ -1187,7 +1190,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { [slider setMaxValue:100]; [slider setContinuous:YES]; [slider setTarget:self]; - [slider setAction:@selector(sliderChanged:)]; + [slider setAction:@selector(handleSliderNotification:)]; [self addSubview:slider]; [self setAutoresizingMask:NSViewWidthSizable]; @@ -1197,8 +1200,12 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { return self; } -- (void)sliderChanged:(NSNotification *)notification { +- (void)handleSliderNotification:(NSNotification *)notification { (void)notification; + [self handleSlider]; +} + +- (void)handleSlider { int pos = [slider intValue]; NSString *temp = [self initialName]; NSString *text = [NSString stringWithFormat:@"%@ %d", temp, pos]; From 7fc14504b1f162e0253169bc94b13d8fc64206ef Mon Sep 17 00:00:00 2001 From: ocpalo <44481097+ocpalo@users.noreply.github.com> Date: Sun, 11 Sep 2022 23:45:40 +0300 Subject: [PATCH 070/313] Update 3rdparty readme.txt for spng --- 3rdparty/readme.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/3rdparty/readme.txt b/3rdparty/readme.txt index e67304c5ef..28bac17d7a 100644 --- a/3rdparty/readme.txt +++ b/3rdparty/readme.txt @@ -27,6 +27,13 @@ libpng Portable Network Graphics library. for details and links to the source code WITH_PNG CMake option must be ON to add libpng support to imgcodecs. + +libspng Portable Network Graphics library. + The license and copyright notes can be found in libspng/LICENSE. + See libspng home page https://www.libspng.org + for details and links to the source code + + WITH_SPNG CMake option must be ON to add libspng support to imgcodecs ------------------------------------------------------------------------------------ libtiff Tag Image File Format (TIFF) Software Copyright (c) 1988-1997 Sam Leffler From 46d988e2cb98f55bdcd8713c21a3df6c83087ecc Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Mon, 12 Sep 2022 07:12:28 +0100 Subject: [PATCH 071/313] Merge pull request #22248 from cudawarped:ffmpeg_rtsp_low_fps * Allow the number of threads FFMpeg uses to be selected during VideoCapture::open(). Reset interupt timer in grab if err = avformat_find_stream_info(ic, NULL); is interupted but open is successful. * Correct the returned number of threads and amend test cases. * Update container test case. * Reverse changes added to existing videoio_container test case and include test combining thread change and raw read in the newly added videoio_read test case. --- modules/videoio/include/opencv2/videoio.hpp | 1 + modules/videoio/src/cap_ffmpeg_impl.hpp | 12 +++++- modules/videoio/test/test_ffmpeg.cpp | 48 +++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index bbe392266f..a7fbe36544 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -204,6 +204,7 @@ enum VideoCaptureProperties { CAP_PROP_LRF_HAS_KEY_FRAME = 67, //!< FFmpeg back-end only - Indicates whether the Last Raw Frame (LRF), output from VideoCapture::read() when VideoCapture is initialized with VideoCapture::open(CAP_FFMPEG, {CAP_PROP_FORMAT, -1}) or VideoCapture::set(CAP_PROP_FORMAT,-1) is called before the first call to VideoCapture::read(), contains encoded data for a key frame. CAP_PROP_CODEC_EXTRADATA_INDEX = 68, //!< Positive index indicates that returning extra data is supported by the video back end. This can be retrieved as cap.retrieve(data, ). E.g. When reading from a h264 encoded RTSP stream, the FFmpeg backend could return the SPS and/or PPS if available (if sent in reply to a DESCRIBE request), from calls to cap.retrieve(data, ). CAP_PROP_FRAME_TYPE = 69, //!< (read-only) FFmpeg back-end only - Frame type ascii code (73 = 'I', 80 = 'P', 66 = 'B' or 63 = '?' if unknown) of the most recently read frame. + CAP_PROP_N_THREADS = 70, //!< (**open-only**) Set the maximum number of threads to use. Use 0 to use as many threads as CPU cores (applicable for FFmpeg back-end only). #ifndef CV_DOXYGEN CV__CAP_PROP_LATEST #endif diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index caeae31e1f..c25ab0c40a 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -987,7 +987,8 @@ inline void fill_codec_context(AVCodecContext * enc, AVDictionary * dict) //#ifdef FF_API_THREAD_INIT // avcodec_thread_init(enc, get_number_of_cpus()); //#else - enc->thread_count = get_number_of_cpus(); + const int nCpus = get_number_of_cpus(); + enc->thread_count = enc->thread_count ? enc->thread_count: nCpus; //#endif AVDictionaryEntry* avdiscard_entry = av_dict_get(dict, "avdiscard", NULL, 0); @@ -1024,6 +1025,7 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& unsigned i; bool valid = false; + int nThreads = 0; close(); @@ -1081,6 +1083,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& read_timeout = params.get(CAP_PROP_READ_TIMEOUT_MSEC); } #endif + if (params.has(CAP_PROP_N_THREADS)) + { + nThreads = params.get(CAP_PROP_N_THREADS); + } if (params.warnUnusedParameters()) { CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: unsupported parameters in .open(), see logger INFO channel for details. Bailout"); @@ -1248,6 +1254,7 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& #endif continue; } + context->thread_count = nThreads; fill_codec_context(context, dict); #ifdef CV_FFMPEG_CODECPAR avcodec_parameters_to_context(context, par); @@ -1444,6 +1451,7 @@ bool CvCapture_FFMPEG::grabFrame() #if USE_AV_INTERRUPT_CALLBACK // activate interrupt callback + interrupt_metadata.timeout = 0; get_monotonic_time(&interrupt_metadata.value); interrupt_metadata.timeout_after_ms = read_timeout; #endif @@ -1774,6 +1782,8 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const case CAP_PROP_STREAM_OPEN_TIME_USEC: //ic->start_time_realtime is in microseconds return ((double)ic->start_time_realtime); + case CAP_PROP_N_THREADS: + return static_cast(context->thread_count); default: break; } diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 6f0f2f28b4..3578637cdb 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -95,6 +95,54 @@ TEST(videoio_ffmpeg, image) //========================================================================== +#define THREADS testing::ValuesIn({ 0,1,2,2000 }) +#define RAW_READ testing::ValuesIn({true, false}) +typedef tuple videoio_read_params_t; +typedef testing::TestWithParam< testing::tuple> videoio_read; + +TEST_P(videoio_read, threads) +{ + const VideoCaptureAPIs api = CAP_FFMPEG; + if (!videoio_registry::hasBackend(api)) + throw SkipTestException("Backend was not found"); + const string fileName = get<0>(get<0>(GetParam())); + const int nFrames = get<1>(get<0>(GetParam())); + const bool fixedThreadCount = get<2>(get<0>(GetParam())); + const int nThreads = get<1>(GetParam()); + const bool rawRead = get<2>(GetParam()); + VideoCapture cap(findDataFile(fileName), api, { CAP_PROP_N_THREADS, nThreads }); + if (!cap.isOpened()) + throw SkipTestException("Video stream is not supported"); + if (nThreads == 0 || fixedThreadCount) + EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), VideoCapture(findDataFile(fileName), api).get(CAP_PROP_N_THREADS)); + else + EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), nThreads); + if (rawRead && !cap.set(CAP_PROP_FORMAT, -1)) // turn off video decoder (extract stream) + throw SkipTestException("Fetching of RAW video streams is not supported"); + Mat frame; + int n = 0; + while (cap.read(frame)) { + ASSERT_FALSE(frame.empty()); + n++; + } + ASSERT_EQ(n, nFrames); +} + +const videoio_read_params_t videoio_read_params[] = +{ + videoio_read_params_t("video/big_buck_bunny.h264", 125, false), + //videoio_read_params_t("video/big_buck_bunny.h265", 125, false), + videoio_read_params_t("video/big_buck_bunny.mjpg.avi", 125, true), + //videoio_read_params_t("video/big_buck_bunny.mov", 125, false), + //videoio_read_params_t("video/big_buck_bunny.mp4", 125, false), + //videoio_read_params_t("video/big_buck_bunny.mpg", 125, false), + //videoio_read_params_t("video/big_buck_bunny.wmv", 125, true), +}; + +INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params), THREADS, RAW_READ)); + +//========================================================================== + typedef tuple videoio_container_params_t; typedef testing::TestWithParam< videoio_container_params_t > videoio_container; From 2eff70fbf41b4224681ed96e65b4dc520709eb3b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 11 Sep 2022 21:40:22 +0000 Subject: [PATCH 072/313] ffmpeg/4.x: update FFmpeg wrapper 2022.09 - FFmpeg 4.4.2 - added AV1 support hrough aom 3.4.0: https://aomedia.googlesource.com/aom/+/refs/tags/v3.4.0 - use Ubuntu 18.04 => 20.04 as build image --- 3rdparty/ffmpeg/ffmpeg.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/3rdparty/ffmpeg/ffmpeg.cmake b/3rdparty/ffmpeg/ffmpeg.cmake index 6dcb24db6b..dabab5911f 100644 --- a/3rdparty/ffmpeg/ffmpeg.cmake +++ b/3rdparty/ffmpeg/ffmpeg.cmake @@ -1,8 +1,8 @@ -# Binaries branch name: ffmpeg/4.x_20220524 -# Binaries were created for OpenCV: d6e9616256b46bd59be0a93d397f6ab958d39cd2 -ocv_update(FFMPEG_BINARIES_COMMIT "65ec04d4573dcdfa4531f0b9e67f35d8ffff873e") -ocv_update(FFMPEG_FILE_HASH_BIN32 "5573e2262ad1298e603122b7759fc2f6") -ocv_update(FFMPEG_FILE_HASH_BIN64 "5f9e2b2e04c15f080f40e844de80c867") +# Binaries branch name: ffmpeg/4.x_20220912 +# Binaries were created for OpenCV: 4154bd06677c30475e2545cbee05906dd9a367cb +ocv_update(FFMPEG_BINARIES_COMMIT "524023e38e27649d4f5ce97d57ceb8864c838fb6") +ocv_update(FFMPEG_FILE_HASH_BIN32 "88f87420899e07151b682a76e30d3e01") +ocv_update(FFMPEG_FILE_HASH_BIN64 "81b1e1e9fd2a10f4ec7b239c743240fe") ocv_update(FFMPEG_FILE_HASH_CMAKE "8862c87496e2e8c375965e1277dee1c7") function(download_win_ffmpeg script_var) From fb3fc5322c0cfb109047e4dbbbfb250f2e61019a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 12 Sep 2022 13:09:40 +0000 Subject: [PATCH 073/313] videoio(ffmpeg): update tests with new Windows wrapper --- modules/videoio/test/test_ffmpeg.cpp | 10 ---------- modules/videoio/test/test_video_io.cpp | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 6f0f2f28b4..d2c8e4c432 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -413,10 +413,6 @@ TEST_P(ffmpeg_get_fourcc, check_short_codecs) if (!cap.isOpened()) throw SkipTestException("Video stream is not supported"); const double fourcc = cap.get(CAP_PROP_FOURCC); -#ifdef _WIN32 // handle old FFmpeg backend - if(!fourcc && fileName == "../cv/tracking/faceocc2/data/faceocc2.webm") - throw SkipTestException("Feature not yet supported by Windows FFmpeg shared library!"); -#endif ASSERT_EQ(fourcc_string, fourccToString((int)fourcc)); } @@ -443,10 +439,8 @@ TEST(videoio, mp4_orientation_meta_auto) EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl; -#ifndef _WIN32 // TODO: FFmpeg wrapper update // related issue: https://github.com/opencv/opencv/issues/22088 EXPECT_EQ(90, cap.get(CAP_PROP_ORIENTATION_META)); -#endif cap.set(CAP_PROP_ORIENTATION_AUTO, true); if (cap.get(CAP_PROP_ORIENTATION_AUTO) == 0) @@ -521,10 +515,6 @@ TEST(videoio_ffmpeg, ffmpeg_check_extra_data) EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); ASSERT_TRUE(cap.isOpened()) << "Can't open the video"; const int codecExtradataIdx = (int)cap.get(CAP_PROP_CODEC_EXTRADATA_INDEX); -#ifdef _WIN32 // handle old FFmpeg backend - if (codecExtradataIdx <= 0) - throw SkipTestException("Codec extra data is not supported by backend or video stream"); -#endif Mat data; ASSERT_TRUE(cap.retrieve(data, codecExtradataIdx)); EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type()); diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 6661b13c5a..7e64a29aff 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -771,8 +771,8 @@ static const VideoCaptureAccelerationInput hw_filename[] = { { "sample_322x242_15frames.yuv420p.libxvid.mp4", 28.0 }, { "sample_322x242_15frames.yuv420p.mjpeg.mp4", 20.0 }, { "sample_322x242_15frames.yuv420p.mpeg2video.mp4", 24.0 }, // GSTREAMER on Ubuntu 18.04 - { "sample_322x242_15frames.yuv420p.libx264.mp4", 24.0 }, // GSTREAMER on Ubuntu 18.04 - { "sample_322x242_15frames.yuv420p.libx265.mp4", 30.0 }, + { "sample_322x242_15frames.yuv420p.libx264.mp4", 23.0 }, // D3D11 on GHA/Windows, GSTREAMER on Ubuntu 18.04 + { "sample_322x242_15frames.yuv420p.libx265.mp4", 23.0 }, // D3D11 on GHA/Windows { "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", 30.0 }, { "sample_322x242_15frames.yuv420p.libaom-av1.mp4", 30.0 } }; From bcc19a622d8fd9db60cf9a4d45acf491b86198ca Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 12 Sep 2022 17:54:00 +0100 Subject: [PATCH 074/313] Replace MFX major version assertion to warning --- modules/gapi/src/streaming/onevpl/source_priv.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/gapi/src/streaming/onevpl/source_priv.cpp b/modules/gapi/src/streaming/onevpl/source_priv.cpp index e8d26b41e2..c2762d0079 100644 --- a/modules/gapi/src/streaming/onevpl/source_priv.cpp +++ b/modules/gapi/src/streaming/onevpl/source_priv.cpp @@ -227,16 +227,15 @@ GSource::Priv::Priv(std::shared_ptr provider, // TODO Add factory static method in ProcessingEngineBase if (mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION) { - GAPI_Assert(false && + GAPI_LOG_WARNING(NULL, "GSource mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION" - " - is not implemented"); + " - is not implemented. G-API only supports an older version of OneVPL API."); + } + const auto& transcode_params = VPLLegacyTranscodeEngine::get_vpp_params(preferred_params); + if (!transcode_params.empty()) { + engine.reset(new VPLLegacyTranscodeEngine(std::move(acceleration))); } else { - const auto& transcode_params = VPLLegacyTranscodeEngine::get_vpp_params(preferred_params); - if (!transcode_params.empty()) { - engine.reset(new VPLLegacyTranscodeEngine(std::move(acceleration))); - } else { - engine.reset(new VPLLegacyDecodeEngine(std::move(acceleration))); - } + engine.reset(new VPLLegacyDecodeEngine(std::move(acceleration))); } } From d717de571900258dac325fd0c29e39d9b7f45ca0 Mon Sep 17 00:00:00 2001 From: ocpalo Date: Fri, 5 Aug 2022 23:13:06 +0300 Subject: [PATCH 075/313] nasm/simd support for libjpeg-turbo --- 3rdparty/libjpeg-turbo/CMakeLists.txt | 114 +++++++++++++++++- .../libjpeg-turbo/src/simd/CMakeLists.txt | 25 ++-- CMakeLists.txt | 10 ++ modules/imgcodecs/perf/perf_jpeg.cpp | 38 ++++++ 4 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 modules/imgcodecs/perf/perf_jpeg.cpp diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt index 4dd3095f94..e316e24822 100644 --- a/3rdparty/libjpeg-turbo/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/CMakeLists.txt @@ -15,8 +15,55 @@ endif() message(STATUS "libjpeg-turbo: VERSION = ${VERSION}, BUILD = ${BUILD}") +math(EXPR BITS "${CMAKE_SIZEOF_VOID_P} * 8") +string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR_LC) + +if(CMAKE_SYSTEM_PROCESSOR_LC MATCHES "x86_64" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "amd64" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "i[0-9]86" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "x86" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "ia32") + if(BITS EQUAL 64 OR CMAKE_C_COMPILER_ABI MATCHES "ELF X32") + set(CPU_TYPE x86_64) + else() + set(CPU_TYPE i386) + endif() + if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL ${CPU_TYPE}) + set(CMAKE_SYSTEM_PROCESSOR ${CPU_TYPE}) + endif() +elseif(CMAKE_SYSTEM_PROCESSOR_LC STREQUAL "aarch64" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "^arm") + if(BITS EQUAL 64) + set(CPU_TYPE arm64) + else() + set(CPU_TYPE arm) + endif() +elseif(CMAKE_SYSTEM_PROCESSOR_LC MATCHES "^ppc" OR + CMAKE_SYSTEM_PROCESSOR_LC MATCHES "^powerpc") + set(CPU_TYPE powerpc) +else() + set(CPU_TYPE ${CMAKE_SYSTEM_PROCESSOR_LC}) +endif() +if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR + CMAKE_OSX_ARCHITECTURES MATCHES "arm64" OR + CMAKE_OSX_ARCHITECTURES MATCHES "i386") + set(CPU_TYPE ${CMAKE_OSX_ARCHITECTURES}) +endif() +if(CMAKE_OSX_ARCHITECTURES MATCHES "ppc") + set(CPU_TYPE powerpc) +endif() +if(MSVC_IDE AND CMAKE_GENERATOR_PLATFORM MATCHES "arm64") + set(CPU_TYPE arm64) +endif() + +message(STATUS "${BITS}-bit build (${CPU_TYPE})") + +OCV_OPTION(ENABLE_LIBJPEG_TURBO_SIMD "Include SIMD extensions for libjpeg-turbo, if available for this platform" (NOT CV_DISABLE_OPTIMIZATION) + VISIBLE_IF BUILD_JPEG) option(WITH_ARITH_ENC "Include arithmetic encoding support when emulating the libjpeg v6b API/ABI" TRUE) option(WITH_ARITH_DEC "Include arithmetic decoding support when emulating the libjpeg v6b API/ABI" TRUE) +set(WITH_SIMD 1) +set(IS_LIBJPEG_TURBO_SIMD_AVAILABLE 0 PARENT_SCOPE) include(CheckCSourceCompiles) include(CheckIncludeFiles) @@ -99,12 +146,73 @@ if(WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jdarith.c) endif() -# No SIMD -set(JPEG_SOURCES ${JPEG_SOURCES} jsimd_none.c) +if(MSVC) + option(WITH_CRT_DLL + "Link all ${CMAKE_PROJECT_NAME} libraries and executables with the C run-time DLL (msvcr*.dll) instead of the static C run-time library (libcmt*.lib.) The default is to use the C run-time DLL only with the libraries and executables that need it." + FALSE) + if(NOT WITH_CRT_DLL) + # Use the static C library for all build types + foreach(var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + if(${var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}") + endif() + endforeach() + endif() + add_definitions(-D_CRT_NONSTDC_NO_WARNINGS) +endif() + +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + # Use the maximum optimization level for release builds + foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO) + if(${var} MATCHES "-O2") + string(REGEX REPLACE "-O2" "-O3" ${var} "${${var}}") + endif() + endforeach() +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + if(CMAKE_C_COMPILER_ID MATCHES "SunPro") + # Use the maximum optimization level for release builds + foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO) + if(${var} MATCHES "-xO3") + string(REGEX REPLACE "-xO3" "-xO5" ${var} "${${var}}") + endif() + if(${var} MATCHES "-xO2") + string(REGEX REPLACE "-xO2" "-xO5" ${var} "${${var}}") + endif() + endforeach() + endif() +endif() + +message(STATUS ${WITH_LIBJPEG_TURBO_SIMD}) +if(ENABLE_LIBJPEG_TURBO_SIMD) + add_subdirectory(src/simd) + if(NEON_INTRINSICS) + add_definitions(-DNEON_INTRINSICS) + endif() +else() + set(WITH_SIMD 0) +endif() + +if(WITH_SIMD) + message(STATUS "SIMD extensions: ${CPU_TYPE} (WITH_SIMD = ${WITH_SIMD})") + set(IS_LIBJPEG_TURBO_SIMD_AVAILABLE 1 PARENT_SCOPE) + if(MSVC_IDE OR XCODE) + set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1) + endif() +else() + add_library(simd OBJECT src/jsimd_none.c) + if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)) + set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + endif() +endif() ocv_list_add_prefix(JPEG_SOURCES src/) -add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${JPEG_SOURCES} ${SIMD_OBJS}) +set(JPEG_SOURCES ${JPEG_SOURCES} ${SIMD_OBJS}) + +add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${JPEG_SOURCES} $ ${SIMD_OBJS}) set_target_properties(${JPEG_LIBRARY} PROPERTIES OUTPUT_NAME ${JPEG_LIBRARY} diff --git a/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt b/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt index 8521e42b44..db85d9f443 100644 --- a/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt @@ -1,9 +1,13 @@ macro(simd_fail message) - if(REQUIRE_SIMD) - message(FATAL_ERROR "${message}.") - else() message(WARNING "${message}. Performance will suffer.") set(WITH_SIMD 0 PARENT_SCOPE) +endmacro() + +macro(boolean_number var) + if(${var}) + set(${var} 1 ${ARGN}) + else() + set(${var} 0 ${ARGN}) endif() endmacro() @@ -41,14 +45,14 @@ elseif(CPU_TYPE STREQUAL "i386") endif() endif() -if(NOT REQUIRE_SIMD) - include(CheckLanguage) - check_language(ASM_NASM) - if(NOT CMAKE_ASM_NASM_COMPILER) - simd_fail("SIMD extensions disabled: could not find NASM compiler") - return() - endif() + +include(CheckLanguage) +check_language(ASM_NASM) +if(NOT CMAKE_ASM_NASM_COMPILER) + simd_fail("SIMD extensions disabled: could not find NASM compiler") + return() endif() + enable_language(ASM_NASM) message(STATUS "CMAKE_ASM_NASM_COMPILER = ${CMAKE_ASM_NASM_COMPILER}") @@ -224,6 +228,7 @@ elseif(CPU_TYPE STREQUAL "arm64" OR CPU_TYPE STREQUAL "arm") # following test determines whether -mfloat-abi=softfp should be explicitly # added to the compile flags for the intrinsics implementation of the Neon SIMD # extensions. + if(BITS EQUAL 32) check_c_source_compiles(" #if defined(__ARM_NEON__) || (!defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43b6023242..129cf0aae2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1341,6 +1341,16 @@ if(WITH_JPEG OR HAVE_JPEG) status(" JPEG:" NO) elseif(BUILD_JPEG) status(" JPEG:" "build-${JPEG_LIBRARY} (ver ${JPEG_LIB_VERSION})") + if(ENABLE_LIBJPEG_TURBO_SIMD) + status(" SIMD Support Request:" "YES") + if(IS_LIBJPEG_TURBO_SIMD_AVAILABLE) + status(" SIMD Support:" "YES") + else() + status(" SIMD Support:" "NO") + endif() + else() + status(" SIMD Support Request:" "NO") + endif() else() status(" JPEG:" "${JPEG_LIBRARY} (ver ${JPEG_LIB_VERSION})") endif() diff --git a/modules/imgcodecs/perf/perf_jpeg.cpp b/modules/imgcodecs/perf/perf_jpeg.cpp new file mode 100644 index 0000000000..694e2e698e --- /dev/null +++ b/modules/imgcodecs/perf/perf_jpeg.cpp @@ -0,0 +1,38 @@ +// 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 "perf_precomp.hpp" + +namespace opencv_test +{ +using namespace perf; + +PERF_TEST(JPEG, Decode) +{ + String filename = getDataPath("stitching/boat1.jpg"); + + FILE *f = fopen(filename.c_str(), "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + vector file_buf((size_t)len); + EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f)); + fclose(f); f = NULL; + + TEST_CYCLE() imdecode(file_buf, IMREAD_UNCHANGED); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST(JPEG, Encode) +{ + String filename = getDataPath("stitching/boat1.jpg"); + cv::Mat src = imread(filename); + + vector buf; + TEST_CYCLE() imencode(".jpg", src, buf); + + SANITY_CHECK_NOTHING(); +} + +} // namespace \ No newline at end of file From 1be40554af523f18640a3f33495d43de9f35ecfd Mon Sep 17 00:00:00 2001 From: "xiang.zhang" Date: Tue, 13 Sep 2022 11:10:39 +0800 Subject: [PATCH 076/313] Disable default path while search libtim-vx.so with TIMVX_INSTALL_DIR Signed-off-by: xiang.zhang --- cmake/OpenCVFindTIMVX.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCVFindTIMVX.cmake b/cmake/OpenCVFindTIMVX.cmake index 339f726bd9..ce084e45fb 100644 --- a/cmake/OpenCVFindTIMVX.cmake +++ b/cmake/OpenCVFindTIMVX.cmake @@ -30,7 +30,7 @@ if(TIMVX_INSTALL_DIR AND NOT BUILD_TIMVX) set(BUILD_TIMVX OFF) set(TIMVX_INC_DIR "${TIMVX_INSTALL_DIR}/include" CACHE INTERNAL "TIM-VX include directory") - find_library(TIMVX_LIB "tim-vx" PATHS "${TIMVX_INSTALL_DIR}/lib") + find_library(TIMVX_LIB "tim-vx" PATHS "${TIMVX_INSTALL_DIR}/lib" NO_DEFAULT_PATH) if(TIMVX_LIB) set(TIMVX_FOUND ON) else() From 5ea912e7784bde39925617e3553de2cbb5077a93 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Tue, 13 Sep 2022 08:28:34 +0100 Subject: [PATCH 077/313] Change warning message --- modules/gapi/src/streaming/onevpl/source_priv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gapi/src/streaming/onevpl/source_priv.cpp b/modules/gapi/src/streaming/onevpl/source_priv.cpp index c2762d0079..bd74ca0a25 100644 --- a/modules/gapi/src/streaming/onevpl/source_priv.cpp +++ b/modules/gapi/src/streaming/onevpl/source_priv.cpp @@ -229,7 +229,7 @@ GSource::Priv::Priv(std::shared_ptr provider, if (mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION) { GAPI_LOG_WARNING(NULL, "GSource mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION" - " - is not implemented. G-API only supports an older version of OneVPL API."); + " - is not implemented. Rollback to MFX implementation"); } const auto& transcode_params = VPLLegacyTranscodeEngine::get_vpp_params(preferred_params); if (!transcode_params.empty()) { From fce8349c9990c50a9efd7649f38ef12122b6ffd1 Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Wed, 20 Apr 2022 14:13:02 +0800 Subject: [PATCH 078/313] Optimize the cvCeil and cvFloor functions. This patch optimizes the cvCeil and cvFloor functions on the LoongArch platform. Signed-off-by: Hao Chen --- .../core/include/opencv2/core/fast_math.hpp | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/fast_math.hpp b/modules/core/include/opencv2/core/fast_math.hpp index cd5de0b546..9ee7dba672 100644 --- a/modules/core/include/opencv2/core/fast_math.hpp +++ b/modules/core/include/opencv2/core/fast_math.hpp @@ -128,8 +128,12 @@ #define CV_INLINE_ISNAN_FLT(value) CV_INLINE_ISNAN_DBL(value) #endif - #if !defined(OPENCV_USE_FASTMATH_BUILTINS) && \ - (defined __GNUC__ || defined __clang__ || defined _MSC_VER) + #if !defined(OPENCV_USE_FASTMATH_BUILTINS) \ + && ( \ + defined(__x86_64__) || defined(__i686__) \ + || defined(__arm__) \ + || defined(__PPC64__) \ + ) /* Let builtin C math functions when available. Dedicated hardware is available to round and convert FP values. */ #define OPENCV_USE_FASTMATH_BUILTINS 1 @@ -229,6 +233,15 @@ CV_INLINE int cvFloor( double value ) #if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \ defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS return (int)__builtin_floor(value); +#elif defined __loongarch64 + int i; + double tmp; + __asm__ ("ftintrm.l.d %[tmp], %[in] \n\t" + "movfr2gr.d %[i], %[tmp] \n\t" + : [i] "=r" (i), [tmp] "=f" (tmp) + : [in] "f" (value) + :); + return i; #else int i = (int)value; return i - (i > value); @@ -247,6 +260,15 @@ CV_INLINE int cvCeil( double value ) #if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \ defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS return (int)__builtin_ceil(value); +#elif defined __loongarch64 + int i; + double tmp; + __asm__ ("ftintrp.l.d %[tmp], %[in] \n\t" + "movfr2gr.d %[i], %[tmp] \n\t" + : [i] "=r" (i), [tmp] "=f" (tmp) + : [in] "f" (value) + :); + return i; #else int i = (int)value; return i + (i < value); @@ -281,7 +303,7 @@ CV_INLINE int cvIsInf( double value ) { #if defined CV_INLINE_ISINF_DBL CV_INLINE_ISINF_DBL(value); -#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__PPC64__) +#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__PPC64__) || defined(__loongarch64) Cv64suf ieee754; ieee754.f = value; return (ieee754.u & 0x7fffffff00000000) == @@ -332,6 +354,15 @@ CV_INLINE int cvFloor( float value ) #if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \ defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS return (int)__builtin_floorf(value); +#elif defined __loongarch + int i; + float tmp; + __asm__ ("ftintrm.w.s %[tmp], %[in] \n\t" + "movfr2gr.s %[i], %[tmp] \n\t" + : [i] "=r" (i), [tmp] "=f" (tmp) + : [in] "f" (value) + :); + return i; #else int i = (int)value; return i - (i > value); @@ -350,6 +381,15 @@ CV_INLINE int cvCeil( float value ) #if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \ defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS return (int)__builtin_ceilf(value); +#elif defined __loongarch + int i; + float tmp; + __asm__ ("ftintrp.w.s %[tmp], %[in] \n\t" + "movfr2gr.s %[i], %[tmp] \n\t" + : [i] "=r" (i), [tmp] "=f" (tmp) + : [in] "f" (value) + :); + return i; #else int i = (int)value; return i + (i < value); From 26a7647e0e7c665e48e058f71cc1ab952cc4303d Mon Sep 17 00:00:00 2001 From: Andrew Chinery Date: Tue, 13 Sep 2022 14:35:42 +0100 Subject: [PATCH 079/313] Fix stitching Python bindings PR #22329 --- .../opencv2/stitching/detail/matchers.hpp | 21 +++++++--- .../opencv2/stitching/detail/seam_finders.hpp | 2 +- .../misc/python/test/test_stitching.py | 42 +++++++++++++++++++ modules/stitching/src/matchers.cpp | 8 ++-- modules/stitching/test/test_matchers.cpp | 26 ++++++++++++ 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp index 1b7d7d6897..2edc9564ba 100644 --- a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp @@ -138,7 +138,7 @@ public: @sa detail::MatchesInfo */ CV_WRAP_AS(apply2) void operator ()(const std::vector &features, CV_OUT std::vector &pairwise_matches, - const cv::UMat &mask = cv::UMat()); + const cv::UMat &mask = cv::UMat()) { match(features, pairwise_matches, mask); }; /** @return True, if it's possible to use the same matcher instance in parallel, false otherwise */ @@ -161,6 +161,16 @@ protected: virtual void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) = 0; + /** @brief This method implements logic to match features between arbitrary number of features. + By default this checks every pair of inputs in the input, but the behaviour can be changed by subclasses. + + @param features vector of image features + @param pairwise_matches found matches + @param mask (optional) mask indicating which image pairs should be matched + */ + virtual void match(const std::vector &features, std::vector &pairwise_matches, + const cv::UMat &mask = cv::UMat()); + bool is_thread_safe_; }; @@ -202,11 +212,12 @@ public: CV_WRAP BestOf2NearestRangeMatcher(int range_width = 5, bool try_use_gpu = false, float match_conf = 0.3f, int num_matches_thresh1 = 6, int num_matches_thresh2 = 6); - void operator ()(const std::vector &features, std::vector &pairwise_matches, - const cv::UMat &mask = cv::UMat()); - - protected: + // indicate that we do not want to hide the base class match method with a different signature + using BestOf2NearestMatcher::match; + void match(const std::vector &features, std::vector &pairwise_matches, + const cv::UMat &mask = cv::UMat()) CV_OVERRIDE; + int range_width_; }; diff --git a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp index 71dae7fdff..9ccfd14424 100644 --- a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp @@ -248,7 +248,7 @@ public: ~GraphCutSeamFinder(); CV_WRAP void find(const std::vector &src, const std::vector &corners, - std::vector &masks) CV_OVERRIDE; + CV_IN_OUT std::vector &masks) CV_OVERRIDE; private: // To avoid GCGraph dependency diff --git a/modules/stitching/misc/python/test/test_stitching.py b/modules/stitching/misc/python/test/test_stitching.py index 2e7b2b5818..5f143d0013 100644 --- a/modules/stitching/misc/python/test/test_stitching.py +++ b/modules/stitching/misc/python/test/test_stitching.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import cv2 as cv +import numpy as np from tests_common import NewOpenCVTests @@ -134,6 +135,47 @@ class stitching_matches_info_test(NewOpenCVTests): self.assertIsNotNone(matches_info.matches) self.assertIsNotNone(matches_info.inliers_mask) +class stitching_range_matcher_test(NewOpenCVTests): + + def test_simple(self): + images = [ + self.get_sample('stitching/a1.png'), + self.get_sample('stitching/a2.png'), + self.get_sample('stitching/a3.png') + ] + + orb = cv.ORB_create() + + features = [cv.detail.computeImageFeatures2(orb, img) for img in images] + + matcher = cv.detail_BestOf2NearestRangeMatcher(range_width=1) + matches = matcher.apply2(features) + + # matches[1] is image 0 and image 1, should have non-zero confidence + self.assertNotEqual(matches[1].confidence, 0) + + # matches[2] is image 0 and image 2, should have zero confidence due to range_width=1 + self.assertEqual(matches[2].confidence, 0) + + +class stitching_seam_finder_graph_cuts(NewOpenCVTests): + + def test_simple(self): + images = [ + self.get_sample('stitching/a1.png'), + self.get_sample('stitching/a2.png'), + self.get_sample('stitching/a3.png') + ] + + images = [cv.resize(img, [100, 100]) for img in images] + + finder = cv.detail_GraphCutSeamFinder('COST_COLOR_GRAD') + masks = [cv.UMat(255 * np.ones((img.shape[0], img.shape[1]), np.uint8)) for img in images] + images_f = [img.astype(np.float32) for img in images] + masks_warped = finder.find(images_f, [(0, 0), (75, 0), (150, 0)], masks) + + self.assertIsNotNone(masks_warped) + if __name__ == '__main__': NewOpenCVTests.bootstrap() diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index 4791584366..cf742dd4b8 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -335,8 +335,8 @@ MatchesInfo& MatchesInfo::operator =(const MatchesInfo &other) ////////////////////////////////////////////////////////////////////////////// -void FeaturesMatcher::operator ()(const std::vector &features, std::vector &pairwise_matches, - const UMat &mask) +void FeaturesMatcher::match(const std::vector &features, std::vector &pairwise_matches, + const UMat &mask) { const int num_images = static_cast(features.size()); @@ -484,8 +484,8 @@ BestOf2NearestRangeMatcher::BestOf2NearestRangeMatcher(int range_width, bool try } -void BestOf2NearestRangeMatcher::operator ()(const std::vector &features, std::vector &pairwise_matches, - const UMat &mask) +void BestOf2NearestRangeMatcher::match(const std::vector &features, std::vector &pairwise_matches, + const UMat &mask) { const int num_images = static_cast(features.size()); diff --git a/modules/stitching/test/test_matchers.cpp b/modules/stitching/test/test_matchers.cpp index 08c5aa56db..2deb676fb3 100644 --- a/modules/stitching/test/test_matchers.cpp +++ b/modules/stitching/test/test_matchers.cpp @@ -114,4 +114,30 @@ TEST(ParallelFeaturesFinder, IsSameWithSerial) } } +TEST(RangeMatcher, MatchesRangeOnly) +{ + Ptr finder = ORB::create(); + + Mat img0 = imread(string(cvtest::TS::ptr()->get_data_path()) + "stitching/a1.png", IMREAD_GRAYSCALE); + Mat img1 = imread(string(cvtest::TS::ptr()->get_data_path()) + "stitching/a2.png", IMREAD_GRAYSCALE); + Mat img2 = imread(string(cvtest::TS::ptr()->get_data_path()) + "stitching/a3.png", IMREAD_GRAYSCALE); + + vector features(3); + + computeImageFeatures(finder, img0, features[0]); + computeImageFeatures(finder, img1, features[1]); + computeImageFeatures(finder, img2, features[2]); + + vector pairwise_matches; + Ptr matcher = makePtr(1); + + (*matcher)(features, pairwise_matches); + + // matches[1] will be image 0 and image 1, should have non-zero confidence + EXPECT_NE(pairwise_matches[1].confidence, .0); + + // matches[2] will be image 0 and image 2, should have zero confidence due to range_width=1 + EXPECT_DOUBLE_EQ(pairwise_matches[2].confidence, .0); +} + }} // namespace From 04849f26b2c20465d475a29c86bb92351502c8f3 Mon Sep 17 00:00:00 2001 From: ocpalo Date: Tue, 13 Sep 2022 22:12:52 +0300 Subject: [PATCH 080/313] libjpegturbo simd extension update cmake flag --- 3rdparty/libjpeg-turbo/CMakeLists.txt | 5 ++--- CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt index e316e24822..d55dcdf962 100644 --- a/3rdparty/libjpeg-turbo/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/CMakeLists.txt @@ -63,7 +63,7 @@ OCV_OPTION(ENABLE_LIBJPEG_TURBO_SIMD "Include SIMD extensions for libjpeg-turbo, option(WITH_ARITH_ENC "Include arithmetic encoding support when emulating the libjpeg v6b API/ABI" TRUE) option(WITH_ARITH_DEC "Include arithmetic decoding support when emulating the libjpeg v6b API/ABI" TRUE) set(WITH_SIMD 1) -set(IS_LIBJPEG_TURBO_SIMD_AVAILABLE 0 PARENT_SCOPE) +set(HAVE_LIBJPEG_TURBO_SIMD 0 PARENT_SCOPE) include(CheckCSourceCompiles) include(CheckIncludeFiles) @@ -185,7 +185,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") endif() endif() -message(STATUS ${WITH_LIBJPEG_TURBO_SIMD}) if(ENABLE_LIBJPEG_TURBO_SIMD) add_subdirectory(src/simd) if(NEON_INTRINSICS) @@ -197,7 +196,7 @@ endif() if(WITH_SIMD) message(STATUS "SIMD extensions: ${CPU_TYPE} (WITH_SIMD = ${WITH_SIMD})") - set(IS_LIBJPEG_TURBO_SIMD_AVAILABLE 1 PARENT_SCOPE) + set(HAVE_LIBJPEG_TURBO_SIMD 1 PARENT_SCOPE) if(MSVC_IDE OR XCODE) set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 129cf0aae2..2bc79cc2f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1343,7 +1343,7 @@ if(WITH_JPEG OR HAVE_JPEG) status(" JPEG:" "build-${JPEG_LIBRARY} (ver ${JPEG_LIB_VERSION})") if(ENABLE_LIBJPEG_TURBO_SIMD) status(" SIMD Support Request:" "YES") - if(IS_LIBJPEG_TURBO_SIMD_AVAILABLE) + if(HAVE_LIBJPEG_TURBO_SIMD) status(" SIMD Support:" "YES") else() status(" SIMD Support:" "NO") From 6481cfd048e674d8c99406f0c6766ba6b1290afc Mon Sep 17 00:00:00 2001 From: Henrik Dobbe Flemmen Date: Mon, 12 Sep 2022 14:37:11 +0200 Subject: [PATCH 081/313] Update the fourcc codes link --- modules/videoio/include/opencv2/videoio.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index aa247dd84e..b63441aca9 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -876,9 +876,11 @@ public: /** @overload @param filename Name of the output video file. @param fourcc 4-character code of codec used to compress the frames. For example, - VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec, VideoWriter::fourcc('M','J','P','G') is a - motion-jpeg codec etc. List of codes can be obtained at [Video Codecs by - FOURCC](http://www.fourcc.org/codecs.php) page. FFMPEG backend with MP4 container natively uses + VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec, VideoWriter::fourcc('M','J','P','G') + is a motion-jpeg codec etc. List of codes can be obtained at + [MSDN](https://docs.microsoft.com/en-us/windows/win32/medfound/video-fourccs) page + or with this [archived page](https://web.archive.org/web/20220316062600/http://www.fourcc.org/codecs.php) + of the fourcc site for a more complete list). FFMPEG backend with MP4 container natively uses other values as fourcc code: see [ObjectType](http://mp4ra.org/#/codecs), so you may receive a warning message from OpenCV about fourcc code conversion. @param fps Framerate of the created video stream. From 65bdb3a5442af922c5b4fa9d19e448450b4be4c0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 6 Sep 2022 15:50:37 +0000 Subject: [PATCH 082/313] dnn: eliminate GCC12 warning in total() call --- .../dnn/include/opencv2/dnn/shape_utils.hpp | 39 ++++++++++++++++--- .../dnn/src/layers/normalize_bbox_layer.cpp | 4 +- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/shape_utils.hpp b/modules/dnn/include/opencv2/dnn/shape_utils.hpp index fafd1bf725..3241a1a024 100644 --- a/modules/dnn/include/opencv2/dnn/shape_utils.hpp +++ b/modules/dnn/include/opencv2/dnn/shape_utils.hpp @@ -160,22 +160,49 @@ static inline MatShape shape(int a0, int a1=-1, int a2=-1, int a3=-1) static inline int total(const MatShape& shape, int start = -1, int end = -1) { - if (start == -1) start = 0; - if (end == -1) end = (int)shape.size(); - if (shape.empty()) return 0; + int dims = (int)shape.size(); + + if (start == -1) start = 0; + if (end == -1) end = dims; + + CV_CheckLE(0, start, ""); + CV_CheckLE(start, end, ""); + CV_CheckLE(end, dims, ""); + int elems = 1; - CV_Assert(start <= (int)shape.size() && end <= (int)shape.size() && - start <= end); - for(int i = start; i < end; i++) + for (int i = start; i < end; i++) { elems *= shape[i]; } return elems; } +// TODO: rename to countDimsElements() +static inline int total(const Mat& mat, int start = -1, int end = -1) +{ + if (mat.empty()) + return 0; + + int dims = mat.dims; + + if (start == -1) start = 0; + if (end == -1) end = dims; + + CV_CheckLE(0, start, ""); + CV_CheckLE(start, end, ""); + CV_CheckLE(end, dims, ""); + + int elems = 1; + for (int i = start; i < end; i++) + { + elems *= mat.size[i]; + } + return elems; +} + static inline MatShape concat(const MatShape& a, const MatShape& b) { MatShape c = a; diff --git a/modules/dnn/src/layers/normalize_bbox_layer.cpp b/modules/dnn/src/layers/normalize_bbox_layer.cpp index 37202bf863..11dc911087 100644 --- a/modules/dnn/src/layers/normalize_bbox_layer.cpp +++ b/modules/dnn/src/layers/normalize_bbox_layer.cpp @@ -208,8 +208,8 @@ public: const float* inpData = inp0.ptr(); float* outData = outputs[0].ptr(); - size_t num = total(shape(inp0.size), 0, startAxis); - size_t numPlanes = total(shape(inp0.size), startAxis, endAxis + 1); + size_t num = total(inp0, 0, startAxis); + size_t numPlanes = total(inp0, startAxis, endAxis + 1); CV_Assert(num * numPlanes != 0); size_t planeSize = inp0.total() / (num * numPlanes); for (size_t n = 0; n < num; ++n) From 2e155827997f5160bea9d738aff300bbe582c7bb Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 14 Sep 2022 11:57:46 +0000 Subject: [PATCH 083/313] build: eliminate uninitialized warnings from GCC12 --- modules/core/include/opencv2/core/hal/intrin.hpp | 10 ++++++++++ modules/imgproc/src/sumpixels.avx512_skx.hpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index 16d5284e64..cf49bffceb 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -50,6 +50,12 @@ #include #include "opencv2/core/cvdef.h" +#if defined(__GNUC__) && __GNUC__ == 12 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + #define OPENCV_HAL_ADD(a, b) ((a) + (b)) #define OPENCV_HAL_AND(a, b) ((a) & (b)) #define OPENCV_HAL_NOP(a) (a) @@ -695,4 +701,8 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END //! @endcond +#if defined(__GNUC__) && __GNUC__ == 12 +#pragma GCC diagnostic pop +#endif + #endif diff --git a/modules/imgproc/src/sumpixels.avx512_skx.hpp b/modules/imgproc/src/sumpixels.avx512_skx.hpp index 3c9c90c658..81d9d1d846 100644 --- a/modules/imgproc/src/sumpixels.avx512_skx.hpp +++ b/modules/imgproc/src/sumpixels.avx512_skx.hpp @@ -6,6 +6,12 @@ #include "opencv2/core/hal/intrin.hpp" +#if defined(__GNUC__) && __GNUC__ == 12 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + namespace cv { namespace hal { CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN @@ -465,3 +471,7 @@ void calculate_integral_avx512(const uchar *src, size_t _srcstep, CV_CPU_OPTIMIZATION_NAMESPACE_END }} // end namespace cv::hal + +#if defined(__GNUC__) && __GNUC__ == 12 +#pragma GCC diagnostic pop +#endif From 13823f117b2f77701ae99ce8cdce3645a2d1f259 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 14 Sep 2022 14:19:04 +0200 Subject: [PATCH 084/313] #22214 and #22198 --- modules/videoio/src/cap_android_mediandk.cpp | 122 +++++++++++++++---- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/modules/videoio/src/cap_android_mediandk.cpp b/modules/videoio/src/cap_android_mediandk.cpp index c7f2855502..9844d9c651 100644 --- a/modules/videoio/src/cap_android_mediandk.cpp +++ b/modules/videoio/src/cap_android_mediandk.cpp @@ -48,15 +48,29 @@ class AndroidMediaNdkCapture : public IVideoCapture public: AndroidMediaNdkCapture(): sawInputEOS(false), sawOutputEOS(false), - frameWidth(0), frameHeight(0), colorFormat(0) {} + frameStride(0), frameWidth(0), frameHeight(0), colorFormat(0), + videoWidth(0), videoHeight(0), + videoFrameCount(0), + videoRotation(0), videoRotationCode(-1), + videoOrientationAuto(false) {} + std::shared_ptr mediaExtractor; std::shared_ptr mediaCodec; bool sawInputEOS; bool sawOutputEOS; + int32_t frameStride; int32_t frameWidth; int32_t frameHeight; int32_t colorFormat; + int32_t videoWidth; + int32_t videoHeight; + float videoFrameRate; + int32_t videoFrameCount; + int32_t videoRotation; + int32_t videoRotationCode; + bool videoOrientationAuto; std::vector buffer; + Mat frame; ~AndroidMediaNdkCapture() { cleanUp(); } @@ -89,6 +103,7 @@ public: size_t bufferSize = 0; auto mediaFormat = std::shared_ptr(AMediaCodec_getOutputFormat(mediaCodec.get()), deleter_AMediaFormat); AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_WIDTH, &frameWidth); + AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_STRIDE, &frameStride); AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_HEIGHT, &frameHeight); AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, &colorFormat); uint8_t* codecBuffer = AMediaCodec_getOutputBuffer(mediaCodec.get(), bufferIndex, &bufferSize); @@ -96,30 +111,14 @@ public: LOGV("colorFormat: %d", colorFormat); LOGV("buffer size: %zu", bufferSize); LOGV("width (frame): %d", frameWidth); + LOGV("stride (frame): %d", frameStride); LOGV("height (frame): %d", frameHeight); if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { LOGV("output EOS"); sawOutputEOS = true; } - if ((size_t)frameWidth * frameHeight * 3 / 2 > bufferSize) - { - if (bufferSize == 3110400 && frameWidth == 1920 && frameHeight == 1088) - { - frameHeight = 1080; - LOGV("Buffer size is too small, force using height = %d", frameHeight); - } - else if(bufferSize == 3110400 && frameWidth == 1088 && frameHeight == 1920) - { - frameWidth = 1080; - LOGV("Buffer size is too small, force using width = %d", frameWidth); - } - else - { - LOGE("Buffer size is too small. Frame is ignored. Enable verbose logging to see actual values of parameters"); - return false; - } - } + AMediaCodec_releaseOutputBuffer(mediaCodec.get(), bufferIndex, info.size != 0); return true; } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { @@ -154,15 +153,25 @@ public: if (buffer.empty()) { return false; } - Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data()); + + Mat yuv(frameHeight + frameHeight/2, frameStride, CV_8UC1, buffer.data()); + if (colorFormat == COLOR_FormatYUV420Planar) { - cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_YV12); + cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_YV12); } else if (colorFormat == COLOR_FormatYUV420SemiPlanar) { - cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_NV21); + cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_NV21); } else { LOGE("Unsupported video format: %d", colorFormat); return false; } + + Mat croppedFrame = frame(Rect(0, 0, videoWidth, videoHeight)); + out.assign(croppedFrame); + + if (videoOrientationAuto && -1 != videoRotationCode) { + cv::rotate(out, out, videoRotationCode); + } + return true; } @@ -170,14 +179,32 @@ public: { switch (property_id) { - case CV_CAP_PROP_FRAME_WIDTH: return frameWidth; - case CV_CAP_PROP_FRAME_HEIGHT: return frameHeight; + case CV_CAP_PROP_FRAME_WIDTH: + return (( videoOrientationAuto && + (cv::ROTATE_90_CLOCKWISE == videoRotationCode || cv::ROTATE_90_COUNTERCLOCKWISE == videoRotationCode)) + ? videoHeight : videoWidth); + case CV_CAP_PROP_FRAME_HEIGHT: + return (( videoOrientationAuto && + (cv::ROTATE_90_CLOCKWISE == videoRotationCode || cv::ROTATE_90_COUNTERCLOCKWISE == videoRotationCode)) + ? videoWidth : videoHeight); + case CV_CAP_PROP_FPS: return videoFrameRate; + case CV_CAP_PROP_FRAME_COUNT: return videoFrameCount; + case CAP_PROP_ORIENTATION_META: return videoRotation; + case CAP_PROP_ORIENTATION_AUTO: return videoOrientationAuto ? 1 : 0; } return 0; } - bool setProperty(int /* property_id */, double /* value */) CV_OVERRIDE + bool setProperty(int property_id, double value) CV_OVERRIDE { + switch (property_id) + { + case CAP_PROP_ORIENTATION_AUTO: { + videoOrientationAuto = value != 0 ? true : false; + return true; + } + } + return false; } @@ -221,9 +248,20 @@ public: if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) { LOGV("no mime type"); } else if (!strncmp(mime, "video/", 6)) { - int32_t trackWidth, trackHeight; + int32_t trackWidth, trackHeight, fps, frameCount = 0, rotation = 0; AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &trackWidth); AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &trackHeight); + AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_FRAME_RATE, &fps); + + #if __ANDROID_API__ >= 28 + AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation); + LOGV("rotation (track): %d", rotation); + #endif + + #if __ANDROID_API__ >= 29 + AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount); + #endif + LOGV("width (track): %d", trackWidth); LOGV("height (track): %d", trackHeight); if (AMediaExtractor_selectTrack(mediaExtractor.get(), i) != AMEDIA_OK) { @@ -241,6 +279,31 @@ public: if (AMediaCodec_start(mediaCodec.get()) != AMEDIA_OK) { continue; } + + videoWidth = trackWidth; + videoHeight = trackHeight; + videoFrameRate = fps; + videoFrameCount = frameCount; + videoRotation = rotation; + + switch(videoRotation) { + case 90: + videoRotationCode = cv::ROTATE_90_CLOCKWISE; + break; + + case 180: + videoRotationCode = cv::ROTATE_180; + break; + + case 270: + videoRotationCode = cv::ROTATE_90_COUNTERCLOCKWISE; + break; + + default: + videoRotationCode = -1; + break; + } + return true; } } @@ -251,9 +314,16 @@ public: void cleanUp() { sawInputEOS = true; sawOutputEOS = true; + frameStride = 0; frameWidth = 0; frameHeight = 0; colorFormat = 0; + videoWidth = 0; + videoHeight = 0; + videoFrameRate = 0; + videoFrameCount = 0; + videoRotation = 0; + videoRotationCode = -1; } }; From e3e61078a56112939be87fe5bc6e8a82cd3ebb80 Mon Sep 17 00:00:00 2001 From: ocpalo <44481097+ocpalo@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:20:41 +0300 Subject: [PATCH 085/313] 3rdparty: Update readme.txt for libjpeg-turbo simd --- 3rdparty/readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/3rdparty/readme.txt b/3rdparty/readme.txt index 28bac17d7a..c3068521e3 100644 --- a/3rdparty/readme.txt +++ b/3rdparty/readme.txt @@ -20,6 +20,7 @@ libjpeg-turbo libjpeg-turbo is covered by three compatible BSD-style ope WITH_JPEG CMake option must be ON to add libjpeg or libjpeg-turbo support to imgcodecs. BUILD_JPEG=ON selects libjpeg-turbo by default (since OpenCV 3.4.2). Enable BUILD_JPEG_TURBO_DISABLE=ON to force using of libjpeg (this option is removed in OpenCV 4.0). + SIMD instructions are enabled by default. Use ENABLE_LIBJPEG_TURBO_SIMD to control SIMD instructions. ------------------------------------------------------------------------------------ libpng Portable Network Graphics library. The license and copyright notes can be found in libpng/LICENSE. From eb8883160d8705fba59f884d9ebcb9842c8552d7 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Thu, 15 Sep 2022 10:47:13 +0000 Subject: [PATCH 086/313] Support config as part of Infer node in yml --- .../gapi/samples/pipeline_modeling_tool.cpp | 22 ++++++++++++++----- .../samples/pipeline_modeling_tool/utils.hpp | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index 7a0f94655c..a10cb5f052 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -190,6 +190,15 @@ CallParams read(const cv::FileNode& fn) { return CallParams{std::move(name), static_cast(call_every_nth)}; } +template +std::map readMap(const cv::FileNode& fn) { + std::map map; + for (auto item : fn) { + map.emplace(item.name(), read(item)); + } + return map; +} + template <> InferParams read(const cv::FileNode& fn) { auto name = @@ -200,7 +209,7 @@ InferParams read(const cv::FileNode& fn) { params.device = check_and_read(fn, "device", name); params.input_layers = readList(fn, "input_layers", name); params.output_layers = readList(fn, "output_layers", name); - + params.config = readMap(fn["config"]); return params; } @@ -309,13 +318,13 @@ int main(int argc, char* argv[]) { cv::FileStorage::MEMORY); } - std::map config; + std::map gconfig; if (!load_config.empty()) { - loadConfig(load_config, config); + loadConfig(load_config, gconfig); } // NB: Takes priority over config from file if (!cached_dir.empty()) { - config = + gconfig = std::map{{"CACHE_DIR", cached_dir}}; } @@ -371,7 +380,10 @@ int main(int argc, char* argv[]) { builder.addDummy(call_params, read(node_fn)); } else if (node_type == "Infer") { auto infer_params = read(node_fn); - infer_params.config = config; + RETHROW_WITH_MSG_IF_FAILED( + utils::intersectMapWith(infer_params.config, gconfig), + "Failed to combine global and local configs for Infer node: " + + call_params.name); builder.addInfer(call_params, infer_params); } else { throw std::logic_error("Unsupported node type: " + node_type); diff --git a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp index c110bf3b47..c8f0101fe1 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp @@ -1,6 +1,8 @@ #ifndef OPENCV_GAPI_PIPELINE_MODELING_TOOL_UTILS_HPP #define OPENCV_GAPI_PIPELINE_MODELING_TOOL_UTILS_HPP +#include + #include #if defined(_WIN32) @@ -91,6 +93,26 @@ typename duration_t::rep timestamp() { return duration_cast(now.time_since_epoch()).count(); } +#define RETHROW_WITH_MSG_IF_FAILED(expr, msg) \ + try { \ + expr; \ + } catch (const std::exception& e) { \ + std::stringstream ss; \ + ss << msg << "\n caused by: " << e.what(); \ + throw std::logic_error(ss.str()); \ + } \ + +template +void intersectMapWith(std::map& target, const std::map& second) { + for (auto&& item : second) { + auto it = target.find(item.first); + if (it != target.end()) { + throw std::logic_error("Met already existing key: " + item.first); + } + target.insert(item); + } +} + } // namespace utils #endif // OPENCV_GAPI_PIPELINE_MODELING_TOOL_UTILS_HPP From 122250b554b413601bd19d4dc4e6377e0a038e97 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 15 Sep 2022 13:24:18 +0200 Subject: [PATCH 087/313] fix #22490 --- modules/videoio/src/cap_android_mediandk.cpp | 351 ++++++++++++++++++- modules/videoio/src/cap_interface.hpp | 3 + modules/videoio/src/videoio_registry.cpp | 6 +- 3 files changed, 358 insertions(+), 2 deletions(-) diff --git a/modules/videoio/src/cap_android_mediandk.cpp b/modules/videoio/src/cap_android_mediandk.cpp index 9844d9c651..2f8b193404 100644 --- a/modules/videoio/src/cap_android_mediandk.cpp +++ b/modules/videoio/src/cap_android_mediandk.cpp @@ -13,14 +13,17 @@ #include #include #include - +#include #include "media/NdkMediaCodec.h" +#include "media/NdkMediaMuxer.h" #include "media/NdkMediaExtractor.h" +#include "media/NdkMediaFormat.h" #define INPUT_TIMEOUT_MS 2000 #define COLOR_FormatYUV420Planar 19 #define COLOR_FormatYUV420SemiPlanar 21 +#define COLOR_FormatSurface 0x7f000789 //See https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities for codes using namespace cv; @@ -327,6 +330,341 @@ public: } }; + + +class AndroidMediaNdkVideoWriter CV_FINAL : + public cv::IVideoWriter +{ + typedef struct { + int fourcc; + const char* mime; + OutputFormat muxerFormat; + } + FourCCInfo; + + static const int64_t TIMEOUT = 1000L; + static const FourCCInfo FOURCC_INFO[]; + + static const FourCCInfo* findInfo(int fourcc) { + for( const FourCCInfo *it = FOURCC_INFO; NULL != it->mime; it++ ) { + if (fourcc == it->fourcc) return it; + } + return NULL; + } + + AMediaFormat* format; + AMediaCodec* encoder; + AMediaMuxer* muxer; + + #if __ANDROID_API__ >= 26 + ANativeWindow* surface; + #endif + + long frameIndex; + int width; + int height; + double frameRate; + ssize_t videoTrackIndex; + int fd; + + void drainEncoder(bool end) { + if (end) { + #if __ANDROID_API__ >= 26 + AMediaCodec_signalEndOfInputStream(encoder); + #else + writeBytes(NULL, 0); + #endif + } + + AMediaCodecBufferInfo bufferInfo; + ssize_t bufferIndex; + size_t bufferSize; + uint8_t *buffer; + + while (true) { + bufferIndex = AMediaCodec_dequeueOutputBuffer(encoder, &bufferInfo, TIMEOUT); + if (bufferIndex >= 0) { + buffer = AMediaCodec_getOutputBuffer(encoder, (size_t)bufferIndex, &bufferSize); + + if (NULL == buffer || 0 == bufferSize){ + LOGE("Can't get output buffer"); + break; + } + + if (videoTrackIndex >= 0) { + bufferInfo.presentationTimeUs = frameIndex * 1000000L / frameRate; + LOGV("Muxer write to track %d: %d byte(s)", (int)videoTrackIndex, (int)bufferInfo.size); + AMediaMuxer_writeSampleData(muxer, (size_t)videoTrackIndex, buffer, &bufferInfo); + } else { + LOGE("Invalid video track !"); + } + + AMediaCodec_releaseOutputBuffer(encoder, (size_t)bufferIndex, false); + if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) break; + } else if (AMEDIACODEC_INFO_TRY_AGAIN_LATER == bufferIndex) { + if (!end) break; + } else if (AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED == bufferIndex) { + videoTrackIndex = AMediaMuxer_addTrack(muxer, AMediaCodec_getOutputFormat(encoder)); + if (videoTrackIndex >= 0) { + AMediaMuxer_start(muxer); + } + LOGV("New videoTrackIndex: %d", (int)videoTrackIndex); + } + } + } + + #if __ANDROID_API__ < 26 + void writeBytes( uint8_t* inputBuffer, size_t inputBufferSize ) { + LOGV("[writeBytes] inputBufferSize=%u", (unsigned int)inputBufferSize); + + ssize_t bufferIndex; + size_t bufferSize; + uint8_t* buffer; + size_t partialSize; + bool firstCall = true; + uint32_t flags; + + while(inputBufferSize > 0 || firstCall) { + bufferIndex = AMediaCodec_dequeueInputBuffer(encoder, TIMEOUT); + + if (bufferIndex >= 0) { + firstCall = false; + buffer = AMediaCodec_getInputBuffer(encoder, (size_t)bufferIndex, &bufferSize); + if (NULL == buffer || 0 == bufferSize) break; + + flags = 0; + partialSize = (inputBufferSize > bufferSize) ? bufferSize : inputBufferSize; + if (partialSize > 0) { + memcpy(buffer, inputBuffer, partialSize); + inputBuffer += partialSize; + inputBufferSize -= partialSize; + if (inputBufferSize > 0) { + flags = AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME; + } + } else { + flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; + } + + LOGV( + "[writeBytes] partial - bufferIndex=%d, bufferSize=%u, partialSize=%u, remaining inputBufferSize=%u", + (int)bufferIndex, (unsigned int)bufferSize, (unsigned int)partialSize, (unsigned int)inputBufferSize + ); + + AMediaCodec_queueInputBuffer(encoder, (size_t)bufferIndex, 0, partialSize, frameIndex * 1000000L / frameRate, flags); + if (NULL != inputBuffer) drainEncoder(false); + } + } + } + #endif + +public: + AndroidMediaNdkVideoWriter(const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params) + : format(NULL), + encoder(NULL), + muxer(NULL), + #if __ANDROID_API__ >= 26 + surface(NULL), + #endif + frameIndex(0), + width(0), + height(0), + frameRate(0.), + videoTrackIndex(-1), + fd(-1) { + open(filename, fourcc, fps, frameSize, params); + } + virtual ~AndroidMediaNdkVideoWriter() { close(); } + + virtual int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_ANDROID; } + + virtual void write(cv::InputArray image_ ) CV_OVERRIDE + { + if (!image_.isMat()) { + LOGE("Support only Mat input"); + return; + } + + Mat image = image_.getMat(); + if (CV_8UC3 != image.type() || image.cols > width || image.rows > height) { + LOGE( + "Expected input to be a mat of maximum %d x %d of type CV_8UC3 (%d), but received %d x %d of type: %d", + width, height, CV_8UC3, + image.cols, image.rows, image.type() + ); + return; + } + + #if __ANDROID_API__ >= 26 + ANativeWindow_Buffer buffer; + if (0 != ANativeWindow_lock(surface, &buffer, NULL)) { + LOGE("Failed to lock the surface"); + } else { + if (AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM == buffer.format) { + Mat bufferMat(image.rows, image.cols, CV_8UC4, buffer.bits, buffer.stride * 4); + cvtColor(image, bufferMat, CV_BGR2RGBA); + } else { + LOGE("Unknow surface buffer format: %u", buffer.format); + } + + ANativeWindow_unlockAndPost(surface); + } + #else + LOGV("[write] image: %d x %d", image.cols, image.rows); + + //OpenCV don't support RGB to NV12 so we need to connvert to YV12 and then manually changed it to NV12 + Mat imageYV12; + cvtColor(image, imageYV12, CV_BGR2YUV_YV12); + + //convert from YV12 to NV12 + size_t yPlaneSize = width * height; + size_t vPlaneSize = yPlaneSize / 4; + + Mat channels[2] = { + Mat( vPlaneSize, 1, CV_8UC1, imageYV12.ptr() + yPlaneSize + vPlaneSize ).clone(), + Mat( vPlaneSize, 1, CV_8UC1, imageYV12.ptr() + yPlaneSize ).clone() + }; + Mat vuMat( vPlaneSize, 1, CV_8UC2, imageYV12.ptr() + yPlaneSize ); + merge(channels, 2, vuMat); + + writeBytes( imageYV12.ptr(), imageYV12.rows * imageYV12.cols ); + #endif + + drainEncoder(false); + + frameIndex++; + } + + virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params ) + { + media_status_t status; + + close(); + + const FourCCInfo* info = findInfo(fourcc); + if (NULL == info) { + LOGE("ERROR: findInfo"); + return false; + } + + format = AMediaFormat_new(); + if (NULL == format) { + LOGE("ERROR: AMediaFormat_new"); + goto error; + } + + LOGV("mime: %s, width: %d, height: %d, fps: %f", info->mime, frameSize.width, frameSize.height, fps); + + AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, info->mime); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, frameSize.width); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, frameSize.height); + AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, (float)fps); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 5); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, frameSize.width * frameSize.height * 5); + AMediaFormat_setInt32( + format, AMEDIAFORMAT_KEY_COLOR_FORMAT, + #if __ANDROID_API__ >= 26 + COLOR_FormatSurface + #else + COLOR_FormatYUV420SemiPlanar + #endif + ); + + encoder = AMediaCodec_createEncoderByType(info->mime); + if (NULL == encoder) { + LOGE("ERROR: AMediaCodec_createEncoderByType"); + goto error; + } + + status = AMediaCodec_configure(encoder, format, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE); + if (AMEDIA_OK != status) { + LOGE("ERROR: AMediaCodec_configure (%d)", status); + goto error; + } + + #if __ANDROID_API__ >= 26 + status = AMediaCodec_createInputSurface(encoder, &surface); + if (AMEDIA_OK != status || NULL == surface) { + LOGE("ERROR: AMediaCodec_createInputSurface (%d)", status); + goto error; + } + #endif + + AMediaCodec_start(encoder); + + fd = ::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + LOGE("ERROR: open"); + goto error; + } + + muxer = AMediaMuxer_new(fd, info->muxerFormat); + if (NULL == muxer) { + LOGE("ERROR: AMediaMuxer_new"); + goto error; + } + + AMediaMuxer_setOrientationHint(muxer, params.get(CAP_PROP_ORIENTATION_META, 0)); + + frameIndex = 0; + width = frameSize.width; + height = frameSize.height; + frameRate = fps; + videoTrackIndex = -1; + + return true; + + error: + close(); + return false; + } + + virtual void close() + { + if (videoTrackIndex >= 0 && NULL != muxer) { + drainEncoder(true); + AMediaMuxer_stop(muxer); + } + + if (NULL != encoder) AMediaCodec_delete(encoder); + if (NULL != muxer) AMediaMuxer_delete(muxer); + + #if __ANDROID_API__ >= 26 + if (NULL != surface) ANativeWindow_release(surface); + #endif + + if (fd >= 0) ::close(fd); + if (NULL != format) AMediaFormat_delete(format); + + format = NULL; + encoder = NULL; + muxer = NULL; + #if __ANDROID_API__ >= 26 + surface = NULL; + #endif + frameIndex = 0; + width = 0; + height = 0; + frameRate = 0.; + videoTrackIndex = -1; + fd = -1; + } + + virtual double getProperty(int) const CV_OVERRIDE { return 0.; } + virtual bool setProperty(int, double) CV_OVERRIDE { return false; } + virtual bool isOpened() const CV_OVERRIDE { return NULL != encoder; } +}; + + +const AndroidMediaNdkVideoWriter::FourCCInfo AndroidMediaNdkVideoWriter::FOURCC_INFO[] = { + { CV_FOURCC('H', '2', '6', '4'), "video/avc", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 }, + { CV_FOURCC('H', '2', '6', '5'), "video/hevc", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 }, + { CV_FOURCC('H', '2', '6', '3'), "video/3gpp", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 }, + { CV_FOURCC('M', 'P', '4', 'V'), "video/mp4v-es", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 }, + { 0, NULL }, +}; + + + /****************** Implementation of interface functions ********************/ Ptr cv::createAndroidCapture_file(const std::string &filename) { @@ -335,3 +673,14 @@ Ptr cv::createAndroidCapture_file(const std::string &filename) { return res; return Ptr(); } + + +Ptr cv::createAndroidVideoWriter( + const std::string& filename, int fourcc, + double fps, const cv::Size& frameSize, + const VideoWriterParameters& params) { + Ptr writer = makePtr(filename, fourcc, fps, frameSize, params); + if (writer && writer->isOpened()) + return writer; + return Ptr(); +} diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index ebc07d77b0..3b3d5398fc 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -410,6 +410,9 @@ Ptr createXINECapture(const std::string &filename); Ptr createAndroidCapture_cam( int index ); Ptr createAndroidCapture_file(const std::string &filename); +Ptr createAndroidVideoWriter(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_obsensor_capture(int index); diff --git a/modules/videoio/src/videoio_registry.cpp b/modules/videoio/src/videoio_registry.cpp index 52f4227396..fb4f2fc26f 100644 --- a/modules/videoio/src/videoio_registry.cpp +++ b/modules/videoio/src/videoio_registry.cpp @@ -149,7 +149,7 @@ static const struct VideoBackendInfo builtin_backends[] = #if defined(HAVE_ANDROID_MEDIANDK) || defined(HAVE_ANDROID_NATIVE_CAMERA) DECLARE_STATIC_BACKEND(CAP_ANDROID, "ANDROID_NATIVE", #ifdef HAVE_ANDROID_MEDIANDK - MODE_CAPTURE_BY_FILENAME + MODE_CAPTURE_BY_FILENAME | MODE_WRITER #else 0 #endif @@ -169,8 +169,12 @@ static const struct VideoBackendInfo builtin_backends[] = #else 0, #endif +#ifdef HAVE_ANDROID_MEDIANDK + createAndroidVideoWriter) +#else 0) #endif +#endif #ifdef HAVE_OBSENSOR DECLARE_STATIC_BACKEND(CAP_OBSENSOR, "OBSENSOR", MODE_CAPTURE_BY_INDEX, 0, create_obsensor_capture, 0) From 8ae03fcd6e0e1fa895af719b394e3b4ea9d8a470 Mon Sep 17 00:00:00 2001 From: Stefan Spiss Date: Wed, 7 Sep 2022 17:03:51 +0200 Subject: [PATCH 088/313] Extended stereoCalibrate function for pinhole model to return per view rotation and translation vectors between the calibration object coordinate space and the coordinate space of the first camera of the stereo pair. Added overloaded versions of the function for downward compatibility. --- modules/calib3d/include/opencv2/calib3d.hpp | 22 +++- modules/calib3d/src/calibration.cpp | 128 ++++++++++++++++++-- 2 files changed, 140 insertions(+), 10 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 893eb22761..176a2dfc5f 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -1748,6 +1748,15 @@ second camera coordinate system. @param T Output translation vector, see description above. @param E Output essential matrix. @param F Output fundamental matrix. +@param rvecs Output vector of rotation vectors ( @ref Rodrigues ) estimated for each pattern view in the +coordinate system of the first camera of the stereo pair (e.g. std::vector). More in detail, each +i-th rotation vector together with the corresponding i-th translation vector (see the next output parameter +description) brings the calibration pattern from the object coordinate space (in which object points are +specified) to the camera coordinate space of the first camera of the stereo pair. In more technical terms, +the tuple of the i-th rotation and translation vector performs a change of basis from object coordinate space +to camera coordinate space of the first camera of the stereo pair. +@param tvecs Output vector of translation vectors estimated for each pattern view, see parameter description +of previous output parameter ( rvecs ). @param perViewErrors Output vector of the RMS re-projection error estimated for each pattern view. @param flags Different flags that may be zero or a combination of the following values: - @ref CALIB_FIX_INTRINSIC Fix cameraMatrix? and distCoeffs? so that only R, T, E, and F @@ -1844,8 +1853,8 @@ CV_EXPORTS_AS(stereoCalibrateExtended) double stereoCalibrate( InputArrayOfArray InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, InputOutputArray cameraMatrix1, InputOutputArray distCoeffs1, InputOutputArray cameraMatrix2, InputOutputArray distCoeffs2, - Size imageSize, InputOutputArray R,InputOutputArray T, OutputArray E, OutputArray F, - OutputArray perViewErrors, int flags = CALIB_FIX_INTRINSIC, + Size imageSize, InputOutputArray R, InputOutputArray T, OutputArray E, OutputArray F, + OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, OutputArray perViewErrors, int flags = CALIB_FIX_INTRINSIC, TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) ); /// @overload @@ -1857,6 +1866,15 @@ CV_EXPORTS_W double stereoCalibrate( InputArrayOfArrays objectPoints, int flags = CALIB_FIX_INTRINSIC, TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) ); +/// @overload +CV_EXPORTS_W double stereoCalibrate( InputArrayOfArrays objectPoints, + InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, + InputOutputArray cameraMatrix1, InputOutputArray distCoeffs1, + InputOutputArray cameraMatrix2, InputOutputArray distCoeffs2, + Size imageSize, InputOutputArray R, InputOutputArray T, OutputArray E, OutputArray F, + OutputArray perViewErrors, int flags = CALIB_FIX_INTRINSIC, + TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) ); + /** @brief Computes rectification transforms for each head of a calibrated stereo camera. @param cameraMatrix1 First camera intrinsic matrix. diff --git a/modules/calib3d/src/calibration.cpp b/modules/calib3d/src/calibration.cpp index 43e58dad19..ba5848891c 100644 --- a/modules/calib3d/src/calibration.cpp +++ b/modules/calib3d/src/calibration.cpp @@ -1973,7 +1973,7 @@ static double cvStereoCalibrateImpl( const CvMat* _objectPoints, const CvMat* _i CvMat* _cameraMatrix2, CvMat* _distCoeffs2, CvSize imageSize, CvMat* matR, CvMat* matT, CvMat* matE, CvMat* matF, - CvMat* perViewErr, int flags, + CvMat* rvecs, CvMat* tvecs, CvMat* perViewErr, int flags, CvTermCriteria termCrit ) { const int NINTRINSIC = 18; @@ -2013,6 +2013,28 @@ static double cvStereoCalibrateImpl( const CvMat* _objectPoints, const CvMat* _i cvConvert( _objectPoints, objectPoints ); cvReshape( objectPoints, objectPoints, 3, 1 ); + if( rvecs ) + { + int cn = CV_MAT_CN(rvecs->type); + if( !CV_IS_MAT(rvecs) || + (CV_MAT_DEPTH(rvecs->type) != CV_32F && CV_MAT_DEPTH(rvecs->type) != CV_64F) || + ((rvecs->rows != nimages || (rvecs->cols*cn != 3 && rvecs->cols*cn != 9)) && + (rvecs->rows != 1 || rvecs->cols != nimages || cn != 3)) ) + CV_Error( CV_StsBadArg, "the output array of rotation vectors must be 3-channel " + "1xn or nx1 array or 1-channel nx3 or nx9 array, where n is the number of views" ); + } + + if( tvecs ) + { + int cn = CV_MAT_CN(tvecs->type); + if( !CV_IS_MAT(tvecs) || + (CV_MAT_DEPTH(tvecs->type) != CV_32F && CV_MAT_DEPTH(tvecs->type) != CV_64F) || + ((tvecs->rows != nimages || tvecs->cols*cn != 3) && + (tvecs->rows != 1 || tvecs->cols != nimages || cn != 3)) ) + CV_Error( CV_StsBadArg, "the output array of translation vectors must be 3-channel " + "1xn or nx1 array or 1-channel nx3 array, where n is the number of views" ); + } + for( k = 0; k < 2; k++ ) { const CvMat* points = k == 0 ? _imagePoints1 : _imagePoints2; @@ -2440,6 +2462,39 @@ static double cvStereoCalibrateImpl( const CvMat* _objectPoints, const CvMat* _i } } + CvMat tmp = cvMat(3, 3, CV_64F); + for( i = 0; i < nimages; i++ ) + { + CvMat src, dst; + + if( rvecs ) + { + src = cvMat(3, 1, CV_64F, solver.param->data.db+(i+1)*6); + if( rvecs->rows == nimages && rvecs->cols*CV_MAT_CN(rvecs->type) == 9 ) + { + dst = cvMat(3, 3, CV_MAT_DEPTH(rvecs->type), + rvecs->data.ptr + rvecs->step*i); + cvRodrigues2( &src, &tmp ); + cvConvert( &tmp, &dst ); + } + else + { + dst = cvMat(3, 1, CV_MAT_DEPTH(rvecs->type), rvecs->rows == 1 ? + rvecs->data.ptr + i*CV_ELEM_SIZE(rvecs->type) : + rvecs->data.ptr + rvecs->step*i); + cvConvert( &src, &dst ); + } + } + if( tvecs ) + { + src = cvMat(3, 1,CV_64F,solver.param->data.db+(i+1)*6+3); + dst = cvMat(3, 1, CV_MAT_DEPTH(tvecs->type), tvecs->rows == 1 ? + tvecs->data.ptr + i*CV_ELEM_SIZE(tvecs->type) : + tvecs->data.ptr + tvecs->step*i); + cvConvert( &src, &dst ); + } + } + return std::sqrt(reprojErr/(pointsTotal*2)); } double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1, @@ -2453,7 +2508,7 @@ double cvStereoCalibrate( const CvMat* _objectPoints, const CvMat* _imagePoints1 { return cvStereoCalibrateImpl(_objectPoints, _imagePoints1, _imagePoints2, _npoints, _cameraMatrix1, _distCoeffs1, _cameraMatrix2, _distCoeffs2, imageSize, matR, matT, matE, - matF, NULL, flags, termCrit); + matF, NULL, NULL, NULL, flags, termCrit); } static void @@ -3886,7 +3941,22 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, InputOutputArray _cameraMatrix2, InputOutputArray _distCoeffs2, Size imageSize, InputOutputArray _Rmat, InputOutputArray _Tmat, OutputArray _Emat, OutputArray _Fmat, - OutputArray _perViewErrors, int flags , + OutputArray _perViewErrors, int flags, + TermCriteria criteria) +{ + return stereoCalibrate(_objectPoints, _imagePoints1, _imagePoints2, _cameraMatrix1, _distCoeffs1, + _cameraMatrix2, _distCoeffs2, imageSize, _Rmat, _Tmat, _Emat, _Fmat, + noArray(), noArray(), _perViewErrors, flags, criteria); +} + +double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, + InputArrayOfArrays _imagePoints1, + InputArrayOfArrays _imagePoints2, + InputOutputArray _cameraMatrix1, InputOutputArray _distCoeffs1, + InputOutputArray _cameraMatrix2, InputOutputArray _distCoeffs2, + Size imageSize, InputOutputArray _Rmat, InputOutputArray _Tmat, + OutputArray _Emat, OutputArray _Fmat, + OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, OutputArray _perViewErrors, int flags, TermCriteria criteria) { int rtype = CV_64F; @@ -3901,19 +3971,22 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, if( !(flags & CALIB_RATIONAL_MODEL) && (!(flags & CALIB_THIN_PRISM_MODEL)) && - (!(flags & CALIB_TILTED_MODEL))) + (!(flags & CALIB_TILTED_MODEL)) ) { distCoeffs1 = distCoeffs1.rows == 1 ? distCoeffs1.colRange(0, 5) : distCoeffs1.rowRange(0, 5); distCoeffs2 = distCoeffs2.rows == 1 ? distCoeffs2.colRange(0, 5) : distCoeffs2.rowRange(0, 5); } - if((flags & CALIB_USE_EXTRINSIC_GUESS) == 0) + if( (flags & CALIB_USE_EXTRINSIC_GUESS) == 0 ) { _Rmat.create(3, 3, rtype); _Tmat.create(3, 1, rtype); } - Mat objPt, imgPt, imgPt2, npoints; + int nimages = int(_objectPoints.total()); + CV_Assert( nimages > 0 ); + + Mat objPt, imgPt, imgPt2, npoints, rvecLM, tvecLM; collectCalibrationData( _objectPoints, _imagePoints1, _imagePoints2, objPt, imgPt, &imgPt2, npoints ); @@ -3923,7 +3996,7 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, Mat matR_ = _Rmat.getMat(), matT_ = _Tmat.getMat(); CvMat c_matR = cvMat(matR_), c_matT = cvMat(matT_), c_matE, c_matF, c_matErr; - bool E_needed = _Emat.needed(), F_needed = _Fmat.needed(), errors_needed = _perViewErrors.needed(); + bool E_needed = _Emat.needed(), F_needed = _Fmat.needed(), rvecs_needed = _rvecs.needed(), tvecs_needed = _tvecs.needed(), errors_needed = _perViewErrors.needed(); Mat matE_, matF_, matErr_; if( E_needed ) @@ -3939,9 +4012,31 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, c_matF = cvMat(matF_); } + bool rvecs_mat_vec = _rvecs.isMatVector(); + bool tvecs_mat_vec = _tvecs.isMatVector(); + + if( rvecs_needed ) + { + _rvecs.create(nimages, 1, CV_64FC3); + + if( rvecs_mat_vec ) + rvecLM.create(nimages, 3, CV_64F); + else + rvecLM = _rvecs.getMat(); + } + if( tvecs_needed ) + { + _tvecs.create(nimages, 1, CV_64FC3); + + if( tvecs_mat_vec ) + tvecLM.create(nimages, 3, CV_64F); + else + tvecLM = _tvecs.getMat(); + } + CvMat c_rvecLM = cvMat(rvecLM), c_tvecLM = cvMat(tvecLM); + if( errors_needed ) { - int nimages = int(_objectPoints.total()); _perViewErrors.create(nimages, 2, CV_64F); matErr_ = _perViewErrors.getMat(); c_matErr = cvMat(matErr_); @@ -3950,6 +4045,7 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, double err = cvStereoCalibrateImpl(&c_objPt, &c_imgPt, &c_imgPt2, &c_npoints, &c_cameraMatrix1, &c_distCoeffs1, &c_cameraMatrix2, &c_distCoeffs2, cvSize(imageSize), &c_matR, &c_matT, E_needed ? &c_matE : NULL, F_needed ? &c_matF : NULL, + rvecs_needed ? &c_rvecLM : NULL, tvecs_needed ? &c_tvecLM : NULL, errors_needed ? &c_matErr : NULL, flags, cvTermCriteria(criteria)); cameraMatrix1.copyTo(_cameraMatrix1); @@ -3957,6 +4053,22 @@ double cv::stereoCalibrate( InputArrayOfArrays _objectPoints, distCoeffs1.copyTo(_distCoeffs1); distCoeffs2.copyTo(_distCoeffs2); + for(int i = 0; i < nimages; i++ ) + { + if( rvecs_needed && rvecs_mat_vec ) + { + _rvecs.create(3, 1, CV_64F, i, true); + Mat rv = _rvecs.getMat(i); + memcpy(rv.ptr(), rvecLM.ptr(i), 3*sizeof(double)); + } + if( tvecs_needed && tvecs_mat_vec ) + { + _tvecs.create(3, 1, CV_64F, i, true); + Mat tv = _tvecs.getMat(i); + memcpy(tv.ptr(), tvecLM.ptr(i), 3*sizeof(double)); + } + } + return err; } From 6fb465cb4e77d2ce658e4350b38517d287d19080 Mon Sep 17 00:00:00 2001 From: Stefan Spiss Date: Wed, 7 Sep 2022 17:11:12 +0200 Subject: [PATCH 089/313] Extended stereoCalibrate function for fisheye model to return per view rotation and translation vectors between the calibration object coordinate space and the coordinate space of the first camera of the stereo pair. Added overloaded versions of the function for downward compatibility. --- modules/calib3d/include/opencv2/calib3d.hpp | 15 +++++++++++ modules/calib3d/src/fisheye.cpp | 28 +++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 176a2dfc5f..b8634bde60 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -3995,6 +3995,15 @@ optimization. It is the \f$max(width,height)/\pi\f$ or the provided \f$f_x\f$, \ @param imageSize Size of the image used only to initialize camera intrinsic matrix. @param R Output rotation matrix between the 1st and the 2nd camera coordinate systems. @param T Output translation vector between the coordinate systems of the cameras. + @param rvecs Output vector of rotation vectors ( @ref Rodrigues ) estimated for each pattern view in the + coordinate system of the first camera of the stereo pair (e.g. std::vector). More in detail, each + i-th rotation vector together with the corresponding i-th translation vector (see the next output parameter + description) brings the calibration pattern from the object coordinate space (in which object points are + specified) to the camera coordinate space of the first camera of the stereo pair. In more technical terms, + the tuple of the i-th rotation and translation vector performs a change of basis from object coordinate space + to camera coordinate space of the first camera of the stereo pair. + @param tvecs Output vector of translation vectors estimated for each pattern view, see parameter description + of previous output parameter ( rvecs ). @param flags Different flags that may be zero or a combination of the following values: - @ref fisheye::CALIB_FIX_INTRINSIC Fix K1, K2? and D1, D2? so that only R, T matrices are estimated. @@ -4009,6 +4018,12 @@ optimization. It is the \f$max(width,height)/\pi\f$ or the provided \f$f_x\f$, \ zero. @param criteria Termination criteria for the iterative optimization algorithm. */ + CV_EXPORTS_W double stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, + InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize, + OutputArray R, OutputArray T, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags = fisheye::CALIB_FIX_INTRINSIC, + TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, DBL_EPSILON)); + + /// @overload CV_EXPORTS_W double stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize, OutputArray R, OutputArray T, int flags = fisheye::CALIB_FIX_INTRINSIC, diff --git a/modules/calib3d/src/fisheye.cpp b/modules/calib3d/src/fisheye.cpp index 346638d4c1..c150d10d63 100644 --- a/modules/calib3d/src/fisheye.cpp +++ b/modules/calib3d/src/fisheye.cpp @@ -885,6 +885,13 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize, OutputArray R, OutputArray T, int flags, TermCriteria criteria) +{ + return cv::fisheye::stereoCalibrate(objectPoints, imagePoints1, imagePoints2, K1, D1, K2, D2, imageSize, R, T, noArray(), noArray(), flags, criteria); +} + +double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, + InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize, + OutputArray R, OutputArray T, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags, TermCriteria criteria) { CV_INSTRUMENT_REGION(); @@ -1116,6 +1123,27 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO if (D2.needed()) cv::Mat(intrinsicRight.k).convertTo(D2, D2.empty() ? CV_64FC1 : D2.type()); if (R.needed()) _R.convertTo(R, R.empty() ? CV_64FC1 : R.type()); if (T.needed()) cv::Mat(Tcur).convertTo(T, T.empty() ? CV_64FC1 : T.type()); + if (rvecs.isMatVector()) + { + if(rvecs.empty()) + rvecs.create(n_images, 1, CV_64FC3); + + if(tvecs.empty()) + tvecs.create(n_images, 1, CV_64FC3); + + for(int i = 0; i < n_images; i++ ) + { + rvecs.create(3, 1, CV_64F, i, true); + tvecs.create(3, 1, CV_64F, i, true); + memcpy(rvecs.getMat(i).ptr(), rvecs1[i].val, sizeof(Vec3d)); + memcpy(tvecs.getMat(i).ptr(), tvecs1[i].val, sizeof(Vec3d)); + } + } + else + { + if (rvecs.needed()) cv::Mat(rvecs1).convertTo(rvecs, rvecs.empty() ? CV_64FC3 : rvecs.type()); + if (tvecs.needed()) cv::Mat(tvecs1).convertTo(tvecs, tvecs.empty() ? CV_64FC3 : tvecs.type()); + } return rms; } From 9ca3a3139a54a5aef9e08fa525c9bfd243536460 Mon Sep 17 00:00:00 2001 From: Stefan Spiss Date: Thu, 15 Sep 2022 11:54:31 +0200 Subject: [PATCH 090/313] Extended tests for stereoCalibrate function of pinhole camera model. --- .../calib3d/test/test_cameracalibration.cpp | 165 ++++++++++++++++-- 1 file changed, 155 insertions(+), 10 deletions(-) diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp index 27e0e83b8f..ee7c2510db 100644 --- a/modules/calib3d/test/test_cameracalibration.cpp +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -1237,7 +1237,10 @@ protected: Mat& cameraMatrix1, Mat& distCoeffs1, Mat& cameraMatrix2, Mat& distCoeffs2, Size imageSize, Mat& R, Mat& T, - Mat& E, Mat& F, TermCriteria criteria, int flags ) = 0; + Mat& E, Mat& F, + std::vector& rotationMatrices, std::vector& translationVectors, + vector& perViewErrors1, vector& perViewErrors2, + TermCriteria criteria, int flags ) = 0; virtual void rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, const Mat& cameraMatrix2, const Mat& distCoeffs2, Size imageSize, const Mat& R, const Mat& T, @@ -1253,6 +1256,8 @@ protected: virtual void correct( const Mat& F, const Mat &points1, const Mat &points2, Mat &newPoints1, Mat &newPoints2 ) = 0; + int compare(double* val, double* refVal, int len, + double eps, const char* paramName); void run(int); }; @@ -1319,12 +1324,19 @@ bool CV_StereoCalibrationTest::checkPandROI( int test_case_idx, const Mat& M, co return true; } +int CV_StereoCalibrationTest::compare(double* val, double* ref_val, int len, + double eps, const char* param_name ) +{ + return cvtest::cmpEps2_64f( ts, val, ref_val, len, eps, param_name ); +} + void CV_StereoCalibrationTest::run( int ) { const int ntests = 1; const double maxReprojErr = 2; const double maxScanlineDistErr_c = 3; const double maxScanlineDistErr_uc = 4; + const double maxDiffBtwRmsErrors = 1e-4; FILE* f = 0; for(int testcase = 1; testcase <= ntests; testcase++) @@ -1401,13 +1413,23 @@ void CV_StereoCalibrationTest::run( int ) objpt[i].push_back(Point3f((float)(j%patternSize.width), (float)(j/patternSize.width), 0.f)); } + vector rotMats1(nframes); + vector transVecs1(nframes); + vector rotMats2(nframes); + vector transVecs2(nframes); + vector rmsErrorPerView1(nframes); + vector rmsErrorPerView2(nframes); + vector rmsErrorPerViewFromReprojectedImgPts1(nframes); + vector rmsErrorPerViewFromReprojectedImgPts2(nframes); + // rectify (calibrated) Mat M1 = Mat::eye(3,3,CV_64F), M2 = Mat::eye(3,3,CV_64F), D1(5,1,CV_64F), D2(5,1,CV_64F), R, T, E, F; M1.at(0,2) = M2.at(0,2)=(imgsize.width-1)*0.5; M1.at(1,2) = M2.at(1,2)=(imgsize.height-1)*0.5; D1 = Scalar::all(0); D2 = Scalar::all(0); - double err = calibrateStereoCamera(objpt, imgpt1, imgpt2, M1, D1, M2, D2, imgsize, R, T, E, F, + double rmsErrorFromStereoCalib = calibrateStereoCamera(objpt, imgpt1, imgpt2, M1, D1, M2, D2, imgsize, R, T, E, F, + rotMats1, transVecs1, rmsErrorPerView1, rmsErrorPerView2, TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 30, 1e-6), CV_CALIB_SAME_FOCAL_LENGTH //+ CV_CALIB_FIX_ASPECT_RATIO @@ -1416,11 +1438,89 @@ void CV_StereoCalibrationTest::run( int ) + CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5 //+ CV_CALIB_FIX_K6 ); - err /= nframes*npoints; - if( err > maxReprojErr ) + /* rmsErrorFromStereoCalib /= nframes*npoints; */ + if (rmsErrorFromStereoCalib > maxReprojErr) { - ts->printf( cvtest::TS::LOG, "The average reprojection error is too big (=%g), testcase %d\n", err, testcase); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + ts->printf(cvtest::TS::LOG, "The average reprojection error is too big (=%g), testcase %d\n", + rmsErrorFromStereoCalib, testcase); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return; + } + + double rmsErrorFromReprojectedImgPts = 0.0f; + if (rotMats1.empty() || transVecs1.empty()) + { + rmsErrorPerViewFromReprojectedImgPts1 = rmsErrorPerView1; + rmsErrorPerViewFromReprojectedImgPts2 = rmsErrorPerView2; + rmsErrorFromReprojectedImgPts = rmsErrorFromStereoCalib; + } + else + { + vector reprojectedImgPts[2] = {vector(nframes), vector(nframes)}; + size_t totalPoints = 0; + double totalErr[2] = { 0, 0 }, viewErr[2]; + for (size_t i = 0; i < objpt.size(); ++i) { + RotMat r1 = rotMats1[i]; + Vec3d t1 = transVecs1[i]; + + RotMat r2 = Mat(R * r1); + Mat T2t = R * t1; + Vec3d t2 = Mat(T2t + T); + + projectPoints(objpt[i], r1, t1, M1, D1, reprojectedImgPts[0]); + projectPoints(objpt[i], r2, t2, M2, D2, reprojectedImgPts[1]); + + viewErr[0] = cv::norm(imgpt1[i], reprojectedImgPts[0], cv::NORM_L2SQR); + viewErr[1] = cv::norm(imgpt2[i], reprojectedImgPts[1], cv::NORM_L2SQR); + + size_t n = objpt[i].size(); + totalErr[0] += viewErr[0]; + totalErr[1] += viewErr[1]; + totalPoints += n; + + rmsErrorPerViewFromReprojectedImgPts1[i] = sqrt(viewErr[0] / n); + rmsErrorPerViewFromReprojectedImgPts2[i] = sqrt(viewErr[1] / n); + } + rmsErrorFromReprojectedImgPts = std::sqrt((totalErr[0] + totalErr[1]) / (2 * totalPoints)); + + } + + if (abs(rmsErrorFromStereoCalib - rmsErrorFromReprojectedImgPts) > maxDiffBtwRmsErrors) + { + ts->printf(cvtest::TS::LOG, + "The difference of the average reprojection error from the calibration function and from the " + "reprojected image points is too big (|%g - %g| = %g), testcase %d\n", + rmsErrorFromStereoCalib, rmsErrorFromReprojectedImgPts, + (rmsErrorFromStereoCalib - rmsErrorFromReprojectedImgPts), testcase); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return; + } + + /* ----- Compare per view rms re-projection errors ----- */ + CV_Assert(rmsErrorPerView1.size() == (size_t)nframes); + CV_Assert(rmsErrorPerViewFromReprojectedImgPts1.size() == (size_t)nframes); + CV_Assert(rmsErrorPerView2.size() == (size_t)nframes); + CV_Assert(rmsErrorPerViewFromReprojectedImgPts2.size() == (size_t)nframes); + int code1 = compare(&rmsErrorPerView1[0], &rmsErrorPerViewFromReprojectedImgPts1[0], nframes, + maxDiffBtwRmsErrors, "per view errors vector"); + int code2 = compare(&rmsErrorPerView2[0], &rmsErrorPerViewFromReprojectedImgPts2[0], nframes, + maxDiffBtwRmsErrors, "per view errors vector"); + if (code1 < 0) + { + ts->printf(cvtest::TS::LOG, + "Some of the per view rms reprojection errors differ between calibration function and reprojected " + "points, for the first camera, testcase %d\n", + testcase); + ts->set_failed_test_info(code1); + return; + } + if (code2 < 0) + { + ts->printf(cvtest::TS::LOG, + "Some of the per view rms reprojection errors differ between calibration function and reprojected " + "points, for the second camera, testcase %d\n", + testcase); + ts->set_failed_test_info(code2); return; } @@ -1640,7 +1740,10 @@ protected: Mat& cameraMatrix1, Mat& distCoeffs1, Mat& cameraMatrix2, Mat& distCoeffs2, Size imageSize, Mat& R, Mat& T, - Mat& E, Mat& F, TermCriteria criteria, int flags ); + Mat& E, Mat& F, + std::vector& rotationMatrices, std::vector& translationVectors, + vector& perViewErrors1, vector& perViewErrors2, + TermCriteria criteria, int flags ); virtual void rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, const Mat& cameraMatrix2, const Mat& distCoeffs2, Size imageSize, const Mat& R, const Mat& T, @@ -1664,11 +1767,53 @@ double CV_StereoCalibrationTest_CPP::calibrateStereoCamera( const vector& rotationMatrices, std::vector& translationVectors, + vector& perViewErrors1, vector& perViewErrors2, + TermCriteria criteria, int flags ) { - return stereoCalibrate( objectPoints, imagePoints1, imagePoints2, + vector rvecs, tvecs; + Mat perViewErrorsMat; + double avgErr = stereoCalibrate( objectPoints, imagePoints1, imagePoints2, cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, - imageSize, R, T, E, F, flags, criteria ); + imageSize, R, T, E, F, + rvecs, tvecs, perViewErrorsMat, + flags, criteria ); + + size_t numImgs = imagePoints1.size(); + + if (perViewErrors1.size() != numImgs) + { + perViewErrors1.resize(numImgs); + } + if (perViewErrors2.size() != numImgs) + { + perViewErrors2.resize(numImgs); + } + + for (size_t i = 0; i(i, 0); + perViewErrors2[i] = perViewErrorsMat.at(i, 1); + } + + if (rotationMatrices.size() != numImgs) + { + rotationMatrices.resize(numImgs); + } + if (translationVectors.size() != numImgs) + { + translationVectors.resize(numImgs); + } + + for( size_t i = 0; i < numImgs; i++ ) + { + Mat r9; + cv::Rodrigues( rvecs[i], r9 ); + r9.convertTo(rotationMatrices[i], CV_64F); + tvecs[i].convertTo(translationVectors[i], CV_64F); + } + return avgErr; } void CV_StereoCalibrationTest_CPP::rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, From 66cbb7b9111705f1b555f0fbd741c34821702209 Mon Sep 17 00:00:00 2001 From: Stefan Spiss Date: Thu, 15 Sep 2022 11:55:25 +0200 Subject: [PATCH 091/313] Extended tests for stereoCalibrate function of fisheye camera model. --- modules/calib3d/test/test_fisheye.cpp | 105 ++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/modules/calib3d/test/test_fisheye.cpp b/modules/calib3d/test/test_fisheye.cpp index 23cfa98889..fe4100d831 100644 --- a/modules/calib3d/test/test_fisheye.cpp +++ b/modules/calib3d/test/test_fisheye.cpp @@ -861,6 +861,111 @@ TEST_F(fisheyeTest, CalibrationWithDifferentPointsNumber) cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6)); } +TEST_F(fisheyeTest, stereoCalibrateWithPerViewTransformations) +{ + const int n_images = 34; + + const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY"); + + std::vector > leftPoints(n_images); + std::vector > rightPoints(n_images); + std::vector > objectPoints(n_images); + + cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); + CV_Assert(fs_left.isOpened()); + for(int i = 0; i < n_images; ++i) + fs_left[cv::format("image_%d", i )] >> leftPoints[i]; + fs_left.release(); + + cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ); + CV_Assert(fs_right.isOpened()); + for(int i = 0; i < n_images; ++i) + fs_right[cv::format("image_%d", i )] >> rightPoints[i]; + fs_right.release(); + + cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); + CV_Assert(fs_object.isOpened()); + for(int i = 0; i < n_images; ++i) + fs_object[cv::format("image_%d", i )] >> objectPoints[i]; + fs_object.release(); + + cv::Matx33d K1, K2, theR; + cv::Vec3d theT; + cv::Vec4d D1, D2; + + std::vector rvecs, tvecs; + + int flag = 0; + flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; + flag |= cv::fisheye::CALIB_CHECK_COND; + flag |= cv::fisheye::CALIB_FIX_SKEW; + + double rmsErrorStereoCalib = cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints, + K1, D1, K2, D2, imageSize, theR, theT, rvecs, tvecs, flag, + cv::TermCriteria(3, 12, 0)); + + std::vector reprojectedImgPts[2] = {std::vector(n_images), std::vector(n_images)}; + size_t totalPoints = 0; + float totalMSError[2] = { 0, 0 }, viewMSError[2]; + for( size_t i = 0; i < n_images; i++ ) + { + cv::Matx33d viewRotMat1, viewRotMat2; + cv::Vec3d viewT1, viewT2; + cv::Mat rVec; + cv::Rodrigues( rvecs[i], rVec ); + rVec.convertTo(viewRotMat1, CV_64F); + tvecs[i].convertTo(viewT1, CV_64F); + + viewRotMat2 = theR * viewRotMat1; + cv::Vec3d T2t = theR * viewT1; + viewT2 = T2t + theT; + + cv::Vec3d viewRotVec1, viewRotVec2; + cv::Rodrigues(viewRotMat1, viewRotVec1); + cv::Rodrigues(viewRotMat2, viewRotVec2); + + double alpha1 = K1(0, 1) / K1(0, 0); + double alpha2 = K2(0, 1) / K2(0, 0); + cv::fisheye::projectPoints(objectPoints[i], reprojectedImgPts[0], viewRotVec1, viewT1, K1, D1, alpha1); + cv::fisheye::projectPoints(objectPoints[i], reprojectedImgPts[1], viewRotVec2, viewT2, K2, D2, alpha2); + + viewMSError[0] = cv::norm(leftPoints[i], reprojectedImgPts[0], cv::NORM_L2SQR); + viewMSError[1] = cv::norm(rightPoints[i], reprojectedImgPts[1], cv::NORM_L2SQR); + + size_t n = objectPoints[i].size(); + totalMSError[0] += viewMSError[0]; + totalMSError[1] += viewMSError[1]; + totalPoints += n; + } + double rmsErrorFromReprojectedImgPts = std::sqrt((totalMSError[0] + totalMSError[1]) / (2 * totalPoints)); + + cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523, + -0.06956823121068059, 0.9975601387249519, 0.005833595226966235, + -0.006071257768382089, -0.006271040135405457, 0.9999619062167968); + cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699); + cv::Matx33d K1_correct (561.195925927249, 0, 621.282400272412, + 0, 562.849402029712, 380.555455380889, + 0, 0, 1); + + cv::Matx33d K2_correct (560.395452535348, 0, 678.971652040359, + 0, 561.90171021422, 380.401340535339, + 0, 0, 1); + + cv::Vec4d D1_correct (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771); + cv::Vec4d D2_correct (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222); + + EXPECT_MAT_NEAR(theR, R_correct, 1e-10); + EXPECT_MAT_NEAR(theT, T_correct, 1e-10); + + EXPECT_MAT_NEAR(K1, K1_correct, 1e-10); + EXPECT_MAT_NEAR(K2, K2_correct, 1e-10); + + EXPECT_MAT_NEAR(D1, D1_correct, 1e-10); + EXPECT_MAT_NEAR(D2, D2_correct, 1e-10); + + EXPECT_NEAR(rmsErrorStereoCalib, rmsErrorFromReprojectedImgPts, 1e-4); +} + TEST_F(fisheyeTest, estimateNewCameraMatrixForUndistortRectify) { cv::Size size(1920, 1080); From dc9d775f88f21aac47537ec0ef71b4d0256389dd Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 15 Sep 2022 16:15:52 +0300 Subject: [PATCH 092/313] Do not store full CuDNN version in cache to exclude inconsistency during reconfiguration. --- cmake/FindCUDNN.cmake | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmake/FindCUDNN.cmake b/cmake/FindCUDNN.cmake index 195781b957..df64db39e6 100644 --- a/cmake/FindCUDNN.cmake +++ b/cmake/FindCUDNN.cmake @@ -78,12 +78,7 @@ if(CUDNN_INCLUDE_DIR) string(REGEX MATCH "define CUDNN_PATCHLEVEL ([0-9]+)" _ "${CUDNN_H_CONTENTS}") set(CUDNN_VERSION_PATCH ${CMAKE_MATCH_1} CACHE INTERNAL "") - set(CUDNN_VERSION - "${CUDNN_VERSION_MAJOR}.${CUDNN_VERSION_MINOR}.${CUDNN_VERSION_PATCH}" - CACHE - STRING - "cuDNN version" - ) + set(CUDNN_VERSION "${CUDNN_VERSION_MAJOR}.${CUDNN_VERSION_MINOR}.${CUDNN_VERSION_PATCH}") unset(CUDNN_H_CONTENTS) endif() From 48e50a76743c85269778406fc328ad930bc0a456 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 15 Sep 2022 14:29:50 +0300 Subject: [PATCH 093/313] Extended video timestamp test to cover fix for the issue #22141. --- modules/videoio/test/test_video_io.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 6661b13c5a..c73a292d0b 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -215,8 +215,28 @@ public: throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) + cv::String(" can't open the video: ") + video_file); + int frame_count = (int)cap.get(CAP_PROP_FRAME_COUNT); + + // HACK: Video consists of 125 frames, but cv::VideoCapture with FFmpeg reports only 122 frames for mpg video. + // mpg file reports 5.08 sec * 24 fps => property returns 122 frames,but actual number of frames returned is 125 + // HACK: CAP_PROP_FRAME_COUNT is not supported for vmw + MSMF. Just force check for all 125 frames + if (ext == "mpg") + EXPECT_GT(frame_count, 121); + else if ((ext == "wmv") && (apiPref == CAP_MSMF)) + frame_count = 125; + else + EXPECT_EQ(frame_count, 125); Mat img; - for(int i = 0; i < 10; i++) + +#ifdef _WIN32 // handle old FFmpeg wrapper on Windows till rebuild + frame_count = 10; +#else + // HACK: FFmpeg reports picture_pts = AV_NOPTS_VALUE_ for the last frame for AVI container by some reason + if ((ext == "avi") && (apiPref == CAP_FFMPEG)) + frame_count--; +#endif + + for (int i = 0; i < frame_count; i++) { double timestamp = 0; ASSERT_NO_THROW(cap >> img); From a3cb2020bc2c35f6af2dc5a347e04ca547f3d9d0 Mon Sep 17 00:00:00 2001 From: scottchou007 <48142313+scottchou007@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:37:46 -0700 Subject: [PATCH 094/313] Fix issues in opencv_test_dnn from conv48 kernels using uninitialized tensors when there is no bias. --- .../dnn/src/vkcom/shader/conv48_nobias.comp | 134 +++ .../src/vkcom/shader/conv48_nobias_spv.cpp | 913 ++++++++++++++++++ modules/dnn/src/vkcom/shader/spv_shader.hpp | 1 + modules/dnn/src/vkcom/src/op_conv.cpp | 3 +- 4 files changed, 1050 insertions(+), 1 deletion(-) create mode 100644 modules/dnn/src/vkcom/shader/conv48_nobias.comp create mode 100644 modules/dnn/src/vkcom/shader/conv48_nobias_spv.cpp diff --git a/modules/dnn/src/vkcom/shader/conv48_nobias.comp b/modules/dnn/src/vkcom/shader/conv48_nobias.comp new file mode 100644 index 0000000000..cb26fc716f --- /dev/null +++ b/modules/dnn/src/vkcom/shader/conv48_nobias.comp @@ -0,0 +1,134 @@ +#version 450 + +layout (constant_id = 0) const int LOCAL_SZ_X = 0; +layout (constant_id = 1) const int LOCAL_SZ_Y = 0; +layout (constant_id = 2) const int LOCAL_SZ_Z = 0; +layout (constant_id = 3) const int IN_H = 0; +layout (constant_id = 4) const int IN_W = 0; +layout (constant_id = 5) const int OUT_W = 0; +layout (constant_id = 6) const int STRIDE_H = 0; +layout (constant_id = 7) const int STRIDE_W = 0; +layout (constant_id = 8) const int PAD_H = 0; +layout (constant_id = 9) const int PAD_W = 0; +layout (constant_id = 10) const int FILTER_H = 0; +layout (constant_id = 11) const int FILTER_W = 0; +layout (constant_id = 12) const int CHANNELS = 0; +layout (constant_id = 13) const int BATCH = 0; +layout (constant_id = 14) const int M = 0; +layout (constant_id = 15) const int K = 0; +layout (constant_id = 16) const int N = 0; +layout (constant_id = 17) const int TAIL_M = 0; +layout (constant_id = 18) const int DILATION_H = 0; +layout (constant_id = 19) const int DILATION_W = 0; + +#if defined(ACTIVATION_RELU) +#define ACTIVATION_FUNCTION(x) clamp(x, vec4(0.0), vec4(999999999.0)) +#elif defined(ACTIVATION_RELU1) +#define ACTIVATION_FUNCTION(x) clamp(x, vec4(-1.0), vec4(1.0)) +#elif defined(ACTIVATION_RELU6) +#define ACTIVATION_FUNCTION(x) clamp(x, vec4(0.0), vec4(6.0)) +#else +#define ACTIVATION_FUNCTION(x) (x) +#endif + +layout(binding = 0) readonly buffer Input0{ + float data[]; +} src0; +layout(binding = 1) readonly buffer Input1 { + vec4 data[]; +} bias; +layout(binding = 2) readonly buffer Input3{ + vec4 data[]; +} src1; +layout(binding = 3) writeonly buffer Output{ + vec4 data[]; +} out0; + +layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; + +#define VEC_SIZE 4 +#define BLOCK_H 4 +#define BLOCK_W 8 +#define FILTER_AREA (FILTER_H * FILTER_W) +#define LOAD_A(elm_idx, a_component) \ + src0_x = org_x + ((i * VEC_SIZE + elm_idx) % FILTER_W) * DILATION_W; \ + src0_y = org_y + (((i * VEC_SIZE + elm_idx) % FILTER_AREA) / FILTER_W) * DILATION_H; \ + src0_z = (i * VEC_SIZE + elm_idx) / FILTER_AREA; \ + if(src0_y >= 0 && src0_y < IN_H && src0_x >= 0 && src0_x < IN_W) \ + { \ + a_component = src0.data[input_batch_offset + src0_z * (IN_H * IN_W) + src0_y * IN_W + src0_x]; \ + } + +#define A_MULTIPLY_BTILE(a, sliver_num, comp) \ + dst_x = (out_y + sliver_num) % OUT_W; \ + dst_y = (out_y + sliver_num) / OUT_W; \ + org_y = dst_y * STRIDE_H - PAD_H; \ + org_x = dst_x * STRIDE_W - PAD_W; \ + LOAD_A(0, a.x); \ + LOAD_A(1, a.y); \ + LOAD_A(2, a.z); \ + LOAD_A(3, a.w); \ + dot0.comp += dot(brow0, a); \ + dot1.comp += dot(brow1, a); \ + dot2.comp += dot(brow2, a); \ + dot3.comp += dot(brow3, a); \ + dot4.comp += dot(brow4, a); \ + dot5.comp += dot(brow5, a); \ + dot6.comp += dot(brow6, a); \ + dot7.comp += dot(brow7, a); + +void main() +{ + int gx = int(gl_GlobalInvocationID.x); + int gy = int(gl_GlobalInvocationID.y); + int gz = int(gl_GlobalInvocationID.z); + int out_x = BLOCK_W * gx; + int out_y = BLOCK_H * gy; + int input_batch_offset = gz * IN_H * IN_W * CHANNELS; + int output_batch_offset = gz * M * N / VEC_SIZE; + if (out_x < N && gy < M / BLOCK_H) + { + int width0 = K / VEC_SIZE; + int width1 = N / VEC_SIZE; + int src1_read0_offset = out_x * width0; + vec4 dot0 = vec4(0.f); + vec4 dot1 = vec4(0.f); + vec4 dot2 = vec4(0.f); + vec4 dot3 = vec4(0.f); + vec4 dot4 = vec4(0.f); + vec4 dot5 = vec4(0.f); + vec4 dot6 = vec4(0.f); + vec4 dot7 = vec4(0.f); + int i = 0; + do + { + int dst_x, dst_y, org_x, org_y, src0_x, src0_y, src0_z; + vec4 a0 = vec4(0.f), a1 = vec4(0.f), a2 = vec4(0.f), a3 = vec4(0.f); + vec4 brow0 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow1 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow2 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow3 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow4 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow5 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow6 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + vec4 brow7 = src1.data[src1_read0_offset]; src1_read0_offset += width0; + src1_read0_offset += 1 - BLOCK_W * width0; + + A_MULTIPLY_BTILE(a0, 0, x); + A_MULTIPLY_BTILE(a1, 1, y); + A_MULTIPLY_BTILE(a2, 2, z); + A_MULTIPLY_BTILE(a3, 3, w); + i++; + } + while( i < width0 ); + + out0.data[output_batch_offset + (out_x + 0) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot0); + out0.data[output_batch_offset + (out_x + 1) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot1); + out0.data[output_batch_offset + (out_x + 2) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot2); + out0.data[output_batch_offset + (out_x + 3) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot3); + out0.data[output_batch_offset + (out_x + 4) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot4); + out0.data[output_batch_offset + (out_x + 5) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot5); + out0.data[output_batch_offset + (out_x + 6) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot6); + out0.data[output_batch_offset + (out_x + 7) * M / VEC_SIZE + gy] = ACTIVATION_FUNCTION(dot7); + } +} diff --git a/modules/dnn/src/vkcom/shader/conv48_nobias_spv.cpp b/modules/dnn/src/vkcom/shader/conv48_nobias_spv.cpp new file mode 100644 index 0000000000..2f35417e02 --- /dev/null +++ b/modules/dnn/src/vkcom/shader/conv48_nobias_spv.cpp @@ -0,0 +1,913 @@ +// 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. +// +// Copyright (C) 2018, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "../../precomp.hpp" + +namespace cv { namespace dnn { namespace vkcom { + +extern const unsigned int conv48_nobias_spv[7182] = { + 0x07230203,0x00010000,0x0008000a,0x00000523,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x0000000c,0x00060010,0x00000004, + 0x00000011,0x00000001,0x00000001,0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005, + 0x00000004,0x6e69616d,0x00000000,0x00030005,0x00000008,0x00007867,0x00080005,0x0000000c, + 0x475f6c67,0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00030005,0x00000012, + 0x00007967,0x00030005,0x00000017,0x00007a67,0x00040005,0x0000001c,0x5f74756f,0x00000078, + 0x00040005,0x00000020,0x5f74756f,0x00000079,0x00070005,0x00000024,0x75706e69,0x61625f74, + 0x5f686374,0x7366666f,0x00007465,0x00040005,0x00000026,0x485f4e49,0x00000000,0x00040005, + 0x00000028,0x575f4e49,0x00000000,0x00050005,0x0000002a,0x4e414843,0x534c454e,0x00000000, + 0x00070005,0x0000002c,0x7074756f,0x625f7475,0x68637461,0x66666f5f,0x00746573,0x00030005, + 0x0000002e,0x0000004d,0x00030005,0x00000030,0x0000004e,0x00040005,0x0000003e,0x74646977, + 0x00003068,0x00030005,0x0000003f,0x0000004b,0x00040005,0x00000041,0x74646977,0x00003168, + 0x00070005,0x00000043,0x31637273,0x6165725f,0x6f5f3064,0x65736666,0x00000074,0x00040005, + 0x0000004a,0x30746f64,0x00000000,0x00040005,0x0000004d,0x31746f64,0x00000000,0x00040005, + 0x0000004e,0x32746f64,0x00000000,0x00040005,0x0000004f,0x33746f64,0x00000000,0x00040005, + 0x00000050,0x34746f64,0x00000000,0x00040005,0x00000051,0x35746f64,0x00000000,0x00040005, + 0x00000052,0x36746f64,0x00000000,0x00040005,0x00000053,0x37746f64,0x00000000,0x00030005, + 0x00000054,0x00000069,0x00030005,0x0000005a,0x00003061,0x00030005,0x0000005b,0x00003161, + 0x00030005,0x0000005c,0x00003261,0x00030005,0x0000005d,0x00003361,0x00040005,0x0000005e, + 0x776f7262,0x00000030,0x00040005,0x00000060,0x75706e49,0x00003374,0x00050006,0x00000060, + 0x00000000,0x61746164,0x00000000,0x00040005,0x00000062,0x31637273,0x00000000,0x00040005, + 0x0000006a,0x776f7262,0x00000031,0x00040005,0x00000071,0x776f7262,0x00000032,0x00040005, + 0x00000078,0x776f7262,0x00000033,0x00040005,0x0000007f,0x776f7262,0x00000034,0x00040005, + 0x00000086,0x776f7262,0x00000035,0x00040005,0x0000008d,0x776f7262,0x00000036,0x00040005, + 0x00000094,0x776f7262,0x00000037,0x00040005,0x000000a1,0x5f747364,0x00000078,0x00040005, + 0x000000a4,0x5f54554f,0x00000057,0x00040005,0x000000a6,0x5f747364,0x00000079,0x00040005, + 0x000000aa,0x5f67726f,0x00000079,0x00050005,0x000000ac,0x49525453,0x485f4544,0x00000000, + 0x00040005,0x000000ae,0x5f444150,0x00000048,0x00040005,0x000000b0,0x5f67726f,0x00000078, + 0x00050005,0x000000b2,0x49525453,0x575f4544,0x00000000,0x00040005,0x000000b4,0x5f444150, + 0x00000057,0x00040005,0x000000b6,0x30637273,0x0000785f,0x00050005,0x000000bb,0x544c4946, + 0x575f5245,0x00000000,0x00050005,0x000000bd,0x414c4944,0x4e4f4954,0x0000575f,0x00040005, + 0x000000c0,0x30637273,0x0000795f,0x00050005,0x000000c5,0x544c4946,0x485f5245,0x00000000, + 0x00050005,0x000000c9,0x414c4944,0x4e4f4954,0x0000485f,0x00040005,0x000000cc,0x30637273, + 0x00007a5f,0x00040005,0x000000e0,0x75706e49,0x00003074,0x00050006,0x000000e0,0x00000000, + 0x61746164,0x00000000,0x00040005,0x000000e2,0x30637273,0x00000000,0x00040005,0x000004c0, + 0x7074754f,0x00007475,0x00050006,0x000004c0,0x00000000,0x61746164,0x00000000,0x00040005, + 0x000004c2,0x3074756f,0x00000000,0x00050005,0x00000516,0x41434f4c,0x5a535f4c,0x0000585f, + 0x00050005,0x00000517,0x41434f4c,0x5a535f4c,0x0000595f,0x00050005,0x00000518,0x41434f4c, + 0x5a535f4c,0x00005a5f,0x00040005,0x00000519,0x43544142,0x00000048,0x00040005,0x0000051a, + 0x4c494154,0x00004d5f,0x00040005,0x0000051c,0x75706e49,0x00003174,0x00050006,0x0000051c, + 0x00000000,0x61746164,0x00000000,0x00040005,0x0000051e,0x73616962,0x00000000,0x00040047, + 0x0000000c,0x0000000b,0x0000001c,0x00040047,0x00000026,0x00000001,0x00000003,0x00040047, + 0x00000028,0x00000001,0x00000004,0x00040047,0x0000002a,0x00000001,0x0000000c,0x00040047, + 0x0000002e,0x00000001,0x0000000e,0x00040047,0x00000030,0x00000001,0x00000010,0x00040047, + 0x0000003f,0x00000001,0x0000000f,0x00040047,0x0000005f,0x00000006,0x00000010,0x00040048, + 0x00000060,0x00000000,0x00000018,0x00050048,0x00000060,0x00000000,0x00000023,0x00000000, + 0x00030047,0x00000060,0x00000003,0x00040047,0x00000062,0x00000022,0x00000000,0x00040047, + 0x00000062,0x00000021,0x00000002,0x00040047,0x000000a4,0x00000001,0x00000005,0x00040047, + 0x000000ac,0x00000001,0x00000006,0x00040047,0x000000ae,0x00000001,0x00000008,0x00040047, + 0x000000b2,0x00000001,0x00000007,0x00040047,0x000000b4,0x00000001,0x00000009,0x00040047, + 0x000000bb,0x00000001,0x0000000b,0x00040047,0x000000bd,0x00000001,0x00000013,0x00040047, + 0x000000c5,0x00000001,0x0000000a,0x00040047,0x000000c9,0x00000001,0x00000012,0x00040047, + 0x000000df,0x00000006,0x00000004,0x00040048,0x000000e0,0x00000000,0x00000018,0x00050048, + 0x000000e0,0x00000000,0x00000023,0x00000000,0x00030047,0x000000e0,0x00000003,0x00040047, + 0x000000e2,0x00000022,0x00000000,0x00040047,0x000000e2,0x00000021,0x00000000,0x00040047, + 0x000004bf,0x00000006,0x00000010,0x00040048,0x000004c0,0x00000000,0x00000019,0x00050048, + 0x000004c0,0x00000000,0x00000023,0x00000000,0x00030047,0x000004c0,0x00000003,0x00040047, + 0x000004c2,0x00000022,0x00000000,0x00040047,0x000004c2,0x00000021,0x00000003,0x00040047, + 0x00000516,0x00000001,0x00000000,0x00040047,0x00000517,0x00000001,0x00000001,0x00040047, + 0x00000518,0x00000001,0x00000002,0x00040047,0x00000519,0x00000001,0x0000000d,0x00040047, + 0x0000051a,0x00000001,0x00000011,0x00040047,0x0000051b,0x00000006,0x00000010,0x00040048, + 0x0000051c,0x00000000,0x00000018,0x00050048,0x0000051c,0x00000000,0x00000023,0x00000000, + 0x00030047,0x0000051c,0x00000003,0x00040047,0x0000051e,0x00000022,0x00000000,0x00040047, + 0x0000051e,0x00000021,0x00000001,0x00040047,0x0000051f,0x00000001,0x00000000,0x00040047, + 0x00000520,0x00000001,0x00000001,0x00040047,0x00000521,0x00000001,0x00000002,0x00040047, + 0x00000522,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002, + 0x00040015,0x00000006,0x00000020,0x00000001,0x00040020,0x00000007,0x00000007,0x00000006, + 0x00040015,0x00000009,0x00000020,0x00000000,0x00040017,0x0000000a,0x00000009,0x00000003, + 0x00040020,0x0000000b,0x00000001,0x0000000a,0x0004003b,0x0000000b,0x0000000c,0x00000001, + 0x0004002b,0x00000009,0x0000000d,0x00000000,0x00040020,0x0000000e,0x00000001,0x00000009, + 0x0004002b,0x00000009,0x00000013,0x00000001,0x0004002b,0x00000009,0x00000018,0x00000002, + 0x0004002b,0x00000006,0x0000001d,0x00000008,0x0004002b,0x00000006,0x00000021,0x00000004, + 0x00040032,0x00000006,0x00000026,0x00000000,0x00040032,0x00000006,0x00000028,0x00000000, + 0x00040032,0x00000006,0x0000002a,0x00000000,0x00040032,0x00000006,0x0000002e,0x00000000, + 0x00040032,0x00000006,0x00000030,0x00000000,0x00020014,0x00000033,0x00060034,0x00000006, + 0x00000039,0x00000087,0x0000002e,0x00000021,0x00040032,0x00000006,0x0000003f,0x00000000, + 0x00060034,0x00000006,0x00000040,0x00000087,0x0000003f,0x00000021,0x00060034,0x00000006, + 0x00000042,0x00000087,0x00000030,0x00000021,0x00030016,0x00000047,0x00000020,0x00040017, + 0x00000048,0x00000047,0x00000004,0x00040020,0x00000049,0x00000007,0x00000048,0x0004002b, + 0x00000047,0x0000004b,0x00000000,0x0007002c,0x00000048,0x0000004c,0x0000004b,0x0000004b, + 0x0000004b,0x0000004b,0x0004002b,0x00000006,0x00000055,0x00000000,0x0003001d,0x0000005f, + 0x00000048,0x0003001e,0x00000060,0x0000005f,0x00040020,0x00000061,0x00000002,0x00000060, + 0x0004003b,0x00000061,0x00000062,0x00000002,0x00040020,0x00000064,0x00000002,0x00000048, + 0x0004002b,0x00000006,0x0000009b,0x00000001,0x00040032,0x00000006,0x000000a4,0x00000000, + 0x00040032,0x00000006,0x000000ac,0x00000000,0x00040032,0x00000006,0x000000ae,0x00000000, + 0x00040032,0x00000006,0x000000b2,0x00000000,0x00040032,0x00000006,0x000000b4,0x00000000, + 0x00040032,0x00000006,0x000000bb,0x00000000,0x00040032,0x00000006,0x000000bd,0x00000000, + 0x00040032,0x00000006,0x000000c5,0x00000000,0x00060034,0x00000006,0x000000c6,0x00000084, + 0x000000c5,0x000000bb,0x00040032,0x00000006,0x000000c9,0x00000000,0x00060034,0x00000006, + 0x000000d0,0x00000084,0x000000c5,0x000000bb,0x0003001d,0x000000df,0x00000047,0x0003001e, + 0x000000e0,0x000000df,0x00040020,0x000000e1,0x00000002,0x000000e0,0x0004003b,0x000000e1, + 0x000000e2,0x00000002,0x00060034,0x00000006,0x000000e5,0x00000084,0x00000026,0x00000028, + 0x00040020,0x000000ed,0x00000002,0x00000047,0x00040020,0x000000f0,0x00000007,0x00000047, + 0x00060034,0x00000006,0x000000fd,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x00000105,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000116,0x00000084, + 0x00000026,0x00000028,0x0004002b,0x00000006,0x00000124,0x00000002,0x00060034,0x00000006, + 0x0000012d,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000135,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000146,0x00000084,0x00000026,0x00000028, + 0x0004002b,0x00000006,0x00000154,0x00000003,0x00060034,0x00000006,0x0000015d,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000165,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x00000176,0x00000084,0x00000026,0x00000028,0x0004002b,0x00000009, + 0x00000180,0x00000003,0x00060034,0x00000006,0x000001d1,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x000001d9,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x000001ea,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006,0x00000200,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000208,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x00000219,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006, + 0x0000022f,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000237,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000248,0x00000084,0x00000026,0x00000028, + 0x00060034,0x00000006,0x0000025e,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x00000266,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000277,0x00000084, + 0x00000026,0x00000028,0x00060034,0x00000006,0x000002d1,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x000002d9,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x000002ea,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006,0x00000300,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000308,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x00000319,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006, + 0x0000032f,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000337,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000348,0x00000084,0x00000026,0x00000028, + 0x00060034,0x00000006,0x0000035e,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x00000366,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000377,0x00000084, + 0x00000026,0x00000028,0x00060034,0x00000006,0x000003d1,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x000003d9,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x000003ea,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006,0x00000400,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000408,0x00000084,0x000000c5,0x000000bb, + 0x00060034,0x00000006,0x00000419,0x00000084,0x00000026,0x00000028,0x00060034,0x00000006, + 0x0000042f,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000437,0x00000084, + 0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000448,0x00000084,0x00000026,0x00000028, + 0x00060034,0x00000006,0x0000045e,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006, + 0x00000466,0x00000084,0x000000c5,0x000000bb,0x00060034,0x00000006,0x00000477,0x00000084, + 0x00000026,0x00000028,0x0003001d,0x000004bf,0x00000048,0x0003001e,0x000004c0,0x000004bf, + 0x00040020,0x000004c1,0x00000002,0x000004c0,0x0004003b,0x000004c1,0x000004c2,0x00000002, + 0x0004002b,0x00000006,0x000004f7,0x00000005,0x0004002b,0x00000006,0x00000502,0x00000006, + 0x0004002b,0x00000006,0x0000050d,0x00000007,0x00040032,0x00000006,0x00000516,0x00000000, + 0x00040032,0x00000006,0x00000517,0x00000000,0x00040032,0x00000006,0x00000518,0x00000000, + 0x00040032,0x00000006,0x00000519,0x00000000,0x00040032,0x00000006,0x0000051a,0x00000000, + 0x0003001d,0x0000051b,0x00000048,0x0003001e,0x0000051c,0x0000051b,0x00040020,0x0000051d, + 0x00000002,0x0000051c,0x0004003b,0x0000051d,0x0000051e,0x00000002,0x00040032,0x00000009, + 0x0000051f,0x00000001,0x00040032,0x00000009,0x00000520,0x00000001,0x00040032,0x00000009, + 0x00000521,0x00000001,0x00060033,0x0000000a,0x00000522,0x0000051f,0x00000520,0x00000521, + 0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b, + 0x00000007,0x00000008,0x00000007,0x0004003b,0x00000007,0x00000012,0x00000007,0x0004003b, + 0x00000007,0x00000017,0x00000007,0x0004003b,0x00000007,0x0000001c,0x00000007,0x0004003b, + 0x00000007,0x00000020,0x00000007,0x0004003b,0x00000007,0x00000024,0x00000007,0x0004003b, + 0x00000007,0x0000002c,0x00000007,0x0004003b,0x00000007,0x0000003e,0x00000007,0x0004003b, + 0x00000007,0x00000041,0x00000007,0x0004003b,0x00000007,0x00000043,0x00000007,0x0004003b, + 0x00000049,0x0000004a,0x00000007,0x0004003b,0x00000049,0x0000004d,0x00000007,0x0004003b, + 0x00000049,0x0000004e,0x00000007,0x0004003b,0x00000049,0x0000004f,0x00000007,0x0004003b, + 0x00000049,0x00000050,0x00000007,0x0004003b,0x00000049,0x00000051,0x00000007,0x0004003b, + 0x00000049,0x00000052,0x00000007,0x0004003b,0x00000049,0x00000053,0x00000007,0x0004003b, + 0x00000007,0x00000054,0x00000007,0x0004003b,0x00000049,0x0000005a,0x00000007,0x0004003b, + 0x00000049,0x0000005b,0x00000007,0x0004003b,0x00000049,0x0000005c,0x00000007,0x0004003b, + 0x00000049,0x0000005d,0x00000007,0x0004003b,0x00000049,0x0000005e,0x00000007,0x0004003b, + 0x00000049,0x0000006a,0x00000007,0x0004003b,0x00000049,0x00000071,0x00000007,0x0004003b, + 0x00000049,0x00000078,0x00000007,0x0004003b,0x00000049,0x0000007f,0x00000007,0x0004003b, + 0x00000049,0x00000086,0x00000007,0x0004003b,0x00000049,0x0000008d,0x00000007,0x0004003b, + 0x00000049,0x00000094,0x00000007,0x0004003b,0x00000007,0x000000a1,0x00000007,0x0004003b, + 0x00000007,0x000000a6,0x00000007,0x0004003b,0x00000007,0x000000aa,0x00000007,0x0004003b, + 0x00000007,0x000000b0,0x00000007,0x0004003b,0x00000007,0x000000b6,0x00000007,0x0004003b, + 0x00000007,0x000000c0,0x00000007,0x0004003b,0x00000007,0x000000cc,0x00000007,0x00050041, + 0x0000000e,0x0000000f,0x0000000c,0x0000000d,0x0004003d,0x00000009,0x00000010,0x0000000f, + 0x0004007c,0x00000006,0x00000011,0x00000010,0x0003003e,0x00000008,0x00000011,0x00050041, + 0x0000000e,0x00000014,0x0000000c,0x00000013,0x0004003d,0x00000009,0x00000015,0x00000014, + 0x0004007c,0x00000006,0x00000016,0x00000015,0x0003003e,0x00000012,0x00000016,0x00050041, + 0x0000000e,0x00000019,0x0000000c,0x00000018,0x0004003d,0x00000009,0x0000001a,0x00000019, + 0x0004007c,0x00000006,0x0000001b,0x0000001a,0x0003003e,0x00000017,0x0000001b,0x0004003d, + 0x00000006,0x0000001e,0x00000008,0x00050084,0x00000006,0x0000001f,0x0000001d,0x0000001e, + 0x0003003e,0x0000001c,0x0000001f,0x0004003d,0x00000006,0x00000022,0x00000012,0x00050084, + 0x00000006,0x00000023,0x00000021,0x00000022,0x0003003e,0x00000020,0x00000023,0x0004003d, + 0x00000006,0x00000025,0x00000017,0x00050084,0x00000006,0x00000027,0x00000025,0x00000026, + 0x00050084,0x00000006,0x00000029,0x00000027,0x00000028,0x00050084,0x00000006,0x0000002b, + 0x00000029,0x0000002a,0x0003003e,0x00000024,0x0000002b,0x0004003d,0x00000006,0x0000002d, + 0x00000017,0x00050084,0x00000006,0x0000002f,0x0000002d,0x0000002e,0x00050084,0x00000006, + 0x00000031,0x0000002f,0x00000030,0x00050087,0x00000006,0x00000032,0x00000031,0x00000021, + 0x0003003e,0x0000002c,0x00000032,0x0004003d,0x00000006,0x00000034,0x0000001c,0x000500b1, + 0x00000033,0x00000035,0x00000034,0x00000030,0x000300f7,0x00000037,0x00000000,0x000400fa, + 0x00000035,0x00000036,0x00000037,0x000200f8,0x00000036,0x0004003d,0x00000006,0x00000038, + 0x00000012,0x000500b1,0x00000033,0x0000003a,0x00000038,0x00000039,0x000200f9,0x00000037, + 0x000200f8,0x00000037,0x000700f5,0x00000033,0x0000003b,0x00000035,0x00000005,0x0000003a, + 0x00000036,0x000300f7,0x0000003d,0x00000000,0x000400fa,0x0000003b,0x0000003c,0x0000003d, + 0x000200f8,0x0000003c,0x0003003e,0x0000003e,0x00000040,0x0003003e,0x00000041,0x00000042, + 0x0004003d,0x00000006,0x00000044,0x0000001c,0x0004003d,0x00000006,0x00000045,0x0000003e, + 0x00050084,0x00000006,0x00000046,0x00000044,0x00000045,0x0003003e,0x00000043,0x00000046, + 0x0003003e,0x0000004a,0x0000004c,0x0003003e,0x0000004d,0x0000004c,0x0003003e,0x0000004e, + 0x0000004c,0x0003003e,0x0000004f,0x0000004c,0x0003003e,0x00000050,0x0000004c,0x0003003e, + 0x00000051,0x0000004c,0x0003003e,0x00000052,0x0000004c,0x0003003e,0x00000053,0x0000004c, + 0x0003003e,0x00000054,0x00000055,0x000200f9,0x00000056,0x000200f8,0x00000056,0x000400f6, + 0x00000058,0x00000059,0x00000000,0x000200f9,0x00000057,0x000200f8,0x00000057,0x0003003e, + 0x0000005a,0x0000004c,0x0003003e,0x0000005b,0x0000004c,0x0003003e,0x0000005c,0x0000004c, + 0x0003003e,0x0000005d,0x0000004c,0x0004003d,0x00000006,0x00000063,0x00000043,0x00060041, + 0x00000064,0x00000065,0x00000062,0x00000055,0x00000063,0x0004003d,0x00000048,0x00000066, + 0x00000065,0x0003003e,0x0000005e,0x00000066,0x0004003d,0x00000006,0x00000067,0x0000003e, + 0x0004003d,0x00000006,0x00000068,0x00000043,0x00050080,0x00000006,0x00000069,0x00000068, + 0x00000067,0x0003003e,0x00000043,0x00000069,0x0004003d,0x00000006,0x0000006b,0x00000043, + 0x00060041,0x00000064,0x0000006c,0x00000062,0x00000055,0x0000006b,0x0004003d,0x00000048, + 0x0000006d,0x0000006c,0x0003003e,0x0000006a,0x0000006d,0x0004003d,0x00000006,0x0000006e, + 0x0000003e,0x0004003d,0x00000006,0x0000006f,0x00000043,0x00050080,0x00000006,0x00000070, + 0x0000006f,0x0000006e,0x0003003e,0x00000043,0x00000070,0x0004003d,0x00000006,0x00000072, + 0x00000043,0x00060041,0x00000064,0x00000073,0x00000062,0x00000055,0x00000072,0x0004003d, + 0x00000048,0x00000074,0x00000073,0x0003003e,0x00000071,0x00000074,0x0004003d,0x00000006, + 0x00000075,0x0000003e,0x0004003d,0x00000006,0x00000076,0x00000043,0x00050080,0x00000006, + 0x00000077,0x00000076,0x00000075,0x0003003e,0x00000043,0x00000077,0x0004003d,0x00000006, + 0x00000079,0x00000043,0x00060041,0x00000064,0x0000007a,0x00000062,0x00000055,0x00000079, + 0x0004003d,0x00000048,0x0000007b,0x0000007a,0x0003003e,0x00000078,0x0000007b,0x0004003d, + 0x00000006,0x0000007c,0x0000003e,0x0004003d,0x00000006,0x0000007d,0x00000043,0x00050080, + 0x00000006,0x0000007e,0x0000007d,0x0000007c,0x0003003e,0x00000043,0x0000007e,0x0004003d, + 0x00000006,0x00000080,0x00000043,0x00060041,0x00000064,0x00000081,0x00000062,0x00000055, + 0x00000080,0x0004003d,0x00000048,0x00000082,0x00000081,0x0003003e,0x0000007f,0x00000082, + 0x0004003d,0x00000006,0x00000083,0x0000003e,0x0004003d,0x00000006,0x00000084,0x00000043, + 0x00050080,0x00000006,0x00000085,0x00000084,0x00000083,0x0003003e,0x00000043,0x00000085, + 0x0004003d,0x00000006,0x00000087,0x00000043,0x00060041,0x00000064,0x00000088,0x00000062, + 0x00000055,0x00000087,0x0004003d,0x00000048,0x00000089,0x00000088,0x0003003e,0x00000086, + 0x00000089,0x0004003d,0x00000006,0x0000008a,0x0000003e,0x0004003d,0x00000006,0x0000008b, + 0x00000043,0x00050080,0x00000006,0x0000008c,0x0000008b,0x0000008a,0x0003003e,0x00000043, + 0x0000008c,0x0004003d,0x00000006,0x0000008e,0x00000043,0x00060041,0x00000064,0x0000008f, + 0x00000062,0x00000055,0x0000008e,0x0004003d,0x00000048,0x00000090,0x0000008f,0x0003003e, + 0x0000008d,0x00000090,0x0004003d,0x00000006,0x00000091,0x0000003e,0x0004003d,0x00000006, + 0x00000092,0x00000043,0x00050080,0x00000006,0x00000093,0x00000092,0x00000091,0x0003003e, + 0x00000043,0x00000093,0x0004003d,0x00000006,0x00000095,0x00000043,0x00060041,0x00000064, + 0x00000096,0x00000062,0x00000055,0x00000095,0x0004003d,0x00000048,0x00000097,0x00000096, + 0x0003003e,0x00000094,0x00000097,0x0004003d,0x00000006,0x00000098,0x0000003e,0x0004003d, + 0x00000006,0x00000099,0x00000043,0x00050080,0x00000006,0x0000009a,0x00000099,0x00000098, + 0x0003003e,0x00000043,0x0000009a,0x0004003d,0x00000006,0x0000009c,0x0000003e,0x00050084, + 0x00000006,0x0000009d,0x0000001d,0x0000009c,0x00050082,0x00000006,0x0000009e,0x0000009b, + 0x0000009d,0x0004003d,0x00000006,0x0000009f,0x00000043,0x00050080,0x00000006,0x000000a0, + 0x0000009f,0x0000009e,0x0003003e,0x00000043,0x000000a0,0x0004003d,0x00000006,0x000000a2, + 0x00000020,0x00050080,0x00000006,0x000000a3,0x000000a2,0x00000055,0x0005008b,0x00000006, + 0x000000a5,0x000000a3,0x000000a4,0x0003003e,0x000000a1,0x000000a5,0x0004003d,0x00000006, + 0x000000a7,0x00000020,0x00050080,0x00000006,0x000000a8,0x000000a7,0x00000055,0x00050087, + 0x00000006,0x000000a9,0x000000a8,0x000000a4,0x0003003e,0x000000a6,0x000000a9,0x0004003d, + 0x00000006,0x000000ab,0x000000a6,0x00050084,0x00000006,0x000000ad,0x000000ab,0x000000ac, + 0x00050082,0x00000006,0x000000af,0x000000ad,0x000000ae,0x0003003e,0x000000aa,0x000000af, + 0x0004003d,0x00000006,0x000000b1,0x000000a1,0x00050084,0x00000006,0x000000b3,0x000000b1, + 0x000000b2,0x00050082,0x00000006,0x000000b5,0x000000b3,0x000000b4,0x0003003e,0x000000b0, + 0x000000b5,0x0004003d,0x00000006,0x000000b7,0x000000b0,0x0004003d,0x00000006,0x000000b8, + 0x00000054,0x00050084,0x00000006,0x000000b9,0x000000b8,0x00000021,0x00050080,0x00000006, + 0x000000ba,0x000000b9,0x00000055,0x0005008b,0x00000006,0x000000bc,0x000000ba,0x000000bb, + 0x00050084,0x00000006,0x000000be,0x000000bc,0x000000bd,0x00050080,0x00000006,0x000000bf, + 0x000000b7,0x000000be,0x0003003e,0x000000b6,0x000000bf,0x0004003d,0x00000006,0x000000c1, + 0x000000aa,0x0004003d,0x00000006,0x000000c2,0x00000054,0x00050084,0x00000006,0x000000c3, + 0x000000c2,0x00000021,0x00050080,0x00000006,0x000000c4,0x000000c3,0x00000055,0x0005008b, + 0x00000006,0x000000c7,0x000000c4,0x000000c6,0x00050087,0x00000006,0x000000c8,0x000000c7, + 0x000000bb,0x00050084,0x00000006,0x000000ca,0x000000c8,0x000000c9,0x00050080,0x00000006, + 0x000000cb,0x000000c1,0x000000ca,0x0003003e,0x000000c0,0x000000cb,0x0004003d,0x00000006, + 0x000000cd,0x00000054,0x00050084,0x00000006,0x000000ce,0x000000cd,0x00000021,0x00050080, + 0x00000006,0x000000cf,0x000000ce,0x00000055,0x00050087,0x00000006,0x000000d1,0x000000cf, + 0x000000d0,0x0003003e,0x000000cc,0x000000d1,0x0004003d,0x00000006,0x000000d2,0x000000c0, + 0x000500af,0x00000033,0x000000d3,0x000000d2,0x00000055,0x0004003d,0x00000006,0x000000d4, + 0x000000c0,0x000500b1,0x00000033,0x000000d5,0x000000d4,0x00000026,0x000500a7,0x00000033, + 0x000000d6,0x000000d3,0x000000d5,0x0004003d,0x00000006,0x000000d7,0x000000b6,0x000500af, + 0x00000033,0x000000d8,0x000000d7,0x00000055,0x000500a7,0x00000033,0x000000d9,0x000000d6, + 0x000000d8,0x0004003d,0x00000006,0x000000da,0x000000b6,0x000500b1,0x00000033,0x000000db, + 0x000000da,0x00000028,0x000500a7,0x00000033,0x000000dc,0x000000d9,0x000000db,0x000300f7, + 0x000000de,0x00000000,0x000400fa,0x000000dc,0x000000dd,0x000000de,0x000200f8,0x000000dd, + 0x0004003d,0x00000006,0x000000e3,0x00000024,0x0004003d,0x00000006,0x000000e4,0x000000cc, + 0x00050084,0x00000006,0x000000e6,0x000000e4,0x000000e5,0x00050080,0x00000006,0x000000e7, + 0x000000e3,0x000000e6,0x0004003d,0x00000006,0x000000e8,0x000000c0,0x00050084,0x00000006, + 0x000000e9,0x000000e8,0x00000028,0x00050080,0x00000006,0x000000ea,0x000000e7,0x000000e9, + 0x0004003d,0x00000006,0x000000eb,0x000000b6,0x00050080,0x00000006,0x000000ec,0x000000ea, + 0x000000eb,0x00060041,0x000000ed,0x000000ee,0x000000e2,0x00000055,0x000000ec,0x0004003d, + 0x00000047,0x000000ef,0x000000ee,0x00050041,0x000000f0,0x000000f1,0x0000005a,0x0000000d, + 0x0003003e,0x000000f1,0x000000ef,0x000200f9,0x000000de,0x000200f8,0x000000de,0x0004003d, + 0x00000006,0x000000f2,0x000000b0,0x0004003d,0x00000006,0x000000f3,0x00000054,0x00050084, + 0x00000006,0x000000f4,0x000000f3,0x00000021,0x00050080,0x00000006,0x000000f5,0x000000f4, + 0x0000009b,0x0005008b,0x00000006,0x000000f6,0x000000f5,0x000000bb,0x00050084,0x00000006, + 0x000000f7,0x000000f6,0x000000bd,0x00050080,0x00000006,0x000000f8,0x000000f2,0x000000f7, + 0x0003003e,0x000000b6,0x000000f8,0x0004003d,0x00000006,0x000000f9,0x000000aa,0x0004003d, + 0x00000006,0x000000fa,0x00000054,0x00050084,0x00000006,0x000000fb,0x000000fa,0x00000021, + 0x00050080,0x00000006,0x000000fc,0x000000fb,0x0000009b,0x0005008b,0x00000006,0x000000fe, + 0x000000fc,0x000000fd,0x00050087,0x00000006,0x000000ff,0x000000fe,0x000000bb,0x00050084, + 0x00000006,0x00000100,0x000000ff,0x000000c9,0x00050080,0x00000006,0x00000101,0x000000f9, + 0x00000100,0x0003003e,0x000000c0,0x00000101,0x0004003d,0x00000006,0x00000102,0x00000054, + 0x00050084,0x00000006,0x00000103,0x00000102,0x00000021,0x00050080,0x00000006,0x00000104, + 0x00000103,0x0000009b,0x00050087,0x00000006,0x00000106,0x00000104,0x00000105,0x0003003e, + 0x000000cc,0x00000106,0x0004003d,0x00000006,0x00000107,0x000000c0,0x000500af,0x00000033, + 0x00000108,0x00000107,0x00000055,0x0004003d,0x00000006,0x00000109,0x000000c0,0x000500b1, + 0x00000033,0x0000010a,0x00000109,0x00000026,0x000500a7,0x00000033,0x0000010b,0x00000108, + 0x0000010a,0x0004003d,0x00000006,0x0000010c,0x000000b6,0x000500af,0x00000033,0x0000010d, + 0x0000010c,0x00000055,0x000500a7,0x00000033,0x0000010e,0x0000010b,0x0000010d,0x0004003d, + 0x00000006,0x0000010f,0x000000b6,0x000500b1,0x00000033,0x00000110,0x0000010f,0x00000028, + 0x000500a7,0x00000033,0x00000111,0x0000010e,0x00000110,0x000300f7,0x00000113,0x00000000, + 0x000400fa,0x00000111,0x00000112,0x00000113,0x000200f8,0x00000112,0x0004003d,0x00000006, + 0x00000114,0x00000024,0x0004003d,0x00000006,0x00000115,0x000000cc,0x00050084,0x00000006, + 0x00000117,0x00000115,0x00000116,0x00050080,0x00000006,0x00000118,0x00000114,0x00000117, + 0x0004003d,0x00000006,0x00000119,0x000000c0,0x00050084,0x00000006,0x0000011a,0x00000119, + 0x00000028,0x00050080,0x00000006,0x0000011b,0x00000118,0x0000011a,0x0004003d,0x00000006, + 0x0000011c,0x000000b6,0x00050080,0x00000006,0x0000011d,0x0000011b,0x0000011c,0x00060041, + 0x000000ed,0x0000011e,0x000000e2,0x00000055,0x0000011d,0x0004003d,0x00000047,0x0000011f, + 0x0000011e,0x00050041,0x000000f0,0x00000120,0x0000005a,0x00000013,0x0003003e,0x00000120, + 0x0000011f,0x000200f9,0x00000113,0x000200f8,0x00000113,0x0004003d,0x00000006,0x00000121, + 0x000000b0,0x0004003d,0x00000006,0x00000122,0x00000054,0x00050084,0x00000006,0x00000123, + 0x00000122,0x00000021,0x00050080,0x00000006,0x00000125,0x00000123,0x00000124,0x0005008b, + 0x00000006,0x00000126,0x00000125,0x000000bb,0x00050084,0x00000006,0x00000127,0x00000126, + 0x000000bd,0x00050080,0x00000006,0x00000128,0x00000121,0x00000127,0x0003003e,0x000000b6, + 0x00000128,0x0004003d,0x00000006,0x00000129,0x000000aa,0x0004003d,0x00000006,0x0000012a, + 0x00000054,0x00050084,0x00000006,0x0000012b,0x0000012a,0x00000021,0x00050080,0x00000006, + 0x0000012c,0x0000012b,0x00000124,0x0005008b,0x00000006,0x0000012e,0x0000012c,0x0000012d, + 0x00050087,0x00000006,0x0000012f,0x0000012e,0x000000bb,0x00050084,0x00000006,0x00000130, + 0x0000012f,0x000000c9,0x00050080,0x00000006,0x00000131,0x00000129,0x00000130,0x0003003e, + 0x000000c0,0x00000131,0x0004003d,0x00000006,0x00000132,0x00000054,0x00050084,0x00000006, + 0x00000133,0x00000132,0x00000021,0x00050080,0x00000006,0x00000134,0x00000133,0x00000124, + 0x00050087,0x00000006,0x00000136,0x00000134,0x00000135,0x0003003e,0x000000cc,0x00000136, + 0x0004003d,0x00000006,0x00000137,0x000000c0,0x000500af,0x00000033,0x00000138,0x00000137, + 0x00000055,0x0004003d,0x00000006,0x00000139,0x000000c0,0x000500b1,0x00000033,0x0000013a, + 0x00000139,0x00000026,0x000500a7,0x00000033,0x0000013b,0x00000138,0x0000013a,0x0004003d, + 0x00000006,0x0000013c,0x000000b6,0x000500af,0x00000033,0x0000013d,0x0000013c,0x00000055, + 0x000500a7,0x00000033,0x0000013e,0x0000013b,0x0000013d,0x0004003d,0x00000006,0x0000013f, + 0x000000b6,0x000500b1,0x00000033,0x00000140,0x0000013f,0x00000028,0x000500a7,0x00000033, + 0x00000141,0x0000013e,0x00000140,0x000300f7,0x00000143,0x00000000,0x000400fa,0x00000141, + 0x00000142,0x00000143,0x000200f8,0x00000142,0x0004003d,0x00000006,0x00000144,0x00000024, + 0x0004003d,0x00000006,0x00000145,0x000000cc,0x00050084,0x00000006,0x00000147,0x00000145, + 0x00000146,0x00050080,0x00000006,0x00000148,0x00000144,0x00000147,0x0004003d,0x00000006, + 0x00000149,0x000000c0,0x00050084,0x00000006,0x0000014a,0x00000149,0x00000028,0x00050080, + 0x00000006,0x0000014b,0x00000148,0x0000014a,0x0004003d,0x00000006,0x0000014c,0x000000b6, + 0x00050080,0x00000006,0x0000014d,0x0000014b,0x0000014c,0x00060041,0x000000ed,0x0000014e, + 0x000000e2,0x00000055,0x0000014d,0x0004003d,0x00000047,0x0000014f,0x0000014e,0x00050041, + 0x000000f0,0x00000150,0x0000005a,0x00000018,0x0003003e,0x00000150,0x0000014f,0x000200f9, + 0x00000143,0x000200f8,0x00000143,0x0004003d,0x00000006,0x00000151,0x000000b0,0x0004003d, + 0x00000006,0x00000152,0x00000054,0x00050084,0x00000006,0x00000153,0x00000152,0x00000021, + 0x00050080,0x00000006,0x00000155,0x00000153,0x00000154,0x0005008b,0x00000006,0x00000156, + 0x00000155,0x000000bb,0x00050084,0x00000006,0x00000157,0x00000156,0x000000bd,0x00050080, + 0x00000006,0x00000158,0x00000151,0x00000157,0x0003003e,0x000000b6,0x00000158,0x0004003d, + 0x00000006,0x00000159,0x000000aa,0x0004003d,0x00000006,0x0000015a,0x00000054,0x00050084, + 0x00000006,0x0000015b,0x0000015a,0x00000021,0x00050080,0x00000006,0x0000015c,0x0000015b, + 0x00000154,0x0005008b,0x00000006,0x0000015e,0x0000015c,0x0000015d,0x00050087,0x00000006, + 0x0000015f,0x0000015e,0x000000bb,0x00050084,0x00000006,0x00000160,0x0000015f,0x000000c9, + 0x00050080,0x00000006,0x00000161,0x00000159,0x00000160,0x0003003e,0x000000c0,0x00000161, + 0x0004003d,0x00000006,0x00000162,0x00000054,0x00050084,0x00000006,0x00000163,0x00000162, + 0x00000021,0x00050080,0x00000006,0x00000164,0x00000163,0x00000154,0x00050087,0x00000006, + 0x00000166,0x00000164,0x00000165,0x0003003e,0x000000cc,0x00000166,0x0004003d,0x00000006, + 0x00000167,0x000000c0,0x000500af,0x00000033,0x00000168,0x00000167,0x00000055,0x0004003d, + 0x00000006,0x00000169,0x000000c0,0x000500b1,0x00000033,0x0000016a,0x00000169,0x00000026, + 0x000500a7,0x00000033,0x0000016b,0x00000168,0x0000016a,0x0004003d,0x00000006,0x0000016c, + 0x000000b6,0x000500af,0x00000033,0x0000016d,0x0000016c,0x00000055,0x000500a7,0x00000033, + 0x0000016e,0x0000016b,0x0000016d,0x0004003d,0x00000006,0x0000016f,0x000000b6,0x000500b1, + 0x00000033,0x00000170,0x0000016f,0x00000028,0x000500a7,0x00000033,0x00000171,0x0000016e, + 0x00000170,0x000300f7,0x00000173,0x00000000,0x000400fa,0x00000171,0x00000172,0x00000173, + 0x000200f8,0x00000172,0x0004003d,0x00000006,0x00000174,0x00000024,0x0004003d,0x00000006, + 0x00000175,0x000000cc,0x00050084,0x00000006,0x00000177,0x00000175,0x00000176,0x00050080, + 0x00000006,0x00000178,0x00000174,0x00000177,0x0004003d,0x00000006,0x00000179,0x000000c0, + 0x00050084,0x00000006,0x0000017a,0x00000179,0x00000028,0x00050080,0x00000006,0x0000017b, + 0x00000178,0x0000017a,0x0004003d,0x00000006,0x0000017c,0x000000b6,0x00050080,0x00000006, + 0x0000017d,0x0000017b,0x0000017c,0x00060041,0x000000ed,0x0000017e,0x000000e2,0x00000055, + 0x0000017d,0x0004003d,0x00000047,0x0000017f,0x0000017e,0x00050041,0x000000f0,0x00000181, + 0x0000005a,0x00000180,0x0003003e,0x00000181,0x0000017f,0x000200f9,0x00000173,0x000200f8, + 0x00000173,0x0004003d,0x00000048,0x00000182,0x0000005e,0x0004003d,0x00000048,0x00000183, + 0x0000005a,0x00050094,0x00000047,0x00000184,0x00000182,0x00000183,0x00050041,0x000000f0, + 0x00000185,0x0000004a,0x0000000d,0x0004003d,0x00000047,0x00000186,0x00000185,0x00050081, + 0x00000047,0x00000187,0x00000186,0x00000184,0x00050041,0x000000f0,0x00000188,0x0000004a, + 0x0000000d,0x0003003e,0x00000188,0x00000187,0x0004003d,0x00000048,0x00000189,0x0000006a, + 0x0004003d,0x00000048,0x0000018a,0x0000005a,0x00050094,0x00000047,0x0000018b,0x00000189, + 0x0000018a,0x00050041,0x000000f0,0x0000018c,0x0000004d,0x0000000d,0x0004003d,0x00000047, + 0x0000018d,0x0000018c,0x00050081,0x00000047,0x0000018e,0x0000018d,0x0000018b,0x00050041, + 0x000000f0,0x0000018f,0x0000004d,0x0000000d,0x0003003e,0x0000018f,0x0000018e,0x0004003d, + 0x00000048,0x00000190,0x00000071,0x0004003d,0x00000048,0x00000191,0x0000005a,0x00050094, + 0x00000047,0x00000192,0x00000190,0x00000191,0x00050041,0x000000f0,0x00000193,0x0000004e, + 0x0000000d,0x0004003d,0x00000047,0x00000194,0x00000193,0x00050081,0x00000047,0x00000195, + 0x00000194,0x00000192,0x00050041,0x000000f0,0x00000196,0x0000004e,0x0000000d,0x0003003e, + 0x00000196,0x00000195,0x0004003d,0x00000048,0x00000197,0x00000078,0x0004003d,0x00000048, + 0x00000198,0x0000005a,0x00050094,0x00000047,0x00000199,0x00000197,0x00000198,0x00050041, + 0x000000f0,0x0000019a,0x0000004f,0x0000000d,0x0004003d,0x00000047,0x0000019b,0x0000019a, + 0x00050081,0x00000047,0x0000019c,0x0000019b,0x00000199,0x00050041,0x000000f0,0x0000019d, + 0x0000004f,0x0000000d,0x0003003e,0x0000019d,0x0000019c,0x0004003d,0x00000048,0x0000019e, + 0x0000007f,0x0004003d,0x00000048,0x0000019f,0x0000005a,0x00050094,0x00000047,0x000001a0, + 0x0000019e,0x0000019f,0x00050041,0x000000f0,0x000001a1,0x00000050,0x0000000d,0x0004003d, + 0x00000047,0x000001a2,0x000001a1,0x00050081,0x00000047,0x000001a3,0x000001a2,0x000001a0, + 0x00050041,0x000000f0,0x000001a4,0x00000050,0x0000000d,0x0003003e,0x000001a4,0x000001a3, + 0x0004003d,0x00000048,0x000001a5,0x00000086,0x0004003d,0x00000048,0x000001a6,0x0000005a, + 0x00050094,0x00000047,0x000001a7,0x000001a5,0x000001a6,0x00050041,0x000000f0,0x000001a8, + 0x00000051,0x0000000d,0x0004003d,0x00000047,0x000001a9,0x000001a8,0x00050081,0x00000047, + 0x000001aa,0x000001a9,0x000001a7,0x00050041,0x000000f0,0x000001ab,0x00000051,0x0000000d, + 0x0003003e,0x000001ab,0x000001aa,0x0004003d,0x00000048,0x000001ac,0x0000008d,0x0004003d, + 0x00000048,0x000001ad,0x0000005a,0x00050094,0x00000047,0x000001ae,0x000001ac,0x000001ad, + 0x00050041,0x000000f0,0x000001af,0x00000052,0x0000000d,0x0004003d,0x00000047,0x000001b0, + 0x000001af,0x00050081,0x00000047,0x000001b1,0x000001b0,0x000001ae,0x00050041,0x000000f0, + 0x000001b2,0x00000052,0x0000000d,0x0003003e,0x000001b2,0x000001b1,0x0004003d,0x00000048, + 0x000001b3,0x00000094,0x0004003d,0x00000048,0x000001b4,0x0000005a,0x00050094,0x00000047, + 0x000001b5,0x000001b3,0x000001b4,0x00050041,0x000000f0,0x000001b6,0x00000053,0x0000000d, + 0x0004003d,0x00000047,0x000001b7,0x000001b6,0x00050081,0x00000047,0x000001b8,0x000001b7, + 0x000001b5,0x00050041,0x000000f0,0x000001b9,0x00000053,0x0000000d,0x0003003e,0x000001b9, + 0x000001b8,0x0004003d,0x00000006,0x000001ba,0x00000020,0x00050080,0x00000006,0x000001bb, + 0x000001ba,0x0000009b,0x0005008b,0x00000006,0x000001bc,0x000001bb,0x000000a4,0x0003003e, + 0x000000a1,0x000001bc,0x0004003d,0x00000006,0x000001bd,0x00000020,0x00050080,0x00000006, + 0x000001be,0x000001bd,0x0000009b,0x00050087,0x00000006,0x000001bf,0x000001be,0x000000a4, + 0x0003003e,0x000000a6,0x000001bf,0x0004003d,0x00000006,0x000001c0,0x000000a6,0x00050084, + 0x00000006,0x000001c1,0x000001c0,0x000000ac,0x00050082,0x00000006,0x000001c2,0x000001c1, + 0x000000ae,0x0003003e,0x000000aa,0x000001c2,0x0004003d,0x00000006,0x000001c3,0x000000a1, + 0x00050084,0x00000006,0x000001c4,0x000001c3,0x000000b2,0x00050082,0x00000006,0x000001c5, + 0x000001c4,0x000000b4,0x0003003e,0x000000b0,0x000001c5,0x0004003d,0x00000006,0x000001c6, + 0x000000b0,0x0004003d,0x00000006,0x000001c7,0x00000054,0x00050084,0x00000006,0x000001c8, + 0x000001c7,0x00000021,0x00050080,0x00000006,0x000001c9,0x000001c8,0x00000055,0x0005008b, + 0x00000006,0x000001ca,0x000001c9,0x000000bb,0x00050084,0x00000006,0x000001cb,0x000001ca, + 0x000000bd,0x00050080,0x00000006,0x000001cc,0x000001c6,0x000001cb,0x0003003e,0x000000b6, + 0x000001cc,0x0004003d,0x00000006,0x000001cd,0x000000aa,0x0004003d,0x00000006,0x000001ce, + 0x00000054,0x00050084,0x00000006,0x000001cf,0x000001ce,0x00000021,0x00050080,0x00000006, + 0x000001d0,0x000001cf,0x00000055,0x0005008b,0x00000006,0x000001d2,0x000001d0,0x000001d1, + 0x00050087,0x00000006,0x000001d3,0x000001d2,0x000000bb,0x00050084,0x00000006,0x000001d4, + 0x000001d3,0x000000c9,0x00050080,0x00000006,0x000001d5,0x000001cd,0x000001d4,0x0003003e, + 0x000000c0,0x000001d5,0x0004003d,0x00000006,0x000001d6,0x00000054,0x00050084,0x00000006, + 0x000001d7,0x000001d6,0x00000021,0x00050080,0x00000006,0x000001d8,0x000001d7,0x00000055, + 0x00050087,0x00000006,0x000001da,0x000001d8,0x000001d9,0x0003003e,0x000000cc,0x000001da, + 0x0004003d,0x00000006,0x000001db,0x000000c0,0x000500af,0x00000033,0x000001dc,0x000001db, + 0x00000055,0x0004003d,0x00000006,0x000001dd,0x000000c0,0x000500b1,0x00000033,0x000001de, + 0x000001dd,0x00000026,0x000500a7,0x00000033,0x000001df,0x000001dc,0x000001de,0x0004003d, + 0x00000006,0x000001e0,0x000000b6,0x000500af,0x00000033,0x000001e1,0x000001e0,0x00000055, + 0x000500a7,0x00000033,0x000001e2,0x000001df,0x000001e1,0x0004003d,0x00000006,0x000001e3, + 0x000000b6,0x000500b1,0x00000033,0x000001e4,0x000001e3,0x00000028,0x000500a7,0x00000033, + 0x000001e5,0x000001e2,0x000001e4,0x000300f7,0x000001e7,0x00000000,0x000400fa,0x000001e5, + 0x000001e6,0x000001e7,0x000200f8,0x000001e6,0x0004003d,0x00000006,0x000001e8,0x00000024, + 0x0004003d,0x00000006,0x000001e9,0x000000cc,0x00050084,0x00000006,0x000001eb,0x000001e9, + 0x000001ea,0x00050080,0x00000006,0x000001ec,0x000001e8,0x000001eb,0x0004003d,0x00000006, + 0x000001ed,0x000000c0,0x00050084,0x00000006,0x000001ee,0x000001ed,0x00000028,0x00050080, + 0x00000006,0x000001ef,0x000001ec,0x000001ee,0x0004003d,0x00000006,0x000001f0,0x000000b6, + 0x00050080,0x00000006,0x000001f1,0x000001ef,0x000001f0,0x00060041,0x000000ed,0x000001f2, + 0x000000e2,0x00000055,0x000001f1,0x0004003d,0x00000047,0x000001f3,0x000001f2,0x00050041, + 0x000000f0,0x000001f4,0x0000005b,0x0000000d,0x0003003e,0x000001f4,0x000001f3,0x000200f9, + 0x000001e7,0x000200f8,0x000001e7,0x0004003d,0x00000006,0x000001f5,0x000000b0,0x0004003d, + 0x00000006,0x000001f6,0x00000054,0x00050084,0x00000006,0x000001f7,0x000001f6,0x00000021, + 0x00050080,0x00000006,0x000001f8,0x000001f7,0x0000009b,0x0005008b,0x00000006,0x000001f9, + 0x000001f8,0x000000bb,0x00050084,0x00000006,0x000001fa,0x000001f9,0x000000bd,0x00050080, + 0x00000006,0x000001fb,0x000001f5,0x000001fa,0x0003003e,0x000000b6,0x000001fb,0x0004003d, + 0x00000006,0x000001fc,0x000000aa,0x0004003d,0x00000006,0x000001fd,0x00000054,0x00050084, + 0x00000006,0x000001fe,0x000001fd,0x00000021,0x00050080,0x00000006,0x000001ff,0x000001fe, + 0x0000009b,0x0005008b,0x00000006,0x00000201,0x000001ff,0x00000200,0x00050087,0x00000006, + 0x00000202,0x00000201,0x000000bb,0x00050084,0x00000006,0x00000203,0x00000202,0x000000c9, + 0x00050080,0x00000006,0x00000204,0x000001fc,0x00000203,0x0003003e,0x000000c0,0x00000204, + 0x0004003d,0x00000006,0x00000205,0x00000054,0x00050084,0x00000006,0x00000206,0x00000205, + 0x00000021,0x00050080,0x00000006,0x00000207,0x00000206,0x0000009b,0x00050087,0x00000006, + 0x00000209,0x00000207,0x00000208,0x0003003e,0x000000cc,0x00000209,0x0004003d,0x00000006, + 0x0000020a,0x000000c0,0x000500af,0x00000033,0x0000020b,0x0000020a,0x00000055,0x0004003d, + 0x00000006,0x0000020c,0x000000c0,0x000500b1,0x00000033,0x0000020d,0x0000020c,0x00000026, + 0x000500a7,0x00000033,0x0000020e,0x0000020b,0x0000020d,0x0004003d,0x00000006,0x0000020f, + 0x000000b6,0x000500af,0x00000033,0x00000210,0x0000020f,0x00000055,0x000500a7,0x00000033, + 0x00000211,0x0000020e,0x00000210,0x0004003d,0x00000006,0x00000212,0x000000b6,0x000500b1, + 0x00000033,0x00000213,0x00000212,0x00000028,0x000500a7,0x00000033,0x00000214,0x00000211, + 0x00000213,0x000300f7,0x00000216,0x00000000,0x000400fa,0x00000214,0x00000215,0x00000216, + 0x000200f8,0x00000215,0x0004003d,0x00000006,0x00000217,0x00000024,0x0004003d,0x00000006, + 0x00000218,0x000000cc,0x00050084,0x00000006,0x0000021a,0x00000218,0x00000219,0x00050080, + 0x00000006,0x0000021b,0x00000217,0x0000021a,0x0004003d,0x00000006,0x0000021c,0x000000c0, + 0x00050084,0x00000006,0x0000021d,0x0000021c,0x00000028,0x00050080,0x00000006,0x0000021e, + 0x0000021b,0x0000021d,0x0004003d,0x00000006,0x0000021f,0x000000b6,0x00050080,0x00000006, + 0x00000220,0x0000021e,0x0000021f,0x00060041,0x000000ed,0x00000221,0x000000e2,0x00000055, + 0x00000220,0x0004003d,0x00000047,0x00000222,0x00000221,0x00050041,0x000000f0,0x00000223, + 0x0000005b,0x00000013,0x0003003e,0x00000223,0x00000222,0x000200f9,0x00000216,0x000200f8, + 0x00000216,0x0004003d,0x00000006,0x00000224,0x000000b0,0x0004003d,0x00000006,0x00000225, + 0x00000054,0x00050084,0x00000006,0x00000226,0x00000225,0x00000021,0x00050080,0x00000006, + 0x00000227,0x00000226,0x00000124,0x0005008b,0x00000006,0x00000228,0x00000227,0x000000bb, + 0x00050084,0x00000006,0x00000229,0x00000228,0x000000bd,0x00050080,0x00000006,0x0000022a, + 0x00000224,0x00000229,0x0003003e,0x000000b6,0x0000022a,0x0004003d,0x00000006,0x0000022b, + 0x000000aa,0x0004003d,0x00000006,0x0000022c,0x00000054,0x00050084,0x00000006,0x0000022d, + 0x0000022c,0x00000021,0x00050080,0x00000006,0x0000022e,0x0000022d,0x00000124,0x0005008b, + 0x00000006,0x00000230,0x0000022e,0x0000022f,0x00050087,0x00000006,0x00000231,0x00000230, + 0x000000bb,0x00050084,0x00000006,0x00000232,0x00000231,0x000000c9,0x00050080,0x00000006, + 0x00000233,0x0000022b,0x00000232,0x0003003e,0x000000c0,0x00000233,0x0004003d,0x00000006, + 0x00000234,0x00000054,0x00050084,0x00000006,0x00000235,0x00000234,0x00000021,0x00050080, + 0x00000006,0x00000236,0x00000235,0x00000124,0x00050087,0x00000006,0x00000238,0x00000236, + 0x00000237,0x0003003e,0x000000cc,0x00000238,0x0004003d,0x00000006,0x00000239,0x000000c0, + 0x000500af,0x00000033,0x0000023a,0x00000239,0x00000055,0x0004003d,0x00000006,0x0000023b, + 0x000000c0,0x000500b1,0x00000033,0x0000023c,0x0000023b,0x00000026,0x000500a7,0x00000033, + 0x0000023d,0x0000023a,0x0000023c,0x0004003d,0x00000006,0x0000023e,0x000000b6,0x000500af, + 0x00000033,0x0000023f,0x0000023e,0x00000055,0x000500a7,0x00000033,0x00000240,0x0000023d, + 0x0000023f,0x0004003d,0x00000006,0x00000241,0x000000b6,0x000500b1,0x00000033,0x00000242, + 0x00000241,0x00000028,0x000500a7,0x00000033,0x00000243,0x00000240,0x00000242,0x000300f7, + 0x00000245,0x00000000,0x000400fa,0x00000243,0x00000244,0x00000245,0x000200f8,0x00000244, + 0x0004003d,0x00000006,0x00000246,0x00000024,0x0004003d,0x00000006,0x00000247,0x000000cc, + 0x00050084,0x00000006,0x00000249,0x00000247,0x00000248,0x00050080,0x00000006,0x0000024a, + 0x00000246,0x00000249,0x0004003d,0x00000006,0x0000024b,0x000000c0,0x00050084,0x00000006, + 0x0000024c,0x0000024b,0x00000028,0x00050080,0x00000006,0x0000024d,0x0000024a,0x0000024c, + 0x0004003d,0x00000006,0x0000024e,0x000000b6,0x00050080,0x00000006,0x0000024f,0x0000024d, + 0x0000024e,0x00060041,0x000000ed,0x00000250,0x000000e2,0x00000055,0x0000024f,0x0004003d, + 0x00000047,0x00000251,0x00000250,0x00050041,0x000000f0,0x00000252,0x0000005b,0x00000018, + 0x0003003e,0x00000252,0x00000251,0x000200f9,0x00000245,0x000200f8,0x00000245,0x0004003d, + 0x00000006,0x00000253,0x000000b0,0x0004003d,0x00000006,0x00000254,0x00000054,0x00050084, + 0x00000006,0x00000255,0x00000254,0x00000021,0x00050080,0x00000006,0x00000256,0x00000255, + 0x00000154,0x0005008b,0x00000006,0x00000257,0x00000256,0x000000bb,0x00050084,0x00000006, + 0x00000258,0x00000257,0x000000bd,0x00050080,0x00000006,0x00000259,0x00000253,0x00000258, + 0x0003003e,0x000000b6,0x00000259,0x0004003d,0x00000006,0x0000025a,0x000000aa,0x0004003d, + 0x00000006,0x0000025b,0x00000054,0x00050084,0x00000006,0x0000025c,0x0000025b,0x00000021, + 0x00050080,0x00000006,0x0000025d,0x0000025c,0x00000154,0x0005008b,0x00000006,0x0000025f, + 0x0000025d,0x0000025e,0x00050087,0x00000006,0x00000260,0x0000025f,0x000000bb,0x00050084, + 0x00000006,0x00000261,0x00000260,0x000000c9,0x00050080,0x00000006,0x00000262,0x0000025a, + 0x00000261,0x0003003e,0x000000c0,0x00000262,0x0004003d,0x00000006,0x00000263,0x00000054, + 0x00050084,0x00000006,0x00000264,0x00000263,0x00000021,0x00050080,0x00000006,0x00000265, + 0x00000264,0x00000154,0x00050087,0x00000006,0x00000267,0x00000265,0x00000266,0x0003003e, + 0x000000cc,0x00000267,0x0004003d,0x00000006,0x00000268,0x000000c0,0x000500af,0x00000033, + 0x00000269,0x00000268,0x00000055,0x0004003d,0x00000006,0x0000026a,0x000000c0,0x000500b1, + 0x00000033,0x0000026b,0x0000026a,0x00000026,0x000500a7,0x00000033,0x0000026c,0x00000269, + 0x0000026b,0x0004003d,0x00000006,0x0000026d,0x000000b6,0x000500af,0x00000033,0x0000026e, + 0x0000026d,0x00000055,0x000500a7,0x00000033,0x0000026f,0x0000026c,0x0000026e,0x0004003d, + 0x00000006,0x00000270,0x000000b6,0x000500b1,0x00000033,0x00000271,0x00000270,0x00000028, + 0x000500a7,0x00000033,0x00000272,0x0000026f,0x00000271,0x000300f7,0x00000274,0x00000000, + 0x000400fa,0x00000272,0x00000273,0x00000274,0x000200f8,0x00000273,0x0004003d,0x00000006, + 0x00000275,0x00000024,0x0004003d,0x00000006,0x00000276,0x000000cc,0x00050084,0x00000006, + 0x00000278,0x00000276,0x00000277,0x00050080,0x00000006,0x00000279,0x00000275,0x00000278, + 0x0004003d,0x00000006,0x0000027a,0x000000c0,0x00050084,0x00000006,0x0000027b,0x0000027a, + 0x00000028,0x00050080,0x00000006,0x0000027c,0x00000279,0x0000027b,0x0004003d,0x00000006, + 0x0000027d,0x000000b6,0x00050080,0x00000006,0x0000027e,0x0000027c,0x0000027d,0x00060041, + 0x000000ed,0x0000027f,0x000000e2,0x00000055,0x0000027e,0x0004003d,0x00000047,0x00000280, + 0x0000027f,0x00050041,0x000000f0,0x00000281,0x0000005b,0x00000180,0x0003003e,0x00000281, + 0x00000280,0x000200f9,0x00000274,0x000200f8,0x00000274,0x0004003d,0x00000048,0x00000282, + 0x0000005e,0x0004003d,0x00000048,0x00000283,0x0000005b,0x00050094,0x00000047,0x00000284, + 0x00000282,0x00000283,0x00050041,0x000000f0,0x00000285,0x0000004a,0x00000013,0x0004003d, + 0x00000047,0x00000286,0x00000285,0x00050081,0x00000047,0x00000287,0x00000286,0x00000284, + 0x00050041,0x000000f0,0x00000288,0x0000004a,0x00000013,0x0003003e,0x00000288,0x00000287, + 0x0004003d,0x00000048,0x00000289,0x0000006a,0x0004003d,0x00000048,0x0000028a,0x0000005b, + 0x00050094,0x00000047,0x0000028b,0x00000289,0x0000028a,0x00050041,0x000000f0,0x0000028c, + 0x0000004d,0x00000013,0x0004003d,0x00000047,0x0000028d,0x0000028c,0x00050081,0x00000047, + 0x0000028e,0x0000028d,0x0000028b,0x00050041,0x000000f0,0x0000028f,0x0000004d,0x00000013, + 0x0003003e,0x0000028f,0x0000028e,0x0004003d,0x00000048,0x00000290,0x00000071,0x0004003d, + 0x00000048,0x00000291,0x0000005b,0x00050094,0x00000047,0x00000292,0x00000290,0x00000291, + 0x00050041,0x000000f0,0x00000293,0x0000004e,0x00000013,0x0004003d,0x00000047,0x00000294, + 0x00000293,0x00050081,0x00000047,0x00000295,0x00000294,0x00000292,0x00050041,0x000000f0, + 0x00000296,0x0000004e,0x00000013,0x0003003e,0x00000296,0x00000295,0x0004003d,0x00000048, + 0x00000297,0x00000078,0x0004003d,0x00000048,0x00000298,0x0000005b,0x00050094,0x00000047, + 0x00000299,0x00000297,0x00000298,0x00050041,0x000000f0,0x0000029a,0x0000004f,0x00000013, + 0x0004003d,0x00000047,0x0000029b,0x0000029a,0x00050081,0x00000047,0x0000029c,0x0000029b, + 0x00000299,0x00050041,0x000000f0,0x0000029d,0x0000004f,0x00000013,0x0003003e,0x0000029d, + 0x0000029c,0x0004003d,0x00000048,0x0000029e,0x0000007f,0x0004003d,0x00000048,0x0000029f, + 0x0000005b,0x00050094,0x00000047,0x000002a0,0x0000029e,0x0000029f,0x00050041,0x000000f0, + 0x000002a1,0x00000050,0x00000013,0x0004003d,0x00000047,0x000002a2,0x000002a1,0x00050081, + 0x00000047,0x000002a3,0x000002a2,0x000002a0,0x00050041,0x000000f0,0x000002a4,0x00000050, + 0x00000013,0x0003003e,0x000002a4,0x000002a3,0x0004003d,0x00000048,0x000002a5,0x00000086, + 0x0004003d,0x00000048,0x000002a6,0x0000005b,0x00050094,0x00000047,0x000002a7,0x000002a5, + 0x000002a6,0x00050041,0x000000f0,0x000002a8,0x00000051,0x00000013,0x0004003d,0x00000047, + 0x000002a9,0x000002a8,0x00050081,0x00000047,0x000002aa,0x000002a9,0x000002a7,0x00050041, + 0x000000f0,0x000002ab,0x00000051,0x00000013,0x0003003e,0x000002ab,0x000002aa,0x0004003d, + 0x00000048,0x000002ac,0x0000008d,0x0004003d,0x00000048,0x000002ad,0x0000005b,0x00050094, + 0x00000047,0x000002ae,0x000002ac,0x000002ad,0x00050041,0x000000f0,0x000002af,0x00000052, + 0x00000013,0x0004003d,0x00000047,0x000002b0,0x000002af,0x00050081,0x00000047,0x000002b1, + 0x000002b0,0x000002ae,0x00050041,0x000000f0,0x000002b2,0x00000052,0x00000013,0x0003003e, + 0x000002b2,0x000002b1,0x0004003d,0x00000048,0x000002b3,0x00000094,0x0004003d,0x00000048, + 0x000002b4,0x0000005b,0x00050094,0x00000047,0x000002b5,0x000002b3,0x000002b4,0x00050041, + 0x000000f0,0x000002b6,0x00000053,0x00000013,0x0004003d,0x00000047,0x000002b7,0x000002b6, + 0x00050081,0x00000047,0x000002b8,0x000002b7,0x000002b5,0x00050041,0x000000f0,0x000002b9, + 0x00000053,0x00000013,0x0003003e,0x000002b9,0x000002b8,0x0004003d,0x00000006,0x000002ba, + 0x00000020,0x00050080,0x00000006,0x000002bb,0x000002ba,0x00000124,0x0005008b,0x00000006, + 0x000002bc,0x000002bb,0x000000a4,0x0003003e,0x000000a1,0x000002bc,0x0004003d,0x00000006, + 0x000002bd,0x00000020,0x00050080,0x00000006,0x000002be,0x000002bd,0x00000124,0x00050087, + 0x00000006,0x000002bf,0x000002be,0x000000a4,0x0003003e,0x000000a6,0x000002bf,0x0004003d, + 0x00000006,0x000002c0,0x000000a6,0x00050084,0x00000006,0x000002c1,0x000002c0,0x000000ac, + 0x00050082,0x00000006,0x000002c2,0x000002c1,0x000000ae,0x0003003e,0x000000aa,0x000002c2, + 0x0004003d,0x00000006,0x000002c3,0x000000a1,0x00050084,0x00000006,0x000002c4,0x000002c3, + 0x000000b2,0x00050082,0x00000006,0x000002c5,0x000002c4,0x000000b4,0x0003003e,0x000000b0, + 0x000002c5,0x0004003d,0x00000006,0x000002c6,0x000000b0,0x0004003d,0x00000006,0x000002c7, + 0x00000054,0x00050084,0x00000006,0x000002c8,0x000002c7,0x00000021,0x00050080,0x00000006, + 0x000002c9,0x000002c8,0x00000055,0x0005008b,0x00000006,0x000002ca,0x000002c9,0x000000bb, + 0x00050084,0x00000006,0x000002cb,0x000002ca,0x000000bd,0x00050080,0x00000006,0x000002cc, + 0x000002c6,0x000002cb,0x0003003e,0x000000b6,0x000002cc,0x0004003d,0x00000006,0x000002cd, + 0x000000aa,0x0004003d,0x00000006,0x000002ce,0x00000054,0x00050084,0x00000006,0x000002cf, + 0x000002ce,0x00000021,0x00050080,0x00000006,0x000002d0,0x000002cf,0x00000055,0x0005008b, + 0x00000006,0x000002d2,0x000002d0,0x000002d1,0x00050087,0x00000006,0x000002d3,0x000002d2, + 0x000000bb,0x00050084,0x00000006,0x000002d4,0x000002d3,0x000000c9,0x00050080,0x00000006, + 0x000002d5,0x000002cd,0x000002d4,0x0003003e,0x000000c0,0x000002d5,0x0004003d,0x00000006, + 0x000002d6,0x00000054,0x00050084,0x00000006,0x000002d7,0x000002d6,0x00000021,0x00050080, + 0x00000006,0x000002d8,0x000002d7,0x00000055,0x00050087,0x00000006,0x000002da,0x000002d8, + 0x000002d9,0x0003003e,0x000000cc,0x000002da,0x0004003d,0x00000006,0x000002db,0x000000c0, + 0x000500af,0x00000033,0x000002dc,0x000002db,0x00000055,0x0004003d,0x00000006,0x000002dd, + 0x000000c0,0x000500b1,0x00000033,0x000002de,0x000002dd,0x00000026,0x000500a7,0x00000033, + 0x000002df,0x000002dc,0x000002de,0x0004003d,0x00000006,0x000002e0,0x000000b6,0x000500af, + 0x00000033,0x000002e1,0x000002e0,0x00000055,0x000500a7,0x00000033,0x000002e2,0x000002df, + 0x000002e1,0x0004003d,0x00000006,0x000002e3,0x000000b6,0x000500b1,0x00000033,0x000002e4, + 0x000002e3,0x00000028,0x000500a7,0x00000033,0x000002e5,0x000002e2,0x000002e4,0x000300f7, + 0x000002e7,0x00000000,0x000400fa,0x000002e5,0x000002e6,0x000002e7,0x000200f8,0x000002e6, + 0x0004003d,0x00000006,0x000002e8,0x00000024,0x0004003d,0x00000006,0x000002e9,0x000000cc, + 0x00050084,0x00000006,0x000002eb,0x000002e9,0x000002ea,0x00050080,0x00000006,0x000002ec, + 0x000002e8,0x000002eb,0x0004003d,0x00000006,0x000002ed,0x000000c0,0x00050084,0x00000006, + 0x000002ee,0x000002ed,0x00000028,0x00050080,0x00000006,0x000002ef,0x000002ec,0x000002ee, + 0x0004003d,0x00000006,0x000002f0,0x000000b6,0x00050080,0x00000006,0x000002f1,0x000002ef, + 0x000002f0,0x00060041,0x000000ed,0x000002f2,0x000000e2,0x00000055,0x000002f1,0x0004003d, + 0x00000047,0x000002f3,0x000002f2,0x00050041,0x000000f0,0x000002f4,0x0000005c,0x0000000d, + 0x0003003e,0x000002f4,0x000002f3,0x000200f9,0x000002e7,0x000200f8,0x000002e7,0x0004003d, + 0x00000006,0x000002f5,0x000000b0,0x0004003d,0x00000006,0x000002f6,0x00000054,0x00050084, + 0x00000006,0x000002f7,0x000002f6,0x00000021,0x00050080,0x00000006,0x000002f8,0x000002f7, + 0x0000009b,0x0005008b,0x00000006,0x000002f9,0x000002f8,0x000000bb,0x00050084,0x00000006, + 0x000002fa,0x000002f9,0x000000bd,0x00050080,0x00000006,0x000002fb,0x000002f5,0x000002fa, + 0x0003003e,0x000000b6,0x000002fb,0x0004003d,0x00000006,0x000002fc,0x000000aa,0x0004003d, + 0x00000006,0x000002fd,0x00000054,0x00050084,0x00000006,0x000002fe,0x000002fd,0x00000021, + 0x00050080,0x00000006,0x000002ff,0x000002fe,0x0000009b,0x0005008b,0x00000006,0x00000301, + 0x000002ff,0x00000300,0x00050087,0x00000006,0x00000302,0x00000301,0x000000bb,0x00050084, + 0x00000006,0x00000303,0x00000302,0x000000c9,0x00050080,0x00000006,0x00000304,0x000002fc, + 0x00000303,0x0003003e,0x000000c0,0x00000304,0x0004003d,0x00000006,0x00000305,0x00000054, + 0x00050084,0x00000006,0x00000306,0x00000305,0x00000021,0x00050080,0x00000006,0x00000307, + 0x00000306,0x0000009b,0x00050087,0x00000006,0x00000309,0x00000307,0x00000308,0x0003003e, + 0x000000cc,0x00000309,0x0004003d,0x00000006,0x0000030a,0x000000c0,0x000500af,0x00000033, + 0x0000030b,0x0000030a,0x00000055,0x0004003d,0x00000006,0x0000030c,0x000000c0,0x000500b1, + 0x00000033,0x0000030d,0x0000030c,0x00000026,0x000500a7,0x00000033,0x0000030e,0x0000030b, + 0x0000030d,0x0004003d,0x00000006,0x0000030f,0x000000b6,0x000500af,0x00000033,0x00000310, + 0x0000030f,0x00000055,0x000500a7,0x00000033,0x00000311,0x0000030e,0x00000310,0x0004003d, + 0x00000006,0x00000312,0x000000b6,0x000500b1,0x00000033,0x00000313,0x00000312,0x00000028, + 0x000500a7,0x00000033,0x00000314,0x00000311,0x00000313,0x000300f7,0x00000316,0x00000000, + 0x000400fa,0x00000314,0x00000315,0x00000316,0x000200f8,0x00000315,0x0004003d,0x00000006, + 0x00000317,0x00000024,0x0004003d,0x00000006,0x00000318,0x000000cc,0x00050084,0x00000006, + 0x0000031a,0x00000318,0x00000319,0x00050080,0x00000006,0x0000031b,0x00000317,0x0000031a, + 0x0004003d,0x00000006,0x0000031c,0x000000c0,0x00050084,0x00000006,0x0000031d,0x0000031c, + 0x00000028,0x00050080,0x00000006,0x0000031e,0x0000031b,0x0000031d,0x0004003d,0x00000006, + 0x0000031f,0x000000b6,0x00050080,0x00000006,0x00000320,0x0000031e,0x0000031f,0x00060041, + 0x000000ed,0x00000321,0x000000e2,0x00000055,0x00000320,0x0004003d,0x00000047,0x00000322, + 0x00000321,0x00050041,0x000000f0,0x00000323,0x0000005c,0x00000013,0x0003003e,0x00000323, + 0x00000322,0x000200f9,0x00000316,0x000200f8,0x00000316,0x0004003d,0x00000006,0x00000324, + 0x000000b0,0x0004003d,0x00000006,0x00000325,0x00000054,0x00050084,0x00000006,0x00000326, + 0x00000325,0x00000021,0x00050080,0x00000006,0x00000327,0x00000326,0x00000124,0x0005008b, + 0x00000006,0x00000328,0x00000327,0x000000bb,0x00050084,0x00000006,0x00000329,0x00000328, + 0x000000bd,0x00050080,0x00000006,0x0000032a,0x00000324,0x00000329,0x0003003e,0x000000b6, + 0x0000032a,0x0004003d,0x00000006,0x0000032b,0x000000aa,0x0004003d,0x00000006,0x0000032c, + 0x00000054,0x00050084,0x00000006,0x0000032d,0x0000032c,0x00000021,0x00050080,0x00000006, + 0x0000032e,0x0000032d,0x00000124,0x0005008b,0x00000006,0x00000330,0x0000032e,0x0000032f, + 0x00050087,0x00000006,0x00000331,0x00000330,0x000000bb,0x00050084,0x00000006,0x00000332, + 0x00000331,0x000000c9,0x00050080,0x00000006,0x00000333,0x0000032b,0x00000332,0x0003003e, + 0x000000c0,0x00000333,0x0004003d,0x00000006,0x00000334,0x00000054,0x00050084,0x00000006, + 0x00000335,0x00000334,0x00000021,0x00050080,0x00000006,0x00000336,0x00000335,0x00000124, + 0x00050087,0x00000006,0x00000338,0x00000336,0x00000337,0x0003003e,0x000000cc,0x00000338, + 0x0004003d,0x00000006,0x00000339,0x000000c0,0x000500af,0x00000033,0x0000033a,0x00000339, + 0x00000055,0x0004003d,0x00000006,0x0000033b,0x000000c0,0x000500b1,0x00000033,0x0000033c, + 0x0000033b,0x00000026,0x000500a7,0x00000033,0x0000033d,0x0000033a,0x0000033c,0x0004003d, + 0x00000006,0x0000033e,0x000000b6,0x000500af,0x00000033,0x0000033f,0x0000033e,0x00000055, + 0x000500a7,0x00000033,0x00000340,0x0000033d,0x0000033f,0x0004003d,0x00000006,0x00000341, + 0x000000b6,0x000500b1,0x00000033,0x00000342,0x00000341,0x00000028,0x000500a7,0x00000033, + 0x00000343,0x00000340,0x00000342,0x000300f7,0x00000345,0x00000000,0x000400fa,0x00000343, + 0x00000344,0x00000345,0x000200f8,0x00000344,0x0004003d,0x00000006,0x00000346,0x00000024, + 0x0004003d,0x00000006,0x00000347,0x000000cc,0x00050084,0x00000006,0x00000349,0x00000347, + 0x00000348,0x00050080,0x00000006,0x0000034a,0x00000346,0x00000349,0x0004003d,0x00000006, + 0x0000034b,0x000000c0,0x00050084,0x00000006,0x0000034c,0x0000034b,0x00000028,0x00050080, + 0x00000006,0x0000034d,0x0000034a,0x0000034c,0x0004003d,0x00000006,0x0000034e,0x000000b6, + 0x00050080,0x00000006,0x0000034f,0x0000034d,0x0000034e,0x00060041,0x000000ed,0x00000350, + 0x000000e2,0x00000055,0x0000034f,0x0004003d,0x00000047,0x00000351,0x00000350,0x00050041, + 0x000000f0,0x00000352,0x0000005c,0x00000018,0x0003003e,0x00000352,0x00000351,0x000200f9, + 0x00000345,0x000200f8,0x00000345,0x0004003d,0x00000006,0x00000353,0x000000b0,0x0004003d, + 0x00000006,0x00000354,0x00000054,0x00050084,0x00000006,0x00000355,0x00000354,0x00000021, + 0x00050080,0x00000006,0x00000356,0x00000355,0x00000154,0x0005008b,0x00000006,0x00000357, + 0x00000356,0x000000bb,0x00050084,0x00000006,0x00000358,0x00000357,0x000000bd,0x00050080, + 0x00000006,0x00000359,0x00000353,0x00000358,0x0003003e,0x000000b6,0x00000359,0x0004003d, + 0x00000006,0x0000035a,0x000000aa,0x0004003d,0x00000006,0x0000035b,0x00000054,0x00050084, + 0x00000006,0x0000035c,0x0000035b,0x00000021,0x00050080,0x00000006,0x0000035d,0x0000035c, + 0x00000154,0x0005008b,0x00000006,0x0000035f,0x0000035d,0x0000035e,0x00050087,0x00000006, + 0x00000360,0x0000035f,0x000000bb,0x00050084,0x00000006,0x00000361,0x00000360,0x000000c9, + 0x00050080,0x00000006,0x00000362,0x0000035a,0x00000361,0x0003003e,0x000000c0,0x00000362, + 0x0004003d,0x00000006,0x00000363,0x00000054,0x00050084,0x00000006,0x00000364,0x00000363, + 0x00000021,0x00050080,0x00000006,0x00000365,0x00000364,0x00000154,0x00050087,0x00000006, + 0x00000367,0x00000365,0x00000366,0x0003003e,0x000000cc,0x00000367,0x0004003d,0x00000006, + 0x00000368,0x000000c0,0x000500af,0x00000033,0x00000369,0x00000368,0x00000055,0x0004003d, + 0x00000006,0x0000036a,0x000000c0,0x000500b1,0x00000033,0x0000036b,0x0000036a,0x00000026, + 0x000500a7,0x00000033,0x0000036c,0x00000369,0x0000036b,0x0004003d,0x00000006,0x0000036d, + 0x000000b6,0x000500af,0x00000033,0x0000036e,0x0000036d,0x00000055,0x000500a7,0x00000033, + 0x0000036f,0x0000036c,0x0000036e,0x0004003d,0x00000006,0x00000370,0x000000b6,0x000500b1, + 0x00000033,0x00000371,0x00000370,0x00000028,0x000500a7,0x00000033,0x00000372,0x0000036f, + 0x00000371,0x000300f7,0x00000374,0x00000000,0x000400fa,0x00000372,0x00000373,0x00000374, + 0x000200f8,0x00000373,0x0004003d,0x00000006,0x00000375,0x00000024,0x0004003d,0x00000006, + 0x00000376,0x000000cc,0x00050084,0x00000006,0x00000378,0x00000376,0x00000377,0x00050080, + 0x00000006,0x00000379,0x00000375,0x00000378,0x0004003d,0x00000006,0x0000037a,0x000000c0, + 0x00050084,0x00000006,0x0000037b,0x0000037a,0x00000028,0x00050080,0x00000006,0x0000037c, + 0x00000379,0x0000037b,0x0004003d,0x00000006,0x0000037d,0x000000b6,0x00050080,0x00000006, + 0x0000037e,0x0000037c,0x0000037d,0x00060041,0x000000ed,0x0000037f,0x000000e2,0x00000055, + 0x0000037e,0x0004003d,0x00000047,0x00000380,0x0000037f,0x00050041,0x000000f0,0x00000381, + 0x0000005c,0x00000180,0x0003003e,0x00000381,0x00000380,0x000200f9,0x00000374,0x000200f8, + 0x00000374,0x0004003d,0x00000048,0x00000382,0x0000005e,0x0004003d,0x00000048,0x00000383, + 0x0000005c,0x00050094,0x00000047,0x00000384,0x00000382,0x00000383,0x00050041,0x000000f0, + 0x00000385,0x0000004a,0x00000018,0x0004003d,0x00000047,0x00000386,0x00000385,0x00050081, + 0x00000047,0x00000387,0x00000386,0x00000384,0x00050041,0x000000f0,0x00000388,0x0000004a, + 0x00000018,0x0003003e,0x00000388,0x00000387,0x0004003d,0x00000048,0x00000389,0x0000006a, + 0x0004003d,0x00000048,0x0000038a,0x0000005c,0x00050094,0x00000047,0x0000038b,0x00000389, + 0x0000038a,0x00050041,0x000000f0,0x0000038c,0x0000004d,0x00000018,0x0004003d,0x00000047, + 0x0000038d,0x0000038c,0x00050081,0x00000047,0x0000038e,0x0000038d,0x0000038b,0x00050041, + 0x000000f0,0x0000038f,0x0000004d,0x00000018,0x0003003e,0x0000038f,0x0000038e,0x0004003d, + 0x00000048,0x00000390,0x00000071,0x0004003d,0x00000048,0x00000391,0x0000005c,0x00050094, + 0x00000047,0x00000392,0x00000390,0x00000391,0x00050041,0x000000f0,0x00000393,0x0000004e, + 0x00000018,0x0004003d,0x00000047,0x00000394,0x00000393,0x00050081,0x00000047,0x00000395, + 0x00000394,0x00000392,0x00050041,0x000000f0,0x00000396,0x0000004e,0x00000018,0x0003003e, + 0x00000396,0x00000395,0x0004003d,0x00000048,0x00000397,0x00000078,0x0004003d,0x00000048, + 0x00000398,0x0000005c,0x00050094,0x00000047,0x00000399,0x00000397,0x00000398,0x00050041, + 0x000000f0,0x0000039a,0x0000004f,0x00000018,0x0004003d,0x00000047,0x0000039b,0x0000039a, + 0x00050081,0x00000047,0x0000039c,0x0000039b,0x00000399,0x00050041,0x000000f0,0x0000039d, + 0x0000004f,0x00000018,0x0003003e,0x0000039d,0x0000039c,0x0004003d,0x00000048,0x0000039e, + 0x0000007f,0x0004003d,0x00000048,0x0000039f,0x0000005c,0x00050094,0x00000047,0x000003a0, + 0x0000039e,0x0000039f,0x00050041,0x000000f0,0x000003a1,0x00000050,0x00000018,0x0004003d, + 0x00000047,0x000003a2,0x000003a1,0x00050081,0x00000047,0x000003a3,0x000003a2,0x000003a0, + 0x00050041,0x000000f0,0x000003a4,0x00000050,0x00000018,0x0003003e,0x000003a4,0x000003a3, + 0x0004003d,0x00000048,0x000003a5,0x00000086,0x0004003d,0x00000048,0x000003a6,0x0000005c, + 0x00050094,0x00000047,0x000003a7,0x000003a5,0x000003a6,0x00050041,0x000000f0,0x000003a8, + 0x00000051,0x00000018,0x0004003d,0x00000047,0x000003a9,0x000003a8,0x00050081,0x00000047, + 0x000003aa,0x000003a9,0x000003a7,0x00050041,0x000000f0,0x000003ab,0x00000051,0x00000018, + 0x0003003e,0x000003ab,0x000003aa,0x0004003d,0x00000048,0x000003ac,0x0000008d,0x0004003d, + 0x00000048,0x000003ad,0x0000005c,0x00050094,0x00000047,0x000003ae,0x000003ac,0x000003ad, + 0x00050041,0x000000f0,0x000003af,0x00000052,0x00000018,0x0004003d,0x00000047,0x000003b0, + 0x000003af,0x00050081,0x00000047,0x000003b1,0x000003b0,0x000003ae,0x00050041,0x000000f0, + 0x000003b2,0x00000052,0x00000018,0x0003003e,0x000003b2,0x000003b1,0x0004003d,0x00000048, + 0x000003b3,0x00000094,0x0004003d,0x00000048,0x000003b4,0x0000005c,0x00050094,0x00000047, + 0x000003b5,0x000003b3,0x000003b4,0x00050041,0x000000f0,0x000003b6,0x00000053,0x00000018, + 0x0004003d,0x00000047,0x000003b7,0x000003b6,0x00050081,0x00000047,0x000003b8,0x000003b7, + 0x000003b5,0x00050041,0x000000f0,0x000003b9,0x00000053,0x00000018,0x0003003e,0x000003b9, + 0x000003b8,0x0004003d,0x00000006,0x000003ba,0x00000020,0x00050080,0x00000006,0x000003bb, + 0x000003ba,0x00000154,0x0005008b,0x00000006,0x000003bc,0x000003bb,0x000000a4,0x0003003e, + 0x000000a1,0x000003bc,0x0004003d,0x00000006,0x000003bd,0x00000020,0x00050080,0x00000006, + 0x000003be,0x000003bd,0x00000154,0x00050087,0x00000006,0x000003bf,0x000003be,0x000000a4, + 0x0003003e,0x000000a6,0x000003bf,0x0004003d,0x00000006,0x000003c0,0x000000a6,0x00050084, + 0x00000006,0x000003c1,0x000003c0,0x000000ac,0x00050082,0x00000006,0x000003c2,0x000003c1, + 0x000000ae,0x0003003e,0x000000aa,0x000003c2,0x0004003d,0x00000006,0x000003c3,0x000000a1, + 0x00050084,0x00000006,0x000003c4,0x000003c3,0x000000b2,0x00050082,0x00000006,0x000003c5, + 0x000003c4,0x000000b4,0x0003003e,0x000000b0,0x000003c5,0x0004003d,0x00000006,0x000003c6, + 0x000000b0,0x0004003d,0x00000006,0x000003c7,0x00000054,0x00050084,0x00000006,0x000003c8, + 0x000003c7,0x00000021,0x00050080,0x00000006,0x000003c9,0x000003c8,0x00000055,0x0005008b, + 0x00000006,0x000003ca,0x000003c9,0x000000bb,0x00050084,0x00000006,0x000003cb,0x000003ca, + 0x000000bd,0x00050080,0x00000006,0x000003cc,0x000003c6,0x000003cb,0x0003003e,0x000000b6, + 0x000003cc,0x0004003d,0x00000006,0x000003cd,0x000000aa,0x0004003d,0x00000006,0x000003ce, + 0x00000054,0x00050084,0x00000006,0x000003cf,0x000003ce,0x00000021,0x00050080,0x00000006, + 0x000003d0,0x000003cf,0x00000055,0x0005008b,0x00000006,0x000003d2,0x000003d0,0x000003d1, + 0x00050087,0x00000006,0x000003d3,0x000003d2,0x000000bb,0x00050084,0x00000006,0x000003d4, + 0x000003d3,0x000000c9,0x00050080,0x00000006,0x000003d5,0x000003cd,0x000003d4,0x0003003e, + 0x000000c0,0x000003d5,0x0004003d,0x00000006,0x000003d6,0x00000054,0x00050084,0x00000006, + 0x000003d7,0x000003d6,0x00000021,0x00050080,0x00000006,0x000003d8,0x000003d7,0x00000055, + 0x00050087,0x00000006,0x000003da,0x000003d8,0x000003d9,0x0003003e,0x000000cc,0x000003da, + 0x0004003d,0x00000006,0x000003db,0x000000c0,0x000500af,0x00000033,0x000003dc,0x000003db, + 0x00000055,0x0004003d,0x00000006,0x000003dd,0x000000c0,0x000500b1,0x00000033,0x000003de, + 0x000003dd,0x00000026,0x000500a7,0x00000033,0x000003df,0x000003dc,0x000003de,0x0004003d, + 0x00000006,0x000003e0,0x000000b6,0x000500af,0x00000033,0x000003e1,0x000003e0,0x00000055, + 0x000500a7,0x00000033,0x000003e2,0x000003df,0x000003e1,0x0004003d,0x00000006,0x000003e3, + 0x000000b6,0x000500b1,0x00000033,0x000003e4,0x000003e3,0x00000028,0x000500a7,0x00000033, + 0x000003e5,0x000003e2,0x000003e4,0x000300f7,0x000003e7,0x00000000,0x000400fa,0x000003e5, + 0x000003e6,0x000003e7,0x000200f8,0x000003e6,0x0004003d,0x00000006,0x000003e8,0x00000024, + 0x0004003d,0x00000006,0x000003e9,0x000000cc,0x00050084,0x00000006,0x000003eb,0x000003e9, + 0x000003ea,0x00050080,0x00000006,0x000003ec,0x000003e8,0x000003eb,0x0004003d,0x00000006, + 0x000003ed,0x000000c0,0x00050084,0x00000006,0x000003ee,0x000003ed,0x00000028,0x00050080, + 0x00000006,0x000003ef,0x000003ec,0x000003ee,0x0004003d,0x00000006,0x000003f0,0x000000b6, + 0x00050080,0x00000006,0x000003f1,0x000003ef,0x000003f0,0x00060041,0x000000ed,0x000003f2, + 0x000000e2,0x00000055,0x000003f1,0x0004003d,0x00000047,0x000003f3,0x000003f2,0x00050041, + 0x000000f0,0x000003f4,0x0000005d,0x0000000d,0x0003003e,0x000003f4,0x000003f3,0x000200f9, + 0x000003e7,0x000200f8,0x000003e7,0x0004003d,0x00000006,0x000003f5,0x000000b0,0x0004003d, + 0x00000006,0x000003f6,0x00000054,0x00050084,0x00000006,0x000003f7,0x000003f6,0x00000021, + 0x00050080,0x00000006,0x000003f8,0x000003f7,0x0000009b,0x0005008b,0x00000006,0x000003f9, + 0x000003f8,0x000000bb,0x00050084,0x00000006,0x000003fa,0x000003f9,0x000000bd,0x00050080, + 0x00000006,0x000003fb,0x000003f5,0x000003fa,0x0003003e,0x000000b6,0x000003fb,0x0004003d, + 0x00000006,0x000003fc,0x000000aa,0x0004003d,0x00000006,0x000003fd,0x00000054,0x00050084, + 0x00000006,0x000003fe,0x000003fd,0x00000021,0x00050080,0x00000006,0x000003ff,0x000003fe, + 0x0000009b,0x0005008b,0x00000006,0x00000401,0x000003ff,0x00000400,0x00050087,0x00000006, + 0x00000402,0x00000401,0x000000bb,0x00050084,0x00000006,0x00000403,0x00000402,0x000000c9, + 0x00050080,0x00000006,0x00000404,0x000003fc,0x00000403,0x0003003e,0x000000c0,0x00000404, + 0x0004003d,0x00000006,0x00000405,0x00000054,0x00050084,0x00000006,0x00000406,0x00000405, + 0x00000021,0x00050080,0x00000006,0x00000407,0x00000406,0x0000009b,0x00050087,0x00000006, + 0x00000409,0x00000407,0x00000408,0x0003003e,0x000000cc,0x00000409,0x0004003d,0x00000006, + 0x0000040a,0x000000c0,0x000500af,0x00000033,0x0000040b,0x0000040a,0x00000055,0x0004003d, + 0x00000006,0x0000040c,0x000000c0,0x000500b1,0x00000033,0x0000040d,0x0000040c,0x00000026, + 0x000500a7,0x00000033,0x0000040e,0x0000040b,0x0000040d,0x0004003d,0x00000006,0x0000040f, + 0x000000b6,0x000500af,0x00000033,0x00000410,0x0000040f,0x00000055,0x000500a7,0x00000033, + 0x00000411,0x0000040e,0x00000410,0x0004003d,0x00000006,0x00000412,0x000000b6,0x000500b1, + 0x00000033,0x00000413,0x00000412,0x00000028,0x000500a7,0x00000033,0x00000414,0x00000411, + 0x00000413,0x000300f7,0x00000416,0x00000000,0x000400fa,0x00000414,0x00000415,0x00000416, + 0x000200f8,0x00000415,0x0004003d,0x00000006,0x00000417,0x00000024,0x0004003d,0x00000006, + 0x00000418,0x000000cc,0x00050084,0x00000006,0x0000041a,0x00000418,0x00000419,0x00050080, + 0x00000006,0x0000041b,0x00000417,0x0000041a,0x0004003d,0x00000006,0x0000041c,0x000000c0, + 0x00050084,0x00000006,0x0000041d,0x0000041c,0x00000028,0x00050080,0x00000006,0x0000041e, + 0x0000041b,0x0000041d,0x0004003d,0x00000006,0x0000041f,0x000000b6,0x00050080,0x00000006, + 0x00000420,0x0000041e,0x0000041f,0x00060041,0x000000ed,0x00000421,0x000000e2,0x00000055, + 0x00000420,0x0004003d,0x00000047,0x00000422,0x00000421,0x00050041,0x000000f0,0x00000423, + 0x0000005d,0x00000013,0x0003003e,0x00000423,0x00000422,0x000200f9,0x00000416,0x000200f8, + 0x00000416,0x0004003d,0x00000006,0x00000424,0x000000b0,0x0004003d,0x00000006,0x00000425, + 0x00000054,0x00050084,0x00000006,0x00000426,0x00000425,0x00000021,0x00050080,0x00000006, + 0x00000427,0x00000426,0x00000124,0x0005008b,0x00000006,0x00000428,0x00000427,0x000000bb, + 0x00050084,0x00000006,0x00000429,0x00000428,0x000000bd,0x00050080,0x00000006,0x0000042a, + 0x00000424,0x00000429,0x0003003e,0x000000b6,0x0000042a,0x0004003d,0x00000006,0x0000042b, + 0x000000aa,0x0004003d,0x00000006,0x0000042c,0x00000054,0x00050084,0x00000006,0x0000042d, + 0x0000042c,0x00000021,0x00050080,0x00000006,0x0000042e,0x0000042d,0x00000124,0x0005008b, + 0x00000006,0x00000430,0x0000042e,0x0000042f,0x00050087,0x00000006,0x00000431,0x00000430, + 0x000000bb,0x00050084,0x00000006,0x00000432,0x00000431,0x000000c9,0x00050080,0x00000006, + 0x00000433,0x0000042b,0x00000432,0x0003003e,0x000000c0,0x00000433,0x0004003d,0x00000006, + 0x00000434,0x00000054,0x00050084,0x00000006,0x00000435,0x00000434,0x00000021,0x00050080, + 0x00000006,0x00000436,0x00000435,0x00000124,0x00050087,0x00000006,0x00000438,0x00000436, + 0x00000437,0x0003003e,0x000000cc,0x00000438,0x0004003d,0x00000006,0x00000439,0x000000c0, + 0x000500af,0x00000033,0x0000043a,0x00000439,0x00000055,0x0004003d,0x00000006,0x0000043b, + 0x000000c0,0x000500b1,0x00000033,0x0000043c,0x0000043b,0x00000026,0x000500a7,0x00000033, + 0x0000043d,0x0000043a,0x0000043c,0x0004003d,0x00000006,0x0000043e,0x000000b6,0x000500af, + 0x00000033,0x0000043f,0x0000043e,0x00000055,0x000500a7,0x00000033,0x00000440,0x0000043d, + 0x0000043f,0x0004003d,0x00000006,0x00000441,0x000000b6,0x000500b1,0x00000033,0x00000442, + 0x00000441,0x00000028,0x000500a7,0x00000033,0x00000443,0x00000440,0x00000442,0x000300f7, + 0x00000445,0x00000000,0x000400fa,0x00000443,0x00000444,0x00000445,0x000200f8,0x00000444, + 0x0004003d,0x00000006,0x00000446,0x00000024,0x0004003d,0x00000006,0x00000447,0x000000cc, + 0x00050084,0x00000006,0x00000449,0x00000447,0x00000448,0x00050080,0x00000006,0x0000044a, + 0x00000446,0x00000449,0x0004003d,0x00000006,0x0000044b,0x000000c0,0x00050084,0x00000006, + 0x0000044c,0x0000044b,0x00000028,0x00050080,0x00000006,0x0000044d,0x0000044a,0x0000044c, + 0x0004003d,0x00000006,0x0000044e,0x000000b6,0x00050080,0x00000006,0x0000044f,0x0000044d, + 0x0000044e,0x00060041,0x000000ed,0x00000450,0x000000e2,0x00000055,0x0000044f,0x0004003d, + 0x00000047,0x00000451,0x00000450,0x00050041,0x000000f0,0x00000452,0x0000005d,0x00000018, + 0x0003003e,0x00000452,0x00000451,0x000200f9,0x00000445,0x000200f8,0x00000445,0x0004003d, + 0x00000006,0x00000453,0x000000b0,0x0004003d,0x00000006,0x00000454,0x00000054,0x00050084, + 0x00000006,0x00000455,0x00000454,0x00000021,0x00050080,0x00000006,0x00000456,0x00000455, + 0x00000154,0x0005008b,0x00000006,0x00000457,0x00000456,0x000000bb,0x00050084,0x00000006, + 0x00000458,0x00000457,0x000000bd,0x00050080,0x00000006,0x00000459,0x00000453,0x00000458, + 0x0003003e,0x000000b6,0x00000459,0x0004003d,0x00000006,0x0000045a,0x000000aa,0x0004003d, + 0x00000006,0x0000045b,0x00000054,0x00050084,0x00000006,0x0000045c,0x0000045b,0x00000021, + 0x00050080,0x00000006,0x0000045d,0x0000045c,0x00000154,0x0005008b,0x00000006,0x0000045f, + 0x0000045d,0x0000045e,0x00050087,0x00000006,0x00000460,0x0000045f,0x000000bb,0x00050084, + 0x00000006,0x00000461,0x00000460,0x000000c9,0x00050080,0x00000006,0x00000462,0x0000045a, + 0x00000461,0x0003003e,0x000000c0,0x00000462,0x0004003d,0x00000006,0x00000463,0x00000054, + 0x00050084,0x00000006,0x00000464,0x00000463,0x00000021,0x00050080,0x00000006,0x00000465, + 0x00000464,0x00000154,0x00050087,0x00000006,0x00000467,0x00000465,0x00000466,0x0003003e, + 0x000000cc,0x00000467,0x0004003d,0x00000006,0x00000468,0x000000c0,0x000500af,0x00000033, + 0x00000469,0x00000468,0x00000055,0x0004003d,0x00000006,0x0000046a,0x000000c0,0x000500b1, + 0x00000033,0x0000046b,0x0000046a,0x00000026,0x000500a7,0x00000033,0x0000046c,0x00000469, + 0x0000046b,0x0004003d,0x00000006,0x0000046d,0x000000b6,0x000500af,0x00000033,0x0000046e, + 0x0000046d,0x00000055,0x000500a7,0x00000033,0x0000046f,0x0000046c,0x0000046e,0x0004003d, + 0x00000006,0x00000470,0x000000b6,0x000500b1,0x00000033,0x00000471,0x00000470,0x00000028, + 0x000500a7,0x00000033,0x00000472,0x0000046f,0x00000471,0x000300f7,0x00000474,0x00000000, + 0x000400fa,0x00000472,0x00000473,0x00000474,0x000200f8,0x00000473,0x0004003d,0x00000006, + 0x00000475,0x00000024,0x0004003d,0x00000006,0x00000476,0x000000cc,0x00050084,0x00000006, + 0x00000478,0x00000476,0x00000477,0x00050080,0x00000006,0x00000479,0x00000475,0x00000478, + 0x0004003d,0x00000006,0x0000047a,0x000000c0,0x00050084,0x00000006,0x0000047b,0x0000047a, + 0x00000028,0x00050080,0x00000006,0x0000047c,0x00000479,0x0000047b,0x0004003d,0x00000006, + 0x0000047d,0x000000b6,0x00050080,0x00000006,0x0000047e,0x0000047c,0x0000047d,0x00060041, + 0x000000ed,0x0000047f,0x000000e2,0x00000055,0x0000047e,0x0004003d,0x00000047,0x00000480, + 0x0000047f,0x00050041,0x000000f0,0x00000481,0x0000005d,0x00000180,0x0003003e,0x00000481, + 0x00000480,0x000200f9,0x00000474,0x000200f8,0x00000474,0x0004003d,0x00000048,0x00000482, + 0x0000005e,0x0004003d,0x00000048,0x00000483,0x0000005d,0x00050094,0x00000047,0x00000484, + 0x00000482,0x00000483,0x00050041,0x000000f0,0x00000485,0x0000004a,0x00000180,0x0004003d, + 0x00000047,0x00000486,0x00000485,0x00050081,0x00000047,0x00000487,0x00000486,0x00000484, + 0x00050041,0x000000f0,0x00000488,0x0000004a,0x00000180,0x0003003e,0x00000488,0x00000487, + 0x0004003d,0x00000048,0x00000489,0x0000006a,0x0004003d,0x00000048,0x0000048a,0x0000005d, + 0x00050094,0x00000047,0x0000048b,0x00000489,0x0000048a,0x00050041,0x000000f0,0x0000048c, + 0x0000004d,0x00000180,0x0004003d,0x00000047,0x0000048d,0x0000048c,0x00050081,0x00000047, + 0x0000048e,0x0000048d,0x0000048b,0x00050041,0x000000f0,0x0000048f,0x0000004d,0x00000180, + 0x0003003e,0x0000048f,0x0000048e,0x0004003d,0x00000048,0x00000490,0x00000071,0x0004003d, + 0x00000048,0x00000491,0x0000005d,0x00050094,0x00000047,0x00000492,0x00000490,0x00000491, + 0x00050041,0x000000f0,0x00000493,0x0000004e,0x00000180,0x0004003d,0x00000047,0x00000494, + 0x00000493,0x00050081,0x00000047,0x00000495,0x00000494,0x00000492,0x00050041,0x000000f0, + 0x00000496,0x0000004e,0x00000180,0x0003003e,0x00000496,0x00000495,0x0004003d,0x00000048, + 0x00000497,0x00000078,0x0004003d,0x00000048,0x00000498,0x0000005d,0x00050094,0x00000047, + 0x00000499,0x00000497,0x00000498,0x00050041,0x000000f0,0x0000049a,0x0000004f,0x00000180, + 0x0004003d,0x00000047,0x0000049b,0x0000049a,0x00050081,0x00000047,0x0000049c,0x0000049b, + 0x00000499,0x00050041,0x000000f0,0x0000049d,0x0000004f,0x00000180,0x0003003e,0x0000049d, + 0x0000049c,0x0004003d,0x00000048,0x0000049e,0x0000007f,0x0004003d,0x00000048,0x0000049f, + 0x0000005d,0x00050094,0x00000047,0x000004a0,0x0000049e,0x0000049f,0x00050041,0x000000f0, + 0x000004a1,0x00000050,0x00000180,0x0004003d,0x00000047,0x000004a2,0x000004a1,0x00050081, + 0x00000047,0x000004a3,0x000004a2,0x000004a0,0x00050041,0x000000f0,0x000004a4,0x00000050, + 0x00000180,0x0003003e,0x000004a4,0x000004a3,0x0004003d,0x00000048,0x000004a5,0x00000086, + 0x0004003d,0x00000048,0x000004a6,0x0000005d,0x00050094,0x00000047,0x000004a7,0x000004a5, + 0x000004a6,0x00050041,0x000000f0,0x000004a8,0x00000051,0x00000180,0x0004003d,0x00000047, + 0x000004a9,0x000004a8,0x00050081,0x00000047,0x000004aa,0x000004a9,0x000004a7,0x00050041, + 0x000000f0,0x000004ab,0x00000051,0x00000180,0x0003003e,0x000004ab,0x000004aa,0x0004003d, + 0x00000048,0x000004ac,0x0000008d,0x0004003d,0x00000048,0x000004ad,0x0000005d,0x00050094, + 0x00000047,0x000004ae,0x000004ac,0x000004ad,0x00050041,0x000000f0,0x000004af,0x00000052, + 0x00000180,0x0004003d,0x00000047,0x000004b0,0x000004af,0x00050081,0x00000047,0x000004b1, + 0x000004b0,0x000004ae,0x00050041,0x000000f0,0x000004b2,0x00000052,0x00000180,0x0003003e, + 0x000004b2,0x000004b1,0x0004003d,0x00000048,0x000004b3,0x00000094,0x0004003d,0x00000048, + 0x000004b4,0x0000005d,0x00050094,0x00000047,0x000004b5,0x000004b3,0x000004b4,0x00050041, + 0x000000f0,0x000004b6,0x00000053,0x00000180,0x0004003d,0x00000047,0x000004b7,0x000004b6, + 0x00050081,0x00000047,0x000004b8,0x000004b7,0x000004b5,0x00050041,0x000000f0,0x000004b9, + 0x00000053,0x00000180,0x0003003e,0x000004b9,0x000004b8,0x0004003d,0x00000006,0x000004ba, + 0x00000054,0x00050080,0x00000006,0x000004bb,0x000004ba,0x0000009b,0x0003003e,0x00000054, + 0x000004bb,0x000200f9,0x00000059,0x000200f8,0x00000059,0x0004003d,0x00000006,0x000004bc, + 0x00000054,0x0004003d,0x00000006,0x000004bd,0x0000003e,0x000500b1,0x00000033,0x000004be, + 0x000004bc,0x000004bd,0x000400fa,0x000004be,0x00000056,0x00000058,0x000200f8,0x00000058, + 0x0004003d,0x00000006,0x000004c3,0x0000002c,0x0004003d,0x00000006,0x000004c4,0x0000001c, + 0x00050080,0x00000006,0x000004c5,0x000004c4,0x00000055,0x00050084,0x00000006,0x000004c6, + 0x000004c5,0x0000002e,0x00050087,0x00000006,0x000004c7,0x000004c6,0x00000021,0x00050080, + 0x00000006,0x000004c8,0x000004c3,0x000004c7,0x0004003d,0x00000006,0x000004c9,0x00000012, + 0x00050080,0x00000006,0x000004ca,0x000004c8,0x000004c9,0x0004003d,0x00000048,0x000004cb, + 0x0000004a,0x00060041,0x00000064,0x000004cc,0x000004c2,0x00000055,0x000004ca,0x0003003e, + 0x000004cc,0x000004cb,0x0004003d,0x00000006,0x000004cd,0x0000002c,0x0004003d,0x00000006, + 0x000004ce,0x0000001c,0x00050080,0x00000006,0x000004cf,0x000004ce,0x0000009b,0x00050084, + 0x00000006,0x000004d0,0x000004cf,0x0000002e,0x00050087,0x00000006,0x000004d1,0x000004d0, + 0x00000021,0x00050080,0x00000006,0x000004d2,0x000004cd,0x000004d1,0x0004003d,0x00000006, + 0x000004d3,0x00000012,0x00050080,0x00000006,0x000004d4,0x000004d2,0x000004d3,0x0004003d, + 0x00000048,0x000004d5,0x0000004d,0x00060041,0x00000064,0x000004d6,0x000004c2,0x00000055, + 0x000004d4,0x0003003e,0x000004d6,0x000004d5,0x0004003d,0x00000006,0x000004d7,0x0000002c, + 0x0004003d,0x00000006,0x000004d8,0x0000001c,0x00050080,0x00000006,0x000004d9,0x000004d8, + 0x00000124,0x00050084,0x00000006,0x000004da,0x000004d9,0x0000002e,0x00050087,0x00000006, + 0x000004db,0x000004da,0x00000021,0x00050080,0x00000006,0x000004dc,0x000004d7,0x000004db, + 0x0004003d,0x00000006,0x000004dd,0x00000012,0x00050080,0x00000006,0x000004de,0x000004dc, + 0x000004dd,0x0004003d,0x00000048,0x000004df,0x0000004e,0x00060041,0x00000064,0x000004e0, + 0x000004c2,0x00000055,0x000004de,0x0003003e,0x000004e0,0x000004df,0x0004003d,0x00000006, + 0x000004e1,0x0000002c,0x0004003d,0x00000006,0x000004e2,0x0000001c,0x00050080,0x00000006, + 0x000004e3,0x000004e2,0x00000154,0x00050084,0x00000006,0x000004e4,0x000004e3,0x0000002e, + 0x00050087,0x00000006,0x000004e5,0x000004e4,0x00000021,0x00050080,0x00000006,0x000004e6, + 0x000004e1,0x000004e5,0x0004003d,0x00000006,0x000004e7,0x00000012,0x00050080,0x00000006, + 0x000004e8,0x000004e6,0x000004e7,0x0004003d,0x00000048,0x000004e9,0x0000004f,0x00060041, + 0x00000064,0x000004ea,0x000004c2,0x00000055,0x000004e8,0x0003003e,0x000004ea,0x000004e9, + 0x0004003d,0x00000006,0x000004eb,0x0000002c,0x0004003d,0x00000006,0x000004ec,0x0000001c, + 0x00050080,0x00000006,0x000004ed,0x000004ec,0x00000021,0x00050084,0x00000006,0x000004ee, + 0x000004ed,0x0000002e,0x00050087,0x00000006,0x000004ef,0x000004ee,0x00000021,0x00050080, + 0x00000006,0x000004f0,0x000004eb,0x000004ef,0x0004003d,0x00000006,0x000004f1,0x00000012, + 0x00050080,0x00000006,0x000004f2,0x000004f0,0x000004f1,0x0004003d,0x00000048,0x000004f3, + 0x00000050,0x00060041,0x00000064,0x000004f4,0x000004c2,0x00000055,0x000004f2,0x0003003e, + 0x000004f4,0x000004f3,0x0004003d,0x00000006,0x000004f5,0x0000002c,0x0004003d,0x00000006, + 0x000004f6,0x0000001c,0x00050080,0x00000006,0x000004f8,0x000004f6,0x000004f7,0x00050084, + 0x00000006,0x000004f9,0x000004f8,0x0000002e,0x00050087,0x00000006,0x000004fa,0x000004f9, + 0x00000021,0x00050080,0x00000006,0x000004fb,0x000004f5,0x000004fa,0x0004003d,0x00000006, + 0x000004fc,0x00000012,0x00050080,0x00000006,0x000004fd,0x000004fb,0x000004fc,0x0004003d, + 0x00000048,0x000004fe,0x00000051,0x00060041,0x00000064,0x000004ff,0x000004c2,0x00000055, + 0x000004fd,0x0003003e,0x000004ff,0x000004fe,0x0004003d,0x00000006,0x00000500,0x0000002c, + 0x0004003d,0x00000006,0x00000501,0x0000001c,0x00050080,0x00000006,0x00000503,0x00000501, + 0x00000502,0x00050084,0x00000006,0x00000504,0x00000503,0x0000002e,0x00050087,0x00000006, + 0x00000505,0x00000504,0x00000021,0x00050080,0x00000006,0x00000506,0x00000500,0x00000505, + 0x0004003d,0x00000006,0x00000507,0x00000012,0x00050080,0x00000006,0x00000508,0x00000506, + 0x00000507,0x0004003d,0x00000048,0x00000509,0x00000052,0x00060041,0x00000064,0x0000050a, + 0x000004c2,0x00000055,0x00000508,0x0003003e,0x0000050a,0x00000509,0x0004003d,0x00000006, + 0x0000050b,0x0000002c,0x0004003d,0x00000006,0x0000050c,0x0000001c,0x00050080,0x00000006, + 0x0000050e,0x0000050c,0x0000050d,0x00050084,0x00000006,0x0000050f,0x0000050e,0x0000002e, + 0x00050087,0x00000006,0x00000510,0x0000050f,0x00000021,0x00050080,0x00000006,0x00000511, + 0x0000050b,0x00000510,0x0004003d,0x00000006,0x00000512,0x00000012,0x00050080,0x00000006, + 0x00000513,0x00000511,0x00000512,0x0004003d,0x00000048,0x00000514,0x00000053,0x00060041, + 0x00000064,0x00000515,0x000004c2,0x00000055,0x00000513,0x0003003e,0x00000515,0x00000514, + 0x000200f9,0x0000003d,0x000200f8,0x0000003d,0x000100fd,0x00010038 +}; + +}}} // namespace cv::dnn::vkcom diff --git a/modules/dnn/src/vkcom/shader/spv_shader.hpp b/modules/dnn/src/vkcom/shader/spv_shader.hpp index 248983103f..9f341d6873 100644 --- a/modules/dnn/src/vkcom/shader/spv_shader.hpp +++ b/modules/dnn/src/vkcom/shader/spv_shader.hpp @@ -14,6 +14,7 @@ namespace cv { namespace dnn { namespace vkcom { extern const unsigned int dw_conv_spv[1760]; extern const unsigned int permute_spv[765]; extern const unsigned int conv48_spv[7458]; +extern const unsigned int conv48_nobias_spv[7182]; extern const unsigned int lrn_spv[1845]; extern const unsigned int concat_spv[541]; extern const unsigned int avg_pool_spv[1538]; diff --git a/modules/dnn/src/vkcom/src/op_conv.cpp b/modules/dnn/src/vkcom/src/op_conv.cpp index 4445558b2c..bd982b3eb5 100644 --- a/modules/dnn/src/vkcom/src/op_conv.cpp +++ b/modules/dnn/src/vkcom/src/op_conv.cpp @@ -167,7 +167,8 @@ bool OpConv::forward(Tensor& in, Tensor& filter_weights, Tensor& bias, Tensor& o config_.local_size_z = 1; config_.block_height = 4; config_.block_width = 8; - createShaderModule(conv48_spv, sizeof(conv48_spv)); + has_bias_ ? createShaderModule(conv48_spv, sizeof(conv48_spv)) + : createShaderModule(conv48_nobias_spv, sizeof(conv48_nobias_spv)); // specialization constants VkSpecializationInfo spec_info; ShaderConstant shader_constant; From 45a7b71cacb33d3f1b0b58d6f341ff5c8b57c479 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Fri, 16 Sep 2022 16:02:56 +0800 Subject: [PATCH 095/313] bump ADE to 0.1.2a --- cmake/mirrors/gitcode.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/mirrors/gitcode.cmake b/cmake/mirrors/gitcode.cmake index abd7a29be4..2f1e5e5f6f 100644 --- a/cmake/mirrors/gitcode.cmake +++ b/cmake/mirrors/gitcode.cmake @@ -20,10 +20,10 @@ ocv_update(TBB_PKG_NAME_GITCODE "tbb-${TBB_RELEASE_GITCODE}") ocv_update(TBB_PKG_MD5_GITCODE 4eeafdf16a90cb66e39a31c8d6c6804e) ocv_update(TBB_PKG_MD5_ORIGINAL 5af6f6c2a24c2043e62e47205e273b1f) # same as OPENCV_TBB_RELEASE_MD5 for TBB release of v2020.2 # ADE -ocv_update(ADE_RELEASE_GITCODE "v0.1.1f") +ocv_update(ADE_RELEASE_GITCODE "v0.1.2a") ocv_update(ADE_PKG_NAME_GITCODE "ade-${ADE_RELEASE_GITCODE}") -ocv_update(ADE_PKG_MD5_GITCODE c12909e0ccfa93138c820ba91ff37b3c) -ocv_update(ADE_PKG_MD5_ORIGINAL b624b995ec9c439cbc2e9e6ee940d3a2) # same as ade_md5 for ADE release of v0.1.1f +ocv_update(ADE_PKG_MD5_GITCODE 6c8015a886a98fd8a67635431fa171d8) +ocv_update(ADE_PKG_MD5_ORIGINAL fa4b3e25167319cb0fa9432ef8281945) # same as ade_md5 for ADE release of v0.1.2a # # Replace download links for packages in opencv/opencv_3rdparty: From 65998d8076fbda79c91b0b583d5cac7c1010ea7a Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Fri, 16 Sep 2022 16:04:20 +0800 Subject: [PATCH 096/313] fix a bug when download from github instead but the subdir is changed to a wrong one --- cmake/mirrors/gitcode.cmake | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmake/mirrors/gitcode.cmake b/cmake/mirrors/gitcode.cmake index 2f1e5e5f6f..ec0893f3f7 100644 --- a/cmake/mirrors/gitcode.cmake +++ b/cmake/mirrors/gitcode.cmake @@ -57,13 +57,14 @@ macro(ocv_download_url_gitcode_archive_commit_id) message(WARNING "Package ${DL_ID} from mirror gitcode.net is outdated and will be downloaded from github.com instead.") endif() endmacro() -macro(ocv_download_url_gitcode_archive_release) +macro(ocv_download_url_gitcode_archive_release SUBDIR) if(DL_HASH STREQUAL "${${DL_ID}_PKG_MD5_ORIGINAL}") string(REPLACE "/" ";" DL_URL_split ${DL_URL}) list(GET DL_URL_split 3 __OWNER) list(GET DL_URL_split 4 __REPO_NAME) set(DL_URL "https://gitcode.net/${__OWNER}/${__REPO_NAME}/-/archive/${${DL_ID}_RELEASE_GITCODE}/${__REPO_NAME}-") set(DL_HASH "${${DL_ID}_PKG_MD5_GITCODE}") + set(SUBDIR "${${DL_ID}_PKG_NAME_GITCODE}" PARENT_SCOPE) else() message(WARNING "Package ${DL_ID} from mirror gitcode.net is outdated and will be downloaded from github.com instead.") endif() @@ -76,11 +77,9 @@ elseif(DL_ID STREQUAL "wechat_qrcode") elseif((DL_ID STREQUAL "TENGINE") OR (DL_ID STREQUAL "NVIDIA_OPTICAL_FLOW") OR (DL_ID STREQUAL "TIM-VX")) ocv_download_url_gitcode_archive_commit_id() elseif(DL_ID STREQUAL "TBB") - ocv_download_url_gitcode_archive_release() - set(OPENCV_TBB_SUBDIR "${TBB_PKG_NAME_GITCODE}" PARENT_SCOPE) + ocv_download_url_gitcode_archive_release(OPENCV_TBB_SUBDIR) elseif(DL_ID STREQUAL "ADE") - ocv_download_url_gitcode_archive_release() - set(ade_subdir "${ADE_PKG_NAME_GITCODE}" PARENT_SCOPE) + ocv_download_url_gitcode_archive_release(ade_subdir) else() message(STATUS "ocv_download: Unknown download ID ${DL_ID} for using mirror gitcode.net. Use original source instead.") endif() From ec92f3fefa8421a65f64ab11a942252057483683 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Fri, 16 Sep 2022 15:24:13 +0000 Subject: [PATCH 097/313] Apply comments * Rename intersectMapWith -> mergeMapWith * Remove macro * Add r-value ref --- modules/gapi/samples/pipeline_modeling_tool.cpp | 14 +++++++++----- .../gapi/samples/pipeline_modeling_tool/utils.hpp | 13 ++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index a10cb5f052..bacd1742ea 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -193,7 +193,7 @@ CallParams read(const cv::FileNode& fn) { template std::map readMap(const cv::FileNode& fn) { std::map map; - for (auto item : fn) { + for (auto&& item : fn) { map.emplace(item.name(), read(item)); } return map; @@ -380,10 +380,14 @@ int main(int argc, char* argv[]) { builder.addDummy(call_params, read(node_fn)); } else if (node_type == "Infer") { auto infer_params = read(node_fn); - RETHROW_WITH_MSG_IF_FAILED( - utils::intersectMapWith(infer_params.config, gconfig), - "Failed to combine global and local configs for Infer node: " - + call_params.name); + try { + utils::mergeMapWith(infer_params.config, gconfig); + } catch (std::exception& e) { + std::stringstream ss; + ss << "Failed to merge global and local config for Infer node: " + << call_params.name << std::endl << e.what(); + throw std::logic_error(ss.str()); + } builder.addInfer(call_params, infer_params); } else { throw std::logic_error("Unsupported node type: " + node_type); diff --git a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp index c8f0101fe1..a5be323747 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp @@ -93,21 +93,12 @@ typename duration_t::rep timestamp() { return duration_cast(now.time_since_epoch()).count(); } -#define RETHROW_WITH_MSG_IF_FAILED(expr, msg) \ - try { \ - expr; \ - } catch (const std::exception& e) { \ - std::stringstream ss; \ - ss << msg << "\n caused by: " << e.what(); \ - throw std::logic_error(ss.str()); \ - } \ - template -void intersectMapWith(std::map& target, const std::map& second) { +void mergeMapWith(std::map& target, const std::map& second) { for (auto&& item : second) { auto it = target.find(item.first); if (it != target.end()) { - throw std::logic_error("Met already existing key: " + item.first); + throw std::logic_error("Error: key: " + it->first + " is already in target map"); } target.insert(item); } From b3adffe437d6fca1cc367b3216345b0c4cfa02a0 Mon Sep 17 00:00:00 2001 From: Giles Payne Date: Sun, 18 Sep 2022 13:41:36 +0900 Subject: [PATCH 098/313] Android Camera: poll for frame data if acquisition fails after OnCaptureCompleted --- modules/videoio/src/cap_android_camera.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/videoio/src/cap_android_camera.cpp b/modules/videoio/src/cap_android_camera.cpp index 18fc604367..96062b3d4f 100644 --- a/modules/videoio/src/cap_android_camera.cpp +++ b/modules/videoio/src/cap_android_camera.cpp @@ -157,6 +157,7 @@ void OnCaptureFailed(void* context, ACameraCaptureFailure* failure); #define CAPTURE_TIMEOUT_SECONDS 2 +#define CAPTURE_POLL_INTERVAL_MS 5 /** * Range of Camera Exposure Time: @@ -167,6 +168,10 @@ void OnCaptureFailed(void* context, static const long kMinExposureTime = 1000000L; static const long kMaxExposureTime = 250000000L; +static double elapsedTimeFrom(std::chrono::time_point start) { + return std::chrono::duration(std::chrono::system_clock::now() - start).count(); +} + class AndroidCameraCapture : public IVideoCapture { int cachedIndex; @@ -266,12 +271,21 @@ public: LOGW("No Buffer Available error occured - waiting for callback"); waitingCapture = true; captureSuccess = false; + auto start = std::chrono::system_clock::now(); bool captured = condition.wait_for(lock, std::chrono::seconds(CAPTURE_TIMEOUT_SECONDS), [this]{ return captureSuccess; }); waitingCapture = false; if (captured) { mStatus = AImageReader_acquireLatestImage(imageReader.get(), &img); + // even though an image has been captured we may not be able to acquire it straight away so we poll every 10ms + while (mStatus == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE && elapsedTimeFrom(start) < CAPTURE_TIMEOUT_SECONDS) { + std::this_thread::sleep_for(std::chrono::milliseconds(CAPTURE_POLL_INTERVAL_MS)); + mStatus = AImageReader_acquireLatestImage(imageReader.get(), &img); + } if (mStatus != AMEDIA_OK) { LOGE("Acquire image failed with error code: %d", mStatus); + if (elapsedTimeFrom(start) >= CAPTURE_TIMEOUT_SECONDS) { + LOGE("Image acquisition timed out"); + } return false; } } else { From 4aef9b1c93020f28626005ee5f3b9936ecd24f7e Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Mon, 19 Sep 2022 18:38:03 +0800 Subject: [PATCH 099/313] dnn: support yolov7 (not simplified) --- modules/dnn/src/onnx/onnx_importer.cpp | 86 +++++++++++++++++++++++-- modules/dnn/test/test_onnx_importer.cpp | 84 ++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 7 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 259fd29eeb..7aed220a86 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -180,6 +180,7 @@ private: void parseCumSum (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseElementWise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseDepthToSpace (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseSimpleLayers (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); // Domain: com.microsoft @@ -2427,9 +2428,6 @@ void ONNXImporter::parseExpand(LayerParams& layerParams, const opencv_onnx::Node if (!haveVariables) { - if (broadcast_axes.size() > 1) - CV_Error(Error::StsNotImplemented, "Expand op doesn't support multiple axes for constant input"); - if (broadcast_axes.empty()) { addConstant(output_name, getBlob(node_proto, 0)); @@ -2437,10 +2435,15 @@ void ONNXImporter::parseExpand(LayerParams& layerParams, const opencv_onnx::Node } Mat input = getBlob(node_proto, 0); - input = input.reshape(0, total(inpShape, 0, broadcast_axes[0])); - Mat output = cv::repeat(input, 1, targetShape[broadcast_axes[0]]); - output = output.reshape(0, targetShape); - addConstant(output_name, output); + MatShape subTargetShape = inpShape; + for (auto broadcast_axis : broadcast_axes) + { + subTargetShape[broadcast_axis] = targetShape[broadcast_axis]; + input = input.reshape(0, total(inpShape, 0, broadcast_axis)); + Mat output = cv::repeat(input, 1, subTargetShape[broadcast_axis]); + input = output.reshape(0, subTargetShape); + } + addConstant(output_name, input); return; } @@ -2497,6 +2500,12 @@ void ONNXImporter::parseReshape(LayerParams& layerParams, const opencv_onnx::Nod std::vector inputs(1, getBlob(node_proto, 0)), outputs; runLayer(layerParams, inputs, outputs); addConstant(node_proto.output(0), outputs[0]); + if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end()) + { + const int real_ndims_input0 = getBlobExtraInfo(node_proto, 0).real_ndims; + if (real_ndims_input0 == 1 && blob.total() == 1 && blob.at() == -1) // 1D tensor as input0 (data), and shape is -1 + constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1))); + } return; } } @@ -2548,7 +2557,14 @@ void ONNXImporter::parseShape(LayerParams& layerParams, const opencv_onnx::NodeP CV_Assert(shapeIt != outShapes.end()); const MatShape& inpShape = shapeIt->second; + bool isInput1D = false; + if (constBlobsExtraInfo.find(node_proto.input(0)) != constBlobsExtraInfo.end()) + if (getBlobExtraInfo(node_proto, 0).real_ndims == 1) + isInput1D = true; + int dims = static_cast(inpShape.size()); + if (isInput1D) + dims = 1; Mat shapeMat(dims, 1, CV_32S); bool isDynamicShape = false; for (int j = 0; j < dims; ++j) @@ -3080,8 +3096,63 @@ void ONNXImporter::parseDepthToSpace(LayerParams& layerParams, const opencv_onnx addLayer(layerParams, node_proto); } +// Currently we only support range with all constant inputs +void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + CV_Assert(node_proto.input_size() == 3); // 0 - start, 1 - limit, 2 - delta + layerParams.type = "Range"; + + std::vector const_id; + for (int i = 0; i < node_proto.input_size(); i++) + if (layer_id.find(node_proto.input(i)) == layer_id.end()) + const_id.push_back(i); + + // only supports the case which all inputs are constant + CV_Assert(const_id.size() == 3); + + Mat startMat = getBlob(node_proto, 0); + CV_Assert(startMat.type() == CV_32SC1); + int start = startMat.at(0); + + Mat limitMat = getBlob(node_proto, 1); + CV_Assert(limitMat.type() == CV_32SC1); + int limit = limitMat.at(0); + + Mat deltaMat = getBlob(node_proto, 2); + CV_Assert(deltaMat.type() == CV_32SC1); + int delta = deltaMat.at(0); + + int number_of_elements = std::max(int(std::ceil((limit - start) / delta)), 0); + Mat r(number_of_elements, 1, CV_32SC1); + for (int i = 0; i < number_of_elements; i++) + { + r.at(i) = start + (i * delta); + } + addConstant(node_proto.output(0), r); + constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1))); +} + void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { + bool is_all_input_const = true; + for (int i = 0; i < node_proto.input_size(); i++) + { + if (layer_id.find(node_proto.input(i)) != layer_id.end()) + { + is_all_input_const = false; + break; + } + } + if (is_all_input_const && node_proto.output_size() == 1) + { + std::vector input, output; + for (int i = 0; i < node_proto.input_size(); i++) + input.push_back(getBlob(node_proto, i)); + runLayer(layerParams, input, output); + addConstant(node_proto.output(0), output[0]); + return; + } + for (int j = 0; j < node_proto.input_size(); j++) { if (layer_id.find(node_proto.input(j)) == layer_id.end()) layerParams.blobs.push_back(getBlob(node_proto, j)); @@ -3685,6 +3756,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = dispatch["Pow"] = dispatch["Add"] = dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = &ONNXImporter::parseElementWise; dispatch["Sum"] = dispatch["Min"] = dispatch["Max"] = &ONNXImporter::parseElementWise; + dispatch["Range"] = &ONNXImporter::parseRange; std::vector simpleLayers{"Acos", "Acosh", "Asin", "Asinh", "Atan", "Atanh", "Ceil", "Celu", "Cos", "Cosh", "Dropout", "Erf", "Exp", "Floor", "HardSigmoid", "HardSwish", diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 554c4b7e66..d956f67490 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -2330,6 +2330,90 @@ TEST_P(Test_ONNX_layers, CumSum) testONNXModels("cumsum_3d_dim_2"); } +// This test is mainly to test: +// 1. identity node with constant input +// 2. limited support to range operator (all inputs are constant) +// 3. parseExpand with multiple broadcast axes +// 4. 1D mat dimension issue with the output of range operator +TEST_P(Test_ONNX_layers, YOLOv7) +{ + std::string weightPath = _tf("models/yolov7_not_simplified.onnx"); + std::string imgPath = _tf("../dog_orig_size.png"); + + Size targetSize{640, 640}; + float conf_threshold = 0.3; + float iou_threshold = 0.5; + + // Reference, which is collected with input size of 640x640 + std::vector refClassIds{1, 16, 7}; + std::vector refScores{0.9614331f, 0.9589417f, 0.8679074f}; + // [x1, y1, x2, y2] x 3 + std::vector refBoxes{Rect2d(105.973236f, 150.16716f, 472.59012f, 466.48834f), + Rect2d(109.97953f, 246.17862f, 259.83676f, 600.76624f), + Rect2d(385.96185f, 83.02809f, 576.07355f, 189.82793f)}; + + Mat img = imread(imgPath); + Mat inp = blobFromImage(img, 1/255.0, targetSize, Scalar(0, 0, 0), true, false); + + Net net = readNet(weightPath); + + net.setInput(inp); + std::vector outs; + net.forward(outs, net.getUnconnectedOutLayersNames()); + + Mat preds = outs[3].reshape(1, outs[3].size[1]); // [1, 25200, 85] + + // Retrieve + std::vector classIds; + std::vector confidences; + std::vector boxes; + // each row is [cx, cy, w, h, conf_obj, conf_class1, ..., conf_class80] + for (int i = 0; i < preds.rows; ++i) + { + // filter out non objects + float obj_conf = preds.row(i).at(4); + if (obj_conf < conf_threshold) + continue; + + // get class id and conf + Mat scores = preds.row(i).colRange(5, preds.cols); + double conf; + Point maxLoc; + minMaxLoc(scores, 0, &conf, 0, &maxLoc); + conf *= obj_conf; + if (conf < conf_threshold) + continue; + + // get bbox coords + float* det = preds.ptr(i); + double cx = det[0]; + double cy = det[1]; + double w = det[2]; + double h = det[3]; + // [x1, y1, x2, y2] + boxes.push_back(Rect2d(cx - 0.5 * w, cy - 0.5 * h, + cx + 0.5 * w, cy + 0.5 * h)); + classIds.push_back(maxLoc.x); + confidences.push_back(conf); + } + + // NMS + std::vector keep_idx; + NMSBoxes(boxes, confidences, conf_threshold, iou_threshold, keep_idx); + + std::vector keep_classIds; + std::vector keep_confidences; + std::vector keep_boxes; + for (auto i : keep_idx) + { + keep_classIds.push_back(classIds[i]); + keep_confidences.push_back(confidences[i]); + keep_boxes.push_back(boxes[i]); + } + + normAssertDetections(refClassIds, refScores, refBoxes, keep_classIds, keep_confidences, keep_boxes); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets()); }} // namespace From 65f71ce2ebec60868b4780551032e805ac8ffc2b Mon Sep 17 00:00:00 2001 From: Egor Smirnov Date: Wed, 16 Mar 2022 18:41:39 +0300 Subject: [PATCH 100/313] add Gather implementation --- .../dnn/include/opencv2/dnn/all_layers.hpp | 8 ++ modules/dnn/src/init.cpp | 1 + modules/dnn/src/layers/gather_layer.cpp | 91 +++++++++++++++ modules/dnn/src/onnx/onnx_importer.cpp | 104 +++++++----------- modules/dnn/test/test_onnx_importer.cpp | 15 ++- 5 files changed, 149 insertions(+), 70 deletions(-) create mode 100644 modules/dnn/src/layers/gather_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 66ba08710e..f87a46ba5e 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -301,6 +301,14 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + /** @brief Gather layer + */ + class CV_EXPORTS GatherLayer : public Layer + { + public: + static Ptr create(const LayerParams& params); + }; + class CV_EXPORTS PoolingLayer : public Layer { public: diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index e3ce6de40d..902b6dae88 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -147,6 +147,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Const, ConstLayer); CV_DNN_REGISTER_LAYER_CLASS(Arg, ArgLayer); CV_DNN_REGISTER_LAYER_CLASS(Reciprocal, ReciprocalLayer); + CV_DNN_REGISTER_LAYER_CLASS(Gather, GatherLayer); CV_DNN_REGISTER_LAYER_CLASS(Crop, CropLayer); CV_DNN_REGISTER_LAYER_CLASS(Eltwise, EltwiseLayer); diff --git a/modules/dnn/src/layers/gather_layer.cpp b/modules/dnn/src/layers/gather_layer.cpp new file mode 100644 index 0000000000..8d93a85dc4 --- /dev/null +++ b/modules/dnn/src/layers/gather_layer.cpp @@ -0,0 +1,91 @@ +// 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" +#include "layers_common.hpp" + + +namespace cv { namespace dnn { + +class GatherLayerImpl CV_FINAL : public GatherLayer +{ +public: + GatherLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + m_axis = params.get("axis", 0); + m_real_ndims = params.get("real_ndims", -1); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE + { + CV_CheckEQ(inputs.size(), 2ull, ""); + MatShape inpShape = inputs[0]; + const int axis = normalize_axis(m_axis, inpShape); + + inpShape.erase(inpShape.begin() + axis); + auto end = m_real_ndims == -1 ? inputs[1].end() : inputs[1].begin() + m_real_ndims; + inpShape.insert(inpShape.begin() + axis, inputs[1].begin(), end); + + outputs.assign(1, inpShape); + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const Mat& inp = inputs[0]; + const Mat& indices = inputs[1]; + Mat& out = outputs[0]; + + const int axis = normalize_axis(m_axis, shape(inp)); + + const size_t outer_size = axis == 0 ? inp.total() : inp.step1(axis - 1); + const size_t outer_dims = inp.total() / outer_size; + const size_t inner_size = inp.step1(axis); + + const float* idx = indices.ptr(); // TODO: change type to integer in the future. + const char* src = inp.ptr(); + char* dst = out.ptr(); + + const size_t es = inp.elemSize1(); + for (size_t i = 0; i < outer_dims; ++i) + { + const size_t src_offset = i * outer_size; + for (size_t j = 0 ; j < indices.total(); ++j) + { + const size_t index = (static_cast(idx[j]) + inp.size[axis]) % inp.size[axis]; + const size_t new_offset = src_offset + index * inp.step1(axis); + std::memcpy(dst, src + new_offset * es, inner_size * es); + dst += inner_size * es; + } + } + } + +private: + // The axis to gather along + int m_axis; + int m_real_ndims; +}; + +Ptr GatherLayer::create(const LayerParams& params) +{ + return makePtr(params); +} + +}} // namespace cv::dnn diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 259fd29eeb..59f7c4d1f9 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2622,83 +2622,57 @@ void ONNXImporter::parseConstantFill(LayerParams& layerParams, const opencv_onnx addConstant(node_proto.output(0), tensor); } -void ONNXImporter::parseGather(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) +void ONNXImporter::parseGather(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - opencv_onnx::NodeProto node_proto = node_proto_; - CV_Assert(node_proto.input_size() == 2); - Mat indexMat = getBlob(node_proto, 1); - CV_Assert_N(indexMat.type() == CV_32S, indexMat.total() == 1); - int index = indexMat.at(0); - int axis = layerParams.get("axis", 0); + CV_CheckEQ(node_proto.input_size(), 2, ""); - if ((constBlobs.find(node_proto.input(0)) != constBlobs.end())) + // TODO: get rid of the type conversions and 1-d/0-d special-casing when the time comes + if (layer_id.find(node_proto.input(1)) == layer_id.end()) { - Mat input = getBlob(node_proto, 0); - Mat out; - std::vector ranges(input.dims, Range::all()); - ranges[axis] = Range(index, index + 1); - - out = input(ranges); - MatShape outShape = shape(out); - if (outShape.size() > 1) + int real_ndims = getBlobExtraInfo(node_proto.input(1)).real_ndims; + layerParams.set("real_ndims", real_ndims); + if (layer_id.find(node_proto.input(0)) == layer_id.end()) { - outShape.erase(outShape.begin() + axis); - out.reshape(0, outShape); - } else { - out.dims = 1; + std::vector inputs, output; + + Mat input = getBlob(node_proto, 0); + int input_real_ndims = input.dims; + int type = input.type(); + input.convertTo(input, CV_32FC1); + inputs.push_back(input); + + Mat indices = getBlob(node_proto, 1); + indices.convertTo(indices, CV_32FC1); + inputs.push_back(indices); + + runLayer(layerParams, inputs, output); + output.back().convertTo(output.back(), type); + output.back().dims = std::max(input_real_ndims - real_ndims, 1); + addConstant(node_proto.output(0), output.back()); + return; } - addConstant(node_proto.output(0), out); - return; } - else + + for (int i = 0; i < node_proto.input_size(); ++i) { - IterShape_t shapeIt = outShapes.find(node_proto.input(0)); - CV_Assert(shapeIt != outShapes.end()); - MatShape inpShape = shapeIt->second; - - LayerParams sliceLp; - sliceLp.type = "Slice"; - sliceLp.name = inpShape.size() > 1 ? layerParams.name + "/slice" : layerParams.name; - std::vector begin(inpShape.size(), 0); - std::vector end(inpShape.size(), INT_MAX); - begin[axis] = index; - end[axis] = index + 1; - - cv::dnn::DictValue paramBegin = cv::dnn::DictValue::arrayInt(begin.data(), begin.size()); - cv::dnn::DictValue paramEnd = cv::dnn::DictValue::arrayInt(end.data(), end.size()); - sliceLp.set("begin", paramBegin); - sliceLp.set("end", paramEnd); - sliceLp.set("has_dynamic_shapes", hasDynamicShapes); - - if (inpShape.size() > 1) + if (layer_id.find(node_proto.input(i)) == layer_id.end()) { - opencv_onnx::NodeProto proto; - proto.add_input(node_proto.input(0)); - proto.add_output(sliceLp.name); - addLayer(sliceLp, proto); - - inpShape.erase(inpShape.begin() + axis); - layerParams.type = "Reshape"; - layerParams.set("axis", 0); - layerParams.set("dim", DictValue::arrayInt(&inpShape[0], inpShape.size())); - if (hasDynamicShapes) + LayerParams constParams; + constParams.name = node_proto.input(i); + constParams.type = "Const"; + Mat blob = getBlob(node_proto, i); + if (i == 1) { - std::vector dynamicAxes; - std::vector inputIndices; - for (int index = 0; index < inpShape.size(); ++index) - dynamicAxes.push_back(index); - for (int index = 0; index < inpShape.size(); ++index) - inputIndices.push_back(index); - layerParams.set("dynamic_axes", DictValue::arrayInt(dynamicAxes.data(), dynamicAxes.size())); - layerParams.set("input_indices", DictValue::arrayInt(inputIndices.data(), inputIndices.size())); + blob.convertTo(blob, CV_32FC1); } - node_proto.set_input(0, sliceLp.name); - } - else - { - layerParams = sliceLp; + constParams.blobs.push_back(blob); + + opencv_onnx::NodeProto proto; + proto.add_output(constParams.name); + addLayer(constParams, proto); } } + addLayer(layerParams, node_proto); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 554c4b7e66..9e74e38fc1 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -207,11 +207,16 @@ TEST_P(Test_ONNX_layers, Gather) { if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); - testONNXModels("gather"); + testONNXModels("gather", npy, 0, 0, false, false); + testONNXModels("gather_scalar", npy, 0, 0, false, false); +} + +TEST_P(Test_ONNX_layers, GatherMulti) +{ // GPU plugin unsupported slice for constant if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL, CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); - testONNXModels("gather_scalar", npy, 0, 0, false, false); + testONNXModels("gather_multi", npy, 0, 0, false, false); } TEST_P(Test_ONNX_layers, Convolution3D) @@ -1424,7 +1429,7 @@ TEST_P(Test_ONNX_layers, GatherMultiOutput) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE); #endif - testONNXModels("gather_multi_output"); + testONNXModels("gather_multi_output", npy, 0, 0, false, false); } TEST_P(Test_ONNX_layers, DynamicAxes_squeeze_and_conv) @@ -1475,7 +1480,7 @@ TEST_P(Test_ONNX_layers, DynamicAxes_gather) } #endif #endif - testONNXModels("gather_dynamic_axes"); + testONNXModels("gather_dynamic_axes", npy, 0, 0, false, false); } TEST_P(Test_ONNX_layers, DynamicAxes_gather_scalar) @@ -1504,7 +1509,7 @@ TEST_P(Test_ONNX_layers, DynamicAxes_gather_scalar) } #endif #endif - testONNXModels("gather_scalar_dynamic_axes"); + testONNXModels("gather_scalar_dynamic_axes", npy, 0, 0, false, false); } TEST_P(Test_ONNX_layers, DynamicAxes_slice) From 062cee2933ebf9390a8ff442f8c806369976da29 Mon Sep 17 00:00:00 2001 From: Berke Date: Fri, 17 Jun 2022 02:10:25 +0300 Subject: [PATCH 101/313] new multipage image decoder api - ImageCollection --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 45 +++ modules/imgcodecs/src/loadsave.cpp | 298 +++++++++++++++--- modules/imgcodecs/test/test_read_write.cpp | 177 +++++++++++ 3 files changed, 471 insertions(+), 49 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 67ca34fe55..cb4a170c28 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename ); */ CV_EXPORTS_W bool haveImageWriter( const String& filename ); +/** @brief To read Multi Page images on demand + +The ImageCollection class provides iterator API to read multi page images on demand. Create iterator +to the collection of the images and iterate over the collection. Decode the necessary page with operator*. + +The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page, +then the time Complexity is O(n) because the collection has to be reinitialized every time in order to go to the correct page. +However, the intermediate pages are not decoded during the process, so typically it's quite fast. +This is required because multipage codecs does not support going backwards. +After decoding the one page, it is stored inside the collection cache. Hence, trying to get Mat object from already decoded page is O(1). +If you need memory, you can use .releaseCache() method to release cached index. +The space complexity is O(n) if all pages are decoded into memory. The user is able to decode and release images on demand. +*/ +class CV_EXPORTS ImageCollection { +public: + struct CV_EXPORTS iterator { + iterator(ImageCollection* col); + iterator(ImageCollection* col, int end); + Mat& operator*(); + Mat* operator->(); + iterator& operator++(); + iterator operator++(int); + friend bool operator== (const iterator& a, const iterator& b) { return a.m_curr == b.m_curr; }; + friend bool operator!= (const iterator& a, const iterator& b) { return a.m_curr != b.m_curr; }; + + private: + ImageCollection* m_pCollection; + int m_curr; + }; + + ImageCollection(); + ImageCollection(const String& filename, int flags); + void init(const String& img, int flags); + size_t size() const; + const Mat& at(int index); + const Mat& operator[](int index); + void releaseCache(int index); + iterator begin(); + iterator end(); + + class Impl; + Ptr getImpl(); +protected: + Ptr pImpl; +}; //! @} imgcodecs diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index e9b6d0517c..a973c86b4f 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -54,6 +54,8 @@ #include #include #include +#include + /****************************************************************************************\ @@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector& mats, int start, int static size_t imcount_(const String& filename, int flags) { - /// Search for the relevant decoder to handle the imagery - ImageDecoder decoder; - -#ifdef HAVE_GDAL - if (flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) { - decoder = GdalDecoder().newDecoder(); - } - else { -#else - CV_UNUSED(flags); -#endif - decoder = findDecoder(filename); -#ifdef HAVE_GDAL - } -#endif - - /// if no decoder was found, return nothing. - if (!decoder) { + try{ + ImageCollection collection(filename, flags); + return collection.size(); + } catch(cv::Exception const& e) { + // Reading header or finding decoder for the filename is failed return 0; } - - /// set the filename in the driver - decoder->setSource(filename); - - // read the header to make sure it succeeds - try - { - // read the header to make sure it succeeds - if (!decoder->readHeader()) - return 0; - } - catch (const cv::Exception& e) - { - std::cerr << "imcount_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush; - return 0; - } - catch (...) - { - std::cerr << "imcount_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush; - return 0; - } - - size_t result = 1; - - - while (decoder->nextPage()) - { - ++result; - } - - return result; + return 0; } size_t imcount(const String& filename, int flags) @@ -1032,6 +991,247 @@ bool haveImageWriter( const String& filename ) return !encoder.empty(); } +class ImageCollection::Impl { +public: + Impl() = default; + Impl(const std::string& filename, int flags); + void init(String const& filename, int flags); + size_t size() const; + Mat& at(int index); + Mat& operator[](int index); + void releaseCache(int index); + ImageCollection::iterator begin(ImageCollection* ptr); + ImageCollection::iterator end(ImageCollection* ptr); + Mat read(); + int width() const; + int height() const; + bool readHeader(); + Mat readData(); + bool advance(); + int currentIndex() const; + void reset(); + +private: + String m_filename; + int m_flags{}; + std::size_t m_size{}; + int m_width{}; + int m_height{}; + int m_current{}; + std::vector m_pages; + ImageDecoder m_decoder; +}; + +ImageCollection::Impl::Impl(std::string const& filename, int flags) { + this->init(filename, flags); +} + +void ImageCollection::Impl::init(String const& filename, int flags) { + m_filename = filename; + m_flags = flags; + +#ifdef HAVE_GDAL + if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) { + m_decoder = GdalDecoder().newDecoder(); + } + else { +#endif + m_decoder = findDecoder(filename); +#ifdef HAVE_GDAL + } +#endif + + + CV_Assert(m_decoder); + m_decoder->setSource(filename); + CV_Assert(m_decoder->readHeader()); + + // count the pages of the image collection + size_t count = 1; + while(m_decoder->nextPage()) count++; + + m_size = count; + m_pages.resize(m_size); + // Reinitialize the decoder because we advanced to the last page while counting the pages of the image +#ifdef HAVE_GDAL + if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) { + m_decoder = GdalDecoder().newDecoder(); + } + else { +#endif + m_decoder = findDecoder(m_filename); +#ifdef HAVE_GDAL + } +#endif + + m_decoder->setSource(m_filename); + m_decoder->readHeader(); +} + +size_t ImageCollection::Impl::size() const { return m_size; } + +Mat ImageCollection::Impl::read() { + auto result = this->readHeader(); + if(!result) { + return {}; + } + return this->readData(); +} + +int ImageCollection::Impl::width() const { + return m_width; +} + +int ImageCollection::Impl::height() const { + return m_height; +} + +bool ImageCollection::Impl::readHeader() { + bool status = m_decoder->readHeader(); + m_width = m_decoder->width(); + m_height = m_decoder->height(); + return status; +} + +// readHeader must be called before calling this method +Mat ImageCollection::Impl::readData() { + int type = m_decoder->type(); + if ((m_flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && m_flags != IMREAD_UNCHANGED) { + if ((m_flags & IMREAD_ANYDEPTH) == 0) + type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); + + if ((m_flags & IMREAD_COLOR) != 0 || + ((m_flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1)) + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); + else + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); + } + + // established the required input image size + Size size = validateInputImageSize(Size(m_width, m_height)); + + Mat mat(size.height, size.width, type); + bool success = false; + try { + if (m_decoder->readData(mat)) + success = true; + } + catch (const cv::Exception &e) { + std::cerr << "ImageCollection class: can't read data: " << e.what() << std::endl << std::flush; + } + catch (...) { + std::cerr << "ImageCollection class:: can't read data: unknown exception" << std::endl << std::flush; + } + if (!success) + return cv::Mat(); + + if ((m_flags & IMREAD_IGNORE_ORIENTATION) == 0 && m_flags != IMREAD_UNCHANGED) { + ApplyExifOrientation(m_decoder->getExifTag(ORIENTATION), mat); + } + + return mat; +} + +bool ImageCollection::Impl::advance() { ++m_current; return m_decoder->nextPage(); } + +int ImageCollection::Impl::currentIndex() const { return m_current; } + +ImageCollection::iterator ImageCollection::Impl::begin(ImageCollection* ptr) { return ImageCollection::iterator(ptr); } + +ImageCollection::iterator ImageCollection::Impl::end(ImageCollection* ptr) { return ImageCollection::iterator(ptr, this->size()); } + +void ImageCollection::Impl::reset() { + m_current = 0; +#ifdef HAVE_GDAL + if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) { + m_decoder = GdalDecoder().newDecoder(); + } + else { +#endif + m_decoder = findDecoder(m_filename); +#ifdef HAVE_GDAL + } +#endif + + m_decoder->setSource(m_filename); + m_decoder->readHeader(); +} + +Mat& ImageCollection::Impl::at(int index) { + CV_Assert(index >= 0 && size_t(index) < m_size); + return operator[](index); +} + +Mat& ImageCollection::Impl::operator[](int index) { + if(m_pages.at(index).empty()) { + // We can't go backward in multi images. If the page is not in vector yet, + // go back to first page and advance until the desired page and read it into memory + if(m_current != index) { + reset(); + for(int i = 0; i != index && advance(); ++i) {} + } + m_pages[index] = read(); + } + return m_pages[index]; +} + +void ImageCollection::Impl::releaseCache(int index) { + CV_Assert(index >= 0 && size_t(index) < m_size); + m_pages[index].release(); +} + +/* ImageCollection API*/ + +ImageCollection::ImageCollection() : pImpl(new Impl()) {} + +ImageCollection::ImageCollection(const std::string& filename, int flags) : pImpl(new Impl(filename, flags)) {} + +void ImageCollection::init(const String& img, int flags) { pImpl->init(img, flags); } + +size_t ImageCollection::size() const { return pImpl->size(); } + +const Mat& ImageCollection::at(int index) { return pImpl->at(index); } + +const Mat& ImageCollection::operator[](int index) { return pImpl->operator[](index); } + +void ImageCollection::releaseCache(int index) { pImpl->releaseCache(index); } + +Ptr ImageCollection::getImpl() { return pImpl; } + +/* Iterator API */ + +ImageCollection::iterator ImageCollection::begin() { return pImpl->begin(this); } + +ImageCollection::iterator ImageCollection::end() { return pImpl->end(this); } + +ImageCollection::iterator::iterator(ImageCollection* col) : m_pCollection(col), m_curr(0) {} + +ImageCollection::iterator::iterator(ImageCollection* col, int end) : m_pCollection(col), m_curr(end) {} + +Mat& ImageCollection::iterator::operator*() { + CV_Assert(m_pCollection); + return m_pCollection->getImpl()->operator[](m_curr); +} + +Mat* ImageCollection::iterator::operator->() { + CV_Assert(m_pCollection); + return &m_pCollection->getImpl()->operator[](m_curr); +} + +ImageCollection::iterator& ImageCollection::iterator::operator++() { + if(m_pCollection->pImpl->currentIndex() == m_curr) { + m_pCollection->pImpl->advance(); + } + m_curr++; + return *this; +} + +ImageCollection::iterator ImageCollection::iterator::operator++(int) { + iterator tmp = *this; + ++(*this); + return tmp; +} + } /* End of file. */ diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 9dbd2e33c7..b81d34fdf5 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat) EXPECT_EQ(0, remove(dst_name.c_str())); } +TEST(Imgcodecs_Image, multipage_collection_size) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + + ImageCollection collection(filename, IMREAD_ANYCOLOR); + EXPECT_EQ((std::size_t)6, collection.size()); +} + +TEST(Imgcodecs_Image, multipage_collection_read_pages_iterator) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + root + "readwrite/multipage_p1.tif", + root + "readwrite/multipage_p2.tif", + root + "readwrite/multipage_p3.tif", + root + "readwrite/multipage_p4.tif", + root + "readwrite/multipage_p5.tif", + root + "readwrite/multipage_p6.tif" + }; + + ImageCollection collection(filename, IMREAD_ANYCOLOR); + + auto collectionBegin = collection.begin(); + for(size_t i = 0; i < collection.size(); ++i, ++collectionBegin) + { + double diff = cv::norm(collectionBegin.operator*(), imread(page_files[i]), NORM_INF); + EXPECT_EQ(0., diff); + } +} + +TEST(Imgcodecs_Image, multipage_collection_two_iterator) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + root + "readwrite/multipage_p1.tif", + root + "readwrite/multipage_p2.tif", + root + "readwrite/multipage_p3.tif", + root + "readwrite/multipage_p4.tif", + root + "readwrite/multipage_p5.tif", + root + "readwrite/multipage_p6.tif" + }; + + ImageCollection collection(filename, IMREAD_ANYCOLOR); + auto firstIter = collection.begin(); + auto secondIter = collection.begin(); + + // Decode all odd pages then decode even pages -> 1, 0, 3, 2 ... + firstIter++; + for(size_t i = 1; i < collection.size(); i += 2, ++firstIter, ++firstIter, ++secondIter, ++secondIter) { + Mat mat = *firstIter; + double diff = cv::norm(mat, imread(page_files[i]), NORM_INF); + EXPECT_EQ(0., diff); + Mat evenMat = *secondIter; + diff = cv::norm(evenMat, imread(page_files[i-1]), NORM_INF); + EXPECT_EQ(0., diff); + } +} + +TEST(Imgcodecs_Image, multipage_collection_operator_plusplus) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + + // operator++ test + ImageCollection collection(filename, IMREAD_ANYCOLOR); + auto firstIter = collection.begin(); + auto secondIter = firstIter++; + + // firstIter points to second page, secondIter points to first page + double diff = cv::norm(*firstIter, *secondIter, NORM_INF); + EXPECT_NE(diff, 0.); +} + +TEST(Imgcodecs_Image, multipage_collection_backward_decoding) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + root + "readwrite/multipage_p1.tif", + root + "readwrite/multipage_p2.tif", + root + "readwrite/multipage_p3.tif", + root + "readwrite/multipage_p4.tif", + root + "readwrite/multipage_p5.tif", + root + "readwrite/multipage_p6.tif" + }; + + ImageCollection collection(filename, IMREAD_ANYCOLOR); + EXPECT_EQ((size_t)6, collection.size()); + + // backward decoding -> 5,4,3,2,1,0 + for(int i = (int)collection.size() - 1; i >= 0; --i) + { + cv::Mat ithPage = imread(page_files[i]); + EXPECT_FALSE(ithPage.empty()); + double diff = cv::norm(collection[i], ithPage, NORM_INF); + EXPECT_EQ(diff, 0.); + } + + for(int i = 0; i < (int)collection.size(); ++i) + { + collection.releaseCache(i); + } + + double diff = cv::norm(collection[2], imread(page_files[2]), NORM_INF); + EXPECT_EQ(diff, 0.); +} + +TEST(ImgCodecs, multipage_collection_decoding_range_based_for_loop_test) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + root + "readwrite/multipage_p1.tif", + root + "readwrite/multipage_p2.tif", + root + "readwrite/multipage_p3.tif", + root + "readwrite/multipage_p4.tif", + root + "readwrite/multipage_p5.tif", + root + "readwrite/multipage_p6.tif" + }; + + ImageCollection collection(filename, IMREAD_ANYCOLOR); + + size_t index = 0; + for(auto &i: collection) + { + cv::Mat ithPage = imread(page_files[index]); + EXPECT_FALSE(ithPage.empty()); + double diff = cv::norm(i, ithPage, NORM_INF); + EXPECT_EQ(0., diff); + ++index; + } + EXPECT_EQ(index, collection.size()); + + index = 0; + for(auto &&i: collection) + { + cv::Mat ithPage = imread(page_files[index]); + EXPECT_FALSE(ithPage.empty()); + double diff = cv::norm(i, ithPage, NORM_INF); + EXPECT_EQ(0., diff); + ++index; + } + EXPECT_EQ(index, collection.size()); +} + +TEST(ImgCodecs, multipage_collection_two_iterator_operatorpp) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + + ImageCollection imcol(filename, IMREAD_ANYCOLOR); + + auto it0 = imcol.begin(), it1 = it0, it2 = it0; + vector img(6); + for (int i = 0; i < 6; i++) { + img[i] = *it0; + it0->release(); + ++it0; + } + + for (int i = 0; i < 3; i++) { + ++it2; + } + + for (int i = 0; i < 3; i++) { + auto img2 = *it2; + auto img1 = *it1; + ++it2; + ++it1; + EXPECT_TRUE(cv::norm(img2, img[i+3], NORM_INF) == 0); + EXPECT_TRUE(cv::norm(img1, img[i], NORM_INF) == 0); + } +} + }} // namespace From 2882725927553e5a5882e3d054aae44f36833674 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Sep 2022 21:01:06 +0200 Subject: [PATCH 102/313] build: harden lint_python.yml permissions Signed-off-by: Alex --- .github/workflows/lint_python.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index cbc27489db..b21191934c 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -1,5 +1,7 @@ name: lint_python on: workflow_dispatch +permissions: + contents: read # to fetch code (actions/checkout) jobs: lint_python: runs-on: ubuntu-latest From 7d96ef2671e5067da306049486bb7daa4d581662 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Sep 2022 21:01:34 +0200 Subject: [PATCH 103/313] build: harden arm64-build-checks.yml permissions Signed-off-by: Alex --- .github/workflows/arm64-build-checks.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/arm64-build-checks.yml b/.github/workflows/arm64-build-checks.yml index d3cf532d59..e0d374ea77 100644 --- a/.github/workflows/arm64-build-checks.yml +++ b/.github/workflows/arm64-build-checks.yml @@ -2,6 +2,9 @@ name: arm64 build checks on: workflow_dispatch +permissions: + contents: read # to fetch code (actions/checkout) + jobs: build: From 3f4abcb2280fba1ef5057f100d7de7197ce39549 Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Tue, 20 Sep 2022 13:34:17 +0300 Subject: [PATCH 104/313] Disabled compiling warnings in case of symbols in cmake for 3.4 --- 3rdparty/openexr/CMakeLists.txt | 1 + cmake/OpenCVCompilerOptions.cmake | 1 + modules/core/include/opencv2/core/cuda.hpp | 8 ++++ .../core/include/opencv2/core/cuda.inl.hpp | 40 +++++++++++++++++++ modules/core/include/opencv2/core/matx.hpp | 8 ++++ modules/core/include/opencv2/core/opengl.hpp | 8 ++++ .../opencv2/core/utils/filesystem.private.hpp | 2 +- modules/features2d/src/affine_feature.cpp | 2 +- .../opencv2/stitching/detail/warpers.hpp | 24 +++++++++++ 9 files changed, 92 insertions(+), 2 deletions(-) diff --git a/3rdparty/openexr/CMakeLists.txt b/3rdparty/openexr/CMakeLists.txt index 1fbfa4f857..f17d05a472 100644 --- a/3rdparty/openexr/CMakeLists.txt +++ b/3rdparty/openexr/CMakeLists.txt @@ -123,6 +123,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4334) # vs2005 Win64 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244) # vs2008 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4267) # vs2008 Win64 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4456) # vs2015 +ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4819) # vs2019 Win64 if(MSVC AND CV_ICC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qrestrict") diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index bbfd889690..5b77ebf0d6 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -426,6 +426,7 @@ if(MSVC) ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4275) # non dll-interface class 'std::exception' used as base for dll-interface class 'cv::Exception' ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4512) # Assignment operator could not be generated ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4589) # Constructor of abstract class 'cv::ORB' ignores initializer for virtual base class 'cv::Algorithm' + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4819) # Symbols like delta or epsilon cannot be represented endif() if(CV_ICC AND NOT ENABLE_NOISY_WARNINGS) diff --git a/modules/core/include/opencv2/core/cuda.hpp b/modules/core/include/opencv2/core/cuda.hpp index 7d7bb62e16..527e2e9dc2 100644 --- a/modules/core/include/opencv2/core/cuda.hpp +++ b/modules/core/include/opencv2/core/cuda.hpp @@ -488,8 +488,16 @@ public: //! Allocates a new GpuMat of given size and type. GpuMat getBuffer(int rows, int cols, int type); +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif //! Allocates a new GpuMat of given size and type. GpuMat getBuffer(Size size, int type) { return getBuffer(size.height, size.width, type); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif //! Returns the allocator associated with the stream. Ptr getAllocator() const { return allocator_; } diff --git a/modules/core/include/opencv2/core/cuda.inl.hpp b/modules/core/include/opencv2/core/cuda.inl.hpp index 35ae2e49d7..107b5b5603 100644 --- a/modules/core/include/opencv2/core/cuda.inl.hpp +++ b/modules/core/include/opencv2/core/cuda.inl.hpp @@ -75,6 +75,11 @@ GpuMat::GpuMat(Size size_, int type_, Allocator* allocator_) create(size_.height, size_.width, type_); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline GpuMat::GpuMat(int rows_, int cols_, int type_, Scalar s_, Allocator* allocator_) : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) @@ -96,6 +101,9 @@ GpuMat::GpuMat(Size size_, int type_, Scalar s_, Allocator* allocator_) setTo(s_); } } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline GpuMat::GpuMat(const GpuMat& m) @@ -158,11 +166,19 @@ GpuMat GpuMat::clone() const return m; } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline void GpuMat::copyTo(OutputArray dst, InputArray mask) const { copyTo(dst, mask, Stream::Null()); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline GpuMat& GpuMat::setTo(Scalar s) @@ -176,6 +192,11 @@ GpuMat& GpuMat::setTo(Scalar s, InputArray mask) return setTo(s, mask, Stream::Null()); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline void GpuMat::convertTo(OutputArray dst, int rtype) const { @@ -187,6 +208,9 @@ void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, double beta) co { convertTo(dst, rtype, alpha, beta, Stream::Null()); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const @@ -554,6 +578,11 @@ Event::Event(const Ptr& impl) // Initialization & Info //=================================================================================== +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline bool TargetArchs::has(int major, int minor) { @@ -571,6 +600,9 @@ DeviceInfo::DeviceInfo() { device_id_ = getDevice(); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline DeviceInfo::DeviceInfo(int device_id) @@ -579,6 +611,11 @@ DeviceInfo::DeviceInfo(int device_id) device_id_ = device_id; } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline int DeviceInfo::deviceID() const { @@ -607,6 +644,9 @@ bool DeviceInfo::supports(FeatureSet feature_set) const int version = majorVersion() * 10 + minorVersion(); return version >= feature_set; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif }} // namespace cv { namespace cuda { diff --git a/modules/core/include/opencv2/core/matx.hpp b/modules/core/include/opencv2/core/matx.hpp index be1c26bb64..a9a72ecf67 100644 --- a/modules/core/include/opencv2/core/matx.hpp +++ b/modules/core/include/opencv2/core/matx.hpp @@ -681,11 +681,19 @@ Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp for(int i = 16; i < channels; i++) val[i] = _Tp(0); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif template inline Matx<_Tp, m, n>::Matx(const _Tp* values) { for( int i = 0; i < channels; i++ ) val[i] = values[i]; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif #ifdef CV_CXX11 template inline diff --git a/modules/core/include/opencv2/core/opengl.hpp b/modules/core/include/opencv2/core/opengl.hpp index a6288bebe8..c6f9b928a8 100644 --- a/modules/core/include/opencv2/core/opengl.hpp +++ b/modules/core/include/opencv2/core/opengl.hpp @@ -703,10 +703,18 @@ cv::ogl::Texture2D::Format cv::ogl::Texture2D::format() const /////// +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline cv::ogl::Arrays::Arrays() : size_(0) { } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline int cv::ogl::Arrays::size() const diff --git a/modules/core/include/opencv2/core/utils/filesystem.private.hpp b/modules/core/include/opencv2/core/utils/filesystem.private.hpp index 72b2bb9479..c32be15c61 100644 --- a/modules/core/include/opencv2/core/utils/filesystem.private.hpp +++ b/modules/core/include/opencv2/core/utils/filesystem.private.hpp @@ -36,7 +36,7 @@ namespace cv { namespace utils { namespace fs { * Provides interprocess synchronization mechanism. * Platform dependent. * - * Supports multiple readers / single writer access pattern (RW / readers–writer / shared-exclusive lock). + * Supports multiple readers / single writer access pattern (RW / readers-writer / shared-exclusive lock). * * File must exist. * File can't be re-used (for example, I/O operations via std::fstream is not safe) diff --git a/modules/features2d/src/affine_feature.cpp b/modules/features2d/src/affine_feature.cpp index 41518d945d..40e03e92ba 100644 --- a/modules/features2d/src/affine_feature.cpp +++ b/modules/features2d/src/affine_feature.cpp @@ -41,7 +41,7 @@ /* Guoshen Yu, Jean-Michel Morel, ASIFT: An Algorithm for Fully Affine - Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11–38. + Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11-38. https://doi.org/10.5201/ipol.2011.my-asift */ diff --git a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp index 263cb01032..e67dc0738d 100644 --- a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp @@ -472,6 +472,11 @@ class CV_EXPORTS PlaneWarperGpu : public PlaneWarper public: PlaneWarperGpu(float scale = 1.f) : PlaneWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -505,6 +510,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); @@ -526,6 +534,11 @@ class CV_EXPORTS SphericalWarperGpu : public SphericalWarper public: SphericalWarperGpu(float scale) : SphericalWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -542,6 +555,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); @@ -558,6 +574,11 @@ class CV_EXPORTS CylindricalWarperGpu : public CylindricalWarper public: CylindricalWarperGpu(float scale) : CylindricalWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -574,6 +595,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); From ccfc34b13f71e81ef0324e32775ab3c010c7121f Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Tue, 20 Sep 2022 13:35:48 +0300 Subject: [PATCH 105/313] Disabled compiling warnings in case of symbols in cmake for 4.x --- 3rdparty/openexr/CMakeLists.txt | 1 + 3rdparty/openjpeg/openjp2/CMakeLists.txt | 2 + cmake/OpenCVCompilerOptions.cmake | 1 + modules/calib3d/src/calibration_handeye.cpp | 2 +- modules/calib3d/src/usac.hpp | 10 ++--- modules/core/include/opencv2/core/cuda.hpp | 8 ++++ .../core/include/opencv2/core/cuda.inl.hpp | 40 +++++++++++++++++++ modules/core/include/opencv2/core/matx.hpp | 8 ++++ modules/core/include/opencv2/core/opengl.hpp | 8 ++++ .../opencv2/core/utils/filesystem.private.hpp | 2 +- modules/features2d/src/affine_feature.cpp | 2 +- modules/objdetect/test/test_qrcode_encode.cpp | 6 +-- .../opencv2/stitching/detail/warpers.hpp | 24 +++++++++++ 13 files changed, 103 insertions(+), 11 deletions(-) diff --git a/3rdparty/openexr/CMakeLists.txt b/3rdparty/openexr/CMakeLists.txt index 8d10e7d968..aeb5a55f31 100644 --- a/3rdparty/openexr/CMakeLists.txt +++ b/3rdparty/openexr/CMakeLists.txt @@ -120,6 +120,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4334) # vs2005 Win64 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244) # vs2008 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4267) # vs2008 Win64 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4456) # vs2015 +ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4819) # vs2019 Win64 if(MSVC AND CV_ICC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qrestrict") diff --git a/3rdparty/openjpeg/openjp2/CMakeLists.txt b/3rdparty/openjpeg/openjp2/CMakeLists.txt index 321d318642..4bd6c52f93 100644 --- a/3rdparty/openjpeg/openjp2/CMakeLists.txt +++ b/3rdparty/openjpeg/openjp2/CMakeLists.txt @@ -39,6 +39,8 @@ ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-function # v2.4.0: Clang ) +ocv_warnings_disable(CMAKE_C_FLAGS /wd4819) # vs2019 Win64 + add_library(${OPENJPEG_LIBRARY_NAME} STATIC ${OPENJPEG_SRCS}) target_compile_definitions(${OPENJPEG_LIBRARY_NAME} PUBLIC OPJ_STATIC) diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index 4f5c353980..9e10673ae8 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -437,6 +437,7 @@ if(MSVC) ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4275) # non dll-interface class 'std::exception' used as base for dll-interface class 'cv::Exception' ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4512) # Assignment operator could not be generated ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4589) # Constructor of abstract class 'cv::ORB' ignores initializer for virtual base class 'cv::Algorithm' + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4819) # Symbols like delta or epsilon cannot be represented endif() if(CV_ICC AND NOT ENABLE_NOISY_WARNINGS) diff --git a/modules/calib3d/src/calibration_handeye.cpp b/modules/calib3d/src/calibration_handeye.cpp index 077de110f6..25fa5af053 100644 --- a/modules/calib3d/src/calibration_handeye.cpp +++ b/modules/calib3d/src/calibration_handeye.cpp @@ -839,7 +839,7 @@ static void calibrateRobotWorldHandEyeShah(const std::vector>& cRw, //Reference: //A. Li, L. Wang, and D. Wu, "Simultaneous robot-world and hand-eye calibration using dual-quaternions and kronecker product" -//International Journal of Physical Sciences, vol. 5, pp. 1530–1536, 2010. +//International Journal of Physical Sciences, vol. 5, pp. 1530-1536, 2010. //Matlab code: http://math.loyola.edu/~mili/Calibration/ static void calibrateRobotWorldHandEyeLi(const std::vector>& cRw, const std::vector>& ctw, const std::vector>& gRb, const std::vector>& gtb, diff --git a/modules/calib3d/src/usac.hpp b/modules/calib3d/src/usac.hpp index 6dc79fdc55..8daf4f52a9 100644 --- a/modules/calib3d/src/usac.hpp +++ b/modules/calib3d/src/usac.hpp @@ -406,14 +406,14 @@ struct SPRT_history { /* * delta: * The probability of a data point being consistent - * with a ‘bad’ model is modeled as a probability of + * with a 'bad' model is modeled as a probability of * a random event with Bernoulli distribution with parameter - * δ : p(1|Hb) = δ. + * delta : p(1|Hb) = delta. * epsilon: - * The probability p(1|Hg) = ε - * that any randomly chosen data point is consistent with a ‘good’ model - * is approximated by the fraction of inliers ε among the data + * The probability p(1|Hg) = epsilon + * that any randomly chosen data point is consistent with a 'good' model + * is approximated by the fraction of inliers epsilon among the data * points * A is the decision threshold, the only parameter of the Adapted SPRT diff --git a/modules/core/include/opencv2/core/cuda.hpp b/modules/core/include/opencv2/core/cuda.hpp index 1ebea07c0d..ba4aa290a3 100644 --- a/modules/core/include/opencv2/core/cuda.hpp +++ b/modules/core/include/opencv2/core/cuda.hpp @@ -694,8 +694,16 @@ public: //! Allocates a new GpuMat of given size and type. CV_WRAP GpuMat getBuffer(int rows, int cols, int type); +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif //! Allocates a new GpuMat of given size and type. CV_WRAP GpuMat getBuffer(Size size, int type) { return getBuffer(size.height, size.width, type); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif //! Returns the allocator associated with the stream. CV_WRAP Ptr getAllocator() const { return allocator_; } diff --git a/modules/core/include/opencv2/core/cuda.inl.hpp b/modules/core/include/opencv2/core/cuda.inl.hpp index 3f2a0c7240..9390b3a529 100644 --- a/modules/core/include/opencv2/core/cuda.inl.hpp +++ b/modules/core/include/opencv2/core/cuda.inl.hpp @@ -75,6 +75,11 @@ GpuMat::GpuMat(Size size_, int type_, Allocator* allocator_) create(size_.height, size_.width, type_); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline GpuMat::GpuMat(int rows_, int cols_, int type_, Scalar s_, Allocator* allocator_) : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) @@ -96,6 +101,9 @@ GpuMat::GpuMat(Size size_, int type_, Scalar s_, Allocator* allocator_) setTo(s_); } } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline GpuMat::GpuMat(const GpuMat& m) @@ -158,11 +166,19 @@ GpuMat GpuMat::clone() const return m; } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline void GpuMat::copyTo(OutputArray dst, InputArray mask) const { copyTo(dst, mask, Stream::Null()); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline GpuMat& GpuMat::setTo(Scalar s) @@ -176,6 +192,11 @@ GpuMat& GpuMat::setTo(Scalar s, InputArray mask) return setTo(s, mask, Stream::Null()); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline void GpuMat::convertTo(OutputArray dst, int rtype) const { @@ -187,6 +208,9 @@ void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, double beta) co { convertTo(dst, rtype, alpha, beta, Stream::Null()); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const @@ -646,6 +670,11 @@ Event::Event(const Ptr& impl) // Initialization & Info //=================================================================================== +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline bool TargetArchs::has(int major, int minor) { @@ -663,6 +692,9 @@ DeviceInfo::DeviceInfo() { device_id_ = getDevice(); } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline DeviceInfo::DeviceInfo(int device_id) @@ -671,6 +703,11 @@ DeviceInfo::DeviceInfo(int device_id) device_id_ = device_id; } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline int DeviceInfo::deviceID() const { @@ -699,6 +736,9 @@ bool DeviceInfo::supports(FeatureSet feature_set) const int version = majorVersion() * 10 + minorVersion(); return version >= feature_set; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif }} // namespace cv { namespace cuda { diff --git a/modules/core/include/opencv2/core/matx.hpp b/modules/core/include/opencv2/core/matx.hpp index 162ce6e7f8..686ff5d99b 100644 --- a/modules/core/include/opencv2/core/matx.hpp +++ b/modules/core/include/opencv2/core/matx.hpp @@ -675,11 +675,19 @@ Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp for(int i = 16; i < channels; i++) val[i] = _Tp(0); } +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif template inline Matx<_Tp, m, n>::Matx(const _Tp* values) { for( int i = 0; i < channels; i++ ) val[i] = values[i]; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif template inline Matx<_Tp, m, n>::Matx(std::initializer_list<_Tp> list) diff --git a/modules/core/include/opencv2/core/opengl.hpp b/modules/core/include/opencv2/core/opengl.hpp index a311ce2525..fceb85bd06 100644 --- a/modules/core/include/opencv2/core/opengl.hpp +++ b/modules/core/include/opencv2/core/opengl.hpp @@ -703,10 +703,18 @@ cv::ogl::Texture2D::Format cv::ogl::Texture2D::format() const /////// +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif inline cv::ogl::Arrays::Arrays() : size_(0) { } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif inline int cv::ogl::Arrays::size() const diff --git a/modules/core/include/opencv2/core/utils/filesystem.private.hpp b/modules/core/include/opencv2/core/utils/filesystem.private.hpp index 72b2bb9479..c32be15c61 100644 --- a/modules/core/include/opencv2/core/utils/filesystem.private.hpp +++ b/modules/core/include/opencv2/core/utils/filesystem.private.hpp @@ -36,7 +36,7 @@ namespace cv { namespace utils { namespace fs { * Provides interprocess synchronization mechanism. * Platform dependent. * - * Supports multiple readers / single writer access pattern (RW / readers–writer / shared-exclusive lock). + * Supports multiple readers / single writer access pattern (RW / readers-writer / shared-exclusive lock). * * File must exist. * File can't be re-used (for example, I/O operations via std::fstream is not safe) diff --git a/modules/features2d/src/affine_feature.cpp b/modules/features2d/src/affine_feature.cpp index 41518d945d..40e03e92ba 100644 --- a/modules/features2d/src/affine_feature.cpp +++ b/modules/features2d/src/affine_feature.cpp @@ -41,7 +41,7 @@ /* Guoshen Yu, Jean-Michel Morel, ASIFT: An Algorithm for Fully Affine - Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11–38. + Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11-38. https://doi.org/10.5201/ipol.2011.my-asift */ diff --git a/modules/objdetect/test/test_qrcode_encode.cpp b/modules/objdetect/test/test_qrcode_encode.cpp index fe4c9fc954..6a66e7ad48 100644 --- a/modules/objdetect/test/test_qrcode_encode.cpp +++ b/modules/objdetect/test/test_qrcode_encode.cpp @@ -318,9 +318,9 @@ TEST(Objdetect_QRCode_Encode_Kanji, regression) Mat qrcode; const int testing_versions = 3; - std::string input_infos[testing_versions] = {"\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x90\xa2\x8a\x45", // こんにちは世界 - "\x82\xa8\x95\xa0\x82\xaa\x8b\xf3\x82\xa2\x82\xc4\x82\xa2\x82\xdc\x82\xb7", // お腹が空いています - "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81\x41\x8e\x84\x82\xcd\x8f\xad\x82\xb5\x93\xfa\x96\x7b\x8c\xea\x82\xf0\x98\x62\x82\xb5\x82\xdc\x82\xb7" // こんにちは、私は少し日本語を話します + std::string input_infos[testing_versions] = {"\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x90\xa2\x8a\x45", // "Hello World" in Japanese + "\x82\xa8\x95\xa0\x82\xaa\x8b\xf3\x82\xa2\x82\xc4\x82\xa2\x82\xdc\x82\xb7", // "I am hungry" in Japanese + "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81\x41\x8e\x84\x82\xcd\x8f\xad\x82\xb5\x93\xfa\x96\x7b\x8c\xea\x82\xf0\x98\x62\x82\xb5\x82\xdc\x82\xb7" // "Hello, I speak a little Japanese" in Japanese }; for (int i = 0; i < testing_versions; i++) diff --git a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp index ff005e8da2..d0d7869d45 100644 --- a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp @@ -503,6 +503,11 @@ class CV_EXPORTS PlaneWarperGpu : public PlaneWarper public: PlaneWarperGpu(float scale = 1.f) : PlaneWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -536,6 +541,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); @@ -557,6 +565,11 @@ class CV_EXPORTS SphericalWarperGpu : public SphericalWarper public: SphericalWarperGpu(float scale) : SphericalWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -573,6 +586,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); @@ -589,6 +605,11 @@ class CV_EXPORTS CylindricalWarperGpu : public CylindricalWarper public: CylindricalWarperGpu(float scale) : CylindricalWarper(scale) {} +// WARNING: unreachable code using Ninja +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(push) +#pragma warning(disable: 4702) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE { Rect result = buildMaps(src_size, K, R, d_xmap_, d_ymap_); @@ -605,6 +626,9 @@ public: d_dst_.download(dst); return result; } +#if defined _MSC_VER && _MSC_VER >= 1920 +#pragma warning(pop) +#endif Rect buildMaps(Size src_size, InputArray K, InputArray R, cuda::GpuMat & xmap, cuda::GpuMat & ymap); From e0b21dccf13c7c7f82b5352963514443fad54042 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Wed, 21 Sep 2022 11:40:54 +0300 Subject: [PATCH 106/313] Detect RISC-V compiler from PATH environment variable --- platforms/linux/riscv64-gcc.toolchain.cmake | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/platforms/linux/riscv64-gcc.toolchain.cmake b/platforms/linux/riscv64-gcc.toolchain.cmake index 675879f86b..0be3a2a2b8 100644 --- a/platforms/linux/riscv64-gcc.toolchain.cmake +++ b/platforms/linux/riscv64-gcc.toolchain.cmake @@ -1,11 +1,20 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR riscv64) -set(RISCV_GCC_INSTALL_ROOT /opt/RISCV CACHE PATH "Path to GCC for RISC-V cross compiler installation directory") -set(CMAKE_SYSROOT ${RISCV_GCC_INSTALL_ROOT}/sysroot CACHE PATH "RISC-V sysroot") +if(NOT DEFINED CMAKE_C_COMPILER) + find_program(CMAKE_C_COMPILER NAMES riscv64-unknown-linux-gnu-gcc + PATHS /opt/RISCV/bin ENV PATH) +endif() -set(CMAKE_C_COMPILER ${RISCV_GCC_INSTALL_ROOT}/bin/riscv64-unknown-linux-gnu-gcc) -set(CMAKE_CXX_COMPILER ${RISCV_GCC_INSTALL_ROOT}/bin/riscv64-unknown-linux-gnu-g++) +if(NOT DEFINED CMAKE_CXX_COMPILER) + find_program(CMAKE_CXX_COMPILER NAMES riscv64-unknown-linux-gnu-g++ + PATHS /opt/RISCV/bin ENV PATH) +endif() + +get_filename_component(RISCV_GCC_INSTALL_ROOT ${CMAKE_C_COMPILER} DIRECTORY) +get_filename_component(RISCV_GCC_INSTALL_ROOT ${RISCV_GCC_INSTALL_ROOT} DIRECTORY) + +set(CMAKE_SYSROOT ${RISCV_GCC_INSTALL_ROOT}/sysroot CACHE PATH "RISC-V sysroot") # Don't run the linker on compiler check set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) From df24bd295de672241aeef3da883f3e4b8dd5867d Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Fri, 23 Sep 2022 11:28:50 +0000 Subject: [PATCH 107/313] Fix v_signmask for RISC-V Vector. --- modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp | 2 +- modules/core/test/test_intrin_utils.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 463c010d71..d34aa65c4a 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -1618,7 +1618,7 @@ inline int v_signmask(const _Tpvec& a) \ { \ uint8_t ans[4] = {0}; \ vsm(ans, vmslt(a, 0, VTraits<_Tpvec>::vlanes()), VTraits<_Tpvec>::vlanes()); \ - return *(reinterpret_cast(ans)); \ + return *(reinterpret_cast(ans)) & (((__int128_t)1 << VTraits<_Tpvec>::vlanes()) - 1); \ } \ inline int v_scan_forward(const _Tpvec& a) \ { \ diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index c1db4f49a5..2685d6ac55 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -1081,6 +1081,7 @@ template struct TheTest typedef typename VTraits::lane_type uint_type; Data dataA, dataB(0), dataC, dataD(1), dataE(2); + dataA[0] = std::numeric_limits::max(); dataA[1] *= (LaneType)-1; union { From 50e66a2e53b726f1e4132e9710aa5a0226dfe42d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 26 Sep 2022 10:50:01 +0000 Subject: [PATCH 108/313] riscv: use /opt/riscv path in toolchain - align with defaults from https://github.com/riscv-collab/riscv-gnu-toolchain --- platforms/linux/riscv64-clang.toolchain.cmake | 2 +- platforms/linux/riscv64-gcc.toolchain.cmake | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platforms/linux/riscv64-clang.toolchain.cmake b/platforms/linux/riscv64-clang.toolchain.cmake index 2efd67ad93..13bca12741 100644 --- a/platforms/linux/riscv64-clang.toolchain.cmake +++ b/platforms/linux/riscv64-clang.toolchain.cmake @@ -2,7 +2,7 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR riscv64) set(RISCV_CLANG_BUILD_ROOT /opt/rvv-llvm CACHE PATH "Path to CLANG for RISC-V cross compiler build directory") -set(RISCV_GCC_INSTALL_ROOT /opt/RISCV CACHE PATH "Path to GCC for RISC-V cross compiler installation directory") +set(RISCV_GCC_INSTALL_ROOT /opt/riscv CACHE PATH "Path to GCC for RISC-V cross compiler installation directory") set(CMAKE_SYSROOT ${RISCV_GCC_INSTALL_ROOT}/sysroot CACHE PATH "RISC-V sysroot") set(CLANG_TARGET_TRIPLE riscv64-unknown-linux-gnu) diff --git a/platforms/linux/riscv64-gcc.toolchain.cmake b/platforms/linux/riscv64-gcc.toolchain.cmake index 0be3a2a2b8..2da0fb7bd4 100644 --- a/platforms/linux/riscv64-gcc.toolchain.cmake +++ b/platforms/linux/riscv64-gcc.toolchain.cmake @@ -3,12 +3,12 @@ set(CMAKE_SYSTEM_PROCESSOR riscv64) if(NOT DEFINED CMAKE_C_COMPILER) find_program(CMAKE_C_COMPILER NAMES riscv64-unknown-linux-gnu-gcc - PATHS /opt/RISCV/bin ENV PATH) + PATHS /opt/riscv/bin ENV PATH) endif() if(NOT DEFINED CMAKE_CXX_COMPILER) find_program(CMAKE_CXX_COMPILER NAMES riscv64-unknown-linux-gnu-g++ - PATHS /opt/RISCV/bin ENV PATH) + PATHS /opt/riscv/bin ENV PATH) endif() get_filename_component(RISCV_GCC_INSTALL_ROOT ${CMAKE_C_COMPILER} DIRECTORY) From 3d9f27b87711e957994eb615e299cf353224d1a7 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 26 Sep 2022 13:50:23 +0300 Subject: [PATCH 109/313] Report that animated webp is not supported for now. --- modules/imgcodecs/src/grfmt_webp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index e137b8734d..99eb09e105 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -126,6 +126,8 @@ bool WebPDecoder::readHeader() WebPBitstreamFeatures features; if (VP8_STATUS_OK == WebPGetFeatures(header, sizeof(header), &features)) { + CV_CheckEQ(features.has_animation, false, "WebP backend does not support animated webp images"); + m_width = features.width; m_height = features.height; From 4521d66103cc016ade98777c63c2e37ef42d91be Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 26 Sep 2022 08:50:23 +0000 Subject: [PATCH 110/313] Remove r-value ref --- modules/gapi/samples/pipeline_modeling_tool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index bacd1742ea..60547a9c9b 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -193,7 +193,7 @@ CallParams read(const cv::FileNode& fn) { template std::map readMap(const cv::FileNode& fn) { std::map map; - for (auto&& item : fn) { + for (auto item : fn) { map.emplace(item.name(), read(item)); } return map; From c34c4b50d070b9ac9151669963d4f5bc336af479 Mon Sep 17 00:00:00 2001 From: catree Date: Mon, 26 Sep 2022 18:40:18 +0200 Subject: [PATCH 111/313] Add information about the disparity-to-depth mapping matrix. Add more references about other related functions in the calib3d doc. --- modules/calib3d/include/opencv2/calib3d.hpp | 97 ++++++++++++--------- modules/imgproc/include/opencv2/imgproc.hpp | 2 +- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 2de2d34b0d..e13d488018 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -577,7 +577,7 @@ a vector\ . - @ref RHO - PROSAC-based robust method @param ransacReprojThreshold Maximum allowed reprojection error to treat a point pair as an inlier (used in the RANSAC and RHO methods only). That is, if -\f[\| \texttt{dstPoints} _i - \texttt{convertPointsHomogeneous} ( \texttt{H} * \texttt{srcPoints} _i) \|_2 > \texttt{ransacReprojThreshold}\f] +\f[\| \texttt{dstPoints} _i - \texttt{convertPointsHomogeneous} ( \texttt{H} \cdot \texttt{srcPoints} _i) \|_2 > \texttt{ransacReprojThreshold}\f] then the point \f$i\f$ is considered as an outlier. If srcPoints and dstPoints are measured in pixels, it usually makes sense to set this parameter somewhere in the range of 1 to 10. @param mask Optional output mask set by a robust method ( RANSAC or LMeDS ). Note that the input @@ -642,7 +642,7 @@ CV_EXPORTS Mat findHomography( InputArray srcPoints, InputArray dstPoints, @param Qz Optional output 3x3 rotation matrix around z-axis. The function computes a RQ decomposition using the given rotations. This function is used in -decomposeProjectionMatrix to decompose the left 3x3 submatrix of a projection matrix into a camera +@ref decomposeProjectionMatrix to decompose the left 3x3 submatrix of a projection matrix into a camera and a rotation matrix. It optionally returns three rotation matrices, one for each axis, and the three Euler angles in @@ -676,7 +676,7 @@ be used in OpenGL. Note, there is always more than one sequence of rotations abo principal axes that results in the same orientation of an object, e.g. see @cite Slabaugh . Returned tree rotation matrices and corresponding three Euler angles are only one of the possible solutions. -The function is based on RQDecomp3x3 . +The function is based on @ref RQDecomp3x3 . */ CV_EXPORTS_W void decomposeProjectionMatrix( InputArray projMatrix, OutputArray cameraMatrix, OutputArray rotMatrix, OutputArray transVect, @@ -696,7 +696,7 @@ CV_EXPORTS_W void decomposeProjectionMatrix( InputArray projMatrix, OutputArray The function computes partial derivatives of the elements of the matrix product \f$A*B\f$ with regard to the elements of each of the two input matrices. The function is used to compute the Jacobian -matrices in stereoCalibrate but can also be used in any other similar optimization function. +matrices in @ref stereoCalibrate but can also be used in any other similar optimization function. */ CV_EXPORTS_W void matMulDeriv( InputArray A, InputArray B, OutputArray dABdA, OutputArray dABdB ); @@ -722,10 +722,10 @@ The functions compute: \f[\begin{array}{l} \texttt{rvec3} = \mathrm{rodrigues} ^{-1} \left ( \mathrm{rodrigues} ( \texttt{rvec2} ) \cdot \mathrm{rodrigues} ( \texttt{rvec1} ) \right ) \\ \texttt{tvec3} = \mathrm{rodrigues} ( \texttt{rvec2} ) \cdot \texttt{tvec1} + \texttt{tvec2} \end{array} ,\f] where \f$\mathrm{rodrigues}\f$ denotes a rotation vector to a rotation matrix transformation, and -\f$\mathrm{rodrigues}^{-1}\f$ denotes the inverse transformation. See Rodrigues for details. +\f$\mathrm{rodrigues}^{-1}\f$ denotes the inverse transformation. See @ref Rodrigues for details. Also, the functions can compute the derivatives of the output vectors with regards to the input -vectors (see matMulDeriv ). The functions are used inside stereoCalibrate but can also be used in +vectors (see @ref matMulDeriv ). The functions are used inside @ref stereoCalibrate but can also be used in your own code where Levenberg-Marquardt or another gradient-based solver is used to optimize a function that contains a matrix multiplication. */ @@ -1084,7 +1084,7 @@ calibrateCamera for details. old interface all the per-view vectors are concatenated. @param imageSize Image size in pixels used to initialize the principal point. @param aspectRatio If it is zero or negative, both \f$f_x\f$ and \f$f_y\f$ are estimated independently. -Otherwise, \f$f_x = f_y * \texttt{aspectRatio}\f$ . +Otherwise, \f$f_x = f_y \cdot \texttt{aspectRatio}\f$ . The function estimates and returns an initial camera intrinsic matrix for the camera calibration process. Currently, the function only supports planar calibration patterns, which are patterns where each @@ -1098,12 +1098,12 @@ CV_EXPORTS_W Mat initCameraMatrix2D( InputArrayOfArrays objectPoints, @param image Source chessboard view. It must be an 8-bit grayscale or color image. @param patternSize Number of inner corners per a chessboard row and column -( patternSize = cvSize(points_per_row,points_per_colum) = cvSize(columns,rows) ). +( patternSize = cv::Size(points_per_row,points_per_colum) = cv::Size(columns,rows) ). @param corners Output array of detected corners. @param flags Various operation flags that can be zero or a combination of the following values: - @ref CALIB_CB_ADAPTIVE_THRESH Use adaptive thresholding to convert the image to black and white, rather than a fixed threshold level (computed from the average image brightness). -- @ref CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with equalizeHist before +- @ref CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with @ref equalizeHist before applying fixed or adaptive thresholding. - @ref CALIB_CB_FILTER_QUADS Use additional criteria (like contour area, perimeter, square-like shape) to filter out false quads extracted at the contour retrieval stage. @@ -1117,7 +1117,7 @@ are found and they are placed in a certain order (row by row, left to right in e Otherwise, if the function fails to find all the corners or reorder them, it returns 0. For example, a regular chessboard has 8 x 8 squares and 7 x 7 internal corners, that is, points where the black squares touch each other. The detected coordinates are approximate, and to determine their positions -more accurately, the function calls cornerSubPix. You also may use the function cornerSubPix with +more accurately, the function calls @ref cornerSubPix. You also may use the function @ref cornerSubPix with different parameters if returned coordinates are not accurate enough. Sample usage of detecting and drawing chessboard corners: : @@ -1154,9 +1154,9 @@ CV_EXPORTS_W bool find4QuadCornerSubpix( InputArray img, InputOutputArray corner @param image Destination image. It must be an 8-bit color image. @param patternSize Number of inner corners per a chessboard row and column (patternSize = cv::Size(points_per_row,points_per_column)). -@param corners Array of detected corners, the output of findChessboardCorners. +@param corners Array of detected corners, the output of @ref findChessboardCorners. @param patternWasFound Parameter indicating whether the complete board was found or not. The -return value of findChessboardCorners should be passed here. +return value of @ref findChessboardCorners should be passed here. The function draws individual chessboard corners detected either as red circles if the board was not found, or as colored corners connected with lines if the board was found. @@ -1542,7 +1542,7 @@ Besides the stereo-related information, the function can also perform a full cal the two cameras. However, due to the high dimensionality of the parameter space and noise in the input data, the function can diverge from the correct solution. If the intrinsic parameters can be estimated with high accuracy for each of the cameras individually (for example, using -calibrateCamera ), you are recommended to do so and then pass @ref CALIB_FIX_INTRINSIC flag to the +@ref calibrateCamera ), you are recommended to do so and then pass @ref CALIB_FIX_INTRINSIC flag to the function along with the computed intrinsic parameters. Otherwise, if all the parameters are estimated at once, it makes sense to restrict some parameters, for example, pass @ref CALIB_SAME_FOCAL_LENGTH and @ref CALIB_ZERO_TANGENT_DIST flags, which is usually a @@ -1608,7 +1608,7 @@ pixels from the original images from the cameras are retained in the rectified i image pixels are lost). Any intermediate value yields an intermediate result between those two extreme cases. @param newImageSize New image resolution after rectification. The same size should be passed to -initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) +@ref initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) is passed (default), it is set to the original imageSize . Setting it to a larger value can help you preserve details in the original image, especially when there is a big radial distortion. @param validPixROI1 Optional output rectangles inside the rectified images where all the pixels @@ -1620,7 +1620,7 @@ are valid. If alpha=0 , the ROIs cover the whole images. Otherwise, they are lik The function computes the rotation matrices for each camera that (virtually) make both camera image planes the same plane. Consequently, this makes all the epipolar lines parallel and thus simplifies -the dense stereo correspondence problem. The function takes the matrices computed by stereoCalibrate +the dense stereo correspondence problem. The function takes the matrices computed by @ref stereoCalibrate as input. As output, it provides two rotation matrices and also two projection matrices in the new coordinates. The function distinguishes the following two cases: @@ -1636,11 +1636,18 @@ coordinates. The function distinguishes the following two cases: \end{bmatrix}\f] \f[\texttt{P2} = \begin{bmatrix} - f & 0 & cx_2 & T_x*f \\ + f & 0 & cx_2 & T_x \cdot f \\ 0 & f & cy & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} ,\f] + \f[\texttt{Q} = \begin{bmatrix} + 1 & 0 & 0 & -cx_1 \\ + 0 & 1 & 0 & -cy \\ + 0 & 0 & 0 & f \\ + 0 & 0 & -\frac{1}{T_x} & \frac{cx_1 - cx_2}{T_x} + \end{bmatrix} \f] + where \f$T_x\f$ is a horizontal shift between the cameras and \f$cx_1=cx_2\f$ if @ref CALIB_ZERO_DISPARITY is set. @@ -1656,15 +1663,22 @@ coordinates. The function distinguishes the following two cases: \f[\texttt{P2} = \begin{bmatrix} f & 0 & cx & 0 \\ - 0 & f & cy_2 & T_y*f \\ + 0 & f & cy_2 & T_y \cdot f \\ 0 & 0 & 1 & 0 \end{bmatrix},\f] + \f[\texttt{Q} = \begin{bmatrix} + 1 & 0 & 0 & -cx \\ + 0 & 1 & 0 & -cy_1 \\ + 0 & 0 & 0 & f \\ + 0 & 0 & -\frac{1}{T_y} & \frac{cy_1 - cy_2}{T_y} + \end{bmatrix} \f] + where \f$T_y\f$ is a vertical shift between the cameras and \f$cy_1=cy_2\f$ if @ref CALIB_ZERO_DISPARITY is set. As you can see, the first three columns of P1 and P2 will effectively be the new "rectified" camera -matrices. The matrices, together with R1 and R2 , can then be passed to initUndistortRectifyMap to +matrices. The matrices, together with R1 and R2 , can then be passed to @ref initUndistortRectifyMap to initialize the rectification map for each camera. See below the screenshot from the stereo_calib.cpp sample. Some red horizontal lines pass through @@ -1687,20 +1701,20 @@ CV_EXPORTS_W void stereoRectify( InputArray cameraMatrix1, InputArray distCoeffs @param points1 Array of feature points in the first image. @param points2 The corresponding points in the second image. The same formats as in -findFundamentalMat are supported. +@ref findFundamentalMat are supported. @param F Input fundamental matrix. It can be computed from the same set of point pairs using -findFundamentalMat . +@ref findFundamentalMat . @param imgSize Size of the image. @param H1 Output rectification homography matrix for the first image. @param H2 Output rectification homography matrix for the second image. @param threshold Optional threshold used to filter out the outliers. If the parameter is greater than zero, all the point pairs that do not comply with the epipolar geometry (that is, the points -for which \f$|\texttt{points2[i]}^T*\texttt{F}*\texttt{points1[i]}|>\texttt{threshold}\f$ ) are -rejected prior to computing the homographies. Otherwise, all the points are considered inliers. +for which \f$|\texttt{points2[i]}^T \cdot \texttt{F} \cdot \texttt{points1[i]}|>\texttt{threshold}\f$ ) +are rejected prior to computing the homographies. Otherwise, all the points are considered inliers. The function computes the rectification transformations without knowing intrinsic parameters of the cameras and their relative position in the space, which explains the suffix "uncalibrated". Another -related difference from stereoRectify is that the function outputs not the rectification +related difference from @ref stereoRectify is that the function outputs not the rectification transformations in the object (3D) space, but the planar perspective transformations encoded by the homography matrices H1 and H2 . The function implements the algorithm @cite Hartley99 . @@ -1709,8 +1723,8 @@ homography matrices H1 and H2 . The function implements the algorithm @cite Hart depends on the epipolar geometry. Therefore, if the camera lenses have a significant distortion, it would be better to correct it before computing the fundamental matrix and calling this function. For example, distortion coefficients can be estimated for each head of stereo camera - separately by using calibrateCamera . Then, the images can be corrected using undistort , or - just the point coordinates can be corrected with undistortPoints . + separately by using @ref calibrateCamera . Then, the images can be corrected using @ref undistort , or + just the point coordinates can be corrected with @ref undistortPoints . */ CV_EXPORTS_W bool stereoRectifyUncalibrated( InputArray points1, InputArray points2, InputArray F, Size imgSize, @@ -1738,10 +1752,10 @@ assumed. @param imageSize Original image size. @param alpha Free scaling parameter between 0 (when all the pixels in the undistorted image are valid) and 1 (when all the source image pixels are retained in the undistorted image). See -stereoRectify for details. +@ref stereoRectify for details. @param newImgSize Image size after rectification. By default, it is set to imageSize . @param validPixROI Optional output rectangle that outlines all-good-pixels region in the -undistorted image. See roi1, roi2 description in stereoRectify . +undistorted image. See roi1, roi2 description in @ref stereoRectify . @param centerPrincipalPoint Optional flag that indicates whether in the new camera intrinsic matrix the principal point should be at the image center or not. By default, the principal point is chosen to best fit a subset of the source image (determined by alpha) to the corrected image. @@ -1753,7 +1767,7 @@ image pixels if there is valuable information in the corners alpha=1 , or get so When alpha\>0 , the undistorted result is likely to have some black pixels corresponding to "virtual" pixels outside of the captured distorted image. The original camera intrinsic matrix, distortion coefficients, the computed new camera intrinsic matrix, and newImageSize should be passed to -initUndistortRectifyMap to produce the maps for remap . +@ref initUndistortRectifyMap to produce the maps for @ref remap . */ CV_EXPORTS_W Mat getOptimalNewCameraMatrix( InputArray cameraMatrix, InputArray distCoeffs, Size imageSize, double alpha, Size newImgSize = Size(), @@ -1920,7 +1934,7 @@ CV_EXPORTS_W void convertPointsFromHomogeneous( InputArray src, OutputArray dst @param dst Output vector of 2D, 3D, or 4D points. The function converts 2D or 3D points from/to homogeneous coordinates by calling either -convertPointsToHomogeneous or convertPointsFromHomogeneous. +@ref convertPointsToHomogeneous or @ref convertPointsFromHomogeneous. @note The function is obsolete. Use one of the previous two functions instead. */ @@ -1957,9 +1971,9 @@ the found fundamental matrix. Normally just one matrix is found. But in case of algorithm, the function may return up to 3 solutions ( \f$9 \times 3\f$ matrix that stores all 3 matrices sequentially). -The calculated fundamental matrix may be passed further to computeCorrespondEpilines that finds the +The calculated fundamental matrix may be passed further to @ref computeCorrespondEpilines that finds the epipolar lines corresponding to the specified points. It can also be passed to -stereoRectifyUncalibrated to compute the rectification transformation. : +@ref stereoRectifyUncalibrated to compute the rectification transformation. : @code // Example. Estimation of fundamental matrix using the RANSAC algorithm int point_count = 100; @@ -2023,7 +2037,7 @@ This function estimates essential matrix based on the five-point algorithm solve where \f$E\f$ is an essential matrix, \f$p_1\f$ and \f$p_2\f$ are corresponding points in the first and the second images, respectively. The result of this function may be passed further to -decomposeEssentialMat or recoverPose to recover the relative pose between cameras. +@ref decomposeEssentialMat or @ref recoverPose to recover the relative pose between cameras. */ CV_EXPORTS_W Mat findEssentialMat( InputArray points1, InputArray points2, InputArray cameraMatrix, int method, @@ -2220,14 +2234,14 @@ CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray point @param points Input points. \f$N \times 1\f$ or \f$1 \times N\f$ matrix of type CV_32FC2 or vector\ . @param whichImage Index of the image (1 or 2) that contains the points . -@param F Fundamental matrix that can be estimated using findFundamentalMat or stereoRectify . +@param F Fundamental matrix that can be estimated using @ref findFundamentalMat or @ref stereoRectify . @param lines Output vector of the epipolar lines corresponding to the points in the other image. Each line \f$ax + by + c=0\f$ is encoded by 3 numbers \f$(a, b, c)\f$ . For every point in one of the two images of a stereo pair, the function finds the equation of the corresponding epipolar line in the other image. -From the fundamental matrix definition (see findFundamentalMat ), line \f$l^{(2)}_i\f$ in the second +From the fundamental matrix definition (see @ref findFundamentalMat ), line \f$l^{(2)}_i\f$ in the second image for the point \f$p^{(1)}_i\f$ in the first image (when whichImage=1 ) is computed as: \f[l^{(2)}_i = F p^{(1)}_i\f] @@ -2277,12 +2291,12 @@ CV_EXPORTS_W void triangulatePoints( InputArray projMatr1, InputArray projMatr2, @param newPoints1 The optimized points1. @param newPoints2 The optimized points2. -The function implements the Optimal Triangulation Method (see Multiple View Geometry for details). +The function implements the Optimal Triangulation Method (see Multiple View Geometry @cite HartleyZ00 for details). For each given point correspondence points1[i] \<-\> points2[i], and a fundamental matrix F, it computes the corrected correspondences newPoints1[i] \<-\> newPoints2[i] that minimize the geometric error \f$d(points1[i], newPoints1[i])^2 + d(points2[i],newPoints2[i])^2\f$ (where \f$d(a,b)\f$ is the geometric distance between points \f$a\f$ and \f$b\f$ ) subject to the epipolar constraint -\f$newPoints2^T * F * newPoints1 = 0\f$ . +\f$newPoints2^T \cdot F \cdot newPoints1 = 0\f$ . */ CV_EXPORTS_W void correctMatches( InputArray F, InputArray points1, InputArray points2, OutputArray newPoints1, OutputArray newPoints2 ); @@ -2584,10 +2598,11 @@ CV_EXPORTS_W int decomposeHomographyMat(InputArray H, @param beforePoints Vector of (rectified) visible reference points before the homography is applied @param afterPoints Vector of (rectified) visible reference points after the homography is applied @param possibleSolutions Vector of int indices representing the viable solution set after filtering -@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the findHomography function +@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the +@ref findHomography function -This function is intended to filter the output of the decomposeHomographyMat based on additional -information as described in @cite Malis2007 . The summary of the method: the decomposeHomographyMat function +This function is intended to filter the output of the @ref decomposeHomographyMat based on additional +information as described in @cite Malis2007 . The summary of the method: the @ref decomposeHomographyMat function returns 2 unique solutions and their "opposites" for a total of 4 solutions. If we have access to the sets of points visible in the camera frame before and after the homography transformation is applied, we can determine which are the true potential solutions and which are the opposites by verifying which @@ -2977,14 +2992,14 @@ optimization. It stays at the center or at a different location specified when @ camera. @param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second camera. - @param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see reprojectImageTo3D ). + @param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see @ref reprojectImageTo3D ). @param flags Operation flags that may be zero or @ref fisheye::CALIB_ZERO_DISPARITY . If the flag is set, the function makes the principal points of each camera have the same pixel coordinates in the rectified views. And if the flag is not set, the function may still shift the images in the horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the useful image area. @param newImageSize New image resolution after rectification. The same size should be passed to - initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) + @ref initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) is passed (default), it is set to the original imageSize . Setting it to larger value can help you preserve details in the original image, especially when there is a big radial distortion. @param balance Sets the new focal length in range between the min focal length and the max focal diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 44a2e65d24..481c0bf9f7 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3126,7 +3126,7 @@ where cameraMatrix can be chosen arbitrarily. of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. @param R Optional rectification transformation in the object space (3x3 matrix). R1 or R2 , computed by #stereoRectify can be passed here. If the matrix is empty, the identity transformation -is assumed. In cvInitUndistortMap R assumed to be an identity matrix. +is assumed. In #initUndistortRectifyMap R is assumed to be an identity matrix. @param newCameraMatrix New camera matrix \f$A'=\vecthreethree{f_x'}{0}{c_x'}{0}{f_y'}{c_y'}{0}{0}{1}\f$. @param size Undistorted image size. @param m1type Type of the first output map that can be CV_32FC1, CV_32FC2 or CV_16SC2, see #convertMaps From 64649a1207d30b8136f5fd10ebac0697f95c1c9c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 26 Sep 2022 12:02:55 +0300 Subject: [PATCH 112/313] Fix multiple sdtlib linkage warning on Windows with MSVS. Use global OpenCV settings for MS Visual Studio run-time libraries to prevent colision. --- 3rdparty/libjpeg-turbo/CMakeLists.txt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt index d55dcdf962..ea4f8fca5e 100644 --- a/3rdparty/libjpeg-turbo/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/CMakeLists.txt @@ -146,22 +146,6 @@ if(WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jdarith.c) endif() -if(MSVC) - option(WITH_CRT_DLL - "Link all ${CMAKE_PROJECT_NAME} libraries and executables with the C run-time DLL (msvcr*.dll) instead of the static C run-time library (libcmt*.lib.) The default is to use the C run-time DLL only with the libraries and executables that need it." - FALSE) - if(NOT WITH_CRT_DLL) - # Use the static C library for all build types - foreach(var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) - if(${var} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}") - endif() - endforeach() - endif() - add_definitions(-D_CRT_NONSTDC_NO_WARNINGS) -endif() - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang") # Use the maximum optimization level for release builds foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO) From 1829eba584c3db84fcfc7a779565ad808bebc1c5 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 27 Sep 2022 18:06:46 -0400 Subject: [PATCH 113/313] Fixed most clang -Wextra-semi warnings --- modules/calib3d/src/chessboard.hpp | 18 +++++++++--------- modules/calib3d/src/usac/estimator.cpp | 4 ++-- .../opencv2/core/utils/trace.private.hpp | 2 +- .../src/parallel/registry_parallel.impl.hpp | 2 +- modules/features2d/src/fast.avx2.cpp | 2 +- modules/features2d/src/fast.hpp | 2 +- .../imgcodecs/include/opencv2/imgcodecs.hpp | 4 ++-- modules/imgproc/src/filter.dispatch.cpp | 2 +- modules/imgproc/src/imgwarp.hpp | 2 +- modules/imgproc/src/imgwarp.sse4_1.cpp | 2 +- modules/imgproc/src/resize.cpp | 2 +- modules/ml/src/inner_functions.cpp | 4 ++-- modules/python/src2/cv2_numpy.hpp | 8 ++++---- modules/python/src2/cv2_util.hpp | 2 +- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/modules/calib3d/src/chessboard.hpp b/modules/calib3d/src/chessboard.hpp index 4c862eca4c..f49b83572f 100644 --- a/modules/calib3d/src/chessboard.hpp +++ b/modules/calib3d/src/chessboard.hpp @@ -43,7 +43,7 @@ class FastX : public cv::Feature2D public: FastX(const Parameters &config = Parameters()); - virtual ~FastX(){}; + virtual ~FastX(){} void reconfigure(const Parameters ¶); @@ -74,8 +74,8 @@ class FastX : public cv::Feature2D std::vector > calcAngles(const std::vector &rotated_images, std::vector &keypoints)const; // define pure virtual methods - virtual int descriptorSize()const override{return 0;}; - virtual int descriptorType()const override{return 0;}; + virtual int descriptorSize()const override{return 0;} + virtual int descriptorType()const override{return 0;} virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const { descriptors.clear(); @@ -620,10 +620,10 @@ class Chessboard: public cv::Feature2D */ void swap(Chessboard::Board &other); - bool operator==(const Chessboard::Board& other) const {return rows*cols == other.rows*other.cols;}; - bool operator< (const Chessboard::Board& other) const {return rows*cols < other.rows*other.cols;}; - bool operator> (const Chessboard::Board& other) const {return rows*cols > other.rows*other.cols;}; - bool operator>= (const cv::Size& size)const { return rows*cols >= size.width*size.height; }; + bool operator==(const Chessboard::Board& other) const {return rows*cols == other.rows*other.cols;} + bool operator< (const Chessboard::Board& other) const {return rows*cols < other.rows*other.cols;} + bool operator> (const Chessboard::Board& other) const {return rows*cols > other.rows*other.cols;} + bool operator>= (const cv::Size& size)const { return rows*cols >= size.width*size.height; } /** * \brief Returns a specific corner @@ -824,8 +824,8 @@ class Chessboard: public cv::Feature2D Chessboard::Board detectImpl(const cv::Mat& image,std::vector &feature_maps,const cv::Mat& mask)const; // define pure virtual methods - virtual int descriptorSize()const override{return 0;}; - virtual int descriptorType()const override{return 0;}; + virtual int descriptorSize()const override{return 0;} + virtual int descriptorType()const override{return 0;} virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const { descriptors.clear(); diff --git a/modules/calib3d/src/usac/estimator.cpp b/modules/calib3d/src/usac/estimator.cpp index 75bc3cf5dd..c5b783b655 100644 --- a/modules/calib3d/src/usac/estimator.cpp +++ b/modules/calib3d/src/usac/estimator.cpp @@ -23,7 +23,7 @@ public: int estimateModelNonMinimalSample(const std::vector &sample, int sample_size, std::vector &models, const std::vector &weights) const override { return non_min_solver->estimate (sample, sample_size, models, weights); - }; + } int getMaxNumSolutions () const override { return min_solver->getMaxNumberOfSolutions(); } @@ -118,7 +118,7 @@ public: int estimateModelNonMinimalSample(const std::vector &sample, int sample_size, std::vector &models, const std::vector &weights) const override { return non_min_solver->estimate(sample, sample_size, models, weights); - }; + } int getMaxNumSolutions () const override { return min_solver->getMaxNumberOfSolutions(); } diff --git a/modules/core/include/opencv2/core/utils/trace.private.hpp b/modules/core/include/opencv2/core/utils/trace.private.hpp index afc41159f6..5a826b417b 100644 --- a/modules/core/include/opencv2/core/utils/trace.private.hpp +++ b/modules/core/include/opencv2/core/utils/trace.private.hpp @@ -63,7 +63,7 @@ class TraceMessage; class TraceStorage { public: TraceStorage() {} - virtual ~TraceStorage() {}; + virtual ~TraceStorage() {} virtual bool put(const TraceMessage& msg) const = 0; }; diff --git a/modules/core/src/parallel/registry_parallel.impl.hpp b/modules/core/src/parallel/registry_parallel.impl.hpp index c8b57e7d6c..2208748bb1 100644 --- a/modules/core/src/parallel/registry_parallel.impl.hpp +++ b/modules/core/src/parallel/registry_parallel.impl.hpp @@ -43,7 +43,7 @@ std::vector& getBuiltinParallelBackendsInfo() #endif }; return g_backends; -}; +} static bool sortByPriority(const ParallelBackendInfo &lhs, const ParallelBackendInfo &rhs) diff --git a/modules/features2d/src/fast.avx2.cpp b/modules/features2d/src/fast.avx2.cpp index ca0bcb9269..72e7d66924 100644 --- a/modules/features2d/src/fast.avx2.cpp +++ b/modules/features2d/src/fast.avx2.cpp @@ -165,7 +165,7 @@ public: _mm256_zeroupper(); } - virtual ~FAST_t_patternSize16_AVX2_Impl() CV_OVERRIDE {}; + virtual ~FAST_t_patternSize16_AVX2_Impl() CV_OVERRIDE {} private: int cols; diff --git a/modules/features2d/src/fast.hpp b/modules/features2d/src/fast.hpp index 6f750fea8d..1e221cbdf4 100644 --- a/modules/features2d/src/fast.hpp +++ b/modules/features2d/src/fast.hpp @@ -54,7 +54,7 @@ class FAST_t_patternSize16_AVX2 public: static Ptr getImpl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel); virtual void process(int &j, const uchar* &ptr, uchar* curr, int* cornerpos, int &ncorners) = 0; - virtual ~FAST_t_patternSize16_AVX2() {}; + virtual ~FAST_t_patternSize16_AVX2() {} }; #endif } diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index cb4a170c28..18b04d5687 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -354,8 +354,8 @@ public: Mat* operator->(); iterator& operator++(); iterator operator++(int); - friend bool operator== (const iterator& a, const iterator& b) { return a.m_curr == b.m_curr; }; - friend bool operator!= (const iterator& a, const iterator& b) { return a.m_curr != b.m_curr; }; + friend bool operator== (const iterator& a, const iterator& b) { return a.m_curr == b.m_curr; } + friend bool operator!= (const iterator& a, const iterator& b) { return a.m_curr != b.m_curr; } private: ImageCollection* m_pCollection; diff --git a/modules/imgproc/src/filter.dispatch.cpp b/modules/imgproc/src/filter.dispatch.cpp index 097dd32055..53b8bb42c4 100644 --- a/modules/imgproc/src/filter.dispatch.cpp +++ b/modules/imgproc/src/filter.dispatch.cpp @@ -1401,7 +1401,7 @@ static void ocvSepFilter(int stype, int dtype, int ktype, Mat src(Size(width, height), stype, src_data, src_step); Mat dst(Size(width, height), dtype, dst_data, dst_step); f->apply(src, dst, Size(full_width, full_height), Point(offset_x, offset_y)); -}; +} //=================================================================== // HAL functions diff --git a/modules/imgproc/src/imgwarp.hpp b/modules/imgproc/src/imgwarp.hpp index 4b81b5e79d..35e9f7bb02 100644 --- a/modules/imgproc/src/imgwarp.hpp +++ b/modules/imgproc/src/imgwarp.hpp @@ -82,7 +82,7 @@ public: static Ptr getImpl(const double *M); virtual void processNN(const double *M, short* xy, double X0, double Y0, double W0, int bw) = 0; virtual void process(const double *M, short* xy, short* alpha, double X0, double Y0, double W0, int bw) = 0; - virtual ~WarpPerspectiveLine_SSE4() {}; + virtual ~WarpPerspectiveLine_SSE4() {} }; #endif } diff --git a/modules/imgproc/src/imgwarp.sse4_1.cpp b/modules/imgproc/src/imgwarp.sse4_1.cpp index 5625b45f7b..a2ec9396da 100644 --- a/modules/imgproc/src/imgwarp.sse4_1.cpp +++ b/modules/imgproc/src/imgwarp.sse4_1.cpp @@ -492,7 +492,7 @@ public: (X & (INTER_TAB_SIZE - 1))); } } - virtual ~WarpPerspectiveLine_SSE4_Impl() CV_OVERRIDE {}; + virtual ~WarpPerspectiveLine_SSE4_Impl() CV_OVERRIDE {} }; Ptr WarpPerspectiveLine_SSE4::getImpl(const double *M) diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 4ad64534be..f388587f9c 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -339,7 +339,7 @@ template static void hlineResizeCn(ET* src, int cn, int *ofst, FT* m, FT* dst, int dst_min, int dst_max, int dst_width) { hline::ResizeCn(src, cn, ofst, m, dst, dst_min, dst_max, dst_width); -}; +} template <> void hlineResizeCn(uint8_t* src, int, int *ofst, ufixedpoint16* m, ufixedpoint16* dst, int dst_min, int dst_max, int dst_width) diff --git a/modules/ml/src/inner_functions.cpp b/modules/ml/src/inner_functions.cpp index b823c5ba22..6b3affcebc 100644 --- a/modules/ml/src/inner_functions.cpp +++ b/modules/ml/src/inner_functions.cpp @@ -126,10 +126,10 @@ public: errStrip[idxErr]=err ; - }; + } ParallelCalcError& operator=(const ParallelCalcError &) { return *this; - }; + } }; diff --git a/modules/python/src2/cv2_numpy.hpp b/modules/python/src2/cv2_numpy.hpp index 7c386c7dbc..934333921d 100644 --- a/modules/python/src2/cv2_numpy.hpp +++ b/modules/python/src2/cv2_numpy.hpp @@ -51,13 +51,13 @@ NPY_TYPES asNumpyType() return NPY_U##dst; \ } -CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int8_t, INT8); +CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int8_t, INT8) -CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int16_t, INT16); +CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int16_t, INT16) -CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int32_t, INT32); +CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int32_t, INT32) -CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int64_t, INT64); +CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION(int64_t, INT64) #undef CV_GENERATE_INTEGRAL_TYPE_NPY_CONVERSION diff --git a/modules/python/src2/cv2_util.hpp b/modules/python/src2/cv2_util.hpp index 3662ffcd4d..0d27e98825 100644 --- a/modules/python/src2/cv2_util.hpp +++ b/modules/python/src2/cv2_util.hpp @@ -12,7 +12,7 @@ bool isPythonBindingsDebugEnabled(); void emit_failmsg(PyObject * exc, const char *msg); int failmsg(const char *fmt, ...); -PyObject* failmsgp(const char *fmt, ...);; +PyObject* failmsgp(const char *fmt, ...); //====================================================================================================================== From 2918071a3e17ac06fe13d0c7223676587f9d6877 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Wed, 28 Sep 2022 17:47:32 +0800 Subject: [PATCH 114/313] add stackblur for imgproc. --- modules/imgproc/include/opencv2/imgproc.hpp | 16 + modules/imgproc/perf/perf_blur.cpp | 48 + modules/imgproc/src/stackblur.cpp | 1261 +++++++++++++++++++ modules/imgproc/test/test_stackblur.cpp | 313 +++++ 4 files changed, 1638 insertions(+) create mode 100644 modules/imgproc/src/stackblur.cpp create mode 100644 modules/imgproc/test/test_stackblur.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index ab64ffe6dd..84f7a9c7eb 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1620,6 +1620,22 @@ CV_EXPORTS_W void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT ); +/** @brief Blurs an image using the StackBlur. +The function applies and StackBlur to an image. +StackBlur can generate similar results as Gaussian blur, and the time does not increase as the kernel size increases. +It creates a kind of moving stack of colors whilst scanning through the image. Thereby it just has to add one new block of color to the right side +of the stack and remove the leftmost color. The remaining colors on the topmost layer of the stack are either added on or reduced by one, +depending on if they are on the right or on the left side of the stack. +Described here: http://underdestruction.com/2004/02/25/stackblur-2004. +Stack Blur Algorithm by Mario Klingemann +@param src input image. The number of channels can be arbitrary, but the depth should be one of +CV_8U, CV_16U, CV_16S or CV_32F. +@param dst output image of the same size and type as src. +@param ksize stack-blurring kernel size. The ksize.width and ksize.height can differ but they both must be +positive and odd. +*/ +CV_EXPORTS_W void stackBlur(InputArray src, OutputArray dst, Size ksize); + /** @brief Convolves an image with the kernel. The function applies an arbitrary linear filter to an image. In-place operation is supported. When diff --git a/modules/imgproc/perf/perf_blur.cpp b/modules/imgproc/perf/perf_blur.cpp index e4092ccb16..d1f5a6b1ca 100644 --- a/modules/imgproc/perf/perf_blur.cpp +++ b/modules/imgproc/perf/perf_blur.cpp @@ -253,4 +253,52 @@ PERF_TEST_P(Size_MatType, BlendLinear, SANITY_CHECK_NOTHING(); } +///////////// Stackblur //////////////////////// +PERF_TEST_P(Size_MatType, stackblur3x3, + testing::Combine( + testing::Values(sz720p, sz1080p, sz2160p), + testing::Values(CV_8UC1, CV_8UC4, CV_16UC1, CV_16SC1, CV_32FC1) + ) +) +{ + Size size = get<0>(GetParam()); + int type = get<1>(GetParam()); + double eps = 1e-3; + + eps = CV_MAT_DEPTH(type) <= CV_32S ? 1 : eps; + + Mat src(size, type); + Mat dst(size, type); + + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() stackBlur(src, dst, Size(3,3)); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Size_MatType, stackblur101x101, + testing::Combine( + testing::Values(sz720p, sz1080p, sz2160p), + testing::Values(CV_8UC1, CV_8UC4, CV_16UC1, CV_16SC1, CV_32FC1) + ) +) +{ + Size size = get<0>(GetParam()); + int type = get<1>(GetParam()); + double eps = 1e-3; + + eps = CV_MAT_DEPTH(type) <= CV_32S ? 1 : eps; + + Mat src(size, type); + Mat dst(size, type); + + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() stackBlur(src, dst, Size(101,101)); + + SANITY_CHECK_NOTHING(); +} + + } // namespace diff --git a/modules/imgproc/src/stackblur.cpp b/modules/imgproc/src/stackblur.cpp new file mode 100644 index 0000000000..7a911ffbb8 --- /dev/null +++ b/modules/imgproc/src/stackblur.cpp @@ -0,0 +1,1261 @@ +// 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. + +/* +StackBlur - a fast almost Gaussian Blur +Theory: http://underdestruction.com/2004/02/25/stackblur-2004 +The code has been borrowed from (https://github.com/flozz/StackBlur) +and adapted for OpenCV by Zihao Mu. + +Below is the original copyright +*/ + +/* +Copyright (c) 2010 Mario Klingemann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "precomp.hpp" +#include "opencv2/core/hal/intrin.hpp" + +#include + +using namespace std; + +#define STACKBLUR_MAX_RADIUS 254 + +static unsigned short const stackblurMul[255] = + { + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 + }; + +static unsigned char const stackblurShr[255] = + { + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 + }; + +namespace cv{ + +#if CV_SIMD +template +inline int opRow(const T* , T* , const std::vector& , const float , const int radius, const int CN, const int ) +{ + return radius * CN; +} + +template<> +inline int opRow(const uchar* srcPtr, uchar* dstPtr, const std::vector& kVec, const float , const int radius, const int CN, const int widthCN) +{ + int kernelSize = (int)kVec.size(); + + int i = radius * CN; + if (radius > STACKBLUR_MAX_RADIUS) + return i; + + const int mulValTab= stackblurMul[radius]; + const int shrValTab= stackblurShr[radius]; + + const int VEC_LINE = v_uint8::nlanes; + + if (kernelSize == 3) + { + v_uint32 v_mulVal = vx_setall_u32(mulValTab); + for (; i <= widthCN - VEC_LINE; i += VEC_LINE) + { + v_uint16 x0l, x0h, x1l, x1h, x2l, x2h; + v_expand(vx_load(srcPtr + i - CN), x0l, x0h); + v_expand(vx_load(srcPtr + i), x1l, x1h); + v_expand(vx_load(srcPtr + i + CN), x2l, x2h); + + x1l = v_add_wrap(v_add_wrap(x1l, x1l), v_add_wrap(x0l, x2l)); + x1h = v_add_wrap(v_add_wrap(x1h, x1h), v_add_wrap(x0h, x2h)); + + v_uint32 y00, y01, y10, y11; + v_expand(x1l, y00, y01); + v_expand(x1h, y10, y11); + + y00 = (y00 * v_mulVal)>>shrValTab; + y01 = (y01 * v_mulVal)>>shrValTab; + y10 = (y10 * v_mulVal)>>shrValTab; + y11 = (y11 * v_mulVal)>>shrValTab; + + v_store(dstPtr + i, v_pack(v_pack(y00, y01), v_pack(y10, y11))); + } + } + else + { + const ushort * kx = kVec.data() + kernelSize/2; + v_int32 v_mulVal = vx_setall_s32(mulValTab); + v_int16 k0 = vx_setall_s16((short)(kx[0])); + + srcPtr += i; + for( ; i <= widthCN - VEC_LINE; i += VEC_LINE, srcPtr += VEC_LINE) + { + v_uint8 v_src = vx_load(srcPtr); + v_int32 s0, s1, s2, s3; + v_mul_expand(v_reinterpret_as_s16(v_expand_low(v_src)), k0, s0, s1); + v_mul_expand(v_reinterpret_as_s16(v_expand_high(v_src)), k0, s2, s3); + + int k = 1, j = CN; + for (; k <= kernelSize / 2 - 1; k += 2, j += 2 * CN) + { + v_int16 k12 = v_reinterpret_as_s16(vx_setall_s32(((int)kx[k] & 0xFFFF) | ((int)kx[k + 1] << 16))); + + v_uint8 v_src0 = vx_load(srcPtr - j); + v_uint8 v_src1 = vx_load(srcPtr - j - CN); + v_uint8 v_src2 = vx_load(srcPtr + j); + v_uint8 v_src3 = vx_load(srcPtr + j + CN); + + v_int16 xl, xh; + v_zip(v_reinterpret_as_s16(v_expand_low(v_src0) + v_expand_low(v_src2)), v_reinterpret_as_s16(v_expand_low(v_src1) + v_expand_low(v_src3)), xl, xh); + s0 += v_dotprod(xl, k12); + s1 += v_dotprod(xh, k12); + v_zip(v_reinterpret_as_s16(v_expand_high(v_src0) + v_expand_high(v_src2)), v_reinterpret_as_s16(v_expand_high(v_src1) + v_expand_high(v_src3)), xl, xh); + s2 += v_dotprod(xl, k12); + s3 += v_dotprod(xh, k12); + } + if( k < kernelSize / 2 + 1 ) + { + v_int16 k1 = vx_setall_s16((short)(kx[k])); + + v_uint8 v_src0 = vx_load(srcPtr - j); + v_uint8 v_src1 = vx_load(srcPtr + j); + + v_int16 xl, xh; + v_zip(v_reinterpret_as_s16(v_expand_low(v_src0)), v_reinterpret_as_s16(v_expand_low(v_src1)), xl, xh); + s0 += v_dotprod(xl, k1); + s1 += v_dotprod(xh, k1); + v_zip(v_reinterpret_as_s16(v_expand_high(v_src0)), v_reinterpret_as_s16(v_expand_high(v_src1)), xl, xh); + s2 += v_dotprod(xl, k1); + s3 += v_dotprod(xh, k1); + } + + s0 = (s0 * v_mulVal)>>shrValTab; + s1 = (s1 * v_mulVal)>>shrValTab; + s2 = (s2 * v_mulVal)>>shrValTab; + s3 = (s3 * v_mulVal)>>shrValTab; + + v_store(dstPtr + i, v_pack(v_reinterpret_as_u16(v_pack(s0, s1)), v_reinterpret_as_u16(v_pack(s2, s3)))); + } + } + return i; +} + +template<> +inline int opRow(const ushort* srcPtr, ushort* dstPtr, const std::vector& kVec, const float , const int radius, const int CN, const int widthCN) +{ + int kernelSize = (int)kVec.size(); + + int i = radius * CN; + if (radius > STACKBLUR_MAX_RADIUS) + return i; + + const int mulValTab= stackblurMul[radius]; + const int shrValTab= stackblurShr[radius]; + + const int VEC_LINE = v_uint16::nlanes; + + v_uint32 v_mulVal = vx_setall_u32(mulValTab); + if (kernelSize == 3) + { + for (; i <= widthCN - VEC_LINE; i += VEC_LINE) + { + v_uint32 x0l, x0h, x1l, x1h, x2l, x2h; + v_expand(vx_load(srcPtr + i - CN), x0l, x0h); + v_expand(vx_load(srcPtr + i), x1l, x1h); + v_expand(vx_load(srcPtr + i + CN), x2l, x2h); + + x1l = v_add(v_add(x1l, x1l), v_add(x0l, x2l)); + x1h = v_add(v_add(x1h, x1h), v_add(x0h, x2h)); + + v_store(dstPtr + i, v_pack((x1l * v_mulVal)>>shrValTab, (x1h * v_mulVal)>>shrValTab)); + } + } + else + { + const ushort * kx = kVec.data() + kernelSize/2; + v_uint16 k0 = vx_setall_u16(kx[0]); + + srcPtr += i; + for( ; i <= widthCN - VEC_LINE; i += VEC_LINE, srcPtr += VEC_LINE) + { + v_uint16 v_src = vx_load(srcPtr); + v_uint32 s0, s1; + + v_mul_expand(v_src, k0, s0, s1); + + int k = 1, j = CN; + for (; k <= kernelSize / 2 - 1; k += 2, j += 2*CN) + { + v_uint16 k1 = vx_setall_u16(kx[k]); + v_uint16 k2 = vx_setall_u16(kx[k + 1]); + + v_uint32 y0, y1; + v_mul_expand(vx_load(srcPtr - j) + vx_load(srcPtr + j), k1, y0, y1); + s0 += y0; + s1 += y1; + v_mul_expand(vx_load(srcPtr - j - CN) + vx_load(srcPtr + j + CN), k2, y0, y1); + s0 += y0; + s1 += y1; + } + if( k < kernelSize / 2 + 1 ) + { + v_uint16 k1 = vx_setall_u16(kx[k]); + + v_uint32 y0, y1; + v_mul_expand(vx_load(srcPtr - j) + vx_load(srcPtr + j), k1, y0, y1); + s0 += y0; + s1 += y1; + } + + s0 = (s0 * v_mulVal)>>shrValTab; + s1 = (s1 * v_mulVal)>>shrValTab; + + v_store(dstPtr + i, v_pack(s0, s1)); + } + } + + return i; +} + +template<> +inline int opRow(const short* srcPtr, short* dstPtr, const std::vector& kVec, const float , const int radius, const int CN, const int widthCN) +{ + int kernelSize = (int)kVec.size(); + int i = radius * CN; + + if (radius > STACKBLUR_MAX_RADIUS) + return i; + + const int mulValTab= stackblurMul[radius]; + const int shrValTab= stackblurShr[radius]; + + const int VEC_LINE = v_int16::nlanes; + v_int32 v_mulVal = vx_setall_s32(mulValTab); + + if (kernelSize == 3) + { + for (; i <= widthCN - VEC_LINE; i += VEC_LINE) + { + v_int32 x0l, x0h, x1l, x1h, x2l, x2h; + v_expand(vx_load(srcPtr + i - CN), x0l, x0h); + v_expand(vx_load(srcPtr + i), x1l, x1h); + v_expand(vx_load(srcPtr + i + CN), x2l, x2h); + + x1l = v_add(v_add(x1l, x1l), v_add(x0l, x2l)); + x1h = v_add(v_add(x1h, x1h), v_add(x0h, x2h)); + + v_store(dstPtr + i, v_pack((x1l * v_mulVal)>>shrValTab, (x1h * v_mulVal)>>shrValTab)); + } + } + else + { + const ushort * kx = kVec.data() + kernelSize/2; + v_int16 k0 = vx_setall_s16((short)(kx[0])); + + srcPtr += i; + for( ; i <= widthCN - VEC_LINE; i += VEC_LINE, srcPtr += VEC_LINE) + { + v_int16 v_src = vx_load(srcPtr); + v_int32 s0, s1; + v_mul_expand(v_src, k0, s0, s1); + + int k = 1, j = CN; + for (; k <= kernelSize / 2 - 1; k += 2, j += 2 * CN) + { + v_int16 k1 = vx_setall_s16((short)kx[k]); + v_int16 k2 = vx_setall_s16((short)kx[k + 1]); + + v_int32 y0, y1; + + v_mul_expand(vx_load(srcPtr - j) + vx_load(srcPtr + j), k1, y0, y1); + s0 += y0; + s1 += y1; + v_mul_expand(vx_load(srcPtr - j - CN) + vx_load(srcPtr + j + CN), k2, y0, y1); + s0 += y0; + s1 += y1; + } + if( k < kernelSize / 2 + 1 ) + { + v_int16 k1 = vx_setall_s16((short)kx[k]); + v_int32 y0, y1; + v_mul_expand(vx_load(srcPtr - j) + vx_load(srcPtr + j), k1, y0, y1); + s0 += y0; + s1 += y1; + } + + s0 = (s0 * v_mulVal)>>shrValTab; + s1 = (s1 * v_mulVal)>>shrValTab; + + v_store(dstPtr + i, v_pack(s0, s1)); + } + } + return i; +} + +template<> +inline int opRow(const float* srcPtr, float* dstPtr, const std::vector& kVec, const float mulVal, const int radius, const int CN, const int widthCN) +{ + int kernelSize = (int)kVec.size(); + int i = radius * CN; + + v_float32 v_mulVal = vx_setall_f32(mulVal); + const int VEC_LINE = v_float32::nlanes; + const int VEC_LINE4 = VEC_LINE * 4; + + if (kernelSize == 3) + { + for (; i <= widthCN - VEC_LINE4; i += VEC_LINE4) + { + v_float32 v_srcPtr0 = vx_load(srcPtr + i); + v_float32 v_srcPtr1 = vx_load(srcPtr + VEC_LINE + i) ; + v_float32 v_srcPtr2 = vx_load(srcPtr + VEC_LINE * 2 + i); + v_float32 v_srcPtr3 = vx_load(srcPtr + VEC_LINE * 3 + i); + + v_float32 v_sumVal0 = v_srcPtr0 + v_srcPtr0 + vx_load(srcPtr + i - CN) + vx_load(srcPtr + i + CN); + v_float32 v_sumVal1 = v_srcPtr1 + v_srcPtr1 + vx_load(srcPtr + VEC_LINE + i - CN) + vx_load(srcPtr + VEC_LINE + i + CN); + v_float32 v_sumVal2 = v_srcPtr2 + v_srcPtr2 + vx_load(srcPtr + VEC_LINE * 2 + i - CN) + vx_load(srcPtr + VEC_LINE * 2 + i + CN); + v_float32 v_sumVal3 = v_srcPtr3 + v_srcPtr3 + vx_load(srcPtr + VEC_LINE * 3 + i - CN) + vx_load(srcPtr + VEC_LINE * 3 + i + CN); + + v_store(dstPtr + i, v_sumVal0 * v_mulVal); + v_store(dstPtr + i + VEC_LINE, v_sumVal1 * v_mulVal); + v_store(dstPtr + i + VEC_LINE * 2, v_sumVal2 * v_mulVal); + v_store(dstPtr + i + VEC_LINE * 3, v_sumVal3 * v_mulVal); + } + + for (; i <= widthCN - VEC_LINE; i += VEC_LINE) + { + v_float32 v_srcPtr = vx_load(srcPtr + i); + v_float32 v_sumVal = v_srcPtr + v_srcPtr + vx_load(srcPtr + i - CN) + vx_load(srcPtr + i + CN); + v_store(dstPtr + i, v_sumVal * v_mulVal); + } + } + else + { + const ushort * kx = kVec.data() + kernelSize/2; + v_float32 k0 = vx_setall_f32((float)(kx[0])); + + srcPtr += i; + for( ; i <= widthCN - VEC_LINE; i += VEC_LINE, srcPtr += VEC_LINE) + { + v_float32 v_src = vx_load(srcPtr); + v_float32 s0; + s0 = v_src * k0; + + int k = 1, j = CN; + for (; k <= kernelSize / 2 - 1; k += 2, j += 2 * CN) + { + v_float32 k1 = vx_setall_f32((float)kx[k]); + v_float32 k2 = vx_setall_f32((float)kx[k + 1]); + + s0 += (vx_load(srcPtr - j) + vx_load(srcPtr + j)) * k1; + s0 += (vx_load(srcPtr - j - CN) + vx_load(srcPtr + j + CN)) * k2; + } + if( k < kernelSize / 2 + 1 ) + { + v_float32 k1 = vx_setall_f32((float)kx[k]); + + s0 += (vx_load(srcPtr - j) + vx_load(srcPtr + j)) * k1; + } + + v_store(dstPtr + i, s0 * v_mulVal); + } + } + return i; +} + +template +inline int opComputeDiff(const T*& , TBuf*& , const int , const int) +{ + return 0; +} + +template<> +inline int opComputeDiff(const uchar*& srcPtr, int*& diff0, const int w, const int CNR1) +{ + int index = 0; + const int VEC_LINE_8 = v_uint8::nlanes; + const int VEC_LINE_32 = v_int32::nlanes; + for (; index <= w - VEC_LINE_8; index += VEC_LINE_8, diff0+=VEC_LINE_8, srcPtr+=VEC_LINE_8) + { + v_uint16 x0l, x0h, x1l, x1h; + v_expand(vx_load(srcPtr + CNR1), x0l, x0h); + v_expand(vx_load(srcPtr), x1l, x1h); + + v_int32 y0, y1, y2, y3; + v_expand(v_reinterpret_as_s16(x0l) - v_reinterpret_as_s16(x1l), y0, y1); + v_expand(v_reinterpret_as_s16(x0h) - v_reinterpret_as_s16(x1h), y2, y3); + + v_store(diff0, y0); + v_store(diff0 + VEC_LINE_32, y1); + v_store(diff0 + VEC_LINE_32 * 2, y2); + v_store(diff0 + VEC_LINE_32 * 3, y3); + } + return index; +} +#endif + +template +class ParallelStackBlurRow : public ParallelLoopBody +{ +public: + ParallelStackBlurRow (const Mat &_src, Mat &_dst, int _radius): src(_src), dst(_dst) ,radius(_radius) + { + width= dst.cols; + wm = width - 1; + mulVal = 1.0f / ((radius + 1) * (radius + 1)); + CN = src.channels(); + } + + ~ParallelStackBlurRow() {} + + ParallelStackBlurRow& operator=(const ParallelStackBlurRow &) { return *this; } + + /* + * The idea is as follows: + * The stack can be understood as a sliding window of length kernel size. + * The sliding window moves one element at a time from left to right. + * The sumIn stores the elements added to the stack each time, + * and sumOut stores the subtracted elements. Every time stack moves, stack, sumIn and sumOut are updated. + * The dst will be calculated using the following formula: + * dst[i] = (stack + sumIn - sumOut) / stack_num + * In the Row direction, in order to avoid redundant computation, + * we save the sumIn - sumOut as a diff vector. + * So the new formula is: + * dst[i] = (stack + diff[i]) / stack_num. + * In practice, we use multiplication and bit shift right to simulate integer division: + * dst[i] = ((stack + diff[i]) * mulVal) >> shrVal. + * */ + virtual void operator ()(const Range& range) const CV_OVERRIDE + { + const int kernelSize = 2 * radius + 1; + + if (kernelSize <= 9 && width > kernelSize) // Special branch for small kernel + { + std::vector kVec; + for (int i = 0; i < kernelSize; i++) + { + if (i <= radius) + kVec.push_back(ushort(i + 1)); + else + kVec.push_back(ushort(2 * radius - i + 1)); + } + + const ushort * kx = kVec.data() + kernelSize/2; + for (int row = range.start; row < range.end; row++) + { + const T* srcPtr = src.ptr(row); + T* dstPtr = dst.ptr(row); + TBuf sumVal; + + // init + for (int i = 0; i < radius; i++) + { + for (int ci = 0; ci < CN; ci++) + { + sumVal = 0; + for (int k = 0; k < kernelSize; k++) + { + int index = std::max(k - radius + i, 0); + sumVal += (TBuf)srcPtr[index * CN + ci] * (TBuf)kVec[k]; + } + dstPtr[i*CN + ci] = (T)(sumVal * mulVal); + } + } + + int widthCN = (width - radius) * CN; + + // middle + int wc = radius * CN; +#if CV_SIMD + wc = opRow(srcPtr, dstPtr, kVec, mulVal, radius, CN, widthCN); +#endif + for (; wc < widthCN; wc++) + { + sumVal = srcPtr[wc] * kx[0]; + for (int k = 1; k <= radius; k++) + sumVal += ((TBuf)(srcPtr[wc + k * CN])+(TBuf)(srcPtr[wc - k * CN])) * (TBuf)kx[k]; + dstPtr[wc] = (T)(sumVal * mulVal); + } + + // tail + for (int i = wc / CN; i < width; i++) + { + for (int ci = 0; ci < CN; ci++) + { + sumVal = 0; + for (int k = 0; k < kernelSize; k++) + { + int index = std::min(k - radius + i, wm); + sumVal += (TBuf)srcPtr[index * CN + ci] * (TBuf)kVec[k]; + } + dstPtr[i*CN + ci] = (T)(sumVal * mulVal); + } + } + + } + } + else + { + size_t bufSize = CN * (width + radius) * sizeof(TBuf) + 2 * CN * sizeof(TBuf); + AutoBuffer _buf(bufSize + 16); + uchar* bufptr = alignPtr(_buf.data(), 16); + TBuf* diffVal = (TBuf*)bufptr; + TBuf* sum = diffVal+CN; + TBuf* diff = sum + CN; + + const int CNR1 = CN * (radius + 1); + const int widthCN = (width - radius - 1) * CN; + + for (int row = range.start; row < range.end; row++) + { + memset(bufptr, 0, bufSize); + + const T* srcPtr = src.ptr(row); + T* dstPtr = dst.ptr(row); + + int radiusMul = (radius + 2) * (radius + 1) / 2; + for (int ci = 0; ci < CN; ci++) + sum[ci] += (TBuf)srcPtr[ci] * radiusMul; + + // compute diff + const T* srcPtr0 = srcPtr; + + // init + for (int i = 0; i < radius; i++) + { + if (i < wm) srcPtr0 += CN; + for (int ci = 0; ci < CN; ci++) + { + diff[i*CN + ci] = (TBuf)srcPtr0[ci] - (TBuf)srcPtr[ci]; + diffVal[ci] += diff[i*CN + ci]; + sum[ci] += srcPtr0[ci] * (radius - i); + } + } + + // middle + auto diff0 = diff + radius * CN; + int index = 0; +#if CV_SIMD + index = opComputeDiff(srcPtr, diff0, widthCN, CNR1); +#endif + + for (; index < widthCN; index++, diff0++, srcPtr++) + diff0[0] = (TBuf)(srcPtr[CNR1]) - (TBuf)(srcPtr[0]); + + // tails + srcPtr0 = src.ptr(row) + index; + const T* srcPtr1 = src.ptr(row) + (width - 1) * CN; + int dist = width - index/CN; + for (int r = 0; r < radius; r++, diff0 += CN) + { + for (int ci = 0; ci < CN; ci++) + diff0[ci] = (TBuf)(srcPtr1[ci]) - (TBuf)(srcPtr0[ci]); + + if (dist >= r) + { + srcPtr0 += CN; + dist--; + } + } + + srcPtr = src.ptr(row); + diff0 = diff + radius * CN; + for (int ci = 0; ci < CN; ci++) + diffVal[ci] += diff0[ci]; + diff0 += CN; + + if (CN == 1) + { + for (int i = 0; i < width; i++, diff0 ++, dstPtr ++, srcPtr ++) + { + *(dstPtr) = saturate_cast((sum[0] * mulVal)); + sum[0] += diffVal[0]; + diffVal[0] += (diff0[0] - diff0[-CNR1]); + } + } + else if (CN == 3) + { + for (int i = 0; i < width; i++, diff0 += CN, dstPtr += CN, srcPtr += CN) + { + *(dstPtr + 0) = saturate_cast((sum[0] * mulVal)); + *(dstPtr + 1) = saturate_cast((sum[1] * mulVal)); + *(dstPtr + 2) = saturate_cast((sum[2] * mulVal)); + + sum[0] += diffVal[0]; + sum[1] += diffVal[1]; + sum[2] += diffVal[2]; + + diffVal[0] += (diff0[0] - diff0[0 - CNR1]); + diffVal[1] += (diff0[1] - diff0[1 - CNR1]); + diffVal[2] += (diff0[2] - diff0[2 - CNR1]); + } + } + else if (CN == 4) + { + for (int i = 0; i < width; i++, diff0 += CN, dstPtr += CN, srcPtr += CN) + { + *(dstPtr + 0) = saturate_cast((sum[0] * mulVal)); + *(dstPtr + 1) = saturate_cast((sum[1] * mulVal)); + *(dstPtr + 2) = saturate_cast((sum[2] * mulVal)); + *(dstPtr + 3) = saturate_cast((sum[3] * mulVal)); + + sum[0] += diffVal[0]; + sum[1] += diffVal[1]; + sum[2] += diffVal[2]; + sum[3] += diffVal[3]; + + diffVal[0] += (diff0[0] - diff0[0 - CNR1]); + diffVal[1] += (diff0[1] - diff0[1 - CNR1]); + diffVal[2] += (diff0[2] - diff0[2 - CNR1]); + diffVal[3] += (diff0[3] - diff0[3 - CNR1]); + } + } + else + { + int i = 0; + for (; i < width; i++, diff0 += CN, dstPtr += CN, srcPtr += CN) + { + for (int ci = 0; ci < CN; ci++) + { + *(dstPtr + ci) = saturate_cast((sum[ci] * mulVal)); + sum[ci] += diffVal[ci]; + diffVal[ci] += (diff0[ci] - diff0[ci - CNR1]); + } + } + } + } + } + } + +private: + const Mat &src; + Mat &dst; + int radius; + int width; + int wm; + int CN; + float mulVal; +}; + +#if CV_SIMD +template +inline int opColumn(const T* , T* , T* , TBuf* , TBuf* , TBuf* , const float , + const int , const int , const int , const int , const int ) +{ + return 0; +} + +template<> +inline int opColumn(const float* srcPtr, float* dstPtr, float* stack, float* sum, float* sumIn, + float* sumOut, const float mulVal, const int , const int , + const int widthLen, const int ss, const int sp1) +{ + int k = 0; + v_float32 v_mulVal = vx_setall_f32(mulVal); + const int VEC_LINE = v_float32::nlanes; + const int VEC_LINE4 = 4 * VEC_LINE; + + auto stackStartPtr = stack + ss * widthLen; + auto stackSp1Ptr = stack + sp1 * widthLen; + + for (;k <= widthLen - VEC_LINE4; k += VEC_LINE4) + { + v_float32 v_sum0 = vx_load(sum + k); + v_float32 v_sum1 = vx_load(sum + VEC_LINE + k); + v_float32 v_sum2 = vx_load(sum + VEC_LINE * 2 + k); + v_float32 v_sum3 = vx_load(sum + VEC_LINE * 3 + k); + + v_float32 v_sumOut0 = vx_load(sumOut + k); + v_float32 v_sumOut1 = vx_load(sumOut + VEC_LINE + k); + v_float32 v_sumOut2 = vx_load(sumOut + VEC_LINE * 2 + k); + v_float32 v_sumOut3 = vx_load(sumOut + VEC_LINE * 3 + k); + + v_float32 v_sumIn0 = vx_load(sumIn + k); + v_float32 v_sumIn1 = vx_load(sumIn + VEC_LINE + k); + v_float32 v_sumIn2 = vx_load(sumIn + VEC_LINE * 2 + k); + v_float32 v_sumIn3 = vx_load(sumIn + VEC_LINE * 3+ k); + + v_store(dstPtr + k, v_sum0 * v_mulVal); + v_store(dstPtr + VEC_LINE + k, v_sum1 * v_mulVal); + v_store(dstPtr + VEC_LINE * 2 + k, v_sum2 * v_mulVal); + v_store(dstPtr + VEC_LINE * 3 + k, v_sum3 * v_mulVal); + + v_sum0 -= v_sumOut0; + v_sum1 -= v_sumOut1; + v_sum2 -= v_sumOut2; + v_sum3 -= v_sumOut3; + + v_sumOut0 -= vx_load(stackStartPtr + k); + v_sumOut1 -= vx_load(stackStartPtr + VEC_LINE + k); + v_sumOut2 -= vx_load(stackStartPtr + VEC_LINE * 2 + k); + v_sumOut3 -= vx_load(stackStartPtr + VEC_LINE * 3 + k); + + v_float32 v_srcPtr0 = vx_load(srcPtr + k); + v_float32 v_srcPtr1 = vx_load(srcPtr + VEC_LINE + k); + v_float32 v_srcPtr2 = vx_load(srcPtr + VEC_LINE * 2 + k); + v_float32 v_srcPtr3 = vx_load(srcPtr + VEC_LINE * 3 + k); + + v_store(stackStartPtr + k, v_srcPtr0); + v_store(stackStartPtr + VEC_LINE + k, v_srcPtr1); + v_store(stackStartPtr + VEC_LINE * 2 + k, v_srcPtr2); + v_store(stackStartPtr + VEC_LINE * 3 + k, v_srcPtr3); + + v_sumIn0 += v_srcPtr0; + v_sumIn1 += v_srcPtr1; + v_sumIn2 += v_srcPtr2; + v_sumIn3 += v_srcPtr3; + + v_store(sum + k, v_sum0 + v_sumIn0); + v_store(sum + VEC_LINE + k, v_sum1 + v_sumIn1); + v_store(sum + VEC_LINE * 2 + k, v_sum2 + v_sumIn2); + v_store(sum + VEC_LINE * 3 + k, v_sum3 + v_sumIn3); + + v_srcPtr0 = vx_load(stackSp1Ptr + k); + v_srcPtr1 = vx_load(stackSp1Ptr + VEC_LINE + k); + v_srcPtr2 = vx_load(stackSp1Ptr + VEC_LINE * 2 + k); + v_srcPtr3 = vx_load(stackSp1Ptr + VEC_LINE * 3 + k); + + v_sumOut0 += v_srcPtr0; + v_sumOut1 += v_srcPtr1; + v_sumOut2 += v_srcPtr2; + v_sumOut3 += v_srcPtr3; + + v_store(sumOut + k, v_sumOut0); + v_store(sumOut + VEC_LINE + k, v_sumOut1); + v_store(sumOut + VEC_LINE * 2 + k, v_sumOut2); + v_store(sumOut + VEC_LINE * 3 + k, v_sumOut3); + + v_sumIn0 -= v_srcPtr0; + v_sumIn1 -= v_srcPtr1; + v_sumIn2 -= v_srcPtr2; + v_sumIn3 -= v_srcPtr3; + + v_store(sumIn + k, v_sumIn0); + v_store(sumIn + VEC_LINE + k, v_sumIn1); + v_store(sumIn + VEC_LINE * 2 + k, v_sumIn2); + v_store(sumIn + VEC_LINE * 3 + k, v_sumIn3); + } + + for (;k <= widthLen - VEC_LINE; k += VEC_LINE) + { + v_float32 v_sum = vx_load(sum + k); + v_float32 v_sumOut = vx_load(sumOut + k); + v_float32 v_sumIn = vx_load(sumIn + k); + + v_store(dstPtr + k, v_sum * v_mulVal); + v_sum -= v_sumOut; + v_sumOut -= vx_load(stackStartPtr + k); + + v_float32 v_srcPtr = vx_load(srcPtr + k); + v_store(stackStartPtr + k, v_srcPtr); + + v_sumIn += v_srcPtr; + v_store(sum + k, v_sum + v_sumIn); + + v_srcPtr = vx_load(stackSp1Ptr + k); + v_sumOut += v_srcPtr; + v_store(sumOut + k, v_sumOut); + v_sumIn -= v_srcPtr; + v_store(sumIn + k, v_sumIn); + } + return k; +} + +template<> +inline int opColumn(const uchar* srcPtr, uchar* dstPtr, uchar* stack, int* sum, int* sumIn, + int* sumOut, const float , const int mulValTab, const int shrValTab, + const int widthLen, const int ss, const int sp1) +{ + int k = 0; + if (mulValTab != 0 && shrValTab != 0) + { + const int VEC_LINE_8 = v_uint8::nlanes; + const int VEC_LINE_32 = v_int32::nlanes; + v_int32 v_mulVal = vx_setall_s32(mulValTab); + + auto stackStartPtr = stack + ss * widthLen; + auto stackSp1Ptr = stack + sp1 * widthLen; + + for (;k <= widthLen - VEC_LINE_8; k += VEC_LINE_8) + { + v_int32 v_sum0, v_sum1, v_sum2, v_sum3; + v_int32 v_sumIn0, v_sumIn1, v_sumIn2, v_sumIn3; + v_int32 v_sumOut0, v_sumOut1, v_sumOut2, v_sumOut3; + + v_sum0 = vx_load(sum + k); + v_sum1 = vx_load(sum + k + VEC_LINE_32); + v_sum2 = vx_load(sum + k + VEC_LINE_32 * 2); + v_sum3 = vx_load(sum + k + VEC_LINE_32 * 3); + + v_sumIn0 = vx_load(sumIn + k); + v_sumIn1 = vx_load(sumIn + k + VEC_LINE_32); + v_sumIn2 = vx_load(sumIn + k + VEC_LINE_32 * 2); + v_sumIn3 = vx_load(sumIn + k + VEC_LINE_32 * 3); + + v_sumOut0 = vx_load(sumOut + k); + v_sumOut1 = vx_load(sumOut + k + VEC_LINE_32); + v_sumOut2 = vx_load(sumOut + k + VEC_LINE_32 * 2); + v_sumOut3 = vx_load(sumOut + k + VEC_LINE_32 * 3); + + v_store(dstPtr + k, + v_pack( + v_reinterpret_as_u16(v_pack((v_sum0 * v_mulVal)>>shrValTab, (v_sum1 * v_mulVal)>>shrValTab)), + v_reinterpret_as_u16(v_pack((v_sum2 * v_mulVal)>>shrValTab, (v_sum3 * v_mulVal)>>shrValTab)))); + + v_sum0 -= v_sumOut0; + v_sum1 -= v_sumOut1; + v_sum2 -= v_sumOut2; + v_sum3 -= v_sumOut3; + + v_uint16 x0l, x0h; + v_int32 v_ss0, v_ss1, v_ss2, v_ss3; + + v_expand(vx_load(stackStartPtr + k), x0l, x0h); + v_expand(v_reinterpret_as_s16(x0l), v_ss0, v_ss1); + v_expand(v_reinterpret_as_s16(x0h), v_ss2, v_ss3); + + v_sumOut0 -= v_ss0; + v_sumOut1 -= v_ss1; + v_sumOut2 -= v_ss2; + v_sumOut3 -= v_ss3; + + v_expand(vx_load(srcPtr + k), x0l, x0h); + v_expand(v_reinterpret_as_s16(x0l), v_ss0, v_ss1); + v_expand(v_reinterpret_as_s16(x0h), v_ss2, v_ss3); + + memcpy(stackStartPtr + k,srcPtr + k, VEC_LINE_8 * sizeof (uchar)); + + v_sumIn0 += v_ss0; + v_sumIn1 += v_ss1; + v_sumIn2 += v_ss2; + v_sumIn3 += v_ss3; + + v_store(sum + k, v_sum0 + v_sumIn0); + v_store(sum + VEC_LINE_32 + k, v_sum1 + v_sumIn1); + v_store(sum + VEC_LINE_32 * 2 + k, v_sum2 + v_sumIn2); + v_store(sum + VEC_LINE_32 * 3 + k, v_sum3 + v_sumIn3); + + v_expand(vx_load(stackSp1Ptr + k), x0l, x0h); + v_expand(v_reinterpret_as_s16(x0l), v_ss0, v_ss1); + v_expand(v_reinterpret_as_s16(x0h), v_ss2, v_ss3); + + v_sumOut0 += v_ss0; + v_sumOut1 += v_ss1; + v_sumOut2 += v_ss2; + v_sumOut3 += v_ss3; + + v_store(sumOut + k, v_sumOut0); + v_store(sumOut + VEC_LINE_32 + k, v_sumOut1); + v_store(sumOut + VEC_LINE_32 * 2 + k, v_sumOut2); + v_store(sumOut + VEC_LINE_32 * 3 + k, v_sumOut3); + + v_sumIn0 -= v_ss0; + v_sumIn1 -= v_ss1; + v_sumIn2 -= v_ss2; + v_sumIn3 -= v_ss3; + + v_store(sumIn + k, v_sumIn0); + v_store(sumIn + VEC_LINE_32 + k, v_sumIn1); + v_store(sumIn + VEC_LINE_32 * 2 + k, v_sumIn2); + v_store(sumIn + VEC_LINE_32 * 3 + k, v_sumIn3); + } + } + return k; +} + +template<> +inline int opColumn(const short* srcPtr, short* dstPtr, short* stack, int* sum, int* sumIn, + int* sumOut, const float , const int mulValTab, const int shrValTab, + const int widthLen, const int ss, const int sp1) +{ + int k = 0; + if (mulValTab != 0 && shrValTab != 0) + { + const int VEC_LINE_16 = v_int16::nlanes; + const int VEC_LINE_32 = v_int32::nlanes; + v_int32 v_mulVal = vx_setall_s32(mulValTab); + + auto stackStartPtr = stack + ss * widthLen; + auto stackSp1Ptr = stack + sp1 * widthLen; + for (;k <= widthLen - VEC_LINE_16; k += VEC_LINE_16) + { + v_int32 v_sum0, v_sum1; + v_int32 v_sumIn0, v_sumIn1; + v_int32 v_sumOut0, v_sumOut1; + + v_sum0 = vx_load(sum + k); + v_sum1 = vx_load(sum + k + VEC_LINE_32); + + v_sumIn0 = vx_load(sumIn + k); + v_sumIn1 = vx_load(sumIn + k + VEC_LINE_32); + + v_sumOut0 = vx_load(sumOut + k); + v_sumOut1 = vx_load(sumOut + k + VEC_LINE_32); + + v_store(dstPtr + k,v_pack((v_sum0 * v_mulVal)>>shrValTab, (v_sum1 * v_mulVal)>>shrValTab)); + + v_sum0 -= v_sumOut0; + v_sum1 -= v_sumOut1; + + v_int32 v_ss0, v_ss1; + v_expand(vx_load(stackStartPtr + k), v_ss0, v_ss1); + + v_sumOut0 -= v_ss0; + v_sumOut1 -= v_ss1; + + v_expand(vx_load(srcPtr + k), v_ss0, v_ss1); + memcpy(stackStartPtr + k,srcPtr + k, VEC_LINE_16 * sizeof (short)); + + v_sumIn0 += v_ss0; + v_sumIn1 += v_ss1; + + v_sum0 += v_sumIn0; + v_sum1 += v_sumIn1; + + v_store(sum + k, v_sum0); + v_store(sum + VEC_LINE_32 + k, v_sum1); + + v_expand(vx_load(stackSp1Ptr + k), v_ss0, v_ss1); + + v_sumOut0 += v_ss0; + v_sumOut1 += v_ss1; + + v_store(sumOut + k, v_sumOut0); + v_store(sumOut + VEC_LINE_32 + k, v_sumOut1); + + v_sumIn0 -= v_ss0; + v_sumIn1 -= v_ss1; + + v_store(sumIn + k, v_sumIn0); + v_store(sumIn + VEC_LINE_32 + k, v_sumIn1); + } + } + return k; +} + +template<> +inline int opColumn(const ushort* srcPtr, ushort* dstPtr, ushort* stack, int* sum, int* sumIn, + int* sumOut, const float , const int mulValTab, const int shrValTab, + const int widthLen, const int ss, const int sp1) +{ + int k = 0; + if (mulValTab != 0 && shrValTab != 0) + { + const int VEC_LINE_16 = v_uint16::nlanes; + const int VEC_LINE_32 = v_int32::nlanes; + v_uint32 v_mulVal = vx_setall_u32((uint32_t)mulValTab); + + auto stackStartPtr = stack + ss * widthLen; + auto stackSp1Ptr = stack + sp1 * widthLen; + for (;k <= widthLen - VEC_LINE_16; k += VEC_LINE_16) + { + v_int32 v_sum0, v_sum1; + v_int32 v_sumIn0, v_sumIn1; + v_int32 v_sumOut0, v_sumOut1; + + v_sum0 = vx_load(sum + k); + v_sum1 = vx_load(sum + k + VEC_LINE_32); + + v_sumIn0 = vx_load(sumIn + k); + v_sumIn1 = vx_load(sumIn + k + VEC_LINE_32); + + v_sumOut0 = vx_load(sumOut + k); + v_sumOut1 = vx_load(sumOut + k + VEC_LINE_32); + + v_store(dstPtr + k, v_pack((v_reinterpret_as_u32(v_sum0) * v_mulVal)>>shrValTab, (v_reinterpret_as_u32(v_sum1) * v_mulVal)>>shrValTab)); + + v_sum0 -= v_sumOut0; + v_sum1 -= v_sumOut1; + + v_uint32 v_ss0, v_ss1; + v_expand(vx_load(stackStartPtr + k), v_ss0, v_ss1); + + v_sumOut0 -= v_reinterpret_as_s32(v_ss0); + v_sumOut1 -= v_reinterpret_as_s32(v_ss1); + + v_expand(vx_load(srcPtr + k), v_ss0, v_ss1); + + memcpy(stackStartPtr + k,srcPtr + k, VEC_LINE_16 * sizeof (ushort)); + + v_sumIn0 += v_reinterpret_as_s32(v_ss0); + v_sumIn1 += v_reinterpret_as_s32(v_ss1); + + v_sum0 += v_sumIn0; + v_sum1 += v_sumIn1; + + v_store(sum + k, v_sum0); + v_store(sum + VEC_LINE_32 + k, v_sum1); + + v_expand(vx_load(stackSp1Ptr + k), v_ss0, v_ss1); + + v_sumOut0 += v_reinterpret_as_s32(v_ss0); + v_sumOut1 += v_reinterpret_as_s32(v_ss1); + + v_store(sumOut + k, v_sumOut0); + v_store(sumOut + VEC_LINE_32 + k, v_sumOut1); + + v_sumIn0 -= v_reinterpret_as_s32(v_ss0); + v_sumIn1 -= v_reinterpret_as_s32(v_ss1); + + v_store(sumIn + k, v_sumIn0); + v_store(sumIn + VEC_LINE_32 + k, v_sumIn1); + } + } + return k; +} +#endif + +template +class ParallelStackBlurColumn: + public ParallelLoopBody +{ +public: + ParallelStackBlurColumn (const Mat & _src, Mat &_dst, int _radius):src(_src), dst(_dst) ,radius(_radius) + { + CN = src.channels(); + widthElem = CN * src.cols; + height = src.rows; + hm = src.rows - 1; + mulVal = 1.0f / ((radius + 1)*(radius + 1)); + if (radius <= STACKBLUR_MAX_RADIUS) + { + shrValTab = stackblurShr[radius]; + mulValTab = stackblurMul[radius]; + } + else + { + shrValTab = 0; + mulValTab = 0; + } + } + + ~ParallelStackBlurColumn() {} + + ParallelStackBlurColumn& operator=(const ParallelStackBlurColumn &) { return *this; } + + virtual void operator ()(const Range& range) const CV_OVERRIDE + { + if (radius == 0) + return; + + const int kernelSize = 2 * radius + 1; + int widthImg = std::min(range.end, src.cols * CN); + int widthLen = widthImg - range.start; + + size_t bufSize = 3 * widthLen * sizeof(TBuf) + kernelSize * widthLen * sizeof(T); + + AutoBuffer _buf(bufSize + 16); + uchar* bufptr = alignPtr(_buf.data(), 16); + + TBuf* sum = (TBuf *)bufptr; + TBuf* sumIn = sum + widthLen; + TBuf* sumOut = sumIn + widthLen; + T* stack = (T* )(sumOut + widthLen); + + memset(bufptr, 0, bufSize); + + const T* srcPtr =dst.ptr() + range.start; + + for (int i = 0; i <= radius; i++) + { + for (int k = 0; k < widthLen; k++) + { + stack[i * widthLen + k] = *(srcPtr + k); + sum[k] += *(srcPtr + k) * (i + 1); + sumOut[k] += *(srcPtr + k); + } + } + + for (int i = 1; i <= radius; i++) + { + if (i <= hm) srcPtr += widthElem; + for (int k = 0; k < widthLen; k++) + { + T tmp = *(srcPtr + k); + stack[(i + radius) * widthLen + k] = tmp; + sum[k] += tmp * (radius - i + 1); + sumIn[k] += tmp; + } + } + + int sp = radius; + int yp = radius; + + if (yp > hm) yp = hm; + + T* dstPtr = dst.ptr() + range.start; + srcPtr = dst.ptr(yp) + range.start; + int stackStart = 0; + + for(int i = 0; i < height; i++) + { + stackStart = sp + kernelSize - radius; + if (stackStart >= kernelSize) stackStart -= kernelSize; + + int sp1 = sp + 1; + sp1 &= -(sp1 < kernelSize); + + if (yp < hm) + { + yp++; + srcPtr += widthElem; + } + + int k = 0; +#if CV_SIMD + k = opColumn(srcPtr, dstPtr, stack, sum, sumIn, sumOut, mulVal, mulValTab, shrValTab, + widthLen, stackStart, sp1); +#endif + + for (; k < widthLen; k++) + { + *(dstPtr + k) = static_cast(sum[k] * mulVal); + sum[k] -= sumOut[k]; + sumOut[k] -= stack[stackStart * widthLen + k]; + + stack[stackStart * widthLen + k] = *(srcPtr + k); + sumIn[k] += *(srcPtr + k); + sum[k] += sumIn[k]; + + sumOut[k] += stack[sp1 * widthLen + k]; + sumIn[k] -= stack[sp1 * widthLen + k]; + } + + dstPtr += widthElem; + ++sp; + if (sp >= kernelSize) sp = 0; + } + } + +private: + const Mat &src; + Mat &dst; + int radius; + int CN; + int height; + int widthElem; + int hm; + float mulVal; + int mulValTab; + int shrValTab; +}; + +void stackBlur(InputArray _src, OutputArray _dst, Size ksize) +{ + CV_INSTRUMENT_REGION(); + CV_Assert(!_src.empty()); + + CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 && + ksize.height > 0 && ksize.height % 2 == 1 ); + + int radiusH = ksize.height / 2; + int radiusW = ksize.width / 2; + + int stype = _src.type(), sdepth = _src.depth(); + Mat src = _src.getMat(); + + if (ksize.width == 1) + { + _src.copyTo(_dst); + + if (ksize.height == 1) + return; + } + else + { + _dst.create( src.size(), stype); + } + + Mat dst = _dst.getMat(); + int numOfThreads = getNumThreads(); + int widthElem = src.cols * src.channels(); + + if (dst.rows / numOfThreads < 3) + numOfThreads = std::max(1, dst.rows / 3); + + if (sdepth == CV_8U) + { + if (ksize.width != 1) + parallel_for_(Range(0, src.rows), ParallelStackBlurRow(src, dst, radiusW), numOfThreads); + if (ksize.height != 1) + parallel_for_(Range(0, widthElem), ParallelStackBlurColumn(dst, dst, radiusH), numOfThreads); + } + else if (sdepth == CV_16S) + { + if (ksize.width != 1) + parallel_for_(Range(0, src.rows), ParallelStackBlurRow(src, dst, radiusW), numOfThreads); + if (ksize.height != 1) + parallel_for_(Range(0, widthElem), ParallelStackBlurColumn(dst, dst, radiusH), numOfThreads); + } + else if (sdepth == CV_16U) + { + if (ksize.width != 1) + parallel_for_(Range(0, src.rows), ParallelStackBlurRow(src, dst, radiusW), numOfThreads); + if (ksize.height != 1) + parallel_for_(Range(0, widthElem), ParallelStackBlurColumn(dst, dst, radiusH), numOfThreads); + } + else if (sdepth == CV_32F) + { + if (ksize.width != 1) + parallel_for_(Range(0, src.rows), ParallelStackBlurRow(src, dst, radiusW), numOfThreads); + if (ksize.height != 1) + parallel_for_(Range(0, widthElem), ParallelStackBlurColumn(dst, dst, radiusH), numOfThreads); + } + else + CV_Error_( CV_StsNotImplemented, + ("Unsupported input format in StackBlur, the supported formats are: CV_8U, CV_16U, CV_16S and CV_32F.")); +} +} //namespace diff --git a/modules/imgproc/test/test_stackblur.cpp b/modules/imgproc/test/test_stackblur.cpp new file mode 100644 index 0000000000..32310d2762 --- /dev/null +++ b/modules/imgproc/test/test_stackblur.cpp @@ -0,0 +1,313 @@ +// 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. + +/* +StackBlur - a fast almost Gaussian Blur +Theory: http://underdestruction.com/2004/02/25/stackblur-2004 +The code has been borrowed from (https://github.com/flozz/StackBlur). + +Below is the original copyright +*/ + +/* +Copyright (c) 2010 Mario Klingemann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +template +void _stackblurRef(const Mat& src, Mat& dst, Size ksize) +{ + CV_Assert(!src.empty()); + CV_Assert(ksize.width > 0 && ksize.height > 0 && ksize.height % 2 == 1 && ksize.width % 2 == 1); + + dst.create(src.size(), src.type()); + const int CN = src.channels(); + + int rowsImg = src.rows; + int colsImg = src.cols; + int wm = colsImg - 1; + + int radiusW = ksize.width / 2; + int stackLenW = ksize.width; + const float mulW = 1.0f / (((float )radiusW + 1.0f) * ((float )radiusW + 1.0f)); + + // Horizontal direction + std::vector stack(stackLenW * CN); + for (int row = 0; row < rowsImg; row++) + { + std::vector sum(CN, 0); + std::vector sumIn(CN, 0); + std::vector sumOut(CN, 0); + + const T* srcPtr = src.ptr(row); + + for (int i = 0; i <= radiusW; i++) + { + for (int ci = 0; ci < CN; ci++) + { + T tmp = *(srcPtr + ci); + stack[i * CN + ci] = tmp; + sum[ci] += tmp * (i + 1); + sumOut[ci] += tmp; + } + } + + for (int i = 1; i <= radiusW; i++) + { + if (i <= wm) srcPtr += CN; + for(int ci = 0; ci < CN; ci++) + { + T tmp = *(srcPtr + ci); + stack[(i + radiusW) * CN + ci] = tmp; + sum[ci] += tmp * (radiusW + 1 - i); + sumIn[ci] += tmp; + } + } + + int sp = radiusW; + int xp = radiusW ; + if (xp > wm) xp = wm; + + T* dstPtr = dst.ptr(row); + srcPtr = src.ptr(row) + xp * CN; + + int stackStart= 0; + + for (int i = 0; i < colsImg; i++) + { + stackStart = sp + stackLenW - radiusW; + + if (stackStart >= stackLenW) stackStart -= stackLenW; + + for(int ci = 0; ci < CN; ci++) + { + *(dstPtr + ci) = cv::saturate_cast(sum[ci] * mulW); + sum[ci] -= sumOut[ci]; + sumOut[ci] -= stack[stackStart*CN + ci]; + } + + const T* srcNew = srcPtr; + + if(xp < wm) + srcNew += CN; + + for (int ci = 0; ci < CN; ci++) + { + stack[stackStart * CN + ci] = *(srcNew + ci); + sumIn[ci] += *(srcNew + ci); + sum[ci] += sumIn[ci]; + } + + int sp1 = sp + 1; + sp1 &= -(sp1 < stackLenW); + + for(int ci = 0; ci < CN; ci++) + { + T tmp = stack[sp1*CN + ci]; + sumOut[ci] += tmp; + sumIn[ci] -= tmp; + } + + dstPtr += CN; + + if (xp < wm) + { + xp++; + srcPtr += CN; + } + + ++sp; + if (sp >= stackLenW) sp = 0; + } + } + + // Vertical direction + int hm = rowsImg - 1; + int widthElem = colsImg * CN; + int radiusH = ksize.height / 2; + int stackLenH = ksize.height; + const float mulH = 1.0f / (((float )radiusH + 1.0f) * ((float )radiusH + 1.0f)); + + stack.resize(stackLenH, 0); + for (int col = 0; col < widthElem; col++) + { + const T* srcPtr =dst.ptr() + col; + float sum0 = 0; + float sumIn0 = 0; + float sumOut0 = 0; + + for (int i = 0; i <= radiusH; i++) + { + T tmp = (T)(*srcPtr); + stack[i] = tmp; + sum0 += tmp * (i + 1); + sumOut0 += tmp; + } + + for (int i = 1; i <= radiusH; i++) + { + if (i <= hm) srcPtr += widthElem; + T tmp = (T)(*srcPtr); + stack[i + radiusH] = tmp; + sum0 += tmp * (radiusH - i + 1); + sumIn0 += tmp; + } + + int sp = radiusH; + int yp = radiusH; + + if (yp > hm) yp = hm; + + T* dstPtr = dst.ptr() + col; + srcPtr = dst.ptr(yp) + col; + + const T* srcNew; + + int stackStart = 0; + + for (int i = 0; i < rowsImg; i++) + { + stackStart = sp + stackLenH - radiusH; + if (stackStart >= stackLenH) stackStart -= stackLenH; + + *(dstPtr) = saturate_cast(sum0 * mulH); + sum0 -= sumOut0; + sumOut0 -= stack[stackStart]; + srcNew = srcPtr; + + if (yp < hm) + srcNew += widthElem; + + stack[stackStart] = *(srcNew); + sumIn0 += *(srcNew); + sum0 += sumIn0; + + int sp1 = sp + 1; + sp1 &= -(sp1 < stackLenH); + + sumOut0 += stack[sp1]; + sumIn0 -= stack[sp1]; + + dstPtr += widthElem; + + if (yp < hm) + { + yp++; + srcPtr += widthElem; + } + + ++sp; + if (sp >= stackLenH) sp = 0; + } + } +} + +void stackBlurRef(const Mat& img, Mat& dst, Size ksize) +{ + if(img.depth() == CV_8U) + _stackblurRef(img, dst, ksize); + else if (img.depth() == CV_16S) + _stackblurRef(img, dst, ksize); + else if (img.depth() == CV_16U) + _stackblurRef(img, dst, ksize); + else if (img.depth() == CV_32F) + _stackblurRef(img, dst, ksize); + else + CV_Error_( CV_StsNotImplemented, + ("Unsupported Mat type in stackBlurRef, " + "the supported formats are: CV_8U, CV_16U, CV_16S and CV_32F.")); +} + +std::vector kernelSizeVec = { + Size(3, 3), + Size(5, 5), + Size(101, 101), + Size(3, 9) + }; + +typedef testing::TestWithParam > StackBlur; + +TEST_P (StackBlur, regression) +{ + Mat img_ = imread(findDataFile("shared/fruits.png"), 1); + const int cn = get<0>(GetParam()); + const int kIndex = get<1>(GetParam()); + const int dtype = get<2>(GetParam()); + + Size ksize = kernelSizeVec[kIndex]; + + Mat img, dstRef, dst; + convert(img_, img, dtype); + + vector channels; + split(img, channels); + channels.push_back(channels[0]); // channels size is 4. + + Mat imgCn; + if (cn == 1) + imgCn = channels[0]; + else if (cn == 4) + merge(channels, imgCn); + else + imgCn = img; + + stackBlurRef(imgCn, dstRef, ksize); + stackBlur(imgCn, dst, ksize); + EXPECT_LE(cvtest::norm(dstRef, dst, NORM_INF), 2.); +} + +INSTANTIATE_TEST_CASE_P(Imgproc, StackBlur, + testing::Combine( + testing::Values(1, 3, 4), + testing::Values(0, 1, 2, 3), + testing::Values(CV_8U, CV_16S, CV_16U, CV_32F) + ) +); + +typedef testing::TestWithParam > StackBlur_GaussianBlur; + +// StackBlur should produce similar results as GaussianBlur output. +TEST_P(StackBlur_GaussianBlur, compare) +{ + Mat img_ = imread(findDataFile("shared/fruits.png"), 1); + const int dtype = get<0>(GetParam()); + + Size ksize(3, 3); + Mat img, dstS, dstG; + convert(img_, img, dtype); + + stackBlur(img, dstS, ksize); + GaussianBlur(img, dstG, ksize, 0); + + EXPECT_LE(cvtest::norm(dstS, dstG, NORM_INF), 13.); +} + +INSTANTIATE_TEST_CASE_P(Imgproc, StackBlur_GaussianBlur, testing::Values(CV_8U, CV_16S, CV_16U, CV_32F)); +} +} From cbf43a54fbd436e9bb48a0813c85c7db1e60108d Mon Sep 17 00:00:00 2001 From: Voron Date: Wed, 28 Sep 2022 12:05:28 +0200 Subject: [PATCH 115/313] added opencv for openvino tutorial --- .../dnn/dnn_android/dnn_android.markdown | 2 +- .../dnn_halide_scheduling.markdown | 2 +- .../dnn/dnn_openvino/dnn_openvino.markdown | 28 +++++++++++++++++++ .../dnn/table_of_content_dnn.markdown | 1 + modules/dnn/include/opencv2/dnn/dnn.hpp | 6 ++-- 5 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown diff --git a/doc/tutorials/dnn/dnn_android/dnn_android.markdown b/doc/tutorials/dnn/dnn_android/dnn_android.markdown index 6ab67b3820..2c81b7ed1d 100644 --- a/doc/tutorials/dnn/dnn_android/dnn_android.markdown +++ b/doc/tutorials/dnn/dnn_android/dnn_android.markdown @@ -2,7 +2,7 @@ @tableofcontents -@prev_tutorial{tutorial_dnn_halide_scheduling} +@prev_tutorial{tutorial_dnn_openvino} @next_tutorial{tutorial_dnn_yolo} | | | diff --git a/doc/tutorials/dnn/dnn_halide_scheduling/dnn_halide_scheduling.markdown b/doc/tutorials/dnn/dnn_halide_scheduling/dnn_halide_scheduling.markdown index 6d2751a467..ec89a26eb8 100644 --- a/doc/tutorials/dnn/dnn_halide_scheduling/dnn_halide_scheduling.markdown +++ b/doc/tutorials/dnn/dnn_halide_scheduling/dnn_halide_scheduling.markdown @@ -3,7 +3,7 @@ @tableofcontents @prev_tutorial{tutorial_dnn_halide} -@next_tutorial{tutorial_dnn_android} +@next_tutorial{tutorial_dnn_openvino} | | | | -: | :- | diff --git a/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown b/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown new file mode 100644 index 0000000000..57c9840386 --- /dev/null +++ b/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown @@ -0,0 +1,28 @@ +OpenCV usage with OpenVINO {#tutorial_dnn_openvino} +===================== + +@prev_tutorial{tutorial_dnn_halide_scheduling} +@next_tutorial{tutorial_dnn_android} + +| | | +| -: | :- | +| Original author | Aleksandr Voron | +| Compatibility | OpenCV == 4.x | + +This tutorial provides OpenCV installation guidelines how to use OpenCV with OpenVINO. + +Since 2021.1.1 release OpenVINO does not provide pre-built OpenCV. +The change does not affect you if you are using OpenVINO runtime directly or OpenVINO samples: it does not have a strong dependency to OpenCV. +However, if you are using Open Model Zoo demos or OpenVINO runtime as OpenCV DNN backend you need to get the OpenCV build. + +There are 2 approaches how to get OpenCV: + +- Install pre-built OpenCV from another sources: system repositories, pip, conda, homebrew. Generic pre-built OpenCV package may have several limitations: + - OpenCV version may be out-of-date + - OpenCV may not contain G-API module with enabled OpenVINO support (e.g. some OMZ demos use G-API functionality) + - OpenCV may not be optimized for modern hardware (default builds need to cover wide range of hardware) + - OpenCV may not support Intel TBB, Intel Media SDK + - OpenCV DNN module may not use OpenVINO as an inference backend +- Build OpenCV from source code against specific version of OpenVINO. This approach solves the limitations mentioned above. + +The instruction how to follow both approaches is provided in [OpenCV wiki](https://github.com/opencv/opencv/wiki/BuildOpenCV4OpenVINO). diff --git a/doc/tutorials/dnn/table_of_content_dnn.markdown b/doc/tutorials/dnn/table_of_content_dnn.markdown index 3f74826dac..e878eb2357 100644 --- a/doc/tutorials/dnn/table_of_content_dnn.markdown +++ b/doc/tutorials/dnn/table_of_content_dnn.markdown @@ -4,6 +4,7 @@ Deep Neural Networks (dnn module) {#tutorial_table_of_content_dnn} - @subpage tutorial_dnn_googlenet - @subpage tutorial_dnn_halide - @subpage tutorial_dnn_halide_scheduling +- @subpage tutorial_dnn_openvino - @subpage tutorial_dnn_android - @subpage tutorial_dnn_yolo - @subpage tutorial_dnn_javascript diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 6f03a8c32e..6ed0d6e70c 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -65,12 +65,12 @@ CV__DNN_INLINE_NS_BEGIN enum Backend { //! DNN_BACKEND_DEFAULT equals to DNN_BACKEND_INFERENCE_ENGINE if - //! OpenCV is built with Intel's Inference Engine library or + //! OpenCV is built with Intel OpenVINO or //! DNN_BACKEND_OPENCV otherwise. DNN_BACKEND_DEFAULT = 0, DNN_BACKEND_HALIDE, - DNN_BACKEND_INFERENCE_ENGINE, //!< Intel's Inference Engine computational backend - //!< @sa setInferenceEngineBackendType + DNN_BACKEND_INFERENCE_ENGINE, //!< Intel OpenVINO computational backend + //!< @note Tutorial how to build OpenCV with OpenVINO: @ref tutorial_dnn_openvino DNN_BACKEND_OPENCV, DNN_BACKEND_VKCOM, DNN_BACKEND_CUDA, From 38c9c20a355c62eb7c0940849c6dc88221c597f5 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 28 Sep 2022 21:57:46 +0200 Subject: [PATCH 116/313] Move marking memory as initialized earlier. --- modules/core/src/hal_internal.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/core/src/hal_internal.cpp b/modules/core/src/hal_internal.cpp index d2648fae81..f581d05cf4 100644 --- a/modules/core/src/hal_internal.cpp +++ b/modules/core/src/hal_internal.cpp @@ -244,6 +244,11 @@ lapack_SVD(fptype* a, size_t a_step, fptype *w, fptype* u, size_t u_step, fptype lwork = (int)round(work1); //optimal buffer size fptype* buffer = new fptype[lwork + 1]; + // Make sure MSAN sees the memory as having been written. + // MSAN does not think it has been written because a different language is called. + // Note: we do this here because if dgesdd is C++, MSAN errors can be reported within it. + CV_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, sizeof(fptype) * (lwork + 1)); + if(typeid(fptype) == typeid(float)) OCV_LAPACK_FUNC(sgesdd)(mode, &m, &n, (float*)a, &lda, (float*)w, (float*)u, &ldu, (float*)vt, &ldv, (float*)buffer, &lwork, iworkBuf, info); else if(typeid(fptype) == typeid(double)) @@ -252,7 +257,6 @@ lapack_SVD(fptype* a, size_t a_step, fptype *w, fptype* u, size_t u_step, fptype // Make sure MSAN sees the memory as having been written. // MSAN does not think it has been written because a different language was called. CV_ANNOTATE_MEMORY_IS_INITIALIZED(a, a_step * n); - CV_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, sizeof(fptype) * (lwork + 1)); if (u) CV_ANNOTATE_MEMORY_IS_INITIALIZED(u, u_step * m); if (vt) From d43cb4fe7c546005bd2d3706aa613ad4d18d1766 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 28 Sep 2022 23:52:24 +0300 Subject: [PATCH 117/313] change resize flag INTER_LINEAR to INTER_LINEAR_EXACT fix python test_detect_and_decode_multi, sort QR in multiDetect/multiDecode enable tests with "version_5_up.jpg", "version_5_top.jpg" --- .../misc/python/test/test_qrcode_detect.py | 12 ++++----- .../objdetect/perf/perf_qrcode_pipeline.cpp | 26 ++++++++++++------- modules/objdetect/src/qrcode.cpp | 14 +++++----- modules/objdetect/test/test_qrcode.cpp | 8 +++--- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/modules/objdetect/misc/python/test/test_qrcode_detect.py b/modules/objdetect/misc/python/test/test_qrcode_detect.py index 8a95c8bce5..0237900572 100644 --- a/modules/objdetect/misc/python/test/test_qrcode_detect.py +++ b/modules/objdetect/misc/python/test/test_qrcode_detect.py @@ -43,10 +43,10 @@ class qrcode_detector_test(NewOpenCVTests): retval, decoded_data, points, straight_qrcode = detector.detectAndDecodeMulti(img) self.assertTrue(retval) self.assertEqual(len(decoded_data), 6) - self.assertEqual(decoded_data[0], "TWO STEPS FORWARD") - self.assertEqual(decoded_data[1], "EXTRA") - self.assertEqual(decoded_data[2], "SKIP") - self.assertEqual(decoded_data[3], "STEP FORWARD") - self.assertEqual(decoded_data[4], "STEP BACK") - self.assertEqual(decoded_data[5], "QUESTION") + self.assertTrue("TWO STEPS FORWARD" in decoded_data) + self.assertTrue("EXTRA" in decoded_data) + self.assertTrue("SKIP" in decoded_data) + self.assertTrue("STEP FORWARD" in decoded_data) + self.assertTrue("STEP BACK" in decoded_data) + self.assertTrue("QUESTION" in decoded_data) self.assertEqual(points.shape, (6, 4, 2)) diff --git a/modules/objdetect/perf/perf_qrcode_pipeline.cpp b/modules/objdetect/perf/perf_qrcode_pipeline.cpp index 9e7960d819..ddee994d2e 100644 --- a/modules/objdetect/perf/perf_qrcode_pipeline.cpp +++ b/modules/objdetect/perf/perf_qrcode_pipeline.cpp @@ -66,6 +66,8 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti) std::vector corners; QRCodeDetector qrcode; TEST_CYCLE() ASSERT_TRUE(qrcode.detectMulti(src, corners)); + sort(corners.begin(), corners.end(), [](const Point2f& corner1, const Point2f& corner2) + {return corner1.x == corner2.x ? corner1.y < corner2.y : corner1.x < corner2.x;}); SANITY_CHECK(corners); } @@ -74,7 +76,6 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) { const std::string name_current_image = GetParam(); const std::string root = "cv/qrcode/multiple/"; - std::string image_path = findDataFile(root + name_current_image); Mat src = imread(image_path); ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; @@ -91,15 +92,22 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) ASSERT_FALSE(decoded_info[i].empty()); } } - std::vector < std::vector< uint8_t > > decoded_info_uint8_t; - for(size_t i = 0; i < decoded_info.size(); i++) - { - std::vector< uint8_t > tmp(decoded_info[i].begin(), decoded_info[i].end()); - decoded_info_uint8_t.push_back(tmp); + ASSERT_EQ(decoded_info.size(), straight_barcode.size()); + vector > result; + for (size_t i = 0ull; i < decoded_info.size(); i++) { + result.push_back(make_pair(decoded_info[i], straight_barcode[i])); } - SANITY_CHECK(decoded_info_uint8_t); - SANITY_CHECK(straight_barcode); - + sort(result.begin(), result.end(), [](const pair& v1, const pair& v2) + {return v1.first < v2.first; }); + vector > decoded_info_sort; + vector straight_barcode_sort; + for (size_t i = 0ull; i < result.size(); i++) { + vector tmp(result[i].first.begin(), result[i].first.end()); + decoded_info_sort.push_back(tmp); + straight_barcode_sort.push_back(result[i].second); + } + SANITY_CHECK(decoded_info_sort); + SANITY_CHECK(straight_barcode_sort); } #endif diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index ef6b47373c..f3e48f6ee0 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -136,7 +136,7 @@ void QRDetect::init(const Mat& src, double eps_vertical_, double eps_horizontal_ const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); } else if (min_side > 512.0) { @@ -524,7 +524,7 @@ bool QRDetect::localization() const int height = cvRound(bin_barcode.size().height * coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); for (size_t i = 0; i < localization_points.size(); i++) { @@ -537,7 +537,7 @@ bool QRDetect::localization() const int height = cvRound(bin_barcode.size().height / coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); for (size_t i = 0; i < localization_points.size(); i++) { @@ -2742,7 +2742,7 @@ void QRDetectMulti::init(const Mat& src, double eps_vertical_, double eps_horizo const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); } else if (min_side > 512.0) { @@ -3099,7 +3099,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector& tmp_localizatio const int height = cvRound(bin_barcode.size().height * coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); } else if (purpose == ZOOMING) @@ -3108,7 +3108,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector& tmp_localizatio const int height = cvRound(bin_barcode.size().height / coeff_expansion); Size new_size(width, height); Mat intermediate; - resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT); bin_barcode = intermediate.clone(); } else @@ -3126,7 +3126,7 @@ void QRDetectMulti::findQRCodeContours(vector& tmp_localization_points, const int width = cvRound(bin_barcode.size().width); const int height = cvRound(bin_barcode.size().height); Size new_size(width, height); - resize(bar, bar, new_size, 0, 0, INTER_LINEAR); + resize(bar, bar, new_size, 0, 0, INTER_LINEAR_EXACT); blur(bar, blur_image, Size(3, 3)); threshold(blur_image, threshold_output, 50, 255, THRESH_BINARY); diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index b5680387cb..4ae894ee59 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -11,7 +11,7 @@ std::string qrcode_images_name[] = { "version_2_down.jpg", "version_2_left.jpg", "version_2_right.jpg", "version_2_up.jpg", "version_2_top.jpg", "version_3_down.jpg", "version_3_left.jpg", "version_3_right.jpg", "version_3_up.jpg", "version_3_top.jpg", "version_4_down.jpg", "version_4_left.jpg", "version_4_right.jpg", "version_4_up.jpg", "version_4_top.jpg", - "version_5_down.jpg", "version_5_left.jpg"/*"version_5_right.jpg"*/, + "version_5_down.jpg", "version_5_left.jpg", /*"version_5_right.jpg",*/ "version_5_up.jpg", "version_5_top.jpg", "russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg" // version_5_right.jpg DISABLED after tile fix, PR #22025 }; @@ -87,7 +87,7 @@ TEST(Objdetect_QRCode_Close, generate_test_data) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); #ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); @@ -125,7 +125,7 @@ TEST(Objdetect_QRCode_Monitor, generate_test_data) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); #ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); @@ -380,7 +380,7 @@ TEST_P(Objdetect_QRCode_Monitor, regression) const int width = cvRound(src.size().width * coeff_expansion); const int height = cvRound(src.size().height * coeff_expansion); Size new_size(width, height); - resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); std::vector corners; std::string decoded_info; QRCodeDetector qrcode; From 15cfafb3606be78ac31d05f4a54766c8f23c4ebb Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Thu, 29 Sep 2022 10:53:43 +0800 Subject: [PATCH 118/313] DNN: Remove unused code in onnx_importer.cpp --- modules/dnn/src/onnx/onnx_importer.cpp | 27 -------------------------- 1 file changed, 27 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 0b104d1e63..7de2fb9c79 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -85,7 +85,6 @@ class ONNXImporter void expandMid(const std::string& prefix, opencv_onnx::NodeProto& node_proto, const std::string& input, size_t n); - void addNegation(const LayerParams& layerParams, opencv_onnx::NodeProto& node_proto, int input_id); void lstm_extractConsts(LayerParams& layerParams, const opencv_onnx::NodeProto& lstm_proto, size_t idx, int* blobShape_, int size); void lstm_add_reshape(const std::string& input_name, const std::string& output_name, int* layerShape, size_t n); std::string lstm_add_slice(int index, const std::string& input_name, int* begin, int* end, size_t n); @@ -671,32 +670,6 @@ void ONNXImporter::expandMid(const std::string& prefix, opencv_onnx::NodeProto& } } -/** @brief Multiply one of node_proto inputs by -1 - * @param layerParams parameters of the node - * @param node_proto node which input will be replaced - * @param input_id id of input to be multiplied by -1 - */ -void ONNXImporter::addNegation(const LayerParams& layerParams, opencv_onnx::NodeProto& node_proto, int input_id) -{ - LayerParams powerParams; - powerParams.name = layerParams.name + "/neg"; - powerParams.type = "Power"; - powerParams.set("scale", -1.f); - - //Create Power layer - int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams); - //Connect to input - IterLayerId_t layerId = layer_id.find(node_proto.input(input_id)); - CV_Assert(layerId != layer_id.end()); - dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0); - //Add shape - layer_id.insert(std::make_pair(powerParams.name, LayerInfo(id, 0))); - outShapes[powerParams.name] = outShapes[node_proto.input(input_id)]; - - //Replace input to Power - node_proto.set_input(input_id, powerParams.name); -} - void ONNXImporter::addConstant(const std::string& name, const Mat& blob) { CV_LOG_DEBUG(NULL, "DNN/ONNX: add constant '" << name << "' shape=" << toString(shape(blob)) << ": " << toString(blob)); From 784dd55d88a11652be9f55b1351f046fad3b4ac3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 12 Sep 2022 17:13:43 +0300 Subject: [PATCH 119/313] Extracted matches_confindece_thresh as stitching matcher parameter. --- .../include/opencv2/stitching/detail/matchers.hpp | 7 +++++-- modules/stitching/src/matchers.cpp | 12 +++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp index 1b7d7d6897..1c19a80721 100644 --- a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp @@ -180,19 +180,22 @@ public: estimation used in the inliers classification step @param num_matches_thresh2 Minimum number of matches required for the 2D projective transform re-estimation on inliers + @param matches_confindece_thresh Matching confidence threshold to take the match into account. + The threshold was determined experimentally and set to 3 by default. */ CV_WRAP BestOf2NearestMatcher(bool try_use_gpu = false, float match_conf = 0.3f, int num_matches_thresh1 = 6, - int num_matches_thresh2 = 6); + int num_matches_thresh2 = 6, double matches_confindece_thresh = 3.); CV_WRAP void collectGarbage() CV_OVERRIDE; CV_WRAP static Ptr create(bool try_use_gpu = false, float match_conf = 0.3f, int num_matches_thresh1 = 6, - int num_matches_thresh2 = 6); + int num_matches_thresh2 = 6, double matches_confindece_thresh = 3.); protected: void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo &matches_info) CV_OVERRIDE; int num_matches_thresh1_; int num_matches_thresh2_; + double matches_confindece_thresh_; Ptr impl_; }; diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index 4791584366..07dc6b5a39 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -365,7 +365,7 @@ void FeaturesMatcher::operator ()(const std::vector &features, st ////////////////////////////////////////////////////////////////////////////// -BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2) +BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2, double matches_confindece_thresh) { CV_UNUSED(try_use_gpu); @@ -383,11 +383,13 @@ BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, is_thread_safe_ = impl_->isThreadSafe(); num_matches_thresh1_ = num_matches_thresh1; num_matches_thresh2_ = num_matches_thresh2; + matches_confindece_thresh_ = matches_confindece_thresh; } -Ptr BestOf2NearestMatcher::create(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2) +Ptr BestOf2NearestMatcher::create(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2, + double matches_confindece_thresh) { - return makePtr(try_use_gpu, match_conf, num_matches_thresh1, num_matches_thresh2); + return makePtr(try_use_gpu, match_conf, num_matches_thresh1, num_matches_thresh2, matches_confindece_thresh); } @@ -437,8 +439,8 @@ void BestOf2NearestMatcher::match(const ImageFeatures &features1, const ImageFea matches_info.confidence = matches_info.num_inliers / (8 + 0.3 * matches_info.matches.size()); // Set zero confidence to remove matches between too close images, as they don't provide - // additional information anyway. The threshold was set experimentally. - matches_info.confidence = matches_info.confidence > 3. ? 0. : matches_info.confidence; + // additional information anyway. + matches_info.confidence = matches_info.confidence > matches_confindece_thresh_ ? 0. : matches_info.confidence; // Check if we should try to refine motion if (matches_info.num_inliers < num_matches_thresh2_) From d18362c7269ee23fc4875be83b12163447be5929 Mon Sep 17 00:00:00 2001 From: ocpalo Date: Thu, 29 Sep 2022 20:47:49 +0300 Subject: [PATCH 120/313] fix warnings in ImageCollection --- modules/imgcodecs/src/loadsave.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 3a875ea2b7..daad280fec 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -668,7 +668,7 @@ size_t imcount_(const String& filename, int flags) return collection.size(); } catch(cv::Exception const& e) { // Reading header or finding decoder for the filename is failed - return 0; + std::cerr << "imcount_('" << filename << "'): can't read header or can't find decoder: " << e.what() << std::endl << std::flush; } return 0; } @@ -1141,7 +1141,7 @@ int ImageCollection::Impl::currentIndex() const { return m_current; } ImageCollection::iterator ImageCollection::Impl::begin(ImageCollection* ptr) { return ImageCollection::iterator(ptr); } -ImageCollection::iterator ImageCollection::Impl::end(ImageCollection* ptr) { return ImageCollection::iterator(ptr, this->size()); } +ImageCollection::iterator ImageCollection::Impl::end(ImageCollection* ptr) { return ImageCollection::iterator(ptr, static_cast(this->size())); } void ImageCollection::Impl::reset() { m_current = 0; From 8baf46c0a80cec2790ec05fc560bfcbda06df3e0 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:10:59 +0300 Subject: [PATCH 121/313] Add bindings and test --- modules/photo/include/opencv2/photo/cuda.hpp | 43 ++++++++++++++++---- modules/python/test/test_cuda.py | 5 +++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/modules/photo/include/opencv2/photo/cuda.hpp b/modules/photo/include/opencv2/photo/cuda.hpp index a2f38167e6..b6ab40a764 100644 --- a/modules/photo/include/opencv2/photo/cuda.hpp +++ b/modules/photo/include/opencv2/photo/cuda.hpp @@ -65,11 +65,20 @@ BORDER_REPLICATE , BORDER_CONSTANT , BORDER_REFLECT and BORDER_WRAP are supporte fastNlMeansDenoising */ CV_EXPORTS void nonLocalMeans(InputArray src, OutputArray dst, - float h, - int search_window = 21, - int block_size = 7, - int borderMode = BORDER_DEFAULT, - Stream& stream = Stream::Null()); + float h, + int search_window = 21, + int block_size = 7, + int borderMode = BORDER_DEFAULT, + Stream& stream = Stream::Null()); +CV_WRAP inline void nonLocalMeans(const GpuMat& src, CV_OUT GpuMat& dst, + float h, + int search_window = 21, + int block_size = 7, + int borderMode = BORDER_DEFAULT, + Stream& stream = Stream::Null()) +{ + nonLocalMeans(InputArray(src), OutputArray(dst), h, search_window, block_size, borderMode, stream); +}; /** @brief Perform image denoising using Non-local Means Denoising algorithm with several computational @@ -93,10 +102,18 @@ FastNonLocalMeansDenoising::labMethod. fastNlMeansDenoising */ CV_EXPORTS void fastNlMeansDenoising(InputArray src, OutputArray dst, - float h, - int search_window = 21, - int block_size = 7, - Stream& stream = Stream::Null()); + float h, + int search_window = 21, + int block_size = 7, + Stream& stream = Stream::Null()); +CV_WRAP inline void fastNlMeansDenoising(const GpuMat& src, CV_OUT GpuMat& dst, + float h, + int search_window = 21, + int block_size = 7, + Stream& stream = Stream::Null()) +{ + fastNlMeansDenoising(InputArray(src), OutputArray(dst), h, search_window, block_size, stream); +} /** @brief Modification of fastNlMeansDenoising function for colored images @@ -124,6 +141,14 @@ CV_EXPORTS void fastNlMeansDenoisingColored(InputArray src, OutputArray dst, int search_window = 21, int block_size = 7, Stream& stream = Stream::Null()); +CV_WRAP inline void fastNlMeansDenoisingColored(const GpuMat& src, CV_OUT GpuMat& dst, + float h_luminance, float photo_render, + int search_window = 21, + int block_size = 7, + Stream& stream = Stream::Null()) +{ + fastNlMeansDenoisingColored(InputArray(src), OutputArray(dst), h_luminance, photo_render, search_window, block_size, stream); +} //! @} photo diff --git a/modules/python/test/test_cuda.py b/modules/python/test/test_cuda.py index a5f3fae847..586c0ecf81 100644 --- a/modules/python/test/test_cuda.py +++ b/modules/python/test/test_cuda.py @@ -64,5 +64,10 @@ class cuda_test(NewOpenCVTests): self.assertTrue(cuMat.step == 0) self.assertTrue(cuMat.size() == (0, 0)) + def test_cuda_denoising(self): + self.assertEqual(True, hasattr(cv.cuda, 'fastNlMeansDenoising')) + self.assertEqual(True, hasattr(cv.cuda, 'fastNlMeansDenoisingColored')) + self.assertEqual(True, hasattr(cv.cuda, 'nonLocalMeans')) + if __name__ == '__main__': NewOpenCVTests.bootstrap() From 4557971481bd153428660aedd38222f369a8c4d8 Mon Sep 17 00:00:00 2001 From: zoom Date: Thu, 22 Sep 2022 14:40:39 +0800 Subject: [PATCH 122/313] enhance slice layer refactor the code for parsing Slice layer add test for Slice layer let 'begin' and 'end' resize to dims add opset message comment --- modules/dnn/src/onnx/onnx_importer.cpp | 118 ++++++++++++++---------- modules/dnn/test/test_onnx_importer.cpp | 14 +++ 2 files changed, 83 insertions(+), 49 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 0b104d1e63..4a1ebdbb58 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -1326,72 +1326,59 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - int axis = 0; - std::vector begin; - std::vector end; + MatShape inpShape = outShapes[node_proto.input(0)]; + int dims = inpShape.size(); + std::vector begin(dims, 0); + std::vector end(dims, INT_MAX); std::vector steps; int inp_size = node_proto.input_size(); + int axis = 0; + bool has_axes = false; + DictValue starts_, ends_, axes_, steps_; + // opset = 1 if (inp_size == 1) { - if (layerParams.has("axes")) { - DictValue axes = layerParams.get("axes"); - for (int i = 1; i < axes.size(); ++i) { - CV_Assert(axes.get(i - 1) == axes.get(i) - 1); - } - axis = axes.get(0); - } - - DictValue starts = layerParams.get("starts"); - DictValue ends = layerParams.get("ends"); - CV_Assert(starts.size() == ends.size()); - - if (axis > 0) { - CV_CheckLE(axis, 1024, "Slice layer can't have more than 1024 axes"); // arbitrary limit - begin.resize(axis, 0); - end.resize(axis, INT_MAX); - } - for (int i = 0; i < starts.size(); ++i) + starts_ = layerParams.get("starts"); + ends_ = layerParams.get("ends"); + CV_Assert(starts_.size() == ends_.size()); + if (layerParams.has("axes")) { - begin.push_back(starts.get(i)); - end.push_back(ends.get(i)); + axes_ = layerParams.get("axes"); + CV_Assert(axes_.size() == starts_.size()); + axis = axes_.getIntValue(0) < 0 ? axes_.getIntValue(0) + dims : axes_.getIntValue(0); + has_axes = true; } - } else { // inp_size > 1 + } + // opset > 1 + else + { CV_Assert(inp_size >= 3); - for (int i = 1; i < inp_size; i++) { + for (int i = 1; i < inp_size; ++i) + { CV_Assert(constBlobs.find(node_proto.input(i)) != constBlobs.end()); } Mat start_blob = getBlob(node_proto, 1); - Mat end_blob = getBlob(node_proto, 2); + Mat end_blob = getBlob(node_proto, 2); CV_Assert(start_blob.total() == end_blob.total()); + starts_ = DictValue::arrayInt(start_blob.begin(), start_blob.total()); + ends_ = DictValue::arrayInt(end_blob.begin(), end_blob.total()); - if (inp_size > 3) { + if (inp_size > 3) + { Mat axes_blob = getBlob(node_proto, 3); - const int* axes = (int*)axes_blob.data; - for (int i = 1; i < axes_blob.total(); ++i) { - CV_Assert(axes[i - 1] == axes[i] - 1); - } - axis = axes[0]; + CV_Assert(axes_blob.total() == start_blob.total()); + axes_ = DictValue::arrayInt(axes_blob.begin(), axes_blob.total()); + axis = axes_.getIntValue(0) < 0 ? axes_.getIntValue(0) + dims : axes_.getIntValue(0); + has_axes = true; } - const int* starts = start_blob.ptr(); - const int* ends = end_blob.ptr(); - if (axis > 0) { - begin.resize(axis, 0); - end.resize(axis, INT_MAX); - } - std::copy(starts, starts + start_blob.total(), std::back_inserter(begin)); - std::copy(ends, ends + end_blob.total(), std::back_inserter(end)); - - if (inp_size == 5) { - CV_Assert(constBlobs.find(node_proto.input(4)) != constBlobs.end()); + if (inp_size == 5) + { Mat step_blob = getBlob(node_proto, 4); - const int* steps_ptr = step_blob.ptr(); - - if (axis > 0) - steps.resize(axis, 1); - - std::copy(steps_ptr, steps_ptr + step_blob.total(), std::back_inserter(steps)); + CV_Assert(step_blob.total() == start_blob.total()); + steps_ = DictValue::arrayInt(step_blob.begin(), step_blob.total()); + steps.resize(dims, 1); // Very strange application for Slice op with tensor reversing. // We just workaround it for 2d constants. @@ -1411,12 +1398,45 @@ void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeP } } } + + if (!has_axes) + { + // make a default axes [0, 1, 2...] + Mat axes_tmp(1, starts_.size(), CV_32S); + std::iota(axes_tmp.begin(), axes_tmp.end(), 0); + axes_ = DictValue::arrayInt(axes_tmp.begin(), axes_tmp.total()); + } + + int cur_axe; + std::vector flag(dims, false); + Mat axes(1, starts_.size(), CV_32S); + auto axes_ptr = axes.ptr(); + // resize begin and end + for (int i = 0; i < axes_.size(); ++i) + { + // dims should be added to the negative axes + cur_axe = axes_.getIntValue(i) < 0 ? axes_.getIntValue(i) + dims : axes_.getIntValue(i); + CV_CheckGE(cur_axe, 0, "Axes should be grater or equal to '-dims'."); + CV_CheckLT(cur_axe, dims, "Axes should be less than 'dim'."); + CV_CheckEQ(flag[cur_axe], false, "Axes shouldn't have duplicated values."); + flag[cur_axe] = true; + // change axis to the minimum axe + if (cur_axe < axis) axis = cur_axe; + axes_ptr[i] = cur_axe; + begin[cur_axe] = starts_.getIntValue(i); + end[cur_axe] = ends_.getIntValue(i); + } + layerParams.set("begin", DictValue::arrayInt(&begin[0], begin.size())); layerParams.set("end", DictValue::arrayInt(&end[0], end.size())); layerParams.set("axis", axis); if (!steps.empty()) + { + for (int i = 0; i < axes.total(); ++i) + steps[axes_ptr[i]] = steps_.getIntValue(i); layerParams.set("steps", DictValue::arrayInt(&steps[0], steps.size())); + } if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) { diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 8090eba904..4eb6b5a91b 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1172,6 +1172,20 @@ TEST_P(Test_ONNX_layers, Slice_Steps_5DInput) testONNXModels("slice_opset_11_steps_5d"); } +TEST_P(Test_ONNX_layers, Slice_Nonseq_Axes) +{ + testONNXModels("slice_nonseq_axes"); + testONNXModels("slice_nonseq_axes_steps"); + testONNXModels("slice_nonseq_miss_axes_steps"); +} + +TEST_P(Test_ONNX_layers, Slice_Neg_Axes) +{ + testONNXModels("slice_neg_axes"); + testONNXModels("slice_neg_axes_steps"); + testONNXModels("slice_neg_miss_axes_steps"); +} + TEST_P(Test_ONNX_layers, Softmax) { testONNXModels("softmax"); From 692c536ac5b683e30cb850fbeb8f8f206abfad9b Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Fri, 23 Sep 2022 21:13:13 +0800 Subject: [PATCH 123/313] enable issue template chooser with templates for bug report, feature request and documentation --- .github/ISSUE_TEMPLATE/bug_report.yml | 64 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/documentation.yml | 26 +++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 22 ++++++++ 4 files changed, 117 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..dd53e9508c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,64 @@ +name: Bug Report +description: Create a report to help us reproduce and fix the bug +labels: ["bug"] + +body: +- type: markdown + attributes: + value: > + #### Thank you for contributing! Before reporting a bug, please have a look at the [FAQ](https://github.com/opencv/opencv/wiki/FAQ), make sure the issue has no duplicate and hasn't been already addressed by searching through [the existing and past issues](https://github.com/opencv/opencv/issues?page=1&q=is%3Aissue+sort%3Acreated-desc). + +- type: textarea + attributes: + label: System Information + description: | + Please provide the following system information to help us diagnose the bug. For example: + + // example for c++ user + OpenCV version: 4.6.0 + Operating System / Platform: Ubuntu 20.04 + Compiler & compiler version: GCC 9.3.0 + + // example for python user + OpenCV python version: 4.6.0.66 + Operating System / Platform: Ubuntu 20.04 + Python version: 3.9.6 + validations: + required: true +- type: textarea + attributes: + label: Detailed description + description: | + Please provide a clear and concise description of what the bug is and paste the error log below. It helps improving readability if the error log is wrapped in ```` ```triple quotes blocks``` ````. + placeholder: | + A clear and concise description of what the bug is. + + ``` + # error log + ``` + validations: + required: true +- type: textarea + attributes: + label: Steps to reproduce + description: | + Please provide a minimal example to help us reproduce the bug. Code should be wrapped with ```` ```triple quotes blocks``` ```` to improve readability. If the code is too long, please attach as a file or create and link a public gist: https://gist.github.com. + + Related data files (images, onnx, etc) should be attached below as well. If the data files are too big, feel free to upload them to a online drive, share them and put the link below. + placeholder: | + ```cpp (replace cpp with python if python code) + # sample code to reproduce the bug + ``` + + Test data: [image](https://link/to/the/image), [model.onnx](htts://link/to/the/onnx/model) + validations: + required: true +- type: checkboxes + attributes: + label: Issue submission checklist + options: + - label: I report the issue, it's not a question + required: true + - label: I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution + - label: I updated to the latest OpenCV version and the issue is still there + - label: There is reproducer code and related data files (videos, images, onnx, etc) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..56200aa0a3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Questions + url: https://forum.opencv.org/ + about: Ask questions and discuss with OpenCV community members diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 0000000000..62d105ea69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,26 @@ +name: Documentation +description: Report an issue related to https://docs.opencv.org/ +labels: ["category: documentation"] + +body: +- type: markdown + attributes: + value: > + #### Thank you for contributing! Before submitting a doc issue, please make sure it has no duplicate by searching through [the existing and past issues](https://github.com/opencv/opencv/issues?page=1&q=is%3Aissue+sort%3Acreated-desc) + +- type: textarea + attributes: + label: Descripe the doc issue + description: > + Please provide a clear and concise description of what content in https://docs.opencv.org/ is an issue. Note that there are multiple active branches, such as 3.4, 4.x and 5.x, so please specify the branch with the problem. + placeholder: | + A clear and concise description of what content in https://docs.opencv.org/ is an issue. + + Link to the doc: https://docs.opencv.org/4.x/d3/d63/classcv_1_1Mat.html + validations: + required: true +- type: textarea + attributes: + label: Fix suggestion + description: > + Tell us how we could improve the documentation in this regard. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..ded0d0d025 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,22 @@ +name: Feature request +description: Submit a request for a new OpenCV feature +labels: ["feature"] + +body: +- type: markdown + attributes: + value: > + #### Thank you for contributing! Before submitting a feature request, please make sure the request has no duplicate by searching through [the existing and past issues](https://github.com/opencv/opencv/issues?page=1&q=is%3Aissue+sort%3Acreated-desc) + +- type: textarea + attributes: + label: Descripe the feature and motivation + description: | + Please provide a clear and concise proposal of the feature and outline the motivation. + validations: + required: true +- type: textarea + attributes: + label: Additional context + description: | + Add any other context, such as pseudo code, links, diagram, screenshots, to help the community better understand the feature request. From 6cf09108425a512b64b5c1fea8444a7e7d3be04a Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 3 Oct 2022 11:07:36 +0530 Subject: [PATCH 124/313] Merge pull request #22462 from Biswa96:fix-directx-check * cmake: Fix DirectX detection in mingw The pragma comment directive is valid for MSVC only. So, the DirectX detection fails in mingw. The failure is fixed by adding the required linking library (here d3d11) in the try_compile() function in OpenCVDetectDirectX.cmake file. Also add a message if the first DirectX check fails. * gapi: Fix compilation with mingw These changes remove MSVC specific pragma directive. The compilation fails at linking time due to absence of proper linking library. The required libraries are added in corresponding CMakeLists.txt file. * samples: Fix compilation with mingw These changes remove MSVC specific pragma directive. The compilation fails at linking time due to absence of proper linking library. The required libraries are added in corresponding CMakeLists.txt file. --- cmake/OpenCVDetectDirectX.cmake | 3 +++ cmake/checks/directx.cpp | 1 - modules/gapi/CMakeLists.txt | 9 +++++++++ modules/gapi/samples/onevpl_infer_single_roi.cpp | 2 -- .../onevpl/accelerators/accel_policy_dx11.cpp | 1 - .../onevpl/accelerators/dx11_alloc_resource.hpp | 1 - .../streaming/onevpl/cfg_param_device_selector.cpp | 2 -- .../onevpl/demux/async_mfp_demux_data_provider.cpp | 8 -------- samples/directx/CMakeLists.txt | 12 ++++++++++++ samples/directx/d3d10_interop.cpp | 1 - samples/directx/d3d11_interop.cpp | 1 - samples/directx/d3d9_interop.cpp | 1 - samples/directx/d3d9ex_interop.cpp | 1 - samples/opengl/opengl_interop.cpp | 6 ------ 14 files changed, 24 insertions(+), 25 deletions(-) diff --git a/cmake/OpenCVDetectDirectX.cmake b/cmake/OpenCVDetectDirectX.cmake index fbe4a71185..fa1bac235e 100644 --- a/cmake/OpenCVDetectDirectX.cmake +++ b/cmake/OpenCVDetectDirectX.cmake @@ -2,15 +2,18 @@ if(WIN32) try_compile(__VALID_DIRECTX "${OpenCV_BINARY_DIR}" "${OpenCV_SOURCE_DIR}/cmake/checks/directx.cpp" + LINK_LIBRARIES d3d11 OUTPUT_VARIABLE TRY_OUT ) if(NOT __VALID_DIRECTX) + message(STATUS "No support for DirectX (install Windows 8 SDK)") return() endif() try_compile(__VALID_DIRECTX_NV12 "${OpenCV_BINARY_DIR}" "${OpenCV_SOURCE_DIR}/cmake/checks/directx.cpp" COMPILE_DEFINITIONS "-DCHECK_NV12" + LINK_LIBRARIES d3d11 OUTPUT_VARIABLE TRY_OUT ) if(__VALID_DIRECTX_NV12) diff --git a/cmake/checks/directx.cpp b/cmake/checks/directx.cpp index c617ac341a..be687c24f8 100644 --- a/cmake/checks/directx.cpp +++ b/cmake/checks/directx.cpp @@ -1,7 +1,6 @@ #include #include -#pragma comment (lib, "d3d11.lib") HINSTANCE g_hInst = NULL; D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index edf4e588e0..711fdd1933 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -317,6 +317,12 @@ if(HAVE_GAPI_ONEVPL) ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_ONEVPL) ocv_target_link_libraries(${the_module} PRIVATE ${VPL_IMPORTED_TARGETS}) + if(HAVE_DIRECTX AND HAVE_D3D11) + ocv_target_link_libraries(${the_module} PRIVATE d3d11 dxgi) + endif() + if(WIN32) + ocv_target_link_libraries(${the_module} PRIVATE mf mfuuid mfplat shlwapi mfreadwrite) + endif() if(HAVE_D3D11 AND HAVE_OPENCL) ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() @@ -369,6 +375,9 @@ if(TARGET example_gapi_onevpl_infer_single_roi) if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE ocv.3rdparty.openvino) endif() + if(HAVE_DIRECTX AND HAVE_D3D11) + ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE d3d11 dxgi) + endif() if(HAVE_D3D11 AND HAVE_OPENCL) ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() diff --git a/modules/gapi/samples/onevpl_infer_single_roi.cpp b/modules/gapi/samples/onevpl_infer_single_roi.cpp index 811008b168..de1d233ae5 100644 --- a/modules/gapi/samples/onevpl_infer_single_roi.cpp +++ b/modules/gapi/samples/onevpl_infer_single_roi.cpp @@ -20,13 +20,11 @@ #ifdef HAVE_DIRECTX #ifdef HAVE_D3D11 -#pragma comment(lib,"d3d11.lib") // get rid of generate macro max/min/etc from DX side #define D3D11_NO_HELPERS #define NOMINMAX #include -#pragma comment(lib, "dxgi") #undef NOMINMAX #undef D3D11_NO_HELPERS #endif // HAVE_D3D11 diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp index dba05f0169..456e01d676 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp @@ -14,7 +14,6 @@ #include "logger.hpp" #if defined(HAVE_DIRECTX) && defined(HAVE_D3D11) -#pragma comment(lib,"d3d11.lib") #define D3D11_NO_HELPERS #include diff --git a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp index c68a08a3f8..082e3b5291 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp @@ -13,7 +13,6 @@ #ifdef HAVE_DIRECTX #ifdef HAVE_D3D11 -#pragma comment(lib,"d3d11.lib") #define D3D11_NO_HELPERS #define NOMINMAX diff --git a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp index 62623b94f4..d43b9068f3 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp @@ -17,14 +17,12 @@ #ifdef HAVE_DIRECTX #ifdef HAVE_D3D11 -#pragma comment(lib,"d3d11.lib") // get rid of generate macro max/min/etc from DX side #define D3D11_NO_HELPERS #define NOMINMAX #include #include -#pragma comment(lib, "dxgi") #undef D3D11_NO_HELPERS #undef NOMINMAX #endif // HAVE_D3D11 diff --git a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp index 5d139af824..108f8258ab 100644 --- a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp +++ b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp @@ -5,14 +5,6 @@ // Copyright (C) 2021 Intel Corporation #ifdef HAVE_ONEVPL #include -#ifdef _WIN32 - -#pragma comment(lib, "Mf.lib") -#pragma comment(lib, "Mfuuid.lib") -#pragma comment(lib, "Mfplat.lib") -#pragma comment(lib, "shlwapi.lib") -#pragma comment(lib, "mfreadwrite.lib") -#endif // _WIN32 #include #include "streaming/onevpl/demux/async_mfp_demux_data_provider.hpp" diff --git a/samples/directx/CMakeLists.txt b/samples/directx/CMakeLists.txt index 3041e27381..c74c7d4b31 100644 --- a/samples/directx/CMakeLists.txt +++ b/samples/directx/CMakeLists.txt @@ -19,4 +19,16 @@ foreach(sample_filename ${all_samples}) ocv_define_sample(tgt ${sample_filename} directx) ocv_target_link_libraries(${tgt} PRIVATE ${OPENCV_LINKER_LIBS} ${OPENCV_DIRECTX_SAMPLES_REQUIRED_DEPS}) ocv_target_link_libraries(${tgt} PRIVATE "gdi32") + if(sample_filename STREQUAL "d3d9_interop.cpp") + ocv_target_link_libraries(${tgt} PRIVATE d3d9) + endif() + if(sample_filename STREQUAL "d3d9ex_interop.cpp") + ocv_target_link_libraries(${tgt} PRIVATE d3d9) + endif() + if(sample_filename STREQUAL "d3d10_interop.cpp") + ocv_target_link_libraries(${tgt} PRIVATE d3d10) + endif() + if(sample_filename STREQUAL "d3d11_interop.cpp") + ocv_target_link_libraries(${tgt} PRIVATE d3d11) + endif() endforeach() diff --git a/samples/directx/d3d10_interop.cpp b/samples/directx/d3d10_interop.cpp index e8be8fac50..442a3f0c53 100644 --- a/samples/directx/d3d10_interop.cpp +++ b/samples/directx/d3d10_interop.cpp @@ -17,7 +17,6 @@ #include "d3dsample.hpp" -#pragma comment (lib, "d3d10.lib") class D3D10WinApp : public D3DSample { diff --git a/samples/directx/d3d11_interop.cpp b/samples/directx/d3d11_interop.cpp index df40dd3e89..6c55a0c48b 100644 --- a/samples/directx/d3d11_interop.cpp +++ b/samples/directx/d3d11_interop.cpp @@ -17,7 +17,6 @@ #include "d3dsample.hpp" -#pragma comment (lib, "d3d11.lib") class D3D11WinApp : public D3DSample { diff --git a/samples/directx/d3d9_interop.cpp b/samples/directx/d3d9_interop.cpp index c46cf8e9e8..8c308721e0 100644 --- a/samples/directx/d3d9_interop.cpp +++ b/samples/directx/d3d9_interop.cpp @@ -17,7 +17,6 @@ #include "d3dsample.hpp" -#pragma comment (lib, "d3d9.lib") class D3D9WinApp : public D3DSample diff --git a/samples/directx/d3d9ex_interop.cpp b/samples/directx/d3d9ex_interop.cpp index 68b864f0eb..39dda84b66 100644 --- a/samples/directx/d3d9ex_interop.cpp +++ b/samples/directx/d3d9ex_interop.cpp @@ -17,7 +17,6 @@ #include "d3dsample.hpp" -#pragma comment (lib, "d3d9.lib") class D3D9ExWinApp : public D3DSample diff --git a/samples/opengl/opengl_interop.cpp b/samples/opengl/opengl_interop.cpp index 7fbf4d6084..8d3e0f8d47 100644 --- a/samples/opengl/opengl_interop.cpp +++ b/samples/opengl/opengl_interop.cpp @@ -27,12 +27,6 @@ #include "winapp.hpp" -#if defined(_WIN32) -# pragma comment(lib, "opengl32.lib") -# pragma comment(lib, "glu32.lib") -#endif - - class GLWinApp : public WinApp { public: From b0b77b3047356b2bd507d55ccd3a661b54d5a150 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Sun, 2 Oct 2022 20:54:08 +0000 Subject: [PATCH 125/313] Add cfgOutputPrecision --- .../gapi/include/opencv2/gapi/infer/ie.hpp | 51 ++++++++- .../gapi/samples/pipeline_modeling_tool.cpp | 6 + .../pipeline_builder.hpp | 4 + modules/gapi/src/backends/ie/giebackend.cpp | 49 +++++++- .../gapi/test/infer/gapi_infer_ie_test.cpp | 105 ++++++++++++++++++ 5 files changed, 212 insertions(+), 3 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/infer/ie.hpp b/modules/gapi/include/opencv2/gapi/infer/ie.hpp index 204bd8f266..1e8fbc576c 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ie.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ie.hpp @@ -88,6 +88,15 @@ struct ParamDesc { cv::optional vpl_preproc_device; cv::optional vpl_preproc_ctx; + + using precision_t = int; + using precision_map_t = std::unordered_map; + // NB: cv::util::monostate is default value that means precision wasn't specified. + using precision_variant_t = cv::util::variant; + precision_variant_t output_precision; + }; } // namespace detail @@ -132,6 +141,7 @@ public: , {} , {} , {} + , {} , {}} { }; @@ -156,6 +166,7 @@ public: , {} , {} , {} + , {} , {}} { }; @@ -351,6 +362,29 @@ public: return *this; } + /** @brief Specifies the output precision for model. + + The function is used to set an output precision for model. + + @param precision Precision in OpenCV format. + @return reference to this parameter structure. + */ + Params& cfgOutputPrecision(detail::ParamDesc::precision_t precision) { + desc.output_precision = precision; + return *this; + } + + /** @overload + + @param precision_map Map of pairs: name of corresponding output layer and its precision + @return reference to this parameter structure. + */ + Params& + cfgOutputPrecision(detail::ParamDesc::precision_map_t precision_map) { + desc.output_precision = precision_map; + return *this; + } + // BEGIN(G-API's network parametrization API) GBackend backend() const { return cv::gapi::ie::backend(); } std::string tag() const { return Net::tag(); } @@ -385,7 +419,7 @@ public: const std::string &device) : desc{ model, weights, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u, - {}, {}, {}, {}}, + {}, {}, {}, {}, {}}, m_tag(tag) { }; @@ -403,7 +437,7 @@ public: const std::string &device) : desc{ model, {}, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u, - {}, {}, {}, {}}, + {}, {}, {}, {}, {}}, m_tag(tag) { }; @@ -476,6 +510,19 @@ public: return *this; } + /** @see ie::Params::cfgOutputPrecision */ + Params& cfgOutputPrecision(detail::ParamDesc::precision_t precision) { + desc.output_precision = precision; + return *this; + } + + /** @overload */ + Params& + cfgOutputPrecision(detail::ParamDesc::precision_map_t precision_map) { + desc.output_precision = precision_map; + return *this; + } + // BEGIN(G-API's network parametrization API) GBackend backend() const { return cv::gapi::ie::backend(); } std::string tag() const { return m_tag; } diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index 60547a9c9b..540c8d7766 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -210,6 +210,12 @@ InferParams read(const cv::FileNode& fn) { params.input_layers = readList(fn, "input_layers", name); params.output_layers = readList(fn, "output_layers", name); params.config = readMap(fn["config"]); + + auto out_prec_str = readOpt(fn["output_precision"]); + if (out_prec_str.has_value()) { + params.out_precision = + cv::optional(strToPrecision(out_prec_str.value())); + } return params; } diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index a3f187249d..411a5b2e6d 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -258,6 +258,7 @@ struct InferParams { std::vector input_layers; std::vector output_layers; std::map config; + cv::util::optional out_precision; }; class PipelineBuilder { @@ -362,6 +363,9 @@ void PipelineBuilder::addInfer(const CallParams& call_params, } pp->pluginConfig(infer_params.config); + if (infer_params.out_precision) { + pp->cfgOutputPrecision(infer_params.out_precision.value()); + } m_state->networks += cv::gapi::networks(*pp); addCall(call_params, diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index eca07ce9df..b76a468496 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -197,6 +197,16 @@ inline IE::Blob::Ptr wrapIE(const cv::MediaFrame::View& view, template inline void copyFromIE(const IE::Blob::Ptr &blob, MatType &mat) { + const auto& desc = blob->getTensorDesc(); + const auto ie_type = toCV(desc.getPrecision()); + if (ie_type != mat.type()) { + std::stringstream ss; + ss << "Failed while copying blob from IE to OCV: " + << "Blobs have different data types.\n" + << "IE type: " << ie_type << "\n" + << "OCV type: " << mat.type() << std::endl; + throw std::logic_error(ss.str()); + } switch (blob->getTensorDesc().getPrecision()) { #define HANDLE(E,T) \ case IE::Precision::E: std::copy_n(blob->buffer().as(), \ @@ -365,6 +375,13 @@ struct IEUnit { cv::util::throw_error(std::logic_error("Unsupported ParamDesc::Kind")); } + if (params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import && + !cv::util::holds_alternative(params.output_precision)) { + cv::util::throw_error( + std::logic_error("Setting output precision isn't supported for imported network")); + } + + using namespace cv::gapi::wip::onevpl; if (params.vpl_preproc_device.has_value() && params.vpl_preproc_ctx.has_value()) { using namespace cv::gapi::wip; @@ -1122,6 +1139,32 @@ static IE::PreProcessInfo configurePreProcInfo(const IE::InputInfo::CPtr& ii, return info; } +using namespace cv::gapi::ie::detail; +static void configureOutputPrecision(const IE::OutputsDataMap &outputs_info, + const ParamDesc::precision_variant_t &output_precision) { + switch (output_precision.index()) { + case ParamDesc::precision_variant_t::index_of(): { + auto precision = toIE(cv::util::get(output_precision)); + for (auto it : outputs_info) { + it.second->setPrecision(precision); + } + break; + } + case ParamDesc::precision_variant_t::index_of(): { + const auto& precision_map = + cv::util::get(output_precision); + for (auto it : precision_map) { + outputs_info.at(it.first)->setPrecision(toIE(it.second)); + } + break; + } + case ParamDesc::precision_variant_t::index_of(): { + // Do nothing; + break; + } + } +} + // NB: This is a callback used by async infer // to post outputs blobs (cv::GMat's). static void PostOutputs(InferenceEngine::InferRequest &request, @@ -1241,7 +1284,7 @@ struct Infer: public cv::detail::KernelTag { GAPI_Assert(uu.params.input_names.size() == in_metas.size() && "Known input layers count doesn't match input meta count"); - // NB: Configuring input precision and network reshape must be done + // NB: Configuring input/output precision and network reshape must be done // only in the loadNetwork case. using namespace cv::gapi::ie::detail; if (uu.params.kind == ParamDesc::Kind::Load) { @@ -1275,6 +1318,7 @@ struct Infer: public cv::detail::KernelTag { if (!input_reshape_table.empty()) { const_cast(&uu.net)->reshape(input_reshape_table); } + configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); } else { GAPI_Assert(uu.params.kind == ParamDesc::Kind::Import); auto inputs = uu.this_network.GetInputsInfo(); @@ -1393,6 +1437,7 @@ struct InferROI: public cv::detail::KernelTag { const_cast(uu.net_input_params) .set_param(input_name, ii->getTensorDesc()); } + configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); } else { GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); auto inputs = uu.this_network.GetInputsInfo(); @@ -1513,6 +1558,7 @@ struct InferList: public cv::detail::KernelTag { if (!input_reshape_table.empty()) { const_cast(&uu.net)->reshape(input_reshape_table); } + configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); } else { GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); std::size_t idx = 1u; @@ -1667,6 +1713,7 @@ struct InferList2: public cv::detail::KernelTag { if (!input_reshape_table.empty()) { const_cast(&uu.net)->reshape(input_reshape_table); } + configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); } else { GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); auto inputs = uu.this_network.GetInputsInfo(); diff --git a/modules/gapi/test/infer/gapi_infer_ie_test.cpp b/modules/gapi/test/infer/gapi_infer_ie_test.cpp index 3741438373..54c67c02d0 100644 --- a/modules/gapi/test/infer/gapi_infer_ie_test.cpp +++ b/modules/gapi/test/infer/gapi_infer_ie_test.cpp @@ -2956,6 +2956,111 @@ TEST(TestAgeGender, ThrowBlobAndInputPrecisionMismatchStreaming) } } +TEST(TestAgeGenderIE, ChangeOutputPrecision) +{ + initDLDTDataPath(); + + cv::gapi::ie::detail::ParamDesc params; + params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml"); + params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin"); + params.device_id = "CPU"; + + cv::Mat in_mat(cv::Size(320, 240), CV_8UC3); + cv::randu(in_mat, 0, 255); + + cv::Mat gapi_age, gapi_gender; + + // Load & run IE network + IE::Blob::Ptr ie_age, ie_gender; + { + auto plugin = cv::gimpl::ie::wrap::getPlugin(params); + auto net = cv::gimpl::ie::wrap::readNetwork(params); + setNetParameters(net); + for (auto it : net.getOutputsInfo()) { + it.second->setPrecision(IE::Precision::U8); + } + auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params); + auto infer_request = this_network.CreateInferRequest(); + infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(in_mat)); + infer_request.Infer(); + ie_age = infer_request.GetBlob("age_conv3"); + ie_gender = infer_request.GetBlob("prob"); + } + + // Configure & run G-API + using AGInfo = std::tuple; + G_API_NET(AgeGender, , "test-age-gender"); + + cv::GMat in; + cv::GMat age, gender; + std::tie(age, gender) = cv::gapi::infer(in); + cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender)); + + auto pp = cv::gapi::ie::Params { + params.model_path, params.weights_path, params.device_id + }.cfgOutputLayers({ "age_conv3", "prob" }) + .cfgOutputPrecision(CV_8U); + comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender), + cv::compile_args(cv::gapi::networks(pp))); + + // Validate with IE itself (avoid DNN module dependency here) + normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" ); + normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output"); +} + +TEST(TestAgeGenderIE, ChangeSpecificOutputPrecison) +{ + initDLDTDataPath(); + + cv::gapi::ie::detail::ParamDesc params; + params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml"); + params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin"); + params.device_id = "CPU"; + + cv::Mat in_mat(cv::Size(320, 240), CV_8UC3); + cv::randu(in_mat, 0, 255); + + cv::Mat gapi_age, gapi_gender; + + // Load & run IE network + IE::Blob::Ptr ie_age, ie_gender; + { + auto plugin = cv::gimpl::ie::wrap::getPlugin(params); + auto net = cv::gimpl::ie::wrap::readNetwork(params); + setNetParameters(net); + + // NB: Specify precision only for "prob" output. + net.getOutputsInfo().at("prob")->setPrecision(IE::Precision::U8); + + auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params); + auto infer_request = this_network.CreateInferRequest(); + infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(in_mat)); + infer_request.Infer(); + ie_age = infer_request.GetBlob("age_conv3"); + ie_gender = infer_request.GetBlob("prob"); + } + + // Configure & run G-API + using AGInfo = std::tuple; + G_API_NET(AgeGender, , "test-age-gender"); + + cv::GMat in; + cv::GMat age, gender; + std::tie(age, gender) = cv::gapi::infer(in); + cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender)); + + auto pp = cv::gapi::ie::Params { + params.model_path, params.weights_path, params.device_id + }.cfgOutputLayers({ "age_conv3", "prob" }) + .cfgOutputPrecision({{"prob", CV_8U}}); + comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender), + cv::compile_args(cv::gapi::networks(pp))); + + // Validate with IE itself (avoid DNN module dependency here) + normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" ); + normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output"); +} + } // namespace opencv_test #endif // HAVE_INF_ENGINE From a6fbd8287c325bbcd3f49f52e5b7f83f9b1d73ab Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 3 Oct 2022 08:04:15 +0000 Subject: [PATCH 126/313] Fix comments to review --- .../gapi/include/opencv2/gapi/infer/ie.hpp | 32 ++++++++----- modules/gapi/src/backends/ie/giebackend.cpp | 48 +++++++++---------- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/infer/ie.hpp b/modules/gapi/include/opencv2/gapi/infer/ie.hpp index 1e8fbc576c..d74cf77acd 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ie.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ie.hpp @@ -89,13 +89,17 @@ struct ParamDesc { cv::optional vpl_preproc_device; cv::optional vpl_preproc_ctx; - using precision_t = int; - using precision_map_t = std::unordered_map; - // NB: cv::util::monostate is default value that means precision wasn't specified. - using precision_variant_t = cv::util::variant; - precision_variant_t output_precision; + using PrecisionT = int; + using PrecisionMapT = std::unordered_map; + // NB: This parameter can contain: + // 1. cv::util::monostate - Don't specify precision, but use default from IR/Blob. + // 2. PrecisionT (CV_8U, CV_32F, ...) - Specifies precision for all output layers. + // 3. PrecisionMapT ({{"layer0", CV_32F}, {"layer1", CV_16F}} - Specifies precision for certain output layer. + // cv::util::monostate is default value that means precision wasn't specified. + using PrecisionVariantT = cv::util::variant; + PrecisionVariantT output_precision; }; } // namespace detail @@ -366,21 +370,23 @@ public: The function is used to set an output precision for model. - @param precision Precision in OpenCV format. + @param precision Precision in OpenCV format (CV_8U, CV_32F, ...) + will be applied to all output layers. @return reference to this parameter structure. */ - Params& cfgOutputPrecision(detail::ParamDesc::precision_t precision) { + Params& cfgOutputPrecision(detail::ParamDesc::PrecisionT precision) { desc.output_precision = precision; return *this; } /** @overload - @param precision_map Map of pairs: name of corresponding output layer and its precision + @param precision_map Map of pairs: name of corresponding output layer + and its precision in OpenCV format (CV_8U, CV_32F, ...) @return reference to this parameter structure. */ Params& - cfgOutputPrecision(detail::ParamDesc::precision_map_t precision_map) { + cfgOutputPrecision(detail::ParamDesc::PrecisionMapT precision_map) { desc.output_precision = precision_map; return *this; } @@ -511,14 +517,14 @@ public: } /** @see ie::Params::cfgOutputPrecision */ - Params& cfgOutputPrecision(detail::ParamDesc::precision_t precision) { + Params& cfgOutputPrecision(detail::ParamDesc::PrecisionT precision) { desc.output_precision = precision; return *this; } /** @overload */ Params& - cfgOutputPrecision(detail::ParamDesc::precision_map_t precision_map) { + cfgOutputPrecision(detail::ParamDesc::PrecisionMapT precision_map) { desc.output_precision = precision_map; return *this; } diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index b76a468496..6c95b860a9 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -201,10 +201,10 @@ inline void copyFromIE(const IE::Blob::Ptr &blob, MatType &mat) { const auto ie_type = toCV(desc.getPrecision()); if (ie_type != mat.type()) { std::stringstream ss; - ss << "Failed while copying blob from IE to OCV: " - << "Blobs have different data types.\n" - << "IE type: " << ie_type << "\n" - << "OCV type: " << mat.type() << std::endl; + ss << "Failed to copy blob from IE to OCV: " + << "Blobs have different data types " + << "(IE type: " << ie_type + << " vs OCV type: " << mat.type() << ")." << std::endl; throw std::logic_error(ss.str()); } switch (blob->getTensorDesc().getPrecision()) { @@ -1140,29 +1140,25 @@ static IE::PreProcessInfo configurePreProcInfo(const IE::InputInfo::CPtr& ii, } using namespace cv::gapi::ie::detail; -static void configureOutputPrecision(const IE::OutputsDataMap &outputs_info, - const ParamDesc::precision_variant_t &output_precision) { - switch (output_precision.index()) { - case ParamDesc::precision_variant_t::index_of(): { - auto precision = toIE(cv::util::get(output_precision)); - for (auto it : outputs_info) { - it.second->setPrecision(precision); +static void configureOutputPrecision(const IE::OutputsDataMap &outputs_info, + const ParamDesc::PrecisionVariantT &output_precision) { + cv::util::visit(cv::util::overload_lambdas( + [&outputs_info](ParamDesc::PrecisionT cvdepth) { + auto precision = toIE(cvdepth); + for (auto it : outputs_info) { + it.second->setPrecision(precision); + } + }, + [&outputs_info](const ParamDesc::PrecisionMapT& precision_map) { + for (auto it : precision_map) { + outputs_info.at(it.first)->setPrecision(toIE(it.second)); + } + }, + [&outputs_info](cv::util::monostate) { + // Do nothing. } - break; - } - case ParamDesc::precision_variant_t::index_of(): { - const auto& precision_map = - cv::util::get(output_precision); - for (auto it : precision_map) { - outputs_info.at(it.first)->setPrecision(toIE(it.second)); - } - break; - } - case ParamDesc::precision_variant_t::index_of(): { - // Do nothing; - break; - } - } + ), output_precision + ); } // NB: This is a callback used by async infer From b1d28d5b4a6807d10f44a52c0300843266610236 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 3 Oct 2022 09:04:49 +0000 Subject: [PATCH 127/313] Expand performance report --- .../pipeline_modeling_tool/dummy_source.hpp | 1 + .../pipeline_modeling_tool/pipeline.hpp | 91 +++++++++++-------- .../pipeline_builder.hpp | 16 ++-- .../samples/pipeline_modeling_tool/utils.hpp | 15 +++ 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool/dummy_source.hpp b/modules/gapi/samples/pipeline_modeling_tool/dummy_source.hpp index d77e120081..4c2ea1638c 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/dummy_source.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/dummy_source.hpp @@ -18,6 +18,7 @@ public: const bool drop_frames); bool pull(cv::gapi::wip::Data& data) override; cv::GMetaArg descr_of() const override; + double latency() const { return m_latency; }; private: double m_latency; diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp index 2951d45610..91107c6dad 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp @@ -1,13 +1,18 @@ #ifndef OPENCV_GAPI_PIPELINE_MODELING_TOOL_PIPELINE_HPP #define OPENCV_GAPI_PIPELINE_MODELING_TOOL_PIPELINE_HPP +#include + struct PerfReport { - std::string name; - double avg_latency = 0.0; - double throughput = 0.0; - int64_t first_run_latency = 0; - int64_t elapsed = 0; - int64_t compilation_time = 0; + std::string name; + double avg_latency = 0.0; + int64_t min_latency = 0; + int64_t max_latency = 0; + int64_t first_latency = 0; + double throughput = 0.0; + int64_t elapsed = 0; + int64_t warmup_time = 0; + int64_t num_late_frames = 0; std::vector latencies; std::string toStr(bool expanded = false) const; @@ -15,17 +20,19 @@ struct PerfReport { std::string PerfReport::toStr(bool expand) const { std::stringstream ss; - ss << name << ": Compilation time: " << compilation_time << " ms; " - << "Average latency: " << avg_latency << " ms; Throughput: " - << throughput << " FPS; First latency: " - << first_run_latency << " ms"; - + ss << name << ": \n" + << " Warm up time: " << warmup_time << " ms\n" + << " Execution time: " << elapsed << " ms\n" + << " Frames: " << num_late_frames << "/" << latencies.size() << " (late/all)\n" + << " Latency:\n" + << " first: " << first_latency << " ms\n" + << " min: " << min_latency << " ms\n" + << " max: " << max_latency << " ms\n" + << " avg: " << std::fixed << std::setprecision(3) << avg_latency << " ms\n" + << " Throughput: " << std::fixed << std::setprecision(3) << throughput << " FPS"; if (expand) { - ss << "\nTotal processed frames: " << latencies.size() - << "\nTotal elapsed time: " << elapsed << " ms" << std::endl; for (size_t i = 0; i < latencies.size(); ++i) { - ss << std::endl; - ss << "Frame:" << i << "\nLatency: " + ss << "\nFrame:" << i << "\nLatency: " << latencies[i] << " ms"; } } @@ -37,11 +44,11 @@ class Pipeline { public: using Ptr = std::shared_ptr; - Pipeline(std::string&& name, - cv::GComputation&& comp, - cv::gapi::wip::IStreamSource::Ptr&& src, - cv::GCompileArgs&& args, - const size_t num_outputs); + Pipeline(std::string&& name, + cv::GComputation&& comp, + std::shared_ptr&& src, + cv::GCompileArgs&& args, + const size_t num_outputs); void compile(); void run(double work_time_ms); @@ -59,19 +66,19 @@ protected: virtual void _compile() = 0; virtual RunPerf _run(double work_time_ms) = 0; - std::string m_name; - cv::GComputation m_comp; - cv::gapi::wip::IStreamSource::Ptr m_src; - cv::GCompileArgs m_args; - size_t m_num_outputs; - PerfReport m_perf; + std::string m_name; + cv::GComputation m_comp; + std::shared_ptr m_src; + cv::GCompileArgs m_args; + size_t m_num_outputs; + PerfReport m_perf; }; -Pipeline::Pipeline(std::string&& name, - cv::GComputation&& comp, - cv::gapi::wip::IStreamSource::Ptr&& src, - cv::GCompileArgs&& args, - const size_t num_outputs) +Pipeline::Pipeline(std::string&& name, + cv::GComputation&& comp, + std::shared_ptr&& src, + cv::GCompileArgs&& args, + const size_t num_outputs) : m_name(std::move(name)), m_comp(std::move(comp)), m_src(std::move(src)), @@ -81,7 +88,7 @@ Pipeline::Pipeline(std::string&& name, } void Pipeline::compile() { - m_perf.compilation_time = + m_perf.warmup_time = utils::measure([this]() { _compile(); }); @@ -90,17 +97,23 @@ void Pipeline::compile() { void Pipeline::run(double work_time_ms) { auto run_perf = _run(work_time_ms); - m_perf.elapsed = run_perf.elapsed; - m_perf.latencies = std::move(run_perf.latencies); + m_perf.elapsed = run_perf.elapsed; + m_perf.latencies = std::move(run_perf.latencies); + m_perf.avg_latency = utils::avg(m_perf.latencies); + m_perf.min_latency = utils::min(m_perf.latencies); + m_perf.max_latency = utils::max(m_perf.latencies); + m_perf.first_latency = m_perf.latencies[0]; + + // NB: Count how many executions don't fit into camera latency interval. + m_perf.num_late_frames = + std::count_if(m_perf.latencies.begin(), m_perf.latencies.end(), + [this](int64_t latency) { + return static_cast(latency) > m_src->latency(); + }); - m_perf.avg_latency = - std::accumulate(m_perf.latencies.begin(), - m_perf.latencies.end(), - 0.0) / static_cast(m_perf.latencies.size()); m_perf.throughput = (m_perf.latencies.size() / static_cast(m_perf.elapsed)) * 1000; - m_perf.first_run_latency = m_perf.latencies[0]; } const PerfReport& Pipeline::report() const { diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index a3f187249d..cad8aa651a 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -295,15 +295,15 @@ private: std::vector output_edges; }; - M calls_map; - std::vector all_calls; + M calls_map; + std::vector all_calls; - cv::gapi::GNetPackage networks; - cv::gapi::GKernelPackage kernels; - cv::GCompileArgs compile_args; - cv::gapi::wip::IStreamSource::Ptr src; - PLMode mode = PLMode::STREAMING; - std::string name; + cv::gapi::GNetPackage networks; + cv::gapi::GKernelPackage kernels; + cv::GCompileArgs compile_args; + std::shared_ptr src; + PLMode mode = PLMode::STREAMING; + std::string name; }; std::unique_ptr m_state; diff --git a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp index a5be323747..6eb6bb7202 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp @@ -104,6 +104,21 @@ void mergeMapWith(std::map& target, const std::map& second) { } } +template +double avg(const std::vector& vec) { + return std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size(); +} + +template +T max(const std::vector& vec) { + return *std::max_element(vec.begin(), vec.end()); +} + +template +T min(const std::vector& vec) { + return *std::min_element(vec.begin(), vec.end()); +} + } // namespace utils #endif // OPENCV_GAPI_PIPELINE_MODELING_TOOL_UTILS_HPP From 15d2a5faf8dad1deb44048b77e5271d438f2fbbd Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Wed, 28 Sep 2022 15:46:57 +0000 Subject: [PATCH 128/313] Add sync infer request --- modules/gapi/src/backends/ie/giebackend.cpp | 66 +++++++++++---------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index eca07ce9df..be44d36fcd 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -828,15 +828,15 @@ std::vector cv::gimpl::ie::IECompiled::createInfe class cv::gimpl::ie::RequestPool { public: - using RunF = std::function; - using CallbackF = std::function; + using SetInputDataF = std::function; + using ReadOutputDataF = std::function; // NB: The task is represented by: - // RunF - function which is set blobs and run async inference. - // CallbackF - function which is obtain output blobs and post it to output. + // SetInputDataF - function which set input data. + // ReadOutputDataF - function which read output data. struct Task { - RunF run; - CallbackF callback; + SetInputDataF set_input_data; + ReadOutputDataF read_output_data; }; explicit RequestPool(std::vector&& requests); @@ -850,11 +850,21 @@ private: IE::InferRequest request, IE::StatusCode code) noexcept; void setup(); + void releaseRequest(const int id); QueueClass m_idle_ids; std::vector m_requests; + bool m_use_sync_api = false; }; +void cv::gimpl::ie::RequestPool::releaseRequest(const int id) { + if (!m_use_sync_api) { + auto& request = m_requests[id]; + request.SetCompletionCallback([](){}); + } + m_idle_ids.push(id); +} + // RequestPool implementation ////////////////////////////////////////////// cv::gimpl::ie::RequestPool::RequestPool(std::vector&& requests) : m_requests(std::move(requests)) { @@ -867,25 +877,30 @@ void cv::gimpl::ie::RequestPool::setup() { } } -void cv::gimpl::ie::RequestPool::execute(cv::gimpl::ie::RequestPool::Task&& t) { +void cv::gimpl::ie::RequestPool::execute(cv::gimpl::ie::RequestPool::Task&& task) { size_t id = 0u; m_idle_ids.pop(id); - auto& request = m_requests[id]; - using namespace std::placeholders; - using callback_t = std::function; - request.SetCompletionCallback( - static_cast( - std::bind(&cv::gimpl::ie::RequestPool::callback, this, - t, id, _1, _2))); - // NB: InferRequest is already marked as busy - // in case of exception need to return it back to the idle. try { - t.run(request); + task.set_input_data(request); + if (m_use_sync_api) { + request.Infer(); + task.read_output_data(request, IE::StatusCode::OK); + releaseRequest(id); + } else { + using namespace std::placeholders; + using callback_t = std::function; + request.SetCompletionCallback( + static_cast( + std::bind(&cv::gimpl::ie::RequestPool::callback, this, + task, id, _1, _2))); + request.StartAsync(); + } } catch (...) { - request.SetCompletionCallback([](){}); - m_idle_ids.push(id); + // NB: InferRequest is already marked as busy + // in case of exception need to return it back to the idle. + releaseRequest(id); throw; } } @@ -898,9 +913,8 @@ void cv::gimpl::ie::RequestPool::callback(cv::gimpl::ie::RequestPool::Task task, // 1. Run callback // 2. Destroy callback to free resources. // 3. Mark InferRequest as idle. - task.callback(request, code); - request.SetCompletionCallback([](){}); - m_idle_ids.push(id); + task.read_output_data(request, code); + releaseRequest(id); } // NB: Not thread-safe. @@ -1335,9 +1349,6 @@ struct Infer: public cv::detail::KernelTag { cv::util::optional{}); setBlob(req, layer_name, this_blob, *ctx); } - // FIXME: Should it be done by kernel ? - // What about to do that in RequestPool ? - req.StartAsync(); }, std::bind(PostOutputs, _1, _2, ctx) } @@ -1455,9 +1466,6 @@ struct InferROI: public cv::detail::KernelTag { *(ctx->uu.params.input_names.begin()), this_blob, *ctx); } - // FIXME: Should it be done by kernel ? - // What about to do that in RequestPool ? - req.StartAsync(); }, std::bind(PostOutputs, _1, _2, ctx) } @@ -1575,7 +1583,6 @@ struct InferList: public cv::detail::KernelTag { cv::gimpl::ie::RequestPool::Task { [ctx, rc, this_blob](InferenceEngine::InferRequest &req) { setROIBlob(req, ctx->uu.params.input_names[0u], this_blob, rc, *ctx); - req.StartAsync(); }, std::bind(callback, std::placeholders::_1, std::placeholders::_2, pos) } @@ -1748,7 +1755,6 @@ struct InferList2: public cv::detail::KernelTag { "Only Rect and Mat types are supported for infer list 2!"); } } - req.StartAsync(); }, std::bind(callback, std::placeholders::_1, std::placeholders::_2, list_idx) } // task From 2af081363461c09da5ce015df8a154d37cbfd7e0 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Thu, 29 Sep 2022 09:47:53 +0000 Subject: [PATCH 129/313] Add sync/async executors for infer request --- modules/gapi/src/backends/ie/giebackend.cpp | 159 ++++++++++++-------- 1 file changed, 96 insertions(+), 63 deletions(-) diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index be44d36fcd..ccb59535d0 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -826,8 +826,10 @@ std::vector cv::gimpl::ie::IECompiled::createInfe return requests; } -class cv::gimpl::ie::RequestPool { +class IInferExecutor { public: + using Ptr = std::shared_ptr; + using NotifyCallbackF = std::function; using SetInputDataF = std::function; using ReadOutputDataF = std::function; @@ -835,41 +837,106 @@ public: // SetInputDataF - function which set input data. // ReadOutputDataF - function which read output data. struct Task { - SetInputDataF set_input_data; + SetInputDataF set_input_data; ReadOutputDataF read_output_data; }; - explicit RequestPool(std::vector&& requests); + IInferExecutor(IE::InferRequest request, NotifyCallbackF notify) + : m_request(std::move(request)), + m_notify(std::move(notify)) { + }; - void execute(Task&& t); - void waitAll(); + virtual void execute(const Task& task) = 0; + virtual ~IInferExecutor() = default; + +protected: + IE::InferRequest m_request; + NotifyCallbackF m_notify; +}; + +class SyncInferExecutor : public IInferExecutor { + using IInferExecutor::IInferExecutor; + virtual void execute(const IInferExecutor::Task& task) override; +}; + +void SyncInferExecutor::execute(const IInferExecutor::Task& task) { + try { + task.set_input_data(m_request); + m_request.Infer(); + task.read_output_data(m_request, IE::StatusCode::OK); + } catch (...) { + m_notify(); + throw; + } + // NB: Notify pool that executor has finished. + m_notify(); +} + +class AsyncInferExecutor : public IInferExecutor { +public: + using IInferExecutor::IInferExecutor; + virtual void execute(const IInferExecutor::Task& task) override; private: void callback(Task task, - size_t id, IE::InferRequest request, IE::StatusCode code) noexcept; - void setup(); - void releaseRequest(const int id); - - QueueClass m_idle_ids; - std::vector m_requests; - bool m_use_sync_api = false; }; -void cv::gimpl::ie::RequestPool::releaseRequest(const int id) { - if (!m_use_sync_api) { - auto& request = m_requests[id]; - request.SetCompletionCallback([](){}); +void AsyncInferExecutor::execute(const IInferExecutor::Task& task) { + using namespace std::placeholders; + using callback_t = std::function; + m_request.SetCompletionCallback( + static_cast( + std::bind(&AsyncInferExecutor::callback, this, task, _1, _2))); + try { + task.set_input_data(m_request); + m_request.StartAsync(); + } catch (...) { + m_request.SetCompletionCallback([](){}); + m_notify(); + throw; } +} + +void AsyncInferExecutor::callback(IInferExecutor::Task task, + IE::InferRequest request, + IE::StatusCode code) noexcept { + task.read_output_data(request, code); + request.SetCompletionCallback([](){}); + // NB: Notify pool that executor has finished. + m_notify(); +} + +class cv::gimpl::ie::RequestPool { +public: + + explicit RequestPool(std::vector&& requests); + + IInferExecutor::Ptr getIdleRequest(); + void waitAll(); + +private: + void setup(); + void release(const int id); + + QueueClass m_idle_ids; + std::vector m_requests; +}; + +void cv::gimpl::ie::RequestPool::release(const int id) { m_idle_ids.push(id); } // RequestPool implementation ////////////////////////////////////////////// -cv::gimpl::ie::RequestPool::RequestPool(std::vector&& requests) - : m_requests(std::move(requests)) { - setup(); +cv::gimpl::ie::RequestPool::RequestPool(std::vector&& requests) { + for (size_t i = 0; i < requests.size(); ++i) { + m_requests.emplace_back( + std::make_shared(std::move(requests[0]), + std::bind(&RequestPool::release, this, i))); } + setup(); +} void cv::gimpl::ie::RequestPool::setup() { for (size_t i = 0; i < m_requests.size(); ++i) { @@ -877,44 +944,10 @@ void cv::gimpl::ie::RequestPool::setup() { } } -void cv::gimpl::ie::RequestPool::execute(cv::gimpl::ie::RequestPool::Task&& task) { +IInferExecutor::Ptr cv::gimpl::ie::RequestPool::getIdleRequest() { size_t id = 0u; m_idle_ids.pop(id); - auto& request = m_requests[id]; - - try { - task.set_input_data(request); - if (m_use_sync_api) { - request.Infer(); - task.read_output_data(request, IE::StatusCode::OK); - releaseRequest(id); - } else { - using namespace std::placeholders; - using callback_t = std::function; - request.SetCompletionCallback( - static_cast( - std::bind(&cv::gimpl::ie::RequestPool::callback, this, - task, id, _1, _2))); - request.StartAsync(); - } - } catch (...) { - // NB: InferRequest is already marked as busy - // in case of exception need to return it back to the idle. - releaseRequest(id); - throw; - } -} - -void cv::gimpl::ie::RequestPool::callback(cv::gimpl::ie::RequestPool::Task task, - size_t id, - IE::InferRequest request, - IE::StatusCode code) noexcept { - // NB: Inference is over. - // 1. Run callback - // 2. Destroy callback to free resources. - // 3. Mark InferRequest as idle. - task.read_output_data(request, code); - releaseRequest(id); + return m_requests[id]; } // NB: Not thread-safe. @@ -1330,8 +1363,8 @@ struct Infer: public cv::detail::KernelTag { static void run(std::shared_ptr ctx, cv::gimpl::ie::RequestPool &reqPool) { using namespace std::placeholders; - reqPool.execute( - cv::gimpl::ie::RequestPool::Task { + reqPool.getIdleRequest()->execute( + IInferExecutor::Task { [ctx](InferenceEngine::InferRequest &req) { // non-generic version for now: // - assumes all inputs/outputs are always Mats @@ -1440,8 +1473,8 @@ struct InferROI: public cv::detail::KernelTag { static void run(std::shared_ptr ctx, cv::gimpl::ie::RequestPool &reqPool) { using namespace std::placeholders; - reqPool.execute( - cv::gimpl::ie::RequestPool::Task { + reqPool.getIdleRequest()->execute( + IInferExecutor::Task { [ctx](InferenceEngine::InferRequest &req) { GAPI_Assert(ctx->uu.params.num_in == 1); auto&& this_roi = ctx->inArg(0).rref(); @@ -1579,8 +1612,8 @@ struct InferList: public cv::detail::KernelTag { for (auto&& it : ade::util::indexed(in_roi_vec)) { auto pos = ade::util::index(it); const auto& rc = ade::util::value(it); - reqPool.execute( - cv::gimpl::ie::RequestPool::Task { + reqPool.getIdleRequest()->execute( + IInferExecutor::Task { [ctx, rc, this_blob](InferenceEngine::InferRequest &req) { setROIBlob(req, ctx->uu.params.input_names[0u], this_blob, rc, *ctx); }, @@ -1734,8 +1767,8 @@ struct InferList2: public cv::detail::KernelTag { PostOutputsList callback(list_size, ctx, std::move(cached_dims)); for (const auto &list_idx : ade::util::iota(list_size)) { - reqPool.execute( - cv::gimpl::ie::RequestPool::Task { + reqPool.getIdleRequest()->execute( + IInferExecutor::Task { [ctx, list_idx, list_size, blob_0](InferenceEngine::InferRequest &req) { for (auto in_idx : ade::util::iota(ctx->uu.params.num_in)) { const auto &this_vec = ctx->inArg(in_idx+1u); From 589b6c15f05e4e523a0b149e7aba131ff619a026 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Thu, 29 Sep 2022 11:02:55 +0000 Subject: [PATCH 130/313] Fix windows warning --- modules/gapi/src/backends/ie/giebackend.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index ccb59535d0..10a5dcab36 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -918,13 +918,13 @@ public: private: void setup(); - void release(const int id); + void release(const size_t id); QueueClass m_idle_ids; std::vector m_requests; }; -void cv::gimpl::ie::RequestPool::release(const int id) { +void cv::gimpl::ie::RequestPool::release(const size_t id) { m_idle_ids.push(id); } @@ -932,7 +932,7 @@ void cv::gimpl::ie::RequestPool::release(const int id) { cv::gimpl::ie::RequestPool::RequestPool(std::vector&& requests) { for (size_t i = 0; i < requests.size(); ++i) { m_requests.emplace_back( - std::make_shared(std::move(requests[0]), + std::make_shared(std::move(requests[i]), std::bind(&RequestPool::release, this, i))); } setup(); From cf5db9b94fd04450ebb0e07927b86e45322cf4e0 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 3 Oct 2022 09:43:05 +0000 Subject: [PATCH 131/313] Add handle to configure async/sync infer mode --- .../gapi/include/opencv2/gapi/infer/ie.hpp | 37 +++++++++++++++++-- modules/gapi/src/backends/ie/giebackend.cpp | 31 +++++++++++++--- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/infer/ie.hpp b/modules/gapi/include/opencv2/gapi/infer/ie.hpp index 204bd8f266..27c479624a 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ie.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ie.hpp @@ -88,6 +88,9 @@ struct ParamDesc { cv::optional vpl_preproc_device; cv::optional vpl_preproc_ctx; + + enum InferMode {Sync, Async}; + InferMode mode; }; } // namespace detail @@ -132,7 +135,8 @@ public: , {} , {} , {} - , {}} { + , {} + , detail::ParamDesc::InferMode::Async} { }; /** @overload @@ -156,7 +160,8 @@ public: , {} , {} , {} - , {}} { + , {} + , detail::ParamDesc::InferMode::Async} { }; /** @brief Specifies sequence of network input layers names for inference. @@ -351,6 +356,22 @@ public: return *this; } + /** @brief Specifies which api will be used to run inference. + + The function is used to specify mode for OpenVINO inference. + OpenVINO has two options to run inference: + 1. Asynchronous (using StartAsync: https://docs.openvino.ai/latest/classInferenceEngine_1_1InferRequest.html#doxid-class-inference-engine-1-1-infer-request-1a405293e8423d82a5b45f642a3bef0d24) + 2. Synchronous (using Infer: https://docs.openvino.ai/latest/classInferenceEngine_1_1InferRequest.html#doxid-class-inference-engine-1-1-infer-request-1a3391ce30894abde730523e9ca9371ce8) + By default asynchronous mode is used. + + @param mode Inference mode which will be used. + @return reference to this parameter structure. + */ + Params& cfgInferMode(detail::ParamDesc::InferMode mode) { + desc.mode = mode; + return *this; + } + // BEGIN(G-API's network parametrization API) GBackend backend() const { return cv::gapi::ie::backend(); } std::string tag() const { return Net::tag(); } @@ -385,7 +406,8 @@ public: const std::string &device) : desc{ model, weights, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u, - {}, {}, {}, {}}, + {}, {}, {}, {}, + detail::ParamDesc::InferMode::Async }, m_tag(tag) { }; @@ -403,7 +425,8 @@ public: const std::string &device) : desc{ model, {}, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u, - {}, {}, {}, {}}, + {}, {}, {}, {}, + detail::ParamDesc::InferMode::Async }, m_tag(tag) { }; @@ -476,6 +499,12 @@ public: return *this; } + /** @see ie::Params::cfgInferAPI */ + Params& cfgInferMode(detail::ParamDesc::InferMode mode) { + desc.mode = mode; + return *this; + } + // BEGIN(G-API's network parametrization API) GBackend backend() const { return cv::gapi::ie::backend(); } std::string tag() const { return m_tag; } diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index 10a5dcab36..af0c97108f 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -375,6 +375,12 @@ struct IEUnit { params.vpl_preproc_ctx.value()); GAPI_LOG_INFO(nullptr, "VPP preproc created successfuly"); } + + if (params.mode == cv::gapi::ie::detail::ParamDesc::InferMode::Sync && + params.nireq != 1u) { + throw std::logic_error( + "Failed: ParamDesc::InferMode::Sync works only with nireq equal to 1."); + } } // This method is [supposed to be] called at Island compilation stage @@ -911,7 +917,8 @@ void AsyncInferExecutor::callback(IInferExecutor::Task task, class cv::gimpl::ie::RequestPool { public: - explicit RequestPool(std::vector&& requests); + explicit RequestPool(cv::gapi::ie::detail::ParamDesc::InferMode mode, + std::vector&& requests); IInferExecutor::Ptr getIdleRequest(); void waitAll(); @@ -929,11 +936,23 @@ void cv::gimpl::ie::RequestPool::release(const size_t id) { } // RequestPool implementation ////////////////////////////////////////////// -cv::gimpl::ie::RequestPool::RequestPool(std::vector&& requests) { +cv::gimpl::ie::RequestPool::RequestPool(cv::gapi::ie::detail::ParamDesc::InferMode mode, + std::vector&& requests) { for (size_t i = 0; i < requests.size(); ++i) { - m_requests.emplace_back( - std::make_shared(std::move(requests[i]), - std::bind(&RequestPool::release, this, i))); + IInferExecutor::Ptr iexec = nullptr; + switch (mode) { + case cv::gapi::ie::detail::ParamDesc::InferMode::Async: + iexec = std::make_shared(std::move(requests[i]), + std::bind(&RequestPool::release, this, i)); + break; + case cv::gapi::ie::detail::ParamDesc::InferMode::Sync: + iexec = std::make_shared(std::move(requests[i]), + std::bind(&RequestPool::release, this, i)); + break; + default: + GAPI_Assert(false && "Unsupported ParamDesc::InferMode"); + } + m_requests.emplace_back(std::move(iexec)); } setup(); } @@ -974,7 +993,7 @@ cv::gimpl::ie::GIEExecutable::GIEExecutable(const ade::Graph &g, if (this_nh == nullptr) { this_nh = nh; this_iec = iem.metadata(this_nh).get().compile(); - m_reqPool.reset(new RequestPool(this_iec.createInferRequests())); + m_reqPool.reset(new RequestPool(this_iec.params.mode, this_iec.createInferRequests())); } else util::throw_error(std::logic_error("Multi-node inference is not supported!")); From aafb7567c14f4acfa21a166207ed4a18efb0432e Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 3 Oct 2022 10:58:21 +0000 Subject: [PATCH 132/313] Add tests --- .../gapi/include/opencv2/gapi/infer/ie.hpp | 15 ++-- modules/gapi/src/backends/ie/giebackend.cpp | 14 ++-- .../gapi/test/infer/gapi_infer_ie_test.cpp | 71 +++++++++++++++++++ 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/infer/ie.hpp b/modules/gapi/include/opencv2/gapi/infer/ie.hpp index 27c479624a..4495ac811f 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ie.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ie.hpp @@ -52,6 +52,8 @@ enum class TraitAs: int using IEConfig = std::map; +enum InferMode {Sync, Async}; + namespace detail { struct ParamDesc { std::string model_path; @@ -89,7 +91,6 @@ struct ParamDesc { cv::optional vpl_preproc_device; cv::optional vpl_preproc_ctx; - enum InferMode {Sync, Async}; InferMode mode; }; } // namespace detail @@ -136,7 +137,7 @@ public: , {} , {} , {} - , detail::ParamDesc::InferMode::Async} { + , InferMode::Async} { }; /** @overload @@ -161,7 +162,7 @@ public: , {} , {} , {} - , detail::ParamDesc::InferMode::Async} { + , InferMode::Async} { }; /** @brief Specifies sequence of network input layers names for inference. @@ -367,7 +368,7 @@ public: @param mode Inference mode which will be used. @return reference to this parameter structure. */ - Params& cfgInferMode(detail::ParamDesc::InferMode mode) { + Params& cfgInferMode(InferMode mode) { desc.mode = mode; return *this; } @@ -407,7 +408,7 @@ public: : desc{ model, weights, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u, {}, {}, {}, {}, - detail::ParamDesc::InferMode::Async }, + InferMode::Async }, m_tag(tag) { }; @@ -426,7 +427,7 @@ public: : desc{ model, {}, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u, {}, {}, {}, {}, - detail::ParamDesc::InferMode::Async }, + InferMode::Async }, m_tag(tag) { }; @@ -500,7 +501,7 @@ public: } /** @see ie::Params::cfgInferAPI */ - Params& cfgInferMode(detail::ParamDesc::InferMode mode) { + Params& cfgInferMode(InferMode mode) { desc.mode = mode; return *this; } diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index af0c97108f..27860c8fec 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -376,10 +376,10 @@ struct IEUnit { GAPI_LOG_INFO(nullptr, "VPP preproc created successfuly"); } - if (params.mode == cv::gapi::ie::detail::ParamDesc::InferMode::Sync && + if (params.mode == cv::gapi::ie::InferMode::Sync && params.nireq != 1u) { throw std::logic_error( - "Failed: ParamDesc::InferMode::Sync works only with nireq equal to 1."); + "Failed: cv::gapi::ie::InferMode::Sync works only with nireq equal to 1."); } } @@ -917,7 +917,7 @@ void AsyncInferExecutor::callback(IInferExecutor::Task task, class cv::gimpl::ie::RequestPool { public: - explicit RequestPool(cv::gapi::ie::detail::ParamDesc::InferMode mode, + explicit RequestPool(cv::gapi::ie::InferMode mode, std::vector&& requests); IInferExecutor::Ptr getIdleRequest(); @@ -936,21 +936,21 @@ void cv::gimpl::ie::RequestPool::release(const size_t id) { } // RequestPool implementation ////////////////////////////////////////////// -cv::gimpl::ie::RequestPool::RequestPool(cv::gapi::ie::detail::ParamDesc::InferMode mode, +cv::gimpl::ie::RequestPool::RequestPool(cv::gapi::ie::InferMode mode, std::vector&& requests) { for (size_t i = 0; i < requests.size(); ++i) { IInferExecutor::Ptr iexec = nullptr; switch (mode) { - case cv::gapi::ie::detail::ParamDesc::InferMode::Async: + case cv::gapi::ie::InferMode::Async: iexec = std::make_shared(std::move(requests[i]), std::bind(&RequestPool::release, this, i)); break; - case cv::gapi::ie::detail::ParamDesc::InferMode::Sync: + case cv::gapi::ie::InferMode::Sync: iexec = std::make_shared(std::move(requests[i]), std::bind(&RequestPool::release, this, i)); break; default: - GAPI_Assert(false && "Unsupported ParamDesc::InferMode"); + GAPI_Assert(false && "Unsupported cv::gapi::ie::InferMode"); } m_requests.emplace_back(std::move(iexec)); } diff --git a/modules/gapi/test/infer/gapi_infer_ie_test.cpp b/modules/gapi/test/infer/gapi_infer_ie_test.cpp index 3741438373..fff24e4062 100644 --- a/modules/gapi/test/infer/gapi_infer_ie_test.cpp +++ b/modules/gapi/test/infer/gapi_infer_ie_test.cpp @@ -2956,6 +2956,77 @@ TEST(TestAgeGender, ThrowBlobAndInputPrecisionMismatchStreaming) } } +struct AgeGenderInferTest: public ::testing::Test { + cv::Mat m_in_mat; + cv::Mat m_gapi_age; + cv::Mat m_gapi_gender; + + cv::gimpl::ie::wrap::Plugin m_plugin; + IE::CNNNetwork m_net; + cv::gapi::ie::detail::ParamDesc m_params; + + using AGInfo = std::tuple; + G_API_NET(AgeGender, , "test-age-gender"); + + void SetUp() { + initDLDTDataPath(); + m_params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml"); + m_params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin"); + m_params.device_id = "CPU"; + + m_plugin = cv::gimpl::ie::wrap::getPlugin(m_params); + m_net = cv::gimpl::ie::wrap::readNetwork(m_params); + setNetParameters(m_net); + + m_in_mat = cv::Mat(cv::Size(320, 240), CV_8UC3); + cv::randu(m_in_mat, 0, 255); + } + + cv::GComputation buildGraph() { + cv::GMat in, age, gender; + std::tie(age, gender) = cv::gapi::infer(in); + return cv::GComputation(cv::GIn(in), cv::GOut(age, gender)); + } + + void validate() { + IE::Blob::Ptr ie_age, ie_gender; + { + auto this_network = cv::gimpl::ie::wrap::loadNetwork(m_plugin, m_net, m_params); + auto infer_request = this_network.CreateInferRequest(); + infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(m_in_mat)); + infer_request.Infer(); + ie_age = infer_request.GetBlob("age_conv3"); + ie_gender = infer_request.GetBlob("prob"); + } + // Validate with IE itself (avoid DNN module dependency here) + normAssert(cv::gapi::ie::util::to_ocv(ie_age), m_gapi_age, "Test age output" ); + normAssert(cv::gapi::ie::util::to_ocv(ie_gender), m_gapi_gender, "Test gender output"); + } +}; + +TEST_F(AgeGenderInferTest, SyncExecution) { + auto pp = cv::gapi::ie::Params { + m_params.model_path, m_params.weights_path, m_params.device_id + }.cfgOutputLayers({ "age_conv3", "prob" }) + .cfgInferMode(cv::gapi::ie::InferMode::Sync); + + buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender), + cv::compile_args(cv::gapi::networks(pp))); + + validate(); +} + +TEST_F(AgeGenderInferTest, ThrowSyncWithNireqNotEqualToOne) { + auto pp = cv::gapi::ie::Params { + m_params.model_path, m_params.weights_path, m_params.device_id + }.cfgOutputLayers({ "age_conv3", "prob" }) + .cfgInferMode(cv::gapi::ie::InferMode::Sync) + .cfgNumRequests(4u); + + EXPECT_ANY_THROW(buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender), + cv::compile_args(cv::gapi::networks(pp)))); +} + } // namespace opencv_test #endif // HAVE_INF_ENGINE From 0cd43961809dbebe4108bb4ab0f06ad786400b4c Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Mon, 3 Oct 2022 11:08:15 +0000 Subject: [PATCH 133/313] Expand modeling tool to support infer_mode --- modules/gapi/samples/pipeline_modeling_tool.cpp | 16 +++++++++++++++- .../pipeline_modeling_tool/pipeline_builder.hpp | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index 60547a9c9b..f7a44388c2 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -175,6 +175,17 @@ static PLMode strToPLMode(const std::string& mode_str) { } } +static cv::gapi::ie::InferMode strToInferMode(const std::string& infer_mode) { + if (infer_mode == "async") { + return cv::gapi::ie::InferMode::Async; + } else if (infer_mode == "sync") { + return cv::gapi::ie::InferMode::Sync; + } else { + throw std::logic_error("Unsupported Infer mode: " + infer_mode + + "\nPlease chose between: async and sync"); + } +} + template <> CallParams read(const cv::FileNode& fn) { auto name = @@ -282,7 +293,8 @@ int main(int argc, char* argv[]) { "{ drop_frames | false | Drop frames if they come earlier than pipeline is completed. }" "{ exec_list | | A comma-separated list of pipelines that" " will be executed. Spaces around commas" - " are prohibited. }"; + " are prohibited. }" + "{ infer_mode | async | OpenVINO inference mode (async/sync). }"; cv::CommandLineParser cmd(argc, argv, keys); if (cmd.has("help")) { @@ -298,6 +310,7 @@ int main(int argc, char* argv[]) { const auto qc = cmd.get("qc"); const auto app_mode = strToAppMode(cmd.get("app_mode")); const auto exec_str = cmd.get("exec_list"); + const auto infer_mode = strToInferMode(cmd.get("infer_mode")); const auto drop_frames = cmd.get("drop_frames"); cv::FileStorage fs; @@ -388,6 +401,7 @@ int main(int argc, char* argv[]) { << call_params.name << std::endl << e.what(); throw std::logic_error(ss.str()); } + infer_params.mode = infer_mode; builder.addInfer(call_params, infer_params); } else { throw std::logic_error("Unsupported node type: " + node_type); diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index a3f187249d..e981dae22d 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -258,6 +258,7 @@ struct InferParams { std::vector input_layers; std::vector output_layers; std::map config; + cv::gapi::ie::InferMode mode; }; class PipelineBuilder { @@ -362,6 +363,7 @@ void PipelineBuilder::addInfer(const CallParams& call_params, } pp->pluginConfig(infer_params.config); + pp->cfgInferMode(infer_params.mode); m_state->networks += cv::gapi::networks(*pp); addCall(call_params, From 2f79b1b0877b194f461de0fb73ee4e7ab01a90fa Mon Sep 17 00:00:00 2001 From: Kumataro Date: Tue, 4 Oct 2022 00:24:15 +0900 Subject: [PATCH 134/313] Merge pull request #22404 from Kumataro:3.4-fix22388_2 * imgcodecs: tiff: Reduce memory usage to read 16bit image. * imgcodecs: tiff: Reduce memory usage to read 8bit images * imgcodecs: tiff: split basic test and full test. * imgcodecs: tiff: fix to warning C4244 * imgcodecs: tiff: fix to warning C4244 --- modules/imgcodecs/src/grfmt_tiff.cpp | 249 ++++++++++++++-- modules/imgcodecs/test/test_tiff.cpp | 428 +++++++++++++++++++++++++++ 2 files changed, 646 insertions(+), 31 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 04df6ff8bb..42ddeb2aa1 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -234,7 +234,6 @@ public: bool TiffDecoder::readHeader() { bool result = false; - TIFF* tif = static_cast(m_tif.get()); if (!tif) { @@ -390,18 +389,15 @@ static void fixOrientationFull(Mat &img, int orientation) * For 8 bit some corrections are done by TIFFReadRGBAStrip/Tile already. * Not so for 16/32/64 bit. */ -static void fixOrientation(Mat &img, uint16 orientation, int dst_bpp) +static void fixOrientation(Mat &img, uint16 orientation, bool isOrientationFull) { - switch(dst_bpp) { - case 8: - fixOrientationPartial(img, orientation); - break; - - case 16: - case 32: - case 64: - fixOrientationFull(img, orientation); - break; + if( isOrientationFull ) + { + fixOrientationFull(img, orientation); + } + else + { + fixOrientationPartial(img, orientation); } } @@ -440,17 +436,7 @@ bool TiffDecoder::readData( Mat& img ) (img_orientation == ORIENTATION_BOTRIGHT || img_orientation == ORIENTATION_RIGHTBOT || img_orientation == ORIENTATION_BOTLEFT || img_orientation == ORIENTATION_LEFTBOT); int wanted_channels = normalizeChannelsNumber(img.channels()); - - if (dst_bpp == 8) - { - char errmsg[1024]; - if (!TIFFRGBAImageOK(tif, errmsg)) - { - CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg); - close(); - return false; - } - } + bool doReadScanline = false; uint32 tile_width0 = m_width, tile_height0 = 0; @@ -480,25 +466,139 @@ bool TiffDecoder::readData( Mat& img ) const uint64_t MAX_TILE_SIZE = (CV_BIG_UINT(1) << 30); CV_CheckLE((int)ncn, 4, ""); CV_CheckLE((int)bpp, 64, ""); - CV_Assert(((uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte)) < MAX_TILE_SIZE) && "TIFF tile size is too large: >= 1Gb"); if (dst_bpp == 8) { - // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA - bpp = 8; - ncn = 4; + const int _ncn = 4; // Read RGBA + const int _bpp = 8; // Read 8bit + + // if buffer_size(as 32bit RGBA) >= MAX_TILE_SIZE*95%, + // we will use TIFFReadScanline function. + + if ( + (uint64_t)tile_width0 * tile_height0 * _ncn * std::max(1, (int)(_bpp / bitsPerByte)) + >= + ( (uint64_t) MAX_TILE_SIZE * 95 / 100) + ) + { + uint16_t planerConfig = (uint16)-1; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig)); + + doReadScanline = (!is_tiled) // no tile + && + ( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) ) + && + ( ( bpp == 8 ) || ( bpp == 16 ) ) + && + (tile_height0 == (uint32_t) m_height) // single strip + && + ( + (photometric == PHOTOMETRIC_MINISWHITE) + || + (photometric == PHOTOMETRIC_MINISBLACK) + || + (photometric == PHOTOMETRIC_RGB) + ) + && + (planerConfig != PLANARCONFIG_SEPARATE); + + // Currently only EXTRASAMPLE_ASSOCALPHA is supported. + if ( doReadScanline && ( ncn == 4 ) ) + { + uint16_t extra_samples_num; + uint16_t *extra_samples = NULL; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples )); + doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA ); + } + } + + if ( !doReadScanline ) + { + // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA + bpp = 8; + ncn = 4; + + char errmsg[1024]; + if (!TIFFRGBAImageOK(tif, errmsg)) + { + CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg); + close(); + return false; + } + } + } + else if (dst_bpp == 16) + { + // if buffer_size >= MAX_TILE_SIZE*95%, + // we will use TIFFReadScanline function. + if ( + (uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte)) + >= + MAX_TILE_SIZE * 95 / 100 + ) + { + uint16_t planerConfig = (uint16)-1; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig)); + + doReadScanline = (!is_tiled) // no tile + && + ( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) ) + && + ( ( bpp == 8 ) || ( bpp == 16 ) ) + && + (tile_height0 == (uint32_t) m_height) // single strip + && + ( + (photometric == PHOTOMETRIC_MINISWHITE) + || + (photometric == PHOTOMETRIC_MINISBLACK) + || + (photometric == PHOTOMETRIC_RGB) + ) + && + (planerConfig != PLANARCONFIG_SEPARATE); + + // Currently only EXTRASAMPLE_ASSOCALPHA is supported. + if ( doReadScanline && ( ncn == 4 ) ) + { + uint16_t extra_samples_num; + uint16_t *extra_samples = NULL; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples )); + doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA ); + } + } } else if (dst_bpp == 32 || dst_bpp == 64) { CV_Assert(ncn == img.channels()); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)); } + + if ( doReadScanline ) + { + // Read each scanlines. + tile_height0 = 1; + } + const size_t buffer_size = (bpp / bitsPerByte) * ncn * tile_height0 * tile_width0; + CV_CheckLT( buffer_size, MAX_TILE_SIZE, "buffer_size is too large: >= 1Gb"); + + if ( doReadScanline ) + { + CV_CheckGE( static_cast(buffer_size), + static_cast(TIFFScanlineSize(tif)), + "buffer_size is smaller than TIFFScanlineSize(). "); + } + AutoBuffer _buffer(buffer_size); uchar* buffer = _buffer.data(); ushort* buffer16 = (ushort*)buffer; int tileidx = 0; + #define MAKE_FLAG(a,b) ( (a << 8) | b ) + const int convert_flag = MAKE_FLAG( ncn, wanted_channels ); + const bool isNeedConvert16to8 = ( doReadScanline ) && ( bpp == 16 ) && ( dst_bpp == 8); + for (int y = 0; y < m_height; y += (int)tile_height0) { int tile_height = std::min((int)tile_height0, m_height - y); @@ -514,7 +614,29 @@ bool TiffDecoder::readData( Mat& img ) case 8: { uchar* bstart = buffer; - if (!is_tiled) + if (doReadScanline) + { + CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)buffer, y) >= 0); + + if ( isNeedConvert16to8 ) + { + // Convert buffer image from 16bit to 8bit. + int ix; + for ( ix = 0 ; ix < tile_width * ncn - 4; ix += 4 ) + { + buffer[ ix ] = buffer[ ix * 2 + 1 ]; + buffer[ ix + 1 ] = buffer[ ix * 2 + 3 ]; + buffer[ ix + 2 ] = buffer[ ix * 2 + 5 ]; + buffer[ ix + 3 ] = buffer[ ix * 2 + 7 ]; + } + + for ( ; ix < tile_width * ncn ; ix ++ ) + { + buffer[ ix ] = buffer[ ix * 2 + 1]; + } + } + } + else if (!is_tiled) { CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)buffer)); } @@ -525,9 +647,65 @@ bool TiffDecoder::readData( Mat& img ) bstart += (tile_height0 - tile_height) * tile_width0 * 4; } + uchar* img_line_buffer = (uchar*) img.ptr(y, 0); + for (int i = 0; i < tile_height; i++) { - if (color) + if (doReadScanline) + { + switch ( convert_flag ) + { + case MAKE_FLAG( 1, 1 ): // GRAY to GRAY + memcpy( (void*) img_line_buffer, + (void*) bstart, + tile_width * sizeof(uchar) ); + break; + + case MAKE_FLAG( 1, 3 ): // GRAY to BGR + icvCvt_Gray2BGR_8u_C1C3R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); + break; + + case MAKE_FLAG( 3, 1): // RGB to GRAY + icvCvt_BGR2Gray_8u_C3C1R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); + break; + + case MAKE_FLAG( 3, 3 ): // RGB to BGR + icvCvt_BGR2RGB_8u_C3R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); + break; + + case MAKE_FLAG( 4, 1 ): // RGBA to GRAY + icvCvt_BGRA2Gray_8u_C4C1R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); + break; + + case MAKE_FLAG( 4, 3 ): // RGBA to BGR + icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1), 2 ); + break; + + case MAKE_FLAG( 4, 4 ): // RGBA to BGRA + icvCvt_BGRA2RGBA_8u_C4R(bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); + break; + + default: + CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported convertion :" + << " bpp = " << bpp << " ncn = " << (int)ncn + << " wanted_channels =" << wanted_channels ); + break; + } + #undef MAKE_FLAG + } + else if (color) { if (wanted_channels == 4) { @@ -556,7 +734,11 @@ bool TiffDecoder::readData( Mat& img ) case 16: { - if (!is_tiled) + if (doReadScanline) + { + CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)buffer, y) >= 0); + } + else if (!is_tiled) { CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)buffer, buffer_size) >= 0); } @@ -655,7 +837,11 @@ bool TiffDecoder::readData( Mat& img ) } // for x } // for y } - fixOrientation(img, img_orientation, dst_bpp); + + // If TIFFReadRGBA* function is used -> fixOrientationPartial(). + // Otherwise -> fixOrientationFull(). + fixOrientation(img, img_orientation, + ( ( dst_bpp != 8 ) && ( !doReadScanline ) ) ); } if (m_hdr && depth >= CV_32F) @@ -680,6 +866,7 @@ TiffEncoder::~TiffEncoder() ImageEncoder TiffEncoder::newEncoder() const { + cv_tiffSetErrorHandler(); return makePtr(); } diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index 063bd9ae50..eed9eb8410 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -2,6 +2,8 @@ // 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 "test_precomp.hpp" +#include "opencv2/core/utils/logger.hpp" +#include "opencv2/core/utils/configuration.private.hpp" namespace opencv_test { namespace { @@ -46,6 +48,432 @@ TEST(Imgcodecs_Tiff, decode_tile16384x16384) EXPECT_EQ(0, remove(file4.c_str())); } +//================================================================================================== +// See https://github.com/opencv/opencv/issues/22388 + +/** + * Dummy enum to show combination of IMREAD_*. + */ +enum ImreadMixModes +{ + IMREAD_MIX_UNCHANGED = IMREAD_UNCHANGED , + IMREAD_MIX_GRAYSCALE = IMREAD_GRAYSCALE , + IMREAD_MIX_COLOR = IMREAD_COLOR , + IMREAD_MIX_GRAYSCALE_ANYDEPTH = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH , + IMREAD_MIX_GRAYSCALE_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYCOLOR, + IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR, + IMREAD_MIX_COLOR_ANYDEPTH = IMREAD_COLOR | IMREAD_ANYDEPTH , + IMREAD_MIX_COLOR_ANYCOLOR = IMREAD_COLOR | IMREAD_ANYCOLOR, + IMREAD_MIX_COLOR_ANYDEPTH_ANYCOLOR = IMREAD_COLOR | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR +}; + +typedef tuple< uint64_t, tuple, ImreadMixModes > Bufsize_and_Type; +typedef testing::TestWithParam Imgcodecs_Tiff_decode_Huge; + +static inline +void PrintTo(const ImreadMixModes& val, std::ostream* os) +{ + PrintTo( static_cast(val), os ); +} + +TEST_P(Imgcodecs_Tiff_decode_Huge, regression) +{ + // Get test parameters + const uint64_t buffer_size = get<0>(GetParam()); + const string mat_type_string = get<0>(get<1>(GetParam())); + const int mat_type = get<1>(get<1>(GetParam())); + const int imread_mode = get<2>(GetParam()); + + // Detect data file + const string req_filename = cv::format("readwrite/huge-tiff/%s_%llu.tif", mat_type_string.c_str(), buffer_size); + const string filename = findDataFile( req_filename ); + + // Preparation process for test + { + // Convert from mat_type and buffer_size to tiff file information. + const uint64_t width = 32768; + int ncn = CV_MAT_CN(mat_type); + int depth = ( CV_MAT_DEPTH(mat_type) == CV_16U) ? 2 : 1; // 16bit or 8 bit + const uint64_t height = (uint64_t) buffer_size / width / ncn / depth; + const uint64_t base_scanline_size = (uint64_t) width * ncn * depth; + const uint64_t base_strip_size = (uint64_t) base_scanline_size * height; + + // To avoid exception about pixel size, check it. + static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30); + uint64_t pixels = (uint64_t) width * height; + if ( pixels > CV_IO_MAX_IMAGE_PIXELS ) + { + throw SkipTestException( cv::format("Test is skipped( pixels(%lu) > CV_IO_MAX_IMAGE_PIXELS(%lu) )", + pixels, CV_IO_MAX_IMAGE_PIXELS ) ); + } + + // If buffer_size >= 1GB * 95%, TIFFReadScanline() is used. + const uint64_t BUFFER_SIZE_LIMIT_FOR_READS_CANLINE = (uint64_t) 1024*1024*1024*95/100; + const bool doReadScanline = ( base_strip_size >= BUFFER_SIZE_LIMIT_FOR_READS_CANLINE ); + + // Update ncn and depth for destination Mat. + switch ( imread_mode ) + { + case IMREAD_UNCHANGED: + break; + case IMREAD_GRAYSCALE: + ncn = 1; + depth = 1; + break; + case IMREAD_GRAYSCALE | IMREAD_ANYDEPTH: + ncn = 1; + break; + case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR: + ncn = (ncn == 1)?1:3; + depth = 1; + break; + case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR | IMREAD_ANYDEPTH: + ncn = (ncn == 1)?1:3; + break; + case IMREAD_COLOR: + ncn = 3; + depth = 1; + break; + case IMREAD_COLOR | IMREAD_ANYDEPTH: + ncn = 3; + break; + case IMREAD_COLOR | IMREAD_ANYCOLOR: + ncn = 3; + depth = 1; + break; + case IMREAD_COLOR | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR: + ncn = 3; + break; + default: + break; + } + + // Memory usage for Destination Mat + const uint64_t memory_usage_cvmat = (uint64_t) width * ncn * depth * height; + + // Memory usage for Work memory in libtiff. + uint64_t memory_usage_tiff = 0; + if ( ( depth == 1 ) && ( !doReadScanline ) ) + { + // TIFFReadRGBA*() request to allocate RGBA(32bit) buffer. + memory_usage_tiff = (uint64_t) + width * + 4 * // ncn = RGBA + 1 * // dst_bpp = 8 bpp + height; + } + else + { + // TIFFReadEncodedStrip() or TIFFReadScanline() request to allocate strip memory. + memory_usage_tiff = base_strip_size; + } + + // Memory usage for Work memory in imgcodec/grfmt_tiff.cpp + const uint64_t memory_usage_work = + ( doReadScanline ) ? base_scanline_size // for TIFFReadScanline() + : base_strip_size; // for TIFFReadRGBA*() or TIFFReadEncodedStrip() + + // Total memory usage. + const uint64_t memory_usage_total = + memory_usage_cvmat + // Destination Mat + memory_usage_tiff + // Work memory in libtiff + memory_usage_work; // Work memory in imgcodecs + + // Output memory usage log. + CV_LOG_DEBUG(NULL, cv::format("OpenCV TIFF-test(line %d):memory usage info : mat(%llu), libtiff(%llu), work(%llu) -> total(%llu)", + __LINE__, memory_usage_cvmat, memory_usage_tiff, memory_usage_work, memory_usage_total) ); + + // Add test tags. + if ( memory_usage_total >= (uint64_t) 6144 * 1024 * 1024 ) + { + applyTestTag( CV_TEST_TAG_MEMORY_14GB, CV_TEST_TAG_VERYLONG ); + } + else if ( memory_usage_total >= (uint64_t) 2048 * 1024 * 1024 ) + { + applyTestTag( CV_TEST_TAG_MEMORY_6GB, CV_TEST_TAG_VERYLONG ); + } + else if ( memory_usage_total >= (uint64_t) 1024 * 1024 * 1024 ) + { + applyTestTag( CV_TEST_TAG_MEMORY_2GB, CV_TEST_TAG_LONG ); + } + else if ( memory_usage_total >= (uint64_t) 512 * 1024 * 1024 ) + { + applyTestTag( CV_TEST_TAG_MEMORY_1GB ); + } + else if ( memory_usage_total >= (uint64_t) 200 * 1024 * 1024 ) + { + applyTestTag( CV_TEST_TAG_MEMORY_512MB ); + } + else + { + // do nothing. + } + } + + // TEST Main + + cv::Mat img; + ASSERT_NO_THROW( img = cv::imread(filename, imread_mode) ); + ASSERT_FALSE(img.empty()); + + /** + * Test marker pixels at each corners. + * + * 0xAn,0x00 ... 0x00, 0xBn + * 0x00,0x00 ... 0x00, 0x00 + * : : : : + * 0x00,0x00 ... 0x00, 0x00 + * 0xCn,0x00 .., 0x00, 0xDn + * + */ + +#define MAKE_FLAG(from_type, to_type) (((uint64_t)from_type << 32 ) | to_type ) + + switch ( MAKE_FLAG(mat_type, img.type() ) ) + { + // GRAY TO GRAY + case MAKE_FLAG(CV_8UC1, CV_8UC1): + case MAKE_FLAG(CV_16UC1, CV_8UC1): + EXPECT_EQ( 0xA0, img.at(0, 0) ); + EXPECT_EQ( 0xB0, img.at(0, img.cols-1) ); + EXPECT_EQ( 0xC0, img.at(img.rows-1, 0) ); + EXPECT_EQ( 0xD0, img.at(img.rows-1, img.cols-1) ); + break; + + // RGB/RGBA TO BGR + case MAKE_FLAG(CV_8UC3, CV_8UC3): + case MAKE_FLAG(CV_8UC4, CV_8UC3): + case MAKE_FLAG(CV_16UC3, CV_8UC3): + case MAKE_FLAG(CV_16UC4, CV_8UC3): + EXPECT_EQ( 0xA2, img.at(0, 0) [0] ); + EXPECT_EQ( 0xA1, img.at(0, 0) [1] ); + EXPECT_EQ( 0xA0, img.at(0, 0) [2] ); + EXPECT_EQ( 0xB2, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xB1, img.at(0, img.cols-1)[1] ); + EXPECT_EQ( 0xB0, img.at(0, img.cols-1)[2] ); + EXPECT_EQ( 0xC2, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xC1, img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( 0xC0, img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( 0xD2, img.at(img.rows-1, img.cols-1)[0] ); + EXPECT_EQ( 0xD1, img.at(img.rows-1, img.cols-1)[1] ); + EXPECT_EQ( 0xD0, img.at(img.rows-1, img.cols-1)[2] ); + break; + + // RGBA TO BGRA + case MAKE_FLAG(CV_8UC4, CV_8UC4): + case MAKE_FLAG(CV_16UC4, CV_8UC4): + EXPECT_EQ( 0xA2, img.at(0, 0) [0] ); + EXPECT_EQ( 0xA1, img.at(0, 0) [1] ); + EXPECT_EQ( 0xA0, img.at(0, 0) [2] ); + EXPECT_EQ( 0xA3, img.at(0, 0) [3] ); + EXPECT_EQ( 0xB2, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xB1, img.at(0, img.cols-1)[1] ); + EXPECT_EQ( 0xB0, img.at(0, img.cols-1)[2] ); + EXPECT_EQ( 0xB3, img.at(0, img.cols-1)[3] ); + EXPECT_EQ( 0xC2, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xC1, img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( 0xC0, img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( 0xC3, img.at(img.rows-1, 0) [3] ); + EXPECT_EQ( 0xD2, img.at(img.rows-1, img.cols-1)[0] ); + EXPECT_EQ( 0xD1, img.at(img.rows-1, img.cols-1)[1] ); + EXPECT_EQ( 0xD0, img.at(img.rows-1, img.cols-1)[2] ); + EXPECT_EQ( 0xD3, img.at(img.rows-1, img.cols-1)[3] ); + break; + + // RGB/RGBA to GRAY + case MAKE_FLAG(CV_8UC3, CV_8UC1): + case MAKE_FLAG(CV_8UC4, CV_8UC1): + case MAKE_FLAG(CV_16UC3, CV_8UC1): + case MAKE_FLAG(CV_16UC4, CV_8UC1): + EXPECT_LE( 0xA0, img.at(0, 0) ); + EXPECT_GE( 0xA2, img.at(0, 0) ); + EXPECT_LE( 0xB0, img.at(0, img.cols-1) ); + EXPECT_GE( 0xB2, img.at(0, img.cols-1) ); + EXPECT_LE( 0xC0, img.at(img.rows-1, 0) ); + EXPECT_GE( 0xC2, img.at(img.rows-1, 0) ); + EXPECT_LE( 0xD0, img.at(img.rows-1, img.cols-1) ); + EXPECT_GE( 0xD2, img.at(img.rows-1, img.cols-1) ); + break; + + // GRAY to BGR + case MAKE_FLAG(CV_8UC1, CV_8UC3): + case MAKE_FLAG(CV_16UC1, CV_8UC3): + EXPECT_EQ( 0xA0, img.at(0, 0) [0] ); + EXPECT_EQ( 0xB0, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xC0, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xD0, img.at(img.rows-1, img.cols-1)[0] ); + // R==G==B + EXPECT_EQ( img.at(0, 0) [0], img.at(0, 0) [1] ); + EXPECT_EQ( img.at(0, 0) [0], img.at(0, 0) [2] ); + EXPECT_EQ( img.at(0, img.cols-1) [0], img.at(0, img.cols-1)[1] ); + EXPECT_EQ( img.at(0, img.cols-1) [0], img.at(0, img.cols-1)[2] ); + EXPECT_EQ( img.at(img.rows-1, 0) [0], img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( img.at(img.rows-1, 0) [0], img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( img.at(img.rows-1, img.cols-1) [0], img.at(img.rows-1, img.cols-1)[1] ); + EXPECT_EQ( img.at(img.rows-1, img.cols-1) [0], img.at(img.rows-1, img.cols-1)[2] ); + break; + + // GRAY TO GRAY + case MAKE_FLAG(CV_16UC1, CV_16UC1): + EXPECT_EQ( 0xA090, img.at(0, 0) ); + EXPECT_EQ( 0xB080, img.at(0, img.cols-1) ); + EXPECT_EQ( 0xC070, img.at(img.rows-1, 0) ); + EXPECT_EQ( 0xD060, img.at(img.rows-1, img.cols-1) ); + break; + + // RGB/RGBA TO BGR + case MAKE_FLAG(CV_16UC3, CV_16UC3): + case MAKE_FLAG(CV_16UC4, CV_16UC3): + EXPECT_EQ( 0xA292, img.at(0, 0) [0] ); + EXPECT_EQ( 0xA191, img.at(0, 0) [1] ); + EXPECT_EQ( 0xA090, img.at(0, 0) [2] ); + EXPECT_EQ( 0xB282, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xB181, img.at(0, img.cols-1)[1] ); + EXPECT_EQ( 0xB080, img.at(0, img.cols-1)[2] ); + EXPECT_EQ( 0xC272, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xC171, img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( 0xC070, img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( 0xD262, img.at(img.rows-1, img.cols-1)[0] ); + EXPECT_EQ( 0xD161, img.at(img.rows-1, img.cols-1)[1] ); + EXPECT_EQ( 0xD060, img.at(img.rows-1, img.cols-1)[2] ); + break; + + // RGBA TO RGBA + case MAKE_FLAG(CV_16UC4, CV_16UC4): + EXPECT_EQ( 0xA292, img.at(0, 0) [0] ); + EXPECT_EQ( 0xA191, img.at(0, 0) [1] ); + EXPECT_EQ( 0xA090, img.at(0, 0) [2] ); + EXPECT_EQ( 0xA393, img.at(0, 0) [3] ); + EXPECT_EQ( 0xB282, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xB181, img.at(0, img.cols-1)[1] ); + EXPECT_EQ( 0xB080, img.at(0, img.cols-1)[2] ); + EXPECT_EQ( 0xB383, img.at(0, img.cols-1)[3] ); + EXPECT_EQ( 0xC272, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xC171, img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( 0xC070, img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( 0xC373, img.at(img.rows-1, 0) [3] ); + EXPECT_EQ( 0xD262, img.at(img.rows-1,img.cols-1) [0] ); + EXPECT_EQ( 0xD161, img.at(img.rows-1,img.cols-1) [1] ); + EXPECT_EQ( 0xD060, img.at(img.rows-1,img.cols-1) [2] ); + EXPECT_EQ( 0xD363, img.at(img.rows-1,img.cols-1) [3] ); + break; + + // RGB/RGBA to GRAY + case MAKE_FLAG(CV_16UC3, CV_16UC1): + case MAKE_FLAG(CV_16UC4, CV_16UC1): + EXPECT_LE( 0xA090, img.at(0, 0) ); + EXPECT_GE( 0xA292, img.at(0, 0) ); + EXPECT_LE( 0xB080, img.at(0, img.cols-1) ); + EXPECT_GE( 0xB282, img.at(0, img.cols-1) ); + EXPECT_LE( 0xC070, img.at(img.rows-1, 0) ); + EXPECT_GE( 0xC272, img.at(img.rows-1, 0) ); + EXPECT_LE( 0xD060, img.at(img.rows-1, img.cols-1) ); + EXPECT_GE( 0xD262, img.at(img.rows-1, img.cols-1) ); + break; + + // GRAY to RGB + case MAKE_FLAG(CV_16UC1, CV_16UC3): + EXPECT_EQ( 0xA090, img.at(0, 0) [0] ); + EXPECT_EQ( 0xB080, img.at(0, img.cols-1)[0] ); + EXPECT_EQ( 0xC070, img.at(img.rows-1, 0) [0] ); + EXPECT_EQ( 0xD060, img.at(img.rows-1, img.cols-1)[0] ); + // R==G==B + EXPECT_EQ( img.at(0, 0) [0], img.at(0, 0) [1] ); + EXPECT_EQ( img.at(0, 0) [0], img.at(0, 0) [2] ); + EXPECT_EQ( img.at(0, img.cols-1) [0], img.at(0, img.cols-1)[1] ); + EXPECT_EQ( img.at(0, img.cols-1) [0], img.at(0, img.cols-1)[2] ); + EXPECT_EQ( img.at(img.rows-1, 0) [0], img.at(img.rows-1, 0) [1] ); + EXPECT_EQ( img.at(img.rows-1, 0) [0], img.at(img.rows-1, 0) [2] ); + EXPECT_EQ( img.at(img.rows-1, img.cols-1) [0], img.at(img.rows-1, img.cols-1)[1] ); + EXPECT_EQ( img.at(img.rows-1, img.cols-1) [0], img.at(img.rows-1, img.cols-1)[2] ); + break; + + // No supported. + // (1) 8bit to 16bit + case MAKE_FLAG(CV_8UC1, CV_16UC1): + case MAKE_FLAG(CV_8UC1, CV_16UC3): + case MAKE_FLAG(CV_8UC1, CV_16UC4): + case MAKE_FLAG(CV_8UC3, CV_16UC1): + case MAKE_FLAG(CV_8UC3, CV_16UC3): + case MAKE_FLAG(CV_8UC3, CV_16UC4): + case MAKE_FLAG(CV_8UC4, CV_16UC1): + case MAKE_FLAG(CV_8UC4, CV_16UC3): + case MAKE_FLAG(CV_8UC4, CV_16UC4): + // (2) GRAY/RGB TO RGBA + case MAKE_FLAG(CV_8UC1, CV_8UC4): + case MAKE_FLAG(CV_8UC3, CV_8UC4): + case MAKE_FLAG(CV_16UC1, CV_8UC4): + case MAKE_FLAG(CV_16UC3, CV_8UC4): + case MAKE_FLAG(CV_16UC1, CV_16UC4): + case MAKE_FLAG(CV_16UC3, CV_16UC4): + default: + FAIL() << cv::format("Unknown test pattern: from = %d ( %d, %d) to = %d ( %d, %d )", + mat_type, (int)CV_MAT_CN(mat_type ), ( CV_MAT_DEPTH(mat_type )==CV_16U)?16:8, + img.type(), (int)CV_MAT_CN(img.type() ), ( CV_MAT_DEPTH(img.type() )==CV_16U)?16:8); + break; + } + +#undef MAKE_FLAG +} + +// Basic Test +const Bufsize_and_Type Imgcodecs_Tiff_decode_Huge_list_basic[] = +{ + make_tuple,ImreadMixModes>( 1073479680ull, make_tuple("CV_8UC1", CV_8UC1), IMREAD_MIX_COLOR ), + make_tuple,ImreadMixModes>( 2147483648ull, make_tuple("CV_16UC4", CV_16UC4), IMREAD_MIX_COLOR ), +}; + +INSTANTIATE_TEST_CASE_P(Imgcodecs_Tiff, Imgcodecs_Tiff_decode_Huge, + testing::ValuesIn( Imgcodecs_Tiff_decode_Huge_list_basic ) +); + +// Full Test + +/** + * Test lists for combination of IMREAD_*. + */ +const ImreadMixModes all_modes_Huge_Full[] = +{ + IMREAD_MIX_UNCHANGED, + IMREAD_MIX_GRAYSCALE, + IMREAD_MIX_GRAYSCALE_ANYDEPTH, + IMREAD_MIX_GRAYSCALE_ANYCOLOR, + IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR, + IMREAD_MIX_COLOR, + IMREAD_MIX_COLOR_ANYDEPTH, + IMREAD_MIX_COLOR_ANYCOLOR, + IMREAD_MIX_COLOR_ANYDEPTH_ANYCOLOR, +}; + +const uint64_t huge_buffer_sizes_decode_Full[] = +{ + 1048576ull, // 1 * 1024 * 1024 + 1073479680ull, // 1024 * 1024 * 1024 - 32768 * 4 * 2 + 1073741824ull, // 1024 * 1024 * 1024 + 2147483648ull, // 2048 * 1024 * 1024 +}; + +const tuple mat_types_Full[] = +{ + make_tuple("CV_8UC1", CV_8UC1), // 8bit GRAY + make_tuple("CV_8UC3", CV_8UC3), // 24bit RGB + make_tuple("CV_8UC4", CV_8UC4), // 32bit RGBA + make_tuple("CV_16UC1", CV_16UC1), // 16bit GRAY + make_tuple("CV_16UC3", CV_16UC3), // 48bit RGB + make_tuple("CV_16UC4", CV_16UC4), // 64bit RGBA +}; + +INSTANTIATE_TEST_CASE_P(DISABLED_Imgcodecs_Tiff_Full, Imgcodecs_Tiff_decode_Huge, + testing::Combine( + testing::ValuesIn(huge_buffer_sizes_decode_Full), + testing::ValuesIn(mat_types_Full), + testing::ValuesIn(all_modes_Huge_Full) + ) +); + + +//================================================================================================== + TEST(Imgcodecs_Tiff, write_read_16bit_big_little_endian) { // see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness" From ed3b56d7633a68ad313caa5c3fb6bc4f0775a0a0 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 7 Jun 2022 13:31:56 +0800 Subject: [PATCH 135/313] Add warpAffine IPPIW implementation protected by ipp NE flag Signed-off-by: robin --- modules/imgproc/src/imgwarp.cpp | 123 ++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index c6dd063f73..c36c8cebf5 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -2542,6 +2542,127 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, #endif +#ifdef HAVE_IPP +#define IPP_WARPAFFINE_PARALLEL 1 + +#ifdef HAVE_IPP_IW + +class ipp_warpAffineParallel: public ParallelLoopBody +{ +public: + ipp_warpAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, IppiInterpolationType _inter, double (&_coeffs)[2][3], ::ipp::IwiBorderType _borderType, IwTransDirection _iwTransDirection, bool *_ok):m_src(src), m_dst(dst) + { + pOk = _ok; + + inter = _inter; + borderType = _borderType; + iwTransDirection = _iwTransDirection; + + for( int i = 0; i < 2; i++ ) + for( int j = 0; j < 3; j++ ) + coeffs[i][j] = _coeffs[i][j]; + + *pOk = true; + } + ~ipp_warpAffineParallel() {} + + virtual void operator() (const Range& range) const CV_OVERRIDE + { + CV_INSTRUMENT_REGION_IPP(); + + if(*pOk == false) + return; + + try + { + ::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start); + CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, m_src, m_dst, coeffs, iwTransDirection, inter, ::ipp::IwiWarpAffineParams(), borderType, tile); + } + catch(const ::ipp::IwException &) + { + *pOk = false; + return; + } + } +private: + ::ipp::IwiImage &m_src; + ::ipp::IwiImage &m_dst; + + IppiInterpolationType inter; + double coeffs[2][3]; + ::ipp::IwiBorderType borderType; + IwTransDirection iwTransDirection; + + bool *pOk; + const ipp_warpAffineParallel& operator= (const ipp_warpAffineParallel&); +}; + +#endif + +static bool ipp_warpAffine( InputArray _src, OutputArray _dst, int interpolation, int borderType, InputArray _M, int flags ) +{ +#ifdef HAVE_IPP_IW + CV_INSTRUMENT_REGION_IPP(); + + if (!cv::ipp::useIPP_NotExact()) + return false; + + IppiInterpolationType ippInter = ippiGetInterpolation(interpolation); + if((int)ippInter < 0) + return false; + + // Acquire data and begin processing + try + { + Mat src = _src.getMat(); + Mat dst = _dst.getMat(); + ::ipp::IwiImage iwSrc = ippiGetImage(src); + ::ipp::IwiImage iwDst = ippiGetImage(dst); + ::ipp::IwiBorderType ippBorder(ippiGetBorderType(borderType)); + IwTransDirection iwTransDirection; + if(!ippBorder) + return false; + + if( !(flags & WARP_INVERSE_MAP) ) + iwTransDirection = iwTransForward; + else + iwTransDirection = iwTransInverse; + + Mat M = _M.getMat(); + double coeffs[2][3]; + for( int i = 0; i < 2; i++ ) + for( int j = 0; j < 3; j++ ) + coeffs[i][j] = M.at(i, j); + + const int threads = ippiSuggestThreadsNum(iwDst, 2); + + if(IPP_WARPAFFINE_PARALLEL && threads > 1) + { + bool ok = true; + Range range(0, (int)iwDst.m_size.height); + ipp_warpAffineParallel invoker(iwSrc, iwDst, ippInter, coeffs, ippBorder, iwTransDirection, &ok); + if(!ok) + return false; + + parallel_for_(range, invoker, threads*4); + + if(!ok) + return false; + } else { + CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, iwSrc, iwDst, coeffs, iwTransDirection, ippInter, ::ipp::IwiWarpAffineParams(), ippBorder); + } + + } + catch (const ::ipp::IwException &) + { + return false; + } + + return true; +#endif +} +#endif + namespace hal { void warpAffine(int src_type, @@ -2611,6 +2732,8 @@ void cv::warpAffine( InputArray _src, OutputArray _dst, CV_Assert( (M0.type() == CV_32F || M0.type() == CV_64F) && M0.rows == 2 && M0.cols == 3 ); M0.convertTo(matM, matM.type()); + CV_IPP_RUN_FAST(ipp_warpAffine(src, dst, interpolation, borderType, matM, flags)); + if( !(flags & WARP_INVERSE_MAP) ) { double D = M[0]*M[4] - M[1]*M[3]; From 5a0c85b3ef1f5cbe31d3370e2d41032765aebb12 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Tue, 4 Oct 2022 06:59:22 +0000 Subject: [PATCH 136/313] Refactor tests --- .../gapi/test/infer/gapi_infer_ie_test.cpp | 107 +++--------------- 1 file changed, 15 insertions(+), 92 deletions(-) diff --git a/modules/gapi/test/infer/gapi_infer_ie_test.cpp b/modules/gapi/test/infer/gapi_infer_ie_test.cpp index 6ae8368c0b..738ad6d9ad 100644 --- a/modules/gapi/test/infer/gapi_infer_ie_test.cpp +++ b/modules/gapi/test/infer/gapi_infer_ie_test.cpp @@ -3027,109 +3027,32 @@ TEST_F(AgeGenderInferTest, ThrowSyncWithNireqNotEqualToOne) { cv::compile_args(cv::gapi::networks(pp)))); } -TEST(TestAgeGenderIE, ChangeOutputPrecision) -{ - initDLDTDataPath(); - - cv::gapi::ie::detail::ParamDesc params; - params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml"); - params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin"); - params.device_id = "CPU"; - - cv::Mat in_mat(cv::Size(320, 240), CV_8UC3); - cv::randu(in_mat, 0, 255); - - cv::Mat gapi_age, gapi_gender; - - // Load & run IE network - IE::Blob::Ptr ie_age, ie_gender; - { - auto plugin = cv::gimpl::ie::wrap::getPlugin(params); - auto net = cv::gimpl::ie::wrap::readNetwork(params); - setNetParameters(net); - for (auto it : net.getOutputsInfo()) { - it.second->setPrecision(IE::Precision::U8); - } - auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params); - auto infer_request = this_network.CreateInferRequest(); - infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(in_mat)); - infer_request.Infer(); - ie_age = infer_request.GetBlob("age_conv3"); - ie_gender = infer_request.GetBlob("prob"); - } - - // Configure & run G-API - using AGInfo = std::tuple; - G_API_NET(AgeGender, , "test-age-gender"); - - cv::GMat in; - cv::GMat age, gender; - std::tie(age, gender) = cv::gapi::infer(in); - cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender)); - +TEST_F(AgeGenderInferTest, ChangeOutputPrecision) { auto pp = cv::gapi::ie::Params { - params.model_path, params.weights_path, params.device_id + m_params.model_path, m_params.weights_path, m_params.device_id }.cfgOutputLayers({ "age_conv3", "prob" }) .cfgOutputPrecision(CV_8U); - comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender), - cv::compile_args(cv::gapi::networks(pp))); - // Validate with IE itself (avoid DNN module dependency here) - normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" ); - normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output"); -} - -TEST(TestAgeGenderIE, ChangeSpecificOutputPrecison) -{ - initDLDTDataPath(); - - cv::gapi::ie::detail::ParamDesc params; - params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml"); - params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin"); - params.device_id = "CPU"; - - cv::Mat in_mat(cv::Size(320, 240), CV_8UC3); - cv::randu(in_mat, 0, 255); - - cv::Mat gapi_age, gapi_gender; - - // Load & run IE network - IE::Blob::Ptr ie_age, ie_gender; - { - auto plugin = cv::gimpl::ie::wrap::getPlugin(params); - auto net = cv::gimpl::ie::wrap::readNetwork(params); - setNetParameters(net); - - // NB: Specify precision only for "prob" output. - net.getOutputsInfo().at("prob")->setPrecision(IE::Precision::U8); - - auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params); - auto infer_request = this_network.CreateInferRequest(); - infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(in_mat)); - infer_request.Infer(); - ie_age = infer_request.GetBlob("age_conv3"); - ie_gender = infer_request.GetBlob("prob"); + for (auto it : m_net.getOutputsInfo()) { + it.second->setPrecision(IE::Precision::U8); } - // Configure & run G-API - using AGInfo = std::tuple; - G_API_NET(AgeGender, , "test-age-gender"); - - cv::GMat in; - cv::GMat age, gender; - std::tie(age, gender) = cv::gapi::infer(in); - cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender)); + buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender), + cv::compile_args(cv::gapi::networks(pp))); + validate(); +} +TEST_F(AgeGenderInferTest, ChangeSpecificOutputPrecison) { auto pp = cv::gapi::ie::Params { - params.model_path, params.weights_path, params.device_id + m_params.model_path, m_params.weights_path, m_params.device_id }.cfgOutputLayers({ "age_conv3", "prob" }) .cfgOutputPrecision({{"prob", CV_8U}}); - comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender), - cv::compile_args(cv::gapi::networks(pp))); - // Validate with IE itself (avoid DNN module dependency here) - normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" ); - normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output"); + m_net.getOutputsInfo().at("prob")->setPrecision(IE::Precision::U8); + + buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender), + cv::compile_args(cv::gapi::networks(pp))); + validate(); } } // namespace opencv_test From 1113c9ab1029f272abf184b78582d03ed0396418 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Tue, 4 Oct 2022 08:37:56 +0000 Subject: [PATCH 137/313] Support num_iters criteria for pipeline tool --- .../gapi/samples/pipeline_modeling_tool.cpp | 29 +++- .../pipeline_modeling_tool/pipeline.hpp | 132 +++++++++--------- .../pipeline_builder.hpp | 52 +++++++ .../samples/pipeline_modeling_tool/utils.hpp | 6 + 4 files changed, 150 insertions(+), 69 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index fda1c62985..ca70515083 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -347,10 +347,14 @@ int main(int argc, char* argv[]) { std::map{{"CACHE_DIR", cached_dir}}; } - const double work_time_ms = - check_and_read(fs, "work_time", "Config"); - if (work_time_ms < 0) { - throw std::logic_error("work_time must be positive"); + auto opt_work_time_ms = readOpt(fs["work_time"]); + cv::optional opt_work_time_mcs; + if (opt_work_time_ms) { + const double work_time_ms = opt_work_time_ms.value(); + if (work_time_ms < 0) { + throw std::logic_error("work_time must be positive"); + } + opt_work_time_mcs = cv::optional(utils::ms_to_mcs(work_time_ms)); } auto pipelines_fn = check_and_get_fn(fs, "Pipelines", "Config"); @@ -369,6 +373,21 @@ int main(int argc, char* argv[]) { for (const auto& name : exec_list) { const auto& pl_fn = check_and_get_fn(pipelines_fn, name, "Pipelines"); builder.setName(name); + StopCriteria::Ptr stop_criteria; + auto opt_num_iters = readOpt(pl_fn["num_iters"]); + // NB: num_iters for specific pipeline takes priority over global work_time. + if (opt_num_iters) { + stop_criteria.reset(new NumItersCriteria(opt_num_iters.value())); + } else if (opt_work_time_mcs) { + stop_criteria.reset(new ElapsedTimeCriteria(opt_work_time_mcs.value())); + } else { + throw std::logic_error( + "Failed: Pipeline " + name + " doesn't have stop criteria!\n" + "Please specify either work_time: in the config root" + " or num_iters: for specific pipeline."); + } + builder.setStopCriteria(std::move(stop_criteria)); + // NB: Set source { const auto& src_fn = check_and_get_fn(pl_fn, "source", name); @@ -464,7 +483,7 @@ int main(int argc, char* argv[]) { for (size_t i = 0; i < pipelines.size(); ++i) { threads[i] = std::thread([&, i]() { try { - pipelines[i]->run(work_time_ms); + pipelines[i]->run(); } catch (...) { eptrs[i] = std::current_exception(); } diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp index 91107c6dad..a6c2b868b4 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp @@ -40,6 +40,16 @@ std::string PerfReport::toStr(bool expand) const { return ss.str(); } +class StopCriteria { +public: + using Ptr = std::unique_ptr; + + virtual void start() = 0; + virtual void iter() = 0; + virtual bool isOver() = 0; + virtual ~StopCriteria() = default; +}; + class Pipeline { public: using Ptr = std::shared_ptr; @@ -47,28 +57,28 @@ public: Pipeline(std::string&& name, cv::GComputation&& comp, std::shared_ptr&& src, + StopCriteria::Ptr stop_criteria, cv::GCompileArgs&& args, const size_t num_outputs); void compile(); - void run(double work_time_ms); + void run(); + const PerfReport& report() const; const std::string& name() const { return m_name;} virtual ~Pipeline() = default; protected: - struct RunPerf { - int64_t elapsed = 0; - std::vector latencies; - }; - - virtual void _compile() = 0; - virtual RunPerf _run(double work_time_ms) = 0; + virtual void _compile() = 0; + virtual int64_t run_iter() = 0; + virtual void init() {}; + virtual void deinit() {}; std::string m_name; cv::GComputation m_comp; std::shared_ptr m_src; + StopCriteria::Ptr m_stop_criteria; cv::GCompileArgs m_args; size_t m_num_outputs; PerfReport m_perf; @@ -77,11 +87,13 @@ protected: Pipeline::Pipeline(std::string&& name, cv::GComputation&& comp, std::shared_ptr&& src, + StopCriteria::Ptr stop_criteria, cv::GCompileArgs&& args, const size_t num_outputs) : m_name(std::move(name)), m_comp(std::move(comp)), m_src(std::move(src)), + m_stop_criteria(std::move(stop_criteria)), m_args(std::move(args)), m_num_outputs(num_outputs) { m_perf.name = m_name; @@ -94,11 +106,23 @@ void Pipeline::compile() { }); } -void Pipeline::run(double work_time_ms) { - auto run_perf = _run(work_time_ms); +void Pipeline::run() { + using namespace std::chrono; + + init(); + auto start = high_resolution_clock::now(); + m_stop_criteria->start(); + while (true) { + m_perf.latencies.push_back(run_iter()); + m_perf.elapsed = duration_cast(high_resolution_clock::now() - start).count(); + m_stop_criteria->iter(); + + if (m_stop_criteria->isOver()) { + deinit(); + break; + } + } - m_perf.elapsed = run_perf.elapsed; - m_perf.latencies = std::move(run_perf.latencies); m_perf.avg_latency = utils::avg(m_perf.latencies); m_perf.min_latency = utils::min(m_perf.latencies); m_perf.max_latency = utils::max(m_perf.latencies); @@ -113,7 +137,6 @@ void Pipeline::run(double work_time_ms) { m_perf.throughput = (m_perf.latencies.size() / static_cast(m_perf.elapsed)) * 1000; - } const PerfReport& Pipeline::report() const { @@ -131,39 +154,31 @@ private: cv::GCompileArgs(m_args)); } - Pipeline::RunPerf _run(double work_time_ms) override { - // NB: Setup. + virtual void init() override { using namespace std::chrono; // NB: N-1 buffers + timestamp. - std::vector out_mats(m_num_outputs - 1); - int64_t start_ts = -1; - cv::GRunArgsP pipeline_outputs; - for (auto& m : out_mats) { - pipeline_outputs += cv::gout(m); + m_out_mats.resize(m_num_outputs - 1); + for (auto& m : m_out_mats) { + m_pipeline_outputs += cv::gout(m); } - pipeline_outputs += cv::gout(start_ts); + m_pipeline_outputs += cv::gout(m_start_ts); m_compiled.setSource(m_src); - - // NB: Start execution & measure performance statistics. - Pipeline::RunPerf perf; - auto start = high_resolution_clock::now(); m_compiled.start(); - while (m_compiled.pull(cv::GRunArgsP{pipeline_outputs})) { - int64_t latency = utils::timestamp() - start_ts; + } - perf.latencies.push_back(latency); - perf.elapsed = duration_cast( - high_resolution_clock::now() - start).count(); + virtual void deinit() override { + m_compiled.stop(); + } - if (perf.elapsed >= work_time_ms) { - m_compiled.stop(); - break; - } - }; - return perf; + virtual int64_t run_iter() override { + m_compiled.pull(cv::GRunArgsP{m_pipeline_outputs}); + return utils::timestamp() - m_start_ts; } cv::GStreamingCompiled m_compiled; + cv::GRunArgsP m_pipeline_outputs; + std::vector m_out_mats; + int64_t m_start_ts; }; class RegularPipeline : public Pipeline { @@ -177,37 +192,26 @@ private: cv::GCompileArgs(m_args)); } - Pipeline::RunPerf _run(double work_time_ms) override { - // NB: Setup - using namespace std::chrono; - cv::gapi::wip::Data d; - std::vector out_mats(m_num_outputs); - cv::GRunArgsP pipeline_outputs; - for (auto& m : out_mats) { - pipeline_outputs += cv::gout(m); + virtual void init() override { + m_out_mats.resize(m_num_outputs); + for (auto& m : m_out_mats) { + m_pipeline_outputs += cv::gout(m); } - - // NB: Start execution & measure performance statistics. - Pipeline::RunPerf perf; - auto start = high_resolution_clock::now(); - while (m_src->pull(d)) { - auto in_mat = cv::util::get(d); - int64_t latency = utils::measure([&]{ - m_compiled(cv::gin(in_mat), cv::GRunArgsP{pipeline_outputs}); - }); - - perf.latencies.push_back(latency); - perf.elapsed = duration_cast( - high_resolution_clock::now() - start).count(); - - if (perf.elapsed >= work_time_ms) { - break; - } - }; - return perf; } - cv::GCompiled m_compiled; + virtual int64_t run_iter() override { + using namespace std::chrono; + cv::gapi::wip::Data d; + m_src->pull(d); + auto in_mat = cv::util::get(d); + return utils::measure([&]{ + m_compiled(cv::gin(in_mat), cv::GRunArgsP{m_pipeline_outputs}); + }); + } + + cv::GCompiled m_compiled; + cv::GRunArgsP m_pipeline_outputs; + std::vector m_out_mats; }; enum class PLMode { diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index 094f4235e3..56e63ff67b 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -262,6 +262,49 @@ struct InferParams { cv::util::optional out_precision; }; +class ElapsedTimeCriteria : public StopCriteria { +public: + ElapsedTimeCriteria(int64_t work_time_mcs) : m_work_time_mcs(work_time_mcs) { }; + + void start() override { + m_start_ts = m_curr_ts = utils::timestamp(); + } + + void iter() override { + m_curr_ts = utils::timestamp(); + } + + bool isOver() override { + return (m_curr_ts - m_start_ts) >= m_work_time_mcs; + } + +private: + int64_t m_work_time_mcs; + int64_t m_start_ts = -1; + int64_t m_curr_ts = -1; +}; + +class NumItersCriteria : public StopCriteria { +public: + NumItersCriteria(int64_t num_iters) : m_num_iters(num_iters) { }; + + void start() override { + m_curr_iters = 0; + } + + void iter() override { + ++m_curr_iters; + } + + bool isOver() override { + return m_curr_iters == m_num_iters; + } + +private: + int64_t m_num_iters; + int64_t m_curr_iters = 0; +}; + class PipelineBuilder { public: PipelineBuilder(); @@ -279,6 +322,7 @@ public: void setDumpFilePath(const std::string& dump); void setQueueCapacity(const size_t qc); void setName(const std::string& name); + void setStopCriteria(StopCriteria::Ptr stop_criteria); Pipeline::Ptr build(); @@ -306,6 +350,7 @@ private: std::shared_ptr src; PLMode mode = PLMode::STREAMING; std::string name; + StopCriteria::Ptr stop_criteria; }; std::unique_ptr m_state; @@ -432,6 +477,10 @@ void PipelineBuilder::setName(const std::string& name) { m_state->name = name; } +void PipelineBuilder::setStopCriteria(StopCriteria::Ptr stop_criteria) { + m_state->stop_criteria = std::move(stop_criteria); +} + static bool visit(Node::Ptr node, std::vector& sorted, std::unordered_map& visited) { @@ -590,6 +639,7 @@ Pipeline::Ptr PipelineBuilder::construct() { } } + GAPI_Assert(m_state->stop_criteria); if (m_state->mode == PLMode::STREAMING) { GAPI_Assert(graph_inputs.size() == 1); GAPI_Assert(cv::util::holds_alternative(graph_inputs[0])); @@ -605,6 +655,7 @@ Pipeline::Ptr PipelineBuilder::construct() { cv::GProtoInputArgs{graph_inputs}, cv::GProtoOutputArgs{graph_outputs}), std::move(m_state->src), + std::move(m_state->stop_criteria), std::move(m_state->compile_args), graph_outputs.size()); } @@ -614,6 +665,7 @@ Pipeline::Ptr PipelineBuilder::construct() { cv::GProtoInputArgs{graph_inputs}, cv::GProtoOutputArgs{graph_outputs}), std::move(m_state->src), + std::move(m_state->stop_criteria), std::move(m_state->compile_args), graph_outputs.size()); } diff --git a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp index 6eb6bb7202..c0f0897c35 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/utils.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/utils.hpp @@ -119,6 +119,12 @@ T min(const std::vector& vec) { return *std::min_element(vec.begin(), vec.end()); } +template +int64_t ms_to_mcs(T ms) { + using namespace std::chrono; + return duration_cast(duration(ms)).count(); +} + } // namespace utils #endif // OPENCV_GAPI_PIPELINE_MODELING_TOOL_UTILS_HPP From ab5279f4adc855a3c701fb4d3349c8cb552760a9 Mon Sep 17 00:00:00 2001 From: catree Date: Tue, 20 Sep 2022 13:39:31 +0200 Subject: [PATCH 138/313] Add an image to illustrate camera poses interpolation for the small homography exercise. --- .../features2d/homography/homography.markdown | 8 +++++--- .../homography_camera_poses_interpolation.jpg | Bin 0 -> 80375 bytes 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 doc/tutorials/features2d/homography/images/homography_camera_poses_interpolation.jpg diff --git a/doc/tutorials/features2d/homography/homography.markdown b/doc/tutorials/features2d/homography/homography.markdown index b8c17c0856..c70c313bb8 100644 --- a/doc/tutorials/features2d/homography/homography.markdown +++ b/doc/tutorials/features2d/homography/homography.markdown @@ -412,15 +412,17 @@ homography from camera displacement: The homography matrices are similar. If we compare the image 1 warped using both homography matrices: -![Left: image warped using the homography estimated. Right: using the homography computed from the camera displacement](images/homography_camera_displacement_compare.jpg) +![Left: image warped using the estimated homography. Right: using the homography computed from the camera displacement.](images/homography_camera_displacement_compare.jpg) Visually, it is hard to distinguish a difference between the result image from the homography computed from the camera displacement and the one estimated with @ref cv::findHomography function. #### Exercise -This demo shows you how to compute the homography transformation from two camera poses. Try to perform the same operations, but by computing N inter homography this time. Instead of computing one homography to directly warp the source image to the desired camera viewpoint, perform N warping operations to the see the different transformations operating. +This demo shows you how to compute the homography transformation from two camera poses. Try to perform the same operations, but by computing N inter homography this time. Instead of computing one homography to directly warp the source image to the desired camera viewpoint, perform N warping operations to see the different transformations operating. -You should get something similar to this video: +You should get something similar to the following: + +![The first three images show the source image warped at three different interpolated camera viewpoints. The 4th image shows the "error image" between the warped source image at the final camera viewpoint and the desired image.](images/homography_camera_poses_interpolation.jpg) ### Demo 4: Decompose the homography matrix {#tutorial_homography_Demo4} diff --git a/doc/tutorials/features2d/homography/images/homography_camera_poses_interpolation.jpg b/doc/tutorials/features2d/homography/images/homography_camera_poses_interpolation.jpg new file mode 100644 index 0000000000000000000000000000000000000000..353260c88048f22a9fafd7fd829e24b1f3d81373 GIT binary patch literal 80375 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%0Ahx^jShx+jzem9h*r>|{2By? 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

#a#`=$nq+YU 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𝔐`b}7Zd28mWie=(@66*>BjkCHWH^Rf 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 literal 0 HcmV?d00001 From af9332dfaf3f46eea085f4e2efc72fafeeb69a2f Mon Sep 17 00:00:00 2001 From: "Alessandro de Oliveira Faria (A.K.A. CABELO)" Date: Mon, 12 Sep 2022 00:53:20 -0300 Subject: [PATCH 139/313] OAK Information --- .../gapi/oak_devices/oak_devices.markdown | 26 ++++++++++++++++++ doc/tutorials/gapi/oak_devices/pics/oak.jpg | Bin 0 -> 163279 bytes .../gapi/table_of_content_gapi.markdown | 11 ++++++++ 3 files changed, 37 insertions(+) create mode 100644 doc/tutorials/gapi/oak_devices/oak_devices.markdown create mode 100644 doc/tutorials/gapi/oak_devices/pics/oak.jpg diff --git a/doc/tutorials/gapi/oak_devices/oak_devices.markdown b/doc/tutorials/gapi/oak_devices/oak_devices.markdown new file mode 100644 index 0000000000..4a635fdb93 --- /dev/null +++ b/doc/tutorials/gapi/oak_devices/oak_devices.markdown @@ -0,0 +1,26 @@ +Using DepthAI Hardware /OAK depth sensors {#tutorial_gapi_oak_devices} +======================================================================================= + +@tableofcontents + +@prev_tutorial{tutorial_gapi_face_beautification} + +![hardwares](pics/oak.jpg) + +Depth sensors compatible with Luxonis DepthAI library are supported through OpenCV Graph API (or G-API) module. RGB image and some other formats of output can be retrieved by using familiar interface of G-API module. + +In order to use DepthAI sensor with OpenCV you should do the following preliminary steps: +-# Install Intel DepthAI library/depthai-core (from here ). + +-# Configure OpenCV with DepthAI library support by setting WITH_OAK flag in CMake. If DepthAI library is found in install folders OpenCV will be built with depthai-core (see a status WITH_OAK in CMake log). + +-# Build OpenCV. + +Source code +----------- + +You can find source code how to process heterogeneous graphs in the `modules/gapi/samples/oak_basic_infer.cpp` of the OpenCV source code library. + +@add_toggle_cpp + @include modules/gapi/samples/oak_basic_infer.cpp +@end_toggle diff --git a/doc/tutorials/gapi/oak_devices/pics/oak.jpg b/doc/tutorials/gapi/oak_devices/pics/oak.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9ad30ce1e9bb3fedf00fb94c9d06275c5d6116b6 GIT binary patch literal 163279 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 wD;sAc~#fB?24(wx@z}ZIJJ?0&ZXsKj^ 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

7xO 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�$#-^2GD>wV4ar{Lck!jgNDn==P2mR% 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

;e(1_m+uFsk34Lr_U7=6KCu8DS8QCc= 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 N>T-}{a0`84|JkQvJ`n%_ literal 0 HcmV?d00001 diff --git a/doc/tutorials/gapi/table_of_content_gapi.markdown b/doc/tutorials/gapi/table_of_content_gapi.markdown index f9d7b0389a..1b33172b9e 100644 --- a/doc/tutorials/gapi/table_of_content_gapi.markdown +++ b/doc/tutorials/gapi/table_of_content_gapi.markdown @@ -40,3 +40,14 @@ how G-API module can be used for that. In this tutorial we build a complex hybrid Computer Vision/Deep Learning video processing pipeline with G-API. + + +- @subpage tutorial_gapi_oak_devices + + *Languages:* C++ + + *Compatibility:* \> OpenCV 4.6 + + *Author:* Alessandro de Oliveira Faria (A.K.A. CABELO) + + In this tutorial we showed how to use the Luxonis DepthAI library with G-API. From 0ae126d3b8c5369a80909edf53e495783effb196 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 4 Oct 2022 15:45:44 +0300 Subject: [PATCH 140/313] Typos and formating fixes. --- doc/tutorials/gapi/oak_devices/oak_devices.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/tutorials/gapi/oak_devices/oak_devices.markdown b/doc/tutorials/gapi/oak_devices/oak_devices.markdown index 4a635fdb93..6046cdef25 100644 --- a/doc/tutorials/gapi/oak_devices/oak_devices.markdown +++ b/doc/tutorials/gapi/oak_devices/oak_devices.markdown @@ -1,18 +1,18 @@ -Using DepthAI Hardware /OAK depth sensors {#tutorial_gapi_oak_devices} -======================================================================================= +Using DepthAI Hardware / OAK depth sensors {#tutorial_gapi_oak_devices} +======================================================================= @tableofcontents @prev_tutorial{tutorial_gapi_face_beautification} -![hardwares](pics/oak.jpg) +![Oak-D and Oak-D-Light cameras](pics/oak.jpg) Depth sensors compatible with Luxonis DepthAI library are supported through OpenCV Graph API (or G-API) module. RGB image and some other formats of output can be retrieved by using familiar interface of G-API module. In order to use DepthAI sensor with OpenCV you should do the following preliminary steps: --# Install Intel DepthAI library/depthai-core (from here ). +-# Install Luxonis DepthAI library [depthai-core](https://github.com/luxonis/depthai-core). --# Configure OpenCV with DepthAI library support by setting WITH_OAK flag in CMake. If DepthAI library is found in install folders OpenCV will be built with depthai-core (see a status WITH_OAK in CMake log). +-# Configure OpenCV with DepthAI library support by setting `WITH_OAK` flag in CMake. If DepthAI library is found in install folders OpenCV will be built with depthai-core (see a status `WITH_OAK` in CMake log). -# Build OpenCV. From 9f88a6587386136f574abfdca87ee41f4c506311 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Tue, 4 Oct 2022 12:10:49 +0000 Subject: [PATCH 141/313] Fix functional pipeline tool tests --- .../pipeline_modeling_tool/pipeline.hpp | 8 ++-- .../pipeline_builder.hpp | 4 +- .../test_pipeline_modeling_tool.py | 37 ++++++++++++++----- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp index a6c2b868b4..440f624b79 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp @@ -44,9 +44,9 @@ class StopCriteria { public: using Ptr = std::unique_ptr; - virtual void start() = 0; - virtual void iter() = 0; - virtual bool isOver() = 0; + virtual void start() = 0; + virtual void iter() = 0; + virtual bool done() = 0; virtual ~StopCriteria() = default; }; @@ -117,7 +117,7 @@ void Pipeline::run() { m_perf.elapsed = duration_cast(high_resolution_clock::now() - start).count(); m_stop_criteria->iter(); - if (m_stop_criteria->isOver()) { + if (m_stop_criteria->done()) { deinit(); break; } diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index 56e63ff67b..3615032ead 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -274,7 +274,7 @@ public: m_curr_ts = utils::timestamp(); } - bool isOver() override { + bool done() override { return (m_curr_ts - m_start_ts) >= m_work_time_mcs; } @@ -296,7 +296,7 @@ public: ++m_curr_iters; } - bool isOver() override { + bool done() override { return m_curr_iters == m_num_iters; } diff --git a/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py b/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py index d56a0399e9..43e979ed9a 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py +++ b/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py @@ -26,14 +26,6 @@ def test_error_no_config_exists(): assert 'Failed to open config file: not_existing_cfg.yml' in out -def test_error_no_work_time(): - cfg_file = """\"%YAML:1.0\" """ - - exec_str = '{} --cfg={}'.format(pipeline_modeling_tool, cfg_file) - out = get_output(exec_str) - assert out.startswith('Config must contain field: work_time') - - def test_error_work_time_not_positive(): cfg_file = """\"%YAML:1.0 work_time: -1\" """ @@ -77,7 +69,8 @@ def test_error_no_source(): cfg_file = """\"%YAML:1.0 work_time: 1000 Pipelines: - PL1:\" """ + PL1: + queue_capacity: 1\" """ exec_str = '{} --cfg={}'.format(pipeline_modeling_tool, cfg_file) out = get_output(exec_str) @@ -982,3 +975,29 @@ Pipelines: check(cfg_file, -3) check(cfg_file, 0) + + +def test_error_no_worktime_and_num_iters(): + cfg_file = """\"%YAML:1.0 +Pipelines: + PL1: + source: + name: 'Src' + latency: 20 + output: + dims: [1,1] + precision: 'U8' + nodes: + - name: 'Node0' + type: 'Dummy' + time: 0.2 + output: + dims: [1,2,3,4] + precision: 'U8' + edges: + - from: 'Src' + to: 'Node0'\" """ + + exec_str = '{} --cfg={}'.format(pipeline_modeling_tool, cfg_file) + out = get_output(exec_str) + assert out.startswith('Failed: Pipeline PL1 doesn\'t have stop criteria!') From 839321642eb106e9bad0e38e9ddf1a8219f3cf94 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Wed, 5 Oct 2022 12:01:45 +0000 Subject: [PATCH 142/313] Move impl from class --- .../pipeline_builder.hpp | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index 3615032ead..c6b8fd94b7 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -264,19 +264,11 @@ struct InferParams { class ElapsedTimeCriteria : public StopCriteria { public: - ElapsedTimeCriteria(int64_t work_time_mcs) : m_work_time_mcs(work_time_mcs) { }; + ElapsedTimeCriteria(int64_t work_time_mcs); - void start() override { - m_start_ts = m_curr_ts = utils::timestamp(); - } - - void iter() override { - m_curr_ts = utils::timestamp(); - } - - bool done() override { - return (m_curr_ts - m_start_ts) >= m_work_time_mcs; - } + void start() override; + void iter() override; + bool done() override; private: int64_t m_work_time_mcs; @@ -284,27 +276,51 @@ private: int64_t m_curr_ts = -1; }; +ElapsedTimeCriteria::ElapsedTimeCriteria(int64_t work_time_mcs) + : m_work_time_mcs(work_time_mcs) { +}; + +void ElapsedTimeCriteria::start() { + m_start_ts = m_curr_ts = utils::timestamp(); +} + +void ElapsedTimeCriteria::iter() { + m_curr_ts = utils::timestamp(); +} + +bool ElapsedTimeCriteria::done() { + return (m_curr_ts - m_start_ts) >= m_work_time_mcs; +} + class NumItersCriteria : public StopCriteria { public: - NumItersCriteria(int64_t num_iters) : m_num_iters(num_iters) { }; + NumItersCriteria(int64_t num_iters); - void start() override { - m_curr_iters = 0; - } - - void iter() override { - ++m_curr_iters; - } - - bool done() override { - return m_curr_iters == m_num_iters; - } + void start() override; + void iter() override; + bool done() override; private: int64_t m_num_iters; int64_t m_curr_iters = 0; }; +NumItersCriteria::NumItersCriteria(int64_t num_iters) + : m_num_iters(num_iters) { +} + +void NumItersCriteria::start() { + m_curr_iters = 0; +} + +void NumItersCriteria::iter() { + ++m_curr_iters; +} + +bool NumItersCriteria::done() { + return m_curr_iters == m_num_iters; +} + class PipelineBuilder { public: PipelineBuilder(); From d480e2e51b3c89b6ce3750ed03663295be190227 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 5 Oct 2022 21:51:39 +0300 Subject: [PATCH 143/313] cmake(opt): force separate targets for dispatched code - PCH may not pass compilation flags properly --- cmake/OpenCVCompilerOptimizations.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index 1e0e812afc..598389bd34 100644 --- a/cmake/OpenCVCompilerOptimizations.cmake +++ b/cmake/OpenCVCompilerOptimizations.cmake @@ -680,7 +680,7 @@ macro(ocv_compiler_optimization_process_sources SOURCES_VAR_NAME LIBS_VAR_NAME T if(fname_LOWER MATCHES "\\.${OPT_LOWER}\\.cpp$") #message("${fname} BASELINE-${OPT}") set(__opt_found 1) - list(APPEND __result "${fname}") + list(APPEND __result_${OPT} "${fname}") break() endif() endforeach() @@ -714,7 +714,7 @@ macro(ocv_compiler_optimization_process_sources SOURCES_VAR_NAME LIBS_VAR_NAME T endif() endforeach() - foreach(OPT ${CPU_DISPATCH_FINAL}) + foreach(OPT ${CPU_BASELINE_FINAL} ${CPU_DISPATCH_FINAL}) if(__result_${OPT}) #message("${OPT}: ${__result_${OPT}}") if(CMAKE_GENERATOR MATCHES "^Visual" From 40ae06091d34c38e5b1291bc9df482e70b18afe4 Mon Sep 17 00:00:00 2001 From: Christine Poerschke <6458642+cpoerschke@users.noreply.github.com> Date: Wed, 5 Oct 2022 21:51:26 +0100 Subject: [PATCH 144/313] add cvGetPropVisible_COCOA --- modules/highgui/src/precomp.hpp | 1 + modules/highgui/src/window.cpp | 2 ++ modules/highgui/src/window_cocoa.mm | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/modules/highgui/src/precomp.hpp b/modules/highgui/src/precomp.hpp index 574eaba773..9f0ea59b60 100644 --- a/modules/highgui/src/precomp.hpp +++ b/modules/highgui/src/precomp.hpp @@ -114,6 +114,7 @@ double cvGetOpenGlProp_W32(const char* name); double cvGetOpenGlProp_GTK(const char* name); double cvGetPropVisible_W32(const char* name); +double cvGetPropVisible_COCOA(const char* name); double cvGetPropTopmost_W32(const char* name); double cvGetPropTopmost_COCOA(const char* name); diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index dd70fd2456..2e528fe8e5 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -366,6 +366,8 @@ CV_IMPL double cvGetWindowProperty(const char* name, int prop_id) return cvGetPropVisible_QT(name); #elif defined(HAVE_WIN32UI) return cvGetPropVisible_W32(name); + #elif defined(HAVE_COCOA) + return cvGetPropVisible_COCOA(name); #else return -1; #endif diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index 3f46d47de7..07c1ed86d2 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -740,6 +740,31 @@ void cvSetModeWindow_COCOA( const char* name, double prop_value ) __END__; } +double cvGetPropVisible_COCOA(const char* name) +{ + double result = -1; + CVWindow* window = nil; + + CV_FUNCNAME("cvGetPropVisible_COCOA"); + + __BEGIN__; + if (name == NULL) + { + CV_ERROR(CV_StsNullPtr, "NULL name string"); + } + + window = cvGetWindow(name); + if (window == NULL) + { + CV_ERROR(CV_StsNullPtr, "NULL window"); + } + + result = window.isVisible ? 1 : 0; + + __END__; + return result; +} + double cvGetPropTopmost_COCOA(const char* name) { double result = -1; From 5f50e7bafe57152663a354f1639962becb99bd78 Mon Sep 17 00:00:00 2001 From: TolyaTalamanov Date: Thu, 6 Oct 2022 09:41:30 +0000 Subject: [PATCH 145/313] Criteria -> Criterion --- .../gapi/samples/pipeline_modeling_tool.cpp | 10 ++--- .../pipeline_modeling_tool/pipeline.hpp | 20 +++++----- .../pipeline_builder.hpp | 38 +++++++++---------- .../test_pipeline_modeling_tool.py | 2 +- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/modules/gapi/samples/pipeline_modeling_tool.cpp b/modules/gapi/samples/pipeline_modeling_tool.cpp index ca70515083..7c202642a9 100644 --- a/modules/gapi/samples/pipeline_modeling_tool.cpp +++ b/modules/gapi/samples/pipeline_modeling_tool.cpp @@ -373,20 +373,20 @@ int main(int argc, char* argv[]) { for (const auto& name : exec_list) { const auto& pl_fn = check_and_get_fn(pipelines_fn, name, "Pipelines"); builder.setName(name); - StopCriteria::Ptr stop_criteria; + StopCriterion::Ptr stop_criterion; auto opt_num_iters = readOpt(pl_fn["num_iters"]); // NB: num_iters for specific pipeline takes priority over global work_time. if (opt_num_iters) { - stop_criteria.reset(new NumItersCriteria(opt_num_iters.value())); + stop_criterion.reset(new NumItersCriterion(opt_num_iters.value())); } else if (opt_work_time_mcs) { - stop_criteria.reset(new ElapsedTimeCriteria(opt_work_time_mcs.value())); + stop_criterion.reset(new ElapsedTimeCriterion(opt_work_time_mcs.value())); } else { throw std::logic_error( - "Failed: Pipeline " + name + " doesn't have stop criteria!\n" + "Failed: Pipeline " + name + " doesn't have stop criterion!\n" "Please specify either work_time: in the config root" " or num_iters: for specific pipeline."); } - builder.setStopCriteria(std::move(stop_criteria)); + builder.setStopCriterion(std::move(stop_criterion)); // NB: Set source { diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp index 440f624b79..ac192cba52 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp @@ -40,14 +40,14 @@ std::string PerfReport::toStr(bool expand) const { return ss.str(); } -class StopCriteria { +class StopCriterion { public: - using Ptr = std::unique_ptr; + using Ptr = std::unique_ptr; virtual void start() = 0; virtual void iter() = 0; virtual bool done() = 0; - virtual ~StopCriteria() = default; + virtual ~StopCriterion() = default; }; class Pipeline { @@ -57,7 +57,7 @@ public: Pipeline(std::string&& name, cv::GComputation&& comp, std::shared_ptr&& src, - StopCriteria::Ptr stop_criteria, + StopCriterion::Ptr stop_criterion, cv::GCompileArgs&& args, const size_t num_outputs); @@ -78,7 +78,7 @@ protected: std::string m_name; cv::GComputation m_comp; std::shared_ptr m_src; - StopCriteria::Ptr m_stop_criteria; + StopCriterion::Ptr m_stop_criterion; cv::GCompileArgs m_args; size_t m_num_outputs; PerfReport m_perf; @@ -87,13 +87,13 @@ protected: Pipeline::Pipeline(std::string&& name, cv::GComputation&& comp, std::shared_ptr&& src, - StopCriteria::Ptr stop_criteria, + StopCriterion::Ptr stop_criterion, cv::GCompileArgs&& args, const size_t num_outputs) : m_name(std::move(name)), m_comp(std::move(comp)), m_src(std::move(src)), - m_stop_criteria(std::move(stop_criteria)), + m_stop_criterion(std::move(stop_criterion)), m_args(std::move(args)), m_num_outputs(num_outputs) { m_perf.name = m_name; @@ -111,13 +111,13 @@ void Pipeline::run() { init(); auto start = high_resolution_clock::now(); - m_stop_criteria->start(); + m_stop_criterion->start(); while (true) { m_perf.latencies.push_back(run_iter()); m_perf.elapsed = duration_cast(high_resolution_clock::now() - start).count(); - m_stop_criteria->iter(); + m_stop_criterion->iter(); - if (m_stop_criteria->done()) { + if (m_stop_criterion->done()) { deinit(); break; } diff --git a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp index c6b8fd94b7..6ac6374f07 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp +++ b/modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp @@ -262,9 +262,9 @@ struct InferParams { cv::util::optional out_precision; }; -class ElapsedTimeCriteria : public StopCriteria { +class ElapsedTimeCriterion : public StopCriterion { public: - ElapsedTimeCriteria(int64_t work_time_mcs); + ElapsedTimeCriterion(int64_t work_time_mcs); void start() override; void iter() override; @@ -276,25 +276,25 @@ private: int64_t m_curr_ts = -1; }; -ElapsedTimeCriteria::ElapsedTimeCriteria(int64_t work_time_mcs) +ElapsedTimeCriterion::ElapsedTimeCriterion(int64_t work_time_mcs) : m_work_time_mcs(work_time_mcs) { }; -void ElapsedTimeCriteria::start() { +void ElapsedTimeCriterion::start() { m_start_ts = m_curr_ts = utils::timestamp(); } -void ElapsedTimeCriteria::iter() { +void ElapsedTimeCriterion::iter() { m_curr_ts = utils::timestamp(); } -bool ElapsedTimeCriteria::done() { +bool ElapsedTimeCriterion::done() { return (m_curr_ts - m_start_ts) >= m_work_time_mcs; } -class NumItersCriteria : public StopCriteria { +class NumItersCriterion : public StopCriterion { public: - NumItersCriteria(int64_t num_iters); + NumItersCriterion(int64_t num_iters); void start() override; void iter() override; @@ -305,19 +305,19 @@ private: int64_t m_curr_iters = 0; }; -NumItersCriteria::NumItersCriteria(int64_t num_iters) +NumItersCriterion::NumItersCriterion(int64_t num_iters) : m_num_iters(num_iters) { } -void NumItersCriteria::start() { +void NumItersCriterion::start() { m_curr_iters = 0; } -void NumItersCriteria::iter() { +void NumItersCriterion::iter() { ++m_curr_iters; } -bool NumItersCriteria::done() { +bool NumItersCriterion::done() { return m_curr_iters == m_num_iters; } @@ -338,7 +338,7 @@ public: void setDumpFilePath(const std::string& dump); void setQueueCapacity(const size_t qc); void setName(const std::string& name); - void setStopCriteria(StopCriteria::Ptr stop_criteria); + void setStopCriterion(StopCriterion::Ptr stop_criterion); Pipeline::Ptr build(); @@ -366,7 +366,7 @@ private: std::shared_ptr src; PLMode mode = PLMode::STREAMING; std::string name; - StopCriteria::Ptr stop_criteria; + StopCriterion::Ptr stop_criterion; }; std::unique_ptr m_state; @@ -493,8 +493,8 @@ void PipelineBuilder::setName(const std::string& name) { m_state->name = name; } -void PipelineBuilder::setStopCriteria(StopCriteria::Ptr stop_criteria) { - m_state->stop_criteria = std::move(stop_criteria); +void PipelineBuilder::setStopCriterion(StopCriterion::Ptr stop_criterion) { + m_state->stop_criterion = std::move(stop_criterion); } static bool visit(Node::Ptr node, @@ -655,7 +655,7 @@ Pipeline::Ptr PipelineBuilder::construct() { } } - GAPI_Assert(m_state->stop_criteria); + GAPI_Assert(m_state->stop_criterion); if (m_state->mode == PLMode::STREAMING) { GAPI_Assert(graph_inputs.size() == 1); GAPI_Assert(cv::util::holds_alternative(graph_inputs[0])); @@ -671,7 +671,7 @@ Pipeline::Ptr PipelineBuilder::construct() { cv::GProtoInputArgs{graph_inputs}, cv::GProtoOutputArgs{graph_outputs}), std::move(m_state->src), - std::move(m_state->stop_criteria), + std::move(m_state->stop_criterion), std::move(m_state->compile_args), graph_outputs.size()); } @@ -681,7 +681,7 @@ Pipeline::Ptr PipelineBuilder::construct() { cv::GProtoInputArgs{graph_inputs}, cv::GProtoOutputArgs{graph_outputs}), std::move(m_state->src), - std::move(m_state->stop_criteria), + std::move(m_state->stop_criterion), std::move(m_state->compile_args), graph_outputs.size()); } diff --git a/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py b/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py index 43e979ed9a..d1701d9ad2 100644 --- a/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py +++ b/modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py @@ -1000,4 +1000,4 @@ Pipelines: exec_str = '{} --cfg={}'.format(pipeline_modeling_tool, cfg_file) out = get_output(exec_str) - assert out.startswith('Failed: Pipeline PL1 doesn\'t have stop criteria!') + assert out.startswith('Failed: Pipeline PL1 doesn\'t have stop criterion!') From 07c795408d5deb64f5f7369801407b00bd8e6d49 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Fri, 7 Oct 2022 01:40:50 +0200 Subject: [PATCH 146/313] doc fix --- modules/calib3d/include/opencv2/calib3d.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index b8634bde60..09fe9f83ec 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -619,7 +619,7 @@ public: @param param the current vector of parameters @param err output vector of errors: err_i = actual_f_i - ideal_f_i - @param J output Jacobian: J_ij = d(err_i)/d(param_j) + @param J output Jacobian: J_ij = d(ideal_f_i)/d(param_j) when J=noArray(), it means that it does not need to be computed. Dimensionality of error vector and param vector can be different. From a3ebafbdeb2a4de795fd34ae574939195fd24ae1 Mon Sep 17 00:00:00 2001 From: Petr Glotov <1294313+pglotov@users.noreply.github.com> Date: Fri, 7 Oct 2022 09:07:51 -0700 Subject: [PATCH 147/313] Merge pull request #21942 from pglotov:add-blob-contours added blob contours to blob detector * added blob contours * Fixed Java regression test after new parameter addition to SimpleBlobDetector. * Added stub implementation of SimpleBlobDetector::getBlobContours to presume source API compatibility. --- .../features2d/include/opencv2/features2d.hpp | 7 ++ .../test/SIMPLEBLOBFeatureDetectorTest.java | 2 +- modules/features2d/src/blobdetector.cpp | 72 +++++++++++++++++-- modules/features2d/src/keypoint.cpp | 23 ++++++ modules/features2d/test/test_blobdetector.cpp | 24 +++++++ 5 files changed, 122 insertions(+), 6 deletions(-) diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index 952d24ca0c..03f776d578 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -107,6 +107,10 @@ public: * Remove keypoints from some image by mask for pixels of this image. */ static void runByPixelsMask( std::vector& keypoints, const Mat& mask ); + /* + * Remove objects from some image and a vector of points by mask for pixels of this image + */ + static void runByPixelsMask2VectorPoint(std::vector &keypoints, std::vector > &removeFrom, const Mat &mask); /* * Remove duplicated keypoints. */ @@ -719,6 +723,8 @@ public: CV_PROP_RW bool filterByConvexity; CV_PROP_RW float minConvexity, maxConvexity; + CV_PROP_RW bool collectContours; + void read( const FileNode& fn ); void write( FileStorage& fs ) const; }; @@ -726,6 +732,7 @@ public: CV_WRAP static Ptr create(const SimpleBlobDetector::Params ¶meters = SimpleBlobDetector::Params()); CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; + CV_WRAP virtual const std::vector >& getBlobContours() const; }; //! @} features2d_main diff --git a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java index 1d8517bbd4..34c0d94a76 100644 --- a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java @@ -112,7 +112,7 @@ public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase { detector.write(filename); - String truth = "\n\n3\n10.\n50.\n220.\n2\n10.\n1\n0\n1\n25.\n5000.\n0\n8.0000001192092896e-01\n3.4028234663852886e+38\n1\n1.0000000149011612e-01\n3.4028234663852886e+38\n1\n9.4999998807907104e-01\n3.4028234663852886e+38\n\n"; + String truth = "\n\n3\n10.\n50.\n220.\n2\n10.\n1\n0\n1\n25.\n5000.\n0\n8.0000001192092896e-01\n3.4028234663852886e+38\n1\n1.0000000149011612e-01\n3.4028234663852886e+38\n1\n9.4999998807907104e-01\n3.4028234663852886e+38\n0\n\n"; assertEquals(truth, readFile(filename)); } } diff --git a/modules/features2d/src/blobdetector.cpp b/modules/features2d/src/blobdetector.cpp index fb79831807..9fb4ac4974 100644 --- a/modules/features2d/src/blobdetector.cpp +++ b/modules/features2d/src/blobdetector.cpp @@ -56,6 +56,12 @@ namespace cv { +// TODO: To be removed in 5.x branch +const std::vector >& SimpleBlobDetector::getBlobContours() const +{ + CV_Error(Error::StsNotImplemented, "Method SimpleBlobDetector::getBlobContours() is not implemented"); +} + class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector { public: @@ -74,9 +80,12 @@ protected: }; virtual void detect( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) CV_OVERRIDE; - virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector

¢ers) const; + virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector
¢ers, + std::vector > &contours, std::vector &moments) const; + virtual const std::vector >& getBlobContours() const CV_OVERRIDE; Params params; + std::vector > blobContours; }; /* @@ -110,6 +119,8 @@ SimpleBlobDetector::Params::Params() //minConvexity = 0.8; minConvexity = 0.95f; maxConvexity = std::numeric_limits::max(); + + collectContours = false; } void SimpleBlobDetector::Params::read(const cv::FileNode& fn ) @@ -139,6 +150,8 @@ void SimpleBlobDetector::Params::read(const cv::FileNode& fn ) filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false; minConvexity = fn["minConvexity"]; maxConvexity = fn["maxConvexity"]; + + collectContours = (int)fn["collectContours"] != 0 ? true : false; } void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const @@ -168,6 +181,8 @@ void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const fs << "filterByConvexity" << (int)filterByConvexity; fs << "minConvexity" << minConvexity; fs << "maxConvexity" << maxConvexity; + + fs << "collectContours" << (int)collectContours; } SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params ¶meters) : @@ -186,13 +201,16 @@ void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const params.write(fs); } -void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector
¢ers) const +void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector
¢ers, + std::vector > &contoursOut, std::vector &momentss) const { CV_INSTRUMENT_REGION(); Mat image = _image.getMat(), binaryImage = _binaryImage.getMat(); CV_UNUSED(image); centers.clear(); + contoursOut.clear(); + momentss.clear(); std::vector < std::vector > contours; findContours(binaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE); @@ -291,7 +309,11 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag } centers.push_back(center); - + if (params.collectContours) + { + contoursOut.push_back(contours[contourIdx]); + momentss.push_back(moms); + } #ifdef DEBUG_BLOB_DETECTOR circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 ); @@ -308,6 +330,8 @@ void SimpleBlobDetectorImpl::detect(InputArray image, std::vector& CV_INSTRUMENT_REGION(); keypoints.clear(); + blobContours.clear(); + CV_Assert(params.minRepeatability != 0); Mat grayscaleImage; if (image.channels() == 3 || image.channels() == 4) @@ -333,14 +357,19 @@ void SimpleBlobDetectorImpl::detect(InputArray image, std::vector& } std::vector < std::vector
> centers; + std::vector momentss; for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep) { Mat binarizedImage; threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY); std::vector < Center > curCenters; - findBlobs(grayscaleImage, binarizedImage, curCenters); + std::vector > curContours; + std::vector curMomentss; + findBlobs(grayscaleImage, binarizedImage, curCenters, curContours, curMomentss); std::vector < std::vector
> newCenters; + std::vector > newContours; + std::vector newMomentss; for (size_t i = 0; i < curCenters.size(); i++) { bool isNew = true; @@ -358,15 +387,37 @@ void SimpleBlobDetectorImpl::detect(InputArray image, std::vector& centers[j][k] = centers[j][k-1]; k--; } + + if (params.collectContours) + { + if (curCenters[i].confidence > centers[j][k].confidence + || (curCenters[i].confidence == centers[j][k].confidence && curMomentss[i].m00 > momentss[j].m00)) + { + blobContours[j] = curContours[i]; + momentss[j] = curMomentss[i]; + } + } centers[j][k] = curCenters[i]; break; } } if (isNew) + { newCenters.push_back(std::vector
(1, curCenters[i])); + if (params.collectContours) + { + newContours.push_back(curContours[i]); + newMomentss.push_back(curMomentss[i]); + } + } } std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers)); + if (params.collectContours) + { + std::copy(newContours.begin(), newContours.end(), std::back_inserter(blobContours)); + std::copy(newMomentss.begin(), newMomentss.end(), std::back_inserter(momentss)); + } } for (size_t i = 0; i < centers.size(); i++) @@ -387,10 +438,21 @@ void SimpleBlobDetectorImpl::detect(InputArray image, std::vector& if (!mask.empty()) { - KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat()); + if (params.collectContours) + { + KeyPointsFilter::runByPixelsMask2VectorPoint(keypoints, blobContours, mask.getMat()); + } + else + { + KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat()); + } } } +const std::vector >& SimpleBlobDetectorImpl::getBlobContours() const { + return blobContours; +} + Ptr SimpleBlobDetector::create(const SimpleBlobDetector::Params& params) { return makePtr(params); diff --git a/modules/features2d/src/keypoint.cpp b/modules/features2d/src/keypoint.cpp index 21d9eb30f7..4d2007f6d7 100644 --- a/modules/features2d/src/keypoint.cpp +++ b/modules/features2d/src/keypoint.cpp @@ -165,6 +165,29 @@ void KeyPointsFilter::runByPixelsMask( std::vector& keypoints, const M keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end()); } +/* + * Remove objects from some image and a vector by mask for pixels of this image + */ +template +void runByPixelsMask2(std::vector &keypoints, std::vector &removeFrom, const Mat &mask) +{ + if (mask.empty()) + return; + + MaskPredicate maskPredicate(mask); + removeFrom.erase(std::remove_if(removeFrom.begin(), removeFrom.end(), + [&](const T &x) + { + auto index = &x - &removeFrom.front(); + return maskPredicate(keypoints[index]); + }), + removeFrom.end()); + keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), maskPredicate), keypoints.end()); +} +void KeyPointsFilter::runByPixelsMask2VectorPoint(std::vector &keypoints, std::vector > &removeFrom, const Mat &mask) +{ + runByPixelsMask2(keypoints, removeFrom, mask); +} struct KeyPoint_LessThan { diff --git a/modules/features2d/test/test_blobdetector.cpp b/modules/features2d/test/test_blobdetector.cpp index c6ed09e6a2..2aa99b60f7 100644 --- a/modules/features2d/test/test_blobdetector.cpp +++ b/modules/features2d/test/test_blobdetector.cpp @@ -19,4 +19,28 @@ TEST(Features2d_BlobDetector, bug_6667) detector->detect(image, keypoints); ASSERT_NE((int) keypoints.size(), 0); } + +TEST(Features2d_BlobDetector, withContours) +{ + cv::Mat image = cv::Mat(cv::Size(100, 100), CV_8UC1, cv::Scalar(255, 255, 255)); + cv::circle(image, Point(50, 50), 20, cv::Scalar(0), -1); + SimpleBlobDetector::Params params; + params.minThreshold = 250; + params.maxThreshold = 260; + params.minRepeatability = 1; // https://github.com/opencv/opencv/issues/6667 + params.collectContours = true; + std::vector keypoints; + + Ptr detector = SimpleBlobDetector::create(params); + detector->detect(image, keypoints); + ASSERT_NE((int)keypoints.size(), 0); + + ASSERT_GT((int)detector->getBlobContours().size(), 0); + std::vector contour = detector->getBlobContours()[0]; + ASSERT_TRUE(std::any_of(contour.begin(), contour.end(), + [](Point p) + { + return abs(p.x - 30) < 2 && abs(p.y - 50) < 2; + })); +} }} // namespace From 43b2bb2c25d5ed4474d0355ba39f34660fdc0cdd Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 18 Mar 2022 06:19:30 +0000 Subject: [PATCH 148/313] dnn: plugin support for OpenVINO --- cmake/OpenCVUtils.cmake | 12 + .../include/opencv2/core/utils/filesystem.hpp | 2 +- modules/dnn/CMakeLists.txt | 66 +++- modules/dnn/cmake/init.cmake | 29 ++ modules/dnn/cmake/plugin.cmake | 80 +++++ modules/dnn/include/opencv2/dnn/dnn.hpp | 4 +- .../dnn/misc/plugin/openvino/CMakeLists.txt | 2 + modules/dnn/misc/python/test/test_dnn.py | 6 +- modules/dnn/src/backend.cpp | 31 ++ modules/dnn/src/backend.hpp | 43 +++ modules/dnn/src/factory.hpp | 30 ++ modules/dnn/src/init.cpp | 6 + modules/dnn/src/layer_factory.cpp | 2 - modules/dnn/src/layer_internals.hpp | 16 - modules/dnn/src/net_impl.cpp | 21 +- modules/dnn/src/net_impl.hpp | 27 +- modules/dnn/src/net_impl_backend.cpp | 17 +- modules/dnn/src/net_impl_fuse.cpp | 2 +- modules/dnn/src/net_openvino.cpp | 221 +++++++++--- modules/dnn/src/op_inf_engine.cpp | 83 ++++- modules/dnn/src/op_inf_engine.hpp | 16 +- modules/dnn/src/plugin_api.hpp | 72 ++++ modules/dnn/src/plugin_wrapper.impl.hpp | 319 ++++++++++++++++++ modules/dnn/src/precomp.hpp | 8 +- modules/dnn/src/registry.cpp | 42 +-- modules/dnn/test/test_common.hpp | 2 - modules/dnn/test/test_common.impl.hpp | 8 - 27 files changed, 1035 insertions(+), 132 deletions(-) create mode 100644 modules/dnn/cmake/init.cmake create mode 100644 modules/dnn/cmake/plugin.cmake create mode 100644 modules/dnn/misc/plugin/openvino/CMakeLists.txt create mode 100644 modules/dnn/src/backend.cpp create mode 100644 modules/dnn/src/backend.hpp create mode 100644 modules/dnn/src/factory.hpp create mode 100644 modules/dnn/src/plugin_api.hpp create mode 100644 modules/dnn/src/plugin_wrapper.impl.hpp diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 579c5ce54c..3a81ada6ae 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -1094,6 +1094,18 @@ macro(ocv_list_filterout lst regex) endforeach() endmacro() +# Usage: ocv_list_filterout_ex(list_name regex1 regex2 ...) +macro(ocv_list_filterout_ex lst) + foreach(regex ${ARGN}) + foreach(item ${${lst}}) + if(item MATCHES "${regex}") + list(REMOVE_ITEM ${lst} "${item}") + endif() + endforeach() + endforeach() +endmacro() + + # filter matching elements from the list macro(ocv_list_filter lst regex) set(dst ${ARGN}) diff --git a/modules/core/include/opencv2/core/utils/filesystem.hpp b/modules/core/include/opencv2/core/utils/filesystem.hpp index a98d2202fc..8619ae4d1a 100644 --- a/modules/core/include/opencv2/core/utils/filesystem.hpp +++ b/modules/core/include/opencv2/core/utils/filesystem.hpp @@ -62,7 +62,7 @@ CV_EXPORTS void glob_relative(const cv::String& directory, const cv::String& pat CV_EXPORTS bool createDirectory(const cv::String& path); CV_EXPORTS bool createDirectories(const cv::String& path); -#ifdef __OPENCV_BUILD +#if defined(__OPENCV_BUILD) || defined(BUILD_PLUGIN) // TODO //CV_EXPORTS cv::String getTempDirectory(); diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index e0773d5214..ffa7b57663 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -13,18 +13,22 @@ ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX) ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js) + +include(${CMAKE_CURRENT_LIST_DIR}/cmake/plugin.cmake) + + ocv_option(OPENCV_DNN_OPENCL "Build with OpenCL support" HAVE_OPENCL AND NOT APPLE) if(OPENCV_DNN_OPENCL AND HAVE_OPENCL) - add_definitions(-DCV_OCL4DNN=1) + ocv_target_compile_definitions(${the_module} PRIVATE "CV_OCL4DNN=1") endif() if(WITH_WEBNN AND HAVE_WEBNN) - add_definitions(-DHAVE_WEBNN=1) + ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_WEBNN=1") endif() if(HAVE_TIMVX) - add_definitions(-DHAVE_TIMVX=1) + ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_TIMVX=1") endif() ocv_option(OPENCV_DNN_CUDA "Build with CUDA support" @@ -35,7 +39,7 @@ ocv_option(OPENCV_DNN_CUDA "Build with CUDA support" if(OPENCV_DNN_CUDA) if(HAVE_CUDA AND HAVE_CUBLAS AND HAVE_CUDNN) - add_definitions(-DCV_CUDA4DNN=1) + ocv_target_compile_definitions(${the_module} PRIVATE "CV_CUDA4DNN=1") else() if(NOT HAVE_CUDA) message(SEND_ERROR "DNN: CUDA backend requires CUDA Toolkit. Please resolve dependency or disable OPENCV_DNN_CUDA=OFF") @@ -47,12 +51,15 @@ if(OPENCV_DNN_CUDA) endif() endif() + ocv_cmake_hook_append(INIT_MODULE_SOURCES_opencv_dnn "${CMAKE_CURRENT_LIST_DIR}/cmake/hooks/INIT_MODULE_SOURCES_opencv_dnn.cmake") + if(HAVE_TENGINE) - add_definitions(-DHAVE_TENGINE=1) + ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_TENGINE=1") endif() + if(MSVC) add_definitions( -D_CRT_SECURE_NO_WARNINGS=1 ) ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244 /wd4267 /wd4018 /wd4355 /wd4800 /wd4251 /wd4996 /wd4146 @@ -87,10 +94,10 @@ if(ANDROID) endif() if(NOT BUILD_PROTOBUF) - add_definitions(-DOPENCV_DNN_EXTERNAL_PROTOBUF=1) + ocv_target_compile_definitions(${the_module} PRIVATE "OPENCV_DNN_EXTERNAL_PROTOBUF=1") endif() -add_definitions(-DHAVE_PROTOBUF=1) +ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_PROTOBUF=1") #suppress warnings in autogenerated caffe.pb.* files ocv_warnings_disable(CMAKE_CXX_FLAGS @@ -175,12 +182,34 @@ endif() set(dnn_runtime_libs "") +file(GLOB_RECURSE dnn_srcs + "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp" +) +file(GLOB_RECURSE dnn_int_hdrs + "${CMAKE_CURRENT_LIST_DIR}/src/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/src/*.h" +) +set(dnn_plugin_srcs ${dnn_srcs} ${dnn_int_hdrs}) +ocv_list_filterout_ex(dnn_plugin_srcs + "/src/dnn.cpp$|/src/dnn_utils.cpp$|/src/dnn_utils.cpp$|/src/dnn_read.cpp$|/src/registry.cpp$|/src/backend.cpp$" + # importers + "/src/(caffe|darknet|onnx|tensorflow|torch)/" + # executors + "/src/(cuda|cuda4dnn|ocl4dnn|vkcom|webnn)/" +) + ocv_option(OPENCV_DNN_OPENVINO "Build with OpenVINO support (2021.4+)" (TARGET ocv.3rdparty.openvino)) if(TARGET ocv.3rdparty.openvino AND OPENCV_DNN_OPENVINO) if(NOT HAVE_OPENVINO AND NOT HAVE_NGRAPH) message(FATAL_ERROR "DNN: Inference Engine is not supported without enabled 'nGraph'. Check build configuration.") endif() - list(APPEND dnn_runtime_libs ocv.3rdparty.openvino) + if("openvino" IN_LIST DNN_PLUGIN_LIST OR DNN_PLUGIN_LIST STREQUAL "all") + # plugin doesn't support PCH, separate directory scope is necessary + # opencv_world requires absolute path + add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/misc/plugin/openvino" "${CMAKE_CURRENT_BINARY_DIR}/dnn_plugin_openvino") + elseif(NOT OPENCV_DNN_BUILTIN_BACKEND) + list(APPEND dnn_runtime_libs ocv.3rdparty.openvino) + endif() endif() ocv_glob_module_sources(${sources_options} SOURCES ${fw_srcs} ${webnn_srcs}) @@ -205,7 +234,7 @@ if(BUILD_PERF_TESTS) ) find_package(Caffe QUIET) if (Caffe_FOUND) - add_definitions(-DHAVE_CAFFE=1) + ocv_target_compile_definitions(opencv_perf_dnn PRIVATE "HAVE_CAFFE=1") ocv_target_link_libraries(opencv_perf_dnn caffe) endif() elseif(OPENCV_DNN_PERF_CLCAFFE @@ -213,8 +242,25 @@ if(BUILD_PERF_TESTS) ) find_package(Caffe QUIET) if (Caffe_FOUND) - add_definitions(-DHAVE_CLCAFFE=1) + ocv_target_compile_definitions(opencv_perf_dnn PRIVATE "HAVE_CLCAFFE=1") ocv_target_link_libraries(opencv_perf_dnn caffe) endif() endif() endif() + +if(DNN_ENABLE_PLUGINS) + ocv_target_compile_definitions(${the_module} PRIVATE ENABLE_PLUGINS) + if(TARGET opencv_test_dnn) + ocv_target_compile_definitions(opencv_test_dnn PRIVATE ENABLE_PLUGINS) + endif() + if(OPENCV_DEBUG_POSTFIX) + ocv_append_source_file_compile_definitions("${CMAKE_CURRENT_LIST_DIR}/src/backend.cpp" "DEBUG_POSTFIX=${OPENCV_DEBUG_POSTFIX}") + endif() +endif() + +ocv_option(OPENCV_TEST_DNN_OPENVINO "Build test with OpenVINO code" (TARGET ocv.3rdparty.openvino)) +if(TARGET ocv.3rdparty.openvino AND OPENCV_TEST_DNN_OPENVINO) + if(TARGET opencv_test_dnn) + ocv_target_link_libraries(opencv_test_dnn ocv.3rdparty.openvino) + endif() +endif() diff --git a/modules/dnn/cmake/init.cmake b/modules/dnn/cmake/init.cmake new file mode 100644 index 0000000000..f4493c53e9 --- /dev/null +++ b/modules/dnn/cmake/init.cmake @@ -0,0 +1,29 @@ +if(PROJECT_NAME STREQUAL "OpenCV") + set(ENABLE_PLUGINS_DEFAULT ON) + if(EMSCRIPTEN OR IOS OR WINRT) + set(ENABLE_PLUGINS_DEFAULT OFF) + endif() + set(DNN_PLUGIN_LIST "" CACHE STRING "List of DNN backends to be compiled as plugins (openvino, etc or special value 'all')") + set(DNN_ENABLE_PLUGINS "${ENABLE_PLUGINS_DEFAULT}" CACHE BOOL "Allow building and using of DNN plugins") + mark_as_advanced(DNN_PLUGIN_LIST DNN_ENABLE_PLUGINS) + + string(REPLACE "," ";" DNN_PLUGIN_LIST "${DNN_PLUGIN_LIST}") # support comma-separated list (,) too + string(TOLOWER "${DNN_PLUGIN_LIST}" DNN_PLUGIN_LIST) + if(NOT DNN_ENABLE_PLUGINS) + if(DNN_PLUGIN_LIST) + message(WARNING "DNN: plugins are disabled through DNN_ENABLE_PLUGINS, so DNN_PLUGIN_LIST='${DNN_PLUGIN_LIST}' is ignored") + set(DNN_PLUGIN_LIST "") + endif() + else() + # Make virtual plugins target + if(NOT TARGET opencv_dnn_plugins) + add_custom_target(opencv_dnn_plugins ALL) + endif() + endif() +endif() + +# +# Detect available dependencies +# + +# OpenVINO - detected by main CMake scripts (shared with G-API) diff --git a/modules/dnn/cmake/plugin.cmake b/modules/dnn/cmake/plugin.cmake new file mode 100644 index 0000000000..055d21efc3 --- /dev/null +++ b/modules/dnn/cmake/plugin.cmake @@ -0,0 +1,80 @@ +function(ocv_create_builtin_dnn_plugin name target) + + ocv_debug_message("ocv_create_builtin_dnn_plugin(${ARGV})") + + if(NOT TARGET ${target}) + message(FATAL_ERROR "${target} does not exist!") + endif() + if(NOT OpenCV_SOURCE_DIR) + message(FATAL_ERROR "OpenCV_SOURCE_DIR must be set to build the plugin!") + endif() + + message(STATUS "DNN: add builtin plugin '${name}'") + + set(ENABLE_PRECOMPILED_HEADERS OFF) # no support for PCH in plugins, conflicts with module's source files + + # TODO: update CPU optimizations scripts to support plugins + add_definitions(-D__OPENCV_BUILD=1) + add_definitions(-DBUILD_PLUGIN=1) + include_directories("${OPENCV_MODULE_opencv_dnn_BINARY_DIR}") # Cannot open include file: 'layers/layers_common.simd_declarations.hpp' + + foreach(src ${ARGN}) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/src/${src}") + list(APPEND sources "${CMAKE_CURRENT_LIST_DIR}/src/${src}") + elseif(IS_ABSOLUTE "${src}") + list(APPEND sources "${src}") + else() + message(FATAL_ERROR "Unknown source: ${src}") + endif() + endforeach() + + if(OPENCV_MODULE_${the_module}_SOURCES_DISPATCHED) + list(APPEND sources ${OPENCV_MODULE_${the_module}_SOURCES_DISPATCHED}) + endif() + + set(__${name}_DEPS_EXT "") + ocv_compiler_optimization_process_sources(sources __${name}_DEPS_EXT ${name}) + + add_library(${name} MODULE ${sources}) + target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_link_libraries(${name} PRIVATE ${target} ${__${name}_DEPS_EXT}) + target_link_libraries(${name} PRIVATE ${__plugin_libs}) + + foreach(mod opencv_dnn + opencv_core + opencv_imgproc + opencv_dnn + ) + ocv_target_link_libraries(${name} LINK_PRIVATE ${mod}) + ocv_target_include_directories(${name} "${OPENCV_MODULE_${mod}_LOCATION}/include") + endforeach() + + if(WIN32) + set(OPENCV_PLUGIN_VERSION "${OPENCV_DLLVERSION}" CACHE STRING "") + if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) + set(OPENCV_PLUGIN_ARCH "_64" CACHE STRING "") + else() + set(OPENCV_PLUGIN_ARCH "" CACHE STRING "") + endif() + else() + set(OPENCV_PLUGIN_VERSION "" CACHE STRING "") + set(OPENCV_PLUGIN_ARCH "" CACHE STRING "") + endif() + + set_target_properties(${name} PROPERTIES + CXX_STANDARD 11 + CXX_VISIBILITY_PRESET hidden + DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" + OUTPUT_NAME "${name}${OPENCV_PLUGIN_VERSION}${OPENCV_PLUGIN_ARCH}" + ) + + if(WIN32) + set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT plugins) + else() + install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT plugins) + endif() + + add_dependencies(opencv_dnn_plugins ${name}) + +endfunction() diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 3382b27d8b..0f952c72be 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -81,9 +81,11 @@ CV__DNN_INLINE_NS_BEGIN DNN_BACKEND_CUDA, DNN_BACKEND_WEBNN, DNN_BACKEND_TIMVX, -#ifdef __OPENCV_BUILD +#if defined(__OPENCV_BUILD) || defined(BUILD_PLUGIN) +#if !defined(OPENCV_BINDING_PARSER) DNN_BACKEND_INFERENCE_ENGINE_NGRAPH = 1000000, // internal - use DNN_BACKEND_INFERENCE_ENGINE + setInferenceEngineBackendType() DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019, // internal - use DNN_BACKEND_INFERENCE_ENGINE + setInferenceEngineBackendType() +#endif #endif }; diff --git a/modules/dnn/misc/plugin/openvino/CMakeLists.txt b/modules/dnn/misc/plugin/openvino/CMakeLists.txt new file mode 100644 index 0000000000..398218484e --- /dev/null +++ b/modules/dnn/misc/plugin/openvino/CMakeLists.txt @@ -0,0 +1,2 @@ +#include_directories("${OPENCV_MODULE_opencv_dnn_BINARY_DIR}") # Cannot open include file: 'layers/layers_common.simd_declarations.hpp' +ocv_create_builtin_dnn_plugin(opencv_dnn_openvino ocv.3rdparty.openvino ${dnn_plugin_srcs}) diff --git a/modules/dnn/misc/python/test/test_dnn.py b/modules/dnn/misc/python/test/test_dnn.py index 272121ba36..82d07f402b 100644 --- a/modules/dnn/misc/python/test/test_dnn.py +++ b/modules/dnn/misc/python/test/test_dnn.py @@ -113,10 +113,10 @@ class dnn_test(NewOpenCVTests): proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt') model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel') net = cv.dnn.readNet(proto, model) - net.setPreferableBackend(backend) - net.setPreferableTarget(target) - inp = np.random.standard_normal([1, 2, 10, 11]).astype(np.float32) try: + net.setPreferableBackend(backend) + net.setPreferableTarget(target) + inp = np.random.standard_normal([1, 2, 10, 11]).astype(np.float32) net.setInput(inp) net.forward() except BaseException as e: diff --git a/modules/dnn/src/backend.cpp b/modules/dnn/src/backend.cpp new file mode 100644 index 0000000000..f6c6fecdad --- /dev/null +++ b/modules/dnn/src/backend.cpp @@ -0,0 +1,31 @@ +// 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" +#include "backend.hpp" + +#include + +#include +#include +#ifdef NDEBUG +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1 +#else +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 +#endif +#include + +#include "factory.hpp" + +#include "plugin_api.hpp" +#include "plugin_wrapper.impl.hpp" + + +namespace cv { namespace dnn_backend { + +NetworkBackend::~NetworkBackend() +{ + // nothing +} + +}} // namespace cv::dnn_backend diff --git a/modules/dnn/src/backend.hpp b/modules/dnn/src/backend.hpp new file mode 100644 index 0000000000..37cc8a3cc0 --- /dev/null +++ b/modules/dnn/src/backend.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_DNN_BACKEND_HPP +#define OPENCV_DNN_BACKEND_HPP + +#include +#include + +namespace cv { namespace dnn_backend { + +using namespace cv::dnn; + +class CV_EXPORTS NetworkBackend +{ +public: + virtual ~NetworkBackend(); + + virtual void switchBackend(Net& net) = 0; + + /** + @param loaderID use empty "" for auto + @param model see cv::dnn::readNetwork + @param config see cv::dnn::readNetwork + */ + virtual Net readNetwork(const std::string& loaderID, const std::string& model, const std::string& config) = 0; + + /** @overload */ + virtual Net readNetwork( + const std::string& loaderID, + const uchar* bufferModelConfigPtr, size_t bufferModelConfigSize, + const uchar* bufferWeightsPtr, size_t bufferWeightsSize + ) = 0; + + // TODO: target as string + configuration + virtual bool checkTarget(Target target) = 0; +}; + + +} // namespace dnn_backend +} // namespace cv + +#endif // OPENCV_DNN_BACKEND_HPP diff --git a/modules/dnn/src/factory.hpp b/modules/dnn/src/factory.hpp new file mode 100644 index 0000000000..0d8750a8c0 --- /dev/null +++ b/modules/dnn/src/factory.hpp @@ -0,0 +1,30 @@ +// 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_DNN_FACTORY_HPP +#define OPENCV_DNN_FACTORY_HPP + +#include "backend.hpp" + +namespace cv { namespace dnn_backend { + +class IDNNBackendFactory +{ +public: + virtual ~IDNNBackendFactory() {} + virtual std::shared_ptr createNetworkBackend() const = 0; +}; + +// +// PluginDNNBackendFactory is implemented in plugin_wrapper +// + +std::shared_ptr createPluginDNNBackendFactory(const std::string& baseName); + +/// @brief Returns createPluginDNNBackendFactory()->createNetworkBackend() +cv::dnn_backend::NetworkBackend& createPluginDNNNetworkBackend(const std::string& baseName); + +}} // namespace + +#endif // OPENCV_DNN_FACTORY_HPP diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index e3ce6de40d..68d24ef5bd 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -42,7 +42,9 @@ #include "precomp.hpp" #include +#if !defined(BUILD_PLUGIN) #include +#endif namespace cv { namespace dnn { @@ -58,6 +60,7 @@ Mutex& getInitializationMutex() // force initialization (single-threaded environment) Mutex* __initialization_mutex_initializer = &getInitializationMutex(); +#if !defined(BUILD_PLUGIN) namespace { using namespace google::protobuf; class ProtobufShutdown { @@ -71,12 +74,15 @@ public: } }; } // namespace +#endif void initializeLayerFactory() { CV_TRACE_FUNCTION(); +#if !defined(BUILD_PLUGIN) static ProtobufShutdown protobufShutdown; CV_UNUSED(protobufShutdown); +#endif CV_DNN_REGISTER_LAYER_CLASS(Slice, SliceLayer); CV_DNN_REGISTER_LAYER_CLASS(Split, SplitLayer); diff --git a/modules/dnn/src/layer_factory.cpp b/modules/dnn/src/layer_factory.cpp index 5c80cd09ad..e5b835143e 100644 --- a/modules/dnn/src/layer_factory.cpp +++ b/modules/dnn/src/layer_factory.cpp @@ -4,8 +4,6 @@ #include "precomp.hpp" -#include - #include // getLayerFactoryImpl diff --git a/modules/dnn/src/layer_internals.hpp b/modules/dnn/src/layer_internals.hpp index e805ab53bb..f19b99f260 100644 --- a/modules/dnn/src/layer_internals.hpp +++ b/modules/dnn/src/layer_internals.hpp @@ -96,22 +96,6 @@ struct LayerData int flag; - Ptr getLayerInstance() - { - CV_TRACE_FUNCTION(); - CV_TRACE_ARG_VALUE(type, "type", type.c_str()); - - if (layerInstance) - return layerInstance; - - layerInstance = LayerFactory::createLayerInstance(type, params); - if (!layerInstance) - { - CV_Error(Error::StsError, "Can't create layer \"" + name + "\" of type \"" + type + "\""); - } - - return layerInstance; - } void resetAllocation() { diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index dfab6de59a..3839cba329 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -222,14 +222,14 @@ void Net::Impl::setUpNet(const std::vector& blobsToKeep_) Ptr Net::Impl::getLayer(int layerId) const { LayerData& ld = getLayerData(layerId); - return ld.getLayerInstance(); + return getLayerInstance(ld); } Ptr Net::Impl::getLayer(const LayerId& layerId) const { LayerData& ld = getLayerData(layerId); - return ld.getLayerInstance(); + return getLayerInstance(ld); } @@ -321,7 +321,7 @@ int Net::Impl::resolvePinOutputName(LayerData& ld, const String& outName) const { if (outName.empty()) return 0; - return ld.getLayerInstance()->outputNameToIndex(outName); + return getLayerInstance(ld)->outputNameToIndex(outName); } @@ -522,7 +522,7 @@ void Net::Impl::allocateLayer(int lid, const LayersShapesMap& layersShapes) for (int i = 0; i < ld.internalBlobsWrappers.size(); ++i) ld.internalBlobsWrappers[i] = wrap(ld.internals[i]); - Ptr layerPtr = ld.getLayerInstance(); + Ptr layerPtr = getLayerInstance(ld); { std::vector inps(ld.inputBlobs.size()); for (int i = 0; i < ld.inputBlobs.size(); ++i) @@ -1148,7 +1148,7 @@ void Net::Impl::getLayerShapesRecursively(int id, LayersShapesMap& inOutShapes) ShapesVec& os = layerShapes.out; ShapesVec& ints = layerShapes.internal; int requiredOutputs = layerData.requiredOutputs.size(); - Ptr l = layerData.getLayerInstance(); + const Ptr& l = getLayerInstance(layerData); CV_Assert(l); bool layerSupportInPlace = false; try @@ -1302,7 +1302,7 @@ void Net::Impl::updateLayersShapes() const MatShape& shape = layersShapes[inputLayerId].out[inputPin.oid]; layerShapes.in.push_back(shape); } - layerData.getLayerInstance()->updateMemoryShapes(layerShapes.in); + getLayerInstance(layerData)->updateMemoryShapes(layerShapes.in); } CV_LOG_DEBUG(NULL, "Layer " << layerId << ": " << toString(layerShapes.in, "input shapes")); CV_LOG_IF_DEBUG(NULL, !layerShapes.out.empty(), "Layer " << layerId << ": " << toString(layerShapes.out, "output shapes")); @@ -1451,7 +1451,7 @@ void Net::Impl::setInput(InputArray blob, const String& name, double scalefactor Mat Net::Impl::getParam(int layer, int numParam) const { LayerData& ld = getLayerData(layer); - std::vector& layerBlobs = ld.getLayerInstance()->blobs; + std::vector& layerBlobs = getLayerInstance(ld)->blobs; CV_Assert(numParam < (int)layerBlobs.size()); return layerBlobs[numParam]; } @@ -1460,7 +1460,8 @@ void Net::Impl::setParam(int layer, int numParam, const Mat& blob) { LayerData& ld = getLayerData(layer); - std::vector& layerBlobs = ld.getLayerInstance()->blobs; + // FIXIT we should not modify "execution" instance + std::vector& layerBlobs = getLayerInstance(ld)->blobs; CV_Assert(numParam < (int)layerBlobs.size()); // we don't make strong checks, use this function carefully layerBlobs[numParam] = blob; @@ -1927,7 +1928,7 @@ int64 Net::Impl::getFLOPS(const std::vector& netInputShapes) /*const*/ for (int i = 0; i < ids.size(); i++) { - flops += layers[ids[i]].getLayerInstance()->getFLOPS(inShapes[i], outShapes[i]); + flops += getLayerInstance(layers[ids[i]])->getFLOPS(inShapes[i], outShapes[i]); } return flops; @@ -1944,7 +1945,7 @@ int64 Net::Impl::getFLOPS( LayerShapes shapes; getLayerShapes(netInputShapes, layerId, shapes); - return const_cast(layer->second).getLayerInstance()->getFLOPS(shapes.in, shapes.out); + return getLayerInstance(const_cast(layer->second))->getFLOPS(shapes.in, shapes.out); } diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index 269a46be79..290ce50c13 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -54,7 +54,6 @@ struct Net::Impl : public detail::NetImplBase int preferableBackend; int preferableTarget; String halideConfigFile; -// bool skipInfEngineInit; bool hasDynamicShapes; // Map host data to backend specific wrapper. std::map> backendWrappers; @@ -84,6 +83,32 @@ struct Net::Impl : public detail::NetImplBase void setUpNet(const std::vector& blobsToKeep_ = std::vector()); + virtual Ptr createLayerInstance(const LayerData& ld) const + { + return LayerFactory::createLayerInstance(ld.type, const_cast(ld.params)); + } + Ptr getLayerInstance(LayerData& ld) const + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(type, "type", ld.type.c_str()); + + if (ld.layerInstance) + return ld.layerInstance; + + ld.layerInstance = createLayerInstance(ld); + if (!ld.layerInstance && basePtr_) + { + ld.layerInstance = basePtr_->createLayerInstance(ld); + CV_LOG_IF_DEBUG(NULL, ld.layerInstance, "Created layer \"" + ld.name + "\" of type \"" + ld.type + "\" from upstream layers registry"); + } + if (!ld.layerInstance) + { + CV_Error(Error::StsError, "Can't create layer \"" + ld.name + "\" of type \"" + ld.type + "\""); + } + + return ld.layerInstance; + } + Ptr getLayer(int layerId) const; Ptr getLayer(const LayerId& layerId) const; diff --git a/modules/dnn/src/net_impl_backend.cpp b/modules/dnn/src/net_impl_backend.cpp index c9c61eb893..1d313c70c4 100644 --- a/modules/dnn/src/net_impl_backend.cpp +++ b/modules/dnn/src/net_impl_backend.cpp @@ -7,6 +7,9 @@ #include "net_impl.hpp" #include "legacy_backend.hpp" +#include "backend.hpp" +#include "factory.hpp" + namespace cv { namespace dnn { CV__DNN_INLINE_NS_BEGIN @@ -166,14 +169,22 @@ void Net::Impl::setPreferableBackend(Net& net, int backendId) if (preferableBackend != backendId) { - preferableBackend = backendId; clear(); -#ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { +#if defined(HAVE_INF_ENGINE) switchToOpenVINOBackend(net); - } +#elif defined(ENABLE_PLUGINS) + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + networkBackend.switchBackend(net); +#else + CV_Error(Error::StsNotImplemented, "OpenVINO backend is not available in the current OpenCV build"); #endif + } + else + { + preferableBackend = backendId; + } } } diff --git a/modules/dnn/src/net_impl_fuse.cpp b/modules/dnn/src/net_impl_fuse.cpp index deb356f7b9..79365d0411 100644 --- a/modules/dnn/src/net_impl_fuse.cpp +++ b/modules/dnn/src/net_impl_fuse.cpp @@ -634,7 +634,7 @@ void Net::Impl::fuseLayers(const std::vector& blobsToKeep_) pin = inp_i_data->inputBlobsId[0]; inp_i_data = &layers[pin.lid]; } - conv_layer = conv_layer && (inp_i_data->getLayerInstance()->type == "Convolution"); + conv_layer = conv_layer && (getLayerInstance(*inp_i_data)->type == "Convolution"); } if (!conv_layer) continue; diff --git a/modules/dnn/src/net_openvino.cpp b/modules/dnn/src/net_openvino.cpp index 77186f074c..d55c26a0de 100644 --- a/modules/dnn/src/net_openvino.cpp +++ b/modules/dnn/src/net_openvino.cpp @@ -11,6 +11,9 @@ #include "net_impl.hpp" +#include "backend.hpp" +#include "factory.hpp" + namespace cv { namespace dnn { CV__DNN_INLINE_NS_BEGIN @@ -80,11 +83,12 @@ public: if (backendId == DNN_BACKEND_INFERENCE_ENGINE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) return; // no-op if (!basePtr_) - CV_Error(Error::StsError, "DNN: Can't switch backend of network created by OpenVINO"); + CV_Error(Error::StsError, "DNN: Can't switch backend of network created by OpenVINO native loader"); Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); impl_ptr_ref = basePtr_; - return basePtr_->setPreferableBackend(net, backendId); + basePtr_->setPreferableBackend(net, backendId); } + void setPreferableTarget(int targetId) override { if (preferableTarget != targetId) @@ -121,10 +125,14 @@ public: ); } - //void setUpNet(const std::vector& blobsToKeep_ = std::vector()) override; - - - //void setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean) override; + Ptr createLayerInstance(const LayerData& ld) const override + { + // try to create layer instance from backend-specific pool (e.g., plugin) + Ptr instance = LayerFactory::createLayerInstance(ld.type, const_cast(ld.params)); + if (!instance) + instance = Base::createLayerInstance(ld); + return instance; + } void addNgraphOutputs(LayerData& ld); @@ -132,8 +140,6 @@ public: void fuseLayers(const std::vector& blobsToKeep_) override; - //void allocateLayers(const std::vector& blobsToKeep_) override; - void forwardLayer(LayerData& ld) override; AsyncArray getBlobAsync(const LayerPin& pin) override; @@ -176,7 +182,7 @@ void NetImplOpenVINO::forwardLayer(LayerData& ld) tm.stop(); int64 t = tm.getTimeTicks(); - layersTimings[ld.id] = (t > 0) ? t : t + 1; // zero for skipped layers only + layersTimings[ld.id] = (t > 0) ? t : 1; // zero for skipped layers only } else { @@ -681,14 +687,14 @@ void NetImplOpenVINO::fuseLayers(const std::vector& blobsToKeep_) void switchToOpenVINOBackend(Net& net) { CV_TRACE_FUNCTION(); - CV_LOG_INFO(NULL, "DNN: switching to OpenVINO backend..."); Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); + CV_Assert(impl_ptr_ref); + CV_LOG_INFO(NULL, "DNN: switching to OpenVINO backend... (networkID=" << impl_ptr_ref->networkId << ")"); Ptr openvino_impl_ptr = makePtr(impl_ptr_ref); impl_ptr_ref = openvino_impl_ptr; } - /*static*/ Net NetImplOpenVINO::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet) { @@ -792,23 +798,70 @@ Net NetImplOpenVINO::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork return cvNet; } + + +static +Net openvino_readNetwork(const String& modelPath, const String& binPath) +{ + FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; + + InferenceEngine::Core& ie = getCore(""); + InferenceEngine::CNNNetwork ieNet; + try + { + ieNet = ie.ReadNetwork(modelPath, binPath); + } + catch (const std::exception& e) + { + CV_Error(Error::StsError, std::string("DNN: OpenVINO failed to read model '") + modelPath + "': " + e.what()); + } + + return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); +} + + +static +Net openvino_readNetwork( + const uchar* bufferModelConfigPtr, size_t bufferModelConfigSize, + const uchar* bufferWeightsPtr, size_t bufferWeightsSize +) +{ + FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; + + InferenceEngine::Core& ie = getCore(""); + + std::string model; model.assign((char*)bufferModelConfigPtr, bufferModelConfigSize); + + InferenceEngine::CNNNetwork ieNet; + try + { + InferenceEngine::TensorDesc tensorDesc(InferenceEngine::Precision::U8, { bufferWeightsSize }, InferenceEngine::Layout::C); + InferenceEngine::Blob::CPtr weights_blob = InferenceEngine::make_shared_blob(tensorDesc, (uint8_t*)bufferWeightsPtr, bufferWeightsSize); + + ieNet = ie.ReadNetwork(model, weights_blob); + } + catch (const std::exception& e) + { + CV_Error(Error::StsError, std::string("DNN: OpenVINO failed to read model: ") + e.what()); + } + + return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); +} + #endif // HAVE_INF_ENGINE Net Net::readFromModelOptimizer(const String& xml, const String& bin) { CV_TRACE_FUNCTION(); -#ifndef HAVE_INF_ENGINE +#if defined(HAVE_INF_ENGINE) + return openvino_readNetwork(xml, bin); +#elif defined(ENABLE_PLUGINS) + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + return networkBackend.readNetwork(std::string(), xml, bin); +#else CV_UNUSED(xml); CV_UNUSED(bin); CV_Error(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); -#else - - FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; - - InferenceEngine::Core& ie = getCore(""); - InferenceEngine::CNNNetwork ieNet = ie.ReadNetwork(xml, bin); - - return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); -#endif // HAVE_INF_ENGINE +#endif } Net Net::readFromModelOptimizer(const std::vector& bufferModelConfig, const std::vector& bufferWeights) @@ -826,34 +879,112 @@ Net Net::readFromModelOptimizer( ) { CV_TRACE_FUNCTION(); -#ifndef HAVE_INF_ENGINE +#if defined(HAVE_INF_ENGINE) + return openvino_readNetwork(bufferModelConfigPtr, bufferModelConfigSize, bufferWeightsPtr, bufferWeightsSize); +#elif defined(ENABLE_PLUGINS) + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + return networkBackend.readNetwork(std::string(), bufferModelConfigPtr, bufferModelConfigSize, bufferWeightsPtr, bufferWeightsSize); +#else CV_UNUSED(bufferModelConfigPtr); CV_UNUSED(bufferWeightsPtr); CV_UNUSED(bufferModelConfigSize); CV_UNUSED(bufferModelConfigSize); CV_Error(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); -#else - - FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; - - InferenceEngine::Core& ie = getCore(""); - - std::string model; model.assign((char*)bufferModelConfigPtr, bufferModelConfigSize); - - InferenceEngine::CNNNetwork ieNet; - try - { - InferenceEngine::TensorDesc tensorDesc(InferenceEngine::Precision::U8, { bufferWeightsSize }, InferenceEngine::Layout::C); - InferenceEngine::Blob::CPtr weights_blob = InferenceEngine::make_shared_blob(tensorDesc, (uint8_t*)bufferWeightsPtr, bufferWeightsSize); - - ieNet = ie.ReadNetwork(model, weights_blob); - } - catch (const std::exception& e) - { - CV_Error(Error::StsError, std::string("DNN: IE failed to load model: ") + e.what()); - } - - return NetImplOpenVINO::createNetworkFromModelOptimizer(ieNet); -#endif // HAVE_INF_ENGINE +#endif } + CV__DNN_INLINE_NS_END }} // namespace cv::dnn + + + +#ifdef BUILD_PLUGIN + +#define ABI_VERSION 0 +#define API_VERSION 0 +#include "plugin_api.hpp" + + +namespace cv { namespace dnn_backend { + +using namespace cv::dnn; + +class NetworkBackendOpenVINO : public NetworkBackend +{ +public: + void switchBackend(Net& net) CV_OVERRIDE + { + cv::dnn::switchToOpenVINOBackend(net); + } + Net readNetwork(const std::string& loaderID, const std::string& model, const std::string& config) CV_OVERRIDE + { + if (!loaderID.empty()) // only auto ("") is supported + { + CV_Error(Error::StsError, "DNN/OpenVINO: unsupported network loader ID: " + loaderID); + } + return openvino_readNetwork(model, config); + } + Net readNetwork( + const std::string& loaderID, + const uchar* bufferModelConfigPtr, size_t bufferModelConfigSize, + const uchar* bufferWeightsPtr, size_t bufferWeightsSize + ) CV_OVERRIDE + { + if (!loaderID.empty()) // only auto ("") is supported + { + CV_Error(Error::StsError, "DNN/OpenVINO: unsupported network loader ID: " + loaderID); + } + return openvino_readNetwork(bufferModelConfigPtr, bufferModelConfigSize, bufferWeightsPtr, bufferWeightsSize); + } + bool checkTarget(Target target) CV_OVERRIDE + { + return openvino::checkTarget(target); + } +}; + +static +std::shared_ptr& getInstanceNetworkBackendOpenVINO() +{ + static std::shared_ptr g_instance = std::make_shared(); + return g_instance; +} + + +}} // namespace + + +static +CvResult cv_getInstanceNetworkBackend(CV_OUT CvPluginDNNNetworkBackend* handle) CV_NOEXCEPT +{ + try + { + if (!handle) + return CV_ERROR_FAIL; + *handle = cv::dnn_backend::getInstanceNetworkBackendOpenVINO().get(); + return CV_ERROR_OK; + } + catch (...) + { + return CV_ERROR_FAIL; + } +} + +static const OpenCV_DNN_Plugin_API plugin_api = +{ + { + sizeof(OpenCV_DNN_Plugin_API), ABI_VERSION, API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "OpenVINO OpenCV DNN plugin (" CVAUX_STR(INF_ENGINE_RELEASE) ")" + }, + { + /* 1*/cv_getInstanceNetworkBackend + } +}; + +const OpenCV_DNN_Plugin_API* CV_API_CALL opencv_dnn_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) + return &plugin_api; + return NULL; +} + +#endif // BUILD_PLUGIN diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index 8a27dc2221..e237be07cf 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -11,7 +11,11 @@ #ifdef HAVE_INF_ENGINE #include -#endif // HAVE_INF_ENGINE +#elif defined(ENABLE_PLUGINS) +// using plugin API +#include "backend.hpp" +#include "factory.hpp" +#endif #include #include @@ -155,7 +159,6 @@ static bool detectMyriadX_(const std::string& device) } #endif // !defined(OPENCV_DNN_IE_VPU_TYPE_DEFAULT) - #endif // HAVE_INF_ENGINE @@ -281,24 +284,100 @@ bool checkTarget(Target target) #else // HAVE_INF_ENGINE + +namespace openvino { + +bool checkTarget(Target target) +{ +#if defined(ENABLE_PLUGINS) + try + { + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + return networkBackend.checkTarget(target); + } + catch (const std::exception& e) + { + CV_LOG_INFO(NULL, "DNN/OpenVINO: checkTarget failed: " << e.what()) + } +#endif + return false; +} + +} // namespace openvino + + cv::String getInferenceEngineBackendType() { +#if defined(ENABLE_PLUGINS) + try + { + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + CV_UNUSED(networkBackend); + return CV_DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; + } + catch (const std::exception& e) + { + CV_LOG_INFO(NULL, "DNN/OpenVINO: plugin is not available: " << e.what()) + } +#endif CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support"); } cv::String setInferenceEngineBackendType(const cv::String& newBackendType) { +#if defined(ENABLE_PLUGINS) + try + { + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + CV_UNUSED(networkBackend); + CV_Assert(newBackendType == CV_DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); + } + catch (const std::exception& e) + { + CV_LOG_INFO(NULL, "DNN/OpenVINO: plugin is not available: " << e.what()) + } +#endif CV_UNUSED(newBackendType); CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support"); } cv::String getInferenceEngineVPUType() { +#if defined(ENABLE_PLUGINS) + try + { + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + if (networkBackend.checkTarget(DNN_TARGET_MYRIAD)) + return CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X; // 2021.4 supports NCS2 only + CV_Error(Error::StsError, "DNN/OpenVINO: DNN_TARGET_MYRIAD is not available"); + } + catch (const std::exception& e) + { + CV_LOG_INFO(NULL, "DNN/OpenVINO: plugin is not available: " << e.what()) + } +#endif CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support"); } cv::String getInferenceEngineCPUType() { +#if defined(ENABLE_PLUGINS) + try + { + auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino"); + CV_UNUSED(networkBackend); +#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) + return CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE; +#else + return CV_DNN_INFERENCE_ENGINE_CPU_TYPE_X86; +#endif + } + catch (const std::exception& e) + { + CV_LOG_INFO(NULL, "DNN/OpenVINO: plugin is not available: " << e.what()) + } +#endif CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support"); } + #endif // HAVE_INF_ENGINE diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index 7f9f4bf47c..6ac4d955cc 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -60,6 +60,15 @@ namespace cv { namespace dnn { +CV__DNN_INLINE_NS_BEGIN +namespace openvino { + +// TODO: use std::string as parameter +bool checkTarget(Target target); + +} // namespace openvino +CV__DNN_INLINE_NS_END + #ifdef HAVE_INF_ENGINE Backend& getInferenceEngineBackendTypeParam(); @@ -75,13 +84,6 @@ CV__DNN_INLINE_NS_BEGIN void switchToOpenVINOBackend(Net& net); -namespace openvino { - -// TODO: use std::string as parameter -bool checkTarget(Target target); - -} // namespace openvino - bool isMyriadX(); bool isArmComputePlugin(); diff --git a/modules/dnn/src/plugin_api.hpp b/modules/dnn/src/plugin_api.hpp new file mode 100644 index 0000000000..83f4189df2 --- /dev/null +++ b/modules/dnn/src/plugin_api.hpp @@ -0,0 +1,72 @@ +// 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 DNN_PLUGIN_API_HPP +#define DNN_PLUGIN_API_HPP + +#include +#include + +#include "backend.hpp" + +#if !defined(BUILD_PLUGIN) + +/// increased for backward-compatible changes, e.g. add new function +/// Caller API <= Plugin API -> plugin is fully compatible +/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API +#define API_VERSION 0 // preview + +/// increased for incompatible changes, e.g. remove function argument +/// Caller ABI == Plugin ABI -> plugin is compatible +/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible) +/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that) +#define ABI_VERSION 0 // preview + +#else // !defined(BUILD_PLUGIN) + +#if !defined(ABI_VERSION) || !defined(API_VERSION) +#error "Plugin must define ABI_VERSION and API_VERSION before including plugin_api.hpp" +#endif + +#endif // !defined(BUILD_PLUGIN) + +typedef cv::dnn_backend::NetworkBackend* CvPluginDNNNetworkBackend; + +struct OpenCV_DNN_Plugin_API_v0_0_api_entries +{ + /** @brief Get backend API instance + + @param[out] handle pointer on inference backend API handle + + @note API-CALL 1, API-Version == 0 + */ + CvResult (CV_API_CALL *getInstance)(CV_OUT CvPluginDNNNetworkBackend* handle) CV_NOEXCEPT; +}; // OpenCV_DNN_Plugin_API_v0_0_api_entries + +typedef struct OpenCV_DNN_Plugin_API_v0 +{ + OpenCV_API_Header api_header; + struct OpenCV_DNN_Plugin_API_v0_0_api_entries v0; +} OpenCV_DNN_Plugin_API_v0; + +#if ABI_VERSION == 0 && API_VERSION == 0 +typedef OpenCV_DNN_Plugin_API_v0 OpenCV_DNN_Plugin_API; +#else +#error "Not supported configuration: check ABI_VERSION/API_VERSION" +#endif + +#ifdef BUILD_PLUGIN +extern "C" { + +CV_PLUGIN_EXPORTS +const OpenCV_DNN_Plugin_API* CV_API_CALL opencv_dnn_plugin_init_v0 + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; + +} // extern "C" +#else // BUILD_PLUGIN +typedef const OpenCV_DNN_Plugin_API* (CV_API_CALL *FN_opencv_dnn_plugin_init_t) + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/); +#endif // BUILD_PLUGIN + +#endif // DNN_PLUGIN_API_HPP diff --git a/modules/dnn/src/plugin_wrapper.impl.hpp b/modules/dnn/src/plugin_wrapper.impl.hpp new file mode 100644 index 0000000000..94b2e4d219 --- /dev/null +++ b/modules/dnn/src/plugin_wrapper.impl.hpp @@ -0,0 +1,319 @@ +// 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. + +// +// Not a standalone header, part of backend.cpp +// + +//================================================================================================== +// Dynamic backend implementation + +#include "opencv2/core/utils/plugin_loader.private.hpp" + +namespace cv { namespace impl { + +using namespace cv::dnn_backend; + +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) + +using namespace cv::plugin::impl; // plugin_loader.hpp + +class PluginDNNBackend CV_FINAL: public std::enable_shared_from_this +{ +protected: + void initPluginAPI() + { + const char* init_name = "opencv_dnn_plugin_init_v0"; + FN_opencv_dnn_plugin_init_t fn_init = reinterpret_cast(lib_->getSymbol(init_name)); + if (fn_init) + { + CV_LOG_DEBUG(NULL, "Found entry: '" << init_name << "'"); + for (int supported_api_version = API_VERSION; supported_api_version >= 0; supported_api_version--) + { + plugin_api_ = fn_init(ABI_VERSION, supported_api_version, NULL); + if (plugin_api_) + break; + } + if (!plugin_api_) + { + CV_LOG_INFO(NULL, "DNN: plugin is incompatible (can't be initialized): " << lib_->getName()); + return; + } + // NB: force strict minor version check (ABI is not preserved for now) + if (!checkCompatibility(plugin_api_->api_header, ABI_VERSION, API_VERSION, true)) + { + plugin_api_ = NULL; + return; + } + CV_LOG_INFO(NULL, "DNN: plugin is ready to use '" << plugin_api_->api_header.api_description << "'"); + } + else + { + CV_LOG_INFO(NULL, "DNN: plugin is incompatible, missing init function: '" << init_name << "', file: " << lib_->getName()); + } + } + + + bool checkCompatibility(const OpenCV_API_Header& api_header, unsigned int abi_version, unsigned int api_version, bool checkMinorOpenCVVersion) + { + if (api_header.opencv_version_major != CV_VERSION_MAJOR) + { + CV_LOG_ERROR(NULL, "DNN: wrong OpenCV major version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + if (!checkMinorOpenCVVersion) + { + // no checks for OpenCV minor version + } + else if (api_header.opencv_version_minor != CV_VERSION_MINOR) + { + CV_LOG_ERROR(NULL, "DNN: wrong OpenCV minor version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + CV_LOG_DEBUG(NULL, "DNN: initialized '" << api_header.api_description << "': built with " + << cv::format("OpenCV %d.%d (ABI/API = %d/%d)", + api_header.opencv_version_major, api_header.opencv_version_minor, + api_header.min_api_version, api_header.api_version) + << ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << abi_version << "/" << api_version << ")" + ); + if (api_header.min_api_version != abi_version) // future: range can be here + { + // actually this should never happen due to checks in plugin's init() function + CV_LOG_ERROR(NULL, "DNN: plugin is not supported due to incompatible ABI = " << api_header.min_api_version); + return false; + } + if (api_header.api_version != api_version) + { + CV_LOG_INFO(NULL, "DNN: NOTE: plugin is supported, but there is API version mismath: " + << cv::format("plugin API level (%d) != OpenCV API level (%d)", api_header.api_version, api_version)); + if (api_header.api_version < api_version) + { + CV_LOG_INFO(NULL, "DNN: NOTE: some functionality may be unavailable due to lack of support by plugin implementation"); + } + } + return true; + } + +public: + std::shared_ptr lib_; + const OpenCV_DNN_Plugin_API* plugin_api_; + + PluginDNNBackend(const std::shared_ptr& lib) + : lib_(lib) + , plugin_api_(NULL) + { + initPluginAPI(); + } + + std::shared_ptr createNetworkBackend() const + { + CV_Assert(plugin_api_); + + CvPluginDNNNetworkBackend instancePtr = NULL; + + if (plugin_api_->v0.getInstance) + { + if (CV_ERROR_OK == plugin_api_->v0.getInstance(&instancePtr)) + { + CV_Assert(instancePtr); + // TODO C++20 "aliasing constructor" + return std::shared_ptr(instancePtr, [](cv::dnn_backend::NetworkBackend*){}); // empty deleter + } + } + return std::shared_ptr(); + } + +}; // class PluginDNNBackend + + +class PluginDNNBackendFactory CV_FINAL: public IDNNBackendFactory +{ +public: + std::string baseName_; + std::shared_ptr backend; + bool initialized; +public: + PluginDNNBackendFactory(const std::string& baseName) + : baseName_(baseName) + , initialized(false) + { + // nothing, plugins are loaded on demand + } + + std::shared_ptr createNetworkBackend() const CV_OVERRIDE + { + if (!initialized) + { + const_cast(this)->initBackend(); + } + if (backend) + return backend->createNetworkBackend(); + return std::shared_ptr(); + } + +protected: + void initBackend() + { + AutoLock lock(getInitializationMutex()); + try + { + if (!initialized) + loadPlugin(); + } + catch (...) + { + CV_LOG_INFO(NULL, "DNN: exception during plugin loading: " << baseName_ << ". SKIP"); + } + initialized = true; + } + void loadPlugin(); +}; + +static +std::vector getPluginCandidates(const std::string& baseName) +{ + using namespace cv::utils; + using namespace cv::utils::fs; + const std::string baseName_l = toLowerCase(baseName); + const std::string baseName_u = toUpperCase(baseName); + const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l); + std::vector paths; + // TODO OPENCV_PLUGIN_PATH + const std::vector paths_ = getConfigurationParameterPaths("OPENCV_DNN_PLUGIN_PATH", std::vector()); + if (paths_.size() != 0) + { + for (size_t i = 0; i < paths_.size(); i++) + { + paths.push_back(toFileSystemPath(paths_[i])); + } + } + else + { + FileSystemPath_t binaryLocation; + if (getBinLocation(binaryLocation)) + { + binaryLocation = getParent(binaryLocation); +#ifndef CV_DNN_PLUGIN_SUBDIRECTORY + paths.push_back(binaryLocation); +#else + paths.push_back(binaryLocation + toFileSystemPath("/") + toFileSystemPath(CV_DNN_PLUGIN_SUBDIRECTORY_STR)); +#endif + } + } + const std::string default_expr = libraryPrefix() + "opencv_dnn_" + baseName_l + "*" + librarySuffix(); + const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_DNN_PLUGIN_") + baseName_u).c_str(), default_expr.c_str()); + std::vector results; +#ifdef _WIN32 + FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_dnn_" + baseName_l + librarySuffix()); + if (plugin_expr != default_expr) + { + moduleName = toFileSystemPath(plugin_expr); + results.push_back(moduleName); + } + for (const FileSystemPath_t& path : paths) + { + results.push_back(path + L"\\" + moduleName); + } + results.push_back(moduleName); +#else + CV_LOG_DEBUG(NULL, "DNN: " << baseName << " plugin's glob is '" << plugin_expr << "', " << paths.size() << " location(s)"); + for (const std::string& path : paths) + { + if (path.empty()) + continue; + std::vector candidates; + cv::glob(utils::fs::join(path, plugin_expr), candidates); + // Prefer candidates with higher versions + // TODO: implemented accurate versions-based comparator + std::sort(candidates.begin(), candidates.end(), std::greater()); + CV_LOG_DEBUG(NULL, " - " << path << ": " << candidates.size()); + copy(candidates.begin(), candidates.end(), back_inserter(results)); + } +#endif + CV_LOG_DEBUG(NULL, "Found " << results.size() << " plugin(s) for " << baseName); + return results; +} + +void PluginDNNBackendFactory::loadPlugin() +{ + for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_)) + { + auto lib = std::make_shared(plugin); + if (!lib->isLoaded()) + { + continue; + } + try + { + auto pluginBackend = std::make_shared(lib); + if (!pluginBackend) + { + continue; + } + if (pluginBackend->plugin_api_ == NULL) + { + CV_LOG_ERROR(NULL, "DNN: no compatible plugin API for backend: " << baseName_ << " in " << toPrintablePath(plugin)); + continue; + } + // NB: we are going to use backend, so prevent automatic library unloading + lib->disableAutomaticLibraryUnloading(); + backend = pluginBackend; + return; + } + catch (...) + { + CV_LOG_WARNING(NULL, "DNN: exception during plugin initialization: " << toPrintablePath(plugin) << ". SKIP"); + } + } +} + +#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) + +} // namespace + + + +namespace dnn_backend { + + +std::shared_ptr createPluginDNNBackendFactory(const std::string& baseName) +{ +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) + const std::string baseName_u = toUpperCase(baseName); + AutoLock lock(getInitializationMutex()); + static std::map> g_plugins_cache; + auto it = g_plugins_cache.find(baseName_u); + if (it == g_plugins_cache.end()) + { + auto factory = std::make_shared(baseName); + g_plugins_cache.insert(std::pair>(baseName_u, factory)); + return factory; + } + return it->second; +#else + CV_UNUSED(baseName); + return std::shared_ptr(); +#endif +} + + +cv::dnn_backend::NetworkBackend& createPluginDNNNetworkBackend(const std::string& baseName) +{ + auto factory = dnn_backend::createPluginDNNBackendFactory(baseName); + if (!factory) + { + CV_Error(Error::StsNotImplemented, cv::format("Plugin factory is not available: '%s'", baseName.c_str())); + } + auto backend = factory->createNetworkBackend(); + if (!backend) + { + CV_Error(Error::StsNotImplemented, cv::format("Backend (plugin) is not available: '%s'", baseName.c_str())); + } + return *backend; +} + + +}} // namespace diff --git a/modules/dnn/src/precomp.hpp b/modules/dnn/src/precomp.hpp index abcd3745f9..0100eb2c7f 100644 --- a/modules/dnn/src/precomp.hpp +++ b/modules/dnn/src/precomp.hpp @@ -39,8 +39,14 @@ // //M*/ -#include +#if !defined(BUILD_PLUGIN) #include "cvconfig.h" +#else +#include +#undef __OPENCV_BUILD // allow public API only +#endif + +#include #ifndef CV_OCL4DNN #define CV_OCL4DNN 0 diff --git a/modules/dnn/src/registry.cpp b/modules/dnn/src/registry.cpp index 697fca6015..56b96f4c4c 100644 --- a/modules/dnn/src/registry.cpp +++ b/modules/dnn/src/registry.cpp @@ -14,6 +14,8 @@ #include "halide_scheduler.hpp" +#include "backend.hpp" +#include "factory.hpp" namespace cv { namespace dnn { @@ -43,43 +45,46 @@ private: #endif #endif // HAVE_HALIDE + bool haveBackendOpenVINO = false; #ifdef HAVE_INF_ENGINE - if (openvino::checkTarget(DNN_TARGET_CPU)) + haveBackendOpenVINO = true; +#elif defined(ENABLE_PLUGINS) + { + auto factory = dnn_backend::createPluginDNNBackendFactory("openvino"); + if (factory) + { + auto backend = factory->createNetworkBackend(); + if (backend) + haveBackendOpenVINO = true; + } + } +#endif + + if (haveBackendOpenVINO && openvino::checkTarget(DNN_TARGET_CPU)) { -#ifdef HAVE_DNN_NGRAPH backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_CPU)); -#endif } - if (openvino::checkTarget(DNN_TARGET_MYRIAD)) + if (haveBackendOpenVINO && openvino::checkTarget(DNN_TARGET_MYRIAD)) { -#ifdef HAVE_DNN_NGRAPH backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_MYRIAD)); -#endif } - if (openvino::checkTarget(DNN_TARGET_HDDL)) + if (haveBackendOpenVINO && openvino::checkTarget(DNN_TARGET_HDDL)) { -#ifdef HAVE_DNN_NGRAPH backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_HDDL)); -#endif } #ifdef HAVE_OPENCL if (cv::ocl::useOpenCL() && ocl::Device::getDefault().isIntel()) { - if (openvino::checkTarget(DNN_TARGET_OPENCL)) + if (haveBackendOpenVINO && openvino::checkTarget(DNN_TARGET_OPENCL)) { -#ifdef HAVE_DNN_NGRAPH backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_OPENCL)); -#endif } - if (openvino::checkTarget(DNN_TARGET_OPENCL_FP16)) + if (haveBackendOpenVINO && openvino::checkTarget(DNN_TARGET_OPENCL_FP16)) { -#ifdef HAVE_DNN_NGRAPH backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_OPENCL_FP16)); -#endif } } -#endif -#endif // HAVE_INF_ENGINE +#endif // HAVE_OPENCL #ifdef HAVE_WEBNN if (haveWebnn()) @@ -132,10 +137,9 @@ std::vector getAvailableTargets(Backend be) { if (be == DNN_BACKEND_DEFAULT) be = (Backend)getParam_DNN_BACKEND_DEFAULT(); -#ifdef HAVE_INF_ENGINE + if (be == DNN_BACKEND_INFERENCE_ENGINE) be = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; -#endif std::vector result; const BackendRegistry::BackendsList all_backends = getAvailableBackends(); diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index 3d616e17da..e36374bd98 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -130,9 +130,7 @@ void normAssertTextDetections( void readFileContent(const std::string& filename, CV_OUT std::vector& content); -#ifdef HAVE_INF_ENGINE bool validateVPUType(); -#endif testing::internal::ParamGenerator< tuple > dnnBackendsAndTargets( bool withInferenceEngine = true, diff --git a/modules/dnn/test/test_common.impl.hpp b/modules/dnn/test/test_common.impl.hpp index 35f658cc90..5fdf6c3d1e 100644 --- a/modules/dnn/test/test_common.impl.hpp +++ b/modules/dnn/test/test_common.impl.hpp @@ -254,9 +254,7 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget bool withWebnn /*= false*/ ) { -#ifdef HAVE_INF_ENGINE bool withVPU = validateVPUType(); -#endif std::vector< tuple > targets; std::vector< Target > available; @@ -266,7 +264,6 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget for (std::vector< Target >::const_iterator i = available.begin(); i != available.end(); ++i) targets.push_back(make_tuple(DNN_BACKEND_HALIDE, *i)); } -#ifdef HAVE_INF_ENGINE if (withInferenceEngine) { available = getAvailableTargets(DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019); @@ -288,9 +285,6 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget } } -#else - CV_UNUSED(withInferenceEngine); -#endif if (withVkCom) { available = getAvailableTargets(DNN_BACKEND_VKCOM); @@ -356,7 +350,6 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget #endif } -#ifdef HAVE_INF_ENGINE static std::string getTestInferenceEngineVPUType() { static std::string param_vpu_type = utils::getConfigurationParameterString("OPENCV_TEST_DNN_IE_VPU_TYPE", ""); @@ -419,7 +412,6 @@ bool validateVPUType() static bool result = validateVPUType_(); return result; } -#endif // HAVE_INF_ENGINE void initDNNTests() From 9821fae59df5f25f0ee5a6c4d7fd102c5dea0906 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Sat, 8 Oct 2022 15:51:40 +0800 Subject: [PATCH 149/313] add greater_or_equal and less_or_equal ONNX support --- modules/dnn/src/layers/nary_eltwise_layers.cpp | 4 ++-- modules/dnn/src/onnx/onnx_importer.cpp | 6 ++++-- modules/dnn/test/test_onnx_importer.cpp | 10 ++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index db37f16060..9431ff2b4b 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -51,11 +51,11 @@ public: op = OPERATION::EQUAL; else if (operation == "greater") op = OPERATION::GREATER; - else if (operation == "greater_equal") + else if (operation == "greaterorequal") op = OPERATION::GREATER_EQUAL; else if (operation == "less") op = OPERATION::LESS; - else if (operation == "less_equal") + else if (operation == "lessorequal") op = OPERATION::LESS_EQUAL; else if (operation == "pow") op = OPERATION::POW; diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 025f69da91..013520d13d 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2912,7 +2912,7 @@ void ONNXImporter::parseCumSum(LayerParams& layerParams, const opencv_onnx::Node addLayer(layerParams, node_proto); } -// "Equal" "Greater" "Less" "Pow" "Add" "Sub" "Mul" "Div" "Sum" "Min" "Max" +// "Equal" "Greater" "Less" "Pow" "Add" "Sub" "Mul" "Div" "Sum" "Min" "Max" "GreaterOrEqual" "LessOrEqual" void ONNXImporter::parseElementWise(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { opencv_onnx::NodeProto node_proto = node_proto_; @@ -3721,7 +3721,9 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["SpaceToDepth"] = dispatch["DepthToSpace"] = &ONNXImporter::parseDepthToSpace; dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = dispatch["Pow"] = dispatch["Add"] = - dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = &ONNXImporter::parseElementWise; + dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = dispatch["GreaterOrEqual"] = + dispatch["LessOrEqual"] = &ONNXImporter::parseElementWise; + dispatch["Sum"] = dispatch["Min"] = dispatch["Max"] = &ONNXImporter::parseElementWise; dispatch["Range"] = &ONNXImporter::parseRange; diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 4eb6b5a91b..6372bcc659 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -682,6 +682,16 @@ TEST_P(Test_ONNX_layers, Compare_LT) testONNXModels("less"); } +TEST_P(Test_ONNX_layers, Compare_GTorEQ) +{ + testONNXModels("greater_or_equal"); +} + +TEST_P(Test_ONNX_layers, Compare_LEorEQ) +{ + testONNXModels("less_or_equal"); +} + TEST_P(Test_ONNX_layers, CompareSameDims_EQ) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) From 68bd156a71c39b7ca99a9d8b33023eee6845201c Mon Sep 17 00:00:00 2001 From: Eran Geva Date: Sat, 8 Oct 2022 12:05:33 +0300 Subject: [PATCH 150/313] add willReadFrequently on imread in js --- modules/js/src/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index a2f3101b08..59e6e65b29 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -55,7 +55,7 @@ Module['imread'] = function(imageSource) { canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; - ctx = canvas.getContext('2d'); + ctx = canvas.getContext('2d', { willReadFrequently: true }); ctx.drawImage(img, 0, 0, img.width, img.height); } else if (img instanceof HTMLCanvasElement) { canvas = img; From 1e2ceca4dfa889aa9c50b7d72076d3c0bd0eb85f Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Sun, 9 Oct 2022 09:33:07 +0800 Subject: [PATCH 151/313] add enableWinograd API for Net. --- .../dnn/include/opencv2/dnn/all_layers.hpp | 2 ++ modules/dnn/include/opencv2/dnn/dnn.hpp | 6 ++++ .../dnn/src/int8layers/convolution_layer.cpp | 2 +- modules/dnn/src/int8layers/layers_common.hpp | 2 +- modules/dnn/src/layers/convolution_layer.cpp | 5 +-- .../fast_convolution/fast_convolution.cpp | 16 ++++------ .../fast_convolution/fast_convolution.hpp | 4 +-- .../fast_convolution/winograd_3x3s1_f63.cpp | 2 +- modules/dnn/src/layers/layers_common.cpp | 4 ++- modules/dnn/src/layers/layers_common.hpp | 2 +- modules/dnn/src/net.cpp | 7 ++++ modules/dnn/src/net_impl.cpp | 32 +++++++++++++++++++ modules/dnn/src/net_impl.hpp | 2 ++ modules/dnn/src/net_quantization.cpp | 1 + modules/dnn/test/test_torch_importer.cpp | 1 + 15 files changed, 70 insertions(+), 18 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index f87a46ba5e..1cbc654603 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -259,6 +259,7 @@ CV__DNN_INLINE_NS_BEGIN bool fusedActivation = false; bool fusedAdd = false; bool isConv2D = false; // Should be deleted after fastconv branch support Conv1D and Conv3D. + bool useWinograd = false; // Flag whether to use Winograd to speed up 3x3 convolution. }; class CV_EXPORTS ConvolutionLayerInt8 : public BaseConvolutionLayer @@ -270,6 +271,7 @@ CV__DNN_INLINE_NS_BEGIN // quantization type flag. The perChannel default is true, that means it contains the parameters // of per-Channel quantization. Otherwise, that means this layer contains per-Tensor quantized parameters. bool per_channel; + bool useWinograd = true; // Flag whether to use Winograd to speed up 3x3 convolution. static Ptr create(const LayerParams& params); }; diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 78c18c15b7..29d6cfa4f3 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -837,6 +837,12 @@ CV__DNN_INLINE_NS_BEGIN */ CV_WRAP void enableFusion(bool fusion); + /** @brief Enables or disables the Winograd compute branch. The Winograd compute branch can speed up + * 3x3 Convolution at a small loss of accuracy. + * @param useWinograd true to enable the Winograd compute branch. The default is true. + */ + CV_WRAP void enableWinograd(bool useWinograd); + /** @brief Returns overall time for inference and timings (in ticks) for layers. * * Indexes in returned vector correspond to layers ids. Some layers can be fused with others, diff --git a/modules/dnn/src/int8layers/convolution_layer.cpp b/modules/dnn/src/int8layers/convolution_layer.cpp index 320a18e5ab..728ef24d91 100644 --- a/modules/dnn/src/int8layers/convolution_layer.cpp +++ b/modules/dnn/src/int8layers/convolution_layer.cpp @@ -41,7 +41,7 @@ public: BaseConvolutionLayerInt8Impl(const LayerParams ¶ms) { setParamsFrom(params); - getConvolutionKernelParams(params, kernel_size, pads_begin, pads_end, strides, dilations, padMode, adjust_pads); + getConvolutionKernelParams(params, kernel_size, pads_begin, pads_end, strides, dilations, padMode, adjust_pads, useWinograd); numOutput = params.get("num_output"); int ngroups = params.get("group", 1); diff --git a/modules/dnn/src/int8layers/layers_common.hpp b/modules/dnn/src/int8layers/layers_common.hpp index cb185a9eda..5fdafbeab8 100644 --- a/modules/dnn/src/int8layers/layers_common.hpp +++ b/modules/dnn/src/int8layers/layers_common.hpp @@ -23,7 +23,7 @@ namespace dnn { void getConvolutionKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, std::vector& dilations, - cv::String &padMode, std::vector& adjust_pads); + cv::String &padMode, std::vector& adjust_pads, bool& useWinograd); void getPoolingKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& globalPooling, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, cv::String &padMode); diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 678a052c7c..bc1acd0f72 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -89,7 +89,8 @@ public: BaseConvolutionLayerImpl(const LayerParams ¶ms) { setParamsFrom(params); - getConvolutionKernelParams(params, kernel_size, pads_begin, pads_end, strides, dilations, padMode, adjust_pads); + getConvolutionKernelParams(params, kernel_size, pads_begin, pads_end, strides, dilations, + padMode, adjust_pads, useWinograd); numOutput = params.get("num_output"); int ngroups = params.get("group", 1); @@ -2112,7 +2113,7 @@ public: int dilation_w = dilations.back(); fastConv2dImpl = initFastConv2d(ngroups, K, C, Hk, Wk, stride_w, stride_h, dilation_w, - dilation_h, pads_begin, pads_end, weightsMat, &biasvec[0]); + dilation_h, pads_begin, pads_end, weightsMat, &biasvec[0], useWinograd); } if (fastConv2dImpl) diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index d62b6f230c..8c829eaf81 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -23,7 +23,8 @@ Ptr initFastConv2d( const std::vector& pads_begin, const std::vector& pads_end, InputArray _weightsMat, - float* srcBias) + float* srcBias, + bool useWinograd) { Ptr conv = makePtr(); @@ -48,11 +49,11 @@ Ptr initFastConv2d( const size_t wstep = weightsMat.step1(); #if CV_NEON // For now, winograd is ARM platform only. - if (ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && + if (useWinograd && ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && dilation_x == 1 && dilation_y ==1 && K >= 16 && C >= 16) - conv->ifWinograd63 = true; + conv->useWinograd63 = true; #else - conv->ifWinograd63 = false; + conv->useWinograd63 = false; #endif float *srcWeights = (float *)weightsMat.data; @@ -115,7 +116,7 @@ Ptr initFastConv2d( }}); // Prepare Weight for Winograd F(6x6, 3x3) - if (conv->ifWinograd63) + if (conv->useWinograd63) { initWinograd63(conv, weightsMat, K, C); } @@ -191,10 +192,7 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr } #if CV_NEON - if (conv->ifWinograd63 - && inputShape[2] > 12 && inputShape[3] > 12 - && inputShape[2] < 120 && inputShape[3] < 120 - ) + if (conv->useWinograd63 && inputShape[2] > 12 && inputShape[3] > 12) { if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) return; diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index ba85077f70..671cb707d1 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -44,7 +44,7 @@ struct FastConv2d std::vector weightsBuf; // For generic Conv 2D std::vector weightsWino63Buf; // For Winograd F(6x6, 3x3). std::vector biasBuf; - bool ifWinograd63 = false; + bool useWinograd63 = false; bool useAVX2 = checkHardwareSupport(CPU_AVX2); bool useNEON = checkHardwareSupport(CPU_NEON); }; @@ -58,7 +58,7 @@ Ptr initFastConv2d( const std::vector& pads_begin, const std::vector& pads_end, InputArray weightsMat, - float* srcBias); + float* srcBias, bool useWinograd); // It contains different computing branches, like winograd, 1x1 conv. void runFastConv2d(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, diff --git a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp index fb668d63c6..7475397901 100644 --- a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp +++ b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp @@ -1689,7 +1689,7 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu void initWinograd63(Ptr& conv, InputArray _weightsMat, int K, int C) { - conv->ifWinograd63 = false; + conv->useWinograd63 = false; } int runWinograd63(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) diff --git a/modules/dnn/src/layers/layers_common.cpp b/modules/dnn/src/layers/layers_common.cpp index 445a89ff98..b128872817 100644 --- a/modules/dnn/src/layers/layers_common.cpp +++ b/modules/dnn/src/layers/layers_common.cpp @@ -187,12 +187,14 @@ void getPoolingKernelParams(const LayerParams ¶ms, std::vector& kern void getConvolutionKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, - std::vector& dilations, cv::String &padMode, std::vector& adjust_pads) + std::vector& dilations, cv::String &padMode, std::vector& adjust_pads, + bool& useWinograd) { util::getKernelSize(params, kernel); util::getStrideAndPadding(params, pads_begin, pads_end, strides, padMode, kernel.size()); util::getParameter(params, "dilation", "dilation", dilations, true, std::vector(kernel.size(), 1)); util::getParameter(params, "adj", "adj", adjust_pads, true, std::vector(kernel.size(), 0)); + useWinograd = params.get("use_winograd", true); for (int i = 0; i < dilations.size(); i++) CV_Assert(dilations[i] > 0); diff --git a/modules/dnn/src/layers/layers_common.hpp b/modules/dnn/src/layers/layers_common.hpp index 85f442c78e..4510f6b106 100644 --- a/modules/dnn/src/layers/layers_common.hpp +++ b/modules/dnn/src/layers/layers_common.hpp @@ -61,7 +61,7 @@ namespace dnn { void getConvolutionKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, std::vector& dilations, - cv::String &padMode, std::vector& adjust_pads); + cv::String &padMode, std::vector& adjust_pads, bool& useWinograd); void getPoolingKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& globalPooling, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, cv::String &padMode); diff --git a/modules/dnn/src/net.cpp b/modules/dnn/src/net.cpp index b3cf811a94..3b200a108e 100644 --- a/modules/dnn/src/net.cpp +++ b/modules/dnn/src/net.cpp @@ -395,6 +395,13 @@ void Net::enableFusion(bool fusion) return impl->enableFusion(fusion); } +void Net::enableWinograd(bool useWinograd) +{ + CV_TRACE_FUNCTION(); + CV_Assert(impl); + return impl->enableWinograd(useWinograd); +} + void Net::setHalideScheduler(const String& scheduler) { CV_TRACE_FUNCTION(); diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index 3839cba329..5411051484 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -55,6 +55,7 @@ Net::Impl::Impl() preferableBackend = (Backend)getParam_DNN_BACKEND_DEFAULT(); preferableTarget = DNN_TARGET_CPU; hasDynamicShapes = false; + useWinograd = true; } @@ -2038,6 +2039,37 @@ void Net::Impl::getMemoryConsumption( } } +void Net::Impl::enableWinograd(bool useWinograd_) +{ + if (useWinograd != useWinograd_) + { + useWinograd = useWinograd_; + + for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) + { + int lid = it->first; + LayerData &ld = layers[lid]; + Ptr& currLayer = ld.layerInstance; + + if (ld.type == "Convolution") + { + ld.params.set("use_winograd", useWinograd_); + Ptr convLayer = ld.layerInstance.dynamicCast(); + if (!convLayer.empty()) + convLayer->useWinograd = useWinograd_; + } + + if (ld.type == "ConvolutionInt8") + { + Ptr convLayer = currLayer.dynamicCast(); + ld.params.set("use_winograd", useWinograd_); + if (!convLayer.empty()) + convLayer->useWinograd = useWinograd_; + } + } + } +} + // TODO drop? void Net::Impl::getLayerTypes(std::vector& layersTypes) const diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index 290ce50c13..08ac1932ca 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -64,6 +64,7 @@ struct Net::Impl : public detail::NetImplBase bool netWasQuantized; bool fusion; bool isAsync; // FIXIT: drop + bool useWinograd; std::vector layersTimings; @@ -211,6 +212,7 @@ struct Net::Impl : public detail::NetImplBase void enableFusion(bool fusion_); virtual void fuseLayers(const std::vector& blobsToKeep_); + void enableWinograd(bool useWinograd_); void allocateLayers(const std::vector& blobsToKeep_); diff --git a/modules/dnn/src/net_quantization.cpp b/modules/dnn/src/net_quantization.cpp index 0add2d2d79..803a240770 100644 --- a/modules/dnn/src/net_quantization.cpp +++ b/modules/dnn/src/net_quantization.cpp @@ -51,6 +51,7 @@ Net Net::Impl::quantize(Net& net, InputArrayOfArrays calibData, int inputsDtype, setPreferableBackend(net, DNN_BACKEND_OPENCV); setPreferableTarget(DNN_TARGET_CPU); enableFusion(false); + enableWinograd(false); if (calibData.isMat()) { diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index bd9572748b..260e95537d 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -476,6 +476,7 @@ TEST_P(Test_Torch_nets, ENet_accuracy) ASSERT_TRUE(!net.empty()); } + net.enableWinograd(false); net.setPreferableBackend(backend); net.setPreferableTarget(target); From d9eff7daeb0534a8f15da90d40cd8d1d79a5bf52 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Mon, 10 Oct 2022 17:08:46 +0800 Subject: [PATCH 152/313] parse quantized nodes does not rely on name. --- modules/dnn/src/int8layers/pooling_layer.cpp | 4 +- modules/dnn/src/onnx/onnx_importer.cpp | 272 ++++++++++++------- 2 files changed, 170 insertions(+), 106 deletions(-) diff --git a/modules/dnn/src/int8layers/pooling_layer.cpp b/modules/dnn/src/int8layers/pooling_layer.cpp index 98cf17c06c..6c52d19e83 100644 --- a/modules/dnn/src/int8layers/pooling_layer.cpp +++ b/modules/dnn/src/int8layers/pooling_layer.cpp @@ -26,11 +26,11 @@ public: computeMaxIdx = false; globalPooling = false; isGlobalPooling = std::vector(3, false); - output_zp = params.get("zeropoints"); + output_zp = params.get("zeropoints", 0); input_zp = params.get("input_zeropoint", output_zp); multiplier = params.get("multiplier", 1.f); - output_sc = params.get("scales"); + output_sc = params.get("scales", 1.f); input_sc = multiplier * output_sc; hasDynamicShapes = params.get("has_dynamic_shapes", false); diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 259fd29eeb..a687acedfa 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -52,6 +52,13 @@ extern bool DNN_DIAGNOSTICS_RUN; class ONNXLayerHandler; +template +static T getScaleFromMat(Mat m) +{ + CV_Assert(m.total() == 1); + return m.at(0); +} + class ONNXImporter { FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; @@ -60,7 +67,9 @@ class ONNXImporter struct LayerInfo { int layerId; int outputId; - LayerInfo(int _layerId = 0, int _outputId = 0) : layerId(_layerId), outputId(_outputId) {} + int depth; + LayerInfo(int _layerId = 0, int _outputId = 0, int _depth = CV_32F) + :layerId(_layerId), outputId(_outputId), depth(_depth) {} }; struct TensorInfo { @@ -80,8 +89,7 @@ class ONNXImporter void addConstant(const std::string& name, const Mat& blob); void addLayer(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); - void handleQuantizedNode(LayerParams& layerParams, - const opencv_onnx::NodeProto& node_proto); + void setParamsDtype(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void expandMid(const std::string& prefix, opencv_onnx::NodeProto& node_proto, const std::string& input, size_t n); @@ -607,7 +615,7 @@ void ONNXImporter::addLayer(LayerParams& layerParams, const std::string& output_name = node_proto.output(i); if (!output_name.empty()) { - layer_id.insert(std::make_pair(output_name, LayerInfo(id, i))); + layer_id.insert(std::make_pair(output_name, LayerInfo(id, i, depth))); } } @@ -754,49 +762,71 @@ void ONNXImporter::parseOperatorSet() } } -void ONNXImporter::handleQuantizedNode(LayerParams& layerParams, - const opencv_onnx::NodeProto& node_proto) +static bool ifInt8Output(const String& layerType) { - // Quantized nodes have output names ending with 'quantized' - std::string outName = node_proto.output(0); - int len = outName.length(); - if (len <= 9) - return; + // Contains all node types whose output should be int8 when it get int8 input. + // ai.onnx opset 15 + static std::vector input8output8List = { + "QuantizeLinear", + "QLinearAdd", + "QLinearMul", + "QLinearAveragePool", + "QLinearGlobalAveragePool", + "QLinearLeakyRelu", + "QLinearSigmoid", + "QLinearConcat", + "QGemm", + "QLinearConv", + "QLinearMatMul", + "MaxPool", + "ReduceMax", + "ReduceMin", + "Split", + "Clip", + "Abs", + "Transpose", + "Squeeze", + "Flatten", + "Unsqueeze", + "Expand", + "Reshape", + "Pad", + "Gather", + "Concat", + "Resize", + "SpaceToDepth", + "DepthToSpace", + "Pow", + "Add", + "Sub", + "Mul", + "Div" + }; + auto layerIt = std::find(input8output8List.begin(), input8output8List.end(), layerType); + return layerIt != input8output8List.end(); +} - if (outName.substr(len - 9) == "quantized") +void ONNXImporter::setParamsDtype(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + // If the current layer should output the same data type as the input, and it's input type is int8, we think the current + // layer should also output int8. + + // Check if the layer has int8 input. + const std::string& layer_type = node_proto.op_type(); + for (int i = 0; i < node_proto.input_size(); ++i) { - outName = outName.substr(0, len - 9); - Mat scale, zeropoint; - - if (constBlobs.find(outName + "scale") != constBlobs.end() && - constBlobs.find(outName + "zero_point") != constBlobs.end()) + if (layer_id.find(node_proto.input(i)) != layer_id.end()) { - scale = getBlob(outName + "scale"); - zeropoint = getBlob(outName + "zero_point"); - } - else - { - std::string inpName = node_proto.input(0); - inpName = inpName.substr(0, inpName.length() - 9); - scale = getBlob(inpName + "scale"); - zeropoint = getBlob(inpName + "zero_point"); + LayerInfo layerInfo = layer_id.find(node_proto.input(i))->second; - for (int i = 0; i < node_proto.output_size(); i++) + if (layerInfo.depth == CV_8S && ifInt8Output(layer_type)) { - std::string out = node_proto.output(i); - out = out.substr(0, out.length() - 9); - addConstant(out + "scale", scale); - addConstant(out + "zero_point", zeropoint); + layerParams.set("depth", CV_8S); + return; } } - - if (scale.total() != 1 || zeropoint.total() != 1) - CV_Error(Error::StsNotImplemented, "Per-channel scales/zeropoints are not supported"); - - layerParams.set("depth", CV_8S); - layerParams.set("scales", DictValue::arrayReal(scale.ptr(), 1)); - layerParams.set("zeropoints", DictValue::arrayInt(zeropoint.ptr(), 1)); } + layerParams.set("depth", CV_32F); } void ONNXImporter::populateNet() @@ -979,7 +1009,7 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto) const std::string& layer_type_domain = getLayerTypeDomain(node_proto); const auto& dispatch = getDispatchMap(node_proto); - CV_LOG_DEBUG(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and " + CV_LOG_INFO(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: " << cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str()) << cv::format(" from %sdomain='", onnx_opset_map.count(layer_type_domain) == 1 ? "" : "undeclared ") @@ -1001,7 +1031,7 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto) layerParams.type = layer_type; layerParams.set("has_dynamic_shapes", hasDynamicShapes); - handleQuantizedNode(layerParams, node_proto); + setParamsDtype(layerParams, node_proto); DispatchMap::const_iterator iter = dispatch.find(layer_type); if (iter != dispatch.end()) @@ -3113,17 +3143,22 @@ void ONNXImporter::parseCustomLayer(LayerParams& layerParams, const opencv_onnx: void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - CV_Assert(node_proto.input_size() == 3); + CV_Assert(node_proto.input_size() == 2 || node_proto.input_size() == 3); layerParams.type = (node_proto.op_type() == "QuantizeLinear") ? "Quantize" : "Dequantize"; - if (node_proto.op_type() == "DequantizeLinear") - { - Mat scale = getBlob(node_proto, 1); - Mat zeropoint = getBlob(node_proto, 2); + float scale = getScaleFromMat(getBlob(node_proto, 1)); + int zeropoint = 0; + if (node_proto.input_size() == 3) + zeropoint = (int)getScaleFromMat(getBlob(node_proto, 2)); + + layerParams.set("scales", scale); + layerParams.set("zeropoints", zeropoint); + + if (layerParams.type == "Quantize") + layerParams.set("depth", CV_8S); + else // Dequantize + layerParams.set("depth", CV_32F); - layerParams.set("scales", DictValue::arrayReal(scale.ptr(), 1)); - layerParams.set("zeropoints", DictValue::arrayInt(zeropoint.ptr(), 1)); - } addLayer(layerParams, node_proto); } @@ -3133,8 +3168,8 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP int ninputs = node_proto.input_size(); CV_Assert(ninputs == 8 || ninputs == 9); - Mat inp_sc = getBlob(node_proto, 1); - Mat inp_zp = getBlob(node_proto, 2); + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int inp_zp = (int)getScaleFromMat(getBlob(node_proto, 2)); if (layerParams.has("pad")) { @@ -3164,7 +3199,7 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP padLp.type = "PaddingInt8"; padLp.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size())); padLp.set("depth", CV_8S); - padLp.set("value", inp_zp.at(0)); + padLp.set("value", inp_zp); opencv_onnx::NodeProto proto; proto.add_input(node_proto.input(0)); @@ -3179,10 +3214,12 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP int outCn = weights.size[0]; Mat w_scale = getBlob(node_proto, 4); CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn); - bool per_channel = w_scale.total() == outCn ? true : false; + bool per_channel = w_scale.total() == outCn; Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at(0))); - Mat out_sc = getBlob(node_proto, 6); + float out_sc = getScaleFromMat(getBlob(node_proto, 6)); + int8_t out_zp = getScaleFromMat(getBlob(node_proto, 7)); + Mat bias = (ninputs == 9) ? getBlob(node_proto, 8) : Mat::zeros(1, outCn, CV_32S); Mat weights_2d = weights.reshape(1, outCn); @@ -3190,14 +3227,16 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP Mat outputMultiplier(1, outCn, CV_32F); for (int i = 0; i < outCn; i++) { - biasFused.at(i) = bias.at(i) - inp_zp.at(0)*(cv::sum(weights_2d.row(i))[0]); - outputMultiplier.at(i) = (inp_sc.at(0) * wt_sc.at(i)) / out_sc.at(0); + biasFused.at(i) = bias.at(i) - inp_zp*(cv::sum(weights_2d.row(i))[0]); + outputMultiplier.at(i) = (inp_sc * wt_sc.at(i)) / out_sc; } layerParams.type = "ConvolutionInt8"; layerParams.set("num_output", outCn); - layerParams.set("input_zeropoint", inp_zp.at(0)); - layerParams.set("input_scale",inp_sc.at(0)); + layerParams.set("input_zeropoint", inp_zp); + layerParams.set("input_scale",inp_sc); + layerParams.set("zeropoints", out_zp); + layerParams.set("scales", out_sc); layerParams.set("per_channel", per_channel); layerParams.blobs.push_back(weights); layerParams.blobs.push_back(biasFused); @@ -3215,8 +3254,8 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod int firstInpDims = outShapes[node_proto.input(0)].size(); - Mat inp_sc = getBlob(node_proto, 1); - Mat inp_zp = getBlob(node_proto, 2); + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); Mat weights = getBlob(node_proto, 3).t(); int outCn = weights.size[0]; @@ -3226,21 +3265,25 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn); bool per_channel = w_scale.total() == outCn ? true : false; Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at(0))); - Mat out_sc = getBlob(node_proto, 6); + + float out_sc = getScaleFromMat(getBlob(node_proto, 6)); + int8_t out_zp = getScaleFromMat(getBlob(node_proto, 7)); Mat bias(1, outCn, CV_32S); Mat outputMultiplier(1, outCn, CV_32F); for (int i = 0; i < outCn; i++) { - bias.at(i) = -inp_zp.at(0)*(cv::sum(weights.row(i))[0]); - outputMultiplier.at(i) = (inp_sc.at(0) * wt_sc.at(i)) / out_sc.at(0); + bias.at(i) = -inp_zp*(cv::sum(weights.row(i))[0]); + outputMultiplier.at(i) = (inp_sc * wt_sc.at(i)) / out_sc; } layerParams.type = "InnerProductInt8"; layerParams.set("num_output", outCn); layerParams.set("axis", firstInpDims - secondInpDims + 1); - layerParams.set("input_scale", inp_sc.at(0)); - layerParams.set("input_zeropoint", inp_zp.at(0)); + layerParams.set("input_scale", inp_sc); + layerParams.set("input_zeropoint", inp_zp); + layerParams.set("zeropoints", out_zp); + layerParams.set("scales", out_sc); layerParams.set("per_channel", per_channel); layerParams.blobs.push_back(weights); @@ -3273,8 +3316,8 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP int firstInpDims = outShapes[node_proto.input(0)].size(); - Mat inp_sc = getBlob(node_proto, 1); - Mat inp_zp = getBlob(node_proto, 2); + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); int outCn = weights.size[0]; int secondInpDims = weights.dims; @@ -3293,8 +3336,10 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP CV_Error(Error::StsUnsupportedFormat, "The zero-point non-zero case of W is not supported!"); } - Mat out_sc, bias; - out_sc = getBlob(node_proto, 7); + float out_sc = getScaleFromMat(getBlob(node_proto, 7)); + int8_t out_zp = ninputs == 9 ? getScaleFromMat(getBlob(node_proto, 8)) : 0; + + Mat bias; if (constBlobs.find(node_proto.input(6)) != constBlobs.end()) bias = getBlob(node_proto, 6); else @@ -3304,15 +3349,17 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP Mat outputMultiplier(1, outCn, CV_32F); for (int i = 0; i < outCn; i++) { - biasFused.at(i) = bias.at(i) - inp_zp.at(0)*(cv::sum(weights.row(i))[0]); - outputMultiplier.at(i) = (inp_sc.at(0) * wt_sc.at(i)) / out_sc.at(0); + biasFused.at(i) = bias.at(i) - inp_zp*(cv::sum(weights.row(i))[0]); + outputMultiplier.at(i) = (inp_sc * wt_sc.at(i)) / out_sc; } layerParams.type = "InnerProductInt8"; layerParams.set("num_output", outCn); layerParams.set("axis", firstInpDims - secondInpDims + 1); - layerParams.set("input_scale", inp_sc.at(0)); - layerParams.set("input_zeropoint", inp_zp.at(0)); + layerParams.set("input_scale", inp_sc); + layerParams.set("input_zeropoint", inp_zp); + layerParams.set("scales", out_sc); + layerParams.set("zeropoints", out_zp); layerParams.set("per_channel", per_channel); layerParams.blobs.push_back(weights); @@ -3324,7 +3371,7 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { opencv_onnx::NodeProto node_proto = node_proto_; - CV_Assert(node_proto.input_size() == 8); + CV_Assert(node_proto.input_size() == 7 || node_proto.input_size() == 8); std::string op = (node_proto.op_type() == "QLinearAdd") ? "sum" : "prod"; int constId = -1; for (int i = 0; i < 4; i += 3) @@ -3333,11 +3380,11 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No constId = i; } - Mat inp_0_sc = getBlob(node_proto, 1); - Mat inp_0_zp = getBlob(node_proto, 2); + float inp_0_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_0_zp = getScaleFromMat(getBlob(node_proto, 2)); - Mat inp_1_sc = getBlob(node_proto, 4); - Mat inp_1_zp = getBlob(node_proto, 5); + float inp_1_sc = getScaleFromMat(getBlob(node_proto, 4)); + int8_t inp_1_zp = getScaleFromMat(getBlob(node_proto, 5)); // Set 2nd input as the const input if (constId == 0) @@ -3346,11 +3393,14 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No cv::swap(inp_0_zp, inp_1_zp); } - float out_sc = getBlob(node_proto, 6).at(0); - int8_t out_zp = getBlob(node_proto, 7).at(0); + float out_sc = getScaleFromMat(getBlob(node_proto, 6)); - std::vector inp_scales = {inp_0_sc.at(0), inp_1_sc.at(0)}; - std::vector inp_zps = {inp_0_zp.at(0), inp_1_zp.at(0)}; + int8_t out_zp = 0; + if (node_proto.input_size() == 8) + out_zp = getScaleFromMat(getBlob(node_proto, 7)); + + std::vector inp_scales = {inp_0_sc, inp_1_sc}; + std::vector inp_zps = {inp_0_zp, inp_1_zp}; std::vector coeffs; float offset; @@ -3401,12 +3451,12 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No constParams.name = layerParams.name + "/const"; constParams.type = "ConstInt8"; constParams.set("depth", CV_8S); - constParams.set("scales", DictValue::arrayReal(inp_1_sc.ptr(), 1)); - constParams.set("zeropoints", DictValue::arrayInt(inp_1_zp.ptr(), 1)); + constParams.set("scales", inp_1_sc); + constParams.set("zeropoints", inp_1_zp); constParams.blobs.push_back(blob); int id = dstNet.addLayer(constParams.name, constParams.type, CV_8S, constParams); - layer_id.insert(std::make_pair(constParams.name, LayerInfo(id, 0))); + layer_id.insert(std::make_pair(constParams.name, LayerInfo(id, 0, CV_8S))); outShapes[constParams.name] = shape(blob); node_proto.set_input(constId, constParams.name); @@ -3452,18 +3502,21 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No layerParams.set("input_scales", DictValue::arrayReal(inp_scales.data(), inp_scales.size())); layerParams.set("input_zeropoints", DictValue::arrayInt(inp_zps.data(), inp_zps.size())); + layerParams.set("scales", out_sc); + layerParams.set("zeropoints", out_zp); + addLayer(layerParams, node_proto); } void ONNXImporter::parseQLeakyRelu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - CV_Assert(node_proto.input_size() == 5); + CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); float slope = layerParams.get("alpha"); - float inp_sc = getBlob(node_proto, 1).at(0); - int8_t inp_zp = getBlob(node_proto, 2).at(0); - float out_sc = getBlob(node_proto, 3).at(0); - int8_t out_zp = getBlob(node_proto, 4).at(0); + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); + float out_sc = getScaleFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); Mat lookUpTable(1, 256, CV_8S); int8_t* table = lookUpTable.ptr(); @@ -3478,6 +3531,8 @@ void ONNXImporter::parseQLeakyRelu(LayerParams& layerParams, const opencv_onnx:: layerParams.type = "ReLUInt8"; layerParams.set("input_scale", inp_sc); layerParams.set("input_zeropoint", inp_zp); + layerParams.set("scales", out_sc); + layerParams.set("zeropoints", out_zp); layerParams.set("slope", slope); layerParams.blobs.push_back(lookUpTable); addLayer(layerParams, node_proto); @@ -3485,12 +3540,12 @@ void ONNXImporter::parseQLeakyRelu(LayerParams& layerParams, const opencv_onnx:: void ONNXImporter::parseQSigmoid(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - CV_Assert(node_proto.input_size() == 5); + CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); - float inp_sc = getBlob(node_proto, 1).at(0); - int8_t inp_zp = getBlob(node_proto, 2).at(0); - float out_sc = getBlob(node_proto, 3).at(0); - int8_t out_zp = getBlob(node_proto, 4).at(0); + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); + float out_sc = getScaleFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); Mat lookUpTable(1, 256, CV_8S); int8_t* table = lookUpTable.ptr(); @@ -3505,22 +3560,29 @@ void ONNXImporter::parseQSigmoid(LayerParams& layerParams, const opencv_onnx::No layerParams.type = "SigmoidInt8"; layerParams.set("input_scale", inp_sc); layerParams.set("input_zeropoint", inp_zp); + layerParams.set("scales", out_sc); + layerParams.set("zeropoints", out_zp); layerParams.blobs.push_back(lookUpTable); addLayer(layerParams, node_proto); } void ONNXImporter::parseQAvgPool(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { - CV_Assert(node_proto.input_size() == 5); - float inp_sc = getBlob(node_proto, 1).at(0); - int8_t inp_zp = getBlob(node_proto, 2).at(0); - float out_sc = getBlob(node_proto, 3).at(0); + CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); + + float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); + float out_sc = getScaleFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); layerParams.type = "PoolingInt8"; layerParams.set("pool", "ave"); layerParams.set("global_pooling", node_proto.op_type() == "QLinearGlobalAveragePool"); layerParams.set("multiplier", inp_sc/out_sc); + layerParams.set("input_scale", inp_sc); layerParams.set("input_zeropoint", inp_zp); + layerParams.set("scales", out_sc); + layerParams.set("zeropoints", out_zp); addLayer(layerParams, node_proto); } @@ -3530,13 +3592,13 @@ void ONNXImporter::parseQConcat(LayerParams& layerParams, const opencv_onnx::Nod layerParams.type = "ConcatInt8"; int num_inputs = node_proto.input_size(); - float out_scale = getBlob(node_proto, 0).at(0); - int out_zp = getBlob(node_proto, 1).at(0); + float out_scale = getScaleFromMat(getBlob(node_proto, 0)); + int8_t out_zp = getScaleFromMat(getBlob(node_proto, 1)); for (int i = 2; i < num_inputs; i += 3) { - float inp_scale = getBlob(node_proto, i + 1).at(0); - int inp_zp = getBlob(node_proto, i + 2).at(0); + float inp_scale = getScaleFromMat(getBlob(node_proto, i + 1)); + int8_t inp_zp = getScaleFromMat(getBlob(node_proto, i + 2)); if (inp_scale != out_scale || inp_zp != out_zp) { @@ -3624,6 +3686,8 @@ void ONNXImporter::parseQConcat(LayerParams& layerParams, const opencv_onnx::Nod } } } + layerParams.set("scales", out_scale); + layerParams.set("zeropoints", out_zp); addLayer(layerParams, node_proto); } @@ -3639,8 +3703,8 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["MaxPool"] = &ONNXImporter::parseMaxPool; dispatch["AveragePool"] = &ONNXImporter::parseAveragePool; dispatch["GlobalAveragePool"] = dispatch["GlobalMaxPool"] = &ONNXImporter::parseGlobalPool; - dispatch["ReduceMax"] = dispatch["ReduceMin"] = dispatch["ReduceMean"] = dispatch["ReduceSum"] = dispatch["ReduceMax"] = - dispatch["ReduceMin"] = dispatch["ReduceSumSquare"] = dispatch["ReduceProd"] = dispatch["ReduceL1"] = + dispatch["ReduceMax"] = dispatch["ReduceMin"] = dispatch["ReduceMean"] = dispatch["ReduceSum"] = + dispatch["ReduceSumSquare"] = dispatch["ReduceProd"] = dispatch["ReduceL1"] = dispatch["ReduceL2"] = dispatch["ReduceLogSum"] = dispatch["ReduceLogSumExp"] = &ONNXImporter::parseReduce; dispatch["Slice"] = &ONNXImporter::parseSlice; dispatch["Split"] = &ONNXImporter::parseSplit; From 8b0aa6a64d8c1a6dfeab2177b2bfd1fa71c8700c Mon Sep 17 00:00:00 2001 From: Harvey Huang <619328684@qq.com> Date: Tue, 11 Oct 2022 19:25:35 +0800 Subject: [PATCH 153/313] Merge pull request #21966 from Harvey-Huang:Unicode_Path Support use memory buffer to read multi-page image --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 14 ++ modules/imgcodecs/src/loadsave.cpp | 151 ++++++++++++++++++ modules/imgcodecs/test/test_tiff.cpp | 37 +++++ 3 files changed, 202 insertions(+) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 18b04d5687..a95a4ccdb4 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -306,6 +306,20 @@ reallocations when the function is called repeatedly for images of the same size */ CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst); +/** @brief Reads a multi-page image from a buffer in memory. + +The function imdecodemulti reads a multi-page image from the specified buffer in the memory. If the buffer is too short or +contains invalid data, the function returns false. + +See cv::imreadmulti for the list of supported formats and flags description. + +@note In the case of color images, the decoded images will have the channels stored in **B G R** order. +@param buf Input array or vector of bytes. +@param flags The same flags as in cv::imread, see cv::ImreadModes. +@param mats A vector of Mat objects holding each page, if more than one. +*/ +CV_EXPORTS_W bool imdecodemulti(InputArray buf, int flags, CV_OUT std::vector& mats); + /** @brief Encodes an image into a memory buffer. The function imencode compresses the image and stores it in the memory buffer that is resized to fit the diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index daad280fec..10c4dcad2d 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -929,6 +929,157 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) return *dst; } +static bool +imdecodemulti_(const Mat& buf, int flags, std::vector& mats, int start, int count) +{ + CV_Assert(!buf.empty()); + CV_Assert(buf.isContinuous()); + CV_Assert(buf.checkVector(1, CV_8U) > 0); + Mat buf_row = buf.reshape(1, 1); // decoders expects single row, avoid issues with vector columns + + String filename; + + ImageDecoder decoder = findDecoder(buf_row); + if (!decoder) + return 0; + + if (count < 0) { + count = std::numeric_limits::max(); + } + + if (!decoder->setSource(buf_row)) + { + filename = tempfile(); + FILE* f = fopen(filename.c_str(), "wb"); + if (!f) + return 0; + size_t bufSize = buf_row.total() * buf.elemSize(); + if (fwrite(buf_row.ptr(), 1, bufSize, f) != bufSize) + { + fclose(f); + CV_Error(Error::StsError, "failed to write image data to temporary file"); + } + if (fclose(f) != 0) + { + CV_Error(Error::StsError, "failed to write image data to temporary file"); + } + decoder->setSource(filename); + } + + // read the header to make sure it succeeds + bool success = false; + try + { + // read the header to make sure it succeeds + if (decoder->readHeader()) + success = true; + } + catch (const cv::Exception& e) + { + std::cerr << "imreadmulti_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush; + } + catch (...) + { + std::cerr << "imreadmulti_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush; + } + + int current = start; + while (success && current > 0) + { + if (!decoder->nextPage()) + { + success = false; + break; + } + --current; + } + + if (!success) + { + decoder.release(); + if (!filename.empty()) + { + if (0 != remove(filename.c_str())) + { + std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush; + } + } + return 0; + } + + while (current < count) + { + // grab the decoded type + int type = decoder->type(); + if ((flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED) + { + if ((flags & IMREAD_ANYDEPTH) == 0) + type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); + + if ((flags & IMREAD_COLOR) != 0 || + ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1)) + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); + else + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); + } + + // established the required input image size + Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); + + // read the image data + Mat mat(size.height, size.width, type); + success = false; + try + { + if (decoder->readData(mat)) + success = true; + } + catch (const cv::Exception& e) + { + std::cerr << "imreadmulti_('" << filename << "'): can't read data: " << e.what() << std::endl << std::flush; + } + catch (...) + { + std::cerr << "imreadmulti_('" << filename << "'): can't read data: unknown exception" << std::endl << std::flush; + } + if (!success) + break; + + // optionally rotate the data if EXIF' orientation flag says so + if ((flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED) + { + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat); + } + + mats.push_back(mat); + if (!decoder->nextPage()) + { + break; + } + ++current; + } + + if (!filename.empty()) + { + if (0 != remove(filename.c_str())) + { + std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush; + } + } + + if (!success) + mats.clear(); + return !mats.empty(); +} + +bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector& mats) +{ + CV_TRACE_FUNCTION(); + + Mat buf = _buf.getMat(); + return imdecodemulti_(buf, flags, mats, 0, -1); +} + bool imencode( const String& ext, InputArray _image, std::vector& buf, const std::vector& params ) { diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index 76a39c0fae..dc8624dd31 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -442,6 +442,43 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage) } } +TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer) +{ + const int mode = GetParam(); + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + "readwrite/multipage_p1.tif", + "readwrite/multipage_p2.tif", + "readwrite/multipage_p3.tif", + "readwrite/multipage_p4.tif", + "readwrite/multipage_p5.tif", + "readwrite/multipage_p6.tif" + }; + const size_t page_count = sizeof(page_files) / sizeof(page_files[0]); + vector pages; + + FILE* fp = fopen(filename.c_str(), "rb"); + ASSERT_TRUE(fp != NULL); + fseek(fp, 0, SEEK_END); + long pos = ftell(fp); + + std::vector buf; + buf.resize((size_t)pos); + fseek(fp, 0, SEEK_SET); + buf.resize(fread(&buf[0], 1, buf.size(), fp)); + fclose(fp); + + bool res = imdecodemulti(buf, mode, pages); + ASSERT_TRUE(res == true); + ASSERT_EQ(page_count, pages.size()); + for (size_t i = 0; i < page_count; i++) + { + const Mat page = imread(root + page_files[i], mode); + EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), page, pages[i]); + } +} + const int all_modes[] = { IMREAD_UNCHANGED, From 9119692bb84a0944ce17f4e770e6941edc0c3297 Mon Sep 17 00:00:00 2001 From: zoom Date: Wed, 12 Oct 2022 11:47:31 +0800 Subject: [PATCH 154/313] let StridedSlice layer support const input --- modules/dnn/src/tensorflow/tf_importer.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 96e0af99ec..44e70bac41 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -1706,6 +1706,19 @@ void TFImporter::parseStridedSlice(tensorflow::GraphDef& net, const tensorflow:: layerParams.set("begin", DictValue::arrayInt((int*)begins.data, begins.total())); layerParams.set("end", DictValue::arrayInt((int*)ends.data, ends.total())); + Pin inp = parsePin(layer.input(0)); + if (value_id.find(inp.name) != value_id.end()) + { + // The input is constant. + LayerParams lp; + lp.name = inp.name; + lp.type = "Const"; + lp.blobs.push_back(getTensorContent(getConstBlob(layer, value_id, 0))); + + int constInpId = dstNet.addLayer(lp.name, lp.type, lp); + layer_id[lp.name] = constInpId; + } + int id = dstNet.addLayer(name, "Slice", layerParams); layer_id[name] = id; From cfafd0493cd84f2e67a7e6f29a3fb5676abbba98 Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Wed, 12 Oct 2022 14:41:19 +0300 Subject: [PATCH 155/313] Workflow Ubuntu 20.04 x64 with CUDA support (4.x) --- .github/workflows/PR-4.x.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index 2142e0fb6c..a8514f6577 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -12,6 +12,10 @@ jobs: Ubuntu2004-x64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20.yaml@main + Ubuntu2004-x64-CUDA: + if: "${{ contains(github.event.pull_request.labels.*.name, 'category: dnn') }}" + uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20-Cuda.yaml@main + Windows10-x64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-W10.yaml@main From 35f43cc429e3d39c71e3692fedbe0c9bd29ca877 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sat, 17 Sep 2022 13:02:02 +0200 Subject: [PATCH 156/313] core: expose rectangle intersection to bindings --- modules/core/include/opencv2/core/types.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/core/include/opencv2/core/types.hpp b/modules/core/include/opencv2/core/types.hpp index 2867520361..ca6e29bf2d 100644 --- a/modules/core/include/opencv2/core/types.hpp +++ b/modules/core/include/opencv2/core/types.hpp @@ -2017,6 +2017,15 @@ double jaccardDistance(const Rect_<_Tp>& a, const Rect_<_Tp>& b) { return 1.0 - Aab / (Aa + Ab - Aab); } +/** @brief Finds out if there is any intersection between two rectangles + * + * mainly useful for language bindings + * @param rect1 First rectangle + * @param rect2 Second rectangle + * @return the area of the intersection + */ +CV_EXPORTS_W inline double rectangleIntersectionArea(const Rect2d& a, const Rect2d& b) { return (a & b).area(); } + ////////////////////////////// RotatedRect ////////////////////////////// inline From c0ecf08ca0821adb71e0ea9649f03b64ef888510 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sat, 17 Sep 2022 13:04:22 +0200 Subject: [PATCH 157/313] cmake: use upstream PCH support if possible --- cmake/OpenCVPCHSupport.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVPCHSupport.cmake b/cmake/OpenCVPCHSupport.cmake index 08cd06def4..34a088b839 100644 --- a/cmake/OpenCVPCHSupport.cmake +++ b/cmake/OpenCVPCHSupport.cmake @@ -331,7 +331,8 @@ ENDMACRO(ADD_PRECOMPILED_HEADER) MACRO(GET_NATIVE_PRECOMPILED_HEADER _targetName _input) if(ENABLE_PRECOMPILED_HEADERS) - if(CMAKE_GENERATOR MATCHES "^Visual.*$") + if(CMAKE_GENERATOR MATCHES "^Visual.*$" + AND (CMAKE_VERSION VERSION_LESS "3.16" OR OPENCV_SKIP_CMAKE_BUILTIN_PCH)) # with 3.16+ we use target_precompile_headers set(${_targetName}_pch ${CMAKE_CURRENT_BINARY_DIR}/${_targetName}_pch.cpp) endif() endif() @@ -406,7 +407,9 @@ ENDMACRO(ADD_NATIVE_PRECOMPILED_HEADER) macro(ocv_add_precompiled_header_to_target the_target pch_header) if(PCHSupport_FOUND AND ENABLE_PRECOMPILED_HEADERS AND EXISTS "${pch_header}") - if(CMAKE_GENERATOR MATCHES "^Visual" OR CMAKE_GENERATOR MATCHES Xcode) + if(NOT CMAKE_VERSION VERSION_LESS "3.16" AND NOT OPENCV_SKIP_CMAKE_BUILTIN_PCH) + target_precompile_headers(${the_target} PRIVATE ${pch_header}) + elseif(CMAKE_GENERATOR MATCHES "^Visual" OR CMAKE_GENERATOR MATCHES Xcode) add_native_precompiled_header(${the_target} ${pch_header}) elseif(CV_GCC AND CMAKE_GENERATOR MATCHES "Makefiles|Ninja") add_precompiled_header(${the_target} ${pch_header}) From 70779d4e66946dca4e0da602f10ec37573b9ac88 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sat, 17 Sep 2022 13:04:42 +0200 Subject: [PATCH 158/313] calib3d: use OCV_LAPACK_FUNC --- modules/calib3d/src/usac/dls_solver.cpp | 2 +- modules/calib3d/src/usac/essential_solver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/calib3d/src/usac/dls_solver.cpp b/modules/calib3d/src/usac/dls_solver.cpp index 0abb26cecc..8f109d51bf 100644 --- a/modules/calib3d/src/usac/dls_solver.cpp +++ b/modules/calib3d/src/usac/dls_solver.cpp @@ -160,7 +160,7 @@ public: double wr[27], wi[27] = {0}; // 27 = mat_order std::vector work(lwork), eig_vecs(729); char jobvl = 'N', jobvr = 'V'; // only left eigen vectors are computed - dgeev_(&jobvl, &jobvr, &mat_order, (double*)solution_polynomial.data, &lda, wr, wi, nullptr, &ldvl, + OCV_LAPACK_FUNC(dgeev)(&jobvl, &jobvr, &mat_order, (double*)solution_polynomial.data, &lda, wr, wi, nullptr, &ldvl, &eig_vecs[0], &ldvr, &work[0], &lwork, &info); if (info != 0) return 0; #endif diff --git a/modules/calib3d/src/usac/essential_solver.cpp b/modules/calib3d/src/usac/essential_solver.cpp index 0adca0966f..014cd36f40 100644 --- a/modules/calib3d/src/usac/essential_solver.cpp +++ b/modules/calib3d/src/usac/essential_solver.cpp @@ -161,7 +161,7 @@ public: int mat_order = 10, info, lda = 10, ldvl = 10, ldvr = 1, lwork = 100; double wr[10], wi[10] = {0}, eig_vecs[100], work[100]; // 10 = mat_order, 100 = lwork char jobvl = 'V', jobvr = 'N'; // only left eigen vectors are computed - dgeev_(&jobvl, &jobvr, &mat_order, action_mat_data, &lda, wr, wi, eig_vecs, &ldvl, + OCV_LAPACK_FUNC(dgeev)(&jobvl, &jobvr, &mat_order, action_mat_data, &lda, wr, wi, eig_vecs, &ldvl, nullptr, &ldvr, work, &lwork, &info); if (info != 0) return 0; #endif From f89dee4f3e12469d2d161ef09086aa8267c5b485 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:15:40 +0300 Subject: [PATCH 159/313] Reset cuda error code to cudasuccess. --- modules/core/include/opencv2/core/cuda/common.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/cuda/common.hpp b/modules/core/include/opencv2/core/cuda/common.hpp index 80b2ff08b1..b36b3d17b5 100644 --- a/modules/core/include/opencv2/core/cuda/common.hpp +++ b/modules/core/include/opencv2/core/cuda/common.hpp @@ -65,8 +65,10 @@ namespace cv { namespace cuda { static inline void checkCudaError(cudaError_t err, const char* file, const int line, const char* func) { - if (cudaSuccess != err) + if (cudaSuccess != err) { + cudaGetLastError(); // reset the last stored error to cudaSuccess cv::error(cv::Error::GpuApiCallError, cudaGetErrorString(err), func, file, line); + } } }} From a565aa6db9a0a21066e41ffd8a8cd9b5f0805dbd Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 13 Oct 2022 20:40:56 +0000 Subject: [PATCH 160/313] docs: prefer # for links generation - avoid `@ref` - align with 4.x branch (minimize merge conflicts) --- modules/calib3d/include/opencv2/calib3d.hpp | 65 ++++++++++----------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index e13d488018..3e6ab372b0 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -642,7 +642,7 @@ CV_EXPORTS Mat findHomography( InputArray srcPoints, InputArray dstPoints, @param Qz Optional output 3x3 rotation matrix around z-axis. The function computes a RQ decomposition using the given rotations. This function is used in -@ref decomposeProjectionMatrix to decompose the left 3x3 submatrix of a projection matrix into a camera +#decomposeProjectionMatrix to decompose the left 3x3 submatrix of a projection matrix into a camera and a rotation matrix. It optionally returns three rotation matrices, one for each axis, and the three Euler angles in @@ -676,7 +676,7 @@ be used in OpenGL. Note, there is always more than one sequence of rotations abo principal axes that results in the same orientation of an object, e.g. see @cite Slabaugh . Returned tree rotation matrices and corresponding three Euler angles are only one of the possible solutions. -The function is based on @ref RQDecomp3x3 . +The function is based on #RQDecomp3x3 . */ CV_EXPORTS_W void decomposeProjectionMatrix( InputArray projMatrix, OutputArray cameraMatrix, OutputArray rotMatrix, OutputArray transVect, @@ -696,7 +696,7 @@ CV_EXPORTS_W void decomposeProjectionMatrix( InputArray projMatrix, OutputArray The function computes partial derivatives of the elements of the matrix product \f$A*B\f$ with regard to the elements of each of the two input matrices. The function is used to compute the Jacobian -matrices in @ref stereoCalibrate but can also be used in any other similar optimization function. +matrices in #stereoCalibrate but can also be used in any other similar optimization function. */ CV_EXPORTS_W void matMulDeriv( InputArray A, InputArray B, OutputArray dABdA, OutputArray dABdB ); @@ -722,10 +722,10 @@ The functions compute: \f[\begin{array}{l} \texttt{rvec3} = \mathrm{rodrigues} ^{-1} \left ( \mathrm{rodrigues} ( \texttt{rvec2} ) \cdot \mathrm{rodrigues} ( \texttt{rvec1} ) \right ) \\ \texttt{tvec3} = \mathrm{rodrigues} ( \texttt{rvec2} ) \cdot \texttt{tvec1} + \texttt{tvec2} \end{array} ,\f] where \f$\mathrm{rodrigues}\f$ denotes a rotation vector to a rotation matrix transformation, and -\f$\mathrm{rodrigues}^{-1}\f$ denotes the inverse transformation. See @ref Rodrigues for details. +\f$\mathrm{rodrigues}^{-1}\f$ denotes the inverse transformation. See #Rodrigues for details. Also, the functions can compute the derivatives of the output vectors with regards to the input -vectors (see @ref matMulDeriv ). The functions are used inside @ref stereoCalibrate but can also be used in +vectors (see #matMulDeriv ). The functions are used inside #stereoCalibrate but can also be used in your own code where Levenberg-Marquardt or another gradient-based solver is used to optimize a function that contains a matrix multiplication. */ @@ -1103,7 +1103,7 @@ CV_EXPORTS_W Mat initCameraMatrix2D( InputArrayOfArrays objectPoints, @param flags Various operation flags that can be zero or a combination of the following values: - @ref CALIB_CB_ADAPTIVE_THRESH Use adaptive thresholding to convert the image to black and white, rather than a fixed threshold level (computed from the average image brightness). -- @ref CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with @ref equalizeHist before +- @ref CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with #equalizeHist before applying fixed or adaptive thresholding. - @ref CALIB_CB_FILTER_QUADS Use additional criteria (like contour area, perimeter, square-like shape) to filter out false quads extracted at the contour retrieval stage. @@ -1117,7 +1117,7 @@ are found and they are placed in a certain order (row by row, left to right in e Otherwise, if the function fails to find all the corners or reorder them, it returns 0. For example, a regular chessboard has 8 x 8 squares and 7 x 7 internal corners, that is, points where the black squares touch each other. The detected coordinates are approximate, and to determine their positions -more accurately, the function calls @ref cornerSubPix. You also may use the function @ref cornerSubPix with +more accurately, the function calls #cornerSubPix. You also may use the function #cornerSubPix with different parameters if returned coordinates are not accurate enough. Sample usage of detecting and drawing chessboard corners: : @@ -1154,9 +1154,9 @@ CV_EXPORTS_W bool find4QuadCornerSubpix( InputArray img, InputOutputArray corner @param image Destination image. It must be an 8-bit color image. @param patternSize Number of inner corners per a chessboard row and column (patternSize = cv::Size(points_per_row,points_per_column)). -@param corners Array of detected corners, the output of @ref findChessboardCorners. +@param corners Array of detected corners, the output of #findChessboardCorners. @param patternWasFound Parameter indicating whether the complete board was found or not. The -return value of @ref findChessboardCorners should be passed here. +return value of #findChessboardCorners should be passed here. The function draws individual chessboard corners detected either as red circles if the board was not found, or as colored corners connected with lines if the board was found. @@ -1542,7 +1542,7 @@ Besides the stereo-related information, the function can also perform a full cal the two cameras. However, due to the high dimensionality of the parameter space and noise in the input data, the function can diverge from the correct solution. If the intrinsic parameters can be estimated with high accuracy for each of the cameras individually (for example, using -@ref calibrateCamera ), you are recommended to do so and then pass @ref CALIB_FIX_INTRINSIC flag to the +#calibrateCamera ), you are recommended to do so and then pass @ref CALIB_FIX_INTRINSIC flag to the function along with the computed intrinsic parameters. Otherwise, if all the parameters are estimated at once, it makes sense to restrict some parameters, for example, pass @ref CALIB_SAME_FOCAL_LENGTH and @ref CALIB_ZERO_TANGENT_DIST flags, which is usually a @@ -1608,7 +1608,7 @@ pixels from the original images from the cameras are retained in the rectified i image pixels are lost). Any intermediate value yields an intermediate result between those two extreme cases. @param newImageSize New image resolution after rectification. The same size should be passed to -@ref initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) +#initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) is passed (default), it is set to the original imageSize . Setting it to a larger value can help you preserve details in the original image, especially when there is a big radial distortion. @param validPixROI1 Optional output rectangles inside the rectified images where all the pixels @@ -1620,7 +1620,7 @@ are valid. If alpha=0 , the ROIs cover the whole images. Otherwise, they are lik The function computes the rotation matrices for each camera that (virtually) make both camera image planes the same plane. Consequently, this makes all the epipolar lines parallel and thus simplifies -the dense stereo correspondence problem. The function takes the matrices computed by @ref stereoCalibrate +the dense stereo correspondence problem. The function takes the matrices computed by #stereoCalibrate as input. As output, it provides two rotation matrices and also two projection matrices in the new coordinates. The function distinguishes the following two cases: @@ -1678,7 +1678,7 @@ coordinates. The function distinguishes the following two cases: @ref CALIB_ZERO_DISPARITY is set. As you can see, the first three columns of P1 and P2 will effectively be the new "rectified" camera -matrices. The matrices, together with R1 and R2 , can then be passed to @ref initUndistortRectifyMap to +matrices. The matrices, together with R1 and R2 , can then be passed to #initUndistortRectifyMap to initialize the rectification map for each camera. See below the screenshot from the stereo_calib.cpp sample. Some red horizontal lines pass through @@ -1701,9 +1701,9 @@ CV_EXPORTS_W void stereoRectify( InputArray cameraMatrix1, InputArray distCoeffs @param points1 Array of feature points in the first image. @param points2 The corresponding points in the second image. The same formats as in -@ref findFundamentalMat are supported. +#findFundamentalMat are supported. @param F Input fundamental matrix. It can be computed from the same set of point pairs using -@ref findFundamentalMat . +#findFundamentalMat . @param imgSize Size of the image. @param H1 Output rectification homography matrix for the first image. @param H2 Output rectification homography matrix for the second image. @@ -1714,7 +1714,7 @@ are rejected prior to computing the homographies. Otherwise, all the points are The function computes the rectification transformations without knowing intrinsic parameters of the cameras and their relative position in the space, which explains the suffix "uncalibrated". Another -related difference from @ref stereoRectify is that the function outputs not the rectification +related difference from #stereoRectify is that the function outputs not the rectification transformations in the object (3D) space, but the planar perspective transformations encoded by the homography matrices H1 and H2 . The function implements the algorithm @cite Hartley99 . @@ -1723,8 +1723,8 @@ homography matrices H1 and H2 . The function implements the algorithm @cite Hart depends on the epipolar geometry. Therefore, if the camera lenses have a significant distortion, it would be better to correct it before computing the fundamental matrix and calling this function. For example, distortion coefficients can be estimated for each head of stereo camera - separately by using @ref calibrateCamera . Then, the images can be corrected using @ref undistort , or - just the point coordinates can be corrected with @ref undistortPoints . + separately by using #calibrateCamera . Then, the images can be corrected using #undistort , or + just the point coordinates can be corrected with #undistortPoints . */ CV_EXPORTS_W bool stereoRectifyUncalibrated( InputArray points1, InputArray points2, InputArray F, Size imgSize, @@ -1752,10 +1752,10 @@ assumed. @param imageSize Original image size. @param alpha Free scaling parameter between 0 (when all the pixels in the undistorted image are valid) and 1 (when all the source image pixels are retained in the undistorted image). See -@ref stereoRectify for details. +#stereoRectify for details. @param newImgSize Image size after rectification. By default, it is set to imageSize . @param validPixROI Optional output rectangle that outlines all-good-pixels region in the -undistorted image. See roi1, roi2 description in @ref stereoRectify . +undistorted image. See roi1, roi2 description in #stereoRectify . @param centerPrincipalPoint Optional flag that indicates whether in the new camera intrinsic matrix the principal point should be at the image center or not. By default, the principal point is chosen to best fit a subset of the source image (determined by alpha) to the corrected image. @@ -1767,7 +1767,7 @@ image pixels if there is valuable information in the corners alpha=1 , or get so When alpha\>0 , the undistorted result is likely to have some black pixels corresponding to "virtual" pixels outside of the captured distorted image. The original camera intrinsic matrix, distortion coefficients, the computed new camera intrinsic matrix, and newImageSize should be passed to -@ref initUndistortRectifyMap to produce the maps for @ref remap . +#initUndistortRectifyMap to produce the maps for #remap . */ CV_EXPORTS_W Mat getOptimalNewCameraMatrix( InputArray cameraMatrix, InputArray distCoeffs, Size imageSize, double alpha, Size newImgSize = Size(), @@ -1934,7 +1934,7 @@ CV_EXPORTS_W void convertPointsFromHomogeneous( InputArray src, OutputArray dst @param dst Output vector of 2D, 3D, or 4D points. The function converts 2D or 3D points from/to homogeneous coordinates by calling either -@ref convertPointsToHomogeneous or @ref convertPointsFromHomogeneous. +#convertPointsToHomogeneous or #convertPointsFromHomogeneous. @note The function is obsolete. Use one of the previous two functions instead. */ @@ -1971,9 +1971,9 @@ the found fundamental matrix. Normally just one matrix is found. But in case of algorithm, the function may return up to 3 solutions ( \f$9 \times 3\f$ matrix that stores all 3 matrices sequentially). -The calculated fundamental matrix may be passed further to @ref computeCorrespondEpilines that finds the +The calculated fundamental matrix may be passed further to #computeCorrespondEpilines that finds the epipolar lines corresponding to the specified points. It can also be passed to -@ref stereoRectifyUncalibrated to compute the rectification transformation. : +#stereoRectifyUncalibrated to compute the rectification transformation. : @code // Example. Estimation of fundamental matrix using the RANSAC algorithm int point_count = 100; @@ -2037,7 +2037,7 @@ This function estimates essential matrix based on the five-point algorithm solve where \f$E\f$ is an essential matrix, \f$p_1\f$ and \f$p_2\f$ are corresponding points in the first and the second images, respectively. The result of this function may be passed further to -@ref decomposeEssentialMat or @ref recoverPose to recover the relative pose between cameras. +#decomposeEssentialMat or #recoverPose to recover the relative pose between cameras. */ CV_EXPORTS_W Mat findEssentialMat( InputArray points1, InputArray points2, InputArray cameraMatrix, int method, @@ -2234,14 +2234,14 @@ CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray point @param points Input points. \f$N \times 1\f$ or \f$1 \times N\f$ matrix of type CV_32FC2 or vector\ . @param whichImage Index of the image (1 or 2) that contains the points . -@param F Fundamental matrix that can be estimated using @ref findFundamentalMat or @ref stereoRectify . +@param F Fundamental matrix that can be estimated using #findFundamentalMat or #stereoRectify . @param lines Output vector of the epipolar lines corresponding to the points in the other image. Each line \f$ax + by + c=0\f$ is encoded by 3 numbers \f$(a, b, c)\f$ . For every point in one of the two images of a stereo pair, the function finds the equation of the corresponding epipolar line in the other image. -From the fundamental matrix definition (see @ref findFundamentalMat ), line \f$l^{(2)}_i\f$ in the second +From the fundamental matrix definition (see #findFundamentalMat ), line \f$l^{(2)}_i\f$ in the second image for the point \f$p^{(1)}_i\f$ in the first image (when whichImage=1 ) is computed as: \f[l^{(2)}_i = F p^{(1)}_i\f] @@ -2598,11 +2598,10 @@ CV_EXPORTS_W int decomposeHomographyMat(InputArray H, @param beforePoints Vector of (rectified) visible reference points before the homography is applied @param afterPoints Vector of (rectified) visible reference points after the homography is applied @param possibleSolutions Vector of int indices representing the viable solution set after filtering -@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the -@ref findHomography function +@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the #findHomography function -This function is intended to filter the output of the @ref decomposeHomographyMat based on additional -information as described in @cite Malis2007 . The summary of the method: the @ref decomposeHomographyMat function +This function is intended to filter the output of the #decomposeHomographyMat based on additional +information as described in @cite Malis2007 . The summary of the method: the #decomposeHomographyMat function returns 2 unique solutions and their "opposites" for a total of 4 solutions. If we have access to the sets of points visible in the camera frame before and after the homography transformation is applied, we can determine which are the true potential solutions and which are the opposites by verifying which @@ -2992,14 +2991,14 @@ optimization. It stays at the center or at a different location specified when @ camera. @param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second camera. - @param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see @ref reprojectImageTo3D ). + @param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see #reprojectImageTo3D ). @param flags Operation flags that may be zero or @ref fisheye::CALIB_ZERO_DISPARITY . If the flag is set, the function makes the principal points of each camera have the same pixel coordinates in the rectified views. And if the flag is not set, the function may still shift the images in the horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the useful image area. @param newImageSize New image resolution after rectification. The same size should be passed to - @ref initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) + #initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0) is passed (default), it is set to the original imageSize . Setting it to larger value can help you preserve details in the original image, especially when there is a big radial distortion. @param balance Sets the new focal length in range between the min focal length and the max focal From 085fb78e85a4cf44ee2f6f3afc6d60c23296703d Mon Sep 17 00:00:00 2001 From: Hyunggi Chang Date: Sun, 9 Oct 2022 21:58:09 +0900 Subject: [PATCH 161/313] fix typo (portatibility -> portability) --- modules/core/include/opencv2/core/cvdef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 9102316968..f1ea80c06c 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -723,7 +723,7 @@ Cv64suf; -// Integer types portatibility +// Integer types portability #ifdef OPENCV_STDINT_HEADER #include OPENCV_STDINT_HEADER #elif defined(__cplusplus) From 0fa43e3aaca8112551f36e910b085be300ae0b75 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Fri, 14 Oct 2022 10:15:45 +0800 Subject: [PATCH 162/313] Optimize the winograd futher more. --- .../depthwise_convolution.cpp | 2 +- .../fast_convolution.avx2.cpp | 380 +++ .../fast_convolution/fast_convolution.cpp | 134 +- .../fast_convolution/fast_convolution.hpp | 51 +- .../fast_convolution/winograd_3x3s1_f63.cpp | 2680 +++++++---------- modules/dnn/test/test_backends.cpp | 2 +- modules/dnn/test/test_layers.cpp | 1 + 7 files changed, 1587 insertions(+), 1663 deletions(-) diff --git a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp index 4eb47c46b2..4e5adf25d1 100644 --- a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp @@ -333,7 +333,7 @@ void runDepthwise(InputArray _input, OutputArray _output, const Ptr& ofstab[k] = dy * Wi + dx; } - const float *weights0 = conv->weightsBuf.data(), *bias = conv->biasBuf.data(); + const float *weights0 = conv->weightsBufPtr, *bias = conv->biasBuf.data(); int inner_ytop = (pad_bottom + stride_y - 1) / stride_y, inner_ybottom = 3; int inner_xleft = (pad_left + stride_x - 1) / stride_x, inner_xright = 4; diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp index de1a2ef6b8..604e45e628 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp @@ -354,7 +354,387 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights x1 = W0; } } + _mm256_zeroupper(); } + +void _fx_winograd_accum_f32(const float* inwptr, const float* wptr, + float* outbuf, int Cg, int iblock) +{ + CV_Assert(_FX_WINO_IBLOCK == 6 && _FX_WINO_KBLOCK == 4);// && _FX_WINO_ATOM_F32 == 8); + if (iblock > 3) + { + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, + outbuf += _FX_WINO_ATOM_F32) + { + __m256 s00 = _mm256_set1_ps(0.f), s01 = s00, s02 = s00, s03 = s00, s04 = s00, s05 = s00; + __m256 s10 = _mm256_set1_ps(0.f), s11 = s00, s12 = s00, s13 = s00, s14 = s00, s15 = s00; + __m256 s20 = _mm256_set1_ps(0.f), s21 = s00, s22 = s00, s23 = s00, s24 = s00, s25 = s00; + __m256 s30 = _mm256_set1_ps(0.f), s31 = s00, s32 = s00, s33 = s00, s34 = s00, s35 = s00; + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) + { + __m256 w0 = _mm256_load_ps(wptr), w1 = _mm256_load_ps(wptr + 8); + __m256 w2 = _mm256_load_ps(wptr + 16), w3 = _mm256_load_ps(wptr + 24); + __m256 x0, x1; + x0 = _mm256_load_ps(inwptr); + x1 = _mm256_load_ps(inwptr + 8); + s00 = _mm256_fmadd_ps(w0, x0, s00); + s01 = _mm256_fmadd_ps(w0, x1, s01); + s10 = _mm256_fmadd_ps(w1, x0, s10); + s11 = _mm256_fmadd_ps(w1, x1, s11); + s20 = _mm256_fmadd_ps(w2, x0, s20); + s21 = _mm256_fmadd_ps(w2, x1, s21); + s30 = _mm256_fmadd_ps(w3, x0, s30); + s31 = _mm256_fmadd_ps(w3, x1, s31); + x0 = _mm256_load_ps(inwptr + 16); + x1 = _mm256_load_ps(inwptr + 24); + s02 = _mm256_fmadd_ps(w0, x0, s02); + s03 = _mm256_fmadd_ps(w0, x1, s03); + s12 = _mm256_fmadd_ps(w1, x0, s12); + s13 = _mm256_fmadd_ps(w1, x1, s13); + s22 = _mm256_fmadd_ps(w2, x0, s22); + s23 = _mm256_fmadd_ps(w2, x1, s23); + s32 = _mm256_fmadd_ps(w3, x0, s32); + s33 = _mm256_fmadd_ps(w3, x1, s33); + x0 = _mm256_load_ps(inwptr + 32); + x1 = _mm256_load_ps(inwptr + 40); + s04 = _mm256_fmadd_ps(w0, x0, s04); + s05 = _mm256_fmadd_ps(w0, x1, s05); + s14 = _mm256_fmadd_ps(w1, x0, s14); + s15 = _mm256_fmadd_ps(w1, x1, s15); + s24 = _mm256_fmadd_ps(w2, x0, s24); + s25 = _mm256_fmadd_ps(w2, x1, s25); + s34 = _mm256_fmadd_ps(w3, x0, s34); + s35 = _mm256_fmadd_ps(w3, x1, s35); + } + + _mm256_store_ps(outbuf, s00); + _mm256_store_ps(outbuf + 1*64, s01); + _mm256_store_ps(outbuf + 2*64, s02); + _mm256_store_ps(outbuf + 3*64, s03); + _mm256_store_ps(outbuf + 4*64, s04); + _mm256_store_ps(outbuf + 5*64, s05); + + _mm256_store_ps(outbuf + 6*64, s10); + _mm256_store_ps(outbuf + 7*64, s11); + _mm256_store_ps(outbuf + 8*64, s12); + _mm256_store_ps(outbuf + 9*64, s13); + _mm256_store_ps(outbuf + 10*64, s14); + _mm256_store_ps(outbuf + 11*64, s15); + + _mm256_store_ps(outbuf + 12*64, s20); + _mm256_store_ps(outbuf + 13*64, s21); + _mm256_store_ps(outbuf + 14*64, s22); + _mm256_store_ps(outbuf + 15*64, s23); + _mm256_store_ps(outbuf + 16*64, s24); + _mm256_store_ps(outbuf + 17*64, s25); + + _mm256_store_ps(outbuf + 18*64, s30); + _mm256_store_ps(outbuf + 19*64, s31); + _mm256_store_ps(outbuf + 20*64, s32); + _mm256_store_ps(outbuf + 21*64, s33); + _mm256_store_ps(outbuf + 22*64, s34); + _mm256_store_ps(outbuf + 23*64, s35); + } + } + else + { + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, + outbuf += _FX_WINO_ATOM_F32) + { + __m256 s00 = _mm256_set1_ps(0.f), s01 = s00, s02 = s00; + __m256 s10 = _mm256_set1_ps(0.f), s11 = s00, s12 = s00; + __m256 s20 = _mm256_set1_ps(0.f), s21 = s00, s22 = s00; + __m256 s30 = _mm256_set1_ps(0.f), s31 = s00, s32 = s00; + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) { + __m256 w0 = _mm256_load_ps(wptr), w1 = _mm256_load_ps(wptr + 8); + __m256 w2 = _mm256_load_ps(wptr + 16), w3 = _mm256_load_ps(wptr + 24); + __m256 x0, x1, x2; + x0 = _mm256_load_ps(inwptr); + x1 = _mm256_load_ps(inwptr + 8); + x2 = _mm256_load_ps(inwptr + 16); + s00 = _mm256_fmadd_ps(w0, x0, s00); + s01 = _mm256_fmadd_ps(w0, x1, s01); + s02 = _mm256_fmadd_ps(w0, x2, s02); + s10 = _mm256_fmadd_ps(w1, x0, s10); + s11 = _mm256_fmadd_ps(w1, x1, s11); + s12 = _mm256_fmadd_ps(w1, x2, s12); + s20 = _mm256_fmadd_ps(w2, x0, s20); + s21 = _mm256_fmadd_ps(w2, x1, s21); + s22 = _mm256_fmadd_ps(w2, x2, s22); + s30 = _mm256_fmadd_ps(w3, x0, s30); + s31 = _mm256_fmadd_ps(w3, x1, s31); + s32 = _mm256_fmadd_ps(w3, x2, s32); + } + + _mm256_store_ps(outbuf, s00); + _mm256_store_ps(outbuf + 1*64, s01); + _mm256_store_ps(outbuf + 2*64, s02); + _mm256_store_ps(outbuf + 6*64, s10); + _mm256_store_ps(outbuf + 7*64, s11); + _mm256_store_ps(outbuf + 8*64, s12); + _mm256_store_ps(outbuf + 12*64, s20); + _mm256_store_ps(outbuf + 13*64, s21); + _mm256_store_ps(outbuf + 14*64, s22); + _mm256_store_ps(outbuf + 18*64, s30); + _mm256_store_ps(outbuf + 19*64, s31); + _mm256_store_ps(outbuf + 20*64, s32); + } + } + _mm256_zeroupper(); +} +static inline +void transpose8_ps(__m256 &row0, __m256 &row1, __m256 &row2, __m256 &row3, __m256 &row4, __m256 &row5, __m256 &row6, __m256 &row7) +{ + __m256 __t0, __t1, __t2, __t3, __t4, __t5, __t6, __t7; + __m256 __tt0, __tt1, __tt2, __tt3, __tt4, __tt5, __tt6, __tt7; + __t0 = _mm256_unpacklo_ps(row0, row1); + __t1 = _mm256_unpackhi_ps(row0, row1); + __t2 = _mm256_unpacklo_ps(row2, row3); + __t3 = _mm256_unpackhi_ps(row2, row3); + __t4 = _mm256_unpacklo_ps(row4, row5); + __t5 = _mm256_unpackhi_ps(row4, row5); + __t6 = _mm256_unpacklo_ps(row6, row7); + __t7 = _mm256_unpackhi_ps(row6, row7); + __tt0 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(1,0,1,0)); + __tt1 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(3,2,3,2)); + __tt2 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(1,0,1,0)); + __tt3 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(3,2,3,2)); + __tt4 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(1,0,1,0)); + __tt5 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(3,2,3,2)); + __tt6 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(1,0,1,0)); + __tt7 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(3,2,3,2)); + row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20); + row1 = _mm256_permute2f128_ps(__tt1, __tt5, 0x20); + row2 = _mm256_permute2f128_ps(__tt2, __tt6, 0x20); + row3 = _mm256_permute2f128_ps(__tt3, __tt7, 0x20); + row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31); + row5 = _mm256_permute2f128_ps(__tt1, __tt5, 0x31); + row6 = _mm256_permute2f128_ps(__tt2, __tt6, 0x31); + row7 = _mm256_permute2f128_ps(__tt3, __tt7, 0x31); +} + +/*Input transform*/ +void _fx_winograd_BtXB_8x8_f32(const float* inptr, int inpstep, float* outptr, int Cg) +{ + __m256 x00 = _mm256_loadu_ps(inptr); + __m256 x10 = _mm256_loadu_ps(inptr + inpstep); + __m256 x20 = _mm256_loadu_ps(inptr + inpstep*2); + __m256 x30 = _mm256_loadu_ps(inptr + inpstep*3); + __m256 x40 = _mm256_loadu_ps(inptr + inpstep*4); + __m256 x50 = _mm256_loadu_ps(inptr + inpstep*5); + __m256 x60 = _mm256_loadu_ps(inptr + inpstep*6); + __m256 x70 = _mm256_loadu_ps(inptr + inpstep*7); + + __m256 z00, z10, z20, z30, z40, z50, z60, z70; + + { + /* Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X */ + /* Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X */ + __m256 q5_25 = _mm256_set1_ps(5.25f), t00, t10; + t00 = _mm256_sub_ps(x40, x20); + t10 = _mm256_sub_ps(x30, x50); + + __m256 y00 = _mm256_fmadd_ps(t00, q5_25, _mm256_sub_ps(x00, x60)); + __m256 y70 = _mm256_fmadd_ps(t10, q5_25, _mm256_sub_ps(x70, x10)); + + /* Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X */ + /* Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X */ + __m256 qm4_25 = _mm256_set1_ps(-4.25f); + t00 = _mm256_fmadd_ps(x30, qm4_25, _mm256_add_ps(x10, x50)); + t10 = _mm256_fmadd_ps(x40, qm4_25, _mm256_add_ps(x20, x60)); + + __m256 y10 = _mm256_add_ps(t00, t10); + __m256 y20 = _mm256_sub_ps(t10, t00); + + /* Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X */ + /* Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X */ + __m256 q0_5 = _mm256_set1_ps(0.5f), q0_25 = _mm256_set1_ps(0.25f); + __m256 qm2_5 = _mm256_set1_ps(-2.5f), qm1_25 = _mm256_set1_ps(-1.25f); + t00 = _mm256_fmadd_ps(x10, q0_5, _mm256_add_ps(x50, x50)); + t10 = _mm256_fmadd_ps(x20, q0_25, x60); + t00 = _mm256_fmadd_ps(x30, qm2_5, t00); + t10 = _mm256_fmadd_ps(x40, qm1_25, t10); + + __m256 y30 = _mm256_add_ps(t00, t10); + __m256 y40 = _mm256_sub_ps(t10, t00); + + /* Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X */ + /* Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X */ + __m256 q4 = _mm256_set1_ps(4.f), qm5 = _mm256_set1_ps(-5.f); + t00 = _mm256_fmadd_ps(x50, q0_5, _mm256_add_ps(x10, x10)); + t10 = _mm256_fmadd_ps(x20, q4 , x60); + t00 = _mm256_fmadd_ps(x30, qm2_5, t00); + t10 = _mm256_fmadd_ps(x40, qm5 , t10); + + __m256 y50 = _mm256_add_ps(t00, t10); + __m256 y60 = _mm256_sub_ps(t10, t00); + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + transpose8_ps(y00, y10, y20, y30, y40, y50, y60, y70); + + /* Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y */ + /* Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y */ + t00 = _mm256_sub_ps(y40, y20); + t10 = _mm256_sub_ps(y30, y50); + z00 = _mm256_fmadd_ps(t00, q5_25, _mm256_sub_ps(y00, y60)); + z70 = _mm256_fmadd_ps(t10, q5_25, _mm256_sub_ps(y70, y10)); + + /* Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y */ + /* Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y */ + t00 = _mm256_fmadd_ps(y30, qm4_25, _mm256_add_ps(y10, y50)); + t10 = _mm256_fmadd_ps(y40, qm4_25, _mm256_add_ps(y20, y60)); + z10 = _mm256_add_ps(t00, t10); + z20 = _mm256_sub_ps(t10, t00); + + /* Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y */ + /* Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y */ + t00 = _mm256_fmadd_ps(y10, q0_5, _mm256_add_ps(y50, y50)); + t10 = _mm256_fmadd_ps(y20, q0_25, y60); + t00 = _mm256_fmadd_ps(y30, qm2_5, t00); + t10 = _mm256_fmadd_ps(y40, qm1_25, t10); + + z30 = _mm256_add_ps(t00, t10); + z40 = _mm256_sub_ps(t10, t00); + + /* Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y */ + /* Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y */ + t00 = _mm256_fmadd_ps(y50, q0_5, _mm256_add_ps(y10, y10)); + t10 = _mm256_fmadd_ps(y20, q4, y60); + t00 = _mm256_fmadd_ps(y30, qm2_5, t00); + t10 = _mm256_fmadd_ps(y40, qm5, t10); + + z50 = _mm256_add_ps(t00, t10); + z60 = _mm256_sub_ps(t10, t00); + } + + const int outstep = _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32*Cg; + + _mm256_storeu_ps(outptr, z00); + _mm256_storeu_ps(outptr + outstep, z10); + _mm256_storeu_ps(outptr + outstep*2, z20); + _mm256_storeu_ps(outptr + outstep*3, z30); + _mm256_storeu_ps(outptr + outstep*4, z40); + _mm256_storeu_ps(outptr + outstep*5, z50); + _mm256_storeu_ps(outptr + outstep*6, z60); + _mm256_storeu_ps(outptr + outstep*7, z70); + _mm256_zeroupper(); +} + +#define STORE6_ELE_FROM_16(ptr, z00, lowM, highM) \ + lowM = _mm256_castps256_ps128(z00); \ + highM = _mm256_extractf128_ps(z00, 1); \ + _mm_storeu_ps(ptr, lowM); \ + _mm_storel_epi64((__m128i*)(ptr + 4), _mm_castps_si128(highM)) + +/* Inverse Winograd 8x8 transform: + out = (A'*inp*A)', where + inp is input 8x8 FP32 matrix, + A' is + [1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.f, + 0.f, 1.f, -1.f, 2.f, -2.f, 0.5f, -0.5f, 0.f, + 0.f, 1.f, 1.f, 4.f, 4.f, 0.25f, 0.25f, 0.f, + 0.f, 1.f, -1.f, 8.f, -8.f, 0.125f, -0.125f, 0.f, + 0.f, 1.f, 1.f, 16.f, 16.f, 1.f/16, 1.f/16, 0.f, + 0.f, 1.f, -1.f, 32.f, -32.f, 1.f/32, -1.f/32, 1.f] +*/ +void _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, + float* bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct) +{ + + __m256 x00 = _mm256_load_ps(inptr); + __m256 x10 = _mm256_load_ps(inptr + inpstep); + __m256 x20 = _mm256_load_ps(inptr + inpstep*2); + __m256 x30 = _mm256_load_ps(inptr + inpstep*3); + __m256 x40 = _mm256_load_ps(inptr + inpstep*4); + __m256 x50 = _mm256_load_ps(inptr + inpstep*5); + __m256 x60 = _mm256_load_ps(inptr + inpstep*6); + __m256 x70 = _mm256_load_ps(inptr + inpstep*7); + __m256 z00, z10, z20, z30, z40, z50; + + { + __m256 s12_0, s34_0, s56_0; + s12_0 = _mm256_add_ps(x10, x20); + s34_0 = _mm256_add_ps(x30, x40); + s56_0 = _mm256_add_ps(x50, x60); + + __m256 y00 = _mm256_add_ps(x00, _mm256_add_ps(s12_0, _mm256_add_ps(s34_0, s56_0))); + __m256 y20 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.25f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(4.0f), s12_0)); + __m256 y40 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(1.f/16), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(16.0f), s12_0)); + + s12_0 = _mm256_sub_ps(x10, x20); + s34_0 = _mm256_sub_ps(x30, x40); + s56_0 = _mm256_sub_ps(x50, x60); + __m256 y50 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(1.f/32), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(32.f), _mm256_add_ps(x70, s12_0))); + __m256 y10 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.5f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(2.f), s12_0)); + __m256 y30 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.125f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(8.f), s12_0)); + __m256 y60 = _mm256_set1_ps(0.f), y70 = y60; + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + + transpose8_ps(y00, y10, y20, y30, y40, y50, y60, y70); + + s12_0 = _mm256_add_ps(y10, y20); + s34_0 = _mm256_add_ps(y30, y40); + s56_0 = _mm256_add_ps(y50, y60); + + z00 = _mm256_add_ps(y00, _mm256_add_ps(s12_0, _mm256_add_ps(s34_0, s56_0))); + z20 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.25f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(4.0f), s12_0)); + z40 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(1.f/16), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(16.0f), s12_0)); + + s12_0 = _mm256_sub_ps(y10, y20); + s34_0 = _mm256_sub_ps(y30, y40); + s56_0 = _mm256_sub_ps(y50, y60); + + z50 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(1.f/32), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(32.0f), _mm256_add_ps(y70, s12_0))); + z10 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.5f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(2.0f), s12_0)); + z30 = _mm256_fmadd_ps(s56_0, _mm256_set1_ps(0.125f), _mm256_fmadd_ps(s34_0, _mm256_set1_ps(8.0f), s12_0)); + + __m256 vbias = _mm256_set1_ps(bias); + z00 = _mm256_add_ps(vbias, z00); + z10 = _mm256_add_ps(vbias, z10); + z20 = _mm256_add_ps(vbias, z20); + z30 = _mm256_add_ps(vbias, z30); + z40 = _mm256_add_ps(vbias, z40); + z50 = _mm256_add_ps(vbias, z50); + } + + // TODO make sure the lenght of bpptr is 8. + if (bpptr) + { + z00 = _mm256_add_ps(z00, _mm256_loadu_ps(bpptr)); + z10 = _mm256_add_ps(z10, _mm256_loadu_ps(bpptr + bpstep)); + z20 = _mm256_add_ps(z20, _mm256_loadu_ps(bpptr + bpstep*2)); + z30 = _mm256_add_ps(z30, _mm256_loadu_ps(bpptr + bpstep*3)); + z40 = _mm256_add_ps(z40, _mm256_loadu_ps(bpptr + bpstep*4)); + z50 = _mm256_add_ps(z50, _mm256_loadu_ps(bpptr + bpstep*5)); + } + + if (ifMinMaxAct) + { + __m256 vmax = _mm256_set1_ps(maxval); + __m256 vmin = _mm256_set1_ps(minval); + + z00 = _mm256_min_ps(_mm256_max_ps(z00, vmin), vmax); + z10 = _mm256_min_ps(_mm256_max_ps(z10, vmin), vmax); + z20 = _mm256_min_ps(_mm256_max_ps(z20, vmin), vmax); + z30 = _mm256_min_ps(_mm256_max_ps(z30, vmin), vmax); + z40 = _mm256_min_ps(_mm256_max_ps(z40, vmin), vmax); + z50 = _mm256_min_ps(_mm256_max_ps(z50, vmin), vmax); + } + + __m128 lowM, highM; + STORE6_ELE_FROM_16(outptr, z00, lowM, highM); + STORE6_ELE_FROM_16(outptr + outstep, z10, lowM, highM); + STORE6_ELE_FROM_16(outptr + outstep * 2, z20, lowM, highM); + STORE6_ELE_FROM_16(outptr + outstep * 3, z30, lowM, highM); + STORE6_ELE_FROM_16(outptr + outstep * 4, z40, lowM, highM); + STORE6_ELE_FROM_16(outptr + outstep * 5, z50, lowM, highM); + _mm256_zeroupper(); +} + #endif } // namespace opt_AVX2 } // namespace cv \ No newline at end of file diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index 8c829eaf81..6e6b6c0ead 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -14,7 +14,7 @@ #include "fast_convolution.simd.hpp" namespace cv { namespace dnn { - +enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. Ptr initFastConv2d( int ngroups, int K, int C, int Hk, int Wk, @@ -44,20 +44,17 @@ Ptr initFastConv2d( conv->pad_bottom = pads_end[0]; conv->pad_left = pads_begin[1]; conv->pad_right = pads_end[1]; + conv->conv_type = + (ngroups > 1 && ngroups == K && ngroups == C) ? _FX_CONV_TYPE_DEPTHWISE : + useWinograd && ((conv->useSIMD128 || conv->useAVX2 || conv->useNEON) && Hk == 3 && Wk == 3 && + dilation_y == 1 && dilation_x == 1 && stride_y == 1 && stride_x == 1) ? _FX_CONV_TYPE_WINOGRAD3X3 : + _FX_CONV_TYPE_GENERIC; Mat weightsMat = _weightsMat.getMat(); auto wShape = shape(weightsMat); const size_t wstep = weightsMat.step1(); -#if CV_NEON // For now, winograd is ARM platform only. - if (useWinograd && ngroups == 1 && Hk ==3 && Wk == 3 && stride_x == 1 && stride_y == 1 && - dilation_x == 1 && dilation_y ==1 && K >= 16 && C >= 16) - conv->useWinograd63 = true; -#else - conv->useWinograd63 = false; -#endif - float *srcWeights = (float *)weightsMat.data; - if (ngroups > 1 && ngroups == K && ngroups == C) + if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE) { // for depth-wise convolutions on NCHW data we just preserve the weights in KCHW layout, // but add some padding to make the weights array layout more SIMD-friendly @@ -66,17 +63,97 @@ Ptr initFastConv2d( // this code aims to let memory fit with vector size. int padded_ksize = ((ksize + FAST_VEC_NLANES-1) / FAST_VEC_NLANES) * FAST_VEC_NLANES; int nweights = C*padded_ksize; - conv->weightsBuf.reserve(nweights); - float* weightsBufPtr = conv->weightsBuf.data(); - memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0])); - for(int c = 0; c < C; c++) + conv->weightsBuf.reserve(nweights + VEC_ALIGN); + conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); + memset(conv->weightsBufPtr, 0, nweights*sizeof(conv->weightsBufPtr[0])); + auto weightsBufPtr = conv->weightsBufPtr; + parallel_for_(Range(0, C), [&](const Range& r0){ + for(int c = r0.start; c < r0.end; c++) { for (int k = 0; k < ksize; k++) weightsBufPtr[c*padded_ksize + k] = srcWeights[c*wstep + k]; - } + }}); } else { + if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // winograd + { + static const float ktm[8][3] = { + {1.0f, 0.0f, 0.0f}, + {-2.0f / 9, -2.0f / 9, -2.0f / 9}, + {-2.0f / 9, 2.0f / 9, -2.0f / 9}, + {1.0f / 90, 1.0f / 45, 2.0f / 45}, + {1.0f / 90, -1.0f / 45, 2.0f / 45}, + {32.f/45, 16.f/45, 8.f/45}, + {32.f/45, -16.f/45, 8.f/45}, + {0.0f, 0.0f, 1.0f} + }; + + // the weights are packed as 6-dim tensor: + // ngroups * ceil((K/ngroups)/KBLOCK) * (W*W/ATOM_SIZE) * (C/ngroups) * KBLOCK * ATOM_SIZE, + // where W is the size of Winograd-transformed kernel (8x8), + // ATOM_SIZE is number of lanes in SIMD register (4 for NEON and FP32), + // KBLOCK is some platform-dependent constant dependent on the number of SIMD registers. + int ksize = _FX_WINO_KSIZE * _FX_WINO_KSIZE; + int Cg = C/ngroups; + int Kg = K/ngroups; + int Kg_nblocks = (Kg + _FX_WINO_KBLOCK - 1)/_FX_WINO_KBLOCK; + size_t nweights = ngroups*Kg_nblocks*Cg*_FX_WINO_KBLOCK*_FX_WINO_AREA; + conv->weightsWinoBuf.reserve(nweights + VEC_ALIGN); + conv->weightsWinoBufPtr = alignPtr(conv->weightsWinoBuf.data(), VEC_ALIGN); + float* wptrWino = conv->weightsWinoBufPtr; + memset(wptrWino, 0, nweights * sizeof(wptrWino[0])); + + parallel_for_(Range(0, K), [&](const Range& r0){ + float kernelTm[_FX_WINO_AREA]; + for (int k = r0.start; k < r0.end; k++) + { + int g = k / Kg; + int k_ = k - g*Kg; + int ki = k_ / _FX_WINO_KBLOCK; + int dk = k_ - ki*_FX_WINO_KBLOCK; + + for (int c = 0; c < Cg; c++) + { + // wstep = Hk*Wk*Cg + const float *kernel0 = srcWeights + k * wstep + c * ksize; + + // transform kernel, transposed + const float *k0 = kernel0; + const float *k1 = kernel0 + 3; + const float *k2 = kernel0 + 6; + + // h + float tmp[8][3]; + for (int i = 0; i < 8; i++) + { + tmp[i][0] = k0[0] * ktm[i][0] + k0[1] * ktm[i][1] + k0[2] * ktm[i][2]; + tmp[i][1] = k1[0] * ktm[i][0] + k1[1] * ktm[i][1] + k1[2] * ktm[i][2]; + tmp[i][2] = k2[0] * ktm[i][0] + k2[1] * ktm[i][1] + k2[2] * ktm[i][2]; + } + + // v + for (int j = 0; j < 8; j++) + { + float *tmpp = &tmp[j][0]; + + for (int i = 0; i < 8; i++) + kernelTm[j * 8 + i] = tmpp[0] * ktm[i][0] + tmpp[1] * ktm[i][1] + tmpp[2] * ktm[i][2]; + } + + // repack the data. + float* wptr = wptrWino + (g*Kg_nblocks + ki) * Cg *_FX_WINO_KBLOCK*_FX_WINO_AREA + + (c*_FX_WINO_KBLOCK + dk)*_FX_WINO_ATOM_F32; + for (int i = 0; i < _FX_WINO_NATOMS_F32; i++, + wptr += Cg * _FX_WINO_KBLOCK * _FX_WINO_ATOM_F32) + { + CV_Assert(conv->weightsWinoBufPtr <= wptr && wptr + _FX_WINO_ATOM_F32 <= conv->weightsWinoBufPtr + nweights); + memcpy(wptr, kernelTm + i * _FX_WINO_ATOM_F32, _FX_WINO_ATOM_F32*sizeof (wptr[0])); + } + } + }}); + } + // The weights are packed as // ngroups x (ceil((K/ngroups)/CONV_MR)*CONV_MR) x (Cg*Hk*Wk) x CONV_MR tensor int Kg = K/ngroups, Cg = max(C/ngroups, 1); @@ -84,8 +161,9 @@ Ptr initFastConv2d( int Kg_aligned = numStripsMR * CONV_MR; int HkWkCg = Hk*Wk*Cg; size_t nweights = ngroups*Kg_aligned*HkWkCg; - conv->weightsBuf.reserve(nweights); - float* weightsBufPtr = conv->weightsBuf.data(); + conv->weightsBuf.reserve(nweights + VEC_ALIGN); + conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); + float* weightsBufPtr = conv->weightsBufPtr; memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0])); // Pack the weight. @@ -114,18 +192,12 @@ Ptr initFastConv2d( } } }}); - - // Prepare Weight for Winograd F(6x6, 3x3) - if (conv->useWinograd63) - { - initWinograd63(conv, weightsMat, K, C); - } } // store bias; append some zero's to make sure that // we can always read MR elements starting from any valid index { - int k = 0, nbias = K + CONV_MR - 1; + int k = 0, nbias = K + 32; conv->biasBuf.reserve(nbias); float* biasBufPtr = conv->biasBuf.data(); for(; k < K; k++) @@ -185,19 +257,16 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr else activ = nullptr; - if (conv->ngroups > 1 && conv->ngroups == conv->K && conv->ngroups == conv->C) + if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE) { CV_Assert(fusedAddMat.empty()); // Depthwise-Convolution layer should not be followed by Add layer. return runDepthwise(input, output, conv, minval, maxval, activ, ifMinMaxAct); } - -#if CV_NEON - if (conv->useWinograd63 && inputShape[2] > 12 && inputShape[3] > 12) + else if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3 && inputShape[2] >= 12 && inputShape[3] >= 12) // winograd { - if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) - return; + CV_Assert(conv->weightsWinoBufPtr); + return runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct); } -#endif int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W] int K = conv->K, Hk = conv->Hk, Wk = conv->Wk; @@ -217,7 +286,6 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr bool fast_1x1 = stride_x == 1 && stride_y == 1 && ksize == 1; int HkWkCg = Hk*Wk*Cg; - enum { VEC_ALIGN = 8, DFT_TYPE = CV_32F }; // Memory alignment. int MAX_STRIPES = 2; // (56 + CONV_NR - 1)/CONV_NR; // Friendly to L1 cache @@ -447,7 +515,7 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr } yx0 = yx0_saved; - float* weights = conv->weightsBuf.data() + g * Kg_aligned * HkWkCg; + float* weights = conv->weightsBufPtr + g * Kg_aligned * HkWkCg; const float* biasptr = conv->biasBuf.data() + Kg * g; int ldc = nstripes * CONV_NR; diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index 671cb707d1..62c4170a3a 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -21,7 +21,7 @@ enum { FAST_VEC_NLANES=4 }; #define CONV_MR 4 #define CONV_NR 24 -#if CV_AVX2 +#if CV_TRY_AVX2 enum { FAST_VEC_NLANES=8 }; // AVX2 #else enum { FAST_VEC_NLANES=4 }; // SIMD 128 @@ -30,6 +30,31 @@ enum { FAST_VEC_NLANES=4 }; // SIMD 128 #endif #endif +enum { + _FX_WINO_STEP=6, + _FX_WINO_KSIZE=3, + _FX_WINO_SIZE=_FX_WINO_STEP+_FX_WINO_KSIZE-1, + _FX_WINO_AREA=_FX_WINO_SIZE*_FX_WINO_SIZE, + +#if CV_TRY_AVX2 || (CV_NEON && CV_NEON_AARCH64) + _FX_WINO_KBLOCK = 4, + _FX_WINO_IBLOCK = 6, +#else + _FX_WINO_KBLOCK = 4, + _FX_WINO_IBLOCK = 3, +#endif + +#if CV_TRY_AVX2 + _FX_WINO_ATOM_F32 = 8, +#else + _FX_WINO_ATOM_F32 = 4, +#endif + + _FX_WINO_NATOMS_F32 = _FX_WINO_AREA / _FX_WINO_ATOM_F32, // for AVX2, it is 8, otherwise, it's 16. +}; + +enum { _FX_CONV_TYPE_GENERIC=0, _FX_CONV_TYPE_DEPTHWISE=1, _FX_CONV_TYPE_WINOGRAD3X3=2 }; + namespace cv { namespace dnn { @@ -41,10 +66,17 @@ struct FastConv2d int dilation_y, dilation_x; int pad_top, pad_bottom, pad_left, pad_right; - std::vector weightsBuf; // For generic Conv 2D - std::vector weightsWino63Buf; // For Winograd F(6x6, 3x3). + std::vector weightsBuf; // For generic Conv 2D + float* weightsBufPtr; + std::vector weightsWinoBuf; // For Winograd F(6x6, 3x3). + float* weightsWinoBufPtr; std::vector biasBuf; - bool useWinograd63 = false; + int conv_type; +#if CV_SIMD128 + bool useSIMD128 = true; +#else + bool useSIMD128 = false; +#endif bool useAVX2 = checkHardwareSupport(CPU_AVX2); bool useNEON = checkHardwareSupport(CPU_NEON); }; @@ -67,10 +99,7 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); -// winograd init -void initWinograd63(Ptr& conv, InputArray weightsMat, int K, int C); - -int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, +void runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); } // namespace dnn @@ -84,6 +113,12 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left, int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3); + +void _fx_winograd_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock); +void _fx_winograd_BtXB_8x8_f32(const float* inptr, int inpstep, float* outptr, int Cg); +void _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct); + #endif } // namespace opt_AVX2 diff --git a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp index 7475397901..bc44f73a22 100644 --- a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp +++ b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp @@ -2,536 +2,925 @@ // 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. +// This file is modified from the ficus (https://github.com/vpisarev/ficus/blob/master/lib/NN/OpConv_Winograd.fx). +// Here is the original license: /* -Winograd-based convolution F(6x6, 3x3). -The code has been borrowed from ncnn inference engine (https://github.com/Tencent/ncnn) -and adapted for OpenCV by Zihao Mu. - -Below is the original copyright + This file is a part of ficus language project. + See ficus/LICENSE for the licensing terms */ -// Tencent is pleased to support the open source community by making ncnn available. -// -// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - #include "../../precomp.hpp" #include "fast_convolution.hpp" namespace cv { namespace dnn { -enum -{ - WINO_STEP=6, - WINO_KSIZE=3, - WINO_SIZE= WINO_STEP + WINO_KSIZE - 1, - WINO_AREA= WINO_SIZE * WINO_SIZE -}; +enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. + +static void +_fx_winograd_accum_f32(const float* inwptr, const float* wptr, + float* outbuf, int Cg, int iblock) + { +#if CV_NEON && CV_NEON_AARCH64 + CV_Assert(_FX_WINO_IBLOCK == 6 && _FX_WINO_KBLOCK == 4); + if (iblock > 3) + { + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, + outbuf += _FX_WINO_ATOM_F32) + { + float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00, s03 = s00, s04 = s00, s05 = s00; + float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00, s13 = s00, s14 = s00, s15 = s00; + float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00, s23 = s00, s24 = s00, s25 = s00; + float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00, s33 = s00, s34 = s00, s35 = s00; + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) { + float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); + float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); + float32x4_t x0, x1; + x0 = vld1q_f32(inwptr); + x1 = vld1q_f32(inwptr + 4); + s00 = vfmaq_f32(s00, w0, x0); + s01 = vfmaq_f32(s01, w0, x1); + s10 = vfmaq_f32(s10, w1, x0); + s11 = vfmaq_f32(s11, w1, x1); + s20 = vfmaq_f32(s20, w2, x0); + s21 = vfmaq_f32(s21, w2, x1); + s30 = vfmaq_f32(s30, w3, x0); + s31 = vfmaq_f32(s31, w3, x1); + x0 = vld1q_f32(inwptr + 8); + x1 = vld1q_f32(inwptr + 12); + s02 = vfmaq_f32(s02, w0, x0); + s03 = vfmaq_f32(s03, w0, x1); + s12 = vfmaq_f32(s12, w1, x0); + s13 = vfmaq_f32(s13, w1, x1); + s22 = vfmaq_f32(s22, w2, x0); + s23 = vfmaq_f32(s23, w2, x1); + s32 = vfmaq_f32(s32, w3, x0); + s33 = vfmaq_f32(s33, w3, x1); + x0 = vld1q_f32(inwptr + 16); + x1 = vld1q_f32(inwptr + 20); + s04 = vfmaq_f32(s04, w0, x0); + s05 = vfmaq_f32(s05, w0, x1); + s14 = vfmaq_f32(s14, w1, x0); + s15 = vfmaq_f32(s15, w1, x1); + s24 = vfmaq_f32(s24, w2, x0); + s25 = vfmaq_f32(s25, w2, x1); + s34 = vfmaq_f32(s34, w3, x0); + s35 = vfmaq_f32(s35, w3, x1); + } + + vst1q_f32(outbuf, s00); + vst1q_f32(outbuf + 1*64, s01); + vst1q_f32(outbuf + 2*64, s02); + vst1q_f32(outbuf + 3*64, s03); + vst1q_f32(outbuf + 4*64, s04); + vst1q_f32(outbuf + 5*64, s05); + + vst1q_f32(outbuf + 6*64, s10); + vst1q_f32(outbuf + 7*64, s11); + vst1q_f32(outbuf + 8*64, s12); + vst1q_f32(outbuf + 9*64, s13); + vst1q_f32(outbuf + 10*64, s14); + vst1q_f32(outbuf + 11*64, s15); + + vst1q_f32(outbuf + 12*64, s20); + vst1q_f32(outbuf + 13*64, s21); + vst1q_f32(outbuf + 14*64, s22); + vst1q_f32(outbuf + 15*64, s23); + vst1q_f32(outbuf + 16*64, s24); + vst1q_f32(outbuf + 17*64, s25); + + vst1q_f32(outbuf + 18*64, s30); + vst1q_f32(outbuf + 19*64, s31); + vst1q_f32(outbuf + 20*64, s32); + vst1q_f32(outbuf + 21*64, s33); + vst1q_f32(outbuf + 22*64, s34); + vst1q_f32(outbuf + 23*64, s35); + } + } + else + { + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, + outbuf += _FX_WINO_ATOM_F32) + { + float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00; + float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00; + float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00; + float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00; + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) { + float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); + float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); + float32x4_t x0, x1, x2; + x0 = vld1q_f32(inwptr); + x1 = vld1q_f32(inwptr + 4); + x2 = vld1q_f32(inwptr + 8); + s00 = vfmaq_f32(s00, w0, x0); + s01 = vfmaq_f32(s01, w0, x1); + s02 = vfmaq_f32(s02, w0, x2); + s10 = vfmaq_f32(s10, w1, x0); + s11 = vfmaq_f32(s11, w1, x1); + s12 = vfmaq_f32(s12, w1, x2); + s20 = vfmaq_f32(s20, w2, x0); + s21 = vfmaq_f32(s21, w2, x1); + s22 = vfmaq_f32(s22, w2, x2); + s30 = vfmaq_f32(s30, w3, x0); + s31 = vfmaq_f32(s31, w3, x1); + s32 = vfmaq_f32(s32, w3, x2); + } + + vst1q_f32(outbuf, s00); + vst1q_f32(outbuf + 1*64, s01); + vst1q_f32(outbuf + 2*64, s02); + vst1q_f32(outbuf + 6*64, s10); + vst1q_f32(outbuf + 7*64, s11); + vst1q_f32(outbuf + 8*64, s12); + vst1q_f32(outbuf + 12*64, s20); + vst1q_f32(outbuf + 13*64, s21); + vst1q_f32(outbuf + 14*64, s22); + vst1q_f32(outbuf + 18*64, s30); + vst1q_f32(outbuf + 19*64, s31); + vst1q_f32(outbuf + 20*64, s32); + } + } +#elif CV_SIMD + CV_Assert(_FX_WINO_IBLOCK == 3 && _FX_WINO_KBLOCK == 4); + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, + outbuf += _FX_WINO_ATOM_F32) + { + v_float32x4 s00 = v_setzero_f32(), s01 = s00, s02 = s00; + v_float32x4 s10 = v_setzero_f32(), s11 = s00, s12 = s00; + v_float32x4 s20 = v_setzero_f32(), s21 = s00, s22 = s00; + v_float32x4 s30 = v_setzero_f32(), s31 = s00, s32 = s00; + + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) + { + v_float32x4 x0, x1, x2; + x0 = v_load(inwptr); + x1 = v_load(inwptr + 4); + x2 = v_load(inwptr + 8); + + v_float32x4 w0 = v_load(wptr); + s00 = v_fma(w0, x0, s00); + s01 = v_fma(w0, x1, s01); + s02 = v_fma(w0, x2, s02); + + w0 = v_load(wptr + 4); + s10 = v_fma(w0, x0, s10); + s11 = v_fma(w0, x1, s11); + s12 = v_fma(w0, x2, s12); + + w0 = v_load(wptr + 8); + s20 = v_fma(w0, x0, s20); + s21 = v_fma(w0, x1, s21); + s22 = v_fma(w0, x2, s22); + + w0 = v_load(wptr + 12); + s30 = v_fma(w0, x0, s30); + s31 = v_fma(w0, x1, s31); + s32 = v_fma(w0, x2, s32); + } + + v_store(outbuf, s00); + v_store(outbuf + 1*64, s01); + v_store(outbuf + 2*64, s02); + v_store(outbuf + 6*64, s10); + v_store(outbuf + 7*64, s11); + v_store(outbuf + 8*64, s12); + v_store(outbuf + 12*64, s20); + v_store(outbuf + 13*64, s21); + v_store(outbuf + 14*64, s22); + v_store(outbuf + 18*64, s30); + v_store(outbuf + 19*64, s31); + v_store(outbuf + 20*64, s32); + } +#else + for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; + atom_id++, outbuf += _FX_WINO_ATOM_F32) + { + float sumbuf[_FX_WINO_IBLOCK*_FX_WINO_KBLOCK*_FX_WINO_ATOM_F32]; + memset(sumbuf, 0, sizeof(sumbuf)); + for (int c = 0; c < Cg; c++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32, + wptr += _FX_WINO_KBLOCK*_FX_WINO_ATOM_F32) + { + for (int i = 0; i < _FX_WINO_KBLOCK; i++) + { + for (int j = 0; j < _FX_WINO_IBLOCK; j++) + { + int i_ = i*_FX_WINO_ATOM_F32; + int j_ = j*_FX_WINO_ATOM_F32; + int ij_ = i_*_FX_WINO_IBLOCK + j_; + float s0 = inwptr[j_ + 0]*wptr[i_ + 0]; + float s1 = inwptr[j_ + 1]*wptr[i_ + 1]; + float s2 = inwptr[j_ + 2]*wptr[i_ + 2]; + float s3 = inwptr[j_ + 3]*wptr[i_ + 3]; + sumbuf[ij_ + 0] += s0; + sumbuf[ij_ + 1] += s1; + sumbuf[ij_ + 2] += s2; + sumbuf[ij_ + 3] += s3; + } + } + } + for (int ij = 0; ij < _FX_WINO_KBLOCK*_FX_WINO_IBLOCK; ij++) + { + int ij_ = ij*_FX_WINO_ATOM_F32; + int ij_out = ij*_FX_WINO_AREA; + outbuf[ij_out + 0] = sumbuf[ij_ + 0]; + outbuf[ij_out + 1] = sumbuf[ij_ + 1]; + outbuf[ij_out + 2] = sumbuf[ij_ + 2]; + outbuf[ij_out + 3] = sumbuf[ij_ + 3]; + } + } +#endif +} #if CV_NEON - -#undef _FAST_CONV_T4x4 -#define _FAST_CONV_T4x4(a, b, c, d, tr0, tr1) \ +#define T4x4(a, b, c, d, tr0, tr1) \ tr0 = vtrnq_f32(a, b); \ tr1 = vtrnq_f32(c, d); \ a = vcombine_f32(vget_low_f32(tr0.val[0]), vget_low_f32(tr1.val[0])); \ b = vcombine_f32(vget_low_f32(tr0.val[1]), vget_low_f32(tr1.val[1])); \ c = vcombine_f32(vget_high_f32(tr0.val[0]), vget_high_f32(tr1.val[0])); \ d = vcombine_f32(vget_high_f32(tr0.val[1]), vget_high_f32(tr1.val[1])) - -// The input is the pack4 data, and the output is unpack4 data. -static void transpose12x4(float* src, float* dst, const int cn) -{ - float32x4_t r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, r10, r11; - float32x4x2_t tr0, tr1; - for (int i = 0; i < cn; i++, src += 48, dst += 48) - { - r00 = vld1q_f32(src); - r01 = vld1q_f32(src + 4); - r02 = vld1q_f32(src + 8); - r03 = vld1q_f32(src + 12); - r04 = vld1q_f32(src + 16); - r05 = vld1q_f32(src + 20); - r06 = vld1q_f32(src + 24); - r07 = vld1q_f32(src + 28); - r08 = vld1q_f32(src + 32); - r09 = vld1q_f32(src + 36); - r10 = vld1q_f32(src + 40); - r11 = vld1q_f32(src + 44); - - _FAST_CONV_T4x4(r00, r01, r02, r03, tr0, tr1); - _FAST_CONV_T4x4(r04, r05, r06, r07, tr0, tr1); - _FAST_CONV_T4x4(r08, r09, r10, r11, tr0, tr1); - - vst1q_f32(dst, r00), vst1q_f32(dst + 4, r04), vst1q_f32(dst + 8, r08); - vst1q_f32(dst + 12, r01), vst1q_f32(dst + 16, r05), vst1q_f32(dst + 20, r09); - vst1q_f32(dst + 24, r02), vst1q_f32(dst + 28, r06), vst1q_f32(dst + 32, r10); - vst1q_f32(dst + 36, r03), vst1q_f32(dst + 40, r07), vst1q_f32(dst + 44, r11); - } -} - -static void winograd_trans_input_F63(float* src, float* dst, int Channle_div4, const int tiles, const int big_step, const int line_step, const int* ofstab0) -{ - // const float itm[8][8] = { - // {1.0f, 0.0f, -5.25f, 0.00f, 5.25f, 0.00f, -1.0f, 0.0f}, - // - // {0.0f, 1.0f, 1.00f, -4.25f, -4.25f, 1.00f, 1.0f, 0.0f}, - // {0.0f, -1.0f, 1.00f, 4.25f, -4.25f, -1.00f, 1.0f, 0.0f}, - // - // {0.0f, 0.5f, 0.25f, -2.50f, -1.25f, 2.00f, 1.0f, 0.0f}, - // {0.0f, -0.5f, 0.25f, 2.50f, -1.25f, -2.00f, 1.0f, 0.0f}, - // - // {0.0f, 2.0f, 4.00f, -2.50f, -5.00f, 0.50f, 1.0f, 0.0f}, - // {0.0f, -2.0f, 4.00f, 2.50f, -5.00f, -0.50f, 1.0f, 0.0f}, - // - // {0.0f, -1.0f, 0.00f, 5.25f, 0.00f, -5.25f, 0.0f, 1.0f} - // }; - - // 0 = r00 - r06 + (r04 - r02) * 5.25 - // 7 = r07 - r01 + (r03 - r05) * 5.25 - - // 1 = (r02 + r06 - r04 * 4.25) + (r01 - r03 * 4.25 + r05) - // 2 = (r02 + r06 - r04 * 4.25) - (r01 - r03 * 4.25 + r05) - - // 3 = (r06 + r02 * 0.25 - r04 * 1.25) + (r01 * 0.5 - r03 * 2.5 + r05 * 2) - // 4 = (r06 + r02 * 0.25 - r04 * 1.25) - (r01 * 0.5 - r03 * 2.5 + r05 * 2) - - // reuse r04 * 1.25 - // reuse r03 * 2.5 - // 5 = (r06 + (r02 - r04 * 1.25) * 4) + (r01 * 2 - r03 * 2.5 + r05 * 0.5) - // 6 = (r06 + (r02 - r04 * 1.25) * 4) - (r01 * 2 - r03 * 2.5 + r05 * 0.5) - - float tmp[8][8][FAST_VEC_NLANES]; - AutoBuffer input_buf0_; - input_buf0_.allocate(64 * tiles * FAST_VEC_NLANES); - - float* input_buf0 = input_buf0_.data(); - memset(input_buf0, 0, 64 * tiles * FAST_VEC_NLANES * sizeof(float )); - - for (int ti = 0; ti < tiles; ti++) - { - float* input0 = src + ti * 64 * 4; - float* input = input0; - for (int m = 0; m < 8; m++) - { - float32x4_t _r00 = vld1q_f32(input); - float32x4_t _r01 = vld1q_f32(input + 4); - float32x4_t _r02 = vld1q_f32(input + 8); - float32x4_t _r03 = vld1q_f32(input + 12); - float32x4_t _r04 = vld1q_f32(input + 16); - float32x4_t _r05 = vld1q_f32(input + 20); - float32x4_t _r06 = vld1q_f32(input + 24); - float32x4_t _r07 = vld1q_f32(input + 28); - - float32x4_t _tmp0m = vmlaq_n_f32(vsubq_f32(_r00, _r06), vsubq_f32(_r04, _r02), 5.25f); - float32x4_t _tmp7m = vmlaq_n_f32(vsubq_f32(_r07, _r01), vsubq_f32(_r03, _r05), 5.25f); - vst1q_f32(tmp[0][m], _tmp0m); - vst1q_f32(tmp[7][m], _tmp7m); - - float32x4_t _tmp12a = vmlsq_n_f32(vaddq_f32(_r02, _r06), _r04, 4.25f); - float32x4_t _tmp12b = vmlsq_n_f32(vaddq_f32(_r01, _r05), _r03, 4.25f); - - float32x4_t _tmp1m = vaddq_f32(_tmp12a, _tmp12b); - float32x4_t _tmp2m = vsubq_f32(_tmp12a, _tmp12b); - vst1q_f32(tmp[1][m], _tmp1m); - vst1q_f32(tmp[2][m], _tmp2m); - - float32x4_t _tmp34a = vmlsq_n_f32(vmlaq_n_f32(_r06, _r02, 0.25f), _r04, 1.25f); - float32x4_t _tmp34b = vmlaq_n_f32(vmlsq_n_f32(vmulq_n_f32(_r01, 0.5f), _r03, 2.5f), _r05, 2.f); - - float32x4_t _tmp3m = vaddq_f32(_tmp34a, _tmp34b); - float32x4_t _tmp4m = vsubq_f32(_tmp34a, _tmp34b); - vst1q_f32(tmp[3][m], _tmp3m); - vst1q_f32(tmp[4][m], _tmp4m); - - float32x4_t _tmp56a = vmlaq_n_f32(_r06, vmlsq_n_f32(_r02, _r04, 1.25f), 4.f); - float32x4_t _tmp56b = vmlaq_n_f32(vmlsq_n_f32(vmulq_n_f32(_r01, 2.f), _r03, 2.5f), _r05, 0.5f); - - float32x4_t _tmp5m = vaddq_f32(_tmp56a, _tmp56b); - float32x4_t _tmp6m = vsubq_f32(_tmp56a, _tmp56b); - vst1q_f32(tmp[5][m], _tmp5m); - vst1q_f32(tmp[6][m], _tmp6m); - - input += 8 * FAST_VEC_NLANES; - } - - float* input_buf00 = input_buf0 + ti * 4; - float* input_buf01 = input_buf00 + tiles * 4; - float* input_buf02 = input_buf00 + tiles * 8; - float* input_buf03 = input_buf00 + tiles * 12; - float* input_buf04 = input_buf00 + tiles * 16; - float* input_buf05 = input_buf00 + tiles * 20; - float* input_buf06 = input_buf00 + tiles * 24; - float* input_buf07 = input_buf00 + tiles * 28; - - for (int m = 0; m < 8; m++) - { - float32x4_t _tmp00 = vld1q_f32(tmp[m][0]); - float32x4_t _tmp01 = vld1q_f32(tmp[m][1]); - float32x4_t _tmp02 = vld1q_f32(tmp[m][2]); - float32x4_t _tmp03 = vld1q_f32(tmp[m][3]); - float32x4_t _tmp04 = vld1q_f32(tmp[m][4]); - float32x4_t _tmp05 = vld1q_f32(tmp[m][5]); - float32x4_t _tmp06 = vld1q_f32(tmp[m][6]); - float32x4_t _tmp07 = vld1q_f32(tmp[m][7]); - - float32x4_t _r0tm0 = vmlaq_n_f32(vsubq_f32(_tmp00, _tmp06), vsubq_f32(_tmp04, _tmp02), 5.25f); - float32x4_t _r0tm7 = vmlaq_n_f32(vsubq_f32(_tmp07, _tmp01), vsubq_f32(_tmp03, _tmp05), 5.25f); - - float32x4_t _tmp12a = vmlsq_n_f32(vaddq_f32(_tmp02, _tmp06), _tmp04, 4.25f); - float32x4_t _tmp12b = vmlsq_n_f32(vaddq_f32(_tmp01, _tmp05), _tmp03, 4.25f); - - float32x4_t _r0tm1 = vaddq_f32(_tmp12a, _tmp12b); - float32x4_t _r0tm2 = vsubq_f32(_tmp12a, _tmp12b); - - float32x4_t _tmp34a = vmlsq_n_f32(vmlaq_n_f32(_tmp06, _tmp02, 0.25f), _tmp04, 1.25f); - float32x4_t _tmp34b = vmlaq_n_f32(vmlsq_n_f32(vmulq_n_f32(_tmp01, 0.5f), _tmp03, 2.5f), _tmp05, 2.f); - - float32x4_t _r0tm3 = vaddq_f32(_tmp34a, _tmp34b); - float32x4_t _r0tm4 = vsubq_f32(_tmp34a, _tmp34b); - - float32x4_t _tmp56a = vmlaq_n_f32(_tmp06, vmlsq_n_f32(_tmp02, _tmp04, 1.25f), 4.f); - float32x4_t _tmp56b = vmlaq_n_f32(vmlsq_n_f32(vmulq_n_f32(_tmp01, 2.f), _tmp03, 2.5f), _tmp05, 0.5f); - - float32x4_t _r0tm5 = vaddq_f32(_tmp56a, _tmp56b); - float32x4_t _r0tm6 = vsubq_f32(_tmp56a, _tmp56b); - - vst1q_f32(input_buf00, _r0tm0); - vst1q_f32(input_buf01, _r0tm1); - vst1q_f32(input_buf02, _r0tm2); - vst1q_f32(input_buf03, _r0tm3); - vst1q_f32(input_buf04, _r0tm4); - vst1q_f32(input_buf05, _r0tm5); - vst1q_f32(input_buf06, _r0tm6); - vst1q_f32(input_buf07, _r0tm7); - - input_buf00 += tiles * 32; - input_buf01 += tiles * 32; - input_buf02 += tiles * 32; - input_buf03 += tiles * 32; - input_buf04 += tiles * 32; - input_buf05 += tiles * 32; - input_buf06 += tiles * 32; - input_buf07 += tiles * 32; - } - } - - // [line Number, input pack] - // if InpPack == 8; - for (int r = 0; r < 64; r++) - { - int ti = 0; - float* out0 = dst + r * big_step; - float* input0 = input_buf0 + 4 * tiles * r; - - // TODO! support tiles > 12 -#if CV_NEON_AARCH64 - for (; ti + 11 < tiles; ti += 12) - { - float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; - float* input1 = input0 + ti * 4; - memcpy(out1, input1, 12 * 4 * sizeof(float )); - } -#endif - for (; ti + 7 < tiles; ti += 8) - { - float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; - float* input1 = input0 + ti * 4; - memcpy(out1, input1, 8 * 4 * sizeof(float )); - } - - for (; ti + 3 < tiles; ti += 4) - { - float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; - float* input1 = input0 + ti * 4; - memcpy(out1, input1, 4 * 4 * sizeof(float )); - } - - for (; ti + 1 < tiles; ti += 2) - { - float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; - float* input1 = input0 + ti * 4; - memcpy(out1, input1, 2 * 4 * sizeof(float )); - } - - for (; ti < tiles; ti++) - { - float* out1 = out0 + line_step * ofstab0[ti * 2] + Channle_div4 * ofstab0[ti * 2 + 1] * 4; - float* input1 = input0 + ti * 4; - memcpy(out1, input1, 1 * 4 * sizeof(float )); - } - } -} - -static void winograd_trans_output_F63(float* src_, float* bias_, float* fAbuf0, float minval, float maxval, bool ifMinMaxAct) -{ - // const float otm[6][8] = { - // {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 32.0f, 32.0f, 0.0f}, - // {0.0f, 1.0f, -1.0f, 2.0f, -2.0f, 16.0f,-16.0f, 0.0f}, - // {0.0f, 1.0f, 1.0f, 4.0f, 4.0f, 8.0f, 8.0f, 0.0f}, - // {0.0f, 1.0f, -1.0f, 8.0f, -8.0f, 4.0f, -4.0f, 0.0f}, - // {0.0f, 1.0f, 1.0f, 16.0f, 16.0f, 2.0f, 2.0f, 0.0f}, - // {0.0f, 1.0f, -1.0f, 32.0f, -32.0f, 1.0f, -1.0f, 1.0f} - // }; - - // 0 = r0 + (r1 + r2) + (r3 + r4) + (r5 + r6) * 32 - // 1 = (r1 - r2) + (r3 - r4) * 2 + (r5 - r6) * 16 - // 2 = (r1 + r2) + (r3 + r4) * 4 + (r5 + r6) * 8 - // 3 = (r1 - r2) + (r3 - r4) * 8 + (r5 - r6) * 4 - // 4 = (r1 + r2) + (r3 + r4) * 16+ (r5 + r6) * 2 - // 5 = r7 + (r1 - r2) + (r3 - r4) * 32+ (r5 - r6) - - float32x4_t bias0 = bias_ ? vld1q_f32(bias_) : vdupq_n_f32(0.f); - float tmp[6][8][4]; - - for (int m = 0; m < 8; m++) - { - float* output0 = src_ + 8 * m * FAST_VEC_NLANES; - - float32x4_t _out0tm0 = vld1q_f32(output0); - float32x4_t _out0tm1 = vld1q_f32(output0 + FAST_VEC_NLANES * 1); - float32x4_t _out0tm2 = vld1q_f32(output0 + FAST_VEC_NLANES * 2); - float32x4_t _out0tm3 = vld1q_f32(output0 + FAST_VEC_NLANES * 3); - float32x4_t _out0tm4 = vld1q_f32(output0 + FAST_VEC_NLANES * 4); - float32x4_t _out0tm5 = vld1q_f32(output0 + FAST_VEC_NLANES * 5); - float32x4_t _out0tm6 = vld1q_f32(output0 + FAST_VEC_NLANES * 6); - float32x4_t _out0tm7 = vld1q_f32(output0 + FAST_VEC_NLANES * 7); - - float32x4_t _tmp024a = vaddq_f32(_out0tm1, _out0tm2); - float32x4_t _tmp135a = vsubq_f32(_out0tm1, _out0tm2); - - float32x4_t _tmp024b = vaddq_f32(_out0tm3, _out0tm4); - float32x4_t _tmp135b = vsubq_f32(_out0tm3, _out0tm4); - - float32x4_t _tmp024c = vaddq_f32(_out0tm5, _out0tm6); - float32x4_t _tmp135c = vsubq_f32(_out0tm5, _out0tm6); - - float32x4_t _tmp0m = vaddq_f32(vaddq_f32(_out0tm0, _tmp024a), vmlaq_n_f32(_tmp024b, _tmp024c, 32.f)); - float32x4_t _tmp2m = vmlaq_n_f32(vmlaq_n_f32(_tmp024a, _tmp024b, 4.f), _tmp024c, 8.f); - float32x4_t _tmp4m = vmlaq_n_f32(vmlaq_n_f32(_tmp024a, _tmp024b, 16.f), _tmp024c, 2.f); - vst1q_f32(tmp[0][m], _tmp0m); - vst1q_f32(tmp[2][m], _tmp2m); - vst1q_f32(tmp[4][m], _tmp4m); - - float32x4_t _tmp1m = vmlaq_n_f32(vmlaq_n_f32(_tmp135a, _tmp135b, 2.f), _tmp135c, 16.f); - float32x4_t _tmp3m = vmlaq_n_f32(vmlaq_n_f32(_tmp135a, _tmp135b, 8.f), _tmp135c, 4.f); - float32x4_t _tmp5m = vaddq_f32(vaddq_f32(_out0tm7, _tmp135a), vmlaq_n_f32(_tmp135c, _tmp135b, 32.f)); - vst1q_f32(tmp[1][m], _tmp1m); - vst1q_f32(tmp[3][m], _tmp3m); - vst1q_f32(tmp[5][m], _tmp5m); - } - - for (int m = 0; m < 6; m++) - { - float* output0 = src_ + 6 * m * FAST_VEC_NLANES; - float* fAbuf = fAbuf0 ? fAbuf0 + 6 * m * FAST_VEC_NLANES : 0; - - float32x4_t _tmp00 = vld1q_f32(tmp[m][0]); - float32x4_t _tmp01 = vld1q_f32(tmp[m][1]); - float32x4_t _tmp02 = vld1q_f32(tmp[m][2]); - float32x4_t _tmp03 = vld1q_f32(tmp[m][3]); - float32x4_t _tmp04 = vld1q_f32(tmp[m][4]); - float32x4_t _tmp05 = vld1q_f32(tmp[m][5]); - float32x4_t _tmp06 = vld1q_f32(tmp[m][6]); - float32x4_t _tmp07 = vld1q_f32(tmp[m][7]); - - float32x4_t _tmp024a = vaddq_f32(_tmp01, _tmp02); - float32x4_t _tmp135a = vsubq_f32(_tmp01, _tmp02); - - float32x4_t _tmp024b = vaddq_f32(_tmp03, _tmp04); - float32x4_t _tmp135b = vsubq_f32(_tmp03, _tmp04); - - float32x4_t _tmp024c = vaddq_f32(_tmp05, _tmp06); - float32x4_t _tmp135c = vsubq_f32(_tmp05, _tmp06); - - float32x4_t _out00 = vaddq_f32(bias0, vaddq_f32(vaddq_f32(_tmp00, _tmp024a), vmlaq_n_f32(_tmp024b, _tmp024c, 32.f))); - float32x4_t _out02 = vaddq_f32(bias0, vmlaq_n_f32(vmlaq_n_f32(_tmp024a, _tmp024b, 4.f), _tmp024c, 8.f)); - float32x4_t _out04 = vaddq_f32(bias0, vmlaq_n_f32(vmlaq_n_f32(_tmp024a, _tmp024b, 16.f), _tmp024c, 2.f)); - - float32x4_t _out01 = vaddq_f32(bias0, vmlaq_n_f32(vmlaq_n_f32(_tmp135a, _tmp135b, 2.f), _tmp135c, 16.f)); - float32x4_t _out03 = vaddq_f32(bias0, vmlaq_n_f32(vmlaq_n_f32(_tmp135a, _tmp135b, 8.f), _tmp135c, 4.f)); - float32x4_t _out05 = vaddq_f32(bias0, vaddq_f32(vaddq_f32(_tmp07, _tmp135a), vmlaq_n_f32(_tmp135c, _tmp135b, 32.f))); - - if (fAbuf) - { - _out00 = vaddq_f32(_out00, vld1q_f32(fAbuf)); - _out01 = vaddq_f32(_out01, vld1q_f32(fAbuf + 4)); - _out02 = vaddq_f32(_out02, vld1q_f32(fAbuf + 8)); - _out03 = vaddq_f32(_out03, vld1q_f32(fAbuf + 12)); - _out04 = vaddq_f32(_out04, vld1q_f32(fAbuf + 16)); - _out05 = vaddq_f32(_out05, vld1q_f32(fAbuf + 20)); - } - - if (ifMinMaxAct) - { - float32x4_t vmin = vdupq_n_f32(minval), vmax = vdupq_n_f32(maxval); - _out00 = vminq_f32(vmaxq_f32(_out00, vmin), vmax); - _out01 = vminq_f32(vmaxq_f32(_out01, vmin), vmax); - _out02 = vminq_f32(vmaxq_f32(_out02, vmin), vmax); - _out03 = vminq_f32(vmaxq_f32(_out03, vmin), vmax); - _out04 = vminq_f32(vmaxq_f32(_out04, vmin), vmax); - _out05 = vminq_f32(vmaxq_f32(_out05, vmin), vmax); - } - - vst1q_f32(output0, _out00); - vst1q_f32(output0 + FAST_VEC_NLANES, _out01); - vst1q_f32(output0 + 2 * FAST_VEC_NLANES, _out02); - vst1q_f32(output0 + 3 * FAST_VEC_NLANES, _out03); - vst1q_f32(output0 + 4 * FAST_VEC_NLANES, _out04); - vst1q_f32(output0 + 5 * FAST_VEC_NLANES, _out05); - } -} - -void initWinograd63(Ptr& conv, InputArray _weightsMat, int K, int C) -{ - static const float ktm[8][3] = { - {1.0f, 0.0f, 0.0f}, - {-2.0f / 9, -2.0f / 9, -2.0f / 9}, - {-2.0f / 9, 2.0f / 9, -2.0f / 9}, - {1.0f / 90, 1.0f / 45, 2.0f / 45}, - {1.0f / 90, -1.0f / 45, 2.0f / 45}, - {1.0f / 45, 1.0f / 90, 1.0f / 180}, - {1.0f / 45, -1.0f / 90, 1.0f / 180}, - {0.0f, 0.0f, 1.0f} - }; - - Mat weightsMat = _weightsMat.getMat(); - float* srcWeight = weightsMat.ptr(); - size_t wstep = weightsMat.step1(); - - int K_aligned = ((K + FAST_VEC_NLANES - 1)/FAST_VEC_NLANES) * FAST_VEC_NLANES; - int C_aligned = ((C + FAST_VEC_NLANES - 1)/FAST_VEC_NLANES) * FAST_VEC_NLANES; - const int winoSize = C * WINO_AREA; - const int kArea = WINO_KSIZE * WINO_KSIZE; - - // Allocate memory for winograd. - int nweights = K_aligned * C_aligned * WINO_AREA; - - conv->weightsWino63Buf.reserve(nweights); - float* weightsWino63Ptr = conv->weightsWino63Buf.data(); - memset(weightsWino63Ptr, 0, nweights*sizeof(weightsWino63Ptr[0])); - float* wptrWino = weightsWino63Ptr; - - AutoBuffer kernelTm0_; - kernelTm0_.allocate(WINO_AREA * K * C); - float *kernelTm = kernelTm0_.data(); - memset(kernelTm, 0, WINO_AREA * K * C*sizeof(kernelTm[0])); - - // Step1 Transform : size [K, C, 8, 8] - parallel_for_(Range(0, K), [&](const Range& r0) - { - for (int outc = r0.start; outc < r0.end; outc++) - { - for (int inc = 0; inc < C; inc++) - { - float *kernel_tm0 = kernelTm + outc * winoSize + inc * WINO_AREA; - const float *kernel0 = srcWeight + outc * wstep + inc * kArea; - - // transform kernel, transposed - const float *k0 = kernel0; - const float *k1 = kernel0 + 3; - const float *k2 = kernel0 + 6; - - // h - float tmp[8][3]; - for (int i = 0; i < 8; i++) - { - tmp[i][0] = k0[0] * ktm[i][0] + k0[1] * ktm[i][1] + k0[2] * ktm[i][2]; - tmp[i][1] = k1[0] * ktm[i][0] + k1[1] * ktm[i][1] + k1[2] * ktm[i][2]; - tmp[i][2] = k2[0] * ktm[i][0] + k2[1] * ktm[i][1] + k2[2] * ktm[i][2]; - } - - // v - for (int j = 0; j < 8; j++) - { - float *tmpp = &tmp[j][0]; - - for (int i = 0; i < 8; i++) - { - kernel_tm0[j * 8 + i] = tmpp[0] * ktm[i][0] + tmpp[1] * ktm[i][1] + tmpp[2] * ktm[i][2]; - } - } - } - } - }); - - // Step2 Pack 4: - // If the number of vector registers >= 32 and outch >= 8, - // the size = [8*8, K/4/2, C * 2, 4], otherwise [8*8, K/4, C, 4] - for (int r = 0; r < 64; r++) - { - int outc = 0; - float* out0 = wptrWino + r * K_aligned * C_aligned; - float* tmp0 = kernelTm + r; - -#if CV_NEON_AARCH64 - // Pack 8 - for (;outc + 7 < K_aligned; outc += 8) - { - for (int i = 0; i < 8; i++) - { - int outc_i = outc + i; - int offset8 = outc_i % 8; - int outc8 = outc_i / 8; - float* out1 = out0 + outc8 * 8 * C_aligned + offset8; - - if (outc_i >= K) - { - continue; - } - else - { - float* tmp1 = tmp0 + outc_i * 64 * C; - - for (int inc = 0; inc < C_aligned; inc++) - { - if (inc >= C) - continue; - - out1[inc * 8] = tmp1[inc * 64]; - } - } - } - } #endif - // Pack 4 - for (;outc < K_aligned; outc++) - { - int offset4 = outc % FAST_VEC_NLANES; - int outc4 = outc / FAST_VEC_NLANES; - float* out1 = out0 + outc4 * 4 * C_aligned + offset4; +/*Input transform*/ +static void +_fx_winograd_BtXB_8x8_f32(const float* inptr, int inpstep, + float* outptr, int Cg) +{ +#if CV_NEON && CV_NEON_AARCH64 + float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); + float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); + float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); + float32x4_t x30 = vld1q_f32(inptr + inpstep*3), x31 = vld1q_f32(inptr + inpstep*3 + 4); + float32x4_t x40 = vld1q_f32(inptr + inpstep*4), x41 = vld1q_f32(inptr + inpstep*4 + 4); + float32x4_t x50 = vld1q_f32(inptr + inpstep*5), x51 = vld1q_f32(inptr + inpstep*5 + 4); + float32x4_t x60 = vld1q_f32(inptr + inpstep*6), x61 = vld1q_f32(inptr + inpstep*6 + 4); + float32x4_t x70 = vld1q_f32(inptr + inpstep*7), x71 = vld1q_f32(inptr + inpstep*7 + 4); - if (outc >= K) - { - continue; - } - else - { - float* tmp1 = tmp0 + outc * 64 * C; + float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51, z60, z61, z70, z71; - for (int inc = 0; inc < C_aligned; inc++) - { - if (inc >= C) - continue; + { + /* Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X */ + /* Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X */ + float32x4_t q5_25 = vdupq_n_f32(5.25f), t00, t01, t10, t11; + t00 = vsubq_f32(x40, x20); + t01 = vsubq_f32(x41, x21); + t10 = vsubq_f32(x30, x50); + t11 = vsubq_f32(x31, x51); + float32x4_t y00 = vfmaq_f32(vsubq_f32(x00, x60), t00, q5_25); + float32x4_t y01 = vfmaq_f32(vsubq_f32(x01, x61), t01, q5_25); + float32x4_t y70 = vfmaq_f32(vsubq_f32(x70, x10), t10, q5_25); + float32x4_t y71 = vfmaq_f32(vsubq_f32(x71, x11), t11, q5_25); - out1[inc * 4] = tmp1[inc * 64]; - } - } - } + /* Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X */ + /* Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X */ + float32x4_t qm4_25 = vdupq_n_f32(-4.25f); + t00 = vfmaq_f32(vaddq_f32(x10, x50), x30, qm4_25); + t01 = vfmaq_f32(vaddq_f32(x11, x51), x31, qm4_25); + t10 = vfmaq_f32(vaddq_f32(x20, x60), x40, qm4_25); + t11 = vfmaq_f32(vaddq_f32(x21, x61), x41, qm4_25); + + float32x4_t y10 = vaddq_f32(t00, t10), y11 = vaddq_f32(t01, t11); + float32x4_t y20 = vsubq_f32(t10, t00), y21 = vsubq_f32(t11, t01); + + /* Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X */ + /* Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X */ + float32x4_t q0_5 = vdupq_n_f32(0.5f), q0_25 = vdupq_n_f32(0.25f); + float32x4_t qm2_5 = vdupq_n_f32(-2.5f), qm1_25 = vdupq_n_f32(-1.25f); + t00 = vfmaq_f32(vaddq_f32(x50, x50), x10, q0_5); + t01 = vfmaq_f32(vaddq_f32(x51, x51), x11, q0_5); + t10 = vfmaq_f32(x60, x20, q0_25); + t11 = vfmaq_f32(x61, x21, q0_25); + t00 = vfmaq_f32(t00, x30, qm2_5); + t01 = vfmaq_f32(t01, x31, qm2_5); + t10 = vfmaq_f32(t10, x40, qm1_25); + t11 = vfmaq_f32(t11, x41, qm1_25); + + float32x4_t y30 = vaddq_f32(t00, t10), y31 = vaddq_f32(t01, t11); + float32x4_t y40 = vsubq_f32(t10, t00), y41 = vsubq_f32(t11, t01); + + /* Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X */ + /* Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X */ + float32x4_t q4 = vdupq_n_f32(4.f), qm5 = vdupq_n_f32(-5.f); + t00 = vfmaq_f32(vaddq_f32(x10, x10), x50, q0_5); + t01 = vfmaq_f32(vaddq_f32(x11, x11), x51, q0_5); + t10 = vfmaq_f32(x60, x20, q4); + t11 = vfmaq_f32(x61, x21, q4); + t00 = vfmaq_f32(t00, x30, qm2_5); + t01 = vfmaq_f32(t01, x31, qm2_5); + t10 = vfmaq_f32(t10, x40, qm5); + t11 = vfmaq_f32(t11, x41, qm5); + + float32x4_t y50 = vaddq_f32(t00, t10), y51 = vaddq_f32(t01, t11); + float32x4_t y60 = vsubq_f32(t10, t00), y61 = vsubq_f32(t11, t01); + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y70 y71 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + float32x4x2_t tr0, tr1; + + T4x4(y00, y10, y20, y30, tr0, tr1); + T4x4(y01, y11, y21, y31, tr0, tr1); + T4x4(y40, y50, y60, y70, tr0, tr1); + T4x4(y41, y51, y61, y71, tr0, tr1); + + /* Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y */ + /* Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y */ + t00 = vsubq_f32(y01, y20); + t01 = vsubq_f32(y41, y60); + t10 = vsubq_f32(y30, y11); + t11 = vsubq_f32(y70, y51); + z00 = vfmaq_f32(vsubq_f32(y00, y21), t00, q5_25); + z01 = vfmaq_f32(vsubq_f32(y40, y61), t01, q5_25); + z70 = vfmaq_f32(vsubq_f32(y31, y10), t10, q5_25); + z71 = vfmaq_f32(vsubq_f32(y71, y50), t11, q5_25); + + /* Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y */ + /* Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y10, y11), y30, qm4_25); + t01 = vfmaq_f32(vaddq_f32(y50, y51), y70, qm4_25); + t10 = vfmaq_f32(vaddq_f32(y20, y21), y01, qm4_25); + t11 = vfmaq_f32(vaddq_f32(y60, y61), y41, qm4_25); + + z10 = vaddq_f32(t00, t10); z11 = vaddq_f32(t01, t11); + z20 = vsubq_f32(t10, t00); z21 = vsubq_f32(t11, t01); + + /* Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y */ + /* Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y11, y11), y10, q0_5); + t01 = vfmaq_f32(vaddq_f32(y51, y51), y50, q0_5); + t10 = vfmaq_f32(y21, y20, q0_25); + t11 = vfmaq_f32(y61, y60, q0_25); + t00 = vfmaq_f32(t00, y30, qm2_5); + t01 = vfmaq_f32(t01, y70, qm2_5); + t10 = vfmaq_f32(t10, y01, qm1_25); + t11 = vfmaq_f32(t11, y41, qm1_25); + + z30 = vaddq_f32(t00, t10); z31 = vaddq_f32(t01, t11); + z40 = vsubq_f32(t10, t00); z41 = vsubq_f32(t11, t01); + + /* Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y */ + /* Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y10, y10), y11, q0_5); + t01 = vfmaq_f32(vaddq_f32(y50, y50), y51, q0_5); + t10 = vfmaq_f32(y21, y20, q4); + t11 = vfmaq_f32(y61, y60, q4); + t00 = vfmaq_f32(t00, y30, qm2_5); + t01 = vfmaq_f32(t01, y70, qm2_5); + t10 = vfmaq_f32(t10, y01, qm5); + t11 = vfmaq_f32(t11, y41, qm5); + + z50 = vaddq_f32(t00, t10); z51 = vaddq_f32(t01, t11); + z60 = vsubq_f32(t10, t00); z61 = vsubq_f32(t11, t01); } + + const int outstep = _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32*Cg; + + vst1q_f32(outptr, z00); + vst1q_f32(outptr + outstep, z01); + vst1q_f32(outptr + outstep*2, z10); + vst1q_f32(outptr + outstep*3, z11); + vst1q_f32(outptr + outstep*4, z20); + vst1q_f32(outptr + outstep*5, z21); + vst1q_f32(outptr + outstep*6, z30); + vst1q_f32(outptr + outstep*7, z31); + vst1q_f32(outptr + outstep*8, z40); + vst1q_f32(outptr + outstep*9, z41); + vst1q_f32(outptr + outstep*10, z50); + vst1q_f32(outptr + outstep*11, z51); + vst1q_f32(outptr + outstep*12, z60); + vst1q_f32(outptr + outstep*13, z61); + vst1q_f32(outptr + outstep*14, z70); + vst1q_f32(outptr + outstep*15, z71); +#elif CV_SIMD + v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4); + v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4); + v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4); + v_float32x4 x30 = v_load(inptr + inpstep*3), x31 = v_load(inptr + inpstep*3 + 4); + v_float32x4 x40 = v_load(inptr + inpstep*4), x41 = v_load(inptr + inpstep*4 + 4); + v_float32x4 x50 = v_load(inptr + inpstep*5), x51 = v_load(inptr + inpstep*5 + 4); + v_float32x4 x60 = v_load(inptr + inpstep*6), x61 = v_load(inptr + inpstep*6 + 4); + v_float32x4 x70 = v_load(inptr + inpstep*7), x71 = v_load(inptr + inpstep*7 + 4); + + v_float32x4 z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51, z60, z61, z70, z71; + + { + /* Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X */ + /* Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X */ + v_float32x4 q5_25 = v_setall_f32(5.25f), t00, t01, t10, t11; + t00 = x40 - x20; + t01 = x41 - x21; + t10 = x30 - x50; + t11 = x31 - x51; + v_float32x4 y00 = v_fma(t00, q5_25, x00 - x60); + v_float32x4 y01 = v_fma(t01, q5_25, x01 - x61); + v_float32x4 y70 = v_fma(t10, q5_25, x70 - x10); + v_float32x4 y71 = v_fma(t11, q5_25, x71 - x11); + + /* Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X */ + /* Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X */ + v_float32x4 qm4_25 = v_setall_f32(-4.25f); + t00 = v_fma(x30, qm4_25, x10 + x50); + t01 = v_fma(x31, qm4_25, x11 + x51); + t10 = v_fma(x40, qm4_25, x20 + x60); + t11 = v_fma(x41, qm4_25, x21 + x61); + + v_float32x4 y10 = t00 + t10, y11 = t01 + t11; + v_float32x4 y20 = t10 - t00, y21 = t11 - t01; + + /* Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X */ + /* Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X */ + v_float32x4 q0_5 = v_setall_f32(0.5f), q0_25 = v_setall_f32(0.25f); + v_float32x4 qm2_5 = v_setall_f32(-2.5f), qm1_25 = v_setall_f32(-1.25f); + t00 = v_fma(x10, q0_5, x50 + x50); + t01 = v_fma(x11, q0_5, x51 + x51); + t10 = v_fma(x20, q0_25, x60); + t11 = v_fma(x21, q0_25, x61); + t00 = v_fma(x30, qm2_5, t00); + t01 = v_fma(x31, qm2_5, t01); + t10 = v_fma(x40, qm1_25, t10); + t11 = v_fma(x41, qm1_25, t11); + + v_float32x4 y30 = t00 + t10, y31 = t01 + t11; + v_float32x4 y40 = t10 - t00, y41 = t11 - t01; + + /* Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X */ + /* Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X */ + v_float32x4 q4 = v_setall_f32(4.f), qm5 = v_setall_f32(-5.f); + t00 = v_fma(x50, q0_5, x10 + x10); + t01 = v_fma(x51, q0_5, x11 + x11); + t10 = v_fma(x20, q4 , x60); + t11 = v_fma(x21, q4 , x61); + t00 = v_fma(x30, qm2_5, t00); + t01 = v_fma(x31, qm2_5, t01); + t10 = v_fma(x40, qm5 , t10); + t11 = v_fma(x41, qm5 , t11); + + v_float32x4 y50 = t00 + t10, y51 = t01 + t11; + v_float32x4 y60 = t10 - t00, y61 = t11 - t01; + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y70 y71 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + + v_transpose4x4(y00, y10, y20, y30, y00, y10, y20, y30); + v_transpose4x4(y01, y11, y21, y31, y01, y11, y21, y31); + v_transpose4x4(y40, y50, y60, y70, y40, y50, y60, y70); + v_transpose4x4(y41, y51, y61, y71, y41, y51, y61, y71); + + /* Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y */ + /* Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y */ + t00 = y01 - y20; + t01 = y41 - y60; + t10 = y30 - y11; + t11 = y70 - y51; + z00 = v_fma(t00, q5_25, y00 - y21); + z01 = v_fma(t01, q5_25, y40 - y61); + z70 = v_fma(t10, q5_25, y31 - y10); + z71 = v_fma(t11, q5_25, y71 - y50); + + /* Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y */ + /* Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y */ + t00 = v_fma(y30, qm4_25, y10 + y11); + t01 = v_fma(y70, qm4_25, y50 + y51); + t10 = v_fma(y01, qm4_25, y20 + y21); + t11 = v_fma(y41, qm4_25, y60 + y61); + + z10 = t00 + t10; z11 = t01 + t11; + z20 = t10 - t00; z21 = t11 - t01; + + /* Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y */ + /* Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y */ + t00 = v_fma(y10, q0_5, y11 + y11); + t01 = v_fma(y50, q0_5, y51 + y51); + t10 = v_fma(y20, q0_25, y21); + t11 = v_fma(y60, q0_25, y61); + t00 = v_fma(y30, qm2_5, t00); + t01 = v_fma(y70, qm2_5, t01); + t10 = v_fma(y01, qm1_25, t10); + t11 = v_fma(y41, qm1_25, t11); + + z30 = t00 + t10; z31 = t01 + t11; + z40 = t10 - t00; z41 = t11 - t01; + + /* Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y */ + /* Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y */ + t00 = v_fma(y11, q0_5, y10 + y10); + t01 = v_fma(y51, q0_5, y50 + y50); + t10 = v_fma(y20, q4, y21); + t11 = v_fma(y60, q4, y61); + t00 = v_fma(y30, qm2_5, t00); + t01 = v_fma(y70, qm2_5, t01); + t10 = v_fma(y01, qm5, t10); + t11 = v_fma(y41, qm5, t11); + + z50 = t00 + t10; z51 = t01 + t11; + z60 = t10 - t00; z61 = t11 - t01; + } + + const int outstep = _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32*Cg; + + v_store(outptr, z00); + v_store(outptr + outstep, z01); + v_store(outptr + outstep*2, z10); + v_store(outptr + outstep*3, z11); + v_store(outptr + outstep*4, z20); + v_store(outptr + outstep*5, z21); + v_store(outptr + outstep*6, z30); + v_store(outptr + outstep*7, z31); + v_store(outptr + outstep*8, z40); + v_store(outptr + outstep*9, z41); + v_store(outptr + outstep*10, z50); + v_store(outptr + outstep*11, z51); + v_store(outptr + outstep*12, z60); + v_store(outptr + outstep*13, z61); + v_store(outptr + outstep*14, z70); + v_store(outptr + outstep*15, z71); +#else +#error "Only SIMD128, AVX2 and NEON are supported in Winograd." +#endif } -int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, - float maxval, ActivationLayer* activ, bool ifMinMaxAct) +/* Inverse Winograd 8x8 transform: + out = (A'*inp*A)', where + inp is input 8x8 FP32 matrix, + A' is + [1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.f, + 0.f, 1.f, -1.f, 2.f, -2.f, 0.5f, -0.5f, 0.f, + 0.f, 1.f, 1.f, 4.f, 4.f, 0.25f, 0.25f, 0.f, + 0.f, 1.f, -1.f, 8.f, -8.f, 0.125f, -0.125f, 0.f, + 0.f, 1.f, 1.f, 16.f, 16.f, 1.f/16, 1.f/16, 0.f, + 0.f, 1.f, -1.f, 32.f, -32.f, 1.f/32, -1.f/32, 1.f] + + inp is pre-loaded into xij registers, + out will be stored in zij, where (0<=i<=7 for x, 0<=i<=5 for z), 0<=j<=1. + + After the inverse transform is done, we add bias, + optionally add results from the earlier tensors (by-pass), + optionally apply activation function and then + store the final results. + + Note that both _FX_WINOGRAD_FWD_8x8() and + _FX_WINOGRAD_INV_8x8() produce tranposed output. + That is, after both forward and then inverse transformation, + we get non-transposed result. + Of course, for the correct work of Winograd-based convolution, + the Winograd-transformed weights should also be transposed. + init_conv() (see OpConv.fx) takes care of that. +*/ +static void +_fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, + float* bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct) +{ +#if CV_NEON && CV_NEON_AARCH64 + float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); + float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); + float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); + float32x4_t x30 = vld1q_f32(inptr + inpstep*3), x31 = vld1q_f32(inptr + inpstep*3 + 4); + float32x4_t x40 = vld1q_f32(inptr + inpstep*4), x41 = vld1q_f32(inptr + inpstep*4 + 4); + float32x4_t x50 = vld1q_f32(inptr + inpstep*5), x51 = vld1q_f32(inptr + inpstep*5 + 4); + float32x4_t x60 = vld1q_f32(inptr + inpstep*6), x61 = vld1q_f32(inptr + inpstep*6 + 4); + float32x4_t x70 = vld1q_f32(inptr + inpstep*7), x71 = vld1q_f32(inptr + inpstep*7 + 4); + float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51; + + { + float32x4_t s12_0, s12_1, s34_0, s34_1, s56_0, s56_1; + s12_0 = vaddq_f32(x10, x20); s12_1 = vaddq_f32(x11, x21); + s34_0 = vaddq_f32(x30, x40); s34_1 = vaddq_f32(x31, x41); + s56_0 = vaddq_f32(x50, x60); s56_1 = vaddq_f32(x51, x61); + + float32x4_t y00 = vaddq_f32(vaddq_f32(vaddq_f32(x00, s12_0), s34_0), s56_0); + float32x4_t y01 = vaddq_f32(vaddq_f32(vaddq_f32(x01, s12_1), s34_1), s56_1); + float32x4_t y20 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 4.0f), s56_0, 0.25f); + float32x4_t y21 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 4.0f), s56_1, 0.25f); + float32x4_t y40 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 16.0f), s56_0, 1.f/16); + float32x4_t y41 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 16.0f), s56_1, 1.f/16); + + s12_0 = vsubq_f32(x10, x20); s12_1 = vsubq_f32(x11, x21); + s34_0 = vsubq_f32(x30, x40); s34_1 = vsubq_f32(x31, x41); + s56_0 = vsubq_f32(x50, x60); s56_1 = vsubq_f32(x51, x61); + + float32x4_t y50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x70, s12_0), + s34_0, 32.f), s56_0, 1.f/32); + float32x4_t y51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x71, s12_1), + s34_1, 32.f), s56_1, 1.f/32); + float32x4_t y10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); + float32x4_t y11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); + float32x4_t y30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); + float32x4_t y31 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 8.0f), s56_1, 0.125f); + float32x4_t y60 = vdupq_n_f32(0.f), y61 = y60, y70 = y60, y71 = y60; + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y50 y51 */ + /* 0 0 */ + /* 0 0 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + float32x4x2_t tr0, tr1; + + T4x4(y00, y10, y20, y30, tr0, tr1); + T4x4(y01, y11, y21, y31, tr0, tr1); + T4x4(y40, y50, y60, y70, tr0, tr1); + T4x4(y41, y51, y61, y71, tr0, tr1); + + s12_0 = vaddq_f32(y10, y20); s12_1 = vaddq_f32(y50, y60); + s34_0 = vaddq_f32(y30, y01); s34_1 = vaddq_f32(y70, y41); + s56_0 = vaddq_f32(y11, y21); s56_1 = vaddq_f32(y51, y61); + + z00 = vaddq_f32(vaddq_f32(vaddq_f32(y00, s12_0), s34_0), s56_0); + z01 = vaddq_f32(vaddq_f32(vaddq_f32(y40, s12_1), s34_1), s56_1); + z20 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 4.0f), s56_0, 0.25f); + z21 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 4.0f), s56_1, 0.25f); + z40 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 16.0f), s56_0, 1.f/16); + z41 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 16.0f), s56_1, 1.f/16); + + s12_0 = vsubq_f32(y10, y20); s12_1 = vsubq_f32(y50, y60); + s34_0 = vsubq_f32(y30, y01); s34_1 = vsubq_f32(y70, y41); + s56_0 = vsubq_f32(y11, y21); s56_1 = vsubq_f32(y51, y61); + + z50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y31, s12_0), + s34_0, 32.f), s56_0, 1.f/32); + z51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y71, s12_1), + s34_1, 32.f), s56_1, 1.f/32); + z10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); + z11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); + z30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); + z31 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 8.0f), s56_1, 0.125f); + float32x4_t vbias = vdupq_n_f32(bias); + + z00 = vaddq_f32(z00, vbias); + z01 = vaddq_f32(z01, vbias); + z10 = vaddq_f32(z10, vbias); + z11 = vaddq_f32(z11, vbias); + z20 = vaddq_f32(z20, vbias); + z21 = vaddq_f32(z21, vbias); + z30 = vaddq_f32(z30, vbias); + z31 = vaddq_f32(z31, vbias); + z40 = vaddq_f32(z40, vbias); + z41 = vaddq_f32(z41, vbias); + z50 = vaddq_f32(z50, vbias); + z51 = vaddq_f32(z51, vbias); + } + + if (bpptr) + { + float32x2_t zhalf = vdup_n_f32(0.f); + z00 = vaddq_f32(z00, vld1q_f32(bpptr)); + z01 = vaddq_f32(z01, vcombine_f32(vld1_f32(bpptr + 4), zhalf)); + z10 = vaddq_f32(z10, vld1q_f32(bpptr + bpstep)); + z11 = vaddq_f32(z11, vcombine_f32(vld1_f32(bpptr + bpstep + 4), zhalf)); + z20 = vaddq_f32(z20, vld1q_f32(bpptr + bpstep*2)); + z21 = vaddq_f32(z21, vcombine_f32(vld1_f32(bpptr + bpstep*2 + 4), zhalf)); + z30 = vaddq_f32(z30, vld1q_f32(bpptr + bpstep*3)); + z31 = vaddq_f32(z31, vcombine_f32(vld1_f32(bpptr + bpstep*3 + 4), zhalf)); + z40 = vaddq_f32(z40, vld1q_f32(bpptr + bpstep*4)); + z41 = vaddq_f32(z41, vcombine_f32(vld1_f32(bpptr + bpstep*4 + 4), zhalf)); + z50 = vaddq_f32(z50, vld1q_f32(bpptr + bpstep*5)); + z51 = vaddq_f32(z51, vcombine_f32(vld1_f32(bpptr + bpstep*5 + 4), zhalf)); + } + + if (ifMinMaxAct) + { + float32x4_t vmax = vdupq_n_f32(maxval); + float32x4_t vmin = vdupq_n_f32(minval); + + z00 = vminq_f32(vmaxq_f32(z00, vmin), vmax); + z01 = vminq_f32(vmaxq_f32(z01, vmin), vmax); + z10 = vminq_f32(vmaxq_f32(z10, vmin), vmax); + z11 = vminq_f32(vmaxq_f32(z11, vmin), vmax); + z20 = vminq_f32(vmaxq_f32(z20, vmin), vmax); + z21 = vminq_f32(vmaxq_f32(z21, vmin), vmax); + z30 = vminq_f32(vmaxq_f32(z30, vmin), vmax); + z31 = vminq_f32(vmaxq_f32(z31, vmin), vmax); + z40 = vminq_f32(vmaxq_f32(z40, vmin), vmax); + z41 = vminq_f32(vmaxq_f32(z41, vmin), vmax); + z50 = vminq_f32(vmaxq_f32(z50, vmin), vmax); + z51 = vminq_f32(vmaxq_f32(z51, vmin), vmax); + } + + vst1q_f32(outptr, z00); + vst1_f32(outptr + 4, vget_low_f32(z01)); + vst1q_f32(outptr + outstep, z10); + vst1_f32(outptr + outstep + 4, vget_low_f32(z11)); + vst1q_f32(outptr + outstep*2, z20); + vst1_f32(outptr + outstep*2 + 4, vget_low_f32(z21)); + vst1q_f32(outptr + outstep*3, z30); + vst1_f32(outptr + outstep*3 + 4, vget_low_f32(z31)); + vst1q_f32(outptr + outstep*4, z40); + vst1_f32(outptr + outstep*4 + 4, vget_low_f32(z41)); + vst1q_f32(outptr + outstep*5, z50); + vst1_f32(outptr + outstep*5 + 4, vget_low_f32(z51)); +//#elif CV_AVX2 +#elif CV_SIMD + v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4); + v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4); + v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4); + v_float32x4 x30 = v_load(inptr + inpstep*3), x31 = v_load(inptr + inpstep*3 + 4); + v_float32x4 x40 = v_load(inptr + inpstep*4), x41 = v_load(inptr + inpstep*4 + 4); + v_float32x4 x50 = v_load(inptr + inpstep*5), x51 = v_load(inptr + inpstep*5 + 4); + v_float32x4 x60 = v_load(inptr + inpstep*6), x61 = v_load(inptr + inpstep*6 + 4); + v_float32x4 x70 = v_load(inptr + inpstep*7), x71 = v_load(inptr + inpstep*7 + 4); + v_float32x4 z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51; + + { + v_float32x4 s12_0, s12_1, s34_0, s34_1, s56_0, s56_1; + s12_0 = x10 + x20; s12_1 = x11 + x21; + s34_0 = x30 + x40; s34_1 = x31 + x41; + s56_0 = x50 + x60; s56_1 = x51 + x61; + + v_float32x4 y00 = x00 + s12_0 + s34_0 + s56_0; + v_float32x4 y01 = x01 + s12_1 + s34_1 + s56_1; + + v_float32x4 a0 = v_setall_f32(0.25f), a1 = v_setall_f32(4.0f); + v_float32x4 y20 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + v_float32x4 y21 = v_fma(s56_1, a0 ,v_fma(s34_1, a1, s12_1) ); + + a0 = v_setall_f32(1.f/16), a1 = v_setall_f32(16.0f); + v_float32x4 y40 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + v_float32x4 y41 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + s12_0 = x10 - x20; s12_1 = x11 - x21; + s34_0 = x30 - x40; s34_1 = x31 - x41; + s56_0 = x50 - x60; s56_1 = x51 - x61; + + a0 = v_setall_f32(1.f/32), a1 = v_setall_f32(32.f); + v_float32x4 y50 = v_fma(s56_0, a0, v_fma(s34_0, a1, x70 + s12_0)); + v_float32x4 y51 = v_fma(s56_1, a0, v_fma(s34_1, a1, x71 + s12_1)); + + a0 = v_setall_f32(0.5f), a1 = v_setall_f32(2.f); + v_float32x4 y10 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + v_float32x4 y11 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + a0 = v_setall_f32(0.125f), a1 = v_setall_f32(8.f); + v_float32x4 y30 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + v_float32x4 y31 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + v_float32x4 y60 = v_setall_f32(0.f), y61 = y60, y70 = y60, y71 = y60; + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y50 y51 */ + /* 0 0 */ + /* 0 0 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + + v_transpose4x4(y00, y10, y20, y30, y00, y10, y20, y30); + v_transpose4x4(y01, y11, y21, y31, y01, y11, y21, y31); + v_transpose4x4(y40, y50, y60, y70, y40, y50, y60, y70); + v_transpose4x4(y41, y51, y61, y71, y41, y51, y61, y71); + + s12_0 = y10 + y20; s12_1 = y50 + y60; + s34_0 = y30 + y01; s34_1 = y70 + y41; + s56_0 = y11 + y21; s56_1 = y51 + y61; + + z00 = y00 + s12_0 + s34_0 + s56_0; + z01 = y40 + s12_1 + s34_1 + s56_1; + + a0 = v_setall_f32(0.25f), a1 = v_setall_f32(4.0f); + z20 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + z21 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + a0 = v_setall_f32(1.f/16), a1 = v_setall_f32(16.0f); + z40 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + z41 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + s12_0 = y10 - y20; s12_1 = y50 - y60; + s34_0 = y30 - y01; s34_1 = y70 - y41; + s56_0 = y11 - y21; s56_1 = y51 - y61; + + a0 = v_setall_f32(1.f/32), a1 = v_setall_f32(32.0f); + z50 = v_fma(s56_0, a0, v_fma(s34_0, a1, y31 + s12_0)); + z51 = v_fma(s56_1, a0, v_fma(s34_1, a1, y71 + s12_1)); + + a0 = v_setall_f32(0.5f), a1 = v_setall_f32(2.0f); + z10 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + z11 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + a0 = v_setall_f32(0.125f), a1 = v_setall_f32(8.0f); + z30 = v_fma(s56_0, a0, v_fma(s34_0, a1, s12_0)); + z31 = v_fma(s56_1, a0, v_fma(s34_1, a1, s12_1)); + + v_float32x4 vbias = v_setall_f32(bias); + z00 += vbias; + z01 += vbias; + z10 += vbias; + z11 += vbias; + z20 += vbias; + z21 += vbias; + z30 += vbias; + z31 += vbias; + z40 += vbias; + z41 += vbias; + z50 += vbias; + z51 += vbias; + } + + if (bpptr) + { + z00 += v_load(bpptr); + z01 += v_load_low(bpptr + 4); + z10 += v_load(bpptr + bpstep); + z11 += v_load_low(bpptr + bpstep + 4); + z20 += v_load(bpptr + bpstep*2); + z21 += v_load_low(bpptr + bpstep*2 + 4); + z30 += v_load(bpptr + bpstep*3); + z31 += v_load_low(bpptr + bpstep*3 + 4); + z40 += v_load(bpptr + bpstep*4); + z41 += v_load_low(bpptr + bpstep*4 + 4); + z50 += v_load(bpptr + bpstep*5); + z51 += v_load_low(bpptr + bpstep*5 + 4); + } + + if (ifMinMaxAct) + { + v_float32x4 vmax = v_setall_f32(maxval); + v_float32x4 vmin = v_setall_f32(minval); + + z00 = v_min(v_max(z00, vmin), vmax); + z01 = v_min(v_max(z01, vmin), vmax); + z10 = v_min(v_max(z10, vmin), vmax); + z11 = v_min(v_max(z11, vmin), vmax); + z20 = v_min(v_max(z20, vmin), vmax); + z21 = v_min(v_max(z21, vmin), vmax); + z30 = v_min(v_max(z30, vmin), vmax); + z31 = v_min(v_max(z31, vmin), vmax); + z40 = v_min(v_max(z40, vmin), vmax); + z41 = v_min(v_max(z41, vmin), vmax); + z50 = v_min(v_max(z50, vmin), vmax); + z51 = v_min(v_max(z51, vmin), vmax); + } + + v_store(outptr, z00); + v_store_low(outptr + 4, z01); + v_store(outptr + outstep, z10); + v_store_low(outptr + outstep + 4, z11); + v_store(outptr + outstep*2, z20); + v_store_low(outptr + outstep*2 + 4, z21); + v_store(outptr + outstep*3, z30); + v_store_low(outptr + outstep*3 + 4, z31); + v_store(outptr + outstep*4, z40); + v_store_low(outptr + outstep*4 + 4, z41); + v_store(outptr + outstep*5, z50); + v_store_low(outptr + outstep*5 + 4, z51); +#else +#error "Only SIMD128, AVX2 and NEON are supported in Winograd." +#endif +} + +void runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, + int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) { Mat input = _input.getMat(); Mat output = _output.getMat(); @@ -545,1158 +934,209 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu int K = conv->K; int H0 = outputShape[2], W0 = outputShape[3]; - // Allocate the right memory size for output. - // H and W is integer of 6. the output HxW is integer of 6x6 - int H_tiles = ((H0 + 5) / 6); - int W_tiles = ((W0 + 5) / 6); - int tiles = H_tiles * W_tiles; - - int H0_align = H_tiles * 6; - int W0_align = W_tiles * 6; - - int Hi_align = H0_align + 2; - int Wi_align = W0_align + 2; - - int pad_top = conv->pad_top, pad_bottom = Hi_align - pad_top - Hi; - int pad_left = conv->pad_left, pad_right = Wi_align - pad_left - Wi; - - int in_top = pad_top, in_bottom = Hi_align - pad_bottom; - int in_left = pad_left, in_right = Wi_align - pad_right; - - CV_Assert(in_bottom >= in_top && in_right >= in_left); - - int C_aligned = ((C + FAST_VEC_NLANES - 1)/FAST_VEC_NLANES) * FAST_VEC_NLANES; - int K_aligned = ((K + FAST_VEC_NLANES - 1)/FAST_VEC_NLANES) * FAST_VEC_NLANES; - - int inpPack = 0; - int lineNum =0; - -#if CV_NEON_AARCH64 - if (tiles >= 12) - { - inpPack = 12; - lineNum = tiles / 12 + (tiles % 12) / 8 + (tiles % 12 % 8) / 4 + (tiles % 12 % 4) / 2 + tiles % 12 % 2; - } - else -#endif - if (tiles >= 8) - { - inpPack = 8; - lineNum = tiles / 8 + (tiles % 8) / 4 + (tiles % 4) / 2 + tiles % 2; - } - else - if (tiles >= 4) - { - inpPack = 4; - lineNum = tiles / 4 + (tiles % 4) / 2 + tiles % 2; - } - else if (tiles >= 2) - { - inpPack = 2; - lineNum = tiles / 2 + tiles % 2; - } - else // tiles >= 1 - { - inpPack = 1; - lineNum = tiles; - } - CV_Assert(lineNum > 0 && inpPack > 0); - std::vector ofstab0_(tiles * 2, 0); - int* ofstab0 = ofstab0_.data(); // [line Number, input pack] - - int tiles_tmp = tiles; - int line_0 = 0; - - int* ofstab_tmp = ofstab0; - int big_step = inpPack * C_aligned * lineNum; - int line_step = inpPack * C_aligned; - - std::vector linePackList = {12, 8, 4, 2, 1}; - auto iter = std::find(linePackList.begin(), linePackList.end(), inpPack); - CV_Assert(iter != linePackList.end()); - int ptr = iter - linePackList.begin(); - - while (ptr < linePackList.size() && tiles_tmp != 0) - { - if (tiles_tmp >= linePackList[ptr]) - { - int num = tiles_tmp / linePackList[ptr]; - for (int i = 0; i < num; i ++) - { - for (int j = 0; j < linePackList[ptr]; j++) - { - ofstab_tmp[0] = line_0; - ofstab_tmp[1] = linePackList[ptr]; - ofstab_tmp += 2; - } - line_0++; - } - tiles_tmp -= num * linePackList[ptr]; - } - else - { - ptr++; - } - } + int pad_top = conv->pad_top; + int pad_left = conv->pad_left; + int ngroups = conv->ngroups, Cg = C/ngroups, Kg = K/ngroups; + int Kg_nblocks = (Kg + _FX_WINO_KBLOCK - 1)/_FX_WINO_KBLOCK; const size_t inp_planesize = (size_t)Hi*Wi; const size_t out_planesize = (size_t)H0*W0; - size_t inputbuf_size = inpPack * C_aligned * lineNum * 64; - size_t inputbufCn_size = ntasks * tiles * 4 * 8 * 8; + int blocks_per_row = (W0+_FX_WINO_STEP-1)/_FX_WINO_STEP; + int blocks_per_plane = ((H0+_FX_WINO_STEP-1)/_FX_WINO_STEP)*blocks_per_row; + int blocks_per_plane_aligned = ((blocks_per_plane + + _FX_WINO_IBLOCK-1)/_FX_WINO_IBLOCK)*_FX_WINO_IBLOCK; - size_t outputbuf_size = tiles * K_aligned * 8 * 8; - size_t outputCnbuf_size = ntasks * 8 * 8 * 4; + size_t totalbufsize = (size_t)N*C*blocks_per_plane_aligned*_FX_WINO_AREA; - size_t part0_size = std::max(inputbuf_size, outputCnbuf_size); - size_t allbuf_size = part0_size + std::max(inputbufCn_size, outputbuf_size); + AutoBuffer _buf; + _buf.allocate(totalbufsize + VEC_ALIGN); + float* wbuf_all = alignPtr(_buf.data(), VEC_ALIGN); - AutoBuffer allbuf_; - allbuf_.allocate(allbuf_size); - float* inputbuf0 = alignPtr(allbuf_.data(), (int)(sizeof(float))); - float* inputCnbuf0 = inputbuf0 + inputbuf_size; - float* outputbuf0 = inputCnbuf0; - float* outputCnbuf0 = inputbuf0; + float* inp = input.ptr(); + float* out = output.ptr(); - // Input Parallel For - float* weight_ptr0 = conv->weightsWino63Buf.data(); + float* fusedAddPtr = fusedAddMat.empty() ? nullptr : fusedAddMat.ptr(); - for (int bn = 0; bn < N; bn++) + // Phase 1. compute forward Winograd transforms for all input blocks, + // all input planes, all samples in the batch. + // [TODO]: maybe, if there are too many input channels, it makes sense to + // transform only part of input channels at once and then compute the partial + // accumulated sums (i.e. update the output buffers several times, + // rather than compute them in one pass). + parallel_for_(Range(0, ntasks), [&](const Range& r0) { + for (int task_id = r0.start; task_id < r0.end; task_id++) { - float* input_ptr0 = input.ptr() + bn * inp_planesize * C; - float* output_ptr0 = output.ptr() + bn * out_planesize * K; - float* fusedAddPtr0 = fusedAddMat.empty() ? 0 : fusedAddMat.ptr() + bn * out_planesize * K; - - // Transform Input - int C_aligned_div4 = C_aligned/4; - const int tiStep = 8 * 8 * FAST_VEC_NLANES; - - parallel_for_(Range(0, ntasks), [&](const Range& range){ - for (int task_i = range.start; task_i < range.end; task_i++) + int nc0 = (N*C)*task_id/ntasks; + int nc1 = (N*C)*(task_id+1)/ntasks; + for(; nc0 < nc1; nc0++) + { + int n = nc0 / C; + int c = nc0 - n*C; + int g = c / Cg; + c -= g*Cg; + for (int block_id = 0; block_id < blocks_per_plane; block_id += _FX_WINO_IBLOCK) { - float *inpCnbuf = inputCnbuf0 + tiles * tiStep * task_i; - for (int inc4 = task_i; inc4 < C_aligned_div4; inc4 += ntasks) + for (int db = 0; db < _FX_WINO_IBLOCK; db++) { - for (int cn = 0; cn < 4; cn++) + size_t inwofs = ((n*ngroups + g)*blocks_per_plane_aligned + + block_id)*Cg*_FX_WINO_AREA + + (c*_FX_WINO_IBLOCK + db)*_FX_WINO_ATOM_F32; + float* inwptr = (float*)wbuf_all + inwofs; + + if (block_id + db < blocks_per_plane) { - if (cn + inc4 * 4 >= C) + int y0 = (block_id + db) / blocks_per_row; + int x0 = (block_id + db) - y0 * blocks_per_row; + y0 = y0*_FX_WINO_STEP - pad_top; + x0 = x0*_FX_WINO_STEP - pad_left; + bool partial = y0 < 0 || y0 + _FX_WINO_SIZE > Hi || + x0 < 0 || x0 + _FX_WINO_SIZE > Wi; + int dx1 = 0, dx2 = _FX_WINO_SIZE, dy1 = 0, dy2 = _FX_WINO_SIZE; + int inpstep = Wi; + + float inpbuf[_FX_WINO_AREA]; + float* inptr0 = (float*)inp + nc0*inp_planesize + y0*Wi + x0; + float* inptr = inptr0; + + if (partial) { - // set value to zero - for (int ti = 0; ti < tiles; ti++) + memset(inpbuf, 0, sizeof(inpbuf)); + dy1 = -y0 > 0 ? -y0 : 0; + dy2 = Hi - y0 < _FX_WINO_SIZE ? Hi - y0 : _FX_WINO_SIZE; + + if (dy2 < dy1) {dy2 = dy1 = 0;} + dx1 = -x0 > 0 ? -x0 : 0; + dx2 = Wi - x0 < _FX_WINO_SIZE ? Wi - x0 : _FX_WINO_SIZE; + + if (dx2 < dx1) {dx2 = dx1 = 0;} + inptr0 -= y0*Wi + x0; + + if (dx1 < dx2 && dy1 < dy2) { - float *inpCnbuf_i = inpCnbuf + ti * 4 * 64 + cn; - - for (int i = 0; i < 8; i++) - { - inpCnbuf_i[0] = 0.0f; - inpCnbuf_i[4] = 0.0f; - inpCnbuf_i[8] = 0.0f; - inpCnbuf_i[12] = 0.0f; - - inpCnbuf_i[16] = 0.0f; - inpCnbuf_i[20] = 0.0f; - inpCnbuf_i[24] = 0.0f; - inpCnbuf_i[28] = 0.0f; - - inpCnbuf_i += 4 * 8; - } + for(int dy = dy1; dy < dy2; dy++) + memcpy(&inpbuf[dy*_FX_WINO_SIZE + dx1], + inptr0 + (y0+dy)*Wi + (x0+dx1), + (dx2-dx1)*sizeof(inpbuf[0])); } + + inptr = inpbuf; + inpstep = _FX_WINO_SIZE; } +#if CV_TRY_AVX2 + if (conv->useAVX2) + opt_AVX2::_fx_winograd_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg); else - { - float *input_ptr = input_ptr0 + (inc4 * 4 + cn) * Hi * Wi; - - for (int ti = 0; ti < tiles; ti++) - { - float *input_buf0_i = inpCnbuf + ti * 256 + cn; - - int hi = ti / W_tiles; - int wi = ti % W_tiles; - - int h_top = hi * 6, h_bottom = hi * 6 + 8; - int w_left = wi * 6, w_right = wi * 6 + 8; - - for (int h = h_top; h < h_bottom; h++) - { - if (h >= in_bottom || h < in_top) - { - input_buf0_i[0] = 0.0f; - input_buf0_i[4] = 0.0f; - input_buf0_i[8] = 0.0f; - input_buf0_i[12] = 0.0f; - - input_buf0_i[16] = 0.0f; - input_buf0_i[20] = 0.0f; - input_buf0_i[24] = 0.0f; - input_buf0_i[28] = 0.0f; - - input_buf0_i += 32; - continue; - } - - for (int w = w_left; w < w_right; w++) - { - if (w >= in_right || w < in_left) - { - input_buf0_i[0] = 0.0f; - input_buf0_i += 4; - continue; - } - input_buf0_i[0] = input_ptr[(h - pad_top) * Wi + w - pad_left]; - input_buf0_i += 4; - } - } - } - } +#endif + _fx_winograd_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg); } - - // Transform Compute BdB^T - winograd_trans_input_F63(inpCnbuf, inputbuf0, inc4, tiles, big_step, line_step, ofstab0); - } - } - }); - // Matrix multiplication 8 channel - int K_div8 = 0; -#if CV_NEON_AARCH64 - K_div8 = K_aligned/8; - // Transpose 12 - if (inpPack == 12) - { - int C_div4 = C_aligned/4; - parallel_for_(Range(0, 64), [&](const Range &range){ - for (int r = range.start; r < range.end; r++) - { - float* input_tm = inputbuf0 + r * big_step; - - for (int ti = 0; ti + 11 < tiles; ti += 12) - { - float* r0 = input_tm + ofstab0[ti * 2] * line_step; - transpose12x4(r0, r0, C_div4); - } - } - }); - } - - parallel_for_(Range(0, 64), [&](const Range &range){ - for (int r = range.start; r < range.end; r++) - { - float* input_tm = inputbuf0 + r * big_step; - float* output_tmp = outputbuf0 + tiles * K_aligned * r; - float* kernel_tmp = weight_ptr0 + r * C_aligned * K_aligned; - - for (int out_div8 = 0; out_div8 < K_div8; out_div8 ++) - { - float* output0_tm = output_tmp + tiles * out_div8 * 8; - float* output1_tm = output0_tm + tiles * 4; - float* kernel_tm_i = kernel_tmp + out_div8 * 8 * C_aligned; - - int ti = 0; - for (; ti + 11 < tiles; ti += 12) - { - float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k01 = kernel_tm_i; - - int nn = C_aligned/4; - r0 = input_tm + ofstab0[ti * 2] * line_step; - - // init 32 registers. FMA/load ratio = 96/20 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00, r02 = r00, r03 = r00; - float32x4_t r04 = r00, r05 = r00, r06 = r00, r07 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r12 = r00, r13 = r00, r14 = r00, r15 = r00; - float32x4_t r16 = r00, r17 = r00, r18 = r00, r19 = r00; - float32x4_t r20 = r00, r21 = r00, r22 = r00, r23 = r00; - float32x4_t r24 = r00, r25 = r00, r26 = r00, r27 = r00; - float32x4_t r28 = r00, r29 = r00, r30 = r00, r31 = r00; - - for(;nn > 0; nn--) + else { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r04 = vld1q_f32(k01), r05 = vld1q_f32(k01+4), r06 = vld1q_f32(k01+8), r07 = vld1q_f32(k01+12); - r0 += 16, k01 += 16; - - // Cn0 - // 8 ~ 19 - r08 = vfmaq_laneq_f32(r08, r04, r00, 0); - r09 = vfmaq_laneq_f32(r09, r04, r00, 1); - r10 = vfmaq_laneq_f32(r10, r04, r00, 2); - r11 = vfmaq_laneq_f32(r11, r04, r00, 3); - - r12 = vfmaq_laneq_f32(r12, r04, r01, 0); - r13 = vfmaq_laneq_f32(r13, r04, r01, 1); - r14 = vfmaq_laneq_f32(r14, r04, r01, 2); - r15 = vfmaq_laneq_f32(r15, r04, r01, 3); - - r16 = vfmaq_laneq_f32(r16, r04, r02, 0); - r17 = vfmaq_laneq_f32(r17, r04, r02, 1); - r18 = vfmaq_laneq_f32(r18, r04, r02, 2); - r19 = vfmaq_laneq_f32(r19, r04, r02, 3); - - // 20 ~ 31 - r20 = vfmaq_laneq_f32(r20, r05, r00, 0); - r21 = vfmaq_laneq_f32(r21, r05, r00, 1); - r22 = vfmaq_laneq_f32(r22, r05, r00, 2); - r23 = vfmaq_laneq_f32(r23, r05, r00, 3); - - r24 = vfmaq_laneq_f32(r24, r05, r01, 0); - r25 = vfmaq_laneq_f32(r25, r05, r01, 1); - r26 = vfmaq_laneq_f32(r26, r05, r01, 2); - r27 = vfmaq_laneq_f32(r27, r05, r01, 3); - - r28 = vfmaq_laneq_f32(r28, r05, r02, 0); - r29 = vfmaq_laneq_f32(r29, r05, r02, 1); - r30 = vfmaq_laneq_f32(r30, r05, r02, 2); - r31 = vfmaq_laneq_f32(r31, r05, r02, 3); - - // Cn1 - r08 = vfmaq_laneq_f32(r08, r06, r03, 0); - r09 = vfmaq_laneq_f32(r09, r06, r03, 1); - r10 = vfmaq_laneq_f32(r10, r06, r03, 2); - r11 = vfmaq_laneq_f32(r11, r06, r03, 3); - - r20 = vfmaq_laneq_f32(r20, r07, r03, 0); - r21 = vfmaq_laneq_f32(r21, r07, r03, 1); - r22 = vfmaq_laneq_f32(r22, r07, r03, 2); - r23 = vfmaq_laneq_f32(r23, r07, r03, 3); - - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r0 += 16; - - r12 = vfmaq_laneq_f32(r12, r06, r00, 0); - r13 = vfmaq_laneq_f32(r13, r06, r00, 1); - r14 = vfmaq_laneq_f32(r14, r06, r00, 2); - r15 = vfmaq_laneq_f32(r15, r06, r00, 3); - - r16 = vfmaq_laneq_f32(r16, r06, r01, 0); - r17 = vfmaq_laneq_f32(r17, r06, r01, 1); - r18 = vfmaq_laneq_f32(r18, r06, r01, 2); - r19 = vfmaq_laneq_f32(r19, r06, r01, 3); - - r24 = vfmaq_laneq_f32(r24, r07, r00, 0); - r25 = vfmaq_laneq_f32(r25, r07, r00, 1); - r26 = vfmaq_laneq_f32(r26, r07, r00, 2); - r27 = vfmaq_laneq_f32(r27, r07, r00, 3); - - r28 = vfmaq_laneq_f32(r28, r07, r01, 0); - r29 = vfmaq_laneq_f32(r29, r07, r01, 1); - r30 = vfmaq_laneq_f32(r30, r07, r01, 2); - r31 = vfmaq_laneq_f32(r31, r07, r01, 3); - - r04 = vld1q_f32(k01), r05 = vld1q_f32(k01+4), r06 = vld1q_f32(k01+8), r07 = vld1q_f32(k01+12); - k01 += 16; - - // Cn2 - r08 = vfmaq_laneq_f32(r08, r04, r02, 0); - r09 = vfmaq_laneq_f32(r09, r04, r02, 1); - r10 = vfmaq_laneq_f32(r10, r04, r02, 2); - r11 = vfmaq_laneq_f32(r11, r04, r02, 3); - - r12 = vfmaq_laneq_f32(r12, r04, r03, 0); - r13 = vfmaq_laneq_f32(r13, r04, r03, 1); - r14 = vfmaq_laneq_f32(r14, r04, r03, 2); - r15 = vfmaq_laneq_f32(r15, r04, r03, 3); - - r20 = vfmaq_laneq_f32(r20, r05, r02, 0); - r21 = vfmaq_laneq_f32(r21, r05, r02, 1); - r22 = vfmaq_laneq_f32(r22, r05, r02, 2); - r23 = vfmaq_laneq_f32(r23, r05, r02, 3); - - r24 = vfmaq_laneq_f32(r24, r05, r03, 0); - r25 = vfmaq_laneq_f32(r25, r05, r03, 1); - r26 = vfmaq_laneq_f32(r26, r05, r03, 2); - r27 = vfmaq_laneq_f32(r27, r05, r03, 3); - - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r0 += 16; - - r16 = vfmaq_laneq_f32(r16, r04, r00, 0); - r17 = vfmaq_laneq_f32(r17, r04, r00, 1); - r18 = vfmaq_laneq_f32(r18, r04, r00, 2); - r19 = vfmaq_laneq_f32(r19, r04, r00, 3); - - r28 = vfmaq_laneq_f32(r28, r05, r00, 0); - r29 = vfmaq_laneq_f32(r29, r05, r00, 1); - r30 = vfmaq_laneq_f32(r30, r05, r00, 2); - r31 = vfmaq_laneq_f32(r31, r05, r00, 3); - - // Cn3 - // 8 ~ 19 - r08 = vfmaq_laneq_f32(r08, r06, r01, 0); - r09 = vfmaq_laneq_f32(r09, r06, r01, 1); - r10 = vfmaq_laneq_f32(r10, r06, r01, 2); - r11 = vfmaq_laneq_f32(r11, r06, r01, 3); - - r12 = vfmaq_laneq_f32(r12, r06, r02, 0); - r13 = vfmaq_laneq_f32(r13, r06, r02, 1); - r14 = vfmaq_laneq_f32(r14, r06, r02, 2); - r15 = vfmaq_laneq_f32(r15, r06, r02, 3); - - r16 = vfmaq_laneq_f32(r16, r06, r03, 0); - r17 = vfmaq_laneq_f32(r17, r06, r03, 1); - r18 = vfmaq_laneq_f32(r18, r06, r03, 2); - r19 = vfmaq_laneq_f32(r19, r06, r03, 3); - - // 20 ~ 31 - r20 = vfmaq_laneq_f32(r20, r07, r01, 0); - r21 = vfmaq_laneq_f32(r21, r07, r01, 1); - r22 = vfmaq_laneq_f32(r22, r07, r01, 2); - r23 = vfmaq_laneq_f32(r23, r07, r01, 3); - - r24 = vfmaq_laneq_f32(r24, r07, r02, 0); - r25 = vfmaq_laneq_f32(r25, r07, r02, 1); - r26 = vfmaq_laneq_f32(r26, r07, r02, 2); - r27 = vfmaq_laneq_f32(r27, r07, r02, 3); - - r28 = vfmaq_laneq_f32(r28, r07, r03, 0); - r29 = vfmaq_laneq_f32(r29, r07, r03, 1); - r30 = vfmaq_laneq_f32(r30, r07, r03, 2); - r31 = vfmaq_laneq_f32(r31, r07, r03, 3); + for (int i = 0; i < _FX_WINO_NATOMS_F32; i++, inwptr += _FX_WINO_IBLOCK*_FX_WINO_ATOM_F32) + memset(inwptr, 0, _FX_WINO_ATOM_F32*sizeof(inwptr[0])); } - - vst1q_f32(output0_tm, r08), vst1q_f32(output0_tm + 4, r09), vst1q_f32(output0_tm + 8, r10), vst1q_f32(output0_tm + 12, r11); - output0_tm += 16; - vst1q_f32(output1_tm, r20), vst1q_f32(output1_tm + 4, r21), vst1q_f32(output1_tm + 8, r22), vst1q_f32(output1_tm + 12, r23); - output1_tm += 16; - - vst1q_f32(output0_tm, r12), vst1q_f32(output0_tm + 4, r13), vst1q_f32(output0_tm + 8, r14), vst1q_f32(output0_tm + 12, r15); - output0_tm += 16; - vst1q_f32(output1_tm, r24), vst1q_f32(output1_tm + 4, r25), vst1q_f32(output1_tm + 8, r26), vst1q_f32(output1_tm + 12, r27); - output1_tm += 16; - - vst1q_f32(output0_tm, r16), vst1q_f32(output0_tm + 4, r17), vst1q_f32(output0_tm + 8, r18), vst1q_f32(output0_tm + 12, r19); - output0_tm += 16; - vst1q_f32(output1_tm, r28), vst1q_f32(output1_tm + 4, r29), vst1q_f32(output1_tm + 8, r30), vst1q_f32(output1_tm + 12, r31); - output1_tm += 16; - } - - for (; ti + 7 < tiles; ti += 8) - { - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k01 = kernel_tm_i; - - int nn = C_aligned/4; - - // init 32 registers. FMA/load ratio = 64/16 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00, r02 = r00, r03 = r00; - float32x4_t r04 = r00, r05 = r00, r06 = r00, r07 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r12 = r00, r13 = r00, r14 = r00, r15 = r00; - float32x4_t r16 = r00, r17 = r00, r18 = r00, r19 = r00; - float32x4_t r20 = r00, r21 = r00, r22 = r00, r23 = r00; - float32x4_t r24 = r00, r25 = r00, r26 = r00, r27 = r00; - float32x4_t r28 = r00, r29 = r00, r30 = r00, r31 = r00; - - for(;nn > 0; nn--) - { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r08 = vld1q_f32(k01), r09 = vld1q_f32(k01+4), r10 = vld1q_f32(k01+8), r11 = vld1q_f32(k01+12); - r0 += 16, k01 += 16; - - r16 = vfmaq_laneq_f32(r16, r08, r00, 0); - r17 = vfmaq_laneq_f32(r17, r08, r01, 0); - r18 = vfmaq_laneq_f32(r18, r08, r02, 0); - r19 = vfmaq_laneq_f32(r19, r08, r03, 0); - - r04 = vld1q_f32(r0), r05 = vld1q_f32(r0+4), r06 = vld1q_f32(r0+8), r07 = vld1q_f32(r0+12); - r0 += 16; - - r20 = vfmaq_laneq_f32(r20, r08, r04, 0); - r21 = vfmaq_laneq_f32(r21, r08, r05, 0); - r22 = vfmaq_laneq_f32(r22, r08, r06, 0); - r23 = vfmaq_laneq_f32(r23, r08, r07, 0); - - r24 = vfmaq_laneq_f32(r24, r09, r00, 0); - r25 = vfmaq_laneq_f32(r25, r09, r01, 0); - r26 = vfmaq_laneq_f32(r26, r09, r02, 0); - r27 = vfmaq_laneq_f32(r27, r09, r03, 0); - r28 = vfmaq_laneq_f32(r28, r09, r04, 0); - r29 = vfmaq_laneq_f32(r29, r09, r05, 0); - r30 = vfmaq_laneq_f32(r30, r09, r06, 0); - r31 = vfmaq_laneq_f32(r31, r09, r07, 0); - - r12 = vld1q_f32(k01), r13 = vld1q_f32(k01+4), r14 = vld1q_f32(k01+8), r15 = vld1q_f32(k01+12); - k01 += 16; - - r16 = vfmaq_laneq_f32(r16, r10, r00, 1); - r17 = vfmaq_laneq_f32(r17, r10, r01, 1); - r18 = vfmaq_laneq_f32(r18, r10, r02, 1); - r19 = vfmaq_laneq_f32(r19, r10, r03, 1); - r20 = vfmaq_laneq_f32(r20, r10, r04, 1); - r21 = vfmaq_laneq_f32(r21, r10, r05, 1); - r22 = vfmaq_laneq_f32(r22, r10, r06, 1); - r23 = vfmaq_laneq_f32(r23, r10, r07, 1); - - r24 = vfmaq_laneq_f32(r24, r11, r00, 1); - r25 = vfmaq_laneq_f32(r25, r11, r01, 1); - r26 = vfmaq_laneq_f32(r26, r11, r02, 1); - r27 = vfmaq_laneq_f32(r27, r11, r03, 1); - r28 = vfmaq_laneq_f32(r28, r11, r04, 1); - r29 = vfmaq_laneq_f32(r29, r11, r05, 1); - r30 = vfmaq_laneq_f32(r30, r11, r06, 1); - r31 = vfmaq_laneq_f32(r31, r11, r07, 1); - - r16 = vfmaq_laneq_f32(r16, r12, r00, 2); - r17 = vfmaq_laneq_f32(r17, r12, r01, 2); - r18 = vfmaq_laneq_f32(r18, r12, r02, 2); - r19 = vfmaq_laneq_f32(r19, r12, r03, 2); - r20 = vfmaq_laneq_f32(r20, r12, r04, 2); - r21 = vfmaq_laneq_f32(r21, r12, r05, 2); - r22 = vfmaq_laneq_f32(r22, r12, r06, 2); - r23 = vfmaq_laneq_f32(r23, r12, r07, 2); - - r24 = vfmaq_laneq_f32(r24, r13, r00, 2); - r25 = vfmaq_laneq_f32(r25, r13, r01, 2); - r26 = vfmaq_laneq_f32(r26, r13, r02, 2); - r27 = vfmaq_laneq_f32(r27, r13, r03, 2); - r28 = vfmaq_laneq_f32(r28, r13, r04, 2); - r29 = vfmaq_laneq_f32(r29, r13, r05, 2); - r30 = vfmaq_laneq_f32(r30, r13, r06, 2); - r31 = vfmaq_laneq_f32(r31, r13, r07, 2); - - r16 = vfmaq_laneq_f32(r16, r14, r00, 3); - r17 = vfmaq_laneq_f32(r17, r14, r01, 3); - r18 = vfmaq_laneq_f32(r18, r14, r02, 3); - r19 = vfmaq_laneq_f32(r19, r14, r03, 3); - r20 = vfmaq_laneq_f32(r20, r14, r04, 3); - r21 = vfmaq_laneq_f32(r21, r14, r05, 3); - r22 = vfmaq_laneq_f32(r22, r14, r06, 3); - r23 = vfmaq_laneq_f32(r23, r14, r07, 3); - - r24 = vfmaq_laneq_f32(r24, r15, r00, 3); - r25 = vfmaq_laneq_f32(r25, r15, r01, 3); - r26 = vfmaq_laneq_f32(r26, r15, r02, 3); - r27 = vfmaq_laneq_f32(r27, r15, r03, 3); - r28 = vfmaq_laneq_f32(r28, r15, r04, 3); - r29 = vfmaq_laneq_f32(r29, r15, r05, 3); - r30 = vfmaq_laneq_f32(r30, r15, r06, 3); - r31 = vfmaq_laneq_f32(r31, r15, r07, 3); - } - - vst1q_f32(output0_tm, r16), vst1q_f32(output0_tm + 4, r17), vst1q_f32(output0_tm + 8, r18), vst1q_f32(output0_tm + 12, r19); - output0_tm += 16; - vst1q_f32(output1_tm, r24), vst1q_f32(output1_tm + 4, r25), vst1q_f32(output1_tm + 8, r26), vst1q_f32(output1_tm + 12, r27); - output1_tm += 16; - - vst1q_f32(output0_tm, r20), vst1q_f32(output0_tm + 4, r21), vst1q_f32(output0_tm + 8, r22), vst1q_f32(output0_tm + 12, r23); - output0_tm += 16; - vst1q_f32(output1_tm, r28), vst1q_f32(output1_tm + 4, r29), vst1q_f32(output1_tm + 8, r30), vst1q_f32(output1_tm + 12, r31); - output1_tm += 16; - } - - for (; ti + 3 < tiles; ti += 4) - { - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k01 = kernel_tm_i; - - int nn = C_aligned/4; - - // init 20 registers. FMA/load ratio = 32/12 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00, r02 = r00, r03 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r12 = r00, r13 = r00, r14 = r00, r15 = r00; - float32x4_t r24 = r00, r25 = r00, r26 = r00, r27 = r00; - float32x4_t r28 = r00, r29 = r00, r30 = r00, r31 = r00; - - for(; nn > 0; nn--) - { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r08 = vld1q_f32(k01), r09 = vld1q_f32(k01+4), r10 = vld1q_f32(k01+8), r11 = vld1q_f32(k01+12); - r0 += 16, k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r08, r00, 0); - r25 = vfmaq_laneq_f32(r25, r08, r01, 0); - r26 = vfmaq_laneq_f32(r26, r08, r02, 0); - r27 = vfmaq_laneq_f32(r27, r08, r03, 0); - - r28 = vfmaq_laneq_f32(r28, r09, r00, 0); - r29 = vfmaq_laneq_f32(r29, r09, r01, 0); - r30 = vfmaq_laneq_f32(r30, r09, r02, 0); - r31 = vfmaq_laneq_f32(r31, r09, r03, 0); - - r12 = vld1q_f32(k01), r13 = vld1q_f32(k01+4), r14 = vld1q_f32(k01+8), r15 = vld1q_f32(k01+12); - k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r10, r00, 1); - r25 = vfmaq_laneq_f32(r25, r10, r01, 1); - r26 = vfmaq_laneq_f32(r26, r10, r02, 1); - r27 = vfmaq_laneq_f32(r27, r10, r03, 1); - - r28 = vfmaq_laneq_f32(r28, r11, r00, 1); - r29 = vfmaq_laneq_f32(r29, r11, r01, 1); - r30 = vfmaq_laneq_f32(r30, r11, r02, 1); - r31 = vfmaq_laneq_f32(r31, r11, r03, 1); - - r24 = vfmaq_laneq_f32(r24, r12, r00, 2); - r25 = vfmaq_laneq_f32(r25, r12, r01, 2); - r26 = vfmaq_laneq_f32(r26, r12, r02, 2); - r27 = vfmaq_laneq_f32(r27, r12, r03, 2); - - r28 = vfmaq_laneq_f32(r28, r13, r00, 2); - r29 = vfmaq_laneq_f32(r29, r13, r01, 2); - r30 = vfmaq_laneq_f32(r30, r13, r02, 2); - r31 = vfmaq_laneq_f32(r31, r13, r03, 2); - - r24 = vfmaq_laneq_f32(r24, r14, r00, 3); - r25 = vfmaq_laneq_f32(r25, r14, r01, 3); - r26 = vfmaq_laneq_f32(r26, r14, r02, 3); - r27 = vfmaq_laneq_f32(r27, r14, r03, 3); - - r28 = vfmaq_laneq_f32(r28, r15, r00, 3); - r29 = vfmaq_laneq_f32(r29, r15, r01, 3); - r30 = vfmaq_laneq_f32(r30, r15, r02, 3); - r31 = vfmaq_laneq_f32(r31, r15, r03, 3); - } - - vst1q_f32(output0_tm, r24), vst1q_f32(output0_tm + 4, r25), vst1q_f32(output0_tm + 8, r26), vst1q_f32(output0_tm + 12, r27); - output0_tm += 16; - vst1q_f32(output1_tm, r28), vst1q_f32(output1_tm + 4, r29), vst1q_f32(output1_tm + 8, r30), vst1q_f32(output1_tm + 12, r31); - output1_tm += 16; - } - - for (; ti + 1 < tiles; ti += 2) - { - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k01 = kernel_tm_i; - - int nn = C_aligned/4; - - // init 14 registers. FMA/load ratio = 15/10 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r12 = r00, r13 = r00, r14 = r00, r15 = r00; - float32x4_t r24 = r00, r25 = r00; - float32x4_t r28 = r00, r29 = r00; - - for (; nn > 0; nn--) - { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4); - r08 = vld1q_f32(k01), r09 = vld1q_f32(k01+4), r10 = vld1q_f32(k01+8), r11 = vld1q_f32(k01+12); - r0 += 8, k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r08, r00, 0); - r25 = vfmaq_laneq_f32(r25, r08, r01, 0); - - r28 = vfmaq_laneq_f32(r28, r09, r00, 0); - r29 = vfmaq_laneq_f32(r29, r09, r01, 0); - - r12 = vld1q_f32(k01), r13 = vld1q_f32(k01+4), r14 = vld1q_f32(k01+8), r15 = vld1q_f32(k01+12); - k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r10, r00, 1); - r25 = vfmaq_laneq_f32(r25, r10, r01, 1); - - r28 = vfmaq_laneq_f32(r28, r11, r00, 1); - r29 = vfmaq_laneq_f32(r29, r11, r01, 1); - - r24 = vfmaq_laneq_f32(r24, r12, r00, 2); - r25 = vfmaq_laneq_f32(r25, r12, r01, 2); - - r28 = vfmaq_laneq_f32(r28, r13, r00, 2); - r29 = vfmaq_laneq_f32(r29, r13, r01, 2); - - r24 = vfmaq_laneq_f32(r24, r14, r00, 3); - r25 = vfmaq_laneq_f32(r25, r14, r01, 3); - - r28 = vfmaq_laneq_f32(r28, r15, r00, 3); - r29 = vfmaq_laneq_f32(r29, r15, r01, 3); - } - - vst1q_f32(output0_tm, r24), vst1q_f32(output0_tm + 4, r25); - output0_tm += 8; - vst1q_f32(output1_tm, r28), vst1q_f32(output1_tm + 4, r29); - output1_tm += 8; - } - - for (; ti < tiles; ti ++) - { - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k01 = kernel_tm_i; - - int nn = C_aligned/4; - - float32x4_t r00 = vdupq_n_f32(0.0f); - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r12 = r00, r13 = r00, r14 = r00, r15 = r00; - float32x4_t r24 = r00; - float32x4_t r28 = r00; - - for(;nn > 0; nn--) - { - r00 = vld1q_f32(r0); - r08 = vld1q_f32(k01), r09 = vld1q_f32(k01+4), r10 = vld1q_f32(k01+8), r11 = vld1q_f32(k01+12); - r0 += 4, k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r08, r00, 0); - r28 = vfmaq_laneq_f32(r28, r09, r00, 0); - - r12 = vld1q_f32(k01), r13 = vld1q_f32(k01+4), r14 = vld1q_f32(k01+8), r15 = vld1q_f32(k01+12); - k01 += 16; - - r24 = vfmaq_laneq_f32(r24, r10, r00, 1); - r28 = vfmaq_laneq_f32(r28, r11, r00, 1); - - r24 = vfmaq_laneq_f32(r24, r12, r00, 2); - r28 = vfmaq_laneq_f32(r28, r13, r00, 2); - - r24 = vfmaq_laneq_f32(r24, r14, r00, 3); - r28 = vfmaq_laneq_f32(r28, r15, r00, 3); - } - - vst1q_f32(output0_tm, r24); - output0_tm += 4; - vst1q_f32(output1_tm, r28); - output1_tm += 4; } } } - }); -#endif + }}); - // Matrix multiplication, 4 output channel. - int Ock_div4 = (K_aligned - K_div8 * 8) / 4; - parallel_for_(Range(0, 64), [&](const Range &range){ - for (int r = range.start; r < range.end; r++) + // Phase 2. compute elemwise-weighted sums of transformed blocks, + // apply inverse Winograd transforms to the sums, + // add bias, apply activation function if any and store the results. + parallel_for_(Range(0, ntasks), [&](const Range& r0) { + for (int task_id = r0.start; task_id < r0.end; task_id++) + { + size_t out_wbuf_size = _FX_WINO_AREA*_FX_WINO_KBLOCK*_FX_WINO_IBLOCK; + size_t outbuf_size = _FX_WINO_AREA; + AutoBuffer out_wbuf_, outbuf_; + out_wbuf_.allocate(out_wbuf_size + VEC_ALIGN); + float* out_wbuf = alignPtr(out_wbuf_.data(), VEC_ALIGN); + outbuf_.allocate(outbuf_size + VEC_ALIGN); + float* outbuf = alignPtr(outbuf_.data(), VEC_ALIGN); + + memset(out_wbuf, 0, out_wbuf_size * sizeof(float)); + memset(outbuf, 0, outbuf_size * sizeof(float)); + + int ngk0 = (int)(((int64_t)N*Kg_nblocks*ngroups)*task_id/ntasks); + int ngk1 = (int)(((int64_t)N*Kg_nblocks*ngroups)*(task_id+1)/ntasks); + + for(; ngk0 < ngk1; ngk0++) + { + int n = ngk0 / (Kg_nblocks*ngroups); + int gk0 = ngk0 % (Kg_nblocks*ngroups); + int g = gk0 / Kg_nblocks; + int k0 = (gk0 % Kg_nblocks)*_FX_WINO_KBLOCK; + int k1 = k0 + _FX_WINO_KBLOCK <= Kg ? k0 + _FX_WINO_KBLOCK : Kg; + + for (int block_id0 = 0; block_id0 < blocks_per_plane; block_id0 += _FX_WINO_IBLOCK) { - float* input_tm = inputbuf0 + r * big_step; - float* output_tmp = outputbuf0 + tiles * K_aligned * r; - float* kernel_tmp = weight_ptr0 + r * C_aligned * K_aligned; + int block_id1 = block_id0 + _FX_WINO_IBLOCK; + block_id1 = block_id1 < blocks_per_plane ? block_id1 : blocks_per_plane; + size_t inwofs = ((n*ngroups + g)*blocks_per_plane_aligned + block_id0)*Cg*_FX_WINO_AREA; + size_t wofs = (g*Kg_nblocks*_FX_WINO_KBLOCK + k0)*Cg*_FX_WINO_AREA; - for (int out_div4 = 0; out_div4 < Ock_div4; out_div4 ++) + float* inwptr = wbuf_all + inwofs; + const float* wptr = conv->weightsWinoBufPtr + wofs; + +#if CV_TRY_AVX2 + if (conv->useAVX2) + opt_AVX2::_fx_winograd_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0); + else +#endif + _fx_winograd_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0); + for (int k = k0; k < k1; k++) { - float* output0_tm = output_tmp + tiles * (out_div4 + K_div8 * 2) * 4 ; - float* kernel_tm_i = kernel_tmp + (out_div4 + K_div8 * 2) * 4 * C_aligned; - - int ti = 0; - for (; ti + 7 < tiles; ti += 8) + float biasv = conv->biasBuf[g*Kg + k]; + for (int block_id = block_id0; block_id < block_id1; block_id++) { - int nn = C_aligned/4; - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k0 = kernel_tm_i; + int y0 = block_id / blocks_per_row; + int x0 = block_id - y0 * blocks_per_row; + y0 = y0*_FX_WINO_STEP; + x0 = x0*_FX_WINO_STEP; + int dy1 = H0 - y0; + if (dy1 > _FX_WINO_STEP) dy1 = _FX_WINO_STEP; + int dx1 = W0 - x0; + if (dx1 > _FX_WINO_STEP) dx1 = _FX_WINO_STEP; + assert(dx1 > 0 && dy1 > 0); + bool partial = activ || dy1 < _FX_WINO_STEP || dx1 < _FX_WINO_STEP; + size_t outofs = (n*K + g*Kg + k)*out_planesize + y0*W0 + x0; + int outstep = W0; -#if CV_NEON_AARCH64 - // init 24 registers. FMA/load ratio = 32/12 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00, r02 = r00, r03 = r00; - float32x4_t r04 = r00, r05 = r00, r06 = r00, r07 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r16 = r00, r17 = r00, r18 = r00, r19 = r00; - float32x4_t r20 = r00, r21 = r00, r22 = r00, r23 = r00; + float* outptr0 = (float*)out + outofs; + float* pbptr0 = fusedAddPtr ? fusedAddPtr + outofs : nullptr; + float *outptr = outptr0, *bpptr = pbptr0; - for(; nn > 0; nn--) + if (partial) { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 16, k0 += 16; - - r16 = vfmaq_laneq_f32(r16, r08, r00, 0); - r17 = vfmaq_laneq_f32(r17, r08, r01, 0); - r18 = vfmaq_laneq_f32(r18, r08, r02, 0); - r19 = vfmaq_laneq_f32(r19, r08, r03, 0); - - r04 = vld1q_f32(r0), r05 = vld1q_f32(r0+4), r06 = vld1q_f32(r0+8), r07 = vld1q_f32(r0+12); - r0 += 16; - - r20 = vfmaq_laneq_f32(r20, r08, r04, 0); - r21 = vfmaq_laneq_f32(r21, r08, r05, 0); - r22 = vfmaq_laneq_f32(r22, r08, r06, 0); - r23 = vfmaq_laneq_f32(r23, r08, r07, 0); - - r16 = vfmaq_laneq_f32(r16, r09, r00, 1); - r17 = vfmaq_laneq_f32(r17, r09, r01, 1); - r18 = vfmaq_laneq_f32(r18, r09, r02, 1); - r19 = vfmaq_laneq_f32(r19, r09, r03, 1); - r20 = vfmaq_laneq_f32(r20, r09, r04, 1); - r21 = vfmaq_laneq_f32(r21, r09, r05, 1); - r22 = vfmaq_laneq_f32(r22, r09, r06, 1); - r23 = vfmaq_laneq_f32(r23, r09, r07, 1); - - r16 = vfmaq_laneq_f32(r16, r10, r00, 2); - r17 = vfmaq_laneq_f32(r17, r10, r01, 2); - r18 = vfmaq_laneq_f32(r18, r10, r02, 2); - r19 = vfmaq_laneq_f32(r19, r10, r03, 2); - r20 = vfmaq_laneq_f32(r20, r10, r04, 2); - r21 = vfmaq_laneq_f32(r21, r10, r05, 2); - r22 = vfmaq_laneq_f32(r22, r10, r06, 2); - r23 = vfmaq_laneq_f32(r23, r10, r07, 2); - - r16 = vfmaq_laneq_f32(r16, r11, r00, 3); - r17 = vfmaq_laneq_f32(r17, r11, r01, 3); - r18 = vfmaq_laneq_f32(r18, r11, r02, 3); - r19 = vfmaq_laneq_f32(r19, r11, r03, 3); - r20 = vfmaq_laneq_f32(r20, r11, r04, 3); - r21 = vfmaq_laneq_f32(r21, r11, r05, 3); - r22 = vfmaq_laneq_f32(r22, r11, r06, 3); - r23 = vfmaq_laneq_f32(r23, r11, r07, 3); + outptr = outbuf; + outstep = _FX_WINO_SIZE; + if (pbptr0) + { + bpptr = outbuf; + for (int y = 0; y < dy1; y++) + memcpy(outbuf + y*_FX_WINO_SIZE, pbptr0 + y*W0, + dx1*sizeof(pbptr0[0])); + } } - - vst1q_f32(output0_tm, r16), vst1q_f32(output0_tm + 4, r17), vst1q_f32(output0_tm + 8, r18), vst1q_f32(output0_tm + 12, r19); - output0_tm += 16; - - vst1q_f32(output0_tm, r20), vst1q_f32(output0_tm + 4, r21), vst1q_f32(output0_tm + 8, r22), vst1q_f32(output0_tm + 12, r23); - output0_tm += 16; - -#else // ARMv7 16 registers. - - // init 16 registers. FMA/load ratio = 32/12 - float32x2_t q00 = vdup_n_f32(0.0f), q01 = q00, q02 = q00, q03 = q00, - q04 = q00, q05 = q00, q06 = q00, q07 = q00; - - float32x4_t r04 = vdupq_n_f32(0.0f), r05 = r04, r06 = r04, r07 = r04; - float32x4_t r08 = r04, r09 = r04, r10 = r04, r11 = r04; - float32x4_t r12 = r04, r13 = r04, r14 = r04, r15 = r04; - - for (; nn > 0; nn--) - { - q00 = vld1_f32(r0), q01 = vld1_f32(r0+2), q02 = vld1_f32(r0+4), q03 = vld1_f32(r0+6); - q04 = vld1_f32(r0+8), q05 = vld1_f32(r0+10), q06 = vld1_f32(r0+12), q07 = vld1_f32(r0+14); - r04 = vld1q_f32(k0), r05 = vld1q_f32(k0+4), r06 = vld1q_f32(k0+8), r07 = vld1q_f32(k0+12); - r0 += 16, k0 += 16; - - r08 = vmlaq_lane_f32(r08, r04, q00, 0); - r09 = vmlaq_lane_f32(r09, r04, q02, 0); - r10 = vmlaq_lane_f32(r10, r04, q04, 0); - r11 = vmlaq_lane_f32(r11, r04, q06, 0); - - r08 = vmlaq_lane_f32(r08, r05, q00, 1); - r09 = vmlaq_lane_f32(r09, r05, q02, 1); - r10 = vmlaq_lane_f32(r10, r05, q04, 1); - r11 = vmlaq_lane_f32(r11, r05, q06, 1); - - r08 = vmlaq_lane_f32(r08, r06, q01, 0); - r09 = vmlaq_lane_f32(r09, r06, q03, 0); - r10 = vmlaq_lane_f32(r10, r06, q05, 0); - r11 = vmlaq_lane_f32(r11, r06, q07, 0); - - r08 = vmlaq_lane_f32(r08, r07, q01, 1); - r09 = vmlaq_lane_f32(r09, r07, q03, 1); - r10 = vmlaq_lane_f32(r10, r07, q05, 1); - r11 = vmlaq_lane_f32(r11, r07, q07, 1); - - q00 = vld1_f32(r0), q01 = vld1_f32(r0+2), q02 = vld1_f32(r0+4), q03 = vld1_f32(r0+6); - q04 = vld1_f32(r0+8), q05 = vld1_f32(r0+10), q06 = vld1_f32(r0+12), q07 = vld1_f32(r0+14); - r0 += 16; - - r12 = vmlaq_lane_f32(r12, r04, q00, 0); - r13 = vmlaq_lane_f32(r13, r04, q02, 0); - r14 = vmlaq_lane_f32(r14, r04, q04, 0); - r15 = vmlaq_lane_f32(r15, r04, q06, 0); - - r12 = vmlaq_lane_f32(r12, r05, q00, 1); - r13 = vmlaq_lane_f32(r13, r05, q02, 1); - r14 = vmlaq_lane_f32(r14, r05, q04, 1); - r15 = vmlaq_lane_f32(r15, r05, q06, 1); - - r12 = vmlaq_lane_f32(r12, r06, q01, 0); - r13 = vmlaq_lane_f32(r13, r06, q03, 0); - r14 = vmlaq_lane_f32(r14, r06, q05, 0); - r15 = vmlaq_lane_f32(r15, r06, q07, 0); - - r12 = vmlaq_lane_f32(r12, r07, q01, 1); - r13 = vmlaq_lane_f32(r13, r07, q03, 1); - r14 = vmlaq_lane_f32(r14, r07, q05, 1); - r15 = vmlaq_lane_f32(r15, r07, q07, 1); - } - - vst1q_f32(output0_tm, r08), vst1q_f32(output0_tm + 4, r09), vst1q_f32(output0_tm + 8, r10), vst1q_f32(output0_tm + 12, r11); - output0_tm += 16; - - vst1q_f32(output0_tm, r12), vst1q_f32(output0_tm + 4, r13), vst1q_f32(output0_tm + 8, r14), vst1q_f32(output0_tm + 12, r15); - output0_tm += 16; +#if CV_TRY_AVX2 + if (conv->useAVX2) + opt_AVX2::_fx_winograd_AtXA_8x8_f32(out_wbuf + ((k - k0)*_FX_WINO_IBLOCK + (block_id - block_id0))*_FX_WINO_AREA, _FX_WINO_SIZE, + bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); + else #endif - } - - for (; ti + 3 < tiles; ti += 4) - { - int nn = C_aligned/4; - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k0 = kernel_tm_i; - -#if CV_NEON_AARCH64 - // init 12 registers. FMA/load ratio = 12/8 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00, r02 = r00, r03 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r16 = r00, r17 = r00, r18 = r00, r19 = r00; - - for(; nn > 0; nn--) + _fx_winograd_AtXA_8x8_f32(out_wbuf + ((k - k0)*_FX_WINO_IBLOCK + (block_id - block_id0))*_FX_WINO_AREA, _FX_WINO_SIZE, + bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); + if (partial) { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4), r02 = vld1q_f32(r0+8), r03 = vld1q_f32(r0+12); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 16, k0 += 16; - - r16 = vfmaq_laneq_f32(r16, r08, r00, 0); - r17 = vfmaq_laneq_f32(r17, r08, r01, 0); - r18 = vfmaq_laneq_f32(r18, r08, r02, 0); - r19 = vfmaq_laneq_f32(r19, r08, r03, 0); - - r16 = vfmaq_laneq_f32(r16, r09, r00, 1); - r17 = vfmaq_laneq_f32(r17, r09, r01, 1); - r18 = vfmaq_laneq_f32(r18, r09, r02, 1); - r19 = vfmaq_laneq_f32(r19, r09, r03, 1); - - r16 = vfmaq_laneq_f32(r16, r10, r00, 2); - r17 = vfmaq_laneq_f32(r17, r10, r01, 2); - r18 = vfmaq_laneq_f32(r18, r10, r02, 2); - r19 = vfmaq_laneq_f32(r19, r10, r03, 2); - - r16 = vfmaq_laneq_f32(r16, r11, r00, 3); - r17 = vfmaq_laneq_f32(r17, r11, r01, 3); - r18 = vfmaq_laneq_f32(r18, r11, r02, 3); - r19 = vfmaq_laneq_f32(r19, r11, r03, 3); + if (activ) + activ->forwardSlice(outptr, outptr, _FX_WINO_SIZE*_FX_WINO_STEP, 0, g*Kg + k, g*Kg + k + 1); + for (int y = 0; y < dy1; y++) + memcpy(outptr0 + y*W0, outptr + y*_FX_WINO_SIZE,dx1*sizeof(outptr0[0])); } -#else - // init 12 registers. FMA/load ratio = 12/8 - float32x2_t q00 = vdup_n_f32(0.0f), q01 = q00, q02 = q00, q03 = q00, - q04 = q00, q05 = q00, q06 = q00, q07 = q00; - float32x4_t r08 = vdupq_n_f32(0.0f), r09 = r08, r10 = r08, r11 = r08; - float32x4_t r16 = r08, r17 = r08, r18 = r08, r19 = r08; - - for(; nn > 0; nn--) - { - q00 = vld1_f32(r0), q01 = vld1_f32(r0+2), q02 = vld1_f32(r0+4), q03 = vld1_f32(r0+6); - q04 = vld1_f32(r0+8), q05 = vld1_f32(r0+10), q06 = vld1_f32(r0+12), q07 = vld1_f32(r0+14); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 16, k0 += 16; - - r16 = vmlaq_lane_f32(r16, r08, q00, 0); - r17 = vmlaq_lane_f32(r17, r08, q02, 0); - r18 = vmlaq_lane_f32(r18, r08, q04, 0); - r19 = vmlaq_lane_f32(r19, r08, q06, 0); - - r16 = vmlaq_lane_f32(r16, r09, q00, 1); - r17 = vmlaq_lane_f32(r17, r09, q02, 1); - r18 = vmlaq_lane_f32(r18, r09, q04, 1); - r19 = vmlaq_lane_f32(r19, r09, q06, 1); - - r16 = vmlaq_lane_f32(r16, r10, q01, 0); - r17 = vmlaq_lane_f32(r17, r10, q03, 0); - r18 = vmlaq_lane_f32(r18, r10, q05, 0); - r19 = vmlaq_lane_f32(r19, r10, q07, 0); - - r16 = vmlaq_lane_f32(r16, r11, q01, 1); - r17 = vmlaq_lane_f32(r17, r11, q03, 1); - r18 = vmlaq_lane_f32(r18, r11, q05, 1); - r19 = vmlaq_lane_f32(r19, r11, q07, 1); - - } -#endif - vst1q_f32(output0_tm, r16), vst1q_f32(output0_tm + 4, r17), vst1q_f32(output0_tm + 8, r18), vst1q_f32(output0_tm + 12, r19); - output0_tm += 16; - } - - for (; ti + 1 < tiles; ti += 2) - { - int nn = C_aligned/4; - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k0 = kernel_tm_i; - -#if CV_NEON_AARCH64 - // init 8 registers. FMA/load ratio = 8/6 - float32x4_t r00 = vdupq_n_f32(0.0f), r01 = r00; - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r16 = r00, r17 = r00; - - for(; nn > 0; nn--) - { - r00 = vld1q_f32(r0), r01 = vld1q_f32(r0+4); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 8, k0 += 16; - - r16 = vfmaq_laneq_f32(r16, r08, r00, 0); - r17 = vfmaq_laneq_f32(r17, r08, r01, 0); - - r16 = vfmaq_laneq_f32(r16, r09, r00, 1); - r17 = vfmaq_laneq_f32(r17, r09, r01, 1); - - r16 = vfmaq_laneq_f32(r16, r10, r00, 2); - r17 = vfmaq_laneq_f32(r17, r10, r01, 2); - - r16 = vfmaq_laneq_f32(r16, r11, r00, 3); - r17 = vfmaq_laneq_f32(r17, r11, r01, 3); - } -#else - // init 8 registers. FMA/load ratio = 8/6 - float32x2_t q00 = vdup_n_f32(0.0f), q01 = q00, q02 = q00, q03 = q00; - float32x4_t r08 = vdupq_n_f32(0.0f), r09 = r08, r10 = r08, r11 = r08; - float32x4_t r16 = r08, r17 = r08; - - for(; nn > 0; nn--) - { - q00 = vld1_f32(r0), q01 = vld1_f32(r0+2), q02 = vld1_f32(r0+4), q03 = vld1_f32(r0+6); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 8, k0 += 16; - - r16 = vmlaq_lane_f32(r16, r08, q00, 0); - r17 = vmlaq_lane_f32(r17, r08, q02, 0); - - r16 = vmlaq_lane_f32(r16, r09, q00, 1); - r17 = vmlaq_lane_f32(r17, r09, q02, 1); - - r16 = vmlaq_lane_f32(r16, r10, q01, 0); - r17 = vmlaq_lane_f32(r17, r10, q03, 0); - - r16 = vmlaq_lane_f32(r16, r11, q01, 1); - r17 = vmlaq_lane_f32(r17, r11, q03, 1); - } -#endif - vst1q_f32(output0_tm, r16), vst1q_f32(output0_tm + 4, r17); - output0_tm += 8; - } - - for (; ti < tiles; ti ++) - { - int nn = C_aligned/4; - const float* r0 = input_tm + ofstab0[ti * 2] * line_step; - const float* k0 = kernel_tm_i; - -#if CV_NEON_AARCH64 - // init 6 registers. FMA/load ratio = 6/5 - float32x4_t r00 = vdupq_n_f32(0.0f); - float32x4_t r08 = r00, r09 = r00, r10 = r00, r11 = r00; - float32x4_t r16 = r00; - - for(; nn > 0; nn--) - { - r00 = vld1q_f32(r0); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 4, k0 += 16; - - r16 = vfmaq_laneq_f32(r16, r08, r00, 0); - r16 = vfmaq_laneq_f32(r16, r09, r00, 1); - r16 = vfmaq_laneq_f32(r16, r10, r00, 2); - r16 = vfmaq_laneq_f32(r16, r11, r00, 3); - } -#else - // init 6 registers. FMA/load ratio = 6/5 - float32x2_t q00 = vdup_n_f32(0.0f), q01 = q00; - float32x4_t r08 = vdupq_n_f32(0.0f), r09 = r08, r10 = r08, r11 = r08; - float32x4_t r16 = r08; - - for(; nn > 0; nn--) - { - q00 = vld1_f32(r0), q01 = vld1_f32(r0+2); - r08 = vld1q_f32(k0), r09 = vld1q_f32(k0+4), r10 = vld1q_f32(k0+8), r11 = vld1q_f32(k0+12); - r0 += 4, k0 += 16; - - r16 = vmlaq_lane_f32(r16, r08, q00, 0); - r16 = vmlaq_lane_f32(r16, r09, q00, 1); - r16 = vmlaq_lane_f32(r16, r10, q01, 0); - r16 = vmlaq_lane_f32(r16, r11, q01, 1); - } -#endif - vst1q_f32(output0_tm, r16); - output0_tm += 4; } } } - }); - - int bigStepOut = tiles * K_aligned; - AutoBuffer _fAbuf; - float* fAbuf0 = 0; - if (fusedAddPtr0) - { - _fAbuf.allocate(6 * 6 * 4 * ntasks); - fAbuf0 = _fAbuf.data(); } - - // Transfor Ouput - parallel_for_(Range(0, ntasks), [&](const Range& range) - { - for (int task_i = range.start; task_i < range.end; task_i++) - { - float* fAbuf = fAbuf0 ? fAbuf0 + task_i * 6 * 6 * 4 : 0; - float* outputCnbuf = outputCnbuf0 + task_i * 8 * 8 * 4; - for (int outCn4 = task_i; outCn4 < K_aligned / 4; outCn4 += ntasks) - { - - int outCn = outCn4 * 4; - float* output_buf = outputbuf0 + outCn * tiles; - float* output_ptr = output_ptr0 + outCn * W0 * H0; - float* fusedAddPtr = fusedAddPtr0 + outCn * W0 * H0; - - for (int ti = 0; ti < tiles; ti++) - { - float* output_buf_i = output_buf + ti * 4; - float* outputCnbuf_i = outputCnbuf; - int hi = ti / W_tiles; - int wi = ti % W_tiles; - - int wEnd = (wi + 1) * 6 > W0 ? W0 - (wi * 6) : 6; - int hEnd = (hi + 1) * 6 > H0 ? H0 - (hi * 6) : 6; - - // construct the output tile. - for (int r = 0; r < 64; r++) - { - memcpy(outputCnbuf_i, output_buf_i, FAST_VEC_NLANES * sizeof(float )); - output_buf_i += bigStepOut; - outputCnbuf_i += FAST_VEC_NLANES; - } - - // construct the fusedAdd buffer. - if (fAbuf && fusedAddPtr0) - { - memset(fAbuf, 0, sizeof(fAbuf[0]) * 6 * 6 * 4); - float* fAPtr = fusedAddPtr + (hi * W0 + wi) * 6; - for (int outCni = 0; outCni < FAST_VEC_NLANES; outCni++) - { - float* fAbufCnPtr = fAPtr + outCni * out_planesize; // skip channel - for (int i = 0; i < hEnd; i++) - { - for (int j = 0; j < wEnd; j++) - { - fAbuf[(i * 6 + j) * FAST_VEC_NLANES + outCni] = fAbufCnPtr[i * W0 + j]; - } - } - } - } - - winograd_trans_output_F63(outputCnbuf, conv->biasBuf.data() + outCn, fAbuf, - minval, maxval, ifMinMaxAct); - - float* output_ptr_i = output_ptr + (hi * W0 + wi) * 6; - - // write back the output data. - for (int outCni = 0; outCni < FAST_VEC_NLANES; outCni++) - { - float* output_ptr_i_cn = output_ptr_i + outCni * out_planesize; - outputCnbuf_i = outputCnbuf + outCni; - - if (outCni + outCn < K) - { - for (int i = 0; i < hEnd; i++) - { - for (int j = 0; j < wEnd; j++) - { - output_ptr_i_cn[i * W0 + j] = outputCnbuf_i[(i * 6 + j) * FAST_VEC_NLANES ]; - } - } - } - } - } - - if (activ) - { - int outCnEnd = std::min(outCn + FAST_VEC_NLANES, K); - activ->forwardSlice(output_ptr, output_ptr, out_planesize, - out_planesize, outCn, outCnEnd); - } - } - } - }); - } - return 1; + }}); } -#else - -void initWinograd63(Ptr& conv, InputArray _weightsMat, int K, int C) -{ - conv->useWinograd63 = false; -} - -int runWinograd63(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) -{ - return 0; -} - -#endif - }} // namespace cv::dnn diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index dd2ff1db38..1933c11572 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -545,7 +545,7 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) Mat img = imread(findDataFile("dnn/googlenet_1.png")); Mat inp = blobFromImage(img, 1.0, Size(320, 240), Scalar(103.939, 116.779, 123.68), false, false); // Output image has values in range [-143.526, 148.539]. - float l1 = 1e-4, lInf = 2e-3; + float l1 = 2e-4, lInf = 2e-3; if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) { l1 = 0.4; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 14a19266cc..9796630fc9 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1221,6 +1221,7 @@ TEST_P(Layer_Test_DWconv_Prelu, Accuracy) Mat in_blob(4, &shape[0], CV_32FC1, Scalar(1)); net.setPreferableBackend(DNN_BACKEND_OPENCV); + net.enableWinograd(false); net.setInput(in_blob); Mat out = net.forward(); From d816442e4d4218b715e867a139fba25feecf6c32 Mon Sep 17 00:00:00 2001 From: zoom Date: Fri, 14 Oct 2022 16:46:25 +0800 Subject: [PATCH 163/313] Make Unsqueeze layer support negative axes. --- modules/dnn/src/onnx/onnx_importer.cpp | 6 ++++-- modules/dnn/test/test_onnx_importer.cpp | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 013520d13d..b324ad9c23 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2313,8 +2313,9 @@ void ONNXImporter::parseUnsqueeze(LayerParams& layerParams, const opencv_onnx::N } // CV_Assert(axes.getIntValue(axes.size()-1) <= dims.size()); for (int j = 0; j < axes.size(); j++) { - const int idx = axes.getIntValue(j); - CV_Assert(idx <= dims.size()); + int idx = axes.getIntValue(j); + idx = idx < 0 ? idx + input_dims + 1 : idx; + CV_Assert(0 <= idx && idx <= dims.size()); dims.insert(dims.begin() + idx, 1); } @@ -2331,6 +2332,7 @@ void ONNXImporter::parseUnsqueeze(LayerParams& layerParams, const opencv_onnx::N MatShape inpShape = outShapes[node_proto.input(0)]; int axis = axes.getIntValue(0); + axis = axis < 0 ? axis + (int)inpShape.size() + 1 : axis; CV_Assert(0 <= axis && axis <= inpShape.size()); std::vector outShape = inpShape; outShape.insert(outShape.begin() + axis, 1); diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 6372bcc659..9fe00787e0 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1096,6 +1096,11 @@ TEST_P(Test_ONNX_layers, Reshape) testONNXModels("unsqueeze_opset_13"); } +TEST_P(Test_ONNX_layers, Unsqueeze_Neg_Axes) +{ + testONNXModels("unsqueeze_neg_axes"); +} + TEST_P(Test_ONNX_layers, Squeeze) { if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD) From 292f62c5ccf84fa382a41d08a2d5c5554056c65a Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sun, 16 Oct 2022 04:01:00 +0300 Subject: [PATCH 164/313] cosmetic changes --- 3rdparty/libjpeg-turbo/CMakeLists.txt | 9 +++--- .../libjpeg-turbo/src/simd/CMakeLists.txt | 32 +++++++++---------- CMakeLists.txt | 8 ++--- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt index ea4f8fca5e..ac0aaf63e1 100644 --- a/3rdparty/libjpeg-turbo/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/CMakeLists.txt @@ -56,8 +56,6 @@ if(MSVC_IDE AND CMAKE_GENERATOR_PLATFORM MATCHES "arm64") set(CPU_TYPE arm64) endif() -message(STATUS "${BITS}-bit build (${CPU_TYPE})") - OCV_OPTION(ENABLE_LIBJPEG_TURBO_SIMD "Include SIMD extensions for libjpeg-turbo, if available for this platform" (NOT CV_DISABLE_OPTIMIZATION) VISIBLE_IF BUILD_JPEG) option(WITH_ARITH_ENC "Include arithmetic encoding support when emulating the libjpeg v6b API/ABI" TRUE) @@ -185,9 +183,10 @@ if(WITH_SIMD) set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1) endif() else() - add_library(simd OBJECT src/jsimd_none.c) + add_library(jsimd OBJECT src/jsimd_none.c) + set_target_properties(jsimd PROPERTIES FOLDER "3rdparty") if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() endif() @@ -195,7 +194,7 @@ ocv_list_add_prefix(JPEG_SOURCES src/) set(JPEG_SOURCES ${JPEG_SOURCES} ${SIMD_OBJS}) -add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${JPEG_SOURCES} $ ${SIMD_OBJS}) +add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${JPEG_SOURCES} $ ${SIMD_OBJS}) set_target_properties(${JPEG_LIBRARY} PROPERTIES OUTPUT_NAME ${JPEG_LIBRARY} diff --git a/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt b/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt index db85d9f443..ec76491d5f 100644 --- a/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/src/simd/CMakeLists.txt @@ -75,8 +75,6 @@ elseif(CPU_TYPE STREQUAL "i386") endif() endif() -message(STATUS "CMAKE_ASM_NASM_OBJECT_FORMAT = ${CMAKE_ASM_NASM_OBJECT_FORMAT}") - if(NOT CMAKE_ASM_NASM_OBJECT_FORMAT) simd_fail("SIMD extensions disabled: could not determine NASM object format") return() @@ -102,7 +100,6 @@ endif() string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC) set(EFFECTIVE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} ${CMAKE_ASM_NASM_FLAGS_${CMAKE_BUILD_TYPE_UC}}") -message(STATUS "CMAKE_ASM_NASM_FLAGS = ${EFFECTIVE_ASM_NASM_FLAGS}") set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -I\"${CMAKE_CURRENT_SOURCE_DIR}/nasm/\" -I\"${CMAKE_CURRENT_SOURCE_DIR}/${CPU_TYPE}/\"") @@ -116,6 +113,7 @@ add_custom_target(jsimdcfg COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/nasm/jsimdcfg.inc.h | ${GREP} -E '^[\;%]|^\ %' | sed 's%_cpp_protection_%%' | sed 's@% define@%define@g' >${CMAKE_CURRENT_SOURCE_DIR}/nasm/jsimdcfg.inc) +set_target_properties(jsimdcfg PROPERTIES FOLDER "3rdparty") if(CPU_TYPE STREQUAL "x86_64") set(SIMD_SOURCES x86_64/jsimdcpu.asm x86_64/jfdctflt-sse.asm @@ -200,14 +198,16 @@ endforeach() if(MSVC_IDE OR XCODE) set(SIMD_OBJS ${SIMD_OBJS} PARENT_SCOPE) - add_library(simd OBJECT ${CPU_TYPE}/jsimd.c) - add_custom_target(simd-objs DEPENDS ${SIMD_OBJS}) - add_dependencies(simd simd-objs) + add_library(jsimd OBJECT ${CPU_TYPE}/jsimd.c) + add_custom_target(jsimd-objs DEPENDS ${SIMD_OBJS}) + add_dependencies(jsimd jsimd-objs) + set_target_properties(jsimd PROPERTIES FOLDER "3rdparty") + set_target_properties(jsimd-objs PROPERTIES FOLDER "3rdparty") else() - add_library(simd OBJECT ${SIMD_SOURCES} ${CPU_TYPE}/jsimd.c) + add_library(jsimd OBJECT ${SIMD_SOURCES} ${CPU_TYPE}/jsimd.c) endif() if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() @@ -397,10 +397,10 @@ if(NOT NEON_INTRINSICS) set(SIMD_SOURCES ${SIMD_SOURCES} arm/aarch${BITS}/jsimd_neon.S) endif() -add_library(simd OBJECT ${SIMD_SOURCES} arm/aarch${BITS}/jsimd.c) +add_library(jsimd OBJECT ${SIMD_SOURCES} arm/aarch${BITS}/jsimd.c) if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() @@ -439,10 +439,10 @@ if(NOT HAVE_DSPR2) return() endif() -add_library(simd OBJECT mips/jsimd_dspr2.S mips/jsimd.c) +add_library(jsimd OBJECT mips/jsimd_dspr2.S mips/jsimd.c) if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() ############################################################################### @@ -487,10 +487,10 @@ foreach(file ${SIMD_SOURCES}) " -Wa,-mloongson-mmi,-mloongson-ext") endforeach() -add_library(simd OBJECT ${SIMD_SOURCES} mips64/jsimd.c) +add_library(jsimd OBJECT ${SIMD_SOURCES} mips64/jsimd.c) if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() ############################################################################### @@ -527,10 +527,10 @@ set(SIMD_SOURCES powerpc/jccolor-altivec.c powerpc/jcgray-altivec.c set_source_files_properties(${SIMD_SOURCES} PROPERTIES COMPILE_FLAGS -maltivec) -add_library(simd OBJECT ${SIMD_SOURCES} powerpc/jsimd.c) +add_library(jsimd OBJECT ${SIMD_SOURCES} powerpc/jsimd.c) if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) + set_target_properties(jsimd PROPERTIES POSITION_INDEPENDENT_CODE 1) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index f24f819042..dbbb10ac9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1347,14 +1347,14 @@ if(WITH_JPEG OR HAVE_JPEG) elseif(BUILD_JPEG) status(" JPEG:" "build-${JPEG_LIBRARY} (ver ${JPEG_LIB_VERSION})") if(ENABLE_LIBJPEG_TURBO_SIMD) - status(" SIMD Support Request:" "YES") + status(" SIMD Support Request:" "YES") if(HAVE_LIBJPEG_TURBO_SIMD) - status(" SIMD Support:" "YES") + status(" SIMD Support:" "YES") else() - status(" SIMD Support:" "NO") + status(" SIMD Support:" "NO") endif() else() - status(" SIMD Support Request:" "NO") + status(" SIMD Support Request:" "NO") endif() else() status(" JPEG:" "${JPEG_LIBRARY} (ver ${JPEG_LIB_VERSION})") From d24d8f2abe520d50d6345a06bc6dc63e3903f6a1 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Sun, 18 Sep 2022 22:13:55 +0800 Subject: [PATCH 165/313] implementation of scatter and scatternd with conformance tests enabled --- .../dnn/include/opencv2/dnn/all_layers.hpp | 12 + modules/dnn/perf/perf_layer.cpp | 171 ++++++++++++++ modules/dnn/src/init.cpp | 3 + modules/dnn/src/layers/scatterND_layer.cpp | 202 +++++++++++++++++ modules/dnn/src/layers/scatter_layer.cpp | 208 ++++++++++++++++++ modules/dnn/src/onnx/onnx_importer.cpp | 54 +++++ modules/dnn/test/test_onnx_conformance.cpp | 4 + ...rmance_layer_filter__cuda_denylist.inl.hpp | 13 ++ ...ance_layer_filter__halide_denylist.inl.hpp | 13 ++ ...conformance_layer_filter__openvino.inl.hpp | 8 + ...ance_layer_filter__vulkan_denylist.inl.hpp | 13 ++ ...er_filter_opencv_ocl_fp16_denylist.inl.hpp | 15 +- ...er_filter_opencv_ocl_fp32_denylist.inl.hpp | 13 ++ ..._conformance_layer_parser_denylist.inl.hpp | 9 - 14 files changed, 728 insertions(+), 10 deletions(-) create mode 100644 modules/dnn/src/layers/scatterND_layer.cpp create mode 100644 modules/dnn/src/layers/scatter_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index f87a46ba5e..a0711b868c 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -1065,6 +1065,18 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS ScatterLayer : public Layer + { + public: + static Ptr create(const LayerParams& params); + }; + + class CV_EXPORTS ScatterNDLayer : public Layer + { + public: + static Ptr create(const LayerParams& params); + }; + //! @} //! @} CV__DNN_INLINE_NS_END diff --git a/modules/dnn/perf/perf_layer.cpp b/modules/dnn/perf/perf_layer.cpp index 03ba8ab0e9..f169f4e6a8 100644 --- a/modules/dnn/perf/perf_layer.cpp +++ b/modules/dnn/perf/perf_layer.cpp @@ -239,7 +239,178 @@ PERF_TEST_P_(Layer_Slice, FastNeuralStyle_eccv16) test_slice<4>(inputShape, begin, end); } +struct Layer_Scatter : public TestBaseWithParam > +{ + void test_layer(const std::vector& shape, const String reduction = "none", int axis = 0) + { + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + Mat data(shape, CV_32FC1); + Mat indices(shape, CV_32FC1); + Mat updates(shape, CV_32FC1); + + Scalar mean = 0.f; + Scalar std = 1.f; + randn(data, mean, std); + randu(indices, 0, shape[axis]); + randn(updates, mean, std); + + indices.convertTo(indices, CV_32SC1, 1, -1); + + Net net; + LayerParams lp; + lp.type = "Scatter"; + lp.name = "testLayer"; + lp.set("reduction", reduction); + lp.set("axis", axis); + + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 0); + net.connect(0, 1, id, 1); + net.connect(0, 2, id, 2); + + // warmup + { + std::vector inpNames(3); + inpNames[0] = "data"; + inpNames[1] = "indices"; + inpNames[2] = "updates"; + net.setInputsNames(inpNames); + net.setInput(data, inpNames[0]); + net.setInput(indices, inpNames[1]); + net.setInput(updates, inpNames[2]); + + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); + } + + int N = 8; + int C = 256; + int H = 128; + int W = 100; +}; + +PERF_TEST_P_(Layer_Scatter, DISABLED_Scatter) +{ + test_layer({N, C, H, W}); +} + +PERF_TEST_P_(Layer_Scatter, DISABLED_Scatter_add) +{ + test_layer({N, C, H, W}, "add"); +} + +struct Layer_ScatterND : public TestBaseWithParam > +{ + void test_layer(const std::vector& shape, const String reduction = "none") + { + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + std::vector indices_shape(shape); + indices_shape.push_back(int(shape.size())); + Mat data(shape, CV_32FC1); + Mat indices(indices_shape, CV_32FC1); + Mat updates(shape, CV_32FC1); + + Scalar mean = 0.f; + Scalar std = 1.f; + randn(data, mean, std); + randn(updates, mean, std); + + // initialize the indices with index tuples like [0...N, 0...C, 0...H, 0...W] + std::vector current_index_tuple(shape.size()); + int total = data.total(); + std::vector indices_step; + for (int i = 0; i < indices.dims; i++) + { + int step = indices.step.p[i] / sizeof(float); + indices_step.push_back(step); + } + int t, j, idx, offset_at_idx, offset; + for (int i = 0; i < total; i++) + { + t = i; + for (j = shape.size() - 1; j >= 0; j--) + { + idx = t / shape[j]; + offset_at_idx = (int)(t - idx * shape[j]); + current_index_tuple[j] = offset_at_idx; + t = idx; + } + + offset = 0; + for (j = 0; j < shape.size(); j++) + offset += current_index_tuple[j] * indices_step[j]; + + for (j = 0; j < shape.size(); j++) + indices.at(offset + j) = current_index_tuple[j]; + } + + Net net; + LayerParams lp; + lp.type = "ScatterND"; + lp.name = "testLayer"; + lp.set("reduction", reduction); + + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 0); + net.connect(0, 1, id, 1); + net.connect(0, 2, id, 2); + + // warmup + { + std::vector inpNames(3); + inpNames[0] = "data"; + inpNames[1] = "indices"; + inpNames[2] = "updates"; + net.setInputsNames(inpNames); + net.setInput(data, inpNames[0]); + net.setInput(indices, inpNames[1]); + net.setInput(updates, inpNames[2]); + + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); + } + + int N = 8; + int C = 256; + int H = 128; + int W = 100; +}; + +PERF_TEST_P_(Layer_ScatterND, DISABLED_ScatterND) +{ + test_layer({N, C, H ,W}); +} + +PERF_TEST_P_(Layer_ScatterND, DISABLED_ScatterND_add) +{ + test_layer({N, C, H , W}, "add"); +} + INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(false, false)); INSTANTIATE_TEST_CASE_P(/**/, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); +INSTANTIATE_TEST_CASE_P(/**/, Layer_Scatter, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); +INSTANTIATE_TEST_CASE_P(/**/, Layer_ScatterND, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); } // namespace diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index 902b6dae88..63bbf2cb3f 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -175,6 +175,9 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(GRU, GRULayer); CV_DNN_REGISTER_LAYER_CLASS(CumSum, CumSumLayer); + CV_DNN_REGISTER_LAYER_CLASS(Scatter, ScatterLayer); + CV_DNN_REGISTER_LAYER_CLASS(ScatterND, ScatterNDLayer); + CV_DNN_REGISTER_LAYER_CLASS(Quantize, QuantizeLayer); CV_DNN_REGISTER_LAYER_CLASS(Dequantize, DequantizeLayer); CV_DNN_REGISTER_LAYER_CLASS(Requantize, RequantizeLayer); diff --git a/modules/dnn/src/layers/scatterND_layer.cpp b/modules/dnn/src/layers/scatterND_layer.cpp new file mode 100644 index 0000000000..648d35fc0c --- /dev/null +++ b/modules/dnn/src/layers/scatterND_layer.cpp @@ -0,0 +1,202 @@ +// 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" +#include "layers_common.hpp" + +#include // for std::max & std::min + +namespace cv { namespace dnn { + +class ScatterNDLayerImpl CV_FINAL : public ScatterNDLayer +{ +public: + enum class REDUCTION + { + NONE = 1, + ADD, + MUL, + MAX, + MIN + } reduction; + + ScatterNDLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + + String reduction_name = toLowerCase(params.get("reduction", "none")); + if (reduction_name == "none") + reduction = REDUCTION::NONE; + else if (reduction_name == "add") + reduction = REDUCTION::ADD; + else if (reduction_name == "mul") + reduction = REDUCTION::MUL; + else if (reduction_name == "max") + reduction = REDUCTION::MAX; + else if (reduction_name == "min") + reduction = REDUCTION::MIN; + else + CV_Error(cv::Error::StsBadArg, "Unkown reduction \"" + reduction_name + "\""); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE + { + CV_CheckEQ(inputs.size(), 3ull, "ScatterND: require three inputs."); + + size_t r = inputs[0].size(), q = inputs[1].size(), p = inputs[2].size(), k = inputs[1].back(); + CV_CheckEQ(r + q - inputs[1].back() - 1, p, "ScatterND: updates should have rank of data.dims + indices.dims - indices.size[-1] - 1"); + CV_CheckLE(k, r, "ScatterND: indices.shape[-1] must be less than (or equal to) the rank of input data."); + + for (int i = 0; i < q - 1; i++) // np.ndindex(indices.shape[-1]) + { + CV_CheckEQ(inputs[2][i], inputs[1][i], "ScatterND: updates.shape[0 : rank(indices)-1] must equal to indices.shape[0 : rank(indices)-1]."); + } + for (int i = q - 1, j = k, m = 0; i + m < p; m++) + { + CV_CheckEQ(inputs[2][i + m], inputs[0][j + m], "ScatterND: updates.shape[rank(indices)-1 : ] must equal to data[indices.shape[-1] : rank(data)-1]."); + } + + outputs.assign(1, inputs[0]); + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const Mat& data = inputs[0]; + const Mat& indices = inputs[1]; + const Mat& updates = inputs[2]; + Mat& out = outputs[0]; + + typeDispatch(outputs[0].type(), data, indices, updates, out); + } + + // NOTE: This impl does not check whether indices have duplicate entries. + // The last duplicate entry will overwrite the previous. + template + void forward_impl(const Functor& rd, const Mat& data, const Mat& indices, const Mat& updates, Mat& out) + { + data.copyTo(out); + + const int* shape = data.size.p; + const size_t* step = data.step.p; + + const int ind_ndims = indices.dims; + const int* ind_shape = indices.size.p; + const T* p_indices = indices.ptr(); + + const int upd_ndims = updates.dims; + const int* upd_shape = updates.size.p; + const T* p_updates = updates.ptr(); + + T* p_out = out.ptr(); + + int k = ind_shape[ind_ndims - 1]; // last dim of indices + size_t total = (size_t)(indices.total() / k); + + size_t updates_size = 1; + for (int i = ind_ndims - 1; i < upd_ndims; i++) + updates_size *= upd_shape[i]; + + size_t inp_start_offset = 0; + size_t ind_start_offset = 0; + size_t upd_start_offset = 0; + for (size_t i = 0; i < total; i++, ind_start_offset += k, upd_start_offset += updates_size) + { + const T* tmp_p_indices = p_indices + ind_start_offset; + inp_start_offset = 0; + for (int j = 0; j < k; j++) + { + CV_Assert(tmp_p_indices[j] < shape[j] && tmp_p_indices[j] > -shape[j]); + inp_start_offset += (((int)tmp_p_indices[j] + shape[j]) % shape[j]) * step[j]; + } + inp_start_offset /= sizeof(T); + + const T* tmp_p_updates = p_updates + upd_start_offset; + T* tmp_p_out = p_out + inp_start_offset; + for (int j = 0; j < updates_size; j++) + tmp_p_out[j] = rd(tmp_p_out[j], tmp_p_updates[j]); + } + } + + template + inline void typeDispatch(const int type, Args&&... args) + { + switch (type) + { + case CV_8U: + reductionDispatch(std::forward(args)...); + break; + case CV_32S: + reductionDispatch(std::forward(args)...); + break; + case CV_32F: + reductionDispatch(std::forward(args)...); + break; + default: + CV_Error(cv::Error::BadDepth, "Unsupported type."); + }; + } + + template + inline void reductionDispatch(Args&&... args) + { + switch (reduction) + { + case REDUCTION::NONE: + { + auto rd = [](const T& a, const T& b) { return b; }; // a from input data, b from updates + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::ADD: + { + auto rd = [](const T& a, const T& b) { return a + b; }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MUL: + { + auto rd = [](const T& a, const T& b) { return a * b; }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MAX: + { + auto rd = [](const T& a, const T& b) { return std::max(a, b); }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MIN: + { + auto rd = [](const T& a, const T& b) { return std::min(a, b); }; + forward_impl(rd, std::forward(args)...); + break; + } + default: + CV_Error(Error::StsBadArg, "Unsupported reduction."); + }; + } +}; + +Ptr ScatterNDLayer::create(const LayerParams& params) +{ + return makePtr(params); +} + +}} // namespace cv::dnn diff --git a/modules/dnn/src/layers/scatter_layer.cpp b/modules/dnn/src/layers/scatter_layer.cpp new file mode 100644 index 0000000000..084eecb03c --- /dev/null +++ b/modules/dnn/src/layers/scatter_layer.cpp @@ -0,0 +1,208 @@ +// 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" +#include "layers_common.hpp" + +#include // for std::max & std::min + +namespace cv { namespace dnn { + +class ScatterLayerImpl CV_FINAL : public ScatterLayer +{ +public: + enum class REDUCTION + { + NONE = 1, + ADD, + MUL, + MAX, + MIN + } reduction; + + ScatterLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + + axis = params.get("axis", 0); + String reduction_name = toLowerCase(params.get("reduction", "none")); + if (reduction_name == "none") + reduction = REDUCTION::NONE; + else if (reduction_name == "add") + reduction = REDUCTION::ADD; + else if (reduction_name == "mul") + reduction = REDUCTION::MUL; + else if (reduction_name == "max") + reduction = REDUCTION::MAX; + else if (reduction_name == "min") + reduction = REDUCTION::MIN; + else + CV_Error(cv::Error::StsBadArg, "Unkown reduction \"" + reduction_name + "\""); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE + { + CV_CheckEQ(inputs.size(), 3ull, "Scatter: require three inputs."); + CV_CheckEQ(inputs[0].size(), inputs[1].size(), "Scatter: input data should have the same ndim with indices."); + CV_CheckEQ(inputs[0].size(), inputs[2].size(), "Scatter: input data should have the same ndim with updates."); + for (size_t i = 0; i < inputs[0].size(); i++) + { + CV_CheckGE(inputs[0][i], inputs[1][i], "Scatter: each dim of input data should be greater than (or equal to) indices'."); + CV_CheckEQ(inputs[1][i], inputs[2][i], "Scatter: each dim of indices should be equal to updates'."); + } + outputs.assign(1, inputs[0]); + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const Mat& data = inputs[0]; + const Mat& indices = inputs[1]; + const Mat& updates = inputs[2]; + Mat& out = outputs[0]; + + typeDispatch(outputs[0].type(), data, indices, updates, out); + } + + template + void forward_impl(const Functor& rd, const Mat& data, const Mat& indices, const Mat& updates, Mat& out) + { + data.copyTo(out); + + const int ndims = data.dims; + const int* shape = data.size.p; + const size_t* step = data.step.p; + + const int* ind_shape = indices.size.p; + const size_t* ind_step = indices.step.p; + + size_t inp_offset = 0; + size_t ind_offset = 0; + const T* p_index = indices.ptr(); + const T* p_update = updates.ptr(); + T* p_out = out.ptr(); + + size_t total = indices.total(); + + int j, offset_at_idx, index; + size_t t, idx; + for (size_t i = 0; i < total; i++) + { + t = i; + inp_offset = 0; + ind_offset = 0; + int offset_at_axis = 0; + for (j = ndims - 1; j >= 0; j--) + { + idx = t / ind_shape[j]; + offset_at_idx = (int)(t - idx * ind_shape[j]); + ind_offset += offset_at_idx * ind_step[j]; + inp_offset += offset_at_idx * step[j]; + t = idx; + if (j == axis) + { + offset_at_axis = offset_at_idx * step[j]; + } + } + ind_offset /= sizeof(T); + + // get index and overwrite current indices + const T* tmp_p_index = p_index + ind_offset; + index = (int)(*tmp_p_index); + CV_Assert(index < shape[axis] && index > -shape[axis]); + + inp_offset = inp_offset - offset_at_axis + ((index + shape[axis]) % shape[axis]) * step[axis]; + inp_offset /= sizeof(T); + + const T* tmp_p_update = p_update + ind_offset; + T* tmp_p_out = p_out + inp_offset; + *tmp_p_out = rd(*tmp_p_out, *tmp_p_update); + } + } + + template + inline void typeDispatch(const int type, Args&&... args) + { + switch (type) + { + case CV_8U: + reductionDispatch(std::forward(args)...); + break; + case CV_32S: + reductionDispatch(std::forward(args)...); + break; + case CV_32F: + reductionDispatch(std::forward(args)...); + break; + default: + CV_Error(cv::Error::BadDepth, "Unsupported type."); + }; + } + + template + inline void reductionDispatch(Args&&... args) + { + switch (reduction) + { + case REDUCTION::NONE: + { + auto rd = [](const T& a, const T& b) { return b; }; // a from input data, b from updates + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::ADD: + { + auto rd = [](const T& a, const T& b) { return a + b; }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MUL: + { + auto rd = [](const T& a, const T& b) { return a * b; }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MAX: + { + auto rd = [](const T& a, const T& b) { return std::max(a, b); }; + forward_impl(rd, std::forward(args)...); + break; + } + case REDUCTION::MIN: + { + auto rd = [](const T& a, const T& b) { return std::min(a, b); }; + forward_impl(rd, std::forward(args)...); + break; + } + default: + CV_Error(Error::StsBadArg, "Unsupported reduction."); + }; + } + +private: + // Attributes + int axis; +}; + +Ptr ScatterLayer::create(const LayerParams& params) +{ + return makePtr(params); +} + +}} // namespace cv::dnn diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 0b104d1e63..e91534e409 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -181,6 +181,7 @@ private: void parseElementWise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseDepthToSpace (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseSimpleLayers (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); // Domain: com.microsoft @@ -3106,6 +3107,58 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1))); } +void ONNXImporter::parseScatter(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + CV_CheckEQ(node_proto.input_size(), 3, "Scatter: three inputs are required."); + layerParams.type = "Scatter"; + if (node_proto.op_type() == "ScatterND") + layerParams.type = "ScatterND"; + + size_t consts = 0; + for (size_t i = 0; i < node_proto.input_size(); ++i) + if (layer_id.find(node_proto.input(i)) == layer_id.end()) + ++consts; + + if (consts == node_proto.input_size()) + { + std::vector inputs, output; + for (size_t i = 0; i < node_proto.input_size(); i++) + { + Mat blob = getBlob(node_proto, i); + if (i == 1) // indices + blob.convertTo(blob, CV_32F); + inputs.push_back(blob); + } + runLayer(layerParams, inputs, output); + CV_Assert(output.size() == 1); + addConstant(node_proto.output(0), output[0]); + return; + } + else if (consts > 0) + { + for (size_t i = 0; i < node_proto.input_size(); i++) + { + if (layer_id.find(node_proto.input(i)) == layer_id.end()) + { + Mat blob = getBlob(node_proto, i); + if (i == 1) // indices, from int32/int64 to float32 + blob.convertTo(blob, CV_32F); + + LayerParams constParams; + constParams.name = node_proto.input(i); + constParams.type = "Const"; + constParams.blobs.push_back(blob); + + opencv_onnx::NodeProto proto; + proto.add_output(constParams.name); + addLayer(constParams, proto); + } + } + } + + addLayer(layerParams, node_proto); +} + void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { bool is_all_input_const = true; @@ -3726,6 +3779,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["DetectionOutput"] = &ONNXImporter::parseDetectionOutput; dispatch["CumSum"] = &ONNXImporter::parseCumSum; dispatch["SpaceToDepth"] = dispatch["DepthToSpace"] = &ONNXImporter::parseDepthToSpace; + dispatch["ScatterElements"] = dispatch["Scatter"] = dispatch["ScatterND"] = &ONNXImporter::parseScatter; dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = dispatch["Pow"] = dispatch["Add"] = dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = &ONNXImporter::parseElementWise; diff --git a/modules/dnn/test/test_onnx_conformance.cpp b/modules/dnn/test/test_onnx_conformance.cpp index e9bc0e4187..fc766c2b81 100644 --- a/modules/dnn/test/test_onnx_conformance.cpp +++ b/modules/dnn/test/test_onnx_conformance.cpp @@ -666,11 +666,15 @@ static const TestCase testConformanceConfig[] = { {"test_scatter_elements_with_axis", 3, 1}, {"test_scatter_elements_with_duplicate_indices", 3, 1}, {"test_scatter_elements_with_negative_indices", 3, 1}, + {"test_scatter_elements_with_reduction_max", 3, 1}, + {"test_scatter_elements_with_reduction_min", 3, 1}, {"test_scatter_elements_without_axis", 3, 1}, {"test_scatter_with_axis", 3, 1}, {"test_scatter_without_axis", 3, 1}, {"test_scatternd", 3, 1}, {"test_scatternd_add", 3, 1}, + {"test_scatternd_max", 3, 1}, + {"test_scatternd_min", 3, 1}, {"test_scatternd_multiply", 3, 1}, {"test_sce_NCd1_mean_weight_negative_ii", 3, 1}, {"test_sce_NCd1_mean_weight_negative_ii_expanded", 3, 1}, diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp index c18ced0c59..4c05f10305 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp @@ -82,3 +82,16 @@ "test_sub_uint8", "test_tan", // FP16 only "test_upsample_nearest", +"test_scatter_elements_with_axis", +"test_scatter_elements_with_duplicate_indices", +"test_scatter_elements_with_negative_indices", +"test_scatter_elements_with_reduction_max", +"test_scatter_elements_with_reduction_min", +"test_scatter_elements_without_axis", +"test_scatter_with_axis", +"test_scatter_without_axis", +"test_scatternd", +"test_scatternd_add", +"test_scatternd_max", +"test_scatternd_min", +"test_scatternd_multiply", diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__halide_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__halide_denylist.inl.hpp index 72900a8194..4924aaf9da 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__halide_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__halide_denylist.inl.hpp @@ -95,3 +95,16 @@ "test_sub_uint8", "test_tanh", "test_upsample_nearest", +"test_scatter_elements_with_axis", +"test_scatter_elements_with_duplicate_indices", +"test_scatter_elements_with_negative_indices", +"test_scatter_elements_with_reduction_max", +"test_scatter_elements_with_reduction_min", +"test_scatter_elements_without_axis", +"test_scatter_with_axis", +"test_scatter_without_axis", +"test_scatternd", +"test_scatternd_add", +"test_scatternd_max", +"test_scatternd_min", +"test_scatternd_multiply", diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp index cad914d05a..e6a35dfab9 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp @@ -1588,6 +1588,10 @@ CASE(test_scatter_elements_with_duplicate_indices) // no filter CASE(test_scatter_elements_with_negative_indices) // no filter +CASE(test_scatter_elements_with_reduction_max) + // no filter +CASE(test_scatter_elements_with_reduction_min) + // no filter CASE(test_scatter_elements_without_axis) // no filter CASE(test_scatter_with_axis) @@ -1598,6 +1602,10 @@ CASE(test_scatternd) // no filter CASE(test_scatternd_add) // no filter +CASE(test_scatternd_max) + // no filter +CASE(test_scatternd_min) + // no filter CASE(test_scatternd_multiply) // no filter CASE(test_sce_NCd1_mean_weight_negative_ii) diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__vulkan_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__vulkan_denylist.inl.hpp index 101d44cbf0..8156686428 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__vulkan_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__vulkan_denylist.inl.hpp @@ -63,3 +63,16 @@ "test_sub_uint8", "test_transpose_all_permutations_0", "test_upsample_nearest", +"test_scatter_elements_with_axis", +"test_scatter_elements_with_duplicate_indices", +"test_scatter_elements_with_negative_indices", +"test_scatter_elements_with_reduction_max", +"test_scatter_elements_with_reduction_min", +"test_scatter_elements_without_axis", +"test_scatter_with_axis", +"test_scatter_without_axis", +"test_scatternd", +"test_scatternd_add", +"test_scatternd_max", +"test_scatternd_min", +"test_scatternd_multiply", diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp16_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp16_denylist.inl.hpp index c2425d469f..9b6b2414db 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp16_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp16_denylist.inl.hpp @@ -30,4 +30,17 @@ "test_reduce_sum_square_default_axes_keepdims_random", // Expected: (normL1) <= (l1), actual: 0.0183411 vs 0.004 "test_reduce_sum_square_do_not_keepdims_random", // Expected: (normL1) <= (l1), actual: 0.010789 vs 0.004, Expected: (normInf) <= (lInf), actual: 0.0290298 vs 0.02 "test_reduce_sum_square_keepdims_random", // Expected: (normL1) <= (l1), actual: 0.010789 vs 0.004, Expected: (normInf) <= (lInf), actual: 0.0290298 vs 0.02 -"test_reduce_sum_square_negative_axes_keepdims_random", // Expected: (normL1) <= (l1), actual: 0.010789 vs 0.004, Expected: (normInf) <= (lInf), actual: 0.0290298 vs 0.02 \ No newline at end of file +"test_reduce_sum_square_negative_axes_keepdims_random", // Expected: (normL1) <= (l1), actual: 0.010789 vs 0.004, Expected: (normInf) <= (lInf), actual: 0.0290298 vs 0.02 +"test_scatter_elements_with_axis", +"test_scatter_elements_with_duplicate_indices", +"test_scatter_elements_with_negative_indices", +"test_scatter_elements_with_reduction_max", +"test_scatter_elements_with_reduction_min", +"test_scatter_elements_without_axis", +"test_scatter_with_axis", +"test_scatter_without_axis", +"test_scatternd", +"test_scatternd_add", +"test_scatternd_max", +"test_scatternd_min", +"test_scatternd_multiply", diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp32_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp32_denylist.inl.hpp index 9a7a21f393..7fe58a07fd 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp32_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_ocl_fp32_denylist.inl.hpp @@ -1,2 +1,15 @@ "test_averagepool_3d_default", "test_maxpool_3d_default", +"test_scatter_elements_with_axis", +"test_scatter_elements_with_duplicate_indices", +"test_scatter_elements_with_negative_indices", +"test_scatter_elements_with_reduction_max", +"test_scatter_elements_with_reduction_min", +"test_scatter_elements_without_axis", +"test_scatter_with_axis", +"test_scatter_without_axis", +"test_scatternd", +"test_scatternd_add", +"test_scatternd_max", +"test_scatternd_min", +"test_scatternd_multiply", diff --git a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp index 1437e5475b..0630833b1f 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp @@ -384,15 +384,6 @@ "test_roialign_aligned_true", "test_scan9_sum", "test_scan_sum", -"test_scatter_elements_with_axis", -"test_scatter_elements_with_duplicate_indices", -"test_scatter_elements_with_negative_indices", -"test_scatter_elements_without_axis", -"test_scatter_with_axis", -"test_scatter_without_axis", -"test_scatternd", -"test_scatternd_add", -"test_scatternd_multiply", "test_sce_NCd1_mean_weight_negative_ii", "test_sce_NCd1_mean_weight_negative_ii_expanded", "test_sce_NCd1_mean_weight_negative_ii_log_prob", From 871bc989333a853de67adeaf686729dca10a6107 Mon Sep 17 00:00:00 2001 From: Andrey Senyaev Date: Tue, 18 Oct 2022 08:55:10 +0300 Subject: [PATCH 166/313] Trigger on dnn (onnx) label --- .github/workflows/PR-4.x.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index a8514f6577..2664968448 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -13,7 +13,7 @@ jobs: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20.yaml@main Ubuntu2004-x64-CUDA: - if: "${{ contains(github.event.pull_request.labels.*.name, 'category: dnn') }}" + if: "${{ contains(github.event.pull_request.labels.*.name, 'category: dnn') }} || ${{ contains(github.event.pull_request.labels.*.name, 'category: dnn (onnx)') }}" uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20-Cuda.yaml@main Windows10-x64: From 6eb34716b81596d2a6ae69cd18a70c4507cbc293 Mon Sep 17 00:00:00 2001 From: Hashem Zavvari Date: Tue, 18 Oct 2022 11:43:08 +0330 Subject: [PATCH 167/313] Merge pull request #22635 from hzawary:4.x Setting CAP_PROP_AUTO_EXPOSURE on VideoCapture with backend DSHOW does not change anything. Now with this implementation the property can be used with value 1 for availability. --- modules/videoio/src/cap_dshow.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/videoio/src/cap_dshow.cpp b/modules/videoio/src/cap_dshow.cpp index db42601dab..3d7013d48f 100644 --- a/modules/videoio/src/cap_dshow.cpp +++ b/modules/videoio/src/cap_dshow.cpp @@ -3474,6 +3474,18 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal) return g_VI.isDeviceSetup(m_index); } + case CV_CAP_PROP_AUTO_EXPOSURE: + { + // Flags are required to toggle auto exposure or not, but the setProperty interface does not support multiple parameters + bool enabled = cvRound(propVal) == 1; + long minExposure, maxExposure, delta, currentExposure, flags, defaultValue; + if (!g_VI.getVideoSettingCamera(m_index, CameraControl_Exposure, minExposure, maxExposure, delta, currentExposure, flags, defaultValue)) + { + return false; + } + return g_VI.setVideoSettingCamera(m_index, CameraControl_Exposure, currentExposure, enabled ? CameraControl_Flags_Auto | CameraControl_Flags_Manual : CameraControl_Flags_Manual, enabled ? true : false); + } + case CV_CAP_PROP_AUTOFOCUS: { // Flags are required to toggle autofocus or not, but the setProperty interface does not support multiple parameters From dd14cf6a9cef34cd8092ce546f933d4f0d2c7c17 Mon Sep 17 00:00:00 2001 From: Smirnov Egor Date: Mon, 17 Oct 2022 23:00:12 +0300 Subject: [PATCH 168/313] address CUDA-related errors and enable cuda in elementwise ops --- modules/dnn/src/layers/elementwise_layers.cpp | 12 +++---- modules/dnn/src/onnx/onnx_importer.cpp | 2 ++ modules/dnn/test/test_onnx_importer.cpp | 34 ++++++++++++++++--- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 353ce8c0b4..01c369e5fd 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -1108,7 +1108,7 @@ struct CeilFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const @@ -1143,7 +1143,7 @@ struct FloorFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const @@ -1178,7 +1178,7 @@ struct LogFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const @@ -1213,7 +1213,7 @@ struct RoundFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const @@ -1253,7 +1253,7 @@ struct SqrtFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const @@ -1295,7 +1295,7 @@ struct NotFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; } inline float calculate(float x) const diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 013520d13d..4a81d28fe8 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2971,6 +2971,8 @@ void ONNXImporter::parseElementWise(LayerParams& layerParams, const opencv_onnx: LayerParams constParams; constParams.name = node_proto.input(i); constParams.type = "Const"; + // Non-constant propagated layers cannot output 1-d or 0-d tensors. + inp.dims = std::max(inp.dims, 2); constParams.blobs.push_back(inp); opencv_onnx::NodeProto proto; diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 6372bcc659..380255d28d 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -221,11 +221,21 @@ TEST_P(Test_ONNX_layers, GatherMulti) TEST_P(Test_ONNX_layers, Convolution3D) { + if (backend == DNN_BACKEND_CUDA && target == DNN_TARGET_CUDA_FP16) + { + // CUDA_FP16: cuDNN did not return a suitable algorithm for convolution. + applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA_FP16); + } testONNXModels("conv3d"); } TEST_P(Test_ONNX_layers, Convolution3D_bias) { + if (backend == DNN_BACKEND_CUDA && target == DNN_TARGET_CUDA_FP16) + { + // CUDA_FP16: cuDNN did not return a suitable algorithm for convolution. + applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA_FP16); + } testONNXModels("conv3d_bias"); } @@ -868,6 +878,12 @@ TEST_P(Test_ONNX_layers, PoolConv3D) if (backend == DNN_BACKEND_VKCOM) applyTestTag(CV_TEST_TAG_DNN_SKIP_VULKAN); + if (backend == DNN_BACKEND_CUDA && target == DNN_TARGET_CUDA_FP16) + { + // CUDA_FP16: cuDNN did not return a suitable algorithm for convolution. + applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA_FP16); + } + testONNXModels("pool_conv_3d"); } @@ -1073,10 +1089,9 @@ TEST_P(Test_ONNX_layers, Div) Mat out = net.forward(); normAssert(ref, out, "", default_l1, default_lInf); - expectNoFallbacksFromIE(net); - expectNoFallbacksFromCUDA(net); - testONNXModels("div_test_1x1",npy, 0, 0, false, true, 2); + // NaryEltwise layer suuports only CPU for now + testONNXModels("div_test_1x1", npy, 0, 0, false, false, 2); } TEST_P(Test_ONNX_layers, DynamicReshape) @@ -1122,10 +1137,19 @@ TEST_P(Test_ONNX_layers, Split) testONNXModels("split_2"); testONNXModels("split_3"); testONNXModels("split_4"); - testONNXModels("split_sizes"); testONNXModels("split_neg_axis"); } +// Mul inside with 0-d tensor, output should be A x 1, but is 1 x A. PR #22652 +TEST_P(Test_ONNX_layers, DISABLED_Split_sizes_0d) +{ + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); + testONNXModels("split_sizes"); +} + TEST_P(Test_ONNX_layers, Slice) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2019010000) @@ -2179,7 +2203,7 @@ TEST_P(Test_ONNX_nets, LResNet100E_IR) } else if (target == DNN_TARGET_CUDA_FP16) { - l1 = 0.008; + l1 = 0.009; lInf = 0.04; } testONNXModels("LResNet100E_IR", pb, l1, lInf); From ba575fd4ad182b0d099ad141da170cc589269a35 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Tue, 18 Oct 2022 23:25:41 +0300 Subject: [PATCH 169/313] reduce extra adaptiveThreshold() --- modules/objdetect/src/qrcode.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index f3e48f6ee0..23cd4f3ad4 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -2273,10 +2273,8 @@ bool QRDecode::updatePerspective() pts.push_back(centerPt); Mat H = findHomography(pts, perspective_points); - Mat bin_original; - adaptiveThreshold(original, bin_original, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2); Mat temp_intermediate; - warpPerspective(bin_original, temp_intermediate, H, temporary_size, INTER_NEAREST); + warpPerspective(bin_barcode, temp_intermediate, H, temporary_size, INTER_NEAREST); no_border_intermediate = temp_intermediate(Range(1, temp_intermediate.rows), Range(1, temp_intermediate.cols)); const int border = cvRound(0.1 * test_perspective_size); From c7d24c0fb3849149b969708f7dab14615ac8b7da Mon Sep 17 00:00:00 2001 From: catree Date: Wed, 19 Oct 2022 13:55:33 +0200 Subject: [PATCH 170/313] Fix AKAZE bib reference using dblp.org and bmva.org sources. --- doc/opencv.bib | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/opencv.bib b/doc/opencv.bib index dddb957995..6b5fedb49e 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -8,14 +8,18 @@ url = {https://www.doc.ic.ac.uk/~ajd/Publications/alcantarilla_etal_eccv2012.pdf} } @article{ANB13, - author = {Alcantarilla, Pablo F and Nuevo, Jes{\'u}s and Bartoli, Adrien}, + author = {Pablo Fern{\'{a}}ndez Alcantarilla and Jes{\'{u}}s Nuevo and Adrien Bartoli}, + editor = {Tilo Burghardt and Dima Damen and Walterio W. Mayol{-}Cuevas and Majid Mirmehdi}, title = {Fast Explicit Diffusion for Accelerated Features in Nonlinear Scale Spaces}, - year = {2011}, - pages = {1281--1298}, - journal = {Trans. Pattern Anal. Machine Intell}, - volume = {34}, - number = {7}, - url = {http://www.bmva.org/bmvc/2013/Papers/paper0013/paper0013.pdf} + booktitle = {British Machine Vision Conference, {BMVC} 2013, Bristol, UK, September 9-13, 2013}, + pages = {13.1--13.11}, + publisher = {{BMVA} Press}, + year = {2013}, + url = {https://doi.org/10.5244/C.27.13}, + doi = {10.5244/C.27.13}, + timestamp = {Sat, 09 Apr 2022 12:44:13 +0200}, + biburl = {https://dblp.org/rec/conf/bmvc/AlcantarillaNB13.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} } @inproceedings{Andreff99, author = {Andreff, Nicolas and Horaud, Radu and Espiau, Bernard}, From 5a9fe9dedb67fa41531980d3f143766e6396fb58 Mon Sep 17 00:00:00 2001 From: catree Date: Wed, 19 Oct 2022 14:11:01 +0200 Subject: [PATCH 171/313] Replace the chessboard.png image with the pattern.png image since the chessboard.png image does not have proper squares which is not good for camera calibration accuracy. --- samples/data/chessboard.png | Bin 62550 -> 27835 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/samples/data/chessboard.png b/samples/data/chessboard.png index 39bb399e89ffe43397125f457fad252fcc746ffc..1f9112c3a45c8191c969c20649aafb528803a6f6 100644 GIT binary patch literal 27835 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 VJu)40-bwyj{OGA85r@t@{tp6o+Lr(T literal 62550 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 Qm&9J+yLj%w*}>`m2JU)0G5`Po From c0a84dcc859536df55fd73dad82b7841a19cadf3 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 20 Oct 2022 14:11:02 +0300 Subject: [PATCH 172/313] Merge pull request #22651 from mshabunin:script-doc ts: basic documentation for utility scripts --- modules/ts/misc/chart.py | 44 +++++++++++++++++++++++++++- modules/ts/misc/color.py | 3 +- modules/ts/misc/concatlogs.py | 5 ++++ modules/ts/misc/perf_tests_timing.py | 24 ++++++++++++++- modules/ts/misc/report.py | 34 +++++++++++++++++++++ modules/ts/misc/run.py | 37 +++++++++++++++++++++++ modules/ts/misc/run_android.py | 3 ++ modules/ts/misc/run_long.py | 3 ++ modules/ts/misc/run_suite.py | 3 ++ modules/ts/misc/run_utils.py | 3 ++ modules/ts/misc/summary.py | 32 ++++++++++++++++++++ modules/ts/misc/table_formatter.py | 4 +++ modules/ts/misc/testlog_parser.py | 3 ++ modules/ts/misc/trace_profiler.py | 28 ++++++++++++++++++ 14 files changed, 223 insertions(+), 3 deletions(-) mode change 100755 => 100644 modules/ts/misc/color.py mode change 100644 => 100755 modules/ts/misc/perf_tests_timing.py mode change 100755 => 100644 modules/ts/misc/table_formatter.py mode change 100755 => 100644 modules/ts/misc/testlog_parser.py mode change 100644 => 100755 modules/ts/misc/trace_profiler.py diff --git a/modules/ts/misc/chart.py b/modules/ts/misc/chart.py index 6ae726abeb..e2b79ed78e 100755 --- a/modules/ts/misc/chart.py +++ b/modules/ts/misc/chart.py @@ -1,4 +1,46 @@ #!/usr/bin/env python +""" OpenCV performance test results charts generator. + +This script formats results of a performance test as a table or a series of tables according to test +parameters. + +### Description + +Performance data is stored in the GTest log file created by performance tests. Default name is +`test_details.xml`. It can be changed with the `--gtest_output=xml:/.xml` test +option. See https://github.com/opencv/opencv/wiki/HowToUsePerfTests for more details. + +Script accepts an XML with performance test results as an input. Only one test (aka testsuite) +containing multiple cases (aka testcase) with different parameters can be used. Test should have 2 +or more parameters, for example resolution (640x480), data type (8UC1), mode (NORM_TYPE), etc. +Parameters #2 and #1 will be used as table row and column by default, this mapping can be changed +with `-x` and `-y` options. Parameter combination besides the two selected for row and column will +be represented as a separate table. I.e. one table (RES x TYPE) for `NORM_L1`, another for +`NORM_L2`, etc. + +Test can be selected either by using `--gtest_filter` option when running the test, or by using the +`--filter` script option. + +### Options: + +-f REGEX, --filter=REGEX - regular expression used to select a test +-x ROW, -y COL - choose different parameters for rows and columns +-u UNITS, --units=UNITS - units for output values (s, ms (default), us, ns or ticks) +-m NAME, --metric=NAME - output metric (mean, median, stddev, etc.) +-o FMT, --output=FMT - output format ('txt', 'html' or 'auto') + +### Example: + +./chart.py -f sum opencv_perf_core.xml + +Geometric mean for +sum::Size_MatType::(Y, X) + + X\Y 127x61 640x480 1280x720 1920x1080 +8UC1 0.03 ms 1.21 ms 3.61 ms 8.11 ms +8UC4 0.10 ms 3.56 ms 10.67 ms 23.90 ms +32FC1 0.05 ms 1.77 ms 5.23 ms 11.72 ms +""" from __future__ import print_function import testlog_parser, sys, os, xml, re @@ -180,7 +222,7 @@ if __name__ == "__main__": exit(1) for i in range(argsnum): - arglists[i] = sorted([str(key) for key in arglists[i].iterkeys()], key=alphanum_keyselector) + arglists[i] = sorted([str(key) for key in arglists[i].keys()], key=alphanum_keyselector) if options.generateHtml and options.format != "moinwiki": htmlPrintHeader(sys.stdout, "Report %s for %s" % (args[0], sname)) diff --git a/modules/ts/misc/color.py b/modules/ts/misc/color.py old mode 100755 new mode 100644 index 4492ed4798..41e799892d --- a/modules/ts/misc/color.py +++ b/modules/ts/misc/color.py @@ -1,5 +1,6 @@ #!/usr/bin/env python - +""" Utility package used by other test result formatting scripts. +""" import math, os, sys webcolors = { diff --git a/modules/ts/misc/concatlogs.py b/modules/ts/misc/concatlogs.py index afcb9cc89f..ce717ef1eb 100755 --- a/modules/ts/misc/concatlogs.py +++ b/modules/ts/misc/concatlogs.py @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" Combines multiple uniform HTML documents with tables into a single one. + +HTML header from the first document will be used in the output document. Largest +`...` part from each document will be joined together. +""" from optparse import OptionParser import glob, sys, os, re diff --git a/modules/ts/misc/perf_tests_timing.py b/modules/ts/misc/perf_tests_timing.py old mode 100644 new mode 100755 index e3e94f4ce5..af26572969 --- a/modules/ts/misc/perf_tests_timing.py +++ b/modules/ts/misc/perf_tests_timing.py @@ -1,4 +1,26 @@ #!/usr/bin/env python +""" Prints total execution time and number of total/failed tests. + +Performance data is stored in the GTest log file created by performance tests. Default name is +`test_details.xml`. It can be changed with the `--gtest_output=xml:/.xml` test +option. See https://github.com/opencv/opencv/wiki/HowToUsePerfTests for more details. + +This script uses XML test log to produce basic runtime statistics in a text or HTML table. + +### Example: + +./perf_tests_timing.py opencv_perf_core.xml + +Overall time: 222.71 min + +Module Testsuit Time (min) Num of tests Failed +opencv Gemm::OCL_GemmFixture 113.669 24 +opencv dft::Size_MatType_FlagsType_NzeroRows 21.127 180 +opencv Dft::OCL_DftFixture 11.153 144 +opencv convertTo::Size_DepthSrc_DepthDst_Channels_alpha 7.992 392 +opencv Normalize::OCL_NormalizeFixture 5.412 96 +... ... ... ... +""" from __future__ import print_function import testlog_parser, sys, os, xml, glob, re @@ -78,7 +100,7 @@ if __name__ == "__main__": suit_time = 0 suit_num = 0 fails_num = 0 - for name in sorted(test_cases.iterkeys(), key=alphanum_keyselector): + for name in sorted(test_cases.keys(), key=alphanum_keyselector): cases = test_cases[name] groupName = next(c for c in cases if c).shortName() diff --git a/modules/ts/misc/report.py b/modules/ts/misc/report.py index ec60e16a13..0300d4426d 100755 --- a/modules/ts/misc/report.py +++ b/modules/ts/misc/report.py @@ -1,4 +1,38 @@ #!/usr/bin/env python +""" Print performance test run statistics. + +Performance data is stored in the GTest log file created by performance tests. Default name is +`test_details.xml`. It can be changed with the `--gtest_output=xml:/.xml` test +option. See https://github.com/opencv/opencv/wiki/HowToUsePerfTests for more details. + +This script produces configurable performance report tables in text and HTML formats. It allows to +filter test cases by name and parameter string and select specific performance metrics columns. One +or multiple test results can be used for input. + +### Example + +./report.py -c min,mean,median -f '(LUT|Match).*640' opencv_perf_core.xml opencv_perf_features2d.xml + +opencv_perf_features2d.xml, opencv_perf_core.xml + + Name of Test Min Mean Median +KnnMatch::OCL_BruteForceMatcherFixture::(640x480, 32FC1) 1365.04 ms 1368.18 ms 1368.52 ms +LUT::OCL_LUTFixture::(640x480, 32FC1) 2.57 ms 2.62 ms 2.64 ms +LUT::OCL_LUTFixture::(640x480, 32FC4) 21.15 ms 21.25 ms 21.24 ms +LUT::OCL_LUTFixture::(640x480, 8UC1) 2.22 ms 2.28 ms 2.29 ms +LUT::OCL_LUTFixture::(640x480, 8UC4) 19.12 ms 19.24 ms 19.19 ms +LUT::SizePrm::640x480 2.22 ms 2.27 ms 2.29 ms +Match::OCL_BruteForceMatcherFixture::(640x480, 32FC1) 1364.15 ms 1367.73 ms 1365.45 ms +RadiusMatch::OCL_BruteForceMatcherFixture::(640x480, 32FC1) 1372.68 ms 1375.52 ms 1375.42 ms + +### Options + +-o FMT, --output=FMT - output results in text format (can be 'txt', 'html' or 'auto' - default) +-u UNITS, --units=UNITS - units for output values (s, ms (default), us, ns or ticks) +-c COLS, --columns=COLS - comma-separated list of columns to show +-f REGEX, --filter=REGEX - regex to filter tests +--show-all - also include empty and "notrun" lines +""" from __future__ import print_function import testlog_parser, sys, os, xml, re, glob diff --git a/modules/ts/misc/run.py b/modules/ts/misc/run.py index bb17aa884f..b03553feeb 100755 --- a/modules/ts/misc/run.py +++ b/modules/ts/misc/run.py @@ -1,4 +1,41 @@ #!/usr/bin/env python +""" Test runner and results collector for OpenCV + +This script abstracts execution procedure for OpenCV tests. Target scenario: running automated tests +in a continuous integration system. +See https://github.com/opencv/opencv/wiki/HowToUsePerfTests for more details. + +### Main features + +- Collect test executables, distinguish between accuracy and performance, main and contrib test sets +- Pass through common GTest and OpenCV test options and handle some of them internally +- Set up testing environment and handle some OpenCV-specific environment variables +- Test Java and Python bindings +- Test on remote android device +- Support valgrind, qemu wrapping and trace collection + +### Main options + +-t MODULES, --tests MODULES - Comma-separated list of modules to test (example: -t core,imgproc,java) +-b MODULES, --blacklist MODULES - Comma-separated list of modules to exclude from test (example: -b java) +-a, --accuracy - Look for accuracy tests instead of performance tests +--check - Shortcut for '--perf_min_samples=1 --perf_force_samples=1' +-w PATH, --cwd PATH - Working directory for tests (default is current) +-n, --dry_run - Do not run anything +-v, --verbose - Print more debug information + +### Example + +./run.py -a -t core --gtest_filter=*CopyTo* + +Run: /work/build-opencv/bin/opencv_test_core --gtest_filter=*CopyTo* --gtest_output=xml:core_20221017-195300.xml --gtest_color=yes +CTEST_FULL_OUTPUT +... +regular test output +... +[ PASSED ] 113 tests. +Collected: ['core_20221017-195300.xml'] +""" import os import argparse diff --git a/modules/ts/misc/run_android.py b/modules/ts/misc/run_android.py index 4aa2b0dd78..498819cb30 100644 --- a/modules/ts/misc/run_android.py +++ b/modules/ts/misc/run_android.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" Utility package for run.py +""" + import os import re import getpass diff --git a/modules/ts/misc/run_long.py b/modules/ts/misc/run_long.py index 8b3d1e71ca..43f8a874e4 100644 --- a/modules/ts/misc/run_long.py +++ b/modules/ts/misc/run_long.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" Utility package for run.py +""" + from __future__ import print_function import xml.etree.ElementTree as ET from glob import glob diff --git a/modules/ts/misc/run_suite.py b/modules/ts/misc/run_suite.py index 2f382238cd..f1812cdf2a 100644 --- a/modules/ts/misc/run_suite.py +++ b/modules/ts/misc/run_suite.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" Utility package for run.py +""" + import os import re import sys diff --git a/modules/ts/misc/run_utils.py b/modules/ts/misc/run_utils.py index 8c942ba9d1..aadc2b4eae 100644 --- a/modules/ts/misc/run_utils.py +++ b/modules/ts/misc/run_utils.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" Utility package for run.py +""" + import sys import os import platform diff --git a/modules/ts/misc/summary.py b/modules/ts/misc/summary.py index bfb98358d2..5874d37e72 100755 --- a/modules/ts/misc/summary.py +++ b/modules/ts/misc/summary.py @@ -1,4 +1,36 @@ #!/usr/bin/env python +""" Format performance test results and compare metrics between test runs + +Performance data is stored in the GTest log file created by performance tests. Default name is +`test_details.xml`. It can be changed with the `--gtest_output=xml:/.xml` test +option. See https://github.com/opencv/opencv/wiki/HowToUsePerfTests for more details. + +This script allows to compare performance data collected during separate test runs and present it in +a text, Markdown or HTML table. + +### Major options + +-o FMT, --output=FMT - output format ('txt', 'html', 'markdown', 'tabs' or 'auto') +-f REGEX, --filter=REGEX - regex to filter tests +-m NAME, --metric=NAME - output metric +-u UNITS, --units=UNITS - units for output values (s, ms (default), us, ns or ticks) + +### Example + +./summary.py -f LUT.*640 core1.xml core2.xml + +Geometric mean (ms) + + Name of Test core1 core2 core2 + vs + core1 + (x-factor) +LUT::OCL_LUTFixture::(640x480, 8UC1) 2.278 0.737 3.09 +LUT::OCL_LUTFixture::(640x480, 32FC1) 2.622 0.805 3.26 +LUT::OCL_LUTFixture::(640x480, 8UC4) 19.243 3.624 5.31 +LUT::OCL_LUTFixture::(640x480, 32FC4) 21.254 4.296 4.95 +LUT::SizePrm::640x480 2.268 0.687 3.30 +""" from __future__ import print_function import testlog_parser, sys, os, xml, glob, re diff --git a/modules/ts/misc/table_formatter.py b/modules/ts/misc/table_formatter.py old mode 100755 new mode 100644 index 96bafab72d..f88681b200 --- a/modules/ts/misc/table_formatter.py +++ b/modules/ts/misc/table_formatter.py @@ -1,4 +1,8 @@ #!/usr/bin/env python +""" Prints data in a table format. + +This module serves as utility for other scripts. +""" from __future__ import print_function import sys, re, os.path, stat, math diff --git a/modules/ts/misc/testlog_parser.py b/modules/ts/misc/testlog_parser.py old mode 100755 new mode 100644 index f52a051c8f..a148b31116 --- a/modules/ts/misc/testlog_parser.py +++ b/modules/ts/misc/testlog_parser.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +""" Parse XML test log file. +This module serves as utility for other scripts. +""" from __future__ import print_function import collections import re diff --git a/modules/ts/misc/trace_profiler.py b/modules/ts/misc/trace_profiler.py old mode 100644 new mode 100755 index 189bed2743..1e4d52e409 --- a/modules/ts/misc/trace_profiler.py +++ b/modules/ts/misc/trace_profiler.py @@ -1,3 +1,31 @@ +#!/usr/bin/env python +""" Parse OpenCV trace logs and present summarized statistics in a table + +To collect trace logs use OpenCV built with tracing support (enabled by default), set +`OPENCV_TRACE=1` environment variable and run your application. `OpenCVTrace.txt` file will be +created in the current folder. +See https://github.com/opencv/opencv/wiki/Profiling-OpenCV-Applications for more details. + +### Options + +./trace_profiler.py + + - usually OpenCVTrace.txt + - number of functions to show (depth) + +### Example + +./trace_profiler.py OpenCVTrace.txt 2 + + ID name count thr min ... + t-min ... + 1 main#test_main.cpp:6 1 1 88.484 ... + 200.210 ... + + 2 UMatBasicTests_copyTo#test_umat.cpp:176|main 40 1 0.125 ... + 0.173 ... +""" + from __future__ import print_function import os From cee8c86b6e340f32f670c7f9a98e7ef19d1ac4a4 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Fri, 21 Oct 2022 19:14:54 +0800 Subject: [PATCH 173/313] fixed bug at winograd of SIMD128 and more robust code. --- .../depthwise_convolution.cpp | 14 +++++-- .../fast_convolution.avx2.cpp | 8 ++-- .../fast_convolution/fast_convolution.cpp | 14 ++++++- .../fast_convolution/fast_convolution.hpp | 28 ++++++------- .../fast_convolution/winograd_3x3s1_f63.cpp | 39 ++++++++++++------- 5 files changed, 65 insertions(+), 38 deletions(-) diff --git a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp index 4e5adf25d1..a527523a32 100644 --- a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp @@ -20,6 +20,7 @@ static void depthWiseBlock(const float *inptr, float *outptr, const float *weigh int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3) { #if CV_SIMD128 + const int VEC_NLANES = 4; v_float32x4 vminval = v_setall_f32(minval), vmaxval = v_setall_f32(maxval); v_float32x4 w0 = v_setall_f32( @@ -110,7 +111,7 @@ static void depthWiseBlock(const float *inptr, float *outptr, const float *weigh { if (dy0 == 3) { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -186,7 +187,7 @@ static void depthWiseBlock(const float *inptr, float *outptr, const float *weigh } else { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -211,7 +212,7 @@ static void depthWiseBlock(const float *inptr, float *outptr, const float *weigh } else { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left, k = 0; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -314,7 +315,12 @@ void runDepthwise(InputArray _input, OutputArray _output, const Ptr& int pad_top = conv->pad_top, pad_bottom = conv->pad_bottom; int pad_left = conv->pad_left, pad_right = conv->pad_right; - int ksize = Hk * Wk, padded_ksize = ((ksize + FAST_VEC_NLANES - 1) / FAST_VEC_NLANES) * FAST_VEC_NLANES; + int VEC_NLANES = 4; +#if CV_TRY_AVX2 + if (conv->useAVX2) + VEC_NLANES = 8; +#endif + int ksize = Hk * Wk, padded_ksize = ((ksize + VEC_NLANES - 1) / VEC_NLANES) * VEC_NLANES; const float *inp = input.ptr(); float *out = output.ptr(); diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp index 604e45e628..aa10d40bee 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp @@ -78,6 +78,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3) { + const int VEC_NLANES = 8; __m256 vminval = _mm256_set1_ps(minval); __m256 vmaxval = _mm256_set1_ps(maxval); @@ -174,7 +175,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights { if (dy0 == 3) { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -250,7 +251,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights } else { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -276,7 +277,7 @@ void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights } else { - for (; x0 <= x1 - FAST_VEC_NLANES; x0 += FAST_VEC_NLANES) + for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) { int xi_ = x0 * stride_x - pad_left, k = 0; const float *inptr_xi = inptr + Wi * yi_ + xi_; @@ -701,7 +702,6 @@ void _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, z50 = _mm256_add_ps(vbias, z50); } - // TODO make sure the lenght of bpptr is 8. if (bpptr) { z00 = _mm256_add_ps(z00, _mm256_loadu_ps(bpptr)); diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index 6e6b6c0ead..8002f396f1 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -49,6 +49,15 @@ Ptr initFastConv2d( useWinograd && ((conv->useSIMD128 || conv->useAVX2 || conv->useNEON) && Hk == 3 && Wk == 3 && dilation_y == 1 && dilation_x == 1 && stride_y == 1 && stride_x == 1) ? _FX_CONV_TYPE_WINOGRAD3X3 : _FX_CONV_TYPE_GENERIC; + + int VEC_NLANES = 4; +#if CV_TRY_AVX2 + if (!conv->useAVX2 && conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // convert Winograd to generic conv. + conv->conv_type = _FX_CONV_TYPE_GENERIC; + if (conv->useAVX2) + VEC_NLANES = 8; +#endif + Mat weightsMat = _weightsMat.getMat(); auto wShape = shape(weightsMat); const size_t wstep = weightsMat.step1(); @@ -61,7 +70,7 @@ Ptr initFastConv2d( int ksize = Hk*Wk; // this code aims to let memory fit with vector size. - int padded_ksize = ((ksize + FAST_VEC_NLANES-1) / FAST_VEC_NLANES) * FAST_VEC_NLANES; + int padded_ksize = ((ksize + VEC_NLANES-1) / VEC_NLANES) * VEC_NLANES; int nweights = C*padded_ksize; conv->weightsBuf.reserve(nweights + VEC_ALIGN); conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); @@ -265,7 +274,8 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr else if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3 && inputShape[2] >= 12 && inputShape[3] >= 12) // winograd { CV_Assert(conv->weightsWinoBufPtr); - return runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct); + if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) + return; } int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W] diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index 62c4170a3a..01f5edee48 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -12,35 +12,25 @@ #if CV_NEON && CV_NEON_AARCH64 // 32 registers. #define CONV_MR 4 #define CONV_NR 28 -enum { FAST_VEC_NLANES=4 }; #elif CV_NEON // 16 registers. #define CONV_MR 4 #define CONV_NR 12 -enum { FAST_VEC_NLANES=4 }; #else // SIMD 128, AVX or AVX2 #define CONV_MR 4 #define CONV_NR 24 - -#if CV_TRY_AVX2 -enum { FAST_VEC_NLANES=8 }; // AVX2 -#else -enum { FAST_VEC_NLANES=4 }; // SIMD 128 -#endif - -#endif #endif +// Winograd Params enum { _FX_WINO_STEP=6, _FX_WINO_KSIZE=3, _FX_WINO_SIZE=_FX_WINO_STEP+_FX_WINO_KSIZE-1, _FX_WINO_AREA=_FX_WINO_SIZE*_FX_WINO_SIZE, -#if CV_TRY_AVX2 || (CV_NEON && CV_NEON_AARCH64) _FX_WINO_KBLOCK = 4, +#if (CV_NEON && CV_NEON_AARCH64) || CV_TRY_AVX2 _FX_WINO_IBLOCK = 6, #else - _FX_WINO_KBLOCK = 4, _FX_WINO_IBLOCK = 3, #endif @@ -52,8 +42,8 @@ enum { _FX_WINO_NATOMS_F32 = _FX_WINO_AREA / _FX_WINO_ATOM_F32, // for AVX2, it is 8, otherwise, it's 16. }; - enum { _FX_CONV_TYPE_GENERIC=0, _FX_CONV_TYPE_DEPTHWISE=1, _FX_CONV_TYPE_WINOGRAD3X3=2 }; +#endif namespace cv { namespace dnn { @@ -77,8 +67,18 @@ struct FastConv2d #else bool useSIMD128 = false; #endif + +#if CV_TRY_AVX2 bool useAVX2 = checkHardwareSupport(CPU_AVX2); +#else + bool useAVX2 = false; +#endif + +#if CV_NEON bool useNEON = checkHardwareSupport(CPU_NEON); +#else + bool useNEON = false; +#endif }; // return a FastConv2d instance. @@ -99,7 +99,7 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); -void runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); } // namespace dnn diff --git a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp index bc44f73a22..10b55f3604 100644 --- a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp +++ b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp @@ -13,6 +13,8 @@ #include "fast_convolution.hpp" namespace cv { namespace dnn { + +#if CV_NEON || CV_SIMD128 || CV_TRY_AVX2 enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. static void @@ -141,7 +143,7 @@ _fx_winograd_accum_f32(const float* inwptr, const float* wptr, vst1q_f32(outbuf + 20*64, s32); } } -#elif CV_SIMD +#elif CV_SIMD128 CV_Assert(_FX_WINO_IBLOCK == 3 && _FX_WINO_KBLOCK == 4); for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; atom_id++, outbuf += _FX_WINO_ATOM_F32) @@ -183,15 +185,15 @@ _fx_winograd_accum_f32(const float* inwptr, const float* wptr, v_store(outbuf, s00); v_store(outbuf + 1*64, s01); v_store(outbuf + 2*64, s02); - v_store(outbuf + 6*64, s10); - v_store(outbuf + 7*64, s11); - v_store(outbuf + 8*64, s12); - v_store(outbuf + 12*64, s20); - v_store(outbuf + 13*64, s21); - v_store(outbuf + 14*64, s22); - v_store(outbuf + 18*64, s30); - v_store(outbuf + 19*64, s31); - v_store(outbuf + 20*64, s32); + v_store(outbuf + 3*64, s10); + v_store(outbuf + 4*64, s11); + v_store(outbuf + 5*64, s12); + v_store(outbuf + 6*64, s20); + v_store(outbuf + 7*64, s21); + v_store(outbuf + 8*64, s22); + v_store(outbuf + 9*64, s30); + v_store(outbuf + 10*64, s31); + v_store(outbuf + 11*64, s32); } #else for (int atom_id = 0; atom_id < _FX_WINO_NATOMS_F32; @@ -406,7 +408,7 @@ _fx_winograd_BtXB_8x8_f32(const float* inptr, int inpstep, vst1q_f32(outptr + outstep*13, z61); vst1q_f32(outptr + outstep*14, z70); vst1q_f32(outptr + outstep*15, z71); -#elif CV_SIMD +#elif CV_SIMD128 v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4); v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4); v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4); @@ -750,8 +752,7 @@ _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, vst1_f32(outptr + outstep*4 + 4, vget_low_f32(z41)); vst1q_f32(outptr + outstep*5, z50); vst1_f32(outptr + outstep*5 + 4, vget_low_f32(z51)); -//#elif CV_AVX2 -#elif CV_SIMD +#elif CV_SIMD128 v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4); v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4); v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4); @@ -919,7 +920,7 @@ _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, #endif } -void runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) { Mat input = _input.getMat(); @@ -1138,5 +1139,15 @@ void runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outp } } }}); + return 1; } + +#else + +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, + int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) +{ + return 0; +} +#endif }} // namespace cv::dnn From 8e5f37f87c741e09e8fb1d206c00a7927083e891 Mon Sep 17 00:00:00 2001 From: Ramiro Silveyra d'Avila Date: Fri, 21 Oct 2022 11:04:35 -0300 Subject: [PATCH 174/313] Merge pull request #22672 from ramasilveyra:docs/remove-dup-v4 * docs: remove duplicated step in Build .js doc * docs: add missing emcmake prefix to command --- doc/js_tutorials/js_setup/js_setup/js_setup.markdown | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown index 2a7a111d8a..26a4e419bd 100644 --- a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown +++ b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown @@ -135,14 +135,7 @@ Building OpenCV.js from Source For example: @code{.bash} - python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules" - @endcode - --# [optional] To enable OpenCV contrib modules append `--cmake_option="-DOPENCV_EXTRA_MODULES_PATH=/path/to/opencv_contrib/modules/"` - - For example: - @code{.bash} - python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules" + emcmake python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules" @endcode -# [optional] To enable WebNN backend, append `--webnn` option. From 35b2cff295ab48ea9ac6cdcd2220eab040bc22bd Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 21 Oct 2022 17:49:49 +0300 Subject: [PATCH 175/313] Merge pull request #22656 from dkurt:halide_fixes * Fixes for Halide * Enable some Halide tests --- modules/dnn/src/halide_scheduler.cpp | 24 ++++++++++++++++++++---- modules/dnn/src/layers/pooling_layer.cpp | 6 ++++++ modules/dnn/test/test_backends.cpp | 14 -------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/modules/dnn/src/halide_scheduler.cpp b/modules/dnn/src/halide_scheduler.cpp index 78335ddaf9..679c5ab7d5 100644 --- a/modules/dnn/src/halide_scheduler.cpp +++ b/modules/dnn/src/halide_scheduler.cpp @@ -71,9 +71,17 @@ static void applyFuse(const FileNode& directive, Halide::Func& func) static void applyParallel(const FileNode& directive, Halide::Func& func) { std::string varName; - for (int i = 0, n = directive.size(); i < n; ++i) + if (directive.isSeq()) { - directive[i] >> varName; + for (int i = 0, n = directive.size(); i < n; ++i) + { + directive[i] >> varName; + func.parallel(Halide::Var(varName)); + } + } + else + { + directive >> varName; func.parallel(Halide::Var(varName)); } } @@ -81,9 +89,17 @@ static void applyParallel(const FileNode& directive, Halide::Func& func) static void applyUnroll(const FileNode& directive, Halide::Func& func) { std::string varName; - for (int i = 0, n = directive.size(); i < n; ++i) + if (directive.isSeq()) { - directive[i] >> varName; + for (int i = 0, n = directive.size(); i < n; ++i) + { + directive[i] >> varName; + func.unroll(Halide::Var(varName)); + } + } + else + { + directive >> varName; func.unroll(Halide::Var(varName)); } } diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index 6c584bf2dd..9a808a7c67 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -1173,6 +1173,12 @@ public: // Halide::argmax returns tuple (r.x, r.y, max). Halide::Tuple res = argmax(inputBuffer(kx, ky, c, n)); + if (!computeMaxIdx) + { + top(x, y, c, n) = res[2]; + return Ptr(new HalideBackendNode(top)); + } + // Compute offset from argmax in range [0, kernel_size). Halide::Expr max_index; if(paddingLeft || paddingTop) diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 1933c11572..2e1ff14689 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -101,9 +101,6 @@ public: TEST_P(DNNTestNetwork, AlexNet) { applyTestTag(CV_TEST_TAG_MEMORY_1GB); - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); - processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt", Size(227, 227), "prob", target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_alexnet.yml" : @@ -118,8 +115,6 @@ TEST_P(DNNTestNetwork, ResNet_50) (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), CV_TEST_TAG_DEBUG_LONG ); - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); processNet("dnn/ResNet-50-model.caffemodel", "dnn/ResNet-50-deploy.prototxt", Size(224, 224), "prob", @@ -131,9 +126,6 @@ TEST_P(DNNTestNetwork, ResNet_50) TEST_P(DNNTestNetwork, SqueezeNet_v1_1) { - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); - processNet("dnn/squeezenet_v1.1.caffemodel", "dnn/squeezenet_v1.1.prototxt", Size(227, 227), "prob", target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_squeezenet_v1_1.yml" : @@ -145,8 +137,6 @@ TEST_P(DNNTestNetwork, SqueezeNet_v1_1) TEST_P(DNNTestNetwork, GoogLeNet) { applyTestTag(target == DNN_TARGET_CPU ? "" : CV_TEST_TAG_MEMORY_512MB); - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); processNet("dnn/bvlc_googlenet.caffemodel", "dnn/bvlc_googlenet.prototxt", Size(224, 224), "prob"); @@ -157,8 +147,6 @@ TEST_P(DNNTestNetwork, GoogLeNet) TEST_P(DNNTestNetwork, Inception_5h) { applyTestTag(CV_TEST_TAG_MEMORY_512MB); - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); double l1 = default_l1, lInf = default_lInf; if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && (target == DNN_TARGET_CPU || target == DNN_TARGET_OPENCL)) @@ -177,8 +165,6 @@ TEST_P(DNNTestNetwork, Inception_5h) TEST_P(DNNTestNetwork, ENet) { applyTestTag(target == DNN_TARGET_CPU ? "" : CV_TEST_TAG_MEMORY_512MB); - if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs - applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); From eae5fd81e559d6d4723eb9f04d15d1fbb9b8a11f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 23 Oct 2022 12:11:10 +0000 Subject: [PATCH 176/313] android: add android:exported="true" --- .../15-puzzle/gradle/AndroidManifest.xml | 1 + .../gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 4 +- .../face-detection/gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 6 +- .../gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 4 +- .../gradle/AndroidManifest.xml | 61 ++++++++++--------- 10 files changed, 57 insertions(+), 39 deletions(-) diff --git a/samples/android/15-puzzle/gradle/AndroidManifest.xml b/samples/android/15-puzzle/gradle/AndroidManifest.xml index 6d1d2b3bc5..1495649220 100644 --- a/samples/android/15-puzzle/gradle/AndroidManifest.xml +++ b/samples/android/15-puzzle/gradle/AndroidManifest.xml @@ -8,6 +8,7 @@ android:label="@string/app_name"> - diff --git a/samples/android/color-blob-detection/gradle/AndroidManifest.xml b/samples/android/color-blob-detection/gradle/AndroidManifest.xml index 1f09a5e3ca..4fb8b58210 100644 --- a/samples/android/color-blob-detection/gradle/AndroidManifest.xml +++ b/samples/android/color-blob-detection/gradle/AndroidManifest.xml @@ -8,7 +8,9 @@ android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" > - diff --git a/samples/android/face-detection/gradle/AndroidManifest.xml b/samples/android/face-detection/gradle/AndroidManifest.xml index e3a46ac5a0..157b318d3b 100644 --- a/samples/android/face-detection/gradle/AndroidManifest.xml +++ b/samples/android/face-detection/gradle/AndroidManifest.xml @@ -7,7 +7,9 @@ android:label="@string/app_name" android:icon="@drawable/icon"> - diff --git a/samples/android/image-manipulations/gradle/AndroidManifest.xml b/samples/android/image-manipulations/gradle/AndroidManifest.xml index bc89d9615d..2aa9c09ad6 100644 --- a/samples/android/image-manipulations/gradle/AndroidManifest.xml +++ b/samples/android/image-manipulations/gradle/AndroidManifest.xml @@ -7,7 +7,9 @@ android:label="@string/app_name" android:icon="@drawable/icon"> - diff --git a/samples/android/mobilenet-objdetect/gradle/AndroidManifest.xml b/samples/android/mobilenet-objdetect/gradle/AndroidManifest.xml index d17e633b41..473a5db3d0 100644 --- a/samples/android/mobilenet-objdetect/gradle/AndroidManifest.xml +++ b/samples/android/mobilenet-objdetect/gradle/AndroidManifest.xml @@ -10,8 +10,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.NoActionBar"> - + diff --git a/samples/android/tutorial-1-camerapreview/gradle/AndroidManifest.xml b/samples/android/tutorial-1-camerapreview/gradle/AndroidManifest.xml index 490dd6a1e7..79aaf3f5d4 100644 --- a/samples/android/tutorial-1-camerapreview/gradle/AndroidManifest.xml +++ b/samples/android/tutorial-1-camerapreview/gradle/AndroidManifest.xml @@ -8,7 +8,9 @@ android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" > - diff --git a/samples/android/tutorial-2-mixedprocessing/gradle/AndroidManifest.xml b/samples/android/tutorial-2-mixedprocessing/gradle/AndroidManifest.xml index 574facc3d5..90d4bbf8dc 100644 --- a/samples/android/tutorial-2-mixedprocessing/gradle/AndroidManifest.xml +++ b/samples/android/tutorial-2-mixedprocessing/gradle/AndroidManifest.xml @@ -7,7 +7,9 @@ android:label="@string/app_name" android:icon="@drawable/icon"> - diff --git a/samples/android/tutorial-3-cameracontrol/gradle/AndroidManifest.xml b/samples/android/tutorial-3-cameracontrol/gradle/AndroidManifest.xml index d8baa0859c..87cbc760b0 100644 --- a/samples/android/tutorial-3-cameracontrol/gradle/AndroidManifest.xml +++ b/samples/android/tutorial-3-cameracontrol/gradle/AndroidManifest.xml @@ -7,7 +7,9 @@ android:label="@string/app_name" android:icon="@drawable/icon"> - diff --git a/samples/android/tutorial-4-opencl/gradle/AndroidManifest.xml b/samples/android/tutorial-4-opencl/gradle/AndroidManifest.xml index f44d9a5dcc..7cef5cc675 100644 --- a/samples/android/tutorial-4-opencl/gradle/AndroidManifest.xml +++ b/samples/android/tutorial-4-opencl/gradle/AndroidManifest.xml @@ -1,31 +1,32 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + From c29a3aa0a0775df71afdd89fb619fb75957a95c8 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 23 Oct 2022 14:31:02 +0000 Subject: [PATCH 177/313] android: update build scripts --- cmake/android/android_gradle_projects.cmake | 14 +++++++++++++- samples/android/build.gradle.in | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cmake/android/android_gradle_projects.cmake b/cmake/android/android_gradle_projects.cmake index e07d26b5bb..4a98421c6a 100644 --- a/cmake/android/android_gradle_projects.cmake +++ b/cmake/android/android_gradle_projects.cmake @@ -3,7 +3,7 @@ set(ANDROID_GRADLE_PLUGIN_VERSION "3.2.1" CACHE STRING "Android Gradle Plugin ve message(STATUS "Android Gradle Plugin version: ${ANDROID_GRADLE_PLUGIN_VERSION}") set(KOTLIN_PLUGIN_VERSION "1.4.10" CACHE STRING "Kotlin Plugin version") -message(STATUS "kotlin Plugin version: ${KOTLIN_GRADLE_PLUGIN_VERSION}") +message(STATUS "Kotlin Plugin version: ${KOTLIN_PLUGIN_VERSION}") if(BUILD_KOTLIN_EXTENSIONS) set(KOTLIN_PLUGIN_DECLARATION "apply plugin: 'kotlin-android'" CACHE STRING "Kotlin Plugin version") @@ -50,9 +50,11 @@ endif() #string(REPLACE "\n" "\n${__spaces}" ANDROID_ABI_FILTER "${__spaces}${ANDROID_BUILD_ABI_FILTER}") #string(REPLACE REGEX "[ ]+$" "" ANDROID_ABI_FILTER "${ANDROID_ABI_FILTER}") set(ANDROID_ABI_FILTER "${ANDROID_BUILD_ABI_FILTER}") +set(ANDROID_STRICT_BUILD_CONFIGURATION "true") configure_file("${OpenCV_SOURCE_DIR}/samples/android/build.gradle.in" "${ANDROID_BUILD_BASE_DIR}/build.gradle" @ONLY) set(ANDROID_ABI_FILTER "${ANDROID_INSTALL_ABI_FILTER}") +set(ANDROID_STRICT_BUILD_CONFIGURATION "false") configure_file("${OpenCV_SOURCE_DIR}/samples/android/build.gradle.in" "${ANDROID_TMP_INSTALL_BASE_DIR}/${ANDROID_INSTALL_SAMPLES_DIR}/build.gradle" @ONLY) install(FILES "${ANDROID_TMP_INSTALL_BASE_DIR}/${ANDROID_INSTALL_SAMPLES_DIR}/build.gradle" DESTINATION "${ANDROID_INSTALL_SAMPLES_DIR}" COMPONENT samples) @@ -80,6 +82,15 @@ foreach(fname ${GRADLE_WRAPPER_FILES}) install(FILES "${OpenCV_SOURCE_DIR}/platforms/android/gradle-wrapper/${fname}" DESTINATION "${ANDROID_INSTALL_SAMPLES_DIR}/${__dir}" COMPONENT samples ${__permissions}) endforeach() +# force reusing of the same CMake version +if(NOT OPENCV_SKIP_ANDROID_FORCE_CMAKE) + if(NOT DEFINED _CMAKE_INSTALL_DIR) + get_filename_component(_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) + get_filename_component(_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) + endif() + ocv_update_file("${ANDROID_BUILD_BASE_DIR}/local.properties" "cmake.dir=${_CMAKE_INSTALL_DIR}") +endif() + file(WRITE "${ANDROID_BUILD_BASE_DIR}/settings.gradle" " include ':opencv' ") @@ -94,6 +105,7 @@ include ':opencv' project(':opencv').projectDir = new File(opencvsdk + '/sdk') ") +ocv_check_environment_variables(OPENCV_GRADLE_VERBOSE_OPTIONS) macro(add_android_project target path) get_filename_component(__dir "${path}" NAME) diff --git a/samples/android/build.gradle.in b/samples/android/build.gradle.in index 934a1a69d0..f36fe216f5 100644 --- a/samples/android/build.gradle.in +++ b/samples/android/build.gradle.in @@ -35,6 +35,7 @@ task clean(type: Delete) { // } //} +def opencv_strict_build_configuration = @ANDROID_STRICT_BUILD_CONFIGURATION@; gradle.afterProject { project -> if (project.pluginManager.hasPlugin('com.android.application') @@ -90,5 +91,14 @@ gradle.afterProject { project -> } } + // Android Gradle Plugin (AGP) 3.5+ is required + // https://github.com/android/ndk-samples/wiki/Configure-NDK-Path + def isNdkVersionSupported = project.android.metaClass.getProperties().find { it.name == 'ndkVersion' } != null + if ((false || opencv_strict_build_configuration) && isNdkVersionSupported) { + gradle.println("Override ndkVersion for the project ${project.name}") + project.android { + ndkVersion '@ANDROID_NDK_REVISION@' + } + } } } From 22f8fb4d5c5fe5902eafc5ddb7529dfcae4120c5 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 24 Oct 2022 17:59:18 +0300 Subject: [PATCH 178/313] Do not fail tests in Yolo v7 model was not found. --- modules/dnn/test/test_onnx_importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index abafe6f243..c90195783c 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -2395,7 +2395,7 @@ TEST_P(Test_ONNX_layers, CumSum) // 4. 1D mat dimension issue with the output of range operator TEST_P(Test_ONNX_layers, YOLOv7) { - std::string weightPath = _tf("models/yolov7_not_simplified.onnx"); + std::string weightPath = _tf("models/yolov7_not_simplified.onnx", false); std::string imgPath = _tf("../dog_orig_size.png"); Size targetSize{640, 640}; From aae317c01772b46cad96adaab5c7611897b1ade5 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 24 Oct 2022 22:21:49 +0000 Subject: [PATCH 179/313] android: add configuration for NDK 25 and Android API 32 (12L) --- platforms/android/ndk-25.config.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 platforms/android/ndk-25.config.py diff --git a/platforms/android/ndk-25.config.py b/platforms/android/ndk-25.config.py new file mode 100644 index 0000000000..b6b12126eb --- /dev/null +++ b/platforms/android/ndk-25.config.py @@ -0,0 +1,19 @@ +# Docs: https://developer.android.com/ndk/guides/cmake#android_native_api_level +ANDROID_NATIVE_API_LEVEL = int(os.environ.get('ANDROID_NATIVE_API_LEVEL', 32)) +cmake_common_vars = { + # Docs: https://source.android.com/docs/setup/about/build-numbers + # Docs: https://developer.android.com/studio/publish/versioning + 'ANDROID_COMPILE_SDK_VERSION': os.environ.get('ANDROID_COMPILE_SDK_VERSION', 32), + 'ANDROID_TARGET_SDK_VERSION': os.environ.get('ANDROID_TARGET_SDK_VERSION', 32), + 'ANDROID_MIN_SDK_VERSION': os.environ.get('ANDROID_MIN_SDK_VERSION', ANDROID_NATIVE_API_LEVEL), + # Docs: https://developer.android.com/studio/releases/gradle-plugin + 'ANDROID_GRADLE_PLUGIN_VERSION': '7.3.1', + 'GRADLE_VERSION': '7.5.1', + 'KOTLIN_PLUGIN_VERSION': '1.5.20', +} +ABIs = [ + ABI("2", "armeabi-v7a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars), + ABI("3", "arm64-v8a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars), + ABI("5", "x86_64", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars), + ABI("4", "x86", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars), +] From a6fadfe1c275b9885b59612c8a036f4fb15f32c4 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 24 Oct 2022 18:20:54 +0300 Subject: [PATCH 180/313] libav for jetson tk1 does not provide libavutil/display.h. --- modules/videoio/src/cap_ffmpeg_impl.hpp | 4 ++++ modules/videoio/test/test_ffmpeg.cpp | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index c7976ffb82..00732f5201 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -82,7 +82,11 @@ extern "C" { #include #include +// https://github.com/FFmpeg/FFmpeg/blame/d79c240196f43b93bd204363f1facc270029f113/doc/APIchanges#L1689-L1695 +#if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \ + ? CALC_FFMPEG_VERSION(52, 85, 100) : CALC_FFMPEG_VERSION(53, 15, 0)) #include +#endif #if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \ ? CALC_FFMPEG_VERSION(51, 63, 100) : CALC_FFMPEG_VERSION(54, 6, 0)) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 1e2a91bd30..3ae27de1b4 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -95,8 +95,6 @@ TEST(videoio_ffmpeg, image) //========================================================================== -#define THREADS testing::ValuesIn({ 0,1,2,2000 }) -#define RAW_READ testing::ValuesIn({true, false}) typedef tuple videoio_read_params_t; typedef testing::TestWithParam< testing::tuple> videoio_read; @@ -139,7 +137,9 @@ const videoio_read_params_t videoio_read_params[] = //videoio_read_params_t("video/big_buck_bunny.wmv", 125, true), }; -INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params), THREADS, RAW_READ)); +INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params), + testing::Values(0, 1, 2, 2000), + testing::Values(true, false))); //========================================================================== From d933034ad452e490867eb54b94736b798af65234 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 25 Oct 2022 13:41:10 +0300 Subject: [PATCH 181/313] Added ARM64 debug configuration to CI. --- .github/workflows/PR-4.x.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index 2664968448..84cd94763b 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -9,6 +9,9 @@ jobs: Ubuntu2004-ARM64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-ARM64.yaml@main + Ubuntu2004-ARM64-Debug: + uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-ARM64-Debug.yaml@main + Ubuntu2004-x64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20.yaml@main From 5462a6be6e870b4a894592207921db2c9495f17c Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Wed, 26 Oct 2022 13:02:03 +0000 Subject: [PATCH 182/313] Update SIMD macro for RVV backend. --- modules/core/include/opencv2/core/hal/intrin.hpp | 9 +++++---- .../include/opencv2/core/hal/intrin_rvv_scalable.hpp | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index c12140bbf8..4239a7e229 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -522,6 +522,11 @@ using namespace CV__SIMD_NAMESPACE; #endif +//! @cond IGNORED +#ifndef CV_SIMD_64F +#define CV_SIMD_64F 0 +#endif + namespace CV__SIMD_NAMESPACE { //! @addtogroup core_hal_intrin //! @{ @@ -886,10 +891,6 @@ namespace CV__SIMD_NAMESPACE { #undef VXPREFIX } // namespace -//! @cond IGNORED -#ifndef CV_SIMD_64F -#define CV_SIMD_64F 0 -#endif #ifndef CV_SIMD_FP16 #define CV_SIMD_FP16 0 //!< Defined to 1 on native support of operations with float16x8_t / float16x16_t (SIMD256) types diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 463c010d71..5dd9e2cda7 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -1,3 +1,9 @@ +// 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. + +// The original implementation is contributed by HAN Liutong. +// Copyright (C) 2022, Institute of Software, Chinese Academy of Sciences. #ifndef OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP #define OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP @@ -1689,7 +1695,7 @@ OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(v_float64, vlmul_trunc_u8mf8) ////// FP16 support /////// -#if __riscv_zfh +#if defined(__riscv_zfh) && __riscv_zfh inline v_float32 v_load_expand(const float16_t* ptr) { return vfwcvt_f(vle16_v_f16mf2((_Float16*)ptr, VTraits::vlanes()) ,VTraits::vlanes());; From 547f4c2c5af6f2f558722df1f93ebbf9ae9eb9ea Mon Sep 17 00:00:00 2001 From: kallaballa Date: Thu, 27 Oct 2022 00:27:17 +0200 Subject: [PATCH 183/313] print a debug message if the environment variables OPENCV_FFMPEG_CAPTURE_OPTIONS and OPENCV_FFMPEG_WRITER_OPTIONS are set --- modules/videoio/src/cap_ffmpeg_impl.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index c7976ffb82..935e373692 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1116,6 +1116,7 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& } else { + CV_LOG_DEBUG(NULL, "VIDEOIO/FFMPEG: using capture options from environment: " << options); #if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 ? CALC_FFMPEG_VERSION(52, 17, 100) : CALC_FFMPEG_VERSION(52, 7, 0)) av_dict_parse_string(&dict, options, ";", "|", 0); #else @@ -2911,7 +2912,9 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, AVDictionary *dict = NULL; #if !defined(NO_GETENV) && (LIBAVUTIL_VERSION_MAJOR >= 53) char* options = getenv("OPENCV_FFMPEG_WRITER_OPTIONS"); - if (options) { + if (options) + { + CV_LOG_DEBUG(NULL, "VIDEOIO/FFMPEG: using writer options from environment: " << options); av_dict_parse_string(&dict, options, ";", "|", 0); } #endif From b619477be940533df3662a0df2c2153dcacf1c3c Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Thu, 27 Oct 2022 18:25:36 +0000 Subject: [PATCH 184/313] Fix issues with VA_INCLUDE_HEADERS when building with CUDA support ...and not only? --- modules/gapi/CMakeLists.txt | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 66a559bf7f..2a9d598bb8 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -328,18 +328,13 @@ if(HAVE_GAPI_ONEVPL) ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() - if(UNIX) - if(WITH_VA) - include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindVA.cmake") - if(VA_INCLUDE_DIR) - ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${VA_INCLUDE_DIR}) - ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${VA_INCLUDE_DIR}) - ocv_target_link_libraries(${the_module} PRIVATE ${VA_LIBRARIES}) - ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VA_LIBRARIES}) - endif() - else(WITH_VA) - message(FATAL_ERROR "libva not found: building HAVE_GAPI_ONEVPL without libVA support is impossible on UNIX systems") - endif(WITH_VA) + if(UNIX AND HAVE_VA) + ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(${the_module} PRIVATE ${VA_LIBRARIES}) + if(TARGET opencv_test_gapi) + ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VA_LIBRARIES}) + endif() endif() endif() @@ -382,7 +377,7 @@ if(TARGET example_gapi_onevpl_infer_single_roi) if(HAVE_D3D11 AND HAVE_OPENCL) ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() - if(WITH_VA AND UNIX) + if(UNIX AND HAVE_VA) message ("GAPI VPL samples with VAAPI") ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${VA_INCLUDE_DIR}) ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE ${VA_LIBRARIES}) From be670e168a9b82054f8acd2e1885f9afef0f7332 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Fri, 28 Oct 2022 11:03:51 +0300 Subject: [PATCH 185/313] Merge pull request #22615 from cudawarped:nvcuvenc Update CMake rules to include Nvidia Video Codec SDK encoding libs --- CMakeLists.txt | 4 ++ cmake/FindCUDA.cmake | 6 +-- cmake/OpenCVDetectCUDA.cmake | 32 ++++++----- cmake/templates/OpenCVConfig-CUDA.cmake.in | 10 +--- samples/gpu/video_reader.cpp | 62 +++++++--------------- samples/gpu/video_writer.cpp | 45 ++++------------ 6 files changed, 57 insertions(+), 102 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dbbb10ac9f..aba65ba23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,9 @@ OCV_OPTION(WITH_CUDNN "Include NVIDIA CUDA Deep Neural Network (cuDNN) library s OCV_OPTION(WITH_NVCUVID "Include NVidia Video Decoding library support" OFF # disabled, details: https://github.com/opencv/opencv/issues/14850 VISIBLE_IF WITH_CUDA VERIFY HAVE_NVCUVID) +OCV_OPTION(WITH_NVCUVENC "Include NVidia Video Encoding library support" OFF + VISIBLE_IF WITH_CUDA + VERIFY HAVE_NVCUVENC) OCV_OPTION(WITH_EIGEN "Include Eigen2/Eigen3 support" (NOT CV_DISABLE_OPTIMIZATION AND NOT CMAKE_CROSSCOMPILING) VISIBLE_IF NOT WINRT VERIFY HAVE_EIGEN) @@ -1661,6 +1664,7 @@ if(WITH_CUDA OR HAVE_CUDA) IF HAVE_CUFFT THEN "CUFFT" IF HAVE_CUBLAS THEN "CUBLAS" IF HAVE_NVCUVID THEN "NVCUVID" + IF HAVE_NVCUVENC THEN "NVCUVENC" IF CUDA_FAST_MATH THEN "FAST_MATH" ELSE "no extra features") status("") diff --git a/cmake/FindCUDA.cmake b/cmake/FindCUDA.cmake index 37d557a792..ca04bb4a4e 100644 --- a/cmake/FindCUDA.cmake +++ b/cmake/FindCUDA.cmake @@ -287,7 +287,7 @@ # Only available for CUDA version 5.5+. # CUDA_npps_LIBRARY -- NVIDIA Performance Primitives lib (signal processing). # Only available for CUDA version 5.5+. -# CUDA_nvcuvenc_LIBRARY -- CUDA Video Encoder library. +# CUDA_nvencodeapi_LIBRARY -- CUDA Video Encoder library. # Only available for CUDA version 3.2+. # Windows only. # CUDA_nvcuvid_LIBRARY -- CUDA Video Decoder library. @@ -530,7 +530,7 @@ macro(cuda_unset_include_and_libraries) unset(CUDA_nppc_LIBRARY CACHE) unset(CUDA_nppi_LIBRARY CACHE) unset(CUDA_npps_LIBRARY CACHE) - unset(CUDA_nvcuvenc_LIBRARY CACHE) + unset(CUDA_nvencodeapi_LIBRARY CACHE) unset(CUDA_nvcuvid_LIBRARY CACHE) endmacro() @@ -790,7 +790,7 @@ if(NOT CUDA_VERSION VERSION_LESS "3.2") find_cuda_helper_libs(cusparse) find_cuda_helper_libs(curand) if (WIN32) - find_cuda_helper_libs(nvcuvenc) + find_cuda_helper_libs(nvencodeapi) find_cuda_helper_libs(nvcuvid) endif() endif() diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index acc101396c..69d0455cc8 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -28,6 +28,7 @@ else() endif() if(CUDA_FOUND) + unset(CUDA_nvcuvenc_LIBRARY CACHE) set(HAVE_CUDA 1) if(NOT CUDA_VERSION VERSION_LESS 11.0) # CUDA 11.0 removes nppicom @@ -53,7 +54,7 @@ if(CUDA_FOUND) endif() endif() - if(WITH_NVCUVID) + if(WITH_NVCUVID OR WITH_NVCUVENC) macro(ocv_cuda_SEARCH_NVCUVID_HEADER _filename _result) # place header file under CUDA_TOOLKIT_TARGET_DIR or CUDA_TOOLKIT_ROOT_DIR find_path(_header_result @@ -71,18 +72,25 @@ if(CUDA_FOUND) endif() unset(_header_result CACHE) endmacro() - ocv_cuda_SEARCH_NVCUVID_HEADER("nvcuvid.h" HAVE_NVCUVID_HEADER) - ocv_cuda_SEARCH_NVCUVID_HEADER("dynlink_nvcuvid.h" HAVE_DYNLINK_NVCUVID_HEADER) - find_cuda_helper_libs(nvcuvid) - if(WIN32) - find_cuda_helper_libs(nvcuvenc) + if(WITH_NVCUVID) + ocv_cuda_SEARCH_NVCUVID_HEADER("nvcuvid.h" HAVE_NVCUVID_HEADER) + ocv_cuda_SEARCH_NVCUVID_HEADER("dynlink_nvcuvid.h" HAVE_DYNLINK_NVCUVID_HEADER) + find_cuda_helper_libs(nvcuvid) + if(CUDA_nvcuvid_LIBRARY AND (${HAVE_NVCUVID_HEADER} OR ${HAVE_DYNLINK_NVCUVID_HEADER})) + # make sure to have both header and library before enabling + set(HAVE_NVCUVID 1) + endif() endif() - if(CUDA_nvcuvid_LIBRARY AND (${HAVE_NVCUVID_HEADER} OR ${HAVE_DYNLINK_NVCUVID_HEADER})) - # make sure to have both header and library before enabling - set(HAVE_NVCUVID 1) - endif() - if(CUDA_nvcuvenc_LIBRARY) - set(HAVE_NVCUVENC 1) + if(WITH_NVCUVENC) + ocv_cuda_SEARCH_NVCUVID_HEADER("nvEncodeAPI.h" HAVE_NVCUVENC_HEADER) + if(WIN32) + find_cuda_helper_libs(nvencodeapi) + else() + find_cuda_helper_libs(nvidia-encode) + endif() + if((CUDA_nvencodeapi_LIBRARY OR CUDA_nvidia-encode_LIBRARY) AND ${HAVE_NVCUVENC_HEADER}) + set(HAVE_NVCUVENC 1) + endif() endif() endif() diff --git a/cmake/templates/OpenCVConfig-CUDA.cmake.in b/cmake/templates/OpenCVConfig-CUDA.cmake.in index e71c9e2e31..25a20556ec 100644 --- a/cmake/templates/OpenCVConfig-CUDA.cmake.in +++ b/cmake/templates/OpenCVConfig-CUDA.cmake.in @@ -5,7 +5,7 @@ set(OpenCV_CUDA_VERSION "@CUDA_VERSION_STRING@") set(OpenCV_USE_CUBLAS "@HAVE_CUBLAS@") set(OpenCV_USE_CUFFT "@HAVE_CUFFT@") set(OpenCV_USE_NVCUVID "@HAVE_NVCUVID@") - +set(OpenCV_USE_NVCUVENC "@HAVE_NVCUVENC@") set(OpenCV_CUDNN_VERSION "@CUDNN_VERSION@") set(OpenCV_USE_CUDNN "@HAVE_CUDNN@") @@ -36,14 +36,6 @@ if(OpenCV_USE_CUFFT) list(APPEND OpenCV_CUDA_LIBS_ABSPATH ${CUDA_CUFFT_LIBRARIES}) endif() -if(OpenCV_USE_NVCUVID) - list(APPEND OpenCV_CUDA_LIBS_ABSPATH ${CUDA_nvcuvid_LIBRARIES}) -endif() - -if(WIN32) - list(APPEND OpenCV_CUDA_LIBS_ABSPATH ${CUDA_nvcuvenc_LIBRARIES}) -endif() - set(OpenCV_CUDA_LIBS_RELPATH "") foreach(l ${OpenCV_CUDA_LIBS_ABSPATH}) get_filename_component(_tmp ${l} PATH) diff --git a/samples/gpu/video_reader.cpp b/samples/gpu/video_reader.cpp index ea141c3b1a..e8a738f905 100644 --- a/samples/gpu/video_reader.cpp +++ b/samples/gpu/video_reader.cpp @@ -22,65 +22,41 @@ int main(int argc, const char* argv[]) const std::string fname(argv[1]); cv::namedWindow("CPU", cv::WINDOW_NORMAL); +#if defined(HAVE_OPENGL) cv::namedWindow("GPU", cv::WINDOW_OPENGL); cv::cuda::setGlDevice(); +#else + cv::namedWindow("GPU", cv::WINDOW_NORMAL); +#endif + cv::TickMeter tm; cv::Mat frame; cv::VideoCapture reader(fname); + for (;;) + { + if (!reader.read(frame)) + break; + cv::imshow("CPU", frame); + if (cv::waitKey(3) > 0) + break; + } cv::cuda::GpuMat d_frame; cv::Ptr d_reader = cv::cudacodec::createVideoReader(fname); - - cv::TickMeter tm; - std::vector cpu_times; - std::vector gpu_times; - - int gpu_frame_count=0, cpu_frame_count=0; - for (;;) { - tm.reset(); tm.start(); - if (!reader.read(frame)) - break; - tm.stop(); - cpu_times.push_back(tm.getTimeMilli()); - cpu_frame_count++; - - cv::imshow("CPU", frame); - - if (cv::waitKey(3) > 0) - break; - } - - for (;;) - { - tm.reset(); tm.start(); if (!d_reader->nextFrame(d_frame)) break; - tm.stop(); - gpu_times.push_back(tm.getTimeMilli()); - gpu_frame_count++; - - cv::imshow("GPU", d_frame); - +#if defined(HAVE_OPENGL) + cv::imshow("GPU", cv::ogl::Texture2D(d_frame)); +#else + d_frame.download(frame); + cv::imshow("GPU", frame); +#endif if (cv::waitKey(3) > 0) break; } - if (!cpu_times.empty() && !gpu_times.empty()) - { - std::cout << std::endl << "Results:" << std::endl; - - std::sort(cpu_times.begin(), cpu_times.end()); - std::sort(gpu_times.begin(), gpu_times.end()); - - double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size(); - double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size(); - - std::cout << "CPU : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << " Frames " << cpu_frame_count << std::endl; - std::cout << "GPU : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << " Frames " << gpu_frame_count << std::endl; - } - return 0; } diff --git a/samples/gpu/video_writer.cpp b/samples/gpu/video_writer.cpp index fa1d709c09..02b17700fc 100644 --- a/samples/gpu/video_writer.cpp +++ b/samples/gpu/video_writer.cpp @@ -2,7 +2,7 @@ #include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_CUDACODEC) && defined(_WIN32) +#if defined(HAVE_OPENCV_CUDACODEC) #include #include @@ -20,7 +20,7 @@ int main(int argc, const char* argv[]) return -1; } - const double FPS = 25.0; + constexpr double fps = 25.0; cv::VideoCapture reader(argv[1]); @@ -37,17 +37,12 @@ int main(int argc, const char* argv[]) cv::Mat frame; cv::cuda::GpuMat d_frame; - - std::vector cpu_times; - std::vector gpu_times; - TickMeter tm; + cv::cuda::Stream stream; for (int i = 1;; ++i) { std::cout << "Read " << i << " frame" << std::endl; - reader >> frame; - if (frame.empty()) { std::cout << "Stop" << std::endl; @@ -57,47 +52,27 @@ int main(int argc, const char* argv[]) if (!writer.isOpened()) { std::cout << "Frame Size : " << frame.cols << "x" << frame.rows << std::endl; - std::cout << "Open CPU Writer" << std::endl; - - if (!writer.open("output_cpu.avi", cv::VideoWriter::fourcc('X', 'V', 'I', 'D'), FPS, frame.size())) + const String outputFilename = "output_cpu.avi"; + if (!writer.open(outputFilename, cv::VideoWriter::fourcc('X', 'V', 'I', 'D'), fps, frame.size())) return -1; + std::cout << "Writing to " << outputFilename << std::endl; } if (d_writer.empty()) { std::cout << "Open CUDA Writer" << std::endl; - - const cv::String outputFilename = "output_gpu.avi"; - d_writer = cv::cudacodec::createVideoWriter(outputFilename, frame.size(), FPS); + const cv::String outputFilename = "output_gpu.h264"; + d_writer = cv::cudacodec::createVideoWriter(outputFilename, frame.size(), cv::cudacodec::Codec::H264, fps, cv::cudacodec::ColorFormat::BGR, 0, stream); + std::cout << "Writing to " << outputFilename << std::endl; } - d_frame.upload(frame); - + d_frame.upload(frame, stream); std::cout << "Write " << i << " frame" << std::endl; - - tm.reset(); tm.start(); writer.write(frame); - tm.stop(); - cpu_times.push_back(tm.getTimeMilli()); - - tm.reset(); tm.start(); d_writer->write(d_frame); - tm.stop(); - gpu_times.push_back(tm.getTimeMilli()); } - std::cout << std::endl << "Results:" << std::endl; - - std::sort(cpu_times.begin(), cpu_times.end()); - std::sort(gpu_times.begin(), gpu_times.end()); - - double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size(); - double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size(); - - std::cout << "CPU [XVID] : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << std::endl; - std::cout << "GPU [H264] : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << std::endl; - return 0; } From 028d4d83d3087db6ce789ec3fa900649cde761d9 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 28 Oct 2022 17:03:43 +0000 Subject: [PATCH 186/313] imgproc: sigma2=sigma1 in top-level function of GaussianBlur --- modules/imgproc/src/smooth.dispatch.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/imgproc/src/smooth.dispatch.cpp b/modules/imgproc/src/smooth.dispatch.cpp index b001a37f89..2fb2fef8b7 100644 --- a/modules/imgproc/src/smooth.dispatch.cpp +++ b/modules/imgproc/src/smooth.dispatch.cpp @@ -635,6 +635,9 @@ void GaussianBlur(InputArray _src, OutputArray _dst, Size ksize, return; } + if (sigma2 <= 0) + sigma2 = sigma1; + bool useOpenCL = ocl::isOpenCLActivated() && _dst.isUMat() && _src.dims() <= 2 && _src.rows() >= ksize.height && _src.cols() >= ksize.width && ksize.width > 1 && ksize.height > 1; From 17b98dd005cd9739e0afdc24e0dc52067abcbb9b Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Sat, 29 Oct 2022 17:34:28 +0800 Subject: [PATCH 187/313] improve code style and Doc of stackblur. --- modules/imgproc/include/opencv2/imgproc.hpp | 13 +++++++------ modules/imgproc/src/stackblur.cpp | 12 +++++------- modules/imgproc/test/test_stackblur.cpp | 8 +++++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index bb19abd7ff..afc2742ab6 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1620,14 +1620,15 @@ CV_EXPORTS_W void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT ); -/** @brief Blurs an image using the StackBlur. -The function applies and StackBlur to an image. -StackBlur can generate similar results as Gaussian blur, and the time does not increase as the kernel size increases. +/** @brief Blurs an image using the stackBlur. + +The function applies and stackBlur to an image. +stackBlur can generate similar results as Gaussian blur, and the time consumption does not increase with the increase of kernel size. It creates a kind of moving stack of colors whilst scanning through the image. Thereby it just has to add one new block of color to the right side of the stack and remove the leftmost color. The remaining colors on the topmost layer of the stack are either added on or reduced by one, -depending on if they are on the right or on the left side of the stack. -Described here: http://underdestruction.com/2004/02/25/stackblur-2004. -Stack Blur Algorithm by Mario Klingemann +depending on if they are on the right or on the left side of the stack. The only supported borderType is BORDER_REPLICATE. +Original paper was proposed by Mario Klingemann, which can be found http://underdestruction.com/2004/02/25/stackblur-2004. + @param src input image. The number of channels can be arbitrary, but the depth should be one of CV_8U, CV_16U, CV_16S or CV_32F. @param dst output image of the same size and type as src. diff --git a/modules/imgproc/src/stackblur.cpp b/modules/imgproc/src/stackblur.cpp index 7a911ffbb8..5d60a1d365 100644 --- a/modules/imgproc/src/stackblur.cpp +++ b/modules/imgproc/src/stackblur.cpp @@ -461,8 +461,6 @@ public: ~ParallelStackBlurRow() {} - ParallelStackBlurRow& operator=(const ParallelStackBlurRow &) { return *this; } - /* * The idea is as follows: * The stack can be understood as a sliding window of length kernel size. @@ -1084,8 +1082,6 @@ public: ~ParallelStackBlurColumn() {} - ParallelStackBlurColumn& operator=(const ParallelStackBlurColumn &) { return *this; } - virtual void operator ()(const Range& range) const CV_OVERRIDE { if (radius == 0) @@ -1146,7 +1142,8 @@ public: if (stackStart >= kernelSize) stackStart -= kernelSize; int sp1 = sp + 1; - sp1 &= -(sp1 < kernelSize); + if (sp1 >= kernelSize) + sp1 = 0; if (yp < hm) { @@ -1176,7 +1173,8 @@ public: dstPtr += widthElem; ++sp; - if (sp >= kernelSize) sp = 0; + if (sp >= kernelSize) + sp = 0; } } @@ -1255,7 +1253,7 @@ void stackBlur(InputArray _src, OutputArray _dst, Size ksize) parallel_for_(Range(0, widthElem), ParallelStackBlurColumn(dst, dst, radiusH), numOfThreads); } else - CV_Error_( CV_StsNotImplemented, + CV_Error(Error::StsNotImplemented, ("Unsupported input format in StackBlur, the supported formats are: CV_8U, CV_16U, CV_16S and CV_32F.")); } } //namespace diff --git a/modules/imgproc/test/test_stackblur.cpp b/modules/imgproc/test/test_stackblur.cpp index 32310d2762..b7525467b3 100644 --- a/modules/imgproc/test/test_stackblur.cpp +++ b/modules/imgproc/test/test_stackblur.cpp @@ -125,7 +125,8 @@ void _stackblurRef(const Mat& src, Mat& dst, Size ksize) } int sp1 = sp + 1; - sp1 &= -(sp1 < stackLenW); + if (sp1 >= stackLenW) + sp1 = 0; for(int ci = 0; ci < CN; ci++) { @@ -143,7 +144,8 @@ void _stackblurRef(const Mat& src, Mat& dst, Size ksize) } ++sp; - if (sp >= stackLenW) sp = 0; + if (sp >= stackLenW) + sp = 0; } } @@ -239,7 +241,7 @@ void stackBlurRef(const Mat& img, Mat& dst, Size ksize) else if (img.depth() == CV_32F) _stackblurRef(img, dst, ksize); else - CV_Error_( CV_StsNotImplemented, + CV_Error(Error::StsNotImplemented, ("Unsupported Mat type in stackBlurRef, " "the supported formats are: CV_8U, CV_16U, CV_16S and CV_32F.")); } From 0b5fd4f6bbfb2a253a22653b3407a63bb5acfdbc Mon Sep 17 00:00:00 2001 From: JopKnoppers Date: Mon, 31 Oct 2022 12:19:04 +0100 Subject: [PATCH 188/313] Included thread in gapi_async_test.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preventing: gapi_async_test.cpp:448:26: error: ‘sleep_for’ is not a member of ‘std::this_thread’ --- modules/gapi/test/gapi_async_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gapi/test/gapi_async_test.cpp b/modules/gapi/test/gapi_async_test.cpp index 34a58e90d1..5a7194a17f 100644 --- a/modules/gapi/test/gapi_async_test.cpp +++ b/modules/gapi/test/gapi_async_test.cpp @@ -13,6 +13,7 @@ #include #include +#include namespace opencv_test { From 18fbb72f7db0728a178110b3c7d8bbaba5c8abad Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Mon, 31 Oct 2022 20:10:25 +0800 Subject: [PATCH 189/313] fix the infinite loop in tf importer. --- modules/dnn/src/tensorflow/tf_graph_simplifier.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index 72f546ead9..25ef850417 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -829,12 +829,19 @@ void RemoveIdentityOps(tensorflow::GraphDef& net) IdentityOpsMap::iterator it = identity_ops.find(input_op_name); if (it != identity_ops.end()) { + std::set loopCheckSet; // In case of Identity after Identity while (true) { IdentityOpsMap::iterator nextIt = identity_ops.find(it->second); if (nextIt != identity_ops.end()) + { + // Loop check + if (loopCheckSet.find(it->second) != loopCheckSet.end()) + CV_Error(Error::StsError, "Found a loop in your input Tensorflow model, which is illegal!"); + loopCheckSet.insert(it->second); it = nextIt; + } else break; } From 903bf0147e49ab9d370926627f384df533d49603 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Tue, 1 Nov 2022 00:06:31 +0800 Subject: [PATCH 190/313] Merge pull request #22666 from zihaomu:support_onnx_qdq_model DNN: let Quant and Dequant of ONNX_importer support the Constant input. * let Quant and Dequant support the Constant input. * fix negative value of axis. --- .../dnn/include/opencv2/dnn/all_layers.hpp | 8 +- .../dnn/src/int8layers/quantization_utils.cpp | 161 ++++++++++++++++-- modules/dnn/src/onnx/onnx_importer.cpp | 133 ++++++++++----- modules/dnn/test/test_onnx_importer.cpp | 19 ++- 4 files changed, 261 insertions(+), 60 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 46c5f338af..d74566e57c 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -422,16 +422,16 @@ CV__DNN_INLINE_NS_BEGIN class CV_EXPORTS QuantizeLayer : public Layer { public: - float scale; - int zeropoint; + std::vector scales; + std::vector zeropoints; static Ptr create(const LayerParams ¶ms); }; class CV_EXPORTS DequantizeLayer : public Layer { public: - float scale; - int zeropoint; + std::vector scales; + std::vector zeropoints; static Ptr create(const LayerParams ¶ms); }; diff --git a/modules/dnn/src/int8layers/quantization_utils.cpp b/modules/dnn/src/int8layers/quantization_utils.cpp index 6e2f0bb61c..a4a822efdd 100644 --- a/modules/dnn/src/int8layers/quantization_utils.cpp +++ b/modules/dnn/src/int8layers/quantization_utils.cpp @@ -11,14 +11,88 @@ namespace cv namespace dnn { +static void broadcast1D2TargetMat(Mat& data, const MatShape& targetShape, int axis) +{ + // The data is the 1-D scales or zeropoints. + CV_Assert(axis >= 0 && targetShape.size() > axis && data.total() == targetShape[axis]); + std::vector broadcast_axes; + for (int i = 0; i < targetShape.size(); i++) + { + if (i != axis) + broadcast_axes.push_back(i); + } + + MatShape subTargetShape = shape(data); + + // convert std::vector to 1D Mat. + for (auto broadcast_axis : broadcast_axes) + { + subTargetShape[broadcast_axis] = targetShape[broadcast_axis]; + data = data.reshape(0, total(data, 0, broadcast_axis)); + Mat tmp = cv::repeat(data, 1, subTargetShape[broadcast_axis]); + data = tmp.reshape(0, subTargetShape); + } +} + +static void broadcastScaleAndZeropoint(Mat& scalesMat, Mat& zeropointsMat, const std::vector& scales, + const std::vector& zeropoints, const MatShape& targetShape, int axis) +{ + // broad cast the scales and zeropoint to the input shape. + MatShape subTargetShape(targetShape.size(), 1); + subTargetShape[axis] = scales.size(); + + zeropointsMat.create(subTargetShape.size(), subTargetShape.data(), CV_32FC1); + scalesMat.create(subTargetShape.size(), subTargetShape.data(), CV_32FC1); + + const int len = scales.size(); + // Deep copy the scales and zeropoint data and prevent the original data from being changed. + + float * scalePtr = scalesMat.ptr(0); + for (int i = 0; i < len; i++) + scalePtr[i] = scales[i]; + + float * zpPtr = zeropointsMat.ptr(0); + for (int i = 0; i < len; i++) + zpPtr[i] = (float )zeropoints[i]; + + broadcast1D2TargetMat(scalesMat, targetShape, axis); + broadcast1D2TargetMat(zeropointsMat, targetShape, axis); +} + // Quantize FP32/FP16 Inputs to INT8 class QuantizeLayerImpl CV_FINAL : public QuantizeLayer { public: + int axis; + bool is1D; + Mat scalesMat, zeropointsMat; // Saving the broadcasetd scales data. + QuantizeLayerImpl(const LayerParams& params) { - scale = params.get("scales", 1.0f); - zeropoint = params.get("zeropoints", 0); + is1D = params.get("is1D", false); + axis = params.get("axis", 1); + if (!is1D) + { + scales.push_back(params.get("scales", 1.0f)); + zeropoints.push_back(params.get("zeropoints", 0)); + } + else + { + DictValue paramScales = params.get("scales"); + int i, n = paramScales.size(); + + CV_Assert(n > 0); + scales.resize(n, 0.); + for (i = 0; i < n; i++) + scales[i] = paramScales.get(i); + + zeropoints.resize(n, 0); + DictValue paramZp = params.get("zeropoints"); + n = paramZp.size(); + + for (i = 0; i < n; i++) + zeropoints[i] = paramZp.get(i); + } setParamsFrom(params); } @@ -42,6 +116,14 @@ public: std::vector inputs, outputs; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); + + axis = normalize_axis(axis, shape(inputs[0]).size()); + + if (is1D) + { + MatShape inputShape = shape(inputs[0]); + broadcastScaleAndZeropoint(scalesMat, zeropointsMat, scales, zeropoints, inputShape, axis); + } } #ifdef HAVE_OPENCL @@ -58,7 +140,7 @@ public: inputs[0] = inputFp32; // replace } - inputs[0].convertTo(outputs[0], CV_8S, 1.f/scale, zeropoint); + inputs[0].convertTo(outputs[0], CV_8S, 1.f/scales[0], zeropoints[0]); return true; } #endif @@ -68,14 +150,26 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && !is1D, forward_ocl(inputs_arr, outputs_arr, internals_arr)) std::vector inputs, outputs; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); - inputs[0].convertTo(outputs[0], CV_8S, 1.f/scale, zeropoint); + if (outputs[0].depth() != CV_8S) + outputs[0].convertTo(outputs[0], CV_8S); + + if (is1D) + { + Mat inputTmp; + divide(inputs[0], scalesMat, inputTmp); + subtract(inputTmp, zeropointsMat, inputTmp); + + inputTmp.convertTo(outputs[0], CV_8S); + } + else + inputs[0].convertTo(outputs[0], CV_8S, 1.f/scales[0], zeropoints[0]); } }; @@ -83,10 +177,38 @@ public: class DequantizeLayerImpl CV_FINAL : public DequantizeLayer { public: + int axis; + bool is1D; + Mat scalesMat, zeropointsMat; // Saving the broadcasetd scales data. + DequantizeLayerImpl(const LayerParams& params) { - scale = params.get("scales", 1.0f); - zeropoint = params.get("zeropoints", 0); + is1D = params.get("is1D", false); + axis = params.get("axis", 1); + + if (!is1D) + { + scales.push_back(params.get("scales", 1.0f)); + zeropoints.push_back(params.get("zeropoints", 0)); + } + else + { + DictValue paramScales = params.get("scales"); + int i, n = paramScales.size(); + + CV_Assert(n > 0); + scales.resize(n); + for (i = 0; i < n; i++) + scales[i] = paramScales.get(i); + + zeropoints.resize(n, 0); + DictValue paramZp = params.get("zeropoints"); + n = paramZp.size(); + + for (i = 0; i < n; i++) + zeropoints[i] = paramZp.get(i); + } + setParamsFrom(params); } @@ -110,6 +232,14 @@ public: std::vector inputs, outputs; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); + + axis = normalize_axis(axis, shape(inputs[0]).size()); + + if (is1D) + { + MatShape inputShape = shape(inputs[0]); + broadcastScaleAndZeropoint(scalesMat, zeropointsMat, scales, zeropoints, inputShape, axis); + } } #ifdef HAVE_OPENCL @@ -120,7 +250,7 @@ public: outputs_.getUMatVector(outputs); UMat outputFp32; - inputs[0].convertTo(outputFp32, CV_32F, scale, -(scale*zeropoint)); + inputs[0].convertTo(outputFp32, CV_32F, scales[0], -(scales[0]*zeropoints[0])); if (outputs_.depth() == CV_16S) convertFp16(outputFp32, outputs[0]); @@ -135,14 +265,25 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && !is1D, forward_ocl(inputs_arr, outputs_arr, internals_arr)) std::vector inputs, outputs; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); - inputs[0].convertTo(outputs[0], CV_32F, scale, -(scale*zeropoint)); + if (outputs[0].depth() != CV_32F) + outputs[0].convertTo(outputs[0], CV_32F); + + if (is1D) + { + Mat inputTmp; + inputs[0].convertTo(inputTmp, CV_32F); + subtract(inputTmp, zeropointsMat, inputTmp); + multiply(inputTmp, scalesMat, outputs[0]); + } + else + inputs[0].convertTo(outputs[0], CV_32F, scales[0], -(scales[0]*zeropoints[0])); } }; diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 9901df0dad..c420f3b5d1 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -53,7 +53,7 @@ extern bool DNN_DIAGNOSTICS_RUN; class ONNXLayerHandler; template -static T getScaleFromMat(Mat m) +static T getScalarFromMat(Mat m) { CV_Assert(m.total() == 1); return m.at(0); @@ -380,7 +380,10 @@ void runLayer(LayerParams& params, const std::vector& inputs, inpShapes[i] = shape(inputs[i]); if (i > 0 && ddepth != inputs[i].depth()) CV_Error(Error::StsNotImplemented, "Mixed input data types."); - ddepth = inputs[i].depth(); + + // Quantize and Dequantize layer have different output type than input. + if (params.type != "Quantize" && params.type != "Dequantize") + ddepth = inputs[i].depth(); } std::vector outShapes, internalShapes; @@ -3240,21 +3243,67 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx { CV_Assert(node_proto.input_size() == 2 || node_proto.input_size() == 3); layerParams.type = (node_proto.op_type() == "QuantizeLinear") ? "Quantize" : "Dequantize"; + int axis = layerParams.get("axis", 1); + // For QuantizeLinear and DequantizeLinear, the scale and zeropoint can be a Scalar (per-tensor quantized) + // or 1-D tensor (per-channel quantized). + bool is1D = false; - float scale = getScaleFromMat(getBlob(node_proto, 1)); - int zeropoint = 0; + Mat scaleMat = getBlob(node_proto, 1); + if(scaleMat.total() > 1) is1D = true; + + Mat zpMat; if (node_proto.input_size() == 3) - zeropoint = (int)getScaleFromMat(getBlob(node_proto, 2)); + { + zpMat = getBlob(node_proto, 2); + CV_Assert(zpMat.total() == scaleMat.total()); // zero point should has the same shape as scale. + } - layerParams.set("scales", scale); - layerParams.set("zeropoints", zeropoint); + if (is1D) + { + const int num = scaleMat.total(); + + std::vector zeropoints(num, 0); + std::vector scales(num, 0); + + for (int i = 0; i < num; i++) + { + scales[i] = scaleMat.at(i); + if (!zpMat.empty()) + zeropoints[i] = zpMat.depth() == CV_32S ? + zpMat.at(i) : (int)zpMat.at(i); + } + + layerParams.set("is1D", true); + layerParams.set("axis", axis); + layerParams.set("scales", DictValue::arrayReal(scales.data(), scales.size())); + layerParams.set("zeropoints", DictValue::arrayInt(zeropoints.data(), zeropoints.size())); + } + else + { + int zeropoint = zpMat.empty() ? 0 : zpMat.depth() == CV_32S ? + getScalarFromMat(zpMat) : (int)getScalarFromMat(zpMat); + float scale = getScalarFromMat(scaleMat); + + layerParams.set("is1D", false); + layerParams.set("scales", scale); + layerParams.set("zeropoints", zeropoint); + } if (layerParams.type == "Quantize") layerParams.set("depth", CV_8S); else // Dequantize layerParams.set("depth", CV_32F); - addLayer(layerParams, node_proto); + if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) // Variable input. + { + std::vector inputs, outputs; + inputs.push_back(getBlob(node_proto, 0)); + + runLayer(layerParams, inputs, outputs); + addConstant(node_proto.output(0), outputs[0]); + } + else + addLayer(layerParams, node_proto); } void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) @@ -3263,8 +3312,8 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP int ninputs = node_proto.input_size(); CV_Assert(ninputs == 8 || ninputs == 9); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int inp_zp = (int)getScaleFromMat(getBlob(node_proto, 2)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int inp_zp = (int)getScalarFromMat(getBlob(node_proto, 2)); if (layerParams.has("pad")) { @@ -3312,8 +3361,8 @@ void ONNXImporter::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeP bool per_channel = w_scale.total() == outCn; Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at(0))); - float out_sc = getScaleFromMat(getBlob(node_proto, 6)); - int8_t out_zp = getScaleFromMat(getBlob(node_proto, 7)); + float out_sc = getScalarFromMat(getBlob(node_proto, 6)); + int8_t out_zp = getScalarFromMat(getBlob(node_proto, 7)); Mat bias = (ninputs == 9) ? getBlob(node_proto, 8) : Mat::zeros(1, outCn, CV_32S); @@ -3349,8 +3398,8 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod int firstInpDims = outShapes[node_proto.input(0)].size(); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, 2)); Mat weights = getBlob(node_proto, 3).t(); int outCn = weights.size[0]; @@ -3361,8 +3410,8 @@ void ONNXImporter::parseQMatMul(LayerParams& layerParams, const opencv_onnx::Nod bool per_channel = w_scale.total() == outCn ? true : false; Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at(0))); - float out_sc = getScaleFromMat(getBlob(node_proto, 6)); - int8_t out_zp = getScaleFromMat(getBlob(node_proto, 7)); + float out_sc = getScalarFromMat(getBlob(node_proto, 6)); + int8_t out_zp = getScalarFromMat(getBlob(node_proto, 7)); Mat bias(1, outCn, CV_32S); Mat outputMultiplier(1, outCn, CV_32F); @@ -3411,8 +3460,8 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP int firstInpDims = outShapes[node_proto.input(0)].size(); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, 2)); int outCn = weights.size[0]; int secondInpDims = weights.dims; @@ -3431,8 +3480,8 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP CV_Error(Error::StsUnsupportedFormat, "The zero-point non-zero case of W is not supported!"); } - float out_sc = getScaleFromMat(getBlob(node_proto, 7)); - int8_t out_zp = ninputs == 9 ? getScaleFromMat(getBlob(node_proto, 8)) : 0; + float out_sc = getScalarFromMat(getBlob(node_proto, 7)); + int8_t out_zp = ninputs == 9 ? getScalarFromMat(getBlob(node_proto, 8)) : 0; Mat bias; if (constBlobs.find(node_proto.input(6)) != constBlobs.end()) @@ -3475,11 +3524,11 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No constId = i; } - float inp_0_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_0_zp = getScaleFromMat(getBlob(node_proto, 2)); + float inp_0_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_0_zp = getScalarFromMat(getBlob(node_proto, 2)); - float inp_1_sc = getScaleFromMat(getBlob(node_proto, 4)); - int8_t inp_1_zp = getScaleFromMat(getBlob(node_proto, 5)); + float inp_1_sc = getScalarFromMat(getBlob(node_proto, 4)); + int8_t inp_1_zp = getScalarFromMat(getBlob(node_proto, 5)); // Set 2nd input as the const input if (constId == 0) @@ -3488,11 +3537,11 @@ void ONNXImporter::parseQEltwise(LayerParams& layerParams, const opencv_onnx::No cv::swap(inp_0_zp, inp_1_zp); } - float out_sc = getScaleFromMat(getBlob(node_proto, 6)); + float out_sc = getScalarFromMat(getBlob(node_proto, 6)); int8_t out_zp = 0; if (node_proto.input_size() == 8) - out_zp = getScaleFromMat(getBlob(node_proto, 7)); + out_zp = getScalarFromMat(getBlob(node_proto, 7)); std::vector inp_scales = {inp_0_sc, inp_1_sc}; std::vector inp_zps = {inp_0_zp, inp_1_zp}; @@ -3608,10 +3657,10 @@ void ONNXImporter::parseQLeakyRelu(LayerParams& layerParams, const opencv_onnx:: CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); float slope = layerParams.get("alpha"); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); - float out_sc = getScaleFromMat(getBlob(node_proto, 3)); - int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, 2)); + float out_sc = getScalarFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScalarFromMat(getBlob(node_proto, 4)); Mat lookUpTable(1, 256, CV_8S); int8_t* table = lookUpTable.ptr(); @@ -3637,10 +3686,10 @@ void ONNXImporter::parseQSigmoid(LayerParams& layerParams, const opencv_onnx::No { CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); - float out_sc = getScaleFromMat(getBlob(node_proto, 3)); - int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, 2)); + float out_sc = getScalarFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScalarFromMat(getBlob(node_proto, 4)); Mat lookUpTable(1, 256, CV_8S); int8_t* table = lookUpTable.ptr(); @@ -3665,10 +3714,10 @@ void ONNXImporter::parseQAvgPool(LayerParams& layerParams, const opencv_onnx::No { CV_Assert(node_proto.input_size() == 4 || node_proto.input_size() == 5); - float inp_sc = getScaleFromMat(getBlob(node_proto, 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, 2)); - float out_sc = getScaleFromMat(getBlob(node_proto, 3)); - int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScaleFromMat(getBlob(node_proto, 4)); + float inp_sc = getScalarFromMat(getBlob(node_proto, 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, 2)); + float out_sc = getScalarFromMat(getBlob(node_proto, 3)); + int8_t out_zp = node_proto.input_size() == 4 ? 0 : getScalarFromMat(getBlob(node_proto, 4)); layerParams.type = "PoolingInt8"; layerParams.set("pool", "ave"); @@ -3687,13 +3736,13 @@ void ONNXImporter::parseQConcat(LayerParams& layerParams, const opencv_onnx::Nod layerParams.type = "ConcatInt8"; int num_inputs = node_proto.input_size(); - float out_scale = getScaleFromMat(getBlob(node_proto, 0)); - int8_t out_zp = getScaleFromMat(getBlob(node_proto, 1)); + float out_scale = getScalarFromMat(getBlob(node_proto, 0)); + int8_t out_zp = getScalarFromMat(getBlob(node_proto, 1)); for (int i = 2; i < num_inputs; i += 3) { - float inp_scale = getScaleFromMat(getBlob(node_proto, i + 1)); - int8_t inp_zp = getScaleFromMat(getBlob(node_proto, i + 2)); + float inp_scale = getScalarFromMat(getBlob(node_proto, i + 1)); + int8_t inp_zp = getScalarFromMat(getBlob(node_proto, i + 2)); if (inp_scale != out_scale || inp_zp != out_zp) { diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index c90195783c..b310dce808 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1824,11 +1824,22 @@ TEST_P(Test_ONNX_layers, Gemm) TEST_P(Test_ONNX_layers, Quantized_Convolution) { - testONNXModels("quantized_conv_uint8_weights", npy, 0.004, 0.02); - testONNXModels("quantized_conv_int8_weights", npy, 0.03, 0.5); - testONNXModels("quantized_conv_per_channel_weights", npy, 0.06, 0.4); + // The difference of QOperator and QDQ format: + // https://onnxruntime.ai/docs/performance/quantization.html#onnx-quantization-representation-format. + { + SCOPED_TRACE("QOperator quantized model."); + testONNXModels("quantized_conv_uint8_weights", npy, 0.004, 0.02); + testONNXModels("quantized_conv_int8_weights", npy, 0.03, 0.5); + testONNXModels("quantized_conv_per_channel_weights", npy, 0.06, 0.4); + testONNXModels("quantized_conv_asymmetric_pads_int8_weights"); + } - testONNXModels("quantized_conv_asymmetric_pads_int8_weights"); + { + SCOPED_TRACE("QDQ quantized model."); + testONNXModels("quantized_conv_uint8_weights_qdq", npy, 0.004, 0.02); + testONNXModels("quantized_conv_int8_weights_qdq", npy, 0.03, 0.5); + testONNXModels("quantized_conv_per_channel_weights_qdq", npy, 0.06, 0.4); + } } TEST_P(Test_ONNX_layers, Quantized_MatMul) From d1d8ac57f3259a92ecd411d77908c84ad3cae0ec Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 1 Nov 2022 18:24:10 +0100 Subject: [PATCH 191/313] Cocoa/highgui: Set activateIgnoringOtherApps --- modules/highgui/src/window_cocoa.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index 07c1ed86d2..3f44deb1f1 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -587,6 +587,8 @@ CV_IMPL int cvNamedWindow( const char* name, int flags ) [window setContentView:[[CVView alloc] init]]; + [NSApp activateIgnoringOtherApps:YES]; + [window setHasShadow:YES]; [window setAcceptsMouseMovedEvents:YES]; [window useOptimizedDrawing:YES]; From 17f2b5629167bb92687c9afaf54b748942ee7049 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Wed, 2 Nov 2022 10:45:16 +0800 Subject: [PATCH 192/313] remove never used code in onnximporter --- modules/dnn/src/onnx/onnx_importer.cpp | 33 -------------------------- 1 file changed, 33 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index c420f3b5d1..66f524a14a 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2053,39 +2053,6 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node addLayer(layerParams, node_proto); } -void findBroadAxis(const MatShape& broadShape, const MatShape& outShape, size_t& axis, int& broadAxis) -{ - // Currently, this function can only complete 1-dimensional expansion of broadShape. - // If there are two dimensions in broadShape that need to be expended, it will fail. - const size_t diff = outShape.size() - broadShape.size(); - - // find the first non-one element of the broadcasting shape - axis = 0; - for (; axis < broadShape.size() && broadShape[axis] == 1; ++axis) {} - - // find the last non-one element of the broadcasting shape - size_t endAxis = broadShape.size(); - for (; endAxis > axis && broadShape[endAxis - 1] == 1; --endAxis) {} - - // find one between axis and endAxis - as it needs to be broadcasted, - // dimensions from the left of axis and from the right of endAxis will be handled by Scale layer - broadAxis = -1; - for (size_t i = axis; i < endAxis; ++i) - { - size_t outAxis = i + diff; - if (outShape[outAxis] == broadShape[i]) - { - continue; - } - - // ensure we need to broadcast only 1 dimension in the middle - CV_Assert(broadShape[i] == 1 && broadAxis == -1); - broadAxis = static_cast(outAxis); - } - - axis += diff; -} - void ONNXImporter::parseConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { opencv_onnx::NodeProto node_proto = node_proto_; From 11d492b0b90a8b2fd3fedc2b99e6750fc2babffa Mon Sep 17 00:00:00 2001 From: zoom Date: Fri, 21 Oct 2022 13:11:22 +0800 Subject: [PATCH 193/313] Let part of the operators in nary_eltwise support cuda --- modules/dnn/perf/perf_layer.cpp | 12 ++++++ .../dnn/src/layers/nary_eltwise_layers.cpp | 41 +++++++++++++++++++ modules/dnn/src/op_cuda.cpp | 7 +++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/modules/dnn/perf/perf_layer.cpp b/modules/dnn/perf/perf_layer.cpp index f169f4e6a8..ffe0240a18 100644 --- a/modules/dnn/perf/perf_layer.cpp +++ b/modules/dnn/perf/perf_layer.cpp @@ -55,6 +55,8 @@ struct Layer_Slice : public TestBaseWithParam > } }; +static std::set nary_eltwise_cuda_deny_ops = {"add", "equal", "greater", "less", "mean", "mul", "pow", "sub"}; + struct Layer_NaryEltwise : public TestBaseWithParam > { void test_layer(const std::vector& a_shape, const std::vector& b_shape, const String op, bool isRef = false) @@ -62,6 +64,13 @@ struct Layer_NaryEltwise : public TestBaseWithParam > int backendId = get<0>(GetParam()); int targetId = get<1>(GetParam()); + if (!isRef && backendId == DNN_BACKEND_CUDA) + { + if (a_shape != b_shape) + throw SkipTestException("The test is skipped because inputs with different shapes are not supported."); + if (nary_eltwise_cuda_deny_ops.find(op) != nary_eltwise_cuda_deny_ops.end()) + throw SkipTestException("The operator '" + op + "' is skipped because is not support with cuda currently."); + } Mat a(a_shape, CV_32FC1); Mat b(b_shape, CV_32FC1); @@ -410,6 +419,9 @@ PERF_TEST_P_(Layer_ScatterND, DISABLED_ScatterND_add) INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(false, false)); INSTANTIATE_TEST_CASE_P(/**/, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); +#ifdef HAVE_CUDA +INSTANTIATE_TEST_CASE_P(CUDA, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_CUDA, DNN_TARGET_CUDA))); +#endif INSTANTIATE_TEST_CASE_P(/**/, Layer_Scatter, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); INSTANTIATE_TEST_CASE_P(/**/, Layer_ScatterND, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index 9431ff2b4b..ecd8b7351a 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -4,12 +4,18 @@ #include "../precomp.hpp" #include "layers_common.hpp" +#include "../op_cuda.hpp" #include #include #include #include +#ifdef HAVE_CUDA +#include "../cuda4dnn/primitives/eltwise.hpp" +using namespace cv::dnn::cuda4dnn; +#endif + namespace cv { namespace dnn @@ -91,6 +97,9 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { + if (op == OPERATION::MAX || op == OPERATION::MIN || op == OPERATION::SUM || + op == OPERATION::PROD || op == OPERATION::DIV) + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA; return backendId == DNN_BACKEND_OPENCV; } @@ -641,6 +650,38 @@ public: }; } +#ifdef HAVE_CUDA + Ptr initCUDA( + void *context_, + const std::vector>& inputs, + const std::vector>& outputs + ) override + { + auto context = reinterpret_cast(context_); + + auto input_wrapper = inputs[0].dynamicCast(); + for (int i = 1; i < inputs.size(); i++) + { + auto from_wrapper = inputs[i].dynamicCast(); + if (input_wrapper->getShape() != from_wrapper->getShape()) + return Ptr(); + } + + auto op_ = [this] { + switch (op) { + case OPERATION::MAX: return cuda4dnn::EltwiseOpType::MAX; + case OPERATION::MIN: return cuda4dnn::EltwiseOpType::MIN; + case OPERATION::SUM: return cuda4dnn::EltwiseOpType::SUM; + case OPERATION::PROD: return cuda4dnn::EltwiseOpType::PRODUCT; + case OPERATION::DIV: return cuda4dnn::EltwiseOpType::DIV; + default: CV_Error(Error::StsNotImplemented, "Other operators except MAX, MIN, SUM, PRODUCT and DIV are not supported with cuda."); + } + }(); + + return make_cuda_node(preferableTarget, std::move(context->stream), op_, std::vector()); + } +#endif + virtual bool tryQuantize(const std::vector > &scales, const std::vector > &zeropoints, LayerParams& params) CV_OVERRIDE { diff --git a/modules/dnn/src/op_cuda.cpp b/modules/dnn/src/op_cuda.cpp index a1b588ecfb..46e68f7689 100644 --- a/modules/dnn/src/op_cuda.cpp +++ b/modules/dnn/src/op_cuda.cpp @@ -86,8 +86,11 @@ void Net::Impl::initCUDABackend(const std::vector& blobsToKeep_) auto node = layerInstance->initCUDA(&context, ld.inputBlobsWrappers, ld.outputBlobsWrappers); ld.backendNodes[DNN_BACKEND_CUDA] = node; - auto cudaNode = node.dynamicCast(); - cudaInfo->workspace.require(cudaNode->get_workspace_memory_in_bytes()); + if(!node.empty()) + { + auto cudaNode = node.dynamicCast(); + cudaInfo->workspace.require(cudaNode->get_workspace_memory_in_bytes()); + } } if (blobsToKeep_.size() > 1) From 39f995e31915e021b1d2661151893380b900680b Mon Sep 17 00:00:00 2001 From: zoom Date: Thu, 3 Nov 2022 16:54:08 +0800 Subject: [PATCH 194/313] Fix the problem that "ADE" downloaded from GitCode doesn't work. --- cmake/mirrors/gitcode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/mirrors/gitcode.cmake b/cmake/mirrors/gitcode.cmake index ec0893f3f7..c9d41e7458 100644 --- a/cmake/mirrors/gitcode.cmake +++ b/cmake/mirrors/gitcode.cmake @@ -64,7 +64,7 @@ macro(ocv_download_url_gitcode_archive_release SUBDIR) list(GET DL_URL_split 4 __REPO_NAME) set(DL_URL "https://gitcode.net/${__OWNER}/${__REPO_NAME}/-/archive/${${DL_ID}_RELEASE_GITCODE}/${__REPO_NAME}-") set(DL_HASH "${${DL_ID}_PKG_MD5_GITCODE}") - set(SUBDIR "${${DL_ID}_PKG_NAME_GITCODE}" PARENT_SCOPE) + set(${SUBDIR} "${${DL_ID}_PKG_NAME_GITCODE}" PARENT_SCOPE) else() message(WARNING "Package ${DL_ID} from mirror gitcode.net is outdated and will be downloaded from github.com instead.") endif() From 63bff33e85567962f6acf52b51793680a526f63c Mon Sep 17 00:00:00 2001 From: Juha Reunanen Date: Mon, 7 Nov 2022 13:40:02 +0200 Subject: [PATCH 195/313] Fix floodFill for very large images --- modules/imgproc/src/floodfill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/floodfill.cpp b/modules/imgproc/src/floodfill.cpp index 8595011d48..926c48e65d 100644 --- a/modules/imgproc/src/floodfill.cpp +++ b/modules/imgproc/src/floodfill.cpp @@ -283,7 +283,7 @@ floodFillGrad_CnIR( Mat& image, Mat& msk, Diff diff, ConnectedComp* region, int flags, std::vector* buffer ) { - int step = (int)image.step, maskStep = (int)msk.step; + size_t step = image.step, maskStep = msk.step; uchar* pImage = image.ptr(); _Tp* img = (_Tp*)(pImage + step*seed.y); uchar* pMask = msk.ptr() + maskStep + sizeof(_MTp); From 2aad039b4f64d8e4a6136388c48d1368c120feec Mon Sep 17 00:00:00 2001 From: Anatoliy Talamanov Date: Tue, 8 Nov 2022 11:43:38 +0000 Subject: [PATCH 196/313] Merge pull request #22494 from TolyaTalamanov:at/expose-all-core-to-python G-API Expose all core operations to python * Expose all G-API core operations to python * Fix typo in python gapi types test --- modules/gapi/include/opencv2/gapi/core.hpp | 150 +++++++++--------- modules/gapi/include/opencv2/gapi/gcommon.hpp | 8 +- modules/gapi/include/opencv2/gapi/gscalar.hpp | 1 + modules/gapi/include/opencv2/gapi/imgproc.hpp | 4 +- modules/gapi/include/opencv2/gapi/s11n.hpp | 3 + .../gapi/misc/python/package/gapi/__init__.py | 11 ++ modules/gapi/misc/python/pyopencv_gapi.hpp | 7 + modules/gapi/misc/python/python_bridge.hpp | 3 + .../gapi/misc/python/test/test_gapi_core.py | 29 +++- .../gapi/misc/python/test/test_gapi_types.py | 11 +- .../src/backends/common/serialization.cpp | 8 + 11 files changed, 149 insertions(+), 86 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/core.hpp b/modules/gapi/include/opencv2/gapi/core.hpp index f46f159adf..6351e22114 100644 --- a/modules/gapi/include/opencv2/gapi/core.hpp +++ b/modules/gapi/include/opencv2/gapi/core.hpp @@ -635,7 +635,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref */ GAPI_EXPORTS_W GMat addC(const GMat& src1, const GScalar& c, int ddepth = -1); //! @overload -GAPI_EXPORTS GMat addC(const GScalar& c, const GMat& src1, int ddepth = -1); +GAPI_EXPORTS_W GMat addC(const GScalar& c, const GMat& src1, int ddepth = -1); /** @brief Calculates the per-element difference between two matrices. @@ -660,7 +660,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. @sa add, addC */ -GAPI_EXPORTS GMat sub(const GMat& src1, const GMat& src2, int ddepth = -1); +GAPI_EXPORTS_W GMat sub(const GMat& src1, const GMat& src2, int ddepth = -1); /** @brief Calculates the per-element difference between matrix and given scalar. @@ -679,7 +679,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. @sa add, addC, subRC */ -GAPI_EXPORTS GMat subC(const GMat& src, const GScalar& c, int ddepth = -1); +GAPI_EXPORTS_W GMat subC(const GMat& src, const GScalar& c, int ddepth = -1); /** @brief Calculates the per-element difference between given scalar and the matrix. @@ -698,7 +698,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. @sa add, addC, subC */ -GAPI_EXPORTS GMat subRC(const GScalar& c, const GMat& src, int ddepth = -1); +GAPI_EXPORTS_W GMat subRC(const GScalar& c, const GMat& src, int ddepth = -1); /** @brief Calculates the per-element scaled product of two matrices. @@ -719,7 +719,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. @sa add, sub, div, addWeighted */ -GAPI_EXPORTS GMat mul(const GMat& src1, const GMat& src2, double scale = 1.0, int ddepth = -1); +GAPI_EXPORTS_W GMat mul(const GMat& src1, const GMat& src2, double scale = 1.0, int ddepth = -1); /** @brief Multiplies matrix by scalar. @@ -737,11 +737,11 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth. @sa add, sub, div, addWeighted */ -GAPI_EXPORTS GMat mulC(const GMat& src, double multiplier, int ddepth = -1); +GAPI_EXPORTS_W GMat mulC(const GMat& src, double multiplier, int ddepth = -1); //! @overload -GAPI_EXPORTS GMat mulC(const GMat& src, const GScalar& multiplier, int ddepth = -1); // FIXME: merge with mulc +GAPI_EXPORTS_W GMat mulC(const GMat& src, const GScalar& multiplier, int ddepth = -1); // FIXME: merge with mulc //! @overload -GAPI_EXPORTS GMat mulC(const GScalar& multiplier, const GMat& src, int ddepth = -1); // FIXME: merge with mulc +GAPI_EXPORTS_W GMat mulC(const GScalar& multiplier, const GMat& src, int ddepth = -1); // FIXME: merge with mulc /** @brief Performs per-element division of two matrices. @@ -764,7 +764,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix; you can only pass -1 when src1.depth() == src2.depth(). @sa mul, add, sub */ -GAPI_EXPORTS GMat div(const GMat& src1, const GMat& src2, double scale, int ddepth = -1); +GAPI_EXPORTS_W GMat div(const GMat& src1, const GMat& src2, double scale, int ddepth = -1); /** @brief Divides matrix by scalar. @@ -785,7 +785,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param scale scale factor. @sa add, sub, div, addWeighted */ -GAPI_EXPORTS GMat divC(const GMat& src, const GScalar& divisor, double scale, int ddepth = -1); +GAPI_EXPORTS_W GMat divC(const GMat& src, const GScalar& divisor, double scale, int ddepth = -1); /** @brief Divides scalar by matrix. @@ -806,7 +806,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param scale scale factor @sa add, sub, div, addWeighted */ -GAPI_EXPORTS GMat divRC(const GScalar& divident, const GMat& src, double scale, int ddepth = -1); +GAPI_EXPORTS_W GMat divRC(const GScalar& divident, const GMat& src, double scale, int ddepth = -1); /** @brief Applies a mask to a matrix. @@ -819,7 +819,7 @@ Supported src matrix data types are @ref CV_8UC1, @ref CV_16SC1, @ref CV_16UC1. @param src input matrix. @param mask input mask matrix. */ -GAPI_EXPORTS GMat mask(const GMat& src, const GMat& mask); +GAPI_EXPORTS_W GMat mask(const GMat& src, const GMat& mask); /** @brief Calculates an average (mean) of matrix elements. @@ -854,8 +854,8 @@ Both output must have the same size and depth as input matrices. degrees, otherwise, they are measured in radians. @sa cartToPolar, exp, log, pow, sqrt */ -GAPI_EXPORTS std::tuple polarToCart(const GMat& magnitude, const GMat& angle, - bool angleInDegrees = false); +GAPI_EXPORTS_W std::tuple polarToCart(const GMat& magnitude, const GMat& angle, + bool angleInDegrees = false); /** @brief Calculates the magnitude and angle of 2D vectors. @@ -878,8 +878,8 @@ x; the angles are measured in radians (from 0 to 2\*Pi) or in degrees (0 to 360 in radians (which is by default), or in degrees. @sa polarToCart */ -GAPI_EXPORTS std::tuple cartToPolar(const GMat& x, const GMat& y, - bool angleInDegrees = false); +GAPI_EXPORTS_W std::tuple cartToPolar(const GMat& x, const GMat& y, + bool angleInDegrees = false); /** @brief Calculates the rotation angle of 2D vectors. @@ -896,7 +896,7 @@ same size and the same type as x. degrees, otherwise, they are measured in radians. @return array of vector angles; it has the same size and same type as x. */ -GAPI_EXPORTS GMat phase(const GMat& x, const GMat &y, bool angleInDegrees = false); +GAPI_EXPORTS_W GMat phase(const GMat& x, const GMat &y, bool angleInDegrees = false); /** @brief Calculates a square root of array elements. @@ -907,7 +907,7 @@ std::sqrt . @param src input floating-point array. @return output array of the same size and type as src. */ -GAPI_EXPORTS GMat sqrt(const GMat &src); +GAPI_EXPORTS_W GMat sqrt(const GMat &src); //! @} gapi_math //! @@ -934,11 +934,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_16UC1, @ref CV_16SC1 @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpLE, cmpGE, cmpLT */ -GAPI_EXPORTS GMat cmpGT(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpGT(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGTScalar" */ -GAPI_EXPORTS GMat cmpGT(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpGT(const GMat& src1, const GScalar& src2); /** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less than elements in second. @@ -960,11 +960,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpLE, cmpGE, cmpGT */ -GAPI_EXPORTS GMat cmpLT(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpLT(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLTScalar" */ -GAPI_EXPORTS GMat cmpLT(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpLT(const GMat& src1, const GScalar& src2); /** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are greater or equal compare to elements in second. @@ -986,11 +986,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpLE, cmpGT, cmpLT */ -GAPI_EXPORTS GMat cmpGE(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpGE(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLGEcalar" */ -GAPI_EXPORTS GMat cmpGE(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpGE(const GMat& src1, const GScalar& src2); /** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less or equal compare to elements in second. @@ -1012,11 +1012,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpGT, cmpGE, cmpLT */ -GAPI_EXPORTS GMat cmpLE(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpLE(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLEScalar" */ -GAPI_EXPORTS GMat cmpLE(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpLE(const GMat& src1, const GScalar& src2); /** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are equal to elements in second. @@ -1038,11 +1038,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpNE */ -GAPI_EXPORTS GMat cmpEQ(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpEQ(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpEQScalar" */ -GAPI_EXPORTS GMat cmpEQ(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpEQ(const GMat& src1, const GScalar& src2); /** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are not equal to elements in second. @@ -1064,11 +1064,11 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix/scalar of the same depth as first input matrix. @sa min, max, threshold, cmpEQ */ -GAPI_EXPORTS GMat cmpNE(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat cmpNE(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.compare.cmpNEScalar" */ -GAPI_EXPORTS GMat cmpNE(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat cmpNE(const GMat& src1, const GScalar& src2); /** @brief computes bitwise conjunction of the two matrixes (src1 & src2) Calculates the per-element bit-wise logical conjunction of two matrices of the same size. @@ -1086,13 +1086,13 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src1 first input matrix. @param src2 second input matrix. */ -GAPI_EXPORTS GMat bitwise_and(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat bitwise_and(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.bitwise_andS" @param src1 first input matrix. @param src2 scalar, which will be per-lemenetly conjuncted with elements of src1. */ -GAPI_EXPORTS GMat bitwise_and(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat bitwise_and(const GMat& src1, const GScalar& src2); /** @brief computes bitwise disjunction of the two matrixes (src1 | src2) Calculates the per-element bit-wise logical disjunction of two matrices of the same size. @@ -1110,13 +1110,13 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src1 first input matrix. @param src2 second input matrix. */ -GAPI_EXPORTS GMat bitwise_or(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat bitwise_or(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.bitwise_orS" @param src1 first input matrix. @param src2 scalar, which will be per-lemenetly disjuncted with elements of src1. */ -GAPI_EXPORTS GMat bitwise_or(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat bitwise_or(const GMat& src1, const GScalar& src2); /** @brief computes bitwise logical "exclusive or" of the two matrixes (src1 ^ src2) @@ -1135,13 +1135,13 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src1 first input matrix. @param src2 second input matrix. */ -GAPI_EXPORTS GMat bitwise_xor(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat bitwise_xor(const GMat& src1, const GMat& src2); /** @overload @note Function textual ID is "org.opencv.core.pixelwise.bitwise_xorS" @param src1 first input matrix. @param src2 scalar, for which per-lemenet "logical or" operation on elements of src1 will be performed. */ -GAPI_EXPORTS GMat bitwise_xor(const GMat& src1, const GScalar& src2); +GAPI_EXPORTS_W GMat bitwise_xor(const GMat& src1, const GScalar& src2); /** @brief Inverts every bit of an array. @@ -1162,7 +1162,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src input matrix. */ -GAPI_EXPORTS GMat bitwise_not(const GMat& src); +GAPI_EXPORTS_W GMat bitwise_not(const GMat& src); /** @brief Select values from either first or second of input matrices by given mask. The function set to the output matrix either the value from the first input matrix if corresponding value of mask matrix is 255, @@ -1178,7 +1178,7 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix. @param mask mask input matrix. */ -GAPI_EXPORTS GMat select(const GMat& src1, const GMat& src2, const GMat& mask); +GAPI_EXPORTS_W GMat select(const GMat& src1, const GMat& src2, const GMat& mask); //! @} gapi_pixelwise @@ -1200,7 +1200,7 @@ Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @param src2 second input matrix of the same size and depth as src1. @sa max, cmpEQ, cmpLT, cmpLE */ -GAPI_EXPORTS GMat min(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat min(const GMat& src1, const GMat& src2); /** @brief Calculates per-element maximum of two matrices. @@ -1217,7 +1217,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src2 second input matrix of the same size and depth as src1. @sa min, compare, cmpEQ, cmpGT, cmpGE */ -GAPI_EXPORTS GMat max(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat max(const GMat& src1, const GMat& src2); /** @brief Calculates the per-element absolute difference between two matrices. @@ -1234,7 +1234,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src2 second input matrix. @sa abs */ -GAPI_EXPORTS GMat absDiff(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat absDiff(const GMat& src1, const GMat& src2); /** @brief Calculates absolute value of matrix elements. @@ -1251,7 +1251,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param c scalar to be subtracted. @sa min, max */ -GAPI_EXPORTS GMat absDiffC(const GMat& src, const GScalar& c); +GAPI_EXPORTS_W GMat absDiffC(const GMat& src, const GScalar& c); /** @brief Calculates sum of all matrix elements. @@ -1263,7 +1263,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src input matrix. @sa countNonZero, mean, min, max */ -GAPI_EXPORTS GScalar sum(const GMat& src); +GAPI_EXPORTS_W GScalar sum(const GMat& src); /** @brief Counts non-zero array elements. @@ -1276,7 +1276,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_16UC1, @ref CV_16SC1, @ref @param src input single-channel matrix. @sa mean, min, max */ -GAPI_EXPORTS GOpaque countNonZero(const GMat& src); +GAPI_EXPORTS_W GOpaque countNonZero(const GMat& src); /** @brief Calculates the weighted sum of two matrices. @@ -1299,7 +1299,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param ddepth optional depth of the output matrix. @sa add, sub */ -GAPI_EXPORTS GMat addWeighted(const GMat& src1, double alpha, const GMat& src2, double beta, double gamma, int ddepth = -1); +GAPI_EXPORTS_W GMat addWeighted(const GMat& src1, double alpha, const GMat& src2, double beta, double gamma, int ddepth = -1); /** @brief Calculates the absolute L1 norm of a matrix. @@ -1322,7 +1322,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src input matrix. @sa normL2, normInf */ -GAPI_EXPORTS GScalar normL1(const GMat& src); +GAPI_EXPORTS_W GScalar normL1(const GMat& src); /** @brief Calculates the absolute L2 norm of a matrix. @@ -1344,7 +1344,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src input matrix. @sa normL1, normInf */ -GAPI_EXPORTS GScalar normL2(const GMat& src); +GAPI_EXPORTS_W GScalar normL2(const GMat& src); /** @brief Calculates the absolute infinite norm of a matrix. @@ -1367,7 +1367,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src input matrix. @sa normL1, normL2 */ -GAPI_EXPORTS GScalar normInf(const GMat& src); +GAPI_EXPORTS_W GScalar normInf(const GMat& src); /** @brief Calculates the integral of an image. @@ -1387,7 +1387,7 @@ The function return integral image as \f$(W+1)\times (H+1)\f$ , 32-bit integer o CV_64F. @param sqdepth desired depth of the integral image of squared pixel values, CV_32F or CV_64F. */ -GAPI_EXPORTS std::tuple integral(const GMat& src, int sdepth = -1, int sqdepth = -1); +GAPI_EXPORTS_W std::tuple integral(const GMat& src, int sdepth = -1, int sqdepth = -1); /** @brief Applies a fixed-level threshold to each matrix element. @@ -1416,7 +1416,7 @@ types. @sa min, max, cmpGT, cmpLE, cmpGE, cmpLT */ -GAPI_EXPORTS GMat threshold(const GMat& src, const GScalar& thresh, const GScalar& maxval, int type); +GAPI_EXPORTS_W GMat threshold(const GMat& src, const GScalar& thresh, const GScalar& maxval, int type); /** @overload This function applicable for all threshold types except CV_THRESH_OTSU and CV_THRESH_TRIANGLE @note Function textual ID is "org.opencv.core.matrixop.thresholdOT" @@ -1438,7 +1438,7 @@ Input and output matrices must be CV_8UC1. @sa threshold */ -GAPI_EXPORTS GMat inRange(const GMat& src, const GScalar& threshLow, const GScalar& threshUp); +GAPI_EXPORTS_W GMat inRange(const GMat& src, const GScalar& threshLow, const GScalar& threshUp); //! @} gapi_matrixop @@ -1462,7 +1462,7 @@ The function split4 does the reverse operation. @param src4 fourth input @ref CV_8UC1 matrix to be merged. @sa merge3, split4, split3 */ -GAPI_EXPORTS GMat merge4(const GMat& src1, const GMat& src2, const GMat& src3, const GMat& src4); +GAPI_EXPORTS_W GMat merge4(const GMat& src1, const GMat& src2, const GMat& src3, const GMat& src4); /** @brief Creates one 3-channel matrix out of 3 single-channel ones. @@ -1481,7 +1481,7 @@ The function split3 does the reverse operation. @param src3 third input @ref CV_8UC1 matrix to be merged. @sa merge4, split4, split3 */ -GAPI_EXPORTS GMat merge3(const GMat& src1, const GMat& src2, const GMat& src3); +GAPI_EXPORTS_W GMat merge3(const GMat& src1, const GMat& src2, const GMat& src3); /** @brief Divides a 4-channel matrix into 4 single-channel matrices. @@ -1498,7 +1498,7 @@ The function merge4 does the reverse operation. @param src input @ref CV_8UC4 matrix. @sa split3, merge3, merge4 */ -GAPI_EXPORTS std::tuple split4(const GMat& src); +GAPI_EXPORTS_W std::tuple split4(const GMat& src); /** @brief Divides a 3-channel matrix into 3 single-channel matrices. @@ -1548,9 +1548,9 @@ borderMode=BORDER_TRANSPARENT, it means that the pixels in the destination image corresponds to the "outliers" in the source image are not modified by the function. @param borderValue Value used in case of a constant border. By default, it is 0. */ -GAPI_EXPORTS GMat remap(const GMat& src, const Mat& map1, const Mat& map2, - int interpolation, int borderMode = BORDER_CONSTANT, - const Scalar& borderValue = Scalar()); +GAPI_EXPORTS_W GMat remap(const GMat& src, const Mat& map1, const Mat& map2, + int interpolation, int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); /** @brief Flips a 2D matrix around vertical, horizontal, or both axes. @@ -1587,7 +1587,7 @@ flipping around y-axis. Negative value (for example, -1) means flipping around both axes. @sa remap */ -GAPI_EXPORTS GMat flip(const GMat& src, int flipCode); +GAPI_EXPORTS_W GMat flip(const GMat& src, int flipCode); /** @brief Crops a 2D matrix. @@ -1601,7 +1601,7 @@ Output matrix must be of the same depth as input one, size is specified by given @param rect a rect to crop a matrix to @sa resize */ -GAPI_EXPORTS GMat crop(const GMat& src, const Rect& rect); +GAPI_EXPORTS_W GMat crop(const GMat& src, const Rect& rect); /** @brief Applies horizontal concatenation to given matrices. @@ -1629,7 +1629,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src2 second input matrix to be considered for horizontal concatenation. @sa concatVert */ -GAPI_EXPORTS GMat concatHor(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat concatHor(const GMat& src1, const GMat& src2); /** @overload The function horizontally concatenates given number of GMat matrices (with the same number of columns). @@ -1637,7 +1637,7 @@ Output matrix must the same number of columns and depth as the input matrices, a @param v vector of input matrices to be concatenated horizontally. */ -GAPI_EXPORTS GMat concatHor(const std::vector &v); +GAPI_EXPORTS_W GMat concatHor(const std::vector &v); /** @brief Applies vertical concatenation to given matrices. @@ -1669,7 +1669,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref @param src2 second input matrix to be considered for vertical concatenation. @sa concatHor */ -GAPI_EXPORTS GMat concatVert(const GMat& src1, const GMat& src2); +GAPI_EXPORTS_W GMat concatVert(const GMat& src1, const GMat& src2); /** @overload The function vertically concatenates given number of GMat matrices (with the same number of columns). @@ -1677,7 +1677,7 @@ Output matrix must the same number of columns and depth as the input matrices, a @param v vector of input matrices to be concatenated vertically. */ -GAPI_EXPORTS GMat concatVert(const std::vector &v); +GAPI_EXPORTS_W GMat concatVert(const std::vector &v); /** @brief Performs a look-up table transform of a matrix. @@ -1696,7 +1696,7 @@ Output is a matrix of the same size and number of channels as src, and the same either have a single channel (in this case the same table is used for all channels) or the same number of channels as in the input matrix. */ -GAPI_EXPORTS GMat LUT(const GMat& src, const Mat& lut); +GAPI_EXPORTS_W GMat LUT(const GMat& src, const Mat& lut); /** @brief Converts a matrix to another data depth with optional scaling. @@ -1713,7 +1713,7 @@ same as the input has; if rdepth is negative, the output matrix will have the sa @param alpha optional scale factor. @param beta optional delta added to the scaled values. */ -GAPI_EXPORTS GMat convertTo(const GMat& src, int rdepth, double alpha=1, double beta=0); +GAPI_EXPORTS_W GMat convertTo(const GMat& src, int rdepth, double alpha=1, double beta=0); /** @brief Normalizes the norm or value range of an array. @@ -1735,8 +1735,8 @@ normalization. number of channels as src and the depth =ddepth. @sa norm, Mat::convertTo */ -GAPI_EXPORTS GMat normalize(const GMat& src, double alpha, double beta, - int norm_type, int ddepth = -1); +GAPI_EXPORTS_W GMat normalize(const GMat& src, double alpha, double beta, + int norm_type, int ddepth = -1); /** @brief Applies a perspective transformation to an image. @@ -1759,8 +1759,8 @@ optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation ( @sa warpAffine, resize, remap, getRectSubPix, perspectiveTransform */ -GAPI_EXPORTS GMat warpPerspective(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, - int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); +GAPI_EXPORTS_W GMat warpPerspective(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, + int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); /** @brief Applies an affine transformation to an image. @@ -1784,8 +1784,8 @@ borderMode=#BORDER_TRANSPARENT isn't supported @sa warpPerspective, resize, remap, getRectSubPix, transform */ -GAPI_EXPORTS GMat warpAffine(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, - int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); +GAPI_EXPORTS_W GMat warpAffine(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, + int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); //! @} gapi_transform /** @brief Finds centers of clusters and groups input samples around the clusters. @@ -1834,7 +1834,7 @@ compactness value are returned by the function. - Integer array that stores the cluster indices for every sample. - Array of the cluster centers. */ -GAPI_EXPORTS std::tuple,GMat,GMat> +GAPI_EXPORTS_W std::tuple,GMat,GMat> kmeans(const GMat& data, const int K, const GMat& bestLabels, const TermCriteria& criteria, const int attempts, const KmeansFlags flags); @@ -1857,7 +1857,7 @@ kmeans(const GArray& data, const int K, const GArray& bestLabels, /** @overload @note Function textual ID is "org.opencv.core.kmeans3D" */ -GAPI_EXPORTS std::tuple,GArray,GArray> +GAPI_EXPORTS_W std::tuple,GArray,GArray> kmeans(const GArray& data, const int K, const GArray& bestLabels, const TermCriteria& criteria, const int attempts, const KmeansFlags flags); @@ -1873,7 +1873,7 @@ The function transposes the matrix: @param src input array. */ -GAPI_EXPORTS GMat transpose(const GMat& src); +GAPI_EXPORTS_W GMat transpose(const GMat& src); namespace streaming { @@ -1903,7 +1903,7 @@ GAPI_EXPORTS_W GOpaque size(const GOpaque& r); @param src Input frame @return Size (frame dimensions). */ -GAPI_EXPORTS GOpaque size(const GFrame& src); +GAPI_EXPORTS_W GOpaque size(const GFrame& src); } //namespace streaming } //namespace gapi } //namespace cv diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp index dfbd9e3a2a..b08baaa365 100644 --- a/modules/gapi/include/opencv2/gapi/gcommon.hpp +++ b/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -51,6 +51,7 @@ namespace detail CV_STRING, // std::string user G-API data CV_POINT, // cv::Point user G-API data CV_POINT2F, // cv::Point2f user G-API data + CV_POINT3F, // cv::Point3f user G-API data CV_SIZE, // cv::Size user G-API data CV_RECT, // cv::Rect user G-API data CV_SCALAR, // cv::Scalar user G-API data @@ -72,16 +73,17 @@ namespace detail template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_SCALAR; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT2F; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT3F; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_RECT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_DRAW_PRIM; }; using GOpaqueTraitsArrayTypes = std::tuple; + cv::Point3f, cv::Mat, cv::Rect, cv::gapi::wip::draw::Prim>; // GOpaque is not supporting cv::Mat and cv::Scalar since there are GScalar and GMat types - using GOpaqueTraitsOpaqueTypes = std::tuple; + using GOpaqueTraitsOpaqueTypes = std::tuple; } // namespace detail // This definition is here because it is reused by both public(?) and internal diff --git a/modules/gapi/include/opencv2/gapi/gscalar.hpp b/modules/gapi/include/opencv2/gapi/gscalar.hpp index d8a47c8ea8..43d12c782a 100644 --- a/modules/gapi/include/opencv2/gapi/gscalar.hpp +++ b/modules/gapi/include/opencv2/gapi/gscalar.hpp @@ -67,6 +67,7 @@ public: * * @param s a cv::Scalar value to associate with this GScalar object. */ + GAPI_WRAP explicit GScalar(const cv::Scalar& s); /** diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp index 44f0528153..cad7a403c9 100644 --- a/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -654,8 +654,8 @@ center. @param borderValue border value in case of constant border type @sa boxFilter, bilateralFilter, GaussianBlur, medianBlur */ -GAPI_EXPORTS GMat blur(const GMat& src, const Size& ksize, const Point& anchor = Point(-1,-1), - int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat blur(const GMat& src, const Size& ksize, const Point& anchor = Point(-1,-1), + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); //GAPI_EXPORTS_W void blur( InputArray src, OutputArray dst, diff --git a/modules/gapi/include/opencv2/gapi/s11n.hpp b/modules/gapi/include/opencv2/gapi/s11n.hpp index adbcfdbdeb..3157a18b84 100644 --- a/modules/gapi/include/opencv2/gapi/s11n.hpp +++ b/modules/gapi/include/opencv2/gapi/s11n.hpp @@ -229,6 +229,9 @@ GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point &pt); GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point2f &pt); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point2f &pt); +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point3f &pt); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point3f &pt); + GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Size &sz); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Size &sz); diff --git a/modules/gapi/misc/python/package/gapi/__init__.py b/modules/gapi/misc/python/package/gapi/__init__.py index 87ad9e2086..992fa2aee2 100644 --- a/modules/gapi/misc/python/package/gapi/__init__.py +++ b/modules/gapi/misc/python/package/gapi/__init__.py @@ -76,6 +76,10 @@ class GOpaque(): def __new__(self): return cv.GOpaqueT(cv.gapi.CV_POINT2F) + class Point3f(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_POINT3F) + class Size(): def __new__(self): return cv.GOpaqueT(cv.gapi.CV_SIZE) @@ -127,6 +131,10 @@ class GArray(): def __new__(self): return cv.GArrayT(cv.gapi.CV_POINT2F) + class Point3f(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_POINT3F) + class Size(): def __new__(self): return cv.GArrayT(cv.gapi.CV_SIZE) @@ -167,6 +175,7 @@ def op(op_id, in_types, out_types): cv.GArray.String: cv.gapi.CV_STRING, cv.GArray.Point: cv.gapi.CV_POINT, cv.GArray.Point2f: cv.gapi.CV_POINT2F, + cv.GArray.Point3f: cv.gapi.CV_POINT3F, cv.GArray.Size: cv.gapi.CV_SIZE, cv.GArray.Rect: cv.gapi.CV_RECT, cv.GArray.Scalar: cv.gapi.CV_SCALAR, @@ -186,6 +195,7 @@ def op(op_id, in_types, out_types): cv.GOpaque.String: cv.gapi.CV_STRING, cv.GOpaque.Point: cv.gapi.CV_POINT, cv.GOpaque.Point2f: cv.gapi.CV_POINT2F, + cv.GOpaque.Point3f: cv.gapi.CV_POINT3F, cv.GOpaque.Size: cv.gapi.CV_SIZE, cv.GOpaque.Rect: cv.gapi.CV_RECT, cv.GOpaque.Prim: cv.gapi.CV_DRAW_PRIM, @@ -200,6 +210,7 @@ def op(op_id, in_types, out_types): cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' , cv.gapi.CV_POINT: 'cv.gapi.CV_POINT' , cv.gapi.CV_POINT2F: 'cv.gapi.CV_POINT2F' , + cv.gapi.CV_POINT3F: 'cv.gapi.CV_POINT3F' , cv.gapi.CV_SIZE: 'cv.gapi.CV_SIZE', cv.gapi.CV_RECT: 'cv.gapi.CV_RECT', cv.gapi.CV_SCALAR: 'cv.gapi.CV_SCALAR', diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 65a25fc235..8fda308ab8 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -19,6 +19,7 @@ using gapi_wip_IStreamSource_Ptr = cv::Ptr; using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback; using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback; using vector_GNetParam = std::vector; +using vector_GMat = std::vector; using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity; using GStreamerSource_OutputType = cv::gapi::wip::GStreamerSource::OutputType; @@ -41,6 +42,7 @@ using GArray_float = cv::GArray; using GArray_string = cv::GArray; using GArray_Point2i = cv::GArray; using GArray_Point2f = cv::GArray; +using GArray_Point3f = cv::GArray; using GArray_Size = cv::GArray; using GArray_Rect = cv::GArray; using GArray_Scalar = cv::GArray; @@ -238,6 +240,7 @@ PyObject* pyopencv_from(const cv::GArg& value) HANDLE_CASE(STRING, std::string); HANDLE_CASE(POINT, cv::Point); HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(SCALAR, cv::Scalar); @@ -295,6 +298,7 @@ PyObject* pyopencv_from(const cv::detail::OpaqueRef& o) case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_POINT : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_POINT2F : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_POINT3F : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_SIZE : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from(o.rref()); @@ -321,6 +325,7 @@ PyObject* pyopencv_from(const cv::detail::VectorRef& v) case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_POINT : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_POINT2F : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_POINT3F : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_SIZE : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_SCALAR : return pyopencv_from_generic_vec(v.rref()); @@ -491,6 +496,7 @@ static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::Opaq HANDLE_CASE(STRING, std::string); HANDLE_CASE(POINT, cv::Point); HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(UNKNOWN, cv::GArg); @@ -523,6 +529,7 @@ static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::Opaq HANDLE_CASE(STRING, std::string); HANDLE_CASE(POINT, cv::Point); HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(SCALAR, cv::Scalar); diff --git a/modules/gapi/misc/python/python_bridge.hpp b/modules/gapi/misc/python/python_bridge.hpp index 11d1728730..9cbea7f753 100644 --- a/modules/gapi/misc/python/python_bridge.hpp +++ b/modules/gapi/misc/python/python_bridge.hpp @@ -36,6 +36,7 @@ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \ WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \ +WRAP_ARGS(cv::Point3f , cv::gapi::ArgType::CV_POINT3F, G) \ WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \ WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G) \ WRAP_ARGS(cv::Scalar , cv::gapi::ArgType::CV_SCALAR, G) \ @@ -53,6 +54,7 @@ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \ WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \ +WRAP_ARGS(cv::Point3f , cv::gapi::ArgType::CV_POINT3F, G) \ WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \ WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \ WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G2) \ @@ -70,6 +72,7 @@ enum ArgType { CV_STRING, CV_POINT, CV_POINT2F, + CV_POINT3F, CV_SIZE, CV_RECT, CV_SCALAR, diff --git a/modules/gapi/misc/python/test/test_gapi_core.py b/modules/gapi/misc/python/test/test_gapi_core.py index 780558d98b..19cc87d442 100644 --- a/modules/gapi/misc/python/test/test_gapi_core.py +++ b/modules/gapi/misc/python/test/test_gapi_core.py @@ -164,7 +164,7 @@ try: def generate_random_points(self, sz): arr = np.random.random(sz).astype(np.float32).T - return list(zip(arr[0], arr[1])) + return list(zip(*[arr[i] for i in range(sz[1])])) def test_kmeans_2d(self): @@ -194,6 +194,33 @@ try: self.assertEqual(K, len(centers)) + def test_kmeans_3d(self): + # K-means 3D params + count = 100 + sz = (count, 3) + amount = sz[0] + K = 5 + flags = cv.KMEANS_RANDOM_CENTERS + attempts = 1 + criteria = (cv.TERM_CRITERIA_MAX_ITER + cv.TERM_CRITERIA_EPS, 30, 0) + in_vector = self.generate_random_points(sz) + in_labels = [] + + # G-API + data = cv.GArrayT(cv.gapi.CV_POINT3F) + best_labels = cv.GArrayT(cv.gapi.CV_INT) + + compactness, out_labels, centers = cv.gapi.kmeans(data, K, best_labels, criteria, attempts, flags) + comp = cv.GComputation(cv.GIn(data, best_labels), cv.GOut(compactness, out_labels, centers)) + + compact, labels, centers = comp.apply(cv.gin(in_vector, in_labels)) + + # Assert + self.assertTrue(compact >= 0) + self.assertEqual(amount, len(labels)) + self.assertEqual(K, len(centers)) + + except unittest.SkipTest as e: message = str(e) diff --git a/modules/gapi/misc/python/test/test_gapi_types.py b/modules/gapi/misc/python/test/test_gapi_types.py index dde554f5e1..41bfbabd61 100644 --- a/modules/gapi/misc/python/test/test_gapi_types.py +++ b/modules/gapi/misc/python/test/test_gapi_types.py @@ -18,8 +18,9 @@ try: def test_garray_type(self): types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT, - cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_SIZE , - cv.gapi.CV_RECT , cv.gapi.CV_SCALAR, cv.gapi.CV_MAT , cv.gapi.CV_GMAT] + cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F , + cv.gapi.CV_SIZE , cv.gapi.CV_RECT , cv.gapi.CV_SCALAR , cv.gapi.CV_MAT , + cv.gapi.CV_GMAT] for t in types: g_array = cv.GArrayT(t) @@ -27,9 +28,9 @@ try: def test_gopaque_type(self): - types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT, - cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_SIZE , - cv.gapi.CV_RECT] + types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT , + cv.gapi.CV_STRING, cv.gapi.CV_POINT, cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F, + cv.gapi.CV_SIZE , cv.gapi.CV_RECT] for t in types: g_opaque = cv.GOpaqueT(t) diff --git a/modules/gapi/src/backends/common/serialization.cpp b/modules/gapi/src/backends/common/serialization.cpp index 638ce2f025..13a74c17b5 100644 --- a/modules/gapi/src/backends/common/serialization.cpp +++ b/modules/gapi/src/backends/common/serialization.cpp @@ -176,6 +176,13 @@ IIStream& operator>> (IIStream& is, cv::Point2f& pt) { return is >> pt.x >> pt.y; } +IOStream& operator<< (IOStream& os, const cv::Point3f &pt) { + return os << pt.x << pt.y << pt.z; +} +IIStream& operator>> (IIStream& is, cv::Point3f& pt) { + return is >> pt.x >> pt.y >> pt.z; +} + IOStream& operator<< (IOStream& os, const cv::Size &sz) { return os << sz.width << sz.height; } @@ -584,6 +591,7 @@ IIStream& operator>> (IIStream& is, cv::GArg &arg) { HANDLE_CASE(STRING , std::string); HANDLE_CASE(POINT , cv::Point); HANDLE_CASE(POINT2F , cv::Point2f); + HANDLE_CASE(POINT3F , cv::Point3f); HANDLE_CASE(SIZE , cv::Size); HANDLE_CASE(RECT , cv::Rect); HANDLE_CASE(SCALAR , cv::Scalar); From ef2677b0a645d2552232d96749109cbed60b4bc0 Mon Sep 17 00:00:00 2001 From: zoom Date: Wed, 9 Nov 2022 16:23:42 +0800 Subject: [PATCH 197/313] Make MatMul layer support 3d or 4d operation with const input --- modules/dnn/src/onnx/onnx_importer.cpp | 23 ++++++++++++++++++++--- modules/dnn/test/test_onnx_importer.cpp | 7 +++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 66f524a14a..a1a99f6d26 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2031,8 +2031,9 @@ void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodePr addLayer(layerParams, node_proto); } -void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { + opencv_onnx::NodeProto node_proto = node_proto_; CV_Assert(node_proto.input_size() == 2); layerParams.type = "InnerProduct"; layerParams.set("bias_term", false); @@ -2044,8 +2045,24 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node { Mat blob = getBlob(node_proto, 1); secondInpDims = blob.dims; - layerParams.blobs.push_back(blob.t()); - layerParams.set("num_output", layerParams.blobs[0].size[0]); + if (secondInpDims == 2) + { + layerParams.blobs.push_back(blob.t()); + layerParams.set("num_output", layerParams.blobs[0].size[0]); + } + else + { + LayerParams constParams; + constParams.name = layerParams.name + "/const"; + constParams.type = "Const"; + constParams.blobs.push_back(blob); + + opencv_onnx::NodeProto tmpProto; + tmpProto.add_output(constParams.name); + addLayer(constParams, tmpProto); + + node_proto.set_input(1, constParams.name); + } } else { secondInpDims = outShapes[node_proto.input(1)].size(); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index b310dce808..be14041bf0 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -956,6 +956,13 @@ TEST_P(Test_ONNX_layers, MatMul) testONNXModels("matmul_4d"); } +TEST_P(Test_ONNX_layers, MatMul_init) +{ + testONNXModels("matmul_2d_init"); + testONNXModels("matmul_3d_init"); + testONNXModels("matmul_4d_init"); +} + TEST_P(Test_ONNX_layers, MatMulAdd) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) From f5e852cdf0aac451575fa1d9dd2f2ad23087e09e Mon Sep 17 00:00:00 2001 From: kallaballa Date: Wed, 9 Nov 2022 01:32:07 +0100 Subject: [PATCH 198/313] define the number of dstChannels for HLS and HSV conversion as well --- modules/imgproc/src/color.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/imgproc/src/color.hpp b/modules/imgproc/src/color.hpp index 3f3b634790..e3c25f08d9 100644 --- a/modules/imgproc/src/color.hpp +++ b/modules/imgproc/src/color.hpp @@ -104,6 +104,10 @@ inline int dstChannels(int code) return 4; case COLOR_BGRA2BGR: case COLOR_RGBA2BGR: case COLOR_RGB2BGR: + case COLOR_HSV2RGB: case COLOR_HSV2BGR: case COLOR_RGB2HSV: case COLOR_BGR2HSV: + case COLOR_HLS2RGB: case COLOR_HLS2BGR: case COLOR_RGB2HLS: case COLOR_BGR2HLS: + case COLOR_HSV2RGB_FULL: case COLOR_HSV2BGR_FULL: case COLOR_RGB2HSV_FULL: case COLOR_BGR2HSV_FULL: + case COLOR_HLS2RGB_FULL: case COLOR_HLS2BGR_FULL: case COLOR_RGB2HLS_FULL: case COLOR_BGR2HLS_FULL: case COLOR_YUV2RGB: case COLOR_YUV2BGR: case COLOR_RGB2YUV: case COLOR_BGR2YUV: case COLOR_BGR5652BGR: case COLOR_BGR5552BGR: case COLOR_BGR5652RGB: case COLOR_BGR5552RGB: case COLOR_GRAY2BGR: From 1ba0984203ab10c9356c56abf4146707bf8e925d Mon Sep 17 00:00:00 2001 From: Juha Reunanen Date: Fri, 11 Nov 2022 10:40:53 +0200 Subject: [PATCH 199/313] Merge pull request #22790 from reunanen:add-capability-to-set-DWA-compression-level-in-OpenEXR-encoding OpenEXR encoder: add capability to set the DWA compression level * OpenEXR encoder: add capability to set the DWA compression level from outside * Do not try to call `header.dwaCompressionLevel()` if OpenEXR is not version 3 or later * Minor cleanup --- modules/imgcodecs/include/opencv2/imgcodecs.hpp | 1 + modules/imgcodecs/src/grfmt_exr.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index a95a4ccdb4..b651fdff3f 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -97,6 +97,7 @@ enum ImwriteFlags { IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default) IMWRITE_EXR_COMPRESSION = (3 << 4) + 1, /* 49 */ //!< override EXR compression type (ZIP_COMPRESSION = 3 is default) + IMWRITE_EXR_DWA_COMPRESSION_LEVEL = (3 << 4) + 2, /* 50 */ //!< override EXR DWA compression level (45 is default) IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 0585035202..786b9d176b 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -691,6 +691,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported"); } } + if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL) + { +#if OPENEXR_VERSION_MAJOR >= 3 + header.dwaCompressionLevel() = params[i + 1]; +#else + CV_LOG_ONCE_WARNING(NULL, "Setting `IMWRITE_EXR_DWA_COMPRESSION_LEVEL` not supported in OpenEXR version " + std::to_string(OPENEXR_VERSION_MAJOR) + " (version 3 is required)"); +#endif + } } if( channels == 3 || channels == 4 ) From da4ac6b7eff2e8869567e4faaff73312f9e1ef57 Mon Sep 17 00:00:00 2001 From: Amir Hassan Date: Fri, 11 Nov 2022 23:28:02 +0100 Subject: [PATCH 200/313] Merge pull request #22706 from kallaballa:libavdevice_for_ffmpeg_v4l2 Introduce libavdevice to make v4l2 available to the ffmpeg backend * introduce libavdevice to make v4l2 available to the ffmpeg backend * downgrade the min required libavdevice version to 53.2.0 * make libavdevice optional * create OCV_OPTION OPENCV_FFMPEG_ENABLE_LIBAVDEVICE and add definition through ocv_add_external_target * move OCV_OPTION 'OPENCV_FFMPEG_ENABLE_LIBAVDEVICE' to detect_ffmpeg.cmake --- CMakeLists.txt | 3 +++ modules/videoio/cmake/detect_ffmpeg.cmake | 19 ++++++++++++++++++- .../videoio/misc/plugin_ffmpeg/CMakeLists.txt | 3 +++ modules/videoio/src/cap_ffmpeg_impl.hpp | 7 +++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aba65ba23f..d293210014 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1444,6 +1444,9 @@ if(WITH_FFMPEG OR HAVE_FFMPEG) status(" avutil:" FFMPEG_libavutil_VERSION THEN "YES (${FFMPEG_libavutil_VERSION})" ELSE NO) status(" swscale:" FFMPEG_libswscale_VERSION THEN "YES (${FFMPEG_libswscale_VERSION})" ELSE NO) status(" avresample:" FFMPEG_libavresample_VERSION THEN "YES (${FFMPEG_libavresample_VERSION})" ELSE NO) + if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE) + status(" avdevice:" FFMPEG_libavdevice_VERSION THEN "YES (${FFMPEG_libavdevice_VERSION})" ELSE NO) + endif() endif() if(WITH_GSTREAMER OR HAVE_GSTREAMER) diff --git a/modules/videoio/cmake/detect_ffmpeg.cmake b/modules/videoio/cmake/detect_ffmpeg.cmake index c33eaf221b..aa669f36b0 100644 --- a/modules/videoio/cmake/detect_ffmpeg.cmake +++ b/modules/videoio/cmake/detect_ffmpeg.cmake @@ -1,4 +1,7 @@ # --- FFMPEG --- +OCV_OPTION(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE "Include FFMPEG/libavdevice library support." OFF + VISIBLE_IF WITH_FFMPEG) + if(NOT HAVE_FFMPEG AND OPENCV_FFMPEG_USE_FIND_PACKAGE) if(OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "1" OR OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "ON") set(OPENCV_FFMPEG_USE_FIND_PACKAGE "FFMPEG") @@ -29,6 +32,13 @@ if(NOT HAVE_FFMPEG AND PKG_CONFIG_FOUND) list(APPEND FFMPEG_LIBRARIES ${FFMPEG_libavresample_LIBRARIES}) list(APPEND _used_ffmpeg_libraries libavresample) endif() + if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE) + ocv_check_modules(FFMPEG_libavdevice libavdevice) # optional + if(FFMPEG_libavdevice_FOUND) + list(APPEND FFMPEG_LIBRARIES ${FFMPEG_libavdevice_LIBRARIES}) + list(APPEND _used_ffmpeg_libraries libavdevice) + endif() + endif() set(HAVE_FFMPEG TRUE) else() set(_missing_ffmpeg_libraries "") @@ -51,6 +61,7 @@ if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER) set(_min_libavutil_version 52.3.0) set(_min_libswscale_version 2.1.1) set(_min_libavresample_version 1.0.1) + set(_min_libavdevice_version 53.2.0) foreach(ffmpeg_lib ${_used_ffmpeg_libraries}) if(FFMPEG_${ffmpeg_lib}_VERSION VERSION_LESS _min_${ffmpeg_lib}_version) message(STATUS "FFMPEG is disabled. Can't find suitable ${ffmpeg_lib} library" @@ -67,6 +78,7 @@ if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER) unset(_min_libavutil_version) unset(_min_libswscale_version) unset(_min_libavresample_version) + unset(_min_libavdevice_version) endif() #================================== @@ -93,7 +105,12 @@ unset(_used_ffmpeg_libraries) if(HAVE_FFMPEG_WRAPPER) ocv_add_external_target(ffmpeg "" "" "HAVE_FFMPEG_WRAPPER") elseif(HAVE_FFMPEG) - ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG") + if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE AND FFMPEG_libavdevice_FOUND) + set(HAVE_FFMPEG_LIBAVDEVICE TRUE) + ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG;HAVE_FFMPEG_LIBAVDEVICE") + else() + ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG") + endif() set(__builtin_defines "") set(__builtin_include_dirs "") set(__builtin_libs "") diff --git a/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt b/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt index ebe388a886..204a425b17 100644 --- a/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt +++ b/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt @@ -16,3 +16,6 @@ message(STATUS "FFMPEG_libavformat_VERSION=${FFMPEG_libavformat_VERSION}") message(STATUS "FFMPEG_libavutil_VERSION=${FFMPEG_libavutil_VERSION}") message(STATUS "FFMPEG_libswscale_VERSION=${FFMPEG_libswscale_VERSION}") message(STATUS "FFMPEG_libavresample_VERSION=${FFMPEG_libavresample_VERSION}") +if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE) + message(STATUS "FFMPEG_libavdevice_VERSION=${FFMPEG_libavdevice_VERSION}") +endif() diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index cf289f6bf3..600bcee407 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -95,6 +95,9 @@ extern "C" { #include #include +#ifdef HAVE_FFMPEG_LIBAVDEVICE +#include +#endif // https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L602-L605 #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(58, 9, 100) @@ -625,6 +628,10 @@ struct CvCapture_FFMPEG void CvCapture_FFMPEG::init() { +#ifdef HAVE_FFMPEG_LIBAVDEVICE + //libavdevice is available, so let's register all input and output devices (e.g v4l2) + avdevice_register_all(); +#endif ic = 0; video_stream = -1; video_st = 0; From 51b897b6724626a7a9bfdf2940ae575c144ad470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Ma=C5=A1ek?= Date: Tue, 15 Nov 2022 00:44:51 +0100 Subject: [PATCH 201/313] Fix #22765: Remove unnecessary function resulting in infinite recursion. Since In all four places it was used, we already check the shared pointer, the extra assert that the function provided was redundand, so I removed it, and I added a dereference to the `window` parameters. --- modules/highgui/src/window_w32.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 47a549e66a..7110417e7a 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -2337,12 +2337,6 @@ std::shared_ptr icvFindTrackbarByName(CvWindow& window, const std::s } return std::shared_ptr(); } -static inline -std::shared_ptr icvFindTrackbarByName(const std::shared_ptr& window, const std::string& name) -{ - CV_Assert(window); - return icvFindTrackbarByName(window, name); -} static std::shared_ptr createTrackbar_(CvWindow& window, const std::string& trackbar_name, @@ -2586,7 +2580,7 @@ CV_IMPL int cvGetTrackbarPos(const char* trackbar_name, const char* window_name) if (!window) CV_Error_(Error::StsNullPtr, ("NULL window: '%s'", window_name)); - auto trackbar = icvFindTrackbarByName(window, trackbar_name); + auto trackbar = icvFindTrackbarByName(*window, trackbar_name); if (!trackbar) CV_Error_(Error::StsNullPtr, ("NULL trackbar: '%s'", trackbar_name)); @@ -2607,7 +2601,7 @@ CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name if (!window) CV_Error_(Error::StsNullPtr, ("NULL window: '%s'", window_name)); - auto trackbar = icvFindTrackbarByName(window, trackbar_name); + auto trackbar = icvFindTrackbarByName(*window, trackbar_name); if (!trackbar) CV_Error_(Error::StsNullPtr, ("NULL trackbar: '%s'", trackbar_name)); @@ -2639,7 +2633,7 @@ CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name if (!window) CV_Error_(Error::StsNullPtr, ("NULL window: '%s'", window_name)); - auto trackbar = icvFindTrackbarByName(window, trackbar_name); + auto trackbar = icvFindTrackbarByName(*window, trackbar_name); if (!trackbar) CV_Error_(Error::StsNullPtr, ("NULL trackbar: '%s'", trackbar_name)); @@ -2668,7 +2662,7 @@ CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name if (!window) CV_Error_(Error::StsNullPtr, ("NULL window: '%s'", window_name)); - auto trackbar = icvFindTrackbarByName(window, trackbar_name); + auto trackbar = icvFindTrackbarByName(*window, trackbar_name); if (!trackbar) CV_Error_(Error::StsNullPtr, ("NULL trackbar: '%s'", trackbar_name)); From 5bf64e7dfef3539767940c2d8c0ba47c10ddbcce Mon Sep 17 00:00:00 2001 From: zihaomu Date: Tue, 15 Nov 2022 11:42:10 +0800 Subject: [PATCH 202/313] fix the infinite loop in tf importer of 3.4 branch --- modules/dnn/src/tensorflow/tf_graph_simplifier.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index fc0fc94d76..13d7028a87 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -829,12 +829,19 @@ void RemoveIdentityOps(tensorflow::GraphDef& net) IdentityOpsMap::iterator it = identity_ops.find(input_op_name); if (it != identity_ops.end()) { + std::set loopCheckSet; // In case of Identity after Identity while (true) { IdentityOpsMap::iterator nextIt = identity_ops.find(it->second); if (nextIt != identity_ops.end()) + { + // Loop check + if (loopCheckSet.find(it->second) != loopCheckSet.end()) + CV_Error(Error::StsError, "Found a loop in your input Tensorflow model, which is illegal!"); + loopCheckSet.insert(it->second); it = nextIt; + } else break; } From 54531f8e3b5f91f9b016caa4563cb28029dd7d02 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 15 Nov 2022 09:55:22 +0000 Subject: [PATCH 203/313] core: support CV_Check*() macros with 'bool' parameters --- modules/core/include/opencv2/core/check.hpp | 10 ++++++++++ modules/core/src/check.cpp | 20 ++++++++++++++++++++ modules/imgcodecs/src/grfmt_webp.cpp | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/check.hpp b/modules/core/include/opencv2/core/check.hpp index d975223cc5..76445a9934 100644 --- a/modules/core/include/opencv2/core/check.hpp +++ b/modules/core/include/opencv2/core/check.hpp @@ -65,6 +65,7 @@ struct CheckContext { static const cv::detail::CheckContext CV__CHECK_LOCATION_VARNAME(id) = \ { CV__CHECK_FUNCTION, CV__CHECK_FILENAME, __LINE__, testOp, "" message, "" p1_str, "" p2_str } +CV_EXPORTS void CV_NORETURN check_failed_auto(const bool v1, const bool v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const int v1, const int v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v1, const size_t v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const float v1, const float v2, const CheckContext& ctx); @@ -74,6 +75,9 @@ CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v1, const int v2, co CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v1, const int v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_true(const bool v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_false(const bool v, const CheckContext& ctx); + CV_EXPORTS void CV_NORETURN check_failed_auto(const int v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const float v, const CheckContext& ctx); @@ -134,6 +138,12 @@ CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v, const CheckCon /// Example: v == A || v == B #define CV_Check(v, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, auto, v, (test_expr), #v, #test_expr, msg) +/// Example: v == true +#define CV_CheckTrue(v, msg) CV__CHECK_CUSTOM_TEST(_, true, v, v, #v, "", msg) + +/// Example: v == false +#define CV_CheckFalse(v, msg) CV__CHECK_CUSTOM_TEST(_, false, v, (!(v)), #v, "", msg) + /// Some complex conditions: CV_Check(src2, src2.empty() || (src2.type() == src1.type() && src2.size() == src1.size()), "src2 should have same size/type as src1") // TODO define pretty-printers diff --git a/modules/core/src/check.cpp b/modules/core/src/check.cpp index 4988b87c34..1c0471a34c 100644 --- a/modules/core/src/check.cpp +++ b/modules/core/src/check.cpp @@ -97,6 +97,10 @@ void check_failed_MatChannels(const int v1, const int v2, const CheckContext& ct { check_failed_auto_(v1, v2, ctx); } +void check_failed_auto(const bool v1, const bool v2, const CheckContext& ctx) +{ + check_failed_auto_(v1, v2, ctx); +} void check_failed_auto(const int v1, const int v2, const CheckContext& ctx) { check_failed_auto_(v1, v2, ctx); @@ -151,6 +155,22 @@ void check_failed_MatChannels(const int v, const CheckContext& ctx) { check_failed_auto_(v, ctx); } +void check_failed_true(const bool v, const CheckContext& ctx) +{ + CV_UNUSED(v); + std::stringstream ss; + ss << ctx.message << ":" << std::endl + << " '" << ctx.p1_str << "' must be 'true'"; + cv::errorNoReturn(cv::Error::StsError, ss.str(), ctx.func, ctx.file, ctx.line); +} +void check_failed_false(const bool v, const CheckContext& ctx) +{ + CV_UNUSED(v); + std::stringstream ss; + ss << ctx.message << ":" << std::endl + << " '" << ctx.p1_str << "' must be 'false'"; + cv::errorNoReturn(cv::Error::StsError, ss.str(), ctx.func, ctx.file, ctx.line); +} void check_failed_auto(const int v, const CheckContext& ctx) { check_failed_auto_(v, ctx); diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index 99eb09e105..1f530e7b9b 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -126,7 +126,7 @@ bool WebPDecoder::readHeader() WebPBitstreamFeatures features; if (VP8_STATUS_OK == WebPGetFeatures(header, sizeof(header), &features)) { - CV_CheckEQ(features.has_animation, false, "WebP backend does not support animated webp images"); + CV_CheckEQ(features.has_animation, 0, "WebP backend does not support animated webp images"); m_width = features.width; m_height = features.height; From 1b1bbe426277715a876878890a3dc88231b871bc Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 16 Nov 2022 07:05:08 +0300 Subject: [PATCH 204/313] Merge pull request #22801 from alalek:update_zlib * 3rdparty: zlib 1.2.12 => 1.2.13 https://github.com/madler/zlib/releases/tag/v1.2.13 - replace README -> LICENSE in install distribution * 3rdparty(zlib): re-apply patch 20190330-ununitialized-use-state-check.diff --- 3rdparty/zlib/CMakeLists.txt | 2 +- 3rdparty/zlib/ChangeLog | 24 +++- 3rdparty/zlib/LICENSE | 22 ++++ 3rdparty/zlib/README | 4 +- 3rdparty/zlib/compress.c | 6 +- 3rdparty/zlib/crc32.c | 33 ++++-- 3rdparty/zlib/deflate.c | 218 ++++++++++++++++++----------------- 3rdparty/zlib/deflate.h | 4 +- 3rdparty/zlib/gzlib.c | 2 +- 3rdparty/zlib/gzread.c | 8 +- 3rdparty/zlib/gzwrite.c | 2 +- 3rdparty/zlib/infback.c | 17 +-- 3rdparty/zlib/inflate.c | 7 +- 3rdparty/zlib/inftrees.c | 4 +- 3rdparty/zlib/inftrees.h | 2 +- 3rdparty/zlib/trees.c | 123 ++++++++++---------- 3rdparty/zlib/uncompr.c | 4 +- 3rdparty/zlib/zconf.h | 19 ++- 3rdparty/zlib/zlib.h | 20 ++-- 3rdparty/zlib/zutil.c | 16 +-- 3rdparty/zlib/zutil.h | 1 + 21 files changed, 303 insertions(+), 235 deletions(-) create mode 100644 3rdparty/zlib/LICENSE diff --git a/3rdparty/zlib/CMakeLists.txt b/3rdparty/zlib/CMakeLists.txt index 709e293c28..addd3e5a14 100644 --- a/3rdparty/zlib/CMakeLists.txt +++ b/3rdparty/zlib/CMakeLists.txt @@ -102,4 +102,4 @@ if(NOT BUILD_SHARED_LIBS) ocv_install_target(${ZLIB_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() -ocv_install_3rdparty_licenses(zlib README) +ocv_install_3rdparty_licenses(zlib LICENSE) diff --git a/3rdparty/zlib/ChangeLog b/3rdparty/zlib/ChangeLog index f0b0e61809..457526bc6a 100644 --- a/3rdparty/zlib/ChangeLog +++ b/3rdparty/zlib/ChangeLog @@ -1,6 +1,18 @@ ChangeLog file for zlib +Changes in 1.2.13 (13 Oct 2022) +- Fix configure issue that discarded provided CC definition +- Correct incorrect inputs provided to the CRC functions +- Repair prototypes and exporting of new CRC functions +- Fix inflateBack to detect invalid input with distances too far +- Have infback() deliver all of the available output up to any error +- Fix a bug when getting a gzip header extra field with inflate() +- Fix bug in block type selection when Z_FIXED used +- Tighten deflateBound bounds +- Remove deleted assembler code references +- Various portability and appearance improvements + Changes in 1.2.12 (27 Mar 2022) - Cygwin does not have _wopen(), so do not create gzopen_w() there - Permit a deflateParams() parameter change as soon as possible @@ -159,7 +171,7 @@ Changes in 1.2.7.1 (24 Mar 2013) - Fix types in contrib/minizip to match result of get_crc_table() - Simplify contrib/vstudio/vc10 with 'd' suffix - Add TOP support to win32/Makefile.msc -- Suport i686 and amd64 assembler builds in CMakeLists.txt +- Support i686 and amd64 assembler builds in CMakeLists.txt - Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h - Add vc11 and vc12 build files to contrib/vstudio - Add gzvprintf() as an undocumented function in zlib @@ -359,14 +371,14 @@ Changes in 1.2.5.1 (10 Sep 2011) - Use u4 type for crc_table to avoid conversion warnings - Apply casts in zlib.h to avoid conversion warnings - Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] -- Improve inflateSync() documentation to note indeterminancy +- Improve inflateSync() documentation to note indeterminacy - Add deflatePending() function to return the amount of pending output - Correct the spelling of "specification" in FAQ [Randers-Pehrson] - Add a check in configure for stdarg.h, use for gzprintf() - Check that pointers fit in ints when gzprint() compiled old style - Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] - Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] -- Add debug records in assmebler code [Londer] +- Add debug records in assembler code [Londer] - Update RFC references to use http://tools.ietf.org/html/... [Li] - Add --archs option, use of libtool to configure for Mac OS X [Borstel] @@ -1033,7 +1045,7 @@ Changes in 1.2.0.1 (17 March 2003) - Include additional header file on VMS for off_t typedef - Try to use _vsnprintf where it supplants vsprintf [Vollant] - Add some casts in inffast.c -- Enchance comments in zlib.h on what happens if gzprintf() tries to +- Enhance comments in zlib.h on what happens if gzprintf() tries to write more than 4095 bytes before compression - Remove unused state from inflateBackEnd() - Remove exit(0) from minigzip.c, example.c @@ -1211,7 +1223,7 @@ Changes in 1.0.9 (17 Feb 1998) - Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 - in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) - in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with - the declaration of FAR (Gilles VOllant) + the declaration of FAR (Gilles Vollant) - install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) - read_buf buf parameter of type Bytef* instead of charf* - zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) @@ -1567,7 +1579,7 @@ Changes in 0.4: - renamed deflateOptions as deflateInit2, call one or the other but not both - added the method parameter for deflateInit2 - added inflateInit2 -- simplied considerably deflateInit and inflateInit by not supporting +- simplified considerably deflateInit and inflateInit by not supporting user-provided history buffer. This is supported only in deflateInit2 and inflateInit2 diff --git a/3rdparty/zlib/LICENSE b/3rdparty/zlib/LICENSE new file mode 100644 index 0000000000..ab8ee6f714 --- /dev/null +++ b/3rdparty/zlib/LICENSE @@ -0,0 +1,22 @@ +Copyright notice: + + (C) 1995-2022 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu diff --git a/3rdparty/zlib/README b/3rdparty/zlib/README index 024b79d3d8..ba34d1894a 100644 --- a/3rdparty/zlib/README +++ b/3rdparty/zlib/README @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.12 is a general purpose data compression library. All the code is +zlib 1.2.13 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and @@ -31,7 +31,7 @@ Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at http://marknelson.us/1997/01/01/zlib-engine/ . -The changes made in version 1.2.12 are documented in the file ChangeLog. +The changes made in version 1.2.13 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . diff --git a/3rdparty/zlib/compress.c b/3rdparty/zlib/compress.c index e2db404abf..2ad5326c14 100644 --- a/3rdparty/zlib/compress.c +++ b/3rdparty/zlib/compress.c @@ -19,7 +19,7 @@ memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ -int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) +int ZEXPORT compress2(dest, destLen, source, sourceLen, level) Bytef *dest; uLongf *destLen; const Bytef *source; @@ -65,7 +65,7 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) /* =========================================================================== */ -int ZEXPORT compress (dest, destLen, source, sourceLen) +int ZEXPORT compress(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; @@ -78,7 +78,7 @@ int ZEXPORT compress (dest, destLen, source, sourceLen) If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ -uLong ZEXPORT compressBound (sourceLen) +uLong ZEXPORT compressBound(sourceLen) uLong sourceLen; { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + diff --git a/3rdparty/zlib/crc32.c b/3rdparty/zlib/crc32.c index a1bdce5c23..f8357b083f 100644 --- a/3rdparty/zlib/crc32.c +++ b/3rdparty/zlib/crc32.c @@ -98,13 +98,22 @@ # endif #endif +/* If available, use the ARM processor CRC32 instruction. */ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8 +# define ARMCRC32 +#endif + /* Local functions. */ local z_crc_t multmodp OF((z_crc_t a, z_crc_t b)); local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); -/* If available, use the ARM processor CRC32 instruction. */ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8 -# define ARMCRC32 +#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) + local z_word_t byte_swap OF((z_word_t word)); +#endif + +#if defined(W) && !defined(ARMCRC32) + local z_crc_t crc_word OF((z_word_t data)); + local z_word_t crc_word_big OF((z_word_t data)); #endif #if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) @@ -630,7 +639,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) #endif /* DYNAMIC_CRC_TABLE */ /* Pre-condition the CRC */ - crc ^= 0xffffffff; + crc = (~crc) & 0xffffffff; /* Compute the CRC up to a word boundary. */ while (len && ((z_size_t)buf & 7) != 0) { @@ -645,8 +654,8 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) len &= 7; /* Do three interleaved CRCs to realize the throughput of one crc32x - instruction per cycle. Each CRC is calcuated on Z_BATCH words. The three - CRCs are combined into a single CRC after each set of batches. */ + instruction per cycle. Each CRC is calculated on Z_BATCH words. The + three CRCs are combined into a single CRC after each set of batches. */ while (num >= 3 * Z_BATCH) { crc1 = 0; crc2 = 0; @@ -749,7 +758,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) #endif /* DYNAMIC_CRC_TABLE */ /* Pre-condition the CRC */ - crc ^= 0xffffffff; + crc = (~crc) & 0xffffffff; #ifdef W @@ -1077,7 +1086,7 @@ uLong ZEXPORT crc32_combine64(crc1, crc2, len2) #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ - return multmodp(x2nmodp(len2, 3), crc1) ^ crc2; + return multmodp(x2nmodp(len2, 3), crc1) ^ (crc2 & 0xffffffff); } /* ========================================================================= */ @@ -1086,7 +1095,7 @@ uLong ZEXPORT crc32_combine(crc1, crc2, len2) uLong crc2; z_off_t len2; { - return crc32_combine64(crc1, crc2, len2); + return crc32_combine64(crc1, crc2, (z_off64_t)len2); } /* ========================================================================= */ @@ -1103,14 +1112,14 @@ uLong ZEXPORT crc32_combine_gen64(len2) uLong ZEXPORT crc32_combine_gen(len2) z_off_t len2; { - return crc32_combine_gen64(len2); + return crc32_combine_gen64((z_off64_t)len2); } /* ========================================================================= */ -uLong crc32_combine_op(crc1, crc2, op) +uLong ZEXPORT crc32_combine_op(crc1, crc2, op) uLong crc1; uLong crc2; uLong op; { - return multmodp(op, crc1) ^ crc2; + return multmodp(op, crc1) ^ (crc2 & 0xffffffff); } diff --git a/3rdparty/zlib/deflate.c b/3rdparty/zlib/deflate.c index 799fb93cc0..4a689db359 100644 --- a/3rdparty/zlib/deflate.c +++ b/3rdparty/zlib/deflate.c @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.12 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; + " deflate 1.2.13 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -87,13 +87,7 @@ local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -#ifdef ASMV -# pragma message("Assembler code may have bugs -- use at your own risk") - void match_init OF((void)); /* asm code initialization */ - uInt longest_match OF((deflate_state *s, IPos cur_match)); -#else local uInt longest_match OF((deflate_state *s, IPos cur_match)); -#endif #ifdef ZLIB_DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, @@ -160,7 +154,7 @@ local const config configuration_table[10] = { * characters, so that a running hash key can be computed from the previous * key instead of complete recalculation each time. */ -#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) +#define UPDATE_HASH(s,h,c) (h = (((h) << s->hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== @@ -191,9 +185,9 @@ local const config configuration_table[10] = { */ #define CLEAR_HASH(s) \ do { \ - s->head[s->hash_size-1] = NIL; \ + s->head[s->hash_size - 1] = NIL; \ zmemzero((Bytef *)s->head, \ - (unsigned)(s->hash_size-1)*sizeof(*s->head)); \ + (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \ } while (0) /* =========================================================================== @@ -285,6 +279,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; + if (windowBits < -15) + return Z_STREAM_ERROR; windowBits = -windowBits; } #ifdef GZIP @@ -314,7 +310,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; - s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + s->hash_shift = ((s->hash_bits + MIN_MATCH-1) / MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); @@ -340,11 +336,11 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, * sym_buf value to read moves forward three bytes. From that symbol, up to * 31 bits are written to pending_buf. The closest the written pending_buf * bits gets to the next sym_buf symbol to read is just before the last - * code is written. At that time, 31*(n-2) bits have been written, just - * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at - * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1 + * code is written. At that time, 31*(n - 2) bits have been written, just + * after 24*(n - 2) bits have been consumed from sym_buf. sym_buf starts at + * 8*n bits into pending_buf. (Note that the symbol buffer fills when n - 1 * symbols are written.) The closest the writing gets to what is unread is - * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and + * then n + 14 bits. Here n is lit_bufsize, which is 16384 by default, and * can range from 128 to 32768. * * Therefore, at a minimum, there are 142 bits of space between what is @@ -390,7 +386,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ -local int deflateStateCheck (strm) +local int deflateStateCheck(strm) z_streamp strm; { deflate_state *s; @@ -413,7 +409,7 @@ local int deflateStateCheck (strm) } /* ========================================================================= */ -int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) +int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; @@ -482,7 +478,7 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) +int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; @@ -504,7 +500,7 @@ int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateResetKeep (strm) +int ZEXPORT deflateResetKeep(strm) z_streamp strm; { deflate_state *s; @@ -542,7 +538,7 @@ int ZEXPORT deflateResetKeep (strm) } /* ========================================================================= */ -int ZEXPORT deflateReset (strm) +int ZEXPORT deflateReset(strm) z_streamp strm; { int ret; @@ -554,7 +550,7 @@ int ZEXPORT deflateReset (strm) } /* ========================================================================= */ -int ZEXPORT deflateSetHeader (strm, head) +int ZEXPORT deflateSetHeader(strm, head) z_streamp strm; gz_headerp head; { @@ -565,7 +561,7 @@ int ZEXPORT deflateSetHeader (strm, head) } /* ========================================================================= */ -int ZEXPORT deflatePending (strm, pending, bits) +int ZEXPORT deflatePending(strm, pending, bits) unsigned *pending; int *bits; z_streamp strm; @@ -579,7 +575,7 @@ int ZEXPORT deflatePending (strm, pending, bits) } /* ========================================================================= */ -int ZEXPORT deflatePrime (strm, bits, value) +int ZEXPORT deflatePrime(strm, bits, value) z_streamp strm; int bits; int value; @@ -674,36 +670,50 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) } /* ========================================================================= - * For the default windowBits of 15 and memLevel of 8, this function returns - * a close to exact, as well as small, upper bound on the compressed size. - * They are coded as constants here for a reason--if the #define's are - * changed, then this function needs to be changed as well. The return - * value for 15 and 8 only works for those exact settings. + * For the default windowBits of 15 and memLevel of 8, this function returns a + * close to exact, as well as small, upper bound on the compressed size. This + * is an expansion of ~0.03%, plus a small constant. * - * For any setting other than those defaults for windowBits and memLevel, - * the value returned is a conservative worst case for the maximum expansion - * resulting from using fixed blocks instead of stored blocks, which deflate - * can emit on compressed data for some combinations of the parameters. + * For any setting other than those defaults for windowBits and memLevel, one + * of two worst case bounds is returned. This is at most an expansion of ~4% or + * ~13%, plus a small constant. * - * This function could be more sophisticated to provide closer upper bounds for - * every combination of windowBits and memLevel. But even the conservative - * upper bound of about 14% expansion does not seem onerous for output buffer - * allocation. + * Both the 0.03% and 4% derive from the overhead of stored blocks. The first + * one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second + * is for stored blocks of 127 bytes (the worst case memLevel == 1). The + * expansion results from five bytes of header for each stored block. + * + * The larger expansion of 13% results from a window size less than or equal to + * the symbols buffer size (windowBits <= memLevel + 7). In that case some of + * the data being compressed may have slid out of the sliding window, impeding + * a stored block from being emitted. Then the only choice is a fixed or + * dynamic block, where a fixed block limits the maximum expansion to 9 bits + * per 8-bit byte, plus 10 bits for every block. The smallest block size for + * which this can occur is 255 (memLevel == 2). + * + * Shifts are used to approximate divisions, for speed. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; - uLong complen, wraplen; + uLong fixedlen, storelen, wraplen; - /* conservative upper bound for compressed data */ - complen = sourceLen + - ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + /* upper bound for fixed blocks with 9-bit literals and length 255 + (memLevel == 2, which is the lowest that may not use stored blocks) -- + ~13% overhead plus a small constant */ + fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) + + (sourceLen >> 9) + 4; - /* if can't get parameters, return conservative bound plus zlib wrapper */ + /* upper bound for stored blocks with length 127 (memLevel == 1) -- + ~4% overhead plus a small constant */ + storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) + + (sourceLen >> 11) + 7; + + /* if can't get parameters, return larger bound plus a zlib wrapper */ if (deflateStateCheck(strm)) - return complen + 6; + return (fixedlen > storelen ? fixedlen : storelen) + 6; /* compute wrapper length */ s = strm->state; @@ -740,11 +750,12 @@ uLong ZEXPORT deflateBound(strm, sourceLen) wraplen = 6; } - /* if not default parameters, return conservative bound */ + /* if not default parameters, return one of the conservative bounds */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return complen + wraplen; + return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen; - /* default settings: return tight bound for that case */ + /* default settings: return tight bound for that case -- ~0.03% overhead + plus a small constant */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } @@ -754,7 +765,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen) * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ -local void putShortMSB (s, b) +local void putShortMSB(s, b) deflate_state *s; uInt b; { @@ -801,7 +812,7 @@ local void flush_pending(strm) } while (0) /* ========================================================================= */ -int ZEXPORT deflate (strm, flush) +int ZEXPORT deflate(strm, flush) z_streamp strm; int flush; { @@ -856,7 +867,7 @@ int ZEXPORT deflate (strm, flush) s->status = BUSY_STATE; if (s->status == INIT_STATE) { /* zlib header */ - uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt header = (Z_DEFLATED + ((s->w_bits - 8) << 4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) @@ -1116,7 +1127,7 @@ int ZEXPORT deflate (strm, flush) } /* ========================================================================= */ -int ZEXPORT deflateEnd (strm) +int ZEXPORT deflateEnd(strm) z_streamp strm; { int status; @@ -1142,7 +1153,7 @@ int ZEXPORT deflateEnd (strm) * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ -int ZEXPORT deflateCopy (dest, source) +int ZEXPORT deflateCopy(dest, source) z_streamp dest; z_streamp source; { @@ -1231,7 +1242,7 @@ local unsigned read_buf(strm, buf, size) /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ -local void lm_init (s) +local void lm_init(s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; @@ -1252,11 +1263,6 @@ local void lm_init (s) s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; -#ifndef FASTEST -#ifdef ASMV - match_init(); /* initialize the asm code */ -#endif -#endif } #ifndef FASTEST @@ -1269,10 +1275,6 @@ local void lm_init (s) * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ -#ifndef ASMV -/* For 80x86 and 680x0, an optimized version will be provided in match.asm or - * match.S. The code will be functionally equivalent. - */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ @@ -1297,10 +1299,10 @@ local uInt longest_match(s, cur_match) */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; - register ush scan_end = *(ushf*)(scan+best_len-1); + register ush scan_end = *(ushf*)(scan + best_len - 1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; - register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end1 = scan[best_len - 1]; register Byte scan_end = scan[best_len]; #endif @@ -1318,7 +1320,8 @@ local uInt longest_match(s, cur_match) */ if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; - Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); @@ -1336,43 +1339,44 @@ local uInt longest_match(s, cur_match) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ - if (*(ushf*)(match+best_len-1) != scan_end || + if (*(ushf*)(match + best_len - 1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at - * strstart+3, +5, ... up to strstart+257. We check for insufficient + * strstart + 3, + 5, up to strstart + 257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made - * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * at strstart + 257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { - } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + } while (*(ushf*)(scan += 2) == *(ushf*)(match += 2) && + *(ushf*)(scan += 2) == *(ushf*)(match += 2) && + *(ushf*)(scan += 2) == *(ushf*)(match += 2) && + *(ushf*)(scan += 2) == *(ushf*)(match += 2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ - /* Here, scan <= window+strstart+257 */ - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + /* Here, scan <= window + strstart + 257 */ + Assert(scan <= s->window + (unsigned)(s->window_size - 1), + "wild scan"); if (*scan == *match) scan++; - len = (MAX_MATCH - 1) - (int)(strend-scan); + len = (MAX_MATCH - 1) - (int)(strend - scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ - if (match[best_len] != scan_end || - match[best_len-1] != scan_end1 || - *match != *scan || - *++match != scan[1]) continue; + if (match[best_len] != scan_end || + match[best_len - 1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; - /* The check at best_len-1 can be removed because it will be made + /* The check at best_len - 1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that @@ -1382,7 +1386,7 @@ local uInt longest_match(s, cur_match) Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. + * the 256th check will be made at strstart + 258. */ do { } while (*++scan == *++match && *++scan == *++match && @@ -1391,7 +1395,8 @@ local uInt longest_match(s, cur_match) *++scan == *++match && *++scan == *++match && scan < strend); - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + Assert(scan <= s->window + (unsigned)(s->window_size - 1), + "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; @@ -1403,9 +1408,9 @@ local uInt longest_match(s, cur_match) best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK - scan_end = *(ushf*)(scan+best_len-1); + scan_end = *(ushf*)(scan + best_len - 1); #else - scan_end1 = scan[best_len-1]; + scan_end1 = scan[best_len - 1]; scan_end = scan[best_len]; #endif } @@ -1415,7 +1420,6 @@ local uInt longest_match(s, cur_match) if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } -#endif /* ASMV */ #else /* FASTEST */ @@ -1436,7 +1440,8 @@ local uInt longest_match(s, cur_match) */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "need lookahead"); Assert(cur_match < s->strstart, "no future"); @@ -1446,7 +1451,7 @@ local uInt longest_match(s, cur_match) */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; - /* The check at best_len-1 can be removed because it will be made + /* The check at best_len - 1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that @@ -1456,7 +1461,7 @@ local uInt longest_match(s, cur_match) Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. + * the 256th check will be made at strstart + 258. */ do { } while (*++scan == *++match && *++scan == *++match && @@ -1465,7 +1470,7 @@ local uInt longest_match(s, cur_match) *++scan == *++match && *++scan == *++match && scan < strend); - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); @@ -1501,7 +1506,7 @@ local void check_match(s, start, match, length) z_error("invalid match"); } if (z_verbose > 1) { - fprintf(stderr,"\\[%d,%d]", start-match, length); + fprintf(stderr,"\\[%d,%d]", start - match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } @@ -1547,9 +1552,9 @@ local void fill_window(s) /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ - if (s->strstart >= wsize+MAX_DIST(s)) { + if (s->strstart >= wsize + MAX_DIST(s)) { - zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); + zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; @@ -1680,7 +1685,7 @@ local void fill_window(s) * * deflate_stored() is written to minimize the number of times an input byte is * copied. It is most efficient with large input and output buffers, which - * maximizes the opportunites to have a single copy from next_in to next_out. + * maximizes the opportunities to have a single copy from next_in to next_out. */ local block_state deflate_stored(s, flush) deflate_state *s; @@ -1890,7 +1895,7 @@ local block_state deflate_fast(s, flush) if (s->lookahead == 0) break; /* flush the current block */ } - /* Insert the string window[strstart .. strstart+2] in the + /* Insert the string window[strstart .. strstart + 2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; @@ -1938,7 +1943,7 @@ local block_state deflate_fast(s, flush) s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; - UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); + UPDATE_HASH(s, s->ins_h, s->window[s->strstart + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif @@ -1949,7 +1954,7 @@ local block_state deflate_fast(s, flush) } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); + _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } @@ -1993,7 +1998,7 @@ local block_state deflate_slow(s, flush) if (s->lookahead == 0) break; /* flush the current block */ } - /* Insert the string window[strstart .. strstart+2] in the + /* Insert the string window[strstart .. strstart + 2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; @@ -2035,17 +2040,17 @@ local block_state deflate_slow(s, flush) uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ - check_match(s, s->strstart-1, s->prev_match, s->prev_length); + check_match(s, s->strstart - 1, s->prev_match, s->prev_length); - _tr_tally_dist(s, s->strstart -1 - s->prev_match, + _tr_tally_dist(s, s->strstart - 1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not + * strstart - 1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ - s->lookahead -= s->prev_length-1; + s->lookahead -= s->prev_length - 1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { @@ -2063,8 +2068,8 @@ local block_state deflate_slow(s, flush) * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ - Tracevv((stderr,"%c", s->window[s->strstart-1])); - _tr_tally_lit(s, s->window[s->strstart-1], bflush); + Tracevv((stderr,"%c", s->window[s->strstart - 1])); + _tr_tally_lit(s, s->window[s->strstart - 1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } @@ -2082,8 +2087,8 @@ local block_state deflate_slow(s, flush) } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { - Tracevv((stderr,"%c", s->window[s->strstart-1])); - _tr_tally_lit(s, s->window[s->strstart-1], bflush); + Tracevv((stderr,"%c", s->window[s->strstart - 1])); + _tr_tally_lit(s, s->window[s->strstart - 1], bflush); s->match_available = 0; } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; @@ -2140,7 +2145,8 @@ local block_state deflate_rle(s, flush) if (s->match_length > s->lookahead) s->match_length = s->lookahead; } - Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + Assert(scan <= s->window + (uInt)(s->window_size - 1), + "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ @@ -2155,7 +2161,7 @@ local block_state deflate_rle(s, flush) } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); + _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } @@ -2195,7 +2201,7 @@ local block_state deflate_huff(s, flush) /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); + _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); diff --git a/3rdparty/zlib/deflate.h b/3rdparty/zlib/deflate.h index 17c226113b..1a06cd5f25 100644 --- a/3rdparty/zlib/deflate.h +++ b/3rdparty/zlib/deflate.h @@ -329,8 +329,8 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (uch)(length); \ ush dist = (ush)(distance); \ - s->sym_buf[s->sym_next++] = dist; \ - s->sym_buf[s->sym_next++] = dist >> 8; \ + s->sym_buf[s->sym_next++] = (uch)dist; \ + s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \ s->sym_buf[s->sym_next++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ diff --git a/3rdparty/zlib/gzlib.c b/3rdparty/zlib/gzlib.c index dddaf26873..55da46a453 100644 --- a/3rdparty/zlib/gzlib.c +++ b/3rdparty/zlib/gzlib.c @@ -30,7 +30,7 @@ local gzFile gz_open OF((const void *, int, const char *)); The gz_strwinerror function does not change the current setting of GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror (error) +char ZLIB_INTERNAL *gz_strwinerror(error) DWORD error; { static char buf[1024]; diff --git a/3rdparty/zlib/gzread.c b/3rdparty/zlib/gzread.c index 884c9bfe4c..dd77381596 100644 --- a/3rdparty/zlib/gzread.c +++ b/3rdparty/zlib/gzread.c @@ -157,11 +157,9 @@ local int gz_look(state) the output buffer is larger than the input buffer, which also assures space for gzungetc() */ state->x.next = state->out; - if (strm->avail_in) { - memcpy(state->x.next, strm->next_in, strm->avail_in); - state->x.have = strm->avail_in; - strm->avail_in = 0; - } + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; state->how = COPY; state->direct = 1; return 0; diff --git a/3rdparty/zlib/gzwrite.c b/3rdparty/zlib/gzwrite.c index a8ffc8f53d..eb8a0e5893 100644 --- a/3rdparty/zlib/gzwrite.c +++ b/3rdparty/zlib/gzwrite.c @@ -474,7 +474,7 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ -int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, +int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) gzFile file; const char *format; diff --git a/3rdparty/zlib/infback.c b/3rdparty/zlib/infback.c index a390c58e81..babeaf1806 100644 --- a/3rdparty/zlib/infback.c +++ b/3rdparty/zlib/infback.c @@ -66,6 +66,7 @@ int stream_size; state->window = window; state->wnext = 0; state->whave = 0; + state->sane = 1; return Z_OK; } @@ -605,25 +606,27 @@ void FAR *out_desc; break; case DONE: - /* inflate stream terminated properly -- write leftover output */ + /* inflate stream terminated properly */ ret = Z_STREAM_END; - if (left < state->wsize) { - if (out(out_desc, state->window, state->wsize - left)) - ret = Z_BUF_ERROR; - } goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; - default: /* can't happen, but makes compilers happy */ + default: + /* can't happen, but makes compilers happy */ ret = Z_STREAM_ERROR; goto inf_leave; } - /* Return unused input */ + /* Write leftover output and return unused input */ inf_leave: + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left) && + ret == Z_STREAM_END) + ret = Z_BUF_ERROR; + } strm->next_in = next; strm->avail_in = have; return ret; diff --git a/3rdparty/zlib/inflate.c b/3rdparty/zlib/inflate.c index 0e7c4f26b1..c84f52507c 100644 --- a/3rdparty/zlib/inflate.c +++ b/3rdparty/zlib/inflate.c @@ -168,6 +168,8 @@ int windowBits; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { + if (windowBits < -15) + return Z_STREAM_ERROR; wrap = 0; windowBits = -windowBits; } @@ -765,8 +767,9 @@ int flush; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && - state->head->extra != Z_NULL) { - len = state->head->extra_len - state->length; + state->head->extra != Z_NULL && + (len = state->head->extra_len - state->length) < + state->head->extra_max) { zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); diff --git a/3rdparty/zlib/inftrees.c b/3rdparty/zlib/inftrees.c index 09462a740b..57d2793bec 100644 --- a/3rdparty/zlib/inftrees.c +++ b/3rdparty/zlib/inftrees.c @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.12 Copyright 1995-2022 Mark Adler "; + " inflate 1.2.13 Copyright 1995-2022 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -62,7 +62,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 199, 202}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 194, 65}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, diff --git a/3rdparty/zlib/inftrees.h b/3rdparty/zlib/inftrees.h index baa53a0b1a..f53665311c 100644 --- a/3rdparty/zlib/inftrees.h +++ b/3rdparty/zlib/inftrees.h @@ -38,7 +38,7 @@ typedef struct { /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program - examples/enough.c found in the zlib distribtution. The arguments to that + examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns returns 852, and "enough 30 6 15" for distance codes returns 592. diff --git a/3rdparty/zlib/trees.c b/3rdparty/zlib/trees.c index f73fd99c37..5f305c4722 100644 --- a/3rdparty/zlib/trees.c +++ b/3rdparty/zlib/trees.c @@ -193,7 +193,7 @@ local void send_bits(s, value, length) s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and - * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * (16 - bi_valid) bits from value, leaving (width - (16 - bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { @@ -256,7 +256,7 @@ local void tr_static_init() length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; - for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; - for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; - for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = (uch)code; } } - Assert (dist == 256, "tr_static_init: 256+dist != 512"); + Assert (dist == 256, "tr_static_init: 256 + dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; @@ -312,7 +312,7 @@ local void tr_static_init() } /* =========================================================================== - * Genererate the file trees.h describing the static trees. + * Generate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef ZLIB_DEBUG @@ -321,7 +321,7 @@ local void tr_static_init() # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ - ((i) % (width) == (width)-1 ? ",\n" : ", ")) + ((i) % (width) == (width) - 1 ? ",\n" : ", ")) void gen_trees_header() { @@ -458,7 +458,7 @@ local void pqdownheap(s, tree, k) while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && - smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + smaller(tree, s->heap[j + 1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ @@ -507,7 +507,7 @@ local void gen_bitlen(s, desc) */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ - for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + for (h = s->heap_max + 1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; @@ -518,7 +518,7 @@ local void gen_bitlen(s, desc) s->bl_count[bits]++; xbits = 0; - if (n >= base) xbits = extra[n-base]; + if (n >= base) xbits = extra[n - base]; f = tree[n].Freq; s->opt_len += (ulg)f * (unsigned)(bits + xbits); if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); @@ -530,10 +530,10 @@ local void gen_bitlen(s, desc) /* Find the first bit length which could increase: */ do { - bits = max_length-1; + bits = max_length - 1; while (s->bl_count[bits] == 0) bits--; - s->bl_count[bits]--; /* move one leaf down the tree */ - s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits + 1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] @@ -569,7 +569,7 @@ local void gen_bitlen(s, desc) * OUT assertion: the field code is set for all tree elements of non * zero code length. */ -local void gen_codes (tree, max_code, bl_count) +local void gen_codes(tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ @@ -583,13 +583,13 @@ local void gen_codes (tree, max_code, bl_count) * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { - code = (code + bl_count[bits-1]) << 1; + code = (code + bl_count[bits - 1]) << 1; next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ - Assert (code + bl_count[MAX_BITS]-1 == (1<heap_len = 0, s->heap_max = HEAP_SIZE; @@ -652,7 +652,7 @@ local void build_tree(s, desc) } desc->max_code = max_code; - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + /* The elements heap[heap_len/2 + 1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); @@ -700,7 +700,7 @@ local void build_tree(s, desc) * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ -local void scan_tree (s, tree, max_code) +local void scan_tree(s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ @@ -714,10 +714,10 @@ local void scan_tree (s, tree, max_code) int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; - tree[max_code+1].Len = (ush)0xffff; /* guard */ + tree[max_code + 1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; + curlen = nextlen; nextlen = tree[n + 1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { @@ -745,7 +745,7 @@ local void scan_tree (s, tree, max_code) * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ -local void send_tree (s, tree, max_code) +local void send_tree(s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ @@ -758,11 +758,11 @@ local void send_tree (s, tree, max_code) int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ - /* tree[max_code+1].Len = -1; */ /* guard already set */ + /* tree[max_code + 1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; + curlen = nextlen; nextlen = tree[n + 1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { @@ -773,13 +773,13 @@ local void send_tree (s, tree, max_code) send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count - 3, 2); } else if (count <= 10) { - send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count - 3, 3); } else { - send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count - 11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { @@ -807,8 +807,8 @@ local int build_bl_tree(s) /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + /* opt_len now includes the length of the tree representations, except the + * lengths of the bit lengths codes and the 5 + 5 + 4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format @@ -819,7 +819,7 @@ local int build_bl_tree(s) if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; + s->opt_len += 3*((ulg)max_blindex + 1) + 5 + 5 + 4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); @@ -841,19 +841,19 @@ local void send_all_trees(s, lcodes, dcodes, blcodes) Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes-1, 5); - send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes - 1, 5); + send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + send_tree(s, (ct_data *)s->dyn_ltree, lcodes - 1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + send_tree(s, (ct_data *)s->dyn_dtree, dcodes - 1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } @@ -866,7 +866,7 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { - send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ + send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); put_short(s, (ush)~stored_len); @@ -877,7 +877,7 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; s->bits_sent += 2*16; - s->bits_sent += stored_len<<3; + s->bits_sent += stored_len << 3; #endif } @@ -943,14 +943,17 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s->opt_len+3+7)>>3; - static_lenb = (s->static_len+3+7)>>3; + opt_lenb = (s->opt_len + 3 + 7) >> 3; + static_lenb = (s->static_len + 3 + 7) >> 3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->sym_next / 3)); - if (static_lenb <= opt_lenb) opt_lenb = static_lenb; +#ifndef FORCE_STATIC + if (static_lenb <= opt_lenb || s->strategy == Z_FIXED) +#endif + opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); @@ -960,7 +963,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else - if (stored_len+4 <= opt_lenb && buf != (char*)0) { + if (stored_len + 4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. @@ -971,21 +974,17 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) */ _tr_stored_block(s, buf, stored_len, last); -#ifdef FORCE_STATIC - } else if (static_lenb >= 0) { /* force static trees */ -#else - } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { -#endif - send_bits(s, (STATIC_TREES<<1)+last, 3); + } else if (static_lenb == opt_lenb) { + send_bits(s, (STATIC_TREES<<1) + last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { - send_bits(s, (DYN_TREES<<1)+last, 3); - send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, - max_blindex+1); + send_bits(s, (DYN_TREES<<1) + last, 3); + send_all_trees(s, s->l_desc.max_code + 1, s->d_desc.max_code + 1, + max_blindex + 1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); #ifdef ZLIB_DEBUG @@ -1004,22 +1003,22 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) s->compressed_len += 7; /* align on byte boundary */ #endif } - Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - s->compressed_len-7*last)); + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len >> 3, + s->compressed_len - 7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ -int ZLIB_INTERNAL _tr_tally (s, dist, lc) +int ZLIB_INTERNAL _tr_tally(s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ - unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ + unsigned lc; /* match length - MIN_MATCH or unmatched char (dist==0) */ { - s->sym_buf[s->sym_next++] = dist; - s->sym_buf[s->sym_next++] = dist >> 8; - s->sym_buf[s->sym_next++] = lc; + s->sym_buf[s->sym_next++] = (uch)dist; + s->sym_buf[s->sym_next++] = (uch)(dist >> 8); + s->sym_buf[s->sym_next++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; @@ -1031,7 +1030,7 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc) (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_ltree[_length_code[lc] + LITERALS + 1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } return (s->sym_next == s->sym_end); @@ -1061,7 +1060,7 @@ local void compress_block(s, ltree, dtree) } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; - send_code(s, code+LITERALS+1, ltree); /* send the length code */ + send_code(s, code + LITERALS + 1, ltree); /* send length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; @@ -1177,6 +1176,6 @@ local void bi_windup(s) s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG - s->bits_sent = (s->bits_sent+7) & ~7; + s->bits_sent = (s->bits_sent + 7) & ~7; #endif } diff --git a/3rdparty/zlib/uncompr.c b/3rdparty/zlib/uncompr.c index f03a1a865e..f9532f46c1 100644 --- a/3rdparty/zlib/uncompr.c +++ b/3rdparty/zlib/uncompr.c @@ -24,7 +24,7 @@ Z_DATA_ERROR if the input data was corrupted, including if the input data is an incomplete zlib stream. */ -int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) +int ZEXPORT uncompress2(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; @@ -83,7 +83,7 @@ int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) err; } -int ZEXPORT uncompress (dest, destLen, source, sourceLen) +int ZEXPORT uncompress(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; diff --git a/3rdparty/zlib/zconf.h b/3rdparty/zlib/zconf.h index 5e1d68a004..bf977d3e70 100644 --- a/3rdparty/zlib/zconf.h +++ b/3rdparty/zlib/zconf.h @@ -38,6 +38,9 @@ # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 +# define crc32_combine_gen z_crc32_combine_gen +# define crc32_combine_gen64 z_crc32_combine_gen64 +# define crc32_combine_op z_crc32_combine_op # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound @@ -349,6 +352,9 @@ # ifdef FAR # undef FAR # endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ @@ -467,11 +473,18 @@ typedef uLong FAR uLongf; # undef _LARGEFILE64_SOURCE #endif -#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) -# define Z_HAVE_UNISTD_H +#ifndef Z_HAVE_UNISTD_H +# ifdef __WATCOMC__ +# define Z_HAVE_UNISTD_H +# endif +#endif +#ifndef Z_HAVE_UNISTD_H +# if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) +# define Z_HAVE_UNISTD_H +# endif #endif #ifndef Z_SOLO -# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# if defined(Z_HAVE_UNISTD_H) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ diff --git a/3rdparty/zlib/zlib.h b/3rdparty/zlib/zlib.h index 4a98e38bf3..953cb5012d 100644 --- a/3rdparty/zlib/zlib.h +++ b/3rdparty/zlib/zlib.h @@ -1,5 +1,5 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.12, March 11th, 2022 + version 1.2.13, October 13th, 2022 Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.12" -#define ZLIB_VERNUM 0x12c0 +#define ZLIB_VERSION "1.2.13" +#define ZLIB_VERNUM 0x12d0 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 12 +#define ZLIB_VER_REVISION 13 #define ZLIB_VER_SUBREVISION 0 /* @@ -276,7 +276,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. See deflatePending(), - which can be used if desired to determine whether or not there is more ouput + which can be used if desired to determine whether or not there is more output in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to @@ -660,7 +660,7 @@ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If deflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similary, if dictLength is Z_NULL, then it is not set. + Similarly, if dictLength is Z_NULL, then it is not set. deflateGetDictionary() may return a length less than the window size, even when more than the window size in input has been provided. It may return up @@ -915,7 +915,7 @@ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similary, if dictLength is Z_NULL, then it is not set. + Similarly, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. @@ -1437,12 +1437,12 @@ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, In the event that the end of file is reached and only a partial item is available at the end, i.e. the remaining uncompressed data length is not a - multiple of size, then the final partial item is nevetheless read into buf + multiple of size, then the final partial item is nevertheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior is the same as the behavior of fread() implementations in common libraries, but it prevents the direct use of gzfread() to read a concurrently written - file, reseting and retrying on end-of-file, when size is not 1. + file, resetting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); @@ -1913,7 +1913,7 @@ ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); -ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if defined(_WIN32) && !defined(Z_SOLO) diff --git a/3rdparty/zlib/zutil.c b/3rdparty/zlib/zutil.c index dcab28a0d5..9543ae825e 100644 --- a/3rdparty/zlib/zutil.c +++ b/3rdparty/zlib/zutil.c @@ -61,9 +61,11 @@ uLong ZEXPORT zlibCompileFlags() #ifdef ZLIB_DEBUG flags += 1 << 8; #endif + /* #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif + */ #ifdef ZLIB_WINAPI flags += 1 << 10; #endif @@ -119,7 +121,7 @@ uLong ZEXPORT zlibCompileFlags() # endif int ZLIB_INTERNAL z_verbose = verbose; -void ZLIB_INTERNAL z_error (m) +void ZLIB_INTERNAL z_error(m) char *m; { fprintf(stderr, "%s\n", m); @@ -214,7 +216,7 @@ local ptr_table table[MAX_PTR]; * a protected system like OS/2. Use Microsoft C instead. */ -voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; @@ -240,7 +242,7 @@ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) return buf; } -void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { int n; @@ -277,13 +279,13 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) # define _hfree hfree #endif -voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } -void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); @@ -302,7 +304,7 @@ extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif -voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) +voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) voidpf opaque; unsigned items; unsigned size; @@ -312,7 +314,7 @@ voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) (voidpf)calloc(items, size); } -void ZLIB_INTERNAL zcfree (opaque, ptr) +void ZLIB_INTERNAL zcfree(opaque, ptr) voidpf opaque; voidpf ptr; { diff --git a/3rdparty/zlib/zutil.h b/3rdparty/zlib/zutil.h index d9a20ae1bf..0bc7f4ecd1 100644 --- a/3rdparty/zlib/zutil.h +++ b/3rdparty/zlib/zutil.h @@ -193,6 +193,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); #endif /* common defaults */ From 687c9b7b2906dd109db69599d855e11ce03b7030 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Wed, 16 Nov 2022 10:41:13 +0300 Subject: [PATCH 205/313] log QR version and corners --- modules/objdetect/src/qrcode.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 23cd4f3ad4..2bbb38fc6c 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -8,6 +8,7 @@ #include "precomp.hpp" #include "opencv2/objdetect.hpp" #include "opencv2/calib3d.hpp" +#include #ifdef HAVE_QUIRC #include "quirc.h" @@ -2360,6 +2361,9 @@ bool QRDecode::versionDefinition() version = saturate_cast((std::min(transition_x, transition_y) - 1) * 0.25 - 1); if ( !( 0 < version && version <= 40 ) ) { return false; } version_size = 21 + (version - 1) * 4; + CV_LOG_INFO(NULL, "QR corners: " << original_points[0] << " " << original_points[1] << " " << original_points[2] << + " " << original_points[3]); + CV_LOG_INFO(NULL, "QR version: " << (int)version); return true; } From 45d04175d4211254105cebfe6304805eb48a6823 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Wed, 16 Nov 2022 11:14:17 +0200 Subject: [PATCH 206/313] Suppress warning #1394-D: field of class type without a DLL interface used in a class with a DLL interface from windows CUDA builds. --- cmake/OpenCVDetectCUDA.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 69d0455cc8..275fdb6f71 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -421,6 +421,10 @@ if(CUDA_FOUND) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fno-finite-math-only) endif() + if(WIN32) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388) + endif() + if(CMAKE_CROSSCOMPILING AND (ARM OR AARCH64)) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xlinker --unresolved-symbols=ignore-in-shared-libs) endif() From 4c74e6d89d35dc45a3889d96158989352f73b1db Mon Sep 17 00:00:00 2001 From: Alexey Smirnov Date: Fri, 23 Sep 2022 12:08:29 +0100 Subject: [PATCH 207/313] Copy mpashchenkov's changes Minor refactoring Partially address review comments Move DX-related stuff from the sample to a default source Simplify the default OneVPL config Address minor review comments Add class for the default VPL source WIP: Add initial stub for tests with description Removing default vpl source and minor refactoring Refactor default files Fix build and application crash Address review comments Add test on VPL + OCL interaction compared to CPU behavior Fix test --- modules/gapi/CMakeLists.txt | 13 ++- .../include/opencv2/gapi/ocl/goclkernel.hpp | 4 + .../opencv2/gapi/streaming/onevpl/default.hpp | 29 +++++ ..._infer_with_advanced_device_selection.cpp} | 0 .../samples/onevpl_source_to_bgr_conv.cpp | 106 ++++++++++++++++++ modules/gapi/src/backends/ocl/goclbackend.cpp | 4 +- modules/gapi/src/backends/ocl/goclcore.cpp | 95 ++++++++++++++++ .../surface/cpu_frame_adapter.cpp | 1 - .../surface/dx11_frame_adapter.cpp | 19 +++- .../surface/dx11_frame_adapter.hpp | 5 + .../onevpl/cfg_param_device_selector.hpp | 2 +- modules/gapi/src/streaming/onevpl/default.cpp | 52 +++++++++ .../engine/decode/decode_engine_legacy.cpp | 3 +- .../onevpl/engine/preproc/preproc_engine.cpp | 2 +- modules/gapi/src/streaming/onevpl/utils.cpp | 1 + .../gapi_streaming_vpl_core_test.cpp | 68 +++++++++++ 16 files changed, 387 insertions(+), 17 deletions(-) create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp rename modules/gapi/samples/{onevpl_infer_single_roi.cpp => onevpl_infer_with_advanced_device_selection.cpp} (100%) create mode 100644 modules/gapi/samples/onevpl_source_to_bgr_conv.cpp create mode 100644 modules/gapi/src/streaming/onevpl/default.cpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 2a9d598bb8..a25af7a5c2 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -188,6 +188,7 @@ set(gapi_srcs src/streaming/onevpl/cfg_params.cpp src/streaming/onevpl/cfg_params_parser.cpp src/streaming/onevpl/utils.cpp + src/streaming/onevpl/default.cpp src/streaming/onevpl/data_provider_interface_exception.cpp src/streaming/onevpl/accelerators/surface/base_frame_adapter.cpp src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp @@ -367,20 +368,20 @@ ocv_add_samples() # Required for sample with inference on host -if(TARGET example_gapi_onevpl_infer_single_roi) +if(TARGET example_gapi_onevpl_infer_with_advanced_device_selection) if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) - ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE ocv.3rdparty.openvino) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE ocv.3rdparty.openvino) endif() if(HAVE_DIRECTX AND HAVE_D3D11) - ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE d3d11 dxgi) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE d3d11 dxgi) endif() if(HAVE_D3D11 AND HAVE_OPENCL) - ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) + ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() if(UNIX AND HAVE_VA) message ("GAPI VPL samples with VAAPI") - ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${VA_INCLUDE_DIR}) - ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE ${VA_LIBRARIES}) + ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE ${VA_LIBRARIES}) endif() endif() diff --git a/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp index 6a2f1df769..b70b54267d 100644 --- a/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp @@ -119,6 +119,10 @@ template struct ocl_get_in > { static const std::vector& get(GOCLContext &ctx, int idx) { return ctx.inArg(idx).rref(); } }; +template<> struct ocl_get_in +{ + static cv::MediaFrame get(GOCLContext &ctx, int idx) { return ctx.inArg(idx); } +}; template struct ocl_get_in > { static const U& get(GOCLContext &ctx, int idx) { return ctx.inArg(idx).rref(); } diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp new file mode 100644 index 0000000000..8b547e1aba --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp @@ -0,0 +1,29 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP +#define OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP + +#include // GAPI_EXPORTS +#include +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +/** + * @brief Provides default device selector based on config. + */ +GAPI_EXPORTS std::shared_ptr getDefaultDeviceSelector(const std::vector& cfg_params); + +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP diff --git a/modules/gapi/samples/onevpl_infer_single_roi.cpp b/modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp similarity index 100% rename from modules/gapi/samples/onevpl_infer_single_roi.cpp rename to modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp diff --git a/modules/gapi/samples/onevpl_source_to_bgr_conv.cpp b/modules/gapi/samples/onevpl_source_to_bgr_conv.cpp new file mode 100644 index 0000000000..660d7ed9a8 --- /dev/null +++ b/modules/gapi/samples/onevpl_source_to_bgr_conv.cpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include // CommandLineParser +#include + +const std::string about = + "This is an example presents decoding on GPU using VPL Source and passing it to OpenCL backend"; +const std::string keys = + "{ h help | | Print this help message }" + "{ input | | Path to the input video file. Use .avi extension }" + "{ accel_mode | mfxImplDescription.AccelerationMode:MFX_ACCEL_MODE_VIA_D3D11 | Acceleration mode for VPL }"; + +namespace { +namespace cfg { +// FIXME: Move OneVPL arguments parser to a single place +typename cv::gapi::wip::onevpl::CfgParam create_from_string(const std::string &line); +} // namespace cfg +} // anonymous namespace + +int main(int argc, char *argv[]) { + cv::CommandLineParser cmd(argc, argv, keys); + cmd.about(about); + if (cmd.has("help")) { + cmd.printMessage(); + return 0; + } + + // Get file name + const auto input = cmd.get("input"); + const auto accel_mode = cmd.get("accel_mode"); + + // Create VPL config + std::vector source_cfgs; + source_cfgs.push_back(cfg::create_from_string(accel_mode)); + + // Create VPL-based source + std::shared_ptr default_device_selector = + cv::gapi::wip::onevpl::getDefaultDeviceSelector(source_cfgs); + + cv::gapi::wip::IStreamSource::Ptr source = cv::gapi::wip::make_onevpl_src(input, source_cfgs, + default_device_selector); + + // Build the graph + cv::GFrame in; // input frame from VPL source + auto bgr_gmat = cv::gapi::streaming::BGR(in); // conversion from VPL source frame to BGR UMat + auto out = cv::gapi::blur(bgr_gmat, cv::Size(4,4)); // ocl kernel of blur operation + + cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out)) + .compileStreaming(cv::compile_args(cv::gapi::core::ocl::kernels())); + pipeline.setSource(std::move(source)); + + // The execution part + size_t frames = 0u; + cv::TickMeter tm; + cv::Mat outMat; + + pipeline.start(); + tm.start(); + + while (pipeline.pull(cv::gout(outMat))) { + cv::imshow("OutVideo", outMat); + cv::waitKey(1); + ++frames; + } + tm.stop(); + std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl; + + return 0; +} + +namespace { +namespace cfg { +typename cv::gapi::wip::onevpl::CfgParam create_from_string(const std::string &line) { + using namespace cv::gapi::wip; + + if (line.empty()) { + throw std::runtime_error("Cannot parse CfgParam from emply line"); + } + + std::string::size_type name_endline_pos = line.find(':'); + if (name_endline_pos == std::string::npos) { + throw std::runtime_error("Cannot parse CfgParam from: " + line + + "\nExpected separator \":\""); + } + + std::string name = line.substr(0, name_endline_pos); + std::string value = line.substr(name_endline_pos + 1); + + return cv::gapi::wip::onevpl::CfgParam::create(name, value, + /* vpp params strongly optional */ + name.find("vpp.") == std::string::npos); +} +} // namespace cfg +} // anonymous namespace diff --git a/modules/gapi/src/backends/ocl/goclbackend.cpp b/modules/gapi/src/backends/ocl/goclbackend.cpp index dba2b27b59..9c6d7154e4 100644 --- a/modules/gapi/src/backends/ocl/goclbackend.cpp +++ b/modules/gapi/src/backends/ocl/goclbackend.cpp @@ -114,7 +114,8 @@ cv::GArg cv::gimpl::GOCLExecutable::packArg(const GArg &arg) GAPI_Assert( arg.kind != cv::detail::ArgKind::GMAT && arg.kind != cv::detail::ArgKind::GSCALAR && arg.kind != cv::detail::ArgKind::GARRAY - && arg.kind != cv::detail::ArgKind::GOPAQUE); + && arg.kind != cv::detail::ArgKind::GOPAQUE + && arg.kind != cv::detail::ArgKind::GFRAME); if (arg.kind != cv::detail::ArgKind::GOBJREF) { @@ -136,6 +137,7 @@ cv::GArg cv::gimpl::GOCLExecutable::packArg(const GArg &arg) // Note: .at() is intentional for GOpaque as object MUST be already there // (and constructed by either bindIn/Out or resetInternal) case GShape::GOPAQUE: return GArg(m_res.slot().at(ref.id)); + case GShape::GFRAME: return GArg(m_res.slot().at(ref.id)); default: util::throw_error(std::logic_error("Unsupported GShape type")); break; diff --git a/modules/gapi/src/backends/ocl/goclcore.cpp b/modules/gapi/src/backends/ocl/goclcore.cpp index f3c5aa32bc..19fa54a40a 100644 --- a/modules/gapi/src/backends/ocl/goclcore.cpp +++ b/modules/gapi/src/backends/ocl/goclcore.cpp @@ -6,11 +6,32 @@ #include "precomp.hpp" +#include "logger.hpp" #include #include +#include + #include "backends/ocl/goclcore.hpp" +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +#pragma comment(lib,"d3d11.lib") + +// get rid of generate macro max/min/etc from DX side +#define D3D11_NO_HELPERS +#define NOMINMAX +#include +#pragma comment(lib, "dxgi") +#undef NOMINMAX +#undef D3D11_NO_HELPERS +#include +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX + +#include +#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp" + GAPI_OCL_KERNEL(GOCLAdd, cv::gapi::core::GAdd) { static void run(const cv::UMat& a, const cv::UMat& b, int dtype, cv::UMat& out) @@ -523,6 +544,79 @@ GAPI_OCL_KERNEL(GOCLTranspose, cv::gapi::core::GTranspose) } }; +GAPI_OCL_KERNEL(GOCLBGR, cv::gapi::streaming::GBGR) +{ + static void run(const cv::MediaFrame& in, cv::UMat& out) + { + cv::util::suppress_unused_warning(in); + cv::util::suppress_unused_warning(out); +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +#ifdef HAVE_ONEVPL + auto d = in.desc(); + if (d.fmt != cv::MediaFormat::NV12) + { + GAPI_LOG_FATAL(nullptr, "Unsupported format provided: " << static_cast(d.fmt) << + ". Expected cv::MediaFormat::NV12."); + cv::util::throw_error(std::logic_error("Unsupported MediaFrame format provided")); + } + + // FIXME: consider a better solution. + // Current approach cannot be easily extended for other adapters (getHandle). + auto adapterPtr = in.get(); + if (adapterPtr == nullptr) + { + GAPI_LOG_FATAL(nullptr, "Unsupported adapter type. Only VPLMediaFrameDX11Adapter is supported"); + cv::util::throw_error(std::logic_error("Unsupported adapter type. Only VPLMediaFrameDX11Adapter is supported")); + } + + auto params = adapterPtr->getHandle(); + auto handle = cv::util::any_cast(params); + ID3D11Texture2D* texture = reinterpret_cast(handle.first); + if (texture == nullptr) + { + GAPI_LOG_FATAL(nullptr, "mfxHDLPair contains ID3D11Texture2D that is nullptr. Handle address" << + reinterpret_cast(handle.first)); + cv::util::throw_error(std::logic_error("mfxHDLPair contains ID3D11Texture2D that is nullptr")); + } + + // FIXME: Assuming here that we only have 1 device + // TODO: Textures are reusable, so to improve the peroformance here + // consider creating a hash map texture <-> device/ctx + static thread_local ID3D11Device* pD3D11Device = nullptr; + if (pD3D11Device == nullptr) + { + texture->GetDevice(&pD3D11Device); + } + if (pD3D11Device == nullptr) + { + GAPI_LOG_FATAL(nullptr, "D3D11Texture2D::GetDevice returns pD3D11Device that is nullptr"); + cv::util::throw_error(std::logic_error("D3D11Texture2D::GetDevice returns pD3D11Device that is nullptr")); + } + + // FIXME: assuming here that the context is always the same + // TODO: Textures are reusable, so to improve the peroformance here + // consider creating a hash map texture <-> device/ctx + static thread_local cv::ocl::Context ctx = cv::directx::ocl::initializeContextFromD3D11Device(pD3D11Device); + if (ctx.ptr() == nullptr) + { + GAPI_LOG_FATAL(nullptr, "initializeContextFromD3D11Device returned null context"); + cv::util::throw_error(std::logic_error("initializeContextFromD3D11Device returned null context")); + } + + cv::directx::convertFromD3D11Texture2D(texture, out); +#else + GAPI_LOG_FATAL(nullptr, "HAVE_ONEVPL is not set. Please, check your cmake flags"); + cv::util::throw_error(std::logic_error("HAVE_ONEVPL is not set. Please, check your cmake flags")); +#endif // HAVE_ONEVPL +#else + GAPI_LOG_FATAL(nullptr, "HAVE_D3D11 or HAVE_DIRECTX is not set. Please, check your cmake flags"); + cv::util::throw_error(std::logic_error("HAVE_D3D11 or HAVE_DIRECTX is not set. Please, check your cmake flags")); +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX + } +}; + cv::GKernelPackage cv::gapi::core::ocl::kernels() { static auto pkg = cv::gapi::kernels @@ -587,6 +681,7 @@ cv::GKernelPackage cv::gapi::core::ocl::kernels() , GOCLLUT , GOCLConvertTo , GOCLTranspose + , GOCLBGR >(); return pkg; } diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp index 24a5b9fb7f..f78d97d571 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp @@ -65,7 +65,6 @@ MediaFrame::View VPLMediaFrameCPUAdapter::access(MediaFrame::Access) { cv::util::any VPLMediaFrameCPUAdapter::blobParams() const { throw std::runtime_error("VPLMediaFrameCPUAdapter::blobParams() is not implemented"); - return {}; } void VPLMediaFrameCPUAdapter::serialize(cv::gapi::s11n::IOStream&) { diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp index fad26e50a8..3744ddc8ce 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp @@ -114,17 +114,24 @@ MediaFrame::View VPLMediaFrameDX11Adapter::access(MediaFrame::Access mode) { } } -cv::util::any VPLMediaFrameDX11Adapter::blobParams() const { - /*GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not fully integrated" - "in OpenVINO InferenceEngine and would be temporary disable.");*/ -#ifdef HAVE_INF_ENGINE +mfxHDLPair VPLMediaFrameDX11Adapter::getHandle() const { auto surface_ptr_copy = get_surface(); - Surface::data_t& data = surface_ptr_copy->get_data(); - const Surface::info_t& info = surface_ptr_copy->get_info(); + const Surface::data_t& data = surface_ptr_copy->get_data(); NativeHandleAdapter* native_handle_getter = reinterpret_cast(data.MemId); mfxHDLPair handle{}; native_handle_getter->get_handle(data.MemId, reinterpret_cast(handle)); + return handle; +} + +cv::util::any VPLMediaFrameDX11Adapter::blobParams() const { + /*GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not fully integrated" + "in OpenVINO InferenceEngine and would be temporary disable.");*/ +#ifdef HAVE_INF_ENGINE + mfxHDLPair handle = getHandle(); + + auto surface_ptr_copy = get_surface(); + const Surface::info_t& info = surface_ptr_copy->get_info(); GAPI_Assert(frame_desc.fmt == MediaFormat::NV12 && "blobParams() for VPLMediaFrameDX11Adapter supports NV12 only"); diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp index 39528ca6a5..a5eddbb407 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp @@ -37,6 +37,11 @@ public: GAPI_EXPORTS ~VPLMediaFrameDX11Adapter(); MediaFrame::View access(MediaFrame::Access) override; + // FIXME: Consider a better solution since this approach + // is not easily extendable for other adapters (oclcore.cpp) + // FIXME: Use with caution since the handle might become invalid + // due to reference counting + mfxHDLPair getHandle() const; // The default implementation does nothing cv::util::any blobParams() const override; void serialize(cv::gapi::s11n::IOStream&) override; diff --git a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.hpp b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.hpp index f7672ce924..18b468fd86 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.hpp +++ b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.hpp @@ -20,7 +20,7 @@ namespace gapi { namespace wip { namespace onevpl { -class PlatformSpecificParams; +struct PlatformSpecificParams; std::vector update_param_with_accel_type(std::vector &¶m_array, AccelType type); struct GAPI_EXPORTS CfgParamDeviceSelector final: public IDeviceSelector { diff --git a/modules/gapi/src/streaming/onevpl/default.cpp b/modules/gapi/src/streaming/onevpl/default.cpp new file mode 100644 index 0000000000..0456915a00 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/default.cpp @@ -0,0 +1,52 @@ +// 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. +// +// Copyright (C) 2022 Intel Corporation + +#include +#include + +#include +#include +#include + +#include "cfg_param_device_selector.hpp" + +#ifdef HAVE_ONEVPL + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +std::shared_ptr getDefaultDeviceSelector(const std::vector& cfg_params) { + std::shared_ptr default_accel_contex(new CfgParamDeviceSelector(cfg_params)); + + return default_accel_contex; +} + +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#else // HAVE_ONEVPL + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +std::shared_ptr getDefaultDeviceSelector(const std::vector&) { + std::cerr << "Cannot utilize getDefaultVPLDeviceAndCtx without HAVE_ONEVPL enabled" << std::endl; + util::throw_error(std::logic_error("Cannot utilize getDefaultVPLDeviceAndCtx without HAVE_ONEVPL enabled")); + return nullptr; +} + +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp index 0ab8301799..8a35cca063 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp @@ -251,7 +251,8 @@ VPLLegacyDecodeEngine::SessionParam VPLLegacyDecodeEngine::prepare_session_param } - decRequest.Type |= MFX_MEMTYPE_EXTERNAL_FRAME | MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_VPPIN; + decRequest.Type |= MFX_MEMTYPE_EXTERNAL_FRAME | MFX_MEMTYPE_FROM_DECODE | + MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_SHARED_RESOURCE; VPLAccelerationPolicy::pool_key_t decode_pool_key = acceleration_policy->create_surface_pool(decRequest, mfxDecParams.mfx.FrameInfo); diff --git a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp index 10ce92e20a..d0c4a85f38 100644 --- a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp @@ -281,7 +281,7 @@ pp_session VPPPreprocEngine::initialize_preproc(const pp_params& initial_frame_p vppRequests[1].AllocId = std::numeric_limits::max() - request_id++; GAPI_Assert(request_id != std::numeric_limits::max() && "Something wrong"); - vppRequests[1].Type |= MFX_MEMTYPE_FROM_VPPIN; + vppRequests[1].Type |= MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_SHARED_RESOURCE; vpp_out_pool_key = acceleration_policy->create_surface_pool(vppRequests[1], mfxVPPParams.vpp.Out); diff --git a/modules/gapi/src/streaming/onevpl/utils.cpp b/modules/gapi/src/streaming/onevpl/utils.cpp index efd1618b71..a5513f5377 100644 --- a/modules/gapi/src/streaming/onevpl/utils.cpp +++ b/modules/gapi/src/streaming/onevpl/utils.cpp @@ -429,4 +429,5 @@ std::string ext_mem_frame_type_to_cstr(int type) { } // namespace wip } // namespace gapi } // namespace cv + #endif // HAVE_ONEVPL diff --git a/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp b/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp index d83b09d7d3..cabfe15d7e 100644 --- a/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp @@ -8,6 +8,7 @@ #include "../test_precomp.hpp" #include "../common/gapi_streaming_tests_common.hpp" +#include "../common/gapi_tests_common.hpp" #include #include @@ -29,6 +30,7 @@ #ifdef HAVE_ONEVPL #include +#include #include "streaming/onevpl/file_data_provider.hpp" #include "streaming/onevpl/cfg_param_device_selector.hpp" @@ -327,6 +329,72 @@ TEST(OneVPL_Source_CPU_FrameAdapter, InitFrameAdapter) EXPECT_TRUE(0 == surf->get_locks_count()); } +TEST(OneVPL_Source_Default_Source_With_OCL_Backend, Accuracy) +{ + using namespace cv::gapi::wip::onevpl; + + auto create_from_string = [](const std::string& line){ + std::string::size_type name_endline_pos = line.find(':'); + std::string name = line.substr(0, name_endline_pos); + std::string value = line.substr(name_endline_pos + 1); + return CfgParam::create(name, value); + }; + + std::vector source_cfgs; + source_cfgs.push_back(create_from_string("mfxImplDescription.AccelerationMode:MFX_ACCEL_MODE_VIA_D3D11")); + + // Create VPL-based source + std::shared_ptr default_device_selector = getDefaultDeviceSelector(source_cfgs); + + cv::gapi::wip::IStreamSource::Ptr source; + cv::gapi::wip::IStreamSource::Ptr source_cpu; + + auto input = findDataFile("cv/video/768x576.avi"); + try { + source = cv::gapi::wip::make_onevpl_src(input, source_cfgs, default_device_selector); + source_cpu = cv::gapi::wip::make_onevpl_src(input, source_cfgs, default_device_selector); + } catch(...) { + throw SkipTestException("Video file can not be opened"); + } + + // Build the graph w/ OCL backend + cv::GFrame in; // input frame from VPL source + auto bgr_gmat = cv::gapi::streaming::BGR(in); // conversion from VPL source frame to BGR UMat + auto out = cv::gapi::blur(bgr_gmat, cv::Size(4,4)); // ocl kernel of blur operation + + cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out)) + .compileStreaming(std::move(cv::compile_args(cv::gapi::core::ocl::kernels()))); + pipeline.setSource(std::move(source)); + + cv::GStreamingCompiled pipeline_cpu = cv::GComputation(cv::GIn(in), cv::GOut(out)) + .compileStreaming(std::move(cv::compile_args(cv::gapi::core::cpu::kernels()))); + pipeline_cpu.setSource(std::move(source_cpu)); + + // The execution part + cv::Mat out_mat; + std::vector ocl_mats, cpu_mats; + + // Run the pipelines + pipeline.start(); + while (pipeline.pull(cv::gout(out_mat))) + { + ocl_mats.push_back(out_mat); + } + + pipeline_cpu.start(); + while (pipeline_cpu.pull(cv::gout(out_mat))) + { + cpu_mats.push_back(out_mat); + } + + // Compare results + // FIXME: investigate why 2 sources produce different number of frames sometimes + for (size_t i = 0; i < std::min(ocl_mats.size(), cpu_mats.size()); ++i) + { + EXPECT_TRUE(AbsTolerance(1).to_compare_obj()(ocl_mats[i], cpu_mats[i])); + } +} + TEST(OneVPL_Source_CPU_Accelerator, InitDestroy) { using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy; From d21761c0fd1fa917d7e507b996d014540674f979 Mon Sep 17 00:00:00 2001 From: su77ungr <69374354+su77ungr@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:54:25 +0100 Subject: [PATCH 208/313] Merge pull request #22727 from su77ungr:patch-1 Rename file name to correct spelling --- ...us_16stages.xml => haarcascade_license_plate_rus_16stages.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data/haarcascades/{haarcascade_licence_plate_rus_16stages.xml => haarcascade_license_plate_rus_16stages.xml} (100%) diff --git a/data/haarcascades/haarcascade_licence_plate_rus_16stages.xml b/data/haarcascades/haarcascade_license_plate_rus_16stages.xml similarity index 100% rename from data/haarcascades/haarcascade_licence_plate_rus_16stages.xml rename to data/haarcascades/haarcascade_license_plate_rus_16stages.xml From 64aad34cb4abfb6a2603f3f4ecae7f4f0ba1414d Mon Sep 17 00:00:00 2001 From: Anatoliy Talamanov Date: Fri, 18 Nov 2022 15:25:51 +0000 Subject: [PATCH 209/313] Merge pull request #22735 from TolyaTalamanov:at/expose-all-imgproc-to-python G-API Expose all imgproc operations to python * Expose imgproc operations * Fix alignment --- modules/gapi/include/opencv2/gapi/imgproc.hpp | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp index cad7a403c9..96aaa5a447 100644 --- a/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -556,9 +556,9 @@ is at the kernel center. @param borderValue border value in case of constant border type @sa boxFilter, gaussianBlur, medianBlur */ -GAPI_EXPORTS GMat sepFilter(const GMat& src, int ddepth, const Mat& kernelX, const Mat& kernelY, const Point& anchor /*FIXME: = Point(-1,-1)*/, - const Scalar& delta /*FIXME = GScalar(0)*/, int borderType = BORDER_DEFAULT, - const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat sepFilter(const GMat& src, int ddepth, const Mat& kernelX, const Mat& kernelY, const Point& anchor /*FIXME: = Point(-1,-1)*/, + const Scalar& delta /*FIXME = GScalar(0)*/, int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); /** @brief Convolves an image with the kernel. @@ -593,8 +593,8 @@ is at the kernel center. @param borderValue border value in case of constant border type @sa sepFilter */ -GAPI_EXPORTS GMat filter2D(const GMat& src, int ddepth, const Mat& kernel, const Point& anchor = Point(-1,-1), const Scalar& delta = Scalar(0), - int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat filter2D(const GMat& src, int ddepth, const Mat& kernel, const Point& anchor = Point(-1,-1), const Scalar& delta = Scalar(0), + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); /** @brief Blurs an image using the box filter. @@ -627,9 +627,9 @@ is at the kernel center. @param borderValue border value in case of constant border type @sa sepFilter, gaussianBlur, medianBlur, integral */ -GAPI_EXPORTS GMat boxFilter(const GMat& src, int dtype, const Size& ksize, const Point& anchor = Point(-1,-1), - bool normalize = true, int borderType = BORDER_DEFAULT, - const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat boxFilter(const GMat& src, int dtype, const Size& ksize, const Point& anchor = Point(-1,-1), + bool normalize = true, int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); /** @brief Blurs an image using the normalized box filter. @@ -687,8 +687,8 @@ sigmaX, and sigmaY. @param borderValue border value in case of constant border type @sa sepFilter, boxFilter, medianBlur */ -GAPI_EXPORTS GMat gaussianBlur(const GMat& src, const Size& ksize, double sigmaX, double sigmaY = 0, - int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat gaussianBlur(const GMat& src, const Size& ksize, double sigmaX, double sigmaY = 0, + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); /** @brief Blurs an image using the median filter. @@ -730,9 +730,9 @@ anchor is at the element center. @param borderValue border value in case of a constant border @sa dilate, morphologyEx */ -GAPI_EXPORTS GMat erode(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, - int borderType = BORDER_CONSTANT, - const Scalar& borderValue = morphologyDefaultBorderValue()); +GAPI_EXPORTS_W GMat erode(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); /** @brief Erodes an image by using 3 by 3 rectangular structuring element. @@ -750,9 +750,9 @@ Output image must have the same type, size, and number of channels as the input @param borderValue border value in case of a constant border @sa erode, dilate3x3 */ -GAPI_EXPORTS GMat erode3x3(const GMat& src, int iterations = 1, - int borderType = BORDER_CONSTANT, - const Scalar& borderValue = morphologyDefaultBorderValue()); +GAPI_EXPORTS_W GMat erode3x3(const GMat& src, int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); /** @brief Dilates an image by using a specific structuring element. @@ -777,9 +777,9 @@ anchor is at the element center. @param borderValue border value in case of a constant border @sa erode, morphologyEx, getStructuringElement */ -GAPI_EXPORTS GMat dilate(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, - int borderType = BORDER_CONSTANT, - const Scalar& borderValue = morphologyDefaultBorderValue()); +GAPI_EXPORTS_W GMat dilate(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); /** @brief Dilates an image by using 3 by 3 rectangular structuring element. @@ -801,9 +801,9 @@ Output image must have the same type, size, and number of channels as the input @sa dilate, erode3x3 */ -GAPI_EXPORTS GMat dilate3x3(const GMat& src, int iterations = 1, - int borderType = BORDER_CONSTANT, - const Scalar& borderValue = morphologyDefaultBorderValue()); +GAPI_EXPORTS_W GMat dilate3x3(const GMat& src, int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); /** @brief Performs advanced morphological transformations. @@ -831,11 +831,11 @@ the kernel center. meaning. @sa dilate, erode, getStructuringElement */ -GAPI_EXPORTS GMat morphologyEx(const GMat &src, const MorphTypes op, const Mat &kernel, - const Point &anchor = Point(-1,-1), - const int iterations = 1, - const BorderTypes borderType = BORDER_CONSTANT, - const Scalar &borderValue = morphologyDefaultBorderValue()); +GAPI_EXPORTS_W GMat morphologyEx(const GMat &src, const MorphTypes op, const Mat &kernel, + const Point &anchor = Point(-1,-1), + const int iterations = 1, + const BorderTypes borderType = BORDER_CONSTANT, + const Scalar &borderValue = morphologyDefaultBorderValue()); /** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. @@ -883,10 +883,10 @@ applied (see cv::getDerivKernels for details). @param borderValue border value in case of constant border type @sa filter2D, gaussianBlur, cartToPolar */ -GAPI_EXPORTS GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize = 3, - double scale = 1, double delta = 0, - int borderType = BORDER_DEFAULT, - const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); /** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. @@ -934,10 +934,10 @@ applied (see cv::getDerivKernels for details). @param borderValue border value in case of constant border type @sa filter2D, gaussianBlur, cartToPolar */ -GAPI_EXPORTS std::tuple SobelXY(const GMat& src, int ddepth, int order, int ksize = 3, - double scale = 1, double delta = 0, - int borderType = BORDER_DEFAULT, - const Scalar& borderValue = Scalar(0)); +GAPI_EXPORTS_W std::tuple SobelXY(const GMat& src, int ddepth, int order, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); /** @brief Calculates the Laplacian of an image. @@ -964,8 +964,8 @@ applied. See #getDerivKernels for details. @return Destination image of the same size and the same number of channels as src. @sa Sobel, Scharr */ -GAPI_EXPORTS GMat Laplacian(const GMat& src, int ddepth, int ksize = 1, - double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT); +GAPI_EXPORTS_W GMat Laplacian(const GMat& src, int ddepth, int ksize = 1, + double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT); /** @brief Applies the bilateral filter to an image. @@ -998,8 +998,8 @@ proportional to sigmaSpace. @param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes @return Destination image of the same size and type as src. */ -GAPI_EXPORTS GMat bilateralFilter(const GMat& src, int d, double sigmaColor, double sigmaSpace, - int borderType = BORDER_DEFAULT); +GAPI_EXPORTS_W GMat bilateralFilter(const GMat& src, int d, double sigmaColor, double sigmaSpace, + int borderType = BORDER_DEFAULT); //! @} gapi_filters @@ -1023,8 +1023,8 @@ largest value is used to find initial segments of strong edges. See L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( L2gradient=false ). */ -GAPI_EXPORTS GMat Canny(const GMat& image, double threshold1, double threshold2, - int apertureSize = 3, bool L2gradient = false); +GAPI_EXPORTS_W GMat Canny(const GMat& image, double threshold1, double threshold2, + int apertureSize = 3, bool L2gradient = false); /** @brief Determines strong corners on an image. @@ -1070,14 +1070,14 @@ or #cornerMinEigenVal. @return vector of detected corners. */ -GAPI_EXPORTS_W GArray goodFeaturesToTrack(const GMat &image, - int maxCorners, - double qualityLevel, - double minDistance, - const Mat &mask = Mat(), - int blockSize = 3, - bool useHarrisDetector = false, - double k = 0.04); +GAPI_EXPORTS_W GArray goodFeaturesToTrack(const GMat &image, + int maxCorners, + double qualityLevel, + double minDistance, + const Mat &mask = Mat(), + int blockSize = 3, + bool useHarrisDetector = false, + double k = 0.04); /** @brief Equalizes the histogram of a grayscale image. @@ -1098,7 +1098,7 @@ The algorithm normalizes the brightness and increases the contrast of the image. @param src Source 8-bit single channel image. */ -GAPI_EXPORTS GMat equalizeHist(const GMat& src); +GAPI_EXPORTS_W GMat equalizeHist(const GMat& src); //! @addtogroup gapi_shape //! @{ @@ -1209,7 +1209,7 @@ Calculates the up-right bounding rectangle of a point set. @param src Input 2D point set, stored in std::vector. */ -GAPI_EXPORTS GOpaque boundingRect(const GArray& src); +GAPI_EXPORTS_W GOpaque boundingRect(const GArray& src); /** @brief Fits a line to a 2D point set. @@ -1399,7 +1399,7 @@ Resulting gray color value computed as @param bY float multiplier for B channel. @sa RGB2YUV */ -GAPI_EXPORTS GMat RGB2Gray(const GMat& src, float rY, float gY, float bY); +GAPI_EXPORTS_W GMat RGB2Gray(const GMat& src, float rY, float gY, float bY); /** @brief Converts an image from BGR color space to gray-scaled. @@ -1412,7 +1412,7 @@ Resulting gray color value computed as @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. @sa BGR2LUV */ -GAPI_EXPORTS GMat BGR2Gray(const GMat& src); +GAPI_EXPORTS_W GMat BGR2Gray(const GMat& src); /** @brief Converts an image from RGB color space to YUV color space. @@ -1429,7 +1429,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2RGB, RGB2Lab */ -GAPI_EXPORTS GMat RGB2YUV(const GMat& src); +GAPI_EXPORTS_W GMat RGB2YUV(const GMat& src); /** @brief Converts an image from BGR color space to I420 color space. @@ -1445,7 +1445,7 @@ Height of I420 output image must be equal 3/2 from height of input image. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa I4202BGR */ -GAPI_EXPORTS GMat BGR2I420(const GMat& src); +GAPI_EXPORTS_W GMat BGR2I420(const GMat& src); /** @brief Converts an image from RGB color space to I420 color space. @@ -1461,7 +1461,7 @@ Height of I420 output image must be equal 3/2 from height of input image. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa I4202RGB */ -GAPI_EXPORTS GMat RGB2I420(const GMat& src); +GAPI_EXPORTS_W GMat RGB2I420(const GMat& src); /** @brief Converts an image from I420 color space to BGR color space. @@ -1477,7 +1477,7 @@ Height of BGR output image must be equal 2/3 from height of input image. @param src input image: 8-bit unsigned 1-channel image @ref CV_8UC1. @sa BGR2I420 */ -GAPI_EXPORTS GMat I4202BGR(const GMat& src); +GAPI_EXPORTS_W GMat I4202BGR(const GMat& src); /** @brief Converts an image from I420 color space to BGR color space. @@ -1493,7 +1493,7 @@ Height of RGB output image must be equal 2/3 from height of input image. @param src input image: 8-bit unsigned 1-channel image @ref CV_8UC1. @sa RGB2I420 */ -GAPI_EXPORTS GMat I4202RGB(const GMat& src); +GAPI_EXPORTS_W GMat I4202RGB(const GMat& src); /** @brief Converts an image from BGR color space to LUV color space. @@ -1507,7 +1507,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa RGB2Lab, RGB2LUV */ -GAPI_EXPORTS GMat BGR2LUV(const GMat& src); +GAPI_EXPORTS_W GMat BGR2LUV(const GMat& src); /** @brief Converts an image from LUV color space to BGR color space. @@ -1521,7 +1521,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa BGR2LUV */ -GAPI_EXPORTS GMat LUV2BGR(const GMat& src); +GAPI_EXPORTS_W GMat LUV2BGR(const GMat& src); /** @brief Converts an image from YUV color space to BGR color space. @@ -1535,7 +1535,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa BGR2YUV */ -GAPI_EXPORTS GMat YUV2BGR(const GMat& src); +GAPI_EXPORTS_W GMat YUV2BGR(const GMat& src); /** @brief Converts an image from BGR color space to YUV color space. @@ -1549,7 +1549,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2BGR */ -GAPI_EXPORTS GMat BGR2YUV(const GMat& src); +GAPI_EXPORTS_W GMat BGR2YUV(const GMat& src); /** @brief Converts an image from RGB color space to Lab color space. @@ -1563,7 +1563,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC1. @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. @sa RGB2YUV, RGB2LUV */ -GAPI_EXPORTS GMat RGB2Lab(const GMat& src); +GAPI_EXPORTS_W GMat RGB2Lab(const GMat& src); /** @brief Converts an image from YUV color space to RGB. The function converts an input image from YUV color space to RGB. @@ -1577,7 +1577,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa RGB2Lab, RGB2YUV */ -GAPI_EXPORTS GMat YUV2RGB(const GMat& src); +GAPI_EXPORTS_W GMat YUV2RGB(const GMat& src); /** @brief Converts an image from NV12 (YUV420p) color space to RGB. The function converts an input image from NV12 color space to RGB. @@ -1592,7 +1592,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2RGB, NV12toBGR */ -GAPI_EXPORTS GMat NV12toRGB(const GMat& src_y, const GMat& src_uv); +GAPI_EXPORTS_W GMat NV12toRGB(const GMat& src_y, const GMat& src_uv); /** @brief Converts an image from NV12 (YUV420p) color space to gray-scaled. The function converts an input image from NV12 color space to gray-scaled. @@ -1607,7 +1607,7 @@ Output image must be 8-bit unsigned 1-channel image @ref CV_8UC1. @sa YUV2RGB, NV12toBGR */ -GAPI_EXPORTS GMat NV12toGray(const GMat& src_y, const GMat& src_uv); +GAPI_EXPORTS_W GMat NV12toGray(const GMat& src_y, const GMat& src_uv); /** @brief Converts an image from NV12 (YUV420p) color space to BGR. The function converts an input image from NV12 color space to RGB. @@ -1622,7 +1622,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2BGR, NV12toRGB */ -GAPI_EXPORTS GMat NV12toBGR(const GMat& src_y, const GMat& src_uv); +GAPI_EXPORTS_W GMat NV12toBGR(const GMat& src_y, const GMat& src_uv); /** @brief Converts an image from BayerGR color space to RGB. The function converts an input image from BayerGR color space to RGB. @@ -1636,7 +1636,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2BGR, NV12toRGB */ -GAPI_EXPORTS GMat BayerGR2RGB(const GMat& src_gr); +GAPI_EXPORTS_W GMat BayerGR2RGB(const GMat& src_gr); /** @brief Converts an image from RGB color space to HSV. The function converts an input image from RGB color space to HSV. @@ -1650,7 +1650,7 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2BGR, NV12toRGB */ -GAPI_EXPORTS GMat RGB2HSV(const GMat& src); +GAPI_EXPORTS_W GMat RGB2HSV(const GMat& src); /** @brief Converts an image from RGB color space to YUV422. The function converts an input image from RGB color space to YUV422. @@ -1664,7 +1664,7 @@ Output image must be 8-bit unsigned 2-channel image @ref CV_8UC2. @sa YUV2BGR, NV12toRGB */ -GAPI_EXPORTS GMat RGB2YUV422(const GMat& src); +GAPI_EXPORTS_W GMat RGB2YUV422(const GMat& src); /** @brief Converts an image from NV12 (YUV420p) color space to RGB. The function converts an input image from NV12 color space to RGB. From e93d976d0057c25996061981f7e264528ac46b2a Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Sat, 19 Nov 2022 01:56:45 +0300 Subject: [PATCH 210/313] gapi: fix InferWithReshape test crash when data is not found --- modules/gapi/test/infer/gapi_infer_ie_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gapi/test/infer/gapi_infer_ie_test.cpp b/modules/gapi/test/infer/gapi_infer_ie_test.cpp index 738ad6d9ad..f7dc23e1e6 100644 --- a/modules/gapi/test/infer/gapi_infer_ie_test.cpp +++ b/modules/gapi/test/infer/gapi_infer_ie_test.cpp @@ -304,7 +304,7 @@ struct InferWithReshape: public ::testing::Test { InferenceEngine::CNNNetwork net; InferenceEngine::Core plugin; - InferWithReshape() { + void SetUp() { // FIXME: it must be cv::imread(findDataFile("../dnn/grace_hopper_227.png", false)); m_in_mat = cv::Mat(cv::Size(320, 240), CV_8UC3); cv::randu(m_in_mat, 0, 255); @@ -386,6 +386,7 @@ struct InferWithReshapeNV12: public InferWithReshape { cv::Mat m_in_uv; cv::Mat m_in_y; void SetUp() { + InferWithReshape::SetUp(); cv::Size sz{320, 240}; m_in_y = cv::Mat{sz, CV_8UC1}; cv::randu(m_in_y, 0, 255); From f0df78b7e743a8d735a4bc669ae64a155f08645d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 18 Nov 2022 18:09:26 +0000 Subject: [PATCH 211/313] imgcodecs: ensure parameters are key-value pairs, fix HDR encoder --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 7 +++ modules/imgcodecs/src/grfmt_hdr.cpp | 18 ++++++- modules/imgcodecs/src/grfmt_hdr.hpp | 6 --- modules/imgcodecs/src/loadsave.cpp | 48 +++++++++++++++++-- modules/imgcodecs/test/test_grfmt.cpp | 40 ++++++++++++---- modules/imgcodecs/test/test_read_write.cpp | 19 ++++++++ 6 files changed, 119 insertions(+), 19 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 49f76826b9..c4b570e68c 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -96,6 +96,7 @@ enum ImwriteFlags { IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default) IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. + IMWRITE_HDR_COMPRESSION = (5 << 4) + 0, /* 80 */ //!< specify HDR compression IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values. IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI. @@ -145,6 +146,12 @@ enum ImwritePAMFlags { IMWRITE_PAM_FORMAT_RGB_ALPHA = 5 }; +//! Imwrite HDR specific values for IMWRITE_HDR_COMPRESSION parameter key +enum ImwriteHDRCompressionFlags { + IMWRITE_HDR_COMPRESSION_NONE = 0, + IMWRITE_HDR_COMPRESSION_RLE = 1 +}; + //! @} imgcodecs_flags /** @brief Loads an image from a file. diff --git a/modules/imgcodecs/src/grfmt_hdr.cpp b/modules/imgcodecs/src/grfmt_hdr.cpp index a274b2233c..6e2f565e32 100644 --- a/modules/imgcodecs/src/grfmt_hdr.cpp +++ b/modules/imgcodecs/src/grfmt_hdr.cpp @@ -141,14 +141,28 @@ bool HdrEncoder::write( const Mat& input_img, const std::vector& params ) if(img.depth() != CV_32F) { img.convertTo(img, CV_32FC3, 1/255.0f); } - CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE); + + int compression = IMWRITE_HDR_COMPRESSION_RLE; + for (size_t i = 0; i + 1 < params.size(); i += 2) + { + switch (params[i]) + { + case IMWRITE_HDR_COMPRESSION: + compression = params[i + 1]; + break; + default: + break; + } + } + CV_Check(compression, compression == IMWRITE_HDR_COMPRESSION_NONE || compression == IMWRITE_HDR_COMPRESSION_RLE, ""); + FILE *fout = fopen(m_filename.c_str(), "wb"); if(!fout) { return false; } RGBE_WriteHeader(fout, img.cols, img.rows, NULL); - if(params.empty() || params[0] == HDR_RLE) { + if (compression == IMWRITE_HDR_COMPRESSION_RLE) { RGBE_WritePixels_RLE(fout, const_cast(img.ptr()), img.cols, img.rows); } else { RGBE_WritePixels(fout, const_cast(img.ptr()), img.cols * img.rows); diff --git a/modules/imgcodecs/src/grfmt_hdr.hpp b/modules/imgcodecs/src/grfmt_hdr.hpp index fa29fbe0d2..f0a920083f 100644 --- a/modules/imgcodecs/src/grfmt_hdr.hpp +++ b/modules/imgcodecs/src/grfmt_hdr.hpp @@ -50,12 +50,6 @@ namespace cv { -enum HdrCompression -{ - HDR_NONE = 0, - HDR_RLE = 1 -}; - // Radiance rgbe (.hdr) reader class HdrDecoder CV_FINAL : public BaseImageDecoder { diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index bd87c379ab..1bdc7029c0 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -662,7 +662,7 @@ bool imreadmulti(const String& filename, std::vector& mats, int flags) } static bool imwrite_( const String& filename, const std::vector& img_vec, - const std::vector& params, bool flipv ) + const std::vector& params_, bool flipv ) { bool isMultiImg = img_vec.size() > 1; std::vector write_vec; @@ -696,7 +696,27 @@ static bool imwrite_( const String& filename, const std::vector& img_vec, } encoder->setDestination( filename ); - CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2); +#if CV_VERSION_MAJOR < 5 && defined(HAVE_IMGCODEC_HDR) + bool fixed = false; + std::vector params_pair(2); + if (dynamic_cast(encoder.get())) + { + if (params_.size() == 1) + { + CV_LOG_WARNING(NULL, "imwrite() accepts key-value pair of parameters, but single value is passed. " + "HDR encoder behavior has been changed, please use IMWRITE_HDR_COMPRESSION key."); + params_pair[0] = IMWRITE_HDR_COMPRESSION; + params_pair[1] = params_[0]; + fixed = true; + } + } + const std::vector& params = fixed ? params_pair : params_; +#else + const std::vector& params = params_; +#endif + + CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); + CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); bool code = false; try { @@ -936,7 +956,7 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) } bool imencode( const String& ext, InputArray _image, - std::vector& buf, const std::vector& params ) + std::vector& buf, const std::vector& params_ ) { CV_TRACE_FUNCTION(); @@ -958,6 +978,28 @@ bool imencode( const String& ext, InputArray _image, image = temp; } +#if CV_VERSION_MAJOR < 5 && defined(HAVE_IMGCODEC_HDR) + bool fixed = false; + std::vector params_pair(2); + if (dynamic_cast(encoder.get())) + { + if (params_.size() == 1) + { + CV_LOG_WARNING(NULL, "imwrite() accepts key-value pair of parameters, but single value is passed. " + "HDR encoder behavior has been changed, please use IMWRITE_HDR_COMPRESSION key."); + params_pair[0] = IMWRITE_HDR_COMPRESSION; + params_pair[1] = params_[0]; + fixed = true; + } + } + const std::vector& params = fixed ? params_pair : params_; +#else + const std::vector& params = params_; +#endif + + CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); + CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); + bool code; if( encoder->setDestination(buf) ) { diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index cbf6289d23..81550bffba 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -301,21 +301,45 @@ TEST(Imgcodecs_Hdr, regression) Mat img_no_rle = imread(name_no_rle, -1); ASSERT_FALSE(img_no_rle.empty()) << "Could not open " << name_no_rle; - double min = 0.0, max = 1.0; - minMaxLoc(abs(img_rle - img_no_rle), &min, &max); - ASSERT_FALSE(max > DBL_EPSILON); + EXPECT_EQ(cvtest::norm(img_rle, img_no_rle, NORM_INF), 0.0); + string tmp_file_name = tempfile(".hdr"); - vectorparam(1); + vector param(2); + param[0] = IMWRITE_HDR_COMPRESSION; for(int i = 0; i < 2; i++) { - param[0] = i; + param[1] = i; imwrite(tmp_file_name, img_rle, param); Mat written_img = imread(tmp_file_name, -1); - ASSERT_FALSE(written_img.empty()) << "Could not open " << tmp_file_name; - minMaxLoc(abs(img_rle - written_img), &min, &max); - ASSERT_FALSE(max > DBL_EPSILON); + EXPECT_EQ(cvtest::norm(written_img, img_rle, NORM_INF), 0.0); } remove(tmp_file_name.c_str()); } + +TEST(Imgcodecs_Hdr, regression_imencode) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/"; + string name = folder + "rle.hdr"; + Mat img_ref = imread(name, -1); + ASSERT_FALSE(img_ref.empty()) << "Could not open " << name; + + vector params(2); + params[0] = IMWRITE_HDR_COMPRESSION; + { + vector buf; + params[1] = IMWRITE_HDR_COMPRESSION_NONE; + imencode(".hdr", img_ref, buf, params); + Mat img = imdecode(buf, -1); + EXPECT_EQ(cvtest::norm(img_ref, img, NORM_INF), 0.0); + } + { + vector buf; + params[1] = IMWRITE_HDR_COMPRESSION_RLE; + imencode(".hdr", img_ref, buf, params); + Mat img = imdecode(buf, -1); + EXPECT_EQ(cvtest::norm(img_ref, img, NORM_INF), 0.0); + } +} + #endif #ifdef HAVE_IMGCODEC_PXM diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 985d5c110a..30692452b2 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -288,4 +288,23 @@ TEST(Imgcodecs_Image, write_umat) EXPECT_EQ(0, remove(dst_name.c_str())); } +TEST(Imgcodecs_Params, imwrite_regression_22752) +{ + const Mat img(16, 16, CV_8UC3, cv::Scalar::all(0)); + vector params; + params.push_back(IMWRITE_JPEG_QUALITY); +// params.push_back(100)); // Forget it. + EXPECT_ANY_THROW(cv::imwrite("test.jpg", img, params)); // parameters size or missing JPEG codec +} + +TEST(Imgcodecs_Params, imencode_regression_22752) +{ + const Mat img(16, 16, CV_8UC3, cv::Scalar::all(0)); + vector params; + params.push_back(IMWRITE_JPEG_QUALITY); +// params.push_back(100)); // Forget it. + vector buf; + EXPECT_ANY_THROW(cv::imencode("test.jpg", img, buf, params)); // parameters size or missing JPEG codec +} + }} // namespace From e9d64e0a8cea0b92378bde064b6d018c5eaa8a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Ma=C5=A1ek?= Date: Tue, 15 Nov 2022 01:18:46 +0100 Subject: [PATCH 212/313] Fix #22767: Ensure that the buttons are spaced to the size of the toolbar window, which is always visible. (rebased to 3.x) --- modules/highgui/src/window_w32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 13ad679b4b..7c5668cb31 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -2253,7 +2253,7 @@ icvCreateTrackbar( const char* trackbar_name, const char* window_name, tbis.cbSize = sizeof(tbis); tbis.dwMask = TBIF_SIZE; - GetClientRect(window->hwnd, &rect); + GetClientRect(window->toolbar.toolbar, &rect); tbis.cx = (unsigned short)(rect.right - rect.left); SendMessage(window->toolbar.toolbar, TB_SETBUTTONINFO, @@ -2271,7 +2271,7 @@ icvCreateTrackbar( const char* trackbar_name, const char* window_name, trackbar->parent = window; trackbar->pos = 0; trackbar->data = 0; - trackbar->id = bcount; + trackbar->id = tbs.idCommand; trackbar->next = window->toolbar.first; trackbar->name = (char*)(trackbar + 1); memcpy( trackbar->name, trackbar_name, len + 1 ); From e5bea2bde4b8485a9051fb68aedaf08d9f7cf569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Ma=C5=A1ek?= Date: Tue, 15 Nov 2022 01:04:03 +0100 Subject: [PATCH 213/313] Fix #22766: Corrected off-by one error causing inconsistent row spacing. (rebased to 3.4) --- modules/highgui/src/window_w32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 13ad679b4b..8dbff9875c 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -2212,7 +2212,7 @@ icvCreateTrackbar( const char* trackbar_name, const char* window_name, /* Retrieve current buttons count */ bcount = (int)SendMessage(window->toolbar.toolbar, TB_BUTTONCOUNT, 0, 0); - if(bcount > 1) + if (bcount > 0) { /* If this is not the first button then we need to separate it from the previous one */ From c63a6c472ddb232acbddf6e2d2d677490b057039 Mon Sep 17 00:00:00 2001 From: Zhuo Zhang Date: Mon, 21 Nov 2022 10:44:12 +0800 Subject: [PATCH 214/313] fix typo: Gausssian to Gaussian --- .../js_imgproc/js_gradients/js_gradients.markdown | 4 ++-- .../py_imgproc/py_gradients/py_gradients.markdown | 2 +- modules/video/src/bgfg_gaussmix2.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/js_tutorials/js_imgproc/js_gradients/js_gradients.markdown b/doc/js_tutorials/js_imgproc/js_gradients/js_gradients.markdown index 21e36a0bd9..e97f93a78f 100644 --- a/doc/js_tutorials/js_imgproc/js_gradients/js_gradients.markdown +++ b/doc/js_tutorials/js_imgproc/js_gradients/js_gradients.markdown @@ -15,7 +15,7 @@ We will see each one of them. ### 1. Sobel and Scharr Derivatives -Sobel operators is a joint Gausssian smoothing plus differentiation operation, so it is more +Sobel operators is a joint Gaussian smoothing plus differentiation operation, so it is more resistant to noise. You can specify the direction of derivatives to be taken, vertical or horizontal (by the arguments, yorder and xorder respectively). You can also specify the size of kernel by the argument ksize. If ksize = -1, a 3x3 Scharr filter is used which gives better results than 3x3 Sobel @@ -97,4 +97,4 @@ Try it -\endhtmlonly \ No newline at end of file +\endhtmlonly diff --git a/doc/py_tutorials/py_imgproc/py_gradients/py_gradients.markdown b/doc/py_tutorials/py_imgproc/py_gradients/py_gradients.markdown index 9b07d14f50..0a52cd431c 100644 --- a/doc/py_tutorials/py_imgproc/py_gradients/py_gradients.markdown +++ b/doc/py_tutorials/py_imgproc/py_gradients/py_gradients.markdown @@ -17,7 +17,7 @@ We will see each one of them. ### 1. Sobel and Scharr Derivatives -Sobel operators is a joint Gausssian smoothing plus differentiation operation, so it is more +Sobel operators is a joint Gaussian smoothing plus differentiation operation, so it is more resistant to noise. You can specify the direction of derivatives to be taken, vertical or horizontal (by the arguments, yorder and xorder respectively). You can also specify the size of kernel by the argument ksize. If ksize = -1, a 3x3 Scharr filter is used which gives better results than 3x3 Sobel diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index 69e4baf657..8e12634bd9 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -47,7 +47,7 @@ //International Conference Pattern Recognition, UK, August, 2004 //http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf //The code is very fast and performs also shadow detection. -//Number of Gausssian components is adapted per pixel. +//Number of Gaussian components is adapted per pixel. // // and // @@ -97,7 +97,7 @@ namespace cv http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf Advantages: - -fast - number of Gausssian components is constantly adapted per pixel. + -fast - number of Gaussian components is constantly adapted per pixel. -performs also shadow detection (see bgfg_segm_test.cpp example) */ From 90b144cf0a4a7c9945b83d3724dc0cf84a2e5328 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 1 Nov 2022 18:24:10 +0100 Subject: [PATCH 215/313] Cocoa/highgui: Set activateIgnoringOtherApps --- modules/highgui/src/window_cocoa.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index 36db7e836b..1cf55f9397 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -579,6 +579,8 @@ CV_IMPL int cvNamedWindow( const char* name, int flags ) [window setContentView:[[CVView alloc] init]]; + [NSApp activateIgnoringOtherApps:YES]; + [window setHasShadow:YES]; [window setAcceptsMouseMovedEvents:YES]; [window useOptimizedDrawing:YES]; From aba2167d9cc7dd7ff21cbca48a7713e55500da1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Ma=C5=A1ek?= Date: Tue, 22 Nov 2022 11:47:27 +0100 Subject: [PATCH 216/313] Merge pull request #22838 from dan-masek:fix_issue_22837 Fix issue 22837: No more blank buttons on toolbar after resizing the window --- modules/highgui/src/window_w32.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 7764a9913c..eb17d5fc01 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -1939,26 +1939,19 @@ static LRESULT CALLBACK HGToolbarProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPAR case WM_NCCALCSIZE: { LRESULT ret = CallWindowProc(window->toolbar.toolBarProc, hwnd, uMsg, wParam, lParam); - int rows = (int)SendMessage(hwnd, TB_GETROWS, 0, 0); + CvTrackbar* trackbar = window->toolbar.first; - if(window->toolbar.rows != rows) - { - SendMessage(window->toolbar.toolbar, TB_BUTTONCOUNT, 0, 0); - CvTrackbar* trackbar = window->toolbar.first; - - for( ; trackbar != 0; trackbar = trackbar->next ) - { - RECT rect = { 0 }; - SendMessage(window->toolbar.toolbar, TB_GETITEMRECT, - (WPARAM)trackbar->id, (LPARAM)&rect); - MoveWindow(trackbar->hwnd, rect.left + HG_BUDDY_WIDTH, rect.top, - rect.right - rect.left - HG_BUDDY_WIDTH, - rect.bottom - rect.top, FALSE); - MoveWindow(trackbar->buddy, rect.left, rect.top, - HG_BUDDY_WIDTH, rect.bottom - rect.top, FALSE); - } - window->toolbar.rows = rows; + for (; trackbar != 0; trackbar = trackbar->next) { + RECT rect = { 0 }; + SendMessage(window->toolbar.toolbar, TB_GETITEMRECT, + (WPARAM)trackbar->id, (LPARAM)&rect); + MoveWindow(trackbar->hwnd, rect.left + HG_BUDDY_WIDTH, rect.top, + rect.right - rect.left - HG_BUDDY_WIDTH, + rect.bottom - rect.top, FALSE); + MoveWindow(trackbar->buddy, rect.left, rect.top, + HG_BUDDY_WIDTH, rect.bottom - rect.top, FALSE); } + window->toolbar.rows = static_cast(SendMessage(hwnd, TB_GETROWS, 0, 0)); return ret; } } From 3f371fe2dd909b29fa6cc01b35649e3b58b265f2 Mon Sep 17 00:00:00 2001 From: Amir Hassan Date: Fri, 25 Nov 2022 07:13:57 +0100 Subject: [PATCH 217/313] Merge pull request #22855 from kallaballa:print_cl_status_on_fail Print CL status code on error in opengl interop functions --- modules/core/src/opengl.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index ab39b1b8ac..592fdc52ca 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -1638,14 +1638,14 @@ Context& initializeContextFromGL() cl_uint numPlatforms; cl_int status = clGetPlatformIDs(0, NULL, &numPlatforms); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms"); + CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms: %d ", status); if (numPlatforms == 0) CV_Error(cv::Error::OpenCLInitError, "OpenCL: No available platforms"); std::vector platforms(numPlatforms); status = clGetPlatformIDs(numPlatforms, &platforms[0], NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms"); + CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms: %d ", status); // TODO Filter platforms by name from OPENCV_OPENCL_DEVICE @@ -1667,7 +1667,7 @@ Context& initializeContextFromGL() status = clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, extensionSize, (char*)extensionStr.data(), NULL); } if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get platform extension string"); + CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get platform extension string: %d ", status); if (!strstr((const char*)extensionStr.data(), "cl_khr_gl_sharing")) continue; @@ -1759,31 +1759,31 @@ void convertToGLTexture2D(InputArray src, Texture2D& texture) cl_int status = 0; cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed: %d ", status); cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); size_t offset = 0; // TODO size_t dst_origin[3] = {0, 0, 0}; size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1}; status = clEnqueueCopyBufferToImage(q, clBuffer, clImage, offset, dst_origin, region, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyBufferToImage failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyBufferToImage failed: %d ", status); status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status); status = clFinish(q); // TODO Use events if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: %d ", status); status = clReleaseMemObject(clImage); // TODO RAII if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: %d ", status); #endif } @@ -1821,31 +1821,31 @@ void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst) cl_int status = 0; cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_READ_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed: %d ", status); cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); size_t offset = 0; // TODO size_t src_origin[3] = {0, 0, 0}; size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1}; status = clEnqueueCopyImageToBuffer(q, clImage, clBuffer, src_origin, region, offset, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyImageToBuffer failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyImageToBuffer failed: %d ", status); status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status); status = clFinish(q); // TODO Use events if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: : %d ", status); status = clReleaseMemObject(clImage); // TODO RAII if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: : %d ", status); #endif } @@ -1883,13 +1883,13 @@ UMat mapGLBuffer(const Buffer& buffer, AccessFlag accessFlags) cl_int status = 0; cl_mem clBuffer = clCreateFromGLBuffer(context, clAccessFlags, buffer.bufId(), &status); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLBuffer failed"); + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLBuffer failed: %d ", status); gl::Finish(); status = clEnqueueAcquireGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); size_t step = buffer.cols() * buffer.elemSize(); int rows = buffer.rows(); @@ -1921,15 +1921,15 @@ void unmapGLBuffer(UMat& u) cl_int status = clEnqueueReleaseGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status);; status = clFinish(clQueue); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: %d ", status);; status = clReleaseMemObject(clBuffer); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); + CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: %d ", status);; #endif } From 7622fbf8952cf00e525057613f75030f975e4f82 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 25 Nov 2022 16:46:12 +0300 Subject: [PATCH 218/313] Fixed OpenGL errors formatting. --- modules/core/src/opengl.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index 592fdc52ca..6bc11f9a2f 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -1638,14 +1638,14 @@ Context& initializeContextFromGL() cl_uint numPlatforms; cl_int status = clGetPlatformIDs(0, NULL, &numPlatforms); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms: %d ", status); + CV_Error_(cv::Error::OpenCLInitError, ("OpenCL: Can't get number of platforms: %d", status)); if (numPlatforms == 0) CV_Error(cv::Error::OpenCLInitError, "OpenCL: No available platforms"); std::vector platforms(numPlatforms); status = clGetPlatformIDs(numPlatforms, &platforms[0], NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms: %d ", status); + CV_Error_(cv::Error::OpenCLInitError, ("OpenCL: Can't get number of platforms: %d", status)); // TODO Filter platforms by name from OPENCV_OPENCL_DEVICE @@ -1667,7 +1667,7 @@ Context& initializeContextFromGL() status = clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, extensionSize, (char*)extensionStr.data(), NULL); } if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLInitError, "OpenCL: Can't get platform extension string: %d ", status); + CV_Error_(cv::Error::OpenCLInitError, ("OpenCL: Can't get platform extension string: %d", status)); if (!strstr((const char*)extensionStr.data(), "cl_khr_gl_sharing")) continue; @@ -1759,31 +1759,31 @@ void convertToGLTexture2D(InputArray src, Texture2D& texture) cl_int status = 0; cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clCreateFromGLTexture failed: %d", status)); cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueAcquireGLObjects failed: %d", status)); size_t offset = 0; // TODO size_t dst_origin[3] = {0, 0, 0}; size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1}; status = clEnqueueCopyBufferToImage(q, clBuffer, clImage, offset, dst_origin, region, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyBufferToImage failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueCopyBufferToImage failed: %d", status)); status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueReleaseGLObjects failed: %d", status)); status = clFinish(q); // TODO Use events if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clFinish failed: %d", status)); status = clReleaseMemObject(clImage); // TODO RAII if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clReleaseMemObject failed: %d", status)); #endif } @@ -1821,31 +1821,31 @@ void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst) cl_int status = 0; cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_READ_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clCreateFromGLTexture failed: %d", status)); cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueAcquireGLObjects failed: %d", status)); size_t offset = 0; // TODO size_t src_origin[3] = {0, 0, 0}; size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1}; status = clEnqueueCopyImageToBuffer(q, clImage, clBuffer, src_origin, region, offset, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyImageToBuffer failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueCopyImageToBuffer failed: %d", status)); status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueReleaseGLObjects failed: %d", status)); status = clFinish(q); // TODO Use events if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: : %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clFinish failed: %d", status)); status = clReleaseMemObject(clImage); // TODO RAII if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: : %d ", status); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clReleaseMemObject failed: %d", status)); #endif } @@ -1883,13 +1883,13 @@ UMat mapGLBuffer(const Buffer& buffer, AccessFlag accessFlags) cl_int status = 0; cl_mem clBuffer = clCreateFromGLBuffer(context, clAccessFlags, buffer.bufId(), &status); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLBuffer failed: %d ", status); + CV_Error(cv::Error::OpenCLApiCallError, ("OpenCL: clCreateFromGLBuffer failed: %d", status)); gl::Finish(); status = clEnqueueAcquireGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed: %d ", status); + CV_Error(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueAcquireGLObjects failed: %d", status)); size_t step = buffer.cols() * buffer.elemSize(); int rows = buffer.rows(); @@ -1921,15 +1921,15 @@ void unmapGLBuffer(UMat& u) cl_int status = clEnqueueReleaseGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed: %d ", status);; + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueReleaseGLObjects failed: %d", status)); status = clFinish(clQueue); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed: %d ", status);; + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clFinish failed: %d", status)); status = clReleaseMemObject(clBuffer); if (status != CL_SUCCESS) - CV_Error_(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed: %d ", status);; + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clReleaseMemObject failed: %d", status)); #endif } From a462f49b993aeeaa73b3d1176b3c122901e702ad Mon Sep 17 00:00:00 2001 From: Stefan Dragnev Date: Thu, 10 Nov 2022 18:08:59 +0100 Subject: [PATCH 219/313] add support for CAP_PROP_ORIENTATION_AUTO to AVFoundation backend * extract rotateFrame as free function, rename to applyMetadataRotation * LegacyCapture::get() always return 0, if cap is null --- modules/videoio/include/opencv2/videoio.hpp | 4 +- modules/videoio/src/cap.cpp | 25 +++++++ modules/videoio/src/cap_avfoundation.mm | 10 +++ modules/videoio/src/cap_avfoundation_mac.mm | 10 +++ modules/videoio/src/cap_ffmpeg.cpp | 26 +------ modules/videoio/src/cap_interface.hpp | 46 ++++++++++++- modules/videoio/test/test_ffmpeg.cpp | 62 ----------------- modules/videoio/test/test_orientation.cpp | 76 +++++++++++++++++++++ 8 files changed, 167 insertions(+), 92 deletions(-) create mode 100644 modules/videoio/test/test_orientation.cpp diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 91aa083ef0..59f1fcb9b0 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -182,8 +182,8 @@ enum VideoCaptureProperties { CAP_PROP_WB_TEMPERATURE=45, //!< white-balance color temperature CAP_PROP_CODEC_PIXEL_FORMAT =46, //!< (read-only) codec's pixel format. 4-character code - see VideoWriter::fourcc . Subset of [AV_PIX_FMT_*](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/raw.c) or -1 if unknown CAP_PROP_BITRATE =47, //!< (read-only) Video bitrate in kbits/s - CAP_PROP_ORIENTATION_META=48, //!< (read-only) Frame rotation defined by stream meta (applicable for FFmpeg back-end only) - CAP_PROP_ORIENTATION_AUTO=49, //!< if true - rotates output frames of CvCapture considering video file's metadata (applicable for FFmpeg back-end only) (https://github.com/opencv/opencv/issues/15499) + CAP_PROP_ORIENTATION_META=48, //!< (read-only) Frame rotation defined by stream meta (applicable for FFmpeg and AVFoundation back-ends only) + CAP_PROP_ORIENTATION_AUTO=49, //!< if true - rotates output frames of CvCapture considering video file's metadata (applicable for FFmpeg and AVFoundation back-ends only) (https://github.com/opencv/opencv/issues/15499) CAP_PROP_HW_ACCELERATION=50, //!< (**open-only**) Hardware acceleration type (see #VideoAccelerationType). Setting supported only via `params` parameter in cv::VideoCapture constructor / .open() method. Default value is backend-specific. CAP_PROP_HW_DEVICE =51, //!< (**open-only**) Hardware device index (select GPU if multiple available). Device enumeration is acceleration type specific. CAP_PROP_HW_ACCELERATION_USE_OPENCL=52, //!< (**open-only**) If non-zero, create new OpenCL context and bind it to current thread. The OpenCL context created with Video Acceleration context attached it (if not attached yet) for optimized GPU data copy between HW accelerated decoder and cv::UMat. diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index fa958e2c8f..691fb9ab38 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -698,4 +698,29 @@ int VideoWriter::fourcc(char c1, char c2, char c3, char c4) return (c1 & 255) + ((c2 & 255) << 8) + ((c3 & 255) << 16) + ((c4 & 255) << 24); } + +void applyMetadataRotation(const IVideoCapture& cap, OutputArray mat) +{ + bool rotation_auto = 0 != cap.getProperty(CAP_PROP_ORIENTATION_AUTO); + int rotation_angle = static_cast(cap.getProperty(CAP_PROP_ORIENTATION_META)); + + if(!rotation_auto || rotation_angle%360 == 0) + { + return; + } + + cv::RotateFlags flag; + if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees + flag = cv::ROTATE_90_CLOCKWISE; + } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees + flag = cv::ROTATE_90_COUNTERCLOCKWISE; + } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees + flag = cv::ROTATE_180; + } else { // Unsupported rotation + return; + } + + cv::rotate(mat, mat, flag); +} + } // namespace cv diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 17c5879014..ef13dd6d14 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -162,6 +162,7 @@ private: bool setupReadingAt(CMTime position); IplImage* retrieveFramePixelBuffer(); + int getPreferredOrientationDegrees() const; CMTime mFrameTimestamp; size_t mFrameNum; @@ -1098,6 +1099,13 @@ IplImage* CvCaptureFile::retrieveFramePixelBuffer() { return mOutImage; } +int CvCaptureFile::getPreferredOrientationDegrees() const { + if (mAssetTrack == nil) return 0; + + CGAffineTransform transform = mAssetTrack.preferredTransform; + double radians = atan2(transform.b, transform.a); + return static_cast(round(radians * 180 / M_PI)); +} IplImage* CvCaptureFile::retrieveFrame(int) { return retrieveFramePixelBuffer(); @@ -1129,6 +1137,8 @@ double CvCaptureFile::getProperty(int property_id) const{ return mFormat; case CV_CAP_PROP_FOURCC: return mMode; + case cv::CAP_PROP_ORIENTATION_META: + return getPreferredOrientationDegrees(); default: break; } diff --git a/modules/videoio/src/cap_avfoundation_mac.mm b/modules/videoio/src/cap_avfoundation_mac.mm index f33574104f..bdd4a934f8 100644 --- a/modules/videoio/src/cap_avfoundation_mac.mm +++ b/modules/videoio/src/cap_avfoundation_mac.mm @@ -169,6 +169,7 @@ private: bool setupReadingAt(CMTime position); IplImage* retrieveFramePixelBuffer(); + int getPreferredOrientationDegrees() const; CMTime mFrameTimestamp; size_t mFrameNum; @@ -1064,6 +1065,13 @@ IplImage* CvCaptureFile::retrieveFramePixelBuffer() { return mOutImage; } +int CvCaptureFile::getPreferredOrientationDegrees() const { + if (mAssetTrack == nil) return 0; + + CGAffineTransform transform = mAssetTrack.preferredTransform; + double radians = atan2(transform.b, transform.a); + return static_cast(round(radians * 180 / M_PI)); +} IplImage* CvCaptureFile::retrieveFrame(int) { return retrieveFramePixelBuffer(); @@ -1095,6 +1103,8 @@ double CvCaptureFile::getProperty(int property_id) const{ return mFormat; case CV_CAP_PROP_FOURCC: return mMode; + case cv::CAP_PROP_ORIENTATION_META: + return getPreferredOrientationDegrees(); default: break; } diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 7030d0e653..ed2e4336db 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -112,7 +112,7 @@ public: } cv::Mat tmp(height, width, CV_MAKETYPE(CV_8U, cn), data, step); - this->rotateFrame(tmp); + applyMetadataRotation(*this, tmp); tmp.copyTo(frame); return true; @@ -137,30 +137,6 @@ public: protected: CvCapture_FFMPEG* ffmpegCapture; - - void rotateFrame(cv::Mat &mat) const - { - bool rotation_auto = 0 != getProperty(CAP_PROP_ORIENTATION_AUTO); - int rotation_angle = static_cast(getProperty(CAP_PROP_ORIENTATION_META)); - - if(!rotation_auto || rotation_angle%360 == 0) - { - return; - } - - cv::RotateFlags flag; - if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees - flag = cv::ROTATE_90_CLOCKWISE; - } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees - flag = cv::ROTATE_90_COUNTERCLOCKWISE; - } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees - flag = cv::ROTATE_180; - } else { // Unsupported rotation - return; - } - - cv::rotate(mat, mat, flag); - } }; } // namespace diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index 3b3d5398fc..52639f3605 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -221,6 +221,8 @@ public: virtual int getCaptureDomain() { return CAP_ANY; } // Return the type of the capture object: CAP_DSHOW, etc... }; +void applyMetadataRotation(const IVideoCapture& cap, OutputArray mat); + class IVideoWriter { public: @@ -249,21 +251,58 @@ class LegacyCapture : public IVideoCapture { private: CvCapture * cap; + bool autorotate; LegacyCapture(const LegacyCapture &); LegacyCapture& operator=(const LegacyCapture &); + + bool shouldSwapWidthHeight() const + { + if (!autorotate) + return false; + int rotation = static_cast(cap->getProperty(cv::CAP_PROP_ORIENTATION_META)); + return std::abs(rotation % 180) == 90; + } + public: - LegacyCapture(CvCapture * cap_) : cap(cap_) {} + LegacyCapture(CvCapture * cap_) : cap(cap_), autorotate(true) {} ~LegacyCapture() { cvReleaseCapture(&cap); } double getProperty(int propId) const CV_OVERRIDE { - return cap ? cap->getProperty(propId) : 0; + if (!cap) + return 0; + + switch(propId) + { + case cv::CAP_PROP_ORIENTATION_AUTO: + return static_cast(autorotate); + + case cv::CAP_PROP_FRAME_WIDTH: + return shouldSwapWidthHeight() ? cap->getProperty(cv::CAP_PROP_FRAME_HEIGHT) : cap->getProperty(cv::CAP_PROP_FRAME_WIDTH); + + case cv::CAP_PROP_FRAME_HEIGHT: + return shouldSwapWidthHeight() ? cap->getProperty(cv::CAP_PROP_FRAME_WIDTH) : cap->getProperty(cv::CAP_PROP_FRAME_HEIGHT); + + default: + return cap->getProperty(propId); + } } bool setProperty(int propId, double value) CV_OVERRIDE { - return cvSetCaptureProperty(cap, propId, value) != 0; + if (!cap) + return false; + + switch(propId) + { + case cv::CAP_PROP_ORIENTATION_AUTO: + autorotate = (value != 0); + return true; + + default: + return cvSetCaptureProperty(cap, propId, value) != 0; + } } bool grabFrame() CV_OVERRIDE { @@ -286,6 +325,7 @@ public: Mat temp = cv::cvarrToMat(_img); flip(temp, image, 0); } + applyMetadataRotation(*this, image); return true; } bool isOpened() const CV_OVERRIDE diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 3ae27de1b4..cab49dcabb 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -475,68 +475,6 @@ const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] = INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_get_fourcc, testing::ValuesIn(ffmpeg_get_fourcc_param)); -// related issue: https://github.com/opencv/opencv/issues/15499 -TEST(videoio, mp4_orientation_meta_auto) -{ - if (!videoio_registry::hasBackend(CAP_FFMPEG)) - throw SkipTestException("FFmpeg backend was not found"); - - string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4"; - - VideoCapture cap; - EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); - ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl; - - // related issue: https://github.com/opencv/opencv/issues/22088 - EXPECT_EQ(90, cap.get(CAP_PROP_ORIENTATION_META)); - - cap.set(CAP_PROP_ORIENTATION_AUTO, true); - if (cap.get(CAP_PROP_ORIENTATION_AUTO) == 0) - throw SkipTestException("FFmpeg frame rotation metadata is not supported"); - - Size actual; - EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), - (int)cap.get(CAP_PROP_FRAME_HEIGHT))); - EXPECT_EQ(384, actual.width); - EXPECT_EQ(672, actual.height); - - Mat frame; - - cap >> frame; - - ASSERT_EQ(384, frame.cols); - ASSERT_EQ(672, frame.rows); -} - -// related issue: https://github.com/opencv/opencv/issues/15499 -TEST(videoio, mp4_orientation_no_rotation) -{ - if (!videoio_registry::hasBackend(CAP_FFMPEG)) - throw SkipTestException("FFmpeg backend was not found"); - - string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4"; - - VideoCapture cap; - EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); - cap.set(CAP_PROP_ORIENTATION_AUTO, 0); - ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl; - ASSERT_FALSE(cap.get(CAP_PROP_ORIENTATION_AUTO)); - - Size actual; - EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), - (int)cap.get(CAP_PROP_FRAME_HEIGHT))); - EXPECT_EQ(672, actual.width); - EXPECT_EQ(384, actual.height); - - Mat frame; - - cap >> frame; - - ASSERT_EQ(672, frame.cols); - ASSERT_EQ(384, frame.rows); -} - - static void ffmpeg_check_read_raw(VideoCapture& cap) { ASSERT_TRUE(cap.isOpened()) << "Can't open the video"; diff --git a/modules/videoio/test/test_orientation.cpp b/modules/videoio/test/test_orientation.cpp new file mode 100644 index 0000000000..96530e2a03 --- /dev/null +++ b/modules/videoio/test/test_orientation.cpp @@ -0,0 +1,76 @@ +// 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 "test_precomp.hpp" + +using namespace std; + +namespace opencv_test { namespace { + +typedef TestWithParam VideoCaptureAPITests; + +// related issue: https://github.com/opencv/opencv/issues/15499 +TEST_P(VideoCaptureAPITests, mp4_orientation_meta_auto) +{ + cv::VideoCaptureAPIs api = GetParam(); + if (!videoio_registry::hasBackend(api)) + throw SkipTestException("backend " + std::to_string(int(api)) + " was not found"); + + string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/rotated_metadata.mp4"; + + VideoCapture cap; + EXPECT_NO_THROW(cap.open(video_file, api)); + ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << api << std::endl; + + // related issue: https://github.com/opencv/opencv/issues/22088 + EXPECT_EQ(90, cap.get(CAP_PROP_ORIENTATION_META)); + + EXPECT_TRUE(cap.set(CAP_PROP_ORIENTATION_AUTO, true)); + + Size actual; + EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), + (int)cap.get(CAP_PROP_FRAME_HEIGHT))); + EXPECT_EQ(270, actual.width); + EXPECT_EQ(480, actual.height); + + Mat frame; + + cap >> frame; + + ASSERT_EQ(270, frame.cols); + ASSERT_EQ(480, frame.rows); +} + +// related issue: https://github.com/opencv/opencv/issues/15499 +TEST_P(VideoCaptureAPITests, mp4_orientation_no_rotation) +{ + cv::VideoCaptureAPIs api = GetParam(); + if (!videoio_registry::hasBackend(api)) + throw SkipTestException("backend " + std::to_string(int(api)) + " was not found"); + + string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/rotated_metadata.mp4"; + + VideoCapture cap; + EXPECT_NO_THROW(cap.open(video_file, api)); + cap.set(CAP_PROP_ORIENTATION_AUTO, 0); + ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << api << std::endl; + ASSERT_FALSE(cap.get(CAP_PROP_ORIENTATION_AUTO)); + + Size actual; + EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), + (int)cap.get(CAP_PROP_FRAME_HEIGHT))); + EXPECT_EQ(480, actual.width); + EXPECT_EQ(270, actual.height); + + Mat frame; + + cap >> frame; + + ASSERT_EQ(480, frame.cols); + ASSERT_EQ(270, frame.rows); +} + +INSTANTIATE_TEST_CASE_P(videoio, VideoCaptureAPITests, testing::Values(CAP_FFMPEG, CAP_AVFOUNDATION)); + +}} // namespace From 5044af69d18ce40e4eff0234272090ca2df84f5a Mon Sep 17 00:00:00 2001 From: zoom Date: Sun, 27 Nov 2022 17:32:41 +0800 Subject: [PATCH 220/313] let MatMul can work when both two inputs are const --- modules/dnn/src/onnx/onnx_importer.cpp | 29 ++++++++++++++++++++----- modules/dnn/test/test_onnx_importer.cpp | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index a1a99f6d26..6ef8894063 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2037,9 +2037,25 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node CV_Assert(node_proto.input_size() == 2); layerParams.type = "InnerProduct"; layerParams.set("bias_term", false); - CV_Assert(constBlobs.find(node_proto.input(0)) == constBlobs.end()); - int firstInpDims = outShapes[node_proto.input(0)].size(); - int secondInpDims; + int firstInpDims, secondInpDims; + + if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) + { + Mat blob = getBlob(node_proto, 0); + firstInpDims = blob.dims; + LayerParams constParams; + constParams.name = layerParams.name + "/const_0"; + constParams.type = "Const"; + constParams.blobs.push_back(blob); + + opencv_onnx::NodeProto tmpProto; + tmpProto.add_output(constParams.name); + addLayer(constParams, tmpProto); + + node_proto.set_input(0, constParams.name); + } + else + firstInpDims = outShapes[node_proto.input(0)].size(); if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) { @@ -2053,7 +2069,7 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node else { LayerParams constParams; - constParams.name = layerParams.name + "/const"; + constParams.name = layerParams.name + "/const_1"; constParams.type = "Const"; constParams.blobs.push_back(blob); @@ -2063,9 +2079,10 @@ void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::Node node_proto.set_input(1, constParams.name); } - } else { - secondInpDims = outShapes[node_proto.input(1)].size(); } + else + secondInpDims = outShapes[node_proto.input(1)].size(); + layerParams.set("axis", firstInpDims - secondInpDims + 1); addLayer(layerParams, node_proto); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index be14041bf0..43dc817733 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -961,6 +961,8 @@ TEST_P(Test_ONNX_layers, MatMul_init) testONNXModels("matmul_2d_init"); testONNXModels("matmul_3d_init"); testONNXModels("matmul_4d_init"); + + testONNXModels("matmul_init_2"); } TEST_P(Test_ONNX_layers, MatMulAdd) From 1c3e287d32749ae4d536694d0f4c6a4dcf0af098 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 28 Nov 2022 09:47:51 +0300 Subject: [PATCH 221/313] More fixes for OpenCL error reporting. --- modules/core/src/opengl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index 6bc11f9a2f..45aa121a4a 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -1883,13 +1883,13 @@ UMat mapGLBuffer(const Buffer& buffer, AccessFlag accessFlags) cl_int status = 0; cl_mem clBuffer = clCreateFromGLBuffer(context, clAccessFlags, buffer.bufId(), &status); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, ("OpenCL: clCreateFromGLBuffer failed: %d", status)); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clCreateFromGLBuffer failed: %d", status)); gl::Finish(); status = clEnqueueAcquireGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); if (status != CL_SUCCESS) - CV_Error(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueAcquireGLObjects failed: %d", status)); + CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueAcquireGLObjects failed: %d", status)); size_t step = buffer.cols() * buffer.elemSize(); int rows = buffer.rows(); From ed3810f7a5935115fc1e526cdc5b02dba53707b2 Mon Sep 17 00:00:00 2001 From: AleksandrPanov Date: Mon, 28 Nov 2022 17:45:09 +0300 Subject: [PATCH 222/313] add getNumModules(), add decode version --- modules/objdetect/src/qrcode.cpp | 211 ++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 2bbb38fc6c..bb913fdbfe 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -990,6 +989,7 @@ public: bool straightDecodingProcess(); bool curvedDecodingProcess(); protected: + double getNumModules(); bool updatePerspective(); bool versionDefinition(); bool samplingForVersion(); @@ -2251,6 +2251,91 @@ bool QRDecode::preparingCurvedQRCodes() return true; } +/** + * @param finderPattern 4 points of finder pattern markers, calculated by findPatternsVerticesPoints() + * @return true if the pattern has the correct side lengths + */ +static inline bool checkFinderPatternByAspect(const vector &finderPattern) { + if (finderPattern.size() != 4ull) + return false; + float sidesLen[4]; + for (size_t i = 0; i < finderPattern.size(); i++) { + sidesLen[i] = (sqrt(normL2Sqr(Point2f(finderPattern[i] - finderPattern[(i+1ull)%finderPattern.size()])))); + } + const float maxSide = max(max(sidesLen[0], sidesLen[1]), max(sidesLen[2], sidesLen[3])); + const float minSide = min(min(sidesLen[0], sidesLen[1]), min(sidesLen[2], sidesLen[3])); + + const float patternMaxRelativeLen = .3f; + if (1.f - minSide / maxSide > patternMaxRelativeLen) + return false; + return true; +} + +/** + * @param finderPattern - 4 points of finder pattern markers, calculated by findPatternsVerticesPoints() + * @param cornerPointsQR - 4 corner points of QR code + * @return pair first - the index in points of finderPattern closest to the corner of the QR code, + * second - the index in points of cornerPointsQR closest to the corner of finderPattern + * + * This function matches finder patterns to the corners of the QR code. Points of finder pattern calculated by + * findPatternsVerticesPoints() may be erroneous, so they are checked. + */ +static inline std::pair matchPatternPoints(const vector &finderPattern, + const vector cornerPointsQR) { + if (!checkFinderPatternByAspect(finderPattern)) + return std::make_pair(-1, -1); + + float distanceToOrig = normL2Sqr(Point2f(finderPattern[0]) - cornerPointsQR[0]); + int closestFinderPatternV = 0; + int closetOriginalV = 0; + + for (size_t i = 0ull; i < finderPattern.size(); i++) { + for (size_t j = 0ull; j < cornerPointsQR.size(); j++) { + const float tmp = normL2Sqr(Point2f(finderPattern[i]) - cornerPointsQR[j]); + if (tmp < distanceToOrig) { + distanceToOrig = tmp; + closestFinderPatternV = i; + closetOriginalV = j; + } + } + } + + // check that the distance from the QR pattern to the corners of the QR code is small + const float originalQrSide = sqrt(normL2Sqr(cornerPointsQR[0] - cornerPointsQR[1]))*0.5f + + sqrt(normL2Sqr(cornerPointsQR[0] - cornerPointsQR[3]))*0.5f; + const float maxRelativeDistance = .1f; + + if (distanceToOrig/originalQrSide > maxRelativeDistance) + return std::make_pair(-1, -1); + return std::make_pair(closestFinderPatternV, closetOriginalV); +} + +double QRDecode::getNumModules() { + vector> finderPatterns; + double numModulesX = 0., numModulesY = 0.; + bool flag = findPatternsVerticesPoints(finderPatterns); + if (flag) { + vector pattern_distance(4); + for (auto& pattern : finderPatterns) { + auto indexes = matchPatternPoints(pattern, original_points); + if (indexes == std::make_pair(-1, -1)) + return 0.; + Point2f vf[4] = {pattern[indexes.first % 4], pattern[(1+indexes.first) % 4], + pattern[(2+indexes.first) % 4], pattern[(3+indexes.first) % 4]}; + for (int i = 1; i < 4; i++) { + pattern_distance[indexes.second] += (norm(vf[i] - vf[i-1])); + } + pattern_distance[indexes.second] += norm(vf[3] - vf[0]); + pattern_distance[indexes.second] /= 4.; + } + const double moduleSizeX = (pattern_distance[0] + pattern_distance[1])/(2.*7.); + const double moduleSizeY = (pattern_distance[0] + pattern_distance[3])/(2.*7.); + numModulesX = norm(original_points[1] - original_points[0])/moduleSizeX; + numModulesY = norm(original_points[3] - original_points[0])/moduleSizeY; + } + return (numModulesX + numModulesY)/2.; +} + bool QRDecode::updatePerspective() { CV_TRACE_FUNCTION(); @@ -2284,7 +2369,7 @@ bool QRDecode::updatePerspective() return true; } -inline Point computeOffset(const vector& v) +static inline Point computeOffset(const vector& v) { // compute the width/height of convex hull Rect areaBox = boundingRect(v); @@ -2298,9 +2383,80 @@ inline Point computeOffset(const vector& v) return offset; } +// QR code with version 7 or higher has a special 18 bit version number code. +// @return std::pair first - distance to estimatedVersion, second - version +/** + * @param numModules - estimated numModules + * @param estimatedVersion + * @return pair, first - Hamming distance to 18 bit code, second - closest version + * + * QR code with version 7 or higher has a special 18 bit version number code: + * https://www.thonky.com/qr-code-tutorial/format-version-information + */ +static inline std::pair getVersionByCode(double numModules, Mat qr, int estimatedVersion) { + const double moduleSize = qr.rows / numModules; + Point2d startVersionInfo1 = Point2d((numModules-8.-3.)*moduleSize, 0.); + Point2d endVersionInfo1 = Point2d((numModules-8.)*moduleSize, moduleSize*6.); + Point2d startVersionInfo2 = Point2d(0., (numModules-8.-3.)*moduleSize); + Point2d endVersionInfo2 = Point2d(moduleSize*6., (numModules-8.)*moduleSize); + Mat v1(qr, Rect2d(startVersionInfo1, endVersionInfo1)); + Mat v2(qr, Rect2d(startVersionInfo2, endVersionInfo2)); + const double thresh = 127.; + resize(v1, v1, Size(3, 6), 0., 0., INTER_AREA); + threshold(v1, v1, thresh, 255, THRESH_BINARY); + resize(v2, v2, Size(6, 3), 0., 0., INTER_AREA); + threshold(v2, v2, thresh, 255, THRESH_BINARY); + + Mat version1, version2; + // convert version1 (top right version information block) and + // version2 (bottom left version information block) to version table format + // https://www.thonky.com/qr-code-tutorial/format-version-tables + rotate((255-v1)/255, version1, ROTATE_180), rotate(((255-v2)/255).t(), version2, ROTATE_180); + + static uint8_t versionCodes[][18] = {{0,0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0},{0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0}, + {0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1},{0,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1}, + {0,0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0},{0,0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0}, + {0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1},{0,0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1}, + {0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0},{0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0}, + {0,1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1},{0,1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1}, + {0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0},{0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1,0}, + {0,1,0,1,0,1,0,1,1,0,1,0,0,0,0,0,1,1},{0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0,0,1}, + {0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,0},{0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0}, + {0,1,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,1},{0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1}, + {0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0},{0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0}, + {0,1,1,1,0,1,0,0,1,1,0,0,1,1,1,1,1,1},{0,1,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1}, + {0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0,0},{1,0,0,0,0,0,1,0,0,1,1,1,0,1,0,1,0,1}, + {1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0,0},{1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1,0}, + {1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1},{1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,1,1}, + {1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0,0}, + {1,0,0,1,1,1,0,1,0,1,0,1,0,0,0,0,0,1},{1,0,1,0,0,0,1,1,0,0,0,1,1,0,1,0,0,1} + }; + double minDist = 19.; + int bestVersion = -1; + const double penaltyFactor = 0.8; + + for (int i = 0; i < (int)(sizeof(versionCodes)/sizeof(versionCodes[0])); i++) { + Mat currVers(Size(3, 6), CV_8UC1, versionCodes[i]); + // minimum hamming distance between version = 8 + double tmp = norm(currVers, version1, NORM_HAMMING) + penaltyFactor*abs(estimatedVersion-i-7); + if (tmp < minDist) { + bestVersion = i+7; + minDist = tmp; + } + tmp = norm(currVers, version2, NORM_HAMMING) + penaltyFactor*abs(estimatedVersion-i-7); + if (tmp < minDist) { + bestVersion = i+7; + minDist = tmp; + } + } + return std::make_pair(minDist, bestVersion); +} + bool QRDecode::versionDefinition() { CV_TRACE_FUNCTION(); + CV_LOG_INFO(NULL, "QR corners: " << original_points[0] << " " << original_points[1] << " " << original_points[2] << + " " << original_points[3]); LineIterator line_iter(intermediate, Point2f(0, 0), Point2f(test_perspective_size, test_perspective_size)); Point black_point = Point(0, 0); for(int j = 0; j < line_iter.count; j++, ++line_iter) @@ -2358,11 +2514,54 @@ bool QRDecode::versionDefinition() transition_y++; } } - version = saturate_cast((std::min(transition_x, transition_y) - 1) * 0.25 - 1); - if ( !( 0 < version && version <= 40 ) ) { return false; } + + const int versionByTransition = saturate_cast((std::min(transition_x, transition_y) - 1) * 0.25 - 1); + const int numModulesByTransition = 21 + (versionByTransition - 1) * 4; + + const double numModulesByFinderPattern = getNumModules(); + const double versionByFinderPattern = (numModulesByFinderPattern - 21.) * .25 + 1.; + bool useFinderPattern = false; + const double thresholdFinderPattern = 0.2; + const double roundingError = abs(numModulesByFinderPattern - cvRound(numModulesByFinderPattern)); + if (cvRound(versionByFinderPattern) >= 1 && versionByFinderPattern <= 6 && transition_x != transition_y) { + if (roundingError < thresholdFinderPattern) + useFinderPattern = true; + } + + bool useCode = false; + int versionByCode = 7; + if (cvRound(versionByFinderPattern) >= 7 || versionByTransition >= 7) { + vector> versionAndDistances; + if (cvRound(versionByFinderPattern) >= 7) { + versionAndDistances.push_back(getVersionByCode(numModulesByFinderPattern, no_border_intermediate, + cvRound(versionByFinderPattern))); + } + if (versionByTransition >= 7) { + versionAndDistances.push_back(getVersionByCode(numModulesByTransition, no_border_intermediate, + versionByTransition)); + } + const auto& bestVersion = min(versionAndDistances.front(), versionAndDistances.back()); + double distanceByCode = bestVersion.first; + versionByCode = bestVersion.second; + if (distanceByCode < 5.) { + useCode = true; + } + } + + if (useCode) { + CV_LOG_INFO(NULL, "Version type: useCode"); + version = versionByCode; + } + else if (useFinderPattern ) { + CV_LOG_INFO(NULL, "Version type: useFinderPattern"); + version = cvRound(versionByFinderPattern); + } + else { + CV_LOG_INFO(NULL, "Version type: useTransition"); + version = versionByTransition; + } version_size = 21 + (version - 1) * 4; - CV_LOG_INFO(NULL, "QR corners: " << original_points[0] << " " << original_points[1] << " " << original_points[2] << - " " << original_points[3]); + if ( !(0 < version && version <= 40) ) { return false; } CV_LOG_INFO(NULL, "QR version: " << (int)version); return true; } From a32f2cd24a75bd3ced5db4f676cd72be96a02bca Mon Sep 17 00:00:00 2001 From: HAN Liutong Date: Tue, 29 Nov 2022 02:28:14 +0800 Subject: [PATCH 223/313] Merge pull request #22520 from hanliutong:hsv Modify the SIMD loop in color_hsv. * Modify the SIMD loops in color_hsv. * Add FP supporting in bit logic. * Add temporary compatibility code. * Use max_nlanes instead of vlanes for array declaration. * Use "CV_SIMD || CV_SIMD_SCALABLE". * Revert the modify of the Universal Intrinsic API * Fix warnings. * Use v_select instead of bits manipulation. --- .../core/include/opencv2/core/hal/intrin.hpp | 1 - .../opencv2/core/hal/intrin_rvv_scalable.hpp | 29 + modules/imgproc/src/color_hsv.simd.hpp | 745 ++++++++++-------- 3 files changed, 455 insertions(+), 320 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index 4f10584713..1971c91e1d 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -785,7 +785,6 @@ namespace CV__SIMD_NAMESPACE { OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int32) OPENCV_HAL_WRAP_BIN_OP_LOGIC(v_int64) - #define OPENCV_HAL_WRAP_BIN_OP_MUL(_Tpvec) \ inline _Tpvec v_mul(const _Tpvec& a, const _Tpvec& b) \ { \ diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 5073e85d8d..c38f1f57db 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -639,6 +639,35 @@ OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int32, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_uint64, VTraits::vlanes()) OPENCV_HAL_IMPL_RVV_LOGIC_OP(v_int64, VTraits::vlanes()) +#define OPENCV_HAL_IMPL_RVV_FLT_BIT_OP(intrin) \ +inline v_float32 intrin (const v_float32& a, const v_float32& b) \ +{ \ + return vreinterpret_f32m1(intrin(vreinterpret_i32m1(a), vreinterpret_i32m1(b))); \ +} +OPENCV_HAL_IMPL_RVV_FLT_BIT_OP(v_and) +OPENCV_HAL_IMPL_RVV_FLT_BIT_OP(v_or) +OPENCV_HAL_IMPL_RVV_FLT_BIT_OP(v_xor) + +inline v_float32 v_not (const v_float32& a) \ +{ \ + return vreinterpret_f32m1(v_not(vreinterpret_i32m1(a))); \ +} + +#if CV_SIMD_SCALABLE_64F +#define OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(intrin) \ +inline v_float64 intrin (const v_float64& a, const v_float64& b) \ +{ \ + return vreinterpret_f64m1(intrin(vreinterpret_i64m1(a), vreinterpret_i64m1(b))); \ +} +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(v_and) +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(v_or) +OPENCV_HAL_IMPL_RVV_FLT64_BIT_OP(v_xor) + +inline v_float64 v_not (const v_float64& a) \ +{ \ + return vreinterpret_f64m1(v_not(vreinterpret_i64m1(a))); \ +} +#endif ////////////// Bitwise shifts ////////////// diff --git a/modules/imgproc/src/color_hsv.simd.hpp b/modules/imgproc/src/color_hsv.simd.hpp index 9837af4acb..218103c4fe 100644 --- a/modules/imgproc/src/color_hsv.simd.hpp +++ b/modules/imgproc/src/color_hsv.simd.hpp @@ -5,6 +5,19 @@ #include "precomp.hpp" #include "opencv2/core/hal/intrin.hpp" +#if CV_SIMD_SCALABLE +/* FIX IT: +// std::swap(a, b) is not available for RVV vector types, +// and CV_SWAP needs another "t" as input, +// For compatibility, we swap RVV vector manually by using this macro. + +// If others scalable types (e.g. type in ARM SVE) can use std::swap, +// then replace CV_SIMD_SCALABLE with CV_RVV. +// If std::swap is available for RVV vector types in future, remove this macro. +*/ +#define swap(a, b) {auto t = a; a = b; b = t;} +#endif + namespace cv { namespace hal { CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN @@ -85,8 +98,8 @@ struct RGB2HSV_b int i = 0; -#if CV_SIMD - const int vsize = v_uint8::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + const int vsize = VTraits::vlanes(); for ( ; i <= n - vsize; i += vsize, src += scn*vsize, dst += 3*vsize) { @@ -110,108 +123,109 @@ struct RGB2HSV_b vmin = v_min(b, v_min(g, r)); v_uint8 diff, vr, vg; - diff = v - vmin; + diff = v_sub(v, vmin); v_uint8 v255 = vx_setall_u8(0xff), vz = vx_setzero_u8(); - vr = v_select(v == r, v255, vz); - vg = v_select(v == g, v255, vz); + vr = v_select(v_eq(v, r), v255, vz); + vg = v_select(v_eq(v, g), v255, vz); // sdiv = sdiv_table[v] - v_int32 sdiv[4]; - v_uint16 vd[2]; - v_expand(v, vd[0], vd[1]); - v_int32 vq[4]; - v_expand(v_reinterpret_as_s16(vd[0]), vq[0], vq[1]); - v_expand(v_reinterpret_as_s16(vd[1]), vq[2], vq[3]); + v_int32 sdiv0, sdiv1, sdiv2, sdiv3;; + v_uint16 vd0, vd1, vd2; + v_expand(v, vd0, vd1); + v_int32 vq0, vq1, vq2, vq3; + v_expand(v_reinterpret_as_s16(vd0), vq0, vq1); + v_expand(v_reinterpret_as_s16(vd1), vq2, vq3); { - int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storevq[vsize]; - for (int k = 0; k < 4; k++) - { - v_store_aligned(storevq + k*vsize/4, vq[k]); - } + int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storevq[VTraits::max_nlanes]; + v_store_aligned(storevq, vq0); + v_store_aligned(storevq + vsize/4, vq1); + v_store_aligned(storevq + 2*vsize/4, vq2); + v_store_aligned(storevq + 3*vsize/4, vq3); - for(int k = 0; k < 4; k++) - { - sdiv[k] = vx_lut(sdiv_table, storevq + k*vsize/4); - } + sdiv0 = vx_lut(sdiv_table, storevq); + sdiv1 = vx_lut(sdiv_table, storevq + vsize/4); + sdiv2 = vx_lut(sdiv_table, storevq + 2*vsize/4); + sdiv3 = vx_lut(sdiv_table, storevq + 3*vsize/4); } // hdiv = hdiv_table[diff] - v_int32 hdiv[4]; - v_uint16 diffd[2]; - v_expand(diff, diffd[0], diffd[1]); - v_int32 diffq[4]; - v_expand(v_reinterpret_as_s16(diffd[0]), diffq[0], diffq[1]); - v_expand(v_reinterpret_as_s16(diffd[1]), diffq[2], diffq[3]); + v_int32 hdiv0, hdiv1, hdiv2, hdiv3; + v_uint16 diffd0, diffd1, diffd2; + v_expand(diff, diffd0, diffd1); + v_int32 diffq0, diffq1, diffq2, diffq3; + v_expand(v_reinterpret_as_s16(diffd0), diffq0, diffq1); + v_expand(v_reinterpret_as_s16(diffd1), diffq2, diffq3); { - int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storediffq[vsize]; - for (int k = 0; k < 4; k++) - { - v_store_aligned(storediffq + k*vsize/4, diffq[k]); - } - - for (int k = 0; k < 4; k++) - { - hdiv[k] = vx_lut((int32_t*)hdiv_table, storediffq + k*vsize/4); - } + int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storediffq[VTraits::max_nlanes]; + v_store_aligned(storediffq, diffq0); + v_store_aligned(storediffq + vsize/4, diffq1); + v_store_aligned(storediffq + 2*vsize/4, diffq2); + v_store_aligned(storediffq + 3*vsize/4, diffq3); + hdiv0 = vx_lut((int32_t*)hdiv_table, storediffq + 0*vsize/4); + hdiv1 = vx_lut((int32_t*)hdiv_table, storediffq + 1*vsize/4); + hdiv2 = vx_lut((int32_t*)hdiv_table, storediffq + 2*vsize/4); + hdiv3 = vx_lut((int32_t*)hdiv_table, storediffq + 3*vsize/4); } // s = (diff * sdiv + (1 << (hsv_shift-1))) >> hsv_shift; - v_int32 sq[4]; + v_int32 sq0, sq1, sq2, sq3; v_int32 vdescale = vx_setall_s32(1 << (hsv_shift-1)); - for (int k = 0; k < 4; k++) - { - sq[k] = (diffq[k]*sdiv[k] + vdescale) >> hsv_shift; - } - v_int16 sd[2]; - sd[0] = v_pack(sq[0], sq[1]); - sd[1] = v_pack(sq[2], sq[3]); - s = v_pack_u(sd[0], sd[1]); + sq0 = v_shr(v_add(v_mul(diffq0, sdiv0), vdescale)); + sq1 = v_shr(v_add(v_mul(diffq1, sdiv1), vdescale)); + sq2 = v_shr(v_add(v_mul(diffq2, sdiv2), vdescale)); + sq3 = v_shr(v_add(v_mul(diffq3, sdiv3), vdescale)); + v_int16 sd0, sd1; + sd0 = v_pack(sq0, sq1); + sd1 = v_pack(sq2, sq3); + s = v_pack_u(sd0, sd1); // expand all to 16 bits - v_uint16 bdu[2], gdu[2], rdu[2]; - v_expand(b, bdu[0], bdu[1]); - v_expand(g, gdu[0], gdu[1]); - v_expand(r, rdu[0], rdu[1]); - v_int16 bd[2], gd[2], rd[2]; - bd[0] = v_reinterpret_as_s16(bdu[0]); - bd[1] = v_reinterpret_as_s16(bdu[1]); - gd[0] = v_reinterpret_as_s16(gdu[0]); - gd[1] = v_reinterpret_as_s16(gdu[1]); - rd[0] = v_reinterpret_as_s16(rdu[0]); - rd[1] = v_reinterpret_as_s16(rdu[1]); + v_uint16 bdu0, bdu1, gdu0, gdu1, rdu0, rdu1; + v_expand(b, bdu0, bdu1); + v_expand(g, gdu0, gdu1); + v_expand(r, rdu0, rdu1); + v_int16 bd0, bd1, gd0, gd1, rd0, rd1; + bd0 = v_reinterpret_as_s16(bdu0); + bd1 = v_reinterpret_as_s16(bdu1); + gd0 = v_reinterpret_as_s16(gdu0); + gd1 = v_reinterpret_as_s16(gdu1); + rd0 = v_reinterpret_as_s16(rdu0); + rd1 = v_reinterpret_as_s16(rdu1); - v_int16 vrd[2], vgd[2]; - v_expand(v_reinterpret_as_s8(vr), vrd[0], vrd[1]); - v_expand(v_reinterpret_as_s8(vg), vgd[0], vgd[1]); - v_int16 diffsd[2]; - diffsd[0] = v_reinterpret_as_s16(diffd[0]); - diffsd[1] = v_reinterpret_as_s16(diffd[1]); + v_int16 vrd0, vrd1, vgd0, vgd1; + v_expand(v_reinterpret_as_s8(vr), vrd0, vrd1); + v_expand(v_reinterpret_as_s8(vg), vgd0, vgd1); + v_int16 diffsd0, diffsd1; + diffsd0 = v_reinterpret_as_s16(diffd0); + diffsd1 = v_reinterpret_as_s16(diffd1); - v_int16 hd[2]; + v_int16 hd0, hd1; // h before division - for (int k = 0; k < 2; k++) - { - v_int16 gb = gd[k] - bd[k]; - v_int16 br = bd[k] - rd[k] + (diffsd[k] << 1); - v_int16 rg = rd[k] - gd[k] + (diffsd[k] << 2); - hd[k] = (vrd[k] & gb) + ((~vrd[k]) & ((vgd[k] & br) + ((~vgd[k]) & rg))); - } + v_int16 gb = v_sub(gd0 ,bd0); + v_int16 br = v_add(v_sub(bd0 ,rd0), v_shl<1>(diffsd0)); + v_int16 rg = v_add(v_sub(rd0 ,gd0), v_shl<2>(diffsd0)); + hd0 = v_add(v_and(vrd0, gb), v_and(v_not(vrd0), v_add(v_and(vgd0, br), v_and(v_not(vgd0), rg)))); + gb = v_sub(gd1, bd1); + br = v_add(v_sub(bd1, rd1), v_shl<1>(diffsd1)); + rg = v_add(v_sub(rd1, gd1), v_shl<2>(diffsd1)); + hd1 = v_add(v_and(vrd1, gb), v_and(v_not(vrd1), v_add(v_and(vgd1, br), v_and(v_not(vgd1), rg)))); // h div and fix - v_int32 hq[4]; - v_expand(hd[0], hq[0], hq[1]); - v_expand(hd[1], hq[2], hq[3]); - for(int k = 0; k < 4; k++) - { - hq[k] = (hq[k]*hdiv[k] + vdescale) >> hsv_shift; - } - hd[0] = v_pack(hq[0], hq[1]); - hd[1] = v_pack(hq[2], hq[3]); + v_int32 hq0, hq1, hq2, hq3; + v_expand(hd0, hq0, hq1); + v_expand(hd1, hq2, hq3); + hq0 = v_shr(v_add(v_mul(hq0, hdiv0), vdescale)); + hq1 = v_shr(v_add(v_mul(hq1, hdiv1), vdescale)); + hq2 = v_shr(v_add(v_mul(hq2, hdiv2), vdescale)); + hq3 = v_shr(v_add(v_mul(hq3, hdiv3), vdescale)); + + hd0 = v_pack(hq0, hq1); + hd1 = v_pack(hq2, hq3); v_int16 vhr = vx_setall_s16((short)hr); v_int16 vzd = vx_setzero_s16(); - hd[0] += v_select(hd[0] < vzd, vhr, vzd); - hd[1] += v_select(hd[1] < vzd, vhr, vzd); - h = v_pack_u(hd[0], hd[1]); + hd0 = v_add(hd0 ,v_select(v_lt(hd0, vzd), vhr, vzd)); + hd1 = v_add(hd1 ,v_select(v_lt(hd1, vzd), vhr, vzd)); + h = v_pack_u(hd0, hd1); v_store_interleave(dst, h, s, v); } @@ -260,7 +274,7 @@ struct RGB2HSV_f : srccn(_srccn), blueIdx(_blueIdx), hrange(_hrange) { } - #if CV_SIMD + #if CV_SIMD || CV_SIMD_SCALABLE inline void process(const v_float32& v_r, const v_float32& v_g, const v_float32& v_b, v_float32& v_h, v_float32& v_s, v_float32& v_v, float hscale) const @@ -269,17 +283,18 @@ struct RGB2HSV_f v_float32 v_max_rgb = v_max(v_max(v_r, v_g), v_b); v_float32 v_eps = vx_setall_f32(FLT_EPSILON); - v_float32 v_diff = v_max_rgb - v_min_rgb; - v_s = v_diff / (v_abs(v_max_rgb) + v_eps); + v_float32 v_diff = v_sub(v_max_rgb, v_min_rgb); + v_s = v_div(v_diff, v_add(v_abs(v_max_rgb), v_eps)); - v_float32 v_r_eq_max = v_r == v_max_rgb; - v_float32 v_g_eq_max = v_g == v_max_rgb; - v_h = v_select(v_r_eq_max, v_g - v_b, - v_select(v_g_eq_max, v_b - v_r, v_r - v_g)); - v_float32 v_res = v_select(v_r_eq_max, (v_g < v_b) & vx_setall_f32(360.0f), - v_select(v_g_eq_max, vx_setall_f32(120.0f), vx_setall_f32(240.0f))); - v_float32 v_rev_diff = vx_setall_f32(60.0f) / (v_diff + v_eps); - v_h = v_muladd(v_h, v_rev_diff, v_res) * vx_setall_f32(hscale); + v_float32 v_r_eq_max = v_eq(v_r, v_max_rgb); + v_float32 v_g_eq_max = v_eq(v_g, v_max_rgb); + v_h = v_select(v_r_eq_max, v_sub(v_g, v_b), + v_select(v_g_eq_max, v_sub(v_b, v_r), v_sub(v_r, v_g))); + v_float32 v_res = v_select(v_r_eq_max, + v_select(v_lt(v_g, v_b), vx_setall_f32(360.0f), vx_setall_f32(0.0f)), + v_select(v_g_eq_max, vx_setall_f32(120.0f), vx_setall_f32(240.0f))); + v_float32 v_rev_diff = v_div(vx_setall_f32(60.0f), v_add(v_diff, v_eps)); + v_h = v_mul(v_muladd(v_h, v_rev_diff, v_res), vx_setall_f32(hscale)); v_v = v_max_rgb; } @@ -293,8 +308,8 @@ struct RGB2HSV_f float hscale = hrange*(1.f/360.f); n *= 3; -#if CV_SIMD - const int vsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + const int vsize = VTraits::vlanes(); for ( ; i <= n - 3*vsize; i += 3*vsize, src += scn * vsize) { v_float32 r, g, b, a; @@ -353,7 +368,7 @@ struct RGB2HSV_f }; -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE inline void HSV2RGB_simd(const v_float32& h, const v_float32& s, const v_float32& v, v_float32& b, v_float32& g, v_float32& r, float hscale) { @@ -361,43 +376,43 @@ inline void HSV2RGB_simd(const v_float32& h, const v_float32& s, const v_float32 v_float32 v_s = s; v_float32 v_v = v; - v_h = v_h * vx_setall_f32(hscale); + v_h = v_mul(v_h, vx_setall_f32(hscale)); v_float32 v_pre_sector = v_cvt_f32(v_trunc(v_h)); - v_h = v_h - v_pre_sector; + v_h = v_sub(v_h, v_pre_sector); v_float32 v_tab0 = v_v; v_float32 v_one = vx_setall_f32(1.0f); - v_float32 v_tab1 = v_v * (v_one - v_s); - v_float32 v_tab2 = v_v * (v_one - (v_s * v_h)); - v_float32 v_tab3 = v_v * (v_one - (v_s * (v_one - v_h))); + v_float32 v_tab1 = v_mul(v_v, v_sub(v_one, v_s)); + v_float32 v_tab2 = v_mul(v_v, v_sub(v_one, v_mul(v_s, v_h))); + v_float32 v_tab3 = v_mul(v_v, v_sub(v_one, v_mul(v_s, v_sub(v_one, v_h)))); v_float32 v_one_sixth = vx_setall_f32(1.0f / 6.0f); - v_float32 v_sector = v_pre_sector * v_one_sixth; + v_float32 v_sector = v_mul(v_pre_sector, v_one_sixth); v_sector = v_cvt_f32(v_trunc(v_sector)); v_float32 v_six = vx_setall_f32(6.0f); - v_sector = v_pre_sector - (v_sector * v_six); + v_sector = v_sub(v_pre_sector, v_mul(v_sector, v_six)); v_float32 v_two = vx_setall_f32(2.0f); - v_h = v_tab1 & (v_sector < v_two); - v_h = v_h | (v_tab3 & (v_sector == v_two)); + v_h = v_select(v_lt(v_sector, v_two), v_tab1, vx_setall_f32(0.0f)); + v_h = v_select(v_eq(v_sector, v_two), v_tab3, v_h); v_float32 v_three = vx_setall_f32(3.0f); - v_h = v_h | (v_tab0 & (v_sector == v_three)); + v_h = v_select(v_eq(v_sector, v_three), v_tab0, v_h); v_float32 v_four = vx_setall_f32(4.0f); - v_h = v_h | (v_tab0 & (v_sector == v_four)); - v_h = v_h | (v_tab2 & (v_sector > v_four)); + v_h = v_select(v_eq(v_sector, v_four), v_tab0, v_h); + v_h = v_select(v_gt(v_sector, v_four), v_tab2, v_h); - v_s = v_tab3 & (v_sector < v_one); - v_s = v_s | (v_tab0 & (v_sector == v_one)); - v_s = v_s | (v_tab0 & (v_sector == v_two)); - v_s = v_s | (v_tab2 & (v_sector == v_three)); - v_s = v_s | (v_tab1 & (v_sector > v_three)); + v_s = v_select(v_lt(v_sector, v_one), v_tab3, v_s); + v_s = v_select(v_eq(v_sector, v_one), v_tab0, v_s); + v_s = v_select(v_eq(v_sector, v_two), v_tab0, v_s); + v_s = v_select(v_eq(v_sector, v_three), v_tab2, v_s); + v_s = v_select(v_gt(v_sector, v_three), v_tab1, v_s); - v_v = v_tab0 & (v_sector < v_one); - v_v = v_v | (v_tab2 & (v_sector == v_one)); - v_v = v_v | (v_tab1 & (v_sector == v_two)); - v_v = v_v | (v_tab1 & (v_sector == v_three)); - v_v = v_v | (v_tab3 & (v_sector == v_four)); - v_v = v_v | (v_tab0 & (v_sector > v_four)); + v_v = v_select(v_lt(v_sector, v_one), v_tab0, v_v); + v_v = v_select(v_eq(v_sector, v_one), v_tab2, v_v); + v_v = v_select(v_eq(v_sector, v_two), v_tab1, v_v); + v_v = v_select(v_eq(v_sector, v_three), v_tab1, v_v); + v_v = v_select(v_eq(v_sector, v_four), v_tab3, v_v); + v_v = v_select(v_gt(v_sector, v_four), v_tab0, v_v); b = v_h; g = v_s; @@ -457,8 +472,8 @@ struct HSV2RGB_f float hs = hscale; n *= 3; -#if CV_SIMD - const int vsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + const int vsize = VTraits::vlanes(); v_float32 valpha = vx_setall_f32(alpha); for (; i <= n - vsize*3; i += vsize*3, dst += dcn * vsize) { @@ -514,60 +529,102 @@ struct HSV2RGB_b int j = 0, dcn = dstcn; uchar alpha = ColorChannel::max(); -#if CV_SIMD - const int vsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + const int vsize = VTraits::vlanes(); for (j = 0; j <= (n - vsize*4) * 3; j += 3 * 4 * vsize, dst += dcn * 4 * vsize) { v_uint8 h_b, s_b, v_b; - v_uint16 h_w[2], s_w[2], v_w[2]; - v_uint32 h_u[4], s_u[4], v_u[4]; + v_uint16 h_w0, h_w1, s_w0, s_w1, v_w0, v_w1; + v_uint32 h_u0, h_u1, h_u2, h_u3, s_u0, s_u1, s_u2, s_u3, v_u0, v_u1, v_u2, v_u3; v_load_deinterleave(src + j, h_b, s_b, v_b); - v_expand(h_b, h_w[0], h_w[1]); - v_expand(s_b, s_w[0], s_w[1]); - v_expand(v_b, v_w[0], v_w[1]); - v_expand(h_w[0], h_u[0], h_u[1]); - v_expand(h_w[1], h_u[2], h_u[3]); - v_expand(s_w[0], s_u[0], s_u[1]); - v_expand(s_w[1], s_u[2], s_u[3]); - v_expand(v_w[0], v_u[0], v_u[1]); - v_expand(v_w[1], v_u[2], v_u[3]); + v_expand(h_b, h_w0, h_w1); + v_expand(s_b, s_w0, s_w1); + v_expand(v_b, v_w0, v_w1); + v_expand(h_w0, h_u0, h_u1); + v_expand(h_w1, h_u2, h_u3); + v_expand(s_w0, s_u0, s_u1); + v_expand(s_w1, s_u2, s_u3); + v_expand(v_w0, v_u0, v_u1); + v_expand(v_w1, v_u2, v_u3); - v_int32 b_i[4], g_i[4], r_i[4]; + v_int32 b_i0, b_i1, b_i2, b_i3, g_i0, g_i1, g_i2, g_i3, r_i0, r_i1, r_i2, r_i3; v_float32 v_coeff0 = vx_setall_f32(1.0f / 255.0f); v_float32 v_coeff1 = vx_setall_f32(255.0f); - for( int k = 0; k < 4; k++ ) - { - v_float32 h = v_cvt_f32(v_reinterpret_as_s32(h_u[k])); - v_float32 s = v_cvt_f32(v_reinterpret_as_s32(s_u[k])); - v_float32 v = v_cvt_f32(v_reinterpret_as_s32(v_u[k])); + v_float32 h = v_cvt_f32(v_reinterpret_as_s32(h_u0)); + v_float32 s = v_cvt_f32(v_reinterpret_as_s32(s_u0)); + v_float32 v = v_cvt_f32(v_reinterpret_as_s32(v_u0)); - s *= v_coeff0; - v *= v_coeff0; - v_float32 b, g, r; - HSV2RGB_simd(h, s, v, b, g, r, hscale); + s = v_mul(s, v_coeff0); + v = v_mul(v, v_coeff0); + v_float32 b, g, r; + HSV2RGB_simd(h, s, v, b, g, r, hscale); - b *= v_coeff1; - g *= v_coeff1; - r *= v_coeff1; - b_i[k] = v_trunc(b); - g_i[k] = v_trunc(g); - r_i[k] = v_trunc(r); - } + b = v_mul(b, v_coeff1); + g = v_mul(g, v_coeff1); + r = v_mul(r, v_coeff1); + b_i0 = v_trunc(b); + g_i0 = v_trunc(g); + r_i0 = v_trunc(r); - v_uint16 r_w[2], g_w[2], b_w[2]; + h = v_cvt_f32(v_reinterpret_as_s32(h_u1)); + s = v_cvt_f32(v_reinterpret_as_s32(s_u1)); + v = v_cvt_f32(v_reinterpret_as_s32(v_u1)); + + s = v_mul(s, v_coeff0); + v = v_mul(v, v_coeff0); + HSV2RGB_simd(h, s, v, b, g, r, hscale); + + b = v_mul(b, v_coeff1); + g = v_mul(g, v_coeff1); + r = v_mul(r, v_coeff1); + b_i1 = v_trunc(b); + g_i1 = v_trunc(g); + r_i1 = v_trunc(r); + + h = v_cvt_f32(v_reinterpret_as_s32(h_u2)); + s = v_cvt_f32(v_reinterpret_as_s32(s_u2)); + v = v_cvt_f32(v_reinterpret_as_s32(v_u2)); + + s = v_mul(s, v_coeff0); + v = v_mul(v, v_coeff0); + HSV2RGB_simd(h, s, v, b, g, r, hscale); + + b = v_mul(b, v_coeff1); + g = v_mul(g, v_coeff1); + r = v_mul(r, v_coeff1); + b_i2 = v_trunc(b); + g_i2 = v_trunc(g); + r_i2 = v_trunc(r); + + h = v_cvt_f32(v_reinterpret_as_s32(h_u3)); + s = v_cvt_f32(v_reinterpret_as_s32(s_u3)); + v = v_cvt_f32(v_reinterpret_as_s32(v_u3)); + + s = v_mul(s, v_coeff0); + v = v_mul(v, v_coeff0); + HSV2RGB_simd(h, s, v, b, g, r, hscale); + + b = v_mul(b, v_coeff1); + g = v_mul(g, v_coeff1); + r = v_mul(r, v_coeff1); + b_i3 = v_trunc(b); + g_i3 = v_trunc(g); + r_i3 = v_trunc(r); + + v_uint16 r_w0, r_w1, g_w0, g_w1, b_w0, b_w1; v_uint8 r_b, g_b, b_b; - r_w[0] = v_pack_u(r_i[0], r_i[1]); - r_w[1] = v_pack_u(r_i[2], r_i[3]); - r_b = v_pack(r_w[0], r_w[1]); - g_w[0] = v_pack_u(g_i[0], g_i[1]); - g_w[1] = v_pack_u(g_i[2], g_i[3]); - g_b = v_pack(g_w[0], g_w[1]); - b_w[0] = v_pack_u(b_i[0], b_i[1]); - b_w[1] = v_pack_u(b_i[2], b_i[3]); - b_b = v_pack(b_w[0], b_w[1]); + r_w0 = v_pack_u(r_i0, r_i1); + r_w1 = v_pack_u(r_i2, r_i3); + r_b = v_pack(r_w0, r_w1); + g_w0 = v_pack_u(g_i0, g_i1); + g_w1 = v_pack_u(g_i2, g_i3); + g_b = v_pack(g_w0, g_w1); + b_w0 = v_pack_u(b_i0, b_i1); + b_w1 = v_pack_u(b_i2, b_i3); + b_b = v_pack(b_w0, b_w1); if( dcn == 3 ) { @@ -621,7 +678,7 @@ struct RGB2HLS_f { } -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE inline void process(const v_float32& r, const v_float32& g, const v_float32& b, const v_float32& vhscale, v_float32& h, v_float32& l, v_float32& s) const @@ -629,28 +686,28 @@ struct RGB2HLS_f v_float32 maxRgb = v_max(v_max(r, g), b); v_float32 minRgb = v_min(v_min(r, g), b); - v_float32 diff = maxRgb - minRgb; - v_float32 msum = maxRgb + minRgb; + v_float32 diff = v_sub(maxRgb, minRgb); + v_float32 msum = v_add(maxRgb, minRgb); v_float32 vhalf = vx_setall_f32(0.5f); - l = msum * vhalf; + l = v_mul(msum, vhalf); - s = diff / v_select(l < vhalf, msum, vx_setall_f32(2.0f) - msum); + s = v_div(diff, v_select(v_lt(l, vhalf), msum, v_sub(vx_setall_f32(2.0f), msum))); - v_float32 rMaxMask = maxRgb == r; - v_float32 gMaxMask = maxRgb == g; + v_float32 rMaxMask = v_eq(maxRgb, r); + v_float32 gMaxMask = v_eq(maxRgb, g); - h = v_select(rMaxMask, g - b, v_select(gMaxMask, b - r, r - g)); - v_float32 hpart = v_select(rMaxMask, (g < b) & vx_setall_f32(360.0f), + h = v_select(rMaxMask, v_sub(g, b), v_select(gMaxMask, v_sub(b, r), v_sub(r, g))); + v_float32 hpart = v_select(rMaxMask, v_select(v_lt(g, b), vx_setall_f32(360.0f), vx_setall_f32(0.0f)), v_select(gMaxMask, vx_setall_f32(120.0f), vx_setall_f32(240.0f))); - v_float32 invDiff = vx_setall_f32(60.0f) / diff; - h = v_muladd(h, invDiff, hpart) * vhscale; + v_float32 invDiff = v_div(vx_setall_f32(60.0f), diff); + h = v_mul(v_muladd(h, invDiff, hpart), vhscale); - v_float32 diffEpsMask = diff > vx_setall_f32(FLT_EPSILON); + v_float32 diffEpsMask = v_gt(diff, vx_setall_f32(FLT_EPSILON)); - h = diffEpsMask & h; + h = v_select(diffEpsMask, h, vx_setall_f32(0.0f)); // l = l; - s = diffEpsMask & s; + s = v_select(diffEpsMask, s, vx_setall_f32(0.0f)); } #endif @@ -660,8 +717,8 @@ struct RGB2HLS_f int i = 0, bidx = blueIdx, scn = srccn; -#if CV_SIMD - const int vsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + const int vsize = VTraits::vlanes(); v_float32 vhscale = vx_setall_f32(hscale); for ( ; i <= n - vsize; @@ -744,29 +801,29 @@ struct RGB2HLS_b int scn = srccn; -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; #else float CV_DECL_ALIGNED(16) buf[bufChannels*BLOCK_SIZE]; #endif -#if CV_SIMD - static const int fsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + static const int fsize = VTraits::vlanes(); //TODO: fix that when v_interleave is available - float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[fsize*3]; + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[VTraits::max_nlanes*3]; v_store_interleave(interTmpM, vx_setall_f32(1.f), vx_setall_f32(255.f), vx_setall_f32(255.f)); - v_float32 mhls[3]; - for(int k = 0; k < 3; k++) - { - mhls[k] = vx_load_aligned(interTmpM + k*fsize); - } + v_float32 mhls0, mhls1, mhls2, mhls3; + mhls0 = vx_load_aligned(interTmpM); + mhls1 = vx_load_aligned(interTmpM + fsize); + mhls2 = vx_load_aligned(interTmpM + 2*fsize); + mhls3 = vx_load_aligned(interTmpM + 3*fsize); #endif for(int i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 ) { int dn = std::min(n - i, (int)BLOCK_SIZE); -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE v_float32 v255inv = vx_setall_f32(1.f/255.f); if (scn == 3) { @@ -778,8 +835,8 @@ struct RGB2HLS_b v_uint16 drgb = vx_load_expand(src); v_int32 qrgb0, qrgb1; v_expand(v_reinterpret_as_s16(drgb), qrgb0, qrgb1); - v_store_aligned(buf + j + 0*fsize, v_cvt_f32(qrgb0)*v255inv); - v_store_aligned(buf + j + 1*fsize, v_cvt_f32(qrgb1)*v255inv); + v_store_aligned(buf + j + 0*fsize, v_mul(v_cvt_f32(qrgb0),v255inv)); + v_store_aligned(buf + j + 1*fsize, v_mul(v_cvt_f32(qrgb1),v255inv)); } for( ; j < dn*3; j++, src++ ) { @@ -793,30 +850,39 @@ struct RGB2HLS_b for ( ; j <= dn*bufChannels - nBlock*bufChannels; j += nBlock*bufChannels, src += nBlock*4) { - v_uint8 rgb[3], dummy; - v_load_deinterleave(src, rgb[0], rgb[1], rgb[2], dummy); + v_uint8 rgb0, rgb1, rgb2, rgb3, dummy; + v_load_deinterleave(src, rgb0, rgb1, rgb2, dummy); - v_uint16 d[3*2]; - for(int k = 0; k < 3; k++) - { - v_expand(rgb[k], d[k*2+0], d[k*2+1]); - } - v_int32 q[3*4]; - for(int k = 0; k < 3*2; k++) - { - v_expand(v_reinterpret_as_s16(d[k]), q[k*2+0], q[k*2+1]); - } + v_uint16 d0,d1,d2,d3,d4,d5; + v_expand(rgb0, d0, d1); + v_expand(rgb1, d2, d3); + v_expand(rgb2, d4, d5); + v_int32 q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11; - v_float32 f[3*4]; - for(int k = 0; k < 3*4; k++) - { - f[k] = v_cvt_f32(q[k])*v255inv; - } + v_expand(v_reinterpret_as_s16(d0), q0, q1); + v_expand(v_reinterpret_as_s16(d1), q2, q3); + v_expand(v_reinterpret_as_s16(d2), q4, q5); + v_expand(v_reinterpret_as_s16(d3), q6, q7); + v_expand(v_reinterpret_as_s16(d4), q8, q9); + v_expand(v_reinterpret_as_s16(d5), q10, q11); + v_float32 f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11; + f0 = v_mul(v_cvt_f32(q0), v255inv); + f1 = v_mul(v_cvt_f32(q1), v255inv); + f2 = v_mul(v_cvt_f32(q2), v255inv); + f3 = v_mul(v_cvt_f32(q3), v255inv); + f4 = v_mul(v_cvt_f32(q4), v255inv); + f5 = v_mul(v_cvt_f32(q5), v255inv); + f6 = v_mul(v_cvt_f32(q6), v255inv); + f7 = v_mul(v_cvt_f32(q7), v255inv); + f8 = v_mul(v_cvt_f32(q8), v255inv); + f9 = v_mul(v_cvt_f32(q9), v255inv); + f10 = v_mul(v_cvt_f32(q10), v255inv); + f11 = v_mul(v_cvt_f32(q11), v255inv); - for(int k = 0; k < 4; k++) - { - v_store_interleave(buf + j + k*bufChannels*fsize, f[0*4+k], f[1*4+k], f[2*4+k]); - } + v_store_interleave(buf + j, f0, f4, f8); + v_store_interleave(buf + j + bufChannels*fsize, f1, f5, f9); + v_store_interleave(buf + j + 2*bufChannels*fsize, f2, f6, f10); + v_store_interleave(buf + j + 3*bufChannels*fsize, f3, f7, f11); } for( ; j < dn*3; j += 3, src += 4 ) { @@ -836,34 +902,53 @@ struct RGB2HLS_b cvt(buf, buf, dn); int j = 0; -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE for( ; j <= dn*3 - fsize*3*4; j += fsize*3*4) { - v_float32 f[3*4]; - for(int k = 0; k < 3*4; k++) - { - f[k] = vx_load_aligned(buf + j + k*fsize); - } + v_float32 f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11; + f0 = vx_load_aligned(buf + j + 0*fsize); + f1 = vx_load_aligned(buf + j + 1*fsize); + f2 = vx_load_aligned(buf + j + 2*fsize); + f3 = vx_load_aligned(buf + j + 3*fsize); + f4 = vx_load_aligned(buf + j + 4*fsize); + f5 = vx_load_aligned(buf + j + 5*fsize); + f6 = vx_load_aligned(buf + j + 6*fsize); + f7 = vx_load_aligned(buf + j + 7*fsize); + f8 = vx_load_aligned(buf + j + 8*fsize); + f9 = vx_load_aligned(buf + j + 9*fsize); + f10 = vx_load_aligned(buf + j + 10*fsize); + f11 = vx_load_aligned(buf + j + 11*fsize); - for(int k = 0; k < 4; k++) - { - for(int l = 0; l < 3; l++) - { - f[k*3+l] = f[k*3+l] * mhls[l]; - } - } + f0 = v_mul(f0, mhls0); + f1 = v_mul(f1, mhls1); + f2 = v_mul(f2, mhls2); + f3 = v_mul(f3, mhls0); + f4 = v_mul(f4, mhls1); + f5 = v_mul(f5, mhls2); + f6 = v_mul(f6, mhls0); + f7 = v_mul(f7, mhls1); + f8 = v_mul(f8, mhls2); + f9 = v_mul(f9, mhls0); + f10 = v_mul(f10, mhls1); + f11 = v_mul(f11, mhls2); - v_int32 q[3*4]; - for(int k = 0; k < 3*4; k++) - { - q[k] = v_round(f[k]); - } + v_int32 q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11; + q0 = v_round(f0); + q1 = v_round(f1); + q2 = v_round(f2); + q3 = v_round(f3); + q4 = v_round(f4); + q5 = v_round(f5); + q6 = v_round(f6); + q7 = v_round(f7); + q8 = v_round(f8); + q9 = v_round(f9); + q10 = v_round(f10); + q11 = v_round(f11); - for(int k = 0; k < 3; k++) - { - v_store(dst + j + k*fsize*4, v_pack_u(v_pack(q[k*4+0], q[k*4+1]), - v_pack(q[k*4+2], q[k*4+3]))); - } + v_store(dst + j + 0*fsize*4, v_pack_u(v_pack(q0, q1),v_pack(q2, q3))); + v_store(dst + j + 1*fsize*4, v_pack_u(v_pack(q4, q5),v_pack(q6, q7))); + v_store(dst + j + 2*fsize*4, v_pack_u(v_pack(q8, q9),v_pack(q10, q11))); } #endif for( ; j < dn*3; j += 3 ) @@ -888,39 +973,39 @@ struct HLS2RGB_f : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.f/_hrange) { } -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE inline void process(const v_float32& h, const v_float32& l, const v_float32& s, v_float32& b, v_float32& g, v_float32& r) const { v_float32 v1 = vx_setall_f32(1.0f), v2 = vx_setall_f32(2.0f), v4 = vx_setall_f32(4.0f); - v_float32 lBelowHalfMask = l <= vx_setall_f32(0.5f); - v_float32 ls = l * s; - v_float32 elem0 = v_select(lBelowHalfMask, ls, s - ls); + v_float32 lBelowHalfMask = v_le(l, vx_setall_f32(0.5f)); + v_float32 ls = v_mul(l, s); + v_float32 elem0 = v_select(lBelowHalfMask, ls, v_sub(s, ls)); - v_float32 hsRaw = h * vx_setall_f32(hscale); + v_float32 hsRaw = v_mul(h, vx_setall_f32(hscale)); v_float32 preHs = v_cvt_f32(v_trunc(hsRaw)); - v_float32 hs = hsRaw - preHs; - v_float32 sector = preHs - vx_setall_f32(6.0f) * v_cvt_f32(v_trunc(hsRaw * vx_setall_f32(1.0f / 6.0f))); - v_float32 elem1 = hs + hs; + v_float32 hs = v_sub(hsRaw, preHs); + v_float32 sector = v_sub(preHs, v_mul(vx_setall_f32(6.0f), v_cvt_f32(v_trunc(v_mul(hsRaw, vx_setall_f32(1.0f / 6.0f)))))); + v_float32 elem1 = v_add(hs, hs); - v_float32 tab0 = l + elem0; - v_float32 tab1 = l - elem0; - v_float32 tab2 = l + elem0 - elem0 * elem1; - v_float32 tab3 = l - elem0 + elem0 * elem1; + v_float32 tab0 = v_add(l, elem0); + v_float32 tab1 = v_sub(l, elem0); + v_float32 tab2 = v_sub(v_add(l, elem0), v_mul(elem0, elem1)); + v_float32 tab3 = v_add(v_sub(l, elem0), v_mul(elem0, elem1)); - b = v_select(sector < v2, tab1, - v_select(sector <= v2, tab3, - v_select(sector <= v4, tab0, tab2))); + b = v_select(v_lt(sector, v2), tab1, + v_select(v_le(sector, v2), tab3, + v_select(v_le(sector, v4), tab0, tab2))); - g = v_select(sector < v1, tab3, - v_select(sector <= v2, tab0, - v_select(sector < v4, tab2, tab1))); + g = v_select(v_lt(sector, v1), tab3, + v_select(v_le(sector, v2), tab0, + v_select(v_lt(sector, v4), tab2, tab1))); - r = v_select(sector < v1, tab0, - v_select(sector < v2, tab2, - v_select(sector < v4, tab1, - v_select(sector <= v4, tab3, tab0)))); + r = v_select(v_lt(sector, v1), tab0, + v_select(v_lt(sector, v2), tab2, + v_select(v_lt(sector, v4), tab1, + v_select(v_le(sector, v4), tab3, tab0)))); } #endif @@ -931,8 +1016,8 @@ struct HLS2RGB_f int i = 0, bidx = blueIdx, dcn = dstcn; float alpha = ColorChannel::max(); -#if CV_SIMD - static const int vsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + static const int vsize = VTraits::vlanes(); for (; i <= n - vsize; i += vsize, src += 3*vsize, dst += dcn*vsize) { v_float32 h, l, s, r, g, b; @@ -1020,23 +1105,22 @@ struct HLS2RGB_b int i, j, dcn = dstcn; uchar alpha = ColorChannel::max(); -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; #else float CV_DECL_ALIGNED(16) buf[bufChannels*BLOCK_SIZE]; #endif -#if CV_SIMD - static const int fsize = v_float32::nlanes; +#if CV_SIMD || CV_SIMD_SCALABLE + static const int fsize = VTraits::vlanes(); //TODO: fix that when v_interleave is available - float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[fsize*3]; + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[VTraits::max_nlanes*3]; v_float32 v255inv = vx_setall_f32(1.f/255.f); v_store_interleave(interTmpM, vx_setall_f32(1.f), v255inv, v255inv); - v_float32 mhls[3]; - for(int k = 0; k < 3; k++) - { - mhls[k] = vx_load_aligned(interTmpM + k*fsize); - } + v_float32 mhls0, mhls1, mhls2; + mhls0 = vx_load_aligned(interTmpM + 0*fsize); + mhls1 = vx_load_aligned(interTmpM + 1*fsize); + mhls2 = vx_load_aligned(interTmpM + 2*fsize); #endif for( i = 0; i < n; i += BLOCK_SIZE, src += BLOCK_SIZE*3 ) @@ -1044,39 +1128,53 @@ struct HLS2RGB_b int dn = std::min(n - i, (int)BLOCK_SIZE); j = 0; -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE for( ; j <= dn*3 - 3*4*fsize; j += 3*4*fsize) { // 3x uchar -> 3*4 float - v_uint8 u[3]; - for(int k = 0; k < 3; k++) - { - u[k] = vx_load(src + j + k*4*fsize); - } - v_uint16 d[3*2]; - for(int k = 0; k < 3; k++) - { - v_expand(u[k], d[k*2+0], d[k*2+1]); - } - v_int32 q[3*4]; - for(int k = 0; k < 3*2; k++) - { - v_expand(v_reinterpret_as_s16(d[k]), q[k*2+0], q[k*2+1]); - } + v_uint8 u0, u1, u2; + u0 = vx_load(src + j + 0*4*fsize); + u1 = vx_load(src + j + 1*4*fsize); + u2 = vx_load(src + j + 2*4*fsize); + v_uint16 d0, d1, d2, d3, d4, d5; + v_expand(u0, d0, d1); + v_expand(u1, d2, d3); + v_expand(u2, d4, d5); - v_float32 f[3*4]; - for(int k = 0; k < 4; k++) - { - for(int l = 0; l < 3; l++) - { - f[k*3+l] = v_cvt_f32(q[k*3+l])*mhls[l]; - } - } + v_int32 q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11; + v_expand(v_reinterpret_as_s16(d0), q0, q1); + v_expand(v_reinterpret_as_s16(d1), q2, q3); + v_expand(v_reinterpret_as_s16(d2), q4, q5); + v_expand(v_reinterpret_as_s16(d3), q6, q7); + v_expand(v_reinterpret_as_s16(d4), q8, q9); + v_expand(v_reinterpret_as_s16(d5), q10, q11); - for (int k = 0; k < 4*3; k++) - { - v_store_aligned(buf + j + k*fsize, f[k]); - } + v_float32 f0,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11; + f0 = v_mul(v_cvt_f32(q0),mhls0); + f1 = v_mul(v_cvt_f32(q1),mhls1); + f2 = v_mul(v_cvt_f32(q2),mhls2); + f3 = v_mul(v_cvt_f32(q3),mhls0); + f4 = v_mul(v_cvt_f32(q4),mhls1); + f5 = v_mul(v_cvt_f32(q5),mhls2); + f6 = v_mul(v_cvt_f32(q6),mhls0); + f7 = v_mul(v_cvt_f32(q7),mhls1); + f8 = v_mul(v_cvt_f32(q8),mhls2); + f9 = v_mul(v_cvt_f32(q9),mhls0); + f10 = v_mul(v_cvt_f32(q10),mhls1); + f11 = v_mul(v_cvt_f32(q11),mhls2); + + v_store_aligned(buf + j + 0*fsize, f0); + v_store_aligned(buf + j + 1*fsize, f1); + v_store_aligned(buf + j + 2*fsize, f2); + v_store_aligned(buf + j + 3*fsize, f3); + v_store_aligned(buf + j + 4*fsize, f4); + v_store_aligned(buf + j + 5*fsize, f5); + v_store_aligned(buf + j + 6*fsize, f6); + v_store_aligned(buf + j + 7*fsize, f7); + v_store_aligned(buf + j + 8*fsize, f8); + v_store_aligned(buf + j + 9*fsize, f9); + v_store_aligned(buf + j + 10*fsize, f10); + v_store_aligned(buf + j + 11*fsize, f11); } #endif for( ; j < dn*3; j += 3 ) @@ -1087,7 +1185,7 @@ struct HLS2RGB_b } cvt(buf, buf, dn); -#if CV_SIMD +#if CV_SIMD || CV_SIMD_SCALABLE v_float32 v255 = vx_setall_f32(255.f); if(dcn == 3) { @@ -1095,18 +1193,18 @@ struct HLS2RGB_b float* pbuf = buf; for( ; x <= dn - 4*fsize; x += 4*fsize, dst += 4*fsize, pbuf += 4*fsize) { - v_float32 vf[4]; - vf[0] = vx_load_aligned(pbuf + 0*fsize); - vf[1] = vx_load_aligned(pbuf + 1*fsize); - vf[2] = vx_load_aligned(pbuf + 2*fsize); - vf[3] = vx_load_aligned(pbuf + 3*fsize); - v_int32 vi[4]; - vi[0] = v_round(vf[0]*v255); - vi[1] = v_round(vf[1]*v255); - vi[2] = v_round(vf[2]*v255); - vi[3] = v_round(vf[3]*v255); - v_store(dst, v_pack_u(v_pack(vi[0], vi[1]), - v_pack(vi[2], vi[3]))); + v_float32 vf0, vf1, vf2, vf3; + vf0 = vx_load_aligned(pbuf + 0*fsize); + vf1 = vx_load_aligned(pbuf + 1*fsize); + vf2 = vx_load_aligned(pbuf + 2*fsize); + vf3 = vx_load_aligned(pbuf + 3*fsize); + v_int32 vi0, vi1, vi2, vi3; + vi0 = v_round(v_mul(vf0,v255)); + vi1 = v_round(v_mul(vf1,v255)); + vi2 = v_round(v_mul(vf2,v255)); + vi3 = v_round(v_mul(vf3,v255)); + v_store(dst, v_pack_u(v_pack(vi0, vi1), + v_pack(vi2, vi3))); } for( ; x < dn*3; x++, dst++, pbuf++) { @@ -1119,19 +1217,28 @@ struct HLS2RGB_b float* pbuf = buf; for ( ; x <= dn - 4*fsize; x += fsize, dst += 4*fsize, pbuf += bufChannels*fsize) { - v_float32 r[4], g[4], b[4]; - v_int32 ir[4], ig[4], ib[4]; - for(int k = 0; k < 4; k++) - { - v_load_deinterleave(pbuf, r[k], g[k], b[k]); - ir[k] = v_round(r[k]*v255); - ig[k] = v_round(g[k]*v255); - ib[k] = v_round(b[k]*v255); - } + v_float32 r0, r1, r2, r3, g0, g1, g2, g3, b0, b1, b2, b3; + v_int32 ir0, ir1, ir2, ir3, ig0, ig1, ig2, ig3, ib0, ib1, ib2, ib3; + v_load_deinterleave(pbuf, r0, g0, b0); + ir0 = v_round(v_mul(r0, v255)); + ig0 = v_round(v_mul(g0, v255)); + ib0 = v_round(v_mul(b0, v255)); + v_load_deinterleave(pbuf, r1, g1, b1); + ir1 = v_round(v_mul(r1, v255)); + ig1 = v_round(v_mul(g1, v255)); + ib1 = v_round(v_mul(b1, v255)); + v_load_deinterleave(pbuf, r2, g2, b2); + ir2 = v_round(v_mul(r2, v255)); + ig2 = v_round(v_mul(g2, v255)); + ib2 = v_round(v_mul(b2, v255)); + v_load_deinterleave(pbuf, r3, g3, b3); + ir3 = v_round(v_mul(r3, v255)); + ig3 = v_round(v_mul(g3, v255)); + ib3 = v_round(v_mul(b3, v255)); v_uint8 ur, ug, ub; - ur = v_pack_u(v_pack(ir[0], ir[1]), v_pack(ir[2], ir[3])); - ug = v_pack_u(v_pack(ig[0], ig[1]), v_pack(ig[2], ig[3])); - ub = v_pack_u(v_pack(ib[0], ib[1]), v_pack(ib[2], ib[3])); + ur = v_pack_u(v_pack(ir0, ir1), v_pack(ir2, ir3)); + ug = v_pack_u(v_pack(ig0, ig1), v_pack(ig2, ig3)); + ub = v_pack_u(v_pack(ib0, ib1), v_pack(ib2, ib3)); v_uint8 valpha = vx_setall_u8(alpha); v_store_interleave(dst, ur, ug, ub, valpha); From 441624a5fb9b1cd86c0c458edf0bf8926f5e4636 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Tue, 15 Nov 2022 14:29:30 +0800 Subject: [PATCH 224/313] tile impl --- .../dnn/include/opencv2/dnn/all_layers.hpp | 6 ++ modules/dnn/src/init.cpp | 1 + modules/dnn/src/layers/tile_layer.cpp | 97 +++++++++++++++++++ modules/dnn/src/onnx/onnx_importer.cpp | 78 +++++++++++++++ modules/dnn/test/test_onnx_importer.cpp | 5 + 5 files changed, 187 insertions(+) create mode 100644 modules/dnn/src/layers/tile_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index d74566e57c..37af0ddea5 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -1079,6 +1079,12 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS TileLayer : public Layer + { + public: + static Ptr create(const LayerParams& params); + }; + //! @} //! @} CV__DNN_INLINE_NS_END diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index 27956097ac..d20f9dff8d 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -183,6 +183,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Scatter, ScatterLayer); CV_DNN_REGISTER_LAYER_CLASS(ScatterND, ScatterNDLayer); + CV_DNN_REGISTER_LAYER_CLASS(Tile, TileLayer); CV_DNN_REGISTER_LAYER_CLASS(Quantize, QuantizeLayer); CV_DNN_REGISTER_LAYER_CLASS(Dequantize, DequantizeLayer); diff --git a/modules/dnn/src/layers/tile_layer.cpp b/modules/dnn/src/layers/tile_layer.cpp new file mode 100644 index 0000000000..abaf96bd4a --- /dev/null +++ b/modules/dnn/src/layers/tile_layer.cpp @@ -0,0 +1,97 @@ +// 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" +#include "layers_common.hpp" + +#include + +namespace cv { namespace dnn { + +class TileLayerImpl CV_FINAL : public TileLayer +{ +public: + TileLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + if (params.has("repeats")) + { + DictValue param_repeats = params.get("repeats"); + int n_repeats = param_repeats.size(); + + CV_Assert(n_repeats > 0); + repeats.resize(n_repeats); + for (int i = 0; i < n_repeats; i++) + repeats[i] = param_repeats.get(i); + } + else + CV_Error(Error::StsNotImplemented, "Tile: repeats needs to be treated as parameter but it is missing."); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE + { + CV_CheckEQ(inputs.size(), 1ull, "Tile: one input is expected"); + + // repeats must have the same length as input's dimension number + // FIXIT: it breaks when the input is 1d tensor (represented as 2d mat with size=2 in opencv dnn) + CV_CheckEQ(inputs[0].size(), repeats.size(), "Tile: repeats must be a 1D tensor of the same length as input's dimension number"); + + outputs.assign(1, inputs[0]); + for (int i = 0; i < repeats.size(); i++) + { + outputs[0][i] *= repeats[i]; + } + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const Mat& data = inputs[0]; + Mat& out = outputs[0]; + + Mat tmp = data.clone(); + MatShape tmp_shape = shape(tmp); + MatShape out_shape = shape(out); + int rep_i, ndims = data.dims; + int dims = 1; + for (int i = 0; i < ndims; i++) + { + rep_i = repeats[i]; + if (rep_i != 1) + { + tmp = tmp.reshape(0, dims); + tmp = cv::repeat(tmp, 1, rep_i); + dims *= out_shape[i]; + } + } + tmp = tmp.reshape(0, out_shape); + + tmp.copyTo(out); + } + +private: + std::vector repeats; +}; + +Ptr TileLayer::create(const LayerParams& params) +{ + return makePtr(params); +} + +}} // namespace cv::dnn diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 66f524a14a..5795cb8bf3 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -189,6 +189,7 @@ private: void parseDepthToSpace (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseTile (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseSimpleLayers (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); // Domain: com.microsoft @@ -3156,6 +3157,82 @@ void ONNXImporter::parseScatter(LayerParams& layerParams, const opencv_onnx::Nod addLayer(layerParams, node_proto); } +void ONNXImporter::parseTile(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + // for Tile>1, only the case of 'repeats' being constant is supported. + // 'repeats' is treated as a parameter instead of an input to determine shape in pre-run. + + CV_Assert(node_proto.input_size() == 2 || node_proto.input_size() == 3); // tile-1: 3 inputs, tile>1: 2 inputs + bool is_opset_1 = node_proto.input_size() == 3; + + std::vector const_input_idx; + for (size_t i = 0; i < node_proto.input_size(); ++i) + if (layer_id.find(node_proto.input(i)) == layer_id.end()) + const_input_idx.push_back(i); + + bool all_const = false; + if (const_input_idx.size() == node_proto.input_size()) // all inputs are constant + { + all_const = true; + } + else if ((const_input_idx.size() == 1 && const_input_idx[0] == 1) || // tile>1 + (const_input_idx.size() == 2 && const_input_idx[0] == 1 && const_input_idx[1] == 2)) // tile-1 + { + all_const = false; + } + else + { + if (!is_opset_1) + CV_Error(Error::StsNotImplemented, "ONNX/Tile: repeats being non-constant is not supported."); + else + CV_Error(Error::StsNotImplemented, "ONNX/Tile: tiles or axis being non-constant are not supported."); + } + + int input0_dims = 1; + if (all_const) + input0_dims = getBlob(node_proto, 0).dims; + else + input0_dims = outShapes[node_proto.input(0)].size(); + + // repeats, treated as paramenter + std::vector repeats_vec(input0_dims, 1); + Mat input1_blob = getBlob(node_proto, 1); + if (is_opset_1) + { + // input1 in tile-1: tiles, 1d tensor of shape [1] + CV_CheckEQ(input1_blob.total(), 1ull, "ONNX/Tile: tiles must be a 0D tensor or 1D tensor of shape [1]."); + int tiles = input1_blob.at(0); + // input2 in tile-1: axis, 1d tensor of shape [1] + Mat input2_blob = getBlob(node_proto, 2); + CV_CheckEQ(input2_blob.total(), 1ull, "ONNX/Tile: axis must be a 0D tensor or 1D tensor of shape [1]."); + int axis = input2_blob.at(0); + repeats_vec[axis] = tiles; + } + else + { + // input1 in tile>1: repeats + CV_CheckEQ(input1_blob.dims, 2, "ONNX/Tile: repeats must be a 1D tensor."); // 1D tensor is represented as a 2D Mat + for (int i = 0; i < input1_blob.total(); i++) + repeats_vec[i] = input1_blob.at(i); + } + layerParams.set("repeats", DictValue::arrayInt(repeats_vec.data(), repeats_vec.size())); + + if (all_const) + { + std::vector inputs, output; + Mat input0_blob = getBlob(node_proto, 0); + inputs.push_back(input0_blob); + runLayer(layerParams, inputs, output); + CV_Assert(output.size() == 1); + addConstant(node_proto.output(0), output[0]); + return; + } + else + { + addLayer(layerParams, node_proto); + } +} + void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { bool is_all_input_const = true; @@ -3857,6 +3934,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["CumSum"] = &ONNXImporter::parseCumSum; dispatch["SpaceToDepth"] = dispatch["DepthToSpace"] = &ONNXImporter::parseDepthToSpace; dispatch["ScatterElements"] = dispatch["Scatter"] = dispatch["ScatterND"] = &ONNXImporter::parseScatter; + dispatch["Tile"] = &ONNXImporter::parseTile; dispatch["Equal"] = dispatch["Greater"] = dispatch["Less"] = dispatch["Pow"] = dispatch["Add"] = dispatch["Sub"] = dispatch["Mul"] = dispatch["Div"] = dispatch["GreaterOrEqual"] = diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index b310dce808..70683a64d1 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -2483,6 +2483,11 @@ TEST_P(Test_ONNX_layers, YOLOv7) normAssertDetections(refClassIds, refScores, refBoxes, keep_classIds, keep_confidences, keep_boxes); } +TEST_P(Test_ONNX_layers, Tile) +{ + testONNXModels("tile", pb); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets()); }} // namespace From 9fded9ca537e3022b361fd8eaed10dd5d4ffe103 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Thu, 24 Nov 2022 10:20:04 +0800 Subject: [PATCH 225/313] batched nms impl --- modules/dnn/include/opencv2/dnn/dnn.hpp | 21 ++++++++++ modules/dnn/src/nms.cpp | 55 +++++++++++++++++++++++++ modules/dnn/test/test_nms.cpp | 30 ++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 29d6cfa4f3..affcf780f4 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -1194,6 +1194,27 @@ CV__DNN_INLINE_NS_BEGIN CV_OUT std::vector& indices, const float eta = 1.f, const int top_k = 0); + /** @brief Performs batched non maximum suppression on given boxes and corresponding scores across different classes. + + * @param bboxes a set of bounding boxes to apply NMS. + * @param scores a set of corresponding confidences. + * @param class_ids a set of corresponding class ids. Ids are integer and usually start from 0. + * @param score_threshold a threshold used to filter boxes by score. + * @param nms_threshold a threshold used in non maximum suppression. + * @param indices the kept indices of bboxes after NMS. + * @param eta a coefficient in adaptive threshold formula: \f$nms\_threshold_{i+1}=eta\cdot nms\_threshold_i\f$. + * @param top_k if `>0`, keep at most @p top_k picked indices. + */ + CV_EXPORTS void NMSBoxesBatched(const std::vector& bboxes, const std::vector& scores, const std::vector& class_ids, + const float score_threshold, const float nms_threshold, + CV_OUT std::vector& indices, + const float eta = 1.f, const int top_k = 0); + + CV_EXPORTS_W void NMSBoxesBatched(const std::vector& bboxes, const std::vector& scores, const std::vector& class_ids, + const float score_threshold, const float nms_threshold, + CV_OUT std::vector& indices, + const float eta = 1.f, const int top_k = 0); + /** * @brief Enum of Soft NMS methods. * @see softNMSBoxes diff --git a/modules/dnn/src/nms.cpp b/modules/dnn/src/nms.cpp index d321cfed88..c51a630558 100644 --- a/modules/dnn/src/nms.cpp +++ b/modules/dnn/src/nms.cpp @@ -58,6 +58,61 @@ void NMSBoxes(const std::vector& bboxes, const std::vector& NMSFast_(bboxes, scores, score_threshold, nms_threshold, eta, top_k, indices, rotatedRectIOU); } +template +static inline void NMSBoxesBatchedImpl(const std::vector& bboxes, + const std::vector& scores, const std::vector& class_ids, + const float score_threshold, const float nms_threshold, + std::vector& indices, const float eta, const int top_k) +{ + double x1, y1, x2, y2, max_coord = 0; + for (int i = 0; i < bboxes.size(); i++) + { + x1 = bboxes[i].x; + y1 = bboxes[i].y; + x2 = x1 + bboxes[i].width; + y2 = y1 + bboxes[i].height; + + max_coord = std::max(x1, max_coord); + max_coord = std::max(y1, max_coord); + max_coord = std::max(x2, max_coord); + max_coord = std::max(y2, max_coord); + } + + // calculate offset and add offset to each bbox + std::vector bboxes_offset; + double offset; + for (int i = 0; i < bboxes.size(); i++) + { + offset = class_ids[i] * (max_coord + 1); + bboxes_offset.push_back( + Rect_t(bboxes[i].x + offset, bboxes[i].y + offset, + bboxes[i].width, bboxes[i].height) + ); + } + + NMSFast_(bboxes_offset, scores, score_threshold, nms_threshold, eta, top_k, indices, rectOverlap); +} + +void NMSBoxesBatched(const std::vector& bboxes, + const std::vector& scores, const std::vector& class_ids, + const float score_threshold, const float nms_threshold, + std::vector& indices, const float eta, const int top_k) +{ + CV_Assert_N(bboxes.size() == scores.size(), scores.size() == class_ids.size(), nms_threshold >= 0, eta > 0); + + NMSBoxesBatchedImpl(bboxes, scores, class_ids, score_threshold, nms_threshold, indices, eta, top_k); +} + +void NMSBoxesBatched(const std::vector& bboxes, + const std::vector& scores, const std::vector& class_ids, + const float score_threshold, const float nms_threshold, + std::vector& indices, const float eta, const int top_k) +{ + CV_Assert_N(bboxes.size() == scores.size(), scores.size() == class_ids.size(), nms_threshold >= 0, eta > 0); + + NMSBoxesBatchedImpl(bboxes, scores, class_ids, score_threshold, nms_threshold, indices, eta, top_k); +} + void softNMSBoxes(const std::vector& bboxes, const std::vector& scores, std::vector& updated_scores, diff --git a/modules/dnn/test/test_nms.cpp b/modules/dnn/test/test_nms.cpp index 5ba240287c..6149125119 100644 --- a/modules/dnn/test/test_nms.cpp +++ b/modules/dnn/test/test_nms.cpp @@ -37,6 +37,36 @@ TEST(NMS, Accuracy) ASSERT_EQ(indices[i], ref_indices[i]); } +TEST(BatchedNMS, Accuracy) +{ + //reference results obtained using tf.image.non_max_suppression with iou_threshold=0.5 + std::string dataPath = findDataFile("dnn/batched_nms_reference.yml"); + FileStorage fs(dataPath, FileStorage::READ); + + std::vector bboxes; + std::vector scores; + std::vector idxs; + std::vector ref_indices; + + fs["boxes"] >> bboxes; + fs["probs"] >> scores; + fs["idxs"] >> idxs; + fs["output"] >> ref_indices; + + const float nms_thresh = .5f; + const float score_thresh = .05f; + std::vector indices; + cv::dnn::NMSBoxesBatched(bboxes, scores, idxs, score_thresh, nms_thresh, indices); + + ASSERT_EQ(ref_indices.size(), indices.size()); + + std::sort(indices.begin(), indices.end()); + std::sort(ref_indices.begin(), ref_indices.end()); + + for(size_t i = 0; i < indices.size(); i++) + ASSERT_EQ(indices[i], ref_indices[i]); +} + TEST(SoftNMS, Accuracy) { //reference results are obtained using TF v2.7 tf.image.non_max_suppression_with_scores From 0d56524b72afb0bf097bde176d7913bcb06ff34c Mon Sep 17 00:00:00 2001 From: zihaomu Date: Tue, 29 Nov 2022 17:13:36 +0800 Subject: [PATCH 226/313] gemm support transA and transB, and first input is constance. --- .../dnn/src/layers/fully_connected_layer.cpp | 78 ++++++++++++++----- modules/dnn/src/onnx/onnx_importer.cpp | 51 +++++++++--- modules/dnn/test/test_onnx_importer.cpp | 1 + 3 files changed, 98 insertions(+), 32 deletions(-) diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 71ca706ac4..1be5bbe366 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -80,6 +80,9 @@ public: FullyConnectedLayerImpl(const LayerParams& params) { setParamsFrom(params); + transA = params.get("transA", false); + transB = params.get("transB", false); + bias = params.get("bias_term", true); axis = params.get("axis", 1); if (!blobs.empty()) @@ -116,30 +119,48 @@ public: std::vector &) const CV_OVERRIDE { int numOutput, cAxis; + + std::vector inputsTmp; + inputsTmp.assign(inputs.begin(), inputs.end()); + if (blobs.empty()) { - CV_CheckEQ(inputs.size(), (size_t)2, ""); - numOutput = inputs[1].back(); - cAxis = inputs[0].size() - 1; - int dims = inputs[0].size(); - CV_CheckEQ(inputs[1].size(), (size_t)dims, ""); + CV_CheckEQ(inputsTmp.size(), (size_t)2, ""); + + if (transA) + { + CV_CheckEQ(inputsTmp[0].size(), (size_t)2, ""); + std::swap(inputsTmp[0][0], inputsTmp[0][1]); + } + + if (transB) + { + CV_CheckEQ(inputsTmp[1].size(), (size_t)2, ""); + std::swap(inputsTmp[1][0], inputsTmp[1][1]); + } + + numOutput = inputsTmp[1].back(); + cAxis = inputsTmp[0].size() - 1; + int dims = inputsTmp[0].size(); + CV_CheckEQ(inputsTmp[1].size(), (size_t)dims, ""); CV_CheckGE(dims, 2, ""); for (int i = 0; i < dims - 2; i++) - CV_CheckEQ(inputs[0][i], inputs[1][i], ""); - CV_CheckEQ(inputs[0].back(), inputs[1][dims - 2], ""); + CV_CheckEQ(inputsTmp[0][i], inputsTmp[1][i], ""); + CV_CheckEQ(inputsTmp[0].back(), inputsTmp[1][dims - 2], ""); } else { - CV_CheckEQ(inputs.size(), (size_t)1, ""); + CV_Assert(!transA && !transB); + CV_CheckEQ(inputsTmp.size(), (size_t)1, ""); CV_CheckEQ(blobs[0].dims, 2, ""); numOutput = blobs[0].size[0]; CV_Assert(!bias || (size_t)numOutput == blobs[1].total()); - cAxis = normalize_axis(axis, inputs[0]); + cAxis = normalize_axis(axis, inputsTmp[0]); } MatShape outShape(cAxis + 1); for (int i = 0; i < cAxis; ++i) - outShape[i] = inputs[0][i]; + outShape[i] = inputsTmp[0][i]; outShape.back() = numOutput; outputs.resize(1, outShape); @@ -148,15 +169,15 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { + bool tranAorB = transA || transB; #ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) - return axis == 1; + return axis == 1 && !tranAorB; #endif - return backendId == DNN_BACKEND_OPENCV || - backendId == DNN_BACKEND_CUDA || - (backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1) || - (backendId == DNN_BACKEND_WEBNN && axis == 1); + (backendId == DNN_BACKEND_CUDA && !tranAorB) || + (backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1 && !tranAorB) || + (backendId == DNN_BACKEND_WEBNN && axis == 1 && !tranAorB); } virtual bool setActivation(const Ptr& layer) CV_OVERRIDE @@ -497,6 +518,7 @@ public: if (!blobs.empty()) { + CV_Assert(!transA && !transB); int axisCan = normalize_axis(axis, input[0].dims); int outerSize = input[0].total(0, axisCan); @@ -511,15 +533,30 @@ public: } else { - float* inpData = input[0].ptr(); - float* weightData = input[1].ptr(); + Mat input0 = input[0]; + Mat input1 = input[1]; + + if (transA) + { + CV_Assert(input0.dims == 2); + input0 = input0.t(); + } + + if (transB) + { + CV_Assert(input1.dims == 2); + input1 = input1.t(); + } + + float* inpData = input0.ptr(); + float* weightData = input1.ptr(); float* outData = output[0].ptr(); int dims = output[0].dims; int numSlice = output[0].total() / output[0].total(dims - 2); - int m = input[0].size[dims - 2]; - int n = input[0].size[dims - 1]; - int k = input[1].size[dims - 1]; + int m = input0.size[dims - 2]; + int n = input0.size[dims - 1]; + int k = input1.size[dims - 1]; for (int i = 0; i < numSlice; i++) { Mat inpSlice(m, n, CV_32F, inpData); @@ -716,6 +753,7 @@ public: bool bias; Mat weightsMat, biasMat; + bool transA, transB; Ptr activ; }; diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 6ef8894063..c626a993fb 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2000,18 +2000,9 @@ void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodePr { CV_Assert(node_proto.input_size() >= 2); layerParams.type = "InnerProduct"; - Mat weights = getBlob(node_proto, 1); + int transA = layerParams.get("transA", 0); + layerParams.set("transA", transA == 1); - if (!layerParams.get("transB", 0)) - { - transpose(weights, weights); - } - layerParams.blobs.push_back(weights); - - if (node_proto.input_size() == 3) { - Mat bias = getBlob(node_proto, 2); - layerParams.blobs.push_back(bias); - } if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) { Mat inputBuf = getBlob(node_proto, 0); @@ -2026,7 +2017,43 @@ void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodePr addLayer(constParams, proto); } - layerParams.set("num_output", layerParams.blobs[0].size[0]); + int transB = layerParams.get("transB", 0); + if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) + { + Mat weights = getBlob(node_proto, 1); + + if (transA == 0) // optimized barnch, for now, we can only optimize the Gemm when transA = 0. + { + if (transB == 0) + { + transpose(weights, weights); + } + layerParams.set("transB", false); + layerParams.blobs.push_back(weights); + layerParams.set("num_output", layerParams.blobs[0].size[0]); + } + else // no optimized branch, TODO! optimize when the transA==1. + { + LayerParams constParams; + constParams.name = node_proto.input(1); + constParams.type = "Const"; + constParams.blobs.push_back(weights); + + opencv_onnx::NodeProto proto; + proto.add_output(constParams.name); + addLayer(constParams, proto); + layerParams.set("transB", transB == 1); + } + } + else + layerParams.set("transB", transB == 1); + + if (node_proto.input_size() == 3) + { + Mat bias = getBlob(node_proto, 2); + layerParams.blobs.push_back(bias); + } + layerParams.set("bias_term", node_proto.input_size() == 3); addLayer(layerParams, node_proto); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 43dc817733..cee7cf023f 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1829,6 +1829,7 @@ TEST_P(Test_ONNX_layers, Gemm) { testONNXModels("gemm_no_transB"); testONNXModels("gemm_transB_0"); + testONNXModels("gemm_first_const"); } TEST_P(Test_ONNX_layers, Quantized_Convolution) From 71a1150c95d455c1f640c088f0ccea22de8c86dd Mon Sep 17 00:00:00 2001 From: ClayXrex <87029308+ClayXrex@users.noreply.github.com> Date: Sat, 12 Nov 2022 12:31:57 +0100 Subject: [PATCH 227/313] fix: typo --- .../py_core/py_optimization/py_optimization.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/py_tutorials/py_core/py_optimization/py_optimization.markdown b/doc/py_tutorials/py_core/py_optimization/py_optimization.markdown index 61f403bf54..d24613a643 100644 --- a/doc/py_tutorials/py_core/py_optimization/py_optimization.markdown +++ b/doc/py_tutorials/py_core/py_optimization/py_optimization.markdown @@ -14,7 +14,7 @@ So in this chapter, you will learn: Apart from OpenCV, Python also provides a module **time** which is helpful in measuring the time of execution. Another module **profile** helps to get a detailed report on the code, like how much time each function in the code took, how many times the function was called, etc. But, if you are using -IPython, all these features are integrated in an user-friendly manner. We will see some important +IPython, all these features are integrated in a user-friendly manner. We will see some important ones, and for more details, check links in the **Additional Resources** section. Measuring Performance with OpenCV From f1055a7e91372448a53fb2ecd4f56f5cd75fbc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=A5=9A=E6=B4=8B?= Date: Sun, 2 Oct 2022 17:06:07 +0800 Subject: [PATCH 228/313] add test --- modules/imgproc/include/opencv2/imgproc.hpp | 1 + .../misc/python/test/test_matx_converter.py | 19 +++++++++++++++++++ modules/imgproc/src/test_matx_converter.cpp | 9 +++++++++ modules/python/src2/cv2_convert.hpp | 3 +++ 4 files changed, 32 insertions(+) create mode 100644 modules/imgproc/misc/python/test/test_matx_converter.py create mode 100644 modules/imgproc/src/test_matx_converter.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index afc2742ab6..073833e8f2 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4973,6 +4973,7 @@ public: }; //! @cond IGNORED +CV_EXPORTS_W void testMatxPythonConverter(InputArray src, OutputArray dst, const Vec2d& defaultParam = Vec2d(-5, 5)); // === LineIterator implementation === diff --git a/modules/imgproc/misc/python/test/test_matx_converter.py b/modules/imgproc/misc/python/test/test_matx_converter.py new file mode 100644 index 0000000000..8a39031ca7 --- /dev/null +++ b/modules/imgproc/misc/python/test/test_matx_converter.py @@ -0,0 +1,19 @@ +from __future__ import print_function +import cv2 as cv +from cv2 import testMatxPythonConverter +from tests_common import NewOpenCVTests + + +class MatxConverterTest(NewOpenCVTests): + def test_matxconverter(self): + samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] + + for sample in samples: + img = self.get_sample(sample) + out = testMatxPythonConverter(img) + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() + + diff --git a/modules/imgproc/src/test_matx_converter.cpp b/modules/imgproc/src/test_matx_converter.cpp new file mode 100644 index 0000000000..96e0ecacaf --- /dev/null +++ b/modules/imgproc/src/test_matx_converter.cpp @@ -0,0 +1,9 @@ +#include "precomp.hpp" + +namespace cv{ + void testMatxPythonConverter(InputArray _src, OutputArray _dst, const Vec2d& defaultParam){ + printf("%f %f\n", defaultParam[0], defaultParam[1]); + Mat src = _src.getMat(); + src.copyTo(_dst); + } +} diff --git a/modules/python/src2/cv2_convert.hpp b/modules/python/src2/cv2_convert.hpp index 563f5386b7..7dc0ffb788 100644 --- a/modules/python/src2/cv2_convert.hpp +++ b/modules/python/src2/cv2_convert.hpp @@ -62,6 +62,9 @@ PyObject* pyopencv_from(const T& src) { return PyOpenCV_Converter::from(src); template bool pyopencv_to(PyObject* o, cv::Matx<_Tp, m, n>& mx, const ArgInfo& info) { + if (!o || o == Py_None) + return true; + cv::Mat tmp; if (!pyopencv_to(o, tmp, info)) { return false; From 3a15152be5f4fbd743681ce20bc6fa182547f2aa Mon Sep 17 00:00:00 2001 From: Vadim Levin Date: Wed, 30 Nov 2022 17:40:38 +0300 Subject: [PATCH 229/313] refactor: rework test to be more specific --- .../include/opencv2/core/bindings_utils.hpp | 5 +++++ modules/imgproc/include/opencv2/imgproc.hpp | 1 - .../misc/python/test/test_matx_converter.py | 19 ------------------- modules/imgproc/src/test_matx_converter.cpp | 9 --------- modules/python/src2/cv2_convert.hpp | 3 ++- modules/python/test/test_misc.py | 7 +++++++ 6 files changed, 14 insertions(+), 30 deletions(-) delete mode 100644 modules/imgproc/misc/python/test/test_matx_converter.py delete mode 100644 modules/imgproc/src/test_matx_converter.cpp diff --git a/modules/core/include/opencv2/core/bindings_utils.hpp b/modules/core/include/opencv2/core/bindings_utils.hpp index 4f7eb532b9..76c9437a30 100644 --- a/modules/core/include/opencv2/core/bindings_utils.hpp +++ b/modules/core/include/opencv2/core/bindings_utils.hpp @@ -219,6 +219,11 @@ AsyncArray testAsyncException() return p.getArrayResult(); } +CV_WRAP static inline +String dumpVec2i(const cv::Vec2i value = cv::Vec2i(42, 24)) { + return format("Vec2i(%d, %d)", value[0], value[1]); +} + namespace nested { CV_WRAP static inline bool testEchoBooleanFunction(bool flag) { return flag; diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 073833e8f2..afc2742ab6 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4973,7 +4973,6 @@ public: }; //! @cond IGNORED -CV_EXPORTS_W void testMatxPythonConverter(InputArray src, OutputArray dst, const Vec2d& defaultParam = Vec2d(-5, 5)); // === LineIterator implementation === diff --git a/modules/imgproc/misc/python/test/test_matx_converter.py b/modules/imgproc/misc/python/test/test_matx_converter.py deleted file mode 100644 index 8a39031ca7..0000000000 --- a/modules/imgproc/misc/python/test/test_matx_converter.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import print_function -import cv2 as cv -from cv2 import testMatxPythonConverter -from tests_common import NewOpenCVTests - - -class MatxConverterTest(NewOpenCVTests): - def test_matxconverter(self): - samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] - - for sample in samples: - img = self.get_sample(sample) - out = testMatxPythonConverter(img) - - -if __name__ == '__main__': - NewOpenCVTests.bootstrap() - - diff --git a/modules/imgproc/src/test_matx_converter.cpp b/modules/imgproc/src/test_matx_converter.cpp deleted file mode 100644 index 96e0ecacaf..0000000000 --- a/modules/imgproc/src/test_matx_converter.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "precomp.hpp" - -namespace cv{ - void testMatxPythonConverter(InputArray _src, OutputArray _dst, const Vec2d& defaultParam){ - printf("%f %f\n", defaultParam[0], defaultParam[1]); - Mat src = _src.getMat(); - src.copyTo(_dst); - } -} diff --git a/modules/python/src2/cv2_convert.hpp b/modules/python/src2/cv2_convert.hpp index 7dc0ffb788..700f29e3c5 100644 --- a/modules/python/src2/cv2_convert.hpp +++ b/modules/python/src2/cv2_convert.hpp @@ -62,8 +62,9 @@ PyObject* pyopencv_from(const T& src) { return PyOpenCV_Converter::from(src); template bool pyopencv_to(PyObject* o, cv::Matx<_Tp, m, n>& mx, const ArgInfo& info) { - if (!o || o == Py_None) + if (!o || o == Py_None) { return true; + } cv::Mat tmp; if (!pyopencv_to(o, tmp, info)) { diff --git a/modules/python/test/test_misc.py b/modules/python/test/test_misc.py index fd21656d83..deabbd25aa 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -736,6 +736,13 @@ class CanUsePurePythonModuleFunction(NewOpenCVTests): res = cv.utils._native.testOverwriteNativeMethod(123) self.assertEqual(res, 123, msg="Failed to call native method implementation") + def test_default_matx_argument(self): + res = cv.utils.dumpVec2i() + self.assertEqual(res, "Vec2i(42, 24)", + msg="Default argument is not properly handled") + res = cv.utils.dumpVec2i((12, 21)) + self.assertEqual(res, "Vec2i(12, 21)") + class SamplesFindFile(NewOpenCVTests): From 5862b5021747e90fcbef31546582297573afaccf Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 1 Dec 2022 15:13:52 +0300 Subject: [PATCH 230/313] videoio: fixed FFmpeg plugin build --- modules/videoio/src/cap.cpp | 25 ---------------------- modules/videoio/src/cap_interface.hpp | 30 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index 691fb9ab38..fa958e2c8f 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -698,29 +698,4 @@ int VideoWriter::fourcc(char c1, char c2, char c3, char c4) return (c1 & 255) + ((c2 & 255) << 8) + ((c3 & 255) << 16) + ((c4 & 255) << 24); } - -void applyMetadataRotation(const IVideoCapture& cap, OutputArray mat) -{ - bool rotation_auto = 0 != cap.getProperty(CAP_PROP_ORIENTATION_AUTO); - int rotation_angle = static_cast(cap.getProperty(CAP_PROP_ORIENTATION_META)); - - if(!rotation_auto || rotation_angle%360 == 0) - { - return; - } - - cv::RotateFlags flag; - if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees - flag = cv::ROTATE_90_CLOCKWISE; - } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees - flag = cv::ROTATE_90_COUNTERCLOCKWISE; - } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees - flag = cv::ROTATE_180; - } else { // Unsupported rotation - return; - } - - cv::rotate(mat, mat, flag); -} - } // namespace cv diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index 52639f3605..d9a1148e90 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -221,8 +221,6 @@ public: virtual int getCaptureDomain() { return CAP_ANY; } // Return the type of the capture object: CAP_DSHOW, etc... }; -void applyMetadataRotation(const IVideoCapture& cap, OutputArray mat); - class IVideoWriter { public: @@ -245,6 +243,34 @@ public: //=================================================== +// Utility + +static inline void applyMetadataRotation(const IVideoCapture& cap, OutputArray mat) +{ + bool rotation_auto = 0 != cap.getProperty(CAP_PROP_ORIENTATION_AUTO); + int rotation_angle = static_cast(cap.getProperty(CAP_PROP_ORIENTATION_META)); + + if(!rotation_auto || rotation_angle%360 == 0) + { + return; + } + + cv::RotateFlags flag; + if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees + flag = cv::ROTATE_90_CLOCKWISE; + } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees + flag = cv::ROTATE_90_COUNTERCLOCKWISE; + } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees + flag = cv::ROTATE_180; + } else { // Unsupported rotation + return; + } + + cv::rotate(mat, mat, flag); +} + +//=================================================== + // Wrapper class LegacyCapture : public IVideoCapture From c55613ccf70b44d4fd0d7e6c424dc52f126a0a89 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 29 Nov 2022 16:41:01 +0300 Subject: [PATCH 231/313] Switch QT UI to icons with Google Material Design. --- modules/highgui/src/files_Qt/Material/106.png | Bin 0 -> 832 bytes modules/highgui/src/files_Qt/Material/107.png | Bin 0 -> 799 bytes modules/highgui/src/files_Qt/Material/19.png | Bin 0 -> 522 bytes modules/highgui/src/files_Qt/Material/23.png | Bin 0 -> 531 bytes modules/highgui/src/files_Qt/Material/24.png | Bin 0 -> 561 bytes modules/highgui/src/files_Qt/Material/27.png | Bin 0 -> 761 bytes modules/highgui/src/files_Qt/Material/28.png | Bin 0 -> 536 bytes modules/highgui/src/files_Qt/Material/38.png | Bin 0 -> 688 bytes modules/highgui/src/files_Qt/Material/43.png | Bin 0 -> 579 bytes modules/highgui/src/files_Qt/Material/61.png | Bin 0 -> 914 bytes modules/highgui/src/files_Qt/Material/7.png | Bin 0 -> 674 bytes modules/highgui/src/files_Qt/Material/LICENSE | 201 ++++++++++++++++++ .../highgui/src/files_Qt/Material/Readme.txt | 1 + modules/highgui/src/files_Qt/Milky/48/1.png | Bin 1113 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/10.png | Bin 2706 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/100.png | Bin 3076 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/101.png | Bin 2763 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/102.png | Bin 2906 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/103.png | Bin 2924 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/104.png | Bin 2912 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/105.png | Bin 2849 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/106.png | Bin 2853 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/107.png | Bin 2830 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/108.png | Bin 3235 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/109.png | Bin 3177 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/11.png | Bin 1876 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/110.png | Bin 3197 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/111.png | Bin 2135 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/112.png | Bin 1326 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/113.png | Bin 3406 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/114.png | Bin 2853 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/115.png | Bin 2596 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/116.png | Bin 2877 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/117.png | Bin 831 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/118.png | Bin 2157 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/119.png | Bin 1786 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/12.png | Bin 3489 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/120.png | Bin 2480 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/121.png | Bin 2568 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/122.png | Bin 2725 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/123.png | Bin 2625 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/124.png | Bin 1913 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/125.png | Bin 2882 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/126.png | Bin 2762 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/127.png | Bin 2475 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/128.png | Bin 2383 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/129.png | Bin 2915 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/13.png | Bin 3016 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/130.png | Bin 2495 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/131.png | Bin 2586 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/14.png | Bin 3708 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/15.png | Bin 2286 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/16.png | Bin 2935 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/17.png | Bin 1828 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/18.png | Bin 2646 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/19.png | Bin 1982 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/2.png | Bin 2550 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/20.png | Bin 2426 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/21.png | Bin 3178 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/22.png | Bin 1661 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/23.png | Bin 1742 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/24.png | Bin 1619 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/25.png | Bin 2310 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/26.png | Bin 2586 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/27.png | Bin 3067 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/28.png | Bin 1718 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/29.png | Bin 3035 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/3.png | Bin 1198 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/30.png | Bin 3255 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/31.png | Bin 4314 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/32.png | Bin 2553 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/33.png | Bin 1971 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/34.png | Bin 1105 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/35.png | Bin 2735 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/36.png | Bin 2588 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/37.png | Bin 2412 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/38.png | Bin 3062 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/39.png | Bin 2413 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/4.png | Bin 2123 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/40.png | Bin 3067 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/41.png | Bin 2728 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/42.png | Bin 2836 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/43.png | Bin 2513 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/44.png | Bin 3079 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/45.png | Bin 2717 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/46.png | Bin 3366 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/47.png | Bin 2270 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/48.png | Bin 2588 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/49.png | Bin 2741 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/5.png | Bin 2641 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/50.png | Bin 2233 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/51.png | Bin 2307 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/52.png | Bin 2373 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/53.png | Bin 2872 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/54.png | Bin 2938 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/55.png | Bin 2867 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/56.png | Bin 2862 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/57.png | Bin 2661 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/58.png | Bin 1916 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/59.png | Bin 2100 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/6.png | Bin 3009 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/60.png | Bin 2957 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/61.png | Bin 3442 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/62.png | Bin 3643 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/63.png | Bin 2553 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/64.png | Bin 2301 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/65.png | Bin 3012 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/66.png | Bin 1921 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/67.png | Bin 2289 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/68.png | Bin 2359 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/69.png | Bin 2011 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/7.png | Bin 2036 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/70.png | Bin 1686 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/71.png | Bin 2391 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/72.png | Bin 2364 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/73.png | Bin 1774 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/74.png | Bin 1718 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/75.png | Bin 1231 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/76.png | Bin 2744 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/77.png | Bin 2741 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/78.png | Bin 2417 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/79.png | Bin 3120 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/8.png | Bin 1750 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/80.png | Bin 2851 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/81.png | Bin 2497 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/82.png | Bin 2336 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/83.png | Bin 2336 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/84.png | Bin 2420 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/85.png | Bin 3064 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/86.png | Bin 2497 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/87.png | Bin 2337 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/88.png | Bin 2072 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/89.png | Bin 2521 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/9.png | Bin 1980 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/90.png | Bin 3165 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/91.png | Bin 2830 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/92.png | Bin 3046 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/93.png | Bin 2901 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/94.png | Bin 3050 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/95.png | Bin 2843 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/96.png | Bin 2982 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/97.png | Bin 3074 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/98.png | Bin 2981 -> 0 bytes modules/highgui/src/files_Qt/Milky/48/99.png | Bin 2956 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/1.png | Bin 1436 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/10.png | Bin 3944 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/100.png | Bin 4091 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/101.png | Bin 3948 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/102.png | Bin 4191 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/103.png | Bin 4089 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/104.png | Bin 4086 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/105.png | Bin 4115 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/106.png | Bin 4255 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/107.png | Bin 4226 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/108.png | Bin 4650 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/109.png | Bin 4579 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/11.png | Bin 4391 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/110.png | Bin 4487 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/111.png | Bin 2999 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/112.png | Bin 1640 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/113.png | Bin 5028 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/114.png | Bin 4080 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/115.png | Bin 3810 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/116.png | Bin 4202 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/117.png | Bin 1201 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/118.png | Bin 3080 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/119.png | Bin 2550 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/12.png | Bin 5272 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/120.png | Bin 3414 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/121.png | Bin 3530 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/122.png | Bin 3942 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/123.png | Bin 3616 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/124.png | Bin 2661 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/125.png | Bin 4129 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/126.png | Bin 3934 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/127.png | Bin 3479 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/128.png | Bin 3399 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/129.png | Bin 4114 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/13.png | Bin 3329 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/130.png | Bin 3646 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/131.png | Bin 3379 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/14.png | Bin 5293 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/15.png | Bin 2593 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/16.png | Bin 4376 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/17.png | Bin 2548 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/18.png | Bin 3701 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/19.png | Bin 2719 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/2.png | Bin 3725 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/20.png | Bin 4659 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/21.png | Bin 2321 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/22.png | Bin 3399 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/23.png | Bin 2562 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/24.png | Bin 2357 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/25.png | Bin 3364 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/26.png | Bin 3615 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/27.png | Bin 4494 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/28.png | Bin 2499 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/29.png | Bin 4429 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/3.png | Bin 1886 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/30.png | Bin 4728 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/31.png | Bin 6434 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/32.png | Bin 3741 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/33.png | Bin 2791 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/34.png | Bin 1750 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/35.png | Bin 4105 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/36.png | Bin 3951 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/37.png | Bin 3189 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/38.png | Bin 4678 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/39.png | Bin 3264 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/4.png | Bin 3134 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/40.png | Bin 4555 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/41.png | Bin 4024 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/42.png | Bin 4139 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/43.png | Bin 3763 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/44.png | Bin 4490 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/45.png | Bin 4135 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/46.png | Bin 5031 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/47.png | Bin 3221 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/48.png | Bin 3797 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/49.png | Bin 4077 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/5.png | Bin 3809 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/50.png | Bin 3326 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/51.png | Bin 3192 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/52.png | Bin 3513 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/53.png | Bin 4288 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/54.png | Bin 4316 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/55.png | Bin 4140 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/56.png | Bin 4092 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/57.png | Bin 3899 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/58.png | Bin 2696 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/59.png | Bin 2688 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/6.png | Bin 4345 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/60.png | Bin 4302 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/61.png | Bin 5241 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/62.png | Bin 5416 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/63.png | Bin 3752 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/64.png | Bin 3426 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/65.png | Bin 4367 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/66.png | Bin 2793 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/67.png | Bin 3232 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/68.png | Bin 3267 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/69.png | Bin 3021 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/7.png | Bin 2862 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/70.png | Bin 2371 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/71.png | Bin 3305 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/72.png | Bin 3262 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/73.png | Bin 2427 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/74.png | Bin 2473 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/75.png | Bin 1800 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/76.png | Bin 3767 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/77.png | Bin 3874 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/78.png | Bin 3277 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/79.png | Bin 4376 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/8.png | Bin 2222 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/80.png | Bin 4074 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/81.png | Bin 3476 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/82.png | Bin 3288 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/83.png | Bin 3323 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/84.png | Bin 3475 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/85.png | Bin 4341 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/86.png | Bin 3507 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/87.png | Bin 3289 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/88.png | Bin 2916 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/89.png | Bin 3575 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/9.png | Bin 2685 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/90.png | Bin 4516 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/91.png | Bin 3865 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/92.png | Bin 4331 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/93.png | Bin 4101 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/94.png | Bin 4313 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/95.png | Bin 3983 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/96.png | Bin 4283 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/97.png | Bin 4290 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/98.png | Bin 3951 -> 0 bytes modules/highgui/src/files_Qt/Milky/64/99.png | Bin 3922 -> 0 bytes modules/highgui/src/files_Qt/Milky/README.txt | 19 -- .../src/files_Qt/stylesheet_trackbar.qss | 58 ----- modules/highgui/src/window_QT.cpp | 2 - modules/highgui/src/window_QT.qrc | 23 +- 279 files changed, 213 insertions(+), 91 deletions(-) create mode 100644 modules/highgui/src/files_Qt/Material/106.png create mode 100644 modules/highgui/src/files_Qt/Material/107.png create mode 100644 modules/highgui/src/files_Qt/Material/19.png create mode 100644 modules/highgui/src/files_Qt/Material/23.png create mode 100644 modules/highgui/src/files_Qt/Material/24.png create mode 100644 modules/highgui/src/files_Qt/Material/27.png create mode 100644 modules/highgui/src/files_Qt/Material/28.png create mode 100644 modules/highgui/src/files_Qt/Material/38.png create mode 100644 modules/highgui/src/files_Qt/Material/43.png create mode 100644 modules/highgui/src/files_Qt/Material/61.png create mode 100644 modules/highgui/src/files_Qt/Material/7.png create mode 100644 modules/highgui/src/files_Qt/Material/LICENSE create mode 100644 modules/highgui/src/files_Qt/Material/Readme.txt delete mode 100644 modules/highgui/src/files_Qt/Milky/48/1.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/10.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/100.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/101.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/102.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/103.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/104.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/105.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/106.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/107.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/108.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/109.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/11.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/110.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/111.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/112.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/113.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/114.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/115.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/116.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/117.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/118.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/119.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/12.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/120.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/121.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/122.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/123.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/124.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/125.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/126.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/127.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/128.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/129.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/13.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/130.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/131.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/14.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/15.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/16.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/17.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/18.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/19.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/2.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/20.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/21.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/22.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/23.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/24.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/25.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/26.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/27.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/28.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/29.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/3.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/30.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/31.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/32.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/33.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/34.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/35.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/36.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/37.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/38.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/39.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/4.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/40.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/41.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/42.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/43.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/44.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/45.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/46.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/47.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/48.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/49.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/5.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/50.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/51.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/52.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/53.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/54.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/55.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/56.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/57.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/58.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/59.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/6.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/60.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/61.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/62.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/63.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/64.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/65.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/66.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/67.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/68.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/69.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/7.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/70.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/71.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/72.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/73.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/74.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/75.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/76.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/77.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/78.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/79.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/8.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/80.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/81.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/82.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/83.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/84.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/85.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/86.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/87.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/88.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/89.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/9.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/90.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/91.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/92.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/93.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/94.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/95.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/96.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/97.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/98.png delete mode 100644 modules/highgui/src/files_Qt/Milky/48/99.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/1.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/10.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/100.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/101.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/102.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/103.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/104.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/105.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/106.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/107.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/108.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/109.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/11.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/110.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/111.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/112.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/113.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/114.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/115.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/116.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/117.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/118.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/119.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/12.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/120.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/121.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/122.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/123.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/124.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/125.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/126.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/127.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/128.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/129.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/13.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/130.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/131.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/14.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/15.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/16.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/17.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/18.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/19.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/2.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/20.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/21.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/22.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/23.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/24.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/25.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/26.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/27.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/28.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/29.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/3.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/30.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/31.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/32.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/33.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/34.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/35.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/36.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/37.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/38.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/39.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/4.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/40.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/41.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/42.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/43.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/44.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/45.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/46.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/47.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/48.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/49.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/5.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/50.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/51.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/52.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/53.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/54.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/55.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/56.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/57.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/58.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/59.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/6.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/60.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/61.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/62.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/63.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/64.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/65.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/66.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/67.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/68.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/69.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/7.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/70.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/71.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/72.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/73.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/74.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/75.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/76.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/77.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/78.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/79.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/8.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/80.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/81.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/82.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/83.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/84.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/85.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/86.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/87.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/88.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/89.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/9.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/90.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/91.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/92.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/93.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/94.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/95.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/96.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/97.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/98.png delete mode 100644 modules/highgui/src/files_Qt/Milky/64/99.png delete mode 100644 modules/highgui/src/files_Qt/Milky/README.txt delete mode 100644 modules/highgui/src/files_Qt/stylesheet_trackbar.qss diff --git a/modules/highgui/src/files_Qt/Material/106.png b/modules/highgui/src/files_Qt/Material/106.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c3a11d18a7e1b489736a9e6624eec3635d195a GIT binary patch literal 832 zcmV-G1Hb%-_zxNej33Q+xNC(n^lwb#91<-+ZpjIGOpjLn#NC(_b4!9hTT*9A(d2bGXl4B;j z$%YqaRF65+Wv=q;T;eB?=6o?9%p3D;+Sy~inVDrwn7&3kg%$|aai(y#Sg*f?hES-hv$Kz3c z4g*~VLxwg?s@ydJa-VAEQeVZ*C;|oK!UV`8i0_K;RU<2>O<$uAXk8Xd;Q4%3wlSH+ zp68VjKv6Wgw~j`W_;$OAEAqS=g(g_9*W!vThddv;z{miPuDC)x7cpuBq))LsQUViJl^VRg*4C1iG;1hVQkZEX0-Lyjs<62M5I2`i=$68|=Z4K@GoFE} z2>=4Ixhv8KgfZ-y@8Nuiy3UVbL$VB&1fEfsd zKoqOLiqrrmx*KSdf<2n}yn&ugjANij6XO}^$;7w@dN47*fsQ8ZIS-Rf;Q7L!5478L zxB(M};k-EA_^kXJm=d_QhY6Hge3X}XB<2M@V69&8)se*td-f~)kYg2+;#bqq*> zz%rqAj7S5Y>^%6!JpFY#56nmnGbfwo7fou0000< KMNUMnLSTZ|eQ(+T literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/107.png b/modules/highgui/src/files_Qt/Material/107.png new file mode 100644 index 0000000000000000000000000000000000000000..77088f3d1d195f15c10f16cd33591159ecd27ab1 GIT binary patch literal 799 zcmV+)1K|9LP){ zsxS~8-+2X40v%`vO9#?{l%N%W6+j2tfm(sQ3e*a)1L=U5%Mv!r;S(T%aL>7`oF#H^ zW-^mCyP|qnj1J>h@oEX20%^fFtR|0Q52OAZ_D{E^2&uMs4{>)&En@~Cdlu8k<>(X|+Z5)TPdN>@)>*w=XzK4M^lffZn zfY)Z_C4&u!QGl5~BCv_m)P>~@ zfOxoQqH9A}_jki?x0}yE)dT>6*xVsPs4O6iAFo>~<@jA&egiSe@lGY|@aXjW}aN`r~<28N`dLKD*)P;6oz0}4&dXF!pOc?~Eq zF~5P4Cd@ex6HQ{9W9ftZ$y7(eh;gNX;* zQu)u}EigP)abI)V=k${-n=H)3?(7)cw$X-}~f9x7KYfYpJUY>~yyP)YNzlKDH2ct@k;+vJ`Zy z(Nv(c9CoSLPyLtPBp~*!4JPjfKh@Uey;$N1V8){0R{RaK2+Z^bBp?0F&19C@RRUm@Fb37nlv;Otxi zXXg?)JO4tUA$QYU0w<@!BBgz2J~lSRCs2^PDX_p?lZ_MGrj}Ea$N}Ja70u$(zL3yL z63alBnvx-k?40<V!Z M07*qoM6N<$f**|FY5)KL literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/23.png b/modules/highgui/src/files_Qt/Material/23.png new file mode 100644 index 0000000000000000000000000000000000000000..2c52059bd2599ae11ce4372fa6180ab583dfbcb4 GIT binary patch literal 531 zcmV+u0_^>XP)X0=3YB9WT%G*K}|09!WSNf-ScA^KJzr_ zk<&Rq?|IlQ^E_3TiR4D%X*^RhKtkba1b8KoilP_|)Sa820Ao}KAAZFD5q(VA?O(K% zQbX5u!(1Q;&JNi#k`NdGK?odx5Cj%L00IvHhL8aOLdXGtAY=gm5b^+)A>061gm4F7 z1LB1YD$A14YTK6LI9~NxRn@8W4+&lB%#pZ|$BLrJxQuZKojr{I(e-+zJkMACo*lm4 zO=2DVFWU$@ygL9(;oSgO3@;C0IlL?YAiNv^D7*{+FgzXr93BfG5FQ606n+GN)Wd|r zkFOSc1ONvHhiCVEH_GKq>zdz50w0Q=+|-2GI})z>fu^Bw)o{bLCLasi#_>NVNdpMH zbVdt>G#5(RQR!LGE!SjFMt&B)d7l2N&a;l*TkygOeV1Wx@n(T1(AgIp8QuxBduuHzq!!u8V zs+@@co#$a?#(5gQO(Y3~r}3W>0a6H8Bftj`s4PnuhG8_eiF84L1j5xg2lhII~B^(RKG|{`R1MWz?9M1C`D23q}WB1QhRVn*x zk3bYf_crdB@<`0Xi({*#U4QZ;Fo@yv*cMG6{AP7GKIIoAvAc(?acADab?Zwo+pM`In zr~j(+DAj!n?3}`188(4q9=Ac`qc-j2hjZ31Tt`3Y-E@kY00000NkvXXu0mjfGQ#Fz literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/27.png b/modules/highgui/src/files_Qt/Material/27.png new file mode 100644 index 0000000000000000000000000000000000000000..5d37706030c1cb72436a5fefbc8ac401e5a0a288 GIT binary patch literal 761 zcmV!RMumZ3G?Le(Seg)bAcAy>b^Kx+GAt8Z42s8I)o}9VW z>e9rIE)_SXMS~FXadiicg7pzKgN^lhv$qB#>_INjK0KoiY#zc#z5qJV4<@j zMrG%HOC7D_{8d0|xwBP7o;n}75|_pYK2t(~N8<$-@K?ohwOUb{rj%t_ue@Hb4frR5J|*nurK3(CrLu*^O>H{C-nixacHqv&~!TG?>Czb z#c^Es5$;H>N_I>6h~O%-?TVMnh4;xqR>i*Fpul>)HYNal6_D9}kRtjD8{NK?1&DyS zMa<$l1hfQrp2w$FOnr~XgMuI+)6f!t+39uU!!YEcOro^_gpKREZ|zlWeWJC1`~6;- zY-l%$)&k)CcDw!2w%hIa!_FlBkOuISJe^KF?lJPFod>i80C46j4BJs zR_s9FeV<#4v)SxBvC)?V+>w+dDp|pjnFjil`IY-lR|P{pHy5|IPatF)Y-3&^AXYTC zkuheWgvN^mFGye;54M%!zrcJ14wvau31iBIkOT-VbF_-JXHI}mb{>2qPk)@w0}~Ph r#vXkg!@4xg&uu{b)F#RKwgmVOnlu(u{=eJ}00000NkvXXu0mjfZEIG8 literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/28.png b/modules/highgui/src/files_Qt/Material/28.png new file mode 100644 index 0000000000000000000000000000000000000000..199f74364ba78c3b90fb438a930d3824c69b201f GIT binary patch literal 536 zcmV+z0_XjSP)?t!UlQFt2elnfA4xEcW7$&#`x8*EW`UbJN48Wq9&NxW`jA7gg&4=p)7 ztFCLC4LR8GlY2{qMSt%CaOElJ~nNtt0PRs(cp}T@*!xtT)vL(AT2C_ljpll#cAS1W|I6=FCx`9rhOrV_rHjoX{d@1;N72#q7#Om+8PZ5LM z<LevBQz{j<*iIi77}}=jn(>i^#$MCM zZ{tHt4Q*0Tbs)9e*g9dHx-W$%9^D@*Qo;eB?ic02o6Z%k>rxa&l%^>OAvzP2cf6#; z?025$32C_K1Ex)>L&ad=xQkksPN?HN&uPEktM?{Q-uHcVyxDBd<%1wlt%=V#4^XcB zeaCT#f%-9)w_zBbYv*t{2zhhu>!g9_^Sj+nokNS3=T@tgZVQMPVltb}eq5F{Hl0pe zYrZeo4wT&>NfOGkjBMeRG&!A4TIG+&BP|vSb=-yH+79Rf2cWKCcOhH3dj~r2Lf62! zSg+TAj%I^7fJx8<+74j$EJLbkMxznwg87=YauWOnmJygsi3c3Si0Rwymbzdvnb3GV zCS6#Irjq4yc}x1oQm2bKTY0=OfP_P?@u>@L38R43*O0{YMK#G`h*uEVR3FIYx{&E6 z+X41O2GD&F<}e%AfiQ?%w+ZCGO<}m2H*+x-2IDwxtzncgdCzAU9h#>DL-Q{ju;U5U zZRAe}1}1|82^oTusFzQo4e^B+%>Bk@#`%0+yN7@^4!$#aufle+*r3IilPu>P`Tp1$U9P% zjRryRWH?C@A+Nx_?0i0}3{Y(FH~w8PNMrCkkE;gX_u;xO>bk~$zen4)w;@Hth>ldA zWf|K}p69q;uUxg7KG=s=9LM5ShQlRIQ}HU)FZhYfU#g~Q#1d$@$g?g^r;}I$^*h5K zAE4)Qx!lVh$F&Nmy}oTZbnfMxw=b-m)d%>-Ldvqd-BDDrw5`Pfj>jWRz|H~P*Z{h* z0d!*n=*9-njSZk18$dTUfNpF6-Pi!Sp+EJsnKoeWE z7_g?f5MhGIFr#Biz!1R9jc=N#PrLKP2GdYwUy2@!F7&)^L!wu0vQF)=;x8hXF(XY# RE-wH8002ovPDHLkV1j5m0et`f literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/61.png b/modules/highgui/src/files_Qt/Material/61.png new file mode 100644 index 0000000000000000000000000000000000000000..50ff1209b301a4546a5c7e4a800ba36ab2b6de7b GIT binary patch literal 914 zcmV;D18w|?P)xj%h#k;@Sb=t+9Y_ai1=@kU3e*a)1MPsFn?u+Q?zvo9&Mf~~4!(^;mK#gfFiBY=hwZd(fT1o~nv7pI&{&hK zi^fL{HPj}dN+4~xu{uGV_P%l^eQF;>rUZe2+6zHoNHrn|0y-X#bi3Us%QEqa|D!0P zUaxeS8F%Ps{`Xy)=sXNVml|EKS4xsZ+kU^_3j(&blgUJUdO|2VixwN6v8OU0Y&IKh zAE(plp@Y7lw_2^TIF7S4O@BQbjYeex*{!I4!7Buo%jM%ak3z!(W^N>;UTb`{Mp!Ffg{|(V+Q!F0L_^bW>4lpGzd*B_DlNc+NlC zbD&yvF)|XET0Z@MQN@7gCC=xwRrh|sKSob&XG7%#RKP%Abh}+Iy~+uw@!(3s@VfN; z<#Hh(R89cXMtU9rYAaUL>GW~!8^A!NK}>}di8RVywx(L7?GpW0PXMA8GD!sep>7n5 zBy@GP;-ds$5Z92*7bQs4LqH0aCGfti?(jY@=0uHv!RknPlBkz}&q$RaQEMIW0Wubr z5whs5OuI;hG_YGj_{A zVZ-WmsyZHzi36FcMu|gSvDr?^5$V`)d;+e6u-tY!xd01fY2u4*ulX+#w!q+`L{vbVVqrxFtQuyhPGWK(qGrDMMw~t!Z+FNHCSfFU701kN^e=cm3|39uV<= zz@Pj99F*e<`9g2Sw|k>!a4vKdItYCT?dtwG5IPCvmMIeoHL>H}f`Ks)gry*zfnq^Sm=fQG}Mf@gsxc zT3fAF`7;nE+T=mzph$))i#$T)cswFaQ<%hcT?D~TXA;LT7K=rLJ;>2o?eYrGgsyEH zg<<&UndsMm0Tay)JVKUbrcKyk-~ki%7$`HLi;v@gRjk)*Y&M(P?X5G=l))Q#MGI!L z8QO9_pJ9?#26&y?!ha6*&A)-ZX_dq}I0>51=NOO2a`|vLV7Xk%lDrEVml7(8p!3t| z^rwX2I&Q1857Eo@Q%dm2Z?{`HQMrffI5jQX_6SA|_`WYqRKegyu?CYEH=tuQ8o?w+ z4b&+UE|&{TV${HHr@sSk`o@4!0}M_TAiL8#CYxYYBmVLcQKvtm1Y+bR0+3RQB5$6t#vJ{`0=5QGYRZe5x9W zBk`-adKNrz{BMncJq;wNQ6XBYgh`xYxTO4G0^9gtyVmu8B^W@!_X=^xka8hL1+is; zmMMXT5OzNJW}g0fI!{!?92@)iehllOpVw_DzG{;e`ZHtt0_NybZiV^U`v3p{07*qo IM6N<$f{Rxt(EtDd literal 0 HcmV?d00001 diff --git a/modules/highgui/src/files_Qt/Material/LICENSE b/modules/highgui/src/files_Qt/Material/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/modules/highgui/src/files_Qt/Material/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/modules/highgui/src/files_Qt/Material/Readme.txt b/modules/highgui/src/files_Qt/Material/Readme.txt new file mode 100644 index 0000000000..8d7382be28 --- /dev/null +++ b/modules/highgui/src/files_Qt/Material/Readme.txt @@ -0,0 +1 @@ +Icons for Google Material Design: https://github.com/google/material-design-icons/ \ No newline at end of file diff --git a/modules/highgui/src/files_Qt/Milky/48/1.png b/modules/highgui/src/files_Qt/Milky/48/1.png deleted file mode 100644 index 69b4dee0a5308074316febdd3e8b89297b202685..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1113 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 RHnso&002ovPDHLkV1k9?MsNTC diff --git a/modules/highgui/src/files_Qt/Milky/48/102.png b/modules/highgui/src/files_Qt/Milky/48/102.png deleted file mode 100644 index cb6bb1d1bda446b11a1f9ea70ece7ec931a89dc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2906 zcmV-g3#IglP)s=!g(G`$c)z185*MiptpQ7W3G z0dFq}@<(HZkX8tY3+AxE>-Boix9{lp&AaBkT@w@RR(baLUGrvszwerFW-Mr$1|Q`^ zAOeCQjBBI&dLC&TYT2NA5eSpcB}8(>fyqb<-_+YEkh2x_aFI070-ash@i*I{S`OXdb- z3n_d`=BxE0hJ9aObG-Y1Lf~5`R>cwcUWD96z+0OZ!}KZhVFnfgT#zydHV>+*IBiw* zAh94LCki4Yk<6tVZ$tmBe?c}sL}dC9OV_S-uXj%vfj!5U??LdpsrJH_Rq(NC%fN%c zvc+M@zF8#e;qIQog0 zAb5_T)ZRi&^mwg$hqz@ z6WI3h>?mUBL1&AvjCZi;aWj3z04%cl4)AW^);&0Y-%bk_2|DA?6eK|oo;dp(W>)xY z(Z0{0?{yOpE2{=};}`LTEh_;v0M~~uFtbrq8B58yE`e;E&3CeD)KTBo#ut%6+T}K&#tp{hdVdWi7wXJZ^e8iNhEe`J=hxK@p}22 zrw+7qXsQ;U*|-2l)DcMMZdo=jTDC9h4s7^==>w+KFKd1jTI-jvyIAdFc<0({Mx^20 znG2<5j?DQ27QztlRl%I;OHiQilY~hNofS2J;5Sn!o6$IrG1ny~aT+irtvGF&q$UbJ zXq)wz1)dI7Q*@KEG$3)`Nrpxxr`d6Bn;N?{d>Js5Llfp%ssj42WYiHngFEa}3}kkf{yT z&_YQr!U$?&YW*y@dh;TE(?OYyg8fam<7Yx0mj?{Z-h_L8oX>0Q>tU|z}zRx zz*`znOTgLB?M=uXPO+FIGKC==t*1aZ5P@tiwYiLdiqa1T!YnznZJ)D$AZ>8AkIx!v znqlL8yHEq+iX-{d$XvL8`a=#4*h7L-Bg2IOICt%o1=uE|lSqzS!%UzKqBRYW$);jY z{A>o9QJctc>-zo;nW$=?kE8 z;CU_Nr8HE9A|z~*0?qom8G*<4PisR0KpMyu(@gEm;@v)AkhpK!8d$&JYvVq@^@;)3 zw+_0J8Koc;sv%($frXX{sfrpSfL@Oe^ZFr61ERxu_3O1C{|YRLeP%)?=z6r191hHr zp!tH&6TspJsZ@wt1n`3x*`??eL6IaE0o!yI&saZU;B^9WIRUTJp~0y&C+zp9ZT3vl|< z?^)DoU-ayRX@CUB4|diNVuaq3Qe+xHp(n~rD3eX=AJV478_S@<$diVAmQhNb3qq`U}-{s-k*FAUO2H4 zo>=}9xVN_QV0GrwVR#G6D3d0~F_`IxWacWDW^k(*nttu~1$7^l6{_384)%Vo zs}AznJOrwJjI|)>HfT;4-Go@DBUfPG8;`=a)i1-ex^fap1aloa&Ia%7b+YNj2_S|3+URpl}g>ph718&NoY0i`zfBpUd z;4~cu`-efwl~UM8^Wkp~r^?oa+MlUu!}>kdbs>mMse)og(!&qu!1f{*4_bgbS)kWk z-fJ(JIvv|26RrtE0YhgJ3Ex)U`6qTIIFfQ6BK=0v|Hz?DP3Bd@GPazm)Q9Zr$~V?`KjUxJS0(tKl7Z+FNCs z5{53`_ywU{;==8Bm0Y0R%Z=P0gM=uG;0tfH9QfE6XqB8%>NL^2(-76&i6({!Pd+&IEVC~j`MIwqM z!Vx4gh>rmv_=V~Ldg+S%9{`>x^Nj0JxfR6K4v=kbP>`wv!>ascuuHPDDr}!D>JA4{yM$r`w9=(ux7zqo-a3TFT3iqud71MvVl1+WF=$<-W0N@h*1i zA9!3OA|2Q>5s8_O;179$NRdOjU4YDk0)SFd%{LTbXe+hr)UTAjJ2yjZeE!*EMW;vv z+OfsusDT)5PmPjRqzwC}*;JzEfSMSWfA;t&AO96#0E6)I!A2mk;8 diff --git a/modules/highgui/src/files_Qt/Milky/48/103.png b/modules/highgui/src/files_Qt/Milky/48/103.png deleted file mode 100644 index bf0c6617b0bd516a2f5801117f024d2ebc0c85e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2924 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 W1R4{YCCTgpt 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 KMNUMnLSTZygot_o diff --git a/modules/highgui/src/files_Qt/Milky/48/105.png b/modules/highgui/src/files_Qt/Milky/48/105.png deleted file mode 100644 index c0848cb6ca6020c8b1aaaedc0a8aac021e4c2d0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2849 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 P00000NkvXXu0mjf@O9gt diff --git a/modules/highgui/src/files_Qt/Milky/48/11.png b/modules/highgui/src/files_Qt/Milky/48/11.png deleted file mode 100644 index 565ad498e9de6f6331f4318e38f04d6e0ce62d71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1876 zcmV-a2dnsrP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkU+DSw~RA}DqnrUcLSro_Hru*Wn-S>2{n+s2It)dYMqPSEL7etMM3Klg8B8cFE zD30Rt(JwQB0b!g$!(gpbwT*4kPIc7R+KO$}rm51pjD7P(Q9;g}^SD=D)3nV?lhhdu z{AqZ3_ub$B+;h&oPYeQpp#QRh{uc(u^JBGIh1%L$lg(zcG&VMZ-EN0lw{A&wb#?mY z=4PWLNft?JvEILb-}3O`LxIM869yU@8U%zjBNRM&@FLv_(9zKWj~_pVM~@!CvuDrXl?wi<<#+h? zw=4Mlu65iEAiS}&vlCvtcmZ8qT?#Zso(zzYfq?<|``x=C@QqD>lDY9UK=1@jQNMTZ zo`ktyQD0xLZ)47QWlzfR>gP)3_NxU}JA@FFb$#9MFiFuHCtF$KY@{ z=&K1GZLYjirDK&gXW$4J+(fK8M}p1^zsy_}z)YmxO>Dzyh^Ua3s@9y<=5c}yeC;a3CI)zwC<0mpSbe0dXjgg1{i zhhGigsZqRkt;(yk!6UQPlR5lmAT%^o{P8EV>~QpGhNrU;p8th$GeGbtdDW^_@D8E1 zz$3V$si{fubS|G8;P5#)IdJ;)X=rO}hXGmk2;OdQlyR>Rwan*J0~|g(I~z`%H~|+f zT!4!gFX9_k2Xu6HjRJ3%vDJ!N=k%EY4xg2k1xJq_g)?W)z`1kh;J|?ckdcuA*Q%=E z#ve6q;cYg%j7JO6n|Xa=fWv2IX2PLEhv4MNlW^+PDcHAfAH>GS0{#YqAi&wP=b;r} z6F+|ZsDii2_%0xN8@MqW;P4b>iuSQ%$Kd$!Cz!OrPft%*qD|31eE6{PJtih*5PW!eI82!`1?JD64?A}3fL|_OhAUSs6=Q80 zUK>Ct0VPF8N5hsaTa@S@Ja`cH@87TFygT@^vN9+sDdE?RzG&HA8BoJ-+_+JRIDwl? zCZN@Dg`YfmGR&Jd4~mP6eF{Go0}A}Qb?adF?%l9=?_Q{^tc0|*v?1`NrKK=;?pzKp zqbh=Y_|a?t0n7xqV#NyBxpOD%+O!2Qv}qHhq@)aj zr_>_&88c=;K|z6E;XO9M+hu-!K5W>qK`Gqz>(@haang&&$hGIu7l& z)TPLHApV(hL<0z6BESU;7Qpi5%VFipm5`g8I|!c4(b1VwCm|srpztFaKnOjh0ky&< zOP0XWrAvoIo{kH2`Xp2A++z+T{AdQO6w$?t7eje@IZT^2ZAigV@29grrI1di`x%Et zpdy)~n4;Z?{}#!{grmY}T9ghIf(mBRZU6xd5fKqe%UrZ*kDhf*Y+MF=P6{(drs7qGqS=ZKGBd;g zl;(-WVJlT&#lDGr)Y*Bvc`)2vx#V%KjF!>-2j8 zyFXWDW;i2pY*ivz$|)w?88A?KP}2U zVQT>M|Gdi^l@-B_tUH9VC5%vYnOL^O6Iit3nI@`=J3&>f6IJ){!sRIv?NYpow+DQ({X O0000)dm`-}gS}L`x(R^ld)e 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 j*?}K5fN%5hUjYUH&rB^UNDd}l9SlE zwMlEYNl1aw69j`HO?9+Qnz)Ee({9>OT@VF^Sd#r>moAOQBnCek2Adc#5y6J4B51oI zq>Ud*r;YxohRntmMv^uSAx+yjKJo4N-1mORmpXQx91mMHulm-t{e9ngp7;J)wLlH! zPcu)l7K>#>*Y$~e_B_tl;u%ye30Nt5YFEd=n5L`8YxRtp5qR^1@saJ`>o~WAg)U!z z5Z{MBGX##k{}a|uJI0zDI$?Qj7MeYsKv}F~pBVz0rjG1ry^+1Vz6h!+Lo~4h7)t)5 z;U9(Sp1|+_@k5rexrQ5EK8OnuP&CUgXb# zx1lTe(&>Z2x+I`#^02S53oap0MUzT268jkZZ8zaMWq;ifcEUkVBGJ-9q*>oj~$KM(ZTCTk1a8x!Dv8~A;yv(0BLd2|)Qp>vA zLr=W@<@4md1u`e=pp&sXoLOajw(!$C-zn>*Jf1@*??5mX0xDOd_mRJvJslivWmvM% z*Vvuk*yL^no7G|bn~VwnIzNq7CTW@gnNs_^?tqjS+wi%piV#a(CQJ-TNpTReYtX~f z*=47YC?Pp*`SJ13hwQX-48i$XcN+q00~^gCIL25R7uJj#PK3wqnvpFIB4J!@>-0m;I z%+hHdN0PxK-S_4yR+evK2Ru+zOtLeGCI|> z?Pe|#y8tVZvl|X90aO;L>6IJE6^f7D4m&#cz=w;c!-^^%c=o=z@KqaGv3!4W7mLq3 z(${@!NRrbqzx2LI^OgszSHvDWymtp`7tDVWM)1<$ulLXMWjk>>dE|G2mpgpBLQcB} z<`%G?&32hCDFqh!n)u-JJ2(g9p6YD#v$J#mG+}tXB4Rx~pM#|f zvpkm0!51DnAFld+;Khd*!JQ2l`rx$3pW~*0JH#Aez3pKw-E!9*iMv60U7JS77 z?tiu`XtUW_o{wY!UIA)=(5|9IAoZN)Noj@>1CVp=jKhEbb6s3b2;f74HpT|Yc%lTT zu9j+slvb*0pt=VM@{DMHNj??x5f$nZh;N>UmWSv6N5^$IGePR>_BmPJC~Ui@UD zss!$Px}CK#G?z>y4VRbeoR#$ez2qYyl?IBYf>jS843nUhwm>>5Zj9-oB-J;>d{MZR zgi>kP39&Tx0tyY*5v-~Q5ZpcsPNrf~&RWu(=}mhmd#0F>z|-tB@{Qbit4V-U<;-Yn zc06!4G2m))KypohGy=H)kD1PKV@=Catc<|dV-!RI!}#x5oo91V6q z&Cn2EP3A}E%|;90l5yww=6DNN6#{rDM}`hLt(0w2P@o#F%A{{VXyei^@PUDufxQ4LmJQ!&$}6FINO4K{3uFVOy0D!h?2_hsmOP)!=b9 zI>7FrNdoe?Zv2fW_>vm_`goL#2LoR}#2!YHV~wqDKtdU?MS-MAT5jyvTNMxF5?C+l zpt)ElSIN|R`h+5DfvbTplsA6PA12;O#nRLEtT)I3#UqCzlIK;+13Iu60+~=Dtg#;u zt5a4B)@^YC7aTb8%lNd}g7D@q)`>R;haPVnLF{8#Hd&HiLR`!zxvFGxsydBK*EwPl ztPFq^#VCz(TIR7Tjh}cj7N`V#q2S&L^kx8oPm-K)vrb@KHsd=nC7Jq>z-$sP(2Nxr zj}6fMSY5<%RnY@~d^&OX)BcQd@~H%m=NwQKHGp22B&c01$thcznd|?!(A+uf9Kshe zV4Wg{d;1xoCjaw<$laqZ@;mJiI%FS$JNed;)TXyGOGOv*I+XtjFaQmknFopJ$5Q|R N002ovPDHLkV1mzc3JCxJ diff --git a/modules/highgui/src/files_Qt/Milky/48/112.png b/modules/highgui/src/files_Qt/Milky/48/112.png deleted file mode 100644 index cc91c94caf2c9f739e2f60a318e1eab5dddd8d25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1326 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/114.png b/modules/highgui/src/files_Qt/Milky/48/114.png deleted file mode 100644 index a44368aba22be96596775d534335d8c1631e3d86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2853 zcmV+=3)=LFP)LjMlXR##pszo+6m8uB^sn1x-TQr^H0%%h3PZXYVuT3^OAr+U17LnK^T2@9+EXdv9zbBO~-UHwXUM zY_k(o$)dpk8ZH!D(& z+;=jOy^n+d1BaA%!$juIn@5WlEfS#bXY$_OUb=YkBGuQ|gWR}@jEevRhdq~Ze*@B9 zwrrVUBI5-f+6$PC($2qhxm=!=D_06r33mSO?ru7A_%K~! ziK4f(v{GwJi}-BvZ+(xt}3=)eEn+Y=5R?W==_ zsC4tobmz_;W4^_U7n9rV7FeV}M>Q>s>FjViot_mdRuEqe@MUkmLx&C?d}w6>%%=3^ zS1cqv9uLKHC-78VH(-^heMYUy>RW}alBryC?Uqu{;*>weR=4hVM-|}Db&=|^g6%Y8_tA^p(@3? zwY8PLJ~TGdK|S;nE-_GE3=RxZQgSlQO;0Bd!{naproq7hii(b+GGHaHr*CIv0Y(kzHGiDeA!F-^?V9;r`J?PbW^CppKgzwBgxbkY>}!73HEB zmZ%yJq(W5%mE{!%uIcP!=7p1410HBVN#is}00kNygR3s?;mV&64Gz)l^f{u)yKw$I z-`Ahloml03{OW#HUsp$yr%a~o++3O zn2lxv@#e(U+S(?72+6S2H3kN$2?8L(pY`?kgROAZy1Fakb46IkZfzzi>3*eBB z*?bfU6=BdjJ3DmX%6uZ2kmyXHufLD_`ucv|DK)o$C&K8QQ~uLIZYlF3?0)1Q1BF ziz8AjqDfE)HOb$IM35q~D?&6%7mR0(J}@K9qx(soC(p19NuV*51}c!&l?G6ht$1n$ z-Q`FD0uDnuE{ir6E?g*p+-5TZsD7!5ena{`%RLs7C@}zZ!GZ-MgpQIv&*5-5MU;Yh z<>uuXej|7D%0m%2fG;4nD-uugk`fYzBL`35G?~#)dBp@0Jv}{w2tfK+a{WHN<2A@MTn*UAMYM4URfUO+1b3 z|NMdiQK!If02Y(?bj!}KK?w))C#EBFyzw~0sOLXnpHNC6|p=wMDG60XV7acXfz7E%0ESn>29hzFlLV@oXLI~x+ z+)*>3K*TPfG9M7YV#d7f7`!4ozzlcuswW-`5rD=Z|NP<$tb`~tD^p}=OJS$kEIA-0 zHAR5ceb{Ei@KD)U&%A!&up*4g(CKaViZEOHn_nBb0;Yp-jSb+iYhualeSn1)lqr?% z+$q%QZS#f*sV+ksF4>hwsV!I^x zbOw(1pQ>tcMO~DZmZq;!ITu7lM+?wsINU>R0|$MgAYWV|6f<~a>dPF3m_$VI_j5+} z2KFYAnUN!+qM|}R*HGU;4Gr~dX4lR3!^D#~nk5QNg*r2X)DdmGI$7+zZMoMu*|<+B z<%tbU%7bi*tv=L1_j9mkzK3-&!ZSN-mpqX<@KDc*w3_^Z!dA8U2X5D^y~-F9MHq)95j8{}W&UX1f2Yd5OM}00000NkvXXu0mjf D2n29e diff --git a/modules/highgui/src/files_Qt/Milky/48/115.png b/modules/highgui/src/files_Qt/Milky/48/115.png deleted file mode 100644 index f8226fbea5e745763e375cba15c06164e95139ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2596 zcmV+<3fuLGP) zNE0V%Q_7{<(uxN_iY*!;p#sSRNEK8~0993sphDs~$`cPQN@)QhUT7csP=QF=aua>X z20?)mH6f{>q$shYBu?ze-u2pR@6OCQ{QsHDo|&C>n#8Wuk&frg?ChNH`|tl*3#Al3 z%!k8&gb=It!pWxwN9i;Q!S6+x&Lz68gdb0Adwzamwbo+c3;pLd2%P^`KZD&TEU|~q zV}bz-EfvJC?RQw*6y!Va1F#>%^SQuk~cF{s^s6giea_a_2w*?5h zNarF%INZ4^{N*LM)0g4bYas|t=A|9KsK5Au5cu!cdI>lV!4hMZBT(FEqcD<3uD?Jp zbRc~J<&)6u9IYU=Cahjkij#*munG3yM_~2e0dIaDOYfaU<3A@Mn+2)y&f;wUYAlmM0U>ue0{%IWX8H6OmKQh;f0 zYr*UL2=D8;K>IlnT-)omBfo>JuxB%Be|rP%GAA5c2^i2~u_1Qd`4Nfum!4Q0NP6iP%1vqUgZLNgiG&PTbi?(S2?R)vr zPm|+s#FbZmN6x-jrJykWi9ar%&LUuSRgbTZQe-Ig?+6@qdD@5a6a^M?R6htXJWrmZ zxzdq{2aI`e-?-L886&vYJpUH_>Li9g{{RWXDbe?io_VNH%EFmR;N+*ABg#X$XOp8P zv^?Wccuavz0H80WK&Taghu3sp8f0X2p9IyMJVU9Zg$Jm!#tta7G zteGP#n$Vl~+GDiNSYd;$CBy{UfI|{#u)YCQW&zExcZn6rnBQ^d=Al}PM54sk<&zIO z`??aKwI5@gOu(I&N$HvZv*z7MrEFbxaon^+o0@1{uVG+kUI!EMhwo()IB}0vl1d%O z4_R8$UPES@2>hf5kOrHWXFF~OXfFbc#E3|H3ob3>qJL+BpB?+h$L-M$0`%T}Y}UDc z3;vRffuJKqX3QgUrZ!|o1d>TIa;1>!B{k8SZDR1Q9AqHZeOUzdu=(VBEqF^AvD^TT zZuXxg@fG5Ir`do_BvTV^-A7@)1(6pTYoa3y36u*xmImWDWe^sj+T93Ono0z>EaN`k z;7b*R&Zr_yT!*1FdYuv~&bo2j)N|F@0EKg(2>Qzs#XB6#Pr73r1ZbVIO=U8J*AkAw ztMENk2KNnN%Tv#wwD-|f?`>tHhJ`m@#@y>agj%X~5YQy3WY3o@$O%=d?6x>FS2ck@ z>=dIil8zhpAz`A3rB3r5&;AKLcip!p&AHV(gu#cNq^fiquKwhaWEUDxLh~U4#E$LDRhiB@@R={K8F=&Pz5ii2>$YjTmnmOh|KMssrzB-Kadac;h)HMd z*|qlIqr)ZwiAd#{Q7K8li3qTkI7BIg;WKj3P=C!UlOGrXzg`WZpMgT-3?r0OsYDhr z^1bd^wpw{jzj&>W=ENB+pFfidyY1W{d8lB-1ZY0N7Sn77;Z6IlX-1rxYjZgF#NIU} z(Y$a5Q!hLo+j}}6Fs>#Sn1%_(<@B$%LY4Y#tQ)l8F3LDW$K|lm9u7RXCn%ZC&dP}E z?Rmz17@wJ8l0Iy_^XGOcov|TS+~{Ct!o{_7O?+uWY3ybqt2CN&Y&{`V$rRA08-~>S zKTo83+>B=3>PAMSdH{6p;L(lP`wSBNT%DUMvY4z)e0r&+#@G+I>SOQMYlRNZrld1) zNJ3dENSv0DomIGs75TqvXIo)pTag0Wp$*k`-tZppMX@O_*0c##vTC6dEa*DbK^B2g z;XYMj+(ON>a`}wR@;ULG7Yw@~FgNAFr3QS%0>ynlc>lp0 zZft$~8fk$@EYbqk8dRa$qTSD^wpmFSM37-tvV9Jk`gCOKqJ@wczce>sVd}Dn+Pu_) zee^0OZn&`(9Y5cQqjY!QR_c*Uxo3mS8@4`Oih^57B|+0 zk$^2~sCZkax~OpeiVM%z;d1=(Yq=IYca$~$^wd4~jHJ7kjd-Ags4lP5Vrl^!_!ooV&ln*GdrWLT6`s15$w0M#hN1z?cQ1>4v{ zGMty6v%?abi&s|KY?Ft=+)})O!@mic)(_o38Wy}i$r^c#|FwYM&`VM+ z5K=z#H51}#A})>#iDstmBzsVG^nS;JP}!+H1rFKFr7e1sDLN8q|EQZu7MO0000mMIZeCh;_uZq?)4Xp&ci5`tfo#0ea`zAQ%4Y-KvzC>{w_ z>4?+>sSg9D4%5`YbK|5urKIt1beIR3ZKTDWtmJ!Pr{|Wg@p- z)Hag%LuFJJDA2|ky$#T^McUA47n7CF-FFa;{vzS=zZQXD~|Q@8U!v_g23D_ z!Au(%di6z&zIjlfN)VBjOP(2RpHc!xzZKa^AK$1)7|Q2|5UkLU9Wjv`H!V;CD0CFY z*0~rnY3z`&$vC8f5d`NfL2!N}vV(&d*!x#x`pyV+ZI}LOtYu0FbX^-3Qg04d>nN`c zl09<^JeUmFA)zTqXpU3owZUW}RtO{_V|uPpGYjYT zT=wiSq>k=JS$z$vuD(jlHBj3+ktrz&ynbb{S%42t*D-BQzyfck9S{})iG(xe-f=15 z3L-WcdpidkdUQI%7cRh==ik8i2T2kZU%F&prgKsgBE~zY#mXp5 zW|93HcAx3%B^)Wl7FP=sYkXr_uvuBm6SXZY&z!L@_Wj#(_x(J!C?CXFa~-^9CVS}_t<0D=S(^$$pq&u zLdDg$P*$!*_DmOsp4;ryORR8vzJ5xd?MLN;7zR57$PO8;G}Wf`fddQl2I>w)X9p0O zp;-}!Nug5CtkpN%iJ8}LDH0z`;`A@Sfy{edet%%@&{@9hMpUhO!X?H2$G?rt$(LP) zpB&jEb1N`>d>s9+B!v)`Uvb#z@c95utQC{t>6%>{c%lhp6Y{JfG}6f2O?wa`$rema zGL!sy^a?+C3v9U3XJ)2kUYUV$R9qCyN2|3;A0S*!T8LQ!FjJ;FJUTBN>pI|fCP8Ae zmqd|B^r}^ux#kIFD9rS9IS$NyejvhYb|_1hV4|!xB;MQPBk=D_w0goXpQ#R;A zQc^hU=#l+M{^w~WiO+4xlPF5ReAy~gH><%Xc0Y)0e|PbI10IKWqSCN|s3>JPV*g#J zH@FDUWP^y1!H6bF-@YJ62uS1JZFeAb>WG@eoqNbp7hCt0e|Z&Vt^1u4{KzZMAo1LT zlG$*j7OX5yWlSih!HVRZH>wGU@;JlQnbnhJb3+MqZ@#W1iFtSJLDkAFsQG~!{P4@q zpl|!lo?PG!tHmbDcHy4{Uf`|s5a3G$0Rd2~p5z1tPrXy9aZI6BBC&}`oK%yj`tBAr z_yYv~$qgm*fT_@eF=|?V5{l`Tz`YuX3F!;_!-Uf3z6fKGCgPi}RgzF0L;Ida?-Lu` zfDN}61P(b075u^wX2L8p@)EFAW1G*!Sr)!3Qp+?UgVo$ zml~#t5(G>u8Tg#FU`D1NctIO=YJP|rjf3tWk zwqiBr-LY*_`xmuS?R_sGT(?*qHl=leHy?;PO3bCnnRBL313W2MWi#AboO5n##aCr2 zR#`KyTa61hsln$)lD;`n=hxm}B%$&H6)}NSXdp!d2;V-h208@bG_AUX89DA?w~}=| zQ1$gSsNJ$%ZSQ+`;hh^V#rV5NlqBjN6eJcY!4}NPC~86!gTsA>6L1iB5g?$Q;=)*p zTUB5Ie7-57QPnqBqwZ&F@B@2xq3411Fp>$J+_(axo%@v}>K}R$5z>S|d3hp`*mI)7 zf4Dzq`+(S*lnH$AG!k@A=P(h_oO0gGM<=J3wGo##ch)Gu zpWD3?@q5-QvMSJX|9VR^-a`l@`h~0ATJYopu>+VMHZeMsqu9dRmE|;HYZH=AjGNFk z2P|w7K5Zz#fB4)^ocj4Xf0UBQbpK)<2L7?r_iV7H)?Y$MTF|M;JbfaQ|MqW{1lIH- zLEmf}Bs0pA1cVmy8QQCjc;ijwWVcRl+4{HPSE5*U7c zzkea)XhCdeW~f5$KVz9s+YKKeUUFOIkF!xv-Ki+qTOCz?sjThP`8BFo?&Qw{l`)hr zSc0L~_JKiKI_AYvV#}oPy8*R|FJ6O@L;KvnA4p>nc_K9dbf3)NT%Rb^Fn`qZIyf-QzkJT`-6SiX-Wxf{QR+y*PhbnBz zzul9yY@d#nAD(q@WPR)(I3As4lL*iU=F8uT&ETL^meqwrL7Hgo}U!uEWHKg7P%50O$N<>teDCyMBj2^IeLO2PhH)-Pi zL90#{w%&4UA8()1ANd}h#bQDe)S5`ks3BXO9kCtUTEscd?1_*r(-G&oW&;ld?5cNw zOy&IqN7}FebXIDTDinU)}P4YdYm><)Fsp^$JVogWt9Z~u@d&iA`60-xnW!)N^X bzW@UOK*Vn1)luo100000NkvXXu0mjfOSoop diff --git a/modules/highgui/src/files_Qt/Milky/48/117.png b/modules/highgui/src/files_Qt/Milky/48/117.png deleted file mode 100644 index 54db487ebf841f8edb6f5827b4bf2dd0ee78a675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 831 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/119.png b/modules/highgui/src/files_Qt/Milky/48/119.png deleted file mode 100644 index 683f36620ef1e11e562e287588561cbf8cc03b7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1786 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 e`5(7`3ormi?ARZ<@0Mf$0000g;wt8`@ z5Qns!__H85a62{U8o*|k!HwmO>2@bQ@vWEsJ$+9B{N(fF^=c`;GQP7~kD^e@owtm( z1W3(`!)sIbSIxQ<~)=UQuZP0mo=D z?SFelD)oz3R-VP_iF*bBsUtX_q+BSz(@mgxWBoMp`n;hGSjx!5@PBOYj_UEj%7C~Q zh5?j|(a}40;yd|tlXM)dKqN!}0W7U`k<;mkXMXq2`=Y^*kBuEgp~p3_ZjX)MQvj76 z`=Bto-OxVhHQ|}(UwhviZtOcUTWXZXwS}s^>o~i5_fGtaFYiAV2Eix85T@vQAOedT z0~vD1pZeZ-ZSupO#%P`SS58eYzwzQqzBgM+jXeIf`qqzpZYulzg}=Qpef>|*t>jRS z#uXgTCI zh8h(ZC>0?Zh#@KrK&VDKM~}njBM9OG9%Tp%HHeB^ASw?-T-^q7Z70NoyYajMX?p>B zYwtnQx&i6>0;C(ukZ!I5>$ZR=9pGsfI7?A5hSw5Edp$_IJzzaOPjD_rI`3>IaI@8g z8%rGm^=(XoKNJ9d`qad6ROZPCCx>Cn5DFEcfFvj#81lkM&B-!2k}1Son?+GfnuMDO zBoY@LOwAb+a~c_jUQP!BkaK@z^b9^rfg)v6^R!lkzdL)q1sj{`$~RwVHewmm*^?wp z4Z|8sQt@L__y|H_HacfYcsXj}

4@cj^buT@cIxL4tB2BnfN*xx%AdbZ`&=QUxZC zkix|nTJ5C%qbK(sl>iv8iyu#A5)uMOTnbOx=h^~XX`kf+$YwAf0L`rhBnYNJ*tr#6 z)Jpy#lo<$X(0y870H9=qmJweT+E)i6kn>O-sC^LBk#qtTp&$tQ|bvjibR zBm<>+4Z>1LIx*`9L*LgHAjjxp5r&>E<~$G;0)h}MLb=l%`Z$qC32*gpOkm4sk49ck zAm%C03dnsLrI~A~L!ri&BCM_V;KJL>aOA+SSU6M>g^?wMuxJhD)5mk*tPkWlvQtXC znperm@qrA8)BbFqW`%SN>|t55pWM4`5*i%d1^u z679^}>ZSh9%;P`gYTsPrGT>ZYXhUHjgnF$YDh25rc=y4vAu(ROyds225E$K~k-nI! zb6QN?)7mkIyx;C55Fr&C_ydi>0t{6Pu(-M@?k8$Jvo4bksn;}f2Y`NN0N4Bn%2q4K zuyspG<^>T(l^_wS^O1ww;I)hMF!SyzOzf)3oDhHvvyS$)CA1k_22oRUv(*!0prTxB zBN4xo)joGA#9iuur+w@{;GfN#LLs9g6_{V?2v8;pX~a~>ee9tT`0Ir^A;A98L7ChF zCAg5+&cOv@0C4ONHesWi`T$bjfvpcnff>(tVrp66dS_J#ziV4ru|eKbiHsR@ktn3? zM;{)6zx;g;-oCm9V>_!#>lqL^+Lr)QSVA8t#EGz!1;F~MOqn}#d_Xxf33TaQ622&e z*L2sEGl@(i5{n}Ovc$&^ZHHIS&xsnTUK>#5HCK^STY&cg7(Dz0eDqUOGPkIJw7PH& zg7;p5-eS|1#tiZ)q|WIxgPPy7y#hNLsCi};t*u~63F%-=RC_v!QtkGM!y^L3&fzkQ zG>T4hBU{OA4l5ma3>uGr70Tr@=shPH~X+#P7IC%oRRShZ9C+3=Tsn zJXwLlX2@kGrYLl1Ujwe7X4cw0<;eX2h=9dn5e^W(MLo_9&FB&C8b2ACQ`QidhS25QaWnS%3w?7lIu6xS`aosMlv+hq<$7pi+v(bJS<-+g%g+ zB`drRr+vAA<&@Pq1zw*Mhl(;Z8jZ6IecWQsv4WS4D=}2d1L6+tM)!0bq+fQh%*&2U zPr!1u?*MR}I5BaRnfNanuvP#s90Esd|?Uu0hz!o2-gSlB2t=ZF|*MqacdzuD1}S@D;fz) zWG#>tK5#&A^Y6mV4j%sdH``X`6mzO?&elUd05Fd1QHV;91=P^?MoAQKItp3A-3?yf zi9PxRZchx}&V*fyQm>FyAYn;hW}aN zgIxJDZz}uWk@=ljek8J%u#{SEC(ucQ*_WE_X|p^1nX&4NaR|ru?<~WXvH}r6js=|e z1x_=c-P__WrMTTQWtpw>+fj&V$~aa*MZ;VxrRP5T`)2F30r2UuS{;AkBp%1Ag%BFm z2-NPO5!Q@O;_`P@^0rs>TLH6GWUT=X7yt*Hc6LzUWWm|)3F^>}!-kIm~x-1A;fY@!+%v_JmoSSR2IG+APb9LqZ|CP88 f;J@_wj{pMzLK#@xVbntJ00000NkvXXu0mjfATax{ diff --git a/modules/highgui/src/files_Qt/Milky/48/123.png b/modules/highgui/src/files_Qt/Milky/48/123.png deleted file mode 100644 index aaa02f519df813c754e9858d249f70b89ac4de4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2625 zcmV-H3cmG;P)Y2TrMgv3+Za1pHlCFxTMv@t}8;uP$t z3QQl=XzpomcZ-Z*(ou+$4Mx3EGea=d|rWc-wp`J)6n;o!!T2L7s}N&>skW-lVH%W z0lHV1gBy!)!AfC1%f?T$FJJh@Q}4?Ug~0fCKQ_!xJDcuo+tRtI6KXXTDv90j$o{9G zee(`@nA=?LU6{Oj5-v@@DkH@szxu`pWB(I@za85(%-m%nv#C=`XWD>-!w?FGVEIlC z*5o2At;ld`FKV7*qfG)LXSc{C`(Ri9e)#m(gHWvG;l=Yup|rkwjVuDlPcU)pK-KY{gH5hzs+Ld1v%BHY(F5?n16&w!%V zKrY;YC3yzQm4Zg%Q-dS$%h$ijNsRpF+p}X{SR)N*g~0RCSX4^1CA1mhHz2~E50#s1oauvS%xo6?G^dhWxNkKC@e5yU}Di=`fuTfT$nx8COwddxd2tML%XQisG@pr{b}8jXuT` zz}?kbaBcP?Jt z1~M;~V16zKm8uGx2fLxSFJoAOe@`@$Xe(CWCS?LeVu2@ST)5*?v)1hLRoL1$1ebFc zArJxOiDwU|yZc~060uJF5HC2lv+&Mq&p}H|9=bQSTQ231%iN?yV&>$dJ?AICt*c-kyPh(e2x}yNNyeD98&luzdX$=;~=R+zf^6;;gss;y_&Ap~t8C zwpv<(SZk_z0<~IgYHn^$X22wm@1VGth4x6e9?WK(pyn;EN=BR+EMSdWEiOT8OQLxK z2M->U|ML9 z*i|oNB5)(Bi5Q&dg;dB?4f+T@3t^x-ASDtBsiULA)!@YAaR_(xLTy$t`dO@JAw*kR zM29t;E=0WC-UBB!dgrDI95`@bYV3C}pSt1hUfbYpiCLZJ}T>l7C57D1^|80v3>j`pM;#8l`F zKnPkyXI&G}6L-e&jbc5BF(?5vGA4iHfAQ8ZJTf4$LdIK3qRqaZ!qKLML^tvRtz{~} zCd#kf0)bq}s(z^ZU&tt2_CAOjH`iZ=;CD%cdZ9vlP&df?E(L`T;6hZW13EzJ(aZ^j zOyGdln+Gw^Y(u;n5djO;EgsYY)F@IsRLdm=ZY`Fy)wo_37aO{gx-d5CfX!9V@D)%0 z_iA9Up*!g=6wm_QUwi&=ESZK_N>|iejd!t|EZq1vT=~P#Asi|}S0-Uu$_2b3Xlz`$ zT$35F5?{D*VQ=@wjibDfIk&;VK`5@uu=Mv6&?&|0F6IXug(ikH0NlU=lwzA5a1Hm>d0Kctp>Or*SwTo@T7@)yppfY`N3%^F{ zi~~#i(81oKj09oYu5GAC8$*ir0JEe6dS@JmfV;1h7Ni6Xo`pc7=DH7m8UvbaWerUT ztT6&Y&tQog^aPI95(+j^10F&aqGlm8q_Q^U9EQ=I@iA7Et@Av!zK=Wk0!Duqf(If% zjRs6x00k)5R8Uz%)1HbNW5lS$*gSC0Ohg)5ff~Q)1R@ab`Y@Btn?PYf%xAk$R^YC@ z4*7){P^RC3&+P1iXiK;r#KfsYeY!6ZG-BrklxyaF{@m#lZCZng)8;h9q>h>pF;9 zY8Bl*qSGp1t-kn!_E4$Z0~mp9pdUEC^=fvK0hCJ>RdbU>ECR#ZGVqBVov<+z2lIDg z(}he`s0oI<5o<&ee#{z9YGfU)Abq60+}aD7j`fEgBP z1e~IHo`b04m0%tqFRXDmCxR0|e0sg2X7i<*03K|6>H-S)pK%f)CpR+rZpMWdhdnzmM0nb zANpYo-EVFFnLp1>u?IcQirCc3Y6WU4wUMX;@{0urIR|L!umWERZ{LstrJ}&?Yj^n% z+#?N_Ae;SPiJ$E6mDst**~eq)M2p0Ony*y6oy)=OW2!5Q5b$3{@mdDr?h=VcpvHhN z&&XMpb)IH3Klaf6*+blZ@P(c)v)SeUg7C$BN`>r4D@~|MyWVl0qAFA_=E|3Owqd}h j{2H642>d^7{}o^W)-{BiRBPp$00000NkvXXu0mjfP#o{w diff --git a/modules/highgui/src/files_Qt/Milky/48/124.png b/modules/highgui/src/files_Qt/Milky/48/124.png deleted file mode 100644 index 273400a027a88df7f4bd570d2b59dfe773bf4d9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1913 zcmV-<2Zs2GP)f_9>#-#q#}|py1p~x>wC}n-gmHMG8y`mo0UHCRv=IyP$2M$H;dVqFJ8QG zSuB>jqTdj=E<8inMNg$te~SL&ty{MOXO4gX-UGn#c>I2Gaq*q{`g$rVDk4cBolevG z`Z|Tf;U{HfWxpI}!(4es``qXnV_#q2kST$op`nLD+V=roj1-MVskXM3Dl01q_p*V& z*47pU0s$&1DLJw9a)H(Z*E|b|{?pByHy<7%@cjAn$1oQ~QK+u2E=%GLha-ysp2cD@ zb7J;0U|uxYlb_E4=dZa&oy`sz2&+3E;dh)`A99FxrU-SJ>qG_3Nh69UmV@ zLSx&^)v(su+8QMi2^I$ON<~FQRt~`&kl5bdreH8gdwYAFgGx(F4fDc*BU6?Jv<;vd z8XB0bBzdTi5~{|Wjg1Ww89OKV<>h5w1DZmPF-lS}VSsYG-5e&drq}D``5>$R4d&xo z)qq}20EBV@0L>B>B!;<0J_3m^E-o_obLY-6I9&UDK3Z8>q21kG_63{GMpac+Ot87R znXjSI`T2Rqfc>=^mi1Vc2J`^H5E~2+4SYmnw*Na zDV0_PsRR=P=&M(+vLCdywIN(G39O-YZrO8c01Y;H(r^iTh8@G+0Uka!Iy%ZiN!kLm z@17*5D???@DxSZ#IZyU*17(6eG%_+m*REY-N3E%;;a~!Xg$A_(&(eTVWWaYPO=z%T ztO*;&I5|1V;KR;$6sR1g&)Ns5_tKYSQ-t}6zs=wh{WkhDd6jomyYmH2O-)g6Z!en@ z0s#C1g3#hcx8g&~O5f!~qwmZ*BnC5te*ie7n6&PZ-}x8C5@D(=t);C!FYSgmC>Zin zR6KK+HBvMcp!Jg9S$l9|m<==`D+K*MiuT4V10N@m!7)J^1TX?T=N0?k6iEg|1!XcEh81%4b;g0mw0 zVl(_6(qz9NkqeS>E`evgW3;mQ4;2dnsZ^Xo(V!qALivH5BIHw(EAj=_!#apYTFX!g zNG;?7sxPX{=4dxS2O%bqURMF~ePoaOe+#_-z%tQH;Ltiorh3SP@{#ya=7h~4F#yM0 zL?ZTyWGzK3K2mIUA>Fuc(f9amijB2oOINCE!H2M(%)VNG&CBWtc#KWqbvtX zQ!Nw{X4FvAL{3Kqg@yFx9vB_=V%psCQZ%wj<TqbpZ#ZIe>~r!bUXpihfqJ_7sja?StZl_cluRdRZsnCoa(=3?c*t$<;2Z!gB7`Cv zYcu|_d>|kD*UyF7U}Mxl9UUEf+MAe|psu2CXe#!EM#cxJsjh?E74;NPL}}IcR`|m% zm0BC1Zt~60vcsw5N zDA{EhkK#@42j`!gn`7{($7DIJmp&7~nMCwHq-p)h%Pb4$BCBsQlAwHET3Vu=ogID# zAAw_|%j5@>k*7de<${3rJR}wzP^BY01WeiJ)@MCnb|B1H#Ro1 z293aDM06AFR}gqA`VXS!b|KuENj6_oG9rfv)o62dD4CM7A~GGK(c$kUyX&0~bN z4utTO5j}eJ2=5vnfIv%2%Ljcccam2tX0KS_7dYjKfJ6bKXu6bPKA{VTu#H9*5hAVKHK00000NkvXXu0mjfs0wi% diff --git a/modules/highgui/src/files_Qt/Milky/48/125.png b/modules/highgui/src/files_Qt/Milky/48/125.png deleted file mode 100644 index 4e0573ca96662fe4fb731b45d1ecb45266490d5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2882 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/126.png b/modules/highgui/src/files_Qt/Milky/48/126.png deleted file mode 100644 index fc4f11dca4054e669d3fd979ecadc19b4720339b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2762 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 Qy#N3J07*qoM6N<$g7N+%x&QzG diff --git a/modules/highgui/src/files_Qt/Milky/48/127.png b/modules/highgui/src/files_Qt/Milky/48/127.png deleted file mode 100644 index a2a7af2a8c46634a8343d1d382670cdd47b6beb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2475 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/128.png b/modules/highgui/src/files_Qt/Milky/48/128.png deleted file mode 100644 index f811ee479b198dc0e145d038fed61ca0c958e746..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2383 zcmV-V39$BwP)C4b)im|kH@^HbT$}h5{(Y_+w@wJ0EyP(tZUJ%g@RI7>ikoeFwcYJPtu5o$ zn$fPQio#P{zY9gJ5I;ZsUMD{87jh@o^^VPLPewXlZybdg1EHI6kV+uK?Ad zc2PZ~QDcI_D?~H5k792h#j}0vHVBUF_XLll3K55kHrVF{w-sRuuzqU%6R4|M!Nw9& z1t>lso4;+WzM%jUoYlI*MIvSqe>8HPJlKtGhx3O^zXf<7FqEMb=5lGMt*XOz01hw< zj7(gDNW~(^7BVO}b6}0b0K!>uA{n--Wlh zx{^c@xBq_kXX%nuV$bU>&tj*ms&Y{*6bcLB(G4^lWJxt3r2>)r)ru!;3v|Go3p8%bllo zmn3sPIR)QMxNl>t$vWwX;36oI>;mV5NiJ@^+h5Pg-r}tj67(?vNqUu z{|b z&%0I-5*Ls#QtBe{jj2IM-8=^^jqS00Zx<3>&kyv;$Xs-F!+Q8v@=u1)=BkH8BzCAZ z5x}fmGUww$^N~4=2eE!?{2YQ=6XFsLCUbZ483e6@#X`MQ!sJ0TNC858FroV#t{gJQ z5jjqYsmvHAIMwVy0dyMC`&K&TQfWmKw-4}Plo$Z+Cv#=6Q7&DS`hkbcnd1m(n3)yS z99!sAP(F|x9>0)SQMcB}Ly5&gO-m4u0v-tXMH{z;fdbIs+`hJY+G*sE?T!KC$`N_-v#Try|O;Ygefd~GYXKxS?dW-_<> z4t5Nt0)gy1hhklIOILQbu4>0rZj9qXgkuL1fysQV$WGctTo5>r5V3eJRL_`pH+#5EWfx;;0^XvFr{zCDzj_gsiw zFCE#^fq?QxMWo8iz$7OUK;#@K$eFK?G$UPJ=@?TQae16zQ3)f^#6%S4Zr_(w)x~KR zDOu*Iv4}aFO`iSp^ZR^c?x%m-_6DMH$Htb&q4}P5z8LRKT>M){Zh}{S6Naxmaz8Ez zSi!-3Bq4rUu5;T?Z!`LFYA9qXr{O^VzzvogiwgYsNPE}%)sJ>O5#J4$M$f>&wNseD z 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 N002ovPDHLkV1hEbcSQgI diff --git a/modules/highgui/src/files_Qt/Milky/48/13.png b/modules/highgui/src/files_Qt/Milky/48/13.png deleted file mode 100644 index 6cb5c5dca70d2e1aea3e27883739ba0db88cf554..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3016 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/14.png b/modules/highgui/src/files_Qt/Milky/48/14.png deleted file mode 100644 index 8d217be46416905142171d7291e46eecad1805ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3708 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 a*Z%^77H3Flv$RM60000004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 IM6N<$f<7!A004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/17.png b/modules/highgui/src/files_Qt/Milky/48/17.png deleted file mode 100644 index 4a7e5de4113130b215918aafe13ba8b85c4f933f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1828 zcmV+<2iy3GP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkUs!2paRA}DST5D`mRUH4{dwcs>%UWP841-(tAn^lPjnNpRVTlnFW5Vzm@v-6q zOcdM)KNyX1e2@?cp*)lfjh29XF^0_;jgM#-qX9pdVS>Sl*vi|D(r)YX-gB=1b9!%k zJ7`zfO54+P=RSJd-|zfizyHYuP1B&Bho#sJK@jHr<%2&zzXmk$;$d*(y&sRv_4}^1 z*kUDDCo*-R|J>!Vfp|I=-1n2OwGzKkQ3A5X4&CkdGkZctO$O?YfXCSkloD9JDTIt00U1n!snJn9nfHN`*#PQ>Z>0D8s*^GTM#59&G%KmM0Z^f(Dc=( zd{+7FaxC)LWmjT_|q_4&ET4A8Yt>+C8Y5gka5CAiv@!@C9*^y(VIv zV3v6Is|P-*sWfW1OU`{}fG2u9a1x@#>V$90mpwacMoO67g%r4xngdUA#8i+(3pj0_ z{&&8(udgPu5Yf~;Xj1^^NSVMB{k|1dtW|FCzdd|UcMZgVITM*km@JVeXvByL4y%h* z>%dz>ce`(>Bb%=*#DMYcP7?r&7K%9+uC4=I_J!Cl*dUdjhKZ>&K#0oP2HkW5KXh0Z zcr)=tKOSzWvISIF=xTv@DhlJVe@D-Z{}CKJcQh2b^gAcxZC{OYyR*;=I&quTIq=5k zceGZ=0(zEdaxaIe!~~?3MDNzMzYZJs-#l_x_m$}j1C5TRUT?>0)-S-xv%lo~a(uC_ zE}5Phz|-~B$wKJQ3<#mhs2MyPczzt;Yoqaty+=+KQZ1%!I2hK`{OEg+NHH!Yv$;>u( zJb5CfsOh0g(Gai5#8e-UU6pnvbPz1(mq|L5%1nbw(%f<^0WhJ5_(N5T0TnGEOXU$@ zyN4Mi2@1*ZXYE!Oq%!f)u61MKn_dU9dzsjGVU;Ctm>y!a1FN($RIx&~ z`S6NWrqj{g>&9YvFxiQ%q=;513qOEI#m_SmZrc*LGIcSEoc!}fVDsTFR@VD5FZbXV zdtJ7LJm4_V!DM_KHXZ5=p#)%^RlwLyW`QgarCba2B-4|uFMxP5#x&Bvo@Y*l=S*Pp zr(JFYcBYFKqJoqtNIuD8f$2nSRHL+K?{k00=0xDtgPq;zzW(Os zTj5e93>W@8&HXv66m`_hM+7}!1^te3jVU0?Tu9-fuGDztKt~^1wk?sE9z|E}dB1OV z@Us%2ga&M~!!sT^3(;sKzsfU#8sIhkM960#Hf59yNS0ZH03`%v%}bzM@FfZ0imb|{ z6tAWwA(Mz@5eh|s8rOs@YBp&ml9UjxC`%0lJ$xf++)|0S1;m5`!h*ys0YjA_P~5KU zH>t+uKz@eO=ca26xZPpr#~3@I57wU^zNrL65vwWwGQEmKF*5mU_$=66Hb|$_cr3E! z+*c{RC6>@s0of`=Ssphks+kPE26$GyYz;Dkw}O(v)QN>mltkaY7ycQk6ak^wzZ$SD zjh8=a>%(dqknI)@lw>HM889|WvcyxXfU7LYe5HBo$buj5NAZ6D;ESOH^Q&&Jg8&;F zKWtk=6vBj=2Ujx(bmkW`*`TqEh3Aqa2GAMn=q>` S4|#k50000004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkX+(|@1RA}DqnoEovR~3f8d#k!cPj%cI5GU}#Z6oWnVXFABVKI9K2chj&Hw`_>&hx%!{Cc=_397T)~# z#koh$zxwl=0`Tp_dmlb{-?0-Lw`^hQ`Xa{IaL=?sfkShXQ`4v_#+p6=0H!W|&m~kM z2t`D~bsqq22-_37|C#A6TN#_)$P3T>?fCEi`LA>R@}|T5bK{n+4^B)?k!M|u4T6^d zj0ryf&gIKU=S>dn--NL##-?CXOwv2YBp923O~ACkjK$m$6!GlSFSB&rGc`5UCwu_B zaP=LwSj*I=&1ZmfE0jIR0^dAz*RhZ7xnq86db%mRjoJsYEQ942`SJH|$F#;VY1nCv zh4W+#la4{s#-weqElko10Br&!U_xyC>?ewGH9J(W9cYH*VyuORpb#?D>lq z)=Zv!@F`Xa79rC-_ubjlL#o2H%WtEqo7a^_MHQE4_;!n*{^49BT2FuLtEYguJkJoP zNZ~NnkQfWbpr&V~5Yz&R2lbWq)kN(FUqoDna}M!>s?Po5Pk%XGe(wkO9X+P1^BR-G zx&SEPy~jJZ;+}T}VDR1}PVo_du@)P%QyI*Z0MvuH5EC|m1^`f(1^*Wb&o{gwCInSv z-2gPCKJWYh04`|Fdwd}QK-7BhNyWqu8WmJMMm(B?fNlZ^nM_?55kD34y5irNdR28fX2Y>2cQZ%A^=qlF5*2u`r_xb zDsILEs~AGwE*w5DdetC)C{e|x0^%`=M3g~#VblwDDUBo?ql1e*#*941M@l>Xln-ozjdF`zL9w9GTzoc-fCj4gL z(CAqxmhoTy`Pt9b@BQ#IpEw1~iG~{BGpg>>N{V{Hwgl@FtP23@+AmuZOwXb|Dj@t< zjH$m~SYUFj-5@tBNYBU?h*nq$Krp~qlJfK$lU!R~V%wC%SYOk!X$os=S*)H63|w7s zJolPo$MnX50+yggs>BjCs;YeVlLwB~NsfxlXov=OZQ0D#&LmfsrTo?){WU12_FY@k zX23u#pq=c~qy((GCZF_y;GcnBZXnkM_*VhBe*0i3Aw z<;nu16teyt+$*)FLBs}`d^G~U*1Z7)e_(8^ZGk~1Gr@I?t}$s?McA7aT!(-dY=L0{ ztgy)Fn+|!;YJ!))uNwe0zr&dG1{P|!X5tROVmG5ZpzZFX{0#AU#*&2Tv36bX1>k%` z>#DQ5;*Dz%8trN0!H1uH>8bMG$s_li0_FhTSiHgUFWkzJ{o5li-T*az960ef*s-vi zR@@`0NBPGy7kKnn=h!tpS$_NcZ=QRlQ9QqK@Z&?rWa+9CvtgM6xRK@DKf8&OU)qHo zpTM*xG3`lgyF8!3v^GF%0%OO)ZUEbk0FnqmICAf8i0|_FZ=Yb}SZkn=T6LuBo{gHO z6;`}VS?p#U*fW7R5552?z~`vXD?+a>1J^;N1F{5u3FHIt&HeD<8J3G;Xtf+t!zOtkEH3TiyftEP3yefYR;6V@eJ7;u@XO5C58Qc5 z`xG`(5XX+T0zzlZhyN>Vliu8Y`<=K#C5&oIVVgTx*qi%?N2!SNdHY zgf9tA=Bg<)#>A$gHc}BE;=_iioZ1MqdMp4C>9q>Ov_FysguZ|(o4E>r;_^u9E0PlK zCB%`6ct{FJ+CA;t1Z@n3qXk6QC}uhe00SC&B4f;1B14KHISb5s5nNv2x_L!!gVG~6 zXbUEFAnldz6B%CRLU{+*YaTBFXZwP9ZdhkB)P=ezC8;yt}p}8vX+Gpy0~uI zTb1$BTQYJUTevuxs7)p6d^oz?wQI|ar)eqRp)cRP`^o-nBqLJF`HpjprwK;&(4c2_ zPEYXI^Vd1NGhy?ThBW8}alrz<;g(m@=KYQ?UaL{gVHVK7k&7xU6>cH-at340yms~d#hYHKuWx&|Z#`xj zf}3^nU>!KP(5e=!Y$7kdyYSSofy0^r{{O#K{)gLt0ZCCO3z)W_<^TWy07*qoM6N<$ Ef+l$lUH||9 diff --git a/modules/highgui/src/files_Qt/Milky/48/19.png b/modules/highgui/src/files_Qt/Milky/48/19.png deleted file mode 100644 index 203510ddd4cf48a2af598b3f7123f4dc87598bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1982 zcmV;v2SNCWP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkVL`g(JRA}DSS!--mMHD`FciTc+x-HumEu~8fm50!!5v>732_l%FAjStq)0Ka| zjfN048i)olhKP^IKf6Q?8jP+8NI<2igit_Q!4}%mEqy^lTXtz{q1`*<%-p%}UAkp! zw=|i(bMNlG_xsNI&Y5$j0>&6_=RaGhi-I6T{orlCu3Z8c++^wmz?I~<{q_5Q2t@l_ zqrrtx9*CB}hlf^|XWH{UGjkR~oNWU1U-v_e?-&1IaG-3%@;x=Vl zEr#0GKjHdNKiCs9AZPMSI9j`#l5lNYvG3w&5wMJ=9=9HNaEX0lx~Cv}0i13+$Sg04 zL(tRP0hf9jVe#B2QV~Yp_YbVJkJdX!jlhq%Ji1bUJG@tN15e=Xo!zN==A%b zqo*E93Rk!gLY{F&;N5-8@~zhR%6a)qQ_uJgE0&i>P?U(Ycb^BFH39CJTDpeZQ!y?G zY~H)n9%oDNE}H#VYFl?5`1?DV#bwLWArTp5ME+^{9VSo8fvM@U-J5<|QXX>x2#b|- zrrqai>#BpxS1(AwnbimRaY%Dbx_-8~5(=l3z@$Wn=bb%^S4Z>ysIt)8KQ5{$DO~9m z2|!@0ag8uP@7`n~=kalp0ZkrNlkguol$FD%c?nB&RMoCl{G_iG^Y znP7aWk$^nIke)IPoa8yj&wH!sLbvtNzXBs6U>m9CHh#CDJS{ofLtlEbVLuB}9Ra~C zA{D>U3z)1A4eUx$?mey=-)~-Wvh;t8fepLo7pEjX3*2u4jtVMn?8E7Zu6)Sc=elE_Bd-ov#Vfv>bbUK1bjCGss2vjWIz`S z*bv5&{IM<@VGdrtRyu1nT)1=^x~{djKVDbAGim~_>?)#l3$gseuGTBH)H)`hVA6d^ z+v%p*a4sKgB)!g?UIvXFC!z29)v`~XYB&;AF-LBwEXpl)`8xiFwk!4Q0T}T?=7s_Z&VS3JyYjYZWOs?u7(flK17WXBOXb5R;oLX5`%E9=ve_>RV26 zYjed@#aLv~3nnKh5)wkK40>momOpYxnqY_gtUIB;t;$apO6R9fHwH!|p_jgzQIT%X zb0^0;;GC~Y1)^eBUZ{qGpu!Mz4-Ba?WAGaZrX^>=6h|R6w4JOb)!l&( zuvn~Q#m;rT-~n2_?i`kY%^IJ2WAIE$--A%N#4O`wm_eBB8 z-69(-NCjQ;=??{7B>Zu?F)x2Z9@5rcaY(==g0U`>Lfj~71=3v!$U`14s^HNkRf&e5Do63|vQuhXnILY=f8(5VaDLffJ6lh8a9U)NTLRkk8j$Il>NX~kSQ+s zy$HuDh&3%C!iv?+`VT>fdqeFHVk9A~Xbr2(zfK}zc6 z-E%l^$VL!x?5a^Y2=(Q(p==K44aEa!97qT$+R*64P{e^THbC9gi%l&AenjdZq=^BO z)v>0VhY+NQ^iYs~;O3b8k8ItR44+^ToM=d7M073VF#Emq6&9O?de2YJoV(t<5{NW? zk7#pun5V5FUZ|vdq}DhKSZ#C&1ej)6%!BQz5(X%Un+cw>NWn+!f!pctKOcH;kI9(o QY5)KL07*qoM6N<$g1!%+y#N3J diff --git a/modules/highgui/src/files_Qt/Milky/48/2.png b/modules/highgui/src/files_Qt/Milky/48/2.png deleted file mode 100644 index 8f4903eeab9ae70a7f872d64fb94013ca40e24e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2550 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/21.png b/modules/highgui/src/files_Qt/Milky/48/21.png deleted file mode 100644 index e65e4acd3669530ca6eeb7e3f1211bf47b58cfe4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3178 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 Hu0mjf9?KS7 diff --git a/modules/highgui/src/files_Qt/Milky/48/23.png b/modules/highgui/src/files_Qt/Milky/48/23.png deleted file mode 100644 index ab9e60cfcb73b948f45f7cb941030af6b0e19b58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1742 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/25.png b/modules/highgui/src/files_Qt/Milky/48/25.png deleted file mode 100644 index da93a5962d00c5518d0a9304f5a09da40041d40f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2310 zcmV+h3HkPkP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkWj7da6RA}DqT3cuw*BSoiviHmE%8_JUENx}Ewq!?E9XT<{gH$MKXamth3cVO4 zN(-e>szM756tYPJp|rFMY3Yp=8S<7F`=JkQA#qw7Qre_)os`6v#<6@+n&`69UT4nv z|31v@?9A>+wqx0GXci80=FFLy@B9D%`~P!xS7By+ls_sy;vZ$ZA9(pUhYcvoY$zU^ zR&@WWLbrsGcUiio$q4i?Ip1It9u3`spLYUe|IL-bvG0(3ILF+|3+=;Eb1HO zeD~46Jh7bwwl#!QU7t^nemWBwei&@N2P(b}T00Fjy$jKSLkK5g`K9^$C+|oNyz|WX zWW2k7O7EF~yz&BI76E236KMTB6g`Yc=K$2OK7U6N&|~5G#K>ns%l`zJ04A_=1QWP) z30ig!!trSC>hF#`en$~_`)3EfofsI+snImJd=&sU!Gi&2n?&UjLR}-!!lAh<&mJ-E z2mAm-56j20Q8||Ro z#$XVrw21J|15kBs_VRD<9sE!UoPB!l!H8kZ#fBe%Tssp8x*=rOE3yVXM9?BJ0GR(! z38=bOF!nzHT73%;W?)`5&4bsUtRTFsbl{7A{M$wUEJT$tB zeVt_hjt7_l5IFe>th{y-AfDaz&DW0Iwo1%IbE({LE))xctINJ{VJ7_XjR?-Yt3pbN zm6C$vr#hVL?DL9Ry@GJI2gIDe_=`ivZ3&?_elReo$D(t_=)I83=K!LXa3Ne;28ak! zN=PYDt}#|CYBQW*&m*&pL|;Ex$Q(1x-5w1nN)P)0=!EX@Fsk`H{NWnyO2Q^amV zWeKsK97xEycb*wHZYhD&-|w3YCzG@3-6N1|i<{!k4I;=%L9duk-gBA8d!?pwT&+<+QUZ(v8mPl9$`chIzp)otZF@f(**wh4P#P| zM5cG_8wS;iP0-ZH=bp4kP3sboFML`<2tFHaQMS2uEJf3)xMoAZF86gux7 z0Yu5SAZvtzYkjXki9wQF5{+hGqpgWZf~c7QnTQw$>c#Th*{Am!ABey!UpHpLiTHSS zs1Fde=8Ieq?Z6v->C^$DW=3L9whAI5O$%174iXZwRzt_`K2%mqnQJXE0tvkQwY0(D zLhq3=P<_K=y;kr6W6Y4Ey^z5xUbkj2NJv;!6VepE;wCC(B>TEiUM}U&Jhf-=h6oU2 zF45JINn{hYa2w+D0S)kgV_hQY3m;np_O%8PvLr;c4pB2fp6jlTgn-f8fojEsutfg4 z3H}@T(N*!#VP>!RG`XjDZc62awS2s7hFKTuy7Hc7aB@)JwMbwCfH zv|;7}9P^2o7<18dJh!7SV~0hXNU0^$5Loi2MeWq0a0?g_;jb4Xo=uD$c4(_3jbuUo z&5o0rMa@KSE{mn5YVP!7{q7P&{rwkHh9!8eXLr};#i`Mko21f~B}Ghcf|pVvqKamw zX?yP0=kOH1h{gyfB3N0jxz$-^#&jqa$)vKe;NrAp{wI$mo5!PvHN^Dhg6<0=#Z8_` z*C631A}xWtI^!s=TbX}-ujdf}(3z2orJ{B`JFWv-vk!J0vB)-m`AW^2fZ1YxDM>^ zasblx0N+B;o-tJcB*DfDKxgp6##$9twT@6M;@L-Drw4qg6fGcc6nHS`rl1)cB_I-3 zz{D2-K%IWRbOKVGeErp{sH~MCO#u?pUSP?_;>@#@@PczniJ~Zg(-hTis;WAD6{@B} z(Hch$s-{BKG^aJLX)SX!cdnwTP<0Jb4dL9mB6>UZ3t&F(7P8}yCJiE8azz_e}5CTK;mF*T0CV~X#*NwVcW&(R(C8Kq@z<5>!zKv|gh<=P3~58WUC gSK?y=|K|k$2j_+pF07*qoM6N<$f*B7vZvX%Q diff --git a/modules/highgui/src/files_Qt/Milky/48/26.png b/modules/highgui/src/files_Qt/Milky/48/26.png deleted file mode 100644 index 6ba5d6c1063f34b62bce610f8c856b6250a6b92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2586 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 wHe;Rh2E8-i6BC3bX}F}92Jrv?t004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 JPDHLkV1ma{_A>wg diff --git a/modules/highgui/src/files_Qt/Milky/48/28.png b/modules/highgui/src/files_Qt/Milky/48/28.png deleted file mode 100644 index f0df2d35e805ab413dc6fc9b98fd79b103f7df8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1718 zcmV;n21)seP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkUJV``BRA}DqSzT-tMI4{myY_l*q1U1)1#Gn~DlaV*gFYB8J{Zvl&}e+*&}fv1 z5E6_e#8x6kjZp~E7$x+opdlJ4B7%tz*H`01c_0||p~yGwwfDLAwKL<)?B49o-tOHQ zXxt{o>2s>miU- zr$F~T{owM{h3>hQ&}|bK`tk8LjI`L=-i-roEla^aQx`yUU@`D1exPB^+fKZ9?6H2I zuOas6!_N)){f*#C{11R|323MU5G%CpSqtnrv94cHe6jVbHidGo47lbi(J+U-L*UJ0eIpb+kMY-M{sI_d z0a(t6(2$S@2%h_SR?S4V{b(82l3QZ1Gjb5 zp(Gzpps^J!U-BRz`WJc4KE@5a^v8OZuL%`O85$oM@P!t>^lEL=fZ> zLr4S&iNLF0cI^l>wj8+k?k+HszRJNv2yFnNgX3B);ua%pfNh>lEOOG%i0tr&6?6hx zC4pDIxNoF6xG2)mz7))4u89YA0?-`siqW_B)$#-)wLbEbvW&RnQ}7ajmp@xM z6bJ+(!ImIkQv>#h4jBr^l|v_#l@I^B8xz?SHY->`TP(u)aQ*9fh!J?{(26cP%EL`9 z0g%Zh>_Npr9yayEDo*S|fK+P9ZW}$^Vng>>2al1SJPF381!$w&AM}AjA;-;A3641H z8@j_Kjvd6ok5NYn<0NColHqgLoDP{pg`;mS@ z8Qpa~i8T_*%%}o!*~_s}$ZZt2cVL$`snw5&WC6aU{ATx!Q>ci;si|y2mk~4{AXSJE zOK}5EsuRL4ps`CIFUWLau_iwH*3EIsE}V|56R3!|v$%n(r&Nw8b_H8)#H$giSkxbT z`__4SA)J|2CyIFuKn2{Ee5%O^8>C2$5qG?XtWIn@zTGpGq}7@i-&-=$*whdSE)D>! z;j;hKzv<)>LZ!WA>)hYIPN;BjBGwB?&ct(*EJC@-iC7#HSwfvBwif9~o~P4}fpk1O zQOs$&i8)wHVW3%rU`4|sQg~)>^yK@gB$Y;3P2|S%=^~#k;xb@lwFEBf)q9(}V;`oI zr#{L&o68iUDs@uFENl=WUbBGpIQ?;UYe6mT%+N54G+gNuUX+miIPYJepAY6nX@nY}5h_v2mjrM+!xhUuPZMCr*+cpBbkwPH=O^jf0%#>n z3z{DmC?CIkUZi1`45+Dmj7h4H;psj)=bO6=ztk@fSRk-K;D0^-1uxrDLMw+D?f?J) M07*qoM6N<$f`M%zh5!Hn diff --git a/modules/highgui/src/files_Qt/Milky/48/29.png b/modules/highgui/src/files_Qt/Milky/48/29.png deleted file mode 100644 index 6d79d929fcebac1f0f5adfd4478954200144aca7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3035 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 d55DBbe*x;{-004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 pNf4uNx(004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 P00000NkvXXu0mjfq6MUC diff --git a/modules/highgui/src/files_Qt/Milky/48/33.png b/modules/highgui/src/files_Qt/Milky/48/33.png deleted file mode 100644 index 85bb86ff024094976dc9f757a12d3a85c6f769d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1971 zcmV;k2Tb^hP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkVIY~r8RA}DSnf*^wR~W~izR^->DL5i1arlFqnaffV@g*jP{s;K`lHrmiOEzE7 zIpekrjX&&%C40$q&bbJ2GcYvb=G<_IA}S#n2{%g?6A{6p6w2GRrL?#AZO=LPr7gWE z0>yHYn{)4Nd(ZcCo|p67QJSGI|^Gker7Pn&FuQ_?LKB9 z&zWnYGY)X%QssadtmS5t1tKw)<>FD_$#?ph6#=lX@wJ*__qzJw;6sol84BF5K$Hu! z@i^x@_3i+Z)c`2oljraZ2ywv^j!aPi2@jXc0~|NQ@;vW5{oWvxwEzd2-e~dUl~f1D z2B>&?;7|m@ndbzw0xJrl@67vtChGtW{JNFs$rAo3zB@S^YR zhmV=80U+Xw3)a<)qIk?YNwc2{pg{<6kwp++!~S9Br2?SOdmXmC`g})`cXR@0UJz1* zr&&S0Z+9XDuRxqTIKnJDKyA~;amM=ogq5&{ay%?mu#RabdnR!74&_H5b1Xy+wMMw#sH_ATp-0m|Q#^yi> zfw+_8`~AylX2Aga8ecDWJ6HAM!#oaqJ~-_z5Ts-P4pU|vut6! z=;y>5Ik@;(Z05w^qSi|YApsXrEs-+wbGeQ&hz;X_kBhroPsAFY3((!&?KPXtH!T*6 zM+6S8_SZphx}T^{1JKUN3Tn105E_6cnplF1@Dxaz*NQBq+^4fIN=)ksu3}zUsH>~% zCIVEs-EMF=956OH3|H=d4r7srs_Uo#TCSHBOOUFPcwe%3NmOPPGYG_I_&I|%^*RAS z8Ues+)%W+Qc*nc|?%usyjqcOpa=Bp5nl%uKL?96K!*BOKQy_HF2>?1U{+3t*5F%j( z#g#~pDH!5W;kl)R?nd}PYmS8^gm12WAFq9(h3w9qJ2lwXdpsUky?V6@F$69DeF9_Q zhiMkjQYDRv+++wvyre=%f&_d_cux9M0H}CTX0Dy&Di%~q5D0Xo`htRj6o^az9)V!E z-w;4EJIULmDdJVPP(eh5K$l1ayS7eP`9e&%jQ4i8o#xp^SEI%derWDF45Oh3hE|}D zlNC3hld6d)?x9;kaVbgCgqujP52A&RVZb&l=?je^#Hb&d{y7ArY`-CZc1^Mfy)UL) zg20nC1VyuW)S|yPWbB>T2x1r^8oNJ2hzF@FY*IN5vQi3Yey#zS%ycxGWzY)8P&}LQ zT?s>o5kH*oIshY657Mg7i*BmCS<-={T_*-V3$7G#Urvdq}d-0j=9bxRP4>FH@|i8EI}per_()D2h? z>|X4iYex4*J3BjRU?)QiJsE(rf9++5g7?;6|50GG8bE{4EKyooN+HI^##p@P+q`)* zll1@^1c`~l!a^M)7z|Q~%F0S6>j6l+b?X+jga#1~hp8p-Bj1)STbQf|Ag!aLLx&)5 zb_7DR#6lsK{Xa4SLENdRs7Qwx9UY|*Teogq8Gp0hxN(DKCt``UYu7?36w)EKZQI6V zJ%9#5+=*C1gBTeZp%B}*Z)dU|fVB4Zb{(RutPK2qKSZNZcE^q#g)0JpwCmTeQwXP% z{6`!oV=+5-?yOib0Hn3GwNVI+3kDBgRaF(cqyQ_i{SS(jlPFTbgf9R9002ovPDHLk FV1oHEg>?V` diff --git a/modules/highgui/src/files_Qt/Milky/48/34.png b/modules/highgui/src/files_Qt/Milky/48/34.png deleted file mode 100644 index fd095ee77b76190f16c0e664891d69a362496b7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1105 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/36.png b/modules/highgui/src/files_Qt/Milky/48/36.png deleted file mode 100644 index a6d75ffd7b8328e8cfde7f1b2e2a9502fc179dfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2588 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 Eg7`bR_y7O^ diff --git a/modules/highgui/src/files_Qt/Milky/48/39.png b/modules/highgui/src/files_Qt/Milky/48/39.png deleted file mode 100644 index d76effcd79e3a92356f35cde0f5c5f34621ab361..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 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 JPDHLkV1f|OlIQ>c diff --git a/modules/highgui/src/files_Qt/Milky/48/41.png b/modules/highgui/src/files_Qt/Milky/48/41.png deleted file mode 100644 index 4553c04588bd45f20476e572d844f529402a69da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2728 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 VIpgQ2`mF!}002ovPDHLkV1jc1+*|+v diff --git a/modules/highgui/src/files_Qt/Milky/48/45.png b/modules/highgui/src/files_Qt/Milky/48/45.png deleted file mode 100644 index a77fb9e14c240440998dd9a5d290406f646aa544..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2717 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/48.png b/modules/highgui/src/files_Qt/Milky/48/48.png deleted file mode 100644 index 6092c0c3c2593911966754a6297a3785790172bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2588 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 Hu0mjfMNB$i diff --git a/modules/highgui/src/files_Qt/Milky/48/51.png b/modules/highgui/src/files_Qt/Milky/48/51.png deleted file mode 100644 index b5b77eb9ef7f75620697e133db2dbd059431ccd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/53.png b/modules/highgui/src/files_Qt/Milky/48/53.png deleted file mode 100644 index a13b5687fd11df43d74a37cafd2d7626b92f1d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2872 zcmV-83&-?{P)0qG*DY8j*<#ef!d}BIs*<(bYun{fdW$p87Xx@hp9u$jE;gxQc)0u(t|@BK))q@?Q2eA(OQ_xt@HzyJT+3#Vxs{+~Zw zB?27B&Ff(g{MpwD4&8LOlXg2ZhrKk#3EFyYyUOi76|fbto+^PofBM+PfxDHyZf~yR z-K~qPsIwVC-@zU|91syoC>RMyh$IxB25t8po7?*TCkWjC+{ayXY2R5bHur|*PS|)O z#)C2N zVi%8B2SV^oC1$;+qs4}E&$MG;GJ#h=5(Jug|yfv*Yx z4YQHM`VJ>}qX9?W4hT|G-FVlw>PUPl1b%#Au+wTb{C&&XCYNtY#CxOB^gUI8&wgRw z*S6K^fL=K!2(;VwqpLfF`4BM7HIY$~~^hSPvLXc5!H-VyS>wE~9s=e?BPkh)(G3!dmD#E1moSwE?Ch4n{!NR0Y zXY)iz0@V?gpq&fq0h+*NF>?sTmEy#l_sHi(BAL<+vh(p{?gjHecETk6VUrd~Cs(D6 zYQ>VOWey5t6q080tjY-Z7K8vLuns(_XypXT!iXzX|5*rtiW;Z4AGGgVFb}YYv+$t` ztm}CeBUiwHD=%9G;I*=5gp#H31|)r5UFoa0-t9AFQZld3mNon^h3 z9U(&d7rYLnQfmKLKonS;Y_k}UB=tzjN_tz($X=_Y_Zu8$vc8PCq)*6%rJ^b z(DsZ*8(E#IFd2gKYR9RplMVB^>9HMa4@P3ieeWEf#u5T!vzVYL880xPnNhUQq>{EZ z79(nH)K86um^Zim_LjB6sSq&D_eKKql9EbwPlqKpZ!+rEov|Dj6fi514TMC`4J zbV}gowIaLvzJK0BXSdsIyt}^63M&<&V}pJsv6Zz{E3U>oEn+&{jR-|VM4}Qigy*Vr z`3KjxTRUrNYV_+QNg{$WD{2QjJ3EC>Wl6bZ_sd;$yw6U+8yf5|894X@QA`Kptj#X( zA}SQ+Y(+K`iKa#eoYb0M-mYTpiWb;xHd@%^BU%(iOixb>^x3#>-8%0n5!ih9KlU1p zhFhJKw7i)rr$2&7L@a6IxbpU*qzB40(YgKae%Xml7q27(s5p7@B;Fs6((^f?W3e4B z`Y?}-j*bd}K;Zlh8#eeVVg1A$xZ*BPA5GiCQ|tz(V+e;6I>aK5Iqcjl-Dq(qx7f+y z!c|Ljt9o90ACJDAfTYk944eyEu=noG{!2PKI(ix#8(n_C{}#Gs$DCE@@;m>wm#ovH zQr|LlGE6Rwv1+UjC9~Cs!uGZNe#uZW*XSd&zjBEY4u^w<2_AkSh^grWVsQ!an8XTO z->&Bp`zI$S1)k?&GMT#OtU{OVe6gD*yj7t|S>Na6f9;{h%Pq5@%qgQ*9{bSB z6PZaRFJsx6qo}pVi=?O!irHzw8>1tLnNQ%HT02z;XFEZCRjpdlWbKjAKRXDq5sS#oDTqEe^B| z7p*|o7i<{wPvR%P3?LY6(4VKsNyMWmU7=FccTioZ-UCFSOG-#E@@85 zet~7nK0tFr8{Qj!1v$7}R-?EAndXWlyT#RpÏ^M~tmMaz3a#lwe3`nxaS_Y(>4 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/55.png b/modules/highgui/src/files_Qt/Milky/48/55.png deleted file mode 100644 index 216721545038a8f4107ae03cb3c744b141d1ce55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2867 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 RirN4G002ovPDHLkV1h=uV7CAO diff --git a/modules/highgui/src/files_Qt/Milky/48/56.png b/modules/highgui/src/files_Qt/Milky/48/56.png deleted file mode 100644 index ef8920b00a9f112d0d945ae01369be5290ef3f0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2862 zcmV+}3(@q6P)L$zo6}3Q1TtA;Uk}#f&Ya&lzjMyaBtj{L|K%n= z2k_qpaH}0pKbCH%-UIZ)Ub^nf%Px(>%_PN%}!{)TZPQrAFv; zf^0jq=atgLiAJc^Drci>>Vx*-!^~`4yIV?7f9Mgt4~{<}lWbob-s0qKZ%AF&4T?3-;;`IIS&MICB+O-dLozk_S9oj}Kd zd$IPB15mm1@Mp;>9(hMe8i0fqm(a><#TfttDf4$|uQzXAgT>J-X5P3Of_QHKd)`qE z;F+I)^M#JJtni~f2?s5Gt(bmw4mZY& z{O-v8Bi=p+;FOBx{t|L<-4YzFv;)gBN^8Y+6pJXw@t>rbrD6b~cv_m1Es!ohXCntMTLJ_2?9ZrOU++nIZKv^{{{fPPI2x+PMoaE-BD(# z^+0fy0gx~rSn`((XkXKU)!UO;ILiq_*N|bSR3HF12te5*%R>WTp7VW_;6;@N756I@ zS&_jAMX9hvzz_fs)vv4g`*8pg^&mWRZ@j=%0!ni(TDByR{a6DyXaJB0T-SnEmK6ZP zT%*aO4^>rc^yI1nz;b+eCA#O}4w)zF%4YX zc2XR)IHy~IF!wc(fIcGbex)sJs3Q&EWyKTP1NO}U=*ea1g_s9w=oH34=n*&Ln@mWl zn__LR2Cy(wIzsaDLo?U=ch4`ru>Srg_yvW+6;F5OTraB9r_l5eR>e!ODr<`ER#9^} zqR+AbY63X~$g*)-)y0&a6|VXG{zw&oub!3T!*^LI z&iiQFoG`r8s|8{5x-=fJq+Ys2mN!d^YUM=`7h;JmQ&3;ieqOp(f2BEu@}f#zOOU67OuLh`;xf)eu1+g>86gx z^|czFw?G{w8J~QwfVSQwQZ$GjFb+hbB0-h(%P}g%fcXt5753E!o9u8Tg>z-8OIB4J*Pu9>x)>^;%H1t0P+&zm11B+VISDzjl>npU8@|*UgasFQgWT!oTGB|Ki9s5*= zyI~vpg5XW`>*>{2`}PM6h)a|mA}ZBVsKGj@dQT7o!L%Xed-kL-@j(gKO|>07d=uqw z8vwWCn?*m(^?DP7p!Al#>Jf-imBLFYFKW+mV<0x}Orc|wgV7TO6c(gb{@a&suKbDs zaQnk%;|bdl?c4XK^>3r^7R)JHjq>7V0UX?HrkHA@W%4M|CQr)KJA>buTweJV1K{@i z9??s$A14s~+wX1CnvRVW;TC0023mS}n-(!!`_pt4nD{ppsztp#7<_UPL$|8OiUDx@ z-6pg%o%n!h%PzK}jdR4`MJj$`nJhsRMBkTE@QCW?sZdgJW91Kh_p;e#eI@|7{dSW+ z>*q87r`K~3Zck!j)Wx(3!iIyd?RKz*)>oN19}^$x11eVf@b{)Le!KI3Y5-yT%|`r$ zF3-2L+1R=}g$44$xwEcbHpE*m0bEZTrAuQjuFdE)ImDd*{uDARnfHzX;P&fo4Ddf6 zCDWpP8)X$fju|Bo*(~j`30U8@1TX6`PMf7dPG_1UKe(cSuejY&0Nj4ntq)vZ8Q=ep_>TYs0FPBw?K-nu_y7O^ M07*qoM6N<$f+G)Xx&QzG diff --git a/modules/highgui/src/files_Qt/Milky/48/57.png b/modules/highgui/src/files_Qt/Milky/48/57.png deleted file mode 100644 index 10dd05f60a0421d0a9157d26523d4fb04d83b8c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2661 zcmV-r3YztaP)#~FTRZ>_EsmkxxH zfCN+G5C$v3xB>!GPMH|vii7P+DuzmafcXVMzrf*BJ_=XPa-86@OVm6;04r`w8?b)T@(& zw|hE|^wGdQYyUd?z`R_1I@U(bFR1RNngFv=$xT0WyiVxN#ufhki?o$(t zMGUc=&40Tdz67iJ0VVtH)QZMqO7I>L2Tbo=Z_311cpjjn^^) zRkd_4i^(g4xO0*WlPTCPp?^J(ol+0YNZ{<2Fb}E|{5ra%4vo)jLZop4DtCvVXNRCv za>CL|K(M1$sak+g^Wz9V(v5s_2qV3}B9Dx5!&y@53@=!O#k=>Q?(r^EMm~W$bdugT zO4cV}6&S5v^pmeuO*o+NA|04kUE|;Nw6cE&u9mH37pv+A~!3&!B`OOKO^wP9Ll$@ z3(!FUntz5#z`)HvEAD^}F780&(S69AK5MwXZu16|zkCywu`dj;;DC*4g4P``Sm2o^ zt^?5+Y93vN@qZ2@-9IjZo1tOzIn~HaAy;Hq@ElP>_LM1zny0tG6kOB&y90n_-1$bs!Tp|G7Eg_kVI#EFO@(>noUX1M3 z3|*^yh`mOP$sRbpK9nG9-VN79u;S+(nEc{~ksXB99qAw-mvhrdZ$=X6yY!|J9d6y^jlL|j0T=YOvkm?I@`Aj@)+gW4k_y;MQC zMuEHo2+3;;F-S$|KVa$Kl#0$A87bZXHjygswVi9B<`>Aaob>6lk#VhwHcS$i_b{@A z%mH-KIbi8uc)=#3h|kjnur~&#EXs6;gl&0Yx2ZwxA;VQLd9&>Du`HovX@acJeFZpK z$j&(K>|>=;CM~g<-u6~f7h50@q=eku9j9)H6H#av^JOClZ!xJ~v-dsvU zO!P;XUEnqMV2=A?f-|diPxVMqevd6FxW2RApXi7|qwj`4 zD;U;)dO%OkNPs$UFZ3n-_oxvr`xd(FwcUAd0kJoe=s2pRqVR+UF8b-YS zVUF*_NH*-C7jMY-(17gfBn9CfL=D*mve(x;Motueexww z2F&dVJNqz)ptas>d?JoeB4B{m%8Z|EbU&MPTmPO2ywj%eW4{xWjXFE_)Iuj2rB3FZ z!w3tww4I!?9q>OkbODD1zUhf7#9PC-@TUosvN{LdzGsll?P!sgx0fpk>T#}mVX%#p z$Ww+&oh(4{J6VJe0k4eQld}taGl8!r@R#1rB7eKWL3ckl$ol7XxXa61k0_n|u!~3} z){-`JL}K(*0n;9l`Yq;sJj38DFPcd<)wEo=bj$(2_iKOf_f3G8qcVxzM1nHI$f>-9 zdA8$uX)WX-(z1oXw}uG(Byt3v-qij4(DdNvM1Yq+FLOw&d8*o2v8FyRqN1`HDKH;T z@HP-u(6lLrTC)7bzYzF4!ty_y1^k=|@bYG>AQ6fviFMD^AV~8;@&ih-Wj13FFweXY zq`htX&T43sjV`@6N!f-6tkV6$&Dp`vivTZwY&DL^If>3SJ8BT8jmF?V@+hV&vxrFi zi8TbV{K+^9*$OVxEK8}x@xRzAEm zw1?5MF^17=Wn4d95WmC__!oz9Uq4gLn}AjR&|>^(vA=O;1gp2kF?OYd;d4d5M1-ZC zF*MLJ{rZ^#QUhg}=bHy#x`q4xjqQOE;N|x%*iR^Xt7-Aqu`LcY&oD@9m~vj6;6&m9 ztk_xwoi4wk^qaeDNUi7P5geN9m=BZyFTZO>Co_Y4^>?d~SQ5hRq-q3fq9ufJnmY#0 z77e{q_ve2+g1-5T_rMA8@|$KSE*4U1T(ndyc1Lf}{$flMzh)BnpNt?q-?9E91g!Gw zCLChoXH=_jz=wZ2I(NzZR|)X)dXsSkZXJX7zcM<1tItT_;s2F*NMLT|-vSH(p298# T7=V8-00000NkvXXu0mjf!AlcM diff --git a/modules/highgui/src/files_Qt/Milky/48/58.png b/modules/highgui/src/files_Qt/Milky/48/58.png deleted file mode 100644 index 9bdf5431562a706abacbf33db8c3bc7470ef7163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1916 zcmV-?2ZQ*DP)pTe~fT z4feq{88BttaPk=$OAr&4ff6(kWF$n5#28{g6JvmX`bR*cQ3G*^QKJ$T{%Az9{*X;E zuuYIL#z!|8b6;(5yY=g~tLHs(mu_pJx9bY}CMS12=iGCj=Y8Mvo^#$aMKBn|e_0fv z1dbdzQd3@DzISwV)NZj@w(r=n!!CEg!-o$SEm*MNfKI0~baizd-Mo2oLp%XbQ8-md zrKP3ihSTW`j*N^1&zw2a%D@<8f;TlaHQDHW8t3fUv$l9^JQOb#RcND9skBq2LZLuK zMTLn~H8D_YBujL6cW+=1s$V^MV4KYbyWMW8uC6wOXvx6dX^N=5$lA5A(#}fbym|91 za$Lx?V#SK4{QP_ak)YDX#9%NSVr6eeMn+LeN(vF3Fo@M^#pTPFQCV3z4&v6WTgb}F z;zh3b{eF18UQ!4|Q6v}KRa{(b52u6)1PCy>0g)&uDBvVWIyySQ<_1(M6~@NKrfB}^ z)vKtgsv0*dfoFtaG#aNjLD}~OL4d>IXk$Oul$4a%Clg4T{1S7Qw&v#MH8*eG9Phkn z(IT#4kH<5iug~X0Utiw@@V&jgaRD!BJ3T!enVFf~ox}A>3Rg)a*d!(r;{%nIm2piI zXbC+1rh17efoEn5I9N81e&=RSacY-&P;>nK$DW7R;z2|5a5OR9|tFqICt(G`uqEF z`t)h;Mu`A_S7On0B5)dw2G_4&2V13i9b0M9+1bhK69qopI-zE5_qklc;Bbzo_ix*mxYvMEo{y{ zEX=9F(?xq=3>vu=l9Q9+_Bqk!IEHV3GQ+6Pg;#Vz^m*}E;U=tmWM@JwFl8Zo`LlvA z@@ALR&&n#q$fym0fFF8YCPqDMRdfaMROvpbm0I`$qxj*{o6sB5p;7DMay!@xt>S(A zt=*WbtH733@5Pk>tvQ8-g^GxCp#ET9Jp)irTZPW9Rut=3qB6G**1=&2&SAKNR{YU- z3~WVY&#w?n79luT+pvJ;gt*eRw-wUw0HrZyG%Q5wvt3=Y5+mABWrPfXb)Go@Y*AS?RObwDcXkQu{gE z^mQRA8Tj?D?-Cn@M2~3l`rL5ZN6hoGA8nWFHR^O!7jJ-IwR0^rHh%!xCOHKsRxf;M z!hreNi!fKe7(Txn4yP^Q7O?odeu!?rVRGmG;ckri$KZ2`$V{8VpMGC}&6BBT9>HN{ z%JB2{#1ioMEpEXBm0D>s|M_Lz_~yLtBFyK%{&xUx3LanlJc1rIZ~MOKecmpK`TRHN zwRXTIc#)}_lh`mC5Cey-y^eZgt{$h_KQUdraT;1pIy!IthO|r#gaHxjm+s= zakL^ur-pOLh3cXWcwz0^ye~cJ_lVWa+k2d?7#6QXp;TbdV#BL-A0o$Ckk~`LbbzVZ zrZCOUDn5{!qOEax94INMz{<+?Si5}dlmSk)9K)H`lepH?jx? z)6;{}(o)4e36Na4aA6F;QPO-a7qOrr)_r2J3W2AIOPUu1!OnbtV@$hIN-)sk5qhB) z&Q;>MFA4=ubAdfKR#sM8Vj{3=)hY|~$%AwoLnJ0IY>|k>N@#F!kf-b^?h|cPr$oPN$Q9Lju2K$&!Ycvl0{h$ZR&SgZ+v8 z{QMe<5Ilbr2*fRf(#(@DC&d;fFqx&50HkH@v&`N;Vbz^@QMPEO98-2DFi`$62h9{vU&oN5UMgVrw<0N@S0 zv@cn*L|?jesg|3YE9bv_`Jy2pdH?CtC+*$4ciQLApJVu_0YP;95&_)3dspC#ii-3_ zixz2Lzkb!;ym_NdPEI-uTn&DkXD=#a_I4A;A0S*>maC-;^N|1 z)>h!=&6`f<)lHdsBr9cj`)ES&;uG`@98N7{Jb)WFZV3F!l`94Q*|TTtI09c$Q862M zN{QJ%O_O71!nKZOC4lSKuTKlVb?a6uoonM716%=`o%;x68h+KPRpPKupFS0MYJU6n z?N$bEanui=u>h`JyCx>JX3ZK09u6z5lJF>Kc8@UL|Ap61{6(=Mu@r_rHiS@qe!k2~ zZtqH4LIVQ>0>66oYJndgAJ<;Le(hu)1znHNhastEUDrcsr)-1$XtDK4&Q3ahAEA45 zPU6^s<5WfhFiq2wd5#7Oe#ed-77ChKZ)I@kNLoQbfyP(L3^1)p2GNSnWAOSM@ZjV$ zvD0b$z}2f)rO&TjyH;PeY?+gJ%BHfi(n3vyq1ni~b?dbG^XE%JvlX%p;x{4%NKKq| zIyqt7h@k6)88p97C+79b%F4{NoS5(|SZ1E^?#vS&H6bGa;dzXXjxu|kCJT_kC}tsz zdWM{)IIC;;iy&};=TvHs5dxo95ns7-MPvgwNLtqj!-D0-4a2Zd69T_}{d)QSp8!R-DX^bLO^8`O!wWB9Xp{Y3C71<=>mC-BIuz*80&vLy3WRaI7h zf4{(Q+_=#XAN4u4$cECxqoec!*YM}Sv)f?!6ZkD#wwTj#B^5w#Z?9-R5&Z7myDij& z2OhIK;Tlj6QJ>lA_TyxbZ4ol!3Y@ZsKY>Tba^UU3&rEAqLcs%0XB(uZ3D0h@ckf;c zH6h&wGr9xM%tKO9pV@Kep&jkeV1}VJ~A@x&yV$ErkqX6L{&!+Z`#Ta^sse4~PAn^o{=t!6UY zD$`1+r>Dosyn;v8L^g!Kbm@`@9?z@^;ZfKk8#g@rKedhXjg*aoS3%7LKVuDacXxZ> zDGPK1f#1J>zlDMp%`133a3lp!&BO3K@bn78+oo2xZQC|8mCiWD2hN{AFWm;3){BaY zL>4OZ2M->!P!j@QT3Ra0NO)=<^$_)$9cP$!6ukk%bKsS12v4uzc%U+mdN5P_U zb#<{4+mTs;r!1IxX6(?RLl$a6;Ngby{Ra;oaBpV{JPKPfk5=ix!>ygnQ}gr+!Yesp zb~0x(73BH~qm>UMZg^(q@ZrOR4^i`*Hf>TPuE0Z5QJ>j4@YFVpj)o^0cEfXDNXZFa zF)MhFggQGr|3!Z`*vsj#=)(ek3#&WbKqH+gooiN_`eBHqvQ2dQ1FV3!~46?NzOYg z?y|L3n;fcowFDx1E}%7v$rjss(#?$_o6HY4q&bv;8nKgJwmevS;$+p-@Qv zi|5?AbDT~i2+*KBxK&Pg7Ay+fbQd+w(benFZnFZPaGXW}y1g|hL2y&n9+5dhTY*3z z#2(Lip1TYx!|;o&WjN*M0Tl#C0S3W+d1{$O&dF3gv>^L2{O?n_eF{7(vu5!_g6sI9HFv^hGGdLnCUYom@E@wv2BymABTl&XojJUjvX&c~ks e=KA004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 U%MUoTZ~y=R07*qoM6N<$f(8kcw*UYD diff --git a/modules/highgui/src/files_Qt/Milky/48/62.png b/modules/highgui/src/files_Qt/Milky/48/62.png deleted file mode 100644 index 7434c4959d0a1941d9cc14381fdda4060ae17472..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3643 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 N002ovPDHLkV1nG11v&r# diff --git a/modules/highgui/src/files_Qt/Milky/48/63.png b/modules/highgui/src/files_Qt/Milky/48/63.png deleted file mode 100644 index fa0383b449e3c6624d93d3509c95a93cdd2ca5d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2553 zcmV}$J8 z+}N?51w}v?OGuFN0iqzP2q|j#j092t0PRoU7a+vuc(|(|leC*N8nKNhRdERZ_Iagzh;bp$GLjs2c4hg*E1onHd zva&Ku)vTrs4%w^kdHs7`dkJp8zUk@dzwI|iYQ^Q{Q${)8R2|yfWYG7;@tN3c9p#Z zSF_nZ-kUw7n`H1@R}?@;VT07PSKeEZI)he&U|mfxM5~ zeKi%Id1@{RaH{-uyfOX5lv!0O71ZnXlc!FdS~ed)etfxHE=S@hQ2=H;9EV>qtkD>~V_xu7XyFbQ9_kV{sPJDnL zy#B6ur0Y8J`TTj_HgmaL%5*wz9Ut8t7E;x@M0Ev@0~Nq^WJ3}OZE*0WsWhO6pyGLB z>C-qNbveJT$3($mva;4hX*9owyGvIi0s^>c&WW0{NnSzVMH7z?XK`f265z54z)8t( zwp@%Avv_qRi`h{N$4VxdA@E?Uhc=mmu1u&y?Up0WrI5+sNZvppOY_h=|6Fe1S=AoU zR!GA0jt+$45VzU?z~=-sNx-4S#!D8akK}M-Jcq)Nf#YQhq!6hcstr~1cZjEam+oiF zS=4(zTmr%ctf_s|ra2wzLdW*8+jOzhw1qG5^&hP_$)AWwu=l37 zCjqe_6`LlxyHv*lO`gpfu&D5wn~+7)x*o;+&_pr#VH6wp0kngxrB=WJ-aIjso z;W)l@Y+V~Lp#y0^5`ZY0zBd`}>}4{o39|h>`H4mMSwr@7yWyI*$X7;lK*rI8AbA*d zeH}JMqpk;Rw><&YR)x34nwnbMC&0mh2}lh;JD*5dMFYeP2M0) zaIFdO)Mt@)_}ngmkxY7)uHM8WH z6DoIN0$khO23Zl#l)ppmHkytrb0r0y&&$n*k1uB@aCYMRDCoy9zx;cdkG+TnObU~o zZWGo}_CN$eka0rSL(i>6NnI%ESFF?c`~7Pu7EAClHqGsdL{0`;Ey|rdm*Ka!e@z-8 z!=#Oj3%TD1_IF~MG@H%fYp1_~vGFljdI85WXYqOSK3uIH0S-w72-1WGGSZm9`p#3V zY&}7~Q9$u%AOT1DPnddW`0Vq$SlL)6KQu5rE`ve82A<=@E=3L-BjqAVh;~F$@xM22 zq2n}BEN7sbdWrx`5J+o*Y~E?nX4n>5bUhor7CCgI29LF1d8I^@QMHonW`$DuT1Q5~ zv0{|T9!LXRt)AyZPPCs8-`4gP>Xk0+UJqvB zDC+HJB-yqY+qIj37N32kAQc=QyTcgHZCrH*<_=#4f#*jiqlv6?h|EMor_~nbQX0*O zGX`&m<$xNX1I?pP)D|wm?GS3T>1@*p~fS z3}eKYML-V%D~oDtO`I!On2`oA;sazt;sp+hNq~is3`HhQY$y^aQ@eFo^M9sxZ$@aA_?<+JP9@l z6v-MG&Qe46)~5d^1%Xzxp&Yh_p*&M0r3UaKQAiy9lm-xjXWD?|1yCf- zh$Z|YO(^}A=r3A^z=gZw)A*&(i>sq>$=MX+R4PmWbxz z&dJx|(&g{u@;Yr|WIiU+;ggf#r2RAj*LCILzEY{oCm)YW^XL zrRvmkFMS_^;bgeYOX<=<-w1tBnVAZooq7*%zxC4;fljA0ADYmI>+9?9O-@dV2Nzy@ z7c1Kf`1IjTQmdEv4zKP1F`k9;Ri7VKh8~1cPY;bx?tI_jU+BV!L}2Y!Y%&}?H?0fb z`U&3tZcL!t?P7Ct^TTNFTeoig_1w90=QEj1G-!2a0iUgZOi5fr0c0rza(xzqSAZh> zPJF0}-cR1A+N`fF&FJ*hn>c;qO&lAaNy%$|e!jM`v2pU^#fvr3ar5TQxqJ8Sy%b-k zS65dV;k`WY`@oGGH|B2NzP(hh*T0g$9mipJ?%b&o&MW)=LUaB4^)gjg#>U2eRxX$4 zk|y;geWv P00000NkvXXu0mjfb1BxP diff --git a/modules/highgui/src/files_Qt/Milky/48/64.png b/modules/highgui/src/files_Qt/Milky/48/64.png deleted file mode 100644 index df5cee046c5496453456f52536ecaf99c0b222bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2301 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 LNkvXXu0mjfu6HXy diff --git a/modules/highgui/src/files_Qt/Milky/48/68.png b/modules/highgui/src/files_Qt/Milky/48/68.png deleted file mode 100644 index c9b8a7221864d63a4fc47b0ca375ec0ec504b610..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2359 zcmV-73CQ+|P)A1h?artGf~O_TOd)kWEwE$NhIn>J}$Mb~ZrOxvJp zv!*m1uBNt0N7|c)L8y`qg+g%x_~O_+jPLQj@8Dyu&ArCvp*Ej%bRV|Q`Tf54IbeuH zB5*&8VYMc{apQ&qmE)lMs;Vl_YOF!ZUvUEc{rxVS_#ECkX@RJPWxT$O*OQwzZIZvR z1Q2`+PTFQRn;|PJ3o~fk1PDA{+Oo5=!D6w*77$~0 zb{59R$B7Upv32WKc_j#BtTgz-!ot|(YB3s(P*hX|27|$gk6qWUU9+yl+8L_>UQbLW zQyd9NlB}OA0RcWICr4BBsvwR%t1a=L&pNxFd13A0&n1lt@cH@q9DFz&UI1Dh16naL z?7a+P01EW6EF(}^ zzP`Q%@3(E+#)%}7NVJ4VvCppa2p&__@`7+mXLqh`;7Z+=~lG+l2#E*Vk-x96pN03BE z+I8T}TTiyI2>U2A!|2KYb5igmLi0^cO;A==#w&1nc{zU;1QWB9dZ<$@K6jly{vNd+ zRP36QDcPtE{!m+lDJ`Z&$QZnwj&TetYwxN#$I%-5pH__4deHQU<}92p${MsoPgUSz4Jt7!u%ey z4?Mcz%kackz6Un6mI&*ks%*@Pa2Jxu;DcqF@A={J>>+DrL321e(DDnaVKmEsJ zhf(|O_zLo}i$Gzs;ILQ2fcIlqZ`lTqKibM&A{7#g$-R5``2IkzCa8y+1X&Z41661D zo;`cyxHPJ;uoDL$37DGk!z7Nd5&~;pDRkfdI}D6}$b*sGW4S`XPMe*?IMN~@-jnjE z-x4YI(xpq*xNbCz8Yit&&<1BFIDv`a9Vp5#1G6y~{`|oYp!YM+vPlR~6eC1J4Kne2 z$w=U<_BaBh{jf3%7Ob`uK+`h;=pVcSWBy^NEUSUQ=5Cd-ao=G zUVj>6<2v`_RDtUiFw!XPB~od>#j~&+j>B;-L>8dyBgK1Oe8lSJfO$#_#pmn0zCrlq zYtMi`Fvd6P>1bU`sPc+#HB}PodFKcyv)qj$81;1YvG>7N35m=BkB;MeUxGt5KLnE@ z7c+?rgLnIQc1R?VxL_(mB$8P|k4UT%6L9>9vMA8GO;1mcyXnDWUBly_IKA>sUZ4xh4-#5e2DO67%H;MISfgWKL--Hbs&O_g}5;Du1%rA30k%d$)spdxl8 zE(RFW_S@>}YHo>U*LkU_ss+yf<=6rrRR4}h2p32(gmJqnbOiyUhoJ!wGmq2)h;x^q6EJCyA4*2 zJuN4ZZO)gdu@bJLSA>WJb)o5GBfXfQdGx?*#QX?a%kU|8f#Nw^=l>>FANN67H`T&GRW*fHi03nZcZ6R9j*9 zQYLh{TyZf$)mZEqNc+@gP&P`|9;8L!>!(Vb2-;yZX98~d3AnJplK%il=Qb1*cpCoWDx?2wPhxV8+&EG9xQgKx|rP7lNeT7GzU;v$qPY-)(m^002ovPDHLkV1h~eWG4Us diff --git a/modules/highgui/src/files_Qt/Milky/48/69.png b/modules/highgui/src/files_Qt/Milky/48/69.png deleted file mode 100644 index e0ce5b8b1b06a67665880decf41f63064460b69e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2011 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/71.png b/modules/highgui/src/files_Qt/Milky/48/71.png deleted file mode 100644 index bf3c32a23dffe969cfba552a86052bad0aae5cec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2391 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/73.png b/modules/highgui/src/files_Qt/Milky/48/73.png deleted file mode 100644 index e5ca83924e120e204885a787c85e21c9ab5e847c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774 zcmVlWCu~0{{mA zI&4zV#m|44xsXm|X2mFx>!#?cTDiDV_-pRl+1tfeX*>i-2^!EFPtTq{{?0Vjl*<0C zM56TbZ{OeGgFCM}0)YTDO=oXc+_=dw4KlR3yh`t?t)y?KJEiT` z7or+HordC^eeB%FGg7G(qH*p1{rjL)QBhTmwo04S9Zy)EC%GOr{IhAa>L&HEY#Z#qH=@K7aQB+N(wZbYHx)`MspfZyYEdBPFF)T}KL$~&uNZEF!$3G2gP61cXx6G~7>v%a5DJ4O(*hwq6I`1} zea#W&4j$!oo--!kd)`f3Gi5m3A8(58Ihx zl!=aV-t;`n3&Wy>UNxvcJ=|VJ83Mwc15hF;Qr)b3_wG$|cHcgH_^`OhNkZM--Sp+n zi?mYtLkx39LY5`^Z0Z|2HU4HO&s)`hzw2%}#aghju|YjOJ+!vAMki06loT74ogjUE zeYCT)Lxnk8YaI$j_#Xhi4pojOny1GPi z>zW-#v(6!jy!M9~-wMZ@(S;>b=)a;U}*i44`OE0Gld)1#8@7~cvA+i)-e zbk1Zlp2%U0Cj!o-snu>G3JsEq^M=`x@y$SR!A9%r>!Jq(1_lP2=0;!y>U3g5brFHa zs?T7leEj$^We19sPNzGrM8R()CpeDzK$oDTZVJGiKp(#SG5!AM5dB;D$MH%@^m=ZR zPMbN85jz9y2L@n=F%={!v zENtxI;o)InP*}s5R>nEsy!!k5MMRKSh#;csbW*5{`cVjUfl3Sx4vNX!DGm10P%4j% zj0h^)(QrBwtCc2q8~((^ zi`7-UL^S)Ir8-<`}IIlf?_^`;dzuo{~W@cu7adB}T$1yue;& zdyBqbsTLL%u8ob2T{?R7XiHz$UWSa~JQmn|K3`m3UY%&%{m~ zlhig&vS=yEA7~WlraLzUZ(A?=1413w5yte+=Yp; z!NCN}Z;vI7q2)p=z2@bNz!`y4 zmPF8n&CShJ!;Oi?TfO~uiZ_Ay^wsI<>2%oh4JbA?HYORc`(0gKv%S5&LxHS206qw` zyEu+Rx~{9NK4RU6Gcz-4Ac6Ju^;rgdVPs@vNS5WwMHkR8(5(;(1MI#gjwMMV!!YRR z=;*m=n(yDZaUwUx~He72f;n-CU8p-2m(aeQ055B$_4ykPw@=D3i%hp-^ah_H8GChWrBWIl9pwaecXx?R8ZBp_R#Rpi61iNC<6+r{y1TnMRyP4fbc9<5 z1_t=?-rgRc1wksAOm<9C)gX+#FPqKM$;kv^vU^Ks_g{XlZ z@UA}u5ey0<2qwsDczBrC(_|k2M)pH$92^{^@$vDM+KZV5#JhxH&KSo`YLt2Lbh5OfSVuzdwThm?m>EEf5F$Ziu8 z6MQWr%9Sfu_%k~@JJjFb&+mz|TZ<(j?*otwT3Akia~EVN$N?2ZHBIA|#rSROV@TnAt@AAc3V1XGXoD@zI>UEv-|Y>)*~A19i`i& zA5fQkE;5KEcgGzdu;SL%7C)YxoUDQOOKI&?zmj)$XrORx%C5j^U}LK12z#ro}QkbZixOMfbCfP$;wx> zt*=m5vX>MoNs62#nYEInuvTVc$|NPEii91LTffXHKr%!cosg!elsqAtGVe6c1CWdO z78C_E%;G`Moah-cZJjK~V79IR+T@Hrwv}mF0+X(&-E-x70Zh@;SC5kgW9|6o+y>`BzGlKvMzsw&yA4KW0p4Wy9u0D zO5S2yHW@{e5>j=G$=mAK3D-LkKHl(pg#FnGo_P3|3F+Rwduf(|9%C&N^r+fGrOUa~j{ z)H1iIV^;w2iPWjKtdHs?C_JYf7atdM`rYq7+fxI7;84^vKJ*8U|GUh7-k<6lKKE3X>&&XM zng8j*@uHuUl~;*?Z8&5ZEDn=0$8Q<7%CgrdKYy8B43y`qL;!JO={8T5rfwnA+~u+E%MFdv%=+`ceyyh?ziyoUtHBw8GXk%7`A>iW0J`2GaE=#1#{d8T M07*qoM6N<$f{7X|0RR91 diff --git a/modules/highgui/src/files_Qt/Milky/48/75.png b/modules/highgui/src/files_Qt/Milky/48/75.png deleted file mode 100644 index 392792a72a1e28043df0b50d7eac3680e3b8262a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1231 zcmV;=1Tg!FP)MTjkgeTVvI;nXv+Cr)N)RVkJRGWHwi{onh) z#hJwG>#O%ak3hle#GlbM{H{1plyo6RmaH#aY( zQYo*drlx4)FvVR#g;CbD#=b8vFJ3;M_ik=(GE-Ai%SHej8yjCcJ3BwOw6w?vTxb*3 z6DEaibi$z5*VkSym&?q|%q;r=tgo+s5SIVZ-QE2MS%~uQO(7cyh5!JNkF&G0GCpeF zgg$k2bVOF3wXn;TeqrDLrst=EXZS?e`&43iHAtsL0H9D$Pmf&WD%bTc zfgBYeMeX$T)N5>P^sN;DaEUq~y>W1GK+&iu14UW4-IF>oY-?+))K8Duq#nfl2VqlF zlXrf8E+3>eh#C<~6;`sgY8+gO}y;eOp^wENN6%iUfkOP@et!`uePu*-D|{XQd!O zy1KdwKn@QNB@h)eQ3e!Y&-?p(S!D+Y2TQgTMhck}56&qdTv$ppfRbk1o9Ja5cXxN* z+1Z)gLv#UBak8LmUu5)v-d2|a17KYKR@S|flM`=eXGeMjFenuqx3{-aVXo)rXDLgZ zU0q$3-Ov@qfSPde0xd1EfYrvv#v};N4~kw}TU+uo>(GlTuatz_{r&yETuz3gh++u# z(c{Vn%IX1IfN_t-kB*L{!XqOio;a=f3yMZBWU(~G2f5+l;i3Yh1}S@VbW|E;Z*NaN z52!G27(Eb1At*v&8yFZU+T1iyTBSeNvjB?YArb`OdwYAOQJjL<s&gKm~fv2G?wSl-%-mumIH3=8V z7(+utlAkVz?l^ymPy?e;6VL@?5<_^z*iJKnAQ=Rup$iYV&_sbR_j276JroOecXtbC zmm&&L5ye5dq@~`AMx{W}S`drGhVR!3bs{5pTr5gwv}>FMdOM0y+ZW1zqyFTy9PDN14UN tMf3ZC|Na$I6+jh06+jihKO4UV7yw?yVT`<=UG)F}002ovPDHLkV1lQsF&O{= diff --git a/modules/highgui/src/files_Qt/Milky/48/76.png b/modules/highgui/src/files_Qt/Milky/48/76.png deleted file mode 100644 index cc2d75d7c0c5b657b9952bdfa35dec8bdae5915c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2744 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%0DM$Gca@8MzY2oDJZ8|E7^ry3&#WddLPkag z2wWzci+}%XHJOl|oekC1)$qNUUxGjI0ycepD_a{glw&ynx<*DuECgVy^i8O&sDa$v zTnaYu`Fv1U@O`K$`VLcG#~Y9)mCS0j)m!jd_-nMJj|nlCY=NSpA_lO&W*-dA^5e5ho!2Z_(z%KPF03v0Z zo}L~n-U}cvFOR<4nJ4H@aR2^&sHmub!-o$ux2Ls83W%|>F*ttwIHTOgjT;$-$P>42 z-GX2+2o{UQcJk!O6R%_aBmh*<`XX|;A9J+W*w~n)=)HUQpsTA34jecD2M->E+qZAS z&6_t_MNm8A>&~4!w-*&`rgdhs8BU)*O=%OXRx1nGco`obXLIOfyVL31`MLm-?gMyO zSuk+L9XocgfNN-I0He{!l%#U?>Q#8~;6Wlgkz&2Qy@2kwt2{s#@n(9jsi_I*93Xk5 z{Duu1psK10o)TvVp0F|dtpRykwNO`oleI9Po+H5#k^SX*D)N(ch$DXh~mdDF4JxE$-VG`jj< j4ORiH0(iU2e+3u-fWqsR^AJPm00000NkvXXu0mjf=Qob^ diff --git a/modules/highgui/src/files_Qt/Milky/48/79.png b/modules/highgui/src/files_Qt/Milky/48/79.png deleted file mode 100644 index 9e311fcb95f52a162d06a18ebe8f77424f036d5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3120 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 KMNUMnLSTYXndTn= diff --git a/modules/highgui/src/files_Qt/Milky/48/8.png b/modules/highgui/src/files_Qt/Milky/48/8.png deleted file mode 100644 index 7301dccfdabfb593f337a910dd19e19a32302385..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1750 zcmV;{1}XW8P)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkUTuDShRA}DSn)y=`XBfx%5BL-O3o6sr3LX&sp<3HI)ml%)pxhM4wxe`JC4f>b zS`V-ys6f!^PzMZJI?`z!jzMZbSPn%%FnHjBN4)TMo<836_(Y%nnC^t*Wn z{aT%7x_b4h!4(4o11_;xEK1ffA({wq<+^LvuGLhxl~75;77?`Ztc?;rFEeka%BDl~ z{P}auc=YHI-MMpzu3WidVf0=LaPWFm^ybYQ`m4i81+^Olg|(>yoEy&?sM36hUcP*( zDX(6=q8Be-(Dm!rslUI!*kb{Hg!;R8?~342VO8s4+V^(~<^Ho)K?)VtrIP8K_5w0^ zBDZeca*%K;&ts8_#zMad$P5k+QeR)+SE>ZK z-5H)feM+xizqVIU{%@@2`T!1Ar3Ai(=h zE_DsGP=C76>*~EEM1z5(ld?siWU)r@h~7FE=1rp+f7QCj%| z`tkUDT3_}#Z9W=BJv}{ky1Tmtmo8lrbai#n#fujO7cN|&&dyHi=;)xTs;V&pNGQF% zy*j4?ak;Z8saQwJC9~*TBU3tyc<>?~tORS1jgS02ev2|Fi#n2ubQF^puGtfS0N(`X z0@1s|DgIzMB_58T#3MQpfDkIS1ti22Br7Sl1q@I&f$NAb2&d>hGXf_NxqTYxf0<6v zyQkBd+?f=!eI)q7`9)Xr&|3EaMY8_mzT zz_P4Rirf)KD}SCQF1%{jbRnc?A>7I6y|$nP`xNZ8eJ6gi+g1i;S8*MY+ru;!;C|y= zU~&2IfR$c+FA@p-nb45wladMYi=m`Y0wQ|VA;j%H5)BeF{Y z`QX_j;ZPRd_FIw&N|5;uNElD<_xnI53vjyiP9U>1krt&*p>H-R;U>otC8oT92;97R z6Jv#Y0iK195ik_R(}E2jF(2DVwE>Z$irk4Wu%$4LzFhYq%?IDTV(tCf#|Ui7kEMA@Av7=95eOBP2j&T2M#?wIJAsY+W9ZYE4`@!T zBaoVK0?5as1#*t0(cJjSG&ha|w=E<9XQ6#XyZ*6V$P5@~Ui+q`T_wMqGR4_C&QdN(6Zx+vqr#gU39Hg)H0;f;|>MWl9M(BHjqe zWsnr)|Kr*S9#Ys&^yn26FB+_}z<3g#hN~C z>B*BP_Utg8h&vfiz~DKOZ4kVb=RCf>=&q})o5YI;>u3pQBEHDr&f|$A_j8-K@{~D5 zlRq`6t*xEJ{XxRC0WY3`@mW`4a?U_c?9e06yN5&DvQ3c<jWA9NN^biX8>f8eP=RRm}TiRQVOhQJ8D&G&sKTHY#x?mImipqKu2#Igu?%38YYdi zAty%yEn+g5z-TbDYk96>0@JW!)>B-z0L<9|5ZA_#;{tMBka(mG4`vf679imz#R7B< z`OYk0GMXU|`H%PP$OV0~vH%L%j_g2(EJEQEK>XJ%AW9h)K*8z#q`X-O#8^{Vx{3Rr z13)dlZA8Jm&O+idMB_tg)FBY@M1(oC*=PlyYcl$Yt7&D~)+Wf&krjYP%b#gMYBbr* zPG*H@Vvt232`Q!cfq>r)A+2l_bqD}arv`F(7_*%KHD6=1I60@y2V{*%oC8r7bt**= zfZghXLhm%PO3mtdJGfi|$Vnpm-QQoX2zPs$1^Z3lj2+-9vH*lVU>GidsqVV~5_aJD z-tOwhYD>qjs0mDDp^pkG=v-)D0DjklAV*}FJY_Pt-EN3RqtMsa2U5S&zi;2Z;Kq#` z8~(2-M2BFD)p% z(i$_07EpP*EY{v4KN(hU|irF`U32cyMU@9MM1}}7Vb-|^?A;4U$CN;b!E7wJKpTNNM4RpYT zD{TR^YIEbs?^fhIjQ;$u-wvPvb<|l=wMl}I#$EP&Mjfg&sTa_MqA~0}195i`+*e-7 z$_ZUy6^LR%K`%K%kx$@E*HItOCz_F_!JGlu+q%Jr<~~TBmZo_U-f7FzwN2rZ^#QUb zq=4YO@W7n4u(Y&V*SM4dP*k5){6<-emm_fU^kEnZ57eu4%^mGOZX@;hGtt7G4($SRT5{dql3 zfZ-B_q?V8t^tq>~bv-$x1Ys=PhNAs?6q@8Jg?py1gqisZVX~);&2@dS6Jq=jTpPRq z|G4r8xXPUa>LFz1g@IrqsOQ*lf=X_Fw~%nps|tH$67ymh$DpYiLWPN)wg<&~Ab#iF&qWTP8w;`eIl*r&<@DG~U+z7F_<% z2hbn63bP6pLB6vD7SH$!T*B`lP2hCl=`BGwnGJC5S`Xyq6;|zOeWGI9iUTcU0)U*Y zBB2!k>W%{bCABXChE!GiPFON?T{79VZNeU4P)%ww3Vo8z@j>_7X%cjM!Mq zagl*=xjfrHYwKlH0N9YM&~_GRVroc4HF?Qu#-+|n6b0;YlO;YEy~g}PvzjK5&f!$8 z!${%TX9)oq4eZAL{u{va@v7bLuk)$9dAApZPUbl^8x0hyfT^U3uFa3rEt@d5iRq_ z)330GereeT41peW+Ntox`L%5HaTnld*M2y8xhcbeZ{-WhHDxW)H&zQI2U$YoGqR|l z*Z&$-j-2}iymV+ayocw3NS{8}R9`tfe*X{Q`9}{!NnZJy0t(L8e;;0KG3f5 zKPj|I9rZI!%rqt?7pDH;$l2H6#dlXh-I1@c$fNrQhSJRsH!$mr#{c9{=3_kt`2y{y zN<#M-0SKH{F0h?7A;uyOSwd1R!Ym+Z8q3t4j8*1&c=P!4u=~(@wl_qF0~?ty-Mx8q zt&ir|_>9$VB?^rV0EW?aB@8MPF#xLOH47*{A*X;yF@1_Qd;8Bp(}yqVVY9$BZFH?m z#P<|HqT&;jnRl*fZ5gWtT9YvwARdooL?NDKOkML*dIZU|2&B|8nU)%jMv|ypv)Zhb zn%l=*jqd*X*(RKZ(-sj$+T)C-Ik9X=3cP0Fv#_A#AytdA%3!_2Dmw7~zdQ%MH_xeg=@v;$QnXJ> zDw>&({eYC9!()XO`JCIhEwh9*x$61tHrNI|8~NM3Zm zcajTCR#U@IzS+@`RZE$AY<;6Fn1~7WL>a9W#GAlurT=>Y0^-wZm}I$}q)8fGAe}}E zIc0DcIKl3=HvDu$XHCvGwzf2u1^Kv8FY-!?S9_BY!fO3)8kkQJoX)KFHz=A%g*2em zYbI=KaZAJOfFMW*g_z)%BsrrwhZJC=StDI=3X>$f&1nI<$3|4$wf9?HuTJ>p?&kX0 zRN6i*@Zt*y1e2UJqDfB!0O?dAp^avupBZc}D+PfTT&`yClNY=1==S=i*JslYI4IF= z$IR&?=O!T$nC_G;XQ&J}SxiiDqshqL;}Q+nXE**Vc=5J=YA~Tc_Sp1#DQ!a0SV21| z>d_J+&?%zPse`+7y!5}13Ae(_nH6z002ovPDHLkV1o38 BOU(cP diff --git a/modules/highgui/src/files_Qt/Milky/48/81.png b/modules/highgui/src/files_Qt/Milky/48/81.png deleted file mode 100644 index d9018259a3de3a8e7de67123cbc3ecef5904ee27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2497 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

PNX$PFMuD&3y*) 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 LNkvXXu0mjfg#Ew= diff --git a/modules/highgui/src/files_Qt/Milky/48/87.png b/modules/highgui/src/files_Qt/Milky/48/87.png deleted file mode 100644 index b1bc27b7c09b19330bad29581bb580954dbbab95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2337 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 Hu0mjfvFUa> diff --git a/modules/highgui/src/files_Qt/Milky/48/88.png b/modules/highgui/src/files_Qt/Milky/48/88.png deleted file mode 100644 index cbdca6af473c7d89fe4c9137613470960ed047d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2072 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/9.png b/modules/highgui/src/files_Qt/Milky/48/9.png deleted file mode 100644 index 4ece823fe32f53e149fb13d7a9a7da0919b43c7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1980 zcmV;t2SfOYP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkVLPnA@qwZu2nyn+X=WyBnl@^pm73#YO;aadq^3=l zl3Hr%jA@ho)%Z`7GfidFnB}N|LMto_cNbV#mdEni1!36*MSP6TxAQxAZ6#dpzDV!P zcQ|_==YBtD&+nYyo`W9%zh_x(pL96VMh6O6GlwQ-!~`n*oE*8cdkJ}SdQaECE(m@d zaL58k8WkWI6Xu9bXSbu~)HZv@*g!>jc{xeRlqkuhQ6(0w8!tBgv;*b?i$qhFD$((} zqsq$4oXTV}IVyok;TFJ!@Bl%-ezMr`O&siBjzi!QH!e(QLhrlI?S0`+7PzZ~J02te-`~dT1gnN0+17R?F}6 zMECC9LsL@|Dl02dQc`kS7JzzTs92jg7PX&7z`8$@8?zl6!%fi9zN51zv$5(Kk|2Q9%&L+Xc`r3${1t|G8N~Jxs3wq)Msjo1x^;U;aiF?=k^CE?b99YhEjvb_YTGbW zz8?y6LKrOHZuOdZNfZ}FMMbCE4dUMjVnZZ1WKI~)%~9*}?!nBGs7!zaYTm|LeRC)( zHV#GAR`$%vOG?r z3&V=|0m*&Sp|3+Wr&+CKy1$|J!$^{FV;vb62bbWpw=;sN@ zo7)>KAA(`YFrPxOAa)M$!(cz*_oyIIH@g>dX7)txtX^Qr8aKyHN}|uN_2$=dUhN6p zj2_TV>kjQJ-Jy>kio#3hU^E&ZQ(J*71v3RQiKPvm0h}Ebl$%}h6ywDk%37wHUFT^L`1OYk)I3Ls5t{xWzwIT?a z6FPB2S(CbOa|D%5aGMt+bM_$qnXb`2nd3Y0XIRz>)tEq}GYI33H#;8S`+&by@pp*r&S{(ZU8@@&hER$?oS&XfGbfi{et zYCnhx;zCp)k|P6Ca8G-w(JL;g8Gg{>*8?u<6u331V3Q zbflg*g7e`4xDd`vF>{*jt9kHgwOZ(OIzCg9GJZLOJ&fAg+Q%j!5}osS&(ULRn)b zxV+7po}Mn!j3EIy1MwydrSbG0O5Mpkn|aT=wNIRynwn5kQ^O}4Y7My{p1{zk!}lfZ zcD{_R)9FODS}nK*aP{g{L8VfeXsc5SrnW%ZgK!AB^=CpygssX!q4>7h84Z6W()8 zJwUJnrCR}g(}3K$ath^2-28`v_JINX=kDNS_u=iy;LZ?~m)9Mt2#%mt#<=f$K?ot_ zy&&4sS^11hBj%$~?Tj>|&UYy+pfw0+ZgHD zi#g3?KrQ?Y8};79W~7VIu{Xj8KhaJS;+8u2Olds7VBCB8^^z9eCD|Hx`UiJB-oIsA zL5h%LNb6XAKRy629z?V54-D&@maPr1;QYGMsOjFrW~6lx0~Tz*y)&6QqD;1%P0?!8 zI9_7?rJ@$vAvv0N6;v6cylw&hrsDmHrp4UpxHLC#cO;fS&z;u8d!ZICz45;mMgbzb zL!f6YSU;VcI{chI+AfgPT}oW;5N6Z^0X4sf8giZ4IXk{kS~a1#Y7O1~iZS0nq33Q9 zhai}}Jh=Tw`_sGfDJ|nkniTFSCRC&W1&7ty?F2SX5 z&H6f0N!cdy>xnHXL>YS^Iw;%ba!gngx+ZvyI$l3;%*}WPg^n8)hz=^rS3lK0h}&K; zrqNlaxx%pDqM37D|9cmB6?XQ{h<#lt4J6lEYYD7W+ zVo2a6)Da&5yfGY9MRY=1E}4l-&lemJ%;P5}_~~W3mKbtC zoIWVF1p)di^LBXRSr*9ram}qBqAAmy=)*p80^u&58NFc`(41-7cbDuk7rHUI? z;?fH)2qdZvw8B->Yd!RdIkd!(L#G!Gv<~9Vx2);XrlkpTO(7KvTLrX2fW+$osLWsu z@t12LziI=h&X&ILwgY1MG=gw_qL%Df%^%#BJ!b$94jAE*&+ivoPIl!CO$j2YDzoC7 z7Al!woiuZk5UT=^qa<$TX)Ob~!f=`x_(I}c>MIfMdWWV^Ly6^=iwMHCL`73xE4bGf zSs=O_K@3P^|GY&^wnQVwQl_y*G%Py+Fa^{AU?Nj1nb+$BysXWyTU*Xmk06L5f>?UB z=vab5gF}0-bMr_i1RLp>CQscgTonYqmbP@}^RtRG^BRdVKZQKnud8>OEE}O^Y)dR* zbP5efcE+sI$MRqE{&{iOfi+KLmo$kKNH(`XUC!}PiQslq7|Yy_-Y{TK8qmIG_myGvKF!}!Kxah-U`+|=P#JhiM36nM;v>0 zuh0-;MHyneGO&fGP~$yDrZOt_o;jBC`oO88?(p4ykUE3##1`tXGv9D4k>ktfXsVF5 z5{qw@v|xo!W+RO%d4ngb&Ta&;T}VDNM5-X39wKo$B*YAijMSYsQ@0TjBcs9Q>+?XW zfZ8TJnRy=O0|hW-5s^I5YW)5RYNnV$I=zjS==al!dMn!soe)G?n^ft}dF1`!2GU zm;as`B=JC0nCTyq)xrg%I?rubE^_Dd3YIcrm_QMQ4k#!*)@5d8ifR9$ws~Shg|1=Q z{q+SlRhziSf&`cA4)KoA!VEySmVgk8lFjTDoR{~LLlx_=3#ued6K4?n!L zVQEUR5dio80`hclKvn|*GV|EZ)RzXG;BW()r1;Di)K?uEDHxHE^d%21NIL-d?j&kEh)%&*5*@9>$sez=mP7M3 zZ*>2>ha?fmL=R3kcLW__xPNd!Tg{=F2cU#PD=0K~kt|BSUbY42R2e_E#T{Au2rr_80eIKRT=-zo%hb{c4oKR z2W{zYY0;*}P6=ovYPS{y4B9rq7(*hvfJ)Fx8)At3;TB^sfoQftf+A#FA2Bph+G-=E zN>_s>NN~3iH3hpJA=`GJ^PW4edye0C?)%P6X}ePmvwP>h&OPV*ec$hUoO6c>A>jS| z@zWP$tSz05yz==t_Lc+&Nd_!#-=Y2m{f*&4YR|SqsdkJ<%4hOx5dfY%v16yg*bV?? z0K4hag{5z19(qQVP!NC^!?BM%e9Mc6|K|X{_tNcg#*{rM!3Tf z5`uvUBxvss8!`)+NuPyky$lP5^DvV+3)8q6LS*oJKki2!x$Riysss4$vF&?M_+fk~ z9t$U6bN_Z&)4NFuU9J}7#<>QSy6RGUC=dmO`5+REVcQO<)=Kd1+`nM*!ng#4hRJ~7 z`yajI<$YHL!0^#eBvJ5DY?~x4-?-sUSdq963grbTma|Z*WNp5C*dttDI36g_5nckM zfG`4@&P>AJQ)37uCFvyu-!**a*hJd^Jo@5Ck>|>2C=iK%7&X3e&BsyjG^CLZnpOil z5IFen0wCPSLRk9%(yXU*6@-FOm^?oY<7Zw%s;MO0(06wH`M{z9Jo2j>2J!xAckCLt zdE;FWizHw+`;Ht$cT@)EpDs58`x{)Mn}d3R(6um9Z0V9Updtld{l^QCDO|ui{J`V8 zULU$*01qG9G)S}|;cwk?Ka^_)n9ogNdqqBD12FXN>ZgH3VCNiNXfFurcWKvZ@F@Wp zpF9q!3#aJLfhX>o7`ki#5B>6bL->^_{C$uu%|St3a0zeH#(dtQj<-Kc>$dQw6_D>P z5Om%fUyIJ(34c9(6jC#%==$CteBqxXmki(=zt}*@=wu?cBECI21i8{I3ZHJG`9=`U ze8A-gLKv>>)IPT?VjeF;5aB=!PK-YXnc_4(xo!U!-x|9l0K0#_ejH1r7)S| z42D1^0hq#ig>R|-1}oTfY7T;1mRJ_=$8i-n{>HOVt5;IkddpLHznyUcP`u-O{ioOM z!|TZpto|4Td?A?2y$c$zL7g^?ICN}ob#t#7_vu_~v3c5SMq9hzy4{ztT}c01%Cvdi zb`rJ^xES`z>$1Ru2iC<=lV^LDtcu_KpFgbM&qL}oqUwsoy&&xzdRv2n(fd`)NPj`2% zh~KpF4w#xhZG|>wXkQoHc4Q4phKtE7^@JKMA9M*$t|eZJ>`61UfL5D`^a`Yv9}r0T4T>dZ#`tC+1x2 z@G(F1cCW!KIE_wJMgCWHN0ZK0wH*wPW5J$932%OGymp5y0PX&(RzQ%~j4g+GDAtj{ zLNH8JzsG!DaqxejE7}8UC5PHA+MQ5`TGatU-+(J^nFNIcod}?4M60S1iEL$=3M8K- zCd6aQNZH9RKXYx|nvvc2!_`SNS3DL?$Rdq8pvKa1z1){{LZ?=Ed*+1b5)|oZOlLIW z7LB-dTr6iG+R;VIrYFh#hy4b9lH|wXP>0O8gjjJouVREu+kv^SJpf2p8qcTsT;+r% zIG7<-gSrOyVS ziLDg3^$1Xve19(hTAM5&3=1e4a{=JOZ2>b+)U^uue124V*n|Xxg@bUFaG7z%fl3xxpIK2V{{^Lv&Q0V}90`KbVkUtonw5RYLGKT7W22 z)0|UHXZZ~l0D*$n2!O^d+M5*m56MM0N#nGtV9pK&)A7g-9EBQn(k=uU(s;ST!f;QWCAXf?Dzp-Sh68>>+v4pYPYTuB1prWG^~ zFOBm^XAt&{es)nHe*fCf;Mm{xd$jHxR~MclqrkjWSST4~ggDrAMz-r)T_>5s23D-^ zfm*q)^PDkzp@BK(y2e2%Q-<}+KOzNawJ6c-9ZI@51w}Rs{-DnlDqZc3C7906B~TM8 z%$`k?6;i)`Jlo%NPK?MO!&Eqsohd-q(kPURl#PX4j@AF&xq!YRT_KoKPq~&1Giq))kXe%Gp$!z)l=_BEQ8M0Q?P|b3rry@r9{eytgkQ0m#H5 z)3Ht`YoUky*Zl4wgaxG7`5xp9LDhJ&)aNuSS42gbhldb@0x7yjn^#S~GgQo?E6 zR1Sg>zZ^$*GOjzZ6`f3{<5seh?L3?F8QE6jR_NqbU~q(QgI2x(gTj0X)SL{RBS)Vs zj9qkL%lY}tLy=M34JKB0LKq`XwN$rE>!J{AqWFtAfoQ@d;i-WXg+Z_*04m1(?1h3% zTE|98Tbkt%e@km~LFP)L6_RlT(SgoNRx#@&qr$TsTCmG;0C%Ify|qtwMgb zL?9r|+QEai3%u3e4T!BC^Pt-+onVIJejcAQWE;C}{3`K+3 zp9b0K0%lu!ZH4t23V;0RN~SF@`EGq6un)%?CJlA=bU-u_!8)ExYw$X^BGh}8&Q42E z5>xz&1fU{ia##iE4lG7q{!wjj+h4QZ@)bWVWsq|xeF2{Y5{?D+>J+V^p$28u*rHM; z%aO381bUuusJSwh2&#M*&r>MO(1{<{#;)p%?(O&c2Ik@p^2}n! gv;e%HKmQeA0Mb~JXo91tlK=n!07*qoM6N<$f@&v6&Hw-a diff --git a/modules/highgui/src/files_Qt/Milky/48/92.png b/modules/highgui/src/files_Qt/Milky/48/92.png deleted file mode 100644 index 48e5a942a59bf9409b33567ea56d8cfca9af65df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3046 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/94.png b/modules/highgui/src/files_Qt/Milky/48/94.png deleted file mode 100644 index 1e6999cbf40d0c991cb65e7c2602a8282b88e5f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3050 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/96.png b/modules/highgui/src/files_Qt/Milky/48/96.png deleted file mode 100644 index daad4ea7ce061906b6138d3f8eacbd0efdd0e40a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2982 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 diff --git a/modules/highgui/src/files_Qt/Milky/48/97.png b/modules/highgui/src/files_Qt/Milky/48/97.png deleted file mode 100644 index b76ec545b07003b1d39fddfbfd65220a8e535855..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3074 zcmV+d4E^(oP)0+5hgI{r7z5eCM41|2G0-3_i<; zoBjxba83qWxBdEh0fc%SuolmBX`XSb3)^8dRv6En)K<>_efVtON5&WUpHB(km)owE z5&klKekm&4fCI_;NcrQLzjNMZz&i2Wo3PiuYGHfVc?EFai&x1a2rEGln^AGuBl%#Y zJP~RtCPGDU6jYXv0*~A0R2e%1u^#BhM(6In-4IKMNecqgj(V)WYw`0P|8oFqpI^|5 z^38%E$|L1ExM0*Q-c&UM$xI9sEeokkw7}Z{yq*9^E-&~zL2!u@2l7$(8*r$%lPhBY zX}NpJwytvy;GRESUXMy|AdZuzXVfi#YPl8?sX-V_^g%{Rf?!ca7^;1aI^60KdMFea z4rM+WvPv5Mwf_Y;c&wA|g;Azu%~v}%o-+VHd+v)(D6`HZ1!Piu>bT3$ddDD=I1U=q z48`%UP)K%u?OtzI7%_4G)b2Q54Y|FTtT>oK_g@42yyL7Ztz!`izAWd391#6F0|jHK`^0XLgCk+q zq%T9zFT=YBw$(GxcA&t_VmcP}K=bA)O{fl6e$mWZkuE17mK+$Oy#B0U8$&2aB9l0} z#lc~HW}P+OFZWkMRrwe=(EBdDz4y;}N87mW#@)+HrqB3B58#8x(+Vfr_~<#I!R^r&HZ(NJFRTVAjf&$B*VV|f|KqEjIdnsH6T zLI=_pK8$t41G6SCf!E*LOzCzVKHOL|2EMytqKw@`p|WcE${9C8Pyas5Hi}LchX+K5 z@(v1_st=t#4E2B+#CaY-`&*Q9yCkgelVD%>+pzzWT_n2vky{RIG{+J1IJHt^AYU|L z4yR2j8^`RB%{5G&lIVkrM>N9NVbdU;P4H{>`&Pr>DmK4+7+hF=1&j_&)#WkUsD=#P zS8ei&{W{6vIhl@OedvX;)fa+GkVwboA+u2nfW~g|`+`s&sDw{W?y(Au-2R6=*L{t~iE{xP|F4t#y~{c!Qf zMs6`X?Fz|H8@&K-n*9KJXOTVuw18)92D4EO+lj~#?$ip@Pt(}4LI4^GeLh)2cqid^PvCUo`1qqZ{7*vRF7jN=am9zDf*U#k9{T`$8uTjt>s^n;S*-`KN-%kM(vbse-JI?%*UL6#(AN&4NDqY%R%s8~E2 zgTOE!x`^p(5i&mKkf1xnxjrd)oQQP86R)p=uJAkhb0x|j?BAt(oY~gU%(l+0C%bNFtqdn(2{|BpIeI4O3lwHsB{Bg{iD2tn55i0Tc^F)h%UVJflc$up z?ccwJ7vFyfTyB?6O${8J2NMI+sl+g`ko64!9ggWVUTjas(oK}EtjuIu-3)~y`tLsS z23qmQ5K($Ta0xt12w+#nd3@CUCTx0b6-2TqkJE6Hhn&vVLpuL;J!q?v3RyCm=5+c) zYk$YntDOK|!&6f-k^;$(Q{%Lvr_DSX*@K{O@Ca;veGPoryB$QA=vYUx0zHy{`fsi9 zLC>oQRJ4NA0hF0L7EUS&9)L^+r`E9yH+I1#<+VQ@HsC3f)s8UwT` z?DK&jY0!6Q0FoEg`BgbPr5cv{M)pIUoCZ5(*J-z9DTbiTmW;5$JQ8 zHjfAn&YaE%Y-m{6HoFS(A7`^=KE9ASj@CzDU0Z(NoYXfCah_+}v0SPFj4<3utjn3!Wk~0e>gQ9E1 zzphUbl{Ur)k}z;I3LdNtB3h*og?M19HX3QHU?Asc=6p`GQAqMiAo*O72qz)j6C>Vt zqMnUsyuEH7SAEaBfeIX1q;gD%zbubqT40fOxnPALWX%ESXGAB5t>~ZXL-&GjxhTyxBba3bgaG)09vLi!z`d{jM%{QuEIiwPHJHCbtYriQr%^aUt^FQ>vBz;XqwtJlJBZu3 zc7(0lf2XzoU;bCZel#`-1b{q4pym}@Y2vGBCDpNG1M56z|LpNuKK?7f04l)y=F0aA Qt^fc407*qoM6N<$g4!j__y7O^ diff --git a/modules/highgui/src/files_Qt/Milky/48/98.png b/modules/highgui/src/files_Qt/Milky/48/98.png deleted file mode 100644 index 5694611ab0cadee3f34f7060939d22758d2ded0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2981 zcmV;W3tIGvP)6ej(tvnYDZ_<0kt!9rgjW!wZ0faALB?{2@Eh) zL1LLvX{!WUMMOyCmBfT(H`(mt?%sR)|L4BWy?etWN!#(l-E;2QJ?H%2|9}5~{&Q{^ z=NvxC7b5_Su?ZQh<+WvvI8p-+7-zsLf$GN9fp-$;N>F|E&0E?hVm=}JcmO>5>YZ`a z!3ylZ6a{X?fxsBaJ03lA@8oS9_%^Jy-G6Ia;(rQY{qEaq6=3TaQ&y1Bv63qIRJ;zR zR$T#QRIjSDA#i6l=uLD&Dw`1H>AoYN7Bc255p-ExNCIk?#nKBoyoq2~4B$XL}VY~~v3e48KcRX%s+N^SjM#`Zq5{I&A z6^=0o2aaw<%XE=cT{_pBANtDc?GpyzXIrm(2(`X0hLc@C=W9@1c{!XLJ|l!Tc+Z5; z#xSFgWdPtN2BC2XDnW?HrV1LK>OX=&+JqI+5^H|3^oh>#TMY)1B9Dx93r7Im|Rfy@v#dN0%LH zzGMKaUYyl}qOHCHgSEn^20apP4)xzIgRA=n=x_OyTLbipL6I1G<3MIqqx< zKPrfLt)SJOgBu|+(o0Swlh4;&XlUQ>4qm9_Y> z?!39s4_dFlT!!tdsh$nj)Gvaw1ILVLBeZU5-mw6c)IS!!3`#;V=pFtL!ojFJ2yqy* z1*nXM%Y|dVbz~b144ox|);+cSc$YO!(4~BxucC?Tr!NqTifTHo0ulh14*-+|E8wor zt`+@`b_PK-YN_HB6r}3zDf`H9rJzy zo&5*l%@Z#|PwF@XFcU3W17triDW!AWY|}+*5sOX|Ao>PQH@N^{r$#DcrEvj(G-1Sj zMe+kCtpySQUAw4Myl(P*aUVRn3--MK3@}A;{K4bA4jA1AL1k7Q!R)mHK1Led^K=c# z+#WRP(?C8Mi9{ip`B<3E(!TDqfLV0{K?7gPHGjqum_6-Fu=mJw@Xo0n5DG+mS7OT= z4gd|3NbNu%B>aFI+f^IDp{0HTMZHI}f+O z+2s3TT$dH3P-+39Iit}i`2YD!K+3+chYN}xYRJ!d0geZunv3}@SfO#xXoCk4Thw)xFUG;4kNS%QLAN|?w z$YcS@DvA~q@^QlgwT=^GgN}mawVc6evAAiVn}RAH0CFm=^YH_qD-tL(g!8P(_UcXl zQAP^@cSRv=$Yrw*I`b5SGx34{btlvJY-0EX060DH9=v{Rv!IRaWafCZRaU^~?S+Y05l4ipMG zYtBQm1wNuCRn3Z(j`pQo+8J_7Uh{*1Fz($dUfT7+*|*>iZ$5yD-&rw{r3h!|$=!7< z$kA0bS8M_7Oh!`wq`z~u(me2s@gM3cpUYlLXoC$y$R}c56 zp?q>F6jaUj0hv17^Z{l~JW3RL`@`L^<&8%msdgJlpT>R8Sv}YzhqJ3mA(^!64sX3|X3%28O`xu2Y|0vX#JOwYjvKmgD`zI(0^F1Kq z?v1kN+|-Ht7Pv?>B)FeWWa+VW>-uC@5dc$|hqXL!9PUSFu8Kk-uL(_a#sFmcFy|C* z|GO{2tZ56y^ZtLo1P4yM41rL<+T|5d#dA#Ls&LsdrX@n*FiwPfI5dgWDGSpxB?F;LTadvUWrn9o7_1&-I{{KiWeIc=biU^ocD zNB|N&DaZ_Esqw2j9!s}f^x^K3RnbjYtcXu5hti4&rY=Ovhm0Owty9*Av<1F}-HJ{f80Sw(TPzQKVDVw3t{(3b}&o9e_V`n7{5vSkH0fq=;fBPh|yXR zj0DA)1yoM5H-$}$la1y zSWqES8am$#8j+CEolB_}RfOO;*28RDl*EZ@M|Chg&kgvuStN}-%<34iwn|wnagV#;LJN9kj9pjzT2JlI~ b{v*Hu*!-*KWSWi*00000NkvXXu0mjfW3H9k diff --git a/modules/highgui/src/files_Qt/Milky/48/99.png b/modules/highgui/src/files_Qt/Milky/48/99.png deleted file mode 100644 index 2cbc9521777527d4d3dd4b4ed38606e56edb0f1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2956 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/101.png b/modules/highgui/src/files_Qt/Milky/64/101.png deleted file mode 100644 index 03fe6ff94026ac19f04fd0c9da285c2e2e887d6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3948 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/104.png b/modules/highgui/src/files_Qt/Milky/64/104.png deleted file mode 100644 index a394b07b24f09d60392f1d780b3a8306f8cecf53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4086 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 ghe==Yu-48UO$Q diff --git a/modules/highgui/src/files_Qt/Milky/64/109.png b/modules/highgui/src/files_Qt/Milky/64/109.png deleted file mode 100644 index c42d2d5f3fc7b666a1b43306359650866118f8e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4579 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/110.png b/modules/highgui/src/files_Qt/Milky/64/110.png deleted file mode 100644 index d75974a6e2308a459c00c4dbf138d514c612235e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4487 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/111.png b/modules/highgui/src/files_Qt/Milky/64/111.png deleted file mode 100644 index c61e685766f084f6371a14613479603efb042e89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2999 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/112.png b/modules/highgui/src/files_Qt/Milky/64/112.png deleted file mode 100644 index 9dda7f92a2972db0c0f6a78f552bbc1104c44342..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1640 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 Y07YFE?=JP-K>z>%07*qoM6N<$g8Yy&kpKVy diff --git a/modules/highgui/src/files_Qt/Milky/64/116.png b/modules/highgui/src/files_Qt/Milky/64/116.png deleted file mode 100644 index a3227c7bf0d6a90b57aed00beaf3539c59022de3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4202 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 Af&c&j diff --git a/modules/highgui/src/files_Qt/Milky/64/117.png b/modules/highgui/src/files_Qt/Milky/64/117.png deleted file mode 100644 index 91991ab67253e3bfe3dffd58008b1f01958e3490..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1201 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+Sb{xW978H@y_vB)FT_xwEq%|i24vciU`_UYoFS?Z>tId)RzuMdwOYXJ+JhAN%osPtBLg z^XE+pe#^JttN8Qhp7ZWwB|M~XYHb%*Ib zjN8IBNx4PtW!}|yH@nOBay57=Y!ROu#eT5<~C{|&dUzU^$qZhXMxOVaOm z*GiIi9h|s&@$|(O*?I@`S{G*uI(RJ5;9D4;qHFf`)r#wX6@D2>Xd1`dU;FX*`uu4B zd-v{doVG_90J>rs?KPTthTSl=4|K&6HNiaN`OleQMa?|+26^j?Pj7tv>vB+L>$j+9>=#&s3)7qq z>b?EMo8hCR+;QmS>1`(4_be_E43Kt?sNK@C+W0{IJYAKug3B&nleTFnluX#4V|?IT zqPTD6^i2vUxGzoEWX;_c^MA(Udu;|C4Rftr4Bs*8*i}`7MX|2%sUs=tYlZH(>}68L+6Q@l;yMQmSF*J*>$vpxe_$GzaA`#%=%<8wIPm6 zb%C_;HkP!;d7R05uLGD3Hyudu;z;Bvz4CFDOvj;)RpOl;uHgY1?)IH&7VB7I#H@v{ z{7-1Q=5Fyw>$Ud@||-c7S}C{>#=gK?MSTgHVfPOq*%YQ7kF z>t&WgPM_c@n^Xr~n~?n`9E~fi%MNaSEkBj|M>Fc_@}5HwL@ zev!q|Eaz}f=5J$O4<}o>sUh>J7s~nFFCTRvu{O(zyDxrU$b(b~6)+6F+k#By_ zEo4bZGvPWASe&@5`?RoqVbQ-3zCF*y)24r`dXv-nx3d3&XhgVUr9#%$iA8h&MWi-G zyPs$%WGLg`FZ15#{TdPXTN-w`wQsp!6|yo{U)e6BFWLt^4uiS%Q4r;Tm(7C-W9y&%;Q$yW#))kKqlB43F-eDT(d()M5Yv MPgg&ebxsLQ02SB|fdBvi diff --git a/modules/highgui/src/files_Qt/Milky/64/118.png b/modules/highgui/src/files_Qt/Milky/64/118.png deleted file mode 100644 index 50c4cfc4c677d0d43be06e7f31e66883cd70c024..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3080 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 M07*qoM6N<$f+z;H1poj5 diff --git a/modules/highgui/src/files_Qt/Milky/64/12.png b/modules/highgui/src/files_Qt/Milky/64/12.png deleted file mode 100644 index 822bc42e23010ec0ac969631b03f8fc45e73ae9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5272 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 e@;*fe!2bia3mPpo>oKMP0000! z!GM%lZVAJotn02D!~=DANp`o^+i)m{fCP`yEnS2atJd{MN_H*f3gB+(qHfTwQV|yd zYE;x@P!UV60Rlou!eo+}WUlUg@AY&~&yg@Qi6IpGlAgX^zj^=nfB*5`>)|9x!uecy zoacq>$-}#L?JDLt4$3xh;>4PBNqEZstN^I3t)+rjSuB!znDC>1<@m+`!Adw$-qB8$lmQHqc{5w^FO94N>eAsBKj)r(#kO*f%m1JIS= z+H<<6W$vmAi{`E_+HNvgo*0;S0dzWjobbw?<9XyehQUtQz!5eqe5LQ$kTC$M1lO7Y zJtr)|y)TU@A(GYS<_g8RHmX!9rSNP|+a$+U!j3_Cg@QqEs$RJ9BDx9v(F9Zwv>I2o zK-h=chSg4@?P?(}PcRydh!Ei-T?sFV@oeCD3>1dKZp~+GAda*A#{@v14FeQytD%IG zgUN~3?@?X90uu5ujxqs&mQsrAU-Y_h>RO zz3hOy-1aZYkVtpEBVYizWG9lHGUd0O@g9vkx->)n}shXybw- zA2zGN*f4+H$nvx51Gep1gAeyr!8X7GJxPi{84(>`P)*AWNzi&!+w?fp86s>FVX<1G zOcCRK0XUTbG5-LPeOgEoW)!^M(-rg9j$~|jY4#y^pE1DG(SX%8_n`LBM&#yOq0{qG zVVOUZfl$vSnGqUIMj~Ofd)r~dP>gg;hNs1YP&gE0gBW8-ByJy8?@>S;jsbAkW#2Gw zO;Oft$Y?BZ%by39@H(!F&|NUv4D!R#2*7DRI4-_*=IzOUI2b{PH-H<*FT}KQ3*-^y zfz8bDKIQ3aMVsL82QSMW)+q@n!4hhtdlbJ8iIh-~d(jE-`erX;Z z#8l{w`WcP_D|kJxb|l)rlt8HWNa^$iVdDx=cFi-mce=;fz@FqrlpJdfH;4kAgl#P-cy0v+uA4?bY5cS z>Ul@pJp!QgkM?q2$5%K8+h8#3H5y2!I;T;$(lM%aK4`>@sn5VU>iVwjkc)NW`{SSC z<@e`7G6rF?7&Nr+f(>ydBA z2r)LSU2xQ$7689`z*49OjX`%Bl7;qCA)wY&YB_1JgvbD31+WAf-yTyarp%K8mWd$D2r z!)WZ-53}8vAbfIAbvve9RtAs%2Ylgv3nh~t#Q_4wY5#F7x?v;C zhScV=>EkD{9UD9QEOntZTFMqt@p@1f+X2hiO*!{6S%7qhQfiD}LS5G+FwCJ|z& zFb1=VS7L~L4Bn|<+2yl;xav>1_u3Z`ZqqU68cV#ObQtVUO0~&VbOs%A2U-NZL0?f; zF|?j#f}}z*(Y#=^8Z?!o4T|xZoumOG)EP!|Lo2SnCw2V3{F_XPXiITR^i1JYBtN6gR4x2uDl8r@} zztVo^8IoPvC-U8Hc`ziw6;LP@8_vibSDk>wuF|B&+t|)x`usUBz-n@&KZuG*Xly=$ zQ!Pgb`XrMieue_kc=`yMT8}aS+I=3GVQG|WowIQ9u*s;}`Y=v;_7km|x>7+B41P~1 z{hkf#^{Myhsz|ak!#2PipKfX#Uq@R2k#;@GZd!rj3AZLCFlQ<|>R-cG`*&jY&Cjyu zp51gi)BKtT52koMG_Ggu-A(6#4(89pgrr?QDf@Y;e;jVu^ z+BjZa7a#{qJSW;+-cvq^VKL4LQafuo){0@~vH0tPZL08ETTiF_a{R6M?(nBLd}5D0 zUqI}int;;A)-TsFjb50e8A?Yur%iT9UiRr})(i>iO!wUBg-{9$uccSDM<=C=o9U4x3 zi{}>A;^S}L#8-#@i^uQ!H|V-Swx!A2fOYRZh=a{vFeXI85oS`vbg8}95c!tB_E?kq zjH&R{*}5Wfhvo)YHs?QOmyEo8M&dG9wqQ`3-?a5iXBxW>40m!J|iyE;F0WPhi{IK0cP3(<+-h2u0{QU_tm*Xd8R8m{EXHaPtxqE&YArOfJ6|o(K3B*>4s_WgpWYT zx5H?bCpOwVUbeUA$;nTkGl;kAo*^J@!9%}$txp^(pKYU=IKmIV-h@-W{TMLB4&J~a z90{^55?$y}&UHB;bp*Iu>hbZO*Si?ocfI{+Q6L>3(H$Tkx;DW|23lb;Lmxd6X=aZhzd$4B~+?&8em9WQB1)A^b-^4jSgr{AO;d*)9Xo2P-Z~C z@&P?bbtP>S&`eb-=VFo*6f*`O+dLpEACM^+l>QqN*RQCE}>^hsp6*UVq{Ui!xGbNU2 zmvMOjr%sH3O#ESR7QHU?hbJ^kCMhPfQ?VM;I3dqY02G#YF?{ zSU}SC4Fl2{4c5?~!t``Rs3b$`z({jUZC<;jyFQqz1Ofrt2T!jtVC>kjHM@83_Jl$q zf&LmK9c?FpKu+naGfL{KyvadVZs>8M3DDJQU1HLErzPc(Z$i|b#@+%Zkqbq7bwm&6V%$;il(L} zmv-T^SH}VbgeCR$^*fFoJ62&fn~UgD8U5v0s<^K$R7hn4R67yMrFffEn1ReizsqX=x~{H{{-}w*Vls|wLU-oS8&l-nL?Rbye>Zj1 s6DCY>XVWhC{~u4E=jFTy;C})P0GP~BNj9M_TmS$707*qoM6N<$f&oK|QUCw| diff --git a/modules/highgui/src/files_Qt/Milky/64/121.png b/modules/highgui/src/files_Qt/Milky/64/121.png deleted file mode 100644 index 01679abe84fcce0932ee781848f56815e9edc0b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3530 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 Ef($#5kpKVy diff --git a/modules/highgui/src/files_Qt/Milky/64/122.png b/modules/highgui/src/files_Qt/Milky/64/122.png deleted file mode 100644 index 4ee45a6d1fb82061f20b3beb9b05686b75e59b3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3942 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 AZ~y=R diff --git a/modules/highgui/src/files_Qt/Milky/64/123.png b/modules/highgui/src/files_Qt/Milky/64/123.png deleted file mode 100644 index eaeb829a858a7affe7e72b29bf1284f8f7fd6309..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3616 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 TTA}QW00000NkvXXu0mjfo~G_@ diff --git a/modules/highgui/src/files_Qt/Milky/64/125.png b/modules/highgui/src/files_Qt/Milky/64/125.png deleted file mode 100644 index 35d91e0d6f8fb63e41a7be7a105c095814f7f46d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4129 zcmV++5Z>>JP)!%0LzRCwC#Tib73#U1{g%iitv+PhAi z_?pC62_!TqN(v=XLsg*`ArVAdsZu5KfKcBEe*rx8sS@RdCn{7`s?F=9!=Iq&B@7fL^TH+z&nRA;n-#5SOoJFy1n;zw&(j#8B z2-qTEi-0Wx9_gavkD@4RUK|@6JI>dB$lZhf^Y!lE==HCoG;-fn*G9R1&h=DJPtQ`# zTyb;tghxk5PiUIZ z5)}m8xpU`j27bJ$sp&gy^upmVG0ua$*l&3%z~K8>&+m41b=5quQk|M&6X~>8$ zFt*#0=v&S?+j7raWXWT+U(txfl1TnsYyF4W3mM^g%!Vl?|{%pfh>P;>vx|ay3oMZEZ5Rj~3?) zgR9#&aWDEzq^8Z%`L@5(nal6en?L;%btNCKzy`cbz^l5e`!BJ@uef2kFip1>&(V8- z-a*rIApuBC-UY9U<1%<%R~-_3WJr=!mkh0KW~QDgtZ|G{yo*MX?2Ws@poI z+;X+q0*m~Q?={+Ka_V&^=#TW?C$H0Qe)-P|eDG|#t{>N3y*OCC`s1tD>HJWHre=d; z@`_p}K$4*Q!Mg)Ts)$b+e+teiGggF06E^w z?5`*mIZ0PWKcRzt&sW=ngkINwnmM|3y-7?~u9#dwsS2{{vg)-2$hKMv;AEf@ehILu zS6_hVYAS*RRj85W5@(fdxRe}0AnXhifjQ$GKeL)SME7Q|*AT!GKzhBX3dV*M&6zCM zXjr4}RMZpeGH8d4bkU?t(el81WN<$k(x|sJMBAGK)YB5A5(B(EnWLqg;gKNBDNh4> z%xcY*S0CV6pOZ0_68!#HP~(NR>DEk^CYK7X0aiq{+!lZ>u2Py5Tr;DML*X?EV5^E@ z9C33<*xwtc$9BZ1vn}irLvB^PR&&pdFBip%0dypwI@hWE%$~LoO{|t^fk^;Z%^f3e z*n$MlSVc4nYR>b*Zt|bQz0LIbmy2}mL59-o2{3(6dKj=~DUK@f&9+ptH8A3yx+)+f zz>Vwi(tOFHGZ&}C>IN7Pq@gLSQ)e_dP@K{mk|Ix=1qT;z}COcO&UAy7fkD;*@`p5No zR$se3{$?Zqew7rz1XS9AnUw-9W(}HLDGEXYt|1KQnmCq*G03$&%Id?SpJHOLqH$J< z01T^5o*|SZK^hi#Tu8^=#UhicP>9v6g`bH-4FYP7_eipo*>*rYzmzG_@OWBO#qVZq zsxJX`Z2;Mx1dyV5s-XI|85-sITiZCGn6cl#E0fzfb8^BFr z9}4_HfmHz{$nqCzT^O8+hi0e}>%L_7plP#w_jdFH`8MqlSQGzC>Ez~{Se&K3*70Bw*6 zz)X}1QY=~lPzEW%mP$;X=`NV+pKAm8Z)^k&?@9k)QX*>lKKCX`065r=untLZj32-P zpok2GJ%G`3#)&ck0R{mH2^qo~utYFnrZbl23#8;C)Ss6txgPY|)7V zHF<3eJx|g`2&-8RF*m0I20h+CLr0#RrKNU2gjMj2VR0z zG+HQ|?WKGX?RHj&2t`6Ol+I32CZFaQfbTQL_};Ef8bP)J`}WMy^70~GA0DKg9o@9M zcfbdle0?V)3EQ@lQWAbF{ zHi`y7mP!@_FSv;ihR&MjDPx@{L%AiQKY%u^a)zlYvlNX^Q!K_YM~cICRj0+3X_lTb ziU*wJs+cYE9G=~z8x*OgK_g=~DH4v+#MFK2?cQCp`t^==ZHfY+ISN=ed9Wx&(g!HH zZ5u@*5g`@07u!|$#z!bqC{j4mER0xzS1e!3(sKSwPROpZ4Vb4O0}U{VBJv|Q1y^kE zSk(}*s*b+1+T=B zMOZOxM)>z1HB6kX=G^Rk5tsW*3pI0nw&A|Df8Ji7jV-miNtOS4#-=M*u26q}KlwL( z>e>L)awGsoE|Xgo#>BKrWzaHrr^<~H#nvg6eVT$+3#GQDDmGLBRsxk1?Ck9P#&(JQ z=M2N3>FH^@eEBjx_0&_;+}ykY31Gl`2lms=oByG%?jAu1L!c%tN$Ju8nUt?Eg7`0y z-%GmDOnV;NL)%#7zl|;PdZoR+ovvQJN`r%gboA)ajo5$^>vE9V+EPMn3|5KWrv0Rt z0m^3bY{(qRkW!#oQHm9IQ#=v(z?-I7GyX; zLjY#aP098)VGjUwDLqdkQ$vEB5XbhBrl7a_;Zm#E*W0;sCl!lDY&p@_{riLUM7{dD z{}?1BlSyHFpbp!&Z*Lq20Hh+UYuTXOn7ByAQeO1)tR}%|($i#QUS3`n(zRB>mnjrN80w^)xJ$Uc{A9(4&fdk@RNY3Qsq>u(_W2$XJV+lYeB|#E-U~R=& zSwAUgoqhaTzmL_}NW@qD97Gt_8sd!)n*e{|!UX{=8y_Y+G0PI+09(ynqx^h;jFrdf z{P`RmK73e!#us6bh%o|zfcK6K*#J)keBgb_cDXON-P}R(oIlv{kH48n5P(4_bRJ_~ zyLL^0UyPq4ExAHJIrM8v#yf?lDwcBe@x}LOLj9Q97oVr0p&>eQ8hHR=rPrTNuy zp{kn3k!fL>V&PWG8jCa@Jww5jSH;SM07)jK$e)KaQ~~D`6<6$5U{^)%u~*pxucfjL z@Oy#venQiTD6AT61IAleSPD&U{p1ec_PNnm9hDjP? z!uo{QKth(yW-EZNF9Eh=HK-y(PPSesy4g$+6PHF+rqPyRmF-W( zgv+hmo50G;Ibj^XddjNSw*j^%0iuORFn!0adRSHWN#Z0C{_0=R>T7G=3jpv0=)!wBT*#0eWD3PGQbL`a2H^11RH`Y+;o{^Djs@U(J*^dsuswqN7`NQ&SJ;YlD()kcsBI3-o!-OT zCyxD=o*DQVoxS>dx<7ZF=2jkf9~eA~>wYW1A8Z{|tH4HpHY!M>hKGmg_U+rWZ{I%J zwQH9kBRU?Xp6qj!x0bjrQbOM)bRNKQ9pa6hALJhxqdG*FxV;}re)Q2t@9+ywHcCm? zD`(H1#m{T$(4j+Xf_~WjK!n4H@c{!*ZBuJ<-#g(}RT;|Bgh!A}c1_N5XDA*f( z?X}kwFBy8_g%{pDbLPx3b~gvt`nlXj3W5$0uol}gHGY^Pf4Ip~N;hxb6a?U$R1ujs zh)9tGCWt=B+OubmxTiWg#dY^L#5LLAbAy9}f8(g>*ucQRLroL~*_RAEze+sdeuS1^vv4NQ0=@&0BC#(^i022Q5L{;U%c0pNaX?|(1U0gz&1lnqn+PIeSp7r?;bqKDc1j&8xrtU%KPuX{{fr` zz{}0aZ=+Nbg@lO96-YS~u=I@);F55Rn>SqP`Clo2XW+l9kiM<|aq07*naRCwC$TYGR^RUQ7$-B*%LlTAp{ zq)nUcyG28}+SKRhs5GF1;vbaJ868K3IHL~ZjKvp58R8__0 zyh*ssw;2Ft0GI(_27t?Y^RqvM5TWzyf4nwAK-vhAVswW@ud^dzKkQzv&3<4U_4k;4 zdP{siDe-Hm(4(`e{pekKTMyg%QRR->byXRlfA@X$y-yF6^eOZiqW$3j_{yVK?U0D? zZYpr3I$V$Hih4mnL130q;fW1KXU+s7mjkOE!i{x`F4JkNnYsze*B4Zu=A>Ijj?&bID^V9FOe(7S6{9_I>*U2e%XjV8?HkMX1X6-w?YsvaER%{(SHU zNKB9Gc25k!ZFbMjA(#bR?O%|dSSoHc3~^Np1XnxP+Pc7WWb;CsyQ2F51pFaf)A|w2 ztzCkLy1#KyT&{k8~h@lIw zXfIYFd8og{nSebCSoUuZd&J$}TV7t}%)w>~nw{J1-t5<3Ie=iQZxw@QX7Sj<+vv-hk-r zE75amv!CGp9SYwk}9=l;au0XaC_Q(e&voLXS|FF})N2TO?Rs^{V0vB%lz zV}-(CJ`?=x&ldF5N19_R7hZ$Gkv^OrJ!U00Tn_IY1-xqkcllLY;RC_u9ga{{1$^DUejmznaYLU)Oi)cCV zb2*T=n8mBkB+;fWDm>$qCB*8bgZx*Wd!r!6dN+DSXxeLjE0 z@Ao^syzS_4b}o+3%^i4(t-9!j3Wv!Eya7F?(@Az4v-!@S+}7V)3LoINch)sDMe^~V z%K&*&1bIg&36Vf_f?`4>_ykWnK(&QPK0)H0q@4`0qJfbI&_V1kO)mvL;1BECCF1L< z4mU*BFTN4=G?v1i&k?(|+lQ7Eun9`usg5 zo4^--w<<=3?_M3<61i!^=P)&W5yRsrkVuWYl({afj|Z+P42>bEmOAj&-?)uiY1NW5#z+Jyt(X(d3yJI(R{5<}7Y!CWQJN)U zfsxT|hk6DI@&UA5+#d9o z#oE?<0?+mR7KcwhEta2Zwn(jRmdk~Bfn15Dvsl{lpTTnqiU%LSD%wY$|27!0(^R{J z_1#^R6{bjRZJqy4t&To?@~IL^VadH3R7F__&Rz%ZSNYkLy{_(btKVY{B~qu}p=&=h0L9 zFu%T)_Q?^d;jtpckQ$YUUqenWhY6?@|u?l36srcpjIU zDlodB(pC<0m7&&_F*N!LGPx9*hBG+Z7>E?s0Qzu-roGA3B?qDkH?p|`_MCewT%YUc zJGF@c7lh}6E$MM0`ddz+CJf}f+zoGa0Q#ts)`#pTR{*$=#x!Rq5H&#aq003mN zDTK>w3J{+r7zKDcyFm0jCufTYU)ZcjB8|;|j$`c&@1(iSpHNkE9`CQ*gn2KGiXh15 z_mB}t$=G%aQG~?23BEy@5Ov1VLS$7wgM>y?f)cLxp6(@ z6HjXnE_?h*)LpYC7Me4syYD6u1k(iezBYIOm1Pmsgd6EIiiV`F+Pt%}sh#WsiY$VV z<44HYbM{cj10tJhPlewzf(@IOLmBST)s%iOFkz=g5RP2LHf15AFHabhn5vEZ)@QR= zxpoD8=8&EuQ&7|_HSrVwLhZW6u~0*u0D_hX-SP8mBCa}*5iJYL7;a&IZ5nTO_B~Yh^A^GTM`RQAImgCia0~MG&t71>^ zECv@>VC%kVtlqF3NDk|z1QlM%Q4nPP$o4>e3*s;L_om0jfP_g=-YhsHAf!lX zthy8`^vhDGw)&wytXmd_OcNnq1M((dpLI4gKr;ga1WkC_6$l40c4Tb#s=rNixH3Xt z-m^Z!_yFqQ+8e5)m{-3-TO;7b`ns3K_6(4Ch)s%=$3~b+W)QyjA_fc&R%Qo*iR+Ky%%Sx6Pu5+@(pJy5lqS zJJGm5ohzyVycuZbRPNVC{>o<(9iEgJjr(^u)U?Dt*4C-54fxA}AK+a4Bv<{}Cq^Z1 z=?nyJz^V3ym&B-03)5b#ZvW^UEZubmtD8emsA&|_04RokEc0QsXLNV$`DBM@F+{7S zD9xsJFh1bS=rKIiv&#~iFMnMZNx(^tI6NTnni&-H05#9?I2T3H#7tq%ETcfq$R%lj zzbr2KS*puDuH8x0;jM00{g5A>q7#)oV?ZG}HVTzzR?15_&#uq0~M{8?QG)k$pZ6+tjHAXX8o z!pUHpmp1jlb$L`fwpeExWfZln@q6*hvs4EoK5hG~th8MHwyC zFJ`Qff3q53hQyTa6Dk@ix(;5M#Kyxq2&M*Tt0_YM3IPBW&Q&5HE)%4l5x`~wgt!VX zEQ~M!TKc0SSngAh8O!RU&c%O+2GCz$$cG#akwPw@iV2ZSqX*kVSA-}C+T*)T0D8wK z&&AmKK>1uYPldaUzH@mT?+;wEz*34KoAV0}B~VYDT?TSqU?KnKHGuZ1ejm`Yac$bV0Q(vt`yvTbSX&r$ z%o2Gok>M9fu+!-`~aIr zpdhe}Sf=eznZz_QviM2>X8jec;OR`IN@W4O(3)D zbkF3NdDER8X&xa3eJuTrXQt(C;sS|X3m&bkh_tU>d>s<0I8Kcm)k0#iMG}s+L2k=s ziMT{UwM_-9uIw<20y(rkFob1^9MV*ECF5OeqE-q@=v`QjV;`QK&nMUt+2)EUmVW;L zYA;+wYMLfb%mR!(L_rGpNSr28TdsRe=@n{$+jm7H^c~&d@~T+N+*Xu_D&HQ3Kq8HB z=i_J|jUz=zl3778Krpm!A(EfJ-f=tAq~U>Au#$$)IpbMOPAeKdA^J?vC&jZlWy`yd zE4{AO;O!4a88!AO-P_8`!k87VDX^z0i4czuN0M sJA65BGygO-1HcRbGXTs0a5-=P0TSAm7)t@y>qS-n`$u_vZcH zydaD*e3d_F9)NiO<^h-oU><<4G62pCKR>!*y8_Bqqy!>}+*XhR2$3E=(to56T%Y~} z;b^>;J!kZizl5Ye?1DczA(-C5$s_%X&8xI;UZYJnuf<6J7-{={xuyA7I>OWMTcdjP z)f+-Y*n>pSNNvS>C@rjp%9521A?%?eCw_=~E;Y`nhUReK@CzWBrY9~ncd5H>pOdC1kLAV7VB@v-u>Q;cejKLb6WgA= z`^*tP02JEMb?uQ&*WU-Ti77bx&QmZtIS87r`ZhhqeWN8n(^RABD%VeZ0q=LeHT?AuvH#;`v)&OPpY}8mQK|NUhTm+>Kg_569+p`-?wvu_G-0$`j2H~U^ekbwb8D-dyNgL5UY z&&}BuR|DMvfCV0DqZ5OWD$*rB0H5o-axqd9U69RqLUp^Gy;2#W;b$Vf(Ux$b1S}kzm?R&@1K_w1888OJl?+(mXE< z)j(bKMmXE{1_9u5)6?5K8~sc$BiY&2J9zPIZcZUwTXhTZpdApK7=VfCVI~7+qL_oz znZUgLyOuxXTnLfe0Sdx%sxSBf-b~~|Q)icxwE#*&m9QvO4Ig$MhvBiybQbrd%?5Yu z4{s-D2g{19proi0GJ{!PI%*qNXQ6+HOy2k)J>B-q16@bbCPNH_=c*%QCTaK>&d$n( zKp=x-dLYnRw~s@zLwe*8r@;xaqaz+zq&XX(q8F&sYkcOxu4Cz}LauTC;^@ZE)a3YK zMF})yW@dQ@KZuI}QwBt$%*Z8Z*+f8W!@WYo@H4x#O z?^ypZTo-PDGwrWI_s~Z$GSM%|1t{)(fX7nk9|*fw0cQ*#NqaeR0(MATyr2TECu3h( zc{7~tcpaKQ_*0DBsej_G)6ta6Afc1H*De1REUj3_5(&fOeLl$$J~zlOgH8ba8KEo& zF#E1>I1K;>V^R3W+2>hK;MO&b5FPv=)ZX_#b?Cl60qs0gyOs>}?9QzR;Y`Qh36(r9 z2(L(~u);aPFOm)N0g*HGO@d;Q9?)GNm`ree?G{*3xd9Hmv5Q`-dFtNtQEv^v5V@w} z2C|GK;2enpE#WC64| zTb)g;gQ?lQ&^LUM_=R$68sStKp%9(@fmo*nfT{MF&jrW`krx1xeE<(H1ULm9b`qhu zs*@#8m+vi*PMq9BL=GCp=VleAL5Il$2}eG_oeP*79roG`@!SC6aszSzY(GE+jH}o) zn4|z@Vx6V*Cuhde0-$AdVyI!sqB<)jCeH;ZqR>8c*d6#F&wT(H3lx?@!5nlZV1hIm z0yqgkkl06I-Xanhy-vR#^96u>>-eXG7k;pM*(NB;U(A+3uUvr454e+}5)eK|Qq-9a zqXNb?>|p|!fkSt+#;2~p;7F9-^Nk2Ie%IJ}SdI{Zk&N_S z{}o5@gM=iA4Z!Eg2|5#^hJZ{1Jl-1DbhxIx9zO1Qhh>AFzQ3czUjvLW>^a}{_TDwM z4N$jaBXsngX8CoN3&2#iBpAM{0LKL++i2b?F@9GFd{+U}#!U^7`IQ9m5j2<4-g|~^ zdZnEY_}(AFhXa9Nq_(Oa@^eE3fN_>3_G2*!`7<0e$L-Itv25t<@>?frgIT^K%O!FSj5y z&qwKilm6%JllMXHf)$`B#Jm(5g(7w|tj^G`oSYzJWd}VM7F{PyV@I*%@u_uDG>p~KVD zi(yek4g@j+!2h;54ggivq*Vskkt(CN*`lq`*oFY2(0imGP(atTkmUoce8AzGXpO-A zq(%IeK^6;m`2tCTBsDcMK8b|N92`!*!{QhM!9?;MchGY||0AcHnCQ;K400g{9kNK!vwZ@=3b zftl8*bOlLhFgu$7UF87y(O*`D2#Y$reDRH;+i(0nnS~j)KR~N3+)EuuvOdUR^XX6_ z_nD>^K|HP!2c}xzqy9h%AP7(}l0QPh@%m_*Vei0%HkhMIgvMOxP0uw|wE@s|ZEtB& zb$Ii-9ndv!4o=Wqg(~tFb)gE_jC&jw|`3T_% z53}v@Fo^(@^#KMOr1LM#zAFu;eBpGMo)?vjK!`N(*%xeU`%Y4;+otBgZ5+maSdoK-r0^zbX@KN_kM=n58E1VV`o@Z%t^*aeY7U0}keATp7>@2n$qe@C^hC2I+ZfK&lk)6Ir$7D(>T#wVez=RJD9 z@9FzHVkr|MEBPFniLT@zPR5h+P6?gB!By7DwfCc z8xy0MFgcnn#+6)96%1-kp|Xix&pw!1cxiz|5(t_sSZfk-yH diff --git a/modules/highgui/src/files_Qt/Milky/64/128.png b/modules/highgui/src/files_Qt/Milky/64/128.png deleted file mode 100644 index b9a903cb82f131e584fdac8f04090c944ecfe45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3399 zcmV-N4Y=}&P)OU=ZkP3_;6O3f?gc|O3{*!XdN+jqA+o!Q&lotd58Tc=VbgwO2l?%dtnecybq-|rg` z#uy&uhYud{$D{%#6)>rQNd-L84?lfE2uWYu@rTtR@)|jOfao7RBV@Y$)ue?%`t1-x=(MU4;n zwi6+%S>b&sc?LsRRuL&dEuoPqur)stRDd{v1<~~(Lu6AKmLjmF2<9FqmC;1bTem;g z)I4DYynJZ+ULsW8qVm;HS+X1or_6>aIWry#a~>S+f$o93a3TCRXzRWT8rJH6^7T{u zJS*V)2N&668%TQjl82)%> z7ZJWAY3jNg7(TBHZJ52y_gMv{uH98&$;%7Fnzk;eeyy=)o;;I#O+P0OdL1fW0$*KP z2j_478UERFD1x!N?C0OQ5S1ywZw+YM%VsQu%9+dH(5aspEAL|zAP<6UJP@sd^?f%R zz(Sxi)xr8W8|VZDqy$eX;b!|eNT_jm_K6qaV(Z^S{li@~WTJhJSwR0n4c9EITnAU~ zd<~2uC1TVZ)0fPB+XW-IdntWvWH=E)l5#LP#q7~5V>-Ww;h`6(c)ithl02Q8Q z<5+wY9`r{H1t8uEK(q!21&F~B0kDw(3&J}EJ86nz&7O@1QY%oQvo8!wLQjDkaGLvlhAG>FAd;-SRTwd5kN8lCZ3+JS%F60qiS&& zkB@*G<&lg58bmSqbz`w%D9C?I&!s5^V9Dx(U

WC&>h!9M?qM)Pn+G|J}~3 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/129.png b/modules/highgui/src/files_Qt/Milky/64/129.png deleted file mode 100644 index 000826f4638bfba2b8e486b73acbc2f0a56aff35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4114 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 Q7ytkO07*qoM6N<$g6|96Pyhe` diff --git a/modules/highgui/src/files_Qt/Milky/64/13.png b/modules/highgui/src/files_Qt/Milky/64/13.png deleted file mode 100644 index c4ae0c42fa75f4af787fe5ab2d4ec888c88b68c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3329 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 JPDHLkV1g+ZIokjL diff --git a/modules/highgui/src/files_Qt/Milky/64/14.png b/modules/highgui/src/files_Qt/Milky/64/14.png deleted file mode 100644 index ce96e04ceb86712e96952cb78e3edb8a27ad9e2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5293 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 DgFN~t diff --git a/modules/highgui/src/files_Qt/Milky/64/16.png b/modules/highgui/src/files_Qt/Milky/64/16.png deleted file mode 100644 index 9acf8c20bd3327f0d6c7d5b28878ce9e8046348f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4376 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 KMNUMnLSTYd=(@iE diff --git a/modules/highgui/src/files_Qt/Milky/64/18.png b/modules/highgui/src/files_Qt/Milky/64/18.png deleted file mode 100644 index 3b76256babd6d44499660e41ddbd1100023e4556..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3701 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 TdV9>y00000NkvXXu0mjfM}^|D diff --git a/modules/highgui/src/files_Qt/Milky/64/19.png b/modules/highgui/src/files_Qt/Milky/64/19.png deleted file mode 100644 index b0de5da46b3ba2e5f1a322adde7a77a9d54133b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2719 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 Z{2z$)Yv)_n09yb6002ovPDHLkV1gii_2d8m diff --git a/modules/highgui/src/files_Qt/Milky/64/2.png b/modules/highgui/src/files_Qt/Milky/64/2.png deleted file mode 100644 index 3523cc67061200e46dd2e47282ff2cc8b8617762..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3725 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/20.png b/modules/highgui/src/files_Qt/Milky/64/20.png deleted file mode 100644 index fbd1fee4ff136fad7cd3db5d3afa65cccf90d2bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4659 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/23.png b/modules/highgui/src/files_Qt/Milky/64/23.png deleted file mode 100644 index 11b0899f5c1812f81b06ba910a0d7de42c130331..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2562 zcmV+d3jOtoP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkXh)G02RCwC$Tl;Su)fN8E%&gxV8#{zJPGd(=(vSw(Q2HQ9&9W#`24n8+f*fT?BI!;_O1vGkZkm+iiWA#F?=xgPehwKuxrQd zP!%ol(5qj5Y}o@Kq=1wHLK@JG0+JuT4aow#uD%`7#*V=UU-{Cr%Nzg$LK=`(6VkxB zi|;`fMci=p*AZ>%h(B=r#y1~0zN>Y4x_~qw*fdj%XLI+p?!kAuTcZ{Pw+{d;pkT{_h3C4~8xZI6Lt# zG}cA&xox*VRkXwp|G4e3PdEU8WC0o z5LgR*=g6jR5PCd|B=YT>C*gVnK&5@>qvY&+zUw_0+S+w}U;CO3n4CFp!x99_e&te6 zTg!U1G+cpyy#G38a#JG!`VW8e{q$Ve=Kyf$lN(zBN)VyHv9TFMWHkjK0s$aWtUlXd zvVgJnEkwuxhg?8}0A_V;Si2RC^-)aCoCl*^jgm{Rr5Cg`T!Gf+bvSkI?--jpJqjT9 z#2u&7wF01(#0UrK>LdN@J2xSs)RoxKoJ^i8DBjDG*QE&We?>%S-G}+dl zj6zjZhsQRJ$<1Zgg?r}Hv#R!gB6Rq&lnwyo3tGWP7?g0V9Qeuzqh5+E5ZQo22&4~kZGQPzB003Y{LFyPwokmBr z3tK;R9Zvo4Wa6IRZN`y1PY+jgfxVA*?glWjc4G$`>RMd0?gzljin23JVRKJ&Ha@V& z91ah=p<)mzh&Hx^C6GM#E~FHRr|wD)RA7L4eM0Jx(g>e*zgxINq2EBN<^WXH=pgI4 zJU1LoDD_RwTtKv`1DiK~24~OzC;r{1y8&SEH+P>&2N-b2BkOkq7-{cXgZjG0@<#hz zBZM733@*_HDo{QE411Vjf2%&ygoa2IW8-I#FJwo-i2e3^XVZQS0LbhRHUmIqXFU)B z0!7u3*`HJ4l|5wOa)Hsq3ibeELy*m-nO>L3CX#mwGFu|ggZ6rvDDQ&q~-JVlVpFoP?hR_44RBxbWIG)J3}&*%HTeR$(u z0K=NY8fHjlNFhN0q(60aQeBi%VDhdLN~v%Vfw|@cT&b6rnxl-u#6<~w`MI*4}wonZSJTSoo zkcF;I0cHmy$3Pn}_KBVcNb@`aV50E(Ua03aIK+ttN=jPXhoc*~$r=tMnRg zAKMq|XNn6DVz!v#1%>?KYoYJTarqpA6!UtESslzsI0X@AcIeM+HRKE5gDgxSd)q~OGJ_g#3)#Q+N};nIMxnJ>!(s}Hri z0YaljNyw;*H*gKfll?hWQy>K+H=TpfW#aeuf9RyeK^Nc^L~OdCUrTHBeN`n$A(2fNAas#<;las)T69EnMUDeP7i}{avvI9Xg&I*@dA<4W zHJDsrQ0ST=EJY40oki+3D5|*_^Ls=^owU44(G(bt(qrgP2PW^G_l=QT?rZ7=&>y%l zg5YeITqMjbC^*xB>M{UEym3u^PkmDaLddz@)d4}#2*tdC{B!}#h`;#I)bPA+j4al6 z+s|6xY>d{&>Y5^uLe`uc69I~xe%%AQ>4@t24ONU#4}m7zkW2)$q=iKzdzLkn;G($WCIAjK+k35h~R zhoOs6B8r`OJomBve_#nbCWoXQ3WXsdiUQem0eZnW21e|!hw~rnpC^_A14i|{`Ah;c zL(dzKk`r${r4L+=@fUq9001}Ltvzckz>)+Ez45d$e7W?8R{uG=!naibRsmQAU}bOr Y2ehdqNvC#!>i_@%07*qoM6N<$f)O^YlK=n! diff --git a/modules/highgui/src/files_Qt/Milky/64/24.png b/modules/highgui/src/files_Qt/Milky/64/24.png deleted file mode 100644 index 94a8e94969701e113db222615a3259d68bc8601d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2357 zcmV-53Ci|~P)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkWyGcYrRCwCuTU&@5RTw^JGP!TI=^gc<^}*M61wni=6coV=TZ@Xc&@4WPAh<<@ zT8mv3jid~P1|4mj7V z|B!#L{Z)aZ!r^Uy%=QZax!*3e`_uzFlXAMSY2y~6GKFZGVmN>f0u6-0;vp8Vpj09o z)@>qjIhFtW%I`C9YU{FxK(B*wZSwBb_krAb-EZB}cK~3Sx)%U(X#hgUc*AhYL*xVQ zOojS?jjJ}0h7$m;yDDW=4*`??BcxS75Ofb9!wmqgmN5noRGkZS>pKQfdo1{C0X(*5 z1Oot1AvKI10Cxrn;z=4{xGx2E+}(r*LtyAVfODM)Ib}x7`WWx4j=3uoOaMlL1VyZJ zChW73V22C*OM=DFx7=I20OuoYL_7fLp4oCX4a|V{^v^xH07Hw9N*3VY1!BjnRR9ga zVcZFU1puaw0sw{>BAO8b#8wM*r2rQQi##Ay;LxW*#ti^eLkEy(#ZZ7E91#yNG&@&2 zA<*Ff8h#G9DAGxAWYVAxfMQ{QF5Bc z*5Pyp&WU$%WKn`_lt@rf71CCkuIJHiDoJ75LLCsquA^sVMp?*R(Dj zL2tzgv}=0($JYU!A1)8Pwrdf*mQ$xU0JJw=SG2)YvG6fZ+jM zdmz3Qp-%{mj1aPLK(B7g9oOm{Fzg|~jLr=+Jb*@V0Uj7Fx5Wc?ytDpcy9qSVtzm(A zeW6SbU*5Y5a0ehJ06UKAYjR`fiC*Qi-B*1-Obz^Wc>9rc7hZ7R~o+U)wZKT^a%ko`GE21TryBT2Zq4&2iN?WUXhweq~icW%Yf9@;_x@j^C{Zh z@@}-j-5;RZ_l#RSCP~^2h14s}OJBZuSKFFn zlS3L#1H(T=o6LhS4I9w-L@G|Iu=AS2O5g0iHRacAjSK*eXZEc;5sSx)xiuNmt^){) zX4!Na&fo}{z>R@_NJufA@#n7Agz={iRHnRSx^qPOq9nM}6zu$&+RX-uLuSA&GQEto051XuT!fvS$bvGfS6iK` zkDHv(N4d`ZM1d)E;0-ZDLAD~TTDuJEDx5l4nF~9APhXVWwl}{5t}BBy-H3#P8o@b@ zcT%8{YG58Hl(-1HR4xIsTZMGjtl*}cK2*INRH^m_031)hv0^h^dNG?%<>h1yR9Y(_ z8ANLc_zEG1QpA{7+q&_v+`{R%tFzro^}YdsW9#eVLL=nXWawmL(Y#w;3vc29*odSSo~p@RfGf4z=Y z70({6oebNoSyC488fOmAp9I5aP;x?w6gSKYVc6Yv&XqBU?RCt^269{?Sf1Bz)Un!} z4r_d0NziLN_1f5pm@F016EGAsmGy!C?E)x{Di6{x8jrdD+jkeHqPFXn4FDWZzM8#| z$ixfj@g!+N;l#A1d-bX4^DRRNI4J>kzl<3Hg-K6Bh%LJN zqR`QE{X(TdlvXsMGq4aa#}m6U8(>Dz2+6z*e1Ii1578a>^A%hMdQ=OfcXPg7#&q}n zvBq3)9yOp}j66P_)-}S)R37JP*4mLfLZ5pTk^FCw@5I=<82rmgPrNutT%( zX44>0#4+$fv^u~zM#2-uS3u=)Qdo-d0}BD`cX;HI&wbE3yX3lJP#$28bMH4nBk&TYEvy-k$-@w!oREd_cpY3{?1%BU#RJUo=u3&y z@nmc=J)RPzh>Fo~!0QM$VKIC(RXg_Mhsv(LRhEPH0Lv)0n`#-W#?)A%vlwTZlwj8} z?&oiay1vv;<8P}6;CST4_y#PYA*JO}E;mJbTr7hn8HCPQk~9D$1;9H0v3h^MjlG>7 zKz{m2K?W~CIurFf3MJ$ONs@@1l*Mtq+C;KMp;H*tx(52&Vc6AKFs@jiYgMpn#3rUF zYvH*%6jSYnT1Jr0f1=I}YE1(R0qfZELTm&0;|#p>Bpl}9b_w3C1V|kF`BS!R#z#_u bcgpx5o8Ny diff --git a/modules/highgui/src/files_Qt/Milky/64/25.png b/modules/highgui/src/files_Qt/Milky/64/25.png deleted file mode 100644 index 4e650dc81720ffa566437993923ab180b74959cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3364 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/27.png b/modules/highgui/src/files_Qt/Milky/64/27.png deleted file mode 100644 index 1ab2410c775933472e30e3f55d6ecb876c35731a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4494 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/28.png b/modules/highgui/src/files_Qt/Milky/64/28.png deleted file mode 100644 index 7d4d62435f165b83505c896699580c9b47dd9a03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2499 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 N002ovPDHLkV1ndIl&b&$ diff --git a/modules/highgui/src/files_Qt/Milky/64/29.png b/modules/highgui/src/files_Qt/Milky/64/29.png deleted file mode 100644 index 74a499650ab2c32ff1a4599c892add8d6af7b718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4429 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/32.png b/modules/highgui/src/files_Qt/Milky/64/32.png deleted file mode 100644 index 11b66ad441cb40c8eace4ad926d8acc5bd3584d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3741 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 Hu0mjfY00rz diff --git a/modules/highgui/src/files_Qt/Milky/64/33.png b/modules/highgui/src/files_Qt/Milky/64/33.png deleted file mode 100644 index c76151cc32f181012d9d752863a2580a4fc7b08e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2791 zcmV004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkYZAnByRCwCuo9R1=t;dHnW@~65Rg_I++A{G3GArwLjk_9 z{eM2NvwjX_)P#``&Z!09>}&U4{t01c^6;m9A>nr)*!J5L$f}(Mnm>a{2Za^rJyt832i=`Os&x zPw@ZshAsc`g3Z5rLGsx^mK(O5^yZvGfB@fh;93-hc!+s;oPC1dbYe1WJU)pDpwDbR zF`4_U1&Ht^Hx@%q-FH&b6W2Y#Q|PnVNBB+UlVIbqNx&VQ$UaAcPzd)~3cxiVfrU=b zf$LF_Q9Z*X>51!}4}F5?XP@9Vludy3hbOSl(EIqD`)mYYrdQ!8+u8Uxps=UJCWlY3 z3O^CnADIB_4t)b_4|?MA@M7=dbIzd!V0nw+LE6l^upUZw1%Ler>N5f6Grlb8iR+#_ zW`JLh@aqtMZHXtWDgK&$hUBa%@#Gv%K!z?P0@ry(PW>z>y0!p{uSY|Ab*Q1xu>!y8 zxMAIIAo#-skF#IO!>>Nz0sk&?XM#x1>ir&^LkPf`_Q|LV(HC5r3rE^wq3Fs2&{PFO zQl*~)e!DUJNgm!f=}GX11o#pGe!mB-+UE{G|1zElp!e`OH>d(K>q0fVFZ>;j>3@b} z9jhQyHr1mnadr`aMU6&#>0gk4VJ>9UhQXnhWjs7Cg(3$36Xaj~0d_Tg54jDq zA@h6~XlkahYOn9`9!hwv8Ted+A5FlpBx58jPIF^Rf}|`?cUx{#0o1}~+AnrG!s+ zgJ{hNSd=n?$%#&L>$aJIliCZ7vbY;!4RrvV*n6IjHRc4d)3Qt&?9HUg>w$_O&OCQP=!`w8u=FYD{` z8s`|n2LXy>v#&nK3!rV0B0!Pwticxu@IuBng&#??Ex|8TyI>T!vey@?T_ZkO00Km0 z)y;&whVRW6H$q33czP;i*3IY@Ui>@yJZ*V>fsaLadB*3H-bgmf5_}2|ADQIBq9Gbz zTbSh1{|I>U)j?u ztqkR>I-^rl)&JmMBWB{Ry6$o&$Wh$F@5~*nb2(u6wriX&8Kk&6*)g5?W9~;>jS>SvNk# z5t9q_FP|-e$dkB?-;98emJ= zRB6@~#SJ^ikau+B*rWW2i=FU@2OL6|Cq{sA*wYYefJn;rMhVZZCqkGHm;|a1%t_dE z*w5^&oAmMsN(UN-<1PMxLjjL#pVjyp9-an)(?AJ5xX)`b>{SDPEN(2}{rmR@2{#*H zW2p~6>#{yxI>M55bd>4oQ9Rv|A8v?=kIjAxzsBh`R^27Jl;ILk_2t~s<|jg;1aUdY zCEVz&hYiJ36d|uDZiIGLgr#8?UTAQ{=Q!(nT=zZ>^W~^D3G0fy6|=59#o@zTENy8l zGlHitM*J( zguGPT7`3x>&(?dAi_PE%?7Klr60Ww_!OC6Vuvs@LYXx|=&1JG=*A;0)_yPTP*rJ3s z9mM5&Dnni>Zd|3A@9P%la9@vGl5nL}2eDZmh7%lC*1E8zAoTFz!|ZSk_zxsV0uFYd zB1(Aq@+CW6y3$$;F&XaCA%?gFu%zXN+36aTUx?hgb*onims@IKnPwb+euo0Ek!iB$ zFVhC?H$s*qTxza?m~?k`k~44H7d}oYfUcW2Z*nMt9k0nHJbLtqNr+GP(%SonnS=f_ zIZF~S8oF?(oLYb-3Di}h2)jyfm|u!^c6M?o0;qHrCD4((C;^AcsRdY)Ky?F(=)$3L zY5{b0baZei0;phm^X82tLHIVx<`Nw8{|k@=9A+h8G<4xmIkf<~^m;v$K(&G>;l+y= zAbc%nGYJm;e}>xI+c^{gv_lw6z|n;wR8B3xk_1YWBmsxYsRhv0*4D5(4BBC>m&4kPNCKhl~KaT3T8-6agfH tb_%CYg66K~=H_seV3QM~J_{I1004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkUTuDShRCwCun~7EGOccj0ff6ji5-5QZUJ0)RIlQ4j=i#eG-Ycg=Ty z`4|p{$V|e7#g}vb=ZNo_$^CKX-v3OJ0mpF${$+jt;B)Nl?G1{CMcIRc1Ly4QEU->b zPXp`Zovc?1TqyStk_JUnzB9v+;}&rj#;>#J*ee0+3a?(FPT zMSmj&0A&e2eSLlH4*vfB?!3Ofc3p38Z!QSAHfMW#I~65>)a~u98+6G*XK=2Htq+_HP?L zL7lsC%(b;OUjXOl=g!8)MzjEWSpbyG&DM-kk|N#~fUB#kz5~D%p(~K7xK)8rkz$?* z3_#{mUjm?Z@H3t3RWgKy(|FWC#(RFV$yd2XmKV0q`sK}z+1Be{F?wU zMT`JQEBUnmq`>O|HtW6 z3lE^t2Y3yU&KR{aOJ3iA7$*n-R*=>J{RJ?jsPI_O%GscX~`Hs zw?KHuw+bMW$pi+_DG)!6D|O2VfE0K$0642K0HT1Q8;1@+TQvb7p!5J17Z>XQ*ivo> zLKql9&P!0-FEnYJ6iKOXdH@Rx3*P`Jv7GeV5NX``u>mVc@azF{-+=4024H@E-hC*_ z3xK_K_6fX3NPT92P6f}nDtNBTB+IxyDe&O|G^&%|wAYQ2aa&tk_26||VSxe6&CMk# zEUf@^8U*VQ%npI`v&u+EGXPNc0tgDi1`PEWoL}(t{vfTC z^^FmL>FMc&0>FT%T@Yc#{HFD);Q372udlB+rM|9sl1WnF^|!{Rrltn5mK+4y3PRK% zXuC{D6});-N~OMOCT(nNxUE_M{4sh^a&mGqt9q*fpaT&F48Apj#e|Wq5YFu!DtrT_f8Qa37X zy)yIDL4i`OGHmg)2tYU{SkSY^VLiy zGi3F}N!=l4DT`ynEKv_SVU(2E7sM$tKGt29yML^|Icjol52^L}vGX~QDgXcg07*qoM6N<$f(>R87ytkO diff --git a/modules/highgui/src/files_Qt/Milky/64/35.png b/modules/highgui/src/files_Qt/Milky/64/35.png deleted file mode 100644 index c9b408445f9ecdf3f38cdcae3957554d8d9b7045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4105 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 JPDHLkV1m=IKiU8Q diff --git a/modules/highgui/src/files_Qt/Milky/64/37.png b/modules/highgui/src/files_Qt/Milky/64/37.png deleted file mode 100644 index b0898c8cc99fe6ee74b8af86bc5178bd0c9a9178..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3189 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 b|Ml`8N>&@R-5h004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 IM6N<$g7_2Yt^fc4 diff --git a/modules/highgui/src/files_Qt/Milky/64/39.png b/modules/highgui/src/files_Qt/Milky/64/39.png deleted file mode 100644 index f67ae06b5ef97393db7adc989d29586ca7067d22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3264 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 Y2kQ*t3g0VzsQ>@~07*qoM6N<$f-66v0ssI2 diff --git a/modules/highgui/src/files_Qt/Milky/64/40.png b/modules/highgui/src/files_Qt/Milky/64/40.png deleted file mode 100644 index f39b08537fc3a66fc0357589702ec9b49b940e96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4555 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/41.png b/modules/highgui/src/files_Qt/Milky/64/41.png deleted file mode 100644 index 3061701f56de27a9a73c77127a68850c3fc95373..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4024 zcmV;p4@dBcP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkdK1oDDRCwC$TkDS;*;W6Y>gtz0FOSC_XVxCic;5h8pb zyE`Z!5JG5I`~gcOz91njAdn&yWCsBPA+ak2C<+7{h0#$mOuUK!l6a?Z3qB?KtvF$9^StDM(5t+ zTg!j+{N>IE1%Ryb-H!r;Ug>y?mKp+AFGFx(6xc4xbM1*1SCw{bveL?KLfEkxr zS#;-oB=AOMtB2o*1B7O)5cBCs_!Ah93_2*!E>KqN4*068SY z8t;rvk9o!$KvqO#g+*Tc>(#S2SmcYJyLk2DcO?Q6oj~a%y>pKnPf{#3C2|?3YhJ&nz!J_ZQbjM#R`0z*>-SABq(e zC*I4x*9uB2Xf4p1p;(|f+iJJfVnAYsO7|t*Pd~TEv;FM5{`GQyN858)Ry50{Kl#Pw z7ys`7U_qk#X&R(28x@KZXsw_*f8(hEo*|jBoHNWn&j-pG=6_!muAc#!f-s|5Uis3` zFI{-wDe&7rcI^DaYg^AJKiCJet}H1^Da=eXa3lzFA)SkE8pd`5yJ_CLG{}h29=B;C z_8>E$v;iqjGUqWTcwihCC*t?=|u zeGX@47V;;gI-|t;s?RJd4t^soA1L_(b{^b=iX%K;yN-=_u3+o_I(B-=XSa6y0PyU< z)kFlp^1CaS=H_Ns3`+;LbdfDlZXY;xV!5(R0l>q~Lv*$uTS5|uFjzRofT2 zqYD*~2(tD>a+Rh~cue)NtSBN77Sx&(2v*(&9}he6#ozpg zwbcP7_|lhuMwXTqjqwO*e3q1Kv4>*x}wegOaVSAUMZy&fKRqSfE{+uIl2bwU70 zl!9gjLIBPgZ-dZALKbY4H#}Fbtw2tV7aIlkCI#vH(Iik}xb<}Abxr{RfVbXU!@FDaQh2bjjVG7qaQ(mUpt~32 zY@^v|pw(<6tku!1H!bHlFv_T03QX;Q zFx$Q@opBN)(S7ujK3EH)&CYQ7>*c+8Qqb+^K(V3-BKYXJdE8sy#_1=I;N2^0=EpQd&e{tAelK)4Bw8R=stOSZ=6%8mk|Y6$5JgdDIhECSD)(g}y;%!h zfAbDNw~jM+1Xz#``}4u~1;nXGNK#GhL0oXgsl94`dOYl(u8 zyc+F?r(d^$AY!m(ZLg~u!UcI=$Tvv&V^as~h!c${)=>QfE8l+-orrPj_*7aplU@(A zM`vbI0JiSO&u-t3&x3$Zh`c<1WCEhJpyOBbCrVZA%eI?1?Bmukk`#8Sd!_)}5 zdz1tmj39qM_X~0$XLLadivR~&A+~n=n4fK7d0_%ns{!mZ14H#9Q%W5GU;WY>uK{=s z06zbPXI}pJOdC4x13>@^3<&?o5x%1ZtdUCxpAWQRoE652EJkuU<4@k0$4>nM5&A8% z6e7W-3bSpI8OBgzs~-#qoG7qJc`v}6od~eCE`TtDRYsOYDK9p+nFVa4Bb_yhRP*Gx z9N>k3NcprVoqC%@AgQ0uu#95Zkwl1t8_FA^>(mR|wgNdb{O znPmU0G&pF1Hhz#h(IeT z4SgcA>ky1`@$F%q)I50*Z9sz8<&mu4*dlU0V0)g7>qaRRZ z^3FvG4he&Eq-Q-YLwbeEXpquMFpvfX5PTA3wi4ePr}HQhG!bBpH?jw(1HrEb3g38h z17{bfa|}=Z{EpJuMCN&+;q(8I&5z{92QpLhlpLgfQbZG z-W))gSDoOo>5&V0vghP=KaErWhiQPqh)@b3IM2J;B>CPKjOGBbQqmbuM5&n2Y=|eqrlnc9fXXfo9slv+mVbFN8B7sJEeM~Vh zPDhwigI*f5os(T7 z%2$s8!Q!!roU{CjFkTrniw5b8z=ah6JheDoFg`12RMIF77*FGRUP`?0FUNZCy($nV zoDNRi{DR^0fK&sZS)eVn>3V*;&|xlcupMUE;YnR86*`2n2T;b9!%|?pVY% zn*zmEWQMVESOg%G0ER>ysW@WsJx}6sfKU3C9AwP(!I2ywgQZXgqk&8A)l~;4m4`+^ z3IG*2z!3|(l4mDFUg;xW42ewk)jfW)h%u`A(jUuwQ)!axTiQwJtq<`0t`n`srR#*M^Yd| z88-(8>OqMhs+tG*N-#?U>AAn-`gQ-TD+_nn@q6hQtcoW>l2j58E18-`R(hWXwI4#_iSHIU#B{NGOrgVL<6I687+onjFMQjMCOB_Ictb?ngT8*%mqj4 z0N3p~G2|8-vUb=uLLSRgHXh2$Ucq}xgZ(r^ep{Ta3!sn!nVKD`1K@d|1nI@JWL2Wf zc=GS4P!7vhi>ll$L!+Q@ULgwn3Q$%B*tYc}%b@>EuWKTLS`Bf+XoSH)!{g?FmBPn2 z+Dj1`7l?6Y1CkNB-}Hz#F)|%5Rd9d{^MKHe6dGZGW<3De6_@FW26iKj=>z0>e^B#N z$pC;z_80mxLKKxqV-a94(r7e7!vg@KNTb;Z(kM_`psSt})I#Vh##%?=Z>}-~z;mBy z;OImEKydPC8{fRPjaooB&AnG%kVSuNPPvC#ndys8q z)MC{jq~yVg_f3YC5}AyCA;Q@wCcrGHhlERO08j&W1V3WcY+o_9diruH0I|m9hug^u zi<3>v9}q;EaB5nxu^E9y&}xR5pJ`xzrh%QFhGHpwN~ZKV&I;>kY#lHhgfJjXwrl9c z8ogKp=;PB14Se%H@UbI?{E~#R-swYw{>21l;U_acd2({)$Yglw=_lHlI#`DY@Sqbx zvtXiK$LwSSEZGCfm4gtf7=eu`h{}Gg$c-6;Ld&`+G4y7bIKbLH4f1CMw6(As$Z&Snf^jirSt%uUw4$#C^t zFei@o6Y?Vacse9w`YUU@*zNZ5k=Yi)fUvcf;9x7nkwY!)^%NfLD7t zPc={v18nalxV90ar||68Z+32^&KUNyx#ymq{;f_DynJffl)QE;1PgFzqA_;(mpxwl zo}LdFF$Z+VloCXVMypZ7UZk+wRfv?}{&s>k>D3EY_AdHqHu86aD<=<~4+DDTa7$K> zv;_4Up-~UeYJ?Rd@M0R!SjJ;tx}an=*;^LtCyai=Py%dpfsGzrP8h5IcJtAVBF~Kd z!^zK_oLvF%{A68L+98(84SN6eo~j004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu z7ytkdu}MThRCwCun^}-t)p^H%=iJ-N^z1W2qXmmWNPsLsWK4l$TxKA4oXQ3^4xxw% zB8#h>2M1n~2R~3e8d+-=`;79?|=UPZ#nn05)nSgAG^^-rPTZW;D0#$M|(jA z1WAAC69b`a~+Brd(NuQYTeD|O+-rxS^ zXMQyCO9jARz3}V%Huhe>|F&)S(VrS&esvPxI4G^rN|TBWFqpcQ3kz>@Wa5YErR?+* zPyO!U1HTjieC4@2$F3dP{o*HfKEkW#pQY%nVp%aP9mCRgWN$N+x|XedxA2p<|Bl(^ zv-kYXgKr=Dg$3Zt-`;t@*7`oJt+CJF{?zEJll`0mqF0l#!?n>yNhl8 zxAW{Pe~}g=2aS>EzxL?4gTF8UeCc1d4`@q0w{!CyV>>r}jLic#vXGtPXXD=sjSc~@ zsu`fQjioKLvbnbZRtA!rna@md^!$$)pMCSdKmO^&WB)Gzc>asK_A14wLhUUzEALUf zaBKFy+xC2JFUoLc?iCCMyJWBx4?xg%nF$r+D*$M1VQC9XCm8M;<$vBeNZ+w@&+NDo zTNa?yWf4JyVr_aD1yKq>ayiLU#1-unPsZ@Y(Bvb$m1<3IZb%_D-ECP=ChoC`QP@QfBA(Mzx(LU z0}p-U%(~^WCIH`k^u_@r^5TYVJNMr9`+tU3KDqHjIE&*LXBDM2N-LDopkR9D2!l7> zO1_XmX$1;=&%+plZ~MeUqm2lHDFa}{V2MGD!DtIDK3Z9530UBm1su1?+<*TyVgxY; zj6sZGrH1Jm*~JZa-p!WXw{heNiVPSCqIB%JKAFONcs< zMOO1^vc+Zc#SD2TgXFFH zLCA(MT1e2`7awEtII4gm11fB*g6uH5FBEd5e1#Yf5e*XDzcL6VAcXX(R~X!N7a#uQ z7)O8d<0pYbZ8Kp1L)-TbZr(iFlhWj7&y{a3VyZ`8s#&nRXCup(R}m465sWVw-(Wn0 zU*3E?*WkN8p5x;?KAs!y9Us?maf%MbLV-ddPcFO4(xqi?+HegUcE&KS7kOq42;p)1 z^bxjfzm{ZA&)7eGesr{L1{lMmTXu}HI(;UfJsPO!9YfW~LomE|3~uE=rq8nEt`bYy zL8xq$Qk1SWicY04M<5@k;L)qPc=&gIi@1M<)R04V!4FzQKtLqi41PXKZg!mO_uS0! z!#}?tc&0r7MD`AC=pj2b8#p^^Kv0o$r9c!0@N#*+Jp5sfWM*;uVkC!p(D5OZPE@Q@ zkw8w+X(?M?IuHGf2-`P}@T)i73>Zv7NDib~opV8{GWC&aUnC;rE?r>cLpyoX$lYxN z&@}`FN4zPmwcAHy28-N@K^^v zphBl=?Q3%B6`VqW58b(yYi_v(yJsWH9NF=ylVweYNqyu}bvP zJ!GMj!T2haK)vyp;NHfV_7fp72IF~Ev9^#c;5aV3fA0=@NAE&eJq?Vh2Vza#OLGZG zGqAJ@e8Hebd_NM7D_LSRSu!mqeRX375Q+7{et9v&mc5(k-S#V>>?V{pHO9*t#y4k8 zE6J#252;}rVS~|kUl96?_uYjeJJ@jm_F(p3`VM5t>-(WhC zKsm!QK?JZZ%}P3t_?ZSBGBw7_x?+26l0%!4(Oup2UK2wpgOs9yM3Ny1ow@16307rQ z20M(mmrtAzI?#Xx(nffzHom>~N4UkgdH*sQrywVk1SCY$2x1@^vnb|WF1>T6Ns=QS zv0#0YiPjX6_DZ3;y6N2*D-T+f0mih>1DZ-82m%!*SQ>0gbLrj77-ylnLZ7s1ZwxB2718l>pR1f;rwlNm^Q&Tk z2wh2=nNtf$VKMAb>rs^soussE4`yW*d&^xF$wsIzeun_~Rbj9q0b?l3VD8N1y1cox zSqYYake2O1oFZ1w9#odK%$|9Iu<137OP7I7B5Vb=|eA}m5PjbrLR}VfZ7{HP%X*8wiIXHn8g&* zZBCE3F&R?VV~zNEK*%o4bK&Pl!lZ|)TA<8;tBV4a%@2X_BaBETZ6;1GB87DObwR_T zsCKJ=afB(5@;EpdQ&KGB|Z~HekUzbuTR$(%5(@Vnk3in7-ip&<)Q2#V-FoX>}uX2hDMJczs%&B3>VJlaGb#CL`>7& z8yH#0Mz+k`D+bU4j)};dzA#!G%=PW@bLep&z`%Z~SpTI@RB@ zvXg8E$*yqz%~>YSWSBgc#VHz+af?LUqPH&=F2j{-qr=fAVyx62m&)Q`?l|54n;}%% z_%fh$FybQ8&<}uwGJt`eIBy?aK;&ia`Rp#bMm9n$Q9Ut|TS2lJ&K;X!{M0hzX9J_V z5*Eq0MgKsY=rZM!18Zn>d0A0C3rfBLU@7MxYa4*M75A8D%$S9$8trlfpq3GeRMKWh z;P}x6&YXCa-5(iZ$F4zCJQ3vVm(xs7W*I-dit8GZF^jIG#b9?F9a>#7uCh|BW@R|7 zMn38f1jiE!u1vJ=15nRTFBHeN4kya=FB>j_^=1DME_+izDrvDbmLk3CaP-wHy#C6S zGEkOQq!KpW37f7ytFHWx)-1vrAk+ZTNdKjRZFQnPl zpK7yHW5DqYb1NQmtNy`%pIlCNv{>4po;p5LBwO_G{rZ$R3Wi^jw9a z4qE%J8?eF{t+BK!w<$-28bB&$tLpW^O@G)^F|^9@Mg2jI(WQ2tZ^##Yd?TEk_nFO@ z$6lITJn)|VW@zlXp~nZ3>d75LdLU^BYXz*Z5-c0JmYu5MVSV;5O-^gTA&m8&Uy($* z2i238yLrtcftSw002ovPDHLkV1mb`+ExGn diff --git a/modules/highgui/src/files_Qt/Milky/64/43.png b/modules/highgui/src/files_Qt/Milky/64/43.png deleted file mode 100644 index f3d9e5d43534c91f7ae321d0c83d4d954164f500..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3763 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/44.png b/modules/highgui/src/files_Qt/Milky/64/44.png deleted file mode 100644 index a549bfe0cb18e52aa961ef21231474eb4ace5234..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4490 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/45.png b/modules/highgui/src/files_Qt/Milky/64/45.png deleted file mode 100644 index 123fa1a3dd2b1413efd4137a0fbab00f283c4de6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4135 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/46.png b/modules/highgui/src/files_Qt/Milky/64/46.png deleted file mode 100644 index 4347e5291658b65f2d238a22e26f050b0e3b5e15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5031 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/47.png b/modules/highgui/src/files_Qt/Milky/64/47.png deleted file mode 100644 index 87502ceed3dc283beffdf471e2a06bff1fc8b0f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3221 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 Hu0mjf*V6g> diff --git a/modules/highgui/src/files_Qt/Milky/64/48.png b/modules/highgui/src/files_Qt/Milky/64/48.png deleted file mode 100644 index 6cc7164b61003b28cdad047062f710dcb70d372c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3797 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 LNkvXXu0mjfC0`4; diff --git a/modules/highgui/src/files_Qt/Milky/64/49.png b/modules/highgui/src/files_Qt/Milky/64/49.png deleted file mode 100644 index d6686e9cd13fa10b95db3769078f1922a5b225f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4077 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/5.png b/modules/highgui/src/files_Qt/Milky/64/5.png deleted file mode 100644 index 9d3c5a36833f51a519c5125098c70d268ffa1e7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3809 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 X#QNG3)Kt)V00000NkvXXu0mjf|I9A1 diff --git a/modules/highgui/src/files_Qt/Milky/64/50.png b/modules/highgui/src/files_Qt/Milky/64/50.png deleted file mode 100644 index 0bc5d92887041c4ce8ea5b90463744814ff10531..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3326 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/53.png b/modules/highgui/src/files_Qt/Milky/64/53.png deleted file mode 100644 index 8c88aaab2a6a0bcc6a63c8858fbb2e9f36a8a483..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4288 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 KMNUMnLSTa7oAJ^B diff --git a/modules/highgui/src/files_Qt/Milky/64/55.png b/modules/highgui/src/files_Qt/Milky/64/55.png deleted file mode 100644 index 533f2dee64ef6b48bc852011a22129100a92b0a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4140 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 JPDHLkV1g{iFERiC diff --git a/modules/highgui/src/files_Qt/Milky/64/58.png b/modules/highgui/src/files_Qt/Milky/64/58.png deleted file mode 100644 index 6142acc80672fb1b09cc2a6740f04ce0fe02c6d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2696 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/60.png b/modules/highgui/src/files_Qt/Milky/64/60.png deleted file mode 100644 index 71adc818f773dda1a6031d11197b9cdfe086e343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4302 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 S{~Gk^?6W`2!G2kT}2rkpnjlvEw{sj1)&ffkT2H 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 Ef}!S+i~s-t diff --git a/modules/highgui/src/files_Qt/Milky/64/65.png b/modules/highgui/src/files_Qt/Milky/64/65.png deleted file mode 100644 index f4e9a2880cb99a3b853fa1b9c42e64d1782163a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4367 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 JPDHLkV1o9|vK;^b diff --git a/modules/highgui/src/files_Qt/Milky/64/66.png b/modules/highgui/src/files_Qt/Milky/64/66.png deleted file mode 100644 index b98c32d0657b3d172774e18f8023313682e7809b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2793 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/67.png b/modules/highgui/src/files_Qt/Milky/64/67.png deleted file mode 100644 index 7251a6710d0ded9168ee5c10cf2b270f59ce2124..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3232 zcmV;R3}5q!P)bI&&(5FQM(@zhjRWsTog6eZVBOiaLy z8#f>pi$(C(%?1c`z54|i-x&-BJ70V4HSl`9OsFi&%pmIRjJ09_Z@it?c~Gb)aI34U zVbi8f5Q#)uhlYkeClFpp4nX5W==uJ2>(+%xiM-^)8D%!Gl#K_0@2vnLkO_QAlHlsq zs}PUJcOVP8pPK;>cQ&>nbRm2|G<^JVq{yY6jg5^Vig)q|Z>*stAJ@|XVSaaEXS6v# z5Yx!`ii!$oZf=I_*RQ{ir@QI583WM2V_{ci^NLPy>sAo&o`VOSmrpM5e!SJ@@}DkT zxG<-TVXU2@yg`o07f^BfAUNFl*Gj=KC_6ta*?4|GlgY4fz>o|hJ82|}%%~2CYzpiP z)cZT#K8)e`eeifPP_-o3adT7UKCA0dSS!V#k(kVlPg=%tPT|R6a8_V`a5|=%3&9w< zXL+yr9G?gH^E5KhC}a%9whZ|7-?oMv4rhnc;cN?31jCDJmOUG}E%CdRo03zpp+57YPjSXln20`k2mjo z^Of!GW62R1oakqsmsNo^UOPA+Eg8VJfBl)rs9&cs zUV`QdfVO$NEfD;%H3KxQ)$8$wCX%Bdh)>~X8xKKkWn+Qt-r)-%$>Wd`;sw7cL|dm0 zfKzcZ2Jkt+3IMI^R}febupSmI&Uu0!@cj2g!Fe$uL9%`sI9I)D>wPmb@&Hg}tv#NZ zgjn(s?ES%UOXGDTWe{WmL`f|CO;kYg=m5Ae2WW1sAP1P~uOQ6}JpCN?se%bpQ8h|B zwgwjs0TmUYEM8w3hPvP)dqEhRqLRVNst^EKQb2U*0BDLYM>jkA>s6X&8S66#KxMi) zN&vtz1PB0079pC57ZpU_5{Ut#1pop9;M7L~ED~%1;P+)K20X_b4ANgl1~{_-SS7#W zA&>>Yfiy0PGcbF5+t`^ynoIRFjtgzAXqa1A3&acHvAAi zJ$*ngJsq6a9Qr?BKD-EGl?y~cgwJ|@PZH=ad-%}s0|&I$7-R#0ZoVN_(u}1~= zJF)Eb`3fs_Nkm6UYJQ@`8<^ljH8HvaM_uMKt|M2fq^Kou;(z1Ek zCs#vdZ9QBbJ%bNJ)Tz2c`g`WrgOpZu09?9oQpoP@4sHJ590So}Ca9eH2)tK5EdXGA zT!4zB|0$>gTqf_%xDaA7gc;sb89;*#!%Z(DfDO8gDbx!o1r670cJdfvkz<2LlRyos zxe&05f3wm2F=ceCxd5G-t`eMX2(%3W7DEb|t=Y#R{v@H%>6io_p9_LjewbIi0D``1 zoG+wtBuPVBOoNhg;Z#7Jb`-Fi8p}3w0ZN)4F44`j3N!xiAsNmKrprh@_aBJWiTO34ye

B|{wxL-4v zkYxewr{i)-b$}fM{LQPe{m#1Tv&aE6VSsfVz!;!{OLpiMaEf`IehB#eaOKOdvet6m zhbiM}7MzNT5F5eyK+*-tbi(o_=yb=Wz7n@PE+s$Y0dV~I@z%kK`)#6o5}a-)2x=UL zg?qWsSl!$Vmu_BQ+21@@We?P7BBVlW5VQZJ50WDqq@xOaH1rqdtzwHG{4$u`Vn`{k z%juQ3&*#gXqc_0mblQ>ucx`0+_U-JoxC4z3)z;Sbx3#r}=-oNR7u|gQ&LueczoVdu z4rpE10yl5pGTE->5}+btrk@N#IPfZ{@|4o|?%jjiw{O#uRNj(TseqIcD)g-S`g&No zawVW0teA7V-OTt%Bm$2gKW6h569{LAhK4p93nQ&9EiEBhL^LKiBL(J26o2b<|8%Q&%dD3F?zEK6d-KupRYMSU=a0^WP?J)lKUGU&vK6U;-j z8XOD;nNp!pqg@pzm8OGZ(SIdtd{qx}aTe85(MckkW}d-m*MfDk)E zp%AQEwTgi2K6UEU*&F~nckVpf)6+A6H;0X7>nQ**A>w$>;$O1@Kreg}u@p1z+O=zN z`0!!&+oemFV9uO5EG~_#ObSo8Iy@Z^Q|}TL=4rZym@seNJm~K3hOVwIxN_wRV*oLY zLW1mi_Uu{o^y$-ozLv*S^s_VF3BgRz5e>^EZ1z=vW3Z~oIp8ZOzkmP!NmOL(Yyp5{eh13`oM=z- z=<5^8K?Z{<)Xtwj&j276V(xhtK{zt2-w0tgKa8GuhYApibIQ7uWC(GjoUsTAUw{2I z{k9e3_2g`RfPr%R5&tl;z!yM9*Gv*OE#6HYLQDnq2lpmdK0759Z2T^V{KroN# zL`_Um-6(v=Y=41*@@e_K2Xjk^hF0@8M`S#WI@s?(`Q6{jS7HPLcAujc+UYYC-W!wy zQ1}rP{_eN+84iI!@-YO%B;Np^7=WP{MLpiNGJe*-5<^AWvHKzS4InKmpG4u2Z~H^6 ztP?u2@@YSsmEUcqeE|UF|1N SMS!IM0000 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 P00000NkvXXu0mjf$%V0k diff --git a/modules/highgui/src/files_Qt/Milky/64/7.png b/modules/highgui/src/files_Qt/Milky/64/7.png deleted file mode 100644 index e97ab37ce8aab51e6e6855f836e494840c3f739b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2862 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/70.png b/modules/highgui/src/files_Qt/Milky/64/70.png deleted file mode 100644 index d07791b28745d4a49916a2a848abea4bfa6a7117..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2371 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/71.png b/modules/highgui/src/files_Qt/Milky/64/71.png deleted file mode 100644 index fa32faa00d16df77d31f4d704b270d2adce156c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3305 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/74.png b/modules/highgui/src/files_Qt/Milky/64/74.png deleted file mode 100644 index 2c7ecf3053f004fa0dda44f67a531e735cbdd0b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2473 zcmV;a30C%rP)oSrtB2-EP~Rj?;eU zY1vWYMDivq1~!O^|A8$ci=_iXLTtzgA$BAPu|Yza5g>se;DJF2LPQ#kGy;t@ij>8m z0is@z;5fF^Hi_f-m9pFYtg0^Vce`)9s=J=;vS)%_(RFp#>)!7>=X~efTOG+T3_8f0 zOb2{BB;b&MLjn%=CV5>jKRud=H?~{xyP=*dj9GGc&)7$K#XT z-QD~oV==J1hfRtb*!pwk?-&O&B<1sY+Su4o7?#oD;o+1c0goR)zR4_pt*fhxbX^Y| z>wE1DPDHqoAjYL>8Z9p`E9~6R#fuje^R+w+Oixc=W^sP)*s)`uOYou(yMDaJ2$0Y) zh7Ce~(>4vVi@pQ!K~>&qP)^r*HNQkOLjp975i$Ab(W5~N0S_NOOtKrsSnThrQ#a5- zt=vpXbt3_vp=a&w?I2^a-UBYRw6rt`NVDLBf^POX&5Vnsbl`l`LI8j}fe#w*d!Yrp zA;B&b>P3eLB^z7NQ zz}~Rm)nGG%j5=U_eVt=;^nh3_MtyyKw6d~7ilT(>_F<&JAY&2Vv)L^7?v9QQ+p=DU zYX}lhI`;SX^J3`f)2Ecn<(g)_*;*ki;$iirr6pbpqqGM$yGk)?76!3LQG~()%%S!6 z_L5tj5N2qI`}|CJ?8;skkJbL%@&Jw$?!1VIv%a@!%LBww7{(gxF zvSJNSe)Z}V7dja5`0?Ym^8;T5m@kGloIH7w3Wb7&2nfo42%Z}WV%iJf+uPfGOaLz2 z-448%;WiN9RScnu0uhKQK}0f{*i}wWmIVpu92$YN2_lecdU|@ew}iPN z`~Z4uYm1YN7zg+uYlgl(z{&bfWVE)n^3;g}3PjAz%y4o*1PDQq)y#(2hP9D}@NC34 z`Z1I1XOYBf!Nj5geywlUu}DEkJ0Jxf@%;I7ZY>Z|zuw>k8dip=!RmMx?gJ6X>$_PZ z^Ay2mtZ%NdVSS{?GiT0l0)*>ACl(hMYxRY2Uw8%xgO=caTB z6PA~kx!^%>oC+JaP_PXFvj*4|A#P*<;Ogos|9s-aiCSEWYd{1thY&Bc`T6;VXA95} zQvj^*3+GXj;2cN*S@l}u^Syz;O3IZov_3Q-zQaIS3hq3ZNlKVZFf#V~a*u#@S5Mu|zNZTls*_ z6yBhr&M!KSp*{p;Hrfy5Lv){Ap{C)3vn499_AAwKNjArd4L<)B`r@8qY{cGHBYTAaZ=mo=;P^I^v0

>(MNM{)0cX`&Qr8l`nUA} zG!!8aITRoO@3#;NYum^!(ZgpSQ(nzbr3;B_T}k|#_16s5ubQs1z!vM6)pXAO6`5zW zb;aq^^;CvF1C3HdjxtL|YI|9Z zu)bt&UF;>QETJXZJ85Hk$vi@;4AG!`DZ?tyK6;@1hQ5-xMlEu@F$8b`DGsq-&yXK* zVf=RB`Nn@JtFJL@bW>5yGP6oGfIFTbRUJUG|6Pk=BEUq%>gEEqw|3C|)tfXDpJ2!0 zjUIp+Uexblxqc8m$Q8D@D?`M9)(T$0mUcqHsZa6O5HwUsD5^PXYdJ#qmw!g*iMmX{-P1*X{ph%6z|@D!twgjz^|1Y*yZqSPLb-~7hU zPv2ul)6uc9vDDqWchhXXJy$9*k zOnzZl^uQbvm5(~3!6;L*TsDyE(&7ga?_yf(wb-!RD$udU=OMsCQ5Vgy>~T(4%bcV# zuG1=?d1J@RsyJ5J?>q&wX{RPl`B^iRy&VBcvJ3v9Au9=Vvzy zQ-8j(an&hgKbzmWhPZw0$2~@@E!x14R$>V+SNh>!ex%%R)2!(0P5@7j(Q>s$s8|$w z!UXT?`#;G{d9EnE{sgevAQq_=JWS{mt*DKD@Y8I{*OJ=*g$FRjH<;p=@@iTw>f;~2 zlTZ1*|FtK8SzBTAha|In%FvC`zy6}A1iAmUC!m;D$C>3Xv6M0O(XUh`=>5{+f1Etv n+aUpm1RN4@NWg*K{wKfy#J$N;snSx|00000NkvXXu0mjfC*i;1 diff --git a/modules/highgui/src/files_Qt/Milky/64/75.png b/modules/highgui/src/files_Qt/Milky/64/75.png deleted file mode 100644 index eecf17ecd4609e472bafd40f834efa9acbeaf976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1800 zcmV+j2lx1iP) zpbUY;8{R9c6KI@ySuy2(a}*_48KjKQXc{V@afa1HgR}gRaMoUOeQZT z>k!Z9v?YzVStmLWdSfVGxNNpq$CvLLC`LA z?1#-nd?u9(0J1~CtD2XgV4;LC3Z0-)&j@>;=KyeUaNrys9)?!ftH=O0&o+eucDfyY z0RYt4+S-!UQ&^*}!uTj0fbnDlEdVsq1T0uxU6tkJWwoFXY0x$m;#@ifo)!Q#c<+tyU^0IU&#SqQy+!6%}hTvsZW_|?)N27+sAYqCFh@W7!`ETr*aKrqdL>Khvyj#$vi zclO$)1;DhjB5gD_Hah$J`_9VBid#R%rt)zvv_ z@1Rhr&ua*dz-Zzes>4!0!vlqYmjM@>1{xrgPyz(B`}y;y1OnY~uPLHzcw4OLWqcnU z9m$N<+S+QQx{3bUEZ{{)2ZRm`KzQ`%krYdS`2G901c9DUCxSMLGdzvBVJvLp-eC>* z%>sVADkd=8=b#_LgW=Q=c!|_Qab?2_MRAPA+ZbD2T`f~`h>g*_-*ZGz=Vdz9mEfPl7S7F|QdF_;33y*%^=Z*vV}fC|$HHQnkpsNq@$xZ@(I!Rj13 zK>5t~hYue*Y^*53!~i^~bb`W)u)!$RxE|wq2UvrrRYj^A;4u!ERCe6gyMYrl9E=*E zGtLhTM2VQE+Jc(Aq<7m~EwoT+I@E?06jpfoe3wQt1_Rwj=lqBvyx!8%B3Z(7Ig<7W zuQ1_Y3<2%Fxgq`)C3*QLPo89zntlxf6&SV+&+}YB{Qmv>h3MJ{qLu-EqNnK2INngD zalZN?>TPkDM!2CZ)FXrt-@Rh1bF6O~G#SH1AWk0UK?sq}9)8pf2RwfKSUTm$j~`MS z&Ub$UF`!771-us$vkMCg67`Ob4*7ta($ssSLGA7BQgAFJpi6<)?9Fkyr!YJs%5zu&y`RcAU2fOBL%Q0m&MZO|7F+iigIn?uPg+PfS8#K{rX z>NZ}#?(~<>Xw_MYiyG%+Z*T9%8~|Rve3{cw%DWKe6B$q^2(9j~-VkhzQ^G^mJxpV`J#mt5+Yg#9sGf$oTm98&Nn63m}q| zkXP~dfQ3kTG!{i^(f0QC{^{%Mo6OnpG5s1eHa6BK4#N!%4KG9mm1MgrjImLHVw3LF zA4dvN$MBK<{{B(Zd?NrJFuKKo-sUwf=wFZ&0u0?I$N3SFOAibTr1RY?5&y5TTi#*- qVgOgn~--P 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/81.png b/modules/highgui/src/files_Qt/Milky/64/81.png deleted file mode 100644 index 8ddba8b635dc43c4737a18d6dc5494472a99a263..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3476 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 BaJ~Ql diff --git a/modules/highgui/src/files_Qt/Milky/64/85.png b/modules/highgui/src/files_Qt/Milky/64/85.png deleted file mode 100644 index baf7045789c49188ab01a0aa924cd14698f9d9fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4341 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/87.png b/modules/highgui/src/files_Qt/Milky/64/87.png deleted file mode 100644 index 39f26f1fbb346bb36e32f1f9a57ac4d1cc563692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3289 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 XZB*C(?FAkM00000NkvXXu0mjfE1y3W diff --git a/modules/highgui/src/files_Qt/Milky/64/88.png b/modules/highgui/src/files_Qt/Milky/64/88.png deleted file mode 100644 index 1cae0fca90e5de4b88f8df78f5fdc9c9083fff81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2916 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/9.png b/modules/highgui/src/files_Qt/Milky/64/9.png deleted file mode 100644 index cce6c7ef884824424ef17946454251679179506a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2685 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/90.png b/modules/highgui/src/files_Qt/Milky/64/90.png deleted file mode 100644 index 3721677dd5a9e6797e180c4d31c53b65a56f4ff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4516 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 b{|PVvs>qv;f2FGf00000NkvXXu0mjfiEUk^ diff --git a/modules/highgui/src/files_Qt/Milky/64/92.png b/modules/highgui/src/files_Qt/Milky/64/92.png deleted file mode 100644 index b3a5a4550a8ab53d0e2cc3939e7a1dde8a9258d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4331 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/97.png b/modules/highgui/src/files_Qt/Milky/64/97.png deleted file mode 100644 index 3c664086e2133a3663c72b02234bf1effbd900b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4290 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 diff --git a/modules/highgui/src/files_Qt/Milky/64/98.png b/modules/highgui/src/files_Qt/Milky/64/98.png deleted file mode 100644 index 0a11c643bbe624d172f97c2bc5867dac8b89809d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3951 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 JPDHLkV1ju0000WV@Og>004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XU000XU0RWnu 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 diff --git a/modules/highgui/src/files_Qt/Milky/README.txt b/modules/highgui/src/files_Qt/Milky/README.txt deleted file mode 100644 index 01ab431457..0000000000 --- a/modules/highgui/src/files_Qt/Milky/README.txt +++ /dev/null @@ -1,19 +0,0 @@ -From: -http://iconeden.com/icon/milky-a-free-vector-iconset.html - -License Agreement - -This is a legal agreement between you (the downloader) and IconEden.com. On download of any royalty-free icons from our website you agree to the following: - -All of the icons remain the property of IconEden.com. The icons can be used royalty-free by the license for any personal or commercial project including web application, web design, software application, mobile application, documentation, presentation, computer game, advertising, film, video. - -You may modify the icons in shape, color, and/or file format and use the modified icons royalty-free according to the license terms for any personal or commercial product. - -The license does not permit the following uses: - - 1. The icons may not be resold, sublicensed, rented, transferred or otherwise made available for use or detached from a product, software application or web page; - 2. The icons may not be placed on any electronic bulletin board or downloadable format; - -You may not use, or allow anyone else to use the icons to create pornographic, libelous, obscene, or defamatory material. - -All icon files are provided "as is". You agree not to hold IconEden.com liable for any damages that may occur due to use, or inability to use, icons or image data from IconEden.com. diff --git a/modules/highgui/src/files_Qt/stylesheet_trackbar.qss b/modules/highgui/src/files_Qt/stylesheet_trackbar.qss deleted file mode 100644 index 9d5fd02051..0000000000 --- a/modules/highgui/src/files_Qt/stylesheet_trackbar.qss +++ /dev/null @@ -1,58 +0,0 @@ -/* //from http://thesmithfam.org/blog/2010/03/10/fancy-qslider-stylesheet/ */ - -QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, -stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, -stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, -stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, -stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 4px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index eba3599a0d..07e555e8b7 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -2131,8 +2131,6 @@ void CvWindow::createToolBar() { myToolBar = new QToolBar(this); myToolBar->setFloatable(false); //is not a window - myToolBar->setFixedHeight(28); - myToolBar->setMinimumWidth(1); foreach (QAction *a, vect_QActions) myToolBar->addAction(a); diff --git a/modules/highgui/src/window_QT.qrc b/modules/highgui/src/window_QT.qrc index efdd8c29a9..d68175a987 100644 --- a/modules/highgui/src/window_QT.qrc +++ b/modules/highgui/src/window_QT.qrc @@ -1,16 +1,15 @@ - files_Qt/Milky/48/28.png - files_Qt/Milky/48/23.png - files_Qt/Milky/48/19.png - files_Qt/Milky/48/24.png - files_Qt/Milky/48/27.png - files_Qt/Milky/48/61.png - files_Qt/Milky/48/106.png - files_Qt/Milky/48/107.png - files_Qt/Milky/48/7.png - files_Qt/Milky/48/43.png - files_Qt/Milky/48/38.png - files_Qt/stylesheet_trackbar.qss + files_Qt/Material/28.png + files_Qt/Material/23.png + files_Qt/Material/19.png + files_Qt/Material/24.png + files_Qt/Material/27.png + files_Qt/Material/61.png + files_Qt/Material/106.png + files_Qt/Material/107.png + files_Qt/Material/7.png + files_Qt/Material/43.png + files_Qt/Material/38.png From 8832a9dbd5ffac0eac29d90d015973bd8a55d09a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 2 Dec 2022 22:48:40 +0000 Subject: [PATCH 232/313] cmake: ocv_check_modules: ignore -latomic and -lstdc++ --- cmake/OpenCVUtils.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 938c4e1f82..cf628d9951 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -866,7 +866,10 @@ macro(ocv_check_modules define) foreach(flag ${${define}_LDFLAGS}) if(flag MATCHES "^-L(.*)") list(APPEND _libs_paths ${CMAKE_MATCH_1}) - elseif(IS_ABSOLUTE "${flag}") + elseif(IS_ABSOLUTE "${flag}" + OR flag STREQUAL "-lstdc++" + OR flag STREQUAL "-latomic" + ) list(APPEND _libs "${flag}") elseif(flag MATCHES "^-l(.*)") set(_lib "${CMAKE_MATCH_1}") From 74d0b4cc780a1c82b1c512b3a303357cb329d73a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 30 Nov 2022 01:26:09 +0000 Subject: [PATCH 233/313] dnn(openvino): fix custom layers BlockingDesc --- modules/dnn/src/ie_ngraph.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/dnn/src/ie_ngraph.cpp b/modules/dnn/src/ie_ngraph.cpp index 235fa7dcbb..aee3e294e5 100644 --- a/modules/dnn/src/ie_ngraph.cpp +++ b/modules/dnn/src/ie_ngraph.cpp @@ -204,14 +204,13 @@ public: std::vector outDataConfig; #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2) InferenceEngine::SizeVector order; - size_t offset = std::numeric_limits::max(); for (int i = 0; i < node->get_input_size(); ++i) { InferenceEngine::DataConfig conf; auto shape = node->input_value(i).get_shape(); order.resize(shape.size()); std::iota(order.begin(), order.end(), 0); - conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order, offset}); + conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order}); inDataConfig.push_back(conf); } @@ -221,7 +220,7 @@ public: auto shape = node->output(i).get_shape(); order.resize(shape.size()); std::iota(order.begin(), order.end(), 0); - conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order, offset}); + conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order}); outDataConfig.push_back(conf); } #else From d16b3b2487471e8e92fd7566bf174551808dc98d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 2 Dec 2022 23:55:16 +0000 Subject: [PATCH 234/313] dnn(test): restore openvino tests with 'Cannot get memory' message --- modules/dnn/test/test_backends.cpp | 6 -- modules/dnn/test/test_caffe_importer.cpp | 10 --- modules/dnn/test/test_darknet_importer.cpp | 10 --- modules/dnn/test/test_halide_layers.cpp | 6 -- modules/dnn/test/test_layers.cpp | 17 +---- modules/dnn/test/test_onnx_importer.cpp | 74 ++-------------------- modules/dnn/test/test_tf_importer.cpp | 51 +-------------- modules/dnn/test/test_torch_importer.cpp | 13 ---- 8 files changed, 6 insertions(+), 181 deletions(-) diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 1a8b747a65..805e5f4e2d 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -207,12 +207,6 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_Caffe_Different_Width_Height) if (backend == DNN_BACKEND_HALIDE) applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // May hang on some configurations if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index f56512e932..c66f46d8b0 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -699,10 +699,6 @@ TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) #endif #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Check 'backward_compatible_check || in_out_elements_equal' failed at core/src/op/reshape.cpp:427: // While validating node 'v1::Reshape bbox_pred_reshape (bbox_pred[0]:f32{1,84}, Constant_265242[0]:i64{4}) -> (f32{?,?,?,?})' with friendly_name 'bbox_pred_reshape': // Requested output shape {1,6300,4,1} is incompatible with input shape {1, 84} @@ -734,12 +730,6 @@ TEST_P(Test_Caffe_nets, FasterRCNN_zf) ); #endif -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif - if ((backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && target == DNN_TARGET_OPENCL_FP16) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16); diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index c9f8c6271f..98caeef4bc 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -916,20 +916,10 @@ TEST_P(Test_Darknet_layers, shortcut_leaky) } TEST_P(Test_Darknet_layers, shortcut_unequal) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif testDarknetLayer("shortcut_unequal"); } TEST_P(Test_Darknet_layers, shortcut_unequal_2) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif testDarknetLayer("shortcut_unequal_2"); } diff --git a/modules/dnn/test/test_halide_layers.cpp b/modules/dnn/test/test_halide_layers.cpp index c744d0feaa..4db5199232 100644 --- a/modules/dnn/test/test_halide_layers.cpp +++ b/modules/dnn/test/test_halide_layers.cpp @@ -637,12 +637,6 @@ TEST_P(NoParamActivation, Accuracy) Target targetId = get<1>(get<1>(GetParam())); std::string layer_type = get<0>(GetParam()); -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && targetId == DNN_TARGET_CPU && layer_type == "BNLL") - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif - LayerParams lp; lp.type = layer_type; lp.name = "testLayer"; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index b04a30fe64..29e8172793 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1431,12 +1431,6 @@ static void test_dldt_fused_output(Backend backend, Target target) TEST_P(Test_DLDT_layers, fused_output) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif - CV_DNN_REGISTER_LAYER_CLASS(Unsupported, UnsupportedLayer); try { @@ -1596,16 +1590,7 @@ TEST_P(Test_Caffe_layers, Interp) TEST_P(Test_Caffe_layers, DISABLED_Interp) // requires patched protobuf (available in OpenCV source tree only) #endif { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#elif defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000) +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception #endif diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 3d1dd3858a..607302cf0f 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -103,12 +103,6 @@ TEST_P(Test_ONNX_layers, MaxPooling) } TEST_P(Test_ONNX_layers, MaxPooling_2) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif - testONNXModels("two_maxpooling", npy, 0, 0, false, false); } @@ -793,17 +787,6 @@ TEST_P(Test_ONNX_layers, Slice_Steps_2DInput) TEST_P(Test_ONNX_layers, Slice_Steps_3DInput) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#endif - testONNXModels("slice_opset_11_steps_3d"); } @@ -814,20 +797,6 @@ TEST_P(Test_ONNX_layers, Slice_Steps_4DInput) TEST_P(Test_ONNX_layers, Slice_Steps_5DInput) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // IE exception: Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#endif - testONNXModels("slice_opset_11_steps_5d"); } @@ -850,9 +819,6 @@ TEST_P(Test_ONNX_layers, Split_EltwiseMax) TEST_P(Test_ONNX_layers, LSTM_Activations) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // IE exception: Node Block1326/lstm/reshape_0/permute was not assigned on any pointed device if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, @@ -889,9 +855,6 @@ TEST_P(Test_ONNX_layers, LSTM_hidden) TEST_P(Test_ONNX_layers, LSTM_hidden_bidirectional) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // IE exception: Node Transpose_45 was not assigned on any pointed device. if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, @@ -919,11 +882,7 @@ TEST_P(Test_ONNX_layers, LSTM_cell_forward) } TEST_P(Test_ONNX_layers, LSTM_cell_bidirectional) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#elif defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021040000) +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021040000) // Ngraph operation Reshape with name LSTM_16/lstm_y/reshape has dynamic output shape on 0 port, but CPU plug-in supports only static shape if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -1285,16 +1244,7 @@ TEST_P(Test_ONNX_layers, MaxPoolSigmoid1d) TEST_P(Test_ONNX_layers, MaxPool1d_Twise) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#elif defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) { if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); @@ -1309,11 +1259,7 @@ TEST_P(Test_ONNX_layers, MaxPool1d_Twise) TEST_P(Test_ONNX_layers, AvePool1d) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#elif defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) { if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); @@ -1328,19 +1274,7 @@ TEST_P(Test_ONNX_layers, AvePool1d) TEST_P(Test_ONNX_layers, PoolConv1d) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#elif defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) { if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index d21a5589f9..2d94ba8df2 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -207,9 +207,6 @@ TEST_P(Test_TensorFlow_layers, conv_keras_atrous_conv2d_same) TEST_P(Test_TensorFlow_layers, conv_pool_nchw) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -476,9 +473,6 @@ TEST_P(Test_TensorFlow_layers, slim_batch_norm) TEST_P(Test_TensorFlow_layers, pooling_max_pool_even) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -491,9 +485,6 @@ TEST_P(Test_TensorFlow_layers, pooling_max_pool_even) TEST_P(Test_TensorFlow_layers, pooling_max_pool_odd_valid) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -506,9 +497,6 @@ TEST_P(Test_TensorFlow_layers, pooling_max_pool_odd_valid) TEST_P(Test_TensorFlow_layers, pooling_max_pool_odd_same) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -789,14 +777,6 @@ TEST_P(Test_TensorFlow_layers, BiasAdd) TEST_P(Test_TensorFlow_layers, l2_normalize_3d) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); // accuracy if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -999,9 +979,6 @@ TEST_P(Test_TensorFlow_nets, Faster_RCNN_resnet50_coco_2018_01_28) ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: subgraphTopoSortsStep < subgraphs.size() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -1210,9 +1187,6 @@ TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_single_conv) TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_max_pool_odd_same) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -1241,9 +1215,6 @@ TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_padding_valid) TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_max_pool_even) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -1268,9 +1239,6 @@ TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_deconvolution) TEST_P(Test_TensorFlow_layers, fp16_weights_fp16_max_pool_odd_valid) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); // [ GENERAL_ERROR ] AssertionFailed: !expired() if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); @@ -1355,16 +1323,6 @@ TEST_P(Test_TensorFlow_layers, split_equals) TEST_P(Test_TensorFlow_layers, resize_nearest_neighbor) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#endif runTensorFlowNet("resize_nearest_neighbor"); } TEST_P(Test_TensorFlow_layers, resize_nearest_neighbor_keras_upsampling2d) @@ -1434,14 +1392,7 @@ TEST_P(Test_TensorFlow_layers, relu6) TEST_P(Test_TensorFlow_layers, subpixel) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); -#elif defined(INF_ENGINE_RELEASE) +#if defined(INF_ENGINE_RELEASE) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index 9205a3fbfa..d94ff4368a 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -255,19 +255,6 @@ TEST_P(Test_Torch_layers, net_inception_block) TEST_P(Test_Torch_layers, net_normalize) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); - // Cannot get memory! - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) - applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, - CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION - ); -#endif runTorchNet("net_normalize", "", false, true); } From 4792837f2e357851e76315ebfbdbac31e4431ab8 Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Sat, 3 Dec 2022 05:29:04 +0000 Subject: [PATCH 235/313] Merge pull request #22865 from cpoerschke:3.4-issue-22860 ocl_minMaxIdx to call minmaxloc.cl for OpenCL 1.2+ only --- modules/core/src/minmax.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/core/src/minmax.cpp b/modules/core/src/minmax.cpp index 43785d839b..8f807bed56 100644 --- a/modules/core/src/minmax.cpp +++ b/modules/core/src/minmax.cpp @@ -973,6 +973,12 @@ bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minLoc return false; #endif + if (dev.deviceVersionMajor() == 1 && dev.deviceVersionMinor() < 2) + { + // 'static' storage class specifier used by "minmaxloc" is available from OpenCL 1.2+ only + return false; + } + bool doubleSupport = dev.doubleFPConfig() > 0, haveMask = !_mask.empty(), haveSrc2 = _src2.kind() != _InputArray::NONE; int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), From d1ff87d94d9307b63276233fea2dcaf21d8e5c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20O=C5=BCa=C5=84ski?= Date: Tue, 20 Jul 2021 13:49:40 +0200 Subject: [PATCH 236/313] Bugfix for solvePnPRansac with SOLVEPNP_ITERATIVE The current implementation overwrites the result rotation and translation in every iteration. If SOLVEPNP_ITERATIVE was run as a refinement it will start from the incorrect initial transformation thus degrading the final outcome. --- modules/calib3d/src/solvepnp.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/calib3d/src/solvepnp.cpp b/modules/calib3d/src/solvepnp.cpp index ad5c85b222..21a260f720 100644 --- a/modules/calib3d/src/solvepnp.cpp +++ b/modules/calib3d/src/solvepnp.cpp @@ -149,12 +149,13 @@ public: int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE { Mat opoints = _m1.getMat(), ipoints = _m2.getMat(); - + Mat iter_rvec = rvec.clone(); + Mat iter_tvec = tvec.clone(); bool correspondence = solvePnP( _m1, _m2, cameraMatrix, distCoeffs, - rvec, tvec, useExtrinsicGuess, flags ); + iter_rvec, iter_tvec, useExtrinsicGuess, flags ); Mat _local_model; - hconcat(rvec, tvec, _local_model); + hconcat(iter_rvec, iter_tvec, _local_model); _local_model.copyTo(_model); return correspondence; @@ -313,7 +314,13 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints, ipoints_inliers.resize(npoints1); try { - result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix, + if (flags == SOLVEPNP_ITERATIVE && !useExtrinsicGuess) + { + rvec = _local_model.col(0).clone(); + tvec = _local_model.col(1).clone(); + useExtrinsicGuess = true; + } + result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, (flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P) ? SOLVEPNP_EPNP : flags) ? 1 : -1; } From cb8f1dca3bd27599cfc45fc1161e79125a246e13 Mon Sep 17 00:00:00 2001 From: Zihao Mu Date: Tue, 6 Dec 2022 13:54:32 +0800 Subject: [PATCH 237/313] Merge pull request #22808 from zihaomu:nanotrack [teset data in opencv_extra](https://github.com/opencv/opencv_extra/pull/1016) NanoTrack is an extremely lightweight and fast object-tracking model. The total size is **1.1 MB**. And the FPS on M1 chip is **150**, on Raspberry Pi 4 is about **30**. (Float32 CPU only) With this model, many users can run object tracking on the edge device. The author of NanoTrack is @HonglinChu. The original repo is https://github.com/HonglinChu/NanoTrack. ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../video/include/opencv2/video/tracking.hpp | 37 ++ modules/video/misc/python/pyopencv_video.hpp | 1 + modules/video/src/tracking/tracker_nano.cpp | 359 ++++++++++++++++++ modules/video/test/test_trackers.cpp | 103 +++-- samples/dnn/nanotrack_tracker.cpp | 183 +++++++++ samples/python/tracker.py | 15 +- 6 files changed, 655 insertions(+), 43 deletions(-) create mode 100644 modules/video/src/tracking/tracker_nano.cpp create mode 100644 samples/dnn/nanotrack_tracker.cpp diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 7ec6bc55cd..43877e4848 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -849,6 +849,43 @@ public: //bool update(InputArray image, CV_OUT Rect& boundingBox) CV_OVERRIDE; }; +/** @brief the Nano tracker is a super lightweight dnn-based general object tracking. + * + * Nano tracker is much faster and extremely lightweight due to special model structure, the whole model size is about 1.1 MB. + * Nano tracker needs two models: one for feature extraction (backbone) and the another for localization (neckhead). + * Please download these two onnx models at:https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack/models/onnx. + * Original repo is here: https://github.com/HonglinChu/NanoTrack + * Author:HongLinChu, 1628464345@qq.com + */ +class CV_EXPORTS_W TrackerNano : public Tracker +{ +protected: + TrackerNano(); // use ::create() +public: + virtual ~TrackerNano() CV_OVERRIDE; + + struct CV_EXPORTS_W_SIMPLE Params + { + CV_WRAP Params(); + CV_PROP_RW std::string backbone; + CV_PROP_RW std::string neckhead; + CV_PROP_RW int backend; + CV_PROP_RW int target; + }; + + /** @brief Constructor + @param parameters NanoTrack parameters TrackerNano::Params + */ + static CV_WRAP + Ptr create(const TrackerNano::Params& parameters = TrackerNano::Params()); + + /** @brief Return tracking score + */ + CV_WRAP virtual float getTrackingScore() = 0; + + //void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE; + //bool update(InputArray image, CV_OUT Rect& boundingBox) CV_OVERRIDE; +}; //! @} video_track diff --git a/modules/video/misc/python/pyopencv_video.hpp b/modules/video/misc/python/pyopencv_video.hpp index ea8977911f..02d890d859 100644 --- a/modules/video/misc/python/pyopencv_video.hpp +++ b/modules/video/misc/python/pyopencv_video.hpp @@ -2,4 +2,5 @@ typedef TrackerMIL::Params TrackerMIL_Params; typedef TrackerGOTURN::Params TrackerGOTURN_Params; typedef TrackerDaSiamRPN::Params TrackerDaSiamRPN_Params; +typedef TrackerNano::Params TrackerNano_Params; #endif diff --git a/modules/video/src/tracking/tracker_nano.cpp b/modules/video/src/tracking/tracker_nano.cpp new file mode 100644 index 0000000000..aaf2d0f2e9 --- /dev/null +++ b/modules/video/src/tracking/tracker_nano.cpp @@ -0,0 +1,359 @@ +// 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. + +// This file is modified from the https://github.com/HonglinChu/NanoTrack/blob/master/ncnn_macos_nanotrack/nanotrack.cpp +// Author, HongLinChu, 1628464345@qq.com +// Adapt to OpenCV, ZihaoMu: zihaomu@outlook.com + +// Link to original inference code: https://github.com/HonglinChu/NanoTrack +// Link to original training repo: https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack + +#include "../precomp.hpp" +#ifdef HAVE_OPENCV_DNN +#include "opencv2/dnn.hpp" +#endif + +namespace cv { + +TrackerNano::TrackerNano() +{ + // nothing +} + +TrackerNano::~TrackerNano() +{ + // nothing +} + +TrackerNano::Params::Params() +{ + backbone = "backbone.onnx"; + neckhead = "neckhead.onnx"; +#ifdef HAVE_OPENCV_DNN + backend = dnn::DNN_BACKEND_DEFAULT; + target = dnn::DNN_TARGET_CPU; +#else + backend = -1; // invalid value + target = -1; // invalid value +#endif +} + +#ifdef HAVE_OPENCV_DNN +static void softmax(const Mat& src, Mat& dst) +{ + Mat maxVal; + cv::max(src.row(1), src.row(0), maxVal); + + src.row(1) -= maxVal; + src.row(0) -= maxVal; + + exp(src, dst); + + Mat sumVal = dst.row(0) + dst.row(1); + dst.row(0) = dst.row(0) / sumVal; + dst.row(1) = dst.row(1) / sumVal; +} + +static float sizeCal(float w, float h) +{ + float pad = (w + h) * 0.5f; + float sz2 = (w + pad) * (h + pad); + return sqrt(sz2); +} + +static Mat sizeCal(const Mat& w, const Mat& h) +{ + Mat pad = (w + h) * 0.5; + Mat sz2 = (w + pad).mul((h + pad)); + + cv::sqrt(sz2, sz2); + return sz2; +} + +// Similar python code: r = np.maximum(r, 1. / r) # r is matrix +static void elementReciprocalMax(Mat& srcDst) +{ + size_t totalV = srcDst.total(); + float* ptr = srcDst.ptr(0); + for (size_t i = 0; i < totalV; i++) + { + float val = *(ptr + i); + *(ptr + i) = std::max(val, 1.0f/val); + } +} + +class TrackerNanoImpl : public TrackerNano +{ +public: + TrackerNanoImpl(const TrackerNano::Params& parameters) + : params(parameters) + { + backbone = dnn::readNet(params.backbone); + neckhead = dnn::readNet(params.neckhead); + + CV_Assert(!backbone.empty()); + CV_Assert(!neckhead.empty()); + + backbone.setPreferableBackend(params.backend); + backbone.setPreferableTarget(params.target); + neckhead.setPreferableBackend(params.backend); + neckhead.setPreferableTarget(params.target); + } + + void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE; + bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE; + float getTrackingScore() CV_OVERRIDE; + + // Save the target bounding box for each frame. + std::vector targetSz = {0, 0}; // H and W of bounding box + std::vector targetPos = {0, 0}; // center point of bounding box (x, y) + float tracking_score; + + TrackerNano::Params params; + + struct trackerConfig + { + float windowInfluence = 0.455f; + float lr = 0.37f; + float contextAmount = 0.5; + bool swapRB = true; + int totalStride = 16; + float penaltyK = 0.055f; + }; + +protected: + const int exemplarSize = 127; + const int instanceSize = 255; + + trackerConfig trackState; + int scoreSize; + Size imgSize = {0, 0}; + Mat hanningWindow; + Mat grid2searchX, grid2searchY; + + dnn::Net backbone, neckhead; + Mat image; + + void getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz); + void generateGrids(); +}; + +void TrackerNanoImpl::generateGrids() +{ + int sz = scoreSize; + const int sz2 = sz / 2; + + std::vector x1Vec(sz, 0); + + for (int i = 0; i < sz; i++) + { + x1Vec[i] = i - sz2; + } + + Mat x1M(1, sz, CV_32FC1, x1Vec.data()); + + cv::repeat(x1M, sz, 1, grid2searchX); + cv::repeat(x1M.t(), 1, sz, grid2searchY); + + grid2searchX *= trackState.totalStride; + grid2searchY *= trackState.totalStride; + + grid2searchX += instanceSize/2; + grid2searchY += instanceSize/2; +} + +void TrackerNanoImpl::init(InputArray image_, const Rect &boundingBox_) +{ + scoreSize = (instanceSize - exemplarSize) / trackState.totalStride + 8; + trackState = trackerConfig(); + image = image_.getMat().clone(); + + // convert Rect2d from left-up to center. + targetPos[0] = float(boundingBox_.x) + float(boundingBox_.width) * 0.5f; + targetPos[1] = float(boundingBox_.y) + float(boundingBox_.height) * 0.5f; + + targetSz[0] = float(boundingBox_.width); + targetSz[1] = float(boundingBox_.height); + + imgSize = image.size(); + + // Extent the bounding box. + float sumSz = targetSz[0] + targetSz[1]; + float wExtent = targetSz[0] + trackState.contextAmount * (sumSz); + float hExtent = targetSz[1] + trackState.contextAmount * (sumSz); + int sz = int(cv::sqrt(wExtent * hExtent)); + + Mat crop; + getSubwindow(crop, image, sz, exemplarSize); + Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB); + + backbone.setInput(blob); + Mat out = backbone.forward(); // Feature extraction. + neckhead.setInput(out, "input1"); + + createHanningWindow(hanningWindow, Size(scoreSize, scoreSize), CV_32F); + generateGrids(); +} + +void TrackerNanoImpl::getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz) +{ + Scalar avgChans = mean(srcImg); + Size imgSz = srcImg.size(); + int c = (originalSz + 1) / 2; + + int context_xmin = targetPos[0] - c; + int context_xmax = context_xmin + originalSz - 1; + int context_ymin = targetPos[1] - c; + int context_ymax = context_ymin + originalSz - 1; + + int left_pad = std::max(0, -context_xmin); + int top_pad = std::max(0, -context_ymin); + int right_pad = std::max(0, context_xmax - imgSz.width + 1); + int bottom_pad = std::max(0, context_ymax - imgSz.height + 1); + + context_xmin += left_pad; + context_xmax += left_pad; + context_ymin += top_pad; + context_ymax += top_pad; + + Mat cropImg; + if (left_pad == 0 && top_pad == 0 && right_pad == 0 && bottom_pad == 0) + { + // Crop image without padding. + cropImg = srcImg(cv::Rect(context_xmin, context_ymin, + context_xmax - context_xmin + 1, context_ymax - context_ymin + 1)); + } + else // Crop image with padding, and the padding value is avgChans + { + cv::Mat tmpMat; + cv::copyMakeBorder(srcImg, tmpMat, top_pad, bottom_pad, left_pad, right_pad, cv::BORDER_CONSTANT, avgChans); + cropImg = tmpMat(cv::Rect(context_xmin, context_ymin, context_xmax - context_xmin + 1, context_ymax - context_ymin + 1)); + } + resize(cropImg, dstCrop, Size(resizeSz, resizeSz)); +} + +bool TrackerNanoImpl::update(InputArray image_, Rect &boundingBoxRes) +{ + image = image_.getMat().clone(); + int targetSzSum = targetSz[0] + targetSz[1]; + + float wc = targetSz[0] + trackState.contextAmount * targetSzSum; + float hc = targetSz[1] + trackState.contextAmount * targetSzSum; + float sz = cv::sqrt(wc * hc); + float scale_z = exemplarSize / sz; + float sx = sz * (instanceSize / exemplarSize); + targetSz[0] *= scale_z; + targetSz[1] *= scale_z; + + Mat crop; + getSubwindow(crop, image, int(sx), instanceSize); + + Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB); + backbone.setInput(blob); + Mat xf = backbone.forward(); + neckhead.setInput(xf, "input2"); + std::vector outputName = {"output1", "output2"}; + std::vector outs; + neckhead.forward(outs, outputName); + + CV_Assert(outs.size() == 2); + + Mat clsScore = outs[0]; // 1x2x16x16 + Mat bboxPred = outs[1]; // 1x4x16x16 + + clsScore = clsScore.reshape(0, {2, scoreSize, scoreSize}); + bboxPred = bboxPred.reshape(0, {4, scoreSize, scoreSize}); + + Mat scoreSoftmax; // 2x16x16 + softmax(clsScore, scoreSoftmax); + + Mat score = scoreSoftmax.row(1); + score = score.reshape(0, {scoreSize, scoreSize}); + + Mat predX1 = grid2searchX - bboxPred.row(0).reshape(0, {scoreSize, scoreSize}); + Mat predY1 = grid2searchY - bboxPred.row(1).reshape(0, {scoreSize, scoreSize}); + Mat predX2 = grid2searchX + bboxPred.row(2).reshape(0, {scoreSize, scoreSize}); + Mat predY2 = grid2searchY + bboxPred.row(3).reshape(0, {scoreSize, scoreSize}); + + // size penalty + // scale penalty + Mat sc = sizeCal(predX2 - predX1, predY2 - predY1)/sizeCal(targetPos[0], targetPos[1]); + elementReciprocalMax(sc); + + // ratio penalty + float ratioVal = targetSz[0] / targetSz[1]; + + Mat ratioM(scoreSize, scoreSize, CV_32FC1, Scalar::all(ratioVal)); + Mat rc = ratioM / ((predX2 - predX1) / (predY2 - predY1)); + elementReciprocalMax(rc); + + Mat penalty; + exp(((rc.mul(sc) - 1) * trackState.penaltyK * (-1)), penalty); + Mat pscore = penalty.mul(score); + + // Window penalty + pscore = pscore * (1.0 - trackState.windowInfluence) + hanningWindow * trackState.windowInfluence; + + // get Max + int bestID[2] = { 0, 0 }; + minMaxIdx(pscore, 0, 0, 0, bestID); + + tracking_score = pscore.at(bestID); + + float x1Val = predX1.at(bestID); + float x2Val = predX2.at(bestID); + float y1Val = predY1.at(bestID); + float y2Val = predY2.at(bestID); + + float predXs = (x1Val + x2Val)/2; + float predYs = (y1Val + y2Val)/2; + float predW = (x2Val - x1Val)/scale_z; + float predH = (y2Val - y1Val)/scale_z; + + float diffXs = (predXs - instanceSize / 2) / scale_z; + float diffYs = (predYs - instanceSize / 2) / scale_z; + + targetSz[0] /= scale_z; + targetSz[1] /= scale_z; + + float lr = penalty.at(bestID) * score.at(bestID) * trackState.lr; + + float resX = targetPos[0] + diffXs; + float resY = targetPos[1] + diffYs; + float resW = predW * lr + (1 - lr) * targetSz[0]; + float resH = predH * lr + (1 - lr) * targetSz[1]; + + resX = std::max(0.f, std::min((float)imgSize.width, resX)); + resY = std::max(0.f, std::min((float)imgSize.height, resY)); + resW = std::max(10.f, std::min((float)imgSize.width, resW)); + resH = std::max(10.f, std::min((float)imgSize.height, resH)); + + targetPos[0] = resX; + targetPos[1] = resY; + targetSz[0] = resW; + targetSz[1] = resH; + + // convert center to Rect. + boundingBoxRes = { int(resX - resW/2), int(resY - resH/2), int(resW), int(resH)}; + return true; +} + +float TrackerNanoImpl::getTrackingScore() +{ + return tracking_score; +} + +Ptr TrackerNano::create(const TrackerNano::Params& parameters) +{ + return makePtr(parameters); +} + +#else // OPENCV_HAVE_DNN +Ptr TrackerNano::create(const TrackerNano::Params& parameters) +{ + CV_UNUSED(parameters); + CV_Error(cv::Error::StsNotImplemented, "to use NanoTrack, the tracking module needs to be built with opencv_dnn !"); +} +#endif // OPENCV_HAVE_DNN +} diff --git a/modules/video/test/test_trackers.cpp b/modules/video/test/test_trackers.cpp index 2d0e184408..d080198116 100644 --- a/modules/video/test/test_trackers.cpp +++ b/modules/video/test/test_trackers.cpp @@ -64,40 +64,67 @@ TEST_P(DistanceAndOverlap, GOTURN) INSTANTIATE_TEST_CASE_P(Tracking, DistanceAndOverlap, TESTSET_NAMES); -TEST(GOTURN, memory_usage) +static bool checkIOU(const Rect& r0, const Rect& r1, double threshold) { - cv::Rect roi(145, 70, 85, 85); + int interArea = (r0 & r1).area(); + double iouVal = (interArea * 1.0 )/ (r0.area() + r1.area() - interArea);; + if (iouVal > threshold) + return true; + else + { + std::cout <<"Unmatched IOU: expect IOU val ("< the IOU threadhold ("<& tracker, double iouThreshold = 0.8) +{ + // Template image + Mat img0 = imread(findDataFile("tracking/bag/00000001.jpg"), 1); + + // Tracking image sequence. + std::vector imgs; + imgs.push_back(imread(findDataFile("tracking/bag/00000002.jpg"), 1)); + imgs.push_back(imread(findDataFile("tracking/bag/00000003.jpg"), 1)); + imgs.push_back(imread(findDataFile("tracking/bag/00000004.jpg"), 1)); + imgs.push_back(imread(findDataFile("tracking/bag/00000005.jpg"), 1)); + imgs.push_back(imread(findDataFile("tracking/bag/00000006.jpg"), 1)); + + cv::Rect roi(325, 164, 100, 100); + std::vector targetRois; + targetRois.push_back(cv::Rect(278, 133, 99, 104)); + targetRois.push_back(cv::Rect(293, 88, 93, 110)); + targetRois.push_back(cv::Rect(287, 76, 89, 116)); + targetRois.push_back(cv::Rect(297, 74, 82, 122)); + targetRois.push_back(cv::Rect(311, 83, 78, 125)); + + tracker->init(img0, roi); + CV_Assert(targetRois.size() == imgs.size()); + + for (int i = 0; i < (int)imgs.size(); i++) + { + bool res = tracker->update(imgs[i], roi); + ASSERT_TRUE(res); + ASSERT_TRUE(checkIOU(roi, targetRois[i], iouThreshold)) << cv::format("Fail at img %d.",i); + } +} + +TEST(GOTURN, accuracy) +{ std::string model = cvtest::findDataFile("dnn/gsoc2016-goturn/goturn.prototxt"); std::string weights = cvtest::findDataFile("dnn/gsoc2016-goturn/goturn.caffemodel", false); cv::TrackerGOTURN::Params params; params.modelTxt = model; params.modelBin = weights; cv::Ptr tracker = TrackerGOTURN::create(params); - - string inputVideo = cvtest::findDataFile("tracking/david/data/david.webm"); - cv::VideoCapture video(inputVideo); - ASSERT_TRUE(video.isOpened()) << inputVideo; - - cv::Mat frame; - video >> frame; - ASSERT_FALSE(frame.empty()) << inputVideo; - tracker->init(frame, roi); - string ground_truth_bb; - for (int nframes = 0; nframes < 15; ++nframes) - { - std::cout << "Frame: " << nframes << std::endl; - video >> frame; - bool res = tracker->update(frame, roi); - ASSERT_TRUE(res); - std::cout << "Predicted ROI: " << roi << std::endl; - } + // TODO! GOTURN have low accuracy. Try to remove this api at 5.x. + checkTrackingAccuracy(tracker, 0.08); } -TEST(DaSiamRPN, memory_usage) +TEST(DaSiamRPN, accuracy) { - cv::Rect roi(145, 70, 85, 85); - std::string model = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_model.onnx", false); std::string kernel_r1 = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_kernel_r1.onnx", false); std::string kernel_cls1 = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_kernel_cls1.onnx", false); @@ -106,24 +133,18 @@ TEST(DaSiamRPN, memory_usage) params.kernel_r1 = kernel_r1; params.kernel_cls1 = kernel_cls1; cv::Ptr tracker = TrackerDaSiamRPN::create(params); - - string inputVideo = cvtest::findDataFile("tracking/david/data/david.webm"); - cv::VideoCapture video(inputVideo); - ASSERT_TRUE(video.isOpened()) << inputVideo; - - cv::Mat frame; - video >> frame; - ASSERT_FALSE(frame.empty()) << inputVideo; - tracker->init(frame, roi); - string ground_truth_bb; - for (int nframes = 0; nframes < 15; ++nframes) - { - std::cout << "Frame: " << nframes << std::endl; - video >> frame; - bool res = tracker->update(frame, roi); - ASSERT_TRUE(res); - std::cout << "Predicted ROI: " << roi << std::endl; - } + checkTrackingAccuracy(tracker, 0.7); } +TEST(NanoTrack, accuracy) +{ + std::string backbonePath = cvtest::findDataFile("dnn/onnx/models/nanotrack_backbone_sim.onnx", false); + std::string neckheadPath = cvtest::findDataFile("dnn/onnx/models/nanotrack_head_sim.onnx", false); + + cv::TrackerNano::Params params; + params.backbone = backbonePath; + params.neckhead = neckheadPath; + cv::Ptr tracker = TrackerNano::create(params); + checkTrackingAccuracy(tracker); +} }} // namespace opencv_test:: diff --git a/samples/dnn/nanotrack_tracker.cpp b/samples/dnn/nanotrack_tracker.cpp new file mode 100644 index 0000000000..e98e301f13 --- /dev/null +++ b/samples/dnn/nanotrack_tracker.cpp @@ -0,0 +1,183 @@ +// NanoTrack +// Link to original inference code: https://github.com/HonglinChu/NanoTrack +// Link to original training repo: https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack +// backBone model: https://github.com/HonglinChu/SiamTrackers/blob/master/NanoTrack/models/onnx/nanotrack_backbone_sim.onnx +// headNeck model: https://github.com/HonglinChu/SiamTrackers/blob/master/NanoTrack/models/onnx/nanotrack_head_sim.onnx + +#include +#include + +#include +#include +#include +#include + +using namespace cv; +using namespace cv::dnn; + +const char *keys = + "{ help h | | Print help message }" + "{ input i | | Full path to input video folder, the specific camera index. (empty for camera 0) }" + "{ backbone | backbone.onnx | Path to onnx model of backbone.onnx}" + "{ headneck | headneck.onnx | Path to onnx model of headneck.onnx }" + "{ backend | 0 | Choose one of computation backends: " + "0: automatically (by default), " + "1: Halide language (http://halide-lang.org/), " + "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " + "3: OpenCV implementation, " + "4: VKCOM, " + "5: CUDA }," + "{ target | 0 | Choose one of target computation devices: " + "0: CPU target (by default), " + "1: OpenCL, " + "2: OpenCL fp16 (half-float precision), " + "3: VPU, " + "4: Vulkan, " + "6: CUDA, " + "7: CUDA fp16 (half-float preprocess) }" +; + +static +int run(int argc, char** argv) +{ + // Parse command line arguments. + CommandLineParser parser(argc, argv, keys); + + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + std::string inputName = parser.get("input"); + std::string backbone = parser.get("backbone"); + std::string headneck = parser.get("headneck"); + int backend = parser.get("backend"); + int target = parser.get("target"); + + Ptr tracker; + try + { + TrackerNano::Params params; + params.backbone = samples::findFile(backbone); + params.neckhead = samples::findFile(headneck); + params.backend = backend; + params.target = target; + tracker = TrackerNano::create(params); + } + catch (const cv::Exception& ee) + { + std::cerr << "Exception: " << ee.what() << std::endl; + std::cout << "Can't load the network by using the following files:" << std::endl; + std::cout << "backbone : " << backbone << std::endl; + std::cout << "headneck : " << headneck << std::endl; + return 2; + } + + const std::string winName = "NanoTrack"; + namedWindow(winName, WINDOW_AUTOSIZE); + + // Open a video file or an image file or a camera stream. + VideoCapture cap; + + if (inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1)) + { + int c = inputName.empty() ? 0 : inputName[0] - '0'; + std::cout << "Trying to open camera #" << c << " ..." << std::endl; + if (!cap.open(c)) + { + std::cout << "Capture from camera #" << c << " didn't work. Specify -i=

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 V$HGtKT4W%Y#9nOkUBbV={s-}%F!BHZ literal 0 HcmV?d00001 diff --git a/modules/objdetect/tutorials/images/singlemarkersaxes2.jpg b/modules/objdetect/tutorials/images/singlemarkersaxes2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc8edee15d9515c00e3c15cca0f1940501f8b7f9 GIT binary patch literal 79898 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 literal 0 HcmV?d00001 From cdaf4c73211b247a74e60c8857da84a7d3cd373b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 18 Dec 2022 00:02:07 +0000 Subject: [PATCH 266/313] videoio(test): reduce number of test threads --- modules/videoio/test/test_ffmpeg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index e60b1b69d9..dcd5b86517 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -144,7 +144,7 @@ const videoio_read_params_t videoio_read_params[] = }; INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params), - testing::Values(0, 1, 2, 2000), + testing::Values(0, 1, 2, 50), testing::Values(true, false))); //========================================================================== From 5855eba9f3b23c131083072f19aa02dfc4535787 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 18 Dec 2022 00:14:41 +0000 Subject: [PATCH 267/313] samples: query for Python 3.11 setup --- samples/_winpack_run_python_sample.cmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/_winpack_run_python_sample.cmd b/samples/_winpack_run_python_sample.cmd index ca0d38e21a..bb56046536 100644 --- a/samples/_winpack_run_python_sample.cmd +++ b/samples/_winpack_run_python_sample.cmd @@ -23,6 +23,8 @@ IF %ERRORLEVEL% EQU 0 ( GOTO :PYTHON_FOUND ) +CALL :QUERY_PYTHON 3.11 +IF %ERRORLEVEL% EQU 0 GOTO :PYTHON_FOUND CALL :QUERY_PYTHON 3.10 IF %ERRORLEVEL% EQU 0 GOTO :PYTHON_FOUND CALL :QUERY_PYTHON 3.9 From 4824ce300f74794ea492980eedf2d30aca1da69a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 18 Dec 2022 00:24:52 +0000 Subject: [PATCH 268/313] core: freeze cache directory prefix - "4.x" --- modules/core/src/utils/filesystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp index 663ec311e4..415323490d 100644 --- a/modules/core/src/utils/filesystem.cpp +++ b/modules/core/src/utils/filesystem.cpp @@ -503,7 +503,7 @@ cv::String getCacheDirectory(const char* sub_directory_name, const char* configu if (utils::fs::isDirectory(default_cache_path)) { cv::String default_cache_path_base = utils::fs::join(default_cache_path, "opencv"); - default_cache_path = utils::fs::join(default_cache_path_base, CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) CV_VERSION_STATUS); + default_cache_path = utils::fs::join(default_cache_path_base, "4.x" CV_VERSION_STATUS); if (utils::getConfigurationParameterBool("OPENCV_CACHE_SHOW_CLEANUP_MESSAGE", true) && !utils::fs::isDirectory(default_cache_path)) { From 91998d6424ced366c4e9e7a61e806e53447842a6 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 19 Dec 2022 09:05:15 +0300 Subject: [PATCH 269/313] Merge pull request #22935 from alalek:gapi_error G-API: replace GAPI_Assert() with 'false' and '0' to GAPI_Error() * gapi: GAPI_Error() macro * gapi: replace GAPI_Assert() with 'false' and '0' to GAPI_Error() * build: eliminate 'unreachable code' after CV_Error() (MSVC 2015) * build: eliminate 'unreachable code' warning for MSVS 2015/2017 - observed in constructors stubs with throwing exception --- modules/gapi/CMakeLists.txt | 3 +++ .../include/opencv2/gapi/cpu/gcpukernel.hpp | 10 +++++--- .../opencv2/gapi/fluid/gfluidkernel.hpp | 4 ++-- modules/gapi/include/opencv2/gapi/garray.hpp | 2 +- modules/gapi/include/opencv2/gapi/gopaque.hpp | 2 +- modules/gapi/include/opencv2/gapi/infer.hpp | 4 ++-- modules/gapi/include/opencv2/gapi/media.hpp | 4 ++-- .../gapi/include/opencv2/gapi/own/assert.hpp | 6 +++++ modules/gapi/include/opencv2/gapi/rmat.hpp | 4 ++-- modules/gapi/include/opencv2/gapi/s11n.hpp | 13 ++++++---- .../gapi/include/opencv2/gapi/s11n/base.hpp | 4 ++-- modules/gapi/include/opencv2/gapi/stereo.hpp | 2 +- .../include/opencv2/gapi/streaming/cap.hpp | 2 +- modules/gapi/misc/python/pyopencv_gapi.hpp | 2 +- modules/gapi/misc/python/python_bridge.hpp | 6 ++--- modules/gapi/src/api/gbackend.cpp | 4 ++-- modules/gapi/src/api/gframe.cpp | 2 +- modules/gapi/src/api/gproto.cpp | 2 +- modules/gapi/src/api/s11n.cpp | 6 ++--- .../src/backends/common/gcompoundkernel.cpp | 2 +- .../src/backends/common/serialization.cpp | 24 +++++++++---------- .../src/backends/common/serialization.hpp | 7 +++++- modules/gapi/src/backends/cpu/gcpustereo.cpp | 4 ++-- .../gapi/src/backends/fluid/gfluidbackend.cpp | 16 ++++++------- .../gapi/src/backends/fluid/gfluidbuffer.cpp | 4 ++-- modules/gapi/src/backends/ie/giebackend.cpp | 24 +++++++++---------- modules/gapi/src/backends/ie/giebackend.hpp | 4 ++-- modules/gapi/src/backends/oak/goakbackend.cpp | 21 +++++++--------- .../gapi/src/backends/onnx/gonnxbackend.cpp | 14 +++++------ .../gapi/src/backends/onnx/gonnxbackend.hpp | 2 +- .../src/backends/python/gpythonbackend.cpp | 2 +- .../backends/streaming/gstreamingbackend.cpp | 4 ++-- modules/gapi/src/compiler/gcompiler.cpp | 4 ++-- modules/gapi/src/compiler/gislandmodel.cpp | 4 ++-- modules/gapi/src/compiler/gislandmodel.hpp | 2 +- modules/gapi/src/compiler/gmodelbuilder.cpp | 2 +- modules/gapi/src/compiler/passes/dump_dot.cpp | 4 ++-- modules/gapi/src/compiler/passes/intrin.cpp | 2 +- .../src/compiler/passes/pattern_matching.cpp | 5 ++-- modules/gapi/src/compiler/transactions.hpp | 2 +- modules/gapi/src/executor/gexecutor.cpp | 4 ++-- .../gapi/src/executor/gstreamingexecutor.cpp | 18 +++++++------- .../gstreamer/gstreamer_media_adapter.cpp | 8 +++---- .../src/streaming/gstreamer/gstreamerenv.cpp | 4 ++-- .../streaming/gstreamer/gstreamerpipeline.cpp | 2 +- .../streaming/gstreamer/gstreamersource.cpp | 8 +++---- .../onevpl/accelerators/accel_policy_cpu.cpp | 8 +++---- .../onevpl/accelerators/accel_policy_dx11.cpp | 18 +++++++------- .../accelerators/accel_policy_va_api.cpp | 18 +++++++------- .../accelerators/dx11_alloc_resource.cpp | 8 +++---- .../surface/dx11_frame_adapter.cpp | 8 +++---- .../onevpl/cfg_param_device_selector.cpp | 12 +++++----- .../streaming/onevpl/cfg_params_parser.cpp | 10 ++++---- .../onevpl/data_provider_defines.hpp | 2 +- .../demux/async_mfp_demux_data_provider.cpp | 10 ++++---- .../engine/decode/decode_engine_legacy.cpp | 2 +- .../engine/preproc/preproc_dispatcher.cpp | 8 +++---- .../onevpl/engine/preproc/preproc_engine.cpp | 6 ++--- .../streaming/onevpl/engine/preproc/utils.cpp | 6 ++--- .../engine/preproc_engine_interface.cpp | 4 ++-- .../transcode/transcode_engine_legacy.cpp | 8 +++---- .../streaming/onevpl/file_data_provider.cpp | 11 ++++----- modules/gapi/src/streaming/onevpl/source.cpp | 14 +++++------ .../gapi/src/streaming/onevpl/source_priv.cpp | 6 ++--- modules/gapi/test/common/gapi_core_tests.hpp | 4 ++-- .../gapi/test/common/gapi_operators_tests.hpp | 6 ++--- .../test/common/gapi_stereo_tests_inl.hpp | 4 ++-- .../gapi/test/common/gapi_tests_common.hpp | 16 ++++++------- .../test/common/gapi_video_tests_common.hpp | 10 ++++---- .../gapi/test/infer/gapi_infer_onnx_test.cpp | 4 ++-- modules/gapi/test/s11n/gapi_s11n_tests.cpp | 4 ++-- .../test/streaming/gapi_streaming_tests.cpp | 6 ++--- 72 files changed, 254 insertions(+), 238 deletions(-) diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index a25af7a5c2..3deb518d62 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -41,6 +41,9 @@ if(MSVC) # and IE deprecated code warning C4996 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996) endif() + if(MSVC_VERSION LESS 1920) # MSVS 2015/2017 + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # 'unreachable code' + endif() endif() file(GLOB gapi_ext_hdrs diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index ff3ee45ed3..eb5f784747 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -8,9 +8,9 @@ #ifndef OPENCV_GAPI_GCPUKERNEL_HPP #define OPENCV_GAPI_GCPUKERNEL_HPP -#ifdef _MSC_VER -#pragma warning(disable: 4702) // "Unreachable code" -// on postprocess(...) call inside OCVCallHelper +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) // "Unreachable code" on postprocess(...) call inside OCVCallHelper #endif #include @@ -535,4 +535,8 @@ gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c) } // namespace cv +#if defined _MSC_VER +#pragma warning(pop) +#endif + #endif // OPENCV_GAPI_GCPUKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp index 92f1ccc87f..c3ae9dfdd6 100644 --- a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -248,11 +248,11 @@ struct scratch_helper const cv::GArgs &, gapi::fluid::Buffer &) { - GAPI_Assert(false); + GAPI_Error("InternalError"); } static void help_reset(gapi::fluid::Buffer &) { - GAPI_Assert(false); + GAPI_Error("InternalError"); } }; diff --git a/modules/gapi/include/opencv2/gapi/garray.hpp b/modules/gapi/include/opencv2/gapi/garray.hpp index 55f4d11b12..b6aa715518 100644 --- a/modules/gapi/include/opencv2/gapi/garray.hpp +++ b/modules/gapi/include/opencv2/gapi/garray.hpp @@ -177,7 +177,7 @@ namespace detail { util::get(m_ref).clear(); } - else GAPI_Assert(false); // shouldn't be called in *EXT modes + else GAPI_Error("InternalError"); // shouldn't be called in *EXT modes } // Obtain a WRITE reference to underlying object diff --git a/modules/gapi/include/opencv2/gapi/gopaque.hpp b/modules/gapi/include/opencv2/gapi/gopaque.hpp index f77795c506..1d12f127da 100644 --- a/modules/gapi/include/opencv2/gapi/gopaque.hpp +++ b/modules/gapi/include/opencv2/gapi/gopaque.hpp @@ -171,7 +171,7 @@ namespace detail { util::get(m_ref) = {}; } - else GAPI_Assert(false); // shouldn't be called in *EXT modes + else GAPI_Error("InternalError"); // shouldn't be called in *EXT modes } // Obtain a WRITE reference to underlying object diff --git a/modules/gapi/include/opencv2/gapi/infer.hpp b/modules/gapi/include/opencv2/gapi/infer.hpp index 807c82d31f..c1f9501873 100644 --- a/modules/gapi/include/opencv2/gapi/infer.hpp +++ b/modules/gapi/include/opencv2/gapi/infer.hpp @@ -397,7 +397,7 @@ void inline unpackBlobs(const cv::GInferInputs::Map& blobs, kinds.emplace_back(cv::detail::OpaqueKind::CV_UNKNOWN); break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } } } @@ -629,7 +629,7 @@ infer2(const std::string& tag, kinds.emplace_back(cv::detail::OpaqueKind::CV_RECT); break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } } diff --git a/modules/gapi/include/opencv2/gapi/media.hpp b/modules/gapi/include/opencv2/gapi/media.hpp index 19aaef3fd1..5da8eeab48 100644 --- a/modules/gapi/include/opencv2/gapi/media.hpp +++ b/modules/gapi/include/opencv2/gapi/media.hpp @@ -242,11 +242,11 @@ public: // The default implementation does nothing virtual cv::util::any blobParams() const; virtual void serialize(cv::gapi::s11n::IOStream&) { - GAPI_Assert(false && "Generic serialize method of MediaFrame::IAdapter does nothing by default. " + GAPI_Error("Generic serialize method of MediaFrame::IAdapter does nothing by default. " "Please, implement it in derived class to properly serialize the object."); } virtual void deserialize(cv::gapi::s11n::IIStream&) { - GAPI_Assert(false && "Generic deserialize method of MediaFrame::IAdapter does nothing by default. " + GAPI_Error("Generic deserialize method of MediaFrame::IAdapter does nothing by default. " "Please, implement it in derived class to properly deserialize the object."); } }; diff --git a/modules/gapi/include/opencv2/gapi/own/assert.hpp b/modules/gapi/include/opencv2/gapi/own/assert.hpp index 4bd3eaaf50..ab2fb896f1 100644 --- a/modules/gapi/include/opencv2/gapi/own/assert.hpp +++ b/modules/gapi/include/opencv2/gapi/own/assert.hpp @@ -25,6 +25,8 @@ # define GAPI_DbgAssert(expr) GAPI_DbgAssertNoOp(expr) #endif +#define GAPI_Error(msg) CV_Error(cv::Error::StsError, msg) + #else #include #include @@ -49,6 +51,10 @@ namespace detail # define GAPI_DbgAssert(expr) GAPI_Assert(expr) #endif +#define GAPI_Error(msg) { \ + ::detail::assert_abort(msg, __LINE__, __FILE__, __func__); \ +} + #endif // GAPI_STANDALONE #endif // OPENCV_GAPI_OWN_ASSERT_HPP diff --git a/modules/gapi/include/opencv2/gapi/rmat.hpp b/modules/gapi/include/opencv2/gapi/rmat.hpp index 38668d67a5..46989191b3 100644 --- a/modules/gapi/include/opencv2/gapi/rmat.hpp +++ b/modules/gapi/include/opencv2/gapi/rmat.hpp @@ -112,11 +112,11 @@ public: // is transferred to the device when the view is destroyed virtual View access(Access) = 0; virtual void serialize(cv::gapi::s11n::IOStream&) { - GAPI_Assert(false && "Generic serialize method of RMat::IAdapter does nothing by default. " + GAPI_Error("Generic serialize method of RMat::IAdapter does nothing by default. " "Please, implement it in derived class to properly serialize the object."); } virtual void deserialize(cv::gapi::s11n::IIStream&) { - GAPI_Assert(false && "Generic deserialize method of RMat::IAdapter does nothing by default. " + GAPI_Error("Generic deserialize method of RMat::IAdapter does nothing by default. " "Please, implement it in derived class to properly deserialize the object."); } }; diff --git a/modules/gapi/include/opencv2/gapi/s11n.hpp b/modules/gapi/include/opencv2/gapi/s11n.hpp index 3157a18b84..0bf368a856 100644 --- a/modules/gapi/include/opencv2/gapi/s11n.hpp +++ b/modules/gapi/include/opencv2/gapi/s11n.hpp @@ -17,7 +17,8 @@ #include // FIXME: caused by deserialize_runarg -#if (defined _WIN32 || defined _WIN64) && defined _MSC_VER +#if defined _MSC_VER +#pragma warning(push) #pragma warning(disable: 4702) #endif @@ -335,7 +336,7 @@ IIStream& operator>> (IIStream& is, std::vector &ts) { namespace detail { template IOStream& put_v(IOStream&, const V&, std::size_t) { - GAPI_Assert(false && "variant>>: requested index is invalid"); + GAPI_Error("variant>>: requested index is invalid"); }; template @@ -347,7 +348,7 @@ IOStream& put_v(IOStream& os, const V& v, std::size_t x) { template IIStream& get_v(IIStream&, V&, std::size_t, std::size_t) { - GAPI_Assert(false && "variant<<: requested index is invalid"); + GAPI_Error("variant<<: requested index is invalid"); } template @@ -423,7 +424,7 @@ static GRunArg exec(cv::gapi::s11n::IIStream& is) { template struct deserialize_arg_with_adapter { static GRunArg exec(cv::gapi::s11n::IIStream&) { - GAPI_Assert(false && "No suitable adapter class found during RMat/MediaFrame deserialization. " + GAPI_Error("No suitable adapter class found during RMat/MediaFrame deserialization. " "Please, make sure you've passed them in cv::gapi::deserialize() template"); return GRunArg{}; } @@ -505,4 +506,8 @@ cv::GRunArgs getRunArgsWithAdapters(const std::vector &bytes) { } // namespace gapi } // namespace cv +#if defined _MSC_VER +#pragma warning(pop) +#endif + #endif // OPENCV_GAPI_S11N_HPP diff --git a/modules/gapi/include/opencv2/gapi/s11n/base.hpp b/modules/gapi/include/opencv2/gapi/s11n/base.hpp index 6cea94156e..760e8515f6 100644 --- a/modules/gapi/include/opencv2/gapi/s11n/base.hpp +++ b/modules/gapi/include/opencv2/gapi/s11n/base.hpp @@ -52,7 +52,7 @@ struct S11N: public NotImplemented { * properly overload the function to use it. */ static void serialize(IOStream &, const T &) { - GAPI_Assert(false && "No serialization routine is provided!"); + GAPI_Error("No serialization routine is provided!"); } /** * @brief This function allows user to deserialize their custom type. @@ -61,7 +61,7 @@ struct S11N: public NotImplemented { * properly overload the function to use it. */ static T deserialize(IIStream &) { - GAPI_Assert(false && "No deserialization routine is provided!"); + GAPI_Error("No deserialization routine is provided!"); } }; diff --git a/modules/gapi/include/opencv2/gapi/stereo.hpp b/modules/gapi/include/opencv2/gapi/stereo.hpp index dcf8f4d260..9b00267082 100644 --- a/modules/gapi/include/opencv2/gapi/stereo.hpp +++ b/modules/gapi/include/opencv2/gapi/stereo.hpp @@ -62,7 +62,7 @@ G_TYPED_KERNEL(GStereo, , "org.openc case StereoOutputFormat::DISPARITY_FIXED16_12_4: return left.withDepth(CV_16SC1); default: - GAPI_Assert(false && "Unknown output format!"); + GAPI_Error("Unknown output format!"); } } }; diff --git a/modules/gapi/include/opencv2/gapi/streaming/cap.hpp b/modules/gapi/include/opencv2/gapi/streaming/cap.hpp index 73d5bfcbeb..adf1133c3f 100644 --- a/modules/gapi/include/opencv2/gapi/streaming/cap.hpp +++ b/modules/gapi/include/opencv2/gapi/streaming/cap.hpp @@ -67,7 +67,7 @@ protected: cv::Mat tmp; if (!cap.read(tmp)) { - GAPI_Assert(false && "Couldn't grab the very first frame"); + GAPI_Error("Couldn't grab the very first frame"); } // NOTE: Some decode/media VideoCapture backends continue // owning the video buffer under cv::Mat so in order to diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 8fda308ab8..49b2ddd1eb 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -738,7 +738,7 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, else { // Seems to be impossible case. - GAPI_Assert(false); + GAPI_Error("InternalError"); } } catch (...) diff --git a/modules/gapi/misc/python/python_bridge.hpp b/modules/gapi/misc/python/python_bridge.hpp index 9cbea7f753..07d056abb7 100644 --- a/modules/gapi/misc/python/python_bridge.hpp +++ b/modules/gapi/misc/python/python_bridge.hpp @@ -22,7 +22,7 @@ switch(type) { \ LIST_G(HC, HC) \ default: \ - GAPI_Assert(false && "Unsupported type"); \ + GAPI_Error("Unsupported type"); \ } using cv::gapi::wip::draw::Prim; @@ -157,7 +157,7 @@ public: SWITCH(m_arg.index(), GOPAQUE_TYPE_LIST_G, HC) #undef HC - GAPI_Assert(false); + GAPI_Error("InternalError"); } GAPI_WRAP gapi::ArgType type() { return m_type; } @@ -195,7 +195,7 @@ public: SWITCH(m_arg.index(), GARRAY_TYPE_LIST_G, HC) #undef HC - GAPI_Assert(false); + GAPI_Error("InternalError"); } GAPI_WRAP gapi::ArgType type() { return m_type; } diff --git a/modules/gapi/src/api/gbackend.cpp b/modules/gapi/src/api/gbackend.cpp index e3b1e7123d..efbe17a305 100644 --- a/modules/gapi/src/api/gbackend.cpp +++ b/modules/gapi/src/api/gbackend.cpp @@ -35,7 +35,7 @@ cv::gapi::GBackend::Priv::compile(const ade::Graph&, const std::vector &) const { // ...and this method is here for the same reason! - GAPI_Assert(false); + GAPI_Error("InternalError"); return {}; } @@ -391,7 +391,7 @@ void unbind(Mag& mag, const RcDesc &rc) break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } } diff --git a/modules/gapi/src/api/gframe.cpp b/modules/gapi/src/api/gframe.cpp index b0830b7a63..6e9347c32d 100644 --- a/modules/gapi/src/api/gframe.cpp +++ b/modules/gapi/src/api/gframe.cpp @@ -45,7 +45,7 @@ std::ostream& operator<<(std::ostream& os, const cv::GFrameDesc &d) { case MediaFormat::BGR: os << "BGR"; break; case MediaFormat::NV12: os << "NV12"; break; case MediaFormat::GRAY: os << "GRAY"; break; - default: GAPI_Assert(false && "Invalid media format"); + default: GAPI_Error("Invalid media format"); } os << ' ' << d.size << ']'; return os; diff --git a/modules/gapi/src/api/gproto.cpp b/modules/gapi/src/api/gproto.cpp index 9b012770ca..43bcfb9c14 100644 --- a/modules/gapi/src/api/gproto.cpp +++ b/modules/gapi/src/api/gproto.cpp @@ -313,7 +313,7 @@ std::ostream& operator<<(std::ostream& os, const cv::GMetaArg &arg) break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } return os; diff --git a/modules/gapi/src/api/s11n.cpp b/modules/gapi/src/api/s11n.cpp index bd7f46c88a..989cc79118 100644 --- a/modules/gapi/src/api/s11n.cpp +++ b/modules/gapi/src/api/s11n.cpp @@ -98,7 +98,7 @@ cv::GRunArgsP cv::gapi::bind(cv::GRunArgs &out_args) outputs.emplace_back(&(cv::util::get(res_obj))); break; default: - GAPI_Assert(false && "This value type is not supported!"); // ...maybe because of STANDALONE mode. + GAPI_Error("This value type is not supported!"); // ...maybe because of STANDALONE mode. break; } } @@ -114,7 +114,7 @@ cv::GRunArg cv::gapi::bind(cv::GRunArgP &out) { #if !defined(GAPI_STANDALONE) case T::index_of() : - GAPI_Assert(false && "Please implement this!"); + GAPI_Error("Please implement this!"); break; #endif @@ -138,7 +138,7 @@ cv::GRunArg cv::gapi::bind(cv::GRunArgP &out) default: // ...maybe our types were extended - GAPI_Assert(false && "This value type is UNKNOWN!"); + GAPI_Error("This value type is UNKNOWN!"); break; } return cv::GRunArg(); diff --git a/modules/gapi/src/backends/common/gcompoundkernel.cpp b/modules/gapi/src/backends/common/gcompoundkernel.cpp index 20467117b2..d9410f98eb 100644 --- a/modules/gapi/src/backends/common/gcompoundkernel.cpp +++ b/modules/gapi/src/backends/common/gcompoundkernel.cpp @@ -37,7 +37,7 @@ cv::detail::GCompoundContext::GCompoundContext(const cv::GArgs& in_args) // do nothing - as handled in a special way, see gcompoundkernel.hpp for details // same applies to GMatP break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } } } diff --git a/modules/gapi/src/backends/common/serialization.cpp b/modules/gapi/src/backends/common/serialization.cpp index 13a74c17b5..2a71a782b0 100644 --- a/modules/gapi/src/backends/common/serialization.cpp +++ b/modules/gapi/src/backends/common/serialization.cpp @@ -290,7 +290,7 @@ IOStream& operator<< (IOStream& os, const cv::Mat &m) { case CV_32S: write_mat_data< int32_t>(os, m); break; case CV_32F: write_mat_data< float>(os, m); break; case CV_64F: write_mat_data< double>(os, m); break; - default: GAPI_Assert(false && "Unsupported Mat depth"); + default: GAPI_Error("Unsupported Mat depth"); } return os; } @@ -306,7 +306,7 @@ IIStream& operator>> (IIStream& is, cv::Mat& m) { case CV_32S: read_mat_data< int32_t>(is, m); break; case CV_32F: read_mat_data< float>(is, m); break; case CV_64F: read_mat_data< double>(is, m); break; - default: GAPI_Assert(false && "Unsupported Mat depth"); + default: GAPI_Error("Unsupported Mat depth"); } return is; } @@ -319,10 +319,10 @@ IIStream& operator>> (IIStream& is, cv::gapi::wip::draw::Text &t) { } IOStream& operator<< (IOStream&, const cv::gapi::wip::draw::FText &) { - GAPI_Assert(false && "Serialization: Unsupported << for FText"); + GAPI_Error("Serialization: Unsupported << for FText"); } IIStream& operator>> (IIStream&, cv::gapi::wip::draw::FText &) { - GAPI_Assert(false && "Serialization: Unsupported >> for FText"); + GAPI_Error("Serialization: Unsupported >> for FText"); } IOStream& operator<< (IOStream& os, const cv::gapi::wip::draw::Circle &c) { @@ -398,19 +398,19 @@ IIStream& operator>> (IIStream& is, cv::GArrayDesc &) {return is;} #if !defined(GAPI_STANDALONE) IOStream& operator<< (IOStream& os, const cv::UMat &) { - GAPI_Assert(false && "Serialization: Unsupported << for UMat"); + GAPI_Error("Serialization: Unsupported << for UMat"); return os; } IIStream& operator >> (IIStream& is, cv::UMat &) { - GAPI_Assert(false && "Serialization: Unsupported >> for UMat"); + GAPI_Error("Serialization: Unsupported >> for UMat"); return is; } #endif // !defined(GAPI_STANDALONE) IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &) { - GAPI_Assert(false && "Serialization: Unsupported << for IStreamSource::Ptr"); + GAPI_Error("Serialization: Unsupported << for IStreamSource::Ptr"); return os; } IIStream& operator >> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &) @@ -429,7 +429,7 @@ struct putToStream> { static void put(IOStream&, const Ref &) { - GAPI_Assert(false && "Unsupported type for GArray/GOpaque serialization"); + GAPI_Error("Unsupported type for GArray/GOpaque serialization"); } }; @@ -454,7 +454,7 @@ struct getFromStream> { static void get(IIStream&, Ref &, cv::detail::OpaqueKind) { - GAPI_Assert(false && "Unsupported type for GArray/GOpaque deserialization"); + GAPI_Error("Unsupported type for GArray/GOpaque deserialization"); } }; @@ -560,7 +560,7 @@ IOStream& operator<< (IOStream& os, const cv::GArg &arg) { case cv::detail::OpaqueKind::CV_RECT: os << arg.get(); break; case cv::detail::OpaqueKind::CV_SCALAR: os << arg.get(); break; case cv::detail::OpaqueKind::CV_MAT: os << arg.get(); break; - default: GAPI_Assert(false && "GArg: Unsupported (unknown?) opaque value type"); + default: GAPI_Error("GArg: Unsupported (unknown?) opaque value type"); } } return os; @@ -597,7 +597,7 @@ IIStream& operator>> (IIStream& is, cv::GArg &arg) { HANDLE_CASE(SCALAR , cv::Scalar); HANDLE_CASE(MAT , cv::Mat); #undef HANDLE_CASE - default: GAPI_Assert(false && "GArg: Unsupported (unknown?) opaque value type"); + default: GAPI_Error("GArg: Unsupported (unknown?) opaque value type"); } } return is; @@ -665,7 +665,7 @@ struct initCtor> { static void init(cv::gimpl::Data&) { - GAPI_Assert(false && "Unsupported type for GArray/GOpaque deserialization"); + GAPI_Error("Unsupported type for GArray/GOpaque deserialization"); } }; diff --git a/modules/gapi/src/backends/common/serialization.hpp b/modules/gapi/src/backends/common/serialization.hpp index 3ba2e83581..a64805e25c 100644 --- a/modules/gapi/src/backends/common/serialization.hpp +++ b/modules/gapi/src/backends/common/serialization.hpp @@ -18,7 +18,8 @@ #include "opencv2/gapi/render/render_types.hpp" #include "opencv2/gapi/s11n.hpp" // basic interfaces -#if (defined _WIN32 || defined _WIN64) && defined _MSC_VER +#if defined _MSC_VER +#pragma warning(push) #pragma warning(disable: 4702) #endif @@ -232,4 +233,8 @@ GAPI_EXPORTS std::vector vector_of_strings_deserialize(IIStream& is } // namespace gapi } // namespace cv +#if defined _MSC_VER +#pragma warning(pop) +#endif + #endif // OPENCV_GAPI_COMMON_SERIALIZATION_HPP diff --git a/modules/gapi/src/backends/cpu/gcpustereo.cpp b/modules/gapi/src/backends/cpu/gcpustereo.cpp index 6aec90d30a..385fc50019 100644 --- a/modules/gapi/src/backends/cpu/gcpustereo.cpp +++ b/modules/gapi/src/backends/cpu/gcpustereo.cpp @@ -63,9 +63,9 @@ GAPI_OCV_KERNEL_ST(GCPUStereo, cv::gapi::calib3d::GStereo, StereoSetup) stereoSetup.stereoBM->compute(left, right, out_mat); break; case cv::gapi::StereoOutputFormat::DISPARITY_FIXED16_11_5: - GAPI_Assert(false && "This case may be supported in future."); + GAPI_Error("This case may be supported in future."); default: - GAPI_Assert(false && "Unknown output format!"); + GAPI_Error("Unknown output format!"); } } }; diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/modules/gapi/src/backends/fluid/gfluidbackend.cpp index ed4dda7d49..d24dcd599a 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -313,7 +313,7 @@ static int maxLineConsumption(const cv::GFluidKernel::Kind kind, int window, int } } break; case cv::GFluidKernel::Kind::YUV420toRGB: return inPort == 0 ? 2 : 1; break; - default: GAPI_Assert(false); return 0; + default: GAPI_Error("InternalError"); return 0; } } @@ -325,7 +325,7 @@ static int borderSize(const cv::GFluidKernel::Kind kind, int window) // Resize never reads from border pixels case cv::GFluidKernel::Kind::Resize: return 0; break; case cv::GFluidKernel::Kind::YUV420toRGB: return 0; break; - default: GAPI_Assert(false); return 0; + default: GAPI_Error("InternalError"); return 0; } } @@ -685,7 +685,7 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, case 0: roi = produced; break; case 1: case 2: roi = cv::Rect{ produced.x/2, produced.y/2, produced.width/2, produced.height/2 }; break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } return roi; }; @@ -699,7 +699,7 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, case GFluidKernel::Kind::Filter: resized = produced; break; case GFluidKernel::Kind::Resize: resized = adjResizeRoi(produced, in_meta.size, meta.size); break; case GFluidKernel::Kind::YUV420toRGB: resized = adj420Roi(produced, m_gm.metadata(in_edge).get().port); break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } // All below transformations affect roi of the writer, preserve read start position here @@ -814,7 +814,7 @@ cv::gimpl::FluidGraphInputData cv::gimpl::fluidExtractInputDataFromGraph(const a last_agent++; break; } - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } } @@ -844,7 +844,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph case GFluidKernel::Kind::Filter: agent_ptr.reset(new FluidFilterAgent(g, agent_data.nh)); break; case GFluidKernel::Kind::Resize: agent_ptr.reset(new FluidResizeAgent(g, agent_data.nh)); break; case GFluidKernel::Kind::YUV420toRGB: agent_ptr.reset(new Fluid420toRGBAgent(g, agent_data.nh)); break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } std::tie(agent_ptr->in_buffer_ids, agent_ptr->out_buffer_ids) = std::tie(agent_data.in_buffer_ids, agent_data.out_buffer_ids); return agent_ptr; @@ -1388,7 +1388,7 @@ cv::gimpl::GParallelFluidExecutable::GParallelFluidExecutable(const ade::Graph void cv::gimpl::GParallelFluidExecutable::reshape(ade::Graph&, const GCompileArgs& ) { //TODO: implement ? - GAPI_Assert(false && "Not Implemented;"); + GAPI_Error("Not Implemented;"); } void cv::gimpl::GParallelFluidExecutable::run(std::vector &&input_objs, @@ -1474,7 +1474,7 @@ void GFluidBackendImpl::addMetaSensitiveBackendPasses(ade::ExecutionEngineSetupC case NodeKind::EMIT: case NodeKind::SINK: break; // do nothing for Streaming nodes - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } // switch } // for (gim.nodes()) }); diff --git a/modules/gapi/src/backends/fluid/gfluidbuffer.cpp b/modules/gapi/src/backends/fluid/gfluidbuffer.cpp index 9c2ec000ba..2bdbbbecd6 100644 --- a/modules/gapi/src/backends/fluid/gfluidbuffer.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbuffer.cpp @@ -90,7 +90,7 @@ void fillBorderConstant(int borderSize, cv::Scalar borderValue, cv::Mat& mat) case CV_16S: return &fillConstBorderRow< int16_t>; break; case CV_16U: return &fillConstBorderRow; break; case CV_32F: return &fillConstBorderRow< float >; break; - default: GAPI_Assert(false); return &fillConstBorderRow; + default: GAPI_Error("InternalError"); return &fillConstBorderRow; } }; @@ -231,7 +231,7 @@ void fluid::BufferStorageWithBorder::init(int dtype, int border_size, Border bor case cv::BORDER_REFLECT_101: m_borderHandler.reset(new BorderHandlerT(border_size, dtype)); break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } } diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index e95543e71d..ad9de097e8 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -114,7 +114,7 @@ inline IE::Precision toIE(int depth) { case CV_32S: return IE::Precision::I32; case CV_32F: return IE::Precision::FP32; case CV_16F: return IE::Precision::FP16; - default: GAPI_Assert(false && "IE. Unsupported data type"); + default: GAPI_Error("IE. Unsupported data type"); } return IE::Precision::UNSPECIFIED; } @@ -125,7 +125,7 @@ inline int toCV(IE::Precision prec) { case IE::Precision::I32: return CV_32S; case IE::Precision::I64: return CV_32S; case IE::Precision::FP16: return CV_16F; - default: GAPI_Assert(false && "IE. Unsupported data type"); + default: GAPI_Error("IE. Unsupported data type"); } return -1; } @@ -167,7 +167,7 @@ inline IE::Blob::Ptr wrapIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) { HANDLE(32S, int); HANDLE(16F, int16_t); #undef HANDLE - default: GAPI_Assert(false && "IE. Unsupported data type"); + default: GAPI_Error("IE. Unsupported data type"); } return IE::Blob::Ptr{}; } @@ -190,9 +190,9 @@ inline IE::Blob::Ptr wrapIE(const cv::MediaFrame::View& view, return wrapIE(gray, cv::gapi::ie::TraitAs::IMAGE); } default: - GAPI_Assert(false && "Unsupported media format for IE backend"); + GAPI_Error("Unsupported media format for IE backend"); } - GAPI_Assert(false); + GAPI_Error("InternalError"); } template @@ -225,7 +225,7 @@ inline void copyFromIE(const IE::Blob::Ptr &blob, MatType &mat) { mat.total()); break; } - default: GAPI_Assert(false && "IE. Unsupported data type"); + default: GAPI_Error("IE. Unsupported data type"); } } @@ -439,7 +439,7 @@ void IEUnit::InputFramesDesc::set_param(const input_name_type &input, if (layout != InferenceEngine::NHWC && layout != InferenceEngine::NCHW) { GAPI_LOG_WARNING(nullptr, "Unsupported layout for VPP preproc: " << layout << ", input name: " << input); - GAPI_Assert(false && "Unsupported layout for VPP preproc"); + GAPI_Error("Unsupported layout for VPP preproc"); } GAPI_Assert(inDims.size() == 4u); ret.size.width = static_cast(inDims[3]); @@ -754,7 +754,7 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx, NV12ParamType* blob_params = cv::util::any_cast(&any_blob_params); if (blob_params == nullptr) { - GAPI_Assert(false && "Incorrect type of blobParams:" + GAPI_Error("Incorrect type of blobParams:" "expected std::pair," "with ParamType std::pair>"); @@ -782,7 +782,7 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx, default: GAPI_Assert("Unsupported input shape for IE backend"); } - GAPI_Assert(false); + GAPI_Error("InternalError"); } @@ -967,7 +967,7 @@ cv::gimpl::ie::RequestPool::RequestPool(cv::gapi::ie::InferMode std::bind(&RequestPool::release, this, i)); break; default: - GAPI_Assert(false && "Unsupported cv::gapi::ie::InferMode"); + GAPI_Error("Unsupported cv::gapi::ie::InferMode"); } m_requests.emplace_back(std::move(iexec)); } @@ -1144,7 +1144,7 @@ static void configureInputReshapeByImage(const IE::InputInfo::Ptr& ii, auto input_dims = ii->getTensorDesc().getDims(); const auto size = input_dims.size(); if (size <= 1) { - GAPI_Assert(false && "Unsupported number of dimensions for reshape by image"); + GAPI_Error("Unsupported number of dimensions for reshape by image"); } input_dims.at(size - 2) = static_cast(image_sz.height); input_dims.at(size - 1) = static_cast(image_sz.width); @@ -1173,7 +1173,7 @@ static void configureInputInfo(const IE::InputInfo::Ptr& ii, const cv::GMetaArg // NB: Do nothing break; default: - GAPI_Assert(false && "Unsupported media format for IE backend"); + GAPI_Error("Unsupported media format for IE backend"); } ii->setPrecision(toIE(CV_8U)); break; diff --git a/modules/gapi/src/backends/ie/giebackend.hpp b/modules/gapi/src/backends/ie/giebackend.hpp index fbfeeccd61..c7d938878d 100644 --- a/modules/gapi/src/backends/ie/giebackend.hpp +++ b/modules/gapi/src/backends/ie/giebackend.hpp @@ -62,12 +62,12 @@ public: virtual inline bool canReshape() const override { return false; } virtual inline void reshape(ade::Graph&, const GCompileArgs&) override { - GAPI_Assert(false); // Not implemented yet + GAPI_Error("InternalError"); // Not implemented yet } virtual void run(std::vector &&, std::vector &&) override { - GAPI_Assert(false && "Not implemented"); + GAPI_Error("Not implemented"); } virtual void run(GIslandExecutable::IInput &in, diff --git a/modules/gapi/src/backends/oak/goakbackend.cpp b/modules/gapi/src/backends/oak/goakbackend.cpp index e159160ba9..bb99c21fcf 100644 --- a/modules/gapi/src/backends/oak/goakbackend.cpp +++ b/modules/gapi/src/backends/oak/goakbackend.cpp @@ -39,7 +39,7 @@ class GOAKExecutable final: public GIslandExecutable { friend class OAKKernelParams; virtual void run(std::vector&&, std::vector&&) override { - GAPI_Assert(false && "Not implemented"); + GAPI_Error("Not implemented"); } virtual void run(GIslandExecutable::IInput &in, @@ -121,7 +121,7 @@ public: // FIXME: could it reshape? virtual bool canReshape() const override { return false; } virtual void reshape(ade::Graph&, const GCompileArgs&) override { - GAPI_Assert(false && "GOAKExecutable::reshape() is not supported"); + GAPI_Error("GOAKExecutable::reshape() is not supported"); } virtual void handleNewStream() override; @@ -391,7 +391,7 @@ void cv::gimpl::GOAKExecutable::linkCopy(ade::NodeHandle handle) { for (const auto& copy_next_op : copy_out.front().get()->outNodes()) { const auto& op = m_gm.metadata(copy_next_op).get(); if (op.k.name == "org.opencv.oak.copy") { - GAPI_Assert(false && "Back-to-back Copy operations are not supported in graph"); + GAPI_Error("Back-to-back Copy operations are not supported in graph"); } } @@ -701,14 +701,14 @@ cv::gimpl::GOAKExecutable::GOAKExecutable(const ade::Graph& g, [](ExtractTypeHelper::InputPtr ptr) { return ptr == nullptr; })) { - GAPI_Assert(false && "DAI input are not set"); + GAPI_Error("DAI input are not set"); } if (std::any_of(node_info.outputs.cbegin(), node_info.outputs.cend(), [](ExtractTypeHelper::OutputPtr ptr) { return ptr == nullptr; })) { - GAPI_Assert(false && "DAI outputs are not set"); + GAPI_Error("DAI outputs are not set"); } } } @@ -907,7 +907,7 @@ void cv::gimpl::GOAKExecutable::run(GIslandExecutable::IInput &in, } // FIXME: Add support for remaining types default: - GAPI_Assert(false && "Unsupported type in OAK backend"); + GAPI_Error("Unsupported type in OAK backend"); } out.meta(out_arg, meta); @@ -1080,7 +1080,7 @@ class GOAKBackendImpl final : public cv::gapi::GBackend::Priv { // NB: how could we have non-OAK source in streaming mode, then OAK backend in // streaming mode but without camera input? if (!gm.metadata().contains()) { - GAPI_Assert(false && "OAK backend only supports Streaming mode for now"); + GAPI_Error("OAK backend only supports Streaming mode for now"); } return EPtr{new cv::gimpl::GOAKExecutable(graph, args, nodes, ins_data, outs_data)}; } @@ -1118,14 +1118,11 @@ namespace gapi { namespace oak { cv::gapi::GKernelPackage kernels() { - GAPI_Assert(false && "Built without OAK support"); - return {}; + GAPI_Error("Built without OAK support"); } cv::gapi::GBackend backend() { - GAPI_Assert(false && "Built without OAK support"); - static cv::gapi::GBackend this_backend(nullptr); - return this_backend; + GAPI_Error("Built without OAK support"); } } // namespace oak diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.cpp b/modules/gapi/src/backends/onnx/gonnxbackend.cpp index a99d38654f..b78ba6d05e 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.cpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.cpp @@ -171,7 +171,7 @@ inline int toCV(ONNXTensorElementDataType prec) { case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return CV_32F; case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return CV_32S; case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return CV_32S; - default: GAPI_Assert(false && "ONNX. Unsupported data type"); + default: GAPI_Error("ONNX. Unsupported data type"); } return -1; } @@ -207,7 +207,7 @@ inline void copyFromONNX(Ort::Value &v, cv::Mat& mat) { mat.total()); break; } - default: GAPI_Assert(false && "ONNX. Unsupported data type"); + default: GAPI_Error("ONNX. Unsupported data type"); } } @@ -233,7 +233,7 @@ inline void preprocess(const cv::Mat& src, "32F tensor dimensions should match with all non-dynamic NN input dimensions"); } } else { - GAPI_Assert(false && "32F tensor size should match with NN input"); + GAPI_Error("32F tensor size should match with NN input"); } dst = src; @@ -338,7 +338,7 @@ void preprocess(const cv::MediaFrame::View& view, break; } default: - GAPI_Assert(false && "Unsupported media format for ONNX backend"); + GAPI_Error("Unsupported media format for ONNX backend"); } } @@ -367,7 +367,7 @@ inline Ort::Value createTensor(const Ort::MemoryInfo& memory_info, case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return createTensor(memory_info, tensor_params, data); default: - GAPI_Assert(false && "ONNX. Unsupported data type"); + GAPI_Error("ONNX. Unsupported data type"); } return Ort::Value{nullptr}; } @@ -858,7 +858,7 @@ static void checkInputMeta(const cv::GMetaArg mm) { case cv::MediaFormat::NV12: break; case cv::MediaFormat::BGR: break; default: - GAPI_Assert(false && "Unsupported media format for ONNX backend"); + GAPI_Error("Unsupported media format for ONNX backend"); } break; } break; default: @@ -1101,7 +1101,7 @@ struct InferList2: public cv::detail::KernelTag { const auto &vec = this_vec.rref(); uu.oc->setInput(in_idx, vec[list_idx]); } else { - GAPI_Assert(false && "Only Rect and Mat types are supported for infer list 2!"); + GAPI_Error("Only Rect and Mat types are supported for infer list 2!"); } // }}} (Prepare input) } // }}} (For every input of the net) diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.hpp b/modules/gapi/src/backends/onnx/gonnxbackend.hpp index a3cc897030..8c67df1e1e 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.hpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.hpp @@ -43,7 +43,7 @@ public: virtual inline bool canReshape() const override { return false; } virtual inline void reshape(ade::Graph&, const GCompileArgs&) override { - GAPI_Assert(false); // Not implemented yet + GAPI_Error("InternalError"); // Not implemented yet } virtual void run(std::vector &&input_objs, diff --git a/modules/gapi/src/backends/python/gpythonbackend.cpp b/modules/gapi/src/backends/python/gpythonbackend.cpp index 0c65f4cba8..54fbf7decd 100644 --- a/modules/gapi/src/backends/python/gpythonbackend.cpp +++ b/modules/gapi/src/backends/python/gpythonbackend.cpp @@ -150,7 +150,7 @@ static void writeBack(cv::GRunArg& arg, cv::GRunArgP& out) break; } default: - GAPI_Assert(false && "Unsupported output type"); + GAPI_Error("Unsupported output type"); } } diff --git a/modules/gapi/src/backends/streaming/gstreamingbackend.cpp b/modules/gapi/src/backends/streaming/gstreamingbackend.cpp index 69b5f6c72b..ae7125f2e5 100644 --- a/modules/gapi/src/backends/streaming/gstreamingbackend.cpp +++ b/modules/gapi/src/backends/streaming/gstreamingbackend.cpp @@ -42,7 +42,7 @@ class GStreamingIntrinExecutable final: public cv::gimpl::GIslandExecutable { virtual void run(std::vector &&, std::vector &&) override { - GAPI_Assert(false && "Not implemented"); + GAPI_Error("Not implemented"); } virtual void run(GIslandExecutable::IInput &in, @@ -188,7 +188,7 @@ void Copy::Actor::run(cv::gimpl::GIslandExecutable::IInput &in, break; // FIXME: Add support for remaining types default: - GAPI_Assert(false && "Copy: unsupported data type"); + GAPI_Error("Copy: unsupported data type"); } out.meta(out_arg, in_arg.meta); out.post(std::move(out_arg)); diff --git a/modules/gapi/src/compiler/gcompiler.cpp b/modules/gapi/src/compiler/gcompiler.cpp index bcf91f7dcd..526b2746dc 100644 --- a/modules/gapi/src/compiler/gcompiler.cpp +++ b/modules/gapi/src/compiler/gcompiler.cpp @@ -338,7 +338,7 @@ void cv::gimpl::GCompiler::validateInputMeta() return util::holds_alternative(meta); default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } return false; // should never happen }; @@ -485,7 +485,7 @@ cv::GStreamingCompiled cv::gimpl::GCompiler::produceStreamingCompiled(GPtr &&pg) // Otherwise, set it up with executor object only compiled.priv().setup(std::move(pE)); } - else GAPI_Assert(false && "Impossible happened -- please report a bug"); + else GAPI_Error("Impossible happened -- please report a bug"); return compiled; } diff --git a/modules/gapi/src/compiler/gislandmodel.cpp b/modules/gapi/src/compiler/gislandmodel.cpp index 0567a90e3a..736e4f7170 100644 --- a/modules/gapi/src/compiler/gislandmodel.cpp +++ b/modules/gapi/src/compiler/gislandmodel.cpp @@ -120,7 +120,7 @@ ade::NodeHandle GIsland::producer(const ade::Graph &g, } // Consistency: A GIsland requested for producer() of slot_nh should // always had the appropriate GModel node handle in its m_out_ops vector. - GAPI_Assert(false && "Broken GIslandModel ?."); + GAPI_Error("Broken GIslandModel ?."); } std::string GIsland::name() const @@ -164,7 +164,7 @@ void GIslandModel::generateInitial(GIslandModel::Graph &g, { case NodeType::OP: all_operations.insert(src_nh); break; case NodeType::DATA: data_to_slot[src_nh] = mkSlotNode(g, src_nh); break; - default: GAPI_Assert(false); break; + default: GAPI_Error("InternalError"); break; } } // for (src_g.nodes) diff --git a/modules/gapi/src/compiler/gislandmodel.hpp b/modules/gapi/src/compiler/gislandmodel.hpp index 565b3c4f21..3a1a8d5ab9 100644 --- a/modules/gapi/src/compiler/gislandmodel.hpp +++ b/modules/gapi/src/compiler/gislandmodel.hpp @@ -122,7 +122,7 @@ public: virtual bool canReshape() const = 0; virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0; virtual bool allocatesOutputs() const { return false; } - virtual cv::RMat allocate(const cv::GMatDesc&) const { GAPI_Assert(false && "should never be called"); } + virtual cv::RMat allocate(const cv::GMatDesc&) const { GAPI_Error("should never be called"); } // This method is called when the GStreamingCompiled gets a new // input source to process. Normally this method is called once diff --git a/modules/gapi/src/compiler/gmodelbuilder.cpp b/modules/gapi/src/compiler/gmodelbuilder.cpp index f0e4917d7a..aed3428693 100644 --- a/modules/gapi/src/compiler/gmodelbuilder.cpp +++ b/modules/gapi/src/compiler/gmodelbuilder.cpp @@ -162,7 +162,7 @@ cv::gimpl::Unrolled cv::gimpl::unrollExpr(const GProtoArgs &ins, default: // Unsupported node shape - GAPI_Assert(false); + GAPI_Error("InternalError"); break; } } diff --git a/modules/gapi/src/compiler/passes/dump_dot.cpp b/modules/gapi/src/compiler/passes/dump_dot.cpp index b7f5ea96d3..f7284fb362 100644 --- a/modules/gapi/src/compiler/passes/dump_dot.cpp +++ b/modules/gapi/src/compiler/passes/dump_dot.cpp @@ -149,7 +149,7 @@ void dumpDot(const ade::Graph &g, std::ostream& os) } } break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } } @@ -209,7 +209,7 @@ void dumpDot(const ade::Graph &g, std::ostream& os) } break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); break; } } diff --git a/modules/gapi/src/compiler/passes/intrin.cpp b/modules/gapi/src/compiler/passes/intrin.cpp index 8920be6d4e..a6c1dd289c 100644 --- a/modules/gapi/src/compiler/passes/intrin.cpp +++ b/modules/gapi/src/compiler/passes/intrin.cpp @@ -128,7 +128,7 @@ void traceUp(cv::gimpl::GModel::Graph &g, // this recursive process (e.g. via some other output or branch in the // subgraph) if (g.metadata(nh).get().index != desync_id) { - GAPI_Assert(false && "Desynchronization can't be nested!"); + GAPI_Error("Desynchronization can't be nested!"); } return; // This object belongs to the desync path - exit early. } diff --git a/modules/gapi/src/compiler/passes/pattern_matching.cpp b/modules/gapi/src/compiler/passes/pattern_matching.cpp index 7be2da73ae..d52b48a631 100644 --- a/modules/gapi/src/compiler/passes/pattern_matching.cpp +++ b/modules/gapi/src/compiler/passes/pattern_matching.cpp @@ -371,10 +371,9 @@ cv::gimpl::findMatches(const cv::gimpl::GModel::Graph& patternGraph, testNodeMeta, isAlreadyVisited); default: - GAPI_Assert(false && "Unsupported Node type!"); + break; } - - return false; + GAPI_Error("Unsupported Node type!"); }); if (testIt == testOutputNodesLabeled.end()) { diff --git a/modules/gapi/src/compiler/transactions.hpp b/modules/gapi/src/compiler/transactions.hpp index 9092c66291..200cfcd1b1 100644 --- a/modules/gapi/src/compiler/transactions.hpp +++ b/modules/gapi/src/compiler/transactions.hpp @@ -117,7 +117,7 @@ struct ChangeT { case Direction::In: eh = g.link(m_sibling, m_node); break; case Direction::Out: eh = g.link(m_node, m_sibling); break; - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } GAPI_Assert(eh != nullptr); m_meta.copyTo(g, eh); diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp index 6853c30d3e..bf25302b75 100644 --- a/modules/gapi/src/executor/gexecutor.cpp +++ b/modules/gapi/src/executor/gexecutor.cpp @@ -75,7 +75,7 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr &&g_model) break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); break; } // switch(kind) } // for(gim nodes) @@ -248,7 +248,7 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle & nh, const ade::N break; } default: - GAPI_Assert(false); + GAPI_Error("InternalError"); } } diff --git a/modules/gapi/src/executor/gstreamingexecutor.cpp b/modules/gapi/src/executor/gstreamingexecutor.cpp index 66aad90697..124b27f39c 100644 --- a/modules/gapi/src/executor/gstreamingexecutor.cpp +++ b/modules/gapi/src/executor/gstreamingexecutor.cpp @@ -157,7 +157,7 @@ void sync_data(cv::GRunArgs &results, cv::GRunArgsP &outputs) *cv::util::get(out_obj) = std::move(cv::util::get(res_obj)); break; default: - GAPI_Assert(false && "This value type is not supported!"); // ...maybe because of STANDALONE mode. + GAPI_Error("This value type is not supported!"); // ...maybe because of STANDALONE mode. break; } } @@ -227,7 +227,7 @@ void sync_data(cv::gimpl::stream::Result &r, cv::GOptRunArgsP &outputs) } break; default: // ...maybe because of STANDALONE mode. - GAPI_Assert(false && "This value type is not supported!"); + GAPI_Error("This value type is not supported!"); break; } } @@ -446,7 +446,7 @@ cv::gimpl::StreamMsg QueueReader::getInputVector(std::vector &in_queues, break; } default: - GAPI_Assert(false && "Unsupported cmd type in getInputVector()"); + GAPI_Error("Unsupported cmd type in getInputVector()"); } } // for(in_queues) @@ -920,7 +920,7 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput m_stops_sent++; break; default: - GAPI_Assert(false && "Unreachable code"); + GAPI_Error("Unreachable code"); } for (auto &&q : m_out_queues[out_idx]) @@ -1115,7 +1115,7 @@ void collectorThread(std::vector in_queues, out_queue.push(Cmd{cv::util::get(result)}); break; default: - GAPI_Assert(false && "Unreachable code"); + GAPI_Error("Unreachable code"); } } } @@ -1479,7 +1479,7 @@ cv::gimpl::GStreamingExecutor::GStreamingExecutor(std::unique_ptr && } break; default: - GAPI_Assert(false); + GAPI_Error("InternalError"); break; } // switch(kind) } // for(gim nodes) @@ -1820,9 +1820,9 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GRunArgsP &&outs) return true; } default: - GAPI_Assert(false && "Unsupported cmd type in pull"); + GAPI_Error("Unsupported cmd type in pull"); } - GAPI_Assert(false && "Unreachable code"); + GAPI_Error("Unreachable code"); } bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs) @@ -1853,7 +1853,7 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs) return true; } } - GAPI_Assert(false && "Unreachable code"); + GAPI_Error("Unreachable code"); } cv::gimpl::GAbstractStreamingExecutor::PyPullResult cv::gimpl::GStreamingExecutor::pull() diff --git a/modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.cpp b/modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.cpp index 188f162ffd..b9fce83427 100644 --- a/modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.cpp +++ b/modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.cpp @@ -40,7 +40,7 @@ GStreamerMediaAdapter::GStreamerMediaAdapter(const cv::GFrameDesc& frameDesc, break; } default: { - GAPI_Assert(false && "Non NV12 or GRAY Media format is not expected here"); + GAPI_Error("Non NV12 or GRAY Media format is not expected here"); break; } } @@ -59,7 +59,7 @@ GStreamerMediaAdapter::GStreamerMediaAdapter(const cv::GFrameDesc& frameDesc, break; } default: { - GAPI_Assert(false && "Non NV12 or GRAY Media format is not expected here"); + GAPI_Error("Non NV12 or GRAY Media format is not expected here"); break; } } @@ -160,7 +160,7 @@ cv::MediaFrame::View GStreamerMediaAdapter::access(cv::MediaFrame::Access access break; } default: { - GAPI_Assert(false && "Non NV12 or GRAY Media format is not expected here"); + GAPI_Error("Non NV12 or GRAY Media format is not expected here"); break; } } @@ -171,7 +171,7 @@ cv::MediaFrame::View GStreamerMediaAdapter::access(cv::MediaFrame::Access access } cv::util::any GStreamerMediaAdapter::blobParams() const { - GAPI_Assert(false && "No implementation for GStreamerMediaAdapter::blobParams()"); + GAPI_Error("No implementation for GStreamerMediaAdapter::blobParams()"); } } // namespace gst diff --git a/modules/gapi/src/streaming/gstreamer/gstreamerenv.cpp b/modules/gapi/src/streaming/gstreamer/gstreamerenv.cpp index 138589b9a6..5575c436f9 100644 --- a/modules/gapi/src/streaming/gstreamer/gstreamerenv.cpp +++ b/modules/gapi/src/streaming/gstreamer/gstreamerenv.cpp @@ -69,12 +69,12 @@ GStreamerEnv::~GStreamerEnv() const GStreamerEnv& GStreamerEnv::init() { - GAPI_Assert(false && "Built without GStreamer support!"); + GAPI_Error("Built without GStreamer support!"); } GStreamerEnv::GStreamerEnv() { - GAPI_Assert(false && "Built without GStreamer support!"); + GAPI_Error("Built without GStreamer support!"); } GStreamerEnv::~GStreamerEnv() diff --git a/modules/gapi/src/streaming/gstreamer/gstreamerpipeline.cpp b/modules/gapi/src/streaming/gstreamer/gstreamerpipeline.cpp index 6687076c7e..aaa9b82a34 100644 --- a/modules/gapi/src/streaming/gstreamer/gstreamerpipeline.cpp +++ b/modules/gapi/src/streaming/gstreamer/gstreamerpipeline.cpp @@ -73,7 +73,7 @@ GStreamerPipeline::Priv::~Priv() { } GStreamerPipeline::Priv::Priv(const std::string&) { - GAPI_Assert(false && "Built without GStreamer support!"); + GAPI_Error("Built without GStreamer support!"); } IStreamSource::Ptr GStreamerPipeline::Priv::getStreamingSource(const std::string&, diff --git a/modules/gapi/src/streaming/gstreamer/gstreamersource.cpp b/modules/gapi/src/streaming/gstreamer/gstreamersource.cpp index f1bd438ce2..9fb15729b4 100644 --- a/modules/gapi/src/streaming/gstreamer/gstreamersource.cpp +++ b/modules/gapi/src/streaming/gstreamer/gstreamersource.cpp @@ -205,7 +205,7 @@ void GStreamerSource::Priv::prepareVideoMeta() break; } default: { - GAPI_Assert(false && "Unsupported GStreamerSource FRAME type."); + GAPI_Error("Unsupported GStreamerSource FRAME type."); } } break; @@ -317,7 +317,7 @@ bool GStreamerSource::Priv::retrieveFrame(cv::Mat& data) break; } default: { - GAPI_Assert(false && "retrieveFrame - unsupported GStreamerSource FRAME type."); + GAPI_Error("retrieveFrame - unsupported GStreamerSource FRAME type."); } } } @@ -354,13 +354,13 @@ GStreamerSource::Priv::~Priv() { } GStreamerSource::Priv::Priv(const std::string&, const GStreamerSource::OutputType) { - GAPI_Assert(false && "Built without GStreamer support!"); + GAPI_Error("Built without GStreamer support!"); } GStreamerSource::Priv::Priv(std::shared_ptr, const std::string&, const GStreamerSource::OutputType) { - GAPI_Assert(false && "Built without GStreamer support!"); + GAPI_Error("Built without GStreamer support!"); } bool GStreamerSource::Priv::pull(cv::gapi::wip::Data&) diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp index d81c66b901..893d8f1fa1 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp @@ -65,7 +65,7 @@ static surface_ptr_t create_surface_RGB4_(mfxFrameInfo frameInfo, ", offset: " << out_buf_ptr_offset << ", W: " << surfW << ", H: " << surfH); - GAPI_Assert(false && "Invalid offset"); + GAPI_Error("Invalid offset"); } std::unique_ptr handle(new mfxFrameSurface1); @@ -98,7 +98,7 @@ static surface_ptr_t create_surface_other_(mfxFrameInfo frameInfo, ", offset: " << out_buf_ptr_offset << ", W: " << surfW << ", H: " << surfH); - GAPI_Assert(false && "Invalid offset"); + GAPI_Error("Invalid offset"); } std::unique_ptr handle(new mfxFrameSurface1); @@ -209,7 +209,7 @@ VPLCPUAccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_s if (!pool_table.emplace(preallocated_pool_memory_ptr, std::move(pool)).second) { GAPI_LOG_WARNING(nullptr, "Cannot insert pool, table size: " + std::to_string(pool_table.size()) << ", key: " << preallocated_pool_memory_ptr << " exists"); - GAPI_Assert(false && "Cannot create pool in VPLCPUAccelerationPolicy"); + GAPI_Error("Cannot create pool in VPLCPUAccelerationPolicy"); } return preallocated_pool_memory_ptr; @@ -248,7 +248,7 @@ VPLCPUAccelerationPolicy::surface_weak_ptr_t VPLCPUAccelerationPolicy::get_free_ if (pool_it == pool_table.end()) { GAPI_LOG_WARNING(nullptr, "key is not found, table size: " << pool_table.size()); - GAPI_Assert(false && "Invalid surface key requested in VPLCPUAccelerationPolicy"); + GAPI_Error("Invalid surface key requested in VPLCPUAccelerationPolicy"); } pool_t& requested_pool = pool_it->second; diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp index 456e01d676..9fc1f4dc72 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp @@ -152,7 +152,7 @@ VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_fre } size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t) const { - GAPI_Assert(false && "get_free_surface_count() is not implemented"); + GAPI_Error("get_free_surface_count() is not implemented"); } size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t key) const { @@ -448,39 +448,39 @@ namespace wip { namespace onevpl { VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy(device_selector_ptr_t selector) : VPLAccelerationPolicy(selector) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } VPLDX11AccelerationPolicy::~VPLDX11AccelerationPolicy() = default; void VPLDX11AccelerationPolicy::init(session_t ) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } void VPLDX11AccelerationPolicy::deinit(session_t) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } VPLDX11AccelerationPolicy::pool_key_t VPLDX11AccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest&, mfxFrameInfo&) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t) const { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t) const { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } cv::MediaFrame::AdapterPtr VPLDX11AccelerationPolicy::create_frame_adapter(pool_key_t, const FrameConstructorArgs &) { - GAPI_Assert(false && "VPLDX11AccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLDX11AccelerationPolicy unavailable in current configuration"); } } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp index 0ded066137..93657b49be 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp @@ -39,13 +39,13 @@ VPLVAAPIAccelerationPolicy::VPLVAAPIAccelerationPolicy(device_selector_ptr_t sel va_handle = reinterpret_cast(devices.begin()->second.get_ptr()); #else // defined(HAVE_VA) || defined(HAVE_VA_INTEL) - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) } #else // __linux__ VPLVAAPIAccelerationPolicy::VPLVAAPIAccelerationPolicy(device_selector_ptr_t selector) : VPLAccelerationPolicy(selector) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } #endif // __linux__ @@ -102,33 +102,33 @@ cv::MediaFrame::AdapterPtr VPLVAAPIAccelerationPolicy::create_frame_adapter(pool VPLVAAPIAccelerationPolicy::~VPLVAAPIAccelerationPolicy() = default; void VPLVAAPIAccelerationPolicy::init(session_t ) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } void VPLVAAPIAccelerationPolicy::deinit(session_t) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } VPLVAAPIAccelerationPolicy::pool_key_t VPLVAAPIAccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest&, mfxFrameInfo&) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } VPLVAAPIAccelerationPolicy::surface_weak_ptr_t VPLVAAPIAccelerationPolicy::get_free_surface(pool_key_t) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } size_t VPLVAAPIAccelerationPolicy::get_free_surface_count(pool_key_t) const { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } size_t VPLVAAPIAccelerationPolicy::get_surface_count(pool_key_t) const { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } cv::MediaFrame::AdapterPtr VPLVAAPIAccelerationPolicy::create_frame_adapter(pool_key_t, const FrameConstructorArgs &) { - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current configuration"); } #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) } // namespace onevpl diff --git a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp index 77cfbb18b1..940c704c62 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp @@ -46,7 +46,7 @@ size_t LockAdapter::read_lock(mfxMemId mid, mfxFrameData &data) { // adapter will throw error if VPL frame allocator fails if (sts != MFX_ERR_NONE) { impl->unlock_shared(); - GAPI_Assert(false && "Cannot lock frame on READ using VPL allocator"); + GAPI_Error("Cannot lock frame on READ using VPL allocator"); } return prev_lock_count; @@ -76,7 +76,7 @@ void LockAdapter::write_lock(mfxMemId mid, mfxFrameData &data) { // adapter will throw error if VPL frame allocator fails if (sts != MFX_ERR_NONE) { impl->unlock(); - GAPI_Assert(false && "Cannot lock frame on WRITE using VPL allocator"); + GAPI_Error("Cannot lock frame on WRITE using VPL allocator"); } } @@ -199,13 +199,13 @@ void DX11AllocationItem::on_first_in_impl(mfxFrameData *ptr) { err = shared_device_context->Map(get_staging_texture_ptr(), 0, mapType, mapFlags, &lockedRect); if (S_OK != err && DXGI_ERROR_WAS_STILL_DRAWING != err) { GAPI_LOG_WARNING(nullptr, "Cannot Map staging texture in device context, error: " << std::to_string(HRESULT_CODE(err))); - GAPI_Assert(false && "Cannot Map staging texture in device context"); + GAPI_Error("Cannot Map staging texture in device context"); } } while (DXGI_ERROR_WAS_STILL_DRAWING == err); if (FAILED(err)) { GAPI_LOG_WARNING(nullptr, "Cannot lock frame"); - GAPI_Assert(false && "Cannot lock frame"); + GAPI_Error("Cannot lock frame"); return ; } diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp index 3744ddc8ce..01a9a14d43 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp @@ -125,7 +125,7 @@ mfxHDLPair VPLMediaFrameDX11Adapter::getHandle() const { } cv::util::any VPLMediaFrameDX11Adapter::blobParams() const { - /*GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not fully integrated" + /*GAPI_Error("VPLMediaFrameDX11Adapter::blobParams() is not fully integrated" "in OpenVINO InferenceEngine and would be temporary disable.");*/ #ifdef HAVE_INF_ENGINE mfxHDLPair handle = getHandle(); @@ -162,16 +162,16 @@ cv::util::any VPLMediaFrameDX11Adapter::blobParams() const { return std::make_pair(std::make_pair(y_tdesc, y_params), std::make_pair(uv_tdesc, uv_params)); #else - GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not implemented"); + GAPI_Error("VPLMediaFrameDX11Adapter::blobParams() is not implemented"); #endif // HAVE_INF_ENGINE } void VPLMediaFrameDX11Adapter::serialize(cv::gapi::s11n::IOStream&) { - GAPI_Assert(false && "VPLMediaFrameDX11Adapter::serialize() is not implemented"); + GAPI_Error("VPLMediaFrameDX11Adapter::serialize() is not implemented"); } void VPLMediaFrameDX11Adapter::deserialize(cv::gapi::s11n::IIStream&) { - GAPI_Assert(false && "VPLMediaFrameDX11Adapter::deserialize() is not implemented"); + GAPI_Error("VPLMediaFrameDX11Adapter::deserialize() is not implemented"); } DXGI_FORMAT VPLMediaFrameDX11Adapter::get_dx11_color_format(uint32_t mfx_fourcc) { diff --git a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp index d43b9068f3..83ed99ad91 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp @@ -244,10 +244,10 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) : suggested_device = IDeviceSelector::create(va_handle, "GPU", AccelType::VAAPI); suggested_context = IDeviceSelector::create(nullptr, AccelType::VAAPI); #else // defined(HAVE_VA) || defined(HAVE_VA_INTEL) - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) #else // #ifdef __linux__ - GAPI_Assert(false && "MFX_IMPL_VIA_VAAPI is supported on linux only"); + GAPI_Error("MFX_IMPL_VIA_VAAPI is supported on linux only"); #endif // #ifdef __linux__ break; } @@ -335,10 +335,10 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr, suggested_device = IDeviceSelector::create(device_ptr, device_id, AccelType::VAAPI); suggested_context = IDeviceSelector::create(nullptr, AccelType::VAAPI); #else // defined(HAVE_VA) || defined(HAVE_VA_INTEL) - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) #else // #ifdef __linux__ - GAPI_Assert(false && "MFX_IMPL_VIA_VAAPI is supported on linux only"); + GAPI_Error("MFX_IMPL_VIA_VAAPI is supported on linux only"); #endif // #ifdef __linux__ break; } @@ -394,10 +394,10 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const Device &device, case AccelType::VAAPI: #ifdef __linux__ #if !defined(HAVE_VA) || !defined(HAVE_VA_INTEL) - GAPI_Assert(false && "VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); + GAPI_Error("VPLVAAPIAccelerationPolicy unavailable in current linux configuration"); #endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL) #else // #ifdef __linux__ - GAPI_Assert(false && "MFX_IMPL_VIA_VAAPI is supported on linux only"); + GAPI_Error("MFX_IMPL_VIA_VAAPI is supported on linux only"); #endif // #ifdef __linux__ break; case AccelType::HOST: diff --git a/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp b/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp index 9f5a68a431..a40564aceb 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp @@ -60,7 +60,7 @@ private: return ret; } mfxVariant create_impl(const std::string&, const std::string&) { - GAPI_Assert(false && "Something wrong: you should not create mfxVariant " + GAPI_Error("Something wrong: you should not create mfxVariant " "from string directly - native type is lost in this case"); } }; @@ -173,7 +173,7 @@ void extract_optional_param_by_name(const std::string &name, [&out_param](int64_t value) { out_param = cv::util::make_optional(static_cast(value)); }, [&out_param](float_t value) { out_param = cv::util::make_optional(static_cast(value)); }, [&out_param](double_t value) { out_param = cv::util::make_optional(static_cast(value)); }, - [&out_param](void*) { GAPI_Assert(false && "`void*` is unsupported type"); }, + [&out_param](void*) { GAPI_Error("`void*` is unsupported type"); }, [&out_param](const std::string& value) { out_param = cv::util::make_optional(strtoull_or_throw(value.c_str())); }), @@ -189,7 +189,7 @@ unsigned long strtoul_or_throw(const char* str) { ((ret == ULONG_MAX) && errno == ERANGE)) { // nothing parsed from the string, handle errors or exit GAPI_LOG_WARNING(nullptr, "strtoul failed for: " << str); - GAPI_Assert(false && "strtoul_or_throw"); + GAPI_Error("strtoul_or_throw"); } return ret; } @@ -202,7 +202,7 @@ size_t strtoull_or_throw(const char* str) { ((ret == ULLONG_MAX) && errno == ERANGE)) { // nothing parsed from the string, handle errors or exit GAPI_LOG_WARNING(nullptr, "strtoull failed for: " << str); - GAPI_Assert(false && "strtoull_or_throw"); + GAPI_Error("strtoull_or_throw"); } return ret; } @@ -215,7 +215,7 @@ int64_t strtoll_or_throw(const char* str) { ((ret == LONG_MAX || ret == LONG_MIN) && errno == ERANGE)) { // nothing parsed from the string, handle errors or exit GAPI_LOG_WARNING(nullptr, "strtoll failed for: " << str); - GAPI_Assert(false && "strtoll_or_throw"); + GAPI_Error("strtoll_or_throw"); } return ret; } diff --git a/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp b/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp index 5a4c66fef2..2d2bbd05a0 100644 --- a/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp +++ b/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp @@ -18,7 +18,7 @@ struct IDataProvider::mfx_bitstream : public mfxBitstream {}; #else // HAVE_ONEVPL struct IDataProvider::mfx_bitstream { mfx_bitstream() { - GAPI_Assert(false && "Reject to create `mfxBitstream` because library compiled without VPL/MFX support"); + GAPI_Error("Reject to create `mfxBitstream` because library compiled without VPL/MFX support"); } }; #endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp index 108f8258ab..becd893e09 100644 --- a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp +++ b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp @@ -745,7 +745,7 @@ bool MFPAsyncDemuxDataProvider::fetch_bitstream_data(std::shared_ptr(out_bitsream->Data)); - GAPI_Assert(false && "invalid bitstream key"); + GAPI_Error("invalid bitstream key"); } if (it->second) { it->second->Unlock(); @@ -788,20 +788,20 @@ bool MFPAsyncDemuxDataProvider::empty() const { #else // _WIN32 MFPAsyncDemuxDataProvider::MFPAsyncDemuxDataProvider(const std::string&) { - GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); + GAPI_Error("Unsupported: Microsoft Media Foundation is not available"); } IDataProvider::mfx_codec_id_type MFPAsyncDemuxDataProvider::get_mfx_codec_id() const { - GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); + GAPI_Error("Unsupported: Microsoft Media Foundation is not available"); return std::numeric_limits::max(); } bool MFPAsyncDemuxDataProvider::fetch_bitstream_data(std::shared_ptr &) { - GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); + GAPI_Error("Unsupported: Microsoft Media Foundation is not available"); return false; } bool MFPAsyncDemuxDataProvider::empty() const { - GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); + GAPI_Error("Unsupported: Microsoft Media Foundation is not available"); return true; } #endif // _WIN32 diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp index 8a35cca063..7b0ad8c83d 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp @@ -190,7 +190,7 @@ VPLLegacyDecodeEngine::SessionParam VPLLegacyDecodeEngine::prepare_session_param // TODO make proper direction mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; } else { - GAPI_Assert(false && "unsupported AccelType from device selector"); + GAPI_Error("unsupported AccelType from device selector"); } // try fetch & decode input data diff --git a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp index 5a08f2bd09..c3f63efc55 100644 --- a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp @@ -66,7 +66,7 @@ pp_session VPPPreprocDispatcher::initialize_preproc(const pp_params& initial_fra return sess; } } - GAPI_Assert(false && "Cannot initialize VPP preproc in dispatcher, no suitable worker"); + GAPI_Error("Cannot initialize VPP preproc in dispatcher, no suitable worker"); } cv::MediaFrame VPPPreprocDispatcher::run_sync(const pp_session &session_handle, @@ -80,7 +80,7 @@ cv::MediaFrame VPPPreprocDispatcher::run_sync(const pp_session &session_handle, return w->run_sync(session_handle, in_frame, opt_roi); } } - GAPI_Assert(false && "Cannot invoke VPP preproc in dispatcher, no suitable worker"); + GAPI_Error("Cannot invoke VPP preproc in dispatcher, no suitable worker"); } #else // HAVE_ONEVPL @@ -90,13 +90,13 @@ cv::util::optional VPPPreprocDispatcher::is_applicable(const cv::Medi pp_session VPPPreprocDispatcher::initialize_preproc(const pp_params&, const GFrameDesc&) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } cv::MediaFrame VPPPreprocDispatcher::run_sync(const pp_session &, const cv::MediaFrame&, const cv::util::optional &) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } #endif // HAVE_ONEVPL } // namespace onevpl diff --git a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp index d0c4a85f38..cc81df0626 100644 --- a/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp @@ -249,13 +249,13 @@ pp_session VPPPreprocEngine::initialize_preproc(const pp_params& initial_frame_p sts = MFXCreateSession(mfx_handle, impl_number, &mfx_vpp_session); if (sts != MFX_ERR_NONE) { GAPI_LOG_WARNING(nullptr, "Cannot clone VPP session, error: " << mfxstatus_to_string(sts)); - GAPI_Assert(false && "Cannot continue VPP preprocessing"); + GAPI_Error("Cannot continue VPP preprocessing"); } sts = MFXJoinSession(params.handle, mfx_vpp_session); if (sts != MFX_ERR_NONE) { GAPI_LOG_WARNING(nullptr, "Cannot join VPP sessions, error: " << mfxstatus_to_string(sts)); - GAPI_Assert(false && "Cannot continue VPP preprocessing"); + GAPI_Error("Cannot continue VPP preprocessing"); } GAPI_LOG_INFO(nullptr, "[" << mfx_vpp_session << "] starting pool allocation"); @@ -301,7 +301,7 @@ pp_session VPPPreprocEngine::initialize_preproc(const pp_params& initial_frame_p } } catch (const std::exception&) { MFXClose(mfx_vpp_session); - GAPI_Assert(false && "Cannot init preproc resources"); + GAPI_Error("Cannot init preproc resources"); } // create engine session after all diff --git a/modules/gapi/src/streaming/onevpl/engine/preproc/utils.cpp b/modules/gapi/src/streaming/onevpl/engine/preproc/utils.cpp index 6cf7212f3e..f6c210ad9d 100644 --- a/modules/gapi/src/streaming/onevpl/engine/preproc/utils.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/preproc/utils.cpp @@ -28,7 +28,7 @@ cv::MediaFormat fourcc_to_MediaFormat(int value) { default: GAPI_LOG_WARNING(nullptr, "Unsupported FourCC format requested: " << value << ". Cannot cast to cv::MediaFrame"); - GAPI_Assert(false && "Unsupported FOURCC"); + GAPI_Error("Unsupported FOURCC"); } } @@ -44,7 +44,7 @@ int MediaFormat_to_fourcc(cv::MediaFormat value) { GAPI_LOG_WARNING(nullptr, "Unsupported cv::MediaFormat format requested: " << static_cast::type>(value) << ". Cannot cast to FourCC"); - GAPI_Assert(false && "Unsupported cv::MediaFormat"); + GAPI_Error("Unsupported cv::MediaFormat"); } } int MediaFormat_to_chroma(cv::MediaFormat value) { @@ -58,7 +58,7 @@ int MediaFormat_to_chroma(cv::MediaFormat value) { GAPI_LOG_WARNING(nullptr, "Unsupported cv::MediaFormat format requested: " << static_cast::type>(value) << ". Cannot cast to ChromaFormateIdc"); - GAPI_Assert(false && "Unsupported cv::MediaFormat"); + GAPI_Error("Unsupported cv::MediaFormat"); } } diff --git a/modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.cpp b/modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.cpp index b5787320d6..62d03e1440 100644 --- a/modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.cpp @@ -30,7 +30,7 @@ namespace wip { template std::unique_ptr IPreprocEngine::create_preproc_engine_impl(const PreprocEngineArgs& ...) { - GAPI_Assert(false && "Unsupported "); + GAPI_Error("Unsupported "); } template <> @@ -91,7 +91,7 @@ IPreprocEngine::create_preproc_engine_impl(const onevpl::Device &device, } if (!pp_is_created) { GAPI_LOG_WARNING(nullptr, "Cannot create VPP preprocessing engine: configuration unsupported"); - GAPI_Assert(false && "VPP preproc unsupported"); + GAPI_Error("VPP preproc unsupported"); } #endif // HAVE_ONEVPL return dispatcher; diff --git a/modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp b/modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp index 23703bf172..41feb5c7fe 100644 --- a/modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp @@ -404,7 +404,7 @@ void VPLLegacyTranscodeEngine::validate_vpp_param(const mfxVideoParam& mfxVPPPar " must be less or equal to \"" << CfgParam::vpp_in_width_name() << "\": " << mfxVPPParams.vpp.In.Width); - GAPI_Assert(false && "Invalid VPP params combination: Width & Crop"); + GAPI_Error("Invalid VPP params combination: Width & Crop"); } if (mfxVPPParams.vpp.In.Height < mfxVPPParams.vpp.In.CropH + mfxVPPParams.vpp.In.CropY) { @@ -416,7 +416,7 @@ void VPLLegacyTranscodeEngine::validate_vpp_param(const mfxVideoParam& mfxVPPPar " must be less or equal to \"" << CfgParam::vpp_in_height_name() << "\": " << mfxVPPParams.vpp.In.Height); - GAPI_Assert(false && "Invalid VPP params combination: Height & Crop"); + GAPI_Error("Invalid VPP params combination: Height & Crop"); } if (mfxVPPParams.vpp.Out.Width < mfxVPPParams.vpp.Out.CropW + mfxVPPParams.vpp.Out.CropX) { @@ -428,7 +428,7 @@ void VPLLegacyTranscodeEngine::validate_vpp_param(const mfxVideoParam& mfxVPPPar " must be less or equal to \"" << CfgParam::vpp_out_width_name() << "\": " << mfxVPPParams.vpp.Out.Width); - GAPI_Assert(false && "Invalid VPP params combination: Width & Crop"); + GAPI_Error("Invalid VPP params combination: Width & Crop"); } if (mfxVPPParams.vpp.Out.Height < mfxVPPParams.vpp.Out.CropH + mfxVPPParams.vpp.Out.CropY) { @@ -440,7 +440,7 @@ void VPLLegacyTranscodeEngine::validate_vpp_param(const mfxVideoParam& mfxVPPPar " must be less or equal to \"" << CfgParam::vpp_out_height_name() << "\": " << mfxVPPParams.vpp.Out.Height); - GAPI_Assert(false && "Invalid VPP params combination: Height & Crop"); + GAPI_Error("Invalid VPP params combination: Height & Crop"); } GAPI_LOG_INFO(nullptr, "Finished VPP param validation"); diff --git a/modules/gapi/src/streaming/onevpl/file_data_provider.cpp b/modules/gapi/src/streaming/onevpl/file_data_provider.cpp index a86d541904..189cb3b070 100644 --- a/modules/gapi/src/streaming/onevpl/file_data_provider.cpp +++ b/modules/gapi/src/streaming/onevpl/file_data_provider.cpp @@ -127,26 +127,23 @@ FileDataProvider::FileDataProvider(const std::string&, source_handle(nullptr, &fclose), codec(std::numeric_limits::max()), bitstream_data_size(bitstream_data_size_value) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } FileDataProvider::~FileDataProvider() = default; IDataProvider::mfx_codec_id_type FileDataProvider::get_mfx_codec_id() const { cv::util::suppress_unused_warning(codec); - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); - return codec; + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } bool FileDataProvider::fetch_bitstream_data(std::shared_ptr &) { cv::util::suppress_unused_warning(bitstream_data_size); - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); - return false; + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } bool FileDataProvider::empty() const { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); - return true; + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } #endif // HAVE_ONEVPL } // namespace onevpl diff --git a/modules/gapi/src/streaming/onevpl/source.cpp b/modules/gapi/src/streaming/onevpl/source.cpp index efcc9bf850..b25edb6e5d 100644 --- a/modules/gapi/src/streaming/onevpl/source.cpp +++ b/modules/gapi/src/streaming/onevpl/source.cpp @@ -73,33 +73,33 @@ GSource::GSource(std::shared_ptr source, #else GSource::GSource(const std::string&, const CfgParams&) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(const std::string&, const CfgParams&, const std::string&, void*, void*) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(const std::string&, const CfgParams&, const Device &, const Context &) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(const std::string&, const CfgParams&, std::shared_ptr) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(std::shared_ptr, const CfgParams&) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(std::shared_ptr, const CfgParams&, const std::string&, void*, void*) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } GSource::GSource(std::shared_ptr, const CfgParams&, std::shared_ptr) { - GAPI_Assert(false && "Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); + GAPI_Error("Unsupported: G-API compiled without `WITH_GAPI_ONEVPL=ON`"); } #endif diff --git a/modules/gapi/src/streaming/onevpl/source_priv.cpp b/modules/gapi/src/streaming/onevpl/source_priv.cpp index bd74ca0a25..ce7bca8435 100644 --- a/modules/gapi/src/streaming/onevpl/source_priv.cpp +++ b/modules/gapi/src/streaming/onevpl/source_priv.cpp @@ -109,7 +109,7 @@ GSource::Priv::Priv(std::shared_ptr provider, GAPI_LOG_WARNING(nullptr, "MFXSetConfigFilterProperty failed, error: " << mfxstatus_to_string(sts) << " - for \"" << cfg_param_it->get_name() << "\""); - GAPI_Assert(false && "MFXSetConfigFilterProperty failed"); + GAPI_Error("MFXSetConfigFilterProperty failed"); } mfx_param.Type = MFX_VARIANT_TYPE_U32; @@ -123,7 +123,7 @@ GSource::Priv::Priv(std::shared_ptr provider, GAPI_LOG_WARNING(nullptr, "MFXSetConfigFilterProperty failed, error: " << mfxstatus_to_string(sts) << " - for \"mfxImplDescription.mfxVPPDescription.filter.FilterFourCC\""); - GAPI_Assert(false && "MFXSetConfigFilterProperty failed"); + GAPI_Error("MFXSetConfigFilterProperty failed"); } ++cfg_param_it; @@ -317,7 +317,7 @@ std::unique_ptr GSource::Priv::initializeHWAccel(std::sha GAPI_LOG_WARNING(nullptr, "Cannot initialize HW Accel: " "invalid accelerator type: " << std::to_string(accel_mode.Data.U32)); - GAPI_Assert(false && "Cannot initialize HW Accel"); + GAPI_Error("Cannot initialize HW Accel"); } } diff --git a/modules/gapi/test/common/gapi_core_tests.hpp b/modules/gapi/test/common/gapi_core_tests.hpp index b5a4628212..6d4a4bd41b 100644 --- a/modules/gapi/test/common/gapi_core_tests.hpp +++ b/modules/gapi/test/common/gapi_core_tests.hpp @@ -41,7 +41,7 @@ inline std::ostream& operator<<(std::ostream& os, mathOp op) CASE(SUB); CASE(MUL); CASE(DIV); - default: GAPI_Assert(false && "unknown mathOp value"); + default: GAPI_Error("unknown mathOp value"); } #undef CASE return os; @@ -57,7 +57,7 @@ inline std::ostream& operator<<(std::ostream& os, bitwiseOp op) CASE(OR); CASE(XOR); CASE(NOT); - default: GAPI_Assert(false && "unknown bitwiseOp value"); + default: GAPI_Error("unknown bitwiseOp value"); } #undef CASE return os; diff --git a/modules/gapi/test/common/gapi_operators_tests.hpp b/modules/gapi/test/common/gapi_operators_tests.hpp index 9a195e0688..1f2fdc7df6 100644 --- a/modules/gapi/test/common/gapi_operators_tests.hpp +++ b/modules/gapi/test/common/gapi_operators_tests.hpp @@ -34,7 +34,7 @@ inline std::ostream& operator<<(std::ostream& os, operation op) CASE(GTR); CASE(LTR); CASE(GER); CASE(LER); CASE(EQR); CASE(NER); CASE(AND); CASE(OR); CASE(XOR); CASE(ANDR); CASE(ORR); CASE(XORR); - default: GAPI_Assert(false && "unknown operation value"); + default: GAPI_Error("unknown operation value"); } #undef CASE return os; @@ -187,7 +187,7 @@ struct g_api_ocv_pair_mat_scalar { CASE(GTR); CASE(LTR); CASE(GER); CASE(LER); CASE(EQR); CASE(NER); CASE(AND); CASE(OR); CASE(XOR); CASE(ANDR); CASE(ORR); CASE(XORR); - default: GAPI_Assert(false && "unknown operation value"); + default: GAPI_Error("unknown operation value"); } } #undef CASE @@ -214,7 +214,7 @@ struct g_api_ocv_pair_mat_mat { CASE(ADD); CASE(SUB); CASE(DIV); CASE(GT); CASE(LT); CASE(GE); CASE(LE); CASE(EQ); CASE(NE); CASE(AND); CASE(OR); CASE(XOR); - default: GAPI_Assert(false && "unknown operation value"); + default: GAPI_Error("unknown operation value"); } } #undef CASE diff --git a/modules/gapi/test/common/gapi_stereo_tests_inl.hpp b/modules/gapi/test/common/gapi_stereo_tests_inl.hpp index 79505317bc..8e36646161 100644 --- a/modules/gapi/test/common/gapi_stereo_tests_inl.hpp +++ b/modules/gapi/test/common/gapi_stereo_tests_inl.hpp @@ -25,7 +25,7 @@ TEST_P(TestGAPIStereo, DisparityDepthTest) case format::DEPTH_FLOAT16: dtype = CV_16FC1; break; case format::DEPTH_FLOAT32: dtype = CV_32FC1; break; case format::DISPARITY_FIXED16_12_4: dtype = CV_16SC1; break; - default: GAPI_Assert(false && "Unsupported format in test"); + default: GAPI_Error("Unsupported format in test"); } initOutMats(sz, dtype); @@ -61,7 +61,7 @@ TEST_P(TestGAPIStereo, DisparityDepthTest) case format::DISPARITY_FIXED16_12_4: break; default: - GAPI_Assert(false && "Unsupported format in test"); + GAPI_Error("Unsupported format in test"); } EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); diff --git a/modules/gapi/test/common/gapi_tests_common.hpp b/modules/gapi/test/common/gapi_tests_common.hpp index 4c9c2d9700..f84ee05f49 100644 --- a/modules/gapi/test/common/gapi_tests_common.hpp +++ b/modules/gapi/test/common/gapi_tests_common.hpp @@ -373,7 +373,7 @@ public: initMatByPointsVectorRandU>(sz_in); break; default: - GAPI_Assert(false && "Unsupported depth"); + GAPI_Error("Unsupported depth"); break; } } @@ -1065,7 +1065,7 @@ inline std::ostream& operator<<(std::ostream& os, CmpTypes op) CASE(CMP_LT); CASE(CMP_LE); CASE(CMP_NE); - default: GAPI_Assert(false && "unknown CmpTypes value"); + default: GAPI_Error("unknown CmpTypes value"); } #undef CASE return os; @@ -1084,7 +1084,7 @@ inline std::ostream& operator<<(std::ostream& os, NormTypes op) CASE(NORM_HAMMING2); CASE(NORM_RELATIVE); CASE(NORM_MINMAX); - default: GAPI_Assert(false && "unknown NormTypes value"); + default: GAPI_Error("unknown NormTypes value"); } #undef CASE return os; @@ -1100,7 +1100,7 @@ inline std::ostream& operator<<(std::ostream& os, RetrievalModes op) CASE(RETR_CCOMP); CASE(RETR_TREE); CASE(RETR_FLOODFILL); - default: GAPI_Assert(false && "unknown RetrievalModes value"); + default: GAPI_Error("unknown RetrievalModes value"); } #undef CASE return os; @@ -1115,7 +1115,7 @@ inline std::ostream& operator<<(std::ostream& os, ContourApproximationModes op) CASE(CHAIN_APPROX_SIMPLE); CASE(CHAIN_APPROX_TC89_L1); CASE(CHAIN_APPROX_TC89_KCOS); - default: GAPI_Assert(false && "unknown ContourApproximationModes value"); + default: GAPI_Error("unknown ContourApproximationModes value"); } #undef CASE return os; @@ -1134,7 +1134,7 @@ inline std::ostream& operator<<(std::ostream& os, MorphTypes op) CASE(MORPH_TOPHAT); CASE(MORPH_BLACKHAT); CASE(MORPH_HITMISS); - default: GAPI_Assert(false && "unknown MorphTypes value"); + default: GAPI_Error("unknown MorphTypes value"); } #undef CASE return os; @@ -1153,7 +1153,7 @@ inline std::ostream& operator<<(std::ostream& os, DistanceTypes op) CASE(DIST_FAIR); CASE(DIST_WELSCH); CASE(DIST_HUBER); - default: GAPI_Assert(false && "unknown DistanceTypes value"); + default: GAPI_Error("unknown DistanceTypes value"); } #undef CASE return os; @@ -1176,7 +1176,7 @@ inline std::ostream& operator<<(std::ostream& os, KmeansFlags op) case KmeansFlags::KMEANS_PP_CENTERS | KmeansFlags::KMEANS_USE_INITIAL_LABELS: os << "KMEANS_PP_CENTERS | KMEANS_USE_INITIAL_LABELS"; break; - default: GAPI_Assert(false && "unknown KmeansFlags value"); + default: GAPI_Error("unknown KmeansFlags value"); } return os; } diff --git a/modules/gapi/test/common/gapi_video_tests_common.hpp b/modules/gapi/test/common/gapi_video_tests_common.hpp index f3e7e5aa6c..0f755439c2 100644 --- a/modules/gapi/test/common/gapi_video_tests_common.hpp +++ b/modules/gapi/test/common/gapi_video_tests_common.hpp @@ -435,7 +435,7 @@ inline cv::GComputation runOCVnGAPIBuildOptFlowPyramid(TestFunctional&, BuildOpticalFlowPyramidTestOutput&, BuildOpticalFlowPyramidTestOutput&) { - GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); + GAPI_Error("This function shouldn't be called without opencv_video"); } inline cv::GComputation runOCVnGAPIOptFlowLK(TestFunctional&, @@ -444,7 +444,7 @@ inline cv::GComputation runOCVnGAPIOptFlowLK(TestFunctional&, OptFlowLKTestOutput&, OptFlowLKTestOutput&) { - GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); + GAPI_Error("This function shouldn't be called without opencv_video"); } inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional&, @@ -454,7 +454,7 @@ inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional&, OptFlowLKTestOutput&, OptFlowLKTestOutput&) { - GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); + GAPI_Error("This function shouldn't be called without opencv_video"); } inline GComputation runOCVnGAPIOptFlowPipeline(TestFunctional&, @@ -463,7 +463,7 @@ inline GComputation runOCVnGAPIOptFlowPipeline(TestFunctional&, OptFlowLKTestOutput&, std::vector&) { - GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); + GAPI_Error("This function shouldn't be called without opencv_video"); } #endif // HAVE_OPENCV_VIDEO @@ -481,7 +481,7 @@ inline std::ostream& operator<<(std::ostream& os, const BackgroundSubtractorType { CASE(TYPE_BS_MOG2); CASE(TYPE_BS_KNN); - default: GAPI_Assert(false && "unknown BackgroundSubtractor type"); + default: GAPI_Error("unknown BackgroundSubtractor type"); } #undef CASE return os; diff --git a/modules/gapi/test/infer/gapi_infer_onnx_test.cpp b/modules/gapi/test/infer/gapi_infer_onnx_test.cpp index 5b1e6c9d4f..110ae7d964 100644 --- a/modules/gapi/test/infer/gapi_infer_onnx_test.cpp +++ b/modules/gapi/test/infer/gapi_infer_onnx_test.cpp @@ -114,7 +114,7 @@ inline int toCV(ONNXTensorElementDataType prec) { case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return CV_32F; case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return CV_32S; case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return CV_32S; - default: GAPI_Assert(false && "Unsupported data type"); + default: GAPI_Error("Unsupported data type"); } return -1; } @@ -142,7 +142,7 @@ void copyFromONNX(Ort::Value &v, cv::Mat& mat) { [](int64_t el) { return static_cast(el); }); break; } - default: GAPI_Assert(false && "ONNX. Unsupported data type"); + default: GAPI_Error("ONNX. Unsupported data type"); } } diff --git a/modules/gapi/test/s11n/gapi_s11n_tests.cpp b/modules/gapi/test/s11n/gapi_s11n_tests.cpp index 22398ec92c..0dd88cd5b8 100644 --- a/modules/gapi/test/s11n/gapi_s11n_tests.cpp +++ b/modules/gapi/test/s11n/gapi_s11n_tests.cpp @@ -554,7 +554,7 @@ TEST_F(S11N_Basic, Test_RunArgs_MatScalar) { EXPECT_EQ(scalar, out_scalar); } break; default: - GAPI_Assert(false && "This value type is not supported!"); // ...maybe because of STANDALONE mode. + GAPI_Error("This value type is not supported!"); // ...maybe because of STANDALONE mode. break; } i++; @@ -587,7 +587,7 @@ TEST_F(S11N_Basic, Test_Bind_RunArgs_MatScalar) { EXPECT_EQ(out_scalar->val[2], scalar.val[2]); } break; default: - GAPI_Assert(false && "This value type is not supported!"); // ...maybe because of STANDALONE mode. + GAPI_Error("This value type is not supported!"); // ...maybe because of STANDALONE mode. break; } } diff --git a/modules/gapi/test/streaming/gapi_streaming_tests.cpp b/modules/gapi/test/streaming/gapi_streaming_tests.cpp index 111f43bd19..c27ebe3ca2 100644 --- a/modules/gapi/test/streaming/gapi_streaming_tests.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_tests.cpp @@ -49,7 +49,7 @@ std::ostream& operator<< (std::ostream &os, const KernelPackage &e) _C(OCL); _C(OCL_FLUID); #undef _C - default: GAPI_Assert(false); + default: GAPI_Error("InternalError"); } return os; } @@ -298,7 +298,7 @@ void checkPullOverload(const cv::Mat& ref, out_mat = *opt_mat; break; } - default: GAPI_Assert(false && "Incorrect type of Args"); + default: GAPI_Error("Incorrect type of Args"); } EXPECT_EQ(0., cv::norm(ref, out_mat, cv::NORM_INF)); @@ -2420,7 +2420,7 @@ TEST(GAPI_Streaming, TestPythonAPI) switch (args.index()) { case RunArgs::index_of(): out_args = util::get(args); break; - default: GAPI_Assert(false && "Incorrect type of return value"); + default: GAPI_Error("Incorrect type of return value"); } ASSERT_EQ(1u, out_args.size()); From 7463e9b8bb5004b046a98a219852d53456b7ee02 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 19 Dec 2022 14:15:34 +0100 Subject: [PATCH 270/313] Even faster CV_PAUSE on SkyLake and above. No need to loop as RDTSC is 3/4 times faster than _mm_pause. --- modules/core/src/parallel_impl.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index 087b41233b..18667905e2 100644 --- a/modules/core/src/parallel_impl.cpp +++ b/modules/core/src/parallel_impl.cpp @@ -57,9 +57,8 @@ DECLARE_CV_PAUSE static inline void cv_non_sse_mm_pause() { __asm__ __volatile__ ("rep; nop"); } # define _mm_pause cv_non_sse_mm_pause # endif -// 5 * v is meants for backward compatibility: with pre-Skylake CPUs, _mm_pause took 4 or 5 cycles. -// With post-Skylake CPUs, _mm_pause takes 140 cycles. -# define CV_PAUSE(v) do { const uint64_t __delay = 5 * v; uint64_t __init = __rdtsc(); do { _mm_pause(); } while ((__rdtsc() - __init) < __delay); } while (0) +// With Skylake CPUs and above, _mm_pause takes 140 cycles so no need for a loop. +# define CV_PAUSE(v) do { (void)v; _mm_pause(); } while (0) # elif defined __GNUC__ && defined __aarch64__ # define CV_PAUSE(v) do { for (int __delay = (v); __delay > 0; --__delay) { asm volatile("yield" ::: "memory"); } } while (0) # elif defined __GNUC__ && defined __arm__ From 9aa5ab75570cf53a63c4362adb580af829557ede Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:55:31 +0200 Subject: [PATCH 271/313] cv::cuda: Replace all instances of texture references/objects with texture objects using the existing updated cv::cudev::Texture class. Fixes bugs in cv::cuda::demosaicing, cv::cuda::resize and cv::cuda::HoughSegmentDetector. --- modules/core/include/opencv2/core/cuda/common.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/core/include/opencv2/core/cuda/common.hpp b/modules/core/include/opencv2/core/cuda/common.hpp index b36b3d17b5..348bdf1823 100644 --- a/modules/core/include/opencv2/core/cuda/common.hpp +++ b/modules/core/include/opencv2/core/cuda/common.hpp @@ -98,6 +98,12 @@ namespace cv { namespace cuda return (total + grain - 1) / grain; } +#if (CUDART_VERSION >= 12000) + template inline void bindTexture(const textureReference* tex, const PtrStepSz& img) { CV_Error(cv::Error::GpuNotSupported, "Function removed in CUDA SDK 12"); } + template inline void createTextureObjectPitch2D(cudaTextureObject_t* tex, PtrStepSz& img, const cudaTextureDesc& texDesc) { + CV_Error(cv::Error::GpuNotSupported, "Function removed in CUDA SDK 12"); } +#else + //TODO: remove from OpenCV 5.x template inline void bindTexture(const textureReference* tex, const PtrStepSz& img) { cudaChannelFormatDesc desc = cudaCreateChannelDesc(); @@ -118,6 +124,7 @@ namespace cv { namespace cuda cudaSafeCall( cudaCreateTextureObject(tex, &resDesc, &texDesc, NULL) ); } } +#endif }} //! @endcond From 1102b7eff88728577f1bbec1126108f0795e3534 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 20 Dec 2022 06:09:34 +0000 Subject: [PATCH 272/313] dnn: fix gather layer implementation - support FP16 data --- modules/dnn/src/layers/gather_layer.cpp | 50 +++++++++++++++++++++---- modules/dnn/test/test_onnx_importer.cpp | 6 ++- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/modules/dnn/src/layers/gather_layer.cpp b/modules/dnn/src/layers/gather_layer.cpp index 8d93a85dc4..924b5fcbc1 100644 --- a/modules/dnn/src/layers/gather_layer.cpp +++ b/modules/dnn/src/layers/gather_layer.cpp @@ -45,34 +45,70 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + // FP16 fallback is not needed as we handle FP16 below + std::vector inputs, outputs; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); + CV_CheckEQ(inputs.size(), (size_t)2, ""); + CV_CheckEQ(outputs.size(), (size_t)1, ""); + const Mat& inp = inputs[0]; - const Mat& indices = inputs[1]; + + int indicesType = inputs[1].type(); + CV_CheckType(indicesType, indicesType == CV_32FC1 || indicesType == CV_16SC1, ""); + Mat indices32S; + if (indicesType == CV_16S/*FP16*/) + { + Mat indicesF32; + convertFp16(inputs[1], indicesF32); + indicesF32.convertTo(indices32S, CV_32S); + } + else + { + inputs[1].convertTo(indices32S, CV_32S); + } + const size_t indices_total = indices32S.total(); + indices32S = indices32S.reshape(1, indices_total); + Mat& out = outputs[0]; + CV_CheckTypeEQ(inp.type(), out.type(), ""); + CV_CheckTypeEQ(indices32S.type(), CV_32SC1, ""); + const int axis = normalize_axis(m_axis, shape(inp)); + // FIXIT: why should we work with non-normalized input? it should be handled in importer or layers's output generator + const int axis_size = (int)inp.size[axis]; + for (size_t j = 0 ; j < indices_total; ++j) + { + int& idx = indices32S.at(j); + idx = normalize_axis(idx, axis_size); // validate and normalize indices + } + const size_t outer_size = axis == 0 ? inp.total() : inp.step1(axis - 1); const size_t outer_dims = inp.total() / outer_size; const size_t inner_size = inp.step1(axis); - const float* idx = indices.ptr(); // TODO: change type to integer in the future. + const int* idx = indices32S.ptr(); const char* src = inp.ptr(); char* dst = out.ptr(); + CV_CheckEQ(out.total(), outer_dims * indices_total * inner_size, ""); const size_t es = inp.elemSize1(); + // TODO: optimize through switch (inner_size * es) + const size_t inner_bytes = inner_size * es; for (size_t i = 0; i < outer_dims; ++i) { const size_t src_offset = i * outer_size; - for (size_t j = 0 ; j < indices.total(); ++j) + for (size_t j = 0 ; j < indices_total; ++j) { - const size_t index = (static_cast(idx[j]) + inp.size[axis]) % inp.size[axis]; - const size_t new_offset = src_offset + index * inp.step1(axis); - std::memcpy(dst, src + new_offset * es, inner_size * es); - dst += inner_size * es; + const int index = idx[j]; + CV_DbgCheck(index, index >= 0 && index < axis_size, ""); + const size_t new_offset = src_offset + index * inner_size; + std::memcpy(dst, src + new_offset * es, inner_bytes); + dst += inner_bytes; } } } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index e8350e418d..58d2086b4c 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -199,9 +199,11 @@ TEST_P(Test_ONNX_layers, Convolution_variable_weight_bias) TEST_P(Test_ONNX_layers, Gather) { - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); testONNXModels("gather", npy, 0, 0, false, false); +} + +TEST_P(Test_ONNX_layers, Gather_Scalar) +{ testONNXModels("gather_scalar", npy, 0, 0, false, false); } From 3f7ec99166cd927f00cea34b79b88b19ad34a1bc Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 20 Dec 2022 06:36:01 +0000 Subject: [PATCH 273/313] build: eliminate build warnings on Ubuntu 20.04/16.04 --- modules/gapi/CMakeLists.txt | 2 +- modules/gapi/src/executor/gtbbexecutor.hpp | 3 +++ modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp | 6 +++--- .../streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 3deb518d62..2553b3ce8d 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -382,7 +382,7 @@ if(TARGET example_gapi_onevpl_infer_with_advanced_device_selection) ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() if(UNIX AND HAVE_VA) - message ("GAPI VPL samples with VAAPI") + message(STATUS "GAPI VPL samples with VAAPI") ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${VA_INCLUDE_DIR}) ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE ${VA_LIBRARIES}) endif() diff --git a/modules/gapi/src/executor/gtbbexecutor.hpp b/modules/gapi/src/executor/gtbbexecutor.hpp index 3c2bf1ff98..342ba4817f 100644 --- a/modules/gapi/src/executor/gtbbexecutor.hpp +++ b/modules/gapi/src/executor/gtbbexecutor.hpp @@ -12,6 +12,9 @@ #endif #ifdef HAVE_TBB +#ifndef TBB_SUPPRESS_DEPRECATED_MESSAGES +#define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 +#endif #include #include #if TBB_INTERFACE_VERSION < 12000 diff --git a/modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp b/modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp index e1b93af98b..766ec6b0b2 100644 --- a/modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp +++ b/modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp @@ -50,7 +50,7 @@ TEST(TBBExecutor, Basic) { }); q.push(&n); execute(q); - EXPECT_EQ(true, executed); + EXPECT_TRUE(executed); } TEST(TBBExecutor, SerialExecution) { @@ -117,8 +117,8 @@ TEST(TBBExecutor, AsyncBasic) { async_thread.join(); - EXPECT_EQ(true, callback_called); - EXPECT_EQ(true, master_was_blocked_until_callback_called); + EXPECT_TRUE(callback_called); + EXPECT_TRUE(master_was_blocked_until_callback_called); } TEST(TBBExecutor, Dependencies) { diff --git a/modules/gapi/test/streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp b/modules/gapi/test/streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp index 7b809a8847..6a3fb38d5e 100644 --- a/modules/gapi/test/streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp +++ b/modules/gapi/test/streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp @@ -138,9 +138,9 @@ TEST(GStreamerPipelineFacadeTest, IsPlayingUnitTest) "video/x-raw,width=1920,height=1080,framerate=3/1 ! " "appsink name=sink2"); - EXPECT_EQ(false, pipelineFacade.isPlaying()); + EXPECT_FALSE(pipelineFacade.isPlaying()); pipelineFacade.play(); - EXPECT_EQ(true, pipelineFacade.isPlaying()); + EXPECT_TRUE(pipelineFacade.isPlaying()); } TEST(GStreamerPipelineFacadeTest, MTSafetyUnitTest) From cdbb893b27319dfdb4da0c5e7a4b438b3dc1bae6 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 20 Dec 2022 09:46:48 +0000 Subject: [PATCH 274/313] dnn: disable OpenCL code path in MatMul processing - this mode is not supported by 22828 --- .../dnn/src/layers/fully_connected_layer.cpp | 2 +- modules/dnn/test/test_onnx_importer.cpp | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 321994cbb7..505ea460d5 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -510,7 +510,7 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && !isMatMul, forward_ocl(inputs_arr, outputs_arr, internals_arr)) if (inputs_arr.depth() == CV_16S) diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index e8350e418d..4546dccbb8 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -904,23 +904,37 @@ TEST_P(Test_ONNX_layers, Multiplication) testONNXModels("mul"); } -TEST_P(Test_ONNX_layers, MatMul) +TEST_P(Test_ONNX_layers, MatMul_2d) { - if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) - applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); - testONNXModels("matmul_2d"); +} +TEST_P(Test_ONNX_layers, MatMul_3d) +{ testONNXModels("matmul_3d"); +} +TEST_P(Test_ONNX_layers, MatMul_4d) +{ testONNXModels("matmul_4d"); } -TEST_P(Test_ONNX_layers, MatMul_init) +TEST_P(Test_ONNX_layers, MatMul_2d_init) { testONNXModels("matmul_2d_init"); +} +TEST_P(Test_ONNX_layers, MatMul_3d_init) +{ testONNXModels("matmul_3d_init"); +} +TEST_P(Test_ONNX_layers, MatMul_4d_init) +{ testONNXModels("matmul_4d_init"); - +} +TEST_P(Test_ONNX_layers, MatMul_init_2) +{ testONNXModels("matmul_init_2"); +} +TEST_P(Test_ONNX_layers, MatMul_init_bcast) +{ testONNXModels("matmul_init_bcast"); } From f4b23de9dd85b94c176a4f05cc73efb7faaf2c17 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 21 Dec 2022 03:28:09 +0000 Subject: [PATCH 275/313] videoio(v4l): initialize variables --- modules/videoio/src/cap_v4l.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index bbc05efadc..a5d8561c6b 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -883,8 +883,8 @@ bool CvCaptureCAM_V4L::createBuffers() for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) { v4l2_buffer buf = v4l2_buffer(); v4l2_plane mplanes[VIDEO_MAX_PLANES]; - size_t length; - off_t offset; + size_t length = 0; + off_t offset = 0; buf.type = type; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; From a2b3acfc6ec44bcfbe10be38df2983ee368fdf42 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Wed, 21 Dec 2022 14:04:41 +0800 Subject: [PATCH 276/313] dnn: add the CANN backend (#22634) * cann backend impl v1 * cann backend impl v2: use opencv parsers to build models for cann * adjust fc according to the new transA and transB * put cann net in cann backend node and reuse forwardLayer * use fork() to create a child process and compile cann model * remove legacy code * remove debug code * fall bcak to CPU backend if there is one layer not supoorted by CANN backend * fix netInput forward --- CMakeLists.txt | 15 + cmake/OpenCVFindCANN.cmake | 109 ++++++ cmake/checks/cann.cpp | 20 + modules/dnn/CMakeLists.txt | 16 + modules/dnn/include/opencv2/dnn/dnn.hpp | 10 + modules/dnn/src/layer.cpp | 6 + modules/dnn/src/layers/batch_norm_layer.cpp | 68 +++- modules/dnn/src/layers/blank_layer.cpp | 26 +- modules/dnn/src/layers/concat_layer.cpp | 36 +- modules/dnn/src/layers/const_layer.cpp | 40 +- modules/dnn/src/layers/convolution_layer.cpp | 74 ++++ modules/dnn/src/layers/elementwise_layers.cpp | 327 +++++++++++++++- modules/dnn/src/layers/eltwise_layer.cpp | 48 +++ modules/dnn/src/layers/flatten_layer.cpp | 31 +- .../dnn/src/layers/fully_connected_layer.cpp | 63 +++- modules/dnn/src/layers/lrn_layer.cpp | 36 +- .../dnn/src/layers/nary_eltwise_layers.cpp | 49 +++ modules/dnn/src/layers/padding_layer.cpp | 48 ++- modules/dnn/src/layers/permute_layer.cpp | 32 +- modules/dnn/src/layers/pooling_layer.cpp | 83 +++++ modules/dnn/src/layers/reshape_layer.cpp | 43 ++- modules/dnn/src/layers/resize_layer.cpp | 65 ++++ modules/dnn/src/layers/slice_layer.cpp | 63 +++- modules/dnn/src/layers/softmax_layer.cpp | 32 +- modules/dnn/src/legacy_backend.cpp | 5 + modules/dnn/src/net_cann.cpp | 348 ++++++++++++++++++ modules/dnn/src/net_impl.cpp | 5 +- modules/dnn/src/net_impl.hpp | 1 + modules/dnn/src/net_impl_backend.cpp | 16 + modules/dnn/src/op_cann.cpp | 329 +++++++++++++++++ modules/dnn/src/op_cann.hpp | 164 +++++++++ modules/dnn/src/registry.cpp | 5 + modules/dnn/test/test_common.hpp | 4 +- modules/dnn/test/test_common.impl.hpp | 19 +- 34 files changed, 2208 insertions(+), 28 deletions(-) create mode 100644 cmake/OpenCVFindCANN.cmake create mode 100644 cmake/checks/cann.cpp create mode 100644 modules/dnn/src/net_cann.cpp create mode 100644 modules/dnn/src/op_cann.cpp create mode 100644 modules/dnn/src/op_cann.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 45edafee38..6a620c94af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -468,6 +468,9 @@ OCV_OPTION(WITH_TIMVX "Include Tim-VX support" OFF OCV_OPTION(WITH_OBSENSOR "Include obsensor support (Orbbec RGB-D modules: Astra+/Femto)" ON VISIBLE_IF (WIN32 AND NOT ARM AND NOT WINRT) OR ( UNIX AND NOT APPLE AND NOT ANDROID) VERIFY HAVE_OBSENSOR) +OCV_OPTION(WITH_CANN "Include CANN support" OFF + VISIBLE_IF TRUE + VERIFY HAVE_CANN) # OpenCV build components # =================================================== @@ -753,6 +756,9 @@ endif() if(WITH_TIMVX) include(cmake/OpenCVFindTIMVX.cmake) endif() +if(WITH_CANN) + include(cmake/OpenCVFindCANN.cmake) +endif() # ---------------------------------------------------------------------------- # Detect other 3rd-party libraries/tools @@ -1736,6 +1742,15 @@ if(WITH_ONNX OR HAVE_ONNX) endif() endif() +if(WITH_CANN) + status("") + status(" CANN:" HAVE_CANN THEN "YES" ELSE "NO") + if(HAVE_CANN) + status(" Include path" CANN_INCLUDE_DIRS THEN "${CANN_INCLUDE_DIRS}" ELSE "NO") + status(" Link libraries:" CANN_LIBRARIES THEN "${CANN_LIBRARIES}" ELSE "NO") + endif() +endif() + # ========================== python ========================== if(BUILD_opencv_python2) status("") diff --git a/cmake/OpenCVFindCANN.cmake b/cmake/OpenCVFindCANN.cmake new file mode 100644 index 0000000000..b0b8e35c6b --- /dev/null +++ b/cmake/OpenCVFindCANN.cmake @@ -0,0 +1,109 @@ +ocv_check_environment_variables(CANN_INSTALL_DIR) + +if("cann${CANN_INSTALL_DIR}" STREQUAL "cann" AND DEFINED ENV{ASCEND_TOOLKIT_HOME}) + set(CANN_INSTALL_DIR $ENV{ASCEND_TOOLKIT_HOME}) + message(STATUS "CANN: updated CANN_INSTALL_DIR from ASCEND_TOOLKIT_HOME=$ENV{ASCEND_TOOLKIT_HOME}") +endif() + +if(CANN_INSTALL_DIR) + # Supported platforms: x86-64, arm64 + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") + else() + set(HAVE_CANN OFF) + message(STATUS "CANN: CANN toolkit supports x86-64 and arm64 but not ${CMAKE_SYSTEM_PROCESSOR}. Turning off HAVE_CANN") + return() + endif() + + # Supported OS: linux (because of we need fork() to build models in child process) + # done via checks in cann.cpp + # FIXME: remove the check if a better model building solution is found + + # include + set(incs_cann "${CANN_INSTALL_DIR}/include") + list(APPEND incs_cann "${CANN_INSTALL_DIR}/opp") + + # libs + # * libascendcl.so + set(lib_ascendcl "${CANN_INSTALL_DIR}/acllib/lib64") + find_library(found_lib_ascendcl NAMES ascendcl PATHS ${lib_ascendcl} NO_DEFAULT_PATH) + if(found_lib_ascendcl) + set(lib_ascendcl ${found_lib_ascendcl}) + message(STATUS "CANN: libascendcl.so is found at ${lib_ascendcl}") + else() + message(STATUS "CANN: Missing libascendcl.so. Turning off HAVE_CANN") + set(HAVE_CANN OFF) + return() + endif() + # * libgraph.so + set(lib_graph "${CANN_INSTALL_DIR}/compiler/lib64") + find_library(found_lib_graph NAMES graph PATHS ${lib_graph} NO_DEFAULT_PATH) + if(found_lib_graph) + set(lib_graph ${found_lib_graph}) + message(STATUS "CANN: libgraph.so is found at ${lib_graph}") + else() + message(STATUS "CANN: Missing libgraph.so. Turning off HAVE_CANN") + set(HAVE_CANN OFF) + return() + endif() + # * libge_compiler.so + set(lib_ge_compiler "${CANN_INSTALL_DIR}/compiler/lib64") + find_library(found_lib_ge_compiler NAMES ge_compiler PATHS ${lib_ge_compiler} NO_DEFAULT_PATH) + if(found_lib_ge_compiler) + set(lib_ge_compiler ${found_lib_ge_compiler}) + message(STATUS "CANN: libge_compiler.so is found at ${lib_ge_compiler}") + else() + message(STATUS "CANN: Missing libge_compiler.so. Turning off HAVE_CANN") + set(HAVE_CANN OFF) + return() + endif() + # * libopsproto.so + set(lib_opsproto "${CANN_INSTALL_DIR}/opp/op_proto/built-in") + find_library(found_lib_opsproto NAMES opsproto PATHS ${lib_opsproto} NO_DEFAULT_PATH) + if(found_lib_opsproto) + set(lib_opsproto ${found_lib_opsproto}) + message(STATUS "CANN: libopsproto.so is found at ${lib_opsproto}") + else() + message(STATUS "CANN: Missing libopsproto.so. Turning off HAVE_CANN") + set(HAVE_CANN OFF) + return() + endif() + + + set(libs_cann "") + list(APPEND libs_cann ${lib_ascendcl}) + list(APPEND libs_cann ${lib_opsproto}) + list(APPEND libs_cann ${lib_graph}) + list(APPEND libs_cann ${lib_ge_compiler}) + + try_compile(VALID_ASCENDCL + "${OpenCV_BINARY_DIR}" + "${OpenCV_SOURCE_DIR}/cmake/checks/cann.cpp" + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${incs_cann}" + "-DLINK_LIBRARIES:STRING=${libs_cann}" + OUTPUT_VARIABLE ASCEND_TRY_OUT) + + if(NOT ${VALID_ASCENDCL}) + message(WARNING "Cannot use CANN") + set(HAVE_CANN OFF) + return() + endif() + + set(HAVE_CANN ON) +endif() + +if(HAVE_CANN) + set(CANN_INCLUDE_DIRS ${incs_cann}) + set(CANN_LIBRARIES ${libs_cann}) + ocv_add_external_target(cann "${CANN_INCLUDE_DIRS}" "${CANN_LIBRARIES}" "HAVE_CANN") + ocv_warnings_disable(CMAKE_C_FLAGS -Wignored-qualifiers) + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wignored-qualifiers) +endif() + +MARK_AS_ADVANCED( + incs_cann + libs_cann + lib_ascendcl + lib_graph + lib_ge_compiler +) diff --git a/cmake/checks/cann.cpp b/cmake/checks/cann.cpp new file mode 100644 index 0000000000..08e463c4e5 --- /dev/null +++ b/cmake/checks/cann.cpp @@ -0,0 +1,20 @@ +#include +#include // fork() +#include + +int main(int /*argc*/, char** /*argv*/) +{ + int ret = aclInit(NULL); + if (ret != 0) + { + std::cerr << "Failed to initialize Ascend, ret = " << ret; + } + + ret = aclFinalize(); + if (ret != 0) + { + std::cerr << "Failed to de-initialize Ascend, ret = " << ret; + } + + return 0; +} diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 6333646a5a..1ec21c085d 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -31,6 +31,10 @@ if(HAVE_TIMVX) ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_TIMVX=1") endif() +if(HAVE_CANN) + ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_CANN=1") +endif() + ocv_option(OPENCV_DNN_CUDA "Build with CUDA support" HAVE_CUDA AND HAVE_CUBLAS @@ -162,6 +166,11 @@ if(HAVE_TIMVX) list(APPEND libs -Wl,--whole-archive ${TIMVX_LIBRARY} -Wl,--no-whole-archive) endif() +if(HAVE_CANN) + list(APPEND include_dirs ${CANN_INCLUDE_DIRS}) + list(APPEND libs -Wl,--whole-archive ${CANN_LIBRARIES} -Wl,--no-whole-archive) +endif() + set(webnn_srcs "") if(NOT EMSCRIPTEN) if(HAVE_WEBNN) @@ -264,3 +273,10 @@ if(TARGET ocv.3rdparty.openvino AND OPENCV_TEST_DNN_OPENVINO) ocv_target_link_libraries(opencv_test_dnn ocv.3rdparty.openvino) endif() endif() + +ocv_option(OPENCV_TEST_DNN_CANN "Build test with CANN" (TARGET ocv.3rdparty.cann)) +if(TARGET ocv.3rdparty.cann AND OPENCV_TEST_DNN_CANN) + if(TARGET opencv_test_dnn) + ocv_target_link_libraries(opencv_test_dnn ocv.3rdparty.cann) + endif() +endif() diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index affcf780f4..ffc9473c6e 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -81,6 +81,7 @@ CV__DNN_INLINE_NS_BEGIN DNN_BACKEND_CUDA, DNN_BACKEND_WEBNN, DNN_BACKEND_TIMVX, + DNN_BACKEND_CANN, #if defined(__OPENCV_BUILD) || defined(BUILD_PLUGIN) #if !defined(OPENCV_BINDING_PARSER) DNN_BACKEND_INFERENCE_ENGINE_NGRAPH = 1000000, // internal - use DNN_BACKEND_INFERENCE_ENGINE + setInferenceEngineBackendType() @@ -343,6 +344,15 @@ CV__DNN_INLINE_NS_BEGIN const std::vector > &outputsWrapper, bool isLast); + /** + * @brief Returns a CANN backend node + * + * @param inputsWrapper layer inputs + * @param index layer id for op name + * @param nodes inputs of this node + */ + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes); + /** * @brief Automatic Halide scheduling based on layer hyper-parameters. * @param[in] node Backend node with Halide functions. diff --git a/modules/dnn/src/layer.cpp b/modules/dnn/src/layer.cpp index 0ed3488da6..5305a5221d 100644 --- a/modules/dnn/src/layer.cpp +++ b/modules/dnn/src/layer.cpp @@ -84,6 +84,12 @@ Ptr Layer::initTimVX(void* timVxInfo, return Ptr(); } +Ptr Layer::initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) +{ + CV_Error(Error::StsNotImplemented, "CANN pipeline of " + type + " layers is not defined."); + return Ptr(); +} + Ptr Layer::tryAttach(const Ptr& node) { return Ptr(); diff --git a/modules/dnn/src/layers/batch_norm_layer.cpp b/modules/dnn/src/layers/batch_norm_layer.cpp index 377e05f5cc..e112ba0746 100644 --- a/modules/dnn/src/layers/batch_norm_layer.cpp +++ b/modules/dnn/src/layers/batch_norm_layer.cpp @@ -16,6 +16,7 @@ Implementation of Batch Normalization layer. #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #include @@ -40,6 +41,7 @@ public: Mat weights_, bias_; UMat umat_weight, umat_bias; mutable int dims; + float momentum; BatchNormLayerImpl(const LayerParams& params) @@ -55,6 +57,9 @@ public: hasWeights = hasBias = true; epsilon = params.get("eps", 1E-5); + // std::cout << params.get("momentum", 0.9) << std::endl; + momentum = params.get("momentum", 0.9); + size_t n = blobs[0].total(); CV_Assert(blobs[1].total() == n && blobs[0].isContinuous() && blobs[1].isContinuous() && @@ -177,7 +182,8 @@ public: return (backendId == DNN_BACKEND_OPENCV) || backendId == DNN_BACKEND_CUDA || (backendId == DNN_BACKEND_HALIDE && haveHalide()) || - backendId == DNN_BACKEND_WEBNN; + backendId == DNN_BACKEND_WEBNN || + backendId == DNN_BACKEND_CANN; } #ifdef HAVE_OPENCL @@ -385,6 +391,66 @@ public: } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(nodes.size() == 1); + CV_Assert(blobs.size() == 4); // must have scale, offset, mean and variance + + auto x = inputsWrapper[0].dynamicCast(); + auto channel = x->host->size[1]; + + // create operator + std::string op_name = cv::format("bn_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_epsilon(epsilon); + op->set_attr_data_format("NCHW"); + op->set_attr_is_training(false); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + // set inputs : scale (blobs[2]) + std::vector shape_{channel}; + auto op_const_scale = std::make_shared(blobs[2].data, blobs[2].type(), shape_, cv::format("%s_scale", op_name.c_str())); + op->set_input_scale(*(op_const_scale->getOp())); + op->update_input_desc_scale(*(op_const_scale->getTensorDesc())); + // set inputs : offset (blobs[3]) + auto op_const_offset = std::make_shared(blobs[3].data, blobs[3].type(), shape_, cv::format("%s_offset", op_name.c_str())); + op->set_input_offset(*(op_const_offset->getOp())); + op->update_input_desc_offset(*(op_const_offset->getTensorDesc())); + // set inputs : mean (blobs[0]) + auto op_const_mean = std::make_shared(blobs[0].data, blobs[0].type(), shape_, cv::format("%s_mean", op_name.c_str())); + op->set_input_mean(*(op_const_mean->getOp())); + op->update_input_desc_mean(*(op_const_mean->getTensorDesc())); + // set inputs : variance (blobs[1]) + auto op_const_var = std::make_shared(blobs[1].data, blobs[1].type(), shape_, cv::format("%s_var", op_name.c_str())); + op->set_input_variance(*(op_const_var->getOp())); + op->update_input_desc_variance(*(op_const_var->getTensorDesc())); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + auto output_bm_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_batch_mean(*output_bm_desc); + auto output_bv_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_batch_variance(*output_bv_desc); + auto output_rs1_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_reserve_space_1(*output_rs1_desc); + auto output_rs2_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_reserve_space_2(*output_rs2_desc); + auto output_rs3_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_reserve_space_3(*output_rs3_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE diff --git a/modules/dnn/src/layers/blank_layer.cpp b/modules/dnn/src/layers/blank_layer.cpp index 0d6ab19e4d..972aa7c9c8 100644 --- a/modules/dnn/src/layers/blank_layer.cpp +++ b/modules/dnn/src/layers/blank_layer.cpp @@ -43,6 +43,7 @@ #include "../op_cuda.hpp" #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" +#include "../op_cann.hpp" #ifdef HAVE_CUDA #include "../cuda4dnn/primitives/reshape.hpp" @@ -68,7 +69,8 @@ public: return true; #endif return backendId == DNN_BACKEND_OPENCV || - backendId == DNN_BACKEND_CUDA; + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_CANN; } bool getMemoryShapes(const std::vector &inputs, @@ -118,6 +120,28 @@ public: inputs[i].copyTo(outputs[i]); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + auto x_desc = x->getTensorDesc(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + // create operator + std::string op_name = cv::format("identity_%d", index); + auto op = std::make_shared(op_name); + + // set inputs + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + + // set output + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/concat_layer.cpp b/modules/dnn/src/layers/concat_layer.cpp index 5ba0cd199b..1b520cf87a 100644 --- a/modules/dnn/src/layers/concat_layer.cpp +++ b/modules/dnn/src/layers/concat_layer.cpp @@ -49,6 +49,7 @@ #include "../op_vkcom.hpp" #include "../op_webnn.hpp" #include "../op_timvx.hpp" +#include "../op_cann.hpp" #ifdef HAVE_OPENCL #include "opencl_kernels_dnn.hpp" @@ -140,7 +141,8 @@ public: backendId == DNN_BACKEND_CUDA || (backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1 && !padding) || // By channels (backendId == DNN_BACKEND_WEBNN && !padding) || - (backendId == DNN_BACKEND_VKCOM && haveVulkan() && !padding); + (backendId == DNN_BACKEND_VKCOM && haveVulkan() && !padding) || + (backendId == DNN_BACKEND_CANN && !padding); } template @@ -364,6 +366,38 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(inputsWrapper.size() == nodes.size()); + + // create operator + std::string op_name = cv::format("concat_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + int N = inputsWrapper.size(); + op->set_attr_concat_dim(axis); + op->set_attr_N(N); + + // set inputs : x (dynamic) + op->create_dynamic_input_x(N); + for (int i = 0; i < N; i++) + { + auto x_i = inputsWrapper[i].dynamicCast(); + auto x_i_desc = x_i->getTensorDesc(); + auto op_x_i = nodes[i].dynamicCast()->getOp(); + op->set_dynamic_input_x(i, *op_x_i, "y"); + op->update_dynamic_input_desc_x(i, *x_i_desc); + } + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/const_layer.cpp b/modules/dnn/src/layers/const_layer.cpp index 4392763be7..2141ad987a 100644 --- a/modules/dnn/src/layers/const_layer.cpp +++ b/modules/dnn/src/layers/const_layer.cpp @@ -11,6 +11,9 @@ #include "layers_common.hpp" #include "../ie_ngraph.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" + +#include #ifdef HAVE_OPENCL #include "opencl_kernels_dnn.hpp" @@ -40,7 +43,8 @@ public: #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_WEBNN || - backendId == DNN_BACKEND_CUDA; + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_CANN; } virtual bool getMemoryShapes(const std::vector &inputs, @@ -79,6 +83,40 @@ public: blobs[0].copyTo(outputs[0]); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto mat_shape = shape(blobs[0]); + std::vector mat_shape_{mat_shape.begin(), mat_shape.end()}; + + auto ge_shape = ge::Shape(mat_shape_); + auto ge_dtype = ge::DT_FLOAT; + switch (blobs[0].type()) + { + case CV_32F: break; + case CV_32S: ge_dtype = ge::DT_INT32; break; + default: CV_Error(Error::StsNotImplemented, "Unsuppported data type"); + } + auto size_of_type = sizeof(float); + switch (blobs[0].type()) + { + case CV_32F: break; + case CV_32S: size_of_type = sizeof(int); break; + default: CV_Error(Error::StsNotImplemented, "Unsuppported data type"); + } + + auto desc = std::make_shared(ge_shape, ge::FORMAT_NCHW, ge_dtype); + auto ge_tensor = std::make_shared(); + ge_tensor->SetTensorDesc(*desc); + ge_tensor->SetData(blobs[0].data, ge_shape.GetShapeSize() * size_of_type); + + std::string op_name = cv::format("const_%d", index); + auto op = std::make_shared(op_name); + op->set_attr_value(*ge_tensor); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index cc39593232..8648a25297 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -48,6 +48,7 @@ #include "../ie_ngraph.hpp" #include "../op_vkcom.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #include #include @@ -369,6 +370,17 @@ public: return true; } #endif +#ifdef HAVE_CANN + if (backendId == DNN_BACKEND_CANN) + { + if (ksize != 2) + { + CV_LOG_WARNING(NULL, "CANN supports Conv2D for now"); + return false; + } + return true; + } +#endif // HAVE_CANN return false; } @@ -768,6 +780,68 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(!blobs.empty()); + CV_Assert(inputsWrapper.size() == 1); + CV_Assert(nodes.size() == 1); + + bool has_bias = hasBias() || fusedBias; + + auto x = inputsWrapper[0].dynamicCast(); + const int x_in_channel = x->host->size[1]; + const int filter_out_channel = blobs[0].size[1]; + const int groups = x_in_channel / filter_out_channel; + + // create operator + std::string op_name = cv::format("conv2d_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_strides(ge::Operator::OpListInt( + {1, 1, (int64_t)strides[0], (int64_t)strides[1]} + )); + op->set_attr_pads(ge::Operator::OpListInt( + {(int64_t)pads_begin[1], (int64_t)pads_end[1], (int64_t)pads_begin[0], (int64_t)pads_end[0]} + )); + op->set_attr_dilations(ge::Operator::OpListInt( + {1, 1, (int64_t)dilations[0], (int64_t)dilations[1]} + )); + op->set_attr_groups(groups); + op->set_attr_data_format("NCHW"); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + // set inputs : weight + const Mat& w_mat = blobs[0]; + auto op_const_weight = std::make_shared(w_mat.data, w_mat.type(), shape(w_mat), cv::format("%s_w", op_name.c_str())); + op->set_input_filter(*(op_const_weight->getOp())); + op->update_input_desc_filter(*(op_const_weight->getTensorDesc())); + // set inputs : bias + if (has_bias) + { + int out_channel = blobs[0].size[0]; + Mat b_mat({out_channel}, CV_32F, &biasvec[0]); + + std::vector bias_shape{out_channel}; + auto op_const_bias = std::make_shared(b_mat.data, b_mat.type(), bias_shape, cv::format("%s_b", op_name.c_str())); + op->set_input_bias(*(op_const_bias->getOp())); + op->update_input_desc_bias(*(op_const_bias->getTensorDesc())); + } + + // set outputs + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif + #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector > &inputs, diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 01c369e5fd..a4b71ddddf 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -48,6 +48,7 @@ #include "../ie_ngraph.hpp" #include "../op_vkcom.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #include #include @@ -186,6 +187,12 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + return func.initCannOp(inputsWrapper, index, nodes); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE @@ -350,7 +357,8 @@ struct ReLUFunctor : public BaseFunctor return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE || - backendId == DNN_BACKEND_VKCOM; + backendId == DNN_BACKEND_VKCOM || + backendId == DNN_BACKEND_CANN; } void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const @@ -450,6 +458,42 @@ struct ReLUFunctor : public BaseFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto x_desc = x->getTensorDesc(); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + if (slope) + { + std::string op_name = cv::format("leakyrelu_%d", index); + auto op = std::make_shared(op_name); + + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + + op->set_attr_negative_slope(slope); + + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } + + std::string op_name = cv::format("relu_%d", index); + auto op = std::make_shared(op_name); // FIXIT: Relu6? + + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { @@ -525,7 +569,8 @@ struct ReLU6Functor : public BaseFunctor return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE || - backendId == DNN_BACKEND_WEBNN; + backendId == DNN_BACKEND_WEBNN || + backendId == DNN_BACKEND_CANN; } void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const @@ -607,6 +652,37 @@ struct ReLU6Functor : public BaseFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("clip_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + Mat min_value_mat(1, 1, CV_32F, Scalar(minValue)); + std::vector shape_{1}; + auto op_const_minv = std::make_shared(min_value_mat.data, min_value_mat.type(), shape_, cv::format("%s_min_value", op_name.c_str())); + op->set_input_clip_value_min(*(op_const_minv->getOp())); + op->update_input_desc_clip_value_min(*(op_const_minv->getTensorDesc())); + + Mat max_value_mat(1, 1, CV_32F, Scalar(maxValue)); + auto op_const_maxv = std::make_shared(max_value_mat.data, max_value_mat.type(), shape_, cv::format("%s_max_value", op_name.c_str())); + op->set_input_clip_value_min(*(op_const_maxv->getOp())); + op->update_input_desc_clip_value_min(*(op_const_maxv->getTensorDesc())); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) @@ -728,6 +804,12 @@ struct BaseDefaultFunctor : public BaseFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + CV_Error(Error::StsNotImplemented, ""); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) @@ -767,7 +849,8 @@ struct TanHFunctor : public BaseDefaultFunctor #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -790,6 +873,26 @@ struct TanHFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("tanh_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { @@ -811,7 +914,9 @@ struct SwishFunctor : public BaseDefaultFunctor { return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -834,6 +939,28 @@ struct SwishFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("swish_%d", index); + auto op = std::make_shared(op_name); + + op->set_attr_scale(1.0f); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { @@ -856,7 +983,9 @@ struct MishFunctor : public BaseDefaultFunctor { return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -887,6 +1016,26 @@ struct MishFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("mish_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { @@ -918,7 +1067,8 @@ struct SigmoidFunctor : public BaseDefaultFunctor #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -941,6 +1091,25 @@ struct SigmoidFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("sigmoid_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) @@ -970,7 +1139,8 @@ struct ELUFunctor : public BaseDefaultFunctor #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -998,6 +1168,28 @@ struct ELUFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("elu_%d", index); + auto op = std::make_shared(op_name); + + op->set_attr_alpha(alpha); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { @@ -1023,7 +1215,8 @@ struct AbsValFunctor : public BaseDefaultFunctor #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -1046,6 +1239,25 @@ struct AbsValFunctor : public BaseDefaultFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("abs_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) @@ -1071,7 +1283,8 @@ struct BNLLFunctor : public BaseDefaultFunctor { return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -1087,6 +1300,26 @@ struct BNLLFunctor : public BaseDefaultFunctor } #endif +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("bnll_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_HALIDE void attachHalide(const Halide::Expr& input, Halide::Func& top) { @@ -1123,6 +1356,26 @@ struct CeilFunctor : public BaseDefaultFunctor } #endif +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("bnll_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_HALIDE void attachHalide(const Halide::Expr& input, Halide::Func& top) { @@ -1143,7 +1396,10 @@ struct FloorFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE; + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_HALIDE || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -1158,6 +1414,26 @@ struct FloorFunctor : public BaseDefaultFunctor } #endif +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + + std::string op_name = cv::format("floor_%d", index); + auto op = std::make_shared(op_name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + #ifdef HAVE_HALIDE void attachHalide(const Halide::Expr& input, Halide::Func& top) { @@ -1992,6 +2268,12 @@ struct PowerFunctor : public BaseFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + CV_Error(Error::StsNotImplemented, ""); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) @@ -2240,6 +2522,31 @@ struct ChannelsPReLUFunctor : public BaseFunctor } #endif // HAVE_HALIDE +#ifdef HAVE_CANN + Ptr initCannOp(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) + { + auto x = inputsWrapper[0].dynamicCast(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto x_desc = x->getTensorDesc(); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::string op_name = cv::format("prelu_%d", index); + auto op = std::make_shared(op_name); + + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + + std::vector shape_{scale.size[0]}; // scale should be a 1d of shape [n] tensor, and it is a 2d mat of shape [n, 1] in opencv + auto op_const_slope = std::make_shared(scale.data, scale.type(), shape_, cv::format("%s_weight", op_name.c_str())); + op->set_input_weight(*(op_const_slope->getOp())); + op->update_input_desc_weight(*(op_const_slope->getTensorDesc())); + + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index a67b0c4bb5..24a87bcc17 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -46,6 +46,8 @@ #include "../op_halide.hpp" #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" +#include "../op_cann.hpp" + #include #ifdef HAVE_OPENCL @@ -169,6 +171,11 @@ public: return channelsMode == ELTWISE_CHANNNELS_SAME; #endif +#ifdef HAVE_CANN + if (backendId == DNN_BACKEND_CANN) + return channelsMode == ELTWISE_CHANNNELS_SAME && coeffs.empty(); +#endif + if (backendId == DNN_BACKEND_CUDA) { if(channelsModeInput == ELTWISE_CHANNNELS_INPUT_0 || channelsModeInput == ELTWISE_CHANNNELS_INPUT_0_TRUNCATE) @@ -841,6 +848,47 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(inputsWrapper.size() == 2); + CV_Assert(nodes.size() == 2); + + auto op_x1 = nodes[0].dynamicCast()->getOp(); + auto x1 = inputsWrapper[0].dynamicCast(); + auto x1_desc = x1->getTensorDesc(); + auto op_x2 = nodes[1].dynamicCast()->getOp(); + auto x2 = inputsWrapper[1].dynamicCast(); + auto x2_desc = x2->getTensorDesc(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::shared_ptr eltwise_operator = nullptr; + // add, mul, div, max, min + switch (op) + { +#define BUILD_CANN_ELTWISE_OP(op_type, class_name, op_name) \ + case op_type: { \ + auto eltwise_op = \ + std::make_shared(op_name); \ + eltwise_op->set_input_x1_by_name(*op_x1, "y"); \ + eltwise_op->set_input_x2_by_name(*op_x2, "y"); \ + eltwise_op->update_input_desc_x1(*x1_desc); \ + eltwise_op->update_input_desc_x2(*x2_desc); \ + eltwise_op->update_output_desc_y(*output_desc); \ + eltwise_operator = eltwise_op; \ + } break; + BUILD_CANN_ELTWISE_OP(SUM, Add, cv::format("add_%d", index)); + BUILD_CANN_ELTWISE_OP(PROD, Mul, cv::format("mul_%d", index)); + BUILD_CANN_ELTWISE_OP(DIV, Xdivy, cv::format("div_%d", index)); + BUILD_CANN_ELTWISE_OP(MAX, Maximum, cv::format("max_%d", index)); + BUILD_CANN_ELTWISE_OP(MIN, Minimum, cv::format("min_%d", index)); +#undef BUILD_CANN_ELTWISE_OP + default: CV_Error(Error::StsNotImplemented, "Unsupported eltwise operation"); + } + + return Ptr(new CannBackendNode(eltwise_operator)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/flatten_layer.cpp b/modules/dnn/src/layers/flatten_layer.cpp index b3f57dc7cd..ff30da3a11 100644 --- a/modules/dnn/src/layers/flatten_layer.cpp +++ b/modules/dnn/src/layers/flatten_layer.cpp @@ -45,6 +45,7 @@ #include "../op_cuda.hpp" #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" +#include "../op_cann.hpp" #include #include @@ -77,7 +78,8 @@ public: return true; #endif return backendId == DNN_BACKEND_OPENCV || - backendId == DNN_BACKEND_CUDA; + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_CANN; } bool getMemoryShapes(const std::vector &inputs, @@ -173,6 +175,33 @@ public: } } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + auto x_desc = x->getTensorDesc(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::string op_name = cv::format("flatten_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + int num_axes = x->host->dims; + int start_axis = normalize_axis(_startAxis, num_axes); + int end_axis = normalize_axis(_endAxis, num_axes); + op->set_attr_axis(start_axis); + op->set_attr_end_axis(end_axis); + + // set inputs + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + // set outputs + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 505ea460d5..539c083399 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -47,6 +47,7 @@ #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #include @@ -184,7 +185,8 @@ public: return backendId == DNN_BACKEND_OPENCV || (backendId == DNN_BACKEND_CUDA && !tranAorB) || (backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1 && !tranAorB) || - (backendId == DNN_BACKEND_WEBNN && axis == 1 && !tranAorB); + (backendId == DNN_BACKEND_WEBNN && axis == 1 && !tranAorB) || + backendId == DNN_BACKEND_CANN;; } virtual bool setActivation(const Ptr& layer) CV_OVERRIDE @@ -660,6 +662,65 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x1 = inputsWrapper[0].dynamicCast(); + auto x1_desc = x1->getTensorDesc(); + auto op_x1 = nodes[0].dynamicCast()->getOp(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::string op_name = cv::format("matmul_%d", index); + auto op = std::make_shared(op_name); + + if (!blobs.empty()) // if B is const + { + // set attributes + op->set_attr_transpose_x1(false); + // weightMat always needs to be transposed, since CPU backend + // implementation is input * weight.im2row + op->set_attr_transpose_x2(true); + + // set inputs + // set inputs : x2 (weight) + auto op_const_weight = std::make_shared(weightsMat.data, weightsMat.type(), shape(weightsMat), cv::format("%s_w", op_name.c_str())); + op->set_input_x2_by_name(*(op_const_weight->getOp()), "y"); + op->update_input_desc_x2(*(op_const_weight->getTensorDesc())); + } + else + { + // A and B are variable inputs; non-const bias is not considered + CV_Assert(inputsWrapper.size() == 2); + CV_Assert(nodes.size() == 2); + + // set attributes + op->set_attr_transpose_x1(transA); + op->set_attr_transpose_x2(transB); + + // set inputs : x2 (weight) + auto op_x2 = nodes[1].dynamicCast()->getOp(); + auto x2_desc = inputsWrapper[1].dynamicCast()->getTensorDesc(); + op->set_input_x2_by_name(*op_x2, "y"); + op->update_input_desc_x2(*x2_desc); + } + + // set inputs + // set inputs : x1 (input) + op->set_input_x1_by_name(*op_x1, "y"); + op->update_input_desc_x1(*x1_desc); + // set inputs : bias (bias) + auto bias_mat = bias ? biasMat : Mat::zeros(1, weightsMat.size[0], weightsMat.type()); + std::vector bias_shape{weightsMat.size[0]}; + auto op_const_bias = std::make_shared(bias_mat.data, bias_mat.type(), bias_shape, cv::format("%s_b", op_name.c_str())); + op->set_input_bias(*(op_const_bias->getOp())); + op->update_input_desc_bias(*(op_const_bias->getTensorDesc())); + + // set outputs + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/lrn_layer.cpp b/modules/dnn/src/layers/lrn_layer.cpp index 6c3a654159..f012a91730 100644 --- a/modules/dnn/src/layers/lrn_layer.cpp +++ b/modules/dnn/src/layers/lrn_layer.cpp @@ -47,6 +47,7 @@ #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" #include "../op_vkcom.hpp" +#include "../op_cann.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/dnn/shape_utils.hpp" @@ -106,7 +107,8 @@ public: return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_HALIDE || - (backendId == DNN_BACKEND_VKCOM && haveVulkan() && (size % 2 == 1) && (type == CHANNEL_NRM)); + (backendId == DNN_BACKEND_VKCOM && haveVulkan() && (size % 2 == 1) && (type == CHANNEL_NRM)) || + backendId == DNN_BACKEND_CANN; } #ifdef HAVE_OPENCL @@ -442,6 +444,38 @@ public: #endif // HAVE_HALIDE } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + + // create operator + std::string op_name = cv::format("lrn_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_depth_radius(size); + op->set_attr_bias(bias); + op->set_attr_alpha(alpha); + op->set_attr_beta(beta); + op->set_attr_norm_region("ACROSS_CHANNELS"); + if (type == SPATIAL_NRM) + op->set_attr_norm_region("WITHIN_CHANNEL"); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index ecd8b7351a..6850cd5211 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -5,6 +5,8 @@ #include "../precomp.hpp" #include "layers_common.hpp" #include "../op_cuda.hpp" +#include "../op_cann.hpp" + #include #include @@ -97,6 +99,11 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { +#ifdef HAVE_CANN + if (backendId == DNN_BACKEND_CANN) + return op == OPERATION::ADD || op == OPERATION::PROD || op == OPERATION::DIV || + op == OPERATION::DIV || op == OPERATION::MAX || op == OPERATION::MIN; +#endif if (op == OPERATION::MAX || op == OPERATION::MIN || op == OPERATION::SUM || op == OPERATION::PROD || op == OPERATION::DIV) return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA; @@ -682,6 +689,48 @@ public: } #endif +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(inputsWrapper.size() == 2); + CV_Assert(nodes.size() == 2); + + auto op_x1 = nodes[0].dynamicCast()->getOp(); + auto x1 = inputsWrapper[0].dynamicCast(); + auto x1_desc = x1->getTensorDesc(); + auto op_x2 = nodes[1].dynamicCast()->getOp(); + auto x2 = inputsWrapper[1].dynamicCast(); + auto x2_desc = x2->getTensorDesc(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::shared_ptr eltwise_operator = nullptr; + // add, mul, div, max, min + switch (op) + { +#define BUILD_CANN_ELTWISE_OP(op_type, class_name, op_name) \ + case op_type: { \ + auto eltwise_op = \ + std::make_shared(op_name); \ + eltwise_op->set_input_x1_by_name(*op_x1, "y"); \ + eltwise_op->set_input_x2_by_name(*op_x2, "y"); \ + eltwise_op->update_input_desc_x1(*x1_desc); \ + eltwise_op->update_input_desc_x2(*x2_desc); \ + eltwise_op->update_output_desc_y(*output_desc); \ + eltwise_operator = eltwise_op; \ + } break; + BUILD_CANN_ELTWISE_OP(OPERATION::ADD, Add, cv::format("add_%d", index)); + BUILD_CANN_ELTWISE_OP(OPERATION::PROD, Mul, cv::format("mul_%d", index)); + BUILD_CANN_ELTWISE_OP(OPERATION::DIV, Xdivy, cv::format("div_%d", index)); + BUILD_CANN_ELTWISE_OP(OPERATION::MAX, Maximum, cv::format("max_%d", index)); + BUILD_CANN_ELTWISE_OP(OPERATION::MIN, Minimum, cv::format("min_%d", index)); +#undef BUILD_CANN_ELTWISE_OP + default: CV_Error(Error::StsNotImplemented, "Unsupported eltwise operation"); + } + + return Ptr(new CannBackendNode(eltwise_operator)); + } +#endif // HAVE_CANN + virtual bool tryQuantize(const std::vector > &scales, const std::vector > &zeropoints, LayerParams& params) CV_OVERRIDE { diff --git a/modules/dnn/src/layers/padding_layer.cpp b/modules/dnn/src/layers/padding_layer.cpp index aea8ab3168..359c82a1a3 100644 --- a/modules/dnn/src/layers/padding_layer.cpp +++ b/modules/dnn/src/layers/padding_layer.cpp @@ -15,6 +15,7 @@ Implementation of padding layer, which adds paddings to input blob. #include "../op_halide.hpp" #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" +#include "../op_cann.hpp" #include @@ -113,7 +114,8 @@ public: #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - (backendId == DNN_BACKEND_HALIDE && haveHalide() && dstRanges.size() == 4); + (backendId == DNN_BACKEND_HALIDE && haveHalide() && dstRanges.size() == 4) || + backendId == DNN_BACKEND_CANN; } void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE @@ -219,6 +221,50 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + + // create operator + std::string op_name = cv::format("pad_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_mode(paddingType.c_str()); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + // set inputs : paddings + std::vector pads; + for (int i = 0; i < paddings.size(); i++) + { + pads.push_back(paddings[i].first); + pads.push_back(paddings[i].second); + } + std::vector pads_shape{(int)pads.size()}; + Mat paddings_mat(pads_shape, CV_32S, &pads[0]); + auto op_const_paddings = std::make_shared(paddings_mat.data, paddings_mat.type(), pads_shape, cv::format("%s_paddings", op_name.c_str())); + op->set_input_paddings(*(op_const_paddings->getOp())); + op->update_input_desc_paddings(*(op_const_paddings->getTensorDesc())); + // set inputs : constant_values + std::vector constant_values_shape{1}; + Mat constant_values_mat(1, 1, CV_32F, Scalar(paddingValue)); + auto op_const_constant_values = std::make_shared(constant_values_mat.data, constant_values_mat.type(), constant_values_shape, cv::format("%s_constant_values", op_name.c_str())); + op->set_input_constant_values(*(op_const_constant_values->getOp())); + op->update_input_desc_constant_values(*(op_const_constant_values->getTensorDesc())); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/permute_layer.cpp b/modules/dnn/src/layers/permute_layer.cpp index cce36b951f..1aee12d7ae 100644 --- a/modules/dnn/src/layers/permute_layer.cpp +++ b/modules/dnn/src/layers/permute_layer.cpp @@ -48,6 +48,7 @@ #include "../op_vkcom.hpp" #include "../op_webnn.hpp" #include "../op_timvx.hpp" +#include "../op_cann.hpp" #include #include @@ -143,7 +144,8 @@ public: return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_WEBNN || - (backendId == DNN_BACKEND_VKCOM && haveVulkan()); + (backendId == DNN_BACKEND_VKCOM && haveVulkan()) || + backendId == DNN_BACKEND_CANN; } bool getMemoryShapes(const std::vector &inputs, @@ -438,6 +440,34 @@ public: } } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + + // create operator + std::string op_name = cv::format("permute_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_order(ge::Operator::OpListInt( + _order.begin(), _order.end() + )); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index 9a808a7c67..9b9ced468f 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -47,6 +47,7 @@ #include "../op_halide.hpp" #include "../op_inf_engine.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #ifdef HAVE_DNN_NGRAPH #include "../ie_ngraph.hpp" @@ -199,6 +200,12 @@ public: { return type == MAX || type == AVE || type == ROI; } +#ifdef HAVE_CANN + if (backendId == DNN_BACKEND_CANN) + { + return type == MAX || type == AVE; + } +#endif #ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { @@ -540,6 +547,82 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto x_desc = x->getTensorDesc(); + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + std::string op_name_base = cv::format("pooling_%d", index); + if (type == MAX) + { + std::string op_name = cv::format("max_%s", op_name_base.c_str()); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_ksize(ge::Operator::OpListInt( + {1, 1, (int64_t)kernel_size[0], (int64_t)kernel_size[1]} + )); + op->set_attr_strides(ge::Operator::OpListInt( + {1, 1, (int64_t)strides[0], (int64_t)strides[1]} + )); + std::string cann_pad_mode{"CALCULATED"}; + if (padMode == "SAME" || padMode == "VALID") + cann_pad_mode = padMode; + op->set_attr_padding_mode(cann_pad_mode.c_str()); + op->set_attr_pads(ge::Operator::OpListInt( + {(int64_t)pads_begin[0], (int64_t)pads_end[0], (int64_t)pads_begin[1], (int64_t)pads_end[1]} + )); + op->set_attr_data_format("NCHW"); + op->set_attr_global_pooling(globalPooling); + op->set_attr_ceil_mode(ceilMode); + + // set inputs + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + // set outputs + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } + else if (type == AVE) + { + std::string op_name = cv::format("avg_%s", op_name_base.c_str()); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_ksize(ge::Operator::OpListInt( + {1, 1, (int64_t)kernel_size[0], (int64_t)kernel_size[1]} + )); + op->set_attr_strides(ge::Operator::OpListInt( + {1, 1, (int64_t)strides[0], (int64_t)strides[1]} + )); + std::string cann_pad_mode{"CALCULATED"}; + if (padMode == "SAME" || padMode == "VALID") + cann_pad_mode = padMode; + op->set_attr_padding_mode(cann_pad_mode.c_str()); + op->set_attr_pads(ge::Operator::OpListInt( + {(int64_t)pads_begin[0], (int64_t)pads_end[0], (int64_t)pads_begin[1], (int64_t)pads_end[1]} + )); + op->set_attr_global_pooling(globalPooling); + op->set_attr_ceil_mode(ceilMode); + auto cann_exclusive = !avePoolPaddedArea; + op->set_attr_exclusive(cann_exclusive); + + // set inputs + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + // set outputs + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } + else + CV_Error(Error::StsNotImplemented, "Unsupported pooling type"); + } +#endif #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/reshape_layer.cpp b/modules/dnn/src/layers/reshape_layer.cpp index 290effd380..3ff8a225b7 100644 --- a/modules/dnn/src/layers/reshape_layer.cpp +++ b/modules/dnn/src/layers/reshape_layer.cpp @@ -47,6 +47,7 @@ #include "../ie_ngraph.hpp" #include "../op_webnn.hpp" #include "../op_timvx.hpp" +#include "../op_cann.hpp" #include @@ -163,8 +164,8 @@ public: ReshapeLayerImpl(const LayerParams& params) { setParamsFrom(params); - int axis = params.get("axis", 0); - int numAxes = params.get("num_axes", -1); + axis = params.get("axis", 0); + numAxes = params.get("num_axes", -1); hasDynamicShapes = params.get("has_dynamic_shapes", false); shapesInitialized = !hasDynamicShapes; @@ -224,7 +225,8 @@ public: #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - backendId == DNN_BACKEND_WEBNN; + backendId == DNN_BACKEND_WEBNN || + backendId == DNN_BACKEND_CANN; } bool getMemoryShapes(const std::vector &inputs, @@ -324,6 +326,39 @@ public: } } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + + // create operator + std::string op_name = cv::format("reshape_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_axis(axis); + op->set_attr_num_axes(numAxes); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + // set inputs : shape + std::vector shape_of_shape{(int)newShapeDesc.size()}; + Mat shape_mat(shape_of_shape, CV_32S, newShapeDesc.data()); + auto op_const_shape = std::make_shared(shape_mat.data, shape_mat.type(), shape_of_shape, cv::format("%s_shape", op_name.c_str())); + op->set_input_shape(*(op_const_shape->getOp())); + op->update_input_desc_shape(*(op_const_shape->getTensorDesc())); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, @@ -464,6 +499,8 @@ public: } private: + int axis; + int numAxes; std::vector outShapes; std::vector dynamicShapes; // Which axes shapes are dynamic and require reinitialization with new input std::vector inputIndices; // Which axes from input are needed to compute correct output shape diff --git a/modules/dnn/src/layers/resize_layer.cpp b/modules/dnn/src/layers/resize_layer.cpp index ab640dbf3f..356b193ab5 100644 --- a/modules/dnn/src/layers/resize_layer.cpp +++ b/modules/dnn/src/layers/resize_layer.cpp @@ -8,6 +8,7 @@ #include "layers_common.hpp" #include "../op_cuda.hpp" #include "../op_inf_engine.hpp" +#include "../op_cann.hpp" #include #ifdef HAVE_DNN_NGRAPH @@ -77,6 +78,9 @@ public: if (backendId == DNN_BACKEND_CUDA) return interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear"; + if (backendId == DNN_BACKEND_CANN) + return interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear"; + #ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) { @@ -307,6 +311,67 @@ public: CV_Error(Error::StsNotImplemented, "Unknown interpolation: " + interpolation); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + auto x_desc = x->getTensorDesc(); + auto op_x = nodes[0].dynamicCast()->getOp(); + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + + // create operator + std::string op_name = cv::format("resize_%d", index); + + if (interpolation == "nearest") + { + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_align_corners(alignCorners); + op->set_attr_half_pixel_centers(halfPixelCenters); + + // set inputs : x + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + // set inputs : size + std::vector shape_of_size_mat{2}; + Mat size_mat(2, 1, CV_32S, Scalar(outHeight, outWidth)); + auto op_const_size = std::make_shared(size_mat.data, size_mat.type(), shape_of_size_mat, cv::format("%s_size", op_name.c_str())); + op->set_input_size(*(op_const_size->getOp())); + op->update_input_desc_size(*(op_const_size->getTensorDesc())); + + // set outputs + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } + else if (interpolation == "opencv_linear" || interpolation == "bilinear") + { + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_align_corners(alignCorners); + op->set_attr_half_pixel_centers(halfPixelCenters); + + // set inputs : x + op->set_input_x_by_name(*op_x, "y"); + op->update_input_desc_x(*x_desc); + // set inputs : size + std::vector shape_of_size_mat{2}; + Mat size_mat(2, 1, CV_32S, Scalar(outHeight, outWidth)); + auto op_const_size = std::make_shared(size_mat.data, size_mat.type(), shape_of_size_mat, cv::format("%s_size", op_name.c_str())); + op->set_input_size(*(op_const_size->getOp())); + op->update_input_desc_size(*(op_const_size->getTensorDesc())); + + // set outputs + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } + else + CV_Error(Error::StsNotImplemented, "Unsupported interpolation by CANN backend: " + interpolation); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp index aa44e4a5b9..d646b6c058 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -44,6 +44,7 @@ #include "../op_cuda.hpp" #include "../op_inf_engine.hpp" #include "../ie_ngraph.hpp" +#include "../op_cann.hpp" #include "layers_common.hpp" #include @@ -198,7 +199,7 @@ public: if (backendId == DNN_BACKEND_CUDA) return !hasSteps; #endif - return backendId == DNN_BACKEND_OPENCV; + return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CANN; } bool getMemoryShapes(const std::vector &inputs, @@ -589,6 +590,66 @@ public: } } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(sliceRanges.size() == 1); + CV_Assert(sliceSteps.size() == 1); + CV_Assert(sliceRanges[0].size() == sliceSteps[0].size()); + + auto x = inputsWrapper[0].dynamicCast(); + const int dims = x->host->dims; + + // create operator + std::string op_name = cv::format("slice_%d", index); + auto op = std::make_shared(op_name); + + // retrieve begins, ends, axes and steps + std::vector begins, ends, axes, steps; + for (int i = 0; i < sliceRanges[0].size(); i++) + { + begins.push_back(sliceRanges[0][i].start); + ends.push_back(sliceRanges[0][i].end); + axes.push_back(i); + steps.push_back(sliceSteps[0][i]); + } + std::vector shape_{dims}; + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + // set inputs : begin + Mat begin_mat(shape_, CV_32S, &begins[0]); + auto op_const_begin = std::make_shared(begin_mat.data, begin_mat.type(), shape_, cv::format("%s_begin", op_name.c_str())); + op->set_input_begin(*(op_const_begin->getOp())); + op->update_input_desc_begin(*(op_const_begin->getTensorDesc())); + // set inputs : end + Mat end_mat(shape_, CV_32S, &ends[0]); + auto op_const_end = std::make_shared(end_mat.data, end_mat.type(), shape_, cv::format("%s_end", op_name.c_str())); + op->set_input_end(*(op_const_end->getOp())); + op->update_input_desc_end(*(op_const_end->getTensorDesc())); + // set inputs : axes + Mat axes_mat(shape_, CV_32S, &axes[0]); + auto op_const_axes = std::make_shared(axes_mat.data, axes_mat.type(), shape_, cv::format("%s_axes", op_name.c_str())); + op->set_input_axes(*(op_const_axes->getOp())); + op->update_input_desc_axes(*(op_const_axes->getTensorDesc())); + // set inputs : strides + Mat strides_mat(shape_, CV_32S, &steps[0]); + auto op_const_strides = std::make_shared(strides_mat.data, strides_mat.type(), shape_, cv::format("%s_strides", op_name.c_str())); + op->set_input_strides(*(op_const_strides->getOp())); + op->update_input_desc_strides(*(op_const_strides->getTensorDesc())); + + // set outputs + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif + #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/layers/softmax_layer.cpp b/modules/dnn/src/layers/softmax_layer.cpp index b10aef3453..c1ea4d2297 100644 --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@ -48,6 +48,7 @@ #include "../ie_ngraph.hpp" #include "../op_vkcom.hpp" #include "../op_webnn.hpp" +#include "../op_cann.hpp" #include #include @@ -116,7 +117,8 @@ public: return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || (backendId == DNN_BACKEND_HALIDE && haveHalide() && axisRaw == 1) || - (backendId == DNN_BACKEND_VKCOM && haveVulkan()); + (backendId == DNN_BACKEND_VKCOM && haveVulkan()) || + backendId == DNN_BACKEND_CANN; } #ifdef HAVE_OPENCL @@ -362,6 +364,34 @@ public: return Ptr(); } +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputsWrapper, const int index, const std::vector >& nodes) CV_OVERRIDE + { + auto x = inputsWrapper[0].dynamicCast(); + + // create operator + std::string op_name = cv::format("softmax_%d", index); + auto op = std::make_shared(op_name); + + // set attributes + op->set_attr_axes(ge::Operator::OpListInt( + {(int64_t)axisRaw} + )); + + // set inputs + // set inputs : x + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, "y"); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + // set outputs + auto output_y_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_y_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, diff --git a/modules/dnn/src/legacy_backend.cpp b/modules/dnn/src/legacy_backend.cpp index 431c597fab..b092fb057c 100644 --- a/modules/dnn/src/legacy_backend.cpp +++ b/modules/dnn/src/legacy_backend.cpp @@ -13,6 +13,7 @@ #include "op_cuda.hpp" #include "op_webnn.hpp" #include "op_timvx.hpp" +#include "op_cann.hpp" namespace cv { namespace dnn { @@ -115,6 +116,10 @@ Ptr wrapMat(int backendId, int targetId, cv::Mat& m) return Ptr(new TimVXBackendWrapper(m)); #endif // HAVE_TIMVX } + else if (backendId == DNN_BACKEND_CANN) + { + CV_Assert(0 && "Internal error: DNN_BACKEND_CANN must be implemented through inheritance"); + } else CV_Error(Error::StsNotImplemented, "Unknown backend identifier"); return Ptr(); // TODO Error? diff --git a/modules/dnn/src/net_cann.cpp b/modules/dnn/src/net_cann.cpp new file mode 100644 index 0000000000..62d45d85c5 --- /dev/null +++ b/modules/dnn/src/net_cann.cpp @@ -0,0 +1,348 @@ +// 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" + +#include + +#include "net_impl.hpp" + +namespace cv { namespace dnn { +CV__DNN_INLINE_NS_BEGIN + +#ifdef HAVE_CANN + +static std::shared_ptr compileCannGraph(std::shared_ptr graph); + +class NetImplCann CV_FINAL : public Net::Impl +{ +public: + typedef Net::Impl Base; + + bool newWasSupported, netWasConverted; + + explicit NetImplCann(const Ptr& basePtr) + : Net::Impl() + { + CV_LOG_INFO(NULL, "Initializing NetImplCann"); + basePtr_ = basePtr; + newWasSupported = true; + netWasConverted = false; + + init(); + + CV_LOG_INFO(NULL, "Finished initializing NetImplCann"); + } + + void init() + { + CV_TRACE_FUNCTION(); + CV_Assert(basePtr_); + Net::Impl& base = *basePtr_; + CV_Assert(!base.netWasAllocated); + CV_Assert(!base.netWasQuantized); // does not support quantized net for now + netInputLayer = base.netInputLayer; + blobsToKeep = base.blobsToKeep; + layers = base.layers; + for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) + { + LayerData& ld = it->second; + ld.resetAllocation(); + } + layerNameToId = base.layerNameToId; + outputNameToId = base.outputNameToId; + preferableBackend = DNN_BACKEND_CANN; + preferableTarget = DNN_TARGET_NPU; // force using NPU + hasDynamicShapes = base.hasDynamicShapes; + CV_Assert(base.backendWrappers.empty()); //backendWrappers = base.backendWrappers; + lastLayerId = base.lastLayerId; + netWasAllocated = base.netWasAllocated; + netWasQuantized = base.netWasQuantized; + fusion = base.fusion; + } + + bool empty() const override + { + return Base::empty(); + } + + void setPreferableBackend(Net& net, int backendId) override + { + if (backendId == preferableBackend) + return; // no-op + else + CV_Error(Error::StsError, "DNN: Can't switch backend from CANN to other"); + Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); + impl_ptr_ref = basePtr_; + basePtr_->setPreferableBackend(net, backendId); + } + + void setPreferableTarget(int targetId) override + { + if (targetId != preferableTarget) + { + CV_Error(Error::StsError, "DNN: Can't switch target from NPU to other"); + } + } + + Ptr wrap(Mat& host) override + { + return Ptr(new CannBackendWrapper(host)); + } + + // void fuseLayers(const std::vector& blobsToKeep_); // fusion is done in the CANN graph engine + + void initBackend(const std::vector& blobsToKeep_) override; + + void forwardLayer(LayerData& ld) override; +}; + +void NetImplCann::initBackend(const std::vector& blobsToKeep_) +{ + CV_TRACE_FUNCTION(); + CV_CheckEQ(preferableBackend, DNN_BACKEND_CANN, ""); + + // netWasAllocated turns to false if requested output is changed or input shape changes + if (netWasConverted && netWasAllocated) + return; + + if (!netWasConverted) + { + newWasSupported = true; + for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); ++it) + { + auto& ld = it->second; + auto layer = ld.layerInstance; + if (ld.id != 0 && !layer->supportBackend(preferableBackend)) + { + newWasSupported = false; + CV_LOG_INFO(NULL, "DNN/CANN: layer (name=" << ld.name << ", type=" << ld.type << ") is not supported by CANN backend. Going back to CPU backend"); + } + } + } + if (!newWasSupported) + return ; + + // convert layers to CANN operators, + // collect graph input and output operators, + // collect and input and output wrappers + int firstOutputLayerId = -1; + std::vector > netInputNodes; + std::vector graphInputOps, graphOutputOps; + std::vector> graphInputWrappers, graphOutputWrappers; + CV_LOG_INFO(NULL, "DNN/CANN: converting layers to CANN operators"); + for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); ++it) + { + LayerData& ld = it->second; + auto layer = ld.layerInstance; + + if (ld.id == 0) + { + for (int i = 0; i < ld.outputBlobsWrappers.size(); i++) + { + std::string inputName = netInputLayer->outNames.empty() ? cv::format("%s_%d", ld.name.c_str(), i) : netInputLayer->outNames[i]; + auto inputOp = std::make_shared(inputName); + + // retrieve tensor description + auto wrapper = ld.outputBlobsWrappers[i]; + graphInputWrappers.push_back(wrapper); + auto cannWrapper = wrapper.dynamicCast(); + CV_Assert(!cannWrapper.empty()); + + inputOp->update_input_desc_x(*(cannWrapper->desc_)); + inputOp->update_output_desc_y(*(cannWrapper->desc_)); + + graphInputOps.push_back(*inputOp); + netInputNodes.push_back(Ptr(new CannBackendNode(inputOp))); + } + } + else + { + ld.skip = true; // skip all cann operators + + std::vector > layerInputNodes; + for (int i = 0; i < ld.inputBlobsId.size(); i++) + { + int layerInputLid = ld.inputBlobsId[i].lid; + int layerInputOid = ld.inputBlobsId[i].oid; + if (layerInputLid == 0) + { + layerInputNodes.push_back(netInputNodes[layerInputOid]); + } + else // here we do not consider an op with multiple outputs + { + layerInputNodes.push_back(layers[layerInputLid].backendNodes[preferableBackend]); + } + } + + CV_LOG_INFO(NULL, "DNN/CANN: converting layer " << ld.name << "@" << ld.type << "@" << ld.id << " to CANN operator"); + auto backendNode = layer->initCann(ld.inputBlobsWrappers, ld.id, layerInputNodes); + + // collect outputs + bool isOutputNode = ld.consumers.size() == 0 ? true : false; + if (isOutputNode) + { + if (firstOutputLayerId < 0) + firstOutputLayerId = ld.id; + auto cannNode = backendNode.dynamicCast(); + graphOutputOps.push_back(*(cannNode->getOp())); + // assume cann graph outputs and dnn net outputs have the same order + for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) + { + graphOutputWrappers.push_back(ld.outputBlobsWrappers[i]); + } + } + + ld.backendNodes[preferableBackend] = backendNode; + } + } + CV_LOG_INFO(NULL, "DNN/CANN: done converting layers to CANN operators"); + + // build graph from collected graph inputs and outputs + CV_LOG_INFO(NULL, "DNN/CANN: building ge::Graph"); + std::string graphName = cv::format("graph_%d", 0); + std::shared_ptr graph = std::make_shared(graphName.c_str()); + (void)graph->SetInputs(graphInputOps); + (void)graph->SetOutputs(graphOutputOps); + CV_LOG_INFO(NULL, "DNN/CANN: done building ge::Graph"); + + // convert ge::Graph to OM buffer + CV_LOG_INFO(NULL, "DNN/CANN: converting ge::Graph to OM buffer"); + std::shared_ptr modelBuffer = compileCannGraph(graph); + CV_LOG_INFO(NULL, "DNN/CANN: OM buffer size = " << modelBuffer->length); + CV_LOG_INFO(NULL, "DNN/CANN: done building ge::Graph to OM buffer"); + + // keep net in the first output node and mark the node runnable + auto& ld = layers[firstOutputLayerId]; + auto cannNode = ld.backendNodes[preferableBackend].dynamicCast(); + std::shared_ptr net = std::shared_ptr(new CannNet()); + net->loadModelBuffer(modelBuffer); + net->bindInputWrappers(graphInputWrappers); + net->bindOutputWrappers(graphOutputWrappers); + cannNode->net = net; + ld.skip = false; + + netWasConverted = true; +} + +void NetImplCann::forwardLayer(LayerData& ld) +{ + CV_TRACE_FUNCTION(); + + auto layer = ld.layerInstance; + + if (!ld.skip) + { + auto it = ld.backendNodes.find(preferableBackend); + if (ld.id == 0 || it == ld.backendNodes.end()) // input layer + { + return Base::forwardLayer(ld); + } + + CV_Assert(it != ld.backendNodes.end()); + const Ptr& node = it->second; + CV_Assert(!node.empty()); + auto cannNode = node.dynamicCast(); + CV_Assert(!cannNode.empty()); + CV_Assert(cannNode->net); + + TickMeter tm; + tm.start(); + + cannNode->net->forward(); + + tm.stop(); + int64_t t = tm.getTimeTicks(); + layersTimings[ld.id] = (t > 0) ? t : 1; + } + else + { + layersTimings[ld.id] = 0; + } + + ld.flag = 1; +} + +std::shared_ptr compileCannGraph(std::shared_ptr graph) +{ + const size_t hdrsize = 32; + std::shared_ptr out_buffer = std::make_shared(); + size_t buf_size = (1 << 27), model_size; // default buf_size 128 MB + for (int iter = 0; iter < 2; ++iter) + { + size_t* shared_buf = (size_t*)mmap(NULL, buf_size + hdrsize, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + uint8_t* model_data = (uint8_t*)(shared_buf + 1); + pid_t child; + int childstate = 0; + bool ok; + if ((child=fork()) == 0) + { + // initialize engine + std::map options = { + {ge::AscendString(ge::ir_option::SOC_VERSION), ge::AscendString("Ascend310")}, + }; + ACL_CHECK_GRAPH_RET(ge::aclgrphBuildInitialize(options)); + + // build + std::shared_ptr om_model = std::make_shared(); + std::map build_options; + ACL_CHECK_GRAPH_RET(aclgrphBuildModel(*graph, build_options, *om_model)); + +#if 0 + // (optional). Dump model + AscendString graph_name; + graph.GetName(graph_name); + aclgrphDumpGraph(graph, graph_name.GetString(), 7); + // (optional). Save model + aclgrphSaveModel(graph_name.GetString(), *om_model); +#endif + + // finalize engine + ge::aclgrphBuildFinalize(); + + // send model from child to parent + size_t model_size = om_model->length; + *shared_buf = model_size; + if (model_size > buf_size) + { + exit(1); + } + else + { + memcpy(model_data, om_model->data.get(), model_size); + exit(0); + } + } + waitpid (child, &childstate, 0); + model_size = *shared_buf; + ok = WIFEXITED(childstate) && WEXITSTATUS(childstate) == 0; + if (ok) + { + CV_LOG_INFO(NULL, "Compile success, model size = " << model_size); + out_buffer->data = std::shared_ptr(new uint8_t[model_size]); + memcpy(out_buffer->data.get(), model_data, model_size); + out_buffer->length = model_size; + } + munmap(shared_buf, buf_size + hdrsize); + if (ok) break; + buf_size = model_size; + } + return out_buffer; +} + +void switchToCannBackend(Net& net) +{ + CV_TRACE_FUNCTION(); + Ptr& impl_ptr_ref = accessor::DnnNetAccessor::getImplPtrRef(net); + CV_Assert(impl_ptr_ref); + CV_LOG_INFO(NULL, "DNN: switching to CANN backend... (networkID=" << impl_ptr_ref->networkId << ")"); + Ptr impl_ptr_cann = makePtr(impl_ptr_ref); + impl_ptr_ref = impl_ptr_cann; +} + +#endif // HAVE_CANN + +CV__DNN_INLINE_NS_END +}} // namespace cv::dnn diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index 5411051484..dc0c53191f 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -518,8 +518,8 @@ void Net::Impl::allocateLayer(int lid, const LayersShapesMap& layersShapes) for (int i = 0; i < ld.outputBlobs.size(); ++i) ld.outputBlobsWrappers[i] = wrap(ld.outputBlobs[i]); - /* CUDA backend has its own system for internal blobs; we don't need these */ - ld.internalBlobsWrappers.resize((preferableBackend == DNN_BACKEND_CUDA || preferableBackend == DNN_BACKEND_TIMVX) ? 0 : ld.internals.size()); + /* CUDA & CANN backend has its own system for internal blobs; we don't need these */ + ld.internalBlobsWrappers.resize((preferableBackend == DNN_BACKEND_CUDA || preferableBackend == DNN_BACKEND_TIMVX || preferableBackend == DNN_BACKEND_CANN) ? 0 : ld.internals.size()); for (int i = 0; i < ld.internalBlobsWrappers.size(); ++i) ld.internalBlobsWrappers[i] = wrap(ld.internals[i]); @@ -1566,6 +1566,7 @@ string Net::Impl::dump(bool forceAllocation) const case DNN_BACKEND_CUDA: backend = "CUDA/"; break; case DNN_BACKEND_WEBNN: backend = "WEBNN/"; break; case DNN_BACKEND_TIMVX: backend = "TIMVX/"; break; + case DNN_BACKEND_CANN: backend = "CANN/"; break; // don't use default: } out << "digraph G {\n"; diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index 08ac1932ca..6eb06aa5b7 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -12,6 +12,7 @@ #include "op_cuda.hpp" #include "op_webnn.hpp" #include "op_timvx.hpp" +#include "op_cann.hpp" #include #include diff --git a/modules/dnn/src/net_impl_backend.cpp b/modules/dnn/src/net_impl_backend.cpp index 1d313c70c4..ef816be66d 100644 --- a/modules/dnn/src/net_impl_backend.cpp +++ b/modules/dnn/src/net_impl_backend.cpp @@ -85,6 +85,10 @@ Ptr Net::Impl::wrap(Mat& host) return Ptr(new TimVXBackendWrapper(baseBuffer, host)); #endif } + else if (preferableBackend == DNN_BACKEND_CANN) + { + CV_Assert(0 && "Internal error: DNN_BACKEND_CANN must be implemented through inheritance"); + } else CV_Error(Error::StsNotImplemented, "Unknown backend identifier"); } @@ -146,6 +150,10 @@ void Net::Impl::initBackend(const std::vector& blobsToKeep_) CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of TimVX"); #endif } + else if (preferableBackend == DNN_BACKEND_CANN) + { + CV_Assert(0 && "Internal error: DNN_BACKEND_CANN must be implemented through inheritance"); + } else { CV_Error(Error::StsNotImplemented, cv::format("Unknown backend identifier: %d", preferableBackend)); @@ -179,6 +187,14 @@ void Net::Impl::setPreferableBackend(Net& net, int backendId) networkBackend.switchBackend(net); #else CV_Error(Error::StsNotImplemented, "OpenVINO backend is not available in the current OpenCV build"); +#endif + } + else if (backendId == DNN_BACKEND_CANN) + { +#ifdef HAVE_CANN + switchToCannBackend(net); +#else + CV_Error(Error::StsNotImplemented, "CANN backend is not availlable in the current OpenCV build"); #endif } else diff --git a/modules/dnn/src/op_cann.cpp b/modules/dnn/src/op_cann.cpp new file mode 100644 index 0000000000..6d8a57446b --- /dev/null +++ b/modules/dnn/src/op_cann.cpp @@ -0,0 +1,329 @@ +// 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" +#include "op_cann.hpp" + +#include +#include +#include // memcpy + +#include +#include + +namespace cv { namespace dnn { + +#ifdef HAVE_CANN + +std::shared_ptr AclEnvGuard::global_acl_env_ = nullptr; +std::mutex AclEnvGuard::global_acl_env_mutex_; + +AclEnvGuard::AclEnvGuard() +{ + CV_LOG_INFO(NULL, "Start to initialize CANN"); + ACL_CHECK_RET(aclInit(NULL)); + CV_LOG_INFO(NULL, "[Success] initialized CANN"); +} + +AclEnvGuard::~AclEnvGuard() +{ + CV_LOG_INFO(NULL, "Start to finalize CANN"); + ACL_CHECK_RET(aclFinalize()); + CV_LOG_INFO(NULL, "[Success] finalized CANN"); +} + +std::shared_ptr AclEnvGuard::GetAclEnv() +{ + std::shared_ptr acl_env; + + std::lock_guard lock(global_acl_env_mutex_); + acl_env = global_acl_env_; + if (acl_env != nullptr) + { + CV_LOG_INFO(NULL, "CANN has been initialized. Skipping..."); + } + else + { + acl_env = std::make_shared(); + global_acl_env_ = acl_env; + } + return acl_env; +} + +CannConstOp::CannConstOp(const uint8_t* data, const int dtype, const std::vector& shape, const std::string& name) +{ + std::vector shape_{shape.begin(), shape.end()}; + + auto ge_shape = ge::Shape(shape_); + auto ge_dtype = ge::DT_FLOAT; + switch (dtype) + { + case CV_32F: break; + case CV_32S: ge_dtype = ge::DT_INT32; break; + default: CV_Error(Error::StsNotImplemented, "Unsupported data type"); + } + auto size_of_type = sizeof(float); + switch (dtype) + { + case CV_32F: break; + case CV_32S: size_of_type = sizeof(int); break; + default: CV_Error(Error::StsNotImplemented, "Unsupported data type"); + } + desc_ = std::make_shared(ge_shape, ge::FORMAT_NCHW, ge_dtype); + auto ge_tensor = std::make_shared(); + ge_tensor->SetTensorDesc(*desc_); + ge_tensor->SetData(data, ge_shape.GetShapeSize() * size_of_type); + op_ = std::make_shared(name); + op_->set_attr_value(*ge_tensor); +} + +CannBackendNode::CannBackendNode(const std::shared_ptr& op) + : BackendNode(DNN_BACKEND_CANN), op_(op) { } + +std::shared_ptr CannBackendNode::getOp() { return op_; } + +CannBackendWrapper::CannBackendWrapper(const Mat& m) + : BackendWrapper(DNN_BACKEND_CANN, DNN_TARGET_NPU), host((Mat*)&m) +{ + auto mat_shape = shape(*host); + std::vector shape_{mat_shape.begin(), mat_shape.end()}; + + auto ge_shape = ge::Shape(shape_); + desc_ = std::make_shared(ge_shape, ge::FORMAT_NCHW, ge::DT_FLOAT); +} + +void CannBackendWrapper::copyToHost() +{ + CV_LOG_DEBUG(NULL, "Not implemented"); +} + +void CannBackendWrapper::setHostDirty() +{ + CV_LOG_DEBUG(NULL, "Not implemented"); +} + +CannNet::~CannNet() +{ + CV_LOG_INFO(NULL, "In ~CannNet, inputs = " << inputs << ", outputs = " << outputs); + if (!model_desc) + { + CV_LOG_INFO(NULL, "[Failed] Tried to deconstruct CannNet but model is not loaded"); + return; + } + // free datasets: inputs, outputs + if (inputs) + { + CV_LOG_INFO(NULL, "In ~CannNet: destroy inputs"); + destroyDataset(&inputs); + } + if (outputs) + { + CV_LOG_INFO(NULL, "In ~CannNet: destroy outputs"); + destroyDataset(&outputs); + } + // unload model + ACL_CHECK_RET(aclmdlUnload(model_id)); + // destroy model_desc + ACL_CHECK_RET(aclmdlDestroyDesc(model_desc)); + model_desc = nullptr; + CV_LOG_INFO(NULL, "[Success] Unloaded model (id=" << model_id << ")"); + + // destroy context + if (context != nullptr) + { + ACL_CHECK_RET(aclrtDestroyContext(context)); + context = nullptr; + } + // reset device + if (context == nullptr) + { + ACL_CHECK_RET(aclrtResetDevice(device_id)); + } +} + +bool CannNet::empty() const +{ + return (model_desc == nullptr); +} + +void CannNet::loadModelBuffer(std::shared_ptr modelBuffer) +{ + model.clear(); + model.resize(modelBuffer->length); + std::memcpy(reinterpret_cast(model.data()), + reinterpret_cast(modelBuffer->data.get()), + modelBuffer->length); + loadToDevice(); +} + +void CannNet::bindInputWrappers(const std::vector>& inputWrappers) +{ + CV_Assert(inputWrappers.size() == getInputNum()); + for (size_t i = 0; i < inputWrappers.size(); ++i) + { + auto wrapper = inputWrappers[i].dynamicCast(); + + // verify size + aclmdlIODims model_dims; + ACL_CHECK_RET(aclmdlGetInputDims(model_desc, i, &model_dims)); + CV_CheckEQ((int)model_dims.dimCount, wrapper->host->dims, "Dimension of input does not match with model's requirement"); + for (size_t j = 0; j < model_dims.dimCount; ++j) + CV_CheckEQ((int)model_dims.dims[j], wrapper->host->size[j], "Size of input does not match with model's requirement"); + + input_wrappers.push_back(wrapper); + } +} + +void CannNet::bindOutputWrappers(const std::vector>& outputWrappers) +{ + CV_Assert(outputWrappers.size() == getOutputNum()); + for (int i = 0; i < outputWrappers.size(); ++i) + { + auto wrapper = outputWrappers[i].dynamicCast(); + + // verify size + aclmdlIODims model_dims; + ACL_CHECK_RET(aclmdlGetOutputDims(model_desc, i, &model_dims)); + CV_CheckEQ((int)model_dims.dimCount, wrapper->host->dims, "Dimension of input does not match with model's requirement"); + for (size_t j = 0; j < model_dims.dimCount; ++j) + CV_CheckEQ((int)model_dims.dims[j], wrapper->host->size[j], "Size of input does not match with model's requirement"); + + output_wrappers.push_back(wrapper); + } +} + +void CannNet::forward() +{ + // send inputs from host to device + CV_LOG_DEBUG(NULL, "DNN/CANN: start sending inputs to device"); + for (size_t i = 0; i < input_wrappers.size(); ++i) + { + const void* p_host = (const void*)input_wrappers[i]->host->data; + + auto db = aclmdlGetDatasetBuffer(inputs, i); + auto p_device = aclGetDataBufferAddr(db); + auto db_size = aclGetDataBufferSizeV2(db); + + ACL_CHECK_RET(aclrtMemcpy(p_device, db_size, p_host, db_size, ACL_MEMCPY_HOST_TO_DEVICE)); + } + CV_LOG_DEBUG(NULL, "DNN/CANN: finished sending inputs to device"); + + // forward + CV_LOG_DEBUG(NULL, "DNN/CANN: start network forward"); + ACL_CHECK_RET(aclrtSetCurrentContext(context)); + ACL_CHECK_RET(aclmdlExecute(model_id, inputs, outputs)); + CV_LOG_DEBUG(NULL, "DNN/CANN: finished network forward"); + + // fetch ouputs from device to host + CV_LOG_DEBUG(NULL, "DNN/CANN: start fetching outputs to host"); + for (size_t i = 0; i < output_wrappers.size(); ++i) + { + void* p_host = (void*)output_wrappers[i]->host->data; + + auto db = aclmdlGetDatasetBuffer(outputs, i); + auto p_device = aclGetDataBufferAddr(db); + auto db_size = aclGetDataBufferSizeV2(db); + + ACL_CHECK_RET(aclrtMemcpy(p_host, db_size, p_device, db_size, ACL_MEMCPY_DEVICE_TO_HOST)); + } + CV_LOG_DEBUG(NULL, "DNN/CANN: finish fetching outputs to host"); +} + +size_t CannNet::getInputNum() const +{ + return aclmdlGetNumInputs(model_desc); +} + +size_t CannNet::getOutputNum() const +{ + return aclmdlGetNumOutputs(model_desc); +} + +void CannNet::init() +{ + ACL_CHECK_RET(aclrtSetDevice(device_id)); + ACL_CHECK_RET(aclrtCreateContext(&context, device_id)); +} + +void CannNet::loadToDevice() +{ + if (model_desc != nullptr) + { + CV_LOG_INFO(NULL, "Model has been loaded to device. Skipping ..."); + return; + } + + CV_LOG_INFO(NULL, "Load model to NPU memory"); + ACL_CHECK_RET(aclmdlLoadFromMem(reinterpret_cast(model.data()), model.size(), &model_id)); + + CV_LOG_INFO(NULL, "Create model description"); + model_desc = aclmdlCreateDesc(); + ACL_CHECK_RET(aclmdlGetDesc(model_desc, model_id)); + + createInputDataset(); + createOutputDataset(); +} + +void CannNet::createInputDataset() +{ + inputs = aclmdlCreateDataset(); + size_t n_inputs = aclmdlGetNumInputs(model_desc); + size_t length; + for (size_t i = 0; i < n_inputs; i++) + { + length = aclmdlGetInputSizeByIndex(model_desc, i); + CV_LOG_INFO(NULL, "length = " << length); + void* p_device = nullptr; + ACL_CHECK_RET(aclrtMalloc(&p_device, length, ACL_MEM_MALLOC_NORMAL_ONLY)); + auto p_data_buffer = aclCreateDataBuffer(p_device, length); + ACL_CHECK_RET(aclmdlAddDatasetBuffer(inputs, p_data_buffer)); + } +} + +void CannNet::createOutputDataset() +{ + outputs = aclmdlCreateDataset(); + size_t n_outputs = aclmdlGetNumOutputs(model_desc); + size_t length; + for (size_t i = 0; i < n_outputs; i++) + { + length = aclmdlGetOutputSizeByIndex(model_desc, i); + void* p_device = nullptr; + ACL_CHECK_RET(aclrtMalloc(&p_device, length, ACL_MEM_MALLOC_NORMAL_ONLY)); + auto p_data_buffer = aclCreateDataBuffer(p_device, length); + ACL_CHECK_RET(aclmdlAddDatasetBuffer(outputs, p_data_buffer)); + } +} + +void CannNet::destroyDataset(aclmdlDataset** dataset) +{ + if (!dataset) + { + CV_LOG_INFO(NULL, "CANN dataset is not initialized"); + return; + } + auto buffer_count = aclmdlGetDatasetNumBuffers(*dataset); + CV_LOG_INFO(NULL, "buffer_count = " << buffer_count); + for (auto i = 0; i < buffer_count; i++) + { + auto data_buffer = aclmdlGetDatasetBuffer(*dataset, i); + auto p_device = aclGetDataBufferAddr(data_buffer); + if (p_device) + { + ACL_CHECK_RET(aclrtFree(p_device)); // 107000? + } + else + { + CV_LOG_INFO(NULL, "Data buffer (i=" << i << ") from ACL dataset is invalid"); + } + ACL_CHECK_RET(aclDestroyDataBuffer(data_buffer)); + } + ACL_CHECK_RET(aclmdlDestroyDataset(*dataset)); + *dataset = nullptr; + CV_LOG_INFO(NULL, "[Success] Destroyed dataset"); +} + +#endif // HAVE_CANN + +}} // namespace cv::dnn diff --git a/modules/dnn/src/op_cann.hpp b/modules/dnn/src/op_cann.hpp new file mode 100644 index 0000000000..2237dd4855 --- /dev/null +++ b/modules/dnn/src/op_cann.hpp @@ -0,0 +1,164 @@ +// 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_DNN_OP_CANN_HPP +#define OPENCV_DNN_OP_CANN_HPP + +#ifdef HAVE_CANN +#include "acl/acl.h" // acl* functions +#include "graph/graph.h" // ge::Graph; ge::Operator from operator.h +#include "graph/ge_error_codes.h" // GRAPH_SUCCESS, ... + +#include "op_proto/built-in/inc/all_ops.h" // ge::Conv2D, ... +#include "graph/tensor.h" // ge::Shape, ge::Tensor, ge::TensorDesc +#include "graph/types.h" // DT_FLOAT, ... ; FORMAT_NCHW, ... + +#include "ge/ge_api_types.h" // ge::ir_option::SOC_VERSION +#include "ge/ge_ir_build.h" // build graph + +// for fork() +#include +#include +#include +#include +#include + +#endif // HAVE_CANN + +#include + +#ifdef HAVE_CANN +#define ACL_CHECK_RET(f) \ +{ \ + if (f != ACL_SUCCESS) \ + { \ + CV_LOG_ERROR(NULL, "CANN check failed, ret = " << f); \ + CV_Error(Error::StsError, "CANN check failed"); \ + } \ +} +#define ACL_CHECK_GRAPH_RET(f) \ +{ \ + if (f != ge::GRAPH_SUCCESS) \ + { \ + CV_LOG_ERROR(NULL, "CANN graph check failed, ret = " << f); \ + CV_Error(Error::StsError, "CANN graph check failed"); \ + } \ +} + +#endif + +namespace cv { namespace dnn { + +#ifdef HAVE_CANN + +CV__DNN_INLINE_NS_BEGIN + +void switchToCannBackend(Net& net); + +CV__DNN_INLINE_NS_END + + class CannNet; + + class AclEnvGuard { + public: + explicit AclEnvGuard(); + ~AclEnvGuard(); + static std::shared_ptr GetAclEnv(); + + private: + static std::shared_ptr global_acl_env_; + static std::mutex global_acl_env_mutex_; + }; + + class CannConstOp + { + public: + CannConstOp(const uint8_t* data, const int dtype, const std::vector& shape, const std::string& name); + std::shared_ptr getOp() { return op_; } + std::shared_ptr getTensorDesc() { return desc_; } + private: + std::shared_ptr op_; + std::shared_ptr desc_; + }; + + class CannBackendNode : public BackendNode + { + public: + CannBackendNode(const std::shared_ptr& op); + std::shared_ptr getOp(); + std::shared_ptr net; + private: + std::shared_ptr op_; + }; + + class CannBackendWrapper : public BackendWrapper + { + public: + CannBackendWrapper(const Mat& m); + ~CannBackendWrapper() { } + + std::shared_ptr getTensorDesc() { return desc_; } + + virtual void copyToHost() CV_OVERRIDE; + + virtual void setHostDirty() CV_OVERRIDE; + + Mat* host; + std::shared_ptr desc_; + }; + + class CannNet + { + public: + explicit CannNet(int deviceId = 0) + : device_id(deviceId) + { + init(); + acl_env = AclEnvGuard::GetAclEnv(); + } + ~CannNet(); // release private members + + bool empty() const; + + void loadModelBuffer(std::shared_ptr modelBuffer); + + void bindInputWrappers(const std::vector>& inputWrappers); + void bindOutputWrappers(const std::vector>& outputWrappers); + + void forward(); + + size_t getInputNum() const; + size_t getOutputNum() const; + + private: + void init(); + + void loadToDevice(); // call aclInit before this API is called + void createInputDataset(); + void createOutputDataset(); + + int getOutputIndexByName(const std::string& name); + + void destroyDataset(aclmdlDataset** dataset); + + std::shared_ptr acl_env; + + std::vector> input_wrappers; + std::vector> output_wrappers; + + uint32_t model_id{0}; + aclmdlDesc* model_desc{nullptr}; + std::vector model; + aclmdlDataset* inputs{nullptr}; + aclmdlDataset* outputs{nullptr}; + + int device_id{0}; + aclrtContext context{nullptr}; + }; + +#endif // HAVE_CANN + +}} // namespace cv::dnn + +#endif // OPENCV_DNN_OP_CANN_HPP diff --git a/modules/dnn/src/registry.cpp b/modules/dnn/src/registry.cpp index 56b96f4c4c..f5c9e584c6 100644 --- a/modules/dnn/src/registry.cpp +++ b/modules/dnn/src/registry.cpp @@ -11,6 +11,7 @@ #include "op_cuda.hpp" #include "op_webnn.hpp" #include "op_timvx.hpp" +#include "op_cann.hpp" #include "halide_scheduler.hpp" @@ -122,6 +123,10 @@ private: backends.push_back(std::make_pair(DNN_BACKEND_TIMVX, DNN_TARGET_NPU)); } #endif + +#ifdef HAVE_CANN + backends.push_back(std::make_pair(DNN_BACKEND_CANN, DNN_TARGET_NPU)); +#endif } BackendsList backends; diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index e36374bd98..df93e50c91 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -49,6 +49,7 @@ #define CV_TEST_TAG_DNN_SKIP_PARSER "dnn_skip_parser" #define CV_TEST_TAG_DNN_SKIP_TIMVX "dnn_skip_timvx" +#define CV_TEST_TAG_DNN_SKIP_CANN "dnn_skip_cann" #ifdef HAVE_INF_ENGINE #if INF_ENGINE_VER_MAJOR_EQ(2018050000) @@ -139,7 +140,8 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget bool withVkCom = true, bool withCUDA = true, bool withNgraph = true, - bool withWebnn = true + bool withWebnn = true, + bool withCann = true ); testing::internal::ParamGenerator< tuple > dnnBackendsAndTargetsIE(); diff --git a/modules/dnn/test/test_common.impl.hpp b/modules/dnn/test/test_common.impl.hpp index 5fdf6c3d1e..bad6a8d082 100644 --- a/modules/dnn/test/test_common.impl.hpp +++ b/modules/dnn/test/test_common.impl.hpp @@ -31,6 +31,7 @@ void PrintTo(const cv::dnn::Backend& v, std::ostream* os) case DNN_BACKEND_INFERENCE_ENGINE_NGRAPH: *os << "NGRAPH"; return; case DNN_BACKEND_WEBNN: *os << "WEBNN"; return; case DNN_BACKEND_TIMVX: *os << "TIMVX"; return; + case DNN_BACKEND_CANN: *os << "CANN"; return; } // don't use "default:" to emit compiler warnings *os << "DNN_BACKEND_UNKNOWN(" << (int)v << ")"; } @@ -251,7 +252,8 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget bool withVkCom /*= true*/, bool withCUDA /*= true*/, bool withNgraph /*= true*/, - bool withWebnn /*= false*/ + bool withWebnn /*= false*/, + bool withCann /*= true*/ ) { bool withVPU = validateVPUType(); @@ -311,6 +313,16 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget CV_UNUSED(withWebnn); #endif +#ifdef HAVE_CANN + if (withCann) + { + for (auto target : getAvailableTargets(DNN_BACKEND_CANN)) + targets.push_back(make_tuple(DNN_BACKEND_CANN, target)); + } +#else + CV_UNUSED(withCann); +#endif // HAVE_CANN + { available = getAvailableTargets(DNN_BACKEND_OPENCV); for (std::vector< Target >::const_iterator i = available.begin(); i != available.end(); ++i) @@ -477,6 +489,11 @@ void initDNNTests() registerGlobalSkipTag( CV_TEST_TAG_DNN_SKIP_TIMVX ); +#endif +#ifdef HAVE_CANN + registerGlobalSkipTag( + CV_TEST_TAG_DNN_SKIP_CANN + ); #endif registerGlobalSkipTag( CV_TEST_TAG_DNN_SKIP_ONNX_CONFORMANCE, From 3cfe7375816b5a22191033f75fa06cfb42a4e41c Mon Sep 17 00:00:00 2001 From: Sergei Shutov Date: Thu, 15 Dec 2022 14:00:48 +0200 Subject: [PATCH 277/313] Fix hardcoded maxIters --- modules/calib3d/src/five-point.cpp | 4 +- modules/calib3d/src/usac.hpp | 3 +- modules/calib3d/src/usac/ransac_solvers.cpp | 4 +- modules/calib3d/test/test_usac.cpp | 69 ++++++++++++++++----- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/modules/calib3d/src/five-point.cpp b/modules/calib3d/src/five-point.cpp index 735f8e6b85..9b82dcb11c 100644 --- a/modules/calib3d/src/five-point.cpp +++ b/modules/calib3d/src/five-point.cpp @@ -433,9 +433,9 @@ cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, InputArr { CV_INSTRUMENT_REGION(); - if (method >= 32 && method <= 38) + if (method >= USAC_DEFAULT && method <= USAC_MAGSAC) return usac::findEssentialMat(_points1, _points2, _cameraMatrix, - method, prob, threshold, _mask); + method, prob, threshold, _mask, maxIters); Mat points1, points2, cameraMatrix; _points1.getMat().convertTo(points1, CV_64F); diff --git a/modules/calib3d/src/usac.hpp b/modules/calib3d/src/usac.hpp index 8daf4f52a9..2a3e0eb7fc 100644 --- a/modules/calib3d/src/usac.hpp +++ b/modules/calib3d/src/usac.hpp @@ -801,7 +801,8 @@ bool solvePnPRansac( InputArray objectPoints, InputArray imagePoints, Mat findEssentialMat( InputArray points1, InputArray points2, InputArray cameraMatrix1, int method, double prob, - double threshold, OutputArray mask); + double threshold, OutputArray mask, + int maxIters); Mat estimateAffine2D(InputArray from, InputArray to, OutputArray inliers, int method, double ransacReprojThreshold, int maxIters, diff --git a/modules/calib3d/src/usac/ransac_solvers.cpp b/modules/calib3d/src/usac/ransac_solvers.cpp index fe64907ec0..cca13405bc 100644 --- a/modules/calib3d/src/usac/ransac_solvers.cpp +++ b/modules/calib3d/src/usac/ransac_solvers.cpp @@ -520,9 +520,9 @@ Mat findFundamentalMat( InputArray points1, InputArray points2, int method, doub } Mat findEssentialMat (InputArray points1, InputArray points2, InputArray cameraMatrix1, - int method, double prob, double thr, OutputArray mask) { + int method, double prob, double thr, OutputArray mask, int maxIters) { Ptr params; - setParameters(method, params, EstimationMethod::Essential, thr, 1000, prob, mask.needed()); + setParameters(method, params, EstimationMethod::Essential, thr, maxIters, prob, mask.needed()); Ptr ransac_output; if (run(params, points1, points2, params->getRandomGeneratorState(), ransac_output, cameraMatrix1, cameraMatrix1, noArray(), noArray())) { diff --git a/modules/calib3d/test/test_usac.cpp b/modules/calib3d/test/test_usac.cpp index 8297ab1de8..fc36e8c945 100644 --- a/modules/calib3d/test/test_usac.cpp +++ b/modules/calib3d/test/test_usac.cpp @@ -299,8 +299,11 @@ TEST(usac_Fundamental, regression_19639) EXPECT_TRUE(m.empty()); } +CV_ENUM(UsacMethod, USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC) +typedef TestWithParam usac_Essential; -TEST(usac_Essential, accuracy) { +TEST_P(usac_Essential, accuracy) { + int method = GetParam(); std::vector gt_inliers; const int pts_size = 1500; cv::RNG &rng = cv::theRNG(); @@ -312,26 +315,58 @@ TEST(usac_Essential, accuracy) { int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/, pts_size, TestSolver ::Fundam, inl_ratio, 0.01 /*noise std, works bad with high noise*/, gt_inliers); const double conf = 0.99, thr = 1.; - for (auto flag : flags) { - cv::Mat mask, E; - try { - E = cv::findEssentialMat(pts1, pts2, K1, flag, conf, thr, mask); - } catch (cv::Exception &e) { - if (e.code != cv::Error::StsNotImplemented) - FAIL() << "Essential matrix estimation failed!\n"; - else continue; - } - // calibrate points - cv::Mat cpts1_3d, cpts2_3d; - cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d); - cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d); - cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K1.inv() * cpts2_3d; - checkInliersMask(TestSolver::Essen, inl_size, thr / ((K1.at(0,0) + K1.at(1,1)) / 2), - cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), E, mask); + cv::Mat mask, E; + try { + E = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, mask); + } catch (cv::Exception &e) { + if (e.code != cv::Error::StsNotImplemented) + FAIL() << "Essential matrix estimation failed!\n"; + else continue; } + // calibrate points + cv::Mat cpts1_3d, cpts2_3d; + cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d); + cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d); + cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K1.inv() * cpts2_3d; + checkInliersMask(TestSolver::Essen, inl_size, thr / ((K1.at(0,0) + K1.at(1,1)) / 2), + cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), E, mask); } } +TEST_P(usac_Essential, maxiters) { + int method = GetParam(); + cv::RNG &rng = cv::theRNG(); + cv::Mat mask; + cv::Mat K1 = cv::Mat(cv::Matx33d(1, 0, 0, + 0, 1, 0, + 0, 0, 1.)); + const double conf = 0.99, thr = 0.5; + int roll_results_sum = 0; + + for (int iters = 0; iters < 10; iters++) { + cv::Mat E1, E2; + try { + cv::Mat pts1 = cv::Mat(2, 50, CV_64F); + cv::Mat pts2 = cv::Mat(2, 50, CV_64F); + rng.fill(pts1, cv::RNG::UNIFORM, 0.0, 1.0); + rng.fill(pts2, cv::RNG::UNIFORM, 0.0, 1.0); + + E1 = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, 1, mask); + E2 = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, 1000, mask); + + if (E1.dims != E2.dims) { continue; } + roll_results_sum += cv::norm(E1, E2, NORM_L1) != 0; + } catch (cv::Exception &e) { + if (e.code != cv::Error::StsNotImplemented) + FAIL() << "Essential matrix estimation failed!\n"; + else continue; + } + EXPECT_NE(roll_results_sum, 0); + } +} + +INSTANTIATE_TEST_CASE_P(Calib3d, usac_Essential, UsacMethod::all()); + TEST(usac_P3P, accuracy) { std::vector gt_inliers; const int pts_size = 3000; From 05f4416ba0de408378c58fea4b302226686dd0a6 Mon Sep 17 00:00:00 2001 From: Eran Geva Date: Wed, 21 Dec 2022 12:26:07 +0200 Subject: [PATCH 278/313] SourceForge folder update The folder `http://sourceforge.net/projects/opencvlibrary/files/opencv-win/` is stale and wasn't updated since 2018. The parent folder `http://sourceforge.net/projects/opencvlibrary/files/` has fresh windows install exe files. --- .../introduction/windows_install/windows_install.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/introduction/windows_install/windows_install.markdown b/doc/tutorials/introduction/windows_install/windows_install.markdown index 57a3c785c0..eabf31482f 100644 --- a/doc/tutorials/introduction/windows_install/windows_install.markdown +++ b/doc/tutorials/introduction/windows_install/windows_install.markdown @@ -26,7 +26,7 @@ Installation by Using the Pre-built Libraries {#tutorial_windows_install_prebuil ============================================= -# Launch a web browser of choice and go to our [page on - Sourceforge](http://sourceforge.net/projects/opencvlibrary/files/opencv-win/). + Sourceforge](http://sourceforge.net/projects/opencvlibrary/files/). -# Choose a build you want to use and download it. -# Make sure you have admin rights. Unpack the self-extracting archive. -# You can check the installation at the chosen path as you can see below. From c6a15e1835b4dc0507bc9c3585b07c8c908cdbb4 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 21 Dec 2022 10:48:06 +0000 Subject: [PATCH 279/313] aruco(cleanup): don't use Ptr --- .../opencv2/objdetect/aruco_detector.hpp | 14 +- .../opencv2/objdetect/aruco_dictionary.hpp | 6 +- .../objdetect/src/aruco/aruco_detector.cpp | 132 ++++++++---------- .../objdetect/src/aruco/aruco_dictionary.cpp | 17 +-- modules/objdetect/src/aruco/aruco_utils.hpp | 10 +- 5 files changed, 80 insertions(+), 99 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp index 69c7defd1c..80f84339cd 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp @@ -85,11 +85,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters { /** @brief Write a set of DetectorParameters to FileStorage */ - bool writeDetectorParameters(FileStorage& fs); - - /** @brief simplified API for language bindings - */ - CV_WRAP bool writeDetectorParameters(const Ptr& fs, const String& name = String()); + CV_WRAP bool writeDetectorParameters(FileStorage& fs, const String& name = String()); /// minimum window size for adaptive thresholding before finding contours (default 3). CV_PROP_RW int adaptiveThreshWinSizeMin; @@ -237,11 +233,7 @@ struct CV_EXPORTS_W_SIMPLE RefineParameters { /** @brief Write a set of RefineParameters to FileStorage */ - bool writeRefineParameters(FileStorage& fs); - - /** @brief simplified API for language bindings - */ - CV_WRAP bool writeRefineParameters(const Ptr& fs, const String& name = String()); + CV_WRAP bool writeRefineParameters(FileStorage& fs, const String& name = String()); /** @brief minRepDistance minimum distance between the corners of the rejected candidate and the reprojected marker in order to consider it as a correspondence. @@ -347,7 +339,7 @@ public: /** @brief simplified API for language bindings */ - CV_WRAP inline void write(const Ptr& fs, const String& name = String()) { Algorithm::write(fs, name); } + CV_WRAP inline void write(FileStorage& fs, const String& name) { Algorithm::write(fs, name); } /** @brief Reads algorithm parameters from a file storage */ diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp index e306c65fe8..498bcf1c33 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp @@ -50,11 +50,7 @@ class CV_EXPORTS_W_SIMPLE Dictionary { /** @brief Write a dictionary to FileStorage, format is the same as in readDictionary(). */ - void writeDictionary(FileStorage& fs); - - /** @brief simplified API for language bindings - */ - CV_WRAP void writeDictionary(Ptr& fs, const String& name = String()); + CV_WRAP void writeDictionary(FileStorage& fs, const String& name = String()); /** @brief Given a matrix of bits. Returns whether if marker is identified or not. * diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index c23545c6ad..4ef4710b01 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -16,77 +16,73 @@ namespace aruco { using namespace std; -static inline bool readWrite(DetectorParameters ¶ms, const Ptr& readNode, - const Ptr& writeStorage = nullptr) { - CV_Assert(!readNode.empty() || !writeStorage.empty()); +static inline bool readWrite(DetectorParameters ¶ms, const FileNode* readNode, + FileStorage* writeStorage = nullptr) +{ + CV_Assert(readNode || writeStorage); bool check = false; - check |= readWriteParameter("adaptiveThreshWinSizeMin", params.adaptiveThreshWinSizeMin, *readNode, *writeStorage); - check |= readWriteParameter("adaptiveThreshWinSizeMax", params.adaptiveThreshWinSizeMax, *readNode, *writeStorage); - check |= readWriteParameter("adaptiveThreshWinSizeStep", params.adaptiveThreshWinSizeStep, *readNode, *writeStorage); - check |= readWriteParameter("adaptiveThreshConstant", params.adaptiveThreshConstant, *readNode, *writeStorage); - check |= readWriteParameter("minMarkerPerimeterRate", params.minMarkerPerimeterRate, *readNode, *writeStorage); - check |= readWriteParameter("maxMarkerPerimeterRate", params.maxMarkerPerimeterRate, *readNode, *writeStorage); + check |= readWriteParameter("adaptiveThreshWinSizeMin", params.adaptiveThreshWinSizeMin, readNode, writeStorage); + check |= readWriteParameter("adaptiveThreshWinSizeMax", params.adaptiveThreshWinSizeMax, readNode, writeStorage); + check |= readWriteParameter("adaptiveThreshWinSizeStep", params.adaptiveThreshWinSizeStep, readNode, writeStorage); + check |= readWriteParameter("adaptiveThreshConstant", params.adaptiveThreshConstant, readNode, writeStorage); + check |= readWriteParameter("minMarkerPerimeterRate", params.minMarkerPerimeterRate, readNode, writeStorage); + check |= readWriteParameter("maxMarkerPerimeterRate", params.maxMarkerPerimeterRate, readNode, writeStorage); check |= readWriteParameter("polygonalApproxAccuracyRate", params.polygonalApproxAccuracyRate, - *readNode, *writeStorage); - check |= readWriteParameter("minCornerDistanceRate", params.minCornerDistanceRate, *readNode, *writeStorage); - check |= readWriteParameter("minDistanceToBorder", params.minDistanceToBorder, *readNode, *writeStorage); - check |= readWriteParameter("minMarkerDistanceRate", params.minMarkerDistanceRate, *readNode, *writeStorage); - check |= readWriteParameter("cornerRefinementMethod", params.cornerRefinementMethod, *readNode, *writeStorage); - check |= readWriteParameter("cornerRefinementWinSize", params.cornerRefinementWinSize, *readNode, *writeStorage); + readNode, writeStorage); + check |= readWriteParameter("minCornerDistanceRate", params.minCornerDistanceRate, readNode, writeStorage); + check |= readWriteParameter("minDistanceToBorder", params.minDistanceToBorder, readNode, writeStorage); + check |= readWriteParameter("minMarkerDistanceRate", params.minMarkerDistanceRate, readNode, writeStorage); + check |= readWriteParameter("cornerRefinementMethod", params.cornerRefinementMethod, readNode, writeStorage); + check |= readWriteParameter("cornerRefinementWinSize", params.cornerRefinementWinSize, readNode, writeStorage); check |= readWriteParameter("cornerRefinementMaxIterations", params.cornerRefinementMaxIterations, - *readNode, *writeStorage); + readNode, writeStorage); check |= readWriteParameter("cornerRefinementMinAccuracy", params.cornerRefinementMinAccuracy, - *readNode, *writeStorage); - check |= readWriteParameter("markerBorderBits", params.markerBorderBits, *readNode, *writeStorage); + readNode, writeStorage); + check |= readWriteParameter("markerBorderBits", params.markerBorderBits, readNode, writeStorage); check |= readWriteParameter("perspectiveRemovePixelPerCell", params.perspectiveRemovePixelPerCell, - *readNode, *writeStorage); + readNode, writeStorage); check |= readWriteParameter("perspectiveRemoveIgnoredMarginPerCell", params.perspectiveRemoveIgnoredMarginPerCell, - *readNode, *writeStorage); + readNode, writeStorage); check |= readWriteParameter("maxErroneousBitsInBorderRate", params.maxErroneousBitsInBorderRate, - *readNode, *writeStorage); - check |= readWriteParameter("minOtsuStdDev", params.minOtsuStdDev, *readNode, *writeStorage); - check |= readWriteParameter("errorCorrectionRate", params.errorCorrectionRate, *readNode, *writeStorage); + readNode, writeStorage); + check |= readWriteParameter("minOtsuStdDev", params.minOtsuStdDev, readNode, writeStorage); + check |= readWriteParameter("errorCorrectionRate", params.errorCorrectionRate, readNode, writeStorage); // new aruco 3 functionality - check |= readWriteParameter("useAruco3Detection", params.useAruco3Detection, *readNode, *writeStorage); - check |= readWriteParameter("minSideLengthCanonicalImg", params.minSideLengthCanonicalImg, *readNode, *writeStorage); + check |= readWriteParameter("useAruco3Detection", params.useAruco3Detection, readNode, writeStorage); + check |= readWriteParameter("minSideLengthCanonicalImg", params.minSideLengthCanonicalImg, readNode, writeStorage); check |= readWriteParameter("minMarkerLengthRatioOriginalImg", params.minMarkerLengthRatioOriginalImg, - *readNode, *writeStorage); + readNode, writeStorage); return check; } -bool DetectorParameters::readDetectorParameters(const FileNode& fn) { - if(fn.empty()) +bool DetectorParameters::readDetectorParameters(const FileNode& fn) +{ + if (fn.empty()) return false; - Ptr pfn = makePtr(fn); - return readWrite(*this, pfn); + return readWrite(*this, &fn); } -bool DetectorParameters::writeDetectorParameters(const Ptr& fs, const String& name) { - if (fs.empty()) - return false; - if(name.empty()) - return writeDetectorParameters(*fs); - *fs << name << "{"; - bool res = writeDetectorParameters(*fs); - *fs << "}"; +bool DetectorParameters::writeDetectorParameters(FileStorage& fs, const String& name) +{ + CV_Assert(fs.isOpened()); + if (!name.empty()) + fs << name << "{"; + bool res = readWrite(*this, nullptr, &fs); + if (!name.empty()) + fs << "}"; return res; } -bool DetectorParameters::writeDetectorParameters(FileStorage &fs) { - if (!fs.isOpened()) - return false; - return readWrite(*this, nullptr, makePtr(fs)); -} - -static inline bool readWrite(RefineParameters& refineParameters, const Ptr& readNode, - const Ptr& writeStorage = nullptr) { - CV_Assert(!readNode.empty() || !writeStorage.empty()); +static inline bool readWrite(RefineParameters& refineParameters, const FileNode* readNode, + FileStorage* writeStorage = nullptr) +{ + CV_Assert(readNode || writeStorage); bool check = false; - check |= readWriteParameter("minRepDistance", refineParameters.minRepDistance, *readNode, *writeStorage); - check |= readWriteParameter("errorCorrectionRate", refineParameters.errorCorrectionRate, *readNode, *writeStorage); - check |= readWriteParameter("checkAllOrders", refineParameters.checkAllOrders, *readNode, *writeStorage); + check |= readWriteParameter("minRepDistance", refineParameters.minRepDistance, readNode, writeStorage); + check |= readWriteParameter("errorCorrectionRate", refineParameters.errorCorrectionRate, readNode, writeStorage); + check |= readWriteParameter("checkAllOrders", refineParameters.checkAllOrders, readNode, writeStorage); return check; } @@ -94,27 +90,21 @@ RefineParameters::RefineParameters(float _minRepDistance, float _errorCorrection minRepDistance(_minRepDistance), errorCorrectionRate(_errorCorrectionRate), checkAllOrders(_checkAllOrders){} -bool RefineParameters::readRefineParameters(const FileNode &fn) { - if(fn.empty()) +bool RefineParameters::readRefineParameters(const FileNode &fn) +{ + if (fn.empty()) return false; - Ptr pfn = makePtr(fn); - return readWrite(*this, pfn); + return readWrite(*this, &fn); } -bool RefineParameters::writeRefineParameters(FileStorage &fs) { - if(!fs.isOpened()) - return false; - return readWrite(*this, nullptr, makePtr(fs)); -} - -bool RefineParameters::writeRefineParameters(const Ptr& fs, const String& name) { - if(fs.empty()) - return false; - if(name.empty()) - return writeRefineParameters(*fs); - *fs << name << "{"; - bool res = writeRefineParameters(*fs); - *fs << "}"; +bool RefineParameters::writeRefineParameters(FileStorage& fs, const String& name) +{ + CV_Assert(fs.isOpened()); + if (!name.empty()) + fs << name << "{"; + bool res = readWrite(*this, nullptr, &fs); + if (!name.empty()) + fs << "}"; return res; } @@ -1279,9 +1269,9 @@ void ArucoDetector::refineDetectedMarkers(InputArray _image, const Ptr &_ } } -void ArucoDetector::write(FileStorage &fs) const { - Ptr pfs = makePtr(fs); - arucoDetectorImpl->dictionary.writeDictionary(pfs); +void ArucoDetector::write(FileStorage &fs) const +{ + arucoDetectorImpl->dictionary.writeDictionary(fs); arucoDetectorImpl->detectorParams.writeDetectorParameters(fs); arucoDetectorImpl->refineParams.writeRefineParameters(fs); } diff --git a/modules/objdetect/src/aruco/aruco_dictionary.cpp b/modules/objdetect/src/aruco/aruco_dictionary.cpp index a0177acfee..79eac9a649 100644 --- a/modules/objdetect/src/aruco/aruco_dictionary.cpp +++ b/modules/objdetect/src/aruco/aruco_dictionary.cpp @@ -46,7 +46,13 @@ bool Dictionary::readDictionary(const cv::FileNode& fn) { return true; } -void Dictionary::writeDictionary(FileStorage &fs) { +void Dictionary::writeDictionary(FileStorage& fs, const String &name) +{ + CV_Assert(fs.isOpened()); + + if (!name.empty()) + fs << name << "{"; + fs << "nmarkers" << bytesList.rows; fs << "markersize" << markerSize; fs << "maxCorrectionBits" << maxCorrectionBits; @@ -61,14 +67,9 @@ void Dictionary::writeDictionary(FileStorage &fs) { marker.push_back(bitMarker.at(j) + '0'); fs << markerName << marker; } -} -void Dictionary::writeDictionary(Ptr& fs, const String &name) { - if(name.empty()) - return writeDictionary(*fs); - *fs << name << "{"; - writeDictionary(*fs); - *fs << "}"; + if (!name.empty()) + fs << "}"; } diff --git a/modules/objdetect/src/aruco/aruco_utils.hpp b/modules/objdetect/src/aruco/aruco_utils.hpp index e3aa62051a..d7d29a7d18 100644 --- a/modules/objdetect/src/aruco/aruco_utils.hpp +++ b/modules/objdetect/src/aruco/aruco_utils.hpp @@ -31,10 +31,12 @@ inline bool readParameter(const std::string& name, T& parameter, const FileNode& } template -inline bool readWriteParameter(const std::string& name, T& parameter, const FileNode& readNode, FileStorage& writeStorage) { - if (!readNode.empty()) - return readParameter(name, parameter, readNode); - writeStorage << name << parameter; +inline bool readWriteParameter(const std::string& name, T& parameter, const FileNode* readNode, FileStorage* writeStorage) +{ + if (readNode) + return readParameter(name, parameter, *readNode); + CV_Assert(writeStorage); + *writeStorage << name << parameter; return true; } From b8f57c9a967b0b34861cd3e41c5d650dcdddb02f Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 21 Dec 2022 15:45:43 +0300 Subject: [PATCH 280/313] Update Javascript bindings for Aruco after migration --- platforms/js/opencv_js.config.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/platforms/js/opencv_js.config.py b/platforms/js/opencv_js.config.py index 20380fc970..a7f3e96063 100644 --- a/platforms/js/opencv_js.config.py +++ b/platforms/js/opencv_js.config.py @@ -111,7 +111,11 @@ imgproc = { objdetect = {'': ['groupRectangles'], 'HOGDescriptor': ['load', 'HOGDescriptor', 'getDefaultPeopleDetector', 'getDaimlerPeopleDetector', 'setSVMDetector', 'detectMultiScale'], 'CascadeClassifier': ['load', 'detectMultiScale2', 'CascadeClassifier', 'detectMultiScale3', 'empty', 'detectMultiScale'], - 'QRCodeDetector': ['QRCodeDetector', 'decode', 'decodeCurved', 'detect', 'detectAndDecode', 'detectMulti', 'setEpsX', 'setEpsY']} + 'QRCodeDetector': ['QRCodeDetector', 'decode', 'decodeCurved', 'detect', 'detectAndDecode', 'detectMulti', 'setEpsX', 'setEpsY'], + 'ArucoDetector': ['getPredefinedDictionary', 'detectMarkers', 'refineDetectedMarkers', 'getDictionary', 'stetDictionary', 'getDetectorParameters', 'setDetectorParameters', 'getRefineParameters', 'setRefineParameters'], + 'GridBoard': ['create','generateImage', 'getGridSize', 'getMarkerLength', 'getMarkerSeparation'], + 'CharucoBoard': ['create', 'generateImage', 'getChessboardCorners', 'getNearestMarkerCorners', 'checkCharucoCornersCollinear'] +} video = { '': [ @@ -168,14 +172,6 @@ photo = {'': ['createAlignMTB', 'createCalibrateDebevec', 'createCalibrateRobert 'getColorAdaptation', 'setColorAdaptation'] } -aruco = {'': ['detectMarkers', 'drawDetectedMarkers', 'drawAxis', 'estimatePoseSingleMarkers', 'estimatePoseBoard', 'estimatePoseCharucoBoard', 'interpolateCornersCharuco', 'drawDetectedCornersCharuco'], - 'aruco_Dictionary': ['get', 'drawMarker'], - 'aruco_Board': ['create'], - 'aruco_GridBoard': ['create', 'draw'], - 'aruco_CharucoBoard': ['create', 'draw'], - 'aruco_DetectorParameters': ['create'] - } - calib3d = { '': [ 'findHomography', @@ -197,7 +193,7 @@ calib3d = { ], } -white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, photo, aruco, calib3d]) +white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, photo, calib3d]) # namespace_prefix_override['dnn'] = '' # compatibility stuff (enabled by default) # namespace_prefix_override['aruco'] = '' # compatibility stuff (enabled by default) From 8e495607097257832488bc41ab0ba79a18d4a8e3 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 21 Dec 2022 01:33:05 +0300 Subject: [PATCH 281/313] valgrind: suppressed FFV1 16 bit issue in avcodec-57 --- platforms/scripts/valgrind_3rdparty.supp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platforms/scripts/valgrind_3rdparty.supp b/platforms/scripts/valgrind_3rdparty.supp index 8ca0afbecf..9e9e8e5895 100644 --- a/platforms/scripts/valgrind_3rdparty.supp +++ b/platforms/scripts/valgrind_3rdparty.supp @@ -215,3 +215,16 @@ fun:start_thread fun:clone } + +{ + avcodec57-ffv1-16bit-ubuntu18 + Memcheck:Cond + ... + obj:/usr/lib/x86_64-linux-gnu/libavcodec.so.57.107.100 + fun:avcodec_default_execute + obj:/usr/lib/x86_64-linux-gnu/libavcodec.so.57.107.100 + fun:avcodec_encode_video2 + obj:/usr/lib/x86_64-linux-gnu/libavcodec.so.57.107.100 + fun:avcodec_send_frame + ... +} From 0bd54a60e976e865e6572bfa34b7e2ba3e36686f Mon Sep 17 00:00:00 2001 From: augustinmanecy Date: Wed, 21 Dec 2022 14:03:00 +0100 Subject: [PATCH 282/313] Merge pull request #20367 from augustinmanecy:features2d-rw **Merge with contrib**: https://github.com/opencv/opencv_contrib/pull/3003 ### 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 other license that is incompatible with OpenCV - [x] The PR is proposed to proper branch - [ ] There is reference to original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../features2d/include/opencv2/features2d.hpp | 55 +++++- .../java/test/AGASTFeatureDetectorTest.java | 85 +++++++++ .../test/AKAZEDescriptorExtractorTest.java | 67 +++++++ .../test/BRIEFDescriptorExtractorTest.java | 4 +- .../test/BRISKDescriptorExtractorTest.java | 63 +++++++ .../java/test/FASTFeatureDetectorTest.java | 41 ++-- .../misc/java/test/Features2dTest.java | 4 +- .../java/test/GFTTFeatureDetectorTest.java | 38 +++- .../test/KAZEDescriptorExtractorTest.java | 66 +++++++ .../java/test/MSERFeatureDetectorTest.java | 41 +++- .../java/test/ORBDescriptorExtractorTest.java | 35 ++-- .../test/SIFTDescriptorExtractorTest.java | 33 ++-- .../test/SIMPLEBLOBFeatureDetectorTest.java | 34 +++- .../java/test/STARFeatureDetectorTest.java | 133 ------------- .../test/SURFDescriptorExtractorTest.java | 119 ------------ .../java/test/SURFFeatureDetectorTest.java | 175 ------------------ modules/features2d/misc/objc/gen_dict.json | 6 + modules/features2d/src/agast.cpp | 21 +++ modules/features2d/src/akaze.cpp | 23 ++- modules/features2d/src/blobdetector.cpp | 37 +++- modules/features2d/src/brisk.cpp | 88 ++++++--- modules/features2d/src/fast.cpp | 21 +++ modules/features2d/src/gftt.cpp | 37 +++- modules/features2d/src/kaze.cpp | 20 +- modules/features2d/src/mser.cpp | 60 ++++++ modules/features2d/src/orb.cpp | 42 +++++ modules/features2d/src/sift.dispatch.cpp | 48 +++++ modules/java/check-tests.py | 4 +- 28 files changed, 845 insertions(+), 555 deletions(-) create mode 100644 modules/features2d/misc/java/test/AGASTFeatureDetectorTest.java create mode 100644 modules/features2d/misc/java/test/AKAZEDescriptorExtractorTest.java create mode 100644 modules/features2d/misc/java/test/BRISKDescriptorExtractorTest.java create mode 100644 modules/features2d/misc/java/test/KAZEDescriptorExtractorTest.java delete mode 100644 modules/features2d/misc/java/test/STARFeatureDetectorTest.java delete mode 100644 modules/features2d/misc/java/test/SURFDescriptorExtractorTest.java delete mode 100644 modules/features2d/misc/java/test/SURFFeatureDetectorTest.java diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index deb5eb1cb2..4bb335eb68 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -319,6 +319,21 @@ public: double sigma, int descriptorType); CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; + + CV_WRAP virtual void setNFeatures(int maxFeatures) = 0; + CV_WRAP virtual int getNFeatures() const = 0; + + CV_WRAP virtual void setNOctaveLayers(int nOctaveLayers) = 0; + CV_WRAP virtual int getNOctaveLayers() const = 0; + + CV_WRAP virtual void setContrastThreshold(double contrastThreshold) = 0; + CV_WRAP virtual double getContrastThreshold() const = 0; + + CV_WRAP virtual void setEdgeThreshold(double edgeThreshold) = 0; + CV_WRAP virtual double getEdgeThreshold() const = 0; + + CV_WRAP virtual void setSigma(double sigma) = 0; + CV_WRAP virtual double getSigma() const = 0; }; typedef SIFT SiftFeatureDetector; @@ -374,14 +389,20 @@ public: /** @brief Set detection threshold. @param threshold AGAST detection threshold score. */ - CV_WRAP virtual void setThreshold(int threshold) { CV_UNUSED(threshold); return; } - CV_WRAP virtual int getThreshold() const { return -1; } + CV_WRAP virtual void setThreshold(int threshold) = 0; + CV_WRAP virtual int getThreshold() const = 0; /** @brief Set detection octaves. @param octaves detection octaves. Use 0 to do single scale. */ - CV_WRAP virtual void setOctaves(int octaves) { CV_UNUSED(octaves); return; } - CV_WRAP virtual int getOctaves() const { return -1; } + CV_WRAP virtual void setOctaves(int octaves) = 0; + CV_WRAP virtual int getOctaves() const = 0; + /** @brief Set detection patternScale. + @param patternScale apply this scale to the pattern used for sampling the neighbourhood of a + keypoint. + */ + CV_WRAP virtual void setPatternScale(float patternScale) = 0; + CV_WRAP virtual float getPatternScale() const = 0; }; /** @brief Class implementing the ORB (*oriented BRIEF*) keypoint detector and descriptor extractor @@ -514,8 +535,27 @@ public: CV_WRAP virtual void setMaxArea(int maxArea) = 0; CV_WRAP virtual int getMaxArea() const = 0; + CV_WRAP virtual void setMaxVariation(double maxVariation) = 0; + CV_WRAP virtual double getMaxVariation() const = 0; + + CV_WRAP virtual void setMinDiversity(double minDiversity) = 0; + CV_WRAP virtual double getMinDiversity() const = 0; + + CV_WRAP virtual void setMaxEvolution(int maxEvolution) = 0; + CV_WRAP virtual int getMaxEvolution() const = 0; + + CV_WRAP virtual void setAreaThreshold(double areaThreshold) = 0; + CV_WRAP virtual double getAreaThreshold() const = 0; + + CV_WRAP virtual void setMinMargin(double min_margin) = 0; + CV_WRAP virtual double getMinMargin() const = 0; + + CV_WRAP virtual void setEdgeBlurSize(int edge_blur_size) = 0; + CV_WRAP virtual int getEdgeBlurSize() const = 0; + CV_WRAP virtual void setPass2Only(bool f) = 0; CV_WRAP virtual bool getPass2Only() const = 0; + CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; }; @@ -660,6 +700,9 @@ public: CV_WRAP virtual void setBlockSize(int blockSize) = 0; CV_WRAP virtual int getBlockSize() const = 0; + CV_WRAP virtual void setGradientSize(int gradientSize_) = 0; + CV_WRAP virtual int getGradientSize() = 0; + CV_WRAP virtual void setHarrisDetector(bool val) = 0; CV_WRAP virtual bool getHarrisDetector() const = 0; @@ -734,6 +777,10 @@ public: CV_WRAP static Ptr create(const SimpleBlobDetector::Params ¶meters = SimpleBlobDetector::Params()); + + CV_WRAP virtual void setParams(const SimpleBlobDetector::Params& params ) = 0; + CV_WRAP virtual SimpleBlobDetector::Params getParams() const = 0; + CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; CV_WRAP virtual const std::vector >& getBlobContours() const; }; diff --git a/modules/features2d/misc/java/test/AGASTFeatureDetectorTest.java b/modules/features2d/misc/java/test/AGASTFeatureDetectorTest.java new file mode 100644 index 0000000000..4158219f59 --- /dev/null +++ b/modules/features2d/misc/java/test/AGASTFeatureDetectorTest.java @@ -0,0 +1,85 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.AgastFeatureDetector; + +public class AGASTFeatureDetectorTest extends OpenCVTestCase { + + AgastFeatureDetector detector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = AgastFeatureDetector.create(); // default (10,true,3) + } + + public void testCreate() { + assertNotNull(detector); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testRead() { + String filename = OpenCVTestRunner.getTempFileName("xml"); + writeFile(filename, "\n\nFeature2D.AgastFeatureDetector\n11\n0\n2\n\n"); + + detector.read(filename); + + assertEquals(11, detector.getThreshold()); + assertEquals(false, detector.getNonmaxSuppression()); + assertEquals(2, detector.getType()); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.AgastFeatureDetector\"\nthreshold: 11\nnonmaxSuppression: 0\ntype: 2\n"); + + detector.read(filename); + + assertEquals(11, detector.getThreshold()); + assertEquals(false, detector.getNonmaxSuppression()); + assertEquals(2, detector.getType()); + } + + public void testWrite() { + String filename = OpenCVTestRunner.getTempFileName("xml"); + + detector.write(filename); + + String truth = "\n\nFeature2D.AgastFeatureDetector\n10\n1\n3\n\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.AgastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: 1\ntype: 3\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/features2d/misc/java/test/AKAZEDescriptorExtractorTest.java b/modules/features2d/misc/java/test/AKAZEDescriptorExtractorTest.java new file mode 100644 index 0000000000..fd98cddee1 --- /dev/null +++ b/modules/features2d/misc/java/test/AKAZEDescriptorExtractorTest.java @@ -0,0 +1,67 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.AKAZE; + +public class AKAZEDescriptorExtractorTest extends OpenCVTestCase { + + AKAZE extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = AKAZE.create(); // default (5,0,3,0.001f,4,4,1) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nformat: 3\nname: \"Feature2D.AKAZE\"\ndescriptor: 4\ndescriptor_channels: 2\ndescriptor_size: 32\nthreshold: 0.125\noctaves: 3\nsublevels: 5\ndiffusivity: 2\n"); + + extractor.read(filename); + + assertEquals(4, extractor.getDescriptorType()); + assertEquals(2, extractor.getDescriptorChannels()); + assertEquals(32, extractor.getDescriptorSize()); + assertEquals(0.125, extractor.getThreshold()); + assertEquals(3, extractor.getNOctaves()); + assertEquals(5, extractor.getNOctaveLayers()); + assertEquals(2, extractor.getDiffusivity()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nformat: 3\nname: \"Feature2D.AKAZE\"\ndescriptor: 5\ndescriptor_channels: 3\ndescriptor_size: 0\nthreshold: 1.0000000474974513e-03\noctaves: 4\nsublevels: 4\ndiffusivity: 1\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/features2d/misc/java/test/BRIEFDescriptorExtractorTest.java b/modules/features2d/misc/java/test/BRIEFDescriptorExtractorTest.java index 2c6e543e3d..a8744635ee 100644 --- a/modules/features2d/misc/java/test/BRIEFDescriptorExtractorTest.java +++ b/modules/features2d/misc/java/test/BRIEFDescriptorExtractorTest.java @@ -86,7 +86,7 @@ public class BRIEFDescriptorExtractorTest extends OpenCVTestCase { extractor.write(filename); - String truth = "\n\n32\n\n"; + String truth = "\n\nFeature2D.BRIEF\n32\n0\n\n"; assertEquals(truth, readFile(filename)); } @@ -95,7 +95,7 @@ public class BRIEFDescriptorExtractorTest extends OpenCVTestCase { extractor.write(filename); - String truth = "%YAML:1.0\n---\ndescriptorSize: 32\n"; + String truth = "%YAML:1.0\n---\nname: \"Feature2D.BRIEF\"\ndescriptorSize: 32\nuse_orientation: 0\n"; assertEquals(truth, readFile(filename)); } diff --git a/modules/features2d/misc/java/test/BRISKDescriptorExtractorTest.java b/modules/features2d/misc/java/test/BRISKDescriptorExtractorTest.java new file mode 100644 index 0000000000..faa59dfb1a --- /dev/null +++ b/modules/features2d/misc/java/test/BRISKDescriptorExtractorTest.java @@ -0,0 +1,63 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.BRISK; + +public class BRISKDescriptorExtractorTest extends OpenCVTestCase { + + BRISK extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = BRISK.create(); // default (30,3,1) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.BRISK\"\nthreshold: 31\noctaves: 4\npatternScale: 1.1\n"); + + extractor.read(filename); + + assertEquals(31, extractor.getThreshold()); + assertEquals(4, extractor.getOctaves()); + assertEquals(1.1f, extractor.getPatternScale()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.BRISK\"\nthreshold: 30\noctaves: 3\npatternScale: 1.\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/features2d/misc/java/test/FASTFeatureDetectorTest.java b/modules/features2d/misc/java/test/FASTFeatureDetectorTest.java index 044e70d0ed..cce21837ad 100644 --- a/modules/features2d/misc/java/test/FASTFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/FASTFeatureDetectorTest.java @@ -8,7 +8,6 @@ import org.opencv.core.Mat; import org.opencv.core.MatOfKeyPoint; import org.opencv.core.Point; import org.opencv.core.Scalar; -import org.opencv.features2d.Feature2D; import org.opencv.features2d.FastFeatureDetector; import org.opencv.core.KeyPoint; import org.opencv.test.OpenCVTestCase; @@ -17,7 +16,7 @@ import org.opencv.imgproc.Imgproc; public class FASTFeatureDetectorTest extends OpenCVTestCase { - Feature2D detector; + FastFeatureDetector detector; KeyPoint[] truth; private Mat getMaskImg() { @@ -78,20 +77,24 @@ public class FASTFeatureDetectorTest extends OpenCVTestCase { public void testEmpty() { // assertFalse(detector.empty()); - fail("Not yet implemented"); //FAST does not override empty() method + fail("Not yet implemented"); // FAST does not override empty() method } public void testRead() { - String filename = OpenCVTestRunner.getTempFileName("yml"); + String filename = OpenCVTestRunner.getTempFileName("xml"); - writeFile(filename, "%YAML:1.0\n---\nthreshold: 130\nnonmaxSuppression: 1\n"); + writeFile(filename, "\n\nFeature2D.FastFeatureDetector\n10\n1\n2\n\n"); detector.read(filename); + assertEquals(10, detector.getThreshold()); + assertEquals(true, detector.getNonmaxSuppression()); + assertEquals(2, detector.getType()); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); detector.detect(grayChess, keypoints1); - writeFile(filename, "%YAML:1.0\n---\nthreshold: 150\nnonmaxSuppression: 1\n"); + writeFile(filename, "\n\nFeature2D.FastFeatureDetector\n150\n1\n2\n\n"); detector.read(filename); MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); @@ -104,16 +107,18 @@ public class FASTFeatureDetectorTest extends OpenCVTestCase { public void testReadYml() { String filename = OpenCVTestRunner.getTempFileName("yml"); - writeFile(filename, - "\n\n130\n1\n\n"); + writeFile(filename, "%YAML:1.0\n---\nthreshold: 130\nnonmaxSuppression: 1\ntype: 2\n"); detector.read(filename); + assertEquals(130, detector.getThreshold()); + assertEquals(true, detector.getNonmaxSuppression()); + assertEquals(2, detector.getType()); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); detector.detect(grayChess, keypoints1); - writeFile(filename, - "\n\n150\n1\n\n"); + writeFile(filename, "%YAML:1.0\n---\nthreshold: 150\nnonmaxSuppression: 1\ntype: 2\n"); detector.read(filename); MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); @@ -123,28 +128,14 @@ public class FASTFeatureDetectorTest extends OpenCVTestCase { assertTrue(keypoints2.total() <= keypoints1.total()); } - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); - - detector.write(filename); - -// String truth = "\n\nFeature2D.FAST\n1\n10\n2\n\n"; - String truth = "\n\n\n"; - String data = readFile(filename); - //Log.d("qqq", "\"" + data + "\""); - assertEquals(truth, data); - } - public void testWriteYml() { String filename = OpenCVTestRunner.getTempFileName("yml"); detector.write(filename); -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.FAST\"\nnonmaxSuppression: 1\nthreshold: 10\ntype: 2\n"; - String truth = "%YAML:1.0\n---\n"; + String truth = "%YAML:1.0\n---\nname: \"Feature2D.FastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: 1\ntype: 2\n"; String data = readFile(filename); - //Log.d("qqq", "\"" + data + "\""); assertEquals(truth, data); } } diff --git a/modules/features2d/misc/java/test/Features2dTest.java b/modules/features2d/misc/java/test/Features2dTest.java index db9739bd44..0b4bcc9fab 100644 --- a/modules/features2d/misc/java/test/Features2dTest.java +++ b/modules/features2d/misc/java/test/Features2dTest.java @@ -79,8 +79,8 @@ public class Features2dTest extends OpenCVTestCase { public void testPTOD() { - String detectorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\noctaves: 3\noctaveLayers: 4\nupright: 0\n"; - String extractorCfg = "%YAML:1.0\n---\nnOctaves: 4\nnOctaveLayers: 2\nextended: 0\nupright: 0\n"; + String detectorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n"; + String extractorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n"; Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null); Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null); diff --git a/modules/features2d/misc/java/test/GFTTFeatureDetectorTest.java b/modules/features2d/misc/java/test/GFTTFeatureDetectorTest.java index f05942d14a..86e42cbc1d 100644 --- a/modules/features2d/misc/java/test/GFTTFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/GFTTFeatureDetectorTest.java @@ -1,11 +1,21 @@ package org.opencv.test.features2d; import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.GFTTDetector; public class GFTTFeatureDetectorTest extends OpenCVTestCase { + GFTTDetector detector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = GFTTDetector.create(); // default constructor have (1000, 0.01, 1, 3, 3, false, 0.04) + } + public void testCreate() { - fail("Not yet implemented"); + assertNotNull(detector); } public void testDetectListOfMatListOfListOfKeyPoint() { @@ -28,12 +38,30 @@ public class GFTTFeatureDetectorTest extends OpenCVTestCase { fail("Not yet implemented"); } - public void testRead() { - fail("Not yet implemented"); + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 500\nqualityLevel: 2.0000000000000000e-02\nminDistance: 2.\nblockSize: 4\ngradSize: 5\nuseHarrisDetector: 1\nk: 5.0000000000000000e-02\n"); + detector.read(filename); + + assertEquals(500, detector.getMaxFeatures()); + assertEquals(0.02, detector.getQualityLevel()); + assertEquals(2.0, detector.getMinDistance()); + assertEquals(4, detector.getBlockSize()); + assertEquals(5, detector.getGradientSize()); + assertEquals(true, detector.getHarrisDetector()); + assertEquals(0.05, detector.getK()); } - public void testWrite() { - fail("Not yet implemented"); + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 1000\nqualityLevel: 1.0000000000000000e-02\nminDistance: 1.\nblockSize: 3\ngradSize: 3\nuseHarrisDetector: 0\nk: 4.0000000000000001e-02\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); } } diff --git a/modules/features2d/misc/java/test/KAZEDescriptorExtractorTest.java b/modules/features2d/misc/java/test/KAZEDescriptorExtractorTest.java new file mode 100644 index 0000000000..69ca35e015 --- /dev/null +++ b/modules/features2d/misc/java/test/KAZEDescriptorExtractorTest.java @@ -0,0 +1,66 @@ +package org.opencv.test.features2d; + +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.KAZE; + +public class KAZEDescriptorExtractorTest extends OpenCVTestCase { + + KAZE extractor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = KAZE.create(); // default (false,false,0.001f,4,4,1) + } + + public void testCreate() { + assertNotNull(extractor); + } + + public void testDetectListOfMatListOfListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectListOfMatListOfListOfKeyPointListOfMat() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPoint() { + fail("Not yet implemented"); + } + + public void testDetectMatListOfKeyPointMat() { + fail("Not yet implemented"); + } + + public void testEmpty() { + fail("Not yet implemented"); + } + + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nformat: 3\nname: \"Feature2D.KAZE\"\nextended: 1\nupright: 1\nthreshold: 0.125\noctaves: 3\nsublevels: 5\ndiffusivity: 2\n"); + + extractor.read(filename); + + assertEquals(true, extractor.getExtended()); + assertEquals(true, extractor.getUpright()); + assertEquals(0.125, extractor.getThreshold()); + assertEquals(3, extractor.getNOctaves()); + assertEquals(5, extractor.getNOctaveLayers()); + assertEquals(2, extractor.getDiffusivity()); + } + + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + extractor.write(filename); + + String truth = "%YAML:1.0\n---\nformat: 3\nname: \"Feature2D.KAZE\"\nextended: 0\nupright: 0\nthreshold: 1.0000000474974513e-03\noctaves: 4\nsublevels: 4\ndiffusivity: 1\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); + } + +} diff --git a/modules/features2d/misc/java/test/MSERFeatureDetectorTest.java b/modules/features2d/misc/java/test/MSERFeatureDetectorTest.java index 3710c78ad1..7f5f1c1849 100644 --- a/modules/features2d/misc/java/test/MSERFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/MSERFeatureDetectorTest.java @@ -1,11 +1,21 @@ package org.opencv.test.features2d; import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.features2d.MSER; public class MSERFeatureDetectorTest extends OpenCVTestCase { + MSER detector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + detector = MSER.create(); // default constructor have (5, 60, 14400, .25, .2, 200, 1.01, .003, 5) + } + public void testCreate() { - fail("Not yet implemented"); + assertNotNull(detector); } public void testDetectListOfMatListOfListOfKeyPoint() { @@ -28,12 +38,33 @@ public class MSERFeatureDetectorTest extends OpenCVTestCase { fail("Not yet implemented"); } - public void testRead() { - fail("Not yet implemented"); + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.MSER\"\ndelta: 6\nminArea: 62\nmaxArea: 14402\nmaxVariation: .26\nminDiversity: .3\nmaxEvolution: 201\nareaThreshold: 1.02\nminMargin: 3.0e-3\nedgeBlurSize: 3\npass2Only: 1\n"); + detector.read(filename); + + assertEquals(6, detector.getDelta()); + assertEquals(62, detector.getMinArea()); + assertEquals(14402, detector.getMaxArea()); + assertEquals(.26, detector.getMaxVariation()); + assertEquals(.3, detector.getMinDiversity()); + assertEquals(201, detector.getMaxEvolution()); + assertEquals(1.02, detector.getAreaThreshold()); + assertEquals(0.003, detector.getMinMargin()); + assertEquals(3, detector.getEdgeBlurSize()); + assertEquals(true, detector.getPass2Only()); } - public void testWrite() { - fail("Not yet implemented"); + public void testWriteYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + + detector.write(filename); + + String truth = "%YAML:1.0\n---\nname: \"Feature2D.MSER\"\ndelta: 5\nminArea: 60\nmaxArea: 14400\nmaxVariation: 2.5000000000000000e-01\nminDiversity: 2.0000000000000001e-01\nmaxEvolution: 200\nareaThreshold: 1.0100000000000000e+00\nminMargin: 3.0000000000000001e-03\nedgeBlurSize: 5\npass2Only: 0\n"; + String actual = readFile(filename); + actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation + assertEquals(truth, actual); } } diff --git a/modules/features2d/misc/java/test/ORBDescriptorExtractorTest.java b/modules/features2d/misc/java/test/ORBDescriptorExtractorTest.java index 78dea53620..6bc9bb6299 100644 --- a/modules/features2d/misc/java/test/ORBDescriptorExtractorTest.java +++ b/modules/features2d/misc/java/test/ORBDescriptorExtractorTest.java @@ -75,16 +75,25 @@ public class ORBDescriptorExtractorTest extends OpenCVTestCase { fail("Not yet implemented"); // ORB does not override empty() method } - public void testRead() { + public void testReadYml() { KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1); MatOfKeyPoint keypoints = new MatOfKeyPoint(point); Mat img = getTestImg(); Mat descriptors = new Mat(); -// String filename = OpenCVTestRunner.getTempFileName("yml"); -// writeFile(filename, "%YAML:1.0\n---\nscaleFactor: 1.1\nnLevels: 3\nfirstLevel: 0\nedgeThreshold: 31\npatchSize: 31\n"); -// extractor.read(filename); - extractor = ORB.create(500, 1.1f, 3, 31, 0, 2, ORB.HARRIS_SCORE, 31, 20); + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nnfeatures: 500\nscaleFactor: 1.1\nnlevels: 3\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n"); + extractor.read(filename); + + assertEquals(500, extractor.getMaxFeatures()); + assertEquals(1.1, extractor.getScaleFactor()); + assertEquals(3, extractor.getNLevels()); + assertEquals(31, extractor.getEdgeThreshold()); + assertEquals(0, extractor.getFirstLevel()); + assertEquals(2, extractor.getWTA_K()); + assertEquals(0, extractor.getScoreType()); + assertEquals(31, extractor.getPatchSize()); + assertEquals(20, extractor.getFastThreshold()); extractor.compute(img, keypoints, descriptors); @@ -97,25 +106,13 @@ public class ORBDescriptorExtractorTest extends OpenCVTestCase { assertDescriptorsClose(truth, descriptors, 1); } - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); - - extractor.write(filename); - -// String truth = "\n\nFeature2D.ORB\n2\n31\n0\n500\n8\n31\n1.2000000476837158e+00\n0\n\n"; - String truth = "\n\n\n"; - String actual = readFile(filename); - actual = actual.replaceAll("e\\+000", "e+00"); // NOTE: workaround for different platforms double representation - assertEquals(truth, actual); - } - public void testWriteYml() { String filename = OpenCVTestRunner.getTempFileName("yml"); extractor.write(filename); -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.ORB\"\nWTA_K: 2\nedgeThreshold: 31\nfirstLevel: 0\nnFeatures: 500\nnLevels: 8\npatchSize: 31\nscaleFactor: 1.2000000476837158e+00\nscoreType: 0\n"; - String truth = "%YAML:1.0\n---\n"; + String truth = "%YAML:1.0\n---\nname: \"Feature2D.ORB\"\nnfeatures: 500\nscaleFactor: 1.2000000476837158e+00\nnlevels: 8\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n"; +// String truth = "%YAML:1.0\n---\n"; String actual = readFile(filename); actual = actual.replaceAll("e\\+000", "e+00"); // NOTE: workaround for different platforms double representation assertEquals(truth, actual); diff --git a/modules/features2d/misc/java/test/SIFTDescriptorExtractorTest.java b/modules/features2d/misc/java/test/SIFTDescriptorExtractorTest.java index c548ff7792..63a59aa58c 100644 --- a/modules/features2d/misc/java/test/SIFTDescriptorExtractorTest.java +++ b/modules/features2d/misc/java/test/SIFTDescriptorExtractorTest.java @@ -10,11 +10,11 @@ import org.opencv.features2d.SIFT; import org.opencv.test.OpenCVTestCase; import org.opencv.test.OpenCVTestRunner; import org.opencv.imgproc.Imgproc; -import org.opencv.features2d.Feature2D; +import org.opencv.features2d.SIFT; public class SIFTDescriptorExtractorTest extends OpenCVTestCase { - Feature2D extractor; + SIFT extractor; KeyPoint keypoint; int matSize; Mat truth; @@ -43,7 +43,7 @@ public class SIFTDescriptorExtractorTest extends OpenCVTestCase { 117, 112, 117, 76, 117, 54, 117, 25, 29, 22, 117, 117, 16, 11, 14, 1, 0, 0, 22, 26, 0, 0, 0, 0, 1, 4, 15, 2, 47, 8, 0, 0, 82, 56, 31, 17, 81, 12, 0, 0, 26, 23, 18, 23, 0, 0, 0, 0, 0, 0, 0, 0 - ); + ); } }; } @@ -76,23 +76,23 @@ public class SIFTDescriptorExtractorTest extends OpenCVTestCase { public void testEmpty() { // assertFalse(extractor.empty()); - fail("Not yet implemented"); //SIFT does not override empty() method + fail("Not yet implemented"); // SIFT does not override empty() method } - public void testRead() { - fail("Not yet implemented"); - } + public void testReadYml() { + String filename = OpenCVTestRunner.getTempFileName("yml"); + writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 100\nnOctaveLayers: 4\ncontrastThreshold: 5.0000000000000001e-02\nedgeThreshold: 11\nsigma: 1.7\ndescriptorType: 5\n"); - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); + extractor.read(filename); - extractor.write(filename); + assertEquals(128, extractor.descriptorSize()); -// String truth = "\n\nFeature2D.SIFT\n4.0000000000000001e-02\n10.\n0\n3\n1.6000000000000001e+00\n\n"; - String truth = "\n\n\n"; - String actual = readFile(filename); - actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation - assertEquals(truth, actual); + assertEquals(100, extractor.getNFeatures()); + assertEquals(4, extractor.getNOctaveLayers()); + assertEquals(0.05, extractor.getContrastThreshold()); + assertEquals(11., extractor.getEdgeThreshold()); + assertEquals(1.7, extractor.getSigma()); + assertEquals(5, extractor.descriptorType()); } public void testWriteYml() { @@ -100,8 +100,7 @@ public class SIFTDescriptorExtractorTest extends OpenCVTestCase { extractor.write(filename); -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\ncontrastThreshold: 4.0000000000000001e-02\nedgeThreshold: 10.\nnFeatures: 0\nnOctaveLayers: 3\nsigma: 1.6000000000000001e+00\n"; - String truth = "%YAML:1.0\n---\n"; + String truth = "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 0\nnOctaveLayers: 3\ncontrastThreshold: 4.0000000000000001e-02\nedgeThreshold: 10.\nsigma: 1.6000000000000001e+00\ndescriptorType: 5\n"; String actual = readFile(filename); actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation assertEquals(truth, actual); diff --git a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java index 34c0d94a76..a67a0e8c3a 100644 --- a/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java +++ b/modules/features2d/misc/java/test/SIMPLEBLOBFeatureDetectorTest.java @@ -11,12 +11,12 @@ import org.opencv.core.KeyPoint; import org.opencv.test.OpenCVTestCase; import org.opencv.test.OpenCVTestRunner; import org.opencv.imgproc.Imgproc; -import org.opencv.features2d.Feature2D; import org.opencv.features2d.SimpleBlobDetector; +import org.opencv.features2d.SimpleBlobDetector_Params; public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase { - Feature2D detector; + SimpleBlobDetector detector; int matSize; KeyPoint[] truth; @@ -47,8 +47,8 @@ public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase { detector = SimpleBlobDetector.create(); matSize = 200; truth = new KeyPoint[] { - new KeyPoint( 140, 100, 41.036568f, -1, 0, 0, -1), - new KeyPoint( 60, 100, 48.538486f, -1, 0, 0, -1), + new KeyPoint(140, 100, 41.036568f, -1, 0, 0, -1), + new KeyPoint(60, 100, 48.538486f, -1, 0, 0, -1), new KeyPoint(100, 60, 36.769554f, -1, 0, 0, -1), new KeyPoint(100, 140, 28.635643f, -1, 0, 0, -1), new KeyPoint(100, 100, 20.880613f, -1, 0, 0, -1) @@ -91,16 +91,38 @@ public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase { fail("Not yet implemented"); } - public void testRead() { + public void testReadYml() { Mat img = getTestImg(); MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); detector.detect(img, keypoints1); String filename = OpenCVTestRunner.getTempFileName("yml"); - writeFile(filename, "%YAML:1.0\nthresholdStep: 10\nminThreshold: 50\nmaxThreshold: 220\nminRepeatability: 2\nfilterByArea: true\nminArea: 800\nmaxArea: 5000\n"); + writeFile(filename, "%YAML:1.0\nthresholdStep: 10.0\nminThreshold: 50\nmaxThreshold: 220\nminRepeatability: 2\nminDistBetweenBlobs: 10.\nfilterByColor: 1\nblobColor: 0\nfilterByArea: 1\nminArea: 800\nmaxArea: 6000\nfilterByCircularity: 0\nminCircularity: 0.7\nmaxCircularity: 10.\nfilterByInertia: 1\nminInertiaRatio: 0.2\nmaxInertiaRatio: 11.\nfilterByConvexity: true\nminConvexity: 0.9\nmaxConvexity: 12.\n"); detector.read(filename); + SimpleBlobDetector_Params params = detector.getParams(); + assertEquals(10.0f, params.get_thresholdStep()); + assertEquals(50f, params.get_minThreshold()); + assertEquals(220f, params.get_maxThreshold()); + assertEquals(2, params.get_minRepeatability()); + assertEquals(10.0f, params.get_minDistBetweenBlobs()); + assertEquals(true, params.get_filterByColor()); + // FIXME: blobColor field has uchar type in C++ and cannot be automatically wrapped to Java as it does not support unsigned types + //assertEquals(0, params.get_blobColor()); + assertEquals(true, params.get_filterByArea()); + assertEquals(800f, params.get_minArea()); + assertEquals(6000f, params.get_maxArea()); + assertEquals(false, params.get_filterByCircularity()); + assertEquals(0.7f, params.get_minCircularity()); + assertEquals(10.0f, params.get_maxCircularity()); + assertEquals(true, params.get_filterByInertia()); + assertEquals(0.2f, params.get_minInertiaRatio()); + assertEquals(11.0f, params.get_maxInertiaRatio()); + assertEquals(true, params.get_filterByConvexity()); + assertEquals(0.9f, params.get_minConvexity()); + assertEquals(12.0f, params.get_maxConvexity()); + MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); detector.detect(img, keypoints2); diff --git a/modules/features2d/misc/java/test/STARFeatureDetectorTest.java b/modules/features2d/misc/java/test/STARFeatureDetectorTest.java deleted file mode 100644 index 327084211b..0000000000 --- a/modules/features2d/misc/java/test/STARFeatureDetectorTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.opencv.test.features2d; - -import java.util.Arrays; - -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.MatOfKeyPoint; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.core.KeyPoint; -import org.opencv.test.OpenCVTestCase; -import org.opencv.test.OpenCVTestRunner; -import org.opencv.imgproc.Imgproc; -import org.opencv.features2d.Feature2D; - -public class STARFeatureDetectorTest extends OpenCVTestCase { - - Feature2D detector; - int matSize; - KeyPoint[] truth; - - private Mat getMaskImg() { - Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); - Mat right = mask.submat(0, matSize, matSize / 2, matSize); - right.setTo(new Scalar(0)); - return mask; - } - - private Mat getTestImg() { - Scalar color = new Scalar(0); - int center = matSize / 2; - int radius = 6; - int offset = 40; - - Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); - Imgproc.circle(img, new Point(center - offset, center), radius, color, -1); - Imgproc.circle(img, new Point(center + offset, center), radius, color, -1); - Imgproc.circle(img, new Point(center, center - offset), radius, color, -1); - Imgproc.circle(img, new Point(center, center + offset), radius, color, -1); - Imgproc.circle(img, new Point(center, center), radius, color, -1); - return img; - } - - protected void setUp() throws Exception { - super.setUp(); - detector = createClassInstance(XFEATURES2D+"StarDetector", DEFAULT_FACTORY, null, null); - matSize = 200; - truth = new KeyPoint[] { - new KeyPoint( 95, 80, 22, -1, 31.5957f, 0, -1), - new KeyPoint(105, 80, 22, -1, 31.5957f, 0, -1), - new KeyPoint( 80, 95, 22, -1, 31.5957f, 0, -1), - new KeyPoint(120, 95, 22, -1, 31.5957f, 0, -1), - new KeyPoint(100, 100, 8, -1, 30.f, 0, -1), - new KeyPoint( 80, 105, 22, -1, 31.5957f, 0, -1), - new KeyPoint(120, 105, 22, -1, 31.5957f, 0, -1), - new KeyPoint( 95, 120, 22, -1, 31.5957f, 0, -1), - new KeyPoint(105, 120, 22, -1, 31.5957f, 0, -1) - }; - } - - public void testCreate() { - assertNotNull(detector); - } - - public void testDetectListOfMatListOfListOfKeyPoint() { - fail("Not yet implemented"); - } - - public void testDetectListOfMatListOfListOfKeyPointListOfMat() { - fail("Not yet implemented"); - } - - public void testDetectMatListOfKeyPoint() { - Mat img = getTestImg(); - MatOfKeyPoint keypoints = new MatOfKeyPoint(); - - detector.detect(img, keypoints); - - assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS); - } - - public void testDetectMatListOfKeyPointMat() { - Mat img = getTestImg(); - Mat mask = getMaskImg(); - MatOfKeyPoint keypoints = new MatOfKeyPoint(); - - detector.detect(img, keypoints, mask); - - assertListKeyPointEquals(Arrays.asList(truth[0], truth[2], truth[5], truth[7]), keypoints.toList(), EPS); - } - - public void testEmpty() { -// assertFalse(detector.empty()); - fail("Not yet implemented"); - } - - public void testRead() { - Mat img = getTestImg(); - - MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); - detector.detect(img, keypoints1); - - String filename = OpenCVTestRunner.getTempFileName("yml"); - writeFile(filename, "%YAML:1.0\n---\nmaxSize: 45\nresponseThreshold: 150\nlineThresholdProjected: 10\nlineThresholdBinarized: 8\nsuppressNonmaxSize: 5\n"); - detector.read(filename); - - MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); - detector.detect(img, keypoints2); - - assertTrue(keypoints2.total() <= keypoints1.total()); - } - - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); - - detector.write(filename); - -// String truth = "\n\nFeature2D.STAR\n8\n10\n45\n30\n5\n\n"; - String truth = "\n\n\n"; - assertEquals(truth, readFile(filename)); - } - - public void testWriteYml() { - String filename = OpenCVTestRunner.getTempFileName("yml"); - - detector.write(filename); - -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.STAR\"\nlineThresholdBinarized: 8\nlineThresholdProjected: 10\nmaxSize: 45\nresponseThreshold: 30\nsuppressNonmaxSize: 5\n"; - String truth = "%YAML:1.0\n---\n"; - assertEquals(truth, readFile(filename)); - } - -} diff --git a/modules/features2d/misc/java/test/SURFDescriptorExtractorTest.java b/modules/features2d/misc/java/test/SURFDescriptorExtractorTest.java deleted file mode 100644 index 35131ee254..0000000000 --- a/modules/features2d/misc/java/test/SURFDescriptorExtractorTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.opencv.test.features2d; - -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.MatOfKeyPoint; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.core.KeyPoint; -import org.opencv.test.OpenCVTestCase; -import org.opencv.test.OpenCVTestRunner; -import org.opencv.imgproc.Imgproc; -import org.opencv.features2d.Feature2D; - -public class SURFDescriptorExtractorTest extends OpenCVTestCase { - - Feature2D extractor; - int matSize; - - private Mat getTestImg() { - Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); - Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2); - Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2); - - return cross; - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Class[] cParams = {double.class, int.class, int.class, boolean.class, boolean.class}; - Object[] oValues = {100, 2, 4, true, false}; - extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, cParams, oValues); - - matSize = 100; - } - - public void testComputeListOfMatListOfListOfKeyPointListOfMat() { - fail("Not yet implemented"); - } - - public void testComputeMatListOfKeyPointMat() { - KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1); - MatOfKeyPoint keypoints = new MatOfKeyPoint(point); - Mat img = getTestImg(); - Mat descriptors = new Mat(); - - extractor.compute(img, keypoints, descriptors); - - Mat truth = new Mat(1, 128, CvType.CV_32FC1) { - { - put(0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0.058821894, 0.058821894, -0.045962855, 0.046261817, 0.0085156476, - 0.0085754395, -0.0064509804, 0.0064509804, 0.00044069235, 0.00044069235, 0, 0, 0.00025723741, - 0.00025723741, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00025723741, 0.00025723741, -0.00044069235, - 0.00044069235, 0, 0, 0.36278215, 0.36278215, -0.24688604, 0.26173124, 0.052068226, 0.052662034, - -0.032815345, 0.032815345, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0064523756, - 0.0064523756, 0.0082002236, 0.0088908644, -0.059001274, 0.059001274, 0.045789491, 0.04648013, - 0.11961588, 0.22789426, -0.01322381, 0.18291828, -0.14042182, 0.23973691, 0.073782086, 0.23769434, - -0.027880307, 0.027880307, 0.049587864, 0.049587864, -0.33991757, 0.33991757, 0.21437603, 0.21437603, - -0.0020763327, 0.0020763327, 0.006245892, 0.006245892, -0.04067041, 0.04067041, 0.019361559, - 0.019361559, 0, 0, -0.0035977389, 0.0035977389, 0, 0, -0.00099993451, 0.00099993451, 0.040670406, - 0.040670406, -0.019361559, 0.019361559, 0.006245892, 0.006245892, -0.0020763327, 0.0020763327, - -0.00034532088, 0.00034532088, 0, 0, 0, 0, 0.00034532088, 0.00034532088, -0.00099993451, - 0.00099993451, 0, 0, 0, 0, 0.0035977389, 0.0035977389 - ); - } - }; - - assertMatEqual(truth, descriptors, EPS); - } - - public void testCreate() { - assertNotNull(extractor); - } - - public void testDescriptorSize() { - assertEquals(128, extractor.descriptorSize()); - } - - public void testDescriptorType() { - assertEquals(CvType.CV_32F, extractor.descriptorType()); - } - - public void testEmpty() { -// assertFalse(extractor.empty()); - fail("Not yet implemented"); - } - - public void testRead() { - String filename = OpenCVTestRunner.getTempFileName("yml"); - writeFile(filename, "%YAML:1.0\n---\nnOctaves: 4\nnOctaveLayers: 2\nextended: 1\nupright: 0\n"); - - extractor.read(filename); - - assertEquals(128, extractor.descriptorSize()); - } - - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); - - extractor.write(filename); - -// String truth = "\n\nFeature2D.SURF\n1\n100.\n2\n4\n0\n\n"; - String truth = "\n\n\n"; - assertEquals(truth, readFile(filename)); - } - - public void testWriteYml() { - String filename = OpenCVTestRunner.getTempFileName("yml"); - - extractor.write(filename); - -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nextended: 1\nhessianThreshold: 100.\nnOctaveLayers: 2\nnOctaves: 4\nupright: 0\n"; - String truth = "%YAML:1.0\n---\n"; - assertEquals(truth, readFile(filename)); - } - -} diff --git a/modules/features2d/misc/java/test/SURFFeatureDetectorTest.java b/modules/features2d/misc/java/test/SURFFeatureDetectorTest.java deleted file mode 100644 index f15426cdb8..0000000000 --- a/modules/features2d/misc/java/test/SURFFeatureDetectorTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.opencv.test.features2d; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.MatOfKeyPoint; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.core.KeyPoint; -import org.opencv.test.OpenCVTestCase; -import org.opencv.test.OpenCVTestRunner; -import org.opencv.imgproc.Imgproc; -import org.opencv.features2d.Feature2D; - -public class SURFFeatureDetectorTest extends OpenCVTestCase { - - Feature2D detector; - int matSize; - KeyPoint[] truth; - - private Mat getMaskImg() { - Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); - Mat right = mask.submat(0, matSize, matSize / 2, matSize); - right.setTo(new Scalar(0)); - return mask; - } - - private Mat getTestImg() { - Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); - Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2); - Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2); - - return cross; - } - - private void order(List points) { - Collections.sort(points, new Comparator() { - public int compare(KeyPoint p1, KeyPoint p2) { - if (p1.angle < p2.angle) - return -1; - if (p1.angle > p2.angle) - return 1; - return 0; - } - }); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null); - matSize = 100; - truth = new KeyPoint[] { - new KeyPoint(55.775578f, 55.775578f, 16, 80.245735f, 8617.8633f, 0, -1), - new KeyPoint(44.224422f, 55.775578f, 16, 170.24574f, 8617.8633f, 0, -1), - new KeyPoint(44.224422f, 44.224422f, 16, 260.24573f, 8617.8633f, 0, -1), - new KeyPoint(55.775578f, 44.224422f, 16, 350.24573f, 8617.8633f, 0, -1) - }; - } - - public void testCreate() { - assertNotNull(detector); - } - - public void testDetectListOfMatListOfListOfKeyPoint() { - - setProperty(detector, "hessianThreshold", "double", 8000); - setProperty(detector, "nOctaves", "int", 3); - setProperty(detector, "nOctaveLayers", "int", 4); - setProperty(detector, "upright", "boolean", false); - - List keypoints = new ArrayList(); - Mat cross = getTestImg(); - List crosses = new ArrayList(3); - crosses.add(cross); - crosses.add(cross); - crosses.add(cross); - - detector.detect(crosses, keypoints); - - assertEquals(3, keypoints.size()); - - for (MatOfKeyPoint mkp : keypoints) { - List lkp = mkp.toList(); - order(lkp); - assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS); - } - } - - public void testDetectListOfMatListOfListOfKeyPointListOfMat() { - fail("Not yet implemented"); - } - - public void testDetectMatListOfKeyPoint() { - - setProperty(detector, "hessianThreshold", "double", 8000); - setProperty(detector, "nOctaves", "int", 3); - setProperty(detector, "nOctaveLayers", "int", 4); - setProperty(detector, "upright", "boolean", false); - - MatOfKeyPoint keypoints = new MatOfKeyPoint(); - Mat cross = getTestImg(); - - detector.detect(cross, keypoints); - - List lkp = keypoints.toList(); - order(lkp); - assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS); - } - - public void testDetectMatListOfKeyPointMat() { - - setProperty(detector, "hessianThreshold", "double", 8000); - setProperty(detector, "nOctaves", "int", 3); - setProperty(detector, "nOctaveLayers", "int", 4); - setProperty(detector, "upright", "boolean", false); - - Mat img = getTestImg(); - Mat mask = getMaskImg(); - MatOfKeyPoint keypoints = new MatOfKeyPoint(); - - detector.detect(img, keypoints, mask); - - List lkp = keypoints.toList(); - order(lkp); - assertListKeyPointEquals(Arrays.asList(truth[1], truth[2]), lkp, EPS); - } - - public void testEmpty() { -// assertFalse(detector.empty()); - fail("Not yet implemented"); - } - - public void testRead() { - Mat cross = getTestImg(); - - MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); - detector.detect(cross, keypoints1); - - String filename = OpenCVTestRunner.getTempFileName("yml"); - writeFile(filename, "%YAML:1.0\n---\nhessianThreshold: 8000.\noctaves: 3\noctaveLayers: 4\nupright: 0\n"); - detector.read(filename); - - MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); - detector.detect(cross, keypoints2); - - assertTrue(keypoints2.total() <= keypoints1.total()); - } - - public void testWrite() { - String filename = OpenCVTestRunner.getTempFileName("xml"); - - detector.write(filename); - -// String truth = "\n\nFeature2D.SURF\n0\n100.\n3\n4\n0\n\n"; - String truth = "\n\n\n"; - assertEquals(truth, readFile(filename)); - } - - public void testWriteYml() { - String filename = OpenCVTestRunner.getTempFileName("yml"); - - detector.write(filename); - -// String truth = "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nextended: 0\nhessianThreshold: 100.\nnOctaveLayers: 3\nnOctaves: 4\nupright: 0\n"; - String truth = "%YAML:1.0\n---\n"; - assertEquals(truth, readFile(filename)); - } - -} diff --git a/modules/features2d/misc/objc/gen_dict.json b/modules/features2d/misc/objc/gen_dict.json index f7abcb8837..220a2c74df 100644 --- a/modules/features2d/misc/objc/gen_dict.json +++ b/modules/features2d/misc/objc/gen_dict.json @@ -1,4 +1,10 @@ { + "ManualFuncs" : { + "SimpleBlobDetector": { + "setParams": { "declaration" : [""], "implementation" : [""] }, + "getParams": { "declaration" : [""], "implementation" : [""] } + } + }, "enum_fix" : { "FastFeatureDetector" : { "DetectorType": "FastDetectorType" }, "AgastFeatureDetector" : { "DetectorType": "AgastDetectorType" } diff --git a/modules/features2d/src/agast.cpp b/modules/features2d/src/agast.cpp index e99ceec416..39ac9994d9 100644 --- a/modules/features2d/src/agast.cpp +++ b/modules/features2d/src/agast.cpp @@ -7948,6 +7948,27 @@ public: : threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type) {} + void read( const FileNode& fn) CV_OVERRIDE + { + // if node is empty, keep previous value + if (!fn["threshold"].empty()) + fn["threshold"] >> threshold; + if (!fn["nonmaxSuppression"].empty()) + fn["nonmaxSuppression"] >> nonmaxSuppression; + if (!fn["type"].empty()) + fn["type"] >> type; + } + void write( FileStorage& fs) const CV_OVERRIDE + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "threshold" << threshold; + fs << "nonmaxSuppression" << nonmaxSuppression; + fs << "type" << type; + } + } + void detect( InputArray _image, std::vector& keypoints, InputArray _mask ) CV_OVERRIDE { CV_INSTRUMENT_REGION(); diff --git a/modules/features2d/src/akaze.cpp b/modules/features2d/src/akaze.cpp index 623738eed4..7aa97dae36 100644 --- a/modules/features2d/src/akaze.cpp +++ b/modules/features2d/src/akaze.cpp @@ -207,6 +207,7 @@ namespace cv void write(FileStorage& fs) const CV_OVERRIDE { writeFormat(fs); + fs << "name" << getDefaultName(); fs << "descriptor" << descriptor; fs << "descriptor_channels" << descriptor_channels; fs << "descriptor_size" << descriptor_size; @@ -218,13 +219,21 @@ namespace cv void read(const FileNode& fn) CV_OVERRIDE { - descriptor = static_cast((int)fn["descriptor"]); - descriptor_channels = (int)fn["descriptor_channels"]; - descriptor_size = (int)fn["descriptor_size"]; - threshold = (float)fn["threshold"]; - octaves = (int)fn["octaves"]; - sublevels = (int)fn["sublevels"]; - diffusivity = static_cast((int)fn["diffusivity"]); + // if node is empty, keep previous value + if (!fn["descriptor"].empty()) + descriptor = static_cast((int)fn["descriptor"]); + if (!fn["descriptor_channels"].empty()) + descriptor_channels = (int)fn["descriptor_channels"]; + if (!fn["descriptor_size"].empty()) + descriptor_size = (int)fn["descriptor_size"]; + if (!fn["threshold"].empty()) + threshold = (float)fn["threshold"]; + if (!fn["octaves"].empty()) + octaves = (int)fn["octaves"]; + if (!fn["sublevels"].empty()) + sublevels = (int)fn["sublevels"]; + if (!fn["diffusivity"].empty()) + diffusivity = static_cast((int)fn["diffusivity"]); } DescriptorType descriptor; diff --git a/modules/features2d/src/blobdetector.cpp b/modules/features2d/src/blobdetector.cpp index 9fb4ac4974..396521f72b 100644 --- a/modules/features2d/src/blobdetector.cpp +++ b/modules/features2d/src/blobdetector.cpp @@ -71,6 +71,37 @@ public: virtual void read( const FileNode& fn ) CV_OVERRIDE; virtual void write( FileStorage& fs ) const CV_OVERRIDE; + void setParams(const SimpleBlobDetector::Params& _params ) CV_OVERRIDE { + SimpleBlobDetectorImpl::validateParameters(_params); + params = _params; + } + + SimpleBlobDetector::Params getParams() const CV_OVERRIDE { return params; } + + static void validateParameters(const SimpleBlobDetector::Params& p) + { + if (p.thresholdStep <= 0) + CV_Error(Error::StsBadArg, "thresholdStep>0"); + + if (p.minThreshold > p.maxThreshold || p.minThreshold <= 0) + CV_Error(Error::StsBadArg, "00"); + + if (p.minArea > p.maxArea || p.minArea <=0) + CV_Error(Error::StsBadArg, "0 p.maxCircularity || p.minCircularity <= 0) + CV_Error(Error::StsBadArg, "0 p.maxInertiaRatio || p.minInertiaRatio <= 0) + CV_Error(Error::StsBadArg, "0 p.maxConvexity || p.minConvexity <= 0) + CV_Error(Error::StsBadArg, "0 >& SimpleBlobDetectorImpl::getBlobContours( Ptr SimpleBlobDetector::create(const SimpleBlobDetector::Params& params) { + SimpleBlobDetectorImpl::validateParameters(params); return makePtr(params); } diff --git a/modules/features2d/src/brisk.cpp b/modules/features2d/src/brisk.cpp index adb8b43d30..59a86470f6 100644 --- a/modules/features2d/src/brisk.cpp +++ b/modules/features2d/src/brisk.cpp @@ -54,7 +54,7 @@ namespace cv class BRISK_Impl CV_FINAL : public BRISK { public: - explicit BRISK_Impl(int thresh=30, int octaves=3, float patternScale=1.0f); + explicit BRISK_Impl(int _threshold=30, int _octaves=3, float _patternScale=1.0f); // custom setup explicit BRISK_Impl(const std::vector &radiusList, const std::vector &numberList, float dMax=5.85f, float dMin=8.2f, const std::vector indexChange=std::vector()); @@ -65,6 +65,9 @@ public: virtual ~BRISK_Impl(); + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + int descriptorSize() const CV_OVERRIDE { return strings_; @@ -99,6 +102,35 @@ public: { return octaves; } + virtual void setPatternScale(float _patternScale) CV_OVERRIDE + { + patternScale = _patternScale; + std::vector rList; + std::vector nList; + + // this is the standard pattern found to be suitable also + rList.resize(5); + nList.resize(5); + const double f = 0.85 * patternScale; + + rList[0] = (float)(f * 0.); + rList[1] = (float)(f * 2.9); + rList[2] = (float)(f * 4.9); + rList[3] = (float)(f * 7.4); + rList[4] = (float)(f * 10.8); + + nList[0] = 1; + nList[1] = 10; + nList[2] = 14; + nList[3] = 15; + nList[4] = 20; + + generateKernel(rList, nList, (float)(5.85 * patternScale), (float)(8.2 * patternScale)); + } + virtual float getPatternScale() const CV_OVERRIDE + { + return patternScale; + } // call this to generate the kernel: // circle of radius r (pixels), with n points; @@ -122,6 +154,7 @@ protected: // Feature parameters CV_PROP_RW int threshold; CV_PROP_RW int octaves; + CV_PROP_RW float patternScale; // some helper structures for the Brisk pattern representation struct BriskPatternPoint{ @@ -309,32 +342,12 @@ const float BriskScaleSpace::safetyFactor_ = 1.0f; const float BriskScaleSpace::basicSize_ = 12.0f; // constructors -BRISK_Impl::BRISK_Impl(int thresh, int octaves_in, float patternScale) +BRISK_Impl::BRISK_Impl(int _threshold, int _octaves, float _patternScale) { - threshold = thresh; - octaves = octaves_in; + threshold = _threshold; + octaves = _octaves; - std::vector rList; - std::vector nList; - - // this is the standard pattern found to be suitable also - rList.resize(5); - nList.resize(5); - const double f = 0.85 * patternScale; - - rList[0] = (float)(f * 0.); - rList[1] = (float)(f * 2.9); - rList[2] = (float)(f * 4.9); - rList[3] = (float)(f * 7.4); - rList[4] = (float)(f * 10.8); - - nList[0] = 1; - nList[1] = 10; - nList[2] = 14; - nList[3] = 15; - nList[4] = 20; - - generateKernel(rList, nList, (float)(5.85 * patternScale), (float)(8.2 * patternScale)); + setPatternScale(_patternScale); } BRISK_Impl::BRISK_Impl(const std::vector &radiusList, @@ -359,6 +372,31 @@ BRISK_Impl::BRISK_Impl(int thresh, octaves = octaves_in; } +void BRISK_Impl::read( const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["threshold"].empty()) + fn["threshold"] >> threshold; + if (!fn["octaves"].empty()) + fn["octaves"] >> octaves; + if (!fn["patternScale"].empty()) + { + float _patternScale; + fn["patternScale"] >> _patternScale; + setPatternScale(_patternScale); + } +} +void BRISK_Impl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "threshold" << threshold; + fs << "octaves" << octaves; + fs << "patternScale" << patternScale; + } +} + void BRISK_Impl::generateKernel(const std::vector &radiusList, const std::vector &numberList, diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index e373926a14..163f02717e 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -539,6 +539,27 @@ public: : threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type) {} + void read( const FileNode& fn) CV_OVERRIDE + { + // if node is empty, keep previous value + if (!fn["threshold"].empty()) + fn["threshold"] >> threshold; + if (!fn["nonmaxSuppression"].empty()) + fn["nonmaxSuppression"] >> nonmaxSuppression; + if (!fn["type"].empty()) + fn["type"] >> type; + } + void write( FileStorage& fs) const CV_OVERRIDE + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "threshold" << threshold; + fs << "nonmaxSuppression" << nonmaxSuppression; + fs << "type" << type; + } + } + void detect( InputArray _image, std::vector& keypoints, InputArray _mask ) CV_OVERRIDE { CV_INSTRUMENT_REGION(); diff --git a/modules/features2d/src/gftt.cpp b/modules/features2d/src/gftt.cpp index bc97fc1677..0f98efce61 100644 --- a/modules/features2d/src/gftt.cpp +++ b/modules/features2d/src/gftt.cpp @@ -55,6 +55,39 @@ public: { } + void read( const FileNode& fn) CV_OVERRIDE + { + // if node is empty, keep previous value + if (!fn["nfeatures"].empty()) + fn["nfeatures"] >> nfeatures; + if (!fn["qualityLevel"].empty()) + fn["qualityLevel"] >> qualityLevel; + if (!fn["minDistance"].empty()) + fn["minDistance"] >> minDistance; + if (!fn["blockSize"].empty()) + fn["blockSize"] >> blockSize; + if (!fn["gradSize"].empty()) + fn["gradSize"] >> gradSize; + if (!fn["useHarrisDetector"].empty()) + fn["useHarrisDetector"] >> useHarrisDetector; + if (!fn["k"].empty()) + fn["k"] >> k; + } + void write( FileStorage& fs) const CV_OVERRIDE + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "nfeatures" << nfeatures; + fs << "qualityLevel" << qualityLevel; + fs << "minDistance" << minDistance; + fs << "blockSize" << blockSize; + fs << "gradSize" << gradSize; + fs << "useHarrisDetector" << useHarrisDetector; + fs << "k" << k; + } + } + void setMaxFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; } int getMaxFeatures() const CV_OVERRIDE { return nfeatures; } @@ -67,8 +100,8 @@ public: void setBlockSize(int blockSize_) CV_OVERRIDE { blockSize = blockSize_; } int getBlockSize() const CV_OVERRIDE { return blockSize; } - //void setGradientSize(int gradientSize_) { gradSize = gradientSize_; } - //int getGradientSize() { return gradSize; } + void setGradientSize(int gradientSize_) CV_OVERRIDE { gradSize = gradientSize_; } + int getGradientSize() CV_OVERRIDE { return gradSize; } void setHarrisDetector(bool val) CV_OVERRIDE { useHarrisDetector = val; } bool getHarrisDetector() const CV_OVERRIDE { return useHarrisDetector; } diff --git a/modules/features2d/src/kaze.cpp b/modules/features2d/src/kaze.cpp index 0d841b7f7b..881c5aab1e 100644 --- a/modules/features2d/src/kaze.cpp +++ b/modules/features2d/src/kaze.cpp @@ -163,6 +163,7 @@ namespace cv void write(FileStorage& fs) const CV_OVERRIDE { writeFormat(fs); + fs << "name" << getDefaultName(); fs << "extended" << (int)extended; fs << "upright" << (int)upright; fs << "threshold" << threshold; @@ -173,12 +174,19 @@ namespace cv void read(const FileNode& fn) CV_OVERRIDE { - extended = (int)fn["extended"] != 0; - upright = (int)fn["upright"] != 0; - threshold = (float)fn["threshold"]; - octaves = (int)fn["octaves"]; - sublevels = (int)fn["sublevels"]; - diffusivity = static_cast((int)fn["diffusivity"]); + // if node is empty, keep previous value + if (!fn["extended"].empty()) + extended = (int)fn["extended"] != 0; + if (!fn["upright"].empty()) + upright = (int)fn["upright"] != 0; + if (!fn["threshold"].empty()) + threshold = (float)fn["threshold"]; + if (!fn["octaves"].empty()) + octaves = (int)fn["octaves"]; + if (!fn["sublevels"].empty()) + sublevels = (int)fn["sublevels"]; + if (!fn["diffusivity"].empty()) + diffusivity = static_cast((int)fn["diffusivity"]); } bool extended; diff --git a/modules/features2d/src/mser.cpp b/modules/features2d/src/mser.cpp index 4fe07bd6eb..d59ed39574 100644 --- a/modules/features2d/src/mser.cpp +++ b/modules/features2d/src/mser.cpp @@ -87,6 +87,48 @@ public: virtual ~MSER_Impl() CV_OVERRIDE {} + void read( const FileNode& fn) CV_OVERRIDE + { + // if node is empty, keep previous value + if (!fn["delta"].empty()) + fn["delta"] >> params.delta; + if (!fn["minArea"].empty()) + fn["minArea"] >> params.minArea; + if (!fn["maxArea"].empty()) + fn["maxArea"] >> params.maxArea; + if (!fn["maxVariation"].empty()) + fn["maxVariation"] >> params.maxVariation; + if (!fn["minDiversity"].empty()) + fn["minDiversity"] >> params.minDiversity; + if (!fn["maxEvolution"].empty()) + fn["maxEvolution"] >> params.maxEvolution; + if (!fn["areaThreshold"].empty()) + fn["areaThreshold"] >> params.areaThreshold; + if (!fn["minMargin"].empty()) + fn["minMargin"] >> params.minMargin; + if (!fn["edgeBlurSize"].empty()) + fn["edgeBlurSize"] >> params.edgeBlurSize; + if (!fn["pass2Only"].empty()) + fn["pass2Only"] >> params.pass2Only; + } + void write( FileStorage& fs) const CV_OVERRIDE + { + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "delta" << params.delta; + fs << "minArea" << params.minArea; + fs << "maxArea" << params.maxArea; + fs << "maxVariation" << params.maxVariation; + fs << "minDiversity" << params.minDiversity; + fs << "maxEvolution" << params.maxEvolution; + fs << "areaThreshold" << params.areaThreshold; + fs << "minMargin" << params.minMargin; + fs << "edgeBlurSize" << params.edgeBlurSize; + fs << "pass2Only" << params.pass2Only; + } + } + void setDelta(int delta) CV_OVERRIDE { params.delta = delta; } int getDelta() const CV_OVERRIDE { return params.delta; } @@ -96,6 +138,24 @@ public: void setMaxArea(int maxArea) CV_OVERRIDE { params.maxArea = maxArea; } int getMaxArea() const CV_OVERRIDE { return params.maxArea; } + void setMaxVariation(double maxVariation) CV_OVERRIDE { params.maxVariation = maxVariation; } + double getMaxVariation() const CV_OVERRIDE { return params.maxVariation; } + + void setMinDiversity(double minDiversity) CV_OVERRIDE { params.minDiversity = minDiversity; } + double getMinDiversity() const CV_OVERRIDE { return params.minDiversity; } + + void setMaxEvolution(int maxEvolution) CV_OVERRIDE { params.maxEvolution = maxEvolution; } + int getMaxEvolution() const CV_OVERRIDE { return params.maxEvolution; } + + void setAreaThreshold(double areaThreshold) CV_OVERRIDE { params.areaThreshold = areaThreshold; } + double getAreaThreshold() const CV_OVERRIDE { return params.areaThreshold; } + + void setMinMargin(double min_margin) CV_OVERRIDE { params.minMargin = min_margin; } + double getMinMargin() const CV_OVERRIDE { return params.minMargin; } + + void setEdgeBlurSize(int edge_blur_size) CV_OVERRIDE { params.edgeBlurSize = edge_blur_size; } + int getEdgeBlurSize() const CV_OVERRIDE { return params.edgeBlurSize; } + void setPass2Only(bool f) CV_OVERRIDE { params.pass2Only = f; } bool getPass2Only() const CV_OVERRIDE { return params.pass2Only; } diff --git a/modules/features2d/src/orb.cpp b/modules/features2d/src/orb.cpp index ae1e611127..b0d32002a1 100644 --- a/modules/features2d/src/orb.cpp +++ b/modules/features2d/src/orb.cpp @@ -666,6 +666,9 @@ public: scoreType(_scoreType), patchSize(_patchSize), fastThreshold(_fastThreshold) {} + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + void setMaxFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; } int getMaxFeatures() const CV_OVERRIDE { return nfeatures; } @@ -717,6 +720,45 @@ protected: int fastThreshold; }; +void ORB_Impl::read( const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["nfeatures"].empty()) + fn["nfeatures"] >> nfeatures; + if (!fn["scaleFactor"].empty()) + fn["scaleFactor"] >> scaleFactor; + if (!fn["nlevels"].empty()) + fn["nlevels"] >> nlevels; + if (!fn["edgeThreshold"].empty()) + fn["edgeThreshold"] >> edgeThreshold; + if (!fn["firstLevel"].empty()) + fn["firstLevel"] >> firstLevel; + if (!fn["wta_k"].empty()) + fn["wta_k"] >> wta_k; + if (!fn["scoreType"].empty()) + fn["scoreType"] >> scoreType; + if (!fn["patchSize"].empty()) + fn["patchSize"] >> patchSize; + if (!fn["fastThreshold"].empty()) + fn["fastThreshold"] >> fastThreshold; +} +void ORB_Impl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "nfeatures" << nfeatures; + fs << "scaleFactor" << scaleFactor; + fs << "nlevels" << nlevels; + fs << "edgeThreshold" << edgeThreshold; + fs << "firstLevel" << firstLevel; + fs << "wta_k" << wta_k; + fs << "scoreType" << scoreType; + fs << "patchSize" << patchSize; + fs << "fastThreshold" << fastThreshold; + } +} + int ORB_Impl::descriptorSize() const { return kBytes; diff --git a/modules/features2d/src/sift.dispatch.cpp b/modules/features2d/src/sift.dispatch.cpp index 831750862b..7c72b37898 100644 --- a/modules/features2d/src/sift.dispatch.cpp +++ b/modules/features2d/src/sift.dispatch.cpp @@ -111,6 +111,24 @@ public: void findScaleSpaceExtrema( const std::vector& gauss_pyr, const std::vector& dog_pyr, std::vector& keypoints ) const; + void read( const FileNode& fn) CV_OVERRIDE; + void write( FileStorage& fs) const CV_OVERRIDE; + + void setNFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; } + int getNFeatures() const CV_OVERRIDE { return nfeatures; } + + void setNOctaveLayers(int nOctaveLayers_) CV_OVERRIDE { nOctaveLayers = nOctaveLayers_; } + int getNOctaveLayers() const CV_OVERRIDE { return nOctaveLayers; } + + void setContrastThreshold(double contrastThreshold_) CV_OVERRIDE { contrastThreshold = contrastThreshold_; } + double getContrastThreshold() const CV_OVERRIDE { return contrastThreshold; } + + void setEdgeThreshold(double edgeThreshold_) CV_OVERRIDE { edgeThreshold = edgeThreshold_; } + double getEdgeThreshold() const CV_OVERRIDE { return edgeThreshold; } + + void setSigma(double sigma_) CV_OVERRIDE { sigma = sigma_; } + double getSigma() const CV_OVERRIDE { return sigma; } + protected: CV_PROP_RW int nfeatures; CV_PROP_RW int nOctaveLayers; @@ -554,4 +572,34 @@ void SIFT_Impl::detectAndCompute(InputArray _image, InputArray _mask, } } +void SIFT_Impl::read( const FileNode& fn) +{ + // if node is empty, keep previous value + if (!fn["nfeatures"].empty()) + fn["nfeatures"] >> nfeatures; + if (!fn["nOctaveLayers"].empty()) + fn["nOctaveLayers"] >> nOctaveLayers; + if (!fn["contrastThreshold"].empty()) + fn["contrastThreshold"] >> contrastThreshold; + if (!fn["edgeThreshold"].empty()) + fn["edgeThreshold"] >> edgeThreshold; + if (!fn["sigma"].empty()) + fn["sigma"] >> sigma; + if (!fn["descriptorType"].empty()) + fn["descriptorType"] >> descriptor_type; +} +void SIFT_Impl::write( FileStorage& fs) const +{ + if(fs.isOpened()) + { + fs << "name" << getDefaultName(); + fs << "nfeatures" << nfeatures; + fs << "nOctaveLayers" << nOctaveLayers; + fs << "contrastThreshold" << contrastThreshold; + fs << "edgeThreshold" << edgeThreshold; + fs << "sigma" << sigma; + fs << "descriptorType" << descriptor_type; + } +} + } diff --git a/modules/java/check-tests.py b/modules/java/check-tests.py index 29a9c31116..ce29fa3439 100755 --- a/modules/java/check-tests.py +++ b/modules/java/check-tests.py @@ -68,11 +68,11 @@ class JavaParser: if os.path.isfile(path): if path.endswith("FeatureDetector.java"): for prefix1 in ("", "Grid", "Pyramid", "Dynamic"): - for prefix2 in ("FAST", "STAR", "MSER", "ORB", "SIFT", "SURF", "GFTT", "HARRIS", "SIMPLEBLOB", "DENSE"): + for prefix2 in ("FAST", "STAR", "MSER", "ORB", "SIFT", "SURF", "GFTT", "HARRIS", "SIMPLEBLOB", "DENSE", "AKAZE", "KAZE", "BRISK", "AGAST"): parser.parse_file(path,prefix1+prefix2) elif path.endswith("DescriptorExtractor.java"): for prefix1 in ("", "Opponent"): - for prefix2 in ("BRIEF", "ORB", "SIFT", "SURF"): + for prefix2 in ("BRIEF", "ORB", "SIFT", "SURF", "AKAZE", "KAZE", "BEBLID", "DAISY", "FREAK", "LUCID", "LATCH"): parser.parse_file(path,prefix1+prefix2) elif path.endswith("GenericDescriptorMatcher.java"): for prefix in ("OneWay", "Fern"): From bc8d49461776d7606b43e53a3aaf838875d0f03c Mon Sep 17 00:00:00 2001 From: Marco Feuerstein Date: Wed, 21 Dec 2022 15:10:59 +0100 Subject: [PATCH 283/313] Merge pull request #22959 from feuerste:parallel_mertens Parallelize implementation of HDR MergeMertens. * Parallelize MergeMertens. * Added performance tests for HDR. * Ran clang-format. * Optimizations. * Fix data path for Windows. * Remove compiiation warning on Windows. * Remove clang-format for existing file. * Addressing reviewer comments. * Ensure correct summation order. * Add test for determinism. * Move result pyramid into sync struct. * Reuse sync for first loop as well. * Use OpenCV's threading primitives. * Remove cout. --- modules/photo/perf/perf_hdr.cpp | 64 +++++++++++++ modules/photo/src/merge.cpp | 148 +++++++++++++++++-------------- modules/python/test/test_umat.py | 7 ++ 3 files changed, 150 insertions(+), 69 deletions(-) create mode 100644 modules/photo/perf/perf_hdr.cpp diff --git a/modules/photo/perf/perf_hdr.cpp b/modules/photo/perf/perf_hdr.cpp new file mode 100644 index 0000000000..4001a7913b --- /dev/null +++ b/modules/photo/perf/perf_hdr.cpp @@ -0,0 +1,64 @@ +// 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 "perf_precomp.hpp" + +namespace opencv_test +{ +namespace +{ +struct ExposureSeq +{ + std::vector images; + std::vector times; +}; + +ExposureSeq loadExposureSeq(const std::string& list_filename) +{ + std::ifstream list_file(list_filename); + EXPECT_TRUE(list_file.is_open()); + string name; + float val; + const String path(list_filename.substr(0, list_filename.find_last_of("\\/") + 1)); + ExposureSeq seq; + while (list_file >> name >> val) + { + Mat img = imread(path + name); + EXPECT_FALSE(img.empty()) << "Could not load input image " << path + name; + seq.images.push_back(img); + seq.times.push_back(1 / val); + } + list_file.close(); + return seq; +} + +PERF_TEST(HDR, Mertens) +{ + const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt")); + Ptr merge = createMergeMertens(); + Mat result(seq.images.front().size(), seq.images.front().type()); + TEST_CYCLE() merge->process(seq.images, result); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST(HDR, Debevec) +{ + const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt")); + Ptr merge = createMergeDebevec(); + Mat result(seq.images.front().size(), seq.images.front().type()); + TEST_CYCLE() merge->process(seq.images, result, seq.times); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST(HDR, Robertson) +{ + const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt")); + Ptr merge = createMergeRobertson(); + Mat result(seq.images.front().size(), seq.images.front().type()); + TEST_CYCLE() merge->process(seq.images, result, seq.times); + SANITY_CHECK_NOTHING(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/photo/src/merge.cpp b/modules/photo/src/merge.cpp index e6a00fedb8..b0a36d4f6f 100644 --- a/modules/photo/src/merge.cpp +++ b/modules/photo/src/merge.cpp @@ -172,87 +172,97 @@ public: std::vector weights(images.size()); Mat weight_sum = Mat::zeros(size, CV_32F); + Mutex weight_sum_mutex; - for(size_t i = 0; i < images.size(); i++) { - Mat img, gray, contrast, saturation, wellexp; - std::vector splitted(channels); + parallel_for_(Range(0, static_cast(images.size())), [&](const Range& range) { + for(int i = range.start; i < range.end; i++) { + Mat& img = images[i]; + Mat gray, contrast, saturation, wellexp; + std::vector splitted(channels); - images[i].convertTo(img, CV_32F, 1.0f/255.0f); - if(channels == 3) { - cvtColor(img, gray, COLOR_RGB2GRAY); - } else { - img.copyTo(gray); + img.convertTo(img, CV_32F, 1.0f/255.0f); + if(channels == 3) { + cvtColor(img, gray, COLOR_RGB2GRAY); + } else { + img.copyTo(gray); + } + split(img, splitted); + + Laplacian(gray, contrast, CV_32F); + contrast = abs(contrast); + + Mat mean = Mat::zeros(size, CV_32F); + for(int c = 0; c < channels; c++) { + mean += splitted[c]; + } + mean /= channels; + + saturation = Mat::zeros(size, CV_32F); + for(int c = 0; c < channels; c++) { + Mat deviation = splitted[c] - mean; + pow(deviation, 2.0f, deviation); + saturation += deviation; + } + sqrt(saturation, saturation); + + wellexp = Mat::ones(size, CV_32F); + for(int c = 0; c < channels; c++) { + Mat expo = splitted[c] - 0.5f; + pow(expo, 2.0f, expo); + expo = -expo / 0.08f; + exp(expo, expo); + wellexp = wellexp.mul(expo); + } + + pow(contrast, wcon, contrast); + pow(saturation, wsat, saturation); + pow(wellexp, wexp, wellexp); + + weights[i] = contrast; + if(channels == 3) { + weights[i] = weights[i].mul(saturation); + } + weights[i] = weights[i].mul(wellexp) + 1e-12f; + + AutoLock lock(weight_sum_mutex); + weight_sum += weights[i]; } - split(img, splitted); + }); - Laplacian(gray, contrast, CV_32F); - contrast = abs(contrast); - - Mat mean = Mat::zeros(size, CV_32F); - for(int c = 0; c < channels; c++) { - mean += splitted[c]; - } - mean /= channels; - - saturation = Mat::zeros(size, CV_32F); - for(int c = 0; c < channels; c++) { - Mat deviation = splitted[c] - mean; - pow(deviation, 2.0f, deviation); - saturation += deviation; - } - sqrt(saturation, saturation); - - wellexp = Mat::ones(size, CV_32F); - for(int c = 0; c < channels; c++) { - Mat expo = splitted[c] - 0.5f; - pow(expo, 2.0f, expo); - expo = -expo / 0.08f; - exp(expo, expo); - wellexp = wellexp.mul(expo); - } - - pow(contrast, wcon, contrast); - pow(saturation, wsat, saturation); - pow(wellexp, wexp, wellexp); - - weights[i] = contrast; - if(channels == 3) { - weights[i] = weights[i].mul(saturation); - } - weights[i] = weights[i].mul(wellexp) + 1e-12f; - weight_sum += weights[i]; - } int maxlevel = static_cast(logf(static_cast(min(size.width, size.height))) / logf(2.0f)); std::vector res_pyr(maxlevel + 1); + std::vector res_pyr_mutexes(maxlevel + 1); - for(size_t i = 0; i < images.size(); i++) { - weights[i] /= weight_sum; - Mat img; - images[i].convertTo(img, CV_32F, 1.0f/255.0f); + parallel_for_(Range(0, static_cast(images.size())), [&](const Range& range) { + for(int i = range.start; i < range.end; i++) { + weights[i] /= weight_sum; - std::vector img_pyr, weight_pyr; - buildPyramid(img, img_pyr, maxlevel); - buildPyramid(weights[i], weight_pyr, maxlevel); + std::vector img_pyr, weight_pyr; + buildPyramid(images[i], img_pyr, maxlevel); + buildPyramid(weights[i], weight_pyr, maxlevel); - for(int lvl = 0; lvl < maxlevel; lvl++) { - Mat up; - pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size()); - img_pyr[lvl] -= up; - } - for(int lvl = 0; lvl <= maxlevel; lvl++) { - std::vector splitted(channels); - split(img_pyr[lvl], splitted); - for(int c = 0; c < channels; c++) { - splitted[c] = splitted[c].mul(weight_pyr[lvl]); + for(int lvl = 0; lvl < maxlevel; lvl++) { + Mat up; + pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size()); + img_pyr[lvl] -= up; } - merge(splitted, img_pyr[lvl]); - if(res_pyr[lvl].empty()) { - res_pyr[lvl] = img_pyr[lvl]; - } else { - res_pyr[lvl] += img_pyr[lvl]; + for(int lvl = 0; lvl <= maxlevel; lvl++) { + std::vector splitted(channels); + split(img_pyr[lvl], splitted); + for(int c = 0; c < channels; c++) { + splitted[c] = splitted[c].mul(weight_pyr[lvl]); + } + merge(splitted, img_pyr[lvl]); + + AutoLock lock(res_pyr_mutexes[lvl]); + if(res_pyr[lvl].empty()) { + res_pyr[lvl] = img_pyr[lvl]; + } else { + res_pyr[lvl] += img_pyr[lvl]; + } } } - } + }); for(int lvl = maxlevel; lvl > 0; lvl--) { Mat up; pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size()); diff --git a/modules/python/test/test_umat.py b/modules/python/test/test_umat.py index 0081bec546..d8a9a10283 100644 --- a/modules/python/test/test_umat.py +++ b/modules/python/test/test_umat.py @@ -107,12 +107,19 @@ class UMat(NewOpenCVTests): images, _ = load_exposure_seq(os.path.join(test_data_path, 'exposures')) + # As we want to test mat vs. umat here, we temporarily set only one worker-thread to achieve + # deterministic summations inside mertens' parallelized process. + num_threads = cv.getNumThreads() + cv.setNumThreads(1) + merge = cv.createMergeMertens() mat_result = merge.process(images) umat_images = [cv.UMat(img) for img in images] umat_result = merge.process(umat_images) + cv.setNumThreads(num_threads) + self.assertTrue(np.allclose(umat_result.get(), mat_result)) From 5e03305da5ddc312c11948dc073f384169e646e9 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Wed, 21 Dec 2022 17:09:41 +0200 Subject: [PATCH 284/313] build: only disable win32 cuda warnings on CUDA SDK >= 11.2 --- cmake/OpenCVDetectCUDA.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index bc7391a995..74b550e669 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -427,7 +427,7 @@ if(CUDA_FOUND) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fno-finite-math-only) endif() - if(WIN32) + if(CUDA_VERSION VERSION_GREATER_EQUAL 11.2 AND WIN32) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388) endif() From b774753922dd4cc58eb9c710ec5ed950bd2005ae Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 21 Dec 2022 18:28:26 +0100 Subject: [PATCH 285/313] Fix self converTo. We still need images[i] = img because it is used below in buildPyramid. --- modules/photo/src/merge.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/photo/src/merge.cpp b/modules/photo/src/merge.cpp index b0a36d4f6f..18050574ab 100644 --- a/modules/photo/src/merge.cpp +++ b/modules/photo/src/merge.cpp @@ -176,16 +176,16 @@ public: parallel_for_(Range(0, static_cast(images.size())), [&](const Range& range) { for(int i = range.start; i < range.end; i++) { - Mat& img = images[i]; - Mat gray, contrast, saturation, wellexp; + Mat img, gray, contrast, saturation, wellexp; std::vector splitted(channels); - img.convertTo(img, CV_32F, 1.0f/255.0f); + images[i].convertTo(img, CV_32F, 1.0f/255.0f); if(channels == 3) { cvtColor(img, gray, COLOR_RGB2GRAY); } else { img.copyTo(gray); } + images[i] = img; split(img, splitted); Laplacian(gray, contrast, CV_32F); From 8676d19dc3053a638af621061b180f362a6eee2c Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 22 Dec 2022 04:36:05 +0000 Subject: [PATCH 286/313] videoio(ffmpeg): limit number of default threads --- modules/videoio/src/cap_ffmpeg_impl.hpp | 53 +++++-------------------- 1 file changed, 11 insertions(+), 42 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 1f3e84d940..dfa272639d 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -373,42 +373,6 @@ inline double get_monotonic_time_diff_ms(timespec time1, timespec time2) } #endif // USE_AV_INTERRUPT_CALLBACK -static int get_number_of_cpus(void) -{ -#if defined _WIN32 - SYSTEM_INFO sysinfo; - GetSystemInfo( &sysinfo ); - - return (int)sysinfo.dwNumberOfProcessors; -#elif defined __linux__ || defined __HAIKU__ - return (int)sysconf( _SC_NPROCESSORS_ONLN ); -#elif defined __APPLE__ - int numCPU=0; - int mib[4]; - size_t len = sizeof(numCPU); - - // set the mib for hw.ncpu - mib[0] = CTL_HW; - mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; - - // get the number of CPUs from the system - sysctl(mib, 2, &numCPU, &len, NULL, 0); - - if( numCPU < 1 ) - { - mib[1] = HW_NCPU; - sysctl( mib, 2, &numCPU, &len, NULL, 0 ); - - if( numCPU < 1 ) - numCPU = 1; - } - - return (int)numCPU; -#else - return 1; -#endif -} - struct Image_FFMPEG { @@ -998,12 +962,17 @@ public: inline void fill_codec_context(AVCodecContext * enc, AVDictionary * dict) { -//#ifdef FF_API_THREAD_INIT -// avcodec_thread_init(enc, get_number_of_cpus()); -//#else - const int nCpus = get_number_of_cpus(); - enc->thread_count = enc->thread_count ? enc->thread_count: nCpus; -//#endif + if (!enc->thread_count) + { + int nCpus = cv::getNumberOfCPUs(); + int requestedThreads = std::min(nCpus, 16); // [OPENCV:FFMPEG:24] Application has requested XX threads. Using a thread count greater than 16 is not recommended. + char* threads_option = getenv("OPENCV_FFMPEG_THREADS"); + if (threads_option != NULL) + { + requestedThreads = atoi(threads_option); + } + enc->thread_count = requestedThreads; + } AVDictionaryEntry* avdiscard_entry = av_dict_get(dict, "avdiscard", NULL, 0); From d35fbe6bfc6344682e8bca882ad1349278ae7c78 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 21 Dec 2022 15:37:14 +0300 Subject: [PATCH 287/313] dnn: updated YOLOv4-tiny model and tests --- modules/dnn/perf/perf_net.cpp | 2 +- modules/dnn/test/test_darknet_importer.cpp | 25 +++++++++++----------- modules/dnn/test/test_int8_layers.cpp | 18 ++++++++-------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index 2fb150bb35..cfbb45b173 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -251,7 +251,7 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv4_tiny) cvtColor(sample, sample, COLOR_BGR2RGB); Mat inp; sample.convertTo(inp, CV_32FC3, 1.0f / 255, 0); - processNet("dnn/yolov4-tiny.weights", "dnn/yolov4-tiny.cfg", "", inp); + processNet("dnn/yolov4-tiny-2020-12.weights", "dnn/yolov4-tiny-2020-12.cfg", "", inp); } PERF_TEST_P_(DNNTestNetwork, EAST_text_detection) diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index 4d11193d96..0b4c1bccff 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -562,12 +562,12 @@ TEST_P(Test_Darknet_nets_async, Accuracy) l1 = 0.001; lInf = 0.005; } - if (INF_ENGINE_VER_MAJOR_EQ(2021040000) && targetId == DNN_TARGET_OPENCL_FP16 && prefix == "yolov4-tiny") // FIXIT: 4.x only, 3.4 branch works well + if (INF_ENGINE_VER_MAJOR_EQ(2021040000) && targetId == DNN_TARGET_OPENCL_FP16 && prefix == "yolov4-tiny-2020-12") // FIXIT: 4.x only, 3.4 branch works well { l1 = 0.001; lInf = 0.005; } - if (INF_ENGINE_VER_MAJOR_EQ(2022010000) && targetId == DNN_TARGET_OPENCL_FP16 && prefix == "yolov4-tiny") // FIXIT: 4.x only, 3.4 branch works well + if (INF_ENGINE_VER_MAJOR_EQ(2022010000) && targetId == DNN_TARGET_OPENCL_FP16 && prefix == "yolov4-tiny-2020-12") // FIXIT: 4.x only, 3.4 branch works well { l1 = 0.001; lInf = 0.005; @@ -594,7 +594,7 @@ TEST_P(Test_Darknet_nets_async, Accuracy) } INSTANTIATE_TEST_CASE_P(/**/, Test_Darknet_nets_async, Combine( - Values("yolo-voc", "tiny-yolo-voc", "yolov3", "yolov4", "yolov4-tiny"), + Values("yolo-voc", "tiny-yolo-voc", "yolov3", "yolov4", "yolov4-tiny-2020-12"), dnnBackendsAndTargets() )); @@ -827,25 +827,26 @@ TEST_P(Test_Darknet_nets, YOLOv4_tiny) const double confThreshold = 0.5; // batchId, classId, confidence, left, top, right, bottom - const int N0 = 2; + const int N0 = 3; const int N1 = 3; static const float ref_[/* (N0 + N1) * 7 */] = { -0, 7, 0.85935f, 0.593484f, 0.141211f, 0.920356f, 0.291593f, -0, 16, 0.795188f, 0.169207f, 0.386886f, 0.423753f, 0.933004f, +0, 16, 0.889883f, 0.177204f, 0.356279f, 0.417204f, 0.937517f, +0, 7, 0.816615f, 0.604293f, 0.137345f, 0.918016f, 0.295708f, +0, 1, 0.595912f, 0.0940107f, 0.178122f, 0.750619f, 0.829336f, -1, 2, 0.996832f, 0.653802f, 0.464573f, 0.815193f, 0.653292f, -1, 2, 0.963325f, 0.451151f, 0.458915f, 0.496255f, 0.52241f, -1, 0, 0.926244f, 0.194851f, 0.361743f, 0.260277f, 0.632364f, +1, 2, 0.998224f, 0.652883f, 0.463477f, 0.813952f, 0.657163f, +1, 2, 0.967396f, 0.4539f, 0.466368f, 0.497716f, 0.520299f, +1, 0, 0.807866f, 0.205039f, 0.361842f, 0.260984f, 0.643621f, }; Mat ref(N0 + N1, 7, CV_32FC1, (void*)ref_); - double scoreDiff = 0.01f; + double scoreDiff = 0.012f; double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.15 : 0.01f; if (target == DNN_TARGET_CUDA_FP16) iouDiff = 0.02; - std::string config_file = "yolov4-tiny.cfg"; - std::string weights_file = "yolov4-tiny.weights"; + std::string config_file = "yolov4-tiny-2020-12.cfg"; + std::string weights_file = "yolov4-tiny-2020-12.weights"; #if defined(INF_ENGINE_RELEASE) if (target == DNN_TARGET_MYRIAD) // bad accuracy diff --git a/modules/dnn/test/test_int8_layers.cpp b/modules/dnn/test/test_int8_layers.cpp index 1cafa22619..3551ee239f 100644 --- a/modules/dnn/test/test_int8_layers.cpp +++ b/modules/dnn/test/test_int8_layers.cpp @@ -1320,19 +1320,19 @@ TEST_P(Test_Int8_nets, YOLOv4_tiny) const int N0 = 2; const int N1 = 3; static const float ref_[/* (N0 + N1) * 7 */] = { -0, 7, 0.85935f, 0.593484f, 0.141211f, 0.920356f, 0.291593f, -0, 16, 0.795188f, 0.169207f, 0.386886f, 0.423753f, 0.933004f, +0, 16, 0.912199f, 0.169926f, 0.350896f, 0.422704f, 0.941837f, +0, 7, 0.845388f, 0.617568f, 0.13961f, 0.9008f, 0.29315f, -1, 2, 0.996832f, 0.653802f, 0.464573f, 0.815193f, 0.653292f, -1, 2, 0.963325f, 0.451151f, 0.458915f, 0.496255f, 0.52241f, -1, 0, 0.926244f, 0.194851f, 0.361743f, 0.260277f, 0.632364f, +1, 2, 0.997789f, 0.657455f, 0.459714f, 0.809122f, 0.656829f, +1, 2, 0.924423f, 0.442872f, 0.470127f, 0.49816f, 0.516516f, +1, 0, 0.728307f, 0.202607f, 0.369828f, 0.259445f, 0.613846f, }; Mat ref(N0 + N1, 7, CV_32FC1, (void*)ref_); - std::string config_file = "yolov4-tiny.cfg"; - std::string weights_file = "yolov4-tiny.weights"; + std::string config_file = "yolov4-tiny-2020-12.cfg"; + std::string weights_file = "yolov4-tiny-2020-12.weights"; double scoreDiff = 0.12; - double iouDiff = target == DNN_TARGET_OPENCL_FP16 ? 0.2 : 0.082; + double iouDiff = target == DNN_TARGET_OPENCL_FP16 ? 0.2 : 0.118; { SCOPED_TRACE("batch size 1"); @@ -1340,7 +1340,7 @@ TEST_P(Test_Int8_nets, YOLOv4_tiny) { SCOPED_TRACE("Per-tensor quantize"); - testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, 0.16, 0.7, 0.4, false); + testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, 0.224, 0.7, 0.4, false); } } From b5400902a72c0e1bf7d7c811ad16123edd08a18c Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 22 Dec 2022 17:01:21 +0300 Subject: [PATCH 288/313] Merge pull request #23002 from alalek:issue_22206 * obj-c: de-duplicate values of nested enums - prefix with outer class name * obj-c: handle enum names change in assigned values * obj-c: switch on 'const_fix' * obj-c: add NS_SWIFT_NAME --- modules/objc/generator/gen_objc.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py index 83029312b9..33f49c20ad 100755 --- a/modules/objc/generator/gen_objc.py +++ b/modules/objc/generator/gen_objc.py @@ -203,6 +203,7 @@ class ConstInfo(GeneralInfo): def __init__(self, decl, addedManually=False, namespaces=[], enumType=None): GeneralInfo.__init__(self, "const", decl, namespaces) self.cname = get_cname(self.name) + self.swift_name = None self.value = decl[1] self.enumType = enumType self.addedManually = addedManually @@ -780,14 +781,27 @@ class ObjectiveCWrapperGenerator(object): logging.info('ignored: %s', constinfo) else: objc_type = enumType.rsplit(".", 1)[-1] if enumType else "" - if constinfo.classname in const_fix and objc_type in const_fix[constinfo.classname] and constinfo.name in const_fix[constinfo.classname][objc_type]: - fixed_const = const_fix[constinfo.classname][objc_type][constinfo.name] - constinfo.name = fixed_const - constinfo.cname = fixed_const + if constinfo.enumType and constinfo.classpath: + new_name = constinfo.classname + '_' + constinfo.name + const_fix.setdefault(constinfo.classpath, {}).setdefault(objc_type, {})[constinfo.name] = new_name + constinfo.swift_name = constinfo.name + constinfo.name = new_name + logging.info('use outer class prefix: %s', constinfo) + + if constinfo.classpath in const_fix and objc_type in const_fix[constinfo.classpath]: + fixed_consts = const_fix[constinfo.classpath][objc_type] + if constinfo.name in fixed_consts: + fixed_const = fixed_consts[constinfo.name] + constinfo.name = fixed_const + constinfo.cname = fixed_const + if constinfo.value in fixed_consts: + constinfo.value = fixed_consts[constinfo.value] if not self.isWrapped(constinfo.classname): logging.info('class not found: %s', constinfo) - constinfo.name = constinfo.classname + '_' + constinfo.name + if not constinfo.name.startswith(constinfo.classname + "_"): + constinfo.swift_name = constinfo.name + constinfo.name = constinfo.classname + '_' + constinfo.name constinfo.classname = '' ci = self.getClass(constinfo.classname) @@ -1294,7 +1308,9 @@ $unrefined_call$epilogue$ret ci.enum_declarations.write(""" // C++: enum {1} ({2}) typedef NS_ENUM(int, {1}) {{ - {0}\n}};\n\n""".format(",\n ".join(["%s = %s" % (c.name, c.value) for c in consts]), typeNameShort, typeName) + {0}\n}};\n\n""".format( + ",\n ".join(["%s = %s" % (c.name + (" NS_SWIFT_NAME(" + c.swift_name + ")" if c.swift_name else ""), c.value) for c in consts]), + typeNameShort, typeName) ) else: if not wrote_consts_pragma: From a32100d9ba57b05c28d91aa0e39f045633937704 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 22 Dec 2022 17:29:22 +0300 Subject: [PATCH 289/313] Valgrind issues fix in QRCode detector. --- modules/objdetect/src/qrcode.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index a62b90bbe8..41c604a8db 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -2340,9 +2340,8 @@ static inline std::pair matchPatternPoints(const vector &finder double QRDecode::getNumModules() { vector> finderPatterns; double numModulesX = 0., numModulesY = 0.; - bool flag = findPatternsVerticesPoints(finderPatterns); - if (flag) { - double pattern_distance[4]; + if (findPatternsVerticesPoints(finderPatterns)) { + double pattern_distance[4] = {0.,0.,0.,0.}; for (auto& pattern : finderPatterns) { auto indexes = matchPatternPoints(pattern, original_points); if (indexes == std::make_pair(-1, -1)) @@ -2558,8 +2557,9 @@ bool QRDecode::versionDefinition() bool useFinderPattern = false; const double thresholdFinderPattern = 0.2; const double roundingError = abs(numModulesByFinderPattern - cvRound(numModulesByFinderPattern)); - if (cvRound(versionByFinderPattern) >= 1 && versionByFinderPattern <= 6 && transition_x != transition_y) { - if (roundingError < thresholdFinderPattern) + + if (cvRound(versionByFinderPattern) >= 1 && versionByFinderPattern <= 6. && + transition_x != transition_y && roundingError < thresholdFinderPattern) { useFinderPattern = true; } @@ -2775,7 +2775,12 @@ bool QRDecode::curvedDecodingProcess() #endif } -QRDecode::QRDecode(bool _useAlignmentMarkers): useAlignmentMarkers(_useAlignmentMarkers) {} +QRDecode::QRDecode(bool _useAlignmentMarkers): + useAlignmentMarkers(_useAlignmentMarkers), + version(0), + version_size(0), + test_perspective_size(0.f) + {} std::string QRCodeDetector::decode(InputArray in, InputArray points, OutputArray straight_qrcode) From 34a0897f90db1656f753ed797b438484cf19da56 Mon Sep 17 00:00:00 2001 From: fengyuentau Date: Thu, 1 Dec 2022 17:41:54 +0800 Subject: [PATCH 290/313] add cv::flipND; support onnx slice with negative steps via cv::flipND --- modules/core/include/opencv2/core.hpp | 7 ++ modules/core/src/matrix_transform.cpp | 45 +++++++++++ modules/core/test/test_arithm.cpp | 66 ++++++++++++++++ modules/dnn/src/layers/slice_layer.cpp | 100 ++++++++++++++++++------ modules/dnn/test/test_onnx_importer.cpp | 1 + 5 files changed, 193 insertions(+), 26 deletions(-) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 605909d339..7038da94b2 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -1102,6 +1102,13 @@ around both axes. */ CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode); +/** @brief Flips a n-dimensional at given axis + * @param src input array + * @param dst output array that has the same shape of src + * @param axis axis that performs a flip on. 0 <= axis < src.dims. + */ +CV_EXPORTS_W void flipND(InputArray src, OutputArray dst, int axis); + enum RotateFlags { ROTATE_90_CLOCKWISE = 0, //! // std::swap_ranges + namespace cv { ////////////////////////////////////// transpose ///////////////////////////////////////// @@ -812,6 +814,49 @@ void flip( InputArray _src, OutputArray _dst, int flip_mode ) flipHoriz( dst.ptr(), dst.step, dst.ptr(), dst.step, dst.size(), esz ); } +static void +flipNDImpl(uchar* data, const int* shape, const size_t* step, int axis) +{ + int total = 1; + for (int i = 0; i < axis; ++i) + total *= shape[i]; + + int shape_at_axis = shape[axis]; + size_t step_at_axis = step[axis]; + size_t offset = 0; + size_t offset_increment = axis == 0 ? 0 : step[axis - 1]; + for (int i = 0; i < total; ++i, offset += offset_increment) + for (int j = 0, k = shape_at_axis - 1; j < shape_at_axis / 2; ++j, --k) + std::swap_ranges(data + offset + j * step_at_axis, + data + offset + j * step_at_axis + step_at_axis, + data + offset + k * step_at_axis); +} + +void flipND(InputArray _src, OutputArray _dst, int _axis) +{ + CV_INSTRUMENT_REGION(); + + Mat src = _src.getMat(); + + // verify axis + int ndim = src.dims; + CV_CheckLT(_axis, ndim, "flipND: given axis is out of range"); + CV_CheckGE(_axis, -ndim, "flipND: given axis is out of range"); + int axis = (_axis + ndim) % ndim; + + // in-place flip + _src.copyTo(_dst); + + // return the src if it has only one element on the flip axis + const auto shape = src.size.p; + if (shape[axis] == 1) + return ; + + // call impl + Mat dst = _dst.getMat(); + flipNDImpl(dst.ptr(), dst.size.p, dst.step.p, axis); +} + void rotate(InputArray _src, OutputArray _dst, int rotateMode) { CV_Assert(_src.dims() <= 2); diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 6d50b5a8f7..ea9cda56be 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -2201,6 +2201,72 @@ INSTANTIATE_TEST_CASE_P(Arithm, TransposeND, testing::Combine( testing::Values(perf::MatType(CV_8UC1), CV_32FC1) )); +class FlipND : public testing::TestWithParam< tuple, perf::MatType> > +{ +public: + std::vector m_shape; + int m_type; + + void SetUp() + { + std::tie(m_shape, m_type) = GetParam(); + } +}; + +TEST_P(FlipND, basic) +{ + Mat inp(m_shape, m_type); + randu(inp, 0, 255); + + int ndim = static_cast(m_shape.size()); + std::vector axes(ndim*2); // [-shape, shape) + std::iota(axes.begin(), axes.end(), -ndim); + auto get_flipped_indices = [&inp, ndim] (size_t total, std::vector& indices, int axis) + { + const int* shape = inp.size.p; + size_t t = total, idx; + for (int i = ndim - 1; i >= 0; --i) + { + idx = t / shape[i]; + indices[i] = int(t - idx * shape[i]); + t = idx; + } + + int _axis = (axis + ndim) % ndim; + std::vector flipped_indices = indices; + flipped_indices[_axis] = shape[_axis] - 1 - indices[_axis]; + return flipped_indices; + }; + + for (size_t i = 0; i < axes.size(); ++i) + { + int axis = axes[i]; + Mat out; + cv::flipND(inp, out, axis); + // check values + std::vector indices(ndim, 0); + for (size_t j = 0; j < inp.total(); ++j) + { + auto flipped_indices = get_flipped_indices(j, indices, axis); + switch (inp.type()) + { + case CV_8UC1: + ASSERT_EQ(inp.at(indices.data()), out.at(flipped_indices.data())); + break; + case CV_32FC1: + ASSERT_EQ(inp.at(indices.data()), out.at(flipped_indices.data())); + break; + default: + FAIL() << "Unsupported type: " << inp.type(); + } + } + } +} + +INSTANTIATE_TEST_CASE_P(Arithm, FlipND, testing::Combine( + testing::Values(std::vector{5, 10}, std::vector{2, 3, 4}), + testing::Values(perf::MatType(CV_8UC1), CV_32FC1) +)); TEST(Core_minMaxIdx, regression_9207_2) { diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp index d646b6c058..bea497badd 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -84,6 +84,34 @@ Range normalizeRange(const Range& input_range, int n) return range; } +// TODO: support cv::Range with steps and negative steps to get rid of this transformation +void tranformForNegSteps(const MatShape& inpShape, std::vector >& sliceRanges, std::vector >& sliceSteps) +{ + // in case of negative steps, + // x of shape [5, 10], x[5:0:-1, 10:1:-3] <=> np.flip(x[1:5:1, 2:10:3], aixs=(0, 1)) + // new_end_i = start_i + 1 > dim_i ? dim_i : start_i + 1 + // new_start_i = end + 1 + // new_start_i = new_end_i - 1 - ((new_end_i - 1 - new_start_i) / abs(step_i)) * abs(step_i) + int start, end, new_start, new_end, step; + for (int i = 0; i < sliceSteps[0].size(); ++i) + { + step = sliceSteps[0][i]; + if (step > 0) + continue; + + step = -step; + start = sliceRanges[0][i].start; + end = sliceRanges[0][i].end; + new_end = start >= inpShape[i] ? inpShape[i] : start + 1; + new_start = end + 1; + new_start = new_end - 1 - ((new_end - 1 - new_start) / step) * step; + + sliceSteps[0][i] = step; + sliceRanges[0][i].start = new_start; + sliceRanges[0][i].end = new_end; + } +} + std::vector > finalizeSliceRange(const MatShape& inpShape, int& axis, const std::vector >& inputSliceRanges) { @@ -149,6 +177,24 @@ public: const DictValue &sizesOrEnds = params.has("size") ? params.get("size") : params.get("end"); CV_Assert(begins.size() == sizesOrEnds.size()); + if (params.has("steps")) + { + const DictValue &steps = params.get("steps"); + sliceSteps.resize(1); + sliceSteps[0].resize(steps.size()); + + for (int i = 0; i < steps.size(); ++i) + { + int step = steps.get(i); + CV_Assert(step != 0); + if (step < 0) + neg_step_dims.push_back(i); + if (std::abs(step) > 1) + hasSteps = true; + sliceSteps[0][i] = step; + } + } + sliceRanges.resize(1); sliceRanges[0].resize(begins.size(), Range::all()); for (int i = 0; i < begins.size(); ++i) @@ -166,26 +212,13 @@ public: else { int end = sizeOrEnd; - CV_Assert(end < 0 || end > start); // End index is excluded. + if (hasSteps && !neg_step_dims.empty() && sliceSteps[0][i] < 0) + CV_Assert(end < 0 || end != start); // if current step is negative, end < start is allowed. + else + CV_Assert(end < 0 || end > start); // End index is excluded. sliceRanges[0][i].end = end; // We'll finalize a negative value later. } } - - if (params.has("steps")) - { - const DictValue &steps = params.get("steps"); - sliceSteps.resize(1); - sliceSteps[0].resize(steps.size()); - - for (int i = 0; i < steps.size(); ++i) - { - int step = steps.get(i); - CV_Assert(step >= 1); - if (step > 1) - hasSteps = true; - sliceSteps[0][i] = step; - } - } } } @@ -193,11 +226,11 @@ public: { #ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) - return sliceRanges.size() == 1 && !hasSteps; + return sliceRanges.size() == 1 && !hasSteps && neg_step_dims.empty(); #endif #ifdef HAVE_CUDA if (backendId == DNN_BACKEND_CUDA) - return !hasSteps; + return !hasSteps && neg_step_dims.empty(); #endif return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CANN; } @@ -210,8 +243,13 @@ public: CV_Assert(inputs.size() == 1); MatShape inpShape = inputs[0]; + std::vector > sliceSteps_ = sliceSteps; + std::vector > sliceRanges_ = sliceRanges; + if (hasSteps && !neg_step_dims.empty()) + tranformForNegSteps(inpShape, sliceRanges_, sliceSteps_); + int axis_rw = axis; - std::vector > sliceRanges_rw = finalizeSliceRange(inpShape, axis_rw, sliceRanges); + std::vector > sliceRanges_rw = finalizeSliceRange(inpShape, axis_rw, sliceRanges_); if (!sliceRanges_rw.empty()) { @@ -224,8 +262,8 @@ public: if (shapesInitialized || inpShape[j] > 0) outputs[i][j] = normalizeRange(sliceRanges_rw[i][j], inpShape[j]).size(); - if (!sliceSteps.empty() && (i < sliceSteps.size()) && (j < sliceSteps[i].size()) && (sliceSteps[i][j] > 1)) - outputs[i][j] = (outputs[i][j] + sliceSteps[i][j] - 1) / sliceSteps[i][j]; + if (!sliceSteps_.empty() && (i < sliceSteps_.size()) && (j < sliceSteps_[i].size()) && (sliceSteps_[i][j] > 1)) + outputs[i][j] = (outputs[i][j] + sliceSteps_[i][j] - 1) / sliceSteps_[i][j]; } } } @@ -257,7 +295,10 @@ public: outputs_arr.getMatVector(outputs); CV_Assert(inputs.size() == 1); - const MatSize& inpShape = inputs[0].size; + MatShape inpShape = shape(inputs[0]); + + if (hasSteps && !neg_step_dims.empty()) + tranformForNegSteps(inpShape, sliceRanges, sliceSteps); finalSliceRanges = finalizeSliceRange(shape(inputs[0]), axis, sliceRanges); @@ -280,9 +321,9 @@ public: for (int i = 0; i < outputs.size(); ++i) { - CV_Assert(finalSliceRanges[i].size() <= inpShape.dims()); + CV_Assert(finalSliceRanges[i].size() <= inpShape.size()); // Fill the rest of ranges. - for (int j = finalSliceRanges[i].size(); j < inpShape.dims(); ++j) + for (int j = finalSliceRanges[i].size(); j < inpShape.size(); ++j) { finalSliceRanges[i].push_back(Range::all()); } @@ -586,6 +627,8 @@ public: getSliceRecursive(inpMat, inpIdx, finalSliceRanges[i], sliceSteps[i], 0, dimsNum, outputs[i], outIdx); else getSliceRecursive(inpMat, inpIdx, finalSliceRanges[i], sliceSteps[i], 0, dimsNum, outputs[i], outIdx); + // flip for negative steps + flip(outputs[i]); } } } @@ -650,7 +693,6 @@ public: } #endif - #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE @@ -739,9 +781,15 @@ private: } } + void flip(Mat& output) // break if 1d tensor? + { + for (int i = 0; i < neg_step_dims.size(); ++i) + cv::flipND(output, output, neg_step_dims[i]); + } protected: // The actual non-negative values determined from @p sliceRanges depends on input size. std::vector > finalSliceRanges; + std::vector neg_step_dims; bool hasDynamicShapes; bool shapesInitialized; bool hasSteps; diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 4546dccbb8..3803334b59 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -1145,6 +1145,7 @@ TEST_P(Test_ONNX_layers, Slice) testONNXModels("slice"); testONNXModels("slice_neg_starts"); testONNXModels("slice_opset_11"); + testONNXModels("slice_neg_steps", pb); #endif } From 71c6339af0c0f50df754c2196a2de83e1211c42b Mon Sep 17 00:00:00 2001 From: zihaomu Date: Fri, 23 Dec 2022 16:50:28 +0800 Subject: [PATCH 291/313] remove old convolution branch, and optimize conv3d and conv1d. --- modules/dnn/src/layers/convolution_layer.cpp | 841 +----------------- .../depthwise_convolution.cpp | 645 +++++++------- .../fast_convolution.avx2.cpp | 329 +------ .../fast_convolution/fast_convolution.cpp | 753 ++++++++++++---- .../fast_convolution/fast_convolution.hpp | 60 +- .../fast_convolution.simd.hpp | 282 +++++- .../fast_convolution/winograd_3x3s1_f63.cpp | 4 +- modules/dnn/src/layers/layers_common.simd.hpp | 806 ----------------- 8 files changed, 1283 insertions(+), 2437 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index cc39593232..0bdb0df261 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -259,7 +259,7 @@ public: std::vector reluslope; Ptr activ; - Ptr fastConv2dImpl; + Ptr fastConvImpl; #ifdef HAVE_OPENCL Ptr > convolutionOp; @@ -967,808 +967,6 @@ public: } #endif // HAVE_WEBNN - class ParallelConv : public cv::ParallelLoopBody - { - public: - enum { BLK_SIZE = 32, BLK_SIZE_CN = 64 }; - - const Mat* input_; - const Mat* weights_; - Mat* output_; - int outShape[4]; // used only for conv2d - std::vector kernel_size, pads_begin, pads_end, strides, dilations; - int ngroups_, nstripes_; - std::vector ofstab_; - const std::vector* biasvec_; - const std::vector* reluslope_; - const ActivationLayer* activ_; - bool is1x1_; - bool useAVX; - bool useAVX2; - bool useAVX512; - bool useRVV; - bool useLASX; - int blk_size_cn; - - ParallelConv() - : input_(0), weights_(0), output_(0), ngroups_(0), nstripes_(0), - biasvec_(0), reluslope_(0), activ_(0), is1x1_(false), useAVX(false), useAVX2(false), useAVX512(false), useRVV(false) - , useLASX(false), blk_size_cn(0) - {} - - static void run( const Mat& input, Mat& output, const Mat& weights, - const std::vector& biasvec, - const std::vector& reluslope, - const std::vector& kernel_size, const std::vector& strides, - const std::vector& pads_begin, const std::vector& pads_end, - const std::vector& dilations, - const ActivationLayer* activ, int ngroups, int nstripes ) - { - size_t karea = std::accumulate(kernel_size.begin(), kernel_size.end(), - 1, std::multiplies()); - bool isConv1D = input.dims == 3; - bool isConv2D = input.dims == 4; - bool isConv3D = input.dims == 5; - CV_CheckEQ(static_cast(kernel_size.size()), input.dims - 2, ""); - CV_Assert_N(input.dims == output.dims, - input.size[0] == output.size[0], - weights.rows == output.size[1], - weights.cols == (input.size[1]/ngroups)*karea, - input.type() == output.type(), - input.type() == weights.type(), - input.type() == CV_32FC1, - input.isContinuous(), - output.isContinuous(), - biasvec.size() == (size_t)output.size[1]+2); - CV_Check(weights.step1(), weights.step1() % VEC_ALIGN == 0, ""); - CV_CheckType(weights.type(), CV_32FC1, ""); - ParallelConv p; - - p.input_ = &input; - p.weights_ = &weights; - p.output_ = &output; - int max_ind = isConv1D? 3: 4; - for( int i = 0; i < max_ind; i++ ) p.outShape[i] = output.size[i]; - p.outShape[1] /= ngroups; - - p.kernel_size = kernel_size; p.strides = strides; p.dilations = dilations; - p.pads_begin = pads_begin; p.pads_end = pads_end; - - p.ngroups_ = ngroups; - p.nstripes_ = nstripes; - - int inpCnAll = input.size[1]; - int depth = (input.dims == 5) ? input.size[2] : 1; - int width = input.size[input.dims - 1]; - int height = isConv1D? 1 : input.size[input.dims - 2]; - int inpCn = inpCnAll / ngroups; - - p.is1x1_ = (isConv2D && kernel_size[0] == 1 && kernel_size[1] == 1 && - pads_begin[0] == 0 && pads_begin[1] == 0) || - (isConv1D && pads_begin[0] == 0 && kernel_size[0] == 1); - - p.useAVX = checkHardwareSupport(CPU_AVX) && isConv2D; - p.useAVX2 = checkHardwareSupport(CPU_AVX2) && isConv2D; - p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX && isConv2D; - p.useRVV = checkHardwareSupport(CPU_RVV) && isConv2D; - p.useLASX = checkHardwareSupport(CPU_LASX) && isConv2D; - - int kernel_d = isConv3D? kernel_size[0] : 1; - int kernel_h = isConv1D? 1 : kernel_size[kernel_size.size() - 2]; - int kernel_w = kernel_size.back(); - - int blk_size_cn0 = cvCeil(800./(kernel_w*kernel_h)); - int ncn = 16; - while (ncn*2 < blk_size_cn0 && ncn < inpCn) - ncn *= 2; - ncn = std::min(ncn, inpCn); - p.blk_size_cn = ncn; - - int dil_d = isConv3D? dilations[0] : 1; - int dil_h = isConv1D? 1 : dilations[dilations.size() - 2]; - int dil_w = dilations.back(); - - p.ofstab_.resize(karea * ncn); - int* ofstab = &p.ofstab_[0]; - - if (isConv1D) - { - for( int k = 0; k < ncn; k++ ) - for( int k_c = 0; k_c < kernel_w; k_c++ ) - ofstab[k*kernel_w + k_c] = k*width + k_c*dil_w; - } - else if (isConv2D) - { - for( int k = 0; k < ncn; k++ ) - for( int k_r = 0; k_r < kernel_h; k_r++ ) - for( int k_c = 0; k_c < kernel_w; k_c++ ) - ofstab[(k*kernel_h + k_r)*kernel_w + k_c] = - (k*height + k_r*dil_h)*width + k_c*dil_w; - } - else - { - for( int k = 0; k < ncn; k++ ) - for (int k_d = 0; k_d < kernel_d; k_d++) - for( int k_r = 0; k_r < kernel_h; k_r++ ) - for( int k_c = 0; k_c < kernel_w; k_c++ ) - ofstab[(k*kernel_d*kernel_h + k_d*kernel_h + k_r)*kernel_w + k_c] = - (k*depth*height + k_d*dil_d*height + k_r*dil_h)*width + k_c*dil_w; - } - - p.biasvec_ = &biasvec; - p.reluslope_ = &reluslope; - p.activ_ = p.reluslope_->empty() ? activ : 0; - - parallel_for_(Range(0, nstripes), p, nstripes); - } - - virtual void operator ()(const Range &r0) const CV_OVERRIDE - { - const int valign = ConvolutionLayerImpl::VEC_ALIGN; - int ngroups = ngroups_, batchSize = input_->size[0]*ngroups; - bool isConv1D = input_->dims == 3; - bool isConv2D = input_->dims == 4; - bool isConv3D = input_->dims == 5; - - int outW = output_->size[output_->dims - 1]; - int outH = isConv1D? 1 : output_->size[output_->dims - 2]; - int outCn = output_->size[1]/ngroups; - - int depth = isConv3D? input_->size[2] : 1; - int height = isConv1D? 1 : input_->size[input_->dims - 2]; - int width = input_->size[input_->dims - 1]; - int inpCn = input_->size[1]/ngroups; - - const int nstripes = nstripes_; - - int kernel_d = isConv3D? kernel_size[0] : 1; - int kernel_h = isConv1D? 1 : kernel_size[kernel_size.size() - 2]; - int kernel_w = kernel_size.back(); - int karea = kernel_w*kernel_h*kernel_d; - - int pad_d = isConv3D? pads_begin[0] : 0; - int pad_t = isConv1D? 0 : pads_begin[pads_begin.size() - 2]; - int pad_l = pads_begin.back(); - - int stride_d = isConv3D? strides[0] : 0; - int stride_h = isConv1D? 0 : strides[strides.size() - 2]; - int stride_w = strides.back(); - - int dilation_d = isConv3D? dilations[0] : 1; - int dilation_h = isConv1D? 1 : dilations[dilations.size() - 2]; - int dilation_w = dilations.back(); - - int i, j, k, d; - int inpPlaneSize = (int)input_->total(2); - int outPlaneSize = (int)output_->total(2); - bool is1x1 = is1x1_; - - int stripesPerSample; - int stripeSize; - Range r = r0; - bool depthWiseConvolution = !is1x1 && isConv2D && ngroups > 1 && inpCn == 1 && - outCn == 1 && kernel_d == 1 && dilation_d == 1 && stride_d == 0 && pad_d == 0 && - width >= 16 + dilation_w*(kernel_w - 1); - // for now only 3x3 depth-wise convolutions are supported - depthWiseConvolution = depthWiseConvolution && kernel_w == 3 && kernel_h == 3 && - // computing at most 1 pixel from each side can involve padding - max(stride_w, dilation_w) >= pad_l && max(stride_h, dilation_h) >= pad_t && - pad_l <= 1 && pad_t <= 1; - - if( !depthWiseConvolution && nstripes >= batchSize*2 ) - { - stripesPerSample = nstripes/batchSize; - stripeSize = (int)alignSize((outPlaneSize + stripesPerSample - 1)/stripesPerSample, valign); - stripeSize = std::min(stripeSize, outPlaneSize); - } - else - { - stripesPerSample = 1; - int samplesPerStripe = std::max((batchSize + nstripes - 1)/nstripes, 1); - r.start *= samplesPerStripe; - r.end *= samplesPerStripe; - stripeSize = outPlaneSize; - } - - const float* data_inp0_ = input_->ptr(); - const int* ofstab = &ofstab_[0]; - const float* wptr_orig_ = weights_->ptr(); - size_t wstep = weights_->step1(); - const float* biasptr_ = &biasvec_->at(0); - const float* reluptr_ = reluslope_->empty() ? 0 : &reluslope_->at(0); - float* data_out0_ = output_->ptr(); - AutoBuffer rowbuf0_; - float* rowbuf0 = 0; - bool use_rowbuf = !depthWiseConvolution; - int blk_size = depthWiseConvolution ? outPlaneSize : min((int)BLK_SIZE, stripeSize); - - // im2row buffer is not used for depth-wise convolution - if(use_rowbuf) - { - size_t rowbufsz = alignSize(karea*blk_size_cn, valign)*min((int)BLK_SIZE, blk_size); - //printf("karea=%d, blk_size_cn=%d, rowbufsz=%d, stripeSize=%d\n", karea, blk_size_cn, (int)rowbufsz, stripeSize); - rowbuf0_.allocate(rowbufsz + valign); - rowbuf0 = alignPtr(rowbuf0_.data(), (int)(valign*sizeof(float))); - // we clear the buffer once; ultimately, it lets us to avoid - // tail processing after running the unrolled/vectorized loop. - // the main idea is to make sure that the tail (a.k.a. padding) of each row - // (i.e. the elements with indices between vsz=karea*ncn and vsz_a) - // does not contain NaNs or Infs. Because the padding in the weights - // matrix is explicitly initialized with 0's, we handle all other - // cases nicely, i.e. we can skip expliciting re-initialization - // of the padding - we just retain elements from the previous iteration - // of the loop over channels (cn0). - memset(rowbuf0, 0, rowbufsz*sizeof(rowbuf0[0]) ); - } - - for( int stripe = r.start; stripe < r.end; stripe++ ) - { - int subsampleIdx = stripe/stripesPerSample; - if( subsampleIdx >= batchSize ) - break; - int stripeStart = (int)((stripe - subsampleIdx*stripesPerSample)*stripeSize); - int stripeEnd = (int)std::min(stripeStart + stripeSize, outPlaneSize); - const float* data_inp0 = data_inp0_ + subsampleIdx*inpPlaneSize*inpCn; - float* data_out0 = data_out0_ + subsampleIdx*outPlaneSize*outCn; - int startOutCn = (subsampleIdx % ngroups)*outCn; - const float* wptr_orig = wptr_orig_ + wstep*startOutCn; - const float* biasptr = biasptr_ + startOutCn; - - for( int cn0 = 0; cn0 < inpCn; cn0 += blk_size_cn ) - { - int cn1 = std::min(cn0 + blk_size_cn, inpCn); - int ncn = cn1 - cn0, vsz = karea*ncn; - int vsz_a = (int)alignSize(vsz, valign); - const float* wptr = wptr_orig + cn0*karea; - // we apply [Channels][P]ReLU (if any) during the final pass only. - const float* relu = cn1 == inpCn && reluptr_ ? reluptr_ + startOutCn : 0; - - for( int ofs0 = stripeStart; ofs0 < stripeEnd; ofs0 += blk_size ) - { - int ofs, ofs1 = std::min(ofs0 + blk_size, stripeEnd); - int bsz = ofs1 - ofs0; - - int out_d = ofs0 / (outH * outW); - int out_i = (ofs0 - out_d * outH * outW) / outW; - int out_j = ofs0 % outW; - - if (depthWiseConvolution) - { - CV_Assert(out_i == 0 && out_j == 0); - int in_d = out_d * stride_d - pad_d; - const float* inptr_ = data_inp0 + (cn0*depth*height + in_d*height)*width; - float* outptr_ = data_out0 + ofs0; - - #if CV_TRY_AVX2 - if(useAVX2) - opt_AVX2::fastDepthwiseConv(wptr, kernel_h, kernel_w, - stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, - biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); - else - #endif - #if CV_TRY_AVX - if(useAVX) - opt_AVX::fastDepthwiseConv(wptr, kernel_h, kernel_w, - stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, - biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); - else - #endif - #if CV_TRY_RVV - if(useRVV) - opt_RVV::fastDepthwiseConv(wptr, kernel_h, kernel_w, - stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, - biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); - else - #endif - #if CV_TRY_LASX - if(useLASX) - opt_LASX::fastDepthwiseConv(wptr, kernel_h, kernel_w, - stride_h, stride_w, dilation_h, dilation_w, pad_t, pad_l, - biasptr, relu, inptr_, height, width, outptr_, out_d, outH, outW); - else - #endif - { - const float w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], - w10 = wptr[3], w11 = wptr[4], w12 = wptr[5], - w20_ = wptr[6], w21_ = wptr[7], w22_ = wptr[8]; - int outW1 = min(outW, (width - dilation_w*(kernel_w - 1) + pad_l)/stride_w); - float relu_coeff = relu ? relu[out_d] : 1.f, bias = biasptr[out_d]; - - for (int out_i = 0; out_i < outH; out_i++) - { - int in_i = out_i * stride_h - pad_t, out_j = 0; - const float* imgptr0 = inptr_ + in_i*width; - const float* imgptr1 = imgptr0 + dilation_h*width; - const float* imgptr2 = imgptr0 + (dilation_h*2)*width; - float out, w00 = w00_, w01 = w01_, w02 = w02_; - float w20 = w20_, w21 = w21_, w22 = w22_; - if (in_i < 0) - { - w00 = w01 = w02 = 0.f; - imgptr0 = imgptr1; - } - else if (in_i + dilation_h*(kernel_h-1) >= height) - { - w20 = w21 = w22 = 0.f; - imgptr2 = imgptr1; - } - float* outptr = outptr_ + out_i*outW; - if (pad_l > 0) - { - out = imgptr0[0]*w01 + imgptr0[dilation_w]*w02 + - imgptr1[0]*w11 + imgptr1[dilation_w]*w12 + - imgptr2[0]*w21 + imgptr2[dilation_w]*w22 + bias; - if (relu) - out = out > 0.f ? out : out*relu_coeff; - outptr[0] = out; - out_j = 1; - } - - #if CV_SIMD - // maybe with AVX or AVX512 strided depthwise convolution - // can be accelerated with vector code, but with 4xfloat vectors - // it's hardly the case - if( stride_w == 1 ) - { - const int VECSZ = v_float32::nlanes; - const int out_delta = VECSZ/stride_w; - v_float32 vw00 = vx_setall_f32(w00), vw01 = vx_setall_f32(w01), vw02 = vx_setall_f32(w02), - vw10 = vx_setall_f32(w10), vw11 = vx_setall_f32(w11), vw12 = vx_setall_f32(w12), - vw20 = vx_setall_f32(w20), vw21 = vx_setall_f32(w21), vw22 = vx_setall_f32(w22); - v_float32 z = vx_setzero_f32(), vbias = vx_setall_f32(bias), vrc = vx_setall_f32(relu_coeff); - for( ; out_j < outW1; out_j += out_delta ) - { - if (out_j + out_delta > outW1) - { - if (out_j <= pad_l) - break; - out_j = outW1 - out_delta; - } - int in_j = out_j * stride_w - pad_l; - v_float32 v00 = vx_load(imgptr0 + in_j), - v01 = vx_load(imgptr0 + in_j + dilation_w), - v02 = vx_load(imgptr0 + in_j + dilation_w*2), - v10 = vx_load(imgptr1 + in_j), - v11 = vx_load(imgptr1 + in_j + dilation_w), - v12 = vx_load(imgptr1 + in_j + dilation_w*2), - v20 = vx_load(imgptr2 + in_j), - v21 = vx_load(imgptr2 + in_j + dilation_w), - v22 = vx_load(imgptr2 + in_j + dilation_w*2); - - v_float32 vout = v00*vw00 + v01*vw01 + v02*vw02 + - v10*vw10 + v11*vw11 + v12*vw12 + - v20*vw20 + v21*vw21 + v22*vw22 + vbias; - if (relu) - vout = v_select(vout > z, vout, vout*vrc); - v_store(outptr + out_j, vout); - } - } - #endif - for (; out_j < outW1; out_j++) - { - int in_j = out_j * stride_w - pad_l; - out = imgptr0[in_j]*w00 + imgptr0[in_j + dilation_w]*w01 + imgptr0[in_j + dilation_w*2]*w02 + - imgptr1[in_j]*w10 + imgptr1[in_j + dilation_w]*w11 + imgptr1[in_j + dilation_w*2]*w12 + - imgptr2[in_j]*w20 + imgptr2[in_j + dilation_w]*w21 + imgptr2[in_j + dilation_w*2]*w22 + bias; - if (relu) - out = out > 0.f ? out : out*relu_coeff; - outptr[out_j] = out; - } - - for (; out_j < outW; out_j++ ) - { - int in_j0 = out_j * stride_w - pad_l, in_j1 = in_j0 + dilation_w, in_j2 = in_j0 + dilation_w*2; - float s0 = 1.f, s1 = 1.f, s2 = 1.f; - if (in_j0 >= width) - { - in_j0 = 0; - s0 = 0.f; - } - if (in_j1 >= width) - { - in_j1 = 0; - s1 = 0.f; - } - if (in_j2 >= width) - { - in_j2 = 0; - s2 = 0.f; - } - out = imgptr0[in_j0]*w00*s0 + imgptr0[in_j1]*w01*s1 + imgptr0[in_j2]*w02*s2 + - imgptr1[in_j0]*w10*s0 + imgptr1[in_j1]*w11*s1 + imgptr1[in_j2]*w12*s2 + - imgptr2[in_j0]*w20*s0 + imgptr2[in_j1]*w21*s1 + imgptr2[in_j2]*w22*s2 + bias; - if (relu) - out = out > 0.f ? out : out*relu_coeff; - outptr[out_j] = out; - } - } - } - continue; - } - - // do im2row for a part of input tensor - float* rowbuf = rowbuf0; - - if (isConv1D) - { - for( ofs = ofs0; ofs < ofs1; out_j = 0, ++out_i ) - { - int delta = std::min(ofs1 - ofs, outW - out_j); - int out_j1 = out_j + delta; - - int in_j = out_j * stride_w - pad_l; - const float* imgptr = data_inp0 + cn0*width + in_j; - ofs += delta; - - // do im2row for a part of input tensor - if( is1x1 ) - { - for( ; out_j < out_j1; out_j++, rowbuf += vsz_a, imgptr += stride_w ) - { - for( k = 0; k < vsz; k++ ) - rowbuf[k] = imgptr[k*inpPlaneSize]; - } - } - else - { - for( ; out_j < out_j1; out_j++, rowbuf += vsz_a, imgptr += stride_w, in_j += stride_w ) - { - // this condition should be true for most of the tensor elements, i.e. - // most of the time the kernel aperture is inside the tensor X-Y plane. - if( out_j + 2 <= out_j1 && 0 <= in_j && in_j + stride_w*2 <= width - (kernel_w-1)*dilation_w ) - { - for( k = 0; k < vsz; k++ ) - { - int k1 = ofstab[k]; - float v0 = imgptr[k1]; - float v1 = imgptr[k1 + stride_w]; - rowbuf[k] = v0; - rowbuf[k+vsz_a] = v1; - } - out_j++; - rowbuf += vsz_a; - imgptr += stride_w; - in_j += stride_w; - } - else - { - int i0 = std::max(0, (-in_j + dilation_w-1)/dilation_w); - int i1 = std::min(kernel_w, (width - in_j + dilation_w-1)/dilation_w); - - // here some non-continuous sub-row of the row will not be - // filled from the tensor; we need to make sure that the uncovered - // elements are explicitly set to 0's. the easiest way is to - // set all the elements to 0's before the loop. - memset(rowbuf, 0, vsz*sizeof(rowbuf[0])); - for( k = 0; k < ncn; k++ ) - { - for( i = i0; i < i1; i++ ) - { - int imgofs = k*width + i*dilation_w; - rowbuf[k*kernel_w + i] = imgptr[imgofs]; - } - } - } - } - } - } - } - else if (isConv2D) - { - if( is1x1 && stride_w == 1 && stride_h == 1 ) - { - const float* imgptr = data_inp0 + (cn0*height + out_i)*width + out_j; - for( int j = 0; j < bsz; j++, rowbuf += vsz_a ) - { - if( j + 4 <= bsz ) - { - k = 0; - #if CV_SIMD128 - for( ; k <= vsz - 4; k += 4 ) - { - const float* inp = imgptr + j + k*inpPlaneSize; - v_float32x4 p0 = v_load(inp), p1 = v_load(inp + inpPlaneSize); - v_float32x4 p2 = v_load(inp + inpPlaneSize*2), p3 = v_load(inp + inpPlaneSize*3); - v_float32x4 r0, r1, r2, r3; - v_transpose4x4(p0, p1, p2, p3, r0, r1, r2, r3); - v_store(rowbuf + k, r0); - v_store(rowbuf + k + vsz_a, r1); - v_store(rowbuf + k + vsz_a*2, r2); - v_store(rowbuf + k + vsz_a*3, r3); - } - #endif - for( ; k < vsz; k++ ) - { - const float* inp = imgptr + j + k*inpPlaneSize; - float v0 = inp[0], v1 = inp[1], v2 = inp[2], v3 = inp[3]; - rowbuf[k] = v0; - rowbuf[k + vsz_a] = v1; - rowbuf[k + vsz_a*2] = v2; - rowbuf[k + vsz_a*3] = v3; - } - j += 3; - rowbuf += vsz_a*3; - } - else - { - for( k = 0; k < vsz; k++ ) - { - rowbuf[k] = imgptr[j + k*inpPlaneSize]; - } - } - } - } - else - for( ofs = ofs0; ofs < ofs1; out_j = 0, ++out_i ) - { - int delta = std::min(ofs1 - ofs, outW - out_j); - int out_j1 = out_j + delta; - - int in_i = out_i * stride_h - pad_t; - int in_j = out_j * stride_w - pad_l; - const float* imgptr = data_inp0 + (cn0*height + in_i)*width + in_j; - ofs += delta; - - // do im2row for a part of input tensor - if( is1x1 ) - { - for( ; out_j < out_j1; out_j++, rowbuf += vsz_a, imgptr += stride_w ) - { - for( k = 0; k < vsz; k++ ) - rowbuf[k] = imgptr[k*inpPlaneSize]; - } - } - else - { - bool ok_i = 0 <= in_i && in_i < height - (kernel_h-1)*dilation_h; - int i0 = std::max(0, (-in_i + dilation_h-1)/dilation_h); - int i1 = std::min(kernel_h, (height - in_i + dilation_h-1)/dilation_h); - - for( ; out_j < out_j1; out_j++, rowbuf += vsz_a, imgptr += stride_w, in_j += stride_w ) - { - // this condition should be true for most of the tensor elements, i.e. - // most of the time the kernel aperture is inside the tensor X-Y plane. - if( ok_i && out_j + 2 <= out_j1 && 0 <= in_j && in_j + stride_w*2 <= width - (kernel_w-1)*dilation_w ) - { - for( k = 0; k < vsz; k++ ) - { - int k1 = ofstab[k]; - float v0 = imgptr[k1]; - float v1 = imgptr[k1 + stride_w]; - rowbuf[k] = v0; - rowbuf[k+vsz_a] = v1; - } - out_j++; - rowbuf += vsz_a; - imgptr += stride_w; - in_j += stride_w; - } - else - { - int j0 = std::max(0, (-in_j + dilation_w-1)/dilation_w); - int j1 = std::min(kernel_w, (width - in_j + dilation_w-1)/dilation_w); - - // here some non-continuous sub-row of the row will not be - // filled from the tensor; we need to make sure that the uncovered - // elements are explicitly set to 0's. the easiest way is to - // set all the elements to 0's before the loop. - memset(rowbuf, 0, vsz*sizeof(rowbuf[0])); - for( k = 0; k < ncn; k++ ) - { - for( i = i0; i < i1; i++ ) - { - for( j = j0; j < j1; j++ ) - { - int imgofs = k*(width*height) + i*(dilation_h*width) + j*dilation_w; - rowbuf[(k*kernel_h + i)*kernel_w + j] = imgptr[imgofs]; - } - } - } - } - } - } - } - } - else - { - for( ofs = ofs0; ofs < ofs1; out_d += (out_i + 1) / outH, out_i = (out_i + 1) % outH, out_j = 0 ) - { - int delta = std::min(ofs1 - ofs, outW - out_j); - int out_j1 = out_j + delta; - - int in_d = out_d * stride_d - pad_d; - int in_i = out_i * stride_h - pad_t; - int in_j = out_j * stride_w - pad_l; - const float* imgptr = data_inp0 + (cn0*depth*height + in_d*height + in_i)*width + in_j; - ofs += delta; - - int d0 = std::max(0, (-in_d + dilation_d - 1) / dilation_d); - int d1 = std::min(kernel_d, (depth - in_d + dilation_d - 1) / dilation_d); - - int i0 = std::max(0, (-in_i + dilation_h-1)/dilation_h); - int i1 = std::min(kernel_h, (height - in_i + dilation_h-1)/dilation_h); - - for( ; out_j < out_j1; out_j++, rowbuf += vsz_a, imgptr += stride_w, in_j += stride_w ) - { - int j0 = std::max(0, (-in_j + dilation_w-1)/dilation_w); - int j1 = std::min(kernel_w, (width - in_j + dilation_w-1)/dilation_w); - - // here some non-continuous sub-row of the row will not be - // filled from the tensor; we need to make sure that the uncovered - // elements are explicitly set to 0's. the easiest way is to - // set all the elements to 0's before the loop. - memset(rowbuf, 0, vsz*sizeof(rowbuf[0])); - for( k = 0; k < ncn; k++ ) - { - for ( d = d0; d < d1; d++) - { - for( i = i0; i < i1; i++ ) - { - for( j = j0; j < j1; j++ ) - { - int imgofs = k*(depth*width*height) + d*dilation_d*width*height + i*(dilation_h*width) + j*dilation_w; - rowbuf[(k*kernel_d*kernel_h + d*kernel_h + i)*kernel_w + j] = imgptr[imgofs]; - } - } - } - } - } - } - } - // now compute dot product of the weights - // and im2row-transformed part of the tensor - #if CV_TRY_AVX512_SKX - /* AVX512 convolution requires an alignment of 16, and ROI is only there for larger vector sizes */ - if(useAVX512) - opt_AVX512_SKX::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, - outShape, bsz, vsz, vsz_a, relu, cn0 == 0); - else - #endif - #if CV_TRY_AVX2 - if(useAVX2) - opt_AVX2::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, - outShape, bsz, vsz, vsz_a, relu, cn0 == 0); - else - #endif - #if CV_TRY_AVX - if(useAVX) - opt_AVX::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, - outShape, bsz, vsz, vsz_a, relu, cn0 == 0); - else - #endif - #if CV_TRY_RVV - if(useRVV) - opt_RVV::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, - outShape, bsz, vsz, vsz_a, relu, cn0 == 0); - else - #endif - #if CV_TRY_LASX - if(useLASX) - opt_LASX::fastConv(wptr, wstep, biasptr, rowbuf0, data_out0 + ofs0, - outShape, bsz, vsz, vsz_a, relu, cn0 == 0); - else - #endif - for( int i = 0; i < outCn; i += 2 ) - { - const float* wptr0 = wptr + i*wstep; - const float* wptr1 = wptr0 + wstep; - float* outptr0 = data_out0 + ofs0 + i*outPlaneSize; - float* outptr1 = outptr0 + outPlaneSize; - float bias0 = biasptr[i], bias1 = biasptr[i+1]; - float r0 = 1.f, r1 = 1.f; - - if( i+1 >= outCn ) - { - wptr1 = wptr0; - outptr1 = outptr0; - bias1 = bias0; - } - - if( relu ) - { - r0 = relu[i]; r1 = relu[i+1]; - if( i+1 >= outCn ) - r1 = r0; - } - - int j = 0; - #if CV_SIMD128 - v_float32x4 vr0 = v_setall_f32(r0), vr1 = v_setall_f32(r1), z = v_setzero_f32(); - - for( ; j <= bsz - 4; j += 4 ) - { - const float* rptr = rowbuf0 + j*vsz_a; - v_float32x4 s0, s1; - - if( cn0 == 0 ) - { - s0 = v_setall_f32(bias0); - s1 = v_setall_f32(bias1); - } - else - { - s0 = v_load(outptr0 + j); - s1 = v_load(outptr1 + j); - } - - v_float32x4 vs00 = v_setzero_f32(), vs01 = v_setzero_f32(), - vs02 = v_setzero_f32(), vs03 = v_setzero_f32(), - vs10 = v_setzero_f32(), vs11 = v_setzero_f32(), - vs12 = v_setzero_f32(), vs13 = v_setzero_f32(); - for( k = 0; k < vsz; k += 4, rptr += 4 ) - { - v_float32x4 w0 = v_load_aligned(wptr0 + k); - v_float32x4 w1 = v_load_aligned(wptr1 + k); - v_float32x4 r0 = v_load_aligned(rptr); - v_float32x4 r1 = v_load_aligned(rptr + vsz_a); - v_float32x4 r2 = v_load_aligned(rptr + vsz_a*2); - v_float32x4 r3 = v_load_aligned(rptr + vsz_a*3); - - vs00 = v_fma(w0, r0, vs00); - vs01 = v_fma(w0, r1, vs01); - vs02 = v_fma(w0, r2, vs02); - vs03 = v_fma(w0, r3, vs03); - - vs10 = v_fma(w1, r0, vs10); - vs11 = v_fma(w1, r1, vs11); - vs12 = v_fma(w1, r2, vs12); - vs13 = v_fma(w1, r3, vs13); - } - s0 += v_reduce_sum4(vs00, vs01, vs02, vs03); - s1 += v_reduce_sum4(vs10, vs11, vs12, vs13); - if( relu ) - { - s0 = v_select(s0 > z, s0, s0*vr0); - s1 = v_select(s1 > z, s1, s1*vr1); - } - - v_store(outptr0 + j, s0); - v_store(outptr1 + j, s1); - } - #endif - for( ; j < bsz; j++ ) - { - const float* rptr = rowbuf0 + j*vsz_a; - float s00, s10; - - if( cn0 == 0 ) - { - s00 = bias0; - s10 = bias1; - } - else - { - s00 = outptr0[j]; - s10 = outptr1[j]; - } - - for( k = 0; k < vsz; k++ ) - { - float r0 = rptr[k]; - s00 += wptr0[k]*r0; - s10 += wptr1[k]*r0; - } - if( relu ) - { - s00 = s00 > 0.f ? s00 : s00*r0; - s10 = s10 > 0.f ? s10 : s10*r1; - } - - outptr0[j] = s00; - outptr1[j] = s10; - } - } - } - } - - if( activ_ ) - activ_->forwardSlice(data_out0 + stripeStart, data_out0 + stripeStart, - (int)(stripeEnd - stripeStart), - outPlaneSize, startOutCn, startOutCn + outCn); - } - } - }; - #ifdef HAVE_OPENCL bool forward_ocl(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals) { @@ -2096,40 +1294,27 @@ public: #endif { int nstripes = std::max(getNumThreads(), 1); + int conv_dim = CONV_2D; + if (inputs[0].dims == 3) + conv_dim = CONV_1D; + if (inputs[0].dims == 5) + conv_dim = CONV_3D; // Initialization of FastCovn2d, pack weight. - if ((!fastConv2dImpl || variableWeight) && inputs[0].dims == 4) + if (!fastConvImpl || variableWeight) { int K = outputs[0].size[1]; int C = inputs[0].size[1]; - int Hk = kernel_size[kernel_size.size() - 2]; - int Wk = kernel_size.back(); + + // Winograd only works when input h and w >= 12. + bool canUseWinograd = useWinograd && conv_dim == CONV_2D && inputs[0].size[2] >= 12 && inputs[0].size[3] >= 12; CV_Assert(outputs[0].size[1] % ngroups == 0); - int stride_h = strides[strides.size() - 2]; - int stride_w = strides.back(); - - int dilation_h = dilations[dilations.size() - 2]; - int dilation_w = dilations.back(); - - // Winograd only works well on input h and w >12. - bool canUseWinograd = useWinograd && inputs[0].size[2] >= 12 && inputs[0].size[3] >= 12; - - fastConv2dImpl = initFastConv2d(ngroups, K, C, Hk, Wk, stride_w, stride_h, dilation_w, - dilation_h, pads_begin, pads_end, weightsMat, &biasvec[0], canUseWinograd); + fastConvImpl = initFastConv(weightsMat, &biasvec[0], ngroups, K, C, kernel_size, strides, + dilations, pads_begin, pads_end, conv_dim, canUseWinograd); } - if (fastConv2dImpl) - { - runFastConv2d(inputs[0], outputs[0], fastConv2dImpl, nstripes, activ, fusedAdd); - return; - } - - //TODO: Add support of Conv1D and Conv3D to fastConv, and remove the old Conv branch. - // Use only for Conv1D and Conv3D. - CV_Assert(!fusedAdd); - ParallelConv::run(inputs[0], outputs[0], weightsMat, biasvec, reluslope, - kernel_size, strides, pads_begin, pads_end, dilations, activ.get(), ngroups, nstripes); + runFastConv(inputs[0], outputs[0], fastConvImpl, nstripes, activ, reluslope, fusedAdd); } } diff --git a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp index a527523a32..0c471e8920 100644 --- a/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/depthwise_convolution.cpp @@ -11,381 +11,404 @@ #include "../../precomp.hpp" #include "fast_convolution.hpp" +#include "../layers_common.hpp" namespace cv { namespace dnn { -static void depthWiseBlock(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab, - float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left, - int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, - int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3) +static void depthWiseBlockConv2D(const float* wptr, + int kernel_h, int kernel_w, + int stride_h, int stride_w, + int dilation_h, int dilation_w, + int pad_t, int pad_l, + const float* biasptr, const float* relu, + const float* inptr_, + int height, int width, + float* outptr_, + int out_d, int outH, int outW) { -#if CV_SIMD128 - const int VEC_NLANES = 4; - v_float32x4 vminval = v_setall_f32(minval), vmaxval = v_setall_f32(maxval); + const float w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2], + w10 = wptr[3], w11 = wptr[4], w12 = wptr[5], + w20_ = wptr[6], w21_ = wptr[7], w22_ = wptr[8]; + int outW1 = min(outW, (width - dilation_w*(kernel_w - 1) + pad_l)/stride_w); + float relu_coeff = relu ? relu[out_d] : 1.f, bias = biasptr[out_d]; - v_float32x4 w0 = v_setall_f32( - 0.f), w1 = w0, w2 = w0, w3 = w0, w4 = w0, w5 = w0, w6 = w0, w7 = w0, w8 = w0, vbias = w0; - if (useSIMD) + for (int out_i = 0; out_i < outH; out_i++) { - vbias = v_setall_f32(biasval); - if (is3x3) + int in_i = out_i * stride_h - pad_t, out_j = 0; + const float* imgptr0 = inptr_ + in_i*width; + const float* imgptr1 = imgptr0 + dilation_h*width; + const float* imgptr2 = imgptr0 + (dilation_h*2)*width; + float out, w00 = w00_, w01 = w01_, w02 = w02_; + float w20 = w20_, w21 = w21_, w22 = w22_; + if (in_i < 0) { - w0 = v_setall_f32(weights[0]); - w1 = v_setall_f32(weights[1]); - w2 = v_setall_f32(weights[2]); - w3 = v_setall_f32(weights[3]); - w4 = v_setall_f32(weights[4]); - w5 = v_setall_f32(weights[5]); - w6 = v_setall_f32(weights[6]); - w7 = v_setall_f32(weights[7]); - w8 = v_setall_f32(weights[8]); + w00 = w01 = w02 = 0.f; + imgptr0 = imgptr1; } - } -#endif - int dy0 = 1; - for (int y0 = 0; y0 < H0; y0 += dy0, outptr += W0 * dy0) - { -#if CV_SIMD128 - dy0 = inner_ytop <= y0 && y0 + 3 < inner_ybottom && is3x3 && stride_y == 1 && dilation_y == 1 - ? 3 : 1; -#endif - int x0 = 0, x1 = y0 >= inner_ytop && y0 < inner_ybottom ? inner_xleft : W0; - int yi_ = y0 * stride_y - pad_top; - - for (;;) + else if (in_i + dilation_h*(kernel_h-1) >= height) { - float s_0, s_1, s_2; - if (dy0 == 3) - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - s_0 = s_1 = s_2 = biasval; - for (int k = 0; k < ksize; k++) - { - int dy = yxtab[k * 2]; - int yi = yi_ + dy; - int xi = xi_ + yxtab[k * 2 + 1]; - float w = weights[k]; + w20 = w21 = w22 = 0.f; + imgptr2 = imgptr1; + } + + float* outptr = outptr_ + out_i*outW; + if (pad_l > 0) + { + out = imgptr0[0]*w01 + imgptr0[dilation_w]*w02 + + imgptr1[0]*w11 + imgptr1[dilation_w]*w12 + + imgptr2[0]*w21 + imgptr2[dilation_w]*w22 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[0] = out; + out_j = 1; + } - if ((unsigned) xi < (unsigned) Wi) - { - s_0 += inptr[yi * Wi + xi] * w; - s_1 += inptr[(yi + 1) * Wi + xi] * w; - s_2 += inptr[(yi + 2) * Wi + xi] * w; - } - } - s_0 = std::min(std::max(s_0, minval), maxval); - s_1 = std::min(std::max(s_1, minval), maxval); - s_2 = std::min(std::max(s_2, minval), maxval); - outptr[x0] = s_0; - outptr[x0 + W0] = s_1; - outptr[x0 + W0 * 2] = s_2; - } - } - else - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - s_0 = biasval; - for (int k = 0; k < ksize; k++) { - int dy = yxtab[k * 2]; - int yi = yi_ + dy; - int xi = xi_ + yxtab[k * 2 + 1]; - float w = weights[k]; - if (((unsigned) yi < (unsigned) Hi) & ((unsigned) xi < (unsigned) Wi)) - s_0 += inptr[yi * Wi + xi] * w; - } - s_0 = std::min(std::max(s_0, minval), maxval); - outptr[x0] = s_0; - } - } - if (x0 == W0) - break; - x1 = inner_xright; #if CV_SIMD128 - if (useSIMD) + const int VEC_NLANES = 4; + v_float32x4 vw00 = v_setall_f32(w00); + v_float32x4 vw01 = v_setall_f32(w01); + v_float32x4 vw02 = v_setall_f32(w02); + v_float32x4 vw10 = v_setall_f32(w10); + v_float32x4 vw11 = v_setall_f32(w11); + v_float32x4 vw12 = v_setall_f32(w12); + v_float32x4 vw20 = v_setall_f32(w20); + v_float32x4 vw21 = v_setall_f32(w21); + v_float32x4 vw22 = v_setall_f32(w22); + v_float32x4 z = v_setzero_f32(); + v_float32x4 vbias = v_setall_f32(bias); + v_float32x4 vrc = v_setall_f32(relu_coeff); + + if (stride_w == 1 || (stride_w == 2 && dilation_w == 1)) + { + if( stride_w == 1 ) { - if (is3x3) + for( ; out_j < outW1; out_j += VEC_NLANES ) { - if (dy0 == 3) + if (out_j + VEC_NLANES > outW1) { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - - v_float32x4 s0, s1, s2; - v_float32x4 x00 = v_load(inptr_xi); - v_float32x4 x01 = v_load(inptr_xi + 1); - v_float32x4 x02 = v_load(inptr_xi + 2); - - v_float32x4 x10 = v_load(inptr_xi + Wi); - v_float32x4 x11 = v_load(inptr_xi + Wi + 1); - v_float32x4 x12 = v_load(inptr_xi + Wi + 2); - - v_float32x4 x20 = v_load(inptr_xi + Wi * 2); - v_float32x4 x21 = v_load(inptr_xi + Wi * 2 + 1); - v_float32x4 x22 = v_load(inptr_xi + Wi * 2 + 2); - - v_float32x4 x30 = v_load(inptr_xi + Wi * 3); - v_float32x4 x31 = v_load(inptr_xi + Wi * 3 + 1); - v_float32x4 x32 = v_load(inptr_xi + Wi * 3 + 2); - - v_float32x4 x40 = v_load(inptr_xi + Wi * 4); - v_float32x4 x41 = v_load(inptr_xi + Wi * 4 + 1); - v_float32x4 x42 = v_load(inptr_xi + Wi * 4 + 2); - - s0 = v_fma(x00, w0, vbias); - s1 = v_fma(x10, w0, vbias); - s2 = v_fma(x20, w0, vbias); - - s0 = v_fma(x01, w1, s0); - s1 = v_fma(x11, w1, s1); - s2 = v_fma(x21, w1, s2); - - s0 = v_fma(x02, w2, s0); - s1 = v_fma(x12, w2, s1); - s2 = v_fma(x22, w2, s2); - - s0 = v_fma(x10, w3, s0); - s1 = v_fma(x20, w3, s1); - s2 = v_fma(x30, w3, s2); - - s0 = v_fma(x11, w4, s0); - s1 = v_fma(x21, w4, s1); - s2 = v_fma(x31, w4, s2); - - s0 = v_fma(x12, w5, s0); - s1 = v_fma(x22, w5, s1); - s2 = v_fma(x32, w5, s2); - - s0 = v_fma(x20, w6, s0); - s1 = v_fma(x30, w6, s1); - s2 = v_fma(x40, w6, s2); - - s0 = v_fma(x21, w7, s0); - s1 = v_fma(x31, w7, s1); - s2 = v_fma(x41, w7, s2); - - s0 = v_fma(x22, w8, s0); - s1 = v_fma(x32, w8, s1); - s2 = v_fma(x42, w8, s2); - - if (ifMinMaxAct) - { - s0 = v_min(v_max(s0, vminval), vmaxval); - s1 = v_min(v_max(s1, vminval), vmaxval); - s2 = v_min(v_max(s2, vminval), vmaxval); - } - - v_store(outptr + x0, s0); - v_store(outptr + W0 + x0, s1); - v_store(outptr + W0 * 2 + x0, s2); - } + if (out_j <= pad_l || outW1 - VEC_NLANES < 0) + break; + out_j = outW1 - VEC_NLANES; } - else - { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - v_float32x4 s0 = v_fma(v_load(inptr_xi + ofstab[0]), w0, vbias); - v_float32x4 s1 = v_load(inptr_xi + ofstab[1]) * w1; - v_float32x4 s2 = v_load(inptr_xi + ofstab[2]) * w2; + int in_j = out_j * stride_w - pad_l; + v_float32x4 v00 = v_load(imgptr0 + in_j), + v01 = v_load(imgptr0 + in_j + dilation_w), + v02 = v_load(imgptr0 + in_j + dilation_w*2), + v10 = v_load(imgptr1 + in_j), + v11 = v_load(imgptr1 + in_j + dilation_w), + v12 = v_load(imgptr1 + in_j + dilation_w*2), + v20 = v_load(imgptr2 + in_j), + v21 = v_load(imgptr2 + in_j + dilation_w), + v22 = v_load(imgptr2 + in_j + dilation_w*2); - s0 = v_fma(v_load(inptr_xi + ofstab[3]), w3, s0); - s1 = v_fma(v_load(inptr_xi + ofstab[4]), w4, s1); - s2 = v_fma(v_load(inptr_xi + ofstab[5]), w5, s2); - - s0 = v_fma(v_load(inptr_xi + ofstab[6]), w6, s0); - s1 = v_fma(v_load(inptr_xi + ofstab[7]), w7, s1); - s2 = v_fma(v_load(inptr_xi + ofstab[8]), w8, s2); - - s0 = s0 + s1 + s2; - if (ifMinMaxAct) - s0 = v_min(v_max(s0, vminval), vmaxval); - v_store(outptr + x0, s0); - } - } - } - else - { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left, k = 0; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - v_float32x4 s0 = vbias; - for (; k <= ksize - 4; k += 4) - { - v_float32x4 v0 = v_load(inptr_xi + ofstab[k]); - v_float32x4 v1 = v_load(inptr_xi + ofstab[k + 1]); - v_float32x4 v2 = v_load(inptr_xi + ofstab[k + 2]); - v_float32x4 v3 = v_load(inptr_xi + ofstab[k + 3]); - - v_float32x4 ww0 = v_setall_f32(weights[k]); - v_float32x4 ww1 = v_setall_f32(weights[k+1]); - v_float32x4 ww2 = v_setall_f32(weights[k+2]); - v_float32x4 ww3 = v_setall_f32(weights[k+3]); - - s0 = v_fma(v0, ww0, s0); - s0 = v_fma(v1, ww1, s0); - s0 = v_fma(v2, ww2, s0); - s0 = v_fma(v3, ww3, s0); - } - for (; k < ksize; k++) - s0 = v_fma(v_load(inptr_xi + ofstab[k]), - v_setall_f32(weights[k]), s0); - if (ifMinMaxAct) - s0 = v_min(v_max(s0, vminval), vmaxval); - v_store(outptr + x0, s0); - } + v_float32x4 vout = v00*vw00 + v01*vw01 + v02*vw02 + + v10*vw10 + v11*vw11 + v12*vw12 + + v20*vw20 + v21*vw21 + v22*vw22 + vbias; + if (relu) + vout = v_select(vout > z, vout, vout*vrc); + v_store(outptr + out_j, vout); } } + else // (stride_w == 2 && dilation_w == 1) + { + for( ; out_j < outW1; out_j += VEC_NLANES ) + { + if (out_j + VEC_NLANES > outW1 && out_j > pad_l) + { + if (outW1 - VEC_NLANES < 0) + break; + out_j = outW1 - VEC_NLANES; + } + + int in_j = out_j * stride_w - pad_l; + + v_float32x4 v00, v01, v02, v10, v11, v12, v20, v21, v22, unused; + v_load_deinterleave(imgptr0 + in_j, v00, v01); + v_load_deinterleave(imgptr0 + in_j + 2, v02, unused); + v_load_deinterleave(imgptr1 + in_j, v10, v11); + v_load_deinterleave(imgptr1 + in_j + 2, v12, unused); + v_load_deinterleave(imgptr2 + in_j, v20, v21); + v_load_deinterleave(imgptr2 + in_j + 2, v22, unused); + + v_float32x4 vout = v00 * vw00 + v01 * vw01 + v02 * vw02 + + v10 * vw10 + v11 * vw11 + v12 * vw12 + + v20 * vw20 + v21 * vw21 + v22 * vw22 + vbias; + + if (relu) + vout = v_select(vout > z, vout, vout*vrc); + v_store(outptr + out_j, vout); + } + } + } #endif - if (dy0 == 3) - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + W0 * yi_ + xi_; - s_0 = s_1 = s_2 = biasval; - for (int k = 0; k < ksize; k++) - { - int inp_ofs = ofstab[k]; - float w = weights[k]; - s_0 += inptr_xi[inp_ofs] * w; - s_1 += inptr_xi[inp_ofs + Wi] * w; - s_2 += inptr_xi[inp_ofs + Wi * 2] * w; - } - if (ifMinMaxAct) - { - s_0 = std::min(std::max(s_0, minval), maxval); - s_1 = std::min(std::max(s_1, minval), maxval); - s_2 = std::min(std::max(s_2, minval), maxval); - } - outptr[x0] = s_0; - outptr[x0 + W0] = s_1; - outptr[x0 + W0 * 2] = s_2; - } - } - else - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - s_0 = biasval; - for (int k = 0; k < ksize; k++) - { - s_0 += inptr_xi[ofstab[k]] * weights[k]; - } + for (; out_j < outW1; out_j++) + { + int in_j = out_j * stride_w - pad_l; + out = imgptr0[in_j]*w00 + imgptr0[in_j + dilation_w]*w01 + imgptr0[in_j + dilation_w*2]*w02 + + imgptr1[in_j]*w10 + imgptr1[in_j + dilation_w]*w11 + imgptr1[in_j + dilation_w*2]*w12 + + imgptr2[in_j]*w20 + imgptr2[in_j + dilation_w]*w21 + imgptr2[in_j + dilation_w*2]*w22 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; + } - if (ifMinMaxAct) - s_0 = std::min(std::max(s_0, minval), maxval); - outptr[x0] = s_0; - } + for (; out_j < outW; out_j++ ) + { + int in_j0 = out_j * stride_w - pad_l, in_j1 = in_j0 + dilation_w, in_j2 = in_j0 + dilation_w*2; + float s0 = 1.f, s1 = 1.f, s2 = 1.f; + if (in_j0 >= width) + { + in_j0 = 0; + s0 = 0.f; } - x1 = W0; + if (in_j1 >= width) + { + in_j1 = 0; + s1 = 0.f; + } + if (in_j2 >= width) + { + in_j2 = 0; + s2 = 0.f; + } + out = imgptr0[in_j0]*w00*s0 + imgptr0[in_j1]*w01*s1 + imgptr0[in_j2]*w02*s2 + + imgptr1[in_j0]*w10*s0 + imgptr1[in_j1]*w11*s1 + imgptr1[in_j2]*w12*s2 + + imgptr2[in_j0]*w20*s0 + imgptr2[in_j1]*w21*s1 + imgptr2[in_j2]*w22*s2 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; } } } -void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) { +static void depthWiseBlockConv1D(const float* wptr, + int kernel_w, int stride_w, int dilation_w, int pad_l, + const float* biasptr, const float* relu, + const float* inptr_, int width, + float* outptr_, + int out_d, int outW) +{ + const float w00_ = wptr[0], w01_ = wptr[1], w02_ = wptr[2]; + int outW1 = min(outW, (width - dilation_w * (kernel_w - 1) + pad_l)/stride_w); + float relu_coeff = relu ? relu[out_d] : 1.f, bias = biasptr[out_d]; + + int out_j = 0; + const float* imgptr0 = inptr_; + float out, w00 = w00_, w01 = w01_, w02 = w02_; + float* outptr = outptr_; + + if (pad_l > 0) + { + out = imgptr0[0]*w01 + imgptr0[dilation_w]*w02 + bias; + + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[0] = out; + out_j = 1; + } + +#if CV_SIMD128 + const int VEC_NLANES = 4; + v_float32x4 vw00 = v_setall_f32(w00); + v_float32x4 vw01 = v_setall_f32(w01); + v_float32x4 vw02 = v_setall_f32(w02); + v_float32x4 z = v_setzero_f32(); + v_float32x4 vbias = v_setall_f32(bias); + v_float32x4 vrc = v_setall_f32(relu_coeff); + + if (stride_w == 1 || (stride_w == 2 && dilation_w == 1)) + { + if( stride_w == 1 ) + { + for( ; out_j < outW1; out_j += VEC_NLANES ) + { + if (out_j + VEC_NLANES > outW1) + { + if (out_j <= pad_l || outW1 - VEC_NLANES < 0) + break; + out_j = outW1 - VEC_NLANES; + } + int in_j = out_j * stride_w - pad_l; + v_float32x4 v00 = v_load(imgptr0 + in_j), + v01 = v_load(imgptr0 + in_j + dilation_w), + v02 = v_load(imgptr0 + in_j + dilation_w*2); + + v_float32x4 vout = v00*vw00 + v01*vw01 + v02*vw02 + vbias; + if (relu) + vout = v_select(vout > z, vout, vout*vrc); + v_store(outptr + out_j, vout); + } + } + else // (stride_w == 2 && dilation_w == 1) + { + for( ; out_j < outW1; out_j += VEC_NLANES ) + { + if (out_j + VEC_NLANES > outW1) + { + if (out_j <= pad_l || outW1 - VEC_NLANES < 0) + break; + out_j = outW1 - VEC_NLANES; + } + int in_j = out_j * stride_w - pad_l; + + v_float32x4 v00, v01, v02, unused; + v_load_deinterleave(imgptr0 + in_j, v00, v01); + v_load_deinterleave(imgptr0 + in_j + 2, v02, unused); + + v_float32x4 vout = v00 * vw00 + v01 * vw01 + v02 * vw02 + vbias; + + if (relu) + vout = v_select(vout > z, vout, vout*vrc); + v_store(outptr + out_j, vout); + } + } + } +#endif + + for (; out_j < outW1; out_j++) + { + int in_j = out_j * stride_w - pad_l; + out = imgptr0[in_j]*w00 + imgptr0[in_j + dilation_w]*w01 + imgptr0[in_j + dilation_w*2]*w02 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; + } + + for (; out_j < outW; out_j++ ) + { + int in_j0 = out_j * stride_w - pad_l, in_j1 = in_j0 + dilation_w, in_j2 = in_j0 + dilation_w*2; + float s0 = 1.f, s1 = 1.f, s2 = 1.f; + if (in_j0 >= width) + { + in_j0 = 0; + s0 = 0.f; + } + if (in_j1 >= width) + { + in_j1 = 0; + s1 = 0.f; + } + if (in_j2 >= width) + { + in_j2 = 0; + s2 = 0.f; + } + out = imgptr0[in_j0]*w00*s0 + imgptr0[in_j1]*w01*s1 + imgptr0[in_j2]*w02*s2 + bias; + if (relu) + out = out > 0.f ? out : out*relu_coeff; + outptr[out_j] = out; + } +} + +void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, ActivationLayer* activ_, + const std::vector& reluslope) +{ Mat input = _input.getMat(); Mat output = _output.getMat(); MatShape inputShape = shape(input); MatShape outputShape = shape(output); - CV_Assert(inputShape.size() == 4 && outputShape.size() == 4); - int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W] + CV_Assert(inputShape.size() == 3 || inputShape.size() == 4); + CV_Assert(inputShape.size() == outputShape.size()); + + int conv_dim = conv->conv_dim; + CV_Assert((conv_dim == CONV_2D || conv_dim == CONV_1D) && + "DNN: Currently we do not support depth-wise for Convolution 3D!"); + + ActivationLayer* activ = reluslope.empty() ? activ_ : nullptr; + int N = inputShape[0], C = inputShape[1]; + + int Hi = conv_dim == CONV_1D ? 1 : inputShape[inputShape.size() - 2]; + int Wi = inputShape[inputShape.size() - 1]; + int K = conv->K, Hk = conv->Hk, Wk = conv->Wk; - int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups; + + int H0 = conv_dim == CONV_1D ? 1 : outputShape[outputShape.size() - 2]; + int W0 = outputShape[outputShape.size() - 1]; + int ngroups = conv->ngroups; const size_t inp_planesize = (size_t) Hi * Wi; const size_t out_planesize = (size_t) H0 * W0; CV_Assert(ngroups > 1 && ngroups == K && ngroups == C); - int stride_y = conv->stride_y, stride_x = conv->stride_x; - int dilation_y = conv->dilation_y, dilation_x = conv->dilation_x; + int stride_h = conv->stride_h, stride_w = conv->stride_w; + int dilation_h = conv->dilation_h, dilation_w = conv->dilation_w; int pad_top = conv->pad_top, pad_bottom = conv->pad_bottom; int pad_left = conv->pad_left, pad_right = conv->pad_right; - int VEC_NLANES = 4; -#if CV_TRY_AVX2 - if (conv->useAVX2) - VEC_NLANES = 8; -#endif - int ksize = Hk * Wk, padded_ksize = ((ksize + VEC_NLANES - 1) / VEC_NLANES) * VEC_NLANES; + int ksize = Hk * Wk; + + const int VEC_NLANES = 32; + int padded_ksize = ((ksize + VEC_NLANES-1) / VEC_NLANES) * VEC_NLANES; const float *inp = input.ptr(); float *out = output.ptr(); - std::vector ofstab_(3 * padded_ksize, 0); +#if CV_TRY_AVX2 || CV_TRY_AVX || CV_TRY_RVV + // TODO: remove the following limitation, need change code in layers_common.simd.hpp. + bool canRunOpt = Wi >= 16 + dilation_w*(Wk - 1); +#endif + std::vector ofstab_(3 * ksize, 0); int *ofstab = ofstab_.data(); - int *yxtab = ofstab + padded_ksize; + int *yxtab = ofstab + ksize; - for (int k = 0; k < padded_ksize; k++) + for (int k = 0; k < ksize; k++) { int y = k < ksize ? k / Wk : 0; int x = k < ksize ? k % Wk : 0; - int dy = y * dilation_y, dx = x * dilation_x; + int dy = y * dilation_h, dx = x * dilation_w; yxtab[k * 2] = dy; yxtab[k * 2 + 1] = dx; ofstab[k] = dy * Wi + dx; } const float *weights0 = conv->weightsBufPtr, *bias = conv->biasBuf.data(); - int inner_ytop = (pad_bottom + stride_y - 1) / stride_y, inner_ybottom = 3; - int inner_xleft = (pad_left + stride_x - 1) / stride_x, inner_xright = 4; - + const float* relu = reluslope.data(); CV_Assert(ksize > 1 || (pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0)); - inner_xright = (Wi - (Wk - 1) * dilation_x + pad_left) / stride_x; - inner_xright += inner_xright * stride_x - pad_left + (Wk - 1) * dilation_x < Wi; - inner_ybottom = (Hi - (Hk - 1) * dilation_y + pad_top) / stride_y; - inner_ybottom += inner_ybottom * stride_y - pad_top + (Hk - 1) * dilation_y < Hi; - - if (inner_xleft >= inner_xright || inner_ytop >= inner_ybottom) - { - inner_xleft = W0; - inner_ytop = H0; - } - - inner_ybottom = inner_ybottom < H0 ? inner_ybottom : H0; - - bool useSIMD = stride_x == 1 && inner_xleft < W0; - bool is3x3 = Hk == 3 && Wk == 3; - parallel_for_(Range(0, N * C), [&](const Range &r0) { - for (int nc = r0.start; nc < r0.end; nc++) + for (int nc = r0.start; nc < r0.end; nc++) + { + int c = nc % C; + const float *inptr0 = inp + inp_planesize * nc; + float *outptr0 = out + out_planesize * nc; + + const float *weights = weights0 + c * padded_ksize; + + if (conv_dim == CONV_2D) { - int c = nc % C; - const float *inptr = inp + inp_planesize * nc; - float *outptr0 = out + out_planesize * nc; - - float biasval = bias[c]; - const float *weights = weights0 + c * padded_ksize; - #if CV_TRY_AVX2 - if (conv->useAVX2) - opt_AVX2::depthWiseBlock_AVX2(inptr, outptr0, weights, biasval, ofstab, yxtab, minval, maxval, Hi, Wi, H0, W0, ksize, - pad_top, pad_left, dilation_y, stride_x, stride_y, inner_xleft, inner_xright, inner_ytop, - inner_ybottom, ifMinMaxAct, useSIMD, is3x3); + if(canRunOpt && conv->useAVX2) + opt_AVX2::fastDepthwiseConv(weights, Hk, Wk, stride_h, stride_w, dilation_h, dilation_w, + pad_top, pad_left, bias, relu, inptr0, Hi, Wi, outptr0, c, H0, W0); else #endif - depthWiseBlock(inptr, outptr0, weights, biasval, ofstab, yxtab, minval, maxval, Hi, Wi, H0, W0, ksize, - pad_top, pad_left, dilation_y, stride_x, stride_y, inner_xleft, inner_xright, inner_ytop, - inner_ybottom, ifMinMaxAct, useSIMD, is3x3); - - if (activ) - activ->forwardSlice(outptr0, outptr0, (int) out_planesize, out_planesize, c, c+1); +#if CV_TRY_AVX + if(canRunOpt && conv->useAVX) + opt_AVX::fastDepthwiseConv(weights, Hk, Wk, stride_h, stride_w, dilation_h, dilation_w, + pad_top, pad_left, bias, relu, inptr0, Hi, Wi, outptr0, c, H0, W0); + else +#endif +#if CV_TRY_RVV + if(canRunOpt && conv->useRVV) + opt_RVV::fastDepthwiseConv(weights, Hk, Wk, stride_h, stride_w, dilation_h, dilation_w, + pad_top, pad_left, bias, relu, inptr0, Hi, Wi, outptr0, c, H0, W0); + else +#endif + depthWiseBlockConv2D(weights, Hk, Wk, stride_h, stride_w, dilation_h, dilation_w, + pad_top, pad_left, bias, relu, inptr0, Hi, Wi, outptr0, c, H0, W0); } - }); + else // conv_dim == CONV_1D, spatial branch for depth-wise Conv1D. + { + depthWiseBlockConv1D(weights, Wk, stride_w, dilation_w, pad_left, bias, relu, inptr0, Wi, outptr0, c, W0); + } + + if (activ) + activ->forwardSlice(outptr0, outptr0, (int) out_planesize, out_planesize, c, c+1); + }}); } -}} // namespace cv::dnn \ No newline at end of file +}} // namespace cv::dnn diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp index aa10d40bee..0d3c144762 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.avx2.cpp @@ -6,9 +6,52 @@ #include "fast_convolution.hpp" namespace cv { +namespace dnn { namespace opt_AVX2 { #if CV_TRY_AVX2 +void convBlockMR1(int np, const float* a, const float* b, float *c, const float bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct) +{ +#if CONV_NR == 24 + __m256 c0 = _mm256_set1_ps(bias), c1 = c0, c2 = c0; + + for (int p = 0; p < np; p++, a++, b += CONV_NR) + { + __m256 a0 = _mm256_set1_ps(a[0]); + __m256 b0 = _mm256_loadu_ps(b), b1 = _mm256_loadu_ps(b + 8), b2 = _mm256_loadu_ps(b + 16); + + c0 = _mm256_fmadd_ps(b0, a0, c0); + c1 = _mm256_fmadd_ps(b1, a0, c1); + c2 = _mm256_fmadd_ps(b2, a0, c2); + } + + if (init_c) + { + c0 = _mm256_add_ps(_mm256_loadu_ps(c), c0); + c1 = _mm256_add_ps(_mm256_loadu_ps(c + 8), c1); + c2 = _mm256_add_ps(_mm256_loadu_ps(c + 16), c2); + } + + if (ifMinMaxAct) + { + __m256 vmax = _mm256_set1_ps(maxval); + __m256 vmin = _mm256_set1_ps(minval); + + c0 = _mm256_min_ps(_mm256_max_ps(c0, vmin), vmax); + c1 = _mm256_min_ps(_mm256_max_ps(c1, vmin), vmax); + c2 = _mm256_min_ps(_mm256_max_ps(c2, vmin), vmax); + } + + _mm256_storeu_ps(c, c0); + _mm256_storeu_ps(c + 8, c1); + _mm256_storeu_ps(c + 16, c2); + _mm256_zeroupper(); +#else +#error "unsupported CONV_NR in convBlockMR1." +#endif +} + void convBlock_AVX2(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { #if CONV_MR == 4 && CONV_NR == 24 @@ -73,291 +116,6 @@ void convBlock_AVX2(int np, const float* a, const float* b, float* c, int ldc, b #endif } -void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab, - float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left, - int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, - int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3) -{ - const int VEC_NLANES = 8; - __m256 vminval = _mm256_set1_ps(minval); - __m256 vmaxval = _mm256_set1_ps(maxval); - - __m256 w0 = _mm256_setzero_ps(), - w1 = w0, w2 = w0, w3 = w0, w4 = w0, w5 = w0, w6 = w0, w7 = w0, w8 = w0, vbias = w0; - - if (useSIMD) - { - vbias = _mm256_set1_ps(biasval); - if (is3x3) - { - w0 = _mm256_set1_ps(weights[0]); - w1 = _mm256_set1_ps(weights[1]); - w2 = _mm256_set1_ps(weights[2]); - w3 = _mm256_set1_ps(weights[3]); - w4 = _mm256_set1_ps(weights[4]); - w5 = _mm256_set1_ps(weights[5]); - w6 = _mm256_set1_ps(weights[6]); - w7 = _mm256_set1_ps(weights[7]); - w8 = _mm256_set1_ps(weights[8]); - } - } - - int dy0 = 1; - for (int y0 = 0; y0 < H0; y0 += dy0, outptr += W0 * dy0) - { - dy0 = inner_ytop <= y0 && y0 + 3 < inner_ybottom && is3x3 && stride_y == 1 && dilation_y == 1 - ? 3 : 1; - - int x0 = 0, x1 = y0 >= inner_ytop && y0 < inner_ybottom ? inner_xleft : W0; - int yi_ = y0 * stride_y - pad_top; - - for (;;) - { - float s_0, s_1, s_2; - if (dy0 == 3) - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - s_0 = s_1 = s_2 = biasval; - for (int k = 0; k < ksize; k++) - { - int dy = yxtab[k * 2]; - int yi = yi_ + dy; - int xi = xi_ + yxtab[k * 2 + 1]; - float w = weights[k]; - - if ((unsigned) xi < (unsigned) Wi) - { - s_0 += inptr[yi * Wi + xi] * w; - s_1 += inptr[(yi + 1) * Wi + xi] * w; - s_2 += inptr[(yi + 2) * Wi + xi] * w; - } - } - if (ifMinMaxAct) - { - s_0 = std::min(std::max(s_0, minval), maxval); - s_1 = std::min(std::max(s_1, minval), maxval); - s_2 = std::min(std::max(s_2, minval), maxval); - } - - outptr[x0] = s_0; - outptr[x0 + W0] = s_1; - outptr[x0 + W0 * 2] = s_2; - } - } - else - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - s_0 = biasval; - for (int k = 0; k < ksize; k++) { - int dy = yxtab[k * 2]; - int yi = yi_ + dy; - int xi = xi_ + yxtab[k * 2 + 1]; - float w = weights[k]; - if (((unsigned) yi < (unsigned) Hi) & ((unsigned) xi < (unsigned) Wi)) - s_0 += inptr[yi * Wi + xi] * w; - } - if (ifMinMaxAct) - s_0 = std::min(std::max(s_0, minval), maxval); - outptr[x0] = s_0; - } - } - if (x0 == W0) - break; - x1 = inner_xright; - - if (useSIMD) - { - if (is3x3) - { - if (dy0 == 3) - { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - - __m256 s0, s1, s2; - __m256 x00 = _mm256_loadu_ps(inptr_xi); - __m256 x01 = _mm256_loadu_ps(inptr_xi + 1); - __m256 x02 = _mm256_loadu_ps(inptr_xi + 2); - - __m256 x10 = _mm256_loadu_ps(inptr_xi + Wi); - __m256 x11 = _mm256_loadu_ps(inptr_xi + Wi + 1); - __m256 x12 = _mm256_loadu_ps(inptr_xi + Wi + 2); - - __m256 x20 = _mm256_loadu_ps(inptr_xi + Wi * 2); - __m256 x21 = _mm256_loadu_ps(inptr_xi + Wi * 2 + 1); - __m256 x22 = _mm256_loadu_ps(inptr_xi + Wi * 2 + 2); - - __m256 x30 = _mm256_loadu_ps(inptr_xi + Wi * 3); - __m256 x31 = _mm256_loadu_ps(inptr_xi + Wi * 3 + 1); - __m256 x32 = _mm256_loadu_ps(inptr_xi + Wi * 3 + 2); - - __m256 x40 = _mm256_loadu_ps(inptr_xi + Wi * 4); - __m256 x41 = _mm256_loadu_ps(inptr_xi + Wi * 4 + 1); - __m256 x42 = _mm256_loadu_ps(inptr_xi + Wi * 4 + 2); - - s0 = _mm256_fmadd_ps(x00, w0, vbias); - s1 = _mm256_fmadd_ps(x10, w0, vbias); - s2 = _mm256_fmadd_ps(x20, w0, vbias); - - s0 = _mm256_fmadd_ps(x01, w1, s0); - s1 = _mm256_fmadd_ps(x11, w1, s1); - s2 = _mm256_fmadd_ps(x21, w1, s2); - - s0 = _mm256_fmadd_ps(x02, w2, s0); - s1 = _mm256_fmadd_ps(x12, w2, s1); - s2 = _mm256_fmadd_ps(x22, w2, s2); - - s0 = _mm256_fmadd_ps(x10, w3, s0); - s1 = _mm256_fmadd_ps(x20, w3, s1); - s2 = _mm256_fmadd_ps(x30, w3, s2); - - s0 = _mm256_fmadd_ps(x11, w4, s0); - s1 = _mm256_fmadd_ps(x21, w4, s1); - s2 = _mm256_fmadd_ps(x31, w4, s2); - - s0 = _mm256_fmadd_ps(x12, w5, s0); - s1 = _mm256_fmadd_ps(x22, w5, s1); - s2 = _mm256_fmadd_ps(x32, w5, s2); - - s0 = _mm256_fmadd_ps(x20, w6, s0); - s1 = _mm256_fmadd_ps(x30, w6, s1); - s2 = _mm256_fmadd_ps(x40, w6, s2); - - s0 = _mm256_fmadd_ps(x21, w7, s0); - s1 = _mm256_fmadd_ps(x31, w7, s1); - s2 = _mm256_fmadd_ps(x41, w7, s2); - - s0 = _mm256_fmadd_ps(x22, w8, s0); - s1 = _mm256_fmadd_ps(x32, w8, s1); - s2 = _mm256_fmadd_ps(x42, w8, s2); - - if (ifMinMaxAct) - { - s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval); - s1 = _mm256_min_ps(_mm256_max_ps(s1, vminval), vmaxval); - s2 = _mm256_min_ps(_mm256_max_ps(s2, vminval), vmaxval); - } - - _mm256_storeu_ps(outptr + x0, s0); - _mm256_storeu_ps(outptr + W0 + x0, s1); - _mm256_storeu_ps(outptr + W0 * 2 + x0, s2); - } - } - else - { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - __m256 s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[0]), w0, vbias); - __m256 s1 = _mm256_mul_ps(_mm256_loadu_ps(inptr_xi + ofstab[1]), w1); - __m256 s2 = _mm256_mul_ps(_mm256_loadu_ps(inptr_xi + ofstab[2]), w2); - - s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[3]), w3, s0); - s1 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[4]), w4, s1); - s2 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[5]), w5, s2); - - s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[6]), w6, s0); - s1 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[7]), w7, s1); - s2 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[8]), w8, s2); - - s0 = _mm256_add_ps(_mm256_add_ps(s0, s1), s2); - - if (ifMinMaxAct) - s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval); - _mm256_storeu_ps(outptr + x0, s0); - } - } - } - else - { - for (; x0 <= x1 - VEC_NLANES; x0 += VEC_NLANES) - { - int xi_ = x0 * stride_x - pad_left, k = 0; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - __m256 s0 = vbias; - for (; k <= ksize - 4; k += 4) - { - __m256 v0 = _mm256_loadu_ps(inptr_xi + ofstab[k]); - __m256 v1 = _mm256_loadu_ps(inptr_xi + ofstab[k + 1]); - __m256 v2 = _mm256_loadu_ps(inptr_xi + ofstab[k + 2]); - __m256 v3 = _mm256_loadu_ps(inptr_xi + ofstab[k + 3]); - - __m256 ww0 = _mm256_set1_ps(weights[k]); - __m256 ww1 = _mm256_set1_ps(weights[k+1]); - __m256 ww2 = _mm256_set1_ps(weights[k+2]); - __m256 ww3 = _mm256_set1_ps(weights[k+3]); - - s0 = _mm256_fmadd_ps(v0, ww0, s0); - s0 = _mm256_fmadd_ps(v1, ww1, s0); - s0 = _mm256_fmadd_ps(v2, ww2, s0); - s0 = _mm256_fmadd_ps(v3, ww3, s0); - } - for (; k < ksize; k++) - s0 = _mm256_fmadd_ps(_mm256_loadu_ps(inptr_xi + ofstab[k]), - _mm256_set1_ps(weights[k]), s0); - - if (ifMinMaxAct) - s0 = _mm256_min_ps(_mm256_max_ps(s0, vminval), vmaxval); - _mm256_storeu_ps(outptr + x0, s0); - } - } - } - - if (dy0 == 3) - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + W0 * yi_ + xi_; - s_0 = s_1 = s_2 = biasval; - for (int k = 0; k < ksize; k++) { - int inp_ofs = ofstab[k]; - float w = weights[k]; - s_0 += inptr_xi[inp_ofs] * w; - s_1 += inptr_xi[inp_ofs + Wi] * w; - s_2 += inptr_xi[inp_ofs + Wi * 2] * w; - } - if (ifMinMaxAct) - { - s_0 = std::min(std::max(s_0, minval), maxval); - s_1 = std::min(std::max(s_1, minval), maxval); - s_2 = std::min(std::max(s_2, minval), maxval); - } - - outptr[x0] = s_0; - outptr[x0 + W0] = s_1; - outptr[x0 + W0 * 2] = s_2; - } - } - else - { - for (; x0 < x1; x0++) - { - int xi_ = x0 * stride_x - pad_left; - const float *inptr_xi = inptr + Wi * yi_ + xi_; - s_0 = biasval; - for (int k = 0; k < ksize; k++) - { - s_0 += inptr_xi[ofstab[k]] * weights[k]; - } - if (ifMinMaxAct) - s_0 = std::min(std::max(s_0, minval), maxval); - outptr[x0] = s_0; - } - } - x1 = W0; - } - } - _mm256_zeroupper(); -} - void _fx_winograd_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock) { @@ -737,4 +495,5 @@ void _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, #endif } // namespace opt_AVX2 +} // namespace dnn } // namespace cv \ No newline at end of file diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index fba57e7ee0..02d7121f5e 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -15,47 +15,94 @@ namespace cv { namespace dnn { enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. -Ptr initFastConv2d( - int ngroups, - int K, int C, int Hk, int Wk, - int stride_x, int stride_y, - int dilation_x, int dilation_y, - const std::vector& pads_begin, - const std::vector& pads_end, +Ptr initFastConv( InputArray _weightsMat, float* srcBias, + int ngroups, + int K, int C, + const std::vector& kernel_size, + const std::vector& strides, + const std::vector& dilations, + const std::vector& pads_begin, + const std::vector& pads_end, + int conv_dim, bool useWinograd) { - Ptr conv = makePtr(); + Ptr conv = makePtr(); CV_Assert(ngroups > 0 && K > 0 && C > 0 && K % ngroups == 0); - CV_Assert(Hk > 0 && Wk > 0); - CV_Assert(stride_y > 0 && stride_x > 0); - CV_Assert(dilation_y > 0 && dilation_x > 0); - conv->K = K; conv->C = C; conv->Hk = Hk; conv->Wk = Wk; // [K, iC, kH, kW] - conv->stride_y = stride_y; - conv->stride_x = stride_x; - conv->dilation_y = dilation_y; - conv->dilation_x = dilation_x; + // Weight shape, [K, C, Dk, Hk, Wk] for Conv3D, [K, C, Hk, Wk] for Conv2D, [K, C, Wk] for Conv1D. + int Dk = conv_dim == CONV_3D ? (int)kernel_size[0] : 1; + int Hk = conv_dim == CONV_1D ? 1 : (int)kernel_size[kernel_size.size() - 2]; + int Wk = (int)kernel_size.back(); + int karea = Wk*Hk*Dk; + conv->pad_front = conv_dim == CONV_3D ? (int)pads_begin[0] : 0; + conv->pad_top = conv_dim == CONV_1D ? 0 : (int)pads_begin[pads_begin.size() - 2]; + conv->pad_left = (int)pads_begin.back(); + + conv->pad_behind = conv_dim == CONV_3D ? (int)pads_end[0] : 0; + conv->pad_bottom = conv_dim == CONV_1D ? 0 : (int)pads_end[pads_end.size() - 2]; + conv->pad_right = (int)pads_end.back(); + + int stride_d = conv_dim == CONV_3D ? (int)strides[0] : 0; + int stride_h = conv_dim == CONV_1D ? 0 : (int)strides[strides.size() - 2]; + int stride_w = (int)strides.back(); + + int dilation_d = conv_dim == CONV_3D ? (int)dilations[0] : 1; + int dilation_h = conv_dim == CONV_1D ? 1 : (int)dilations[dilations.size() - 2]; + int dilation_w = (int)dilations.back(); + + CV_Assert(Dk > 0 && Hk > 0 && Wk > 0); + CV_Assert(stride_d >= 0 && stride_h >= 0 && stride_w > 0); + CV_Assert(dilation_d > 0 && dilation_h > 0 && dilation_w > 0); + + conv->K = K; conv->C = C; conv->Hk = Hk; conv->Wk = Wk, conv->Dk = Dk; + + conv->stride_d = stride_d; + conv->stride_h = stride_h; + conv->stride_w = stride_w; + + conv->dilation_d = dilation_d; + conv->dilation_h = dilation_h; + conv->dilation_w = dilation_w; + conv->conv_dim = conv_dim; conv->ngroups = ngroups; - conv->pad_top = pads_begin[0]; - conv->pad_bottom = pads_end[0]; - conv->pad_left = pads_begin[1]; - conv->pad_right = pads_end[1]; - conv->conv_type = - (ngroups > 1 && ngroups == K && ngroups == C) ? _FX_CONV_TYPE_DEPTHWISE : - useWinograd && ((conv->useSIMD128 || conv->useAVX2 || conv->useNEON) && Hk == 3 && Wk == 3 && - dilation_y == 1 && dilation_x == 1 && stride_y == 1 && stride_x == 1) ? _FX_CONV_TYPE_WINOGRAD3X3 : - _FX_CONV_TYPE_GENERIC; - int VEC_NLANES = 4; -#if CV_TRY_AVX2 - if (!conv->useAVX2 && conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // convert Winograd to generic conv. + bool ifRunDepthWise = ngroups > 1 && ngroups == K && ngroups == C; + bool ifRunDepthWiseRemain = false; // It's for big padding or big kernel or Conv3D depth-wise convolution. + + if (ifRunDepthWise) + { + if (conv_dim == CONV_1D) + { + ifRunDepthWise &= Hk == 1 && Wk == 3 && (stride_w == 1 || (stride_w == 2 && dilation_w == 1)) + && max(stride_w, dilation_w) >= conv->pad_left && conv->pad_left <= 1; + } + else if (conv_dim == CONV_2D) + { + ifRunDepthWise &= Hk == 3 && Wk == 3 && ((stride_w == 1) || (stride_w == 2 && dilation_w == 1)) && + max(stride_w, dilation_w) >= conv->pad_left && max(stride_h, dilation_h) >= conv->pad_top + && conv->pad_left <= 1 && conv->pad_top <= 1; + } + + if (!ifRunDepthWise || conv_dim == CONV_3D) + { + ifRunDepthWise = false; + ifRunDepthWiseRemain = true; + } + } + + conv->conv_type = ifRunDepthWise && conv_dim != CONV_3D ? _FX_CONV_TYPE_DEPTHWISE : + useWinograd && (conv_dim == CONV_2D && (conv->useSIMD128 || conv->useAVX2 || conv->useNEON) && + Hk == 3 && Wk == 3 && dilation_h == 1 && dilation_w == 1 && stride_h == 1 && stride_w == 1) ? + _FX_CONV_TYPE_WINOGRAD3X3 : + (ifRunDepthWiseRemain ? _FX_CONV_TYPE_DEPTHWISE_REMAIN : _FX_CONV_TYPE_GENERIC); + +#if !(CV_NEON || CV_SIMD128 || CV_TRY_AVX2) + if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // Disabel Winograd when CV_NEON, CV_SIMD128 and CV_TRY_AVX2 are not available. conv->conv_type = _FX_CONV_TYPE_GENERIC; - if (conv->useAVX2) - VEC_NLANES = 8; #endif Mat weightsMat = _weightsMat.getMat(); @@ -63,14 +110,16 @@ Ptr initFastConv2d( const size_t wstep = weightsMat.step1(); float *srcWeights = (float *)weightsMat.data; - if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE) + if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE || conv->conv_type == _FX_CONV_TYPE_DEPTHWISE_REMAIN) { + // Handle the Conv1D, Conv2D and Conv3D depth-wise. // for depth-wise convolutions on NCHW data we just preserve the weights in KCHW layout, // but add some padding to make the weights array layout more SIMD-friendly - int ksize = Hk*Wk; + int ksize = karea; + // TODO: simplify the following code with std::copy. // this code aims to let memory fit with vector size. - int padded_ksize = ((ksize + VEC_NLANES-1) / VEC_NLANES) * VEC_NLANES; + int padded_ksize = ((ksize + VEC_ALIGN-1) / VEC_ALIGN) * VEC_ALIGN; int nweights = C*padded_ksize; conv->weightsBuf.reserve(nweights + VEC_ALIGN); conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); @@ -163,12 +212,12 @@ Ptr initFastConv2d( else if (conv->conv_type == _FX_CONV_TYPE_GENERIC) { // The weights are packed as - // ngroups x (ceil((K/ngroups)/CONV_MR)*CONV_MR) x (Cg*Hk*Wk) x CONV_MR tensor + // ngroups x (ceil((K/ngroups)/CONV_MR)*CONV_MR) x (Cg*Hk*Wk*Dk) x CONV_MR tensor int Kg = K/ngroups, Cg = max(C/ngroups, 1); int numStripsMR = (Kg + CONV_MR - 1) / CONV_MR; int Kg_aligned = numStripsMR * CONV_MR; - int HkWkCg = Hk*Wk*Cg; - size_t nweights = ngroups*Kg_aligned*HkWkCg; + int DkHkWkCg = Dk*Hk*Wk*Cg; + size_t nweights = ngroups*Kg_aligned*DkHkWkCg; conv->weightsBuf.reserve(nweights + VEC_ALIGN); conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); float* weightsBufPtr = conv->weightsBufPtr; @@ -184,14 +233,14 @@ Ptr initFastConv2d( int startK = si * CONV_MR; CV_Assert(startK < Kg_aligned); - float* packed_wptr = weightsBufPtr + HkWkCg * (startK + g * Kg_aligned); + float* packed_wptr = weightsBufPtr + DkHkWkCg * (startK + g * Kg_aligned); int dk = Kg - startK < CONV_MR ? Kg - startK : CONV_MR; // check if we need zero padding. int k_idx = g*Kg + startK; - for(int yx = 0; yx < Hk*Wk; yx++) { + for(int hwd = 0; hwd < Hk*Wk*Dk; hwd++) { for(int c = 0; c < Cg; c++, packed_wptr += CONV_MR) { - const float* wptr = srcWeights + wstep * k_idx + c*Hk*Wk + yx; + const float* wptr = srcWeights + wstep * k_idx + c*Hk*Wk*Dk + hwd; int k = 0; for(; k < dk; k++, wptr += wstep) packed_wptr[k] = *wptr; @@ -207,7 +256,7 @@ Ptr initFastConv2d( // store bias; append some zero's to make sure that // we can always read MR elements starting from any valid index { - int k = 0, nbias = K + 32; + int k = 0, nbias = K + VEC_ALIGN; conv->biasBuf.reserve(nbias); float* biasBufPtr = conv->biasBuf.data(); for(; k < K; k++) @@ -218,23 +267,121 @@ Ptr initFastConv2d( return conv; } -void runFastConv2d(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, - const Ptr& actLayer, bool fusedAdd) +static inline void packData8(float*& inpbuf, float*& inptrIn, int& in_w, int& x0, int& s0, const int* ofstab, + const int stride_w, const int ksize) +{ + float* inpbufC = inpbuf + s0; + float* inptrInC = inptrIn; + + if (stride_w == 1) + for (int k = 0; k < ksize; k++) + { + int k1 = ofstab[k]; + float v0 = inptrInC[k1]; + float v1 = inptrInC[k1 + 1]; + float v2 = inptrInC[k1 + 2]; + float v3 = inptrInC[k1 + 3]; + float v4 = inptrInC[k1 + 4]; + float v5 = inptrInC[k1 + 5]; + float v6 = inptrInC[k1 + 6]; + float v7 = inptrInC[k1 + 7]; + + inpbufC[k*CONV_NR] = v0; + inpbufC[k*CONV_NR+1] = v1; + inpbufC[k*CONV_NR+2] = v2; + inpbufC[k*CONV_NR+3] = v3; + inpbufC[k*CONV_NR+4] = v4; + inpbufC[k*CONV_NR+5] = v5; + inpbufC[k*CONV_NR+6] = v6; + inpbufC[k*CONV_NR+7] = v7; + } + else + for (int k = 0; k < ksize; k++) + { + int k1 = ofstab[k]; + float v0 = inptrInC[k1]; + float v1 = inptrInC[k1 + stride_w]; + float v2 = inptrInC[k1 + 2*stride_w]; + float v3 = inptrInC[k1 + 3*stride_w]; + float v4 = inptrInC[k1 + 4*stride_w]; + float v5 = inptrInC[k1 + 5*stride_w]; + float v6 = inptrInC[k1 + 6*stride_w]; + float v7 = inptrInC[k1 + 7*stride_w]; + + inpbufC[k*CONV_NR] = v0; + inpbufC[k*CONV_NR+1] = v1; + inpbufC[k*CONV_NR+2] = v2; + inpbufC[k*CONV_NR+3] = v3; + inpbufC[k*CONV_NR+4] = v4; + inpbufC[k*CONV_NR+5] = v5; + inpbufC[k*CONV_NR+6] = v6; + inpbufC[k*CONV_NR+7] = v7; + } + x0+=7; + s0+=7; + inptrIn += 7*stride_w; + in_w += 7*stride_w; +} + +static inline void packData2(float*& inpbuf, float*& inptrIn, int& in_w, int& x0, int& s0, const int* ofstab, + const int stride_w, const int ksize) +{ + float* inpbufC = inpbuf + s0; + float* inptrInC = inptrIn; + + for (int k = 0; k < ksize; k++) + { + int k1 = ofstab[k]; + float v0 = inptrInC[k1]; + float v1 = inptrInC[k1 + stride_w]; + inpbufC[k*CONV_NR] = v0; + inpbufC[k*CONV_NR+1] = v1; + } + + x0++; + s0++; + inptrIn += stride_w; + in_w += stride_w; +} + +void runFastConv(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, + const Ptr& actLayer, const std::vector& reluslope, bool fusedAdd) { Mat input = _input.getMat(); Mat output = _output.getMat(); + int conv_dim = conv->conv_dim; + + CV_Assert_N(input.dims == output.dims, + input.size[0] == output.size[0], + conv->C == input.size[1], + conv->K == output.size[1], + input.type() == output.type(), + input.isContinuous(), + output.isContinuous()); Mat fusedAddMat; if (fusedAdd) + { + CV_Assert(conv->conv_dim != CONV_3D && "Conv3D does not support Conv+Add fusion optimization!"); fusedAddMat = _output.getMat(); + } + + if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE) + { + // Depthwise-Convolution layer should not be followed by Add layer. + CV_Assert(fusedAddMat.empty() && (conv_dim == CONV_1D || conv_dim == CONV_2D)); + return runDepthwise(input, output, conv,actLayer.get(), reluslope); + } MatShape inputShape = shape(input); MatShape outputShape = shape(output); - CV_Assert(inputShape.size() == 4 && outputShape.size() == 4); - ActivationLayer* activ = 0; + CV_Assert(inputShape.size() == outputShape.size()); + + ActivationLayer* activ = nullptr; float minval = -FLT_MAX, maxval = FLT_MAX; bool ifMinMaxAct = false; + if (actLayer) { Ptr activ_relu = actLayer.dynamicCast(); @@ -267,47 +414,97 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr else activ = nullptr; - if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE) + if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // winograd { - CV_Assert(fusedAddMat.empty()); // Depthwise-Convolution layer should not be followed by Add layer. - return runDepthwise(input, output, conv, minval, maxval, activ, ifMinMaxAct); - } - else if (conv->conv_type == _FX_CONV_TYPE_WINOGRAD3X3) // winograd - { - CV_Assert(conv->weightsWinoBufPtr); + CV_Assert(conv->weightsWinoBufPtr && input.dims == 4 && conv_dim == CONV_2D); if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) return; } - int N = inputShape[0], C = inputShape[1], Hi = inputShape[2], Wi = inputShape[3]; // [N, C, H, W] - int K = conv->K, Hk = conv->Hk, Wk = conv->Wk; - int H0 = outputShape[2], W0 = outputShape[3], ngroups = conv->ngroups; + int N = inputShape[0], C = inputShape[1]; + + // input shape: [N, C, D, H, W] for Conv3D, [N, C, H, W] for Conv2D, [N, C, W] for Conv1D. + int Di = conv_dim == CONV_3D ? inputShape[2] : 1; + int Hi = conv_dim == CONV_1D ? 1 : inputShape[inputShape.size() - 2]; + int Wi = inputShape[inputShape.size() - 1]; + + int ngroups = conv->ngroups; + int K = conv->K, Dk = conv->Dk, Hk = conv->Hk, Wk = conv->Wk; + + int D0 = conv_dim == CONV_3D ? outputShape[2] : 1; + int H0 = conv_dim == CONV_1D ? 1 : outputShape[outputShape.size() - 2]; + int W0 = outputShape[outputShape.size() - 1]; + int Cg = C/ngroups, Kg = K/ngroups; - const size_t inp_planesize = (size_t)Hi*Wi; - const size_t out_planesize = (size_t)H0*W0; + const size_t inp_planesize = (size_t)Di*Hi*Wi; + const size_t out_planesize = (size_t)D0*H0*W0; + int pad_front = conv->pad_front; int pad_top = conv->pad_top; int pad_left = conv->pad_left; - int stride_y = conv->stride_y, stride_x = conv->stride_x; - int dilation_y = conv->dilation_y, dilation_x = conv->dilation_x; + int stride_d = conv->stride_d, stride_h = conv->stride_h, stride_w = conv->stride_w; + int dilation_d = conv->dilation_d, dilation_h = conv->dilation_h, dilation_w = conv->dilation_w; - int ksize = Hk * Wk; - bool fast_1x1 = stride_x == 1 && stride_y == 1 && ksize == 1; - int HkWkCg = Hk*Wk*Cg; + int ksize = Dk*Hk*Wk; + bool fast_1x1 = stride_d == 1 && stride_w == 1 && stride_h == 1 && ksize == 1; + int DkHkWkCg = Dk*Hk*Wk*Cg; - int MAX_STRIPES = 2; // (56 + CONV_NR - 1)/CONV_NR; + std::vector ofstab_(Hk*Wk*Dk*4, 0); + int* ofstab = ofstab_.data(); + int* dhwTab = ofstab + Hk*Wk*Dk; + int padded_ksize = ((ksize + VEC_ALIGN-1) / VEC_ALIGN) * VEC_ALIGN; + + if (conv_dim == CONV_1D) + { + for( int w = 0; w < Wk; w++) + { + int dw = w*dilation_w; + dhwTab[w*3+2] = dw; + ofstab[w] = dw; + } + } + else if (conv_dim == CONV_2D) + { + for (int h = 0; h < Hk; h++) + for( int w = 0; w < Wk; w++) + { + int k = h*Wk + w; + int dh = h*dilation_h, dw = w*dilation_w; + dhwTab[k*3+1] = dh; + dhwTab[k*3+2] = dw; + ofstab[k] = dh*Wi + dw; + } + } + else + { + for (int d = 0; d < Dk; d++) + for (int h = 0; h < Hk; h++) + { + for (int w = 0; w < Wk; w++) + { + int k = d*Hk*Wk + h*Wk + w; + int dd = d*dilation_d, dh = h*dilation_h, dw = w*dilation_w; + dhwTab[k*3] = dd; + dhwTab[k*3+1] = dh; + dhwTab[k*3+2] = dw; + ofstab[k] = dd*Hi*Wi + dh*Wi + dw; + } + } + } + + int MAX_STRIPES = (56 + CONV_NR - 1)/CONV_NR; // Friendly to L1 cache - const int K_BLOCK_SIZE = 32; + const int K_BLOCK_SIZE = conv->conv_type == _FX_CONV_TYPE_DEPTHWISE_REMAIN ? 1 : 32; const int C_BLOCK_SIZE = 256; int Kg_nblocks = (Kg + CONV_MR-1)/CONV_MR, Kg_aligned = Kg_nblocks * CONV_MR; - int stripes_per_sample = (out_planesize + CONV_NR - 1) / CONV_NR; + int stripes_per_sample = ((int)out_planesize + CONV_NR - 1) / CONV_NR; - if (stripes_per_sample < ntasks * 4) + if (stripes_per_sample < ntasks * 4 && conv->conv_type != _FX_CONV_TYPE_DEPTHWISE_REMAIN) { MAX_STRIPES = 1; stripes_per_sample = 1; @@ -327,20 +524,6 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr inpbuf_all_.allocate(totalbufsize + VEC_ALIGN); float* inpbuf_all = alignPtr(inpbuf_all_.data(), (int)(VEC_ALIGN*sizeof(inpbuf_all_[0]))); - std::vector ofstab_(Hk*Wk*3, 0); - int* ofstab = ofstab_.data(); - int* yxtab = ofstab + Hk*Wk; - - for (int y = 0; y < Hk; y++) - for( int x = 0; x < Wk; x++) - { - int k = y*Wk + x; - int dy = y*dilation_y, dx = x*dilation_x; - yxtab[k*2] = dy; - yxtab[k*2+1] = dx; - ofstab[k] = dy*Wi + dx; - } - float* inp = input.ptr(); float* out = output.ptr(); float* fusedAddPtr0 = fusedAddMat.empty() ? 0 : fusedAddMat.ptr(); @@ -356,47 +539,47 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr for (int subtask = ngs0; subtask < ngs1; ) { int ng = subtask / Kstripes; - int kyx0 = subtask - ng * Kstripes; - int kyx1 = kyx0 + (ngs1 - subtask); + int kzyx0 = subtask - ng * Kstripes; + int kzyx1 = kzyx0 + (ngs1 - subtask); int n = ng / ngroups, g = ng % ngroups; // ng - n * ngroups; size_t inp_plane_ofs = (size_t)(n * ngroups + g) * Cg * inp_planesize; - kyx1 = kyx1 <= Kstripes ? kyx1 : Kstripes; - subtask += kyx1 - kyx0; + kzyx1 = kzyx1 <= Kstripes ? kzyx1 : Kstripes; + subtask += kzyx1 - kzyx0; int k0, k1; - int yx0, yx_limit, yx_block_limit = 0; + int zyx0, zyx_limit, zyx_block_limit = 0; - if (stripes_per_sample == 1) + if (stripes_per_sample == 1 && conv->conv_type != _FX_CONV_TYPE_DEPTHWISE_REMAIN) { - k0 = kyx0 * CONV_MR; - k1 = kyx1 * CONV_MR; + k0 = kzyx0 * CONV_MR; + k1 = kzyx1 * CONV_MR; k1 = k1 <= Kg ? k1 : Kg; - yx0 = 0; - yx_limit = out_planesize; + zyx0 = 0; + zyx_limit = (int)out_planesize; } else { k0 = 0; k1 = Kg; - yx0 = kyx0 * CONV_NR; - yx_limit = kyx1 * CONV_NR; - yx_limit = yx_limit < out_planesize ? yx_limit : out_planesize; + zyx0 = kzyx0 * CONV_NR; + zyx_limit = kzyx1 * CONV_NR; + zyx_limit = zyx_limit < out_planesize ? zyx_limit : (int)out_planesize; } - for (; yx0 < yx_limit; yx0 = yx_block_limit) + for (; zyx0 < zyx_limit; zyx0 = zyx_block_limit) { // step 1. extract part of input tensor and represent it in zigzag form - yx_block_limit = yx0 + CONV_NR * MAX_STRIPES; - yx_block_limit = yx_block_limit < yx_limit ? yx_block_limit : yx_limit; + zyx_block_limit = zyx0 + CONV_NR * MAX_STRIPES; + zyx_block_limit = zyx_block_limit < zyx_limit ? zyx_block_limit : zyx_limit; - int nstripes = (yx_block_limit - yx0 + CONV_NR - 1) / CONV_NR; - int yx0_saved = yx0; + int nstripes = (zyx_block_limit - zyx0 + CONV_NR - 1) / CONV_NR; + int zyx0_saved = zyx0; CV_Assert(nstripes <= MAX_STRIPES); - for (int stripe = 0; yx0 < yx_block_limit; stripe++, yx0 += CONV_NR) + for (int stripe = 0; zyx0 < zyx_block_limit; stripe++, zyx0 += CONV_NR) { - float* inpbuf = inpbuf_task + stripe * stripesize; - float* inptr = inp + inp_plane_ofs; + float *inpbuf = inpbuf_task + stripe * stripesize; + float *inptr = inp + inp_plane_ofs; /* 1. pack the data. Copy the HkxWk CONV_NR-wide slices from @@ -404,20 +587,20 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr */ if (fast_1x1) { - int slice_len = yx_block_limit - yx0; + int slice_len = zyx_block_limit - zyx0; bool partial = slice_len < CONV_NR; // Superfast branch for 1x1 convolutions with sy=sx=1. // in this case each feature plane can be safely treated // as 1D array, and we just extract next portion // of CONV_NR elements from each feature plane and // put it together. - inptr += yx0; + inptr += zyx0; if (!partial) { // Make special branch where memcpy() is called with a constant buffer size. // Compilers will likely unroll this loop properly. for (int c = 0; c < Cg; c++, inptr += inp_planesize, inpbuf += CONV_NR) - memcpy(inpbuf, inptr, CONV_NR*sizeof(inpbuf[0])); + memcpy(inpbuf, inptr, CONV_NR * sizeof(inpbuf[0])); } else { @@ -428,25 +611,198 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr } } } + else if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE_REMAIN) + { + CV_Assert(Cg == 1); + const int HW0 = H0 * W0; + const int HWi = Hi * Wi; + int slice_len = std::min(zyx_block_limit - zyx0, CONV_NR); + + // here some non-continuous sub-row of the row will not be + // filled from the tensor; we need to make sure that the uncovered + // elements are explicitly set to 0's. the easiest way is to + // set all the elements to 0's before the loop. + memset(inpbuf, 0, stripesize*sizeof(inpbuf[0])); + + int z0 = zyx0 / HW0, yx0 = zyx0 - z0 * HW0; + int y0 = yx0 / W0, x0 = yx0 - y0 * W0; + + if (conv_dim == CONV_1D) + { + for (int slice_i = 0; slice_i < slice_len; y0++, x0=0) + { + int delta = std::min(slice_len - slice_i, W0 - x0); + int x1 = x0 + delta; + + int in_w = x0 * stride_w - pad_left; + float* inptrIn = inptr + in_w; + + int s0 = slice_i; + + for (; x0 < x1; x0++, s0++, inptrIn += stride_w, in_w += stride_w) + { + // Pack 8 + if (x0 + 8 <= x1 && 0 <= in_w && + in_w + stride_w*8 <= Wi - (Wk-1)*dilation_w) + { + packData8(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else if (x0 + 2 <= x1 && 0 <= in_w && + in_w + stride_w*2 <= Wi - (Wk-1)*dilation_w) + { + packData2(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else + { + int w0 = std::max(0, (-in_w + dilation_w-1)/dilation_w); + int w1 = std::min(Wk, (Wi - in_w + dilation_w-1)/dilation_w); + + float* inpbufC = inpbuf + s0; + float* inptrInC = inptrIn; + for (int w = w0; w < w1; w++) + { + int imgofs = w*dilation_w; + inpbufC[w*CONV_NR] = inptrInC[imgofs]; + } + } + } + slice_i += delta; + } + } + else if (conv_dim == CONV_2D) + { + for (int slice_i = 0; slice_i < slice_len; y0++, x0=0) + { + int delta = std::min(slice_len - slice_i, W0 - x0); + int x1 = x0 + delta; + + int in_h = y0 * stride_h - pad_top; + int in_w = x0 * stride_w - pad_left; + + float* inptrIn = inptr + in_h*Wi + in_w; + + bool ok_i = 0 <= in_h && in_h < Hi - (Hk-1)*dilation_h; + int h0 = std::max(0, (-in_h + dilation_h-1)/dilation_h); + int h1 = std::min(Hk, (Hi - in_h + dilation_h-1)/dilation_h); + + int s0 = slice_i; + for (; x0 < x1; x0++, s0++, inptrIn += stride_w, in_w += stride_w) + { + // Pack 8 + if (ok_i && x0 + 8 <= x1 && 0 <= in_w && + in_w + stride_w*8 <= Wi - (Wk-1)*dilation_w) + { + packData8(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else if (ok_i && x0 + 2 <= x1 && 0 <= in_w && + in_w + stride_w*2 <= Wi - (Wk-1)*dilation_w) + { + packData2(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else + { + int w0 = std::max(0, (-in_w + dilation_w-1)/dilation_w); + int w1 = std::min(Wk, (Wi - in_w + dilation_w-1)/dilation_w); + + float* inpbufC = inpbuf + s0; + float* inptrInC = inptrIn; + + for (int h = h0; h < h1; h++) + { + for (int w = w0; w < w1; w++) + { + int imgofs = h*(dilation_h*Wi) + w*dilation_w; + inpbufC[(h*Wk + w)*CONV_NR] = inptrInC[imgofs]; + } + } + } + } + slice_i += delta; + } + } + else if (conv_dim == CONV_3D) + { + for (int slice_i = 0; slice_i < slice_len; z0 += (y0+1)/H0, y0 = (y0+1)%H0, x0=0) + { + int delta = std::min(slice_len - slice_i, W0 - x0); + int x1 = x0 + delta; + + int in_d = z0 * stride_d - pad_front; + int in_h = y0 * stride_h - pad_top; + int in_w = x0 * stride_w - pad_left; + + float* inptrIn = inptr + in_d*HWi + in_h*Wi + in_w; + + int d0 = std::max(0, (-in_d + dilation_d - 1) / dilation_d); + int d1 = std::min(Dk, (Di - in_d + dilation_d - 1) / dilation_d); + + bool ok_i = 0 <= in_h && in_h < Hi - (Hk-1)*dilation_h; + int h0 = std::max(0, (-in_h + dilation_h-1)/dilation_h); + int h1 = std::min(Hk, (Hi - in_h + dilation_h-1)/dilation_h); + + int s0 = slice_i; + for (; x0 < x1; x0++, s0++, inptrIn += stride_w, in_w += stride_w) + { + // Pack 8 + if (ok_i && x0 + 8 <= x1 && 0 <= in_w && + in_w + stride_w*8 <= Wi - (Wk-1)*dilation_w) + { + packData8(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else if (ok_i && x0 + 2 <= x1 && 0 <= in_w && + in_w + stride_w*2 <= Wi - (Wk-1)*dilation_w) + { + packData2(inpbuf, inptrIn, in_w, x0, s0, ofstab, stride_w, ksize); + } + else + { + int w0 = std::max(0, (-in_w + dilation_w-1)/dilation_w); + int w1 = std::min(Wk, (Wi - in_w + dilation_w-1)/dilation_w); + + float* inpbufC = inpbuf + s0; + float* inptrInC = inptrIn; + + for ( int d = d0; d < d1; d++) + { + for (int h = h0; h < h1; h++) + { + for (int w = w0; w < w1; w++) + { + int imgofs = d*dilation_d*HWi + h*(dilation_h*Wi) + w*dilation_w; + inpbufC[((d*Hk + h)*Wk + w)*CONV_NR] = inptrInC[imgofs]; + } + } + } + } + } + slice_i += delta; + } + } + } else { + const int HW0 = H0 * W0; + const int HWi = Hi * Wi; + int z0_ = zyx0 / HW0, yx0 = zyx0 - z0_ * HW0; int y0_ = yx0 / W0, x0_ = yx0 - y0_ * W0; for (int k = 0; k < ksize; k++) { - int dy = yxtab[k * 2], dx = yxtab[k * 2 + 1]; - int i = 0, y0 = y0_, x0 = x0_; + int dz = dhwTab[k * 3], dy = dhwTab[k * 3 + 1], dx = dhwTab[k * 3 + 2]; + int i = 0, z0 = z0_, y0 = y0_, x0 = x0_; for (; i < CONV_NR;) { float *inpbuf_ki = inpbuf + k * CONV_NR * Cg + i; - int yi = y0 * stride_y + dy - pad_top; - int xi = x0 * stride_x + dx - pad_left; + int zi = z0 * stride_d + dz - pad_front; + int yi = y0 * stride_h + dy - pad_top; + int xi = x0 * stride_w + dx - pad_left; - if ((unsigned) yi < (unsigned) Hi && (unsigned) xi < (unsigned) Wi) + if ((unsigned) zi < (unsigned) Di && (unsigned) yi < (unsigned) Hi && + (unsigned) xi < (unsigned) Wi) { - const float *inptr_ki = inptr + yi * Wi + xi; - if (i + 8 <= CONV_NR && x0 + 8 <= W0 && xi + stride_x * 8 <= Wi) + const float *inptr_ki = inptr + zi * HWi + yi * Wi + xi; + if (i + 8 <= CONV_NR && x0 + 8 <= W0 && xi + stride_w * 8 <= Wi) { - if (stride_x == 1) + if (stride_w == 1) { for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) { @@ -454,49 +810,79 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr float t2 = inptr_ki[2], t3 = inptr_ki[3]; float t4 = inptr_ki[4], t5 = inptr_ki[5]; float t6 = inptr_ki[6], t7 = inptr_ki[7]; - inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; - inpbuf_ki[4] = t4; inpbuf_ki[5] = t5; - inpbuf_ki[6] = t6; inpbuf_ki[7] = t7; + inpbuf_ki[0] = t0; + inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; + inpbuf_ki[3] = t3; + inpbuf_ki[4] = t4; + inpbuf_ki[5] = t5; + inpbuf_ki[6] = t6; + inpbuf_ki[7] = t7; + } + } + else if (stride_w == 2) + { + for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) + { + float t0 = inptr_ki[0], t1 = inptr_ki[2]; + float t2 = inptr_ki[4], t3 = inptr_ki[6]; + float t4 = inptr_ki[8], t5 = inptr_ki[10]; + float t6 = inptr_ki[12], t7 = inptr_ki[14]; + inpbuf_ki[0] = t0; + inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; + inpbuf_ki[3] = t3; + inpbuf_ki[4] = t4; + inpbuf_ki[5] = t5; + inpbuf_ki[6] = t6; + inpbuf_ki[7] = t7; } } else { for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) { - float t0 = inptr_ki[0], t1 = inptr_ki[stride_x]; - float t2 = inptr_ki[stride_x*2], t3 = inptr_ki[stride_x*3]; - float t4 = inptr_ki[stride_x*4], t5 = inptr_ki[stride_x*5]; - float t6 = inptr_ki[stride_x*6], t7 = inptr_ki[stride_x*7]; - inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; - inpbuf_ki[4] = t4; inpbuf_ki[5] = t5; - inpbuf_ki[6] = t6; inpbuf_ki[7] = t7; + float t0 = inptr_ki[0], t1 = inptr_ki[stride_w]; + float t2 = inptr_ki[stride_w * 2], t3 = inptr_ki[stride_w * 3]; + float t4 = inptr_ki[stride_w * 4], t5 = inptr_ki[stride_w * 5]; + float t6 = inptr_ki[stride_w * 6], t7 = inptr_ki[stride_w * 7]; + inpbuf_ki[0] = t0; + inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; + inpbuf_ki[3] = t3; + inpbuf_ki[4] = t4; + inpbuf_ki[5] = t5; + inpbuf_ki[6] = t6; + inpbuf_ki[7] = t7; } } i += 8; x0 += 8; } - else if (i + 4 <= CONV_NR && x0 + 4 <= W0 && xi + stride_x * 4 <= Wi) + else if (i + 4 <= CONV_NR && x0 + 4 <= W0 && xi + stride_w * 4 <= Wi) { - if (stride_x == 1) + if (stride_w == 1) { for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) { float t0 = inptr_ki[0], t1 = inptr_ki[1]; float t2 = inptr_ki[2], t3 = inptr_ki[3]; - inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + inpbuf_ki[0] = t0; + inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; + inpbuf_ki[3] = t3; } } else { for (int c = 0; c < Cg; c++, inpbuf_ki += CONV_NR, inptr_ki += inp_planesize) { - float t0 = inptr_ki[0], t1 = inptr_ki[stride_x]; - float t2 = inptr_ki[stride_x*2], t3 = inptr_ki[stride_x*3]; - inpbuf_ki[0] = t0; inpbuf_ki[1] = t1; - inpbuf_ki[2] = t2; inpbuf_ki[3] = t3; + float t0 = inptr_ki[0], t1 = inptr_ki[stride_w]; + float t2 = inptr_ki[stride_w * 2], t3 = inptr_ki[stride_w * 3]; + inpbuf_ki[0] = t0; + inpbuf_ki[1] = t1; + inpbuf_ki[2] = t2; + inpbuf_ki[3] = t3; } } i += 4; @@ -517,64 +903,117 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr i++; x0++; } + int mask = x0 >= W0; y0 += mask; x0 &= mask - 1; + + mask = y0 >= H0; + z0 += mask; + y0 &= mask - 1; } } } } - yx0 = yx0_saved; - float* weights = conv->weightsBufPtr + g * Kg_aligned * HkWkCg; - const float* biasptr = conv->biasBuf.data() + Kg * g; + zyx0 = zyx0_saved; + + // spacial branch for depth-wise convolution implemented using generic convolution. + // In this case, CONV_MR is 1, and CONV_NR is the same. + if (conv->conv_type == _FX_CONV_TYPE_DEPTHWISE_REMAIN) + { + size_t outofs = (n * ngroups + g) * out_planesize + zyx0; + float *cptr0 = cbuf_task; + float *weights = conv->weightsBufPtr + g * padded_ksize; + int out_width = zyx_block_limit - zyx0; + float *outptr = out + outofs; + const float biasVal = *(conv->biasBuf.data() + g); + for (int stripe = 0; stripe < nstripes; stripe++) + { + const float *inptr = inpbuf_task + stripe * stripesize; + const int outLen = std::min(out_width - stripe * CONV_NR, CONV_NR); + bool ifBuffer = outLen < CONV_NR; + float *cptr = outptr + stripe * CONV_NR; + if (ifBuffer) + { + memcpy(cptr0, cptr, outLen * sizeof(cptr[0])); + cptr = cptr0; + } +#if CV_TRY_AVX2 + if (conv->useAVX2 && outLen > CONV_NR/3) + opt_AVX2::convBlockMR1(DkHkWkCg, weights, inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct); + else +#endif + convBlockMR1(DkHkWkCg, weights, inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct, outLen); + + if (ifBuffer) + { + memcpy(outptr + stripe * CONV_NR, cptr, outLen * sizeof(cptr[0])); + } + } + if (activ) + activ->forwardSlice(outptr, outptr, out_width, out_planesize, g, g + 1); + continue; + } + + float *weights = conv->weightsBufPtr + g * Kg_aligned * DkHkWkCg; + const float *biasptr = conv->biasBuf.data() + Kg * g; int ldc = nstripes * CONV_NR; - // 2. do convolution, compute Kg x (yx_block_limit - yx0) part of the output tensor + // 2. do convolution, compute Kg x (zyx_block_limit - zyx0) part of the output tensor + int out_width = zyx_block_limit - zyx0; for (int k0_block = k0; k0_block < k1; k0_block += K_BLOCK_SIZE) { int k1_block = k0_block + K_BLOCK_SIZE < k1 ? k0_block + K_BLOCK_SIZE : k1; - for (int c0 = 0; c0 < HkWkCg; c0 += C_BLOCK_SIZE) + for (int c0 = 0; c0 < DkHkWkCg; c0 += C_BLOCK_SIZE) { - int c1 = c0 + C_BLOCK_SIZE < HkWkCg ? c0 + C_BLOCK_SIZE : HkWkCg; + int c1 = c0 + C_BLOCK_SIZE < DkHkWkCg ? c0 + C_BLOCK_SIZE : DkHkWkCg; for (int stripe = 0; stripe < nstripes; stripe++) { - float* wptr = weights + k0_block*HkWkCg + c0*CONV_MR; - const float* inptr = inpbuf_task + stripe*stripesize + c0 * CONV_NR; - float* cptr = cbuf_task + stripe * CONV_NR; + const int outLen = std::min(out_width - stripe * CONV_NR, CONV_NR); + +#if CV_TRY_AVX2 || CV_TRY_NEON + // The possible CONV_NR is 28, 24, 12, so the possible CONV_NR/3 is 9, 8, 4. + bool runOpt = outLen > std::min(8, CONV_NR/3); +#endif + float *wptr = weights + k0_block * DkHkWkCg + c0 * CONV_MR; + const float *inptr = inpbuf_task + stripe * stripesize + c0 * CONV_NR; + float *cptr = cbuf_task + stripe * CONV_NR; for (int k = k0_block; k < k1_block; k += CONV_MR, - wptr += HkWkCg * CONV_MR, cptr += CONV_MR * ldc) + wptr += DkHkWkCg * CONV_MR, cptr += CONV_MR * ldc) { #if CV_TRY_AVX2 - if (conv->useAVX2) + if (conv->useAVX2 && runOpt) opt_AVX2::convBlock_AVX2(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); else #endif #if CV_TRY_NEON - if (conv->useNEON) + if (conv->useNEON && runOpt) opt_NEON::convBlock_NEON(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); else #endif - convBlock(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0); + // The possible outLen range is 24 or 8~1. + convBlock(c1 - c0, wptr, inptr, cptr, ldc, c0 == 0, outLen); } } } - size_t outofs = ((n*ngroups + g) * Kg + k0_block) * out_planesize + yx0; - int out_width = yx_block_limit - yx0; - const float* cptr = cbuf_task; + size_t outofs = ((n * ngroups + g) * Kg + k0_block) * out_planesize + zyx0; + const float *cptr = cbuf_task; - float* outptr = out + outofs; - const float* pbptr = fusedAddPtr0 ? fusedAddPtr0 + outofs : 0; + float *outptr = out + outofs; + const float *pbptr = fusedAddPtr0 ? fusedAddPtr0 + outofs : 0; for (int k = k0_block; k < k1_block; k++, cptr += ldc, outptr += out_planesize, - pbptr += (pbptr ? out_planesize : 0)) - { + pbptr += (pbptr ? out_planesize : 0)) { float biasval = biasptr[k]; int j = 0; #if CV_SIMD128 - v_float32x4 vbias = v_setall_f32(biasval), vmax = v_setall_f32(maxval), vmin = v_setall_f32(minval); + v_float32x4 vbias = v_setall_f32(biasval); + v_float32x4 vmax = v_setall_f32(maxval); + v_float32x4 vmin = v_setall_f32(minval); + if (pbptr) { for (; j + 7 < out_width; j += 8) @@ -614,19 +1053,15 @@ void runFastConv2d(InputArray _input, OutputArray _output, const Ptr } #endif if (pbptr) { - for (; j < out_width; j++) - { + for (; j < out_width; j++) { float v = cptr[j] + biasval; v += pbptr[j]; if (ifMinMaxAct) v = std::min(std::max(v, minval), maxval); outptr[j] = v; } - } - else - { - for (; j < out_width; j++) - { + } else { + for (; j < out_width; j++) { float v = cptr[j] + biasval; if (ifMinMaxAct) diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp index 01f5edee48..895ad562bb 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.hpp @@ -42,19 +42,20 @@ enum { _FX_WINO_NATOMS_F32 = _FX_WINO_AREA / _FX_WINO_ATOM_F32, // for AVX2, it is 8, otherwise, it's 16. }; -enum { _FX_CONV_TYPE_GENERIC=0, _FX_CONV_TYPE_DEPTHWISE=1, _FX_CONV_TYPE_WINOGRAD3X3=2 }; +enum { _FX_CONV_TYPE_GENERIC=0, _FX_CONV_TYPE_DEPTHWISE=1, _FX_CONV_TYPE_WINOGRAD3X3=2, _FX_CONV_TYPE_DEPTHWISE_REMAIN=3 }; +enum { CONV_1D = 0, CONV_2D = 1, CONV_3D = 2 }; #endif namespace cv { namespace dnn { -struct FastConv2d +struct FastConv { int ngroups; - int K, C, Hk, Wk; - int stride_y, stride_x; - int dilation_y, dilation_x; - int pad_top, pad_bottom, pad_left, pad_right; + int K, C, Hk, Wk, Dk; + int stride_h, stride_w, stride_d; + int dilation_h, dilation_w, dilation_d; + int pad_top, pad_bottom, pad_left, pad_right, pad_front, pad_behind; std::vector weightsBuf; // For generic Conv 2D float* weightsBufPtr; @@ -62,57 +63,55 @@ struct FastConv2d float* weightsWinoBufPtr; std::vector biasBuf; int conv_type; + int conv_dim; // Flag for conv1d, conv2d, or conv3d. #if CV_SIMD128 bool useSIMD128 = true; #else bool useSIMD128 = false; #endif -#if CV_TRY_AVX2 - bool useAVX2 = checkHardwareSupport(CPU_AVX2); -#else - bool useAVX2 = false; -#endif - #if CV_NEON bool useNEON = checkHardwareSupport(CPU_NEON); #else bool useNEON = false; #endif + + bool useAVX = checkHardwareSupport(CPU_AVX); + bool useAVX2 = checkHardwareSupport(CPU_AVX2); + bool useRVV = checkHardwareSupport(CPU_RVV); }; -// return a FastConv2d instance. -Ptr initFastConv2d( +// return a FastConv instance. +Ptr initFastConv( + InputArray weightsMat, + float* srcBias, int ngroups, - int K, int C, int Hk, int Wk, - int stride_x, int stride_y, - int dilation_x, int dilation_y, + int K, int C, + const std::vector& kernel_size, + const std::vector& strides, + const std::vector& dilations, const std::vector& pads_begin, const std::vector& pads_end, - InputArray weightsMat, - float* srcBias, bool useWinograd); + int conv_dim, + bool useWinograd); // It contains different computing branches, like winograd, 1x1 conv. -void runFastConv2d(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, - const Ptr& actLayer, bool fusedAdd); +void runFastConv(InputArray _input, OutputArray _output, const Ptr& conv, int ntasks, + const Ptr& actLayer, const std::vector& reluslope, bool fusedAdd); -void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, float minval, float maxval, - ActivationLayer* activ, bool ifMinMaxAct); +void runDepthwise(InputArray _input, OutputArray _output, const Ptr& conv, ActivationLayer* activ, + const std::vector& reluslope); -int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); -} // namespace dnn - namespace opt_AVX2 { #if CV_TRY_AVX2 void convBlock_AVX2(int np, const float* a, const float* b, float* c, int ldc, bool init_c); -void depthWiseBlock_AVX2(const float *inptr, float *outptr, const float *weights, float biasval, int *ofstab, int *yxtab, - float minval, float maxval, int Hi, int Wi, int H0, int W0, int ksize, int pad_top, int pad_left, - int dilation_y, int stride_x, int stride_y, int inner_xleft, int inner_xright, int inner_ytop, - int inner_ybottom, bool ifMinMaxAct, bool useSIMD, bool is3x3); +void convBlockMR1(int np, const float* a, const float* b, float *c, const float bias, bool init_c, const float minval, + const float maxval, bool ifMinMaxAct); void _fx_winograd_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock); void _fx_winograd_BtXB_8x8_f32(const float* inptr, int inpstep, float* outptr, int Cg); @@ -122,6 +121,7 @@ void _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, float* bpptr, in #endif } // namespace opt_AVX2 +} // namespace dnn } // namespace cv #endif //OPENCV_FAST_CONVOLUTION_HPP diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp index 7325cc375e..e146c0974e 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.simd.hpp @@ -11,9 +11,132 @@ namespace cv { namespace dnn { -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c) +static void convBlockMR1NoSIMD(int np, const float* a, const float* b, float *c, const float bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct, const int outLen) +{ + std::vector cbuffer(outLen, 0); + float* cbuf = cbuffer.data(); + for( int p = 0; p < np; p++ ) + { + float ai = a[p]; + for( int j = 0; j < outLen; j++ ) + cbuf[j] += b[CONV_NR*p + j] * ai; + } + + if (init_c) + { + for(int j = 0; j < outLen; j++) + { + c[j] += cbuf[j] + bias; + if (ifMinMaxAct) + c[j] = std::min(std::max(c[j], minval), maxval); + } + } + else + { + for(int j = 0; j < outLen; j++) + { + c[j] = cbuf[j] + bias; + if (ifMinMaxAct) + c[j] = std::min(std::max(c[j], minval), maxval); + } + } +} + +void convBlockMR1(int np, const float* a, const float* b, float *c, const float bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct, const int outLen) +{ +#if CV_SIMD128 + // The outLen represents the valid output value in CONV_NR length. + // When outLen is very small, we use the no-SIMD branch. + const int CONV_NRby3 = CONV_NR/3; + if (outLen > CONV_NRby3) + { + v_float32x4 c0 = v_setall_f32(bias), c1 = c0, c2 = c0; // CONV_NR == 12 +#if CONV_NR == 28 || CONV_NR == 24 + v_float32x4 c3 = c0, c4 = c0, c5 = c0; +#endif +#if CONV_NR == 28 + v_float32x4 c6 = c0; +#endif + for (int p = 0; p < np; p++, a++, b += CONV_NR) + { + v_float32x4 a0 = v_setall_f32(a[0]); + v_float32x4 b0 = v_load(b), b1 = v_load(b + 4), b2 = v_load(b + 8); +#if CONV_NR == 28 || CONV_NR == 24 + v_float32x4 b3 = v_load(b + 12), b4 = v_load(b + 16), b5 = v_load(b + 20); +#endif +#if CONV_NR == 28 + v_float32x4 b6 = v_load(b + 24); +#endif + + c0 = v_fma(b0, a0, c0); + c1 = v_fma(b1, a0, c1); + c2 = v_fma(b2, a0, c2); +#if CONV_NR == 28 || CONV_NR == 24 + c3 = v_fma(b3, a0, c3); + c4 = v_fma(b4, a0, c4); + c5 = v_fma(b5, a0, c5); +#endif +#if CONV_NR == 28 + c6 = v_fma(b6, a0, c6); +#endif + } + + if (init_c) + { + c0 += v_load(c); + c1 += v_load(c + 4); + c2 += v_load(c + 8); +#if CONV_NR == 28 || CONV_NR == 24 + c3 += v_load(c + 12); + c4 += v_load(c + 16); + c5 += v_load(c + 20); +#endif +#if CONV_NR == 28 + c6 += v_load(c + 24); +#endif + } + + if (ifMinMaxAct) + { + v_float32x4 vmax = v_setall_f32(maxval), vmin = v_setall_f32(minval); + c0 = v_min(v_max(c0, vmin), vmax); + c1 = v_min(v_max(c1, vmin), vmax); + c2 = v_min(v_max(c2, vmin), vmax); +#if CONV_NR == 28 || CONV_NR == 24 + c3 = v_min(v_max(c3, vmin), vmax); + c4 = v_min(v_max(c4, vmin), vmax); + c5 = v_min(v_max(c5, vmin), vmax); +#endif +#if CONV_NR == 28 + c6 = v_min(v_max(c6, vmin), vmax); +#endif + } + + v_store(c, c0); + v_store(c + 4, c1); + v_store(c + 8, c2); +#if CONV_NR == 28 || CONV_NR == 24 + v_store(c + 12, c3); + v_store(c + 16, c4); + v_store(c + 20, c5); +#endif +#if CONV_NR == 28 + v_store(c + 24, c6); +#endif + } + else + convBlockMR1NoSIMD(np, a, b, c, bias, init_c, minval, maxval, ifMinMaxAct, outLen); +#else + convBlockMR1NoSIMD(np, a, b, c, bias, init_c, minval, maxval, ifMinMaxAct, outLen); +#endif +} + +#if CV_SIMD128 +#if CONV_MR == 4 && CONV_NR == 24 +static void convBlock4x24(int np, const float* a, const float* b, float* c, int ldc, bool init_c) { -#if CV_SIMD128 && CONV_MR == 4 && CONV_NR == 24 v_float32x4 c0 = v_setzero_f32(), c1 = c0, c2 = c0, c3 = c0, c4 = c0, c5 = c0; v_float32x4 c6 = v_setzero_f32(), c7 = c6, c8 = c6, c9 = c6, c10 = c6, c11 = c6; v_float32x4 c12 = v_setzero_f32(), c13 = c12, c14 = c12, c15 = c12, c16 = c12, c17 = c12; @@ -115,29 +238,156 @@ void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool i v_store(c + ldc * 3 + 12, c21); v_store(c + ldc * 3 + 16, c22); v_store(c + ldc * 3 + 20, c23); -#else - float cbuf[CONV_MR * CONV_NR]; - memset(cbuf, 0, sizeof(cbuf)); +} +#endif + +static void convBlock4x8(int np, const float* a, const float* b, float* c, int ldc, bool init_c) +{ + CV_Assert(CONV_NR >= 4); + v_float32x4 c0 = v_setzero_f32(), c1 = c0, c2 = c0, c3 = c0; + v_float32x4 c4 = c0, c5 = c0, c6 = c0, c7 = c0; + + for (int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR) + { + v_float32x4 a0 = v_setall_f32(a[0]); + v_float32x4 a1 = v_setall_f32(a[1]); + v_float32x4 a2 = v_setall_f32(a[2]); + v_float32x4 a3 = v_setall_f32(a[3]); + + v_float32x4 b0 = v_load(b), b1 = v_load(b + 4); + + c0 = v_fma(b0, a0, c0); + c1 = v_fma(b1, a0, c1); + + c2 = v_fma(b0, a1, c2); + c3 = v_fma(b1, a1, c3); + + c4 = v_fma(b0, a2, c4); + c5 = v_fma(b1, a2, c5); + + c6 = v_fma(b0, a3, c6); + c7 = v_fma(b1, a3, c7); + } + + if (!init_c) + { + c0 += v_load(c); + c1 += v_load(c + 4); + + c2 += v_load(c + ldc); + c3 += v_load(c + ldc + 4); + + c4 += v_load(c + ldc*2); + c5 += v_load(c + ldc*2 + 4); + + c6 += v_load(c + ldc*3); + c7 += v_load(c + ldc*3 + 4); + } + + v_store(c, c0); + v_store(c + 4, c1); + v_store(c + ldc, c2); + v_store(c + ldc + 4, c3); + v_store(c + ldc * 2, c4); + v_store(c + ldc * 2 + 4, c5); + v_store(c + ldc * 3, c6); + v_store(c + ldc * 3 + 4, c7); +} + +static void convBlock4x4(int np, const float* a, const float* b, float* c, int ldc, bool init_c) +{ + CV_Assert(CONV_NR >= 4); + v_float32x4 c0 = v_setzero_f32(), c1 = c0, c2 = c0, c3 = c0; + + for (int p = 0; p < np; p++, a += CONV_MR, b += CONV_NR) + { + v_float32x4 a0 = v_setall_f32(a[0]); + v_float32x4 a1 = v_setall_f32(a[1]); + v_float32x4 a2 = v_setall_f32(a[2]); + v_float32x4 a3 = v_setall_f32(a[3]); + + v_float32x4 b0 = v_load(b); + + c0 = v_fma(b0, a0, c0); + c1 = v_fma(b0, a1, c1); + c2 = v_fma(b0, a2, c2); + c3 = v_fma(b0, a3, c3); + } + + if (!init_c) + { + c0 += v_load(c); + c1 += v_load(c + ldc); + c2 += v_load(c + ldc*2); + c3 += v_load(c + ldc*3); + } + + v_store(c, c0); + v_store(c + ldc, c1); + v_store(c + ldc * 2, c2); + v_store(c + ldc * 3, c3); +} +#endif + +static void convBlockNoSIMD(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen) +{ + std::vector cbuffer(CONV_MR * outLen, 0); + float* cbuf = cbuffer.data(); for( int p = 0; p < np; p++ ) { for( int i = 0; i < CONV_MR; i++ ) { float ai = a[CONV_MR*p + i]; - for( int j = 0; j < CONV_NR; j++ ) - cbuf[i * CONV_NR+j] += b[CONV_NR*p + j] * ai; + for( int j = 0; j < outLen; j++ ) + cbuf[i * outLen+j] += b[CONV_NR*p + j] * ai; } } - if (!init_c) { - for(int i = 0; i < CONV_MR; i++) { - for(int j = 0; j < CONV_NR; j++) - c[i*ldc + j] += cbuf[i*CONV_NR + j]; - } - } else { - for(int i = 0; i < CONV_MR; i++) { - for(int j = 0; j < CONV_NR; j++) - c[i*ldc + j] = cbuf[i*CONV_NR + j]; + + if (!init_c) + { + for(int i = 0; i < CONV_MR; i++) + { + for(int j = 0; j < outLen; j++) + c[i*ldc + j] += cbuf[i*outLen + j]; } } + else + { + for(int i = 0; i < CONV_MR; i++) + { + for(int j = 0; j < outLen; j++) + c[i*ldc + j] = cbuf[i*outLen + j]; + } + } +} + +void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen) +{ + // The possible outLen range is [24, 8~1]. +#if CV_SIMD128 +#if CONV_MR == 4 && CONV_NR == 24 + const int CONV_NRby3 = CONV_NR/3; + if (outLen > CONV_NRby3) + { + convBlock4x24(np, a, b, c, ldc, init_c); + return; + } +#endif + + if (outLen <= 8 && outLen > 4) + { + convBlock4x8(np, a, b, c, ldc, init_c); + return; + } + + if (outLen <= 4 && outLen > 1) + { + convBlock4x4(np, a, b, c, ldc, init_c); + return; + } + convBlockNoSIMD(np, a, b, c, ldc, init_c, outLen); +#else + convBlockNoSIMD(np, a, b, c, ldc, init_c, outLen); #endif } } // namespace dnn diff --git a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp index 10b55f3604..e3b8088410 100644 --- a/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp +++ b/modules/dnn/src/layers/fast_convolution/winograd_3x3s1_f63.cpp @@ -920,7 +920,7 @@ _fx_winograd_AtXA_8x8_f32(const float* inptr, int inpstep, #endif } -int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) { Mat input = _input.getMat(); @@ -1144,7 +1144,7 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu #else -int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, +int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct) { return 0; diff --git a/modules/dnn/src/layers/layers_common.simd.hpp b/modules/dnn/src/layers/layers_common.simd.hpp index f706abfa76..eb1735639e 100644 --- a/modules/dnn/src/layers/layers_common.simd.hpp +++ b/modules/dnn/src/layers/layers_common.simd.hpp @@ -46,10 +46,6 @@ namespace cv { namespace dnn { CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN -void fastConv( const float* weights, size_t wstep, const float* bias, - const float* rowbuf, float* output, const int* outShape, - int blockSize, int vecsize, int vecsize_aligned, - const float* relu, bool initOutput ); void fastDepthwiseConv( const float* weights, int kernel_h, int kernel_w, int stride_h, int stride_w, @@ -74,305 +70,6 @@ void fastGEMM( const float* aptr, size_t astep, const float* bptr, #define _mm256_fmadd_ps(a, b, c) _mm256_add_ps(c, _mm256_mul_ps(a, b)) #endif -enum { FASCONV_BASE_VECSZ = 4 }; - -void fastConv( const float* weights, size_t wstep, const float* bias, - const float* rowbuf, float* output, const int* outShape, - int blockSize, int vecsize, int vecsize_aligned, - const float* relu, bool initOutput ) -{ - CV_Assert(isAligned<32>(weights)); - - int outCn = outShape[1]; - size_t outPlaneSize = outShape[2]*outShape[3]; - float r0 = 1.f, r1 = 1.f, r2 = 1.f; - __m128 vr0 = _mm_set1_ps(1.f), vr1 = vr0, vr2 = vr0, z = _mm_setzero_ps(); - int CV_DECL_ALIGNED(16) maskbuf[FASCONV_BASE_VECSZ] = {0}; - int rsz = blockSize % FASCONV_BASE_VECSZ; - for( int i = 0; i < rsz; i++ ) - maskbuf[FASCONV_BASE_VECSZ - i - 1] = -1; - __m128 mask = _mm_loadu_ps((const float*)maskbuf); - - // now compute dot product of the weights - // and im2row-transformed part of the tensor - for( int i = 0; i < outCn; i += 3 ) - { - const float* wptr0 = weights + i*wstep; - const float* wptr1 = wptr0 + wstep; - const float* wptr2 = wptr1 + wstep; - float* outptr0 = output + i*outPlaneSize; - float* outptr1 = outptr0 + outPlaneSize; - float* outptr2 = outptr1 + outPlaneSize; - float bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2]; - - if( i+2 >= outCn ) - { - wptr2 = wptr1; - outptr2 = outptr1; - bias2 = bias1; - if( i+1 >= outCn ) - { - wptr2 = wptr1 = wptr0; - outptr2 = outptr1 = outptr0; - bias2 = bias1 = bias0; - } - } - - if( relu ) - { - r0 = relu[i]; r1 = relu[i+1]; r2 = relu[i+2]; - if( i+2 >= outCn ) - { - r2 = r1; - if( i+1 >= outCn ) - r2 = r1 = r0; - } - vr0 = _mm_set1_ps(r0); - vr1 = _mm_set1_ps(r1); - vr2 = _mm_set1_ps(r2); - } - - int j = 0; - for( ; j < blockSize; j += FASCONV_BASE_VECSZ ) - { - bool tail = false; - if (j + FASCONV_BASE_VECSZ > blockSize) - { - if (j == 0) - break; - j = blockSize - FASCONV_BASE_VECSZ; - tail = true; - } - int k = 0; - const float* rptr = rowbuf + j*vecsize_aligned; - - __m256 vs00 = _mm256_setzero_ps(), vs01 = _mm256_setzero_ps(), - vs02 = _mm256_setzero_ps(), vs03 = _mm256_setzero_ps(), - vs10 = _mm256_setzero_ps(), vs11 = _mm256_setzero_ps(), - vs12 = _mm256_setzero_ps(), vs13 = _mm256_setzero_ps(), - vs20 = _mm256_setzero_ps(), vs21 = _mm256_setzero_ps(), - vs22 = _mm256_setzero_ps(), vs23 = _mm256_setzero_ps(); - -#if CV_AVX512_SKX // AVX512VL is necessary to avoid register spilling - if (vecsize >= 32) - { - __m512 vs00_5 = _mm512_setzero_ps(), vs01_5 = _mm512_setzero_ps(), - vs02_5 = _mm512_setzero_ps(), vs03_5 = _mm512_setzero_ps(), - vs10_5 = _mm512_setzero_ps(), vs11_5 = _mm512_setzero_ps(), - vs12_5 = _mm512_setzero_ps(), vs13_5 = _mm512_setzero_ps(), - vs20_5 = _mm512_setzero_ps(), vs21_5 = _mm512_setzero_ps(), - vs22_5 = _mm512_setzero_ps(), vs23_5 = _mm512_setzero_ps(); - - for (; k <= vecsize - 16; k += 16, rptr += 16) - { - __m512 w0 = _mm512_loadu_ps(wptr0 + k); - __m512 w1 = _mm512_loadu_ps(wptr1 + k); - __m512 w2 = _mm512_loadu_ps(wptr2 + k); - __m512 r0 = _mm512_loadu_ps(rptr); - - vs00_5 = _mm512_fmadd_ps(w0, r0, vs00_5); - vs10_5 = _mm512_fmadd_ps(w1, r0, vs10_5); - vs20_5 = _mm512_fmadd_ps(w2, r0, vs20_5); - - r0 = _mm512_loadu_ps(rptr + vecsize_aligned); - vs01_5 = _mm512_fmadd_ps(w0, r0, vs01_5); - vs11_5 = _mm512_fmadd_ps(w1, r0, vs11_5); - vs21_5 = _mm512_fmadd_ps(w2, r0, vs21_5); - - r0 = _mm512_loadu_ps(rptr + vecsize_aligned*2); - vs02_5 = _mm512_fmadd_ps(w0, r0, vs02_5); - vs12_5 = _mm512_fmadd_ps(w1, r0, vs12_5); - vs22_5 = _mm512_fmadd_ps(w2, r0, vs22_5); - - r0 = _mm512_loadu_ps(rptr + vecsize_aligned*3); - vs03_5 = _mm512_fmadd_ps(w0, r0, vs03_5); - vs13_5 = _mm512_fmadd_ps(w1, r0, vs13_5); - vs23_5 = _mm512_fmadd_ps(w2, r0, vs23_5); - } - /* - * now fold the 512 bit accumulator vectors into 256 bit vectors so that the AVX2 code can finish - * the tail of the vector - */ - vs00 = _mm256_add_ps( _mm512_extractf32x8_ps(vs00_5, 0), _mm512_extractf32x8_ps(vs00_5, 1)); - vs10 = _mm256_add_ps( _mm512_extractf32x8_ps(vs10_5, 0), _mm512_extractf32x8_ps(vs10_5, 1)); - vs20 = _mm256_add_ps( _mm512_extractf32x8_ps(vs20_5, 0), _mm512_extractf32x8_ps(vs20_5, 1)); - - vs01 = _mm256_add_ps( _mm512_extractf32x8_ps(vs01_5, 0), _mm512_extractf32x8_ps(vs01_5, 1)); - vs11 = _mm256_add_ps( _mm512_extractf32x8_ps(vs11_5, 0), _mm512_extractf32x8_ps(vs11_5, 1)); - vs21 = _mm256_add_ps( _mm512_extractf32x8_ps(vs21_5, 0), _mm512_extractf32x8_ps(vs21_5, 1)); - - vs02 = _mm256_add_ps( _mm512_extractf32x8_ps(vs02_5, 0), _mm512_extractf32x8_ps(vs02_5, 1)); - vs12 = _mm256_add_ps( _mm512_extractf32x8_ps(vs12_5, 0), _mm512_extractf32x8_ps(vs12_5, 1)); - vs22 = _mm256_add_ps( _mm512_extractf32x8_ps(vs22_5, 0), _mm512_extractf32x8_ps(vs22_5, 1)); - - vs03 = _mm256_add_ps( _mm512_extractf32x8_ps(vs03_5, 0), _mm512_extractf32x8_ps(vs03_5, 1)); - vs13 = _mm256_add_ps( _mm512_extractf32x8_ps(vs13_5, 0), _mm512_extractf32x8_ps(vs13_5, 1)); - vs23 = _mm256_add_ps( _mm512_extractf32x8_ps(vs23_5, 0), _mm512_extractf32x8_ps(vs23_5, 1)); - } -#endif - - for (; k < vecsize; k += 8, rptr += 8 ) - { - __m256 w0 = _mm256_load_ps(wptr0 + k); - __m256 w1 = _mm256_load_ps(wptr1 + k); - __m256 w2 = _mm256_load_ps(wptr2 + k); - __m256 r0 = _mm256_load_ps(rptr); - - vs00 = _mm256_fmadd_ps(w0, r0, vs00); - vs10 = _mm256_fmadd_ps(w1, r0, vs10); - vs20 = _mm256_fmadd_ps(w2, r0, vs20); - - r0 = _mm256_load_ps(rptr + vecsize_aligned); - vs01 = _mm256_fmadd_ps(w0, r0, vs01); - vs11 = _mm256_fmadd_ps(w1, r0, vs11); - vs21 = _mm256_fmadd_ps(w2, r0, vs21); - - r0 = _mm256_load_ps(rptr + vecsize_aligned*2); - vs02 = _mm256_fmadd_ps(w0, r0, vs02); - vs12 = _mm256_fmadd_ps(w1, r0, vs12); - vs22 = _mm256_fmadd_ps(w2, r0, vs22); - - r0 = _mm256_load_ps(rptr + vecsize_aligned*3); - vs03 = _mm256_fmadd_ps(w0, r0, vs03); - vs13 = _mm256_fmadd_ps(w1, r0, vs13); - vs23 = _mm256_fmadd_ps(w2, r0, vs23); - } - - __m256 t0 = _mm256_hadd_ps(_mm256_hadd_ps(vs00, vs01), _mm256_hadd_ps(vs02, vs03)); - __m256 t1 = _mm256_hadd_ps(_mm256_hadd_ps(vs10, vs11), _mm256_hadd_ps(vs12, vs13)); - __m256 t2 = _mm256_hadd_ps(_mm256_hadd_ps(vs20, vs21), _mm256_hadd_ps(vs22, vs23)); - - t0 = _mm256_add_ps(t0, _mm256_permute2f128_ps(t0, t0, 1)); - t1 = _mm256_add_ps(t1, _mm256_permute2f128_ps(t1, t1, 1)); - t2 = _mm256_add_ps(t2, _mm256_permute2f128_ps(t2, t2, 1)); - - __m128 s0, s1, s2; - - if( initOutput ) - { - s0 = _mm_set1_ps(bias0); - s1 = _mm_set1_ps(bias1); - s2 = _mm_set1_ps(bias2); - } - else - { - s0 = _mm_loadu_ps(outptr0 + j); - s1 = _mm_loadu_ps(outptr1 + j); - s2 = _mm_loadu_ps(outptr2 + j); - } - - s0 = _mm_add_ps(s0, _mm256_castps256_ps128(t0)); - s1 = _mm_add_ps(s1, _mm256_castps256_ps128(t1)); - s2 = _mm_add_ps(s2, _mm256_castps256_ps128(t2)); - - if( relu ) - { - __m128 m0 = _mm_cmp_ps(s0, z, _CMP_GT_OS); - __m128 m1 = _mm_cmp_ps(s1, z, _CMP_GT_OS); - __m128 m2 = _mm_cmp_ps(s2, z, _CMP_GT_OS); - s0 = _mm_blendv_ps(_mm_mul_ps(s0, vr0), s0, m0); - s1 = _mm_blendv_ps(_mm_mul_ps(s1, vr1), s1, m1); - s2 = _mm_blendv_ps(_mm_mul_ps(s2, vr2), s2, m2); - } - - if( tail ) - { - s0 = _mm_blendv_ps(_mm_loadu_ps(outptr0 + j), s0, mask); - s1 = _mm_blendv_ps(_mm_loadu_ps(outptr1 + j), s1, mask); - s2 = _mm_blendv_ps(_mm_loadu_ps(outptr2 + j), s2, mask); - } - - _mm_storeu_ps(outptr0 + j, s0); - _mm_storeu_ps(outptr1 + j, s1); - _mm_storeu_ps(outptr2 + j, s2); - } - - for( ; j <= blockSize - 2; j += 2 ) - { - const float* rptr0 = rowbuf + j*vecsize_aligned; - const float* rptr1 = rowbuf + (j+1)*vecsize_aligned; - float s00, s01, s10, s11, s20, s21; - - if( initOutput ) - { - s00 = s01 = bias0; - s10 = s11 = bias1; - s20 = s21 = bias2; - } - else - { - s00 = outptr0[j]; s01 = outptr0[j+1]; - s10 = outptr1[j]; s11 = outptr1[j+1]; - s20 = outptr2[j]; s21 = outptr2[j+1]; - } - - for( int k = 0; k < vecsize; k++ ) - { - float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; - float r = rptr0[k]; - s00 += w0*r; s10 += w1*r; s20 += w2*r; - r = rptr1[k]; - s01 += w0*r; s11 += w1*r; s21 += w2*r; - } - - if( relu ) - { - s00 = s00 > 0.f ? s00 : s00*r0; - s01 = s01 > 0.f ? s01 : s01*r0; - s10 = s10 > 0.f ? s10 : s10*r1; - s11 = s11 > 0.f ? s11 : s11*r1; - s20 = s20 > 0.f ? s20 : s20*r2; - s21 = s21 > 0.f ? s21 : s21*r2; - } - - outptr0[j] = s00; - outptr0[j+1] = s01; - outptr1[j] = s10; - outptr1[j+1] = s11; - outptr2[j] = s20; - outptr2[j+1] = s21; - } - - for( ; j < blockSize; j++ ) - { - const float* rptr0 = rowbuf + j*vecsize_aligned; - float s00, s10, s20; - - if( initOutput ) - { - s00 = bias0; - s10 = bias1; - s20 = bias2; - } - else - { - s00 = outptr0[j]; - s10 = outptr1[j]; - s20 = outptr2[j]; - } - - for( int k = 0; k < vecsize; k++ ) - { - float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; - float r = rptr0[k]; - s00 += w0*r; s10 += w1*r; s20 += w2*r; - } - - if( relu ) - { - s00 = s00 > 0.f ? s00 : s00*r0; - s10 = s10 > 0.f ? s10 : s10*r1; - s20 = s20 > 0.f ? s20 : s20*r2; - } - - outptr0[j] = s00; - outptr1[j] = s10; - outptr2[j] = s20; - } - } - _mm256_zeroupper(); -} - static inline void _mm256_load_deinterleave(const float* ptr, __m256& a, __m256& b) { __m256 t0 = _mm256_loadu_ps(ptr); @@ -957,198 +654,6 @@ void fastGEMM1T( const float* vec, const float* weights, } } -enum { FASCONV_BASE_VECSZ = 8 }; -void fastConv( const float* weights, size_t wstep, const float* bias, - const float* rowbuf, float* output, const int* outShape, - int blockSize, int vecsize, int vecsize_aligned, - const float* relu, bool initOutput ) -{ - const int vlm1 = vsetvlmax_e32m1(); - int outCn = outShape[1]; - size_t outPlaneSize = outShape[2]*outShape[3]; - // now compute dot product of the weights - // and im2row-transformed part of the tensor - for( int i = 0; i < outCn; i += 3 ) - { - int unroll_tail = FASCONV_BASE_VECSZ; - const float* wptr0 = weights + i*wstep; - const float* wptr1 = wptr0 + wstep; - const float* wptr2 = wptr1 + wstep; - float* outptr0 = output + i*outPlaneSize; - float* outptr1 = outptr0 + outPlaneSize; - float* outptr2 = outptr1 + outPlaneSize; - float bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2]; - - if( i+2 >= outCn ) - { - wptr2 = wptr1; - outptr2 = outptr1; - bias2 = bias1; - if( i+1 >= outCn ) - { - wptr2 = wptr1 = wptr0; - outptr2 = outptr1 = outptr0; - bias2 = bias1 = bias0; - } - } - - int j = 0; - for( ; j < blockSize; j += FASCONV_BASE_VECSZ ) - { - const float* rptr = rowbuf + j*vecsize_aligned; - const float *rptr1 = rptr + vecsize_aligned*1, - *rptr2 = rptr + vecsize_aligned*2, - *rptr3 = rptr + vecsize_aligned*3, - *rptr4 = rptr + vecsize_aligned*4, - *rptr5 = rptr + vecsize_aligned*5, - *rptr6 = rptr + vecsize_aligned*6, - *rptr7 = rptr + vecsize_aligned*7; - if (j + FASCONV_BASE_VECSZ > blockSize) - { - unroll_tail = blockSize - j; - rptr1 = rptr + vecsize_aligned*std::min(1, unroll_tail-1), - rptr2 = rptr + vecsize_aligned*std::min(2, unroll_tail-1), - rptr3 = rptr + vecsize_aligned*std::min(3, unroll_tail-1), - rptr4 = rptr + vecsize_aligned*std::min(4, unroll_tail-1), - rptr5 = rptr + vecsize_aligned*std::min(5, unroll_tail-1), - rptr6 = rptr + vecsize_aligned*std::min(6, unroll_tail-1), - rptr7 = rptr + vecsize_aligned*std::min(7, unroll_tail-1); - } - - int vl, avl = vecsize; - vfloat32m1_t - vs00 = vfmv_v_f_f32m1(0, vlm1), vs10 = vfmv_v_f_f32m1(0, vlm1), vs20 = vfmv_v_f_f32m1(0, vlm1), - vs01 = vfmv_v_f_f32m1(0, vlm1), vs11 = vfmv_v_f_f32m1(0, vlm1), vs21 = vfmv_v_f_f32m1(0, vlm1), - vs02 = vfmv_v_f_f32m1(0, vlm1), vs12 = vfmv_v_f_f32m1(0, vlm1), vs22 = vfmv_v_f_f32m1(0, vlm1), - vs03 = vfmv_v_f_f32m1(0, vlm1), vs13 = vfmv_v_f_f32m1(0, vlm1), vs23 = vfmv_v_f_f32m1(0, vlm1), - vs04 = vfmv_v_f_f32m1(0, vlm1), vs14 = vfmv_v_f_f32m1(0, vlm1), vs24 = vfmv_v_f_f32m1(0, vlm1), - vs05 = vfmv_v_f_f32m1(0, vlm1), vs15 = vfmv_v_f_f32m1(0, vlm1), vs25 = vfmv_v_f_f32m1(0, vlm1), - vs06 = vfmv_v_f_f32m1(0, vlm1), vs16 = vfmv_v_f_f32m1(0, vlm1), vs26 = vfmv_v_f_f32m1(0, vlm1), - vs07 = vfmv_v_f_f32m1(0, vlm1), vs17 = vfmv_v_f_f32m1(0, vlm1), vs27 = vfmv_v_f_f32m1(0, vlm1); - - for (int k = 0; k < vecsize; k += vl, avl -= vl) - { - vl = vsetvl_e32m1(avl); - vfloat32m1_t w0 = vle32_v_f32m1(wptr0 + k, vl); - vfloat32m1_t w1 = vle32_v_f32m1(wptr1 + k, vl); - vfloat32m1_t w2 = vle32_v_f32m1(wptr2 + k, vl); - vfloat32m1_t r0 = vle32_v_f32m1(rptr, vl); - - vs00 = vfmacc_vv_f32m1(vs00, w0, r0, vl); - vs10 = vfmacc_vv_f32m1(vs10, w1, r0, vl); - vs20 = vfmacc_vv_f32m1(vs20, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr1, vl); - vs01 = vfmacc_vv_f32m1(vs01, w0, r0, vl); - vs11 = vfmacc_vv_f32m1(vs11, w1, r0, vl); - vs21 = vfmacc_vv_f32m1(vs21, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr2, vl); - vs02 = vfmacc_vv_f32m1(vs02, w0, r0, vl); - vs12 = vfmacc_vv_f32m1(vs12, w1, r0, vl); - vs22 = vfmacc_vv_f32m1(vs22, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr3, vl); - vs03 = vfmacc_vv_f32m1(vs03, w0, r0, vl); - vs13 = vfmacc_vv_f32m1(vs13, w1, r0, vl); - vs23 = vfmacc_vv_f32m1(vs23, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr4, vl); - vs04 = vfmacc_vv_f32m1(vs04, w0, r0, vl); - vs14 = vfmacc_vv_f32m1(vs14, w1, r0, vl); - vs24 = vfmacc_vv_f32m1(vs24, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr5, vl); - vs05 = vfmacc_vv_f32m1(vs05, w0, r0, vl); - vs15 = vfmacc_vv_f32m1(vs15, w1, r0, vl); - vs25 = vfmacc_vv_f32m1(vs25, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr6, vl); - vs06 = vfmacc_vv_f32m1(vs06, w0, r0, vl); - vs16 = vfmacc_vv_f32m1(vs16, w1, r0, vl); - vs26 = vfmacc_vv_f32m1(vs26, w2, r0, vl); - - r0 = vle32_v_f32m1(rptr7, vl); - vs07 = vfmacc_vv_f32m1(vs07, w0, r0, vl); - vs17 = vfmacc_vv_f32m1(vs17, w1, r0, vl); - vs27 = vfmacc_vv_f32m1(vs27, w2, r0, vl); - - rptr += vl; rptr1 += vl; rptr2 += vl; rptr3 += vl; - rptr4 += vl; rptr5 += vl; rptr6 += vl; rptr7 += vl; - } - - // compute sum of each vs - vfloat32m1_t zero = vfmv_v_f_f32m1(0, vlm1); - // unroll_tail(vl) is required here to be at least FASCONV_BASE_VECSZ, aka 8. - float sum0[FASCONV_BASE_VECSZ], sum1[FASCONV_BASE_VECSZ], sum2[FASCONV_BASE_VECSZ]; - sum0[0] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs00, zero, vlm1)); - sum0[1] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs01, zero, vlm1)); - sum0[2] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs02, zero, vlm1)); - sum0[3] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs03, zero, vlm1)); - sum0[4] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs04, zero, vlm1)); - sum0[5] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs05, zero, vlm1)); - sum0[6] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs06, zero, vlm1)); - sum0[7] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs07, zero, vlm1)); - sum1[0] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs10, zero, vlm1)); - sum1[1] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs11, zero, vlm1)); - sum1[2] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs12, zero, vlm1)); - sum1[3] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs13, zero, vlm1)); - sum1[4] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs14, zero, vlm1)); - sum1[5] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs15, zero, vlm1)); - sum1[6] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs16, zero, vlm1)); - sum1[7] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs17, zero, vlm1)); - sum2[0] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs20, zero, vlm1)); - sum2[1] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs21, zero, vlm1)); - sum2[2] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs22, zero, vlm1)); - sum2[3] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs23, zero, vlm1)); - sum2[4] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs24, zero, vlm1)); - sum2[5] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs25, zero, vlm1)); - sum2[6] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs26, zero, vlm1)); - sum2[7] = vfmv_f_s_f32m1_f32(vfredosum_vs_f32m1_f32m1(zero, vs27, zero, vlm1)); - - // if VLEN = 128, so LMUL = 2 for unroll_tail(vl) = 8. - // otherwise, VLEN >=256, we only use fist 8 element of the vReg. - vfloat32m2_t s0, s1, s2; - if( initOutput ) - { - s0 = vfmv_v_f_f32m2(bias0, unroll_tail); - s1 = vfmv_v_f_f32m2(bias1, unroll_tail); - s2 = vfmv_v_f_f32m2(bias2, unroll_tail); - } - else - { - s0 = vle32_v_f32m2(outptr0 + j, unroll_tail); - s1 = vle32_v_f32m2(outptr1 + j, unroll_tail); - s2 = vle32_v_f32m2(outptr2 + j, unroll_tail); - } - s0 = vfadd_vv_f32m2(vle32_v_f32m2(sum0, unroll_tail), s0, unroll_tail); - s1 = vfadd_vv_f32m2(vle32_v_f32m2(sum1, unroll_tail), s1, unroll_tail); - s2 = vfadd_vv_f32m2(vle32_v_f32m2(sum2, unroll_tail), s2, unroll_tail); - - if( relu ) - { - float r0 = relu[i], r1 = relu[i+1], r2 = relu[i+2]; - if( i+2 >= outCn ) - { - r2 = r1; - if( i+1 >= outCn ) - r2 = r1 = r0; - } - vbool16_t m0 = vmfgt_vf_f32m2_b16(s0, 0, unroll_tail); - vbool16_t m1 = vmfgt_vf_f32m2_b16(s1, 0, unroll_tail); - vbool16_t m2 = vmfgt_vf_f32m2_b16(s2, 0, unroll_tail); - s0 = vmerge_vvm_f32m2(m0, vfmul_vf_f32m2(s0, r0, unroll_tail), s0, unroll_tail); - s1 = vmerge_vvm_f32m2(m1, vfmul_vf_f32m2(s1, r1, unroll_tail), s1, unroll_tail); - s2 = vmerge_vvm_f32m2(m2, vfmul_vf_f32m2(s2, r2, unroll_tail), s2, unroll_tail); - } - - vse32_v_f32m2(outptr0 + j, s0, unroll_tail); - vse32_v_f32m2(outptr1 + j, s1, unroll_tail); - vse32_v_f32m2(outptr2 + j, s2, unroll_tail); - } - } -} - /* Example for load_deinterleave: input: ptr[16] = {1,2,3, ... ,14,15,16} @@ -1345,317 +850,6 @@ void fastDepthwiseConv( const float* wptr, #if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_LASX -enum { FASCONV_BASE_VECSZ = 4 }; - -void fastConv( const float* weights, size_t wstep, const float* bias, - const float* rowbuf, float* output, const int* outShape, - int blockSize, int vecsize, int vecsize_aligned, - const float* relu, bool initOutput ) -{ - int outCn = outShape[1]; - size_t outPlaneSize = outShape[2]*outShape[3]; - float r0 = 1.f, r1 = 1.f, r2 = 1.f; - __m256 t1 = _v256_setall_ps(1.f), t2 = _v256_setall_ps(0.f); - __m128 vr0 = *(__m128*)&t1, vr1 = vr0, vr2 = vr0, z = *(__m128*)&t2; - int CV_DECL_ALIGNED(16) maskbuf[FASCONV_BASE_VECSZ] = {0}; - int rsz = blockSize % FASCONV_BASE_VECSZ; - for( int i = 0; i < rsz; i++ ) - maskbuf[FASCONV_BASE_VECSZ - i - 1] = -1; - __m128i mask = __lsx_vld((const float*)maskbuf, 0); - - // now compute dot product of the weights - // and im2row-transformed part of the tensor - for( int i = 0; i < outCn; i += 3 ) - { - const float* wptr0 = weights + i*wstep; - const float* wptr1 = wptr0 + wstep; - const float* wptr2 = wptr1 + wstep; - float* outptr0 = output + i*outPlaneSize; - float* outptr1 = outptr0 + outPlaneSize; - float* outptr2 = outptr1 + outPlaneSize; - float bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2]; - - if( i+2 >= outCn ) - { - wptr2 = wptr1; - outptr2 = outptr1; - bias2 = bias1; - if( i+1 >= outCn ) - { - wptr2 = wptr1 = wptr0; - outptr2 = outptr1 = outptr0; - bias2 = bias1 = bias0; - } - } - - if( relu ) - { - r0 = relu[i]; r1 = relu[i+1]; r2 = relu[i+2]; - if( i+2 >= outCn ) - { - r2 = r1; - if( i+1 >= outCn ) - r2 = r1 = r0; - } - vr0 = _v256_extract_low(_v256_setall_ps(r0)); - vr1 = _v256_extract_low(_v256_setall_ps(r1)); - vr2 = _v256_extract_low(_v256_setall_ps(r2)); - } - - int j = 0; - for( ; j < blockSize; j += FASCONV_BASE_VECSZ ) - { - bool tail = false; - if (j + FASCONV_BASE_VECSZ > blockSize) - { - if (j == 0) - break; - j = blockSize - FASCONV_BASE_VECSZ; - tail = true; - } - int k = 0; - const float* rptr = rowbuf + j*vecsize_aligned; - - __m256i tmp; - __m256 vs00 = (__m256)__lasx_xvxor_v(tmp, tmp), vs01 = (__m256)__lasx_xvxor_v(tmp, tmp), - vs02 = (__m256)__lasx_xvxor_v(tmp, tmp), vs03 = (__m256)__lasx_xvxor_v(tmp, tmp), - vs10 = (__m256)__lasx_xvxor_v(tmp, tmp), vs11 = (__m256)__lasx_xvxor_v(tmp, tmp), - vs12 = (__m256)__lasx_xvxor_v(tmp, tmp), vs13 = (__m256)__lasx_xvxor_v(tmp, tmp), - vs20 = (__m256)__lasx_xvxor_v(tmp, tmp), vs21 = (__m256)__lasx_xvxor_v(tmp, tmp), - vs22 = (__m256)__lasx_xvxor_v(tmp, tmp), vs23 = (__m256)__lasx_xvxor_v(tmp, tmp); - - for (; k < vecsize; k += 8, rptr += 8 ) - { - __m256 w0 = (__m256)__lasx_xvld(wptr0 + k, 0); - __m256 w1 = (__m256)__lasx_xvld(wptr1 + k, 0); - __m256 w2 = (__m256)__lasx_xvld(wptr2 + k, 0); - __m256 r0 = (__m256)__lasx_xvld(rptr, 0); - - vs00 = __lasx_xvfmadd_s(w0, r0, vs00); - vs10 = __lasx_xvfmadd_s(w1, r0, vs10); - vs20 = __lasx_xvfmadd_s(w2, r0, vs20); - - r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned, 0); - vs01 = __lasx_xvfmadd_s(w0, r0, vs01); - vs11 = __lasx_xvfmadd_s(w1, r0, vs11); - vs21 = __lasx_xvfmadd_s(w2, r0, vs21); - - r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned*2, 0); - vs02 = __lasx_xvfmadd_s(w0, r0, vs02); - vs12 = __lasx_xvfmadd_s(w1, r0, vs12); - vs22 = __lasx_xvfmadd_s(w2, r0, vs22); - - r0 = (__m256)__lasx_xvld(rptr + vecsize_aligned*3, 0); - vs03 = __lasx_xvfmadd_s(w0, r0, vs03); - vs13 = __lasx_xvfmadd_s(w1, r0, vs13); - vs23 = __lasx_xvfmadd_s(w2, r0, vs23); - } - - /*t0*/ - __m256 vs00_perm = (__m256)__lasx_xvpermi_d(vs00, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs00_add_2w = __lasx_xvfadd_s(vs00, vs00_perm); - __m256 tmp00_srl = (__m256)__lasx_xvsrli_d(vs00_add_2w, 32); - __m256 vs00_add_4w = __lasx_xvfadd_s(vs00_add_2w, tmp00_srl); - - __m256 vs01_perm = (__m256)__lasx_xvpermi_d(vs01, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs01_add_2w = __lasx_xvfadd_s(vs01, vs01_perm); - __m256 tmp01_srl = (__m256)__lasx_xvsrli_d(vs01_add_2w, 32); - __m256 vs01_add_4w = __lasx_xvfadd_s(vs01_add_2w, tmp01_srl); - - __m256 vs02_perm = (__m256)__lasx_xvpermi_d(vs02, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs02_add_2w = __lasx_xvfadd_s(vs02, vs02_perm); - __m256 tmp02_srl = (__m256)__lasx_xvsrli_d(vs02_add_2w, 32); - __m256 vs02_add_4w = __lasx_xvfadd_s(vs02_add_2w, tmp02_srl); - - __m256 vs03_perm = (__m256)__lasx_xvpermi_d(vs03, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs03_add_2w = __lasx_xvfadd_s(vs03, vs03_perm); - __m256 tmp03_srl = (__m256)__lasx_xvsrli_d(vs03_add_2w, 32); - __m256 vs03_add_4w = __lasx_xvfadd_s(vs03_add_2w, tmp03_srl); - - __m256i vs01_vs00 = __lasx_xvpackev_w((__m256i)vs01_add_4w, (__m256i)vs00_add_4w); - __m256i vs03_vs02 = __lasx_xvpackev_w((__m256i)vs03_add_4w, (__m256i)vs02_add_4w); - __m256 t0 = (__m256)__lasx_xvpackev_d(vs03_vs02, vs01_vs00); - - /*t1*/ - __m256 vs10_perm = (__m256)__lasx_xvpermi_d(vs10, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs10_add_2w = __lasx_xvfadd_s(vs10, vs10_perm); - __m256 tmp10_srl = (__m256)__lasx_xvsrli_d(vs10_add_2w, 32); - __m256 vs10_add_4w = __lasx_xvfadd_s(vs10_add_2w, tmp10_srl); - - __m256 vs11_perm = (__m256)__lasx_xvpermi_d(vs11, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs11_add_2w = __lasx_xvfadd_s(vs11, vs11_perm); - __m256 tmp11_srl = (__m256)__lasx_xvsrli_d(vs11_add_2w, 32); - __m256 vs11_add_4w = __lasx_xvfadd_s(vs11_add_2w, tmp11_srl); - - __m256 vs12_perm = (__m256)__lasx_xvpermi_d(vs12, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs12_add_2w = __lasx_xvfadd_s(vs12, vs12_perm); - __m256 tmp12_srl = (__m256)__lasx_xvsrli_d(vs12_add_2w, 32); - __m256 vs12_add_4w = __lasx_xvfadd_s(vs12_add_2w, tmp12_srl); - - __m256 vs13_perm = (__m256)__lasx_xvpermi_d(vs13, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs13_add_2w = __lasx_xvfadd_s(vs13, vs13_perm); - __m256 tmp13_srl = (__m256)__lasx_xvsrli_d(vs13_add_2w, 32); - __m256 vs13_add_4w = __lasx_xvfadd_s(vs13_add_2w, tmp13_srl); - - __m256i vs11_vs10 = __lasx_xvpackev_w((__m256i)vs11_add_4w, (__m256i)vs10_add_4w); - __m256i vs13_vs12 = __lasx_xvpackev_w((__m256i)vs13_add_4w, (__m256i)vs12_add_4w); - __m256 t1 = (__m256)__lasx_xvpackev_d(vs13_vs12, vs11_vs10); - - /*t2*/ - __m256 vs20_perm = (__m256)__lasx_xvpermi_d(vs20, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs20_add_2w = __lasx_xvfadd_s(vs20, vs20_perm); - __m256 tmp20_srl = (__m256)__lasx_xvsrli_d(vs20_add_2w, 32); - __m256 vs20_add_4w = __lasx_xvfadd_s(vs20_add_2w, tmp20_srl); - - __m256 vs21_perm = (__m256)__lasx_xvpermi_d(vs21, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs21_add_2w = __lasx_xvfadd_s(vs21, vs21_perm); - __m256 tmp21_srl = (__m256)__lasx_xvsrli_d(vs21_add_2w, 32); - __m256 vs21_add_4w = __lasx_xvfadd_s(vs21_add_2w, tmp21_srl); - - __m256 vs22_perm = (__m256)__lasx_xvpermi_d(vs22, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs22_add_2w = __lasx_xvfadd_s(vs22, vs22_perm); - __m256 tmp22_srl = (__m256)__lasx_xvsrli_d(vs22_add_2w, 32); - __m256 vs22_add_4w = __lasx_xvfadd_s(vs22_add_2w, tmp22_srl); - - __m256 vs23_perm = (__m256)__lasx_xvpermi_d(vs23, (2<<6) + (3<<4) + (0<<2) + 1); - __m256 vs23_add_2w = __lasx_xvfadd_s(vs23, vs23_perm); - __m256 tmp23_srl = (__m256)__lasx_xvsrli_d(vs23_add_2w, 32); - __m256 vs23_add_4w = __lasx_xvfadd_s(vs23_add_2w, tmp23_srl); - - __m256i vs21_vs20 = __lasx_xvpackev_w((__m256i)vs21_add_4w, (__m256i)vs20_add_4w); - __m256i vs23_vs22 = __lasx_xvpackev_w((__m256i)vs23_add_4w, (__m256i)vs22_add_4w); - __m256 t2 = (__m256)__lasx_xvpackev_d(vs23_vs22, vs21_vs20); - - t0 = __lasx_xvfadd_s(t0, (__m256)__lasx_xvpermi_q(t0, t0, 1)); - t1 = __lasx_xvfadd_s(t1, (__m256)__lasx_xvpermi_q(t1, t1, 1)); - t2 = __lasx_xvfadd_s(t2, (__m256)__lasx_xvpermi_q(t2, t2, 1)); - - __m128 s0, s1, s2; - - if( initOutput ) - { - s0 = _v256_extract_low(_v256_setall_ps(bias0)); - s1 = _v256_extract_low(_v256_setall_ps(bias1)); - s2 = _v256_extract_low(_v256_setall_ps(bias2)); - } - else - { - s0 = (__m128)__lsx_vld(outptr0 + j, 0); - s1 = (__m128)__lsx_vld(outptr1 + j, 0); - s2 = (__m128)__lsx_vld(outptr2 + j, 0); - } - - s0 = __lsx_vfadd_s(s0, *(__m128*)&t0); - s1 = __lsx_vfadd_s(s1, *(__m128*)&t1); - s2 = __lsx_vfadd_s(s2, *(__m128*)&t2); - - if( relu ) - { - __m128i m0 = __lsx_vfcmp_clt_s(z, s0); - __m128i m1 = __lsx_vfcmp_clt_s(z, s1); - __m128i m2 = __lsx_vfcmp_clt_s(z, s2); - s0 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s0, vr0), (__m128i)s0, m0); - s1 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s1, vr1), (__m128i)s1, m1); - s2 = (__m128)__lsx_vbitsel_v((__m128i)__lsx_vfmul_s(s2, vr2), (__m128i)s2, m2); - } - - if( tail ) - { - s0 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr0 + j, 0), (__m128i)s0, mask); - s1 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr1 + j, 0), (__m128i)s1, mask); - s2 = (__m128)__lsx_vbitsel_v(__lsx_vld(outptr2 + j, 0), (__m128i)s2, mask); - } - - __lsx_vst(s0, outptr0 + j, 0); - __lsx_vst(s1, outptr1 + j, 0); - __lsx_vst(s2, outptr2 + j, 0); - } - - for( ; j <= blockSize - 2; j += 2 ) - { - const float* rptr0 = rowbuf + j*vecsize_aligned; - const float* rptr1 = rowbuf + (j+1)*vecsize_aligned; - float s00, s01, s10, s11, s20, s21; - - if( initOutput ) - { - s00 = s01 = bias0; - s10 = s11 = bias1; - s20 = s21 = bias2; - } - else - { - s00 = outptr0[j]; s01 = outptr0[j+1]; - s10 = outptr1[j]; s11 = outptr1[j+1]; - s20 = outptr2[j]; s21 = outptr2[j+1]; - } - - for( int k = 0; k < vecsize; k++ ) - { - float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; - float r = rptr0[k]; - s00 += w0*r; s10 += w1*r; s20 += w2*r; - r = rptr1[k]; - s01 += w0*r; s11 += w1*r; s21 += w2*r; - } - - if( relu ) - { - s00 = s00 > 0.f ? s00 : s00*r0; - s01 = s01 > 0.f ? s01 : s01*r0; - s10 = s10 > 0.f ? s10 : s10*r1; - s11 = s11 > 0.f ? s11 : s11*r1; - s20 = s20 > 0.f ? s20 : s20*r2; - s21 = s21 > 0.f ? s21 : s21*r2; - } - - outptr0[j] = s00; - outptr0[j+1] = s01; - outptr1[j] = s10; - outptr1[j+1] = s11; - outptr2[j] = s20; - outptr2[j+1] = s21; - } - - for( ; j < blockSize; j++ ) - { - const float* rptr0 = rowbuf + j*vecsize_aligned; - float s00, s10, s20; - - if( initOutput ) - { - s00 = bias0; - s10 = bias1; - s20 = bias2; - } - else - { - s00 = outptr0[j]; - s10 = outptr1[j]; - s20 = outptr2[j]; - } - - for( int k = 0; k < vecsize; k++ ) - { - float w0 = wptr0[k], w1 = wptr1[k], w2 = wptr2[k]; - float r = rptr0[k]; - s00 += w0*r; s10 += w1*r; s20 += w2*r; - } - - if( relu ) - { - s00 = s00 > 0.f ? s00 : s00*r0; - s10 = s10 > 0.f ? s10 : s10*r1; - s20 = s20 > 0.f ? s20 : s20*r2; - } - - outptr0[j] = s00; - outptr1[j] = s10; - outptr2[j] = s20; - } - } -} - static inline void _v256_load_deinterleave(const float* ptr, __m256& a, __m256& b) { __m256 t0 = (__m256)__lasx_xvld(ptr, 0); From 1339c7f30c40f2f480da78d82d5e7c58fddec797 Mon Sep 17 00:00:00 2001 From: Sergei Shutov Date: Wed, 14 Dec 2022 23:28:43 +0200 Subject: [PATCH 292/313] Define the number of dstChannels for Lab, Luv, YCrCb and XYZ conversions --- modules/imgproc/src/color.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/imgproc/src/color.hpp b/modules/imgproc/src/color.hpp index e3c25f08d9..abbd65ec06 100644 --- a/modules/imgproc/src/color.hpp +++ b/modules/imgproc/src/color.hpp @@ -115,6 +115,12 @@ inline int dstChannels(int code) case COLOR_YUV2BGR_YV12: case COLOR_YUV2RGB_YV12: case COLOR_YUV2BGR_IYUV: case COLOR_YUV2RGB_IYUV: case COLOR_YUV2RGB_UYVY: case COLOR_YUV2BGR_UYVY: case COLOR_YUV2RGB_YVYU: case COLOR_YUV2BGR_YVYU: case COLOR_YUV2RGB_YUY2: case COLOR_YUV2BGR_YUY2: + case COLOR_Lab2BGR: case COLOR_Lab2RGB: case COLOR_BGR2Lab: case COLOR_RGB2Lab: + case COLOR_Lab2LBGR: case COLOR_Lab2LRGB: + case COLOR_Luv2BGR: case COLOR_Luv2RGB: case COLOR_BGR2Luv: case COLOR_RGB2Luv: + case COLOR_Luv2LBGR: case COLOR_Luv2LRGB: + case COLOR_YCrCb2BGR: case COLOR_YCrCb2RGB: case COLOR_BGR2YCrCb: case COLOR_RGB2YCrCb: + case COLOR_XYZ2BGR: case COLOR_XYZ2RGB: case COLOR_BGR2XYZ: case COLOR_RGB2XYZ: return 3; From 62b3a20da5e1c9d00ac7ab288738e56315b1b230 Mon Sep 17 00:00:00 2001 From: Maxim Milashchenko <67949029+MaximMilashchenko@users.noreply.github.com> Date: Fri, 23 Dec 2022 13:15:22 +0300 Subject: [PATCH 293/313] Merge pull request #22930 from MaximMilashchenko:gstreamer_support Support one-time audio video reading * stream switching functionality * audio+video pipeline with switch stream functionality * audio video sync * fixed sync * removed switch swtream functionality * changed test for gstreamer audio * fixed error * fixed error * fixed issue * fixed issue * fixed error * fixed error * fixed error --- modules/videoio/src/cap_gstreamer.cpp | 620 +++++++++++++++++++------- modules/videoio/test/test_audio.cpp | 32 +- 2 files changed, 491 insertions(+), 161 deletions(-) diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index a8d381a50f..fc031d2b5f 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -55,6 +55,9 @@ #include #include #include +#include +#include +#include #include #include @@ -130,6 +133,7 @@ protected: T* ptr; public: inline GSafePtr() CV_NOEXCEPT : ptr(NULL) { } + inline GSafePtr(T* p) : ptr(p) { } inline ~GSafePtr() CV_NOEXCEPT { release(); } inline void release() CV_NOEXCEPT { @@ -318,12 +322,28 @@ static void find_hw_element(const GValue *item, gpointer va_type) class GStreamerCapture CV_FINAL : public IVideoCapture { private: + GSafePtr audiopipeline; GSafePtr pipeline; GSafePtr v4l2src; GSafePtr sink; - GSafePtr sample; - GSafePtr caps; + GSafePtr audiosink; + GSafePtr impendingVideoSample; + GSafePtr usedVideoSample; + GSafePtr audioSample; + GSafePtr caps; + GSafePtr audiocaps; + gint64 bufferedAudioDurationNS; + gint64 requiredAudioTimeNS; + gint64 impendingVideoSampleTimeNS; + gint64 usedVideoSampleTimeNS; + gint64 videoSampleDurationNS; + gint64 audioSampleDurationNS; + gint64 audioSampleTimeNS; + gint64 chunkLengthOfBytes; + gint64 givenAudioTimeNS; + gint64 numberOfAdditionalAudioBytes; + gint64 audioSamplePosInSamples; gint videoStream; gint audioStream; gint64 duration; @@ -334,13 +354,22 @@ private: GstClockTime readTimeout; // measured in nanoseconds bool isPosFramesSupported; bool isPosFramesEmulated; + bool vEOS; + bool aEOS; + bool syncLastFrame; + bool lastFrame; gint64 emulatedFrameNumber; gint outputAudioFormat; + gint audioBitPerSample; gint audioBaseIndex; gint nAudioChannels; gint audioSamplesPerSecond; + gint audioBitPerFrame; + gint audioSampleSize; + std::string audioFormat; Mat audioFrame; + std::deque bufferAudioData; VideoAccelerationType va_type; int hw_device; @@ -349,6 +378,8 @@ public: virtual ~GStreamerCapture() CV_OVERRIDE; virtual bool grabFrame() CV_OVERRIDE; virtual bool retrieveFrame(int /*unused*/, OutputArray dst) CV_OVERRIDE; + bool configureAudioFrame(); + bool grabVideoFrame(); bool grabAudioFrame(); bool retrieveVideoFrame(int /*unused*/, OutputArray dst); bool retrieveAudioFrame(int /*unused*/, OutputArray dst); @@ -360,7 +391,7 @@ public: bool open(const String &filename_, const cv::VideoCaptureParameters& params); static void newPad(GstElement * /*elem*/, GstPad *pad, gpointer data); bool configureHW(const cv::VideoCaptureParameters&); - bool configureStreams(const cv::VideoCaptureParameters&); + bool configureStreamsProperty(const cv::VideoCaptureParameters&); bool setAudioProperties(const cv::VideoCaptureParameters&); protected: @@ -373,6 +404,16 @@ protected: }; GStreamerCapture::GStreamerCapture() : + bufferedAudioDurationNS(0), + requiredAudioTimeNS(0), + impendingVideoSampleTimeNS(0), + usedVideoSampleTimeNS(0), + videoSampleDurationNS(0), audioSampleDurationNS(0), + audioSampleTimeNS(0), + chunkLengthOfBytes(0), + givenAudioTimeNS(0), + numberOfAdditionalAudioBytes(0), + audioSamplePosInSamples(0), videoStream(0), audioStream(-1), duration(-1), width(-1), height(-1), fps(-1), @@ -380,15 +421,22 @@ GStreamerCapture::GStreamerCapture() : readTimeout(GSTREAMER_INTERRUPT_READ_DEFAULT_TIMEOUT_NS), isPosFramesSupported(false), isPosFramesEmulated(false), + vEOS(false), + aEOS(false), + syncLastFrame(true), + lastFrame(false), emulatedFrameNumber(-1), outputAudioFormat(CV_16S), + audioBitPerSample(16), audioBaseIndex(1), nAudioChannels(0), - audioSamplesPerSecond(44100) + audioSamplesPerSecond(44100), + audioBitPerFrame(0), + audioSampleSize(0), + audioFormat("S16LE") , va_type(VIDEO_ACCELERATION_NONE) , hw_device(-1) -{ -} +{} /*! * \brief CvCapture_GStreamer::close @@ -433,7 +481,7 @@ bool GStreamerCapture::configureHW(const cv::VideoCaptureParameters& params) return true; } -bool GStreamerCapture::configureStreams(const cv::VideoCaptureParameters& params) +bool GStreamerCapture::configureStreamsProperty(const cv::VideoCaptureParameters& params) { if (params.has(CAP_PROP_VIDEO_STREAM)) { @@ -442,7 +490,7 @@ bool GStreamerCapture::configureStreams(const cv::VideoCaptureParameters& params videoStream = static_cast(value); else { - CV_LOG_ERROR(NULL, "VIDEOIO/MSMF: CAP_PROP_VIDEO_STREAM parameter value is invalid/unsupported: " << value); + CV_LOG_ERROR(NULL, "VIDEOIO/Gstreamer: CAP_PROP_VIDEO_STREAM parameter value is invalid/unsupported: " << value); return false; } } @@ -453,7 +501,7 @@ bool GStreamerCapture::configureStreams(const cv::VideoCaptureParameters& params audioStream = static_cast(value); else { - CV_LOG_ERROR(NULL, "VIDEOIO/MSMF: CAP_PROP_AUDIO_STREAM parameter value is invalid/unsupported: " << value); + CV_LOG_ERROR(NULL, "VIDEOIO/Gstreamer: CAP_PROP_AUDIO_STREAM parameter value is invalid/unsupported: " << value); return false; } } @@ -467,7 +515,7 @@ bool GStreamerCapture::setAudioProperties(const cv::VideoCaptureParameters& para gint value = static_cast(params.get(CAP_PROP_AUDIO_DATA_DEPTH)); if (value != CV_8S && value != CV_16S && value != CV_32S && value != CV_32F) { - CV_LOG_ERROR(NULL, "VIDEOIO/MSMF: CAP_PROP_AUDIO_DATA_DEPTH parameter value is invalid/unsupported: " << value); + CV_LOG_ERROR(NULL, "VIDEOIO/Gstreamer: CAP_PROP_AUDIO_DATA_DEPTH parameter value is invalid/unsupported: " << value); return false; } else @@ -480,7 +528,7 @@ bool GStreamerCapture::setAudioProperties(const cv::VideoCaptureParameters& para int value = static_cast(params.get(CAP_PROP_AUDIO_SAMPLES_PER_SECOND)); if (value < 0) { - CV_LOG_ERROR(NULL, "VIDEOIO/MSMF: CAP_PROP_AUDIO_SAMPLES_PER_SECOND parameter can't be negative: " << value); + CV_LOG_ERROR(NULL, "VIDEOIO/Gstreamer: CAP_PROP_AUDIO_SAMPLES_PER_SECOND parameter can't be negative: " << value); return false; } else @@ -488,6 +536,11 @@ bool GStreamerCapture::setAudioProperties(const cv::VideoCaptureParameters& para audioSamplesPerSecond = value; } } + if (params.has(CAP_PROP_AUDIO_SYNCHRONIZE)) + { + int value = static_cast(params.get(CAP_PROP_AUDIO_SYNCHRONIZE)); + syncLastFrame = (value != 0) ? true : false; + } return true; } @@ -505,116 +558,329 @@ bool GStreamerCapture::grabFrame() // start the pipeline if it was not in playing state yet if (!this->isPipelinePlaying()) this->startPipeline(); - // bail out if EOS - if (gst_app_sink_is_eos(GST_APP_SINK(sink.get()))) - return false; -#if FULL_GST_VERSION >= VERSION_NUM(1,10,0) - sample.attach(gst_app_sink_try_pull_sample(GST_APP_SINK(sink.get()), readTimeout)); -#else - sample.attach(gst_app_sink_pull_sample(GST_APP_SINK(sink.get()))); -#endif - if (!sample) + if (vEOS) + { return false; + } - if (isPosFramesEmulated) - emulatedFrameNumber++; + bool returnFlag = true; + if (videoStream >= 0) + { + if (!vEOS) + returnFlag &= grabVideoFrame(); + if (!returnFlag) + { + return false; + } + } if (audioStream >= 0) - return grabAudioFrame(); + { + bufferedAudioDurationNS = (gint64)(1e9*(((double)bufferAudioData.size()/((audioBitPerSample/8)*nAudioChannels))/audioSamplesPerSecond)); + audioFrame.release(); + if (!aEOS) + returnFlag &= grabAudioFrame(); + } - return true; + return returnFlag; +} + +bool GStreamerCapture::grabVideoFrame() +{ + usedVideoSample.release(); + + bool returnFlag = false; + bool stopFlag = false; + + if (audioStream != -1) + { + usedVideoSample.swap(impendingVideoSample); + std::swap(usedVideoSampleTimeNS, impendingVideoSampleTimeNS); + } + + while (!stopFlag) + { + if (gst_app_sink_is_eos(GST_APP_SINK(sink.get()))) + { + vEOS = true; + lastFrame = true; + stopFlag = true; + if (audioStream == -1) + { + returnFlag = false; + } + else if (usedVideoSample) + { + gst_element_query_position(sink.get(), CV_GST_FORMAT(GST_FORMAT_TIME), &impendingVideoSampleTimeNS); + videoSampleDurationNS = impendingVideoSampleTimeNS - usedVideoSampleTimeNS; + requiredAudioTimeNS = impendingVideoSampleTimeNS - givenAudioTimeNS; + givenAudioTimeNS += requiredAudioTimeNS; + returnFlag = true; + } + } + else + { + #if FULL_GST_VERSION >= VERSION_NUM(1,10,0) + impendingVideoSample.attach(gst_app_sink_try_pull_sample(GST_APP_SINK(sink.get()), readTimeout)); + #else + impendingVideoSample.attach(gst_app_sink_pull_sample(GST_APP_SINK(sink.get()))); + #endif + if (!impendingVideoSample) + { + if (!gst_app_sink_is_eos(GST_APP_SINK(sink.get()))) + { + CV_LOG_DEBUG(NULL, "videoio(Gstreamer): gst_app_sink_pull_sample() method is not succeeded"); + } + else + { + vEOS = true; + lastFrame = true; + stopFlag = true; + if (audioStream == -1) + { + return false; + } + else if (usedVideoSample) + { + gst_element_query_position(sink.get(), CV_GST_FORMAT(GST_FORMAT_TIME), &impendingVideoSampleTimeNS); + videoSampleDurationNS = impendingVideoSampleTimeNS - usedVideoSampleTimeNS; + requiredAudioTimeNS = impendingVideoSampleTimeNS - givenAudioTimeNS; + givenAudioTimeNS += requiredAudioTimeNS; + returnFlag = true; + } + } + } + gst_element_query_position(sink.get(), CV_GST_FORMAT(GST_FORMAT_TIME), &impendingVideoSampleTimeNS); + + if (audioStream != -1) + { + if (!usedVideoSample) + { + usedVideoSample.swap(impendingVideoSample); + std::swap(usedVideoSampleTimeNS, impendingVideoSampleTimeNS); + } + else + { + stopFlag = true; + } + if (impendingVideoSample) + { + emulatedFrameNumber++; + } + videoSampleDurationNS = impendingVideoSampleTimeNS - usedVideoSampleTimeNS; + requiredAudioTimeNS = impendingVideoSampleTimeNS - givenAudioTimeNS; + givenAudioTimeNS += requiredAudioTimeNS; + } + else + { + usedVideoSample.swap(impendingVideoSample); + std::swap(usedVideoSampleTimeNS, impendingVideoSampleTimeNS); + stopFlag = true; + emulatedFrameNumber++; + } + returnFlag = true; + } + } + return returnFlag; } bool GStreamerCapture::grabAudioFrame() { - GstCaps* frame_caps = gst_sample_get_caps(sample); // no lifetime transfer - if (!frame_caps) + audioSample.reset(NULL); + + bool returnFlag = false; + gint64 audioTimeNS = bufferedAudioDurationNS; + + if (bufferedAudioDurationNS > requiredAudioTimeNS) { - CV_LOG_ERROR(NULL, "GStreamer: gst_sample_get_caps() returns NULL"); - return false; + return true; } - if (!GST_CAPS_IS_SIMPLE(frame_caps)) + while ((!vEOS) ? audioTimeNS <= requiredAudioTimeNS : !aEOS) { - // bail out in no caps - CV_LOG_ERROR(NULL, "GStreamer: GST_CAPS_IS_SIMPLE(frame_caps) check is failed"); - return false; - } - - GstAudioInfo info = {}; - gboolean audio_info_res = gst_audio_info_from_caps(&info, frame_caps); - if (!audio_info_res) - { - CV_Error(Error::StsError, "GStreamer: gst_audio_info_from_caps() is failed. Can't handle unknown layout"); - } - int bpf = GST_AUDIO_INFO_BPF(&info); - CV_CheckGT(bpf, 0, ""); - - GstStructure* structure = gst_caps_get_structure(frame_caps, 0); // no lifetime transfer - if (!structure) - { - CV_LOG_ERROR(NULL, "GStreamer: Can't query 'structure'-0 from GStreamer sample"); - return false; - } - - const gchar* name_ = gst_structure_get_name(structure); - if (!name_) - { - CV_LOG_ERROR(NULL, "GStreamer: Can't query 'name' from GStreamer sample"); - return false; - } - std::string name = toLowerCase(std::string(name_)); - - GstBuffer* buf = gst_sample_get_buffer(sample); - if (!buf) - return false; - GstMapInfo map_info = {}; - if (!gst_buffer_map(buf, &map_info, GST_MAP_READ)) - { - CV_LOG_ERROR(NULL, "GStreamer: Failed to map GStreamer buffer to system memory"); - return false; - } - ScopeGuardGstMapInfo map_guard(buf, &map_info); - if (name == "audio/x-raw") - { - const gchar* format_ = gst_structure_get_string(structure, "format"); - if (!format_) + if (gst_app_sink_is_eos(GST_APP_SINK(audiosink.get()))) { - CV_LOG_ERROR(NULL, "GStreamer: Can't query 'format' of 'video/x-raw'"); - return false; + aEOS = true; + if (videoStream != -1 && !vEOS) + returnFlag = true; + if (videoStream == -1) + audioSamplePosInSamples += chunkLengthOfBytes/((audioBitPerSample/8)*nAudioChannels); + break; } - std::string format = toUpperCase(std::string(format_)); - cv::Mat data; - if (format == "S8") + else { - Mat(map_info.size/bpf, nAudioChannels, CV_8S, map_info.data).copyTo(audioFrame); - return true; + audioSample.attach(gst_app_sink_pull_sample(GST_APP_SINK(audiosink.get()))); + if (!audioSample) + { + if (!gst_app_sink_is_eos(GST_APP_SINK(audiosink.get()))) + { + CV_LOG_ERROR(NULL, "videoio(Gstreamer): gst_app_sink_pull_sample() method is not succeeded"); + return false; + } + else + { + aEOS = true; + break; + } + } + gst_element_query_position(audiosink.get(), CV_GST_FORMAT(GST_FORMAT_TIME), &audioSampleTimeNS); + + GstMapInfo map_info = {}; + GstCaps* frame_caps = gst_sample_get_caps(audioSample.get()); // no lifetime transfer + if (!frame_caps) + { + CV_LOG_ERROR(NULL, "GStreamer: gst_sample_get_caps() returns NULL"); + return false; + } + if (!GST_CAPS_IS_SIMPLE(frame_caps)) + { + // bail out in no caps + CV_LOG_ERROR(NULL, "GStreamer: GST_CAPS_IS_SIMPLE(frame_caps) check is failed"); + return false; + } + GstAudioInfo info = {}; + gboolean audio_info_res = gst_audio_info_from_caps(&info, frame_caps); + if (!audio_info_res) + { + CV_Error(Error::StsError, "GStreamer: gst_audio_info_from_caps() is failed. Can't handle unknown layout"); + } + audioBitPerFrame = GST_AUDIO_INFO_BPF(&info); + CV_CheckGT(audioBitPerFrame, 0, ""); + + GstStructure* structure = gst_caps_get_structure(frame_caps, 0); // no lifetime transfer + if (!structure) + { + CV_LOG_ERROR(NULL, "GStreamer: Can't query 'structure'-0 from GStreamer sample"); + return false; + } + + const gchar* name_ = gst_structure_get_name(structure); + if (!name_) + { + CV_LOG_ERROR(NULL, "GStreamer: Can't query 'name' from GStreamer sample"); + return false; + } + std::string name = toLowerCase(std::string(name_)); + if (name != "audio/x-raw") + { + CV_Error_(Error::StsNotImplemented, ("Unsupported GStreamer layer type: %s", name.c_str())); + return false; + } + + const gchar* format_ = gst_structure_get_string(structure, "format"); + if (!format_) + { + CV_LOG_ERROR(NULL, "GStreamer: Can't query 'format' of 'audio/x-raw'"); + return false; + } + std::string format = toUpperCase(std::string(format_)); + if (format != "S8" && format != "S16LE" && format != "S32LE" && format != "F32LE") + { + CV_Error_(Error::StsNotImplemented, ("Unsupported GStreamer audio format: %s", format.c_str())); + return false; + } + if (format != audioFormat) + { + return false; + } + + GstBuffer* buf = gst_sample_get_buffer(audioSample); + if (!buf) + { + CV_LOG_ERROR(NULL, "GStreamer: Failed. Buffer is empty"); + return false; + } + if (!gst_buffer_map(buf, &map_info, GST_MAP_READ)) + { + CV_LOG_ERROR(NULL, "GStreamer: Failed to map GStreamer buffer to system memory"); + return false; + } + ScopeGuardGstMapInfo map_guard(buf, &map_info); + gsize lastSize = bufferAudioData.size(); + audioSampleSize = map_info.size; + bufferAudioData.resize(lastSize+audioSampleSize); + for (gint j = 0; j < audioSampleSize; j++) + { + bufferAudioData[lastSize+j]=*(map_info.data + j); + } + audioSampleDurationNS = 1e9*(((double)audioSampleSize/((audioBitPerSample/8)*nAudioChannels))/audioSamplesPerSecond); + + CV_LOG_DEBUG(NULL, "videoio(Gstreamer): got audio frame with timestamp=" << audioSampleTimeNS << " duration=" << audioSampleDurationNS); + audioTimeNS += (int64_t)(audioSampleDurationNS); + + returnFlag = true; } - if (format == "S16LE") - { - Mat(map_info.size/bpf, nAudioChannels, CV_16S, map_info.data).copyTo(audioFrame); - return true; - } - if (format == "S32LE") - { - Mat(map_info.size/bpf, nAudioChannels, CV_32S, map_info.data).copyTo(audioFrame); - return true; - } - if (format == "F32LE") - { - Mat(map_info.size/bpf, nAudioChannels, CV_32F, map_info.data).copyTo(audioFrame); - return true; - } - CV_Error_(Error::StsNotImplemented, ("Unsupported GStreamer audio format: %s", format.c_str())); } - CV_Error_(Error::StsNotImplemented, ("Unsupported GStreamer layer type: %s", name.c_str())); + returnFlag &= configureAudioFrame(); + return returnFlag; +} + +bool GStreamerCapture::configureAudioFrame() +{ + if (bufferAudioData.empty() && aEOS) + { + return false; + } + std::vector audioDataInUse; + + audioSamplePosInSamples += chunkLengthOfBytes/((audioBitPerSample/8)*nAudioChannels); + chunkLengthOfBytes = (videoStream != -1) ? (int64_t)((requiredAudioTimeNS * 1e-9 * audioSamplesPerSecond*nAudioChannels*(audioBitPerSample)/8)) : audioSampleSize; + if ((videoStream != -1) && (chunkLengthOfBytes % ((int)(audioBitPerSample)/8* (int)nAudioChannels) != 0)) + { + if ( (double)audioSamplePosInSamples/audioSamplesPerSecond - usedVideoSampleTimeNS * 1e-9 >= 0 ) + chunkLengthOfBytes -= numberOfAdditionalAudioBytes; + numberOfAdditionalAudioBytes = ((int)(audioBitPerSample)/8* (int)nAudioChannels) + - chunkLengthOfBytes % ((int)(audioBitPerSample)/8* (int)nAudioChannels); + chunkLengthOfBytes += numberOfAdditionalAudioBytes; + } + if ((lastFrame && !syncLastFrame) || (aEOS && !vEOS)) + { + chunkLengthOfBytes = bufferAudioData.size(); + audioSamplePosInSamples += chunkLengthOfBytes/((audioBitPerSample/8)*nAudioChannels); + } + CV_Check((double)chunkLengthOfBytes, chunkLengthOfBytes >= INT_MIN || chunkLengthOfBytes <= INT_MAX, "GSTREAMER: The chunkLengthOfBytes is out of the allowed range"); + copy(bufferAudioData.begin(), bufferAudioData.begin() + (int)chunkLengthOfBytes, std::back_inserter(audioDataInUse)); + bufferAudioData.erase(bufferAudioData.begin(), bufferAudioData.begin() + (int)chunkLengthOfBytes); + + cv::Mat data; + + if (audioFormat == "S8") + { + Mat((int)chunkLengthOfBytes/audioBitPerFrame, nAudioChannels, CV_8S, audioDataInUse.data()).copyTo(audioFrame); + return true; + } + if (audioFormat == "S16LE") + { + Mat((int)chunkLengthOfBytes/audioBitPerFrame, nAudioChannels, CV_16S, audioDataInUse.data()).copyTo(audioFrame); + return true; + } + if (audioFormat == "S32LE") + { + Mat((int)chunkLengthOfBytes/audioBitPerFrame, nAudioChannels, CV_32S, audioDataInUse.data()).copyTo(audioFrame); + return true; + } + if (audioFormat == "F32LE") + { + Mat((int)chunkLengthOfBytes/audioBitPerFrame, nAudioChannels, CV_32F, audioDataInUse.data()).copyTo(audioFrame); + return true; + } + + audioDataInUse.clear(); + audioDataInUse.shrink_to_fit(); + return true; } bool GStreamerCapture::retrieveAudioFrame(int index, OutputArray dst) { + if (audioFrame.empty()) + { + dst.release(); + if (aEOS) + return true; + } CV_Check(index, index >= audioBaseIndex && index < audioBaseIndex + nAudioChannels, ""); index -= audioBaseIndex; @@ -653,7 +919,7 @@ bool GStreamerCapture::retrieveAudioFrame(int index, OutputArray dst) bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst) { - GstCaps* frame_caps = gst_sample_get_caps(sample); // no lifetime transfer + GstCaps* frame_caps = gst_sample_get_caps(usedVideoSample); // no lifetime transfer if (!frame_caps) { CV_LOG_ERROR(NULL, "GStreamer: gst_sample_get_caps() returns NULL"); @@ -676,7 +942,7 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst) // gstreamer expects us to handle the memory at this point // so we can just wrap the raw buffer and be done with it - GstBuffer* buf = gst_sample_get_buffer(sample); // no lifetime transfer + GstBuffer* buf = gst_sample_get_buffer(usedVideoSample); // no lifetime transfer if (!buf) return false; @@ -859,18 +1125,32 @@ bool GStreamerCapture::retrieveFrame(int index, OutputArray dst) { if (index < 0) return false; - if (!sample) - return false; - if (index == 0) + if ((gint)index < audioBaseIndex) { - CV_CheckGE(videoStream, 0, "No video stream configured"); - return retrieveVideoFrame(index, dst); + if (videoStream == -1) + { + dst.release(); + return false; + } + else + { + CV_CheckGE(videoStream, 0, "No video stream configured"); + return retrieveVideoFrame(index, dst); + } } - else if (index >= audioBaseIndex) + else { - CV_CheckGE(audioStream, 0, "No audio stream configured"); - return retrieveAudioFrame(index, dst); + if (audioStream == -1) + { + dst.release(); + return false; + } + else + { + CV_CheckGE(audioStream, 0, "No audio stream configured"); + return retrieveAudioFrame(index, dst); + } } CV_LOG_ERROR(NULL, "GStreamer(retrive): unrecognized index=" << index); @@ -1084,17 +1364,12 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam return false; } - if (!configureStreams(params)) + if (!configureStreamsProperty(params)) { CV_LOG_WARNING(NULL, "GStreamer: can't configure streams"); return false; } - if ((videoStream >= 0 && audioStream >= 0) || (videoStream < 0 && audioStream < 0)) - { - CV_LOG_ERROR(NULL, "GStreamer backend supports audio-only or video-only capturing. Only one of the properties CAP_PROP_AUDIO_STREAM=" << audioStream << " and CAP_PROP_VIDEO_STREAM=" << videoStream << " should be >= 0"); - return false; - } if (audioStream > 0) { CV_LOG_ERROR(NULL, "GStreamer backend supports the first audio stream only. CAP_PROP_AUDIO_STREAM=" << audioStream); @@ -1112,13 +1387,15 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam bool file = false; bool manualpipeline = false; GSafePtr uri; + GSafePtr bus; + GSafePtr uridecodebin; GSafePtr color; - GstStateChangeReturn status; - GSafePtr convert; GSafePtr resample; + GstStateChangeReturn status; + // test if we have a valid uri. If so, open it with an uridecodebin // else, we might have a file or a manual pipeline. // if gstreamer cannot parse the manual pipeline, we assume we were given and @@ -1181,6 +1458,11 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam CV_Assert(uridecodebin); g_object_set(G_OBJECT(uridecodebin.get()), "uri", uri.get(), NULL); } + if (!uridecodebin) + { + CV_WARN("Can not parse GStreamer URI bin"); + return false; + } } else if (audioStream >= 0) { @@ -1188,12 +1470,6 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam CV_Assert(uridecodebin); g_object_set(G_OBJECT(uridecodebin.get()), "uri", uri.get(), NULL); } - - if (!uridecodebin) - { - CV_WARN("Can not parse GStreamer URI bin"); - return false; - } } if (manualpipeline) @@ -1256,10 +1532,10 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam pipeline.reset(gst_pipeline_new(NULL)); CV_Assert(pipeline); - sink.reset(gst_element_factory_make("appsink", NULL)); - CV_Assert(sink); if (videoStream >= 0) { + sink.reset(gst_element_factory_make("appsink", NULL)); + CV_Assert(sink); // videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert) //automatically selects the correct colorspace conversion based on caps. color.reset(gst_element_factory_make(COLOR_ELEM, NULL)); @@ -1288,13 +1564,17 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam return false; } } - else if (audioStream >= 0) + if (audioStream >= 0) { convert.reset(gst_element_factory_make("audioconvert", NULL)); resample.reset(gst_element_factory_make("audioresample", NULL)); + audiosink.reset(gst_element_factory_make("appsink", NULL)); + CV_Assert(convert); + CV_Assert(resample); + CV_Assert(audiosink); - gst_bin_add_many (GST_BIN (pipeline.get()), uridecodebin.get(), convert.get(), resample.get(), sink.get(), NULL); - if (!gst_element_link_many (convert.get(), resample.get(), sink.get(), NULL)) + gst_bin_add_many (GST_BIN (pipeline.get()), uridecodebin.get(), convert.get(), resample.get(), audiosink.get(), NULL); + if (!gst_element_link_many (convert.get(), resample.get(), audiosink.get(), NULL)) { CV_WARN("GStreamer(audio): cannot link convert -> resample -> sink"); pipeline.release(); @@ -1307,64 +1587,85 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam if (!manualpipeline || strstr(filename, " max-buffers=") == NULL) { //TODO: is 1 single buffer really high enough? - gst_app_sink_set_max_buffers(GST_APP_SINK(sink.get()), 1); + if (videoStream >= 0) + gst_app_sink_set_max_buffers(GST_APP_SINK(sink.get()), 1); + if (audioStream >= 0) + gst_app_sink_set_max_buffers(GST_APP_SINK(audiosink.get()), 1); } if (!manualpipeline) { - gst_base_sink_set_sync(GST_BASE_SINK(sink.get()), FALSE); + if (videoStream >= 0) + gst_base_sink_set_sync(GST_BASE_SINK(sink.get()), FALSE); + if (audioStream >= 0) + gst_base_sink_set_sync(GST_BASE_SINK(audiosink.get()), FALSE); } - //do not emit signals: all calls will be synchronous and blocking - gst_app_sink_set_emit_signals (GST_APP_SINK(sink.get()), FALSE); - if (videoStream >= 0) { + //do not emit signals: all calls will be synchronous and blocking + gst_app_sink_set_emit_signals (GST_APP_SINK(sink.get()), FALSE); caps.attach(gst_caps_from_string("video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}; image/jpeg")); } - else if (audioStream >= 0) + if (audioStream >= 0) { - std::string audioFormat; + gst_app_sink_set_emit_signals(GST_APP_SINK(audiosink.get()), FALSE); switch (outputAudioFormat) { case CV_8S: + { + audioBitPerSample = 8; audioFormat = "S8"; break; + } case CV_16S: + { + audioBitPerSample = 16; audioFormat = "S16LE"; break; + } case CV_32S: + { + audioBitPerSample = 32; audioFormat = "S32LE"; break; + } case CV_32F: + { + audioBitPerSample = 32; audioFormat = "F32LE"; break; + } default: audioFormat = "S16LE"; break; } std::string stringCaps = "audio/x-raw, format=(string)" + audioFormat + ", rate=(int)" + std::to_string(audioSamplesPerSecond) + ", channels=(int){1, 2}, layout=(string)interleaved"; - caps.attach(gst_caps_from_string(stringCaps.c_str())); - } + audiocaps.attach(gst_caps_from_string(stringCaps.c_str())); + gst_app_sink_set_caps(GST_APP_SINK(audiosink.get()), audiocaps); + audiocaps.release(); + } if (manualpipeline) { GSafePtr peer_caps; GSafePtr sink_pad; sink_pad.attach(gst_element_get_static_pad(sink, "sink")); peer_caps.attach(gst_pad_peer_query_caps(sink_pad, NULL)); - if (!gst_caps_can_intersect(caps, peer_caps)) { + if (!gst_caps_can_intersect(caps, peer_caps)) + { caps.attach(gst_caps_from_string("video/x-raw, format=(string){UYVY,YUY2,YVYU,NV12,NV21,YV12,I420,BGRA,RGBA,BGRx,RGBx,GRAY16_LE,GRAY16_BE}")); CV_Assert(caps); } } - - gst_app_sink_set_caps(GST_APP_SINK(sink.get()), caps); - caps.release(); - + if (videoStream >= 0) + { + gst_app_sink_set_caps(GST_APP_SINK(sink.get()), caps); + caps.release(); + } { GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-init"); status = gst_element_set_state(GST_ELEMENT(pipeline.get()), - file ? GST_STATE_PAUSED : GST_STATE_PLAYING); + file ? GST_STATE_PAUSED : GST_STATE_PLAYING); if (status == GST_STATE_CHANGE_ASYNC) { // wait for status update @@ -1379,14 +1680,14 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam return false; } - GSafePtr pad; - pad.attach(gst_element_get_static_pad(sink, "sink")); - - GSafePtr buffer_caps; - buffer_caps.attach(gst_pad_get_current_caps(pad)); - if (videoStream >= 0) { + GSafePtr pad; + pad.attach(gst_element_get_static_pad(sink, "sink")); + + GSafePtr buffer_caps; + buffer_caps.attach(gst_pad_get_current_caps(pad)); + GstFormat format; format = GST_FORMAT_DEFAULT; @@ -1433,8 +1734,15 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam isPosFramesSupported = true; } } - else if (audioStream >= 0) + + if (audioStream >= 0) { + GSafePtr pad; + pad.attach(gst_element_get_static_pad(audiosink, "sink")); + + GSafePtr buffer_caps; + buffer_caps.attach(gst_pad_get_current_caps(pad)); + GstAudioInfo info = {}; if (gst_audio_info_from_caps(&info, buffer_caps)) { @@ -1450,8 +1758,10 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam } std::vector unused_params = params.getUnused(); - for (int key : unused_params) { - if (!setProperty(key, params.get(key))) { + for (int key : unused_params) + { + if (!setProperty(key, params.get(key))) + { CV_LOG_ERROR(NULL, "VIDEOIO/GStreamer: can't set property " << key); return false; } @@ -1506,6 +1816,10 @@ double GStreamerCapture::getProperty(int propId) const case CV_CAP_PROP_POS_MSEC: CV_LOG_ONCE_WARNING(NULL, "OpenCV | GStreamer: CAP_PROP_POS_MSEC property result may be unrealiable: " "https://github.com/opencv/opencv/issues/19025"); + if (audioStream != -1) + { + return usedVideoSampleTimeNS * 1e-6; + } format = GST_FORMAT_TIME; status = gst_element_query_position(sink.get(), CV_GST_FORMAT(format), &value); if(!status) { @@ -1580,6 +1894,14 @@ double GStreamerCapture::getProperty(int propId) const return outputAudioFormat; case CAP_PROP_AUDIO_BASE_INDEX: return audioBaseIndex; + case CAP_PROP_AUDIO_TOTAL_STREAMS: + CV_LOG_ONCE_WARNING(NULL, "OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported"); + return 0; + case CAP_PROP_AUDIO_POS: + return audioSamplePosInSamples; + case CAP_PROP_AUDIO_SHIFT_NSEC: + CV_LOG_ONCE_WARNING(NULL, "OpenCV | GStreamer: CAP_PROP_AUDIO_SHIFT_NSEC property is not supported"); + return 0; case CAP_PROP_OPEN_TIMEOUT_MSEC: return GST_TIME_AS_MSECONDS(openTimeout); case CAP_PROP_READ_TIMEOUT_MSEC: diff --git a/modules/videoio/test/test_audio.cpp b/modules/videoio/test/test_audio.cpp index e077b5bff9..4cf0dc350b 100644 --- a/modules/videoio/test/test_audio.cpp +++ b/modules/videoio/test/test_audio.cpp @@ -170,7 +170,6 @@ public: const int samplePerSecond = (int)cap.get(CAP_PROP_AUDIO_SAMPLES_PER_SECOND); ASSERT_EQ(44100, samplePerSecond); int samplesPerFrame = (int)(1./fps*samplePerSecond); - int audioSamplesTolerance = samplesPerFrame / 2; double audio0_timestamp = 0; @@ -182,7 +181,6 @@ public: SCOPED_TRACE(cv::format("frame=%d", frame)); ASSERT_TRUE(cap.grab()); - if (frame == 0) { double audio_shift = cap.get(CAP_PROP_AUDIO_SHIFT_NSEC); @@ -190,7 +188,6 @@ public: audio0_timestamp = video0_timestamp + audio_shift * 1e-9; std::cout << "video0 timestamp: " << video0_timestamp << " audio0 timestamp: " << audio0_timestamp << " (audio shift nanoseconds: " << audio_shift << " , seconds: " << audio_shift * 1e-9 << ")" << std::endl; } - ASSERT_TRUE(cap.retrieve(videoFrame)); if (epsilon >= 0) { @@ -236,8 +233,12 @@ public: } if (frame != 0 && frame != numberOfFrames-1 && audioData[0].size() != (size_t)numberOfSamples) { - // validate audio frame size - EXPECT_NEAR(audioFrame.cols, samplesPerFrame, audioSamplesTolerance); + if (backend == cv::CAP_MSMF) + { + int audioSamplesTolerance = samplesPerFrame / 2; + // validate audio frame size + EXPECT_NEAR(audioFrame.cols, samplesPerFrame, audioSamplesTolerance); + } } } ASSERT_FALSE(cap.grab()); @@ -267,11 +268,11 @@ TEST_P(Media, audio) doTest(); } -#ifdef _WIN32 const paramCombination mediaParams[] = { + paramCombination("test_audio.mp4", 1, 0.15, CV_8UC3, 240, 320, 90, 132299, 30, 30., cv::CAP_GSTREAMER) #ifdef _WIN32 - paramCombination("test_audio.mp4", 1, 0.15, CV_8UC3, 240, 320, 90, 131819, 30, 30., cv::CAP_MSMF) + , paramCombination("test_audio.mp4", 1, 0.15, CV_8UC3, 240, 320, 90, 131819, 30, 30., cv::CAP_MSMF) #if 0 // https://filesamples.com/samples/video/mp4/sample_960x400_ocean_with_audio.mp4 , paramCombination("sample_960x400_ocean_with_audio.mp4", 2, -1/*eplsilon*/, CV_8UC3, 400, 960, 1116, 2056588, 30, 30., cv::CAP_MSMF) @@ -280,10 +281,12 @@ const paramCombination mediaParams[] = }; INSTANTIATE_TEST_CASE_P(/**/, Media, testing::ValuesIn(mediaParams)); -#endif // _WIN32 TEST(AudioOpenCheck, bad_arg_invalid_audio_stream) { + if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF))) + throw SkipTestException("CAP_MSMF backend was not found"); + std::string fileName = "audio/test_audio.wav"; std::vector params { CAP_PROP_AUDIO_STREAM, 1, @@ -291,12 +294,15 @@ TEST(AudioOpenCheck, bad_arg_invalid_audio_stream) CAP_PROP_AUDIO_DATA_DEPTH, CV_16S }; VideoCapture cap; - cap.open(findDataFile(fileName), cv::CAP_ANY, params); + cap.open(findDataFile(fileName), cv::CAP_MSMF, params); ASSERT_FALSE(cap.isOpened()); } TEST(AudioOpenCheck, bad_arg_invalid_audio_stream_video) { + if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF))) + throw SkipTestException("CAP_MSMF backend was not found"); + std::string fileName = "audio/test_audio.mp4"; std::vector params { CAP_PROP_AUDIO_STREAM, 1, @@ -304,13 +310,16 @@ TEST(AudioOpenCheck, bad_arg_invalid_audio_stream_video) CAP_PROP_AUDIO_DATA_DEPTH, CV_16S }; VideoCapture cap; - cap.open(findDataFile(fileName), cv::CAP_ANY, params); + cap.open(findDataFile(fileName), cv::CAP_MSMF, params); ASSERT_FALSE(cap.isOpened()); } -#ifdef _WIN32 + TEST(AudioOpenCheck, MSMF_bad_arg_invalid_audio_sample_per_second) { + if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF))) + throw SkipTestException("CAP_MSMF backend was not found"); + std::string fileName = "audio/test_audio.mp4"; std::vector params { CAP_PROP_AUDIO_STREAM, 0, @@ -321,7 +330,6 @@ TEST(AudioOpenCheck, MSMF_bad_arg_invalid_audio_sample_per_second) cap.open(findDataFile(fileName), cv::CAP_MSMF, params); ASSERT_FALSE(cap.isOpened()); } -#endif TEST(AudioOpenCheck, bad_arg_invalid_audio_sample_per_second) { From ad568edd7f0ebfc12f69847d0d16184279ace176 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Thu, 15 Dec 2022 11:16:01 +0100 Subject: [PATCH 294/313] Remove references to deprecated NumPy type aliases. This change replaces references to a number of deprecated NumPy type aliases (np.bool, np.int, np.float, np.complex, np.object, np.str) with their recommended replacement (bool, int, float, complex, object, str). Those types were deprecated in 1.20 and are removed in 1.24, cf https://github.com/numpy/numpy/pull/22607. --- modules/dnn/test/pascal_semsegm_test_fcn.py | 4 +- modules/python/test/test_misc.py | 51 ++++++++++----------- modules/python/test/tst_scene_render.py | 2 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/modules/dnn/test/pascal_semsegm_test_fcn.py b/modules/dnn/test/pascal_semsegm_test_fcn.py index 1c8407a79d..d79f6be13b 100644 --- a/modules/dnn/test/pascal_semsegm_test_fcn.py +++ b/modules/dnn/test/pascal_semsegm_test_fcn.py @@ -32,7 +32,7 @@ def eval_segm_result(net_out): channels_dim = 1 y_dim = channels_dim + 1 x_dim = y_dim + 1 - res = np.zeros(net_out.shape).astype(np.int) + res = np.zeros(net_out.shape).astype(int) for i in range(net_out.shape[y_dim]): for j in range(net_out.shape[x_dim]): max_ch = np.argmax(net_out[..., i, j]) @@ -88,7 +88,7 @@ class DatasetImageFetch(object): @staticmethod def color_to_gt(color_img, colors): num_classes = len(colors) - gt = np.zeros((num_classes, color_img.shape[0], color_img.shape[1])).astype(np.int) + gt = np.zeros((num_classes, color_img.shape[0], color_img.shape[1])).astype(int) for img_y in range(color_img.shape[0]): for img_x in range(color_img.shape[1]): c = DatasetImageFetch.pix_to_c(color_img[img_y][img_x]) diff --git a/modules/python/test/test_misc.py b/modules/python/test/test_misc.py index f0c184e390..765201e973 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -211,8 +211,8 @@ class Arguments(NewOpenCVTests): def test_parse_to_bool_convertible(self): try_to_convert = partial(self._try_to_convert, cv.utils.dumpBool) - for convertible_true in (True, 1, 64, np.bool(1), np.int8(123), np.int16(11), np.int32(2), - np.int64(1), np.bool_(3), np.bool8(12)): + for convertible_true in (True, 1, 64, np.int8(123), np.int16(11), np.int32(2), + np.int64(1), np.bool_(12)): actual = try_to_convert(convertible_true) self.assertEqual('bool: true', actual, msg=get_conversion_error_msg(convertible_true, 'bool: true', actual)) @@ -223,8 +223,8 @@ class Arguments(NewOpenCVTests): msg=get_conversion_error_msg(convertible_false, 'bool: false', actual)) def test_parse_to_bool_not_convertible(self): - for not_convertible in (1.2, np.float(2.3), 's', 'str', (1, 2), [1, 2], complex(1, 1), - complex(imag=2), complex(1.1), np.array([1, 0], dtype=np.bool)): + for not_convertible in (1.2, np.float32(2.3), 's', 'str', (1, 2), [1, 2], complex(1, 1), + complex(imag=2), complex(1.1), np.array([1, 0], dtype=bool)): with self.assertRaises((TypeError, OverflowError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpBool(not_convertible) @@ -238,7 +238,7 @@ class Arguments(NewOpenCVTests): msg=get_conversion_error_msg(convertible_true, 'bool: true', actual)) def test_parse_to_bool_not_convertible_extra(self): - for not_convertible in (np.array([False]), np.array([True], dtype=np.bool)): + for not_convertible in (np.array([False]), np.array([True])): with self.assertRaises((TypeError, OverflowError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpBool(not_convertible) @@ -255,7 +255,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_int_not_convertible(self): min_int, max_int = get_limits(ctypes.c_int) - for not_convertible in (1.2, np.float(4), float(3), np.double(45), 's', 'str', + for not_convertible in (1.2, float(3), np.float32(4), np.double(45), 's', 'str', np.array([1, 2]), (1,), [1, 2], min_int - 1, max_int + 1, complex(1, 1), complex(imag=2), complex(1.1)): with self.assertRaises((TypeError, OverflowError, ValueError), @@ -265,7 +265,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_int_not_convertible_extra(self): for not_convertible in (np.bool_(True), True, False, np.float32(2.3), np.array([3, ], dtype=int), np.array([-2, ], dtype=np.int32), - np.array([1, ], dtype=np.int), np.array([11, ], dtype=np.uint8)): + np.array([11, ], dtype=np.uint8)): with self.assertRaises((TypeError, OverflowError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpInt(not_convertible) @@ -282,12 +282,11 @@ class Arguments(NewOpenCVTests): def test_parse_to_int64_not_convertible(self): min_int64, max_int64 = get_limits(ctypes.c_longlong) - for not_convertible in (1.2, np.float(4), float(3), np.double(45), 's', 'str', + for not_convertible in (1.2, np.float32(4), float(3), np.double(45), 's', 'str', np.array([1, 2]), (1,), [1, 2], min_int64 - 1, max_int64 + 1, complex(1, 1), complex(imag=2), complex(1.1), np.bool_(True), True, False, np.float32(2.3), np.array([3, ], dtype=int), - np.array([-2, ], dtype=np.int32), np.array([1, ], dtype=np.int), - np.array([11, ], dtype=np.uint8)): + np.array([-2, ], dtype=np.int32), np.array([11, ], dtype=np.uint8)): with self.assertRaises((TypeError, OverflowError, ValueError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpInt64(not_convertible) @@ -305,7 +304,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_size_t_not_convertible(self): min_long, _ = get_limits(ctypes.c_long) - for not_convertible in (1.2, True, False, np.bool_(True), np.float(4), float(3), + for not_convertible in (1.2, True, False, np.bool_(True), np.float32(4), float(3), np.double(45), 's', 'str', np.array([1, 2]), (1,), [1, 2], np.float64(6), complex(1, 1), complex(imag=2), complex(1.1), -1, min_long, np.int8(-35)): @@ -331,7 +330,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_float_convertible(self): try_to_convert = partial(self._try_to_convert, cv.utils.dumpFloat) min_float, max_float = get_limits(ctypes.c_float) - for convertible in (2, -13, 1.24, float(32), np.float(32.45), np.double(12.23), + for convertible in (2, -13, 1.24, np.float32(32.45), float(32), np.double(12.23), np.float32(-12.3), np.float64(3.22), np.float_(-1.5), min_float, max_float, np.inf, -np.inf, float('Inf'), -float('Inf'), np.double(np.inf), np.double(-np.inf), np.double(float('Inf')), @@ -357,7 +356,7 @@ class Arguments(NewOpenCVTests): msg=get_conversion_error_msg(inf, expected, actual)) def test_parse_to_float_not_convertible(self): - for not_convertible in ('s', 'str', (12,), [1, 2], np.array([1, 2], dtype=np.float), + for not_convertible in ('s', 'str', (12,), [1, 2], np.array([1, 2], dtype=float), np.array([1, 2], dtype=np.double), complex(1, 1), complex(imag=2), complex(1.1)): with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)): @@ -366,7 +365,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_float_not_convertible_extra(self): for not_convertible in (np.bool_(False), True, False, np.array([123, ], dtype=int), np.array([1., ]), np.array([False]), - np.array([True], dtype=np.bool)): + np.array([True])): with self.assertRaises((TypeError, OverflowError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpFloat(not_convertible) @@ -375,7 +374,7 @@ class Arguments(NewOpenCVTests): try_to_convert = partial(self._try_to_convert, cv.utils.dumpDouble) min_float, max_float = get_limits(ctypes.c_float) min_double, max_double = get_limits(ctypes.c_double) - for convertible in (2, -13, 1.24, np.float(32.45), float(2), np.double(12.23), + for convertible in (2, -13, 1.24, np.float32(32.45), float(2), np.double(12.23), np.float32(-12.3), np.float64(3.22), np.float_(-1.5), min_float, max_float, min_double, max_double, np.inf, -np.inf, float('Inf'), -float('Inf'), np.double(np.inf), np.double(-np.inf), @@ -394,7 +393,7 @@ class Arguments(NewOpenCVTests): "Actual: {}".format(type(nan).__name__, actual)) def test_parse_to_double_not_convertible(self): - for not_convertible in ('s', 'str', (12,), [1, 2], np.array([1, 2], dtype=np.float), + for not_convertible in ('s', 'str', (12,), [1, 2], np.array([1, 2], dtype=np.float32), np.array([1, 2], dtype=np.double), complex(1, 1), complex(imag=2), complex(1.1)): with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)): @@ -403,14 +402,14 @@ class Arguments(NewOpenCVTests): def test_parse_to_double_not_convertible_extra(self): for not_convertible in (np.bool_(False), True, False, np.array([123, ], dtype=int), np.array([1., ]), np.array([False]), - np.array([12.4], dtype=np.double), np.array([True], dtype=np.bool)): + np.array([12.4], dtype=np.double), np.array([True])): with self.assertRaises((TypeError, OverflowError), msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpDouble(not_convertible) def test_parse_to_cstring_convertible(self): try_to_convert = partial(self._try_to_convert, cv.utils.dumpCString) - for convertible in ('', 's', 'str', str(123), ('char'), np.str('test1'), np.str_('test2')): + for convertible in ('', 's', 'str', str(123), ('char'), np.str_('test2')): expected = 'string: ' + convertible actual = try_to_convert(convertible) self.assertEqual(expected, actual, @@ -424,7 +423,7 @@ class Arguments(NewOpenCVTests): def test_parse_to_string_convertible(self): try_to_convert = partial(self._try_to_convert, cv.utils.dumpString) - for convertible in (None, '', 's', 'str', str(123), np.str('test1'), np.str_('test2')): + for convertible in (None, '', 's', 'str', str(123), np.str_('test2')): expected = 'string: ' + (convertible if convertible else '') actual = try_to_convert(convertible) self.assertEqual(expected, actual, @@ -546,12 +545,12 @@ class Arguments(NewOpenCVTests): def test_parse_vector_int_not_convertible(self): np.random.seed(123098765) - arr = np.random.randint(-20, 20, 40).astype(np.float).reshape(10, 2, 2) + arr = np.random.randint(-20, 20, 40).astype(np.float32).reshape(10, 2, 2) int_min, int_max = get_limits(ctypes.c_int) test_dict = {1: 2, 3: 10, 10: 20} for not_convertible in ((int_min, 1, 2.5, 3, int_max), [True, 50], 'test', test_dict, reversed([1, 2, 3]), - np.array([int_min, -10, 24, [1, 2]], dtype=np.object), + np.array([int_min, -10, 24, [1, 2]], dtype=object), np.array([[1, 2], [3, 4]]), arr[:, 0, 1],): with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpVectorOfInt(not_convertible) @@ -563,7 +562,7 @@ class Arguments(NewOpenCVTests): for convertible in ((1, 2.12, 3.5), [40, 50], tuple(), np.array([-10, 24], dtype=np.int32), np.array([-12.5, 1.4], dtype=np.double), - np.array([10, 230, 12], dtype=np.float), arr[:, 0, 1], ): + np.array([10, 230, 12], dtype=np.float32), arr[:, 0, 1], ): expected = "[" + ", ".join(map(lambda v: "{:.2f}".format(v), convertible)) + "]" actual = try_to_convert(convertible) self.assertEqual(expected, actual, @@ -572,7 +571,7 @@ class Arguments(NewOpenCVTests): def test_parse_vector_double_not_convertible(self): test_dict = {1: 2, 3: 10, 10: 20} for not_convertible in (('t', 'e', 's', 't'), [True, 50.55], 'test', test_dict, - np.array([-10.1, 24.5, [1, 2]], dtype=np.object), + np.array([-10.1, 24.5, [1, 2]], dtype=object), np.array([[1, 2], [3, 4]]),): with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpVectorOfDouble(not_convertible) @@ -584,7 +583,7 @@ class Arguments(NewOpenCVTests): arr_of_rect_cast = np.random.randint(10, 40, 4 * 5).astype(np.uint8).reshape(5, 4) for convertible in (((1, 2, 3, 4), (10, -20, 30, 10)), arr_of_rect_int32, arr_of_rect_cast, arr_of_rect_int32.astype(np.int8), [[5, 3, 1, 4]], - ((np.int8(4), np.uint8(10), np.int(32), np.int16(55)),)): + ((np.int8(4), np.uint8(10), int(32), np.int16(55)),)): expected = "[" + ", ".join(map(lambda v: "[x={}, y={}, w={}, h={}]".format(*v), convertible)) + "]" actual = try_to_convert(convertible) self.assertEqual(expected, actual, @@ -592,10 +591,10 @@ class Arguments(NewOpenCVTests): def test_parse_vector_rect_not_convertible(self): np.random.seed(1238765) - arr = np.random.randint(5, 20, 4 * 3).astype(np.float).reshape(3, 4) + arr = np.random.randint(5, 20, 4 * 3).astype(np.float32).reshape(3, 4) for not_convertible in (((1, 2, 3, 4), (10.5, -20, 30.1, 10)), arr, [[5, 3, 1, 4], []], - ((np.float(4), np.uint8(10), np.int(32), np.int16(55)),)): + ((float(4), np.uint8(10), int(32), np.int16(55)),)): with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): _ = cv.utils.dumpVectorOfRect(not_convertible) diff --git a/modules/python/test/tst_scene_render.py b/modules/python/test/tst_scene_render.py index 2dd6309ce1..33fef512e9 100644 --- a/modules/python/test/tst_scene_render.py +++ b/modules/python/test/tst_scene_render.py @@ -85,7 +85,7 @@ class TestSceneRender(): img[self.currentCenter[0]:self.currentCenter[0]+self.foreground.shape[0], self.currentCenter[1]:self.currentCenter[1]+self.foreground.shape[1]] = self.foreground else: - self.currentRect = self.initialRect + np.int( 30*cos(self.time) + 50*sin(self.time/3)) + self.currentRect = self.initialRect + int( 30*cos(self.time) + 50*sin(self.time/3)) if self.deformation: self.currentRect[1:3] += int(self.h/20*cos(self.time)) cv.fillConvexPoly(img, self.currentRect, (0, 0, 255)) From 8681686d8f7ba0b29adc3dfc33ae96cefa49f604 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 23 Dec 2022 19:58:41 +0300 Subject: [PATCH 295/313] Merge pull request #22957 from dkurt:new_openvino_api Switch to new OpenVINO API after 2022.1 release * Pass Layer_Test_Convolution_DLDT.Accuracy/0 test * Pass test Test_Caffe_layers.Softmax * Failed 136 tests * Fix Concat. Failed 120 tests * Custom nGraph ops. 19 failed tests * Set and get properties from Core * Read model from buffer * Change MaxPooling layer output names. Restore reshape * Cosmetic changes * Cosmetic changes * Override getOutputsInfo * Fixes for OpenVINO < 2022.1 * Async inference for 2021.4 and less * Compile model with config * Fix serialize for 2022.1 * Asynchronous inference with 2022.1 * Handle 1d outputs * Work with model with dynamic output shape * Fixes with 1d output for old API * Control outputs by nGraph function for all OpenVINO versions * Refer inputs in PrePostProcessor by indices * Fix cycled dependency between InfEngineNgraphNode and InfEngineNgraphNet. Add InferRequest callback only for async inference. Do not capture InferRequest object. * Fix tests thresholds * Fix HETERO:GPU,CPU plugin issues with unsupported layer --- modules/dnn/src/ie_ngraph.cpp | 437 ++++++++++++------ modules/dnn/src/ie_ngraph.hpp | 20 +- modules/dnn/src/layers/concat_layer.cpp | 10 +- .../dnn/src/layers/nary_eltwise_layers.cpp | 38 ++ modules/dnn/src/layers/resize_layer.cpp | 19 + modules/dnn/src/net_openvino.cpp | 74 +-- modules/dnn/src/op_inf_engine.cpp | 90 +++- modules/dnn/src/op_inf_engine.hpp | 66 ++- modules/dnn/test/test_backends.cpp | 2 +- modules/dnn/test/test_caffe_importer.cpp | 5 +- modules/dnn/test/test_darknet_importer.cpp | 6 + modules/dnn/test/test_ie_models.cpp | 3 + modules/dnn/test/test_layers.cpp | 2 +- modules/dnn/test/test_model.cpp | 4 + 14 files changed, 571 insertions(+), 205 deletions(-) diff --git a/modules/dnn/src/ie_ngraph.cpp b/modules/dnn/src/ie_ngraph.cpp index bb65faf4eb..9a80c5e328 100644 --- a/modules/dnn/src/ie_ngraph.cpp +++ b/modules/dnn/src/ie_ngraph.cpp @@ -35,6 +35,7 @@ static bool DNN_IE_SERIALIZE = utils::getConfigurationParameterBool("OPENCV_DNN_ static std::string kDefaultInpLayerName = "opencv_ngraph_empty_inp_layer_name"; static constexpr const char* kOpenCVLayersType = "opencv_ngraph_layer"; +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2022_1) static std::string shapesToStr(const std::vector& mats) { std::ostringstream shapes; @@ -62,6 +63,7 @@ static void strToShapes(const std::string& str, std::vector ss >> shapes[i][j]; } } +#endif // OpenVINO < 2022.1 static std::vector > ngraphWrappers(const std::vector >& ptrs) @@ -76,6 +78,61 @@ ngraphWrappers(const std::vector >& ptrs) return wrappers; } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + +class NgraphCustomOp: public ov::op::Op { +public: + OPENVINO_OP(kOpenCVLayersType); + + NgraphCustomOp(const ngraph::OutputVector& inputs, Ptr& cvLayer, const std::vector& outputs, const std::vector& internals): + Op(inputs), cvLayer(cvLayer), outputs(outputs), internals(internals) + { + constructor_validate_and_infer_types(); + } + + void validate_and_infer_types() override + { + set_output_size(outputs.size()); + for (int i = 0; i < outputs.size(); ++i) + { + ov::PartialShape shape; + for (int j = 0; j < outputs[i].dims; ++j) { + shape.push_back(outputs[i].size[j]); + } + set_output_type(i, get_input_element_type(0), shape); + } + } + + std::shared_ptr clone_with_new_inputs(const ngraph::OutputVector& new_args) const override + { + return std::make_shared(new_args, cvLayer, outputs, internals); + } + + bool has_evaluate() const { + return true; + } + + bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override { + std::vector inpMats, outMats; + infEngineBlobsToMats(inputs, inpMats); + infEngineBlobsToMats(outputs, outMats); + try + { + cvLayer->forward(inpMats, outMats, internals); + return true; + } + catch (...) + { + return false; + } + } + + Ptr& cvLayer; + std::vector outputs, internals; +}; + +#else + class NgraphCustomOp: public ngraph::op::Op { public: const ngraph::NodeTypeInfo& get_type_info() const override @@ -324,7 +381,7 @@ public: #endif }; - +#endif // OpenVINO >= 2022.1 InfEngineNgraphNode::InfEngineNgraphNode(std::shared_ptr&& _node) : BackendNode(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH), node(std::move(_node)) {} @@ -337,15 +394,6 @@ InfEngineNgraphNode::InfEngineNgraphNode(const std::vector >& n std::vector& outputs, std::vector& internals) : BackendNode(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH), cvLayer(cvLayer_) { - std::ostringstream oss; - oss << (size_t)cvLayer.get(); - - std::map params = { - {"impl", oss.str()}, - {"outputs", shapesToStr(outputs)}, - {"internals", shapesToStr(internals)} - }; - #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_3) ngraph::OutputVector inp_nodes; #else @@ -353,7 +401,19 @@ InfEngineNgraphNode::InfEngineNgraphNode(const std::vector >& n #endif for (const auto& node : nodes) inp_nodes.emplace_back(node.dynamicCast()->node); + +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + node = std::make_shared(inp_nodes, cvLayer, outputs, internals); +#else + std::ostringstream oss; + oss << (size_t)cvLayer.get(); + std::map params = { + {"impl", oss.str()}, + {"outputs", shapesToStr(outputs)}, + {"internals", shapesToStr(internals)} + }; node = std::make_shared(inp_nodes, params); +#endif CV_Assert(!cvLayer->name.empty()); setName(cvLayer->name); @@ -383,7 +443,7 @@ void InfEngineNgraphNet::addOutput(const Ptr& node) CV_Assert(node); CV_Assert(node->node); const std::string& name = node->node->get_friendly_name(); - requestedOutputs.insert({name, node}); + requestedOutputs.insert({name, node.get()}); } void InfEngineNgraphNet::setNodePtr(std::shared_ptr* ptr) { @@ -457,6 +517,9 @@ void InfEngineNgraphNet::createNet(Target targetId) { CV_LOG_DEBUG(NULL, "DNN/NGRAPH: Add 'Result' output: " << output_node_it->first); CV_Assert(output_node_it->second); auto out = std::make_shared(output_node_it->second->node); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + out->set_friendly_name(output_node_it->first + (output_node_it->second->node->get_output_size() == 1 ? "" : ".0")); +#endif outs.push_back(out); } CV_Assert_N(!inputs_vec.empty(), !outs.empty()); @@ -504,12 +567,20 @@ void InfEngineNgraphNet::createNet(Target targetId) { } } +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2022_1) +static inline +InferenceEngine::Layout estimateLayout(size_t dims); +#endif + void InfEngineNgraphNet::init(Target targetId) { if (!hasNetOwner) { if (targetId == DNN_TARGET_OPENCL_FP16) { +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ov::pass::ConvertFP32ToFP16().run_on_model(ngraph_function); +#else auto nodes = ngraph_function->get_ordered_ops(); for (auto& node : nodes) { @@ -533,6 +604,7 @@ void InfEngineNgraphNet::init(Target targetId) } } ngraph_function->validate_nodes_and_infer_types(); +#endif // OpenVINO >= 2022.1 } cnn = InferenceEngine::CNNNetwork(ngraph_function); @@ -580,20 +652,45 @@ void InfEngineNgraphNet::init(Target targetId) CV_Error(Error::StsNotImplemented, "Unknown target"); }; - if (!hasNetOwner) { - for (size_t i = 0; i < ngraph_function->get_output_size(); ++i) { - auto node = ngraph_function->output(i).get_node(); - for (size_t j = 0; j < node->get_input_size(); ++j) { - std::string name = node->input_value(j).get_node()->get_friendly_name(); - auto iter = requestedOutputs.find(name); - if (iter != requestedOutputs.end()) { - requestedOutputs.erase(iter); - cnn.addOutput(name); - } - } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + auto model = cnn.getFunction(); + ov::preprocess::PrePostProcessor ppp(model); + int i = 0; + for (const auto& inp : model->inputs()) { // TODO: not sure why but ngraph_function->inputs() here causes segfault. + const std::string& name = inp.get_node()->get_friendly_name(); + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + + auto srcT = blobIt->second.get_element_type(); + if (srcT != inp.get_node()->get_element_type()) { + ppp.input(i++).tensor().set_element_type(srcT); } } + i = 0; + for (const auto& it : model->outputs()) + { + const std::string& name = it.get_node()->get_friendly_name(); + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + const auto& src = blobIt->second; + + // A workaround for single dimension output for which OpenCV allocates 2d Mat. + // For example, face-detection-0105 with Result of shape {200} while output blob is {200, 1} + auto outShape = it.get_partial_shape().get_max_shape(); + if (outShape != src.get_shape()) { + size_t sz = std::accumulate(outShape.begin(), outShape.end(), 1, std::multiplies()); + CV_Assert(sz == src.get_size()); + allBlobs[name] = ov::Tensor(src.get_element_type(), outShape, src.data()); + } + + ppp.output(i++).tensor().set_element_type(ov::element::f32); // Should be always FP32 + } + + ppp.build(); + +#else + for (const auto& it : cnn.getInputsInfo()) { const std::string& name = it.first; @@ -607,8 +704,16 @@ void InfEngineNgraphNet::init(Target targetId) const std::string& name = it.first; auto blobIt = allBlobs.find(name); CV_Assert(blobIt != allBlobs.end()); + InferenceEngine::TensorDesc& desc = blobIt->second->getTensorDesc(); + + auto outShape = it.second->getDims(); + if (outShape != desc.getDims()) { + desc.reshape(outShape, estimateLayout(outShape.size())); + } + it.second->setPrecision(blobIt->second->getTensorDesc().getPrecision()); // Should be always FP32 } +#endif // OpenVINO >= 2022.1 initPlugin(cnn); } @@ -660,6 +765,9 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) const std::string& libName = candidates[i]; try { +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ie.add_extension(libName); +#else InferenceEngine::IExtensionPtr extension = #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4) std::make_shared(libName); @@ -668,6 +776,7 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) #endif ie.AddExtension(extension, "CPU"); +#endif CV_LOG_INFO(NULL, "DNN-IE: Loaded extension plugin: " << libName); found = true; break; @@ -678,6 +787,7 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) { CV_LOG_WARNING(NULL, "DNN-IE: Can't load extension plugin (extra layers for some networks). Specify path via OPENCV_DNN_IE_EXTRA_PLUGIN_PATH parameter"); } +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2022_1) // Some of networks can work without a library of extra layers. // OpenCV fallbacks as extensions. try @@ -688,12 +798,17 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) { CV_LOG_INFO(NULL, "DNN-IE: Can't register OpenCV custom layers nGraph extension: " << e.what()); } +#endif // OpenVINO < 2022.1 #ifndef _WIN32 // Limit the number of CPU threads. if (device_name == "CPU") +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ie.set_property(device_name, ov::inference_num_threads(getNumThreads())); +#else ie.SetConfig({{ InferenceEngine::PluginConfigParams::KEY_CPU_THREADS_NUM, format("%d", getNumThreads()), }}, device_name); +#endif // OpenVINO >= 2022.1 #endif #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_2) if (device_name.find("GPU") == 0) @@ -706,9 +821,13 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) if (!cache_path.empty() && cache_path != "disabled") { CV_LOG_INFO(NULL, "OpenCV/nGraph: using GPU kernels cache: " << cache_path); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ie.set_property(device_name, ov::cache_dir(cache_path)); +#else ie.SetConfig({{ InferenceEngine::PluginConfigParams::KEY_CACHE_DIR, cache_path, }}, device_name); +#endif // OpenVINO >= 2022.1 } } #endif @@ -716,9 +835,9 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) std::map config; if (device_name == "MYRIAD" || device_name == "HDDL") { #if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4) - config.emplace("MYRIAD_DETECT_NETWORK_BATCH", CONFIG_VALUE(NO)); + config.emplace("MYRIAD_DETECT_NETWORK_BATCH", "NO"); #else - config.emplace("VPU_DETECT_NETWORK_BATCH", CONFIG_VALUE(NO)); + config.emplace("VPU_DETECT_NETWORK_BATCH", "NO"); #endif } @@ -757,16 +876,17 @@ bool NgraphBackendLayer::getMemoryShapes(const std::vector &inputs, std::vector &outputs, std::vector &internals) const { - InferenceEngine::ICNNNetwork::InputShapes inShapes = t_net.getInputShapes(); - InferenceEngine::ICNNNetwork::InputShapes::iterator itr; + auto ngraphFunction = t_net.getFunction(); bool equal_flag = true; - size_t i = 0; - for (itr = inShapes.begin(); itr != inShapes.end(); ++itr) + std::map > inShapes; + int i = 0; + for (const auto& inp : ngraphFunction->get_parameters()) { - InferenceEngine::SizeVector currentInShape(inputs[i].begin(), inputs[i].end()); - if (itr->second != currentInShape) + std::vector oldShape = inp->get_shape(); + std::vector newShape(inputs[i].begin(), inputs[i].end()); + inShapes.insert({inp->get_friendly_name(), newShape}); + if (oldShape != newShape) { - itr->second = currentInShape; equal_flag = false; } i++; @@ -777,7 +897,18 @@ bool NgraphBackendLayer::getMemoryShapes(const std::vector &inputs, InferenceEngine::CNNNetwork curr_t_net(t_net); curr_t_net.reshape(inShapes); } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + std::vector dims; + for (const auto& it : ngraphFunction->outputs()) { + if (it.get_node()->get_friendly_name() == name) { + dims = it.get_partial_shape().get_max_shape(); + } + } + if (dims.empty()) + CV_Error(Error::StsError, format("Unable find result with name %s", name.c_str())); +#else std::vector dims = t_net.getOutputsInfo()[name]->getDims(); +#endif outputs.push_back(MatShape(dims.begin(), dims.end())); return false; } @@ -795,6 +926,21 @@ void NgraphBackendLayer::forward(InputArrayOfArrays inputs, OutputArrayOfArrays CV_Error(Error::StsInternal, "Choose Inference Engine as a preferable backend."); } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + +ov::Tensor wrapToNgraphBlob(const Mat& m) { + std::vector shape = getShape(m); + if (m.type() == CV_32F) + return ov::Tensor(ov::element::f32, shape, m.data); + else if (m.type() == CV_8U) + return ov::Tensor(ov::element::u8, shape, m.data); + else if (m.type() == CV_32SC1) + return ov::Tensor(ov::element::i32, shape, m.data); + else + CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str())); +} + +#else static InferenceEngine::Layout estimateLayout(int dims) { @@ -823,19 +969,6 @@ InferenceEngine::Layout estimateLayout(const Mat& m) return estimateLayout(m.dims); } -static InferenceEngine::DataPtr wrapToInfEngineDataNode(const Mat& m, const std::string& name = "") -{ - std::vector shape = getShape(m); - if (m.type() == CV_32F) - return InferenceEngine::DataPtr(new InferenceEngine::Data(name, - {InferenceEngine::Precision::FP32, shape, estimateLayout(m)})); - else if (m.type() == CV_8U) - return InferenceEngine::DataPtr(new InferenceEngine::Data(name, - {InferenceEngine::Precision::U8, shape, estimateLayout(m)})); - else - CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str())); -} - InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m, const std::vector& shape, InferenceEngine::Layout layout) { @@ -845,6 +978,9 @@ InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m, const std::vector( {InferenceEngine::Precision::U8, shape, layout}, (uint8_t*)m.data); + else if (m.type() == CV_32SC1) + return InferenceEngine::make_shared_blob( + {InferenceEngine::Precision::I32, shape, layout}, (int32_t*)m.data); else CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str())); } @@ -855,12 +991,15 @@ InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m, InferenceEngine::Layou return wrapToNgraphBlob(m, shape, layout); } +InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m) { return wrapToNgraphBlob(m, estimateLayout(m)); } + +#endif // OpenVINO >= 2022.1 + NgraphBackendWrapper::NgraphBackendWrapper(int targetId, const cv::Mat& m) : BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, targetId) , host((Mat*)&m) { - dataPtr = wrapToInfEngineDataNode(m); - blob = wrapToNgraphBlob(m, estimateLayout(m)); + blob = wrapToNgraphBlob(m); } NgraphBackendWrapper::NgraphBackendWrapper(Ptr wrapper) @@ -868,8 +1007,7 @@ NgraphBackendWrapper::NgraphBackendWrapper(Ptr wrapper) { Ptr ieWrapper = wrapper.dynamicCast(); CV_Assert(!ieWrapper.empty()); - InferenceEngine::DataPtr srcData = ieWrapper->dataPtr; - dataPtr = InferenceEngine::DataPtr(new InferenceEngine::Data(srcData->getName(), srcData->getTensorDesc())); + name = ieWrapper->name; blob = ieWrapper->blob; } @@ -895,6 +1033,12 @@ void NgraphBackendWrapper::setHostDirty() //CV_Error(Error::StsNotImplemented, ""); } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) +ov::Tensor copyBlob(const ov::Tensor& blob) +{ + return ov::Tensor(blob.get_element_type(), blob.get_shape()); +} +#else InferenceEngine::Blob::Ptr copyBlob(const InferenceEngine::Blob::Ptr& blob) { InferenceEngine::Blob::Ptr copy; @@ -918,88 +1062,13 @@ InferenceEngine::Blob::Ptr copyBlob(const InferenceEngine::Blob::Ptr& blob) return copy; } -InferenceEngine::DataPtr ngraphDataNode(const Ptr& ptr) -{ - CV_Assert(!ptr.empty()); - Ptr p = ptr.dynamicCast(); - CV_Assert(!p.empty()); - return p->dataPtr; -} - -static -InferenceEngine::Blob::Ptr reallocateBlob(Mat &m, const InferenceEngine::TensorDesc& description) -{ - auto dims = description.getDims(); - auto layout = estimateLayout(dims.size()); - MatShape matShape(dims.begin(), dims.end()); - if (description.getPrecision() == InferenceEngine::Precision::FP32) - { - m.create(matShape, CV_32FC1); - return InferenceEngine::make_shared_blob( - {description.getPrecision(), dims, layout}, (float*)m.data); - } - else if (description.getPrecision() == InferenceEngine::Precision::I32) - { - m.create(matShape, CV_32SC1); - return InferenceEngine::make_shared_blob( - {description.getPrecision(), dims, layout}, (int*)m.data); - } - else if (description.getPrecision() == InferenceEngine::Precision::U8) - { - m.create(matShape, CV_8UC1); - return InferenceEngine::make_shared_blob( - {description.getPrecision(), dims, layout}, (uchar*)m.data); - } - std::ostringstream msg; - msg << "Unsupported IE precision: " << description.getPrecision(); - CV_Error(Error::StsNotImplemented, msg.str()); -} - -InferenceEngine::DataPtr ngraphDataOutputNode( - const Ptr& ptr, - const InferenceEngine::TensorDesc& description, - const std::string name) -{ - CV_Assert(!ptr.empty()); - Ptr p = ptr.dynamicCast(); - CV_Assert(!p.empty()); - NgraphBackendWrapper& w = *p; - const InferenceEngine::TensorDesc& blobDesc = w.blob.get()->getTensorDesc(); - auto dims = description.getDims(); - bool reallocate = false; - if (blobDesc.getPrecision() != description.getPrecision()) - { - reallocate = true; - CV_LOG_WARNING(NULL, "Reallocate output '" << name << "' blob due to wrong precision: " << blobDesc.getPrecision() << " => " << description.getPrecision() << " ndims=" << dims.size()); - } - if (dims.size() != blobDesc.getDims().size()) - { - reallocate = true; - CV_LOG_WARNING(NULL, "Reallocate output '" << name << "' blob due to wrong dims: " << blobDesc.getDims().size() << " => " << dims.size()); - } - if (reallocate) - { - auto layout = estimateLayout(dims.size()); - w.dataPtr = InferenceEngine::DataPtr(new InferenceEngine::Data(name, - {description.getPrecision(), dims, layout})); - w.blob = reallocateBlob(*w.host, description); - } - return w.dataPtr; -} - +#endif // OpenVINO < 2022.1 void InfEngineNgraphNet::reset() { allBlobs.clear(); infRequests.clear(); isInit = false; - - outputsDesc.clear(); - for (const auto& it : cnn.getOutputsInfo()) - { - const std::string& name = it.first; - outputsDesc.insert({name, it.second->getTensorDesc()}); - } } void InfEngineNgraphNet::addBlobs(const std::vector >& ptrs) @@ -1007,7 +1076,7 @@ void InfEngineNgraphNet::addBlobs(const std::vector >& p auto wrappers = ngraphWrappers(ptrs); for (const auto& wrapper : wrappers) { - std::string name = wrapper->dataPtr->getName(); + std::string name = wrapper->name; name = name.empty() ? kDefaultInpLayerName : name; allBlobs.insert({name, wrapper->blob}); } @@ -1022,27 +1091,10 @@ void InfEngineNgraphNet::NgraphReqWrapper::makePromises(const std::vectorfutureMat = outProms[i].getArrayResult(); - outsNames[i] = outs[i]->dataPtr->getName(); + outsNames[i] = outs[i]->name; } } -Mat ngraphBlobToMat(const InferenceEngine::Blob::Ptr& blob) -{ - std::vector dims = blob->getTensorDesc().getDims(); - std::vector size(dims.begin(), dims.end()); - auto precision = blob->getTensorDesc().getPrecision(); - - int type = -1; - switch (precision) - { - case InferenceEngine::Precision::FP32: type = CV_32F; break; - case InferenceEngine::Precision::U8: type = CV_8U; break; - default: - CV_Error(Error::StsNotImplemented, "Unsupported blob precision"); - } - return Mat(size, type, (void*)blob->buffer()); -} - void InfEngineNgraphNet::forward(const std::vector >& outBlobsWrappers, bool isAsync) { CV_LOG_DEBUG(NULL, "InfEngineNgraphNet::forward(" << (isAsync ? "async" : "sync") << ")"); @@ -1070,6 +1122,25 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo } infRequests.push_back(reqWrapper); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + int i = 0; + for (const auto& it : netExec.inputs()) + { + const std::string& name = it.get_node()->get_friendly_name(); + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + reqWrapper->req.set_input_tensor(i++, isAsync ? copyBlob(blobIt->second) : blobIt->second); + } + + i = 0; + for (const auto& it : netExec.outputs()) + { + const std::string& name = it.get_node()->get_friendly_name(); + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + reqWrapper->req.set_output_tensor(i++, isAsync ? copyBlob(blobIt->second) : blobIt->second); + } +#else InferenceEngine::BlobMap inpBlobs, outBlobs; for (const auto& it : cnn.getInputsInfo()) { @@ -1087,6 +1158,53 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo } reqWrapper->req.SetInput(inpBlobs); reqWrapper->req.SetOutput(outBlobs); +#endif + +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + if (isAsync) { + bool* isReady = &reqWrapper->isReady; + auto* promises = &reqWrapper->outProms; + auto* req = &reqWrapper->req; + reqWrapper->req.set_callback([isReady, promises, req](std::exception_ptr ex) { + CV_LOG_DEBUG(NULL, "DNN(nGraph): completionCallback(" << (int)status << ")"); + + size_t processedOutputs = 0; + try + { + for (; processedOutputs < promises->size(); ++processedOutputs) + { + Mat m = infEngineBlobToMat(req->get_output_tensor(processedOutputs)); + + try + { + (*promises)[processedOutputs].setValue(m.clone()); + } + catch (...) + { + try { + (*promises)[processedOutputs].setException(std::current_exception()); + } catch(...) { + CV_LOG_ERROR(NULL, "DNN: Exception occurred during async inference exception propagation"); + } + } + } + } + catch (...) + { + std::exception_ptr e = std::current_exception(); + for (; processedOutputs < promises->size(); ++processedOutputs) + { + try { + (*promises)[processedOutputs].setException(e); + } catch(...) { + CV_LOG_ERROR(NULL, "DNN: Exception occurred during async inference exception propagation"); + } + } + } + *isReady = true; + }); + } +#else // OpenVINO >= 2022.1 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4) InferenceEngine::InferRequest infRequest = reqWrapper->req; @@ -1125,7 +1243,7 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo for (; processedOutputs < wrapper.outProms.size(); ++processedOutputs) { const std::string& name = wrapper.outsNames[processedOutputs]; - Mat m = ngraphBlobToMat(wrapper.req.GetBlob(name)); + Mat m = infEngineBlobToMat(wrapper.req.GetBlob(name)); try { @@ -1157,8 +1275,34 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo wrapper.isReady = true; } ); +#endif // OpenVINO >= 2022.1 } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + if (isAsync) + { + // Copy actual data to infer request's input blobs. + int i = 0; + for (const auto& it : cnn.getFunction()->get_parameters()) + { + const std::string& name = it->get_friendly_name(); + auto blobIt = allBlobs.find(name); + Mat srcMat = infEngineBlobToMat(blobIt->second); + Mat dstMat = infEngineBlobToMat(reqWrapper->req.get_input_tensor(i++)); + srcMat.copyTo(dstMat); + } + + // Set promises to output blobs wrappers. + reqWrapper->makePromises(outBlobsWrappers); + + reqWrapper->isReady = false; + reqWrapper->req.start_async(); + } + else + { + reqWrapper->req.infer(); + } +#else if (isAsync) { // Copy actual data to infer request's input blobs. @@ -1166,8 +1310,8 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo { const std::string& name = it.first; auto blobIt = allBlobs.find(name); - Mat srcMat = ngraphBlobToMat(blobIt->second); - Mat dstMat = ngraphBlobToMat(reqWrapper->req.GetBlob(name)); + Mat srcMat = infEngineBlobToMat(blobIt->second); + Mat dstMat = infEngineBlobToMat(reqWrapper->req.GetBlob(name)); srcMat.copyTo(dstMat); } @@ -1181,6 +1325,7 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo { reqWrapper->req.Infer(); } +#endif // OpenVINO >= 2022.1 } #endif diff --git a/modules/dnn/src/ie_ngraph.hpp b/modules/dnn/src/ie_ngraph.hpp index 9ccc182fc8..09afc7f117 100644 --- a/modules/dnn/src/ie_ngraph.hpp +++ b/modules/dnn/src/ie_ngraph.hpp @@ -68,7 +68,11 @@ public: std::unordered_map* > all_nodes; InferenceEngine::ExecutableNetwork netExec; +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + std::map allBlobs; +#else InferenceEngine::BlobMap allBlobs; +#endif std::string device_name; bool isInit = false; @@ -87,9 +91,7 @@ public: InferenceEngine::CNNNetwork cnn; bool hasNetOwner; - std::unordered_map > requestedOutputs; - - std::map outputsDesc; + std::unordered_map requestedOutputs; }; class InfEngineNgraphNode : public BackendNode @@ -123,17 +125,15 @@ public: virtual void setHostDirty() CV_OVERRIDE; Mat* host; - InferenceEngine::DataPtr dataPtr; + std::string name; +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ov::Tensor blob; +#else InferenceEngine::Blob::Ptr blob; +#endif AsyncArray futureMat; }; -InferenceEngine::DataPtr ngraphDataNode(const Ptr& ptr); -InferenceEngine::DataPtr ngraphDataOutputNode( - const Ptr& ptr, - const InferenceEngine::TensorDesc& description, - const std::string name); - // This is a fake class to run networks from Model Optimizer. Objects of that // class simulate responses of layers are imported by OpenCV and supported by // Inference Engine. The main difference is that they do not perform forward pass. diff --git a/modules/dnn/src/layers/concat_layer.cpp b/modules/dnn/src/layers/concat_layer.cpp index 1b520cf87a..52330a8e42 100644 --- a/modules/dnn/src/layers/concat_layer.cpp +++ b/modules/dnn/src/layers/concat_layer.cpp @@ -403,8 +403,7 @@ public: virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE { - InferenceEngine::DataPtr data = ngraphDataNode(inputs[0]); - const int numDims = data->getDims().size(); + const int numDims = nodes[0].dynamicCast()->node->get_shape().size(); const int cAxis = normalize_axis(axis, numDims); std::vector maxDims(numDims, 0); @@ -412,16 +411,17 @@ public: ngraph::OutputVector inp_nodes; for (int i = 0; i < nodes.size(); ++i) { - inp_nodes.push_back(nodes[i].dynamicCast()->node); + auto inp = nodes[i].dynamicCast()->node; + inp_nodes.push_back(inp); - std::vector inpShape = ngraphDataNode(inputs[i])->getDims(); + std::vector inpShape = inp->get_shape(); for (int i = 0; i < numDims; ++i) maxDims[i] = std::max(maxDims[i], inpShape[i]); } for (int i = 0; i < inp_nodes.size(); ++i) { bool needPadding = false; - std::vector inpShape = ngraphDataNode(inputs[i])->getDims(); + std::vector inpShape = inp_nodes[i].get_shape(); std::vector begins(inpShape.size(), 0), ends(inpShape.size(), 0); for (int j = 0; j < inpShape.size(); ++j) { diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index 6850cd5211..3f43c024c7 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -6,6 +6,7 @@ #include "layers_common.hpp" #include "../op_cuda.hpp" #include "../op_cann.hpp" +#include "../ie_ngraph.hpp" #include @@ -104,6 +105,12 @@ public: return op == OPERATION::ADD || op == OPERATION::PROD || op == OPERATION::DIV || op == OPERATION::DIV || op == OPERATION::MAX || op == OPERATION::MIN; #endif + if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + return (op == OPERATION::ADD || + op == OPERATION::PROD || + op == OPERATION::GREATER_EQUAL || + op == OPERATION::LESS_EQUAL + ); if (op == OPERATION::MAX || op == OPERATION::MIN || op == OPERATION::SUM || op == OPERATION::PROD || op == OPERATION::DIV) return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA; @@ -743,6 +750,37 @@ public: CV_Assert(inputs.size()); return inputs.size() * total(outputs[0]); } + +#ifdef HAVE_DNN_NGRAPH + virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE + { + CV_Assert(inputs.size() == 2); + auto& inp0 = nodes[0].dynamicCast()->node; + auto& inp1 = nodes[1].dynamicCast()->node; + + if (inp0->get_element_type() != inp1->get_element_type()) { + auto dtype = preferableTarget == DNN_TARGET_OPENCL_FP16 || preferableTarget == DNN_TARGET_MYRIAD ? + ngraph::element::f16 : ngraph::element::f32; + if (inp0->get_element_type() != dtype) + inp0 = std::make_shared(inp0, dtype); + if (inp1->get_element_type() != dtype) + inp1 = std::make_shared(inp1, dtype); + } + + std::shared_ptr node; + if (op == OPERATION::ADD) + node = std::make_shared(inp0, inp1); + else if (op == OPERATION::PROD) + node = std::make_shared(inp0, inp1); + else if (op == OPERATION::GREATER_EQUAL) + node = std::make_shared(inp0, inp1); + else if (op == OPERATION::LESS_EQUAL) + node = std::make_shared(inp0, inp1); + else + CV_Error(Error::StsNotImplemented, "Operation is not implemented for nGraph backend"); + return Ptr(new InfEngineNgraphNode(node)); + } +#endif }; Ptr NaryEltwiseLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/resize_layer.cpp b/modules/dnn/src/layers/resize_layer.cpp index 356b193ab5..4342b51b78 100644 --- a/modules/dnn/src/layers/resize_layer.cpp +++ b/modules/dnn/src/layers/resize_layer.cpp @@ -401,6 +401,24 @@ public: #else ngraph::op::v4::Interpolate::InterpolateAttrs attrs; +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + if (interpolation == "nearest") { + attrs.mode = ngraph::op::v4::Interpolate::InterpolateMode::NEAREST; + attrs.coordinate_transformation_mode = ngraph::op::v4::Interpolate::CoordinateTransformMode::HALF_PIXEL; + } else if (interpolation == "bilinear") { + attrs.mode = ngraph::op::v4::Interpolate::InterpolateMode::LINEAR_ONNX; + attrs.coordinate_transformation_mode = ngraph::op::v4::Interpolate::CoordinateTransformMode::ASYMMETRIC; + } else { + CV_Error(Error::StsNotImplemented, format("Unsupported interpolation: %s", interpolation.c_str())); + } + attrs.shape_calculation_mode = ngraph::op::v4::Interpolate::ShapeCalcMode::SIZES; + + if (alignCorners) { + attrs.coordinate_transformation_mode = ngraph::op::v4::Interpolate::CoordinateTransformMode::ALIGN_CORNERS; + } + + attrs.nearest_mode = ngraph::op::v4::Interpolate::NearestMode::ROUND_PREFER_FLOOR; +#else if (interpolation == "nearest") { attrs.mode = ngraph::op::v4::Interpolate::InterpolateMode::nearest; attrs.coordinate_transformation_mode = ngraph::op::v4::Interpolate::CoordinateTransformMode::half_pixel; @@ -417,6 +435,7 @@ public: } attrs.nearest_mode = ngraph::op::v4::Interpolate::NearestMode::round_prefer_floor; +#endif // OpenVINO >= 2022.1 std::vector shape = {outHeight, outWidth}; auto out_shape = std::make_shared(ngraph::element::i64, ngraph::Shape{2}, shape.data()); diff --git a/modules/dnn/src/net_openvino.cpp b/modules/dnn/src/net_openvino.cpp index d55c26a0de..5704cb9b64 100644 --- a/modules/dnn/src/net_openvino.cpp +++ b/modules/dnn/src/net_openvino.cpp @@ -275,19 +275,17 @@ void NetImplOpenVINO::initBackend(const std::vector& blobsToKeep_) (netInputLayer->outNames.size() == ld.outputBlobsWrappers.size())); for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) { - InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); std::string outputName = netInputLayer->outNames.empty() ? ld.name : netInputLayer->outNames[i]; outputName = ld.outputBlobsWrappers.size() > 1 ? (outputName + "." + std::to_string(i)) : outputName; - dataPtr->setName(outputName); + ld.outputBlobsWrappers[i].dynamicCast()->name = outputName; } } else { for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) { - InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); std::string outputName = ld.outputBlobsWrappers.size() > 1 ? (ld.name + "." + std::to_string(i)) : ld.name; - dataPtr->setName(outputName); + ld.outputBlobsWrappers[i].dynamicCast()->name = outputName; } } } @@ -311,26 +309,7 @@ void NetImplOpenVINO::initBackend(const std::vector& blobsToKeep_) { for (int i = 0; i < ld.inputBlobsWrappers.size(); ++i) { - InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.inputBlobsWrappers[i]); - dataPtr->setName(netInputLayer->outNames[i]); - } - } - else - { - for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) - { - auto it = ienet.outputsDesc.find(ld.name); - if (it != ienet.outputsDesc.end()) - { - const InferenceEngine::TensorDesc& descriptor = it->second; - InferenceEngine::DataPtr dataPtr = ngraphDataOutputNode(ld.outputBlobsWrappers[i], descriptor, ld.name); - dataPtr->setName(ld.name); - } - else - { - InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); - dataPtr->setName(ld.name); - } + ld.inputBlobsWrappers[i].dynamicCast()->name = netInputLayer->outNames[i]; } } ienet.addBlobs(ld.inputBlobsWrappers); @@ -456,10 +435,10 @@ void NetImplOpenVINO::initBackend(const std::vector& blobsToKeep_) dynamicCast(); CV_Assert(!inpWrapper.empty()); auto iter = std::find(inputNames.begin(), inputNames.end(), - inpWrapper->dataPtr->getName()); + inpWrapper->name); if (iter == inputNames.end()) { - inputNames.push_back(inpWrapper->dataPtr->getName()); + inputNames.push_back(inpWrapper->name); inputs.push_back(inpLd.outputBlobs[cons_inp]); } curr_pos = cons + 1; @@ -505,7 +484,12 @@ void NetImplOpenVINO::initBackend(const std::vector& blobsToKeep_) CV_LOG_DEBUG(NULL, "DNN/IE: bind output port " << lid << ":" << oid << " (" << ngraph_input_node->get_friendly_name() << ":" << ngraph_input_node->get_type_info().name << ")"); // Handle parameters from other subnets. Output port is not used in this case +#if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4) if ((ngraph::op::is_parameter(ngraph_input_node) || ngraph::op::is_constant(ngraph_input_node)) && +#else + if ((ngraph_input_node->is_parameter() || ngraph_input_node->is_constant()) && +#endif + ngraph_input_node->get_output_size() == 1) { inputNodes[i] = Ptr(new InfEngineNgraphNode(ngraph_input_node)); @@ -702,14 +686,33 @@ Net NetImplOpenVINO::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork CV_TRACE_REGION("register_inputs"); + auto ngraphFunction = ieNet.getFunction(); + CV_Assert(ngraphFunction); + std::vector inputsNames; std::vector inp_shapes; - for (auto& it : ieNet.getInputsInfo()) + for (auto& it : ngraphFunction->get_parameters()) { - inputsNames.push_back(it.first); - std::vector dims = it.second->getTensorDesc().getDims(); + inputsNames.push_back(it->get_friendly_name()); + std::vector dims = it->get_shape(); inp_shapes.push_back(std::vector(dims.begin(), dims.end())); } + // nGraph models produce output "Result" layers which have "/sink_port" suffix in their names. + // Their inputs are actual model outputs and we change friendly name to it. + // By this workaround, we produce similar outputs names comparing to ieNet.getOutputsInfo() + for (int i = 0; i < ngraphFunction->get_output_size(); ++i) { + auto res = ngraphFunction->output(i); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + const std::string& name = res.get_any_name(); +#else + auto out = res.get_node()->input(0).get_source_output(); + std::string name = out.get_node()->get_friendly_name(); + if (out.get_node()->get_output_size() > 1) + name += "." + std::to_string(out.get_index()); +#endif + if (res.get_node()->get_friendly_name() != name) + res.get_node()->set_friendly_name(name); + } Net cvNet; Ptr openvino_impl_ptr = makePtr(); @@ -736,17 +739,15 @@ Net NetImplOpenVINO::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork CV_TRACE_REGION_NEXT("register_outputs"); - auto ngraphFunction = ieNet.getFunction(); - CV_Assert(ngraphFunction); std::vector> ngraphOperations = ngraphFunction->get_ops(); - for (auto& it : ieNet.getOutputsInfo()) + for (auto& it : ngraphFunction->get_results()) { CV_TRACE_REGION("output"); - const auto& outputName = it.first; + const auto& outputName = it->get_friendly_name(); LayerParams lp; - int lid = cvNet.addLayer(it.first, "", lp); + int lid = cvNet.addLayer(outputName, "", lp); LayerData& ld = openvino_impl.layers[lid]; @@ -835,10 +836,15 @@ Net openvino_readNetwork( InferenceEngine::CNNNetwork ieNet; try { +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + ov::Tensor weights_blob(ov::element::u8, {bufferWeightsSize}, (void*)bufferWeightsPtr); + ieNet = ie.read_model(model, weights_blob); +#else InferenceEngine::TensorDesc tensorDesc(InferenceEngine::Precision::U8, { bufferWeightsSize }, InferenceEngine::Layout::C); InferenceEngine::Blob::CPtr weights_blob = InferenceEngine::make_shared_blob(tensorDesc, (uint8_t*)bufferWeightsPtr, bufferWeightsSize); ieNet = ie.ReadNetwork(model, weights_blob); +#endif } catch (const std::exception& e) { diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index e237be07cf..f9e3993d20 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -39,6 +39,86 @@ cv::String setInferenceEngineBackendType(const cv::String& newBackendType) CV__DNN_INLINE_NS_END +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) +namespace InferenceEngine { + +CNNNetwork::CNNNetwork() {} + +CNNNetwork::CNNNetwork(std::shared_ptr model) : model(model) {} + +std::shared_ptr CNNNetwork::getFunction() const { + return model; +} + +void CNNNetwork::serialize(const std::string& xmlPath, const std::string& binPath) { + ov::pass::Serialize(xmlPath, binPath).run_on_model(model); +} + +void CNNNetwork::reshape(const std::map >& shapes) { + std::map partialShapes; + for (const auto& it : shapes) { + ov::PartialShape shape; + shape.insert(shape.begin(), it.second.begin(), it.second.end()); + partialShapes.insert({it.first, shape}); + } + model->reshape(partialShapes); +} + +std::vector Core::GetAvailableDevices() { + return get_available_devices(); +} + +void Core::UnregisterPlugin(const std::string& id) { + unload_plugin(id); +} + +CNNNetwork Core::ReadNetwork(const std::string& xmlPath, const std::string& binPath) { + return read_model(xmlPath, binPath); +} + +ExecutableNetwork Core::LoadNetwork(CNNNetwork net, const std::string& device, + const std::map& config) { + ov::AnyMap props; + for (const auto& it : config) { + props.insert(it); + } + return compile_model(net.getFunction(), device, props); +} + +ExecutableNetwork::ExecutableNetwork() {} + +ExecutableNetwork::ExecutableNetwork(const ov::CompiledModel& copy) : CompiledModel(copy) {} + +ov::InferRequest ExecutableNetwork::CreateInferRequest() { return create_infer_request(); } + +} // namespace InferenceEngine + +Mat infEngineBlobToMat(const ov::Tensor& blob) +{ + std::vector dims = blob.get_shape(); + std::vector size(dims.begin(), dims.end()); + auto precision = blob.get_element_type(); + + int type = -1; + switch (precision) + { + case ov::element::f32: type = CV_32F; break; + case ov::element::u8: type = CV_8U; break; + default: + CV_Error(Error::StsNotImplemented, "Unsupported blob precision"); + } + return Mat(size, type, blob.data()); +} + +void infEngineBlobsToMats(const ov::TensorVector& blobs, + std::vector& mats) +{ + mats.resize(blobs.size()); + for (int i = 0; i < blobs.size(); ++i) + mats[i] = infEngineBlobToMat(blobs[i]); +} + +#else Mat infEngineBlobToMat(const InferenceEngine::Blob::Ptr& blob) { @@ -65,7 +145,7 @@ void infEngineBlobsToMats(const std::vector& blobs, for (int i = 0; i < blobs.size(); ++i) mats[i] = infEngineBlobToMat(blobs[i]); } - +#endif // OpenVINO >= 2022.1 static bool init_IE_plugins() { @@ -130,7 +210,11 @@ static bool detectArmPlugin_() { if (i->find("CPU") != std::string::npos) { +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + const std::string name = ie.get_property(*i, ov::device::full_name); +#else const std::string name = ie.GetMetric(*i, METRIC_KEY(FULL_DEVICE_NAME)).as(); +#endif CV_LOG_INFO(NULL, "CPU plugin: " << name); return name.find("arm_compute::NEON") != std::string::npos; } @@ -150,7 +234,11 @@ static bool detectMyriadX_(const std::string& device) { if (i->find(device) != std::string::npos) { +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) + const std::string name = ie.get_property(*i, ov::device::full_name); +#else const std::string name = ie.GetMetric(*i, METRIC_KEY(FULL_DEVICE_NAME)).as(); +#endif CV_LOG_INFO(NULL, "Myriad device: " << name); return name.find("MyriadX") != std::string::npos || name.find("Myriad X") != std::string::npos || name.find("HDDL") != std::string::npos; } diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index 6ac4d955cc..45913d3b31 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -19,11 +19,6 @@ #ifdef HAVE_INF_ENGINE -#define INF_ENGINE_RELEASE_2018R5 2018050000 -#define INF_ENGINE_RELEASE_2019R1 2019010000 -#define INF_ENGINE_RELEASE_2019R2 2019020000 -#define INF_ENGINE_RELEASE_2019R3 2019030000 -#define INF_ENGINE_RELEASE_2020_1 2020010000 #define INF_ENGINE_RELEASE_2020_2 2020020000 #define INF_ENGINE_RELEASE_2020_3 2020030000 #define INF_ENGINE_RELEASE_2020_4 2020040000 @@ -31,6 +26,7 @@ #define INF_ENGINE_RELEASE_2021_2 2021020000 #define INF_ENGINE_RELEASE_2021_3 2021030000 #define INF_ENGINE_RELEASE_2021_4 2021040000 +#define INF_ENGINE_RELEASE_2022_1 2022010000 #ifndef INF_ENGINE_RELEASE #warning("IE version have not been provided via command-line. Using 2021.4 by default") @@ -48,7 +44,13 @@ #pragma GCC diagnostic ignored "-Wsuggest-override" #endif +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) +#include +#include +#include +#else #include +#endif #if defined(__GNUC__) && __GNUC__ >= 5 //#pragma GCC diagnostic pop @@ -73,11 +75,17 @@ CV__DNN_INLINE_NS_END Backend& getInferenceEngineBackendTypeParam(); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) +Mat infEngineBlobToMat(const ov::Tensor& blob); + +void infEngineBlobsToMats(const ov::TensorVector& blobs, + std::vector& mats); +#else Mat infEngineBlobToMat(const InferenceEngine::Blob::Ptr& blob); void infEngineBlobsToMats(const std::vector& blobs, std::vector& mats); - +#endif // OpenVINO >= 2022.1 CV__DNN_INLINE_NS_BEGIN @@ -90,6 +98,52 @@ bool isArmComputePlugin(); CV__DNN_INLINE_NS_END +// A series of wrappers for classes from OpenVINO API 2.0. +// Need just for less conditional compilation inserts. +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2022_1) +namespace InferenceEngine { + +class CNNNetwork { +public: + CNNNetwork(); + + CNNNetwork(std::shared_ptr model); + + std::shared_ptr getFunction() const; + + void serialize(const std::string& xmlPath, const std::string& binPath); + + void reshape(const std::map >& shapes); + +private: + std::shared_ptr model = nullptr; +}; + +typedef ov::InferRequest InferRequest; + +class ExecutableNetwork : public ov::CompiledModel { +public: + ExecutableNetwork(); + + ExecutableNetwork(const ov::CompiledModel& copy); + + ov::InferRequest CreateInferRequest(); +}; + +class Core : public ov::Core { +public: + std::vector GetAvailableDevices(); + + void UnregisterPlugin(const std::string& id); + + CNNNetwork ReadNetwork(const std::string& xmlPath, const std::string& binPath); + + ExecutableNetwork LoadNetwork(CNNNetwork net, const std::string& device, + const std::map& config); +}; + +} +#endif // OpenVINO >= 2022.1 InferenceEngine::Core& getCore(const std::string& id); diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 9b8765b92d..7aa9c756fb 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -531,7 +531,7 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) { l1 = 0.4; - lInf = 7.45; + lInf = 7.46; } else if (target == DNN_TARGET_CUDA_FP16) { diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index 4043bd118e..8059fc6888 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -725,18 +725,21 @@ TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); #endif + double scoreDiff = 0.0; #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) // Check 'backward_compatible_check || in_out_elements_equal' failed at core/src/op/reshape.cpp:427: // While validating node 'v1::Reshape bbox_pred_reshape (bbox_pred[0]:f32{1,84}, Constant_265242[0]:i64{4}) -> (f32{?,?,?,?})' with friendly_name 'bbox_pred_reshape': // Requested output shape {1,6300,4,1} is incompatible with input shape {1, 84} if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH, CV_TEST_TAG_DNN_SKIP_IE_VERSION); + if (target == DNN_TARGET_OPENCL_FP16) + scoreDiff = 0.02; #endif static Mat ref = (Mat_(3, 7) << 0, 2, 0.949398, 99.2454, 210.141, 601.205, 462.849, 0, 7, 0.997022, 481.841, 92.3218, 722.685, 175.953, 0, 12, 0.993028, 133.221, 189.377, 350.994, 563.166); - testFaster("faster_rcnn_vgg16.prototxt", "VGG16_faster_rcnn_final.caffemodel", ref); + testFaster("faster_rcnn_vgg16.prototxt", "VGG16_faster_rcnn_final.caffemodel", ref, scoreDiff); } TEST_P(Test_Caffe_nets, FasterRCNN_zf) diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index 0b4c1bccff..2160c81fad 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -638,6 +638,11 @@ TEST_P(Test_Darknet_nets, YOLOv3) double scoreDiff = 8e-5, iouDiff = 3e-4; if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) { +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2022010000) + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + scoreDiff = 0.009; + else +#endif scoreDiff = 0.006; iouDiff = 0.042; } @@ -771,6 +776,7 @@ TEST_P(Test_Darknet_nets, YOLOv4) // accuracy (batch 2) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16) { + scoreDiff = 0.008f; iouDiff = 0.05f; } // accuracy diff --git a/modules/dnn/test/test_ie_models.cpp b/modules/dnn/test/test_ie_models.cpp index 3622f69bdb..135caa9064 100644 --- a/modules/dnn/test/test_ie_models.cpp +++ b/modules/dnn/test/test_ie_models.cpp @@ -438,6 +438,9 @@ TEST_P(DNNTestOpenVINO, models) { auto dstIt = cvOutputsMap.find(srcIt.first); CV_Assert(dstIt != cvOutputsMap.end()); + + dstIt->second.convertTo(dstIt->second, srcIt.second.type()); + double normInf = cvtest::norm(srcIt.second, dstIt->second, cv::NORM_INF); EXPECT_LE(normInf, eps) << "output=" << srcIt.first; } diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index a54c2fecb1..6afb3a8aa0 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1292,7 +1292,7 @@ TEST_P(Layer_Test_Convolution_DLDT, Accuracy) if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) ASSERT_EQ(net.getLayer(outLayers[0])->type, "Convolution"); else - ASSERT_EQ(net.getLayer(outLayers[0])->type, "Add"); + ASSERT_EQ(net.getLayer(outLayers[0])->type, "Result"); } TEST_P(Layer_Test_Convolution_DLDT, setInput_uint8) diff --git a/modules/dnn/test/test_model.cpp b/modules/dnn/test/test_model.cpp index d7fc2af73d..d3217a0e49 100644 --- a/modules/dnn/test/test_model.cpp +++ b/modules/dnn/test/test_model.cpp @@ -447,6 +447,10 @@ TEST_P(Test_Model, DetectionOutput) { if (backend == DNN_BACKEND_OPENCV) scoreDiff = 4e-3; +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2022010000) + else if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + scoreDiff = 4e-2; +#endif else scoreDiff = 2e-2; iouDiff = 1.8e-1; From 8e6aae0d7a533c2ebca737a381d85a8498e01ad9 Mon Sep 17 00:00:00 2001 From: zihaomu Date: Sat, 24 Dec 2022 15:26:42 +0800 Subject: [PATCH 296/313] Add spaces to make links clickable. --- modules/video/include/opencv2/video/tracking.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 20cfb77bab..eb5a6c7030 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -853,9 +853,9 @@ public: * * Nano tracker is much faster and extremely lightweight due to special model structure, the whole model size is about 1.9 MB. * Nano tracker needs two models: one for feature extraction (backbone) and the another for localization (neckhead). - * Please download these two onnx models at:https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack/models/nanotrackv2. + * Model download link: https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack/models/nanotrackv2 * Original repo is here: https://github.com/HonglinChu/NanoTrack - * Author:HongLinChu, 1628464345@qq.com + * Author: HongLinChu, 1628464345@qq.com */ class CV_EXPORTS_W TrackerNano : public Tracker { From a494c75bfebc869c48be9f91eb2150c056ecdc5a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 15 Dec 2022 21:57:41 +0000 Subject: [PATCH 297/313] pre: OpenCV 3.4.19 (version++) --- .../cross_referencing/tutorial_cross_referencing.markdown | 4 ++-- modules/core/include/opencv2/core/version.hpp | 4 ++-- modules/dnn/include/opencv2/dnn/dnn.hpp | 4 ++-- modules/python/package/setup.py | 2 +- platforms/android/build_sdk.py | 2 +- platforms/android/service/readme.txt | 2 +- platforms/maven/opencv-it/pom.xml | 2 +- platforms/maven/opencv/pom.xml | 2 +- platforms/maven/pom.xml | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown index 740f455c2b..daa7c396cd 100644 --- a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown +++ b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown @@ -39,14 +39,14 @@ Open your Doxyfile using your favorite text editor and search for the key `TAGFILES`. Change it as follows: @code -TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.18 +TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.19 @endcode If you had other definitions already, you can append the line using a `\`: @code TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \ - ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.18 + ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.19 @endcode Doxygen can now use the information from the tag file to link to the OpenCV diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 9bfa15e538..aa1e8f6a83 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -7,8 +7,8 @@ #define CV_VERSION_MAJOR 3 #define CV_VERSION_MINOR 4 -#define CV_VERSION_REVISION 18 -#define CV_VERSION_STATUS "-dev" +#define CV_VERSION_REVISION 19 +#define CV_VERSION_STATUS "-pre" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 2368313c64..f118a0f428 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -47,9 +47,9 @@ #include "opencv2/core/async.hpp" #if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_EXPERIMENTAL_NS -#define CV__DNN_EXPERIMENTAL_NS_BEGIN namespace experimental_dnn_34_v25 { +#define CV__DNN_EXPERIMENTAL_NS_BEGIN namespace experimental_dnn_34_v26 { #define CV__DNN_EXPERIMENTAL_NS_END } -namespace cv { namespace dnn { namespace experimental_dnn_34_v25 { } using namespace experimental_dnn_34_v25; }} +namespace cv { namespace dnn { namespace experimental_dnn_34_v26 { } using namespace experimental_dnn_34_v26; }} #else #define CV__DNN_EXPERIMENTAL_NS_BEGIN #define CV__DNN_EXPERIMENTAL_NS_END diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index ca8fe9138f..1356931720 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -9,7 +9,7 @@ def main(): os.chdir(SCRIPT_DIR) package_name = 'opencv' - package_version = os.environ.get('OPENCV_VERSION', '3.4.18') # TODO + package_version = os.environ.get('OPENCV_VERSION', '3.4.19') # TODO long_description = 'Open Source Computer Vision Library Python bindings' # TODO diff --git a/platforms/android/build_sdk.py b/platforms/android/build_sdk.py index 7974909f05..3a9618b835 100755 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@ -269,7 +269,7 @@ class Builder: # Add extra data apkxmldest = check_dir(os.path.join(apkdest, "res", "xml"), create=True) apklibdest = check_dir(os.path.join(apkdest, "libs", abi.name), create=True) - for ver, d in self.extra_packs + [("3.4.18", os.path.join(self.libdest, "lib"))]: + for ver, d in self.extra_packs + [("3.4.19", os.path.join(self.libdest, "lib"))]: r = ET.Element("library", attrib={"version": ver}) log.info("Adding libraries from %s", d) diff --git a/platforms/android/service/readme.txt b/platforms/android/service/readme.txt index 7be50dd205..11e9f0bae8 100644 --- a/platforms/android/service/readme.txt +++ b/platforms/android/service/readme.txt @@ -12,7 +12,7 @@ manually using adb tool: adb install /apk/OpenCV__Manager__.apk -Example: OpenCV_3.4.18-dev_Manager_3.49_armeabi-v7a.apk +Example: OpenCV_3.4.19-dev_Manager_3.49_armeabi-v7a.apk Use the list of platforms below to determine proper OpenCV Manager package for your device: diff --git a/platforms/maven/opencv-it/pom.xml b/platforms/maven/opencv-it/pom.xml index a344b91e90..befb75fed5 100644 --- a/platforms/maven/opencv-it/pom.xml +++ b/platforms/maven/opencv-it/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 3.4.18 + 3.4.19 org.opencv opencv-it diff --git a/platforms/maven/opencv/pom.xml b/platforms/maven/opencv/pom.xml index feb3e97aaa..0ff0c6fe2f 100644 --- a/platforms/maven/opencv/pom.xml +++ b/platforms/maven/opencv/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 3.4.18 + 3.4.19 org.opencv opencv diff --git a/platforms/maven/pom.xml b/platforms/maven/pom.xml index e5bf9264d3..19ba7d07b3 100644 --- a/platforms/maven/pom.xml +++ b/platforms/maven/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.opencv opencv-parent - 3.4.18 + 3.4.19 pom OpenCV Parent POM From b42c11de826340ffecf826613837f29bcbedfc04 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 15 Dec 2022 22:08:46 +0000 Subject: [PATCH 298/313] pre: OpenCV 4.7.0 (version++) --- .../cross_referencing/tutorial_cross_referencing.markdown | 4 ++-- modules/core/include/opencv2/core/version.hpp | 4 ++-- modules/dnn/include/opencv2/dnn/version.hpp | 2 +- modules/python/package/setup.py | 2 +- platforms/maven/opencv-it/pom.xml | 2 +- platforms/maven/opencv/pom.xml | 2 +- platforms/maven/pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown index ccef42a482..b77891cee9 100644 --- a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown +++ b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown @@ -46,14 +46,14 @@ Open your Doxyfile using your favorite text editor and search for the key `TAGFILES`. Change it as follows: @code -TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.6.0 +TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.7.0 @endcode If you had other definitions already, you can append the line using a `\`: @code TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \ - ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.6.0 + ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.7.0 @endcode Doxygen can now use the information from the tag file to link to the OpenCV diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 0ae71a4448..780691e1a3 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -6,9 +6,9 @@ #define OPENCV_VERSION_HPP #define CV_VERSION_MAJOR 4 -#define CV_VERSION_MINOR 6 +#define CV_VERSION_MINOR 7 #define CV_VERSION_REVISION 0 -#define CV_VERSION_STATUS "-dev" +#define CV_VERSION_STATUS "-pre" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) diff --git a/modules/dnn/include/opencv2/dnn/version.hpp b/modules/dnn/include/opencv2/dnn/version.hpp index 630cccf758..93b5ff3158 100644 --- a/modules/dnn/include/opencv2/dnn/version.hpp +++ b/modules/dnn/include/opencv2/dnn/version.hpp @@ -6,7 +6,7 @@ #define OPENCV_DNN_VERSION_HPP /// Use with major OpenCV version only. -#define OPENCV_DNN_API_VERSION 20220524 +#define OPENCV_DNN_API_VERSION 20221220 #if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS #define CV__DNN_INLINE_NS __CV_CAT(dnn4_v, OPENCV_DNN_API_VERSION) diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index 9736e78db8..13fb4e7b14 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -9,7 +9,7 @@ def main(): os.chdir(SCRIPT_DIR) package_name = 'opencv' - package_version = os.environ.get('OPENCV_VERSION', '4.6.0') # TODO + package_version = os.environ.get('OPENCV_VERSION', '4.7.0') # TODO long_description = 'Open Source Computer Vision Library Python bindings' # TODO diff --git a/platforms/maven/opencv-it/pom.xml b/platforms/maven/opencv-it/pom.xml index 1e66e6979a..f4699df1bf 100644 --- a/platforms/maven/opencv-it/pom.xml +++ b/platforms/maven/opencv-it/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 4.6.0 + 4.7.0 org.opencv opencv-it diff --git a/platforms/maven/opencv/pom.xml b/platforms/maven/opencv/pom.xml index c135d4b75f..bfbf2eef54 100644 --- a/platforms/maven/opencv/pom.xml +++ b/platforms/maven/opencv/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 4.6.0 + 4.7.0 org.opencv opencv diff --git a/platforms/maven/pom.xml b/platforms/maven/pom.xml index de42826a07..73d5302c22 100644 --- a/platforms/maven/pom.xml +++ b/platforms/maven/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.opencv opencv-parent - 4.6.0 + 4.7.0 pom OpenCV Parent POM From b361209d527171b8bc160d54f42989b92536bcdb Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 25 Dec 2022 18:55:19 +0000 Subject: [PATCH 299/313] ffmpeg/4.x: update FFmpeg wrapper 2022.12 - FFmpeg 4.4.3 --- 3rdparty/ffmpeg/ffmpeg.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/3rdparty/ffmpeg/ffmpeg.cmake b/3rdparty/ffmpeg/ffmpeg.cmake index dabab5911f..f3ad63770a 100644 --- a/3rdparty/ffmpeg/ffmpeg.cmake +++ b/3rdparty/ffmpeg/ffmpeg.cmake @@ -1,8 +1,8 @@ -# Binaries branch name: ffmpeg/4.x_20220912 -# Binaries were created for OpenCV: 4154bd06677c30475e2545cbee05906dd9a367cb -ocv_update(FFMPEG_BINARIES_COMMIT "524023e38e27649d4f5ce97d57ceb8864c838fb6") -ocv_update(FFMPEG_FILE_HASH_BIN32 "88f87420899e07151b682a76e30d3e01") -ocv_update(FFMPEG_FILE_HASH_BIN64 "81b1e1e9fd2a10f4ec7b239c743240fe") +# Binaries branch name: ffmpeg/4.x_20221225 +# Binaries were created for OpenCV: 4abe6dc48d4ec6229f332cc6cf6c7e234ac8027e +ocv_update(FFMPEG_BINARIES_COMMIT "7dd0d4f1d6fe75f05f3d3b5e38cbc96c1a2d2809") +ocv_update(FFMPEG_FILE_HASH_BIN32 "e598ae2ece1ddf310bc49b58202fd87a") +ocv_update(FFMPEG_FILE_HASH_BIN64 "b2a40c142c20aef9fd663fc8f85c2971") ocv_update(FFMPEG_FILE_HASH_CMAKE "8862c87496e2e8c375965e1277dee1c7") function(download_win_ffmpeg script_var) From dbd4a0e5e685a415095cb899a6d1f10279c7fbc1 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 26 Dec 2022 02:32:11 +0000 Subject: [PATCH 300/313] videoio(ffmpeg): update tests with new Windows wrapper --- modules/videoio/test/test_ffmpeg.cpp | 31 -------------------------- modules/videoio/test/test_video_io.cpp | 4 ---- 2 files changed, 35 deletions(-) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index dcd5b86517..906b215ab4 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -392,27 +392,6 @@ typedef std::vector cap_properties_t; typedef std::pair ffmpeg_cap_properties_param_t; typedef testing::TestWithParam ffmpeg_cap_properties; -#ifdef _WIN32 -namespace { -::testing::AssertionResult IsOneOf(double value, double expected1, double expected2) -{ - // internal floating point class is used to perform accurate floating point types comparison - typedef ::testing::internal::FloatingPoint FloatingPoint; - - FloatingPoint val(value); - if (val.AlmostEquals(FloatingPoint(expected1)) || val.AlmostEquals(FloatingPoint(expected2))) - { - return ::testing::AssertionSuccess(); - } - else - { - return ::testing::AssertionFailure() - << value << " is neither equal to " << expected1 << " nor " << expected2; - } -} -} -#endif - TEST_P(ffmpeg_cap_properties, can_read_property) { if (!videoio_registry::hasBackend(CAP_FFMPEG)) @@ -429,13 +408,8 @@ TEST_P(ffmpeg_cap_properties, can_read_property) { const cap_property_t& prop = properties[i]; const double actualValue = cap.get(static_cast(prop.first)); - #ifndef _WIN32 EXPECT_DOUBLE_EQ(actualValue, prop.second) << "Property " << static_cast(prop.first) << " has wrong value"; - #else - EXPECT_TRUE(IsOneOf(actualValue, prop.second, 0.0)) - << "Property " << static_cast(prop.first) << " has wrong value"; - #endif } } @@ -588,11 +562,6 @@ TEST_P(videoio_ffmpeg_16bit, basic) const double time_sec = 1; const int numFrames = static_cast(fps * time_sec); -#ifdef _WIN32 // TODO: FFmpeg wrapper update - if (isSupported) - throw SkipTestException("FFmpeg wrapper update is required"); -#endif - { VideoWriter writer; writer.open(filename, CAP_FFMPEG, fourcc, fps, sz, diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 63f48fbf9b..595e1557ef 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -228,13 +228,9 @@ public: EXPECT_EQ(frame_count, 125); Mat img; -#ifdef _WIN32 // handle old FFmpeg wrapper on Windows till rebuild - frame_count = 10; -#else // HACK: FFmpeg reports picture_pts = AV_NOPTS_VALUE_ for the last frame for AVI container by some reason if ((ext == "avi") && (apiPref == CAP_FFMPEG)) frame_count--; -#endif for (int i = 0; i < frame_count; i++) { From b7292bc8990cef10b424c96defd6160965a78f88 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 26 Dec 2022 12:07:00 +0300 Subject: [PATCH 301/313] Fixed blob detector parameters range. --- modules/features2d/src/blobdetector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/features2d/src/blobdetector.cpp b/modules/features2d/src/blobdetector.cpp index 396521f72b..7cd49d8193 100644 --- a/modules/features2d/src/blobdetector.cpp +++ b/modules/features2d/src/blobdetector.cpp @@ -83,8 +83,8 @@ public: if (p.thresholdStep <= 0) CV_Error(Error::StsBadArg, "thresholdStep>0"); - if (p.minThreshold > p.maxThreshold || p.minThreshold <= 0) - CV_Error(Error::StsBadArg, "0 p.maxThreshold || p.minThreshold < 0) + CV_Error(Error::StsBadArg, "0<=minThreshold<=maxThreshold"); if (p.minDistBetweenBlobs <=0 ) CV_Error(Error::StsBadArg, "minDistBetweenBlobs>0"); From 692d6168b3c87eaedc246b3fc75e51ac11474e32 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Mon, 26 Dec 2022 09:09:25 +0200 Subject: [PATCH 302/313] cuda: fix CUDA 12.0 build errors --- modules/core/include/opencv2/core/cuda/common.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/cuda/common.hpp b/modules/core/include/opencv2/core/cuda/common.hpp index 348bdf1823..134809678d 100644 --- a/modules/core/include/opencv2/core/cuda/common.hpp +++ b/modules/core/include/opencv2/core/cuda/common.hpp @@ -99,7 +99,6 @@ namespace cv { namespace cuda } #if (CUDART_VERSION >= 12000) - template inline void bindTexture(const textureReference* tex, const PtrStepSz& img) { CV_Error(cv::Error::GpuNotSupported, "Function removed in CUDA SDK 12"); } template inline void createTextureObjectPitch2D(cudaTextureObject_t* tex, PtrStepSz& img, const cudaTextureDesc& texDesc) { CV_Error(cv::Error::GpuNotSupported, "Function removed in CUDA SDK 12"); } #else @@ -123,8 +122,8 @@ namespace cv { namespace cuda cudaSafeCall( cudaCreateTextureObject(tex, &resDesc, &texDesc, NULL) ); } - } #endif + } }} //! @endcond From 1bc3077890dc68a463d1aa59450ffb505c60363b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 26 Dec 2022 17:41:53 +0000 Subject: [PATCH 303/313] cmake: VERSION_GREATER_EQUAL is not supported in CMake 3.5.1 --- cmake/OpenCVDetectCUDA.cmake | 2 +- modules/world/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 74b550e669..a3d987a2b8 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -427,7 +427,7 @@ if(CUDA_FOUND) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fno-finite-math-only) endif() - if(CUDA_VERSION VERSION_GREATER_EQUAL 11.2 AND WIN32) + if(WIN32 AND NOT (CUDA_VERSION VERSION_LESS "11.2")) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388) endif() diff --git a/modules/world/CMakeLists.txt b/modules/world/CMakeLists.txt index 2f4b1a4eb1..b14378599e 100644 --- a/modules/world/CMakeLists.txt +++ b/modules/world/CMakeLists.txt @@ -59,7 +59,7 @@ ocv_module_include_directories() #message(STATUS "${OPENCV_MODULE_${the_module}_SOURCES}") ocv_create_module(${link_deps}) -if(";${OPENCV_MODULES_BUILD};" MATCHES ";opencv_viz;" AND OPENCV_MODULE_opencv_viz_IS_PART_OF_WORLD AND VTK_VERSION VERSION_GREATER_EQUAL "8.90.0") +if(";${OPENCV_MODULES_BUILD};" MATCHES ";opencv_viz;" AND OPENCV_MODULE_opencv_viz_IS_PART_OF_WORLD AND NOT (VTK_VERSION VERSION_LESS "8.90.0")) vtk_module_autoinit(TARGETS opencv_world MODULES ${VTK_LIBRARIES}) endif() From 93aa94e71ef29cad2622be394ecb4c0233ab57eb Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Sat, 24 Dec 2022 04:08:43 +0100 Subject: [PATCH 304/313] backported changes no lambda whitespace fixing build Java tests --- .../calib3d/misc/java/test/Calib3dTest.java | 8 +- modules/calib3d/src/fisheye.cpp | 4 +- modules/calib3d/test/test_fisheye.cpp | 74 ++++++++++++------- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/modules/calib3d/misc/java/test/Calib3dTest.java b/modules/calib3d/misc/java/test/Calib3dTest.java index ae5377efc5..7d89cc9bd1 100644 --- a/modules/calib3d/misc/java/test/Calib3dTest.java +++ b/modules/calib3d/misc/java/test/Calib3dTest.java @@ -699,10 +699,10 @@ public class Calib3dTest extends OpenCVTestCase { D.put(2,0,-0.021509225493198905); D.put(3,0,0.0043378096628297145); - K_new_truth.put(0,0, 387.4809086880343); - K_new_truth.put(0,2, 1036.669802754649); - K_new_truth.put(1,1, 373.6375700303157); - K_new_truth.put(1,2, 538.8373261247601); + K_new_truth.put(0,0, 387.5118215642316); + K_new_truth.put(0,2, 1033.936556777084); + K_new_truth.put(1,1, 373.6673784974842); + K_new_truth.put(1,2, 538.794152656429); Calib3d.fisheye_estimateNewCameraMatrixForUndistortRectify(K,D,new Size(1920,1080), new Mat().eye(3, 3, CvType.CV_64F), K_new, 0.0, new Size(1920,1080)); diff --git a/modules/calib3d/src/fisheye.cpp b/modules/calib3d/src/fisheye.cpp index 56fd82114d..a562bd2bb2 100644 --- a/modules/calib3d/src/fisheye.cpp +++ b/modules/calib3d/src/fisheye.cpp @@ -388,7 +388,7 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted if (theta_d > 1e-8) { - // compensate distortion iteratively + // compensate distortion iteratively using Newton method double theta = theta_d; const double EPS = 1e-8; // or std::numeric_limits::epsilon(); @@ -572,7 +572,7 @@ void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, Input : K.getMat().at(0,0)/K.getMat().at(1,1); // convert to identity ratio - cn[0] *= aspect_ratio; + cn[1] *= aspect_ratio; for(size_t i = 0; i < points.total(); ++i) pptr[i][1] *= aspect_ratio; diff --git a/modules/calib3d/test/test_fisheye.cpp b/modules/calib3d/test/test_fisheye.cpp index 58a79dcc88..2e12cd8985 100644 --- a/modules/calib3d/test/test_fisheye.cpp +++ b/modules/calib3d/test/test_fisheye.cpp @@ -101,6 +101,15 @@ TEST_F(fisheyeTest, projectPoints) EXPECT_MAT_NEAR(distorted0, distorted2, 1e-10); } +// we use it to reduce patch size for images in testdata +static void throwAwayHalf(Mat img) +{ + int whalf = img.cols / 2, hhalf = img.rows / 2; + Rect tl(0, 0, whalf, hhalf), br(whalf, hhalf, whalf, hhalf); + img(tl) = 0; + img(br) = 0; +}; + TEST_F(fisheyeTest, undistortImage) { cv::Matx33d theK = this->K; @@ -112,32 +121,41 @@ TEST_F(fisheyeTest, undistortImage) newK(0, 0) = 100; newK(1, 1) = 100; cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); - cv::Mat correct = cv::imread(combine(datasets_repository_path, "new_f_100.png")); - if (correct.empty()) - CV_Assert(cv::imwrite(combine(datasets_repository_path, "new_f_100.png"), undistorted)); - else - EXPECT_MAT_NEAR(correct, undistorted, 1e-10); + std::string imageFilename = combine(datasets_repository_path, "new_f_100.png"); + cv::Mat correct = cv::imread(imageFilename); + ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; + + throwAwayHalf(correct); + throwAwayHalf(undistorted); + + EXPECT_MAT_NEAR(correct, undistorted, 1e-10); } { double balance = 1.0; cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance); cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); - cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_1.0.png")); - if (correct.empty()) - CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_1.0.png"), undistorted)); - else - EXPECT_MAT_NEAR(correct, undistorted, 1e-10); + std::string imageFilename = combine(datasets_repository_path, "balance_1.0.png"); + cv::Mat correct = cv::imread(imageFilename); + ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; + + throwAwayHalf(correct); + throwAwayHalf(undistorted); + + EXPECT_MAT_NEAR(correct, undistorted, 1e-10); } { double balance = 0.0; cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance); cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); - cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_0.0.png")); - if (correct.empty()) - CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_0.0.png"), undistorted)); - else - EXPECT_MAT_NEAR(correct, undistorted, 1e-10); + std::string imageFilename = combine(datasets_repository_path, "balance_0.0.png"); + cv::Mat correct = cv::imread(imageFilename); + ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; + + throwAwayHalf(correct); + throwAwayHalf(undistorted); + + EXPECT_MAT_NEAR(correct, undistorted, 1e-10); } } @@ -422,19 +440,19 @@ TEST_F(fisheyeTest, stereoRectify) 0.002076471801477729, 0.006463478587068991, 0.9999769555891836 ); cv::Matx34d P1_ref( - 420.8551870450913, 0, 586.501617798451, 0, - 0, 420.8551870450913, 374.7667511986098, 0, + 420.9684016542647, 0, 586.3059567784627, 0, + 0, 420.9684016542647, 374.8571836462291, 0, 0, 0, 1, 0 ); cv::Matx34d P2_ref( - 420.8551870450913, 0, 586.501617798451, -41.77758076597302, - 0, 420.8551870450913, 374.7667511986098, 0, + 420.9684016542647, 0, 586.3059567784627, -41.78881938824554, + 0, 420.9684016542647, 374.8571836462291, 0, 0, 0, 1, 0 ); cv::Matx44d Q_ref( - 1, 0, 0, -586.501617798451, - 0, 1, 0, -374.7667511986098, - 0, 0, 0, 420.8551870450913, + 1, 0, 0, -586.3059567784627, + 0, 1, 0, -374.8571836462291, + 0, 0, 0, 420.9684016542647, 0, 0, 10.07370889670733, -0 ); @@ -489,7 +507,9 @@ TEST_F(fisheyeTest, stereoRectify) cv::Mat rectification; merge4(l, r, lundist, rundist, rectification); - cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification); + // Add the "--test_debug" to arguments for file output + if (cvtest::debugLevel > 0) + cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification); } } @@ -683,13 +703,13 @@ TEST_F(fisheyeTest, estimateNewCameraMatrixForUndistortRectify) cv::Mat K_new_truth(3, 3, cv::DataType::type); - K_new_truth.at(0, 0) = 387.4809086880343; + K_new_truth.at(0, 0) = 387.5118215642316; K_new_truth.at(0, 1) = 0.0; - K_new_truth.at(0, 2) = 1036.669802754649; + K_new_truth.at(0, 2) = 1033.936556777084; K_new_truth.at(1, 0) = 0.0; - K_new_truth.at(1, 1) = 373.6375700303157; - K_new_truth.at(1, 2) = 538.8373261247601; + K_new_truth.at(1, 1) = 373.6673784974842; + K_new_truth.at(1, 2) = 538.794152656429; K_new_truth.at(2, 0) = 0.0; K_new_truth.at(2, 1) = 0.0; From 83391ac59d270f2148fc99a62ae279b04d37f5d0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 27 Dec 2022 03:50:12 +0000 Subject: [PATCH 305/313] release: OpenCV 3.4.19 --- modules/core/include/opencv2/core/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index aa1e8f6a83..5d75d42c61 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -8,7 +8,7 @@ #define CV_VERSION_MAJOR 3 #define CV_VERSION_MINOR 4 #define CV_VERSION_REVISION 19 -#define CV_VERSION_STATUS "-pre" +#define CV_VERSION_STATUS "" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) From 253429d3f199ef5134e1249a370f4816b94f1f3c Mon Sep 17 00:00:00 2001 From: AlejandroSilvestri Date: Tue, 27 Dec 2022 09:39:56 -0300 Subject: [PATCH 306/313] Add missing ORB.setFastThreshold(int) method ORB constructor is failing to set the very important protected parameter fastThreshold, so binding this method is necessary. --- platforms/js/opencv_js.config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/js/opencv_js.config.py b/platforms/js/opencv_js.config.py index a7f3e96063..23f26a50d0 100644 --- a/platforms/js/opencv_js.config.py +++ b/platforms/js/opencv_js.config.py @@ -139,7 +139,7 @@ dnn = {'dnn_Net': ['setInput', 'forward', 'setPreferableBackend'], features2d = {'Feature2D': ['detect', 'compute', 'detectAndCompute', 'descriptorSize', 'descriptorType', 'defaultNorm', 'empty', 'getDefaultName'], 'BRISK': ['create', 'getDefaultName'], - 'ORB': ['create', 'setMaxFeatures', 'setScaleFactor', 'setNLevels', 'setEdgeThreshold', 'setFirstLevel', 'setWTA_K', 'setScoreType', 'setPatchSize', 'getFastThreshold', 'getDefaultName'], + 'ORB': ['create', 'setMaxFeatures', 'setScaleFactor', 'setNLevels', 'setEdgeThreshold', 'setFastThreshold', 'setFirstLevel', 'setWTA_K', 'setScoreType', 'setPatchSize', 'getFastThreshold', 'getDefaultName'], 'MSER': ['create', 'detectRegions', 'setDelta', 'getDelta', 'setMinArea', 'getMinArea', 'setMaxArea', 'getMaxArea', 'setPass2Only', 'getPass2Only', 'getDefaultName'], 'FastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'], 'AgastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'], From 9a2a34f94e55485fd9a267dadaab2028be2cf10c Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 28 Dec 2022 06:55:00 +0000 Subject: [PATCH 307/313] dnn(openvino): remove undefined status --- modules/dnn/src/ie_ngraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/src/ie_ngraph.cpp b/modules/dnn/src/ie_ngraph.cpp index 9a80c5e328..a49976de74 100644 --- a/modules/dnn/src/ie_ngraph.cpp +++ b/modules/dnn/src/ie_ngraph.cpp @@ -1166,7 +1166,7 @@ void InfEngineNgraphNet::forward(const std::vector >& outBlo auto* promises = &reqWrapper->outProms; auto* req = &reqWrapper->req; reqWrapper->req.set_callback([isReady, promises, req](std::exception_ptr ex) { - CV_LOG_DEBUG(NULL, "DNN(nGraph): completionCallback(" << (int)status << ")"); + CV_LOG_DEBUG(NULL, "DNN(nGraph): completionCallback()"); size_t processedOutputs = 0; try From 71765858dcef42301006daca9f3fc672dfbdce94 Mon Sep 17 00:00:00 2001 From: zihaomu Date: Wed, 28 Dec 2022 17:16:11 +0800 Subject: [PATCH 308/313] fix invalid memory access --- .../dnn/src/layers/fast_convolution/fast_convolution.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp index 02d7121f5e..1cde7b324f 100644 --- a/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp +++ b/modules/dnn/src/layers/fast_convolution/fast_convolution.cpp @@ -448,7 +448,8 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co int dilation_d = conv->dilation_d, dilation_h = conv->dilation_h, dilation_w = conv->dilation_w; int ksize = Dk*Hk*Wk; - bool fast_1x1 = stride_d == 1 && stride_w == 1 && stride_h == 1 && ksize == 1; + bool fast_1x1 = ksize == 1 && stride_d == 1 && stride_w == 1 && stride_h == 1 && + pad_front == 0 && pad_top == 0 && pad_left == 0; int DkHkWkCg = Dk*Hk*Wk*Cg; std::vector ofstab_(Hk*Wk*Dk*4, 0); @@ -736,7 +737,8 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co int d0 = std::max(0, (-in_d + dilation_d - 1) / dilation_d); int d1 = std::min(Dk, (Di - in_d + dilation_d - 1) / dilation_d); - bool ok_i = 0 <= in_h && in_h < Hi - (Hk-1)*dilation_h; + bool ok_i = 0 <= in_d && in_d < Di - (Dk-1)*dilation_d && + 0 <= in_h && in_h < Hi - (Hk-1)*dilation_h; int h0 = std::max(0, (-in_h + dilation_h-1)/dilation_h); int h1 = std::min(Hk, (Hi - in_h + dilation_h-1)/dilation_h); From 121034876ddfe9553babc868db644e2ebd74e318 Mon Sep 17 00:00:00 2001 From: Alexander Panov Date: Wed, 28 Dec 2022 17:28:59 +0300 Subject: [PATCH 309/313] Merge pull request #22986 from AleksandrPanov:move_contrib_charuco_to_main_objdetect merge with https://github.com/opencv/opencv_contrib/pull/3394 move Charuco API from contrib to main repo: - add CharucoDetector: ``` CharucoDetector::detectBoard(InputArray image, InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds, OutputArray charucoCorners, OutputArray charucoIds) const // detect charucoCorners and/or markerCorners CharucoDetector::detectDiamonds(InputArray image, InputOutputArrayOfArrays _markerCorners, InputOutputArrayOfArrays _markerIds, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds) const ``` - add `matchImagePoints()` for `CharucoBoard` - remove contrib aruco dependencies from interactive-calibration tool - move almost all aruco tests to objdetect ### 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 - [x] There is a reference to the original bug report and related work - [x] 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 --- apps/interactive-calibration/CMakeLists.txt | 5 +- .../calibController.cpp | 8 +- .../frameProcessor.cpp | 62 +- .../frameProcessor.hpp | 7 +- apps/interactive-calibration/main.cpp | 37 +- .../parametersController.cpp | 3 +- .../objdetect/include/opencv2/objdetect.hpp | 21 + .../include/opencv2/objdetect/aruco_board.hpp | 141 ++-- .../opencv2/objdetect/aruco_detector.hpp | 29 +- .../opencv2/objdetect/aruco_dictionary.hpp | 2 +- .../opencv2/objdetect/charuco_detector.hpp | 154 ++++ .../objdetect/misc/java/test/ArucoTest.java | 32 +- .../misc/python/test/test_objdetect_aruco.py | 62 +- modules/objdetect/perf/perf_aruco.cpp | 285 ++++++++ modules/objdetect/src/aruco/aruco_board.cpp | 546 ++++++++------- .../objdetect/src/aruco/aruco_detector.cpp | 78 +-- .../objdetect/src/aruco/charuco_detector.cpp | 521 ++++++++++++++ modules/objdetect/test/test_aruco_utils.cpp | 205 ++++++ modules/objdetect/test/test_aruco_utils.hpp | 42 ++ .../objdetect/test/test_boarddetection.cpp | 321 +++++++++ .../objdetect/test/test_charucodetection.cpp | 659 ++++++++++++++++++ 21 files changed, 2721 insertions(+), 499 deletions(-) create mode 100644 modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp create mode 100644 modules/objdetect/perf/perf_aruco.cpp create mode 100644 modules/objdetect/src/aruco/charuco_detector.cpp create mode 100644 modules/objdetect/test/test_aruco_utils.cpp create mode 100644 modules/objdetect/test/test_aruco_utils.hpp create mode 100644 modules/objdetect/test/test_boarddetection.cpp create mode 100644 modules/objdetect/test/test_charucodetection.cpp diff --git a/apps/interactive-calibration/CMakeLists.txt b/apps/interactive-calibration/CMakeLists.txt index dacbb13c79..efac5fcc52 100644 --- a/apps/interactive-calibration/CMakeLists.txt +++ b/apps/interactive-calibration/CMakeLists.txt @@ -1,6 +1,3 @@ -set(DEPS opencv_core opencv_imgproc opencv_features2d opencv_highgui opencv_calib3d opencv_videoio) -if(${BUILD_opencv_aruco}) - list(APPEND DEPS opencv_aruco) -endif() +set(DEPS opencv_core opencv_imgproc opencv_features2d opencv_highgui opencv_calib3d opencv_videoio opencv_objdetect) file(GLOB SRCS *.cpp) ocv_add_application(opencv_interactive-calibration MODULES ${DEPS} SRCS ${SRCS}) diff --git a/apps/interactive-calibration/calibController.cpp b/apps/interactive-calibration/calibController.cpp index 9efb9d054a..b2e5b87106 100644 --- a/apps/interactive-calibration/calibController.cpp +++ b/apps/interactive-calibration/calibController.cpp @@ -219,10 +219,10 @@ void calib::calibDataController::filterFrames() if(mCalibData->imagePoints.size()) { mCalibData->imagePoints.erase(mCalibData->imagePoints.begin() + worstElemIndex); mCalibData->objectPoints.erase(mCalibData->objectPoints.begin() + worstElemIndex); - } - else { - mCalibData->allCharucoCorners.erase(mCalibData->allCharucoCorners.begin() + worstElemIndex); - mCalibData->allCharucoIds.erase(mCalibData->allCharucoIds.begin() + worstElemIndex); + if (mCalibData->allCharucoCorners.size()) { + mCalibData->allCharucoCorners.erase(mCalibData->allCharucoCorners.begin() + worstElemIndex); + mCalibData->allCharucoIds.erase(mCalibData->allCharucoIds.begin() + worstElemIndex); + } } cv::Mat newErrorsVec = cv::Mat((int)numberOfFrames - 1, 1, CV_64F); diff --git a/apps/interactive-calibration/frameProcessor.cpp b/apps/interactive-calibration/frameProcessor.cpp index 18d0d87c94..a3c17ded5e 100644 --- a/apps/interactive-calibration/frameProcessor.cpp +++ b/apps/interactive-calibration/frameProcessor.cpp @@ -11,7 +11,6 @@ #include #include -#include #include using namespace calib; @@ -74,17 +73,12 @@ bool CalibProcessor::detectAndParseChessboard(const cv::Mat &frame) bool CalibProcessor::detectAndParseChAruco(const cv::Mat &frame) { -#ifdef HAVE_OPENCV_ARUCO cv::Ptr board = mCharucoBoard.staticCast(); std::vector > corners, rejected; std::vector ids; - cv::aruco::detectMarkers(frame, cv::makePtr(mArucoDictionary), corners, ids, cv::makePtr(), rejected); - cv::aruco::refineDetectedMarkers(frame, board, corners, ids, rejected); cv::Mat currentCharucoCorners, currentCharucoIds; - if(ids.size() > 0) - cv::aruco::interpolateCornersCharuco(corners, ids, frame, mCharucoBoard, currentCharucoCorners, - currentCharucoIds); + detector->detectBoard(frame, currentCharucoCorners, currentCharucoIds, corners, ids); if(ids.size() > 0) cv::aruco::drawDetectedMarkers(frame, corners); if(currentCharucoCorners.total() > 3) { @@ -102,9 +96,6 @@ bool CalibProcessor::detectAndParseChAruco(const cv::Mat &frame) mCurrentCharucoIds = currentCharucoIds; return true; } -#else - CV_UNUSED(frame); -#endif return false; } @@ -155,6 +146,7 @@ bool CalibProcessor::detectAndParseDualACircles(const cv::Mat &frame) void CalibProcessor::saveFrameData() { std::vector objectPoints; + std::vector imagePoints; switch(mBoardType) { @@ -169,6 +161,11 @@ void CalibProcessor::saveFrameData() case chAruco: mCalibData->allCharucoCorners.push_back(mCurrentCharucoCorners); mCalibData->allCharucoIds.push_back(mCurrentCharucoIds); + + mCharucoBoard->matchImagePoints(mCurrentCharucoCorners, mCurrentCharucoIds, objectPoints, imagePoints); + CV_Assert(mCurrentCharucoIds.total() == imagePoints.size()); + mCalibData->imagePoints.push_back(imagePoints); + mCalibData->objectPoints.push_back(objectPoints); break; case CirclesGrid: objectPoints.reserve(mBoardSize.height*mBoardSize.width); @@ -248,37 +245,17 @@ bool CalibProcessor::checkLastFrame() else mCalibData->cameraMatrix.copyTo(tmpCamMatrix); - if(mBoardType != chAruco) { - cv::Mat r, t, angles; - cv::solvePnP(mCalibData->objectPoints.back(), mCurrentImagePoints, tmpCamMatrix, mCalibData->distCoeffs, r, t); - RodriguesToEuler(r, angles, CALIB_DEGREES); - - if(fabs(angles.at(0)) > badAngleThresh || fabs(angles.at(1)) > badAngleThresh) { - mCalibData->objectPoints.pop_back(); - mCalibData->imagePoints.pop_back(); - isFrameBad = true; - } - } - else { -#ifdef HAVE_OPENCV_ARUCO - cv::Mat r, t, angles; - std::vector allObjPoints; - allObjPoints.reserve(mCurrentCharucoIds.total()); - for(size_t i = 0; i < mCurrentCharucoIds.total(); i++) { - int pointID = mCurrentCharucoIds.at((int)i); - CV_Assert(pointID >= 0 && pointID < (int)mCharucoBoard->getChessboardCorners().size()); - allObjPoints.push_back(mCharucoBoard->getChessboardCorners()[pointID]); - } - - cv::solvePnP(allObjPoints, mCurrentCharucoCorners, tmpCamMatrix, mCalibData->distCoeffs, r, t); - RodriguesToEuler(r, angles, CALIB_DEGREES); - - if(180.0 - fabs(angles.at(0)) > badAngleThresh || fabs(angles.at(1)) > badAngleThresh) { - isFrameBad = true; + cv::Mat r, t, angles; + cv::solvePnP(mCalibData->objectPoints.back(), mCalibData->imagePoints.back(), tmpCamMatrix, mCalibData->distCoeffs, r, t); + RodriguesToEuler(r, angles, CALIB_DEGREES); + if(fabs(angles.at(0)) > badAngleThresh || fabs(angles.at(1)) > badAngleThresh) { + mCalibData->objectPoints.pop_back(); + mCalibData->imagePoints.pop_back(); + if (mCalibData->allCharucoCorners.size()) { mCalibData->allCharucoCorners.pop_back(); mCalibData->allCharucoIds.pop_back(); } -#endif + isFrameBad = true; } return isFrameBad; } @@ -295,15 +272,16 @@ CalibProcessor::CalibProcessor(cv::Ptr data, captureParameters mTemplDist = capParams.templDst; mSaveFrames = capParams.saveFrames; mZoom = capParams.zoom; + cv::aruco::CharucoParameters charucoParameters; + charucoParameters.tryRefineMarkers = true; switch(mBoardType) { case chAruco: -#ifdef HAVE_OPENCV_ARUCO mArucoDictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(capParams.charucoDictName)); - mCharucoBoard = cv::aruco::CharucoBoard::create(mBoardSize.width, mBoardSize.height, capParams.charucoSquareLength, - capParams.charucoMarkerSize, mArucoDictionary); -#endif + mCharucoBoard = cv::makePtr(cv::Size(mBoardSize.width + 1, mBoardSize.height + 1), capParams.charucoSquareLength, + capParams.charucoMarkerSize, mArucoDictionary); + detector = cv::makePtr(cv::aruco::CharucoDetector(*mCharucoBoard, charucoParameters)); break; case CirclesGrid: case AcirclesGrid: diff --git a/apps/interactive-calibration/frameProcessor.hpp b/apps/interactive-calibration/frameProcessor.hpp index 51c2906c8e..8ecdca789e 100644 --- a/apps/interactive-calibration/frameProcessor.hpp +++ b/apps/interactive-calibration/frameProcessor.hpp @@ -7,9 +7,7 @@ #include #include -#ifdef HAVE_OPENCV_ARUCO -#include -#endif +#include #include "calibCommon.hpp" #include "calibController.hpp" @@ -39,10 +37,9 @@ protected: cv::Mat mCurrentCharucoIds; cv::Ptr mBlobDetectorPtr; -#ifdef HAVE_OPENCV_ARUCO cv::aruco::Dictionary mArucoDictionary; cv::Ptr mCharucoBoard; -#endif + cv::Ptr detector; int mNeededFramesNum; unsigned mDelayBetweenCaptures; diff --git a/apps/interactive-calibration/main.cpp b/apps/interactive-calibration/main.cpp index d83ef95aad..4d665a62c5 100644 --- a/apps/interactive-calibration/main.cpp +++ b/apps/interactive-calibration/main.cpp @@ -7,9 +7,6 @@ #include #include -#ifdef HAVE_OPENCV_ARUCO -#include -#endif #include #include @@ -105,11 +102,6 @@ int main(int argc, char** argv) captureParameters capParams = paramsController.getCaptureParameters(); internalParameters intParams = paramsController.getInternalParameters(); -#ifndef HAVE_OPENCV_ARUCO - if(capParams.board == chAruco) - CV_Error(cv::Error::StsNotImplemented, "Aruco module is disabled in current build configuration." - " Consider usage of another calibration pattern\n"); -#endif cv::TermCriteria solverTermCrit = cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, intParams.solverMaxIters, intParams.solverEps); @@ -170,29 +162,12 @@ int main(int argc, char** argv) globalData->imageSize = pipeline->getImageSize(); calibrationFlags = controller->getNewFlags(); - if(capParams.board != chAruco) { - globalData->totalAvgErr = - cv::calibrateCamera(globalData->objectPoints, globalData->imagePoints, - globalData->imageSize, globalData->cameraMatrix, - globalData->distCoeffs, cv::noArray(), cv::noArray(), - globalData->stdDeviations, cv::noArray(), globalData->perViewErrors, - calibrationFlags, solverTermCrit); - } - else { -#ifdef HAVE_OPENCV_ARUCO - cv::aruco::Dictionary dictionary = - cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(capParams.charucoDictName)); - cv::Ptr charucoboard = - cv::aruco::CharucoBoard::create(capParams.boardSize.width, capParams.boardSize.height, - capParams.charucoSquareLength, capParams.charucoMarkerSize, dictionary); - globalData->totalAvgErr = - cv::aruco::calibrateCameraCharuco(globalData->allCharucoCorners, globalData->allCharucoIds, - charucoboard, globalData->imageSize, - globalData->cameraMatrix, globalData->distCoeffs, - cv::noArray(), cv::noArray(), globalData->stdDeviations, cv::noArray(), - globalData->perViewErrors, calibrationFlags, solverTermCrit); -#endif - } + globalData->totalAvgErr = + cv::calibrateCamera(globalData->objectPoints, globalData->imagePoints, + globalData->imageSize, globalData->cameraMatrix, + globalData->distCoeffs, cv::noArray(), cv::noArray(), + globalData->stdDeviations, cv::noArray(), globalData->perViewErrors, + calibrationFlags, solverTermCrit); dataController->updateUndistortMap(); dataController->printParametersToConsole(std::cout); controller->updateState(); diff --git a/apps/interactive-calibration/parametersController.cpp b/apps/interactive-calibration/parametersController.cpp index ebec850323..eab183ab69 100644 --- a/apps/interactive-calibration/parametersController.cpp +++ b/apps/interactive-calibration/parametersController.cpp @@ -109,6 +109,7 @@ bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser) std::string templateType = parser.get("t"); + if(templateType.find("symcircles", 0) == 0) { mCapParams.board = CirclesGrid; mCapParams.boardSize = cv::Size(4, 11); @@ -127,7 +128,7 @@ bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser) } else if(templateType.find("charuco", 0) == 0) { mCapParams.board = chAruco; - mCapParams.boardSize = cv::Size(6, 8); + mCapParams.boardSize = cv::Size(5, 7); mCapParams.charucoDictName = 0; mCapParams.charucoSquareLength = 200; mCapParams.charucoMarkerSize = 100; diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index 58f1aee3b7..c398f3a0cd 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -105,6 +105,26 @@ using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is availa @defgroup objdetect_dnn_face DNN-based face detection and recognition Check @ref tutorial_dnn_face "the corresponding tutorial" for more details. @defgroup objdetect_common Common functions and classes + @defgroup objdetect_aruco ArUco markers and boards detection for robust camera pose estimation + @{ + ArUco Marker Detection + Square fiducial markers (also known as Augmented Reality Markers) are useful for easy, + fast and robust camera pose estimation. + + The main functionality of ArucoDetector class is detection of markers in an image. If the markers are grouped + as a board, then you can try to recover the missing markers with ArucoDetector::refineDetectedMarkers(). + ArUco markers can also be used for advanced chessboard corner finding. To do this, group the markers in the + CharucoBoard and find the corners of the chessboard with the CharucoDetector::detectBoard(). + + The implementation is based on the ArUco Library by R. Muñoz-Salinas and S. Garrido-Jurado @cite Aruco2014. + + Markers can also be detected based on the AprilTag 2 @cite wang2016iros fiducial detection method. + + @sa @cite Aruco2014 + This code has been originally developed by Sergio Garrido-Jurado as a project + for Google Summer of Code 2015 (GSoC 15). + @} + @} */ @@ -852,5 +872,6 @@ protected: #include "opencv2/objdetect/detection_based_tracker.hpp" #include "opencv2/objdetect/face.hpp" #include "opencv2/objdetect/aruco_detector.hpp" +#include "opencv2/objdetect/charuco_detector.hpp" #endif diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp index fb90c7b75d..90ad2b8a7e 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp @@ -8,7 +8,7 @@ namespace cv { namespace aruco { -//! @addtogroup aruco +//! @addtogroup objdetect_aruco //! @{ class Dictionary; @@ -22,29 +22,15 @@ class Dictionary; * - The dictionary which indicates the type of markers of the board * - The identifier of all the markers in the board. */ -class CV_EXPORTS_W Board { -protected: - Board(); // use ::create() +class CV_EXPORTS_W_SIMPLE Board { public: - /** @brief Draw a planar board - * - * @param outSize size of the output image in pixels. - * @param img output image with the board. The size of this image will be outSize - * and the board will be on the center, keeping the board proportions. - * @param marginSize minimum margins (in pixels) of the board in the output image - * @param borderBits width of the marker borders. - * - * This function return the image of the GridBoard, ready to be printed. - */ - CV_WRAP virtual void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const; - - /** @brief Provide way to create Board by passing necessary data. Specially needed in Python. + /** @brief Common Board constructor * * @param objPoints array of object points of all the marker corners in the board * @param dictionary the dictionary of markers employed for this board * @param ids vector of the identifiers of the markers in the board */ - CV_WRAP static Ptr create(InputArrayOfArrays objPoints, const Dictionary &dictionary, InputArray ids); + CV_WRAP Board(InputArrayOfArrays objPoints, const Dictionary& dictionary, InputArray ids); /** @brief return the Dictionary of markers employed for this board */ @@ -72,31 +58,19 @@ public: CV_WRAP const Point3f& getRightBottomCorner() const; /** @brief Given a board configuration and a set of detected markers, returns the corresponding - * image points and object points to call solvePnP + * image points and object points to call solvePnP() * * @param detectedCorners List of detected marker corners of the board. - * @param detectedIds List of identifiers for each marker. + * For CharucoBoard class you can set list of charuco corners. + * @param detectedIds List of identifiers for each marker or list of charuco identifiers for each corner. + * For CharucoBoard class you can set list of charuco identifiers for each corner. * @param objPoints Vector of vectors of board marker points in the board coordinate space. * @param imgPoints Vector of vectors of the projections of board marker corner points. */ CV_WRAP void matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, OutputArray objPoints, OutputArray imgPoints) const; - virtual ~Board(); -protected: - struct BoardImpl; - Ptr boardImpl; -}; -/** @brief Planar board with grid arrangement of markers - * - * More common type of board. All markers are placed in the same plane in a grid arrangement. - * The board image can be drawn using generateImage() method. - */ -class CV_EXPORTS_W GridBoard : public Board { -protected: - GridBoard(); -public: - /** @brief Draw a GridBoard + /** @brief Draw a planar board * * @param outSize size of the output image in pixels. * @param img output image with the board. The size of this image will be outSize @@ -104,50 +78,44 @@ public: * @param marginSize minimum margins (in pixels) of the board in the output image * @param borderBits width of the marker borders. * - * This function return the image of the GridBoard, ready to be printed. + * This function return the image of the board, ready to be printed. */ - CV_WRAP void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const CV_OVERRIDE; + CV_WRAP void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const; + CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first) + Board(); + + struct Impl; +protected: + Board(const Ptr& impl); + Ptr impl; +}; + +/** @brief Planar board with grid arrangement of markers + * + * More common type of board. All markers are placed in the same plane in a grid arrangement. + * The board image can be drawn using generateImage() method. + */ +class CV_EXPORTS_W_SIMPLE GridBoard : public Board { +public: /** - * @brief Create a GridBoard object + * @brief GridBoard constructor * - * @param markersX number of markers in X direction - * @param markersY number of markers in Y direction + * @param size number of markers in x and y directions * @param markerLength marker side length (normally in meters) * @param markerSeparation separation between two markers (same unit as markerLength) * @param dictionary dictionary of markers indicating the type of markers - * @param ids set marker ids in dictionary to use on board. - * @return the output GridBoard object - * - * This functions creates a GridBoard object given the number of markers in each direction and - * the marker size and marker separation. + * @param ids set of marker ids in dictionary to use on board. */ - CV_WRAP static Ptr create(int markersX, int markersY, float markerLength, float markerSeparation, - const Dictionary &dictionary, InputArray ids); - - /** - * @overload - * @brief Create a GridBoard object - * - * @param markersX number of markers in X direction - * @param markersY number of markers in Y direction - * @param markerLength marker side length (normally in meters) - * @param markerSeparation separation between two markers (same unit as markerLength) - * @param dictionary dictionary of markers indicating the type of markers - * @param firstMarker id of first marker in dictionary to use on board. - * @return the output GridBoard object - */ - CV_WRAP static Ptr create(int markersX, int markersY, float markerLength, float markerSeparation, - const Dictionary &dictionary, int firstMarker = 0); + CV_WRAP GridBoard(const Size& size, float markerLength, float markerSeparation, + const Dictionary &dictionary, InputArray ids = noArray()); CV_WRAP Size getGridSize() const; CV_WRAP float getMarkerLength() const; CV_WRAP float getMarkerSeparation() const; -protected: - struct GridImpl; - Ptr gridImpl; - friend class CharucoBoard; + CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first) + GridBoard(); }; /** @@ -156,40 +124,19 @@ protected: * The benefits of ChArUco boards is that they provide both, ArUco markers versatility and chessboard corner precision, * which is important for calibration and pose estimation. The board image can be drawn using generateImage() method. */ -class CV_EXPORTS_W CharucoBoard : public Board { -protected: - CharucoBoard(); +class CV_EXPORTS_W_SIMPLE CharucoBoard : public Board { public: - - /** @brief Draw a ChArUco board + /** @brief CharucoBoard constructor * - * @param outSize size of the output image in pixels. - * @param img output image with the board. The size of this image will be outSize - * and the board will be on the center, keeping the board proportions. - * @param marginSize minimum margins (in pixels) of the board in the output image - * @param borderBits width of the marker borders. - * - * This function return the image of the ChArUco board, ready to be printed. - */ - CV_WRAP void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const CV_OVERRIDE; - - - /** @brief Create a CharucoBoard object - * - * @param squaresX number of chessboard squares in X direction - * @param squaresY number of chessboard squares in Y direction - * @param squareLength chessboard square side length (normally in meters) + * @param size number of chessboard squares in x and y directions + * @param squareLength squareLength chessboard square side length (normally in meters) * @param markerLength marker side length (same unit than squareLength) - * @param dictionary dictionary of markers indicating the type of markers. + * @param dictionary dictionary of markers indicating the type of markers * @param ids array of id used markers * The first markers in the dictionary are used to fill the white chessboard squares. - * @return the output CharucoBoard object - * - * This functions creates a CharucoBoard object given the number of squares in each direction - * and the size of the markers and chessboard squares. */ - CV_WRAP static Ptr create(int squaresX, int squaresY, float squareLength, float markerLength, - const Dictionary &dictionary, InputArray ids = noArray()); + CV_WRAP CharucoBoard(const Size& size, float squareLength, float markerLength, + const Dictionary &dictionary, InputArray ids = noArray()); CV_WRAP Size getChessboardSize() const; CV_WRAP float getSquareLength() const; @@ -220,10 +167,8 @@ public: */ CV_WRAP bool checkCharucoCornersCollinear(InputArray charucoIds) const; -protected: - struct CharucoImpl; - friend struct CharucoImpl; - Ptr charucoImpl; + CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first) + CharucoBoard(); }; //! @} diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp index 80f84339cd..c7338c39e0 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp @@ -10,28 +10,7 @@ namespace cv { namespace aruco { -/** @defgroup aruco ArUco Marker Detection - * Square fiducial markers (also known as Augmented Reality Markers) are useful for easy, - * fast and robust camera pose estimation. - * - * The main functionality of ArucoDetector class is detection of markers in an image. There are even more - * functionalities implemented in the aruco contrib module (files aruco.hpp, charuco.hpp, aruco_calib.hpp): - * - Pose estimation from a single marker or from a board/set of markers - * - Detection of ChArUco board for high subpixel accuracy - * - Camera calibration from both, ArUco boards and ChArUco boards. - * - Detection of ChArUco diamond markers - * The functionalities from the aruco contrib module is planned to be transferred to the main repository. - * - * The implementation is based on the ArUco Library by R. Muñoz-Salinas and S. Garrido-Jurado @cite Aruco2014. - * - * Markers can also be detected based on the AprilTag 2 @cite wang2016iros fiducial detection method. - * - * @sa @cite Aruco2014 - * This code has been originally developed by Sergio Garrido-Jurado as a project - * for Google Summer of Code 2015 (GSoC 15). - */ - -//! @addtogroup aruco +//! @addtogroup objdetect_aruco //! @{ enum CornerRefineMethod{ @@ -294,7 +273,7 @@ public: * @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard */ CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, - OutputArrayOfArrays rejectedImgPoints = noArray()); + OutputArrayOfArrays rejectedImgPoints = noArray()) const; /** @brief Refind not detected markers based on the already detected and the board layout * @@ -318,11 +297,11 @@ public: * using projectPoint function. If not, missing marker projections are interpolated using global * homography, and all the marker corners in the board must have the same Z coordinate. */ - CV_WRAP void refineDetectedMarkers(InputArray image, const Ptr &board, + CV_WRAP void refineDetectedMarkers(InputArray image, const Board &board, InputOutputArrayOfArrays detectedCorners, InputOutputArray detectedIds, InputOutputArrayOfArrays rejectedCorners, InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), - OutputArray recoveredIdxs = noArray()); + OutputArray recoveredIdxs = noArray()) const; CV_WRAP const Dictionary& getDictionary() const; CV_WRAP void setDictionary(const Dictionary& dictionary); diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp index 498bcf1c33..343d876b6c 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp @@ -9,7 +9,7 @@ namespace cv { namespace aruco { -//! @addtogroup aruco +//! @addtogroup objdetect_aruco //! @{ diff --git a/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp new file mode 100644 index 0000000000..f6ad677099 --- /dev/null +++ b/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp @@ -0,0 +1,154 @@ +// 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_OBJDETECT_CHARUCO_DETECTOR_HPP +#define OPENCV_OBJDETECT_CHARUCO_DETECTOR_HPP + +#include "opencv2/objdetect/aruco_detector.hpp" + +namespace cv { +namespace aruco { + +//! @addtogroup objdetect_aruco +//! @{ + +struct CV_EXPORTS_W_SIMPLE CharucoParameters { + CharucoParameters() { + minMarkers = 2; + tryRefineMarkers = false; + } + /// cameraMatrix optional 3x3 floating-point camera matrix + CV_PROP_RW Mat cameraMatrix; + + /// distCoeffs optional vector of distortion coefficients + CV_PROP_RW Mat distCoeffs; + + /// minMarkers number of adjacent markers that must be detected to return a charuco corner, default = 2 + CV_PROP_RW int minMarkers; + + /// try to use refine board, default false + CV_PROP_RW bool tryRefineMarkers; +}; + +class CV_EXPORTS_W CharucoDetector : public Algorithm { +public: + /** @brief Basic CharucoDetector constructor + * + * @param board ChAruco board + * @param charucoParams charuco detection parameters + * @param detectorParams marker detection parameters + * @param refineParams marker refine detection parameters + */ + CV_WRAP CharucoDetector(const CharucoBoard& board, + const CharucoParameters& charucoParams = CharucoParameters(), + const DetectorParameters &detectorParams = DetectorParameters(), + const RefineParameters& refineParams = RefineParameters()); + + CV_WRAP const CharucoBoard& getBoard() const; + CV_WRAP void setBoard(const CharucoBoard& board); + + CV_WRAP const CharucoParameters& getCharucoParameters() const; + CV_WRAP void setCharucoParameters(CharucoParameters& charucoParameters); + + CV_WRAP const DetectorParameters& getDetectorParameters() const; + CV_WRAP void setDetectorParameters(const DetectorParameters& detectorParameters); + + CV_WRAP const RefineParameters& getRefineParameters() const; + CV_WRAP void setRefineParameters(const RefineParameters& refineParameters); + + /** + * @brief detect aruco markers and interpolate position of ChArUco board corners + * @param image input image necesary for corner refinement. Note that markers are not detected and + * should be sent in corners and ids parameters. + * @param charucoCorners interpolated chessboard corners. + * @param charucoIds interpolated chessboard corners identifiers. + * @param markerCorners vector of already detected markers corners. For each marker, its four + * corners are provided, (e.g std::vector > ). For N detected markers, the + * dimensions of this array should be Nx4. The order of the corners should be clockwise. + * If markerCorners and markerCorners are empty, the function detect aruco markers and ids. + * @param markerIds list of identifiers for each marker in corners. + * If markerCorners and markerCorners are empty, the function detect aruco markers and ids. + * + * This function receives the detected markers and returns the 2D position of the chessboard corners + * from a ChArUco board using the detected Aruco markers. + * + * If markerCorners and markerCorners are empty, the detectMarkers() will run and detect aruco markers and ids. + * + * If camera parameters are provided, the process is based in an approximated pose estimation, else it is based on local homography. + * Only visible corners are returned. For each corner, its corresponding identifier is also returned in charucoIds. + * @sa findChessboardCorners + */ + CV_WRAP void detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds, + InputOutputArrayOfArrays markerCorners = noArray(), + InputOutputArray markerIds = noArray()) const; + + /** + * @brief Detect ChArUco Diamond markers + * + * @param image input image necessary for corner subpixel. + * @param diamondCorners output list of detected diamond corners (4 corners per diamond). The order + * is the same than in marker corners: top left, top right, bottom right and bottom left. Similar + * format than the corners returned by detectMarkers (e.g std::vector > ). + * @param diamondIds ids of the diamonds in diamondCorners. The id of each diamond is in fact of + * type Vec4i, so each diamond has 4 ids, which are the ids of the aruco markers composing the + * diamond. + * @param markerCorners list of detected marker corners from detectMarkers function. + * If markerCorners and markerCorners are empty, the function detect aruco markers and ids. + * @param markerIds list of marker ids in markerCorners. + * If markerCorners and markerCorners are empty, the function detect aruco markers and ids. + * + * This function detects Diamond markers from the previous detected ArUco markers. The diamonds + * are returned in the diamondCorners and diamondIds parameters. If camera calibration parameters + * are provided, the diamond search is based on reprojection. If not, diamond search is based on + * homography. Homography is faster than reprojection, but less accurate. + */ + CV_WRAP void detectDiamonds(InputArray image, OutputArrayOfArrays diamondCorners, OutputArray diamondIds, + InputOutputArrayOfArrays markerCorners = noArray(), + InputOutputArrayOfArrays markerIds = noArray()) const; +protected: + struct CharucoDetectorImpl; + Ptr charucoDetectorImpl; +}; + +/** + * @brief Draws a set of Charuco corners + * @param image input/output image. It must have 1 or 3 channels. The number of channels is not + * altered. + * @param charucoCorners vector of detected charuco corners + * @param charucoIds list of identifiers for each corner in charucoCorners + * @param cornerColor color of the square surrounding each corner + * + * This function draws a set of detected Charuco corners. If identifiers vector is provided, it also + * draws the id of each corner. + */ +CV_EXPORTS_W void drawDetectedCornersCharuco(InputOutputArray image, InputArray charucoCorners, + InputArray charucoIds = noArray(), Scalar cornerColor = Scalar(255, 0, 0)); + +/** + * @brief Draw a set of detected ChArUco Diamond markers + * + * @param image input/output image. It must have 1 or 3 channels. The number of channels is not + * altered. + * @param diamondCorners positions of diamond corners in the same format returned by + * detectCharucoDiamond(). (e.g std::vector > ). For N detected markers, + * the dimensions of this array should be Nx4. The order of the corners should be clockwise. + * @param diamondIds vector of identifiers for diamonds in diamondCorners, in the same format + * returned by detectCharucoDiamond() (e.g. std::vector). + * Optional, if not provided, ids are not painted. + * @param borderColor color of marker borders. Rest of colors (text color and first corner color) + * are calculated based on this one. + * + * Given an array of detected diamonds, this functions draws them in the image. The marker borders + * are painted and the markers identifiers if provided. + * Useful for debugging purposes. + */ +CV_EXPORTS_W void drawDetectedDiamonds(InputOutputArray image, InputArrayOfArrays diamondCorners, + InputArray diamondIds = noArray(), + Scalar borderColor = Scalar(0, 0, 255)); + +//! @} + +} +} + +#endif diff --git a/modules/objdetect/misc/java/test/ArucoTest.java b/modules/objdetect/misc/java/test/ArucoTest.java index 685f78443d..932141275c 100644 --- a/modules/objdetect/misc/java/test/ArucoTest.java +++ b/modules/objdetect/misc/java/test/ArucoTest.java @@ -4,8 +4,10 @@ import java.util.ArrayList; import java.util.List; import org.opencv.test.OpenCVTestCase; +import org.junit.Assert; import org.opencv.core.Scalar; import org.opencv.core.Mat; +import org.opencv.core.MatOfInt; import org.opencv.core.Size; import org.opencv.core.CvType; import org.opencv.objdetect.*; @@ -29,7 +31,7 @@ public class ArucoTest extends OpenCVTestCase { Mat ids = new Mat(1, 1, CvType.CV_32SC1); ids.put(row, col, 0); - Board board = Board.create(objPoints, dictionary, ids); + Board board = new Board(objPoints, dictionary, ids); Mat image = new Mat(); board.generateImage(new Size(80, 80), image, 2); @@ -80,4 +82,32 @@ public class ArucoTest extends OpenCVTestCase { assertArrayEquals(new double[]{size + offset - 1, size + offset - 1}, res.get(0, 2), 0.0); assertArrayEquals(new double[]{offset, size + offset - 1}, res.get(0, 3), 0.0); } + + public void testCharucoDetector() { + Dictionary dictionary = Objdetect.getPredefinedDictionary(0); + int boardSizeX = 3, boardSizeY = 3; + CharucoBoard board = new CharucoBoard(new Size(boardSizeX, boardSizeY), 1.f, 0.8f, dictionary); + CharucoDetector charucoDetector = new CharucoDetector(board); + + int cellSize = 80; + Mat boardImage = new Mat(); + board.generateImage(new Size(cellSize*boardSizeX, cellSize*boardSizeY), boardImage); + + assertTrue(boardImage.total() > 0); + + Mat charucoCorners = new Mat(); + Mat charucoIds = new Mat(); + charucoDetector.detectBoard(boardImage, charucoCorners, charucoIds); + + assertEquals(4, charucoIds.total()); + int[] intCharucoIds = (new MatOfInt(charucoIds)).toArray(); + Assert.assertArrayEquals(new int[]{0, 1, 2, 3}, intCharucoIds); + + double eps = 0.2; + assertArrayEquals(new double[]{cellSize, cellSize}, charucoCorners.get(0, 0), eps); + assertArrayEquals(new double[]{2*cellSize, cellSize}, charucoCorners.get(1, 0), eps); + assertArrayEquals(new double[]{cellSize, 2*cellSize}, charucoCorners.get(2, 0), eps); + assertArrayEquals(new double[]{2*cellSize, 2*cellSize}, charucoCorners.get(3, 0), eps); + } + } diff --git a/modules/objdetect/misc/python/test/test_objdetect_aruco.py b/modules/objdetect/misc/python/test/test_objdetect_aruco.py index 318159b5c7..97d4fcb821 100644 --- a/modules/objdetect/misc/python/test/test_objdetect_aruco.py +++ b/modules/objdetect/misc/python/test/test_objdetect_aruco.py @@ -3,7 +3,7 @@ # Python 2/3 compatibility from __future__ import print_function -import os, numpy as np +import os, tempfile, numpy as np import cv2 as cv @@ -17,14 +17,14 @@ class aruco_objdetect_test(NewOpenCVTests): rev_ids = ids[::-1] aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_250) - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict) + board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict) np.testing.assert_array_equal(board.getIds().squeeze(), ids) - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, rev_ids) + board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict, rev_ids) np.testing.assert_array_equal(board.getIds().squeeze(), rev_ids) - board = cv.aruco.CharucoBoard_create(7, 5, 1, 0.5, aruco_dict, ids) + board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict, ids) np.testing.assert_array_equal(board.getIds().squeeze(), ids) def test_identify(self): @@ -72,7 +72,7 @@ class aruco_objdetect_test(NewOpenCVTests): aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params) board_size = (3, 4) - board = cv.aruco.GridBoard_create(board_size[0], board_size[1], 5.0, 1.0, aruco_dict) + board = cv.aruco.GridBoard(board_size, 5.0, 1.0, aruco_dict) board_image = board.generateImage((board_size[0]*50, board_size[1]*50), marginSize=10) corners, ids, rejected = aruco_detector.detectMarkers(board_image) @@ -90,5 +90,57 @@ class aruco_objdetect_test(NewOpenCVTests): self.assertEqual((1, 4, 2), refine_corners[0].shape) np.testing.assert_array_equal(corners, refine_corners) + def test_write_read_dictionary(self): + try: + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_50) + markers_gold = aruco_dict.bytesList + + # write aruco_dict + fd, filename = tempfile.mkstemp(prefix="opencv_python_aruco_dict_", suffix=".yml") + os.close(fd) + + fs_write = cv.FileStorage(filename, cv.FileStorage_WRITE) + aruco_dict.writeDictionary(fs_write) + fs_write.release() + + # reset aruco_dict + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_250) + + # read aruco_dict + fs_read = cv.FileStorage(filename, cv.FileStorage_READ) + aruco_dict.readDictionary(fs_read.root()) + fs_read.release() + + # check equal + self.assertEqual(aruco_dict.markerSize, 5) + self.assertEqual(aruco_dict.maxCorrectionBits, 3) + np.testing.assert_array_equal(aruco_dict.bytesList, markers_gold) + + finally: + if os.path.exists(filename): + os.remove(filename) + + def test_charuco_detector(self): + aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250) + board_size = (3, 3) + board = cv.aruco.CharucoBoard(board_size, 1.0, .8, aruco_dict) + charuco_detector = cv.aruco.CharucoDetector(board) + cell_size = 100 + + image = board.generateImage((cell_size*board_size[0], cell_size*board_size[1])) + + list_gold_corners = [] + for i in range(1, board_size[0]): + for j in range(1, board_size[1]): + list_gold_corners.append((j*cell_size, i*cell_size)) + gold_corners = np.array(list_gold_corners, dtype=np.float32) + + charucoCorners, charucoIds, markerCorners, markerIds = charuco_detector.detectBoard(image) + + self.assertEqual(len(charucoIds), 4) + for i in range(0, 4): + self.assertEqual(charucoIds[i], i) + np.testing.assert_allclose(gold_corners, charucoCorners.reshape(-1, 2), 0.01, 0.1) + if __name__ == '__main__': NewOpenCVTests.bootstrap() diff --git a/modules/objdetect/perf/perf_aruco.cpp b/modules/objdetect/perf/perf_aruco.cpp new file mode 100644 index 0000000000..d3f9141a21 --- /dev/null +++ b/modules/objdetect/perf/perf_aruco.cpp @@ -0,0 +1,285 @@ +// 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 "perf_precomp.hpp" +#include "opencv2/calib3d.hpp" + +namespace opencv_test { +using namespace perf; + +typedef tuple UseArucoParams; +typedef TestBaseWithParam EstimateAruco; +#define ESTIMATE_PARAMS Combine(Values(false, true), Values(-1)) + +static double deg2rad(double deg) { return deg * CV_PI / 180.; } + +class MarkerPainter +{ +private: + int imgMarkerSize = 0; + Mat cameraMatrix; +public: + MarkerPainter(const int size) { + setImgMarkerSize(size); + } + + void setImgMarkerSize(const int size) { + imgMarkerSize = size; + cameraMatrix = Mat::eye(3, 3, CV_64FC1); + cameraMatrix.at(0, 0) = cameraMatrix.at(1, 1) = imgMarkerSize; + cameraMatrix.at(0, 2) = imgMarkerSize / 2.0; + cameraMatrix.at(1, 2) = imgMarkerSize / 2.0; + } + + static std::pair getSyntheticRT(double yaw, double pitch, double distance) { + auto rvec_tvec = std::make_pair(Mat(3, 1, CV_64FC1), Mat(3, 1, CV_64FC1)); + Mat& rvec = rvec_tvec.first; + Mat& tvec = rvec_tvec.second; + + // Rvec + // first put the Z axis aiming to -X (like the camera axis system) + Mat rotZ(3, 1, CV_64FC1); + rotZ.ptr(0)[0] = 0; + rotZ.ptr(0)[1] = 0; + rotZ.ptr(0)[2] = -0.5 * CV_PI; + + Mat rotX(3, 1, CV_64FC1); + rotX.ptr(0)[0] = 0.5 * CV_PI; + rotX.ptr(0)[1] = 0; + rotX.ptr(0)[2] = 0; + + Mat camRvec, camTvec; + composeRT(rotZ, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotX, Mat(3, 1, CV_64FC1, Scalar::all(0)), + camRvec, camTvec); + + // now pitch and yaw angles + Mat rotPitch(3, 1, CV_64FC1); + rotPitch.ptr(0)[0] = 0; + rotPitch.ptr(0)[1] = pitch; + rotPitch.ptr(0)[2] = 0; + + Mat rotYaw(3, 1, CV_64FC1); + rotYaw.ptr(0)[0] = yaw; + rotYaw.ptr(0)[1] = 0; + rotYaw.ptr(0)[2] = 0; + + composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw, + Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); + + // compose both rotations + composeRT(camRvec, Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, + Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); + + // Tvec, just move in z (camera) direction the specific distance + tvec.ptr(0)[0] = 0.; + tvec.ptr(0)[1] = 0.; + tvec.ptr(0)[2] = distance; + return rvec_tvec; + } + + std::pair > getProjectMarker(int id, double yaw, double pitch, + const aruco::DetectorParameters& parameters, + const aruco::Dictionary& dictionary) { + auto marker_corners = std::make_pair(Mat(imgMarkerSize, imgMarkerSize, CV_8UC1, Scalar::all(255)), vector()); + Mat& img = marker_corners.first; + vector& corners = marker_corners.second; + + // canonical image + const int markerSizePixels = static_cast(imgMarkerSize/sqrt(2.f)); + aruco::generateImageMarker(dictionary, id, markerSizePixels, img, parameters.markerBorderBits); + + // get rvec and tvec for the perspective + const double distance = 0.1; + auto rvec_tvec = MarkerPainter::getSyntheticRT(yaw, pitch, distance); + Mat& rvec = rvec_tvec.first; + Mat& tvec = rvec_tvec.second; + + const float markerLength = 0.05f; + vector markerObjPoints; + markerObjPoints.emplace_back(Point3f(-markerLength / 2.f, +markerLength / 2.f, 0)); + markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(markerLength, 0, 0)); + markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(markerLength, -markerLength, 0)); + markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(0, -markerLength, 0)); + + // project markers and draw them + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + projectPoints(markerObjPoints, rvec, tvec, cameraMatrix, distCoeffs, corners); + + vector originalCorners; + originalCorners.emplace_back(Point2f(0.f, 0.f)); + originalCorners.emplace_back(originalCorners[0]+Point2f((float)markerSizePixels, 0)); + originalCorners.emplace_back(originalCorners[0]+Point2f((float)markerSizePixels, (float)markerSizePixels)); + originalCorners.emplace_back(originalCorners[0]+Point2f(0, (float)markerSizePixels)); + + Mat transformation = getPerspectiveTransform(originalCorners, corners); + + warpPerspective(img, img, transformation, Size(imgMarkerSize, imgMarkerSize), INTER_NEAREST, BORDER_CONSTANT, + Scalar::all(255)); + return marker_corners; + } + + std::pair > > getProjectMarkersTile(const int numMarkers, + const aruco::DetectorParameters& params, + const aruco::Dictionary& dictionary) { + Mat tileImage(imgMarkerSize*numMarkers, imgMarkerSize*numMarkers, CV_8UC1, Scalar::all(255)); + map > idCorners; + + int iter = 0, pitch = 0, yaw = 0; + for (int i = 0; i < numMarkers; i++) { + for (int j = 0; j < numMarkers; j++) { + int currentId = iter; + auto marker_corners = getProjectMarker(currentId, deg2rad(70+yaw), deg2rad(pitch), params, dictionary); + Point2i startPoint(j*imgMarkerSize, i*imgMarkerSize); + Mat tmp_roi = tileImage(Rect(startPoint.x, startPoint.y, imgMarkerSize, imgMarkerSize)); + marker_corners.first.copyTo(tmp_roi); + + for (Point2f& point: marker_corners.second) + point += static_cast(startPoint); + idCorners[currentId] = marker_corners.second; + auto test = idCorners[currentId]; + yaw = (yaw + 10) % 51; // 70+yaw >= 70 && 70+yaw <= 120 + iter++; + } + pitch = (pitch + 60) % 360; + } + return std::make_pair(tileImage, idCorners); + } +}; + +static inline double getMaxDistance(map > &golds, const vector& ids, + const vector >& corners) { + std::map mapDist; + for (const auto& el : golds) + mapDist[el.first] = std::numeric_limits::max(); + for (size_t i = 0; i < ids.size(); i++) { + int id = ids[i]; + const auto gold_corners = golds.find(id); + if (gold_corners != golds.end()) { + double distance = 0.; + for (int c = 0; c < 4; c++) + distance = std::max(distance, cv::norm(gold_corners->second[c] - corners[i][c])); + mapDist[id] = distance; + } + } + return std::max_element(std::begin(mapDist), std::end(mapDist), + [](const pair& p1, const pair& p2){return p1.second < p2.second;})->second; +} + +PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) { + UseArucoParams testParams = GetParam(); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + + const int markerSize = 100; + const int numMarkersInRow = 9; + //USE_ARUCO3 + detectorParams.useAruco3Detection = get<0>(testParams); + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = 32; + detectorParams.minMarkerLengthRatioOriginalImg = 0.04f / numMarkersInRow; + } + aruco::ArucoDetector detector(dictionary, detectorParams); + MarkerPainter painter(markerSize); + auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary); + + // detect markers + vector > corners; + vector ids; + TEST_CYCLE() { + detector.detectMarkers(image_map.first, corners, ids); + } + ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast(ids.size())); + double maxDistance = getMaxDistance(image_map.second, ids, corners); + ASSERT_LT(maxDistance, 3.); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) { + UseArucoParams testParams = GetParam(); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + + //USE_ARUCO3 + detectorParams.useAruco3Detection = get<0>(testParams); + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = 64; + detectorParams.minMarkerLengthRatioOriginalImg = 0.f; + } + aruco::ArucoDetector detector(dictionary, detectorParams); + const int markerSize = 200; + const int numMarkersInRow = 11; + MarkerPainter painter(markerSize); + auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary); + + // detect markers + vector > corners; + vector ids; + TEST_CYCLE() { + detector.detectMarkers(image_map.first, corners, ids); + } + ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast(ids.size())); + double maxDistance = getMaxDistance(image_map.second, ids, corners); + ASSERT_LT(maxDistance, 3.); + SANITY_CHECK_NOTHING(); +} + +struct Aruco3Params { + bool useAruco3Detection = false; + float minMarkerLengthRatioOriginalImg = 0.f; + int minSideLengthCanonicalImg = 0; + + Aruco3Params(bool useAruco3, float minMarkerLen, int minSideLen): useAruco3Detection(useAruco3), + minMarkerLengthRatioOriginalImg(minMarkerLen), + minSideLengthCanonicalImg(minSideLen) {} + friend std::ostream& operator<<(std::ostream& os, const Aruco3Params& d) { + os << d.useAruco3Detection << " " << d.minMarkerLengthRatioOriginalImg << " " << d.minSideLengthCanonicalImg; + return os; + } +}; +typedef tuple> ArucoTestParams; + +typedef TestBaseWithParam EstimateLargeAruco; +#define ESTIMATE_FHD_PARAMS Combine(Values(Aruco3Params(false, 0.f, 0), Aruco3Params(true, 0.f, 32), \ +Aruco3Params(true, 0.015f, 32), Aruco3Params(true, 0.f, 16), Aruco3Params(true, 0.0069f, 16)), \ +Values(std::make_pair(1440, 1), std::make_pair(480, 3), std::make_pair(144, 10))) + +PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) { + ArucoTestParams testParams = GetParam(); + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::DetectorParameters detectorParams; + detectorParams.minDistanceToBorder = 1; + detectorParams.markerBorderBits = 1; + detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX; + + //USE_ARUCO3 + detectorParams.useAruco3Detection = get<0>(testParams).useAruco3Detection; + if (detectorParams.useAruco3Detection) { + detectorParams.minSideLengthCanonicalImg = get<0>(testParams).minSideLengthCanonicalImg; + detectorParams.minMarkerLengthRatioOriginalImg = get<0>(testParams).minMarkerLengthRatioOriginalImg; + } + aruco::ArucoDetector detector(dictionary, detectorParams); + const int markerSize = get<1>(testParams).first; // 1440 or 480 or 144 + const int numMarkersInRow = get<1>(testParams).second; // 1 or 3 or 144 + MarkerPainter painter(markerSize); // num pixels is 1440x1440 as in FHD 1920x1080 + auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary); + + // detect markers + vector > corners; + vector ids; + TEST_CYCLE() + { + detector.detectMarkers(image_map.first, corners, ids); + } + ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast(ids.size())); + double maxDistance = getMaxDistance(image_map.second, ids, corners); + ASSERT_LT(maxDistance, 3.); + SANITY_CHECK_NOTHING(); +} + +} diff --git a/modules/objdetect/src/aruco/aruco_board.cpp b/modules/objdetect/src/aruco/aruco_board.cpp index 23bf4cf77f..370d50dd29 100644 --- a/modules/objdetect/src/aruco/aruco_board.cpp +++ b/modules/objdetect/src/aruco/aruco_board.cpp @@ -3,6 +3,8 @@ // of this distribution and at http://opencv.org/license.html #include "../precomp.hpp" +#include "opencv2/objdetect/aruco_board.hpp" + #include #include @@ -10,72 +12,60 @@ namespace cv { namespace aruco { using namespace std; -struct Board::BoardImpl { - std::vector > objPoints; +struct Board::Impl { Dictionary dictionary; - Point3f rightBottomBorder; std::vector ids; + std::vector > objPoints; + Point3f rightBottomBorder; - BoardImpl() { - dictionary = Dictionary(getPredefinedDictionary(PredefinedDictionaryType::DICT_4X4_50)); - } + explicit Impl(const Dictionary& _dictionary): + dictionary(_dictionary) + {} + + virtual ~Impl() {} + + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + virtual void matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray _objPoints, + OutputArray imgPoints) const; + + virtual void generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const; }; -Board::Board(): boardImpl(makePtr()) {} +void Board::Impl::matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray _objPoints, + OutputArray imgPoints) const { -Board::~Board() {} + CV_Assert(ids.size() == objPoints.size()); + CV_Assert(detectedIds.total() == detectedCorners.total()); -Ptr Board::create(InputArrayOfArrays objPoints, const Dictionary &dictionary, InputArray ids) { - CV_Assert(objPoints.total() == ids.total()); - CV_Assert(objPoints.type() == CV_32FC3 || objPoints.type() == CV_32FC1); + size_t nDetectedMarkers = detectedIds.total(); - vector > obj_points_vector; - Point3f rightBottomBorder = Point3f(0.f, 0.f, 0.f); - for (unsigned int i = 0; i < objPoints.total(); i++) { - vector corners; - Mat corners_mat = objPoints.getMat(i); + vector objPnts; + objPnts.reserve(nDetectedMarkers); - if (corners_mat.type() == CV_32FC1) - corners_mat = corners_mat.reshape(3); - CV_Assert(corners_mat.total() == 4); + vector imgPnts; + imgPnts.reserve(nDetectedMarkers); - for (int j = 0; j < 4; j++) { - const Point3f &corner = corners_mat.at(j); - corners.push_back(corner); - rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); - rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); - rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); + // look for detected markers that belong to the board and get their information + for(unsigned int i = 0; i < nDetectedMarkers; i++) { + int currentId = detectedIds.getMat().ptr< int >(0)[i]; + for(unsigned int j = 0; j < ids.size(); j++) { + if(currentId == ids[j]) { + for(int p = 0; p < 4; p++) { + objPnts.push_back(objPoints[j][p]); + imgPnts.push_back(detectedCorners.getMat(i).ptr(0)[p]); + } + } } - obj_points_vector.push_back(corners); } - Board board; - Ptr res = makePtr(board); - ids.copyTo(res->boardImpl->ids); - res->boardImpl->objPoints = obj_points_vector; - res->boardImpl->dictionary = dictionary; - res->boardImpl->rightBottomBorder = rightBottomBorder; - return res; + + // create output + Mat(objPnts).copyTo(_objPoints); + Mat(imgPnts).copyTo(imgPoints); } -const Dictionary& Board::getDictionary() const { - return this->boardImpl->dictionary; -} - -const vector >& Board::getObjPoints() const { - return this->boardImpl->objPoints; -} - -const Point3f& Board::getRightBottomCorner() const { - return this->boardImpl->rightBottomBorder; -} - -const vector& Board::getIds() const { - return this->boardImpl->ids; -} - -/** @brief Implementation of draw planar board that accepts a raw Board pointer. - */ -void Board::generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const { +void Board::Impl::generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const { CV_Assert(!outSize.empty()); CV_Assert(marginSize >= 0); @@ -85,17 +75,17 @@ void Board::generateImage(Size outSize, OutputArray img, int marginSize, int bor out.adjustROI(-marginSize, -marginSize, -marginSize, -marginSize); // calculate max and min values in XY plane - CV_Assert(this->getObjPoints().size() > 0); + CV_Assert(objPoints.size() > 0); float minX, maxX, minY, maxY; - minX = maxX = this->getObjPoints()[0][0].x; - minY = maxY = this->getObjPoints()[0][0].y; + minX = maxX = objPoints[0][0].x; + minY = maxY = objPoints[0][0].y; - for(unsigned int i = 0; i < this->getObjPoints().size(); i++) { + for(unsigned int i = 0; i < objPoints.size(); i++) { for(int j = 0; j < 4; j++) { - minX = min(minX, this->getObjPoints()[i][j].x); - maxX = max(maxX, this->getObjPoints()[i][j].x); - minY = min(minY, this->getObjPoints()[i][j].y); - maxY = max(maxY, this->getObjPoints()[i][j].y); + minX = min(minX, objPoints[i][j].x); + maxX = max(maxX, objPoints[i][j].x); + minY = min(minY, objPoints[i][j].y); + maxY = max(maxY, objPoints[i][j].y); } } @@ -121,10 +111,10 @@ void Board::generateImage(Size outSize, OutputArray img, int marginSize, int bor Mat marker; Point2f outCorners[3]; Point2f inCorners[3]; - for(unsigned int m = 0; m < this->getObjPoints().size(); m++) { + for(unsigned int m = 0; m < objPoints.size(); m++) { // transform corners to markerZone coordinates for(int j = 0; j < 3; j++) { - Point2f pf = Point2f(this->getObjPoints()[m][j].x, this->getObjPoints()[m][j].y); + Point2f pf = Point2f(objPoints[m][j].x, objPoints[m][j].y); // move top left to 0, 0 pf -= Point2f(minX, minY); pf.x = pf.x / sizeX * float(out.cols); @@ -135,7 +125,7 @@ void Board::generateImage(Size outSize, OutputArray img, int marginSize, int bor // get marker Size dst_sz(outCorners[2] - outCorners[0]); // assuming CCW order dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square - getDictionary().generateImageMarker(this->getIds()[m], dst_sz.width, marker, borderBits); + dictionary.generateImageMarker(ids[m], dst_sz.width, marker, borderBits); if((outCorners[0].y == outCorners[1].y) && (outCorners[1].x == outCorners[2].x)) { // marker is aligned to image axes @@ -155,70 +145,119 @@ void Board::generateImage(Size outSize, OutputArray img, int marginSize, int bor } } -void Board::matchImagePoints(InputArray detectedCorners, InputArray detectedIds, - OutputArray _objPoints, OutputArray imgPoints) const { - CV_Assert(getIds().size() == getObjPoints().size()); - CV_Assert(detectedIds.total() == detectedCorners.total()); - - size_t nDetectedMarkers = detectedIds.total(); - - vector objPnts; - objPnts.reserve(nDetectedMarkers); - - vector imgPnts; - imgPnts.reserve(nDetectedMarkers); - - // look for detected markers that belong to the board and get their information - for(unsigned int i = 0; i < nDetectedMarkers; i++) { - int currentId = detectedIds.getMat().ptr< int >(0)[i]; - for(unsigned int j = 0; j < getIds().size(); j++) { - if(currentId == getIds()[j]) { - for(int p = 0; p < 4; p++) { - objPnts.push_back(getObjPoints()[j][p]); - imgPnts.push_back(detectedCorners.getMat(i).ptr(0)[p]); - } - } - } - } - - // create output - Mat(objPnts).copyTo(_objPoints); - Mat(imgPnts).copyTo(imgPoints); +Board::Board(const Ptr& _impl): + impl(_impl) +{ + CV_Assert(impl); } -struct GridBoard::GridImpl { - GridImpl(){}; +Board::Board(): + impl(nullptr) +{} + +Board::Board(InputArrayOfArrays objPoints, const Dictionary &dictionary, InputArray ids): + Board(new Board::Impl(dictionary)) { + CV_Assert(ids.size() == objPoints.size()); + CV_Assert(objPoints.total() == ids.total()); + CV_Assert(objPoints.type() == CV_32FC3 || objPoints.type() == CV_32FC1); + + vector > obj_points_vector; + Point3f rightBottomBorder = Point3f(0.f, 0.f, 0.f); + for (unsigned int i = 0; i < objPoints.total(); i++) { + vector corners; + Mat corners_mat = objPoints.getMat(i); + + if (corners_mat.type() == CV_32FC1) + corners_mat = corners_mat.reshape(3); + CV_Assert(corners_mat.total() == 4); + + for (int j = 0; j < 4; j++) { + const Point3f &corner = corners_mat.at(j); + corners.push_back(corner); + rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); + rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); + rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); + } + obj_points_vector.push_back(corners); + } + + ids.copyTo(impl->ids); + impl->objPoints = obj_points_vector; + impl->rightBottomBorder = rightBottomBorder; +} + +const Dictionary& Board::getDictionary() const { + CV_Assert(this->impl); + return this->impl->dictionary; +} + +const vector >& Board::getObjPoints() const { + CV_Assert(this->impl); + return this->impl->objPoints; +} + +const Point3f& Board::getRightBottomCorner() const { + CV_Assert(this->impl); + return this->impl->rightBottomBorder; +} + +const vector& Board::getIds() const { + CV_Assert(this->impl); + return this->impl->ids; +} + +/** @brief Implementation of draw planar board that accepts a raw Board pointer. + */ +void Board::generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const { + CV_Assert(this->impl); + impl->generateImage(outSize, img, marginSize, borderBits); +} + +void Board::matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray objPoints, + OutputArray imgPoints) const { + CV_Assert(this->impl); + impl->matchImagePoints(detectedCorners, detectedIds, objPoints, imgPoints); +} + +struct GridBoardImpl : public Board::Impl { + GridBoardImpl(const Dictionary& _dictionary, const Size& _size, float _markerLength, float _markerSeparation): + Board::Impl(_dictionary), + size(_size), + markerLength(_markerLength), + markerSeparation(_markerSeparation) + { + CV_Assert(size.width*size.height > 0 && markerLength > 0 && markerSeparation > 0); + } + // number of markers in X and Y directions - int sizeX = 3, sizeY = 3; - + const Size size; // marker side length (normally in meters) - float markerLength = 1.f; - + float markerLength; // separation between markers in the grid - float markerSeparation = .5f; + float markerSeparation; }; -GridBoard::GridBoard(): gridImpl(makePtr()) {} +GridBoard::GridBoard() {} -Ptr GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, - const Dictionary &dictionary, InputArray ids) { - CV_Assert(markersX > 0 && markersY > 0 && markerLength > 0 && markerSeparation > 0); - GridBoard board; - Ptr res = makePtr(board); - res->gridImpl->sizeX = markersX; - res->gridImpl->sizeY = markersY; - res->gridImpl->markerLength = markerLength; - res->gridImpl->markerSeparation = markerSeparation; - res->boardImpl->dictionary = dictionary; +GridBoard::GridBoard(const Size& size, float markerLength, float markerSeparation, + const Dictionary &dictionary, InputArray ids): + Board(new GridBoardImpl(dictionary, size, markerLength, markerSeparation)) { - size_t totalMarkers = (size_t) markersX * markersY; - CV_Assert(totalMarkers == ids.total()); + size_t totalMarkers = (size_t) size.width*size.height; + CV_Assert(ids.empty() || totalMarkers == ids.total()); vector > objPoints; objPoints.reserve(totalMarkers); - ids.copyTo(res->boardImpl->ids); + + if(!ids.empty()) { + ids.copyTo(impl->ids); + } else { + impl->ids = std::vector(totalMarkers); + std::iota(impl->ids.begin(), impl->ids.end(), 0); + } + // calculate Board objPoints - for (int y = 0; y < markersY; y++) { - for (int x = 0; x < markersX; x++) { + for (int y = 0; y < size.height; y++) { + for (int x = 0; x < size.width; x++) { vector corners(4); corners[0] = Point3f(x * (markerLength + markerSeparation), y * (markerLength + markerSeparation), 0); @@ -228,67 +267,141 @@ Ptr GridBoard::create(int markersX, int markersY, float markerLength, objPoints.push_back(corners); } } - res->boardImpl->objPoints = objPoints; - res->boardImpl->rightBottomBorder = Point3f(markersX * markerLength + markerSeparation * (markersX - 1), - markersY * markerLength + markerSeparation * (markersY - 1), 0.f); - return res; -} - -Ptr GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, - const Dictionary &dictionary, int firstMarker) { - vector ids(markersX*markersY); - std::iota(ids.begin(), ids.end(), firstMarker); - return GridBoard::create(markersX, markersY, markerLength, markerSeparation, dictionary, ids); -} - -void GridBoard::generateImage(Size outSize, OutputArray _img, int marginSize, int borderBits) const { - Board::generateImage(outSize, _img, marginSize, borderBits); + impl->objPoints = objPoints; + impl->rightBottomBorder = Point3f(size.width * markerLength + markerSeparation * (size.width - 1), + size.height * markerLength + markerSeparation * (size.height - 1), 0.f); } Size GridBoard::getGridSize() const { - return Size(gridImpl->sizeX, gridImpl->sizeY); + CV_Assert(impl); + return static_pointer_cast(impl)->size; } float GridBoard::getMarkerLength() const { - return gridImpl->markerLength; + CV_Assert(impl); + return static_pointer_cast(impl)->markerLength; } float GridBoard::getMarkerSeparation() const { - return gridImpl->markerSeparation; + CV_Assert(impl); + return static_pointer_cast(impl)->markerSeparation; } -struct CharucoBoard::CharucoImpl : GridBoard::GridImpl { - // size of chessboard squares side (normally in meters) +struct CharucoBoardImpl : Board::Impl { + CharucoBoardImpl(const Dictionary& _dictionary, const Size& _size, float _squareLength, float _markerLength): + Board::Impl(_dictionary), + size(_size), + squareLength(_squareLength), + markerLength(_markerLength) + {} + + // chessboard size + Size size; + + // Physical size of chessboard squares side (normally in meters) float squareLength; - // marker side length (normally in meters) + // Physical marker side length (normally in meters) float markerLength; - static void _getNearestMarkerCorners(CharucoBoard &board, float squareLength); - // vector of chessboard 3D corners precalculated std::vector chessboardCorners; // for each charuco corner, nearest marker id and nearest marker corner id of each marker std::vector > nearestMarkerIdx; std::vector > nearestMarkerCorners; + + void calcNearestMarkerCorners(); + + void matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, + OutputArray objPoints, OutputArray imgPoints) const override; + + void generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const override; }; -CharucoBoard::CharucoBoard(): charucoImpl(makePtr()) {} +/** Fill nearestMarkerIdx and nearestMarkerCorners arrays */ +void CharucoBoardImpl::calcNearestMarkerCorners() { + nearestMarkerIdx.resize(chessboardCorners.size()); + nearestMarkerCorners.resize(chessboardCorners.size()); + unsigned int nMarkers = (unsigned int)objPoints.size(); + unsigned int nCharucoCorners = (unsigned int)chessboardCorners.size(); + for(unsigned int i = 0; i < nCharucoCorners; i++) { + double minDist = -1; // distance of closest markers + Point3f charucoCorner = chessboardCorners[i]; + for(unsigned int j = 0; j < nMarkers; j++) { + // calculate distance from marker center to charuco corner + Point3f center = Point3f(0, 0, 0); + for(unsigned int k = 0; k < 4; k++) + center += objPoints[j][k]; + center /= 4.; + double sqDistance; + Point3f distVector = charucoCorner - center; + sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; + if(j == 0 || fabs(sqDistance - minDist) < cv::pow(0.01 * squareLength, 2)) { + // if same minimum distance (or first iteration), add to nearestMarkerIdx vector + nearestMarkerIdx[i].push_back(j); + minDist = sqDistance; + } else if(sqDistance < minDist) { + // if finding a closest marker to the charuco corner + nearestMarkerIdx[i].clear(); // remove any previous added marker + nearestMarkerIdx[i].push_back(j); // add the new closest marker index + minDist = sqDistance; + } + } + // for each of the closest markers, search the marker corner index closer + // to the charuco corner + for(unsigned int j = 0; j < nearestMarkerIdx[i].size(); j++) { + nearestMarkerCorners[i].resize(nearestMarkerIdx[i].size()); + double minDistCorner = -1; + for(unsigned int k = 0; k < 4; k++) { + double sqDistance; + Point3f distVector = charucoCorner - objPoints[nearestMarkerIdx[i][j]][k]; + sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; + if(k == 0 || sqDistance < minDistCorner) { + // if this corner is closer to the charuco corner, assing its index + // to nearestMarkerCorners + minDistCorner = sqDistance; + nearestMarkerCorners[i][j] = k; + } + } + } + } +} -void CharucoBoard::generateImage(Size outSize, OutputArray _img, int marginSize, int borderBits) const { +void CharucoBoardImpl::matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, + OutputArray _objPoints, OutputArray imgPoints) const { + if (detectedCorners.kind() == _InputArray::STD_VECTOR_VECTOR || + detectedCorners.isMatVector() || detectedCorners.isUMatVector()) + Board::Impl::matchImagePoints(detectedCorners, detectedIds, _objPoints, imgPoints); + else { + CV_Assert(detectedCorners.isMat() || detectedCorners.isVector()); + size_t nDetected = detectedCorners.total(); + vector objPnts(nDetected); + vector imgPnts(nDetected); + for(size_t i = 0ull; i < nDetected; i++) { + int pointId = detectedIds.getMat().at((int)i); + CV_Assert(pointId >= 0 && pointId < (int)chessboardCorners.size()); + objPnts[i] = chessboardCorners[pointId]; + imgPnts[i] = detectedCorners.getMat().at((int)i); + } + Mat(objPnts).copyTo(_objPoints); + Mat(imgPnts).copyTo(imgPoints); + } +} + +void CharucoBoardImpl::generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const { CV_Assert(!outSize.empty()); CV_Assert(marginSize >= 0); - _img.create(outSize, CV_8UC1); - _img.setTo(255); - Mat out = _img.getMat(); + img.create(outSize, CV_8UC1); + img.setTo(255); + Mat out = img.getMat(); Mat noMarginsImg = out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize); double totalLengthX, totalLengthY; - totalLengthX = charucoImpl->squareLength * charucoImpl->sizeX; - totalLengthY = charucoImpl->squareLength * charucoImpl->sizeY; + totalLengthX = squareLength * size.width; + totalLengthY = squareLength * size.height; // proportional transformation double xReduction = totalLengthX / double(noMarginsImg.cols); @@ -308,21 +421,21 @@ void CharucoBoard::generateImage(Size outSize, OutputArray _img, int marginSize, // determine the margins to draw only the markers // take the minimum just to be sure - double squareSizePixels = min(double(chessboardZoneImg.cols) / double(charucoImpl->sizeX), - double(chessboardZoneImg.rows) / double(charucoImpl->sizeY)); + double squareSizePixels = min(double(chessboardZoneImg.cols) / double(size.width), + double(chessboardZoneImg.rows) / double(size.height)); - double diffSquareMarkerLength = (charucoImpl->squareLength - charucoImpl->markerLength) / 2; + double diffSquareMarkerLength = (squareLength - markerLength) / 2; int diffSquareMarkerLengthPixels = - int(diffSquareMarkerLength * squareSizePixels / charucoImpl->squareLength); + int(diffSquareMarkerLength * squareSizePixels / squareLength); // draw markers Mat markersImg; - Board::generateImage(chessboardZoneImg.size(), markersImg, diffSquareMarkerLengthPixels, borderBits); + Board::Impl::generateImage(chessboardZoneImg.size(), markersImg, diffSquareMarkerLengthPixels, borderBits); markersImg.copyTo(chessboardZoneImg); // now draw black squares - for(int y = 0; y < charucoImpl->sizeY; y++) { - for(int x = 0; x < charucoImpl->sizeX; x++) { + for(int y = 0; y < size.height; y++) { + for(int x = 0; x < size.width; x++) { if(y % 2 != x % 2) continue; // white corner, dont do anything @@ -338,78 +451,22 @@ void CharucoBoard::generateImage(Size outSize, OutputArray _img, int marginSize, } } -/** - * Fill nearestMarkerIdx and nearestMarkerCorners arrays - */ -void CharucoBoard::CharucoImpl::_getNearestMarkerCorners(CharucoBoard &board, float squareLength) { - board.charucoImpl->nearestMarkerIdx.resize(board.charucoImpl->chessboardCorners.size()); - board.charucoImpl->nearestMarkerCorners.resize(board.charucoImpl->chessboardCorners.size()); +CharucoBoard::CharucoBoard(){} - unsigned int nMarkers = (unsigned int)board.getIds().size(); - unsigned int nCharucoCorners = (unsigned int)board.charucoImpl->chessboardCorners.size(); - for(unsigned int i = 0; i < nCharucoCorners; i++) { - double minDist = -1; // distance of closest markers - Point3f charucoCorner = board.charucoImpl->chessboardCorners[i]; - for(unsigned int j = 0; j < nMarkers; j++) { - // calculate distance from marker center to charuco corner - Point3f center = Point3f(0, 0, 0); - for(unsigned int k = 0; k < 4; k++) - center += board.getObjPoints()[j][k]; - center /= 4.; - double sqDistance; - Point3f distVector = charucoCorner - center; - sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; - if(j == 0 || fabs(sqDistance - minDist) < cv::pow(0.01 * squareLength, 2)) { - // if same minimum distance (or first iteration), add to nearestMarkerIdx vector - board.charucoImpl->nearestMarkerIdx[i].push_back(j); - minDist = sqDistance; - } else if(sqDistance < minDist) { - // if finding a closest marker to the charuco corner - board.charucoImpl->nearestMarkerIdx[i].clear(); // remove any previous added marker - board.charucoImpl->nearestMarkerIdx[i].push_back(j); // add the new closest marker index - minDist = sqDistance; - } - } - // for each of the closest markers, search the marker corner index closer - // to the charuco corner - for(unsigned int j = 0; j < board.charucoImpl->nearestMarkerIdx[i].size(); j++) { - board.charucoImpl->nearestMarkerCorners[i].resize(board.charucoImpl->nearestMarkerIdx[i].size()); - double minDistCorner = -1; - for(unsigned int k = 0; k < 4; k++) { - double sqDistance; - Point3f distVector = charucoCorner - board.getObjPoints()[board.charucoImpl->nearestMarkerIdx[i][j]][k]; - sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; - if(k == 0 || sqDistance < minDistCorner) { - // if this corner is closer to the charuco corner, assing its index - // to nearestMarkerCorners - minDistCorner = sqDistance; - board.charucoImpl->nearestMarkerCorners[i][j] = k; - } - } - } - } -} +CharucoBoard::CharucoBoard(const Size& size, float squareLength, float markerLength, + const Dictionary &dictionary, InputArray ids): + Board(new CharucoBoardImpl(dictionary, size, squareLength, markerLength)) { -Ptr CharucoBoard::create(int squaresX, int squaresY, float squareLength, float markerLength, - const Dictionary &dictionary, InputArray ids) { - CV_Assert(squaresX > 1 && squaresY > 1 && markerLength > 0 && squareLength > markerLength); - CharucoBoard board; - Ptr res = makePtr(board); + CV_Assert(size.width > 1 && size.height > 1 && markerLength > 0 && squareLength > markerLength); - res->charucoImpl->sizeX = squaresX; - res->charucoImpl->sizeY = squaresY; - res->charucoImpl->squareLength = squareLength; - res->charucoImpl->markerLength = markerLength; - res->boardImpl->dictionary = dictionary; vector > objPoints; - float diffSquareMarkerLength = (squareLength - markerLength) / 2; int totalMarkers = (int)(ids.total()); - ids.copyTo(res->boardImpl->ids); + ids.copyTo(impl->ids); // calculate Board objPoints int nextId = 0; - for(int y = 0; y < squaresY; y++) { - for(int x = 0; x < squaresX; x++) { + for(int y = 0; y < size.height; y++) { + for(int x = 0; x < size.width; x++) { if(y % 2 == x % 2) continue; // black corner, no marker here @@ -422,48 +479,60 @@ Ptr CharucoBoard::create(int squaresX, int squaresY, float squareL objPoints.push_back(corners); // first ids in dictionary if (totalMarkers == 0) - res->boardImpl->ids.push_back(nextId); + impl->ids.push_back(nextId); nextId++; } } if (totalMarkers > 0 && nextId != totalMarkers) CV_Error(cv::Error::StsBadSize, "Size of ids must be equal to the number of markers: "+std::to_string(nextId)); - res->boardImpl->objPoints = objPoints; + impl->objPoints = objPoints; // now fill chessboardCorners - for(int y = 0; y < squaresY - 1; y++) { - for(int x = 0; x < squaresX - 1; x++) { + std::vector & c = static_pointer_cast(impl)->chessboardCorners; + for(int y = 0; y < size.height - 1; y++) { + for(int x = 0; x < size.width - 1; x++) { Point3f corner; corner.x = (x + 1) * squareLength; corner.y = (y + 1) * squareLength; corner.z = 0; - res->charucoImpl->chessboardCorners.push_back(corner); + c.push_back(corner); } } - res->boardImpl->rightBottomBorder = Point3f(squaresX * squareLength, squaresY * squareLength, 0.f); - CharucoBoard::CharucoImpl::_getNearestMarkerCorners(*res, res->charucoImpl->squareLength); - return res; + impl->rightBottomBorder = Point3f(size.width * squareLength, size.height * squareLength, 0.f); + static_pointer_cast(impl)->calcNearestMarkerCorners(); } -Size CharucoBoard::getChessboardSize() const { return Size(charucoImpl->sizeX, charucoImpl->sizeY); } +Size CharucoBoard::getChessboardSize() const { + CV_Assert(impl); + return static_pointer_cast(impl)->size; +} -float CharucoBoard::getSquareLength() const { return charucoImpl->squareLength; } +float CharucoBoard::getSquareLength() const { + CV_Assert(impl); + return static_pointer_cast(impl)->squareLength; +} -float CharucoBoard::getMarkerLength() const { return charucoImpl->markerLength; } +float CharucoBoard::getMarkerLength() const { + CV_Assert(impl); + return static_pointer_cast(impl)->markerLength; +} bool CharucoBoard::checkCharucoCornersCollinear(InputArray charucoIds) const { + CV_Assert(impl); + unsigned int nCharucoCorners = (unsigned int)charucoIds.getMat().total(); if (nCharucoCorners <= 2) return true; // only test if there are 3 or more corners - CV_Assert(charucoImpl->chessboardCorners.size() >= charucoIds.getMat().total()); + auto board = static_pointer_cast(impl); + CV_Assert(board->chessboardCorners.size() >= charucoIds.getMat().total()); - Vec point0(charucoImpl->chessboardCorners[charucoIds.getMat().at(0)].x, - charucoImpl->chessboardCorners[charucoIds.getMat().at(0)].y, 1); + Vec point0(board->chessboardCorners[charucoIds.getMat().at(0)].x, + board->chessboardCorners[charucoIds.getMat().at(0)].y, 1); - Vec point1(charucoImpl->chessboardCorners[charucoIds.getMat().at(1)].x, - charucoImpl->chessboardCorners[charucoIds.getMat().at(1)].y, 1); + Vec point1(board->chessboardCorners[charucoIds.getMat().at(1)].x, + board->chessboardCorners[charucoIds.getMat().at(1)].y, 1); // create a line from the first two points. Vec testLine = point0.cross(point1); @@ -477,8 +546,8 @@ bool CharucoBoard::checkCharucoCornersCollinear(InputArray charucoIds) const { double dotProduct; for (unsigned int i = 2; i < nCharucoCorners; i++){ - testPoint(0) = charucoImpl->chessboardCorners[charucoIds.getMat().at(i)].x; - testPoint(1) = charucoImpl->chessboardCorners[charucoIds.getMat().at(i)].y; + testPoint(0) = board->chessboardCorners[charucoIds.getMat().at(i)].x; + testPoint(1) = board->chessboardCorners[charucoIds.getMat().at(i)].y; // if testPoint is on testLine, dotProduct will be zero (or very, very close) dotProduct = testPoint.dot(testLine); @@ -492,15 +561,18 @@ bool CharucoBoard::checkCharucoCornersCollinear(InputArray charucoIds) const { } std::vector CharucoBoard::getChessboardCorners() const { - return charucoImpl->chessboardCorners; + CV_Assert(impl); + return static_pointer_cast(impl)->chessboardCorners; } std::vector > CharucoBoard::getNearestMarkerIdx() const { - return charucoImpl->nearestMarkerIdx; + CV_Assert(impl); + return static_pointer_cast(impl)->nearestMarkerIdx; } std::vector > CharucoBoard::getNearestMarkerCorners() const { - return charucoImpl->nearestMarkerCorners; + CV_Assert(impl); + return static_pointer_cast(impl)->nearestMarkerCorners; } } diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index 4ef4710b01..bd9dcfbb15 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -856,7 +856,7 @@ ArucoDetector::ArucoDetector(const Dictionary &_dictionary, } void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, - OutputArrayOfArrays _rejectedImgPoints) { + OutputArrayOfArrays _rejectedImgPoints) const { CV_Assert(!_image.empty()); DetectorParameters& detectorParams = arucoDetectorImpl->detectorParams; const Dictionary& dictionary = arucoDetectorImpl->dictionary; @@ -994,37 +994,25 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner /** * Project board markers that are not included in the list of detected markers */ -static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArrayOfArrays _detectedCorners, - InputOutputArray _detectedIds, InputArray _cameraMatrix, InputArray _distCoeffs, - vector >& _undetectedMarkersProjectedCorners, - OutputArray _undetectedMarkersIds) { - // first estimate board pose with the current avaible markers - Mat rvec, tvec; - int boardDetectedMarkers = 0; - { - CV_Assert(_detectedCorners.total() == _detectedIds.total()); - // get object and image points for the solvePnP function - Mat detectedObjPoints, imgPoints; - _board->matchImagePoints(_detectedCorners, _detectedIds, detectedObjPoints, imgPoints); - CV_Assert(imgPoints.total() == detectedObjPoints.total()); - if(detectedObjPoints.total() > 0) // 0 of the detected markers in board - { - solvePnP(detectedObjPoints, imgPoints, _cameraMatrix, _distCoeffs, rvec, tvec); - // divide by four since all the four corners are concatenated in the array for each marker - boardDetectedMarkers = static_cast(detectedObjPoints.total()) / 4; - } - } - - // at least one marker from board so rvec and tvec are valid - if(boardDetectedMarkers == 0) return; +static inline void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArrays detectedCorners, + InputOutputArray detectedIds, InputArray cameraMatrix, InputArray distCoeffs, + vector >& undetectedMarkersProjectedCorners, + OutputArray undetectedMarkersIds) { + Mat rvec, tvec; // first estimate board pose with the current avaible markers + Mat objPoints, imgPoints; // object and image points for the solvePnP function + board.matchImagePoints(detectedCorners, detectedIds, objPoints, imgPoints); + if (objPoints.total() < 4ull) // at least one marker from board so rvec and tvec are valid + return; + solvePnP(objPoints, imgPoints, cameraMatrix, distCoeffs, rvec, tvec); // search undetected markers and project them using the previous pose vector > undetectedCorners; + const std::vector& ids = board.getIds(); vector undetectedIds; - for(unsigned int i = 0; i < _board->getIds().size(); i++) { + for(unsigned int i = 0; i < ids.size(); i++) { int foundIdx = -1; - for(unsigned int j = 0; j < _detectedIds.total(); j++) { - if(_board->getIds()[i] == _detectedIds.getMat().ptr()[j]) { + for(unsigned int j = 0; j < detectedIds.total(); j++) { + if(ids[i] == detectedIds.getMat().ptr()[j]) { foundIdx = j; break; } @@ -1033,31 +1021,31 @@ static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArray // not detected if(foundIdx == -1) { undetectedCorners.push_back(vector()); - undetectedIds.push_back(_board->getIds()[i]); - projectPoints(_board->getObjPoints()[i], rvec, tvec, _cameraMatrix, _distCoeffs, + undetectedIds.push_back(ids[i]); + projectPoints(board.getObjPoints()[i], rvec, tvec, cameraMatrix, distCoeffs, undetectedCorners.back()); } } // parse output - Mat(undetectedIds).copyTo(_undetectedMarkersIds); - _undetectedMarkersProjectedCorners = undetectedCorners; + Mat(undetectedIds).copyTo(undetectedMarkersIds); + undetectedMarkersProjectedCorners = undetectedCorners; } /** * Interpolate board markers that are not included in the list of detected markers using * global homography */ -static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArrayOfArrays _detectedCorners, +static void _projectUndetectedMarkers(const Board &_board, InputOutputArrayOfArrays _detectedCorners, InputOutputArray _detectedIds, vector >& _undetectedMarkersProjectedCorners, OutputArray _undetectedMarkersIds) { // check board points are in the same plane, if not, global homography cannot be applied - CV_Assert(_board->getObjPoints().size() > 0); - CV_Assert(_board->getObjPoints()[0].size() > 0); - float boardZ = _board->getObjPoints()[0][0].z; - for(unsigned int i = 0; i < _board->getObjPoints().size(); i++) { - for(unsigned int j = 0; j < _board->getObjPoints()[i].size(); j++) - CV_Assert(boardZ == _board->getObjPoints()[i][j].z); + CV_Assert(_board.getObjPoints().size() > 0); + CV_Assert(_board.getObjPoints()[0].size() > 0); + float boardZ = _board.getObjPoints()[0][0].z; + for(unsigned int i = 0; i < _board.getObjPoints().size(); i++) { + for(unsigned int j = 0; j < _board.getObjPoints()[i].size(); j++) + CV_Assert(boardZ == _board.getObjPoints()[i][j].z); } vector detectedMarkersObj2DAll; // Object coordinates (without Z) of all the detected @@ -1067,14 +1055,14 @@ static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArray // missing markers in different vectors vector undetectedMarkersIds; // ids of missing markers // find markers included in board, and missing markers from board. Fill the previous vectors - for(unsigned int j = 0; j < _board->getIds().size(); j++) { + for(unsigned int j = 0; j < _board.getIds().size(); j++) { bool found = false; for(unsigned int i = 0; i < _detectedIds.total(); i++) { - if(_detectedIds.getMat().ptr()[i] == _board->getIds()[j]) { + if(_detectedIds.getMat().ptr()[i] == _board.getIds()[j]) { for(int c = 0; c < 4; c++) { imageCornersAll.push_back(_detectedCorners.getMat(i).ptr()[c]); detectedMarkersObj2DAll.push_back( - Point2f(_board->getObjPoints()[j][c].x, _board->getObjPoints()[j][c].y)); + Point2f(_board.getObjPoints()[j][c].x, _board.getObjPoints()[j][c].y)); } found = true; break; @@ -1084,9 +1072,9 @@ static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArray undetectedMarkersObj2D.push_back(vector()); for(int c = 0; c < 4; c++) { undetectedMarkersObj2D.back().push_back( - Point2f(_board->getObjPoints()[j][c].x, _board->getObjPoints()[j][c].y)); + Point2f(_board.getObjPoints()[j][c].x, _board.getObjPoints()[j][c].y)); } - undetectedMarkersIds.push_back(_board->getIds()[j]); + undetectedMarkersIds.push_back(_board.getIds()[j]); } } if(imageCornersAll.size() == 0) return; @@ -1103,10 +1091,10 @@ static void _projectUndetectedMarkers(const Ptr &_board, InputOutputArray Mat(undetectedMarkersIds).copyTo(_undetectedMarkersIds); } -void ArucoDetector::refineDetectedMarkers(InputArray _image, const Ptr &_board, +void ArucoDetector::refineDetectedMarkers(InputArray _image, const Board& _board, InputOutputArrayOfArrays _detectedCorners, InputOutputArray _detectedIds, InputOutputArrayOfArrays _rejectedCorners, InputArray _cameraMatrix, - InputArray _distCoeffs, OutputArray _recoveredIdxs) { + InputArray _distCoeffs, OutputArray _recoveredIdxs) const { DetectorParameters& detectorParams = arucoDetectorImpl->detectorParams; const Dictionary& dictionary = arucoDetectorImpl->dictionary; RefineParameters& refineParams = arucoDetectorImpl->refineParams; diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp new file mode 100644 index 0000000000..3955a8f236 --- /dev/null +++ b/modules/objdetect/src/aruco/charuco_detector.cpp @@ -0,0 +1,521 @@ +// 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" + +#include +#include "opencv2/objdetect/charuco_detector.hpp" +#include "aruco_utils.hpp" + +namespace cv { +namespace aruco { + +using namespace std; + +struct CharucoDetector::CharucoDetectorImpl { + CharucoBoard board; + CharucoParameters charucoParameters; + ArucoDetector arucoDetector; + + CharucoDetectorImpl(const CharucoBoard& _board, const CharucoParameters _charucoParameters, + const ArucoDetector& _arucoDetector): board(_board), charucoParameters(_charucoParameters), + arucoDetector(_arucoDetector) + {} + + /** Calculate the maximum window sizes for corner refinement for each charuco corner based on the distance + * to their closest markers */ + vector getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, InputArray markerIds, + InputArray charucoCorners) { + size_t nCharucoCorners = charucoCorners.getMat().total(); + + CV_Assert(board.getNearestMarkerIdx().size() == nCharucoCorners); + + vector winSizes(nCharucoCorners, Size(-1, -1)); + for(size_t i = 0ull; i < nCharucoCorners; i++) { + if(charucoCorners.getMat().at((int)i) == Point2f(-1.f, -1.f)) continue; + if(board.getNearestMarkerIdx()[i].empty()) continue; + double minDist = -1; + int counter = 0; + // calculate the distance to each of the closest corner of each closest marker + for(size_t j = 0; j < board.getNearestMarkerIdx()[i].size(); j++) { + // find marker + int markerId = board.getIds()[board.getNearestMarkerIdx()[i][j]]; + int markerIdx = -1; + for(size_t k = 0; k < markerIds.getMat().total(); k++) { + if(markerIds.getMat().at((int)k) == markerId) { + markerIdx = (int)k; + break; + } + } + if(markerIdx == -1) continue; + Point2f markerCorner = + markerCorners.getMat(markerIdx).at(board.getNearestMarkerCorners()[i][j]); + Point2f charucoCorner = charucoCorners.getMat().at((int)i); + double dist = norm(markerCorner - charucoCorner); + if(minDist == -1) minDist = dist; // if first distance, just assign it + minDist = min(dist, minDist); + counter++; + } + // if this is the first closest marker, dont do anything + if(counter == 0) + continue; + else { + // else, calculate the maximum window size + int winSizeInt = int(minDist - 2); // remove 2 pixels for safety + if(winSizeInt < 1) winSizeInt = 1; // minimum size is 1 + if(winSizeInt > 10) winSizeInt = 10; // maximum size is 10 + winSizes[i] = Size(winSizeInt, winSizeInt); + } + } + return winSizes; + } + + /** @brief From all projected chessboard corners, select those inside the image and apply subpixel refinement */ + void selectAndRefineChessboardCorners(InputArray allCorners, InputArray image, OutputArray selectedCorners, + OutputArray selectedIds, const vector &winSizes) { + const int minDistToBorder = 2; // minimum distance of the corner to the image border + // remaining corners, ids and window refinement sizes after removing corners outside the image + vector filteredChessboardImgPoints; + vector filteredWinSizes; + vector filteredIds; + // filter corners outside the image + Rect innerRect(minDistToBorder, minDistToBorder, image.getMat().cols - 2 * minDistToBorder, + image.getMat().rows - 2 * minDistToBorder); + for(unsigned int i = 0; i < allCorners.getMat().total(); i++) { + if(innerRect.contains(allCorners.getMat().at(i))) { + filteredChessboardImgPoints.push_back(allCorners.getMat().at(i)); + filteredIds.push_back(i); + filteredWinSizes.push_back(winSizes[i]); + } + } + // if none valid, return 0 + if(filteredChessboardImgPoints.empty()) return; + // corner refinement, first convert input image to grey + Mat grey; + if(image.type() == CV_8UC3) + cvtColor(image, grey, COLOR_BGR2GRAY); + else + grey = image.getMat(); + //// For each of the charuco corners, apply subpixel refinement using its correspondind winSize + parallel_for_(Range(0, (int)filteredChessboardImgPoints.size()), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + for (int i = begin; i < end; i++) { + vector in; + in.push_back(filteredChessboardImgPoints[i] - Point2f(0.5, 0.5)); // adjust sub-pixel coordinates for cornerSubPix + Size winSize = filteredWinSizes[i]; + if (winSize.height == -1 || winSize.width == -1) + winSize = Size(arucoDetector.getDetectorParameters().cornerRefinementWinSize, + arucoDetector.getDetectorParameters().cornerRefinementWinSize); + cornerSubPix(grey, in, winSize, Size(), + TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, + arucoDetector.getDetectorParameters().cornerRefinementMaxIterations, + arucoDetector.getDetectorParameters().cornerRefinementMinAccuracy)); + filteredChessboardImgPoints[i] = in[0] + Point2f(0.5, 0.5); + } + }); + // parse output + Mat(filteredChessboardImgPoints).copyTo(selectedCorners); + Mat(filteredIds).copyTo(selectedIds); + } + + /** Interpolate charuco corners using approximated pose estimation */ + void interpolateCornersCharucoApproxCalib(InputArrayOfArrays markerCorners, InputArray markerIds, + InputArray image, OutputArray charucoCorners, OutputArray charucoIds) { + CV_Assert(image.getMat().channels() == 1 || image.getMat().channels() == 3); + CV_Assert(markerCorners.total() == markerIds.getMat().total()); + + // approximated pose estimation using marker corners + Mat approximatedRvec, approximatedTvec; + Mat objPoints, imgPoints; // object and image points for the solvePnP function + printf("before board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);\n"); + board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints); + printf("after board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);\n"); + if (objPoints.total() < 4ull) // need, at least, 4 corners + return; + + solvePnP(objPoints, imgPoints, charucoParameters.cameraMatrix, charucoParameters.distCoeffs, approximatedRvec, approximatedTvec); + printf("after solvePnP\n"); + + // project chessboard corners + vector allChessboardImgPoints; + projectPoints(board.getChessboardCorners(), approximatedRvec, approximatedTvec, charucoParameters.cameraMatrix, + charucoParameters.distCoeffs, allChessboardImgPoints); + printf("after projectPoints\n"); + // calculate maximum window sizes for subpixel refinement. The size is limited by the distance + // to the closes marker corner to avoid erroneous displacements to marker corners + vector subPixWinSizes = getMaximumSubPixWindowSizes(markerCorners, markerIds, allChessboardImgPoints); + // filter corners outside the image and subpixel-refine charuco corners + printf("before selectAndRefineChessboardCorners\n"); + selectAndRefineChessboardCorners(allChessboardImgPoints, image, charucoCorners, charucoIds, subPixWinSizes); + } + + /** Interpolate charuco corners using local homography */ + void interpolateCornersCharucoLocalHom(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray image, + OutputArray charucoCorners, OutputArray charucoIds) { + CV_Assert(image.getMat().channels() == 1 || image.getMat().channels() == 3); + CV_Assert(markerCorners.total() == markerIds.getMat().total()); + size_t nMarkers = markerIds.getMat().total(); + // calculate local homographies for each marker + vector transformations(nMarkers); + vector validTransform(nMarkers, false); + const auto& ids = board.getIds(); + for(size_t i = 0ull; i < nMarkers; i++) { + vector markerObjPoints2D; + int markerId = markerIds.getMat().at((int)i); + auto it = find(ids.begin(), ids.end(), markerId); + if(it == ids.end()) continue; + auto boardIdx = it - ids.begin(); + markerObjPoints2D.resize(4ull); + for(size_t j = 0ull; j < 4ull; j++) + markerObjPoints2D[j] = + Point2f(board.getObjPoints()[boardIdx][j].x, board.getObjPoints()[boardIdx][j].y); + transformations[i] = getPerspectiveTransform(markerObjPoints2D, markerCorners.getMat((int)i)); + // set transform as valid if transformation is non-singular + double det = determinant(transformations[i]); + validTransform[i] = std::abs(det) > 1e-6; + } + size_t nCharucoCorners = (size_t)board.getChessboardCorners().size(); + vector allChessboardImgPoints(nCharucoCorners, Point2f(-1, -1)); + // for each charuco corner, calculate its interpolation position based on the closest markers + // homographies + for(size_t i = 0ull; i < nCharucoCorners; i++) { + Point2f objPoint2D = Point2f(board.getChessboardCorners()[i].x, board.getChessboardCorners()[i].y); + vector interpolatedPositions; + for(size_t j = 0ull; j < board.getNearestMarkerIdx()[i].size(); j++) { + int markerId = board.getIds()[board.getNearestMarkerIdx()[i][j]]; + int markerIdx = -1; + for(size_t k = 0ull; k < markerIds.getMat().total(); k++) { + if(markerIds.getMat().at((int)k) == markerId) { + markerIdx = (int)k; + break; + } + } + if (markerIdx != -1 && + validTransform[markerIdx]) + { + vector in, out; + in.push_back(objPoint2D); + perspectiveTransform(in, out, transformations[markerIdx]); + interpolatedPositions.push_back(out[0]); + } + } + // none of the closest markers detected + if(interpolatedPositions.empty()) continue; + // more than one closest marker detected, take middle point + if(interpolatedPositions.size() > 1ull) { + allChessboardImgPoints[i] = (interpolatedPositions[0] + interpolatedPositions[1]) / 2.; + } + // a single closest marker detected + else allChessboardImgPoints[i] = interpolatedPositions[0]; + } + // calculate maximum window sizes for subpixel refinement. The size is limited by the distance + // to the closes marker corner to avoid erroneous displacements to marker corners + vector subPixWinSizes = getMaximumSubPixWindowSizes(markerCorners, markerIds, allChessboardImgPoints); + // filter corners outside the image and subpixel-refine charuco corners + selectAndRefineChessboardCorners(allChessboardImgPoints, image, charucoCorners, charucoIds, subPixWinSizes); + } + + /** Remove charuco corners if any of their minMarkers closest markers has not been detected */ + int filterCornersWithoutMinMarkers(InputArray _allCharucoCorners, InputArray allCharucoIds, InputArray allArucoIds, + OutputArray _filteredCharucoCorners, OutputArray _filteredCharucoIds) { + CV_Assert(charucoParameters.minMarkers >= 0 && charucoParameters.minMarkers <= 2); + vector filteredCharucoCorners; + vector filteredCharucoIds; + // for each charuco corner + for(unsigned int i = 0; i < allCharucoIds.getMat().total(); i++) { + int currentCharucoId = allCharucoIds.getMat().at(i); + int totalMarkers = 0; // nomber of closest marker detected + // look for closest markers + for(unsigned int m = 0; m < board.getNearestMarkerIdx()[currentCharucoId].size(); m++) { + int markerId = board.getIds()[board.getNearestMarkerIdx()[currentCharucoId][m]]; + bool found = false; + for(unsigned int k = 0; k < allArucoIds.getMat().total(); k++) { + if(allArucoIds.getMat().at(k) == markerId) { + found = true; + break; + } + } + if(found) totalMarkers++; + } + // if enough markers detected, add the charuco corner to the final list + if(totalMarkers >= charucoParameters.minMarkers) { + filteredCharucoIds.push_back(currentCharucoId); + filteredCharucoCorners.push_back(_allCharucoCorners.getMat().at(i)); + } + } + // parse output + Mat(filteredCharucoCorners).copyTo(_filteredCharucoCorners); + Mat(filteredCharucoIds).copyTo(_filteredCharucoIds); + return (int)_filteredCharucoIds.total(); + } +}; + +CharucoDetector::CharucoDetector(const CharucoBoard &board, const CharucoParameters &charucoParams, + const DetectorParameters &detectorParams, const RefineParameters& refineParams) { + this->charucoDetectorImpl = makePtr(board, charucoParams, ArucoDetector(board.getDictionary(), detectorParams, refineParams)); +} + +const CharucoBoard& CharucoDetector::getBoard() const { + return charucoDetectorImpl->board; +} + +void CharucoDetector::setBoard(const CharucoBoard& board) { + this->charucoDetectorImpl->board = board; + charucoDetectorImpl->arucoDetector.setDictionary(board.getDictionary()); +} + +const CharucoParameters &CharucoDetector::getCharucoParameters() const { + return charucoDetectorImpl->charucoParameters; +} + +void CharucoDetector::setCharucoParameters(CharucoParameters &charucoParameters) { + charucoDetectorImpl->charucoParameters = charucoParameters; +} + +const DetectorParameters& CharucoDetector::getDetectorParameters() const { + return charucoDetectorImpl->arucoDetector.getDetectorParameters(); +} + +void CharucoDetector::setDetectorParameters(const DetectorParameters& detectorParameters) { + charucoDetectorImpl->arucoDetector.setDetectorParameters(detectorParameters); +} + +const RefineParameters& CharucoDetector::getRefineParameters() const { + return charucoDetectorImpl->arucoDetector.getRefineParameters(); +} + +void CharucoDetector::setRefineParameters(const RefineParameters& refineParameters) { + charucoDetectorImpl->arucoDetector.setRefineParameters(refineParameters); +} + +void CharucoDetector::detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds, + InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) const { + CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.size() == markerIds.size())); + vector> tmpMarkerCorners; + vector tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners; + InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds; + + if (markerCorners.empty() && markerIds.empty()) { + vector > rejectedMarkers; + charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers); + if (charucoDetectorImpl->charucoParameters.tryRefineMarkers) + charucoDetectorImpl->arucoDetector.refineDetectedMarkers(image, charucoDetectorImpl->board, _markerCorners, + _markerIds, rejectedMarkers); + } + // if camera parameters are avaible, use approximated calibration + if(!charucoDetectorImpl->charucoParameters.cameraMatrix.empty()) + charucoDetectorImpl->interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, image, charucoCorners, + charucoIds); + // else use local homography + else + charucoDetectorImpl->interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, image, charucoCorners, + charucoIds); + // to return a charuco corner, its closest aruco markers should have been detected + charucoDetectorImpl->filterCornersWithoutMinMarkers(charucoCorners, charucoIds, _markerIds, charucoCorners, + charucoIds); +} + +void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds, + InputOutputArrayOfArrays inMarkerCorners, InputOutputArrayOfArrays inMarkerIds) const { + CV_Assert(getBoard().getChessboardSize() == Size(3, 3)); + CV_Assert((inMarkerCorners.empty() && inMarkerIds.empty() && !image.empty()) || (inMarkerCorners.size() == inMarkerIds.size())); + + vector> tmpMarkerCorners; + vector tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : tmpMarkerCorners; + InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : tmpMarkerIds; + if (_markerCorners.empty() && _markerIds.empty()) { + charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds); + } + + const float minRepDistanceRate = 1.302455f; + vector> diamondCorners; + vector diamondIds; + + // stores if the detected markers have been assigned or not to a diamond + vector assigned(_markerIds.total(), false); + if(_markerIds.total() < 4ull) return; // a diamond need at least 4 markers + + // convert input image to grey + Mat grey; + if(image.type() == CV_8UC3) + cvtColor(image, grey, COLOR_BGR2GRAY); + else + grey = image.getMat(); + auto board = getBoard(); + + // for each of the detected markers, try to find a diamond + for(unsigned int i = 0; i < (unsigned int)_markerIds.total(); i++) { + if(assigned[i]) continue; + + // calculate marker perimeter + float perimeterSq = 0; + Mat corners = _markerCorners.getMat(i); + for(int c = 0; c < 4; c++) { + Point2f edge = corners.at(c) - corners.at((c + 1) % 4); + perimeterSq += edge.x*edge.x + edge.y*edge.y; + } + // maximum reprojection error relative to perimeter + float minRepDistance = sqrt(perimeterSq) * minRepDistanceRate; + + int currentId = _markerIds.getMat().at(i); + + // prepare data to call refineDetectedMarkers() + // detected markers (only the current one) + vector currentMarker; + vector currentMarkerId; + currentMarker.push_back(_markerCorners.getMat(i)); + currentMarkerId.push_back(currentId); + + // marker candidates (the rest of markers if they have not been assigned) + vector candidates; + vector candidatesIdxs; + for(unsigned int k = 0; k < assigned.size(); k++) { + if(k == i) continue; + if(!assigned[k]) { + candidates.push_back(_markerCorners.getMat(k)); + candidatesIdxs.push_back(k); + } + } + if(candidates.size() < 3ull) break; // we need at least 3 free markers + // modify charuco layout id to make sure all the ids are different than current id + vector tmpIds(4ull); + for(int k = 1; k < 4; k++) + tmpIds[k] = currentId + 1 + k; + // current id is assigned to [0], so it is the marker on the top + tmpIds[0] = currentId; + + // create Charuco board layout for diamond (3x3 layout) + charucoDetectorImpl->board = CharucoBoard(Size(3, 3), board.getSquareLength(), + board.getMarkerLength(), board.getDictionary(), tmpIds); + + // try to find the rest of markers in the diamond + vector acceptedIdxs; + if (currentMarker.size() != 4ull) + { + RefineParameters refineParameters(minRepDistance, -1.f, false); + RefineParameters tmp = charucoDetectorImpl->arucoDetector.getRefineParameters(); + charucoDetectorImpl->arucoDetector.setRefineParameters(refineParameters); + charucoDetectorImpl->arucoDetector.refineDetectedMarkers(grey, getBoard(), currentMarker, currentMarkerId, + candidates, + noArray(), noArray(), acceptedIdxs); + charucoDetectorImpl->arucoDetector.setRefineParameters(tmp); + } + + // if found, we have a diamond + if(currentMarker.size() == 4ull) { + assigned[i] = true; + // calculate diamond id, acceptedIdxs array indicates the markers taken from candidates array + Vec4i markerId; + markerId[0] = currentId; + for(int k = 1; k < 4; k++) { + int currentMarkerIdx = candidatesIdxs[acceptedIdxs[k - 1]]; + markerId[k] = _markerIds.getMat().at(currentMarkerIdx); + assigned[currentMarkerIdx] = true; + } + + // interpolate the charuco corners of the diamond + vector currentMarkerCorners; + Mat aux; + detectBoard(grey, currentMarkerCorners, aux, currentMarker, currentMarkerId); + + // if everything is ok, save the diamond + if(currentMarkerCorners.size() > 0ull) { + // reorder corners + vector currentMarkerCornersReorder; + currentMarkerCornersReorder.resize(4); + currentMarkerCornersReorder[0] = currentMarkerCorners[0]; + currentMarkerCornersReorder[1] = currentMarkerCorners[1]; + currentMarkerCornersReorder[2] = currentMarkerCorners[3]; + currentMarkerCornersReorder[3] = currentMarkerCorners[2]; + + diamondCorners.push_back(currentMarkerCornersReorder); + diamondIds.push_back(markerId); + } + } + } + charucoDetectorImpl->board = board; + + if(diamondIds.size() > 0ull) { + // parse output + Mat(diamondIds).copyTo(_diamondIds); + + _diamondCorners.create((int)diamondCorners.size(), 1, CV_32FC2); + for(unsigned int i = 0; i < diamondCorners.size(); i++) { + _diamondCorners.create(4, 1, CV_32FC2, i, true); + for(int j = 0; j < 4; j++) { + _diamondCorners.getMat(i).at(j) = diamondCorners[i][j]; + } + } + } +} + +void drawDetectedCornersCharuco(InputOutputArray _image, InputArray _charucoCorners, + InputArray _charucoIds, Scalar cornerColor) { + CV_Assert(!_image.getMat().empty() && + (_image.getMat().channels() == 1 || _image.getMat().channels() == 3)); + CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total()) || + _charucoIds.getMat().total() == 0); + + size_t nCorners = _charucoCorners.getMat().total(); + for(size_t i = 0; i < nCorners; i++) { + Point2f corner = _charucoCorners.getMat().at((int)i); + // draw first corner mark + rectangle(_image, corner - Point2f(3, 3), corner + Point2f(3, 3), cornerColor, 1, LINE_AA); + // draw ID + if(!_charucoIds.empty()) { + int id = _charucoIds.getMat().at((int)i); + stringstream s; + s << "id=" << id; + putText(_image, s.str(), corner + Point2f(5, -5), FONT_HERSHEY_SIMPLEX, 0.5, + cornerColor, 2); + } + } +} + +void drawDetectedDiamonds(InputOutputArray _image, InputArrayOfArrays _corners, InputArray _ids, Scalar borderColor) { + CV_Assert(_image.getMat().total() != 0 && + (_image.getMat().channels() == 1 || _image.getMat().channels() == 3)); + CV_Assert((_corners.total() == _ids.total()) || _ids.total() == 0); + + // calculate colors + Scalar textColor, cornerColor; + textColor = cornerColor = borderColor; + swap(textColor.val[0], textColor.val[1]); // text color just sawp G and R + swap(cornerColor.val[1], cornerColor.val[2]); // corner color just sawp G and B + + int nMarkers = (int)_corners.total(); + for(int i = 0; i < nMarkers; i++) { + Mat currentMarker = _corners.getMat(i); + CV_Assert(currentMarker.total() == 4 && currentMarker.type() == CV_32FC2); + + // draw marker sides + for(int j = 0; j < 4; j++) { + Point2f p0, p1; + p0 = currentMarker.at< Point2f >(j); + p1 = currentMarker.at< Point2f >((j + 1) % 4); + line(_image, p0, p1, borderColor, 1); + } + + // draw first corner mark + rectangle(_image, currentMarker.at< Point2f >(0) - Point2f(3, 3), + currentMarker.at< Point2f >(0) + Point2f(3, 3), cornerColor, 1, LINE_AA); + + // draw id composed by four numbers + if(_ids.total() != 0) { + Point2f cent(0, 0); + for(int p = 0; p < 4; p++) + cent += currentMarker.at< Point2f >(p); + cent = cent / 4.; + stringstream s; + s << "id=" << _ids.getMat().at< Vec4i >(i); + putText(_image, s.str(), cent, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 2); + } + } +} + +} +} diff --git a/modules/objdetect/test/test_aruco_utils.cpp b/modules/objdetect/test/test_aruco_utils.cpp new file mode 100644 index 0000000000..b301622b60 --- /dev/null +++ b/modules/objdetect/test/test_aruco_utils.cpp @@ -0,0 +1,205 @@ +// 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 "test_precomp.hpp" + +#include "test_aruco_utils.hpp" + +namespace opencv_test { + +vector getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec, + InputArray _tvec, float length, const float offset) { + vector axis; + axis.push_back(Point3f(offset, offset, 0.f)); + axis.push_back(Point3f(length+offset, offset, 0.f)); + axis.push_back(Point3f(offset, length+offset, 0.f)); + axis.push_back(Point3f(offset, offset, length)); + vector axis_to_img; + projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img); + return axis_to_img; +} + +vector getMarkerById(int id, const vector >& corners, const vector& ids) { + for (size_t i = 0ull; i < ids.size(); i++) + if (ids[i] == id) + return corners[i]; + return vector(); +} + +void getSyntheticRT(double yaw, double pitch, double distance, Mat& rvec, Mat& tvec) { + rvec = Mat::zeros(3, 1, CV_64FC1); + tvec = Mat::zeros(3, 1, CV_64FC1); + + // rotate "scene" in pitch axis (x-axis) + Mat rotPitch(3, 1, CV_64FC1); + rotPitch.at(0) = -pitch; + rotPitch.at(1) = 0; + rotPitch.at(2) = 0; + + // rotate "scene" in yaw (y-axis) + Mat rotYaw(3, 1, CV_64FC1); + rotYaw.at(0) = 0; + rotYaw.at(1) = yaw; + rotYaw.at(2) = 0; + + // compose both rotations + composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw, + Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); + + // Tvec, just move in z (camera) direction the specific distance + tvec.at(0) = 0.; + tvec.at(1) = 0.; + tvec.at(2) = distance; +} + +void projectMarker(Mat& img, const aruco::Board& board, int markerIndex, Mat cameraMatrix, Mat rvec, Mat tvec, + int markerBorder) { + // canonical image + Mat markerImg; + const int markerSizePixels = 100; + aruco::generateImageMarker(board.getDictionary(), board.getIds()[markerIndex], markerSizePixels, markerImg, markerBorder); + + // projected corners + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + vector corners; + + // get max coordinate of board + Point3f maxCoord = board.getRightBottomCorner(); + // copy objPoints + vector objPoints = board.getObjPoints()[markerIndex]; + // move the marker to the origin + for (size_t i = 0; i < objPoints.size(); i++) + objPoints[i] -= (maxCoord / 2.f); + + projectPoints(objPoints, rvec, tvec, cameraMatrix, distCoeffs, corners); + + // get perspective transform + vector originalCorners; + originalCorners.push_back(Point2f(0, 0)); + originalCorners.push_back(Point2f((float)markerSizePixels, 0)); + originalCorners.push_back(Point2f((float)markerSizePixels, (float)markerSizePixels)); + originalCorners.push_back(Point2f(0, (float)markerSizePixels)); + Mat transformation = getPerspectiveTransform(originalCorners, corners); + + // apply transformation + Mat aux; + const char borderValue = 127; + warpPerspective(markerImg, aux, transformation, img.size(), INTER_NEAREST, BORDER_CONSTANT, + Scalar::all(borderValue)); + + // copy only not-border pixels + for (int y = 0; y < aux.rows; y++) { + for (int x = 0; x < aux.cols; x++) { + if (aux.at< unsigned char >(y, x) == borderValue) continue; + img.at< unsigned char >(y, x) = aux.at< unsigned char >(y, x); + } + } +} + +Mat projectBoard(const aruco::GridBoard& board, Mat cameraMatrix, double yaw, double pitch, double distance, + Size imageSize, int markerBorder) { + Mat rvec, tvec; + getSyntheticRT(yaw, pitch, distance, rvec, tvec); + + Mat img = Mat(imageSize, CV_8UC1, Scalar::all(255)); + for (unsigned int index = 0; index < board.getIds().size(); index++) + projectMarker(img, board, index, cameraMatrix, rvec, tvec, markerBorder); + return img; +} + +/** Check if a set of 3d points are enough for calibration. Z coordinate is ignored. + * Only axis parallel lines are considered */ +static bool _arePointsEnoughForPoseEstimation(const std::vector &points) { + if(points.size() < 4) return false; + + std::vector sameXValue; // different x values in points + std::vector sameXCounter; // number of points with the x value in sameXValue + for(unsigned int i = 0; i < points.size(); i++) { + bool found = false; + for(unsigned int j = 0; j < sameXValue.size(); j++) { + if(sameXValue[j] == points[i].x) { + found = true; + sameXCounter[j]++; + } + } + if(!found) { + sameXValue.push_back(points[i].x); + sameXCounter.push_back(1); + } + } + + // count how many x values has more than 2 points + int moreThan2 = 0; + for(unsigned int i = 0; i < sameXCounter.size(); i++) { + if(sameXCounter[i] >= 2) moreThan2++; + } + + // if we have more than 1 two xvalues with more than 2 points, calibration is ok + if(moreThan2 > 1) + return true; + return false; +} + +bool getCharucoBoardPose(InputArray charucoCorners, InputArray charucoIds, const aruco::CharucoBoard &board, + InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, InputOutputArray tvec, + bool useExtrinsicGuess) { + CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total())); + if(charucoIds.getMat().total() < 4) return false; // need, at least, 4 corners + + std::vector objPoints; + objPoints.reserve(charucoIds.getMat().total()); + for(unsigned int i = 0; i < charucoIds.getMat().total(); i++) { + int currId = charucoIds.getMat().at< int >(i); + CV_Assert(currId >= 0 && currId < (int)board.getChessboardCorners().size()); + objPoints.push_back(board.getChessboardCorners()[currId]); + } + + // points need to be in different lines, check if detected points are enough + if(!_arePointsEnoughForPoseEstimation(objPoints)) return false; + + solvePnP(objPoints, charucoCorners, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess); + return true; +} + +/** + * @brief Return object points for the system centered in a middle (by default) or in a top left corner of single + * marker, given the marker length + */ +static Mat _getSingleMarkerObjectPoints(float markerLength, bool use_aruco_ccw_center) { + CV_Assert(markerLength > 0); + Mat objPoints(4, 1, CV_32FC3); + // set coordinate system in the top-left corner of the marker, with Z pointing out + if (use_aruco_ccw_center) { + objPoints.ptr(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0); + objPoints.ptr(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0); + objPoints.ptr(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0); + objPoints.ptr(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0); + } + else { + objPoints.ptr(0)[0] = Vec3f(0.f, 0.f, 0); + objPoints.ptr(0)[1] = Vec3f(markerLength, 0.f, 0); + objPoints.ptr(0)[2] = Vec3f(markerLength, markerLength, 0); + objPoints.ptr(0)[3] = Vec3f(0.f, markerLength, 0); + } + return objPoints; +} + +void getMarkersPoses(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs, + OutputArray _rvecs, OutputArray _tvecs, OutputArray objPoints, + bool use_aruco_ccw_center, SolvePnPMethod solvePnPMethod) { + CV_Assert(markerLength > 0); + Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, use_aruco_ccw_center); + int nMarkers = (int)corners.total(); + _rvecs.create(nMarkers, 1, CV_64FC3); + _tvecs.create(nMarkers, 1, CV_64FC3); + + Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); + for (int i = 0; i < nMarkers; i++) + solvePnP(markerObjPoints, corners.getMat(i), cameraMatrix, distCoeffs, rvecs.at(i), tvecs.at(i), + false, solvePnPMethod); + + if(objPoints.needed()) + markerObjPoints.convertTo(objPoints, -1); +} + +} diff --git a/modules/objdetect/test/test_aruco_utils.hpp b/modules/objdetect/test/test_aruco_utils.hpp new file mode 100644 index 0000000000..9b79fa59bc --- /dev/null +++ b/modules/objdetect/test/test_aruco_utils.hpp @@ -0,0 +1,42 @@ +// 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 "test_precomp.hpp" +#include "opencv2/calib3d.hpp" + +namespace opencv_test { + +static inline double deg2rad(double deg) { return deg * CV_PI / 180.; } + +vector getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec, InputArray _tvec, + float length, const float offset = 0.f); + +vector getMarkerById(int id, const vector >& corners, const vector& ids); + +/** + * @brief Get rvec and tvec from yaw, pitch and distance + */ +void getSyntheticRT(double yaw, double pitch, double distance, Mat& rvec, Mat& tvec); + +/** + * @brief Project a synthetic marker + */ +void projectMarker(Mat& img, const aruco::Board& board, int markerIndex, Mat cameraMatrix, Mat rvec, Mat tvec, + int markerBorder); + +/** + * @brief Get a synthetic image of GridBoard in perspective + */ +Mat projectBoard(const aruco::GridBoard& board, Mat cameraMatrix, double yaw, double pitch, double distance, + Size imageSize, int markerBorder); + +bool getCharucoBoardPose(InputArray charucoCorners, InputArray charucoIds, const aruco::CharucoBoard &board, + InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess = false); + +void getMarkersPoses(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs, + OutputArray _rvecs, OutputArray _tvecs, OutputArray objPoints = noArray(), + bool use_aruco_ccw_center = true, SolvePnPMethod solvePnPMethod = SolvePnPMethod::SOLVEPNP_ITERATIVE); + +} diff --git a/modules/objdetect/test/test_boarddetection.cpp b/modules/objdetect/test/test_boarddetection.cpp new file mode 100644 index 0000000000..d3859920fc --- /dev/null +++ b/modules/objdetect/test/test_boarddetection.cpp @@ -0,0 +1,321 @@ +// 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 "test_precomp.hpp" +#include "test_aruco_utils.hpp" + +namespace opencv_test { namespace { + +enum class ArucoAlgParams +{ + USE_DEFAULT = 0, + USE_ARUCO3 = 1 +}; + +/** + * @brief Check pose estimation of aruco board + */ +class CV_ArucoBoardPose : public cvtest::BaseTest { + public: + CV_ArucoBoardPose(ArucoAlgParams arucoAlgParams) + { + aruco::DetectorParameters params; + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + params.minDistanceToBorder = 3; + if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) { + params.useAruco3Detection = true; + params.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + params.minSideLengthCanonicalImg = 16; + params.errorCorrectionRate = 0.8; + } + detector = aruco::ArucoDetector(dictionary, params); + } + + protected: + aruco::ArucoDetector detector; + void run(int); +}; + + +void CV_ArucoBoardPose::run(int) { + int iter = 0; + Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); + Size imgSize(500, 500); + cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; + cameraMatrix.at< double >(0, 2) = imgSize.width / 2; + cameraMatrix.at< double >(1, 2) = imgSize.height / 2; + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + const int sizeX = 3, sizeY = 3; + aruco::DetectorParameters detectorParameters = detector.getDetectorParameters(); + + // for different perspectives + for(double distance = 0.2; distance <= 0.4; distance += 0.15) { + for(int yaw = -55; yaw <= 50; yaw += 25) { + for(int pitch = -55; pitch <= 50; pitch += 25) { + vector tmpIds; + for(int i = 0; i < sizeX*sizeY; i++) + tmpIds.push_back((iter + int(i)) % 250); + aruco::GridBoard gridboard(Size(sizeX, sizeY), 0.02f, 0.005f, detector.getDictionary(), tmpIds); + int markerBorder = iter % 2 + 1; + iter++; + // create synthetic image + Mat img = projectBoard(gridboard, cameraMatrix, deg2rad(yaw), deg2rad(pitch), distance, + imgSize, markerBorder); + vector > corners; + vector ids; + detectorParameters.markerBorderBits = markerBorder; + detector.setDetectorParameters(detectorParameters); + detector.detectMarkers(img, corners, ids); + + ASSERT_EQ(ids.size(), gridboard.getIds().size()); + + // estimate pose + Mat rvec, tvec; + { + Mat objPoints, imgPoints; // get object and image points for the solvePnP function + gridboard.matchImagePoints(corners, ids, objPoints, imgPoints); + solvePnP(objPoints, imgPoints, cameraMatrix, distCoeffs, rvec, tvec); + } + + // check axes + vector axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, gridboard.getRightBottomCorner().x); + vector topLeft = getMarkerById(gridboard.getIds()[0], corners, ids); + ASSERT_NEAR(topLeft[0].x, axes[0].x, 2.f); + ASSERT_NEAR(topLeft[0].y, axes[0].y, 2.f); + vector topRight = getMarkerById(gridboard.getIds()[2], corners, ids); + ASSERT_NEAR(topRight[1].x, axes[1].x, 2.f); + ASSERT_NEAR(topRight[1].y, axes[1].y, 2.f); + vector bottomLeft = getMarkerById(gridboard.getIds()[6], corners, ids); + ASSERT_NEAR(bottomLeft[3].x, axes[2].x, 2.f); + ASSERT_NEAR(bottomLeft[3].y, axes[2].y, 2.f); + + // check estimate result + for(unsigned int i = 0; i < ids.size(); i++) { + int foundIdx = -1; + for(unsigned int j = 0; j < gridboard.getIds().size(); j++) { + if(gridboard.getIds()[j] == ids[i]) { + foundIdx = int(j); + break; + } + } + + if(foundIdx == -1) { + ts->printf(cvtest::TS::LOG, "Marker detected with wrong ID in Board test"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + vector< Point2f > projectedCorners; + projectPoints(gridboard.getObjPoints()[foundIdx], rvec, tvec, cameraMatrix, distCoeffs, + projectedCorners); + + for(int c = 0; c < 4; c++) { + double repError = cv::norm(projectedCorners[c] - corners[i][c]); // TODO cvtest + if(repError > 5.) { + ts->printf(cvtest::TS::LOG, "Corner reprojection error too high"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + } + } + } + } +} + + + +/** + * @brief Check refine strategy + */ +class CV_ArucoRefine : public cvtest::BaseTest { + public: + CV_ArucoRefine(ArucoAlgParams arucoAlgParams) + { + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; + params.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + if (arucoAlgParams == ArucoAlgParams::USE_ARUCO3) + params.useAruco3Detection = true; + aruco::RefineParameters refineParams(10.f, 3.f, true); + detector = aruco::ArucoDetector(dictionary, params, refineParams); + } + + protected: + aruco::ArucoDetector detector; + void run(int); +}; + + +void CV_ArucoRefine::run(int) { + + int iter = 0; + Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); + Size imgSize(500, 500); + cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650; + cameraMatrix.at< double >(0, 2) = imgSize.width / 2; + cameraMatrix.at< double >(1, 2) = imgSize.height / 2; + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + aruco::DetectorParameters detectorParameters = detector.getDetectorParameters(); + + // for different perspectives + for(double distance = 0.2; distance <= 0.4; distance += 0.2) { + for(int yaw = -60; yaw < 60; yaw += 30) { + for(int pitch = -60; pitch <= 60; pitch += 30) { + aruco::GridBoard gridboard(Size(3, 3), 0.02f, 0.005f, detector.getDictionary()); + int markerBorder = iter % 2 + 1; + iter++; + + // create synthetic image + Mat img = projectBoard(gridboard, cameraMatrix, deg2rad(yaw), deg2rad(pitch), distance, + imgSize, markerBorder); + // detect markers + vector > corners, rejected; + vector ids; + detectorParameters.markerBorderBits = markerBorder; + detector.setDetectorParameters(detectorParameters); + detector.detectMarkers(img, corners, ids, rejected); + + // remove a marker from detection + int markersBeforeDelete = (int)ids.size(); + if(markersBeforeDelete < 2) continue; + + rejected.push_back(corners[0]); + corners.erase(corners.begin(), corners.begin() + 1); + ids.erase(ids.begin(), ids.begin() + 1); + + // try to refind the erased marker + detector.refineDetectedMarkers(img, gridboard, corners, ids, rejected, cameraMatrix, + distCoeffs, noArray()); + + // check result + if((int)ids.size() < markersBeforeDelete) { + ts->printf(cvtest::TS::LOG, "Error in refine detected markers"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + } + } +} + +TEST(CV_ArucoBoardPose, accuracy) { + CV_ArucoBoardPose test(ArucoAlgParams::USE_DEFAULT); + test.safe_run(); +} + +typedef CV_ArucoBoardPose CV_Aruco3BoardPose; +TEST(CV_Aruco3BoardPose, accuracy) { + CV_Aruco3BoardPose test(ArucoAlgParams::USE_ARUCO3); + test.safe_run(); +} + +typedef CV_ArucoRefine CV_Aruco3Refine; + +TEST(CV_ArucoRefine, accuracy) { + CV_ArucoRefine test(ArucoAlgParams::USE_DEFAULT); + test.safe_run(); +} + +TEST(CV_Aruco3Refine, accuracy) { + CV_Aruco3Refine test(ArucoAlgParams::USE_ARUCO3); + test.safe_run(); +} + +TEST(CV_ArucoBoardPose, CheckNegativeZ) +{ + double matrixData[9] = { -3.9062571886921410e+02, 0., 4.2350000000000000e+02, + 0., 3.9062571886921410e+02, 2.3950000000000000e+02, + 0., 0., 1 }; + cv::Mat cameraMatrix = cv::Mat(3, 3, CV_64F, matrixData); + + vector pts3d1, pts3d2; + pts3d1.push_back(cv::Point3f(0.326198f, -0.030621f, 0.303620f)); + pts3d1.push_back(cv::Point3f(0.325340f, -0.100594f, 0.301862f)); + pts3d1.push_back(cv::Point3f(0.255859f, -0.099530f, 0.293416f)); + pts3d1.push_back(cv::Point3f(0.256717f, -0.029557f, 0.295174f)); + + pts3d2.push_back(cv::Point3f(-0.033144f, -0.034819f, 0.245216f)); + pts3d2.push_back(cv::Point3f(-0.035507f, -0.104705f, 0.241987f)); + pts3d2.push_back(cv::Point3f(-0.105289f, -0.102120f, 0.237120f)); + pts3d2.push_back(cv::Point3f(-0.102926f, -0.032235f, 0.240349f)); + + vector tmpIds = {0, 1}; + vector > tmpObjectPoints = {pts3d1, pts3d2}; + aruco::Board board(tmpObjectPoints, aruco::getPredefinedDictionary(0), tmpIds); + + vector > corners; + vector pts2d; + pts2d.push_back(cv::Point2f(37.7f, 203.3f)); + pts2d.push_back(cv::Point2f(38.5f, 120.5f)); + pts2d.push_back(cv::Point2f(105.5f, 115.8f)); + pts2d.push_back(cv::Point2f(104.2f, 202.7f)); + corners.push_back(pts2d); + pts2d.clear(); + pts2d.push_back(cv::Point2f(476.0f, 184.2f)); + pts2d.push_back(cv::Point2f(479.6f, 73.8f)); + pts2d.push_back(cv::Point2f(590.9f, 77.0f)); + pts2d.push_back(cv::Point2f(587.5f, 188.1f)); + corners.push_back(pts2d); + + Vec3d rvec, tvec; + int nUsed = 0; + { + Mat objPoints, imgPoints; // get object and image points for the solvePnP function + board.matchImagePoints(corners, board.getIds(), objPoints, imgPoints); + nUsed = (int)objPoints.total()/4; + solvePnP(objPoints, imgPoints, cameraMatrix, Mat(), rvec, tvec); + } + ASSERT_EQ(nUsed, 2); + + cv::Matx33d rotm; cv::Point3d out; + cv::Rodrigues(rvec, rotm); + out = cv::Point3d(tvec) + rotm*Point3d(board.getObjPoints()[0][0]); + ASSERT_GT(out.z, 0); + + corners.clear(); pts2d.clear(); + pts2d.push_back(cv::Point2f(38.4f, 204.5f)); + pts2d.push_back(cv::Point2f(40.0f, 124.7f)); + pts2d.push_back(cv::Point2f(102.0f, 119.1f)); + pts2d.push_back(cv::Point2f(99.9f, 203.6f)); + corners.push_back(pts2d); + pts2d.clear(); + pts2d.push_back(cv::Point2f(476.0f, 184.3f)); + pts2d.push_back(cv::Point2f(479.2f, 75.1f)); + pts2d.push_back(cv::Point2f(588.7f, 79.2f)); + pts2d.push_back(cv::Point2f(586.3f, 188.5f)); + corners.push_back(pts2d); + + nUsed = 0; + { + Mat objPoints, imgPoints; // get object and image points for the solvePnP function + board.matchImagePoints(corners, board.getIds(), objPoints, imgPoints); + nUsed = (int)objPoints.total()/4; + solvePnP(objPoints, imgPoints, cameraMatrix, Mat(), rvec, tvec, true); + } + ASSERT_EQ(nUsed, 2); + + cv::Rodrigues(rvec, rotm); + out = cv::Point3d(tvec) + rotm*Point3d(board.getObjPoints()[0][0]); + ASSERT_GT(out.z, 0); +} + +TEST(CV_ArucoGenerateBoard, regression_1226) { + int bwidth = 1600; + int bheight = 1200; + + cv::aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50); + cv::aruco::CharucoBoard board(Size(7, 5), 1.0, 0.75, dict); + cv::Size sz(bwidth, bheight); + cv::Mat mat; + + ASSERT_NO_THROW( + { + board.generateImage(sz, mat, 0, 1); + }); +} + +}} // namespace diff --git a/modules/objdetect/test/test_charucodetection.cpp b/modules/objdetect/test/test_charucodetection.cpp new file mode 100644 index 0000000000..ef044c893b --- /dev/null +++ b/modules/objdetect/test/test_charucodetection.cpp @@ -0,0 +1,659 @@ +// 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 "test_precomp.hpp" +#include "test_aruco_utils.hpp" + +namespace opencv_test { namespace { + +/** + * @brief Get a synthetic image of Chessboard in perspective + */ +static Mat projectChessboard(int squaresX, int squaresY, float squareSize, Size imageSize, + Mat cameraMatrix, Mat rvec, Mat tvec) { + + Mat img(imageSize, CV_8UC1, Scalar::all(255)); + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + + for(int y = 0; y < squaresY; y++) { + float startY = float(y) * squareSize; + for(int x = 0; x < squaresX; x++) { + if(y % 2 != x % 2) continue; + float startX = float(x) * squareSize; + + vector< Point3f > squareCorners; + squareCorners.push_back(Point3f(startX, startY, 0) - Point3f(squaresX*squareSize/2.f, squaresY*squareSize/2.f, 0.f)); + squareCorners.push_back(squareCorners[0] + Point3f(squareSize, 0, 0)); + squareCorners.push_back(squareCorners[0] + Point3f(squareSize, squareSize, 0)); + squareCorners.push_back(squareCorners[0] + Point3f(0, squareSize, 0)); + + vector< vector< Point2f > > projectedCorners; + projectedCorners.push_back(vector< Point2f >()); + projectPoints(squareCorners, rvec, tvec, cameraMatrix, distCoeffs, projectedCorners[0]); + + vector< vector< Point > > projectedCornersInt; + projectedCornersInt.push_back(vector< Point >()); + + for(int k = 0; k < 4; k++) + projectedCornersInt[0] + .push_back(Point((int)projectedCorners[0][k].x, (int)projectedCorners[0][k].y)); + + fillPoly(img, projectedCornersInt, Scalar::all(0)); + } + } + + return img; +} + + +/** + * @brief Check pose estimation of charuco board + */ +static Mat projectCharucoBoard(aruco::CharucoBoard& board, Mat cameraMatrix, double yaw, + double pitch, double distance, Size imageSize, int markerBorder, + Mat &rvec, Mat &tvec) { + + getSyntheticRT(yaw, pitch, distance, rvec, tvec); + + // project markers + Mat img = Mat(imageSize, CV_8UC1, Scalar::all(255)); + for(unsigned int indexMarker = 0; indexMarker < board.getIds().size(); indexMarker++) { + projectMarker(img, board, indexMarker, cameraMatrix, rvec, tvec, markerBorder); + } + + // project chessboard + Mat chessboard = + projectChessboard(board.getChessboardSize().width, board.getChessboardSize().height, + board.getSquareLength(), imageSize, cameraMatrix, rvec, tvec); + + for(unsigned int i = 0; i < chessboard.total(); i++) { + if(chessboard.ptr< unsigned char >()[i] == 0) { + img.ptr< unsigned char >()[i] = 0; + } + } + + return img; +} + +/** + * @brief Check Charuco detection + */ +class CV_CharucoDetection : public cvtest::BaseTest { + public: + CV_CharucoDetection(); + + protected: + void run(int); +}; + + +CV_CharucoDetection::CV_CharucoDetection() {} + + +void CV_CharucoDetection::run(int) { + + int iter = 0; + Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); + Size imgSize(500, 500); + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; + aruco::CharucoBoard board(Size(4, 4), 0.03f, 0.015f, aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + aruco::CharucoDetector detector(board, aruco::CharucoParameters(), params); + + cameraMatrix.at(0, 0) = cameraMatrix.at(1, 1) = 600; + cameraMatrix.at(0, 2) = imgSize.width / 2; + cameraMatrix.at(1, 2) = imgSize.height / 2; + + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + + // for different perspectives + for(double distance = 0.2; distance <= 0.4; distance += 0.2) { + for(int yaw = -55; yaw <= 50; yaw += 25) { + for(int pitch = -55; pitch <= 50; pitch += 25) { + + int markerBorder = iter % 2 + 1; + iter++; + + // create synthetic image + Mat rvec, tvec; + Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch), + distance, imgSize, markerBorder, rvec, tvec); + + // detect markers and interpolate charuco corners + vector > corners; + vector charucoCorners; + vector ids, charucoIds; + + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); + + //detector.detectMarkers(img, corners, ids); + if(iter % 2 == 0) { + detector.detectBoard(img, charucoCorners, charucoIds, corners, ids); + } else { + aruco::CharucoParameters charucoParameters; + charucoParameters.cameraMatrix = cameraMatrix; + charucoParameters.distCoeffs = distCoeffs; + detector.setCharucoParameters(charucoParameters); + detector.detectBoard(img, charucoCorners, charucoIds, corners, ids); + } + + if(ids.size() == 0) { + ts->printf(cvtest::TS::LOG, "Marker detection failed"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + // check results + vector< Point2f > projectedCharucoCorners; + + // copy chessboardCorners + vector copyChessboardCorners = board.getChessboardCorners(); + // move copyChessboardCorners points + for (size_t i = 0; i < copyChessboardCorners.size(); i++) + copyChessboardCorners[i] -= board.getRightBottomCorner() / 2.f; + projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs, + projectedCharucoCorners); + + for(unsigned int i = 0; i < charucoIds.size(); i++) { + + int currentId = charucoIds[i]; + + if(currentId >= (int)board.getChessboardCorners().size()) { + ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest + + + if(repError > 5.) { + ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + } + } + } +} + + + +/** + * @brief Check charuco pose estimation + */ +class CV_CharucoPoseEstimation : public cvtest::BaseTest { + public: + CV_CharucoPoseEstimation(); + + protected: + void run(int); +}; + + +CV_CharucoPoseEstimation::CV_CharucoPoseEstimation() {} + + +void CV_CharucoPoseEstimation::run(int) { + int iter = 0; + Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); + Size imgSize(500, 500); + aruco::DetectorParameters params; + params.minDistanceToBorder = 3; + aruco::CharucoBoard board(Size(4, 4), 0.03f, 0.015f, aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + aruco::CharucoDetector detector(board, aruco::CharucoParameters(), params); + + cameraMatrix.at(0, 0) = cameraMatrix.at< double >(1, 1) = 650; + cameraMatrix.at(0, 2) = imgSize.width / 2; + cameraMatrix.at(1, 2) = imgSize.height / 2; + + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + // for different perspectives + for(double distance = 0.2; distance <= 0.3; distance += 0.1) { + for(int yaw = -55; yaw <= 50; yaw += 25) { + for(int pitch = -55; pitch <= 50; pitch += 25) { + + int markerBorder = iter % 2 + 1; + iter++; + + // get synthetic image + Mat rvec, tvec; + Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch), + distance, imgSize, markerBorder, rvec, tvec); + + // detect markers + vector > corners; + vector ids; + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); + + // detect markers and interpolate charuco corners + vector charucoCorners; + vector charucoIds; + + if(iter % 2 == 0) { + detector.detectBoard(img, charucoCorners, charucoIds, corners, ids); + } else { + aruco::CharucoParameters charucoParameters; + charucoParameters.cameraMatrix = cameraMatrix; + charucoParameters.distCoeffs = distCoeffs; + detector.setCharucoParameters(charucoParameters); + detector.detectBoard(img, charucoCorners, charucoIds, corners, ids); + } + ASSERT_EQ(ids.size(), board.getIds().size()); + if(charucoIds.size() == 0) continue; + + // estimate charuco pose + getCharucoBoardPose(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec); + + + // check axes + const float offset = (board.getSquareLength() - board.getMarkerLength()) / 2.f; + vector axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, board.getSquareLength(), offset); + vector topLeft = getMarkerById(board.getIds()[0], corners, ids); + ASSERT_NEAR(topLeft[0].x, axes[1].x, 3.f); + ASSERT_NEAR(topLeft[0].y, axes[1].y, 3.f); + vector bottomLeft = getMarkerById(board.getIds()[2], corners, ids); + ASSERT_NEAR(bottomLeft[0].x, axes[2].x, 3.f); + ASSERT_NEAR(bottomLeft[0].y, axes[2].y, 3.f); + + // check estimate result + vector< Point2f > projectedCharucoCorners; + + projectPoints(board.getChessboardCorners(), rvec, tvec, cameraMatrix, distCoeffs, + projectedCharucoCorners); + + for(unsigned int i = 0; i < charucoIds.size(); i++) { + + int currentId = charucoIds[i]; + + if(currentId >= (int)board.getChessboardCorners().size()) { + ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest + + + if(repError > 5.) { + ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + } + } + } +} + + +/** + * @brief Check diamond detection + */ +class CV_CharucoDiamondDetection : public cvtest::BaseTest { + public: + CV_CharucoDiamondDetection(); + + protected: + void run(int); +}; + + +CV_CharucoDiamondDetection::CV_CharucoDiamondDetection() {} + + +void CV_CharucoDiamondDetection::run(int) { + + int iter = 0; + Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1); + Size imgSize(500, 500); + aruco::DetectorParameters params; + params.minDistanceToBorder = 0; + float squareLength = 0.03f; + float markerLength = 0.015f; + aruco::CharucoBoard board(Size(3, 3), squareLength, markerLength, + aruco::getPredefinedDictionary(aruco::DICT_6X6_250)); + aruco::CharucoDetector detector(board); + + + cameraMatrix.at(0, 0) = cameraMatrix.at< double >(1, 1) = 650; + cameraMatrix.at(0, 2) = imgSize.width / 2; + cameraMatrix.at(1, 2) = imgSize.height / 2; + + Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); + aruco::CharucoParameters charucoParameters; + charucoParameters.cameraMatrix = cameraMatrix; + charucoParameters.distCoeffs = distCoeffs; + detector.setCharucoParameters(charucoParameters); + + // for different perspectives + for(double distance = 0.2; distance <= 0.3; distance += 0.1) { + for(int yaw = -50; yaw <= 50; yaw += 25) { + for(int pitch = -50; pitch <= 50; pitch += 25) { + + int markerBorder = iter % 2 + 1; + vector idsTmp; + for(int i = 0; i < 4; i++) + idsTmp.push_back(4 * iter + i); + board = aruco::CharucoBoard(Size(3, 3), squareLength, markerLength, + aruco::getPredefinedDictionary(aruco::DICT_6X6_250), idsTmp); + detector.setBoard(board); + iter++; + + // get synthetic image + Mat rvec, tvec; + Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch), + distance, imgSize, markerBorder, rvec, tvec); + + // detect markers + vector> corners; + vector ids; + params.markerBorderBits = markerBorder; + detector.setDetectorParameters(params); + //detector.detectMarkers(img, corners, ids); + + + // detect diamonds + vector> diamondCorners; + vector diamondIds; + + detector.detectDiamonds(img, diamondCorners, diamondIds, corners, ids); + + // check detect + if(ids.size() != 4) { + ts->printf(cvtest::TS::LOG, "Not enough markers for diamond detection"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + // check results + if(diamondIds.size() != 1) { + ts->printf(cvtest::TS::LOG, "Diamond not detected correctly"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + for(int i = 0; i < 4; i++) { + if(diamondIds[0][i] != board.getIds()[i]) { + ts->printf(cvtest::TS::LOG, "Incorrect diamond ids"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + + + vector< Point2f > projectedDiamondCorners; + + // copy chessboardCorners + vector copyChessboardCorners = board.getChessboardCorners(); + // move copyChessboardCorners points + for (size_t i = 0; i < copyChessboardCorners.size(); i++) + copyChessboardCorners[i] -= board.getRightBottomCorner() / 2.f; + + projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs, + projectedDiamondCorners); + + vector< Point2f > projectedDiamondCornersReorder(4); + projectedDiamondCornersReorder[0] = projectedDiamondCorners[0]; + projectedDiamondCornersReorder[1] = projectedDiamondCorners[1]; + projectedDiamondCornersReorder[2] = projectedDiamondCorners[3]; + projectedDiamondCornersReorder[3] = projectedDiamondCorners[2]; + + + for(unsigned int i = 0; i < 4; i++) { + + double repError = cv::norm(diamondCorners[0][i] - projectedDiamondCornersReorder[i]); // TODO cvtest + + if(repError > 5.) { + ts->printf(cvtest::TS::LOG, "Diamond corner reprojection error too high"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + + // estimate diamond pose + vector< Vec3d > estimatedRvec, estimatedTvec; + getMarkersPoses(diamondCorners, squareLength, cameraMatrix, distCoeffs, estimatedRvec, + estimatedTvec, noArray(), false); + + // check result + vector< Point2f > projectedDiamondCornersPose; + vector< Vec3f > diamondObjPoints(4); + diamondObjPoints[0] = Vec3f(0.f, 0.f, 0); + diamondObjPoints[1] = Vec3f(squareLength, 0.f, 0); + diamondObjPoints[2] = Vec3f(squareLength, squareLength, 0); + diamondObjPoints[3] = Vec3f(0.f, squareLength, 0); + projectPoints(diamondObjPoints, estimatedRvec[0], estimatedTvec[0], cameraMatrix, + distCoeffs, projectedDiamondCornersPose); + + for(unsigned int i = 0; i < 4; i++) { + double repError = cv::norm(projectedDiamondCornersReorder[i] - projectedDiamondCornersPose[i]); // TODO cvtest + + if(repError > 5.) { + ts->printf(cvtest::TS::LOG, "Charuco pose error too high"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + } + } + } + } +} + +/** +* @brief Check charuco board creation +*/ +class CV_CharucoBoardCreation : public cvtest::BaseTest { +public: + CV_CharucoBoardCreation(); + +protected: + void run(int); +}; + +CV_CharucoBoardCreation::CV_CharucoBoardCreation() {} + +void CV_CharucoBoardCreation::run(int) +{ + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_5X5_250); + int n = 6; + + float markerSizeFactor = 0.5f; + + for (float squareSize_mm = 5.0f; squareSize_mm < 35.0f; squareSize_mm += 0.1f) + { + aruco::CharucoBoard board_meters(Size(n, n), squareSize_mm*1e-3f, + squareSize_mm * markerSizeFactor * 1e-3f, dictionary); + + aruco::CharucoBoard board_millimeters(Size(n, n), squareSize_mm, + squareSize_mm * markerSizeFactor, dictionary); + + for (size_t i = 0; i < board_meters.getNearestMarkerIdx().size(); i++) + { + if (board_meters.getNearestMarkerIdx()[i].size() != board_millimeters.getNearestMarkerIdx()[i].size() || + board_meters.getNearestMarkerIdx()[i][0] != board_millimeters.getNearestMarkerIdx()[i][0]) + { + ts->printf(cvtest::TS::LOG, + cv::format("Charuco board topology is sensitive to scale with squareSize=%.1f\n", + squareSize_mm).c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + break; + } + } + } +} + + +TEST(CV_CharucoDetection, accuracy) { + CV_CharucoDetection test; + test.safe_run(); +} + +TEST(CV_CharucoPoseEstimation, accuracy) { + CV_CharucoPoseEstimation test; + test.safe_run(); +} + +TEST(CV_CharucoDiamondDetection, accuracy) { + CV_CharucoDiamondDetection test; + test.safe_run(); +} + +TEST(CV_CharucoBoardCreation, accuracy) { + CV_CharucoBoardCreation test; + test.safe_run(); +} + +TEST(Charuco, testCharucoCornersCollinear_true) +{ + int squaresX = 13; + int squaresY = 28; + float squareLength = 300; + float markerLength = 150; + int dictionaryId = 11; + + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); + + aruco::CharucoBoard charucoBoard(Size(squaresX, squaresY), squareLength, markerLength, dictionary); + + // consistency with C++98 + const int arrLine[9] = {192, 204, 216, 228, 240, 252, 264, 276, 288}; + vector charucoIdsAxisLine(9, 0); + + for (int i = 0; i < 9; i++){ + charucoIdsAxisLine[i] = arrLine[i]; + } + + const int arrDiag[7] = {198, 209, 220, 231, 242, 253, 264}; + + vector charucoIdsDiagonalLine(7, 0); + + for (int i = 0; i < 7; i++){ + charucoIdsDiagonalLine[i] = arrDiag[i]; + } + + bool resultAxisLine = charucoBoard.checkCharucoCornersCollinear(charucoIdsAxisLine); + EXPECT_TRUE(resultAxisLine); + + bool resultDiagonalLine = charucoBoard.checkCharucoCornersCollinear(charucoIdsDiagonalLine); + EXPECT_TRUE(resultDiagonalLine); +} + +TEST(Charuco, testCharucoCornersCollinear_false) +{ + int squaresX = 13; + int squaresY = 28; + float squareLength = 300; + float markerLength = 150; + int dictionaryId = 11; + + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::PredefinedDictionaryType(dictionaryId)); + + aruco::CharucoBoard charucoBoard(Size(squaresX, squaresY), squareLength, markerLength, dictionary); + + // consistency with C++98 + const int arr[63] = {192, 193, 194, 195, 196, 197, 198, 204, 205, 206, 207, 208, + 209, 210, 216, 217, 218, 219, 220, 221, 222, 228, 229, 230, + 231, 232, 233, 234, 240, 241, 242, 243, 244, 245, 246, 252, + 253, 254, 255, 256, 257, 258, 264, 265, 266, 267, 268, 269, + 270, 276, 277, 278, 279, 280, 281, 282, 288, 289, 290, 291, + 292, 293, 294}; + + vector charucoIds(63, 0); + for (int i = 0; i < 63; i++){ + charucoIds[i] = arr[i]; + } + + bool result = charucoBoard.checkCharucoCornersCollinear(charucoIds); + + EXPECT_FALSE(result); +} + +// test that ChArUco board detection is subpixel accurate +TEST(Charuco, testBoardSubpixelCoords) +{ + cv::Size res{500, 500}; + cv::Mat K = (cv::Mat_(3,3) << + 0.5*res.width, 0, 0.5*res.width, + 0, 0.5*res.height, 0.5*res.height, + 0, 0, 1); + + // set expected_corners values + cv::Mat expected_corners = (cv::Mat_(9,2) << + 200, 200, + 250, 200, + 300, 200, + 200, 250, + 250, 250, + 300, 250, + 200, 300, + 250, 300, + 300, 300 + ); + + cv::Mat gray; + + aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_APRILTAG_36h11); + aruco::CharucoBoard board(Size(4, 4), 1.f, .8f, dict); + + // generate ChArUco board + board.generateImage(Size(res.width, res.height), gray, 150); + cv::GaussianBlur(gray, gray, Size(5, 5), 1.0); + + aruco::DetectorParameters params; + params.cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG; + + aruco::CharucoParameters charucoParameters; + charucoParameters.cameraMatrix = K; + aruco::CharucoDetector detector(board, charucoParameters); + detector.setDetectorParameters(params); + + std::vector ids; + std::vector> corners; + cv::Mat c_ids, c_corners; + + detector.detectBoard(gray, c_corners, c_ids, corners, ids); + + ASSERT_EQ(ids.size(), size_t(8)); + ASSERT_EQ(c_corners.rows, expected_corners.rows); + EXPECT_NEAR(0, cvtest::norm(expected_corners, c_corners.reshape(1), NORM_INF), 1e-1); +} + +TEST(Charuco, issue_14014) +{ + string imgPath = cvtest::findDataFile("aruco/recover.png"); + Mat img = imread(imgPath); + + aruco::DetectorParameters detectorParams; + detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX; + detectorParams.cornerRefinementMinAccuracy = 0.01; + aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_7X7_250), detectorParams); + aruco::CharucoBoard board(Size(8, 5), 0.03455f, 0.02164f, detector.getDictionary()); + + vector corners, rejectedPoints; + vector ids; + + detector.detectMarkers(img, corners, ids, rejectedPoints); + + ASSERT_EQ(corners.size(), 19ull); + EXPECT_EQ(Size(4, 1), corners[0].size()); // check dimension of detected corners + + size_t numRejPoints = rejectedPoints.size(); + ASSERT_EQ(rejectedPoints.size(), 26ull); // optional check to track regressions + EXPECT_EQ(Size(4, 1), rejectedPoints[0].size()); // check dimension of detected corners + + detector.refineDetectedMarkers(img, board, corners, ids, rejectedPoints); + + ASSERT_EQ(corners.size(), 20ull); + EXPECT_EQ(Size(4, 1), corners[0].size()); // check dimension of rejected corners after successfully refine + + ASSERT_EQ(rejectedPoints.size() + 1, numRejPoints); + EXPECT_EQ(Size(4, 1), rejectedPoints[0].size()); // check dimension of rejected corners after successfully refine +} + +}} // namespace From 725e440d278aca07d35a5e8963ef990572b07316 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 28 Dec 2022 17:31:52 +0300 Subject: [PATCH 310/313] release: OpenCV 4.7.0 --- modules/core/include/opencv2/core/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 780691e1a3..3227c4e645 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -8,7 +8,7 @@ #define CV_VERSION_MAJOR 4 #define CV_VERSION_MINOR 7 #define CV_VERSION_REVISION 0 -#define CV_VERSION_STATUS "-pre" +#define CV_VERSION_STATUS "" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) From 7e520035332d21a19bc4bdc91dacf6f6f49054d5 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 8 Jan 2023 18:19:37 +0000 Subject: [PATCH 311/313] copyright: 2023 --- COPYRIGHT | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index d5875e9864..5668d0f569 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -2,10 +2,10 @@ Copyright (C) 2000-2022, Intel Corporation, all rights reserved. Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. -Copyright (C) 2015-2022, OpenCV Foundation, all rights reserved. +Copyright (C) 2015-2023, OpenCV Foundation, all rights reserved. Copyright (C) 2008-2016, Itseez Inc., all rights reserved. -Copyright (C) 2019-2022, Xperience AI, all rights reserved. -Copyright (C) 2019-2022, Shenzhen Institute of Artificial Intelligence and +Copyright (C) 2019-2023, Xperience AI, all rights reserved. +Copyright (C) 2019-2023, Shenzhen Institute of Artificial Intelligence and Robotics for Society, all rights reserved. Third party copyrights are property of their respective owners. From 55534eeb068cb66bdfa0bb27ed06c98f160e5afa Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 9 Jan 2023 09:49:22 +0000 Subject: [PATCH 312/313] copyright: 2023 (update) --- COPYRIGHT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 5668d0f569..6b0b6882f6 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -5,7 +5,7 @@ Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. Copyright (C) 2015-2023, OpenCV Foundation, all rights reserved. Copyright (C) 2008-2016, Itseez Inc., all rights reserved. Copyright (C) 2019-2023, Xperience AI, all rights reserved. -Copyright (C) 2019-2023, Shenzhen Institute of Artificial Intelligence and - Robotics for Society, all rights reserved. +Copyright (C) 2019-2022, Shenzhen Institute of Artificial Intelligence and Robotics for Society, all rights reserved. +Copyright (C) 2022-2023, Southern University of Science And Technology, all rights reserved. Third party copyrights are property of their respective owners. From 36815fe3f35dc6439e7e0be663e06e7eea7b02a6 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 9 Jan 2023 10:06:10 +0000 Subject: [PATCH 313/313] videoio(test): skip unstable Media.audio test --- modules/videoio/test/test_audio.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/videoio/test/test_audio.cpp b/modules/videoio/test/test_audio.cpp index 4cf0dc350b..961d8d809f 100644 --- a/modules/videoio/test/test_audio.cpp +++ b/modules/videoio/test/test_audio.cpp @@ -264,6 +264,8 @@ TEST_P(Media, audio) { if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(backend))) throw SkipTestException(cv::videoio_registry::getBackendName(backend) + " backend was not found"); + if (cvtest::skipUnstableTests && backend == CAP_GSTREAMER) + throw SkipTestException("Unstable GStreamer test"); doTest(); }