Safie Engineers' Blog!

Safieのエンジニアが書くブログです

GitHub Copilot で 推論モデルの前後処理をPythonからC++に変換する

はじめに

セーフィー株式会社の AI Vision グループでテックリードを務めます橋本貴博です。

セーフィーではネットワークカメラ上で動作するエッジアプリケーションの開発にC++を利用しています。公開されている推論モデルはPythonで実装されていることが多いため、C++への移植が必要です。

この記事では、GitHub Copilot を使って 推論モデルの前後処理をPythonからC++に変換した結果を紹介したいと思います。

アルゴリズム

mmsegmentation リポジトリで公開されている PP-MobileSeg のPython実装された推論コードを利用します。PP-MobileSeg は、2023年4月に発表された、モバイル向けセマンティックセグメンテーションのSOTAモデルです。論文は こちら から読めます。

引用元: [PaddlePaddle/PaddleSeg (GitHub)](https://github.com/PaddlePaddle/PaddleSeg) / Apache License 2.0

引用元: PaddlePaddle/PaddleSeg (GitHub) / Apache License 2.0

実験条件

  • mmsegmentation をcloneし、リポジトリのルートを VSCode の Open Folder から開きます
git clone git@github.com:open-mmlab/mmsegmentation.git
  • 対象とするファイル(projects/pp_mobileseg/inference_onnx.py)のみをエディタで開きます
  • GitHub Copilot Chat の New Session を作成して、過去のチャット履歴がコンテキストに入らないようにしています(実際に利用する際は何往復か対話をしたほうが良い結果が得られると思います)

前処理

推論コード inference_onnx.py の L39 - 45 をC++に変換してみます。入力された画像にリサイズと正規化を適用する一般的な前処理です。

# Resize
resized_img = cv2.resize(img, input_size)

# Normalize
mean = np.array([123.575, 116.28, 103.53], dtype=np.float32)
std = np.array([58.395, 57.12, 57.375], dtype=np.float32)
resized_img = (resized_img - mean) / std

コードを選択しCopilotのコンテキストに含めた状態でChatを使用します。「C++に書き直して」だけだとEigenライブラリも使ってしまったので、OpenCVライブラリだけを使うように指示します。プロンプトと回答は次の通りです。

t-hashimoto-safie: OpenCVだけを使って、C++に書き直して

GitHub Copilot: 以下に、選択されたPythonコードをC++で書き直したものを示します。このコードはOpenCVライブラリを使用しています。

// Include necessary libraries
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>

// ...

// Assuming 'img' is your input cv::Mat image
cv::Mat resized_img;
cv::Size input_size(your_width, your_height); // replace with your dimensions

// Resize
cv::resize(img, resized_img, input_size);

// Normalize
cv::Scalar mean(123.675, 116.28, 103.53);
cv::Scalar std(58.395, 57.12, 57.375);
resized_img = (resized_img - mean) / std;

このコードは、Pythonコードと同じように画像をリサイズし、正規化します。ただし、C++のOpenCVでは、cv::Scalarを使用して平均と標準偏差を定義します。また、リサイズのための新しいサイズはcv::Sizeオブジェクトとして定義されます。

正解です!以下のポイントが押さえられています。

  • OpenCV API は Python と C++ で微妙にインターフェースが異なることを理解している
    • C++ の cv::resize メソッドはリサイズ後の画像の参照渡しする
  • C++で変数の定義に必要な型を推測できている
    • リサイズ後の画像を cv::Mat 型で事前に定義している
    • np.array の代わりに cv::Scalar を使えばよい

後処理

次に、inference_onnx.py の L108-L122 を書き直してもらいます。以下のコードは、ピクセルごとのラベル(int型)を示す seg_map を、ラベルに対応する色で入力画像 img に重畳し、ファイルに保存する関数です。セマンティックセグメンテーションで一般的に行われる可視化処理です。

def visualize(img: np.ndarray,
              seg_map: np.ndarray,
              filename: str = 'output.jpg',
              opacity: float = 0.8) -> np.ndarray:
    assert 0.0 <= opacity <= 1.0, 'opacity should be in range [0, 1]'
    palette = np.array(PALETTE)
    color_seg = np.zeros((seg_map.shape[0], seg_map.shape[1], 3), dtype=np.uint8)
    for label, color in enumerate(palette):
        color_seg[seg_map == label, :] = color
    # convert to BGR
    color_seg = color_seg[..., ::-1]

    img = img * (1 - opacity) + color_seg * opacity
    cv2.imwrite(filename, img)

GitHub Copilot へのプロンプトと回答は以下の通りです。

t-hashimoto-safie: OpenCVだけを使って、C++に書き直して

GitHub Copilot: 以下に、PythonからC++への変換を行ったコードを示します。このコードは、OpenCVライブラリを使用しています。

#include <opencv2/opencv.hpp>
#include <vector>
#include <cassert>

// Assuming PALETTE is a vector of cv::Vec3b (BGR colors)
extern std::vector<cv::Vec3b> PALETTE;

void visualize(cv::Mat& img, cv::Mat& seg_map, 
                           std::string filename = "output.jpg", float opacity = 0.8) {
    assert(0.0 <= opacity && opacity <= 1.0); // opacity should be in range [0, 1]

    cv::Mat color_seg = cv::Mat::zeros(seg_map.size(), CV_8UC3);
    for (int label = 0; label < PALETTE.size(); ++label) {
        color_seg.setTo(PALETTE[label], seg_map == label);
    }

    img = img * (1 - opacity) + color_seg * opacity;
    cv::imwrite(filename, img);
}

このC++コードは、Pythonコードと同じように動作します。各ラベルに対応する色をセグメンテーションマップに適用し、その結果を元の画像に重ねて表示します。その後、結果を指定されたファイル名で保存します。

こちらも正解です!気づいた点としては以下の通りです。

  • 利用する C++ API の理解
    • ラベルと一致するピクセルに色を付ける処理には [Mat::setTo](https://docs.opencv.org/4.x/d3/d63/classcv_1_1Mat.html#a0440e2a164c0b0d8462fb1e487be9876) メソッドを使えばよい。
  • 効率化のための提案
    • もともとの PythonコードRGB画像からBGRに変換をする部分が非効率だった。はじめからBGRのパレット std::vector<cv::Vec3b> PALETTEを用意したほうが良いと提案している。

むすび

GItHub Copilot に「プログラミング言語」から「他のプログラミング言語」に変換するタスクを解かせてみました。APIリファレンスを調べながらコーディングしなくて済むので、作業時間が短縮できたと感じました。なんとなくは覚えているけど、正確な書き方を忘れたといった場合に効果があるのではないでしょうか。

セーフィーの開発部門では GitHub Copilot を導入しています。

© Safie Inc.