mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 13:23:02 +04:00
Compare commits
178 Commits
4.5.2-openvino
...
4.5.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 69357b1e88 | |||
| 9ef20ad20e | |||
| 1263949e70 | |||
| 12d80b9e64 | |||
| ad333e0d22 | |||
| 34a8a45a6a | |||
| 6a72bf5085 | |||
| 1e51f87298 | |||
| 8095f601d1 | |||
| 0ff57e3292 | |||
| 5db484b6c2 | |||
| b40d24ad75 | |||
| 6865787a75 | |||
| 7f664850f5 | |||
| 2907fb88f5 | |||
| f839fa4070 | |||
| 32d51313d0 | |||
| 69341edfe6 | |||
| 81f0b0e0dc | |||
| d7cb2ea210 | |||
| 28f919d9d2 | |||
| 4ae2c11520 | |||
| 3e1673e8b2 | |||
| 1b3e0b27af | |||
| e4b0251e9d | |||
| b26f5b9468 | |||
| 5340dc6686 | |||
| 174753921d | |||
| aa32e3deb1 | |||
| 2b86de217a | |||
| 1615afd7f4 | |||
| 908957317f | |||
| 6fa2bdd7f2 | |||
| f82303d614 | |||
| 6773fa03e2 | |||
| 47fe017a6c | |||
| 7baae20c20 | |||
| 921c4551d9 | |||
| c20a424e3a | |||
| 2e005f5dc2 | |||
| 9f13fdb840 | |||
| e6f52b0996 | |||
| d651ff8d6b | |||
| e11408f6ad | |||
| 8069a6b4f8 | |||
| b697b3162f | |||
| 53a16b1186 | |||
| a2a92999be | |||
| 40c0830b63 | |||
| 6f1eefec69 | |||
| 61a4100d0c | |||
| bb6e15f2c0 | |||
| 0dcb99cf23 | |||
| 69fc0acd1a | |||
| 3f14cb073b | |||
| cc6d48959e | |||
| 5ffe32439d | |||
| 76a9c0ccb0 | |||
| aab62aa6dd | |||
| c89084e6b7 | |||
| 64072e44cc | |||
| e7007668a6 | |||
| 105b57c8d9 | |||
| 79d4a38d87 | |||
| e08de1101d | |||
| b5339cc01b | |||
| ac9f3a1242 | |||
| 53652a6194 | |||
| 8d232a63ad | |||
| 35eaacd1db | |||
| bf03f5fa3a | |||
| 6e8022a3af | |||
| d27eb79fa6 | |||
| 3e48a91d97 | |||
| ad2f5ccc66 | |||
| 77bfd9434b | |||
| 144443d011 | |||
| 6ba99068c9 | |||
| 86d0a86141 | |||
| b7f6584dab | |||
| 3290a5f370 | |||
| bf9f67e93f | |||
| 26ea4760ad | |||
| 0753408e10 | |||
| f59917bea1 | |||
| b62d015285 | |||
| a394c8b10b | |||
| 24002a5dfe | |||
| 7c4b873ba3 | |||
| e56e4876e7 | |||
| 7949983c41 | |||
| 56bdd7db5c | |||
| 3df6bc58e9 | |||
| b0d66e06f4 | |||
| e75f1b071b | |||
| 7a2b3ed471 | |||
| 551d4a8ec1 | |||
| 29a289dfa1 | |||
| a00d6b8377 | |||
| ca8c3dd9b5 | |||
| c434ca74a2 | |||
| bdd2b57e5d | |||
| a97f6f8058 | |||
| c0dd82fb53 | |||
| 94f00cf096 | |||
| 1211a8b9cd | |||
| 50a264d832 | |||
| 3f52d0e46c | |||
| 716bdd5ee5 | |||
| bad08d26da | |||
| a41394c885 | |||
| f680505086 | |||
| ae60bbc7e4 | |||
| dc31e20925 | |||
| 7664e6d090 | |||
| af63fffb63 | |||
| 0a6adedaf1 | |||
| b19f860384 | |||
| 960f501cc1 | |||
| 7a8e171691 | |||
| 7ca9740da5 | |||
| 68fae94cbb | |||
| 6da5c7c1d0 | |||
| 87e607a19b | |||
| cbe236652b | |||
| d4d805cb3e | |||
| 8d29a902e4 | |||
| 4c1d5840bb | |||
| f136adcad5 | |||
| 95ab9468c1 | |||
| f8181fbef8 | |||
| c5c2b6f9bf | |||
| b995de4ff3 | |||
| 825dadfbdd | |||
| 0f4b27c300 | |||
| 63048812c7 | |||
| fbb38cc245 | |||
| c1a57a10a4 | |||
| e4692ac079 | |||
| 12fa8d8444 | |||
| 47337e2196 | |||
| ddd2447192 | |||
| 1d6a1e5f9c | |||
| 04d907fb97 | |||
| 84fcc4ab9b | |||
| 65b51e1538 | |||
| e2610df073 | |||
| 6c9be1bc1d | |||
| 4c48f1eed2 | |||
| 31f66766b7 | |||
| 601690695e | |||
| 4287961aed | |||
| 85009800b3 | |||
| d643a90efa | |||
| 04a9ff88d8 | |||
| dc24663e8d | |||
| d2cc63e779 | |||
| d0fe6ad109 | |||
| 358878caf7 | |||
| b450dd7a87 | |||
| 640f188ca2 | |||
| a1e2c4f338 | |||
| 625d4fc884 | |||
| f821530eb0 | |||
| 7894cd3c73 | |||
| 125cc79c17 | |||
| 2e429268ff | |||
| 2a808aeec0 | |||
| 94533e12eb | |||
| a0008de281 | |||
| a42d4da003 | |||
| e2ca50f1cb | |||
| 0689c70dba | |||
| 75ad74c893 | |||
| cbfd38bd41 | |||
| cc22a73d0f | |||
| 1b0f781b7c | |||
| 8c74d7e4fa |
+6
-6
@@ -999,6 +999,12 @@ if(COMMAND ocv_pylint_finalize)
|
||||
ocv_pylint_add_directory_recurse(${CMAKE_CURRENT_LIST_DIR}/samples/python/tutorial_code)
|
||||
ocv_pylint_finalize()
|
||||
endif()
|
||||
if(TARGET check_pylint)
|
||||
message(STATUS "Registered 'check_pylint' target: using ${PYLINT_EXECUTABLE} (ver: ${PYLINT_VERSION}), checks: ${PYLINT_TOTAL_TARGETS}")
|
||||
endif()
|
||||
if(TARGET check_flake8)
|
||||
message(STATUS "Registered 'check_flake8' target: using ${FLAKE8_EXECUTABLE} (ver: ${FLAKE8_VERSION})")
|
||||
endif()
|
||||
|
||||
if(OPENCV_GENERATE_SETUPVARS)
|
||||
include(cmake/OpenCVGenSetupVars.cmake)
|
||||
@@ -1628,12 +1634,6 @@ endif()
|
||||
|
||||
status("")
|
||||
status(" Python (for build):" PYTHON_DEFAULT_AVAILABLE THEN "${PYTHON_DEFAULT_EXECUTABLE}" ELSE NO)
|
||||
if(PYLINT_FOUND AND PYLINT_EXECUTABLE)
|
||||
status(" Pylint:" PYLINT_FOUND THEN "${PYLINT_EXECUTABLE} (ver: ${PYLINT_VERSION}, checks: ${PYLINT_TOTAL_TARGETS})" ELSE NO)
|
||||
endif()
|
||||
if(FLAKE8_FOUND AND FLAKE8_EXECUTABLE)
|
||||
status(" Flake8:" FLAKE8_FOUND THEN "${FLAKE8_EXECUTABLE} (ver: ${FLAKE8_VERSION})" ELSE NO)
|
||||
endif()
|
||||
|
||||
# ========================== java ==========================
|
||||
if(BUILD_JAVA)
|
||||
|
||||
@@ -59,3 +59,4 @@ ocv_add_app(annotation)
|
||||
ocv_add_app(visualisation)
|
||||
ocv_add_app(interactive-calibration)
|
||||
ocv_add_app(version)
|
||||
ocv_add_app(model-diagnostics)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ocv_add_application(opencv_model_diagnostics
|
||||
MODULES opencv_core opencv_dnn
|
||||
SRCS model_diagnostics.cpp)
|
||||
@@ -0,0 +1,65 @@
|
||||
/*************************************************
|
||||
USAGE:
|
||||
./model_diagnostics -m <onnx file location>
|
||||
**************************************************/
|
||||
#include <opencv2/dnn.hpp>
|
||||
#include <opencv2/core/utils/filesystem.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using namespace cv;
|
||||
using namespace dnn;
|
||||
|
||||
|
||||
static
|
||||
int diagnosticsErrorCallback(int /*status*/, const char* /*func_name*/,
|
||||
const char* /*err_msg*/, const char* /*file_name*/,
|
||||
int /*line*/, void* /*userdata*/)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::string checkFileExists(const std::string& fileName)
|
||||
{
|
||||
if (fileName.empty() || utils::fs::exists(fileName))
|
||||
return fileName;
|
||||
|
||||
CV_Error(Error::StsObjectNotFound, "File " + fileName + " was not found! "
|
||||
"Please, specify a full path to the file.");
|
||||
}
|
||||
|
||||
std::string diagnosticKeys =
|
||||
"{ model m | | Path to the model .onnx file. }"
|
||||
"{ config c | | Path to the model configuration file. }"
|
||||
"{ framework f | | [Optional] Name of the model framework. }";
|
||||
|
||||
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser argParser(argc, argv, diagnosticKeys);
|
||||
argParser.about("Use this tool to run the diagnostics of provided ONNX model"
|
||||
"to obtain the information about its support (supported layers).");
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
argParser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string model = checkFileExists(argParser.get<std::string>("model"));
|
||||
std::string config = checkFileExists(argParser.get<std::string>("config"));
|
||||
std::string frameworkId = argParser.get<std::string>("framework");
|
||||
|
||||
CV_Assert(!model.empty());
|
||||
|
||||
enableModelDiagnostics(true);
|
||||
redirectError(diagnosticsErrorCallback, NULL);
|
||||
|
||||
Net ocvNet = readNet(model, config, frameworkId);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ if(PYLINT_EXECUTABLE AND NOT DEFINED PYLINT_VERSION)
|
||||
execute_process(COMMAND ${PYLINT_EXECUTABLE} --version RESULT_VARIABLE _result OUTPUT_VARIABLE PYLINT_VERSION_RAW)
|
||||
if(NOT _result EQUAL 0)
|
||||
ocv_clear_vars(PYLINT_EXECUTABLE PYLINT_VERSION)
|
||||
elseif(PYLINT_VERSION_RAW MATCHES "pylint([^,]*) ([0-9\\.]+[0-9])")
|
||||
elseif(PYLINT_VERSION_RAW MATCHES "pylint([^,\n]*) ([0-9\\.]+[0-9])")
|
||||
set(PYLINT_VERSION "${CMAKE_MATCH_2}")
|
||||
else()
|
||||
set(PYLINT_VERSION "unknown")
|
||||
|
||||
@@ -135,9 +135,9 @@ endif()
|
||||
|
||||
if(INF_ENGINE_TARGET)
|
||||
if(NOT INF_ENGINE_RELEASE)
|
||||
message(WARNING "InferenceEngine version has not been set, 2021.2 will be used by default. Set INF_ENGINE_RELEASE variable if you experience build errors.")
|
||||
message(WARNING "InferenceEngine version has not been set, 2021.3 will be used by default. Set INF_ENGINE_RELEASE variable if you experience build errors.")
|
||||
endif()
|
||||
set(INF_ENGINE_RELEASE "2021020000" CACHE STRING "Force IE version, should be in form YYYYAABBCC (e.g. 2020.1.0.2 -> 2020010002)")
|
||||
set(INF_ENGINE_RELEASE "2021030000" CACHE STRING "Force IE version, should be in form YYYYAABBCC (e.g. 2020.1.0.2 -> 2020010002)")
|
||||
set_target_properties(${INF_ENGINE_TARGET} PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "HAVE_INF_ENGINE=1;INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}"
|
||||
)
|
||||
|
||||
@@ -143,10 +143,25 @@ macro(ipp_detect_version)
|
||||
list(APPEND IPP_LIBRARIES ${IPP_LIBRARY_DIR}/${IPP_LIB_PREFIX}${IPP_PREFIX}${name}${IPP_SUFFIX}${IPP_LIB_SUFFIX})
|
||||
else ()
|
||||
add_library(ipp${name} STATIC IMPORTED)
|
||||
set(_filename "${IPP_LIB_PREFIX}${IPP_PREFIX}${name}${IPP_SUFFIX}${IPP_LIB_SUFFIX}")
|
||||
set_target_properties(ipp${name} PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LIBRARIES ""
|
||||
IMPORTED_LOCATION ${IPP_LIBRARY_DIR}/${IPP_LIB_PREFIX}${IPP_PREFIX}${name}${IPP_SUFFIX}${IPP_LIB_SUFFIX}
|
||||
IMPORTED_LOCATION ${IPP_LIBRARY_DIR}/${_filename}
|
||||
)
|
||||
if("${name}" STREQUAL "core") # https://github.com/opencv/opencv/pull/19681
|
||||
if(OPENCV_FORCE_IPP_EXCLUDE_LIBS OR OPENCV_FORCE_IPP_EXCLUDE_LIBS_CORE
|
||||
OR (UNIX AND NOT ANDROID AND NOT APPLE
|
||||
AND (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
)
|
||||
AND NOT OPENCV_SKIP_IPP_EXCLUDE_LIBS_CORE
|
||||
)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.0")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--exclude-libs,${_filename} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
else()
|
||||
target_link_options(ipp${name} INTERFACE "LINKER:--exclude-libs,${_filename}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND IPP_LIBRARIES ipp${name})
|
||||
if (NOT BUILD_SHARED_LIBS AND (HAVE_IPP_ICV OR ";${OPENCV_INSTALL_EXTERNAL_DEPENDENCIES};" MATCHES ";ipp;"))
|
||||
# CMake doesn't support "install(TARGETS ${IPP_PREFIX}${name} " command with imported targets
|
||||
|
||||
@@ -879,7 +879,9 @@ endmacro()
|
||||
macro(_ocv_create_module)
|
||||
|
||||
ocv_compiler_optimization_process_sources(OPENCV_MODULE_${the_module}_SOURCES OPENCV_MODULE_${the_module}_DEPS_EXT ${the_module})
|
||||
set(OPENCV_MODULE_${the_module}_HEADERS ${OPENCV_MODULE_${the_module}_HEADERS} CACHE INTERNAL "List of header files for ${the_module}")
|
||||
set(__module_headers ${OPENCV_MODULE_${the_module}_HEADERS})
|
||||
list(SORT __module_headers) # fix headers order, useful for bindings
|
||||
set(OPENCV_MODULE_${the_module}_HEADERS ${__module_headers} CACHE INTERNAL "List of header files for ${the_module}")
|
||||
set(OPENCV_MODULE_${the_module}_SOURCES ${OPENCV_MODULE_${the_module}_SOURCES} CACHE INTERNAL "List of source files for ${the_module}")
|
||||
|
||||
# The condition we ought to be testing here is whether ocv_add_precompiled_headers will
|
||||
|
||||
@@ -122,7 +122,6 @@ function(ocv_pylint_finalize)
|
||||
|
||||
list(LENGTH PYLINT_TARGET_ID __total)
|
||||
set(PYLINT_TOTAL_TARGETS "${__total}" CACHE INTERNAL "")
|
||||
message(STATUS "Pylint: registered ${__total} targets. Build 'check_pylint' target to run checks (\"cmake --build . --target check_pylint\" or \"make check_pylint\")")
|
||||
configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/pylint.cmake.in" "${CMAKE_BINARY_DIR}/pylint.cmake" @ONLY)
|
||||
|
||||
add_custom_target(check_pylint
|
||||
|
||||
@@ -255,6 +255,12 @@ PREDEFINED = __cplusplus=1 \
|
||||
CV_DEFAULT(x)=" = x" \
|
||||
CV_NEON=1 \
|
||||
CV_SSE2=1 \
|
||||
CV_SIMD128=1 \
|
||||
CV_SIMD256=1 \
|
||||
CV_SIMD512=1 \
|
||||
CV_SIMD128_64F=1 \
|
||||
CV_SIMD256_64F=1 \
|
||||
CV_SIMD512_64F=1 \
|
||||
CV__DEBUG_NS_BEGIN= \
|
||||
CV__DEBUG_NS_END= \
|
||||
CV_DEPRECATED_EXTERNAL= \
|
||||
|
||||
@@ -145,6 +145,7 @@ Building OpenCV.js from Source
|
||||
python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules"
|
||||
@endcode
|
||||
|
||||
|
||||
Running OpenCV.js Tests
|
||||
---------------------------------------
|
||||
|
||||
@@ -310,6 +311,12 @@ The example uses latest version of emscripten. If the build fails you should try
|
||||
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk:2.0.10 emcmake python3 ./platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
In Windows use the following PowerShell command:
|
||||
|
||||
@code{.bash}
|
||||
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk:2.0.10" emcmake python3 ./platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
### Building the documentation with Docker
|
||||
|
||||
To build the documentation `doxygen` needs to be installed. Create a file named `Dockerfile` with the following content:
|
||||
|
||||
@@ -66,7 +66,7 @@ extension, its first version. A direct limitation of this is that you cannot sav
|
||||
larger than 2 GB. Furthermore you can only create and expand a single video track inside the
|
||||
container. No audio or other track editing support here. Nevertheless, any video codec present on
|
||||
your system might work. If you encounter some of these limitations you will need to look into more
|
||||
specialized video writing libraries such as *FFMpeg* or codecs as *HuffYUV*, *CorePNG* and *LCL*. As
|
||||
specialized video writing libraries such as *FFmpeg* or codecs as *HuffYUV*, *CorePNG* and *LCL*. As
|
||||
an alternative, create the video track with OpenCV and expand it with sound tracks or convert it to
|
||||
other formats by using video manipulation programs such as *VirtualDub* or *AviSynth*.
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,140 @@
|
||||
# Conversion of TensorFlow Detection Models and Launch with OpenCV Python {#tf_det_tutorial_dnn_conversion}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Anastasia Murzova |
|
||||
| Compatibility | OpenCV >= 4.5 |
|
||||
|
||||
## Goals
|
||||
In this tutorial you will learn how to:
|
||||
* obtain frozen graphs of TensorFlow (TF) detection models
|
||||
* run converted TensorFlow model with OpenCV Python API
|
||||
|
||||
We will explore the above-listed points by the example of SSD MobileNetV1.
|
||||
|
||||
## Introduction
|
||||
Let's briefly view the key concepts involved in the pipeline of TensorFlow models transition with OpenCV API. The initial step in the conversion of TensorFlow models into cv.dnn.Net
|
||||
is obtaining the frozen TF model graph. A frozen graph defines the combination of the model graph structure with kept values of the required variables, for example, weights. The frozen graph is saved in [protobuf](https://en.wikipedia.org/wiki/Protocol_Buffers) (```.pb```) files.
|
||||
There are special functions for reading ``.pb`` graphs in OpenCV: cv.dnn.readNetFromTensorflow and cv.dnn.readNet.
|
||||
|
||||
## Requirements
|
||||
To be able to experiment with the below code you will need to install a set of libraries. We will use a virtual environment with python3.7+ for this:
|
||||
|
||||
```console
|
||||
virtualenv -p /usr/bin/python3.7 <env_dir_path>
|
||||
source <env_dir_path>/bin/activate
|
||||
```
|
||||
|
||||
For OpenCV-Python building from source, follow the corresponding instructions from the @ref tutorial_py_table_of_contents_setup.
|
||||
|
||||
Before you start the installation of the libraries, you can customize the [requirements.txt](https://github.com/opencv/opencv/tree/master/samples/dnn/dnn_model_runner/dnn_conversion/requirements.txt), excluding or including (for example, ``opencv-python``) some dependencies.
|
||||
The below line initiates requirements installation into the previously activated virtual environment:
|
||||
|
||||
```console
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Practice
|
||||
In this part we are going to cover the following points:
|
||||
1. create a TF classification model conversion pipeline and provide the inference
|
||||
2. provide the inference, process prediction results
|
||||
|
||||
### Model Preparation
|
||||
The code in this subchapter is located in the ``samples/dnn/dnn_model_runner`` module and can be executed with the below line:
|
||||
|
||||
```console
|
||||
python -m dnn_model_runner.dnn_conversion.tf.detection.py_to_py_ssd_mobilenet
|
||||
```
|
||||
|
||||
The following code contains the steps of the TF SSD MobileNetV1 model retrieval:
|
||||
|
||||
```python
|
||||
tf_model_name = 'ssd_mobilenet_v1_coco_2017_11_17'
|
||||
graph_extraction_dir = "./"
|
||||
frozen_graph_path = extract_tf_frozen_graph(tf_model_name, graph_extraction_dir)
|
||||
print("Frozen graph path for {}: {}".format(tf_model_name, frozen_graph_path))
|
||||
```
|
||||
|
||||
In ``extract_tf_frozen_graph`` function we extract the provided in model archive ``frozen_inference_graph.pb`` for its further processing:
|
||||
|
||||
```python
|
||||
# define model archive name
|
||||
tf_model_tar = model_name + '.tar.gz'
|
||||
# define link to retrieve model archive
|
||||
model_link = DETECTION_MODELS_URL + tf_model_tar
|
||||
|
||||
tf_frozen_graph_name = 'frozen_inference_graph'
|
||||
|
||||
try:
|
||||
urllib.request.urlretrieve(model_link, tf_model_tar)
|
||||
except Exception:
|
||||
print("TF {} was not retrieved: {}".format(model_name, model_link))
|
||||
return
|
||||
|
||||
print("TF {} was retrieved.".format(model_name))
|
||||
|
||||
tf_model_tar = tarfile.open(tf_model_tar)
|
||||
frozen_graph_path = ""
|
||||
|
||||
for model_tar_elem in tf_model_tar.getmembers():
|
||||
if tf_frozen_graph_name in os.path.basename(model_tar_elem.name):
|
||||
tf_model_tar.extract(model_tar_elem, extracted_model_path)
|
||||
frozen_graph_path = os.path.join(extracted_model_path, model_tar_elem.name)
|
||||
break
|
||||
tf_model_tar.close()
|
||||
```
|
||||
|
||||
After the successful execution of the above code we will get the following output:
|
||||
|
||||
```console
|
||||
TF ssd_mobilenet_v1_coco_2017_11_17 was retrieved.
|
||||
Frozen graph path for ssd_mobilenet_v1_coco_2017_11_17: ./ssd_mobilenet_v1_coco_2017_11_17/frozen_inference_graph.pb
|
||||
```
|
||||
|
||||
To provide model inference we will use the below [double-decker bus photo](https://www.pexels.com/photo/bus-and-car-on-one-way-street-3626589/) (under [Pexels](https://www.pexels.com/license/) license):
|
||||
|
||||

|
||||
|
||||
To initiate the test process we need to provide an appropriate model configuration. We will use [``ssd_mobilenet_v1_coco.config``](https://github.com/tensorflow/models/blob/master/research/object_detection/samples/configs/ssd_mobilenet_v1_coco.config) from [TensorFlow Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection#tensorflow-object-detection-api).
|
||||
TensorFlow Object Detection API framework contains helpful mechanisms for object detection model manipulations.
|
||||
|
||||
We will use this configuration to provide a text graph representation. To generate ``.pbtxt`` we will use the corresponding [``samples/dnn/tf_text_graph_ssd.py``](https://github.com/opencv/opencv/blob/master/samples/dnn/tf_text_graph_ssd.py) script:
|
||||
|
||||
```console
|
||||
python tf_text_graph_ssd.py --input ssd_mobilenet_v1_coco_2017_11_17/frozen_inference_graph.pb --config ssd_mobilenet_v1_coco_2017_11_17/ssd_mobilenet_v1_coco.config --output ssd_mobilenet_v1_coco_2017_11_17.pbtxt
|
||||
```
|
||||
|
||||
After successful execution ``ssd_mobilenet_v1_coco_2017_11_17.pbtxt`` will be created.
|
||||
|
||||
Before we run ``object_detection.py``, let's have a look at the default values for the SSD MobileNetV1 test process configuration. They are located in [``models.yml``](https://github.com/opencv/opencv/blob/master/samples/dnn/models.yml):
|
||||
|
||||
```yml
|
||||
ssd_tf:
|
||||
model: "ssd_mobilenet_v1_coco_2017_11_17.pb"
|
||||
config: "ssd_mobilenet_v1_coco_2017_11_17.pbtxt"
|
||||
mean: [0, 0, 0]
|
||||
scale: 1.0
|
||||
width: 300
|
||||
height: 300
|
||||
rgb: true
|
||||
classes: "object_detection_classes_coco.txt"
|
||||
sample: "object_detection"
|
||||
```
|
||||
|
||||
To fetch these values we need to provide frozen graph ``ssd_mobilenet_v1_coco_2017_11_17.pb`` model and text graph ``ssd_mobilenet_v1_coco_2017_11_17.pbtxt``:
|
||||
|
||||
```console
|
||||
python object_detection.py ssd_tf --input ../data/pexels_double_decker_bus.jpg
|
||||
```
|
||||
|
||||
This line is equivalent to:
|
||||
|
||||
```console
|
||||
python object_detection.py --model ssd_mobilenet_v1_coco_2017_11_17.pb --config ssd_mobilenet_v1_coco_2017_11_17.pbtxt --input ../data/pexels_double_decker_bus.jpg --width 300 --height 300 --classes ../data/dnn/object_detection_classes_coco.txt
|
||||
```
|
||||
|
||||
The result is:
|
||||
|
||||

|
||||
|
||||
There are several helpful parameters, which can be also customized for result corrections: threshold (``--thr``) and non-maximum suppression (``--nms``) values.
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
# Conversion of PyTorch Segmentation Models and Launch with OpenCV {#pytorch_segm_tutorial_dnn_conversion}
|
||||
|
||||
## Goals
|
||||
In this tutorial you will learn how to:
|
||||
* convert PyTorch segmentation models
|
||||
* run converted PyTorch model with OpenCV
|
||||
* obtain an evaluation of the PyTorch and OpenCV DNN models
|
||||
|
||||
We will explore the above-listed points by the example of the FCN ResNet-50 architecture.
|
||||
|
||||
## Introduction
|
||||
The key points involved in the transition pipeline of the [PyTorch classification](https://link_to_cls_tutorial) and segmentation models with OpenCV API are equal. The first step is model transferring into [ONNX](https://onnx.ai/about.html) format with PyTorch [``torch.onnx.export``](https://pytorch.org/docs/stable/onnx.html#torch.onnx.export) built-in function.
|
||||
Further the obtained ``.onnx`` model is passed into cv.dnn.readNetFromONNX, which returns cv.dnn.Net object ready for DNN manipulations.
|
||||
|
||||
## Practice
|
||||
In this part we are going to cover the following points:
|
||||
1. create a segmentation model conversion pipeline and provide the inference
|
||||
2. evaluate and test segmentation models
|
||||
|
||||
If you'd like merely to run evaluation or test model pipelines, the "Model Conversion Pipeline" part can be skipped.
|
||||
|
||||
### Model Conversion Pipeline
|
||||
The code in this subchapter is located in the ``dnn_model_runner`` module and can be executed with the line:
|
||||
|
||||
``
|
||||
python -m dnn_model_runner.dnn_conversion.pytorch.segmentation.py_to_py_fcnresnet50
|
||||
``
|
||||
|
||||
The following code contains the description of the below-listed steps:
|
||||
1. instantiate PyTorch model
|
||||
2. convert PyTorch model into ``.onnx``
|
||||
3. read the transferred network with OpenCV API
|
||||
4. prepare input data
|
||||
5. provide inference
|
||||
6. get colored masks from predictions
|
||||
7. visualize results
|
||||
|
||||
```python
|
||||
# initialize PyTorch FCN ResNet-50 model
|
||||
original_model = models.segmentation.fcn_resnet50(pretrained=True)
|
||||
|
||||
# get the path to the converted into ONNX PyTorch model
|
||||
full_model_path = get_pytorch_onnx_model(original_model)
|
||||
|
||||
# read converted .onnx model with OpenCV API
|
||||
opencv_net = cv2.dnn.readNetFromONNX(full_model_path)
|
||||
print("OpenCV model was successfully read. Layer IDs: \n", opencv_net.getLayerNames())
|
||||
|
||||
# get preprocessed image
|
||||
img, input_img = get_processed_imgs("test_data/sem_segm/2007_000033.jpg")
|
||||
|
||||
# obtain OpenCV DNN predictions
|
||||
opencv_prediction = get_opencv_dnn_prediction(opencv_net, input_img)
|
||||
|
||||
# obtain original PyTorch ResNet50 predictions
|
||||
pytorch_prediction = get_pytorch_dnn_prediction(original_model, input_img)
|
||||
|
||||
pascal_voc_classes, pascal_voc_colors = read_colors_info("test_data/sem_segm/pascal-classes.txt")
|
||||
|
||||
# obtain colored segmentation masks
|
||||
opencv_colored_mask = get_colored_mask(img.shape, opencv_prediction, pascal_voc_colors)
|
||||
pytorch_colored_mask = get_colored_mask(img.shape, pytorch_prediction, pascal_voc_colors)
|
||||
|
||||
# obtain palette of PASCAL VOC colors
|
||||
color_legend = get_legend(pascal_voc_classes, pascal_voc_colors)
|
||||
|
||||
cv2.imshow('PyTorch Colored Mask', pytorch_colored_mask)
|
||||
cv2.imshow('OpenCV DNN Colored Mask', opencv_colored_mask)
|
||||
cv2.imshow('Color Legend', color_legend)
|
||||
|
||||
cv2.waitKey(0)
|
||||
```
|
||||
|
||||
To provide the model inference we will use the below picture from the [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) validation dataset:
|
||||
|
||||

|
||||
|
||||
The target segmented result is:
|
||||
|
||||

|
||||
|
||||
For the PASCAL VOC colors decoding and its mapping with the predicted masks, we also need ``pascal-classes.txt`` file, which contains the full list of the PASCAL VOC classes and corresponding colors.
|
||||
|
||||
Let's go deeper into each code step by the example of pretrained PyTorch FCN ResNet-50:
|
||||
* instantiate PyTorch FCN ResNet-50 model:
|
||||
|
||||
```python
|
||||
# initialize PyTorch FCN ResNet-50 model
|
||||
original_model = models.segmentation.fcn_resnet50(pretrained=True)
|
||||
```
|
||||
|
||||
* convert PyTorch model into ONNX format:
|
||||
|
||||
```python
|
||||
# define the directory for further converted model save
|
||||
onnx_model_path = "models"
|
||||
# define the name of further converted model
|
||||
onnx_model_name = "fcnresnet50.onnx"
|
||||
|
||||
# create directory for further converted model
|
||||
os.makedirs(onnx_model_path, exist_ok=True)
|
||||
|
||||
# get full path to the converted model
|
||||
full_model_path = os.path.join(onnx_model_path, onnx_model_name)
|
||||
|
||||
# generate model input to build the graph
|
||||
generated_input = Variable(
|
||||
torch.randn(1, 3, 500, 500)
|
||||
)
|
||||
|
||||
# model export into ONNX format
|
||||
torch.onnx.export(
|
||||
original_model,
|
||||
generated_input,
|
||||
full_model_path,
|
||||
verbose=True,
|
||||
input_names=["input"],
|
||||
output_names=["output"],
|
||||
opset_version=11
|
||||
)
|
||||
```
|
||||
|
||||
The code from this step does not differ from the classification conversion case. Thus, after the successful execution of the above code, we will get ``models/fcnresnet50.onnx``.
|
||||
|
||||
* read the transferred network with cv.dnn.readNetFromONNX passing the obtained in the previous step ONNX model into it:
|
||||
|
||||
```python
|
||||
# read converted .onnx model with OpenCV API
|
||||
opencv_net = cv2.dnn.readNetFromONNX(full_model_path)
|
||||
```
|
||||
|
||||
* prepare input data:
|
||||
|
||||
```python
|
||||
# read the image
|
||||
input_img = cv2.imread(img_path, cv2.IMREAD_COLOR)
|
||||
input_img = input_img.astype(np.float32)
|
||||
|
||||
# target image sizes
|
||||
img_height = input_img.shape[0]
|
||||
img_width = input_img.shape[1]
|
||||
|
||||
# define preprocess parameters
|
||||
mean = np.array([0.485, 0.456, 0.406]) * 255.0
|
||||
scale = 1 / 255.0
|
||||
std = [0.229, 0.224, 0.225]
|
||||
|
||||
# prepare input blob to fit the model input:
|
||||
# 1. subtract mean
|
||||
# 2. scale to set pixel values from 0 to 1
|
||||
input_blob = cv2.dnn.blobFromImage(
|
||||
image=input_img,
|
||||
scalefactor=scale,
|
||||
size=(img_width, img_height), # img target size
|
||||
mean=mean,
|
||||
swapRB=True, # BGR -> RGB
|
||||
crop=False # center crop
|
||||
)
|
||||
# 3. divide by std
|
||||
input_blob[0] /= np.asarray(std, dtype=np.float32).reshape(3, 1, 1)
|
||||
```
|
||||
|
||||
In this step we read the image and prepare model input with cv2.dnn.blobFromImage function, which returns 4-dimensional blob.
|
||||
It should be noted that firstly in ``cv2.dnn.blobFromImage`` mean value is subtracted and only then pixel values are scaled. Thus, ``mean`` is multiplied by ``255.0`` to reproduce the original image preprocessing order:
|
||||
|
||||
```python
|
||||
img /= 255.0
|
||||
img -= [0.485, 0.456, 0.406]
|
||||
img /= [0.229, 0.224, 0.225]
|
||||
```
|
||||
|
||||
* OpenCV ``cv.dnn_Net`` inference:
|
||||
|
||||
```python
|
||||
# set OpenCV DNN input
|
||||
opencv_net.setInput(preproc_img)
|
||||
|
||||
# OpenCV DNN inference
|
||||
out = opencv_net.forward()
|
||||
print("OpenCV DNN segmentation prediction: \n")
|
||||
print("* shape: ", out.shape)
|
||||
|
||||
# get IDs of predicted classes
|
||||
out_predictions = np.argmax(out[0], axis=0)
|
||||
```
|
||||
|
||||
After the above code execution we will get the following output:
|
||||
|
||||
```
|
||||
OpenCV DNN segmentation prediction:
|
||||
* shape: (1, 21, 500, 500)
|
||||
```
|
||||
|
||||
Each prediction channel out of 21, where 21 represents the number of PASCAL VOC classes, contains probabilities, which indicate how likely the pixel corresponds to the PASCAL VOC class.
|
||||
|
||||
* PyTorch FCN ResNet-50 model inference:
|
||||
|
||||
```python
|
||||
original_net.eval()
|
||||
preproc_img = torch.FloatTensor(preproc_img)
|
||||
|
||||
with torch.no_grad():
|
||||
# obtaining unnormalized probabilities for each class
|
||||
out = original_net(preproc_img)['out']
|
||||
|
||||
print("\nPyTorch segmentation model prediction: \n")
|
||||
print("* shape: ", out.shape)
|
||||
|
||||
# get IDs of predicted classes
|
||||
out_predictions = out[0].argmax(dim=0)
|
||||
```
|
||||
|
||||
After the above code launching we will get the following output:
|
||||
|
||||
```
|
||||
PyTorch segmentation model prediction:
|
||||
* shape: torch.Size([1, 21, 366, 500])
|
||||
```
|
||||
|
||||
PyTorch prediction also contains probabilities corresponding to each class prediction.
|
||||
|
||||
* get colored masks from predictions:
|
||||
|
||||
```python
|
||||
# convert mask values into PASCAL VOC colors
|
||||
processed_mask = np.stack([colors[color_id] for color_id in segm_mask.flatten()])
|
||||
|
||||
# reshape mask into 3-channel image
|
||||
processed_mask = processed_mask.reshape(mask_height, mask_width, 3)
|
||||
processed_mask = cv2.resize(processed_mask, (img_width, img_height), interpolation=cv2.INTER_NEAREST).astype(
|
||||
np.uint8)
|
||||
|
||||
# convert colored mask from BGR to RGB for compatibility with PASCAL VOC colors
|
||||
processed_mask = cv2.cvtColor(processed_mask, cv2.COLOR_BGR2RGB)
|
||||
```
|
||||
|
||||
In this step we map the probabilities from segmentation masks with appropriate colors of the predicted classes. Let's have a look at the results:
|
||||
|
||||

|
||||
|
||||
For the extended evaluation of the models, we can use ``py_to_py_segm`` script of the ``dnn_model_runner`` module. This module part will be described in the next subchapter.
|
||||
|
||||
### Evaluation of the Models
|
||||
|
||||
The proposed in ``dnn/samples`` ``dnn_model_runner`` module allows to run the full evaluation pipeline on the PASCAL VOC dataset and test execution for the following PyTorch segmentation models:
|
||||
* FCN ResNet-50
|
||||
* FCN ResNet-101
|
||||
|
||||
This list can be also extended with further appropriate evaluation pipeline configuration.
|
||||
|
||||
#### Evaluation Mode
|
||||
|
||||
The below line represents running of the module in the evaluation mode:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.pytorch.segmentation.py_to_py_segm --model_name <pytorch_segm_model_name>
|
||||
```
|
||||
|
||||
Chosen from the list segmentation model will be read into OpenCV ``cv.dnn_Net`` object. Evaluation results of PyTorch and OpenCV models (pixel accuracy, mean IoU, inference time) will be written into the log file. Inference time values will be also depicted in a chart to generalize the obtained model information.
|
||||
|
||||
Necessary evaluation configurations are defined in the [``test_config.py``](https://github.com/opencv/opencv/tree/master/samples/dnn/dnn_model_runner/dnn_conversion/common/test/configs/test_config.py):
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TestSegmConfig:
|
||||
frame_size: int = 500
|
||||
img_root_dir: str = "./VOC2012"
|
||||
img_dir: str = os.path.join(img_root_dir, "JPEGImages/")
|
||||
img_segm_gt_dir: str = os.path.join(img_root_dir, "SegmentationClass/")
|
||||
# reduced val: https://github.com/shelhamer/fcn.berkeleyvision.org/blob/master/data/pascal/seg11valid.txt
|
||||
segm_val_file: str = os.path.join(img_root_dir, "ImageSets/Segmentation/seg11valid.txt")
|
||||
colour_file_cls: str = os.path.join(img_root_dir, "ImageSets/Segmentation/pascal-classes.txt")
|
||||
```
|
||||
|
||||
These values can be modified in accordance with chosen model pipeline.
|
||||
|
||||
To initiate the evaluation of the PyTorch FCN ResNet-50, run the following line:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.pytorch.segmentation.py_to_py_segm --model_name fcnresnet50
|
||||
```
|
||||
|
||||
#### Test Mode
|
||||
|
||||
The below line represents running of the module in the test mode, which provides the steps for the model inference:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.pytorch.segmentation.py_to_py_segm --model_name <pytorch_segm_model_name> --test True --default_img_preprocess <True/False> --evaluate False
|
||||
```
|
||||
|
||||
Here ``default_img_preprocess`` key defines whether you'd like to parametrize the model test process with some particular values or use the default values, for example, ``scale``, ``mean`` or ``std``.
|
||||
|
||||
Test configuration is represented in [``test_config.py``](https://github.com/opencv/opencv/tree/master/samples/dnn/dnn_model_runner/dnn_conversion/common/test/configs/test_config.py) ``TestSegmModuleConfig`` class:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TestSegmModuleConfig:
|
||||
segm_test_data_dir: str = "test_data/sem_segm"
|
||||
test_module_name: str = "segmentation"
|
||||
test_module_path: str = "segmentation.py"
|
||||
input_img: str = os.path.join(segm_test_data_dir, "2007_000033.jpg")
|
||||
model: str = ""
|
||||
|
||||
frame_height: str = str(TestSegmConfig.frame_size)
|
||||
frame_width: str = str(TestSegmConfig.frame_size)
|
||||
scale: float = 1.0
|
||||
mean: List[float] = field(default_factory=lambda: [0.0, 0.0, 0.0])
|
||||
std: List[float] = field(default_factory=list)
|
||||
crop: bool = False
|
||||
rgb: bool = True
|
||||
classes: str = os.path.join(segm_test_data_dir, "pascal-classes.txt")
|
||||
```
|
||||
|
||||
The default image preprocessing options are defined in ``default_preprocess_config.py``:
|
||||
|
||||
```python
|
||||
pytorch_segm_input_blob = {
|
||||
"mean": ["123.675", "116.28", "103.53"],
|
||||
"scale": str(1 / 255.0),
|
||||
"std": ["0.229", "0.224", "0.225"],
|
||||
"crop": "False",
|
||||
"rgb": "True"
|
||||
}
|
||||
```
|
||||
|
||||
The basis of the model testing is represented in ``samples/dnn/segmentation.py``. ``segmentation.py`` can be executed autonomously with provided converted model in ``--input`` and populated parameters for ``cv2.dnn.blobFromImage``.
|
||||
|
||||
To reproduce from scratch the described in "Model Conversion Pipeline" OpenCV steps with ``dnn_model_runner`` execute the below line:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.pytorch.segmentation.py_to_py_segm --model_name fcnresnet50 --test True --default_img_preprocess True --evaluate False
|
||||
```
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
# Conversion of TensorFlow Segmentation Models and Launch with OpenCV {#tf_segm_tutorial_dnn_conversion}
|
||||
|
||||
## Goals
|
||||
In this tutorial you will learn how to:
|
||||
* convert TensorFlow (TF) segmentation models
|
||||
* run converted TensorFlow model with OpenCV
|
||||
* obtain an evaluation of the TensorFlow and OpenCV DNN models
|
||||
|
||||
We will explore the above-listed points by the example of the DeepLab architecture.
|
||||
|
||||
## Introduction
|
||||
The key concepts involved in the transition pipeline of the [TensorFlow classification](https://link_to_cls_tutorial) and segmentation models with OpenCV API are almost equal excepting the phase of graph optimization. The initial step in conversion of TensorFlow models into cv.dnn.Net
|
||||
is obtaining the frozen TF model graph. Frozen graph defines the combination of the model graph structure with kept values of the required variables, for example, weights. Usually the frozen graph is saved in [protobuf](https://en.wikipedia.org/wiki/Protocol_Buffers) (```.pb```) files.
|
||||
To read the generated segmentation model ``.pb`` file with cv.dnn.readNetFromTensorflow, it is needed to modify the graph with TF [graph transform tool](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms).
|
||||
|
||||
## Practice
|
||||
In this part we are going to cover the following points:
|
||||
1. create a TF classification model conversion pipeline and provide the inference
|
||||
2. evaluate and test TF classification models
|
||||
|
||||
If you'd like merely to run evaluation or test model pipelines, the "Model Conversion Pipeline" tutorial part can be skipped.
|
||||
|
||||
### Model Conversion Pipeline
|
||||
The code in this subchapter is located in the ``dnn_model_runner`` module and can be executed with the line:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_deeplab
|
||||
```
|
||||
|
||||
TensorFlow segmentation models can be found in [TensorFlow Research Models](https://github.com/tensorflow/models/tree/master/research/#tensorflow-research-models) section, which contains the implementations of models on the basis of published research papers.
|
||||
We will retrieve the archive with the pre-trained TF DeepLabV3 from the below link:
|
||||
|
||||
```
|
||||
http://download.tensorflow.org/models/deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz
|
||||
```
|
||||
|
||||
The full frozen graph obtaining pipeline is described in ``deeplab_retrievement.py``:
|
||||
|
||||
```python
|
||||
def get_deeplab_frozen_graph():
|
||||
# define model path to download
|
||||
models_url = 'http://download.tensorflow.org/models/'
|
||||
mobilenetv2_voctrainval = 'deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz'
|
||||
|
||||
# construct model link to download
|
||||
model_link = models_url + mobilenetv2_voctrainval
|
||||
|
||||
try:
|
||||
urllib.request.urlretrieve(model_link, mobilenetv2_voctrainval)
|
||||
except Exception:
|
||||
print("TF DeepLabV3 was not retrieved: {}".format(model_link))
|
||||
return
|
||||
|
||||
tf_model_tar = tarfile.open(mobilenetv2_voctrainval)
|
||||
|
||||
# iterate the obtained model archive
|
||||
for model_tar_elem in tf_model_tar.getmembers():
|
||||
# check whether the model archive contains frozen graph
|
||||
if TF_FROZEN_GRAPH_NAME in os.path.basename(model_tar_elem.name):
|
||||
# extract frozen graph
|
||||
tf_model_tar.extract(model_tar_elem, FROZEN_GRAPH_PATH)
|
||||
|
||||
tf_model_tar.close()
|
||||
```
|
||||
|
||||
After running this script:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.deeplab_retrievement
|
||||
```
|
||||
|
||||
we will get ``frozen_inference_graph.pb`` in ``deeplab/deeplabv3_mnv2_pascal_trainval``.
|
||||
|
||||
Before going to the network loading with OpenCV it is needed to optimize the extracted ``frozen_inference_graph.pb``.
|
||||
To optimize the graph we use TF ``TransformGraph`` with default parameters:
|
||||
|
||||
```python
|
||||
DEFAULT_OPT_GRAPH_NAME = "optimized_frozen_inference_graph.pb"
|
||||
DEFAULT_INPUTS = "sub_7"
|
||||
DEFAULT_OUTPUTS = "ResizeBilinear_3"
|
||||
DEFAULT_TRANSFORMS = "remove_nodes(op=Identity)" \
|
||||
" merge_duplicate_nodes" \
|
||||
" strip_unused_nodes" \
|
||||
" fold_constants(ignore_errors=true)" \
|
||||
" fold_batch_norms" \
|
||||
" fold_old_batch_norms"
|
||||
|
||||
|
||||
def optimize_tf_graph(
|
||||
in_graph,
|
||||
out_graph=DEFAULT_OPT_GRAPH_NAME,
|
||||
inputs=DEFAULT_INPUTS,
|
||||
outputs=DEFAULT_OUTPUTS,
|
||||
transforms=DEFAULT_TRANSFORMS,
|
||||
is_manual=True,
|
||||
was_optimized=True
|
||||
):
|
||||
# ...
|
||||
|
||||
tf_opt_graph = TransformGraph(
|
||||
tf_graph,
|
||||
inputs,
|
||||
outputs,
|
||||
transforms
|
||||
)
|
||||
```
|
||||
|
||||
To run graph optimization process, execute the line:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.tf_graph_optimizer --in_graph deeplab/deeplabv3_mnv2_pascal_trainval/frozen_inference_graph.pb
|
||||
```
|
||||
|
||||
As a result ``deeplab/deeplabv3_mnv2_pascal_trainval`` directory will contain ``optimized_frozen_inference_graph.pb``.
|
||||
|
||||
After we have obtained the model graphs, let's examine the below-listed steps:
|
||||
1. read TF ``frozen_inference_graph.pb`` graph
|
||||
2. read optimized TF frozen graph with OpenCV API
|
||||
3. prepare input data
|
||||
4. provide inference
|
||||
5. get colored masks from predictions
|
||||
6. visualize results
|
||||
|
||||
```python
|
||||
# get TF model graph from the obtained frozen graph
|
||||
deeplab_graph = read_deeplab_frozen_graph(deeplab_frozen_graph_path)
|
||||
|
||||
# read DeepLab frozen graph with OpenCV API
|
||||
opencv_net = cv2.dnn.readNetFromTensorflow(opt_deeplab_frozen_graph_path)
|
||||
print("OpenCV model was successfully read. Model layers: \n", opencv_net.getLayerNames())
|
||||
|
||||
# get processed image
|
||||
original_img_shape, tf_input_blob, opencv_input_img = get_processed_imgs("test_data/sem_segm/2007_000033.jpg")
|
||||
|
||||
# obtain OpenCV DNN predictions
|
||||
opencv_prediction = get_opencv_dnn_prediction(opencv_net, opencv_input_img)
|
||||
|
||||
# obtain TF model predictions
|
||||
tf_prediction = get_tf_dnn_prediction(deeplab_graph, tf_input_blob)
|
||||
|
||||
# get PASCAL VOC classes and colors
|
||||
pascal_voc_classes, pascal_voc_colors = read_colors_info("test_data/sem_segm/pascal-classes.txt")
|
||||
|
||||
# obtain colored segmentation masks
|
||||
opencv_colored_mask = get_colored_mask(original_img_shape, opencv_prediction, pascal_voc_colors)
|
||||
tf_colored_mask = get_tf_colored_mask(original_img_shape, tf_prediction, pascal_voc_colors)
|
||||
|
||||
# obtain palette of PASCAL VOC colors
|
||||
color_legend = get_legend(pascal_voc_classes, pascal_voc_colors)
|
||||
|
||||
cv2.imshow('TensorFlow Colored Mask', tf_colored_mask)
|
||||
cv2.imshow('OpenCV DNN Colored Mask', opencv_colored_mask)
|
||||
|
||||
cv2.imshow('Color Legend', color_legend)
|
||||
```
|
||||
|
||||
To provide the model inference we will use the below picture from the [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) validation dataset:
|
||||
|
||||

|
||||
|
||||
The target segmented result is:
|
||||
|
||||

|
||||
|
||||
For the PASCAL VOC colors decoding and its mapping with the predicted masks, we also need ``pascal-classes.txt`` file, which contains the full list of the PASCAL VOC classes and corresponding colors.
|
||||
|
||||
Let's go deeper into each step by the example of pretrained TF DeepLabV3 MobileNetV2:
|
||||
|
||||
* read TF ``frozen_inference_graph.pb`` graph :
|
||||
|
||||
```python
|
||||
# init deeplab model graph
|
||||
model_graph = tf.Graph()
|
||||
|
||||
# obtain
|
||||
with tf.io.gfile.GFile(frozen_graph_path, 'rb') as graph_file:
|
||||
tf_model_graph = GraphDef()
|
||||
tf_model_graph.ParseFromString(graph_file.read())
|
||||
|
||||
with model_graph.as_default():
|
||||
tf.import_graph_def(tf_model_graph, name='')
|
||||
```
|
||||
|
||||
* read optimized TF frozen graph with OpenCV API:
|
||||
|
||||
```python
|
||||
# read DeepLab frozen graph with OpenCV API
|
||||
opencv_net = cv2.dnn.readNetFromTensorflow(opt_deeplab_frozen_graph_path)
|
||||
```
|
||||
|
||||
* prepare input data with cv2.dnn.blobFromImage function:
|
||||
|
||||
```python
|
||||
# read the image
|
||||
input_img = cv2.imread(img_path, cv2.IMREAD_COLOR)
|
||||
input_img = input_img.astype(np.float32)
|
||||
|
||||
# preprocess image for TF model input
|
||||
tf_preproc_img = cv2.resize(input_img, (513, 513))
|
||||
tf_preproc_img = cv2.cvtColor(tf_preproc_img, cv2.COLOR_BGR2RGB)
|
||||
|
||||
# define preprocess parameters for OpenCV DNN
|
||||
mean = np.array([1.0, 1.0, 1.0]) * 127.5
|
||||
scale = 1 / 127.5
|
||||
|
||||
# prepare input blob to fit the model input:
|
||||
# 1. subtract mean
|
||||
# 2. scale to set pixel values from 0 to 1
|
||||
input_blob = cv2.dnn.blobFromImage(
|
||||
image=input_img,
|
||||
scalefactor=scale,
|
||||
size=(513, 513), # img target size
|
||||
mean=mean,
|
||||
swapRB=True, # BGR -> RGB
|
||||
crop=False # center crop
|
||||
)
|
||||
```
|
||||
|
||||
Please, pay attention at the preprocessing order in the ``cv2.dnn.blobFromImage`` function. Firstly, the mean value is subtracted and only then pixel values are multiplied by the defined scale.
|
||||
Therefore, to reproduce TF image preprocessing pipeline, we multiply ``mean`` by ``127.5``.
|
||||
Another important point is image preprocessing for TF DeepLab. To pass the image into TF model we need only to construct an appropriate shape, the rest image preprocessing is described in [feature_extractor.py](https://github.com/tensorflow/models/blob/master/research/deeplab/core/feature_extractor.py) and will be invoked automatically.
|
||||
|
||||
* provide OpenCV ``cv.dnn_Net`` inference:
|
||||
|
||||
```python
|
||||
# set OpenCV DNN input
|
||||
opencv_net.setInput(preproc_img)
|
||||
|
||||
# OpenCV DNN inference
|
||||
out = opencv_net.forward()
|
||||
print("OpenCV DNN segmentation prediction: \n")
|
||||
print("* shape: ", out.shape)
|
||||
|
||||
# get IDs of predicted classes
|
||||
out_predictions = np.argmax(out[0], axis=0)
|
||||
```
|
||||
|
||||
After the above code execution we will get the following output:
|
||||
|
||||
```
|
||||
OpenCV DNN segmentation prediction:
|
||||
* shape: (1, 21, 513, 513)
|
||||
|
||||
```
|
||||
|
||||
Each prediction channel out of 21, where 21 represents the number of PASCAL VOC classes, contains probabilities, which indicate how likely the pixel corresponds to the PASCAL VOC class.
|
||||
|
||||
* provide TF model inference:
|
||||
|
||||
```python
|
||||
preproc_img = np.expand_dims(preproc_img, 0)
|
||||
|
||||
# init TF session
|
||||
tf_session = Session(graph=model_graph)
|
||||
|
||||
input_tensor_name = "ImageTensor:0",
|
||||
output_tensor_name = "SemanticPredictions:0"
|
||||
|
||||
# run inference
|
||||
out = tf_session.run(
|
||||
output_tensor_name,
|
||||
feed_dict={input_tensor_name: [preproc_img]}
|
||||
)
|
||||
|
||||
print("TF segmentation model prediction: \n")
|
||||
print("* shape: ", out.shape)
|
||||
```
|
||||
|
||||
TF inference results are the following:
|
||||
|
||||
```
|
||||
TF segmentation model prediction:
|
||||
* shape: (1, 513, 513)
|
||||
```
|
||||
|
||||
TensorFlow prediction contains the indexes of corresponding PASCAL VOC classes.
|
||||
|
||||
* transform OpenCV prediction into colored mask:
|
||||
|
||||
```python
|
||||
mask_height = segm_mask.shape[0]
|
||||
mask_width = segm_mask.shape[1]
|
||||
|
||||
img_height = original_img_shape[0]
|
||||
img_width = original_img_shape[1]
|
||||
|
||||
# convert mask values into PASCAL VOC colors
|
||||
processed_mask = np.stack([colors[color_id] for color_id in segm_mask.flatten()])
|
||||
|
||||
# reshape mask into 3-channel image
|
||||
processed_mask = processed_mask.reshape(mask_height, mask_width, 3)
|
||||
processed_mask = cv2.resize(processed_mask, (img_width, img_height), interpolation=cv2.INTER_NEAREST).astype(
|
||||
np.uint8)
|
||||
|
||||
# convert colored mask from BGR to RGB
|
||||
processed_mask = cv2.cvtColor(processed_mask, cv2.COLOR_BGR2RGB)
|
||||
```
|
||||
|
||||
In this step we map the probabilities from segmentation masks with appropriate colors of the predicted classes. Let's have a look at the results:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
* transform TF prediction into colored mask:
|
||||
|
||||
```python
|
||||
colors = np.array(colors)
|
||||
processed_mask = colors[segm_mask[0]]
|
||||
|
||||
img_height = original_img_shape[0]
|
||||
img_width = original_img_shape[1]
|
||||
|
||||
processed_mask = cv2.resize(processed_mask, (img_width, img_height), interpolation=cv2.INTER_NEAREST).astype(
|
||||
np.uint8)
|
||||
|
||||
# convert colored mask from BGR to RGB for compatibility with PASCAL VOC colors
|
||||
processed_mask = cv2.cvtColor(processed_mask, cv2.COLOR_BGR2RGB)
|
||||
```
|
||||
|
||||
The result is:
|
||||
|
||||

|
||||
|
||||
As a result, we get two equal segmentation masks.
|
||||
|
||||
### Evaluation of the Models
|
||||
|
||||
The proposed in ``dnn/samples`` ``dnn_model_runner`` module allows to run the full evaluation pipeline on the PASCAL VOC dataset and test execution for the DeepLab MobileNet model.
|
||||
|
||||
#### Evaluation Mode
|
||||
|
||||
To below line represents running of the module in the evaluation mode:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm
|
||||
```
|
||||
|
||||
The model will be read into OpenCV ``cv.dnn_Net`` object. Evaluation results of TF and OpenCV models (pixel accuracy, mean IoU, inference time) will be written into the log file. Inference time values will be also depicted in a chart to generalize the obtained model information.
|
||||
|
||||
Necessary evaluation configurations are defined in the [``test_config.py``](https://github.com/opencv/opencv/tree/master/samples/dnn/dnn_model_runner/dnn_conversion/common/test/configs/test_config.py):
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TestSegmConfig:
|
||||
frame_size: int = 500
|
||||
img_root_dir: str = "./VOC2012"
|
||||
img_dir: str = os.path.join(img_root_dir, "JPEGImages/")
|
||||
img_segm_gt_dir: str = os.path.join(img_root_dir, "SegmentationClass/")
|
||||
# reduced val: https://github.com/shelhamer/fcn.berkeleyvision.org/blob/master/data/pascal/seg11valid.txt
|
||||
segm_val_file: str = os.path.join(img_root_dir, "ImageSets/Segmentation/seg11valid.txt")
|
||||
colour_file_cls: str = os.path.join(img_root_dir, "ImageSets/Segmentation/pascal-classes.txt")
|
||||
```
|
||||
|
||||
These values can be modified in accordance with chosen model pipeline.
|
||||
|
||||
#### Test Mode
|
||||
|
||||
The below line represents running of the module in the test mode, which provides the steps for the model inference:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm --test True --default_img_preprocess <True/False> --evaluate False
|
||||
```
|
||||
|
||||
Here ``default_img_preprocess`` key defines whether you'd like to parametrize the model test process with some particular values or use the default values, for example, ``scale``, ``mean`` or ``std``.
|
||||
|
||||
Test configuration is represented in [``test_config.py``](https://github.com/opencv/opencv/tree/master/samples/dnn/dnn_model_runner/dnn_conversion/common/test/configs/test_config.py) ``TestSegmModuleConfig`` class:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TestSegmModuleConfig:
|
||||
segm_test_data_dir: str = "test_data/sem_segm"
|
||||
test_module_name: str = "segmentation"
|
||||
test_module_path: str = "segmentation.py"
|
||||
input_img: str = os.path.join(segm_test_data_dir, "2007_000033.jpg")
|
||||
model: str = ""
|
||||
|
||||
frame_height: str = str(TestSegmConfig.frame_size)
|
||||
frame_width: str = str(TestSegmConfig.frame_size)
|
||||
scale: float = 1.0
|
||||
mean: List[float] = field(default_factory=lambda: [0.0, 0.0, 0.0])
|
||||
std: List[float] = field(default_factory=list)
|
||||
crop: bool = False
|
||||
rgb: bool = True
|
||||
classes: str = os.path.join(segm_test_data_dir, "pascal-classes.txt")
|
||||
```
|
||||
|
||||
The default image preprocessing options are defined in ``default_preprocess_config.py``:
|
||||
|
||||
```python
|
||||
tf_segm_input_blob = {
|
||||
"scale": str(1 / 127.5),
|
||||
"mean": ["127.5", "127.5", "127.5"],
|
||||
"std": [],
|
||||
"crop": "False",
|
||||
"rgb": "True"
|
||||
}
|
||||
```
|
||||
|
||||
The basis of the model testing is represented in ``samples/dnn/segmentation.py``. ``segmentation.py`` can be executed autonomously with provided converted model in ``--input`` and populated parameters for ``cv2.dnn.blobFromImage``.
|
||||
|
||||
To reproduce from scratch the described in "Model Conversion Pipeline" OpenCV steps with ``dnn_model_runner`` execute the below line:
|
||||
|
||||
```
|
||||
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm --test True --default_img_preprocess True --evaluate False
|
||||
```
|
||||
@@ -15,7 +15,10 @@ Deep Neural Networks (dnn module) {#tutorial_table_of_content_dnn}
|
||||
In this section you will find the guides, which describe how to run classification, segmentation and detection PyTorch DNN models with OpenCV.
|
||||
- @subpage pytorch_cls_tutorial_dnn_conversion
|
||||
- @subpage pytorch_cls_c_tutorial_dnn_conversion
|
||||
- @subpage pytorch_segm_tutorial_dnn_conversion
|
||||
|
||||
#### TensorFlow models with OpenCV
|
||||
In this section you will find the guides, which describe how to run classification, segmentation and detection TensorFlow DNN models with OpenCV.
|
||||
- @subpage tf_cls_tutorial_dnn_conversion
|
||||
- @subpage tf_det_tutorial_dnn_conversion
|
||||
- @subpage tf_segm_tutorial_dnn_conversion
|
||||
|
||||
@@ -3773,7 +3773,8 @@ namespace fisheye
|
||||
CALIB_FIX_K4 = 1 << 7,
|
||||
CALIB_FIX_INTRINSIC = 1 << 8,
|
||||
CALIB_FIX_PRINCIPAL_POINT = 1 << 9,
|
||||
CALIB_ZERO_DISPARITY = 1 << 10
|
||||
CALIB_ZERO_DISPARITY = 1 << 10,
|
||||
CALIB_FIX_FOCAL_LENGTH = 1 << 11
|
||||
};
|
||||
|
||||
/** @brief Projects points using fisheye model
|
||||
@@ -3927,6 +3928,8 @@ namespace fisheye
|
||||
are set to zeros and stay zero.
|
||||
- @ref fisheye::CALIB_FIX_PRINCIPAL_POINT The principal point is not changed during the global
|
||||
optimization. It stays at the center or at a different location specified when @ref fisheye::CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
- @ref fisheye::CALIB_FIX_FOCAL_LENGTH The focal length is not changed during the global
|
||||
optimization. It is the \f$max(width,height)/\pi\f$ or the provided \f$f_x\f$, \f$f_y\f$ when @ref fisheye::CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||
*/
|
||||
CV_EXPORTS_W double calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size,
|
||||
|
||||
@@ -1842,7 +1842,7 @@ void ChessBoardDetector::generateQuads(const cv::Mat& image_, int flags)
|
||||
if (boardIdx != parentIdx && (boardIdx < 0 || contour_child_counter[boardIdx] < contour_child_counter[parentIdx]))
|
||||
boardIdx = parentIdx;
|
||||
|
||||
contour_quads.push_back(QuadCountour(pt, parentIdx));
|
||||
contour_quads.emplace_back(pt, parentIdx);
|
||||
}
|
||||
|
||||
size_t total = contour_quads.size();
|
||||
|
||||
@@ -78,7 +78,7 @@ static void icvGetQuadrangleHypotheses(const std::vector<std::vector< cv::Point
|
||||
continue;
|
||||
}
|
||||
|
||||
quads.push_back(std::pair<float, int>(box_size, class_id));
|
||||
quads.emplace_back(box_size, class_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,15 +384,15 @@ void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2
|
||||
{
|
||||
//indices of corner points in pattern
|
||||
std::vector<Point> trueIndices;
|
||||
trueIndices.push_back(Point(0, 0));
|
||||
trueIndices.push_back(Point(patternSize.width - 1, 0));
|
||||
trueIndices.emplace_back(0, 0);
|
||||
trueIndices.emplace_back(patternSize.width - 1, 0);
|
||||
if(isAsymmetricGrid)
|
||||
{
|
||||
trueIndices.push_back(Point(patternSize.width - 1, 1));
|
||||
trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 2));
|
||||
trueIndices.emplace_back(patternSize.width - 1, 1);
|
||||
trueIndices.emplace_back(patternSize.width - 1, patternSize.height - 2);
|
||||
}
|
||||
trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 1));
|
||||
trueIndices.push_back(Point(0, patternSize.height - 1));
|
||||
trueIndices.emplace_back(patternSize.width - 1, patternSize.height - 1);
|
||||
trueIndices.emplace_back(0, patternSize.height - 1);
|
||||
|
||||
std::vector<Point2f> idealPoints;
|
||||
for(size_t idx=0; idx<trueIndices.size(); idx++)
|
||||
@@ -401,11 +401,11 @@ void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2
|
||||
int j = trueIndices[idx].x;
|
||||
if(isAsymmetricGrid)
|
||||
{
|
||||
idealPoints.push_back(Point2f((2*j + i % 2)*squareSize, i*squareSize));
|
||||
idealPoints.emplace_back((2*j + i % 2)*squareSize, i*squareSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
idealPoints.push_back(Point2f(j*squareSize, i*squareSize));
|
||||
idealPoints.emplace_back(j*squareSize, i*squareSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ void Graph::addVertex(size_t id)
|
||||
{
|
||||
CV_Assert( !doesVertexExist( id ) );
|
||||
|
||||
vertices.insert(std::pair<size_t, Vertex> (id, Vertex()));
|
||||
vertices.emplace(id, Vertex());
|
||||
}
|
||||
|
||||
void Graph::addEdge(size_t id1, size_t id2)
|
||||
@@ -887,10 +887,9 @@ Mat CirclesGridFinder::rectifyGrid(Size detectedGridSize, const std::vector<Poin
|
||||
convertPointsFromHomogeneous(dstKeypointsMat, dstKeypoints);
|
||||
|
||||
warpedKeypoints.clear();
|
||||
for (size_t i = 0; i < dstKeypoints.size(); i++)
|
||||
for (auto &pt:dstKeypoints)
|
||||
{
|
||||
Point2f pt = dstKeypoints[i];
|
||||
warpedKeypoints.push_back(pt);
|
||||
warpedKeypoints.emplace_back(std::move(pt));
|
||||
}
|
||||
|
||||
return H;
|
||||
@@ -1526,35 +1525,35 @@ void CirclesGridFinder::getCornerSegments(const std::vector<std::vector<size_t>
|
||||
|
||||
//all 8 segments with one end in a corner
|
||||
std::vector<Segment> corner;
|
||||
corner.push_back(Segment(keypoints[points[1][0]], keypoints[points[0][0]]));
|
||||
corner.push_back(Segment(keypoints[points[0][0]], keypoints[points[0][1]]));
|
||||
corner.emplace_back(keypoints[points[1][0]], keypoints[points[0][0]]);
|
||||
corner.emplace_back(keypoints[points[0][0]], keypoints[points[0][1]]);
|
||||
segments.push_back(corner);
|
||||
cornerIndices.push_back(Point(0, 0));
|
||||
firstSteps.push_back(Point(1, 0));
|
||||
secondSteps.push_back(Point(0, 1));
|
||||
cornerIndices.emplace_back(0, 0);
|
||||
firstSteps.emplace_back(1, 0);
|
||||
secondSteps.emplace_back(0, 1);
|
||||
corner.clear();
|
||||
|
||||
corner.push_back(Segment(keypoints[points[0][w - 2]], keypoints[points[0][w - 1]]));
|
||||
corner.push_back(Segment(keypoints[points[0][w - 1]], keypoints[points[1][w - 1]]));
|
||||
corner.emplace_back(keypoints[points[0][w - 2]], keypoints[points[0][w - 1]]);
|
||||
corner.emplace_back(keypoints[points[0][w - 1]], keypoints[points[1][w - 1]]);
|
||||
segments.push_back(corner);
|
||||
cornerIndices.push_back(Point(w - 1, 0));
|
||||
firstSteps.push_back(Point(0, 1));
|
||||
secondSteps.push_back(Point(-1, 0));
|
||||
cornerIndices.emplace_back(w - 1, 0);
|
||||
firstSteps.emplace_back(0, 1);
|
||||
secondSteps.emplace_back(-1, 0);
|
||||
corner.clear();
|
||||
|
||||
corner.push_back(Segment(keypoints[points[h - 2][w - 1]], keypoints[points[h - 1][w - 1]]));
|
||||
corner.push_back(Segment(keypoints[points[h - 1][w - 1]], keypoints[points[h - 1][w - 2]]));
|
||||
corner.emplace_back(keypoints[points[h - 2][w - 1]], keypoints[points[h - 1][w - 1]]);
|
||||
corner.emplace_back(keypoints[points[h - 1][w - 1]], keypoints[points[h - 1][w - 2]]);
|
||||
segments.push_back(corner);
|
||||
cornerIndices.push_back(Point(w - 1, h - 1));
|
||||
firstSteps.push_back(Point(-1, 0));
|
||||
secondSteps.push_back(Point(0, -1));
|
||||
cornerIndices.emplace_back(w - 1, h - 1);
|
||||
firstSteps.emplace_back(-1, 0);
|
||||
secondSteps.emplace_back(0, -1);
|
||||
corner.clear();
|
||||
|
||||
corner.push_back(Segment(keypoints[points[h - 1][1]], keypoints[points[h - 1][0]]));
|
||||
corner.push_back(Segment(keypoints[points[h - 1][0]], keypoints[points[h - 2][0]]));
|
||||
cornerIndices.push_back(Point(0, h - 1));
|
||||
firstSteps.push_back(Point(0, -1));
|
||||
secondSteps.push_back(Point(1, 0));
|
||||
corner.emplace_back(keypoints[points[h - 1][1]], keypoints[points[h - 1][0]]);
|
||||
corner.emplace_back(keypoints[points[h - 1][0]], keypoints[points[h - 2][0]]);
|
||||
cornerIndices.emplace_back(0, h - 1);
|
||||
firstSteps.emplace_back(0, -1);
|
||||
secondSteps.emplace_back(1, 0);
|
||||
segments.push_back(corner);
|
||||
corner.clear();
|
||||
|
||||
|
||||
@@ -754,8 +754,8 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
|
||||
IntrinsicParams currentParam;
|
||||
IntrinsicParams errors;
|
||||
|
||||
finalParam.isEstimate[0] = 1;
|
||||
finalParam.isEstimate[1] = 1;
|
||||
finalParam.isEstimate[0] = flags & CALIB_FIX_FOCAL_LENGTH ? 0 : 1;
|
||||
finalParam.isEstimate[1] = flags & CALIB_FIX_FOCAL_LENGTH ? 0 : 1;
|
||||
finalParam.isEstimate[2] = flags & CALIB_FIX_PRINCIPAL_POINT ? 0 : 1;
|
||||
finalParam.isEstimate[3] = flags & CALIB_FIX_PRINCIPAL_POINT ? 0 : 1;
|
||||
finalParam.isEstimate[4] = flags & CALIB_FIX_SKEW ? 0 : 1;
|
||||
|
||||
@@ -65,7 +65,8 @@ int solve_deg3(double a, double b, double c, double d,
|
||||
return 3;
|
||||
}
|
||||
else {
|
||||
x0 = pow(2 * R, 1 / 3.0) - b_a_3;
|
||||
double cube_root = cv::cubeRoot(2 * R);
|
||||
x0 = cube_root - b_a_3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -82,8 +83,15 @@ int solve_deg3(double a, double b, double c, double d,
|
||||
}
|
||||
|
||||
// D > 0, only one real root
|
||||
double AD = pow(fabs(R) + sqrt(D), 1.0 / 3.0) * (R > 0 ? 1 : (R < 0 ? -1 : 0));
|
||||
double BD = (AD == 0) ? 0 : -Q / AD;
|
||||
double AD = 0.;
|
||||
double BD = 0.;
|
||||
double R_abs = fabs(R);
|
||||
if (R_abs > DBL_EPSILON)
|
||||
{
|
||||
AD = cv::cubeRoot(R_abs + sqrt(D));
|
||||
AD = (R >= 0) ? AD : -AD;
|
||||
BD = -Q / AD;
|
||||
}
|
||||
|
||||
// Calculate the only real root
|
||||
x0 = AD + BD - b_a_3;
|
||||
|
||||
@@ -334,18 +334,42 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
|
||||
|
||||
opoints_inliers.resize(npoints1);
|
||||
ipoints_inliers.resize(npoints1);
|
||||
result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix,
|
||||
distCoeffs, rvec, tvec, useExtrinsicGuess,
|
||||
(flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P) ? SOLVEPNP_EPNP : flags) ? 1 : -1;
|
||||
try
|
||||
{
|
||||
result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix,
|
||||
distCoeffs, rvec, tvec, useExtrinsicGuess,
|
||||
(flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P) ? SOLVEPNP_EPNP : flags) ? 1 : -1;
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (flags == SOLVEPNP_ITERATIVE &&
|
||||
npoints1 == 5 &&
|
||||
e.what() &&
|
||||
std::string(e.what()).find("DLT algorithm needs at least 6 points") != std::string::npos
|
||||
)
|
||||
{
|
||||
CV_LOG_INFO(NULL, "solvePnPRansac(): solvePnP stage to compute the final pose using points "
|
||||
"in the consensus set raised DLT 6 points exception, use result from MSS (Minimal Sample Sets) stage instead.");
|
||||
rvec = _local_model.col(0); // output rotation vector
|
||||
tvec = _local_model.col(1); // output translation vector
|
||||
result = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// raise other exceptions
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if( result <= 0 )
|
||||
if (result <= 0)
|
||||
{
|
||||
_rvec.assign(_local_model.col(0)); // output rotation vector
|
||||
_tvec.assign(_local_model.col(1)); // output translation vector
|
||||
|
||||
if( _inliers.needed() )
|
||||
if (_inliers.needed())
|
||||
_inliers.release();
|
||||
|
||||
CV_LOG_DEBUG(NULL, "solvePnPRansac(): solvePnP stage to compute the final pose using points in the consensus set failed. Return false");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1148,13 +1148,15 @@ class StereoBMImpl CV_FINAL : public StereoBM
|
||||
{
|
||||
public:
|
||||
StereoBMImpl()
|
||||
: params()
|
||||
{
|
||||
params = StereoBMParams();
|
||||
// nothing
|
||||
}
|
||||
|
||||
StereoBMImpl( int _numDisparities, int _SADWindowSize )
|
||||
: params(_numDisparities, _SADWindowSize)
|
||||
{
|
||||
params = StereoBMParams(_numDisparities, _SADWindowSize);
|
||||
// nothing
|
||||
}
|
||||
|
||||
void compute( InputArray leftarr, InputArray rightarr, OutputArray disparr ) CV_OVERRIDE
|
||||
|
||||
@@ -2186,19 +2186,21 @@ class StereoSGBMImpl CV_FINAL : public StereoSGBM
|
||||
{
|
||||
public:
|
||||
StereoSGBMImpl()
|
||||
: params()
|
||||
{
|
||||
params = StereoSGBMParams();
|
||||
// nothing
|
||||
}
|
||||
|
||||
StereoSGBMImpl( int _minDisparity, int _numDisparities, int _SADWindowSize,
|
||||
int _P1, int _P2, int _disp12MaxDiff, int _preFilterCap,
|
||||
int _uniquenessRatio, int _speckleWindowSize, int _speckleRange,
|
||||
int _mode )
|
||||
: params(_minDisparity, _numDisparities, _SADWindowSize,
|
||||
_P1, _P2, _disp12MaxDiff, _preFilterCap,
|
||||
_uniquenessRatio, _speckleWindowSize, _speckleRange,
|
||||
_mode)
|
||||
{
|
||||
params = StereoSGBMParams( _minDisparity, _numDisparities, _SADWindowSize,
|
||||
_P1, _P2, _disp12MaxDiff, _preFilterCap,
|
||||
_uniquenessRatio, _speckleWindowSize, _speckleRange,
|
||||
_mode );
|
||||
// nothing
|
||||
}
|
||||
|
||||
void compute( InputArray leftarr, InputArray rightarr, OutputArray disparr ) CV_OVERRIDE
|
||||
|
||||
@@ -345,7 +345,7 @@ TEST_F(fisheyeTest, Calibration)
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
|
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
|
||||
CV_Assert(fs_left.isOpened());
|
||||
for(int i = 0; i < n_images; ++i)
|
||||
@@ -373,6 +373,53 @@ TEST_F(fisheyeTest, Calibration)
|
||||
EXPECT_MAT_NEAR(theD, this->D, 1e-10);
|
||||
}
|
||||
|
||||
TEST_F(fisheyeTest, CalibrationWithFixedFocalLength)
|
||||
{
|
||||
const int n_images = 34;
|
||||
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
|
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
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 )] >> imagePoints[i];
|
||||
fs_left.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();
|
||||
|
||||
int flag = 0;
|
||||
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
|
||||
flag |= cv::fisheye::CALIB_CHECK_COND;
|
||||
flag |= cv::fisheye::CALIB_FIX_SKEW;
|
||||
flag |= cv::fisheye::CALIB_FIX_FOCAL_LENGTH;
|
||||
flag |= cv::fisheye::CALIB_USE_INTRINSIC_GUESS;
|
||||
|
||||
cv::Matx33d theK = this->K;
|
||||
const cv::Matx33d newK(
|
||||
558.478088, 0.000000, 620.458461,
|
||||
0.000000, 560.506767, 381.939362,
|
||||
0.000000, 0.000000, 1.000000);
|
||||
|
||||
cv::Vec4d theD;
|
||||
const cv::Vec4d newD(-0.001461, -0.003298, 0.006057, -0.003742);
|
||||
|
||||
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
|
||||
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
|
||||
|
||||
// ensure that CALIB_FIX_FOCAL_LENGTH works and focal lenght has not changed
|
||||
EXPECT_EQ(theK(0,0), K(0,0));
|
||||
EXPECT_EQ(theK(1,1), K(1,1));
|
||||
|
||||
EXPECT_MAT_NEAR(theK, newK, 1e-6);
|
||||
EXPECT_MAT_NEAR(theD, newD, 1e-6);
|
||||
}
|
||||
|
||||
TEST_F(fisheyeTest, Homography)
|
||||
{
|
||||
const int n_images = 1;
|
||||
@@ -380,7 +427,7 @@ TEST_F(fisheyeTest, Homography)
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
|
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
|
||||
CV_Assert(fs_left.isOpened());
|
||||
for(int i = 0; i < n_images; ++i)
|
||||
@@ -498,7 +545,7 @@ TEST_F(fisheyeTest, stereoRectify)
|
||||
"For the purpose of continuity the following should be true: cv::CALIB_ZERO_DISPARITY == cv::fisheye::CALIB_ZERO_DISPARITY"
|
||||
);
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
|
||||
cv::Size calibration_size = this->imageSize, requested_size = calibration_size;
|
||||
cv::Matx33d K1 = this->K, K2 = K1;
|
||||
@@ -599,7 +646,7 @@ TEST_F(fisheyeTest, stereoCalibrate)
|
||||
{
|
||||
const int n_images = 34;
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
|
||||
std::vector<std::vector<cv::Point2d> > leftPoints(n_images);
|
||||
std::vector<std::vector<cv::Point2d> > rightPoints(n_images);
|
||||
@@ -666,7 +713,7 @@ TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic)
|
||||
{
|
||||
const int n_images = 34;
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
|
||||
std::vector<std::vector<cv::Point2d> > leftPoints(n_images);
|
||||
std::vector<std::vector<cv::Point2d> > rightPoints(n_images);
|
||||
@@ -822,6 +869,7 @@ const cv::Matx33d fisheyeTest::K(558.478087865323, 0, 620.45851536
|
||||
|
||||
const cv::Vec4d fisheyeTest::D(-0.0014613319981768, -0.00329861110580401, 0.00605760088590183, -0.00374209380722371);
|
||||
|
||||
|
||||
const cv::Matx33d fisheyeTest::R ( 9.9756700084424932e-01, 6.9698277640183867e-02, 1.4929569991321144e-03,
|
||||
-6.9711825162322980e-02, 9.9748249845531767e-01, 1.2997180766418455e-02,
|
||||
-5.8331736398316541e-04,-1.3069635393884985e-02, 9.9991441852366736e-01);
|
||||
|
||||
@@ -837,6 +837,43 @@ TEST(Calib3d_SolvePnPRansac, double_support)
|
||||
EXPECT_LE(cvtest::norm(t, Mat_<double>(tF), NORM_INF), 1e-3);
|
||||
}
|
||||
|
||||
TEST(Calib3d_SolvePnPRansac, bad_input_points_19253)
|
||||
{
|
||||
// with this specific data
|
||||
// when computing the final pose using points in the consensus set with SOLVEPNP_ITERATIVE and solvePnP()
|
||||
// an exception is thrown from solvePnP because there are 5 non-coplanar 3D points and the DLT algorithm needs at least 6 non-coplanar 3D points
|
||||
// with PR #19253 we choose to return true, with the pose estimated from the MSS stage instead of throwing the exception
|
||||
|
||||
float pts2d_[] = {
|
||||
-5.38358629e-01f, -5.09638414e-02f,
|
||||
-5.07192254e-01f, -2.20743284e-01f,
|
||||
-5.43107152e-01f, -4.90474701e-02f,
|
||||
-5.54325163e-01f, -1.86715424e-01f,
|
||||
-5.59334219e-01f, -4.01909500e-02f,
|
||||
-5.43504596e-01f, -4.61776406e-02f
|
||||
};
|
||||
Mat pts2d(6, 2, CV_32FC1, pts2d_);
|
||||
|
||||
float pts3d_[] = {
|
||||
-3.01153604e-02f, -1.55665115e-01f, 4.50000018e-01f,
|
||||
4.27827090e-01f, 4.28645730e-01f, 1.08600008e+00f,
|
||||
-3.14165242e-02f, -1.52656138e-01f, 4.50000018e-01f,
|
||||
-1.46217480e-01f, 5.57961613e-02f, 7.17000008e-01f,
|
||||
-4.89348806e-02f, -1.38795510e-01f, 4.47000027e-01f,
|
||||
-3.13065052e-02f, -1.52636901e-01f, 4.51000035e-01f
|
||||
};
|
||||
Mat pts3d(6, 3, CV_32FC1, pts3d_);
|
||||
|
||||
Mat camera_mat = Mat::eye(3, 3, CV_64FC1);
|
||||
Mat rvec, tvec;
|
||||
vector<int> inliers;
|
||||
|
||||
// solvePnPRansac will return true with 5 inliers, which means the result is from MSS stage.
|
||||
bool result = solvePnPRansac(pts3d, pts2d, camera_mat, noArray(), rvec, tvec, false, 100, 4.f / 460.f, 0.99, inliers);
|
||||
EXPECT_EQ(inliers.size(), size_t(5));
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
|
||||
TEST(Calib3d_SolvePnP, input_type)
|
||||
{
|
||||
Matx33d intrinsics(5.4794130238156129e+002, 0., 2.9835545700043139e+002, 0.,
|
||||
|
||||
@@ -18,8 +18,12 @@ ocv_add_dispatched_file_force_all(test_intrin256 TEST AVX2 AVX512_SKX)
|
||||
ocv_add_dispatched_file_force_all(test_intrin512 TEST AVX512_SKX)
|
||||
|
||||
|
||||
set(PARALLEL_ENABLE_PLUGINS_DEFAULT ON)
|
||||
if(EMSCRIPTEN OR IOS OR WINRT)
|
||||
set(PARALLEL_ENABLE_PLUGINS_DEFAULT OFF)
|
||||
endif()
|
||||
# parallel backends configuration
|
||||
set(PARALLEL_ENABLE_PLUGINS "ON" CACHE BOOL "Allow building parallel plugin support")
|
||||
set(PARALLEL_ENABLE_PLUGINS "${PARALLEL_ENABLE_PLUGINS_DEFAULT}" CACHE BOOL "Allow building parallel plugin support")
|
||||
# TODO building plugins with OpenCV is not supported yet
|
||||
#set(PARALLEL_PLUGIN_LIST "" CACHE STRING "List of parallel backends to be compiled as plugins (tbb, openmp or special value 'all')")
|
||||
#string(REPLACE "," ";" PARALLEL_PLUGIN_LIST "${PARALLEL_PLUGIN_LIST}") # support comma-separated list (,) too
|
||||
|
||||
@@ -538,6 +538,16 @@ _AccTp normInf(const _Tp* a, const _Tp* b, int n)
|
||||
*/
|
||||
CV_EXPORTS_W float cubeRoot(float val);
|
||||
|
||||
/** @overload
|
||||
|
||||
cubeRoot with argument of `double` type calls `std::cbrt(double)`
|
||||
*/
|
||||
static inline
|
||||
double cubeRoot(double val)
|
||||
{
|
||||
return std::cbrt(val);
|
||||
}
|
||||
|
||||
/** @brief Calculates the angle of a 2D vector in degrees.
|
||||
|
||||
The function fastAtan2 calculates the full-range angle of an input 2D vector. The angle is measured
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <opencv2/core/async.hpp>
|
||||
#include <opencv2/core/detail/async_promise.hpp>
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -144,7 +145,30 @@ AsyncArray testAsyncException()
|
||||
return p.getArrayResult();
|
||||
}
|
||||
|
||||
//! @}
|
||||
}} // namespace
|
||||
namespace fs {
|
||||
CV_EXPORTS_W cv::String getCacheDirectoryForDownloads();
|
||||
} // namespace fs
|
||||
|
||||
//! @} // core_utils
|
||||
} // namespace cv::utils
|
||||
|
||||
//! @cond IGNORED
|
||||
|
||||
CV_WRAP static inline
|
||||
int setLogLevel(int level)
|
||||
{
|
||||
// NB: Binding generators doesn't work with enums properly yet, so we define separate overload here
|
||||
return cv::utils::logging::setLogLevel((cv::utils::logging::LogLevel)level);
|
||||
}
|
||||
|
||||
CV_WRAP static inline
|
||||
int getLogLevel()
|
||||
{
|
||||
return cv::utils::logging::getLogLevel();
|
||||
}
|
||||
|
||||
//! @endcond IGNORED
|
||||
|
||||
} // namespaces cv / utils
|
||||
|
||||
#endif // OPENCV_CORE_BINDINGS_UTILS_HPP
|
||||
|
||||
@@ -76,6 +76,9 @@
|
||||
#if defined __PPC64__ && defined __GNUC__ && defined _ARCH_PWR8 \
|
||||
&& !defined(OPENCV_SKIP_INCLUDE_ALTIVEC_H)
|
||||
#include <altivec.h>
|
||||
#undef vector
|
||||
#undef bool
|
||||
#undef pixel
|
||||
#endif
|
||||
|
||||
#if defined(CV_INLINE_ROUND_FLT)
|
||||
|
||||
@@ -104,7 +104,7 @@ template<typename _Tp> struct V_TypeTraits
|
||||
{
|
||||
};
|
||||
|
||||
#define CV_INTRIN_DEF_TYPE_TRAITS(type, int_type_, uint_type_, abs_type_, w_type_, q_type_, sum_type_, nlanes128_) \
|
||||
#define CV_INTRIN_DEF_TYPE_TRAITS(type, int_type_, uint_type_, abs_type_, w_type_, q_type_, sum_type_) \
|
||||
template<> struct V_TypeTraits<type> \
|
||||
{ \
|
||||
typedef type value_type; \
|
||||
@@ -114,7 +114,6 @@ template<typename _Tp> struct V_TypeTraits
|
||||
typedef w_type_ w_type; \
|
||||
typedef q_type_ q_type; \
|
||||
typedef sum_type_ sum_type; \
|
||||
enum { nlanes128 = nlanes128_ }; \
|
||||
\
|
||||
static inline int_type reinterpret_int(type x) \
|
||||
{ \
|
||||
@@ -131,7 +130,7 @@ template<typename _Tp> struct V_TypeTraits
|
||||
} \
|
||||
}
|
||||
|
||||
#define CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(type, int_type_, uint_type_, abs_type_, w_type_, sum_type_, nlanes128_) \
|
||||
#define CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(type, int_type_, uint_type_, abs_type_, w_type_, sum_type_) \
|
||||
template<> struct V_TypeTraits<type> \
|
||||
{ \
|
||||
typedef type value_type; \
|
||||
@@ -140,7 +139,6 @@ template<typename _Tp> struct V_TypeTraits
|
||||
typedef uint_type_ uint_type; \
|
||||
typedef w_type_ w_type; \
|
||||
typedef sum_type_ sum_type; \
|
||||
enum { nlanes128 = nlanes128_ }; \
|
||||
\
|
||||
static inline int_type reinterpret_int(type x) \
|
||||
{ \
|
||||
@@ -157,16 +155,16 @@ template<typename _Tp> struct V_TypeTraits
|
||||
} \
|
||||
}
|
||||
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(uchar, schar, uchar, uchar, ushort, unsigned, unsigned, 16);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(schar, schar, uchar, uchar, short, int, int, 16);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(ushort, short, ushort, ushort, unsigned, uint64, unsigned, 8);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(short, short, ushort, ushort, int, int64, int, 8);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(unsigned, int, unsigned, unsigned, uint64, unsigned, 4);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int, int, unsigned, unsigned, int64, int, 4);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(float, int, unsigned, float, double, float, 4);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(uint64, int64, uint64, uint64, void, uint64, 2);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int64, int64, uint64, uint64, void, int64, 2);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(double, int64, uint64, double, void, double, 2);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(uchar, schar, uchar, uchar, ushort, unsigned, unsigned);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(schar, schar, uchar, uchar, short, int, int);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(ushort, short, ushort, ushort, unsigned, uint64, unsigned);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS(short, short, ushort, ushort, int, int64, int);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(unsigned, int, unsigned, unsigned, uint64, unsigned);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int, int, unsigned, unsigned, int64, int);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(float, int, unsigned, float, double, float);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(uint64, int64, uint64, uint64, void, uint64);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int64, int64, uint64, uint64, void, int64);
|
||||
CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(double, int64, uint64, double, void, double);
|
||||
|
||||
#ifndef CV_DOXYGEN
|
||||
|
||||
@@ -314,54 +312,6 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_INTRIN(typ, vtyp, short_typ, prefix, loadsfx) \
|
||||
inline vtyp vx_setall_##short_typ(typ v) { return prefix##_setall_##short_typ(v); } \
|
||||
inline vtyp vx_setzero_##short_typ() { return prefix##_setzero_##short_typ(); } \
|
||||
inline vtyp vx_##loadsfx(const typ* ptr) { return prefix##_##loadsfx(ptr); } \
|
||||
inline vtyp vx_##loadsfx##_aligned(const typ* ptr) { return prefix##_##loadsfx##_aligned(ptr); } \
|
||||
inline vtyp vx_##loadsfx##_low(const typ* ptr) { return prefix##_##loadsfx##_low(ptr); } \
|
||||
inline vtyp vx_##loadsfx##_halves(const typ* ptr0, const typ* ptr1) { return prefix##_##loadsfx##_halves(ptr0, ptr1); } \
|
||||
inline void vx_store(typ* ptr, const vtyp& v) { return v_store(ptr, v); } \
|
||||
inline void vx_store_aligned(typ* ptr, const vtyp& v) { return v_store_aligned(ptr, v); } \
|
||||
inline vtyp vx_lut(const typ* ptr, const int* idx) { return prefix##_lut(ptr, idx); } \
|
||||
inline vtyp vx_lut_pairs(const typ* ptr, const int* idx) { return prefix##_lut_pairs(ptr, idx); }
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_LUT_QUAD(typ, vtyp, prefix) \
|
||||
inline vtyp vx_lut_quads(const typ* ptr, const int* idx) { return prefix##_lut_quads(ptr, idx); }
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(typ, wtyp, prefix) \
|
||||
inline wtyp vx_load_expand(const typ* ptr) { return prefix##_load_expand(ptr); }
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND_Q(typ, qtyp, prefix) \
|
||||
inline qtyp vx_load_expand_q(const typ* ptr) { return prefix##_load_expand_q(ptr); }
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(typ, vtyp, short_typ, wtyp, qtyp, prefix, loadsfx) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(typ, vtyp, short_typ, prefix, loadsfx) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(typ, vtyp, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(typ, wtyp, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND_Q(typ, qtyp, prefix)
|
||||
|
||||
#define CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(uchar, v_uint8, u8, v_uint16, v_uint32, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(schar, v_int8, s8, v_int16, v_int32, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(ushort, v_uint16, u16, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(ushort, v_uint16, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(ushort, v_uint32, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(short, v_int16, s16, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(short, v_int16, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(short, v_int32, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(int, v_int32, s32, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(int, v_int32, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(int, v_int64, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(unsigned, v_uint32, u32, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(unsigned, v_uint32, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(unsigned, v_uint64, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(float, v_float32, f32, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LUT_QUAD(float, v_float32, prefix) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(int64, v_int64, s64, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(uint64, v_uint64, u64, prefix, load) \
|
||||
CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(float16_t, v_float32, prefix)
|
||||
|
||||
template<typename _Tp> struct V_RegTraits
|
||||
{
|
||||
};
|
||||
@@ -421,6 +371,7 @@ template<typename _Tp> struct V_RegTraits
|
||||
CV_DEF_REG_TRAITS(v512, v_int64x8, int64, s64, v_uint64x8, void, void, v_int64x8, void);
|
||||
CV_DEF_REG_TRAITS(v512, v_float64x8, double, f64, v_float64x8, void, void, v_int64x8, v_int32x16);
|
||||
#endif
|
||||
//! @endcond
|
||||
|
||||
#if CV_SIMD512 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 512)
|
||||
#define CV__SIMD_NAMESPACE simd512
|
||||
@@ -429,21 +380,33 @@ namespace CV__SIMD_NAMESPACE {
|
||||
#define CV_SIMD_64F CV_SIMD512_64F
|
||||
#define CV_SIMD_FP16 CV_SIMD512_FP16
|
||||
#define CV_SIMD_WIDTH 64
|
||||
//! @addtogroup core_hal_intrin
|
||||
//! @{
|
||||
//! @brief Maximum available vector register capacity 8-bit unsigned integer values
|
||||
typedef v_uint8x64 v_uint8;
|
||||
//! @brief Maximum available vector register capacity 8-bit signed integer values
|
||||
typedef v_int8x64 v_int8;
|
||||
//! @brief Maximum available vector register capacity 16-bit unsigned integer values
|
||||
typedef v_uint16x32 v_uint16;
|
||||
//! @brief Maximum available vector register capacity 16-bit signed integer values
|
||||
typedef v_int16x32 v_int16;
|
||||
//! @brief Maximum available vector register capacity 32-bit unsigned integer values
|
||||
typedef v_uint32x16 v_uint32;
|
||||
//! @brief Maximum available vector register capacity 32-bit signed integer values
|
||||
typedef v_int32x16 v_int32;
|
||||
//! @brief Maximum available vector register capacity 64-bit unsigned integer values
|
||||
typedef v_uint64x8 v_uint64;
|
||||
//! @brief Maximum available vector register capacity 64-bit signed integer values
|
||||
typedef v_int64x8 v_int64;
|
||||
//! @brief Maximum available vector register capacity 32-bit floating point values (single precision)
|
||||
typedef v_float32x16 v_float32;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v512)
|
||||
#if CV_SIMD512_64F
|
||||
#if CV_SIMD512_64F
|
||||
//! @brief Maximum available vector register capacity 64-bit floating point values (double precision)
|
||||
typedef v_float64x8 v_float64;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v512, load)
|
||||
#endif
|
||||
inline void vx_cleanup() { v512_cleanup(); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
#define VXPREFIX(func) v512##func
|
||||
} // namespace
|
||||
using namespace CV__SIMD_NAMESPACE;
|
||||
#elif CV_SIMD256 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 256)
|
||||
@@ -453,21 +416,33 @@ namespace CV__SIMD_NAMESPACE {
|
||||
#define CV_SIMD_64F CV_SIMD256_64F
|
||||
#define CV_SIMD_FP16 CV_SIMD256_FP16
|
||||
#define CV_SIMD_WIDTH 32
|
||||
//! @addtogroup core_hal_intrin
|
||||
//! @{
|
||||
//! @brief Maximum available vector register capacity 8-bit unsigned integer values
|
||||
typedef v_uint8x32 v_uint8;
|
||||
//! @brief Maximum available vector register capacity 8-bit signed integer values
|
||||
typedef v_int8x32 v_int8;
|
||||
//! @brief Maximum available vector register capacity 16-bit unsigned integer values
|
||||
typedef v_uint16x16 v_uint16;
|
||||
//! @brief Maximum available vector register capacity 16-bit signed integer values
|
||||
typedef v_int16x16 v_int16;
|
||||
//! @brief Maximum available vector register capacity 32-bit unsigned integer values
|
||||
typedef v_uint32x8 v_uint32;
|
||||
//! @brief Maximum available vector register capacity 32-bit signed integer values
|
||||
typedef v_int32x8 v_int32;
|
||||
//! @brief Maximum available vector register capacity 64-bit unsigned integer values
|
||||
typedef v_uint64x4 v_uint64;
|
||||
//! @brief Maximum available vector register capacity 64-bit signed integer values
|
||||
typedef v_int64x4 v_int64;
|
||||
//! @brief Maximum available vector register capacity 32-bit floating point values (single precision)
|
||||
typedef v_float32x8 v_float32;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v256)
|
||||
#if CV_SIMD256_64F
|
||||
//! @brief Maximum available vector register capacity 64-bit floating point values (double precision)
|
||||
typedef v_float64x4 v_float64;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v256, load)
|
||||
#endif
|
||||
inline void vx_cleanup() { v256_cleanup(); }
|
||||
//! @}
|
||||
|
||||
#define VXPREFIX(func) v256##func
|
||||
} // namespace
|
||||
using namespace CV__SIMD_NAMESPACE;
|
||||
#elif (CV_SIMD128 || CV_SIMD128_CPP) && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 128)
|
||||
@@ -480,25 +455,228 @@ namespace CV__SIMD_NAMESPACE {
|
||||
#define CV_SIMD CV_SIMD128
|
||||
#define CV_SIMD_64F CV_SIMD128_64F
|
||||
#define CV_SIMD_WIDTH 16
|
||||
//! @addtogroup core_hal_intrin
|
||||
//! @{
|
||||
//! @brief Maximum available vector register capacity 8-bit unsigned integer values
|
||||
typedef v_uint8x16 v_uint8;
|
||||
//! @brief Maximum available vector register capacity 8-bit signed integer values
|
||||
typedef v_int8x16 v_int8;
|
||||
//! @brief Maximum available vector register capacity 16-bit unsigned integer values
|
||||
typedef v_uint16x8 v_uint16;
|
||||
//! @brief Maximum available vector register capacity 16-bit signed integer values
|
||||
typedef v_int16x8 v_int16;
|
||||
//! @brief Maximum available vector register capacity 32-bit unsigned integer values
|
||||
typedef v_uint32x4 v_uint32;
|
||||
//! @brief Maximum available vector register capacity 32-bit signed integer values
|
||||
typedef v_int32x4 v_int32;
|
||||
//! @brief Maximum available vector register capacity 64-bit unsigned integer values
|
||||
typedef v_uint64x2 v_uint64;
|
||||
//! @brief Maximum available vector register capacity 64-bit signed integer values
|
||||
typedef v_int64x2 v_int64;
|
||||
//! @brief Maximum available vector register capacity 32-bit floating point values (single precision)
|
||||
typedef v_float32x4 v_float32;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v)
|
||||
#if CV_SIMD128_64F
|
||||
//! @brief Maximum available vector register capacity 64-bit floating point values (double precision)
|
||||
typedef v_float64x2 v_float64;
|
||||
CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v, load)
|
||||
#endif
|
||||
inline void vx_cleanup() { v_cleanup(); }
|
||||
//! @}
|
||||
|
||||
#define VXPREFIX(func) v##func
|
||||
} // namespace
|
||||
using namespace CV__SIMD_NAMESPACE;
|
||||
#endif
|
||||
|
||||
namespace CV__SIMD_NAMESPACE {
|
||||
//! @addtogroup core_hal_intrin
|
||||
//! @{
|
||||
//! @name Wide init with value
|
||||
//! @{
|
||||
//! @brief Create maximum available capacity vector with elements set to a specific value
|
||||
inline v_uint8 vx_setall_u8(uchar v) { return VXPREFIX(_setall_u8)(v); }
|
||||
inline v_int8 vx_setall_s8(schar v) { return VXPREFIX(_setall_s8)(v); }
|
||||
inline v_uint16 vx_setall_u16(ushort v) { return VXPREFIX(_setall_u16)(v); }
|
||||
inline v_int16 vx_setall_s16(short v) { return VXPREFIX(_setall_s16)(v); }
|
||||
inline v_int32 vx_setall_s32(int v) { return VXPREFIX(_setall_s32)(v); }
|
||||
inline v_uint32 vx_setall_u32(unsigned v) { return VXPREFIX(_setall_u32)(v); }
|
||||
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
|
||||
inline v_float64 vx_setall_f64(double v) { return VXPREFIX(_setall_f64)(v); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide init with zero
|
||||
//! @{
|
||||
//! @brief Create maximum available capacity vector with elements set to zero
|
||||
inline v_uint8 vx_setzero_u8() { return VXPREFIX(_setzero_u8)(); }
|
||||
inline v_int8 vx_setzero_s8() { return VXPREFIX(_setzero_s8)(); }
|
||||
inline v_uint16 vx_setzero_u16() { return VXPREFIX(_setzero_u16)(); }
|
||||
inline v_int16 vx_setzero_s16() { return VXPREFIX(_setzero_s16)(); }
|
||||
inline v_int32 vx_setzero_s32() { return VXPREFIX(_setzero_s32)(); }
|
||||
inline v_uint32 vx_setzero_u32() { return VXPREFIX(_setzero_u32)(); }
|
||||
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
|
||||
inline v_float64 vx_setzero_f64() { return VXPREFIX(_setzero_f64)(); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide load from memory
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents from memory
|
||||
inline v_uint8 vx_load(const uchar * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
inline v_int8 vx_load(const schar * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
inline v_uint16 vx_load(const ushort * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
inline v_int16 vx_load(const short * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
inline v_int32 vx_load(const int * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
inline v_uint32 vx_load(const unsigned * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
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
|
||||
inline v_float64 vx_load(const double * ptr) { return VXPREFIX(_load)(ptr); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide load from memory(aligned)
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents from memory(aligned)
|
||||
inline v_uint8 vx_load_aligned(const uchar * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
inline v_int8 vx_load_aligned(const schar * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
inline v_uint16 vx_load_aligned(const ushort * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
inline v_int16 vx_load_aligned(const short * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
inline v_int32 vx_load_aligned(const int * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
inline v_uint32 vx_load_aligned(const unsigned * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
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
|
||||
inline v_float64 vx_load_aligned(const double * ptr) { return VXPREFIX(_load_aligned)(ptr); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide load lower half from memory
|
||||
//! @{
|
||||
//! @brief Load lower half of maximum available capacity register from memory
|
||||
inline v_uint8 vx_load_low(const uchar * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
inline v_int8 vx_load_low(const schar * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
inline v_uint16 vx_load_low(const ushort * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
inline v_int16 vx_load_low(const short * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
inline v_int32 vx_load_low(const int * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
inline v_uint32 vx_load_low(const unsigned * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
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
|
||||
inline v_float64 vx_load_low(const double * ptr) { return VXPREFIX(_load_low)(ptr); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide load halfs from memory
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents from two memory blocks
|
||||
inline v_uint8 vx_load_halves(const uchar * ptr0, const uchar * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
inline v_int8 vx_load_halves(const schar * ptr0, const schar * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
inline v_uint16 vx_load_halves(const ushort * ptr0, const ushort * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
inline v_int16 vx_load_halves(const short * ptr0, const short * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
inline v_int32 vx_load_halves(const int * ptr0, const int * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
inline v_uint32 vx_load_halves(const unsigned * ptr0, const unsigned * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
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
|
||||
inline v_float64 vx_load_halves(const double * ptr0, const double * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide LUT of elements
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents with array elements by provided indexes
|
||||
inline v_uint8 vx_lut(const uchar * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
inline v_int8 vx_lut(const schar * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
inline v_uint16 vx_lut(const ushort * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
inline v_int16 vx_lut(const short* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
inline v_int32 vx_lut(const int* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
inline v_uint32 vx_lut(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
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
|
||||
inline v_float64 vx_lut(const double* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide LUT of element pairs
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents with array element pairs by provided indexes
|
||||
inline v_uint8 vx_lut_pairs(const uchar * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
inline v_int8 vx_lut_pairs(const schar * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
inline v_uint16 vx_lut_pairs(const ushort * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
inline v_int16 vx_lut_pairs(const short* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
inline v_int32 vx_lut_pairs(const int* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
inline v_uint32 vx_lut_pairs(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
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
|
||||
inline v_float64 vx_lut_pairs(const double* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); }
|
||||
#endif
|
||||
//! @}
|
||||
|
||||
//! @name Wide LUT of element quads
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents with array element quads by provided indexes
|
||||
inline v_uint8 vx_lut_quads(const uchar* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_int8 vx_lut_quads(const schar* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_uint16 vx_lut_quads(const ushort* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_int16 vx_lut_quads(const short* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_int32 vx_lut_quads(const int* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_uint32 vx_lut_quads(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
inline v_float32 vx_lut_quads(const float* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); }
|
||||
//! @}
|
||||
|
||||
//! @name Wide load with double expansion
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents from memory with double expand
|
||||
inline v_uint16 vx_load_expand(const uchar * ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_int16 vx_load_expand(const schar * ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_uint32 vx_load_expand(const ushort * ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_int32 vx_load_expand(const short* ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_int64 vx_load_expand(const int* ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_uint64 vx_load_expand(const unsigned* ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
inline v_float32 vx_load_expand(const float16_t * ptr) { return VXPREFIX(_load_expand)(ptr); }
|
||||
//! @}
|
||||
|
||||
//! @name Wide load with quad expansion
|
||||
//! @{
|
||||
//! @brief Load maximum available capacity register contents from memory with quad expand
|
||||
inline v_uint32 vx_load_expand_q(const uchar * ptr) { return VXPREFIX(_load_expand_q)(ptr); }
|
||||
inline v_int32 vx_load_expand_q(const schar * ptr) { return VXPREFIX(_load_expand_q)(ptr); }
|
||||
//! @}
|
||||
|
||||
/** @brief SIMD processing state cleanup call */
|
||||
inline void vx_cleanup() { VXPREFIX(_cleanup)(); }
|
||||
|
||||
|
||||
//! @cond IGNORED
|
||||
|
||||
// backward compatibility
|
||||
template<typename _Tp, typename _Tvec> static inline
|
||||
void vx_store(_Tp* dst, const _Tvec& v) { return v_store(dst, v); }
|
||||
// backward compatibility
|
||||
template<typename _Tp, typename _Tvec> static inline
|
||||
void vx_store_aligned(_Tp* dst, const _Tvec& v) { return v_store_aligned(dst, v); }
|
||||
|
||||
//! @endcond
|
||||
|
||||
|
||||
//! @}
|
||||
#undef VXPREFIX
|
||||
} // namespace
|
||||
|
||||
//! @cond IGNORED
|
||||
#ifndef CV_SIMD_64F
|
||||
#define CV_SIMD_64F 0
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1539,6 +1539,26 @@ OPENCV_HAL_IMPL_NEON_SELECT(v_float32x4, f32, u32)
|
||||
OPENCV_HAL_IMPL_NEON_SELECT(v_float64x2, f64, u64)
|
||||
#endif
|
||||
|
||||
#if CV_NEON_AARCH64
|
||||
#define OPENCV_HAL_IMPL_NEON_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix) \
|
||||
inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \
|
||||
{ \
|
||||
b0.val = vmovl_##suffix(vget_low_##suffix(a.val)); \
|
||||
b1.val = vmovl_high_##suffix(a.val); \
|
||||
} \
|
||||
inline _Tpwvec v_expand_low(const _Tpvec& a) \
|
||||
{ \
|
||||
return _Tpwvec(vmovl_##suffix(vget_low_##suffix(a.val))); \
|
||||
} \
|
||||
inline _Tpwvec v_expand_high(const _Tpvec& a) \
|
||||
{ \
|
||||
return _Tpwvec(vmovl_high_##suffix(a.val)); \
|
||||
} \
|
||||
inline _Tpwvec v_load_expand(const _Tp* ptr) \
|
||||
{ \
|
||||
return _Tpwvec(vmovl_##suffix(vld1_##suffix(ptr))); \
|
||||
}
|
||||
#else
|
||||
#define OPENCV_HAL_IMPL_NEON_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix) \
|
||||
inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \
|
||||
{ \
|
||||
@@ -1557,6 +1577,7 @@ inline _Tpwvec v_load_expand(const _Tp* ptr) \
|
||||
{ \
|
||||
return _Tpwvec(vmovl_##suffix(vld1_##suffix(ptr))); \
|
||||
}
|
||||
#endif
|
||||
|
||||
OPENCV_HAL_IMPL_NEON_EXPAND(v_uint8x16, v_uint16x8, uchar, u8)
|
||||
OPENCV_HAL_IMPL_NEON_EXPAND(v_int8x16, v_int16x8, schar, s8)
|
||||
|
||||
@@ -576,24 +576,24 @@ CV_ENUM_FLAGS(UMatData::MemoryFlag)
|
||||
|
||||
struct CV_EXPORTS MatSize
|
||||
{
|
||||
explicit MatSize(int* _p);
|
||||
int dims() const;
|
||||
explicit MatSize(int* _p) CV_NOEXCEPT;
|
||||
int dims() const CV_NOEXCEPT;
|
||||
Size operator()() const;
|
||||
const int& operator[](int i) const;
|
||||
int& operator[](int i);
|
||||
operator const int*() const; // TODO OpenCV 4.0: drop this
|
||||
bool operator == (const MatSize& sz) const;
|
||||
bool operator != (const MatSize& sz) const;
|
||||
operator const int*() const CV_NOEXCEPT; // TODO OpenCV 4.0: drop this
|
||||
bool operator == (const MatSize& sz) const CV_NOEXCEPT;
|
||||
bool operator != (const MatSize& sz) const CV_NOEXCEPT;
|
||||
|
||||
int* p;
|
||||
};
|
||||
|
||||
struct CV_EXPORTS MatStep
|
||||
{
|
||||
MatStep();
|
||||
explicit MatStep(size_t s);
|
||||
const size_t& operator[](int i) const;
|
||||
size_t& operator[](int i);
|
||||
MatStep() CV_NOEXCEPT;
|
||||
explicit MatStep(size_t s) CV_NOEXCEPT;
|
||||
const size_t& operator[](int i) const CV_NOEXCEPT;
|
||||
size_t& operator[](int i) CV_NOEXCEPT;
|
||||
operator size_t() const;
|
||||
MatStep& operator = (size_t s);
|
||||
|
||||
@@ -807,7 +807,7 @@ public:
|
||||
The constructed matrix can further be assigned to another matrix or matrix expression or can be
|
||||
allocated with Mat::create . In the former case, the old content is de-referenced.
|
||||
*/
|
||||
Mat();
|
||||
Mat() CV_NOEXCEPT;
|
||||
|
||||
/** @overload
|
||||
@param rows Number of rows in a 2D array.
|
||||
@@ -2193,7 +2193,7 @@ public:
|
||||
typedef MatConstIterator_<_Tp> const_iterator;
|
||||
|
||||
//! default constructor
|
||||
Mat_();
|
||||
Mat_() CV_NOEXCEPT;
|
||||
//! equivalent to Mat(_rows, _cols, DataType<_Tp>::type)
|
||||
Mat_(int _rows, int _cols);
|
||||
//! constructor that sets each matrix element to specified value
|
||||
@@ -2385,7 +2385,7 @@ class CV_EXPORTS UMat
|
||||
{
|
||||
public:
|
||||
//! default constructor
|
||||
UMat(UMatUsageFlags usageFlags = USAGE_DEFAULT);
|
||||
UMat(UMatUsageFlags usageFlags = USAGE_DEFAULT) CV_NOEXCEPT;
|
||||
//! constructs 2D matrix of the specified size and type
|
||||
// (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.)
|
||||
UMat(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT);
|
||||
|
||||
@@ -1116,11 +1116,11 @@ void Mat::push_back(const std::vector<_Tp>& v)
|
||||
///////////////////////////// MatSize ////////////////////////////
|
||||
|
||||
inline
|
||||
MatSize::MatSize(int* _p)
|
||||
MatSize::MatSize(int* _p) CV_NOEXCEPT
|
||||
: p(_p) {}
|
||||
|
||||
inline
|
||||
int MatSize::dims() const
|
||||
int MatSize::dims() const CV_NOEXCEPT
|
||||
{
|
||||
return (p - 1)[0];
|
||||
}
|
||||
@@ -1153,13 +1153,13 @@ int& MatSize::operator[](int i)
|
||||
}
|
||||
|
||||
inline
|
||||
MatSize::operator const int*() const
|
||||
MatSize::operator const int*() const CV_NOEXCEPT
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
inline
|
||||
bool MatSize::operator != (const MatSize& sz) const
|
||||
bool MatSize::operator != (const MatSize& sz) const CV_NOEXCEPT
|
||||
{
|
||||
return !(*this == sz);
|
||||
}
|
||||
@@ -1169,25 +1169,25 @@ bool MatSize::operator != (const MatSize& sz) const
|
||||
///////////////////////////// MatStep ////////////////////////////
|
||||
|
||||
inline
|
||||
MatStep::MatStep()
|
||||
MatStep::MatStep() CV_NOEXCEPT
|
||||
{
|
||||
p = buf; p[0] = p[1] = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
MatStep::MatStep(size_t s)
|
||||
MatStep::MatStep(size_t s) CV_NOEXCEPT
|
||||
{
|
||||
p = buf; p[0] = s; p[1] = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
const size_t& MatStep::operator[](int i) const
|
||||
const size_t& MatStep::operator[](int i) const CV_NOEXCEPT
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
inline
|
||||
size_t& MatStep::operator[](int i)
|
||||
size_t& MatStep::operator[](int i) CV_NOEXCEPT
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
@@ -1210,7 +1210,7 @@ inline MatStep& MatStep::operator = (size_t s)
|
||||
////////////////////////////// Mat_<_Tp> ////////////////////////////
|
||||
|
||||
template<typename _Tp> inline
|
||||
Mat_<_Tp>::Mat_()
|
||||
Mat_<_Tp>::Mat_() CV_NOEXCEPT
|
||||
: Mat()
|
||||
{
|
||||
flags = (flags & ~CV_MAT_TYPE_MASK) + traits::Type<_Tp>::value;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define CV_VERSION_MAJOR 4
|
||||
#define CV_VERSION_MINOR 5
|
||||
#define CV_VERSION_REVISION 2
|
||||
#define CV_VERSION_STATUS "-pre"
|
||||
#define CV_VERSION_STATUS ""
|
||||
|
||||
#define CVAUX_STR_EXP(__A) #__A
|
||||
#define CVAUX_STR(__A) CVAUX_STR_EXP(__A)
|
||||
|
||||
@@ -497,11 +497,13 @@ VSX_IMPL_CONV_EVEN_2_4(vec_uint4, vec_double2, vec_ctu, vec_ctuo)
|
||||
VSX_FINLINE(rt) fnm(const rg& a, int only_truncate) \
|
||||
{ \
|
||||
assert(only_truncate == 0); \
|
||||
CV_UNUSED(only_truncate); \
|
||||
CV_UNUSED(only_truncate); \
|
||||
return fn2(a); \
|
||||
}
|
||||
VSX_IMPL_CONV_2VARIANT(vec_int4, vec_float4, vec_cts, vec_cts)
|
||||
VSX_IMPL_CONV_2VARIANT(vec_uint4, vec_float4, vec_ctu, vec_ctu)
|
||||
VSX_IMPL_CONV_2VARIANT(vec_float4, vec_int4, vec_ctf, vec_ctf)
|
||||
VSX_IMPL_CONV_2VARIANT(vec_float4, vec_uint4, vec_ctf, vec_ctf)
|
||||
// define vec_cts for converting double precision to signed doubleword
|
||||
// which isn't compatible with xlc but its okay since Eigen only uses it for gcc
|
||||
VSX_IMPL_CONV_2VARIANT(vec_dword2, vec_double2, vec_cts, vec_ctsl)
|
||||
|
||||
@@ -627,7 +627,8 @@ static void arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst,
|
||||
(kind1 == _InputArray::MATX && (sz1 == Size(1,4) || sz1 == Size(1,1))) ||
|
||||
(kind2 == _InputArray::MATX && (sz2 == Size(1,4) || sz2 == Size(1,1))) )
|
||||
{
|
||||
if( checkScalar(*psrc1, type2, kind1, kind2) )
|
||||
if ((type1 == CV_64F && (sz1.height == 1 || sz1.height == 4)) &&
|
||||
checkScalar(*psrc1, type2, kind1, kind2))
|
||||
{
|
||||
// src1 is a scalar; swap it with src2
|
||||
swap(psrc1, psrc2);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/bindings_utils.hpp"
|
||||
#include <sstream>
|
||||
#include <opencv2/core/utils/filesystem.hpp>
|
||||
#include <opencv2/core/utils/filesystem.private.hpp>
|
||||
|
||||
namespace cv { namespace utils {
|
||||
|
||||
@@ -208,4 +210,15 @@ CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argume
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
namespace fs {
|
||||
cv::String getCacheDirectoryForDownloads()
|
||||
{
|
||||
#if OPENCV_HAVE_FILESYSTEM_SUPPORT
|
||||
return cv::utils::fs::getCacheDirectory("downloads", "OPENCV_DOWNLOADS_CACHE_DIR");
|
||||
#else
|
||||
CV_Error(Error::StsNotImplemented, "File system support is disabled in this OpenCV build!");
|
||||
#endif
|
||||
}
|
||||
} // namespace fs
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -1050,7 +1050,7 @@ bool ocl_convert_nv12_to_bgr(
|
||||
|
||||
k.args(clImageY, clImageUV, clBuffer, step, cols, rows);
|
||||
|
||||
size_t globalsize[] = { (size_t)cols, (size_t)rows };
|
||||
size_t globalsize[] = { (size_t)cols/2, (size_t)rows/2 };
|
||||
return k.run(2, globalsize, 0, false);
|
||||
}
|
||||
|
||||
@@ -1071,7 +1071,7 @@ bool ocl_convert_bgr_to_nv12(
|
||||
|
||||
k.args(clBuffer, step, cols, rows, clImageY, clImageUV);
|
||||
|
||||
size_t globalsize[] = { (size_t)cols, (size_t)rows };
|
||||
size_t globalsize[] = { (size_t)cols/2, (size_t)rows/2 };
|
||||
return k.run(2, globalsize, 0, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "mathfuncs_core.simd.hpp"
|
||||
#include "mathfuncs_core.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content
|
||||
|
||||
|
||||
#define IPP_DISABLE_MAGNITUDE_32F 1 // accuracy: https://github.com/opencv/opencv/issues/19506
|
||||
|
||||
|
||||
namespace cv { namespace hal {
|
||||
|
||||
///////////////////////////////////// ATAN2 ////////////////////////////////////
|
||||
@@ -44,8 +48,25 @@ void magnitude32f(const float* x, const float* y, float* mag, int len)
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CALL_HAL(magnitude32f, cv_hal_magnitude32f, x, y, mag, len);
|
||||
|
||||
#ifdef HAVE_IPP
|
||||
bool allowIPP = true;
|
||||
#ifdef IPP_DISABLE_MAGNITUDE_32F
|
||||
if (cv::ipp::getIppTopFeatures() & (
|
||||
#if IPP_VERSION_X100 >= 201700
|
||||
ippCPUID_AVX512F |
|
||||
#endif
|
||||
ippCPUID_AVX2)
|
||||
)
|
||||
{
|
||||
allowIPP = (len & 7) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// SSE42 performance issues
|
||||
CV_IPP_RUN(IPP_VERSION_X100 > 201800 || cv::ipp::getIppTopFeatures() != ippCPUID_SSE42, CV_INSTRUMENT_FUN_IPP(ippsMagnitude_32f, x, y, mag, len) >= 0);
|
||||
CV_IPP_RUN((IPP_VERSION_X100 > 201800 || cv::ipp::getIppTopFeatures() != ippCPUID_SSE42) && allowIPP,
|
||||
CV_INSTRUMENT_FUN_IPP(ippsMagnitude_32f, x, y, mag, len) >= 0);
|
||||
#endif
|
||||
|
||||
CV_CPU_DISPATCH(magnitude32f, (x, y, mag, len),
|
||||
CV_CPU_DISPATCH_MODES_ALL);
|
||||
|
||||
@@ -204,7 +204,7 @@ MatAllocator* Mat::getStdAllocator()
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
bool MatSize::operator==(const MatSize& sz) const
|
||||
bool MatSize::operator==(const MatSize& sz) const CV_NOEXCEPT
|
||||
{
|
||||
int d = dims();
|
||||
int dsz = sz.dims();
|
||||
@@ -337,7 +337,7 @@ void finalizeHdr(Mat& m)
|
||||
|
||||
//======================================= Mat ======================================================
|
||||
|
||||
Mat::Mat()
|
||||
Mat::Mat() CV_NOEXCEPT
|
||||
: flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0),
|
||||
datalimit(0), allocator(0), u(0), size(&rows), step(0)
|
||||
{}
|
||||
|
||||
@@ -536,8 +536,8 @@ flipVert( const uchar* src0, size_t sstep, uchar* dst0, size_t dstep, Size size,
|
||||
{
|
||||
v_int32 t0 = vx_load((int*)(src0 + i));
|
||||
v_int32 t1 = vx_load((int*)(src1 + i));
|
||||
vx_store((int*)(dst0 + i), t1);
|
||||
vx_store((int*)(dst1 + i), t0);
|
||||
v_store((int*)(dst0 + i), t1);
|
||||
v_store((int*)(dst1 + i), t0);
|
||||
}
|
||||
}
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
@@ -547,8 +547,8 @@ flipVert( const uchar* src0, size_t sstep, uchar* dst0, size_t dstep, Size size,
|
||||
{
|
||||
v_uint8 t0 = vx_load(src0 + i);
|
||||
v_uint8 t1 = vx_load(src1 + i);
|
||||
vx_store(dst0 + i, t1);
|
||||
vx_store(dst1 + i, t0);
|
||||
v_store(dst0 + i, t1);
|
||||
v_store(dst1 + i, t0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -91,63 +91,50 @@ void YUV2BGR_NV12_8u(
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
// each iteration computes 2*2=4 pixels
|
||||
int x2 = x*2;
|
||||
int y2 = y*2;
|
||||
|
||||
if (x + 1 < cols)
|
||||
{
|
||||
if (y + 1 < rows)
|
||||
{
|
||||
__global uchar* pDstRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
|
||||
__global uchar* pDstRow2 = pDstRow1 + bgrStep;
|
||||
if (x2 + 1 < cols) {
|
||||
if (y2 + 1 < rows) {
|
||||
__global uchar *pDstRow1 = pBGR + mad24(y2, bgrStep, mad24(x2, NCHANNELS, 0));
|
||||
__global uchar *pDstRow2 = pDstRow1 + bgrStep;
|
||||
|
||||
float4 Y1 = read_imagef(imgY, (int2)(x+0, y+0));
|
||||
float4 Y2 = read_imagef(imgY, (int2)(x+1, y+0));
|
||||
float4 Y3 = read_imagef(imgY, (int2)(x+0, y+1));
|
||||
float4 Y4 = read_imagef(imgY, (int2)(x+1, y+1));
|
||||
float4 Y1 = read_imagef(imgY, (int2)(x2 + 0, y2 + 0));
|
||||
float4 Y2 = read_imagef(imgY, (int2)(x2 + 1, y2 + 0));
|
||||
float4 Y3 = read_imagef(imgY, (int2)(x2 + 0, y2 + 1));
|
||||
float4 Y4 = read_imagef(imgY, (int2)(x2 + 1, y2 + 1));
|
||||
float4 Y = (float4)(Y1.x, Y2.x, Y3.x, Y4.x);
|
||||
|
||||
float4 UV = read_imagef(imgUV, (int2)(x/2, y/2)) - d2;
|
||||
float4 UV = read_imagef(imgUV, (int2)(x, y)) - d2;
|
||||
|
||||
__constant float* coeffs = c_YUV2RGBCoeffs_420;
|
||||
__constant float *coeffs = c_YUV2RGBCoeffs_420;
|
||||
|
||||
Y1 = max(0.f, Y1 - d1) * coeffs[0];
|
||||
Y2 = max(0.f, Y2 - d1) * coeffs[0];
|
||||
Y3 = max(0.f, Y3 - d1) * coeffs[0];
|
||||
Y4 = max(0.f, Y4 - d1) * coeffs[0];
|
||||
Y = max(0.f, Y - d1) * coeffs[0];
|
||||
|
||||
float ruv = fma(coeffs[4], UV.y, 0.0f);
|
||||
float guv = fma(coeffs[3], UV.y, fma(coeffs[2], UV.x, 0.0f));
|
||||
float buv = fma(coeffs[1], UV.x, 0.0f);
|
||||
|
||||
float R1 = (Y1.x + ruv) * CV_8U_MAX;
|
||||
float G1 = (Y1.x + guv) * CV_8U_MAX;
|
||||
float B1 = (Y1.x + buv) * CV_8U_MAX;
|
||||
float4 R = (Y + ruv) * CV_8U_MAX;
|
||||
float4 G = (Y + guv) * CV_8U_MAX;
|
||||
float4 B = (Y + buv) * CV_8U_MAX;
|
||||
|
||||
float R2 = (Y2.x + ruv) * CV_8U_MAX;
|
||||
float G2 = (Y2.x + guv) * CV_8U_MAX;
|
||||
float B2 = (Y2.x + buv) * CV_8U_MAX;
|
||||
pDstRow1[0*NCHANNELS + 0] = convert_uchar_sat(B.x);
|
||||
pDstRow1[0*NCHANNELS + 1] = convert_uchar_sat(G.x);
|
||||
pDstRow1[0*NCHANNELS + 2] = convert_uchar_sat(R.x);
|
||||
|
||||
float R3 = (Y3.x + ruv) * CV_8U_MAX;
|
||||
float G3 = (Y3.x + guv) * CV_8U_MAX;
|
||||
float B3 = (Y3.x + buv) * CV_8U_MAX;
|
||||
pDstRow1[1*NCHANNELS + 0] = convert_uchar_sat(B.y);
|
||||
pDstRow1[1*NCHANNELS + 1] = convert_uchar_sat(G.y);
|
||||
pDstRow1[1*NCHANNELS + 2] = convert_uchar_sat(R.y);
|
||||
|
||||
float R4 = (Y4.x + ruv) * CV_8U_MAX;
|
||||
float G4 = (Y4.x + guv) * CV_8U_MAX;
|
||||
float B4 = (Y4.x + buv) * CV_8U_MAX;
|
||||
pDstRow2[0*NCHANNELS + 0] = convert_uchar_sat(B.z);
|
||||
pDstRow2[0*NCHANNELS + 1] = convert_uchar_sat(G.z);
|
||||
pDstRow2[0*NCHANNELS + 2] = convert_uchar_sat(R.z);
|
||||
|
||||
pDstRow1[0*NCHANNELS + 0] = convert_uchar_sat(B1);
|
||||
pDstRow1[0*NCHANNELS + 1] = convert_uchar_sat(G1);
|
||||
pDstRow1[0*NCHANNELS + 2] = convert_uchar_sat(R1);
|
||||
|
||||
pDstRow1[1*NCHANNELS + 0] = convert_uchar_sat(B2);
|
||||
pDstRow1[1*NCHANNELS + 1] = convert_uchar_sat(G2);
|
||||
pDstRow1[1*NCHANNELS + 2] = convert_uchar_sat(R2);
|
||||
|
||||
pDstRow2[0*NCHANNELS + 0] = convert_uchar_sat(B3);
|
||||
pDstRow2[0*NCHANNELS + 1] = convert_uchar_sat(G3);
|
||||
pDstRow2[0*NCHANNELS + 2] = convert_uchar_sat(R3);
|
||||
|
||||
pDstRow2[1*NCHANNELS + 0] = convert_uchar_sat(B4);
|
||||
pDstRow2[1*NCHANNELS + 1] = convert_uchar_sat(G4);
|
||||
pDstRow2[1*NCHANNELS + 2] = convert_uchar_sat(R4);
|
||||
pDstRow2[1*NCHANNELS + 0] = convert_uchar_sat(B.w);
|
||||
pDstRow2[1*NCHANNELS + 1] = convert_uchar_sat(G.w);
|
||||
pDstRow2[1*NCHANNELS + 2] = convert_uchar_sat(R.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,12 +159,15 @@ void BGR2YUV_NV12_8u(
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
// each iteration computes 2*2=4 pixels
|
||||
int x2 = x*2;
|
||||
int y2 = y*2;
|
||||
|
||||
if (x < cols)
|
||||
if (x2 + 1 < cols)
|
||||
{
|
||||
if (y < rows)
|
||||
if (y2 + 1 < rows)
|
||||
{
|
||||
__global const uchar* pSrcRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
|
||||
__global const uchar* pSrcRow1 = pBGR + mad24(y2, bgrStep, mad24(x2, NCHANNELS, 0));
|
||||
__global const uchar* pSrcRow2 = pSrcRow1 + bgrStep;
|
||||
|
||||
float4 src_pix1 = convert_float4(vload4(0, pSrcRow1 + 0*NCHANNELS)) * CV_8U_SCALE;
|
||||
@@ -196,12 +186,12 @@ void BGR2YUV_NV12_8u(
|
||||
UV.x = fma(coeffs[3], src_pix1.z, fma(coeffs[4], src_pix1.y, fma(coeffs[5], src_pix1.x, d2)));
|
||||
UV.y = fma(coeffs[5], src_pix1.z, fma(coeffs[6], src_pix1.y, fma(coeffs[7], src_pix1.x, d2)));
|
||||
|
||||
write_imagef(imgY, (int2)(x+0, y+0), Y1);
|
||||
write_imagef(imgY, (int2)(x+1, y+0), Y2);
|
||||
write_imagef(imgY, (int2)(x+0, y+1), Y3);
|
||||
write_imagef(imgY, (int2)(x+1, y+1), Y4);
|
||||
write_imagef(imgY, (int2)(x2+0, y2+0), Y1);
|
||||
write_imagef(imgY, (int2)(x2+1, y2+0), Y2);
|
||||
write_imagef(imgY, (int2)(x2+0, y2+1), Y3);
|
||||
write_imagef(imgY, (int2)(x2+1, y2+1), Y4);
|
||||
|
||||
write_imagef(imgUV, (int2)((x/2), (y/2)), UV);
|
||||
write_imagef(imgUV, (int2)(x, y), UV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,11 @@ std::shared_ptr<ParallelForAPI> createParallelForAPI()
|
||||
try
|
||||
{
|
||||
CV_LOG_DEBUG(NULL, "core(parallel): trying backend: " << info.name << " (priority=" << info.priority << ")");
|
||||
CV_Assert(info.backendFactory);
|
||||
if (!info.backendFactory)
|
||||
{
|
||||
CV_LOG_DEBUG(NULL, "core(parallel): factory is not available (plugins require filesystem support): " << info.name);
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<ParallelForAPI> backend = info.backendFactory->create();
|
||||
if (!backend)
|
||||
{
|
||||
|
||||
@@ -6,17 +6,23 @@
|
||||
// Not a standalone header, part of parallel.cpp
|
||||
//
|
||||
|
||||
#include "opencv2/core/utils/filesystem.private.hpp" // OPENCV_HAVE_FILESYSTEM_SUPPORT
|
||||
|
||||
namespace cv { namespace parallel {
|
||||
|
||||
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS)
|
||||
#define DECLARE_DYNAMIC_BACKEND(name) \
|
||||
ParallelBackendInfo { \
|
||||
1000, name, createPluginParallelBackendFactory(name) \
|
||||
}
|
||||
},
|
||||
#else
|
||||
#define DECLARE_DYNAMIC_BACKEND(name) /* nothing */
|
||||
#endif
|
||||
|
||||
#define DECLARE_STATIC_BACKEND(name, createBackendAPI) \
|
||||
ParallelBackendInfo { \
|
||||
1000, name, std::make_shared<cv::parallel::StaticBackendFactory>([=] () -> std::shared_ptr<cv::parallel::ParallelForAPI> { return createBackendAPI(); }) \
|
||||
}
|
||||
},
|
||||
|
||||
static
|
||||
std::vector<ParallelBackendInfo>& getBuiltinParallelBackendsInfo()
|
||||
@@ -24,14 +30,14 @@ std::vector<ParallelBackendInfo>& getBuiltinParallelBackendsInfo()
|
||||
static std::vector<ParallelBackendInfo> g_backends
|
||||
{
|
||||
#ifdef HAVE_TBB
|
||||
DECLARE_STATIC_BACKEND("TBB", createParallelBackendTBB),
|
||||
DECLARE_STATIC_BACKEND("TBB", createParallelBackendTBB)
|
||||
#elif defined(PARALLEL_ENABLE_PLUGINS)
|
||||
DECLARE_DYNAMIC_BACKEND("ONETBB"), // dedicated oneTBB plugin (interface >= 12000, binary incompatibe with TBB 2017-2020)
|
||||
DECLARE_DYNAMIC_BACKEND("TBB"), // generic TBB plugins
|
||||
DECLARE_DYNAMIC_BACKEND("ONETBB") // dedicated oneTBB plugin (interface >= 12000, binary incompatibe with TBB 2017-2020)
|
||||
DECLARE_DYNAMIC_BACKEND("TBB") // generic TBB plugins
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENMP
|
||||
DECLARE_STATIC_BACKEND("OPENMP", createParallelBackendOpenMP),
|
||||
DECLARE_STATIC_BACKEND("OPENMP", createParallelBackendOpenMP)
|
||||
#elif defined(PARALLEL_ENABLE_PLUGINS)
|
||||
DECLARE_DYNAMIC_BACKEND("OPENMP") // TODO Intel OpenMP?
|
||||
#endif
|
||||
|
||||
+24
-11
@@ -128,11 +128,14 @@ void* allocSingletonNewBuffer(size_t size) { return malloc(size); }
|
||||
#endif
|
||||
|
||||
|
||||
#if CV_VSX && defined __linux__
|
||||
#if (defined __ppc64__ || defined __PPC64__) && defined __linux__
|
||||
# include "sys/auxv.h"
|
||||
# ifndef AT_HWCAP2
|
||||
# define AT_HWCAP2 26
|
||||
# endif
|
||||
# ifndef PPC_FEATURE2_ARCH_2_07
|
||||
# define PPC_FEATURE2_ARCH_2_07 0x80000000
|
||||
# endif
|
||||
# ifndef PPC_FEATURE2_ARCH_3_00
|
||||
# define PPC_FEATURE2_ARCH_3_00 0x00800000
|
||||
# endif
|
||||
@@ -345,7 +348,6 @@ struct HWFeatures
|
||||
|
||||
HWFeatures(bool run_initialize = false)
|
||||
{
|
||||
memset( have, 0, sizeof(have[0]) * MAX_FEATURE );
|
||||
if (run_initialize)
|
||||
initialize();
|
||||
}
|
||||
@@ -589,14 +591,25 @@ struct HWFeatures
|
||||
#ifdef __mips_msa
|
||||
have[CV_CPU_MSA] = true;
|
||||
#endif
|
||||
// there's no need to check VSX availability in runtime since it's always available on ppc64le CPUs
|
||||
have[CV_CPU_VSX] = (CV_VSX);
|
||||
// TODO: Check VSX3 availability in runtime for other platforms
|
||||
#if CV_VSX && defined __linux__
|
||||
uint64 hwcap2 = getauxval(AT_HWCAP2);
|
||||
have[CV_CPU_VSX3] = (hwcap2 & PPC_FEATURE2_ARCH_3_00);
|
||||
|
||||
#if (defined __ppc64__ || defined __PPC64__) && defined __linux__
|
||||
unsigned int hwcap = getauxval(AT_HWCAP);
|
||||
if (hwcap & PPC_FEATURE_HAS_VSX) {
|
||||
hwcap = getauxval(AT_HWCAP2);
|
||||
if (hwcap & PPC_FEATURE2_ARCH_3_00) {
|
||||
have[CV_CPU_VSX] = have[CV_CPU_VSX3] = true;
|
||||
} else {
|
||||
have[CV_CPU_VSX] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
have[CV_CPU_VSX3] = (CV_VSX3);
|
||||
// TODO: AIX, FreeBSD
|
||||
#if CV_VSX || defined _ARCH_PWR8 || defined __POWER9_VECTOR__
|
||||
have[CV_CPU_VSX] = true;
|
||||
#endif
|
||||
#if CV_VSX3 || defined __POWER9_VECTOR__
|
||||
have[CV_CPU_VSX3] = true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined __riscv && defined __riscv_vector
|
||||
@@ -730,7 +743,7 @@ struct HWFeatures
|
||||
}
|
||||
}
|
||||
|
||||
bool have[MAX_FEATURE+1];
|
||||
bool have[MAX_FEATURE+1]{};
|
||||
};
|
||||
|
||||
static HWFeatures featuresEnabled(true), featuresDisabled = HWFeatures(false);
|
||||
@@ -1862,7 +1875,7 @@ class ParseError
|
||||
{
|
||||
std::string bad_value;
|
||||
public:
|
||||
ParseError(const std::string bad_value_) :bad_value(bad_value_) {}
|
||||
ParseError(const std::string &bad_value_) :bad_value(bad_value_) {}
|
||||
std::string toString(const std::string ¶m) const
|
||||
{
|
||||
std::ostringstream out;
|
||||
|
||||
@@ -230,7 +230,7 @@ UMatDataAutoLock::~UMatDataAutoLock()
|
||||
|
||||
//////////////////////////////// UMat ////////////////////////////////
|
||||
|
||||
UMat::UMat(UMatUsageFlags _usageFlags)
|
||||
UMat::UMat(UMatUsageFlags _usageFlags) CV_NOEXCEPT
|
||||
: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows)
|
||||
{}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ static bool ocl_convert_nv12_to_bgr(cl_mem clImageY, cl_mem clImageUV, cl_mem cl
|
||||
|
||||
k.args(clImageY, clImageUV, clBuffer, step, cols, rows);
|
||||
|
||||
size_t globalsize[] = { (size_t)cols, (size_t)rows };
|
||||
size_t globalsize[] = { (size_t)cols/2, (size_t)rows/2 };
|
||||
return k.run(2, globalsize, 0, false);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ static bool ocl_convert_bgr_to_nv12(cl_mem clBuffer, int step, int cols, int row
|
||||
|
||||
k.args(clBuffer, step, cols, rows, clImageY, clImageUV);
|
||||
|
||||
size_t globalsize[] = { (size_t)cols, (size_t)rows };
|
||||
size_t globalsize[] = { (size_t)cols/2, (size_t)rows/2 };
|
||||
return k.run(2, globalsize, 0, false);
|
||||
}
|
||||
#endif // HAVE_VA_INTEL
|
||||
|
||||
@@ -120,6 +120,11 @@ TEST(OpenCL, support_SPIR_programs)
|
||||
cv::ocl::ProgramSource src = cv::ocl::ProgramSource::fromSPIR(module_name, "simple_spir", (uchar*)&program_binary_code[0], program_binary_code.size(), "");
|
||||
cv::String errmsg;
|
||||
cv::ocl::Program program(src, "", errmsg);
|
||||
if (program.ptr() == NULL && device.isAMD())
|
||||
{
|
||||
// https://community.amd.com/t5/opencl/spir-support-in-new-drivers-lost/td-p/170165
|
||||
throw cvtest::SkipTestException("Bypass AMD OpenCL runtime bug: 'cl_khr_spir' extension is declared, but it doesn't really work");
|
||||
}
|
||||
ASSERT_TRUE(program.ptr() != NULL);
|
||||
k.create("test_kernel", program);
|
||||
}
|
||||
|
||||
@@ -2456,4 +2456,16 @@ TEST(Core_MinMaxIdx, rows_overflow)
|
||||
}
|
||||
|
||||
|
||||
TEST(Core_Magnitude, regression_19506)
|
||||
{
|
||||
for (int N = 1; N <= 64; ++N)
|
||||
{
|
||||
Mat a(1, N, CV_32FC1, Scalar::all(1e-20));
|
||||
Mat res;
|
||||
magnitude(a, a, res);
|
||||
EXPECT_LE(cvtest::norm(res, NORM_L1), 1e-15) << N;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -1466,7 +1466,7 @@ template<typename R> struct TheTest
|
||||
R r1 = vx_load_expand((const cv::float16_t*)data.a.d);
|
||||
R r2(r1);
|
||||
EXPECT_EQ(1.0f, r1.get0());
|
||||
vx_store(data_f32.a.d, r2);
|
||||
v_store(data_f32.a.d, r2);
|
||||
EXPECT_EQ(-2.0f, data_f32.a.d[R::nlanes - 1]);
|
||||
|
||||
out.a.clear();
|
||||
|
||||
@@ -1551,4 +1551,14 @@ TEST(Core_MatExpr, empty_check_15760)
|
||||
EXPECT_THROW(Mat c = Mat().cross(Mat()), cv::Exception);
|
||||
}
|
||||
|
||||
TEST(Core_Arithm, scalar_handling_19599) // https://github.com/opencv/opencv/issues/19599 (OpenCV 4.x+ only)
|
||||
{
|
||||
Mat a(1, 1, CV_32F, Scalar::all(1));
|
||||
Mat b(4, 1, CV_64F, Scalar::all(1)); // MatExpr may convert Scalar to Mat
|
||||
Mat c;
|
||||
EXPECT_NO_THROW(cv::multiply(a, b, c));
|
||||
EXPECT_EQ(1, c.cols);
|
||||
EXPECT_EQ(1, c.rows);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -364,6 +364,7 @@ CV__DNN_INLINE_NS_BEGIN
|
||||
* Inner vector has slice ranges for the first number of input dimensions.
|
||||
*/
|
||||
std::vector<std::vector<Range> > sliceRanges;
|
||||
std::vector<std::vector<int> > sliceSteps;
|
||||
int axis;
|
||||
int num_split;
|
||||
|
||||
|
||||
@@ -100,6 +100,18 @@ CV__DNN_INLINE_NS_BEGIN
|
||||
CV_EXPORTS std::vector< std::pair<Backend, Target> > getAvailableBackends();
|
||||
CV_EXPORTS_W std::vector<Target> getAvailableTargets(dnn::Backend be);
|
||||
|
||||
/**
|
||||
* @brief Enables detailed logging of the DNN model loading with CV DNN API.
|
||||
* @param[in] isDiagnosticsMode Indicates whether diagnostic mode should be set.
|
||||
*
|
||||
* Diagnostic mode provides detailed logging of the model loading stage to explore
|
||||
* potential problems (ex.: not implemented layer type).
|
||||
*
|
||||
* @note In diagnostic mode series of assertions will be skipped, it can lead to the
|
||||
* expected application crash.
|
||||
*/
|
||||
CV_EXPORTS void enableModelDiagnostics(bool isDiagnosticsMode);
|
||||
|
||||
/** @brief This class provides all data needed to initialize layer.
|
||||
*
|
||||
* It includes dictionary with scalar params (which can be read by using Dict interface),
|
||||
|
||||
@@ -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.
|
||||
|
||||
#ifndef OPENCV_DNN_LAYER_REG_HPP
|
||||
#define OPENCV_DNN_LAYER_REG_HPP
|
||||
#include <opencv2/dnn.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
CV__DNN_INLINE_NS_BEGIN
|
||||
//! @addtogroup dnn
|
||||
//! @{
|
||||
|
||||
//! Register layer types of DNN model.
|
||||
typedef std::map<std::string, std::vector<LayerFactory::Constructor> > LayerFactory_Impl;
|
||||
LayerFactory_Impl& getLayerFactoryImpl();
|
||||
|
||||
//! @}
|
||||
CV__DNN_INLINE_NS_END
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -235,6 +235,24 @@ Range normalize_axis_range(const Range& r, int axisSize)
|
||||
return clamped;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool isAllOnes(const MatShape &inputShape, int startPos, int endPos)
|
||||
{
|
||||
CV_Assert(!inputShape.empty());
|
||||
|
||||
CV_CheckGE((int) inputShape.size(), startPos, "");
|
||||
CV_CheckGE(startPos, 0, "");
|
||||
CV_CheckLE(startPos, endPos, "");
|
||||
CV_CheckLE((size_t)endPos, inputShape.size(), "");
|
||||
|
||||
for (size_t i = startPos; i < endPos; i++)
|
||||
{
|
||||
if (inputShape[i] != 1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CV__DNN_INLINE_NS_END
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ CV_EXPORTS_W void resetMyriadDevice();
|
||||
#define CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_2 "Myriad2"
|
||||
/// Intel(R) Neural Compute Stick 2, NCS2 (USB 03e7:2485), MyriadX (https://software.intel.com/ru-ru/neural-compute-stick)
|
||||
#define CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X "MyriadX"
|
||||
#define CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE "ARM_COMPUTE"
|
||||
#define CV_DNN_INFERENCE_ENGINE_CPU_TYPE_X86 "X86"
|
||||
|
||||
|
||||
/** @brief Returns Inference Engine VPU type.
|
||||
@@ -57,6 +59,11 @@ CV_EXPORTS_W void resetMyriadDevice();
|
||||
*/
|
||||
CV_EXPORTS_W cv::String getInferenceEngineVPUType();
|
||||
|
||||
/** @brief Returns Inference Engine CPU type.
|
||||
*
|
||||
* Specify OpenVINO plugin: CPU or ARM.
|
||||
*/
|
||||
CV_EXPORTS_W cv::String getInferenceEngineCPUType();
|
||||
|
||||
/** @brief Release a HDDL plugin.
|
||||
*/
|
||||
|
||||
@@ -108,6 +108,10 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace csl { namespace de
|
||||
|
||||
template <class T> __device__ T clamp(T value, T lower, T upper) { return min(max(value, lower), upper); }
|
||||
|
||||
template <class T> __device__ long lround(T value);
|
||||
template <> inline __device__ long lround(double value) { return ::lround(value); }
|
||||
template <> inline __device__ long lround(float value) { return lroundf(value); }
|
||||
|
||||
template <class T> __device__ T round(T value);
|
||||
template <> inline __device__ double round(double value) { return ::round(value); }
|
||||
template <> inline __device__ float round(float value) { return roundf(value); }
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
template <class T, std::size_t CHANNELS_PER_ITER>
|
||||
__global__ void resize_nn(
|
||||
Span<T> output, size_type out_height, size_type out_width,
|
||||
View<T> input, size_type in_height, size_type in_width)
|
||||
View<T> input, size_type in_height, size_type in_width,
|
||||
float o2i_fy, float o2i_fx, bool round, bool half_pixel_centers)
|
||||
{
|
||||
auto in_image_size = in_height * in_width;
|
||||
auto out_image_size = out_height * out_width;
|
||||
@@ -60,12 +61,16 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
const index_type y = (iter % out_image_size) / out_width;
|
||||
const index_type x = iter % out_width;
|
||||
|
||||
/* o2i = output to input */
|
||||
auto o2i_fy = static_cast<float>(in_height) / out_height;
|
||||
auto o2i_fx = static_cast<float>(in_width) / out_width;
|
||||
auto in_yf = half_pixel_centers ? (y + 0.5f) * o2i_fy : y * o2i_fy;
|
||||
auto in_xf = half_pixel_centers ? (x + 0.5f) * o2i_fx : x * o2i_fx;
|
||||
|
||||
auto in_y = static_cast<index_type>(y * o2i_fy);
|
||||
auto in_x = static_cast<index_type>(x * o2i_fx);
|
||||
using device::lround;
|
||||
index_type in_y = round ? lround(in_yf) : static_cast<index_type>(in_yf);
|
||||
index_type in_x = round ? lround(in_xf) : static_cast<index_type>(in_xf);
|
||||
|
||||
using device::min;
|
||||
in_y = min(in_y, in_height - 1);
|
||||
in_x = min(in_x, in_width - 1);
|
||||
|
||||
index_type in_idx = c_start * in_image_size + in_y * in_width + in_x;
|
||||
index_type out_idx = c_start * out_image_size + y * out_width + x;
|
||||
@@ -83,7 +88,7 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
__global__ void resize_bilinear(
|
||||
Span<T> output, size_type out_height, size_type out_width,
|
||||
View<T> input, size_type in_height, size_type in_width,
|
||||
float o2i_fy, float o2i_fx)
|
||||
float o2i_fy, float o2i_fx, bool half_pixel_centers)
|
||||
{
|
||||
auto in_image_size = in_height * in_width;
|
||||
auto out_image_size = out_height * out_width;
|
||||
@@ -119,8 +124,9 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
const index_type y = (iter % out_image_size) / out_width;
|
||||
const index_type x = iter % out_width;
|
||||
|
||||
auto in_x = x * o2i_fx;
|
||||
auto in_y = y * o2i_fy;
|
||||
using device::max;
|
||||
auto in_x = half_pixel_centers ? max<float>((x + 0.5f) * o2i_fx - 0.5f, 0.0f) : x * o2i_fx;
|
||||
auto in_y = half_pixel_centers ? max<float>((y + 0.5f) * o2i_fy - 0.5f, 0.0f) : y * o2i_fy;
|
||||
|
||||
auto in_x0 = static_cast<index_type>(in_x);
|
||||
auto in_y0 = static_cast<index_type>(in_y);
|
||||
@@ -157,15 +163,16 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
template <class T, std::size_t CHANNELS_PER_ITER> static
|
||||
void launch_multichannel_resize_nn(const Stream& stream,
|
||||
Span<T> output, size_type out_height, size_type out_width,
|
||||
View<T> input, size_type in_height, size_type in_width)
|
||||
View<T> input, size_type in_height, size_type in_width,
|
||||
float scale_y, float scale_x, bool round, bool half_pixel_centers)
|
||||
{
|
||||
auto kernel = raw::resize_nn<T, CHANNELS_PER_ITER>;
|
||||
auto policy = make_policy(kernel, output.size() / CHANNELS_PER_ITER, 0, stream);
|
||||
launch_kernel(kernel, policy, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_kernel(kernel, policy, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void resize_nn(const Stream& stream, TensorSpan<T> output, TensorView<T> input) {
|
||||
void resize_nn(const Stream& stream, TensorSpan<T> output, TensorView<T> input, float scale_y, float scale_x, bool round, bool half_pixel_centers) {
|
||||
auto out_height = output.get_axis_size(-2);
|
||||
auto out_width = output.get_axis_size(-1);
|
||||
|
||||
@@ -176,38 +183,38 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
auto num_iters = num_effective_channels * out_height * out_width;
|
||||
|
||||
if (num_effective_channels % 32 == 0 && num_iters > 655360) {
|
||||
launch_multichannel_resize_nn<T, 32>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 32>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
} else if (num_effective_channels % 16 == 0 && num_iters > 327680) {
|
||||
launch_multichannel_resize_nn<T, 16>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 16>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
} else if (num_effective_channels % 8 == 0 && num_iters > 163840) {
|
||||
launch_multichannel_resize_nn<T, 8>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 8>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
} else if (num_effective_channels % 4 == 0 && num_iters > 81920) {
|
||||
launch_multichannel_resize_nn<T, 4>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 4>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
} else if (num_effective_channels % 2 == 0) {
|
||||
launch_multichannel_resize_nn<T, 2>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 2>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
} else {
|
||||
launch_multichannel_resize_nn<T, 1>(stream, output, out_height, out_width, input, in_height, in_width);
|
||||
launch_multichannel_resize_nn<T, 1>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, round, half_pixel_centers);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530)
|
||||
template void resize_nn<__half>(const Stream&, TensorSpan<__half>, TensorView<__half>);
|
||||
template void resize_nn<__half>(const Stream&, TensorSpan<__half>, TensorView<__half>, float, float, bool, bool);
|
||||
#endif
|
||||
template void resize_nn<float>(const Stream&, TensorSpan<float>, TensorView<float>);
|
||||
template void resize_nn<float>(const Stream&, TensorSpan<float>, TensorView<float>, float, float, bool,bool);
|
||||
|
||||
template <class T, std::size_t CHANNELS_PER_ITER> static
|
||||
void launch_multichannel_resize_bilinear(const Stream& stream,
|
||||
Span<T> output, size_type out_height, size_type out_width,
|
||||
View<T> input, size_type in_height, size_type in_width,
|
||||
float scale_y, float scale_x)
|
||||
float scale_y, float scale_x, bool half_pixel_centers)
|
||||
{
|
||||
auto kernel = raw::resize_bilinear<T, CHANNELS_PER_ITER>;
|
||||
auto policy = make_policy(kernel, output.size() / CHANNELS_PER_ITER, 0, stream);
|
||||
launch_kernel(kernel, policy, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_kernel(kernel, policy, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void resize_bilinear(const Stream& stream, TensorSpan<T> output, TensorView<T> input, float scale_y, float scale_x) {
|
||||
void resize_bilinear(const Stream& stream, TensorSpan<T> output, TensorView<T> input, float scale_y, float scale_x, bool half_pixel_centers) {
|
||||
auto out_height = output.get_axis_size(-2);
|
||||
auto out_width = output.get_axis_size(-1);
|
||||
|
||||
@@ -218,21 +225,21 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
auto num_iters = num_effective_channels * out_height * out_width;
|
||||
|
||||
if (num_effective_channels % 16 == 0 && num_iters > 163840) {
|
||||
launch_multichannel_resize_bilinear<T, 16>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_multichannel_resize_bilinear<T, 16>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
} else if (num_effective_channels % 8 == 0 && num_iters > 81920) {
|
||||
launch_multichannel_resize_bilinear<T, 8>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_multichannel_resize_bilinear<T, 8>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
} else if (num_effective_channels % 4 == 0 && num_iters > 40960) {
|
||||
launch_multichannel_resize_bilinear<T, 4>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_multichannel_resize_bilinear<T, 4>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
} else if (num_effective_channels % 2 == 0) {
|
||||
launch_multichannel_resize_bilinear<T, 2>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_multichannel_resize_bilinear<T, 2>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
} else {
|
||||
launch_multichannel_resize_bilinear<T, 1>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x);
|
||||
launch_multichannel_resize_bilinear<T, 1>(stream, output, out_height, out_width, input, in_height, in_width, scale_y, scale_x, half_pixel_centers);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530)
|
||||
template void resize_bilinear<__half>(const Stream&, TensorSpan<__half>, TensorView<__half>, float, float);
|
||||
template void resize_bilinear<__half>(const Stream&, TensorSpan<__half>, TensorView<__half>, float, float, bool);
|
||||
#endif
|
||||
template void resize_bilinear<float>(const Stream&, TensorSpan<float>, TensorView<float>, float, float);
|
||||
template void resize_bilinear<float>(const Stream&, TensorSpan<float>, TensorView<float>, float, float, bool);
|
||||
|
||||
}}}} /* namespace cv::dnn::cuda4dnn::kernels */
|
||||
|
||||
@@ -17,28 +17,18 @@ namespace cv { namespace dnn { namespace cuda4dnn {
|
||||
|
||||
void checkVersions()
|
||||
{
|
||||
int cudart_version = 0;
|
||||
CUDA4DNN_CHECK_CUDA(cudaRuntimeGetVersion(&cudart_version));
|
||||
if (cudart_version != CUDART_VERSION)
|
||||
// https://docs.nvidia.com/deeplearning/cudnn/developer-guide/index.html#programming-model
|
||||
// cuDNN API Compatibility
|
||||
// Beginning in cuDNN 7, the binary compatibility of a patch and minor releases is maintained as follows:
|
||||
// Any patch release x.y.z is forward or backward-compatible with applications built against another cuDNN patch release x.y.w (meaning, of the same major and minor version number, but having w!=z).
|
||||
// cuDNN minor releases beginning with cuDNN 7 are binary backward-compatible with applications built against the same or earlier patch release (meaning, an application built against cuDNN 7.x is binary compatible with cuDNN library 7.y, where y>=x).
|
||||
// Applications compiled with a cuDNN version 7.y are not guaranteed to work with 7.x release when y > x.
|
||||
auto cudnn_bversion = cudnnGetVersion();
|
||||
auto cudnn_major_bversion = cudnn_bversion / 1000, cudnn_minor_bversion = cudnn_bversion % 1000 / 100;
|
||||
if (cudnn_major_bversion != CUDNN_MAJOR || cudnn_minor_bversion < CUDNN_MINOR)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "CUDART reports version " << cudart_version << " which does not match with the version " << CUDART_VERSION << " with which OpenCV was built";
|
||||
CV_LOG_WARNING(NULL, oss.str().c_str());
|
||||
}
|
||||
|
||||
auto cudnn_version = cudnnGetVersion();
|
||||
if (cudnn_version != CUDNN_VERSION)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "cuDNN reports version " << cudnn_version << " which does not match with the version " << CUDNN_VERSION << " with which OpenCV was built";
|
||||
CV_LOG_WARNING(NULL, oss.str().c_str());
|
||||
}
|
||||
|
||||
auto cudnn_cudart_version = cudnnGetCudartVersion();
|
||||
if (cudart_version != cudnn_cudart_version)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "CUDART version " << cudnn_cudart_version << " reported by cuDNN " << cudnn_version << " does not match with the version reported by CUDART " << cudart_version;
|
||||
oss << "cuDNN reports version " << cudnn_major_bversion << "." << cudnn_minor_bversion << " which is not compatible with the version " << CUDNN_MAJOR << "." << CUDNN_MINOR << " with which OpenCV was built";
|
||||
CV_LOG_WARNING(NULL, oss.str().c_str());
|
||||
}
|
||||
}
|
||||
@@ -57,9 +47,6 @@ namespace cv { namespace dnn { namespace cuda4dnn {
|
||||
|
||||
bool isDeviceCompatible()
|
||||
{
|
||||
if (getDeviceCount() <= 0)
|
||||
return false;
|
||||
|
||||
int device_id = getDevice();
|
||||
if (device_id < 0)
|
||||
return false;
|
||||
@@ -80,9 +67,6 @@ namespace cv { namespace dnn { namespace cuda4dnn {
|
||||
|
||||
bool doesDeviceSupportFP16()
|
||||
{
|
||||
if (getDeviceCount() <= 0)
|
||||
return false;
|
||||
|
||||
int device_id = getDevice();
|
||||
if (device_id < 0)
|
||||
return false;
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels {
|
||||
|
||||
template <class T>
|
||||
void resize_nn(const csl::Stream& stream, csl::TensorSpan<T> output, csl::TensorView<T> input);
|
||||
void resize_nn(const csl::Stream& stream, csl::TensorSpan<T> output, csl::TensorView<T> input, float scale_y, float scale_x, bool round, bool half_pixel_centers);
|
||||
|
||||
template <class T>
|
||||
void resize_bilinear(const csl::Stream& stream, csl::TensorSpan<T> output, csl::TensorView<T> input, float scale_y, float scale_x);
|
||||
void resize_bilinear(const csl::Stream& stream, csl::TensorSpan<T> output, csl::TensorView<T> input, float scale_y, float scale_x, bool half_pixel_centers);
|
||||
|
||||
}}}} /* namespace cv::dnn::cuda4dnn::kernels */
|
||||
|
||||
|
||||
@@ -20,14 +20,23 @@ namespace cv { namespace dnn { namespace cuda4dnn {
|
||||
BILINEAR
|
||||
};
|
||||
|
||||
struct ResizeConfiguration {
|
||||
InterpolationType type;
|
||||
bool align_corners;
|
||||
bool half_pixel_centers;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ResizeOp final : public CUDABackendNode {
|
||||
public:
|
||||
using wrapper_type = GetCUDABackendWrapperType<T>;
|
||||
|
||||
ResizeOp(csl::Stream stream_, InterpolationType type_, float scaleHeight_, float scaleWidth_)
|
||||
: stream(std::move(stream_)), type{ type_ }, scaleHeight{ scaleHeight_ }, scaleWidth{ scaleWidth_ }
|
||||
ResizeOp(csl::Stream stream_, const ResizeConfiguration& config)
|
||||
: stream(std::move(stream_))
|
||||
{
|
||||
type = config.type;
|
||||
align_corners = config.align_corners;
|
||||
half_pixel_centers = config.half_pixel_centers;
|
||||
}
|
||||
|
||||
void forward(
|
||||
@@ -44,16 +53,27 @@ namespace cv { namespace dnn { namespace cuda4dnn {
|
||||
auto output_wrapper = outputs[0].dynamicCast<wrapper_type>();
|
||||
auto output = output_wrapper->getSpan();
|
||||
|
||||
const auto compute_scale = [this](std::size_t input_size, std::size_t output_size) {
|
||||
return (align_corners && output_size > 1) ?
|
||||
static_cast<float>(input_size - 1) / (output_size - 1) :
|
||||
static_cast<float>(input_size) / output_size;
|
||||
};
|
||||
|
||||
auto out_height = output.get_axis_size(-2), out_width = output.get_axis_size(-1);
|
||||
auto in_height = input.get_axis_size(-2), in_width = input.get_axis_size(-1);
|
||||
float scale_height = compute_scale(in_height, out_height),
|
||||
scale_width = compute_scale(in_width, out_width);
|
||||
|
||||
if (type == InterpolationType::NEAREST_NEIGHBOUR)
|
||||
kernels::resize_nn<T>(stream, output, input);
|
||||
kernels::resize_nn<T>(stream, output, input, scale_height, scale_width, align_corners, half_pixel_centers);
|
||||
else if (type == InterpolationType::BILINEAR)
|
||||
kernels::resize_bilinear<T>(stream, output, input, scaleHeight, scaleWidth);
|
||||
kernels::resize_bilinear<T>(stream, output, input, scale_height, scale_width, half_pixel_centers);
|
||||
}
|
||||
|
||||
private:
|
||||
csl::Stream stream;
|
||||
InterpolationType type;
|
||||
float scaleHeight, scaleWidth; /* for bilinear interpolation */
|
||||
bool align_corners, half_pixel_centers;
|
||||
};
|
||||
|
||||
}}} /* namespace cv::dnn::cuda4dnn */
|
||||
|
||||
@@ -558,6 +558,29 @@ namespace cv {
|
||||
fused_layer_names.push_back(last_layer);
|
||||
}
|
||||
|
||||
void setSAM(int from)
|
||||
{
|
||||
cv::dnn::LayerParams eltwise_param;
|
||||
eltwise_param.name = "SAM-name";
|
||||
eltwise_param.type = "Eltwise";
|
||||
|
||||
eltwise_param.set<std::string>("operation", "prod");
|
||||
eltwise_param.set<std::string>("output_channels_mode", "same");
|
||||
|
||||
darknet::LayerParameter lp;
|
||||
std::string layer_name = cv::format("sam_%d", layer_id);
|
||||
lp.layer_name = layer_name;
|
||||
lp.layer_type = eltwise_param.type;
|
||||
lp.layerParams = eltwise_param;
|
||||
lp.bottom_indexes.push_back(last_layer);
|
||||
lp.bottom_indexes.push_back(fused_layer_names.at(from));
|
||||
last_layer = layer_name;
|
||||
net->layers.push_back(lp);
|
||||
|
||||
layer_id++;
|
||||
fused_layer_names.push_back(last_layer);
|
||||
}
|
||||
|
||||
void setUpsample(int scaleFactor)
|
||||
{
|
||||
cv::dnn::LayerParams param;
|
||||
@@ -837,6 +860,14 @@ namespace cv {
|
||||
from = from < 0 ? from + layers_counter : from;
|
||||
setParams.setScaleChannels(from);
|
||||
}
|
||||
else if (layer_type == "sam")
|
||||
{
|
||||
std::string bottom_layer = getParam<std::string>(layer_params, "from", "");
|
||||
CV_Assert(!bottom_layer.empty());
|
||||
int from = std::atoi(bottom_layer.c_str());
|
||||
from = from < 0 ? from + layers_counter : from;
|
||||
setParams.setSAM(from);
|
||||
}
|
||||
else if (layer_type == "upsample")
|
||||
{
|
||||
int scaleFactor = getParam<int>(layer_params, "stride", 1);
|
||||
|
||||
+23
-11
@@ -63,6 +63,7 @@
|
||||
#include <memory>
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/dnn/layer_reg.private.hpp>
|
||||
|
||||
#include <opencv2/core/utils/configuration.private.hpp>
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
@@ -93,6 +94,13 @@ static bool DNN_CHECK_NAN_INF = utils::getConfigurationParameterBool("OPENCV_DNN
|
||||
static bool DNN_CHECK_NAN_INF_DUMP = utils::getConfigurationParameterBool("OPENCV_DNN_CHECK_NAN_INF_DUMP", false);
|
||||
static bool DNN_CHECK_NAN_INF_RAISE_ERROR = utils::getConfigurationParameterBool("OPENCV_DNN_CHECK_NAN_INF_RAISE_ERROR", false);
|
||||
|
||||
bool DNN_DIAGNOSTICS_RUN = false;
|
||||
|
||||
void enableModelDiagnostics(bool isDiagnosticsMode)
|
||||
{
|
||||
DNN_DIAGNOSTICS_RUN = isDiagnosticsMode;
|
||||
}
|
||||
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::make_pair;
|
||||
@@ -239,11 +247,10 @@ private:
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CUDA
|
||||
if (haveCUDA() && cuda4dnn::isDeviceCompatible())
|
||||
if (haveCUDA())
|
||||
{
|
||||
backends.push_back(std::make_pair(DNN_BACKEND_CUDA, DNN_TARGET_CUDA));
|
||||
if (cuda4dnn::doesDeviceSupportFP16())
|
||||
backends.push_back(std::make_pair(DNN_BACKEND_CUDA, DNN_TARGET_CUDA_FP16));
|
||||
backends.push_back(std::make_pair(DNN_BACKEND_CUDA, DNN_TARGET_CUDA_FP16));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1383,11 +1390,12 @@ struct Net::Impl : public detail::NetImplBase
|
||||
CV_Assert(preferableBackend != DNN_BACKEND_HALIDE ||
|
||||
preferableTarget == DNN_TARGET_CPU ||
|
||||
preferableTarget == DNN_TARGET_OPENCL);
|
||||
#ifdef HAVE_INF_ENGINE
|
||||
if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 ||
|
||||
preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
CV_Assert(
|
||||
preferableTarget == DNN_TARGET_CPU ||
|
||||
(preferableTarget == DNN_TARGET_CPU && (!isArmComputePlugin() || preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)) ||
|
||||
preferableTarget == DNN_TARGET_OPENCL ||
|
||||
preferableTarget == DNN_TARGET_OPENCL_FP16 ||
|
||||
preferableTarget == DNN_TARGET_MYRIAD ||
|
||||
@@ -1395,6 +1403,7 @@ struct Net::Impl : public detail::NetImplBase
|
||||
preferableTarget == DNN_TARGET_FPGA
|
||||
);
|
||||
}
|
||||
#endif
|
||||
CV_Assert(preferableBackend != DNN_BACKEND_VKCOM ||
|
||||
preferableTarget == DNN_TARGET_VULKAN);
|
||||
CV_Assert(preferableBackend != DNN_BACKEND_CUDA ||
|
||||
@@ -2099,8 +2108,8 @@ struct Net::Impl : public detail::NetImplBase
|
||||
return;
|
||||
}
|
||||
|
||||
bool supportsCPUFallback = preferableTarget == DNN_TARGET_CPU ||
|
||||
BackendRegistry::checkIETarget(DNN_TARGET_CPU);
|
||||
bool supportsCPUFallback = !isArmComputePlugin() && (preferableTarget == DNN_TARGET_CPU ||
|
||||
BackendRegistry::checkIETarget(DNN_TARGET_CPU));
|
||||
|
||||
// Build Inference Engine networks from sets of layers that support this
|
||||
// backend. Split a whole model on several Inference Engine networks if
|
||||
@@ -2363,6 +2372,9 @@ struct Net::Impl : public detail::NetImplBase
|
||||
CV_Assert(preferableBackend == DNN_BACKEND_CUDA);
|
||||
|
||||
#ifdef HAVE_CUDA
|
||||
if (!cudaInfo) /* we need to check only once */
|
||||
cuda4dnn::checkVersions();
|
||||
|
||||
if (cuda4dnn::getDeviceCount() <= 0)
|
||||
CV_Error(Error::StsError, "No CUDA capable device found.");
|
||||
|
||||
@@ -2373,7 +2385,10 @@ struct Net::Impl : public detail::NetImplBase
|
||||
CV_Error(Error::GpuNotSupported, "OpenCV was not built to work with the selected device. Please check CUDA_ARCH_PTX or CUDA_ARCH_BIN in your build configuration.");
|
||||
|
||||
if (preferableTarget == DNN_TARGET_CUDA_FP16 && !cuda4dnn::doesDeviceSupportFP16())
|
||||
CV_Error(Error::StsError, "The selected CUDA device does not support FP16 operations.");
|
||||
{
|
||||
CV_LOG_WARNING(NULL, "The selected CUDA device does not support FP16 target; switching to FP32 target.");
|
||||
preferableTarget = DNN_TARGET_CUDA;
|
||||
}
|
||||
|
||||
if (!cudaInfo)
|
||||
{
|
||||
@@ -2384,7 +2399,6 @@ struct Net::Impl : public detail::NetImplBase
|
||||
|
||||
auto d2h_stream = cuda4dnn::csl::Stream(true); // stream for background D2H data transfers
|
||||
cudaInfo = std::unique_ptr<CudaInfo_t>(new CudaInfo_t(std::move(context), std::move(d2h_stream)));
|
||||
cuda4dnn::checkVersions();
|
||||
}
|
||||
|
||||
cudaInfo->workspace = cuda4dnn::csl::Workspace(); // release workspace memory if any
|
||||
@@ -5304,15 +5318,13 @@ static Mutex& getLayerFactoryMutex()
|
||||
return *instance;
|
||||
}
|
||||
|
||||
typedef std::map<String, std::vector<LayerFactory::Constructor> > LayerFactory_Impl;
|
||||
|
||||
static LayerFactory_Impl& getLayerFactoryImpl_()
|
||||
{
|
||||
static LayerFactory_Impl impl;
|
||||
return impl;
|
||||
}
|
||||
|
||||
static LayerFactory_Impl& getLayerFactoryImpl()
|
||||
LayerFactory_Impl& getLayerFactoryImpl()
|
||||
{
|
||||
static LayerFactory_Impl* volatile instance = NULL;
|
||||
if (instance == NULL)
|
||||
|
||||
@@ -772,8 +772,14 @@ static InferenceEngine::Layout estimateLayout(const Mat& m)
|
||||
{
|
||||
if (m.dims == 4)
|
||||
return InferenceEngine::Layout::NCHW;
|
||||
else if (m.dims == 3)
|
||||
return InferenceEngine::Layout::CHW;
|
||||
else if (m.dims == 2)
|
||||
return InferenceEngine::Layout::NC;
|
||||
else if (m.dims == 1)
|
||||
return InferenceEngine::Layout::C;
|
||||
else if (m.dims == 5)
|
||||
return InferenceEngine::Layout::NCDHW;
|
||||
else
|
||||
return InferenceEngine::Layout::ANY;
|
||||
}
|
||||
|
||||
@@ -324,10 +324,13 @@ public:
|
||||
#ifdef HAVE_INF_ENGINE
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (ksize == 1)
|
||||
bool isArmTarget = preferableTarget == DNN_TARGET_CPU && isArmComputePlugin();
|
||||
if (isArmTarget && blobs.empty())
|
||||
return false;
|
||||
if (ksize == 1)
|
||||
return isArmTarget;
|
||||
if (ksize == 3)
|
||||
return preferableTarget == DNN_TARGET_CPU;
|
||||
return preferableTarget != DNN_TARGET_MYRIAD && !isArmTarget;
|
||||
bool isMyriad = preferableTarget == DNN_TARGET_MYRIAD || preferableTarget == DNN_TARGET_HDDL;
|
||||
if ((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || !isMyriad) && blobs.empty())
|
||||
return false;
|
||||
@@ -805,7 +808,7 @@ public:
|
||||
CV_Assert_N(inputs.size() >= 1, nodes.size() >= 1);
|
||||
auto& ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
|
||||
std::vector<size_t> dims = ieInpNode->get_shape();
|
||||
CV_Assert(dims.size() == 4 || dims.size() == 5);
|
||||
CV_Check(dims.size(), dims.size() >= 3 && dims.size() <= 5, "");
|
||||
std::shared_ptr<ngraph::Node> ieWeights = nodes.size() > 1 ? nodes[1].dynamicCast<InfEngineNgraphNode>()->node : nullptr;
|
||||
if (nodes.size() > 1)
|
||||
CV_Assert(ieWeights); // dynamic_cast should not fail
|
||||
@@ -843,7 +846,7 @@ public:
|
||||
else
|
||||
{
|
||||
auto shape = std::make_shared<ngraph::op::Constant>(ngraph::element::i64,
|
||||
ngraph::Shape{kernel_shape.size()}, kernel_shape.data());
|
||||
ngraph::Shape{kernel_shape.size()}, std::vector<int64_t>(kernel_shape.begin(), kernel_shape.end()));
|
||||
ieWeights = std::make_shared<ngraph::op::v1::Reshape>(ieWeights, shape, true);
|
||||
}
|
||||
|
||||
@@ -878,7 +881,7 @@ public:
|
||||
if (nodes.size() == 3)
|
||||
{
|
||||
auto bias_shape = std::make_shared<ngraph::op::Constant>(ngraph::element::i64,
|
||||
ngraph::Shape{shape.size()}, shape.data());
|
||||
ngraph::Shape{shape.size()}, std::vector<int64_t>(shape.begin(), shape.end()));
|
||||
bias = std::make_shared<ngraph::op::v1::Reshape>(nodes[2].dynamicCast<InfEngineNgraphNode>()->node, bias_shape, true);
|
||||
}
|
||||
else
|
||||
@@ -1247,7 +1250,7 @@ public:
|
||||
v20*vw20 + v21*vw21 + v22*vw22 + vbias;
|
||||
if (relu)
|
||||
vout = v_select(vout > z, vout, vout*vrc);
|
||||
vx_store(outptr + out_j, vout);
|
||||
v_store(outptr + out_j, vout);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -138,6 +138,12 @@ public:
|
||||
|
||||
typedef std::map<int, std::vector<util::NormalizedBBox> > LabelBBox;
|
||||
|
||||
inline int getNumOfTargetClasses() {
|
||||
unsigned numBackground =
|
||||
(_backgroundLabelId >= 0 && _backgroundLabelId < _numClasses) ? 1 : 0;
|
||||
return (_numClasses - numBackground);
|
||||
}
|
||||
|
||||
bool getParameterDict(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
DictValue& result)
|
||||
@@ -590,12 +596,13 @@ public:
|
||||
LabelBBox::const_iterator label_bboxes = decodeBBoxes.find(label);
|
||||
if (label_bboxes == decodeBBoxes.end())
|
||||
CV_Error_(cv::Error::StsError, ("Could not find location predictions for label %d", label));
|
||||
int limit = (getNumOfTargetClasses() == 1) ? _keepTopK : std::numeric_limits<int>::max();
|
||||
if (_bboxesNormalized)
|
||||
NMSFast_(label_bboxes->second, scores, _confidenceThreshold, _nmsThreshold, 1.0, _topK,
|
||||
indices[c], util::caffe_norm_box_overlap);
|
||||
indices[c], util::caffe_norm_box_overlap, limit);
|
||||
else
|
||||
NMSFast_(label_bboxes->second, scores, _confidenceThreshold, _nmsThreshold, 1.0, _topK,
|
||||
indices[c], util::caffe_box_overlap);
|
||||
indices[c], util::caffe_box_overlap, limit);
|
||||
numDetections += indices[c].size();
|
||||
}
|
||||
if (_keepTopK > -1 && numDetections > (size_t)_keepTopK)
|
||||
|
||||
@@ -1354,11 +1354,15 @@ struct PowerFunctor : public BaseFunctor
|
||||
ngraph::Shape{1}, &scale);
|
||||
auto shift_node = std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
|
||||
ngraph::Shape{1}, &shift);
|
||||
auto power_node = std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
|
||||
ngraph::Shape{1}, &power);
|
||||
|
||||
auto mul = std::make_shared<ngraph::op::v1::Multiply>(scale_node, node, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
auto scale_shift = std::make_shared<ngraph::op::v1::Add>(mul, shift_node, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
|
||||
if (power == 1)
|
||||
return scale_shift;
|
||||
|
||||
auto power_node = std::make_shared<ngraph::op::Constant>(ngraph::element::f32,
|
||||
ngraph::Shape{1}, &power);
|
||||
return std::make_shared<ngraph::op::v1::Power>(scale_shift, power_node, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "../op_halide.hpp"
|
||||
#include "../op_inf_engine.hpp"
|
||||
#include "../ie_ngraph.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
#include "opencl_kernels_dnn.hpp"
|
||||
@@ -97,6 +98,7 @@ public:
|
||||
: outputChannels(0)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
hasVecInput = false;
|
||||
op = SUM;
|
||||
if (params.has("operation"))
|
||||
{
|
||||
@@ -156,6 +158,9 @@ public:
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
{
|
||||
if (hasVecInput && ELTWISE_CHANNNELS_SAME)
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
|
||||
if (backendId == DNN_BACKEND_CUDA)
|
||||
{
|
||||
if(channelsModeInput == ELTWISE_CHANNNELS_INPUT_0 || channelsModeInput == ELTWISE_CHANNNELS_INPUT_0_TRUNCATE)
|
||||
@@ -211,9 +216,6 @@ public:
|
||||
{
|
||||
CV_Assert(0 && "Internal error");
|
||||
}
|
||||
|
||||
for (size_t j = 2; j < dims; j++)
|
||||
CV_Assert(inputs[0][j] == inputs[i][j]);
|
||||
}
|
||||
|
||||
channelsMode = variableChannels ? channelsModeInput : ELTWISE_CHANNNELS_SAME;
|
||||
@@ -221,9 +223,56 @@ public:
|
||||
|
||||
outputs.assign(1, inputs[0]);
|
||||
outputs[0][1] = numChannels;
|
||||
|
||||
if (dims > 2)
|
||||
{
|
||||
size_t vecIdx = 0;
|
||||
bool isVecFound = false;
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
bool allOnes = isAllOnes(inputs[i], 2, dims);
|
||||
if (!allOnes && !isVecFound)
|
||||
{
|
||||
vecIdx = i;
|
||||
isVecFound = true;
|
||||
}
|
||||
|
||||
if (!allOnes && i != vecIdx)
|
||||
{
|
||||
for (size_t j = 2; j < dims; j++)
|
||||
{
|
||||
CV_Assert(inputs[vecIdx][j] == inputs[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channelsModeInput == ELTWISE_CHANNNELS_SAME && isVecFound)
|
||||
{
|
||||
for (size_t j = 2; j < dims; j++)
|
||||
{
|
||||
outputs[0][j] = inputs[vecIdx][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays) CV_OVERRIDE
|
||||
{
|
||||
std::vector<Mat> inputs;
|
||||
inputs_arr.getMatVector(inputs);
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
MatShape inpShape = shape(inputs[i].size);
|
||||
if (isAllOnes(inpShape, 2, inputs[i].dims))
|
||||
{
|
||||
hasVecInput = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EltwiseInvoker : public ParallelLoopBody
|
||||
{
|
||||
@@ -516,6 +565,9 @@ public:
|
||||
if ((inputs_.depth() == CV_16S && op != SUM) || (channelsMode != ELTWISE_CHANNNELS_SAME))
|
||||
return false;
|
||||
|
||||
if (hasVecInput)
|
||||
return false; // TODO not implemented yet: https://github.com/opencv/opencv/pull/19477
|
||||
|
||||
inputs_.getUMatVector(inputs);
|
||||
outputs_.getUMatVector(outputs);
|
||||
|
||||
@@ -616,6 +668,47 @@ public:
|
||||
|
||||
CV_Assert(outputs.size() == 1);
|
||||
const int nstripes = getNumThreads();
|
||||
|
||||
if (channelsModeInput == ELTWISE_CHANNNELS_SAME && inputs[0].dims > 2)
|
||||
{
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
MatShape inpShape = shape(inputs[i].size);
|
||||
bool allOnes = isAllOnes(inpShape, 2, inputs[i].dims);
|
||||
|
||||
if (allOnes)
|
||||
{
|
||||
Mat tmpInput = inputs[i];
|
||||
MatShape outShape = shape(outputs[0].size);
|
||||
size_t xSize = outShape[2];
|
||||
for (size_t j = 3; j < outShape.size(); j++)
|
||||
xSize *= outShape[j];
|
||||
|
||||
int dimVec[3] = {outShape[0], outShape[1], (int) xSize};
|
||||
std::vector<int> matSizesVec(&dimVec[0], &dimVec[0] + 3);
|
||||
inputs[i] = Mat(matSizesVec, tmpInput.type());
|
||||
|
||||
std::vector<int> idx(outShape.size(), 0);
|
||||
std::vector<int> outIdx(inpShape.size(), 0);
|
||||
|
||||
for (size_t j = 0; j < outShape[0]; j++)
|
||||
{
|
||||
outIdx[0] = idx[0] = j;
|
||||
for(size_t k = 0; k < outShape[1]; k++)
|
||||
{
|
||||
outIdx[1] = idx[1] = k;
|
||||
for (size_t x = 0; x < xSize; x++)
|
||||
{
|
||||
outIdx[2] = x;
|
||||
inputs[i].at<float>(outIdx.data()) = tmpInput.at<float>(idx.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
inputs[i] = inputs[i].reshape(0, outShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EltwiseInvoker::run(*this,
|
||||
&inputs[0], (int)inputs.size(), outputs[0],
|
||||
nstripes);
|
||||
@@ -795,6 +888,9 @@ public:
|
||||
}
|
||||
|
||||
Ptr<ActivationLayer> activ;
|
||||
|
||||
private:
|
||||
bool hasVecInput;
|
||||
};
|
||||
|
||||
Ptr<EltwiseLayer> EltwiseLayer::create(const LayerParams& params)
|
||||
|
||||
@@ -403,7 +403,15 @@ public:
|
||||
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
||||
{
|
||||
auto& ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
|
||||
#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2021_2)
|
||||
auto mvn = std::make_shared<ngraph::op::MVN>(ieInpNode, acrossChannels, normVariance, eps);
|
||||
#else
|
||||
int64_t start_axis = acrossChannels ? 1 : 2;
|
||||
std::vector<int64_t> axes_v(ieInpNode->get_shape().size() - start_axis);
|
||||
std::iota(axes_v.begin(), axes_v.end(), start_axis);
|
||||
auto axes = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{axes_v.size()}, axes_v.data());
|
||||
auto mvn = std::make_shared<ngraph::op::v6::MVN>(ieInpNode, axes, normVariance, eps, ngraph::op::MVNEpsMode::INSIDE_SQRT);
|
||||
#endif
|
||||
return Ptr<BackendNode>(new InfEngineNgraphNode(mvn));
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
|
||||
@@ -334,8 +334,8 @@ public:
|
||||
if (!acrossSpatial) {
|
||||
axes_data.push_back(1);
|
||||
} else {
|
||||
axes_data.resize(ieInpNode->get_shape().size());
|
||||
std::iota(axes_data.begin(), axes_data.end(), 0);
|
||||
axes_data.resize(ieInpNode->get_shape().size() - 1);
|
||||
std::iota(axes_data.begin(), axes_data.end(), 1);
|
||||
}
|
||||
auto axes = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{axes_data.size()}, axes_data);
|
||||
auto norm = std::make_shared<ngraph::op::NormalizeL2>(ieInpNode, axes, epsilon, ngraph::op::EpsMode::ADD);
|
||||
@@ -344,23 +344,18 @@ public:
|
||||
std::vector<size_t> shape(ieInpNode->get_shape().size(), 1);
|
||||
shape[0] = blobs.empty() ? 1 : batch;
|
||||
shape[1] = numChannels;
|
||||
std::shared_ptr<ngraph::op::Constant> weight;
|
||||
if (blobs.empty())
|
||||
if (!blobs.empty())
|
||||
{
|
||||
std::vector<float> ones(numChannels, 1);
|
||||
weight = std::make_shared<ngraph::op::Constant>(ngraph::element::f32, ngraph::Shape(shape), ones.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
weight = std::make_shared<ngraph::op::Constant>(
|
||||
auto weight = std::make_shared<ngraph::op::Constant>(
|
||||
ngraph::element::f32, ngraph::Shape(shape), blobs[0].data);
|
||||
}
|
||||
#if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2021_2)
|
||||
auto mul = std::make_shared<ngraph::op::v1::Multiply>(norm, weight, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
auto mul = std::make_shared<ngraph::op::v1::Multiply>(norm, weight, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
#else
|
||||
auto mul = std::make_shared<ngraph::op::v0::Multiply>(norm, weight, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
auto mul = std::make_shared<ngraph::op::v0::Multiply>(norm, weight, ngraph::op::AutoBroadcastType::NUMPY);
|
||||
#endif
|
||||
return Ptr<BackendNode>(new InfEngineNgraphNode(mul));
|
||||
return Ptr<BackendNode>(new InfEngineNgraphNode(mul));
|
||||
}
|
||||
return Ptr<BackendNode>(new InfEngineNgraphNode(norm));
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
|
||||
|
||||
@@ -105,9 +105,10 @@ public:
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
bool isMyriad = preferableTarget == DNN_TARGET_MYRIAD || preferableTarget == DNN_TARGET_HDDL;
|
||||
return INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) &&
|
||||
(!isMyriad ||
|
||||
(dstRanges.size() == 4 && paddings[0].first == 0 && paddings[0].second == 0));
|
||||
if (INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) && isMyriad)
|
||||
return dstRanges.size() == 4 && paddings[0].first == 0 && paddings[0].second == 0;
|
||||
|
||||
return (dstRanges.size() <= 4 || !isArmComputePlugin());
|
||||
}
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
|
||||
@@ -113,6 +113,10 @@ public:
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
{
|
||||
#ifdef HAVE_INF_ENGINE
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && preferableTarget == DNN_TARGET_CPU)
|
||||
return _order.size() <= 4 || !isArmComputePlugin();
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
backendId == DNN_BACKEND_CUDA ||
|
||||
((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && haveInfEngine()) ||
|
||||
|
||||
@@ -220,7 +220,9 @@ public:
|
||||
#endif
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
return !computeMaxIdx && type != STOCHASTIC && kernel_size.size() > 1;
|
||||
#ifdef HAVE_DNN_NGRAPH
|
||||
return !computeMaxIdx && type != STOCHASTIC && kernel_size.size() > 1 && (kernel_size.size() != 3 || !isArmComputePlugin());
|
||||
#endif
|
||||
}
|
||||
else if (backendId == DNN_BACKEND_OPENCV)
|
||||
{
|
||||
|
||||
@@ -460,8 +460,10 @@ public:
|
||||
std::vector<int64_t> mask(anchors, 1);
|
||||
region = std::make_shared<ngraph::op::RegionYolo>(tr_input, coords, classes, anchors, useSoftmax, mask, 1, 3, anchors_vec);
|
||||
|
||||
auto tr_shape = tr_input->get_shape();
|
||||
auto shape_as_inp = std::make_shared<ngraph::op::Constant>(ngraph::element::i64,
|
||||
ngraph::Shape{tr_input->get_shape().size()}, tr_input->get_shape().data());
|
||||
ngraph::Shape{tr_shape.size()},
|
||||
std::vector<int64_t>(tr_shape.begin(), tr_shape.end()));
|
||||
|
||||
region = std::make_shared<ngraph::op::v1::Reshape>(region, shape_as_inp, true);
|
||||
new_axes = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{4}, std::vector<int64_t>{0, 2, 3, 1});
|
||||
@@ -607,7 +609,7 @@ public:
|
||||
result = std::make_shared<ngraph::op::Transpose>(result, tr_axes);
|
||||
if (b > 1)
|
||||
{
|
||||
std::vector<size_t> sizes = {(size_t)b, result->get_shape()[0] / b, result->get_shape()[1]};
|
||||
std::vector<int64_t> sizes{b, static_cast<int64_t>(result->get_shape()[0]) / b, static_cast<int64_t>(result->get_shape()[1])};
|
||||
auto shape_node = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{sizes.size()}, sizes.data());
|
||||
result = std::make_shared<ngraph::op::v1::Reshape>(result, shape_node, true);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
{
|
||||
if (backendId == DNN_BACKEND_CUDA)
|
||||
return interpolation == "nearest" || interpolation == "bilinear";
|
||||
return interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear";
|
||||
|
||||
#ifdef HAVE_INF_ENGINE
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
@@ -267,6 +267,7 @@ public:
|
||||
{
|
||||
auto& ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
|
||||
|
||||
#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2021_2)
|
||||
ngraph::op::InterpolateAttrs attrs;
|
||||
attrs.pads_begin.push_back(0);
|
||||
attrs.pads_end.push_back(0);
|
||||
@@ -285,6 +286,37 @@ public:
|
||||
std::vector<int64_t> shape = {outHeight, outWidth};
|
||||
auto out_shape = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{2}, shape.data());
|
||||
auto interp = std::make_shared<ngraph::op::Interpolate>(ieInpNode, out_shape, attrs);
|
||||
#else
|
||||
ngraph::op::v4::Interpolate::InterpolateAttrs attrs;
|
||||
|
||||
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;
|
||||
|
||||
std::vector<int64_t> shape = {outHeight, outWidth};
|
||||
auto out_shape = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{2}, shape.data());
|
||||
|
||||
auto& input_shape = ieInpNode->get_shape();
|
||||
CV_Assert_N(input_shape[2] != 0, input_shape[3] != 0);
|
||||
std::vector<float> scales = {static_cast<float>(outHeight) / input_shape[2], static_cast<float>(outWidth) / input_shape[3]};
|
||||
auto scales_shape = std::make_shared<ngraph::op::Constant>(ngraph::element::f32, ngraph::Shape{2}, scales.data());
|
||||
|
||||
auto axes = std::make_shared<ngraph::op::Constant>(ngraph::element::i64, ngraph::Shape{2}, std::vector<int64_t>{2, 3});
|
||||
auto interp = std::make_shared<ngraph::op::v4::Interpolate>(ieInpNode, out_shape, scales_shape, axes, attrs);
|
||||
#endif
|
||||
return Ptr<BackendNode>(new InfEngineNgraphNode(interp));
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
@@ -299,15 +331,28 @@ public:
|
||||
{
|
||||
auto context = reinterpret_cast<csl::CSLContext*>(context_);
|
||||
|
||||
cuda4dnn::InterpolationType itype;
|
||||
cuda4dnn::ResizeConfiguration config;
|
||||
if (interpolation == "nearest")
|
||||
itype = InterpolationType::NEAREST_NEIGHBOUR;
|
||||
{
|
||||
config.type = InterpolationType::NEAREST_NEIGHBOUR;
|
||||
config.align_corners = alignCorners;
|
||||
config.half_pixel_centers = halfPixelCenters;
|
||||
}
|
||||
else if (interpolation == "bilinear")
|
||||
itype = InterpolationType::BILINEAR;
|
||||
{
|
||||
config.type = InterpolationType::BILINEAR;
|
||||
config.align_corners = alignCorners;
|
||||
config.half_pixel_centers = halfPixelCenters;
|
||||
}
|
||||
else if (interpolation == "opencv_linear")
|
||||
{
|
||||
config.type = InterpolationType::BILINEAR;
|
||||
config.align_corners = false;
|
||||
config.half_pixel_centers = true;
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsNotImplemented, "Requested interpolation mode is not available in resize layer.");
|
||||
|
||||
return make_cuda_node<cuda4dnn::ResizeOp>(preferableTarget, std::move(context->stream), itype, scaleHeight, scaleWidth);
|
||||
return make_cuda_node<cuda4dnn::ResizeOp>(preferableTarget, std::move(context->stream), config);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
SliceLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
hasSteps = false;
|
||||
axis = params.get<int>("axis", 1);
|
||||
num_split = params.get<int>("num_split", 0);
|
||||
hasDynamicShapes = params.get<bool>("has_dynamic_shapes", false);
|
||||
@@ -79,7 +80,7 @@ public:
|
||||
CV_Assert(!params.has("begin") && !params.has("size") && !params.has("end"));
|
||||
const DictValue &indicesValue = params.get("slice_point");
|
||||
sliceRanges.resize(indicesValue.size() + 1,
|
||||
std::vector<Range>(axis + 1, Range::all()));
|
||||
std::vector<Range>(std::max(axis,0) + 1, Range::all()));
|
||||
int prevSlice = 0;
|
||||
for (int i = 0; i < indicesValue.size(); ++i)
|
||||
{
|
||||
@@ -118,6 +119,22 @@ public:
|
||||
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<int>(i);
|
||||
CV_Assert(step >= 1);
|
||||
if (step > 1)
|
||||
hasSteps = true;
|
||||
sliceSteps[0][i] = step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,14 +143,17 @@ public:
|
||||
#ifdef HAVE_DNN_IE_NN_BUILDER_2019
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
return INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) &&
|
||||
sliceRanges.size() == 1 && sliceRanges[0].size() == 4;
|
||||
sliceRanges.size() == 1 && sliceRanges[0].size() == 4 && !hasSteps;
|
||||
#endif
|
||||
#ifdef HAVE_DNN_NGRAPH
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
return sliceRanges.size() == 1;
|
||||
return sliceRanges.size() == 1 && !hasSteps;
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
backendId == DNN_BACKEND_CUDA;
|
||||
#ifdef HAVE_CUDA
|
||||
if (backendId == DNN_BACKEND_CUDA)
|
||||
return !hasSteps;
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
@@ -154,6 +174,9 @@ public:
|
||||
{
|
||||
if (shapesInitialized || inpShape[j] > 0)
|
||||
outputs[i][j] = normalize_axis_range(sliceRanges[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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,6 +211,7 @@ public:
|
||||
const MatSize& inpShape = inputs[0].size;
|
||||
|
||||
finalSliceRanges = sliceRanges;
|
||||
|
||||
if (sliceRanges.empty())
|
||||
{
|
||||
// Divide input blob on equal parts by axis.
|
||||
@@ -220,6 +244,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!sliceSteps.empty() && sliceSteps[0].size() != inputs[0].dims)
|
||||
sliceSteps[0].resize(inputs[0].dims, 1);
|
||||
|
||||
#if 0
|
||||
std::cout << "DEBUG: DNN/Slice: " << outputs.size() << " inpShape=" << inpShape << std::endl;
|
||||
for (int i = 0; i < outputs.size(); ++i)
|
||||
@@ -427,6 +454,9 @@ public:
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
if (hasSteps)
|
||||
return false; // TODO not implemented yet: https://github.com/opencv/opencv/pull/19546
|
||||
|
||||
std::vector<UMat> inputs;
|
||||
std::vector<UMat> outputs;
|
||||
|
||||
@@ -485,9 +515,24 @@ public:
|
||||
|
||||
const Mat& inpMat = inputs[0];
|
||||
CV_Assert(outputs.size() == finalSliceRanges.size());
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
|
||||
if (!hasSteps)
|
||||
{
|
||||
inpMat(finalSliceRanges[i]).copyTo(outputs[i]);
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
inpMat(finalSliceRanges[i]).copyTo(outputs[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int dimsNum = inpMat.dims;
|
||||
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
std::vector<int> inpIdx(dimsNum, 0);
|
||||
std::vector<int> outIdx(dimsNum, 0);
|
||||
getSliceRecursive(inpMat, inpIdx, finalSliceRanges[i], sliceSteps[i], 0, dimsNum, outputs[i], outIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,11 +648,42 @@ public:
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
void getSliceRecursive(const Mat &inpMat, std::vector<int> &inpIdx,
|
||||
const std::vector<Range> &sliceRanges,
|
||||
const std::vector<int> &sliceSteps, int dim, int dimsNum,
|
||||
Mat &outputs, std::vector<int> &outIdx)
|
||||
{
|
||||
int begin = sliceRanges[dim].start;
|
||||
int end = sliceRanges[dim].end;
|
||||
int step = !sliceSteps.empty() ? sliceSteps[dim] : 1;
|
||||
|
||||
const bool is32F = inpMat.depth() == CV_32F;
|
||||
|
||||
// TODO optimization is required (for 2D tail case at least)
|
||||
for (int k = begin, j = 0; k < end; k += step, j++)
|
||||
{
|
||||
inpIdx[dim] = k;
|
||||
outIdx[dim] = j;
|
||||
|
||||
if (dim + 1 < dimsNum)
|
||||
getSliceRecursive(inpMat, inpIdx, sliceRanges, sliceSteps, dim + 1, dimsNum, outputs, outIdx);
|
||||
else
|
||||
{
|
||||
if (is32F)
|
||||
outputs.at<float>(outIdx.data()) = inpMat.at<float>(inpIdx.data());
|
||||
else
|
||||
outputs.at<short>(outIdx.data()) = inpMat.at<short>(inpIdx.data()); // 16F emulation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// The actual non-negative values determined from @p sliceRanges depends on input size.
|
||||
std::vector<std::vector<Range> > finalSliceRanges;
|
||||
bool hasDynamicShapes;
|
||||
bool shapesInitialized;
|
||||
bool hasSteps;
|
||||
};
|
||||
|
||||
class CropLayerImpl CV_FINAL : public SliceLayerImpl
|
||||
|
||||
@@ -62,12 +62,15 @@ inline void GetMaxScoreIndex(const std::vector<float>& scores, const float thres
|
||||
// score_threshold: a threshold used to filter detection results.
|
||||
// nms_threshold: a threshold used in non maximum suppression.
|
||||
// top_k: if not > 0, keep at most top_k picked indices.
|
||||
// limit: early terminate once the # of picked indices has reached it.
|
||||
// indices: the kept indices of bboxes after nms.
|
||||
template <typename BoxType>
|
||||
inline void NMSFast_(const std::vector<BoxType>& bboxes,
|
||||
const std::vector<float>& scores, const float score_threshold,
|
||||
const float nms_threshold, const float eta, const int top_k,
|
||||
std::vector<int>& indices, float (*computeOverlap)(const BoxType&, const BoxType&))
|
||||
std::vector<int>& indices,
|
||||
float (*computeOverlap)(const BoxType&, const BoxType&),
|
||||
int limit = std::numeric_limits<int>::max())
|
||||
{
|
||||
CV_Assert(bboxes.size() == scores.size());
|
||||
|
||||
@@ -86,8 +89,12 @@ inline void NMSFast_(const std::vector<BoxType>& bboxes,
|
||||
float overlap = computeOverlap(bboxes[idx], bboxes[kept_idx]);
|
||||
keep = overlap <= adaptive_threshold;
|
||||
}
|
||||
if (keep)
|
||||
if (keep) {
|
||||
indices.push_back(idx);
|
||||
if (indices.size() >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keep && eta < 1 && adaptive_threshold > 0.5) {
|
||||
adaptive_threshold *= eta;
|
||||
}
|
||||
|
||||
@@ -112,14 +112,14 @@ ocl::Image2D ocl4dnnGEMMCopyBufferToImage(UMat buffer, int offset,
|
||||
global_copy[0] = padded_width;
|
||||
global_copy[1] = padded_height;
|
||||
|
||||
oclk_gemm_copy
|
||||
bool res = oclk_gemm_copy
|
||||
.args(
|
||||
ocl::KernelArg::PtrReadOnly(buffer),
|
||||
image, offset,
|
||||
width, height,
|
||||
ld)
|
||||
.run(2, global_copy, NULL, false);
|
||||
oclk_gemm_copy.run(2, global_copy, NULL, false);
|
||||
CV_Assert(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
#include "../graph_simplifier.hpp"
|
||||
#include "onnx_graph_simplifier.hpp"
|
||||
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include <queue>
|
||||
|
||||
namespace cv { namespace dnn {
|
||||
CV__DNN_INLINE_NS_BEGIN
|
||||
|
||||
extern bool DNN_DIAGNOSTICS_RUN;
|
||||
|
||||
// This wrapper can behave differently for fake input nodes and real graph nodes.
|
||||
class ONNXNodeWrapper : public ImportNodeWrapper
|
||||
{
|
||||
@@ -639,8 +642,17 @@ Mat getMatFromTensor(opencv_onnx::TensorProto& tensor_proto)
|
||||
}
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsUnsupportedFormat, "Unsupported data type: " +
|
||||
opencv_onnx::TensorProto_DataType_Name(datatype));
|
||||
{
|
||||
std::string errorMsg = "Unsupported data type: " +
|
||||
opencv_onnx::TensorProto_DataType_Name(datatype);
|
||||
|
||||
if (!DNN_DIAGNOSTICS_RUN)
|
||||
{
|
||||
CV_Error(Error::StsUnsupportedFormat, errorMsg);
|
||||
}
|
||||
CV_LOG_ERROR(NULL, errorMsg);
|
||||
return blob;
|
||||
}
|
||||
if (tensor_proto.dims_size() == 0)
|
||||
blob.dims = 1; // To force 1-dimensional cv::Mat for scalars.
|
||||
return blob;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "../precomp.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
#include <opencv2/dnn/layer_reg.private.hpp>
|
||||
|
||||
#include <opencv2/core/utils/logger.defines.hpp>
|
||||
#undef CV_LOG_STRIP_LEVEL
|
||||
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
|
||||
@@ -37,6 +39,7 @@ namespace cv {
|
||||
namespace dnn {
|
||||
CV__DNN_INLINE_NS_BEGIN
|
||||
|
||||
extern bool DNN_DIAGNOSTICS_RUN;
|
||||
|
||||
class ONNXImporter
|
||||
{
|
||||
@@ -58,11 +61,12 @@ class ONNXImporter
|
||||
void addConstant(const std::string& name, const Mat& blob);
|
||||
void addLayer(LayerParams& layerParams,
|
||||
const opencv_onnx::NodeProto& node_proto);
|
||||
static const std::set<String>& getSupportedTypes();
|
||||
|
||||
public:
|
||||
|
||||
ONNXImporter(Net& net, const char *onnxFile)
|
||||
: dstNet(net)
|
||||
: dstNet(net), utilNet()
|
||||
{
|
||||
hasDynamicShapes = false;
|
||||
CV_Assert(onnxFile);
|
||||
@@ -83,7 +87,7 @@ public:
|
||||
}
|
||||
|
||||
ONNXImporter(Net& net, const char* buffer, size_t sizeBuffer)
|
||||
: dstNet(net)
|
||||
: dstNet(net), utilNet()
|
||||
{
|
||||
hasDynamicShapes = false;
|
||||
CV_LOG_DEBUG(NULL, "DNN/ONNX: processing in-memory ONNX model (" << sizeBuffer << " bytes)");
|
||||
@@ -110,6 +114,7 @@ public:
|
||||
|
||||
protected:
|
||||
Net& dstNet;
|
||||
Net utilNet;
|
||||
|
||||
opencv_onnx::GraphProto graph_proto;
|
||||
std::string framework_name;
|
||||
@@ -182,6 +187,10 @@ std::map<std::string, Mat> ONNXImporter::getGraphTensors(
|
||||
tensor_proto = graph_proto.initializer(i);
|
||||
Mat mat = getMatFromTensor(tensor_proto);
|
||||
releaseONNXTensor(tensor_proto);
|
||||
|
||||
if (DNN_DIAGNOSTICS_RUN && mat.empty())
|
||||
continue;
|
||||
|
||||
layers_weights.insert(std::make_pair(tensor_proto.name(), mat));
|
||||
}
|
||||
return layers_weights;
|
||||
@@ -201,118 +210,132 @@ LayerParams ONNXImporter::getLayerParams(const opencv_onnx::NodeProto& node_prot
|
||||
opencv_onnx::AttributeProto attribute_proto = node_proto.attribute(i);
|
||||
std::string attribute_name = attribute_proto.name();
|
||||
|
||||
if(attribute_name == "kernel_shape")
|
||||
try
|
||||
{
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("kernel_size", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if(attribute_name == "strides")
|
||||
{
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("stride", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if(attribute_name == "pads")
|
||||
{
|
||||
if (node_proto.op_type() == "Pad")
|
||||
if(attribute_name == "kernel_shape")
|
||||
{
|
||||
// Padding layer.
|
||||
// Paddings are in order begin0, begin1, .. beginN, end0, end1, ..., endN.
|
||||
// We need to shuffle it to begin0, end0, begin1, end1, ...
|
||||
CV_Assert(attribute_proto.ints_size() % 2 == 0);
|
||||
const int dims = attribute_proto.ints_size() / 2;
|
||||
std::vector<int32_t> paddings;
|
||||
paddings.reserve(attribute_proto.ints_size());
|
||||
for (int i = 0; i < dims; ++i)
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("kernel_size", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if(attribute_name == "strides")
|
||||
{
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("stride", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if(attribute_name == "pads")
|
||||
{
|
||||
if (node_proto.op_type() == "Pad")
|
||||
{
|
||||
paddings.push_back(attribute_proto.ints(i));
|
||||
paddings.push_back(attribute_proto.ints(dims + i));
|
||||
// Padding layer.
|
||||
// Paddings are in order begin0, begin1, .. beginN, end0, end1, ..., endN.
|
||||
// We need to shuffle it to begin0, end0, begin1, end1, ...
|
||||
CV_Assert(attribute_proto.ints_size() % 2 == 0);
|
||||
const int dims = attribute_proto.ints_size() / 2;
|
||||
std::vector<int32_t> paddings;
|
||||
paddings.reserve(attribute_proto.ints_size());
|
||||
for (int i = 0; i < dims; ++i)
|
||||
{
|
||||
paddings.push_back(attribute_proto.ints(i));
|
||||
paddings.push_back(attribute_proto.ints(dims + i));
|
||||
}
|
||||
lp.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size()));
|
||||
}
|
||||
lp.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size()));
|
||||
else
|
||||
{
|
||||
// Convolution or pooling.
|
||||
CV_Assert(attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 4 || attribute_proto.ints_size() == 6);
|
||||
lp.set("pad", parse(attribute_proto.ints()));
|
||||
}
|
||||
}
|
||||
else if(attribute_name == "auto_pad")
|
||||
{
|
||||
if (attribute_proto.s() == "SAME_UPPER" || attribute_proto.s() == "SAME_LOWER") {
|
||||
lp.set("pad_mode", "SAME");
|
||||
}
|
||||
else if (attribute_proto.s() == "VALID") {
|
||||
lp.set("pad_mode", "VALID");
|
||||
}
|
||||
}
|
||||
else if(attribute_name == "dilations")
|
||||
{
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("dilation", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if (attribute_proto.has_i())
|
||||
{
|
||||
::google::protobuf::int64 src = attribute_proto.i();
|
||||
if (src < std::numeric_limits<int32_t>::min() || src > std::numeric_limits<int32_t>::max())
|
||||
CV_Error(Error::StsOutOfRange, "Input is out of OpenCV 32S range");
|
||||
else
|
||||
lp.set(attribute_name, saturate_cast<int32_t>(src));
|
||||
}
|
||||
else if (attribute_proto.has_f())
|
||||
{
|
||||
lp.set(attribute_name, attribute_proto.f());
|
||||
}
|
||||
else if (attribute_proto.has_s())
|
||||
{
|
||||
lp.set(attribute_name, attribute_proto.s());
|
||||
}
|
||||
else if (attribute_proto.floats_size() > 0)
|
||||
{
|
||||
lp.set(attribute_name, DictValue::arrayReal(
|
||||
attribute_proto.floats().data(), attribute_proto.floats_size()));
|
||||
}
|
||||
else if (attribute_proto.ints_size() > 0)
|
||||
{
|
||||
lp.set(attribute_name, parse(attribute_proto.ints()));
|
||||
}
|
||||
else if (attribute_proto.has_t())
|
||||
{
|
||||
opencv_onnx::TensorProto tensor = attribute_proto.t();
|
||||
Mat blob = getMatFromTensor(tensor);
|
||||
lp.blobs.push_back(blob);
|
||||
}
|
||||
else if (attribute_proto.has_g())
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, cv::format("DNN/ONNX/Attribute[%s]: 'Graph' is not supported", attribute_name.c_str()));
|
||||
}
|
||||
else if (attribute_proto.graphs_size() > 0)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
cv::format("DNN/ONNX/Attribute[%s]: 'Graphs' (%d) in attributes is not supported",
|
||||
attribute_name.c_str(), attribute_proto.graphs_size())
|
||||
);
|
||||
}
|
||||
else if (attribute_proto.strings_size() > 0)
|
||||
{
|
||||
std::string msg = cv::format("DNN/ONNX/Attribute[%s]: 'Strings' (%d) are not supported",
|
||||
attribute_name.c_str(), attribute_proto.strings_size());
|
||||
CV_LOG_ERROR(NULL, msg);
|
||||
for (int i = 0; i < attribute_proto.strings_size(); i++)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, " Attribute[" << attribute_name << "].string(" << i << ") = '" << attribute_proto.strings(i) << "'");
|
||||
}
|
||||
CV_Error(Error::StsNotImplemented, msg);
|
||||
}
|
||||
else if (attribute_proto.tensors_size() > 0)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
cv::format("DNN/ONNX/Attribute[%s]: 'Tensors' (%d) in attributes are not supported",
|
||||
attribute_name.c_str(), attribute_proto.tensors_size())
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convolution or pooling.
|
||||
CV_Assert(attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 4 || attribute_proto.ints_size() == 6);
|
||||
lp.set("pad", parse(attribute_proto.ints()));
|
||||
CV_Error(Error::StsNotImplemented, cv::format("DNN/ONNX/Attribute[%s]: unsupported attribute format", attribute_name.c_str()));
|
||||
}
|
||||
}
|
||||
else if(attribute_name == "auto_pad")
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (attribute_proto.s() == "SAME_UPPER" || attribute_proto.s() == "SAME_LOWER") {
|
||||
lp.set("pad_mode", "SAME");
|
||||
}
|
||||
else if (attribute_proto.s() == "VALID") {
|
||||
lp.set("pad_mode", "VALID");
|
||||
}
|
||||
}
|
||||
else if(attribute_name == "dilations")
|
||||
{
|
||||
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
|
||||
lp.set("dilation", parse(attribute_proto.ints()));
|
||||
}
|
||||
else if (attribute_proto.has_i())
|
||||
{
|
||||
::google::protobuf::int64 src = attribute_proto.i();
|
||||
if (src < std::numeric_limits<int32_t>::min() || src > std::numeric_limits<int32_t>::max())
|
||||
CV_Error(Error::StsOutOfRange, "Input is out of OpenCV 32S range");
|
||||
else
|
||||
lp.set(attribute_name, saturate_cast<int32_t>(src));
|
||||
}
|
||||
else if (attribute_proto.has_f())
|
||||
{
|
||||
lp.set(attribute_name, attribute_proto.f());
|
||||
}
|
||||
else if (attribute_proto.has_s())
|
||||
{
|
||||
lp.set(attribute_name, attribute_proto.s());
|
||||
}
|
||||
else if (attribute_proto.floats_size() > 0)
|
||||
{
|
||||
lp.set(attribute_name, DictValue::arrayReal(
|
||||
attribute_proto.floats().data(), attribute_proto.floats_size()));
|
||||
}
|
||||
else if (attribute_proto.ints_size() > 0)
|
||||
{
|
||||
lp.set(attribute_name, parse(attribute_proto.ints()));
|
||||
}
|
||||
else if (attribute_proto.has_t())
|
||||
{
|
||||
opencv_onnx::TensorProto tensor = attribute_proto.t();
|
||||
Mat blob = getMatFromTensor(tensor);
|
||||
lp.blobs.push_back(blob);
|
||||
}
|
||||
else if (attribute_proto.has_g())
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, cv::format("DNN/ONNX/Attribute[%s]: 'Graph' is not supported", attribute_name.c_str()));
|
||||
}
|
||||
else if (attribute_proto.graphs_size() > 0)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
cv::format("DNN/ONNX/Attribute[%s]: 'Graphs' (%d) in attributes is not supported",
|
||||
attribute_name.c_str(), attribute_proto.graphs_size())
|
||||
);
|
||||
}
|
||||
else if (attribute_proto.strings_size() > 0)
|
||||
{
|
||||
std::string msg = cv::format("DNN/ONNX/Attribute[%s]: 'Strings' (%d) are not supported",
|
||||
attribute_name.c_str(), attribute_proto.strings_size());
|
||||
CV_LOG_ERROR(NULL, msg);
|
||||
for (int i = 0; i < attribute_proto.strings_size(); i++)
|
||||
CV_UNUSED(e);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, " Attribute[" << attribute_name << "].string(" << i << ") = '" << attribute_proto.strings(i) << "'");
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: Potential problem with processing attributes for node " << node_proto.name() << " Attribute " << attribute_name.c_str()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
CV_Error(Error::StsNotImplemented, msg);
|
||||
}
|
||||
else if (attribute_proto.tensors_size() > 0)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
cv::format("DNN/ONNX/Attribute[%s]: 'Tensors' (%d) in attributes are not supported",
|
||||
attribute_name.c_str(), attribute_proto.tensors_size())
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, cv::format("DNN/ONNX/Attribute[%s]: unsupported attribute format", attribute_name.c_str()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return lp;
|
||||
@@ -338,7 +361,11 @@ Mat ONNXImporter::getBlob(const std::string& input_name)
|
||||
void ONNXImporter::addLayer(LayerParams& layerParams,
|
||||
const opencv_onnx::NodeProto& node_proto)
|
||||
{
|
||||
int id = dstNet.addLayer(layerParams.name, layerParams.type, layerParams);
|
||||
int id;
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
id = utilNet.addLayer(layerParams.name, layerParams.type, layerParams);
|
||||
else
|
||||
id = dstNet.addLayer(layerParams.name, layerParams.type, layerParams);
|
||||
for (int i = 0; i < node_proto.output_size(); ++i)
|
||||
{
|
||||
layer_id.insert(std::make_pair(node_proto.output(i), LayerInfo(id, i)));
|
||||
@@ -351,7 +378,10 @@ void ONNXImporter::addLayer(LayerParams& layerParams,
|
||||
const std::string& input_name = node_proto.input(j);
|
||||
IterLayerId_t layerId = layer_id.find(input_name);
|
||||
if (layerId != layer_id.end()) {
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, inpNum);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
utilNet.connect(layerId->second.layerId, layerId->second.outputId, id, inpNum);
|
||||
else
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, inpNum);
|
||||
++inpNum;
|
||||
// Collect input shapes.
|
||||
IterShape_t shapeIt = outShapes.find(input_name);
|
||||
@@ -360,7 +390,11 @@ void ONNXImporter::addLayer(LayerParams& layerParams,
|
||||
}
|
||||
}
|
||||
// Compute shape of output blob for this layer.
|
||||
Ptr<Layer> layer = dstNet.getLayer(id); // FIXIT: avoid instantiation of layers during the import stage
|
||||
Ptr<Layer> layer;
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
layer = utilNet.getLayer(id);
|
||||
else
|
||||
layer = dstNet.getLayer(id); // FIXIT: avoid instantiation of layers during the import stage
|
||||
layer->getMemoryShapes(layerInpShapes, 0, layerOutShapes, layerInternalShapes);
|
||||
for (int i = 0; i < node_proto.output_size() && i < (int)layerOutShapes.size(); ++i)
|
||||
{
|
||||
@@ -437,8 +471,37 @@ void ONNXImporter::populateNet()
|
||||
layer_id.insert(std::make_pair(name, LayerInfo(0, netInputs.size() - 1)));
|
||||
}
|
||||
}
|
||||
utilNet.setInputsNames(netInputs);
|
||||
dstNet.setInputsNames(netInputs);
|
||||
|
||||
if (DNN_DIAGNOSTICS_RUN) {
|
||||
auto &supportedTypes = getSupportedTypes();
|
||||
for (int li = 0; li < layersSize; li++) {
|
||||
const opencv_onnx::NodeProto &node_proto = graph_proto.node(li);
|
||||
std::string name = node_proto.output(0);
|
||||
std::string layer_type = node_proto.op_type();
|
||||
auto registered = supportedTypes.find(layer_type);
|
||||
if (registered == supportedTypes.end()) {
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: NOTE: Potential problem with creating node " << name<< " with type " << layer_type << ".\n Type "
|
||||
<< layer_type << " IS NOT SUPPORTED!\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
auto oldConstBlobs = constBlobs;
|
||||
auto oldOutShapes = outShapes;
|
||||
auto oldLayerId = layer_id;
|
||||
CV_LOG_INFO(NULL, "DNN/ONNX: start diagnostic run!");
|
||||
for (int li = 0; li < layersSize; li++) {
|
||||
const opencv_onnx::NodeProto &node_proto = graph_proto.node(li);
|
||||
handleNode(node_proto);
|
||||
}
|
||||
CV_LOG_INFO(NULL, "DNN/ONNX: diagnostic run completed!");
|
||||
constBlobs = oldConstBlobs;
|
||||
outShapes = oldOutShapes;
|
||||
layer_id = oldLayerId;
|
||||
enableModelDiagnostics(false);
|
||||
}
|
||||
|
||||
for(int li = 0; li < layersSize; li++)
|
||||
{
|
||||
const opencv_onnx::NodeProto& node_proto = graph_proto.node(li);
|
||||
@@ -448,6 +511,80 @@ void ONNXImporter::populateNet()
|
||||
CV_LOG_DEBUG(NULL, "DNN/ONNX: import completed!");
|
||||
}
|
||||
|
||||
const std::set<String>& ONNXImporter::getSupportedTypes()
|
||||
{
|
||||
static const std::set<String> layerTypes = {
|
||||
"MaxPool",
|
||||
"AveragePool",
|
||||
"GlobalAveragePool",
|
||||
"GlobalMaxPool",
|
||||
"ReduceMean",
|
||||
"ReduceSum",
|
||||
"ReduceMax",
|
||||
"Slice",
|
||||
"Split",
|
||||
"Add",
|
||||
"Sum",
|
||||
"Sub",
|
||||
"Pow",
|
||||
"Max",
|
||||
"Neg",
|
||||
"Constant",
|
||||
"LSTM",
|
||||
"ImageScaler",
|
||||
"Clip",
|
||||
"LeakyRelu",
|
||||
"Relu",
|
||||
"Elu",
|
||||
"Tanh",
|
||||
"PRelu",
|
||||
"LRN",
|
||||
"InstanceNormalization",
|
||||
"BatchNormalization",
|
||||
"Gemm",
|
||||
"MatMul",
|
||||
"Mul",
|
||||
"Div",
|
||||
"Conv",
|
||||
"ConvTranspose",
|
||||
"Transpose",
|
||||
"Squeeze",
|
||||
"Flatten",
|
||||
"Unsqueeze",
|
||||
"Expand",
|
||||
"Reshape",
|
||||
"Pad",
|
||||
"Shape",
|
||||
"Cast",
|
||||
"ConstantOfShape",
|
||||
"ConstantFill",
|
||||
"Gather",
|
||||
"Concat",
|
||||
"Resize",
|
||||
"Upsample",
|
||||
"SoftMax",
|
||||
"Softmax",
|
||||
"LogSoftmax",
|
||||
"DetectionOutput",
|
||||
"Interp",
|
||||
"CropAndResize",
|
||||
"ROIPooling",
|
||||
"PSROIPooling",
|
||||
"ChannelsPReLU",
|
||||
"Sigmoid",
|
||||
"Swish",
|
||||
"Mish",
|
||||
"AbsVal",
|
||||
"BNLL",
|
||||
"MaxUnpool",
|
||||
"Dropout",
|
||||
"Identity",
|
||||
"Crop",
|
||||
"Normalize"
|
||||
};
|
||||
return layerTypes;
|
||||
}
|
||||
|
||||
void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
{
|
||||
opencv_onnx::NodeProto node_proto = node_proto_; // TODO FIXIT
|
||||
@@ -458,11 +595,11 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
CV_LOG_DEBUG(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())
|
||||
);
|
||||
|
||||
LayerParams layerParams;
|
||||
try
|
||||
{
|
||||
// FIXIT not all cases can be repacked into "LayerParams". Importer should handle such cases directly for each "layer_type"
|
||||
LayerParams layerParams = getLayerParams(node_proto);
|
||||
layerParams = getLayerParams(node_proto);
|
||||
|
||||
layerParams.name = name;
|
||||
layerParams.type = layer_type;
|
||||
@@ -641,20 +778,11 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
int axis = 0;
|
||||
std::vector<int> begin;
|
||||
std::vector<int> end;
|
||||
std::vector<int> steps;
|
||||
int inp_size = node_proto.input_size();
|
||||
|
||||
if (inp_size == 1)
|
||||
{
|
||||
if (layerParams.has("steps"))
|
||||
{
|
||||
DictValue steps = layerParams.get("steps");
|
||||
for (int i = 0; i < steps.size(); ++i)
|
||||
{
|
||||
if (steps.get<int>(i) != 1)
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
"Slice layer only supports steps = 1");
|
||||
}
|
||||
}
|
||||
if (layerParams.has("axes")) {
|
||||
DictValue axes = layerParams.get("axes");
|
||||
for (int i = 1; i < axes.size(); ++i) {
|
||||
@@ -677,7 +805,7 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
int finish = ends.get<int>(i);
|
||||
end.push_back((finish < 0) ? --finish : finish); // numpy doesn't include last dim
|
||||
}
|
||||
} else {
|
||||
} else { // inp_size > 1
|
||||
CV_Assert(inp_size >= 3);
|
||||
for (int i = 1; i < inp_size; i++) {
|
||||
CV_Assert(constBlobs.find(node_proto.input(i)) != constBlobs.end());
|
||||
@@ -711,6 +839,12 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
if (inp_size == 5) {
|
||||
CV_Assert(constBlobs.find(node_proto.input(4)) != constBlobs.end());
|
||||
Mat step_blob = getBlob(node_proto, 4);
|
||||
const int* steps_ptr = step_blob.ptr<int>();
|
||||
|
||||
if (axis > 0)
|
||||
steps.resize(axis, 1);
|
||||
|
||||
std::copy(steps_ptr, steps_ptr + step_blob.total(), std::back_inserter(steps));
|
||||
|
||||
// Very strange application for Slice op with tensor reversing.
|
||||
// We just workaround it for 2d constants.
|
||||
@@ -728,13 +862,15 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
return;
|
||||
}
|
||||
}
|
||||
CV_CheckEQ(countNonZero(step_blob != 1), 0, "Slice layer only supports steps = 1");
|
||||
}
|
||||
}
|
||||
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())
|
||||
layerParams.set("steps", DictValue::arrayInt(&steps[0], steps.size()));
|
||||
|
||||
if (constBlobs.find(node_proto.input(0)) != constBlobs.end())
|
||||
{
|
||||
Mat inp = getBlob(node_proto, 0);
|
||||
@@ -799,7 +935,11 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
constParams.name = layerParams.name + "/const";
|
||||
constParams.type = "Const";
|
||||
constParams.blobs.push_back((isSub ? -1 : 1) * blob);
|
||||
int id = dstNet.addLayer(constParams.name, constParams.type, constParams);
|
||||
int id;
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
id = utilNet.addLayer(constParams.name, constParams.type, constParams);
|
||||
else
|
||||
id = dstNet.addLayer(constParams.name, constParams.type, constParams);
|
||||
layer_id.insert(std::make_pair(constParams.name, LayerInfo(id, 0)));
|
||||
outShapes[constParams.name] = shape(blob);
|
||||
|
||||
@@ -844,12 +984,19 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
powerParams.type = "Power";
|
||||
powerParams.set("scale", -1);
|
||||
|
||||
int id;
|
||||
//Create Power layer
|
||||
int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
id = utilNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
else
|
||||
id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
//Connect to input
|
||||
IterLayerId_t layerId = layer_id.find(node_proto.input(1));
|
||||
CV_Assert(layerId != layer_id.end());
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
utilNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
else
|
||||
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(1)];
|
||||
@@ -1036,11 +1183,18 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
layerParams.erase("epsilon");
|
||||
|
||||
//Create MVN layer
|
||||
int id = dstNet.addLayer(mvnParams.name, mvnParams.type, mvnParams);
|
||||
int id;
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
id = utilNet.addLayer(mvnParams.name, mvnParams.type, mvnParams);
|
||||
else
|
||||
id = dstNet.addLayer(mvnParams.name, mvnParams.type, mvnParams);
|
||||
//Connect to input
|
||||
IterLayerId_t layerId = layer_id.find(node_proto.input(0));
|
||||
CV_Assert(layerId != layer_id.end());
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
utilNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
else
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
//Add shape
|
||||
layer_id.insert(std::make_pair(mvnParams.name, LayerInfo(id, 0)));
|
||||
outShapes[mvnParams.name] = outShapes[node_proto.input(0)];
|
||||
@@ -1233,12 +1387,19 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
powerParams.type = "Power";
|
||||
powerParams.set("power", -1);
|
||||
|
||||
int id;
|
||||
//Create Power layer
|
||||
int id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
id = utilNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
else
|
||||
id = dstNet.addLayer(powerParams.name, powerParams.type, powerParams);
|
||||
//Connect to input
|
||||
IterLayerId_t layerId = layer_id.find(node_proto.input(1));
|
||||
CV_Assert(layerId != layer_id.end());
|
||||
dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
utilNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0);
|
||||
else
|
||||
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(1)];
|
||||
@@ -1923,9 +2084,31 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: ERROR during 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())
|
||||
);
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: Potential problem during 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()) << "\n" << e.msg
|
||||
);
|
||||
auto registeredLayers = getLayerFactoryImpl();
|
||||
if (registeredLayers.find(layerParams.type) != registeredLayers.end())
|
||||
{
|
||||
try
|
||||
{
|
||||
Ptr<Layer> layer = LayerFactory::createLayerInstance(layerParams.type, layerParams);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: Layer of type " << layerParams.type << "(" << layer_type << ") cannot be created with parameters " << layerParams << ". Error: " << e.what()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "DNN/ONNX: ERROR during 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())
|
||||
);
|
||||
}
|
||||
for (int i = 0; i < node_proto.input_size(); i++)
|
||||
{
|
||||
CV_LOG_INFO(NULL, " Input[" << i << "] = '" << node_proto.input(i) << "'");
|
||||
@@ -1934,7 +2117,16 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
{
|
||||
CV_LOG_INFO(NULL, " Output[" << i << "] = '" << node_proto.output(i) << "'");
|
||||
}
|
||||
CV_Error(Error::StsError, cv::format("Node [%s]:(%s) parse error: %s", layer_type.c_str(), name.c_str(), e.what()));
|
||||
if (DNN_DIAGNOSTICS_RUN)
|
||||
{
|
||||
for (int i = 0; i < node_proto.output_size(); ++i)
|
||||
{
|
||||
layer_id.insert(std::make_pair(node_proto.output(i), LayerInfo(0, i)));
|
||||
outShapes[node_proto.output(i)] = outShapes[node_proto.input(0)];
|
||||
}
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsError, cv::format("Node [%s]:(%s) parse error: %s", layer_type.c_str(), name.c_str(), e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -655,6 +655,22 @@ InferenceEngine::Core& getCore(const std::string& id)
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool detectArmPlugin_()
|
||||
{
|
||||
InferenceEngine::Core& ie = getCore("CPU");
|
||||
const std::vector<std::string> devices = ie.GetAvailableDevices();
|
||||
for (std::vector<std::string>::const_iterator i = devices.begin(); i != devices.end(); ++i)
|
||||
{
|
||||
if (i->find("CPU") != std::string::npos)
|
||||
{
|
||||
const std::string name = ie.GetMetric(*i, METRIC_KEY(FULL_DEVICE_NAME)).as<std::string>();
|
||||
CV_LOG_INFO(NULL, "CPU plugin: " << name);
|
||||
return name.find("arm_compute::NEON") != std::string::npos;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(OPENCV_DNN_IE_VPU_TYPE_DEFAULT)
|
||||
static bool detectMyriadX_(std::string device)
|
||||
{
|
||||
@@ -1185,6 +1201,12 @@ bool isMyriadX()
|
||||
return myriadX;
|
||||
}
|
||||
|
||||
bool isArmComputePlugin()
|
||||
{
|
||||
static bool armPlugin = getInferenceEngineCPUType() == CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE;
|
||||
return armPlugin;
|
||||
}
|
||||
|
||||
static std::string getInferenceEngineVPUType_()
|
||||
{
|
||||
static std::string param_vpu_type = utils::getConfigurationParameterString("OPENCV_DNN_IE_VPU_TYPE", "");
|
||||
@@ -1223,6 +1245,14 @@ cv::String getInferenceEngineVPUType()
|
||||
return vpu_type;
|
||||
}
|
||||
|
||||
cv::String getInferenceEngineCPUType()
|
||||
{
|
||||
static cv::String cpu_type = detectArmPlugin_() ?
|
||||
CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE :
|
||||
CV_DNN_INFERENCE_ENGINE_CPU_TYPE_X86;
|
||||
return cpu_type;
|
||||
}
|
||||
|
||||
#else // HAVE_INF_ENGINE
|
||||
|
||||
cv::String getInferenceEngineBackendType()
|
||||
@@ -1238,6 +1268,11 @@ cv::String getInferenceEngineVPUType()
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support");
|
||||
}
|
||||
|
||||
cv::String getInferenceEngineCPUType()
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "This OpenCV build doesn't include InferenceEngine support");
|
||||
}
|
||||
#endif // HAVE_INF_ENGINE
|
||||
|
||||
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
#define INF_ENGINE_RELEASE_2020_4 2020040000
|
||||
#define INF_ENGINE_RELEASE_2021_1 2021010000
|
||||
#define INF_ENGINE_RELEASE_2021_2 2021020000
|
||||
#define INF_ENGINE_RELEASE_2021_3 2021030000
|
||||
|
||||
#ifndef INF_ENGINE_RELEASE
|
||||
#warning("IE version have not been provided via command-line. Using 2021.2 by default")
|
||||
#define INF_ENGINE_RELEASE INF_ENGINE_RELEASE_2021_2
|
||||
#warning("IE version have not been provided via command-line. Using 2021.3 by default")
|
||||
#define INF_ENGINE_RELEASE INF_ENGINE_RELEASE_2021_3
|
||||
#endif
|
||||
|
||||
#define INF_ENGINE_VER_MAJOR_GT(ver) (((INF_ENGINE_RELEASE) / 10000) > ((ver) / 10000))
|
||||
@@ -255,8 +256,11 @@ CV__DNN_INLINE_NS_BEGIN
|
||||
|
||||
bool isMyriadX();
|
||||
|
||||
bool isArmComputePlugin();
|
||||
|
||||
CV__DNN_INLINE_NS_END
|
||||
|
||||
|
||||
InferenceEngine::Core& getCore(const std::string& id);
|
||||
|
||||
template<typename T = size_t>
|
||||
|
||||
@@ -12,6 +12,7 @@ Implementation of Tensorflow models parser
|
||||
#include "../precomp.hpp"
|
||||
|
||||
#include <opencv2/core/utils/logger.defines.hpp>
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
#undef CV_LOG_STRIP_LEVEL
|
||||
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
@@ -295,6 +296,22 @@ DataLayout getDataLayout(
|
||||
return it != data_layouts.end() ? it->second : DATA_LAYOUT_UNKNOWN;
|
||||
}
|
||||
|
||||
static
|
||||
bool hasAllOnes(const Mat &inputs, int startPos, int endPos)
|
||||
{
|
||||
CV_CheckLE(inputs.dims, 2, "");
|
||||
CV_CheckGE(startPos, 0, "");
|
||||
CV_CheckLE(startPos, endPos, "");
|
||||
CV_CheckLT((size_t)endPos, inputs.total(), "");
|
||||
|
||||
for (int i = startPos; i < endPos; i++)
|
||||
{
|
||||
if (inputs.at<int>(i) != 1 && inputs.at<int>(i) != -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setStrides(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
||||
{
|
||||
if (hasLayerAttr(layer, "strides"))
|
||||
@@ -490,6 +507,9 @@ protected:
|
||||
std::map<String, Mat> sharedWeights;
|
||||
|
||||
std::map<String, int> layer_id;
|
||||
|
||||
private:
|
||||
void addPermuteLayer(const int* order, const std::string& permName, Pin& inpId);
|
||||
};
|
||||
|
||||
TFImporter::TFImporter(Net& net, const char *model, const char *config)
|
||||
@@ -895,6 +915,17 @@ void TFImporter::populateNet()
|
||||
CV_LOG_DEBUG(NULL, "DNN/TF: ===================== Import completed =====================");
|
||||
}
|
||||
|
||||
void TFImporter::addPermuteLayer(const int* order, const std::string& permName, Pin& inpId)
|
||||
{
|
||||
LayerParams permLP;
|
||||
permLP.set("order", DictValue::arrayInt<const int*>(order, 4));
|
||||
CV_Assert(layer_id.find(permName) == layer_id.end());
|
||||
int permId = dstNet.addLayer(permName, "Permute", permLP);
|
||||
layer_id[permName] = permId;
|
||||
connect(layer_id, dstNet, inpId, permId, 0);
|
||||
inpId = Pin(permName);
|
||||
}
|
||||
|
||||
void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
{
|
||||
tensorflow::NodeDef layer = layer_;
|
||||
@@ -1276,37 +1307,49 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
if (value_id.find(layer.input(1)) != value_id.end())
|
||||
{
|
||||
Mat newShape = getTensorContent(getConstBlob(layer, value_id, 1));
|
||||
if (newShape.total() == 4)
|
||||
int newShapeSize = newShape.total();
|
||||
bool hasSwap = false;
|
||||
if (newShapeSize == 4 && hasAllOnes(newShape, 0, 2))
|
||||
{
|
||||
// NHWC->NCHW
|
||||
std::swap(*newShape.ptr<int32_t>(0, 2), *newShape.ptr<int32_t>(0, 3));
|
||||
std::swap(*newShape.ptr<int32_t>(0, 1), *newShape.ptr<int32_t>(0, 2));
|
||||
hasSwap = true;
|
||||
}
|
||||
if (inpLayout == DATA_LAYOUT_NHWC)
|
||||
{
|
||||
if (newShape.total() != 4 || newShape.at<int>(1) == 1)
|
||||
if (newShapeSize >= 2 || newShape.at<int>(1) == 1)
|
||||
{
|
||||
LayerParams permLP;
|
||||
int order[] = {0, 2, 3, 1}; // From OpenCV's NCHW to NHWC.
|
||||
permLP.set("order", DictValue::arrayInt<int*>(order, 4));
|
||||
|
||||
std::string permName = name + "/nchw";
|
||||
CV_Assert(layer_id.find(permName) == layer_id.end());
|
||||
int permId = dstNet.addLayer(permName, "Permute", permLP);
|
||||
layer_id[permName] = permId;
|
||||
connect(layer_id, dstNet, inpId, permId, 0);
|
||||
inpId = Pin(permName);
|
||||
inpLayout = DATA_LAYOUT_NCHW;
|
||||
addPermuteLayer(order, name + "/nhwc", inpId);
|
||||
if (newShapeSize < 4)
|
||||
{
|
||||
inpLayout = DATA_LAYOUT_NCHW;
|
||||
}
|
||||
else
|
||||
{
|
||||
inpLayout = DATA_LAYOUT_NHWC;
|
||||
}
|
||||
}
|
||||
}
|
||||
layerParams.set("dim", DictValue::arrayInt<int*>(newShape.ptr<int>(), newShape.total()));
|
||||
layerParams.set("dim", DictValue::arrayInt<int*>(newShape.ptr<int>(), newShapeSize));
|
||||
|
||||
int id = dstNet.addLayer(name, "Reshape", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
connect(layer_id, dstNet, inpId, id, 0);
|
||||
data_layouts[name] = newShape.total() == 2 ? DATA_LAYOUT_PLANAR : inpLayout;
|
||||
inpId = Pin(name);
|
||||
|
||||
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);
|
||||
inpLayout = DATA_LAYOUT_NCHW;
|
||||
}
|
||||
|
||||
data_layouts[name] = newShapeSize == 2 ? DATA_LAYOUT_PLANAR : inpLayout;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1783,6 +1826,7 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
{
|
||||
// Check if all the inputs have the same shape.
|
||||
bool equalInpShapes = true;
|
||||
bool isShapeOnes = false;
|
||||
MatShape outShape0;
|
||||
for (int ii = 0; ii < num_inputs && !netInputShapes.empty(); ii++)
|
||||
{
|
||||
@@ -1803,12 +1847,14 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
else if (outShape != outShape0)
|
||||
{
|
||||
equalInpShapes = false;
|
||||
isShapeOnes = isAllOnes(outShape, 2, outShape.size()) ||
|
||||
isAllOnes(outShape0, 2, outShape0.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int id;
|
||||
if (equalInpShapes || netInputShapes.empty())
|
||||
if (equalInpShapes || netInputShapes.empty() || (!equalInpShapes && isShapeOnes))
|
||||
{
|
||||
layerParams.set("operation", type == "RealDiv" ? "div" : "prod");
|
||||
id = dstNet.addLayer(name, "Eltwise", layerParams);
|
||||
@@ -2314,12 +2360,9 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
// To keep correct order after squeeze dims we first need to change layout from NCHW to NHWC
|
||||
LayerParams permLP;
|
||||
int order[] = {0, 2, 3, 1}; // From OpenCV's NCHW to NHWC.
|
||||
permLP.set("order", DictValue::arrayInt<int*>(order, 4));
|
||||
std::string permName = name + "/nchw";
|
||||
CV_Assert(layer_id.find(permName) == layer_id.end());
|
||||
int permId = dstNet.addLayer(permName, "Permute", permLP);
|
||||
layer_id[permName] = permId;
|
||||
connect(layer_id, dstNet, Pin(name), permId, 0);
|
||||
Pin inpId = Pin(name);
|
||||
addPermuteLayer(order, permName, inpId);
|
||||
|
||||
LayerParams squeezeLp;
|
||||
std::string squeezeName = name + "/squeeze";
|
||||
@@ -2331,6 +2374,38 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
|
||||
connect(layer_id, dstNet, Pin(permName), squeezeId, 0);
|
||||
}
|
||||
}
|
||||
else if (axis == 1)
|
||||
{
|
||||
int order[] = {0, 2, 3, 1}; // From OpenCV's NCHW to NHWC.
|
||||
Pin inpId = parsePin(layer.input(0));
|
||||
addPermuteLayer(order, name + "/nhwc", inpId);
|
||||
|
||||
layerParams.set("pool", type == "Mean" ? "ave" : "sum");
|
||||
layerParams.set("kernel_h", 1);
|
||||
layerParams.set("global_pooling_w", true);
|
||||
int id = dstNet.addLayer(name, "Pooling", layerParams);
|
||||
layer_id[name] = id;
|
||||
connect(layer_id, dstNet, inpId, id, 0);
|
||||
|
||||
if (!keepDims)
|
||||
{
|
||||
LayerParams squeezeLp;
|
||||
std::string squeezeName = name + "/squeeze";
|
||||
CV_Assert(layer_id.find(squeezeName) == layer_id.end());
|
||||
int channel_id = 3; // TF NHWC layout
|
||||
squeezeLp.set("axis", channel_id - 1);
|
||||
squeezeLp.set("end_axis", channel_id);
|
||||
int squeezeId = dstNet.addLayer(squeezeName, "Flatten", squeezeLp);
|
||||
layer_id[squeezeName] = squeezeId;
|
||||
connect(layer_id, dstNet, Pin(name), squeezeId, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int order[] = {0, 3, 1, 2}; // From NHWC to OpenCV's NCHW.
|
||||
Pin inpId = parsePin(name);
|
||||
addPermuteLayer(order, name + "/nchw", inpId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (indices.total() != 2 || indices.at<int>(0) != 1 || indices.at<int>(1) != 2)
|
||||
CV_Error(Error::StsNotImplemented, "Unsupported mode of reduce_mean or reduce_sum operation.");
|
||||
|
||||
@@ -30,11 +30,13 @@
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_2019R1_1 "dnn_skip_ie_2019r1_1"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_2019R2 "dnn_skip_ie_2019r2"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_2019R3 "dnn_skip_ie_2019r3"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_CPU "dnn_skip_ie_cpu"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_OPENCL "dnn_skip_ie_ocl"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16 "dnn_skip_ie_ocl_fp16"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_2 "dnn_skip_ie_myriad2"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_X "dnn_skip_ie_myriadx"
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_MYRIAD CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_2, CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_X
|
||||
#define CV_TEST_TAG_DNN_SKIP_IE_ARM_CPU "dnn_skip_ie_arm_cpu"
|
||||
|
||||
#define CV_TEST_TAG_DNN_SKIP_VULKAN "dnn_skip_vulkan"
|
||||
|
||||
|
||||
@@ -453,13 +453,13 @@ void initDNNTests()
|
||||
#ifdef HAVE_DNN_IE_NN_BUILDER_2019
|
||||
CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER,
|
||||
#endif
|
||||
""
|
||||
CV_TEST_TAG_DNN_SKIP_IE_CPU
|
||||
);
|
||||
#endif
|
||||
registerGlobalSkipTag(
|
||||
// see validateVPUType(): CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_2, CV_TEST_TAG_DNN_SKIP_IE_MYRIAD_X
|
||||
CV_TEST_TAG_DNN_SKIP_IE_OPENCL, CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16
|
||||
);
|
||||
#endif
|
||||
#ifdef HAVE_VULKAN
|
||||
registerGlobalSkipTag(
|
||||
CV_TEST_TAG_DNN_SKIP_VULKAN
|
||||
|
||||
@@ -727,6 +727,10 @@ TEST_P(Test_Darknet_layers, shortcut)
|
||||
|
||||
TEST_P(Test_Darknet_layers, upsample)
|
||||
{
|
||||
#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
|
||||
testDarknetLayer("upsample");
|
||||
}
|
||||
|
||||
@@ -803,6 +807,11 @@ TEST_P(Test_Darknet_layers, relu)
|
||||
testDarknetLayer("relu");
|
||||
}
|
||||
|
||||
TEST_P(Test_Darknet_layers, sam)
|
||||
{
|
||||
testDarknetLayer("sam", true);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/**/, Test_Darknet_layers, dnnBackendsAndTargets());
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -258,7 +258,17 @@ TEST_P(LRN, Accuracy)
|
||||
|
||||
int sz[] = {1, inChannels, inSize.height, inSize.width};
|
||||
Mat input(4, &sz[0], CV_32F);
|
||||
test(lp, input, backendId, targetId);
|
||||
|
||||
double l1 = 0.0, lInf = 0.0;
|
||||
// The OpenCL kernels use the native_ math functions which have
|
||||
// implementation defined accuracy, so we use relaxed thresholds. See
|
||||
// https://github.com/opencv/opencv/issues/9821 for more details.
|
||||
if (targetId == DNN_TARGET_OPENCL)
|
||||
{
|
||||
l1 = 0.01;
|
||||
lInf = 0.01;
|
||||
}
|
||||
test(lp, input, backendId, targetId, false, l1, lInf);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Layer_Test_Halide, LRN, Combine(
|
||||
|
||||
@@ -169,8 +169,17 @@ TEST_P(Test_Caffe_layers, Softmax)
|
||||
|
||||
TEST_P(Test_Caffe_layers, LRN)
|
||||
{
|
||||
testLayerUsingCaffeModels("layer_lrn_spatial");
|
||||
testLayerUsingCaffeModels("layer_lrn_channels");
|
||||
double l1 = 0.0, lInf = 0.0;
|
||||
// The OpenCL kernels use the native_ math functions which have
|
||||
// implementation defined accuracy, so we use relaxed thresholds. See
|
||||
// https://github.com/opencv/opencv/issues/9821 for more details.
|
||||
if (target == DNN_TARGET_OPENCL)
|
||||
{
|
||||
l1 = 0.01;
|
||||
lInf = 0.01;
|
||||
}
|
||||
testLayerUsingCaffeModels("layer_lrn_spatial", false, true, l1, lInf);
|
||||
testLayerUsingCaffeModels("layer_lrn_channels", false, true, l1, lInf);
|
||||
}
|
||||
|
||||
TEST_P(Test_Caffe_layers, Convolution)
|
||||
@@ -1583,6 +1592,11 @@ 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(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
|
||||
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD);
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ TEST_P(Test_ONNX_layers, Convolution_variable_weight_bias)
|
||||
if (backend == DNN_BACKEND_VKCOM)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_VULKAN); // not supported
|
||||
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_CPU &&
|
||||
getInferenceEngineCPUType() == CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_ARM_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
|
||||
String basename = "conv_variable_wb";
|
||||
Net net = readNetFromONNX(_tf("models/" + basename + ".onnx"));
|
||||
ASSERT_FALSE(net.empty());
|
||||
@@ -436,11 +440,19 @@ TEST_P(Test_ONNX_layers, BatchNormalization3D)
|
||||
|
||||
TEST_P(Test_ONNX_layers, BatchNormalizationUnfused)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
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); // exception
|
||||
#endif
|
||||
testONNXModels("frozenBatchNorm2d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, BatchNormalizationSubgraph)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
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); // exception
|
||||
#endif
|
||||
testONNXModels("batch_norm_subgraph");
|
||||
}
|
||||
|
||||
@@ -652,6 +664,26 @@ TEST_P(Test_ONNX_layers, Slice)
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_2DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_2d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_3DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_3d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_4DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_4d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_5DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_5d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Softmax)
|
||||
{
|
||||
testONNXModels("softmax");
|
||||
@@ -766,6 +798,8 @@ TEST_P(Test_ONNX_layers, Conv1d_variable_weight_bias)
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
if (target == DNN_TARGET_CPU && getInferenceEngineCPUType() == CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_ARM_CPU, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
String basename = "conv1d_variable_wb";
|
||||
Net net = readNetFromONNX(_tf("models/" + basename + ".onnx"));
|
||||
@@ -789,6 +823,13 @@ TEST_P(Test_ONNX_layers, Conv1d_variable_weight_bias)
|
||||
|
||||
TEST_P(Test_ONNX_layers, GatherMultiOutput)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
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_NGRAPH); // exception
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception
|
||||
#endif
|
||||
|
||||
#if defined(INF_ENGINE_RELEASE)
|
||||
if (target == DNN_TARGET_MYRIAD)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE);
|
||||
@@ -885,6 +926,7 @@ TEST_P(Test_ONNX_layers, PoolConv1d)
|
||||
|
||||
TEST_P(Test_ONNX_layers, ConvResizePool1d)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE)
|
||||
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);
|
||||
@@ -892,7 +934,12 @@ TEST_P(Test_ONNX_layers, ConvResizePool1d)
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
#if INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
if (target == DNN_TARGET_OPENCL) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception
|
||||
if (target == DNN_TARGET_OPENCL_FP16) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
testONNXModels("conv_resize_pool_1d");
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,16 @@ TEST_P(Test_TensorFlow_layers, reduce_sum)
|
||||
runTensorFlowNet("sum_pool_by_axis");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, reduce_sum_channel)
|
||||
{
|
||||
runTensorFlowNet("reduce_sum_channel");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, reduce_sum_channel_keep_dims)
|
||||
{
|
||||
runTensorFlowNet("reduce_sum_channel", false, 0.0, 0.0, false, "_keep_dims");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, conv_single_conv)
|
||||
{
|
||||
runTensorFlowNet("single_conv");
|
||||
@@ -205,6 +215,17 @@ TEST_P(Test_TensorFlow_layers, eltwise)
|
||||
runTensorFlowNet("eltwise_sub");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, eltwise_add_vec)
|
||||
{
|
||||
runTensorFlowNet("eltwise_add_vec");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, eltwise_mul_vec)
|
||||
{
|
||||
runTensorFlowNet("eltwise_mul_vec");
|
||||
}
|
||||
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, channel_broadcast)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
@@ -219,6 +240,12 @@ TEST_P(Test_TensorFlow_layers, pad_and_concat)
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, concat_axis_1)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
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_NGRAPH); // exception
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception
|
||||
#endif
|
||||
runTensorFlowNet("concat_axis_1");
|
||||
}
|
||||
|
||||
@@ -279,6 +306,10 @@ TEST_P(Test_TensorFlow_layers, batch_norm_10)
|
||||
}
|
||||
TEST_P(Test_TensorFlow_layers, batch_norm_11)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021030000)
|
||||
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); // nan
|
||||
#endif
|
||||
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);
|
||||
runTensorFlowNet("mvn_batch_norm_1x1");
|
||||
@@ -478,6 +509,21 @@ TEST_P(Test_TensorFlow_layers, unfused_flatten)
|
||||
runTensorFlowNet("unfused_flatten_unknown_batch");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, reshape_layer)
|
||||
{
|
||||
runTensorFlowNet("reshape_layer");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, reshape_nchw)
|
||||
{
|
||||
runTensorFlowNet("reshape_nchw");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, reshape_conv)
|
||||
{
|
||||
runTensorFlowNet("reshape_conv");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, leaky_relu)
|
||||
{
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2018050000)
|
||||
@@ -1073,12 +1119,20 @@ TEST_P(Test_TensorFlow_layers, keras_mobilenet_head)
|
||||
// TF case: align_corners=False, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear)
|
||||
{
|
||||
#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
|
||||
runTensorFlowNet("resize_bilinear");
|
||||
}
|
||||
|
||||
// TF case: align_corners=True, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_align_corners)
|
||||
{
|
||||
#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
|
||||
runTensorFlowNet("resize_bilinear",
|
||||
false, 0.0, 0.0, false, // default parameters
|
||||
"_align_corners");
|
||||
|
||||
@@ -258,6 +258,14 @@ TEST_P(Test_Torch_layers, net_conv_gemm_lrn)
|
||||
l1 = 0.0042;
|
||||
lInf = 0.021;
|
||||
}
|
||||
// The OpenCL kernels use the native_ math functions which have
|
||||
// implementation defined accuracy, so we use relaxed thresholds. See
|
||||
// https://github.com/opencv/opencv/issues/9821 for more details.
|
||||
else if (target == DNN_TARGET_OPENCL)
|
||||
{
|
||||
l1 = 0.02;
|
||||
lInf = 0.02;
|
||||
}
|
||||
runTorchNet("net_conv_gemm_lrn", "", false, true, true, l1, lInf);
|
||||
}
|
||||
|
||||
@@ -282,6 +290,15 @@ TEST_P(Test_Torch_layers, net_padding)
|
||||
|
||||
TEST_P(Test_Torch_layers, net_non_spatial)
|
||||
{
|
||||
#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); // crash
|
||||
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_NGRAPH); // exception
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); // exception
|
||||
#endif
|
||||
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 &&
|
||||
(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,
|
||||
@@ -592,6 +609,11 @@ private:
|
||||
|
||||
TEST_P(Test_Torch_layers, upsampling_nearest)
|
||||
{
|
||||
#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); // TODO
|
||||
#endif
|
||||
|
||||
// Test a custom layer.
|
||||
CV_DNN_REGISTER_LAYER_CLASS(SpatialUpSamplingNearest, SpatialUpSamplingNearestLayer);
|
||||
try
|
||||
|
||||
@@ -481,8 +481,7 @@ article](http://en.wikipedia.org/wiki/Maximally_stable_extremal_regions)).
|
||||
than union-find method; it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop.
|
||||
|
||||
- the color image algorithm is taken from: @cite forssen2007maximally ; it should be much slower
|
||||
than grey image method ( 3~4 times ); the chi_table.h file is taken directly from paper's source
|
||||
code which is distributed under GPL.
|
||||
than grey image method ( 3~4 times )
|
||||
|
||||
- (Python) A complete example showing the use of the %MSER detector can be found at samples/python/mser.py
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
* it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop.
|
||||
* 3. the color image algorithm is taken from: Maximally Stable Colour Regions for Recognition and Match;
|
||||
* it should be much slower than gray image method ( 3~4 times );
|
||||
* the chi_table.h file is taken directly from paper's source code which is distributed under GPL.
|
||||
* the chi_table.h file is taken directly from paper's source code which is distributed under permissive BSD-like license: http://users.isy.liu.se/cvl/perfo/software/chi_table.h
|
||||
* 4. though the name is *contours*, the result actually is a list of point set.
|
||||
*/
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ ocv_add_module(gapi
|
||||
REQUIRED
|
||||
opencv_imgproc
|
||||
OPTIONAL
|
||||
opencv_video
|
||||
opencv_video opencv_calib3d
|
||||
WRAP
|
||||
python
|
||||
)
|
||||
@@ -53,6 +53,7 @@ file(GLOB gapi_ext_hdrs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/*.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/util/*.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/python/*.hpp"
|
||||
)
|
||||
|
||||
set(gapi_srcs
|
||||
@@ -76,6 +77,7 @@ set(gapi_srcs
|
||||
src/api/kernels_video.cpp
|
||||
src/api/kernels_nnparsers.cpp
|
||||
src/api/kernels_streaming.cpp
|
||||
src/api/kernels_stereo.cpp
|
||||
src/api/render.cpp
|
||||
src/api/render_ocv.cpp
|
||||
src/api/ginfer.cpp
|
||||
@@ -111,6 +113,7 @@ set(gapi_srcs
|
||||
src/backends/cpu/gcpubackend.cpp
|
||||
src/backends/cpu/gcpukernel.cpp
|
||||
src/backends/cpu/gcpuimgproc.cpp
|
||||
src/backends/cpu/gcpustereo.cpp
|
||||
src/backends/cpu/gcpuvideo.cpp
|
||||
src/backends/cpu/gcpucore.cpp
|
||||
src/backends/cpu/gnnparsers.cpp
|
||||
@@ -158,6 +161,7 @@ set(gapi_srcs
|
||||
|
||||
# Python bridge
|
||||
src/backends/ie/bindings_ie.cpp
|
||||
src/backends/python/gpythonbackend.cpp
|
||||
)
|
||||
|
||||
ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2)
|
||||
|
||||
@@ -20,12 +20,26 @@ endif()
|
||||
set(ADE_root "${ade_src_dir}/${ade_subdir}/sources/ade")
|
||||
file(GLOB_RECURSE ADE_sources "${ADE_root}/source/*.cpp")
|
||||
file(GLOB_RECURSE ADE_include "${ADE_root}/include/ade/*.hpp")
|
||||
add_library(ade STATIC ${ADE_include} ${ADE_sources})
|
||||
add_library(ade STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL}
|
||||
${ADE_include}
|
||||
${ADE_sources}
|
||||
)
|
||||
target_include_directories(ade PUBLIC $<BUILD_INTERFACE:${ADE_root}/include>)
|
||||
set_target_properties(ade PROPERTIES POSITION_INDEPENDENT_CODE True)
|
||||
set_target_properties(ade PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE True
|
||||
OUTPUT_NAME ade
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
COMPILE_PDB_NAME ade
|
||||
COMPILE_PDB_NAME_DEBUG "ade${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(ade PROPERTIES FOLDER "3rdparty")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(ade EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
||||
ocv_install_target(ade EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||
endif()
|
||||
|
||||
ocv_install_3rdparty_licenses(ade "${ade_src_dir}/${ade_subdir}/LICENSE")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user