AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Delphi Fast integer RGB-HSL

Fast integer RGB-HSL

Ein Thema von WojTec · begonnen am 29. Dez 2010 · letzter Beitrag vom 30. Dez 2010
Antwort Antwort
WojTec

Registriert seit: 17. Mai 2007
477 Beiträge
 
Delphi XE6 Professional
 
#1

Fast integer RGB-HSL

  Alt 29. Dez 2010, 16:37
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:
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.
So, could you share fast 0..360 integer-based HSL (scalable if possible )?
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
38.939 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Fast integer RGB-HSL

  Alt 29. Dez 2010, 22:01
Zitat:
//set these variables to your needs, e.g. 360, 255, 255
Did you even read this line?


Hier im Forum suchenRGB HSV

http://www.delphipraxis.net/21076-rg...bestimmen.html
http://www.delphipraxis.net/146609-d...tml#post992348

The calculations can be necessary also to convert HSL.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (29. Dez 2010 um 22:06 Uhr)
  Mit Zitat antworten Zitat
Thom

Registriert seit: 19. Mai 2006
570 Beiträge
 
Delphi XE3 Professional
 
#3

AW: Fast integer RGB-HSL

  Alt 29. Dez 2010, 23:55
@himitsu,
so, wie ich das verstanden habe, geht es darum, dass ihm diese Funktionen zu langsam sind:
Zitat:
It is exactly what I need... One thing I can't accept is performance
Eine mögliche Antwort wäre: Umschreiben in Assembler...
Thomas Nitzschke
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.631 Beiträge
 
Delphi 2007 Enterprise
 
#4

AW: Fast integer RGB-HSL

  Alt 30. Dez 2010, 01:49
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:
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;
      }
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
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)

Geändert von Medium (30. Dez 2010 um 02:04 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
38.939 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Fast integer RGB-HSL

  Alt 30. Dez 2010, 09:19
@Medium: HSV <> HSL (Wiki)
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
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (30. Dez 2010 um 09:21 Uhr)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.631 Beiträge
 
Delphi 2007 Enterprise
 
#6

AW: Fast integer RGB-HSL

  Alt 30. Dez 2010, 15:10
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. The RGB->HSL/V conversions shown here reflect that similarity pretty well, so that my code's coarse structure is still a valid indicator for the algorithms complexity.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
WojTec

Registriert seit: 17. Mai 2007
477 Beiträge
 
Delphi XE6 Professional
 
#7

Re: Fast integer RGB-HSL

  Alt 30. Dez 2010, 16:38
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
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:49 Uhr.
Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf