diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index d9374a6b93..ec0fa6b0b4 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -51,6 +51,8 @@ public: WHERE, } op; + // If the eltwise implementation is modified, you need to force enable the 'Layer_Test_Eltwise_bcast' + // test in the 'test_layers.cpp' file to make sure it all passes NaryEltwiseLayerImpl(const LayerParams& params) { setParamsFrom(params); @@ -350,7 +352,6 @@ public: size_t dp1 = steps[1][ndims-1]/sizeof(T); size_t dp2 = steps[2][ndims-1]/sizeof(T); - CV_Assert(dp == 1); enum { BLOCK_SIZE = 1024 }; T blck[BLOCK_SIZE]; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 6afb3a8aa0..583d6dc050 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1937,6 +1937,117 @@ INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Eltwise_unequal, Combine( dnnBackendsAndTargets() )); + +struct Layer_Test_Eltwise_bcast : testing::TestWithParam>> +{ +public: + void test_bcast() + { + string op = get<0>(GetParam()); + int dim = get<1>(GetParam()); + tuple backend_target= get<2>(GetParam()); + int backend = get<0>(backend_target); + int target = get<1>(backend_target); + + vector> dim_shape_list; + get_all_arr(dim_shape_list, dim); + replace(dim_shape_list, 1, 3); + // same shape + for (int i = 0; i < dim_shape_list.size(); i++) + for (int j = 0; j < dim_shape_list.size(); j++) + run(dim_shape_list[i], dim_shape_list[j], op, backend, target); + + vector> sub_shape_list; + vector> tmp; + for(int i = 1; i < dim; i++){ + get_all_arr(tmp, i); + replace(tmp, 1, 3); + sub_shape_list.insert(sub_shape_list.end(), tmp.begin(), tmp.end()); + } + + // diff shape + for (const auto &shp1: dim_shape_list) + for (const auto &shp2: sub_shape_list) + run(shp1, shp2, op, backend, target); + + // diff shape + for (const auto &shp1: sub_shape_list) + for (const auto &shp2: dim_shape_list) + run(shp1, shp2, op, backend, target); + } + +private: + // give n to generate all n-D arrays with 0 or 1 + static void get_all_arr(vector> &arr, int n) + { + int total = 1 << n; + arr.assign(total, vector(n, -1)); + for (int i = 0; i < total; i++) + for (int j = 0; j < n; j++) + arr[i][j] = (i >> (n - j - 1)) & 1; + } + + // zero will replace all 0, one will replace all 1 + static void replace(vector> &arr, int zero, int one) + { + for (int i = 0; i < arr.size(); i++) + for (int j = 0; j < arr[0].size(); j++) + arr[i][j] = arr[i][j] ? one : zero; + } + + static void run(const vector &a_shape, const vector &b_shape, const String &op, const int backend, const int target) + { + Mat a = Mat::zeros((int) a_shape.size(), a_shape.data(), CV_32FC1); + Mat b = Mat::ones((int) b_shape.size(), b_shape.data(), CV_32FC1); + + Net net; + LayerParams lp; + lp.type = "NaryEltwise"; + lp.name = "testLayer"; + lp.set("operation", op); + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 1, id, 1); + + vector inpNames(2); + inpNames[0] = "a"; + inpNames[1] = "b"; + net.setInputsNames(inpNames); + net.setInput(a, inpNames[0]); + net.setInput(b, inpNames[1]); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + Mat re; + ASSERT_NO_THROW(re = net.forward()); // runtime error + auto ptr_re = (float *) re.data; + for (int i = 0; i < re.total(); i++) + if (op == "sum"){ + ASSERT_EQ(1, ptr_re[i]); // sum result should be 1 + } + } +}; + +TEST_P(Layer_Test_Eltwise_bcast, DISABLED_brute_force) +{ + test_bcast(); +} + +// This test is to verify whether the broadcast operations of unidirectional and bidirectional, +// as well as tensors with same and different shapes, can be forwarded correctly. +// This can ensure that the elementwise layer does not have any errors when forwarding. +// +// To test which cases the backend will fallback to the cpu, replace the fallback command like +// `return Ptr();` in `initCUDA()` with `throw std::runtime_error("fallback");` +// +// To test more operators, add more ops after "sum". +// Default only "sum" is tested, because for the most cases they have the same implementation. +INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Eltwise_bcast, Combine( + Values("sum"), + Values(1, 2, 3, 4, 5), + dnnBackendsAndTargets() +)); + typedef testing::TestWithParam > Layer_Test_Resize; TEST_P(Layer_Test_Resize, change_input) {