Edited at

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

More than 1 year has passed since last update.


はじめに

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;
}