使用したライブラリ
Q.5. HSV変換
HSV変換を実装して、色相Hを反転せよ。
定義式の通りに変換、逆変換するだけです。
struct HSV
{
double h, s, v;
};
struct RGB
{
RGB(int r, int g, int b)
{
this->r = r;
this->g = g;
this->b = b;
}
int r, g, b;
};
HSV RGB2HSV(const RGB &rgb)
{
HSV hsv;
int r = rgb.r;
int g = rgb.g;
int b = rgb.b;
int Max = std::max({ r, g, b });
int Min = std::min({r, g, b});
if (Min == Max) hsv.h = 0;
else if (Min == b) hsv.h = 60 * (g - r) / (double)(Max - Min) + 60;
else if (Min == r) hsv.h = 60 * (b - g) / (double)(Max - Min) + 180;
else if (Min == g) hsv.h = 60 * (r - b) / (double)(Max - Min) + 300;
hsv.v = Max / 255.;
hsv.s = (Max - Min) / 255.;
return hsv;
}
RGB HSV2RGB(const HSV& hsv)
{
double r, g, b ;
double h = hsv.h;
double s = hsv.s;
double v = hsv.v;
double c = s;
double h2 = h / 60.;
double x = c * (1 - fabs( fmod(h2,2) -1 ));
if (0 <= h2 && h2 < 1) r = c, g = x, b = 0;
else if (1 <= h2 && h2 < 2) r = x, g = c, b = 0;
else if (2 <= h2 && h2 < 3) r = 0, g = c, b = x;
else if (3 <= h2 && h2 < 4) r = 0, g = x, b = c;
else if (4 <= h2 && h2 < 5) r = x, g = 0, b = c;
else if (5 <= h2 && h2 < 6) r = c, g = 0, b = x;
else r = g = b = 0;
r += (v - c);
g += (v - c);
b += (v - c);
return RGB(r*255,g*255,b*255);
}
int main()
{
PPM ppm("imori.pnm");
int width = ppm.Get_width();
int height = ppm.Get_height();
PPM ppm2(width, 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');
HSV hsv = RGB2HSV(RGB(r, g, b));
if (hsv.h > 180)hsv.h -= 180;
else hsv.h += 180;
RGB rgb = HSV2RGB(hsv);
ppm2(i, j, 'r') = rgb.r;
ppm2(i, j, 'g') = rgb.g;
ppm2(i, j, 'b') = rgb.b;
}
ppm2.Flush("out.ppm");
return 0;
}
注意
普段グレースケール画像ばかりいじっている身としてはこういうのは新鮮。あんまりカラー画像に対する処理は興味がなかったのですが、セグメンテーションの色分けとかに使えそうな感じですね。
ついでにお遊びでカラーバーみたいなものを作ろうと思ったときに気が付いた注意点について言及してこの記事を終わりにします。作りたかったのは次のような画像です。
こういう画像を作ろうと思ったとき、RGBでは3チャンネルしかないので困るわけですが、HSVを使えばHを振ることでたくさんの色が作れます。
Q.5ではRGB -> HSV -> RGBと変換したわけですが、今回はHSV -> RGBという変換になります。この際$V \geq S$が成立しなけらばならないことに注意が必要です。これを守らないとRGBに負の値が許されてしまいます。
int main()
{
int width = 256, height = width;
PPM ppm(width, height);
int N = 6;
for (int i = 0; i < width; i++)
{
int n = i / 50;
HSV hsv;
hsv.h = 360. / (double)N * n;
hsv.s = 0.5;
double dv = (1. - hsv.s) / height;
for (int j = 0; j < height; j++)
{
hsv.v = hsv.s + dv * j;
RGB rgb = HSV2RGB(hsv);
int r = rgb.r;
int g = rgb.g;
int b = rgb.b;
ppm(i, j, 'r') = r;
ppm(i, j, 'g') = g;
ppm(i, j, 'b') = b;
}
}
ppm.Flush("out.ppm");
return 0;
}