【画像処理100本ノックに挑戦】Q.14. 微分フィルタ

# 使用したライブラリ
[【画像処理100本ノック】独自の画像入出力クラスを作る](https://qiita.com/jajagacchi/items/834e8425b84f94df8a49)

# Q.14. 微分フィルタ
> 微分フィルタ(3x3)を実装せよ。

これもカーネル変えるだけですね。時々使っています。注意が必要なのは、勾配を取るので負の値が出てきてしまうことです。私は絶対値を取りました。普段はfloat画像を使っていて負値も許される状況が多いのでひっかかってしまいました。

```c++
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <math.h>

class PPM
{
private:
	int width;
	int height;
	int* R;
	int* G;
	int* B;
public:
	PPM(int width, int height)
	{
		this->width = width;
		this->height = height;
		R = new int[width * height];
		G = new int[width * height];
		B = new int[width * height];
	}
	PPM(const char* filename)
	{
		FILE* fp = fopen(filename, "rb");
		char tmp[256];
		fscanf(fp, "%s\n", tmp);
		if (fgetc(fp) == '#')//コメントありの場合
		{
			fseek(fp, -1, SEEK_CUR);
			while (fgetc(fp) != '\n');//読み飛ばす
		}
		else fseek(fp, -1, SEEK_CUR);
		fscanf(fp, "%d %d\n", &width,&height);
		fscanf(fp, "%s\n", tmp);
		R = new int[width * height];
		G = new int[width * height];
		B = new int[width * height];
		for (int j = 0; j < height; j++)
			for (int i = 0; i < width; i++)
			{
				R[i + width * j] = fgetc(fp);
				G[i + width * j] = fgetc(fp);
				B[i + width * j] = fgetc(fp);
			}
		fclose(fp);
	}
	int Get_width() const
	{
		return width;
	}
	int Get_height() const
	{
		return height;
	}

	int &operator()(int i, int j, char rgb)
	{
		if(rgb=='r') return R[i+width*j];
		if (rgb == 'g') return G[i + width * j];
		if (rgb == 'b') return B[i + width * j];
	}
	void Flush(const char *filename)
	{
		FILE* fp = fopen(filename, "wb");
		fprintf(fp, "P6\n");
		fprintf(fp, "%d %d\n", width, height);
		fprintf(fp, "255\n");
		for (int j = 0; j < height; j++)
			for (int i = 0; i < width; i++)
			{
				fputc(R[i + width * j], fp);
				fputc(G[i + width * j], fp);
				fputc(B[i + width * j], fp);
			}

		fclose(fp);
	}

	~PPM()
	{
		delete[] R;
		delete[] G;
		delete[] B;
	}
};

int main()
{
	PPM ppm("imori.pnm");
	int width = ppm.Get_width();
	int height = ppm.Get_height();
	PPM ppm2(width, height);

	auto kernel = [&](int i, int j)
	{
		double ret = 0;
		//if (i == 0 && j == -1) ret = -1;
		//else if (i == 0 && j == 0) ret = 1;
		if (i == -1 && j == 0) ret = -1;
		else if (i == 0 && j == 0) ret = 1;
		return ret;// / 16.;
	};

	auto conv = [&](const std::vector<std::vector<double>>& f)
	{
		std::vector < std::vector < double >> ret(width, std::vector<double>(height));
		for (int j = 0; j < height; j++)
			for (int i = 0; i < width; i++)
			{
				ret[i][j] = 0;
			}
		for (int j = 0; j < height; j++)
			for (int i = 0; i < width; i++)
			{
				int sum = 0;
				for (int di = -1; di <= 1; di++)
					for (int dj = -1; dj <= 1; dj++)
					{
						if (i - di >= 0 && i - di < width && j - dj >= 0 && j - di < height)
						{
							ret[i][j] += kernel(di, dj) * f[i - di][j - dj];
							sum += kernel(di, dj);
						}
					}
				//ret[i][j] /= (double)sum;
			}
		return ret;
	};

	std::vector < std::vector < double >> arry(width, std::vector<double>(height));
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int r = ppm(i, j, 'r');
			int g = ppm(i, j, 'g');
			int b = ppm(i, j, 'b');
			int y = (std::round)(0.2126 * r + 0.7152 * g + 0.0722 * b);
			arry[i][j] = y;
		}
	arry = conv(arry);

	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int val = abs(arry[i][j]);
			if (val > 255) val = 255;
			ppm2(i, j, 'r') = val;
			ppm2(i, j, 'g') = val;
			ppm2(i, j, 'b') = val;
		}

	ppm2.Flush("out.ppm");
	return 0;
}
```

![imori.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/a70031fa-64df-051d-4773-a06b1b80f95b.png) ![out.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/c5504010-f88d-68e2-0af9-6d613431da03.png) ![out1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/573043d4-e8f9-3489-6296-7833306ddaf8.png)