Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision |
isp:hsl-_rgb [2021/07/24 11:02] – [Division-less division] Igor Yefmov | isp:hsl-_rgb [2021/07/24 11:40] – [HSL->RGB] Igor Yefmov |
---|
====== 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 a {{ 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", 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]]). 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 ;-) |
\[(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 ===== | ===== 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: | Armed with the above information we can now compile the necessary sequence of calculations and format it into an easy-to-use table: |
| |