RGB色空間とHSV色空間の変換の実装をしてみる

こんにちは。そらです。
今回は、色HSV色空間へRGB色空間から変換、HSV色空間からRGB空間への変換の処理を実装してみたいと思います。




HSV色空間とは

普段私たちが使用しているコンピュータ画像のほとんどは、RGB色空間で表現されています。コンピュータ画像では、Red,Green,Blueの3色の加法混合で表されています。これを、Hue(色相),Saturation(彩度),Value of Brightness(明度)に変換したものをHSV色空間と呼びます。
以下の式を用いて変換を行います。

画像に含まれている一番高いRGBの値と一番小さいRGBの値を用いて変換を行うようです。HSV色空間に変換することで色情報がHueに集約されるため、RGB色空間で色の検知をするよりもしやすそうだということがわかります。

実装について

先程の式を用いることでRGB色空間からHSV色空間への変換は簡単にできそうです。ただ、HSV色空間からRGB色空間への実装は少し考える必要がありますが、式の意味を一つずつひも解いていくことで理解ができると思います。プログラムから是非読み解いてください。

cv::Mat convert_BGR2HSV(cv::Mat img)
{
	cv::Mat hsv = cv::Mat::zeros(cv::Size(img.cols, img.rows), CV_32FC3);

	float red, green, blue;
	float hue, saturation, value;
	float _max, _min;

	for (int y = 0; y < img.rows; y++) {
		for (int x = 0; x < img.cols; x++) {
			red = (float)img.at<cv::Vec3b>(y, x)[2] / 255.0f;
			green = (float)img.at<cv::Vec3b>(y, x)[1] / 255.0f;
			blue = (float)img.at<cv::Vec3b>(y, x)[0] / 255.0f;
		
			_max = fmax(red, fmax(green, blue));
			_min = fmin(red, fmin(green, blue));

			// get value
			value = _max;
			// get saturation
			saturation = _max - _min;
			// get hue
			if (_max == _min) hue = 0.0f;	
			else if (_min == blue) hue = 60.0f * (green - red) / saturation + 60.0f;
			else if (_min == red)  hue = 60.0f * (blue - green) / saturation + 180.0f;
			else hue = 60.0f * (red- blue) / saturation + 300.0f;

			hsv.at<cv::Vec3b>(y, x)[0] = hue;
			hsv.at<cv::Vec3b>(y, x)[1] = saturation;
			hsv.at<cv::Vec3b>(y, x)[2] = value;
		}
	}

	return hsv;
}

cv::Mat convert_HSV2BGR(cv::Mat hsv)
{

	float hue, saturation, value;
	double c, _h, _x;
	double red, green, blue;

	cv::Mat out = cv::Mat::zeros(cv::Size(hsv.cols, hsv.rows), CV_8UC3);

	for (int y = 0; y < hsv.rows; y++) {
		for (int x = 0; x < hsv.cols; x++) {

			hue = hsv.at<cv::Vec3b>(y, x)[0];
			saturation = hsv.at<cv::Vec3b>(y, x)[1];
			value = hsv.at<cv::Vec3b>(y, x)[2];

			c = saturation;
			_h = hue / 60;
			_x = c * (1 - abs(fmod(_h, 2) - 1));

			red = green = blue = value - c;

			if (_h < 1) {
				red += c;
				green += _x;
			}
			else if (_h < 2) {
				red += _x;
				green += c;
			}
			else if (_h < 3) {
				green += c;
				blue += _x;
			}
			else if (_h < 4) {
				green += _x;
				blue += c;
			}
			else if (_h < 5) {
				red += _x;
				blue += c;
			}
			else if (_h < 6) {
				red += c;
				blue += _x;
			}

			out.at<cv::Vec3b>(y, x)[0] = (uchar)(blue * 255);
			out.at<cv::Vec3b>(y, x)[1] = (uchar)(green * 255);
			out.at<cv::Vec3b>(y, x)[2] = (uchar)(red * 255);
		}
	}
	return out;
}

Hが六角推のような座標で表現されているというように考えればわかりやすいかなと思います。

さいごに

画像処理の中では基礎的な処理になりますが、実際に実装をしてみようと思いうと少し頭を使って考える必要がありますね。OpecCVというつよつよなライブラリがあるのでそれ使えばいいじゃんというのもわかりますが、ちゃんと理解を深めるためには実装が必要ですね(n回目)。自分も、理解をしていたつもりがHSV色空間からRGB色空間に変換のプログラムを書いている最中に”あれ?だめだ。”ってなりました。

参考文献

ディジタル画像処理[改訂新版]