Halideによるポイントオペレータの実装

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

本記事は，その中でもポイントオペレータの入門用です．

**備考**
サンプルコード中，[OpenCVによるHalideプログラミングの開発支援](https://qiita.com/fukushima1981/items/61e226f0c7987f9aa970)で書いたOpenCVのコードを使って可視化していますが，Halideで書いたプログラミングに必須なものではありません．

#ポイントオペレータの書き方と条件分岐
##実装した関数
以下の関数を実装した．


* 加算とその駄目な例
    * ucharの加算は255を超えると一周するのでshortでキャストすること．
    * saturating_castでクリップするもしくはclampで値をクリップすること．
    * 飽和演算（_mm_adds_epu8など）ができればucharのままでもいいが，やり方がわからない．
* 乗算とその駄目な例
    * ucharの乗算は命令にない＆255を超えると一周するのでshortでキャストすること．
    * 負の乗算は正数を対象にしたため対象外のため，clampではなくminでよい．
    * もしくはsaturating_castでも可．
* 浮動小数点の乗算
    * saturating_castでも可．
* 閾値処理とその駄目な例
     * 定数値を必ずucharでキャストすること．関数型は型が大事．
     * if文ではなくてselect文で条件分岐させる．excelのif関数っぽい使い方．
* 色変換（RGB2GrayとRGB2YUV）
    * （YUV変換は，これが最適なコードじゃない気がする）


```cpp
#include <opencv2/opencv.hpp>
#pragma comment(lib, "opencv_core320.lib")
#pragma comment(lib, "opencv_imgproc320.lib")
#pragma comment(lib, "opencv_imgcodecs320.lib")
#pragma comment(lib, "opencv_highgui320.lib")

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

using namespace Halide;

//中でcv::imreadをコール．
Buffer<uint8_t> load_image(cv::String name, int flags = 1);
//中でcv::imwriteをコール．
void save_image(const Buffer<uint8_t>& src, cv::String name);

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

Func add_bad(Buffer<uint8_t>& src, int add)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = min(255, src(x, y, c) + add);
	return output;
}

Func add(Buffer<uint8_t>& src, int add)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = cast<uint8_t>(clamp(cast<int16_t>(src(x, y, c)) + (short)add, 0, 255));
	return output;
}

Func sub(Buffer<uint8_t>& src, int add)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = saturating_cast<uint8_t>(cast<int16_t>(src(x, y, c)) - (short)add);
	return output;
}

Func mul_bad(Buffer<uint8_t>& src, int scale)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = min(255, scale*src(x, y, c));
	return output;
}

Func mul(Buffer<uint8_t>& src, int scale)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = cast<uint8_t>(min(255, scale*cast<uint16_t>(src(x, y, c))));
	return output;
}

Func mul(Buffer<uint8_t>& src, float scale)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = cast<uint8_t>(clamp(scale*cast<float>(src(x, y, c)), 0, 255));
	return output;
}

Func threshold_bad(Buffer<uint8_t>& src, int thresh)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = (select(src(x, y, c) >= thresh, 255, 0));
	return output;
}

Func threshold(Buffer<uint8_t>& src, int thresh)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	output(x, y, c) = (select(src(x, y, c) >= thresh, cast<uint8_t>(255), cast<uint8_t>(0)));
	return output;
}

Func rgbgray(Buffer<uint8_t>& src)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	Func src32f("src32f");
	src32f(x, y, c) = cast<float>(src(x, y, c));
	output(x, y, c) = cast<uint8_t>(0.299f*src32f(x, y, 0) + 0.587f*src32f(x, y, 1) + 0.114f*src32f(x, y, 2));
	return output;
}

Func rgbyuv(Buffer<uint8_t>& src)
{
	Var x("x"), y("y"), c("c");
	Func output("output");
	Func src32f("src32f");
	src32f(x, y, c) = cast<float>(src(x, y, c));
	output(x, y, c) = cast<uint8_t>(select(c == 0, 0.299f*src32f(x, y, 0) + 0.587f*src32f(x, y, 1) + 0.114f*src32f(x, y, 2),
		c == 1, 0.615f*src32f(x, y, 0) - 0.51499f*src32f(x, y, 1) - 0.10001f*src32f(x, y, 2) + 128.f,
		/*else*/       -0.14713f*src32f(x, y, 0) - 0.28886f*src32f(x, y, 1) + 0.436f*src32f(x, y, 2) + 128.f));
	return output;
}

int main(int argc, char **argv)
{
	Buffer<uint8_t> input = load_image("rgb.png");
	
	Func output0 = add_bad(input, 50);
	Buffer<uint8_t> result0 = output0.realize(input.width(), input.height(), 3);
	imshow("add_bad", result0);

	Func output1 = add(input, 50);
	Buffer<uint8_t> result1;
	int64 start = cv::getTickCount();
	result1 = output1.realize(input.width(), input.height(), 3);
	imshow("add", result1);

	output1 = sub(input, 50);
	result1;
	result1 = output1.realize(input.width(), input.height(), 3);
	imshow("sub", result1);

	Func output2 = mul_bad(input, 2);
	Buffer<uint8_t> result2 = output2.realize(input.width(), input.height(), 3);
	imshow("mul_bad", result2);

	Func output3 = mul(input, 2);
	Buffer<uint8_t> result3 = output3.realize(input.width(), input.height(), 3);
	imshow("mul", result3);
	
	Func output4 = mul(input, 1.5f);
	Buffer<uint8_t> result4 = output4.realize(input.width(), input.height(), 3);
	imshow("mul_32f", result4);
	
	Func output5 = threshold_bad(input, 100);
	Buffer<uint8_t> result5 = output5.realize(input.width(), input.height(), 3);
	imshow("threshold_bad", result5);

	Func output6 = threshold(input, 100);
	Buffer<uint8_t> result6 = output6.realize(input.width(), input.height(), 3);
	imshow("threshold", result6);

	Func output7 = rgbgray(input);
	Buffer<uint8_t> result7 = output7.realize(input.width(), input.height(), 3);
	imshow("rgbgray", result7);

	Func output8 = rgbyuv(input);
	Buffer<uint8_t> result8 = output8.realize(input.width(), input.height(), 3);
	imshow("rgbyuv", result8);

	cv::Mat s = cv::imread("rgb.png");
	cv::cvtColor(s, s, cv::COLOR_BGR2YUV);
	imshow("opencvyuv", s);
	
	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> load_image(cv::String name, int flags)
{
	cv::Mat a = cv::imread(name, flags);
	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 save_image(const Buffer<uint8_t>& src, cv::String name)
{
	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, int offset)
{
	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>(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>(src(i, j, 0) + +offset);
				a.at<uchar>(j, 3 * i + 1) = cv::saturate_cast<uchar>(src(i, j, 1) + +offset);
				a.at<uchar>(j, 3 * i + 2) = cv::saturate_cast<uchar>(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);
}
```