From c6b11b463f428e1da89d182e26685a7d8c7141e0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 22 Nov 2024 12:26:21 +0300 Subject: [PATCH 1/5] Migrate G-API module from main repo to opencv_contrib. --- modules/gapi/CMakeLists.txt | 440 +++ modules/gapi/cmake/DownloadADE.cmake | 51 + modules/gapi/cmake/init.cmake | 49 + modules/gapi/cmake/standalone.cmake | 62 + modules/gapi/doc/00-root.markdown | 125 + modules/gapi/doc/01-background.markdown | 76 + modules/gapi/doc/10-hld-overview.md | 160 + modules/gapi/doc/20-kernel-api.markdown | 188 + modules/gapi/doc/30-implementation.markdown | 29 + modules/gapi/doc/pics/demo.jpg | Bin 0 -> 65973 bytes modules/gapi/doc/pics/gapi_scheme.png | Bin 0 -> 76918 bytes modules/gapi/doc/pics/render_example.png | Bin 0 -> 4392 bytes modules/gapi/doc/slides/.gitignore | 6 + modules/gapi/doc/slides/README.md | 27 + modules/gapi/doc/slides/gapi_overview.org | 961 +++++ modules/gapi/doc/slides/get_sty.sh | 25 + modules/gapi/doc/slides/ocv_logo.eps | 181 + modules/gapi/include/opencv2/gapi.hpp | 42 + modules/gapi/include/opencv2/gapi/core.hpp | 1911 ++++++++++ .../gapi/include/opencv2/gapi/cpu/core.hpp | 27 + .../include/opencv2/gapi/cpu/gcpukernel.hpp | 542 +++ .../gapi/include/opencv2/gapi/cpu/imgproc.hpp | 27 + modules/gapi/include/opencv2/gapi/cpu/ot.hpp | 29 + .../gapi/include/opencv2/gapi/cpu/stereo.hpp | 48 + .../gapi/include/opencv2/gapi/cpu/video.hpp | 25 + .../gapi/include/opencv2/gapi/fluid/core.hpp | 20 + .../opencv2/gapi/fluid/gfluidbuffer.hpp | 154 + .../opencv2/gapi/fluid/gfluidkernel.hpp | 442 +++ .../include/opencv2/gapi/fluid/imgproc.hpp | 20 + modules/gapi/include/opencv2/gapi/garg.hpp | 311 ++ modules/gapi/include/opencv2/gapi/garray.hpp | 440 +++ .../include/opencv2/gapi/gasync_context.hpp | 63 + modules/gapi/include/opencv2/gapi/gcall.hpp | 78 + modules/gapi/include/opencv2/gapi/gcommon.hpp | 309 ++ .../gapi/include/opencv2/gapi/gcompiled.hpp | 232 ++ .../include/opencv2/gapi/gcompiled_async.hpp | 73 + .../include/opencv2/gapi/gcompoundkernel.hpp | 139 + .../include/opencv2/gapi/gcomputation.hpp | 581 +++ .../opencv2/gapi/gcomputation_async.hpp | 69 + modules/gapi/include/opencv2/gapi/gframe.hpp | 113 + modules/gapi/include/opencv2/gapi/gkernel.hpp | 757 ++++ modules/gapi/include/opencv2/gapi/gmat.hpp | 292 ++ .../gapi/include/opencv2/gapi/gmetaarg.hpp | 80 + modules/gapi/include/opencv2/gapi/gopaque.hpp | 369 ++ modules/gapi/include/opencv2/gapi/gproto.hpp | 159 + .../gapi/include/opencv2/gapi/gpu/core.hpp | 27 + .../include/opencv2/gapi/gpu/ggpukernel.hpp | 18 + .../gapi/include/opencv2/gapi/gpu/imgproc.hpp | 28 + modules/gapi/include/opencv2/gapi/gscalar.hpp | 140 + .../gapi/include/opencv2/gapi/gstreaming.hpp | 430 +++ .../gapi/include/opencv2/gapi/gtransform.hpp | 103 + .../include/opencv2/gapi/gtype_traits.hpp | 242 ++ modules/gapi/include/opencv2/gapi/gtyped.hpp | 246 ++ modules/gapi/include/opencv2/gapi/imgproc.hpp | 1769 +++++++++ modules/gapi/include/opencv2/gapi/infer.hpp | 717 ++++ .../opencv2/gapi/infer/bindings_ie.hpp | 70 + .../opencv2/gapi/infer/bindings_onnx.hpp | 74 + .../opencv2/gapi/infer/bindings_ov.hpp | 128 + .../gapi/include/opencv2/gapi/infer/ie.hpp | 711 ++++ .../gapi/include/opencv2/gapi/infer/onnx.hpp | 759 ++++ .../gapi/include/opencv2/gapi/infer/ov.hpp | 709 ++++ .../include/opencv2/gapi/infer/parsers.hpp | 138 + modules/gapi/include/opencv2/gapi/media.hpp | 258 ++ .../gapi/include/opencv2/gapi/oak/infer.hpp | 66 + modules/gapi/include/opencv2/gapi/oak/oak.hpp | 158 + .../gapi/include/opencv2/gapi/ocl/core.hpp | 27 + .../include/opencv2/gapi/ocl/goclkernel.hpp | 260 ++ .../gapi/include/opencv2/gapi/ocl/imgproc.hpp | 27 + .../include/opencv2/gapi/opencv_includes.hpp | 42 + .../gapi/include/opencv2/gapi/operators.hpp | 70 + modules/gapi/include/opencv2/gapi/ot.hpp | 194 + .../gapi/include/opencv2/gapi/own/assert.hpp | 60 + .../gapi/include/opencv2/gapi/own/convert.hpp | 55 + .../gapi/include/opencv2/gapi/own/cvdefs.hpp | 166 + .../gapi/include/opencv2/gapi/own/exports.hpp | 42 + modules/gapi/include/opencv2/gapi/own/mat.hpp | 354 ++ .../include/opencv2/gapi/own/saturate.hpp | 83 + .../gapi/include/opencv2/gapi/own/scalar.hpp | 47 + .../gapi/include/opencv2/gapi/own/types.hpp | 162 + .../include/opencv2/gapi/plaidml/core.hpp | 20 + .../opencv2/gapi/plaidml/gplaidmlkernel.hpp | 140 + .../include/opencv2/gapi/plaidml/plaidml.hpp | 53 + .../include/opencv2/gapi/python/python.hpp | 71 + modules/gapi/include/opencv2/gapi/render.hpp | 14 + .../include/opencv2/gapi/render/render.hpp | 196 + .../opencv2/gapi/render/render_types.hpp | 359 ++ modules/gapi/include/opencv2/gapi/rmat.hpp | 162 + modules/gapi/include/opencv2/gapi/s11n.hpp | 513 +++ .../gapi/include/opencv2/gapi/s11n/base.hpp | 80 + modules/gapi/include/opencv2/gapi/stereo.hpp | 85 + .../include/opencv2/gapi/streaming/cap.hpp | 149 + .../include/opencv2/gapi/streaming/desync.hpp | 86 + .../include/opencv2/gapi/streaming/format.hpp | 94 + .../streaming/gstreamer/gstreamerpipeline.hpp | 59 + .../streaming/gstreamer/gstreamersource.hpp | 97 + .../include/opencv2/gapi/streaming/meta.hpp | 80 + .../gapi/streaming/onevpl/accel_types.hpp | 76 + .../gapi/streaming/onevpl/cfg_params.hpp | 209 ++ .../onevpl/data_provider_interface.hpp | 105 + .../opencv2/gapi/streaming/onevpl/default.hpp | 29 + .../onevpl/device_selector_interface.hpp | 61 + .../opencv2/gapi/streaming/onevpl/source.hpp | 94 + .../opencv2/gapi/streaming/queue_source.hpp | 67 + .../include/opencv2/gapi/streaming/source.hpp | 67 + .../include/opencv2/gapi/streaming/sync.hpp | 30 + .../gapi/include/opencv2/gapi/util/any.hpp | 190 + .../opencv2/gapi/util/compiler_hints.hpp | 19 + .../opencv2/gapi/util/copy_through_move.hpp | 34 + .../include/opencv2/gapi/util/optional.hpp | 178 + .../gapi/include/opencv2/gapi/util/throw.hpp | 36 + .../include/opencv2/gapi/util/type_traits.hpp | 31 + .../gapi/include/opencv2/gapi/util/util.hpp | 190 + .../include/opencv2/gapi/util/variant.hpp | 667 ++++ modules/gapi/include/opencv2/gapi/video.hpp | 364 ++ .../gapi/misc/python/package/gapi/__init__.py | 323 ++ modules/gapi/misc/python/pyopencv_gapi.hpp | 1172 ++++++ modules/gapi/misc/python/python_bridge.hpp | 353 ++ .../misc/python/samples/gaze_estimation.py | 462 +++ modules/gapi/misc/python/shadow_gapi.hpp | 86 + .../gapi/misc/python/test/test_gapi_core.py | 239 ++ .../misc/python/test/test_gapi_imgproc.py | 127 + .../gapi/misc/python/test/test_gapi_infer.py | 341 ++ .../misc/python/test/test_gapi_infer_onnx.py | 68 + .../misc/python/test/test_gapi_infer_ov.py | 238 ++ .../misc/python/test/test_gapi_kernels.py | 48 + modules/gapi/misc/python/test/test_gapi_ot.py | 58 + .../gapi/misc/python/test/test_gapi_render.py | 227 ++ .../python/test/test_gapi_sample_pipelines.py | 722 ++++ .../python/test/test_gapi_stateful_kernel.py | 216 ++ .../misc/python/test/test_gapi_streaming.py | 559 +++ .../gapi/misc/python/test/test_gapi_types.py | 55 + .../gapi/perf/common/gapi_core_perf_tests.cpp | 9 + .../gapi/perf/common/gapi_core_perf_tests.hpp | 87 + .../perf/common/gapi_core_perf_tests_inl.hpp | 2502 +++++++++++++ .../perf/common/gapi_imgproc_perf_tests.cpp | 9 + .../perf/common/gapi_imgproc_perf_tests.hpp | 108 + .../common/gapi_imgproc_perf_tests_inl.hpp | 1947 ++++++++++ .../perf/common/gapi_render_perf_tests.cpp | 9 + .../perf/common/gapi_render_perf_tests.hpp | 43 + .../common/gapi_render_perf_tests_inl.hpp | 827 +++++ .../perf/common/gapi_video_perf_tests.cpp | 9 + .../perf/common/gapi_video_perf_tests.hpp | 40 + .../perf/common/gapi_video_perf_tests_inl.hpp | 397 ++ .../perf/cpu/gapi_core_perf_tests_cpu.cpp | 401 ++ .../perf/cpu/gapi_core_perf_tests_fluid.cpp | 332 ++ .../perf/cpu/gapi_imgproc_perf_tests_cpu.cpp | 438 +++ .../cpu/gapi_imgproc_perf_tests_fluid.cpp | 234 ++ .../perf/cpu/gapi_video_perf_tests_cpu.cpp | 132 + .../perf/gpu/gapi_core_perf_tests_gpu.cpp | 327 ++ .../perf/gpu/gapi_imgproc_perf_tests_gpu.cpp | 224 ++ .../internal/gapi_compiler_perf_tests.cpp | 45 + modules/gapi/perf/perf_bench.cpp | 88 + modules/gapi/perf/perf_main.cpp | 11 + modules/gapi/perf/perf_precomp.hpp | 28 + .../render/gapi_render_perf_tests_ocv.cpp | 95 + .../gapi_streaming_source_perf_tests.cpp | 337 ++ modules/gapi/samples/api_example.cpp | 34 + modules/gapi/samples/data/config_template.yml | 192 + modules/gapi/samples/draw_example.cpp | 56 + modules/gapi/samples/face_detection_mtcnn.cpp | 733 ++++ modules/gapi/samples/gaze_estimation.cpp | 351 ++ modules/gapi/samples/infer_ie_onnx_hybrid.cpp | 201 + modules/gapi/samples/infer_single_roi.cpp | 202 + modules/gapi/samples/infer_ssd_onnx.cpp | 159 + modules/gapi/samples/oak_basic_infer.cpp | 122 + modules/gapi/samples/oak_copy.cpp | 48 + .../gapi/samples/oak_rgb_camera_encoding.cpp | 60 + .../samples/oak_small_hetero_pipeline.cpp | 58 + ...l_infer_with_advanced_device_selection.cpp | 704 ++++ .../samples/onevpl_source_to_bgr_conv.cpp | 106 + .../gapi/samples/pipeline_modeling_tool.cpp | 541 +++ .../pipeline_modeling_tool/dummy_source.hpp | 112 + .../pipeline_modeling_tool/pipeline.hpp | 250 ++ .../pipeline_builder.hpp | 692 ++++ .../test_pipeline_modeling_tool.py | 1003 +++++ .../samples/pipeline_modeling_tool/utils.hpp | 144 + .../gapi/samples/privacy_masking_camera.cpp | 167 + .../gapi/samples/semantic_segmentation.cpp | 284 ++ modules/gapi/samples/slides_blur_gapi.cpp | 19 + modules/gapi/samples/slides_sobel_cv.cpp | 27 + modules/gapi/samples/slides_sobel_gapi.cpp | 28 + modules/gapi/samples/text_detection.cpp | 698 ++++ .../age_gender_emotion_recognition.cpp | 410 +++ .../doc_snippets/api_ref_snippets.cpp | 253 ++ .../doc_snippets/dynamic_graph_snippets.cpp | 68 + .../doc_snippets/kernel_api_snippets.cpp | 157 + .../face_beautification.cpp | 905 +++++ ...ng_anisotropic_image_segmentation_gapi.cpp | 107 + ...sotropic_image_segmentation_gapi_fluid.cpp | 127 + .../security_barrier_camera.cpp | 351 ++ .../gapi/scripts/measure_privacy_masking.py | 99 + modules/gapi/src/3rdparty/vasot/LICENSE.txt | 21 + .../src/3rdparty/vasot/include/vas/common.hpp | 83 + .../src/3rdparty/vasot/include/vas/ot.hpp | 440 +++ .../3rdparty/vasot/src/common/exception.hpp | 24 + .../src/3rdparty/vasot/src/common/prof.hpp | 144 + .../kalman_filter/kalman_filter_no_opencv.cpp | 292 ++ .../kalman_filter/kalman_filter_no_opencv.hpp | 91 + .../src/components/ot/mtt/hungarian_wrap.cpp | 325 ++ .../src/components/ot/mtt/hungarian_wrap.hpp | 71 + .../components/ot/mtt/objects_associator.cpp | 183 + .../components/ot/mtt/objects_associator.hpp | 41 + .../src/components/ot/mtt/rgb_histogram.cpp | 126 + .../src/components/ot/mtt/rgb_histogram.hpp | 42 + .../src/components/ot/object_tracker.cpp | 363 ++ .../vasot/src/components/ot/prof_def.hpp | 30 + .../ot/short_term_imageless_tracker.cpp | 256 ++ .../ot/short_term_imageless_tracker.hpp | 39 + .../vasot/src/components/ot/tracker.cpp | 132 + .../vasot/src/components/ot/tracker.hpp | 123 + .../vasot/src/components/ot/tracklet.cpp | 147 + .../vasot/src/components/ot/tracklet.hpp | 94 + .../ot/zero_term_imageless_tracker.cpp | 185 + .../ot/zero_term_imageless_tracker.hpp | 37 + modules/gapi/src/api/README.md | 1 + modules/gapi/src/api/garray.cpp | 55 + modules/gapi/src/api/gbackend.cpp | 421 +++ modules/gapi/src/api/gbackend_priv.hpp | 98 + modules/gapi/src/api/gcall.cpp | 95 + modules/gapi/src/api/gcall_priv.hpp | 56 + modules/gapi/src/api/gcommon.cpp | 18 + modules/gapi/src/api/gcomputation.cpp | 356 ++ modules/gapi/src/api/gcomputation_priv.hpp | 54 + modules/gapi/src/api/gframe.cpp | 54 + modules/gapi/src/api/ginfer.cpp | 26 + modules/gapi/src/api/gkernel.cpp | 121 + modules/gapi/src/api/gmat.cpp | 194 + modules/gapi/src/api/gnode.cpp | 89 + modules/gapi/src/api/gnode.hpp | 58 + modules/gapi/src/api/gnode_priv.hpp | 52 + modules/gapi/src/api/gopaque.cpp | 50 + modules/gapi/src/api/gorigin.cpp | 48 + modules/gapi/src/api/gorigin.hpp | 58 + modules/gapi/src/api/gproto.cpp | 347 ++ modules/gapi/src/api/gproto_priv.hpp | 44 + modules/gapi/src/api/grunarg.cpp | 78 + modules/gapi/src/api/gscalar.cpp | 62 + modules/gapi/src/api/kernels_core.cpp | 432 +++ modules/gapi/src/api/kernels_imgproc.cpp | 341 ++ modules/gapi/src/api/kernels_nnparsers.cpp | 44 + modules/gapi/src/api/kernels_ot.cpp | 43 + modules/gapi/src/api/kernels_stereo.cpp | 18 + modules/gapi/src/api/kernels_streaming.cpp | 93 + modules/gapi/src/api/kernels_video.cpp | 119 + modules/gapi/src/api/media.cpp | 61 + modules/gapi/src/api/ocv_mask_creator.hpp | 0 modules/gapi/src/api/operators.cpp | 216 ++ modules/gapi/src/api/render.cpp | 91 + modules/gapi/src/api/render_ocv.cpp | 263 ++ modules/gapi/src/api/render_ocv.hpp | 26 + modules/gapi/src/api/render_priv.hpp | 31 + modules/gapi/src/api/rmat.cpp | 75 + modules/gapi/src/api/s11n.cpp | 145 + modules/gapi/src/backends/README.md | 2 + modules/gapi/src/backends/common/gbackend.hpp | 240 ++ .../src/backends/common/gcompoundbackend.cpp | 20 + .../src/backends/common/gcompoundkernel.cpp | 51 + .../gapi/src/backends/common/gmetabackend.cpp | 118 + .../gapi/src/backends/common/gmetabackend.hpp | 16 + .../src/backends/common/serialization.cpp | 979 +++++ .../src/backends/common/serialization.hpp | 240 ++ modules/gapi/src/backends/cpu/gcpubackend.cpp | 322 ++ modules/gapi/src/backends/cpu/gcpubackend.hpp | 83 + modules/gapi/src/backends/cpu/gcpucore.cpp | 780 ++++ modules/gapi/src/backends/cpu/gcpuimgproc.cpp | 703 ++++ modules/gapi/src/backends/cpu/gcpukernel.cpp | 56 + modules/gapi/src/backends/cpu/gcpuot.cpp | 163 + modules/gapi/src/backends/cpu/gcpustereo.cpp | 85 + modules/gapi/src/backends/cpu/gcpuvideo.cpp | 197 + modules/gapi/src/backends/cpu/gnnparsers.cpp | 333 ++ modules/gapi/src/backends/cpu/gnnparsers.hpp | 31 + .../gapi/src/backends/fluid/gfluidbackend.cpp | 1641 +++++++++ .../gapi/src/backends/fluid/gfluidbackend.hpp | 189 + .../gapi/src/backends/fluid/gfluidbuffer.cpp | 752 ++++ .../src/backends/fluid/gfluidbuffer_priv.hpp | 298 ++ .../gapi/src/backends/fluid/gfluidcore.cpp | 2657 ++++++++++++++ .../fluid/gfluidcore_func.dispatch.cpp | 407 +++ .../src/backends/fluid/gfluidcore_func.hpp | 322 ++ .../backends/fluid/gfluidcore_func.simd.hpp | 3165 ++++++++++++++++ .../backends/fluid/gfluidcore_simd_sse41.hpp | 561 +++ .../gapi/src/backends/fluid/gfluidimgproc.cpp | 2289 ++++++++++++ .../fluid/gfluidimgproc_func.dispatch.cpp | 216 ++ .../src/backends/fluid/gfluidimgproc_func.hpp | 162 + .../fluid/gfluidimgproc_func.simd.hpp | 2913 +++++++++++++++ .../fluid/gfluidimgproc_simd_avx2.hpp | 181 + .../gapi/src/backends/fluid/gfluidutils.hpp | 110 + modules/gapi/src/backends/ie/bindings_ie.cpp | 57 + modules/gapi/src/backends/ie/giebackend.cpp | 2340 ++++++++++++ modules/gapi/src/backends/ie/giebackend.hpp | 81 + .../src/backends/ie/giebackend/giewrapper.cpp | 156 + .../src/backends/ie/giebackend/giewrapper.hpp | 78 + modules/gapi/src/backends/ie/util.hpp | 37 + modules/gapi/src/backends/oak/goak.cpp | 58 + .../src/backends/oak/goak_memory_adapters.cpp | 54 + modules/gapi/src/backends/oak/goakbackend.cpp | 1132 ++++++ .../src/backends/oak/oak_memory_adapters.hpp | 57 + modules/gapi/src/backends/ocl/goclbackend.cpp | 292 ++ modules/gapi/src/backends/ocl/goclbackend.hpp | 71 + modules/gapi/src/backends/ocl/goclcore.cpp | 687 ++++ modules/gapi/src/backends/ocl/goclcore.hpp | 24 + modules/gapi/src/backends/ocl/goclimgproc.cpp | 305 ++ modules/gapi/src/backends/ocl/goclimgproc.hpp | 23 + modules/gapi/src/backends/ocl/goclkernel.cpp | 55 + .../gapi/src/backends/onnx/bindings_onnx.cpp | 85 + modules/gapi/src/backends/onnx/coreml_ep.cpp | 50 + modules/gapi/src/backends/onnx/coreml_ep.hpp | 23 + modules/gapi/src/backends/onnx/dml_ep.cpp | 277 ++ modules/gapi/src/backends/onnx/dml_ep.hpp | 23 + .../gapi/src/backends/onnx/gonnxbackend.cpp | 1372 +++++++ .../gapi/src/backends/onnx/gonnxbackend.hpp | 56 + modules/gapi/src/backends/ov/bindings_ov.cpp | 168 + modules/gapi/src/backends/ov/govbackend.cpp | 1653 +++++++++ modules/gapi/src/backends/ov/govbackend.hpp | 76 + modules/gapi/src/backends/ov/util.hpp | 41 + .../src/backends/plaidml/gplaidmlbackend.cpp | 294 ++ .../src/backends/plaidml/gplaidmlbackend.hpp | 100 + .../src/backends/plaidml/gplaidmlcore.cpp | 64 + .../src/backends/plaidml/plaidml_util.hpp | 42 + .../src/backends/python/gpythonbackend.cpp | 283 ++ .../gapi/src/backends/render/ft_render.cpp | 236 ++ .../gapi/src/backends/render/ft_render.hpp | 44 + .../src/backends/render/ft_render_priv.hpp | 48 + .../gapi/src/backends/render/grenderocv.cpp | 200 + .../backends/streaming/gstreamingbackend.cpp | 472 +++ .../backends/streaming/gstreamingbackend.hpp | 38 + .../backends/streaming/gstreamingkernel.hpp | 39 + modules/gapi/src/compiler/README.md | 1 + modules/gapi/src/compiler/gcompiled.cpp | 175 + modules/gapi/src/compiler/gcompiled_priv.hpp | 62 + modules/gapi/src/compiler/gcompiler.cpp | 569 +++ modules/gapi/src/compiler/gcompiler.hpp | 67 + modules/gapi/src/compiler/gislandmodel.cpp | 451 +++ modules/gapi/src/compiler/gislandmodel.hpp | 310 ++ modules/gapi/src/compiler/gmodel.cpp | 289 ++ modules/gapi/src/compiler/gmodel.hpp | 368 ++ modules/gapi/src/compiler/gmodel_priv.hpp | 59 + modules/gapi/src/compiler/gmodelbuilder.cpp | 323 ++ modules/gapi/src/compiler/gmodelbuilder.hpp | 78 + modules/gapi/src/compiler/gobjref.hpp | 59 + modules/gapi/src/compiler/gstreaming.cpp | 174 + modules/gapi/src/compiler/gstreaming_priv.hpp | 64 + modules/gapi/src/compiler/passes/dump_dot.cpp | 246 ++ modules/gapi/src/compiler/passes/exec.cpp | 731 ++++ modules/gapi/src/compiler/passes/helpers.cpp | 122 + modules/gapi/src/compiler/passes/helpers.hpp | 31 + modules/gapi/src/compiler/passes/intrin.cpp | 305 ++ modules/gapi/src/compiler/passes/islands.cpp | 233 ++ modules/gapi/src/compiler/passes/kernels.cpp | 259 ++ modules/gapi/src/compiler/passes/meta.cpp | 135 + modules/gapi/src/compiler/passes/passes.hpp | 80 + .../src/compiler/passes/pattern_matching.cpp | 580 +++ .../src/compiler/passes/pattern_matching.hpp | 102 + .../compiler/passes/perform_substitution.cpp | 94 + .../gapi/src/compiler/passes/streaming.cpp | 84 + .../src/compiler/passes/transformations.cpp | 140 + modules/gapi/src/compiler/transactions.hpp | 200 + modules/gapi/src/executor/conc_queue.hpp | 128 + .../gapi/src/executor/gabstractexecutor.cpp | 25 + .../gapi/src/executor/gabstractexecutor.hpp | 80 + .../executor/gabstractstreamingexecutor.cpp | 23 + .../executor/gabstractstreamingexecutor.hpp | 49 + modules/gapi/src/executor/gasync.cpp | 280 ++ modules/gapi/src/executor/gexecutor.cpp | 463 +++ modules/gapi/src/executor/gexecutor.hpp | 59 + .../gapi/src/executor/gstreamingexecutor.cpp | 1948 ++++++++++ .../gapi/src/executor/gstreamingexecutor.hpp | 209 ++ modules/gapi/src/executor/gtbbexecutor.cpp | 437 +++ modules/gapi/src/executor/gtbbexecutor.hpp | 111 + .../gapi/src/executor/gthreadedexecutor.cpp | 515 +++ .../gapi/src/executor/gthreadedexecutor.hpp | 123 + modules/gapi/src/executor/last_value.hpp | 105 + modules/gapi/src/executor/thread_pool.cpp | 67 + modules/gapi/src/executor/thread_pool.hpp | 71 + modules/gapi/src/logger.hpp | 26 + modules/gapi/src/precomp.hpp | 25 + .../gstreamer/gstreamer_buffer_utils.cpp | 27 + .../gstreamer/gstreamer_buffer_utils.hpp | 27 + .../gstreamer/gstreamer_media_adapter.cpp | 181 + .../gstreamer/gstreamer_media_adapter.hpp | 63 + .../gstreamer/gstreamer_pipeline_facade.cpp | 314 ++ .../gstreamer/gstreamer_pipeline_facade.hpp | 89 + .../src/streaming/gstreamer/gstreamerenv.cpp | 90 + .../src/streaming/gstreamer/gstreamerenv.hpp | 37 + .../streaming/gstreamer/gstreamerpipeline.cpp | 112 + .../gstreamer/gstreamerpipeline_priv.hpp | 58 + .../src/streaming/gstreamer/gstreamerptr.hpp | 177 + .../streaming/gstreamer/gstreamersource.cpp | 414 +++ .../gstreamer/gstreamersource_priv.hpp | 95 + .../onevpl/accelerators/accel_policy_cpu.cpp | 298 ++ .../onevpl/accelerators/accel_policy_cpu.hpp | 52 + .../onevpl/accelerators/accel_policy_dx11.cpp | 490 +++ .../onevpl/accelerators/accel_policy_dx11.hpp | 90 + .../accelerators/accel_policy_interface.hpp | 77 + .../accelerators/accel_policy_va_api.cpp | 138 + .../accelerators/accel_policy_va_api.hpp | 61 + .../accelerators/dx11_alloc_resource.cpp | 420 +++ .../accelerators/dx11_alloc_resource.hpp | 150 + .../surface/base_frame_adapter.cpp | 77 + .../surface/base_frame_adapter.hpp | 47 + .../surface/cpu_frame_adapter.cpp | 81 + .../surface/cpu_frame_adapter.hpp | 40 + .../surface/dx11_frame_adapter.cpp | 216 ++ .../surface/dx11_frame_adapter.hpp | 62 + .../onevpl/accelerators/surface/surface.cpp | 81 + .../onevpl/accelerators/surface/surface.hpp | 100 + .../accelerators/surface/surface_pool.cpp | 86 + .../accelerators/surface/surface_pool.hpp | 48 + .../accelerators/utils/elastic_barrier.hpp | 296 ++ .../onevpl/accelerators/utils/shared_lock.cpp | 95 + .../onevpl/accelerators/utils/shared_lock.hpp | 47 + .../onevpl/cfg_param_device_selector.cpp | 475 +++ .../onevpl/cfg_param_device_selector.hpp | 51 + .../gapi/src/streaming/onevpl/cfg_params.cpp | 260 ++ .../streaming/onevpl/cfg_params_parser.cpp | 226 ++ .../streaming/onevpl/cfg_params_parser.hpp | 47 + .../onevpl/data_provider_defines.hpp | 31 + .../onevpl/data_provider_dispatcher.cpp | 69 + .../onevpl/data_provider_dispatcher.hpp | 29 + .../data_provider_interface_exception.cpp | 48 + modules/gapi/src/streaming/onevpl/default.cpp | 51 + .../demux/async_mfp_demux_data_provider.cpp | 813 ++++ .../demux/async_mfp_demux_data_provider.hpp | 126 + .../onevpl/device_selector_interface.cpp | 137 + .../engine/decode/decode_engine_legacy.cpp | 400 ++ .../engine/decode/decode_engine_legacy.hpp | 57 + .../onevpl/engine/decode/decode_session.cpp | 75 + .../onevpl/engine/decode/decode_session.hpp | 62 + .../onevpl/engine/engine_session.cpp | 60 + .../onevpl/engine/engine_session.hpp | 64 + .../engine/preproc/preproc_dispatcher.cpp | 105 + .../engine/preproc/preproc_dispatcher.hpp | 48 + .../onevpl/engine/preproc/preproc_engine.cpp | 473 +++ .../onevpl/engine/preproc/preproc_engine.hpp | 68 + .../onevpl/engine/preproc/preproc_session.cpp | 85 + .../onevpl/engine/preproc/preproc_session.hpp | 73 + .../streaming/onevpl/engine/preproc/utils.cpp | 86 + .../streaming/onevpl/engine/preproc/utils.hpp | 32 + .../engine/preproc/vpp_preproc_defines.hpp | 39 + .../onevpl/engine/preproc_defines.hpp | 88 + .../engine/preproc_engine_interface.cpp | 110 + .../engine/preproc_engine_interface.hpp | 46 + .../onevpl/engine/processing_engine_base.cpp | 110 + .../onevpl/engine/processing_engine_base.hpp | 107 + .../transcode/transcode_engine_legacy.cpp | 469 +++ .../transcode/transcode_engine_legacy.hpp | 45 + .../engine/transcode/transcode_session.cpp | 55 + .../engine/transcode/transcode_session.hpp | 43 + .../streaming/onevpl/file_data_provider.cpp | 152 + .../streaming/onevpl/file_data_provider.hpp | 55 + .../src/streaming/onevpl/onevpl_export.hpp | 30 + modules/gapi/src/streaming/onevpl/source.cpp | 126 + .../gapi/src/streaming/onevpl/source_priv.cpp | 375 ++ .../gapi/src/streaming/onevpl/source_priv.hpp | 83 + modules/gapi/src/streaming/onevpl/utils.cpp | 433 +++ modules/gapi/src/streaming/onevpl/utils.hpp | 91 + modules/gapi/src/streaming/queue_source.cpp | 98 + modules/gapi/src/utils/itt.cpp | 17 + modules/gapi/src/utils/itt.hpp | 78 + .../test/common/gapi_compoundkernel_tests.cpp | 592 +++ modules/gapi/test/common/gapi_core_tests.cpp | 9 + modules/gapi/test/common/gapi_core_tests.hpp | 165 + .../test/common/gapi_core_tests_common.hpp | 180 + .../gapi/test/common/gapi_core_tests_inl.hpp | 1774 +++++++++ .../gapi/test/common/gapi_imgproc_tests.cpp | 9 + .../gapi/test/common/gapi_imgproc_tests.hpp | 130 + .../test/common/gapi_imgproc_tests_common.hpp | 197 + .../test/common/gapi_imgproc_tests_inl.hpp | 1145 ++++++ .../gapi/test/common/gapi_operators_tests.cpp | 9 + .../gapi/test/common/gapi_operators_tests.hpp | 245 ++ .../test/common/gapi_operators_tests_inl.hpp | 167 + .../test/common/gapi_parsers_tests_common.hpp | 411 +++ .../gapi/test/common/gapi_render_tests.cpp | 97 + .../gapi/test/common/gapi_render_tests.hpp | 147 + .../gapi/test/common/gapi_stereo_tests.cpp | 8 + .../gapi/test/common/gapi_stereo_tests.hpp | 26 + .../test/common/gapi_stereo_tests_inl.hpp | 74 + .../common/gapi_streaming_tests_common.hpp | 86 + .../gapi/test/common/gapi_tests_common.hpp | 1185 ++++++ .../gapi/test/common/gapi_tests_helpers.hpp | 102 + modules/gapi/test/common/gapi_video_tests.cpp | 9 + modules/gapi/test/common/gapi_video_tests.hpp | 44 + .../test/common/gapi_video_tests_common.hpp | 491 +++ .../gapi/test/common/gapi_video_tests_inl.hpp | 321 ++ modules/gapi/test/cpu/gapi_core_tests_cpu.cpp | 510 +++ .../gapi/test/cpu/gapi_core_tests_fluid.cpp | 318 ++ .../gapi/test/cpu/gapi_imgproc_tests_cpu.cpp | 563 +++ .../test/cpu/gapi_imgproc_tests_fluid.cpp | 221 ++ .../gapi_ocv_stateful_kernel_test_utils.hpp | 50 + .../cpu/gapi_ocv_stateful_kernel_tests.cpp | 686 ++++ .../test/cpu/gapi_operators_tests_cpu.cpp | 64 + .../test/cpu/gapi_operators_tests_fluid.cpp | 70 + modules/gapi/test/cpu/gapi_ot_tests_cpu.cpp | 287 ++ .../gapi/test/cpu/gapi_stereo_tests_cpu.cpp | 39 + .../gapi/test/cpu/gapi_video_tests_cpu.cpp | 138 + .../executor/gtbbexecutor_internal_tests.cpp | 178 + modules/gapi/test/gapi_array_tests.cpp | 328 ++ modules/gapi/test/gapi_async_test.cpp | 525 +++ modules/gapi/test/gapi_basic_hetero_tests.cpp | 312 ++ modules/gapi/test/gapi_compile_args_tests.cpp | 75 + modules/gapi/test/gapi_desc_tests.cpp | 404 ++ .../test/gapi_fluid_parallel_rois_test.cpp | 315 ++ modules/gapi/test/gapi_fluid_resize_test.cpp | 871 +++++ modules/gapi/test/gapi_fluid_roi_test.cpp | 197 + modules/gapi/test/gapi_fluid_test.cpp | 913 +++++ modules/gapi/test/gapi_fluid_test_kernels.cpp | 627 ++++ modules/gapi/test/gapi_fluid_test_kernels.hpp | 138 + modules/gapi/test/gapi_frame_tests.cpp | 264 ++ modules/gapi/test/gapi_gcompiled_tests.cpp | 232 ++ modules/gapi/test/gapi_gcomputation_tests.cpp | 196 + modules/gapi/test/gapi_gpu_test.cpp | 207 ++ modules/gapi/test/gapi_graph_meta_tests.cpp | 187 + modules/gapi/test/gapi_kernel_tests.cpp | 549 +++ modules/gapi/test/gapi_mat_tests.cpp | 114 + modules/gapi/test/gapi_mock_kernels.hpp | 123 + modules/gapi/test/gapi_opaque_tests.cpp | 328 ++ modules/gapi/test/gapi_plaidml_pipelines.cpp | 185 + modules/gapi/test/gapi_planar_test.cpp | 205 ++ modules/gapi/test/gapi_sample_pipelines.cpp | 575 +++ modules/gapi/test/gapi_scalar_tests.cpp | 117 + modules/gapi/test/gapi_smoke_test.cpp | 97 + modules/gapi/test/gapi_transform_tests.cpp | 350 ++ modules/gapi/test/gapi_typed_tests.cpp | 268 ++ modules/gapi/test/gapi_util_tests.cpp | 75 + modules/gapi/test/gpu/gapi_core_tests_gpu.cpp | 348 ++ .../gapi/test/gpu/gapi_imgproc_tests_gpu.cpp | 264 ++ .../test/gpu/gapi_operators_tests_gpu.cpp | 62 + .../gapi/test/infer/gapi_infer_ie_test.cpp | 3256 +++++++++++++++++ .../gapi/test/infer/gapi_infer_onnx_test.cpp | 1126 ++++++ .../gapi/test/infer/gapi_infer_ov_tests.cpp | 1007 +++++ modules/gapi/test/infer/gapi_infer_tests.cpp | 79 + .../test/internal/gapi_int_backend_tests.cpp | 86 + .../test/internal/gapi_int_dynamic_graph.cpp | 331 ++ .../test/internal/gapi_int_executor_tests.cpp | 324 ++ .../gapi/test/internal/gapi_int_garg_test.cpp | 118 + .../test/internal/gapi_int_gmetaarg_test.cpp | 193 + .../internal/gapi_int_gmodel_builder_test.cpp | 380 ++ .../internal/gapi_int_island_fusion_tests.cpp | 588 +++ .../test/internal/gapi_int_island_tests.cpp | 654 ++++ .../gapi_int_pattern_matching_test.cpp | 997 +++++ .../gapi_int_perform_substitution_test.cpp | 649 ++++ .../test/internal/gapi_int_proto_tests.cpp | 34 + .../internal/gapi_int_recompilation_test.cpp | 236 ++ .../test/internal/gapi_int_vectorref_test.cpp | 207 ++ .../test/internal/gapi_transactions_test.cpp | 369 ++ modules/gapi/test/oak/gapi_tests_oak.cpp | 26 + .../gapi/test/opencl_kernels_test_gapi.hpp | 260 ++ modules/gapi/test/own/conc_queue_tests.cpp | 197 + modules/gapi/test/own/gapi_types_tests.cpp | 175 + .../test/own/last_written_value_tests.cpp | 156 + modules/gapi/test/own/mat_tests.cpp | 641 ++++ modules/gapi/test/own/scalar_tests.cpp | 44 + modules/gapi/test/own/thread_pool_tests.cpp | 124 + modules/gapi/test/render/ftp_render_test.cpp | 73 + .../test/render/gapi_render_tests_ocv.cpp | 874 +++++ .../gapi/test/rmat/rmat_integration_tests.cpp | 170 + modules/gapi/test/rmat/rmat_test_common.hpp | 69 + modules/gapi/test/rmat/rmat_tests.cpp | 126 + modules/gapi/test/rmat/rmat_view_tests.cpp | 284 ++ modules/gapi/test/s11n/gapi_s11n_tests.cpp | 949 +++++ .../test/s11n/gapi_sample_pipelines_s11n.cpp | 838 +++++ ...pi_gstreamer_pipeline_facade_int_tests.cpp | 188 + .../streaming/gapi_gstreamersource_tests.cpp | 563 +++ .../gapi_streaming_queue_source_tests.cpp | 127 + .../streaming/gapi_streaming_sync_tests.cpp | 220 ++ .../test/streaming/gapi_streaming_tests.cpp | 2686 ++++++++++++++ .../streaming/gapi_streaming_utils_test.cpp | 349 ++ .../gapi_streaming_vpl_core_test.cpp | 1279 +++++++ .../gapi_streaming_vpl_data_provider.cpp | 304 ++ .../gapi_streaming_vpl_device_selector.cpp | 359 ++ .../gapi_streaming_vpp_preproc_test.cpp | 736 ++++ modules/gapi/test/test_main.cpp | 12 + modules/gapi/test/test_precomp.hpp | 43 + modules/gapi/test/util/any_tests.cpp | 142 + modules/gapi/test/util/optional_tests.cpp | 175 + modules/gapi/test/util/variant_tests.cpp | 739 ++++ .../pics/massif_export_gapi.png | Bin 0 -> 85388 bytes .../pics/massif_export_gapi_fluid.png | Bin 0 -> 86636 bytes .../pics/massif_export_ocv.png | Bin 0 -> 76635 bytes .../anisotropic_segmentation/pics/result.jpg | Bin 0 -> 35271 bytes .../anisotropic_segmentation/pics/segm.gif | Bin 0 -> 187780 bytes .../pics/segm_fluid.gif | Bin 0 -> 225566 bytes .../porting_anisotropic_segmentation.markdown | 404 ++ .../face_beautification.markdown | 442 +++ .../face_beautification/pics/example.jpg | Bin 0 -> 176525 bytes modules/gapi/tutorials/gapi.markdown | 53 + .../interactive_face_detection.markdown | 355 ++ .../oak_devices/oak_devices.markdown | 26 + .../gapi/tutorials/oak_devices/pics/oak.jpg | Bin 0 -> 163279 bytes 588 files changed, 153065 insertions(+) create mode 100644 modules/gapi/CMakeLists.txt create mode 100644 modules/gapi/cmake/DownloadADE.cmake create mode 100644 modules/gapi/cmake/init.cmake create mode 100644 modules/gapi/cmake/standalone.cmake create mode 100644 modules/gapi/doc/00-root.markdown create mode 100644 modules/gapi/doc/01-background.markdown create mode 100644 modules/gapi/doc/10-hld-overview.md create mode 100644 modules/gapi/doc/20-kernel-api.markdown create mode 100644 modules/gapi/doc/30-implementation.markdown create mode 100644 modules/gapi/doc/pics/demo.jpg create mode 100644 modules/gapi/doc/pics/gapi_scheme.png create mode 100644 modules/gapi/doc/pics/render_example.png create mode 100644 modules/gapi/doc/slides/.gitignore create mode 100644 modules/gapi/doc/slides/README.md create mode 100644 modules/gapi/doc/slides/gapi_overview.org create mode 100755 modules/gapi/doc/slides/get_sty.sh create mode 100644 modules/gapi/doc/slides/ocv_logo.eps create mode 100644 modules/gapi/include/opencv2/gapi.hpp create mode 100644 modules/gapi/include/opencv2/gapi/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/ot.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/stereo.hpp create mode 100644 modules/gapi/include/opencv2/gapi/cpu/video.hpp create mode 100644 modules/gapi/include/opencv2/gapi/fluid/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp create mode 100644 modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp create mode 100644 modules/gapi/include/opencv2/gapi/garg.hpp create mode 100644 modules/gapi/include/opencv2/gapi/garray.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gasync_context.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcall.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcommon.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcompiled.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcompiled_async.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcomputation.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gcomputation_async.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gframe.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gkernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gmat.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gmetaarg.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gopaque.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gproto.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gpu/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gscalar.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gstreaming.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gtransform.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gtype_traits.hpp create mode 100644 modules/gapi/include/opencv2/gapi/gtyped.hpp create mode 100644 modules/gapi/include/opencv2/gapi/imgproc.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/ie.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/onnx.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/ov.hpp create mode 100644 modules/gapi/include/opencv2/gapi/infer/parsers.hpp create mode 100644 modules/gapi/include/opencv2/gapi/media.hpp create mode 100644 modules/gapi/include/opencv2/gapi/oak/infer.hpp create mode 100644 modules/gapi/include/opencv2/gapi/oak/oak.hpp create mode 100644 modules/gapi/include/opencv2/gapi/ocl/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/ocl/imgproc.hpp create mode 100644 modules/gapi/include/opencv2/gapi/opencv_includes.hpp create mode 100644 modules/gapi/include/opencv2/gapi/operators.hpp create mode 100644 modules/gapi/include/opencv2/gapi/ot.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/assert.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/convert.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/cvdefs.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/exports.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/mat.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/saturate.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/scalar.hpp create mode 100644 modules/gapi/include/opencv2/gapi/own/types.hpp create mode 100644 modules/gapi/include/opencv2/gapi/plaidml/core.hpp create mode 100644 modules/gapi/include/opencv2/gapi/plaidml/gplaidmlkernel.hpp create mode 100644 modules/gapi/include/opencv2/gapi/plaidml/plaidml.hpp create mode 100644 modules/gapi/include/opencv2/gapi/python/python.hpp create mode 100644 modules/gapi/include/opencv2/gapi/render.hpp create mode 100644 modules/gapi/include/opencv2/gapi/render/render.hpp create mode 100644 modules/gapi/include/opencv2/gapi/render/render_types.hpp create mode 100644 modules/gapi/include/opencv2/gapi/rmat.hpp create mode 100644 modules/gapi/include/opencv2/gapi/s11n.hpp create mode 100644 modules/gapi/include/opencv2/gapi/s11n/base.hpp create mode 100644 modules/gapi/include/opencv2/gapi/stereo.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/cap.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/desync.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/format.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamerpipeline.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamersource.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/meta.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/accel_types.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/data_provider_interface.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/device_selector_interface.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/onevpl/source.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/queue_source.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/source.hpp create mode 100644 modules/gapi/include/opencv2/gapi/streaming/sync.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/any.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/copy_through_move.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/optional.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/throw.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/type_traits.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/util.hpp create mode 100644 modules/gapi/include/opencv2/gapi/util/variant.hpp create mode 100644 modules/gapi/include/opencv2/gapi/video.hpp create mode 100644 modules/gapi/misc/python/package/gapi/__init__.py create mode 100644 modules/gapi/misc/python/pyopencv_gapi.hpp create mode 100644 modules/gapi/misc/python/python_bridge.hpp create mode 100644 modules/gapi/misc/python/samples/gaze_estimation.py create mode 100644 modules/gapi/misc/python/shadow_gapi.hpp create mode 100644 modules/gapi/misc/python/test/test_gapi_core.py create mode 100644 modules/gapi/misc/python/test/test_gapi_imgproc.py create mode 100644 modules/gapi/misc/python/test/test_gapi_infer.py create mode 100644 modules/gapi/misc/python/test/test_gapi_infer_onnx.py create mode 100644 modules/gapi/misc/python/test/test_gapi_infer_ov.py create mode 100644 modules/gapi/misc/python/test/test_gapi_kernels.py create mode 100644 modules/gapi/misc/python/test/test_gapi_ot.py create mode 100644 modules/gapi/misc/python/test/test_gapi_render.py create mode 100644 modules/gapi/misc/python/test/test_gapi_sample_pipelines.py create mode 100644 modules/gapi/misc/python/test/test_gapi_stateful_kernel.py create mode 100644 modules/gapi/misc/python/test/test_gapi_streaming.py create mode 100644 modules/gapi/misc/python/test/test_gapi_types.py create mode 100644 modules/gapi/perf/common/gapi_core_perf_tests.cpp create mode 100644 modules/gapi/perf/common/gapi_core_perf_tests.hpp create mode 100644 modules/gapi/perf/common/gapi_core_perf_tests_inl.hpp create mode 100644 modules/gapi/perf/common/gapi_imgproc_perf_tests.cpp create mode 100644 modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp create mode 100644 modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp create mode 100644 modules/gapi/perf/common/gapi_render_perf_tests.cpp create mode 100644 modules/gapi/perf/common/gapi_render_perf_tests.hpp create mode 100644 modules/gapi/perf/common/gapi_render_perf_tests_inl.hpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests.cpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests.hpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp create mode 100644 modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp create mode 100644 modules/gapi/perf/cpu/gapi_core_perf_tests_fluid.cpp create mode 100644 modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp create mode 100644 modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp create mode 100644 modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp create mode 100644 modules/gapi/perf/gpu/gapi_core_perf_tests_gpu.cpp create mode 100644 modules/gapi/perf/gpu/gapi_imgproc_perf_tests_gpu.cpp create mode 100644 modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp create mode 100644 modules/gapi/perf/perf_bench.cpp create mode 100644 modules/gapi/perf/perf_main.cpp create mode 100644 modules/gapi/perf/perf_precomp.hpp create mode 100644 modules/gapi/perf/render/gapi_render_perf_tests_ocv.cpp create mode 100644 modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp create mode 100644 modules/gapi/samples/api_example.cpp create mode 100644 modules/gapi/samples/data/config_template.yml create mode 100644 modules/gapi/samples/draw_example.cpp create mode 100644 modules/gapi/samples/face_detection_mtcnn.cpp create mode 100644 modules/gapi/samples/gaze_estimation.cpp create mode 100644 modules/gapi/samples/infer_ie_onnx_hybrid.cpp create mode 100644 modules/gapi/samples/infer_single_roi.cpp create mode 100644 modules/gapi/samples/infer_ssd_onnx.cpp create mode 100644 modules/gapi/samples/oak_basic_infer.cpp create mode 100644 modules/gapi/samples/oak_copy.cpp create mode 100644 modules/gapi/samples/oak_rgb_camera_encoding.cpp create mode 100644 modules/gapi/samples/oak_small_hetero_pipeline.cpp create mode 100644 modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp create mode 100644 modules/gapi/samples/onevpl_source_to_bgr_conv.cpp create mode 100644 modules/gapi/samples/pipeline_modeling_tool.cpp create mode 100644 modules/gapi/samples/pipeline_modeling_tool/dummy_source.hpp create mode 100644 modules/gapi/samples/pipeline_modeling_tool/pipeline.hpp create mode 100644 modules/gapi/samples/pipeline_modeling_tool/pipeline_builder.hpp create mode 100644 modules/gapi/samples/pipeline_modeling_tool/test_pipeline_modeling_tool.py create mode 100644 modules/gapi/samples/pipeline_modeling_tool/utils.hpp create mode 100644 modules/gapi/samples/privacy_masking_camera.cpp create mode 100644 modules/gapi/samples/semantic_segmentation.cpp create mode 100644 modules/gapi/samples/slides_blur_gapi.cpp create mode 100644 modules/gapi/samples/slides_sobel_cv.cpp create mode 100644 modules/gapi/samples/slides_sobel_gapi.cpp create mode 100644 modules/gapi/samples/text_detection.cpp create mode 100644 modules/gapi/samples/tutorial_code/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp create mode 100644 modules/gapi/samples/tutorial_code/doc_snippets/api_ref_snippets.cpp create mode 100644 modules/gapi/samples/tutorial_code/doc_snippets/dynamic_graph_snippets.cpp create mode 100644 modules/gapi/samples/tutorial_code/doc_snippets/kernel_api_snippets.cpp create mode 100644 modules/gapi/samples/tutorial_code/face_beautification/face_beautification.cpp create mode 100644 modules/gapi/samples/tutorial_code/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp create mode 100644 modules/gapi/samples/tutorial_code/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp create mode 100644 modules/gapi/samples/tutorial_code/security_barrier_camera/security_barrier_camera.cpp create mode 100755 modules/gapi/scripts/measure_privacy_masking.py create mode 100644 modules/gapi/src/3rdparty/vasot/LICENSE.txt create mode 100644 modules/gapi/src/3rdparty/vasot/include/vas/common.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/include/vas/ot.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/common/exception.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/common/prof.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/object_tracker.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/prof_def.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.cpp create mode 100644 modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.hpp create mode 100644 modules/gapi/src/api/README.md create mode 100644 modules/gapi/src/api/garray.cpp create mode 100644 modules/gapi/src/api/gbackend.cpp create mode 100644 modules/gapi/src/api/gbackend_priv.hpp create mode 100644 modules/gapi/src/api/gcall.cpp create mode 100644 modules/gapi/src/api/gcall_priv.hpp create mode 100644 modules/gapi/src/api/gcommon.cpp create mode 100644 modules/gapi/src/api/gcomputation.cpp create mode 100644 modules/gapi/src/api/gcomputation_priv.hpp create mode 100644 modules/gapi/src/api/gframe.cpp create mode 100644 modules/gapi/src/api/ginfer.cpp create mode 100644 modules/gapi/src/api/gkernel.cpp create mode 100644 modules/gapi/src/api/gmat.cpp create mode 100644 modules/gapi/src/api/gnode.cpp create mode 100644 modules/gapi/src/api/gnode.hpp create mode 100644 modules/gapi/src/api/gnode_priv.hpp create mode 100644 modules/gapi/src/api/gopaque.cpp create mode 100644 modules/gapi/src/api/gorigin.cpp create mode 100644 modules/gapi/src/api/gorigin.hpp create mode 100644 modules/gapi/src/api/gproto.cpp create mode 100644 modules/gapi/src/api/gproto_priv.hpp create mode 100644 modules/gapi/src/api/grunarg.cpp create mode 100644 modules/gapi/src/api/gscalar.cpp create mode 100644 modules/gapi/src/api/kernels_core.cpp create mode 100644 modules/gapi/src/api/kernels_imgproc.cpp create mode 100644 modules/gapi/src/api/kernels_nnparsers.cpp create mode 100644 modules/gapi/src/api/kernels_ot.cpp create mode 100644 modules/gapi/src/api/kernels_stereo.cpp create mode 100644 modules/gapi/src/api/kernels_streaming.cpp create mode 100644 modules/gapi/src/api/kernels_video.cpp create mode 100644 modules/gapi/src/api/media.cpp create mode 100644 modules/gapi/src/api/ocv_mask_creator.hpp create mode 100644 modules/gapi/src/api/operators.cpp create mode 100644 modules/gapi/src/api/render.cpp create mode 100644 modules/gapi/src/api/render_ocv.cpp create mode 100644 modules/gapi/src/api/render_ocv.hpp create mode 100644 modules/gapi/src/api/render_priv.hpp create mode 100644 modules/gapi/src/api/rmat.cpp create mode 100644 modules/gapi/src/api/s11n.cpp create mode 100644 modules/gapi/src/backends/README.md create mode 100644 modules/gapi/src/backends/common/gbackend.hpp create mode 100644 modules/gapi/src/backends/common/gcompoundbackend.cpp create mode 100644 modules/gapi/src/backends/common/gcompoundkernel.cpp create mode 100644 modules/gapi/src/backends/common/gmetabackend.cpp create mode 100644 modules/gapi/src/backends/common/gmetabackend.hpp create mode 100644 modules/gapi/src/backends/common/serialization.cpp create mode 100644 modules/gapi/src/backends/common/serialization.hpp create mode 100644 modules/gapi/src/backends/cpu/gcpubackend.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpubackend.hpp create mode 100644 modules/gapi/src/backends/cpu/gcpucore.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpuimgproc.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpukernel.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpuot.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpustereo.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpuvideo.cpp create mode 100644 modules/gapi/src/backends/cpu/gnnparsers.cpp create mode 100644 modules/gapi/src/backends/cpu/gnnparsers.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidbackend.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidbackend.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidbuffer.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidcore.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidcore_func.dispatch.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidcore_func.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidcore_func.simd.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidcore_simd_sse41.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidimgproc.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp create mode 100644 modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidimgproc_simd_avx2.hpp create mode 100644 modules/gapi/src/backends/fluid/gfluidutils.hpp create mode 100644 modules/gapi/src/backends/ie/bindings_ie.cpp create mode 100644 modules/gapi/src/backends/ie/giebackend.cpp create mode 100644 modules/gapi/src/backends/ie/giebackend.hpp create mode 100644 modules/gapi/src/backends/ie/giebackend/giewrapper.cpp create mode 100644 modules/gapi/src/backends/ie/giebackend/giewrapper.hpp create mode 100644 modules/gapi/src/backends/ie/util.hpp create mode 100644 modules/gapi/src/backends/oak/goak.cpp create mode 100644 modules/gapi/src/backends/oak/goak_memory_adapters.cpp create mode 100644 modules/gapi/src/backends/oak/goakbackend.cpp create mode 100644 modules/gapi/src/backends/oak/oak_memory_adapters.hpp create mode 100644 modules/gapi/src/backends/ocl/goclbackend.cpp create mode 100644 modules/gapi/src/backends/ocl/goclbackend.hpp create mode 100644 modules/gapi/src/backends/ocl/goclcore.cpp create mode 100644 modules/gapi/src/backends/ocl/goclcore.hpp create mode 100644 modules/gapi/src/backends/ocl/goclimgproc.cpp create mode 100644 modules/gapi/src/backends/ocl/goclimgproc.hpp create mode 100644 modules/gapi/src/backends/ocl/goclkernel.cpp create mode 100644 modules/gapi/src/backends/onnx/bindings_onnx.cpp create mode 100644 modules/gapi/src/backends/onnx/coreml_ep.cpp create mode 100644 modules/gapi/src/backends/onnx/coreml_ep.hpp create mode 100644 modules/gapi/src/backends/onnx/dml_ep.cpp create mode 100644 modules/gapi/src/backends/onnx/dml_ep.hpp create mode 100644 modules/gapi/src/backends/onnx/gonnxbackend.cpp create mode 100644 modules/gapi/src/backends/onnx/gonnxbackend.hpp create mode 100644 modules/gapi/src/backends/ov/bindings_ov.cpp create mode 100644 modules/gapi/src/backends/ov/govbackend.cpp create mode 100644 modules/gapi/src/backends/ov/govbackend.hpp create mode 100644 modules/gapi/src/backends/ov/util.hpp create mode 100644 modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp create mode 100644 modules/gapi/src/backends/plaidml/gplaidmlbackend.hpp create mode 100644 modules/gapi/src/backends/plaidml/gplaidmlcore.cpp create mode 100644 modules/gapi/src/backends/plaidml/plaidml_util.hpp create mode 100644 modules/gapi/src/backends/python/gpythonbackend.cpp create mode 100644 modules/gapi/src/backends/render/ft_render.cpp create mode 100644 modules/gapi/src/backends/render/ft_render.hpp create mode 100644 modules/gapi/src/backends/render/ft_render_priv.hpp create mode 100644 modules/gapi/src/backends/render/grenderocv.cpp create mode 100644 modules/gapi/src/backends/streaming/gstreamingbackend.cpp create mode 100644 modules/gapi/src/backends/streaming/gstreamingbackend.hpp create mode 100644 modules/gapi/src/backends/streaming/gstreamingkernel.hpp create mode 100644 modules/gapi/src/compiler/README.md create mode 100644 modules/gapi/src/compiler/gcompiled.cpp create mode 100644 modules/gapi/src/compiler/gcompiled_priv.hpp create mode 100644 modules/gapi/src/compiler/gcompiler.cpp create mode 100644 modules/gapi/src/compiler/gcompiler.hpp create mode 100644 modules/gapi/src/compiler/gislandmodel.cpp create mode 100644 modules/gapi/src/compiler/gislandmodel.hpp create mode 100644 modules/gapi/src/compiler/gmodel.cpp create mode 100644 modules/gapi/src/compiler/gmodel.hpp create mode 100644 modules/gapi/src/compiler/gmodel_priv.hpp create mode 100644 modules/gapi/src/compiler/gmodelbuilder.cpp create mode 100644 modules/gapi/src/compiler/gmodelbuilder.hpp create mode 100644 modules/gapi/src/compiler/gobjref.hpp create mode 100644 modules/gapi/src/compiler/gstreaming.cpp create mode 100644 modules/gapi/src/compiler/gstreaming_priv.hpp create mode 100644 modules/gapi/src/compiler/passes/dump_dot.cpp create mode 100644 modules/gapi/src/compiler/passes/exec.cpp create mode 100644 modules/gapi/src/compiler/passes/helpers.cpp create mode 100644 modules/gapi/src/compiler/passes/helpers.hpp create mode 100644 modules/gapi/src/compiler/passes/intrin.cpp create mode 100644 modules/gapi/src/compiler/passes/islands.cpp create mode 100644 modules/gapi/src/compiler/passes/kernels.cpp create mode 100644 modules/gapi/src/compiler/passes/meta.cpp create mode 100644 modules/gapi/src/compiler/passes/passes.hpp create mode 100644 modules/gapi/src/compiler/passes/pattern_matching.cpp create mode 100644 modules/gapi/src/compiler/passes/pattern_matching.hpp create mode 100644 modules/gapi/src/compiler/passes/perform_substitution.cpp create mode 100644 modules/gapi/src/compiler/passes/streaming.cpp create mode 100644 modules/gapi/src/compiler/passes/transformations.cpp create mode 100644 modules/gapi/src/compiler/transactions.hpp create mode 100644 modules/gapi/src/executor/conc_queue.hpp create mode 100644 modules/gapi/src/executor/gabstractexecutor.cpp create mode 100644 modules/gapi/src/executor/gabstractexecutor.hpp create mode 100644 modules/gapi/src/executor/gabstractstreamingexecutor.cpp create mode 100644 modules/gapi/src/executor/gabstractstreamingexecutor.hpp create mode 100644 modules/gapi/src/executor/gasync.cpp create mode 100644 modules/gapi/src/executor/gexecutor.cpp create mode 100644 modules/gapi/src/executor/gexecutor.hpp create mode 100644 modules/gapi/src/executor/gstreamingexecutor.cpp create mode 100644 modules/gapi/src/executor/gstreamingexecutor.hpp create mode 100644 modules/gapi/src/executor/gtbbexecutor.cpp create mode 100644 modules/gapi/src/executor/gtbbexecutor.hpp create mode 100644 modules/gapi/src/executor/gthreadedexecutor.cpp create mode 100644 modules/gapi/src/executor/gthreadedexecutor.hpp create mode 100644 modules/gapi/src/executor/last_value.hpp create mode 100644 modules/gapi/src/executor/thread_pool.cpp create mode 100644 modules/gapi/src/executor/thread_pool.hpp create mode 100644 modules/gapi/src/logger.hpp create mode 100644 modules/gapi/src/precomp.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_buffer_utils.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_buffer_utils.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_media_adapter.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_pipeline_facade.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamer_pipeline_facade.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamerenv.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamerenv.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamerpipeline.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamerpipeline_priv.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamerptr.hpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamersource.cpp create mode 100644 modules/gapi/src/streaming/gstreamer/gstreamersource_priv.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_va_api.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/base_frame_adapter.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/base_frame_adapter.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/utils/elastic_barrier.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.hpp create mode 100644 modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp create mode 100644 modules/gapi/src/streaming/onevpl/cfg_param_device_selector.hpp create mode 100644 modules/gapi/src/streaming/onevpl/cfg_params.cpp create mode 100644 modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp create mode 100644 modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp create mode 100644 modules/gapi/src/streaming/onevpl/data_provider_defines.hpp create mode 100644 modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp create mode 100644 modules/gapi/src/streaming/onevpl/data_provider_dispatcher.hpp create mode 100644 modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp create mode 100644 modules/gapi/src/streaming/onevpl/default.cpp create mode 100644 modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp create mode 100644 modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp create mode 100644 modules/gapi/src/streaming/onevpl/device_selector_interface.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/engine_session.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/engine_session.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_dispatcher.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_engine.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_session.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/preproc_session.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/utils.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/utils.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc/vpp_preproc_defines.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc_defines.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/preproc_engine_interface.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/transcode/transcode_engine_legacy.hpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/transcode/transcode_session.cpp create mode 100644 modules/gapi/src/streaming/onevpl/engine/transcode/transcode_session.hpp create mode 100644 modules/gapi/src/streaming/onevpl/file_data_provider.cpp create mode 100644 modules/gapi/src/streaming/onevpl/file_data_provider.hpp create mode 100644 modules/gapi/src/streaming/onevpl/onevpl_export.hpp create mode 100644 modules/gapi/src/streaming/onevpl/source.cpp create mode 100644 modules/gapi/src/streaming/onevpl/source_priv.cpp create mode 100644 modules/gapi/src/streaming/onevpl/source_priv.hpp create mode 100644 modules/gapi/src/streaming/onevpl/utils.cpp create mode 100644 modules/gapi/src/streaming/onevpl/utils.hpp create mode 100644 modules/gapi/src/streaming/queue_source.cpp create mode 100644 modules/gapi/src/utils/itt.cpp create mode 100644 modules/gapi/src/utils/itt.hpp create mode 100644 modules/gapi/test/common/gapi_compoundkernel_tests.cpp create mode 100644 modules/gapi/test/common/gapi_core_tests.cpp create mode 100644 modules/gapi/test/common/gapi_core_tests.hpp create mode 100644 modules/gapi/test/common/gapi_core_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_core_tests_inl.hpp create mode 100644 modules/gapi/test/common/gapi_imgproc_tests.cpp create mode 100644 modules/gapi/test/common/gapi_imgproc_tests.hpp create mode 100644 modules/gapi/test/common/gapi_imgproc_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_imgproc_tests_inl.hpp create mode 100644 modules/gapi/test/common/gapi_operators_tests.cpp create mode 100644 modules/gapi/test/common/gapi_operators_tests.hpp create mode 100644 modules/gapi/test/common/gapi_operators_tests_inl.hpp create mode 100644 modules/gapi/test/common/gapi_parsers_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_render_tests.cpp create mode 100644 modules/gapi/test/common/gapi_render_tests.hpp create mode 100644 modules/gapi/test/common/gapi_stereo_tests.cpp create mode 100644 modules/gapi/test/common/gapi_stereo_tests.hpp create mode 100644 modules/gapi/test/common/gapi_stereo_tests_inl.hpp create mode 100644 modules/gapi/test/common/gapi_streaming_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_tests_helpers.hpp create mode 100644 modules/gapi/test/common/gapi_video_tests.cpp create mode 100644 modules/gapi/test/common/gapi_video_tests.hpp create mode 100644 modules/gapi/test/common/gapi_video_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_video_tests_inl.hpp create mode 100644 modules/gapi/test/cpu/gapi_core_tests_cpu.cpp create mode 100644 modules/gapi/test/cpu/gapi_core_tests_fluid.cpp create mode 100644 modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp create mode 100644 modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp create mode 100644 modules/gapi/test/cpu/gapi_ocv_stateful_kernel_test_utils.hpp create mode 100644 modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp create mode 100644 modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp create mode 100644 modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp create mode 100644 modules/gapi/test/cpu/gapi_ot_tests_cpu.cpp create mode 100644 modules/gapi/test/cpu/gapi_stereo_tests_cpu.cpp create mode 100644 modules/gapi/test/cpu/gapi_video_tests_cpu.cpp create mode 100644 modules/gapi/test/executor/gtbbexecutor_internal_tests.cpp create mode 100644 modules/gapi/test/gapi_array_tests.cpp create mode 100644 modules/gapi/test/gapi_async_test.cpp create mode 100644 modules/gapi/test/gapi_basic_hetero_tests.cpp create mode 100644 modules/gapi/test/gapi_compile_args_tests.cpp create mode 100644 modules/gapi/test/gapi_desc_tests.cpp create mode 100644 modules/gapi/test/gapi_fluid_parallel_rois_test.cpp create mode 100644 modules/gapi/test/gapi_fluid_resize_test.cpp create mode 100644 modules/gapi/test/gapi_fluid_roi_test.cpp create mode 100644 modules/gapi/test/gapi_fluid_test.cpp create mode 100644 modules/gapi/test/gapi_fluid_test_kernels.cpp create mode 100644 modules/gapi/test/gapi_fluid_test_kernels.hpp create mode 100644 modules/gapi/test/gapi_frame_tests.cpp create mode 100644 modules/gapi/test/gapi_gcompiled_tests.cpp create mode 100644 modules/gapi/test/gapi_gcomputation_tests.cpp create mode 100644 modules/gapi/test/gapi_gpu_test.cpp create mode 100644 modules/gapi/test/gapi_graph_meta_tests.cpp create mode 100644 modules/gapi/test/gapi_kernel_tests.cpp create mode 100644 modules/gapi/test/gapi_mat_tests.cpp create mode 100644 modules/gapi/test/gapi_mock_kernels.hpp create mode 100644 modules/gapi/test/gapi_opaque_tests.cpp create mode 100644 modules/gapi/test/gapi_plaidml_pipelines.cpp create mode 100644 modules/gapi/test/gapi_planar_test.cpp create mode 100644 modules/gapi/test/gapi_sample_pipelines.cpp create mode 100644 modules/gapi/test/gapi_scalar_tests.cpp create mode 100644 modules/gapi/test/gapi_smoke_test.cpp create mode 100644 modules/gapi/test/gapi_transform_tests.cpp create mode 100644 modules/gapi/test/gapi_typed_tests.cpp create mode 100644 modules/gapi/test/gapi_util_tests.cpp create mode 100644 modules/gapi/test/gpu/gapi_core_tests_gpu.cpp create mode 100644 modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp create mode 100644 modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp create mode 100644 modules/gapi/test/infer/gapi_infer_ie_test.cpp create mode 100644 modules/gapi/test/infer/gapi_infer_onnx_test.cpp create mode 100644 modules/gapi/test/infer/gapi_infer_ov_tests.cpp create mode 100644 modules/gapi/test/infer/gapi_infer_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_backend_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_dynamic_graph.cpp create mode 100644 modules/gapi/test/internal/gapi_int_executor_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_garg_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_island_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_pattern_matching_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_perform_substitution_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_proto_tests.cpp create mode 100644 modules/gapi/test/internal/gapi_int_recompilation_test.cpp create mode 100644 modules/gapi/test/internal/gapi_int_vectorref_test.cpp create mode 100644 modules/gapi/test/internal/gapi_transactions_test.cpp create mode 100644 modules/gapi/test/oak/gapi_tests_oak.cpp create mode 100644 modules/gapi/test/opencl_kernels_test_gapi.hpp create mode 100644 modules/gapi/test/own/conc_queue_tests.cpp create mode 100644 modules/gapi/test/own/gapi_types_tests.cpp create mode 100644 modules/gapi/test/own/last_written_value_tests.cpp create mode 100644 modules/gapi/test/own/mat_tests.cpp create mode 100644 modules/gapi/test/own/scalar_tests.cpp create mode 100644 modules/gapi/test/own/thread_pool_tests.cpp create mode 100644 modules/gapi/test/render/ftp_render_test.cpp create mode 100644 modules/gapi/test/render/gapi_render_tests_ocv.cpp create mode 100644 modules/gapi/test/rmat/rmat_integration_tests.cpp create mode 100644 modules/gapi/test/rmat/rmat_test_common.hpp create mode 100644 modules/gapi/test/rmat/rmat_tests.cpp create mode 100644 modules/gapi/test/rmat/rmat_view_tests.cpp create mode 100644 modules/gapi/test/s11n/gapi_s11n_tests.cpp create mode 100644 modules/gapi/test/s11n/gapi_sample_pipelines_s11n.cpp create mode 100644 modules/gapi/test/streaming/gapi_gstreamer_pipeline_facade_int_tests.cpp create mode 100644 modules/gapi/test/streaming/gapi_gstreamersource_tests.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_queue_source_tests.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_sync_tests.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_tests.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_utils_test.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp create mode 100644 modules/gapi/test/streaming/gapi_streaming_vpp_preproc_test.cpp create mode 100644 modules/gapi/test/test_main.cpp create mode 100644 modules/gapi/test/test_precomp.hpp create mode 100644 modules/gapi/test/util/any_tests.cpp create mode 100644 modules/gapi/test/util/optional_tests.cpp create mode 100644 modules/gapi/test/util/variant_tests.cpp create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/massif_export_gapi.png create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/massif_export_gapi_fluid.png create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/massif_export_ocv.png create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/result.jpg create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/segm.gif create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/pics/segm_fluid.gif create mode 100644 modules/gapi/tutorials/anisotropic_segmentation/porting_anisotropic_segmentation.markdown create mode 100644 modules/gapi/tutorials/face_beautification/face_beautification.markdown create mode 100644 modules/gapi/tutorials/face_beautification/pics/example.jpg create mode 100644 modules/gapi/tutorials/gapi.markdown create mode 100644 modules/gapi/tutorials/interactive_face_detection/interactive_face_detection.markdown create mode 100644 modules/gapi/tutorials/oak_devices/oak_devices.markdown create mode 100644 modules/gapi/tutorials/oak_devices/pics/oak.jpg diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt new file mode 100644 index 00000000000..6757c853bd1 --- /dev/null +++ b/modules/gapi/CMakeLists.txt @@ -0,0 +1,440 @@ +# FIXME: Rework standalone build in more generic maner +# (Restructure directories, add common pass, etc) +if(NOT DEFINED OPENCV_INITIAL_PASS) + cmake_minimum_required(VERSION 3.13) + project(gapi_standalone) + include("cmake/standalone.cmake") + return() +endif() + +if(NOT TARGET ade) + # can't build G-API because of the above reasons + ocv_module_disable(gapi) + return() +endif() + +if(TARGET ocv.3rdparty.openvino) + # TODO: remove OPENCV_GAPI_INF_ENGINE option + set(initial_value ON) + if(DEFINED OPENCV_GAPI_INF_ENGINE) + set(initial_value ${OPENCV_GAPI_INF_ENGINE}) + message(WARNING "OPENCV_GAPI_INF_ENGINE option is deprecated. Use OPENCV_GAPI_WITH_OPENVINO option instead.") + endif() + ocv_option(OPENCV_GAPI_WITH_OPENVINO "G-API: Enable OpenVINO Toolkit support" ${initial_value}) +endif() + +set(the_description "OpenCV G-API Core Module") + +ocv_add_module(gapi + REQUIRED + opencv_imgproc + OPTIONAL + opencv_video opencv_stereo + WRAP + python +) + +if(MSVC) + if(MSVC_VERSION LESS 1910) + # Disable obsolete warning C4503 popping up on MSVC << 15 2017 + # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4503?view=vs-2019 + # and IE deprecated code warning C4996 + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996) + endif() + if((MSVC_VERSION LESS 1920) OR ARM OR AARCH64) # MSVS 2015/2017 on x86 and ARM + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # 'unreachable code' + endif() +endif() + +file(GLOB gapi_ext_hdrs + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.h" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cpu/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/fluid/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/gpu/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/infer/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/oak/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/ocl/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/own/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/python/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/render/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/s11n/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/gstreamer/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/onevpl/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp" + "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/util/*.hpp" + ) + +set(gapi_srcs + # Front-end part + src/api/grunarg.cpp + src/api/gorigin.cpp + src/api/gmat.cpp + src/api/garray.cpp + src/api/gopaque.cpp + src/api/gscalar.cpp + src/api/gframe.cpp + src/api/gkernel.cpp + src/api/gbackend.cpp + src/api/gcommon.cpp + src/api/gproto.cpp + src/api/gnode.cpp + src/api/gcall.cpp + src/api/gcomputation.cpp + src/api/operators.cpp + src/api/kernels_core.cpp + src/api/kernels_imgproc.cpp + src/api/kernels_video.cpp + src/api/kernels_nnparsers.cpp + src/api/kernels_ot.cpp + src/api/kernels_streaming.cpp + src/api/kernels_stereo.cpp + src/api/render.cpp + src/api/render_ocv.cpp + src/api/ginfer.cpp + src/api/media.cpp + src/api/rmat.cpp + + # Compiler part + src/compiler/gmodel.cpp + src/compiler/gmodelbuilder.cpp + src/compiler/gislandmodel.cpp + src/compiler/gcompiler.cpp + src/compiler/gcompiled.cpp + src/compiler/gstreaming.cpp + src/compiler/passes/helpers.cpp + src/compiler/passes/dump_dot.cpp + src/compiler/passes/islands.cpp + src/compiler/passes/meta.cpp + src/compiler/passes/kernels.cpp + src/compiler/passes/exec.cpp + src/compiler/passes/transformations.cpp + src/compiler/passes/pattern_matching.cpp + src/compiler/passes/perform_substitution.cpp + src/compiler/passes/streaming.cpp + src/compiler/passes/intrin.cpp + + # Executor + src/executor/gabstractexecutor.cpp + src/executor/gabstractstreamingexecutor.cpp + src/executor/gexecutor.cpp + src/executor/gtbbexecutor.cpp + src/executor/gthreadedexecutor.cpp + src/executor/gstreamingexecutor.cpp + src/executor/gasync.cpp + src/executor/thread_pool.cpp + + # CPU Backend (currently built-in) + 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/gcpuot.cpp + src/backends/cpu/gnnparsers.cpp + + # Fluid Backend (also built-in, FIXME:move away) + src/backends/fluid/gfluidbuffer.cpp + src/backends/fluid/gfluidbackend.cpp + src/backends/fluid/gfluidimgproc.cpp + src/backends/fluid/gfluidimgproc_func.dispatch.cpp + src/backends/fluid/gfluidcore.cpp + src/backends/fluid/gfluidcore_func.dispatch.cpp + + # OAK Backend (optional) + src/backends/oak/goak.cpp + src/backends/oak/goakbackend.cpp + src/backends/oak/goak_memory_adapters.cpp + + # OCL Backend (currently built-in) + src/backends/ocl/goclbackend.cpp + src/backends/ocl/goclkernel.cpp + src/backends/ocl/goclimgproc.cpp + src/backends/ocl/goclcore.cpp + + # IE Backend. FIXME: should be included by CMake + # if and only if IE support is enabled + src/backends/ie/giebackend.cpp + src/backends/ie/giebackend/giewrapper.cpp + + # OV Backend. FIXME: should be included by CMake + # if and only if OV support is enabled + src/backends/ov/govbackend.cpp + + # ONNX backend + src/backends/onnx/gonnxbackend.cpp + src/backends/onnx/dml_ep.cpp + src/backends/onnx/coreml_ep.cpp + + # Render backend + src/backends/render/grenderocv.cpp + src/backends/render/ft_render.cpp + + # PlaidML Backend + src/backends/plaidml/gplaidmlcore.cpp + src/backends/plaidml/gplaidmlbackend.cpp + + # Common backend code + src/backends/common/gmetabackend.cpp + src/backends/common/gcompoundbackend.cpp + src/backends/common/gcompoundkernel.cpp + + # Serialization API and routines + src/api/s11n.cpp + src/backends/common/serialization.cpp + + # Streaming backend + src/backends/streaming/gstreamingbackend.cpp + + # Python bridge + src/backends/ie/bindings_ie.cpp + src/backends/onnx/bindings_onnx.cpp + src/backends/ov/bindings_ov.cpp + src/backends/python/gpythonbackend.cpp + + # Queue Streaming source + src/streaming/queue_source.cpp + + # OpenVPL Streaming source + src/streaming/onevpl/source.cpp + src/streaming/onevpl/source_priv.cpp + src/streaming/onevpl/file_data_provider.cpp + src/streaming/onevpl/cfg_params.cpp + src/streaming/onevpl/cfg_params_parser.cpp + src/streaming/onevpl/utils.cpp + src/streaming/onevpl/default.cpp + src/streaming/onevpl/data_provider_interface_exception.cpp + src/streaming/onevpl/accelerators/surface/base_frame_adapter.cpp + src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp + src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp + src/streaming/onevpl/accelerators/surface/surface.cpp + src/streaming/onevpl/accelerators/surface/surface_pool.cpp + src/streaming/onevpl/accelerators/utils/shared_lock.cpp + src/streaming/onevpl/accelerators/accel_policy_cpu.cpp + src/streaming/onevpl/accelerators/accel_policy_dx11.cpp + src/streaming/onevpl/accelerators/accel_policy_va_api.cpp + src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp + src/streaming/onevpl/engine/engine_session.cpp + src/streaming/onevpl/engine/processing_engine_base.cpp + src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp + src/streaming/onevpl/engine/decode/decode_session.cpp + src/streaming/onevpl/engine/transcode/transcode_engine_legacy.cpp + src/streaming/onevpl/engine/transcode/transcode_session.cpp + src/streaming/onevpl/engine/preproc/preproc_engine.cpp + src/streaming/onevpl/engine/preproc/preproc_session.cpp + src/streaming/onevpl/engine/preproc/preproc_dispatcher.cpp + src/streaming/onevpl/engine/preproc_engine_interface.cpp + src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp + src/streaming/onevpl/data_provider_dispatcher.cpp + + src/streaming/onevpl/cfg_param_device_selector.cpp + src/streaming/onevpl/device_selector_interface.cpp + + # GStreamer Streaming source + src/streaming/gstreamer/gstreamer_pipeline_facade.cpp + src/streaming/gstreamer/gstreamerpipeline.cpp + src/streaming/gstreamer/gstreamersource.cpp + src/streaming/gstreamer/gstreamer_buffer_utils.cpp + src/streaming/gstreamer/gstreamer_media_adapter.cpp + src/streaming/gstreamer/gstreamerenv.cpp + + # Utils (ITT tracing) + src/utils/itt.cpp + ) + +file(GLOB_RECURSE gapi_3rdparty_srcs + "${CMAKE_CURRENT_LIST_DIR}/src/3rdparty/vasot/src/*.cpp" +) + +ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2) +ocv_add_dispatched_file(backends/fluid/gfluidcore_func SSE4_1 AVX2) + +ocv_list_add_prefix(gapi_srcs "${CMAKE_CURRENT_LIST_DIR}/") + +# For IDE users +ocv_source_group("Src" FILES ${gapi_srcs} ${gapi_3rdparty_srcs}) +ocv_source_group("Include" FILES ${gapi_ext_hdrs}) + +ocv_set_module_sources(HEADERS ${gapi_ext_hdrs} SOURCES ${gapi_srcs} ${gapi_3rdparty_srcs}) +ocv_module_include_directories("${CMAKE_CURRENT_LIST_DIR}/src") + +# VAS Object Tracking includes +ocv_module_include_directories(${CMAKE_CURRENT_LIST_DIR}/src/3rdparty/vasot/include) + +ocv_create_module() + +ocv_target_link_libraries(${the_module} PRIVATE ade) + +if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) + ocv_target_link_libraries(${the_module} PRIVATE ocv.3rdparty.openvino) + ocv_install_used_external_targets(ocv.3rdparty.openvino) +endif() + +if(HAVE_TBB) + ocv_target_link_libraries(${the_module} PRIVATE tbb) +endif() + +# TODO: Consider support of ITT in G-API standalone mode. +if(CV_TRACE AND HAVE_ITT) + ocv_target_compile_definitions(${the_module} PRIVATE -DOPENCV_WITH_ITT=1) + ocv_module_include_directories(${ITT_INCLUDE_DIRS}) + ocv_target_link_libraries(${the_module} PRIVATE ${ITT_LIBRARIES}) +endif() + +set(__test_extra_deps "") +if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) + list(APPEND __test_extra_deps ocv.3rdparty.openvino) +endif() +ocv_add_accuracy_tests(${__test_extra_deps}) + +# FIXME: test binary is linked with ADE directly since ADE symbols +# are not exported from libopencv_gapi.so in any form - thus +# there're two copies of ADE code in memory when tests run (!) +# src/ is specified to include dirs for INTERNAL tests only. +if(TARGET opencv_test_gapi) + target_include_directories(opencv_test_gapi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src") + target_link_libraries(opencv_test_gapi PRIVATE ade) +endif() + +if(HAVE_TBB AND TARGET opencv_test_gapi) + ocv_target_link_libraries(opencv_test_gapi PRIVATE tbb) +endif() + +if(HAVE_FREETYPE) + ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_FREETYPE) + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_FREETYPE) + endif() + ocv_target_link_libraries(${the_module} PRIVATE ${FREETYPE_LIBRARIES}) + ocv_target_include_directories(${the_module} PRIVATE ${FREETYPE_INCLUDE_DIRS}) +endif() + +if(HAVE_OAK) + ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_OAK) + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_OAK) + endif() + ocv_target_link_libraries(${the_module} PRIVATE depthai::core) +endif() + +if(HAVE_PLAIDML) + ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_PLAIDML) + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_PLAIDML) + endif() + ocv_target_link_libraries(${the_module} PRIVATE ${PLAIDML_LIBRARIES}) + ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${PLAIDML_INCLUDE_DIRS}) +endif() + +if(HAVE_GAPI_ONEVPL) + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_ONEVPL) + ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VPL_IMPORTED_TARGETS}) + if(MSVC) + target_compile_options(opencv_test_gapi PUBLIC "/wd4201") + endif() + if(HAVE_D3D11 AND HAVE_OPENCL) + ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) + endif() + endif() + + ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_ONEVPL) + ocv_target_link_libraries(${the_module} PRIVATE ${VPL_IMPORTED_TARGETS}) + + if(HAVE_DIRECTX AND HAVE_D3D11) + ocv_target_link_libraries(${the_module} PRIVATE d3d11 dxgi) + endif() + if(WIN32) + ocv_target_link_libraries(${the_module} PRIVATE mf mfuuid mfplat shlwapi mfreadwrite) + endif() + if(HAVE_D3D11 AND HAVE_OPENCL) + ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) + endif() + + if(UNIX AND HAVE_VA) + ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(${the_module} PRIVATE ${VA_LIBRARIES}) + if(TARGET opencv_test_gapi) + ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VA_LIBRARIES}) + endif() + endif() +endif() + +ocv_option(OPENCV_GAPI_GSTREAMER "Build G-API with GStreamer support" HAVE_GSTREAMER) +if(HAVE_GSTREAMER AND OPENCV_GAPI_GSTREAMER) + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_GSTREAMER) + ocv_target_link_libraries(opencv_test_gapi PRIVATE ocv.3rdparty.gstreamer) + endif() + ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_GSTREAMER) + ocv_target_link_libraries(${the_module} PRIVATE ocv.3rdparty.gstreamer) +endif() + +if(WIN32) + # Required for htonl/ntohl on Windows + ocv_target_link_libraries(${the_module} PRIVATE wsock32 ws2_32) +endif() + +if(HAVE_DIRECTML) + ocv_target_compile_definitions(${the_module} PRIVATE HAVE_DIRECTML=1) +endif() + +if(HAVE_ONNX) + ocv_target_link_libraries(${the_module} PRIVATE ${ONNX_LIBRARY}) + ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX=1) + if(HAVE_ONNX_DML) + ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX_DML=1) + endif() + if(TARGET opencv_test_gapi) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE HAVE_ONNX=1) + ocv_target_link_libraries(opencv_test_gapi PRIVATE ${ONNX_LIBRARY}) + endif() +endif() + +ocv_install_3rdparty_licenses(vasot "${CMAKE_CURRENT_SOURCE_DIR}/src/3rdparty/vasot/LICENSE.txt") + +ocv_add_perf_tests() +ocv_add_samples() + +# Required for sample with inference on host +if(TARGET example_gapi_onevpl_infer_with_advanced_device_selection) + if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE ocv.3rdparty.openvino) + endif() + if(HAVE_DIRECTX AND HAVE_D3D11) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE d3d11 dxgi) + endif() + if(HAVE_D3D11 AND HAVE_OPENCL) + ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) + endif() + if(UNIX AND HAVE_VA) + message(STATUS "GAPI VPL samples with VAAPI") + ocv_target_include_directories(example_gapi_onevpl_infer_with_advanced_device_selection SYSTEM PRIVATE ${VA_INCLUDE_DIR}) + ocv_target_link_libraries(example_gapi_onevpl_infer_with_advanced_device_selection PRIVATE ${VA_LIBRARIES}) + endif() +endif() + +if(TARGET example_gapi_pipeline_modeling_tool) + if(WIN32) + ocv_target_link_libraries(example_gapi_pipeline_modeling_tool winmm.lib) + endif() +endif() + +# perf test dependencies postprocessing +if(HAVE_GAPI_ONEVPL) + # NB: TARGET opencv_perf_gapi doesn't exist before `ocv_add_perf_tests` + # src/ is specified to include dirs for INTERNAL tests only. + if(TARGET opencv_perf_gapi) + target_include_directories(opencv_perf_gapi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src") + ocv_target_compile_definitions(opencv_perf_gapi PRIVATE -DHAVE_ONEVPL) + ocv_target_link_libraries(opencv_perf_gapi PRIVATE ${VPL_IMPORTED_TARGETS}) + if(HAVE_D3D11 AND HAVE_OPENCL) + ocv_target_include_directories(opencv_perf_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) + endif() + endif() +endif() diff --git a/modules/gapi/cmake/DownloadADE.cmake b/modules/gapi/cmake/DownloadADE.cmake new file mode 100644 index 00000000000..8ddaadb5119 --- /dev/null +++ b/modules/gapi/cmake/DownloadADE.cmake @@ -0,0 +1,51 @@ +set(ade_src_dir "${OpenCV_BINARY_DIR}/3rdparty/ade") +set(ade_filename "v0.1.2e.zip") +set(ade_subdir "ade-0.1.2e") +set(ade_md5 "962ce79e0b95591f226431f7b5f152cd") +ocv_download(FILENAME ${ade_filename} + HASH ${ade_md5} + URL + "${OPENCV_ADE_URL}" + "$ENV{OPENCV_ADE_URL}" + "https://github.com/opencv/ade/archive/" + DESTINATION_DIR ${ade_src_dir} + ID ADE + STATUS res + UNPACK RELATIVE_URL) + +if (NOT res) + return() +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 ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} + ${ADE_include} + ${ADE_sources} +) + +# https://github.com/opencv/ade/issues/32 +if(CV_CLANG AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.1) + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wdeprecated-copy) +endif() + +target_include_directories(ade PUBLIC $) +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 OPTIONAL) +endif() + +ocv_install_3rdparty_licenses(ade "${ade_src_dir}/${ade_subdir}/LICENSE") diff --git a/modules/gapi/cmake/init.cmake b/modules/gapi/cmake/init.cmake new file mode 100644 index 00000000000..dd4b0bccfa3 --- /dev/null +++ b/modules/gapi/cmake/init.cmake @@ -0,0 +1,49 @@ +OCV_OPTION(WITH_ADE "Enable ADE framework (required for Graph API module)" ON) + +OCV_OPTION(WITH_FREETYPE "Enable FreeType framework" OFF) +OCV_OPTION(WITH_PLAIDML "Include PlaidML2 support" OFF) +OCV_OPTION(WITH_OAK "Include OpenCV AI Kit support" OFF) + +if(NOT WITH_ADE) + return() +endif() + +if(ade_DIR) + # if ade_DIR is set, use ADE-supplied CMake script + # to set up variables to the prebuilt ADE + find_package(ade 0.1.0) +endif() + +if(NOT TARGET ade) + # if ade_DIR is not set, try to use automatically + # downloaded one (if there any) + include("${CMAKE_CURRENT_LIST_DIR}/DownloadADE.cmake") +endif() + +if(WITH_FREETYPE) + ocv_check_modules(FREETYPE freetype2) + if (FREETYPE_FOUND) + set(HAVE_FREETYPE TRUE) + endif() +endif() + +if(WITH_PLAIDML) + find_package(PlaidML2 CONFIG QUIET) + if (PLAIDML_FOUND) + set(HAVE_PLAIDML TRUE) + endif() +endif() + +if(WITH_GAPI_ONEVPL) + find_package(VPL) + if(VPL_FOUND) + set(HAVE_GAPI_ONEVPL TRUE) + endif() +endif() + +if(WITH_OAK) + find_package(depthai QUIET) + if(depthai_FOUND) + set(HAVE_OAK TRUE) + endif() +endif() diff --git a/modules/gapi/cmake/standalone.cmake b/modules/gapi/cmake/standalone.cmake new file mode 100644 index 00000000000..f81c1c8a85d --- /dev/null +++ b/modules/gapi/cmake/standalone.cmake @@ -0,0 +1,62 @@ +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE "Release") +endif() + +if (NOT TARGET ade ) + find_package(ade 0.1.0 REQUIRED) +endif() + +if (WITH_GAPI_ONEVPL) + find_package(VPL) + if(VPL_FOUND) + set(HAVE_GAPI_ONEVPL TRUE) + endif() +endif() + +set(FLUID_TARGET fluid) +set(FLUID_ROOT "${CMAKE_CURRENT_LIST_DIR}/../") + +file(GLOB FLUID_includes "${FLUID_ROOT}/include/opencv2/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/g*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/util/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/own/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/fluid/*.hpp") +file(GLOB FLUID_sources "${FLUID_ROOT}/src/api/g*.cpp" + "${FLUID_ROOT}/src/api/rmat.cpp" + "${FLUID_ROOT}/src/api/media.cpp" + "${FLUID_ROOT}/src/compiler/*.cpp" + "${FLUID_ROOT}/src/compiler/passes/*.cpp" + "${FLUID_ROOT}/src/executor/*.cpp" + "${FLUID_ROOT}/src/backends/fluid/*.cpp" + "${FLUID_ROOT}/src/backends/streaming/*.cpp" + "${FLUID_ROOT}/src/backends/common/*.cpp") + +add_library(${FLUID_TARGET} STATIC ${FLUID_includes} ${FLUID_sources}) + +target_include_directories(${FLUID_TARGET} + PUBLIC $ + PRIVATE ${FLUID_ROOT}/src) + +target_compile_definitions(${FLUID_TARGET} PUBLIC GAPI_STANDALONE +# This preprocessor definition resolves symbol clash when +# standalone fluid meets gapi ocv module in one application + PUBLIC cv=fluidcv) + +set_target_properties(${FLUID_TARGET} PROPERTIES POSITION_INDEPENDENT_CODE True) +set_property(TARGET ${FLUID_TARGET} PROPERTY CXX_STANDARD 11) + +if(MSVC) + target_compile_options(${FLUID_TARGET} PUBLIC "/wd4251") + target_compile_options(${FLUID_TARGET} PUBLIC "/wd4275") + target_compile_definitions(${FLUID_TARGET} PRIVATE _CRT_SECURE_NO_DEPRECATE) + # Disable obsollete warning C4503 popping up on MSVC <<2017 + # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4503?view=vs-2019 + set_target_properties(${FLUID_TARGET} PROPERTIES COMPILE_FLAGS "/wd4503") +endif() + +target_link_libraries(${FLUID_TARGET} PRIVATE ade) + +if(WIN32) + # Required for htonl/ntohl on Windows + target_link_libraries(${FLUID_TARGET} PRIVATE wsock32 ws2_32) +endif() diff --git a/modules/gapi/doc/00-root.markdown b/modules/gapi/doc/00-root.markdown new file mode 100644 index 00000000000..cb99495c1b3 --- /dev/null +++ b/modules/gapi/doc/00-root.markdown @@ -0,0 +1,125 @@ +# Graph API {#gapi} + +# Introduction {#gapi_root_intro} + +OpenCV Graph API (or G-API) is a new OpenCV module targeted to make +regular image processing fast and portable. These two goals are +achieved by introducing a new graph-based model of execution. + +G-API is a special module in OpenCV -- in contrast with the majority +of other main modules, this one acts as a framework rather than some +specific CV algorithm. G-API provides means to define CV operations, +construct graphs (in form of expressions) using it, and finally +implement and run the operations for a particular backend. + +@note G-API is a new module and now is in active development. It's API +is volatile at the moment and there may be minor but +compatibility-breaking changes in the future. + +# Contents + +G-API documentation is organized into the following chapters: + +- @subpage gapi_purposes + + The motivation behind G-API and its goals. + +- @subpage gapi_hld + + General overview of G-API architecture and its major internal + components. + +- @subpage gapi_kernel_api + + Learn how to introduce new operations in G-API and implement it for + various backends. + +- @subpage gapi_impl + + Low-level implementation details of G-API, for those who want to + contribute. + +- API Reference: functions and classes + + - @subpage gapi_ref + + Core G-API classes, data types, backends, etc. + + - @subpage gapi_core + + Core G-API operations - arithmetic, boolean, and other matrix + operations; + + - @subpage gapi_imgproc + + Image processing functions: color space conversions, various + filters, etc. + + - @subpage gapi_video + + Video processing functionality. + + - @subpage gapi_draw + + Drawing and composition functionality + +# API Example {#gapi_example} + +A very basic example of G-API pipeline is shown below: + +@include modules/gapi/samples/api_example.cpp + + + +G-API is a separate OpenCV module so its header files have to be +included explicitly. The first four lines of `main()` create and +initialize OpenCV's standard video capture object, which fetches +video frames from either an attached camera or a specified file. + +G-API pipeline is constructed next. In fact, it is a series of G-API +operation calls on cv::GMat data. The important aspect of G-API is +that this code block is just a declaration of actions, but not the +actions themselves. No processing happens at this point, G-API only +tracks which operations form pipeline and how it is connected. G-API +_Data objects_ (here it is cv::GMat) are used to connect operations +each other. `in` is an _empty_ cv::GMat signalling that it is a +beginning of computation. + +After G-API code is written, it is captured into a call graph with +instantiation of cv::GComputation object. This object takes +input/output data references (in this example, `in` and `out` +cv::GMat objects, respectively) as parameters and reconstructs the +call graph based on all the data flow between `in` and `out`. + +cv::GComputation is a thin object in sense that it just captures which +operations form up a computation. However, it can be used to execute +computations -- in the following processing loop, every captured frame (a +cv::Mat `input_frame`) is passed to cv::GComputation::apply(). + +![Example pipeline running on sample video 'vtest.avi'](pics/demo.jpg) + +cv::GComputation::apply() is a polimorphic method which accepts a +variadic number of arguments. Since this computation is defined on one +input, one output, a special overload of cv::GComputation::apply() is +used to pass input data and get output data. + +Internally, cv::GComputation::apply() compiles the captured graph for +the given input parameters and executes the compiled graph on data +immediately. + +There is a number important concepts can be outlines with this example: +* Graph declaration and graph execution are distinct steps; +* Graph is built implicitly from a sequence of G-API expressions; +* G-API supports function-like calls -- e.g. cv::gapi::resize(), and + operators, e.g operator|() which is used to compute bitwise OR; +* G-API syntax aims to look pure: every operation call within a graph + yields a new result, thus forming a directed acyclic graph (DAG); +* Graph declaration is not bound to any data -- real data objects + (cv::Mat) come into picture after the graph is already declared. + + + +See [tutorials and porting examples](@ref tutorial_table_of_content_gapi) +to learn more on various G-API features and concepts. + + diff --git a/modules/gapi/doc/01-background.markdown b/modules/gapi/doc/01-background.markdown new file mode 100644 index 00000000000..08014d1f673 --- /dev/null +++ b/modules/gapi/doc/01-background.markdown @@ -0,0 +1,76 @@ +# Why Graph API? {#gapi_purposes} + +# Motivation behind G-API {#gapi_intro_why} + +G-API module brings graph-based model of execution to OpenCV. This +chapter briefly describes how this new model can help software +developers in two aspects: optimizing and porting image processing +algorithms. + +## Optimizing with Graph API {#gapi_intro_opt} + +Traditionally OpenCV provided a lot of stand-alone image processing +functions (see modules `core` and `imgproc`). Many of that functions +are well-optimized (e.g. vectorized for specific CPUs, parallel, etc) +but still the out-of-box optimization scope has been limited to a +single function only -- optimizing the whole algorithm built atop of that +functions was a responsibility of a programmer. + +OpenCV 3.0 introduced _Transparent API_ (or _T-API_) which allowed to +offload OpenCV function calls transparently to OpenCL devices and save +on Host/Device data transfers with cv::UMat -- and it was a great step +forward. However, T-API is a dynamic API -- user code still remains +unconstrained and OpenCL kernels are enqueued in arbitrary order, thus +eliminating further pipeline-level optimization potential. + +G-API brings implicit graph model to OpenCV 4.0. Graph model captures +all operations and its data dependencies in a pipeline and so provides +G-API framework with extra information to do pipeline-level +optimizations. + +The cornerstone of graph-based optimizations is _Tiling_. Tiling +allows to break the processing into smaller parts and reorganize +operations to enable data parallelism, improve data locality, and save +memory footprint. Data locality is an especially important aspect of +software optimization due to diffent costs of memory access on modern +computer architectures -- the more data is reused in the first level +cache, the more efficient pipeline is. + +Definitely the aforementioned techniques can be applied manually -- +but it requires extra skills and knowledge of the target platform and +the algorithm implementation changes irrevocably -- becoming more +specific, less flexible, and harder to extend and maintain. + +G-API takes this responsibility and complexity from user and does the +majority of the work by itself, keeping the algorithm code clean from +device or optimization details. This approach has its own limitations, +though, as graph model is a _constrained_ model and not every +algorithm can be represented as a graph, so the G-API scope is limited +only to regular image processing -- various filters, arithmetic, +binary operations, and well-defined geometrical transformations. + +## Porting with Graph API {#gapi_intro_port} + +The essence of G-API is declaring a sequence of operations to run, and +then executing that sequence. G-API is a constrained API, so it puts a +number of limitations on which operations can form a pipeline and +which data these operations may exchange each other. + +This formalization in fact helps to make an algorithm portable. G-API +clearly separates operation _interfaces_ from its _implementations_. + +One operation (_kernel_) may have multiple implementations even for a +single device (e.g., OpenCV-based "reference" implementation and a +tiled optimized implementation, both running on CPU). Graphs (or +_Computations_ in G-API terms) are built only using operation +interfaces, not implementations -- thus the same graph can be executed +on different devices (and, of course, using different optimization +techniques) with little-to-no changes in the graph itself. + +G-API supports plugins (_Backends_) which aggregate logic and +intelligence on what is the best way to execute on a particular +platform. Once a pipeline is built with G-API, it can be parametrized +to use either of the backends (or a combination of it) and so a graph +can be ported easily to a new platform. + +@sa @ref gapi_hld diff --git a/modules/gapi/doc/10-hld-overview.md b/modules/gapi/doc/10-hld-overview.md new file mode 100644 index 00000000000..6de6efa9216 --- /dev/null +++ b/modules/gapi/doc/10-hld-overview.md @@ -0,0 +1,160 @@ +# High-level design overview {#gapi_hld} + +[TOC] + +# G-API High-level design overview + +G-API is a heterogeneous framework and provides an unified API to +program image processing pipelines with a number of supported +backends. + +The key design idea is to keep pipeline code itself platform-neutral +while specifying which kernels to use and which devices to utilize +using extra parameters at graph compile (configuration) time. This +requirement has led to the following architecture: + + + +![G-API framework architecture](pics/gapi_scheme.png) + +There are three layers in this architecture: +* **API Layer** -- this is the top layer, which implements G-API + public interface, its building blocks and semantics. + When user constructs a pipeline with G-API, he interacts with this + layer directly, and the entities the user operates on (like cv::GMat + or cv::GComputation) are provided by this layer. +* **Graph Compiler Layer** -- this is the intermediate layer which + unrolls user computation into a graph and then applies a number of + transformations to it (e.g. optimizations). This layer is built atop + of [ADE Framework](@ref gapi_detail_ade). +* **Backends Layer** -- this is the lowest level layer, which lists a + number of _Backends_. In contrast with the above two layers, + backends are highly coupled with low-level platform details, with + every backend standing for every platform. A backend operates on a + processed graph (coming from the graph compiler) and executes this + graph optimally for a specific platform or device. + +# API layer {#gapi_api_layer} + +API layer is what user interacts with when defining and using a +pipeline (a Computation in G-API terms). API layer defines a set of +G-API _dynamic_ objects which can be used as inputs, outputs, and +intermediate data objects within a graph: +* cv::GMat +* cv::GScalar +* cv::GArray (template class) + +API layer specifies a list of Operations which are defined on these +data objects -- so called kernels. See G-API [core](@ref gapi_core) +and [imgproc](@ref gapi_imgproc) namespaces for details on which +operations G-API provides by default. + +G-API is not limited to these operations only -- users can define +their own kernels easily using a special macro G_TYPED_KERNEL(). + +API layer is also responsible for marshalling and storing operation +parameters on pipeline creation. In addition to the aforementioned +G-API dynamic objects, operations may also accept arbitrary +parameters (more on this [here](@ref gapi_detail_params)), so API +layer captures its values and stores internally upon the moment of +execution. + +Finally, cv::GComputation and cv::GCompiled are the remaining +important components of API layer. The former wraps a series of G-API +expressions into an object (graph), and the latter is a product of +graph _compilation_ (see [this chapter](@ref gapi_detail_compiler) for +details). + +# Graph compiler layer {#gapi_compiler} + +Every G-API computation is compiled before it executes. Compilation +process is triggered in two ways: +* _implicitly_, when cv::GComputation::apply() is used. In this case, + graph compilation is then immediately followed by execution. +* _explicitly_, when cv::GComputation::compile() is used. In this case, + a cv::GCompiled object is returned which then can be invoked as a + C++ functor. + +The first way is recommended for cases when input data format is not +known in advance -- e.g. when it comes from an arbitrary input file. +The second way is recommended for deployment (production) scenarios +where input data characteristics are usually predefined. + +Graph compilation process is built atop of ADE Framework. Initially, a +bipartite graph is generated from expressions captured by API layer. +This graph contains nodes of two types: _Data_ and _Operations_. Graph +always starts and ends with a Data node(s), with Operations nodes +in-between. Every Operation node has inputs and outputs, both are Data +nodes. + +After the initial graph is generated, it is actually processed by a +number of graph transformations, called _passes_. ADE Framework acts +as a compiler pass management engine, and passes are written +specifically for G-API. + +There are different passes which check graph validity, refine details +on operations and data, organize nodes into clusters ("Islands") based +on affinity or user-specified regioning[TBD], and more. Backends also +are able to inject backend-specific passes into the compilation +process, see more on this in the [dedicated chapter](@ref gapi_detail_meta). + +Result of graph compilation is a compiled object, represented by class +cv::GCompiled. A new cv::GCompiled object is always created regardless +if there was an explicit or implicit compilation request (see +above). Actual graph execution happens within cv::GCompiled and is +determined by backends which participated in the graph compilation. + +@sa cv::GComputation::apply(), cv::GComputation::compile(), cv::GCompiled + +# Backends layer {#gapi_backends} + +The above diagram lists two backends, _OpenCV_ and _Fluid_. _OpenCV_ +is so-called "reference backend", which implements G-API operations +using plain old OpenCV functions. This backend is useful for +prototyping on a familiar development system. _Fluid_ is a plugin for +cache-efficient execution on CPU -- it implements a different +execution policy and operates with its own, special kernels. Fluid +backend allows to achieve less memory footprint and better memory +locality when running on CPU. + +There may be more backends available, e.g. Halide, OpenCL, etc. -- +G-API provides an uniform internal API to develop backends so any +enthusiast or a company are free to scale G-API on a new platform or +accelerator. In terms of OpenCV infrastructure, every new backend is a +new distinct OpenCV module, which extends G-API when build as a part +of OpenCV. + +# Graph execution {#gapi_compiled} + +The way graph executed is defined by backends selected for +compilation. In fact, every backend builds its own execution script as +the final stage of graph compilation process, when an executable +(compiled) object is being generated. For example, in OpenCV backend, +this script is just a topologically-sorted sequence of OpenCV +functions to call; for Fluid backend, it is a similar thing -- a +topologically sorted list of _Agents_ processing lines of input on +every iteration. + +Graph execution is triggered in two ways: +* via cv::GComputation::apply(), with graph compiled in-place exactly + for the given input data; +* via cv::GCompiled::operator()(), when the graph has been precompiled. + +Both methods are polimorphic and take a variadic number of arguments, +with validity checks performed in runtime. If a number, shapes, and +formats of passed data objects differ from expected, a runtime +exception is thrown. G-API also provides _typed_ wrappers to move +these checks to the compile time -- see `cv::GComputationT<>`. + +G-API graph execution is declared stateless -- it means that a +compiled functor (cv::GCompiled) acts like a pure C++ function and +provides the same result for the same set of input arguments. + +Both execution methods take \f$N+M\f$ parameters, where \f$N\f$ is a +number of inputs, and \f$M\f$ is a number of outputs on which a +cv::GComputation is defined. Note that while G-API types (cv::GMat, +etc) are used in definition, the execution methods accept OpenCV's +traditional data types (like cv::Mat) which hold actual data -- see +table in [parameter marshalling](@ref gapi_detail_params). + +@sa @ref gapi_impl, @ref gapi_kernel_api diff --git a/modules/gapi/doc/20-kernel-api.markdown b/modules/gapi/doc/20-kernel-api.markdown new file mode 100644 index 00000000000..9a7cf39f678 --- /dev/null +++ b/modules/gapi/doc/20-kernel-api.markdown @@ -0,0 +1,188 @@ +# Kernel API {#gapi_kernel_api} + +[TOC] + +# G-API Kernel API + +The core idea behind G-API is portability -- a pipeline built with +G-API must be portable (or at least able to be portable). It means +that either it works out-of-the box when compiled for new platform, +_or_ G-API provides necessary tools to make it running there, with +little-to-no changes in the algorithm itself. + +This idea can be achieved by separating kernel interface from its +implementation. Once a pipeline is built using kernel interfaces, it +becomes implementation-neutral -- the implementation details +(i.e. which kernels to use) are passed on a separate stage (graph +compilation). + +Kernel-implementation hierarchy may look like: + +@dot Kernel API/implementation hierarchy example +digraph { + rankdir=BT; + node [shape=record]; + + ki_a [label="{ interface\nA}"]; + ki_b [label="{ interface\nB}"]; + + {rank=same; ki_a ki_b}; + + "CPU::A" -> ki_a [dir="forward"]; + "OpenCL::A" -> ki_a [dir="forward"]; + "Halide::A" -> ki_a [dir="forward"]; + + "CPU::B" -> ki_b [dir="forward"]; + "OpenCL::B" -> ki_b [dir="forward"]; + "Halide::B" -> ki_b [dir="forward"]; +} +@enddot + +A pipeline itself then can be expressed only in terms of `A`, `B`, and +so on, and choosing which implementation to use in execution becomes +an external parameter. + +# Defining a kernel {#gapi_defining_kernel} + +G-API provides a macro to define a new kernel interface -- +G_TYPED_KERNEL(): + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_api + +This macro is a shortcut to a new type definition. It takes three +arguments to register a new type, and requires type body to be present +(see [below](@ref gapi_kernel_supp_info)). The macro arguments are: +1. Kernel interface name -- also serves as a name of new type defined + with this macro; +2. Kernel signature -- an `std::function<>`-like signature which defines + API of the kernel; +3. Kernel's unique name -- used to identify kernel when its type + informattion is stripped within the system. + +Kernel declaration may be seen as function declaration -- in both cases +a new entity must be used then according to the way it was defined. + +Kernel signature defines kernel's usage syntax -- which parameters +it takes during graph construction. Implementations can also use this +signature to derive it into backend-specific callback signatures (see +next chapter). + +Kernel may accept values of any type, and G-API _dynamic_ types are +handled in a special way. All other types are opaque to G-API and +passed to kernel in `outMeta()` or in execution callbacks as-is. + +Kernel's return value can _only_ be of G-API dynamic type -- cv::GMat, +cv::GScalar, or `cv::GArray`. If an operation has more than one +output, it should be wrapped into an `std::tuple<>` (which can contain +only mentioned G-API types). Arbitrary-output-number operations are +not supported. + +Once a kernel is defined, it can be used in pipelines with special, +G-API-supplied method "::on()". This method has the same signature as +defined in kernel, so this code: + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_on + +is a perfectly legal construction. This example has some verbosity, +though, so usually a kernel declaration comes with a C++ function +wrapper ("factory method") which enables optional parameters, more +compact syntax, Doxygen comments, etc: + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_wrap + +so now it can be used like: + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_wrap_call + +# Extra information {#gapi_kernel_supp_info} + +In the current version, kernel declaration body (everything within the +curly braces) must contain a static function `outMeta()`. This function +establishes a functional dependency between operation's input and +output metadata. + +_Metadata_ is an information about data kernel operates on. Since +non-G-API types are opaque to G-API, G-API cares only about `G*` data +descriptors (i.e. dimensions and format of cv::GMat, etc). + +`outMeta()` is also an example of how kernel's signature can be +transformed into a derived callback -- note that in this example, +`outMeta()` signature exactly follows the kernel signature (defined +within the macro) but is different -- where kernel expects cv::GMat, +`outMeta()` takes and returns cv::GMatDesc (a G-API structure metadata +for cv::GMat). + +The point of `outMeta()` is to propagate metadata information within +computation from inputs to outputs and infer metadata of internal +(intermediate, temporary) data objects. This information is required +for further pipeline optimizations, memory allocation, and other +operations done by G-API framework during graph compilation. + + + +# Implementing a kernel {#gapi_kernel_implementing} + +Once a kernel is declared, its interface can be used to implement +versions of this kernel in different backends. This concept is +naturally projected from object-oriented programming +"Interface/Implementation" idiom: an interface can be implemented +multiple times, and different implementations of a kernel should be +substitutable with each other without breaking the algorithm +(pipeline) logic (Liskov Substitution Principle). + +Every backend defines its own way to implement a kernel interface. +This way is regular, though -- whatever plugin is, its kernel +implementation must be "derived" from a kernel interface type. + +Kernel implementation are then organized into _kernel +packages_. Kernel packages are passed to cv::GComputation::compile() +as compile arguments, with some hints to G-API on how to select proper +kernels (see more on this in "Heterogeneity"[TBD]). + +For example, the aforementioned `Filter2D` is implemented in +"reference" CPU (OpenCV) plugin this way (*NOTE* -- this is a +simplified form with improper border handling): + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_ocv + +Note how CPU (OpenCV) plugin has transformed the original kernel +signature: +- Input cv::GMat has been substituted with cv::Mat, holding actual input + data for the underlying OpenCV function call; +- Output cv::GMat has been transformed into extra output parameter, thus + `GCPUFilter2D::run()` takes one argument more than the original + kernel signature. + +The basic intuition for kernel developer here is _not to care_ where +that cv::Mat objects come from instead of the original cv::GMat -- and +just follow the signature conventions defined by the plugin. G-API +will call this method during execution and supply all the necessary +information (and forward the original opaque data as-is). + +# Compound kernels {#gapi_kernel_compound} + +Sometimes kernel is a single thing only on API level. It is convenient +for users, but on a particular implementation side it would be better to +have multiple kernels (a subgraph) doing the thing instead. An example +is goodFeaturesToTrack() -- while in OpenCV backend it may remain a +single kernel, with Fluid it becomes compound -- Fluid can handle Harris +response calculation but can't do sparse non-maxima suppression and +point extraction to an STL vector: + + + +A compound kernel _implementation_ can be defined using a generic +macro GAPI_COMPOUND_KERNEL(): + +@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp compound + + + + +It is important to distinguish a compound kernel from G-API high-order +function, i.e. a C++ function which looks like a kernel but in fact +generates a subgraph. The core difference is that a compound kernel is +an _implementation detail_ and a kernel implementation may be either +compound or not (depending on backend capabilities), while a +high-order function is a "macro" in terms of G-API and so cannot act as +an interface which then needs to be implemented by a backend. diff --git a/modules/gapi/doc/30-implementation.markdown b/modules/gapi/doc/30-implementation.markdown new file mode 100644 index 00000000000..cdb5df413be --- /dev/null +++ b/modules/gapi/doc/30-implementation.markdown @@ -0,0 +1,29 @@ +# Implementation details {#gapi_impl} + +[TOC] + +# G-API Implementation details + +@note this section is still in progress. + +# API layer {#gapi_detail_api} + +## Expression unrolling {#gapi_detail_expr} + +## Parameter marshalling {#gapi_detail_params} + +## Operations representation {#gapi_detail_operations} + +# Graph compiler {#gapi_detail_compiler} + +## ADE basics {#gapi_detail_ade} + +## Graph model representation {#gapi_detail_gmodel} + +## G-API metadata and passes {#gapi_detail_meta} + +# Backends {#gapi_detail_backends} + +## Backend scope of work {#gapi_backend_scope} + +## Graph transformation {#gapi_backend_pass} diff --git a/modules/gapi/doc/pics/demo.jpg b/modules/gapi/doc/pics/demo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..742d135f7a89e051cf505daa0b8ed265965bb821 GIT binary patch literal 65973 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

0af`4+vUW6$eSFHDg&n>>$ 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 KT$!wC(Ek94r|^~l literal 0 HcmV?d00001 diff --git a/modules/gapi/doc/pics/render_example.png b/modules/gapi/doc/pics/render_example.png new file mode 100644 index 0000000000000000000000000000000000000000..b2675988b3da13bf447fd30c1aced7721ff9c98f GIT binary patch literal 4392 zcmeHLYfw{X8m8;4m?G7NU1^cR3WOxcZABJQFiYf?K)D1hP*^NQxrl&(Q>C;p?!-&R z1&V|KE_Y1Z-L)#P6=K=J24>MffyHsXfKV=}WweGyT49ZGpD*Wxg~Fem*&o}P{y3bI z^L^j@KJWWH@B4iRNkmwX`HNOBnwXfF3xfScCMGX@iRbz?Kf~X+Dvp(j$$E{z-*5NP z7ry!2FVo_8!|(j#{Y|HaS_14R>&td}Z0_~^eBYW=uih4J&V0yonzfz(v?e0JW@dac zVY8E5D*Bg2xk$);CJ}PwQoTQ32n!qWLe7iCi=n!Y@nSlBH(t!Lf8^7(Ubjmo54Y8#XsXTIvWWdQkg$u?*-!*i`}@5{)B;?K{vY3trJUsc$N@KxmxdrHTWG57^Z zOyNj@^}PKXU027r$cHxNo1Ri$Dh7`?w)n5X3$NQnar(}i?lWx?)xk|zK9=N_>D^+r zT5l6s$#Ftrs^SCgrdV6MCmF!krZ zyOh|nu`M1(u2`1e;a8r5S)ZrbQv+|V+vxaNb}#EMiS_vlOAw2`om$fIw&&Ve7pK3>ctN@%kUx{%^~uo7YN5X_Zd7$I-| ze)8I$PfnVb-D}@}0!!)^mNa9@0cwfI`4h+V{xqjl<|%Tz3Vu(jR7Xp4pL@BXvv z#P!UPEdxLtYD8QCw$kIMy!aq59>%=Hx4=mT<>WD(^e;F`<`o})iyCM}jJ5yY1KQ5- zCxdON&|A6@dZ01%6bzWsIT^-)z%VdWC$>}#GZQ$^%kiKPTMBv1f(TW!I7#(O7AM!C z!!K`_(oEfmLA$5f=T=diR-qbZ9Sq8u4&^s>I5tF%x-~==v$m>kvZ^D5%3&$bdE8W; z!SLX+nO16lep4Ta-%iWx&Kq674X5PY@32*sjv3Ez#qgWrqm|xj+Y0xSS~g2p-{F$D zlf11CPLHmNTHsXO`l86JVvR0(AMvyAK z1qe?ut63bVXcPx>c@EZCWoUi=p&$5c*+L`bhzLPezfp z0JEGMA}??nB30|~?Aj1X10hxd)BdYeMDm-YmN0r&95{cE+0VTQtf!e)a@;H!MJ?A> z6#rpRkx(|s$wJCVUXb6)i z@QbqGymG}E8Ig$VM1(s8;9WWsrOJmCy=8b<2M5)`a~C-p0IWfn6k=E59L5JZklX%J zbe{}bsVSao4?EIIRf=HF+rTAQDrWVa>j+W?AcQuM-gnMA|7!Gc&Qy_p%xDpUz zNl{vY+y}9gmIu)CT&eC|BZv{Ep>T)__N_uY9zUstD~@GF)SzFbflPI7`O-wJ!sd`A@Jagp4A;0Xblk$_04 zg+b$Xc!cAEFHjL@=&dacxpP|5pw5ctr6>--6QVvW!NCq(gn~ty2Wj_dh7{slRn{5z zlf?7}s^+xmh^3s4<#m*Q^pf%)GHA#^%@AM_(-6NQCO9UU!>u1q6)4IQs&VOFVmyX? zpjkrJ3_q^~0x+ipkarJlI)-Y~be}Wi2>lr8A|jy89-&AfEr@s{lRymdln)%Fy&egs zBz79VHRRN={0Lxdntjo=WqMr77SaAvm!%+Y2&?rHYi&jcBZb{nVk$mR1H+JIjM?2nS8V*Xc?D6B6PxgOm2gt=P z&<5k04Jg*?RwCh+vW@gNky{T-QkI$v< zwsm8%6OVeMWplde;)SD9dj3x{f6CJTN+DCFh1V^@owEmgLq>8!=yRk<^{KD+TIZC6 zEo;AD%gR*9>7yA}eb**$YOl82B4i|YfY~-OR7QVTG<71z z7vAL{oB0Z#Z@ZaKMq0aF+US!mR~|oXU!n`CJ^HpJ|55COEY46z9QonNW2SmmxKDGp z?-S?jUKd~L+wRJ@S64kh!>)~)sLhwPt7Rw$$$BC8{i9zVTN(hq`gSY>&z0+HmI0TF z^kK_D // G-API framework header +#include // cv::gapi::blur() +#include // cv::imread/imwrite + +int main(int argc, char *argv[]) { + if (argc < 3) return 1; + + cv::GMat in; // Express the graph: + cv::GMat out = cv::gapi::blur(in, cv::Size(3,3)); // `out` is a result of `blur` of `in` + + cv::Mat in_mat = cv::imread(argv[1]); // Get the real data + cv::Mat out_mat; // Output buffer (may be empty) + + cv::GComputation(cv::GIn(in), cv::GOut(out)) // Declare a graph from `in` to `out` + .apply(cv::gin(in_mat), cv::gout(out_mat)); // ...and run it immediately + + cv::imwrite(argv[2], out_mat); // Save the result + return 0; +} +#+END_SRC + +** The code is worth a thousand words + :PROPERTIES: + :BEAMER_opt: shrink=42 + :END: + +*** Traditional OpenCV :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.45 + :END: +#+BEGIN_SRC C++ +#include +#include + +#include + +int main(int argc, char *argv[]) { + using namespace cv; + if (argc != 3) return 1; + + Mat in_mat = imread(argv[1]); + Mat gx, gy; + + Sobel(in_mat, gx, CV_32F, 1, 0); + Sobel(in_mat, gy, CV_32F, 0, 1); + + Mat mag, out_mat; + sqrt(gx.mul(gx) + gy.mul(gy), mag); + mag.convertTo(out_mat, CV_8U); + + imwrite(argv[2], out_mat); + return 0; +} +#+END_SRC + +*** OpenCV G-API :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.5 + :END: +#+BEGIN_SRC C++ +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + using namespace cv; + if (argc != 3) return 1; + + GMat in; + GMat gx = gapi::Sobel(in, CV_32F, 1, 0); + GMat gy = gapi::Sobel(in, CV_32F, 0, 1); + GMat mag = gapi::sqrt( gapi::mul(gx, gx) + + gapi::mul(gy, gy)); + GMat out = gapi::convertTo(mag, CV_8U); + GComputation sobel(GIn(in), GOut(out)); + + Mat in_mat = imread(argv[1]), out_mat; + sobel.apply(in_mat, out_mat); + imwrite(argv[2], out_mat); + return 0; +} +#+END_SRC + +** The code is worth a thousand words (cont'd) +# FIXME: sections!!! + +*** What we have just learned? + +- G-API functions mimic their traditional OpenCV ancestors; +- No real data is required to construct a graph; +- Graph construction and graph execution are separate steps. + +*** What else? + +- Graph is first /expressed/ and then /captured/ in an object; +- Graph constructor defines /protocol/; user can pass vectors of + inputs/outputs like + #+BEGIN_SRC C++ +cv::GComputation(cv::GIn(...), cv::GOut(...)) + #+END_SRC +- Calls to ~.apply()~ must conform to graph's protocol + +** On data objects + +Graph *protocol* defines what arguments a computation was defined on +(both inputs and outputs), and what are the *shapes* (or types) of +those arguments: + + | *Shape* | *Argument* | Size | + |--------------+------------------+-----------------------------| + | ~GMat~ | ~Mat~ | Static; defined during | + | | | graph compilation | + |--------------+------------------+-----------------------------| + | ~GScalar~ | ~Scalar~ | 4 x ~double~ | + |--------------+------------------+-----------------------------| + | ~GArray~ | ~std::vector~ | Dynamic; defined in runtime | + |--------------+------------------+-----------------------------| + | ~GOpaque~ | ~T~ | Static, ~sizeof(T)~ | + +~GScalar~ may be value-initialized at construction time to allow + expressions like ~GMat a = 2*(b + 1)~. + +** On operations and kernels + :PROPERTIES: + :BEAMER_opt: shrink=22 + :END: + +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.45 + :END: + +- Graphs are built with *Operations* over virtual *Data*; +- *Operations* define interfaces (literally); +- *Kernels* are implementations to *Operations* (like in OOP); +- An *Operation* is platform-agnostic, a *kernel* is not; +- *Kernels* are implemented for *Backends*, the latter provide + APIs to write kernels; +- Users can /add/ their *own* operations and kernels, + and also /redefine/ "standard" kernels their *own* way. + +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.45 + :END: + +#+BEGIN_SRC dot :file "000-ops-kernels.eps" :cmdline "-Kdot -Teps" +digraph G { +node [shape=box]; +rankdir=BT; + +Gr [label="Graph"]; +Op [label="Operation\nA"]; +{rank=same +Impl1 [label="Kernel\nA:2"]; +Impl2 [label="Kernel\nA:1"]; +} + +Op -> Gr [dir=back, label="'consists of'"]; +Impl1 -> Op []; +Impl2 -> Op [label="'is implemented by'"]; + +node [shape=note,style=dashed]; +{rank=same +Op; +CommentOp [label="Abstract:\ndeclared via\nG_API_OP()"]; +} +{rank=same +Comment1 [label="Platform:\ndefined with\nOpenCL backend"]; +Comment2 [label="Platform:\ndefined with\nOpenCV backend"]; +} + +CommentOp -> Op [constraint=false, style=dashed, arrowhead=none]; +Comment1 -> Impl1 [style=dashed, arrowhead=none]; +Comment2 -> Impl2 [style=dashed, arrowhead=none]; +} +#+END_SRC + +** On operations and kernels (cont'd) + +*** Defining an operation + +- A type name (every operation is a C++ type); +- Operation signature (similar to ~std::function<>~); +- Operation identifier (a string); +- Metadata callback -- describe what is the output value format(s), + given the input and arguments. +- Use ~OpType::on(...)~ to use a new kernel ~OpType~ to construct graphs. + +#+LaTeX: {\footnotesize +#+BEGIN_SRC C++ +G_API_OP(GSqrt,,"org.opencv.core.math.sqrt") { + static GMatDesc outMeta(GMatDesc in) { return in; } +}; +#+END_SRC +#+LaTeX: } + +** On operations and kernels (cont'd) + +*** ~GSqrt~ vs. ~cv::gapi::sqrt()~ + +- How a *type* relates to a *functions* from the example? +- These functions are just wrappers over ~::on~: + #+LaTeX: {\scriptsize + #+BEGIN_SRC C++ + G_API_OP(GSqrt,,"org.opencv.core.math.sqrt") { + static GMatDesc outMeta(GMatDesc in) { return in; } + }; + GMat gapi::sqrt(const GMat& src) { return GSqrt::on(src); } + #+END_SRC + #+LaTeX: } +- Why -- Doxygen, default parameters, 1:n mapping: + #+LaTeX: {\scriptsize + #+BEGIN_SRC C++ + cv::GMat custom::unsharpMask(const cv::GMat &src, + const int sigma, + const float strength) { + cv::GMat blurred = cv::gapi::medianBlur(src, sigma); + cv::GMat laplacian = cv::gapi::Laplacian(blurred, CV_8U); + return (src - (laplacian * strength)); + } + #+END_SRC + #+LaTeX: } + +** On operations and kernels (cont'd) + +*** Implementing an operation + +- Depends on the backend and its API; +- Common part for all backends: refer to operation being implemented + using its /type/. + +*** OpenCV backend +- OpenCV backend is the default one: OpenCV kernel is a wrapped OpenCV + function: + #+LaTeX: {\footnotesize + #+BEGIN_SRC C++ + GAPI_OCV_KERNEL(GCPUSqrt, cv::gapi::core::GSqrt) { + static void run(const cv::Mat& in, cv::Mat &out) { + cv::sqrt(in, out); + } + }; + #+END_SRC + #+LaTeX: } + +** Operations and Kernels (cont'd) +# FIXME!!! + +*** Fluid backend + +- Fluid backend operates with row-by-row kernels and schedules its + execution to optimize data locality: + #+LaTeX: {\footnotesize + #+BEGIN_SRC C++ + GAPI_FLUID_KERNEL(GFluidSqrt, cv::gapi::core::GSqrt, false) { + static const int Window = 1; + static void run(const View &in, Buffer &out) { + hal::sqrt32f(in .InLine (0) + out.OutLine(0), + out.length()); + } + }; + #+END_SRC + #+LaTeX: } +- Note ~run~ changes signature but still is derived from the operation + signature. + +** Operations and Kernels (cont'd) + +*** Specifying which kernels to use + +- Graph execution model is defined by kernels which are available/used; +- Kernels can be specified via the graph compilation arguments: + #+LaTeX: {\footnotesize + #+BEGIN_SRC C++ + #include + #include + ... + auto pkg = cv::gapi::combine(cv::gapi::core::fluid::kernels(), + cv::gapi::imgproc::fluid::kernels()); + sobel.apply(in_mat, out_mat, cv::compile_args(pkg)); + #+END_SRC + #+LaTeX: } +- Users can combine kernels of different backends and G-API will partition + the execution among those automatically. + +** Heterogeneity in G-API + :PROPERTIES: + :BEAMER_opt: shrink=35 + :END: +*** Automatic subgraph partitioning in G-API +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.18 + :END: + +#+BEGIN_SRC dot :file "010-hetero-init.eps" :cmdline "-Kdot -Teps" +digraph G { +rankdir=TB; +ranksep=0.3; + +node [shape=box margin=0 height=0.25]; +A; B; C; + +node [shape=ellipse]; +GMat0; +GMat1; +GMat2; +GMat3; + +GMat0 -> A -> GMat1 -> B -> GMat2; +GMat2 -> C; +GMat0 -> C -> GMat3 + +subgraph cluster {style=invis; A; GMat1; B; GMat2; C}; +} +#+END_SRC + +The initial graph: operations are not resolved yet. + +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.18 + :END: + +#+BEGIN_SRC dot :file "011-hetero-homo.eps" :cmdline "-Kdot -Teps" +digraph G { +rankdir=TB; +ranksep=0.3; + +node [shape=box margin=0 height=0.25]; +A; B; C; + +node [shape=ellipse]; +GMat0; +GMat1; +GMat2; +GMat3; + +GMat0 -> A -> GMat1 -> B -> GMat2; +GMat2 -> C; +GMat0 -> C -> GMat3 + +subgraph cluster {style=filled;color=azure2; A; GMat1; B; GMat2; C}; +} +#+END_SRC + +All operations are handled by the same backend. + +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.18 + :END: + +#+BEGIN_SRC dot :file "012-hetero-a.eps" :cmdline "-Kdot -Teps" +digraph G { +rankdir=TB; +ranksep=0.3; + +node [shape=box margin=0 height=0.25]; +A; B; C; + +node [shape=ellipse]; +GMat0; +GMat1; +GMat2; +GMat3; + +GMat0 -> A -> GMat1 -> B -> GMat2; +GMat2 -> C; +GMat0 -> C -> GMat3 + +subgraph cluster_1 {style=filled;color=azure2; A; GMat1; B; } +subgraph cluster_2 {style=filled;color=ivory2; C}; +} +#+END_SRC + +~A~ & ~B~ are of backend ~1~, ~C~ is of backend ~2~. + +*** :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.18 + :END: + +#+BEGIN_SRC dot :file "013-hetero-b.eps" :cmdline "-Kdot -Teps" +digraph G { +rankdir=TB; +ranksep=0.3; + +node [shape=box margin=0 height=0.25]; +A; B; C; + +node [shape=ellipse]; +GMat0; +GMat1; +GMat2; +GMat3; + +GMat0 -> A -> GMat1 -> B -> GMat2; +GMat2 -> C; +GMat0 -> C -> GMat3 + +subgraph cluster_1 {style=filled;color=azure2; A}; +subgraph cluster_2 {style=filled;color=ivory2; B}; +subgraph cluster_3 {style=filled;color=azure2; C}; +} +#+END_SRC + +~A~ & ~C~ are of backend ~1~, ~B~ is of backend ~2~. + +** Heterogeneity in G-API + +*** Heterogeneity summary + +- G-API automatically partitions its graph in subgraphs (called "islands") + based on the available kernels; +- Adjacent kernels taken from the same backend are "fused" into the same + "island"; +- G-API implements a two-level execution model: + - Islands are executed at the top level by a G-API's *Executor*; + - Island internals are run at the bottom level by its *Backend*; +- G-API fully delegates the low-level execution and memory management to backends. + +* Inference and Streaming + +** Inference with G-API + +*** In-graph inference example + +- Starting with OpencV 4.2 (2019), G-API allows to integrate ~infer~ + operations into the graph: + #+LaTeX: {\scriptsize + #+BEGIN_SRC C++ + G_API_NET(ObjDetect, , "pdf.example.od"); + + cv::GMat in; + cv::GMat blob = cv::gapi::infer(bgr); + cv::GOpaque size = cv::gapi::streaming::size(bgr); + cv::GArray objs = cv::gapi::streaming::parseSSD(blob, size); + cv::GComputation pipelne(cv::GIn(in), cv::GOut(objs)); + #+END_SRC + #+LaTeX: } +- Starting with OpenCV 4.5 (2020), G-API will provide more streaming- + and NN-oriented operations out of the box. + +** Inference with G-API + +*** What is the difference? + +- ~ObjDetect~ is not an operation, ~cv::gapi::infer~ is; +- ~cv::gapi::infer~ is a *generic* operation, where ~T=ObjDetect~ describes + the calling convention: + - How many inputs the network consumes, + - How many outputs the network produces. +- Inference data types are ~GMat~ only: + - Representing an image, then preprocessed automatically; + - Representing a blob (n-dimensional ~Mat~), then passed as-is. +- Inference *backends* only need to implement a single generic operation ~infer~. + +** Inference with G-API + +*** But how does it run? + +- Since ~infer~ is an *Operation*, backends may provide *Kernels* implementing it; +- The only publicly available inference backend now is *OpenVINO™*: + - Brings its ~infer~ kernel atop of the Inference Engine; +- NN model data is passed through G-API compile arguments (like kernels); +- Every NN backend provides its own structure to configure the network (like + a kernel API). + +** Inference with G-API + +*** Passing OpenVINO™ parameters to G-API + +- ~ObjDetect~ example: + #+LaTeX: {\footnotesize + #+BEGIN_SRC C++ + auto face_net = cv::gapi::ie::Params { + face_xml_path, // path to the topology IR + face_bin_path, // path to the topology weights + face_device_string, // OpenVINO plugin (device) string + }; + auto networks = cv::gapi::networks(face_net); + pipeline.compile(.., cv::compile_args(..., networks)); + #+END_SRC + #+LaTeX: } +- ~AgeGender~ requires binding Op's outputs to NN layers: + #+LaTeX: {\footnotesize + #+BEGIN_SRC C++ + auto age_net = cv::gapi::ie::Params { + ... + }.cfgOutputLayers({"age_conv3", "prob"}); // array ! + #+END_SRC + #+LaTeX: } + +** Streaming with G-API + +#+BEGIN_SRC dot :file 020-fd-demo.eps :cmdline "-Kdot -Teps" +digraph { + rankdir=LR; + node [shape=box]; + + cap [label=Capture]; + dec [label=Decode]; + res [label=Resize]; + cnn [label=Infer]; + vis [label=Visualize]; + + cap -> dec; + dec -> res; + res -> cnn; + cnn -> vis; +} +#+END_SRC +Anatomy of a regular video analytics application + +** Streaming with G-API + +#+BEGIN_SRC dot :file 021-fd-serial.eps :cmdline "-Kdot -Teps" +digraph { + node [shape=box margin=0 width=0.3 height=0.4] + nodesep=0.2; + rankdir=LR; + + subgraph cluster0 { + colorscheme=blues9 + pp [label="..." shape=plaintext]; + v0 [label=V]; + label="Frame N-1"; + color=7; + } + + subgraph cluster1 { + colorscheme=blues9 + c1 [label=C]; + d1 [label=D]; + r1 [label=R]; + i1 [label=I]; + v1 [label=V]; + label="Frame N"; + color=6; + } + + subgraph cluster2 { + colorscheme=blues9 + c2 [label=C]; + nn [label="..." shape=plaintext]; + label="Frame N+1"; + color=5; + } + + c1 -> d1 -> r1 -> i1 -> v1; + + pp-> v0; + v0 -> c1 [style=invis]; + v1 -> c2 [style=invis]; + c2 -> nn; +} +#+END_SRC +Serial execution of the sample video analytics application + +** Streaming with G-API + :PROPERTIES: + :BEAMER_opt: shrink + :END: + +#+BEGIN_SRC dot :file 022-fd-pipelined.eps :cmdline "-Kdot -Teps" +digraph { + nodesep=0.2; + ranksep=0.2; + node [margin=0 width=0.4 height=0.2]; + node [shape=plaintext] + Camera [label="Camera:"]; + GPU [label="GPU:"]; + FPGA [label="FPGA:"]; + CPU [label="CPU:"]; + Time [label="Time:"]; + t6 [label="T6"]; + t7 [label="T7"]; + t8 [label="T8"]; + t9 [label="T9"]; + t10 [label="T10"]; + tnn [label="..."]; + + node [shape=box margin=0 width=0.4 height=0.4 colorscheme=blues9] + node [color=9] V3; + node [color=8] F4; V4; + node [color=7] DR5; F5; V5; + node [color=6] C6; DR6; F6; V6; + node [color=5] C7; DR7; F7; V7; + node [color=4] C8; DR8; F8; + node [color=3] C9; DR9; + node [color=2] C10; + + {rank=same; rankdir=LR; Camera C6 C7 C8 C9 C10} + Camera -> C6 -> C7 -> C8 -> C9 -> C10 [style=invis]; + + {rank=same; rankdir=LR; GPU DR5 DR6 DR7 DR8 DR9} + GPU -> DR5 -> DR6 -> DR7 -> DR8 -> DR9 [style=invis]; + + C6 -> DR5 [style=invis]; + C6 -> DR6 [constraint=false]; + C7 -> DR7 [constraint=false]; + C8 -> DR8 [constraint=false]; + C9 -> DR9 [constraint=false]; + + {rank=same; rankdir=LR; FPGA F4 F5 F6 F7 F8} + FPGA -> F4 -> F5 -> F6 -> F7 -> F8 [style=invis]; + + DR5 -> F4 [style=invis]; + DR5 -> F5 [constraint=false]; + DR6 -> F6 [constraint=false]; + DR7 -> F7 [constraint=false]; + DR8 -> F8 [constraint=false]; + + {rank=same; rankdir=LR; CPU V3 V4 V5 V6 V7} + CPU -> V3 -> V4 -> V5 -> V6 -> V7 [style=invis]; + + F4 -> V3 [style=invis]; + F4 -> V4 [constraint=false]; + F5 -> V5 [constraint=false]; + F6 -> V6 [constraint=false]; + F7 -> V7 [constraint=false]; + + {rank=same; rankdir=LR; Time t6 t7 t8 t9 t10 tnn} + Time -> t6 -> t7 -> t8 -> t9 -> t10 -> tnn [style=invis]; + + CPU -> Time [style=invis]; + V3 -> t6 [style=invis]; + V4 -> t7 [style=invis]; + V5 -> t8 [style=invis]; + V6 -> t9 [style=invis]; + V7 -> t10 [style=invis]; +} +#+END_SRC +Pipelined execution for the video analytics application + +** Streaming with G-API: Example + +**** Serial mode (4.0) :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.45 + :END: +#+LaTeX: {\tiny +#+BEGIN_SRC C++ +pipeline = cv::GComputation(...); + +cv::VideoCapture cap(input); +cv::Mat in_frame; +std::vector out_faces; + +while (cap.read(in_frame)) { + pipeline.apply(cv::gin(in_frame), + cv::gout(out_faces), + cv::compile_args(kernels, + networks)); + // Process results + ... +} +#+END_SRC +#+LaTeX: } + +**** Streaming mode (since 4.2) :B_block:BMCOL: + :PROPERTIES: + :BEAMER_env: block + :BEAMER_col: 0.45 + :END: +#+LaTeX: {\tiny +#+BEGIN_SRC C++ +pipeline = cv::GComputation(...); + +auto in_src = cv::gapi::wip::make_src + (input) +auto cc = pipeline.compileStreaming + (cv::compile_args(kernels, networks)) +cc.setSource(cv::gin(in_src)); +cc.start(); + +std::vector out_faces; +while (cc.pull(cv::gout(out_faces))) { + // Process results + ... +} +#+END_SRC +#+LaTeX: } + +**** More information + +#+LaTeX: {\footnotesize +https://opencv.org/hybrid-cv-dl-pipelines-with-opencv-4-4-g-api/ +#+LaTeX: } + +* Latest features +** Latest features +*** Python API + +- Initial Python3 binding is available now in ~master~ (future 4.5); +- Only basic CV functionality is supported (~core~ & ~imgproc~ namespaces, + selecting backends); +- Adding more programmability, inference, and streaming is next. + +** Latest features +*** Python API + +#+LaTeX: {\footnotesize +#+BEGIN_SRC Python +import numpy as np +import cv2 as cv + +sz = (1280, 720) +in1 = np.random.randint(0, 100, sz).astype(np.uint8) +in2 = np.random.randint(0, 100, sz).astype(np.uint8) + +g_in1 = cv.GMat() +g_in2 = cv.GMat() +g_out = cv.gapi.add(g_in1, g_in2) +gr = cv.GComputation(g_in1, g_in2, g_out) + +pkg = cv.gapi.core.fluid.kernels() +out = gr.apply(in1, in2, args=cv.compile_args(pkg)) +#+END_SRC +#+LaTeX: } + +* Understanding the "G-Effect" + +** Understanding the "G-Effect" + +*** What is "G-Effect"? + +- G-API is not only an API, but also an /implementation/; + - i.e. it does some work already! +- We call "G-Effect" any measurable improvement which G-API demonstrates + against traditional methods; +- So far the list is: + - Memory consumption; + - Performance; + - Programmer efforts. + +Note: in the following slides, all measurements are taken on +Intel\textregistered{} Core\texttrademark-i5 6600 CPU. + +** Understanding the "G-Effect" +# FIXME + +*** Memory consumption: Sobel Edge Detector + +- G-API/Fluid backend is designed to minimize footprint: +#+LaTeX: {\footnotesize +| Input | OpenCV | G-API/Fluid | Factor | +| | MiB | MiB | Times | +|-------------+--------+-------------+--------| +| 512 x 512 | 17.33 | 0.59 | 28.9x | +| 640 x 480 | 20.29 | 0.62 | 32.8x | +| 1280 x 720 | 60.73 | 0.72 | 83.9x | +| 1920 x 1080 | 136.53 | 0.83 | 164.7x | +| 3840 x 2160 | 545.88 | 1.22 | 447.4x | +#+LaTeX: } +- The detector itself can be written manually in two ~for~ + loops, but G-API covers cases more complex than that; +- OpenCV code requires changes to shrink footprint. + +** Understanding the "G-Effect" + +*** Performance: Sobel Edge Detector + +- G-API/Fluid backend also optimizes cache reuse: + +#+LaTeX: {\footnotesize +| Input | OpenCV | G-API/Fluid | Factor | +| | ms | ms | Times | +|-------------+--------+-------------+--------| +| 320 x 240 | 1.16 | 0.53 | 2.17x | +| 640 x 480 | 5.66 | 1.89 | 2.99x | +| 1280 x 720 | 17.24 | 5.26 | 3.28x | +| 1920 x 1080 | 39.04 | 12.29 | 3.18x | +| 3840 x 2160 | 219.57 | 51.22 | 4.29x | +#+LaTeX: } + +- The more data is processed, the bigger "G-Effect" is. + +** Understanding the "G-Effect" + +*** Relative speed-up based on cache efficiency + +#+BEGIN_LATEX +\begin{figure} + \begin{tikzpicture} + \begin{axis}[ + xlabel={Image size}, + ylabel={Relative speed-up}, + nodes near coords, + width=0.8\textwidth, + xtick=data, + xticklabels={QVGA, VGA, HD, FHD, UHD}, + height=4.5cm, + ] + + \addplot plot coordinates {(1, 1.0) (2, 1.38) (3, 1.51) (4, 1.46) (5, 1.97)}; + + \end{axis} + \end{tikzpicture} +\end{figure} +#+END_LATEX + +The higher resolution is, the higher relative speed-up is (with +speed-up on QVGA taken as 1.0). + +* Resources on G-API + +** Resources on G-API + :PROPERTIES: + :BEAMER_opt: shrink + :END: +*** Repository + +- https://github.com/opencv/opencv (see ~modules/gapi~) + +*** Article + +- https://opencv.org/hybrid-cv-dl-pipelines-with-opencv-4-4-g-api/ + +*** Documentation + +- https://docs.opencv.org/4.4.0/d0/d1e/gapi.html + +*** Tutorials +- https://docs.opencv.org/4.4.0/df/d7e/tutorial_table_of_content_gapi.html + +* Thank you! diff --git a/modules/gapi/doc/slides/get_sty.sh b/modules/gapi/doc/slides/get_sty.sh new file mode 100755 index 00000000000..0b97cf3eceb --- /dev/null +++ b/modules/gapi/doc/slides/get_sty.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -e + +MTHEME_VER=2fa6084b9d34fec9d2d5470eb9a17d0bf712b6c8 +MTHEME_DIR=mtheme.sty + +function make_sty { + if [ -d "$MTHEME_DIR" ]; then rm -rf "$MTHEME_DIR"; fi + mkdir "$MTHEME_DIR" + + # Download template from Github + tmp_dir=$(mktemp -d) + wget -P "$tmp_dir" -c https://github.com/matze/mtheme/archive/${MTHEME_VER}.tar.gz + pushd "$tmp_dir" + tar -xzvf "$MTHEME_VER.tar.gz" + popd + make -C "$tmp_dir"/mtheme-"$MTHEME_VER" + cp -v "$tmp_dir"/mtheme-"$MTHEME_VER"/*.sty "$MTHEME_DIR" + rm -r "$tmp_dir" + # Put our own .gitignore to ignore this directory completely + echo "*" > "$MTHEME_DIR/.gitignore" +} + +make_sty diff --git a/modules/gapi/doc/slides/ocv_logo.eps b/modules/gapi/doc/slides/ocv_logo.eps new file mode 100644 index 00000000000..67005bd3d02 --- /dev/null +++ b/modules/gapi/doc/slides/ocv_logo.eps @@ -0,0 +1,181 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.14.6 (http://cairographics.org) +%%CreationDate: Wed Dec 12 17:03:17 2018 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 -1 598 739 +%%EndComments +%%BeginProlog +save +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 -1 598 739 +%%EndPageSetup +q 0 -1 598 740 rectclip q +1 0.00392157 0.00392157 rg +225.648 478.363 m 171.051 509.887 144.43 574.156 160.746 635.051 c 177.066 + 695.945 232.254 738.277 295.301 738.277 c 358.348 738.277 413.535 695.945 + 429.855 635.051 c 446.172 574.156 419.551 509.887 364.949 478.363 c 323.008 + 551.008 l 344.73 563.547 355.324 589.117 348.832 613.34 c 342.34 637.566 + 320.383 654.41 295.301 654.41 c 270.219 654.41 248.262 637.566 241.77 613.34 + c 235.277 589.117 245.871 563.547 267.59 551.008 c h +225.648 478.363 m f +0.00392157 0.00392157 1 rg +523.949 444.637 m 578.551 413.113 605.172 348.844 588.855 287.949 c 572.535 + 227.055 517.348 184.723 454.301 184.723 c 391.254 184.723 336.066 227.055 + 319.746 287.949 c 303.43 348.844 330.051 413.113 384.648 444.637 c 426.59 + 371.992 l 404.871 359.453 394.277 333.883 400.77 309.66 c 407.262 285.434 + 429.219 268.59 454.301 268.59 c 479.383 268.59 501.34 285.434 507.832 309.66 + c 514.324 333.883 503.73 359.453 482.008 371.992 c h +523.949 444.637 m f +0.00392157 1 0.00392157 rg +278.602 324 m 278.602 260.953 236.254 205.762 175.359 189.449 c 114.461 + 173.133 50.207 199.762 18.684 254.363 c -12.84 308.961 -3.773 377.922 40.805 + 422.504 c 85.383 467.082 154.352 476.164 208.949 444.637 c 167.008 371.992 + l 145.289 384.535 117.852 380.922 100.117 363.188 c 82.383 345.453 78.773 + 318.016 91.316 296.297 c 103.855 274.574 129.418 263.98 153.645 270.473 + c 177.871 276.961 194.719 298.918 194.719 324 c h +278.602 324 m f +0.0196078 g +39.781 151.301 m 51.57 152.359 63.492 152.352 75.223 150.672 c 82.449 149.391 + 90.121 147.52 95.551 142.25 c 101.242 135.898 102.641 127.078 103.891 118.949 + c 105.941 102.078 105.699 84.969 103.891 68.09 c 102.68 59.852 101.492 +50.949 96.09 44.25 c 90.199 38.27 81.5 36.57 73.52 35.309 c 61.742 33.84 + 49.789 33.5 37.961 34.68 c 29.949 35.5 21.59 36.91 14.77 41.48 c 10.359 + 44.281 7.992 49.219 6.379 54.012 c 3.152 63.988 2.742 74.59 2.301 84.988 + c 2.25 98.73 2.512 112.609 5.191 126.129 c 6.641 132.441 8.402 139.379 +13.73 143.59 c 21.242 149.039 30.789 150.359 39.781 151.301 c h +41.73 132.469 m 51.723 133.27 61.922 133.512 71.801 131.57 c 75.629 130.801 + 80.152 128.941 80.871 124.578 c 83.871 112.309 83.172 99.531 83.289 86.988 + c 82.922 78.07 83.129 68.852 80.141 60.309 c 77.531 54.699 70.422 54.238 + 65.062 53.422 c 54.312 52.809 43.152 52.27 32.723 55.461 c 27.91 56.73 +26.391 61.891 25.652 66.219 c 23.652 79.051 24.301 92.102 24.551 105.031 + c 25.082 112.281 24.992 119.801 27.602 126.691 c 30.59 131.309 36.77 131.719 + 41.73 132.469 c h +41.73 132.469 m f* +147.07 112.219 m 154.23 116.77 163.121 117.512 171.379 116.762 c 179.09 + 116.102 187.652 113.48 191.781 106.379 c 196.711 97.469 196.992 86.941 +197.332 77 c 197.109 66.781 196.922 56.109 192.699 46.609 c 190.289 40.84 + 184.75 37.059 178.82 35.57 c 169.742 33.34 159.762 33.102 151.012 36.719 + c 146.281 38.57 143.012 42.59 140.301 46.711 c 140.301 0 l 120.301 0 l +120.312 38.66 120.281 77.328 120.312 115.988 c 126.781 116.02 133.25 116.02 + 139.711 115.988 c 139.492 112.012 139.27 108.039 139.16 104.051 c 141.562 + 106.98 143.789 110.199 147.07 112.219 c h +153.582 101.781 m 159.18 102.211 165.102 102.328 170.34 100.02 c 173.66 + 98.59 175.41 95.078 176 91.68 c 177.742 82.91 177.52 73.852 176.902 64.969 + c 176.281 59.609 175.422 52.672 169.52 50.59 c 162.699 48.359 154.922 48.219 + 148.18 50.828 c 141.91 53.469 141.18 61.059 140.562 66.949 c 140.191 75.988 + 139.742 85.289 142.289 94.07 c 143.641 99.051 148.82 101.41 153.582 101.781 + c h +153.582 101.781 m f* +221.262 112.07 m 231.09 117.121 242.602 117.301 253.391 116.789 c 262.371 + 116.039 273.27 114.539 278.223 105.949 c 283.801 95.578 282.891 83.379 +283.672 72 c 228.961 72 l 229.602 66.129 228.84 59.801 231.801 54.422 c +234.332 50.172 239.699 49.301 244.242 49.051 c 249.852 49.012 255.891 48.551 + 261.062 51.16 c 264.02 53.48 264.039 57.602 264.422 61 c 270.82 61.012 +277.223 61.012 283.621 61 c 283.379 54.32 282.52 46.84 277.16 42.141 c 269.109 + 34.922 257.59 34.172 247.289 33.969 c 238.199 34.238 228.602 34.699 220.461 + 39.18 c 213.871 43.07 211.77 51.059 210.609 58.102 c 209.141 68.559 208.77 + 79.219 210.02 89.719 c 211.039 98.012 213.27 107.762 221.262 112.07 c h +232.949 99.34 m 238.41 102.66 245.172 101.988 251.301 101.898 c 255.102 + 101.488 259.73 101.27 262.199 97.91 c 264.723 93.762 264.27 88.68 264.289 + 84.02 c 252.52 84 240.762 83.969 229 84.031 c 229.18 89.211 228.77 95.531 + 232.949 99.34 c h +232.949 99.34 m f* +326.262 112.121 m 333.18 116.922 342.121 117.59 350.262 116.648 c 357.191 + 115.922 364.531 113.281 368.621 107.301 c 372.25 102.34 373.262 96.02 373.312 + 90.012 c 373.281 71.672 373.32 53.34 373.301 35 c 366.961 34.988 360.629 + 34.988 354.312 35 c 354.281 52.352 354.332 69.691 354.281 87.031 c 354.09 + 90.82 354.242 95.199 351.391 98.121 c 348.352 101.41 343.582 102.051 339.332 + 102.02 c 334.191 102.051 328.629 101.172 324.672 97.621 c 320.801 94.32 + 319.332 89 319.312 84.078 c 319.281 67.719 319.32 51.359 319.289 35.012 + c 312.961 34.988 306.629 34.988 300.312 35 c 300.301 62 300.301 89 300.312 + 116 c 306.531 116.02 312.762 116.012 318.98 116 c 318.949 111.262 318.48 + 106.551 318.34 101.809 c 320.379 105.641 322.52 109.68 326.262 112.121 +c h +326.262 112.121 m f* +407.691 147.602 m 418.172 151.121 429.34 151.621 440.301 152.012 c 450.922 + 151.961 462.02 151.859 471.941 147.578 c 476.98 145.48 480.473 140.879 +482.172 135.801 c 484.941 128.211 485.02 119.988 485.082 112 c 477.77 112 + 470.461 111.98 463.16 112.012 c 463.039 117.629 463.473 123.93 459.992 +128.711 c 456.473 132.309 450.973 132.301 446.301 132.852 c 436.801 133.031 + 426.91 133.641 417.812 130.359 c 414.531 129.32 412.832 126.039 412.172 + 122.879 c 410.301 114.398 410.289 105.648 410.301 97 c 410.41 85.441 410.23 + 73.711 412.699 62.34 c 413.352 58.18 417.18 55.621 421.02 54.699 c 429.902 + 52.488 439.172 52.809 448.242 53.352 c 452.973 53.969 458.73 54.281 461.699 + 58.621 c 464.871 63.801 464.34 70.172 464.172 75.988 c 471.551 76.02 478.922 + 76.012 486.301 75.988 c 486.211 66.801 486.051 57.309 482.711 48.609 c +480.992 44.059 477.441 40.199 472.84 38.461 c 463.812 34.84 453.91 34.609 + 444.332 34.031 c 433.223 33.84 421.973 34.109 411.109 36.699 c 404.742 +38.359 397.781 41.281 394.832 47.609 c 391.062 55.98 390.371 65.289 389.402 + 74.301 c 388.59 86.199 388.07 98.121 388.359 110.039 c 388.93 119.691 389.812 + 129.859 395.02 138.27 c 397.789 142.949 402.652 145.879 407.691 147.602 + c h +407.691 147.602 m f* +489.902 150.969 m 497.52 150.961 505.141 151.18 512.75 150.859 c 520.16 + 127.352 528.301 104.078 535.781 80.602 c 538.691 71.578 540.75 62.301 543.762 + 53.309 c 547.129 63.012 549.289 73.09 552.59 82.809 c 559.902 105.52 567.41 + 128.16 574.711 150.871 c 582.23 151.191 589.77 150.91 597.301 151.012 c + 597.301 148.52 l 584.922 110.789 572.832 72.961 560.699 35.141 c 549.379 + 34.91 538.039 34.879 526.723 35.16 c 514.66 73.828 502.02 112.32 489.902 + 150.969 c h +489.902 150.969 m f* +Q Q +showpage +%%Trailer +end restore +%%EOF diff --git a/modules/gapi/include/opencv2/gapi.hpp b/modules/gapi/include/opencv2/gapi.hpp new file mode 100644 index 00000000000..2087641023c --- /dev/null +++ b/modules/gapi/include/opencv2/gapi.hpp @@ -0,0 +1,42 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_HPP +#define OPENCV_GAPI_HPP + +#include + +/** \defgroup gapi_ref G-API framework +@{ + @defgroup gapi_main_classes G-API Main Classes + @defgroup gapi_data_objects G-API Data Types + @{ + @defgroup gapi_meta_args G-API Metadata Descriptors + @} + @defgroup gapi_std_backends G-API Standard Backends + @defgroup gapi_compile_args G-API Graph Compilation Arguments + @defgroup gapi_serialization G-API Serialization functionality +@} + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Include these files here to avoid cyclic dependency between +// Desync & GKernel & GComputation & GStreamingCompiled. +#include +#include + +#endif // OPENCV_GAPI_HPP diff --git a/modules/gapi/include/opencv2/gapi/core.hpp b/modules/gapi/include/opencv2/gapi/core.hpp new file mode 100644 index 00000000000..60bb2c50745 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/core.hpp @@ -0,0 +1,1911 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_CORE_HPP +#define OPENCV_GAPI_CORE_HPP + +#include +#include // std::tuple + +#include +#include + +#include +#include +#include +#include + +/** \defgroup gapi_core G-API Core functionality +@{ + @defgroup gapi_math Graph API: Math operations + @defgroup gapi_pixelwise Graph API: Pixelwise operations + @defgroup gapi_matrixop Graph API: Operations on matrices + @defgroup gapi_transform Graph API: Image and channel composition functions +@} + */ + +namespace cv { namespace gapi { +/** + * @brief This namespace contains G-API Operation Types for OpenCV + * Core module functionality. + */ +namespace core { + using GResize = cv::gapi::imgproc::GResize; + using GResizeP = cv::gapi::imgproc::GResizeP; + + using GMat2 = std::tuple; + using GMat3 = std::tuple; // FIXME: how to avoid this? + using GMat4 = std::tuple; + using GMatScalar = std::tuple; + + G_TYPED_KERNEL(GAdd, , "org.opencv.core.math.add") { + static GMatDesc outMeta(GMatDesc a, GMatDesc b, int ddepth) { + if (ddepth == -1) + { + // OpenCV: When the input arrays in add/subtract/multiply/divide + // functions have different depths, the output array depth must be + // explicitly specified! + // See artim_op() @ arithm.cpp + GAPI_Assert(a.chan == b.chan); + GAPI_Assert(a.depth == b.depth); + return a; + } + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GAddC, , "org.opencv.core.math.addC") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) { + GAPI_Assert(a.chan <= 4); + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GSub, , "org.opencv.core.math.sub") { + static GMatDesc outMeta(GMatDesc a, GMatDesc b, int ddepth) { + if (ddepth == -1) + { + // This macro should select a larger data depth from a and b + // considering the number of channels in the same + // FIXME!!! Clarify if it is valid for sub() + GAPI_Assert(a.chan == b.chan); + ddepth = std::max(a.depth, b.depth); + } + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GSubC, , "org.opencv.core.math.subC") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) { + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GSubRC,, "org.opencv.core.math.subRC") { + static GMatDesc outMeta(GScalarDesc, GMatDesc b, int ddepth) { + return b.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GMul, , "org.opencv.core.math.mul") { + static GMatDesc outMeta(GMatDesc a, GMatDesc, double, int ddepth) { + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GMulCOld, , "org.opencv.core.math.mulCOld") { + static GMatDesc outMeta(GMatDesc a, double, int ddepth) { + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GMulC, , "org.opencv.core.math.mulC") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) { + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GMulS, , "org.opencv.core.math.muls") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a; + } + }; // FIXME: Merge with MulC + + G_TYPED_KERNEL(GDiv, , "org.opencv.core.math.div") { + static GMatDesc outMeta(GMatDesc a, GMatDesc b, double, int ddepth) { + if (ddepth == -1) + { + GAPI_Assert(a.depth == b.depth); + return b; + } + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GDivC, , "org.opencv.core.math.divC") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc, double, int ddepth) { + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GDivRC, , "org.opencv.core.math.divRC") { + static GMatDesc outMeta(GScalarDesc, GMatDesc b, double, int ddepth) { + return b.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GMean, , "org.opencv.core.math.mean") { + static GScalarDesc outMeta(GMatDesc) { + return empty_scalar_desc(); + } + }; + + G_TYPED_KERNEL_M(GPolarToCart, , "org.opencv.core.math.polarToCart") { + static std::tuple outMeta(GMatDesc, GMatDesc a, bool) { + return std::make_tuple(a, a); + } + }; + + G_TYPED_KERNEL_M(GCartToPolar, , "org.opencv.core.math.cartToPolar") { + static std::tuple outMeta(GMatDesc x, GMatDesc, bool) { + return std::make_tuple(x, x); + } + }; + + G_TYPED_KERNEL(GPhase, , "org.opencv.core.math.phase") { + static GMatDesc outMeta(const GMatDesc &inx, const GMatDesc &, bool) { + return inx; + } + }; + + G_TYPED_KERNEL(GMask, , "org.opencv.core.pixelwise.mask") { + static GMatDesc outMeta(GMatDesc in, GMatDesc) { + return in; + } + }; + + G_TYPED_KERNEL(GCmpGT, , "org.opencv.core.pixelwise.compare.cmpGT") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpGE, , "org.opencv.core.pixelwise.compare.cmpGE") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpLE, , "org.opencv.core.pixelwise.compare.cmpLE") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpLT, , "org.opencv.core.pixelwise.compare.cmpLT") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpEQ, , "org.opencv.core.pixelwise.compare.cmpEQ") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpNE, , "org.opencv.core.pixelwise.compare.cmpNE") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpGTScalar, , "org.opencv.core.pixelwise.compare.cmpGTScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpGEScalar, , "org.opencv.core.pixelwise.compare.cmpGEScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpLEScalar, , "org.opencv.core.pixelwise.compare.cmpLEScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpLTScalar, , "org.opencv.core.pixelwise.compare.cmpLTScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpEQScalar, , "org.opencv.core.pixelwise.compare.cmpEQScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GCmpNEScalar, , "org.opencv.core.pixelwise.compare.cmpNEScalar") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a.withDepth(CV_8U); + } + }; + + G_TYPED_KERNEL(GAnd, , "org.opencv.core.pixelwise.bitwise_and") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GAndS, , "org.opencv.core.pixelwise.bitwise_andS") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GOr, , "org.opencv.core.pixelwise.bitwise_or") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GOrS, , "org.opencv.core.pixelwise.bitwise_orS") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GXor, , "org.opencv.core.pixelwise.bitwise_xor") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GXorS, , "org.opencv.core.pixelwise.bitwise_xorS") { + static GMatDesc outMeta(GMatDesc a, GScalarDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GNot, , "org.opencv.core.pixelwise.bitwise_not") { + static GMatDesc outMeta(GMatDesc a) { + return a; + } + }; + + G_TYPED_KERNEL(GSelect, , "org.opencv.core.pixelwise.select") { + static GMatDesc outMeta(GMatDesc a, GMatDesc, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GMin, , "org.opencv.core.matrixop.min") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GMax, , "org.opencv.core.matrixop.max") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GAbsDiff, , "org.opencv.core.matrixop.absdiff") { + static GMatDesc outMeta(GMatDesc a, GMatDesc) { + return a; + } + }; + + G_TYPED_KERNEL(GAbsDiffC, , "org.opencv.core.matrixop.absdiffC") { + static GMatDesc outMeta(const GMatDesc& a, const GScalarDesc&) { + return a; + } + }; + + G_TYPED_KERNEL(GSum, , "org.opencv.core.matrixop.sum") { + static GScalarDesc outMeta(GMatDesc) { + return empty_scalar_desc(); + } + }; + + G_TYPED_KERNEL(GCountNonZero, (GMat)>, "org.opencv.core.matrixop.countNonZero") { + static GOpaqueDesc outMeta(GMatDesc in) { + GAPI_Assert(in.chan == 1); + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GAddW, , "org.opencv.core.matrixop.addweighted") { + static GMatDesc outMeta(GMatDesc a, double, GMatDesc b, double, double, int ddepth) { + if (ddepth == -1) + { + // OpenCV: When the input arrays in add/subtract/multiply/divide + // functions have different depths, the output array depth must be + // explicitly specified! + // See artim_op() @ arithm.cpp + GAPI_Assert(a.chan == b.chan); + GAPI_Assert(a.depth == b.depth); + return a; + } + return a.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GNormL1, , "org.opencv.core.matrixop.norml1") { + static GScalarDesc outMeta(GMatDesc) { + return empty_scalar_desc(); + } + }; + + G_TYPED_KERNEL(GNormL2, , "org.opencv.core.matrixop.norml2") { + static GScalarDesc outMeta(GMatDesc) { + return empty_scalar_desc(); + } + }; + + G_TYPED_KERNEL(GNormInf, , "org.opencv.core.matrixop.norminf") { + static GScalarDesc outMeta(GMatDesc) { + return empty_scalar_desc(); + } + }; + + G_TYPED_KERNEL_M(GIntegral, , "org.opencv.core.matrixop.integral") { + static std::tuple outMeta(GMatDesc in, int sd, int sqd) { + return std::make_tuple(in.withSizeDelta(1,1).withDepth(sd), + in.withSizeDelta(1,1).withDepth(sqd)); + } + }; + + G_TYPED_KERNEL(GThreshold, , "org.opencv.core.matrixop.threshold") { + static GMatDesc outMeta(GMatDesc in, GScalarDesc, GScalarDesc, int) { + return in; + } + }; + + + G_TYPED_KERNEL_M(GThresholdOT, , "org.opencv.core.matrixop.thresholdOT") { + static std::tuple outMeta(GMatDesc in, GScalarDesc, int) { + return std::make_tuple(in, empty_scalar_desc()); + } + }; + + G_TYPED_KERNEL(GInRange, , "org.opencv.core.matrixop.inrange") { + static GMatDesc outMeta(GMatDesc in, GScalarDesc, GScalarDesc) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL_M(GSplit3, , "org.opencv.core.transform.split3") { + static std::tuple outMeta(GMatDesc in) { + const auto out_depth = in.depth; + const auto out_desc = in.withType(out_depth, 1); + return std::make_tuple(out_desc, out_desc, out_desc); + } + }; + + G_TYPED_KERNEL_M(GSplit4, ,"org.opencv.core.transform.split4") { + static std::tuple outMeta(GMatDesc in) { + const auto out_depth = in.depth; + const auto out_desc = in.withType(out_depth, 1); + return std::make_tuple(out_desc, out_desc, out_desc, out_desc); + } + }; + + G_TYPED_KERNEL(GMerge3, , "org.opencv.core.transform.merge3") { + static GMatDesc outMeta(GMatDesc in, GMatDesc, GMatDesc) { + // Preserve depth and add channel component + return in.withType(in.depth, 3); + } + }; + + G_TYPED_KERNEL(GMerge4, , "org.opencv.core.transform.merge4") { + static GMatDesc outMeta(GMatDesc in, GMatDesc, GMatDesc, GMatDesc) { + // Preserve depth and add channel component + return in.withType(in.depth, 4); + } + }; + + G_TYPED_KERNEL(GRemap, , "org.opencv.core.transform.remap") { + static GMatDesc outMeta(GMatDesc in, Mat m1, Mat, int, int, Scalar) { + return in.withSize(m1.size()); + } + }; + + G_TYPED_KERNEL(GFlip, , "org.opencv.core.transform.flip") { + static GMatDesc outMeta(GMatDesc in, int) { + return in; + } + }; + + // TODO: eliminate the need in this kernel (streaming) + G_TYPED_KERNEL(GCrop, , "org.opencv.core.transform.crop") { + static GMatDesc outMeta(GMatDesc in, Rect rc) { + return in.withSize(Size(rc.width, rc.height)); + } + }; + + G_TYPED_KERNEL(GConcatHor, , "org.opencv.imgproc.transform.concatHor") { + static GMatDesc outMeta(GMatDesc l, GMatDesc r) { + return l.withSizeDelta(+r.size.width, 0); + } + }; + + G_TYPED_KERNEL(GConcatVert, , "org.opencv.imgproc.transform.concatVert") { + static GMatDesc outMeta(GMatDesc t, GMatDesc b) { + return t.withSizeDelta(0, +b.size.height); + } + }; + + G_TYPED_KERNEL(GLUT, , "org.opencv.core.transform.LUT") { + static GMatDesc outMeta(GMatDesc in, Mat) { + return in; + } + }; + + G_TYPED_KERNEL(GConvertTo, , "org.opencv.core.transform.convertTo") { + static GMatDesc outMeta(GMatDesc in, int rdepth, double, double) { + return rdepth < 0 ? in : in.withDepth(rdepth); + } + }; + + G_TYPED_KERNEL(GSqrt, , "org.opencv.core.math.sqrt") { + static GMatDesc outMeta(GMatDesc in) { + return in; + } + }; + + G_TYPED_KERNEL(GNormalize, , "org.opencv.core.normalize") { + static GMatDesc outMeta(GMatDesc in, double, double, int, int ddepth) { + // unlike opencv doesn't have a mask as a parameter + return (ddepth < 0 ? in : in.withDepth(ddepth)); + } + }; + + G_TYPED_KERNEL(GWarpPerspective, , "org.opencv.core.warpPerspective") { + static GMatDesc outMeta(GMatDesc in, const Mat&, Size dsize, int, int borderMode, const cv::Scalar&) { + GAPI_Assert((borderMode == cv::BORDER_CONSTANT || borderMode == cv::BORDER_REPLICATE) && + "cv::gapi::warpPerspective supports only cv::BORDER_CONSTANT and cv::BORDER_REPLICATE border modes"); + return in.withType(in.depth, in.chan).withSize(dsize); + } + }; + + G_TYPED_KERNEL(GWarpAffine, , "org.opencv.core.warpAffine") { + static GMatDesc outMeta(GMatDesc in, const Mat&, Size dsize, int, int border_mode, const cv::Scalar&) { + GAPI_Assert(border_mode != cv::BORDER_TRANSPARENT && + "cv::BORDER_TRANSPARENT mode is not supported in cv::gapi::warpAffine"); + return in.withType(in.depth, in.chan).withSize(dsize); + } + }; + + G_TYPED_KERNEL( + GKMeansND, + ,GMat,GMat>(GMat,int,GMat,TermCriteria,int,KmeansFlags)>, + "org.opencv.core.kmeansND") { + + static std::tuple + outMeta(const GMatDesc& in, int K, const GMatDesc& bestLabels, const TermCriteria&, int, + KmeansFlags flags) { + GAPI_Assert(in.depth == CV_32F); + std::vector amount_n_dim = detail::checkVector(in); + int amount = amount_n_dim[0], dim = amount_n_dim[1]; + if (amount == -1) // Mat with height != 1, width != 1, channels != 1 given + { // which means that kmeans will consider the following: + amount = in.size.height; + dim = in.size.width * in.chan; + } + // kmeans sets these labels' sizes when no bestLabels given: + GMatDesc out_labels(CV_32S, 1, Size{1, amount}); + // kmeans always sets these centers' sizes: + GMatDesc centers (CV_32F, 1, Size{dim, K}); + if (flags & KMEANS_USE_INITIAL_LABELS) + { + GAPI_Assert(bestLabels.depth == CV_32S); + int labels_amount = detail::checkVector(bestLabels, 1u); + GAPI_Assert(labels_amount == amount); + out_labels = bestLabels; // kmeans preserves bestLabels' sizes if given + } + return std::make_tuple(empty_gopaque_desc(), out_labels, centers); + } + }; + + G_TYPED_KERNEL( + GKMeansNDNoInit, + ,GMat,GMat>(GMat,int,TermCriteria,int,KmeansFlags)>, + "org.opencv.core.kmeansNDNoInit") { + + static std::tuple + outMeta(const GMatDesc& in, int K, const TermCriteria&, int, KmeansFlags flags) { + GAPI_Assert( !(flags & KMEANS_USE_INITIAL_LABELS) ); + GAPI_Assert(in.depth == CV_32F); + std::vector amount_n_dim = detail::checkVector(in); + int amount = amount_n_dim[0], dim = amount_n_dim[1]; + if (amount == -1) // Mat with height != 1, width != 1, channels != 1 given + { // which means that kmeans will consider the following: + amount = in.size.height; + dim = in.size.width * in.chan; + } + GMatDesc out_labels(CV_32S, 1, Size{1, amount}); + GMatDesc centers (CV_32F, 1, Size{dim, K}); + return std::make_tuple(empty_gopaque_desc(), out_labels, centers); + } + }; + + G_TYPED_KERNEL(GKMeans2D, ,GArray,GArray> + (GArray,int,GArray,TermCriteria,int,KmeansFlags)>, + "org.opencv.core.kmeans2D") { + static std::tuple + outMeta(const GArrayDesc&,int,const GArrayDesc&,const TermCriteria&,int,KmeansFlags) { + return std::make_tuple(empty_gopaque_desc(), empty_array_desc(), empty_array_desc()); + } + }; + + G_TYPED_KERNEL(GKMeans3D, ,GArray,GArray> + (GArray,int,GArray,TermCriteria,int,KmeansFlags)>, + "org.opencv.core.kmeans3D") { + static std::tuple + outMeta(const GArrayDesc&,int,const GArrayDesc&,const TermCriteria&,int,KmeansFlags) { + return std::make_tuple(empty_gopaque_desc(), empty_array_desc(), empty_array_desc()); + } + }; + + G_TYPED_KERNEL(GTranspose, , "org.opencv.core.transpose") { + static GMatDesc outMeta(GMatDesc in) { + return in.withSize({in.size.height, in.size.width}); + } + }; +} // namespace core + +namespace streaming { + +// Operations for Streaming (declared in this header for convenience) +G_TYPED_KERNEL(GSize, (GMat)>, "org.opencv.streaming.size") { + static GOpaqueDesc outMeta(const GMatDesc&) { + return empty_gopaque_desc(); + } +}; + +G_TYPED_KERNEL(GSizeR, (GOpaque)>, "org.opencv.streaming.sizeR") { + static GOpaqueDesc outMeta(const GOpaqueDesc&) { + return empty_gopaque_desc(); + } +}; + +G_TYPED_KERNEL(GSizeMF, (GFrame)>, "org.opencv.streaming.sizeMF") { + static GOpaqueDesc outMeta(const GFrameDesc&) { + return empty_gopaque_desc(); + } +}; +} // namespace streaming + +//! @addtogroup gapi_math +//! @{ + +/** @brief Calculates the per-element sum of two matrices. + +The function add calculates sum of two matrices of the same size and the same number of channels: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) + \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f] + +The function can be replaced with matrix expressions: + \f[\texttt{dst} = \texttt{src1} + \texttt{src2}\f] + +The input matrices and the output matrix can all have the same or different depths. For example, you +can add a 16-bit unsigned matrix to a 8-bit signed matrix and store the sum as a 32-bit +floating-point matrix. Depth of the output matrix is determined by the ddepth parameter. +If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have +the same depth as the input matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.add" +@param src1 first input matrix. +@param src2 second input matrix. +@param ddepth optional depth of the output matrix. +@sa sub, addWeighted +*/ +GAPI_EXPORTS_W GMat add(const GMat& src1, const GMat& src2, int ddepth = -1); + +/** @brief Calculates the per-element sum of matrix and given scalar. + +The function addC adds a given scalar value to each element of given matrix. +The function can be replaced with matrix expressions: + + \f[\texttt{dst} = \texttt{src1} + \texttt{c}\f] + +Depth of the output matrix is determined by the ddepth parameter. +If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix. +The matrices can be single or multi channel. Output matrix must have the same size and number of channels as the input matrix. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.addC" +@param src1 first input matrix. +@param c scalar value to be added. +@param ddepth optional depth of the output matrix. +@sa sub, addWeighted +*/ +GAPI_EXPORTS_W GMat addC(const GMat& src1, const GScalar& c, int ddepth = -1); +//! @overload +GAPI_EXPORTS_W GMat addC(const GScalar& c, const GMat& src1, int ddepth = -1); + +/** @brief Calculates the per-element difference between two matrices. + +The function sub calculates difference between two matrices, when both matrices have the same size and the same number of +channels: + \f[\texttt{dst}(I) = \texttt{src1}(I) - \texttt{src2}(I)\f] + +The function can be replaced with matrix expressions: +\f[\texttt{dst} = \texttt{src1} - \texttt{src2}\f] + +The input matrices and the output matrix can all have the same or different depths. For example, you +can subtract two 8-bit unsigned matrices store the result as a 16-bit signed matrix. +Depth of the output matrix is determined by the ddepth parameter. +If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have +the same depth as the input matrices. The matrices can be single or multi channel. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.sub" +@param src1 first input matrix. +@param src2 second input matrix. +@param ddepth optional depth of the output matrix. +@sa add, addC + */ +GAPI_EXPORTS_W GMat sub(const GMat& src1, const GMat& src2, int ddepth = -1); + +/** @brief Calculates the per-element difference between matrix and given scalar. + +The function can be replaced with matrix expressions: + \f[\texttt{dst} = \texttt{src} - \texttt{c}\f] + +Depth of the output matrix is determined by the ddepth parameter. +If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix. +The matrices can be single or multi channel. Output matrix must have the same size as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.subC" +@param src first input matrix. +@param c scalar value to subtracted. +@param ddepth optional depth of the output matrix. +@sa add, addC, subRC + */ +GAPI_EXPORTS_W GMat subC(const GMat& src, const GScalar& c, int ddepth = -1); + +/** @brief Calculates the per-element difference between given scalar and the matrix. + +The function can be replaced with matrix expressions: + \f[\texttt{dst} = \texttt{c} - \texttt{src}\f] + +Depth of the output matrix is determined by the ddepth parameter. +If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix. +The matrices can be single or multi channel. Output matrix must have the same size as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.subRC" +@param c scalar value to subtract from. +@param src input matrix to be subtracted. +@param ddepth optional depth of the output matrix. +@sa add, addC, subC + */ +GAPI_EXPORTS_W GMat subRC(const GScalar& c, const GMat& src, int ddepth = -1); + +/** @brief Calculates the per-element scaled product of two matrices. + +The function mul calculates the per-element product of two matrices: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{scale} \cdot \texttt{src1} (I) \cdot \texttt{src2} (I))\f] + +If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have +the same depth as the input matrices. The matrices can be single or multi channel. +Output matrix must have the same size as input matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.mul" +@param src1 first input matrix. +@param src2 second input matrix of the same size and the same depth as src1. +@param scale optional scale factor. +@param ddepth optional depth of the output matrix. +@sa add, sub, div, addWeighted +*/ +GAPI_EXPORTS_W GMat mul(const GMat& src1, const GMat& src2, double scale = 1.0, int ddepth = -1); + +/** @brief Multiplies matrix by scalar. + +The function mulC multiplies each element of matrix src by given scalar value: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I) \cdot \texttt{multiplier} )\f] + +The matrices can be single or multi channel. Output matrix must have the same size as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.mulC" +@param src input matrix. +@param multiplier factor to be multiplied. +@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth. +@sa add, sub, div, addWeighted +*/ +GAPI_EXPORTS_W GMat mulC(const GMat& src, double multiplier, int ddepth = -1); +//! @overload +GAPI_EXPORTS_W GMat mulC(const GMat& src, const GScalar& multiplier, int ddepth = -1); // FIXME: merge with mulc +//! @overload +GAPI_EXPORTS_W GMat mulC(const GScalar& multiplier, const GMat& src, int ddepth = -1); // FIXME: merge with mulc + +/** @brief Performs per-element division of two matrices. + +The function divides one matrix by another: +\f[\texttt{dst(I) = saturate(src1(I)*scale/src2(I))}\f] + +For integer types when src2(I) is zero, dst(I) will also be zero. +Floating point case returns Inf/NaN (according to IEEE). + +Different channels of +multi-channel matrices are processed independently. +The matrices can be single or multi channel. Output matrix must have the same size and depth as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.div" +@param src1 first input matrix. +@param src2 second input matrix of the same size and depth as src1. +@param scale scalar factor. +@param ddepth optional depth of the output matrix; you can only pass -1 when src1.depth() == src2.depth(). +@sa mul, add, sub +*/ +GAPI_EXPORTS_W GMat div(const GMat& src1, const GMat& src2, double scale, int ddepth = -1); + +/** @brief Divides matrix by scalar. + +The function divC divides each element of matrix src by given scalar value: + +\f[\texttt{dst(I) = saturate(src(I)*scale/divisor)}\f] + +When divisor is zero, dst(I) will also be zero. Different channels of +multi-channel matrices are processed independently. +The matrices can be single or multi channel. Output matrix must have the same size and depth as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.divC" +@param src input matrix. +@param divisor number to be divided by. +@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth. +@param scale scale factor. +@sa add, sub, div, addWeighted +*/ +GAPI_EXPORTS_W GMat divC(const GMat& src, const GScalar& divisor, double scale, int ddepth = -1); + +/** @brief Divides scalar by matrix. + +The function divRC divides given scalar by each element of matrix src and keep the division result in new matrix of the same size and type as src: + +\f[\texttt{dst(I) = saturate(divident*scale/src(I))}\f] + +When src(I) is zero, dst(I) will also be zero. Different channels of +multi-channel matrices are processed independently. +The matrices can be single or multi channel. Output matrix must have the same size and depth as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.divRC" +@param src input matrix. +@param divident number to be divided. +@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth. +@param scale scale factor +@sa add, sub, div, addWeighted +*/ +GAPI_EXPORTS_W GMat divRC(const GScalar& divident, const GMat& src, double scale, int ddepth = -1); + +/** @brief Applies a mask to a matrix. + +The function mask set value from given matrix if the corresponding pixel value in mask matrix set to true, +and set the matrix value to 0 otherwise. + +Supported src matrix data types are @ref CV_8UC1, @ref CV_16SC1, @ref CV_16UC1. Supported mask data type is @ref CV_8UC1. + +@note Function textual ID is "org.opencv.core.math.mask" +@param src input matrix. +@param mask input mask matrix. +*/ +GAPI_EXPORTS_W GMat mask(const GMat& src, const GMat& mask); + +/** @brief Calculates an average (mean) of matrix elements. + +The function mean calculates the mean value M of matrix elements, +independently for each channel, and return it. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.math.mean" +@param src input matrix. +@sa countNonZero, min, max +*/ +GAPI_EXPORTS_W GScalar mean(const GMat& src); + +/** @brief Calculates x and y coordinates of 2D vectors from their magnitude and angle. + +The function polarToCart calculates the Cartesian coordinates of each 2D +vector represented by the corresponding elements of magnitude and angle: +\f[\begin{array}{l} \texttt{x} (I) = \texttt{magnitude} (I) \cos ( \texttt{angle} (I)) \\ \texttt{y} (I) = \texttt{magnitude} (I) \sin ( \texttt{angle} (I)) \\ \end{array}\f] + +The relative accuracy of the estimated coordinates is about 1e-6. + +First output is a matrix of x-coordinates of 2D vectors. +Second output is a matrix of y-coordinates of 2D vectors. +Both output must have the same size and depth as input matrices. + +@note Function textual ID is "org.opencv.core.math.polarToCart" + +@param magnitude input floating-point @ref CV_32FC1 matrix (1xN) of magnitudes of 2D vectors; +@param angle input floating-point @ref CV_32FC1 matrix (1xN) of angles of 2D vectors. +@param angleInDegrees when true, the input angles are measured in +degrees, otherwise, they are measured in radians. +@sa cartToPolar, exp, log, pow, sqrt +*/ +GAPI_EXPORTS_W std::tuple polarToCart(const GMat& magnitude, const GMat& angle, + bool angleInDegrees = false); + +/** @brief Calculates the magnitude and angle of 2D vectors. + +The function cartToPolar calculates either the magnitude, angle, or both +for every 2D vector (x(I),y(I)): +\f[\begin{array}{l} \texttt{magnitude} (I)= \sqrt{\texttt{x}(I)^2+\texttt{y}(I)^2} , \\ \texttt{angle} (I)= \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))[ \cdot180 / \pi ] \end{array}\f] + +The angles are calculated with accuracy about 0.3 degrees. For the point +(0,0), the angle is set to 0. + +First output is a matrix of magnitudes of the same size and depth as input x. +Second output is a matrix of angles that has the same size and depth as +x; the angles are measured in radians (from 0 to 2\*Pi) or in degrees (0 to 360 degrees). + +@note Function textual ID is "org.opencv.core.math.cartToPolar" + +@param x matrix of @ref CV_32FC1 x-coordinates. +@param y array of @ref CV_32FC1 y-coordinates. +@param angleInDegrees a flag, indicating whether the angles are measured +in radians (which is by default), or in degrees. +@sa polarToCart +*/ +GAPI_EXPORTS_W std::tuple cartToPolar(const GMat& x, const GMat& y, + bool angleInDegrees = false); + +/** @brief Calculates the rotation angle of 2D vectors. + +The function cv::phase calculates the rotation angle of each 2D vector that +is formed from the corresponding elements of x and y : +\f[\texttt{angle} (I) = \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))\f] + +The angle estimation accuracy is about 0.3 degrees. When x(I)=y(I)=0 , +the corresponding angle(I) is set to 0. +@param x input floating-point array of x-coordinates of 2D vectors. +@param y input array of y-coordinates of 2D vectors; it must have the +same size and the same type as x. +@param angleInDegrees when true, the function calculates the angle in +degrees, otherwise, they are measured in radians. +@return array of vector angles; it has the same size and same type as x. +*/ +GAPI_EXPORTS_W GMat phase(const GMat& x, const GMat &y, bool angleInDegrees = false); + +/** @brief Calculates a square root of array elements. + +The function cv::gapi::sqrt calculates a square root of each input array element. +In case of multi-channel arrays, each channel is processed +independently. The accuracy is approximately the same as of the built-in +std::sqrt . +@param src input floating-point array. +@return output array of the same size and type as src. +*/ +GAPI_EXPORTS_W GMat sqrt(const GMat &src); + +//! @} gapi_math +//! +//! @addtogroup gapi_pixelwise +//! @{ + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are greater compare to elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) > \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: +\f[\texttt{dst} = \texttt{src1} > \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices/matrix. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGT" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpLE, cmpGE, cmpLT +*/ +GAPI_EXPORTS_W GMat cmpGT(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGTScalar" +*/ +GAPI_EXPORTS_W GMat cmpGT(const GMat& src1, const GScalar& src2); + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less than elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) < \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: + \f[\texttt{dst} = \texttt{src1} < \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices/matrix. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLT" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpLE, cmpGE, cmpGT +*/ +GAPI_EXPORTS_W GMat cmpLT(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLTScalar" +*/ +GAPI_EXPORTS_W GMat cmpLT(const GMat& src1, const GScalar& src2); + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are greater or equal compare to elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) >= \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: + \f[\texttt{dst} = \texttt{src1} >= \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGE" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpLE, cmpGT, cmpLT +*/ +GAPI_EXPORTS_W GMat cmpGE(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLGEcalar" +*/ +GAPI_EXPORTS_W GMat cmpGE(const GMat& src1, const GScalar& src2); + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less or equal compare to elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) <= \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: + \f[\texttt{dst} = \texttt{src1} <= \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLE" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpGT, cmpGE, cmpLT +*/ +GAPI_EXPORTS_W GMat cmpLE(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLEScalar" +*/ +GAPI_EXPORTS_W GMat cmpLE(const GMat& src1, const GScalar& src2); + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are equal to elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) == \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: + \f[\texttt{dst} = \texttt{src1} == \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpEQ" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpNE +*/ +GAPI_EXPORTS_W GMat cmpEQ(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpEQScalar" +*/ +GAPI_EXPORTS_W GMat cmpEQ(const GMat& src1, const GScalar& src2); + +/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are not equal to elements in second. + +The function compares elements of two matrices src1 and src2 of the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) != \texttt{src2} (I)\f] + +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: + \f[\texttt{dst} = \texttt{src1} != \texttt{src2}\f] + +Output matrix of depth @ref CV_8U must have the same size and the same number of channels as + the input matrices. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpNE" +@param src1 first input matrix. +@param src2 second input matrix/scalar of the same depth as first input matrix. +@sa min, max, threshold, cmpEQ +*/ +GAPI_EXPORTS_W GMat cmpNE(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpNEScalar" +*/ +GAPI_EXPORTS_W GMat cmpNE(const GMat& src1, const GScalar& src2); + +/** @brief computes bitwise conjunction of the two matrixes (src1 & src2) +Calculates the per-element bit-wise logical conjunction of two matrices of the same size. + +In case of floating-point matrices, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel matrices, each channel is processed +independently. Output matrix must have the same size and depth as the input +matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_and" + +@param src1 first input matrix. +@param src2 second input matrix. +*/ +GAPI_EXPORTS_W GMat bitwise_and(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_andS" +@param src1 first input matrix. +@param src2 scalar, which will be per-lemenetly conjuncted with elements of src1. +*/ +GAPI_EXPORTS_W GMat bitwise_and(const GMat& src1, const GScalar& src2); + +/** @brief computes bitwise disjunction of the two matrixes (src1 | src2) +Calculates the per-element bit-wise logical disjunction of two matrices of the same size. + +In case of floating-point matrices, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel matrices, each channel is processed +independently. Output matrix must have the same size and depth as the input +matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_or" + +@param src1 first input matrix. +@param src2 second input matrix. +*/ +GAPI_EXPORTS_W GMat bitwise_or(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_orS" +@param src1 first input matrix. +@param src2 scalar, which will be per-lemenetly disjuncted with elements of src1. +*/ +GAPI_EXPORTS_W GMat bitwise_or(const GMat& src1, const GScalar& src2); + + +/** @brief computes bitwise logical "exclusive or" of the two matrixes (src1 ^ src2) +Calculates the per-element bit-wise logical "exclusive or" of two matrices of the same size. + +In case of floating-point matrices, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel matrices, each channel is processed +independently. Output matrix must have the same size and depth as the input +matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_xor" + +@param src1 first input matrix. +@param src2 second input matrix. +*/ +GAPI_EXPORTS_W GMat bitwise_xor(const GMat& src1, const GMat& src2); +/** @overload +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_xorS" +@param src1 first input matrix. +@param src2 scalar, for which per-lemenet "logical or" operation on elements of src1 will be performed. +*/ +GAPI_EXPORTS_W GMat bitwise_xor(const GMat& src1, const GScalar& src2); + + +/** @brief Inverts every bit of an array. + +The function bitwise_not calculates per-element bit-wise inversion of the input +matrix: +\f[\texttt{dst} (I) = \neg \texttt{src} (I)\f] + +In case of floating-point matrices, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel matrices, each channel is processed +independently. Output matrix must have the same size and depth as the input +matrix. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.bitwise_not" + +@param src input matrix. +*/ +GAPI_EXPORTS_W GMat bitwise_not(const GMat& src); + +/** @brief Select values from either first or second of input matrices by given mask. +The function set to the output matrix either the value from the first input matrix if corresponding value of mask matrix is 255, + or value from the second input matrix (if value of mask matrix set to 0). + +Input mask matrix must be of @ref CV_8UC1 type, two other inout matrices and output matrix should be of the same type. The size should +be the same for all input and output matrices. +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.pixelwise.select" + +@param src1 first input matrix. +@param src2 second input matrix. +@param mask mask input matrix. +*/ +GAPI_EXPORTS_W GMat select(const GMat& src1, const GMat& src2, const GMat& mask); + +//! @} gapi_pixelwise + + +//! @addtogroup gapi_matrixop +//! @{ +/** @brief Calculates per-element minimum of two matrices. + +The function min calculates the per-element minimum of two matrices of the same size, number of channels and depth: +\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{src2} (I))\f] + where I is a multi-dimensional index of matrix elements. In case of + multi-channel matrices, each channel is processed independently. +Output matrix must be of the same size and depth as src1. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.min" +@param src1 first input matrix. +@param src2 second input matrix of the same size and depth as src1. +@sa max, cmpEQ, cmpLT, cmpLE +*/ +GAPI_EXPORTS_W GMat min(const GMat& src1, const GMat& src2); + +/** @brief Calculates per-element maximum of two matrices. + +The function max calculates the per-element maximum of two matrices of the same size, number of channels and depth: +\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{src2} (I))\f] + where I is a multi-dimensional index of matrix elements. In case of + multi-channel matrices, each channel is processed independently. +Output matrix must be of the same size and depth as src1. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.max" +@param src1 first input matrix. +@param src2 second input matrix of the same size and depth as src1. +@sa min, compare, cmpEQ, cmpGT, cmpGE +*/ +GAPI_EXPORTS_W GMat max(const GMat& src1, const GMat& src2); + +/** @brief Calculates the per-element absolute difference between two matrices. + +The function absDiff calculates absolute difference between two matrices of the same size and depth: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{src2}(I)|)\f] + where I is a multi-dimensional index of matrix elements. In case of + multi-channel matrices, each channel is processed independently. +Output matrix must have the same size and depth as input matrices. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.absdiff" +@param src1 first input matrix. +@param src2 second input matrix. +@sa abs +*/ +GAPI_EXPORTS_W GMat absDiff(const GMat& src1, const GMat& src2); + +/** @brief Calculates absolute value of matrix elements. + +The function abs calculates absolute difference between matrix elements and given scalar value: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{matC}(I)|)\f] + where matC is constructed from given scalar c and has the same sizes and depth as input matrix src. + +Output matrix must be of the same size and depth as src. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.absdiffC" +@param src input matrix. +@param c scalar to be subtracted. +@sa min, max +*/ +GAPI_EXPORTS_W GMat absDiffC(const GMat& src, const GScalar& c); + +/** @brief Calculates sum of all matrix elements. + +The function sum calculates sum of all matrix elements, independently for each channel. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.sum" +@param src input matrix. +@sa countNonZero, mean, min, max +*/ +GAPI_EXPORTS_W GScalar sum(const GMat& src); + +/** @brief Counts non-zero array elements. + +The function returns the number of non-zero elements in src : +\f[\sum _{I: \; \texttt{src} (I) \ne0 } 1\f] + +Supported matrix data types are @ref CV_8UC1, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.countNonZero" +@param src input single-channel matrix. +@sa mean, min, max +*/ +GAPI_EXPORTS_W GOpaque countNonZero(const GMat& src); + +/** @brief Calculates the weighted sum of two matrices. + +The function addWeighted calculates the weighted sum of two matrices as follows: +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} + \texttt{src2} (I)* \texttt{beta} + \texttt{gamma} )\f] +where I is a multi-dimensional index of array elements. In case of multi-channel matrices, each +channel is processed independently. + +The function can be replaced with a matrix expression: + \f[\texttt{dst}(I) = \texttt{alpha} * \texttt{src1}(I) - \texttt{beta} * \texttt{src2}(I) + \texttt{gamma} \f] + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.addweighted" +@param src1 first input matrix. +@param alpha weight of the first matrix elements. +@param src2 second input matrix of the same size and channel number as src1. +@param beta weight of the second matrix elements. +@param gamma scalar added to each sum. +@param ddepth optional depth of the output matrix. +@sa add, sub +*/ +GAPI_EXPORTS_W GMat addWeighted(const GMat& src1, double alpha, const GMat& src2, double beta, double gamma, int ddepth = -1); + +/** @brief Calculates the absolute L1 norm of a matrix. + +This version of normL1 calculates the absolute L1 norm of src. + +As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$. +The \f$ L_{1} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$ +is calculated as follows +\f{align*} + \| r(-1) \|_{L_1} &= |-1| + |2| = 3 \\ +\f} +and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is +\f{align*} + \| r(0.5) \|_{L_1} &= |0.5| + |0.5| = 1 \\ +\f} + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.norml1" +@param src input matrix. +@sa normL2, normInf +*/ +GAPI_EXPORTS_W GScalar normL1(const GMat& src); + +/** @brief Calculates the absolute L2 norm of a matrix. + +This version of normL2 calculates the absolute L2 norm of src. + +As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$. +The \f$ L_{2} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$ +is calculated as follows +\f{align*} + \| r(-1) \|_{L_2} &= \sqrt{(-1)^{2} + (2)^{2}} = \sqrt{5} \\ +\f} +and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is +\f{align*} + \| r(0.5) \|_{L_2} &= \sqrt{(0.5)^{2} + (0.5)^{2}} = \sqrt{0.5} \\ +\f} + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +@note Function textual ID is "org.opencv.core.matrixop.norml2" +@param src input matrix. +@sa normL1, normInf +*/ +GAPI_EXPORTS_W GScalar normL2(const GMat& src); + +/** @brief Calculates the absolute infinite norm of a matrix. + +This version of normInf calculates the absolute infinite norm of src. + +As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$. +The \f$ L_{\infty} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$ +is calculated as follows +\f{align*} + \| r(-1) \|_{L_\infty} &= \max(|-1|,|2|) = 2 +\f} +and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is +\f{align*} + \| r(0.5) \|_{L_\infty} &= \max(|0.5|,|0.5|) = 0.5. +\f} + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.core.matrixop.norminf" +@param src input matrix. +@sa normL1, normL2 +*/ +GAPI_EXPORTS_W GScalar normInf(const GMat& src); + +/** @brief Calculates the integral of an image. + +The function calculates one or more integral images for the source image as follows: + +\f[\texttt{sum} (X,Y) = \sum _{x integral(const GMat& src, int sdepth = -1, int sqdepth = -1); + +/** @brief Applies a fixed-level threshold to each matrix element. + +The function applies fixed-level thresholding to a single- or multiple-channel matrix. +The function is typically used to get a bi-level (binary) image out of a grayscale image ( cmp functions could be also used for +this purpose) or for removing a noise, that is, filtering out pixels with too small or too large +values. There are several types of thresholding supported by the function. They are determined by +type parameter. + +Also, the special values cv::THRESH_OTSU or cv::THRESH_TRIANGLE may be combined with one of the +above values. In these cases, the function determines the optimal threshold value using the Otsu's +or Triangle algorithm and uses it instead of the specified thresh . The function returns the +computed threshold value in addititon to thresholded matrix. +The Otsu's and Triangle methods are implemented only for 8-bit matrices. + +Input image should be single channel only in case of cv::THRESH_OTSU or cv::THRESH_TRIANGLE flags. +Output matrix must be of the same size and depth as src. + +@note Function textual ID is "org.opencv.core.matrixop.threshold" + +@param src input matrix (@ref CV_8UC1, @ref CV_8UC3, or @ref CV_32FC1). +@param thresh threshold value. +@param maxval maximum value to use with the cv::THRESH_BINARY and cv::THRESH_BINARY_INV thresholding +types. +@param type thresholding type (see the cv::ThresholdTypes). + +@sa min, max, cmpGT, cmpLE, cmpGE, cmpLT + */ +GAPI_EXPORTS_W GMat threshold(const GMat& src, const GScalar& thresh, const GScalar& maxval, int type); +/** @overload +This function applicable for all threshold types except cv::THRESH_OTSU and cv::THRESH_TRIANGLE +@note Function textual ID is "org.opencv.core.matrixop.thresholdOT" +*/ +GAPI_EXPORTS_W std::tuple threshold(const GMat& src, const GScalar& maxval, int type); + +/** @brief Applies a range-level threshold to each matrix element. + +The function applies range-level thresholding to a single- or multiple-channel matrix. +It sets output pixel value to OxFF if the corresponding pixel value of input matrix is in specified range,or 0 otherwise. + +Input and output matrices must be CV_8UC1. + +@note Function textual ID is "org.opencv.core.matrixop.inRange" + +@param src input matrix (CV_8UC1). +@param threshLow lower boundary value. +@param threshUp upper boundary value. + +@sa threshold + */ +GAPI_EXPORTS_W GMat inRange(const GMat& src, const GScalar& threshLow, const GScalar& threshUp); + +//! @} gapi_matrixop + +//! @addtogroup gapi_transform +//! @{ +/** @brief Creates one 4-channel matrix out of 4 single-channel ones. + +The function merges several matrices to make a single multi-channel matrix. That is, each +element of the output matrix will be a concatenation of the elements of the input matrices, where +elements of i-th input matrix are treated as mv[i].channels()-element vectors. +Output matrix must be of @ref CV_8UC4 type. + +The function split4 does the reverse operation. + +@note + - Function textual ID is "org.opencv.core.transform.merge4" + +@param src1 first input @ref CV_8UC1 matrix to be merged. +@param src2 second input @ref CV_8UC1 matrix to be merged. +@param src3 third input @ref CV_8UC1 matrix to be merged. +@param src4 fourth input @ref CV_8UC1 matrix to be merged. +@sa merge3, split4, split3 +*/ +GAPI_EXPORTS_W GMat merge4(const GMat& src1, const GMat& src2, const GMat& src3, const GMat& src4); + +/** @brief Creates one 3-channel matrix out of 3 single-channel ones. + +The function merges several matrices to make a single multi-channel matrix. That is, each +element of the output matrix will be a concatenation of the elements of the input matrices, where +elements of i-th input matrix are treated as mv[i].channels()-element vectors. +Output matrix must be of @ref CV_8UC3 type. + +The function split3 does the reverse operation. + +@note + - Function textual ID is "org.opencv.core.transform.merge3" + +@param src1 first input @ref CV_8UC1 matrix to be merged. +@param src2 second input @ref CV_8UC1 matrix to be merged. +@param src3 third input @ref CV_8UC1 matrix to be merged. +@sa merge4, split4, split3 +*/ +GAPI_EXPORTS_W GMat merge3(const GMat& src1, const GMat& src2, const GMat& src3); + +/** @brief Divides a 4-channel matrix into 4 single-channel matrices. + +The function splits a 4-channel matrix into 4 single-channel matrices: +\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f] + +All output matrices must be of @ref CV_8UC1 type. + +The function merge4 does the reverse operation. + +@note + - Function textual ID is "org.opencv.core.transform.split4" + +@param src input @ref CV_8UC4 matrix. +@sa split3, merge3, merge4 +*/ +GAPI_EXPORTS_W std::tuple split4(const GMat& src); + +/** @brief Divides a 3-channel matrix into 3 single-channel matrices. + +The function splits a 3-channel matrix into 3 single-channel matrices: +\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f] + +All output matrices must be of @ref CV_8UC1 type. + +The function merge3 does the reverse operation. + +@note + - Function textual ID is "org.opencv.core.transform.split3" + +@param src input @ref CV_8UC3 matrix. +@sa split4, merge3, merge4 +*/ +GAPI_EXPORTS_W std::tuple split3(const GMat& src); + +/** @brief Applies a generic geometrical transformation to an image. + +The function remap transforms the source image using the specified map: + +\f[\texttt{dst} (x,y) = \texttt{src} (map_x(x,y),map_y(x,y))\f] + +where values of pixels with non-integer coordinates are computed using one of available +interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps +in \f$map_1\f$ and \f$map_2\f$ respectively, or interleaved floating-point maps of \f$(x,y)\f$ in +\f$map_1\f$, or fixed-point maps created by using convertMaps. The reason you might want to +convert from floating to fixed-point representations of a map is that they can yield much faster +(\~2x) remapping operations. In the converted case, \f$map_1\f$ contains pairs (cvFloor(x), +cvFloor(y)) and \f$map_2\f$ contains indices in a table of interpolation coefficients. +Output image must be of the same size and depth as input one. + +@note + - Function textual ID is "org.opencv.core.transform.remap" + - Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + +@param src Source image. +@param map1 The first map of either (x,y) points or just x values having the type CV_16SC2, +CV_32FC1, or CV_32FC2. +@param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map +if map1 is (x,y) points), respectively. +@param interpolation Interpolation method (see cv::InterpolationFlags). The methods #INTER_AREA +and #INTER_LINEAR_EXACT are not supported by this function. +@param borderMode Pixel extrapolation method (see cv::BorderTypes). When +borderMode=BORDER_TRANSPARENT, it means that the pixels in the destination image that +corresponds to the "outliers" in the source image are not modified by the function. +@param borderValue Value used in case of a constant border. By default, it is 0. + */ +GAPI_EXPORTS_W GMat remap(const GMat& src, const Mat& map1, const Mat& map2, + int interpolation, int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @brief Flips a 2D matrix around vertical, horizontal, or both axes. + +The function flips the matrix in one of three different ways (row +and column indices are 0-based): +\f[\texttt{dst} _{ij} = +\left\{ +\begin{array}{l l} +\texttt{src} _{\texttt{src.rows}-i-1,j} & if\; \texttt{flipCode} = 0 \\ +\texttt{src} _{i, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} > 0 \\ +\texttt{src} _{ \texttt{src.rows} -i-1, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} < 0 \\ +\end{array} +\right.\f] +The example scenarios of using the function are the following: +* Vertical flipping of the image (flipCode == 0) to switch between + top-left and bottom-left image origin. This is a typical operation + in video processing on Microsoft Windows\* OS. +* Horizontal flipping of the image with the subsequent horizontal + shift and absolute difference calculation to check for a + vertical-axis symmetry (flipCode \> 0). +* Simultaneous horizontal and vertical flipping of the image with + the subsequent shift and absolute difference calculation to check + for a central symmetry (flipCode \< 0). +* Reversing the order of point arrays (flipCode \> 0 or + flipCode == 0). +Output image must be of the same depth as input one, size should be correct for given flipCode. + +@note Function textual ID is "org.opencv.core.transform.flip" + +@param src input matrix. +@param flipCode a flag to specify how to flip the array; 0 means +flipping around the x-axis and positive value (for example, 1) means +flipping around y-axis. Negative value (for example, -1) means flipping +around both axes. +@sa remap +*/ +GAPI_EXPORTS_W GMat flip(const GMat& src, int flipCode); + +/** @brief Crops a 2D matrix. + +The function crops the matrix by given cv::Rect. + +Output matrix must be of the same depth as input one, size is specified by given rect size. + +@note Function textual ID is "org.opencv.core.transform.crop" + +@param src input matrix. +@param rect a rect to crop a matrix to +@sa resize +*/ +GAPI_EXPORTS_W GMat crop(const GMat& src, const Rect& rect); + +/** @brief Applies horizontal concatenation to given matrices. + +The function horizontally concatenates two GMat matrices (with the same number of rows). +@code{.cpp} + GMat A = { 1, 4, + 2, 5, + 3, 6 }; + GMat B = { 7, 10, + 8, 11, + 9, 12 }; + + GMat C = gapi::concatHor(A, B); + //C: + //[1, 4, 7, 10; + // 2, 5, 8, 11; + // 3, 6, 9, 12] +@endcode +Output matrix must the same number of rows and depth as the src1 and src2, and the sum of cols of the src1 and src2. +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.imgproc.transform.concatHor" + +@param src1 first input matrix to be considered for horizontal concatenation. +@param src2 second input matrix to be considered for horizontal concatenation. +@sa concatVert +*/ +GAPI_EXPORTS_W GMat concatHor(const GMat& src1, const GMat& src2); + +/** @overload +The function horizontally concatenates given number of GMat matrices (with the same number of columns). +Output matrix must the same number of columns and depth as the input matrices, and the sum of rows of input matrices. + +@param v vector of input matrices to be concatenated horizontally. +*/ +GAPI_EXPORTS_W GMat concatHor(const std::vector &v); + +/** @brief Applies vertical concatenation to given matrices. + +The function vertically concatenates two GMat matrices (with the same number of cols). + @code{.cpp} + GMat A = { 1, 7, + 2, 8, + 3, 9 }; + GMat B = { 4, 10, + 5, 11, + 6, 12 }; + + GMat C = gapi::concatVert(A, B); + //C: + //[1, 7; + // 2, 8; + // 3, 9; + // 4, 10; + // 5, 11; + // 6, 12] + @endcode + +Output matrix must the same number of cols and depth as the src1 and src2, and the sum of rows of the src1 and src2. +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. + +@note Function textual ID is "org.opencv.imgproc.transform.concatVert" + +@param src1 first input matrix to be considered for vertical concatenation. +@param src2 second input matrix to be considered for vertical concatenation. +@sa concatHor +*/ +GAPI_EXPORTS_W GMat concatVert(const GMat& src1, const GMat& src2); + +/** @overload +The function vertically concatenates given number of GMat matrices (with the same number of columns). +Output matrix must the same number of columns and depth as the input matrices, and the sum of rows of input matrices. + +@param v vector of input matrices to be concatenated vertically. +*/ +GAPI_EXPORTS_W GMat concatVert(const std::vector &v); + + +/** @brief Performs a look-up table transform of a matrix. + +The function LUT fills the output matrix with values from the look-up table. Indices of the entries +are taken from the input matrix. That is, the function processes each element of src as follows: +\f[\texttt{dst} (I) \leftarrow \texttt{lut(src(I))}\f] + +Supported matrix data types are @ref CV_8UC1. +Output is a matrix of the same size and number of channels as src, and the same depth as lut. + +@note Function textual ID is "org.opencv.core.transform.LUT" + +@param src input matrix of 8-bit elements. +@param lut look-up table of 256 elements; in case of multi-channel input array, the table should +either have a single channel (in this case the same table is used for all channels) or the same +number of channels as in the input matrix. +*/ +GAPI_EXPORTS_W GMat LUT(const GMat& src, const Mat& lut); + +/** @brief Converts a matrix to another data depth with optional scaling. + +The method converts source pixel values to the target data depth. saturate_cast\<\> is applied at +the end to avoid possible overflows: + +\f[m(x,y) = saturate \_ cast( \alpha (*this)(x,y) + \beta )\f] +Output matrix must be of the same size as input one. + +@note Function textual ID is "org.opencv.core.transform.convertTo" +@param src input matrix to be converted from. +@param rdepth desired output matrix depth or, rather, the depth since the number of channels are the +same as the input has; if rdepth is negative, the output matrix will have the same depth as the input. +@param alpha optional scale factor. +@param beta optional delta added to the scaled values. + */ +GAPI_EXPORTS_W GMat convertTo(const GMat& src, int rdepth, double alpha=1, double beta=0); + +/** @brief Normalizes the norm or value range of an array. + +The function normalizes scale and shift the input array elements so that +\f[\| \texttt{dst} \| _{L_p}= \texttt{alpha}\f] +(where p=Inf, 1 or 2) when normType=NORM_INF, NORM_L1, or NORM_L2, respectively; or so that +\f[\min _I \texttt{dst} (I)= \texttt{alpha} , \, \, \max _I \texttt{dst} (I)= \texttt{beta}\f] +when normType=NORM_MINMAX (for dense arrays only). + +@note Function textual ID is "org.opencv.core.normalize" + +@param src input array. +@param alpha norm value to normalize to or the lower range boundary in case of the range +normalization. +@param beta upper range boundary in case of the range normalization; it is not used for the norm +normalization. +@param norm_type normalization type (see cv::NormTypes). +@param ddepth when negative, the output array has the same type as src; otherwise, it has the same +number of channels as src and the depth =ddepth. +@sa norm, Mat::convertTo +*/ +GAPI_EXPORTS_W GMat normalize(const GMat& src, double alpha, double beta, + int norm_type, int ddepth = -1); + +/** @brief Applies a perspective transformation to an image. + +The function warpPerspective transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , + \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right )\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted with invert +and then put in the formula above instead of M. The function cannot operate in-place. + +@param src input image. +@param M \f$3\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (#INTER_LINEAR or #INTER_NEAREST) and the +optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (#BORDER_CONSTANT or #BORDER_REPLICATE). +@param borderValue value used in case of a constant border; by default, it equals 0. + +@sa warpAffine, resize, remap, getRectSubPix, perspectiveTransform + */ +GAPI_EXPORTS_W GMat warpPerspective(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, + int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); + +/** @brief Applies an affine transformation to an image. + +The function warpAffine transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23})\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted +with #invertAffineTransform and then put in the formula above instead of M. The function cannot +operate in-place. + +@param src input image. +@param M \f$2\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (see #InterpolationFlags) and the optional +flag #WARP_INVERSE_MAP that means that M is the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (see #BorderTypes); +borderMode=#BORDER_TRANSPARENT isn't supported +@param borderValue value used in case of a constant border; by default, it is 0. + +@sa warpPerspective, resize, remap, getRectSubPix, transform + */ +GAPI_EXPORTS_W GMat warpAffine(const GMat& src, const Mat& M, const Size& dsize, int flags = cv::INTER_LINEAR, + int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar()); +//! @} gapi_transform + +/** @brief Finds centers of clusters and groups input samples around the clusters. + +The function kmeans implements a k-means algorithm that finds the centers of K clusters +and groups the input samples around the clusters. As an output, \f$\texttt{bestLabels}_i\f$ +contains a 0-based cluster index for the \f$i^{th}\f$ sample. + +@note + - Function textual ID is "org.opencv.core.kmeansND" + - In case of an N-dimentional points' set given, input GMat can have the following traits: +2 dimensions, a single row or column if there are N channels, +or N columns if there is a single channel. Mat should have @ref CV_32F depth. + - Although, if GMat with height != 1, width != 1, channels != 1 given as data, n-dimensional +samples are considered given in amount of A, where A = height, n = width * channels. + - In case of GMat given as data: + - the output labels are returned as 1-channel GMat with sizes +width = 1, height = A, where A is samples amount, or width = bestLabels.width, +height = bestLabels.height if bestLabels given; + - the cluster centers are returned as 1-channel GMat with sizes +width = n, height = K, where n is samples' dimentionality and K is clusters' amount. + - As one of possible usages, if you want to control the initial labels for each attempt +by yourself, you can utilize just the core of the function. To do that, set the number +of attempts to 1, initialize labels each time using a custom algorithm, pass them with the +( flags = #KMEANS_USE_INITIAL_LABELS ) flag, and then choose the best (most-compact) clustering. + +@param data Data for clustering. An array of N-Dimensional points with float coordinates is needed. +Function can take GArray, GArray for 2D and 3D cases or GMat for any +dimentionality and channels. +@param K Number of clusters to split the set by. +@param bestLabels Optional input integer array that can store the supposed initial cluster indices +for every sample. Used when ( flags = #KMEANS_USE_INITIAL_LABELS ) flag is set. +@param criteria The algorithm termination criteria, that is, the maximum number of iterations +and/or the desired accuracy. The accuracy is specified as criteria.epsilon. As soon as each of +the cluster centers moves by less than criteria.epsilon on some iteration, the algorithm stops. +@param attempts Flag to specify the number of times the algorithm is executed using different +initial labellings. The algorithm returns the labels that yield the best compactness (see the first +function return value). +@param flags Flag that can take values of cv::KmeansFlags . + +@return + - Compactness measure that is computed as +\f[\sum _i \| \texttt{samples} _i - \texttt{centers} _{ \texttt{labels} _i} \| ^2\f] +after every attempt. The best (minimum) value is chosen and the corresponding labels and the +compactness value are returned by the function. + - Integer array that stores the cluster indices for every sample. + - Array of the cluster centers. +*/ +GAPI_EXPORTS_W std::tuple,GMat,GMat> +kmeans(const GMat& data, const int K, const GMat& bestLabels, + const TermCriteria& criteria, const int attempts, const KmeansFlags flags); + +/** @overload +@note + - Function textual ID is "org.opencv.core.kmeansNDNoInit" + - #KMEANS_USE_INITIAL_LABELS flag must not be set while using this overload. + */ +GAPI_EXPORTS_W std::tuple,GMat,GMat> +kmeans(const GMat& data, const int K, const TermCriteria& criteria, const int attempts, + const KmeansFlags flags); + +/** @overload +@note Function textual ID is "org.opencv.core.kmeans2D" + */ +GAPI_EXPORTS_W std::tuple,GArray,GArray> +kmeans(const GArray& data, const int K, const GArray& bestLabels, + const TermCriteria& criteria, const int attempts, const KmeansFlags flags); + +/** @overload +@note Function textual ID is "org.opencv.core.kmeans3D" + */ +GAPI_EXPORTS_W std::tuple,GArray,GArray> +kmeans(const GArray& data, const int K, const GArray& bestLabels, + const TermCriteria& criteria, const int attempts, const KmeansFlags flags); + + +/** @brief Transposes a matrix. + +The function transposes the matrix: +\f[\texttt{dst} (i,j) = \texttt{src} (j,i)\f] + +@note + - Function textual ID is "org.opencv.core.transpose" + - No complex conjugation is done in case of a complex matrix. It should be done separately if needed. + +@param src input array. +*/ +GAPI_EXPORTS_W GMat transpose(const GMat& src); + + +namespace streaming { +/** @brief Gets dimensions from Mat. + +@note Function textual ID is "org.opencv.streaming.size" + +@param src Input tensor +@return Size (tensor dimensions). +*/ +GAPI_EXPORTS_W GOpaque size(const GMat& src); + +/** @overload +Gets dimensions from rectangle. + +@note Function textual ID is "org.opencv.streaming.sizeR" + +@param r Input rectangle. +@return Size (rectangle dimensions). +*/ +GAPI_EXPORTS_W GOpaque size(const GOpaque& r); + +/** @brief Gets dimensions from MediaFrame. + +@note Function textual ID is "org.opencv.streaming.sizeMF" + +@param src Input frame +@return Size (frame dimensions). +*/ +GAPI_EXPORTS_W GOpaque size(const GFrame& src); +} //namespace streaming +} //namespace gapi +} //namespace cv + +#endif //OPENCV_GAPI_CORE_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/core.hpp b/modules/gapi/include/opencv2/gapi/cpu/core.hpp new file mode 100644 index 00000000000..ee86fb72c23 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/core.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_CPU_CORE_API_HPP +#define OPENCV_GAPI_CPU_CORE_API_HPP + +#include // GKernelPackage +#include // GAPI_EXPORTS + +namespace cv { +namespace gapi { +namespace core { +namespace cpu { + +GAPI_EXPORTS_W cv::GKernelPackage kernels(); + +} // namespace cpu +} // namespace core +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_CORE_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp new file mode 100644 index 00000000000..eb5f7847478 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -0,0 +1,542 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2022 Intel Corporation + + +#ifndef OPENCV_GAPI_GCPUKERNEL_HPP +#define OPENCV_GAPI_GCPUKERNEL_HPP + +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) // "Unreachable code" on postprocess(...) call inside OCVCallHelper +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include //suppress_unused_warning +#include + +// FIXME: namespace scheme for backends? +namespace cv { + +namespace gimpl +{ + // Forward-declare an internal class + class GCPUExecutable; +} // namespace gimpl + +namespace gapi +{ +/** + * @brief This namespace contains G-API CPU backend functions, + * structures, and symbols. + */ +namespace cpu +{ + /** + * \addtogroup gapi_std_backends + * @{ + * + * @brief G-API backends available in this OpenCV version + * + * G-API backends play a corner stone role in G-API execution + * stack. Every backend is hardware-oriented and thus can run its + * kernels efficiently on the target platform. + * + * Backends are usually "black boxes" for G-API users -- on the API + * side, all backends are represented as different objects of the + * same class cv::gapi::GBackend. + * User can manipulate with backends by specifying which kernels to use. + * + * @sa @ref gapi_hld + */ + + /** + * @brief Get a reference to CPU (OpenCV) backend. + * + * This is the default backend in G-API at the moment, providing + * broader functional coverage but losing some graph model + * advantages. Provided mostly for reference and prototyping + * purposes. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ + + class GOCVFunctor; + + //! @cond IGNORED + template + GOCVFunctor ocv_kernel(const Callable& c); + + template + GOCVFunctor ocv_kernel(Callable& c); + //! @endcond + +} // namespace cpu +} // namespace gapi + +// Represents arguments which are passed to a wrapped CPU function +// FIXME: put into detail? +class GAPI_EXPORTS GCPUContext +{ +public: + // Generic accessor API + template + const T& inArg(int input) { return m_args.at(input).get(); } + + // Syntax sugar + const cv::Mat& inMat(int input); + cv::Mat& outMatR(int output); // FIXME: Avoid cv::Mat m = ctx.outMatR() + + const cv::Scalar& inVal(int input); + cv::Scalar& outValR(int output); // FIXME: Avoid cv::Scalar s = ctx.outValR() + cv::MediaFrame& outFrame(int output); + template std::vector& outVecR(int output) // FIXME: the same issue + { + return outVecRef(output).wref(); + } + template T& outOpaqueR(int output) // FIXME: the same issue + { + return outOpaqueRef(output).wref(); + } + + GArg state() + { + return m_state; + } + +protected: + detail::VectorRef& outVecRef(int output); + detail::OpaqueRef& outOpaqueRef(int output); + + std::vector m_args; + GArg m_state; + + //FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call + //to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run, + //once on enter for input and output arguments, and once before return for output arguments only + std::unordered_map m_results; + + friend class gimpl::GCPUExecutable; +}; + +class GAPI_EXPORTS GCPUKernel +{ +public: + // This function is a kernel's execution entry point (does the processing work) + using RunF = std::function; + // This function is a stateful kernel's setup routine (configures state) + using SetupF = std::function; + + GCPUKernel(); + GCPUKernel(const RunF& runF, const SetupF& setupF = nullptr); + + RunF m_runF = nullptr; + SetupF m_setupF = nullptr; + + bool m_isStateful = false; +}; + +// FIXME: This is an ugly ad-hoc implementation. TODO: refactor + +namespace detail +{ +template struct get_in; +template<> struct get_in +{ + static cv::Mat get(GCPUContext &ctx, int idx) { return ctx.inMat(idx); } +}; +template<> struct get_in +{ + static cv::Mat get(GCPUContext &ctx, int idx) { return get_in::get(ctx, idx); } +}; +template<> struct get_in +{ + static cv::MediaFrame get(GCPUContext &ctx, int idx) { return ctx.inArg(idx); } +}; +template<> struct get_in +{ + static cv::Scalar get(GCPUContext &ctx, int idx) { return ctx.inVal(idx); } +}; +template struct get_in > +{ + static const std::vector& get(GCPUContext &ctx, int idx) { return ctx.inArg(idx).rref(); } +}; +template struct get_in > +{ + static const U& get(GCPUContext &ctx, int idx) { return ctx.inArg(idx).rref(); } +}; + +//FIXME(dm): GArray/GArray conversion should be done more gracefully in the system +template<> struct get_in >: public get_in > +{ +}; + +//FIXME(dm): GArray/GArray conversion should be done more gracefully in the system +template<> struct get_in >: public get_in > +{ +}; + +// FIXME(dm): GArray>/GArray> conversion should be done more gracefully in the system +template struct get_in> >: public get_in> > +{ +}; + +//FIXME(dm): GOpaque/GOpaque conversion should be done more gracefully in the system +template<> struct get_in >: public get_in > +{ +}; + +//FIXME(dm): GOpaque/GOpaque conversion should be done more gracefully in the system +template<> struct get_in >: public get_in > +{ +}; + +template struct get_in +{ + static T get(GCPUContext &ctx, int idx) { return ctx.inArg(idx); } +}; + +struct tracked_cv_mat{ + tracked_cv_mat(cv::Mat& m) : r{m}, original_data{m.data} {} + cv::Mat r; + uchar* original_data; + + operator cv::Mat& (){ return r;} + void validate() const{ + if (r.data != original_data) + { + util::throw_error + (std::logic_error + ("OpenCV kernel output parameter was reallocated. \n" + "Incorrect meta data was provided ?")); + } + } +}; + +template +void postprocess(Outputs&... outs) +{ + struct + { + void operator()(tracked_cv_mat* bm) { bm->validate(); } + void operator()(...) { } + + } validate; + //dummy array to unfold parameter pack + int dummy[] = { 0, (validate(&outs), 0)... }; + cv::util::suppress_unused_warning(dummy); +} + +template struct get_out; +template<> struct get_out +{ + static tracked_cv_mat get(GCPUContext &ctx, int idx) + { + auto& r = ctx.outMatR(idx); + return {r}; + } +}; +template<> struct get_out +{ + static tracked_cv_mat get(GCPUContext &ctx, int idx) + { + return get_out::get(ctx, idx); + } +}; +template<> struct get_out +{ + static cv::Scalar& get(GCPUContext &ctx, int idx) + { + return ctx.outValR(idx); + } +}; +template<> struct get_out +{ + static cv::MediaFrame& get(GCPUContext &ctx, int idx) + { + return ctx.outFrame(idx); + } +}; +template struct get_out> +{ + static std::vector& get(GCPUContext &ctx, int idx) + { + return ctx.outVecR(idx); + } +}; + +//FIXME(dm): GArray/GArray conversion should be done more gracefully in the system +template<> struct get_out >: public get_out > +{ +}; + +// FIXME(dm): GArray>/GArray> conversion should be done more gracefully in the system +template struct get_out> >: public get_out> > +{ +}; + +template struct get_out> +{ + static U& get(GCPUContext &ctx, int idx) + { + return ctx.outOpaqueR(idx); + } +}; + +template +struct OCVSetupHelper; + +template +struct OCVSetupHelper> +{ + // Using 'auto' return type and 'decltype' specifier in both 'setup_impl' versions + // to check existence of required 'Impl::setup' functions. + // While 'decltype' specifier accepts expression we pass expression with 'comma-operator' + // where first operand of comma-operator is call attempt to desired 'Impl::setup' and + // the second operand is 'void()' expression. + // + // SFINAE for 'Impl::setup' which accepts compile arguments. + template + static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args, + GArg &state, const GCompileArgs &compileArgs, + detail::Seq) -> + decltype(Impl::setup(detail::get_in_meta(metaArgs, args, IIs)..., + std::declval + >::type + >(), + compileArgs) + , void()) + { + // TODO: unique_ptr <-> shared_ptr conversion ? + // To check: Conversion is possible only if the state which should be passed to + // 'setup' user callback isn't required to have previous value + std::shared_ptr stPtr; + Impl::setup(detail::get_in_meta(metaArgs, args, IIs)..., stPtr, compileArgs); + state = GArg(stPtr); + } + + // SFINAE for 'Impl::setup' which doesn't accept compile arguments. + template + static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args, + GArg &state, const GCompileArgs &/* compileArgs */, + detail::Seq) -> + decltype(Impl::setup(detail::get_in_meta(metaArgs, args, IIs)..., + std::declval + >::type + >() + ) + , void()) + { + // The same comment as in 'setup' above. + std::shared_ptr stPtr; + Impl::setup(detail::get_in_meta(metaArgs, args, IIs)..., stPtr); + state = GArg(stPtr); + } + + static void setup(const GMetaArgs &metaArgs, const GArgs &args, + GArg& state, const GCompileArgs &compileArgs) + { + setup_impl(metaArgs, args, state, compileArgs, + typename detail::MkSeq::type()); + } +}; + +// OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors. +template +struct OCVCallHelper; + +// FIXME: probably can be simplified with std::apply or analogue. +template +struct OCVCallHelper, std::tuple> +{ + template + struct call_and_postprocess + { + template + static void call(Inputs&&... ins, Outputs&&... outs) + { + //not using a std::forward on outs is deliberate in order to + //cause compilation error, by trying to bind rvalue references to lvalue references + Impl::run(std::forward(ins)..., outs...); + postprocess(outs...); + } + + template + static void call(Impl& impl, Inputs&&... ins, Outputs&&... outs) + { + impl(std::forward(ins)..., outs...); + } + }; + + template + static void call_impl(GCPUContext &ctx, detail::Seq, detail::Seq) + { + //Make sure that OpenCV kernels do not reallocate memory for output parameters + //by comparing it's state (data ptr) before and after the call. + //This is done by converting each output Mat into tracked_cv_mat object, and binding + //them to parameters of ad-hoc function + call_and_postprocess::get(ctx, IIs))...> + ::call(get_in::get(ctx, IIs)..., get_out::get(ctx, OIs)...); + } + + template + static void call_impl(cv::GCPUContext &ctx, Impl& impl, + detail::Seq, detail::Seq) + { + call_and_postprocess::get(ctx, IIs))...> + ::call(impl, get_in::get(ctx, IIs)..., get_out::get(ctx, OIs)...); + } + + static void call(GCPUContext &ctx) + { + call_impl(ctx, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } + + // NB: Same as call but calling the object + // This necessary for kernel implementations that have a state + // and are represented as an object + static void callFunctor(cv::GCPUContext &ctx, Impl& impl) + { + call_impl(ctx, impl, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } +}; + +// OCVStCallHelper is a helper class to call stateful OCV kernels. +template +struct OCVStCallHelper; + +template +struct OCVStCallHelper, std::tuple> : + OCVCallHelper, std::tuple> +{ + template + struct call_and_postprocess + { + template + static void call(typename Impl::State& st, Inputs&&... ins, Outputs&&... outs) + { + Impl::run(std::forward(ins)..., outs..., st); + postprocess(outs...); + } + }; + + template + static void call_impl(GCPUContext &ctx, detail::Seq, detail::Seq) + { + auto& st = *ctx.state().get>(); + call_and_postprocess::get(ctx, IIs))...> + ::call(st, get_in::get(ctx, IIs)..., get_out::get(ctx, OIs)...); + } + + static void call(GCPUContext &ctx) + { + call_impl(ctx, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } +}; + +} // namespace detail + +template +class GCPUKernelImpl: public cv::detail::KernelTag +{ + using CallHelper = cv::detail::OCVCallHelper; + +public: + using API = K; + + static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); } + static cv::GCPUKernel kernel() { return GCPUKernel(&CallHelper::call); } +}; + +template +class GCPUStKernelImpl: public cv::detail::KernelTag +{ + using StSetupHelper = detail::OCVSetupHelper; + using StCallHelper = detail::OCVStCallHelper; + +public: + using API = K; + using State = S; + + static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); } + static cv::GCPUKernel kernel() { return GCPUKernel(&StCallHelper::call, + &StSetupHelper::setup); } +}; + +#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl + +// TODO: Reuse Anatoliy's logic for support of types with commas in macro. +// Retrieve the common part from Anatoliy's logic to the separate place. +#define GAPI_OCV_KERNEL_ST(Name, API, State) \ + struct Name: public cv::GCPUStKernelImpl \ + +/// @private +class gapi::cpu::GOCVFunctor : public gapi::GFunctor +{ +public: + using Impl = std::function; + using Meta = cv::GKernel::M; + + GOCVFunctor(const char* id, const Meta &meta, const Impl& impl) + : gapi::GFunctor(id), impl_{GCPUKernel(impl), meta} + { + } + + GKernelImpl impl() const override { return impl_; } + gapi::GBackend backend() const override { return gapi::cpu::backend(); } + +private: + GKernelImpl impl_; +}; + +//! @cond IGNORED +template +gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(Callable& c) +{ + using P = cv::detail::OCVCallHelper; + return GOCVFunctor{ K::id() + , &K::getOutMeta + , std::bind(&P::callFunctor, std::placeholders::_1, std::ref(c)) + }; +} + +template +gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c) +{ + using P = cv::detail::OCVCallHelper; + return GOCVFunctor{ K::id() + , &K::getOutMeta + , std::bind(&P::callFunctor, std::placeholders::_1, c) + }; +} +//! @endcond + +} // namespace cv + +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#endif // OPENCV_GAPI_GCPUKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp b/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp new file mode 100644 index 00000000000..0b96db08ae1 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_CPU_IMGPROC_API_HPP +#define OPENCV_GAPI_CPU_IMGPROC_API_HPP + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace imgproc { +namespace cpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace cpu +} // namespace imgproc +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_IMGPROC_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/ot.hpp b/modules/gapi/include/opencv2/gapi/cpu/ot.hpp new file mode 100644 index 00000000000..03dbe904cc9 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/ot.hpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_CPU_OT_API_HPP +#define OPENCV_GAPI_CPU_OT_API_HPP + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +/** + * @brief This namespace contains G-API Operation Types for + * VAS Object Tracking module functionality. + */ +namespace ot { +namespace cpu { +GAPI_EXPORTS_W GKernelPackage kernels(); +} // namespace cpu +} // namespace ot +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_OT_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/stereo.hpp b/modules/gapi/include/opencv2/gapi/cpu/stereo.hpp new file mode 100644 index 00000000000..e2a2242bd0b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/stereo.hpp @@ -0,0 +1,48 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_CPU_STEREO_API_HPP +#define OPENCV_GAPI_CPU_STEREO_API_HPP + +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace calib3d { +namespace cpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +/** @brief Structure for the Stereo operation initialization parameters.*/ +struct GAPI_EXPORTS StereoInitParam { + StereoInitParam(int nD, int bS, double bL, double f): + numDisparities(nD), blockSize(bS), baseline(bL), focus(f) {} + + StereoInitParam() = default; + + int numDisparities = 0; + int blockSize = 21; + double baseline = 63.5; + double focus = 3.6; +}; + +} // namespace cpu +} // namespace calib3d +} // namespace gapi + +namespace detail { + + template<> struct CompileArgTag { + static const char* tag() { + return "org.opencv.stereoInit"; + } +}; + +} // namespace detail +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_STEREO_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/cpu/video.hpp b/modules/gapi/include/opencv2/gapi/cpu/video.hpp new file mode 100644 index 00000000000..d3c1f2e6704 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/video.hpp @@ -0,0 +1,25 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_CPU_VIDEO_API_HPP +#define OPENCV_GAPI_CPU_VIDEO_API_HPP + +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace video { +namespace cpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace cpu +} // namespace video +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_VIDEO_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/fluid/core.hpp b/modules/gapi/include/opencv2/gapi/fluid/core.hpp new file mode 100644 index 00000000000..a4329d6f50f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/fluid/core.hpp @@ -0,0 +1,20 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_FLUID_CORE_HPP +#define OPENCV_GAPI_FLUID_CORE_HPP + +#include // GKernelPackage +#include // GAPI_EXPORTS + +namespace cv { namespace gapi { namespace core { namespace fluid { + +GAPI_EXPORTS_W cv::GKernelPackage kernels(); + +}}}} + +#endif // OPENCV_GAPI_FLUID_CORE_HPP diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp new file mode 100644 index 00000000000..551f0a398fe --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp @@ -0,0 +1,154 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_FLUID_BUFFER_HPP +#define OPENCV_GAPI_FLUID_BUFFER_HPP + +#include +#include // accumulate +#include // ostream +#include // uint8_t + +#include +#include + +#include + +namespace cv { +namespace gapi { +namespace fluid { + +struct Border +{ + // This constructor is required to support existing kernels which are part of G-API + Border(int _type, cv::Scalar _val) : type(_type), value(_val) {} + + int type; + cv::Scalar value; +}; + +using BorderOpt = util::optional; + +bool operator == (const Border& b1, const Border& b2); + +class GAPI_EXPORTS Buffer; + +class GAPI_EXPORTS View +{ +public: + struct Cache + { + std::vector m_linePtrs; + GMatDesc m_desc; + int m_border_size = 0; + + inline const uint8_t* linePtr(int index) const + { + // "out_of_window" check: + // user must not request the lines which are outside of specified kernel window + GAPI_DbgAssert(index >= -m_border_size + && index < -m_border_size + static_cast(m_linePtrs.size())); + return m_linePtrs[index + m_border_size]; + } + }; + + const inline uint8_t* InLineB(int index) const // -(w-1)/2...0...+(w-1)/2 for Filters + { + return m_cache->linePtr(index); + } + + template const inline T* InLine(int i) const + { + const uint8_t* ptr = this->InLineB(i); + return reinterpret_cast(ptr); + } + + inline operator bool() const { return m_priv != nullptr; } + bool ready() const; + inline int length() const { return m_cache->m_desc.size.width; } + int y() const; + + inline const GMatDesc& meta() const { return m_cache->m_desc; } + + class GAPI_EXPORTS Priv; // internal use only + Priv& priv(); // internal use only + const Priv& priv() const; // internal use only + + View(); + View(std::unique_ptr&& p); + View(View&& v); + View& operator=(View&& v); + ~View(); + +private: + std::unique_ptr m_priv; + const Cache* m_cache = nullptr; +}; + +class GAPI_EXPORTS Buffer +{ +public: + struct Cache + { + std::vector m_linePtrs; + GMatDesc m_desc; + }; + + // Default constructor (executable creation stage, + // all following initialization performed in Priv::init()) + Buffer(); + // Scratch constructor (user kernels) + Buffer(const cv::GMatDesc &desc); + + // Constructor for intermediate buffers (for tests) + Buffer(const cv::GMatDesc &desc, + int max_line_consumption, int border_size, + int skew, + int wlpi, + BorderOpt border); + // Constructor for in/out buffers (for tests) + Buffer(const cv::Mat &data, bool is_input); + ~Buffer(); + Buffer& operator=(Buffer&&); + + inline uint8_t* OutLineB(int index = 0) + { + return m_cache->m_linePtrs[index]; + } + + template inline T* OutLine(int index = 0) + { + uint8_t* ptr = this->OutLineB(index); + return reinterpret_cast(ptr); + } + + int y() const; + + int linesReady() const; + void debug(std::ostream &os) const; + inline int length() const { return m_cache->m_desc.size.width; } + int lpi() const; // LPI for WRITER + + inline const GMatDesc& meta() const { return m_cache->m_desc; } + + View mkView(int borderSize, bool ownStorage); + void addView(const View* v); + + class GAPI_EXPORTS Priv; // internal use only + Priv& priv(); // internal use only + const Priv& priv() const; // internal use only + +private: + std::unique_ptr m_priv; + const Cache* m_cache; +}; + +} // namespace cv::gapi::fluid +} // namespace cv::gapi +} // namespace cv + +#endif // OPENCV_GAPI_FLUID_BUFFER_HPP diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp new file mode 100644 index 00000000000..c3ae9dfdd6e --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -0,0 +1,442 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2019 Intel Corporation + + +#ifndef OPENCV_GAPI_FLUID_KERNEL_HPP +#define OPENCV_GAPI_FLUID_KERNEL_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +// FIXME: namespace scheme for backends? +namespace cv { + +namespace gapi +{ +/** + * @brief This namespace contains G-API Fluid backend functions, structures, and symbols. + */ +namespace fluid +{ + /** + * \addtogroup gapi_std_backends G-API Standard Backends + * @{ + */ + /** + * @brief Get a reference to Fluid backend. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ +} // namespace fluid +} // namespace gapi + + +class GAPI_EXPORTS GFluidKernel +{ +public: + enum class Kind + { + Filter, + Resize, + YUV420toRGB //Color conversion of 4:2:0 chroma sub-sampling formats (NV12, I420 ..etc) to RGB + }; + + // This function is a generic "doWork" callback + using F = std::function &)>; + + // This function is a generic "initScratch" callback + using IS = std::function; + + // This function is a generic "resetScratch" callback + using RS = std::function; + + // This function describes kernel metadata inference rule. + using M = std::function; + + // This function is a generic "getBorder" callback (extracts border-related data from kernel's input parameters) + using B = std::function; + + // This function is a generic "getWindow" callback (extracts window-related data from kernel's input parameters) + using GW = std::function; + + // FIXME: move implementations out of header file + GFluidKernel() {} + GFluidKernel(Kind k, int l, bool scratch, const F& f, const IS &is, const RS &rs, const B& b, const GW& win) + : m_kind(k) + , m_lpi(l) + , m_scratch(scratch) + , m_f(f) + , m_is(is) + , m_rs(rs) + , m_b(b) + , m_gw(win) {} + + Kind m_kind; + const int m_lpi = -1; + const bool m_scratch = false; + + const F m_f; + const IS m_is; + const RS m_rs; + const B m_b; + const GW m_gw; +}; + +// FIXME!!! +// This is the temporary and experimental API +// which should be replaced by runtime roi-based scheduling +/** \addtogroup gapi_compile_args + * @{ + */ +/** + * @brief This structure allows to control the output image region + * which Fluid backend will produce in the graph. + * + * This feature is useful for external tiling and parallelism, but + * will be deprecated in the future releases. + */ +struct GFluidOutputRois +{ + std::vector rois; +}; + +/** + * @brief This structure forces Fluid backend to generate multiple + * parallel output regions in the graph. These regions execute in parallel. + * + * This feature may be deprecated in the future releases. + */ +struct GFluidParallelOutputRois +{ + std::vector parallel_rois; +}; + +/** + * @brief This structure allows to customize the way how Fluid executes + * parallel regions. + * + * For example, user can utilize his own threading runtime via this parameter. + * The `parallel_for` member functor is called by the Fluid runtime with the + * following arguments: + * + * @param size Size of the parallel range to process + * @param f A function which should be called for every integer index + * in this range by the specified parallel_for implementation. + * + * This feature may be deprecated in the future releases. + */ +struct GFluidParallelFor +{ + //this function accepts: + // - size of the "parallel" range as the first argument + // - and a function to be called on the range items, designated by item index + std::function)> parallel_for; +}; +/** @} gapi_compile_args */ + +namespace detail +{ +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.fluid.outputRois"; } +}; + +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.fluid.parallelFor"; } +}; + +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.fluid.parallelOutputRois"; } +}; + +} // namespace detail + +namespace detail +{ +template struct fluid_get_in; +template<> struct fluid_get_in +{ + static const cv::gapi::fluid::View& get(const cv::GArgs &in_args, int idx) + { + return *in_args[idx].unsafe_get(); + } +}; + +template<> struct fluid_get_in +{ + // FIXME: change to return by reference when moved to own::Scalar + static cv::Scalar get(const cv::GArgs &in_args, int idx) + { + return in_args[idx].unsafe_get(); + } +}; + +template struct fluid_get_in> +{ + static const std::vector& get(const cv::GArgs &in_args, int idx) + { + return in_args.at(idx).unsafe_get().rref(); + } +}; + +template struct fluid_get_in> +{ + static const U& get(const cv::GArgs &in_args, int idx) + { + return in_args.at(idx).unsafe_get().rref(); + } +}; + +template struct fluid_get_in +{ + static const T& get(const cv::GArgs &in_args, int idx) + { + return in_args[idx].unsafe_get(); + } +}; + +template +struct scratch_helper; + +template +struct scratch_helper +{ + // Init + template + static void help_init_impl(const cv::GMetaArgs &metas, + const cv::GArgs &in_args, + gapi::fluid::Buffer &scratch_buf, + detail::Seq) + { + Impl::initScratch(get_in_meta(metas, in_args, IIs)..., scratch_buf); + } + + static void help_init(const cv::GMetaArgs &metas, + const cv::GArgs &in_args, + gapi::fluid::Buffer &b) + { + help_init_impl(metas, in_args, b, typename detail::MkSeq::type()); + } + + // Reset + static void help_reset(gapi::fluid::Buffer &b) + { + Impl::resetScratch(b); + } +}; + +template +struct scratch_helper +{ + static void help_init(const cv::GMetaArgs &, + const cv::GArgs &, + gapi::fluid::Buffer &) + { + GAPI_Error("InternalError"); + } + static void help_reset(gapi::fluid::Buffer &) + { + GAPI_Error("InternalError"); + } +}; + +template struct is_gmat_type +{ + static const constexpr bool value = std::is_same::value; +}; + +template +struct get_border_helper; + +template +struct get_border_helper +{ + template + static gapi::fluid::BorderOpt get_border_impl(const GMetaArgs &metas, + const cv::GArgs &in_args, + cv::detail::Seq) + { + return util::make_optional(Impl::getBorder(cv::detail::get_in_meta(metas, in_args, IIs)...)); + } + + static gapi::fluid::BorderOpt help(const GMetaArgs &metas, + const cv::GArgs &in_args) + { + return get_border_impl(metas, in_args, typename detail::MkSeq::type()); + } +}; + +template +struct get_border_helper +{ + static gapi::fluid::BorderOpt help(const cv::GMetaArgs &, + const cv::GArgs &) + { + return {}; + } +}; + +template +struct get_window_helper; + +template +struct get_window_helper +{ + template + static int get_window_impl(const GMetaArgs &metas, + const cv::GArgs &in_args, + cv::detail::Seq) + { + return Impl::getWindow(cv::detail::get_in_meta(metas, in_args, IIs)...); + } + + static int help(const GMetaArgs &metas, const cv::GArgs &in_args) + { + return get_window_impl(metas, in_args, typename detail::MkSeq::type()); + } +}; + +template +struct get_window_helper +{ + static int help(const cv::GMetaArgs &, + const cv::GArgs &) + { + return Impl::Window; + } +}; + +template +struct has_Window +{ +private: + template + static constexpr auto Check(U*) -> typename std::is_same::type; + + template + static constexpr std::false_type Check(...); + + typedef decltype(Check(0)) Result; + +public: + static constexpr bool value = Result::value; +}; + +template +struct callCustomGetBorder; + +template +struct callCustomGetBorder +{ + static constexpr bool value = (Impl::Window != 1); +}; + +template +struct callCustomGetBorder +{ + static constexpr bool value = true; +}; + +template +struct FluidCallHelper; + +template +struct FluidCallHelper, std::tuple, UseScratch> +{ + static_assert(all_satisfy::value, "return type must be GMat"); + static_assert(contains::value, "input must contain at least one GMat"); + + // Execution dispatcher //////////////////////////////////////////////////// + template + static void call_impl(const cv::GArgs &in_args, + const std::vector &out_bufs, + detail::Seq, + detail::Seq) + { + Impl::run(fluid_get_in::get(in_args, IIs)..., *out_bufs[OIs]...); + } + + static void call(const cv::GArgs &in_args, + const std::vector &out_bufs) + { + constexpr int numOuts = (sizeof...(Outs)) + (UseScratch ? 1 : 0); + call_impl(in_args, out_bufs, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } + + // Scratch buffer initialization dispatcher //////////////////////////////// + static void init_scratch(const GMetaArgs &metas, + const cv::GArgs &in_args, + gapi::fluid::Buffer &b) + { + scratch_helper::help_init(metas, in_args, b); + } + + // Scratch buffer reset dispatcher ///////////////////////////////////////// + static void reset_scratch(gapi::fluid::Buffer &scratch_buf) + { + scratch_helper::help_reset(scratch_buf); + } + + static gapi::fluid::BorderOpt getBorder(const GMetaArgs &metas, const cv::GArgs &in_args) + { + constexpr bool hasWindow = has_Window::value; + + // User must provide "init" callback if Window != 1 + // TODO: move to constexpr if when we enable C++17 + return get_border_helper::value, Impl, Ins...>::help(metas, in_args); + } + + static int getWindow(const GMetaArgs &metas, const cv::GArgs &in_args) + { + constexpr bool callCustomGetWindow = !(has_Window::value); + return get_window_helper::help(metas, in_args); + } +}; +} // namespace detail + + +template +class GFluidKernelImpl : public cv::detail::KernelTag +{ + static const int LPI = 1; + static const auto Kind = GFluidKernel::Kind::Filter; + using P = detail::FluidCallHelper; + +public: + using API = K; + + static GFluidKernel kernel() + { + // FIXME: call() and getOutMeta() needs to be renamed so it is clear these + // functions are internal wrappers, not user API + return GFluidKernel(Impl::Kind, Impl::LPI, + UseScratch, + &P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder, &P::getWindow); + } + + static cv::gapi::GBackend backend() { return cv::gapi::fluid::backend(); } +}; + +#define GAPI_FLUID_KERNEL(Name, API, Scratch) struct Name: public cv::GFluidKernelImpl + +} // namespace cv + +#endif // OPENCV_GAPI_GCPUKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp b/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp new file mode 100644 index 00000000000..a4e8ac0f992 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp @@ -0,0 +1,20 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_FLUID_IMGPROC_HPP +#define OPENCV_GAPI_FLUID_IMGPROC_HPP + +#include // GKernelPackage +#include // GAPI_EXPORTS + +namespace cv { namespace gapi { namespace imgproc { namespace fluid { + +GAPI_EXPORTS_W GKernelPackage kernels(); + +}}}} + +#endif // OPENCV_GAPI_FLUID_IMGPROC_HPP diff --git a/modules/gapi/include/opencv2/gapi/garg.hpp b/modules/gapi/include/opencv2/gapi/garg.hpp new file mode 100644 index 00000000000..2a8315f9d83 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/garg.hpp @@ -0,0 +1,311 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_GARG_HPP +#define OPENCV_GAPI_GARG_HPP + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cv { + +class GArg; + +namespace detail { + template + using is_garg = std::is_same::type>; +} + +// Parameter holder class for a node +// Depending on platform capabilities, can either support arbitrary types +// (as `boost::any`) or a limited number of types (as `boot::variant`). +// FIXME: put into "details" as a user shouldn't use it in his code +class GAPI_EXPORTS GArg +{ +public: + GArg() {} + + template::value, int>::type = 0> + explicit GArg(const T &t) + : kind(detail::GTypeTraits::kind) + , opaque_kind(detail::GOpaqueTraits::kind) + , value(detail::wrap_gapi_helper::wrap(t)) + { + } + + template::value, int>::type = 0> + explicit GArg(T &&t) + : kind(detail::GTypeTraits::type>::kind) + , opaque_kind(detail::GOpaqueTraits::type>::kind) + , value(detail::wrap_gapi_helper::wrap(t)) + { + } + + template inline T& get() + { + return util::any_cast::type>(value); + } + + template inline const T& get() const + { + return util::any_cast::type>(value); + } + + template inline T& unsafe_get() + { + return util::unsafe_any_cast::type>(value); + } + + template inline const T& unsafe_get() const + { + return util::unsafe_any_cast::type>(value); + } + + detail::ArgKind kind = detail::ArgKind::OPAQUE_VAL; + detail::OpaqueKind opaque_kind = detail::OpaqueKind::CV_UNKNOWN; + +protected: + util::any value; +}; + +using GArgs = std::vector; + +// FIXME: Express as M::type +// FIXME: Move to a separate file! +using GRunArgBase = util::variant< +#if !defined(GAPI_STANDALONE) + cv::UMat, +#endif // !defined(GAPI_STANDALONE) + cv::RMat, + cv::gapi::wip::IStreamSource::Ptr, + cv::Mat, + cv::Scalar, + cv::detail::VectorRef, + cv::detail::OpaqueRef, + cv::MediaFrame + >; + +namespace detail { +template +struct in_variant; + +template +struct in_variant > + : std::integral_constant::value > { +}; +} // namespace detail + +struct GAPI_EXPORTS GRunArg: public GRunArgBase +{ + // Metadata information here + using Meta = std::unordered_map; + Meta meta; + + // Mimic the old GRunArg semantics here, old of the times when + // GRunArg was an alias to variant<> + GRunArg(); + GRunArg(const cv::GRunArg &arg); + GRunArg(cv::GRunArg &&arg); + + GRunArg& operator= (const GRunArg &arg); + GRunArg& operator= (GRunArg &&arg); + + template + GRunArg(const T &t, + const Meta &m = Meta{}, + typename std::enable_if< detail::in_variant::value, int>::type = 0) + : GRunArgBase(t) + , meta(m) + { + } + template + GRunArg(T &&t, + const Meta &m = Meta{}, + typename std::enable_if< detail::in_variant::value, int>::type = 0) + : GRunArgBase(std::move(t)) + , meta(m) + { + } + template auto operator= (const T &t) + -> typename std::enable_if< detail::in_variant::value, cv::GRunArg>::type& + { + GRunArgBase::operator=(t); + return *this; + } + template auto operator= (T&& t) + -> typename std::enable_if< detail::in_variant::value, cv::GRunArg>::type& + { + GRunArgBase::operator=(std::move(t)); + return *this; + } +}; +using GRunArgs = std::vector; + +// TODO: Think about the addition operator +/** + * @brief This operator allows to complement the input vector at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgs usage + * + */ +inline GRunArgs& operator += (GRunArgs &lhs, const GRunArgs &rhs) +{ + lhs.reserve(lhs.size() + rhs.size()); + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + +namespace gapi +{ +namespace wip +{ +/** + * @brief This aggregate type represents all types which G-API can + * handle (via variant). + * + * It only exists to overcome C++ language limitations (where a + * `using`-defined class can't be forward-declared). + */ +struct GAPI_EXPORTS Data: public GRunArg +{ + using GRunArg::GRunArg; + template + Data& operator= (const T& t) { GRunArg::operator=(t); return *this; } + template + Data& operator= (T&& t) { GRunArg::operator=(std::move(t)); return *this; } +}; +} // namespace wip +} // namespace gapi + +using GRunArgP = util::variant< +#if !defined(GAPI_STANDALONE) + cv::UMat*, +#endif // !defined(GAPI_STANDALONE) + cv::Mat*, + cv::RMat*, + cv::Scalar*, + cv::MediaFrame*, + cv::detail::VectorRef, + cv::detail::OpaqueRef + >; +using GRunArgsP = std::vector; + +// TODO: Think about the addition operator +/** + * @brief This operator allows to complement the output vector at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgsP usage + * + */ +inline GRunArgsP& operator += (GRunArgsP &lhs, const GRunArgsP &rhs) +{ + lhs.reserve(lhs.size() + rhs.size()); + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + +namespace gapi +{ +/** + * \addtogroup gapi_serialization + * @{ + * + * @brief G-API functions and classes for serialization and deserialization. + */ + +/** @brief Wraps deserialized output GRunArgs to GRunArgsP which can be used by GCompiled. + * + * Since it's impossible to get modifiable output arguments from deserialization + * it needs to be wrapped by this function. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind after deserialization + * + * @param out_args deserialized GRunArgs. + * @return the same GRunArgs wrapped in GRunArgsP. + * @see deserialize + */ +GAPI_EXPORTS cv::GRunArgsP bind(cv::GRunArgs &out_args); + +/** @brief Wraps output GRunArgsP available during graph execution to GRunArgs which can be serialized. + * + * GRunArgsP is pointer-to-value, so to be serialized they need to be binded to real values + * which this function does. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind before serialization + * + * @param out output GRunArgsP available during graph execution. + * @return the same GRunArgsP wrapped in serializable GRunArgs. + * @see serialize + */ +GAPI_EXPORTS cv::GRunArg bind(cv::GRunArgP &out); // FIXME: think more about it +/** @} */ +} + +template inline GRunArgs gin(const Ts&... args) +{ + return GRunArgs{ GRunArg(detail::wrap_host_helper::wrap_in(args))... }; +} + +template inline GRunArgsP gout(Ts&... args) +{ + return GRunArgsP{ GRunArgP(detail::wrap_host_helper::wrap_out(args))... }; +} + +struct GTypeInfo; +using GTypesInfo = std::vector; + +// FIXME: Needed for python bridge, must be moved to more appropriate header +namespace detail { +struct ExtractArgsCallback +{ + cv::GRunArgs operator()(const cv::GTypesInfo& info) const { return c(info); } + using CallBackT = std::function; + CallBackT c; +}; + +struct ExtractMetaCallback +{ + cv::GMetaArgs operator()(const cv::GTypesInfo& info) const { return c(info); } + using CallBackT = std::function; + CallBackT c; +}; + +void constructGraphOutputs(const cv::GTypesInfo &out_info, + cv::GRunArgs &args, + cv::GRunArgsP &outs); +} // namespace detail + +} // namespace cv + +#endif // OPENCV_GAPI_GARG_HPP diff --git a/modules/gapi/include/opencv2/gapi/garray.hpp b/modules/gapi/include/opencv2/gapi/garray.hpp new file mode 100644 index 00000000000..a2951993f2b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/garray.hpp @@ -0,0 +1,440 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GARRAY_HPP +#define OPENCV_GAPI_GARRAY_HPP + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include // flatten_g only! +#include // flatten_g only! + +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; +template class GArray; + +/** + * \addtogroup gapi_meta_args + * @{ + */ +struct GAPI_EXPORTS_W_SIMPLE GArrayDesc +{ + // FIXME: Body + // FIXME: Also implement proper operator== then + bool operator== (const GArrayDesc&) const { return true; } +}; +template GArrayDesc descr_of(const std::vector &) { return {};} +GAPI_EXPORTS_W inline GArrayDesc empty_array_desc() {return {}; } +/** @} */ + +std::ostream& operator<<(std::ostream& os, const cv::GArrayDesc &desc); + +namespace detail +{ + // ConstructVec is a callback which stores information about T and is used by + // G-API runtime to construct arrays in host memory (T remains opaque for G-API). + // ConstructVec is carried into G-API internals by GArrayU. + // Currently it is suitable for Host (CPU) plugins only, real offload may require + // more information for manual memory allocation on-device. + class VectorRef; + using ConstructVec = std::function; + + // This is the base struct for GArrayU type holder + struct TypeHintBase{virtual ~TypeHintBase() = default;}; + + // This class holds type of initial GArray to be checked from GArrayU + template + struct TypeHint final : public TypeHintBase{}; + + // This class strips type information from GArray and makes it usable + // in the G-API graph compiler (expression unrolling, graph generation, etc). + // Part of GProtoArg. + class GAPI_EXPORTS GArrayU + { + public: + GArrayU(const GNode &n, std::size_t out); // Operation result constructor + + template + bool holds() const; // Check if was created from GArray + + GOrigin& priv(); // Internal use only + const GOrigin& priv() const; // Internal use only + + protected: + GArrayU(); // Default constructor + GArrayU(const detail::VectorRef& vref); // Constant value constructor + template friend class cv::GArray; // (available to GArray only) + + void setConstructFcn(ConstructVec &&cv); // Store T-aware constructor + + template + void specifyType(); // Store type of initial GArray + + template + void storeKind(); + + void setKind(cv::detail::OpaqueKind); + + std::shared_ptr m_priv; + std::shared_ptr m_hint; + }; + + template + bool GArrayU::holds() const{ + GAPI_Assert(m_hint != nullptr); + using U = typename std::decay::type; + return dynamic_cast*>(m_hint.get()) != nullptr; + } + + template + void GArrayU::specifyType(){ + m_hint.reset(new TypeHint::type>); + } + + template + void GArrayU::storeKind(){ + setKind(cv::detail::GOpaqueTraits::kind); + } + + // This class represents a typed STL vector reference. + // Depending on origins, this reference may be either "just a" reference to + // an object created externally, OR actually own the underlying object + // (be value holder). + class BasicVectorRef + { + public: + // These fields are set by the derived class(es) + std::size_t m_elemSize = 0ul; + cv::GArrayDesc m_desc; + virtual ~BasicVectorRef() {} + + virtual void mov(BasicVectorRef &ref) = 0; + virtual const void* ptr() const = 0; + virtual std::size_t size() const = 0; + }; + + template class VectorRefT final: public BasicVectorRef + { + using empty_t = util::monostate; + using ro_ext_t = const std::vector *; + using rw_ext_t = std::vector *; + using rw_own_t = std::vector ; + util::variant m_ref; + + inline bool isEmpty() const { return util::holds_alternative(m_ref); } + inline bool isROExt() const { return util::holds_alternative(m_ref); } + inline bool isRWExt() const { return util::holds_alternative(m_ref); } + inline bool isRWOwn() const { return util::holds_alternative(m_ref); } + + void init(const std::vector* vec = nullptr) + { + m_elemSize = sizeof(T); + if (vec) m_desc = cv::descr_of(*vec); + } + + public: + VectorRefT() { init(); } + virtual ~VectorRefT() {} + + explicit VectorRefT(const std::vector& vec) : m_ref(&vec) { init(&vec); } + explicit VectorRefT(std::vector& vec) : m_ref(&vec) { init(&vec); } + explicit VectorRefT(std::vector&& vec) : m_ref(std::move(vec)) { init(&vec); } + + // Reset a VectorRefT. Called only for objects instantiated + // internally in G-API (e.g. temporary GArray's within a + // computation). Reset here means both initialization + // (creating an object) and reset (discarding its existing + // content before the next execution). Must never be called + // for external VectorRefTs. + void reset() + { + if (isEmpty()) + { + std::vector empty_vector; + m_desc = cv::descr_of(empty_vector); + m_ref = std::move(empty_vector); + GAPI_Assert(isRWOwn()); + } + else if (isRWOwn()) + { + util::get(m_ref).clear(); + } + else GAPI_Error("InternalError"); // shouldn't be called in *EXT modes + } + + // Obtain a WRITE reference to underlying object + // Used by CPU kernel API wrappers when a kernel execution frame + // is created + std::vector& wref() + { + GAPI_Assert(isRWExt() || isRWOwn()); + if (isRWExt()) return *util::get(m_ref); + if (isRWOwn()) return util::get(m_ref); + util::throw_error(std::logic_error("Impossible happened")); + } + + // Obtain a READ reference to underlying object + // Used by CPU kernel API wrappers when a kernel execution frame + // is created + const std::vector& rref() const + { + // ANY vector can be accessed for reading, even if it declared for + // output. Example -- a GComputation from [in] to [out1,out2] + // where [out2] is a result of operation applied to [out1]: + // + // GComputation boundary + // . . . . . . . + // . . + // [in] ----> foo() ----> [out1] + // . . : + // . . . .:. . . + // . V . + // . bar() ---> [out2] + // . . . . . . . . . . . . + // + if (isROExt()) return *util::get(m_ref); + if (isRWExt()) return *util::get(m_ref); + if (isRWOwn()) return util::get(m_ref); + util::throw_error(std::logic_error("Impossible happened")); + } + + virtual void mov(BasicVectorRef &v) override { + VectorRefT *tv = dynamic_cast*>(&v); + GAPI_Assert(tv != nullptr); + wref() = std::move(tv->wref()); + } + + virtual const void* ptr() const override { return &rref(); } + virtual std::size_t size() const override { return rref().size(); } + }; + + // This class strips type information from VectorRefT<> and makes it usable + // in the G-API executables (carrying run-time data/information to kernels). + // Part of GRunArg. + // Its methods are typed proxies to VectorRefT. + // VectorRef maintains "reference" semantics so two copies of VectoRef refer + // to the same underlying object. + // FIXME: Put a good explanation on why cv::OutputArray doesn't fit this role + class VectorRef + { + std::shared_ptr m_ref; + cv::detail::OpaqueKind m_kind = cv::detail::OpaqueKind::CV_UNKNOWN; + + template inline void check() const + { + GAPI_DbgAssert(dynamic_cast*>(m_ref.get()) != nullptr); + GAPI_Assert(sizeof(T) == m_ref->m_elemSize); + } + + public: + VectorRef() = default; + template explicit VectorRef(const std::vector& vec) + : m_ref(new VectorRefT(vec)) + , m_kind(GOpaqueTraits::kind) + {} + template explicit VectorRef(std::vector& vec) + : m_ref(new VectorRefT(vec)) + , m_kind(GOpaqueTraits::kind) + {} + template explicit VectorRef(std::vector&& vec) + : m_ref(new VectorRefT(std::move(vec))) + , m_kind(GOpaqueTraits::kind) + {} + + cv::detail::OpaqueKind getKind() const + { + return m_kind; + } + + template void reset() + { + if (!m_ref) m_ref.reset(new VectorRefT()); + check(); + storeKind(); + static_cast&>(*m_ref).reset(); + } + + template + void storeKind() + { + m_kind = cv::detail::GOpaqueTraits::kind; + } + + template std::vector& wref() + { + check(); + return static_cast&>(*m_ref).wref(); + } + + template const std::vector& rref() const + { + check(); + return static_cast&>(*m_ref).rref(); + } + + // Check if was created for/from std::vector + template bool holds() const + { + if (!m_ref) return false; + using U = typename std::decay::type; + return dynamic_cast*>(m_ref.get()) != nullptr; + } + + void mov(VectorRef &v) + { + m_ref->mov(*v.m_ref); + } + + cv::GArrayDesc descr_of() const + { + return m_ref->m_desc; + } + + std::size_t size() const + { + return m_ref->size(); + } + + // May be used to uniquely identify this object internally + const void *ptr() const { return m_ref->ptr(); } + }; + + // Helper (FIXME: work-around?) + // stripping G types to their host types + // like cv::GArray would still map to std::vector + // but not to std::vector +#if defined(GAPI_STANDALONE) +# define FLATTEN_NS cv::gapi::own +#else +# define FLATTEN_NS cv +#endif + template struct flatten_g; + template<> struct flatten_g { using type = FLATTEN_NS::Mat; }; + template<> struct flatten_g { using type = FLATTEN_NS::Scalar; }; + template struct flatten_g> { using type = std::vector; }; + template struct flatten_g { using type = T; }; +#undef FLATTEN_NS + // FIXME: the above mainly duplicates "ProtoToParam" thing from gtyped.hpp + // but I decided not to include gtyped here - probably worth moving that stuff + // to some common place? (DM) +} // namespace detail + +/** \addtogroup gapi_data_objects + * @{ + */ +/** + * @brief `cv::GArray` template class represents a list of objects + * of class `T` in the graph. + * + * `cv::GArray` describes a functional relationship between + * operations consuming and producing arrays of objects of class + * `T`. The primary purpose of `cv::GArray` is to represent a + * dynamic list of objects -- where the size of the list is not known + * at the graph construction or compile time. Examples include: corner + * and feature detectors (`cv::GArray`), object detection + * and tracking results (`cv::GArray`). Programmers can use + * their own types with `cv::GArray` in the custom operations. + * + * Similar to `cv::GScalar`, `cv::GArray` may be value-initialized + * -- in this case a graph-constant value is associated with the object. + * + * `GArray` is a virtual counterpart of `std::vector`, which is + * usually used to represent the `GArray` data in G-API during the + * execution. + * + * @sa `cv::GOpaque` + */ +template class GArray +{ +public: + // Host type (or Flat type) - the type this GArray is actually + // specified to. + /// @private + using HT = typename detail::flatten_g::type>::type; + + /** + * @brief Constructs a value-initialized `cv::GArray` + * + * `cv::GArray` objects may have their values + * be associated at graph construction time. It is useful when + * some operation has a `cv::GArray` input which doesn't change during + * the program execution, and is set only once. In this case, + * there is no need to declare such `cv::GArray` as a graph input. + * + * @note The value of `cv::GArray` may be overwritten by assigning some + * other `cv::GArray` to the object using `operator=` -- on the + * assignment, the old association or value is discarded. + * + * @param v a std::vector to associate with this + * `cv::GArray` object. Vector data is copied into the + * `cv::GArray` (no reference to the passed data is held). + */ + explicit GArray(const std::vector& v) // Constant value constructor + : m_ref(detail::GArrayU(detail::VectorRef(v))) { putDetails(); } + + /** + * @overload + * @brief Constructs a value-initialized `cv::GArray` + * + * @param v a std::vector to associate with this + * `cv::GArray` object. Vector data is moved into the `cv::GArray`. + */ + explicit GArray(std::vector&& v) // Move-constructor + : m_ref(detail::GArrayU(detail::VectorRef(std::move(v)))) { putDetails(); } + + /** + * @brief Constructs an empty `cv::GArray` + * + * Normally, empty G-API data objects denote a starting point of + * the graph. When an empty `cv::GArray` is assigned to a result + * of some operation, it obtains a functional link to this + * operation (and is not empty anymore). + */ + GArray() { putDetails(); } // Empty constructor + + /// @private + explicit GArray(detail::GArrayU &&ref) // GArrayU-based constructor + : m_ref(ref) { putDetails(); } // (used by GCall, not for users) + + /// @private + detail::GArrayU strip() const { + return m_ref; + } + /// @private + static void VCtor(detail::VectorRef& vref) { + vref.reset(); + } + +private: + void putDetails() { + m_ref.setConstructFcn(&VCtor); + m_ref.specifyType(); // FIXME: to unify those 2 to avoid excessive dynamic_cast + m_ref.storeKind(); // + } + + detail::GArrayU m_ref; +}; + +/** @} */ + +} // namespace cv + +#endif // OPENCV_GAPI_GARRAY_HPP diff --git a/modules/gapi/include/opencv2/gapi/gasync_context.hpp b/modules/gapi/include/opencv2/gapi/gasync_context.hpp new file mode 100644 index 00000000000..f49b59822d9 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gasync_context.hpp @@ -0,0 +1,63 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_GASYNC_CONTEXT_HPP +#define OPENCV_GAPI_GASYNC_CONTEXT_HPP + +#if !defined(GAPI_STANDALONE) +# include +#else // Without OpenCV +# include +#endif // !defined(GAPI_STANDALONE) + +#include + +namespace cv { +namespace gapi{ + +/** + * @brief This namespace contains experimental G-API functionality, + * functions or structures in this namespace are subjects to change or + * removal in the future releases. This namespace also contains + * functions which API is not stabilized yet. + */ +namespace wip { + +/** + * @brief A class to group async requests to cancel them in a single shot. + * + * GAsyncContext is passed as an argument to async() and async_apply() functions + */ + +class GAPI_EXPORTS GAsyncContext{ + std::atomic cancelation_requested = {false}; +public: + /** + * @brief Start cancellation process for an associated request. + * + * User still has to wait for each individual request (either via callback or according std::future object) to make sure it actually canceled. + * + * @return true if it was a first request to cancel the context + */ + bool cancel(); + + /** + * @brief Returns true if cancellation was requested for this context. + * + * @return true if cancellation was requested for this context + */ + bool isCanceled() const; +}; + +class GAPI_EXPORTS GAsyncCanceled : public std::exception { +public: + virtual const char* what() const noexcept CV_OVERRIDE; +}; +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif //OPENCV_GAPI_GASYNC_CONTEXT_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcall.hpp b/modules/gapi/include/opencv2/gapi/gcall.hpp new file mode 100644 index 00000000000..8d1b8d60100 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcall.hpp @@ -0,0 +1,78 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GCALL_HPP +#define OPENCV_GAPI_GCALL_HPP + +#include // GArg +#include // GMat +#include // GScalar +#include // GFrame +#include // GArray +#include // GOpaque + +namespace cv { + +struct GKernel; + +// The whole idea of this class is to represent an operation +// which is applied to arguments. This is part of public API, +// since it is what users should use to define kernel interfaces. + +class GAPI_EXPORTS GCall final +{ +public: + class Priv; + + explicit GCall(const GKernel &k); + ~GCall(); + + template + GCall& pass(Ts&&... args) + { + setArgs({cv::GArg(std::move(args))...}); + return *this; + } + + // A generic yield method - obtain a link to operator's particular GMat output + GMat yield (int output = 0); + GMatP yieldP (int output = 0); + GScalar yieldScalar(int output = 0); + GFrame yieldFrame (int output = 0); + + template GArray yieldArray(int output = 0) + { + return GArray(yieldArray(output)); + } + + template GOpaque yieldOpaque(int output = 0) + { + return GOpaque(yieldOpaque(output)); + } + + // Internal use only + Priv& priv(); + const Priv& priv() const; + + // GKernel and params can be modified, it's needed for infer, + // because information about output shapes doesn't exist in compile time + GKernel& kernel(); + cv::util::any& params(); + + void setArgs(std::vector &&args); + +protected: + std::shared_ptr m_priv; + + // Public versions return a typed array or opaque, those are implementation details + detail::GArrayU yieldArray(int output = 0); + detail::GOpaqueU yieldOpaque(int output = 0); +}; + +} // namespace cv + +#endif // OPENCV_GAPI_GCALL_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp new file mode 100644 index 00000000000..c61110e4d5b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -0,0 +1,309 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GCOMMON_HPP +#define OPENCV_GAPI_GCOMMON_HPP + +#include // std::hash +#include // std::vector +#include // decay + +#include + +#include +#include +#include +#include +#include +#include + +namespace cv { + +class GMat; // FIXME: forward declaration for GOpaqueTraits + +namespace detail +{ + // This is a trait-like structure to mark backend-specific compile arguments + // with tags + template struct CompileArgTag; + + // These structures are tags which separate kernels and transformations + struct KernelTag + {}; + struct TransformTag + {}; + + // This enum is utilized mostly by GArray and GOpaque to store and recognize their internal data + // types (aka Host type). Also it is widely used during serialization routine. + enum class OpaqueKind: int + { + CV_UNKNOWN, // Unknown, generic, opaque-to-GAPI data type unsupported in graph seriallization + CV_BOOL, // bool user G-API data + CV_INT, // int user G-API data + CV_INT64, // int64_t user G-API data + CV_DOUBLE, // double user G-API data + CV_FLOAT, // float user G-API data + CV_UINT64, // uint64_t user G-API data + CV_STRING, // std::string user G-API data + CV_POINT, // cv::Point user G-API data + CV_POINT2F, // cv::Point2f user G-API data + CV_POINT3F, // cv::Point3f user G-API data + CV_SIZE, // cv::Size user G-API data + CV_RECT, // cv::Rect user G-API data + CV_SCALAR, // cv::Scalar user G-API data + CV_MAT, // cv::Mat user G-API data + CV_DRAW_PRIM, // cv::gapi::wip::draw::Prim user G-API data + }; + + // Type traits helper which simplifies the extraction of kind from type + template struct GOpaqueTraits; + template struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_UNKNOWN; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_INT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_INT64; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_DOUBLE; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_FLOAT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_UINT64; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_BOOL; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_STRING; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_SIZE; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_SCALAR; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT2F; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT3F; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_RECT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; + template<> struct GOpaqueTraits + { static constexpr const OpaqueKind kind = OpaqueKind::CV_DRAW_PRIM; }; + using GOpaqueTraitsArrayTypes = std::tuple; + // GOpaque is not supporting cv::Mat and cv::Scalar since there are GScalar and GMat types + using GOpaqueTraitsOpaqueTypes = std::tuple; +} // namespace detail + +// This definition is here because it is reused by both public(?) and internal +// modules. Keeping it here wouldn't expose public details (e.g., API-level) +// to components which are internal and operate on a lower-level entities +// (e.g., compiler, backends). +// FIXME: merge with ArgKind? +// FIXME: replace with variant[format desc]? +enum class GShape: int +{ + GMAT, + GSCALAR, + GARRAY, + GOPAQUE, + GFRAME, +}; + +namespace gapi { +namespace s11n { +namespace detail { +template struct wrap_serialize; +} // namespace detail +} // namespace s11n +} // namespace gapi + + +struct GCompileArg; + +namespace detail { + template + using is_compile_arg = std::is_same::type>; +} // namespace detail + +// CompileArg is an unified interface over backend-specific compilation +// information +// FIXME: Move to a separate file? +/** \addtogroup gapi_compile_args + * @{ + * + * @brief Compilation arguments: data structures controlling the + * compilation process + * + * G-API comes with a number of graph compilation options which can be + * passed to cv::GComputation::apply() or + * cv::GComputation::compile(). Known compilation options are listed + * in this page, while extra backends may introduce their own + * compilation options (G-API transparently accepts _everything_ which + * can be passed to cv::compile_args(), it depends on underlying + * backends if an option would be interpreted or not). + * + * For example, if an example computation is executed like this: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_decl_apply + * + * Extra parameter specifying which kernels to compile with can be + * passed like this: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp apply_with_param + */ + +/** + * @brief Represents an arbitrary compilation argument. + * + * Any value can be wrapped into cv::GCompileArg, but only known ones + * (to G-API or its backends) can be interpreted correctly. + * + * Normally objects of this class shouldn't be created manually, use + * cv::compile_args() function which automatically wraps everything + * passed in (a variadic template parameter pack) into a vector of + * cv::GCompileArg objects. + */ +struct GCompileArg +{ +public: + // NB: Required for pythnon bindings + GCompileArg() = default; + + std::string tag; + + // FIXME: use decay in GArg/other trait-based wrapper before leg is shot! + template::value, int>::type = 0> + explicit GCompileArg(T &&t) + : tag(detail::CompileArgTag::type>::tag()) + , serializeF(cv::gapi::s11n::detail::has_S11N_spec::value ? + &cv::gapi::s11n::detail::wrap_serialize::serialize : + nullptr) + , arg(t) + { + } + + template T& get() + { + return util::any_cast(arg); + } + + template const T& get() const + { + return util::any_cast(arg); + } + + void serialize(cv::gapi::s11n::IOStream& os) const + { + if (serializeF) + { + serializeF(os, *this); + } + } + +private: + std::function serializeF; + util::any arg; +}; + +using GCompileArgs = std::vector; + +inline cv::GCompileArgs& operator += ( cv::GCompileArgs &lhs, + const cv::GCompileArgs &rhs) +{ + lhs.reserve(lhs.size() + rhs.size()); + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + +/** + * @brief Wraps a list of arguments (a parameter pack) into a vector of + * compilation arguments (cv::GCompileArg). + */ +template GCompileArgs compile_args(Ts&&... args) +{ + return GCompileArgs{ GCompileArg(args)... }; +} + +namespace gapi +{ +/** + * @brief Retrieves particular compilation argument by its type from + * cv::GCompileArgs + */ +template +inline cv::util::optional getCompileArg(const cv::GCompileArgs &args) +{ + for (auto &compile_arg : args) + { + if (compile_arg.tag == cv::detail::CompileArgTag::tag()) + { + return cv::util::optional(compile_arg.get()); + } + } + return cv::util::optional(); +} + +namespace s11n { +namespace detail { +template struct wrap_serialize +{ + static void serialize(IOStream& os, const GCompileArg& arg) + { + using DT = typename std::decay::type; + S11N
::serialize(os, arg.get
()); + } +}; +} // namespace detail +} // namespace s11n +} // namespace gapi + +/** @} gapi_compile_args */ + +/** + * @brief Ask G-API to dump compiled graph in Graphviz format under + * the given file name. + * + * Specifies a graph dump path (path to .dot file to be generated). + * G-API will dump a .dot file under specified path during a + * compilation process if this flag is passed. + */ +struct graph_dump_path +{ + std::string m_dump_path; +}; + +/** + * @brief Ask G-API to use threaded executor when cv::GComputation + * is compiled via cv::GComputation::compile method. + * + * Specifies a number of threads that should be used by executor. + */ +struct GAPI_EXPORTS use_threaded_executor +{ + use_threaded_executor(); + explicit use_threaded_executor(const uint32_t nthreads); + + uint32_t num_threads; +}; + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.graph_dump_path"; } + }; + + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.threaded_executor"; } + }; +} + +} // namespace cv + +// std::hash overload for GShape +namespace std +{ +template<> struct hash +{ + size_t operator() (cv::GShape sh) const + { + return std::hash()(static_cast(sh)); + } +}; +} // namespace std + + +#endif // OPENCV_GAPI_GCOMMON_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcompiled.hpp b/modules/gapi/include/opencv2/gapi/gcompiled.hpp new file mode 100644 index 00000000000..ac36783d621 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcompiled.hpp @@ -0,0 +1,232 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GCOMPILED_HPP +#define OPENCV_GAPI_GCOMPILED_HPP + +#include + +#include +#include +#include + +namespace cv { + +// This class represents a compiled computation. +// In theory (and ideally), it can be used w/o the rest of APIs. +// In theory (and ideally), it can be serialized/deserialized. +// It can enable scenarious like deployment to an autonomous devince, FuSa, etc. +// +// Currently GCompiled assumes all GMats you used to pass data to G-API +// are valid and not destroyed while you use a GCompiled object. +// +// FIXME: In future, there should be a way to name I/O objects and specify it +// to GCompiled externally (for example, when it is loaded on the target system). + +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @brief Represents a compiled computation (graph). Can only be used + * with image / data formats & resolutions it was compiled for, with + * some exceptions. + * + * This class represents a product of graph compilation (calling + * cv::GComputation::compile()). Objects of this class actually do + * data processing, and graph execution is incapsulated into objects + * of this class. Execution model itself depends on kernels and + * backends which were using during the compilation, see @ref + * gapi_compile_args for details. + * + * In a general case, GCompiled objects can be applied to data only in + * that formats/resolutions they were compiled for (see @ref + * gapi_meta_args). However, if the underlying backends allow, a + * compiled object can be _reshaped_ to handle data (images) of + * different resolution, though formats and types must remain the same. + * + * GCompiled is very similar to `std::function<>` in its semantics -- + * running it looks like a function call in the user code. + * + * At the moment, GCompiled objects are not reentrant -- generally, + * the objects are stateful since graph execution itself is a stateful + * process and this state is now maintained in GCompiled's own memory + * (not on the process stack). + * + * At the same time, two different GCompiled objects produced from the + * single cv::GComputation are completely independent and can be used + * concurrently. + * + * @sa GStreamingCompiled + */ +class GAPI_EXPORTS GCompiled +{ +public: + /// @private + class GAPI_EXPORTS Priv; + + /** + * @brief Constructs an empty object + */ + GCompiled(); + + /** + * @brief Run the compiled computation, a generic version. + * + * @param ins vector of inputs to process. + * @param outs vector of outputs to produce. + * + * Input/output vectors must have the same number of elements as + * defined in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::Mat needs to be passed where cv::GMat has been + * declared as input, and so on). Run-time exception is generated + * otherwise. + * + * Objects in output vector may remain empty (like cv::Mat) -- + * G-API will automatically initialize output objects to proper formats. + * + * @note Don't construct GRunArgs/GRunArgsP objects manually, use + * cv::gin()/cv::gout() wrappers instead. + */ + void operator() (GRunArgs &&ins, GRunArgsP &&outs); // Generic arg-to-arg +#if !defined(GAPI_STANDALONE) + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Mat for unary computation + * process. + */ + void operator() (cv::Mat in, cv::Mat &out); // Unary overload + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Scalar for unary computation + * process. + */ + void operator() (cv::Mat in, cv::Scalar &out); // Unary overload (scalar) + + /** + * @brief Execute a binary computation + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Mat for binary computation + * process. + */ + void operator() (cv::Mat in1, cv::Mat in2, cv::Mat &out); // Binary overload + + /** + * @brief Execute an binary computation + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Scalar for binary computation + * process. + */ + void operator() (cv::Mat in1, cv::Mat in2, cv::Scalar &out); // Binary overload (scalar) + + /** + * @brief Execute a computation with arbitrary number of + * inputs/outputs. + * + * @overload + * @param ins vector of input cv::Mat objects to process by the + * computation. + * @param outs vector of output cv::Mat objects to produce by the + * computation. + * + * Numbers of elements in ins/outs vectors must match numbers of + * inputs/outputs which were used to define the source GComputation. + */ + void operator() (const std::vector &ins, // Compatibility overload + const std::vector &outs); +#endif // !defined(GAPI_STANDALONE) + /// @private + Priv& priv(); + + /** + * @brief Check if compiled object is valid (non-empty) + * + * @return true if the object is runnable (valid), false otherwise + */ + explicit operator bool () const; + + /** + * @brief Vector of metadata this graph was compiled for. + * + * @return Unless _reshape_ is not supported, return value is the + * same vector which was passed to cv::GComputation::compile() to + * produce this compiled object. Otherwise, it is the latest + * metadata vector passed to reshape() (if that call was + * successful). + */ + const GMetaArgs& metas() const; // Meta passed to compile() + + /** + * @brief Vector of metadata descriptions of graph outputs + * + * @return vector with formats/resolutions of graph's output + * objects, auto-inferred from input metadata vector by + * operations which form this computation. + * + * @note GCompiled objects produced from the same + * cv::GComputiation graph with different input metas may return + * different values in this vector. + */ + const GMetaArgs& outMetas() const; + + /** + * @brief Check if the underlying backends support reshape or not. + * + * @return true if supported, false otherwise. + */ + bool canReshape() const; + + /** + * @brief Reshape a compiled graph to support new image + * resolutions. + * + * Throws an exception if an error occurs. + * + * @param inMetas new metadata to reshape on. Vector size and + * metadata shapes must match the computation's protocol. + * @param args compilation arguments to use. + */ + // FIXME: Why it requires compile args? + void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); + + /** + * @brief Prepare inner kernels states for a new video-stream. + * + * GCompiled objects may be used to process video streams frame by frame. + * In this case, a GCompiled is called on every image frame individually. + * Starting OpenCV 4.4, some kernels in the graph may have their internal + * states (see GAPI_OCV_KERNEL_ST for the OpenCV backend). + * In this case, if user starts processing another video stream with + * this GCompiled, this method needs to be called to let kernels re-initialize + * their internal states to a new video stream. + */ + void prepareForNewStream(); + +protected: + /// @private + std::shared_ptr m_priv; +}; +/** @} */ + +} + +#endif // OPENCV_GAPI_GCOMPILED_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp b/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp new file mode 100644 index 00000000000..a0c2917d6a8 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp @@ -0,0 +1,73 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + + +#ifndef OPENCV_GAPI_GCOMPILED_ASYNC_HPP +#define OPENCV_GAPI_GCOMPILED_ASYNC_HPP + +#include //for std::future +#include //for std::exception_ptr +#include //for std::function +#include +#include + +namespace cv { + //fwd declaration + class GCompiled; + +namespace gapi{ +namespace wip { + class GAsyncContext; + /** + These functions asynchronously (i.e. probably on a separate thread of execution) call GCompiled::operator() member function of their first argument with copies of rest of arguments (except callback) passed in. + The difference between the function is the way to get the completion notification (via callback or a waiting on std::future object) + If exception is occurred during execution of apply it is transferred to the callback (via function parameter) or passed to future (and will be thrown on call to std::future::get) + + N.B. : + Input arguments are copied on call to async function (actually on call to cv::gin) and thus do not have to outlive the actual completion of asynchronous activity. + While output arguments are "captured" by reference(pointer) and therefore _must_ outlive the asynchronous activity + (i.e. live at least until callback is called or future is unblocked) + + @param gcmpld Compiled computation (graph) to start asynchronously + @param callback Callback to be called when execution of gcmpld is done + @param ins Input parameters for gcmpld + @param outs Output parameters for gcmpld + */ + GAPI_EXPORTS void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs); + + /** @overload + @param gcmpld Compiled computation (graph) to run asynchronously + @param callback Callback to be called when execution of gcmpld is done + @param ins Input parameters for gcmpld + @param outs Output parameters for gcmpld + @param ctx Context this request belongs to + @see async GAsyncContext + */ + GAPI_EXPORTS void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx); + + /** @overload + @param gcmpld Compiled computation (graph) to run asynchronously + @param ins Input parameters for gcmpld + @param outs Output parameters for gcmpld + @return std::future object to wait for completion of async operation + @see async + */ + GAPI_EXPORTS std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs); + + /** + @param gcmpld Compiled computation (graph) to run asynchronously + @param ins Input parameters for gcmpld + @param outs Output parameters for gcmpld + @param ctx Context this request belongs to + @return std::future object to wait for completion of async operation + @see async GAsyncContext + */ + GAPI_EXPORTS std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx); +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_GCOMPILED_ASYNC_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp b/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp new file mode 100644 index 00000000000..df0ce340457 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp @@ -0,0 +1,139 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2019 Intel Corporation + + +#ifndef OPENCV_GAPI_GCOMPOUNDKERNEL_HPP +#define OPENCV_GAPI_GCOMPOUNDKERNEL_HPP + +#include +#include +#include +#include + +namespace cv { +namespace gapi +{ +namespace compound +{ + // FIXME User does not need to know about this function + // Needs that user may define compound kernels(as cpu kernels) + GAPI_EXPORTS cv::gapi::GBackend backend(); +} // namespace compound +} // namespace gapi + +namespace detail +{ + +struct GCompoundContext +{ + explicit GCompoundContext(const GArgs& in_args); + template + const T& inArg(int input) { return m_args.at(input).get(); } + + GArgs m_args; + GArgs m_results; +}; + +class GAPI_EXPORTS GCompoundKernel +{ +// Compound kernel must use all of it's inputs +public: + using F = std::function; + + explicit GCompoundKernel(const F& f); + void apply(GCompoundContext& ctx); + +protected: + F m_f; +}; + +template struct get_compound_in +{ + static T get(GCompoundContext &ctx, int idx) { return ctx.inArg(idx); } +}; + +template struct get_compound_in> +{ + static cv::GArray get(GCompoundContext &ctx, int idx) + { + auto array = cv::GArray(); + ctx.m_args[idx] = GArg(array); + return array; + } +}; + +template struct get_compound_in> +{ + static cv::GOpaque get(GCompoundContext &ctx, int idx) + { + auto opaq = cv::GOpaque(); + ctx.m_args[idx] = GArg(opaq); + return opaq; + } +}; + +template<> struct get_compound_in +{ + static cv::GMatP get(GCompoundContext &ctx, int idx) + { + auto mat = cv::GMatP(); + ctx.m_args[idx] = GArg(mat); + return mat; + } +}; + +template +struct GCompoundCallHelper; + +template +struct GCompoundCallHelper, std::tuple > +{ + template + static void expand_impl(GCompoundContext &ctx, detail::Seq, detail::Seq) + { + auto result = Impl::expand(get_compound_in::get(ctx, IIs)...); + auto tuple_return = tuple_wrap_helper::get(std::move(result)); + ctx.m_results = { cv::GArg(std::get(tuple_return))... }; + } + + static void expand(GCompoundContext &ctx) + { + expand_impl(ctx, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } +}; + +template +class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper, + public cv::detail::KernelTag +{ + using P = cv::detail::GCompoundCallHelper; + +public: + using API = K; + + static cv::gapi::GBackend backend() { return cv::gapi::compound::backend(); } + static GCompoundKernel kernel() { return GCompoundKernel(&P::expand); } +}; + +} // namespace detail + + +/** + * Declares a new compound kernel. See this + * [documentation chapter](@ref gapi_kernel_compound) + * on compound kernels for more details. + * + * @param Name type name for new kernel + * @param API the interface this kernel implements + */ +#define GAPI_COMPOUND_KERNEL(Name, API) \ + struct Name: public cv::detail::GCompoundKernelImpl + +} // namespace cv + +#endif // OPENCV_GAPI_GCOMPOUNDKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcomputation.hpp b/modules/gapi/include/opencv2/gapi/gcomputation.hpp new file mode 100644 index 00000000000..196eb37c6b4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcomputation.hpp @@ -0,0 +1,581 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GCOMPUTATION_HPP +#define OPENCV_GAPI_GCOMPUTATION_HPP + +#include + +#include +#include +#include +#include +#include +#include + +namespace cv { + +namespace detail +{ + // FIXME: move to algorithm, cover with separate tests + // FIXME: replace with O(1) version (both memory and compilation time) + template + struct last_type; + + template + struct last_type { using type = T;}; + + template + struct last_type { using type = typename last_type::type; }; + + template + using last_type_t = typename last_type::type; +} + +// Forward-declare the serialization objects +namespace gapi { +namespace s11n { + struct IIStream; + struct IOStream; +} // namespace s11n +} // namespace gapi + +/** + * \addtogroup gapi_main_classes + * @{ + * + * @brief G-API classes for constructed and compiled graphs. + */ + +/** + * @brief GComputation class represents a captured computation + * graph. GComputation objects form boundaries for expression code + * user writes with G-API, allowing to compile and execute it. + * + * G-API computations are defined with input/output data + * objects. G-API will track automatically which operations connect + * specified outputs to the inputs, forming up a call graph to be + * executed. The below example expresses calculation of Sobel operator + * for edge detection (\f$G = \sqrt{G_x^2 + G_y^2}\f$): + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_def + * + * Full pipeline can be now captured with this object declaration: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_cap_full + * + * Input/output data objects on which a call graph should be + * reconstructed are passed using special wrappers cv::GIn and + * cv::GOut. G-API will track automatically which operations form a + * path from inputs to outputs and build the execution graph appropriately. + * + * Note that cv::GComputation doesn't take ownership on data objects + * it is defined. Moreover, multiple GComputation objects may be + * defined on the same expressions, e.g. a smaller pipeline which + * expects that image gradients are already pre-calculated may be + * defined like this: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_cap_sub + * + * The resulting graph would expect two inputs and produce one + * output. In this case, it doesn't matter if gx/gy data objects are + * results of cv::gapi::Sobel operators -- G-API will stop unrolling + * expressions and building the underlying graph one reaching this + * data objects. + * + * The way how GComputation is defined is important as its definition + * specifies graph _protocol_ -- the way how the graph should be + * used. Protocol is defined by number of inputs, number of outputs, + * and shapes of inputs and outputs. + * + * In the above example, sobelEdge expects one Mat on input and + * produces one Mat; while sobelEdgeSub expects two Mats on input and + * produces one Mat. GComputation's protocol defines how other + * computation methods should be used -- cv::GComputation::compile() and + * cv::GComputation::apply(). For example, if a graph is defined on + * two GMat inputs, two cv::Mat objects have to be passed to apply() + * for execution. GComputation checks protocol correctness in runtime + * so passing a different number of objects in apply() or passing + * cv::Scalar instead of cv::Mat there would compile well as a C++ + * source but raise an exception in run-time. G-API also comes with a + * typed wrapper cv::GComputationT<> which introduces this type-checking in + * compile-time. + * + * cv::GComputation itself is a thin object which just captures what + * the graph is. The compiled graph (which actually process data) is + * represented by class GCompiled. Use compile() method to generate a + * compiled graph with given compile options. cv::GComputation can + * also be used to process data with implicit graph compilation + * on-the-fly, see apply() for details. + * + * GComputation is a reference-counted object -- once defined, all its + * copies will refer to the same instance. + * + * @sa GCompiled + */ +class GAPI_EXPORTS_W GComputation +{ +public: + class Priv; + typedef std::function Generator; + + // Various constructors enable different ways to define a computation: ///// + // 1. Generic constructors + /** + * @brief Define a computation using a generator function. + * + * Graph can be defined in-place directly at the moment of its + * construction with a lambda: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_gen + * + * This may be useful since all temporary objects (cv::GMats) and + * namespaces can be localized to scope of lambda, without + * contaminating the parent scope with probably unnecessary objects + * and information. + * + * @param gen generator function which returns a cv::GComputation, + * see Generator. + */ + GComputation(const Generator& gen); // Generator + // overload + + /** + * @brief Generic GComputation constructor. + * + * Constructs a new graph with a given protocol, specified as a + * flow of operations connecting input/output objects. Throws if + * the passed boundaries are invalid, e.g. if there's no + * functional dependency (path) between given outputs and inputs. + * + * @param ins Input data vector. + * @param outs Output data vector. + * + * @note Don't construct GProtoInputArgs/GProtoOutputArgs objects + * directly, use cv::GIn()/cv::GOut() wrapper functions instead. + * + * @sa @ref gapi_data_objects + */ + GAPI_WRAP GComputation(GProtoInputArgs &&ins, + GProtoOutputArgs &&outs); // Arg-to-arg overload + + // 2. Syntax sugar and compatibility overloads + /** + * @brief Defines an unary (one input -- one output) computation + * + * @overload + * @param in input GMat of the defined unary computation + * @param out output GMat of the defined unary computation + */ + GAPI_WRAP GComputation(GMat in, GMat out); // Unary overload + + /** + * @brief Defines an unary (one input -- one output) computation + * + * @overload + * @param in input GMat of the defined unary computation + * @param out output GScalar of the defined unary computation + */ + GAPI_WRAP GComputation(GMat in, GScalar out); // Unary overload (scalar) + + /** + * @brief Defines a binary (two inputs -- one output) computation + * + * @overload + * @param in1 first input GMat of the defined binary computation + * @param in2 second input GMat of the defined binary computation + * @param out output GMat of the defined binary computation + */ + GAPI_WRAP GComputation(GMat in1, GMat in2, GMat out); // Binary overload + + /** + * @brief Defines a binary (two inputs -- one output) computation + * + * @overload + * @param in1 first input GMat of the defined binary computation + * @param in2 second input GMat of the defined binary computation + * @param out output GScalar of the defined binary computation + */ + GComputation(GMat in1, GMat in2, GScalar out); // Binary + // overload + // (scalar) + + /** + * @brief Defines a computation with arbitrary input/output number. + * + * @overload + * @param ins vector of inputs GMats for this computation + * @param outs vector of outputs GMats for this computation + * + * Use this overload for cases when number of computation + * inputs/outputs is not known in compile-time -- e.g. when graph + * is programmatically generated to build an image pyramid with + * the given number of levels, etc. + */ + GComputation(const std::vector &ins, // Compatibility overload + const std::vector &outs); + + // Various versions of apply(): //////////////////////////////////////////// + // 1. Generic apply() + /** + * @brief Compile graph on-the-fly and immediately execute it on + * the inputs data vectors. + * + * Number of input/output data objects must match GComputation's + * protocol, also types of host data objects (cv::Mat, cv::Scalar) + * must match the shapes of data objects from protocol (cv::GMat, + * cv::GScalar). If there's a mismatch, a run-time exception will + * be generated. + * + * Internally, a cv::GCompiled object is created for the given + * input format configuration, which then is executed on the input + * data immediately. cv::GComputation caches compiled objects + * produced within apply() -- if this method would be called next + * time with the same input parameters (image formats, image + * resolution, etc), the underlying compiled graph will be reused + * without recompilation. If new metadata doesn't match the cached + * one, the underlying compiled graph is regenerated. + * + * @note compile() always triggers a compilation process and + * produces a new GCompiled object regardless if a similar one has + * been cached via apply() or not. + * + * @param ins vector of input data to process. Don't create + * GRunArgs object manually, use cv::gin() wrapper instead. + * @param outs vector of output data to fill results in. cv::Mat + * objects may be empty in this vector, G-API will automatically + * initialize it with the required format & dimensions. Don't + * create GRunArgsP object manually, use cv::gout() wrapper instead. + * @param args a list of compilation arguments to pass to the + * underlying compilation process. Don't create GCompileArgs + * object manually, use cv::compile_args() wrapper instead. + * + * @sa @ref gapi_data_objects, @ref gapi_compile_args + */ + void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); // Arg-to-arg overload + + /// @private -- Exclude this function from OpenCV documentation + GAPI_WRAP GRunArgs apply(const cv::detail::ExtractArgsCallback &callback, + GCompileArgs &&args = {}); + + /// @private -- Exclude this function from OpenCV documentation + void apply(const std::vector& ins, // Compatibility overload + const std::vector& outs, + GCompileArgs &&args = {}); + + // 2. Syntax sugar and compatibility overloads +#if !defined(GAPI_STANDALONE) + /** + * @brief Execute an unary computation (with compilation on the fly) + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Mat for unary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args = {}); // Unary overload + + /** + * @brief Execute an unary computation (with compilation on the fly) + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Scalar for unary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {}); // Unary overload (scalar) + + /** + * @brief Execute a binary computation (with compilation on the fly) + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Mat for binary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload + + /** + * @brief Execute an binary computation (with compilation on the fly) + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Scalar for binary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args = {}); // Binary overload (scalar) + + /** + * @brief Execute a computation with arbitrary number of + * inputs/outputs (with compilation on-the-fly). + * + * @overload + * @param ins vector of input cv::Mat objects to process by the + * computation. + * @param outs vector of output cv::Mat objects to produce by the + * computation. + * @param args compilation arguments for underlying compilation + * process. + * + * Numbers of elements in ins/outs vectors must match numbers of + * inputs/outputs which were used to define this GComputation. + */ + void apply(const std::vector& ins, // Compatibility overload + std::vector& outs, + GCompileArgs &&args = {}); +#endif // !defined(GAPI_STANDALONE) + // Various versions of compile(): ////////////////////////////////////////// + // 1. Generic compile() - requires metas to be passed as vector + /** + * @brief Compile the computation for specific input format(s). + * + * This method triggers compilation process and produces a new + * GCompiled object which then can process data of the given + * format. Passing data with different format to the compiled + * computation will generate a run-time exception. + * + * @param in_metas vector of input metadata configuration. Grab + * metadata from real data objects (like cv::Mat or cv::Scalar) + * using cv::descr_of(), or create it on your own. + * @param args compilation arguments for this compilation + * process. Compilation arguments directly affect what kind of + * executable object would be produced, e.g. which kernels (and + * thus, devices) would be used to execute computation. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + * + * @sa @ref gapi_compile_args + */ + GCompiled compile(GMetaArgs &&in_metas, GCompileArgs &&args = {}); + + // 2. Syntax sugar - variadic list of metas, no extra compile args + // FIXME: SFINAE looks ugly in the generated documentation + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + */ + template + auto compile(const Ts&... metas) -> + typename std::enable_if::value, GCompiled>::type + { + return compile(GMetaArgs{GMetaArg(metas)...}, GCompileArgs()); + } + + // 3. Syntax sugar - variadic list of metas, extra compile args + // (seems optional parameters don't work well when there's an variadic template + // comes first) + // + // Ideally it should look like: + // + // template + // GCompiled compile(const Ts&... metas, GCompileArgs &&args) + // + // But not all compilers can handle this (and seems they shouldn't be able to). + // FIXME: SFINAE looks ugly in the generated documentation + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced, + * followed by GCompileArgs object representing compilation + * arguments for this process. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + */ + template + auto compile(const Ts&... meta_and_compile_args) -> + typename std::enable_if::value + && std::is_same >::value, + GCompiled>::type + { + //FIXME: wrapping meta_and_compile_args into a tuple to unwrap them inside a helper function is the overkill + return compile(std::make_tuple(meta_and_compile_args...), + typename detail::MkSeq::type()); + } + + + // FIXME: Document properly in the Doxygen format + // Video-oriented pipeline compilation: + // 1. A generic version + /** + * @brief Compile the computation for streaming mode. + * + * This method triggers compilation process and produces a new + * GStreamingCompiled object which then can process video stream + * data of the given format. Passing a stream in a different + * format to the compiled computation will generate a run-time + * exception. + * + * @param in_metas vector of input metadata configuration. Grab + * metadata from real data objects (like cv::Mat or cv::Scalar) + * using cv::descr_of(), or create it on your own. + * + * @param args compilation arguments for this compilation + * process. Compilation arguments directly affect what kind of + * executable object would be produced, e.g. which kernels (and + * thus, devices) would be used to execute computation. + * + * @return GStreamingCompiled, a streaming-oriented executable + * computation compiled specifically for the given input + * parameters. + * + * @sa @ref gapi_compile_args + */ + GAPI_WRAP GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {}); + + /** + * @brief Compile the computation for streaming mode. + * + * This method triggers compilation process and produces a new + * GStreamingCompiled object which then can process video stream + * data in any format. Underlying mechanisms will be adjusted to + * every new input video stream automatically, but please note that + * _not all_ existing backends support this (see reshape()). + * + * @param args compilation arguments for this compilation + * process. Compilation arguments directly affect what kind of + * executable object would be produced, e.g. which kernels (and + * thus, devices) would be used to execute computation. + * + * @return GStreamingCompiled, a streaming-oriented executable + * computation compiled for any input image format. + * + * @sa @ref gapi_compile_args + */ + GAPI_WRAP GStreamingCompiled compileStreaming(GCompileArgs &&args = {}); + + /// @private -- Exclude this function from OpenCV documentation + GAPI_WRAP GStreamingCompiled compileStreaming(const cv::detail::ExtractMetaCallback &callback, + GCompileArgs &&args = {}); + + // 2. Direct metadata version + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced. + * + * @return GStreamingCompiled, a streaming-oriented executable + * computation compiled specifically for the given input + * parameters. + */ + template + auto compileStreaming(const Ts&... metas) -> + typename std::enable_if::value, GStreamingCompiled>::type + { + return compileStreaming(GMetaArgs{GMetaArg(metas)...}, GCompileArgs()); + } + + // 2. Direct metadata + compile arguments version + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced, + * followed by GCompileArgs object representing compilation + * arguments for this process. + * + * @return GStreamingCompiled, a streaming-oriented executable + * computation compiled specifically for the given input + * parameters. + */ + template + auto compileStreaming(const Ts&... meta_and_compile_args) -> + typename std::enable_if::value + && std::is_same >::value, + GStreamingCompiled>::type + { + //FIXME: wrapping meta_and_compile_args into a tuple to unwrap them inside a helper function is the overkill + return compileStreaming(std::make_tuple(meta_and_compile_args...), + typename detail::MkSeq::type()); + } + + // Internal use only + /// @private + Priv& priv(); + /// @private + const Priv& priv() const; + /// @private + explicit GComputation(cv::gapi::s11n::IIStream &); + /// @private + void serialize(cv::gapi::s11n::IOStream &) const; + +protected: + + // 4. Helper methods for (3) + /// @private + template + GCompiled compile(const std::tuple &meta_and_compile_args, detail::Seq) + { + GMetaArgs meta_args = {GMetaArg(std::get(meta_and_compile_args))...}; + GCompileArgs comp_args = std::get(meta_and_compile_args); + return compile(std::move(meta_args), std::move(comp_args)); + } + template + GStreamingCompiled compileStreaming(const std::tuple &meta_and_compile_args, detail::Seq) + { + GMetaArgs meta_args = {GMetaArg(std::get(meta_and_compile_args))...}; + GCompileArgs comp_args = std::get(meta_and_compile_args); + return compileStreaming(std::move(meta_args), std::move(comp_args)); + } + void recompile(GMetaArgs&& in_metas, GCompileArgs &&args); + /// @private + std::shared_ptr m_priv; +}; +/** @} */ + +namespace gapi +{ + // FIXME: all these standalone functions need to be added to some + // common documentation section + /** + * @brief Define an tagged island (subgraph) within a computation. + * + * Declare an Island tagged with `name` and defined from `ins` to `outs` + * (exclusively, as ins/outs are data objects, and regioning is done on + * operations level). + * Throws if any operation between `ins` and `outs` are already assigned + * to another island. + * + * Islands allow to partition graph into subgraphs, fine-tuning + * the way it is scheduled by the underlying executor. + * + * @param name name of the Island to create + * @param ins vector of input data objects where the subgraph + * begins + * @param outs vector of output data objects where the subgraph + * ends. + * + * The way how an island is defined is similar to how + * cv::GComputation is defined on input/output data objects. + * Same rules apply here as well -- if there's no functional + * dependency between inputs and outputs or there's not enough + * input data objects were specified to properly calculate all + * outputs, an exception is thrown. + * + * Use cv::GIn() / cv::GOut() to specify input/output vectors. + */ + void GAPI_EXPORTS island(const std::string &name, + GProtoInputArgs &&ins, + GProtoOutputArgs &&outs); +} // namespace gapi + +} // namespace cv +#endif // OPENCV_GAPI_GCOMPUTATION_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp b/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp new file mode 100644 index 00000000000..8af603efead --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp @@ -0,0 +1,69 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_GCOMPUTATION_ASYNC_HPP +#define OPENCV_GAPI_GCOMPUTATION_ASYNC_HPP + + +#include //for std::future +#include //for std::exception_ptr +#include //for std::function +#include //for GRunArgs, GRunArgsP +#include //for GCompileArgs +#include + + +namespace cv { + //fwd declaration + class GComputation; +namespace gapi { +namespace wip { + class GAsyncContext; + /** In contrast to async() functions, these do call GComputation::apply() member function of the GComputation passed in. + + @param gcomp Computation (graph) to run asynchronously + @param callback Callback to be called when execution of gcomp is done + @param ins Input parameters for gcomp + @param outs Output parameters for gcomp + @param args Compile arguments to pass to GComputation::apply() + @see async + */ + GAPI_EXPORTS void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); + /** @overload + @param gcomp Computation (graph) to run asynchronously + @param callback Callback to be called when execution of gcomp is done + @param ins Input parameters for gcomp + @param outs Output parameters for gcomp + @param args Compile arguments to pass to GComputation::apply() + @param ctx Context this request belongs to + @see async_apply async GAsyncContext + */ + GAPI_EXPORTS void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx); + /** @overload + @param gcomp Computation (graph) to run asynchronously + @param ins Input parameters for gcomp + @param outs Output parameters for gcomp + @param args Compile arguments to pass to GComputation::apply() + @return std::future object to wait for completion of async operation + @see async_apply async + */ + GAPI_EXPORTS std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); + /** @overload + @param gcomp Computation (graph) to run asynchronously + @param ins Input parameters for gcomp + @param outs Output parameters for gcomp + @param args Compile arguments to pass to GComputation::apply() + @param ctx Context this request belongs to + @return std::future object to wait for completion of async operation + @see async_apply async GAsyncContext + */ + GAPI_EXPORTS std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx); +} // namespace wip +} // namespace gapi +} // namespace cv + + +#endif //OPENCV_GAPI_GCOMPUTATION_ASYNC_HPP diff --git a/modules/gapi/include/opencv2/gapi/gframe.hpp b/modules/gapi/include/opencv2/gapi/gframe.hpp new file mode 100644 index 00000000000..54fb30789e3 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gframe.hpp @@ -0,0 +1,113 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GFRAME_HPP +#define OPENCV_GAPI_GFRAME_HPP + +#include +#include // std::shared_ptr + +#include +#include // GShape + +#include +#include + +// TODO GAPI_EXPORTS or so +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; + +/** \addtogroup gapi_data_objects + * @{ + */ +/** + * @brief GFrame class represents an image or media frame in the graph. + * + * GFrame doesn't store any data itself, instead it describes a + * functional relationship between operations consuming and producing + * GFrame objects. + * + * GFrame is introduced to handle various media formats (e.g., NV12 or + * I420) under the same type. Various image formats may differ in the + * number of planes (e.g. two for NV12, three for I420) and the pixel + * layout inside. GFrame type allows to handle these media formats in + * the graph uniformly -- the graph structure will not change if the + * media format changes, e.g. a different camera or decoder is used + * with the same graph. G-API provides a number of operations which + * operate directly on GFrame, like `infer<>()` or + * renderFrame(); these operations are expected to handle different + * media formats inside. There is also a number of accessor + * operations like BGR(), Y(), UV() -- these operations provide + * access to frame's data in the familiar cv::GMat form, which can be + * used with the majority of the existing G-API operations. These + * accessor functions may perform color space conversion on the fly if + * the image format of the GFrame they are applied to differs from the + * operation's semantic (e.g. the BGR() accessor is called on an NV12 + * image frame). + * + * GFrame is a virtual counterpart of cv::MediaFrame. + * + * @sa cv::MediaFrame, cv::GFrameDesc, BGR(), Y(), UV(), infer<>(). + */ +class GAPI_EXPORTS_W_SIMPLE GFrame +{ +public: + /** + * @brief Constructs an empty GFrame + * + * Normally, empty G-API data objects denote a starting point of + * the graph. When an empty GFrame is assigned to a result of some + * operation, it obtains a functional link to this operation (and + * is not empty anymore). + */ + GAPI_WRAP GFrame(); // Empty constructor + + /// @private + GFrame(const GNode &n, std::size_t out); // Operation result constructor + /// @private + GOrigin& priv(); // Internal use only + /// @private + const GOrigin& priv() const; // Internal use only + +private: + std::shared_ptr m_priv; +}; +/** @} */ + +enum class MediaFormat: int +{ + BGR = 0, + NV12, + GRAY, +}; + +/** + * \addtogroup gapi_meta_args + * @{ + */ +struct GAPI_EXPORTS GFrameDesc +{ + MediaFormat fmt; + cv::Size size; + + bool operator== (const GFrameDesc &) const; +}; +static inline GFrameDesc empty_gframe_desc() { return GFrameDesc{}; } +/** @} */ + +class MediaFrame; +GAPI_EXPORTS GFrameDesc descr_of(const MediaFrame &frame); + +GAPI_EXPORTS std::ostream& operator<<(std::ostream& os, const cv::GFrameDesc &desc); + +} // namespace cv + +#endif // OPENCV_GAPI_GFRAME_HPP diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp new file mode 100644 index 00000000000..6ec6bf573d0 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -0,0 +1,757 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_GKERNEL_HPP +#define OPENCV_GAPI_GKERNEL_HPP + +#include +#include +#include // string +#include // false_type, true_type +#include // map (for GKernelPackage) +#include // tuple + +#include // CompileArgTag +#include // Seq +#include +#include // GArg +#include // GMetaArg +#include // GTypeTraits +#include //suppress_unused_warning +#include + +namespace cv { + +struct GTypeInfo +{ + GShape shape; + cv::detail::OpaqueKind kind; + detail::HostCtor ctor; +}; + +using GShapes = std::vector; +using GKinds = std::vector; +using GCtors = std::vector; +using GTypesInfo = std::vector; + +// GKernel describes kernel API to the system +// FIXME: add attributes of a kernel, (e.g. number and types +// of inputs, etc) +struct GAPI_EXPORTS GKernel +{ + using M = std::function; + + std::string name; // kernel ID, defined by its API (signature) + std::string tag; // some (implementation-specific) tag + M outMeta; // generic adaptor to API::outMeta(...) + GShapes outShapes; // types (shapes) kernel's outputs + GKinds inKinds; // kinds of kernel's inputs (fixme: below) + GCtors outCtors; // captured constructors for template output types + GKinds outKinds; // kinds of kernel's outputs (fixme: below) +}; +// TODO: It's questionable if inKinds should really be here. Instead, +// this information could come from meta. + +// GKernelImpl describes particular kernel implementation to the system +struct GAPI_EXPORTS GKernelImpl +{ + util::any opaque; // backend-specific opaque info + GKernel::M outMeta; // for deserialized graphs, the outMeta is taken here +}; + +template class GKernelTypeM; + +namespace detail +{ + //////////////////////////////////////////////////////////////////////////// + // yield() is used in graph construction time as a generic method to obtain + // lazy "return value" of G-API operations + // + template struct Yield; + template<> struct Yield + { + static inline cv::GMat yield(cv::GCall &call, int i) { return call.yield(i); } + }; + template<> struct Yield + { + static inline cv::GMatP yield(cv::GCall &call, int i) { return call.yieldP(i); } + }; + template<> struct Yield + { + static inline cv::GScalar yield(cv::GCall &call, int i) { return call.yieldScalar(i); } + }; + template struct Yield > + { + static inline cv::GArray yield(cv::GCall &call, int i) { return call.yieldArray(i); } + }; + template struct Yield > + { + static inline cv::GOpaque yield(cv::GCall &call, int i) { return call.yieldOpaque(i); } + }; + template<> struct Yield + { + static inline cv::GFrame yield(cv::GCall &call, int i) { return call.yieldFrame(i); } + }; + + //////////////////////////////////////////////////////////////////////////// + // Helper classes which brings outputMeta() marshalling to kernel + // implementations + // + // 1. MetaType establishes G#Type -> G#Meta mapping between G-API dynamic + // types and its metadata descriptor types. + // This mapping is used to transform types to call outMeta() callback. + template struct MetaType; + template<> struct MetaType { using type = GMatDesc; }; + template<> struct MetaType { using type = GMatDesc; }; + template<> struct MetaType { using type = GFrameDesc; }; + template<> struct MetaType { using type = GScalarDesc; }; + template struct MetaType > { using type = GArrayDesc; }; + template struct MetaType > { using type = GOpaqueDesc; }; + template struct MetaType { using type = T; }; // opaque args passed as-is + // FIXME: Move it to type traits? + + // 2. Hacky test based on MetaType to check if we operate on G-* type or not + template using is_nongapi_type = std::is_same::type>; + + // 3. Two ways to transform input arguments to its meta - for G-* and non-G* types: + template + typename std::enable_if::value, typename MetaType::type> + ::type get_in_meta(const GMetaArgs &in_meta, const GArgs &, int idx) + { + return util::get::type>(in_meta.at(idx)); + } + + template + typename std::enable_if::value, T> + ::type get_in_meta(const GMetaArgs &, const GArgs &in_args, int idx) + { + return in_args.at(idx).template get(); + } + + // 4. The MetaHelper itself: an entity which generates outMeta() call + // based on kernel signature, with arguments properly substituted. + // 4.1 - case for multiple return values + // FIXME: probably can be simplified with std::apply or analogue. + template + struct MetaHelper; + + template + struct MetaHelper, std::tuple > + { + template + static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta, + const GArgs &in_args, + detail::Seq, + detail::Seq) + { + // FIXME: decay? + using R = std::tuple::type...>; + const R r = K::outMeta( get_in_meta(in_meta, in_args, IIs)... ); + return GMetaArgs{ GMetaArg(std::get(r))... }; + } + // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?) + + static GMetaArgs getOutMeta(const GMetaArgs &in_meta, + const GArgs &in_args) + { + return getOutMeta_impl(in_meta, + in_args, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } + }; + + // 4.1 - case for a single return value + // FIXME: How to avoid duplication here? + template + struct MetaHelper, Out > + { + template + static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta, + const GArgs &in_args, + detail::Seq) + { + // FIXME: decay? + using R = typename MetaType::type; + const R r = K::outMeta( get_in_meta(in_meta, in_args, IIs)... ); + return GMetaArgs{ GMetaArg(r) }; + } + // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?) + + static GMetaArgs getOutMeta(const GMetaArgs &in_meta, + const GArgs &in_args) + { + return getOutMeta_impl(in_meta, + in_args, + typename detail::MkSeq::type()); + } + }; + + //////////////////////////////////////////////////////////////////////////// + // Helper class to introduce tags to calls. By default there's no tag + struct NoTag { + static constexpr const char *tag() { return ""; } + }; + +} // namespace detail + +// GKernelType and GKernelTypeM are base classes which implement typed ::on() +// method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels +// +// G_TYPED_KERNEL and G_TYPED_KERNEL_M macros inherit user classes from GKernelType and +// GKernelTypeM respectively. + +template +class GKernelTypeM(Args...)> > + : public detail::MetaHelper, std::tuple> + , public detail::NoTag +{ + template + static std::tuple yield(cv::GCall &call, detail::Seq) + { + return std::make_tuple(detail::Yield::yield(call, IIs)...); + } + +public: + using InArgs = std::tuple; + using OutArgs = std::tuple; + + // TODO: Args&&... here? + static std::tuple on(Args... args) + { + cv::GCall call(GKernel{ K::id() + , K::tag() + , &K::getOutMeta + , {detail::GTypeTraits::shape...} + , {detail::GTypeTraits::op_kind...} + , {detail::GObtainCtor::get()...} + , {detail::GTypeTraits::op_kind...}}); + call.pass(args...); // TODO: std::forward() here? + return yield(call, typename detail::MkSeq::type()); + } +}; + +template class GKernelType; + +template +class GKernelType > + : public detail::MetaHelper, R> + , public detail::NoTag +{ +public: + using InArgs = std::tuple; + using OutArgs = std::tuple; + + static R on(Args... args) + { + cv::GCall call(GKernel{ K::id() + , K::tag() + , &K::getOutMeta + , {detail::GTypeTraits::shape} + , {detail::GTypeTraits::op_kind...} + , {detail::GObtainCtor::get()} + , {detail::GTypeTraits::op_kind}}); + call.pass(args...); + return detail::Yield::yield(call, 0); + } +}; + +namespace detail { +// This tiny class eliminates the semantic difference between +// GKernelType and GKernelTypeM. +template class KernelTypeMedium; + +template +class KernelTypeMedium(Args...)>> : + public cv::GKernelTypeM(Args...)>> {}; + +template +class KernelTypeMedium> : + public cv::GKernelType> {}; +} // namespace detail + +} // namespace cv + + +// FIXME: I don't know a better way so far. Feel free to suggest one +// The problem is that every typed kernel should have ::id() but body +// of the class is defined by user (with outMeta, other stuff) + +//! @cond IGNORED +#define G_ID_HELPER_CLASS(Class) Class##IdHelper + +#define G_ID_HELPER_BODY(Class, Id) \ + struct G_ID_HELPER_CLASS(Class) \ + { \ + static constexpr const char * id() {return Id;} \ + }; \ +//! @endcond + +#define GET_G_TYPED_KERNEL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, NAME, ...) NAME +#define COMBINE_SIGNATURE(...) __VA_ARGS__ +// Ensure correct __VA_ARGS__ expansion on Windows +#define __WRAP_VAARGS(x) x + +/** + * Helper for G_TYPED_KERNEL declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api) + * for more details. + * + * @param Class type name for this operation. + * @param API an `std::function<>`-like signature for the operation; + * return type is a single value or a tuple of multiple values. + * @param Id string identifier for the operation. Must be unique. + */ +#define G_TYPED_KERNEL_HELPER(Class, API, Id) \ + G_ID_HELPER_BODY(Class, Id) \ + struct Class final: public cv::detail::KernelTypeMedium, \ + public G_ID_HELPER_CLASS(Class) +// {body} is to be defined by user + +#define G_TYPED_KERNEL_HELPER_2(Class, _1, _2, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2), Id) + +#define G_TYPED_KERNEL_HELPER_3(Class, _1, _2, _3, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3), Id) + +#define G_TYPED_KERNEL_HELPER_4(Class, _1, _2, _3, _4, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4), Id) + +#define G_TYPED_KERNEL_HELPER_5(Class, _1, _2, _3, _4, _5, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5), Id) + +#define G_TYPED_KERNEL_HELPER_6(Class, _1, _2, _3, _4, _5, _6, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6), Id) + +#define G_TYPED_KERNEL_HELPER_7(Class, _1, _2, _3, _4, _5, _6, _7, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7), Id) + +#define G_TYPED_KERNEL_HELPER_8(Class, _1, _2, _3, _4, _5, _6, _7, _8, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8), Id) + +#define G_TYPED_KERNEL_HELPER_9(Class, _1, _2, _3, _4, _5, _6, _7, _8, _9, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8, _9), Id) + +#define G_TYPED_KERNEL_HELPER_10(Class, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, Id) \ +G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10), Id) + +/** + * Declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api) + * for more details. + * + * @param Class type name for this operation. + */ +#define G_TYPED_KERNEL(Class, ...) __WRAP_VAARGS(GET_G_TYPED_KERNEL(__VA_ARGS__, \ + G_TYPED_KERNEL_HELPER_10, \ + G_TYPED_KERNEL_HELPER_9, \ + G_TYPED_KERNEL_HELPER_8, \ + G_TYPED_KERNEL_HELPER_7, \ + G_TYPED_KERNEL_HELPER_6, \ + G_TYPED_KERNEL_HELPER_5, \ + G_TYPED_KERNEL_HELPER_4, \ + G_TYPED_KERNEL_HELPER_3, \ + G_TYPED_KERNEL_HELPER_2, \ + G_TYPED_KERNEL_HELPER)(Class, __VA_ARGS__)) \ + +/** + * Declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api) for more details. + * + * @deprecated This macro is deprecated in favor of `G_TYPED_KERNEL` that is used for declaring any + * G-API Operation. + * + * @param Class type name for this operation. + */ +#define G_TYPED_KERNEL_M G_TYPED_KERNEL + +#define G_API_OP G_TYPED_KERNEL +#define G_API_OP_M G_API_OP + +namespace cv +{ +namespace gapi +{ + // Prework: model "Device" API before it gets to G-API headers. + // FIXME: Don't mix with internal Backends class! + /// @private + class GAPI_EXPORTS GBackend + { + public: + class Priv; + + // TODO: make it template (call `new` within??) + GBackend(); + explicit GBackend(std::shared_ptr &&p); + + Priv& priv(); + const Priv& priv() const; + std::size_t hash() const; + + bool operator== (const GBackend &rhs) const; + + private: + std::shared_ptr m_priv; + }; + + inline bool operator != (const GBackend &lhs, const GBackend &rhs) + { + return !(lhs == rhs); + } +} // namespace gapi +} // namespace cv + +namespace std +{ + template<> struct hash + { + std::size_t operator() (const cv::gapi::GBackend &b) const + { + return b.hash(); + } + }; +} // namespace std + +namespace cv { + class GAPI_EXPORTS_W_SIMPLE GKernelPackage; + +namespace gapi { + GAPI_EXPORTS_W cv::GKernelPackage combine(const cv::GKernelPackage &lhs, + const cv::GKernelPackage &rhs); + + /// @private + class GFunctor + { + public: + virtual cv::GKernelImpl impl() const = 0; + virtual cv::gapi::GBackend backend() const = 0; + const char* id() const { return m_id; } + + virtual ~GFunctor() = default; + protected: + GFunctor(const char* id) : m_id(id) { } + private: + const char* m_id; + }; +} // namespace gapi + + /** \addtogroup gapi_compile_args + * @{ + */ + + // FIXME: Hide implementation + /** + * @brief A container class for heterogeneous kernel + * implementation collections and graph transformations. + * + * GKernelPackage is a special container class which stores kernel + * _implementations_ and graph _transformations_. Objects of this class + * are created and passed to cv::GComputation::compile() to specify + * which kernels to use and which transformations to apply in the + * compiled graph. GKernelPackage may contain kernels of + * different backends, e.g. be heterogeneous. + * + * The most easy way to create a kernel package is to use function + * cv::gapi::kernels(). This template functions takes kernel + * implementations in form of type list (variadic template) and + * generates a kernel package atop of that. + * + * Kernel packages can be also generated programmatically, starting + * with an empty package (created with the default constructor) + * and then by populating it with kernels via call to + * GKernelPackage::include(). Note this method is also a template + * one since G-API kernel and transformation implementations are _types_, + * not objects. + * + * Finally, two kernel packages can be combined into a new one + * with function cv::gapi::combine(). + */ + class GAPI_EXPORTS_W_SIMPLE GKernelPackage + { + + /// @private + using M = std::unordered_map>; + + /// @private + M m_id_kernels; + + /// @private + std::vector m_transformations; + + protected: + /// @private + // Remove ALL implementations of the given API (identified by ID) + void removeAPI(const std::string &id); + + /// @private + // Partial include() specialization for kernels + template + typename std::enable_if<(std::is_base_of::value), void>::type + includeHelper() + { + auto backend = KImpl::backend(); + auto kernel_id = KImpl::API::id(); + auto kernel_impl = GKernelImpl{KImpl::kernel(), &KImpl::API::getOutMeta}; + removeAPI(kernel_id); + + m_id_kernels[kernel_id] = std::make_pair(backend, kernel_impl); + } + + /// @private + // Partial include() specialization for transformations + template + typename std::enable_if<(std::is_base_of::value), void>::type + includeHelper() + { + m_transformations.emplace_back(TImpl::transformation()); + } + + public: + void include(const cv::gapi::GFunctor& functor); + + /** + * @brief Returns total number of kernels + * in the package (across all backends included) + * + * @return a number of kernels in the package + */ + GAPI_WRAP std::size_t size() const; + + /** + * @brief Returns vector of transformations included in the package + * + * @return vector of transformations included in the package + */ + const std::vector& get_transformations() const; + + /** + * @brief Returns vector of kernel ids included in the package + * + * @return vector of kernel ids included in the package + */ + std::vector get_kernel_ids() const; + + /** + * @brief Test if a particular kernel _implementation_ KImpl is + * included in this kernel package. + * + * @sa includesAPI() + * + * @note cannot be applied to transformations + * + * @return true if there is such kernel, false otherwise. + */ + template + bool includes() const + { + static_assert(std::is_base_of::value, + "includes() can be applied to kernels only"); + + auto kernel_it = m_id_kernels.find(KImpl::API::id()); + return kernel_it != m_id_kernels.end() && + kernel_it->second.first == KImpl::backend(); + } + + /** + * @brief Remove all kernels associated with the given backend + * from the package. + * + * Does nothing if there's no kernels of this backend in the package. + * + * @param backend backend which kernels to remove + */ + void remove(const cv::gapi::GBackend& backend); + + /** + * @brief Remove all kernels implementing the given API from + * the package. + * + * Does nothing if there's no kernels implementing the given interface. + */ + template + void remove() + { + removeAPI(KAPI::id()); + } + + // FIXME: Rename to includes() and distinguish API/impl case by + // statically? + /** + * Check if package contains ANY implementation of a kernel API + * by API type. + */ + template + bool includesAPI() const + { + return includesAPI(KAPI::id()); + } + + /// @private + bool includesAPI(const std::string &id) const; + + // FIXME: The below comment is wrong, and who needs this function? + /** + * @brief Find a kernel (by its API) + * + * Returns implementation corresponding id. + * Throws if nothing found. + * + * @return Backend which hosts matching kernel implementation. + * + */ + template + cv::gapi::GBackend lookup() const + { + return lookup(KAPI::id()).first; + } + + /// @private + std::pair + lookup(const std::string &id) const; + + // FIXME: No overwrites allowed? + /** + * @brief Put a new kernel implementation or a new transformation + * KImpl into the package. + */ + template + void include() + { + includeHelper(); + } + + /** + * @brief Adds a new kernel based on it's backend and id into the kernel package + * + * @param backend backend associated with the kernel + * @param kernel_id a name/id of the kernel + */ + void include(const cv::gapi::GBackend& backend, const std::string& kernel_id); + + /** + * @brief Lists all backends which are included into package + * + * @return vector of backends + */ + std::vector backends() const; + + // TODO: Doxygen bug -- it wants me to place this comment + // here, not below. + /** + * @brief Create a new package based on `lhs` and `rhs`. + * + * @param lhs "Left-hand-side" package in the process + * @param rhs "Right-hand-side" package in the process + * @return a new kernel package. + */ + friend GAPI_EXPORTS GKernelPackage cv::gapi::combine(const GKernelPackage &lhs, + const GKernelPackage &rhs); + }; + /** @} */ + +namespace gapi { + using GKernelPackage = cv::GKernelPackage; // Keep backward compatibility + + /** \addtogroup gapi_compile_args + * @{ + */ + + /** + * @brief Create a kernel package object containing kernels + * and transformations specified in variadic template argument. + * + * In G-API, kernel implementations and transformations are _types_. + * Every backend has its own kernel API (like GAPI_OCV_KERNEL() and + * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for + * each kernel implementation. + * + * Use this function to pass kernel implementations (defined in + * either way) and transformations to the system. Example: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp kernels_snippet + * + * Note that kernels() itself is a function returning object, not + * a type, so having `()` at the end is important -- it must be a + * function call. + */ + template GKernelPackage kernels() + { + // FIXME: currently there is no check that transformations' signatures are unique + // and won't be any intersection in graph compilation stage + static_assert(cv::detail::all_unique::value, "Kernels API must be unique"); + + GKernelPackage pkg; + + // For those who wonder - below is a trick to call a number of + // methods based on parameter pack (zeroes just help hiding these + // calls into a sequence which helps to expand this parameter pack). + // Just note that `f(),a` always equals to `a` (with f() called!) + // and parentheses are used to hide function call in the expanded sequence. + // Leading 0 helps to handle case when KK is an empty list (kernels<>()). + int unused[] = { 0, (pkg.include(), 0)... }; + cv::util::suppress_unused_warning(unused); + return pkg; + } + + template + GKernelPackage kernels(FF&... functors) + { + GKernelPackage pkg; + int unused[] = { 0, (pkg.include(functors), 0)... }; + cv::util::suppress_unused_warning(unused); + return pkg; + } + + /** @} */ + + /** + * @brief Combines multiple G-API kernel packages into one + * + * @overload + * + * This function successively combines the passed kernel packages using a right fold. + * Calling `combine(a, b, c)` is equal to `combine(a, combine(b, c))`. + * + * @return The resulting kernel package + */ + template + cv::GKernelPackage combine(const cv::GKernelPackage &a, const cv::GKernelPackage &b, Ps&&... rest) + { + return combine(a, combine(b, rest...)); + } + // NB(DM): Variadic-arg version in Python may require the same + // approach as used in GComputation::compile/apply. + + /** \addtogroup gapi_compile_args + * @{ + */ + /** + * @brief cv::gapi::use_only() is a special combinator which hints G-API to use only + * kernels specified in cv::GComputation::compile() (and not to extend kernels available by + * default with that package). + */ + struct GAPI_EXPORTS use_only + { + GKernelPackage pkg; + }; + /** @} */ + +} // namespace gapi + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.kernel_package"; } + }; + + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.use_only"; } + }; +} // namespace detail + +} // namespace cv + +#endif // OPENCV_GAPI_GKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp new file mode 100644 index 00000000000..6d6f74ff7f4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -0,0 +1,292 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GMAT_HPP +#define OPENCV_GAPI_GMAT_HPP + +#include +#include // std::shared_ptr + +#include +#include // GShape + +#include + +// TODO GAPI_EXPORTS or so +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; + +/** \addtogroup gapi_data_objects + * @{ + * + * @brief G-API data objects used to build G-API expressions. + * + * These objects do not own any particular data (except compile-time + * associated values like with cv::GScalar or `cv::GArray`) and are + * used only to construct graphs. + * + * Every graph in G-API starts and ends with data objects. + * + * Once constructed and compiled, G-API operates with regular host-side + * data instead. Refer to the below table to find the mapping between + * G-API and regular data types when passing input and output data + * structures to G-API: + * + * G-API data type | I/O data type + * ------------------ | ------------- + * cv::GMat | cv::Mat, cv::UMat, cv::RMat + * cv::GScalar | cv::Scalar + * `cv::GArray` | std::vector + * `cv::GOpaque` | T + * cv::GFrame | cv::MediaFrame + */ + +/** + * @brief GMat class represents image or tensor data in the + * graph. + * + * GMat doesn't store any data itself, instead it describes a + * functional relationship between operations consuming and producing + * GMat objects. + * + * GMat is a virtual counterpart of Mat and UMat, but it + * doesn't mean G-API use Mat or UMat objects internally to represent + * GMat objects -- the internal data representation may be + * backend-specific or optimized out at all. + * + * @sa Mat, GMatDesc + */ +class GAPI_EXPORTS_W_SIMPLE GMat +{ +public: + /** + * @brief Constructs an empty GMat + * + * Normally, empty G-API data objects denote a starting point of + * the graph. When an empty GMat is assigned to a result of some + * operation, it obtains a functional link to this operation (and + * is not empty anymore). + */ + GAPI_WRAP GMat(); // Empty constructor + + /** + * @brief Constructs a value-initialized GMat + * + * GMat may be associated with a buffer at graph construction time. + * It is useful when some operation has a Mat input which doesn't + * change during the program execution, and is set only once. + * In this case, there's no need to declare such GMat as graph input. + * + * @param m a cv::Mat buffer to associate with this GMat object. + */ + GAPI_WRAP explicit GMat(cv::Mat m); // Value-initialization constructor + + /// @private + GMat(const GNode &n, std::size_t out); // Operation result constructor + /// @private + GOrigin& priv(); // Internal use only + /// @private + const GOrigin& priv() const; // Internal use only + +private: + std::shared_ptr m_priv; +}; + +class GAPI_EXPORTS GMatP : public GMat +{ +public: + using GMat::GMat; +}; + +class RMat; + +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ +struct GAPI_EXPORTS_W_SIMPLE GMatDesc +{ + // FIXME: Default initializers in C++14 + GAPI_PROP int depth; + GAPI_PROP int chan; + GAPI_PROP cv::Size size; // NB.: no multi-dimensional cases covered yet + GAPI_PROP bool planar; + GAPI_PROP std::vector dims; // FIXME: Maybe it's real questionable to have it here + + GAPI_WRAP GMatDesc(int d, int c, cv::Size s, bool p = false) + : depth(d), chan(c), size(s), planar(p) {} + + GAPI_WRAP GMatDesc(int d, const std::vector &dd) + : depth(d), chan(-1), size{-1,-1}, planar(false), dims(dd) {} + + GAPI_WRAP GMatDesc(int d, std::vector &&dd) + : depth(d), chan(-1), size{-1,-1}, planar(false), dims(std::move(dd)) {} + + GAPI_WRAP GMatDesc() : GMatDesc(-1, -1, {-1,-1}) {} + + inline bool operator== (const GMatDesc &rhs) const + { + return depth == rhs.depth + && chan == rhs.chan + && size == rhs.size + && planar == rhs.planar + && dims == rhs.dims; + } + + inline bool operator!= (const GMatDesc &rhs) const + { + return !(*this == rhs); + } + + bool isND() const { return !dims.empty(); } + + // Checks if the passed mat can be described by this descriptor + // (it handles the case when + // 1-channel mat can be reinterpreted as is (1-channel mat) + // and as a 3-channel planar mat with height divided by 3) + bool canDescribe(const cv::Mat& mat) const; + + bool canDescribe(const cv::RMat& mat) const; + + // Meta combinator: return a new GMatDesc which differs in size by delta + // (all other fields are taken unchanged from this GMatDesc) + // FIXME: a better name? + GAPI_WRAP GMatDesc withSizeDelta(cv::Size delta) const + { + GMatDesc desc(*this); + desc.size += delta; + return desc; + } + // Meta combinator: return a new GMatDesc which differs in size by delta + // (all other fields are taken unchanged from this GMatDesc) + // + // This is an overload. + GAPI_WRAP GMatDesc withSizeDelta(int dx, int dy) const + { + return withSizeDelta(cv::Size{dx,dy}); + } + + GAPI_WRAP GMatDesc withSize(cv::Size sz) const + { + GMatDesc desc(*this); + desc.size = sz; + return desc; + } + + // Meta combinator: return a new GMatDesc with specified data depth. + // (all other fields are taken unchanged from this GMatDesc) + GAPI_WRAP GMatDesc withDepth(int ddepth) const + { + GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1); + GMatDesc desc(*this); + if (ddepth != -1) desc.depth = ddepth; + return desc; + } + + // Meta combinator: return a new GMatDesc with specified data depth + // and number of channels. + // (all other fields are taken unchanged from this GMatDesc) + GAPI_WRAP GMatDesc withType(int ddepth, int dchan) const + { + GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1); + GMatDesc desc = withDepth(ddepth); + desc.chan = dchan; + return desc; + } + + // Meta combinator: return a new GMatDesc with planar flag set + // (no size changes are performed, only channel interpretation is changed + // (interleaved -> planar) + GAPI_WRAP GMatDesc asPlanar() const + { + GAPI_Assert(planar == false); + GMatDesc desc(*this); + desc.planar = true; + return desc; + } + + // Meta combinator: return a new GMatDesc + // reinterpreting 1-channel input as planar image + // (size height is divided by plane number) + GAPI_WRAP GMatDesc asPlanar(int planes) const + { + GAPI_Assert(planar == false); + GAPI_Assert(chan == 1); + GAPI_Assert(planes > 1); + GAPI_Assert(size.height % planes == 0); + GMatDesc desc(*this); + desc.size.height /= planes; + desc.chan = planes; + return desc.asPlanar(); + } + + // Meta combinator: return a new GMatDesc with planar flag set to false + // (no size changes are performed, only channel interpretation is changed + // (planar -> interleaved) + GAPI_WRAP GMatDesc asInterleaved() const + { + GAPI_Assert(planar == true); + GMatDesc desc(*this); + desc.planar = false; + return desc; + } +}; + +static inline GMatDesc empty_gmat_desc() { return GMatDesc{-1,-1,{-1,-1}}; } + +namespace gapi { namespace detail { +/** Checks GMatDesc fields if the passed matrix is a set of n-dimentional points. +@param in GMatDesc to check. +@param n expected dimensionality. +@return the amount of points. In case input matrix can't be described as vector of points +of expected dimensionality, returns -1. + */ +int checkVector(const GMatDesc& in, const size_t n); + +/** @overload + +Checks GMatDesc fields if the passed matrix can be described as a set of points of any +dimensionality. + +@return array of two elements in form of std::vector: the amount of points +and their calculated dimensionality. In case input matrix can't be described as vector of points, +returns {-1, -1}. + */ +std::vector checkVector(const GMatDesc& in); +}} // namespace gapi::detail + +#if !defined(GAPI_STANDALONE) +GAPI_EXPORTS GMatDesc descr_of(const cv::UMat &mat); +#endif // !defined(GAPI_STANDALONE) + +//Fwd declarations +namespace gapi { namespace own { + class Mat; + GAPI_EXPORTS GMatDesc descr_of(const Mat &mat); +}}//gapi::own + +GAPI_EXPORTS GMatDesc descr_of(const RMat &mat); + +#if !defined(GAPI_STANDALONE) +GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat); +#else +using gapi::own::descr_of; +#endif + +/** @} */ + +GAPI_EXPORTS std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc); + +} // namespace cv + +#endif // OPENCV_GAPI_GMAT_HPP diff --git a/modules/gapi/include/opencv2/gapi/gmetaarg.hpp b/modules/gapi/include/opencv2/gapi/gmetaarg.hpp new file mode 100644 index 00000000000..f21182c19f4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gmetaarg.hpp @@ -0,0 +1,80 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GMETAARG_HPP +#define OPENCV_GAPI_GMETAARG_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace cv +{ +// FIXME: Rename to GMeta? +// FIXME: user shouldn't deal with it - put to detail? +// GMetaArg is an union type over descriptions of G-types which can serve as +// GComputation's in/output slots. +// +// GMetaArg objects are passed as arguments to GComputation::compile() +// to specify which data a compiled computation should be specialized on. +// For manual compile(), user must supply this metadata, in case of apply() +// this metadata is taken from arguments computation should operate on. +// +// The first type (monostate) is equal to "uninitialized"/"unresolved" meta. +using GMetaArg = util::variant + < util::monostate + , GMatDesc + , GScalarDesc + , GArrayDesc + , GOpaqueDesc + , GFrameDesc + >; +GAPI_EXPORTS std::ostream& operator<<(std::ostream& os, const GMetaArg &); + +using GMetaArgs = std::vector; + +namespace detail +{ + // These traits are used by GComputation::compile() + + // FIXME: is_constructible doesn't work as variant doesn't do any SFINAE + // in its current template constructor + + template struct is_meta_descr : std::false_type {}; + template<> struct is_meta_descr : std::true_type {}; + template<> struct is_meta_descr : std::true_type {}; + template<> struct is_meta_descr : std::true_type {}; + template<> struct is_meta_descr : std::true_type {}; + + template + using are_meta_descrs = all_satisfy; + + template + using are_meta_descrs_but_last = all_satisfy::type>; + +} // namespace detail + +// Note: descr_of(std::vector<..>) returns a GArrayDesc, while +// descrs_of(std::vector<..>) returns an array of Meta args! +class UMat; +GAPI_EXPORTS cv::GMetaArgs descrs_of(const std::vector &vec); +GAPI_EXPORTS cv::GMetaArgs descrs_of(const std::vector &vec); +namespace gapi { namespace own { + GAPI_EXPORTS cv::GMetaArgs descrs_of(const std::vector &vec); +}} // namespace gapi::own + +} // namespace cv + +#endif // OPENCV_GAPI_GMETAARG_HPP diff --git a/modules/gapi/include/opencv2/gapi/gopaque.hpp b/modules/gapi/include/opencv2/gapi/gopaque.hpp new file mode 100644 index 00000000000..a3f98a9867e --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gopaque.hpp @@ -0,0 +1,369 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GOPAQUE_HPP +#define OPENCV_GAPI_GOPAQUE_HPP + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include // OpaqueKind +#include // TypeHintBase + +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; +template class GOpaque; + +/** + * \addtogroup gapi_meta_args + * @{ + */ +struct GAPI_EXPORTS_W_SIMPLE GOpaqueDesc +{ + // FIXME: Body + // FIXME: Also implement proper operator== then + bool operator== (const GOpaqueDesc&) const { return true; } +}; +template GOpaqueDesc descr_of(const U &) { return {};} +GAPI_EXPORTS_W inline GOpaqueDesc empty_gopaque_desc() {return {}; } +/** @} */ + +std::ostream& operator<<(std::ostream& os, const cv::GOpaqueDesc &desc); + +namespace detail +{ + // ConstructOpaque is a callback which stores information about T and is used by + // G-API runtime to construct an object in host memory (T remains opaque for G-API). + // ConstructOpaque is carried into G-API internals by GOpaqueU. + // Currently it is suitable for Host (CPU) plugins only, real offload may require + // more information for manual memory allocation on-device. + class OpaqueRef; + using ConstructOpaque = std::function; + + // FIXME: garray.hpp already contains hint classes (for actual T type verification), + // need to think where it can be moved (currently opaque uses it from garray) + + // This class strips type information from GOpaque and makes it usable + // in the G-API graph compiler (expression unrolling, graph generation, etc). + // Part of GProtoArg. + class GAPI_EXPORTS GOpaqueU + { + public: + GOpaqueU(const GNode &n, std::size_t out); // Operation result constructor + + template + bool holds() const; // Check if was created from GOpaque + + GOrigin& priv(); // Internal use only + const GOrigin& priv() const; // Internal use only + + protected: + GOpaqueU(); // Default constructor + template friend class cv::GOpaque; // (available for GOpaque only) + + void setConstructFcn(ConstructOpaque &&cv); // Store T-aware constructor + + template + void specifyType(); // Store type of initial GOpaque + + template + void storeKind(); + + void setKind(cv::detail::OpaqueKind); + + std::shared_ptr m_priv; + std::shared_ptr m_hint; + }; + + template + bool GOpaqueU::holds() const{ + GAPI_Assert(m_hint != nullptr); + using U = util::decay_t; + return dynamic_cast*>(m_hint.get()) != nullptr; + } + + template + void GOpaqueU::specifyType(){ + m_hint.reset(new TypeHint>); + } + + template + void GOpaqueU::storeKind(){ + // FIXME: Add assert here on cv::Mat and cv::Scalar? + setKind(cv::detail::GOpaqueTraits::kind); + } + + // This class represents a typed object reference. + // Depending on origins, this reference may be either "just a" reference to + // an object created externally, OR actually own the underlying object + // (be value holder). + class BasicOpaqueRef + { + public: + cv::GOpaqueDesc m_desc; + virtual ~BasicOpaqueRef() {} + + virtual void mov(BasicOpaqueRef &ref) = 0; + virtual const void* ptr() const = 0; + virtual void set(const cv::util::any &a) = 0; + }; + + template class OpaqueRefT final: public BasicOpaqueRef + { + using empty_t = util::monostate; + using ro_ext_t = const T *; + using rw_ext_t = T *; + using rw_own_t = T ; + util::variant m_ref; + + inline bool isEmpty() const { return util::holds_alternative(m_ref); } + inline bool isROExt() const { return util::holds_alternative(m_ref); } + inline bool isRWExt() const { return util::holds_alternative(m_ref); } + inline bool isRWOwn() const { return util::holds_alternative(m_ref); } + + void init(const T* obj = nullptr) + { + if (obj) m_desc = cv::descr_of(*obj); + } + + public: + OpaqueRefT() { init(); } + virtual ~OpaqueRefT() {} + + explicit OpaqueRefT(const T& obj) : m_ref(&obj) { init(&obj); } + explicit OpaqueRefT( T& obj) : m_ref(&obj) { init(&obj); } + explicit OpaqueRefT( T&& obj) : m_ref(std::move(obj)) { init(&obj); } + + // Reset a OpaqueRefT. Called only for objects instantiated + // internally in G-API (e.g. temporary GOpaque's within a + // computation). Reset here means both initialization + // (creating an object) and reset (discarding its existing + // content before the next execution). Must never be called + // for external OpaqueRefTs. + void reset() + { + if (isEmpty()) + { + T empty_obj{}; + m_desc = cv::descr_of(empty_obj); + m_ref = std::move(empty_obj); + GAPI_Assert(isRWOwn()); + } + else if (isRWOwn()) + { + util::get(m_ref) = {}; + } + else GAPI_Error("InternalError"); // shouldn't be called in *EXT modes + } + + // Obtain a WRITE reference to underlying object + // Used by CPU kernel API wrappers when a kernel execution frame + // is created + T& wref() + { + GAPI_Assert(isRWExt() || isRWOwn()); + if (isRWExt()) return *util::get(m_ref); + if (isRWOwn()) return util::get(m_ref); + util::throw_error(std::logic_error("Impossible happened")); + } + + // Obtain a READ reference to underlying object + // Used by CPU kernel API wrappers when a kernel execution frame + // is created + const T& rref() const + { + // ANY object can be accessed for reading, even if it declared for + // output. Example -- a GComputation from [in] to [out1,out2] + // where [out2] is a result of operation applied to [out1]: + // + // GComputation boundary + // . . . . . . . + // . . + // [in] ----> foo() ----> [out1] + // . . : + // . . . .:. . . + // . V . + // . bar() ---> [out2] + // . . . . . . . . . . . . + // + if (isROExt()) return *util::get(m_ref); + if (isRWExt()) return *util::get(m_ref); + if (isRWOwn()) return util::get(m_ref); + util::throw_error(std::logic_error("Impossible happened")); + } + + virtual void mov(BasicOpaqueRef &v) override { + OpaqueRefT *tv = dynamic_cast*>(&v); + GAPI_Assert(tv != nullptr); + wref() = std::move(tv->wref()); + } + + virtual const void* ptr() const override { return &rref(); } + + virtual void set(const cv::util::any &a) override { + wref() = util::any_cast(a); + } + }; + + // This class strips type information from OpaqueRefT<> and makes it usable + // in the G-API executables (carrying run-time data/information to kernels). + // Part of GRunArg. + // Its methods are typed proxies to OpaqueRefT. + // OpaqueRef maintains "reference" semantics so two copies of OpaqueRef refer + // to the same underlying object. + class OpaqueRef + { + std::shared_ptr m_ref; + cv::detail::OpaqueKind m_kind = cv::detail::OpaqueKind::CV_UNKNOWN; + + template inline void check() const + { + GAPI_DbgAssert(dynamic_cast*>(m_ref.get()) != nullptr); + } + + public: + OpaqueRef() = default; + + template< + typename T, + typename = util::are_different_t + > + // FIXME: probably won't work with const object + explicit OpaqueRef(T&& obj) : + m_ref(new OpaqueRefT>(std::forward(obj))), + m_kind(GOpaqueTraits>::kind) {} + + cv::detail::OpaqueKind getKind() const + { + return m_kind; + } + + template void reset() + { + if (!m_ref) m_ref.reset(new OpaqueRefT()); + check(); + storeKind(); + static_cast&>(*m_ref).reset(); + } + + template + void storeKind() + { + m_kind = cv::detail::GOpaqueTraits::kind; + } + + template T& wref() + { + check(); + return static_cast&>(*m_ref).wref(); + } + + template const T& rref() const + { + check(); + return static_cast&>(*m_ref).rref(); + } + + void mov(OpaqueRef &v) + { + m_ref->mov(*v.m_ref); + } + + cv::GOpaqueDesc descr_of() const + { + return m_ref->m_desc; + } + + // May be used to uniquely identify this object internally + const void *ptr() const { return m_ref->ptr(); } + + // Introduced for in-graph meta handling + OpaqueRef& operator= (const cv::util::any &a) + { + m_ref->set(a); + return *this; + } + }; +} // namespace detail + +/** \addtogroup gapi_data_objects + * @{ + */ +/** + * @brief `cv::GOpaque` template class represents an object of + * class `T` in the graph. + * + * `cv::GOpaque` describes a functional relationship between operations + * consuming and producing object of class `T`. `cv::GOpaque` is + * designed to extend G-API with user-defined data types, which are + * often required with user-defined operations. G-API can't apply any + * optimizations to user-defined types since these types are opaque to + * the framework. However, there is a number of G-API operations + * declared with `cv::GOpaque` as a return type, + * e.g. cv::gapi::streaming::timestamp() or cv::gapi::streaming::size(). + * + * @sa `cv::GArray` + */ +template class GOpaque +{ +public: + // Host type (or Flat type) - the type this GOpaque is actually + // specified to. + /// @private + using HT = typename detail::flatten_g>::type; + + /** + * @brief Constructs an empty `cv::GOpaque` + * + * Normally, empty G-API data objects denote a starting point of + * the graph. When an empty `cv::GOpaque` is assigned to a result + * of some operation, it obtains a functional link to this + * operation (and is not empty anymore). + */ + GOpaque() { putDetails(); } // Empty constructor + + /// @private + explicit GOpaque(detail::GOpaqueU &&ref) // GOpaqueU-based constructor + : m_ref(ref) { putDetails(); } // (used by GCall, not for users) + + /// @private + detail::GOpaqueU strip() const { + return m_ref; + } + /// @private + static void Ctor(detail::OpaqueRef& ref) { + ref.reset(); + } +private: + void putDetails() { + m_ref.setConstructFcn(&Ctor); + m_ref.specifyType(); + m_ref.storeKind(); + } + + detail::GOpaqueU m_ref; +}; + +/** @} */ + +} // namespace cv + +#endif // OPENCV_GAPI_GOPAQUE_HPP diff --git a/modules/gapi/include/opencv2/gapi/gproto.hpp b/modules/gapi/include/opencv2/gapi/gproto.hpp new file mode 100644 index 00000000000..a2b5d83bc1e --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gproto.hpp @@ -0,0 +1,159 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GPROTO_HPP +#define OPENCV_GAPI_GPROTO_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace cv { + +// FIXME: user shouldn't deal with it - put to detail? +// GProtoArg is an union type over G-types which can serve as +// GComputation's in/output slots. In other words, GProtoArg +// wraps any type which can serve as G-API exchange type. +// +// In Runtime, GProtoArgs are substituted with appropriate GRunArgs. +// +// GProtoArg objects are constructed in-place when user describes +// (captures) computations, user doesn't interact with these types +// directly. +using GProtoArg = util::variant + < GMat + , GMatP + , GFrame + , GScalar + , detail::GArrayU // instead of GArray + , detail::GOpaqueU // instead of GOpaque + >; + +using GProtoArgs = std::vector; + +namespace detail +{ +template inline GProtoArgs packArgs(Ts... args) +{ + return GProtoArgs{ GProtoArg(wrap_gapi_helper::wrap(args))... }; +} + +} + +template +struct GIOProtoArgs +{ +public: + // NB: Used by python wrapper + GIOProtoArgs() = default; + explicit GIOProtoArgs(const GProtoArgs& args) : m_args(args) {} + explicit GIOProtoArgs(GProtoArgs &&args) : m_args(std::move(args)) {} + + GProtoArgs m_args; + + // TODO: Think about the addition operator + /** + * @brief This operator allows to complement the proto vectors at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GIOProtoArgs usage + * + */ + template + friend GIOProtoArgs& operator += (GIOProtoArgs &lhs, const GIOProtoArgs &rhs); +}; + +template +cv::GIOProtoArgs& operator += (cv::GIOProtoArgs &lhs, const cv::GIOProtoArgs &rhs) +{ + lhs.m_args.reserve(lhs.m_args.size() + rhs.m_args.size()); + lhs.m_args.insert(lhs.m_args.end(), rhs.m_args.begin(), rhs.m_args.end()); + return lhs; +} + +struct In_Tag{}; +struct Out_Tag{}; + +using GProtoInputArgs = GIOProtoArgs; +using GProtoOutputArgs = GIOProtoArgs; + +// Perfect forwarding +template inline GProtoInputArgs GIn(Ts&&... ts) +{ + return GProtoInputArgs(detail::packArgs(std::forward(ts)...)); +} + +template inline GProtoOutputArgs GOut(Ts&&... ts) +{ + return GProtoOutputArgs(detail::packArgs(std::forward(ts)...)); +} + +namespace detail +{ + // Extract elements form tuple + // FIXME: Someday utilize a generic tuple_to_vec<> routine + template + static GProtoOutputArgs getGOut_impl(const std::tuple& ts, detail::Seq) + { + return GProtoOutputArgs{ detail::packArgs(std::get(ts)...)}; + } +} + +template inline GProtoOutputArgs GOut(const std::tuple& ts) +{ + // TODO: think of std::forward(ts) + return detail::getGOut_impl(ts, typename detail::MkSeq::type()); +} + +// Takes rvalue as input arg +template inline GProtoOutputArgs GOut(std::tuple&& ts) +{ + // TODO: think of std::forward(ts) + return detail::getGOut_impl(ts, typename detail::MkSeq::type()); +} + +// Extract run-time arguments from node origin +// Can be used to extract constant values associated with G-objects +// (like GScalar) at graph construction time +GRunArg value_of(const GOrigin &origin); + +// Transform run-time computation arguments into a collection of metadata +// extracted from that arguments +GMetaArg GAPI_EXPORTS descr_of(const GRunArg &arg ); +GMetaArgs GAPI_EXPORTS descr_of(const GRunArgs &args); + +// Transform run-time operation result argument into metadata extracted from that argument +// Used to compare the metadata, which generated at compile time with the metadata result operation in run time +GMetaArg GAPI_EXPORTS descr_of(const GRunArgP& argp); + +// Checks if run-time computation argument can be described by metadata +bool GAPI_EXPORTS can_describe(const GMetaArg& meta, const GRunArg& arg); +bool GAPI_EXPORTS can_describe(const GMetaArgs& metas, const GRunArgs& args); + +// Checks if run-time computation result argument can be described by metadata. +// Used to check if the metadata generated at compile time +// coincides with output arguments passed to computation in cpu and ocl backends +bool GAPI_EXPORTS can_describe(const GMetaArg& meta, const GRunArgP& argp); + +// Validates input arguments +void GAPI_EXPORTS validate_input_arg(const GRunArg& arg); +void GAPI_EXPORTS validate_input_args(const GRunArgs& args); + +} // namespace cv + +#endif // OPENCV_GAPI_GPROTO_HPP diff --git a/modules/gapi/include/opencv2/gapi/gpu/core.hpp b/modules/gapi/include/opencv2/gapi/gpu/core.hpp new file mode 100644 index 00000000000..a7ee59577ce --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gpu/core.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GPU_CORE_API_HPP +#define OPENCV_GAPI_GPU_CORE_API_HPP +/** @file +* @deprecated Use instead. +*/ + +#include + +namespace cv { +namespace gapi { +namespace core { +namespace gpu { + using namespace ocl; +} // namespace gpu +} // namespace core +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_GPU_CORE_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp b/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp new file mode 100644 index 00000000000..b52c21de6bf --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp @@ -0,0 +1,18 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GGPUKERNEL_HPP +#define OPENCV_GAPI_GGPUKERNEL_HPP +/** @file +* @deprecated Use instead. +*/ + +#include +#define GAPI_GPU_KERNEL GAPI_OCL_KERNEL + + +#endif // OPENCV_GAPI_GGPUKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp b/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp new file mode 100644 index 00000000000..b0df7ae3315 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GPU_IMGPROC_API_HPP +#define OPENCV_GAPI_GPU_IMGPROC_API_HPP +/** @file +* @deprecated Use instead. +*/ + +#include + + +namespace cv { +namespace gapi { +namespace imgproc { +namespace gpu { + using namespace ocl; +} // namespace gpu +} // namespace imgproc +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_GPU_IMGPROC_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/gscalar.hpp b/modules/gapi/include/opencv2/gapi/gscalar.hpp new file mode 100644 index 00000000000..de0dfe1383c --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gscalar.hpp @@ -0,0 +1,140 @@ +// This file is part of OpenCV project. + +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GSCALAR_HPP +#define OPENCV_GAPI_GSCALAR_HPP + +#include + +#include +#include // GShape +#include + +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; + +/** \addtogroup gapi_data_objects + * @{ + */ +/** + * @brief GScalar class represents cv::Scalar data in the graph. + * + * GScalar may be associated with a cv::Scalar value, which becomes + * its constant value bound in graph compile time. cv::GScalar describes a + * functional relationship between operations consuming and producing + * GScalar objects. + * + * GScalar is a virtual counterpart of cv::Scalar, which is usually used + * to represent the GScalar data in G-API during the execution. + * + * @sa Scalar + */ +class GAPI_EXPORTS_W_SIMPLE GScalar +{ +public: + /** + * @brief Constructs an empty GScalar + * + * Normally, empty G-API data objects denote a starting point of + * the graph. When an empty GScalar is assigned to a result of some + * operation, it obtains a functional link to this operation (and + * is not empty anymore). + */ + GAPI_WRAP GScalar(); + + /** + * @brief Constructs a value-initialized GScalar + * + * GScalars may have their values be associated at graph + * construction time. It is useful when some operation has a + * GScalar input which doesn't change during the program + * execution, and is set only once. In this case, there is no need + * to declare such GScalar as a graph input. + * + * @note The value of GScalar may be overwritten by assigning some + * other GScalar to the object using `operator=` -- on the + * assignment, the old GScalar value is discarded. + * + * @param s a cv::Scalar value to associate with this GScalar object. + */ + GAPI_WRAP + explicit GScalar(const cv::Scalar& s); + + /** + * @overload + * @brief Constructs a value-initialized GScalar + * + * @param s a cv::Scalar value to associate with this GScalar object. + */ + explicit GScalar(cv::Scalar&& s); // Constant value move-constructor from cv::Scalar + + /** + * @overload + * @brief Constructs a value-initialized GScalar + * + * @param v0 A `double` value to associate with this GScalar. Note + * that only the first component of a four-component cv::Scalar is + * set to this value, with others remain zeros. + * + * This constructor overload is not marked `explicit` and can be + * used in G-API expression code like this: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp gscalar_implicit + * + * Here operator+(GMat,GScalar) is used to wrap cv::gapi::addC() + * and a value-initialized GScalar is created on the fly. + * + * @overload + */ + GScalar(double v0); // Constant value constructor from double + + /// @private + GScalar(const GNode &n, std::size_t out); // Operation result constructor + /// @private + GOrigin& priv(); // Internal use only + /// @private + const GOrigin& priv() const; // Internal use only + +private: + std::shared_ptr m_priv; +}; + +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ +struct GAPI_EXPORTS_W_SIMPLE GScalarDesc +{ + // NB.: right now it is empty + + inline bool operator== (const GScalarDesc &) const + { + return true; // NB: implement this method if GScalar meta appears + } + + inline bool operator!= (const GScalarDesc &rhs) const + { + return !(*this == rhs); + } +}; + +GAPI_EXPORTS_W inline GScalarDesc empty_scalar_desc() { return GScalarDesc(); } + +GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar &scalar); + +std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &desc); + +} // namespace cv + +#endif // OPENCV_GAPI_GSCALAR_HPP diff --git a/modules/gapi/include/opencv2/gapi/gstreaming.hpp b/modules/gapi/include/opencv2/gapi/gstreaming.hpp new file mode 100644 index 00000000000..d413195b817 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gstreaming.hpp @@ -0,0 +1,430 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_GSTREAMING_COMPILED_HPP +#define OPENCV_GAPI_GSTREAMING_COMPILED_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace cv { + +template using optional = cv::util::optional; + +namespace detail { +template struct wref_spec { + using type = T; +}; +template struct wref_spec > { + using type = T; +}; + +template +struct OptRef { + struct OptHolder { + virtual void mov(RefHolder &h) = 0; + virtual void reset() = 0; + virtual ~OptHolder() = default; + using Ptr = std::shared_ptr; + }; + template struct Holder final: OptHolder { + std::reference_wrapper > m_opt_ref; + + explicit Holder(cv::optional& opt) : m_opt_ref(std::ref(opt)) { + } + virtual void mov(RefHolder &h) override { + using U = typename wref_spec::type; + m_opt_ref.get() = cv::util::make_optional(std::move(h.template wref())); + } + virtual void reset() override { + m_opt_ref.get().reset(); + } + }; + template + explicit OptRef(cv::optional& t) : m_opt{new Holder(t)} {} + void mov(RefHolder &h) { m_opt->mov(h); } + void reset() { m_opt->reset();} +private: + typename OptHolder::Ptr m_opt; +}; +using OptionalVectorRef = OptRef; +using OptionalOpaqueRef = OptRef; +} // namespace detail + +// TODO: Keep it in sync with GRunArgP (derive the type automatically?) +using GOptRunArgP = util::variant< + optional*, + optional*, + optional*, + optional*, + cv::detail::OptionalVectorRef, + cv::detail::OptionalOpaqueRef +>; +using GOptRunArgsP = std::vector; + +using GOptRunArg = util::variant< + optional, + optional, + optional, + optional, + optional, + optional +>; +using GOptRunArgs = std::vector; + +namespace detail { + +template inline GOptRunArgP wrap_opt_arg(optional& arg) { + // By default, T goes to an OpaqueRef. All other types are specialized + return GOptRunArgP{OptionalOpaqueRef(arg)}; +} + +template inline GOptRunArgP wrap_opt_arg(optional >& arg) { + return GOptRunArgP{OptionalVectorRef(arg)}; +} + +template<> inline GOptRunArgP wrap_opt_arg(optional &m) { + return GOptRunArgP{&m}; +} + +template<> inline GOptRunArgP wrap_opt_arg(optional &m) { + return GOptRunArgP{&m}; +} + +template<> inline GOptRunArgP wrap_opt_arg(optional &f) { + return GOptRunArgP{&f}; +} + +template<> inline GOptRunArgP wrap_opt_arg(optional &s) { + return GOptRunArgP{&s}; +} + +} // namespace detail + +// Now cv::gout() may produce an empty vector (see "dynamic graphs"), so +// there may be a conflict between these two. State here that Opt version +// _must_ have at least one input for this overload +template +inline GOptRunArgsP gout(optional&arg, optional&... args) +{ + return GOptRunArgsP{ detail::wrap_opt_arg(arg), detail::wrap_opt_arg(args)... }; +} + +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @brief Represents a computation (graph) compiled for streaming. + * + * This class represents a product of graph compilation (calling + * cv::GComputation::compileStreaming()). Objects of this class + * actually do stream processing, and the whole pipeline execution + * complexity is incapsulated into objects of this class. Execution + * model has two levels: at the very top, the execution of a + * heterogeneous graph is aggressively pipelined; at the very bottom + * the execution of every internal block is determined by its + * associated backend. Backends are selected based on kernel packages + * passed via compilation arguments ( see @ref gapi_compile_args, + * GNetworkPackage, GKernelPackage for details). + * + * GStreamingCompiled objects have a "player" semantics -- there are + * methods like start() and stop(). GStreamingCompiled has a full + * control over a videostream and so is stateful. You need to specify the + * input stream data using setSource() and then call start() to + * actually start processing. After that, use pull() or try_pull() to + * obtain next processed data frame from the graph in a blocking or + * non-blocking way, respectively. + * + * Currently a single GStreamingCompiled can process only one video + * streat at time. Produce multiple GStreamingCompiled objects to run the + * same graph on multiple video streams. + * + * @sa GCompiled + */ +class GAPI_EXPORTS_W_SIMPLE GStreamingCompiled +{ +public: + class GAPI_EXPORTS Priv; + GAPI_WRAP GStreamingCompiled(); + + // FIXME: More overloads? + /** + * @brief Specify the input data to GStreamingCompiled for + * processing, a generic version. + * + * Use gin() to create an input parameter vector. + * + * Input vectors must have the same number of elements as defined + * in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::Mat needs to be passed where cv::GMat has been + * declared as input, and so on). Run-time exception is generated + * on type mismatch. + * + * In contrast with regular GCompiled, user can also pass an + * object of type GVideoCapture for a GMat parameter of the parent + * GComputation. The compiled pipeline will start fetching data + * from that GVideoCapture and feeding it into the + * pipeline. Pipeline stops when a GVideoCapture marks end of the + * stream (or when stop() is called). + * + * Passing a regular Mat for a GMat parameter makes it "infinite" + * source -- pipeline may run forever feeding with this Mat until + * stopped explicitly. + * + * Currently only a single GVideoCapture is supported as input. If + * the parent GComputation is declared with multiple input GMat's, + * one of those can be specified as GVideoCapture but all others + * must be regular Mat objects. + * + * Throws if pipeline is already running. Use stop() and then + * setSource() to run the graph on a new video stream. + * + * @note This method is not thread-safe (with respect to the user + * side) at the moment. Protect the access if + * start()/stop()/setSource() may be called on the same object in + * multiple threads in your application. + * + * @param ins vector of inputs to process. + * @sa gin + */ + void setSource(GRunArgs &&ins); + + /// @private -- Exclude this function from OpenCV documentation + GAPI_WRAP void setSource(const cv::detail::ExtractArgsCallback& callback); + + /** + * @brief Specify an input video stream for a single-input + * computation pipeline. + * + * Throws if pipeline is already running. Use stop() and then + * setSource() to run the graph on a new video stream. + * + * @overload + * @param s a shared pointer to IStreamSource representing the + * input video stream. + */ + void setSource(const gapi::wip::IStreamSource::Ptr& s); + + /** + * @brief Constructs and specifies an input video stream for a + * single-input computation pipeline with the given parameters. + * + * Throws if pipeline is already running. Use stop() and then + * setSource() to run the graph on a new video stream. + * + * @overload + * @param args arguments used to construct and initialize a stream + * source. + */ + template + void setSource(Args&&... args) { + setSource(cv::gapi::wip::make_src(std::forward(args)...)); + } + + /** + * @brief Start the pipeline execution. + * + * Use pull()/try_pull() to obtain data. Throws an exception if + * a video source was not specified. + * + * setSource() must be called first, even if the pipeline has been + * working already and then stopped (explicitly via stop() or due + * stream completion) + * + * @note This method is not thread-safe (with respect to the user + * side) at the moment. Protect the access if + * start()/stop()/setSource() may be called on the same object in + * multiple threads in your application. + */ + GAPI_WRAP void start(); + + /** + * @brief Get the next processed frame from the pipeline. + * + * Use gout() to create an output parameter vector. + * + * Output vectors must have the same number of elements as defined + * in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::Mat needs to be passed where cv::GMat has been + * declared as output, and so on). Run-time exception is generated + * on type mismatch. + * + * This method writes new data into objects passed via output + * vector. If there is no data ready yet, this method blocks. Use + * try_pull() if you need a non-blocking version. + * + * @param outs vector of output parameters to obtain. + * @return true if next result has been obtained, + * false marks end of the stream. + */ + bool pull(cv::GRunArgsP &&outs); + + // NB: Used from python + /// @private -- Exclude this function from OpenCV documentation + GAPI_WRAP std::tuple> pull(); + + /** + * @brief Get some next available data from the pipeline. + * + * This method takes a vector of cv::optional object. An object is + * assigned to some value if this value is available (ready) at + * the time of the call, and resets the object to empty() if it is + * not. + * + * This is a blocking method which guarantees that some data has + * been written to the output vector on return. + * + * Using this method only makes sense if the graph has + * desynchronized parts (see cv::gapi::desync). If there is no + * desynchronized parts in the graph, the behavior of this + * method is identical to the regular pull() (all data objects are + * produced synchronously in the output vector). + * + * Use gout() to create an output parameter vector. + * + * Output vectors must have the same number of elements as defined + * in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::optional needs to be passed where cv::GMat + * has been declared as output, and so on). Run-time exception is + * generated on type mismatch. + * + * This method writes new data into objects passed via output + * vector. If there is no data ready yet, this method blocks. Use + * try_pull() if you need a non-blocking version. + * + * @param outs vector of output parameters to obtain. + * @return true if next result has been obtained, + * false marks end of the stream. + * + * @sa cv::gapi::desync + */ + bool pull(cv::GOptRunArgsP &&outs); + + /** + * @brief Try to get the next processed frame from the pipeline. + * + * Use gout() to create an output parameter vector. + * + * This method writes new data into objects passed via output + * vector. If there is no data ready yet, the output vector + * remains unchanged and false is returned. + * + * @return true if data has been obtained, and false if it was + * not. Note: false here doesn't mark the end of the stream. + */ + bool try_pull(cv::GRunArgsP &&outs); + + /** + * @brief Stop (abort) processing the pipeline. + * + * Note - it is not pause but a complete stop. Calling start() + * will cause G-API to start processing the stream from the early beginning. + * + * Throws if the pipeline is not running. + */ + GAPI_WRAP void stop(); + + /** + * @brief Test if the pipeline is running. + * + * @note This method is not thread-safe (with respect to the user + * side) at the moment. Protect the access if + * start()/stop()/setSource() may be called on the same object in + * multiple threads in your application. + * + * @return true if the current stream is not over yet. + */ + GAPI_WRAP bool running() const; + + /// @private + Priv& priv(); + + /** + * @brief Check if compiled object is valid (non-empty) + * + * @return true if the object is runnable (valid), false otherwise + */ + explicit operator bool () const; + + /** + * @brief Vector of metadata this graph was compiled for. + * + * @return Unless _reshape_ is not supported, return value is the + * same vector which was passed to cv::GComputation::compile() to + * produce this compiled object. Otherwise, it is the latest + * metadata vector passed to reshape() (if that call was + * successful). + */ + const GMetaArgs& metas() const; // Meta passed to compile() + + /** + * @brief Vector of metadata descriptions of graph outputs + * + * @return vector with formats/resolutions of graph's output + * objects, auto-inferred from input metadata vector by + * operations which form this computation. + * + * @note GCompiled objects produced from the same + * cv::GComputiation graph with different input metas may return + * different values in this vector. + */ + const GMetaArgs& outMetas() const; + +protected: + /// @private + std::shared_ptr m_priv; +}; + +namespace gapi { + +/** + * @brief This namespace contains G-API functions, structures, and + * symbols related to the Streaming execution mode. + * + * Some of the operations defined in this namespace (e.g. size(), + * BGR(), etc.) can be used in the traditional execution mode too. + */ +namespace streaming { +/** + * @brief Specify queue capacity for streaming execution. + * + * In the streaming mode the pipeline steps are connected with queues + * and this compile argument controls every queue's size. + */ +struct GAPI_EXPORTS_W_SIMPLE queue_capacity +{ + GAPI_WRAP + explicit queue_capacity(size_t cap = 1) : capacity(cap) { } + GAPI_PROP_RW + size_t capacity; +}; +} // namespace streaming +} // namespace gapi + +namespace detail +{ +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.queue_capacity"; } +}; +} + +/** @} gapi_main_classes */ + +} + +#endif // OPENCV_GAPI_GSTREAMING_COMPILED_HPP diff --git a/modules/gapi/include/opencv2/gapi/gtransform.hpp b/modules/gapi/include/opencv2/gapi/gtransform.hpp new file mode 100644 index 00000000000..ce88c894d7f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gtransform.hpp @@ -0,0 +1,103 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_GTRANSFORM_HPP +#define OPENCV_GAPI_GTRANSFORM_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace cv +{ + +struct GAPI_EXPORTS GTransform +{ + // FIXME: consider another simplified + // class instead of GComputation + using F = std::function; + + std::string description; + F pattern; + F substitute; + + GTransform(const std::string& d, const F &p, const F &s) : description(d), pattern(p), substitute(s) {} +}; + +namespace detail +{ + +template +struct TransHelper; + +template +struct TransHelper, Out> +{ + template + static GComputation invoke(Callable f, Seq, Seq) + { + const std::tuple ins; + const auto r = tuple_wrap_helper::get(f(std::get(ins)...)); + return GComputation(cv::GIn(std::get(ins)...), + cv::GOut(std::get(r)...)); + } + + static GComputation get_pattern() + { + return invoke(K::pattern, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } + static GComputation get_substitute() + { + return invoke(K::substitute, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } +}; +} // namespace detail + +template +class GTransformImpl; + +template +class GTransformImpl> : public cv::detail::TransHelper, R>, + public cv::detail::TransformTag +{ +public: + // FIXME: currently there is no check that transformations' signatures are unique + // and won't be any intersection in graph compilation stage + using API = K; + + static GTransform transformation() + { + return GTransform(K::descr(), &K::get_pattern, &K::get_substitute); + } +}; +} // namespace cv + +#define G_DESCR_HELPER_CLASS(Class) Class##DescrHelper + +#define G_DESCR_HELPER_BODY(Class, Descr) \ + namespace detail \ + { \ + struct G_DESCR_HELPER_CLASS(Class) \ + { \ + static constexpr const char *descr() { return Descr; } \ + }; \ + } + +#define GAPI_TRANSFORM(Class, API, Descr) \ + G_DESCR_HELPER_BODY(Class, Descr) \ + struct Class final : public cv::GTransformImpl, \ + public detail::G_DESCR_HELPER_CLASS(Class) + +#endif // OPENCV_GAPI_GTRANSFORM_HPP diff --git a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp new file mode 100644 index 00000000000..c42d64a7617 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp @@ -0,0 +1,242 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GTYPE_TRAITS_HPP +#define OPENCV_GAPI_GTYPE_TRAITS_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cv +{ +namespace detail +{ + template + struct contains_shape_field : std::false_type {}; + + template + struct contains_shape_field> : + std::is_same::type, GShape> + {}; + + template + struct has_gshape : contains_shape_field {}; + + // FIXME: These traits and enum and possible numerous switch(kind) + // block may be replaced with a special Handler object or with + // a double dispatch + enum class ArgKind: int + { + OPAQUE_VAL, // Unknown, generic, opaque-to-GAPI data type - STATIC + // Note: OPAQUE is sometimes defined in Win sys headers +#if !defined(OPAQUE) && !defined(CV_DOXYGEN) + OPAQUE = OPAQUE_VAL, // deprecated value used for compatibility, use OPAQUE_VAL instead +#endif + GOBJREF, // reference to object + GMAT, // a cv::GMat + GMATP, // a cv::GMatP + GFRAME, // a cv::GFrame + GSCALAR, // a cv::GScalar + GARRAY, // a cv::GArrayU (note - exactly GArrayU, not GArray!) + GOPAQUE, // a cv::GOpaqueU (note - exactly GOpaqueU, not GOpaque!) + }; + + // Describe G-API types (G-types) with traits. Mostly used by + // cv::GArg to store meta information about types passed into + // operation arguments. Please note that cv::GComputation is + // defined on GProtoArgs, not GArgs! + template struct GTypeTraits; + template struct GTypeTraits + { + static constexpr const ArgKind kind = ArgKind::OPAQUE_VAL; + static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; + }; + template<> struct GTypeTraits + { + static constexpr const ArgKind kind = ArgKind::GMAT; + static constexpr const GShape shape = GShape::GMAT; + static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; + }; + template<> struct GTypeTraits + { + static constexpr const ArgKind kind = ArgKind::GMATP; + static constexpr const GShape shape = GShape::GMAT; + static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; + }; + template<> struct GTypeTraits + { + static constexpr const ArgKind kind = ArgKind::GFRAME; + static constexpr const GShape shape = GShape::GFRAME; + static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; + }; + template<> struct GTypeTraits + { + static constexpr const ArgKind kind = ArgKind::GSCALAR; + static constexpr const GShape shape = GShape::GSCALAR; + static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; + }; + template struct GTypeTraits > + { + static constexpr const ArgKind kind = ArgKind::GARRAY; + static constexpr const GShape shape = GShape::GARRAY; + static constexpr const OpaqueKind op_kind = GOpaqueTraits::kind; + using host_type = std::vector; + using strip_type = cv::detail::VectorRef; + static cv::detail::GArrayU wrap_value(const cv::GArray &t) { return t.strip();} + static cv::detail::VectorRef wrap_in (const std::vector &t) { return detail::VectorRef(t); } + static cv::detail::VectorRef wrap_out ( std::vector &t) { return detail::VectorRef(t); } + }; + template struct GTypeTraits > + { + static constexpr const ArgKind kind = ArgKind::GOPAQUE; + static constexpr const GShape shape = GShape::GOPAQUE; + static constexpr const OpaqueKind op_kind = GOpaqueTraits::kind; + using host_type = T; + using strip_type = cv::detail::OpaqueRef; + static cv::detail::GOpaqueU wrap_value(const cv::GOpaque &t) { return t.strip();} + static cv::detail::OpaqueRef wrap_in (const T &t) { return detail::OpaqueRef(t); } + static cv::detail::OpaqueRef wrap_out ( T &t) { return detail::OpaqueRef(t); } + }; + + // Tests if Trait for type T requires extra marshalling ("custom wrap") or not. + // If Traits has wrap_value() defined, it does. + template struct has_custom_wrap + { + template class check; + template static std::true_type test(check::wrap_value)> *); + template static std::false_type test(...); + using type = decltype(test(nullptr)); + static const constexpr bool value = std::is_same(nullptr))>::value; + }; + + // Resolve a Host type back to its associated G-Type. + // FIXME: Probably it can be avoided + // FIXME: GMatP is not present here. + // (Actually these traits is used only to check + // if associated G-type has custom wrap functions + // and GMat behavior is correct for GMatP) + template struct GTypeOf; +#if !defined(GAPI_STANDALONE) + template<> struct GTypeOf { using type = cv::GMat; }; +#endif // !defined(GAPI_STANDALONE) + template<> struct GTypeOf { using type = cv::GMat; }; + template<> struct GTypeOf { using type = cv::GMat; }; + template<> struct GTypeOf { using type = cv::GScalar; }; + template struct GTypeOf > { using type = cv::GArray; }; + template struct GTypeOf { using type = cv::GOpaque;}; + template<> struct GTypeOf { using type = cv::GFrame; }; + + // FIXME: This is not quite correct since IStreamSource may + // produce not only Mat but also MediaFrame, Scalar and vector + // data. TODO: Extend the type dispatching on these types too. + template<> struct GTypeOf { using type = cv::GMat;}; + template using g_type_of_t = typename GTypeOf::type; + + // Marshalling helper for G-types and its Host types. Helps G-API + // to store G types in internal generic containers for further + // processing. Implements the following callbacks: + // + // * wrap() - converts user-facing G-type into an internal one + // for internal storage. + // Used when G-API operation is instantiated (G::on(), + // etc) during expressing a pipeline. Mostly returns input + // value "as is" except the case when G-type is a template. For + // template G-classes, calls custom wrap() from Traits. + // The value returned by wrap() is then wrapped into GArg() and + // stored in G-API metadata. + // + // Example: + // - cv::GMat arguments are passed as-is. + // - integers, pointers, STL containers, user types are passed as-is. + // - cv::GArray is converted to cv::GArrayU. + // + // * wrap_in() / wrap_out() - convert Host type associated with + // G-type to internal representation type. + // + // - For "simple" (non-template) G-types, returns value as-is. + // Example: cv::GMat has host type cv::Mat, when user passes a + // cv::Mat, system stores it internally as cv::Mat. + // + // - For "complex" (template) G-types, utilizes custom + // wrap_in()/wrap_out() as described in Traits. + // Example: cv::GArray has host type std::vector, when + // user passes a std::vector, system stores it + // internally as VectorRef (with stripped away). + template struct WrapValue + { + static auto wrap(const T& t) -> + typename std::remove_reference::type + { + return static_cast::type>(t); + } + + template static U wrap_in (const U &u) { return u; } + template static U* wrap_out(U &u) { return &u; } + }; + template struct WrapValue::value>::type> + { + static auto wrap(const T& t) -> decltype(GTypeTraits::wrap_value(t)) + { + return GTypeTraits::wrap_value(t); + } + template static auto wrap_in (const U &u) -> typename GTypeTraits::strip_type + { + static_assert(!(cv::detail::has_gshape>::value + || cv::detail::contains::type, GAPI_OWN_TYPES_LIST>::value), + "gin/gout must not be used with G* classes or cv::gapi::own::*"); + return GTypeTraits::wrap_in(u); + } + template static auto wrap_out(U &u) -> typename GTypeTraits::strip_type + { + static_assert(!(cv::detail::has_gshape>::value + || cv::detail::contains::type, GAPI_OWN_TYPES_LIST>::value), + "gin/gout must not be used with G* classes or cv::gapi::own::*"); + return GTypeTraits::wrap_out(u); + } + }; + + template using wrap_gapi_helper = WrapValue::type>; + template using wrap_host_helper = WrapValue >::type>; + +// Union type for various user-defined type constructors (GArray, +// GOpaque, etc) +// +// TODO: Replace construct-only API with a more generic one (probably +// with bits of introspection) +// +// Not required for non-user-defined types (GMat, GScalar, etc) +using HostCtor = util::variant + < util::monostate + , detail::ConstructVec + , detail::ConstructOpaque + >; + +template struct GObtainCtor { + static HostCtor get() { return HostCtor{}; } +}; +template struct GObtainCtor > { + static HostCtor get() { return HostCtor{ConstructVec{&GArray::VCtor}}; } +}; +template struct GObtainCtor > { + static HostCtor get() { return HostCtor{ConstructOpaque{&GOpaque::Ctor}}; } +}; +} // namespace detail +} // namespace cv + +#endif // OPENCV_GAPI_GTYPE_TRAITS_HPP diff --git a/modules/gapi/include/opencv2/gapi/gtyped.hpp b/modules/gapi/include/opencv2/gapi/gtyped.hpp new file mode 100644 index 00000000000..2acc2f7ffbf --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gtyped.hpp @@ -0,0 +1,246 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GTYPED_HPP +#define OPENCV_GAPI_GTYPED_HPP +#if !defined(GAPI_STANDALONE) + +#include + +#include +#include +#include +#include + +namespace cv { + +namespace detail +{ + // FIXME: How to prevent coolhackers from extending it by their own types? + // FIXME: ...Should we care? + template struct ProtoToParam; + template<> struct ProtoToParam { using type = cv::Mat; }; + template<> struct ProtoToParam { using type = cv::Scalar; }; + template struct ProtoToParam > { using type = std::vector; }; + template<> struct ProtoToParam> { using type = std::vector; }; + template struct ProtoToParam > { using type = U; }; + template using ProtoToParamT = typename ProtoToParam::type; + + template struct ProtoToMeta; + template<> struct ProtoToMeta { using type = cv::GMatDesc; }; + template<> struct ProtoToMeta { using type = cv::GScalarDesc; }; + template struct ProtoToMeta > { using type = cv::GArrayDesc; }; + template struct ProtoToMeta > { using type = cv::GOpaqueDesc; }; + template using ProtoToMetaT = typename ProtoToMeta::type; + + //workaround for MSVC 19.0 bug + template + auto make_default()->decltype(T{}) {return {};} +} // detail + +/** + * @brief This class is a typed wrapper over a regular GComputation. + * + * `std::function<>`-like template parameter specifies the graph + * signature so methods so the object's constructor, methods like + * `apply()` and the derived `GCompiledT::operator()` also become + * typed. + * + * There is no need to use cv::gin() or cv::gout() modifiers with + * objects of this class. Instead, all input arguments are followed + * by all output arguments in the order from the template argument + * signature. + * + * Refer to the following example. Regular (untyped) code is written this way: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp Untyped_Example + * + * Here: + * + * - cv::GComputation object is created with a lambda constructor + * where it is defined as a two-input, one-output graph. + * + * - Its method `apply()` in fact takes arbitrary number of arguments + * (as vectors) so user can pass wrong number of inputs/outputs + * here. C++ compiler wouldn't notice that since the cv::GComputation + * API is polymorphic, and only a run-time error will be generated. + * + * Now the same code written with typed API: + * + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp Typed_Example + * + * The key difference is: + * + * - Now the constructor lambda *must take* parameters and *must + * return* values as defined in the `GComputationT<>` signature. + * - Its method `apply()` does not require any extra specifiers to + * separate input arguments from the output ones + * - A `GCompiledT` (compilation product) takes input/output + * arguments with no extra specifiers as well. + */ +template class GComputationT; + +// Single return value implementation +template class GComputationT +{ +public: + typedef std::function Gen; + + class GCompiledT + { + private: + friend class GComputationT; + + cv::GCompiled m_comp; + + explicit GCompiledT(const cv::GCompiled &comp) : m_comp(comp) {} + + public: + GCompiledT() {} + + void operator()(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT &outArg) + { + m_comp(cv::gin(inArgs...), cv::gout(outArg)); + } + + explicit operator bool() const + { + return static_cast(m_comp); + } + }; + +private: + typedef std::pair Captured; + + Captured capture(const Gen& g, Args... args) + { + return Captured(g(args...), cv::GIn(args...)); + } + + Captured m_capture; + cv::GComputation m_comp; + +public: + GComputationT(const Gen &generator) + : m_capture(capture(generator, detail::make_default()...)) + , m_comp(cv::GProtoInputArgs(std::move(m_capture.second)), + cv::GOut(m_capture.first)) + { + } + + void apply(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT &outArg, + GCompileArgs &&args) + { + m_comp.apply(cv::gin(inArgs...), cv::gout(outArg), std::move(args)); + } + + void apply(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT &outArg) + { + apply(inArgs..., outArg, GCompileArgs()); + } + + + GCompiledT compile(detail::ProtoToMetaT... inDescs) + { + GMetaArgs inMetas = { GMetaArg(inDescs)... }; + return GCompiledT(m_comp.compile(std::move(inMetas), GCompileArgs())); + } + + GCompiledT compile(detail::ProtoToMetaT... inDescs, GCompileArgs &&args) + { + GMetaArgs inMetas = { GMetaArg(inDescs)... }; + return GCompiledT(m_comp.compile(std::move(inMetas), std::move(args))); + } +}; + +// Multiple (fixed) return value implementation. FIXME: How to avoid copy-paste? +template class GComputationT(Args...)> +{ +public: + typedef std::function(Args...)> Gen; + + class GCompiledT + { + private: + friend class GComputationT(Args...)>; + + cv::GCompiled m_comp; + explicit GCompiledT(const cv::GCompiled &comp) : m_comp(comp) {} + + public: + GCompiledT() {} + + void operator()(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT&... outArgs) + { + m_comp(cv::gin(inArgs...), cv::gout(outArgs...)); + } + + explicit operator bool() const + { + return static_cast(m_comp); + } + }; + +private: + typedef std::pair Captured; + + template + Captured capture(GProtoArgs &&args, const std::tuple &rr, detail::Seq) + { + return Captured(cv::GOut(std::get(rr)...).m_args, args); + } + + Captured capture(const Gen& g, Args... args) + { + return capture(cv::GIn(args...).m_args, g(args...), typename detail::MkSeq::type()); + } + + Captured m_capture; + cv::GComputation m_comp; + +public: + GComputationT(const Gen &generator) + : m_capture(capture(generator, detail::make_default()...)) + , m_comp(cv::GProtoInputArgs(std::move(m_capture.second)), + cv::GProtoOutputArgs(std::move(m_capture.first))) + { + } + + void apply(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT&... outArgs, + GCompileArgs &&args) + { + m_comp.apply(cv::gin(inArgs...), cv::gout(outArgs...), std::move(args)); + } + + void apply(detail::ProtoToParamT... inArgs, + detail::ProtoToParamT&... outArgs) + { + apply(inArgs..., outArgs..., GCompileArgs()); + } + + + GCompiledT compile(detail::ProtoToMetaT... inDescs) + { + GMetaArgs inMetas = { GMetaArg(inDescs)... }; + return GCompiledT(m_comp.compile(std::move(inMetas), GCompileArgs())); + } + + GCompiledT compile(detail::ProtoToMetaT... inDescs, GCompileArgs &&args) + { + GMetaArgs inMetas = { GMetaArg(inDescs)... }; + return GCompiledT(m_comp.compile(std::move(inMetas), std::move(args))); + } +}; + +} // namespace cv +#endif // !defined(GAPI_STANDALONE) +#endif // OPENCV_GAPI_GTYPED_HPP diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp new file mode 100644 index 00000000000..96aaa5a447f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -0,0 +1,1769 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_IMGPROC_HPP +#define OPENCV_GAPI_IMGPROC_HPP + +#include + +#include // std::tuple + +#include +#include +#include + + +/** \defgroup gapi_imgproc G-API Image processing functionality +@{ + @defgroup gapi_filters Graph API: Image filters + @defgroup gapi_colorconvert Graph API: Converting image from one color space to another + @defgroup gapi_feature Graph API: Image Feature Detection + @defgroup gapi_shape Graph API: Image Structural Analysis and Shape Descriptors + @defgroup gapi_transform Graph API: Image and channel composition functions +@} + */ + +namespace { +void validateFindingContoursMeta(const int depth, const int chan, const int mode) +{ + GAPI_Assert(chan == 1); + switch (mode) + { + case cv::RETR_CCOMP: + GAPI_Assert(depth == CV_8U || depth == CV_32S); + break; + case cv::RETR_FLOODFILL: + GAPI_Assert(depth == CV_32S); + break; + default: + GAPI_Assert(depth == CV_8U); + break; + } +} +} // anonymous namespace + +namespace cv { namespace gapi { + +/** + * @brief This namespace contains G-API Operation Types for OpenCV + * ImgProc module functionality. + */ +namespace imgproc { + using GMat2 = std::tuple; + using GMat3 = std::tuple; // FIXME: how to avoid this? + using GFindContoursOutput = std::tuple>,GArray>; + + G_TYPED_KERNEL(GFilter2D, , "org.opencv.imgproc.filters.filter2D") { + static GMatDesc outMeta(GMatDesc in, int ddepth, Mat, Point, Scalar, int, Scalar) { + return in.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GSepFilter, , "org.opencv.imgproc.filters.sepfilter") { + static GMatDesc outMeta(GMatDesc in, int ddepth, Mat, Mat, Point, Scalar, int, Scalar) { + return in.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GBoxFilter, , "org.opencv.imgproc.filters.boxfilter") { + static GMatDesc outMeta(GMatDesc in, int ddepth, Size, Point, bool, int, Scalar) { + return in.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GBlur, , "org.opencv.imgproc.filters.blur") { + static GMatDesc outMeta(GMatDesc in, Size, Point, int, Scalar) { + return in; + } + }; + + G_TYPED_KERNEL(GGaussBlur, , "org.opencv.imgproc.filters.gaussianBlur") { + static GMatDesc outMeta(GMatDesc in, Size, double, double, int, Scalar) { + return in; + } + }; + + G_TYPED_KERNEL(GMedianBlur, , "org.opencv.imgproc.filters.medianBlur") { + static GMatDesc outMeta(GMatDesc in, int) { + return in; + } + }; + + G_TYPED_KERNEL(GErode, , "org.opencv.imgproc.filters.erode") { + static GMatDesc outMeta(GMatDesc in, Mat, Point, int, int, Scalar) { + return in; + } + }; + + G_TYPED_KERNEL(GDilate, , "org.opencv.imgproc.filters.dilate") { + static GMatDesc outMeta(GMatDesc in, Mat, Point, int, int, Scalar) { + return in; + } + }; + + G_TYPED_KERNEL(GMorphologyEx, , + "org.opencv.imgproc.filters.morphologyEx") { + static GMatDesc outMeta(const GMatDesc &in, MorphTypes, Mat, Point, int, + BorderTypes, Scalar) { + return in; + } + }; + + G_TYPED_KERNEL(GSobel, , "org.opencv.imgproc.filters.sobel") { + static GMatDesc outMeta(GMatDesc in, int ddepth, int, int, int, double, double, int, Scalar) { + return in.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL_M(GSobelXY, , "org.opencv.imgproc.filters.sobelxy") { + static std::tuple outMeta(GMatDesc in, int ddepth, int, int, double, double, int, Scalar) { + return std::make_tuple(in.withDepth(ddepth), in.withDepth(ddepth)); + } + }; + + G_TYPED_KERNEL(GLaplacian, , + "org.opencv.imgproc.filters.laplacian") { + static GMatDesc outMeta(GMatDesc in, int ddepth, int, double, double, int) { + return in.withDepth(ddepth); + } + }; + + G_TYPED_KERNEL(GBilateralFilter, , + "org.opencv.imgproc.filters.bilateralfilter") { + static GMatDesc outMeta(GMatDesc in, int, double, double, int) { + return in; + } + }; + + G_TYPED_KERNEL(GEqHist, , "org.opencv.imgproc.equalizeHist") { + static GMatDesc outMeta(GMatDesc in) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GCanny, , "org.opencv.imgproc.feature.canny") { + static GMatDesc outMeta(GMatDesc in, double, double, int, bool) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GGoodFeatures, + (GMat,int,double,double,Mat,int,bool,double)>, + "org.opencv.imgproc.feature.goodFeaturesToTrack") { + static GArrayDesc outMeta(GMatDesc, int, double, double, const Mat&, int, bool, double) { + return empty_array_desc(); + } + }; + + using RetrMode = RetrievalModes; + using ContMethod = ContourApproximationModes; + G_TYPED_KERNEL(GFindContours, >(GMat,RetrMode,ContMethod,GOpaque)>, + "org.opencv.imgproc.shape.findContours") + { + static GArrayDesc outMeta(GMatDesc in, RetrMode mode, ContMethod, GOpaqueDesc) + { + validateFindingContoursMeta(in.depth, in.chan, mode); + return empty_array_desc(); + } + }; + + // FIXME oc: make default value offset = Point() + G_TYPED_KERNEL(GFindContoursNoOffset, >(GMat,RetrMode,ContMethod)>, + "org.opencv.imgproc.shape.findContoursNoOffset") + { + static GArrayDesc outMeta(GMatDesc in, RetrMode mode, ContMethod) + { + validateFindingContoursMeta(in.depth, in.chan, mode); + return empty_array_desc(); + } + }; + + G_TYPED_KERNEL(GFindContoursH,)>, + "org.opencv.imgproc.shape.findContoursH") + { + static std::tuple + outMeta(GMatDesc in, RetrMode mode, ContMethod, GOpaqueDesc) + { + validateFindingContoursMeta(in.depth, in.chan, mode); + return std::make_tuple(empty_array_desc(), empty_array_desc()); + } + }; + + // FIXME oc: make default value offset = Point() + G_TYPED_KERNEL(GFindContoursHNoOffset,, + "org.opencv.imgproc.shape.findContoursHNoOffset") + { + static std::tuple + outMeta(GMatDesc in, RetrMode mode, ContMethod) + { + validateFindingContoursMeta(in.depth, in.chan, mode); + return std::make_tuple(empty_array_desc(), empty_array_desc()); + } + }; + + G_TYPED_KERNEL(GBoundingRectMat, (GMat)>, + "org.opencv.imgproc.shape.boundingRectMat") { + static GOpaqueDesc outMeta(GMatDesc in) { + if (in.depth == CV_8U) + { + GAPI_Assert(in.chan == 1); + } + else + { + GAPI_Assert (in.depth == CV_32S || in.depth == CV_32F); + int amount = detail::checkVector(in, 2u); + GAPI_Assert(amount != -1 && + "Input Mat can't be described as vector of 2-dimentional points"); + } + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GBoundingRectVector32S, (GArray)>, + "org.opencv.imgproc.shape.boundingRectVector32S") { + static GOpaqueDesc outMeta(GArrayDesc) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GBoundingRectVector32F, (GArray)>, + "org.opencv.imgproc.shape.boundingRectVector32F") { + static GOpaqueDesc outMeta(GArrayDesc) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine2DMat, (GMat,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine2DMat") { + static GOpaqueDesc outMeta(GMatDesc in,DistanceTypes,double,double,double) { + int amount = detail::checkVector(in, 2u); + GAPI_Assert(amount != -1 && + "Input Mat can't be described as vector of 2-dimentional points"); + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine2DVector32S, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine2DVector32S") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine2DVector32F, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine2DVector32F") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine2DVector64F, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine2DVector64F") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine3DMat, (GMat,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine3DMat") { + static GOpaqueDesc outMeta(GMatDesc in,int,double,double,double) { + int amount = detail::checkVector(in, 3u); + GAPI_Assert(amount != -1 && + "Input Mat can't be described as vector of 3-dimentional points"); + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine3DVector32S, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine3DVector32S") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine3DVector32F, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine3DVector32F") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GFitLine3DVector64F, + (GArray,DistanceTypes,double,double,double)>, + "org.opencv.imgproc.shape.fitLine3DVector64F") { + static GOpaqueDesc outMeta(GArrayDesc,DistanceTypes,double,double,double) { + return empty_gopaque_desc(); + } + }; + + G_TYPED_KERNEL(GBGR2RGB, , "org.opencv.imgproc.colorconvert.bgr2rgb") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GRGB2YUV, , "org.opencv.imgproc.colorconvert.rgb2yuv") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GYUV2RGB, , "org.opencv.imgproc.colorconvert.yuv2rgb") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GBGR2I420, , "org.opencv.imgproc.colorconvert.bgr2i420") { + static GMatDesc outMeta(GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.size.height % 2 == 0); + return in.withType(in.depth, 1).withSize(Size(in.size.width, in.size.height * 3 / 2)); + } + }; + + G_TYPED_KERNEL(GRGB2I420, , "org.opencv.imgproc.colorconvert.rgb2i420") { + static GMatDesc outMeta(GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.size.height % 2 == 0); + return in.withType(in.depth, 1).withSize(Size(in.size.width, in.size.height * 3 / 2)); + } + }; + + G_TYPED_KERNEL(GI4202BGR, , "org.opencv.imgproc.colorconvert.i4202bgr") { + static GMatDesc outMeta(GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 1); + GAPI_Assert(in.size.height % 3 == 0); + return in.withType(in.depth, 3).withSize(Size(in.size.width, in.size.height * 2 / 3)); + } + }; + + G_TYPED_KERNEL(GI4202RGB, , "org.opencv.imgproc.colorconvert.i4202rgb") { + static GMatDesc outMeta(GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 1); + GAPI_Assert(in.size.height % 3 == 0); + return in.withType(in.depth, 3).withSize(Size(in.size.width, in.size.height * 2 / 3)); + } + }; + + G_TYPED_KERNEL(GNV12toRGB, , "org.opencv.imgproc.colorconvert.nv12torgb") { + static GMatDesc outMeta(GMatDesc in_y, GMatDesc in_uv) { + GAPI_Assert(in_y.chan == 1); + GAPI_Assert(in_uv.chan == 2); + GAPI_Assert(in_y.depth == CV_8U); + GAPI_Assert(in_uv.depth == CV_8U); + // UV size should be aligned with Y + GAPI_Assert(in_y.size.width == 2 * in_uv.size.width); + GAPI_Assert(in_y.size.height == 2 * in_uv.size.height); + return in_y.withType(CV_8U, 3); // type will be CV_8UC3; + } + }; + + G_TYPED_KERNEL(GNV12toBGR, , "org.opencv.imgproc.colorconvert.nv12tobgr") { + static GMatDesc outMeta(GMatDesc in_y, GMatDesc in_uv) { + GAPI_Assert(in_y.chan == 1); + GAPI_Assert(in_uv.chan == 2); + GAPI_Assert(in_y.depth == CV_8U); + GAPI_Assert(in_uv.depth == CV_8U); + // UV size should be aligned with Y + GAPI_Assert(in_y.size.width == 2 * in_uv.size.width); + GAPI_Assert(in_y.size.height == 2 * in_uv.size.height); + return in_y.withType(CV_8U, 3); // type will be CV_8UC3; + } + }; + + G_TYPED_KERNEL(GRGB2Lab, , "org.opencv.imgproc.colorconvert.rgb2lab") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GBGR2LUV, , "org.opencv.imgproc.colorconvert.bgr2luv") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GLUV2BGR, , "org.opencv.imgproc.colorconvert.luv2bgr") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GYUV2BGR, , "org.opencv.imgproc.colorconvert.yuv2bgr") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GBGR2YUV, , "org.opencv.imgproc.colorconvert.bgr2yuv") { + static GMatDesc outMeta(GMatDesc in) { + return in; // type still remains CV_8UC3; + } + }; + + G_TYPED_KERNEL(GRGB2Gray, , "org.opencv.imgproc.colorconvert.rgb2gray") { + static GMatDesc outMeta(GMatDesc in) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GRGB2GrayCustom, , "org.opencv.imgproc.colorconvert.rgb2graycustom") { + static GMatDesc outMeta(GMatDesc in, float, float, float) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GBGR2Gray, , "org.opencv.imgproc.colorconvert.bgr2gray") { + static GMatDesc outMeta(GMatDesc in) { + return in.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GBayerGR2RGB, , "org.opencv.imgproc.colorconvert.bayergr2rgb") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + return in.withType(CV_8U, 3); + } + }; + + G_TYPED_KERNEL(GRGB2HSV, , "org.opencv.imgproc.colorconvert.rgb2hsv") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + return in; + } + }; + + G_TYPED_KERNEL(GRGB2YUV422, , "org.opencv.imgproc.colorconvert.rgb2yuv422") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + return in.withType(in.depth, 2); + } + }; + + G_TYPED_KERNEL(GNV12toRGBp, , "org.opencv.imgproc.colorconvert.nv12torgbp") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 3).asPlanar(); + } + }; + + G_TYPED_KERNEL(GNV12toGray, , "org.opencv.imgproc.colorconvert.nv12togray") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 1); + } + }; + + G_TYPED_KERNEL(GNV12toBGRp, , "org.opencv.imgproc.colorconvert.nv12tobgrp") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 3).asPlanar(); + } + }; + + G_TYPED_KERNEL(GResize, , "org.opencv.imgproc.transform.resize") { + static GMatDesc outMeta(GMatDesc in, Size sz, double fx, double fy, int /*interp*/) { + if (sz.width != 0 && sz.height != 0) + { + return in.withSize(sz); + } + else + { + int outSz_w = saturate_cast(in.size.width * fx); + int outSz_h = saturate_cast(in.size.height * fy); + GAPI_Assert(outSz_w > 0 && outSz_h > 0); + return in.withSize(Size(outSz_w, outSz_h)); + } + } + }; + + G_TYPED_KERNEL(GResizeP, , "org.opencv.imgproc.transform.resizeP") { + static GMatDesc outMeta(GMatDesc in, Size sz, int interp) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.planar); + GAPI_Assert(interp == cv::INTER_LINEAR); + return in.withSize(sz); + } + }; + +} //namespace imgproc + +//! @addtogroup gapi_filters +//! @{ +/** @brief Applies a separable linear filter to a matrix(image). + +The function applies a separable linear filter to the matrix. That is, first, every row of src is +filtered with the 1D kernel kernelX. Then, every column of the result is filtered with the 1D +kernel kernelY. The final result is returned. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - In case of floating-point computation, rounding to nearest even is procedeed +if hardware supports it (if not - to nearest value). + - Function textual ID is "org.opencv.imgproc.filters.sepfilter" +@param src Source image. +@param ddepth desired depth of the destination image (the following combinations of src.depth() and ddepth are supported: + + src.depth() = CV_8U, ddepth = -1/CV_16S/CV_32F/CV_64F + src.depth() = CV_16U/CV_16S, ddepth = -1/CV_32F/CV_64F + src.depth() = CV_32F, ddepth = -1/CV_32F/CV_64F + src.depth() = CV_64F, ddepth = -1/CV_64F + +when ddepth=-1, the output image will have the same depth as the source) +@param kernelX Coefficients for filtering each row. +@param kernelY Coefficients for filtering each column. +@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor +is at the kernel center. +@param delta Value added to the filtered results before storing them. +@param borderType Pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa boxFilter, gaussianBlur, medianBlur + */ +GAPI_EXPORTS_W GMat sepFilter(const GMat& src, int ddepth, const Mat& kernelX, const Mat& kernelY, const Point& anchor /*FIXME: = Point(-1,-1)*/, + const Scalar& delta /*FIXME = GScalar(0)*/, int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); + +/** @brief Convolves an image with the kernel. + +The function applies an arbitrary linear filter to an image. When +the aperture is partially outside the image, the function interpolates outlier pixel values +according to the specified border mode. + +The function does actually compute correlation, not the convolution: + +\f[\texttt{dst} (x,y) = \sum _{ \substack{0\leq x' < \texttt{kernel.cols}\\{0\leq y' < \texttt{kernel.rows}}}} \texttt{kernel} (x',y')* \texttt{src} (x+x'- \texttt{anchor.x} ,y+y'- \texttt{anchor.y} )\f] + +That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip +the kernel using flip and set the new anchor to `(kernel.cols - anchor.x - 1, kernel.rows - +anchor.y - 1)`. + +Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +Output image must have the same size and number of channels an input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.filter2D" + +@param src input image. +@param ddepth desired depth of the destination image +@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point +matrix; if you want to apply different kernels to different channels, split the image into +separate color planes using split and process them individually. +@param anchor anchor of the kernel that indicates the relative position of a filtered point within +the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor +is at the kernel center. +@param delta optional value added to the filtered pixels before storing them in dst. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa sepFilter + */ +GAPI_EXPORTS_W GMat filter2D(const GMat& src, int ddepth, const Mat& kernel, const Point& anchor = Point(-1,-1), const Scalar& delta = Scalar(0), + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); + + +/** @brief Blurs an image using the box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \alpha \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \end{bmatrix}\f] + +where + +\f[\alpha = \begin{cases} \frac{1}{\texttt{ksize.width*ksize.height}} & \texttt{when } \texttt{normalize=true} \\1 & \texttt{otherwise} \end{cases}\f] + +Unnormalized box filter is useful for computing various integral characteristics over each pixel +neighborhood, such as covariance matrices of image derivatives (used in dense optical flow +algorithms, and so on). If you need to compute pixel sums over variable-size windows, use cv::integral. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.boxfilter" + +@param src Source image. +@param dtype the output image depth (-1 to set the input image data type). +@param ksize blurring kernel size. +@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor +is at the kernel center. +@param normalize flag, specifying whether the kernel is normalized by its area or not. +@param borderType Pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa sepFilter, gaussianBlur, medianBlur, integral + */ +GAPI_EXPORTS_W GMat boxFilter(const GMat& src, int dtype, const Size& ksize, const Point& anchor = Point(-1,-1), + bool normalize = true, int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); + +/** @brief Blurs an image using the normalized box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \frac{1}{\texttt{ksize.width*ksize.height}} \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \end{bmatrix}\f] + +The call `blur(src, ksize, anchor, borderType)` is equivalent to `boxFilter(src, src.type(), ksize, anchor, +true, borderType)`. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.blur" + +@param src Source image. +@param ksize blurring kernel size. +@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel +center. +@param borderType border mode used to extrapolate pixels outside of the image, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa boxFilter, bilateralFilter, GaussianBlur, medianBlur + */ +GAPI_EXPORTS_W GMat blur(const GMat& src, const Size& ksize, const Point& anchor = Point(-1,-1), + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); + + +//GAPI_EXPORTS_W void blur( InputArray src, OutputArray dst, + // Size ksize, Point anchor = Point(-1,-1), + // int borderType = BORDER_DEFAULT ); + + +/** @brief Blurs an image using a Gaussian filter. + +The function filter2Ds the source image with the specified Gaussian kernel. +Output image must have the same type and number of channels an input image. + +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.gaussianBlur" + +@param src input image; +@param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be +positive and odd. Or, they can be zero's and then they are computed from sigma. +@param sigmaX Gaussian kernel standard deviation in X direction. +@param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be +equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height, +respectively (see cv::getGaussianKernel for details); to fully control the result regardless of +possible future modifications of all this semantics, it is recommended to specify all of ksize, +sigmaX, and sigmaY. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa sepFilter, boxFilter, medianBlur + */ +GAPI_EXPORTS_W GMat gaussianBlur(const GMat& src, const Size& ksize, double sigmaX, double sigmaY = 0, + int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0)); + +/** @brief Blurs an image using the median filter. + +The function smoothes an image using the median filter with the \f$\texttt{ksize} \times +\texttt{ksize}\f$ aperture. Each channel of a multi-channel image is processed independently. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. +The median filter uses cv::BORDER_REPLICATE internally to cope with border pixels, see cv::BorderTypes + - Function textual ID is "org.opencv.imgproc.filters.medianBlur" + +@param src input matrix (image) +@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ... +@sa boxFilter, gaussianBlur + */ +GAPI_EXPORTS_W GMat medianBlur(const GMat& src, int ksize); + +/** @brief Erodes an image by using a specific structuring element. + +The function erodes the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the minimum is taken: + +\f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +Erosion can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently. +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.erode" + +@param src input image +@param kernel structuring element used for erosion; if `element=Mat()`, a `3 x 3` rectangular +structuring element is used. Kernel can be created using getStructuringElement. +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times erosion is applied. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of a constant border +@sa dilate, morphologyEx + */ +GAPI_EXPORTS_W GMat erode(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); + +/** @brief Erodes an image by using 3 by 3 rectangular structuring element. + +The function erodes the source image using the rectangular structuring element with rectangle center as an anchor. +Erosion can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently. +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.erode" + +@param src input image +@param iterations number of times erosion is applied. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of a constant border +@sa erode, dilate3x3 + */ +GAPI_EXPORTS_W GMat erode3x3(const GMat& src, int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); + +/** @brief Dilates an image by using a specific structuring element. + +The function dilates the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the maximum is taken: +\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +Dilation can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently. +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.dilate" + +@param src input image. +@param kernel structuring element used for dilation; if elemenat=Mat(), a 3 x 3 rectangular +structuring element is used. Kernel can be created using getStructuringElement +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times dilation is applied. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of a constant border +@sa erode, morphologyEx, getStructuringElement + */ +GAPI_EXPORTS_W GMat dilate(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); + +/** @brief Dilates an image by using 3 by 3 rectangular structuring element. + +The function dilates the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the maximum is taken: +\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +Dilation can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently. +Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1. +Output image must have the same type, size, and number of channels as the input image. +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.dilate" + +@param src input image. +@param iterations number of times dilation is applied. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of a constant border +@sa dilate, erode3x3 + */ + +GAPI_EXPORTS_W GMat dilate3x3(const GMat& src, int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue()); + +/** @brief Performs advanced morphological transformations. + +The function can perform advanced morphological transformations using an erosion and dilation as +basic operations. + +Any of the operations can be done in-place. In case of multi-channel images, each channel is +processed independently. + +@note + - Function textual ID is "org.opencv.imgproc.filters.morphologyEx" + - The number of iterations is the number of times erosion or dilatation operation will be +applied. For instance, an opening operation (#MORPH_OPEN) with two iterations is equivalent to +apply successively: erode -> erode -> dilate -> dilate +(and not erode -> dilate -> erode -> dilate). + +@param src Input image. +@param op Type of a morphological operation, see #MorphTypes +@param kernel Structuring element. It can be created using #getStructuringElement. +@param anchor Anchor position within the element. Both negative values mean that the anchor is at +the kernel center. +@param iterations Number of times erosion and dilation are applied. +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@param borderValue Border value in case of a constant border. The default value has a special +meaning. +@sa dilate, erode, getStructuringElement + */ +GAPI_EXPORTS_W GMat morphologyEx(const GMat &src, const MorphTypes op, const Mat &kernel, + const Point &anchor = Point(-1,-1), + const int iterations = 1, + const BorderTypes borderType = BORDER_CONSTANT, + const Scalar &borderValue = morphologyDefaultBorderValue()); + +/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. + +In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to +calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$ +kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first +or the second x- or y- derivatives. + +There is also the special value `ksize = FILTER_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr +filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is + +\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f] + +for the x-derivative, or transposed for the y-derivative. + +The function calculates an image derivative by convolving the image with the appropriate kernel: + +\f[\texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f] + +The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less +resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3) +or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first +case corresponds to a kernel of: + +\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f] + +The second case corresponds to a kernel of: + +\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f] + +@note + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.sobel" + +@param src input image. +@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of + 8-bit input images it will result in truncated derivatives. +@param dx order of the derivative x. +@param dy order of the derivative y. +@param ksize size of the extended Sobel kernel; it must be odd. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see cv::getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa filter2D, gaussianBlur, cartToPolar + */ +GAPI_EXPORTS_W GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); + +/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. + +In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to +calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$ +kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first +or the second x- or y- derivatives. + +There is also the special value `ksize = FILTER_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr +filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is + +\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f] + +for the x-derivative, or transposed for the y-derivative. + +The function calculates an image derivative by convolving the image with the appropriate kernel: + +\f[\texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f] + +The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less +resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3) +or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first +case corresponds to a kernel of: + +\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f] + +The second case corresponds to a kernel of: + +\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f] + +@note + - First returned matrix correspons to dx derivative while the second one to dy. + - Rounding to nearest even is procedeed if hardware supports it, if not - to nearest. + - Function textual ID is "org.opencv.imgproc.filters.sobelxy" + +@param src input image. +@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of + 8-bit input images it will result in truncated derivatives. +@param order order of the derivatives. +@param ksize size of the extended Sobel kernel; it must be odd. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see cv::getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see cv::BorderTypes +@param borderValue border value in case of constant border type +@sa filter2D, gaussianBlur, cartToPolar + */ +GAPI_EXPORTS_W std::tuple SobelXY(const GMat& src, int ddepth, int order, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT, + const Scalar& borderValue = Scalar(0)); + +/** @brief Calculates the Laplacian of an image. + +The function calculates the Laplacian of the source image by adding up the second x and y +derivatives calculated using the Sobel operator: + +\f[\texttt{dst} = \Delta \texttt{src} = \frac{\partial^2 \texttt{src}}{\partial x^2} + \frac{\partial^2 \texttt{src}}{\partial y^2}\f] + +This is done when `ksize > 1`. When `ksize == 1`, the Laplacian is computed by filtering the image +with the following \f$3 \times 3\f$ aperture: + +\f[\vecthreethree {0}{1}{0}{1}{-4}{1}{0}{1}{0}\f] + +@note Function textual ID is "org.opencv.imgproc.filters.laplacian" + +@param src Source image. +@param ddepth Desired depth of the destination image. +@param ksize Aperture size used to compute the second-derivative filters. See #getDerivKernels for +details. The size must be positive and odd. +@param scale Optional scale factor for the computed Laplacian values. By default, no scaling is +applied. See #getDerivKernels for details. +@param delta Optional delta value that is added to the results prior to storing them in dst . +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@return Destination image of the same size and the same number of channels as src. +@sa Sobel, Scharr + */ +GAPI_EXPORTS_W GMat Laplacian(const GMat& src, int ddepth, int ksize = 1, + double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT); + +/** @brief Applies the bilateral filter to an image. + +The function applies bilateral filtering to the input image, as described in +http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html +bilateralFilter can reduce unwanted noise very well while keeping edges fairly sharp. However, it is +very slow compared to most filters. + +_Sigma values_: For simplicity, you can set the 2 sigma values to be the same. If they are small (\< +10), the filter will not have much effect, whereas if they are large (\> 150), they will have a very +strong effect, making the image look "cartoonish". + +_Filter size_: Large filters (d \> 5) are very slow, so it is recommended to use d=5 for real-time +applications, and perhaps d=9 for offline applications that need heavy noise filtering. + +This filter does not work inplace. + +@note Function textual ID is "org.opencv.imgproc.filters.bilateralfilter" + +@param src Source 8-bit or floating-point, 1-channel or 3-channel image. +@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, +it is computed from sigmaSpace. +@param sigmaColor Filter sigma in the color space. A larger value of the parameter means that +farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting +in larger areas of semi-equal color. +@param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that +farther pixels will influence each other as long as their colors are close enough (see sigmaColor +). When d\>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is +proportional to sigmaSpace. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes +@return Destination image of the same size and type as src. + */ +GAPI_EXPORTS_W GMat bilateralFilter(const GMat& src, int d, double sigmaColor, double sigmaSpace, + int borderType = BORDER_DEFAULT); + +//! @} gapi_filters + +//! @addtogroup gapi_feature +//! @{ +/** @brief Finds edges in an image using the Canny algorithm. + +The function finds edges in the input image and marks them in the output map edges using the +Canny algorithm. The smallest value between threshold1 and threshold2 is used for edge linking. The +largest value is used to find initial segments of strong edges. See + + +@note Function textual ID is "org.opencv.imgproc.feature.canny" + +@param image 8-bit input image. +@param threshold1 first threshold for the hysteresis procedure. +@param threshold2 second threshold for the hysteresis procedure. +@param apertureSize aperture size for the Sobel operator. +@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm +\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude ( +L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( +L2gradient=false ). + */ +GAPI_EXPORTS_W GMat Canny(const GMat& image, double threshold1, double threshold2, + int apertureSize = 3, bool L2gradient = false); + +/** @brief Determines strong corners on an image. + +The function finds the most prominent corners in the image or in the specified image region, as +described in @cite Shi94 + +- Function calculates the corner quality measure at every source image pixel using the + #cornerMinEigenVal or #cornerHarris . +- Function performs a non-maximum suppression (the local maximums in *3 x 3* neighborhood are + retained). +- The corners with the minimal eigenvalue less than + \f$\texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y)\f$ are rejected. +- The remaining corners are sorted by the quality measure in the descending order. +- Function throws away each corner for which there is a stronger corner at a distance less than + maxDistance. + +The function can be used to initialize a point-based tracker of an object. + +@note + - If the function is called with different values A and B of the parameter qualityLevel , and +A \> B, the vector of returned corners with qualityLevel=A will be the prefix of the output vector +with qualityLevel=B . + - Function textual ID is "org.opencv.imgproc.feature.goodFeaturesToTrack" + +@param image Input 8-bit or floating-point 32-bit, single-channel image. +@param maxCorners Maximum number of corners to return. If there are more corners than are found, +the strongest of them is returned. `maxCorners <= 0` implies that no limit on the maximum is set +and all detected corners are returned. +@param qualityLevel Parameter characterizing the minimal accepted quality of image corners. The +parameter value is multiplied by the best corner quality measure, which is the minimal eigenvalue +(see #cornerMinEigenVal ) or the Harris function response (see #cornerHarris ). The corners with the +quality measure less than the product are rejected. For example, if the best corner has the +quality measure = 1500, and the qualityLevel=0.01 , then all the corners with the quality measure +less than 15 are rejected. +@param minDistance Minimum possible Euclidean distance between the returned corners. +@param mask Optional region of interest. If the image is not empty (it needs to have the type +CV_8UC1 and the same size as image ), it specifies the region in which the corners are detected. +@param blockSize Size of an average block for computing a derivative covariation matrix over each +pixel neighborhood. See cornerEigenValsAndVecs . +@param useHarrisDetector Parameter indicating whether to use a Harris detector (see #cornerHarris) +or #cornerMinEigenVal. +@param k Free parameter of the Harris detector. + +@return vector of detected corners. + */ +GAPI_EXPORTS_W GArray goodFeaturesToTrack(const GMat &image, + int maxCorners, + double qualityLevel, + double minDistance, + const Mat &mask = Mat(), + int blockSize = 3, + bool useHarrisDetector = false, + double k = 0.04); + +/** @brief Equalizes the histogram of a grayscale image. + +//! @} gapi_feature + +The function equalizes the histogram of the input image using the following algorithm: + +- Calculate the histogram \f$H\f$ for src . +- Normalize the histogram so that the sum of histogram bins is 255. +- Compute the integral of the histogram: +\f[H'_i = \sum _{0 \le j < i} H(j)\f] +- Transform the image using \f$H'\f$ as a look-up table: \f$\texttt{dst}(x,y) = H'(\texttt{src}(x,y))\f$ + +The algorithm normalizes the brightness and increases the contrast of the image. +@note + - The returned image is of the same size and type as input. + - Function textual ID is "org.opencv.imgproc.equalizeHist" + +@param src Source 8-bit single channel image. + */ +GAPI_EXPORTS_W GMat equalizeHist(const GMat& src); + +//! @addtogroup gapi_shape +//! @{ +/** @brief Finds contours in a binary image. + +The function retrieves contours from the binary image using the algorithm @cite Suzuki85 . +The contours are a useful tool for shape analysis and object detection and recognition. +See squares.cpp in the OpenCV sample directory. + +@note Function textual ID is "org.opencv.imgproc.shape.findContours" + +@param src Input gray-scale image @ref CV_8UC1. Non-zero pixels are treated as 1's. Zero +pixels remain 0's, so the image is treated as binary . You can use #compare, #inRange, #threshold , +#adaptiveThreshold, #Canny, and others to create a binary image out of a grayscale or color one. +If mode equals to #RETR_CCOMP, the input can also be a 32-bit integer +image of labels ( @ref CV_32SC1 ). If #RETR_FLOODFILL then @ref CV_32SC1 is supported only. +@param mode Contour retrieval mode, see #RetrievalModes +@param method Contour approximation method, see #ContourApproximationModes +@param offset Optional offset by which every contour point is shifted. This is useful if the +contours are extracted from the image ROI and then they should be analyzed in the whole image +context. + +@return GArray of detected contours. Each contour is stored as a GArray of points. + */ +GAPI_EXPORTS GArray> +findContours(const GMat &src, const RetrievalModes mode, const ContourApproximationModes method, + const GOpaque &offset); + +// FIXME oc: make default value offset = Point() +/** @overload +@note Function textual ID is "org.opencv.imgproc.shape.findContoursNoOffset" + */ +GAPI_EXPORTS GArray> +findContours(const GMat &src, const RetrievalModes mode, const ContourApproximationModes method); + +/** @brief Finds contours and their hierarchy in a binary image. + +The function retrieves contours from the binary image using the algorithm @cite Suzuki85 +and calculates their hierarchy. +The contours are a useful tool for shape analysis and object detection and recognition. +See squares.cpp in the OpenCV sample directory. + +@note Function textual ID is "org.opencv.imgproc.shape.findContoursH" + +@param src Input gray-scale image @ref CV_8UC1. Non-zero pixels are treated as 1's. Zero +pixels remain 0's, so the image is treated as binary . You can use #compare, #inRange, #threshold , +#adaptiveThreshold, #Canny, and others to create a binary image out of a grayscale or color one. +If mode equals to #RETR_CCOMP, the input can also be a 32-bit integer +image of labels ( @ref CV_32SC1 ). If #RETR_FLOODFILL -- @ref CV_32SC1 supports only. +@param mode Contour retrieval mode, see #RetrievalModes +@param method Contour approximation method, see #ContourApproximationModes +@param offset Optional offset by which every contour point is shifted. This is useful if the +contours are extracted from the image ROI and then they should be analyzed in the whole image +context. + +@return + - GArray of detected contours. Each contour is stored as a GArray of points. + - Optional output GArray of cv::Vec4i, containing information about the image topology. +It has as many elements as the number of contours. For each i-th contour contours[i], the elements +hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , and hierarchy[i][3] are set to 0-based +indices in contours of the next and previous contours at the same hierarchical level, the first +child contour and the parent contour, respectively. If for the contour i there are no next, +previous, parent, or nested contours, the corresponding elements of hierarchy[i] will be negative. + */ +GAPI_EXPORTS std::tuple>,GArray> +findContoursH(const GMat &src, const RetrievalModes mode, const ContourApproximationModes method, + const GOpaque &offset); + +// FIXME oc: make default value offset = Point() +/** @overload +@note Function textual ID is "org.opencv.imgproc.shape.findContoursHNoOffset" + */ +GAPI_EXPORTS std::tuple>,GArray> +findContoursH(const GMat &src, const RetrievalModes mode, const ContourApproximationModes method); + +/** @brief Calculates the up-right bounding rectangle of a point set or non-zero pixels +of gray-scale image. + +The function calculates and returns the minimal up-right bounding rectangle for the specified +point set or non-zero pixels of gray-scale image. + +@note + - Function textual ID is "org.opencv.imgproc.shape.boundingRectMat" + - In case of a 2D points' set given, Mat should be 2-dimensional, have a single row or column +if there are 2 channels, or have 2 columns if there is a single channel. Mat should have either +@ref CV_32S or @ref CV_32F depth + +@param src Input gray-scale image @ref CV_8UC1; or input set of @ref CV_32S or @ref CV_32F +2D points stored in Mat. + */ +GAPI_EXPORTS_W GOpaque boundingRect(const GMat& src); + +/** @overload + +Calculates the up-right bounding rectangle of a point set. + +@note Function textual ID is "org.opencv.imgproc.shape.boundingRectVector32S" + +@param src Input 2D point set, stored in std::vector. + */ +GAPI_EXPORTS_W GOpaque boundingRect(const GArray& src); + +/** @overload + +Calculates the up-right bounding rectangle of a point set. + +@note Function textual ID is "org.opencv.imgproc.shape.boundingRectVector32F" + +@param src Input 2D point set, stored in std::vector. + */ +GAPI_EXPORTS_W GOpaque boundingRect(const GArray& src); + +/** @brief Fits a line to a 2D point set. + +The function fits a line to a 2D point set by minimizing \f$\sum_i \rho(r_i)\f$ where +\f$r_i\f$ is a distance between the \f$i^{th}\f$ point, the line and \f$\rho(r)\f$ is a distance +function, one of the following: +- DIST_L2 +\f[\rho (r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}\f] +- DIST_L1 +\f[\rho (r) = r\f] +- DIST_L12 +\f[\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)\f] +- DIST_FAIR +\f[\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998\f] +- DIST_WELSCH +\f[\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846\f] +- DIST_HUBER +\f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f] + +The algorithm is based on the M-estimator ( ) technique +that iteratively fits the line using the weighted least-squares algorithm. After each iteration the +weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . + +@note + - Function textual ID is "org.opencv.imgproc.shape.fitLine2DMat" + - In case of an N-dimentional points' set given, Mat should be 2-dimensional, have a single row +or column if there are N channels, or have N columns if there is a single channel. + +@param src Input set of 2D points stored in one of possible containers: Mat, +std::vector, std::vector, std::vector. +@param distType Distance used by the M-estimator, see #DistanceTypes. @ref DIST_USER +and @ref DIST_C are not supported. +@param param Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value +is chosen. +@param reps Sufficient accuracy for the radius (distance between the coordinate origin and the +line). 1.0 would be a good default value for reps. If it is 0, a default value is chosen. +@param aeps Sufficient accuracy for the angle. 0.01 would be a good default value for aeps. +If it is 0, a default value is chosen. + +@return Output line parameters: a vector of 4 elements (like Vec4f) - (vx, vy, x0, y0), +where (vx, vy) is a normalized vector collinear to the line and (x0, y0) is a point on the line. + */ +GAPI_EXPORTS GOpaque fitLine2D(const GMat& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine2DVector32S" + + */ +GAPI_EXPORTS GOpaque fitLine2D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine2DVector32F" + + */ +GAPI_EXPORTS GOpaque fitLine2D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine2DVector64F" + + */ +GAPI_EXPORTS GOpaque fitLine2D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @brief Fits a line to a 3D point set. + +The function fits a line to a 3D point set by minimizing \f$\sum_i \rho(r_i)\f$ where +\f$r_i\f$ is a distance between the \f$i^{th}\f$ point, the line and \f$\rho(r)\f$ is a distance +function, one of the following: +- DIST_L2 +\f[\rho (r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}\f] +- DIST_L1 +\f[\rho (r) = r\f] +- DIST_L12 +\f[\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)\f] +- DIST_FAIR +\f[\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998\f] +- DIST_WELSCH +\f[\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846\f] +- DIST_HUBER +\f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f] + +The algorithm is based on the M-estimator ( ) technique +that iteratively fits the line using the weighted least-squares algorithm. After each iteration the +weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . + +@note + - Function textual ID is "org.opencv.imgproc.shape.fitLine3DMat" + - In case of an N-dimentional points' set given, Mat should be 2-dimensional, have a single row +or column if there are N channels, or have N columns if there is a single channel. + +@param src Input set of 3D points stored in one of possible containers: Mat, +std::vector, std::vector, std::vector. +@param distType Distance used by the M-estimator, see #DistanceTypes. @ref DIST_USER +and @ref DIST_C are not supported. +@param param Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value +is chosen. +@param reps Sufficient accuracy for the radius (distance between the coordinate origin and the +line). 1.0 would be a good default value for reps. If it is 0, a default value is chosen. +@param aeps Sufficient accuracy for the angle. 0.01 would be a good default value for aeps. +If it is 0, a default value is chosen. + +@return Output line parameters: a vector of 6 elements (like Vec6f) - (vx, vy, vz, x0, y0, z0), +where (vx, vy, vz) is a normalized vector collinear to the line and (x0, y0, z0) is a point on +the line. + */ +GAPI_EXPORTS GOpaque fitLine3D(const GMat& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine3DVector32S" + + */ +GAPI_EXPORTS GOpaque fitLine3D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine3DVector32F" + + */ +GAPI_EXPORTS GOpaque fitLine3D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +/** @overload + +@note Function textual ID is "org.opencv.imgproc.shape.fitLine3DVector64F" + + */ +GAPI_EXPORTS GOpaque fitLine3D(const GArray& src, const DistanceTypes distType, + const double param = 0., const double reps = 0., + const double aeps = 0.); + +//! @} gapi_shape + +//! @addtogroup gapi_colorconvert +//! @{ +/** @brief Converts an image from BGR color space to RGB color space. + +The function converts an input image from BGR color space to RGB. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image is 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2rgb" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa RGB2BGR +*/ +GAPI_EXPORTS_W GMat BGR2RGB(const GMat& src); + +/** @brief Converts an image from RGB color space to gray-scaled. + +The conventional ranges for R, G, and B channel values are 0 to 255. +Resulting gray color value computed as +\f[\texttt{dst} (I)= \texttt{0.299} * \texttt{src}(I).R + \texttt{0.587} * \texttt{src}(I).G + \texttt{0.114} * \texttt{src}(I).B \f] + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2gray" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. +@sa RGB2YUV + */ +GAPI_EXPORTS_W GMat RGB2Gray(const GMat& src); + +/** @overload +Resulting gray color value computed as +\f[\texttt{dst} (I)= \texttt{rY} * \texttt{src}(I).R + \texttt{gY} * \texttt{src}(I).G + \texttt{bY} * \texttt{src}(I).B \f] + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2graycustom" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. +@param rY float multiplier for R channel. +@param gY float multiplier for G channel. +@param bY float multiplier for B channel. +@sa RGB2YUV + */ +GAPI_EXPORTS_W GMat RGB2Gray(const GMat& src, float rY, float gY, float bY); + +/** @brief Converts an image from BGR color space to gray-scaled. + +The conventional ranges for B, G, and R channel values are 0 to 255. +Resulting gray color value computed as +\f[\texttt{dst} (I)= \texttt{0.114} * \texttt{src}(I).B + \texttt{0.587} * \texttt{src}(I).G + \texttt{0.299} * \texttt{src}(I).R \f] + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2gray" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. +@sa BGR2LUV + */ +GAPI_EXPORTS_W GMat BGR2Gray(const GMat& src); + +/** @brief Converts an image from RGB color space to YUV color space. + +The function converts an input image from RGB color space to YUV. +The conventional ranges for R, G, and B channel values are 0 to 255. + +In case of linear transformations, the range does not matter. But in case of a non-linear +transformation, an input RGB image should be normalized to the proper value range to get the correct +results, like here, at RGB \f$\rightarrow\f$ Y\*u\*v\* transformation. +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2yuv" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa YUV2RGB, RGB2Lab +*/ +GAPI_EXPORTS_W GMat RGB2YUV(const GMat& src); + +/** @brief Converts an image from BGR color space to I420 color space. + +The function converts an input image from BGR color space to I420. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 1-channel image. @ref CV_8UC1. +Width of I420 output image must be the same as width of input image. +Height of I420 output image must be equal 3/2 from height of input image. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2i420" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa I4202BGR +*/ +GAPI_EXPORTS_W GMat BGR2I420(const GMat& src); + +/** @brief Converts an image from RGB color space to I420 color space. + +The function converts an input image from RGB color space to I420. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 1-channel image. @ref CV_8UC1. +Width of I420 output image must be the same as width of input image. +Height of I420 output image must be equal 3/2 from height of input image. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2i420" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa I4202RGB +*/ +GAPI_EXPORTS_W GMat RGB2I420(const GMat& src); + +/** @brief Converts an image from I420 color space to BGR color space. + +The function converts an input image from I420 color space to BGR. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image. @ref CV_8UC3. +Width of BGR output image must be the same as width of input image. +Height of BGR output image must be equal 2/3 from height of input image. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.i4202bgr" + +@param src input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@sa BGR2I420 +*/ +GAPI_EXPORTS_W GMat I4202BGR(const GMat& src); + +/** @brief Converts an image from I420 color space to BGR color space. + +The function converts an input image from I420 color space to BGR. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image. @ref CV_8UC3. +Width of RGB output image must be the same as width of input image. +Height of RGB output image must be equal 2/3 from height of input image. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.i4202rgb" + +@param src input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@sa RGB2I420 +*/ +GAPI_EXPORTS_W GMat I4202RGB(const GMat& src); + +/** @brief Converts an image from BGR color space to LUV color space. + +The function converts an input image from BGR color space to LUV. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2luv" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa RGB2Lab, RGB2LUV +*/ +GAPI_EXPORTS_W GMat BGR2LUV(const GMat& src); + +/** @brief Converts an image from LUV color space to BGR color space. + +The function converts an input image from LUV color space to BGR. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.luv2bgr" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa BGR2LUV +*/ +GAPI_EXPORTS_W GMat LUV2BGR(const GMat& src); + +/** @brief Converts an image from YUV color space to BGR color space. + +The function converts an input image from YUV color space to BGR. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.yuv2bgr" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa BGR2YUV +*/ +GAPI_EXPORTS_W GMat YUV2BGR(const GMat& src); + +/** @brief Converts an image from BGR color space to YUV color space. + +The function converts an input image from BGR color space to YUV. +The conventional ranges for B, G, and R channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2yuv" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@sa YUV2BGR +*/ +GAPI_EXPORTS_W GMat BGR2YUV(const GMat& src); + +/** @brief Converts an image from RGB color space to Lab color space. + +The function converts an input image from BGR color space to Lab. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2lab" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. +@sa RGB2YUV, RGB2LUV +*/ +GAPI_EXPORTS_W GMat RGB2Lab(const GMat& src); + +/** @brief Converts an image from YUV color space to RGB. +The function converts an input image from YUV color space to RGB. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.yuv2rgb" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. + +@sa RGB2Lab, RGB2YUV +*/ +GAPI_EXPORTS_W GMat YUV2RGB(const GMat& src); + +/** @brief Converts an image from NV12 (YUV420p) color space to RGB. +The function converts an input image from NV12 color space to RGB. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12torgb" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toBGR +*/ +GAPI_EXPORTS_W GMat NV12toRGB(const GMat& src_y, const GMat& src_uv); + +/** @brief Converts an image from NV12 (YUV420p) color space to gray-scaled. +The function converts an input image from NV12 color space to gray-scaled. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned 1-channel image @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12togray" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toBGR +*/ +GAPI_EXPORTS_W GMat NV12toGray(const GMat& src_y, const GMat& src_uv); + +/** @brief Converts an image from NV12 (YUV420p) color space to BGR. +The function converts an input image from NV12 color space to RGB. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12tobgr" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS_W GMat NV12toBGR(const GMat& src_y, const GMat& src_uv); + +/** @brief Converts an image from BayerGR color space to RGB. +The function converts an input image from BayerGR color space to RGB. +The conventional ranges for G, R, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bayergr2rgb" + +@param src_gr input image: 8-bit unsigned 1-channel image @ref CV_8UC1. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS_W GMat BayerGR2RGB(const GMat& src_gr); + +/** @brief Converts an image from RGB color space to HSV. +The function converts an input image from RGB color space to HSV. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2hsv" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS_W GMat RGB2HSV(const GMat& src); + +/** @brief Converts an image from RGB color space to YUV422. +The function converts an input image from RGB color space to YUV422. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 2-channel image @ref CV_8UC2. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2yuv422" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS_W GMat RGB2YUV422(const GMat& src); + +/** @brief Converts an image from NV12 (YUV420p) color space to RGB. +The function converts an input image from NV12 color space to RGB. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned planar 3-channel image @ref CV_8UC1. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, +image type is @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12torgbp" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toBGRp, NV12toRGB +*/ +GAPI_EXPORTS GMatP NV12toRGBp(const GMat &src_y, const GMat &src_uv); + +/** @brief Converts an image from NV12 (YUV420p) color space to BGR. +The function converts an input image from NV12 color space to BGR. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned planar 3-channel image @ref CV_8UC1. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, +image type is @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12torgbp" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toRGBp, NV12toBGR +*/ +GAPI_EXPORTS GMatP NV12toBGRp(const GMat &src_y, const GMat &src_uv); + +//! @} gapi_colorconvert +//! @addtogroup gapi_transform +//! @{ +/** @brief Resizes an image. + +The function resizes the image src down to or up to the specified size. + +Output image size will have the size dsize (when dsize is non-zero) or the size computed from +src.size(), fx, and fy; the depth of output is the same as of src. + +If you want to resize src so that it fits the pre-created dst, +you may call the function as follows: +@code + // explicitly specify dsize=dst.size(); fx and fy will be computed from that. + resize(src, dst, dst.size(), 0, 0, interpolation); +@endcode +If you want to decimate the image by factor of 2 in each direction, you can call the function this +way: +@code + // specify fx and fy and let the function compute the destination image size. + resize(src, dst, Size(), 0.5, 0.5, interpolation); +@endcode +To shrink an image, it will generally look best with cv::INTER_AREA interpolation, whereas to +enlarge an image, it will generally look best with cv::INTER_CUBIC (slow) or cv::INTER_LINEAR +(faster but still looks OK). + +@note Function textual ID is "org.opencv.imgproc.transform.resize" + +@param src input image. +@param dsize output image size; if it equals zero, it is computed as: + \f[\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}\f] + Either dsize or both fx and fy must be non-zero. +@param fx scale factor along the horizontal axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.width/src.cols}\f] +@param fy scale factor along the vertical axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.height/src.rows}\f] +@param interpolation interpolation method, see cv::InterpolationFlags + +@sa warpAffine, warpPerspective, remap, resizeP + */ +GAPI_EXPORTS_W GMat resize(const GMat& src, const Size& dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR); + +/** @brief Resizes a planar image. + +The function resizes the image src down to or up to the specified size. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, image type is @ref CV_8UC1. + +Output image size will have the size dsize, the depth of output is the same as of src. + +@note Function textual ID is "org.opencv.imgproc.transform.resizeP" + +@param src input image, must be of @ref CV_8UC1 type; +@param dsize output image size; +@param interpolation interpolation method, only cv::INTER_LINEAR is supported at the moment + +@sa warpAffine, warpPerspective, remap, resize + */ +GAPI_EXPORTS GMatP resizeP(const GMatP& src, const Size& dsize, int interpolation = cv::INTER_LINEAR); + +//! @} gapi_transform +} //namespace gapi +} //namespace cv + +#endif // OPENCV_GAPI_IMGPROC_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer.hpp b/modules/gapi/include/opencv2/gapi/infer.hpp new file mode 100644 index 00000000000..abbd32ba206 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer.hpp @@ -0,0 +1,717 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_INFER_HPP +#define OPENCV_GAPI_INFER_HPP + +// FIXME: Inference API is currently only available in full mode +#if !defined(GAPI_STANDALONE) + +#include +#include // string +#include // tuple +#include // is_same, false_type + +#include // all_satisfy +#include // any<> +#include // GKernelType[M], GBackend +#include // GArg +#include // CompileArgTag +#include // GMetaArg + +namespace cv { + +template class GNetworkType; + +namespace detail { + +// Infer /////////////////////////////////////////////////////////////////////// +template +struct accepted_infer_types { + static constexpr const auto value = + std::is_same::type, cv::GMat>::value + || std::is_same::type, cv::GFrame>::value; +}; + +template +using valid_infer_types = all_satisfy; + +// Infer2 ////////////////////////////////////////////////////////////////////// + +template +struct valid_infer2_types; + +// Terminal case 1 (50/50 success) +template +struct valid_infer2_types< std::tuple, std::tuple > { + // By default, Nets are limited to GMat argument types only + // for infer2, every GMat argument may translate to either + // GArray or GArray. GArray<> part is stripped + // already at this point. + static constexpr const auto value = + std::is_same::type, cv::GMat>::value + || std::is_same::type, cv::Rect>::value; +}; + +// Terminal case 2 (100% failure) +template +struct valid_infer2_types< std::tuple<>, std::tuple > + : public std::false_type { +}; + +// Terminal case 3 (100% failure) +template +struct valid_infer2_types< std::tuple, std::tuple<> > + : public std::false_type { +}; + +// Recursion -- generic +template +struct valid_infer2_types< std::tuple, std::tuple > { + static constexpr const auto value = + valid_infer2_types< std::tuple, std::tuple >::value + && valid_infer2_types< std::tuple, std::tuple >::value; +}; + +// Struct stores network input/output names. +// Used by infer +struct InOutInfo +{ + std::vector in_names; + std::vector out_names; +}; + +template +class GInferOutputsTyped +{ +public: + GInferOutputsTyped() = default; + GInferOutputsTyped(std::shared_ptr call) + : m_priv(std::make_shared(std::move(call))) + { + } + + OutT at(const std::string& name) + { + auto it = m_priv->blobs.find(name); + if (it == m_priv->blobs.end()) { + // FIXME: Avoid modifying GKernel + auto shape = cv::detail::GTypeTraits::shape; + auto kind = cv::detail::GTypeTraits::op_kind; + m_priv->call->kernel().outShapes.push_back(shape); + m_priv->call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor::get()); + m_priv->call->kernel().outKinds.emplace_back(kind); + auto out_idx = static_cast(m_priv->blobs.size()); + it = m_priv->blobs.emplace(name, + cv::detail::Yield::yield(*(m_priv->call), out_idx)).first; + m_priv->info->out_names.push_back(name); + } + return it->second; + } +private: + struct Priv + { + Priv(std::shared_ptr c) + : call(std::move(c)), info(cv::util::any_cast(&call->params())) + { + } + + std::shared_ptr call; + InOutInfo* info = nullptr; + std::unordered_map blobs; + }; + + std::shared_ptr m_priv; +}; + +template +class GInferInputsTyped +{ +public: + GInferInputsTyped() + : m_priv(std::make_shared()) + { + } + + template + GInferInputsTyped& setInput(const std::string& name, U in) + { + m_priv->blobs.emplace(std::piecewise_construct, + std::forward_as_tuple(name), + std::forward_as_tuple(in)); + return *this; + } + + using StorageT = cv::util::variant; + StorageT& operator[](const std::string& name) { + return m_priv->blobs[name]; + } + + using Map = std::unordered_map; + const Map& getBlobs() const { + return m_priv->blobs; + } + +private: + struct Priv + { + std::unordered_map blobs; + }; + + std::shared_ptr m_priv; +}; + +template +std::shared_ptr makeCall(const std::string &tag, + std::vector &&args, + std::vector &&names, + cv::GKinds &&kinds) { + auto call = std::make_shared(GKernel{ + InferT::id(), + tag, + InferT::getOutMeta, + {}, // outShape will be filled later + std::move(kinds), + {}, // outCtors will be filled later + {}, // outKinds will be filled later + }); + + call->setArgs(std::move(args)); + call->params() = cv::detail::InOutInfo{std::move(names), {}}; + + return call; +} + +} // namespace detail + +// TODO: maybe tuple_wrap_helper from util.hpp may help with this. +// Multiple-return-value network definition (specialized base class) +template +class GNetworkType(Args...)> > +{ +public: + using InArgs = std::tuple; + using OutArgs = std::tuple; + + using Result = OutArgs; + using API = std::function; + + using ResultL = std::tuple< cv::GArray... >; +}; + +// Single-return-value network definition (specialized base class) +template +class GNetworkType > +{ +public: + using InArgs = std::tuple; + using OutArgs = std::tuple; + + using Result = R; + using API = std::function; + + using ResultL = cv::GArray; +}; + +// InferAPI: Accepts either GMat or GFrame for very individual network's input +template +struct InferAPI { + using type = typename std::enable_if + < detail::valid_infer_types::value + && std::tuple_size::value == sizeof...(Ts) + , std::function + >::type; +}; + +// InferAPIRoi: Accepts a rectangle and either GMat or GFrame +template +struct InferAPIRoi { + using type = typename std::enable_if + < detail::valid_infer_types::value + && std::tuple_size::value == 1u + , std::function, T)> + >::type; +}; + +// InferAPIList: Accepts a list of rectangles and list of GMat/GFrames; +// crops every input. +template +struct InferAPIList { + using type = typename std::enable_if + < detail::valid_infer_types::value + && std::tuple_size::value == sizeof...(Ts) + , std::function, Ts...)> + >::type; +}; + +// APIList2 is also template to allow different calling options +// (GArray vs GArray per input) +template +struct InferAPIList2 { + using type = typename std::enable_if + < detail::valid_infer_types::value && + cv::detail::valid_infer2_types< typename Net::InArgs + , std::tuple >::value, + std::function...)> + >::type; +}; + +// Base "Infer" kernel. Note - for whatever network, kernel ID +// is always the same. Different inference calls are distinguished by +// network _tag_ (an extra field in GCall) +// +// getOutMeta is a stub callback collected by G-API kernel subsystem +// automatically. This is a rare case when this callback is defined by +// a particular backend, not by a network itself. +struct GInferBase { + static constexpr const char * id() { + return "org.opencv.dnn.infer"; // Universal stub + } + static GMetaArgs getOutMeta(const GMetaArgs &, const GArgs &) { + return GMetaArgs{}; // One more universal stub + } +}; + +// Base "InferROI" kernel. +// All notes from "Infer" kernel apply here as well. +struct GInferROIBase { + static constexpr const char * id() { + return "org.opencv.dnn.infer-roi"; // Universal stub + } + static GMetaArgs getOutMeta(const GMetaArgs &, const GArgs &) { + return GMetaArgs{}; // One more universal stub + } +}; + +// Base "Infer list" kernel. +// All notes from "Infer" kernel apply here as well. +struct GInferListBase { + static constexpr const char * id() { + return "org.opencv.dnn.infer-roi-list-1"; // Universal stub + } + static GMetaArgs getOutMeta(const GMetaArgs &, const GArgs &) { + return GMetaArgs{}; // One more universal stub + } +}; + +// Base "Infer list 2" kernel. +// All notes from "Infer" kernel apply here as well. +struct GInferList2Base { + static constexpr const char * id() { + return "org.opencv.dnn.infer-roi-list-2"; // Universal stub + } + static GMetaArgs getOutMeta(const GMetaArgs &, const GArgs &) { + return GMetaArgs{}; // One more universal stub + } +}; + +// A generic inference kernel. API (::on()) is fully defined by the Net +// template parameter. +// Acts as a regular kernel in graph (via KernelTypeMedium). +template +struct GInfer final + : public GInferBase + , public detail::KernelTypeMedium< GInfer + , typename InferAPI::type > { + using GInferBase::getOutMeta; // FIXME: name lookup conflict workaround? + + static constexpr const char* tag() { return Net::tag(); } +}; + +// A specific roi-inference kernel. API (::on()) is fixed here and +// verified against Net. +template +struct GInferROI final + : public GInferROIBase + , public detail::KernelTypeMedium< GInferROI + , typename InferAPIRoi::type > { + using GInferROIBase::getOutMeta; // FIXME: name lookup conflict workaround? + + static constexpr const char* tag() { return Net::tag(); } +}; + + +// A generic roi-list inference kernel. API (::on()) is derived from +// the Net template parameter (see more in infer<> overload). +template +struct GInferList final + : public GInferListBase + , public detail::KernelTypeMedium< GInferList + , typename InferAPIList::type > { + using GInferListBase::getOutMeta; // FIXME: name lookup conflict workaround? + + static constexpr const char* tag() { return Net::tag(); } +}; + +// An even more generic roi-list inference kernel. API (::on()) is +// derived from the Net template parameter (see more in infer<> +// overload). +// Takes an extra variadic template list to reflect how this network +// was called (with Rects or GMats as array parameters) +template +struct GInferList2 final + : public GInferList2Base + , public detail::KernelTypeMedium< GInferList2 + , typename InferAPIList2::type > { + using GInferList2Base::getOutMeta; // FIXME: name lookup conflict workaround? + + static constexpr const char* tag() { return Net::tag(); } +}; + +/** + * @brief G-API object used to collect network inputs + */ +using GInferInputs = cv::detail::GInferInputsTyped; + +/** + * @brief G-API object used to collect the list of network inputs + */ +using GInferListInputs = cv::detail::GInferInputsTyped, cv::GArray>; + +/** + * @brief G-API object used to collect network outputs + */ +using GInferOutputs = cv::detail::GInferOutputsTyped; + +/** + * @brief G-API object used to collect the list of network outputs + */ +using GInferListOutputs = cv::detail::GInferOutputsTyped>; + +namespace detail { +void inline unpackBlobs(const cv::GInferInputs::Map& blobs, + std::vector& args, + std::vector& names, + cv::GKinds& kinds) +{ + for (auto&& p : blobs) { + names.emplace_back(p.first); + switch (p.second.index()) { + case cv::GInferInputs::StorageT::index_of(): + args.emplace_back(cv::util::get(p.second)); + kinds.emplace_back(cv::detail::OpaqueKind::CV_MAT); + break; + case cv::GInferInputs::StorageT::index_of(): + args.emplace_back(cv::util::get(p.second)); + kinds.emplace_back(cv::detail::OpaqueKind::CV_UNKNOWN); + break; + default: + GAPI_Error("InternalError"); + } + } +} + +template +struct InferROITraits; + +template <> +struct InferROITraits +{ + using outType = cv::GInferOutputs; + using inType = cv::GOpaque; +}; + +template <> +struct InferROITraits +{ + using outType = cv::GInferListOutputs; + using inType = cv::GArray; +}; + +template +typename InferROITraits::outType +inferGenericROI(const std::string& tag, + const typename InferROITraits::inType& in, + const cv::GInferInputs& inputs) +{ + std::vector args; + std::vector names; + cv::GKinds kinds; + + args.emplace_back(in); + kinds.emplace_back(cv::detail::OpaqueKind::CV_RECT); + + unpackBlobs(inputs.getBlobs(), args, names, kinds); + + auto call = cv::detail::makeCall(tag, + std::move(args), + std::move(names), + std::move(kinds)); + + return {std::move(call)}; +} + +} // namespace detail +} // namespace cv + +// FIXME: Probably the signature makes a function/tuple/function round-trip +#define G_API_NET(Class, API, Tag) \ + struct Class final: public cv::GNetworkType { \ + static constexpr const char * tag() { return Tag; } \ + } + +namespace cv { +namespace gapi { + +/** @brief Calculates response for the specified network (template + * parameter) for the specified region in the source image. + * Currently expects a single-input network only. + * + * @tparam A network type defined with G_API_NET() macro. + * @param in input image where to take ROI from. + * @param roi an object describing the region of interest + * in the source image. May be calculated in the same graph dynamically. + * @return an object of return type as defined in G_API_NET(). + * If a network has multiple return values (defined with a tuple), a tuple of + * objects of appropriate type is returned. + * @sa G_API_NET() + */ +template +typename Net::Result infer(cv::GOpaque roi, T in) { + return GInferROI::on(roi, in); +} + +/** @brief Calculates responses for the specified network (template + * parameter) for every region in the source image. + * + * @tparam A network type defined with G_API_NET() macro. + * @param roi a list of rectangles describing regions of interest + * in the source image. Usually an output of object detector or tracker. + * @param args network's input parameters as specified in G_API_NET() macro. + * NOTE: verified to work reliably with 1-input topologies only. + * @return a list of objects of return type as defined in G_API_NET(). + * If a network has multiple return values (defined with a tuple), a tuple of + * GArray<> objects is returned with the appropriate types inside. + * @sa G_API_NET() + */ +template +typename Net::ResultL infer(cv::GArray roi, Args&&... args) { + return GInferList::on(roi, std::forward(args)...); +} + +/** @brief Calculates responses for the specified network (template + * parameter) for every region in the source image, extended version. + * + * @tparam A network type defined with G_API_NET() macro. + * @param image A source image containing regions of interest + * @param args GArray<> objects of cv::Rect or cv::GMat, one per every + * network input: + * - If a cv::GArray is passed, the appropriate + * regions are taken from `image` and preprocessed to this particular + * network input; + * - If a cv::GArray is passed, the underlying data traited + * as tensor (no automatic preprocessing happen). + * @return a list of objects of return type as defined in G_API_NET(). + * If a network has multiple return values (defined with a tuple), a tuple of + * GArray<> objects is returned with the appropriate types inside. + * @sa G_API_NET() + */ + +template +typename Net::ResultL infer2(T image, cv::GArray... args) { + // FIXME: Declared as "2" because in the current form it steals + // overloads from the regular infer + return GInferList2::on(image, args...); +} + +/** + * @brief Calculates response for the specified network (template + * parameter) given the input data. + * + * @tparam A network type defined with G_API_NET() macro. + * @param args network's input parameters as specified in G_API_NET() macro. + * @return an object of return type as defined in G_API_NET(). + * If a network has multiple return values (defined with a tuple), a tuple of + * objects of appropriate type is returned. + * @sa G_API_NET() + */ +template +typename Net::Result infer(Args&&... args) { + return GInfer::on(std::forward(args)...); +} + +/** + * @brief Generic network type: input and output layers are configured dynamically at runtime + * + * Unlike the network types defined with G_API_NET macro, this one + * doesn't fix number of network inputs and outputs at the compilation stage + * thus providing user with an opportunity to program them in runtime. + */ +struct Generic { }; + +/** + * @brief Calculates response for generic network + * + * @param tag a network tag + * @param inputs networks's inputs + * @return a GInferOutputs + */ +template cv::GInferOutputs +infer(const std::string& tag, const cv::GInferInputs& inputs) +{ + std::vector args; + std::vector names; + cv::GKinds kinds; + + cv::detail::unpackBlobs(inputs.getBlobs(), args, names, kinds); + + auto call = cv::detail::makeCall(tag, + std::move(args), + std::move(names), + std::move(kinds)); + + return cv::GInferOutputs{std::move(call)}; +} + +/** @brief Calculates response for the generic network + * for the specified region in the source image. + * Currently expects a single-input network only. + * + * @param tag a network tag + * @param roi a an object describing the region of interest + * in the source image. May be calculated in the same graph dynamically. + * @param inputs networks's inputs + * @return a cv::GInferOutputs + */ +template cv::GInferOutputs +infer(const std::string& tag, const cv::GOpaque& roi, const cv::GInferInputs& inputs) +{ + return cv::detail::inferGenericROI(tag, roi, inputs); +} + +/** @brief Calculates responses for the specified network + * for every region in the source image. + * + * @param tag a network tag + * @param rois a list of rectangles describing regions of interest + * in the source image. Usually an output of object detector or tracker. + * @param inputs networks's inputs + * @return a cv::GInferListOutputs + */ +template cv::GInferListOutputs +infer(const std::string& tag, const cv::GArray& rois, const cv::GInferInputs& inputs) +{ + return cv::detail::inferGenericROI(tag, rois, inputs); +} + +/** @brief Calculates responses for the specified network + * for every region in the source image, extended version. + * + * @param tag a network tag + * @param in a source image containing regions of interest. + * @param inputs networks's inputs + * @return a cv::GInferListOutputs + */ +template +typename std::enable_if::value, cv::GInferListOutputs>::type +infer2(const std::string& tag, + const Input& in, + const cv::GInferListInputs& inputs) +{ + std::vector args; + std::vector names; + cv::GKinds kinds; + + args.emplace_back(in); + auto k = cv::detail::GOpaqueTraits::kind; + kinds.emplace_back(k); + + for (auto&& p : inputs.getBlobs()) { + names.emplace_back(p.first); + switch (p.second.index()) { + case cv::GInferListInputs::StorageT::index_of>(): + args.emplace_back(cv::util::get>(p.second)); + kinds.emplace_back(cv::detail::OpaqueKind::CV_MAT); + break; + case cv::GInferListInputs::StorageT::index_of>(): + args.emplace_back(cv::util::get>(p.second)); + kinds.emplace_back(cv::detail::OpaqueKind::CV_RECT); + break; + default: + GAPI_Error("InternalError"); + } + } + + auto call = cv::detail::makeCall(tag, + std::move(args), + std::move(names), + std::move(kinds)); + + return cv::GInferListOutputs{std::move(call)}; +} + +} // namespace gapi +} // namespace cv + +#endif // GAPI_STANDALONE + +namespace cv { +namespace gapi { + +// Note: the below code _is_ part of STANDALONE build, +// just to make our compiler code compileable. + +// A type-erased form of network parameters. +// Similar to how a type-erased GKernel is represented and used. +/// @private +struct GAPI_EXPORTS_W_SIMPLE GNetParam { + std::string tag; // FIXME: const? + GBackend backend; // Specifies the execution model + util::any params; // Backend-interpreted parameter structure +}; + +/** \addtogroup gapi_compile_args + * @{ + */ +/** + * @brief A container class for network configurations. Similar to + * GKernelPackage. Use cv::gapi::networks() to construct this object. + * + * @sa cv::gapi::networks + */ +struct GAPI_EXPORTS_W_SIMPLE GNetPackage { + GAPI_WRAP GNetPackage() = default; + GAPI_WRAP explicit GNetPackage(std::vector nets); + explicit GNetPackage(std::initializer_list ii); + std::vector backends() const; + std::vector networks; +}; +/** @} gapi_compile_args */ +} // namespace gapi + +namespace detail { +template +gapi::GNetParam strip(T&& t) { + return gapi::GNetParam { t.tag() + , t.backend() + , t.params() + }; +} + +template<> struct CompileArgTag { + static const char* tag() { return "gapi.net_package"; } +}; + +} // namespace cv::detail + +namespace gapi { +template +cv::gapi::GNetPackage networks(Args&&... args) { + return cv::gapi::GNetPackage({ cv::detail::strip(args)... }); +} + +inline cv::gapi::GNetPackage& operator += ( cv::gapi::GNetPackage& lhs, + const cv::gapi::GNetPackage& rhs) { + lhs.networks.reserve(lhs.networks.size() + rhs.networks.size()); + lhs.networks.insert(lhs.networks.end(), rhs.networks.begin(), rhs.networks.end()); + return lhs; +} + +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp b/modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp new file mode 100644 index 00000000000..94272dea55b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp @@ -0,0 +1,70 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_INFER_BINDINGS_IE_HPP +#define OPENCV_GAPI_INFER_BINDINGS_IE_HPP + +#include +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include // GKernelPackage +#include // Params + +#include + +namespace cv { +namespace gapi { +namespace ie { + +// NB: Used by python wrapper +// This class can be marked as SIMPLE, because it's implemented as pimpl +class GAPI_EXPORTS_W_SIMPLE PyParams { +public: + GAPI_WRAP + PyParams() = default; + + GAPI_WRAP + PyParams(const std::string &tag, + const std::string &model, + const std::string &weights, + const std::string &device); + + GAPI_WRAP + PyParams(const std::string &tag, + const std::string &model, + const std::string &device); + + GAPI_WRAP + PyParams& constInput(const std::string &layer_name, + const cv::Mat &data, + TraitAs hint = TraitAs::TENSOR); + + GAPI_WRAP + PyParams& cfgNumRequests(size_t nireq); + + GAPI_WRAP + PyParams& cfgBatchSize(const size_t size); + + GBackend backend() const; + std::string tag() const; + cv::util::any params() const; + +private: + std::shared_ptr> m_priv; +}; + +GAPI_EXPORTS_W PyParams params(const std::string &tag, + const std::string &model, + const std::string &weights, + const std::string &device); + +GAPI_EXPORTS_W PyParams params(const std::string &tag, + const std::string &model, + const std::string &device); +} // namespace ie +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_BINDINGS_IE_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp new file mode 100644 index 00000000000..f7bb2599242 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp @@ -0,0 +1,74 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level +// directory of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_GAPI_INFER_BINDINGS_ONNX_HPP +#define OPENCV_GAPI_INFER_BINDINGS_ONNX_HPP + +#include // GKernelPackage +#include // Params +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include + +#include + +namespace cv { +namespace gapi { +namespace onnx { + +// NB: Used by python wrapper +// This class can be marked as SIMPLE, because it's implemented as pimpl +class GAPI_EXPORTS_W_SIMPLE PyParams { +public: + GAPI_WRAP + PyParams() = default; + + GAPI_WRAP + PyParams(const std::string& tag, const std::string& model_path); + + GAPI_WRAP + PyParams& cfgMeanStd(const std::string &layer_name, + const cv::Scalar &m, + const cv::Scalar &s); + GAPI_WRAP + PyParams& cfgNormalize(const std::string &layer_name, bool flag); + + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::OpenVINO ep); + + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::DirectML ep); + + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::CoreML ep); + + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::CUDA ep); + + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::TensorRT ep); + + GAPI_WRAP + PyParams& cfgDisableMemPattern(); + + GAPI_WRAP + PyParams& cfgSessionOptions(const std::map& options); + + GAPI_WRAP + PyParams& cfgOptLevel(const int opt_level); + + GBackend backend() const; + std::string tag() const; + cv::util::any params() const; + +private: + std::shared_ptr> m_priv; +}; + +GAPI_EXPORTS_W PyParams params(const std::string& tag, const std::string& model_path); + +} // namespace onnx +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_BINDINGS_ONNX_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp b/modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp new file mode 100644 index 00000000000..08f5c83a3ff --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp @@ -0,0 +1,128 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_INFER_BINDINGS_OV_HPP +#define OPENCV_GAPI_INFER_BINDINGS_OV_HPP + +#include +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include // GKernelPackage +#include // Params + +#include + +namespace cv { +namespace gapi { +namespace ov { + +// NB: Used by python wrapper +// This class can be marked as SIMPLE, because it's implemented as pimpl +class GAPI_EXPORTS_W_SIMPLE PyParams { +public: + GAPI_WRAP + PyParams() = default; + + GAPI_WRAP + PyParams(const std::string &tag, + const std::string &model_path, + const std::string &bin_path, + const std::string &device); + + GAPI_WRAP + PyParams(const std::string &tag, + const std::string &blob_path, + const std::string &device); + + GAPI_WRAP + PyParams& cfgPluginConfig( + const std::map &config); + + GAPI_WRAP + PyParams& cfgInputTensorLayout(std::string tensor_layout); + + GAPI_WRAP + PyParams& cfgInputTensorLayout( + std::map layout_map); + + GAPI_WRAP + PyParams& cfgInputModelLayout(std::string tensor_layout); + + GAPI_WRAP + PyParams& cfgInputModelLayout( + std::map layout_map); + + GAPI_WRAP + PyParams& cfgOutputTensorLayout(std::string tensor_layout); + + GAPI_WRAP + PyParams& cfgOutputTensorLayout( + std::map layout_map); + + GAPI_WRAP + PyParams& cfgOutputModelLayout(std::string tensor_layout); + + GAPI_WRAP + PyParams& cfgOutputModelLayout( + std::map layout_map); + + GAPI_WRAP + PyParams& cfgOutputTensorPrecision(int precision); + + GAPI_WRAP + PyParams& cfgOutputTensorPrecision( + std::map precision_map); + + GAPI_WRAP + PyParams& cfgReshape(std::vector new_shape); + + GAPI_WRAP + PyParams& cfgReshape( + std::map> new_shape_map); + + GAPI_WRAP + PyParams& cfgNumRequests(const size_t nireq); + + GAPI_WRAP + PyParams& cfgMean(std::vector mean_values); + + GAPI_WRAP + PyParams& cfgMean( + std::map> mean_map); + + GAPI_WRAP + PyParams& cfgScale(std::vector scale_values); + + GAPI_WRAP + PyParams& cfgScale( + std::map> scale_map); + + GAPI_WRAP + PyParams& cfgResize(int interpolation); + + GAPI_WRAP + PyParams& cfgResize(std::map interpolation); + + GBackend backend() const; + std::string tag() const; + cv::util::any params() const; + +private: + std::shared_ptr> m_priv; +}; + +GAPI_EXPORTS_W PyParams params(const std::string &tag, + const std::string &model_path, + const std::string &weights, + const std::string &device); + +GAPI_EXPORTS_W PyParams params(const std::string &tag, + const std::string &bin_path, + const std::string &device); +} // namespace ov +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_BINDINGS_OV_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/ie.hpp b/modules/gapi/include/opencv2/gapi/infer/ie.hpp new file mode 100644 index 00000000000..9f9518d0b8e --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/ie.hpp @@ -0,0 +1,711 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019-2023 Intel Corporation + +#ifndef OPENCV_GAPI_INFER_IE_HPP +#define OPENCV_GAPI_INFER_IE_HPP + +#include +#include +#include +#include +#include // tuple, tuple_size +#include + +#include +#include + +#include // GAPI_EXPORTS +#include // GKernelPackage +#include // Generic +#include // Preproc Dev & Ctx + +namespace cv { +namespace gapi { +// FIXME: introduce a new sub-namespace for NN? + +/** + * @brief This namespace contains G-API OpenVINO backend functions, + * structures, and symbols. + */ +namespace ie { + +GAPI_EXPORTS cv::gapi::GBackend backend(); + +/** + * Specifies how G-API and IE should trait input data + * + * In OpenCV, the same cv::Mat is used to represent both + * image and tensor data. Sometimes those are hardly distinguishable, + * so this extra parameter is used to give G-API a hint. + * + * This hint controls how G-API reinterprets the data when converting + * it to IE Blob format (and which layout/etc is assigned to this data). + */ +enum class TraitAs: int +{ + TENSOR, //!< G-API traits an associated cv::Mat as a raw tensor and passes dimensions as-is + IMAGE //!< G-API traits an associated cv::Mat as an image so creates an "image" blob (NCHW/NHWC, etc) +}; + +using IEConfig = std::map; + +enum InferMode {Sync, Async}; + +namespace detail { + +template +using AttrMap = std::map; +// NB: This type is used to hold in/out layers +// attributes such as precision, layout, shape etc. +// +// User can provide attributes either: +// 1. cv::util::monostate - No value specified explicitly. +// 2. Attr - value specified explicitly that should be broadcasted to all layers. +// 3. AttrMap[str->T] - map specifies value for particular layer. +template +using LayerVariantAttr = cv::util::variant< cv::util::monostate + , AttrMap + , Attr>; + +struct ParamDesc { + std::string model_path; + std::string weights_path; + std::string device_id; + + std::vector input_names; + std::vector output_names; + + using ConstInput = std::pair; + std::unordered_map const_inputs; + + std::size_t num_in; + std::size_t num_out; + + enum class Kind {Load, Import}; + Kind kind; + bool is_generic; + IEConfig config; + + std::map> reshape_table; + std::unordered_set layer_names_to_reshape; + + // NB: Number of asyncrhonious infer requests + size_t nireq; + + // NB: An optional config to setup RemoteContext for IE + cv::util::any context_config; + + // NB: batch_size can't be equal to 1 by default, because some of models + // have 2D (Layout::NC) input and if the first dimension not equal to 1 + // net.setBatchSize(1) will overwrite it. + cv::optional batch_size; + + cv::optional vpl_preproc_device; + cv::optional vpl_preproc_ctx; + + InferMode mode; + + using PrecisionT = int; + using PrecisionMapT = std::unordered_map; + // NB: This parameter can contain: + // 1. cv::util::monostate - Don't specify precision, but use default from IR/Blob. + // 2. PrecisionT (CV_8U, CV_32F, ...) - Specifies precision for all output layers. + // 3. PrecisionMapT ({{"layer0", CV_32F}, {"layer1", CV_16F}} - Specifies precision for certain output layer. + // cv::util::monostate is default value that means precision wasn't specified. + using PrecisionVariantT = cv::util::variant; + + PrecisionVariantT output_precision; + LayerVariantAttr input_layout; + LayerVariantAttr output_layout; + LayerVariantAttr interpolation; +}; +} // namespace detail + +// FIXME: this is probably a shared (reusable) thing +template +struct PortCfg { + using In = std::array + < std::string + , std::tuple_size::value >; + using Out = std::array + < std::string + , std::tuple_size::value >; +}; + +/** + * @brief This structure provides functions + * that fill inference parameters for "OpenVINO Toolkit" model. + */ +template class Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and specifies default values for other + inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit". + + @param model Path to topology IR (.xml file). + @param weights Path to weights (.bin file). + @param device target device to use. + */ + Params(const std::string &model, + const std::string &weights, + const std::string &device) + : desc{ model, weights, device, {}, {}, {} + , std::tuple_size::value // num_in + , std::tuple_size::value // num_out + , detail::ParamDesc::Kind::Load + , false + , {} + , {} + , {} + , 1u + , {} + , {} + , {} + , {} + , InferMode::Async + , {} + , {} + , {} + , {} } { + } + + /** @overload + Use this constructor to work with pre-compiled network. + Model is imported from a pre-compiled blob. + + @param model Path to model. + @param device target device to use. + */ + Params(const std::string &model, + const std::string &device) + : desc{ model, {}, device, {}, {}, {} + , std::tuple_size::value // num_in + , std::tuple_size::value // num_out + , detail::ParamDesc::Kind::Import + , false + , {} + , {} + , {} + , 1u + , {} + , {} + , {} + , {} + , InferMode::Async + , {} + , {} + , {} + , {} } { + } + + /** @brief Specifies sequence of network input layers names for inference. + + The function is used to associate cv::gapi::infer<> inputs with the model inputs. + Number of names has to match the number of network inputs as defined in G_API_NET(). + In case a network has only single input layer, there is no need to specify name manually. + + @param layer_names std::array where N is the number of inputs + as defined in the @ref G_API_NET. Contains names of input layers. + @return reference to this parameter structure. + */ + Params& cfgInputLayers(const typename PortCfg::In &layer_names) { + desc.input_names.clear(); + desc.input_names.reserve(layer_names.size()); + std::copy(layer_names.begin(), layer_names.end(), + std::back_inserter(desc.input_names)); + return *this; + } + + /** @brief Specifies sequence of network output layers names for inference. + + The function is used to associate cv::gapi::infer<> outputs with the model outputs. + Number of names has to match the number of network outputs as defined in G_API_NET(). + In case a network has only single output layer, there is no need to specify name manually. + + @param layer_names std::array where N is the number of outputs + as defined in the @ref G_API_NET. Contains names of output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputLayers(const typename PortCfg::Out &layer_names) { + desc.output_names.clear(); + desc.output_names.reserve(layer_names.size()); + std::copy(layer_names.begin(), layer_names.end(), + std::back_inserter(desc.output_names)); + return *this; + } + + /** @brief Specifies a constant input. + + The function is used to set a constant input. This input has to be + a preprocessed tensor if its type is TENSOR. Need to provide name of the + network layer which will receive provided data. + + @param layer_name Name of network layer. + @param data cv::Mat that contains data which will be associated with network layer. + @param hint Input type @sa cv::gapi::ie::TraitAs. + @return reference to this parameter structure. + */ + Params& constInput(const std::string &layer_name, + const cv::Mat &data, + TraitAs hint = TraitAs::TENSOR) { + desc.const_inputs[layer_name] = {data, hint}; + return *this; + } + + /** @brief Specifies OpenVINO plugin configuration. + + The function is used to set configuration for OpenVINO plugin. Some parameters + can be different for each plugin. Please follow https://docs.openvinotoolkit.org/latest/index.html + to check information about specific plugin. + + @param cfg Map of pairs: (config parameter name, config parameter value). + @return reference to this parameter structure. + */ + Params& pluginConfig(const IEConfig& cfg) { + desc.config = cfg; + return *this; + } + + /** @overload + Function with a rvalue parameter. + + @param cfg rvalue map of pairs: (config parameter name, config parameter value). + @return reference to this parameter structure. + */ + Params& pluginConfig(IEConfig&& cfg) { + desc.config = std::move(cfg); + return *this; + } + + /** @brief Specifies configuration for RemoteContext in InferenceEngine. + + When RemoteContext is configured the backend imports the networks using the context. + It also expects cv::MediaFrames to be actually remote, to operate with blobs via the context. + + @param ctx_cfg cv::util::any value which holds InferenceEngine::ParamMap. + @return reference to this parameter structure. + */ + Params& cfgContextParams(const cv::util::any& ctx_cfg) { + desc.context_config = ctx_cfg; + return *this; + } + + /** @overload + Function with an rvalue parameter. + + @param ctx_cfg cv::util::any value which holds InferenceEngine::ParamMap. + @return reference to this parameter structure. + */ + Params& cfgContextParams(cv::util::any&& ctx_cfg) { + desc.context_config = std::move(ctx_cfg); + return *this; + } + + /** @brief Specifies number of asynchronous inference requests. + + @param nireq Number of inference asynchronous requests. + @return reference to this parameter structure. + */ + Params& cfgNumRequests(size_t nireq) { + GAPI_Assert(nireq > 0 && "Number of infer requests must be greater than zero!"); + desc.nireq = nireq; + return *this; + } + + /** @brief Specifies new input shapes for the network inputs. + + The function is used to specify new input shapes for the network inputs. + Follow https://docs.openvinotoolkit.org/latest/classInferenceEngine_1_1networkNetwork.html + for additional information. + + @param reshape_table Map of pairs: name of corresponding data and its dimension. + @return reference to this parameter structure. + */ + Params& cfgInputReshape(const std::map>& reshape_table) { + desc.reshape_table = reshape_table; + return *this; + } + + /** @overload */ + Params& cfgInputReshape(std::map>&& reshape_table) { + desc.reshape_table = std::move(reshape_table); + return *this; + } + + /** @overload + + @param layer_name Name of layer. + @param layer_dims New dimensions for this layer. + @return reference to this parameter structure. + */ + Params& cfgInputReshape(const std::string& layer_name, const std::vector& layer_dims) { + desc.reshape_table.emplace(layer_name, layer_dims); + return *this; + } + + /** @overload */ + Params& cfgInputReshape(std::string&& layer_name, std::vector&& layer_dims) { + desc.reshape_table.emplace(layer_name, layer_dims); + return *this; + } + + /** @overload + + @param layer_names set of names of network layers that will be used for network reshape. + @return reference to this parameter structure. + */ + Params& cfgInputReshape(const std::unordered_set& layer_names) { + desc.layer_names_to_reshape = layer_names; + return *this; + } + + /** @overload + + @param layer_names rvalue set of the selected layers will be reshaped automatically + its input image size. + @return reference to this parameter structure. + */ + Params& cfgInputReshape(std::unordered_set&& layer_names) { + desc.layer_names_to_reshape = std::move(layer_names); + return *this; + } + + /** @brief Specifies the inference batch size. + + The function is used to specify inference batch size. + Follow https://docs.openvinotoolkit.org/latest/classInferenceEngine_1_1CNNNetwork.html#a8e9d19270a48aab50cb5b1c43eecb8e9 for additional information + + @param size batch size which will be used. + @return reference to this parameter structure. + */ + Params& cfgBatchSize(const size_t size) { + desc.batch_size = cv::util::make_optional(size); + return *this; + } + + Params& cfgPreprocessingParams(const cv::gapi::wip::onevpl::Device &device, + const cv::gapi::wip::onevpl::Context &ctx) { + desc.vpl_preproc_device = cv::util::make_optional(device); + desc.vpl_preproc_ctx = cv::util::make_optional(ctx); + return *this; + } + + /** @brief Specifies which api will be used to run inference. + + The function is used to specify mode for OpenVINO inference. + OpenVINO has two options to run inference: + 1. Asynchronous (using StartAsync: https://docs.openvino.ai/latest/classInferenceEngine_1_1InferRequest.html#doxid-class-inference-engine-1-1-infer-request-1a405293e8423d82a5b45f642a3bef0d24) + 2. Synchronous (using Infer: https://docs.openvino.ai/latest/classInferenceEngine_1_1InferRequest.html#doxid-class-inference-engine-1-1-infer-request-1a3391ce30894abde730523e9ca9371ce8) + By default asynchronous mode is used. + + @param mode Inference mode which will be used. + @return reference to this parameter structure. + */ + Params& cfgInferMode(InferMode mode) { + desc.mode = mode; + return *this; + } + + /** @brief Specifies the output precision for model. + + The function is used to set an output precision for model. + + @param precision Precision in OpenCV format (CV_8U, CV_32F, ...) + will be applied to all output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputPrecision(detail::ParamDesc::PrecisionT precision) { + desc.output_precision = precision; + return *this; + } + + /** @overload + + @param precision_map Map of pairs: name of corresponding output layer + and its precision in OpenCV format (CV_8U, CV_32F, ...) + @return reference to this parameter structure. + */ + Params& + cfgOutputPrecision(detail::ParamDesc::PrecisionMapT precision_map) { + desc.output_precision = precision_map; + return *this; + } + + /** @brief Specifies the input layout for model. + + The function is used to set an input layout for model. + + @param layout Layout in string representation ("NCHW", "NHWC", etc) + will be applied to all input layers. + @return reference to this parameter structure. + */ + Params& cfgInputLayout(std::string layout) { + desc.input_layout = std::move(layout); + return *this; + } + + /** @overload + + @param layout_map Map of pairs: name of corresponding input layer + and its layout in string representation ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgInputLayout(detail::AttrMap layout_map) { + desc.input_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies the output layout for model. + + The function is used to set an output layout for model. + + @param layout Layout in string representation ("NCHW", "NHWC", etc) + will be applied to all output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputLayout(std::string layout) { + desc.output_layout = std::move(layout); + return *this; + } + + /** @overload + + @param layout_map Map of pairs: name of corresponding output layer + and its layout in string representation ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgOutputLayout(detail::AttrMap layout_map) { + desc.output_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies resize interpolation algorithm. + * + The function is used to configure resize preprocessing for input layer. + + @param interpolation Resize interpolation algorithm. + Supported algorithms: #INTER_LINEAR, #INTER_AREA. + @return reference to this parameter structure. + */ + Params& cfgResize(int interpolation) { + desc.interpolation = interpolation; + return *this; + } + + /** @overload + + @param interpolation Map of pairs: name of corresponding input layer + and its resize algorithm. + @return reference to this parameter structure. + */ + Params& cfgResize(detail::AttrMap interpolation) { + desc.interpolation = std::move(interpolation); + return *this; + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::ie::backend(); } + std::string tag() const { return Net::tag(); } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) + +protected: + detail::ParamDesc desc; +}; + +/* +* @brief This structure provides functions for generic network type that +* fill inference parameters. +* @see struct Generic +*/ +template<> +class Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and sets default values for other + inference description parameters. Model is loaded and compiled using OpenVINO Toolkit. + + @param tag string tag of the network for which these parameters are intended. + @param model path to topology IR (.xml file). + @param weights path to weights (.bin file). + @param device target device to use. + */ + Params(const std::string &tag, + const std::string &model, + const std::string &weights, + const std::string &device) + : desc{ model, weights, device, {}, {}, {}, 0u, 0u, + detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u, + {}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} }, + m_tag(tag) { + } + + /** @overload + + This constructor for pre-compiled networks. Model is imported from pre-compiled + blob. + + @param tag string tag of the network for which these parameters are intended. + @param model path to model. + @param device target device to use. + */ + Params(const std::string &tag, + const std::string &model, + const std::string &device) + : desc{ model, {}, device, {}, {}, {}, 0u, 0u, + detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u, + {}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} }, + m_tag(tag) { + } + + /** @see ie::Params::pluginConfig. */ + Params& pluginConfig(const IEConfig& cfg) { + desc.config = cfg; + return *this; + } + + /** @overload */ + Params& pluginConfig(IEConfig&& cfg) { + desc.config = std::move(cfg); + return *this; + } + + /** @see ie::Params::constInput. */ + Params& constInput(const std::string &layer_name, + const cv::Mat &data, + TraitAs hint = TraitAs::TENSOR) { + desc.const_inputs[layer_name] = {data, hint}; + return *this; + } + + /** @see ie::Params::cfgNumRequests. */ + Params& cfgNumRequests(size_t nireq) { + GAPI_Assert(nireq > 0 && "Number of infer requests must be greater than zero!"); + desc.nireq = nireq; + return *this; + } + + /** @see ie::Params::cfgInputReshape */ + Params& cfgInputReshape(const std::map>&reshape_table) { + desc.reshape_table = reshape_table; + return *this; + } + + /** @overload */ + Params& cfgInputReshape(std::map> && reshape_table) { + desc.reshape_table = std::move(reshape_table); + return *this; + } + + /** @overload */ + Params& cfgInputReshape(std::string && layer_name, std::vector && layer_dims) { + desc.reshape_table.emplace(layer_name, layer_dims); + return *this; + } + + /** @overload */ + Params& cfgInputReshape(const std::string & layer_name, const std::vector&layer_dims) { + desc.reshape_table.emplace(layer_name, layer_dims); + return *this; + } + + /** @overload */ + Params& cfgInputReshape(std::unordered_set && layer_names) { + desc.layer_names_to_reshape = std::move(layer_names); + return *this; + } + + /** @overload */ + Params& cfgInputReshape(const std::unordered_set&layer_names) { + desc.layer_names_to_reshape = layer_names; + return *this; + } + + /** @see ie::Params::cfgBatchSize */ + Params& cfgBatchSize(const size_t size) { + desc.batch_size = cv::util::make_optional(size); + return *this; + } + + /** @see ie::Params::cfgInferAPI */ + Params& cfgInferMode(InferMode mode) { + desc.mode = mode; + return *this; + } + + /** @see ie::Params::cfgOutputPrecision */ + Params& cfgOutputPrecision(detail::ParamDesc::PrecisionT precision) { + desc.output_precision = precision; + return *this; + } + + /** @overload */ + Params& + cfgOutputPrecision(detail::ParamDesc::PrecisionMapT precision_map) { + desc.output_precision = precision_map; + return *this; + } + + /** @see ie::Params::cfgInputLayout */ + Params& cfgInputLayout(std::string layout) { + desc.input_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgInputLayout(detail::AttrMap layout_map) { + desc.input_layout = std::move(layout_map); + return *this; + } + + /** @see ie::Params::cfgOutputLayout */ + Params& cfgOutputLayout(std::string layout) { + desc.output_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgOutputLayout(detail::AttrMap layout_map) { + desc.output_layout = std::move(layout_map); + return *this; + } + + /** @see ie::Params::cfgResize */ + Params& cfgResize(int interpolation) { + desc.interpolation = interpolation; + return *this; + } + + /** @overload */ + Params& cfgResize(detail::AttrMap interpolation) { + desc.interpolation = std::move(interpolation); + return *this; + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::ie::backend(); } + std::string tag() const { return m_tag; } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) + +protected: + detail::ParamDesc desc; + std::string m_tag; +}; + +} // namespace ie +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_IE_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp new file mode 100644 index 00000000000..eb6316b4468 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp @@ -0,0 +1,759 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020-2021 Intel Corporation + +#ifndef OPENCV_GAPI_INFER_ONNX_HPP +#define OPENCV_GAPI_INFER_ONNX_HPP + +#include +#include +#include +#include // tuple, tuple_size +#include + +#include +#include +#include + +#include // GAPI_EXPORTS +#include // GKernelPackage +#include // Generic + +namespace cv { +namespace gapi { + +/** + * @brief This namespace contains G-API ONNX Runtime backend functions, structures, and symbols. + */ +namespace onnx { + +/** + * @brief This namespace contains Execution Providers structures for G-API ONNX Runtime backend. + */ +namespace ep { + +/** + * @brief This structure provides functions + * that fill inference options for ONNX CoreML Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml-execution-provider + */ +struct GAPI_EXPORTS_W_SIMPLE CoreML { + /** @brief Class constructor. + + Constructs CoreML parameters. + + */ + GAPI_WRAP + CoreML() = default; + + /** @brief Limit CoreML Execution Provider to run on CPU only. + + This function is used to limit CoreML to run on CPU only. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_use_cpu_only + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgUseCPUOnly() { + use_cpu_only = true; + return *this; + } + + /** @brief Enable CoreML EP to run on a subgraph in the body of a control flow ONNX operator (i.e. a Loop, Scan or If operator). + + This function is used to enable CoreML EP to run on + a subgraph of a control flow of ONNX operation. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_enable_on_subgraph + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgEnableOnSubgraph() { + enable_on_subgraph = true; + return *this; + } + + /** @brief Enable CoreML EP to run only on Apple Neural Engine. + + This function is used to enable CoreML EP to run only on Apple Neural Engine. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_only_enable_device_with_ane + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgEnableOnlyNeuralEngine() { + enable_only_ane = true; + return *this; + } + + bool use_cpu_only = false; + bool enable_on_subgraph = false; + bool enable_only_ane = false; +}; + +/** + * @brief This structure provides functions + * that fill inference options for CUDA Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html#cuda-execution-provider + */ +struct GAPI_EXPORTS_W_SIMPLE CUDA { + // NB: Used from python. + /// @private -- Exclude this constructor from OpenCV documentation + GAPI_WRAP + CUDA() = default; + + /** @brief Class constructor. + + Constructs CUDA parameters based on device type information. + + @param dev_id Target device id to use. + */ + GAPI_WRAP + explicit CUDA(const int dev_id) + : device_id(dev_id) { + } + + int device_id; +}; + +/** + * @brief This structure provides functions + * that fill inference options for TensorRT Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/TensorRT-ExecutionProvider.html#tensorrt-execution-provider + */ +struct GAPI_EXPORTS_W_SIMPLE TensorRT { + // NB: Used from python. + /// @private -- Exclude this constructor from OpenCV documentation + GAPI_WRAP + TensorRT() = default; + + /** @brief Class constructor. + + Constructs TensorRT parameters based on device type information. + + @param dev_id Target device id to use. + */ + GAPI_WRAP + explicit TensorRT(const int dev_id) + : device_id(dev_id) { + } + + int device_id; +}; + +/** + * @brief This structure provides functions + * that fill inference options for ONNX OpenVINO Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html#summary-of-options + */ +struct GAPI_EXPORTS_W_SIMPLE OpenVINO { + // NB: Used from python. + /// @private -- Exclude this constructor from OpenCV documentation + GAPI_WRAP + OpenVINO() = default; + + /** @brief Class constructor. + + Constructs OpenVINO parameters based on device type information. + + @param dev_type Target device type to use. ("CPU", "GPU", "GPU.0" etc) + */ + GAPI_WRAP + explicit OpenVINO(const std::string &dev_type) + : device_type(dev_type) { + } + + /** @brief Class constructor. + + Constructs OpenVINO parameters based on map of options passed. + + * @param params A map of parameter names and their corresponding string values. + */ + GAPI_WRAP + explicit OpenVINO(const std::map& params) + : params_map(params) { + } + + /** @brief Specifies OpenVINO Execution Provider cache dir. + + This function is used to explicitly specify the path to save and load + the blobs enabling model caching feature. + + @param dir Path to the directory what will be used as cache. + @return reference to this parameter structure. + */ + GAPI_WRAP + OpenVINO& cfgCacheDir(const std::string &dir) { + if (!params_map.empty()) { + cv::util::throw_error(std::logic_error("ep::OpenVINO cannot be changed if" + "created from the parameters map.")); + } + cache_dir = dir; + return *this; + } + + /** @brief Specifies OpenVINO Execution Provider number of threads. + + This function is used to override the accelerator default value + of number of threads with this value at runtime. + + @param nthreads Number of threads. + @return reference to this parameter structure. + */ + GAPI_WRAP + OpenVINO& cfgNumThreads(size_t nthreads) { + if (!params_map.empty()) { + cv::util::throw_error(std::logic_error("ep::OpenVINO cannot be changed if" + "created from the parameters map.")); + } + num_of_threads = nthreads; + return *this; + } + + /** @brief Enables OpenVINO Execution Provider opencl throttling. + + This function is used to enable OpenCL queue throttling for GPU devices + (reduces CPU utilization when using GPU). + + @return reference to this parameter structure. + */ + GAPI_WRAP + OpenVINO& cfgEnableOpenCLThrottling() { + if (!params_map.empty()) { + cv::util::throw_error(std::logic_error("ep::OpenVINO cannot be changed if" + "created from the parameters map.")); + } + enable_opencl_throttling = true; + return *this; + } + + /** @brief Enables OpenVINO Execution Provider dynamic shapes. + + This function is used to enable OpenCL queue throttling for GPU devices + (reduces CPU utilization when using GPU). + This function is used to enable work with dynamic shaped models + whose shape will be set dynamically based on the infer input + image/data shape at run time in CPU. + + @return reference to this parameter structure. + */ + GAPI_WRAP + OpenVINO& cfgEnableDynamicShapes() { + if (!params_map.empty()) { + cv::util::throw_error(std::logic_error("ep::OpenVINO cannot be changed if" + "created from the parameters map.")); + } + enable_dynamic_shapes = true; + return *this; + } + + std::string device_type; + std::string cache_dir; + size_t num_of_threads = 0; + bool enable_opencl_throttling = false; + bool enable_dynamic_shapes = false; + std::map params_map; +}; + +/** + * @brief This structure provides functions + * that fill inference options for ONNX DirectML Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html#directml-execution-provider + */ +class GAPI_EXPORTS_W_SIMPLE DirectML { +public: + // NB: Used from python. + /// @private -- Exclude this constructor from OpenCV documentation + GAPI_WRAP + DirectML() = default; + + /** @brief Class constructor. + + Constructs DirectML parameters based on device id. + + @param device_id Target device id to use. ("0", "1", etc) + */ + GAPI_WRAP + explicit DirectML(const int device_id) : ddesc(device_id) { }; + + /** @brief Class constructor. + + Constructs DirectML parameters based on adapter name. + + @param adapter_name Target adapter_name to use. + */ + GAPI_WRAP + explicit DirectML(const std::string &adapter_name) : ddesc(adapter_name) { }; + + using DeviceDesc = cv::util::variant; + DeviceDesc ddesc; +}; + +using EP = cv::util::variant< cv::util::monostate + , OpenVINO + , DirectML + , CoreML + , CUDA + , TensorRT>; + +} // namespace ep + +GAPI_EXPORTS cv::gapi::GBackend backend(); + +enum class TraitAs: int { + TENSOR, //!< G-API traits an associated cv::Mat as a raw tensor + // and passes dimensions as-is + IMAGE //!< G-API traits an associated cv::Mat as an image so + // creates an "image" blob (NCHW/NHWC, etc) +}; + +using PostProc = std::function &, + std::unordered_map &)>; + +namespace detail { +/** +* @brief This structure contains description of inference parameters +* which is specific to ONNX models. +*/ +struct ParamDesc { + std::string model_path; //!< Path to model. + + // NB: nun_* may differ from topology's real input/output port numbers + // (e.g. topology's partial execution) + std::size_t num_in; //!< How many inputs are defined in the operation + std::size_t num_out; //!< How many outputs are defined in the operation + + // NB: Here order follows the `Net` API + std::vector input_names; //!< Names of input network layers. + std::vector output_names; //!< Names of output network layers. + + using ConstInput = std::pair; + std::unordered_map const_inputs; //!< Map with pair of name of network layer and ConstInput which will be associated with this. + + std::vector mean; //!< Mean values for preprocessing. + std::vector stdev; //!< Standard deviation values for preprocessing. + + std::vector out_metas; //!< Out meta information about your output (type, dimension). + PostProc custom_post_proc; //!< Post processing function. + + std::vector normalize; //!< Vector of bool values that enabled or disabled normalize of input data. + + std::vector names_to_remap; //!< Names of output layers that will be processed in PostProc function. + + bool is_generic; + + // TODO: Needs to modify the rest of ParamDesc accordingly to support + // both generic and non-generic options without duplication + // (as it was done for the OV IE backend) + // These values are pushed into the respective vector<> fields above + // when the generic infer parameters are unpacked (see GONNXBackendImpl::unpackKernel) + std::unordered_map > generic_mstd; + std::unordered_map generic_norm; + + std::map session_options; + std::vector execution_providers; + bool disable_mem_pattern; + cv::util::optional opt_level; +}; +} // namespace detail + +template +struct PortCfg { + using In = std::array + < std::string + , std::tuple_size::value >; + using Out = std::array + < std::string + , std::tuple_size::value >; + using NormCoefs = std::array + < cv::Scalar + , std::tuple_size::value >; + using Normalize = std::array + < bool + , std::tuple_size::value >; +}; + +/** + * Contains description of inference parameters and kit of functions that + * fill this parameters. + */ +template class Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and sets default values for other + inference description parameters. + + @param model Path to model (.onnx file). + */ + Params(const std::string &model) { + desc.model_path = model; + desc.num_in = std::tuple_size::value; + desc.num_out = std::tuple_size::value; + desc.is_generic = false; + desc.disable_mem_pattern = false; + } + + /** @brief Specifies sequence of network input layers names for inference. + + The function is used to associate data of graph inputs with input layers of + network topology. Number of names has to match the number of network inputs. If a network + has only one input layer, there is no need to call it as the layer is + associated with input automatically but this doesn't prevent you from + doing it yourself. Count of names has to match to number of network inputs. + + @param layer_names std::array where N is the number of inputs + as defined in the @ref G_API_NET. Contains names of input layers. + @return the reference on modified object. + */ + Params& cfgInputLayers(const typename PortCfg::In &layer_names) { + desc.input_names.assign(layer_names.begin(), layer_names.end()); + return *this; + } + + /** @brief Specifies sequence of output layers names for inference. + + The function is used to associate data of graph outputs with output layers of + network topology. If a network has only one output layer, there is no need to call it + as the layer is associated with output automatically but this doesn't prevent + you from doing it yourself. Count of names has to match to number of network + outputs or you can set your own output but for this case you have to + additionally use @ref cfgPostProc function. + + @param layer_names std::array where N is the number of outputs + as defined in the @ref G_API_NET. Contains names of output layers. + @return the reference on modified object. + */ + Params& cfgOutputLayers(const typename PortCfg::Out &layer_names) { + desc.output_names.assign(layer_names.begin(), layer_names.end()); + return *this; + } + + /** @brief Sets a constant input. + + The function is used to set constant input. This input has to be + a prepared tensor since preprocessing is disabled for this case. You should + provide name of network layer which will receive provided data. + + @param layer_name Name of network layer. + @param data cv::Mat that contains data which will be associated with network layer. + @param hint Type of input (TENSOR). + @return the reference on modified object. + */ + Params& constInput(const std::string &layer_name, + const cv::Mat &data, + TraitAs hint = TraitAs::TENSOR) { + desc.const_inputs[layer_name] = {data, hint}; + return *this; + } + + /** @brief Specifies mean value and standard deviation for preprocessing. + + The function is used to set mean value and standard deviation for preprocessing + of input data. + + @param m std::array where N is the number of inputs + as defined in the @ref G_API_NET. Contains mean values. + @param s std::array where N is the number of inputs + as defined in the @ref G_API_NET. Contains standard deviation values. + @return the reference on modified object. + */ + Params& cfgMeanStd(const typename PortCfg::NormCoefs &m, + const typename PortCfg::NormCoefs &s) { + desc.mean.assign(m.begin(), m.end()); + desc.stdev.assign(s.begin(), s.end()); + return *this; + } + + /** @brief Configures graph output and provides the post processing function from user. + + The function is used when you work with networks with dynamic outputs. + Since we can't know dimensions of inference result needs provide them for + construction of graph output. This dimensions can differ from inference result. + So you have to provide @ref PostProc function that gets information from inference + result and fill output which is constructed by dimensions from out_metas. + + @param out_metas Out meta information about your output (type, dimension). + @param remap_function Post processing function, which has two parameters. First is onnx + result, second is graph output. Both parameters is std::map that contain pair of + layer's name and cv::Mat. + @return the reference on modified object. + */ + Params& cfgPostProc(const std::vector &out_metas, + const PostProc &remap_function) { + desc.out_metas = out_metas; + desc.custom_post_proc = remap_function; + return *this; + } + + /** @overload + Function with a rvalue parameters. + + @param out_metas rvalue out meta information about your output (type, dimension). + @param remap_function rvalue post processing function, which has two parameters. First is onnx + result, second is graph output. Both parameters is std::map that contain pair of + layer's name and cv::Mat. + @return the reference on modified object. + */ + Params& cfgPostProc(std::vector &&out_metas, + PostProc &&remap_function) { + desc.out_metas = std::move(out_metas); + desc.custom_post_proc = std::move(remap_function); + return *this; + } + + /** @overload + The function has additional parameter names_to_remap. This parameter provides + information about output layers which will be used for inference and post + processing function. + + @param out_metas Out meta information. + @param remap_function Post processing function. + @param names_to_remap Names of output layers. network's inference will + be done on these layers. Inference's result will be processed in post processing + function using these names. + @return the reference on modified object. + */ + Params& cfgPostProc(const std::vector &out_metas, + const PostProc &remap_function, + const std::vector &names_to_remap) { + desc.out_metas = out_metas; + desc.custom_post_proc = remap_function; + desc.names_to_remap = names_to_remap; + return *this; + } + + /** @overload + Function with a rvalue parameters and additional parameter names_to_remap. + + @param out_metas rvalue out meta information. + @param remap_function rvalue post processing function. + @param names_to_remap rvalue names of output layers. network's inference will + be done on these layers. Inference's result will be processed in post processing + function using these names. + @return the reference on modified object. + */ + Params& cfgPostProc(std::vector &&out_metas, + PostProc &&remap_function, + std::vector &&names_to_remap) { + desc.out_metas = std::move(out_metas); + desc.custom_post_proc = std::move(remap_function); + desc.names_to_remap = std::move(names_to_remap); + return *this; + } + + /** @brief Specifies normalize parameter for preprocessing. + + The function is used to set normalize parameter for preprocessing of input data. + + @param normalizations std::array where N is the number of inputs + as defined in the @ref G_API_NET. Сontains bool values that enabled or disabled + normalize of input data. + @return the reference on modified object. + */ + Params& cfgNormalize(const typename PortCfg::Normalize &normalizations) { + desc.normalize.assign(normalizations.begin(), normalizations.end()); + return *this; + } + + /** @brief Adds execution provider for runtime. + + The function is used to add ONNX Runtime OpenVINO Execution Provider options. + + @param ep OpenVINO Execution Provider options. + @see cv::gapi::onnx::ep::OpenVINO. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::OpenVINO&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Adds execution provider for runtime. + + The function is used to add ONNX Runtime DirectML Execution Provider options. + + @param ep DirectML Execution Provider options. + @see cv::gapi::onnx::ep::DirectML. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::DirectML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Adds execution provider for runtime. + + The function is used to add ONNX Runtime CoreML Execution Provider options. + + @param ep CoreML Execution Provider options. + @see cv::gapi::onnx::ep::CoreML. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::CoreML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Adds execution provider for runtime. + + The function is used to add ONNX Runtime CUDA Execution Provider options. + + @param ep CUDA Execution Provider options. + @see cv::gapi::onnx::ep::CUDA. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::CUDA&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Adds execution provider for runtime. + + The function is used to add ONNX Runtime TensorRT Execution Provider options. + + @param ep TensorRT Execution Provider options. + @see cv::gapi::onnx::ep::TensorRT. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::TensorRT&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Disables the memory pattern optimization. + + @return the reference on modified object. + */ + Params& cfgDisableMemPattern() { + desc.disable_mem_pattern = true; + return *this; + } + + /** @brief Configures session options for ONNX Runtime. + + This function is used to set various session options for the ONNX Runtime + session by accepting a map of key-value pairs. + + @param options A map of session option to be applied to the ONNX Runtime session. + @return the reference on modified object. + */ + Params& cfgSessionOptions(const std::map& options) { + desc.session_options.insert(options.begin(), options.end()); + return *this; + } + + /** @brief Configures optimization level for ONNX Runtime. + + @param opt_level [optimization level]: Valid values are 0 (disable), 1 (basic), 2 (extended), 99 (all). + Please see onnxruntime_c_api.h (enum GraphOptimizationLevel) for the full list of all optimization levels. + @return the reference on modified object. + */ + Params& cfgOptLevel(const int opt_level) { + desc.opt_level = cv::util::make_optional(opt_level); + return *this; + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::onnx::backend(); } + std::string tag() const { return Net::tag(); } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) + +protected: + detail::ParamDesc desc; +}; + +/* +* @brief This structure provides functions for generic network type that +* fill inference parameters. +* @see struct Generic +*/ +template<> +class Params { +public: + /** @brief Class constructor. + + Constructs Params based on input information and sets default values for other + inference description parameters. + + @param tag string tag of the network for which these parameters are intended. + @param model_path path to model file (.onnx file). + */ + Params(const std::string& tag, const std::string& model_path) + : desc{ model_path, 0u, 0u, {}, {}, {}, {}, {}, {}, {}, {}, {}, true, {}, {}, {}, {}, false, {} }, m_tag(tag) {} + + /** @see onnx::Params::cfgMeanStdDev. */ + void cfgMeanStdDev(const std::string &layer, + const cv::Scalar &m, + const cv::Scalar &s) { + desc.generic_mstd[layer] = std::make_pair(m, s); + } + + /** @see onnx::Params::cfgNormalize. */ + void cfgNormalize(const std::string &layer, bool flag) { + desc.generic_norm[layer] = flag; + } + + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::OpenVINO&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::DirectML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::CoreML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::CUDA&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::TensorRT&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + + /** @see onnx::Params::cfgDisableMemPattern. */ + void cfgDisableMemPattern() { + desc.disable_mem_pattern = true; + } + + /** @see onnx::Params::cfgSessionOptions. */ + void cfgSessionOptions(const std::map& options) { + desc.session_options.insert(options.begin(), options.end()); + } + +/** @see onnx::Params::cfgOptLevel. */ + void cfgOptLevel(const int opt_level) { + desc.opt_level = cv::util::make_optional(opt_level); + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::onnx::backend(); } + std::string tag() const { return m_tag; } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) +protected: + detail::ParamDesc desc; + std::string m_tag; +}; + +} // namespace onnx +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_INFER_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/ov.hpp b/modules/gapi/include/opencv2/gapi/infer/ov.hpp new file mode 100644 index 00000000000..782792489ba --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/ov.hpp @@ -0,0 +1,709 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_INFER_OV_HPP +#define OPENCV_GAPI_INFER_OV_HPP + +#include + +#include +#include // GAPI_EXPORTS +#include // GKernelType[M], GBackend +#include // Generic + +#include + +namespace cv { +namespace gapi { + +/** + * @brief This namespace contains G-API OpenVINO 2.0 backend functions, + * structures, and symbols. + */ +namespace ov { + +GAPI_EXPORTS cv::gapi::GBackend backend(); + +namespace detail { + +template +using AttrMap = std::map; +// NB: This type is supposed to be used to hold in/out layers +// attributes such as precision, layout, shape etc. +// +// User can provide attributes either: +// 1. cv::util::monostate - No value specified explicitly. +// 2. Attr - value specified explicitly that should be broadcasted to all layers. +// 3. AttrMap[str->T] - map specifies value for particular layer. +template +using LayerVariantAttr = cv::util::variant< cv::util::monostate + , AttrMap + , Attr>; + +struct ParamDesc { + struct Model { + + Model(const std::string &model_path_, + const std::string &bin_path_) + : model_path(model_path_), bin_path(bin_path_) { + } + + std::string model_path; + std::string bin_path; + + LayerVariantAttr input_tensor_layout; + LayerVariantAttr input_model_layout; + LayerVariantAttr output_tensor_layout; + LayerVariantAttr output_model_layout; + LayerVariantAttr output_tensor_precision; + + LayerVariantAttr> new_shapes; + + LayerVariantAttr> mean_values; + LayerVariantAttr> scale_values; + + LayerVariantAttr interpolation; + }; + + struct CompiledModel { + std::string blob_path; + }; + + using Kind = cv::util::variant; + + ParamDesc(Kind &&kind_, + const std::string &device_, + const bool is_generic_, + const size_t num_in_, + const size_t num_out_) + : kind(std::move(kind_)), device(device_), + is_generic(is_generic_), + num_in(num_in_), num_out(num_out_) { + } + + Kind kind; + + std::string device; + bool is_generic; + + std::size_t num_in; + std::size_t num_out; + + std::vector input_names; + std::vector output_names; + + using PluginConfigT = std::map; + PluginConfigT config; + + size_t nireq = 1; +}; + +// NB: Just helper to avoid code duplication. +static detail::ParamDesc::Model& +getModelToSetAttrOrThrow(detail::ParamDesc::Kind &kind, + const std::string &attr_name) { + if (cv::util::holds_alternative(kind)) { + cv::util::throw_error( + std::logic_error("Specifying " + attr_name + " isn't" + " possible for compiled model.")); + } + GAPI_Assert(cv::util::holds_alternative(kind)); + return cv::util::get(kind); +} + +} // namespace detail + +/** + * @brief This structure provides functions + * that fill inference parameters for "OpenVINO Toolkit" model. + */ +template struct Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and specifies default values for other + inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit". + + @param model_path Path to a model. + @param bin_path Path to a data file. + For IR format (*.bin): + If path is empty, will try to read a bin file with the same name as xml. + If the bin file with the same name is not found, will load IR without weights. + For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used. + @param device target device to use. + */ + Params(const std::string &model_path, + const std::string &bin_path, + const std::string &device) + : m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}} + , device + , false /* is generic */ + , std::tuple_size::value + , std::tuple_size::value) { + } + + /** @overload + Use this constructor to work with pre-compiled network. + Model is imported from a pre-compiled blob. + + @param blob_path path to the compiled model (*.blob). + @param device target device to use. + */ + Params(const std::string &blob_path, + const std::string &device) + : m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}} + , device + , false /* is generic */ + , std::tuple_size::value + , std::tuple_size::value) { + } + + /** @brief Specifies sequence of network input layers names for inference. + + The function is used to associate cv::gapi::infer<> inputs with the model inputs. + Number of names has to match the number of network inputs as defined in G_API_NET(). + In case a network has only single input layer, there is no need to specify name manually. + + @param layer_names std::array where N is the number of inputs + as defined in the @ref G_API_NET. Contains names of input layers. + @return reference to this parameter structure. + */ + Params& cfgInputLayers(const std::vector &layer_names) { + m_desc.input_names = layer_names; + return *this; + } + + /** @brief Specifies sequence of network output layers names for inference. + + The function is used to associate cv::gapi::infer<> outputs with the model outputs. + Number of names has to match the number of network outputs as defined in G_API_NET(). + In case a network has only single output layer, there is no need to specify name manually. + + @param layer_names std::array where N is the number of outputs + as defined in the @ref G_API_NET. Contains names of output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputLayers(const std::vector &layer_names) { + m_desc.output_names = layer_names; + return *this; + } + + /** @brief Specifies OpenVINO plugin configuration. + + The function is used to set configuration for OpenVINO plugin. Some parameters + can be different for each plugin. Please follow https://docs.openvinotoolkit.org/latest/index.html + to check information about specific plugin. + + @param config Map of pairs: (config parameter name, config parameter value). + @return reference to this parameter structure. + */ + Params& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) { + m_desc.config = config; + return *this; + } + + /** @brief Specifies tensor layout for an input layer. + + The function is used to set tensor layout for an input layer. + + @param layout Tensor layout ("NCHW", "NWHC", etc) + will be applied to all input layers. + @return reference to this parameter structure. + */ + Params& cfgInputTensorLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout") + .input_tensor_layout = std::move(layout); + return *this; + } + + /** @overload + @param layout_map Map of pairs: name of corresponding input layer + and its tensor layout represented in std::string ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgInputTensorLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout") + .input_tensor_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies model layout for an input layer. + + The function is used to set model layout for an input layer. + + @param layout Model layout ("NCHW", "NHWC", etc) + will be applied to all input layers. + @return reference to this parameter structure. + */ + Params& cfgInputModelLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout") + .input_model_layout = std::move(layout); + return *this; + } + + /** @overload + @param layout_map Map of pairs: name of corresponding input layer + and its model layout ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgInputModelLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout") + .input_model_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies tensor layout for an output layer. + + The function is used to set tensor layout for an output layer. + + @param layout Tensor layout ("NCHW", "NWHC", etc) + will be applied to all output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputTensorLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout") + .output_tensor_layout = std::move(layout); + return *this; + } + + /** @overload + @param layout_map Map of pairs: name of corresponding output layer + and its tensor layout represented in std::string ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgOutputTensorLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout") + .output_tensor_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies model layout for an output layer. + + The function is used to set model layout for an output layer. + + @param layout Model layout ("NCHW", "NHWC", etc) + will be applied to all output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputModelLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout") + .output_model_layout = std::move(layout); + return *this; + } + + /** @overload + @param layout_map Map of pairs: name of corresponding output layer + and its model layout ("NCHW", "NHWC", etc) + @return reference to this parameter structure. + */ + Params& + cfgOutputModelLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout") + .output_model_layout = std::move(layout_map); + return *this; + } + + /** @brief Specifies tensor precision for an output layer. + + The function is used to set tensor precision for an output layer.. + + @param precision Precision in OpenCV format (CV_8U, CV_32F, ...) + will be applied to all output layers. + @return reference to this parameter structure. + */ + Params& cfgOutputTensorPrecision(int precision) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision") + .output_tensor_precision = precision; + return *this; + } + + /** @overload + + @param precision_map Map of pairs: name of corresponding output layer + and its precision in OpenCV format (CV_8U, CV_32F, ...) + @return reference to this parameter structure. + */ + Params& + cfgOutputTensorPrecision(detail::AttrMap precision_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision") + .output_tensor_precision = std::move(precision_map); + return *this; + } + + /** @brief Specifies the new shape for input layers. + + The function is used to set new shape for input layers. + + @param new_shape New shape will be applied to all input layers. + @return reference to this parameter structure. + */ + Params& + cfgReshape(std::vector new_shape) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") + .new_shapes = std::move(new_shape); + return *this; + } + + /** @overload + + @param new_shape_map Map of pairs: name of corresponding output layer + and its new shape. + @return reference to this parameter structure. + */ + Params& + cfgReshape(detail::AttrMap> new_shape_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") + .new_shapes = std::move(new_shape_map); + return *this; + } + + /** @brief Specifies number of asynchronous inference requests. + + @param nireq Number of inference asynchronous requests. + @return reference to this parameter structure. + */ + Params& cfgNumRequests(const size_t nireq) { + if (nireq == 0) { + cv::util::throw_error( + std::logic_error("Number of inference requests" + " must be greater than zero.")); + } + m_desc.nireq = nireq; + return *this; + } + + /** @brief Specifies mean values for preprocessing. + * + The function is used to set mean values for input layer preprocessing. + + @param mean_values Float vector contains mean values + @return reference to this parameter structure. + */ + Params& cfgMean(std::vector mean_values) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values") + .mean_values = std::move(mean_values); + return *this; + } + + /** @overload + + @param mean_map Map of pairs: name of corresponding input layer + and its mean values. + @return reference to this parameter structure. + */ + Params& cfgMean(detail::AttrMap> mean_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values") + .mean_values = std::move(mean_map); + return *this; + } + + /** @brief Specifies scale values for preprocessing. + * + The function is used to set scale values for input layer preprocessing. + + @param scale_values Float vector contains scale values + @return reference to this parameter structure. + */ + Params& cfgScale(std::vector scale_values) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values") + .scale_values = std::move(scale_values); + return *this; + } + + /** @overload + + @param scale_map Map of pairs: name of corresponding input layer + and its mean values. + @return reference to this parameter structure. + */ + Params& cfgScale(detail::AttrMap> scale_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values") + .scale_values = std::move(scale_map); + return *this; + } + + /** @brief Specifies resize interpolation algorithm. + * + The function is used to configure resize preprocessing for input layer. + + @param interpolation Resize interpolation algorithm. + Supported algorithms: #INTER_NEAREST, #INTER_LINEAR, #INTER_CUBIC. + @return reference to this parameter structure. + */ + Params& cfgResize(int interpolation) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing") + .interpolation = std::move(interpolation); + return *this; + } + + /** @overload + + @param interpolation Map of pairs: name of corresponding input layer + and its resize algorithm. + @return reference to this parameter structure. + */ + Params& cfgResize(detail::AttrMap interpolation) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing") + .interpolation = std::move(interpolation); + return *this; + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::ov::backend(); } + std::string tag() const { return Net::tag(); } + cv::util::any params() const { return { m_desc }; } + // END(G-API's network parametrization API) + +protected: + detail::ParamDesc m_desc; +}; + +/* +* @brief This structure provides functions for generic network type that +* fill inference parameters. +* @see struct Generic +*/ +template<> +class Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and specifies default values for other + inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit". + + @param tag string tag of the network for which these parameters are intended. + @param model_path Path to a model. + @param bin_path Path to a data file. + For IR format (*.bin): + If path is empty, will try to read a bin file with the same name as xml. + If the bin file with the same name is not found, will load IR without weights. + For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used. + @param device target device to use. + */ + Params(const std::string &tag, + const std::string &model_path, + const std::string &bin_path, + const std::string &device) + : m_tag(tag), + m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}} + , device + , true /* is generic */ + , 0u + , 0u) { + } + + /** @overload + + This constructor for pre-compiled networks. Model is imported from pre-compiled + blob. + + @param tag string tag of the network for which these parameters are intended. + @param blob_path path to the compiled model (*.blob). + @param device target device to use. + */ + Params(const std::string &tag, + const std::string &blob_path, + const std::string &device) + : m_tag(tag), + m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}} + , device + , true /* is generic */ + , 0u + , 0u) { + } + + /** @see ov::Params::cfgPluginConfig. */ + Params& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) { + m_desc.config = config; + return *this; + } + + /** @see ov::Params::cfgInputTensorLayout. */ + Params& cfgInputTensorLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout") + .input_tensor_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgInputTensorLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout") + .input_tensor_layout = std::move(layout_map); + return *this; + } + + /** @see ov::Params::cfgInputModelLayout. */ + Params& cfgInputModelLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout") + .input_model_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgInputModelLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout") + .input_model_layout = std::move(layout_map); + return *this; + } + + /** @see ov::Params::cfgOutputTensorLayout. */ + Params& cfgOutputTensorLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout") + .output_tensor_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgOutputTensorLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout") + .output_tensor_layout = std::move(layout_map); + return *this; + } + + /** @see ov::Params::cfgOutputModelLayout. */ + Params& cfgOutputModelLayout(std::string layout) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout") + .output_model_layout = std::move(layout); + return *this; + } + + /** @overload */ + Params& + cfgOutputModelLayout(detail::AttrMap layout_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout") + .output_model_layout = std::move(layout_map); + return *this; + } + + /** @see ov::Params::cfgOutputTensorPrecision. */ + Params& cfgOutputTensorPrecision(int precision) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision") + .output_tensor_precision = precision; + return *this; + } + + /** @overload */ + Params& + cfgOutputTensorPrecision(detail::AttrMap precision_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision") + .output_tensor_precision = std::move(precision_map); + return *this; + } + + /** @see ov::Params::cfgReshape. */ + Params& cfgReshape(std::vector new_shape) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") + .new_shapes = std::move(new_shape); + return *this; + } + + /** @overload */ + Params& + cfgReshape(detail::AttrMap> new_shape_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape") + .new_shapes = std::move(new_shape_map); + return *this; + } + + /** @see ov::Params::cfgNumRequests. */ + Params& cfgNumRequests(const size_t nireq) { + if (nireq == 0) { + cv::util::throw_error( + std::logic_error("Number of inference requests" + " must be greater than zero.")); + } + m_desc.nireq = nireq; + return *this; + } + + /** @see ov::Params::cfgMean. */ + Params& cfgMean(std::vector mean_values) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values") + .mean_values = std::move(mean_values); + return *this; + } + + /** @overload */ + Params& cfgMean(detail::AttrMap> mean_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values") + .mean_values = std::move(mean_map); + return *this; + } + + /** @see ov::Params::cfgScale. */ + Params& cfgScale(std::vector scale_values) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values") + .scale_values = std::move(scale_values); + return *this; + } + + /** @overload */ + Params& cfgScale(detail::AttrMap> scale_map) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values") + .scale_values = std::move(scale_map); + return *this; + } + + /** @see ov::Params::cfgResize. */ + Params& cfgResize(int interpolation) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing") + .interpolation = std::move(interpolation); + return *this; + } + + /** @overload */ + Params& cfgResize(detail::AttrMap interpolation) { + detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing") + .interpolation = std::move(interpolation); + return *this; + } + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::ov::backend(); } + std::string tag() const { return m_tag; } + cv::util::any params() const { return { m_desc }; } + // END(G-API's network parametrization API) + +protected: + std::string m_tag; + detail::ParamDesc m_desc; +}; + +} // namespace ov + +namespace wip { namespace ov { +/** + * @brief Ask G-API OpenVINO backend to run only inference of model provided. + * + * G-API OpenVINO backend will perform only the inference of the model provided + * without populating input and copying back output data. + * This mode is used to evaluate the pure inference performance of the model without + * taking into account the i/o data transfer. + */ +struct benchmark_mode { }; + +} // namespace ov +} // namespace wip + +} // namespace gapi + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.wip.ov.benchmark_mode"; } + }; +} + +} // namespace cv + +#endif // OPENCV_GAPI_INFER_OV_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/parsers.hpp b/modules/gapi/include/opencv2/gapi/infer/parsers.hpp new file mode 100644 index 00000000000..e39d6fd4c6a --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/parsers.hpp @@ -0,0 +1,138 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + + +#ifndef OPENCV_GAPI_PARSERS_HPP +#define OPENCV_GAPI_PARSERS_HPP + +#include // std::tuple + +#include +#include + +namespace cv { namespace gapi { +namespace nn { +namespace parsers { + using GRects = GArray; + using GDetections = std::tuple, GArray>; + + G_TYPED_KERNEL(GParseSSDBL, , float, int)>, + "org.opencv.nn.parsers.parseSSD_BL") { + static std::tuple outMeta(const GMatDesc&, const GOpaqueDesc&, float, int) { + return std::make_tuple(empty_array_desc(), empty_array_desc()); + } + }; + + G_TYPED_KERNEL(GParseSSD, , float, bool, bool)>, + "org.opencv.nn.parsers.parseSSD") { + static GArrayDesc outMeta(const GMatDesc&, const GOpaqueDesc&, float, bool, bool) { + return empty_array_desc(); + } + }; + + G_TYPED_KERNEL(GParseYolo, , float, float, std::vector)>, + "org.opencv.nn.parsers.parseYolo") { + static std::tuple outMeta(const GMatDesc&, const GOpaqueDesc&, + float, float, const std::vector&) { + return std::make_tuple(empty_array_desc(), empty_array_desc()); + } + static const std::vector& defaultAnchors() { + static std::vector anchors { + 0.57273f, 0.677385f, 1.87446f, 2.06253f, 3.33843f, 5.47434f, 7.88282f, 3.52778f, 9.77052f, 9.16828f + }; + return anchors; + } + }; +} // namespace parsers +} // namespace nn + +/** @brief Parses output of SSD network. + +Extracts detection information (box, confidence, label) from SSD output and +filters it by given confidence and label. + +@note Function textual ID is "org.opencv.nn.parsers.parseSSD_BL" + +@param in Input CV_32F tensor with {1,1,N,7} dimensions. +@param inSz Size to project detected boxes to (size of the input image). +@param confidenceThreshold If confidence of the +detection is smaller than confidence threshold, detection is rejected. +@param filterLabel If provided (!= -1), only detections with +given label will get to the output. +@return a tuple with a vector of detected boxes and a vector of appropriate labels. +*/ +GAPI_EXPORTS_W std::tuple, GArray> parseSSD(const GMat& in, + const GOpaque& inSz, + const float confidenceThreshold = 0.5f, + const int filterLabel = -1); + +/** @brief Parses output of SSD network. + +Extracts detection information (box, confidence) from SSD output and +filters it by given confidence and by going out of bounds. + +@note Function textual ID is "org.opencv.nn.parsers.parseSSD" + +@param in Input CV_32F tensor with {1,1,N,7} dimensions. +@param inSz Size to project detected boxes to (size of the input image). +@param confidenceThreshold If confidence of the +detection is smaller than confidence threshold, detection is rejected. +@param alignmentToSquare If provided true, bounding boxes are extended to squares. +The center of the rectangle remains unchanged, the side of the square is +the larger side of the rectangle. +@param filterOutOfBounds If provided true, out-of-frame boxes are filtered. +@return a vector of detected bounding boxes. +*/ +GAPI_EXPORTS_W GArray parseSSD(const GMat& in, + const GOpaque& inSz, + const float confidenceThreshold, + const bool alignmentToSquare, + const bool filterOutOfBounds); + +/** @brief Parses output of Yolo network. + +Extracts detection information (box, confidence, label) from Yolo output, +filters it by given confidence and performs non-maximum suppression for overlapping boxes. + +@note Function textual ID is "org.opencv.nn.parsers.parseYolo" + +@param in Input CV_32F tensor with {1,13,13,N} dimensions, N should satisfy: +\f[\texttt{N} = (\texttt{num_classes} + \texttt{5}) * \texttt{5},\f] +where num_classes - a number of classes Yolo network was trained with. +@param inSz Size to project detected boxes to (size of the input image). +@param confidenceThreshold If confidence of the +detection is smaller than confidence threshold, detection is rejected. +@param nmsThreshold Non-maximum suppression threshold which controls minimum +relative box intersection area required for rejecting the box with a smaller confidence. +If 1.f, nms is not performed and no boxes are rejected. +@param anchors Anchors Yolo network was trained with. +@note The default anchor values are specified for YOLO v2 Tiny as described in Intel Open Model Zoo +documentation. +@return a tuple with a vector of detected boxes and a vector of appropriate labels. +*/ +GAPI_EXPORTS_W std::tuple, GArray> parseYolo(const GMat& in, + const GOpaque& inSz, + const float confidenceThreshold = 0.5f, + const float nmsThreshold = 0.5f, + const std::vector& anchors + = nn::parsers::GParseYolo::defaultAnchors()); + +} // namespace gapi +} // namespace cv + +// Reimport parseSSD & parseYolo under their initial namespace +namespace cv { +namespace gapi { +namespace streaming { + +using cv::gapi::parseSSD; +using cv::gapi::parseYolo; + +} // namespace streaming +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_PARSERS_HPP diff --git a/modules/gapi/include/opencv2/gapi/media.hpp b/modules/gapi/include/opencv2/gapi/media.hpp new file mode 100644 index 00000000000..1470f00d042 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/media.hpp @@ -0,0 +1,258 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_MEDIA_HPP +#define OPENCV_GAPI_MEDIA_HPP + +#include // unique_ptr<>, shared_ptr<> +#include // array<> +#include // function<> +#include // forward<>() + +#include +#include + +// Forward declaration +namespace cv { +namespace gapi { +namespace s11n { +struct IOStream; +struct IIStream; +} // namespace s11n +} // namespace gapi +} // namespace cv + +namespace cv { + +/** \addtogroup gapi_data_structures + * @{ + * + * @brief Extra G-API data structures used to pass input/output data + * to the graph for processing. + */ + +/** + * @brief cv::MediaFrame class represents an image/media frame + * obtained from an external source. + * + * cv::MediaFrame represents image data as specified in + * cv::MediaFormat. cv::MediaFrame is designed to be a thin wrapper over some + * external memory of buffer; the class itself provides an uniform + * interface over such types of memory. cv::MediaFrame wraps data from + * a camera driver or from a media codec and provides an abstraction + * layer over this memory to G-API. MediaFrame defines a compact interface + * to access and manage the underlying data; the implementation is + * fully defined by the associated Adapter (which is usually + * user-defined). + * + * @sa cv::RMat + */ +class GAPI_EXPORTS MediaFrame { +public: + /// This enum defines different types of cv::MediaFrame provided + /// access to the underlying data. Note that different flags can't + /// be combined in this version. + enum class Access { + R, ///< Access data for reading + W, ///< Access data for writing + }; + class IAdapter; + class View; + using AdapterPtr = std::unique_ptr; + + /** + * @brief Constructs an empty MediaFrame + * + * The constructed object has no any data associated with it. + */ + MediaFrame(); + + /** + * @brief Constructs a MediaFrame with the given + * Adapter. MediaFrame takes ownership over the passed adapter. + * + * @param p an unique pointer to instance of IAdapter derived class. + */ + explicit MediaFrame(AdapterPtr &&p); + + /** + * @overload + * @brief Constructs a MediaFrame with the given parameters for + * the Adapter. The adapter of type `T` is costructed on the fly. + * + * @param args list of arguments to construct an adapter of type + * `T`. + */ + template static cv::MediaFrame Create(Args&&... args); + + /** + * @brief Obtain access to the underlying data with the given + * mode. + * + * Depending on the associated Adapter and the data wrapped, this + * method may be cheap (e.g., the underlying memory is local) or + * costly (if the underlying memory is external or device + * memory). + * + * @param mode an access mode flag + * @return a MediaFrame::View object. The views should be handled + * carefully, refer to the MediaFrame::View documentation for details. + */ + View access(Access mode) const; + + /** + * @brief Returns a media frame descriptor -- the information + * about the media format, dimensions, etc. + * @return a cv::GFrameDesc + */ + cv::GFrameDesc desc() const; + + // FIXME: design a better solution + // Should be used only if the actual adapter provides implementation + /// @private -- exclude from the OpenCV documentation for now. + cv::util::any blobParams() const; + + /** + * @brief Casts and returns the associated MediaFrame adapter to + * the particular adapter type `T`, returns nullptr if the type is + * different. + * + * This method may be useful if the adapter type is known by the + * caller, and some lower level access to the memory is required. + * Depending on the memory type, it may be more efficient than + * access(). + * + * @return a pointer to the adapter object, nullptr if the adapter + * type is different. + */ + template T* get() const { + static_assert(std::is_base_of::value, + "T is not derived from cv::MediaFrame::IAdapter!"); + auto* adapter = getAdapter(); + GAPI_Assert(adapter != nullptr); + return dynamic_cast(adapter); + } + + /** + * @brief Serialize MediaFrame's data to a byte array. + * + * @note The actual logic is implemented by frame's adapter class. + * Does nothing by default. + * + * @param os Bytestream to store serialized MediaFrame data in. + */ + void serialize(cv::gapi::s11n::IOStream& os) const; + +private: + struct Priv; + std::shared_ptr m; + IAdapter* getAdapter() const; +}; + +template +inline cv::MediaFrame cv::MediaFrame::Create(Args&&... args) { + std::unique_ptr ptr(new T(std::forward(args)...)); + return cv::MediaFrame(std::move(ptr)); +} + +/** + * @brief Provides access to the MediaFrame's underlying data. + * + * This object contains the necessary information to access the pixel + * data of the associated MediaFrame: arrays of pointers and strides + * (distance between every plane row, in bytes) for every image + * plane, as defined in cv::MediaFormat. + * There may be up to four image planes in MediaFrame. + * + * Depending on the MediaFrame::Access flag passed in + * MediaFrame::access(), a MediaFrame::View may be read- or + * write-only. + * + * Depending on the MediaFrame::IAdapter implementation associated + * with the parent MediaFrame, writing to memory with + * MediaFrame::Access::R flag may have no effect or lead to + * undefined behavior. Same applies to reading the memory with + * MediaFrame::Access::W flag -- again, depending on the IAdapter + * implementation, the host-side buffer the view provides access to + * may have no current data stored in (so in-place editing of the + * buffer contents may not be possible). + * + * MediaFrame::View objects must be handled carefully, as an external + * resource associated with MediaFrame may be locked for the time the + * MediaFrame::View object exists. Obtaining MediaFrame::View should + * be seen as "map" and destroying it as "unmap" in the "map/unmap" + * idiom (applicable to OpenCL, device memory, remote + * memory). + * + * When a MediaFrame buffer is accessed for writing, and the memory + * under MediaFrame::View::Ptrs is altered, the data synchronization + * of a host-side and device/remote buffer is not guaranteed until the + * MediaFrame::View is destroyed. In other words, the real data on the + * device or in a remote target may be updated at the MediaFrame::View + * destruction only -- but it depends on the associated + * MediaFrame::IAdapter implementation. + */ +class GAPI_EXPORTS MediaFrame::View final { +public: + static constexpr const size_t MAX_PLANES = 4; + using Ptrs = std::array; + using Strides = std::array; // in bytes + using Callback = std::function; + + /// @private + View(Ptrs&& ptrs, Strides&& strs, Callback &&cb = [](){}); + + /// @private + View(const View&) = delete; + + /// @private + View(View&&) = default; + + /// @private + View& operator = (const View&) = delete; + + ~View(); + + Ptrs ptr; ///< Array of image plane pointers + Strides stride; ///< Array of image plane strides, in bytes. + +private: + Callback m_cb; +}; + +/** + * @brief An interface class for MediaFrame data adapters. + * + * Implement this interface to wrap media data in the MediaFrame. It + * makes sense to implement this class if there is a custom + * cv::gapi::wip::IStreamSource defined -- in this case, a stream + * source can produce MediaFrame objects with this adapter and the + * media data may be passed to graph without any copy. For example, a + * GStreamer-based stream source can implement an adapter over + * `GstBuffer` and G-API will transparently use it in the graph. + */ +class GAPI_EXPORTS MediaFrame::IAdapter { +public: + virtual ~IAdapter() = 0; + virtual cv::GFrameDesc meta() const = 0; + virtual MediaFrame::View access(MediaFrame::Access) = 0; + // FIXME: design a better solution + // The default implementation does nothing + virtual cv::util::any blobParams() const; + virtual void serialize(cv::gapi::s11n::IOStream&) { + GAPI_Error("Generic serialize method of MediaFrame::IAdapter does nothing by default. " + "Please, implement it in derived class to properly serialize the object."); + } + virtual void deserialize(cv::gapi::s11n::IIStream&) { + GAPI_Error("Generic deserialize method of MediaFrame::IAdapter does nothing by default. " + "Please, implement it in derived class to properly deserialize the object."); + } +}; +/** @} */ + +} //namespace cv + +#endif // OPENCV_GAPI_MEDIA_HPP diff --git a/modules/gapi/include/opencv2/gapi/oak/infer.hpp b/modules/gapi/include/opencv2/gapi/oak/infer.hpp new file mode 100644 index 00000000000..4a1b9f6db6a --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/oak/infer.hpp @@ -0,0 +1,66 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2022 Intel Corporation + +#ifndef OPENCV_GAPI_OAK_INFER_HPP +#define OPENCV_GAPI_OAK_INFER_HPP + +#include +#include +#include +#include + +#include +#include + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace oak { + +namespace detail { +/** +* @brief This structure contains description of inference parameters +* which is specific to OAK models. +*/ +struct ParamDesc { + std::string blob_file; +}; +} // namespace detail + +/** + * Contains description of inference parameters and kit of functions that + * fill this parameters. + */ +template class Params { +public: + /** @brief Class constructor. + + Constructs Params based on model information and sets default values for other + inference description parameters. + + @param model Path to model (.blob file) + */ + explicit Params(const std::string &model) { + desc.blob_file = model; + }; + + // BEGIN(G-API's network parametrization API) + GBackend backend() const { return cv::gapi::oak::backend(); } + std::string tag() const { return Net::tag(); } + cv::util::any params() const { return { desc }; } + // END(G-API's network parametrization API) + +protected: + detail::ParamDesc desc; +}; + +} // namespace oak +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_OAK_INFER_HPP diff --git a/modules/gapi/include/opencv2/gapi/oak/oak.hpp b/modules/gapi/include/opencv2/gapi/oak/oak.hpp new file mode 100644 index 00000000000..8b56b8a3658 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/oak/oak.hpp @@ -0,0 +1,158 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_OAK_HPP +#define OPENCV_GAPI_OAK_HPP + +#include // IStreamSource +#include // GKernelPackage +#include // GOptRunArgsP + +namespace cv { +namespace gapi { +namespace oak { + +// FIXME: copypasted from dai library +struct EncoderConfig { + /** + * Rate control mode specifies if constant or variable bitrate should be used (H264 / H265) + */ + enum class RateControlMode: int { CBR, VBR }; + + /** + * Encoding profile, H264, H265 or MJPEG + */ + enum class Profile: int { H264_BASELINE, H264_HIGH, H264_MAIN, H265_MAIN, MJPEG }; + /** + * Specifies preferred bitrate (kb) of compressed output bitstream + */ + std::int32_t bitrate = 8000; + /** + * Every x number of frames a keyframe will be inserted + */ + std::int32_t keyframeFrequency = 30; + /** + * Specifies maximum bitrate (kb) of compressed output bitstream + */ + std::int32_t maxBitrate = 8000; + /** + * Specifies number of B frames to be inserted + */ + std::int32_t numBFrames = 0; + /** + * This options specifies how many frames are available in this nodes pool (can help if + * receiver node is slow at consuming + */ + std::uint32_t numFramesPool = 4; + /** + * Encoding profile, H264, H265 or MJPEG + */ + Profile profile = Profile::H265_MAIN; + /** + * Value between 0-100% (approximates quality) + */ + std::int32_t quality = 80; + /** + * Lossless mode ([M]JPEG only) + */ + bool lossless = false; + /** + * Rate control mode specifies if constant or variable bitrate should be used (H264 / H265) + */ + RateControlMode rateCtrlMode = RateControlMode::CBR; + /** + * Input and compressed output frame width + */ + std::int32_t width = 1920; + /** + * Input and compressed output frame height + */ + std::int32_t height = 1080; + /** + * Frame rate + */ + float frameRate = 30.0f; +}; + +G_API_OP(GEncFrame, (GFrame, EncoderConfig)>, "org.opencv.oak.enc_frame") { + static GArrayDesc outMeta(const GFrameDesc&, const EncoderConfig&) { + return cv::empty_array_desc(); + } +}; + +G_API_OP(GSobelXY, , "org.opencv.oak.sobelxy") { + static GFrameDesc outMeta(const GFrameDesc& in, const cv::Mat&, const cv::Mat&) { + return in; + } +}; + +G_API_OP(GCopy, , "org.opencv.oak.copy") { + static GFrameDesc outMeta(const GFrameDesc& in) { + return in; + } +}; + +// FIXME: add documentation on operations below + +GAPI_EXPORTS GArray encode(const GFrame& in, const EncoderConfig&); + +GAPI_EXPORTS GFrame sobelXY(const GFrame& in, + const cv::Mat& hk, + const cv::Mat& vk); + +GAPI_EXPORTS GFrame copy(const GFrame& in); + +// OAK backend & kernels //////////////////////////////////////////////////////// +GAPI_EXPORTS cv::gapi::GBackend backend(); +GAPI_EXPORTS cv::gapi::GKernelPackage kernels(); + +// Camera object /////////////////////////////////////////////////////////////// + +struct GAPI_EXPORTS ColorCameraParams { + /** + * Format of the frame one gets from the camera + */ + bool interleaved = false; + + // FIXME: extend + enum class BoardSocket: int { RGB, BGR }; + + BoardSocket board_socket = BoardSocket::RGB; + + // FIXME: extend + enum class Resolution: int { THE_1080_P }; + + Resolution resolution = Resolution::THE_1080_P; +}; + +class GAPI_EXPORTS ColorCamera: public cv::gapi::wip::IStreamSource { + cv::MediaFrame m_dummy; + ColorCameraParams m_params; + + virtual bool pull(cv::gapi::wip::Data &data) override; + virtual GMetaArg descr_of() const override; + +public: + ColorCamera(); + explicit ColorCamera(const ColorCameraParams& params); +}; + +} // namespace oak +} // namespace gapi + +namespace detail { +template<> struct CompileArgTag { + static const char* tag() { return "gapi.oak.colorCameraParams"; } +}; + +template<> struct CompileArgTag { + static const char* tag() { return "gapi.oak.encoderConfig"; } +}; +} // namespace detail + +} // namespace cv + +#endif // OPENCV_GAPI_OAK_HPP diff --git a/modules/gapi/include/opencv2/gapi/ocl/core.hpp b/modules/gapi/include/opencv2/gapi/ocl/core.hpp new file mode 100644 index 00000000000..b79aace0ca7 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/ocl/core.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OCL_CORE_API_HPP +#define OPENCV_GAPI_OCL_CORE_API_HPP + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace core { +namespace ocl { + +GAPI_EXPORTS_W cv::GKernelPackage kernels(); + +} // namespace ocl +} // namespace core +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_OCL_CORE_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp new file mode 100644 index 00000000000..b09082282f5 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp @@ -0,0 +1,260 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GOCLKERNEL_HPP +#define OPENCV_GAPI_GOCLKERNEL_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +// FIXME: namespace scheme for backends? +namespace cv { + +namespace gimpl +{ + // Forward-declare an internal class + class GOCLExecutable; +} // namespace gimpl + +namespace gapi +{ +/** + * @brief This namespace contains G-API OpenCL backend functions, structures, and symbols. + */ +namespace ocl +{ + /** + * \addtogroup gapi_std_backends G-API Standard Backends + * @{ + */ + /** + * @brief Get a reference to OCL backend. + * + * At the moment, the OCL backend is built atop of OpenCV + * "Transparent API" (T-API), see cv::UMat for details. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ +} // namespace ocl +} // namespace gapi + + +// Represents arguments which are passed to a wrapped OCL function +// FIXME: put into detail? +class GAPI_EXPORTS GOCLContext +{ +public: + // Generic accessor API + template + const T& inArg(int input) { return m_args.at(input).get(); } + + // Syntax sugar + const cv::UMat& inMat(int input); + cv::UMat& outMatR(int output); // FIXME: Avoid cv::Mat m = ctx.outMatR() + + const cv::Scalar& inVal(int input); + cv::Scalar& outValR(int output); // FIXME: Avoid cv::Scalar s = ctx.outValR() + template std::vector& outVecR(int output) // FIXME: the same issue + { + return outVecRef(output).wref(); + } + template T& outOpaqueR(int output) // FIXME: the same issue + { + return outOpaqueRef(output).wref(); + } + +protected: + detail::VectorRef& outVecRef(int output); + detail::OpaqueRef& outOpaqueRef(int output); + + std::vector m_args; + std::unordered_map m_results; + + + friend class gimpl::GOCLExecutable; +}; + +class GAPI_EXPORTS GOCLKernel +{ +public: + // This function is kernel's execution entry point (does the processing work) + using F = std::function; + + GOCLKernel(); + explicit GOCLKernel(const F& f); + + void apply(GOCLContext &ctx); + +protected: + F m_f; +}; + +// FIXME: This is an ugly ad-hoc implementation. TODO: refactor + +namespace detail +{ +template struct ocl_get_in; +template<> struct ocl_get_in +{ + static cv::UMat get(GOCLContext &ctx, int idx) { return ctx.inMat(idx); } +}; +template<> struct ocl_get_in +{ + static cv::Scalar get(GOCLContext &ctx, int idx) { return ctx.inVal(idx); } +}; +template struct ocl_get_in > +{ + static const std::vector& get(GOCLContext &ctx, int idx) { return ctx.inArg(idx).rref(); } +}; +template<> struct ocl_get_in +{ + static cv::MediaFrame get(GOCLContext &ctx, int idx) { return ctx.inArg(idx); } +}; +template struct ocl_get_in > +{ + static const U& get(GOCLContext &ctx, int idx) { return ctx.inArg(idx).rref(); } +}; +template struct ocl_get_in +{ + static T get(GOCLContext &ctx, int idx) { return ctx.inArg(idx); } +}; + +struct tracked_cv_umat{ + //TODO Think if T - API could reallocate UMat to a proper size - how do we handle this ? + //tracked_cv_umat(cv::UMat& m) : r{(m)}, original_data{m.getMat(ACCESS_RW).data} {} + tracked_cv_umat(cv::UMat& m) : r(m), original_data{ nullptr } {} + cv::UMat &r; // FIXME: It was a value (not a reference) before. + // Actually OCL backend should allocate its internal data! + uchar* original_data; + + operator cv::UMat& (){ return r;} + void validate() const{ + //if (r.getMat(ACCESS_RW).data != original_data) + //{ + // util::throw_error + // (std::logic_error + // ("OpenCV kernel output parameter was reallocated. \n" + // "Incorrect meta data was provided ?")); + //} + + } +}; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) // unreachable code +#endif +template +void postprocess_ocl(Outputs&... outs) +{ + struct + { + void operator()(tracked_cv_umat* bm) { bm->validate(); } + void operator()(...) { } + + } validate; + //dummy array to unfold parameter pack + int dummy[] = { 0, (validate(&outs), 0)... }; + cv::util::suppress_unused_warning(dummy); +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template struct ocl_get_out; +template<> struct ocl_get_out +{ + static tracked_cv_umat get(GOCLContext &ctx, int idx) + { + auto& r = ctx.outMatR(idx); + return{ r }; + } +}; +template<> struct ocl_get_out +{ + static cv::Scalar& get(GOCLContext &ctx, int idx) + { + return ctx.outValR(idx); + } +}; +template struct ocl_get_out > +{ + static std::vector& get(GOCLContext &ctx, int idx) { return ctx.outVecR(idx); } +}; +template struct ocl_get_out > +{ + static U& get(GOCLContext &ctx, int idx) { return ctx.outOpaqueR(idx); } +}; + +template +struct OCLCallHelper; + +// FIXME: probably can be simplified with std::apply or analogue. +template +struct OCLCallHelper, std::tuple > +{ + template + struct call_and_postprocess + { + template + static void call(Inputs&&... ins, Outputs&&... outs) + { + //not using a std::forward on outs is deliberate in order to + //cause compilation error, by trying to bind rvalue references to lvalue references + Impl::run(std::forward(ins)..., outs...); + + postprocess_ocl(outs...); + } + }; + + template + static void call_impl(GOCLContext &ctx, detail::Seq, detail::Seq) + { + //TODO: Make sure that OpenCV kernels do not reallocate memory for output parameters + //by comparing it's state (data ptr) before and after the call. + //Convert own::Scalar to cv::Scalar before call kernel and run kernel + //convert cv::Scalar to own::Scalar after call kernel and write back results + call_and_postprocess::get(ctx, IIs))...>::call(ocl_get_in::get(ctx, IIs)..., ocl_get_out::get(ctx, OIs)...); + } + + static void call(GOCLContext &ctx) + { + call_impl(ctx, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } +}; + +} // namespace detail + +template +class GOCLKernelImpl: public cv::detail::OCLCallHelper, + public cv::detail::KernelTag +{ + using P = detail::OCLCallHelper; + +public: + using API = K; + + static cv::gapi::GBackend backend() { return cv::gapi::ocl::backend(); } + static cv::GOCLKernel kernel() { return GOCLKernel(&P::call); } +}; + +#define GAPI_OCL_KERNEL(Name, API) struct Name: public cv::GOCLKernelImpl + +} // namespace cv + +#endif // OPENCV_GAPI_GOCLKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/ocl/imgproc.hpp b/modules/gapi/include/opencv2/gapi/ocl/imgproc.hpp new file mode 100644 index 00000000000..1bb5911b186 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/ocl/imgproc.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OCL_IMGPROC_API_HPP +#define OPENCV_GAPI_OCL_IMGPROC_API_HPP + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace imgproc { +namespace ocl { + + GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace ocl +} // namespace imgproc +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_OCL_IMGPROC_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/opencv_includes.hpp b/modules/gapi/include/opencv2/gapi/opencv_includes.hpp new file mode 100644 index 00000000000..7c2c42d8a2b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/opencv_includes.hpp @@ -0,0 +1,42 @@ +// This file is part of OpenCV project. + +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OPENCV_INCLUDES_HPP +#define OPENCV_GAPI_OPENCV_INCLUDES_HPP + +#if !defined(GAPI_STANDALONE) +# include +# include +# include +# include +#define GAPI_OWN_TYPES_LIST cv::gapi::own::Rect, \ + cv::gapi::own::Size, \ + cv::gapi::own::Point, \ + cv::gapi::own::Point2f, \ + cv::gapi::own::Scalar, \ + cv::gapi::own::Mat +#else // Without OpenCV +# include +# include // cv::gapi::own::Rect/Size/Point +# include // cv::gapi::own::Scalar +# include +// replacement of cv's structures: +namespace cv { + using Rect = gapi::own::Rect; + using Size = gapi::own::Size; + using Point = gapi::own::Point; + using Point2f = gapi::own::Point2f; + using Point3f = gapi::own::Point3f; + using Scalar = gapi::own::Scalar; + using Mat = gapi::own::Mat; +} // namespace cv +#define GAPI_OWN_TYPES_LIST cv::gapi::own::VoidType + +#endif // !defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_OPENCV_INCLUDES_HPP diff --git a/modules/gapi/include/opencv2/gapi/operators.hpp b/modules/gapi/include/opencv2/gapi/operators.hpp new file mode 100644 index 00000000000..6794b44b6e4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/operators.hpp @@ -0,0 +1,70 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OPERATORS_HPP +#define OPENCV_GAPI_OPERATORS_HPP + +#include +#include + +namespace cv +{ +GAPI_EXPORTS cv::GMat operator+(const cv::GMat& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator+(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator+(const cv::GScalar& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator-(const cv::GMat& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator-(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator-(const cv::GScalar& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator*(const cv::GMat& lhs, float rhs); +GAPI_EXPORTS cv::GMat operator*(float lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator*(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator*(const cv::GScalar& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator/(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator/(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator/(const cv::GMat& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator&(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator|(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator^(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator~(const cv::GMat& lhs); + +GAPI_EXPORTS cv::GMat operator&(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator|(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator^(const cv::GScalar& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator&(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator|(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator^(const cv::GMat& lhs, const cv::GScalar& rhs); + +GAPI_EXPORTS cv::GMat operator>(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator>=(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator<(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator<=(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator==(const cv::GMat& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator!=(const cv::GMat& lhs, const cv::GMat& rhs); + +GAPI_EXPORTS cv::GMat operator>(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator>=(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator<(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator<=(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator==(const cv::GMat& lhs, const cv::GScalar& rhs); +GAPI_EXPORTS cv::GMat operator!=(const cv::GMat& lhs, const cv::GScalar& rhs); + +GAPI_EXPORTS cv::GMat operator>(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator>=(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator<(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator<=(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator==(const cv::GScalar& lhs, const cv::GMat& rhs); +GAPI_EXPORTS cv::GMat operator!=(const cv::GScalar& lhs, const cv::GMat& rhs); +} // cv + +#endif // OPENCV_GAPI_OPERATORS_HPP diff --git a/modules/gapi/include/opencv2/gapi/ot.hpp b/modules/gapi/include/opencv2/gapi/ot.hpp new file mode 100644 index 00000000000..b73d7e6ee00 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/ot.hpp @@ -0,0 +1,194 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_OT_HPP +#define OPENCV_GAPI_OT_HPP + +#include +#include +#include + +namespace cv { +namespace gapi { +/** + * @brief This namespace contains G-API Operation Types for + * VAS Object Tracking module functionality. + */ +namespace ot { + +/** + * @enum TrackingStatus + * + * Tracking status twin for vas::ot::TrackingStatus + */ +enum TrackingStatus +{ + NEW = 0, /**< The object is newly added. */ + TRACKED, /**< The object is being tracked. */ + LOST /**< The object gets lost now. The object can be tracked again + by specifying detected object manually. */ +}; + +struct GAPI_EXPORTS_W_SIMPLE ObjectTrackerParams +{ + /** + * Maximum number of trackable objects in a frame. + * Valid range: 1 <= max_num_objects. Or it can be -1 if there is no limitation + * of maximum number in X86. KMB/TBH has limitation up to 1024. + * Default value is -1 which means there is no limitation in X86. KMB/TBH is -1 means 200. + */ + GAPI_PROP_RW int32_t max_num_objects = -1; + + /** + * Input color format. Supports 0(BGR), 1(NV12), 2(BGRX) and 4(I420) + */ + GAPI_PROP_RW int32_t input_image_format = 0; + + /** + * Specifies whether tracker to use detection class for keeping id of an object. + * If it is true, new detection will be associated from previous tracking only when + * those two have same class. + * class id of an object is fixed across video frames. + * If it is false, new detection can be associated across different-class objects. + * In this case, the class id of an object may change across video frames depending on the tracker input. + * It is recommended to turn this option off when it is likely that detector confuses the class of object. + * For example, when detector confuses bicycle and motorbike. Turning this option off will increase + * the tracking reliability as tracker will ignore the class label of detector. + * @n + * Default value is true. + */ + GAPI_PROP_RW bool tracking_per_class = true; + + bool operator==(const ObjectTrackerParams& other) const + { + return max_num_objects == other.max_num_objects + && input_image_format == other.input_image_format + && tracking_per_class == other.tracking_per_class; + } +}; + +using GTrackedInfo = std::tuple, cv::GArray, cv::GArray, cv::GArray>; + +G_API_OP(GTrackFromMat, , cv::GArray, float)>, "com.intel.track_from_mat") +{ + static std::tuple outMeta(cv::GMatDesc, cv::GArrayDesc, cv::GArrayDesc, float) + { + return std::make_tuple(cv::empty_array_desc(), cv::empty_array_desc(), + cv::empty_array_desc(), cv::empty_array_desc()); + } +}; + +G_API_OP(GTrackFromFrame, , cv::GArray, float)>, "com.intel.track_from_frame") +{ + static std::tuple outMeta(cv::GFrameDesc, cv::GArrayDesc, cv::GArrayDesc, float) + { + return std::make_tuple(cv::empty_array_desc(), cv::empty_array_desc(), + cv::empty_array_desc(), cv::empty_array_desc()); + } +}; + +/** + * @brief Tracks objects with video frames. + * If a detected object is overlapped enough with one of tracked object, the tracked object's + * informationis updated with the input detected object. + * On the other hand, if a detected object is overlapped with none of tracked objects, + * the detected object is newly added and ObjectTracker starts to track the object. + * In zero term tracking type, ObjectTracker clears tracked objects in case that empty + * list of detected objects is passed in. + * + * @param mat Input frame. + * @param detected_rects Detected objects rectangles in the input frame. + * @param detected_class_labels Detected objects class labels in the input frame. + * @param delta Frame_delta_t Delta time between two consecutive tracking in seconds. + * The valid range is [0.005 ~ 0.5]. + * @return Tracking results of target objects. + * cv::GArray Array of rectangles for tracked objects. + * cv::GArray Array of detected objects labels. + * cv::GArray Array of tracking IDs for objects. + * Numbering sequence starts from 1. + * The value 0 means the tracking ID of this object has + * not been assigned. + * cv::GArray Array of tracking statuses for objects. + */ +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GMat& mat, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta); + + +/** + @overload + * @brief Tracks objects with video frames. Overload of track(...) for frame as GFrame. + * + * @param frame Input frame. + * @param detected_rects Detected objects rectangles in the input frame. + * @param detected_class_labels Detected objects class labels in the input frame. + * @param delta Frame_delta_t Delta time between two consecutive tracking in seconds. + * The valid range is [0.005 ~ 0.5]. + * @return Tracking results of target objects. + * @return Tracking results of target objects. + * cv::GArray Array of rectangles for tracked objects. + * cv::GArray Array of detected objects labels. + * cv::GArray Array of tracking IDs for objects. + * Numbering sequence starts from 1. + * The value 0 means the tracking ID of this object has + * not been assigned. + * cv::GArray Array of tracking statuses for objects. + */ +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GFrame& frame, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta); +} // namespace ot +} // namespace gapi +} // namespace cv + +// FIXME: move to a separate file? +namespace cv +{ +namespace detail +{ +template<> struct CompileArgTag +{ + static const char* tag() + { + return "cv.gapi.ot.object_tracker_params"; + } +}; +} // namespace detail + +namespace gapi +{ +namespace s11n +{ +namespace detail +{ +template<> struct S11N { + static void serialize(IOStream &os, const cv::gapi::ot::ObjectTrackerParams &p) { + os << p. max_num_objects << p.input_image_format << p.tracking_per_class; + } + static cv::gapi::ot::ObjectTrackerParams deserialize(IIStream &is) { + cv::gapi::ot::ObjectTrackerParams p; + is >> p. max_num_objects >> p.input_image_format >> p.tracking_per_class; + return p; + } +}; +} // namespace detail +} // namespace s11n +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_OT_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/assert.hpp b/modules/gapi/include/opencv2/gapi/own/assert.hpp new file mode 100644 index 00000000000..ab2fb896f12 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/assert.hpp @@ -0,0 +1,60 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_OWN_ASSERT_HPP +#define OPENCV_GAPI_OWN_ASSERT_HPP + +#include + +#define GAPI_DbgAssertNoOp(expr) { \ + constexpr bool _assert_tmp = false && (expr); \ + cv::util::suppress_unused_warning(_assert_tmp); \ +} + +#if !defined(GAPI_STANDALONE) +#include +#define GAPI_Assert CV_Assert + +#if defined _DEBUG || defined CV_STATIC_ANALYSIS +# define GAPI_DbgAssert CV_DbgAssert +#else +# define GAPI_DbgAssert(expr) GAPI_DbgAssertNoOp(expr) +#endif + +#define GAPI_Error(msg) CV_Error(cv::Error::StsError, msg) + +#else +#include +#include +#include + +namespace detail +{ + [[noreturn]] inline void assert_abort(const char* str, int line, const char* file, const char* func) + { + std::stringstream ss; + ss << file << ":" << line << ": Assertion " << str << " in function " << func << " failed\n"; + cv::util::throw_error(std::logic_error(ss.str())); + } +} + +#define GAPI_Assert(expr) \ +{ if (!(expr)) ::detail::assert_abort(#expr, __LINE__, __FILE__, __func__); } + +#ifdef NDEBUG +# define GAPI_DbgAssert(expr) GAPI_DbgAssertNoOp(expr) +#else +# define GAPI_DbgAssert(expr) GAPI_Assert(expr) +#endif + +#define GAPI_Error(msg) { \ + ::detail::assert_abort(msg, __LINE__, __FILE__, __func__); \ +} + +#endif // GAPI_STANDALONE + +#endif // OPENCV_GAPI_OWN_ASSERT_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/convert.hpp b/modules/gapi/include/opencv2/gapi/own/convert.hpp new file mode 100644 index 00000000000..7bebec9cf0a --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/convert.hpp @@ -0,0 +1,55 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OWN_CONVERT_HPP +#define OPENCV_GAPI_OWN_CONVERT_HPP + +#if !defined(GAPI_STANDALONE) + +#include +#include + +namespace cv +{ + template + std::vector to_own(const cv::MatSize &sz) { + std::vector result(sz.dims()); + for (int i = 0; i < sz.dims(); i++) { + // Note: cv::MatSize is not iterable + result[i] = static_cast(sz[i]); + } + return result; + } + + cv::gapi::own::Mat to_own(Mat&&) = delete; + + inline cv::gapi::own::Mat to_own(Mat const& m) { + return (m.dims <= 2) + ? cv::gapi::own::Mat{m.rows, m.cols, m.type(), m.data, m.step} + : cv::gapi::own::Mat{to_own(m.size), m.type(), m.data}; + } + +namespace gapi +{ +namespace own +{ + + inline cv::Mat to_ocv(Mat const& m) { + return m.dims.empty() + ? cv::Mat{m.rows, m.cols, m.type(), m.data, m.step} + : cv::Mat{m.dims, m.type(), m.data}; + } + + cv::Mat to_ocv(Mat&&) = delete; + +} // namespace own +} // namespace gapi +} // namespace cv + +#endif // !defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_OWN_CONVERT_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp b/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp new file mode 100644 index 00000000000..d3bef98e980 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp @@ -0,0 +1,166 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_CV_DEFS_HPP +#define OPENCV_GAPI_CV_DEFS_HPP + +#if defined(GAPI_STANDALONE) +// Simulate OpenCV definitions taken from various +// OpenCV interface headers if G-API is built in a +// standalone mode. + +// interface.h: + +typedef unsigned char uchar; +typedef char schar; + +typedef unsigned short ushort; + +#define CV_USRTYPE1 (void)"CV_USRTYPE1 support has been dropped in OpenCV 4.0" + +#define CV_CN_MAX 512 +#define CV_CN_SHIFT 3 +#define CV_DEPTH_MAX (1 << CV_CN_SHIFT) + +#define CV_8U 0 +#define CV_8S 1 +#define CV_16U 2 +#define CV_16S 3 +#define CV_32S 4 +#define CV_32F 5 +#define CV_64F 6 +#define CV_16F 7 + +#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1) +#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK) + +#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT)) +#define CV_MAKE_TYPE CV_MAKETYPE + +#define CV_8UC1 CV_MAKETYPE(CV_8U,1) +#define CV_8UC2 CV_MAKETYPE(CV_8U,2) +#define CV_8UC3 CV_MAKETYPE(CV_8U,3) +#define CV_8UC4 CV_MAKETYPE(CV_8U,4) +#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n)) + +#define CV_8SC1 CV_MAKETYPE(CV_8S,1) +#define CV_8SC2 CV_MAKETYPE(CV_8S,2) +#define CV_8SC3 CV_MAKETYPE(CV_8S,3) +#define CV_8SC4 CV_MAKETYPE(CV_8S,4) +#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n)) + +#define CV_16UC1 CV_MAKETYPE(CV_16U,1) +#define CV_16UC2 CV_MAKETYPE(CV_16U,2) +#define CV_16UC3 CV_MAKETYPE(CV_16U,3) +#define CV_16UC4 CV_MAKETYPE(CV_16U,4) +#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n)) + +#define CV_16SC1 CV_MAKETYPE(CV_16S,1) +#define CV_16SC2 CV_MAKETYPE(CV_16S,2) +#define CV_16SC3 CV_MAKETYPE(CV_16S,3) +#define CV_16SC4 CV_MAKETYPE(CV_16S,4) +#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n)) + +#define CV_32SC1 CV_MAKETYPE(CV_32S,1) +#define CV_32SC2 CV_MAKETYPE(CV_32S,2) +#define CV_32SC3 CV_MAKETYPE(CV_32S,3) +#define CV_32SC4 CV_MAKETYPE(CV_32S,4) +#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n)) + +#define CV_16FC1 CV_MAKETYPE(CV_16F,1) +#define CV_16FC2 CV_MAKETYPE(CV_16F,2) +#define CV_16FC3 CV_MAKETYPE(CV_16F,3) +#define CV_16FC4 CV_MAKETYPE(CV_16F,4) +#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n)) + +#define CV_32FC1 CV_MAKETYPE(CV_32F,1) +#define CV_32FC2 CV_MAKETYPE(CV_32F,2) +#define CV_32FC3 CV_MAKETYPE(CV_32F,3) +#define CV_32FC4 CV_MAKETYPE(CV_32F,4) +#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n)) + +#define CV_64FC1 CV_MAKETYPE(CV_64F,1) +#define CV_64FC2 CV_MAKETYPE(CV_64F,2) +#define CV_64FC3 CV_MAKETYPE(CV_64F,3) +#define CV_64FC4 CV_MAKETYPE(CV_64F,4) +#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n)) + +// cvdef.h: + +#ifndef CV_ALWAYS_INLINE +# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define CV_ALWAYS_INLINE inline __attribute__((always_inline)) +# elif defined(_MSC_VER) +# define CV_ALWAYS_INLINE __forceinline +# else +# define CV_ALWAYS_INLINE inline +# endif +#endif + +#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT) +#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1) +#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1) +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) +#define CV_MAT_CONT_FLAG_SHIFT 14 +#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT) +#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG) +#define CV_IS_CONT_MAT CV_IS_MAT_CONT +#define CV_SUBMAT_FLAG_SHIFT 15 +#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT) +#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG) + +//** Size of each channel item, +// 0x8442211 = 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */ +#define CV_ELEM_SIZE1(type) \ + ((((sizeof(size_t)<<28)|0x8442211) >> CV_MAT_DEPTH(type)*4) & 15) + +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) + +/** 0x3a50 = 11 10 10 01 01 00 00 ~ array of log2(sizeof(arr_type_elem)) */ +#define CV_ELEM_SIZE(type) \ + (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3)) + +#ifndef CV_OVERRIDE +# define CV_OVERRIDE override +#endif + +// base.h: +namespace cv +{ +enum BorderTypes { + BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i` + BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh` + BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb` + BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg` + BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba` + BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno` + + BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_ISOLATED = 16 //!< do not look outside of ROI +}; +// imgproc.hpp: +enum InterpolationFlags{ + INTER_NEAREST = 0, + INTER_LINEAR = 1, + INTER_CUBIC = 2, + INTER_AREA = 3, + INTER_LANCZOS4 = 4, + INTER_LINEAR_EXACT = 5, + INTER_MAX = 7, +}; +} // namespace cv + +static inline int cvFloor( double value ) +{ + int i = (int)value; + return i - (i > value); +} + +#endif // defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_CV_DEFS_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/exports.hpp b/modules/gapi/include/opencv2/gapi/own/exports.hpp new file mode 100644 index 00000000000..c36f4003d0f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/exports.hpp @@ -0,0 +1,42 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OWN_TYPES_HPP +#define OPENCV_GAPI_OWN_TYPES_HPP + +# if defined(__OPENCV_BUILD) +# include +# define GAPI_EXPORTS CV_EXPORTS + /* special informative macros for wrapper generators */ +# define GAPI_PROP CV_PROP +# define GAPI_PROP_RW CV_PROP_RW +# define GAPI_WRAP CV_WRAP +# define GAPI_EXPORTS_W_SIMPLE CV_EXPORTS_W_SIMPLE +# define GAPI_EXPORTS_W CV_EXPORTS_W +# else +# define GAPI_PROP +# define GAPI_PROP_RW +# define GAPI_WRAP +# define GAPI_EXPORTS +# define GAPI_EXPORTS_W_SIMPLE +# define GAPI_EXPORTS_W + +#if 0 // Note: the following version currently is not needed for non-OpenCV build +# if defined _WIN32 +# define GAPI_EXPORTS __declspec(dllexport) +# elif defined __GNUC__ && __GNUC__ >= 4 +# define GAPI_EXPORTS __attribute__ ((visibility ("default"))) +# endif + +# ifndef GAPI_EXPORTS +# define GAPI_EXPORTS +# endif +#endif + +# endif + +#endif // OPENCV_GAPI_OWN_TYPES_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/mat.hpp b/modules/gapi/include/opencv2/gapi/own/mat.hpp new file mode 100644 index 00000000000..ce9c0bf3623 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/mat.hpp @@ -0,0 +1,354 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OWN_MAT_HPP +#define OPENCV_GAPI_OWN_MAT_HPP + +#include +#include +#include +#include +#include + +#include //std::shared_ptr +#include //std::memcpy +#include //std::accumulate +#include +#include + +namespace cv { namespace gapi { namespace own { + namespace detail { + template + void assign_row(void* ptr, int cols, Scalar const& s) + { + auto p = static_cast(ptr); + for (int c = 0; c < cols; c++) + { + for (int ch = 0; ch < channels; ch++) + { + p[c * channels + ch] = saturate(s[ch], roundd); + } + } + } + + inline size_t default_step(int type, int cols) + { + return CV_ELEM_SIZE(type) * cols; + } + //Matrix header, i.e. fields that are unique to each Mat object. + //Devoted class is needed to implement custom behavior on move (erasing state of moved from object) + struct MatHeader{ + enum { AUTO_STEP = 0}; + enum { TYPE_MASK = 0x00000FFF }; + + MatHeader() = default; + + MatHeader(int _rows, int _cols, int type, void* _data, size_t _step) + : flags((type & TYPE_MASK)), rows(_rows), cols(_cols), data((uchar*)_data), step(_step == AUTO_STEP ? detail::default_step(type, _cols) : _step) + {} + + MatHeader(const std::vector &_dims, int type, void* _data) + : flags((type & TYPE_MASK)), data((uchar*)_data), step(0), dims(_dims) + {} + + MatHeader(const MatHeader& ) = default; + MatHeader(MatHeader&& src) : MatHeader(src) // reuse copy constructor here + { + MatHeader empty; //give it a name to call copy(not move) assignment below + src = empty; + } + MatHeader& operator=(const MatHeader& ) = default; + MatHeader& operator=(MatHeader&& src) + { + *this = src; //calling a copy assignment here, not move one + MatHeader empty; //give it a name to call copy(not move) assignment below + src = empty; + return *this; + } + /*! includes several bit-fields: + - depth + - number of channels + */ + int flags = 0; + + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows = 0, cols = 0; + //! pointer to the data + uchar* data = nullptr; + size_t step = 0; + //! dimensions (ND-case) + std::vector dims; + }; + } // namespace detail + //concise version of cv::Mat suitable for GAPI needs (used when no dependence on OpenCV is required) + class Mat : public detail::MatHeader{ + public: + + Mat() = default; + + /** @overload + @param _rows Number of rows in a 2D array. + @param _cols Number of columns in a 2D array. + @param _type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param _data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param _step Number of bytes each matrix row occupies. The value should include the padding bytes at + the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed + and the actual step is calculated as cols*elemSize(). See Mat::elemSize. + */ + Mat(int _rows, int _cols, int _type, void* _data, size_t _step = AUTO_STEP) + : MatHeader (_rows, _cols, _type, _data, _step) + {} + + Mat(const std::vector &_dims, int _type, void* _data) + : MatHeader (_dims, _type, _data) + {} + + Mat(std::vector &&_dims, int _type, void* _data) + : MatHeader (std::move(_dims), _type, _data) + {} + + Mat(Mat const& src, const Rect& roi ) + : Mat(src) + { + rows = roi.height; + cols = roi.width; + data = ptr(roi.y, roi.x); + } + + Mat(Mat const& ) = default; + Mat(Mat&& ) = default; + + Mat& operator=(Mat const& ) = default; + Mat& operator=(Mat&& ) = default; + + /** @brief Sets all or some of the array elements to the specified value. + @param s Assigned scalar converted to the actual array type. + */ + Mat& operator = (const Scalar& s) + { + constexpr unsigned max_channels = 4; //Scalar can't fit more than 4 + using func_p_t = void (*)(void*, int, Scalar const&); + using detail::assign_row; + #define TABLE_ENTRY(type) {assign_row, assign_row, assign_row, assign_row} + static constexpr func_p_t func_tbl[][max_channels] = { + TABLE_ENTRY(uchar), + TABLE_ENTRY(schar), + TABLE_ENTRY(ushort), + TABLE_ENTRY(short), + TABLE_ENTRY(int), + TABLE_ENTRY(float), + TABLE_ENTRY(double) + }; + #undef TABLE_ENTRY + + static_assert(CV_8U == 0 && CV_8S == 1 && CV_16U == 2 && CV_16S == 3 + && CV_32S == 4 && CV_32F == 5 && CV_64F == 6, + "OCV type ids used as indexes to array, thus exact numbers are important!" + ); + + const auto depth = static_cast(this->depth()); + GAPI_Assert(depth < sizeof(func_tbl)/sizeof(func_tbl[0])); + + if (dims.empty()) + { + const auto channels = static_cast(this->channels()); + GAPI_Assert(channels <= max_channels); + + auto* f = func_tbl[depth][channels - 1]; + for (int r = 0; r < rows; ++r) + { + (*f)(static_cast(ptr(r)), cols, s ); + } + } + else + { + auto* f = func_tbl[depth][0]; + // FIXME: better to refactor assign_row to use std::size_t by default + (*f)(static_cast(data), static_cast(total()), s); + } + return *this; + } + + /** @brief Returns the matrix element size in bytes. + + The method returns the matrix element size in bytes. For example, if the matrix type is CV_16SC3 , + the method returns 3\*sizeof(short) or 6. + */ + size_t elemSize() const + { + return CV_ELEM_SIZE(type()); + } + /** @brief Returns the type of a matrix element. + + The method returns a matrix element type. This is an identifier compatible with the CvMat type + system, like CV_16SC3 or 16-bit signed 3-channel array, and so on. + */ + int type() const {return CV_MAT_TYPE(flags);} + + /** @brief Returns the depth of a matrix element. + + The method returns the identifier of the matrix element depth (the type of each individual channel). + For example, for a 16-bit signed element array, the method returns CV_16S . A complete list of + matrix types contains the following values: + - CV_8U - 8-bit unsigned integers ( 0..255 ) + - CV_8S - 8-bit signed integers ( -128..127 ) + - CV_16U - 16-bit unsigned integers ( 0..65535 ) + - CV_16S - 16-bit signed integers ( -32768..32767 ) + - CV_32S - 32-bit signed integers ( -2147483648..2147483647 ) + - CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN ) + - CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN ) + */ + int depth() const {return CV_MAT_DEPTH(flags);} + + /** @brief Returns the number of matrix channels. + + The method returns the number of matrix channels. + If matrix is N-dimensional, -1 is returned. + */ + int channels() const {return dims.empty() ? CV_MAT_CN(flags) : -1;} + + /** + @param _rows New number of rows. + @param _cols New number of columns. + @param _type New matrix type. + */ + void create(int _rows, int _cols, int _type) + { + create(Size{_cols, _rows}, _type); + } + /** @overload + @param _size Alternative new matrix size specification: Size(cols, rows) + @param _type New matrix type. + */ + void create(Size _size, int _type) + { + GAPI_Assert(_size.height >= 0 && _size.width >= 0); + if (_size != Size{cols, rows} ) + { + Mat tmp{_size.height, _size.width, _type, nullptr}; + tmp.memory.reset(new uchar[ tmp.step * tmp.rows], [](uchar * p){delete[] p;}); + tmp.data = tmp.memory.get(); + + *this = std::move(tmp); + } + } + + void create(const std::vector &_dims, int _type) + { + // FIXME: make a proper reallocation-on-demands + // WARNING: no tensor views, so no strides + Mat tmp{_dims, _type, nullptr}; + // FIXME: this accumulate duplicates a lot + const auto sz = std::accumulate(_dims.begin(), _dims.end(), 1, std::multiplies()); + tmp.memory.reset(new uchar[CV_ELEM_SIZE(_type)*sz], [](uchar * p){delete[] p;}); + tmp.data = tmp.memory.get(); + *this = std::move(tmp); + } + + /** @brief Creates a full copy of the matrix and the underlying data. + + The method creates a full copy of the matrix. The original step[] is not taken into account. + So, the copy has a continuous buffer occupying total() * elemSize() bytes. + */ + Mat clone() const + { + Mat m; + copyTo(m); + return m; + } + + /** @brief Copies the matrix to another one. + + The method copies the matrix data to another matrix. Before copying the data, the method invokes : + @code + m.create(this->size(), this->type()); + @endcode + so that the destination matrix is reallocated if needed. While m.copyTo(m); works flawlessly, the + function does not handle the case of a partial overlap between the source and the destination + matrices. + */ + void copyTo(Mat& dst) const + { + if (dims.empty()) + { + dst.create(rows, cols, type()); + for (int r = 0; r < rows; ++r) + { + std::copy_n(ptr(r), detail::default_step(type(),cols), dst.ptr(r)); + } + } + else + { + dst.create(dims, depth()); + std::copy_n(data, total()*elemSize(), data); + } + } + + /** @brief Returns true if the array has no elements. + + The method returns true if Mat::total() is 0 or if Mat::data is NULL. Because of pop_back() and + resize() methods `M.total() == 0` does not imply that `M.data == NULL`. + */ + bool empty() const + { + return data == 0 || total() == 0; + } + + /** @brief Returns the total number of array elements. + + The method returns the number of array elements (a number of pixels if the array represents an + image). + */ + size_t total() const + { + return dims.empty() + ? (static_cast(rows) * cols) + : std::accumulate(dims.begin(), dims.end(), static_cast(1), std::multiplies()); + } + + /** @overload + @param roi Extracted submatrix specified as a rectangle. + */ + Mat operator()( const Rect& roi ) const + { + return Mat{*this, roi}; + } + + + /** @brief Returns a pointer to the specified matrix row. + + The methods return `uchar*` or typed pointer to the specified matrix row. See the sample in + Mat::isContinuous to know how to use these methods. + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + uchar* ptr(int row, int col = 0) + { + return const_cast(const_cast(this)->ptr(row,col)); + } + /** @overload */ + const uchar* ptr(int row, int col = 0) const + { + return data + step * row + CV_ELEM_SIZE(type()) * col; + } + + + private: + //actual memory allocated for storage, or nullptr if object is non owning view to over memory + std::shared_ptr memory; + }; + +} //namespace own +} //namespace gapi +} //namespace cv + +#endif /* OPENCV_GAPI_OWN_MAT_HPP */ diff --git a/modules/gapi/include/opencv2/gapi/own/saturate.hpp b/modules/gapi/include/opencv2/gapi/own/saturate.hpp new file mode 100644 index 00000000000..74eaecf57e6 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/saturate.hpp @@ -0,0 +1,83 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_OWN_SATURATE_HPP +#define OPENCV_GAPI_OWN_SATURATE_HPP + +#include + +#include + +#include +#include + +namespace cv { namespace gapi { namespace own { +//----------------------------- +// +// Numeric cast with saturation +// +//----------------------------- + +template::value && + std::is_integral::value && + std::is_integral::value> > +static CV_ALWAYS_INLINE DST saturate(SRC x) +{ + if (sizeof(DST) > sizeof(SRC)) + return static_cast(x); + + // compiler must recognize this saturation, + // so compile saturate(a + b) with adds + // instruction (e.g.: _mm_adds_epi16 if x86) + return x < std::numeric_limits::min()? + std::numeric_limits::min(): + x > std::numeric_limits::max()? + std::numeric_limits::max(): + static_cast(x); +} +template +static CV_ALWAYS_INLINE T saturate(T x) +{ + return x; +} + +template::value, bool> = true > +static CV_ALWAYS_INLINE DST saturate(SRC x, R) +{ + return static_cast(x); +} +template::value && + std::is_integral::value , bool> = true > +static CV_ALWAYS_INLINE DST saturate(SRC x, R) +{ + return saturate(x); +} +// Note, that OpenCV rounds differently: +// - like std::round() for add, subtract +// - like std::rint() for multiply, divide +template::value && + std::is_floating_point::value, bool> = true > +static CV_ALWAYS_INLINE DST saturate(SRC x, R round) +{ + int ix = static_cast(round(x)); + return saturate(ix); +} + +// explicit suffix 'd' for double type +inline double ceild(double x) { return ceil(x); } +inline double floord(double x) { return floor(x); } +inline double roundd(double x) { return round(x); } +inline double rintd(double x) { return rint(x); } + +} //namespace own +} //namespace gapi +} //namespace cv +#endif /* OPENCV_GAPI_OWN_SATURATE_HPP */ diff --git a/modules/gapi/include/opencv2/gapi/own/scalar.hpp b/modules/gapi/include/opencv2/gapi/own/scalar.hpp new file mode 100644 index 00000000000..3b107befcca --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/scalar.hpp @@ -0,0 +1,47 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_GAPI_OWN_SCALAR_HPP +#define OPENCV_GAPI_GAPI_OWN_SCALAR_HPP + +#include + +namespace cv +{ +namespace gapi +{ +namespace own +{ + +class GAPI_EXPORTS Scalar +{ +public: + Scalar() = default; + explicit Scalar(double v0) { val[0] = v0; } + Scalar(double v0, double v1, double v2 = 0, double v3 = 0) + : val{v0, v1, v2, v3} + { + } + + const double& operator[](int i) const { return val[i]; } + double& operator[](int i) { return val[i]; } + + static Scalar all(double v0) { return Scalar(v0, v0, v0, v0); } + + double val[4] = {0}; +}; + +inline bool operator==(const Scalar& lhs, const Scalar& rhs) +{ + return std::equal(std::begin(lhs.val), std::end(lhs.val), std::begin(rhs.val)); +} + +} // namespace own +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_GAPI_OWN_SCALAR_HPP diff --git a/modules/gapi/include/opencv2/gapi/own/types.hpp b/modules/gapi/include/opencv2/gapi/own/types.hpp new file mode 100644 index 00000000000..211b5c85ff1 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/own/types.hpp @@ -0,0 +1,162 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_TYPES_HPP +#define OPENCV_GAPI_TYPES_HPP + +#include // std::max, std::min +#include + +namespace cv +{ +namespace gapi +{ + +/** + * @brief This namespace contains G-API own data structures used in + * its standalone mode build. + */ +namespace own +{ + +class Point +{ +public: + Point() = default; + Point(int _x, int _y) : x(_x), y(_y) {} + + int x = 0; + int y = 0; +}; + +class Point2f +{ +public: + Point2f() = default; + Point2f(float _x, float _y) : x(_x), y(_y) {} + + float x = 0.f; + float y = 0.f; +}; + +class Point3f +{ +public: + Point3f() = default; + Point3f(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + + float x = 0.f; + float y = 0.f; + float z = 0.f; +}; + +class Rect +{ +public: + Rect() = default; + Rect(int _x, int _y, int _width, int _height) : x(_x), y(_y), width(_width), height(_height) {} +#if !defined(GAPI_STANDALONE) + Rect(const cv::Rect& other) : x(other.x), y(other.y), width(other.width), height(other.height) {} + inline Rect& operator=(const cv::Rect& other) + { + x = other.x; + y = other.x; + width = other.width; + height = other.height; + return *this; + } +#endif // !defined(GAPI_STANDALONE) + + int x = 0; //!< x coordinate of the top-left corner + int y = 0; //!< y coordinate of the top-left corner + int width = 0; //!< width of the rectangle + int height = 0; //!< height of the rectangle +}; + +inline bool operator==(const Rect& lhs, const Rect& rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height; +} + +inline bool operator!=(const Rect& lhs, const Rect& rhs) +{ + return !(lhs == rhs); +} + +inline Rect& operator&=(Rect& lhs, const Rect& rhs) +{ + int x1 = std::max(lhs.x, rhs.x); + int y1 = std::max(lhs.y, rhs.y); + lhs.width = std::min(lhs.x + lhs.width, rhs.x + rhs.width) - x1; + lhs.height = std::min(lhs.y + lhs.height, rhs.y + rhs.height) - y1; + lhs.x = x1; + lhs.y = y1; + if( lhs.width <= 0 || lhs.height <= 0 ) + lhs = Rect(); + return lhs; +} + +inline Rect operator&(const Rect& lhs, const Rect& rhs) +{ + Rect result = lhs; + return result &= rhs; +} + +inline std::ostream& operator<<(std::ostream& o, const Rect& rect) +{ + return o << "[" << rect.width << " x " << rect.height << " from (" << rect.x << ", " << rect.y << ")]"; +} + +class Size +{ +public: + Size() = default; + Size(int _width, int _height) : width(_width), height(_height) {} +#if !defined(GAPI_STANDALONE) + Size(const cv::Size& other) : width(other.width), height(other.height) {} + inline Size& operator=(const cv::Size& rhs) + { + width = rhs.width; + height = rhs.height; + return *this; + } +#endif // !defined(GAPI_STANDALONE) + + int width = 0; + int height = 0; +}; + +inline Size& operator+=(Size& lhs, const Size& rhs) +{ + lhs.width += rhs.width; + lhs.height += rhs.height; + return lhs; +} + +inline bool operator==(const Size& lhs, const Size& rhs) +{ + return lhs.width == rhs.width && lhs.height == rhs.height; +} + +inline bool operator!=(const Size& lhs, const Size& rhs) +{ + return !(lhs == rhs); +} + + +inline std::ostream& operator<<(std::ostream& o, const Size& s) +{ + o << "[" << s.width << " x " << s.height << "]"; + return o; +} + +struct VoidType {}; +} // namespace own +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_TYPES_HPP diff --git a/modules/gapi/include/opencv2/gapi/plaidml/core.hpp b/modules/gapi/include/opencv2/gapi/plaidml/core.hpp new file mode 100644 index 00000000000..20e8812b3ab --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/plaidml/core.hpp @@ -0,0 +1,20 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + + +#ifndef OPENCV_GAPI_PLAIDML_CORE_HPP +#define OPENCV_GAPI_PLAIDML_CORE_HPP + +#include // GKernelPackage +#include // GAPI_EXPORTS + +namespace cv { namespace gapi { namespace core { namespace plaidml { + +GAPI_EXPORTS cv::GKernelPackage kernels(); + +}}}} + +#endif // OPENCV_GAPI_PLAIDML_CORE_HPP diff --git a/modules/gapi/include/opencv2/gapi/plaidml/gplaidmlkernel.hpp b/modules/gapi/include/opencv2/gapi/plaidml/gplaidmlkernel.hpp new file mode 100644 index 00000000000..e22ecc7211f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/plaidml/gplaidmlkernel.hpp @@ -0,0 +1,140 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation +// + + +#ifndef OPENCV_GAPI_GPLAIDMLKERNEL_HPP +#define OPENCV_GAPI_GPLAIDMLKERNEL_HPP + +#include +#include + +namespace plaidml +{ +namespace edsl +{ + class Tensor; +} // namespace edsl +} // namespace plaidml + +namespace cv +{ +namespace gapi +{ +namespace plaidml +{ + +GAPI_EXPORTS cv::gapi::GBackend backend(); + +} // namespace plaidml +} // namespace gapi + +struct GPlaidMLContext +{ + // Generic accessor API + template + const T& inArg(int input) { return m_args.at(input).get(); } + + // Syntax sugar + const plaidml::edsl::Tensor& inTensor(int input) + { + return inArg(input); + } + + plaidml::edsl::Tensor& outTensor(int output) + { + return *(m_results.at(output).get()); + } + + std::vector m_args; + std::unordered_map m_results; +}; + +class GAPI_EXPORTS GPlaidMLKernel +{ +public: + using F = std::function; + + GPlaidMLKernel() = default; + explicit GPlaidMLKernel(const F& f) : m_f(f) {} + + void apply(GPlaidMLContext &ctx) const + { + GAPI_Assert(m_f); + m_f(ctx); + } + +protected: + F m_f; +}; + + +namespace detail +{ + +template struct plaidml_get_in; +template<> struct plaidml_get_in +{ + static const plaidml::edsl::Tensor& get(GPlaidMLContext& ctx, int idx) + { + return ctx.inTensor(idx); + } +}; + +template struct plaidml_get_in +{ + static T get(GPlaidMLContext &ctx, int idx) { return ctx.inArg(idx); } +}; + +template struct plaidml_get_out; +template<> struct plaidml_get_out +{ + static plaidml::edsl::Tensor& get(GPlaidMLContext& ctx, int idx) + { + return ctx.outTensor(idx); + } +}; + +template +struct PlaidMLCallHelper; + +template +struct PlaidMLCallHelper, std::tuple > +{ + template + static void call_impl(GPlaidMLContext &ctx, detail::Seq, detail::Seq) + { + Impl::run(plaidml_get_in::get(ctx, IIs)..., plaidml_get_out::get(ctx, OIs)...); + } + + static void call(GPlaidMLContext& ctx) + { + call_impl(ctx, + typename detail::MkSeq::type(), + typename detail::MkSeq::type()); + } +}; + +} // namespace detail + +template +class GPlaidMLKernelImpl: public cv::detail::PlaidMLCallHelper, + public cv::detail::KernelTag +{ + using P = detail::PlaidMLCallHelper; + +public: + using API = K; + + static cv::gapi::GBackend backend() { return cv::gapi::plaidml::backend(); } + static cv::GPlaidMLKernel kernel() { return GPlaidMLKernel(&P::call); } +}; + +#define GAPI_PLAIDML_KERNEL(Name, API) struct Name: public cv::GPlaidMLKernelImpl + +} // namespace cv + +#endif // OPENCV_GAPI_GPLAIDMLKERNEL_HPP diff --git a/modules/gapi/include/opencv2/gapi/plaidml/plaidml.hpp b/modules/gapi/include/opencv2/gapi/plaidml/plaidml.hpp new file mode 100644 index 00000000000..3207a8cb2e4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/plaidml/plaidml.hpp @@ -0,0 +1,53 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + + +#ifndef OPENCV_GAPI_PLAIDML_PLAIDML_HPP +#define OPENCV_GAPI_PLAIDML_PLAIDML_HPP + +#include +#include // CompileArgTag + +namespace cv +{ +namespace gapi +{ + +/** + * @brief This namespace contains G-API PlaidML backend functions, + * structures, and symbols. + */ +namespace plaidml +{ + +/** \addtogroup gapi_compile_args + * @{ + */ +/** + * @brief This structure represents the basic parameters for the experimental + * PlaidML backend. + */ +struct config +{ + std::string dev_id; //!< Device ID. Refer to PlaidML documentation for details. + std::string trg_id; //!< Target ID. Refer to PlaidML documentation for details. +}; +/** @} gapi_compile_args */ + +} // namespace plaidml +} // namespace gapi + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.plaidml.config"; } + }; +} // namespace detail + +} // namespace cv + +#endif // OPENCV_GAPI_PLAIDML_PLAIDML_HPP diff --git a/modules/gapi/include/opencv2/gapi/python/python.hpp b/modules/gapi/include/opencv2/gapi/python/python.hpp new file mode 100644 index 00000000000..1857a938d5b --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/python/python.hpp @@ -0,0 +1,71 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + + +#ifndef OPENCV_GAPI_PYTHON_API_HPP +#define OPENCV_GAPI_PYTHON_API_HPP + +#include // GKernelPackage +#include // GAPI_EXPORTS + +namespace cv { +namespace gapi { + +/** + * @brief This namespace contains G-API Python backend functions, + * structures, and symbols. + * + * This functionality is required to enable G-API custom operations + * and kernels when using G-API from Python, no need to use it in the + * C++ form. + */ +namespace python { + +GAPI_EXPORTS cv::gapi::GBackend backend(); + +struct GPythonContext +{ + const cv::GArgs &ins; + const cv::GMetaArgs &in_metas; + const cv::GTypesInfo &out_info; + + cv::optional m_state; +}; + +using Impl = std::function; +using Setup = std::function; + +class GAPI_EXPORTS GPythonKernel +{ +public: + GPythonKernel() = default; + GPythonKernel(Impl run, Setup setup); + + Impl run; + Setup setup = nullptr; + bool is_stateful = false; +}; + +class GAPI_EXPORTS GPythonFunctor : public cv::gapi::GFunctor +{ +public: + using Meta = cv::GKernel::M; + + GPythonFunctor(const char* id, const Meta& meta, const Impl& impl, + const Setup& setup = nullptr); + + GKernelImpl impl() const override; + gapi::GBackend backend() const override; + +private: + GKernelImpl impl_; +}; + +} // namespace python +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_PYTHON_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/render.hpp b/modules/gapi/include/opencv2/gapi/render.hpp new file mode 100644 index 00000000000..52e55b0d800 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/render.hpp @@ -0,0 +1,14 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_RENDER_ROOT_HPP +#define OPENCV_GAPI_RENDER_ROOT_HPP + +// This file is just a shortcut to render/render.hpp + +#include + +#endif // OPENCV_GAPI_RENDER_ROOT_HPP diff --git a/modules/gapi/include/opencv2/gapi/render/render.hpp b/modules/gapi/include/opencv2/gapi/render/render.hpp new file mode 100644 index 00000000000..8d93a6efc02 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/render/render.hpp @@ -0,0 +1,196 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2020 Intel Corporation + + +#ifndef OPENCV_GAPI_RENDER_HPP +#define OPENCV_GAPI_RENDER_HPP + +#include + +#include + +/** \defgroup gapi_draw G-API Drawing and composition functionality + * @{ + * + * @brief Functions for in-graph drawing. + * + * @note This is a Work in Progress functionality and APIs may + * change in the future releases. + * + * G-API can do some in-graph drawing with a generic operations and a + * set of [rendering primitives](@ref gapi_draw_prims). + * In contrast with traditional OpenCV, in G-API user need to form a + * *rendering list* of primitives to draw. This list can be built + * manually or generated within a graph. This list is passed to + * [special operations or functions](@ref gapi_draw_api) where all + * primitives are interpreted and applied to the image. + * + * For example, in a complex pipeline a list of detected objects + * can be translated in-graph to a list of cv::gapi::wip::draw::Rect + * primitives to highlight those with bounding boxes, or a list of + * detected faces can be translated in-graph to a list of + * cv::gapi::wip::draw::Mosaic primitives to hide sensitive content + * or protect privacy. + * + * Like any other operations, rendering in G-API can be reimplemented + * by different backends. Currently only an OpenCV-based backend is + * available. + * + * In addition to the graph-level operations, there are also regular + * (immediate) OpenCV-like functions are available -- see + * cv::gapi::wip::draw::render(). These functions are just wrappers + * over regular G-API and build the rendering graphs on the fly, so + * take compilation arguments as parameters. + * + * Currently this API is more machine-oriented than human-oriented. + * The main purpose is to translate a set of domain-specific objects + * to a list of primitives to draw. For example, in order to generate + * a picture like this: + * + * ![](modules/gapi/doc/pics/render_example.png) + * + * Rendering list needs to be generated as follows: + * + * @include modules/gapi/samples/draw_example.cpp + * + * @defgroup gapi_draw_prims Drawing primitives + * @defgroup gapi_draw_api Drawing operations and functions + * @} + */ + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +using GMat2 = std::tuple; +using GMatDesc2 = std::tuple; + +//! @addtogroup gapi_draw_api +//! @{ +/** @brief The function renders on the input image passed drawing primitivies + +@param bgr input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@param prims vector of drawing primitivies +@param args graph compile time parameters +*/ +void GAPI_EXPORTS_W render(cv::Mat& bgr, + const Prims& prims, + cv::GCompileArgs&& args = {}); + +/** @brief The function renders on two NV12 planes passed drawing primitivies + +@param y_plane input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param uv_plane input image: 8-bit unsigned 2-channel image @ref CV_8UC2. +@param prims vector of drawing primitivies +@param args graph compile time parameters +*/ +void GAPI_EXPORTS_W render(cv::Mat& y_plane, + cv::Mat& uv_plane, + const Prims& prims, + cv::GCompileArgs&& args = {}); + +/** @brief The function renders on the input media frame passed drawing primitivies + +@param frame input Media Frame : @ref cv::MediaFrame. +@param prims vector of drawing primitivies +@param args graph compile time parameters +*/ +void GAPI_EXPORTS render(cv::MediaFrame& frame, + const Prims& prims, + cv::GCompileArgs&& args = {}); + + +G_TYPED_KERNEL_M(GRenderNV12, )>, "org.opencv.render.nv12") +{ + static GMatDesc2 outMeta(GMatDesc y_plane, GMatDesc uv_plane, GArrayDesc) + { + return std::make_tuple(y_plane, uv_plane); + } +}; + +G_TYPED_KERNEL(GRenderBGR, )>, "org.opencv.render.bgr") +{ + static GMatDesc outMeta(GMatDesc bgr, GArrayDesc) + { + return bgr; + } +}; + +G_TYPED_KERNEL(GRenderFrame, )>, "org.opencv.render.frame") +{ + static GFrameDesc outMeta(GFrameDesc desc, GArrayDesc) + { + return desc; + } +}; + +/** @brief Renders on 3 channels input + +Output image must be 8-bit unsigned planar 3-channel image + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3 +@param prims draw primitives +*/ +GAPI_EXPORTS_W GMat render3ch(const GMat& src, const GArray& prims); + +/** @brief Renders on two planes + +Output y image must be 8-bit unsigned planar 1-channel image @ref CV_8UC1 +uv image must be 8-bit unsigned planar 2-channel image @ref CV_8UC2 + +@param y input image: 8-bit unsigned 1-channel image @ref CV_8UC1 +@param uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2 +@param prims draw primitives +*/ +GAPI_EXPORTS_W GMat2 renderNV12(const GMat& y, + const GMat& uv, + const GArray& prims); + +/** @brief Renders Media Frame + +Output media frame frame cv::MediaFrame + +@param m_frame input image: cv::MediaFrame @ref cv::MediaFrame +@param prims draw primitives +*/ +GAPI_EXPORTS GFrame renderFrame(const GFrame& m_frame, + const GArray& prims); + +//! @} gapi_draw_api + +} // namespace draw +} // namespace wip + +/** + * @brief This namespace contains G-API CPU rendering backend functions, + * structures, and symbols. See @ref gapi_draw for details. + */ +namespace render +{ +namespace ocv +{ + GAPI_EXPORTS_W cv::GKernelPackage kernels(); + +} // namespace ocv +} // namespace render +} // namespace gapi + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.freetype_font"; } + }; +} // namespace detail + +} // namespace cv + +#endif // OPENCV_GAPI_RENDER_HPP diff --git a/modules/gapi/include/opencv2/gapi/render/render_types.hpp b/modules/gapi/include/opencv2/gapi/render/render_types.hpp new file mode 100644 index 00000000000..6d70e3a877d --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/render/render_types.hpp @@ -0,0 +1,359 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + + +#ifndef OPENCV_GAPI_RENDER_TYPES_HPP +#define OPENCV_GAPI_RENDER_TYPES_HPP + +#include +#include + +#include +#include +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +/** + * @brief This structure specifies which FreeType font to use by FText primitives. + */ +struct freetype_font +{ + /*@{*/ + std::string path; //!< The path to the font file (.ttf) + /*@{*/ +}; + +//! @addtogroup gapi_draw_prims +//! @{ +/** + * @brief This structure represents a text string to draw. + * + * Parameters match cv::putText(). + */ +struct GAPI_EXPORTS_W_SIMPLE Text +{ + /** + * @brief Text constructor + * + * @param text_ The text string to be drawn + * @param org_ The bottom-left corner of the text string in the image + * @param ff_ The font type, see #HersheyFonts + * @param fs_ The font scale factor that is multiplied by the font-specific base size + * @param color_ The text color + * @param thick_ The thickness of the lines used to draw a text + * @param lt_ The line type. See #LineTypes + * @param bottom_left_origin_ When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner + */ + GAPI_WRAP + Text(const std::string& text_, + const cv::Point& org_, + int ff_, + double fs_, + const cv::Scalar& color_, + int thick_ = 1, + int lt_ = 8, + bool bottom_left_origin_ = false) : + text(text_), org(org_), ff(ff_), fs(fs_), + color(color_), thick(thick_), lt(lt_), bottom_left_origin(bottom_left_origin_) + { + } + + GAPI_WRAP + Text() = default; + + /*@{*/ + GAPI_PROP_RW std::string text; //!< The text string to be drawn + GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the text string in the image + GAPI_PROP_RW int ff; //!< The font type, see #HersheyFonts + GAPI_PROP_RW double fs; //!< The font scale factor that is multiplied by the font-specific base size + GAPI_PROP_RW cv::Scalar color; //!< The text color + GAPI_PROP_RW int thick; //!< The thickness of the lines used to draw a text + GAPI_PROP_RW int lt; //!< The line type. See #LineTypes + GAPI_PROP_RW bool bottom_left_origin; //!< When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner + /*@{*/ +}; + +/** + * @brief This structure represents a text string to draw using + * FreeType renderer. + * + * If OpenCV is built without FreeType support, this primitive will + * fail at the execution stage. + */ +struct FText +{ + /** + * @brief FText constructor + * + * @param text_ The text string to be drawn + * @param org_ The bottom-left corner of the text string in the image + * @param fh_ The height of text + * @param color_ The text color + */ + FText(const std::wstring& text_, + const cv::Point& org_, + int fh_, + const cv::Scalar& color_) : + text(text_), org(org_), fh(fh_), color(color_) + { + } + + FText() = default; + + /*@{*/ + std::wstring text; //!< The text string to be drawn + cv::Point org; //!< The bottom-left corner of the text string in the image + int fh; //!< The height of text + cv::Scalar color; //!< The text color + /*@{*/ +}; + +/** + * @brief This structure represents a rectangle to draw. + * + * Parameters match cv::rectangle(). + */ +struct GAPI_EXPORTS_W_SIMPLE Rect +{ + /** + * @brief Rect constructor + * + * @param rect_ Coordinates of the rectangle + * @param color_ The bottom-left corner of the text string in the image + * @param thick_ The thickness of lines that make up the rectangle. Negative values, like #FILLED, mean that the function has to draw a filled rectangle + * @param lt_ The type of the line. See #LineTypes + * @param shift_ The number of fractional bits in the point coordinates + */ + Rect(const cv::Rect& rect_, + const cv::Scalar& color_, + int thick_ = 1, + int lt_ = 8, + int shift_ = 0) : + rect(rect_), color(color_), thick(thick_), lt(lt_), shift(shift_) + { + } + + GAPI_WRAP + Rect() = default; + + /*@{*/ + GAPI_PROP_RW cv::Rect rect; //!< Coordinates of the rectangle + GAPI_PROP_RW cv::Scalar color; //!< The rectangle color or brightness (grayscale image) + GAPI_PROP_RW int thick; //!< The thickness of lines that make up the rectangle. Negative values, like #FILLED, mean that the function has to draw a filled rectangle + GAPI_PROP_RW int lt; //!< The type of the line. See #LineTypes + GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinates + /*@{*/ +}; + +/** + * @brief This structure represents a circle to draw. + * + * Parameters match cv::circle(). + */ +struct GAPI_EXPORTS_W_SIMPLE Circle +{ + /** + * @brief Circle constructor + * + * @param center_ The center of the circle + * @param radius_ The radius of the circle + * @param color_ The color of the circle + * @param thick_ The thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn + * @param lt_ The Type of the circle boundary. See #LineTypes + * @param shift_ The Number of fractional bits in the coordinates of the center and in the radius value + */ + GAPI_WRAP + Circle(const cv::Point& center_, + int radius_, + const cv::Scalar& color_, + int thick_ = 1, + int lt_ = 8, + int shift_ = 0) : + center(center_), radius(radius_), color(color_), thick(thick_), lt(lt_), shift(shift_) + { + } + + GAPI_WRAP + Circle() = default; + + /*@{*/ + GAPI_PROP_RW cv::Point center; //!< The center of the circle + GAPI_PROP_RW int radius; //!< The radius of the circle + GAPI_PROP_RW cv::Scalar color; //!< The color of the circle + GAPI_PROP_RW int thick; //!< The thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn + GAPI_PROP_RW int lt; //!< The Type of the circle boundary. See #LineTypes + GAPI_PROP_RW int shift; //!< The Number of fractional bits in the coordinates of the center and in the radius value + /*@{*/ +}; + +/** + * @brief This structure represents a line to draw. + * + * Parameters match cv::line(). + */ +struct GAPI_EXPORTS_W_SIMPLE Line +{ + /** + * @brief Line constructor + * + * @param pt1_ The first point of the line segment + * @param pt2_ The second point of the line segment + * @param color_ The line color + * @param thick_ The thickness of line + * @param lt_ The Type of the line. See #LineTypes + * @param shift_ The number of fractional bits in the point coordinates + */ + GAPI_WRAP + Line(const cv::Point& pt1_, + const cv::Point& pt2_, + const cv::Scalar& color_, + int thick_ = 1, + int lt_ = 8, + int shift_ = 0) : + pt1(pt1_), pt2(pt2_), color(color_), thick(thick_), lt(lt_), shift(shift_) + { + } + + GAPI_WRAP + Line() = default; + + /*@{*/ + GAPI_PROP_RW cv::Point pt1; //!< The first point of the line segment + GAPI_PROP_RW cv::Point pt2; //!< The second point of the line segment + GAPI_PROP_RW cv::Scalar color; //!< The line color + GAPI_PROP_RW int thick; //!< The thickness of line + GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes + GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinates + /*@{*/ +}; + +/** + * @brief This structure represents a mosaicing operation. + * + * Mosaicing is a very basic method to obfuscate regions in the image. + */ +struct GAPI_EXPORTS_W_SIMPLE Mosaic +{ + /** + * @brief Mosaic constructor + * + * @param mos_ Coordinates of the mosaic + * @param cellSz_ Cell size (same for X, Y) + * @param decim_ Decimation (0 stands for no decimation) + */ + Mosaic(const cv::Rect& mos_, + int cellSz_, + int decim_) : + mos(mos_), cellSz(cellSz_), decim(decim_) + { + } + + GAPI_WRAP + Mosaic() : cellSz(0), decim(0) {} + + /*@{*/ + GAPI_PROP_RW cv::Rect mos; //!< Coordinates of the mosaic + GAPI_PROP_RW int cellSz; //!< Cell size (same for X, Y) + GAPI_PROP_RW int decim; //!< Decimation (0 stands for no decimation) + /*@{*/ +}; + +/** + * @brief This structure represents an image to draw. + * + * Image is blended on a frame using the specified mask. + */ +struct GAPI_EXPORTS_W_SIMPLE Image +{ + /** + * @brief Mosaic constructor + * + * @param org_ The bottom-left corner of the image + * @param img_ Image to draw + * @param alpha_ Alpha channel for image to draw (same size and number of channels) + */ + GAPI_WRAP + Image(const cv::Point& org_, + const cv::Mat& img_, + const cv::Mat& alpha_) : + org(org_), img(img_), alpha(alpha_) + { + } + + GAPI_WRAP + Image() = default; + + /*@{*/ + GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the image + GAPI_PROP_RW cv::Mat img; //!< Image to draw + GAPI_PROP_RW cv::Mat alpha; //!< Alpha channel for image to draw (same size and number of channels) + /*@{*/ +}; + +/** + * @brief This structure represents a polygon to draw. + */ +struct GAPI_EXPORTS_W_SIMPLE Poly +{ + /** + * @brief Mosaic constructor + * + * @param points_ Points to connect + * @param color_ The line color + * @param thick_ The thickness of line + * @param lt_ The Type of the line. See #LineTypes + * @param shift_ The number of fractional bits in the point coordinate + */ + GAPI_WRAP + Poly(const std::vector& points_, + const cv::Scalar& color_, + int thick_ = 1, + int lt_ = 8, + int shift_ = 0) : + points(points_), color(color_), thick(thick_), lt(lt_), shift(shift_) + { + } + + GAPI_WRAP + Poly() = default; + + /*@{*/ + GAPI_PROP_RW std::vector points; //!< Points to connect + GAPI_PROP_RW cv::Scalar color; //!< The line color + GAPI_PROP_RW int thick; //!< The thickness of line + GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes + GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinate + /*@{*/ +}; + +using Prim = util::variant + < Text + , FText + , Rect + , Circle + , Line + , Mosaic + , Image + , Poly + >; + +using Prims = std::vector; +//! @} gapi_draw_prims + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_RENDER_TYPES_HPP diff --git a/modules/gapi/include/opencv2/gapi/rmat.hpp b/modules/gapi/include/opencv2/gapi/rmat.hpp new file mode 100644 index 00000000000..ed456f901eb --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/rmat.hpp @@ -0,0 +1,162 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_RMAT_HPP +#define OPENCV_GAPI_RMAT_HPP + +#include +#include + +// Forward declaration +namespace cv { +namespace gapi { +namespace s11n { +struct IOStream; +struct IIStream; +} // namespace s11n +} // namespace gapi +} // namespace cv + +namespace cv { + +// "Remote Mat", a general class which provides an abstraction layer over the data +// storage and placement (host, remote device etc) and allows to access this data. +// +// The device specific implementation is hidden in the RMat::IAdapter class +// +// The basic flow is the following: +// * Backend which is aware of the remote device: +// - Implements own AdapterT class which is derived from RMat::IAdapter +// - Wraps device memory into RMat via make_rmat utility function: +// cv::RMat rmat = cv::make_rmat(args); +// +// * End user: +// - Writes the code which works with RMats without any knowledge of the remote device: +// void func(const cv::RMat& in_rmat, cv::RMat& out_rmat) { +// // Fetch input data from the device, get mapped memory for output +// cv::RMat::View in_view = in_rmat.access(Access::R); +// cv::RMat::View out_view = out_rmat.access(Access::W); +// performCalculations(in_view, out_view); +// // data from out_view is transferred to the device when out_view is destroyed +// } +/** \addtogroup gapi_data_structures + * @{ + */ +class GAPI_EXPORTS RMat +{ +public: + // A lightweight wrapper on image data: + // - Doesn't own the memory; + // - Doesn't implement copy semantics (it's assumed that a view is created each time + // wrapped data is being accessed); + // - Has an optional callback which is called when the view is destroyed. + class GAPI_EXPORTS View + { + public: + using DestroyCallback = std::function; + using stepsT = std::vector; + + View() = default; + View(const GMatDesc& desc, uchar* data, const stepsT& steps = {}, DestroyCallback&& cb = nullptr); + View(const GMatDesc& desc, uchar* data, size_t step, DestroyCallback&& cb = nullptr); + + View(const View&) = delete; + View& operator=(const View&) = delete; + View(View&&) = default; + View& operator=(View&& v); + ~View() { if (m_cb) m_cb(); } + + cv::Size size() const { return m_desc.size; } + const std::vector& dims() const { return m_desc.dims; } + int cols() const { return m_desc.size.width; } + int rows() const { return m_desc.size.height; } + int type() const; + int depth() const { return m_desc.depth; } + int chan() const { return m_desc.chan; } + size_t elemSize() const { return CV_ELEM_SIZE(type()); } + + template T* ptr(int y = 0) { + return reinterpret_cast(m_data + step()*y); + } + template const T* ptr(int y = 0) const { + return reinterpret_cast(m_data + step()*y); + } + template T* ptr(int y, int x) { + return reinterpret_cast(m_data + step()*y + step(1)*x); + } + template const T* ptr(int y, int x) const { + return reinterpret_cast(m_data + step()*y + step(1)*x); + } + size_t step(size_t i = 0) const { + GAPI_DbgAssert(i; + + RMat() = default; + RMat(AdapterP&& a) : m_adapter(std::move(a)) {} + GMatDesc desc() const { return m_adapter->desc(); } + + // Note: When accessed for write there is no guarantee that returned view + // will contain actual snapshot of the mapped device memory + // (no guarantee that fetch from a device is performed). The only + // guaranty is that when the view is destroyed, its data will be + // transferred to the device + View access(Access a) const { return m_adapter->access(a); } + + // Cast underlying RMat adapter to the particular adapter type, + // return nullptr if underlying type is different + template T* get() const + { + static_assert(std::is_base_of::value, "T is not derived from IAdapter!"); + GAPI_Assert(m_adapter != nullptr); + return dynamic_cast(m_adapter.get()); + } + + void serialize(cv::gapi::s11n::IOStream& os) const { + m_adapter->serialize(os); + } + +private: + AdapterP m_adapter = nullptr; +}; + +template +RMat make_rmat(Ts&&... args) { return { std::make_shared(std::forward(args)...) }; } +/** @} */ + +} //namespace cv + +#endif /* OPENCV_GAPI_RMAT_HPP */ diff --git a/modules/gapi/include/opencv2/gapi/s11n.hpp b/modules/gapi/include/opencv2/gapi/s11n.hpp new file mode 100644 index 00000000000..a94f55c249a --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/s11n.hpp @@ -0,0 +1,513 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020-2021 Intel Corporation + +#ifndef OPENCV_GAPI_S11N_HPP +#define OPENCV_GAPI_S11N_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +// FIXME: caused by deserialize_runarg +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) +#endif + +namespace cv { +namespace gapi { + +/** +* \addtogroup gapi_serialization +* @{ +*/ + +namespace detail { + GAPI_EXPORTS cv::GComputation getGraph(const std::vector &bytes); + + GAPI_EXPORTS cv::GMetaArgs getMetaArgs(const std::vector &bytes); + + GAPI_EXPORTS cv::GRunArgs getRunArgs(const std::vector &bytes); + + GAPI_EXPORTS std::vector getVectorOfStrings(const std::vector &bytes); + + template + cv::GCompileArgs getCompileArgs(const std::vector &bytes); + + template + cv::GRunArgs getRunArgsWithAdapters(const std::vector &bytes); +} // namespace detail + +/** @brief Serialize a graph represented by GComputation into an array of bytes. + * + * Check different overloads for more examples. + * @param c GComputation to serialize. + * @return serialized vector of bytes. + */ +GAPI_EXPORTS std::vector serialize(const cv::GComputation &c); + +/** @overload + * @param ca GCompileArgs to serialize. + */ +GAPI_EXPORTS std::vector serialize(const cv::GCompileArgs& ca); + +/** @overload + * @param ma GMetaArgs to serialize. + */ +GAPI_EXPORTS std::vector serialize(const cv::GMetaArgs& ma); + +/** @overload + * @param ra GRunArgs to serialize. + */ +GAPI_EXPORTS std::vector serialize(const cv::GRunArgs& ra); + +/** @overload + * @param vs std::vector to serialize. + */ +GAPI_EXPORTS std::vector serialize(const std::vector& vs); + +/** + * @private + */ +template static inline +T deserialize(const std::vector &bytes); + +/** @brief Deserialize GComputation from a byte array. + * + * Check different overloads for more examples. + * @param bytes serialized vector of bytes. + * @return deserialized GComputation object. + */ +template<> inline +cv::GComputation deserialize(const std::vector &bytes) { + return detail::getGraph(bytes); +} + +/** @brief Deserialize GMetaArgs from a byte array. + * + * Check different overloads for more examples. + * @param bytes serialized vector of bytes. + * @return deserialized GMetaArgs object. + */ +template<> inline +cv::GMetaArgs deserialize(const std::vector &bytes) { + return detail::getMetaArgs(bytes); +} + +/** @brief Deserialize GRunArgs from a byte array. + * + * Check different overloads for more examples. + * @param bytes serialized vector of bytes. + * @return deserialized GRunArgs object. + */ +template<> inline +cv::GRunArgs deserialize(const std::vector &bytes) { + return detail::getRunArgs(bytes); +} + +/** @brief Deserialize std::vector from a byte array. + * + * Check different overloads for more examples. + * @param bytes serialized vector of bytes. + * @return deserialized std::vector object. + */ +template<> inline +std::vector deserialize(const std::vector &bytes) { + return detail::getVectorOfStrings(bytes); +} + +/** + * @brief Deserialize GCompileArgs which types were specified in the template from a byte array. + * + * @note cv::gapi::s11n::detail::S11N template specialization must be provided to make a custom type + * in GCompileArgs deserializable. + * + * @param bytes vector of bytes to deserialize GCompileArgs object from. + * @return GCompileArgs object. + * @see GCompileArgs cv::gapi::s11n::detail::S11N + */ +template inline +typename std::enable_if::value, GCompileArgs>:: +type deserialize(const std::vector &bytes) { + return detail::getCompileArgs(bytes); +} + +/** + * @brief Deserialize GRunArgs including RMat and MediaFrame objects if any from a byte array. + * + * Adapter types are specified in the template. + * @note To be used properly specified adapter types must overload their deserialize() method. + * @param bytes vector of bytes to deserialize GRunArgs object from. + * @return GRunArgs including RMat and MediaFrame objects if any. + * @see RMat MediaFrame + */ +template inline +typename std::enable_if::value, GRunArgs>:: +type deserialize(const std::vector &bytes) { + return detail::getRunArgsWithAdapters(bytes); +} +} // namespace gapi +} // namespace cv + +namespace cv { +namespace gapi { +namespace s11n { + +/** @brief This structure is an interface for serialization routines. + * + * It's main purpose is to provide multiple overloads for operator<<() + * with basic C++ in addition to OpenCV/G-API types. + * + * This sctructure can be inherited and further extended with additional types. + * + * For example, it is utilized in cv::gapi::s11n::detail::S11N as input parameter + * in serialize() method. + */ +struct GAPI_EXPORTS IOStream { + virtual ~IOStream() = default; + // Define the native support for basic C++ types at the API level: + virtual IOStream& operator<< (bool) = 0; + virtual IOStream& operator<< (char) = 0; + virtual IOStream& operator<< (unsigned char) = 0; + virtual IOStream& operator<< (short) = 0; + virtual IOStream& operator<< (unsigned short) = 0; + virtual IOStream& operator<< (int) = 0; + virtual IOStream& operator<< (uint32_t) = 0; + virtual IOStream& operator<< (uint64_t) = 0; + virtual IOStream& operator<< (float) = 0; + virtual IOStream& operator<< (double) = 0; + virtual IOStream& operator<< (const std::string&) = 0; +}; + +/** @brief This structure is an interface for deserialization routines. + * + * It's main purpose is to provide multiple overloads for operator>>() + * with basic C++ in addition to OpenCV/G-API types. + * + * This structure can be inherited and further extended with additional types. + * + * For example, it is utilized in cv::gapi::s11n::detail::S11N as input parameter + * in deserialize() method. + */ +struct GAPI_EXPORTS IIStream { + virtual ~IIStream() = default; + virtual IIStream& operator>> (bool &) = 0; + virtual IIStream& operator>> (std::vector::reference) = 0; + virtual IIStream& operator>> (char &) = 0; + virtual IIStream& operator>> (unsigned char &) = 0; + virtual IIStream& operator>> (short &) = 0; + virtual IIStream& operator>> (unsigned short &) = 0; + virtual IIStream& operator>> (int &) = 0; + virtual IIStream& operator>> (float &) = 0; + virtual IIStream& operator>> (double &) = 0; + virtual IIStream& operator >> (uint32_t &) = 0; + virtual IIStream& operator >> (uint64_t &) = 0; + virtual IIStream& operator>> (std::string &) = 0; +}; + +namespace detail { +GAPI_EXPORTS std::unique_ptr getInStream(const std::vector &bytes); +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// S11N operators +// Note: operators for basic types are defined in IIStream/IOStream + +// OpenCV types //////////////////////////////////////////////////////////////// + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point &pt); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point &pt); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point2f &pt); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point2f &pt); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point3f &pt); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point3f &pt); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Size &sz); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Size &sz); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Rect &rc); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Rect &rc); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Scalar &s); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Scalar &s); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Mat &m); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Mat &m); + +// FIXME: for GRunArgs serialization +#if !defined(GAPI_STANDALONE) +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat & um); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat & um); +#endif // !defined(GAPI_STANDALONE) + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::RMat &r); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::RMat &r); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &issptr); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &issptr); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::VectorRef &vr); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::VectorRef &vr); + +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::OpaqueRef &opr); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::OpaqueRef &opr); + +/// @private -- Exclude this function from OpenCV documentation +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::MediaFrame &mf); +/// @private -- Exclude this function from OpenCV documentation +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::MediaFrame &mf); + +// Generic STL types //////////////////////////////////////////////////////////////// +template +IOStream& operator<< (IOStream& os, const std::map &m) { + const uint32_t sz = static_cast(m.size()); + os << sz; + for (const auto& it : m) os << it.first << it.second; + return os; +} +template +IIStream& operator>> (IIStream& is, std::map &m) { + m.clear(); + uint32_t sz = 0u; + is >> sz; + for (std::size_t i = 0; i < sz; ++i) { + K k{}; + V v{}; + is >> k >> v; + m[k] = v; + } + return is; +} + +template +IOStream& operator<< (IOStream& os, const std::unordered_map &m) { + const uint32_t sz = static_cast(m.size()); + os << sz; + for (auto &&it : m) os << it.first << it.second; + return os; +} +template +IIStream& operator>> (IIStream& is, std::unordered_map &m) { + m.clear(); + uint32_t sz = 0u; + is >> sz; + for (std::size_t i = 0; i < sz; ++i) { + K k{}; + V v{}; + is >> k >> v; + m[k] = v; + } + return is; +} + +template +IOStream& operator<< (IOStream& os, const std::vector &ts) { + const uint32_t sz = static_cast(ts.size()); + os << sz; + for (auto &&v : ts) os << v; + return os; +} +template +IIStream& operator>> (IIStream& is, std::vector &ts) { + uint32_t sz = 0u; + is >> sz; + if (sz == 0u) { + ts.clear(); + } + else { + ts.resize(sz); + for (std::size_t i = 0; i < sz; ++i) is >> ts[i]; + } + return is; +} + +// Generic: variant serialization +namespace detail { +template +IOStream& put_v(IOStream&, const V&, std::size_t) { + GAPI_Error("variant>>: requested index is invalid"); +} + +template +IOStream& put_v(IOStream& os, const V& v, std::size_t x) { + return (x == 0u) + ? os << cv::util::get(v) + : put_v(os, v, x-1); +} + +template +IIStream& get_v(IIStream&, V&, std::size_t, std::size_t) { + GAPI_Error("variant<<: requested index is invalid"); +} + +template +IIStream& get_v(IIStream& is, V& v, std::size_t i, std::size_t gi) { + if (i == gi) { + X x{}; + is >> x; + v = V{std::move(x)}; + return is; + } else return get_v(is, v, i+1, gi); +} +} // namespace detail + +//! @overload +template +IOStream& operator<< (IOStream& os, const cv::util::variant &v) { + os << static_cast(v.index()); + return detail::put_v, Ts...>(os, v, v.index()); +} +//! @overload +template +IIStream& operator>> (IIStream& is, cv::util::variant &v) { + int idx = -1; + is >> idx; + GAPI_Assert(idx >= 0 && idx < (int)sizeof...(Ts)); + return detail::get_v, Ts...>(is, v, 0u, idx); +} + +// FIXME: consider a better solution +/// @private -- Exclude this function from OpenCV documentation +template +void getRunArgByIdx (IIStream& is, cv::util::variant &v, uint32_t idx) { + is = detail::get_v, Ts...>(is, v, 0u, idx); +} +} // namespace s11n + +namespace detail +{ +template struct try_deserialize_comparg; + +template<> struct try_deserialize_comparg> { +static cv::util::optional exec(const std::string&, cv::gapi::s11n::IIStream&) { + return { }; + } +}; + +template +struct try_deserialize_comparg> { +static cv::util::optional exec(const std::string& tag, cv::gapi::s11n::IIStream& is) { + if (tag == cv::detail::CompileArgTag::tag()) { + static_assert(cv::gapi::s11n::detail::has_S11N_spec::value, + "cv::gapi::deserialize expects Types to have S11N " + "specializations with deserialization callbacks!"); + return cv::util::optional( + GCompileArg { cv::gapi::s11n::detail::S11N::deserialize(is) }); + } + return try_deserialize_comparg>::exec(tag, is); +} +}; + +template +struct deserialize_arg_with_adapter; + +template +struct deserialize_arg_with_adapter { +static GRunArg exec(cv::gapi::s11n::IIStream& is) { + std::unique_ptr ptr(new TA); + ptr->deserialize(is); + return GRunArg { RA(std::move(ptr)) }; +} +}; + +template +struct deserialize_arg_with_adapter { +static GRunArg exec(cv::gapi::s11n::IIStream&) { + GAPI_Error("No suitable adapter class found during RMat/MediaFrame deserialization. " + "Please, make sure you've passed them in cv::gapi::deserialize() template"); + return GRunArg{}; +} +}; + +template +struct deserialize_runarg { +static GRunArg exec(cv::gapi::s11n::IIStream& is, uint32_t idx) { + if (idx == GRunArg::index_of()) { + // Type or void (if not found) + using TA = typename cv::util::find_adapter_impl::type; + return deserialize_arg_with_adapter::exec(is); + } else if (idx == GRunArg::index_of()) { + // Type or void (if not found) + using TA = typename cv::util::find_adapter_impl::type; + return deserialize_arg_with_adapter::exec(is); + } else { // not an adapter holding type runarg - use default deserialization + GRunArg arg; + getRunArgByIdx(is, arg, idx); + return arg; + } +} +}; + +template +inline cv::util::optional tryDeserializeCompArg(const std::string& tag, + const std::vector& sArg) { + std::unique_ptr pArgIs = cv::gapi::s11n::detail::getInStream(sArg); + return try_deserialize_comparg>::exec(tag, *pArgIs); +} + +template +cv::GCompileArgs getCompileArgs(const std::vector &sArgs) { + cv::GCompileArgs args; + + std::unique_ptr pIs = cv::gapi::s11n::detail::getInStream(sArgs); + cv::gapi::s11n::IIStream& is = *pIs; + + uint32_t sz = 0; + is >> sz; + for (uint32_t i = 0; i < sz; ++i) { + std::string tag; + is >> tag; + + std::vector sArg; + is >> sArg; + + cv::util::optional dArg = + cv::gapi::detail::tryDeserializeCompArg(tag, sArg); + + if (dArg.has_value()) + { + args.push_back(dArg.value()); + } + } + + return args; +} + +template +cv::GRunArgs getRunArgsWithAdapters(const std::vector &bytes) { + std::unique_ptr pIs = cv::gapi::s11n::detail::getInStream(bytes); + cv::gapi::s11n::IIStream& is = *pIs; + cv::GRunArgs args; + + uint32_t sz = 0; + is >> sz; + for (uint32_t i = 0; i < sz; ++i) { + uint32_t idx = 0; + is >> idx; + args.push_back(cv::gapi::detail::deserialize_runarg::exec(is, idx)); + } + + return args; +} +} // namespace detail +/** @} */ + +} // namespace gapi +} // namespace cv + +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#endif // OPENCV_GAPI_S11N_HPP diff --git a/modules/gapi/include/opencv2/gapi/s11n/base.hpp b/modules/gapi/include/opencv2/gapi/s11n/base.hpp new file mode 100644 index 00000000000..760e8515f6a --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/s11n/base.hpp @@ -0,0 +1,80 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020-2021 Intel Corporation + +#ifndef OPENCV_GAPI_S11N_BASE_HPP +#define OPENCV_GAPI_S11N_BASE_HPP + +#include +#include + +namespace cv { +namespace gapi { + +/** + * @brief This namespace contains G-API serialization and + * deserialization functions and data structures. + */ +namespace s11n { +struct IOStream; +struct IIStream; + +namespace detail { + +//! @addtogroup gapi_serialization +//! @{ + +struct NotImplemented { +}; + +/** @brief This structure allows to implement serialization routines for custom types. + * + * The default S11N for custom types is not implemented. + * + * @note When providing an overloaded implementation for S11N with your type + * don't inherit it from NotImplemented structure. + * + * @note There are lots of overloaded >> and << operators for basic and OpenCV/G-API types + * which can be utilized when serializing a custom type. + * + * Example of usage: + * @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp S11N usage + * + */ +template +struct S11N: public NotImplemented { + /** + * @brief This function allows user to serialize their custom type. + * + * @note The default overload throws an exception if called. User need to + * properly overload the function to use it. + */ + static void serialize(IOStream &, const T &) { + GAPI_Error("No serialization routine is provided!"); + } + /** + * @brief This function allows user to deserialize their custom type. + * + * @note The default overload throws an exception if called. User need to + * properly overload the function to use it. + */ + static T deserialize(IIStream &) { + GAPI_Error("No deserialization routine is provided!"); + } +}; + +/// @private -- Exclude this struct from OpenCV documentation +template struct has_S11N_spec { + static constexpr bool value = !std::is_base_of::type>>::value; +}; +//! @} gapi_serialization + +} // namespace detail +} // namespace s11n +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_S11N_BASE_HPP diff --git a/modules/gapi/include/opencv2/gapi/stereo.hpp b/modules/gapi/include/opencv2/gapi/stereo.hpp new file mode 100644 index 00000000000..9b00267082c --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/stereo.hpp @@ -0,0 +1,85 @@ +// 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 distereoibution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STEREO_HPP +#define OPENCV_GAPI_STEREO_HPP + +#include +#include +#include + +namespace cv { +namespace gapi { + +/** + * The enum specified format of result that you get from @ref cv::gapi::stereo. + */ +enum class StereoOutputFormat { + DEPTH_FLOAT16, ///< Floating point 16 bit value, CV_16FC1. + ///< This identifier is deprecated, use DEPTH_16F instead. + DEPTH_FLOAT32, ///< Floating point 32 bit value, CV_32FC1 + ///< This identifier is deprecated, use DEPTH_16F instead. + DISPARITY_FIXED16_11_5, ///< 16 bit signed: first bit for sign, + ///< 10 bits for integer part, + ///< 5 bits for fractional part. + ///< This identifier is deprecated, + ///< use DISPARITY_16Q_10_5 instead. + DISPARITY_FIXED16_12_4, ///< 16 bit signed: first bit for sign, + ///< 11 bits for integer part, + ///< 4 bits for fractional part. + ///< This identifier is deprecated, + ///< use DISPARITY_16Q_11_4 instead. + DEPTH_16F = DEPTH_FLOAT16, ///< Same as DEPTH_FLOAT16 + DEPTH_32F = DEPTH_FLOAT32, ///< Same as DEPTH_FLOAT32 + DISPARITY_16Q_10_5 = DISPARITY_FIXED16_11_5, ///< Same as DISPARITY_FIXED16_11_5 + DISPARITY_16Q_11_4 = DISPARITY_FIXED16_12_4 ///< Same as DISPARITY_FIXED16_12_4 +}; + + +/** + * @brief This namespace contains G-API Operation Types for Stereo and + * related functionality. + */ +namespace calib3d { + +G_TYPED_KERNEL(GStereo, , "org.opencv.stereo") { + static GMatDesc outMeta(const GMatDesc &left, const GMatDesc &right, const StereoOutputFormat of) { + GAPI_Assert(left.chan == 1); + GAPI_Assert(left.depth == CV_8U); + + GAPI_Assert(right.chan == 1); + GAPI_Assert(right.depth == CV_8U); + + switch(of) { + case StereoOutputFormat::DEPTH_FLOAT16: + return left.withDepth(CV_16FC1); + case StereoOutputFormat::DEPTH_FLOAT32: + return left.withDepth(CV_32FC1); + case StereoOutputFormat::DISPARITY_FIXED16_11_5: + case StereoOutputFormat::DISPARITY_FIXED16_12_4: + return left.withDepth(CV_16SC1); + default: + GAPI_Error("Unknown output format!"); + } + } +}; + +} // namespace calib3d + +/** @brief Computes disparity/depth map for the specified stereo-pair. +The function computes disparity or depth map depending on passed StereoOutputFormat argument. + +@param left 8-bit single-channel left image of @ref CV_8UC1 type. +@param right 8-bit single-channel right image of @ref CV_8UC1 type. +@param of enum to specified output kind: depth or disparity and corresponding type +*/ +GAPI_EXPORTS GMat stereo(const GMat& left, + const GMat& right, + const StereoOutputFormat of = StereoOutputFormat::DEPTH_FLOAT32); +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STEREO_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/cap.hpp b/modules/gapi/include/opencv2/gapi/streaming/cap.hpp new file mode 100644 index 00000000000..9c2185c1ab4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/cap.hpp @@ -0,0 +1,149 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_CAP_HPP +#define OPENCV_GAPI_STREAMING_CAP_HPP + +/** + * YOUR ATTENTION PLEASE! + * + * This is a header-only implementation of cv::VideoCapture-based + * Stream source. It is not built by default with G-API as G-API + * doesn't depend on videoio module. + * + * If you want to use it in your application, please make sure + * videioio is available in your OpenCV package and is linked to your + * application. + * + * Note for developers: please don't put videoio dependency in G-API + * because of this file. + */ +#include +#include + +#include +#include +#include + +namespace cv { +namespace gapi { +namespace wip { + +/** + * @brief OpenCV's VideoCapture-based streaming source. + * + * This class implements IStreamSource interface. + * Its constructor takes the same parameters as cv::VideoCapture does. + * + * Please make sure that videoio OpenCV module is available before using + * this in your application (G-API doesn't depend on it directly). + * + * @note stream sources are passed to G-API via shared pointers, so + * please gapi::make_src<> to create objects and ptr() to pass a + * GCaptureSource to cv::gin(). + */ +class GCaptureSource: public IStreamSource +{ +public: + explicit GCaptureSource(int id, const std::map &properties = {}) + : cap(id) { prep(properties); } + + explicit GCaptureSource(const std::string &path, + const std::map &properties = {}) + : cap(path) { prep(properties); } + + void set(int propid, double value) { + cap.set(propid, value); + } + + // TODO: Add more constructor overloads to make it + // fully compatible with VideoCapture's interface. + +protected: + cv::VideoCapture cap; + cv::Mat first; + bool first_pulled = false; + int64_t counter = 0; + + void prep(const std::map &properties) + { + for (const auto &it : properties) { + cap.set(it.first, it.second); + } + + // Prepare first frame to report its meta to engine + // when needed + GAPI_Assert(first.empty()); + cv::Mat tmp; + if (!cap.read(tmp)) + { + GAPI_Error("Couldn't grab the very first frame"); + } + // NOTE: Some decode/media VideoCapture backends continue + // owning the video buffer under cv::Mat so in order to + // process it safely in a highly concurrent pipeline, clone() + // is the only right way. + first = tmp.clone(); + } + + virtual bool pull(cv::gapi::wip::Data &data) override + { + if (!first_pulled) + { + GAPI_Assert(!first.empty()); + first_pulled = true; + data = first; // no need to clone here since it was cloned already + } + else + { + if (!cap.isOpened()) return false; + + cv::Mat frame; + if (!cap.read(frame)) + { + // end-of-stream happened + return false; + } + // Same reason to clone as in prep() + data = frame.clone(); + } + // Tag data with seq_id/ts + const auto now = std::chrono::system_clock::now(); + const auto dur = std::chrono::duration_cast + (now.time_since_epoch()); + data.meta[cv::gapi::streaming::meta_tag::timestamp] = int64_t{dur.count()}; + data.meta[cv::gapi::streaming::meta_tag::seq_id] = int64_t{counter++}; + return true; + } + + virtual GMetaArg descr_of() const override + { + GAPI_Assert(!first.empty()); + return cv::GMetaArg{cv::descr_of(first)}; + } +}; + +// NB: Overload for using from python +GAPI_EXPORTS_W cv::Ptr +inline make_capture_src(const std::string& path, + const std::map& properties = {}) +{ + return make_src(path, properties); +} + +// NB: Overload for using from python +GAPI_EXPORTS_W cv::Ptr +inline make_capture_src(const int id, + const std::map& properties = {}) +{ + return make_src(id, properties); +} + +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_CAP_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/desync.hpp b/modules/gapi/include/opencv2/gapi/streaming/desync.hpp new file mode 100644 index 00000000000..0e04f5beb93 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/desync.hpp @@ -0,0 +1,86 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020-2021 Intel Corporation + + +#ifndef OPENCV_GAPI_GSTREAMING_DESYNC_HPP +#define OPENCV_GAPI_GSTREAMING_DESYNC_HPP + +#include + +#include +#include +#include +#include +#include + +namespace cv { +namespace gapi { +namespace streaming { + +namespace detail { +struct GDesync { + static const char *id() { + return "org.opencv.streaming.desync"; + } + + // An universal yield for desync. + // Yields output objects according to the input Types... + // Reuses gkernel machinery. + // FIXME: This function can be generic and declared in gkernel.hpp + // (it is there already, but a part of GKernelType[M] + template + static std::tuple yield(cv::GCall &call, cv::detail::Seq) { + return std::make_tuple(cv::detail::Yield::yield(call, IIs)...); + } +}; + +template +G desync(const G &g) { + cv::GKernel k{ + GDesync::id() // kernel id + , "" // kernel tag + , [](const GMetaArgs &a, const GArgs &) {return a;} // outMeta callback + , {cv::detail::GTypeTraits::shape} // output Shape + , {cv::detail::GTypeTraits::op_kind} // input data kinds + , {cv::detail::GObtainCtor::get()} // output template ctors + , {cv::detail::GTypeTraits::op_kind} // output data kinds + }; + cv::GCall call(std::move(k)); + call.pass(g); + return std::get<0>(GDesync::yield(call, cv::detail::MkSeq<1>::type())); +} +} // namespace detail + +/** + * @brief Starts a desynchronized branch in the graph. + * + * This operation takes a single G-API data object and returns a + * graph-level "duplicate" of this object. + * + * Operations which use this data object can be desynchronized + * from the rest of the graph. + * + * This operation has no effect when a GComputation is compiled with + * regular cv::GComputation::compile(), since cv::GCompiled objects + * always produce their full output vectors. + * + * This operation only makes sense when a GComputation is compiled in + * streaming mode with cv::GComputation::compileStreaming(). If this + * operation is used and there are desynchronized outputs, the user + * should use a special version of cv::GStreamingCompiled::pull() + * which produces an array of cv::util::optional<> objects. + * + * @note This feature is highly experimental now and is currently + * limited to a single GMat/GFrame argument only. + */ +GAPI_EXPORTS GMat desync(const GMat &g); +GAPI_EXPORTS GFrame desync(const GFrame &f); + +} // namespace streaming +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_GSTREAMING_DESYNC_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/format.hpp b/modules/gapi/include/opencv2/gapi/streaming/format.hpp new file mode 100644 index 00000000000..739a3852a64 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/format.hpp @@ -0,0 +1,94 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_GSTREAMING_FORMAT_HPP +#define OPENCV_GAPI_GSTREAMING_FORMAT_HPP + +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace streaming { + +GAPI_EXPORTS cv::GKernelPackage kernels(); + +G_API_OP(GBGR, , "org.opencv.streaming.BGR") +{ + static GMatDesc outMeta(const GFrameDesc& in) { return GMatDesc{CV_8U, 3, in.size}; } +}; + +G_API_OP(GY, , "org.opencv.streaming.Y") { + static GMatDesc outMeta(const GFrameDesc& frameDesc) { + return GMatDesc { CV_8U, 1, frameDesc.size , false }; + } +}; + +G_API_OP(GUV, , "org.opencv.streaming.UV") { + static GMatDesc outMeta(const GFrameDesc& frameDesc) { + return GMatDesc { CV_8U, 2, cv::Size(frameDesc.size.width / 2, frameDesc.size.height / 2), + false }; + } +}; + +/** @brief Gets bgr plane from input frame + +@note Function textual ID is "org.opencv.streaming.BGR" + +@param in Input frame +@return Image in BGR format +*/ +GAPI_EXPORTS cv::GMat BGR(const cv::GFrame& in); + +/** @brief Extracts Y plane from media frame. + +Output image is 8-bit 1-channel image of @ref CV_8UC1. + +@note Function textual ID is "org.opencv.streaming.Y" + +@param frame input media frame. +*/ +GAPI_EXPORTS GMat Y(const cv::GFrame& frame); + +/** @brief Extracts UV plane from media frame. + +Output image is 8-bit 2-channel image of @ref CV_8UC2. + +@note Function textual ID is "org.opencv.streaming.UV" + +@param frame input media frame. +*/ +GAPI_EXPORTS GMat UV(const cv::GFrame& frame); +} // namespace streaming + +//! @addtogroup gapi_transform +//! @{ +/** @brief Makes a copy of the input image. Note that this copy may be not real +(no actual data copied). Use this function to maintain graph contracts, +e.g when graph's input needs to be passed directly to output, like in Streaming mode. + +@note Function textual ID is "org.opencv.streaming.copy" + +@param in Input image +@return Copy of the input +*/ +GAPI_EXPORTS_W GMat copy(const GMat& in); + +/** @brief Makes a copy of the input frame. Note that this copy may be not real +(no actual data copied). Use this function to maintain graph contracts, +e.g when graph's input needs to be passed directly to output, like in Streaming mode. + +@note Function textual ID is "org.opencv.streaming.copy" + +@param in Input frame +@return Copy of the input +*/ +GAPI_EXPORTS GFrame copy(const GFrame& in); +//! @} gapi_transform + +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_GSTREAMING_FORMAT_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamerpipeline.hpp b/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamerpipeline.hpp new file mode 100644 index 00000000000..c566656cb61 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamerpipeline.hpp @@ -0,0 +1,59 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP +#define OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP + +#include +#include + +#include +#include +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace gst { + +class GAPI_EXPORTS_W GStreamerPipeline +{ +public: + class Priv; + + GAPI_WRAP explicit GStreamerPipeline(const std::string& pipeline); + IStreamSource::Ptr getStreamingSource(const std::string& appsinkName, + const GStreamerSource::OutputType outputType = + GStreamerSource::OutputType::MAT); + virtual ~GStreamerPipeline(); + +protected: + explicit GStreamerPipeline(std::unique_ptr priv); + + std::unique_ptr m_priv; +}; + +} // namespace gst + +using GStreamerPipeline = gst::GStreamerPipeline; + +// NB: Function for using from python +// FIXME: a separate function is created due to absence of wrappers for `shared_ptr<> ` +// Ideally would be to wrap the `GStreamerPipeline::getStreamingSource()` method as is +GAPI_EXPORTS_W cv::Ptr +inline get_streaming_source(cv::Ptr& pipeline, + const std::string& appsinkName, + const GStreamerSource::OutputType outputType + = GStreamerSource::OutputType::MAT) +{ + return pipeline->getStreamingSource(appsinkName, outputType); +} + +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamersource.hpp b/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamersource.hpp new file mode 100644 index 00000000000..8b8a5ae3121 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/gstreamer/gstreamersource.hpp @@ -0,0 +1,97 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP +#define OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP + +#include +#include + +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace gst { + +/** + * @brief OpenCV's GStreamer streaming source. + * Streams cv::Mat-s/cv::MediaFrame from passed GStreamer pipeline. + * + * This class implements IStreamSource interface. + * + * To create GStreamerSource instance you need to pass 'pipeline' and, optionally, 'outputType' + * arguments into constructor. + * 'pipeline' should represent GStreamer pipeline in form of textual description. + * Almost any custom pipeline is supported which can be successfully ran via gst-launch. + * The only two limitations are: + * - there should be __one__ appsink element in the pipeline to pass data to OpenCV app. + * Pipeline can actually contain many sink elements, but it must have one and only one + * appsink among them. + * + * - data passed to appsink should be video-frame in NV12 or GRAY8 format. + * + * 'outputType' is used to select type of output data to produce: 'cv::MediaFrame' or 'cv::Mat'. + * To produce 'cv::MediaFrame'-s you need to pass 'GStreamerSource::OutputType::FRAME' and, + * correspondingly, 'GStreamerSource::OutputType::MAT' to produce 'cv::Mat'-s. + * Please note, that in the last case, output 'cv::Mat' will be of BGR format, internal conversion + * from NV12 / GRAY8 GStreamer data will happen. + * Default value for 'outputType' is 'GStreamerSource::OutputType::MAT'. + * + * @note Stream sources are passed to G-API via shared pointers, so please use gapi::make_src<> + * to create objects and ptr() to pass a GStreamerSource to cv::gin(). + * + * @note You need to build OpenCV with GStreamer support to use this class. + */ + +class GStreamerPipelineFacade; + +class GAPI_EXPORTS GStreamerSource : public IStreamSource +{ +public: + class Priv; + + // Indicates what type of data should be produced by GStreamerSource: cv::MediaFrame or cv::Mat + enum class OutputType { + FRAME, + MAT + }; + + GStreamerSource(const std::string& pipeline, + const GStreamerSource::OutputType outputType = + GStreamerSource::OutputType::MAT); + GStreamerSource(std::shared_ptr pipeline, + const std::string& appsinkName, + const GStreamerSource::OutputType outputType = + GStreamerSource::OutputType::MAT); + + bool pull(cv::gapi::wip::Data& data) override; + GMetaArg descr_of() const override; + ~GStreamerSource() override; + +protected: + explicit GStreamerSource(std::unique_ptr priv); + + std::unique_ptr m_priv; +}; + +} // namespace gst + +using GStreamerSource = gst::GStreamerSource; + +// NB: Overload for using from python +GAPI_EXPORTS_W cv::Ptr +inline make_gst_src(const std::string& pipeline, + const GStreamerSource::OutputType outputType = + GStreamerSource::OutputType::MAT) +{ + return make_src(pipeline, outputType); +} +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/meta.hpp b/modules/gapi/include/opencv2/gapi/streaming/meta.hpp new file mode 100644 index 00000000000..cdd3d371cb4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/meta.hpp @@ -0,0 +1,80 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + + +#ifndef OPENCV_GAPI_GSTREAMING_META_HPP +#define OPENCV_GAPI_GSTREAMING_META_HPP + +#include +#include +#include +#include + +namespace cv { +namespace gapi { +namespace streaming { + +// FIXME: the name is debatable +namespace meta_tag { +static constexpr const char * timestamp = "org.opencv.gapi.meta.timestamp"; +static constexpr const char * seq_id = "org.opencv.gapi.meta.seq_id"; +} // namespace meta_tag + +namespace detail { +struct GMeta { + static const char *id() { + return "org.opencv.streaming.meta"; + } + // A universal yield for meta(), same as in GDesync + template + static std::tuple yield(cv::GCall &call, cv::detail::Seq) { + return std::make_tuple(cv::detail::Yield::yield(call, IIs)...); + } + // Also a universal outMeta stub here + static GMetaArgs getOutMeta(const GMetaArgs &args, const GArgs &) { + return args; + } +}; +} // namespace detail + +template +cv::GOpaque meta(G g, const std::string &tag) { + using O = cv::GOpaque; + cv::GKernel k{ + detail::GMeta::id() // kernel id + , tag // kernel tag. Use meta tag here + , &detail::GMeta::getOutMeta // outMeta callback + , {cv::detail::GTypeTraits::shape} // output Shape + , {cv::detail::GTypeTraits::op_kind} // input data kinds + , {cv::detail::GObtainCtor::get()} // output template ctors + , {cv::detail::GTypeTraits::op_kind} // output data kind + }; + cv::GCall call(std::move(k)); + call.pass(g); + return std::get<0>(detail::GMeta::yield(call, cv::detail::MkSeq<1>::type())); +} + +template +cv::GOpaque timestamp(G g) { + return meta(g, meta_tag::timestamp); +} + +template +cv::GOpaque seq_id(G g) { + return meta(g, meta_tag::seq_id); +} + +template +cv::GOpaque seqNo(G g) { + // Old name, compatibility only + return seq_id(g); +} + +} // namespace streaming +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_GSTREAMING_META_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/accel_types.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/accel_types.hpp new file mode 100644 index 00000000000..b670aebd1d4 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/accel_types.hpp @@ -0,0 +1,76 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2022 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_ACCEL_TYPES_HPP +#define GAPI_STREAMING_ONEVPL_ACCEL_TYPES_HPP + +#include +#include + +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +enum class AccelType: uint8_t { + HOST, + DX11, + VAAPI, + + LAST_VALUE = std::numeric_limits::max() +}; + +GAPI_EXPORTS const char* to_cstring(AccelType type); + +struct IDeviceSelector; +struct GAPI_EXPORTS Device { + friend struct IDeviceSelector; + using Ptr = void*; + + ~Device(); + const std::string& get_name() const; + Ptr get_ptr() const; + AccelType get_type() const; +private: + Device(Ptr device_ptr, const std::string& device_name, + AccelType device_type); + + std::string name; + Ptr ptr; + AccelType type; +}; + +struct GAPI_EXPORTS Context { + friend struct IDeviceSelector; + using Ptr = void*; + + ~Context(); + Ptr get_ptr() const; + AccelType get_type() const; +private: + Context(Ptr ctx_ptr, AccelType ctx_type); + Ptr ptr; + AccelType type; +}; + +GAPI_EXPORTS Device create_host_device(); +GAPI_EXPORTS Context create_host_context(); + +GAPI_EXPORTS Device create_dx11_device(Device::Ptr device_ptr, + const std::string& device_name); +GAPI_EXPORTS Context create_dx11_context(Context::Ptr ctx_ptr); + +GAPI_EXPORTS Device create_vaapi_device(Device::Ptr device_ptr, + const std::string& device_name); +GAPI_EXPORTS Context create_vaapi_context(Context::Ptr ctx_ptr); +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // GAPI_STREAMING_ONEVPL_ACCEL_TYPES_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp new file mode 100644 index 00000000000..0db9a86e58d --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp @@ -0,0 +1,209 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP +#define OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP + +#include +#include +#include + +#include +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +/** + * @brief Public class is using for creation of onevpl::GSource instances. + * + * Class members available through methods @ref CfgParam::get_name() and @ref CfgParam::get_value() are used by + * onevpl::GSource inner logic to create or find oneVPL particular implementation + * (software/hardware, specific API version and etc.). + * + * @note Because oneVPL may provide several implementations which are satisfying with multiple (or single one) @ref CfgParam + * criteria therefore it is possible to configure `preferred` parameters. This kind of CfgParams are created + * using `is_major = false` argument in @ref CfgParam::create method and are not used by creating oneVPL particular implementations. + * Instead they fill out a "score table" to select preferable implementation from available list. Implementation are satisfying + * with most of these optional params would be chosen. + * If no one optional CfgParam params were present then first of available oneVPL implementation would be applied. + * Please get on https://spec.oneapi.io/versions/latest/elements/oneVPL/source/API_ref/VPL_disp_api_func.html?highlight=mfxcreateconfig#mfxsetconfigfilterproperty + * for using OneVPL configuration. In this schema `mfxU8 *name` represents @ref CfgParam::get_name() and + * `mfxVariant value` is @ref CfgParam::get_value() + */ +struct GAPI_EXPORTS CfgParam { + using name_t = std::string; + using value_t = cv::util::variant; + /** + * @brief frames_pool_size_name + * + * Special configuration parameter name for onevp::GSource: + * + * @note frames_pool_size_name allows to allocate surfaces pool appropriate size to keep + * decoded frames in accelerator memory ready before + * they would be consumed by onevp::GSource::pull operation. If you see + * a lot of WARNING about lack of free surface then it's time to increase + * frames_pool_size_name but be aware of accelerator free memory volume. + * If not set then MFX implementation use + * mfxFrameAllocRequest::NumFrameSuggested behavior + * + */ + static constexpr const char *frames_pool_size_name() { return "frames_pool_size"; } + static CfgParam create_frames_pool_size(size_t value); + + /** + * @brief acceleration_mode_name + * + * Special configuration parameter names for onevp::GSource: + * + * @note acceleration_mode_name allows to activate hardware acceleration & + * device memory management. + * Supported values: + * - MFX_ACCEL_MODE_VIA_D3D11 Will activate DX11 acceleration and will produces + * MediaFrames with data allocated in DX11 device memory + * + * If not set then MFX implementation will use default acceleration behavior: + * all decoding operation uses default GPU resources but MediaFrame produces + * data allocated by using host RAM + * + */ + static constexpr const char *acceleration_mode_name() { return "mfxImplDescription.AccelerationMode"; } + static CfgParam create_acceleration_mode(uint32_t value); + static CfgParam create_acceleration_mode(const char* value); + + /** + * @brief decoder_id_name + * + * Special configuration parameter names for onevp::GSource: + * + * @note decoder_id_name allows to specify VPL decoder type which MUST present + * in case of RAW video input data and MUST NOT present as CfgParam if video + * stream incapsulated into container(*.mp4, *.mkv and so on). In latter case + * onevp::GSource will determine it automatically + * Supported values: + * - MFX_CODEC_AVC + * - MFX_CODEC_HEVC + * - MFX_CODEC_MPEG2 + * - MFX_CODEC_VC1 + * - MFX_CODEC_CAPTURE + * - MFX_CODEC_VP9 + * - MFX_CODEC_AV1 + * + */ + static constexpr const char *decoder_id_name() { return "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; } + static CfgParam create_decoder_id(uint32_t value); + static CfgParam create_decoder_id(const char* value); + + static constexpr const char *implementation_name() { return "mfxImplDescription.Impl"; } + static CfgParam create_implementation(uint32_t value); + static CfgParam create_implementation(const char* value); + + + static constexpr const char *vpp_frames_pool_size_name() { return "vpp_frames_pool_size"; } + static CfgParam create_vpp_frames_pool_size(size_t value); + + static constexpr const char *vpp_in_width_name() { return "vpp.In.Width"; } + static CfgParam create_vpp_in_width(uint16_t value); + + static constexpr const char *vpp_in_height_name() { return "vpp.In.Height"; } + static CfgParam create_vpp_in_height(uint16_t value); + + static constexpr const char *vpp_in_crop_x_name() { return "vpp.In.CropX"; } + static CfgParam create_vpp_in_crop_x(uint16_t value); + + static constexpr const char *vpp_in_crop_y_name() { return "vpp.In.CropY"; } + static CfgParam create_vpp_in_crop_y(uint16_t value); + + static constexpr const char *vpp_in_crop_w_name() { return "vpp.In.CropW"; } + static CfgParam create_vpp_in_crop_w(uint16_t value); + + static constexpr const char *vpp_in_crop_h_name() { return "vpp.In.CropH"; } + static CfgParam create_vpp_in_crop_h(uint16_t value); + + + static constexpr const char *vpp_out_fourcc_name() { return "vpp.Out.FourCC"; } + static CfgParam create_vpp_out_fourcc(uint32_t value); + + static constexpr const char *vpp_out_chroma_format_name() { return "vpp.Out.ChromaFormat"; } + static CfgParam create_vpp_out_chroma_format(uint16_t value); + + static constexpr const char *vpp_out_width_name() { return "vpp.Out.Width"; } + static CfgParam create_vpp_out_width(uint16_t value); + + static constexpr const char *vpp_out_height_name() { return "vpp.Out.Height"; } + static CfgParam create_vpp_out_height(uint16_t value); + + static constexpr const char *vpp_out_crop_x_name() { return "vpp.Out.CropX"; } + static CfgParam create_vpp_out_crop_x(uint16_t value); + + static constexpr const char *vpp_out_crop_y_name() { return "vpp.Out.CropY"; } + static CfgParam create_vpp_out_crop_y(uint16_t value); + + static constexpr const char *vpp_out_crop_w_name() { return "vpp.Out.CropW"; } + static CfgParam create_vpp_out_crop_w(uint16_t value); + + static constexpr const char *vpp_out_crop_h_name() { return "vpp.Out.CropH"; } + static CfgParam create_vpp_out_crop_h(uint16_t value); + + static constexpr const char *vpp_out_pic_struct_name() { return "vpp.Out.PicStruct"; } + static CfgParam create_vpp_out_pic_struct(uint16_t value); + + static constexpr const char *vpp_out_framerate_n_name() { return "vpp.Out.FrameRateExtN"; } + static CfgParam create_vpp_out_framerate_n(uint32_t value); + + static constexpr const char *vpp_out_framerate_d_name() { return "vpp.Out.FrameRateExtD"; } + static CfgParam create_vpp_out_framerate_d(uint32_t value); + + /** + * Create generic onevp::GSource configuration parameter. + * + *@param name name of parameter. + *@param value value of parameter. + *@param is_major TRUE if parameter MUST be provided by OneVPL inner implementation, FALSE for optional (for resolve multiple available implementations). + * + */ + template + static CfgParam create(const std::string& name, ValueType&& value, bool is_major = true) { + CfgParam param(name, CfgParam::value_t(std::forward(value)), is_major); + return param; + } + + struct Priv; + + const name_t& get_name() const; + const value_t& get_value() const; + bool is_major() const; + std::string to_string() const; + + bool operator==(const CfgParam& rhs) const; + bool operator< (const CfgParam& rhs) const; + bool operator!=(const CfgParam& rhs) const; + + CfgParam& operator=(const CfgParam& src); + CfgParam& operator=(CfgParam&& src); + CfgParam(const CfgParam& src); + CfgParam(CfgParam&& src); + ~CfgParam(); +private: + CfgParam(const std::string& param_name, value_t&& param_value, bool is_major_param); + std::shared_ptr m_priv; +}; + +} //namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/data_provider_interface.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/data_provider_interface.hpp new file mode 100644 index 00000000000..ec683a7527f --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/data_provider_interface.hpp @@ -0,0 +1,105 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP +#define GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP +#include +#include +#include + +#include // GAPI_EXPORTS +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +struct GAPI_EXPORTS DataProviderException : public std::exception { + DataProviderException(const std::string& descr); + DataProviderException(std::string&& descr); + + virtual ~DataProviderException() = default; + virtual const char* what() const noexcept override; +private: + std::string reason; +}; + +struct GAPI_EXPORTS DataProviderSystemErrorException final : public DataProviderException { + DataProviderSystemErrorException(int error_code, const std::string& description = std::string()); + ~DataProviderSystemErrorException() = default; +}; + +struct GAPI_EXPORTS DataProviderUnsupportedException final : public DataProviderException { + DataProviderUnsupportedException(const std::string& description); + ~DataProviderUnsupportedException() = default; +}; + +struct GAPI_EXPORTS DataProviderImplementationException : public DataProviderException { + DataProviderImplementationException(const std::string& description); + ~DataProviderImplementationException() = default; +}; +/** + * @brief Public interface allows to customize extraction of video stream data + * used by onevpl::GSource instead of reading stream from file (by default). + * + * Interface implementation constructor MUST provide consistency and creates fully operable object. + * If error happened implementation MUST throw `DataProviderException` kind exceptions + * + * @note Interface implementation MUST manage stream and other constructed resources by itself to avoid any kind of leak. + * For simple interface implementation example please see `StreamDataProvider` in `tests/streaming/gapi_streaming_tests.cpp` + */ +struct GAPI_EXPORTS IDataProvider { + using Ptr = std::shared_ptr; + using mfx_codec_id_type = uint32_t; + + /** + * NB: here is supposed to be forward declaration of mfxBitstream + * But according to current oneVPL implementation it is impossible to forward + * declare untagged struct mfxBitstream. + * + * IDataProvider makes sense only for HAVE_VPL is ON and to keep IDataProvider + * interface API/ABI compliant between core library and user application layer + * let's introduce wrapper mfx_bitstream which inherits mfxBitstream in private + * G-API code section and declare forward for wrapper mfx_bitstream here + */ + struct mfx_bitstream; + + virtual ~IDataProvider() = default; + + /** + * The function is used by onevpl::GSource to extract codec id from data + * + */ + virtual mfx_codec_id_type get_mfx_codec_id() const = 0; + + /** + * The function is used by onevpl::GSource to extract binary data stream from @ref IDataProvider + * implementation. + * + * It MUST throw `DataProviderException` kind exceptions in fail cases. + * It MUST return MFX_ERR_MORE_DATA in EOF which considered as not-fail case. + * + * @param in_out_bitsream the input-output reference on MFX bitstream buffer which MUST be empty at the first request + * to allow implementation to allocate it by itself and to return back. Subsequent invocation of `fetch_bitstream_data` + * MUST use the previously used in_out_bitsream to avoid skipping rest of frames which haven't been consumed + * @return true for fetched data, false on EOF and throws exception on error + */ + virtual bool fetch_bitstream_data(std::shared_ptr &in_out_bitsream) = 0; + + /** + * The function is used by onevpl::GSource to check more binary data availability. + * + * It MUST return TRUE in case of EOF and NO_THROW exceptions. + * + * @return boolean value which detects end of stream + */ + virtual bool empty() const = 0; +}; +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp new file mode 100644 index 00000000000..8b547e1aba9 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/default.hpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2022 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP +#define OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP + +#include // GAPI_EXPORTS +#include +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +/** + * @brief Provides default device selector based on config. + */ +GAPI_EXPORTS std::shared_ptr getDefaultDeviceSelector(const std::vector& cfg_params); + +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_ONEVPL_UTILS_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/device_selector_interface.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/device_selector_interface.hpp new file mode 100644 index 00000000000..2e2d879fba6 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/device_selector_interface.hpp @@ -0,0 +1,61 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP +#define GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP + +#include +#include +#include +#include + +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { +struct GAPI_EXPORTS IDeviceSelector { + using Ptr = std::shared_ptr; + + struct GAPI_EXPORTS Score { + friend struct IDeviceSelector; + using Type = int16_t; + static constexpr Type MaxActivePriority = std::numeric_limits::max(); + static constexpr Type MinActivePriority = 0; + static constexpr Type MaxPassivePriority = MinActivePriority - 1; + static constexpr Type MinPassivePriority = std::numeric_limits::min(); + + Score(Type val); + ~Score(); + + operator Type () const; + Type get() const; + friend bool operator< (Score lhs, Score rhs) { + return lhs.get() < rhs.get(); + } + private: + Type value; + }; + + using DeviceScoreTable = std::map; + using DeviceContexts = std::vector; + + virtual ~IDeviceSelector(); + virtual DeviceScoreTable select_devices() const = 0; + virtual DeviceContexts select_context() = 0; +protected: + template + static Entity create(Args &&...args) { + return Entity(std::forward(args)...); + } +}; +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/source.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/source.hpp new file mode 100644 index 00000000000..04dc2e246d3 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/source.hpp @@ -0,0 +1,94 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP +#define OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP + +#include +#include +#include +#include +#include +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { +using CfgParams = std::vector; + +/** + * @brief G-API streaming source based on OneVPL implementation. + * + * This class implements IStreamSource interface. + * Its constructor takes source file path (in usual way) or @ref onevpl::IDataProvider + * interface implementation (for not file-based sources). It also allows to pass-through + * oneVPL configuration parameters by using several @ref onevpl::CfgParam. + * + * @note stream sources are passed to G-API via shared pointers, so + * please gapi::make_onevpl_src<> to create objects and ptr() to pass a + * GSource to cv::gin(). + */ +class GAPI_EXPORTS GSource : public IStreamSource +{ +public: + struct Priv; + + GSource(const std::string& filePath, + const CfgParams& cfg_params = CfgParams{}); + + GSource(const std::string& filePath, + const CfgParams& cfg_params, + const std::string& device_id, + void* accel_device_ptr, + void* accel_ctx_ptr); + + GSource(const std::string& filePath, + const CfgParams& cfg_params, + const Device &device, const Context &ctx); + + GSource(const std::string& filePath, + const CfgParams& cfg_params, + std::shared_ptr selector); + + + GSource(std::shared_ptr source, + const CfgParams& cfg_params = CfgParams{}); + + GSource(std::shared_ptr source, + const CfgParams& cfg_params, + const std::string& device_id, + void* accel_device_ptr, + void* accel_ctx_ptr); + + GSource(std::shared_ptr source, + const CfgParams& cfg_params, + std::shared_ptr selector); + + ~GSource() override; + + bool pull(cv::gapi::wip::Data& data) override; + GMetaArg descr_of() const override; + +private: + explicit GSource(std::unique_ptr&& impl); + std::unique_ptr m_priv; +}; +} // namespace onevpl + +using GVPLSource = onevpl::GSource; + +template +GAPI_EXPORTS_W cv::Ptr inline make_onevpl_src(Args&&... args) +{ + return make_src(std::forward(args)...); +} + +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/queue_source.hpp b/modules/gapi/include/opencv2/gapi/streaming/queue_source.hpp new file mode 100644 index 00000000000..bd385ed16e7 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/queue_source.hpp @@ -0,0 +1,67 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_QUEUE_SOURCE_HPP +#define OPENCV_GAPI_STREAMING_QUEUE_SOURCE_HPP + +#include // shared_ptr +#include // is_base_of + +#include // GRunArgs +#include // GMetaArg + all descr_of +#include // IStreamSource + +namespace cv { +namespace gapi { +namespace wip { +struct Data; // fwd-declare to avoid circular? header dependencies + +class GAPI_EXPORTS QueueSourceBase: public cv::gapi::wip::IStreamSource { + class Priv; + std::shared_ptr m_priv; + // FIXME: Need to understand how it works with IStreamSource's shared_from_this + // Can we avoid having too many shared_ptrs here? + +public: + explicit QueueSourceBase(const cv::GMetaArg &m); + void push(Data &&data); + virtual bool pull(Data &data) override; + virtual void halt() override; + virtual GMetaArg descr_of() const override; + virtual ~QueueSourceBase() = default; +}; + +/** + * @brief Queued streaming pipeline source. + * + */ +template +class QueueSource final: public QueueSourceBase +{ +public: + using Meta = decltype(cv::descr_of(T{})); + explicit QueueSource(Meta m) : QueueSourceBase(GMetaArg{m}) { + } + void push(T t) { + QueueSourceBase::push(Data{t}); + } +}; + +class GAPI_EXPORTS QueueInput { + std::vector > m_sources; + +public: + explicit QueueInput(const cv::GMetaArgs &args); + + void push(cv::GRunArgs &&ins); + operator cv::GRunArgs(); +}; + +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_SOURCE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/source.hpp b/modules/gapi/include/opencv2/gapi/streaming/source.hpp new file mode 100644 index 00000000000..267469ad1b3 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/source.hpp @@ -0,0 +1,67 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_SOURCE_HPP +#define OPENCV_GAPI_STREAMING_SOURCE_HPP + +#include // shared_ptr +#include // is_base_of + +#include // GMetaArg + + +namespace cv { +namespace gapi { +namespace wip { +struct Data; // forward-declaration of Data to avoid circular dependencies + +/** + * @brief Abstract streaming pipeline source. + * + * Implement this interface if you want customize the way how data is + * streaming into GStreamingCompiled. + * + * Objects implementing this interface can be passed to + * GStreamingCompiled using setSource() with cv::gin(). Regular + * compiled graphs (GCompiled) don't support input objects of this + * type. + * + * Default cv::VideoCapture-based implementation is available, see + * cv::gapi::wip::GCaptureSource. + * + * @note stream sources are passed to G-API via shared pointers, so + * please use ptr() when passing a IStreamSource implementation to + * cv::gin(). + */ +class IStreamSource: public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + Ptr ptr() { return shared_from_this(); } + virtual bool pull(Data &data) = 0; + virtual GMetaArg descr_of() const = 0; + virtual void halt() { + // Do nothing by default to maintain compatibility with the existing sources... + // In fact needs to be decorated atop of the child classes to maintain the behavior + // FIXME: Make it mandatory in OpenCV 5.0 + }; + virtual ~IStreamSource() = default; +}; + +template +IStreamSource::Ptr inline make_src(Args&&... args) +{ + static_assert(std::is_base_of::value, + "T must implement the cv::gapi::IStreamSource interface!"); + auto src_ptr = std::make_shared(std::forward(args)...); + return src_ptr->ptr(); +} + +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_SOURCE_HPP diff --git a/modules/gapi/include/opencv2/gapi/streaming/sync.hpp b/modules/gapi/include/opencv2/gapi/streaming/sync.hpp new file mode 100644 index 00000000000..5801e6f00a3 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/streaming/sync.hpp @@ -0,0 +1,30 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_SYNC_HPP +#define OPENCV_GAPI_STREAMING_SYNC_HPP + +namespace cv { +namespace gapi { +namespace streaming { + +enum class sync_policy { + dont_sync, + drop +}; + +} // namespace streaming +} // namespace gapi + +namespace detail { + template<> struct CompileArgTag { + static const char* tag() { return "gapi.streaming.sync_policy"; } + }; + +} // namespace detail +} // namespace cv + +#endif // OPENCV_GAPI_STREAMING_SYNC_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/any.hpp b/modules/gapi/include/opencv2/gapi/util/any.hpp new file mode 100644 index 00000000000..94451c77171 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/any.hpp @@ -0,0 +1,190 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_ANY_HPP +#define OPENCV_GAPI_UTIL_ANY_HPP + +#include +#include +#include +#include + +#include + +#if defined(_MSC_VER) + // disable MSVC warning on "multiple copy constructors specified" +# pragma warning(disable: 4521) +#endif + +namespace cv +{ + +namespace internal +{ + template + T down_cast(Source operand) + { +#if defined(__GXX_RTTI) || defined(_CPPRTTI) + return dynamic_cast(operand); +#else +#ifdef __GNUC__ +#warning used static cast instead of dynamic because RTTI is disabled +#else +#pragma message("WARNING: used static cast instead of dynamic because RTTI is disabled") +#endif + return static_cast(operand); +#endif + } +} + +namespace util +{ + class bad_any_cast : public std::bad_cast + { + public: + virtual const char* what() const noexcept override + { + return "Bad any cast"; + } + }; + + //modeled against C++17 std::any + + class any + { + private: + struct holder; + using holder_ptr = std::unique_ptr; + struct holder + { + virtual holder_ptr clone() = 0; + virtual ~holder() = default; + }; + + template + struct holder_impl : holder + { + value_t v; + template + holder_impl(arg_t&& a) : v(std::forward(a)) {} + holder_ptr clone() override { return holder_ptr(new holder_impl (v));} + }; + + holder_ptr hldr; + public: + template + any(value_t&& arg) : hldr(new holder_impl::type>( std::forward(arg))) {} + + any(any const& src) : hldr( src.hldr ? src.hldr->clone() : nullptr) {} + //simple hack in order not to write enable_if for the template constructor + any(any & src) : any (const_cast(src)) {} + + any() = default; + any(any&& ) = default; + + any& operator=(any&&) = default; + + any& operator=(any const& src) + { + any copy(src); + swap(*this, copy); + return *this; + } + + template + friend value_t* any_cast(any* operand); + + template + friend const value_t* any_cast(const any* operand); + + template + friend value_t& unsafe_any_cast(any& operand); + + template + friend const value_t& unsafe_any_cast(const any& operand); + + friend void swap(any & lhs, any& rhs) + { + swap(lhs.hldr, rhs.hldr); + } + + }; + + template + value_t* any_cast(any* operand) + { + auto casted = internal::down_cast::type> *>(operand->hldr.get()); + if (casted){ + return & (casted->v); + } + return nullptr; + } + + template + const value_t* any_cast(const any* operand) + { + auto casted = internal::down_cast::type> *>(operand->hldr.get()); + if (casted){ + return & (casted->v); + } + return nullptr; + } + + template + value_t& any_cast(any& operand) + { + auto ptr = any_cast(&operand); + if (ptr) + { + return *ptr; + } + + throw_error(bad_any_cast()); + } + + + template + const value_t& any_cast(const any& operand) + { + auto ptr = any_cast(&operand); + if (ptr) + { + return *ptr; + } + + throw_error(bad_any_cast()); + } + + template + inline value_t& unsafe_any_cast(any& operand) + { +#ifdef DEBUG + return any_cast(operand); +#else + return static_cast::type> *>(operand.hldr.get())->v; +#endif + } + + template + inline const value_t& unsafe_any_cast(const any& operand) + { +#ifdef DEBUG + return any_cast(operand); +#else + return static_cast::type> *>(operand.hldr.get())->v; +#endif + } + +} // namespace util +} // namespace cv + +#if defined(_MSC_VER) + // Enable "multiple copy constructors specified" back +# pragma warning(default: 4521) +#endif + +#endif // OPENCV_GAPI_UTIL_ANY_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp b/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp new file mode 100644 index 00000000000..a41a97145dc --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp @@ -0,0 +1,19 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + +#ifndef OPENCV_GAPI_UTIL_COMPILER_HINTS_HPP +#define OPENCV_GAPI_UTIL_COMPILER_HINTS_HPP + +namespace cv +{ +namespace util +{ + //! Utility template function to prevent "unused" warnings by various compilers. + template void suppress_unused_warning( const T& ) {} +} // namespace util +} // namespace cv + +#endif /* OPENCV_GAPI_UTIL_COMPILER_HINTS_HPP */ diff --git a/modules/gapi/include/opencv2/gapi/util/copy_through_move.hpp b/modules/gapi/include/opencv2/gapi/util/copy_through_move.hpp new file mode 100644 index 00000000000..1a1121eb218 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/copy_through_move.hpp @@ -0,0 +1,34 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_UTIL_COPY_THROUGH_MOVE_HPP +#define OPENCV_GAPI_UTIL_COPY_THROUGH_MOVE_HPP + +#include //decay_t + +namespace cv +{ +namespace util +{ + //This is a tool to move initialize captures of a lambda in C++11 + template + struct copy_through_move_t{ + T value; + const T& get() const {return value;} + T& get() {return value;} + copy_through_move_t(T&& g) : value(std::move(g)) {} + copy_through_move_t(copy_through_move_t&&) = default; + copy_through_move_t(copy_through_move_t const& lhs) : copy_through_move_t(std::move(const_cast(lhs))) {} + }; + + template + copy_through_move_t> copy_through_move(T&& t){ + return std::forward(t); + } +} // namespace util +} // namespace cv + +#endif /* OPENCV_GAPI_UTIL_COPY_THROUGH_MOVE_HPP */ diff --git a/modules/gapi/include/opencv2/gapi/util/optional.hpp b/modules/gapi/include/opencv2/gapi/util/optional.hpp new file mode 100644 index 00000000000..dca03cadad8 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/optional.hpp @@ -0,0 +1,178 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_OPTIONAL_HPP +#define OPENCV_GAPI_UTIL_OPTIONAL_HPP + +#include + +// A poor man's `optional` implementation, incompletely modeled against C++17 spec. +namespace cv +{ +namespace util +{ + class bad_optional_access: public std::exception + { + public: + virtual const char *what() const noexcept override + { + return "Bad optional access"; + } + }; + + // TODO: nullopt_t + + // Interface /////////////////////////////////////////////////////////////// + template class optional + { + public: + // Constructors + // NB.: there were issues with Clang 3.8 when =default() was used + // instead {} + optional() {} + optional(const optional&) = default; + explicit optional(T&&) noexcept; + explicit optional(const T&) noexcept; + optional(optional&&) noexcept; + // TODO: optional(nullopt_t) noexcept; + // TODO: optional(const optional &) + // TODO: optional(optional &&) + // TODO: optional(Args&&...) + // TODO: optional(initializer_list) + // TODO: optional(U&& value); + + // Assignment + optional& operator=(const optional&) = default; + optional& operator=(optional&&); + + // Observers + T* operator-> (); + const T* operator-> () const; + T& operator* (); + const T& operator* () const; + // TODO: && versions + + operator bool() const noexcept; + bool has_value() const noexcept; + + T& value(); + const T& value() const; + // TODO: && versions + + template + T value_or(U &&default_value) const; + + void swap(optional &other) noexcept; + void reset() noexcept; + // TODO: emplace + + // TODO: operator==, !=, <, <=, >, >= + + private: + struct nothing {}; + util::variant m_holder; + }; + + template + optional::type> make_optional(T&& value); + + // TODO: Args... and initializer_list versions + + // Implementation ////////////////////////////////////////////////////////// + template optional::optional(T &&v) noexcept + : m_holder(std::move(v)) + { + } + + template optional::optional(const T &v) noexcept + : m_holder(v) + { + } + + template optional::optional(optional&& rhs) noexcept + : m_holder(std::move(rhs.m_holder)) + { + rhs.reset(); + } + + template optional& optional::operator=(optional&& rhs) + { + m_holder = std::move(rhs.m_holder); + rhs.reset(); + return *this; + } + + template T* optional::operator-> () + { + return & *(*this); + } + + template const T* optional::operator-> () const + { + return & *(*this); + } + + template T& optional::operator* () + { + return this->value(); + } + + template const T& optional::operator* () const + { + return this->value(); + } + + template optional::operator bool() const noexcept + { + return this->has_value(); + } + + template bool optional::has_value() const noexcept + { + return util::holds_alternative(m_holder); + } + + template T& optional::value() + { + if (!this->has_value()) + throw_error(bad_optional_access()); + return util::get(m_holder); + } + + template const T& optional::value() const + { + if (!this->has_value()) + throw_error(bad_optional_access()); + return util::get(m_holder); + } + + template + template T optional::value_or(U &&default_value) const + { + return (this->has_value() ? this->value() : T(default_value)); + } + + template void optional::swap(optional &other) noexcept + { + m_holder.swap(other.m_holder); + } + + template void optional::reset() noexcept + { + if (this->has_value()) + m_holder = nothing{}; + } + + template + optional::type> make_optional(T&& value) + { + return optional::type>(std::forward(value)); + } +} // namespace util +} // namespace cv + +#endif // OPENCV_GAPI_UTIL_OPTIONAL_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/throw.hpp b/modules/gapi/include/opencv2/gapi/util/throw.hpp new file mode 100644 index 00000000000..689bf583cfe --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/throw.hpp @@ -0,0 +1,36 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_THROW_HPP +#define OPENCV_GAPI_UTIL_THROW_HPP + +#include // std::forward + +#if !defined(__EXCEPTIONS) +#include +#include +#endif + +namespace cv +{ +namespace util +{ +template +[[noreturn]] void throw_error(ExceptionType &&e) +{ +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) + throw std::forward(e); +#else + fprintf(stderr, "An exception thrown! %s\n" , e.what()); + fflush(stderr); + abort(); +#endif +} +} // namespace util +} // namespace cv + +#endif // OPENCV_GAPI_UTIL_THROW_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/type_traits.hpp b/modules/gapi/include/opencv2/gapi/util/type_traits.hpp new file mode 100644 index 00000000000..637f18460bc --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/type_traits.hpp @@ -0,0 +1,31 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_TYPE_TRAITS_HPP +#define OPENCV_GAPI_UTIL_TYPE_TRAITS_HPP + +#include + +namespace cv +{ +namespace util +{ + //these are C++14 parts of type_traits : + template< bool B, class T = void > + using enable_if_t = typename std::enable_if::type; + + template + using decay_t = typename std::decay::type; + + //this is not part of C++14 but still, of pretty common usage + template + using are_different_t = enable_if_t< !std::is_same, decay_t>::value, V>; + +} // namespace cv +} // namespace util + +#endif // OPENCV_GAPI_UTIL_TYPE_TRAITS_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/util.hpp b/modules/gapi/include/opencv2/gapi/util/util.hpp new file mode 100644 index 00000000000..3be46d7ec2e --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/util.hpp @@ -0,0 +1,190 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018-2019 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_HPP +#define OPENCV_GAPI_UTIL_HPP + +#include + +// \cond HIDDEN_SYMBOLS +// This header file contains some generic utility functions which are +// used in other G-API Public API headers. +// +// PLEASE don't put any stuff here if it is NOT used in public API headers! + +namespace cv +{ +namespace detail +{ + // Recursive integer sequence type, useful for enumerating elements of + // template parameter packs. + template struct Seq { using next = Seq; }; + template struct MkSeq { using type = typename MkSeq::type::next; }; + template<> struct MkSeq<0>{ using type = Seq<>; }; + + // Checks if elements of variadic template satisfy the given Predicate. + // Implemented via tuple, with an interface to accept plain type lists + template class, typename, typename...> struct all_satisfy; + + template class F, typename T, typename... Ts> + struct all_satisfy > + { + static const constexpr bool value = F::value + && all_satisfy >::value; + }; + template class F, typename T> + struct all_satisfy > + { + static const constexpr bool value = F::value; + }; + + template class F, typename T, typename... Ts> + struct all_satisfy: public all_satisfy > {}; + + // Permute given tuple type C with given integer sequence II + // Sequence may be less than tuple C size. + template struct permute_tuple; + + template + struct permute_tuple > + { + using type = std::tuple< typename std::tuple_element::type... >; + }; + + // Given T..., generates a type sequence of sizeof...(T)-1 elements + // which is T... without its last element + // Implemented via tuple, with an interface to accept plain type lists + template struct all_but_last; + + template + struct all_but_last > + { + using C = std::tuple; + using S = typename MkSeq::value - 1>::type; + using type = typename permute_tuple::type; + }; + + template + struct all_but_last: public all_but_last > {}; + + template + using all_but_last_t = typename all_but_last::type; + + // NB.: This is here because there's no constexpr std::max in C++11 + template struct max_of_t + { + static constexpr const std::size_t rest = max_of_t::value; + static constexpr const std::size_t value = rest > S0 ? rest : S0; + }; + template struct max_of_t + { + static constexpr const std::size_t value = S; + }; + + template + struct contains : std::false_type{}; + + template + struct contains : std::integral_constant::value || + contains::value> {}; + template + struct contains> : std::integral_constant::value> {}; + + template + struct all_unique : std::true_type{}; + + template + struct all_unique : std::integral_constant::value && + all_unique::value> {}; + + template + struct tuple_wrap_helper; + + template struct tuple_wrap_helper + { + using type = std::tuple; + static type get(T&& obj) { return std::make_tuple(std::move(obj)); } + }; + + template + struct tuple_wrap_helper> + { + using type = std::tuple; + static type get(std::tuple&& objs) { return std::forward>(objs); } + }; + + template + struct make_void { typedef void type;}; + + template + using void_t = typename make_void::type; + +} // namespace detail + +namespace util +{ +template +struct overload_lamba_set; + +template +struct overload_lamba_set : public L1 +{ + overload_lamba_set(L1&& lambda) : L1(std::move(lambda)) {} + overload_lamba_set(const L1& lambda) : L1(lambda) {} + + using L1::operator(); +}; + +template +struct overload_lamba_set : public L1, public overload_lamba_set +{ + using base_type = overload_lamba_set; + overload_lamba_set(L1 &&lambda1, L&& ...lambdas): + L1(std::move(lambda1)), + base_type(std::forward(lambdas)...) {} + + overload_lamba_set(const L1 &lambda1, L&& ...lambdas): + L1(lambda1), + base_type(std::forward(lambdas)...) {} + + using L1::operator(); + using base_type::operator(); +}; + +template +overload_lamba_set overload_lambdas(L&& ...lambdas) +{ + return overload_lamba_set(std::forward(lambdas)...); +} + +template +struct find_adapter_impl; + +template +struct find_adapter_impl +{ + using type = typename std::conditional::value, + T, + void>::type; + static constexpr bool found = std::is_base_of::value; +}; + +template +struct find_adapter_impl +{ + using type = typename std::conditional::value, + T, + typename find_adapter_impl::type>::type; + static constexpr bool found = std::is_base_of::value || + find_adapter_impl::found; +}; +} // namespace util +} // namespace cv + +// \endcond + +#endif // OPENCV_GAPI_UTIL_HPP diff --git a/modules/gapi/include/opencv2/gapi/util/variant.hpp b/modules/gapi/include/opencv2/gapi/util/variant.hpp new file mode 100644 index 00000000000..48b55646c53 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/util/variant.hpp @@ -0,0 +1,667 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_UTIL_VARIANT_HPP +#define OPENCV_GAPI_UTIL_VARIANT_HPP + +#include +#include + +#include +#include +#include // max_of_t +#include + +// A poor man's `variant` implementation, incompletely modeled against C++17 spec. +namespace cv +{ +namespace util +{ + namespace detail + { + template + struct type_list_index_helper + { + static const constexpr bool is_same = std::is_same::value; + static const constexpr std::size_t value = + std::conditional, type_list_index_helper>::type::value; + }; + + template + struct type_list_index_helper + { + static_assert(std::is_same::value, "Type not found"); + static const constexpr std::size_t value = I; + }; + } + + template + struct type_list_index + { + static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value; + }; + + template + struct type_list_element + { + using type = typename std::tuple_element >::type; + }; + + class bad_variant_access: public std::exception + { + public: + virtual const char *what() const noexcept override + { + return "Bad variant access"; + } + }; + + // Interface /////////////////////////////////////////////////////////////// + struct monostate {}; + inline bool operator==(const util::monostate&, const util::monostate&) + { + return true; + } + + template // FIXME: no references, arrays, and void + class variant + { + // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped + static constexpr const std::size_t S = cv::detail::max_of_t::value; + static constexpr const std::size_t A = cv::detail::max_of_t::value; + using Memory = typename std::aligned_storage::type[1]; + + template struct cctr_h { + static void help(Memory memory, const Memory from) { + new (memory) T(*reinterpret_cast(from)); + } + }; + + template struct mctr_h { + static void help(Memory memory, void *pval) { + new (memory) T(std::move(*reinterpret_cast(pval))); + } + }; + + //FIXME: unify with cctr_h and mctr_h + template struct cnvrt_ctor_h { + static void help(Memory memory, void* from) { + using util::decay_t; + new (memory) decay_t(std::forward(*reinterpret_cast*>(from))); + } + }; + + template struct copy_h { + static void help(Memory to, const Memory from) { + *reinterpret_cast(to) = *reinterpret_cast(from); + } + }; + + template struct move_h { + static void help(Memory to, Memory from) { + *reinterpret_cast(to) = std::move(*reinterpret_cast(from)); + } + }; + + //FIXME: unify with copy_h and move_h + template struct cnvrt_assign_h { + static void help(Memory to, void* from) { + using util::decay_t; + *reinterpret_cast*>(to) = std::forward(*reinterpret_cast*>(from)); + } + }; + + template struct swap_h { + static void help(Memory to, Memory from) { + std::swap(*reinterpret_cast(to), *reinterpret_cast(from)); + } + }; + + template struct dtor_h { + static void help(Memory memory) { + (void) memory; // MSCV warning + reinterpret_cast(memory)->~T(); + } + }; + + template struct equal_h { + static bool help(const Memory lhs, const Memory rhs) { + const T& t_lhs = *reinterpret_cast(lhs); + const T& t_rhs = *reinterpret_cast(rhs); + return t_lhs == t_rhs; + } + }; + + typedef void (*CCtr) (Memory, const Memory); // Copy c-tor (variant) + typedef void (*MCtr) (Memory, void*); // Generic move c-tor + typedef void (*Copy) (Memory, const Memory); // Copy assignment + typedef void (*Move) (Memory, Memory); // Move assignment + + typedef void (*Swap) (Memory, Memory); // Swap + typedef void (*Dtor) (Memory); // Destructor + + using cnvrt_assgn_t = void (*) (Memory, void*); // Converting assignment (via std::forward) + using cnvrt_ctor_t = void (*) (Memory, void*); // Converting constructor (via std::forward) + + typedef bool (*Equal)(const Memory, const Memory); // Equality test (external) + + static constexpr std::array cctrs(){ return {{(&cctr_h::help)...}};} + static constexpr std::array mctrs(){ return {{(&mctr_h::help)...}};} + static constexpr std::array cpyrs(){ return {{(©_h::help)...}};} + static constexpr std::array mvers(){ return {{(&move_h::help)...}};} + static constexpr std::array swprs(){ return {{(&swap_h::help)...}};} + static constexpr std::array dtors(){ return {{(&dtor_h::help)...}};} + + template + struct conditional_ref : std::conditional::type&, typename std::remove_reference::type > {}; + + template + using conditional_ref_t = typename conditional_ref::type; + + + template + static constexpr std::array cnvrt_assgnrs(){ + return {{(&cnvrt_assign_h>::help)...}}; + } + + template + static constexpr std::array cnvrt_ctors(){ + return {{(&cnvrt_ctor_h>::help)...}}; + } + + std::size_t m_index = 0; + + protected: + template friend T& get(variant &v); + template friend const T& get(const variant &v); + template friend T* get_if(variant *v) noexcept; + template friend const T* get_if(const variant *v) noexcept; + + template friend bool operator==(const variant &lhs, + const variant &rhs); + Memory memory; + + public: + // Constructors + variant() noexcept; + variant(const variant& other); + variant(variant&& other) noexcept; + // are_different_t is a SFINAE trick to avoid variant(T &&t) with T=variant + // for some reason, this version is called instead of variant(variant&& o) when + // variant is used in STL containers (examples: vector assignment). + template< + typename T, + typename = util::are_different_t + > + explicit variant(T&& t); + // template explicit variant(Args&&... args); + // FIXME: other constructors + + // Destructor + ~variant(); + + // Assignment + variant& operator=(const variant& rhs); + variant& operator=(variant &&rhs) noexcept; + + // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above + template< + typename T, + typename = util::are_different_t + > + variant& operator=(T&& t) noexcept; + + // Observers + std::size_t index() const noexcept; + // FIXME: valueless_by_exception() + + // Modifiers + // FIXME: emplace() + void swap(variant &rhs) noexcept; + + // Non-C++17x! + template static constexpr std::size_t index_of(); + }; + + // FIMXE: visit + template + T* get_if(util::variant* v) noexcept; + + template + const T* get_if(const util::variant* v) noexcept; + + template + T& get(util::variant &v); + + template + const T& get(const util::variant &v); + + template + typename util::type_list_element::type& get(util::variant &v); + + template + const typename util::type_list_element::type& get(const util::variant &v); + + template + bool holds_alternative(const util::variant &v) noexcept; + + + // Visitor + namespace detail + { + struct visitor_interface {}; + + // Class `visitor_return_type_deduction_helper` + // introduces solution for deduction `return_type` in `visit` function in common way + // for both Lambda and class Visitor and keep one interface invocation point: `visit` only + // his helper class is required to unify return_type deduction mechanism because + // for Lambda it is possible to take type of `decltype(visitor(get<0>(var)))` + // but for class Visitor there is no operator() in base case, + // because it provides `operator() (std::size_t index, ...)` + // So `visitor_return_type_deduction_helper` expose `operator()` + // uses only for class Visitor only for deduction `return type` in visit() + template + struct visitor_return_type_deduction_helper + { + using return_type = R; + + // to be used in Lambda return type deduction context only + template + return_type operator() (T&&); + }; + } + + // Special purpose `static_visitor` can receive additional arguments + template + struct static_visitor : public detail::visitor_interface, + public detail::visitor_return_type_deduction_helper { + + // assign responsibility for return type deduction to helper class + using return_type = typename detail::visitor_return_type_deduction_helper::return_type; + using detail::visitor_return_type_deduction_helper::operator(); + friend Impl; + + template + return_type operator() (std::size_t index, VariantValue&& value, Args&& ...args) + { + suppress_unused_warning(index); + return static_cast(this)-> visit( + std::forward(value), + std::forward(args)...); + } + }; + + // Special purpose `static_indexed_visitor` can receive additional arguments + // And make forwarding current variant index as runtime function argument to its `Impl` + template + struct static_indexed_visitor : public detail::visitor_interface, + public detail::visitor_return_type_deduction_helper { + + // assign responsibility for return type deduction to helper class + using return_type = typename detail::visitor_return_type_deduction_helper::return_type; + using detail::visitor_return_type_deduction_helper::operator(); + friend Impl; + + template + return_type operator() (std::size_t Index, VariantValue&& value, Args&& ...args) + { + return static_cast(this)-> visit(Index, + std::forward(value), + std::forward(args)...); + } + }; + + template + struct variant_size; + + template + struct variant_size> + : std::integral_constant { }; + // FIXME: T&&, const TT&& versions. + + // Implementation ////////////////////////////////////////////////////////// + template + variant::variant() noexcept + { + typedef typename std::tuple_element<0, std::tuple >::type TFirst; + new (memory) TFirst(); + } + + template + variant::variant(const variant &other) + : m_index(other.m_index) + { + (cctrs()[m_index])(memory, other.memory); + } + + template + variant::variant(variant &&other) noexcept + : m_index(other.m_index) + { + (mctrs()[m_index])(memory, other.memory); + } + + template + template + variant::variant(T&& t) + : m_index(util::type_list_index, Ts...>::value) + { + const constexpr bool is_lvalue_arg = std::is_lvalue_reference::value; + (cnvrt_ctors()[m_index])(memory, const_cast *>(&t)); + } + + template + variant::~variant() + { + (dtors()[m_index])(memory); + } + + template + variant& variant::operator=(const variant &rhs) + { + if (m_index != rhs.m_index) + { + (dtors()[ m_index])(memory); + (cctrs()[rhs.m_index])(memory, rhs.memory); + m_index = rhs.m_index; + } + else + { + (cpyrs()[rhs.m_index])(memory, rhs.memory); + } + return *this; + } + + template + variant& variant::operator=(variant &&rhs) noexcept + { + if (m_index != rhs.m_index) + { + (dtors()[ m_index])(memory); + (mctrs()[rhs.m_index])(memory, rhs.memory); + m_index = rhs.m_index; + } + else + { + (mvers()[rhs.m_index])(memory, rhs.memory); + } + return *this; + } + + template + template + variant& variant::operator=(T&& t) noexcept + { + using decayed_t = util::decay_t; + // FIXME: No version with implicit type conversion available! + const constexpr std::size_t t_index = + util::type_list_index::value; + + const constexpr bool is_lvalue_arg = std::is_lvalue_reference::value; + + if (t_index != m_index) + { + (dtors()[m_index])(memory); + (cnvrt_ctors()[t_index])(memory, &t); + m_index = t_index; + } + else + { + (cnvrt_assgnrs()[m_index])(memory, &t); + } + return *this; + + } + + template + std::size_t util::variant::index() const noexcept + { + return m_index; + } + + template + void variant::swap(variant &rhs) noexcept + { + if (m_index == rhs.index()) + { + (swprs()[m_index](memory, rhs.memory)); + } + else + { + variant tmp(std::move(*this)); + *this = std::move(rhs); + rhs = std::move(tmp); + } + } + + template + template + constexpr std::size_t variant::index_of() + { + return util::type_list_index::value; // FIXME: tests! + } + + template + T* get_if(util::variant* v) noexcept + { + const constexpr std::size_t t_index = + util::type_list_index::value; + + if (v && v->index() == t_index) + return (T*)(&v->memory); // workaround for ICC 2019 + // original code: return reinterpret_cast(v.memory); + return nullptr; + } + + template + const T* get_if(const util::variant* v) noexcept + { + const constexpr std::size_t t_index = + util::type_list_index::value; + + if (v && v->index() == t_index) + return (const T*)(&v->memory); // workaround for ICC 2019 + // original code: return reinterpret_cast(v.memory); + return nullptr; + } + + template + T& get(util::variant &v) + { + if (auto* p = get_if(&v)) + return *p; + else + throw_error(bad_variant_access()); + } + + template + const T& get(const util::variant &v) + { + if (auto* p = get_if(&v)) + return *p; + else + throw_error(bad_variant_access()); + } + + template + typename util::type_list_element::type& get(util::variant &v) + { + using ReturnType = typename util::type_list_element::type; + return const_cast(get(static_cast &>(v))); + } + + template + const typename util::type_list_element::type& get(const util::variant &v) + { + static_assert(Index < sizeof...(Types), + "`Index` it out of bound of `util::variant` type list"); + using ReturnType = typename util::type_list_element::type; + return get(v); + } + + template + bool holds_alternative(const util::variant &v) noexcept + { + return v.index() == util::variant::template index_of(); + } + +#if defined(__GNUC__) && (__GNUC__ == 11 || __GNUC__ == 12) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + + template bool operator==(const variant &lhs, + const variant &rhs) + { + using V = variant; + + // Instantiate table only here since it requires operator== for + // should have operator== only if this one is used, not in general + static const std::array eqs = { + {(&V::template equal_h::help)...} + }; + if (lhs.index() != rhs.index()) + return false; + return (eqs[lhs.index()])(lhs.memory, rhs.memory); + } + +#if defined(__GNUC__) && (__GNUC__ == 11 || __GNUC__ == 12) +#pragma GCC diagnostic pop +#endif + + template bool operator!=(const variant &lhs, + const variant &rhs) + { + return !(lhs == rhs); + } + +namespace detail +{ + // terminate recursion implementation for `non-void` ReturnType + template + ReturnType apply_visitor_impl(Visitor&&, Variant&, + std::true_type, std::false_type, + VisitorArgs&& ...) + { + return {}; + } + + // terminate recursion implementation for `void` ReturnType + template + void apply_visitor_impl(Visitor&&, Variant&, + std::true_type, std::true_type, + VisitorArgs&& ...) + { + } + + // Intermediate resursion processor for Lambda Visitors + template + typename std::enable_if::type>::value, ReturnType>::type + apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed, + std::integral_constant should_no_return, + VisitorArgs&& ...args) + { + static_assert(std::is_same(v)))>::value, + "Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`" + " must return the same type"); + suppress_unused_warning(not_processed); + if (v.index() == CurIndex) + { + return visitor.operator()(get(v), std::forward(args)... ); + } + + using is_variant_processed_t = std::integral_constant= ElemCount>; + return apply_visitor_impl( + std::forward(visitor), + std::forward(v), + is_variant_processed_t{}, + should_no_return, + std::forward(args)...); + } + + //Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator() + template + typename std::enable_if::type>, + typename std::decay::type>::value, ReturnType>::type + invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args) + { + return static_cast::type>&>(visitor).operator() (CurIndex, std::forward(v), std::forward(args)... ); + } + + //Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator() + template + typename std::enable_if::type>, + typename std::decay::type>::value, ReturnType>::type + invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args) + { + return static_cast::type>&>(visitor).operator() (CurIndex, std::forward(v), std::forward(args)... ); + } + + // Intermediate recursion processor for special case `visitor_interface` derived Visitors + template + typename std::enable_if::type>::value, ReturnType>::type + apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed, + std::integral_constant should_no_return, + VisitorArgs&& ...args) + { + static_assert(std::is_same(v)))>::value, + "Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`" + " must return the same type"); + suppress_unused_warning(not_processed); + if (v.index() == CurIndex) + { + return invoke_class_visitor(visitor, get(v), std::forward(args)... ); + } + + using is_variant_processed_t = std::integral_constant= ElemCount>; + return apply_visitor_impl( + std::forward(visitor), + std::forward(v), + is_variant_processed_t{}, + should_no_return, + std::forward(args)...); + } +} // namespace detail + + template + auto visit(Visitor &visitor, const Variant& var, VisitorArg &&...args) -> decltype(visitor(get<0>(var))) + { + constexpr std::size_t varsize = util::variant_size::value; + static_assert(varsize != 0, "utils::variant must contains one type at least "); + using is_variant_processed_t = std::false_type; + + using ReturnType = decltype(visitor(get<0>(var))); + using return_t = std::is_same; + return detail::apply_visitor_impl( + std::forward(visitor), + var, is_variant_processed_t{}, + return_t{}, + std::forward(args)...); + } + + template + auto visit(Visitor&& visitor, const Variant& var) -> decltype(visitor(get<0>(var))) + { + constexpr std::size_t varsize = util::variant_size::value; + static_assert(varsize != 0, "utils::variant must contains one type at least "); + using is_variant_processed_t = std::false_type; + + using ReturnType = decltype(visitor(get<0>(var))); + using return_t = std::is_same; + return detail::apply_visitor_impl( + std::forward(visitor), + var, is_variant_processed_t{}, + return_t{}); + } +} // namespace util +} // namespace cv + +#endif // OPENCV_GAPI_UTIL_VARIANT_HPP diff --git a/modules/gapi/include/opencv2/gapi/video.hpp b/modules/gapi/include/opencv2/gapi/video.hpp new file mode 100644 index 00000000000..4dcc1d41824 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/video.hpp @@ -0,0 +1,364 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_VIDEO_HPP +#define OPENCV_GAPI_VIDEO_HPP + +#include // std::tuple + +#include + + +/** \defgroup gapi_video G-API Video processing functionality + */ + +namespace cv { namespace gapi { + +/** @brief Structure for the Kalman filter's initialization parameters.*/ + +struct GAPI_EXPORTS KalmanParams +{ + // initial state + + //! corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) + Mat state; + //! posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) + Mat errorCov; + + // dynamic system description + + //! state transition matrix (A) + Mat transitionMatrix; + //! measurement matrix (H) + Mat measurementMatrix; + //! process noise covariance matrix (Q) + Mat processNoiseCov; + //! measurement noise covariance matrix (R) + Mat measurementNoiseCov; + //! control matrix (B) (Optional: not used if there's no control) + Mat controlMatrix; +}; + +/** + * @brief This namespace contains G-API Operations and functions for + * video-oriented algorithms, like optical flow and background subtraction. + */ +namespace video +{ +using GBuildPyrOutput = std::tuple, GScalar>; + +using GOptFlowLKOutput = std::tuple, + cv::GArray, + cv::GArray>; + +G_TYPED_KERNEL(GBuildOptFlowPyramid, , + "org.opencv.video.buildOpticalFlowPyramid") +{ + static std::tuple + outMeta(GMatDesc,const Size&,GScalarDesc,bool,int,int,bool) + { + return std::make_tuple(empty_array_desc(), empty_scalar_desc()); + } +}; + +G_TYPED_KERNEL(GCalcOptFlowLK, + ,cv::GArray,Size, + GScalar,TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLK") +{ + static std::tuple outMeta(GMatDesc,GMatDesc,GArrayDesc, + GArrayDesc,const Size&,GScalarDesc, + const TermCriteria&,int,double) + { + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } + +}; + +G_TYPED_KERNEL(GCalcOptFlowLKForPyr, + ,cv::GArray, + cv::GArray,cv::GArray,Size,GScalar, + TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLKForPyr") +{ + static std::tuple outMeta(GArrayDesc,GArrayDesc, + GArrayDesc,GArrayDesc, + const Size&,GScalarDesc, + const TermCriteria&,int,double) + { + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } +}; + +enum BackgroundSubtractorType +{ + TYPE_BS_MOG2, + TYPE_BS_KNN +}; + +/** @brief Structure for the Background Subtractor operation's initialization parameters.*/ + +struct BackgroundSubtractorParams +{ + //! Type of the Background Subtractor operation. + BackgroundSubtractorType operation = TYPE_BS_MOG2; + + //! Length of the history. + int history = 500; + + //! For MOG2: Threshold on the squared Mahalanobis distance between the pixel + //! and the model to decide whether a pixel is well described by + //! the background model. + //! For KNN: Threshold on the squared distance between the pixel and the sample + //! to decide whether a pixel is close to that sample. + double threshold = 16; + + //! If true, the algorithm will detect shadows and mark them. + bool detectShadows = true; + + //! The value between 0 and 1 that indicates how fast + //! the background model is learnt. + //! Negative parameter value makes the algorithm use some automatically + //! chosen learning rate. + double learningRate = -1; + + //! default constructor + BackgroundSubtractorParams() {} + + /** Full constructor + @param op MOG2/KNN Background Subtractor type. + @param histLength Length of the history. + @param thrshld For MOG2: Threshold on the squared Mahalanobis distance between + the pixel and the model to decide whether a pixel is well described by the background model. + For KNN: Threshold on the squared distance between the pixel and the sample to decide + whether a pixel is close to that sample. + @param detect If true, the algorithm will detect shadows and mark them. It decreases the + speed a bit, so if you do not need this feature, set the parameter to false. + @param lRate The value between 0 and 1 that indicates how fast the background model is learnt. + Negative parameter value makes the algorithm to use some automatically chosen learning rate. + */ + BackgroundSubtractorParams(BackgroundSubtractorType op, int histLength, + double thrshld, bool detect, double lRate) : operation(op), + history(histLength), + threshold(thrshld), + detectShadows(detect), + learningRate(lRate){} +}; + +G_TYPED_KERNEL(GBackgroundSubtractor, , + "org.opencv.video.BackgroundSubtractor") +{ + static GMatDesc outMeta(const GMatDesc& in, const BackgroundSubtractorParams& bsParams) + { + GAPI_Assert(bsParams.history >= 0); + GAPI_Assert(bsParams.learningRate <= 1); + return in.withType(CV_8U, 1); + } +}; + +void checkParams(const cv::gapi::KalmanParams& kfParams, + const cv::GMatDesc& measurement, const cv::GMatDesc& control = {}); + +G_TYPED_KERNEL(GKalmanFilter, , GMat, KalmanParams)>, + "org.opencv.video.KalmanFilter") +{ + static GMatDesc outMeta(const GMatDesc& measurement, const GOpaqueDesc&, + const GMatDesc& control, const KalmanParams& kfParams) + { + checkParams(kfParams, measurement, control); + return measurement.withSize(Size(1, kfParams.transitionMatrix.rows)); + } +}; + +G_TYPED_KERNEL(GKalmanFilterNoControl, , KalmanParams)>, "org.opencv.video.KalmanFilterNoControl") +{ + static GMatDesc outMeta(const GMatDesc& measurement, const GOpaqueDesc&, const KalmanParams& kfParams) + { + checkParams(kfParams, measurement); + return measurement.withSize(Size(1, kfParams.transitionMatrix.rows)); + } +}; +} //namespace video + +//! @addtogroup gapi_video +//! @{ +/** @brief Constructs the image pyramid which can be passed to calcOpticalFlowPyrLK. + +@note Function textual ID is "org.opencv.video.buildOpticalFlowPyramid" + +@param img 8-bit input image. +@param winSize window size of optical flow algorithm. Must be not less than winSize + argument of calcOpticalFlowPyrLK. It is needed to calculate required + padding for pyramid levels. +@param maxLevel 0-based maximal pyramid level number. +@param withDerivatives set to precompute gradients for the every pyramid level. If pyramid is + constructed without the gradients then calcOpticalFlowPyrLK will calculate + them internally. +@param pyrBorder the border mode for pyramid layers. +@param derivBorder the border mode for gradients. +@param tryReuseInputImage put ROI of input image into the pyramid if possible. You can pass false + to force data copying. + +@return + - output pyramid. + - number of levels in constructed pyramid. Can be less than maxLevel. + */ +GAPI_EXPORTS std::tuple, GScalar> +buildOpticalFlowPyramid(const GMat &img, + const Size &winSize, + const GScalar &maxLevel, + bool withDerivatives = true, + int pyrBorder = BORDER_REFLECT_101, + int derivBorder = BORDER_CONSTANT, + bool tryReuseInputImage = true); + +/** @brief Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade +method with pyramids. + +See @cite Bouguet00 . + +@note Function textual ID is "org.opencv.video.calcOpticalFlowPyrLK" + +@param prevImg first 8-bit input image (GMat) or pyramid (GArray) constructed by +buildOpticalFlowPyramid. +@param nextImg second input image (GMat) or pyramid (GArray) of the same size and the same +type as prevImg. +@param prevPts GArray of 2D points for which the flow needs to be found; point coordinates must be +single-precision floating-point numbers. +@param predPts GArray of 2D points initial for the flow search; make sense only when +OPTFLOW_USE_INITIAL_FLOW flag is passed; in that case the vector must have the same size as in +the input. +@param winSize size of the search window at each pyramid level. +@param maxLevel 0-based maximal pyramid level number; if set to 0, pyramids are not used (single +level), if set to 1, two levels are used, and so on; if pyramids are passed to input then +algorithm will use as many levels as pyramids have but no more than maxLevel. +@param criteria parameter, specifying the termination criteria of the iterative search algorithm +(after the specified maximum number of iterations criteria.maxCount or when the search window +moves by less than criteria.epsilon). +@param flags operation flags: + - **OPTFLOW_USE_INITIAL_FLOW** uses initial estimations, stored in nextPts; if the flag is + not set, then prevPts is copied to nextPts and is considered the initial estimate. + - **OPTFLOW_LK_GET_MIN_EIGENVALS** use minimum eigen values as an error measure (see + minEigThreshold description); if the flag is not set, then L1 distance between patches + around the original and a moved point, divided by number of pixels in a window, is used as a + error measure. +@param minEigThresh the algorithm calculates the minimum eigen value of a 2x2 normal matrix of +optical flow equations (this matrix is called a spatial gradient matrix in @cite Bouguet00), divided +by number of pixels in a window; if this value is less than minEigThreshold, then a corresponding +feature is filtered out and its flow is not processed, so it allows to remove bad points and get a +performance boost. + +@return + - GArray of 2D points (with single-precision floating-point coordinates) +containing the calculated new positions of input features in the second image. + - status GArray (of unsigned chars); each element of the vector is set to 1 if +the flow for the corresponding features has been found, otherwise, it is set to 0. + - GArray of errors (doubles); each element of the vector is set to an error for the +corresponding feature, type of the error measure can be set in flags parameter; if the flow wasn't +found then the error is not defined (use the status parameter to find such cases). + */ +GAPI_EXPORTS std::tuple, GArray, GArray> +calcOpticalFlowPyrLK(const GMat &prevImg, + const GMat &nextImg, + const GArray &prevPts, + const GArray &predPts, + const Size &winSize = Size(21, 21), + const GScalar &maxLevel = 3, + const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | + TermCriteria::EPS, + 30, 0.01), + int flags = 0, + double minEigThresh = 1e-4); + +/** +@overload +@note Function textual ID is "org.opencv.video.calcOpticalFlowPyrLKForPyr" +*/ +GAPI_EXPORTS std::tuple, GArray, GArray> +calcOpticalFlowPyrLK(const GArray &prevPyr, + const GArray &nextPyr, + const GArray &prevPts, + const GArray &predPts, + const Size &winSize = Size(21, 21), + const GScalar &maxLevel = 3, + const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | + TermCriteria::EPS, + 30, 0.01), + int flags = 0, + double minEigThresh = 1e-4); + +/** @brief Gaussian Mixture-based or K-nearest neighbours-based Background/Foreground Segmentation Algorithm. +The operation generates a foreground mask. + +@return Output image is foreground mask, i.e. 8-bit unsigned 1-channel (binary) matrix @ref CV_8UC1. + +@note Functional textual ID is "org.opencv.video.BackgroundSubtractor" + +@param src input image: Floating point frame is used without scaling and should be in range [0,255]. +@param bsParams Set of initialization parameters for Background Subtractor kernel. +*/ +GAPI_EXPORTS GMat BackgroundSubtractor(const GMat& src, const cv::gapi::video::BackgroundSubtractorParams& bsParams); + +/** @brief Standard Kalman filter algorithm . + +@note Functional textual ID is "org.opencv.video.KalmanFilter" + +@param measurement input matrix: 32-bit or 64-bit float 1-channel matrix containing measurements. +@param haveMeasurement dynamic input flag that indicates whether we get measurements +at a particular iteration . +@param control input matrix: 32-bit or 64-bit float 1-channel matrix contains control data +for changing dynamic system. +@param kfParams Set of initialization parameters for Kalman filter kernel. + +@return Output matrix is predicted or corrected state. They can be 32-bit or 64-bit float +1-channel matrix @ref CV_32FC1 or @ref CV_64FC1. + +@details If measurement matrix is given (haveMeasurements == true), corrected state will +be returned which corresponds to the pipeline +cv::KalmanFilter::predict(control) -> cv::KalmanFilter::correct(measurement). +Otherwise, predicted state will be returned which corresponds to the call of +cv::KalmanFilter::predict(control). +@sa cv::KalmanFilter +*/ +GAPI_EXPORTS GMat KalmanFilter(const GMat& measurement, const GOpaque& haveMeasurement, + const GMat& control, const cv::gapi::KalmanParams& kfParams); + +/** @overload +The case of Standard Kalman filter algorithm when there is no control in a dynamic system. +In this case the controlMatrix is empty and control vector is absent. + +@note Function textual ID is "org.opencv.video.KalmanFilterNoControl" + +@param measurement input matrix: 32-bit or 64-bit float 1-channel matrix containing measurements. +@param haveMeasurement dynamic input flag that indicates whether we get measurements +at a particular iteration. +@param kfParams Set of initialization parameters for Kalman filter kernel. + +@return Output matrix is predicted or corrected state. They can be 32-bit or 64-bit float +1-channel matrix @ref CV_32FC1 or @ref CV_64FC1. + +@sa cv::KalmanFilter + */ +GAPI_EXPORTS GMat KalmanFilter(const GMat& measurement, const GOpaque& haveMeasurement, + const cv::gapi::KalmanParams& kfParams); + +//! @} gapi_video +} //namespace gapi +} //namespace cv + + +namespace cv { namespace detail { +template<> struct CompileArgTag +{ + static const char* tag() + { + return "org.opencv.video.background_substractor_params"; + } +}; +} // namespace detail +} // namespace cv + +#endif // OPENCV_GAPI_VIDEO_HPP diff --git a/modules/gapi/misc/python/package/gapi/__init__.py b/modules/gapi/misc/python/package/gapi/__init__.py new file mode 100644 index 00000000000..2b21e54e419 --- /dev/null +++ b/modules/gapi/misc/python/package/gapi/__init__.py @@ -0,0 +1,323 @@ +__all__ = ['op', 'kernel'] + +import sys +import cv2 as cv + +# NB: Register function in specific module +def register(mname): + def parameterized(func): + sys.modules[mname].__dict__[func.__name__] = func + return func + return parameterized + + +@register('cv2.gapi') +def networks(*args): + return cv.gapi_GNetPackage(list(map(cv.detail.strip, args))) + + +@register('cv2.gapi') +def compile_args(*args): + return list(map(cv.GCompileArg, args)) + + +@register('cv2') +def GIn(*args): + return [*args] + + +@register('cv2') +def GOut(*args): + return [*args] + + +@register('cv2') +def gin(*args): + return [*args] + + +@register('cv2.gapi') +def descr_of(*args): + return [*args] + + +@register('cv2') +class GOpaque(): + # NB: Inheritance from c++ class cause segfault. + # So just aggregate cv.GOpaqueT instead of inheritance + def __new__(cls, argtype): + return cv.GOpaqueT(argtype) + + class Bool(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_BOOL) + + class Int(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_INT) + + class Int64(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_INT64) + + class UInt64(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_UINT64) + + class Double(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_DOUBLE) + + class Float(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_FLOAT) + + class String(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_STRING) + + class Point(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_POINT) + + class Point2f(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_POINT2F) + + class Point3f(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_POINT3F) + + class Size(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_SIZE) + + class Rect(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_RECT) + + class Prim(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_DRAW_PRIM) + + class Any(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_ANY) + +@register('cv2') +class GArray(): + # NB: Inheritance from c++ class cause segfault. + # So just aggregate cv.GArrayT instead of inheritance + def __new__(cls, argtype): + return cv.GArrayT(argtype) + + class Bool(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_BOOL) + + class Int(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_INT) + + class Int64(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_INT64) + + class UInt64(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_UINT64) + + class Double(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_DOUBLE) + + class Float(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_FLOAT) + + class String(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_STRING) + + class Point(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_POINT) + + class Point2f(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_POINT2F) + + class Point3f(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_POINT3F) + + class Size(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_SIZE) + + class Rect(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_RECT) + + class Scalar(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_SCALAR) + + class Mat(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_MAT) + + class GMat(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_GMAT) + + class Prim(): + def __new__(self): + return cv.GArray(cv.gapi.CV_DRAW_PRIM) + + class Any(): + def __new__(self): + return cv.GArray(cv.gapi.CV_ANY) + + +# NB: Top lvl decorator takes arguments +def op(op_id, in_types, out_types): + + garray_types= { + cv.GArray.Bool: cv.gapi.CV_BOOL, + cv.GArray.Int: cv.gapi.CV_INT, + cv.GArray.Int64: cv.gapi.CV_INT64, + cv.GArray.UInt64: cv.gapi.CV_UINT64, + cv.GArray.Double: cv.gapi.CV_DOUBLE, + cv.GArray.Float: cv.gapi.CV_FLOAT, + cv.GArray.String: cv.gapi.CV_STRING, + cv.GArray.Point: cv.gapi.CV_POINT, + cv.GArray.Point2f: cv.gapi.CV_POINT2F, + cv.GArray.Point3f: cv.gapi.CV_POINT3F, + cv.GArray.Size: cv.gapi.CV_SIZE, + cv.GArray.Rect: cv.gapi.CV_RECT, + cv.GArray.Scalar: cv.gapi.CV_SCALAR, + cv.GArray.Mat: cv.gapi.CV_MAT, + cv.GArray.GMat: cv.gapi.CV_GMAT, + cv.GArray.Prim: cv.gapi.CV_DRAW_PRIM, + cv.GArray.Any: cv.gapi.CV_ANY + } + + gopaque_types= { + cv.GOpaque.Size: cv.gapi.CV_SIZE, + cv.GOpaque.Rect: cv.gapi.CV_RECT, + cv.GOpaque.Bool: cv.gapi.CV_BOOL, + cv.GOpaque.Int: cv.gapi.CV_INT, + cv.GOpaque.Int64: cv.gapi.CV_INT64, + cv.GOpaque.UInt64: cv.gapi.CV_UINT64, + cv.GOpaque.Double: cv.gapi.CV_DOUBLE, + cv.GOpaque.Float: cv.gapi.CV_FLOAT, + cv.GOpaque.String: cv.gapi.CV_STRING, + cv.GOpaque.Point: cv.gapi.CV_POINT, + cv.GOpaque.Point2f: cv.gapi.CV_POINT2F, + cv.GOpaque.Point3f: cv.gapi.CV_POINT3F, + cv.GOpaque.Size: cv.gapi.CV_SIZE, + cv.GOpaque.Rect: cv.gapi.CV_RECT, + cv.GOpaque.Prim: cv.gapi.CV_DRAW_PRIM, + cv.GOpaque.Any: cv.gapi.CV_ANY + } + + type2str = { + cv.gapi.CV_BOOL: 'cv.gapi.CV_BOOL' , + cv.gapi.CV_INT: 'cv.gapi.CV_INT' , + cv.gapi.CV_INT64: 'cv.gapi.CV_INT64' , + cv.gapi.CV_UINT64: 'cv.gapi.CV_UINT64' , + cv.gapi.CV_DOUBLE: 'cv.gapi.CV_DOUBLE' , + cv.gapi.CV_FLOAT: 'cv.gapi.CV_FLOAT' , + cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' , + cv.gapi.CV_POINT: 'cv.gapi.CV_POINT' , + cv.gapi.CV_POINT2F: 'cv.gapi.CV_POINT2F' , + cv.gapi.CV_POINT3F: 'cv.gapi.CV_POINT3F' , + cv.gapi.CV_SIZE: 'cv.gapi.CV_SIZE', + cv.gapi.CV_RECT: 'cv.gapi.CV_RECT', + cv.gapi.CV_SCALAR: 'cv.gapi.CV_SCALAR', + cv.gapi.CV_MAT: 'cv.gapi.CV_MAT', + cv.gapi.CV_GMAT: 'cv.gapi.CV_GMAT', + cv.gapi.CV_DRAW_PRIM: 'cv.gapi.CV_DRAW_PRIM' + } + + # NB: Second lvl decorator takes class to decorate + def op_with_params(cls): + if not in_types: + raise Exception('{} operation should have at least one input!'.format(cls.__name__)) + + if not out_types: + raise Exception('{} operation should have at least one output!'.format(cls.__name__)) + + for i, t in enumerate(out_types): + if t not in [cv.GMat, cv.GScalar, *garray_types, *gopaque_types]: + raise Exception('{} unsupported output type: {} in position: {}' + .format(cls.__name__, t.__name__, i)) + + def on(*args): + if len(in_types) != len(args): + raise Exception('Invalid number of input elements!\nExpected: {}, Actual: {}' + .format(len(in_types), len(args))) + + for i, (t, a) in enumerate(zip(in_types, args)): + if t in garray_types: + if not isinstance(a, cv.GArrayT): + raise Exception("{} invalid type for argument {}.\nExpected: {}, Actual: {}" + .format(cls.__name__, i, cv.GArrayT.__name__, type(a).__name__)) + + elif a.type() != garray_types[t]: + raise Exception("{} invalid GArrayT type for argument {}.\nExpected: {}, Actual: {}" + .format(cls.__name__, i, type2str[garray_types[t]], type2str[a.type()])) + + elif t in gopaque_types: + if not isinstance(a, cv.GOpaqueT): + raise Exception("{} invalid type for argument {}.\nExpected: {}, Actual: {}" + .format(cls.__name__, i, cv.GOpaqueT.__name__, type(a).__name__)) + + elif a.type() != gopaque_types[t]: + raise Exception("{} invalid GOpaque type for argument {}.\nExpected: {}, Actual: {}" + .format(cls.__name__, i, type2str[gopaque_types[t]], type2str[a.type()])) + + else: + if t != type(a): + raise Exception('{} invalid input type for argument {}.\nExpected: {}, Actual: {}' + .format(cls.__name__, i, t.__name__, type(a).__name__)) + + op = cv.gapi.__op(op_id, cls.outMeta, *args) + + out_protos = [] + for i, out_type in enumerate(out_types): + if out_type == cv.GMat: + out_protos.append(op.getGMat()) + elif out_type == cv.GScalar: + out_protos.append(op.getGScalar()) + elif out_type in gopaque_types: + out_protos.append(op.getGOpaque(gopaque_types[out_type])) + elif out_type in garray_types: + out_protos.append(op.getGArray(garray_types[out_type])) + else: + raise Exception("""In {}: G-API operation can't produce the output with type: {} in position: {}""" + .format(cls.__name__, out_type.__name__, i)) + + return tuple(out_protos) if len(out_protos) != 1 else out_protos[0] + + # NB: Extend operation class + cls.id = op_id + cls.on = staticmethod(on) + return cls + + return op_with_params + + +def kernel(op_cls): + # NB: Second lvl decorator takes class to decorate + def kernel_with_params(cls): + # NB: Add new members to kernel class + cls.id = op_cls.id + cls.outMeta = op_cls.outMeta + return cls + + return kernel_with_params + + +cv.gapi.wip.GStreamerPipeline = cv.gapi_wip_gst_GStreamerPipeline diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp new file mode 100644 index 00000000000..66c3910756b --- /dev/null +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -0,0 +1,1172 @@ +#ifndef OPENCV_GAPI_PYOPENCV_GAPI_HPP +#define OPENCV_GAPI_PYOPENCV_GAPI_HPP + +#ifdef HAVE_OPENCV_GAPI + +#ifdef _MSC_VER +#pragma warning(disable: 4503) // "decorated name length exceeded" +#endif + +#include +#include + +// NB: Python wrapper replaces :: with _ for classes +using gapi_GKernelPackage = cv::GKernelPackage; +using gapi_GNetPackage = cv::gapi::GNetPackage; +using gapi_ie_PyParams = cv::gapi::ie::PyParams; +using gapi_onnx_PyParams = cv::gapi::onnx::PyParams; +using gapi_ov_PyParams = cv::gapi::ov::PyParams; +using gapi_wip_IStreamSource_Ptr = cv::Ptr; +using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback; +using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback; +using vector_GNetParam = std::vector; +using vector_GMat = std::vector; +using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity; +using GStreamerSource_OutputType = cv::gapi::wip::GStreamerSource::OutputType; +using map_string_and_int = std::map; +using map_string_and_string = std::map; +using map_string_and_string = std::map; +using map_string_and_vector_size_t = std::map>; +using map_string_and_vector_float = std::map>; +using map_int_and_double = std::map; +using ep_OpenVINO = cv::gapi::onnx::ep::OpenVINO; +using ep_DirectML = cv::gapi::onnx::ep::DirectML; +using ep_CoreML = cv::gapi::onnx::ep::CoreML; +using ep_CUDA = cv::gapi::onnx::ep::CUDA; +using ep_TensorRT = cv::gapi::onnx::ep::TensorRT; + +// NB: Python wrapper generate T_U for T +// This behavior is only observed for inputs +using GOpaque_bool = cv::GOpaque; +using GOpaque_int = cv::GOpaque; +using GOpaque_double = cv::GOpaque; +using GOpaque_float = cv::GOpaque; +using GOpaque_string = cv::GOpaque; +using GOpaque_Point2i = cv::GOpaque; +using GOpaque_Point2f = cv::GOpaque; +using GOpaque_Size = cv::GOpaque; +using GOpaque_Rect = cv::GOpaque; + +using GArray_bool = cv::GArray; +using GArray_int = cv::GArray; +using GArray_double = cv::GArray; +using GArray_float = cv::GArray; +using GArray_string = cv::GArray; +using GArray_Point2i = cv::GArray; +using GArray_Point2f = cv::GArray; +using GArray_Point3f = cv::GArray; +using GArray_Size = cv::GArray; +using GArray_Rect = cv::GArray; +using GArray_Scalar = cv::GArray; +using GArray_Mat = cv::GArray; +using GArray_GMat = cv::GArray; +using GArray_Prim = cv::GArray; + +// FIXME: Python wrapper generate code without namespace std, +// so it cause error: "string wasn't declared" +// WA: Create using +using std::string; + +namespace cv +{ +namespace detail +{ + +class PyObjectHolder +{ +public: + PyObjectHolder(PyObject* o, bool owner = true); + PyObject* get() const; + +private: + class Impl; + std::shared_ptr m_impl; +}; + +} // namespace detail +} // namespace cv + +class cv::detail::PyObjectHolder::Impl +{ +public: + Impl(PyObject* object, bool owner); + PyObject* get() const; + ~Impl(); + +private: + PyObject* m_object; +}; + +cv::detail::PyObjectHolder::Impl::Impl(PyObject* object, bool owner) + : m_object(object) +{ + // NB: Become an owner of that PyObject. + // Need to store this and get access + // after the caller which provide the object is out of range. + if (owner) + { + // NB: Impossible take ownership if object is NULL. + GAPI_Assert(object); + Py_INCREF(m_object); + } +} + +cv::detail::PyObjectHolder::Impl::~Impl() +{ + // NB: If NULL was set, don't decrease counter. + if (m_object) + { + Py_DECREF(m_object); + } +} + +PyObject* cv::detail::PyObjectHolder::Impl::get() const +{ + return m_object; +} + +cv::detail::PyObjectHolder::PyObjectHolder(PyObject* object, bool owner) + : m_impl(new cv::detail::PyObjectHolder::Impl{object, owner}) +{ +} + +PyObject* cv::detail::PyObjectHolder::get() const +{ + return m_impl->get(); +} + +template<> +PyObject* pyopencv_from(const cv::detail::PyObjectHolder& v) +{ + PyObject* o = cv::util::any_cast(v).get(); + Py_INCREF(o); + return o; +} + +// #FIXME: Is it possible to implement pyopencv_from/pyopencv_to for generic +// cv::variant ? +template <> +PyObject* pyopencv_from(const cv::gapi::wip::draw::Prim& prim) +{ + switch (prim.index()) + { + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + case cv::gapi::wip::draw::Prim::index_of(): + return pyopencv_from(cv::util::get(prim)); + } + + util::throw_error(std::logic_error("Unsupported draw primitive type")); +} + +template <> +PyObject* pyopencv_from(const cv::gapi::wip::draw::Prims& value) +{ + return pyopencv_from_generic_vec(value); +} + +template<> +bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prim& value, const ArgInfo&) +{ +#define TRY_EXTRACT(Prim) \ + if (PyObject_TypeCheck(obj, reinterpret_cast(pyopencv_gapi_wip_draw_##Prim##_TypePtr))) \ + { \ + value = reinterpret_cast(obj)->v; \ + return true; \ + } \ + + TRY_EXTRACT(Rect) + TRY_EXTRACT(Text) + TRY_EXTRACT(Circle) + TRY_EXTRACT(Line) + TRY_EXTRACT(Mosaic) + TRY_EXTRACT(Image) + TRY_EXTRACT(Poly) +#undef TRY_EXTRACT + + failmsg("Unsupported primitive type"); + return false; +} + +template <> +bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prims& value, const ArgInfo& info) +{ + return pyopencv_to_generic_vec(obj, value, info); +} + +template <> +bool pyopencv_to(PyObject* obj, cv::GMetaArg& value, const ArgInfo&) +{ +#define TRY_EXTRACT(Meta) \ + if (PyObject_TypeCheck(obj, \ + reinterpret_cast(pyopencv_##Meta##_TypePtr))) \ + { \ + value = reinterpret_cast(obj)->v; \ + return true; \ + } \ + + TRY_EXTRACT(GMatDesc) + TRY_EXTRACT(GScalarDesc) + TRY_EXTRACT(GArrayDesc) + TRY_EXTRACT(GOpaqueDesc) +#undef TRY_EXTRACT + + failmsg("Unsupported cv::GMetaArg type"); + return false; +} + +template <> +bool pyopencv_to(PyObject* obj, cv::GMetaArgs& value, const ArgInfo& info) +{ + return pyopencv_to_generic_vec(obj, value, info); +} + + +template<> +PyObject* pyopencv_from(const cv::GArg& value) +{ + GAPI_Assert(value.kind != cv::detail::ArgKind::GOBJREF); +#define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T: \ + { \ + return pyopencv_from(value.get()); \ + } + +#define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break + switch (value.opaque_kind) + { + HANDLE_CASE(BOOL, bool); + HANDLE_CASE(INT, int); + HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); + HANDLE_CASE(DOUBLE, double); + HANDLE_CASE(FLOAT, float); + HANDLE_CASE(STRING, std::string); + HANDLE_CASE(POINT, cv::Point); + HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); + HANDLE_CASE(SIZE, cv::Size); + HANDLE_CASE(RECT, cv::Rect); + HANDLE_CASE(SCALAR, cv::Scalar); + HANDLE_CASE(MAT, cv::Mat); + HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder); + HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim); +#undef HANDLE_CASE +#undef UNSUPPORTED + } + util::throw_error(std::logic_error("Unsupported kernel input type")); +} + +template<> +bool pyopencv_to(PyObject* obj, cv::GArg& value, const ArgInfo& info) +{ + value = cv::GArg(cv::detail::PyObjectHolder(obj)); + return true; +} + +template <> +bool pyopencv_to(PyObject* obj, std::vector& value, const ArgInfo& info) +{ + return pyopencv_to_generic_vec(obj, value, info); +} + +template <> +PyObject* pyopencv_from(const std::vector& value) +{ + return pyopencv_from_generic_vec(value); +} + +template <> +bool pyopencv_to(PyObject* obj, std::vector& value, const ArgInfo& info) +{ + return pyopencv_to_generic_vec(obj, value, info); +} + +template <> +PyObject* pyopencv_from(const std::vector& value) +{ + return pyopencv_from_generic_vec(value); +} + +template<> +PyObject* pyopencv_from(const cv::detail::OpaqueRef& o) +{ + switch (o.getKind()) + { + case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_INT : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_UINT64 : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_POINT : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_POINT2F : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_POINT3F : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_SIZE : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_SCALAR : break; + case cv::detail::OpaqueKind::CV_MAT : break; + } + + PyErr_SetString(PyExc_TypeError, "Unsupported GOpaque type"); + return NULL; +} + +template <> +PyObject* pyopencv_from(const cv::detail::VectorRef& v) +{ + switch (v.getKind()) + { + case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_INT : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_UINT64 : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_POINT : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_POINT2F : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_POINT3F : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_SIZE : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_SCALAR : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_MAT : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from_generic_vec(v.rref()); + } + + PyErr_SetString(PyExc_TypeError, "Unsupported GArray type"); + return NULL; +} + +template <> +PyObject* pyopencv_from(const GRunArg& v) +{ + switch (v.index()) + { + case GRunArg::index_of(): + return pyopencv_from(util::get(v)); + + case GRunArg::index_of(): + return pyopencv_from(util::get(v)); + + case GRunArg::index_of(): + return pyopencv_from(util::get(v)); + + case GRunArg::index_of(): + return pyopencv_from(util::get(v)); + } + + PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs. Index of variant is unknown"); + return NULL; +} + +template +PyObject* pyopencv_from(const cv::optional& opt) +{ + if (!opt.has_value()) + { + Py_RETURN_NONE; + } + return pyopencv_from(*opt); +} + +template <> +PyObject* pyopencv_from(const GOptRunArg& v) +{ + switch (v.index()) + { + case GOptRunArg::index_of>(): + return pyopencv_from(util::get>(v)); + + case GOptRunArg::index_of>(): + return pyopencv_from(util::get>(v)); + + case GOptRunArg::index_of>(): + return pyopencv_from(util::get>(v)); + + case GOptRunArg::index_of>(): + return pyopencv_from(util::get>(v)); + } + + PyErr_SetString(PyExc_TypeError, "Failed to unpack GOptRunArg. Index of variant is unknown"); + return NULL; +} + +template<> +PyObject* pyopencv_from(const GRunArgs& value) +{ + return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value); +} + +template<> +PyObject* pyopencv_from(const GOptRunArgs& value) +{ + return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value); +} + +// FIXME: cv::variant should be wrapped once for all types. +template <> +PyObject* pyopencv_from(const cv::util::variant& v) +{ + using RunArgs = cv::util::variant; + switch (v.index()) + { + case RunArgs::index_of(): + return pyopencv_from(util::get(v)); + case RunArgs::index_of(): + return pyopencv_from(util::get(v)); + } + + PyErr_SetString(PyExc_TypeError, "Failed to recognize kind of RunArgs. Index of variant is unknown"); + return NULL; +} + +template +void pyopencv_to_with_check(PyObject* from, T& to, const std::string& msg = "") +{ + if (!pyopencv_to(from, to, ArgInfo("", false))) + { + cv::util::throw_error(std::logic_error(msg)); + } +} + +template +void pyopencv_to_generic_vec_with_check(PyObject* from, + std::vector& to, + const std::string& msg = "") +{ + if (!pyopencv_to_generic_vec(from, to, ArgInfo("", false))) + { + cv::util::throw_error(std::logic_error(msg)); + } +} + +template +static T extract_proto_args(PyObject* py_args) +{ + using namespace cv; + + GProtoArgs args; + Py_ssize_t size = PyList_Size(py_args); + args.reserve(size); + for (int i = 0; i < size; ++i) + { + PyObject* item = PyList_GetItem(py_args, i); + if (PyObject_TypeCheck(item, reinterpret_cast(pyopencv_GScalar_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v); + } + else if (PyObject_TypeCheck(item, reinterpret_cast(pyopencv_GMat_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v); + } + else if (PyObject_TypeCheck(item, reinterpret_cast(pyopencv_GOpaqueT_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v.strip()); + } + else if (PyObject_TypeCheck(item, reinterpret_cast(pyopencv_GArrayT_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v.strip()); + } + else + { + util::throw_error(std::logic_error("Unsupported type for GProtoArgs")); + } + } + + return T(std::move(args)); +} + +static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::OpaqueKind kind) +{ +#define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T: \ +{ \ + O obj{}; \ + pyopencv_to_with_check(from, obj, "Failed to obtain " # O); \ + return cv::detail::OpaqueRef{std::move(obj)}; \ +} +#define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break + switch (kind) + { + HANDLE_CASE(BOOL, bool); + HANDLE_CASE(INT, int); + HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); + HANDLE_CASE(DOUBLE, double); + HANDLE_CASE(FLOAT, float); + HANDLE_CASE(STRING, std::string); + HANDLE_CASE(POINT, cv::Point); + HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); + HANDLE_CASE(SIZE, cv::Size); + HANDLE_CASE(RECT, cv::Rect); + HANDLE_CASE(UNKNOWN, cv::GArg); + UNSUPPORTED(SCALAR); + UNSUPPORTED(MAT); + UNSUPPORTED(DRAW_PRIM); +#undef HANDLE_CASE +#undef UNSUPPORTED + } + util::throw_error(std::logic_error("Unsupported type for GOpaqueT")); +} + +static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::OpaqueKind kind) +{ +#define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T: \ +{ \ + std::vector obj; \ + pyopencv_to_generic_vec_with_check(from, obj, "Failed to obtain vector of " # O); \ + return cv::detail::VectorRef{std::move(obj)}; \ +} +#define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break + switch (kind) + { + HANDLE_CASE(BOOL, bool); + HANDLE_CASE(INT, int); + HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); + HANDLE_CASE(DOUBLE, double); + HANDLE_CASE(FLOAT, float); + HANDLE_CASE(STRING, std::string); + HANDLE_CASE(POINT, cv::Point); + HANDLE_CASE(POINT2F, cv::Point2f); + HANDLE_CASE(POINT3F, cv::Point3f); + HANDLE_CASE(SIZE, cv::Size); + HANDLE_CASE(RECT, cv::Rect); + HANDLE_CASE(SCALAR, cv::Scalar); + HANDLE_CASE(MAT, cv::Mat); + HANDLE_CASE(UNKNOWN, cv::GArg); + HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim); +#undef HANDLE_CASE +#undef UNSUPPORTED + } + util::throw_error(std::logic_error("Unsupported type for GArrayT")); +} + +static cv::GRunArg extract_run_arg(const cv::GTypeInfo& info, PyObject* item) +{ + switch (info.shape) + { + case cv::GShape::GMAT: + { + // NB: In case streaming it can be IStreamSource or cv::Mat + if (PyObject_TypeCheck(item, + reinterpret_cast(pyopencv_gapi_wip_IStreamSource_TypePtr))) + { + cv::gapi::wip::IStreamSource::Ptr source = + reinterpret_cast(item)->v; + return source; + } + cv::Mat obj; + pyopencv_to_with_check(item, obj, "Failed to obtain cv::Mat"); + return obj; + } + case cv::GShape::GSCALAR: + { + cv::Scalar obj; + pyopencv_to_with_check(item, obj, "Failed to obtain cv::Scalar"); + return obj; + } + case cv::GShape::GOPAQUE: + { + return extract_opaque_ref(item, info.kind); + } + case cv::GShape::GARRAY: + { + return extract_vector_ref(item, info.kind); + } + case cv::GShape::GFRAME: + { + // NB: Isn't supported yet. + break; + } + } + + util::throw_error(std::logic_error("Unsupported output shape")); +} + +static cv::GRunArgs extract_run_args(const cv::GTypesInfo& info, PyObject* py_args) +{ + GAPI_Assert(PyList_Check(py_args)); + + cv::GRunArgs args; + Py_ssize_t list_size = PyList_Size(py_args); + args.reserve(list_size); + + for (int i = 0; i < list_size; ++i) + { + args.push_back(extract_run_arg(info[i], PyList_GetItem(py_args, i))); + } + + return args; +} + +static cv::GMetaArg extract_meta_arg(const cv::GTypeInfo& info, PyObject* item) +{ + switch (info.shape) + { + case cv::GShape::GMAT: + { + cv::Mat obj; + pyopencv_to_with_check(item, obj, "Failed to obtain cv::Mat"); + return cv::GMetaArg{cv::descr_of(obj)}; + } + case cv::GShape::GSCALAR: + { + cv::Scalar obj; + pyopencv_to_with_check(item, obj, "Failed to obtain cv::Scalar"); + return cv::GMetaArg{cv::descr_of(obj)}; + } + case cv::GShape::GARRAY: + { + return cv::GMetaArg{cv::empty_array_desc()}; + } + case cv::GShape::GOPAQUE: + { + return cv::GMetaArg{cv::empty_gopaque_desc()}; + } + case cv::GShape::GFRAME: + { + // NB: Isn't supported yet. + break; + } + } + util::throw_error(std::logic_error("Unsupported output shape")); +} + +static cv::GMetaArgs extract_meta_args(const cv::GTypesInfo& info, PyObject* py_args) +{ + GAPI_Assert(PyList_Check(py_args)); + + cv::GMetaArgs metas; + Py_ssize_t list_size = PyList_Size(py_args); + metas.reserve(list_size); + + for (int i = 0; i < list_size; ++i) + { + metas.push_back(extract_meta_arg(info[i], PyList_GetItem(py_args, i))); + } + + return metas; +} + +static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel, + const cv::gapi::python::GPythonContext &ctx) +{ + const auto& ins = ctx.ins; + const auto& in_metas = ctx.in_metas; + const auto& out_info = ctx.out_info; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GRunArgs outs; + try + { + // NB: Doesn't increase reference counter (false), + // because PyObject already have ownership. + // In case exception decrement reference counter. + cv::detail::PyObjectHolder args( + PyTuple_New(ctx.m_state.has_value() ? ins.size() + 1 : ins.size()), false); + for (size_t i = 0; i < ins.size(); ++i) + { + // NB: If meta is monostate then object isn't associated with G-TYPE. + if (cv::util::holds_alternative(in_metas[i])) + { + PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i])); + continue; + } + + switch (in_metas[i].index()) + { + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get())); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get())); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get())); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get())); + break; + case cv::GMetaArg::index_of(): + util::throw_error(std::logic_error("GFrame isn't supported for custom operation")); + break; + } + } + + if (ctx.m_state.has_value()) + { + PyTuple_SetItem(args.get(), ins.size(), pyopencv_from(ctx.m_state.value())); + } + + // NB: Doesn't increase reference counter (false). + // In case PyObject_CallObject return NULL, do nothing in destructor. + cv::detail::PyObjectHolder result( + PyObject_CallObject(kernel.get(), args.get()), false); + + if (PyErr_Occurred()) + { + PyErr_PrintEx(0); + PyErr_Clear(); + throw std::logic_error("Python kernel failed with error!"); + } + // NB: In fact it's impossible situation, because errors were handled above. + GAPI_Assert(result.get() && "Python kernel returned NULL!"); + + if (out_info.size() == 1) + { + outs = cv::GRunArgs{extract_run_arg(out_info[0], result.get())}; + } + else if (out_info.size() > 1) + { + GAPI_Assert(PyTuple_Check(result.get())); + + Py_ssize_t tuple_size = PyTuple_Size(result.get()); + outs.reserve(tuple_size); + + for (int i = 0; i < tuple_size; ++i) + { + outs.push_back(extract_run_arg(out_info[i], PyTuple_GetItem(result.get(), i))); + } + } + else + { + // Seems to be impossible case. + GAPI_Error("InternalError"); + } + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + + return outs; +} + +static void unpackMetasToTuple(const cv::GMetaArgs& meta, + const cv::GArgs& gargs, + cv::detail::PyObjectHolder& tuple) +{ + size_t idx = 0; + for (auto&& m : meta) + { + switch (m.index()) + { + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, + pyopencv_from(cv::util::get(m))); + break; + case cv::GMetaArg::index_of(): + PyTuple_SetItem(tuple.get(), idx, pyopencv_from(gargs[idx])); + break; + case cv::GMetaArg::index_of(): + util::throw_error( + std::logic_error("GFrame isn't supported for custom operation")); + break; + } + ++idx; + } +} + +static cv::GArg run_py_setup(cv::detail::PyObjectHolder setup, + const cv::GMetaArgs &meta, + const cv::GArgs &gargs) +{ + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GArg state; + try + { + // NB: Doesn't increase reference counter (false), + // because PyObject already have ownership. + // In case exception decrement reference counter. + cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false); + unpackMetasToTuple(meta, gargs, args); + + PyObject *py_kernel_state = PyObject_CallObject(setup.get(), args.get()); + if (PyErr_Occurred()) + { + PyErr_PrintEx(0); + PyErr_Clear(); + throw std::logic_error("Python kernel setup failed with error!"); + } + // NB: In fact it's impossible situation, because errors were handled above. + GAPI_Assert(py_kernel_state && "Python kernel setup returned NULL!"); + + if (!pyopencv_to(py_kernel_state, state, ArgInfo("arg", false))) + { + util::throw_error(std::logic_error("Failed to convert python state")); + } + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + return state; +} + +static GMetaArg get_meta_arg(PyObject* obj) +{ + cv::GMetaArg arg; + if (!pyopencv_to(obj, arg, ArgInfo("arg", false))) + { + util::throw_error(std::logic_error("Unsupported output meta type")); + } + return arg; +} + +static cv::GMetaArgs get_meta_args(PyObject* tuple) +{ + size_t size = PyTuple_Size(tuple); + + cv::GMetaArgs metas; + metas.reserve(size); + for (size_t i = 0; i < size; ++i) + { + metas.push_back(get_meta_arg(PyTuple_GetItem(tuple, i))); + } + + return metas; +} + +static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta, + const cv::GMetaArgs &meta, + const cv::GArgs &gargs) +{ + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GMetaArgs out_metas; + try + { + // NB: Doesn't increase reference counter (false), + // because PyObject already have ownership. + // In case exception decrement reference counter. + cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false); + unpackMetasToTuple(meta, gargs, args); + // NB: Doesn't increase reference counter (false). + // In case PyObject_CallObject return NULL, do nothing in destructor. + cv::detail::PyObjectHolder result( + PyObject_CallObject(out_meta.get(), args.get()), false); + + if (PyErr_Occurred()) + { + PyErr_PrintEx(0); + PyErr_Clear(); + throw std::logic_error("Python outMeta failed with error!"); + } + // NB: In fact it's impossible situation, because errors were handled above. + GAPI_Assert(result.get() && "Python outMeta returned NULL!"); + + out_metas = PyTuple_Check(result.get()) ? get_meta_args(result.get()) + : cv::GMetaArgs{get_meta_arg(result.get())}; + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + + return out_metas; +} + +static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObject*) +{ + using namespace cv; + GKernelPackage pkg; + Py_ssize_t size = PyTuple_Size(py_args); + + for (int i = 0; i < size; ++i) + { + PyObject* user_kernel = PyTuple_GetItem(py_args, i); + + PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id"); + if (!id_obj) + { + PyErr_SetString(PyExc_TypeError, + "Python kernel should contain id, please use cv.gapi.kernel to define kernel"); + return NULL; + } + + PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta"); + if (!out_meta) + { + PyErr_SetString(PyExc_TypeError, + "Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel"); + return NULL; + } + + PyObject* run = PyObject_GetAttrString(user_kernel, "run"); + if (!run) + { + PyErr_SetString(PyExc_TypeError, + "Python kernel should contain run, please use cv.gapi.kernel to define kernel"); + return NULL; + } + PyObject* setup = nullptr; + if (PyObject_HasAttrString(user_kernel, "setup")) { + setup = PyObject_GetAttrString(user_kernel, "setup"); + } + + std::string id; + if (!pyopencv_to(id_obj, id, ArgInfo("id", false))) + { + PyErr_SetString(PyExc_TypeError, "Failed to obtain string"); + return NULL; + } + + using namespace std::placeholders; + + if (setup) + { + gapi::python::GPythonFunctor f( + id.c_str(), std::bind(run_py_meta, cv::detail::PyObjectHolder{out_meta}, _1, _2), + std::bind(run_py_kernel, cv::detail::PyObjectHolder{run}, _1), + std::bind(run_py_setup, cv::detail::PyObjectHolder{setup}, _1, _2)); + pkg.include(f); + } + else + { + gapi::python::GPythonFunctor f( + id.c_str(), std::bind(run_py_meta, cv::detail::PyObjectHolder{out_meta}, _1, _2), + std::bind(run_py_kernel, cv::detail::PyObjectHolder{run}, _1)); + pkg.include(f); + } + } + return pyopencv_from(pkg); +} + +static PyObject* pyopencv_cv_gapi_op(PyObject* , PyObject* py_args, PyObject*) +{ + using namespace cv; + Py_ssize_t size = PyTuple_Size(py_args); + std::string id; + if (!pyopencv_to(PyTuple_GetItem(py_args, 0), id, ArgInfo("id", false))) + { + PyErr_SetString(PyExc_TypeError, "Failed to obtain: operation id must be a string"); + return NULL; + } + PyObject* outMeta = PyTuple_GetItem(py_args, 1); + + cv::GArgs args; + for (int i = 2; i < size; i++) + { + PyObject* item = PyTuple_GetItem(py_args, i); + if (PyObject_TypeCheck(item, + reinterpret_cast(pyopencv_GMat_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v); + } + else if (PyObject_TypeCheck(item, + reinterpret_cast(pyopencv_GScalar_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v); + } + else if (PyObject_TypeCheck(item, + reinterpret_cast(pyopencv_GOpaqueT_TypePtr))) + { + auto&& arg = reinterpret_cast(item)->v.arg(); +#define HC(T, K) case cv::GOpaqueT::Storage:: index_of>(): \ + args.emplace_back(cv::util::get>(arg)); \ + break; \ + + SWITCH(arg.index(), GOPAQUE_TYPE_LIST_G, HC) +#undef HC + } + else if (PyObject_TypeCheck(item, + reinterpret_cast(pyopencv_GArrayT_TypePtr))) + { + auto&& arg = reinterpret_cast(item)->v.arg(); +#define HC(T, K) case cv::GArrayT::Storage:: index_of>(): \ + args.emplace_back(cv::util::get>(arg)); \ + break; \ + + SWITCH(arg.index(), GARRAY_TYPE_LIST_G, HC) +#undef HC + } + else + { + args.emplace_back(cv::GArg(cv::detail::PyObjectHolder{item})); + } + } + + cv::GKernel::M outMetaWrapper = std::bind(run_py_meta, + cv::detail::PyObjectHolder{outMeta}, + std::placeholders::_1, + std::placeholders::_2); + return pyopencv_from(cv::gapi::wip::op(id, outMetaWrapper, std::move(args))); +} + +template<> +bool pyopencv_to(PyObject* obj, cv::detail::ExtractArgsCallback& value, const ArgInfo&) +{ + cv::detail::PyObjectHolder holder{obj}; + value = cv::detail::ExtractArgsCallback{[=](const cv::GTypesInfo& info) + { + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GRunArgs args; + try + { + args = extract_run_args(info, holder.get()); + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + return args; + }}; + return true; +} + +template<> +bool pyopencv_to(PyObject* obj, cv::detail::ExtractMetaCallback& value, const ArgInfo&) +{ + cv::detail::PyObjectHolder holder{obj}; + value = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info) + { + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + cv::GMetaArgs args; + try + { + args = extract_meta_args(info, holder.get()); + } + catch (...) + { + PyGILState_Release(gstate); + throw; + } + PyGILState_Release(gstate); + return args; + }}; + return true; +} + +template +struct PyOpenCV_Converter> +{ + static PyObject* from(const cv::GArray& p) + { + return pyopencv_from(cv::GArrayT(p)); + } + static bool to(PyObject *obj, cv::GArray& value, const ArgInfo& info) + { + if (PyObject_TypeCheck(obj, reinterpret_cast(pyopencv_GArrayT_TypePtr))) + { + auto& array = reinterpret_cast(obj)->v; + try + { + value = cv::util::get>(array.arg()); + } + catch (...) + { + return false; + } + return true; + } + return false; + } +}; + +template +struct PyOpenCV_Converter> +{ + static PyObject* from(const cv::GOpaque& p) + { + return pyopencv_from(cv::GOpaqueT(p)); + } + static bool to(PyObject *obj, cv::GOpaque& value, const ArgInfo& info) + { + if (PyObject_TypeCheck(obj, reinterpret_cast(pyopencv_GOpaqueT_TypePtr))) + { + auto& opaque = reinterpret_cast(obj)->v; + try + { + value = cv::util::get>(opaque.arg()); + } + catch (...) + { + return false; + } + return true; + } + return false; + } +}; + +template<> +bool pyopencv_to(PyObject* obj, cv::GProtoInputArgs& value, const ArgInfo& info) +{ + try + { + value = extract_proto_args(obj); + return true; + } + catch (...) + { + failmsg("Can't parse cv::GProtoInputArgs"); + return false; + } +} + +template<> +bool pyopencv_to(PyObject* obj, cv::GProtoOutputArgs& value, const ArgInfo& info) +{ + try + { + value = extract_proto_args(obj); + return true; + } + catch (...) + { + failmsg("Can't parse cv::GProtoOutputArgs"); + return false; + } +} + +// extend cv.gapi methods +#define PYOPENCV_EXTRA_METHODS_GAPI \ + {"kernels", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_kernels), "kernels(...) -> GKernelPackage"}, \ + {"__op", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_op), "__op(...) -> retval\n"}, + + +#endif // HAVE_OPENCV_GAPI +#endif // OPENCV_GAPI_PYOPENCV_GAPI_HPP diff --git a/modules/gapi/misc/python/python_bridge.hpp b/modules/gapi/misc/python/python_bridge.hpp new file mode 100644 index 00000000000..f384b6907bd --- /dev/null +++ b/modules/gapi/misc/python/python_bridge.hpp @@ -0,0 +1,353 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_PYTHON_BRIDGE_HPP +#define OPENCV_GAPI_PYTHON_BRIDGE_HPP + +#include +#include +#include +#include // Prim + +#define ID(T, E) T +#define ID_(T, E) ID(T, E), + +#define WRAP_ARGS(T, E, G) \ + G(T, E) + +#define SWITCH(type, LIST_G, HC) \ + switch(type) { \ + LIST_G(HC, HC) \ + default: \ + GAPI_Error("Unsupported type"); \ + } + +using cv::gapi::wip::draw::Prim; + +#define GARRAY_TYPE_LIST_G(G, G2) \ +WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ +WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \ +WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \ +WRAP_ARGS(uint64_t , cv::gapi::ArgType::CV_UINT64, G) \ +WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \ +WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ +WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ +WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \ +WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \ +WRAP_ARGS(cv::Point3f , cv::gapi::ArgType::CV_POINT3F, G) \ +WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \ +WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G) \ +WRAP_ARGS(cv::Scalar , cv::gapi::ArgType::CV_SCALAR, G) \ +WRAP_ARGS(cv::Mat , cv::gapi::ArgType::CV_MAT, G) \ +WRAP_ARGS(Prim , cv::gapi::ArgType::CV_DRAW_PRIM, G) \ +WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \ +WRAP_ARGS(cv::GMat , cv::gapi::ArgType::CV_GMAT, G2) \ + +#define GOPAQUE_TYPE_LIST_G(G, G2) \ +WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ +WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \ +WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \ +WRAP_ARGS(uint64_t , cv::gapi::ArgType::CV_UINT64, G) \ +WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \ +WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ +WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ +WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \ +WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \ +WRAP_ARGS(cv::Point3f , cv::gapi::ArgType::CV_POINT3F, G) \ +WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \ +WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \ +WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G2) \ + +namespace cv { +namespace gapi { + +// NB: cv.gapi.CV_BOOL in python +enum ArgType { + CV_BOOL, + CV_INT, + CV_INT64, + CV_UINT64, + CV_DOUBLE, + CV_FLOAT, + CV_STRING, + CV_POINT, + CV_POINT2F, + CV_POINT3F, + CV_SIZE, + CV_RECT, + CV_SCALAR, + CV_MAT, + CV_GMAT, + CV_DRAW_PRIM, + CV_ANY, +}; + +GAPI_EXPORTS_W inline cv::GInferOutputs infer(const String& name, const cv::GInferInputs& inputs) +{ + return infer(name, inputs); +} + +GAPI_EXPORTS_W inline GInferOutputs infer(const std::string& name, + const cv::GOpaque& roi, + const GInferInputs& inputs) +{ + return infer(name, roi, inputs); +} + +GAPI_EXPORTS_W inline GInferListOutputs infer(const std::string& name, + const cv::GArray& rois, + const GInferInputs& inputs) +{ + return infer(name, rois, inputs); +} + +GAPI_EXPORTS_W inline GInferListOutputs infer2(const std::string& name, + const cv::GMat in, + const GInferListInputs& inputs) +{ + return infer2(name, in, inputs); +} + +} // namespace gapi + +namespace detail { + +template