C++で書いたコードをPythonで動かすには【pybind11】

4 min read読了の目安(約4300字

はじめに

弊社には過去にC/C++で作った画像処理と機械学習の高速なライブラリがありました。
このライブラリの機能を組み合わせて使うには、C/C++でプログラムを書いてコンパイルする必要があり、ちょっと手軽には使えませんでした。
そこで、このライブラリを最近流行りのPythonで気軽に動かしたいと考え、pybind11というC++とPythonの橋渡しをするライブラリを使うことにしました。

今回は、pybind11の簡単な紹介をしたいと思います。

pybind11について

C++とPythonの連携にはいくつか方法がありますが、pybind11は新しいライブラリで、C++11の機能を使っているので、とてもやりやすいです。BSDライセンスです。

https://github.com/pybind/pybind11

公式のチュートリアルやリファレンスに詳しく書いてありますが、どんなことができるか、よく使う機能を簡単に紹介します。

C++の関数をPythonから実行

例えば、2つの整数の和を返すC++の関数があったとして、Pythonのモジュールにするには以下のように書くだけです。

myadd.cpp
#include <pybind11/pybind11.h>

int add(int x, int y) {
    return x + y;
}

PYBIND11_MODULE(myadd, m) {
    m.def("add", &add);
}

これをビルドしてできたモジュールは、Pythonでimportして使うことができます。

myadd_usage.py
import myadd
result = myadd.add(1, 2)
print(result)   # output 3

かんたんですね!

PythonのコードをC++から実行

逆に、Pythonのコードを直接実行したり、Pythonのモジュールを読み込んでメソッドを実行したりすることもできます。

mymul.py
def mul(x: int, y: int):
    return x * y
mymul_usage.cpp
#include <iostream>
#include <pybind11/embed.h>
namespace py = pybind11;

int main() {
    auto m = py::module::import("mymul");
    auto result = m.attr("mul")(3, 4).cast<int>();
    std::cout << result << std::endl;   // output 12
}

C++で定義したクラスをPythonで使う

C++で定義したクラスもモジュール化して、Pythonでimportして使うこともできます。

mycat.cpp
#include <iostream>
#include <string>
#include <pybind11/pybind11.h>
namespace py = pybind11;

class Cat {
public:
    explicit Cat(const std::string& name) : name_{ name } {}
    void say() const { std::cout << name_ << " said meow." << std::endl; }
private:
    std::string name_;
};

PYBIND11_MODULE(mycat, m) {
    py::class_<Cat>(m, "Cat")
        .def(py::init<const std::string&>())
        .def("say", &Cat::say)
        ;
}
mycat_usage.py
import mycat
cat = mycat.Cat("Shiro")
cat.say()   # output "Shiro said meow."

STLコンテナとPythonのデータ型の相互変換

STLコンテナは、Pythonのデータ型と相互に変換できます。

  • std::vector<>, std::deque<>, std::list<>, std::array<>list
  • std::set<>, std::unordered_set<>set
  • std::map<>, std::unordered_map<>dict
myvec.cpp
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;

std::vector<int> x2(const std::vector<int>& x) {
    std::vector<int> y;
    for (int a : x)
        y.push_back(a * 2);
    return y;
}

PYBIND11_MODULE(myvec, m) {
    m.def("x2", &x2);
}
myvec_usage.py
import myvec
result = myvec.x2([1, 2, 3, 4, 5])
print(result)   # output [2, 4, 6, 8, 10]

Eigen ⇔ NumPy

Eigen(C++の線形代数ライブラリ)の行列とNumPy(Pythonの数値演算ライブラリ)の行列も相互に変換できます。

  • Eigen::Matrix<>numpy.ndarray
mymat.cpp
#include <Eigen/Dense>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
namespace py = pybind11;

Eigen::MatrixXd inverse(const Eigen::MatrixXd& x) {
    return x.inverse();
}

PYBIND11_MODULE(mymat, m) {
    m.def("inverse", &inverse);
}
mymat_usage.py
import numpy as np
import mymat
result = mymat.inverse(np.array([[1., 2.], [3., 4.]]))
print(result)   # output [[-2.   1. ] [ 1.5 -0.5]]

画像処理への応用

過去にC++で作った画像フィルタをPythonで使いたいと思い、pybind11でモジュール化しています。
Python版のOpenCVは画像をNumPyのndarrayで扱うので、このような画像フィルターを用意しておけば、あとはPython上で実行したい処理を書くだけです。

myimage.cpp
#include <Eigen/Dense>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/eigen.h>
namespace py = pybind11;
using Image8bpp =
    Eigen::Matrix<uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;

class ReverseFilter {
public:
    ReverseFilter() {}
    Image8bpp transform(const Image8bpp& x) {
        return Image8bpp::Ones(x.rows(), x.cols()) * 255 - x;
    }
};

PYBIND11_MODULE(myimage, m) {
    py::class_<ReverseFilter>(m, "ReverseFilter")
        .def(py::init<>())
        .def("transform", &ReverseFilter::transform)
	;
}
myimage_usage.py
import numpy as np
import cv2
import myimage
x = cv2.imread('kuro-neko.png', cv2.IMREAD_GRAYSCALE)
f = myimage.ReverseFilter()
y = f.transform(x)
cv2.imwrite('shiro-neko.png', y)

さいごに

今回は、C++とPythonの橋渡しをするpybind11について説明しました。
Pythonだけでピクセル単位の画像処理をするととても重いので、そういうときはC++で高速に動作するプログラムをつくって、pybind11でモジュール化すると良いですね。

メンバー募集中です
アダコテックは上記のような画像処理技術を使って、大手メーカーの検査ラインを自動化するソフトウェアを開発している会社です。
機械学習や画像処理の内部ロジックに興味がある方、ご連絡下さい!
我々と一緒にモノづくりに革新を起こしましょう!

https://adacotech.co.jp/recruit