Hallo zusammen, ich verzweifle gerade an der Umrechnung des HSV-Farbraums in den RGB-Farbraum. Für einen LED-Strip mit 60 RGB-LEDs (einzeln ansteuerbar) möchte ich mit einem ATmega88PA ein paar Animationen programmieren. Leider scheitere ich nun an der genannten Farbraumkonvertierung. Im Internet gibt es ja bereits einige derartige Funktionen, doch leider verwenden diese Gleitkommazahlen, auf die ich im ATmega88-Code verzichten möchte. Die Wiedergabe beliebiger RGB-Animationen funktioniert fehlerfrei. Soweit funktioniert das Programm also. Nun habe ich eine hsv2rgb-Funktion mit Ganzzahlen geschrieben, doch leider sind die Ergebnisse nicht ganz das was ich erwartet habe. Hier ein Video was passiert: http://www.myvideo.de/watch/9162393/HSV_nach_RGB_konvertierungs_fehler Das Video zeigt, was der angehängte Code macht. Erwartet hätte ich einen Farbverlauf von Rot(links) über grün nach blau bis lila/rosa(rechts). Mit der Zeit sollte dieser Farbverlauf dann lediglich heller werden. Was alledings das Video zeigt, kann ich mir so gar nicht erklären. Im Video links ist übrigends die 1. und rechts die 60. LED am Strang. In der mitte wird nur der Strom eingespeist und ganz links eben die Daten von einem ATmega88PA und einer kleinen Schaltung die das SPI-Signal in ein LED-Verständliches Format bringt. Meinen Code hänge ich einfach mal in den Anhang. Vielleicht erkennt ja jemand meinen Fehler. Ich bedanke mich bereits für jegliche Hilfe und Ideen. Vielen Dank Gruß Lukas
> Hier ein Video was passiert: > http://www.myvideo.de/watch/9162393/HSV_nach_RGB_k... Warum sollte ich mich da anmelden?
> Da dieses Video privat ist, musst Du Dich einloggen, um Dir das Video anzusehen.
Soweit kommt das noch, dass ich der nächsten Datenkranke meine Daten in
den Hals schmeiße!
Tut mir leid. Kleines Missgeschick. Das wollte ich nicht! Fehler sollte behoben sein! Das Video ist jetzt öffentlich!
Als ob beim Anmelden bei so einer Seite irgendwelche privaten Daten preisgegeben werden... Für so etwas habe ich seit ettlichen Jahren ein eigenes E-Mail Postfach. Es gibt sogar 10 Minuten Mailadressen für sowas.
1 | region = (uint8_t)( ( (uint16_t)(hsv.h) * 6 ) >> 8 ); |
2 | remainder = (uint8_t)( (uint16_t)(hsv.h) * 6 - ( (uint16_t)(region) << 8 ) ) ; |
3 | |
4 | p = (hsv.v * (255 - hsv.s)) >> 8; |
5 | q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; |
6 | t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; |
Wenn ich das richtig verstehe, dann ist der 'remainder' bei dir das, was der Faktor f (double) zb hier http://codezentrale.bplaced.net/dcz/?p=2999 ist. f läuft normalerweise von 0 bis 1. Bei dir durch die Integer Rechnung, hast du das so aufgeblasen, dass ein Wert von 255 einem Wert von 1 im Original entspricht. Dann kommen mir allerdings die Umsetzungen der Formeln für q und t etwas 'simplifiziert' vor. Durch das zwischendurch dividieren durch 256 verlierst du jedesmal einen Haufen 'Kommastellen'. Ich habs noch nicht durchgerechnet, aber aus dem Bauch raus, ist das etwas zu naiv nach Schema F ohne Rücksicht auf Verluste umgesetzt. Was ich an deiner Stelle tun würde: Ich würde jetzt erst mal auf den PC gehen und dort ein Testprogramm schreiben, bei dem ich die beiden Berechnungen für eine Original-double Version und deiner int Version vergleiche. Spezielles Augenmerk: die Werte für p, q und t. Weiters: Da bei dir alles ein uint8_t ist, kannst du in ein weiteres 'Problem' reinlaufen, welches ich jetzt auch noch nicht näher untersucht habe: Die C Rechenregeln. Die besagen nämlich, dass eine Berechnung mindestens im Datentyp int gemacht wird und kleiner Datentypen erst mal promoted werden. Jetzt ist es aber so, dass zb hier
1 | p = (hsv.v * (255 - hsv.s)) >> 8; |
255 ein int ist. hsv.s ist zwar ein uint8_t, aber die C Regeln sagen weiters, dass diese automatische Promotion in den kleinsten Datentyp erfolgt, der alle Werte aufnehmen kann. Für einen uint8_t ist das aber ein int. Und zwar ein signed int. D.h. das hier 255 - hsv.s wird bereits als vorzeichenbehaftete Subtraktion berechnet. Auch die Multiplikation mit hsv.v ändert nichts mehr daran, dass wir es hier mit vorzeichenbehafteten Zahlen zu tun haben. Und das kann dann ins Auge gehen, wenn ohne Rücksicht auf Verluste um 8 Stellen nach rechts geschoben wird. Denn wenn die Bits richtig stehen, dann werden da nicht nur die Bits verschoben, sondern das Vorzeichenbit rückt nach. Was zu einem, sagen wir mal unsinnigem Ergebnis führt, wenn man eigentlich ein unsigned Ergebnis erwartet. UNter anderem aus solchen Gründen halte ich nicht viel von diesen sog. 'Cleveren Optimierungen', bei denen Multiplikationen oder DIvisionen durch Schieben ersetzt wird. Wenn es möglich ist, das zu tun, dann machen das die Compiler seit 50 Jahren ganz von alleine. Das muss mir als Programmierer keine Kopfzerbrechen machen. Auf jeden Fall würde ich da sicherheitshalber die 255 mal unsigned machen: 255U, damit der ganze Ausdruck auf keinen Fall eine Chance hat, als signed Expression ausgewertet zu werden. Man muss sich immer vor Augen halten, dass hier absichtlich alle 16 Bit voll als unsigned Wert ausgenutzt werden. Sobald da irgendwo, egal wie, ein gesetztes 15-tes Bit als Vorzeichenbit bei irgendeiner Operation fehlinterpretiert wird, gibt es sehr wahrscheinlich irgendwo Ärger. Das können auch die Multiplikationen sein, die dann falsche Ergebnisse liefern. Das darf auf keinen Fall passieren. Daher: festnageln auf unsigned Arithmetik, dem Compiler kein Schlupfloch offen lassen irgendwie auf signed zu wechseln. Speziell die Promotion-Regeln lassen da aber schnell mal ein Schlupfloch, wenn ein Operand signed ist.
Hey danke, du hast offenbar den Fehler genau erkannt... ich habe jetzt die Sättigung rausgenommen und siehe da der Fehler ist weg...
1 | region = (uint8_t)( ( (uint16_t)(hsv.h) * 6U ) / 256 ); |
2 | remainder = (uint8_t)( (uint16_t)(hsv.h) * 6U ); // noch eine kleine Optimierung in dieser Zeile... |
3 | |
4 | p = 0; |
5 | q = (hsv.v * (255U - remainder) ) /256; |
6 | t = (hsv.v * (255U - (255U - remainder))) /256; |
irgendwie geht mir das gerade nur nicht in den Kopf wo genau der Fehler liegt... Ich mache daher nun einfach mal eine kreative Pause und hoffe späte bekomme ich das dann richtig hin. Dir/Euch kann ich derweilen nur Danken. Ihr habt mir riesig geholfen!
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.