解説
通常用いられるRGB処理系ではR(赤)、G(緑)、B(青)の個別調節は容易にできるが色相などの調節は難しい。 しかし色をHue(色相)、Lightness(明度)、Saturation(彩度)の3値で処理するHLS色空間に変換すると容易に 色相などを変えることができる。ここではMicrosoftのホームページに掲載されていたソースコードをほぼそのまま用いてRGBからHLSへの変換を 担うクラスCDnpColorSpaceHLSを作成し、GDI+を扱うクラスから簡単に利用できるようにした。
実行例
画像処理として以下のコードでHLS色調整を行った。_cImage2.AdjustHLS(10,-25,20);

ソースコード
GDI+を利用した画像処理テストのスケルトン2を 元に拡張する。以下のソースコードを「DnpColorSpaceHLS.h」のファイル名で保存する。
#pragma once
//
// RGB-HLS色空間変換クラス
//
//HLS関連の変換処理はMicrosoftのソースをそのまま利用している
// http://support.microsoft.com/kb/q29240/
//
//
class CDnpColorSpaceHLS
{
////////////////////////////////////////////
//HLS用定義
#define HLSMAX 252 //H,L,Sの最大値(取りうる値は0~HLSMAX)HLSは6で割れる方がいいので255でなく252(=42*6)にした
#define RGBMAX 255 //R,G,Bの最大値(取りうる値は0~RGBMAX)
/* Hue is undefined if Saturation is 0 (grey-scale) */
/* This value determines where the Hue scrollbar is */
/* initially set for achromatic colors */
#define HLSUNDEFINED (HLSMAX*2/3)
////////////////////////////////////////////
public:
//
// RGB->HLS変換
//
void RGBtoHLS(RGBTRIPLE sRGBColor,int& H,int& L,int& S)
{
WORD R,G,B; /* input RGB values */
BYTE cMax,cMin; /* max and min RGB values */
WORD Rdelta,Gdelta,Bdelta; /* intermediate value: % of spread from max*/
/* get R, G, and B out of DWORD */
R = sRGBColor.rgbtRed;
G = sRGBColor.rgbtGreen;
B = sRGBColor.rgbtBlue;
/* calculate lightness */
cMax = (BYTE)max( max(R,G), B);
cMin = (BYTE)min( min(R,G), B);
L = (((cMax + cMin) * HLSMAX) + RGBMAX) / (2 * RGBMAX);
if(cMax == cMin)
{
/* r=g=b --> achromatic case */
S = 0; /* saturation */
H = HLSUNDEFINED; /* hue */
return;
}
/* chromatic case */
/* saturation */
if (L <= (HLSMAX/2))
S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
else
S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) ) / (2*RGBMAX-cMax-cMin);
/* hue */
Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
if (R == cMax)
H = Bdelta - Gdelta;
else if (G == cMax)
H = (HLSMAX/3) + Rdelta - Bdelta;
else /* B == cMax */
H = ((2*HLSMAX)/3) + Gdelta - Rdelta;
if (H < 0)
H += HLSMAX;
if (H > HLSMAX)
H -= HLSMAX;
}
protected:
/* utility routine for HLStoRGB */
int HueToRGB(int n1,int n2,int hue)
{
/* range check: note values passed add/subtract thirds of range */
if (hue < 0)
hue += HLSMAX;
if (hue > HLSMAX)
hue -= HLSMAX;
/* return r,g, or b value from this tridrant */
if (hue < (HLSMAX/6))
return ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) );
if (hue < (HLSMAX/2))
return ( n2 );
if (hue < ((HLSMAX*2)/3))
return ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6)));
else
return ( n1 );
}
public:
//
// HLS->RGB変換
//
bool HLStoRGB(int hue,int lum,int sat,RGBTRIPLE& sRGB)
{
WORD Magic1,Magic2; /* calculated magic numbers (really!) */
ZeroMemory(&sRGB,sizeof(RGBTRIPLE));
if(sat == 0)
{ /* achromatic case */
sRGB.rgbtRed = (lum * RGBMAX) / HLSMAX;;
sRGB.rgbtGreen = sRGB.rgbtRed;
sRGB.rgbtBlue = sRGB.rgbtBlue;
if(hue != HLSUNDEFINED)
{
/* ERROR */
return false;
}
return true;
}
/* set up magic numbers */
if (lum <= (HLSMAX/2))
Magic2 = (lum*(HLSMAX + sat) + (HLSMAX/2))/HLSMAX;
else
Magic2 = lum + sat - ((lum*sat) + (HLSMAX/2))/HLSMAX;
Magic1 = 2*lum-Magic2;
/* get RGB, change units from HLSMAX to RGBMAX */
sRGB.rgbtRed = (HueToRGB(Magic1,Magic2,hue+(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX;
sRGB.rgbtGreen = (HueToRGB(Magic1,Magic2,hue)*RGBMAX + (HLSMAX/2)) / HLSMAX;
sRGB.rgbtBlue = (HueToRGB(Magic1,Magic2,hue-(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX;
return true;
}
};
前に作成した「DnpImage.h」に変更を加える。派生元クラスとしてCDnpColorSpaceHLSを入れ、 HLS色空間調整処理関数を追加する。
#pragma once
#include "DnpImageBase.h"
#include "DnpImageConvolution.h"
#include "DnpColorSpaceHLS.h"
class CDnpImage : public CDnpImageBase
,public CDnpImageConvolution
,public CDnpColorSpaceHLS
{
public:
//
// HLS色空間調整処理
//
//Hue(色相)、Lightness(明度)、Saturation(彩度)の調節ができる
//調整は指定された値をH,L,Sそれぞれに加算する単純な方法を採用した。
//指定範囲は±255。0のときは処理しない
//
bool AdjustHLS(int nAdjH,int nAdjL,int nAdjS)
{
if(IsValidImage() == false)
return false;
if(nAdjH == 0 && nAdjL == 0 && nAdjS == 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++)
{
int H;
int L;
int S;
RGBtoHLS(pRGB[x],H,L,S);
H += nAdjH;
L += nAdjL;
S += nAdjS;
if(H > HLSMAX)
H = HLSMAX;
if(H < 0)
H = 0;
if(L > HLSMAX)
L = HLSMAX;
if(L < 0)
L = 0;
if(S > HLSMAX)
S = HLSMAX;
if(S < 0)
S = 0;
HLStoRGB(H,L,S,pRGB[x]);
}
pData += nStrideBytes;
}
UnlockBits();
return true;
}
。。。。省略。。。。
};
