ArmNN  NotReleased
MobileNetSsdInferenceTest.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "InferenceTest.hpp"
9 
10 #include <boost/assert.hpp>
11 #include <boost/numeric/conversion/cast.hpp>
12 #include <boost/test/tools/floating_point_comparison.hpp>
13 
14 #include <vector>
15 
16 namespace
17 {
18 
19 template<typename Model>
20 class MobileNetSsdTestCase : public InferenceModelTestCase<Model>
21 {
22 public:
23  MobileNetSsdTestCase(Model& model,
24  unsigned int testCaseId,
25  const MobileNetSsdTestCaseData& testCaseData)
27  testCaseId,
28  { std::move(testCaseData.m_InputData) },
29  { k_OutputSize1, k_OutputSize2, k_OutputSize3, k_OutputSize4 })
30  , m_FloatComparer(boost::math::fpc::percent_tolerance(1.0f))
31  , m_DetectedObjects(testCaseData.m_ExpectedDetectedObject)
32  {}
33 
35  {
36  boost::ignore_unused(options);
37 
38  const std::vector<float>& output1 = boost::get<std::vector<float>>(this->GetOutputs()[0]); // bounding boxes
39  BOOST_ASSERT(output1.size() == k_OutputSize1);
40 
41  const std::vector<float>& output2 = boost::get<std::vector<float>>(this->GetOutputs()[1]); // classes
42  BOOST_ASSERT(output2.size() == k_OutputSize2);
43 
44  const std::vector<float>& output3 = boost::get<std::vector<float>>(this->GetOutputs()[2]); // scores
45  BOOST_ASSERT(output3.size() == k_OutputSize3);
46 
47  const std::vector<float>& output4 = boost::get<std::vector<float>>(this->GetOutputs()[3]); // valid detections
48  BOOST_ASSERT(output4.size() == k_OutputSize4);
49 
50  const size_t numDetections = boost::numeric_cast<size_t>(output4[0]);
51 
52  // Check if number of valid detections matches expectations
53  const size_t expectedNumDetections = m_DetectedObjects.size();
54  if (numDetections != expectedNumDetections)
55  {
56  ARMNN_LOG(error) << "Number of detections is incorrect: Expected (" <<
57  expectedNumDetections << ")" << " but got (" << numDetections << ")";
59  }
60 
61  // Extract detected objects from output data
62  std::vector<DetectedObject> detectedObjects;
63  const float* outputData = output1.data();
64  for (unsigned int i = 0u; i < numDetections; i++)
65  {
66  // NOTE: Order of coordinates in output data is yMin, xMin, yMax, xMax
67  float yMin = *outputData++;
68  float xMin = *outputData++;
69  float yMax = *outputData++;
70  float xMax = *outputData++;
71 
72  DetectedObject detectedObject(
73  output2.at(i),
74  BoundingBox(xMin, yMin, xMax, yMax),
75  output3.at(i));
76 
77  detectedObjects.push_back(detectedObject);
78  }
79 
80  std::sort(detectedObjects.begin(), detectedObjects.end());
81  std::sort(m_DetectedObjects.begin(), m_DetectedObjects.end());
82 
83  // Compare detected objects with expected results
84  std::vector<DetectedObject>::const_iterator it = detectedObjects.begin();
85  for (unsigned int i = 0; i < numDetections; i++)
86  {
87  if (it == detectedObjects.end())
88  {
89  ARMNN_LOG(error) << "No more detected objects found! Index out of bounds: " << i;
90  return TestCaseResult::Abort;
91  }
92 
93  const DetectedObject& detectedObject = *it;
94  const DetectedObject& expectedObject = m_DetectedObjects[i];
95 
96  if (detectedObject.m_Class != expectedObject.m_Class)
97  {
98  ARMNN_LOG(error) << "Prediction for test case " << this->GetTestCaseId() <<
99  " is incorrect: Expected (" << expectedObject.m_Class << ")" <<
100  " but predicted (" << detectedObject.m_Class << ")";
101  return TestCaseResult::Failed;
102  }
103 
104  if(!m_FloatComparer(detectedObject.m_Confidence, expectedObject.m_Confidence))
105  {
106  ARMNN_LOG(error) << "Confidence of prediction for test case " << this->GetTestCaseId() <<
107  " is incorrect: Expected (" << expectedObject.m_Confidence << ") +- 1.0 pc" <<
108  " but predicted (" << detectedObject.m_Confidence << ")";
109  return TestCaseResult::Failed;
110  }
111 
112  if (!m_FloatComparer(detectedObject.m_BoundingBox.m_XMin, expectedObject.m_BoundingBox.m_XMin) ||
113  !m_FloatComparer(detectedObject.m_BoundingBox.m_YMin, expectedObject.m_BoundingBox.m_YMin) ||
114  !m_FloatComparer(detectedObject.m_BoundingBox.m_XMax, expectedObject.m_BoundingBox.m_XMax) ||
115  !m_FloatComparer(detectedObject.m_BoundingBox.m_YMax, expectedObject.m_BoundingBox.m_YMax))
116  {
117  ARMNN_LOG(error) << "Detected bounding box for test case " << this->GetTestCaseId() <<
118  " is incorrect";
119  return TestCaseResult::Failed;
120  }
121 
122  ++it;
123  }
124 
125  return TestCaseResult::Ok;
126  }
127 
128 private:
129  static constexpr unsigned int k_Shape = 10u;
130 
131  static constexpr unsigned int k_OutputSize1 = k_Shape * 4u;
132  static constexpr unsigned int k_OutputSize2 = k_Shape;
133  static constexpr unsigned int k_OutputSize3 = k_Shape;
134  static constexpr unsigned int k_OutputSize4 = 1u;
135 
136  boost::math::fpc::close_at_tolerance<float> m_FloatComparer;
137  std::vector<DetectedObject> m_DetectedObjects;
138 };
139 
140 template <typename Model>
141 class MobileNetSsdTestCaseProvider : public IInferenceTestCaseProvider
142 {
143 public:
144  template <typename TConstructModelCallable>
145  explicit MobileNetSsdTestCaseProvider(TConstructModelCallable constructModel)
146  : m_ConstructModel(constructModel)
147  {}
148 
149  virtual void AddCommandLineOptions(boost::program_options::options_description& options) override
150  {
151  namespace po = boost::program_options;
152 
153  options.add_options()
154  ("data-dir,d", po::value<std::string>(&m_DataDir)->required(),
155  "Path to directory containing test data");
156 
157  Model::AddCommandLineOptions(options, m_ModelCommandLineOptions);
158  }
159 
160  virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override
161  {
162  if (!ValidateDirectory(m_DataDir))
163  {
164  return false;
165  }
166 
167  m_Model = m_ConstructModel(commonOptions, m_ModelCommandLineOptions);
168  if (!m_Model)
169  {
170  return false;
171  }
172  std::pair<float, int32_t> qParams = m_Model->GetInputQuantizationParams();
173  m_Database = std::make_unique<MobileNetSsdDatabase>(m_DataDir.c_str(), qParams.first, qParams.second);
174  if (!m_Database)
175  {
176  return false;
177  }
178 
179  return true;
180  }
181 
182  std::unique_ptr<IInferenceTestCase> GetTestCase(unsigned int testCaseId) override
183  {
184  std::unique_ptr<MobileNetSsdTestCaseData> testCaseData = m_Database->GetTestCaseData(testCaseId);
185  if (!testCaseData)
186  {
187  return nullptr;
188  }
189 
190  return std::make_unique<MobileNetSsdTestCase<Model>>(*m_Model, testCaseId, *testCaseData);
191  }
192 
193 private:
194  typename Model::CommandLineOptions m_ModelCommandLineOptions;
195  std::function<std::unique_ptr<Model>(const InferenceTestOptions &,
196  typename Model::CommandLineOptions)> m_ConstructModel;
197  std::unique_ptr<Model> m_Model;
198 
199  std::string m_DataDir;
200  std::unique_ptr<MobileNetSsdDatabase> m_Database;
201 };
202 
203 } // anonymous namespace
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
The test completed without any errors.
const std::vector< TContainer > & GetOutputs() const
virtual TestCaseResult ProcessResult(const InferenceTestOptions &options)=0
The test failed with a fatal error. The remaining tests will not be run.
armnn::Runtime::CreationOptions::ExternalProfilingOptions options
bool ValidateDirectory(std::string &dir)