前回
Excel 今日のトリビア HSLも実は選べるがExcel色ダイアログボックスのHEXはインチキ
に続きこのHSLについて
白のRGBとHSL
白(&HFFFFFF)はRGB(255,255,255)
これを手動でHSL変換する
まずHUEは最大、最小がないので
Hue=0となる(はずである)
次にSaturationは
(nMax - nMin) / (nMax + nMin)
この式からSaturation=0となる
さらにLightness
は
255
でOK。3つが同じなら2つたして2つで割るからこうなる。
すると
HLS(0,0,255)
が答えである。
Excelが表示する白のHSLの値
ここでHSLに変換する
170,0,255...
ちなみに170の値を変化させても色は変わらない。
なぜならHSLの角度は無彩色には無関係だから。
純粋に彩度だけで決まる。
もとからExcelのHSLはおかしい
次に、ExcelのHSLは非常におかしい
Hue(色相)
彩度は角度で0~360(マイナスの時は360を足す)
これを無理やり0~255で表現している。
おそらくこの360を255に変換するときにバグがあるのだろう。
Saturation(彩度) Lightness(輝度)
こちらは0~100だが、Excelはこれを0~255にする。
この結果、色相は360を255であらわすため不正確で、残りの2つは大体再現できるということになる。
というかHSLの場合この色相環が重要なのが…
Excel用のVBA RGB、HSL変換関数
これはまず色番号(10進)をRGBに分解し、そのうえでHSLにするというもの
http://yamatyuu.net/other/color/hsl/index.html
を参考にしているが、無彩色の場合をの例外を加えている。
Public Type rgbnum
Red As Long
Gre As Long
BLu As Long
End Type
Public Type HSLNum
Hue As Double
Saturation As Double
Lightness As Double
End Type
Function ColorToRGB(num As Long) As rgbnum
'色番号をRGBに変換する
'https://excel.syogyoumujou.com/memorandum/rgb.html
With ColorToRGB
.Red = num Mod 256
.Gre = Int(num / 256) Mod 256
.BLu = Int(num / 256 / 256)
End With
End Function
Function RGBtoHSL2(num As Long) As HSLNum
Dim nMax As Long, nMin As Long, Ldash As Long, Sdash As Long
Dim ar As rgbnum
With ColorToRGB(num)
ar.Red = .Red
ar.Gre = .Gre
ar.BLu = .BLu
End With
If ar.Red = ar.Gre And ar.BLu = ar.Gre Then
RGBtoHSL2.Hue = 0
Else
nMax = WorksheetFunction.Max(ar.Red, ar.Gre, ar.BLu)
nMin = WorksheetFunction.Min(ar.Red, ar.Gre, ar.BLu)
Select Case nMax
Case Is = ar.Red
RGBtoHSL2.Hue = IIf(60 * (ar.Gre - ar.BLu) / (nMax - nMin) >= 0, 60 * (ar.Gre - ar.BLu) / (nMax - nMin), 60 * (ar.Gre - ar.BLu) / (nMax - nMin) + 360)
Case Is = ar.Gre
RGBtoHSL2.Hue = IIf((60 * (ar.BLu - ar.Red) / (nMax - nMin)) + 120 >= 0, (60 * (ar.BLu - ar.Red) / (nMax - nMin)) + 120, (60 * (ar.BLu - ar.Red) / (nMax - nMin)) + 120 + 360)
Case Is = ar.BLu
RGBtoHSL2.Hue = IIf((60 * (ar.Red - ar.Gre) / (nMax - nMin)) + 240 >= 0, (60 * (ar.Red - ar.Gre) / (nMax - nMin)) + 240, (60 * (ar.Red - ar.Gre) / (nMax - nMin)) + 240 + 360)
End Select
End If
If (nMax + nMin) / 2 < 127 Then
If nMax = nMin Then
RGBtoHSL2.Saturation = 0
Else
RGBtoHSL2.Saturation = (nMax - nMin) / (nMax + nMin)
End If
Else
RGBtoHSL2.Saturation = Int((255 * ((nMax - nMin) / (510 - nMax - nMin))) + 0.5)
End If
If ar.Red = ar.Gre And ar.BLu = ar.Gre Then
RGBtoHSL2.Lightness = ar.Gre ' 無彩色の時例外処理
Else
RGBtoHSL2.Lightness = (nMax + nMin) / 2
End If
End Function
ついでにChatGPTがつくったやつ(もちろん修正)
r,g,bをDoubleにしていたが255までの整数値しか相手にしないので、Integerにしている。
他もおかしい
' RGB値を0-1の範囲に正規化
rNorm = r / 255
gNorm = g / 255
bNorm = b / 255
この部分はr,g,bはDoubleと言いながら0~1の値に変換しており、最初からRGB的な値を前提としている。
本来ここで1を超えることはあり得ないはずである。
' RGBからHSLに変換する関数
Function RGBtoHSL(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer) As Variant
Dim maxRGB As Double
Dim minRGB As Double
Dim delta As Double
Dim h As Double
Dim s As Double
Dim l As Double
Dim rNorm As Double
Dim gNorm As Double
Dim bNorm As Double
' RGB値を0-1の範囲に正規化
rNorm = r / 255
gNorm = g / 255
bNorm = b / 255
' 最大値と最小値を取得
maxRGB = Excel.Application.WorksheetFunction.Max(rNorm, gNorm, bNorm)
minRGB = Excel.Application.WorksheetFunction.Min(rNorm, gNorm, bNorm)
' 輝度の計算
l = (maxRGB + minRGB) / 2
If maxRGB = minRGB Then
' グレースケールの色の場合
h = 0
s = 0
Else
' 差分の計算
delta = maxRGB - minRGB
' 彩度の計算
If l < 0.5 Then
s = delta / (maxRGB + minRGB)
Else
s = delta / (2 - maxRGB - minRGB)
End If
' 色相の計算
Select Case maxRGB
Case rNorm
h = (gNorm - bNorm) / delta + (IIf(gNorm < bNorm, 6, 0))
Case gNorm
h = (bNorm - rNorm) / delta + 2
Case bNorm
h = (rNorm - gNorm) / delta + 4
End Select
h = h / 6
End If
' HSL値を返す
RGBtoHSL = Array(h * 360, s, l)
End Function
実際に使うコード
Excelのセルの色を横にRGB、16進数RGB、色番号、色番号Excel版、HSL(ChatGPT)、HSL(作成版)
で変換し出力する。
chatGPTは変換が必要
色相は角度で出力されるので255に変換
ほかも、ChatGPTの変換関数は0~1の値で出てくるためそれを255を乗じて変換している。
当方で作成したものはこの問題を解決している。
Intで切れるか?
いくつか試験して調整しているが、端数処理の関係で正しい値と1違う可能性がある
特に角度は誤差が大きい。端数が.5でも切り捨てたほうが表示される値と一致する。
Excel試験用コード
Sub test()
Dim hsl As Variant
Dim r As Integer
Dim g As Integer
Dim b As Integer
With ColorToRGB(ActiveCell.Interior.Color)
ActiveCell.Offset(, 1) = .Red
r = .Red: g = .Gre: b = .BLu
ActiveCell.Offset(, 2) = .Gre
ActiveCell.Offset(, 3) = .BLu
ActiveCell.Offset(, 4) = ActiveCell.Interior.Color
ActiveCell.Offset(, 5) = Hex(ActiveCell.Interior.Color)
With Excel.Application.WorksheetFunction
ActiveCell.Offset(, 6) = .Dec2Hex(ActiveCell.Offset(, 1), 2) & .Dec2Hex(ActiveCell.Offset(, 2), 2) & .Dec2Hex(ActiveCell.Offset(, 3), 2)
End With
End With
hsl = RGBtoHSL(r, g, b)
ActiveCell.Offset(, 7) = Int(hsl(0) / 360 * 255)
ActiveCell.Offset(, 8) = Int(hsl(1) * 255)
ActiveCell.Offset(, 9) = Int(hsl(2) * 255)
With RGBtoHSL2(ActiveCell.Interior.Color)
ActiveCell.Offset(, 10) = .Hue
ActiveCell.Offset(, 11) = .Saturation
ActiveCell.Offset(, 12) = .Lightness
End With
End Sub
タイトル回収
そうすると白は次のような並びになる
255 255 255 16777215 FFFFFF FFFFFF 0 0 255 0 0 0
つまり、HSLは0,0,255が正しい
にも拘わらず170となる。一体どういうことなのか?
170はExcelしか表示しない
この無意味な170は度数法で言うと240度である。ラジアンで言うと$2/3π$となる。
しかしHLSで白の色相は常に0であり、240度、$2/3π$、0.75とするサイトは一つもない
おそらく、何らかの形で0で処理が止まるため、それを回避したものと思われる。