Forum: Projekte & Code hsv nach rgb für 12Bit


von Frank L. (franklink)


Angehängte Dateien:

Lesenswert?

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

von Frank L. (franklink)


Angehängte Dateien:

Lesenswert?

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

von Micha K. (Gast)


Lesenswert?

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
  }

von devmo (Gast)


Lesenswert?

@ Micha,
vielleicht kannst du deine Änderung komplettieren? Zumindest die 
Var.deklaration red, green, blue, fehlt hier, nur damit für andere keine 
Verwirrung entsteht

von Moritz E. (devmo)


Lesenswert?

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
};

von Joe D. (Gast)


Lesenswert?

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!

von Frank L. (frank_l)


Lesenswert?

Hallo,

die Berechnung erfolgt wie folgt für 12 Bit:

60° -> 683 -> (4096 / 360°) * 60° -> 683,5
 1 -> 682 * 4095

Gruß
Frank

von Joe D. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.