Hello Reshape SSD サンプル#

このサンプルでは、形状推論機能を使用してオブジェクト検出モデルの同期推論を行う方法を示します。サンプルを使用する前に、次の要件を参照してください:

  • 入力と出力が 1 つだけのモデルがサポートされます。

  • このサンプルは、core.read_model でサポートされるすべてのファイル形式を受け入れます。

  • サンプルは、person-detection-retail-0013 モデル、および NCHW レイアウト形式で検証されています。

  • サンプルをビルドするには、 “サンプルの導入” ガイドのサンプル・アプリケーションのビルドセクションにある手順を参照してください。

どのように動作するか#

起動時に、サンプル・アプリケーションはコマンドライン・パラメーターを読み取り、入力データを準備し、指定されたモデルとイメージを OpenVINO™ ランタイムプラグインにロードし、同期推論を実行して、出力データを処理します。その結果、プログラムは出力イメージを作成し、各ステップを標準出力ストリームに記録します。

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
# Copyright (C) 2018-2024 Intel Corporation 
# SPDX-License-Identifier: Apache-2.0 
import logging as log
import os
import sys
 
import cv2 
import numpy as np 
import openvino as ov 

def main(): 
    log.basicConfig(format='[ %(levelname)s ] %(message)s', level=log.INFO, stream=sys.stdout) 

    # 入力引数の解析と検証 
    if len(sys.argv) != 4: 
        log.info(f'Usage: {sys.argv[0]} <path_to_model> <path_to_image> <device_name>') 
        return 1 

    model_path = sys.argv[1] 
    image_path = sys.argv[2] 
    device_name = sys.argv[3] 

# --------------------------- ステップ 1. OpenVINO ランタイムコアを初期化 ------------------------------------------------ 
    log.info('Creating OpenVINO Runtime Core') 
    core = ov.Core() 

# --------------------------- ステップ 2. モデルを読み込み -------------------------------------------------------------------- 
    log.info(f'Reading the model: {model_path}') 
    # (.xml と .bin files) または (.onnx file) 
    model = core.read_model(model_path) 

    if len(model.inputs) != 1: 
        log.error('Sample supports only single input topologies') 
        return -1 

    if len(model.outputs) != 1: 
        log.error('Sample supports only single output topologies') 
        return -1 

# --------------------------- ステップ 3. 入力をセットアップ -------------------------------------------------------------------- 
    # 入力画像の読み取り 
    image = cv2.imread(image_path) 
    # N 次元を追加 
    input_tensor = np.expand_dims(image, 0) 

    log.info('Reshaping the model to the height and width of the input image') 
    n, h, w, c = input_tensor.shape model.reshape({model.input().get_any_name(): ov.PartialShape((n, c, h, w))}) 

# --------------------------- ステップ 4. 前処理を適用 ------------------------------------------------------------- 
    ppp = ov.preprocess.PrePostProcessor(model) 

    # 1) 入力テンソル情報を設定:
    # - input() は単一のモデル入力に関する情報を提供 
    # - テンソルの精度は 'u8’ 
    # - データのレイアウトは 'NHWC’ 
    ppp.input().tensor() \ 
        .set_element_type(ov.Type.u8) \ 
        .set_layout(ov.Layout('NHWC')) # noqa: N400 

    # 2) モデルの入力に 'NCHW' レイアウトがあると仮定 
    ppp.input().model().set_layout(ov.Layout('NCHW')) 

    # 3) 出力テンソル情報を設定: # - テンソルの精度は 'f32’ 
    ppp.output().tensor().set_element_type(ov.Type.f32)

    # 4) 元の 'model’ を変更する前処理を適用 
    model = ppp.build() 

# --------------------------- ステップ 4. モデルをデバイスにロード ----------------------------------------------------- 
    log.info('Loading the model to the plugin') 
    compiled_model = core.compile_model(model, device_name) 

# --------------------------- ステップ 5. 推論リクエストを作成して推論を同期的に実行 ----------------------------- 
    log.info('Starting inference in synchronous mode') 
    results = compiled_model.infer_new_request({0: input_tensor}) 

# --------------------------- ステップ 6. 出力を処理 --------------------------------------------------------------------     
    predictions = next(iter(results.values())) 

    # 結果 ([1, 1, N, 7]) を持つ numpy.ndarray の形状を変更して、別の形状 ([N, 7]) を取得します。 
    # ここで、N は検出された境界ボックスの数です。 
    detections = predictions.reshape(-1, 7) 

    for detection in detections: 
        confidence = detection[2] 

        if confidence > 0.5: 
            class_id = int(detection[1]) 

            xmin = int(detection[3] * w) 
            ymin = int(detection[4] * h) 
            xmax = int(detection[5] * w) 
            ymax = int(detection[6] * h) 

            log.info(f'Found: class_id = {class_id}, confidence = {confidence:.2f}, ' f'coords = ({xmin}, {ymin}), ({xmax}, {ymax})') 

            # 出力画像に境界ボックスを描画 
            cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) 

    cv2.imwrite('out.bmp', image) 

    if os.path.exists('out.bmp'): 
        log.info('Image out.bmp was created!') 
    else: 
        log.error('Image out.bmp was not created.Check your permissions.')# ---------------------------------------------------------------------------------------------------------------------- 
    log.info('This sample is an API example, for any performance measurements please use the dedicated benchmark_app tool\n') 
    return 0 

if __name__ == '__main__': 
    sys.exit(main())
// Copyright (C) 2018-2024 Intel Corporation 
// SPDX-License-Identifier: Apache-2.0 
// 

#include <memory> 
#include <string> 
#include <vector> 

// 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 

// 境界ボックスに使用される線の太さ (ピクセル単位) 
constexpr int BBOX_THICKNESS = 2; 

using namespace ov::preprocess; 

int main(int argc, char* argv[]) { 
    try { 
        // -------- OpenVINO ランタイムのバージョンを取得 ----------------------------- 
        slog::info << ov::get_openvino_version() << slog::endl; 

        // --------------------------- 入力引数の解析と検証 
        if (argc != 4) { 
            std::cout << "Usage : " << argv[0] << " <path_to_model> <path_to_image> <device>" << 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]}; 
        // ------------------------------------------------------------------- 

        // ステップ 1. OpenVINO ランタイムコアを初期化 
        ov::Core core; 
        // ------------------------------------------------------------------- 

        // ステップ 2. モデルの読み取り  
        slog::info << "Loading model files: " << model_path << slog::endl; 
        std::shared_ptr<ov::Model> model = core.read_model(model_path); 
        printInputAndOutputsInfo(*model); 

        // ステップ 3. モデルの入力と出力を検証 
        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 には、出力フィルタリングを簡素化する追加の後処理 DetectionOutput レイヤーがあるので、 
        // それを探してみてください。 
        const ov::NodeVector ops = model->get_ops(); 
        const auto it = std::find_if(ops.begin(), ops.end(), [](const std::shared_ptr<ov::Node>& 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"); 
        } 
        // ------------------------------------------------------------------- 
        // ステップ 4. 入力画像を読み取り 
        // 入力画像をサイズ変更せずに読み取る 
        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<unsigned char> image_data = reader->getData(); 
        size_t image_channels = 3; 
        size_t image_width = reader->width(); }
        size_t image_height = reader->height(); 
        // ------------------------------------------------------------------- 

        // ステップ 5. モデルを画像サイズとバッチサイズに合わせて再形成し、 
        // モデルレイアウト 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); 
        // ------------------------------------------------------------------- 
        // ステップ 6. モデルの前処理を構成 
        const ov::Layout tensor_layout{"NHWC"}; 

        // clang-format off 
        ov::preprocess::PrePostProcessor ppp = ov::preprocess::PrePostProcessor(model); 

        // 1) 引数のない input() はモデルに単一の入力があると想定 
        ov::preprocess::InputInfo& input_info = ppp.input(); 
        // 2) 入力テンソル情報を設定: // - テンソルの精度は 'u8’ 
        // - データのレイアウトは 'NHWC’ 
        input_info.tensor(). 
            set_element_type(ov::element::u8). 
            set_layout(tensor_layout); 
        // 3) 明示的な前処理ステップの追加: // - u8 を f32 に変換 
        // - レイアウトを 'NCHW' に変換 (テンソルレイアウトで指定された 'NHWC' から) 
        ppp.input().preprocess(). 
            convert_element_type(ov::element::f32). 
            convert_layout("NCHW"); 
        // 4) モデルの入力に 'NCHW' レイアウトがあると仮定 
        input_info.model().set_layout("NCHW"); 
        // 5) 引数のない output () はモデルの出力が 1 つであると想定 
        ov::preprocess::OutputInfo& output_info = ppp.output(); 
        // 6) 出力要素タイプを FP32 として宣言 
        output_info.tensor().set_element_type(ov::element::f32); 

        // 7) 元の 'model’ を変更する前処理を適用 
        model = ppp.build(); 
        // clang-format on 
        // ------------------------------------------------------------------- 

        // ステップ 7. モデルをデバイスにロード 
        ov::CompiledModel compiled_model = core.compile_model(model, device_name); 
        // ------------------------------------------------------------------- 

        // ステップ 8. 推論要求を作成  
        ov::InferRequest infer_request = compiled_model.create_infer_request(); 

        // ステップ 9. モデルにデータを入力 
        ov::Tensor input_tensor = infer_request.get_input_tensor(); 

        // NHWC データをイメージからテンソルにバッチでコピー 
        unsigned char* image_data_ptr = image_data.get(); 
        unsigned char* tensor_data_ptr = input_tensor.data<unsigned char>(); 
        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]; 
        } 
        // ------------------------------------------------------------------- 

        // ステップ 10. 推論を同期的に行う  
        infer_request.infer(); 

        // ステップ 11. モデルから出力データを取得 
        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<const float>(); 
        // ------------------------------------------------------------------- 

        std::vector<int> boxes; 
        std::vector<int> classes; 

        // ステップ 12. SSD 出力を解析 
        for (size_t object = 0; object < ssd_object_count; object++) { 
            int image_id = static_cast<int>(detections[object * ssd_object_size + 0]); 
            if (image_id < 0) { 
                break; 
            } 

            // 検出、フォーマットあり: [image_id, label, conf, x_min, y_min, x_max, y_max] 
            int label = static_cast<int>(detections[object * ssd_object_size + 1]); 
            float confidence = detections[object * ssd_object_size + 2]; 
            int xmin = static_cast<int>(detections[object * ssd_object_size + 3] * image_width); 
            int ymin = static_cast<int>(detections[object * ssd_object_size + 4] * image_height); 
            int xmax = static_cast<int>(detections[object * ssd_object_size + 5] * image_width); 
            int ymax = static_cast<int>(detections[object * ssd_object_size + 6] * image_height); 

            if (confidence > 0.5f) { 
                // 50% 以上の確率のオブジェクトのみを収集 
                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; 
        } 
    } 

    // 画像に境界ボックスを描画 
    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; 
}

各サンプルの明示的な説明は、“OpenVINO™ ランタイムとアプリケーションの統合” ガイドの統合ステップセクションで確認できます。

実行する#

python hello_reshape_ssd.py <path_to_model> <path_to_image> <device_name>
hello_reshape_ssd <path_to_model> <path_to_image> <device_name>

サンプルを実行するには、モデルとイメージを指定する必要があります:

  • TensorFlow Zoo、HuggingFace、TensorFlow Hub などのモデル・リポジトリーから推論タスクに固有のモデルを取得できます。

  • ストレージで利用可能なメディア・ファイル・コレクションの画像を使用できます。

  • OpenVINO™ ツールキットのサンプルとデモは、デフォルトでは BGR チャンネル順序での入力を想定しています。RGB 順序で動作するようにモデルをトレーニングした場合は、サンプルまたはデモ・アプリケーションでデフォルトのチャンネル順序を手動で再配置するか、reverse_input_channels 引数を指定したモデル・トランスフォーメーション API を使用してモデルを再変換する必要があります。引数の詳細については、前処理計算の埋め込み入力チャンネルを反転するときセクションを参照してください。

  • トレーニングされたモデルでサンプルを実行する前に、モデル・トランスフォーメーション API を使用してモデルが中間表現 (IR) 形式 (*.xml + *.bin) に変換されていることを確認してください。

  • このサンプルは、前処理を必要としない ONNX 形式 (.onnx) のモデルを受け入れます。

#

  1. 事前トレーニングされたモデルをダウンロードします:

  2. 以下を使用して変換できます:

    import openvino as ov 
    
    ov_model = ov.convert_model('./test_data/models/mobilenet-ssd') 
    # または、モデルが Python モデル・オブジェクトの場合 
    ov_model = ov.convert_model(mobilenet-ssd)
    ovc ./test_data/models/mobilenet-ssd
  1. GPU 上のモデルを使用して、画像の推論を実行します。例:

    python hello_reshape_ssd.py ./test_data/models/mobilenet-ssd.xml banana.jpg GPU
    hello_reshape_ssd ./models/person-detection-retail-0013.xml person_detection.bmp GPU

サンプルの出力#

サンプル・アプリケーションは、各ステップを標準出力ストリームに記録し、出力イメージを作成し、50% 以上の信頼度で推論結果の境界ボックスを描画します。

[ INFO ] Creating OpenVINO Runtime Core 
[ INFO ] Reading the model: C:/test_data/models/mobilenet-ssd.xml 
[ INFO ] Reshaping the model to the height and width of the input image 
[ INFO ] Loading the model to the plugin 
[ INFO ] Starting inference in synchronous mode 
[ INFO ] Found: class_id = 52, confidence = 0.98, coords = (21, 98), (276, 210) 
[ INFO ] Image out.bmp was created! 
[ INFO ] This sample is an API example, for any performance measurements please use the dedicated benchmark_app tool

アプリケーションは、検出されたオブジェクトを矩形で囲んだ画像をレンダリングします。検出されたオブジェクトのクラスのリストを、それぞれの信頼値および矩形の座標とともに標準出力ストリームに出力します。

[ INFO ] OpenVINO Runtime version .........<version> 
[ INFO ] Build ...........<build> 
[ INFO ] 
[ INFO ] Loading model files: \models\person-detection-retail-0013.xml 
[ INFO ] model name: ResMobNet_v4 (LReLU) with single SSD head 
[ INFO ]     inputs 
[ INFO ]         input name: data 
[ INFO ]         input type: f32 
[ INFO ]         input shape: {1, 3, 320, 544} 
[ INFO ]     outputs 
[ INFO ]         output name: detection_out 
[ INFO ]         output type: f32 
[ INFO ]         output shape: {1, 1, 200, 7} 
Reshape network to the image size = [960x1699] 
[ INFO ] model name: ResMobNet_v4 (LReLU) with single SSD head 
[ INFO ]     inputs 
[ INFO ]         input name: data 
[ INFO ]         input type: f32 
[ INFO ]         input shape: {1, 3, 960, 1699} 
[ INFO ]     outputs 
[ INFO ]         output name: detection_out 
[ INFO ]         output type: f32 
[ INFO ]         output shape: {1, 1, 200, 7} 
[0,1] element, prob = 0.716309, (852,187)-(983,520) 
The resulting image was saved in the file: hello_reshape_ssd_output.bmp

This sample is an API example, for any performance measurements please use the dedicated benchmark_app tool

関連情報#