Cette page s’adresse à celles et ceux qui veulent comprendre pourquoi OculiX trouve — ou rate — un match. En une phrase : derrière chaque find() il y a matchTemplate d’OpenCV, doublé d’un repli en feature matching, le tout enveloppé dans une couche JNA appelée Apertix qui évite les conflits classiques de bibliothèques natives sous Java.
flowchart TD
A["Votre script<br/>(Jython / Java)"] --> B["sikuli.script<br/>Screen · Region · Pattern"]
B --> C["org.sikuli.script.Finder<br/>similarité · target offset · clipping de region"]
C --> D["Apertix<br/>OpenCV 4.10.0 via JNA"]
D --> E["Libs OpenCV natives<br/>embarquées dans le JAR"]
style E fill:#fff3cd,stroke:#ffc107,color:#856404
OculiX dépend d’Apertix, une compilation JNA personnalisée d’OpenCV 4.10.0, à la place de l’artefact plus connu org.openpnp:opencv. Deux raisons :
Plus de conflit avec System.loadLibrary
Apertix charge OpenCV via JNA, qui n’entre pas en concurrence avec les autres bibliothèques JNI sous Windows. Le mélange OpenCV + VNC + JFreeChart, qui pose problème depuis dix ans avec l’artefact classique, fonctionne sans patch.
Une version d'OpenCV figée
OpenCV 4.10.0, compilé depuis les sources sous Windows x86-64 avec MSVC. Chaque release d’OculiX est buildée contre cette même version exacte. Comportement reproductible d’une machine à l’autre.
Quand vous écrivez Region.find("button.png"), OculiX appelle la fonction matchTemplate d’OpenCV avec la métrique TM_CCOEFF_NORMED. Concrètement :
L’image button.png capturée devient le template.
La capture actuelle de la region devient la scène.
OpenCV fait glisser le template sur chaque pixel de la scène et calcule un score de corrélation normalisé entre 0,0 et 1,0.
Le pixel au score le plus élevé devient le match candidat.
Si ce score atteint la similarité demandée (0,7 par défaut), OculiX renvoie un Match. Sinon il lève FindFailed.
Le template matching est précis au pixel près, mais sensible à l’échelle. Si la même cible est rendue 10 % plus grande sur l’écran d’exécution — High DPI, changement de thème — le score chute. Deux façons d’absorber ça :
Abaisser le seuil de similarité
Pattern("button.png").similar(0.6) élargit la tolérance pour un seul appel sans toucher au reste du script.
Recapturer à la bonne échelle
Plus simple, plus fiable, plus rapide quand la cible est très déformée par rapport à la capture d’origine.
Feature matching — quand le template ne suffit plus
Si le template matching échoue trop souvent — rotation, changement d’échelle, variations de lumière — OculiX bascule sur du feature matching :
finder =Finder(image)
finder.findFeatures("logo.png")
if finder.hasNext():
print finder.next()
Le feature matching s’appuie sur les descripteurs ORB (Oriented FAST and Rotated BRIEF). Plus lent que le template matching, mais résistant à de petites rotations, à de l’occlusion partielle, et à un facteur d’échelle modéré. Utile quand :
la cible se déplace à l’intérieur d’une fenêtre (drag-and-drop),
la cible tourne (boussole, indicateur rotatif),
la même image s’affiche à plusieurs tailles (UI responsive).
Region.right(N) ne déclenche aucune capture. La méthode se contente d’ajuster le rectangle de recherche. La capture écran réelle a lieu paresseusement, au prochain find(), wait() ou click().
D’où la différence radicale de coût entre un find() imbriqué qui cible une petite region et un find() qui balaie tout l’écran. OpenCV scanne dans les deux cas, mais sur des surfaces qui n’ont rien à voir.
# Bon — OpenCV scanne 300 × 50 px
btn = dialog.right(300).find("save.png")
# Mauvais — OpenCV scanne 1920 × 1080 px à chaque appel
Chaque moniteur dispose de sa propre instance Screen(n). Screen(0) est l’écran principal. Screen.getNumberScreens() indique combien il y en a au total.
C’est l’outil à dégainer en premier quand un script clique au mauvais endroit :
match =find("button.png")
match.highlight(2) # encadré rouge pendant 2 s
match.highlight(2,"green") # idem en vert
Vous voyez immédiatement où OculiX pense que le match se trouve. Neuf bugs « pourquoi a-t-il cliqué là ? » sur dix se résolvent en cinq secondes après un coup de highlight.
Le pipeline de vision est indépendant de la source de l’image. VNCScreen, ADBScreen et le Screen local exposent la même API find/click/type — ils diffèrent uniquement par l’endroit d’où viennent les captures. La pile OpenCV en aval ne se soucie pas de savoir si l’image vient de votre moniteur, d’un téléphone Android via ADB, ou d’une machine distante via VNC.