[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 : 82
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 »

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