Hallo zusammen, bei der Entwicklung eines Weihnachtsgeschenkes ist eine 12Bit HSV nach RGB Routine abgefallen, die ich hier gerne veröffentlichen möchte. Die Werte für H, S und V können zwischen 0 und 4095 liegen. Gruß Frank
Hallo zusammen, leider hat sich ein kleiner Fehler in die Routine eingeschlichen. In der Berechnung für die Variablen q fehlt ein Typecast. Die korrekte Zeile lautet:
1 | q = (uint16_t)( ( value->V * ( ( 2796885 - ( value->S * (uint32_t)f ) ) / 683 ) ) / 4096 ); |
Im Anhang mal ein vollständiger Plot aller RGB-Werte für den kompletten HSV-Farbraum berechnet über die Funktion. Der Plot legt die Werte H = {0..4095}; S = 4095 und V = 4095 zu Grunde. Gruß Frank
Anbei der Code etwas abgeändert damit er ohne den struct funktioniert.
1 | uint16_t q = 0; |
2 | uint16_t p = 0; |
3 | uint16_t t = 0; |
4 | |
5 | uint8_t i = 0; |
6 | uint16_t f = 0; |
7 | |
8 | if (s == 0) { |
9 | |
10 | *red = *green = *blue = v; |
11 | |
12 | } else { |
13 | |
14 | i = h / 683; |
15 | f = h % 683; |
16 | p = (uint16_t)( ( v * ( 4095 - s ) ) / 4096 ); |
17 | q = (uint16_t)( ( v * ( ( 2796885 - ( s * (uint32_t)f ) ) / 683 ) ) / 4096 ); |
18 | t = (uint16_t)( ( ( v * ( ( 2796885 - ( s * ( 683 - ( (uint32_t)f ) ) ) ) / 683 ) ) ) / 4096 ); |
19 | |
20 | switch(i) { |
21 | case 0: |
22 | *red = v; |
23 | *green = t; |
24 | *blue = p; |
25 | break; |
26 | |
27 | case 1: |
28 | *red = q; |
29 | *green = v; |
30 | *blue = p; |
31 | break; |
32 | |
33 | case 2: |
34 | *red = p; |
35 | *green = v; |
36 | *blue = t; |
37 | break; |
38 | |
39 | case 3: |
40 | *red = p; |
41 | *green = q; |
42 | *blue = v; |
43 | break; |
44 | |
45 | case 4: |
46 | *red = t; |
47 | *green = p; |
48 | *blue = v; |
49 | break; |
50 | |
51 | default:
|
52 | *red = v; |
53 | *green = p; |
54 | *blue = q; |
55 | break; |
56 | }
|
57 | |
58 | }
|
@ Micha, vielleicht kannst du deine Änderung komplettieren? Zumindest die Var.deklaration red, green, blue, fehlt hier, nur damit für andere keine Verwirrung entsteht
Ich habe die halbe Nacht damit verbracht, den Code etwas anzupassen, vielleicht ist er für andere nützlich. Ursprünglich habe ich vorallem die Divisionen durch skalierte Multiplikationen ersetzt, nach debuggen habe ich die Formeln nochmal umgebaut, insbesondere die switch-case Fallunterscheidung stimmte nicht mit dem Artikel von Wikipedia überein. Nach weiterem Test tut diese Version das was sie soll. Im Prinzip ist alles auf 12bit skaliert, also alles was nach Wikipediaartikel auf [0..1) definiert ist, ist hier auf [0..4095] skaliert. Als verbliebene Ungenauigkeit kommen t und evtl. weitere teilweise nicht ganz an 4095 heran (z.B. Maxwert 4088, wenn Hue als Mischfarbe).
1 | /********header************************************************************************************
|
2 | *
|
3 | * pwm_hsv.c pwm_hsv.h
|
4 | *
|
5 | *********************************************************************************************/
|
6 | |
7 | #ifndef pwm_hsv_h
|
8 | #define pwm_hsv_h
|
9 | |
10 | |
11 | typedef uint16_t ( *rgb_triplet_ptr_t)[3]; |
12 | |
13 | typedef uint16_t rgb_triplet_t[3]; |
14 | |
15 | rgb_triplet_t * hsv2rgb12(uint16_t h, uint16_t s, uint16_t v); |
16 | |
17 | void Unittest_pwm_hsv(void); |
18 | |
19 | #endif
|
20 | |
21 | /********************************************************************************************
|
22 | *
|
23 | * pwm_hsv.c pwm_hsv.h 03.04.2012
|
24 | *
|
25 | * Author: Devmo
|
26 | ********************************************************************************************/
|
27 | #include <util/atomic.h> |
28 | #include <avr/cpufunc.h> |
29 | #include <avr/pgmspace.h> |
30 | |
31 | #include "pwm_hsv.h" |
32 | #include "../mod_math/mod_math.h" |
33 | |
34 | |
35 | |
36 | rgb_triplet_t rgb; |
37 | |
38 | #define TOP12 4095
|
39 | #define TOP12ceil 4096
|
40 | |
41 | #define TOP12_12 16769025 //4095*4095
|
42 | #define MUL_COEF_i_6_4096 6
|
43 | |
44 | rgb_triplet_t (* rgb_ptr) = &rgb; |
45 | uint16_t q; |
46 | uint16_t p; |
47 | uint16_t t; |
48 | uint8_t i; |
49 | uint32_t tmp32; |
50 | uint16_t f, tmp16, i16; |
51 | |
52 | rgb_triplet_t * hsv2rgb12(uint16_t h, uint16_t s, uint16_t v) //256clk ... 1223clk |
53 | {
|
54 | |
55 | // folgendes auskommentiert und oben deklariert für debugging:
|
56 | // rgb_triplet_t (* rgb_ptr) = &rgb;
|
57 | |
58 | // uint16_t q;
|
59 | // uint16_t p;
|
60 | // uint16_t t;
|
61 | |
62 | // uint8_t i;
|
63 | // uint32_t tmp32;
|
64 | // uint16_t f, tmp16;
|
65 | |
66 | if( s == 0 ) |
67 | {
|
68 | rgb[0] = v; |
69 | rgb[1] = v; |
70 | rgb[2] = v; |
71 | }
|
72 | else
|
73 | {
|
74 | |
75 | //--------------------------------------------------------------------------
|
76 | // i = h / (top12/6)
|
77 | //--------------------------------------------------------------------------
|
78 | |
79 | tmp32 = MAT_MULU32(h, (uint16_t)MUL_COEF_i_6_4096); //skaliert i: scaledres = wert i * 2^12 top max = 15bit |
80 | i16 = (uint16_t)tmp32; //zwischenergebnis: scaledres = ungerundeter wert i * 2^12 + (6) |
81 | i = (uint8_t)(tmp32 >> 12); //rückskalieren: scaledres/2^12 = wert, top max = 3bit (5) |
82 | |
83 | tmp16 = ((uint16_t)i << 12); //zwischenergebnis: scaledres = gerundeter wert i * 2^12 + (5) |
84 | |
85 | //--------------------------------------------------------------------------
|
86 | // F = h - h_floor +, F = [0..1)
|
87 | //--------------------------------------------------------------------------
|
88 | |
89 | f = i16 - tmp16; //f [0..1) skaliert : scaledres = wert f * 2^12 |
90 | |
91 | |
92 | |
93 | //--------------------------------------------------------------------------
|
94 | // p = v * (1 - s), mit p,s,v = [0..1) -> P_12 = ((V_12 * (top12 - S_12)_12)_24)>>12, mit scaled P,S,V = wert p,s,v * 2^12
|
95 | //--------------------------------------------------------------------------
|
96 | |
97 | // TOP12 - s
|
98 | tmp16 = ((uint16_t)TOP12 - s); |
99 | |
100 | // v * ans
|
101 | tmp32 = MAT_MULU32(v, tmp16); //scaledres = wert p * 2^24 //127clk //172clk |
102 | |
103 | // von 24 auf 12bit skalieren
|
104 | p = (uint16_t)(tmp32>>12); //rückskalieren: scaledres = wert p * 2^12 |
105 | |
106 | |
107 | //--------------------------------------------------------------------------
|
108 | // q = v * ( 1 - (s * f)), mit q,s,v, f = [0..1) -> Q_12 = ((V_12 * ((top24 - (S_12*F_12)_24)>>12)_12)_24)>>12
|
109 | //--------------------------------------------------------------------------
|
110 | |
111 | |
112 | // s * f
|
113 | tmp32 = MAT_MULU32(s, f); //top max = 24bit |
114 | |
115 | // TOP12_12 - ans
|
116 | tmp32 = ((uint32_t)TOP12_12 - tmp32 ); //top max = 24 bit, |
117 | |
118 | // von 24 auf 12bit skalieren
|
119 | tmp16 = (uint16_t)(tmp32 >> 12); |
120 | |
121 | // v * ans
|
122 | tmp32 = MAT_MULU32(v, tmp16); |
123 | |
124 | //von 24 auf 12 bit skalieren)
|
125 | q = (uint16_t)(tmp32 >> 12); |
126 | |
127 | |
128 | //--------------------------------------------------------------------------
|
129 | // t = v * (1 - s * (1 - f)), mit t,s,v, f = [0..1) -> T_12 = ((V_12 * (( TOP24 - (S_12 * (TOP12 - F_12)_12 )_24 )>>12)_12)_24)>>12
|
130 | //--------------------------------------------------------------------------
|
131 | |
132 | //TOP12 - F_12
|
133 | tmp16 = (uint16_t)TOP12 - f; |
134 | |
135 | |
136 | // S_12 * ans_12
|
137 | tmp32 = MAT_MULU32(s, tmp16); //top max = 24bit |
138 | |
139 | // TOP24 - ans_24
|
140 | tmp32 = ((uint32_t)TOP12_12 - tmp32 ); //top max = 24 bit, |
141 | |
142 | // von 24 auf 12bit skalieren
|
143 | tmp16 = (uint16_t)(tmp32 >> 12); |
144 | |
145 | |
146 | // V_12 * ans_12
|
147 | tmp32 = MAT_MULU32(v, tmp16); |
148 | |
149 | //von 24 auf 12 bit skalieren)
|
150 | t = (uint16_t)(tmp32 >> 12); //top max = 12 bit |
151 | |
152 | |
153 | switch(i) |
154 | {
|
155 | case 0: |
156 | case 6: |
157 | rgb[0] = v; |
158 | rgb[1] = t; |
159 | rgb[2] = p; |
160 | break; |
161 | |
162 | case 1: |
163 | rgb[0] = q; |
164 | rgb[1] = v; |
165 | rgb[2] = p; |
166 | break; |
167 | |
168 | case 2: |
169 | rgb[0] = p; |
170 | rgb[1] = v; |
171 | rgb[2] = t; |
172 | break; |
173 | |
174 | case 3: |
175 | rgb[0] = p; |
176 | rgb[1] = q; |
177 | rgb[2] = v; |
178 | break; |
179 | |
180 | case 4: |
181 | rgb[0] = t; |
182 | rgb[1] = p; |
183 | rgb[2] = v; |
184 | break; |
185 | |
186 | case 5: |
187 | rgb[0] = v; |
188 | rgb[1] = p; |
189 | rgb[2] = q; |
190 | break; |
191 | default:
|
192 | _NOP(); |
193 | }
|
194 | }
|
195 | |
196 | return rgb_ptr; |
197 | }
|
198 | |
199 | |
200 | |
201 | //--------------------------------------------------------------------------
|
202 | // Unit Test
|
203 | //--------------------------------------------------------------------------
|
204 | #include <avr/pgmspace.h> |
205 | |
206 | |
207 | |
208 | |
209 | uint16_t th, ts, tv; |
210 | |
211 | const uint16_t hsv_input_h[6]; |
212 | const uint16_t hsv_input_s[3]; |
213 | const uint16_t hsv_input_v[8]; |
214 | |
215 | typedef struct |
216 | {
|
217 | volatile rgb_triplet_t input; |
218 | volatile rgb_triplet_t output; |
219 | }
|
220 | struct_hsv_in_out_t; |
221 | |
222 | struct_hsv_in_out_t volatile hsv_res_vekt[8]; |
223 | |
224 | rgb_triplet_t * tmp_rgb_ptr; |
225 | |
226 | void Unittest_pwm_hsv(void) |
227 | {
|
228 | for (uint8_t l = 0; l <6; l++) |
229 | {
|
230 | for (uint8_t m = 0; m <3; m++) |
231 | {
|
232 | for (uint16_t n = 0; n<8; n++) |
233 | {
|
234 | th = (uint16_t) pgm_read_word(hsv_input_h + l); |
235 | ts = (uint16_t) pgm_read_word(hsv_input_s + m); |
236 | tv = (uint16_t) pgm_read_word(hsv_input_v + n); |
237 | |
238 | hsv_res_vekt[n].input[0] = th; |
239 | hsv_res_vekt[n].input[1] = ts; |
240 | hsv_res_vekt[n].input[2] = tv; |
241 | |
242 | tmp_rgb_ptr = hsv2rgb12(th, ts, tv); |
243 | |
244 | hsv_res_vekt[n].output[0] = (*tmp_rgb_ptr)[0]; |
245 | hsv_res_vekt[n].output[1] = (*tmp_rgb_ptr)[1]; |
246 | hsv_res_vekt[n].output[2] = (*tmp_rgb_ptr)[2]; |
247 | |
248 | _NOP(); |
249 | }
|
250 | _NOP(); |
251 | }
|
252 | _NOP(); |
253 | }
|
254 | }
|
255 | |
256 | const uint16_t hsv_input_h[6] PROGMEM = |
257 | {// rot grün blau gr-bl bl-rt rt-gr |
258 | 4095, 1365, 2730, 2047, 3412, 680 |
259 | };
|
260 | |
261 | const uint16_t hsv_input_s[3] PROGMEM = |
262 | {
|
263 | 0, 1023, 4095 |
264 | };
|
265 | |
266 | const uint16_t hsv_input_v[8] PROGMEM = |
267 | {
|
268 | 1, 3, 11, 35, 116, 380, 1248, 4095 |
269 | };
|
Woher kommt im Code die Zahl "2796885" ? Und wie sind die Formeln aus Franks Code mit der Grundlage des Wikipedia-Artikels zur Konvertierung HSV nach RGB vergleichbar? Ich sehe hier keinerlei Zusammenhang bezüglich der Berechnung der Variablen i, f, p, q, t. Vielen Dank für jeden Hinweis!
Hallo, die Berechnung erfolgt wie folgt für 12 Bit: 60° -> 683 -> (4096 / 360°) * 60° -> 683,5 1 -> 682 * 4095 Gruß Frank
Hallo Frank, vielen Dank für Deine Antwort. Frank L. schrieb: > Hallo, > > die Berechnung erfolgt wie folgt für 12 Bit: > > 60° -> 683 -> (4096 / 360°) * 60° -> 683,5 > 1 -> 682 * 4095 Mit anderen Worten: 360° für den Hue-Wert entsprechen in 12 Bit dann der 2796885? Während dann für Hue = 1° gilt: 1° -> 2796885 / 360° = 62153/8 Danke! Gruß
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.