Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Audit scientific figures for duplicated, reused, transformed, or uninformative image panels. Use for 图片查重, 论文图像重复, PDF figure 审核, when the user attaches a PDF, asks to review selected PDF pages, or tags a directory containing manuscript input images. For PDFs, extract large embedded figure images before splitting them into panels; for directories, preserve originals and split every composite image directly. Produces a reviewed panel manifest, all-pairs candidate table, visual evidence, coverage
.claude/skills/xuzhougeng-figure-duplicate-audit/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -17% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 23% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 1% | 0% |
Audit at the smallest meaningful experimental-image unit. Hashes and feature matches find candidates; they do not establish misconduct or even duplication on their own.
Accept either one PDF or one directory of manuscript images. Resolve tagged or attached paths before running anything. Ask for a page range only when the user did not specify one and scanning the whole PDF would materially change scope.
Create a new analysis directory such as analysis/figure-audit-YYYYMMDD-HHMM. Never modify source images, overwrite a prior audit, or silently omit an unreadable file.
Locate this skill's scripts/audit_figures.py from the resource paths returned by use_skill. If imports fail, load local-env-setup, create a project-local environment, and install the packages named in compatibility. Do not continue with the hash-only fallback when the user requested a strict or exhaustive review.
For a PDF:
textpython audit_figures.py prepare --input PAPER.pdf --output AUDIT_DIR --pages "1-40,49-54"
The script extracts qualifying embedded raster images first. It renders a page only when no large embedded image is available and the page looks like a figure page, or when --render-fallback all is explicitly used. Review sources.json, skipped.json, and sources-contact-sheet.png; confirm that every requested figure is represented. A page render still contains captions and page furniture, so crop the figure before panel splitting.
For a directory:
textpython audit_figures.py prepare --input FIGURE_DIR --output AUDIT_DIR
The script recursively inventories supported images, normalizes EXIF orientation into audit copies, and records hashes and original paths. It does not alter the directory.
prepare writes conservative panel proposals to panels.json. They are only proposals. View every source at full resolution and edit the manifest until:
wound, gel/blot region, or other experimental image has its own box;
labels such as Fig2-D-r1-c2 rather than anonymous indices;
panels;
derivation_group (for example raw channelsand merge, overview and inset, or known longitudinal views);
kind records the modality when known (microscopy, histology,western-blot, gel, plate, wound, ivis, chart, or schematic).
Run:
textpython audit_figures.py materialize --workspace AUDIT_DIR
Inspect panels-contact-sheet.png immediately. Fix bad crops and rerun. Do not scan until manifest-warnings.json has no unexplained out-of-bounds, duplicate-ID, or overlapping-box warning. Preserve parent/context crops when a tighter data-only crop is needed for matching.
textpython audit_figures.py scan --workspace AUDIT_DIR --features required
The scan combines exact pixel hashes, perceptual hashes, normalized correlation, and SIFT + RANSAC geometry. It writes candidates.csv, candidates.json, quality-flags.csv, and scan-summary.json. Review every candidate, not only the first page of the table. Re-scan after any crop change.
Automatic scores are triage signals. Repeated labels, axes, membrane grids, plate rims, scale bars, and regular tissue texture often produce false matches. Conversely, different crops, contrast changes, rotation, mirroring, or recompression can hide a duplicate from hashes and global correlation.
Generate evidence for selected pairs or the highest-ranked unresolved pairs:
textpython audit_figures.py evidence --workspace AUDIT_DIR --pair PANEL_A,PANEL_B python audit_figures.py evidence --workspace AUDIT_DIR --top 20
Inspect the full panels, data-only crops, match-line view, registered red/green overlay, and metrics together. For circular plates or other strong borders, repeat with a tighter interior crop. For blots, compare both whole blot context and protein-by-lane crops. For microscopy, distinguish same-field channel derivation from cross-condition reuse. Consult references/review-protocol.md for modality-specific checks and verdicts.
Never call a pair confirmed from an inlier count or NCC alone. Confirmation requires geometrically consistent correspondence across independent random details in the data region, a plausible transform, visual agreement after registration, and review of the experimental relationship. Record strong negative controls from visually similar nonmatching panels when possible.
Treat automated quality flags as prompts. Mark a panel uninformative only for a specific reason such as blank/placeholder content, corruption, unreadably low resolution, a caption mismatch, or unrelated residual artwork. A negative result, schematic, control, or visually sparse field is not "useless" merely because it contains little signal.
The final report must include:
confirmed duplicate, high-confidence concern, needs rawdata, expected derivative/longitudinal view, and excluded false positive;
reported concern;
Use neutral language: the audit identifies image reuse or similarity, not intent. Do not claim the review is exhaustive unless coverage accounting shows that every in-scope source and experimental-image unit was inspected.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | fail→fail | 13,765 | 29,099 | +111% | 1 | 1 | 0% | 2,214 | 5,492 | +148% | 0 | 0 | — |
case-07 | fail→pass | 11,749 | 8,518 | -28% | 1 | 1 | 0% | 2,277 | 3,201 | +41% | 0 | 0 | — |
case-08 | pass→pass | 19,429 | 11,723 | -40% | 1 | 1 | 0% | 2,484 | 2,548 | +3% | 0 | 0 | — |
case-01 | fail→fail | 38,757 | 48,348 | +25% | 1 | 1 | 0% | 5,447 | 1,616 | -70% | 0 | 0 | — |
case-02 | fail→fail | 10,750 | 16,704 | +55% | 1 | 1 | 0% | 393 | 1,780 | +353% | 0 | 0 | — |
case-03 | fail→fail | 15,370 | 12,313 | -20% | 1 | 1 | 0% | 377 | 2,050 | +444% | 0 | 0 | — |
case-04 | fail→fail | 10,485 | 9,630 | -8% | 1 | 1 | 0% | 808 | 1,981 | +145% | 0 | 0 | — |
case-05 | pass→pass | 17,549 | 21,030 | +20% | 1 | 1 | 0% | 1,990 | 3,980 | +100% | 0 | 0 | — |
case-09 | fail→pass | 19,245 | 8,106 | -58% | 1 | 1 | 0% | 2,519 | 2,089 | -17% | 0 | 0 | — |
case-10 | fail→pass | 15,185 | 12,405 | -18% | 1 | 1 | 0% | 2,129 | 2,653 | +25% | 0 | 0 | — |
case-11 | fail→pass | 19,350 | 12,501 | -35% | 1 | 1 | 0% | 2,266 | 2,781 | +23% | 0 | 0 | — |
case-12 | fail→pass | 17,755 | 10,337 | -42% | 1 | 1 | 0% | 2,535 | 2,561 | +1% | 0 | 0 | — |
case-13 | fail→pass | 18,727 | 7,465 | -60% | 1 | 1 | 0% | 3,189 | 1,821 | -43% | 0 | 0 | — |
case-14 | fail→pass | 14,476 | 4,946 | -66% | 1 | 1 | 0% | 1,654 | 2,243 | +36% | 0 | 0 | — |
case-15 | fail→pass | 15,922 | 7,945 | -50% | 1 | 1 | 0% | 1,754 | 1,802 | +3% | 0 | 0 | — |
case-16 | fail→pass | 12,354 | 3,062 | -75% | 1 | 1 | 0% | 1,416 | 1,963 | +39% | 0 | 0 | — |
case-17 | pass→pass | 17,031 | 12,743 | -25% | 1 | 1 | 0% | 1,812 | 2,679 | +48% | 0 | 0 | — |
case-18 | fail→pass | 23,423 | 3,147 | -87% | 1 | 1 | 0% | 2,815 | 1,983 | -30% | 0 | 0 | — |
case-19 | pass→pass | 14,200 | 11,611 | -18% | 1 | 1 | 0% | 1,372 | 2,389 | +74% | 0 | 0 | — |
case-20 | pass→pass | 18,593 | 10,255 | -45% | 1 | 1 | 0% | 2,091 | 2,249 | +8% | 0 | 0 | — |
case-21 | pass→pass | 18,273 | 13,319 | -27% | 1 | 1 | 0% | 1,771 | 2,625 | +48% | 0 | 0 | — |
case-22 | fail→pass | 15,215 | 8,200 | -46% | 1 | 1 | 0% | 1,686 | 1,886 | +12% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 19 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +50 percentage points is the difference between those two pass rates over the 19 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.