OpenCV
Halide

HalideにおけるバッファHalide::Buffer<T>の操作(HalideとOpenCVの相互変換)

はじめに

Halideに関するリンクは以下の記事にまとめてあります.
Halideによる画像処理まとめ

メンバ変数へのアクセス

画像サイズやチャネル数,先頭ポインタの取得などのネイティブなC/C++コードとデータをやり取りするために必要なメンバ変数の情報取得は以下通り.

//Halideのデータ生成用テスト
Func grad;
grad(x, y, c) = cast<uint8_t>((x + y) % 256);
Halide::Buffer<uint8_t> output = realize(256,256);
//ここまで

output.width();//横サイズ
output.height();//縦サイズ
output.channels()//チャネル数
output.dimensions()//次元数,テストしたところチャネル数と一緒だと思う.
output2.size_in_bytes()//バイト数(縦x横xチャネルxsizeof(T)).

uchar* data = output2.begin();//RAWデータの先頭ポインタ
uchar* data = output2.data();//RAWデータの先頭ポインタ.テストしたところ上と同じ.
uchar* data = output2.end();//RAWデータの最後尾のポインタ

その他参考ページ

色々な初期化とデータのやり取り

Halide::Buffer<uint8_t>の代表的な初期化の方法は以下の通り

  • 確保だけ Buffer<uint8_t> input(width, height, channel);
  • 計算時に確保 Buffer<uint8_t> output = grad.realize(width, height, channel);
  • 先頭ポインタを指定(uchar* dataが先頭ポインタ) Buffer<uint8_t> input(data, width, height, channel);

以下,OpenCVとHalideのデータをやり取りするためのサンプルコード.
Halide処理中で余計なメモリ確保をしないようにするにはTest3とTest5の形になる.
Test6は,ループでコピーしているが,Halideが効率的なコンパイルをしてくれるわけではないので遅い.

void halideInitTest1(Mat& dest)
{
    Var x, y, c;
    Func grad;
    grad(x, y, c) = cast<uint8_t>((x + y) % 256);

    Halide::Buffer<uint8_t> output = grad.realize(dest.cols, dest.rows, dest.channels());

    uchar* data = output.data();
    //uchar* data = output.begin();//こっちでも同じ

    memcpy(dest.data, data, output.size_in_bytes());
    //memcpy(dest.data, data, output.width()*output.height()*output.channels());//こっちでも同じ
    //memcpy(dest.data, data, output.width()*output.height()*output.dimensions());//こっちでも同じ
}

void halideInitTest2(Mat& dest)
{
    Var x, y, c;
    Func grad;
    grad(x, y, c) = cast<uint8_t>((x + y) % 256);
    Halide::Buffer<uint8_t> output(dest.cols, dest.rows, dest.channels());

    grad.realize(output);

    uchar* data = output.data();
    memcpy(dest.data, data, output.size_in_bytes());
}

void halideInitTest3(Mat& dest)
{
    Var x, y, c;
    Func grad;
    grad(x, y, c) = cast<uint8_t>((x + y) % 256);
    //dest.ptr<uchar>(0)はOpenCVの画像データの先頭ポインタ
    Halide::Buffer<uint8_t> output(dest.ptr<uchar>(0), dest.cols, dest.rows, dest.channels());

    grad.realize(output);
}

void halideInitTest4(Mat& src, Mat& dest)
{
    Var x, y, c;
    Halide::Buffer<uint8_t> input(src.ptr<uchar>(0), src.cols, src.rows, src.channels());
    Func adder;
    adder(x, y, c) = cast<uint8_t>(min((cast<int16_t>(input(x, y, c)) + (short)50), 255));

    Halide::Buffer<uint8_t> output = adder.realize(dest.cols, dest.rows, dest.channels());

    uchar* data = output.data();
    memcpy(dest.data, data, output.size_in_bytes());
}

void halideInitTest5(Mat& src, Mat& dest)
{
    Var x, y, c;
    Halide::Buffer<uint8_t> input(src.ptr<uchar>(0), src.cols, src.rows, src.channels());
    Halide::Buffer<uint8_t> output(dest.ptr<uchar>(0), dest.cols, dest.rows, dest.channels());
    Func adder;
    adder(x, y, c) = cast<uint8_t>(min((cast<int16_t>(input(x, y, c)) + (short)50), 255));

    adder.realize(output);
}

void halideInitTest6(Mat& src, Mat& dest)
{
    Var x, y, c;
    Halide::Buffer<uint8_t> input(src.cols, src.rows, src.channels());
    Halide::Buffer<uint8_t> output(dest.cols, dest.rows, dest.channels());
    //srcからコピー
    for (int j = 0; j < src.rows; j++)
    {
        for (int i = 0; i < src.cols; i++)
        {
            for (int n = 0; n < src.channels(); n++)
            {
                input(i, j, n) = src.at<uchar>(j, i*src.channels() + n);
            }
        }
    }

    Func adder;
    adder(x, y, c) = cast<uint8_t>(min((cast<int16_t>(input(x, y, c)) + (short)50), 255));

    adder.realize(output);

    //destへコピー
    for (int j = 0; j < dest.rows; j++)
    {
        for (int i = 0; i < dest.cols; i++)
        {
            for (int n = 0; n < dest.channels(); n++)
            {
                dest.at<uchar>(j, i*dest.channels() + n) = (uchar)output(i, j, n);
            }
        }
    }
}

int main(int argc, char **argv)
{
    Mat src = imread("lenna.png");
    Mat dest = Mat::zeros(src.size(), src.type());

    halideInitTest1(dest);//realizeでデータサイズを指定
    imshow("Halide1", dest);

    halideInitTest2(dest);//事前にbufferのサイズを指定
    imshow("Halide2", dest);

    halideInitTest3(dest);//事前にbufferのサイズと先頭ポインタを指定.メモリ確保の無駄が無い
    imshow("Halide3", dest);

    halideInitTest4(src, dest);//OpenCVのMat中のRAW先頭ポインタを使って初期化して計算結果をmemcpyで返す
    imshow("Halide4", dest);

    halideInitTest5(src, dest);//上記に加えて,計算結果のバッファも初期化で指定
    imshow("Halide5", dest);

    halideInitTest6(src, dest);//何をやっているか直感的にわかるが遅くなる例
    imshow("Halide6", dest);

    waitKey();
    return 0;
}