XYZ Farbraum - Darstellungsproblem

  • Moin,
    Das folgende Skript zeigt den XYZ Farbraum in 2 Dimensionen (x,y = 0 -> 100).
    Der Slider verstellt die Z Koordinate.

    Die Darstellung sieht für mich seltsam aus, irgendwas stimmt hier nicht und ich finde den Fehler nicht.
    Man kann in Zeile 48 und 49 durch Auskommentieren zwischen RGB und XYZ umschalten, RGB läuft (wie üblich) Problemlos.

    Skript
    [autoit]

    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $iSize = 5 ; Größe der Kacheln in Px
    Global Const $iAnz = 125 ; Anzahl der Kacheln in x, y und z Richtung

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGUI, $hSli, $iZ, $hGFX, $hBRU, $hBUF, $hBMP

    [/autoit] [autoit][/autoit] [autoit]

    $hGUI = GUICreate('XYZ Farbraum', $iSize * $iAnz, $iSize * $iAnz + 25)

    [/autoit] [autoit][/autoit] [autoit]

    $hSli = GUICtrlCreateSlider(0, $iAnz * $iSize, $iSize * $iAnz, 25)
    GUICtrlSetLimit($hSli, 1000, 0)

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()
    $hGFX = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hBMP = _GDIPlus_BitmapCreateFromGraphics($iSize * $iAnz, $iSize * $iAnz, $hGFX)
    $hBUF = _GDIPlus_ImageGetGraphicsContext($hBMP)
    $hBRU = _GDIPlus_BrushCreateSolid()

    [/autoit] [autoit][/autoit] [autoit]

    GUIRegisterMsg(0xF, 'WM_PAINT')
    GUISetState(@SW_SHOW)

    [/autoit] [autoit][/autoit] [autoit]

    _ApplyCol(0.9)

    [/autoit] [autoit][/autoit] [autoit]

    While GUIGetMsg() <> -3
    If GUICtrlRead($hSli) <> $iZ Then
    $iZ = GUICtrlRead($hSli)
    _ApplyCol($iZ/1000)
    EndIf
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_GraphicsDispose($hGFX)
    _GDIPlus_BrushDispose($hBRU)
    _GDIPlus_GraphicsDispose($hBUF)
    _GDIPlus_BitmapDispose($hBMP)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT()
    _GDIPlus_GraphicsDrawImage($hGFX, $hBMP, 0, 0)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ApplyCol($Z = 0)
    Local $t = TimerInit()
    Local $sCol, $iAnzMinus1 = $iAnz - 1, $klX, $klY
    For $x = 0 To $iAnzMinus1 Step 1
    For $y = 0 To $iAnzMinus1 Step 1
    $klX = $x / $iAnzMinus1
    $klY = $y / $iAnzMinus1
    $sCol = _XYZ_to_RGB($klX, $klY, $Z)
    ;~ $sCol = '0xFF' & Hex(Int(Round($klX * 255, 0)),2) & Hex(Int(Round($klY * 255, 0)),2) & Hex(Int(Round($Z * 255, 0)),2)
    DllCall($ghGDIPDll, 'int', 'GdipSetSolidFillColor', 'handle', $hBRU, 'dword', $sCol)
    DllCall($ghGDIPDll, 'int', 'GdipFillRectangleI', 'handle', $hBUF, 'handle', $hBRU, 'int', $x * $iSize, 'int', $y * $iSize, 'int', $iSize, 'int', $iSize)
    Next
    If IsInt($x/3) Then WM_PAINT()
    Next
    WM_PAINT()
    $t = TimerDiff($t)
    ConsoleWrite('Dauer für ' & $iAnz^2 & ' Kacheln: ' & Round($t/1000, 2) & 's' & @TAB & ' Zeit pro Kachel: ' & Round($t /$iAnz^2, 2) & 'ms' & @CRLF)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; Von BugFix
    Func _XYZ_to_RGB($iX, $iY, $iZ)
    Local $iR, $iG, $iB
    $iR = ($iX * 3.2406) + ($iY * -1.5372) + ($iZ * -0.4986)
    $iG = ($iX * -0.9689) + ($iY * 1.8758) + ($iZ * 0.0415)
    $iB = ($iX * 0.0557) + ($iY * -0.2040) + ($iZ * 1.0570)
    If $iR > 0.0031308 Then
    $iR = 1.055 * ($iR ^ (1/2.4)) - 0.055
    Else
    $iR *= 12.92
    EndIf
    If $iG > 0.0031308 Then
    $iG = 1.055 * ($iG ^ (1/2.4)) - 0.055
    Else
    $iG *= 12.92
    EndIf
    If $iB > 0.0031308 Then
    $iB = 1.055 * ($iB ^ (1/2.4)) - 0.055
    Else
    $iB *= 12.92
    EndIf
    Return '0xFF' & Hex(Int($iR*255), 2) & Hex(Int($iG*255), 2) & Hex(Int($iB*255), 2)
    EndFunc ;==>_XYZ_to_RGB

    [/autoit]

    lg
    Mars

  • :D

    Ich vermute, dass es irgendwie mit dem Definitionsbereich zu tun hat. Ich denke, dass nur dieses schiefe Viereck in der Mitte relevant ist.
    (also dass alle RGB Farben in dieses Viereck abgebildet werden).
    Sollte das zutreffen: Wie filtere ich relevante Werte heraus ?

  • So eventuell?

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $iSize = 5 ; Größe der Kacheln in Px
    Global Const $iAnz = 125 ; Anzahl der Kacheln in x, y und z Richtung

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGUI, $hSli, $iZ, $hGFX, $hBRU, $hBUF, $hBMP

    [/autoit] [autoit][/autoit] [autoit]

    $hGUI = GUICreate('XYZ Farbraum', $iSize * $iAnz, $iSize * $iAnz + 25)

    [/autoit] [autoit][/autoit] [autoit]

    $hSli = GUICtrlCreateSlider(0, $iAnz * $iSize, $iSize * $iAnz, 25)
    GUICtrlSetLimit($hSli, 1000, 0)

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()
    $hGFX = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hBMP = _GDIPlus_BitmapCreateFromGraphics($iSize * $iAnz, $iSize * $iAnz, $hGFX)
    $hBUF = _GDIPlus_ImageGetGraphicsContext($hBMP)
    $hBRU = _GDIPlus_BrushCreateSolid()

    [/autoit] [autoit][/autoit] [autoit]

    GUIRegisterMsg(0xF, 'WM_PAINT')
    GUISetState(@SW_SHOW)

    [/autoit] [autoit][/autoit] [autoit]

    _ApplyCol(0.9)

    [/autoit] [autoit][/autoit] [autoit]

    While GUIGetMsg() <> -3
    If GUICtrlRead($hSli) <> $iZ Then
    $iZ = GUICtrlRead($hSli)
    _ApplyCol($iZ/1000)
    EndIf
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_GraphicsDispose($hGFX)
    _GDIPlus_BrushDispose($hBRU)
    _GDIPlus_GraphicsDispose($hBUF)
    _GDIPlus_BitmapDispose($hBMP)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT()
    _GDIPlus_GraphicsDrawImage($hGFX, $hBMP, 0, 0)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ApplyCol($Z = 0)
    Local $t = TimerInit()
    Local $sCol, $iAnzMinus1 = $iAnz - 1, $klX, $klY
    For $x = 0 To $iAnzMinus1 Step 1
    For $y = 0 To $iAnzMinus1 Step 1
    $klX = $x / $iAnzMinus1
    $klY = $y / $iAnzMinus1
    $sCol = _XYZ_to_RGB($klX, $klY, $Z)
    ;~ $sCol = '0xFF' & Hex(Int(Round($klX * 255, 0)),2) & Hex(Int(Round($klY * 255, 0)),2) & Hex(Int(Round($Z * 255, 0)),2)
    DllCall($ghGDIPDll, 'int', 'GdipSetSolidFillColor', 'handle', $hBRU, 'dword', $sCol)
    DllCall($ghGDIPDll, 'int', 'GdipFillRectangleI', 'handle', $hBUF, 'handle', $hBRU, 'int', $x * $iSize, 'int', $y * $iSize, 'int', $iSize, 'int', $iSize)
    Next
    If IsInt($x/3) Then WM_PAINT()
    Next
    WM_PAINT()
    $t = TimerDiff($t)
    ConsoleWrite('Dauer für ' & $iAnz^2 & ' Kacheln: ' & Round($t/1000, 2) & 's' & @TAB & ' Zeit pro Kachel: ' & Round($t /$iAnz^2, 2) & 'ms' & @CRLF)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; Von BugFix
    Func _XYZ_to_RGB($iX, $iY, $iZ)
    Local $iR, $iG, $iB
    $iR = ($iX * 0.4124564) + ($iY * 0.3575761) + ($iZ * 0.1804375)
    $iG = ($iX * 0.2126729) + ($iY * 0.7151522) + ($iZ * 0.0721750)
    $iB = ($iX * 0.0193339) + ($iY * 0.1191920) + ($iZ * 0.9503041)
    If $iR > 0.0031308 Then
    $iR = 1.055 * ($iR ^ (1/2.4)) - 0.055
    Else
    $iR *= 12.92
    EndIf
    If $iG > 0.0031308 Then
    $iG = 1.055 * ($iG ^ (1/2.4)) - 0.055
    Else
    $iG *= 12.92
    EndIf
    If $iB > 0.0031308 Then
    $iB = 1.055 * ($iB ^ (1/2.4)) - 0.055
    Else
    $iB *= 12.92
    EndIf
    Return '0xFF' & Hex(Int($iR*255), 2) & Hex(Int($iG*255), 2) & Hex(Int($iB*255), 2)
    EndFunc ;==>_XYZ_to_RGB

    [/autoit]
  • Das sieht schon wesentlich besser aus.
    Die ganze Änderung hängt an der Matrixmultiplikation wenn ich das richtig sehe.
    Ab einem relativ hohen Wert von Z (Slider recht weit rechts, so etwa bei 0.916) erscheint bei mir eine gelbgrüne Fläche von unten. Bei Z=0 ist oben links die Farbe Schwarz, bei 0.916 ist unten rechts die Farbe Weiß. Für höhere Z macht die Berechnung also keinen Sinn. Wie kann man alle Z bestimmen, für die die Berechnung bei 0<=x<=1 und 0<=y<=1 Sinn ergibt ?

  • Der XYZ-Farbraum kann Farben enthalten welche im RGB-Farbraum nicht vorhanden sind. Es gibt also nicht für jeden XYZ-Wert eine Entsprechung im RGB-Raum.
    Es kann bei der Umrechnung durchaus vorkommen, dass für die einzelnen RGB-Komponenten Werte außerhalb des Bereiches 0..255 herauskommen. Das wären dann solche nicht definierten Farben.
    Man muss also diese Fälle noch abfangen in dem man die resultierenden Werte in diesen Wertebereich presst (was natürlich aber mit Informationsverlust einhergeht).

    Z.B. so:

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <Math.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $iSize = 5 ; Größe der Kacheln in Px
    Global Const $iAnz = 125 ; Anzahl der Kacheln in x, y und z Richtung

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGUI, $hSli, $iZ, $hGFX, $hBRU, $hBUF, $hBMP

    [/autoit] [autoit][/autoit] [autoit]

    $hGUI = GUICreate('XYZ Farbraum', $iSize * $iAnz, $iSize * $iAnz + 25)

    [/autoit] [autoit][/autoit] [autoit]

    $hSli = GUICtrlCreateSlider(0, $iAnz * $iSize, $iSize * $iAnz, 25)
    GUICtrlSetLimit($hSli, 1000, 0)

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()
    $hGFX = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hBMP = _GDIPlus_BitmapCreateFromGraphics($iSize * $iAnz, $iSize * $iAnz, $hGFX)
    $hBUF = _GDIPlus_ImageGetGraphicsContext($hBMP)
    $hBRU = _GDIPlus_BrushCreateSolid()

    [/autoit] [autoit][/autoit] [autoit]

    GUIRegisterMsg(0xF, 'WM_PAINT')
    GUISetState(@SW_SHOW)

    [/autoit] [autoit][/autoit] [autoit]

    _ApplyCol(0.9)

    [/autoit] [autoit][/autoit] [autoit]

    While GUIGetMsg() <> -3
    If GUICtrlRead($hSli) <> $iZ Then
    $iZ = GUICtrlRead($hSli)
    _ApplyCol($iZ / 1000)
    EndIf
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_GraphicsDispose($hGFX)
    _GDIPlus_BrushDispose($hBRU)
    _GDIPlus_GraphicsDispose($hBUF)
    _GDIPlus_BitmapDispose($hBMP)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT()
    _GDIPlus_GraphicsDrawImage($hGFX, $hBMP, 0, 0)
    EndFunc ;==>WM_PAINT

    [/autoit] [autoit][/autoit] [autoit]

    Func _ApplyCol($Z = 0)
    Local $t = TimerInit()
    Local $sCol, $iAnzMinus1 = $iAnz - 1, $klX, $klY
    For $x = 0 To $iAnzMinus1 Step 1
    For $y = 0 To $iAnzMinus1 Step 1
    $klX = $x / $iAnzMinus1
    $klY = $y / $iAnzMinus1
    $sCol = _XYZ_to_RGB($klX, $klY, $Z)
    ;~ $sCol = '0xFF' & Hex(Int(Round($klX * 255, 0)),2) & Hex(Int(Round($klY * 255, 0)),2) & Hex(Int(Round($Z * 255, 0)),2)
    DllCall($ghGDIPDll, 'int', 'GdipSetSolidFillColor', 'handle', $hBRU, 'dword', $sCol)
    DllCall($ghGDIPDll, 'int', 'GdipFillRectangleI', 'handle', $hBUF, 'handle', $hBRU, 'int', $x * $iSize, 'int', $y * $iSize, 'int', $iSize, 'int', $iSize)
    Next
    If IsInt($x / 3) Then WM_PAINT()
    Next
    WM_PAINT()
    $t = TimerDiff($t)
    ConsoleWrite('Dauer für ' & $iAnz ^ 2 & ' Kacheln: ' & Round($t / 1000, 2) & 's' & @TAB & ' Zeit pro Kachel: ' & Round($t / $iAnz ^ 2, 2) & 'ms' & @CRLF)
    EndFunc ;==>_ApplyCol

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func NormalizeTo255($c)
    Return Hex(_Min(255, _Max(0, Int($c * 255))), 2)
    EndFunc ;==>NormalizeTo255

    [/autoit] [autoit][/autoit] [autoit]

    Func _XYZ_to_RGB(Const $iX, Const $iY, Const $iZ)
    Local $iR, $iG, $iB
    $iR = ($iX * 3.2406) + ($iY * - 1.5372) + ($iZ * - 0.4986)
    $iG = ($iX * - 0.9689) + ($iY * 1.8758) + ($iZ * 0.0415)
    $iB = ($iX * 0.0557) + ($iY * - 0.2040) + ($iZ * 1.0570)

    [/autoit] [autoit][/autoit] [autoit]

    If $iR > 0.0031308 Then
    $iR = 1.055 * ($iR ^ (1.0 / 2.4)) - 0.055
    Else
    $iR *= 12.92
    EndIf
    If $iG > 0.0031308 Then
    $iG = 1.055 * ($iG ^ (1.0 / 2.4)) - 0.055
    Else
    $iG *= 12.92
    EndIf
    If $iB > 0.0031308 Then
    $iB = 1.055 * ($iB ^ (1.0 / 2.4)) - 0.055
    Else
    $iB *= 12.92
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Return '0xFF' & NormalizeTo255($iR) & NormalizeTo255($iG) & NormalizeTo255($iB)
    EndFunc ;==>_XYZ_to_RGB

    [/autoit]

    Die Funktion welche m-obi gepostet hat ist übrigens nicht die Umrechnung von XYZ zu RGB sondern von RGB zu XYZ.

  • Ich glaub jetzt hab ichs durchblickt.
    Um eine Verlustfreie Umrechnung zu erhalten müsste es eine bijektive Abbildung zwischen dem [0,1]³RGB und [0,1]³XYZ Intervall geben, die beide Intervalle exakt abdeckt. Der XYZ Raum im Intervall [0,1]³ beinhaltet aber Werte die außerhalb des RGB Intervalls liegen. Daher ist [0,1]³XYZ > [0,1]³RGB.

  • Um eine Verlustfreie Umrechnung zu erhalten müsste es eine bijektive Abbildung zwischen dem [0,1]³RGB und [0,1]³XYZ Intervall geben


    Mensch jetzt wird aber tief in der Wörterkiste gekramt... ;)

    Ich bin zwar kein Mathematiker würde dir aber beipflichten. Die Umwandlung XYZ-RGB ist nicht bijektiv da die Umkehrfunktion nicht immer wieder die selben Ausgangswerte erzeugt.
    Wenn wir noch weiter in der Wörterkiste kramen können wir noch sagen dass die Abbildung XYZ-RGB surjektiv ist und die Abbildung RGB-XYZ injektiv. ;)

  • ...und subjektiv betrachtet (das ist ein Wortspiel^^) sieht sowieso niemand den Unterschied!