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