[R] Pixelsearch & _PixelColorAreaSearch - recherche multiples pixels

Aide et conseils concernant AutoIt et ses outils.
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

[R] Pixelsearch & _PixelColorAreaSearch - recherche multiples pixels

#1

Message par JulienBoul »

Bonjour,

Bonjour,
Il nous est tous arrivé de fantasmer sur Autoit dans la voiture en se rendant au travail, de se dire « et si telle fonction existait, qu’est-ce que ça changerai, elle serait utilisée ? Elle répondrait à telle ou telle problématique? », et de se rendre compte qu’on n’a pas le niveau un peu comme avec certaines femmes en fait, l’analogie est bien choisie ^^)… J’avoue, je suis dans la fantasme, et pire encore, je le partage avec vous…
Coquins codeurs, qui réalisera ce fantasme ?...

Ma problématique :
Hypertrophie de l’appareil… Heu, pardon, restons sérieux.
Chercher un pixel a un caractère hasardeux, sachant que si deux pixels figurent dans la zone de recherche, pixelsearch retourne les coordonnées du premier dans le sens de lecture. L’ennui est qu’il peut retourner une couleur du background alors qu’on cherche un logo ou une icône. C’est d’autant plus vrai lorsque la mise en page change et qu’on doit élargir la zone de recherche, parfois à l’écran entier.
La solution imaginée :
J’ai tenté d’imaginer une architecture qui permette de travailler en une seule étape et qui soit le plus propre possible. L’idée est de comparer une image avec la page active, et indiquer si elle y figure. A l’usage, l’utilisateur qui veut cliquer sur un logo en particulier, fera une capture d’écran, et indiquera le chemin d’accès de cette capture dans les paramètres de la fonction (pas de pb de résolution à prévoir?)
Voici tous les détails de mon fantasme ;-)
; #FUNCTION# ;======================================================================================
;
; 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.
;                  top - Ordonnée du coin NW du rectangle.
;                  right - Abscisse du coin SE du rectangle.
;                  bottom - Ordonnée du coin SE du rectangle.
;                  $sFile - Chemin d’accès à l’image recherché.
;                  hwnd : [optionnel] Handle de la fenêtre à utiliser.
; Return values..: Success - Retourne les coordonnées NW et SE de la zone trouvée.
;                          - 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.
; Author.........: Le nom du bienfaiteur de l'humanité et le lien vers le topic pour plus d'infos
;
;=================================================================================================

;Exemple d'utilisation:
Local $aCoord = _PixelColorAreaSearch (0,0,1024,728,"C:\icon.jpg") ; effectue une recherche sur l'intégralité de l'écran
  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] & " et se termine aux coordonnées " & $aCoord[1]) ;Exemple: "Les coordonnées de votre grappe de pixels débute aux coordonnées 150,250,500,600 et se termine aux coordonnées 160,260,510,610" 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
Malheureusement, c’est souvent lorsqu’on revient à la réalité qu’on se rend compte que ça n’était qu’un fantasme, malgré l’apparente réalité qui composait ce songe… Reste donc à construire la fonction :-P

Au plaisir de réaliser ce rêve avec vous :-p
Avec une fonction aussi utile, à vous la postérité sur les fofo US et les belles américaines ^^

@+ Julien.
Modifié en dernier par JulienBoul le lun. 07 nov. 2016 11:50, modifié 1 fois.
Avatar du membre
TT22
Membre émérite
Membre émérite
Messages : 1566
Enregistré le : lun. 18 avr. 2011 15:21
Localisation : La Quatrième Dimension
Status : Hors ligne

Re: [..] Pixelsearch & _PixelColorAreaSearch - recherche multiples pixels

#2

Message par TT22 »

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 !
Cordialement,
TT22
Avatar du membre
orax
Modérateur
Modérateur
Messages : 1479
Enregistré le : lun. 23 mars 2009 04:50
Localisation : ::1
Status : Hors ligne

Re: [..] Pixelsearch & _PixelColorAreaSearch - recherche multiples pixels

#3

Message par orax »

JulienBoul a écrit :L’ennui est qu’il peut retourner une couleur du background alors qu’on cherche un logo ou une icône.
Pour ce qui est de chercher une image dans une autre (plus grande), il y a aussi ImageMagick qui pourrait faire ça (avec compare -subimage-search).
De petits détails peuvent faire toute la différence. — Quand la boule de neige commence à rouler… poussez-la. (Columbo)
JulienBoul
Niveau 4
Niveau 4
Messages : 82
Enregistré le : ven. 04 déc. 2015 13:55
Status : Hors ligne

Re: [..] Pixelsearch & _PixelColorAreaSearch - recherche multiples pixels

#4

Message par JulienBoul »

Capture.PNG
Capture.PNG (4.48 Kio) Vu 2226 fois
Ralalaaaaaa TT22, tu viens d'écrire un script dont je rève depuis que j'ai commencé Autoit... J'ai vu que ça existait coté imagemagick également, merci Orax, j'ai téléchargé, ça ne sera pas pour cette partie mais il est super puissant comme logiciel quand on a besoin de manier l'image, et c'est programmable, ça c'est top ! Tu peux pas savoir comme ça m'aide, fini les erreurs de pixelcolor, c'est vraiment génial, ça me fait penser à une recherche string dans un immense texte. Tu cherches un ensemble de lettres juxtaposées dans un sens bien précis, tout comme les pixels.
J'imagine même une extension possible à un script d'OCR (à condition d'avoir une bibliothèque de caractères + gestion du rapport de proportions de la taille des images), mais c'est super puissant, j'adore. Moi qui croyait être dans le phantasme... 1 message, 1 réponse, 1000 users interessés sur les années à venir. J'adore, encore merci.

Je vais partager de suite le script sur le fofo dans la bonne rubrique et cloture celle-ci.

Merci TT22, Merci Orax !
Répondre