[Ex] _PixelColorAreaSearch - trouver une gappe de pixels dans une image

Partagez vos scripts, et vos applications AutoIt.
Règles du forum
.
Répondre
JulienBoul
Niveau 4
Niveau 4
Messages : 80
Enregistré le : ven. 04 déc. 2015 13:55
Status : Hors ligne

[Ex] _PixelColorAreaSearch - trouver une gappe de pixels dans une image

#1

Message par JulienBoul » lun. 07 nov. 2016 12:07

Bonjour,
T22, sur ma demande, à créé ce script qui permet de rechercher un ensemble de pixels sur une image, et pas uniquement 1 seul pixel. Cette dema,de avait vocation à limiter le risque que 1 seuk pixel soit reconnu plusieurs fois sur une page, et permet par exemple de cliquer assurément sur un bouton/icone/logo lors d'un mouseclic. Les modifications du script peuvent amener à de super choses en fonction de l'application que vous souhaitez en faire. Merci encore à TT22 pour ce script.

PS: le logiciel http://www.imagemagick.org/ qui fonctionne par lignes de commande permet d'arriver à un résultat similaire, mais en passant par un logiciel extérieur. Ca vaut le coup d'oeil si vous n'avez besoin de manier essentiellement de l'image, c'est plus optimisé et développé dans ce sens. Pour ma part, une gestion en interne avec uniquement Autoit m'allait bien dans mon cas. Merci Orax pour tes conseils avisés et pour la présentation de ce bel outil.

TT22 a écrit :Bon, je savais pas quoi faire ce soir, alors j'ai fais ça :
#include <GDIPlus.au3>
#include <ScreenCapture.au3>

;======================================================================================
;
; Name...........: _PixelColorAreaSearch
; Description....: Trouver une grappe de pixels sur un écran en fonction de leur couleur
; Syntax.........: _PixelColorAreaSearch (left, top, right, bottom ,$sFile ,hwnd)
; Parameters.....: left - Abscisse du coin NW du rectangle dans lequel chercher
;                  top - Ordonnée du coin NW du rectangle dans lequel chercher
;                  right - Abscisse du coin SE du rectangle dans lequel chercher
;                  bottom - Ordonnée du coin SE du rectangle dans lequel chercher
;                  $sFile - Chemin d’accès à l’image recherché
; Return values..: Success - Retourne les coordonnées NW et SE de la zone trouvée [left][top][right][bottom]
;                          - Sets @error to 0
;                  Failure - Afficher un message d'erreur affichant la raison de l'erreur :
;                  |1 - Fichier image source non trouvé
;                  |2 - Format de fichier source non supporté
;                  |3 - image non trouvée dans la zone définie par les paramètres
;                  |4 - Les paramètres sont incorrects
; Author.........: TT22
;
;=================================================================================================
Func _PixelColorAreaSearch($left, $top, $right, $bottom, $sFile)

        ; Vérification des paramètres
        If $right - $left <= 0 Or $bottom - $top <= 0 Then
                MsgBox(16, "_PixelColorAreaSearch", "Les paramètres sont incorrects")
                Return SetError(4, Default, False)
        EndIf
        If Not FileExists($sFile) Then
                MsgBox(16, "_PixelColorAreaSearch", "Fichier image source non trouvé")
                Return SetError(1, Default, False)
        EndIf

        ; On va effectuer une capture d'écran puis une recherche sur l'image obtenue,
        ; en effet cela sera beaucoup plus rapide que des PixelGetColor.
        $ScreenBitmap = _ScreenCapture_Capture("", $left, $top, $right, $bottom)
        _GDIPlus_Startup()
        $hImage = _GDIPlus_BitmapCreateFromHBITMAP($ScreenBitmap)
        $ScreenWidth = Null
        $ScreenHeight = Null
        $Screen = Null
        _hImageToArray($hImage, $Screen, $ScreenWidth, $ScreenHeight)
        _GDIPlus_ImageDispose($hImage)
        _GDIPlus_Shutdown()

        ; On charge les pixels de l'image à chercher
        $Width = Null
        $Height = Null
        $Img = Null
        _FileImageToArray($sFile, $Img, $Width, $Height)
        If $Width = -1 Or $Height = -1 Then
                MsgBox(16, "_PixelColorAreaSearch", "Format de fichier source non supporté")
                Return SetError(2, Default, False)
        EndIf

        ; On cherche si le premier pixel est affiché à l'écran
        Dim $Pixels[$right - $left][$bottom - $top]
        For $i = 0 To $right - $left - 1 Step 1
                For $j = 0 To $bottom - $top - 1 Step 1
                        ; Si le premier pixel se trouve sur l'écran, on cherche si les autres correspondent
                        If $Screen[$i][$j] = $Img[0][0] Then
                                $Error = False
                                ; Cherche les pixels correspondant
                                For $k = 0 To $Width - 1 Step 1
                                        $x = $i + $k
                                        ; On veille à ne pas dépasser de l'écran
                                        If $x >= $ScreenWidth Then
                                                $Error = True
                                                ExitLoop
                                        EndIf
                                        For $l = 0 To $Height - 1 Step 1
                                                $y = $j + $l
                                                ; On veille à ne pas dépasser de l'écran
                                                If $y >= $ScreenHeight Then
                                                        $Error = True
                                                        ExitLoop
                                                EndIf
                                                ; En cas d'erreur, on quitte directement la recherche
                                                If $Screen[$x][$y] <> $Img[$k][$l] Then
                                                        $Error = True
                                                        ExitLoop
                                                EndIf
                                        Next
                                        If $Error Then ExitLoop
                                Next
                                ; Si l'image a été trouvée, on retourne ses coordonées
                                If Not $Error Then
                                        Dim $Result[4] = [$i + $left, $j + $top, $i + $left + $Width, $j + $top + $Height]
                                        Return $Result
                                EndIf
                        EndIf
                Next
        Next

        ; Si rien n'a été trouvé, on retourne une erreur
        Return SetError(3, Default, False)

EndFunc   ;==>_PixelColorAreaSearch

; Basé sur https://www.autoitscript.com/forum/topic/131661-solved-gdi-image-file-to-2d-array-and-vice-versa/?do=findComment&comment=916892
Func _FileImageToArray($filename, ByRef $aArray, ByRef $iW, ByRef $iH)
        _GDIPlus_Startup()
        $hImage = _GDIPlus_ImageLoadFromFile($filename)
        _hImageToArray($hImage, $aArray, $iW, $iH)
        _GDIPlus_ImageDispose($hImage)
        _GDIPlus_Shutdown()
EndFunc   ;==>_FileImageToArray

; Retourne un tableau à deux dimensions depuis une image
Func _hImageToArray(ByRef $hImage, ByRef $aArray, ByRef $iW, ByRef $iH)
        $iW = _GDIPlus_ImageGetWidth($hImage)
        $iH = _GDIPlus_ImageGetHeight($hImage)
        $Reslt = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB)

        ; Recupère les valeurs de _GDIPlus_BitmapLockBits ()
        $Width = DllStructGetData($Reslt, "width")
        $Height = DllStructGetData($Reslt, "height")
        $stride = DllStructGetData($Reslt, "stride")
        $format = DllStructGetData($Reslt, "format")
        $Scan0 = DllStructGetData($Reslt, "Scan0")

        Dim $aArray[$Width][$Height]
        For $i = 0 To $iW - 1
                For $j = 0 To $iH - 1
                        $v_Buffer = DllStructCreate("dword", $Scan0 + ($j * $stride) + ($i * 4))
                        ; On supprime la composante alpha
                        $aArray[$i][$j] = BitAND(DllStructGetData($v_Buffer, 1), 0x00FFFFFF)
                Next
        Next
        _GDIPlus_BitmapUnlockBits($hImage, $Reslt)
EndFunc   ;==>_hImageToArray


; Exemple d'utilisation :
; Utiliser une image en PNG car la compression JPEG risquerait de changer la couleur de certains pixels
Local $aCoord = _PixelColorAreaSearch(0, 0, @DesktopWidth, @DesktopHeight, @ScriptDir & "\test.png") ; Effectue une recherche sur l'intégralité de l'écran
$Error = @error
If IsArray($aCoord) = True Then ; << vérifie que _PixelColorAreaSearch a bien trouvé la grappe de couleur définie dans icon.jpg
        MsgBox($MB_SYSTEMMODAL, "", "Les coordonnées de votre grappe de pixels débute aux coordonnées " & $aCoord[0] & "," & $aCoord[1] & " et se termine aux coordonnées " & $aCoord[2] & "," & $aCoord[3]) ;Exemple: "Les coordonnées de votre grappe de pixels débute aux coordonnées 150,250 et se termine aux coordonnées 160,260" pour une image recherchée de 10*10 pixels. -> idéalement, les variables NW et SE seront réutilisées par la suite pour un mouseclic etc.
ElseIf $Error Then
        MsgBox(4096, "Echec de la recherche", "grappe de pixels non trouvée sur sélection donnée", 10)
EndIf
Je dois avouer que je n'ai pas compris pourquoi tu attendais 8 coordonnées en retour, seulement 4 suffisent (les deux du coin haut-gauche et les deux du coin bas-droit), ou même juste deux si tu connais la taille de l'image recherchée.
Et utilises de préférence des images PNG, avec le JPG il risque d'y avoir des problèmes à cause de la compression ;)

Edit : Optimisation, la capture d'écran est faite uniquement sur la zone de recherche. Si tu sais à l'avance dans quelle zone de l'écran se trouve l'image recherchée, met le en paramètre cela permet de gagner beaucoup de temps !

Répondre