|
| 1 | +#include "tasks/anomaly.h" |
| 2 | + |
| 3 | +#include "adapters/openvino_adapter.h" |
| 4 | +#include "utils/preprocessing.h" |
| 5 | +#include "utils/tensor.h" |
| 6 | + |
| 7 | +void Anomaly::serialize(std::shared_ptr<ov::Model>& ov_model) { |
| 8 | + auto input = ov_model->inputs().front(); |
| 9 | + |
| 10 | + auto layout = ov::layout::get_layout(input); |
| 11 | + if (layout.empty()) { |
| 12 | + layout = utils::getLayoutFromShape(input.get_partial_shape()); |
| 13 | + } |
| 14 | + |
| 15 | + const ov::Shape& shape = input.get_partial_shape().get_max_shape(); |
| 16 | + |
| 17 | + auto interpolation_mode = cv::INTER_LINEAR; |
| 18 | + utils::RESIZE_MODE resize_mode = utils::RESIZE_FILL; |
| 19 | + uint8_t pad_value = 0; |
| 20 | + bool reverse_input_channels = false; |
| 21 | + |
| 22 | + std::vector<float> scale_values; |
| 23 | + std::vector<float> mean_values; |
| 24 | + if (ov_model->has_rt_info("model_info")) { |
| 25 | + auto config = ov_model->get_rt_info<ov::AnyMap>("model_info"); |
| 26 | + reverse_input_channels = |
| 27 | + utils::get_from_any_maps("reverse_input_channels", config, ov::AnyMap{}, reverse_input_channels); |
| 28 | + scale_values = utils::get_from_any_maps("scale_values", config, ov::AnyMap{}, scale_values); |
| 29 | + mean_values = utils::get_from_any_maps("mean_values", config, ov::AnyMap{}, mean_values); |
| 30 | + } |
| 31 | + |
| 32 | + auto input_shape = ov::Shape{shape[ov::layout::width_idx(layout)], shape[ov::layout::height_idx(layout)]}; |
| 33 | + |
| 34 | + ov_model = utils::embedProcessing(ov_model, |
| 35 | + input.get_any_name(), |
| 36 | + layout, |
| 37 | + resize_mode, |
| 38 | + interpolation_mode, |
| 39 | + input_shape, |
| 40 | + pad_value, |
| 41 | + reverse_input_channels, |
| 42 | + mean_values, |
| 43 | + scale_values); |
| 44 | + |
| 45 | + ov_model->set_rt_info(input_shape[0], "model_info", "orig_width"); |
| 46 | + ov_model->set_rt_info(input_shape[1], "model_info", "orig_height"); |
| 47 | +} |
| 48 | + |
| 49 | +Anomaly Anomaly::load(const std::string& model_path) { |
| 50 | + auto core = ov::Core(); |
| 51 | + std::shared_ptr<ov::Model> model = core.read_model(model_path); |
| 52 | + |
| 53 | + if (model->has_rt_info("model_info", "model_type")) { |
| 54 | + std::cout << "has model type in info: " << model->get_rt_info<std::string>("model_info", "model_type") |
| 55 | + << std::endl; |
| 56 | + } else { |
| 57 | + throw std::runtime_error("Incorrect or unsupported model_type"); |
| 58 | + } |
| 59 | + |
| 60 | + if (utils::model_has_embedded_processing(model)) { |
| 61 | + std::cout << "model already was serialized" << std::endl; |
| 62 | + } else { |
| 63 | + serialize(model); |
| 64 | + } |
| 65 | + auto adapter = std::make_shared<OpenVINOInferenceAdapter>(); |
| 66 | + adapter->loadModel(model, core, "AUTO"); |
| 67 | + return Anomaly(adapter); |
| 68 | +} |
| 69 | + |
| 70 | +AnomalyResult Anomaly::infer(cv::Mat image) { |
| 71 | + return pipeline.infer(image); |
| 72 | +} |
| 73 | + |
| 74 | +std::vector<AnomalyResult> Anomaly::inferBatch(std::vector<cv::Mat> images) { |
| 75 | + return pipeline.inferBatch(images); |
| 76 | +} |
| 77 | + |
| 78 | +std::map<std::string, ov::Tensor> Anomaly::preprocess(cv::Mat image) { |
| 79 | + std::map<std::string, ov::Tensor> input = {}; |
| 80 | + input.emplace(adapter->getInputNames()[0], utils::wrapMat2Tensor(image)); |
| 81 | + return input; |
| 82 | +} |
| 83 | + |
| 84 | +AnomalyResult Anomaly::postprocess(InferenceResult& infResult) { |
| 85 | + auto tensorName = adapter->getOutputNames().front(); |
| 86 | + ov::Tensor predictions = infResult.data[tensorName]; |
| 87 | + const auto& inputImgSize = infResult.inputImageSize; |
| 88 | + |
| 89 | + double pred_score; |
| 90 | + std::string pred_label; |
| 91 | + cv::Mat anomaly_map; |
| 92 | + cv::Mat pred_mask; |
| 93 | + std::vector<cv::Rect> pred_boxes; |
| 94 | + if (predictions.get_shape().size() == 1) { |
| 95 | + pred_score = predictions.data<float>()[0]; |
| 96 | + } else { |
| 97 | + const ov::Layout& layout = utils::getLayoutFromShape(predictions.get_shape()); |
| 98 | + const ov::Shape& predictionsShape = predictions.get_shape(); |
| 99 | + anomaly_map = cv::Mat(static_cast<int>(predictionsShape[ov::layout::height_idx(layout)]), |
| 100 | + static_cast<int>(predictionsShape[ov::layout::width_idx(layout)]), |
| 101 | + CV_32FC1, |
| 102 | + predictions.data<float>()); |
| 103 | + // find the max predicted score |
| 104 | + cv::minMaxLoc(anomaly_map, NULL, &pred_score); |
| 105 | + } |
| 106 | + pred_label = labels[pred_score > image_threshold ? 1 : 0]; |
| 107 | + |
| 108 | + pred_mask = anomaly_map >= pixel_threshold; |
| 109 | + pred_mask.convertTo(pred_mask, CV_8UC1, 1 / 255.); |
| 110 | + cv::resize(pred_mask, pred_mask, cv::Size{inputImgSize.width, inputImgSize.height}); |
| 111 | + anomaly_map = normalize(anomaly_map, pixel_threshold); |
| 112 | + anomaly_map.convertTo(anomaly_map, CV_8UC1, 255); |
| 113 | + |
| 114 | + pred_score = normalize(pred_score, image_threshold); |
| 115 | + if (pred_label == labels[0]) { // normal label |
| 116 | + pred_score = 1 - pred_score; // Score of normal is 1 - score of anomaly |
| 117 | + } |
| 118 | + |
| 119 | + if (!anomaly_map.empty()) { |
| 120 | + cv::resize(anomaly_map, anomaly_map, cv::Size{inputImgSize.width, inputImgSize.height}); |
| 121 | + } |
| 122 | + |
| 123 | + if (task == "detection") { |
| 124 | + pred_boxes = getBoxes(pred_mask); |
| 125 | + } |
| 126 | + |
| 127 | + AnomalyResult result; |
| 128 | + result.anomaly_map = std::move(anomaly_map); |
| 129 | + result.pred_score = pred_score; |
| 130 | + result.pred_label = std::move(pred_label); |
| 131 | + result.pred_mask = std::move(pred_mask); |
| 132 | + result.pred_boxes = std::move(pred_boxes); |
| 133 | + return result; |
| 134 | +} |
| 135 | + |
| 136 | +cv::Mat Anomaly::normalize(cv::Mat& tensor, float threshold) { |
| 137 | + cv::Mat normalized = ((tensor - threshold) / normalization_scale) + 0.5f; |
| 138 | + normalized = cv::min(cv::max(normalized, 0.f), 1.f); |
| 139 | + return normalized; |
| 140 | +} |
| 141 | + |
| 142 | +double Anomaly::normalize(double& value, float threshold) { |
| 143 | + double normalized = ((value - threshold) / normalization_scale) + 0.5f; |
| 144 | + return std::min(std::max(normalized, 0.), 1.); |
| 145 | +} |
| 146 | + |
| 147 | +std::vector<cv::Rect> Anomaly::getBoxes(cv::Mat& mask) { |
| 148 | + std::vector<cv::Rect> boxes; |
| 149 | + std::vector<std::vector<cv::Point>> contours; |
| 150 | + cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); |
| 151 | + for (auto& contour : contours) { |
| 152 | + std::vector<int> box; |
| 153 | + cv::Rect rect = cv::boundingRect(contour); |
| 154 | + boxes.push_back(rect); |
| 155 | + } |
| 156 | + return boxes; |
| 157 | +} |
0 commit comments