hsv nach rgb für 12Bit

#2382940
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
Angehängte Dateien:
#2387303
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
Angehängte Dateien:
Gast #2528520
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
  }
#2621200
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
};
Gast #4129418
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!
Gast #4130297
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ß

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren