PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Farben nach R,G,B umwandeln in 4-stell. hex-code?



HaWe
06.09.2018, 15:29
Hallo,
Adafruit verwendet für Farben einen 4-stelligen hex-Code, der nicht mit den 6-stelligen hex-Codes identisch ist, wie man sie von html kennt.
Kennt jemand eine Konvertierungs-Routine oder Vorschrift, um die R,G,B-Werte (jew. 0-255) in den Adafruit Code umzurechnen?



#define ILI9341_BLACK 0x0000 ///< 0, 0, 0
#define ILI9341_NAVY 0x000F ///< 0, 0, 123
#define ILI9341_DARKGREEN 0x03E0 ///< 0, 125, 0
#define ILI9341_DARKCYAN 0x03EF ///< 0, 125, 123
#define ILI9341_MAROON 0x7800 ///< 123, 0, 0
#define ILI9341_PURPLE 0x780F ///< 123, 0, 123
#define ILI9341_OLIVE 0x7BE0 ///< 123, 125, 0
#define ILI9341_LIGHTGREY 0xC618 ///< 198, 195, 198
#define ILI9341_DARKGREY 0x7BEF ///< 123, 125, 123
#define ILI9341_BLUE 0x001F ///< 0, 0, 255
#define ILI9341_GREEN 0x07E0 ///< 0, 255, 0
#define ILI9341_CYAN 0x07FF ///< 0, 255, 255
#define ILI9341_RED 0xF800 ///< 255, 0, 0
#define ILI9341_MAGENTA 0xF81F ///< 255, 0, 255
#define ILI9341_YELLOW 0xFFE0 ///< 255, 255, 0
#define ILI9341_WHITE 0xFFFF ///< 255, 255, 255
#define ILI9341_ORANGE 0xFD20 ///< 255, 165, 0
#define ILI9341_GREENYELLOW 0xAFE5 ///< 173, 255, 41
#define ILI9341_PINK 0xFC18 ///< 255, 130, 198

Ceos
06.09.2018, 16:43
Die Farben sind MSB>LSB 5bit R, 6bit G und 5bit B codiert

du könntest also ganz simpel mit maskieren und shiften 3 bytes daraus machen wie du es gewohnt bist

R = (0b1111100000000000 & input) >> 11;
G = (0b0000011111100000 & input) >> 5;
B = (0b0000000000011111 & input);

https://en.wikipedia.org/wiki/List_of_monochrome_and_RGB_palettes#16-bit_RGB

HaWe
06.09.2018, 18:13
Die Farben sind MSB>LSB 5bit R, 6bit G und 5bit B codiert

du könntest also ganz simpel mit maskieren und shiften 3 bytes daraus machen wie du es gewohnt bist

R = (0b1111100000000000 & input) >> 11;
G = (0b0000011111100000 & input) >> 5;
B = (0b0000000000011111 & input);

https://en.wikipedia.org/wiki/List_of_monochrome_and_RGB_palettes#16-bit_RGB

aha, danke!
verstehe ich das richtig:
also z.B. für LightHeliotrop (220, 139, 231):

uint16_t R = (0b1111100000000000 & 220) >> 11;
uint16_t G = (0b0000011111100000 & 139) >> 5;
uint16_t B = (0b0000000000011111 & 231);

uint16_t colorcode=R+G+B;

?

- - - Aktualisiert - - -

hmm, nee, scheint genau anders rum zu arbeiten...

mit der Bitarithmetik komme ich aber nicht zurecht - wie sähe die Umkehrfunktion aus, aus den RGW-Einzelwerten die hex colorcode Zahl?

einfach
R =220 << 11;
G = 139 << 5;
B = 231;
colorcode=R+G+B;

?

PS,
ja, schein zu klappen so rum...
Danke!

Ceos
06.09.2018, 18:24
ne das war die umrechnung von 16bit (sog. 65k Farbraum) auf R G B

wolltest du es umgekehrt? Sorry :)

RGB16 = ((uint16_t)R << 11) | (((uint16_t)G << 5) & 0b0000011111100000) | ((uint16_t)B & 0b0000000000011111);

(uint16_t) muss zwingend vorher stehen und castet den 8bit auf einen 16bit, sonst fallen die bits nach dem 8ten ins nirvana :D

ich kenne gerade nicht den 16bit unsigned int typ für arduino sorry

(PS: Das ist auch der Grund weswegen ich so einen sprechenden typen verwende, egal welcher compiler oder welche umgebnung jeder macht es anders)

HaWe
06.09.2018, 21:52
danke vielmals, so passts! 8)

HaWe
06.09.2018, 23:59
das erste in eine Funktion zu packen mit Rückgabewert, ist ja einfach -


//---------------------------------------------------------
// ColorRGB2color16bit
//---------------------------------------------------------

int16_t ColorRGB2color16bit(uint16_t R, uint16_t G, uint16_t B) {

return ((uint16_t)R << 11) | (((uint16_t)G << 5) & 0b0000011111100000) | ((uint16_t)B & 0b0000000000011111);
]


kann man die Rückwärtstransformierung so programmieren (bin mir unsicher wegen pass per reference):



//---------------------------------------------------------
// Color16bit2RGB
//---------------------------------------------------------

void Color16bit2RGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (0b1111100000000000 & color16) >> 11;
G = (0b0000011111100000 & color16) >> 5;
B = (0b0000000000011111 & color16);
}

Ceos
07.09.2018, 06:22
im zweifelsfall mit pointer



void Color16bit2RGB(uint16_t color16, uint8_t* R, uint8_t* G, uint8_t* B) {

*R = (uint8_t)((0b1111100000000000 & color16) >> 11);
*G = (uint8_t)((0b0000011111100000 & color16) >> 5);
*B = (uint8_t)((0b0000000000011111 & color16));
}


muss gestehen dass ich solche reference calls meide, weils mir leicht spanisch ist :D
pointer sind für mich persönlich einfacher zu handhaben

beim aufruf musst du dann natürlich dioe adressen auflösen



uint8_t myR,myG,myB;
Color16bit2RGB(some16bitColor, &myR, &myG, &myB);
printf("<font color=\"#%00h%00h%00h\">",myR,myG,myB);



PS: ich habe die einzelnen farben mal auf 8 bit reduziert und einen expliziten cast mit eingebaut damit alles sauber aussieht :)

HaWe
07.09.2018, 10:30
danke für die Mühe!
Ich muss allerdings auch etwas gestehen...: dass mir das mit diese Pointern mit * und dann doch wieder & im Hauptprogramm noch viel unklarer ist, mit Pointer hin und vorher und hinterher referenzieren oder dereferenzieren oder wie auch immer das heißt - wenn möglich würde ich lieber eindeutige Variablennamen im Hauptprogramm verwenden, die dann per Referenz übergeben werden und auf die man dann nach Ende der Funktion wieder als aktuaisierte Werte per Variabennamen zugreifen kann.
Aber auch da komme ich immer ins Schleudern. Hatte gehofft, das könnte irgendwer aus dem Stegreif... 8)

so, nochmal getestet - das mit dem "&" klappt...
Rest nochmal in Arbeit...



funktioniert noch nicht ganz perfekt...

- - - Aktualisiert - - -




uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return ((uint16_t)R << 11) | (((uint16_t)G << 5) & 0b0000011111100000) | ((uint16_t)B & 0b0000000000011111);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)((0b1111100000000000 & color16) >> 11);
G = (uint8_t)((0b0000011111100000 & color16) >> 5);
B = (uint8_t)((0b0000000000011111 & color16));
Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // Dark Pink (255,102,78)


Serial.begin(115200);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb Einzelwerte dann gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}



Ausgabe:

Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb Einzelwerte dann gelöscht):
col16=64718
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64718
R=31
G=38
B=14

Hauptprogramm nach col16 zu rgb:
col16=64718
r=31
g=38
b=14

HaWe
08.09.2018, 12:04
kann es sein, dass die zurück-Richtung clolor16 => r,g,b nicht richtig rechnet? Zumindest schein es so nach direkter Kontrolle der (lokalen) Rechenschritte
R = (uint8_t)((0b1111100000000000 & color16) >> 11);
G = (uint8_t)((0b0000011111100000 & color16) >> 5);
B = (uint8_t)((0b0000000000011111 & color16));

denn die geben ja andere Ergebnisse als die hinwärts-Richtung r,g,b => color16:


die hinwärts-Richtung ermittelt ja aus den 3 rgb-Werten
r=255
g=102
b=78
die color16-Zahl 64718

die Rückwärts-Funktion dann aber aus color16=64718
die (abweichenden) rgb-Werte
R=31
G=38
B=14

Hatte ich etwas falsch c+p'tet ?

Ceos
10.09.2018, 09:10
Sorry dass ich nicht früher antworten konnte, hatte nicht viel gelegenheit überhaupt mal länger als 5 Minuten ins Forum zu schauen.

Mir ist da ein Denkfehler passiert, bei der Rückrechnung der einzelnen Komponenten stimmt zumindest schonmal deine Formel, bei der Umrechnung von Komponenten auf RGB16 allerdings ist rigendwas schief, da komme ich auf einen anderen Wert.

Der primäre Denkfehler liegt jedoch beim Rückrechnen von RGB16 auf R G B

G wird hier mit 6Bit aufgelöst, also müsste man streng genommen >> 6 statt >> 5 machen ... was allerdings der größte Denkfehler von mir war ... die Werte sind doch nur 5 bzw. 6Bit groß und müssten ja nochmal auf 8Bit aufgeblasen werden (inklusive eines Verlust der Auflösung selbsttverständlich)

Und ich glaube beim Maskieren und schieben ist auch noch ein dreher drinne ... ich habe immer die MSB ausmaskiert und da kann nur blödsinn bei rauskommen XD

Ich versuche heute Nachmittag mal eine Korrektur, da ich jetzt noch was wegschaffen muss bevor hier alles abbrennt :)

HaWe
10.09.2018, 09:52
besten Dank schon im vorraus - mach dir aber keinen Stress, es ist absolut nicht eilig, es hatte mich nur stutzig gemacht!

Moppi
10.09.2018, 12:18
Das mit dem Konvertieren der Farben ist so eine Sache, das kann man machen, es kommt aber nicht unbedingt das gewünschte Ergebnis heraus. Da die Farbgewichtung auch eine Rolle spielt. Ohne das weiter kompliziert zu machen, kommt es bei der einfachen Farbanpassung per Logikoperatoren teils zu falschen Farbdarstellungen beim Ziel. Das sollte man nur mal so im Hinterkopf haben.
Ansonsten ist RGB-5-6-5, 5Bit für Rot, 6Bit für Grün und 5Bit für Blau.
Bei RGB-8-8-8, jeweils 8Bit für eine Farbe.

RGB 8-8-8: 11111111-11111111-11111111
RGB 5-6-5: xxx11111-xx111111-xxx11111

Schiebe die ersten 8 Bit nach rechts, um 3 Stellen.
Schiebe die zweiten 8 Bit nach rechts, um 2 Stellen.
Schiebe die dritten 8 Bit nach rechts, um 3 Stellen.

int rot1 = rot/8; int gruen1 = gruen/4; int blau1 = blau/8;

Das Problem dabei: Grün ist anders gewichtet, das kann sich in einem Grünstich des Bildes bemerkbar machen.

Rückwärts geht es genau andersrum:

Schiebe die ersten 5 Bit nach links, um 3 Stellen.
Schiebe die zweiten 6 Bit nach links, um 2 Stellen.
Schiebe die dritten 5 Bit nach links, um 3 Stellen.

int rot = rot1*8; int gruen = gruen1*4; int blau = blau1*8;

--------------------------

Die einzelnen Farben aus rgb-8-8-8 nach rgb-5-6-5:

r = rgb888/524288;
g = (rgb888/1024)&63;
b = (rgb888/8)&31;

Ganze RGB-Werte umrechnen:

rgb565 = (( ((rgb888/524288)*64) | ((rgb888/1024)&63) )*32) | ((rgb888/8)&31);

10111000 10101100 10101000 => ((1472 | 43) * 32) | 21 => 1011110101110101

und zurück (als Probe):

rgb888 = ((rgb565 & 63488) * 256) | ((rgb565 & 2016) * 32) | ((rgb565 & 31) * 8)

1011110101110101 => (12058624 | 44032) | 168 => 10111000 10101100 10101000

HaWe
10.09.2018, 12:33
danke für den Post!
ich habe jetzt neu codiert:



uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return ((uint16_t)R << 11) | (((uint16_t)G << 5) & 0b0000011111100000) | ((uint16_t)B & 0b0000000000011111);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)(color16/524288);
G = (uint8_t)(color16/1024);
B = (uint8_t)((color16/8)&31);
Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // 0xFF66B2; // Dark Pink (255,102,78)


Serial.begin(115200);
delay(3000);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}


und bekomme jetzt den output:


Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64718
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64718
R=0
G=63
B=25

Hauptprogramm nach col16 zu rgb:
col16=64718
r=0
g=63
b=25



hmmmm... :confused:

Moppi
10.09.2018, 13:06
G = (uint8_t)(color16/1024);

muss, wenn dann so:

G = (uint8_t)(color16/1024)&63;



Allerdings schreibst Du "color16". sind das als Quelle 16Bit-Farben? RGB-8-8-8 sind 24Bit-Farben. RGB-5-6-5 sind 16Bit-Farben.

HaWe
10.09.2018, 13:11
ja, es handelt sich immer um die 16-bit Zahlen
r,g,b haben immer die volle range von 0-255, die dann nur anders in die 16- bzw. auch optional 24-bit oder 32-bit truecolor umkodiert werden sollen.

Moppi
10.09.2018, 13:15
Ich hatte gelesen, Du schriebst, dass nicht so einfach ist, wie in HTML - Farben für Webseiten. Die sind dort 8bit-8bit-8bit (style="color:#452f63"), 45h - 2fh - 63h. Macht 24Bit.

Von welchem Farbformat, nach welchem muss es denn?

"r,g,b haben immer die volle range von 0-255, die dann nur anders in die 16- bzw. auch optional 24-bit oder 32-bit truecolor umkodiert werden sollen."

Dann ist es 8-8-8 nach 5-6-5. Wo ist der Fehler?



sinngemäß wäre so richtig:

G = (uint8_t)(color24/1024)&63;


Allerdings kann man die 24Bit-Farbe natürlich nur in ein 32Bit-Register packen. Um in einem Stück zu rechnen.
Dumm gelaufen, wenn der Arduino nur 16Bit-Register hat ;-) Vor Jahren noch hätte ich das jetzt einfach auf 16Bit-Operationen aufgeteilt, als ich noch in Übung war. So muss ich nochmal drüber nachdenken.

Nachtrag:

Vorher war der ganze 24Bit-Farbwert: rgb888

Wenn die Farbwerte einzeln vorliegen, ist es so, wie ich Anfangs schrieb:

int rot1 = rot/8; int gruen1 = gruen/4; int blau1 = blau/8;

So wird aus Rot mit 8 Bit, Rot mit 5 Bit, aus Grün mit 8 Bit, wird Grün mit 6 Bit, aus Blau mit 8 Bit, wird Blau mit 5 Bit.

HaWe
10.09.2018, 13:30
also noch mal von vorn:
r,g,b-Werte sind ja als Vorgabe universell,
weiß ist immer (255, 255,255)
und rot immer (255,0,0 )
und "Dark Pink" z.B. (255,102,78 ) etc.

in html habe ich meist 24-bit, das ist einfach: color24 = (r<<16)+(g<<8 )+b

hier geht es aber jetzt um die color16 - Kodierung (hin und zurück).

Moppi
10.09.2018, 13:50
Demnach müsste es so sein, von R,G,B Einzelwerten zu 16Bit-Werten:



rgb = ((rot/8)*2048) | ((grün/4)*32) | (blau/8);


und zurück müsste dann so sein:



rot = (rgb/2048)*8;
grün = (rgb&2016)/8;
blau = (rgb&31)*8;

HaWe
10.09.2018, 15:54
hast du das mal selber getestet? Ich hatte doch den Arduino Code gepostet...


uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return ((R/8)*2048) | ((G/4)*32) | (B/8);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)(color16 / 2048)*8;
G = (uint8_t)(color16 & 2016)/8;
B = (uint8_t)(color16 &31)*8;
Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // 0xFF66B2; // Dark Pink (255,102,78)


Serial.begin(115200);
delay(3000);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}


ergibt:


Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=4
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=4
b=72



Teste doch bitte mal selber, sei so nett... 8)

Moppi
10.09.2018, 16:57
Ich guck mal, ob noch ein Fehler drin ist.

Hier mal die Hin-Rechnung:



r=255, g=102, b=78

r=11111111, g=01100110, b=01001110

muss ergeben: 11111 011001 01001 = 64297

rgb = ((rot/8)*2048) | ((grün/4)*32) | (blau/8);

rgb = 11111 000000 00000
| 00000 011001 00000
| 00000 000000 01001
--------------------
11111 011001 01001

= 64297


und das Ganze zurück:



rgb = 64297;

rot = (rgb/2048)*8; => 11111000 => 248
grün = (rgb&2016)/8; => 01100100 => 100
blau = (rgb&31)*8; => 01001000 => 72


So weit ich das sehe, ist das richtig so, wenn ich die Bitmuster vergleiche.

Mit den Werten Rot=248, Grün=100 und Blau=72 müsste das Hin und Zurück dann stimmen.
Auf die Originalwerte kann man nicht kommen, weil man 24Bit-Farbe auf 16Bit-Farbe komprimiert, das ist verlustbehaftet - die unteren Bits gehen hier verloren und können nicht rekonstruiert werden. 16Bit-Farben haben einen kleineren Farbraum als 24Bit-Farben.

Allerdings benötigt man für diese Berechnungen:
rot = (rgb/2048)*8; ... etc. 16 Bit. Die Berechnung muss mit 16 Bit stattfinden und dann am Ende könnte man auf 8 Bit umwandeln, weil im Ergebnis nur noch die unteren 8 Bit der Berechnung belegt sind.

Ich denke Dein Problem liegt hier:
R = (uint8_t)(color16 / 2048)*8; sieht für mich so aus, als ob die gesamte Berechnung mit 8 Bit durchgeführt werden soll(!?).

Eventuell so besser (ohne jede Gewähr):
(uint8_t) R = (color16 / 2048)*8; Weiß ich aber nicht, weil ich diese sprachspezifischen Sachen hier nicht gut so kenne. Aber ich denke, Du weißt, was ich meine.

Oder mach es so:


int R = (color16 / 2048)*8;

Die obersten 8 Bit sind nach erfolgter Berechnung sowieso nicht belegt. Normalerweise switcht man auch zwischen den Datentypen nicht so oft hin und her. Das dürfte Zeit kosten, wenn aus 16 Bit-Operanden 8 Bit-Operanden gemacht werden und umgekehrt. Da sind bestimmt unnütze Operationen, die der Compiler für Dich einbaut. Ich würde es bei den Datentypen für die Farben immer bei 16 Bit belassen. Vielleicht kann man das später aus Platzgründen optimieren, aber erst mal sollte man schauen, dass es funktioniert.

HaWe
10.09.2018, 17:40
...danke für die Idee..und, schon getested...? ;)

Moppi
10.09.2018, 17:51
Nein, ich habe das von Hand gerechnet. Du hattest ja nur auf dem Rückweg Probleme, mit den Berechnungen, daher denke ich, dass es was mit dem 8 Bit-Gedöhns zu tun hat. Ich habe die Rechnung genau aufgeschlüsselt. Kann man nachvollziehen und überprüfen.

HaWe
10.09.2018, 18:34
leider nein, stimmt immer noch nicht.

Bitte teste es doch vorher selber, bevor du einen falschen Lösungsweg postest.



uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return (uint16_t)((R/8)*2048) | (uint16_t)((G/4)*32) | (uint16_t)(B/8);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)(color16 / 2048)*8; ;
G = (uint8_t)(color16 & 2016)/8;;
B = (uint8_t)(color16 & 31)*8;
Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // 0xFF66B2; // Dark Pink (255,102,78)


Serial.begin(115200);
delay(3000);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}



Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gel⸮scht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=4
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=4
b=72

Ceos
10.09.2018, 18:36
Ich habe die Berechnung gerade einmal in einem Excel Blatt angelegt um es verständlich zu machen

https://puu.sh/BsLSg/6fe9cf4a10.png

ich liefere später noch etwas code nach, wenn mein kopf sich von dem krampf auf arbeit erholt hat XD

ich sehe im moment nur miraculi vor augen

man kann den leichten farbunterschied durch die skallierung von 5 bzw. 6 bit doch besser erkennen als mir lieb ist XD

Moppi
10.09.2018, 18:45
HaWe, soll das auf Arduino laufen?

Nächstes mal komme ich vorbei und haue Dich! Schreibt der Kerl "...bevor du einen falschen Lösungsweg postest". Tse, tse, tse!

Was hältst Du denn von dieser Ausgabe mit Deinem Programm? Sieht das besser aus? :


Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=100
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=100
b=72

HaWe
10.09.2018, 18:51
da bin ich jetzt gespantn (ich habs noch nicht hingekriegt, evtl was falsch verstanden)

Moppi
10.09.2018, 18:59
Ich habe doch geschrieben, wo ich denke, dass das Problem liegt. Dass Du die Berechnung auf 8 Bit erzwingst.

mach bitte so:



void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (color16 / 2048)*8; ;
G = (color16 & 2016)/8;;
B = (color16 & 31)*8;
Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}


:-)

Ich hoffe, dass es jetzt besser ist!

HaWe
10.09.2018, 19:03
hatte ich das nicht gemacht?

R = (uint8_t)(color16 / 2048 )*8; ;
G = (uint8_t)(color16 & 2016)/8;;
B = (uint8_t)(color16 & 31)*8;

ach so, du meinst ich soll das nicht machen?
Das wäre nicht korrekt, denn es sind ja uint8_t Zahlen, die zurückgegeben werden sollen.
Probier doch mal bitte selber!

Moppi
10.09.2018, 19:12
Ich hatte das hier schon geschrieben (Beirag #20): https://www.roboternetz.de/community/threads/72412-Farben-nach-R-G-B-umwandeln-in-4-stell-hex-code/page2?p=646541&viewfull=1#post646541

Ich denke, er führt die Berechnung mit 8 Bit aus.
so: (uint8_t)(color16 / 2048 )*8;
Du brauchst aber 16 Bit während der Berechnung, weil zumindest Dein Farbwert color16 ja 16 Bit umfasst.

Du müsstest hinterher eine Umwandlung vornehmen
(uint8_t)R = (color16 / 2048 )*8;

Aber das funktioniert nicht!

Deshalb habe ich jetzt das probiert, da gibt es kein Gemecker:
void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (color16 / 2048)*8; ;
G = (color16 & 2016)/8;;
B = (color16 & 31)*8;
R = (uint8_t)R;
G = (uint8_t)G;
B = (uint8_t)B;

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

HaWe
10.09.2018, 19:21
bittebittebitte mal selber testen!

- - - Aktualisiert - - -

...ach so, jetzt habe ich deinen Einwand verstanden!
Klammer ums ganze, dann erst casten!

- - - Aktualisiert - - -

nein, stimmt auch nicht dann.

Moppi
10.09.2018, 19:27
Schau noch mal im letzten Beitrag vor Deinem. Habe es geändert.

Allerdings glaube ich auch, dass das
R = (uint8_t)R; überflüssig ist. Weil der kleinste Typ, den der Arduino kennt, ist: integer
Er würde den von Dir geglaubten 8-Bit-Wert vermutlich als Integer ablegen/speichern. So, wie er nach der Berechnung schon vorliegt.

HaWe
10.09.2018, 19:33
das habe ich verstanden und auch so gemacht.
aber nein, der kleinste Wert ist uint16_t , nicht int, denn int ist bei mir int32_t (ARM Cortex M0 = Arduino M0/Zero), deshalb ist es immer explizit auf uint16_t oder uint8_t gecastet.

Tipps sind wirklich sehr willkomen, aber bitte immer vorher selber testen!

Moppi
10.09.2018, 19:39
Na ja, ich hatte geschrieben: "vermutlich" und vorher schon, dass ich mich mit den Typen nicht so genau bei den Teilen auskenne.

Aber um das aufzuklären: Gibt es noch ein Problem?
Und welchen Tipp sollte ich testen - was soll ich da testen? - k.A.

:-)

HaWe
10.09.2018, 19:39
Der Code soll mit dem expliziten Casting damit eben auch auf 8bit Arduinos lauffähig sein.

Teste einfach meinen Testcode, in den du deine Algorithmen einsetzt, du bekommst dann ja über Serial die Debug-Informationen zur Kontrolle.

Moppi
10.09.2018, 19:44
Da habe ich noch byte gefunden:


R = byte((color16 / 2048)*8);
G = byte((color16 & 2016)/8);
B = byte((color16 & 31)*8);

HaWe
10.09.2018, 19:46
das ist doch wurscht, byte == uint8_t

bitte testen und Serial Meldungen posten!

Moppi
10.09.2018, 19:48
Spuckt genau dasselbe aus.

Falls der Controller nur 8 Bit beherrschen würde, müsste man die 16-Bit-Farbwerte getrennt in Low- und High-Byte (MSB und LSB) ablegen und anders rechnen.


Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=100
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=100
b=72

HaWe
10.09.2018, 19:55
Der Grund ist doch klar:
ein 16bit Integer dividiert durch 2048 ist gleich >>11, und damit immer ein 5bit Wert, und passt damit immer in ein 8bit Byte.

warum sollte die MCU nur 8 bit beherrschen? die 8bit AVRs rechnen bis zu int32_t, und die 32bit MCUs bis int64_t.
Ich bin mal auf Ceos' Lösung gespannt, aber ist ja eh nicht so schrecklich eilig.

Moppi
10.09.2018, 22:41
Im Übrigen war es die falsche Schreibweise, die zu den merkwürdigen Ergebnissen führte:



R = (uint8_t)(color16 / 2048)*8; ;
G = (uint8_t)(color16 & 2016)/8;;
B = (uint8_t)(color16 & 31)*8;


was immer die Schreibweise (uint8_t) bewirken sollte. Hier hast Du offenbar uint8_t nur auf die Folgeausdrücke angewandt. Also:
uint8_t(color16 & 2016) und das dann durch 8 dividiert. Du stampfst
(color16 & 2016) auf 8 Bit ein (obwohl Du an der Stelle noch 16 Bit brauchst - eigentlich nur 11). Richtig wäre die Schreibweise und Funktion, die Du wolltest, so:



R = uint8_t((color16 / 2048)*8);
G = uint8_t((color16 & 2016)/8);
B = uint8_t((color16 & 31)*8);


Allerdings habe ich woanders gelesen, dass man statt uint8_t() lieber byte() verwenden soll. Wäre der bessere Stil (kann man sehen wie man möchte):

"An unsigned char data type that occupies 1 byte of memory. It is the same as the byte datatype. The unsigned char datatype encodes numbers from 0 to 255. For consistency of Arduino programming style, the byte data type is to be preferred."
Arduino: Difference in “Byte” VS “uint8_t” VS “unsigned char” (https://oscarliang.com/arduino-difference-byte-uint8-t-unsigned-cha/)

HaWe
11.09.2018, 02:16
nein, das stimmt doch nicht, was du schreibst:
ich habe deine gesammelten Codes in allen erdenklichen Klammerungen getestet, auch der obigen, und es hat nie funktioniert. Da du nie vollständigen Code gepostet hast, habe ich das tatsächlich hier und da ggf missverstanden, aber deine andere Klammerung von oben per
R = uint8_t((color16 / 2048 )*8 );
G = uint8_t((color16 & 2016)/8 );
B = uint8_t((color16 & 31)*8 );
hat ja auch nicht funktioniert.

Wenn du allerdings einen funktionierenden, kompilierfähigen Arduino-Sketch hast, selber von dir getestet, stelle ihn gerne ein, ich bin sehr gespannt.

byte ist i.Ü.auch nicht "besser", es ist einfach nur Arduino Kauderwelsch,
hingegen uin8_t usw. sind die stdint-Dateitypen u.a. auch für C++11 ,
und weil Arduino den gpp mit C++11 verwendet, funktioniert das selbstverständlich 100% standardgemäß.

Und das funktioniert dann auch ohne weiteres auf dem Raspi mit gcc/g++, wenn man will - im Gegensatz zu byte, das müsste man dann erst mal neu definieren als #define byte uint8_t.

übrigens heißt es nicht
uint8_t ((color16 / 2048 )*8 );
sondern
(uint8_t) ((color16 / 2048 )*8 );
denn für type casting schreibt man in C den Ziel-Datentyp in runde Klammern.
Aber auch DAS habe ich getestet, ebenfalls leider mit falschem Ergebnis, aber trotzdem herzlichen Dank für deine Ideen!

Und wie gesagt, ich freue mich auf einen funktionierenden, kompilierfähigen Arduino-Sketch von dir, vorher selber von dir getestet... 8)

Moppi
11.09.2018, 07:25
Ich habe gestern Abend ja noch diese Varianten ausprobiert.

Die Eine:
void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)((color16 / 2048)*8);
G = (uint8_t)((color16 & 2016)/8);
B = (uint8_t)((color16 & 31)*8);

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

und die Andere:
void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = uint8_t((color16 / 2048)*8);
G = uint8_t((color16 & 2016)/8);
B = uint8_t((color16 & 31)*8);

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

Beide liefern dasselbe Ergebnis (sogar heute Morgen noch):
Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=100
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=100
b=72


Dasselbe Ergebnis erzielst Du auch mit:
void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t)(color16 / 2048)*8;
G = (uint8_t)((color16 & 2016)/8);
B = (uint8_t)(color16 & 31)*8;

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}


habe ich auch gerade probiert. Und funktioniert erwartungsgemäß. Hier ist die Frage dann, warum soll das so funktionieren? Das liegt daran, dass bei der Berechnung von R schon nach der ersten Operation - color16 geteilt durch 2048 - nur noch 8 Bit benötigt werden und dieser Ausdruck ist geklammert. Bei der Berechnung von G funktioniert es nicht mehr, weil nach der Rechnung - color16 UND 2016 - 11 Bit übrig bleiben, die erst mit dem Teilen durch 8 auf 8 Bit reduziert werden. Bei der Berechnung von B - color16 UND 31 - geht es nur um die untersten 5 Bit, die hier maskiert werden und durch die Multiplikation mit 8 dann auf 8 Bit erweitert werden.

Gestern war der Kopf schon etwas voll, vom ganzen Hin und Her.
Um das Ergebnis und nicht einzelne Rechenschritte umzuwandeln, hätte man gleich die Klammerung der gesamten Rechnung versuchen sollen
G = (uint8_t)(....);
Zumal Du das an anderer Stelle ja auch schon von Anfang an gemacht hast, nämlich hier:
uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return (uint16_t)((R/8)*2048) | (uint16_t)((G/4)*32) | (uint16_t)(B/8);
}


Um das alles richtig zu verstehen, muss man Bit-Rechnung verinnerlicht haben. Einfach mal so da was zu übernehmen, sich nicht bewusst sein, was es bedeutet und deshalb nicht die richtigen Schlüsse ziehen können, ist eben nicht der richtige Ansatz. Aber ausreichend erklärt sollte ich es nun haben, denke ich.

Leider hast Du noch nicht geschrieben, ob das Ergebnis nun so ist wie Du es benötigst. Ich kann nur davon ausgehen - tue ich jetzt auch. Denn Ceos kam zum selben Ergebnis von R=248,G=100,B=72. (https://www.roboternetz.de/community/threads/72412-Farben-nach-R-G-B-umwandeln-in-4-stell-hex-code?p=646546&viewfull=1#post646546) So dass der Rechenweg hier nicht falsch war, wie Du es zwischenzeitlich angenommen hast.

HaWe
11.09.2018, 09:38
hallo,
der Grund für die verschiedenen Ergebnisse mit verschiedener Klammerung ist mir ja klar, ich hatte ja auch beide getestet - aber mit anderem Ergebnis als das, das du oben listest. Bisher hatte es nie gestimmt bei mir.

Ich bin nun auch kein C++ Spezialist, aber ich vermute, dass
uint8_t((color16 / 2048 )*8 )
kein legaler C/C++ Code ist und nur zufällig (!) hier das gleiche liefert wie
(uint8_t) ((color16 / 2048 )*8 )

Wo ist denn dein kompletter Sketch Code, fertig zum Kompilieren? Ich will nicht wieder was falsch einfügen.

Ich kopiere den kompletten Code noch mal und compiliere ihn neu, wenn ich ihn habe, vlt hat irgendwo anders was nicht gestimmt bei meinen Versuchen. Es gingen ja auch sehr viele verschiedene Versionen durcheinander.

Melde mich dann, wenn ichs habe, habe grade keinen Arduino.

Moppi
11.09.2018, 09:39
Zum Optimieren habe ich dann mal diesen Entwurf:
uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return (R>>3<<11) | (G>>2<<5) | (B>>3);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t) (color16 >> 11) << 3;
G = (uint8_t)((color16 & 2016) >> 3);
B = (uint8_t) (color16 & 31) << 3;

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}


Ausgabe:
Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=100
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=100
b=72


Mann kann R>>3<<11 und G>>2<<5 übrigens nicht zusammenfassen zu R<<8 und G<<3, weil durch die jeweils erste Shift-Operation die untersten Bit gelöscht werden - was wichtig ist. Man müsste dann zusätzlich wieder ein UND anhängen, um die untersten Bits zu löschen, damit wird der Ausdruck nicht kürzer. Beim Verschieben nach Rechts werden die unteren Bits ins Nirvana verschoben und beim Verschieben nach Links werden Nullen eingefügt. Das sind Standardoperationen, wo das Überlaufbit immer gelöscht ist, bzw. nicht berücksichtigt wird. Für bestimmte Zwecke gibt es das auf Maschinenspracheebene auch noch mit Berücksichtigung des Überlaufbits - ist hier nicht wichtig.

HaWe
11.09.2018, 10:00
danke, das sieht nun schon ganz anders aus alles andere, das ich bisher probiert habe.
Verständnisfrage:
Was bedeutet per Rechen-Preferenz
(R>>3<<11)
?

vermutlich das gleiche wie
((R>>3)<<11)
oder ?

durch >> schneidest du ja auch die letztem Ziffern ab, wäre "runden" nicht besser?

Ceos
11.09.2018, 10:48
Mann kann R>>3<<11 und G>>2<<5 übrigens nicht zusammenfassen

Aber man könnte es durch ( R & 0xF8 ) << ( 11-3 ) ersetzen und so wertvolle Takte sparen (kommt auf den Prozessor und seine Intructions an ob ein Bitshift nur einen oder mehrere Takte dauert) ... die 11-3 sollte man natürlich durch 8 ersetzen, damit es ein literal bleibt und nciht erst noch vom vompiler in eine formel verwandelt wird :)

Für G wäre dass dann ( G & 0xFC ) << 3

HaWe
11.09.2018, 11:34
hi,
ich sehe schon, da sind 2 bitakrobatikspezialisten unter sich...
für mich ist das zu hoch oder besser: zu low-level.

Immerhin:

ES GEHT!
(@moppi: mit deinem letzten Code)


uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return (R>>3<<11) | (G>>2<<5) | (B>>3);
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t) (color16 >> 11) << 3;
G = (uint8_t)((color16 & 2016) >> 3);
B = (uint8_t) (color16 & 31) << 3;

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}




Hauptprogramm vor Aufruf:
col16=0
r=255
g=102
b=78

Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):
col16=64297
r=0
g=0
b=0

Unterprogramm color16 zu RGB:
color16=64297
R=248
G=100
B=72

Hauptprogramm nach col16 zu rgb:
col16=64297
r=248
g=100
b=72



irgendwo muss gestern der Wurm drin gewesen sein.
Vielen herzlichen Dank! (y)


Zum Verständnis noch 1 Frage:
ein r=255 Wert wird ja zu r=248, das liegt ja an der kleineren 5bit-Codierbreite.
Kann man das bei der Rückumwandlung wieder sinnvoll auf 255 hochskalieren, also runden, analog auch für die mittleren 6bit Werte?

Ceos
11.09.2018, 12:43
nein leider nicht, das problem ist eine verlorene information kann man nicht wiederherstellen, wenn 2 bzw. 3 bits fehlen, fehlen sie, man könnte höchstens auf R und B +4 oder +3 aufaddieren, denn (2^3bit) -1 = 7, davon die Hälfte ungefähr 3 bis 4 um wenigstens etwas annähernd den original wert wieder her zu stellen. Auf G müsstest du dann +2 oder +1 aufaddieren

HaWe
11.09.2018, 13:27
ja, ich hatte da an einen Multiplikationsfaktor gedacht, der größere Zahlen bevorzugt und kleine vernachlässigt, so dass keine 1,2,3 oder 4 auf eine Null aufaddiert werden.

also was in der Art
R*= 255/248
G*= 255/252
B*= 255/248

edit:
Worauf ich eigentlich hinaus wollte - klar kann man es "irgendwie" machen, so oder so, meine Frage geht eher in die Richtung, wie es standardmäßig gmacht wird, denn für die Umrechnung R,G,B in color16bit und zurück gibts ja sicher Standardverfahren, damit alle immer dasselbe Ergebnis rauskriegen, egal ob Geforce, Intel, Adobe oder Broadcom.

Moppi
11.09.2018, 13:58
Aber man könnte es durch ( R & 0xF8 ) << ( 11-3 ) ersetzen und so wertvolle Takte sparen (kommt auf den Prozessor und seine Intructions an ob ein Bitshift nur einen oder mehrere Takte dauert) ... die 11-3 sollte man natürlich durch 8 ersetzen, damit es ein literal bleibt und nciht erst noch vom vompiler in eine formel verwandelt wird :)

Für G wäre dass dann ( G & 0xFC ) << 3

Ja, so meinte ich :-) Früher zu Zeiten, als IBM-Computer die ersten PCs waren und dort noch TTL-Schaltkreise verbaut waren, da war das sicher so, beim 286er glaub ich auch noch. Weil jeder Shift-Befehl normalerweise pro verschobenem Bit einen Takt benötigt. UND könnte daher schneller sein, wenn auf einmal alle Bits verknüpft werden. Allerdings weiß ich nicht, wie das heute ist. Ich könnte mir aber vorstellen, dass bei den neueren CPUs die Shift-Vorgänge auch schon in einem Taktzyklus erledigt werden. Bei Controllern weiß ich es nicht. Da muss mal jemand anders sagen, was schneller geht.

@HaWe


(R>>3<<11)
?

vermutlich das gleiche wie
((R>>3)<<11)
oder ?

In beiden Fällen passiert dasselbe, mit und ohne Klammer. Bloß wenn man noch mit UND, ODER etc. arbeitet, um Einzelergebnisse zu verknüpfen, sieht das wieder anders aus.

return (R>>3<<11) | (G>>2<<5) | (B>>3);

ist nicht gleichbedeutend mit

return R>>3<<11 | G>>2<<5 | B>>3;

------------------------------------------------------------

Aber, solch ein Ausdruck:

(color16 & 2016)/8)
ist logisch eindeutig. Wenn der Compiler gut ist, macht er aus diesem Ausdruck in Maschinensprache das, was auf der Zielhardware am schnellsten verarbeitet wird und setzt also selber Logikbefehle statt Arithmetischer ein. Dann wäre dieser Ausdruck der Universellste.


---------------------------------------------------------------
Sketch
---------------------------------------------------------------

uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return (R>>3<<11) | (G>>2<<5) | (B>>3); //Ersatzweise: ((R & 248)<<8) | ((G & 252)<<3) | (B>>3)
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t) (color16 >> 11) << 3; //Ersatzweise: (color16 >> 8) & 248
G = (uint8_t)((color16 & 2016) >> 3); //Ersatzweise: (color16 >> 3) & 252
B = (uint8_t) (color16 & 31) << 3; //Ersatzweise: (color16 << 3) & 248

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // 0xFF66B2; // Dark Pink (255,102,78)


Serial.begin(115200);
delay(3000);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}


Nochmal die andere (dritte) Variante:


uint16_t ColorRGB216bit(uint16_t R, uint16_t G, uint16_t B) {

return ((R & 248)<<8) | ((G & 252)<<3) | (B>>3); //Die Klammern müssen so sein, sonst stimmt die Logik nicht
}


void Color16bit2colorRGB(uint16_t color16, uint16_t &R, uint16_t &G, uint16_t &B) {

R = (uint8_t) (color16 >> 8) & 248; //Das funktioniert jetzt ohne zusätzliche Klammerung,
G = (uint8_t) (color16 >> 3) & 252; //weil schon nach dem Shift-Befehl die oberen 8 Bit nicht mehr von Bedeutung sind,
B = (uint8_t) (color16 << 3) & 248; //bzw. die Bits, die wir brauchen, in die unteren 8 Bit verschoben wurden. (uint8_t) richtet keinen "Schaden" mehr an.

Serial.println("");
Serial.println("Unterprogramm color16 zu RGB:");
Serial.println( (String)"color16="+(String)color16);
Serial.println( (String)"R="+(String)R);
Serial.println( (String)"G="+(String)G);
Serial.println( (String)"B="+(String)B);
}

void setup() {
// put your setup code here, to run once:

uint16_t r=255, g=102, b=78,
col16=0; // 0xFF66B2; // Dark Pink (255,102,78)


Serial.begin(115200);
delay(3000);

Serial.println("Hauptprogramm vor Aufruf:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);
Serial.println("");

col16=ColorRGB216bit(r,g,b);
r=g=b=0; // Rücksetzen!

Serial.println("Hauptprogramm nach rgb zu col16-Berechnung (rgb gelöscht):");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

Color16bit2colorRGB(col16, r, g, b);
Serial.println("");
Serial.println("Hauptprogramm nach col16 zu rgb:");
Serial.println( (String)"col16="+(String)col16);
Serial.println( (String)"r="+(String)r);
Serial.println( (String)"g="+(String)g);
Serial.println( (String)"b="+(String)b);

}

void loop() {
// put your main code here, to run repeatedly:

}


getestet sind die.

HaWe
11.09.2018, 15:33
jap, kommt das gleiche raus, danke!
Ich dachte allerdings bis jetzt, dass shiften in C immer schneller geht als Multipl./Division.
(Rein akademisch, denn das ist momentan absolut nicht zeitkritisch bei mir.)

Ceos
11.09.2018, 15:55
Ja, so meinte ich :-) Früher zu Zeiten, als IBM-Computer die ersten PCs waren und dort noch TTL-Schaltkreise verbaut waren, da war das sicher so, beim 286er glaub ich auch noch. Weil jeder Shift-Befehl normalerweise pro verschobenem Bit einen Takt benötigt

bist du dir sicher dass der avr compiler das automatisch konvertiert?! ein lsr hat den barrel shift aber mir ist kein single clock rsr >1 bekannt. Ich mein ich kann mich irren, aber das ginge glaube ich zu OT. (Schicks mir mal per PM weil ich kenns wirklich nicht)

Moppi
11.09.2018, 18:00
Das kann ich auch so beantworten: ich hatte schon geschrieben, dass ich das nicht weiß. Auch nicht, was kompiliert wird. :-) Da muss jemand anders mal was dazu sagen.
Ich kenne Maschinensprache noch vom 80x86. Irgendwann brauchte ich das aber auch nicht mehr. Deshalb ist vieles eingeschlafen. Aber Grundlagen sind noch da. Heute brauche ich Maschinensprache eigentlich auch gar nicht mehr.

- - - Aktualisiert - - -


jap, kommt das gleiche raus, danke!
Ich dachte allerdings bis jetzt, dass shiften in C immer schneller geht als Multipl./Division.
(Rein akademisch, denn das ist momentan absolut nicht zeitkritisch bei mir.)


Kannst Du ausprobieren. Normalerweise kann man eine Schleife bauen, die die Befehlszeile enthält. Der Schleifenzähler muss möglichst groß gewählt werden. Vor der Schleife Timer auslesen und nach der Schleife ebenfalls. Dann kann man sehen wie lange unterschiedliche Lösungsvarianten so brauchen, bzw. welche schneller ist. Kommt ja auch immer drauf an, was der Compiler für einen Maschinencode erzeugt.

HaWe
12.09.2018, 12:56
ja, ich habe ja schon mal einen Benchmarktest für verschiedene MCUs geschrieben, könnte ich mal machen



- - - Aktualisiert - - -

edit: Code aktualisiert nach Ceos' Einwand (s.u.!)



#define TimerMS() millis() //for cross plattform compatib
uint32_t time0;

long test_Int_multdiv2() {
uint32_t x,y;
volatile uint32_t s;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { s*=2;}
for(x=16; x>0;x--) { s/=2;}
s=7;
for(x=1;x<=8;x++) { s*=8;}
for(x=8; x>0;x--) { s/=8;}
}
return s;
}


long test_Int_shl_shr() {
uint32_t x,y;
volatile uint32_t s;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { s=(s<<1);}
for(x=16; x>0;x--) { s=(s>>1);}
s=7;
for(x=1;x<=8;x++) { s=(s<<3);}
for(x=8; x>0;x--) { s=(s>>3);}
}
return s;
}


// *SNIP*


time0=TimerMS();
s=test_Int_multdiv2();
runtime[0]=TimerMS()-time0;
sprintf (buf, "%3d %9ld Int_multdiv2", 0, runtime[0]); Serial.println( buf);


time0=TimerMS();
s=test_Int_shl_shr();
runtime[1]=TimerMS()-time0;
sprintf (buf, "%3d %9ld Int_shl_shr", 1, runtime[1]); Serial.println( buf);

Ergebnis: doch identisch, anders als vermutet (Angaben in ms):


Plattform: AVR (Arduino Mega2560, 16 MHz)


start test
0 1220 Int_multdiv2
1 1220 Int_shl_shr




Plattform: ARM Cortex M0 (Adafruit Itsybitsy M0, 48 MHz)


start test
0 93 Int_multdiv2
1 93 Int_shl_shr





Plattform: ARM Cortex M3 (Arduino Due, 84MHz)


start test
0 49 Int_multdiv2
1 49 Int_shl_shr




Pattform: ESP8266 (nodeMCU, Xtensa LX106, 80 MHz)


start test
start test
0 56 Int_multdiv2
1 55 Int_shl_shr




edit:
gleiches Ergebnis auch für Zuweisung an 2. Variable:



long test_Int_multdiv2() {
uint32_t x,y;
volatile uint32_t s,t;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { t=s*2;}
for(x=16; x>0;x--) { t=s/2;}
s=7;
for(x=1;x<=8;x++) { t=s*8;}
for(x=8; x>0;x--) { t=s/8;}
}
return s;
}


long test_Int_shl_shr() {
uint32_t x,y;
volatile uint32_t s,t;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { t=(s<<1);}
for(x=16; x>0;x--) { t=(s>>1);}
s=7;
for(x=1;x<=8;x++) { t=(s<<3);}
for(x=8; x>0;x--) { t=(s>>3);}
}
return s;
}

Ceos
12.09.2018, 14:15
hey moment mal, du schummelst doch :P

mach mal aus (s << 1) ein s = (s << 1) bzw. 3 natürlich, du schiebst hier eine zahl aber sie wird nicht gespeichert, wenn dem so ist optimiert er den inhalt der schleife wahrscheinlich einfach weg und du hast nur eine leere schleife gemessen

bei der multiplikation und division machst du ja auch eine zuweisung

EDIT: by the way hab cih das mit dem barrel shifter wohl falsch verstanden, den gibt es wohl für links als auch für rechts, aber atmel benutzt beim rsr scheinbar nicht immer den barrel shifter, das warum hat sich mir aus den bisherigen topics nicht wirklich erscshlossen, aber es scheint wohl ein bekanntes phänomen zu sein (primär englische atmel foren die sich mit komplexerer verwendung beschäftigen und nciht mit so trivialen tests)

HaWe
12.09.2018, 14:53
hey moment mal, du schummelst doch :P

kleiner Aufmerksamkeitstest :p

ich korrigiere es sofort!

edit:
Korrektur, sind doch immer jeweils identisch, ist oben korrigiert!

Moppi
12.09.2018, 14:58
s=(s<<1); dürfte Quatsch sein. s<<1 genügt und ist dasselbe, solange Variable s existiert. Wozu soll der nach s<<1 nochmal s=s ausführen? Korrigiere mich, wenn ich falsch liege!

HaWe
12.09.2018, 15:04
Ceos hatte Recht, es ist ein Unterschied, ich hatte es zuvor auch falsch vermutet. Zu Vergleichszwecken muss die Zuweisung in beiden Fällen gemacht werden, das macht dann im Ergebnis einen Unterschied zu vorher (s.o.), denn jetzt sind die Messwerte identisch nach beiden Methoden (vorher war ja fälschlich shiften schneller) !

Moppi
12.09.2018, 15:59
Ja, aber ich bin der Meinung, da wird was verwechselt.

s<<1; verschiebt Variable#S um 1 Bit nach links.


shl var_s,1


Bei Multiplikation (s=s*2)passiert was anderes. Ich denke in etwa so:


mov ax,var_s
mov bx,2
mul bx
mov var_s,ax

oder so:

mov ax,var_s
mul 2
mov var_s,ax

oder so, falls es möglich wäre:

mul var_s,2


Vom Prinzip her kommt es drauf an, was der Compiler für Maschinencode erzeugt.

Mit S=S<<1 machst Du in etwa so etwas:



mov ax,var_s
shl ax,1
mov var_s,ax


Das meine ich, ergibt keinen Sinn. Unnötige Operationen einzufügen. Wenn dann optimiert man einen Code, damit er schneller abgearbeitet wird. Und nicht, damit eine Befehlssequenz, die schneller ist, genau so schnell wird, wie eine andere, die langsamer ist.
Wichtig wäre zu wissen, ob s<<1 zum selben Ergebnis führt, wie s=s*2.

HaWe
12.09.2018, 18:03
interessanter Gesichtspunkt...
s ist ja immerhin volatile, also darf der Compiler diese Operation nicht wegoptimieren (was gpp sonst automatisch macht).
Dadurch wird das Shiften also keine Null- oder Leernummer.


Da müssen jetzt mal die hier einschlägig bekannten C/C++ Spezialisten etwas sagen... 8)

- - - Aktualisiert - - -

habe jetzt mal den Mess-Code per Zusatz-Variable auf einen gemeinsamen Nenner gestellt:




long test_Int_multdiv2() {
uint32_t x,y;
volatile uint32_t s,t;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { t=s*2;}
for(x=16; x>0;x--) { t=s/2;}
s=7;
for(x=1;x<=8;x++) { t=s*8;}
for(x=8; x>0;x--) { t=s/8;}
}
return s;
}


long test_Int_shl_shr() {
uint32_t x,y;
volatile uint32_t s,t;

for(y=0;y<10000;y++) {
s=11;
for(x=1;x<=16;x++) { t=(s<<1);}
for(x=16; x>0;x--) { t=(s>>1);}
s=7;
for(x=1;x<=8;x++) { t=(s<<3);}
for(x=8; x>0;x--) { t=(s>>3);}
}
return s;
}

auch hier: beide identisch!

ARM Cortex M0

start test
0 93 Int_multdiv2
1 93 Int_shl_shr

AVR (Mega2560)


start test
0 1220 Int_multdiv2
1 1220 Int_shl_shr


Fazit:
wenn einfach nur eine Variable erhöht werden soll, ist shiften schneller als Multipl./Division.

Wenn aber Werte zugewiesen und damit weiter gerechnet werden soll, sind beide gleich schnell, und das war ja hier die vorrangige Fragestellung

Moppi
12.09.2018, 18:25
wenn einfach nur eine Variable erhöht werden soll, ist shiften schneller als Multipl./Division.


Variable erhöhen: v=v+1 (bspw.: inc)
Multiplizieren mit 2: v=v*2 oder eben v=v<<1 (bspw.: shl)

Aber das ist ein ganz anderes Thema, gehört hier nicht mehr rein, denke ich.

HaWe
12.09.2018, 19:15
Spitzklicker! ;)
auch durch Multiplikation oder shl wird die Variable aber erhöht, insb. im Sinne dieses Themas, und ich sprach ja nicht von "um 1 erhöhen".

Moppi
12.09.2018, 20:06
Erhöhe die Variable X um 5: X=X+5
Vervielfache Variable X, womit? -> mit 2: X=X*2

Bei der Multiplikation wird die Variable um ein Vielfaches erhöht, ja.

erhöhen: inkrementieren oder addieren
erniedrigen: dekrementieren oder subtrahieren
vervielfachen: multiplizieren oder anderes

Von mir aus aber auch nicht. Mach so wie Du denkst. ;-)

HaWe
12.09.2018, 21:17
Erhöhe die Variable X um 5: X=X+5
Vervielfache Variable X, womit? -> mit 2: X=X*2

Bei der Multiplikation wird die Variable um ein Vielfaches erhöht, ja.

erhöhen: inkrementieren oder addieren
erniedrigen: dekrementieren oder subtrahieren
vervielfachen: multiplizieren oder anderes

Von mir aus aber auch nicht. Mach so wie Du denkst. ;-)
nein, erhöhen ist alles, was es erhöht, egal wodurch, und außerdem war klar, was gemeint war, Spitzklicker ;)

Wichtig ist aber nur, dass Erhöhen per Multipl. bzw. shl (und entspr. Erniedrigen per Divis. bzw. shr) die gleiche Zeit dauert 8)