GAPI Fluid: Dynamic window size
authorAnna Khakimova <anna.khakimova@intel.com>
Wed, 30 Oct 2019 12:25:32 +0000 (15:25 +0300)
committerAnna Khakimova <anna.khakimova@intel.com>
Wed, 30 Oct 2019 14:09:54 +0000 (17:09 +0300)
modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp
modules/gapi/src/backends/fluid/gfluidbackend.cpp
modules/gapi/src/backends/fluid/gfluidbackend.hpp
modules/gapi/src/backends/fluid/gfluidimgproc.cpp

index 3235de7..a0e6894 100644 (file)
@@ -68,19 +68,21 @@ public:
     // This function is a generic "getBorder" callback (extracts border-related data from kernel's input parameters)
     using B = std::function<gapi::fluid::BorderOpt(const GMetaArgs&, const GArgs&)>;
 
+    // This function is a generic "getWindow" callback (extracts window-related data from kernel's input parameters)
+    using GW = std::function<int(const GMetaArgs&, const GArgs&)>;
+
     // FIXME: move implementations out of header file
     GFluidKernel() {}
-    GFluidKernel(int w, Kind k, int l, bool scratch, const F& f, const IS &is, const RS &rs, const B& b)
-        : m_window(w)
-        , m_kind(k)
+    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_b(b)
+        , m_gw(win) {}
 
-    int m_window = -1;
     Kind m_kind;
     const int  m_lpi     = -1;
     const bool m_scratch = false;
@@ -89,6 +91,7 @@ public:
     const IS   m_is;
     const RS   m_rs;
     const B    m_b;
+    const GW   m_gw;
 };
 
 // FIXME!!!
@@ -255,6 +258,67 @@ struct get_border_helper<false, Impl, Ins...>
     }
 };
 
+template<bool CallCustomGetWindow, typename, typename... Ins>
+struct get_window_helper;
+
+template<typename Impl, typename... Ins>
+struct get_window_helper<true, Impl, Ins...>
+{
+    template<int... IIs>
+    static int get_window_impl(const GMetaArgs &metas,
+                               const cv::GArgs &in_args,
+                               cv::detail::Seq<IIs...>)
+    {
+        return Impl::getWindow(cv::detail::get_in_meta<Ins>(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<sizeof...(Ins)>::type());
+    }
+};
+
+template<typename Impl, typename... Ins>
+struct get_window_helper<false, Impl, Ins...>
+{
+    static int help(const cv::GMetaArgs &,
+                    const cv::GArgs     &)
+    {
+        return Impl::Window;
+    }
+};
+
+template<typename C, typename T>
+struct has_Window
+{
+private:
+    template<class U>
+    static constexpr auto Check(U*) -> typename std::is_same<decltype(U::Window), T>::type;
+
+    template<typename>
+    static constexpr std::false_type Check(...);
+
+    typedef decltype(Check<C>(0)) Result;
+
+public:
+    static constexpr bool value = Result::value;
+};
+
+template<bool hasWindow, typename Impl>
+struct callCustomGetBorder;
+
+template<typename Impl>
+struct callCustomGetBorder<true, Impl>
+{
+    static constexpr bool value = (Impl::Window != 1);
+};
+
+template<typename Impl>
+struct callCustomGetBorder<false, Impl>
+{
+    static constexpr bool value = true;
+};
+
 template<typename, typename, typename, bool UseScratch>
 struct FluidCallHelper;
 
@@ -299,10 +363,17 @@ struct FluidCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>, UseScratch
 
     static gapi::fluid::BorderOpt getBorder(const GMetaArgs &metas, const cv::GArgs &in_args)
     {
+        constexpr bool hasWindow = has_Window<Impl, const int>::value;
+
         // User must provide "init" callback if Window != 1
         // TODO: move to constexpr if when we enable C++17
-        constexpr bool callCustomGetBorder = (Impl::Window != 1);
-        return get_border_helper<callCustomGetBorder, Impl, Ins...>::help(metas, in_args);
+        return get_border_helper<callCustomGetBorder<hasWindow, Impl>::value, Impl, Ins...>::help(metas, in_args);
+    }
+
+    static int getWindow(const GMetaArgs &metas, const cv::GArgs &in_args)
+    {
+        constexpr bool callCustomGetWindow = !(has_Window<Impl, const int>::value);
+        return get_window_helper<callCustomGetWindow, Impl, Ins...>::help(metas, in_args);
     }
 };
 } // namespace detail
@@ -322,9 +393,9 @@ public:
     {
         // FIXME: call() and getOutMeta() needs to be renamed so it is clear these
         // functions are internal wrappers, not user API
-        return GFluidKernel(Impl::Window, Impl::Kind, Impl::LPI,
+        return GFluidKernel(Impl::Kind, Impl::LPI,
                             UseScratch,
-                            &P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder);
+                            &P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder, &P::getWindow);
     }
 
     static cv::gapi::GBackend backend() { return cv::gapi::fluid::backend(); }
index 1a8a3c8..6f45b3d 100644 (file)
@@ -69,7 +69,7 @@ namespace
         {
             GFluidModel fm(graph);
             auto fluid_impl = cv::util::any_cast<cv::GFluidKernel>(impl.opaque);
-            fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, {}, 0.0});
+            fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, -1, {}, 0.0});
         }
 
         virtual EPtr compile(const ade::Graph &graph,
@@ -178,6 +178,12 @@ private:
     virtual void setRatio(double) override { /* nothing */ }
 public:
     using FluidAgent::FluidAgent;
+    int m_window;
+
+    FluidFilterAgent(const ade::Graph &g, ade::NodeHandle nh)
+        : FluidAgent(g, nh)
+        , m_window(GConstFluidModel(g).metadata(nh).get<FluidUnit>().window)
+    {}
 };
 
 struct FluidResizeAgent : public FluidAgent
@@ -287,11 +293,11 @@ static int calcResizeWindow(int inH, int outH)
     }
 }
 
-static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi, std::size_t inPort)
+static int maxLineConsumption(const cv::GFluidKernel::Kind kind, int window, int inH, int outH, int lpi, std::size_t inPort)
 {
-    switch (k.m_kind)
+    switch (kind)
     {
-    case cv::GFluidKernel::Kind::Filter: return k.m_window + lpi - 1; break;
+    case cv::GFluidKernel::Kind::Filter: return window + lpi - 1; break;
     case cv::GFluidKernel::Kind::Resize:
     {
         if  (inH >= outH)
@@ -312,11 +318,11 @@ static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int
     }
 }
 
-static int borderSize(const cv::GFluidKernel& k)
+static int borderSize(const cv::GFluidKernel::Kind kind, int window)
 {
-    switch (k.m_kind)
+    switch (kind)
     {
-    case cv::GFluidKernel::Kind::Filter: return (k.m_window - 1) / 2; break;
+    case cv::GFluidKernel::Kind::Filter: return (window - 1) / 2; break;
     // Resize never reads from border pixels
     case cv::GFluidKernel::Kind::Resize: return 0; break;
     case cv::GFluidKernel::Kind::NV12toRGB: return 0; break;
@@ -406,13 +412,13 @@ std::pair<int,int> cv::gimpl::FluidUpscaleMapper::linesReadAndNextWindow(int out
 int cv::gimpl::FluidFilterAgent::firstWindow(std::size_t) const
 {
     int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines);
-    return k.m_window + lpi - 1;
+    return m_window + lpi - 1;
 }
 
 std::pair<int,int> cv::gimpl::FluidFilterAgent::linesReadAndnextWindow(std::size_t) const
 {
     int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines - k.m_lpi);
-    return std::make_pair(k.m_lpi, k.m_window - 1 + lpi);
+    return std::make_pair(k.m_lpi, m_window - 1 + lpi);
 }
 
 int cv::gimpl::FluidResizeAgent::firstWindow(std::size_t) const
@@ -1016,14 +1022,14 @@ namespace
                     if (d.shape == cv::GShape::GMAT)
                     {
                         auto port = g.metadata(in_edge).get<Input>().port;
-                        fu.line_consumption[port] = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi, port);
+                        fu.line_consumption[port] = maxLineConsumption(fu.k.m_kind, fu.window, in_h, out_h, fu.k.m_lpi, port);
 
                         GModel::log(g, node, "Line consumption (port " + std::to_string(port) + "): "
                                     + std::to_string(fu.line_consumption[port]));
                     }
                 }
 
-                fu.border_size = borderSize(fu.k);
+                fu.border_size = borderSize(fu.k.m_kind, fu.window);
                 GModel::log(g, node, "Border size: " + std::to_string(fu.border_size));
             }
         }
@@ -1489,7 +1495,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
     // FIXME:
     // move to unpackKernel method
     // when https://gitlab-icv.inn.intel.com/G-API/g-api/merge_requests/66 is merged
-    ectx.addPass("exec", "init_fluid_unit_borders", [](ade::passes::PassContext &ctx)
+    ectx.addPass("exec", "init_fluid_unit_windows_and_borders", [](ade::passes::PassContext &ctx)
     {
         GModel::Graph g(ctx.graph);
         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
@@ -1505,9 +1511,13 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
                 // FIXME: check that op has only one data node on input
                 auto &fu = fg.metadata(node).get<FluidUnit>();
                 const auto &op = g.metadata(node).get<Op>();
+                auto inputMeta = GModel::collectInputMeta(fg, node);
+
+                // Trigger user-defined "getWindow" callback
+                fu.window = fu.k.m_gw(inputMeta, op.args);
 
                 // Trigger user-defined "getBorder" callback
-                fu.border = fu.k.m_b(GModel::collectInputMeta(fg, node), op.args);
+                fu.border = fu.k.m_b(inputMeta, op.args);
             }
         }
     });
index a2c853a..c8598d7 100644 (file)
@@ -27,6 +27,7 @@ struct FluidUnit
     GFluidKernel k;
     gapi::fluid::BorderOpt border;
     int border_size;
+    int window;
     std::vector<int> line_consumption;
     double ratio;
 };
index dfbce1e..1db0ba5 100644 (file)
@@ -778,7 +778,6 @@ GAPI_FLUID_KERNEL(GFluidSepFilter, cv::gapi::imgproc::GSepFilter, true)
 GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
 {
     // TODO: support kernel height 3, 5, 7, 9, ...
-    static const int Window = 3;
 
     static void run(const     View  &    src,
                     const cv::Size  &    ksize,
@@ -828,6 +827,7 @@ GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
                             const cv::Scalar & /* borderValue */,
                                   Buffer  &    scratch)
     {
+        GAPI_Assert(ksize.height == ksize.width);
         int kxsize = ksize.width;
         int kysize = ksize.height;
 
@@ -835,7 +835,7 @@ GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
         int chan  = in.chan;
 
         int buflen = kxsize + kysize +       // x, y kernels
-                     width * chan * Window;  // work buffers
+                     width * chan * ksize.height;  // work buffers
 
         cv::gapi::own::Size bufsize(buflen, 1);
         GMatDesc bufdesc = {CV_32F, 1, bufsize};
@@ -876,6 +876,17 @@ GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
     {
         return { borderType, borderValue};
     }
+
+    static int getWindow(const cv::GMatDesc& /* src */,
+                         const cv::Size&        ksize,
+                         double              /* sigmaX */,
+                         double              /* sigmaY */,
+                         int                 /* borderType */,
+                         const cv::Scalar&   /* borderValue */)
+    {
+        GAPI_Assert(ksize.height == ksize.width);
+        return ksize.height;
+    }
 };
 
 //---------------------
@@ -1688,8 +1699,8 @@ GAPI_FLUID_KERNEL(GFluidRGB2YUV422, cv::gapi::imgproc::GRGB2YUV422, false)
     static const int Window = 1;
     static const auto Kind = cv::GFluidKernel::Kind::Filter;
 
-    static void run(const cv::gapi::fluid::View&   in,
-            cv::gapi::fluid::Buffer& out)
+    static void run(const cv::gapi::fluid::View& in,
+                    cv::gapi::fluid::Buffer& out)
     {
         const auto *src = in.InLine<uchar>(0);
         auto *dst = out.OutLine<uchar>();