// // Copyright © 2017 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include "ParserFlatbuffersFixture.hpp" #include "ParserPrototxtFixture.hpp" #include "ParserHelper.hpp" #include #include #include TEST_SUITE("TensorflowLiteParser_DetectionPostProcess") { struct DetectionPostProcessFixture : ParserFlatbuffersFixture { explicit DetectionPostProcessFixture(const std::string& custom_options) { /* The following values were used for the custom_options: use_regular_nms = true max_classes_per_detection = 1 detections_per_class = 1 nms_score_threshold = 0.0 nms_iou_threshold = 0.5 max_detections = 3 max_detections = 3 num_classes = 2 h_scale = 5 w_scale = 5 x_scale = 10 y_scale = 10 */ m_JsonString = R"( { "version": 3, "operator_codes": [{ "builtin_code": "CUSTOM", "custom_code": "TFLite_Detection_PostProcess" }], "subgraphs": [{ "tensors": [{ "shape": [1, 6, 4], "type": "UINT8", "buffer": 0, "name": "box_encodings", "quantization": { "min": [0.0], "max": [255.0], "scale": [1.0], "zero_point": [ 1 ] } }, { "shape": [1, 6, 3], "type": "UINT8", "buffer": 1, "name": "scores", "quantization": { "min": [0.0], "max": [255.0], "scale": [0.01], "zero_point": [0] } }, { "shape": [6, 4], "type": "UINT8", "buffer": 2, "name": "anchors", "quantization": { "min": [0.0], "max": [255.0], "scale": [0.5], "zero_point": [0] } }, { "type": "FLOAT32", "buffer": 3, "name": "detection_boxes", "quantization": {} }, { "type": "FLOAT32", "buffer": 4, "name": "detection_classes", "quantization": {} }, { "type": "FLOAT32", "buffer": 5, "name": "detection_scores", "quantization": {} }, { "type": "FLOAT32", "buffer": 6, "name": "num_detections", "quantization": {} } ], "inputs": [0, 1, 2], "outputs": [3, 4, 5, 6], "operators": [{ "opcode_index": 0, "inputs": [0, 1, 2], "outputs": [3, 4, 5, 6], "builtin_options_type": 0, "custom_options": [)" + custom_options + R"(], "custom_options_format": "FLEXBUFFERS" }] }], "buffers": [{}, {}, { "data": [ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 21, 2, 2, 1, 21, 2, 2, 1, 201, 2, 2]}, {}, {}, {}, {}, ] } )"; } }; struct ParseDetectionPostProcessCustomOptions : DetectionPostProcessFixture { private: static armnn::DetectionPostProcessDescriptor GenerateDescriptor() { static armnn::DetectionPostProcessDescriptor descriptor; descriptor.m_UseRegularNms = true; descriptor.m_MaxDetections = 3u; descriptor.m_MaxClassesPerDetection = 1u; descriptor.m_DetectionsPerClass = 1u; descriptor.m_NumClasses = 2u; descriptor.m_NmsScoreThreshold = 0.0f; descriptor.m_NmsIouThreshold = 0.5f; descriptor.m_ScaleH = 5.0f; descriptor.m_ScaleW = 5.0f; descriptor.m_ScaleX = 10.0f; descriptor.m_ScaleY = 10.0f; return descriptor; } public: ParseDetectionPostProcessCustomOptions() : DetectionPostProcessFixture( GenerateDetectionPostProcessJsonString(GenerateDescriptor())) {} }; TEST_CASE_FIXTURE(ParseDetectionPostProcessCustomOptions, "ParseDetectionPostProcess") { Setup(); // Inputs using UnquantizedContainer = std::vector; UnquantizedContainer boxEncodings = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; UnquantizedContainer scores = { 0.0f, 0.9f, 0.8f, 0.0f, 0.75f, 0.72f, 0.0f, 0.6f, 0.5f, 0.0f, 0.93f, 0.95f, 0.0f, 0.5f, 0.4f, 0.0f, 0.3f, 0.2f }; // Outputs UnquantizedContainer detectionBoxes = { 0.0f, 10.0f, 1.0f, 11.0f, 0.0f, 10.0f, 1.0f, 11.0f, 0.0f, 0.0f, 0.0f, 0.0f }; UnquantizedContainer detectionClasses = { 1.0f, 0.0f, 0.0f }; UnquantizedContainer detectionScores = { 0.95f, 0.93f, 0.0f }; UnquantizedContainer numDetections = { 2.0f }; // Quantize inputs and outputs using QuantizedContainer = std::vector; QuantizedContainer quantBoxEncodings = armnnUtils::QuantizedVector(boxEncodings, 1.00f, 1); QuantizedContainer quantScores = armnnUtils::QuantizedVector(scores, 0.01f, 0); std::map input = { { "box_encodings", quantBoxEncodings }, { "scores", quantScores } }; std::map output = { { "detection_boxes", detectionBoxes}, { "detection_classes", detectionClasses}, { "detection_scores", detectionScores}, { "num_detections", numDetections} }; RunTest(0, input, output); } TEST_CASE_FIXTURE(ParseDetectionPostProcessCustomOptions, "DetectionPostProcessGraphStructureTest") { /* Inputs: box_encodings scores \ / DetectionPostProcess / / \ \ / / \ \ Outputs: detection detection detection num_detections boxes classes scores */ ReadStringToBinary(); armnn::INetworkPtr network = m_Parser->CreateNetworkFromBinary(m_GraphBinary); auto optimized = Optimize(*network, { armnn::Compute::CpuRef }, m_Runtime->GetDeviceSpec()); armnn::Graph& graph = GetGraphForTesting(optimized.get()); // Check the number of layers in the graph CHECK((graph.GetNumInputs() == 2)); CHECK((graph.GetNumOutputs() == 4)); CHECK((graph.GetNumLayers() == 7)); // Input layers armnn::Layer* boxEncodingLayer = GetFirstLayerWithName(graph, "box_encodings"); CHECK((boxEncodingLayer->GetType() == armnn::LayerType::Input)); CHECK(CheckNumberOfInputSlot(boxEncodingLayer, 0)); CHECK(CheckNumberOfOutputSlot(boxEncodingLayer, 1)); armnn::Layer* scoresLayer = GetFirstLayerWithName(graph, "scores"); CHECK((scoresLayer->GetType() == armnn::LayerType::Input)); CHECK(CheckNumberOfInputSlot(scoresLayer, 0)); CHECK(CheckNumberOfOutputSlot(scoresLayer, 1)); // DetectionPostProcess layer armnn::Layer* detectionPostProcessLayer = GetFirstLayerWithName(graph, "DetectionPostProcess:0:0"); CHECK((detectionPostProcessLayer->GetType() == armnn::LayerType::DetectionPostProcess)); CHECK(CheckNumberOfInputSlot(detectionPostProcessLayer, 2)); CHECK(CheckNumberOfOutputSlot(detectionPostProcessLayer, 4)); // Output layers armnn::Layer* detectionBoxesLayer = GetFirstLayerWithName(graph, "detection_boxes"); CHECK((detectionBoxesLayer->GetType() == armnn::LayerType::Output)); CHECK(CheckNumberOfInputSlot(detectionBoxesLayer, 1)); CHECK(CheckNumberOfOutputSlot(detectionBoxesLayer, 0)); armnn::Layer* detectionClassesLayer = GetFirstLayerWithName(graph, "detection_classes"); CHECK((detectionClassesLayer->GetType() == armnn::LayerType::Output)); CHECK(CheckNumberOfInputSlot(detectionClassesLayer, 1)); CHECK(CheckNumberOfOutputSlot(detectionClassesLayer, 0)); armnn::Layer* detectionScoresLayer = GetFirstLayerWithName(graph, "detection_scores"); CHECK((detectionScoresLayer->GetType() == armnn::LayerType::Output)); CHECK(CheckNumberOfInputSlot(detectionScoresLayer, 1)); CHECK(CheckNumberOfOutputSlot(detectionScoresLayer, 0)); armnn::Layer* numDetectionsLayer = GetFirstLayerWithName(graph, "num_detections"); CHECK((numDetectionsLayer->GetType() == armnn::LayerType::Output)); CHECK(CheckNumberOfInputSlot(numDetectionsLayer, 1)); CHECK(CheckNumberOfOutputSlot(numDetectionsLayer, 0)); // Check the connections armnn::TensorInfo boxEncodingTensor(armnn::TensorShape({ 1, 6, 4 }), armnn::DataType::QAsymmU8, 1, 1); armnn::TensorInfo scoresTensor(armnn::TensorShape({ 1, 6, 3 }), armnn::DataType::QAsymmU8, 0.00999999978f, 0); armnn::TensorInfo detectionBoxesTensor(armnn::TensorShape({ 1, 3, 4 }), armnn::DataType::Float32, 0, 0); armnn::TensorInfo detectionClassesTensor(armnn::TensorShape({ 1, 3 }), armnn::DataType::Float32, 0, 0); armnn::TensorInfo detectionScoresTensor(armnn::TensorShape({ 1, 3 }), armnn::DataType::Float32, 0, 0); armnn::TensorInfo numDetectionsTensor(armnn::TensorShape({ 1} ), armnn::DataType::Float32, 0, 0); CHECK(IsConnected(boxEncodingLayer, detectionPostProcessLayer, 0, 0, boxEncodingTensor)); CHECK(IsConnected(scoresLayer, detectionPostProcessLayer, 0, 1, scoresTensor)); CHECK(IsConnected(detectionPostProcessLayer, detectionBoxesLayer, 0, 0, detectionBoxesTensor)); CHECK(IsConnected(detectionPostProcessLayer, detectionClassesLayer, 1, 0, detectionClassesTensor)); CHECK(IsConnected(detectionPostProcessLayer, detectionScoresLayer, 2, 0, detectionScoresTensor)); CHECK(IsConnected(detectionPostProcessLayer, numDetectionsLayer, 3, 0, numDetectionsTensor)); } }