#+TITLE:     OpenCV 4.4 Graph API
#+AUTHOR:    Dmitry Matveev\newline Intel Corporation
#+OPTIONS: H:2 toc:t num:t
#+LATEX_CLASS: beamer
#+LATEX_CLASS_OPTIONS: [presentation]
#+LATEX_HEADER: \usepackage{transparent} \usepackage{listings} \usepackage{pgfplots} \usepackage{mtheme.sty/beamerthememetropolis}
#+LATEX_HEADER: \setbeamertemplate{frame footer}{OpenCV 4.4 G-API: Overview and programming by example}
#+BEAMER_HEADER: \subtitle{Overview and programming by example}
#+BEAMER_HEADER: \titlegraphic{ \vspace*{3cm}\hspace*{5cm} {\transparent{0.2}\includegraphics[height=\textheight]{ocv_logo.eps}}}
#+COLUMNS: %45ITEM %10BEAMER_ENV(Env) %10BEAMER_ACT(Act) %4BEAMER_COL(Col) %8BEAMER_OPT(Opt)

* G-API: What is, why, what's for?

** OpenCV evolution in one slide

*** Version 1.x -- Library inception

- Just a set of CV functions + helpers around (visualization, IO);

*** Version 2.x -- Library rewrite

- OpenCV meets C++, ~cv::Mat~ replaces ~IplImage*~;

*** Version 3.0 -- Welcome Transparent API (T-API)

- ~cv::UMat~ is introduced as a /transparent/ addition to
  ~cv::Mat~;
- With ~cv::UMat~, an OpenCL kernel can be enqeueud instead of
  immediately running C code;
- ~cv::UMat~ data is kept on a /device/ until explicitly queried.

** OpenCV evolution in one slide (cont'd)
# FIXME: Learn proper page-breaking!

*** Version 4.0 -- Welcome Graph API (G-API)

- A new separate module (not a full library rewrite);
- A framework (or even a /meta/-framework);
- Usage model:
  - /Express/ an image/vision processing graph and then /execute/ it;
  - Fine-tune execution without changes in the graph;
- Similar to Halide -- separates logic from
  platform details.
- More than Halide:
  - Kernels can be written in unconstrained platform-native code;
  - Halide can serve as a backend (one of many).

** OpenCV evolution in one slide (cont'd)
# FIXME: Learn proper page-breaking!

*** Version 4.2 -- New horizons

- Introduced in-graph inference via OpenVINO™ Toolkit;
- Introduced video-oriented Streaming execution mode;
- Extended focus from individual image processing to the full
  application pipeline optimization.

*** Version 4.4 -- More on video

- Introduced a notion of stateful kernels;
  - The road to object tracking, background subtraction, etc. in the
    graph;
- Added more video-oriented operations (feature detection, Optical
  flow).

** Why G-API?

*** Why introduce a new execution model?

- Ultimately it is all about optimizations;
  - or at least about a /possibility/ to optimize;
- A CV algorithm is usually not a single function call, but a
  composition of functions;
- Different models operate at different levels of knowledge on the
  algorithm (problem) we run.

** Why G-API? (cont'd)
# FIXME: Learn proper page-breaking!

*** Why introduce a new execution model?

- *Traditional* -- every function can be optimized (e.g. vectorized)
  and parallelized, the rest is up to programmer to care about.
- *Queue-based* -- kernels are enqueued dynamically with no guarantee
  where the end is or what is called next;
- *Graph-based* -- nearly all information is there, some compiler
  magic can be done!

** What is G-API for?

*** Bring the value of graph model with OpenCV where it makes sense:

- *Memory consumption* can be reduced dramatically;
- *Memory access* can be optimized to maximize cache reuse;
- *Parallelism* can be applied automatically where it is hard to do
  it manually;
  - It also becomes more efficient when working with graphs;
- *Heterogeneity* gets extra benefits like:
  - Avoiding unnecessary data transfers;
  - Shadowing transfer costs with parallel host co-execution;
  - Improving system throughput with frame-level pipelining.

* Programming with G-API

** G-API Basics

*** G-API Concepts

- *Graphs* are built by applying /operations/ to /data objects/;
  - API itself has no "graphs", it is expression-based instead;
- *Data objects* do not hold actual data, only capture /dependencies/;
- *Operations* consume and produce data objects.
- A graph is defined by specifying its /boundaries/ with data objects:
  - What data objects are /inputs/ to the graph?
  - What are its /outputs/?

** The code is worth a thousand words
   :PROPERTIES:
   :BEAMER_opt: shrink=42
   :END:

#+BEGIN_SRC C++
#include <opencv2/gapi.hpp>                            // G-API framework header
#include <opencv2/gapi/imgproc.hpp>                    // cv::gapi::blur()
#include <opencv2/highgui.hpp>                         // 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 <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

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 <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>
#include <opencv2/highgui.hpp>

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<T>~  | ~std::vector<T>~ | Dynamic; defined in runtime |
  |--------------+------------------+-----------------------------|
  | ~GOpaque<T>~ | ~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,<GMat(GMat)>,"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,<GMat(GMat)>,"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 <float>(0)
                       out.OutLine<float>(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 <opencv2/gapi/fluid/core.hpp>
  #include <opencv2/gapi/fluid/imgproc.hpp>
  ...
  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, <cv::GMat(cv::GMat)>, "pdf.example.od");

  cv::GMat in;
  cv::GMat blob = cv::gapi::infer<ObjDetect>(bgr);
  cv::GOpaque<cv::Size> size = cv::gapi::streaming::size(bgr);
  cv::GArray<cv::Rect>  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<T>~ is;
- ~cv::gapi::infer<T>~ 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* implenting 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<ObjDetect> {
      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<AgeGender> {
      ...
  }.cfgOutputLayers({"age_conv3", "prob"}); // array<string,2> !
  #+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<cv::Rect> 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
    <cv::gapi::wip::GCaptureSource>(input)
auto cc = pipeline.compileStreaming
    (cv::compile_args(kernels, networks))
cc.setSource(cv::gin(in_src));
cc.start();

std::vector<cv::Rect> 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!
