From 44b31dd82c5c95c75778d48bb13e961e0587fd87 Mon Sep 17 00:00:00 2001 From: ramukhsuya <163312977+ramukhsuya@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:25:04 +0530 Subject: [PATCH] Merge pull request #28171 from ramukhsuya:tflite-maximum-support dnn(tflite): add support for MAXIMUM layer #28171 Fixes #26433 This PR adds support for the `MAXIMUM` layer in the TFLite importer. It maps the TFLite `MAXIMUM` opcode to the existing OpenCV Element-wise `Max` operation. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/dnn/src/tflite/tflite_importer.cpp | 8 +++++-- modules/dnn/test/test_tflite_importer.cpp | 28 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/tflite/tflite_importer.cpp b/modules/dnn/src/tflite/tflite_importer.cpp index 7bc4f55988..edcc3804e9 100644 --- a/modules/dnn/src/tflite/tflite_importer.cpp +++ b/modules/dnn/src/tflite/tflite_importer.cpp @@ -287,7 +287,8 @@ TFLiteImporter::DispatchMap TFLiteImporter::buildDispatchMap() dispatch["DEPTHWISE_CONV_2D"] = &TFLiteImporter::parseDWConvolution; dispatch["ADD"] = dispatch["MUL"] = dispatch["SUB"] = dispatch["SQRT"] = dispatch["DIV"] = dispatch["NEG"] = - dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = &TFLiteImporter::parseEltwise; + dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = + dispatch["MAXIMUM"] = &TFLiteImporter::parseEltwise; dispatch["RELU"] = dispatch["PRELU"] = dispatch["HARD_SWISH"] = dispatch["LOGISTIC"] = dispatch["LEAKY_RELU"] = &TFLiteImporter::parseActivation; dispatch["MAX_POOL_2D"] = dispatch["AVERAGE_POOL_2D"] = &TFLiteImporter::parsePooling; @@ -577,7 +578,10 @@ void TFLiteImporter::parseEltwise(const Operator& op, const std::string& opcode, } else if (opcode == "SQRT" && !isOpInt8) { layerParams.type = "Sqrt"; - } else { + } + else if (opcode == "MAXIMUM" && !isOpInt8) { + layerParams.set("operation", "max"); + }else { CV_Error(Error::StsNotImplemented, cv::format("DNN/TFLite: Unknown opcode for %s Eltwise layer '%s'", isOpInt8 ? "INT8" : "FP32", opcode.c_str())); } diff --git a/modules/dnn/test/test_tflite_importer.cpp b/modules/dnn/test/test_tflite_importer.cpp index 186b0ff154..3cee776611 100644 --- a/modules/dnn/test/test_tflite_importer.cpp +++ b/modules/dnn/test/test_tflite_importer.cpp @@ -283,6 +283,34 @@ TEST_P(Test_TFLite, face_blendshapes) testModel("face_blendshapes", inp); } +TEST_P(Test_TFLite, maximum) +{ + Net net = readNetFromTFLite(findDataFile("dnn/tflite/maximum.tflite")); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + Mat input_x = blobFromNPY(findDataFile("dnn/tflite/maximum_input_x.npy")); + Mat input_y = blobFromNPY(findDataFile("dnn/tflite/maximum_input_y.npy")); + + net.setInput(input_x, "x"); + net.setInput(input_y, "y"); + + Mat out = net.forward(); + Mat ref = blobFromNPY(findDataFile("dnn/tflite/maximum_output.npy")); + + double l1 = 1e-5; + double lInf = 1e-4; + + if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_OPENCL_FP16) + { + l1 = 1e-3; + lInf = 1e-3; + } + + normAssert(ref, out, "", l1, lInf); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_TFLite, dnnBackendsAndTargets()); }} // namespace