Hallo,
ich habe WS2812B im Einsatz, die die Farben von 0° - 359° im
HSV-Farbraum durchlaufen.
Gibt es Ansätze, wie ich diese abgleichen kann?
Das Problem ist, dass die Farbübergänge mal mehr oder weniger stark hell
sind.
Derzeit mache ich eine Einfache umrechnung von HSV 2 RGB
Stefan S. schrieb:> Gibt es Ansätze, wie ich diese abgleichen kann?
Mit dieser Tabelle nicht.
Die unteren 32 Werte werden mit nur 2 Werten aus der Tabelle ersetzt,
also mit AUS und 1. Und Werte über 165 dann in zweier Schritten. Das
ist zu grob für kleine Werte und zu fein für grosse.
Wenn du die Helligkeit logarithmisch ändern willst, dann sind 256
Stufen für Werte von 0-255 reiner Unsinn. Wenn es linear gehen soll,
brauchst du die Tabelle überhaupt nicht. Normal sind 50 Schritte gut
genug, aber ohne interpolieren wird das nichts.
Dazu musst du eine Zeitspanne definieren, in der du das machst, z.B.
10ms für eine Genauigkeit von 0.1 bei einer Wiederholfrequenz von
1KHz. Ich glaube, WS2812 können von 400KHz bis 800KHz laufen, also kann
man 400 bis 800 LEDs ansteuern bei einer 100% Auslastung der CPU.
... schrieb:> man könnte auch mit einem anderen faktor multplizieren... dann könnte> man durch 128 dividieren und nicht durch 100
Nutzlos.
Werte von 0-255 kann man nicht logarithmisch in 256 Schritten
darstellen, da kann man multiplizieren und dividieren so oft man
will...
@Marc Vesely: Fürs Fading mit normalen RGB Werten benötige ich die
Tabelle schon, da es ansonsten nicht gleichmäßig linear abläuft. 256
Schritte sind vielleicht nicht notwendig. Kann ich auf weniger Werte
kürzen, zB würde sich 64 anbieten. Wenn mich nicht alles täuscht,
arbeitet die B-Variante nur noch mit der schnellen 800er Variante. Ohne
B oder auch als S-Variante konnte meines Wissens auch die langsamere
Variante. 400 oder 800 Leds sind garnicht geplant, dass die angesteuert
werden. Derzeitige Tests mache ich meist mit 32 Leds bis hin zu ~80. Für
mehr habe ich keine Einsatzzwecke. Will auch den Attiny85 damit nicht
überstrapezieren ^^ Der läuft mit intern mit 8MHz gut.
Was schwebt dir bei Interpolation im Kopf herum? Ich habe gerade mal auf
http://wiki.delphigl.com/index.php/Farbraum etwas gefunden, dass ich mir
nachher mal anschauen werde.
1
// Based on C Code in "Computer Graphics -- Principles and Practice,"
2
// Foley et al, 1996, p. 593.
3
public static RGB HSVToRGB(double h, double s, double v)
4
{
5
double f, hTemp, p,q,t;
6
int i;
7
RGB Result = new RGB();
8
if (s == 0) {
9
//Achromatic
10
Result.R = v;
11
Result.G = v;
12
Result.B = v;
13
return Result;
14
}
15
if (h < 0)
16
h = Math.PI * 2 - h;
17
18
if (h > 2 * Math.PI)
19
h = h - Math.Truncate(1.0 / (Math.PI * 2) *h)* (Math.PI * 2);
20
21
hTemp = h / (2*Math.PI / 6);
22
i = (int) Math.Truncate(hTemp); // largest integer <= h
@easylife: Wird vom Compiler entsprechend bereits verbessert. Kann das
notfalls aber noch einmal exciplit casten. Wobei die errechneten Werte
denen entsprechen.
@...:
Wie stellst du dir genau vor? Jeden Farbwert anfahren und dann
nachjustieren wäre relativ aufwendig und vermutlich speicherlastiger als
die Methode von Marc.
... schrieb:> man könnte auch mit einem anderen faktor multplizieren... dann könnte> man durch 128 dividieren und nicht durch 100
oder durch 256 (bitshift um 8 nach rechts).
Der Faktor wäre dann 2.56 * 425 = 1088
Stefan S. schrieb:> Was schwebt dir bei Interpolation im Kopf herum? Ich habe gerade mal auf> http://wiki.delphigl.com/index.php/Farbraum
Nööö, muss schon auf 10 Werte hintereinander bezogen sein, etwa so:
Werte in der Tabelle (1, 1.12, 1.25, 1,4, 1,6, ..., 255)
1.12 kriegst du mit 10 Werten nicht hin, aber 1,1 schon.
5 mal die 1, 1 mal die 2 und 4 mal wieder 1.
50 Helligkeitsstufen mal 10 Werte ergibt eine Tabelle mit 500 Bytes.
Etwa doppelt so gross wie die alte Tabelle, dafür hast du aber
fließende Übergänge und brauchst nur mit Offset und nicht mit PI
zu rechnen (Double oder Float) - viel, viel schneller.
Die Funktion hsv() kapiere ich um ehrlich zu sein auch nicht.
Also hauptsächlich den unteren Teil, mit diff und so.
Vielleicht kannst du dir ja bei ein paar talentierten Grafikspezialisten
was abgucken (GIMP Projekt).
Die Funktion arbeitet mit doubles, aber das kann man ja ändern.
Btw.
1
if(hue == 360) hue = 0;
2
else if(hue > 360) return;
könnte auch einfach
1
hue %= 360;
sein.
1
/**
2
* gimp_hsv_to_rgb:
3
* @hsv: A color value in the HSV colorspace
4
* @rgb: The returned RGB value.
5
*
6
* Converts a color value from HSV to RGB colorspace
@Easylife:
"Vielleicht kannst du dir ja bei ein paar talentierten
Grafikspezialisten
was abgucken (GIMP Projekt).
Die Funktion arbeitet mit doubles, aber das kann man ja ändern."
Das mit Modulo hatte ich auch drin, hab es aber irgendwann zwischenzeit
mal rausgenommen, da ich gerne wollte, dass die Funktion abgebrochen
wird, wenn willkürliche Werte > 360 reinkommen. Das aber hier nicht das
Problem.
So einen ähnlichen Code hatte ich die Tage bereits schon mal gefunden.
War nicht mein eigener Code, hatte den nur minimal angepasst. Kam aber
nicht besser weg.
Werde mir nachher mal anschauen, in wie fern sich das GIMP Projekt davon
unterscheidet. Wobei ich denke, dass es sich da auch nur um die reine
Berechnung ohne Rücksicht auf die menschliche Wahrnehmung ist.
Tja, alle drei Funktionen liefern unterschiedliche Ergebnisse.
Deine Funktion weicht allerdings von den beiden anderen am meisten ab.
Anbei ein Programm, das alle 3 Funktionen miteinander vergleichen lässt.
Läuft natürlich besser auf dem Desktop...
Gibt es zu deinem Test auch die Ausgabe? Ich gehe davon aus, dass du es
getestet hast. Werde es gleich mal testen und schauen, ob
ich irgendwo noch nen Atmel frei rumliegen hab mit Hardware UART.
Stefan S. schrieb:> Gibt es zu deinem Test auch die Ausgabe? Ich gehe davon aus, dass du es> getestet hast. Werde es gleich mal testen und schauen, ob> ich irgendwo noch nen Atmel frei rumliegen hab mit Hardware UART.
Für solche Tests hat man am PC immer einen C Compiler bereits
installiert.
> Wobei ich denke, dass es sich da auch nur um die reine Berechnung> ohne Rücksicht auf die menschliche Wahrnehmung ist.
Die Annahme ist bei solchen Dingen immer, dass die Helligkeitszunahme
linear ist. Denn die menschliche Wahrnehmung ist noch lange nicht alles,
was es zu berücksichtigen gäbe. Kaum ein Ausgabegerät hat einen linearen
ZUsammenhang zwischen Ansteuerwert und erzeugter Helligkeit. Das beginnt
beim Monitor und hört beim Drucker noch lange nicht auf. Und bei einem
anderen Drucker bzw. einem anderen Druckermodell ist wieder alles ganz
anders.
Was hierbei auch noch zu tragen kommen könnte, das nicht alle Farbräume
deckungsgleich sind. Und gefühlt würd ich sagen, dass der HSV-Farbraum
größer als der RGB-Raum ist, da kann ich mich aber auch irren.
Also müsste man nicht nur logarithmisch interpolieren um die Helligkeit
ans Auge anzupassen, sondern muss doch auch noch zwischen den Werten der
Farbräume interpolieren. Wobei ich mir den Code jetzt nicht wirklich
angeschaut hab....
Aber evtl hilft der Gedanke ja auch weiter =)
Stefan S. schrieb:> Gibt es zu deinem Test auch die Ausgabe?
Die Ausgabe in 36° / 25-er Schritten alleine ist schon 48 MB groß. Ein
uC mit UART wird dir da nicht weiterhelfen.
Du solltest einen C-Compiler (z.B. gcc) auf deinen Rechner installieren,
da du ja an der Funktion sicher noch rumbasteln willst.
Die Gamma-Anpassung im RGB Raum zu machen finde ich prinzipiell richtig,
da einfacher. Um die Genauigkeit zu erhöhen, könntest du bei der
HSV->RGB Umrechnung z.B. nicht gleich auf 8-bit runterteilen, sondern
erst bei/nach der Gamma-Korrektur im RGB Raum.
j. t. schrieb:> Also müsste man nicht nur logarithmisch interpolieren um die Helligkeit> ans Auge anzupassen, sondern muss doch auch noch zwischen den Werten der> Farbräume interpolieren.
Ich glaube, das ginge dann doch ein bisschen zu weit.
Es geht hier nicht um farbgetreue Bildwiedergabe an einem RGB-Monitor,
sondern um WS2812 steuerung. Ganz einfache LEDs, wahrscheinlich als
Lightshow oder Ambilight. Ich habe Lightshow mit 2-Punkt RGB gesteuert
und keiner hat es gemerkt, das hat aber mehr als 30% bisherigen Codes
erspart und das Ganze um etwa genausoviel schneller gemacht.
Irgendwo muss man ja eine Grenze setzen.