Merge pull request #20184 from sivanov-work:fix_gapi_empty_input
authorSergey Ivanov <sergey.ivanov@intel.com>
Thu, 10 Jun 2021 11:05:46 +0000 (14:05 +0300)
committerGitHub <noreply@github.com>
Thu, 10 Jun 2021 11:05:46 +0000 (14:05 +0300)
G-API: Add standalone fix for graph empty input

* Add sandalone fix for graph empty input

* Apply some review comments

* Fix whitespace

* Apply review comment: make Mat check more deeper

* Apply some comments

* Remove tracer apply exception throwing

* Apply comments: move validatio into gproto_priv.hpp

* Apply minor text correction

* Fix alignment, remove try-catch

modules/gapi/include/opencv2/gapi/gtype_traits.hpp
modules/gapi/include/opencv2/gapi/gtyped.hpp
modules/gapi/src/api/gproto.cpp
modules/gapi/src/api/gproto_priv.hpp
modules/gapi/src/compiler/gcompiler.cpp
modules/gapi/src/compiler/gcompiler.hpp
modules/gapi/test/gapi_gcompiled_tests.cpp

index 2e8dcb1..0b11b18 100644 (file)
@@ -43,6 +43,19 @@ namespace detail
         GOPAQUE,      // a cv::GOpaqueU (note - exactly GOpaqueU, not GOpaque<T>!)
     };
 
+    template<typename T>
+    constexpr const char* meta_to_string() noexcept;
+    template<>
+    constexpr const char* meta_to_string<cv::GMatDesc>() noexcept { return "GMatDesc"; }
+    template<>
+    constexpr const char* meta_to_string<cv::GScalarDesc>() noexcept { return "GScalarDesc"; }
+    template<>
+    constexpr const char* meta_to_string<cv::GArrayDesc>() noexcept { return "GArrayDesc"; }
+    template<>
+    constexpr const char* meta_to_string<cv::GOpaqueDesc>() noexcept { return "GOpaqueDesc"; }
+    template<>
+    constexpr const char* meta_to_string<cv::GFrameDesc>() noexcept { return "GFrameDesc";}
+
     // 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
index 6fe52a6..27d9777 100644 (file)
@@ -35,6 +35,7 @@ namespace detail
     template<> struct ProtoToMeta<cv::GScalar>  { using type = cv::GScalarDesc; };
     template<typename U> struct ProtoToMeta<cv::GArray<U> >  { using type = cv::GArrayDesc; };
     template<typename U> struct ProtoToMeta<cv::GOpaque<U> > { using type = cv::GOpaqueDesc; };
+    template<> struct ProtoToMeta<cv::GFrame>  { using type = cv::GFrameDesc; };
     template<typename T> using ProtoToMetaT = typename ProtoToMeta<T>::type;
 
     //workaround for MSVC 19.0 bug
index 0c7c646..94234c9 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "api/gorigin.hpp"
 #include "api/gproto_priv.hpp"
+#include "logger.hpp"
 
 // FIXME: it should be a visitor!
 // FIXME: Reimplement with traits?
@@ -201,6 +202,52 @@ bool cv::can_describe(const GMetaArgs &metas, const GRunArgs &args)
                      });
 }
 
+void cv::gimpl::proto::validate_input_meta_arg(const cv::GMetaArg& meta)
+{
+    switch (meta.index())
+    {
+        case cv::GMetaArg::index_of<cv::GMatDesc>():
+        {
+            cv::gimpl::proto::validate_input_meta(cv::util::get<GMatDesc>(meta)); //may throw
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+void cv::gimpl::proto::validate_input_meta(const cv::GMatDesc& meta)
+{
+    if (meta.dims.empty())
+    {
+        if (!(meta.size.height > 0 && meta.size.width > 0))
+        {
+            cv::util::throw_error
+                (std::logic_error(
+                 "Image format is invalid. Size must contain positive values"
+                 ", got width: " + std::to_string(meta.size.width ) +
+                 (", height: ") + std::to_string(meta.size.height)));
+        }
+
+        if (!(meta.chan > 0))
+        {
+            cv::util::throw_error
+                (std::logic_error(
+                 "Image format is invalid. Channel mustn't be negative value, got channel: " +
+                 std::to_string(meta.chan)));
+        }
+    }
+
+    if (!(meta.depth >= 0))
+    {
+        cv::util::throw_error
+            (std::logic_error(
+             "Image format is invalid. Depth must be positive value, got depth: " +
+             std::to_string(meta.depth)));
+    }
+    // All checks are ok
+}
+
 // FIXME: Is it tested for all types?
 // FIXME: Where does this validation happen??
 void cv::validate_input_arg(const GRunArg& arg)
@@ -212,13 +259,15 @@ void cv::validate_input_arg(const GRunArg& arg)
     case GRunArg::index_of<cv::UMat>():
     {
         const auto desc = cv::descr_of(util::get<cv::UMat>(arg));
-        GAPI_Assert(desc.size.height != 0 && desc.size.width != 0 && "incorrect dimensions of cv::UMat!"); break;
+        cv::gimpl::proto::validate_input_meta(desc); //may throw
+        break;
     }
 #endif //  !defined(GAPI_STANDALONE)
     case GRunArg::index_of<cv::Mat>():
     {
         const auto desc = cv::descr_of(util::get<cv::Mat>(arg));
-        GAPI_Assert(desc.size.height != 0 && desc.size.width != 0 && "incorrect dimensions of Mat!"); break;
+        cv::gimpl::proto::validate_input_meta(desc); //may throw
+        break;
     }
     default:
         // No extra handling
@@ -228,9 +277,13 @@ void cv::validate_input_arg(const GRunArg& arg)
 
 void cv::validate_input_args(const GRunArgs& args)
 {
+    GAPI_LOG_DEBUG(nullptr, "Total count: " << args.size());
+    size_t index = 0;
     for (const auto& arg : args)
     {
+        GAPI_LOG_DEBUG(nullptr, "Process index: " << index);
         validate_input_arg(arg);
+        index ++;
     }
 }
 
index b1d71df..e42afc3 100644 (file)
@@ -31,6 +31,9 @@ GProtoArg      rewrap    (const GArg      &arg);
 // FIXME:: GAPI_EXPORTS because of tests only!!
 GAPI_EXPORTS const void*    ptr       (const GRunArgP  &arg);
 
+void validate_input_meta_arg(const GMetaArg& meta);
+void validate_input_meta(const GMatDesc& meta);
+
 } // proto
 } // gimpl
 } // cv
index 2f442e4..7dd3c80 100644 (file)
@@ -343,19 +343,26 @@ void cv::gimpl::GCompiler::validateInputMeta()
         return false; // should never happen
     };
 
+    GAPI_LOG_DEBUG(nullptr, "Total count: " << m_metas.size());
     for (const auto meta_arg_idx : ade::util::indexed(ade::util::zip(m_metas, c_expr.m_ins)))
     {
         const auto &meta  = std::get<0>(ade::util::value(meta_arg_idx));
         const auto &proto = std::get<1>(ade::util::value(meta_arg_idx));
 
+        const auto index = ade::util::index(meta_arg_idx);
+        GAPI_LOG_DEBUG(nullptr, "Process index: " << index);
+
+        // check types validity
         if (!meta_matches(meta, proto))
         {
-            const auto index  = ade::util::index(meta_arg_idx);
             util::throw_error(std::logic_error
                         ("GComputation object type / metadata descriptor mismatch "
                          "(argument " + std::to_string(index) + ")"));
             // FIXME: report what we've got and what we've expected
         }
+
+        // check value consistency
+        gimpl::proto::validate_input_meta_arg(meta); //may throw
     }
     // All checks are ok
 }
index 85b05d5..2712c79 100644 (file)
@@ -29,7 +29,7 @@ class GAPI_EXPORTS GCompiler
     cv::gapi::GKernelPackage m_all_kernels;
     cv::gapi::GNetPackage    m_all_networks;
 
-    // Patters built from transformations
+    // Patterns built from transformations
     std::vector<std::unique_ptr<ade::Graph>> m_all_patterns;
 
 
index 7951a4c..25d02fb 100644 (file)
@@ -37,6 +37,31 @@ namespace
         {
         }
     };
+
+    struct GCompiledValidateMetaEmpty: public ::testing::Test
+    {
+        cv::GMat in;
+        cv::GScalar scale;
+        cv::GComputation m_ucc;
+
+        G_API_OP(GReturn42, <cv::GOpaque<int>(cv::GMat)>, "org.opencv.test.return_42")
+        {
+            static GOpaqueDesc outMeta(cv::GMatDesc /* in */) { return cv::empty_gopaque_desc(); }
+        };
+
+        GAPI_OCV_KERNEL(GOCVReturn42, GReturn42)
+        {
+            static void run(const cv::Mat &/* in */, int &out)
+            {
+                out = 42;
+            }
+        };
+
+        GCompiledValidateMetaEmpty() : m_ucc(cv::GIn(in),
+                                             cv::GOut(GReturn42::on(in)))
+        {
+        }
+    };
 } // anonymous namespace
 
 TEST_F(GCompiledValidateMetaTyped, ValidMeta)
@@ -170,4 +195,38 @@ TEST_F(GCompiledValidateMetaUntyped, InvalidMetaNumber)
     EXPECT_THROW(f(cv::gin(in1, sc), cv::gout(out1, out2)), std::logic_error);
 }
 
+TEST_F(GCompiledValidateMetaEmpty, InvalidMatMetaCompile)
+{
+    EXPECT_THROW(m_ucc.compile(cv::empty_gmat_desc(),
+                               cv::empty_scalar_desc()),
+                 std::logic_error);
+}
+
+TEST_F(GCompiledValidateMetaEmpty, InvalidMatMetaApply)
+{
+    cv::Mat emptyIn;
+    int out {};
+    const auto pkg = cv::gapi::kernels<GCompiledValidateMetaEmpty::GOCVReturn42>();
+
+    EXPECT_THROW(m_ucc.apply(cv::gin(emptyIn), cv::gout(out), cv::compile_args(pkg)),
+                 std::logic_error);
+}
+
+TEST_F(GCompiledValidateMetaEmpty, ValidInvalidMatMetasApply)
+{
+    int out {};
+    const auto pkg = cv::gapi::kernels<GCompiledValidateMetaEmpty::GOCVReturn42>();
+
+    cv::Mat nonEmptyMat = cv::Mat::eye(cv::Size(64,32), CV_8UC1);
+    m_ucc.apply(cv::gin(nonEmptyMat), cv::gout(out), cv::compile_args(pkg));
+    EXPECT_EQ(out, 42);
+
+    cv::Mat emptyIn;
+    EXPECT_THROW(m_ucc.apply(cv::gin(emptyIn), cv::gout(out), cv::compile_args(pkg)),
+                 std::logic_error);
+
+    out = 0;
+    m_ucc.apply(cv::gin(nonEmptyMat), cv::gout(out), cv::compile_args(pkg));
+    EXPECT_EQ(out, 42);
+}
 } // namespace opencv_test