解説
通常用いられるRGB処理系ではR(赤)、G(緑)、B(青)の個別調節は容易にできるが色相などの調節は難しい。 しかし色を色相(Hue)、彩度(Saturation・Chroma)、明度(Brightness・Lightness・Value)の3値で処理する HSV色空間に変換すると容易に色相などを変えることができる。ここではRGBからHSVへの変換を担うクラスCDnpColorSpaceHSVを作成し、GDI+を扱うクラスから簡単に利用できる ようにした。
実行例
画像処理として以下のコードでHSV色調整を行った。_cImage2.AdjustHSV(10,-0.25,-0.20);

ソースコード
GDI+を利用した画像処理テストのスケルトン2を 元に拡張する。以下のソースコードを「DnpColorSpaceHSV.h」のファイル名で保存する。
#pragma once
//
// RGB-HSV色空間変換クラス
//
//色相(Hue)、彩度(Saturation・Chroma)、明度(Brightness・Lightness・Value)
//Hは0.0から360.0。LとSは0.0~1.0の範囲で変化する。
//
//■■Hの閾値処理にバグがあるような...
//
class CDnpColorSpaceHSV
{
public:
//
// RGB->HSV
//
void RGBtoHSV(RGBTRIPLE rgb,double& dH,double& dS,double& dV)
{
double dR;
double dG;
double dB;
double dMax;
double dMin;
double dDiff;
//RGB->HSV変換時のR,G,Bは 0.0~1.0
dR = rgb.rgbtRed / 255.0;
dG = rgb.rgbtGreen / 255.0;
dB = rgb.rgbtBlue / 255.0;
dMax = max(max(dR,dG),dB);
dMin = min(min(dR,dG),dB);
if(dMax == 0) //巷のコードではdMax==dMinで処理していることがあるがそれはNG
{
dS = 0.0;
dV = 0.0;
return;
}
dDiff = dMax - dMin;
dV = dMax;
dS = dDiff / dMax;
//dS = dMax - dMin; //円錐色空間時のS
if(dR == dMax)
dH = 60.0 * (dG - dB) / dDiff;
else if(dG == dMax)
dH = 60.0 * (dB - dR) / dDiff + 120;
else
dH = 60.0 * (dR - dG) / dDiff + 240;
//上の計算ではdHが負になることがある
if(dH < 0)
dH = dH + 360.0;
if(dH > 360)
dH = dH - 360.0;
return;
}
//
// HSV->RGB
//
void HSVtoRGB(double dH,double dS,double dV,RGBTRIPLE& rgb)
{
if(dS == 0.0)
{
rgb.rgbtRed = (BYTE)(dV * 255);
rgb.rgbtGreen = rgb.rgbtRed;
rgb.rgbtRed = rgb.rgbtRed;
return;
}
double f;
double p;
double q;
double t;
int nHi;
nHi = (int)(dH / 60) % 6;
if(nHi < 0)
nHi *= -1; //dHが負のときの対策
f = dH / 60.0 - nHi;
p = dV * (1.0 - dS);
q = dV * (1.0 - f * dS);
t = dV * (1.0 - (1.0 - f) * dS);
//計算結果のR,G,Bは0.0~1.0なので255倍
dV = dV * 255.0;
p = p * 255.0;
q = q * 255.0;
t = t * 255.0;
if(dV > 255.0)
dV = 255.0;
if(t > 255.0)
t = 255.0;
if(p > 255.0)
p = 255.0;
if(q > 255.0)
q = 255.0;
if(nHi == 0)
{
rgb.rgbtRed = (BYTE)dV;
rgb.rgbtGreen = (BYTE)t;
rgb.rgbtBlue = (BYTE)p;
return;
}
if(nHi == 1)
{
rgb.rgbtRed = (BYTE)q;
rgb.rgbtGreen = (BYTE)dV;
rgb.rgbtBlue = (BYTE)p;
return;
}
if(nHi == 2)
{
rgb.rgbtRed = (BYTE)p;
rgb.rgbtGreen = (BYTE)dV;
rgb.rgbtBlue = (BYTE)t;
return;
}
if(nHi == 3)
{
rgb.rgbtRed = (BYTE)p;
rgb.rgbtGreen = (BYTE)q;
rgb.rgbtBlue = (BYTE)dV;
return;
}
if(nHi == 4)
{
rgb.rgbtRed = (BYTE)t;
rgb.rgbtGreen = (BYTE)p;
rgb.rgbtBlue = (BYTE)dV;
return;
}
//if(nHi == 5)
//{
rgb.rgbtRed = (BYTE)dV;
rgb.rgbtGreen = (BYTE)p;
rgb.rgbtBlue = (BYTE)q;
return;
//}
}
};
前に作成した「DnpImage.h」に変更を加える。派生元クラスとしてCDnpColorSpaceHSVを入れ、 HVS色空間調整処理関数を追加する。
#pragma once
#include "DnpImageBase.h"
#include "DnpImageConvolution.h"
#include "DnpColorSpaceHLS.h"
#include "DnpColorSpaceHSV.h"
class CDnpImage : public CDnpImageBase
,public CDnpImageConvolution
,public CDnpColorSpaceHLS
,public CDnpColorSpaceHSV
{
public:
//
// HSV色空間調整処理
//
//色相(Hue)、彩度(Saturation・Chroma)、明度(Brightness・Lightness・Value)の調節ができる
//調整は指定された値をH,S,Vそれぞれに加算する単純な方法を採用した。
//指定範囲はHが±360、SとVは±1.0。
//
//■■Hの閾値処理にバグがあるような...
//
bool AdjustHSV(double dAdjH,double dAdjS,double dAdjV)
{
if(IsValidImage() == false)
return false;
if(dAdjH == 0 && dAdjS == 0 && dAdjV == 0)
return true;
int nStrideBytes;
bool ret;
UINT x;
UINT y;
UINT nWidth;
UINT nHeight;
BYTE* pData;
RGBTRIPLE* pRGB;
nWidth = GetWidth();
nHeight = GetHeight();
ret = LockBits(&pData,&nStrideBytes);
if(ret == false)
return false;
for(y = 0; y < nHeight; y++)
{
pRGB = (RGBTRIPLE*)pData;
for(x = 0; x < nWidth; x++)
{
double H;
double S;
double V;
RGBtoHSV(pRGB[x],H,S,V);
H += dAdjH;
S += dAdjS;
V += dAdjV;
//SとVは0.0~1.0の範囲にする
//Hは円形なので範囲を限定しない
if(S > 1.0)
S = 1.0;
if(S < 0)
S = 0;
if(V > 1.0)
V = 1.0;
if(V < 0)
V = 0;
HSVtoRGB(H,S,V,pRGB[x]);
}
pData += nStrideBytes;
}
UnlockBits();
return true;
}
。。。。省略。。。。
};
