diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index ca134abfb9..ec63e48055 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -191,19 +191,19 @@ public: std::vector &internals) const CV_OVERRIDE { CV_Assert(inputs.size() >= 2); - CV_Assert(inputs[0].size() >= 2); + CV_Assert(inputs[0].size() >= 1); CV_Assert(coeffs.size() == 0 || coeffs.size() == inputs.size()); CV_Assert(op == SUM || coeffs.size() == 0); int dims = inputs[0].size(); // Number of channels in output shape is determined by the first input tensor. bool variableChannels = false; - int numChannels = inputs[0][1]; + int numChannels = (dims == 1) ? inputs[0][0] : inputs[0][1]; for (size_t i = 1; i < inputs.size(); i++) { CV_Assert(inputs[0][0] == inputs[i][0]); // batch sizes are equal - int input_channels = inputs[i][1]; + int input_channels = (dims == 1) ? inputs[i][0] : inputs[i][1]; if (numChannels != input_channels) variableChannels = true; @@ -235,13 +235,13 @@ public: outputs.assign(1, inputs[0]); outputs[0][1] = numChannels; - if (dims > 2) + if (dims >= 1) { size_t vecIdx = 0; bool isVecFound = false; for (size_t i = 0; i < inputs.size(); i++) { - bool allOnes = isAllOnes(inputs[i], 2, dims); + bool allOnes = isAllOnes(inputs[i], (dims != 1) ? 2 : 1, dims); if (!allOnes && !isVecFound) { vecIdx = i; @@ -277,7 +277,7 @@ public: for (size_t i = 0; i < inputs.size(); i++) { MatShape inpShape = shape(inputs[i].size); - if (isAllOnes(inpShape, 2, inputs[i].dims)) + if (isAllOnes(inpShape, 0, inputs[i].dims)) { hasVecInput = true; return; @@ -310,11 +310,14 @@ public: int nstripes) { const EltwiseOp op = self.op; - CV_Check(dst.dims, 1 < dst.dims && dst.dims <= 5, ""); CV_CheckTypeEQ(dst.type(), CV_32FC1, ""); CV_Assert(dst.isContinuous()); + CV_Check(dst.dims, 1 <= dst.dims && dst.dims <= 5, ""); + CV_CheckTypeEQ(dst.type(), CV_32FC1, ""); + CV_Assert(dst.isContinuous()); CV_Assert(self.coeffs.empty() || self.coeffs.size() == (size_t)nsrcs); CV_CheckGE(nsrcs, 2, ""); - CV_Assert(self.outputChannels == dst.size[1]); + if (dst.dims != 1) + CV_Assert(self.outputChannels == dst.size[1]); EltwiseInvoker p(self); p.srcs.resize(nsrcs); diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 8248049e2e..73e18de231 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -618,6 +618,204 @@ TEST(Layer_LSTM_Test_Accuracy_with_, HiddenParams) normAssert(h_t_reference, outputs[0]); } +typedef testing::TestWithParam> Layer_Gather_1d_Test; +TEST_P(Layer_Gather_1d_Test, Accuracy) { + + int batch_size = get<0>(GetParam()); + int axis = get<1>(GetParam()); + + LayerParams lp; + lp.type = "Gather"; + lp.name = "gatherLayer"; + lp.set("axis", axis); + lp.set("real_ndims", 1); + + Ptr layer = GatherLayer::create(lp); + + std::vector input_shape = {batch_size, 1}; + std::vector indices_shape = {1, 1}; + std::vector output_shape = {batch_size, 1}; + + if (batch_size == 0){ + input_shape.erase(input_shape.begin()); + indices_shape.erase(indices_shape.begin()); + output_shape.erase(output_shape.begin()); + } else if (axis == 0) { + output_shape[0] = 1; + } + + cv::Mat input = cv::Mat(input_shape, CV_32F, 1.0); + cv::randu(input, 0.0, 1.0); + cv::Mat indices = cv::Mat(indices_shape, CV_32F, 0.0); + cv::Mat output_ref = cv::Mat(output_shape, CV_32F, input(cv::Range::all(), cv::Range(0, 1)).data); + + std::vector inputs{input, indices}; + std::vector outputs; + + runLayer(layer, inputs, outputs); + ASSERT_EQ(shape(output_ref), shape(outputs[0])); + normAssert(output_ref, outputs[0]); +} +INSTANTIATE_TEST_CASE_P(/*nothing*/, Layer_Gather_1d_Test, Combine( +/*input blob shape*/ Values(0, 1, 2, 3), +/*operation*/ Values(0, 1) +)); + +typedef testing::TestWithParam> Layer_Arg_1d_Test; +TEST_P(Layer_Arg_1d_Test, Accuracy) { + + int batch_size = get<0>(GetParam()); + int axis = get<1>(GetParam()); + std::string operation = get<2>(GetParam()); + + LayerParams lp; + lp.type = "Arg"; + lp.name = "arg" + operation + "_Layer"; + lp.set("op", operation); + lp.set("axis", axis); + lp.set("keepdims", 1); + lp.set("select_last_index", 0); + + Ptr layer = ArgLayer::create(lp); + + std::vector input_shape = {batch_size, 1}; + std::vector output_shape = {1, 1}; + + if (batch_size == 0){ + input_shape.erase(input_shape.begin()); + output_shape.erase(output_shape.begin()); + } + + if (axis != 0 && batch_size != 0){ + output_shape[0] = batch_size; + } + + cv::Mat input = cv::Mat(input_shape, CV_32F, 1); + cv::Mat output_ref = cv::Mat(output_shape, CV_32F, 0); + + for (int i = 0; i < batch_size; ++i) + input.at(i, 0) = static_cast(i + 1); + + std::vector inputs{input}; + std::vector outputs; + + runLayer(layer, inputs, outputs); + ASSERT_EQ(shape(output_ref), shape(outputs[0])); + normAssert(output_ref, outputs[0]); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Layer_Arg_1d_Test, Combine( +/*input blob shape*/ Values(0, 1, 2, 3), +/*operation*/ Values(0, 1), +/*operation*/ Values( "max", "min") +)); + +typedef testing::TestWithParam> Layer_NaryElemwise_1d_Test; +TEST_P(Layer_NaryElemwise_1d_Test, Accuracy) { + + int batch_size = get<0>(GetParam()); + std::string operation = get<1>(GetParam()); + + LayerParams lp; + lp.type = "Eltwise"; + lp.name = operation + "_Layer"; + lp.set("operation", operation); + Ptr layer = NaryEltwiseLayer::create(lp); + + std::vector input_shape = {batch_size, 1}; + if (batch_size == 0) + input_shape.erase(input_shape.begin()); + + cv::Mat input1 = cv::Mat(input_shape, CV_32F, 0.0); + cv::Mat input2 = cv::Mat(input_shape, CV_32F, 0.0); + cv::randu(input1, 0.0, 1.0); + cv::randu(input2, 0.0, 1.0); + + cv::Mat output_ref; + if (operation == "sum") { + output_ref = input1 + input2; + } else if (operation == "mul") { + output_ref = input1.mul(input2); + } else if (operation == "div") { + output_ref = input1 / input2; + } else if (operation == "sub") { + output_ref = input1 - input2; + } else { + output_ref = cv::Mat(); + } + std::vector inputs{input1, input2}; + std::vector outputs; + + runLayer(layer, inputs, outputs); + if (!output_ref.empty()) { + ASSERT_EQ(shape(output_ref), shape(outputs[0])); + normAssert(output_ref, outputs[0]); + } else { + CV_Error(Error::StsAssert, "Provided operation: " + operation + " is not supported. Please check the test instantiation."); + } +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Layer_NaryElemwise_1d_Test, Combine( +/*input blob shape*/ Values(0, 1), +/*operation*/ Values("div", "mul", "sum", "sub") +)); + +typedef testing::TestWithParam> Layer_Elemwise_1d_Test; +TEST_P(Layer_Elemwise_1d_Test, Accuracy) { + + int batch_size = get<0>(GetParam()); + std::string operation = get<1>(GetParam()); + + LayerParams lp; + lp.type = "Eltwise"; + lp.name = operation + "_Layer"; + lp.set("operation", operation); + Ptr layer = EltwiseLayer::create(lp); + + std::vector input_shape = {batch_size, 1}; + if (batch_size == 0) + input_shape.erase(input_shape.begin()); + + cv::Mat input1 = cv::Mat(input_shape, CV_32F, 1.0); + cv::Mat input2 = cv::Mat(input_shape, CV_32F, 1.0); + cv::randu(input1, 0.0, 1.0); + cv::randu(input2, 0.0, 1.0); + + // Dynamically select the operation + cv::Mat output_ref; + if (operation == "sum") { + output_ref = input1 + input2; + } else if (operation == "max") { + output_ref = cv::max(input1, input2); + } else if (operation == "min") { + output_ref = cv::min(input1, input2); + } else if (operation == "prod") { + output_ref = input1.mul(input2); + } else if (operation == "div") { + output_ref = input1 / input2; + } else { + output_ref = cv::Mat(); + } + + + std::vector inputs{input1, input2}; + std::vector outputs; + + runLayer(layer, inputs, outputs); + + if (!output_ref.empty()) { + ASSERT_EQ(shape(output_ref), shape(outputs[0])); + normAssert(output_ref, outputs[0]); + } else { + CV_Error(Error::StsAssert, "Provided operation: " + operation + " is not supported. Please check the test instantiation."); + } +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Layer_Elemwise_1d_Test, Combine( +/*input blob shape*/ Values(0, 1, 2, 3), +/*operation*/ Values("div", "prod", "max", "min", "sum") +)); + TEST(Layer_GRU_Test_Accuracy_with_, Pytorch) { Mat Wx = blobFromNPY(_tf("gru.W.npy"));