OpenCVによるHalideプログラミングの開発支援

#OpenCVとの連携
 Halide自体には画像の入出力，画面の表示，簡易GUIなど画像処理プログラミング開発に必要な細かなツールがそろっていません．
 Halide本家のチュートリアルではlibjpegやlibpngをリンクして画像の書き込み・保存を行うことで画像の可視化を行っています．しかしそれでは開発効率が低いです．

 ここでは，OpenCVの開発支援周りの関数をHalideから使えるように簡単にラップしたコードを示します．現在書いてある機能リストは下記になります．

* imwrite (画像書き込み)
* imread (画像読み込み)
* convertHalide2Mat（HalideのバッファをOpenCVのMatに変換）
* convertMat2Halide（上の逆）
* imshow(バッファ表示用．uchar)
* imshow16(バッファ表示用．shortを255諧調で表示．0:255でクリップされるので必要な領域をoffsetとスケールで調整)
* guiAlphaBlend（バッファ2つをアルファブレンドして表示．デバッグ用）



#リンク
Halideに関するリンクは以下の記事にまとめてあります．
[Halideによる画像処理まとめ](https://qiita.com/fukushima1981/items/fa3537234e19baffc761)

```cpp
#include <opencv2/opencv.hpp>
#pragma comment(lib, "opencv_core331.lib")
#pragma comment(lib, "opencv_imgcodecs331.lib")
#pragma comment(lib, "opencv_highgui331.lib")

#include "include/Halide.h"
#pragma comment(lib, "Halide.lib")

using namespace Halide;

//OpenCV for Halide functions

//中でcv::imreadをコール．
Buffer<uint8_t> imread(cv::String name);

//中でcv::imwriteをコール．
void imwrite(cv::String name, const Buffer<uint8_t>& src);

//HalideのbufferからOpenCVのMatへ変換
void convertHalide2Mat(const Buffer<uint8_t>& src, cv::Mat& dest);

//OpenCVのMatからHalideのbufferへ変換
void convertMat2Halide(cv::Mat& src, Buffer<uint8_t>& dest);

//ucharのバッファ表示用
void imshow(cv::String name, const Buffer<uint8_t>& src);

//shortのバッファ表示用．可視化のデータはucharなのでオフセットとスケールで０～２５５へのキャストを調整
void imshow16(cv::String name, const Buffer<int16_t>& src, double offset = 0.0, double scale = 1.0);

//アルファブレンドをして出力を比較デバッグ用関数
void guiAlphaBlend(Buffer<uint8_t>& src1, Buffer<uint8_t>& src2, cv::String name = "alpha blend");


Func copy(Buffer<uint8_t>& src)
{
	Var x("x"), y("y"), c("c");

	Func output("output");
	output(x, y, c) = src(x, y, c);

	return output;
}

int main(int argc, char **argv)
{
	Buffer<uint8_t> input = imread("rgb.png");
	Func output = copy(input);
	Buffer<uint8_t> result = output.realize(input.width(), input.height(), input.channels());

	imshow("Halide", result);
	imwrite("out.png", result);
	cv::waitKey();

	cv::Mat ocv;
	convertHalide2Mat(result, ocv);
	cv::imshow("opencv", ocv);
	cv::waitKey();

	return 0;
}

//utility function with OpenCV
void convertMat2Halide(cv::Mat& src, Buffer<uint8_t>& dest)
{
	const int ch = src.channels();
	if (ch == 1)
	{
		for (int j = 0; j < src.rows; j++)
		{
			for (int i = 0; i < src.cols; i++)
			{
				dest(i, j) = src.at<uchar>(j, i);
			}
		}
	}
	else if (ch == 3)
	{
		for (int j = 0; j < src.rows; j++)
		{
			for (int i = 0; i < src.cols; i++)
			{
				dest(i, j, 0) = src.at<uchar>(j, 3 * i);
				dest(i, j, 1) = src.at<uchar>(j, 3 * i + 1);
				dest(i, j, 2) = src.at<uchar>(j, 3 * i + 2);
			}
		}
	}
}

Buffer<uint8_t> imread(cv::String name)
{
	cv::Mat a = cv::imread(name);
	if (a.empty()) std::cout << name << " is empty" << std::endl;

	Buffer<uint8_t> ret(a.cols, a.rows, a.channels());
	convertMat2Halide(a, ret);

	return ret;
}

void convertHalide2Mat(const Buffer<uint8_t>& src, cv::Mat& dest)
{
	if (dest.empty()) dest.create(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
	const int ch = dest.channels();
	if (ch == 1)
	{
		for (int j = 0; j < dest.rows; j++)
		{
			for (int i = 0; i < dest.cols; i++)
			{
				dest.at<uchar>(j, i) = src(i, j);
			}
		}
	}
	else if (ch == 3)
	{
		for (int j = 0; j < dest.rows; j++)
		{
			for (int i = 0; i < dest.cols; i++)
			{
				dest.at<uchar>(j, 3 * i + 0) = src(i, j, 0);
				dest.at<uchar>(j, 3 * i + 1) = src(i, j, 1);
				dest.at<uchar>(j, 3 * i + 2) = src(i, j, 2);
			}
		}
	}
}

void imwrite(cv::String name, const Buffer<uint8_t>& src)
{
	cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
	convertHalide2Mat(src, a);
	cv::imwrite(name, a);
}

void imshow(cv::String name, const Buffer<uint8_t>& src)
{
	cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
	convertHalide2Mat(src, a);
	cv::imshow(name, a);
}

void imshow16(cv::String name, const Buffer<int16_t>& src, double offset, double scale)
{
	cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));

	const int ch = a.channels();
	if (ch == 1)
	{
		for (int j = 0; j < a.rows; j++)
		{
			for (int i = 0; i < a.cols; i++)
			{
				a.at<uchar>(j, i) = cv::saturate_cast<uchar>(scale*src(i, j) + offset);
			}
		}
	}
	else if (ch == 3)
	{
		for (int j = 0; j < a.rows; j++)
		{
			for (int i = 0; i < a.cols; i++)
			{

				a.at<uchar>(j, 3 * i + 0) = cv::saturate_cast<uchar>(scale*src(i, j, 0) + +offset);
				a.at<uchar>(j, 3 * i + 1) = cv::saturate_cast<uchar>(scale*src(i, j, 1) + +offset);
				a.at<uchar>(j, 3 * i + 2) = cv::saturate_cast<uchar>(scale*src(i, j, 2) + +offset);
			}
		}
	}

	cv::imshow(name, a);
}

void guiAlphaBlend(Buffer<uint8_t>& src1, Buffer<uint8_t>& src2, cv::String name)
{
	cv::Mat s1(cv::Size(src1.width(), src1.height()), CV_MAKETYPE(CV_8U, src1.channels()));
	cv::Mat s2(cv::Size(src1.width(), src1.height()), CV_MAKETYPE(CV_8U, src1.channels()));
	convertHalide2Mat(src1, s1);
	convertHalide2Mat(src2, s2);

	cv::namedWindow(name);
	int a = 0; cv::createTrackbar("alpha", name, &a, 100);
	int key = 0;
	while (key != 'q')
	{
		cv::Mat show;
		cv::addWeighted(s1, 1.0 - a / 100.0, s2, a / 100.0, 0.0, show);
		cv::imshow(name, show);
		key = cv::waitKey(1);
	}
	cv::destroyWindow(name);
}
```