Image Forensics Inspector (Skill)
This skill exposes the find_image_hide project as a reusable capability for OpenClaw, Hermes, and other agents. Everything runs 100% locally — no network calls, no telemetry, no SaaS dependency.
When to invoke this skill
Invoke when the user/agent task involves any of:
- "Is this image AI-generated / has C2PA / SynthID-like signal?"
- "Is anything hidden inside this PNG/JPG (zip, text, executable, LSB payload)?"
- "Was this image tampered with (copy-move, splicing, ELA bright regions)?"
- "Does this image carry a Shutterstock / Getty / Adobe Firefly watermark?"
- "Reverse-lookup this image against my local reference library (pHash)."
- "Produce a court-grade, byte-reproducible PDF forensic report."
- Batch scanning a folder of images for risk triage (LOW / MEDIUM / HIGH).
Capability inventory
Mapped to source modules so the agent can cite evidence chain:
| Capability | Source module |
|---|---|
| Basic info + EXIF/XMP/IPTC/PNG text | basic_info.py, metadata_analysis.py |
| Hidden payload extraction (trailing zip / EOI text / appended files) | extraction.py |
| Steganalysis (χ² POV, sliding χ², SPA) | steganalysis.py |
| External steg tools aggregator (binwalk / zsteg / stegoveritas / stegseek) | steganalysis_external.py |
| LSB bit-plane analysis | lsb_analysis.py |
| FFT / DCT frequency-domain anomalies | fft_analysis.py, dct_analysis.py |
| Noise residual + Laplacian | noise_analysis.py |
| ELA (error level analysis) | ela.py |
| Copy-move splice detection (ORB + block match) | copy_move_analysis.py |
| AI provenance (C2PA + keyword) | ai_provenance_analysis.py, c2pa_check.py |
| Invisible watermark (DwtDct, e.g. SDXL/SD2 defaults) | invisible_watermark_detect.py |
| Visible watermark OCR (Getty / Shutterstock / "preview only") | visible_watermark_ocr.py |
| pHash reverse-lookup against local reference dir | phash_match.py |
| Risk scoring (LOW / MEDIUM / HIGH / UNKNOWN) | scoring.py |
| Orchestrator | analyzer.py |
| Byte-reproducible PDF | pdf_report.py |
Prerequisites
Python 3.10+.
Install runtime deps:
cd d:\workspace\project\find_image_hide python -m venv .venv .venv\Scripts\activate pip install -r requirements.txtOptional soft-dependencies (skill degrades gracefully without them — fields are tagged
SKIPPED_NO_LIBRARY/SKIPPED_NO_TOOL):pip install reportlab— needed for the byte-reproducible PDF report.pip install invisible-watermark opencv-python— DwtDct invisible-watermark decode.pip install c2pa-python— pure-Python C2PA verification (or system-widec2patool).- System:
tesseract(visible-watermark OCR),binwalk/zsteg/stegoveritas/stegseek(external steg tools). - Env var
FORENSICS_PHASH_REFERENCE_DIR=<absolute dir>— pHash reverse-lookup library.
Usage modes
Mode A — CLI (recommended for agent automation)
Entry point: analyze_image.py
Single image:
python analyze_image.py --input <path-to-image> --output <out-dir>
Directory (batch):
python analyze_image.py --input <dir> --output <out-dir> --recursive --workers 4
stdout (single-image mode) is a JSON object:
{
"report": "<abs-path>/report.json",
"risk_level": "LOW|MEDIUM|HIGH|UNKNOWN",
"confidence": 0.0
}
Full machine-readable result lives in <out-dir>/report.json (single) or <out-dir>/summary.json + <out-dir>/<slug>__<hash>/report.json (batch). Schema is summarized in README.md.
Mode B — Python API (in-process embedding)
from pathlib import Path
from image_forensics.analyzer import analyze_image
from image_forensics.batch import analyze_directory
report = analyze_image(Path("test.jpg"), Path("./out"))
risk = report["overall"]["risk_level"]
summary = analyze_directory(
Path("./photos"), Path("./out"),
recursive=True, workers=4,
)
Both functions write report.json / summary.json to the output dir and return the same dict.
Mode C — Local HTTP API (for non-Python agents like Hermes)
Boot the local server (binds to 127.0.0.1 only):
python webapp.py --host 127.0.0.1 --port 5050
Then call:
| Method | Path | Purpose |
|---|---|---|
POST |
/api/scan |
Body { "directory": "...", "recursive": true, "workers": 2 } — scan a local absolute path |
POST |
/api/scan_upload |
multipart files[] + paths[] — upload + scan |
POST |
/api/demo |
Run built-in demo set |
GET |
/api/jobs/<job_id> |
Job status + last 50 results |
GET |
/api/jobs/<job_id>/summary |
Whole-batch summary.json |
GET |
/api/jobs/<job_id>/image/<slug>/report |
Single-image full report |
GET |
/api/jobs/<job_id>/image/<slug>/report.pdf |
Court-grade reproducible PDF (sets X-Pdf-Sha256 / X-Source-Sha256 headers) |
GET |
/api/pdf/status |
{available, version, error, package} for PDF backend |
Full server impl: webapp.py.
Mode D — One-shot demo
python demo.py # prepare samples + run
python demo.py --serve # + start web UI
python demo.py --no-download # offline-only synthetic samples
See demo.py.
Output contract
report.json schema (full list in README.md L306-L326):
{
"schema_version": "0.1.0",
"input": { "file_name": "...", "format": "PNG", "sha256": "...", ... },
"overall": {
"risk_level": "LOW|MEDIUM|HIGH|UNKNOWN",
"confidence": 0.0,
"summary": "...",
"module_scores": { "fft": 0.0, "dct": 0.0, "lsb": 0.0, "noise": 0.0, "metadata": 0.0, "provenance": 0.0 }
},
"metadata": { ... },
"ai_provenance": { "status": "NO_PROVENANCE_FOUND | VERIFIED_AI_GENERATED | VERIFIED_AI_EDITED | PROVENANCE_PRESENT_BUT_UNVERIFIED | POSSIBLE_AI_BUT_UNVERIFIED", ... },
"frequency_analysis": { ... },
"dct_analysis": { ... },
"lsb_analysis": { ... },
"noise_analysis": { ... },
"evidence_items": [ ... ]
}
Visualizations written alongside report.json:
visualizations/
spectrum.png, r/g/b_spectrum.png
dct_mean_heatmap.png, dct_histogram.png
lsb_r.png, lsb_g.png, lsb_b.png
residual.png, laplacian.png
Recommended agent recipes
- Triage a folder for an OpenClaw task — run Mode A in batch, then parse
summary.jsonand surface every entry whererisk_level == "HIGH"; deep-dive each by reading itsreport.jsonand citingevidence_items[].description. - Hermes "is this AI?" check — run Mode A single-image, read
report["ai_provenance"]["status"]. TreatVERIFIED_AI_GENERATED/VERIFIED_AI_EDITEDas hard evidence;POSSIBLE_AI_BUT_UNVERIFIEDas soft signal that needs human review; never claim hard AI judgement based solely onai_heuristics. - CTF / payload hunt — read
report["extraction"]andreport["external_steganalysis"]; if either reports trailing zip / archive /stegseekhit, extract the file path it emits and follow up. - Court-grade evidence — call
/api/jobs/.../report.pdfand capture theX-Pdf-Sha256+X-Source-Sha256response headers into the case file. Re-rendering the samereport.jsonon any machine must produce the same PDF SHA-256 (see P2.4_ACCEPTANCE.md).
Honesty / limits the agent MUST surface
- This skill cannot prove an image is or is not AI-generated;
ai_heuristicsis hand-tuned thresholds, not a trained classifier. See the "🟠 C 档" section of README.md. - Classic χ²/SPA steganalysis is ineffective against modern adaptive steg (HUGO / WOW / S-UNIWARD).
- SynthID has no official local decoder — only Google's API is authoritative.
- LSB confidence is auto-down-weighted on lossy formats (JPEG).
- No watermark is ever removed, no C2PA is ever forged; this is a read-only forensic tool.
Security envelope
- All processing is local; no outbound traffic.
- HTTP server defaults to
127.0.0.1; agents binding0.0.0.0accept full responsibility. - Uploaded files are path-sanitized (
..rejected, extension whitelisted) and confined toanalysis_output/<job_id>/_uploaded/. - Upload size cap defaults to 1 GB (see
MAX_UPLOAD_BYTESin webapp.py).