Alternative zu Pixelsearch

  • Hallo liebe AutoitCom,
    leider muss ich euch heute schon wieder um Rat fragen.
    Vorweg, damit der Thread nicht gesperrt wird: Ich kenne die Forenregeln. Ich möchte keinen Bot oder sonstige illegale Dinge schreiben.

    So, nun zum Thema:
    Ich mlchte mir auf eine Gui das Bild einer Webcam projezieren lassen (mithilfe der WebCam.au3).
    Das klappt auch soweit ganz gut. Nun suche ich dieses Bild nach einem weißen Punkt hab (habe aus Verzweiflung PixelSearch verwendet). Leider ist dieser Befehle verdammt langsam und total von äußeren Einflüssen abhängig.
    Deshalb suche ich eine Alternative, um das sich ändernde Bild auf einen weißen Bereich abzutasten.
    Ich vermute, dass das Erzeugen eines Snapshots und das Abtasten von diesem durch GDI+ - Funktionen keinerlei Zeitersparnis einbringt.

    Mein nächster Gedanke wäre, eine Dll zu schreiben, inder in vielen Threads eine kleinere Menge von Pixeln angetastet werden (z.B: 500 Pixel/Thread). Leider habe ich davon keine Ahnung.

    Würde gerne mal wissen, ob ihr mir eine Lösung für dieses Problem vorschlagen könnt.

    Viele Grüße, Dank und Entschuldigungen für das erneute Fragen im Vorraus,
    stayawayknight

    Bisheriges Script:

    Spoiler anzeigen
    [autoit]


    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>
    #include <GDIPlus.au3>
    #include <Webcam.au3>

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

    ;###Intialize Start
    Global $sMainTitle = "Test"
    Global $iMainLeft = 0
    Global $iMainTop = 0
    Global $iMainWidth = @DesktopWidth
    Global $iMainHeight = @DesktopHeight
    Global $iCamLeft = 0
    Global $iCamTop = 0
    Global $iCamWidth = 640
    Global $iCamHeight = 480

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

    Opt("GuiOnEventMode", 1)

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

    OnAutoItExitRegister("ende")

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

    HotKeySet("{ESC}", "ende")
    ;###Intialize Ende

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

    ;###Guis Start
    $hMain = GUICreate($sMainTitle, $iMainWidth, $iMainHeight, $iMainLeft, $iMainTop, $WS_POPUP)
    GUISetOnEvent($GUI_EVENT_CLOSE, "ende", $hMain)
    GUISetBkColor(0xFFFFFF, $hMain)
    GUISetState(@SW_SHOW, $hMain)
    ;----------------------------
    $hCam = GUICreate($sMainTitle, $iCamWidth, $iCamHeight, $iCamLeft, $iCamTop, $WS_POPUP, Default, $hMain)
    GUISetBkColor(0xFFFFFF, $hCam)
    $cCam = _WebcamOpen($hCam, 0, 0, 640, 480)
    $cCam = _WebcamOpen($hCam, 0, 0, 640, 480)
    GUISetState(@SW_SHOW, $hCam)
    ;----------------------------
    ;###Guis Ende

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

    ;###GDI+ Start
    _GDIPlus_Startup()
    $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hMain)
    $hCamGraphics = _GDIPlus_GraphicsCreateFromHWND($hCam)
    $cPointerPen = _GDIPlus_PenCreate(0xFFFF0000, 2)
    ;###GDI+ Ende

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

    While 1
    $aPixPos = PixelSearch(5, 5, 640, 480, 0xFFFFFF, 1, 2, $hCam)
    If IsArray($aPixPos) Then
    _GDIPlus_GraphicsDrawRect($hCamGraphics, $aPixPos[0] - 20, $aPixPos[1] - 20, 40, 40, $cPointerPen)
    _CalcToMouse($aPixPos[0], $aPixPos[1])
    Else
    _GDIPlus_GraphicsDrawStringColor($hCamGraphics, "KEIN POINTER!", 50, $iCamHeight / 2, "Arial", 50, 0xFFFF0000)
    EndIf
    Sleep(10)
    WEnd

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

    Func _CalcToMouse($iX, $iY)
    Local $iBetween
    $iY = $iY / $iCamWidth
    $iY = @DesktopWidth * $iY
    $iX = $iX / $iCamWidth
    $iX = @DesktopWidth * $iX
    If $iX > @DesktopWidth / 2 Then
    $iBetween = $iX - (@DesktopWidth / 2)
    $iX = (@DesktopWidth / 2) - $iBetween
    ElseIf $iX < @DesktopWidth / 2 Then
    $iBetween = (@DesktopWidth / 2) - $iX
    $iX = (@DesktopWidth / 2) + $iBetween
    ElseIf $iX = @DesktopWidth / 2 Then
    $iX = @DesktopWidth / 2
    EndIf
    MouseMove($iX, $iY)
    EndFunc ;==>_CalcToMouse

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

    Func _GDIPlus_GraphicsDrawStringColor($hGraphics, $sString, $nX, $nY, $sFont = "Arial", $nSize = 10, $ARGB = 0xFFFFFFFF, $iFormat = 0)
    Local $hBrush = _GDIPlus_BrushCreateSolid($ARGB)
    Local $hFormat = _GDIPlus_StringFormatCreate($iFormat)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFont)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $nSize)
    Local $tLayout = _GDIPlus_RectFCreate($nX, $nY, 0, 0)
    Local $aInfo = _GDIPlus_GraphicsMeasureString($hGraphics, $sString, $hFont, $tLayout, $hFormat)
    Local $aResult = _GDIPlus_GraphicsDrawStringEx($hGraphics, $sString, $hFont, $aInfo[0], $hFormat, $hBrush)
    Local $iError = @error
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_BrushDispose($hBrush)
    Return SetError($iError, 0, $aResult)
    EndFunc ;==>_GDIPlus_GraphicsDrawStringColor

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

    Func ende()
    _GDIPlus_PenDispose($cPointerPen)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_GraphicsDispose($hCamGraphics)
    _GDIPlus_Shutdown()
    Exit
    EndFunc ;==>ende

    [/autoit]

    PS: Sorry, dass die Überschrift botverdächtig klingt - mir fällt jedoch keine Aussagekräftigere ein.

    3 Mal editiert, zuletzt von stayawayknight (17. Januar 2011 um 14:32)

  • Hi,
    Pixelsearch ist idR eigendlich schnell genug. Bei mir (2.5ghz 1680x1050) wird ein zu suchendes Pixel im Fullscreen in einigen Millisekunden gefunden. Was ist daran langsam?
    Bist du sicher, dass du die richtige Farbe suchst? Ein Bild einer Webcam hat ziemlich viel "noise" (Rauschen), da hats Pixelsearch natürlich schwer, wenn du mit 20% Abweichung bei allen Farben suchst^^

    Stell doch mal das Bild incl GUI hier in den Thread, incl deinem Script.

  • Zitat

    Ich vermute, dass das Erzeugen eines Snapshots und das Abtasten von diesem durch GDI+ - Funktionen keinerlei Zeitersparnis einbringt.


    Du vermutest richtig, dies wäre sogar noch langsamer, denn PixelSearch macht genau das gleiche. ;)
    PixelSearch benutzt GetPixel für die Suche.

    Vielleicht, aber auch nur vielleicht, geht es schneller die Farbwerte des Bildes direkt zu durchsuchen. Dazu sollte das Bild idealerweise als Bitmap vorliegen, sonst ist es auch langsam, da man das Bild ansonsten erst einmal in eine Bitmap konvertieren müsste.


    Gruß
    Greenhorn


  • Hey stayawayknight
    Ja sowas hab ich auch schonmal veraucht aber besser waers,
    Wenndu dein script hier mit spoiler einbindest ;)
    Mfg

  • Wenns nicht unbedingt ausschliesslich 64Bit-Code sein muss, HIER ein Beispielwie die von Greenhorn angesprochene Suche in einer Bitmap schnell geht...

  • Wie groß ist denn der weiße Bereich?
    Und gibt es vielleicht noch eine zweite Farbe?


    Ich habe mich erst von Microsofts Kinect inspirieren lassen....
    Also habe ich ein zweidimensionales Array erstellt: $Display[192][108] (für 1920 x 1080 Pixel)

    Mit PixelGetColor und einer Schleife, lasse ich jeden zehnte Pixel einlesen, also entsteht ein schönes Raster.
    Danach kann ich mit PixelGetColor und einer Schleife jeden Punkt/Muster auf eine Farbe prüfen.

    Bei meiner 1800Mhz Krücke dauert jeder Durchlauf nur 470ms.

  • Bei meiner 1800Mhz Krücke dauert jeder Durchlauf nur 470ms.


    Das schleicht ja schon fast vor sich hin. Ich behaupte jetzt mal, dass, wenn die Pixelsuche im Speicher (in einer Bitmap) stattfindet, sie um den Faktor 40
    schneller sein wird. Selbst wenn man die Bitmap nur in den Speicher laden, und mit den Strukturbefehlen (DLLStructGetData usw.) durchgehen würde,
    würde das wahrscheinlich sogar mit reinen AutoIt Befehlen wesentlich schneller sein, als mit PixelSearch.

    Andy hat schon den entsprechenden Link gepostet, wie das mit Assembler funktinioniert. Mit entsprechenden C++ Kenntnissen könnte man das auch dort machen
    dafür braucht man ja fast nichtmal Threads - ich bin mir wirklich nicht sicher, ob der Arbeitsaufwand (Threads verwalten / untereinander kommunizieren lassen),
    sich für ein simples PixelSearch überhaupt lohnt. Sobald die Pixeldaten als Struktur vorliegen (Das schafft GDI+ ganz leicht mit dem LockBits Befehl), ist es eh nur
    noch eine simple Schleife, in der man alle einzelnen Pixel abfragen kann - und das rasend schnell.

  • Zitat von Seubo

    würde das wahrscheinlich sogar mit reinen AutoIt Befehlen wesentlich schneller sein, als mit PixelSearch.

    du meintest sicher PixelGetcolor()
    Pixelsearch ist bei weitem schneller als PixelGetcolor()
    Das ASM-Beispiel ist nur deshalb viel schneller, weil auf sämtlichen "Komfort" verzichtet wird. Dafür ist es aber auch in 10 Zeilen Autoit erledigt!
    Ich würde wirklich gerne Beispiele sehen, bei denen Pixelsearch zu langsam ist.

    Zitat

    ich bin mir wirklich nicht sicher, ob der Arbeitsaufwand (Threads verwalten / untereinander kommunizieren lassen),sich für ein simples PixelSearch überhaupt lohnt.

    lohnt sich definitiv nicht, sollte in c++ auch ein maximal 10-Zeiler sein. Aber ob man eine dll lädt, oder den AutoIt-Code direkt im Script hat, soll jeder selbst entscheiden...
    Wer nicht in der Lage ist, aus den gezeigten Beispielen die 10 Zeilen Code herauszukopieren, der hat auch kein schnelles Pixelsuchen32() verdient! Genau aus diesem Grund hatte ich auch keine fertige Funktion erstellt, denn nur selbst fressen macht fett!

    Habe in Sprenger120´s DeskStream den Vergleich zweier 1680x1050 Seiten und gleichzeitiger Komprimierung der unterschiedlichen Pixel in eine AutoIt-struct in 5ms hinbekommen ohne mir wehzutun. Threads wären da eher kontraproduktiv

  • Hallo,
    hier geht's ja bereits heiß her ;)
    Script habe ich hinzugefügt, siehe Post #1.

    Im Prinzip ist es ein Versuch, Mircosofts Kinect in Autoit "nachzubauen"; jedoch wird dazu eine weiße LCD-Leuchte benötigt, die mit der Hand festgehalten wird. Momentan lässt sich die Maus damit bewegen... Der weiße Bereich ist, so wie ich sehe immer vorhanden, leider beträgt die Reaktionszeit zwischen Bild da -> Pointer finden bei mir ungefähr zwei Sekunden, was definitiv zuviel ist.

    Andy: Nein, ich würde es auch gerne selber versuchen, wollte nur den richtigen Denkanstoß bekommen - kann mich auch nicht erinnern, hier irgendwo nach einer fertigen Funktion gebettelt zu haben.

    Werde mich mal jetzt mit den Dingen, die ihr gepostet habt, beschäftigen und werde dann mein Ergebnis posten.
    Schonmal vielen Dank an euch!

  • Zitat

    Andy: Nein, ich würde es auch gerne selber versuchen, wollte nur den richtigen Denkanstoß bekommen - kann mich auch nicht erinnern, hier irgendwo nach einer fertigen Funktion gebettelt zu haben.

    War von mir auch nicht als "betteln" aufgefasst worden :D
    Aber ein Programm in Threads aufzuteilen, um ein bissl im Speicher zu scannen halte ich für *hust* stark übertrieben.


    Ändere mal testweise in deinem Beispiel bei Pixelsearch den shade auf 10 und ersetze die _GDIPlus_GraphicsDrawRect() und calctomouse() durch mousemov() und lass die Anzeige "kein Pointer" komplett wegfallen. so etwa:

    Spoiler anzeigen
    [autoit]

    $hgui=guicreate("")
    guisetstate()
    global $fps=0
    adlibregister("_FPS",1000)

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

    While guigetmsg()<>-3
    $aPixPos = PixelSearch(5, 5, 640, 480, 0xFFFFFF, 1, 2, $hgui)
    If IsArray($aPixPos) Then
    tooltip($apixpos[0]&" "&$apixpos[1])
    EndIf
    $fps+=1
    WEnd

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

    func _FPS()
    WinSetTitle($hgui,"",$FPS)
    $fps=0
    endfunc

    [/autoit]
  • Danke, _CalcToMouse muss jedoch drin bleiben, da ich sonst keine Umrechnung auf die Desktopkoordinaten habe.
    Shade Variation kann ich leider auch nicht erhöhen, da Pixelsearch sonst falsche Punkte nehmen wird, die in der Umgebung sind - die LED-Lampe liefert eig. immer exakt weiß.
    FPS liefert immer so Werte um 1-2, also relativ wenig.
    Es klappt ja auch soweit alles wirklich gut, nur ist die Reaktionszeit relativ gering.
    Werde nachher oder morgen noch weitere Methoden versuchen, danke euch soweit!

    Einmal editiert, zuletzt von stayawayknight (17. Januar 2011 um 17:34)

  • Zitat

    FPS liefert immer so Werte um 1-2, also relativ wenig.

    wenn du das von mir gepostete Script ausführst, wieviel FPS hast du dann?

    Zitat

    die LED-Lampe liefert eig. immer exakt weiß.

    Richtig, aber was macht der CCD-Sensor der Cam daraus? Könntest du mal 2-3 unkomprimierte Bilder machen (*.bmp) und einstellen?
    Ggf könnte man die Farbtiefe auf schwarzweiss reduzieren, das würde erstenms die Bitmap wesentlich(!) kleiner machen, und auch der Scan ginge wesentlich schneller! Schau mal, ob die Webcam so einen Filter (einstellbar) anbietet...

  • Man könnte ggf. auch mit GDI+ Matrizenden Kontrast o.Ä erhöhen. Das ist vielleicht einfacher als geringe Abweichungen im Farbwert zu beachten... :S
    Außerdem gibt es zur professionellen Erkennung von Objekten in Bildern gibt es doch auch schon fertige Dlls und Programme.

  • wenn du das von mir gepostete Script ausführst, wieviel FPS hast du dann?


    ca. 2-3.

    Leider lässt sich soweit ich sehe bei der Webcam kein Kontrast o.ä. einstellen, obwohl dieser Lösungsweg doch Gefallen bei mir findet - weiß jemand eine mögliche Umsetzung (natürlich mit Geschwindigkeitsgewinn)? Kenn mich mit GDI+ leider nicht so aus, ein Lösungsvorschlag und einen Hinweis auf entsprechende Befehle würden mich erfreuen.

    Möchte leider kein Webcambild einstellen, Privatsphäre schützen und so.
    Die Webcam macht daraus nicht viel, es entsteht ein kleiner Kreis um den Pointer mit weniger "weißen" Farbwerten.

    name22: Ja es gibt fertige Programme, aber da ist der schwäbische (Ehr-)Geiz zu groß, ich will das selber mal versuchen :rofl: . Dlls wären aber nicht schlecht.

    Danke für die Hilfe!

    4 Mal editiert, zuletzt von stayawayknight (18. Januar 2011 um 20:44)

  • Zitat

    Ja es gibt fertige Programme, aber da ist der schwäbische (Ehr-)Geiz zu groß, ich will das selber mal versuchen :rofl: . Dlls wären aber nicht schlecht.


    Ja, das kenn ich. So ist fast jeder in meiner Familie ^^. Schau mal hier, vielleicht findest du ja da was. ;)