From 7234ed8c3d07c76963eb3bce9530994421ad7e67 Mon Sep 17 00:00:00 2001 From: Isabella Gottardi Date: Tue, 27 Nov 2018 08:51:10 +0000 Subject: COMPMID-1808: Add Detection Output Layer to the GraphAPI COMPMID-1710: Integrate Detection ouput in MobilenetSSD graph example Change-Id: I384d1eb492ef14ece58f2023ad7bbc16f834450b Reviewed-on: https://review.mlplatform.org/356 Tested-by: Arm Jenkins Reviewed-by: Pablo Marquez Reviewed-by: Georgios Pinitas --- utils/GraphUtils.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils/GraphUtils.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) (limited to 'utils') diff --git a/utils/GraphUtils.cpp b/utils/GraphUtils.cpp index 2f1df7aef2..ab2c753eac 100644 --- a/utils/GraphUtils.cpp +++ b/utils/GraphUtils.cpp @@ -420,6 +420,77 @@ void ValidationOutputAccessor::report_top_n(size_t top_n, size_t total_samples, _output_stream << "Accuracy : " << accuracy << std::endl; } +DetectionOutputAccessor::DetectionOutputAccessor(const std::string &labels_path, std::vector &imgs_tensor_shapes, std::ostream &output_stream) + : _labels(), _tensor_shapes(std::move(imgs_tensor_shapes)), _output_stream(output_stream) +{ + _labels.clear(); + + std::ifstream ifs; + + try + { + ifs.exceptions(std::ifstream::badbit); + ifs.open(labels_path, std::ios::in | std::ios::binary); + + for(std::string line; !std::getline(ifs, line).fail();) + { + _labels.emplace_back(line); + } + } + catch(const std::ifstream::failure &e) + { + ARM_COMPUTE_ERROR("Accessing %s: %s", labels_path.c_str(), e.what()); + } +} + +template +void DetectionOutputAccessor::access_predictions_tensor(ITensor &tensor) +{ + const size_t num_detection = tensor.info()->valid_region().shape.y(); + const auto output_prt = reinterpret_cast(tensor.buffer() + tensor.info()->offset_first_element_in_bytes()); + + if(num_detection > 0) + { + _output_stream << "---------------------- Detections ----------------------" << std::endl + << std::endl; + + _output_stream << std::left << std::setprecision(4) << std::setw(8) << "Image | " << std::setw(8) << "Label | " << std::setw(12) << "Confidence | " + << "[ xmin, ymin, xmax, ymax ]" << std::endl; + + for(size_t i = 0; i < num_detection; ++i) + { + auto im = static_cast(output_prt[i * 7]); + _output_stream << std::setw(8) << im << std::setw(8) + << _labels[output_prt[i * 7 + 1]] << std::setw(12) << output_prt[i * 7 + 2] + << " [" << (output_prt[i * 7 + 3] * _tensor_shapes[im].x()) + << ", " << (output_prt[i * 7 + 4] * _tensor_shapes[im].y()) + << ", " << (output_prt[i * 7 + 5] * _tensor_shapes[im].x()) + << ", " << (output_prt[i * 7 + 6] * _tensor_shapes[im].y()) + << "]" << std::endl; + } + } + else + { + _output_stream << "No detection found." << std::endl; + } +} + +bool DetectionOutputAccessor::access_tensor(ITensor &tensor) +{ + ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32); + + switch(tensor.info()->data_type()) + { + case DataType::F32: + access_predictions_tensor(tensor); + break; + default: + ARM_COMPUTE_ERROR("NOT SUPPORTED!"); + } + + return false; +} + TopNPredictionsAccessor::TopNPredictionsAccessor(const std::string &labels_path, size_t top_n, std::ostream &output_stream) : _labels(), _output_stream(output_stream), _top_n(top_n) { diff --git a/utils/GraphUtils.h b/utils/GraphUtils.h index d7f24afdd8..131378e5bd 100644 --- a/utils/GraphUtils.h +++ b/utils/GraphUtils.h @@ -283,6 +283,36 @@ private: size_t _positive_samples_top5; }; +/** Detection output accessor class */ +class DetectionOutputAccessor final : public graph::ITensorAccessor +{ +public: + /** Constructor + * + * @param[in] labels_path Path to labels text file. + * @param[in] imgs_tensor_shapes Network input images tensor shapes. + * @param[out] output_stream (Optional) Output stream + */ + DetectionOutputAccessor(const std::string &labels_path, std::vector &imgs_tensor_shapes, std::ostream &output_stream = std::cout); + /** Allow instances of this class to be move constructed */ + DetectionOutputAccessor(DetectionOutputAccessor &&) = default; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + DetectionOutputAccessor(const DetectionOutputAccessor &) = delete; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + DetectionOutputAccessor &operator=(const DetectionOutputAccessor &) = delete; + + // Inherited methods overriden: + bool access_tensor(ITensor &tensor) override; + +private: + template + void access_predictions_tensor(ITensor &tensor); + + std::vector _labels; + std::vector _tensor_shapes; + std::ostream &_output_stream; +}; + /** Result accessor class */ class TopNPredictionsAccessor final : public graph::ITensorAccessor { @@ -472,6 +502,39 @@ inline std::unique_ptr get_output_accessor(const arm_com return arm_compute::support::cpp14::make_unique(graph_parameters.labels, top_n, output_stream); } } +/** Generates appropriate output accessor according to the specified graph parameters + * + * @note If the output accessor is requested to validate the graph then ValidationOutputAccessor is generated + * else if output_accessor_file is empty will generate a DummyAccessor else will generate a TopNPredictionsAccessor + * + * @param[in] graph_parameters Graph parameters + * @param[in] tensor_shapes Network input images tensor shapes. + * @param[in] is_validation (Optional) Validation flag (default = false) + * @param[out] output_stream (Optional) Output stream (default = std::cout) + * + * @return An appropriate tensor accessor + */ +inline std::unique_ptr get_detection_output_accessor(const arm_compute::utils::CommonGraphParams &graph_parameters, + std::vector tensor_shapes, + bool is_validation = false, + std::ostream &output_stream = std::cout) +{ + if(!graph_parameters.validation_file.empty()) + { + return arm_compute::support::cpp14::make_unique(graph_parameters.validation_file, + output_stream, + graph_parameters.validation_range_start, + graph_parameters.validation_range_end); + } + else if(graph_parameters.labels.empty()) + { + return arm_compute::support::cpp14::make_unique(0); + } + else + { + return arm_compute::support::cpp14::make_unique(graph_parameters.labels, tensor_shapes, output_stream); + } +} /** Generates appropriate npy output accessor according to the specified npy_path * * @note If npy_path is empty will generate a DummyAccessor else will generate a NpyAccessor -- cgit v1.2.1