OpenVINO~合集1

仅openvino合集~~  whaosoft aiot Taobao 开发板商城 天皓智联 www.143ai.com

一、OpenVINO部署Mask-RCNN实例分割网络

OpenVINO是英特尔推出的一款全面的工具套件,用于快速部署应用和解决方案,支持计算机视觉的CNN网络结构超过150余种。本文展示了用OpenVINO部署Mask-RCNN实例分割网络的详细过程及代码演示。

模型介绍

OpenVINO支持Mask-RCNN与yolact两种实例分割模型的部署,其中Mask-RCNN系列的实例分割网络是OpenVINO官方自带的,直接下载即可,yolact是来自第三方的公开模型库。

图片

这里以instance-segmentation-security-0050模型为例说明,该模型基于COCO数据集训练,支持80个类别的实例分割,加上背景为81个类别。

OpenVINO支持部署Faster-RCNN与Mask-RCNN网络时候输入的解析都是基于两个输入层,它们分别是:

im_data : NCHW=[1x3x480x480]
im_info: 1x3 三个值分别是H、W、Scale=1.0

输出有四个,名称与输出格式及解释如下:

  • name: classes, shape: [100, ]
    预测的100个类别可能性,值在[0~1]之间
  • name: scores: shape: [100, ]
    预测的100个Box可能性,值在[0~1]之间
  • name: boxes, shape: [100, 4]
    预测的100个Box坐标,左上角与右下角,基于输入的480x480
  • name: raw_masks, shape: [100, 81, 28, 28]
    Box ROI区域的实例分割输出,81表示类别(包含背景),28x28表示ROI大小。

上面都是官方文档给我的关于模型的相关信息,但是我发现该模型的实际推理输raw_masks输出格式大小为:100x81x14x14这个算文档没更新吗?

代码演示

这边的代码输出层跟输入层都不止一个,所以为了简化,我用了两个for循环设置了输入与输出数据精度,然后直接通过hardcode来获取推理之后各个输出层对应的数据部分,首先获取类别,根据类别ID与Box的索引,直接获取实例分割mask,然后随机生成颜色,基于mask实现与原图BOX ROI的叠加,产生了实例分割之后的效果输出。完整的演示代码分为下面几步:IE引擎初始化与模型加载

InferenceEngine::Core ie;
std::vector<std::string> coco_labels;
read_coco_labels(coco_labels);
cv::RNG rng(12345);


cv::Mat src = cv::imread("D:/images/sport-girls.png");
cv::namedWindow("input", cv::WINDOW_AUTOSIZE);
int im_h = src.rows;
int im_w = src.cols;


InferenceEngine::CNNNetwork network = ie.ReadNetwork(xml, bin);
InferenceEngine::InputsDataMap inputs = network.getInputsInfo();
InferenceEngine::OutputsDataMap outputs = network.getOutputsInfo();
设置输入与输出数据格式
std::string image_input_name = "";
std::string image_info_name = "";
int in_index = 0;
for (auto item : inputs) {
    if (in_index == 0) {
        image_input_name = item.first;
        auto input_data = item.second;
        input_data->setPrecision(Precision::U8);
        input_data->setLayout(Layout::NCHW);
    }
    else {
        image_info_name = item.first;
        auto input_data = item.second;
        input_data->setPrecision(Precision::FP32);
    }
    in_index++;
}


for (auto item : outputs) {
    std::string output_name = item.first;
    auto output_data = item.second;
    output_data->setPrecision(Precision::FP32);
    std::cout << "output name: " << output_name << std::endl;
}

设置blob输入数据与推理

auto executable_network = ie.LoadNetwork(network, "CPU");
auto infer_request = executable_network.CreateInferRequest();


auto input = infer_request.GetBlob(image_input_name);
matU8ToBlob<uchar>(src, input);


auto input2 = infer_request.GetBlob(image_info_name);
auto imInfoDim = inputs.find(image_info_name)->second->getTensorDesc().getDims()[1];
InferenceEngine::MemoryBlob::Ptr minput2 = InferenceEngine::as<InferenceEngine::MemoryBlob>(input2);
auto minput2Holder = minput2->wmap();
float *p = minput2Holder.as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type *>();
p[0] = static_cast<float>(inputs[image_input_name]->getTensorDesc().getDims()[2]);
p[1] = static_cast<float>(inputs[image_input_name]->getTensorDesc().getDims()[3]);
p[2] = 1.0f;


infer_request.Infer();

解析输出结果

auto scores = infer_request.GetBlob("scores");
auto boxes = infer_request.GetBlob("boxes");
auto clazzes = infer_request.GetBlob("classes");
auto raw_masks = infer_request.GetBlob("raw_masks");
const float* score_data = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(scores->buffer());
const float* boxes_data = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(boxes->buffer());
const float* clazzes_data = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(clazzes->buffer());
const auto raw_masks_data = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(raw_masks->buffer());
const SizeVector scores_outputDims = scores->getTensorDesc().getDims();
const SizeVector boxes_outputDims = boxes->getTensorDesc().getDims();
const SizeVector mask_outputDims = raw_masks->getTensorDesc().getDims();
const int max_count = scores_outputDims[0];
const int object_size = boxes_outputDims[1];
printf("mask NCHW=[%d, %d, %d, %d]\n", mask_outputDims[0], mask_outputDims[1], mask_outputDims[2], mask_outputDims[3]);
int mask_h = mask_outputDims[2];
int mask_w = mask_outputDims[3];
size_t box_stride = mask_h * mask_w * mask_outputDims[1];
for (int n = 0; n < max_count; n++) {
    float confidence = score_data[n];
    float xmin = boxes_data[n*object_size] * w_rate;
    float ymin = boxes_data[n*object_size + 1] * h_rate;
    float xmax = boxes_data[n*object_size + 2] * w_rate;
    float ymax = boxes_data[n*object_size + 3] * h_rate;
    if (confidence > 0.5) {
        cv::Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        cv::Rect box;
        float x1 = std::min(std::max(0.0f, xmin), static_cast<float>(im_w));
        float y1 = std::min(std::max(0.0f,ymin), static_cast<float>(im_h));
        float x2 = std::min(std::max(0.0f, xmax), static_cast<float>(im_w));
        float y2 = std::min(std::max(0.0f, ymax), static_cast<float>(im_h));
        box.x = static_cast<int>(x1);
        box.y = static_cast<int>(y1);
        box.width = static_cast<int>(x2 - x1);
        box.height = static_cast<int>(y2 - y1);
        int label = static_cast<int>(clazzes_data[n]);
        std::cout <<"confidence: "<< confidence<<" class name: "<< coco_labels[label] << std::endl;
        // 解析mask
        float* mask_arr = raw_masks_data + box_stride * n + mask_h * mask_w * label;
        cv::Mat mask_mat(mask_h, mask_w, CV_32FC1, mask_arr);
        cv::Mat roi_img = src(box);
        cv::Mat resized_mask_mat(box.height, box.width, CV_32FC1);
        cv::resize(mask_mat, resized_mask_mat, cv::Size(box.width, box.height));
        cv::Mat uchar_resized_mask(box.height, box.width, CV_8UC3,color);
        roi_img.copyTo(uchar_resized_mask, resized_mask_mat <= 0.5);
        cv::addWeighted(uchar_resized_mask, 0.7, roi_img, 0.3, 0.0f, roi_img);
        cv::putText(src, coco_labels[label].c_str(), box.tl()+(box.br()-box.tl())/2, cv::FONT_HERSHEY_PLAIN, 1.0, cv::Scalar(0, 0, 255), 1, 8);
    }
}

最终程序测试结果:

图片

图片

二、将PyTorch和TensorFlow模型导入OpenVINO中使用

  只需几行代码即可轻松将PyTorch和TensorFlow模型导入OpenVINO。使用 PyTorch或TensorFlow 开发的模型可以轻松集成到 OpenVINO 推理管道中,以便在各种设备上实现一流的性能。开发人员不再需要管理模型导出或转换脚本:相反,可以从其原生格式加载和部署模型。

    以下示例展示了如何在 OpenVINO 中轻松加载 TensorFlow 和 PyTorch 模型。只需几行代码即可加载模型、将其转换为 OpenVINO 格式并进行编译以进行推理。

    虽然上述示例提供了将模型导入 OpenVINO 的简单选项,但还有其他选项可以提供更多自定义和灵活性。本解决方案简介的其余部分介绍了各种选项,解释了何时使用每个选项,并提供了展示如何使用它们的代码片段。

使用 OpenVINO 中的模型

    OpenVINO 中的模型状态

   OpenVINO 中的模型有三种状态:保存在磁盘上、已加载但未编译(ov.Model)或已加载并编译(ov.CompiledModel)。

  • 保存在磁盘上

    顾名思义,处于此状态的模型由一个或多个完全代表神经网络的文件组成。由于 OpenVINO 不仅支持其专有格式,还支持其他框架,因此模型的存储方式可能有所不同。例如:

    ✧ OpenVINO IR:一对 .xml 和 .bin 文件

    ✧ONNX:.onnx 文件

    ✧ TensorFlow:包含一个 .pb 文件和两个子文件夹的目录,或者仅包含一个 .pb 文件

    ✧ TensorFlow Lite:.tflite 文件

    ✧ PaddlePaddle:.pdmodel文件

  • 已加载但未编译

    在此状态下,通过解析文件或转换现有框架对象在内存中创建一个模型对象 (ov.Model)。由于此对象尚未连接到任何特定设备,因此尚无法对其进行推理,但它允许自定义,例如重塑其输入、应用量化,甚至添加预处理步骤。

  • 已加载和编译

    当为模型对象指定一个或多个设备来运行时(ov.CompiledModel),即可实现此状态,从而允许进行设备优化并实现推理。

OpenVINO 中读取、转换和保存模型的函数

    OpenVINO 提供了多种与模型配合使用的函数:

  • read_model

    从文件创建 ov.Model。

    支持的文件格式:OpenVINO IR、ONNX、PaddlePaddle、TensorFlow 和 TensorFlow Lite。不直接支持 PyTorch 文件。

    直接读取 OpenVINO 文件,而其他格式则自动转换。

  • compile_model

    从文件或 ov.Model 对象创建 ov.CompiledModel。

    支持的文件格式:OpenVINO IR、ONNX、PaddlePaddle、TensorFlow 和 TensorFlow Lite。不直接支持 PyTorch 文件。

    直接读取 OpenVINO 文件,而其他格式则自动转换。

  • convert_model

    从文件或 Python 内存对象创建 ov.Model。

    支持的文件格式:ONNX、PaddlePaddle、TensorFlow 和 TensorFlow Lite。

    支持的框架对象:PaddlePaddle、TensorFlow 和 PyTorch。

    此方法仅在 Python API 中可用。

  • save_model

    将 ov.Model 保存为 OpenVINO IR 格式。

    默认将权重压缩为 FP16。

    此方法仅在 Python API 中可用。

    虽然本文重点介绍了在 OpenVINO 中运行 TensorFlow 和 PyTorch 模型的不同方法,但从性能角度来看,重复使用它们可能不是最佳选择。与其每次都直接使用框架文件或 Python 对象,更好的选择是将模型导入 OpenVINO 一次,根据需要进行自定义,然后使用 save_model 将其保存到 OpenVINO IR。然后,可以根据需要使用 read_model 读取保存的模型,从而避免额外的转换。查看“进一步改进”部分,了解使用 OpenVINO IR 的其他原因。

    另请注意,尽管可以直接使用来自 TensorFlow 等框架的文件,但这并不意味着 OpenVINO 在后台使用这些框架,文件和对象始终会转换为 OpenVINO 理解的格式,即 OpenVINO IR。

TensorFlow 导入选项

    OpenVINO 对 TensorFlow 的直接支持使开发人员无需更改即可在 OpenVINO 推理管道中使用其模型。但是,由于存在多种方法,因此可能不清楚哪种方法最适合特定情况。下图旨在在特定情况下简化此决策,但根据用例,应考虑一些其他注意事项。 

    方法 1. 使用 ov.convert_model 函数转换(仅限 Python)

    如上所示,如果您的起点是内存中的 Python 对象,例如 tf.keras.Model 或 tf.Module,则在 OpenVINO 中获取模型的直接方法是使用 ov.convert_model。此方法会生成一个 ov.Model(三种状态之一),稍后可以对其进行重塑、保存到 OpenVINO IR 或编译以进行推理。在代码中,它可能如下所示:

import openvino as ov
import tensorflow as tf


# 1a. Convert model created with TF code
model = tf.keras.applications.resnet50.ResNet50(weights="imagenet")
ov_model = ov.convert_model(model)


# 1b. Convert model from file
ov_model = ov.convert_model("model.pb")




# 2. Compile model from memory
core = ov.Core()
compiled_model = core.compile_model(ov_model)

    方法 2. 使用 ov.compile_model 函数从文件转换

    如果您从文件开始,您将需要查看模型是否正常或者是否需要定制,例如应用量化或重塑其输入。

    如果模型不需要定制,则应使用 ov.Core.compile_model,它可以一次性读取、转换(如果需要)和编译模型,使其准备好进行推理。代码应如下所示:

import openvino as ov


# 1. Compile model from file
core = ov.Core()
compiled_model = core.compile_model("model.pb")

    方法 3. 使用 ov.read_model 函数从文件转换

    如果模型确实需要定制,可以使用 ov.read_model,因为它只返回一个可以量化或重塑其输入的 ov.Model。(注意:此方法也适用于 OpenVINO C++ API,因此对于在 C++ 环境中工作的开发人员很有用。)

import openvino as ov


# 1. Convert model from file
core = ov.Core()
ov_model = ov.read_model("model.pb")


# 2. Compile model from memory
compiled_model = core.compile_model(ov_model)

    方法 4. 使用 OpenVINO 模型转换器 (ovc CLI) 从文件进行转换

    但是,如果事先知道输入重塑和/或模型有多个输出但只需要其中一部分,OpenVINO 在转换模型时提供了两种等效方法。其中之一是 CLI 命令 ovc,而另一种是前面提到的 ov.convert_model(在方法 1 中讨论)。

    ovc 工具与 ov.convert_model 类似,只不过它使用命令行而不是 Python 环境。它会将模型转换为 OpenVINO IR 格式,应用您指定的任何配置,并将转换后的模型保存到磁盘。如果您不使用 Python 模型(例如,如果您在 C++ 环境中进行开发)或者您更喜欢使用命令行而不是 Python 脚本,那么它会很有用。

    下面的代码展示了如何使用 ovc 转换模型,然后加载它进行推理:

# 1. Convert model from file
ovc model.pb
import openvino as ov


# 2. Load model from file
core = ov.Core()
ov_model = core.read_model("model.xml")


# 3. Compile model from memory
compiled_model = core.compile_model(ov_model)

支持的模型格式

    OpenVINO 凭借其不同的功能,支持从文件和 Python 对象加载模型,允许使用 1.X 和 2.X 的多种 TensorFlow 格式。

    在 TensorFlow 2.X 中,模型通常以SavedModel格式保存,其中包含模型的检查点和训练信息。还支持另外两种格式:较旧的Keras H5 (.h5) 和较新的Keras v3 (.keras)。相比之下,TensorFlow 1.X 模型通常以冻结图的形式导出,尽管使用非冻结格式(例如 SavedModel 和 MetaGraph)。

    因此,OpenVINO 对 TensorFlow 模型的支持如下:

    请注意,TensorFlow 2.X Keras H5 和 Keras v3 格式均不直接支持,但仍提供前者的说明。

    OpenVINO 还支持 TensorFlow Lite 文件,请参阅“ONNX、PaddlePaddle 和 TensorFlow Lite 导入选项”了解更多信息。

PyTorch 导入选项

    OpenVINO 对 PyTorch 的直接支持使开发人员无需更改即可在 OpenVINO 推理管道中使用他们的模型。OpenVINO 提供了多种使用 PyTorch 的方法,因此可能不清楚哪种方法最适合特定情况。下图旨在在特定情况下简化此决策,但根据用例,应考虑一些其他注意事项。

    PyTorch 模型可以直接从 Python 对象导入 OpenVINO,但也可以使用已保存的 PyTorch 文件。要使用已保存的 PyTorch 文件,需要先将其加载到 PyTorch 中以将其转换为 Python 对象。

    将模型加载为 PyTorch Python 对象后,您可以决定是否直接开始使用 OpenVINO 框架及其功能,或者在利用 OpenVINO 的优化的同时保留在 PyTorch 框架内。

    方法 1. 使用 ov.convert_model 函数进行转换

    如果首选 OpenVINO,则可以使用 ov.convert_model 方法。它会生成一个 ov.Model(3 种状态之一),稍后可以对其进行重塑、保存到 OpenVINO IR 或编译以进行推理。在代码中,它可能如下所示:

import openvino as ov
import torch
from torchvision.models import resnet50


# 1a. Convert model created with PyTorch code
model = resnet50(weights="DEFAULT")
model.eval()
ov_model = ov.convert_model(model, example_input=torch.rand(1, 3, 224, 224))


# 1b. Convert model loaded from PyTorch file
model = torch.load("model.pt")
model.eval()
ov_model = ov.convert_model(model)


# 2. Compile model from memory
core = ov.Core()
compiled_model = core.compile_model(ov_model)

    请注意,是否需要设置 example_input 取决于所使用的模型。但是,建议始终设置它(如果可用),因为它通常会产生更高质量的模型。有关更多详细信息,请查看文档。

https://docs.openvino.ai/2024/openvino-workflow/model-preparation/convert-model-pytorch.html

    方法 2. 在 PyTorch 中使用 OpenVINO 后端

    如果首选 PyTorch 语法,自 PyTorch 2.0 和 OpenVINO 2023.1 以来,可以通过在 torch.compile 中将 PyTorch 模型指定为后端来使用 OpenVINO 进行优化。

import openvino.torch
import torch
from torchvision.models import resnet50


# 1a. Compile model created with PyTorch code
model = resnet50(weights="DEFAULT")
model.eval()
compiled_model = torch.compile(model, backend="openvino")


# 1b. Compile model loaded from PyTorch file
model = torch.load("model.pt")
model.eval()
compiled_model = torch.compile(model, backend="openvino")

    方法 3. 将模型导出到 ONNX 并使用 OpenVINO 的方法之一

    如果这两种方法都无法成功转换模型,还有第三种方法,这种方法曾经是 OpenVINO 中使用 PyTorch 的主要方式,但现在主要被视为备用方案。此方法包括将 PyTorch 模型导出到 ONNX,然后使用 OpenVINO 中可用的不同方法加载它。有关更多详细信息,请参阅“ONNX、PaddlePaddle 和 TensorFlow Lite 导入选项”。

import torch
import openvino as ov
from torchvision.models import resnet50


# 1. Export PyTorch model to ONNX
model = resnet50(weights="DEFAULT")
model.eval()


dummy_input = torch.randn(1,3,224,224)
torch.onnx.export(model, dummy_input, "model.onnx")


# 2. Use an OpenVINO method to read and compile it, for example, compile_model
core = ov.Core()
compiled_model = core.compile_model("model.onnx")

支持的模型格式

    由于 PyTorch 没有包含不使用 torch 重现模型所需的所有内容的保存格式,因此 OpenVINO 仅支持直接加载 Python 对象。支持如下:

    Python Objects

  • torch.nn.Module
  • torch.jit.ScriptModule
  • torch.jit.ScriptFunction

ONNX、PaddlePaddle 和 TensorFlow Lite 导入选项

    TensorFlow 和 PyTorch 并不是 OpenVINO 支持的唯一框架;它还支持 ONNX、PaddlePaddle 和 TensorFlow Lite。本节的目的是简要介绍如何将它们导入 OpenVINO。

    ONNX、PaddlePaddle 和 TensorFlow Lite 文件具有与 TensorFlow 文件相同的支持,即“TensorFlow 导入选项”中描述的所有文件方法都适用于它们。唯一似乎也支持 Python 对象的是 PaddlePaddle。

    所有框架的完整支持如下:

进一步改进

    从解决方案简介中可以看出,有几种方法可以将框架模型导入 OpenVINO。但是,每次都必须转换模型会影响性能。因此,对于大多数用例,通常最好先转换模型一次,然后直接使用 OpenVINO 自己的格式 OpenVINO IR。下面列出了使用 OpenVINO IR 的一些原因。

保存到 IR 以改善首次推理延迟

    当首次推理延迟很重要时,与其每次加载框架模型时都转换它(这可能需要一些时间,具体取决于模型的大小),不如只转换一次,使用 save_model 将模型保存为 OpenVINO IR,然后根据需要使用 read_model 加载它。这应该可以缩短模型进行首次推理所需的时间,因为它避免了转换步骤。

在 FP16 中保存到 IR 以节省空间

    在 OpenVINO IR 中保存的另一个原因可能是节省存储空间,如果使用 FP16 则更是如此,因为它可以将尺寸减少约 50%,这对于像 Llama2–7B 这样的大型模型特别有用。

保存到 IR 以避免推理代码中存在大量依赖

    还有一个考虑因素是,要转换 Python 对象,环境中需要原始框架。TensorFlow 和 PyTorch 等框架往往是大型依赖项(数 GB),并非所有推理环境都有足够的空间来容纳它们。将模型转换为 OpenVINO IR 允许它们在 OpenVINO 是唯一依赖项的环境中使用,因此所需的磁盘空间要少得多。另一个好处是,直接使用 OpenVINO 加载和编译通常比在源框架中加载模型然后转换和编译它占用更少的运行时内存。

    下面显示了如何利用 OpenVINO IR 的示例:

# Run once
import openvino as ov
import tensorflow as tf


# 1. Convert model created with TF code
model = tf.keras.applications.resnet50.ResNet50(weights="imagenet")
ov_model = ov.convert_model(model)


# 2. Save model as OpenVINO IR
ov.save_model(ov_model, 'model.xml', compress_to_fp16=True) # enabled by default

# Repeat as needed
import openvino as ov


# 3. Load model from file
core = ov.Core()
ov_model = core.read_model("model.xml")


# 4. Compile model from memory
compiled_model = core.compile_model(ov_model)

   在OpenVINO IR中保存模型一次,即可多次使用!

三、超轻量级CPU和OpenVINO模型~100+ FPS快速人脸检测

    本文介绍了一个非常令人印象深刻的人脸检测解决方案,该解决方案非常高效和精确。更重要的是,它可以在 Intel CPU 上实现超过 100 fps,而无需 GPU。

    OpenVINO是一个强大的工具包,它是开源的,经过优化,可以增强各种设备的深度学习模型。OpenVINO的主要目的是通过提供一个可以针对不同用例进行定制的简化和高效的框架,简化开发和部署AI驱动的应用程序的过程。无论您是在从事与计算机视觉 🗨️、自然语言处理相关的项目,还是任何其他与人工智能相关的领域,OpenVINO都可以帮助您最大限度地发挥模型的潜力,并将您的想法变为现实。

模型下载

    OpenVINO GitHub 仓库有一个部分包含不少于一堆可供使用的公共预训练模型!

https://github.com/openvinotoolkit
https://github.com/openvinotoolkit/open_model_zoo/blob/master/models/public/index.md

    在该页面上,其中包含所有公共预训练模型、用于分类、分割、样式传输、检测等任务的模型......我们可以在该页面的“目标检测模型”中找到今天的目标模型。

    具体来说,我们今天要找的是“Ultra Lightweight Face Detection RFB 320”,如下图所示。

    按照此模型的文档,我们将看到如下内容:

    该模型是在WIDER FACE数据集上训练的,这是一个具有挑战性的数据集,因此我们可以期待在野外场景中有一个强大的模型。此外,很高兴看到参数数量 (0.28 M) 和浮点操作数量 (0.17 GFLOPS),这证实了该模型确实是“超轻量级”的。

具体步骤

    强烈建议您创建一个 Python 虚拟环境。使用你想要的任何东西,在我的情况下,我正在使用 Miniconda env。

    安装 openvino dev 库

$ pip install openvino-dev

    之后,您需要从 OpenVINO 存储库下载预训练模型,您可以按照以下步骤进行操作

$ omz_downloader --name ultra-lightweight-face-detection-rfb-320 --output_dir model

    该命令行将下载一个 .onnx 文件,因此现在您可以调用一个程序来将此文件转换为 openvino 的中间表示,使用:

$ omz_converter --name ultra-lightweight-face-detection-rfb-320 --download_dir model --output_dir model --precisinotallow=FP16

    完成这些步骤后,模型文件夹下应包含以下两个文件

    现在我们可以进入一些 Python 代码,了解如何在我们的程序中使用它。为了让事情更有条理,更易于在您想要的任何应用程序上即插即用人脸检测器,我将分享一种方法,将这个转换为 OpenVINO IR 的预训练模型用作 Python 对象。

    下面是一个可以使用此模型的 FaceDetector 类。请注意,我们可以轻松地利用此结构来适应并在不同的模型中使用它。

from openvino.runtime import Core
import numpy as np
import cv2


from src import utils


class FaceDetector:
    def __init__(self,
                 model,
                 confidence_thr=0.5,
                 overlap_thr=0.7):
        # load and compile the model
        core = Core()
        model = core.read_model(model=model)
        compiled_model = core.compile_model(model=model)
        self.model = compiled_model


        # 'Cause that model has more than one output,
        # We are saving the names in a more human frendly
        # variable to remember later how to recover the output we wish
        # In our case here, is a output for hte bbox and other for the score
        # /confidence. Have a look at the openvino documentation for more i
        self.output_scores_layer = self.model.output(0)
        self.output_boxes_layer  = self.model.output(1)
        # confidence threshold
        self.confidence_thr = confidence_thr
        # threshold for the nonmaximum suppression
        self.overlap_thr = overlap_thr


    def preprocess(self, image):
        """
            input image is a numpy array image representation,
            in the BGR format of any shape.
        """
        # resize to match the expected by the model
        input_image = cv2.resize(image, dsize=[320,240])
        # changing from [H, W, C] to [C, H, W]. "channels first"
        input_image = np.expand_dims(input_image.transpose(2,0,1), axis=0)
        return input_image


    def posprocess(self, pred_scores, pred_boxes, image_shape):
        # get all predictions with more than confidence_thr of confidence
        filtered_indexes = np.argwhere( pred_scores[0,:,1] > self.confidence_thr  ).tolist()
        filtered_boxes   = pred_boxes[0,filtered_indexes,:]
        filtered_scores  = pred_scores[0,filtered_indexes,1]


        if len(filtered_scores) == 0:
            return [],[]


        # convert all boxes to image coordinates
        h, w = image_shape
        def _convert_bbox_format(*args):
            bbox = args[0]
            x_min, y_min, x_max, y_max = bbox
            x_min = int(w*x_min)
            y_min = int(h*y_min)
            x_max = int(w*x_max)
            y_max = int(h*y_max)
            return x_min, y_min, x_max, y_max


        bboxes_image_coord = np.apply_along_axis(_convert_bbox_format, axis = 2, arr=filtered_boxes)


        # apply non-maximum supressions
        bboxes_image_coord, indexes = utils.non_max_suppression(bboxes_image_coord.reshape([-1,4]),
                                                                overlapThresh=self.overlap_thr)
        filtered_scores = filtered_scores[indexes]
        return bboxes_image_coord, filtered_scores


    def draw_bboxes(self, image, bboxes, color=[0,255,0]):
        # Just for visualization
        # draw all bboxes on the input image
        for boxe in bboxes:
            x_min, y_min, x_max, y_max = boxe
            pt1 = (x_min, y_min)
            pt2 = (x_max, y_max)
            cv2.rectangle(image, pt1, pt2, color=color, thickness=2, lineType=cv2.LINE_4)#BGR


    def inference(self, image):
        input_image = self.preprocess(image)
        # inference
        pred_scores = self.model( [input_image] )[self.output_scores_layer]
        pred_boxes = self.model( [input_image] )[self.output_boxes_layer]


        image_shape = image.shape[:2]
        faces, scores = self.posprocess(pred_scores, pred_boxes, image_shape)
        return faces, scores

    基本上有四种主要方法:

  • __init__:我正在加载 openvino 编译的模型,并保存一些参数供以后使用,例如最小置信度阈值和重叠阈值。用于非极大值抑制算法。
  • preprocess:负责确保原始BRG图像符合要输入到加载模型中的要求的方法;
  • inference:我们最常使用的方法,基本上是将所有事物放在一起,首先是预处理,原始模型推理,然后是后处理步骤;
  • posprocessing:旨在将原始输出转换为更常用的格式,例如像素坐标中的左上角。对于 bbox 部件,并且还应用非极大值抑制来删除冗余边界框预测。

    下面是一个示例,我正在一个通用视频上运行完整的演示脚本。我只需使用 CPU 就可以达到近 100 FPS(关闭录制程序后效果会更多一些)。

图片

    完整代码下载:

https://github.com/Gabriellgpc/ultra-lightweight-face-detection

四、OpenVINO~无监督异常检测

 异常检测(AD) 在欺诈检测、网络安全和医疗诊断等关键任务应用中至关重要。由于数据的高维性和底层模式的复杂性,图像、视频和卫星图像等视觉数据中的异常检测尤其具有挑战性。然而,视觉异常检测对于检测制造中的缺陷、识别监控录像中的可疑活动以及检测医学图像中的异常至关重要。

    在本文中,您将学习如何使用OpenVINO 工具包中的FiftyOne和Anomalib对视觉数据执行异常检测。为了演示,我们将使用MVTec AD 数据集,其中包含具有划痕、凹痕和孔洞等异常的各种物体的图像。

    它涵盖以下内容:

    • 在 FiftyOne 中加载 MVTec AD 数据集

    • 使用 Anomalib 训练异常检测模型

    • 评估 FiftyOne 中的异常检测模型

安装依赖项

    确保你在虚拟环境中运行它python=3.10。Anomalib 需要 Python 3.10,因此请确保你安装了正确的版本。

conda create -n anomalib_env python=3.10conda activate anomalib_env

    此后,按照Anomalib README中的说明从源代码安装 Anomalib及其依赖项。这些在 Google Colab 上可能需要一些时间,但对于本地安装应该很快:

pip install -U torchvision einops FrEIA timm open_clip_torch imgaug lightning kornia openvino git+https://github.com/openvinotoolkit/anomalib.git

    我们准备安装更多软件包。现在您可以明白为什么我们建议为该项目使用虚拟环境!

    • huggingface_hub用于加载 MVTec AD 数据集

    • clip用于计算图像嵌入

    • umap-learn用于降维

pip install -U huggingface_hub umap-learn git+https://github.com/openai/CLIP.git

加载和可视化 MVTec AD 数据集

    现在,让我们从FiftyOne导入我们需要的所有相关模块:

import fiftyone as fo # 基础库和应用程序import fiftyone.brain as fob # ML 方法import fiftyone.zoo as foz # zoo 数据集和模型from fiftyone import ViewField as F # 定义视图的助手import fiftyone.utils.huggingface as fouh # Hugging Face 集成

    并从 Hugging Face Hub 加载 MVTec AD 数据集:

dataset = fouh.load_from_hub("Voxel51/mvtec-ad", persistent=True, overwrite=True)

    在继续之前,让我们看一下FiftyOne 应用程序中的数据集:

session = fo.launch_app(dataset)

 该数据集包含 12 个对象类别的 5354 张图像。每个类别都有“良好”和“异常”图像,这些图像存在划痕、凹痕和孔洞等缺陷。每个异常样本还带有一个掩模,用于定位图像中的缺陷区域。

    缺陷标签因类别而异,这在现实世界的异常检测场景中很常见。在这些场景中,您会为每个类别训练不同的模型。在这里,我们将介绍一个类别的流程,您可以将相同的步骤应用于其他类别。

    还有一点需要注意的是,数据集被分为训练集和测试集。训练集只包含“良好”图像,而测试集则包含“良好”和“异常”图像。

    在训练模型之前,让我们深入研究数据集。通过计算图像嵌入并在低维空间中可视化它们,我们可以了解数据中隐藏的结构和模式。首先,我们将使用 CLIP 模型计算数据集中所有图像的嵌入:

model = foz.load_zoo_model( "clip-vit-base32-torch" )   # 从 zoo 加载 CLIP 模型

# 计算数据集的嵌入
dataset.compute_embeddings( 
    model=model, embeddings_field= "clip_embeddings" , batch_size= 64
 ) 

# 使用 UMAP 对嵌入进行降维
fob.compute_visualization( 
    dataset, embeddings= "clip_embeddings" , method= "umap" , brain_key= "clip_vis"
 )

    刷新 FiftyOne 应用程序,单击“+”选项卡,然后选择“Embeddings”。从下拉菜单中选择“all_clip_vis”。您将看到 2D 空间中图像嵌入的散点图,其中每个点对应于数据集中的一个样本。

 使用颜色下拉菜单,注意嵌入如何根据对象类别进行聚类。这是因为 CLIP 对图像的语义信息进行编码。此外,CLIP 嵌入不会根据缺陷类型在类别内进行聚类。

训练异常检测模型

    现在我们对数据集有了了解,我们准备使用 Anomalib 训练异常检测模型。

    任务:Anomalib 支持图像的分类、检测和分割任务。我们将重点关注分割,其中模型预测图像中的每个像素是否异常,并创建一个定位缺陷的掩码。

    模型:Anomalib 支持多种异常检测算法。在本演练中,我们将使用两种算法:

    • PaDiM:用于异常检测和定位的补丁分布建模框架

    • PatchCore:迈向工业异常检测的全面召回

    预处理:在训练模型之前,我们将在本演练中将图像大小调整为 256x256 像素。通过 Torchvision 的 Resize 类将其添加为转换,我们可以在训练和推理过程中动态调整图像大小。

    从Anomalib 和辅助模块导入处理图像和路径所需的模块:


import numpy as np
import os
from pathlib import Path
from PIL import Image
from torchvision.transforms.v2 import Resize

from anomalib import TaskType
from anomalib.data.image.folder import Folder
from anomalib.deploy import ExportType, OpenVINOInferencer
from anomalib.engine import Engine
from anomalib.models import Padim, Patchcore

现在,定义一些在整个笔记本中使用的常量。

    • OBJECT:我们将重点关注的对象类别。在本演练中,我们将使用“瓶子”。如果您想要循环遍历类别,可以使用 dataset.distinct("category.label") 从数据集中获取类别列表。

    • ROOT_DIR:Anomalib 将在其中查找图像和掩码的根目录。我们的数据已存储在磁盘上,因此我们只需将文件符号链接到 Anomalib 所需的目录即可。

    • TASK:我们正在执行的任务。我们将在本演练中使用“分段”。

    • IMAGE_SIZE:在训练模型之前调整图像的大小。我们将使用 256x 256 像素。

OBJECT = "bottle"  ## 要训练的对象ROOT_DIR = Path( "/tmp/mvtec_ad" ) ## 用于存储 anomalib 数据的根目录TASK = TaskType.SEGMENTATION ## 模型的任务类型IMAGE_SIZE = ( 256 , 256 ) ## 预处理图像大小以保证均匀性

    对于给定的对象类型(类别),create_datamodule()下面的函数会创建一个 AnomalibDataModule对象。这将被传递到我们引擎的fit()方法来训练模型,并用于实例化数据加载器以进行训练和验证。

    代码可能看起来很复杂,所以让我们分解一下发生了什么:

    • 我们创建的数据子集仅包含“良好”的训练图像和“异常”图像以供验证。

    • 我们将图像和掩码符号链接到 Anomalib 期望的目录。

    • 我们从 Anomalib 实例化并设置一个数据模块Folder,它是自定义数据集的通用类。

    💡 也可以DataLoader从头开始创建一个 torch 并将其传递给引擎的fit()方法。这可以让你更好地控制数据加载过程。这留给读者练习 😉。


def create_datamodule(object_type, transform=None):
    ## Build transform
    if transform is None:
        transform = Resize(IMAGE_SIZE, antialias=True)

    normal_data = dataset.match(F("category.label") == object_type).match(
        F("split") == "train"
    )
    abnormal_data = (
        dataset.match(F("category.label") == object_type)
        .match(F("split") == "test")
        .match(F("defect.label") != "good")
    )

    normal_dir = Path(ROOT_DIR) / object_type / "normal"
    abnormal_dir = ROOT_DIR / object_type / "abnormal"
    mask_dir = ROOT_DIR / object_type / "mask"

    # create directories if they do not exist
    os.makedirs(normal_dir, exist_ok=True)
    os.makedirs(abnormal_dir, exist_ok=True)
    os.makedirs(mask_dir, exist_ok=True)

    if not os.path.exists(str(normal_dir)):
        normal_data.export(
            export_dir=str(normal_dir),
            dataset_type=fo.types.ImageDirectory,
            export_media="symlink",
        )

    for sample in abnormal_data.iter_samples():
        base_filename = sample.filename
        dir_name = os.path.dirname(sample.filepath).split("/")[-1]
        new_filename = f"{dir_name}_{base_filename}"
        if not os.path.exists(str(abnormal_dir / new_filename)):
            ## symlink anomalous image into Anomalib abnormal dir
            os.symlink(sample.filepath, str(abnormal_dir / new_filename))


        if not os.path.exists(str(mask_dir / new_filename)):
            ## symlink mask into Anomalib mask dir
            os.symlink(sample.defect_mask.mask_path, str(mask_dir / new_filename))


    ## Create a DataModule in Anomalib
    datamodule = Folder(
        name=object_type,
        root=ROOT_DIR,
        normal_dir=normal_dir,
        abnormal_dir=abnormal_dir,
        mask_dir=mask_dir,
        task=TASK,
        transform=transform
    )
    datamodule.setup()
    return datamodule

 现在,我们可以将所有内容整合在一起。train_and_export_model()下面的函数使用 Anomalib 的类训练异常检测模型Engine,将模型导出到 OpenVINO,并返回模型“推理器”对象。推理器对象用于对新图像进行预测。


def  train_and_export_model ( object_type, model, transform= None ): 
    ## 在我们的数据上训练模型
    datamodule = create_datamodule(object_type, transform=transform) 
    engine = Engine(task=TASK) 
    engine.fit(model=model, datamodule=datamodule) 

    ## 将模型导出为 OpenVINO 格式以进行快速推理
    engine.export( 
        model=model, 
        export_type=ExportType.OPENVINO, 
    ) 
    output_path = Path(engine.trainer.default_root_dir) 

    openvino_model_path = output_path / "weights" / "openvino" / "model.bin"
     metadata = output_path / "weights" / "openvino" / "metadata.json" 


    ## 从导出加载推理对象 inferencer
     = OpenVINOInferencer( 
        path=openvino_model_path, 
        metadata=metadata, 
        device= "CPU" , 
    ) 
    return inferencer

 我们先尝试PaDiM一下。训练过程应该不到一分钟:

model = Padim()

inferencer = train_and_export_model(OBJECT, model)

    就这样,我们就有了一个针对“瓶子”类别进行训练的异常检测模型。让我们在单个图像上运行推理器并检查结果:

## get the test split of the dataset
test_split = dataset.match(F("category.label") == OBJECT).match(F("split") == "test")

## get the first sample from the test split
test_image = Image.open(test_split.first().filepath)

output = inferencer.predict(image=test_image)
print(output)



ImageResult(image=[[[255 255 255]
  [255 255 255]
  [255 255 255]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]

  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]], pred_score=0.7751642969087686, pred_label=1, anomaly_map=[[0.32784402 0.32784402 0.32784414 ... 0.3314721  0.33147204 0.33147204]
 [0.32784402 0.32784402 0.32784414 ... 0.3314721  0.33147204 0.33147204]
 [0.32784408 0.32784408 0.3278442  ... 0.33147222 0.33147216 0.33147216]
 ...
 [0.32959    0.32959    0.32959005 ... 0.3336093  0.3336093  0.3336093 ]
 [0.3295899  0.3295899  0.32958996 ... 0.33360928 0.33360928 0.33360928]
 [0.3295899  0.3295899  0.32958996 ... 0.33360928 0.33360928 0.33360928]], gt_mask=None, gt_boxes=None, pred_boxes=None, box_labels=None, pred_mask=[[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]], heat_map=[[[153 235 255]
  [153 235 255]
  [153 235 255]
  ...
  [153 236 255]
  [153 236 255]
  [153 236 255]]
  ...
  [153 238 255]
  [153 238 255]
  [153 238 255]]], segmentations=[[[255 255 255]
  [255 255 255]
  [255 255 255]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]])

    输出包含一个标量异常分数pred_score、一个pred_mask表示预测异常区域的和一个显示每个像素异常分数的热图 anomaly_map。这些都是理解模型预测的宝贵信息。

    下面的函数run_inference()将以 FiftyOne 样本集合(例如我们的测试集)作为输入,以及推理器对象和用于将结果存储在样本中的键。它将对集合中的每个样本运行模型并存储结果。阈值参数充当异常分数的截止值。如果分数高于阈值,则样本被视为异常。在此示例中,我们将使用 0.5 的阈值,但您可以尝试使用不同的值。


def run_inference(sample_collection, inferencer, key, threshold=0.5):
    for sample in sample_collection.iter_samples(autosave=True, progress=True):
        output = inferencer.predict(image=Image.open(sample.filepath))

        conf = output.pred_score
        anomaly = "normal" if conf < threshold else "anomaly"

        sample[f"pred_anomaly_score_{key}"] = conf
        sample[f"pred_anomaly_{key}"] = fo.Classification(label=anomaly)
        sample[f"pred_anomaly_map_{key}"] = fo.Heatmap(map=output.anomaly_map)
        sample[f"pred_defect_mask_{key}"] = fo.Segmentation(mask=output.pred_mask)

 让我们对测试分割进行推理,并在 FiftyOne 应用程序中可视化结果:

run_inference(test_split, inferencer, "padim")session = fo.launch_app(view=test_split)

评估异常检测模型

    我们有一个异常检测模型,但我们如何知道它是否好用?首先,我们可以使用精度、召回率和 F1 分数指标来评估模型。FiftyOne 的评估 API使这变得简单。我们将评估模型的全图像分类性能以及分割性能。

    我们需要准备评估数据。首先,我们需要为“正常”图像添加空掩码,以确保评估公平:​​​​​​​

for sample in test_split.iter_samples(autosave=True, progress=True):    if sample["defect"].label == "good":        sample["defect_mask"] = fo.Segmentation(            mask=np.zeros_like(sample["pred_defect_mask_padim"].mask)        )

    我们还需要确保真实值和预测值之间的命名/标签的一致性。我们将所有“良好”图像重命名为“正常”,并将每种类型的异常重命名为“异常”:​​​​​​​

old_labels = test_split.distinct("defect.label")label_map = {label:"anomaly" for label in old_labels if label != "good"}label_map["good"] = "normal"mapped_view = test_split.map_labels("defect", label_map)session.view = mapped_view.view()

  对于分类,我们将使用二元评估,其中“正常”为负类,“异常”为正类:

eval_classif_padim = mapped_view.evaluate_classifications(
    "pred_anomaly_padim",
    gt_field="defect",
    eval_key="eval_classif_padim",
    method="binary",
    classes=["normal", "anomaly"],
)



eval_classif_padim.print_report()
               precision    recall  f1-score   support

      normal       0.95      0.90      0.92        20
     anomaly       0.97      0.98      0.98        63

    accuracy                           0.96        83
   macro avg       0.96      0.94      0.95        83
weighted avg       0.96      0.96      0.96        83

比较异常检测模型

    虽然异常检测是无监督的,但这并不意味着我们不能比较模型并选择最适合我们用例的模型。我们可以在同一数据上训练多个模型,并使用 F1 分数、准确率和召回率等指标比较它们的性能。我们还可以通过检查它们生成的掩码和热图来直观地比较模型。

    我们重复一下PatchCore模型的训练过程,并比较一下这两个模型:


## 训练 Patchcore 模型并运行推理

model = Patchcore() 

## 这将需要更长的时间来训练,但仍应少于 5 分钟
inferencer = train_and_export_model(OBJECT, model) 

run_inference(mapped_view, inferencer, "patchcore" ) 

## 在分类任务上评估 Patchcore 模型
eval_classif_patchcore = tagged_view.evaluate_classifications( 
    "pred_anomaly_patchcore" , 
    gt_field= "defect" , 
    eval_key= "eval_classif_patchcore" , 
    method= "binary" , 
    classes=[ "normal" , "anomaly" ], 
) 



eval_classif_patchcore.print_report()
              precision    recall  f1-score   support

      normal       0.95      1.00      0.98        20
     anomaly       1.00      0.98      0.99        63

    accuracy                           0.99        83
   macro avg       0.98      0.99      0.98        83
weighted avg       0.99      0.99      0.99        83
eval_seg_patchcore = mapped_view.match(F("defect.label") == "anomaly").evaluate_segmentations(
    "pred_defect_mask_patchcore",
    gt_field="defect_mask",
    eval_key="eval_seg_patchcore",
)



eval_seg_patchcore.print_report(classes=[0, 255])
session.view = mapped_view.shuffle().view()
      precision    recall  f1-score   support

           0       0.99      0.95      0.97 47143269.0
         255       0.60      0.85      0.70 3886731.0

   micro avg       0.95      0.95      0.95 51030000.0
   macro avg       0.80      0.90      0.84 51030000.0
weighted avg       0.96      0.95      0.95 51030000.0

   这些指标支持了我们在应用程序中看到的结果:PatchCore 对“异常”类别的召回率更高,但准确率较低。这意味着它更有可能发现异常,但也更有可能做出误报预测。毕竟,PatchCore 是为工业异常检测中的“全面召回”而设计的。  Taobao 天皓智联  whaosoft aiot http://143ai.com

 

  通过查看热图,我们还可以看到每个模型更擅长检测哪些类型的异常。两个模型的集合可能对不同类型的异常更具鲁棒性。

下一步是什么

    在本演练中,我们学习了如何使用 FiftyOne 和 Anomalib 对视觉数据执行异常检测。虽然我们训练了两个模型,但我们只触及了视觉异常检测的皮毛。

    如果你想提高性能,还有许多其他方法可以改变:

    • 算法:我们仅使用了 PaDiM 和 PatchCore。Anomalib 目前支持 13 种算法!

    • Backbone:用于特征提取的模型架构

    • 超参数:异常检测算法特有的参数。对于 PatchCore,这包括coreset_sampling_ratio和num_neighbors。

    • 数据增强:人为增加训练集的大小并提高模型泛化的技术。

    • 定制解决方案:引入新算法/技术永远不会太晚!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值