Originally created by: julienmerconsulting
[!IMPORTANT]
This PR brings two major enhancements:
1. 🔧 OpenCV native loading — fixes theUnsatisfiedLinkErrorcrash in headless-rmode after the Apertix 4.10.0 migration
2. 🎯 DPI-aware matching pipeline — 5-mode cascade that handles cross-resolution matching when Windows display scaling changes
❌ java -jar oculixide-3.0.1-complete-win.jar -r script.py
→ UnsatisfiedLinkError: 'long org.opencv.core.Mat.n_Mat()'
✅ java -jar oculixide-3.0.1-complete-win.jar -r script.py
→ OpenCV: loaded via Apertix (nu.pattern.OpenCV)
→ Script executes successfully
After migrating from openpnp/opencv 4.5.4 to Apertix 4.10.0:
| Component | Was | Now |
|---|---|---|
Core.NATIVE_LIBRARY_NAME |
opencv_java454 ❌ |
opencv_java4100 ✅ |
loadOpenCV() failure handling |
libOpenCVloaded = true always ❌ |
Only true on success ✅ |
| Apertix integration | Not called ❌ | nu.pattern.OpenCV.loadLocally() as primary method ✅ |
| File | Change |
|---|---|
Core.java |
Align version 4.5.4 → 4.10.0, native lib name opencv_java454 → opencv_java4100 |
Commons.java |
Rewrite loadOpenCV(): Apertix → System.loadLibrary → manual JAR extraction |
sikulixcontent |
Align manifest to opencv_java4100 |
[!NOTE]
Use case: Template captured at 100% Windows scaling, script executed at 150% scaling (or vice versa). Without this enhancement,find()fails because the template size doesn't match the screen.
graph TD
A[find called] --> B{Mode 1: Exact match}
B -->|score > threshold| Z[✅ Match found]
B -->|fail| C{Mode 2: DPI-aware resize}
C -->|score > threshold| Z
C -->|fail| D{Mode 3: Tolerant - GaussianBlur}
D -->|score > threshold ×0.9| Z
D -->|fail| E{Mode 4: Smart - Grayscale}
E -->|score > threshold ×0.85| Z
E -->|fail| F{Mode 5: Multi-scale brute-force}
F -->|score > threshold ×0.9| Z
F -->|fail| X[❌ FindFailed]
| Mode | Strategy | Threshold | When it helps |
|---|---|---|---|
| 1 | Standard matchTemplate |
Original | Same resolution, identical image |
| 2 | Resize template by DPI ratio | Original | Known DPI mismatch (metadata available) |
| 3 | GaussianBlur(3,3) on both images | × 0.90 | Minor rendering differences, anti-aliasing |
| 4 | Grayscale conversion | × 0.85 | Color shift, theme changes |
| 5 | Try 7 common scale ratios | × 0.90 | Unknown scaling, no DPI metadata (legacy) |
[!WARNING]
Bug fixed — Scoring cascade: Modes 3 & 4 previously found valid matches butFindResult2.hasNext()rejected them because it compared against the original threshold instead of the reduced one. NowfindInput.setSimilarity()is set to the reduced threshold before creating theFindResult2.[!WARNING]
Bug fixed — DPI metadata: Captured PNGs never contained DPI metadata (pHYs chunk), so Mode 2 never triggered. Now all PNG writes go throughFileManager.writePngWithDpi()which embeds the system DPI.
| Fix | File(s) | Change |
|---|---|---|
| P0 — Scoring | Finder.java |
findInput.setSimilarity(reduced) before new FindResult2() in modes 3, 4, 5 |
| P1a — PNG DPI | FileManager.java + 3 files |
New writePngWithDpi(), replaces 7 bare ImageIO.write calls |
| P1b — DPI fallback | Finder.java |
getDpiRatio(): in-memory → PNG metadata → assume 96 DPI |
| P2a — Image DPI | Image.java |
New captureDpi field, auto-injected from Image(ScreenImage) |
| P2b — System DPI | Finder.java |
getSystemDpi() via GraphicsDevice.getDefaultTransform() |
| P3 — Multi-scale | Finder.java |
Mode 5: brute-force at ratios 1.25, 1.5, 1.75, 2.0, 0.8, 0.67, 0.5 |
API/src/main/java/org/opencv/core/Core.java
API/src/main/java/org/sikuli/script/Finder.java
API/src/main/java/org/sikuli/script/Image.java
API/src/main/java/org/sikuli/script/ScreenImage.java
API/src/main/java/org/sikuli/support/Commons.java
API/src/main/java/org/sikuli/support/FileManager.java
API/src/main/resources/sikulixlibs/windows/libs64/sikulixcontent
IDE/src/main/java/org/sikuli/ide/ButtonCapture.java
mvn clean package -DskipTests — compilation succeeds-r): run a script with find() — no UnsatisfiedLinkErrorfind() — no regressionexiftool or any metadata viewer)find() — Mode 2 or 5 triggershasNext()
Ticket changed by: julienmerconsulting