Analyse eines Bildes auf schwarze/weiße Pixel

  • Hey Leute, ich steh vor folgendem Problem.
    Ich hab vor mir ein *.bmp Bild mit den Abmaßen 100x64 (x,y) Pixel.
    Ich versuch ein Skript zu schreiben was mir jeden einzelnen Pixer analysiert ober es sich dabei um ein weißen oder schwarzen Pixel handelt.

    Geplant ist das das Ergebniss der Analyse in eine Array mit der Adresse des jeweiligen Pixels gespeichert wird.
    bsp.: in Pixel 10,45 findet der Skript ein schwarzen Bildpunkt.... Nun soll in das Array[10][45] eine eins geschrieben werden.

    Nur ich hab keine ahnung wie ich das anstellen soll.

    hat jemand einen plan?

    gruß timo

  • Das wäre die schnelle, aber komplizierte Variante (ungetestet, und nur schnell in Scite getippt. Sollte aber funktionieren):

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <Array.au3>
    _GDIPlus_Startup()

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

    $hBitmap = _GDIPlus_BitmapCreateFromFile(...)
    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeight = _GDIPlus_ImageGetHeight($hBitmap)

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

    $tLockBits = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE))
    $tPixelData = DllStructCreate('dword['&$iWidth * $iHeight &']', DllStructGetData($tLockBits, 'Scan0'))
    Dim $aArray[$iWidth][$iHeight]

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

    For $iX = 0 To $iWidth -1
    For $iY = 0 To $iHeight - 1
    $iIndex = $iX + ($iY * $iWidth) + 1
    If DllStructGetData($tPixelData, 1, $iIndex) = 0x000000 Then
    $aArray[$iX][$iY] = 1
    Else
    $aArray[$iX][$iY] = 0
    EndIf
    Next
    Next

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $tLockBits)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()

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

    _ArrayDisplay($aArray)

    [/autoit]

    Etwas einfacher, aber auch langsamer:

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <Array.au3>
    _GDIPlus_Startup()

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

    $hBitmap = _GDIPlus_BitmapCreateFromFile(...)
    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeight = _GDIPlus_ImageGetHeight($hBitmap)
    Dim $aArray[$iWidth][$iHeight]

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

    For $iX = 0 To $iWidth -1
    For $iY = 0 To $iHeight - 1

    If _GDIPlus_BitmapGetPixel($hBitmap, $iX, $iY) = 0x000000 Then
    $aArray[$iX][$iY] = 1
    Else
    $aArray[$iX][$iY] = 0
    EndIf
    Next
    Next

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $tLockBits)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()

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

    _ArrayDisplay($aArray)

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

    Func _GDIPlus_BitmapGetPixel($hBitmap, $iX, $iY) ; From GDIP.au3
    Local $aResult = DllCall($ghGDIPDll, "uint", "GdipBitmapGetPixel", "hwnd", $hBitmap, "int", $iX, "int", $iY, "uint*", 0)

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

    If @error Then Return SetError(@error, @extended, 0)
    $GDIP_STATUS = $aResult[0]
    Return $aResult[4]
    EndFunc ;==>_GDIPlus_BitmapGetPixel

    [/autoit]
  • SEuBo:
    Ich hab mir jetzt nur kurz das erste Script angeschaut, aber es sollte in Zeile 16 eher so aussehn:

    [autoit]

    If Int(DllStructGetData($tPixelData, 1, $iIndex), 1) = 0xFF000000 Then

    [/autoit]

    Edit: Beim zweiten genau das gleiche.

    Zitat

    You just keep on trying 'till you run out of cake. ;)


    [STEAM] Source UDF

    Einmal editiert, zuletzt von K4z (23. April 2012 um 12:26)

  • Ich hab mir jetzt nur kurz das erste Script angeschaut, aber es sollte in Zeile 16 eher so aussehn:


    War mir an der Stelle etwas unsicher, da der Defaultwert des Parameters $iFormat ja GDIP_PXF32RGB ist. Also 4 Byte pro Farbe, ohne Alphakanal.
    Wie gesagt - ich hatte es zu so später Stunde nicht mehr getestet.

    Jetzt bin ich aber etwas über deine Korrektur verwundert. Int() hat nämlich nur einen Parameter, und 0xFF000000 wird von AutoIt generell als negativ interpretiert. Hier ist es besser mit Hex() zu arbeiten. Aber du hattest natürlich Recht - mein Script lief auch nicht.

    [autoit]

    If Hex(DllStructGetData($tPixelData, 1, $iIndex)) = "FF000000" Then

    [/autoit]

    Gruß

  • es hat funktioniert :)
    allerdings ist der y,x - Achsen Skalierung verdreht kann das sein? ;D

    wenn ich ArrayDsiplay benutz zeigts mir 100 Reihen und 64 spalten an, solta aber umgedreht sein

  • wenn ich ArrayDsiplay benutz zeigts mir 100 Reihen und 64 spalten an, solta aber umgedreht sein

    Zitat von Parameter

    $iTranspose [optional] Wenn gesetzt, werden bei einem 2D-Array die erste Dimension als Spalten und die zweite Dimension als Zeilen dargestellt. Per Standard ist es umgekehrt.

    [autoit]

    _ArrayDisplay($aArray, "Array: ListView Display" , -1 , 1)

    [/autoit]
  • Also bei mir hat Int 2 Parameter:D
    Int ( expression [, flag ] )
    Flag: [optional] Defines behavior.
    Can be one of the following:
    Default = the result is auto-sized integer. See remarks.
    1 = the result is 32bit integer.
    2 = the result is 64bit integer.

    Wurde das bei neueren AutoIt Versionen vielleicht weggelassen?
    Hab v3.3.8.1

    Und mit nem 32bit Integer läuft das bei mir auch so, wie ichs geschrieben hab...

  • Die einfachste Lösung währe doch mit einer For-Scheife & PixelGetColor zu prüfen welche Farbe der Pixel hat.
    Ist denke ich aber recht langsam.


    mfg Yellow

    §1 Ich kann nicht für meine scripts inhaftiert werden, auch wenn bereits 3 Menschen erblindeten an den Folgen der Korrekturlesung.  8o


  • My fault. Ich torkel noch auf 3.3.6.1 rum - da ist AutoIt scheinbar noch etwas blöd, was so große Zahlen angeht.

  • Hi,

    Zitat von YellowWeedSeed

    Die einfachste Lösung währe doch mit einer For-Scheife & PixelGetColor zu prüfen welche Farbe der Pixel hat.

    Nur wenn das Bild sichtbar auf dem Bildschirm ist, was man wiederum nicht braucht. Selbst wenn, wäre Pixelsearch dann die wesentlich schnellere Alternative...

    TimoIt, wozu brauchst du das?
    Ich hatte mal für ein Programm zur Auswertung von Faxen/SW-Bildern eine schnelle S/W-Suche in Assembler geschrieben, incl. Auswertung.
    Generell bringt der "Transfer" von Pixelkoordinaten in ein Array nichts ausser doppeltem Speicherplatzverbrauch und niedrige Arbeitsgeschwindigkeit. Die Koordinaten stehen doch schon im Bild, wieso dann noch in ein Array bringen?

    Ich würde alles beides, Suche und "Array" in einem machen:
    Leere Bitmap erstellen und komplett mit nach der zu suchenden Farbe füllen (GDI+).
    Struct mit DWORD´s erstellen an der Position der Bitmap (das ist das "Array")
    BitBlt mit Parameter SRCAND des Suchbildes auf die Bitmap.
    Dauert alles in allem einige hundertstel Millisekunden und die Pixel mit der gesuchten Farbe stehen dank der AND-Verknüpfung in der Struct (an der Position der Bitmap)
    Da eine Struct nichts anderes ist als ein Array, hat man alle gesuchten "Pixel" in kürzester Zeit gefunden.

  • Andy:

    Ich bin grad im 2ten Semester und in unserer Projektarbeit arbeiten wir mit den Lego Mindstorms NXT Microcontroller (mit Displays) und ich hab zusätzlich (find des gewisse + in der Note :D ) nen kleines Skript geschrieben das nen ein mit Paint erstelltes Bild einliest, und den NXT-Code umwandelt, sodass man den Code einfach kopieren kann und auf den Controller überspielen kann.

    Kurz gesagt: Mir ist es jetzt möglich meinen Controller-Display am Computer zu designen und einfach auf den Controler zu Laden

    Hoff einfach mal ich schaff die 1+ :D

  • Hi,

    Zitat

    Mir ist es jetzt möglich meinen Controller-Display am Computer zu designen und einfach auf den Controler zu Laden


    Hört sich gut an...
    Jedenfalls ist diese Aktion nicht zeitkritisch, da bist du mit den o.g. Vorschlägen gut bedient. Würdest du später ein Beispiel posten?