![]() |
Fast integer RGB-HSL
I need to convert color from RGB to HSL, manupulate HSL and then make RGB. I'm using GR32, in GR32 there is function to convert RGB-HSL, is very fast, but has wrong rande - any HSL component is in 0..255. So, I checked mbColorLib, hot it working qith HSL. It allows to scale HSL range for actial needs, for example extend H component to 0..360. It is exactly what I need and looked for this for a looooong time. One thing I can't accept is performance, HSL routins can't be used in image processing. Here is unit I'm talking bout:
Delphi-Quellcode:
So, could you share fast 0..360 integer-based HSL (scalable if possible :D)?
unit RGBHSLUtils;
interface uses Windows, Graphics, Math, Scanlines; var //set these variables to your needs, e.g. 360, 255, 255 MaxHue: integer = 239; MaxSat: integer = 240; MaxLum: integer = 240; function HSLtoRGB (H, S, L: double): TColor; function HSLRangeToRGB (H, S, L: integer): TColor; procedure RGBtoHSLRange (RGB: TColor; var H1, S1, L1 : integer); function GetHValue(AColor: TColor): integer; function GetSValue(AColor: TColor): integer; function GetLValue(AColor: TColor): integer; procedure Clamp(var Input: integer; Min, Max: integer); function HSLToRGBTriple(H, S, L : integer) : TRGBTriple; function HSLToRGBQuad(H, S, L: integer): TRGBQuad; procedure RGBTripleToHSL(RGBTriple : TRGBTriple; var h, s, l: integer); implementation function HSLtoRGB(H, S, L: double): TColor; var M1, M2: double; function HueToColorValue(Hue: double): byte; var V : double; begin if Hue < 0 then Hue := Hue + 1 else if Hue > 1 then Hue := Hue - 1; if 6 * Hue < 1 then V := M1 + (M2 - M1) * Hue * 6 else if 2 * Hue < 1 then V := M2 else if 3 * Hue < 2 then V := M1 + (M2 - M1) * (2/3 - Hue) * 6 else V := M1; Result := round (255 * V) end; var R, G, B: byte; begin if S = 0 then begin R := round (MaxLum * L); G := R; B := R end else begin if L <= 0.5 then M2 := L * (1 + S) else M2 := L + S - L * S; M1 := 2 * L - M2; R := HueToColorValue (H + 1/3); G := HueToColorValue (H); B := HueToColorValue (H - 1/3) end; Result := RGB (R, G, B) end; function HSLRangeToRGB(H, S, L : integer): TColor; begin if s > MaxSat then s := MaxSat; if s < 0 then s := 0; if l > MaxLum then l := MaxLum; if l < 0 then l := 0; Result := HSLToRGB(H / MaxHue, S / MaxSat, L / MaxLum); end; procedure RGBtoHSLRange(RGB: TColor; var H1, S1, L1 : integer); var R, G, B, D, Cmax, Cmin, h, s, l: double; begin H := h1; S := s1; L := l1; R := GetRValue (RGB) / 255; G := GetGValue (RGB) / 255; B := GetBValue (RGB) / 255; Cmax := Max (R, Max (G, B)); Cmin := Min (R, Min (G, B)); L := (Cmax + Cmin) / 2; if Cmax = Cmin then begin H := 0; S := 0; end else begin D := Cmax - Cmin; //calc L if L < 0.5 then S := D / (Cmax + Cmin) else S := D / (2 - Cmax - Cmin); //calc H if R = Cmax then H := (G - B) / D else if G = Cmax then H := 2 + (B - R) /D else H := 4 + (R - G) / D; H := H / 6; if H < 0 then H := H + 1; end; H1 := round (H * MaxHue); S1 := round (S * MaxSat); L1 := round (L * MaxLum); end; function GetHValue(AColor: TColor): integer; var d, h: integer; begin RGBToHSLRange(AColor, h, d, d); Result := h; end; function GetSValue(AColor: TColor): integer; var d, s: integer; begin RGBToHSLRange(AColor, d, s, d); Result := s; end; function GetLValue(AColor: TColor): integer; var d, l: integer; begin RGBToHSLRange(AColor, d, d, l); Result := l; end; procedure Clamp(var Input: integer; Min, Max: integer); begin if (Input < Min) then Input := Min; if (Input > Max) then Input := Max; end; function HSLToRGBTriple(H, S, L: integer): TRGBTriple; const Divisor = 255*60; var hTemp, f, LS, p, q, r: integer; begin Clamp(H, 0, MaxHue); Clamp(S, 0, MaxSat); Clamp(L, 0, MaxLum); if (S = 0) then Result := RGBToRGBTriple(L, L, L) else begin hTemp := H mod MaxHue; f := hTemp mod 60; hTemp := hTemp div 60; LS := L*S; p := L - LS div MaxLum; q := L - (LS*f) div Divisor; r := L - (LS*(60 - f)) div Divisor; case hTemp of 0: Result := RGBToRGBTriple(L, r, p); 1: Result := RGBToRGBTriple(q, L, p); 2: Result := RGBToRGBTriple(p, L, r); 3: Result := RGBToRGBTriple(p, q, L); 4: Result := RGBToRGBTriple(r, p, L); 5: Result := RGBToRGBTriple(L, p, q); else Result := RGBToRGBTriple(0, 0, 0); end; end; end; function HSLToRGBQuad(H, S, L: integer): TRGBQuad; const Divisor = 255*60; var hTemp, f, LS, p, q, r: integer; begin Clamp(H, 0, MaxHue); Clamp(S, 0, MaxSat); Clamp(L, 0, MaxLum); if (S = 0) then Result := RGBToRGBQuad(L, L, L) else begin hTemp := H mod MaxHue; f := hTemp mod 60; hTemp := hTemp div 60; LS := L*S; p := L - LS div MaxLum; q := L - (LS*f) div Divisor; r := L - (LS*(60 - f)) div Divisor; case hTemp of 0: Result := RGBToRGBQuad(L, r, p); 1: Result := RGBToRGBQuad(q, L, p); 2: Result := RGBToRGBQuad(p, L, r); 3: Result := RGBToRGBQuad(p, q, L); 4: Result := RGBToRGBQuad(r, p, L); 5: Result := RGBToRGBQuad(L, p, q); else Result := RGBToRGBQuad(0, 0, 0); end; end; end; procedure RGBTripleToHSL(RGBTriple: TRGBTriple; var h, s, l: integer); function RGBMaxValue(RGB: TRGBTriple): byte; begin Result := RGB.rgbtRed; if (Result < RGB.rgbtGreen) then Result := RGB.rgbtGreen; if (Result < RGB.rgbtBlue) then Result := RGB.rgbtBlue; end; function RGBMinValue(RGB: TRGBTriple) : byte; begin Result := RGB.rgbtRed; if (Result > RGB.rgbtGreen) then Result := RGB.rgbtGreen; if (Result > RGB.rgbtBlue) then Result := RGB.rgbtBlue; end; var Delta, Min: byte; begin L := RGBMaxValue(RGBTriple); Min := RGBMinValue(RGBTriple); Delta := L-Min; if (L = Min) then begin H := 0; S := 0; end else begin S := MulDiv(Delta, 255, L); with RGBTriple do begin if (rgbtRed = L) then H := MulDiv(60, rgbtGreen-rgbtBlue, Delta) else if (rgbtGreen = L) then H := MulDiv(60, rgbtBlue-rgbtRed, Delta) + 120 else if (rgbtBlue = L) then H := MulDiv(60, rgbtRed-rgbtGreen, Delta) + 240; if (H < 0) then H := H + 360; end; end; end; end. |
AW: Fast integer RGB-HSL
Zitat:
![]() ![]() ![]() The calculations can be necessary also to convert HSL. |
AW: Fast integer RGB-HSL
@himitsu,
so, wie ich das verstanden habe, geht es darum, dass ihm diese Funktionen zu langsam sind: Zitat:
|
AW: Fast integer RGB-HSL
As far as I see it, all the calculations can be done purely with integers. To keep some proper precision, this should be done at some higher scaling than 0..255, a factor of 100 or 1000 should suffice - any scale up to |scale*MaxValueAnywhere|<MaxInt may be chosen though.
Working strictly in the range 0..1 usually only makes things a whole lot easier (or merely possible in some cases), if nonlinear functions like powers or trigonometry come into play, of which none are involved here. Going assembler does not generally yield better performance, MMX is not an option with values >255, but handmade SSE(2) might work. Downside: This should be done right where the load of calculations are performed, to avoid switching the FPU from/into SSE mode for every tiny conversion - this could even make things slower than the current solution. Thus, I'd first try to go all integer, and see how that performs. Edit: Another thing might be de-modularizing the whole thing. There's a lot of calling to tiny helper functions going on, and iirc, Delphi doesn't inline (at least not per default, and not at all with older versions). Doing everything in one go, even if it results in much longer and possibly uglier code, may improve speed a bit, too. But certainly not as much as eliminating the floats completely, by far. Edit2: Also, I'm a little unsure why these functions are so long. I haven't read them that thoroughly, but here's two methods I use in a C# project of mine:
Code:
It's full of floats though, and thus not meant for speed, but it seems a lot shorter and less complex than what you posted, and can most certainly even be optimized and shortened further :gruebel:
public void ColorToHSV(Color c, out double h, out double s, out double v)
{ double r = c.R/255f; double g = c.G/255f; double b = c.B/255f; double max = Math.Max(r, Math.Max(g, b)); double min = Math.Min(r, Math.Min(g, b)); if (max.Equals(min)) h = 0; else if (max.Equals(r)) h = 60f*((g-b)/(max-min)); else if (max.Equals(g)) h = 60f*(2f+(b-r)/(max-min)); else if (max.Equals(b)) h = 60f*(4f+(r-g)/(max-min)); else h = 0; if (h<0) h += 360; if (max.Equals(0)) s = 0; else s = (max-min)/max; v = max; return; } public Color HSVToColor(double h, double s, double v) { int hi = (int)Math.Floor(h/60f); double f = h/60f - hi; double p = (v*(1-s))*255f; double q = (v*(1-s*f))*255f; double t = (v*(1-s*(1-f)))*255f; v *= 255f; Color cn = new Color(); switch (hi) { case 0: cn = Color.FromArgb(255, (byte)v, (byte)t, (byte)p); break; case 1: cn = Color.FromArgb(255, (byte)q, (byte)v, (byte)p); break; case 2: cn = Color.FromArgb(255, (byte)p, (byte)v, (byte)t); break; case 3: cn = Color.FromArgb(255, (byte)p, (byte)q, (byte)v); break; case 4: cn = Color.FromArgb(255, (byte)t, (byte)p, (byte)v); break; case 5: cn = Color.FromArgb(255, (byte)v, (byte)p, (byte)q); break; case 6: cn = Color.FromArgb(255, (byte)v, (byte)t, (byte)p); break; } return cn; } |
AW: Fast integer RGB-HSL
@Medium: HSV <> HSL (
![]() Aber grundsätzlich ist es ähnlich, so da man sich einen HSV-Code eventuell entsprechend anpassen könnte. @Thom: Ich dachte er hat sich auch noch drüber beschwert, daß dieses nur von 0..255, anstatt von 0..359 geht :gruebel: |
AW: Fast integer RGB-HSL
HSL and HSV are that similar, that there is virtually no notable difference. It's just that L ranges from black to white, while V is from black to 50% gray.
![]() |
Re: Fast integer RGB-HSL
Maybe you mean HSV/HSB? If I know HSL != HSV.
Ok, let's back to the topic. I made some changes, so now working not bad and I can use in image processing :D |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:03 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz