HSL/HSV to RGB

Just starting out? Need help? Post your questions and find answers here.
User avatar
Piero
Addict
Addict
Posts: 1166
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

HSL/HSV to RGB

Post by Piero »

Forgive me, if I missed decent solutions on Forum... (I saw some but they weren't "perfect"...)

Can some Guru suggest me a SUPERFAST method? (I mostly need changing hue... but anyway...)

Thanks!
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3944
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: HSL/HSV to RGB

Post by wilbert »

Piero wrote: Tue Sep 05, 2023 3:56 pm Forgive me, if I missed decent solutions on Forum... (I saw some but they weren't "perfect"...)
What was the exact problem with the solutions you found on the forum?
Do you need to convert a single value at a time or an array of pixels/rgb values.

On this page https://beesbuzz.biz/code/16-hsv-color-transforms there's also a TransformHSV function that can change the hue of a color.
Windows (x64)
Raspberry Pi OS (Arm64)
AZJIO
Addict
Addict
Posts: 2246
Joined: Sun May 14, 2017 1:48 am

Re: HSL/HSV to RGB

Post by AZJIO »

User avatar
Piero
Addict
Addict
Posts: 1166
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

Re: HSL/HSV to RGB

Post by Piero »

Thanks for your replies!
I'm (between other things) trying to make psychedelic graphs in "real time" (see my avatar)
I will take a look to your "methods" as soon as possible...

Thanks Again!

PS: AZJIO, are you Brasileiro, cara?


https://images2.imgbox.com/06/07/nAyqNxRF_o.jpeg
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3944
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: HSL/HSV to RGB

Post by wilbert »

Piero wrote: Tue Sep 05, 2023 10:07 pm I'm (between other things) trying to make psychedelic graphs in "real time" (see my avatar)
It looks like your are only changing the hue of the hsl/hsv and are keeping the other two components the same.
If this is always the case, you could speed optimize the code for that.
Windows (x64)
Raspberry Pi OS (Arm64)
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

I'm working on that stuff! But until now it is not ready!

I have standard functions to convert from HSV/HSL to RGB and back. But this is done with standard CPU commands.

As you can see here: https://beesbuzz.biz/code/16-hsv-color-transforms

The caclculation is always a Matrix Vector Product of a 3x3Matrix with a Vector3 (x,y,z). To speed up this directly isn't possible.

But it is possible as 4x4 Matrix with a Vector4 (x,y,z,w). This is exactly the 4D Transformation for 3D Grafics.
This can be done with the MMX-SSE Extention.

Extending the Matrix to a 4D calculation:
1. The Vector: set w = 1
2.Tthe Matrix 4'th line: set all values to 0 (then you get a 0 back as result of w, which you do not need

now you can use 4D Matrix Vector Product with SSE

here is my Vectorf Module which can do this with SSE.
https://github.com/Maagic7/PureBasicFra ... VECTORf.pb

It is possible to speed up this agian, if you write functions wich converts a complete Image in a Loop. This will save time by eliminating
a function call for each pixel.

As I said, I'm working on that stuff and it will be become part of the 2 Modules VectorColor and Image.

If you are able to make a ready solution from this, please let me know, than I will integrate it in the PureBasicFrameWork project!
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

because this direct transformation was new to me, i converted it to Purebasic.
But untested. Maybe anyone will write a test code!
For testing the principal function this will be o.k.
But for on the fly caclulations of complete images we can eliminate a lot of multiplications, if we
precalculate the transformation Matrix.

Updated 2023/09/07, it should have a limit check at 255

Code: Select all

 Procedure.l HSVadjustRBG(RGB.l, h.f, s.f, v.f)
  ; ======================================================================
  ; NAME : HSVadjustRBG
  ; DESC : Do a HSV-Color Space adjust at a RGB color of 24/32 Bit
  ; DESC : it will keep the original Alpha
  ; VAR(RGB.l) : 24/32 Bit RGB Color
  ; VAR(h.f) : Hue shift (in degrees) [0..360]
  ; VAR(s.f) : saturation multiplier (scalar)
  ; VAR(v.f) : value multiplier (scalar)
  ; RET.l :  The HSV adjusted color as RGB-Color-Value 
  ; ======================================================================
    
    Protected.f vsu, vsw
    Protected.f r, g, b
    Protected.a A ; Alpha value
    
    r = Red(RGB)
    g = Green(RGB)
    b = Blue(RGB)
    A = Alpha(RGB)
    
    vsu = v * s * Cos(Radian(h))
    vsw = v * s * Sin(Radian(h))
    
    r = (0.299*v + 0.701*vsu + 0.168*vsw) * r + (0.587*v - 0.587*vsu + 0.330*vsw) * g + (0.114*v - 0.114*vsu - 0.497*vsw) * b
    g = (0.299*v - 0.299*vsu - 0.328*vsw) * r + (0.587*v + 0.413*vsu + 0.035*vsw) * g + (0.114*v - 0.114*vsu + 0.292*vsw) * b
    b = (0.299*v - 0.300*vsu + 1.250*vsw) * r + (0.587*v - 0.588*vsu - 1.050*vsw) * g + (0.114*v + 0.886*vsu - 0.203*vsw) * b
    
    If r > 255 
      r = 255
    ElseIf r < 0 
      r = 0
    EndIf
    
    If g > 255 
      g = 255
    ElseIf g < 0 
      g = 0
    EndIf
    
    If b > 255 
      b = 255
    ElseIf b < 0 
      b = 0
    EndIf

    ProcedureReturn RGBA(Int(r), Int(g), Int(b),  A)
  EndProcedure
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3944
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: HSL/HSV to RGB

Post by wilbert »

On this page
https://stackoverflow.com/questions/131 ... conversion
there's a fast procedure for hsl to rgb.
The only issue is that the input values for hue, saturation and lightness are all in range [0, 255] which is not very common.
Usually hue is in range [0, 360] and saturation and lightness in range [0, 100].
Windows (x64)
Raspberry Pi OS (Arm64)
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

Using Integer calculation might be a good way to speed up some %!

here a short calculation how many operations has a full HD 1920x180 = 2.073.600 = 2.1 e+06 Pixel
with the direct transformation HSVadjustRBG
we have 36 multiplications + 24 add/sub + a few compare for each pixel

36*2.1 = 75.6 e06 multiplications
24*2.1 = 50.4 e06 add/sub)
3*2.1 = 6.3 e06 limit checks
-----------------------------------------
132.3 e06 caluclations total per full HD picture if calling HSVadjustRBG for each pixel

With a precalcualted Matrix we can reduce the multiplications to 9 each Pixel + 27mul for the matirx (only once per picture)

9 *2.1 = 18.9 e06 Multiplications compared with 75.6 -> only 25% of multiplications! The sub/add will be the same

75.6 e06 claculations total compared with 132 e06 -> only 57% of calculations

With use of SSE typical we can speed up 4D Vector operations with a factor of 4. At 2D Pixel were we use only 3D Vectors
this will be arraund factor 3. So with SSE I estimate a total amount of calculations from 75.6 e06 /3 = 25.2 e06

now we compare SSE Version when calcualting all pixels in a loop wit the basic version of calling the function HSVadjustRBG for every pixel.
132 e06 -> 25.2 e06 -> 19% of Speed what means a factor of 5 faster.

If we estimate 4 CPU ticks for each calculation, incl. load and save intstructions (modern CPU can do 2 operations per tick).
We need 100 e06 CPU ticks for 1 full HD Picture. So per each GHz = 4 Pictures in a second. -> 3.5GhZ = 14fps for Full HD

So I guess for 'on the fly caclculation' of complete Screens it is obligatory to use SSE Vector commands! Otherwise it will be to slow!

If using 256Bit YMM registers instead of 128Bit XMM Registers it will be possible to caclculate 2 Pixels at same time what will double speed it
again. What means ~28fps. (with a singel thread)
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

Just had an idea to make a quick speed test.

My Ryzen 5700 need for 1 Full HD Picture ~ 540Mil ticks when calling HSVadjustRBG() for each pixel.
That means ~7fps with the slowest version. With 128Bit SSE Vector commands I estimate ~21fps and with 256Bit SSE 42fps.
That's ~50% faster as my calculation bevor!

But a very strange behavior.

Float/ Int
240 / 160 Mio , 126 / 80ms: new Intel I7 8565U (Laptop Win10)
540 / 300 Mio , 142 / 80ms : Ryzen 5800X (Dektop Win 10))
780 / 370 Mio, 202 / 110 ms : old Intel I7 Laptop (2016, Win7) :
970 / 650 Mio : old AMD Laptop (2011, Win7)

Seems to be new Intel I7 has much faster floting point unit (or a bug in RuntimeCounter)
From this test I guess with fixed point integer it is possible to speed up a lot at AMD and Intel

UDATE:
when adding time measuring and frequency calculation, it is clear!
The new Intel I7 isn't faster. It use 1.9MHz and internal double it for the CPU kernel.

UPDATE 2023/09/09 with SSE Version
Download additional Modules form Github

GitHub. https://github.com/Maagic7/PureBasicFra ... in/Modules
"PbFw_Module_PbFw.pb"
"PbFw_Module_Debug.pb"
"PbFw_Module_VECTORf.pb"

Code: Select all

 EnableExplicit

XIncludeFile "..\..\Modules\PbFw_Module_VECTORf.pb"      ; VECf::     single precision Vector Modul

Procedure.q ReadRuntimeCounter()     ; RDTSC
  ; ======================================================================
  ;  NAME: ReadRuntimeCounter
  ;  DESC: Reads the CPU Runtime Counter
  ;  DESC: A counter incremented +1 at each CPU cycle
  ;  RET.q : CPU ticks counted with the CPU's operating frequency 
  ; ====================================================================== 
   
    CompilerIf #PB_Compiler_Backend=#PB_Backend_C
    ; ----------------------------------------------------------------------
    ;   C-Backend
    ; ----------------------------------------------------------------------
    
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x64 Or #PB_Compiler_Processor = #PB_Processor_x86 
        
        Protected t.q
        !unsigned hi, lo;
        !__asm__ __volatile__ ("lfence\n rdtsc\n lfence" : "=a"(lo), "=d"(hi));
        !v_t =((unsigned long long)lo)|(((unsigned long long)hi)<<32 );
        ProcedureReturn t
         
      CompilerElseIf #PB_Compiler_Processor = #PB_Processor_Arm64 Or #PB_Compiler_Processor = #PB_Processor_Arm32 
        ; ARM x32/x64
        Protected pmuseren.l,pmcntenset.l,pmccntr.l;
        !asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(v_pmuseren));
        If pmuseren & 1 
          !asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(v_pmcntenset));
          If pmcntenset & $80000000 
            !asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(v_pmccntr));
            t = pmccntr
            ProcedureReturn t << 6 
          EndIf 
        EndIf 
         
      CompilerEndIf 
     
    CompilerElse
      
    ; ----------------------------------------------------------------------
    ;   ASM-Backend x64 / x32
    ; ----------------------------------------------------------------------
      
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
        
        DisableDebugger
        ; RDTSC transfers TimeStampCounter to EDX, EAX (on x32 and x 64)
        !RDTSC
        ; on x64 a Quad is returned as RAX, so we have to combine hi and lo in RAX
        !SHL RDX, 32   ; EDX to RDX_Hi
        !OR RAX, RDX
        ProcedureReturn
        EnableDebugger
        
      CompilerElse  ; x32
        
        DisableDebugger
        ; RDTSC transfers TimeStampCounter to EDX, EAX (on x32 and x 64)
        !RDTSC
        ; on x32 a Quad is returned as EDX, EAX
        ProcedureReturn ; return the TimeStampCounter [EDX, EAX} on x32 [RAX] on x64
        EnableDebugger
        
      CompilerEndIf
    
    CompilerEndIf
    
  EndProcedure

Procedure.q ElapsedMicroSeconds()
  Protected v.Quad, f.Quad
  
  QueryPerformanceFrequency_(f)
  QueryPerformanceCounter_(v)
  
  ProcedureReturn (v\q / (f\q /1e6) ) ; normalize value to MicroSeconds : 1e6 = 1.000.000 = 1Mhz
EndProcedure

Procedure.l HSVadjustRBG(RGB.l, h.f, s.f, v.f)
  ; ======================================================================
  ; NAME : HSVadjustRBG
  ; DESC : Do a HSV-Color Space adjust at a RGB color of 24/32 Bit
  ; DESC : it will keep the original Alpha
  ; VAR(RGB.l) : 24/32 Bit RGB Color
  ; VAR(h.f) : Hue shift (in degrees) [0..360]
  ; VAR(s.f) : saturation multiplier (scalar)
  ; VAR(v.f) : value multiplier (scalar)
  ; RET.l :  The HSV adjusted color as RGB-Color-Value 
  ; ======================================================================
    
    Protected.f vsu, vsw
    Protected.f r, g, b
    Protected.a A ; Alpha value
    
    r = Red(RGB)
    g = Green(RGB)
    b = Blue(RGB)
    A = Alpha(RGB)
    
    vsu = v * s * Cos(Radian(h))
    vsw = v * s * Sin(Radian(h))
    
    r = (0.299*v + 0.701*vsu + 0.168*vsw) * r + (0.587*v - 0.587*vsu + 0.330*vsw) * g + (0.114*v - 0.114*vsu - 0.497*vsw) * b
    g = (0.299*v - 0.299*vsu - 0.328*vsw) * r + (0.587*v + 0.413*vsu + 0.035*vsw) * g + (0.114*v - 0.114*vsu + 0.292*vsw) * b
    b = (0.299*v - 0.300*vsu + 1.250*vsw) * r + (0.587*v - 0.588*vsu - 1.050*vsw) * g + (0.114*v + 0.886*vsu - 0.203*vsw) * b
    
    If r > 255 
      r = 255
    ElseIf r < 0 
      r = 0
    EndIf
    
    If g > 255 
      g = 255
    ElseIf g < 0 
      g = 0
    EndIf
    
    If b > 255 
      b = 255
    ElseIf b < 0 
      b = 0
    EndIf

    ProcedureReturn RGBA(Int(r), Int(g), Int(b),  A)
  EndProcedure
    
Structure M3
  m11.f : m12.f : m13.f
  m21.f : m22.f : m23.f
  m31.f : m32.f : m33.f
EndStructure

Procedure.i Get_HSVadjustRGB_Matrix (*m.M3, h.f, s.f, v.f)
    Protected.f vsu, vsw
    
    vsu = v * s * Cos(Radian(h))
    vsw = v * s * Sin(Radian(h))
    
    With *m
      \m11 = 0.299*v + 0.701*vsu + 0.168*vsw
      \m12 = 0.587*v - 0.587*vsu + 0.330*vsw
      \m13 = 0.114*v - 0.114*vsu - 0.497*vsw
      
      \m21 = 0.299*v - 0.299*vsu - 0.328*vsw
      \m22 = 0.587*v + 0.413*vsu + 0.035*vsw
      \m23 = 0.114*v - 0.114*vsu + 0.292*vsw
      
      \m31 = 0.299*v - 0.300*vsu + 1.250*vsw
      \m32 = 0.587*v - 0.588*vsu - 1.050*vsw
      \m33 = 0.114*v + 0.886*vsu - 0.203*vsw
    EndWith
    
    ProcedureReturn *m   
  EndProcedure
  
  Procedure.i Get_HSVadjustRBG_VecMatrix (*m.VECf::TMatrix, h.f, s.f, v.f)
    Protected.f vsu, vsw
    
    vsu = v * s * Cos(Radian(h))
    vsw = v * s * Sin(Radian(h))
    
    With *m
      \m11 = 0.299*v + 0.701*vsu + 0.168*vsw
      \m12 = 0.587*v - 0.587*vsu + 0.330*vsw
      \m13 = 0.114*v - 0.114*vsu - 0.497*vsw
      ;\m14 = 0
      
      \m21 = 0.299*v - 0.299*vsu - 0.328*vsw
      \m22 = 0.587*v + 0.413*vsu + 0.035*vsw
      \m23 = 0.114*v - 0.114*vsu + 0.292*vsw
      ;\m24 = 0
      
      \m31 = 0.299*v - 0.300*vsu + 1.250*vsw
      \m32 = 0.587*v - 0.588*vsu - 1.050*vsw
      \m33 = 0.114*v + 0.886*vsu - 0.203*vsw
      \m34 = 0
      
      ;\m41 = 0
      ;\m42 = 0
      ;\m43 = 0
      ;\m44 = 0
    EndWith
    
    ProcedureReturn *m   
  EndProcedure

  Procedure.l HSVadjustRBG_MX(RGB.l, *Matrix.M3)
  ; ======================================================================
  ; NAME : HSVadjustRBG_MX
  ; DESC : Do a HSV-Color Space adjust at a RGB color of 24/32 Bit
  ; DESC : with Matrix caclulation. 
  ; VAR(RGB.l) : 24/32 Bit RGB Color
  ; VAR(m.M3) : Matrix
  ; RET.l :  The HSV adjusted color as RGB-Color-Value 
  ; ======================================================================
    
    Protected.f vsu, vsw
    Protected.f r, g, b
     
    r = Red(RGB)
    g = Green(RGB)
    b = Blue(RGB)
     
    ;( V  0      0  )
    ;( 0  VSU  -VSW )
    ;( 0  VSW  -VSU )
    
    ; m11 = 0.299*v + 0.701*vsu + 0.168*vsw
    ; m12 = 0.587*v - 0.587*vsu + 0.330*vsw
    ; m13 = 0.114*v - 0.114*vsu - 0.497*vsw
    
    ; m21 = 0.299*v - 0.299*vsu - 0.328*vsw
    ; m22 = 0.587*v + 0.413*vsu + 0.035*vsw
    ; m23 = 0.114*v - 0.114*vsu + 0.292*vsw
    
    ; m31 = 0.299*v - 0.300*vsu + 1.250*vsw
    ; m32 = 0.587*v - 0.588*vsu - 1.050*vsw
    ; m33 = 0.114*v + 0.886*vsu - 0.203*vsw
    
    If *Matrix
      With *Matrix
        r = m11 * r + m12 * g + m13 * b
        g = m21 * r + m22 * g + m23 * b
        b = m31 * r + m32 * g + m33 * b
      EndWith
    
      If r > 255 
        r = 255
      ElseIf r < 0 
        r = 0
      EndIf
      
      If g > 255 
        g = 255
      ElseIf g < 0 
        g = 0
      EndIf
      
      If b > 255 
        b = 255
      ElseIf b < 0 
        b = 0
      EndIf
    EndIf
    ProcedureReturn RGBA(Int(r), Int(g), Int(b),  Alpha(RGB))
  EndProcedure
  
  Procedure.l HSVadjustRBG_SSE(RGB.l, *Matrix.VECf::TMatrix)
  ; ======================================================================
  ; NAME : HSVadjustRBG_SSE
  ; DESC : Do a HSV-Color Space adjust at a RGB color of 24/32 Bit
  ; DESC : with Matrix caclulation. 
  ; VAR(RGB.l) : 24/32 Bit RGB Color
  ; VAR(m.M3) : Matrix
  ; RET.l :  The HSV adjusted color as RGB-Color-Value 
  ; ======================================================================
    
    Protected.f vsu, vsw
    Protected IN.VECf::TVector
    Protected OUT.VECf::TVector
    
;     Protected *OUT.VECf::TVector = @OUT
;     Protected *IN.VECf::TVector = @IN   
    
    With IN  
      \x = Red(RGB)
      \y = Green(RGB)
      \z = Blue(RGB)
    EndWith
        
    VECf::Vector_X_Matrix(Out, IN, *Matrix)
    ; ASM_Vector_X_Matrix(RAX, RDX, RCX)     
    With OUT
      If \x > 255 
        \x = 255
      ElseIf \x < 0 
        \x = 0
      EndIf
      
      If \y > 255 
        \y = 255
      ElseIf \y < 0 
        \y = 0
      EndIf
      
      If \z > 255 
        \z = 255
      ElseIf \z < 0 
        \z = 0
      EndIf
    EndWith
  
    ProcedureReturn RGBA(Int(OUT\x), Int(OUT\y), Int(OUT\z), Alpha(RGB))
  EndProcedure

  EnableExplicit
  
  Define.q ticks1, ticks2, ticks3, ticks4
  Define.q time1, time2, time3, time4
  Define.i I  
  Define.l col, newcol
  Define.s msg
  
  col = RGB(120, 200, 99)
  #Pixels = 1920*1080
  
  ; --------------------------------------------------------
  ; Classic Version for ajustting 1 Pixel (Matrix caclulation included)
  time1 = ElapsedMicroSeconds()
  ticks1 = ReadRuntimeCounter()
  For I = 1 To #Pixels 
     newCol = HSVadjustRBG(col, 33, 1.1, 0.7)
  Next
  ticks1 = ReadRuntimeCounter() - ticks1
  time1 = ElapsedMicroSeconds() -  time1
    
  ; --------------------------------------------------------
  ; Classic Version with precaculated Matrix
  ; this is much faster if adjusting more Pixels with same Parameters
  Define hsvMatrix.M3   
  time2 = ElapsedMicroSeconds()
  ticks2 = ReadRuntimeCounter()
  Get_HSVadjustRGB_Matrix(hsvMatrix, 33, 1.1, 0.7)
  For I = 1 To #Pixels 
     newCol = HSVadjustRBG_MX(col, hsvMatrix)
  Next
  ticks2 = ReadRuntimeCounter() - ticks2
  time2 = ElapsedMicroSeconds() - time2
  
  ; --------------------------------------------------------
  
  Define VecMatrix.VECf::TMatrix
  time3 = ElapsedMicroSeconds()
  ticks3 = ReadRuntimeCounter()
  Get_HSVadjustRBG_VecMatrix(VecMatrix, 33, 1.1, 0.7)
  For I = 1 To #Pixels 
     newCol = HSVadjustRBG_SSE(col, VecMatrix)
  Next
  ticks3 = ReadRuntimeCounter() - ticks3
  time3 = ElapsedMicroSeconds() - time3

  msg = "Classic Float  version: ticks =  " + Str(ticks1 / 1e6) + " Mio ; " + StrF(time1/1000,1) + "ms ; " + Str(ticks1/time1) + " MHz" + #CRLF$
  msg + "Classic Matrix version: ticks =  " + Str(ticks2 / 1e6) + " Mio ; " + StrF(time2/1000,1) + "ms ; " + Str(ticks2/time2) + " MHz"  + #CRLF$
  msg + "SSE-VEC-Matrix version: ticks =  " + Str(ticks3 / 1e6) + " Mio ; " + StrF(time3/1000,1) + "ms ; " + Str(ticks3/time3) + " MHz" 
  
  SetClipboardText(msg)
  MessageRequester("CPU Ticks of HSVadjustRBG of a FullHD Picture = ", msg , #PB_MessageRequester_Info)
  
;   Float/ Int
;   240/160 Mio , 126/ 80ms: new Intel I7 8565U (Laptop Win10)
;   540/300 Mio , 142/ 80ms: Ryzen 5800X (Dektop Win 10))
;   780/370 Mio , 202/110ms: old Intel I7 Laptop (2016, Win7) 
;   970/650 Mio : old AMD Laptop (2011, Win7)
  
Last edited by SMaag on Sat Sep 09, 2023 2:52 pm, edited 4 times in total.
User avatar
Piero
Addict
Addict
Posts: 1166
Joined: Sat Apr 29, 2023 6:04 pm
Location: Italy

Re: HSL/HSV to RGB

Post by Piero »

Again, thanks to all.
I think It also would be interesting how to implement "superfast color tables" to apply e.g. to the Z dimension of 3D graphs...

PS: for example, there's a system app called "Grapher" on Mac that does stuff... in theory you can even make animations... but it works very %$#@& (Monterey)...............................
boddhi
Enthusiast
Enthusiast
Posts: 526
Joined: Mon Nov 15, 2010 9:53 pm

Re: HSL/HSV to RGB

Post by boddhi »

Hi, there,

I've arrived a little after the battle :)
But, below, if needed, the procedures used by PB that can be found in the Purebasic IDE source code:

HSV to RGB :

Code: Select all

Procedure HSVToRGB(h.f,s.f,v.f)
  Protected.f i,f,p,q,t
  Protected.f r,g,b
  
  ; make sure it is always less than 360, else we get black
  h=Mod(h,360.0)
  If s=0
    r=v
    g=v
    b=v
  Else
    h/60.0
    i=Round(h,0)
    f=h-i
    p=v*(1.0-s)
    q=v*(1.0-s*f)
    t=v*(1.0-s*(1.0-f))
    Select i
      Case 0:r=v:g=t:b=p
      Case 1:r=q:g=v:b=p
      Case 2:r=p:g=v:b=t
      Case 3:r=p:g=q:b=v
      Case 4:r=t:g=p:b=v
      Case 5:r=v:g=p:b=q
    EndSelect
  EndIf
  ProcedureReturn RGB(Int(r*255),Int(g*255),Int(b*255))
EndProcedure

HSL to RGB :

Code: Select all

Procedure.f HSLToRGBComponent(q1.f,q2.f,h.f)
  If h>=360.0
    h-360.0
  ElseIf h<0.0
    h+360.0
  EndIf
  If h<60.0
    ProcedureReturn q1+(q2-q1)*h/60.0
  ElseIf h<180.0
    ProcedureReturn q2
  ElseIf h<240.0
    ProcedureReturn q1+(q2-q1)*(240.0-h)/60.0
  Else
    ProcedureReturn q1
  EndIf
EndProcedure
;
Procedure HSLToRGB(h.f,s.f,l.f)
  Protected.f p1,p2
  Protected.f r,g,b
  
  ; make sure it is always less than 360, else we get black
  h=Mod(h,360.0)
  If l<=0.5
    p2=l*(1.0+s)
  Else
    p2=l+s-l*s
  EndIf
  p1=2.0*l-p2
  r=HSLToRGBComponent(p1,p2,h+120)
  g=HSLToRGBComponent(p1,p2,h)
  b=HSLToRGBComponent(p1,p2,h-120)
  ProcedureReturn RGB(Int(r*255),Int(g*255),Int(b*255))
EndProcedure

Hue to RGB :

Code: Select all

Procedure HueToRGB(h.f)
  Protected.f r, g, b
  
  ; make sure it is always less than 360, else we get black
  h = Mod(h, 360.0)
  
  If h < 60
    r = 1
    g = h / 60
    b = 0
  ElseIf h < 120
    r = 1 - (h - 60) / 60
    g = 1
    b = 0
  ElseIf h < 180
    r = 0
    g = 1
    b = (h - 120) / 60
  ElseIf h < 240
    r = 0
    g = 1 - (h - 180) / 60
    b = 1
  ElseIf h < 300
    r = (h - 240) / 60
    g = 0
    b = 1
  Else
    r = 1
    g = 0
    b = 1 - (h - 300) / 60
  EndIf
  ProcedureReturn RGB(Int(r * 255), Int(g * 255), Int(b * 255))
EndProcedure
If my English syntax and lexicon are incorrect, please bear with Google translate and DeepL. They rarely agree with each other!
Except on this sentence...
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

Result of new Test with SSE Vector Matrix version for full HD.

Classic Float version: ticks = 556 Mio ; 146.4ms ; 3799 MHz
Classic Matrix version: ticks = 388 Mio ; 102.2ms ; 3799 MHz
SSE-VEC-Matrix version: ticks = 258 Mio ; 68.0ms ; 3799 MHz

My SSE version is not 100% optimized for the ColorSpace use.! There are some possiblities to optimize and speed up again!

I can't not post the complete Code for the SSE! It's to much!
It use a few external modules form PureBasic Framework. If you want to test it, download the additional modules!

I update my Testcode from the previous post! See there how to get the Code

My test ist just a timing test. I did't test it with real images to see the correct function!

Did anyone of you wrote a test code with a real Image, to see the correct function?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3944
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: HSL/HSV to RGB

Post by wilbert »

SMaag wrote: Sat Sep 09, 2023 2:32 pmDid anyone of you wrote a test code with a real Image, to see the correct function?
The transform matrix seems to work fine with a real image but I tried it with my own code.
I precalculate the transform matrix and am using SSE2 integer math for the matrix multiplication.
I also am doing all pixels in one loop so no function calls for each pixel.
With this approach a HSV transform on a 1920x1080 pixel image on my computer takes about 4 ms.
Windows (x64)
Raspberry Pi OS (Arm64)
SMaag
Enthusiast
Enthusiast
Posts: 353
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: HSL/HSV to RGB

Post by SMaag »

With this approach a HSV transform on a 1920x1080 pixel image on my computer takes about 4 ms.
only 4ms for Full HD what is about 2Mio Pixels. How are you doing that???
I tested my SSE Code again and optimized it. But the effect is only 2ms. The SSE calculation itself needs aprox. 60ms, the complete rest 6ms.

can you post your code,?

here is my SSE Code for Vectror * Matrix
it needs about 60ms for 2Mio calls on a Ryzen 5800X at 3.8Ghz

Code: Select all

Procedure.i  Vector_X_Matrix(*OUT.TVector, *IN.TVector, *Matrix.TMatrix)
  	  ; translated from the FreePascal Wiki at https://wiki.freepascal.org/SSE/de	  
      !MOV     REGD,  [p.p_IN]      ; load Adress of IN.VEctor
      !MOV     REGA,  [p.p_Matrix]  ; load Adress of Matrix
      ;!MOVUPS  XMM2, [REGD]
        !MOVLPS XMM2, [REGD]        ; split 128Bit MOVUPS to 2x 64 Bit MOVLPS, MOVHPS. It's faster because of unaligned Memory
        !MOVHPS XMM2, [REGD+8]      ; a modern CPU do 2 64Bit load/save parallel
        
      ; we use Shuffle command and vertical Add instead of horizontal Add, because Shuffle it's faster 
      ; Line 0
      !PSHUFD  XMM0, XMM2, 00000000b
      ;!MOVUPS  XMM3, [REGA + $00]
        !MOVLPS XMM3, [REGA + $00]
        !MOVHPS XMM3, [REGA + $08]
      !MULPS   XMM0, XMM3
      
      ; Line 1
      !PSHUFD  XMM1, XMM2, 01010101b
      ;!MOVUPS  XMM3, [REGA + $10]
        !MOVLPS XMM3, [REGA + $10]
        !MOVHPS XMM3, [REGA + $18]
      !MULPS   XMM1, XMM3
      !ADDPS   XMM0, XMM1
      
      ; Line 2
      !PSHUFD  XMM1, XMM2, 10101010b
      ;!MOVUPS  XMM3, [REGA + $20]
        !MOVLPS XMM3, [REGA + $20]
        !MOVHPS XMM3, [REGA + $28]
      !MULPS   XMM1, XMM3
      !ADDPS   XMM0, XMM1
      
      ; Line 3
      !PSHUFD  XMM1, XMM2, 11111111b
      ;!MOVUPS  XMM3, [REGA + $30]         
        !MOVLPS XMM3, [REGA + $30]
        !MOVHPS XMM3, [REGA + $38]
      !MULPS   XMM1, XMM3
      !ADDPS   XMM0, XMM1
      
      ; Return Result
      !MOV     REGA, [p.p_OUT] 
      !MOVUPS  [REGA], XMM0   
  EndProcedure
Post Reply