User Tools

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
isp:hsl-_rgb [2019/05/27 15:06] – [Step-by-step algorithm] Igor Yefmovisp:hsl-_rgb [2022/04/04 23:32] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== HSL->RGB ====== ====== HSL->RGB ======
-The text below is based on an Excel workbook attached to this page: {{ :isp:hsl-rgb.xlsm |}}. That Excel file provides a sort of playground where you can try various input data and see the final results, both "precise" and "optimized", as well as the discrepancy between the two methods.+The text below is based on {{ https://docs.google.com/spreadsheets/d/1K3kp-Ofc46DEjLrNZLLeVAv1sQBafWe9N2UWncYa6CM/edit?usp=sharing SUB2r ISP Excel workbook}}. That workbook provides a sort of playground where you can try various input data and see the final results, both "precise" and "optimized"((lower precision)), as well as the discrepancy between the two methods.
 ===== Preface ===== ===== Preface =====
-Much is written and is available on the color space conversion from HSL to RGB (for example this [[https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB|Wikipedia article]]). However not only this is not meant to be used with integer arithmetics but is also very costly due to division operations. And once you realize that a 4K video running at 60FPS needs to do this conversion half a billion times a second(!) you see why this needs to be heavily optimized ;-)+Much is written and is available on the color space conversion from HSL to RGB (for example this [[https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB|Wikipedia article]]). Howevernot only this is not meant to be used with integer arithmeticsbut is also very costly due to division operations. And once you realize that a 4K video running at 60FPS needs to do this conversion half a billion times a second(!) you see why this needs to be heavily optimized ;-)
  
 The below article details the way to perform this conversion without the use of division operations with high enough precision as to satisfy the imaging pipeline quality requirements for the SUB2r camera based on Artix-7 100T FPGA. The below article details the way to perform this conversion without the use of division operations with high enough precision as to satisfy the imaging pipeline quality requirements for the SUB2r camera based on Artix-7 100T FPGA.
Line 10: Line 10:
   * \(H\) (hue) is a signed ''14''-bit field that maps into \([-180°..+180°]\) range   * \(H\) (hue) is a signed ''14''-bit field that maps into \([-180°..+180°]\) range
   * \(S\) (saturation) is an ''8''-bit unsigned value which maps into \([0\%..100\%]\) range   * \(S\) (saturation) is an ''8''-bit unsigned value which maps into \([0\%..100\%]\) range
-  * \(L\) (luminosity) is a ''9''-bit unsigned value that maps into \([0\%..100\%]\) range just like the \(saturation\) above+  * \(L\) (luminosity) is a ''9''-bit unsigned value that maps into \([0\%..100\%]\) range, very much like the \(saturation\) above
  
 The output of this conversion is an \(RGB\) triplet ''24''-bits wide with ''8'' bits unsigned value per color channel in range \([0..255]\) The output of this conversion is an \(RGB\) triplet ''24''-bits wide with ''8'' bits unsigned value per color channel in range \([0..255]\)
Line 20: Line 20:
 \[\frac{1}{x} = \frac{1*N}{x*N}\] \[\frac{1}{x} = \frac{1*N}{x*N}\]
 and choosing \(N\) such that \(x*N\) is a whole power of \(2\) we have an optimization where a division is replaced by a pair of a multiplication followed by a (super cheap!) bit-shift operation by \(Z\) bits: and choosing \(N\) such that \(x*N\) is a whole power of \(2\) we have an optimization where a division is replaced by a pair of a multiplication followed by a (super cheap!) bit-shift operation by \(Z\) bits:
-\[\frac{C}{x} = C*\frac{1}{x} = C*\frac{1*N}{x*N} = C*\frac{N}{2^Z} = [(C*N)>>Z]\]+\[\frac{C}{x} = C*\frac{1}{x} = C*\frac{1*N}{x*N}\Bigg|_{\{x*N=2^Z\}} = C*\frac{N}{2^Z} = [(C*N) \gg Z]\]
 The value \(Z\) depends on the needed precision and, of course, the higher the \(Z\) the less precision loss there will be in the end. The value \(Z\) depends on the needed precision and, of course, the higher the \(Z\) the less precision loss there will be in the end.
  
Line 28: Line 28:
  
 The last part, losing the 14 LSB, doesn't have to be done right away and with the goal of preserving as much precision as possible this operation can be postponed until later time (just need to remember that the "real" value is now augmented by that \(2^{14}\) factor and adjust accordingly). The last part, losing the 14 LSB, doesn't have to be done right away and with the goal of preserving as much precision as possible this operation can be postponed until later time (just need to remember that the "real" value is now augmented by that \(2^{14}\) factor and adjust accordingly).
-===== Step-by-step algorithm ===== 
  
-==== Precise calculations ==== +===== Precise calculations ===== 
-As a refresher this is how Wikipedia lists the way to convert HSL to RGB:+As a refresher this is how [[https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB|Wikipedia]] lists the way to convert HSL to RGB:
  
-Given a color with hue \(H ∈ [0°, 360°]\), saturation \(S\)_{HSL} ∈ [0, 1]\), and lightness \(L ∈ [0, 1]\), we first find chroma:+Given a color with hue \(H ∈ [0°, 360°]\), saturation \(S_{HSL} ∈ [0, 1]\), and lightness \(L ∈ [0, 1]\), we first find chroma:
 \[ C = (1 - \left\vert 2 L - 1 \right\vert) \times S_{HSL} \] \[ C = (1 - \left\vert 2 L - 1 \right\vert) \times S_{HSL} \]
  
Line 58: Line 57:
 \[m=L-C/2\] \[m=L-C/2\]
 \[(R,G,B)=(R_1+m,G_1+m,B_1+m)\] \[(R,G,B)=(R_1+m,G_1+m,B_1+m)\]
 +
 +===== Step-by-step algorithm (8x3 bit channel, 24-bit RGB) =====
 +Armed with the above information we can now compile the necessary sequence of calculations and format it into an easy-to-use table:
 +
 +^  #  ^  name  ^  math excerpt from Wikipedia  ^  bits  ^  range  ^  C-like code  ^  factor((multiply by that much to get the "real" value))  ^
 +| 1 | \[h\] | \[hue(pixel)\] |  ''14''\\ signed  |  \[-8192..+8191\] mapped into \(-180^\circ..+180^\circ\)  | <code>h = pixel.hue();</code> | \[\frac{45}{2^{11}}\] |
 +| 2 | \[s\] | \[sat(pixel)\] |  ''8''  | \[0..255\] | <code>s = pixel.sat();</code> | \[1\] |
 +| 3 | \[l\] | \[luma(pixel)\] |  ''9''  |  \[0..511\] mapped into \(0..255\)  | <code>l = pixel.luma();</code> | \[^1/_2\] |
 +| 4 | \[H\] | \[(H+360^\circ) \bmod 360^\circ\] |  ''12''  |  \[0..4096\] mapped into \(0^\circ..+360^\circ\)  | <code>h1 = (h >> 2);
 +H = (h1 > 0 ? h1 : h1 + 4096);</code> ((optimized from<code>H = ((h > 0 ? h : h + 16384) >> 2);</code>)) | \[\frac{45}{2^{9}}\] |
 +| | \(S\)((not needed for calculations)) | \[\frac{S}{255}\] | | \[0..1\] | | |
 +| | \(L\)((not needed for calculations)) | \[\frac{S}{511}\] | | \[0..1\] | | |
 +| 5 | \[L^\prime\] | \[1-|2L-1|\] |  ''9''  | \[0..510\] | <code>L_prime = 2^9 - abs(2 * L - 2^9);</code> | \[\frac{1}{2^9}\] |
 +| 6 | \[C\] | \[L^\prime \times S\] |  ''9''  | \[0..510\] | <code>C = ((L_prime * s) >> 8);</code> | \[\frac{1}{2^9}\] |
 +| 7 | \[H^\prime\] | \[\frac{H}{60^\circ}\] |  ''12''  |  \[0..4095\] mapped into \(0..6\)  | <code>Hp = ((H * 273) >> 9);</code> | \[\frac{45}{2^{14}}\] |
 +| 8 | \[H^\prime_2\] | \[H^\prime \bmod 2\] |  ''10''  | \[0..727\] | <code>Hp2 = Hp - (91 * 2^3) * ((Hp * 90) >> 16)</code>((optimized from <code>Hp2 = Hp % (2^3 * 273 / 3);</code>)) | \[\frac{45}{2^{14}}\] |
 +| 9 | \[H^\prime_{2-1}\] | \[H^\prime \bmod 2 - 1\] |  ''10''\\ signed  | \[-364..+363\] | <code>Hp2m1 = Hp2 - 2^2 * 91;</code> | \[\frac{45}{2^{14}}\] |
 +| 11 | \[H^\prime_{final}\] | \[1-|H^\prime \bmod 2 - 1|\] |  ''9''  |  \[0..364\] mapped into \(0..1\)  | <code>Hpf = 91 * 2^2 - abs(Hp2m1);</code> | \[\frac{45}{2^{14}}\] |
 +| 12 | \[X\] | \[C \times H^\prime_{final}\] |  ''9''  | \[0..510\] | <code>X = ((C * Hpf * 180) >> 16);</code> | \[\frac{1}{2^9}\] |
 +| 13 | \[(R_1,G_1,B_1)\] | multi-branch |  ''9''  | \[0..510\] | <code>if(Hp < 364*1){
 +  /*(C,X,0)*/
 +}else if(Hp < 364*2){
 +  /*(X,C,0)*/
 +}//...</code> | \[\] |
 +| 14 | \[m\] | \[L - C/2\] |  ''9''  | \[0..510\] | <code>m = l - C / 2;</code> | \[\frac{1}{2^9}\] |
 +| 15 | \[(R,G,B)\] | \[(R_1+m,G_1+m,B_1+m)\] |  ''8''  | \[0..255\] | <code>R=R1+m; G=G1+m; B=B1+m;</code> | \[1\] |
 +
 +
 +===== Step-by-step algorithm (10x3 bit channel, 30-bit RGB) =====
 +Armed with the above information we can now compile the necessary sequence of calculations and format it into an easy-to-use table:
 +
 +^  #  ^  name  ^  math excerpt from Wikipedia  ^  bits  ^  range  ^  C-like code  ^  factor((multiply by that much to get the "real" value))  ^
 +| 1 | \[H\] | \[hue(pixel)\] |  ''14''\\ signed  |  \[-8192..+8191\] mapped into \(-180^\circ..+180^\circ\)  | <code>H = pixel.hue();</code> | \[\frac{180}{2^{13}}\] |
 +| 2 | \[S\] | \[sat(pixel)\] |  ''9''  | \[0..511\] | <code>S = pixel.sat();</code> | \[^1/_2\] |
 +| 3 | \[L\] | \[luma(pixel)\] |  ''12''  |  \[0..4095\] mapped into \(0..255\)  | <code>L = pixel.luma();</code> | \[\frac{1}{2^4}\] |
 +| 4 | \[h\] | \[(H+360^\circ) \bmod 360^\circ\] |  ''14''  |  \[0..16383\] mapped into \(0^\circ..+360^\circ\)  | <code>h = (H > 0 ? H : H + 16383);</code> | \[\frac{360}{2^{14}}\] |
 +| 5 | \[L^\prime\] | \[1-|2L-1|\] |  ''12''  | | <code>L_prime = 2^12 - abs(2 * L - 2^12);</code> | \[\frac{1}{2^{12}}\] |
 +| 6 | \[C\] | \[L^\prime \times S\] |  ''21''  | | <code>C = L_prime * s;</code> | \[\frac{1}{2^{21}}\] |
 +| 7 | \[H^\prime\] | \[\frac{H}{60^\circ}\] |  ''23''  |  \[0..2^23-1\] mapped into \(0..6\)  | <code>Hp = H * 273;</code> | \[\frac{180}{2^{27}}\] |
 +| 8 | \[H^\prime_2\] | \[H^\prime \bmod 2\] |  ''21''  | | <code>Hp2 = Hp - (91 * 2^14) * (Hp * 90) >> 27)</code>((optimized from <code>Hp2 = Hp % (2^14 * 273 / 3);</code>)) | \[\frac{180}{2^{27}}\] |
 +| 9 | \[H^\prime_{2-1}\] | \[H^\prime \bmod 2 - 1\] |  ''21''\\ signed  | | <code>Hp2m1 = Hp2 - 2^13 * 91;</code> | \[\frac{180}{2^{27}}\] |
 +| 11 | \[H^\prime_{final}\] | \[1-|H^\prime \bmod 2 - 1|\] |  ''21''  | | <code>Hpf = 91 * 2^13 - abs(Hp2m1);</code> | \[\frac{180}{2^{27}}\] |
 +| 12 | \[X\] | \[C \times H^\prime_{final}\] |  ''21''  | | <code>X = ((C * Hpf * 180) >> 27);</code> | \[\frac{1}{2^21}\] |
 +| 13 | \[(R_1,G_1,B_1)\] | multi-branch |  ''21''  | | <code>if(Hp < 745472*1){
 +  /*(C,X,0)*/
 +}else if(Hp < 745472*2){
 +  /*(X,C,0)*/
 +}//...</code> | \[\] |
 +| 14 | \[m\] | \[L - C/2\] |  ''12''  | | <code>m = L - (C / 2^9) / 2;</code> | \[\frac{1}{2^{12}}\] |
 +| 15 | \[(R,G,B)\] | \[(R_1+m,G_1+m,B_1+m)\] |  ''10''  | \[0..1023\] | <code>R=(R1/2^9+m)/2^2; G=(G1/2^9+m)/2^2; B=(B1/2^9+m)/2^2;</code> | \[1\] |
 +

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information