Halideは画像処理記述用のDSLです。
どういったものかは公式ページをご覧いただくか、福嶋先生がある程度の日本語資料を投稿されていますので、そちらを参照ください。
HalideとOpenCVとの連携
Halideには画像ファイルの読み込み/書き込み関数がありませんので、別途用意する必要があります。
ここではOpenCVのimread
/imwrite
を使ってみましょう。
処理はカラー画像のグレイスケール化にでもしておきましょうか。
#include <Halide.h>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat cv_src = cv::imread("rgb.png");
cv::Mat cv_dst(cv_src.size(), CV_8UC1);
Halide::Buffer<uint8_t> hal_src(cv_src.data, cv_src.cols, cv_src.rows, 3);
Halide::Buffer<uint8_t> hal_dst(cv_dst.data, cv_dst.cols, cv_dst.rows); // 1chなのでx,yだけでOK
Halide::Func GrayScale;
Halide::Var x, y;
Halide::Expr gray =
0.114f * hal_src(x, y, 0) + //B
0.587f * hal_src(x, y, 1) + //G
0.299f * hal_src(x, y, 2); //R
GrayScale(x, y) = Halide::cast<uint8_t>(Halide::min(gray, 255));
GrayScale.realize(hal_dst); // 出力先をhal_dstにして、グレイスケール化の実行
cv::imwrite("gray1.png", cv_dst);
}
Buffer<T>
のコンストラクタ先頭の引数にMatのdata
ポインタを渡すことで、直接Mat画像をHalideで扱うことができます。
ポインタを共用しているので、Buffer
を書き換えればMat
の中身も書き換わります。
HalideとOpenCVのメモリ配置の違い
さて、実際に生成された画像はというと…
なんじゃこりゃ。失敗ですね。
なにが原因かというと、Halideのデフォルトのメモリ配置がプレーナ型を想定しているためです。
プレーナ型とは、以下のようなメモリ配置です。
画像が色チャンネル枚分並んでいる、という形でしょうか。
BBBBBBBBGGGGGGGGRRRRRRRR
一方、OpenCVのMat
はインターリーブ型になっています。
1pixelごとにB,G,Rと並んでいる、素直な形です。
OpenCVを触っている人ならおなじみですね。
BGRBGRBGRBGRBGRBGRBGRBGR
ということで、上記のプログラムはhal_src
の作成時に齟齬が生じてしまっています。
直すのは簡単で、HalideのBuffer
にインターリーブ型であると教えてやれば大丈夫です。
#include <Halide.h>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat cv_src = cv::imread("rgb.png");
cv::Mat cv_dst(cv_src.size(), CV_8UC1);
Halide::Buffer<uint8_t> hal_src =
Halide::Buffer<uint8_t>::make_interleaved(cv_src.data, cv_src.cols, cv_src.rows, 3); // ここでインターリーブ型だと教えてやる
Halide::Buffer<uint8_t> hal_dst(cv_dst.data, cv_dst.cols, cv_dst.rows);
Halide::Func GrayScale;
Halide::Var x, y;
Halide::Expr gray =
0.114f * hal_src(x, y, 0) + //B
0.587f * hal_src(x, y, 1) + //G
0.299f * hal_src(x, y, 2); //R
GrayScale(x, y) = Halide::cast<uint8_t>(Halide::min(gray, 255));
GrayScale.realize(hal_dst); // 出力先をhal_dstにして、グレイスケール化の実行
cv::imwrite("gray2.png", cv_dst);
}