diff --git a/code/MicroPython/network_ap.py b/code/MicroPython/network_ap.py index 1e34a5e..e739827 100644 --- a/code/MicroPython/network_ap.py +++ b/code/MicroPython/network_ap.py @@ -6,16 +6,10 @@ def connect(essid='ESP', clients=3) -> bool: print('Starting AP: {0}...'.format(essid)) ap = network.WLAN(network.AP_IF) ap.active(True) - ap.config(essid=essid) - ap.config(max_clients=clients) + ap.config(essid=essid, max_clients=clients) - time.sleep(3) + while ap.active() == False: + pass - if ap.isconnected(): - print('AP "{0}" started'.format(essid)) - - return True - else: - print('Starting AP failed!') - - return False + print('AP "{0}" started'.format(essid)) + return True diff --git a/projects/OpenVINO/.gitignore b/projects/OpenVINO/.gitignore new file mode 100644 index 0000000..d05959e --- /dev/null +++ b/projects/OpenVINO/.gitignore @@ -0,0 +1,10 @@ +# models +Models/ +*.onnx +*.pt + +# junk +trash/ +11.jpg +12.jpg +Python/ diff --git a/projects/OpenVINO/C++/.clang-format b/projects/OpenVINO/C++/.clang-format new file mode 120000 index 0000000..ead1f91 --- /dev/null +++ b/projects/OpenVINO/C++/.clang-format @@ -0,0 +1 @@ +../../../code/C++/.clang-format \ No newline at end of file diff --git a/projects/OpenVINO/C++/.gitignore b/projects/OpenVINO/C++/.gitignore new file mode 100644 index 0000000..70363e8 --- /dev/null +++ b/projects/OpenVINO/C++/.gitignore @@ -0,0 +1,10 @@ +# xmake +.xmake/ +build/ + +# binary +a.out + +# other +infer.cpp +convert.py diff --git a/projects/OpenVINO/C++/build.sh b/projects/OpenVINO/C++/build.sh new file mode 100755 index 0000000..2c20c17 --- /dev/null +++ b/projects/OpenVINO/C++/build.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +clear +CV_INCLUDE=/opt/opencv-4.8.0/include/opencv4/ +CV_LIB=/opt/opencv-4.8.0/lib/ +export LD_LIBRARY_PATH=${CV_LIB}:${LD_LIBRARY_PATH} + +g++ -I${CV_INCLUDE} -L${CV_LIB} -o inference detect.cpp -lopenvino -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs -lopencv_dnn && + +./inference diff --git a/projects/OpenVINO/C++/i.cpp b/projects/OpenVINO/C++/i.cpp new file mode 100644 index 0000000..35b7e08 --- /dev/null +++ b/projects/OpenVINO/C++/i.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include "infer.hpp" + +int main(int argc, char *argv[]) { + // Проверяет количество аргументов + if (argc != 3) { + std::cerr << "Использование: " << argv[0] << " " << std::endl; + return EXIT_FAILURE; + } + + // Получает пути к модели и изображению из аргументов программы + const std::string model_path = argv[1]; + const std::string image_path = argv[2]; + + // Проверяем наличие OpenVINO попыткой вывести версию + try { + std::cout << ov::get_openvino_version() << std::endl; + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + return EXIT_FAILURE; + } + + // Читает изображение из файла + cv::Mat image = cv::imread(image_path); + if (image.empty()) { + std::cerr << "ОШИБКА: Не удалось загрузить изображение" << std::endl; + return EXIT_FAILURE; + } + + // Определение значений + const float probability = 0.5; + const float NMS = 0.5; + + // Создание объекта класса распознования + Inf *i; + i = new Inf(model_path, cv::Size(640, 640), probability, NMS); + + // Запуск распознования объектов + i->inference(image); + + // Запись результата в файл + cv::imwrite("/tmp/cpp_openvino_result.bmp", image); + + return EXIT_SUCCESS; +} diff --git a/projects/OpenVINO/C++/infer.cc b/projects/OpenVINO/C++/infer.cc new file mode 100644 index 0000000..bf5c507 --- /dev/null +++ b/projects/OpenVINO/C++/infer.cc @@ -0,0 +1,136 @@ +#include "infer.hpp" + +Inf::Inf(const std::string &model_path, const float &model_probability, const float &model_NMS) { + input_shape = cv::Size(640, 640); + probability = model_probability; + NMS = model_NMS; + init(model_path); +}; + +Inf::Inf(const std::string &model_path, const cv::Size model_input_shape, const float &model_probability, const float &model_NMS) { + input_shape = model_input_shape; + probability = model_probability; + NMS = model_NMS; + init(model_path); +}; + +void Inf::init(const std::string &model_path) { + ov::Core core; + std::shared_ptr model = core.read_model(model_path); + + // Если модель имеет динамические формы, + // изменяем модель в соответствиии с указанной формой + if (model->is_dynamic()) { + model->reshape({1, 3, static_cast(input_shape.height), static_cast(input_shape.width)}); + } + + // Настройка предварительной обработки для модели + ov::preprocess::PrePostProcessor ppp = ov::preprocess::PrePostProcessor(model); + ppp.input().tensor().set_element_type(ov::element::u8).set_layout("NHWC").set_color_format(ov::preprocess::ColorFormat::BGR); + ppp.input() + .preprocess() + .convert_element_type(ov::element::f32) + .convert_color(ov::preprocess::ColorFormat::RGB) + .scale({255, 255, 255}); + ppp.input().model().set_layout("NCHW"); + ppp.output().tensor().set_element_type(ov::element::f32); + model = ppp.build(); + + compiled_model = core.compile_model(model, "AUTO"); + inference_request = compiled_model.create_infer_request(); + + const std::vector> inputs = model->inputs(); + const ov::Shape in_shape = inputs[0].get_shape(); + input_shape = cv::Size2f(in_shape[2], in_shape[1]); + + const std::vector> outputs = model->outputs(); + const ov::Shape out_shape = outputs[0].get_shape(); + output_shape = cv::Size(out_shape[2], out_shape[1]); +}; + +void Inf::pre(const cv::Mat &frame) { + cv::Mat resized_frame; + cv::resize(frame, resized_frame, input_shape, 0, 0, cv::INTER_AREA); // Resize the frame to match the model input shape + + // Calculate scaling factor + scale_factor.x = static_cast(frame.cols / input_shape.width); + scale_factor.y = static_cast(frame.rows / input_shape.height); + + float *input_data = (float *)resized_frame.data; // Get pointer to resized frame data + const ov::Tensor input_tensor = + ov::Tensor(compiled_model.input().get_element_type(), compiled_model.input().get_shape(), input_data); + inference_request.set_input_tensor(input_tensor); // Set input tensor for inference +}; + +void Inf::post(cv::Mat &frame) { + std::vector class_list; + std::vector confidence_list; + std::vector box_list; + + const float *detections = inference_request.get_output_tensor().data(); + const cv::Mat detection_outputs(output_shape, CV_32F, (float *)detections); + + for (int i = 0; i < detection_outputs.cols; ++i) { + const cv::Mat classes_scores = detection_outputs.col(i).rowRange(4, detection_outputs.rows); + + cv::Point class_id; + double score; + cv::minMaxLoc(classes_scores, nullptr, &score, nullptr, &class_id); + + if (score > probability) { + class_list.push_back(class_id.y); + confidence_list.push_back(score); + + const float x = detection_outputs.at(0, i); + const float y = detection_outputs.at(1, i); + const float w = detection_outputs.at(2, i); + const float h = detection_outputs.at(3, i); + + cv::Rect box; + box.x = static_cast(x); + box.y = static_cast(y); + box.width = static_cast(w); + box.height = static_cast(h); + box_list.push_back(box); + } + } + + std::vector NMS_result; + cv::dnn::NMSBoxes(box_list, confidence_list, probability, NMS, NMS_result); + + for (int i = 0; i < NMS_result.size(); ++i) { + Detection result; + const unsigned short id = NMS_result[i]; + + result.class_id = class_list[id]; + result.probability = confidence_list[id]; + result.box = GetBoundingBox(box_list[id]); + + DrawDetectedObject(frame, result); + } +}; + +void Inf::inference(cv::Mat &frame) { + pre(frame); + inference_request.infer(); + post(frame); +}; + +cv::Rect Inf::GetBoundingBox(const cv::Rect &src) const { + cv::Rect box = src; + box.x = (box.x - box.width / 2) * scale_factor.x; + box.y = (box.y - box.height / 2) * scale_factor.y; + box.width *= scale_factor.x; + box.height *= scale_factor.y; + return box; +} + +void Inf::DrawDetectedObject(cv::Mat &frame, const Detection &detection) const { + const cv::Rect &box = detection.box; + const float &confidence = detection.probability; + const int &class_id = detection.class_id; + + const cv::Scalar &color = cv::Scalar(0, 0, 180); + + cv::rectangle(frame, cv::Point(box.x, box.y), cv::Point(box.x + box.width, box.y + box.height), color, 3); +} diff --git a/projects/OpenVINO/C++/infer.cpp b/projects/OpenVINO/C++/infer.cpp deleted file mode 100644 index d1064aa..0000000 --- a/projects/OpenVINO/C++/infer.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 2018-2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// -// https://docs.openvino.ai/2023.3/openvino_sample_hello_reshape_ssd.html - -#include -#include -#include - -// clang-format off -#include "openvino/openvino.hpp" -#include "openvino/opsets/opset9.hpp" - -#include "format_reader_ptr.h" -#include "samples/args_helper.hpp" -#include "samples/common.hpp" -#include "samples/slog.hpp" -// clang-format on - -// thickness of a line (in pixels) to be used for bounding boxes -constexpr int BBOX_THICKNESS = 2; - -using namespace ov::preprocess; - -int main(int argc, char* argv[]) { - try { - // -------- Get OpenVINO runtime version ----------------------------- - slog::info << ov::get_openvino_version() << slog::endl; - - // --------------------------- Parsing and validation of input arguments - if (argc != 4) { - std::cout << "Usage : " << argv[0] << " " << std::endl; - return EXIT_FAILURE; - } - const std::string model_path{argv[1]}; - const std::string image_path{argv[2]}; - const std::string device_name{argv[3]}; - // ------------------------------------------------------------------- - - // Step 1. Initialize OpenVINO Runtime core - ov::Core core; - // ------------------------------------------------------------------- - - // Step 2. Read a model - slog::info << "Loading model files: " << model_path << slog::endl; - std::shared_ptr model = core.read_model(model_path); - printInputAndOutputsInfo(*model); - - // Step 3. Validate model inputs and outputs - OPENVINO_ASSERT(model->inputs().size() == 1, "Sample supports models with 1 input only"); - OPENVINO_ASSERT(model->outputs().size() == 1, "Sample supports models with 1 output only"); - - // SSD has an additional post-processing DetectionOutput layer that simplifies output filtering, - // try to find it. - const ov::NodeVector ops = model->get_ops(); - const auto it = std::find_if(ops.begin(), ops.end(), [](const std::shared_ptr& node) { - return std::string{node->get_type_name()} == - std::string{ov::opset9::DetectionOutput::get_type_info_static().name}; - }); - if (it == ops.end()) { - throw std::logic_error("model does not contain DetectionOutput layer"); - } - // ------------------------------------------------------------------- - - // Step 4. Read input image - - // Read input image without resize - FormatReader::ReaderPtr reader(image_path.c_str()); - if (reader.get() == nullptr) { - std::cout << "Image " + image_path + " cannot be read!" << std::endl; - return 1; - } - - std::shared_ptr image_data = reader->getData(); - size_t image_channels = 3; - size_t image_width = reader->width(); - size_t image_height = reader->height(); - // ------------------------------------------------------------------- - - // Step 5. Reshape model to image size and batch size - // assume model layout NCHW - const ov::Layout model_layout{"NCHW"}; - - ov::Shape tensor_shape = model->input().get_shape(); - - size_t batch_size = 1; - - tensor_shape[ov::layout::batch_idx(model_layout)] = batch_size; - tensor_shape[ov::layout::channels_idx(model_layout)] = image_channels; - tensor_shape[ov::layout::height_idx(model_layout)] = image_height; - tensor_shape[ov::layout::width_idx(model_layout)] = image_width; - - std::cout << "Reshape network to the image size = [" << image_height << "x" << image_width << "] " << std::endl; - model->reshape({{model->input().get_any_name(), tensor_shape}}); - printInputAndOutputsInfo(*model); - // ------------------------------------------------------------------- - - // Step 6. Configure model preprocessing - const ov::Layout tensor_layout{"NHWC"}; - - // clang-format off - ov::preprocess::PrePostProcessor ppp = ov::preprocess::PrePostProcessor(model); - - // 1) input() with no args assumes a model has a single input - ov::preprocess::InputInfo& input_info = ppp.input(); - // 2) Set input tensor information: - // - precision of tensor is supposed to be 'u8' - // - layout of data is 'NHWC' - input_info.tensor(). - set_element_type(ov::element::u8). - set_layout(tensor_layout); - // 3) Adding explicit preprocessing steps: - // - convert u8 to f32 - // - convert layout to 'NCHW' (from 'NHWC' specified above at tensor layout) - ppp.input().preprocess(). - convert_element_type(ov::element::f32). - convert_layout("NCHW"); - // 4) Here we suppose model has 'NCHW' layout for input - input_info.model().set_layout("NCHW"); - // 5) output () with no args assumes a model has a single output - ov::preprocess::OutputInfo& output_info = ppp.output(); - // 6) declare output element type as FP32 - output_info.tensor().set_element_type(ov::element::f32); - - // 7) Apply preprocessing modifing the original 'model' - model = ppp.build(); - // clang-format on - // ------------------------------------------------------------------- - - // Step 7. Loading a model to the device - ov::CompiledModel compiled_model = core.compile_model(model, device_name); - // ------------------------------------------------------------------- - - // Step 8. Create an infer request - ov::InferRequest infer_request = compiled_model.create_infer_request(); - - // Step 9. Fill model with input data - ov::Tensor input_tensor = infer_request.get_input_tensor(); - - // copy NHWC data from image to tensor with batch - unsigned char* image_data_ptr = image_data.get(); - unsigned char* tensor_data_ptr = input_tensor.data(); - size_t image_size = image_width * image_height * image_channels; - for (size_t i = 0; i < image_size; i++) { - tensor_data_ptr[i] = image_data_ptr[i]; - } - // ------------------------------------------------------------------- - - // Step 10. Do inference synchronously - infer_request.infer(); - - // Step 11. Get output data from the model - ov::Tensor output_tensor = infer_request.get_output_tensor(); - - ov::Shape output_shape = model->output().get_shape(); - const size_t ssd_object_count = output_shape[2]; - const size_t ssd_object_size = output_shape[3]; - - const float* detections = output_tensor.data(); - // ------------------------------------------------------------------- - - std::vector boxes; - std::vector classes; - - // Step 12. Parse SSD output - for (size_t object = 0; object < ssd_object_count; object++) { - int image_id = static_cast(detections[object * ssd_object_size + 0]); - if (image_id < 0) { - break; - } - - // detection, has the format: [image_id, label, conf, x_min, y_min, x_max, y_max] - int label = static_cast(detections[object * ssd_object_size + 1]); - float confidence = detections[object * ssd_object_size + 2]; - int xmin = static_cast(detections[object * ssd_object_size + 3] * image_width); - int ymin = static_cast(detections[object * ssd_object_size + 4] * image_height); - int xmax = static_cast(detections[object * ssd_object_size + 5] * image_width); - int ymax = static_cast(detections[object * ssd_object_size + 6] * image_height); - - if (confidence > 0.5f) { - // collect only objects with >50% probability - classes.push_back(label); - boxes.push_back(xmin); - boxes.push_back(ymin); - boxes.push_back(xmax - xmin); - boxes.push_back(ymax - ymin); - - std::cout << "[" << object << "," << label << "] element, prob = " << confidence << ", (" << xmin - << "," << ymin << ")-(" << xmax << "," << ymax << ")" << std::endl; - } - } - - // draw bounding boxes on the image - addRectangles(image_data.get(), image_height, image_width, boxes, classes, BBOX_THICKNESS); - - const std::string image_name = "hello_reshape_ssd_output.bmp"; - if (writeOutputBmp(image_name, image_data.get(), image_height, image_width)) { - std::cout << "The resulting image was saved in the file: " + image_name << std::endl; - } else { - throw std::logic_error(std::string("Can't create a file: ") + image_name); - } - - } catch (const std::exception& ex) { - std::cerr << ex.what() << std::endl; - return EXIT_FAILURE; - } - std::cout << std::endl - << "This sample is an API example, for any performance measurements " - "please use the dedicated benchmark_app tool" - << std::endl; - return EXIT_SUCCESS; -} diff --git a/projects/OpenVINO/C++/infer.hpp b/projects/OpenVINO/C++/infer.hpp new file mode 100644 index 0000000..d3a8059 --- /dev/null +++ b/projects/OpenVINO/C++/infer.hpp @@ -0,0 +1,46 @@ +#ifndef INFER_HPP_ +#define INFER_HPP_ + +#include +#include +#include +#include +#include + +// Структура обнаружения +struct Detection { + short class_id; // Идентификатор класс + float probability; // Вероятность обнаружения + cv::Rect box; // Размеры объекта +}; + +// Класс обнаружения +class Inf { +public: + Inf() {}; + Inf(const std::string &model_path, const float &model_probability, const float &model_NMS); + Inf(const std::string &model_path, const cv::Size model_input_shape, const float &model_probability, const float &model_NMS); + ~Inf() {}; + + void inference(cv::Mat &frame); + +private: + void init(const std::string &model_path); + void pre(const cv::Mat &frame); + void post( cv::Mat &frame); + cv::Rect GetBoundingBox(const cv::Rect &src) const; + void DrawDetectedObject(cv::Mat &frame, const Detection &detections) const; + + cv::Point2f scale_factor; + + cv::Size2f input_shape; + cv::Size output_shape; + + ov::InferRequest inference_request; + ov::CompiledModel compiled_model; + + float probability; + float NMS; +}; + +#endif // INFER_HPP_ diff --git a/projects/OpenVINO/C++/result.bmp b/projects/OpenVINO/C++/result.bmp new file mode 120000 index 0000000..db4247b --- /dev/null +++ b/projects/OpenVINO/C++/result.bmp @@ -0,0 +1 @@ +/tmp/cpp_openvino_result.bmp \ No newline at end of file diff --git a/projects/OpenVINO/C++/xmake.lua b/projects/OpenVINO/C++/xmake.lua new file mode 100644 index 0000000..8450a78 --- /dev/null +++ b/projects/OpenVINO/C++/xmake.lua @@ -0,0 +1,26 @@ +set_project("inf") +set_languages("cxx17") +add_rules("mode.debug", "mode.release") + +if is_mode("debug") then + set_symbols("debug") + set_optimize("none") +end + +add_includedirs( + "/opt/opencv-4.8.0/include/opencv4/" -- OpenCV +) +add_linkdirs( + "/opt/opencv-4.8.0/lib/" -- OpenCV +) + +target("inf") + set_kind("binary") + add_syslinks( + "openvino", + "opencv_core", "opencv_imgproc", "opencv_highgui", + "opencv_imgcodecs", "opencv_dnn" + ) + add_files("i.cpp", "infer.cc") + + add_runenvs("LD_LIBRARY_PATH", "/opt/opencv-4.8.0/lib/") diff --git a/projects/OpenVINO/Python/.gitignore b/projects/OpenVINO/Python/.gitignore index ad4b5e6..e69de29 100644 --- a/projects/OpenVINO/Python/.gitignore +++ b/projects/OpenVINO/Python/.gitignore @@ -1,3 +0,0 @@ -# models -intel/ -*.onnx diff --git a/projects/OpenVINO/Python/detect.py b/projects/OpenVINO/Python/detect.py index 7bb8080..0541a8f 100644 --- a/projects/OpenVINO/Python/detect.py +++ b/projects/OpenVINO/Python/detect.py @@ -4,10 +4,8 @@ import cv2 import numpy as np import openvino as ov -# model_path = './yolov8/yolov8n/yolov8n.xml' -model_path = './yolov8/yolov8s/yolov8s.xml' -# model_path = './intel/person-detection-retail-0013/FP16/person-detection-retail-0013.xml' -image_path = 'cat_dog.jpg' +model_path = '../Models/yolov8n_openvino_model/yolov8n.xml' +image_path = '../../../assets/bus.jpg' device_name = 'CPU' @@ -30,7 +28,7 @@ def main(): ppp = ov.preprocess.PrePostProcessor(model) ppp.input().tensor().set_element_type(ov.Type.u8).set_layout(ov.Layout('NHWC')) ppp.input().model().set_layout(ov.Layout('NCHW')) - ppp.output().tensor().set_element_type(ov.Type.f32) # f16 + ppp.output().tensor().set_element_type(ov.Type.f32) model = ppp.build() compiled_model = core.compile_model(model, device_name) @@ -43,7 +41,7 @@ def main(): for detection in detections: confidence = detection[2] - if confidence > 0.7: + if confidence > 0.25: class_id = int(detection[1]) xmin = int(detection[3] * w)