Schnelle Gammakorrektur

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Schnelle Gammakorrektur

Beitrag von Krishty »

Hi,

Ich möchte gerne Gammakorrektur für Bilddaten implementieren, die in drei verschiedenen Ausführungen vorliegen können. Wie setze ich das optimal um? Ich habe einige Ideen, aber keine Zeit, die alle auszuprobieren ...

unsigned chars (8-Bit-UNorm): Hier würde ich eine Lookup-Tabelle erzeugen, in der für jeden Helligkeitswert der entsprechende gammakorrigierte Wert vorliegt. Mit einer Größe von 256 Bytes sollte die Tabelle ja noch Cache-freundlich und das ganze damit sehr fix sein, denke ich.

unsigned shorts (16-Bit-UNorm): Eine Lookup-Tabelle mit einem Umfang von 64 KiB wäre katastrophal, also würde ich hier wieder 256 Werte berechnen und zwischen denen linear interpolieren. Wären dabei Präzisionsfehler zu verschmerzen? Bis zu extremen Gamma-Werten dürften die Abweichungen doch nicht größer als eine oder zwei Einheiten sein, oder? (Schließlich sind 256 Samples schon ganz schön viel ...)

halfs und floats (16-/32-Bit-Float): Hier habe ich absolut keine Ahnung, wie ich das performant hinkriegen soll. Ein pow() pro Komponente muss doch unterirdisch langsam sein? Der Werteumfang ist einfach zu groß und das Format zu komplex, um das über Lookups zu lösen, also muss da eine arithmetische Lösung her ...

Von Detailoptimierungen (Bestimmte Samples in den Lookups sparen, Prefetching, SIMD ...) mal abgesehen: Habt ihr Anregungen oder Tipps für mich?

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Schrompf »

pow() ist gar nicht so langsam, würde ich sagen... probier's aus :-) Alternativ eine schlichte Quadrierung, das kommt der Sache nahe genug. Ich geh mal davon aus, dass Du die Gamma-Vorkorrektur meinst, die Texturen mit für die Bildschirmdarstellung vorkorrigierten Farbwerten zurück in den linearen Helligkeitsbereich bringen soll. Die Formel, die umgesetzt werden soll, müsste also pow( farbe, 2.2f) sein. Liege ich da richtig oder hab ich da grad was verwechselt? Mir fällt's gerade schwer, die ganzen Farbräume zu überschauen :-)

Allgemein: 8Bit - die Genauigkeit ist unterirdisch, weil sich ein Großteil der diskreten Werte nach der Abbildung im unteren Wertebereich tummeln. Ich habs im Shader gemacht, eine simple Quadrierung tut's da ja auch. Man kann wohl aber auch einen Sampler State setzen, so dass die Grafikkarte diesen Schritt übernimmt. Habe ich aber noch nicht erprobt.

16Bit - die Genauigkeit sollte hier reichen. Wenn Dir die Quadrierung nicht behagt, ist lineare Interpolation eine gute Idee, um die Anzahl der notwendigen Samples im LUT zu senken. Heutige Prozessoren haben 16 oder 32kb First Level Cache - wenn wir die Hälfte davon für die Tabelle annehmen, hast Du Platz für 4k bzw. 8k Samples. Ich würde also die obersten 12 bzw. 13 bit für den Tabellenzugriff nehmen und die übrigen Bits einfach linear übernehmen - das wäre eine billige Methode der Interpolation, auf Kosten der Abbildungsgenauigkeit der Tabelle. Ergibt außerdem Diskontinuitäten... hmmm... müsste man ausprobieren, wie es aussieht und ob's das wert ist.

16Bit/32Bit-Floats - Quadrieren und fertig. Oder mach's gleich im Shader. Bei 16Bit-Floats kannst Du evtl. die Mantisse durch eine Tabelle abdecken und nur den Exponent manuell berechnen. Bei 32Bit führt nix an der korrekten Berechnung vorbei - egal ob auf der CPU oder im Shader.

Alternativ schnappst Du Dir Intels Threading Building Blocks und schiebst das Texturladen einfach auf Neben-Cores. Dann ist es halbwegs wurscht, wie lange die Vorberechnung bleibt. Würde mich interessieren, wie Du das dann aufziehst.

bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Krishty »

Schrompf hat geschrieben:Ich geh mal davon aus, dass Du die Gamma-Vorkorrektur meinst, die Texturen mit für die Bildschirmdarstellung vorkorrigierten Farbwerten zurück in den linearen Helligkeitsbereich bringen soll.
Nicht unbedingt, ich arbeite momentan an einem kleinen Framework zur Bildbearbeitung. Besonders bei Formaten mit freiem Gamma (PNG) kann die Korrektur statt der üblichen 2.2 und 1/2.2 auch mal anders ausfallen.

Die GPU kann ich in dem Fall leider nicht nutzen, nur einen einzelnen CPU-Core. Aber wenn pow() (liegst mit pow(Farbe, Gamma) richtig) doch nicht so langsam ist, werde ich es einfach mal ausprobieren ...
Schrompf hat geschrieben:Man kann wohl aber auch einen Sampler State setzen, so dass die Grafikkarte diesen Schritt übernimmt. Habe ich aber noch nicht erprobt.
Nutze ich in D3D10 teils schon, allerdings funktioniert das nur genau für Gamma 2.2.
Schrompf hat geschrieben:16Bit - die Genauigkeit sollte hier reichen. Wenn Dir die Quadrierung nicht behagt, ist lineare Interpolation eine gute Idee, um die Anzahl der notwendigen Samples im LUT zu senken. Heutige Prozessoren haben 16 oder 32kb First Level Cache - wenn wir die Hälfte davon für die Tabelle annehmen, hast Du Platz für 4k bzw. 8k Samples. Ich würde also die obersten 12 bzw. 13 bit für den Tabellenzugriff nehmen und die übrigen Bits einfach linear übernehmen - das wäre eine billige Methode der Interpolation, auf Kosten der Abbildungsgenauigkeit der Tabelle. Ergibt außerdem Diskontinuitäten... hmmm... müsste man ausprobieren, wie es aussieht und ob's das wert ist.
Quadrierung ist auch mit 16 Bits schon schwierig, schließlich liegen die Werte ja "roh" nicht zwischen 0 und 1 sondern zwischen 0 und 65535 vor ... aber das mit dem Cache ist gut zu wissen. DXGI bentutzt afaik eine Gamma-Ramp mit 1025 Samples, daraus schließe ich einfach mal, dass so eine Präzision ausreichend ist ... wenn sie auch in den Cache passt, perfekt. Rampe abschneiden? Auf keinen Fall. Ich habe in einem Valve-Paper über die Probleme mit linearen Rampen und Diskontinuitäten auf der XBox gelesen, das hat mir einen Schreck für's Leben eingejagt ;)
Schrompf hat geschrieben:Bei 16Bit-Floats kannst Du evtl. die Mantisse durch eine Tabelle abdecken und nur den Exponent manuell berechnen.
Klingt komplex, aber effizient :)
Schrompf hat geschrieben:Bei 32Bit führt nix an der korrekten Berechnung vorbei - egal ob auf der CPU oder im Shader.
Okay, dann mache ich das so.
Schrompf hat geschrieben:Alternativ schnappst Du Dir Intels Threading Building Blocks und schiebst das Texturladen einfach auf Neben-Cores. Dann ist es halbwegs wurscht, wie lange die Vorberechnung bleibt. Würde mich interessieren, wie Du das dann aufziehst.
Selbst dann würde ich es noch optimieren wollen ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Schrompf »

Dann sind wohl unsere Ansprüche an das Ergebnis wiedermal unterschiedlich :-) Ich habe bisher immer "kommt grob hin, und sieht cool aus" bevorzugt, während Du anscheinend nach einer exakten Lösung suchst.
Krishty hat geschrieben:Nutze ich in D3D10 teils schon, allerdings funktioniert das nur genau für Gamma 2.2.
Hmm, stimmt. Würde mir reichen, aber Du hast schon Recht: theoretisch bringen PNG-Bilder ja ihre eigene Rampe mit... die ich bisher immer ignoriert habe. Aber wir laden sowieso alles bevorzugt aus DXT. Und da herrschen 5Bit Präzision... :-)
Krishty hat geschrieben:Quadrierung ist auch mit 16 Bits schon schwierig, schließlich liegen die Werte ja "roh" nicht zwischen 0 und 1 sondern zwischen 0 und 65535 vor ...
Nö. Multiplikation zweier 16Bit-Werte bringt einen 32Bit-Wert, dann Bitshift um 16 nach rechts und Du hast eine sauber quadrierte 16Bit-Zahl im Wertebereich 0...65535. Und das perfekt parallel in Daten und Operationen, da dürfte eine moderne Pipeline-CPU durchrennen.
Ich habe in einem Valve-Paper über die Probleme mit linearen Rampen und Diskontinuitäten auf der XBox gelesen, das hat mir einen Schreck für's Leben eingejagt ;)
Hast Du dazu einen Link? Das würde mich interessieren...

Bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Krishty »

Schrompf hat geschrieben:Nö. Multiplikation zweier 16Bit-Werte bringt einen 32Bit-Wert, dann Bitshift um 16 nach rechts und Du hast eine sauber quadrierte 16Bit-Zahl im Wertebereich 0...65535. Und das perfekt parallel in Daten und Operationen, da dürfte eine moderne Pipeline-CPU durchrennen.
Aber sowas von touché.
Schrompf hat geschrieben:Hast Du dazu einen Link? Das würde mich interessieren...
Hier ab Seite 12.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Schrompf »

Danke :-)
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Krishty »

Okay, ich habe eben mal die Gammakorrektur für 8-Bit-Bilder getestet (256 Werte große Lookup-Tabelle).

Dieses 2920×2336 Pixel große Bild habe ich als Referenz genutzt (weil es hübsch viele Cache-Misses produzieren würde, wenn der Cache zu klein wäre), die Berechnung hat ein einzelner Core-2-Kern mit 2.4 Ghz durchgeführt, kompiliert habe ich auf höchster Optimierungsstufe … mit 0.02 s ist die (nicht manuell optimierte) Lookup-Tabelle für beliebiges Gamma genau 10× schneller als die Konvertierung, Berechnung und Rückkonvertierung mit Gleitkommazahlen.

Ich werde das jetzt vielleicht noch ein wenig weiter optimieren und danach die 16-Bit-Werte anpacken.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8246
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Krishty »

Jetzt habe ich aber doch ein Problem mit 16-Bit-Werten, genauer gesagt mit der Interpolation …

Um interpolieren zu können, brauche ich in der Lookup entweder einen Wert an genau der Position des Input-Werts oder einen links- oder rechtsseitigen Wert. Das bedeutet beispielsweise: Möchte ich 256 Werte aus 128 erzeugen, so dass jeder zweite interpoliert ist, brauche ich in Wirklichkeit 129 Werte. Der Wert ganz am rechten Rand müsste nämlich interpoliert werden und dafür brauche ich einen Wert rechtsseitig des Maximums.

Soweit, sogut … nur kann dieser Wert einfach nicht existieren, denn der rechtsseitige Wert ist das Maximum und Werte rechts davon wären größer als das Maximum. Werte größer als das Maximum können aber nicht mehr im Datentyp gespeichert werden.

In besagtem Fall würde ich den Wert ja einfach hardcoden. Wenn ich nun aber nur jeden vierten, achten usw. Wert berechnen möchte, muss ich schon drei, sieben usw. Werte interpolieren …

Also müsste ich einen Wert am linken Rand aufstellen, einen am rechten Rand, und die anderen dazwischen verteilen. Aber das ist nicht möglich, weil die Anzahl der möglichen Werte immer eine Zweierpotenz ist und die Samples dann ungleichmäßig verteilt würden … :/

Wie stellt man so eine Tabelle optimal auf?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4859
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Schnelle Gammakorrektur

Beitrag von Schrompf »

Je nachdem, wie Du darauf zugreifen willst. Ich stelle mir das so vor:

16 Bit - aber nur 10 Bit Tabelle. Die 16 Bits wären also wie folgt aufgeteilt: FEDCBA9876 | 543210

Für die vorderen 10 Bit erstellst Du nun eine Tabelle. In die Tabelle schreibst Du den exakten Wert der Abbildung für jeweils den Basiswert FEDCBA9876 000000. Die Tabelle gibt also den untersten Wert aus der jeweiligen Gruppe an. In dem Fall fügst Du der Tabelle noch einen 1025ten Wert hinzu, der die obere Grenze darstellt, und interpolierst dann fröhlich drauflos zwischen dem Tabellenwert und dem Folgewert anhand der hinteren 6 Bits 0000000000 543210. Das kommt jetzt nicht perfekt genau hin, da die wirkliche Eins nur mit "eins mehr als der Wertebereich" erreicht werden würde, aber ein 6500stel Abweichung ist ok, denke ich. Alternativ könntest Du den 1025sten Tabellenwert so berechnen, das der oberste 16Bit-Basiswert nach der Interpolation damit dann doch Eins ergibt... damit ist die Skale dann nicht mehr perfekt linear, aber der Wertebereich der Abbildung stimmt wieder.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Antworten