Selektive Farbe bei Bildern - Verbessern und beschleunigen

  • Hallo,
    meine Kamera hat den Effektfilter 'Selektive Farbe'. Ich habe nun versucht das ganze nachzubauen, bin aber noch nicht wirklich zufrieden.
    Vielleicht hat jemand eine Idee, wie man das besser und schneller lösen könnte. Schneller kann ich es bestimmt mit einer DLL machen, aber vor allem sollte der Effekt besser funktionieren. Am besten erst mit kleinen Bildern probieren.
    Vielen Dank für jede Hilfe von den Grafik-Profis hier!

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>
    #include <Color.au3>
    #include <WinAPI.au3>
    #include <Array.au3>

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

    Opt("GUIOnEventMode", 1)

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

    Global $sPath = FileOpenDialog("Choose Picture", @ScriptDir, "Pictures (*.bmp;*.jpg;*.png)", 1)
    If @error Then Exit

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

    Global $iCol = -1

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

    _GDIPlus_Startup()
    Global $hImage = _GDIPlus_ImageLoadFromFile($sPath)
    Global $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Global $iHeight = _GDIPlus_ImageGetHeight($hImage)
    ;~ _GDIPlus_ImageGetPixelFormat
    _GDIPlus_ImageDispose($hImage)

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

    Global $hGui = GUICreate("Color effect - Click to choose color", $iWidth, $iHeight);, -1, -1, 0x80000000)
    Global $nPic = GUICtrlCreatePic($sPath, 0, 0, $iWidth, $iHeight)
    GUICtrlSetOnEvent(-1, "_GetColor")
    GUISetState(@SW_SHOW, $hGui)

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

    Do
    Until $iCol <> -1
    Opt("GUIOnEventMode", 0)

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

    WinSetTitle("Color effect", "", "Color effect - Please wait")

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

    Global $hBitmap = _GDIPlus_BitmapCreateFromFile($sPath)
    Global $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD + $GDIP_ILMWRITE, $GDIP_PXF32RGB)

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

    ;Stride - Offset, in bytes, between consecutive scan lines of the bitmap.
    ;If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    Global $iStride = DllStructGetData($tBitmapData, "Stride")
    ;Pixel format - Integer that specifies the pixel format of the bitmap
    Global $iPixelFormat = DllStructGetData($tBitmapData, "Format")
    ;Scan0 - Pointer to the first (index 0) scan line of the bitmap.
    Global $pScan0 = DllStructGetData($tBitmapData, "Scan0")

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

    Global $tPixel

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

    Global $iColPixel, $aCol[3], $aSetCol = _ColorGetRGB($iCol)
    Global $iTol = 80, $Luma

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

    For $row = 0 To $iHeight - 1
    For $col = 0 To $iWidth - 1
    $tPixel = DllStructCreate("dword", $pScan0 + $row * $iStride + $col * 4)
    $iColPixel = DllStructGetData($tPixel, 1)
    $aCol[0] = BitAND(BitShift($iColPixel, 16), 0xFF)
    $aCol[1] = BitAND(BitShift($iColPixel, 8), 0xFF)
    $aCol[2] = BitAND($iColPixel, 0xFF)

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

    If Not _ColorInTolerance($aSetCol, $aCol, $iTol) Then
    $Luma = $aCol[0] * 0.3 + $aCol[1] * 0.59 + $aCol[2] * 0.11
    DllStructSetData($tPixel, 1, BitOR($Luma, BitShift($Luma, -8), BitShift($Luma, -16)))
    EndIf
    Next
    Next

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData)

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

    Global $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($nPic, 370, 0, $hHBitmap))

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

    WinSetTitle("Color effect", "", "Color effect - Finished")

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

    Do
    Until GUIGetMsg() = -3

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

    Global $sFileName = FileSaveDialog("Save picture", @ScriptDir, "JPG mage (*.jpg)", 16, "Bitmap_" & Hex($iCol, 6) & ".JPG")
    If Not @error Then
    If StringRight($sFileName, 4) <> ".JPG" Then $sFileName &= ".JPG"
    Global $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
    _GDIPlus_ImageSaveToFile($hImage, $sFileName)
    _WinAPI_DeleteObject($hImage)
    EndIf

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

    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hHBitmap)
    _GDIPlus_Shutdown()

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

    Func _ColorInTolerance($aSetCol, $aCol, $iTol)
    Global $aTol[3] = [$iTol, $iTol, $iTol]
    $aTol[_ArrayMaxIndex($aSetCol)] = $iTol

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

    If ($aSetCol[0] - $aTol[0]) < $aCol[0] And ($aSetCol[0] + $aTol[0]) > $aCol[0] And _
    ($aSetCol[1] - $aTol[1]) < $aCol[1] And ($aSetCol[1] + $aTol[1]) > $aCol[1] And _
    ($aSetCol[2] - $aTol[2]) < $aCol[2] And ($aSetCol[2] + $aTol[2]) > $aCol[2] Then
    Return 1
    Else
    Return 0
    EndIf
    EndFunc ;==>_ColorInTolerance

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

    Func _GetColor()
    $iCol = PixelGetColor(MouseGetPos(0), MouseGetPos(1))
    EndFunc ;==>_GetColor

    [/autoit]
  • ich bin zwar kein Profi jedoch kannst du die Funktion _ArrayMaxIndex aus der Funktion _ColorInTolerance auslagern. Diese eine Änderung hat bei mir die Berechnungszeitzeit für ein 1024x768 Bild mit 24bit farbtiefe von 35,3s auf 19,6s gesenkt, wenn ich die exakt selbe Farbe angeklickt habe

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>
    #include <Color.au3>
    #include <WinAPI.au3>
    #include <Array.au3>
    Opt("GUIOnEventMode", 1)

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

    Global $sPath = FileOpenDialog("Choose Picture", @ScriptDir, "Pictures (*.bmp;*.jpg;*.png)", 1)
    If @error Then Exit

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

    Global $iCol = -1

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

    _GDIPlus_Startup()
    Global $hImage = _GDIPlus_ImageLoadFromFile($sPath)
    Global $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Global $iHeight = _GDIPlus_ImageGetHeight($hImage)
    ;~ _GDIPlus_ImageGetPixelFormat
    _GDIPlus_ImageDispose($hImage)

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

    Global $hGui = GUICreate("Color effect - Click to choose color", $iWidth, $iHeight);, -1, -1, 0x80000000)
    Global $nPic = GUICtrlCreatePic($sPath, 0, 0, $iWidth, $iHeight)
    GUICtrlSetOnEvent(-1, "_GetColor")
    GUISetState(@SW_SHOW, $hGui)

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

    Do
    Until $iCol <> -1
    Opt("GUIOnEventMode", 0)

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

    WinSetTitle("Color effect", "", "Color effect - Please wait")

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

    Global $hBitmap = _GDIPlus_BitmapCreateFromFile($sPath)
    Global $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD + $GDIP_ILMWRITE, $GDIP_PXF32RGB)

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

    ;Stride - Offset, in bytes, between consecutive scan lines of the bitmap.
    ;If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    Global $iStride = DllStructGetData($tBitmapData, "Stride")
    ;Pixel format - Integer that specifies the pixel format of the bitmap
    Global $iPixelFormat = DllStructGetData($tBitmapData, "Format")
    ;Scan0 - Pointer to the first (index 0) scan line of the bitmap.
    Global $pScan0 = DllStructGetData($tBitmapData, "Scan0")

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

    Global $tPixel

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

    Global $iColPixel, $aCol[3], $aSetCol = _ColorGetRGB($iCol) ,$maxindex=_ArrayMaxIndex($aSetCol);@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    Global $iTol = 80, $Luma
    $timer=TimerInit()
    For $row = 0 To $iHeight - 1

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

    For $col = 0 To $iWidth - 1
    $tPixel = DllStructCreate("dword", $pScan0 + $row * $iStride + $col * 4)
    $iColPixel = DllStructGetData($tPixel, 1)
    $aCol[0] = BitAND(BitShift($iColPixel, 16), 0xFF)
    $aCol[1] = BitAND(BitShift($iColPixel, 8), 0xFF)
    $aCol[2] = BitAND($iColPixel, 0xFF)
    If Not _ColorInTolerance($aSetCol, $aCol, $iTol ,$maxindex) Then ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    $Luma = $aCol[0] * 0.3 + $aCol[1] * 0.59 + $aCol[2] * 0.11
    DllStructSetData($tPixel, 1, BitOR($Luma, BitShift($Luma, -8), BitShift($Luma, -16)))
    EndIf
    Next

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

    Next
    MsgBox(0,"",TimerDiff($timer))
    _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData)

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

    Global $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($nPic, 370, 0, $hHBitmap))

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

    WinSetTitle("Color effect", "", "Color effect - Finished")

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

    Do
    Until GUIGetMsg() = -3

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

    Global $sFileName = FileSaveDialog("Save picture", @ScriptDir, "JPG mage (*.jpg)", 16, "Bitmap_" & Hex($iCol, 6) & ".JPG")
    If Not @error Then
    If StringRight($sFileName, 4) <> ".JPG" Then $sFileName &= ".JPG"
    Global $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
    _GDIPlus_ImageSaveToFile($hImage, $sFileName)
    _WinAPI_DeleteObject($hImage)
    EndIf

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

    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hHBitmap)
    _GDIPlus_Shutdown()

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

    Func _ColorInTolerance($aSetCol, $aCol, $iTol ,$maxindex) ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    Global $aTol[3] = [$iTol, $iTol, $iTol] ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    $aTol[$maxindex] = $iTol
    If ($aSetCol[0] - $aTol[0]) < $aCol[0] And ($aSetCol[0] + $aTol[0]) > $aCol[0] And _
    ($aSetCol[1] - $aTol[1]) < $aCol[1] And ($aSetCol[1] + $aTol[1]) > $aCol[1] And _
    ($aSetCol[2] - $aTol[2]) < $aCol[2] And ($aSetCol[2] + $aTol[2]) > $aCol[2] Then
    Return 1
    Else
    Return 0
    EndIf
    EndFunc ;==>_ColorInTolerance

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

    Func _GetColor()
    $iCol = PixelGetColor(MouseGetPos(0), MouseGetPos(1))
    EndFunc ;==>_GetColor

    [/autoit]

    €: sehr schöner code und ein sehr gutes Ergebnis

    €2: bei etwas genauerer betrachtung kannst du die funktion _ColorInTolerance auf

    [autoit]

    Func _ColorInTolerance($aSetCol, $aCol, $iTol)
    If ($aSetCol[0] - $iTol) < $aCol[0] And ($aSetCol[0] + $iTol) > $aCol[0] And _
    ($aSetCol[1] - $iTol) < $aCol[1] And ($aSetCol[1] + $iTol) > $aCol[1] And _
    ($aSetCol[2] - $iTol) < $aCol[2] And ($aSetCol[2] + $iTol) > $aCol[2] Then
    Return 1
    EndIf
    EndFunc ;==>_ColorInTolerance

    [/autoit]


    kürzen, oder übersehe ich etwas (16 sekunden)

    Einmal editiert, zuletzt von bollen (27. März 2013 um 18:52)

  • Auch eine gute Anlaufstelle ist die GDIP.au3, die eine Menge guter Funktionen mitbringt. Es kann sein, dass auch dein Bespiel sich damit realisieren lässt.

    Schaue dir dazu mal die ImageAttributes() Funktionen an. Damit könnte es eventuell klappen!

    Wenn ich Zeit finde, versuche ich das Ding mal in ASM zu realisieren.

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Danke bollen, das werde ich gleich mal probieren! Das ich _ArrayMaxIndex() zu oft ausführe hätte ich selber erkennen können. Die Frage ist nur, braucht man das um besser Ergebnisse zu erzielen, oder führt ein anderer Weg nach Rom.

    UEZ: Mit der GDIP.au3 habe ich leider noch überhaupt keine Erfahrung. Ich lese da immer nur was von Paths, hab aber keine Ahnung was ein Path sein soll und hab es mir auch noch nie angeschaut.