noise_inconsistency_map — IMGFORENSICS noise op

• Data kinds: image2d → image2d

• Call: import fullseye as fs; fs.ledger.noise_inconsistency_map(image, block: 'int' = 16) -> 'np.ndarray' (to call the implementation directly, import imgforensics; imgforensics.noise_inconsistency_map(image, block: 'int' = 16) -> 'np.ndarray'; from the registry, opsimgforensics.get("noise_inconsistency_map"))

Usage

A map of the noise standard deviation per block. image2d.

Takes the high-frequency component with the 3x3 mask of Immerkær 1996, computes for each non-overlapping block-square block sigma = sqrt(pi/2) / 6 * mean(|conv|) and expands it back as block constants. If a pasted region comes from a different exposure, compression ratio or camera, this value forms a step against its surroundings.

The accuracy is good (pinned by tests/test_imgforensics.py). On a synthetic image with true σ 0.01 in the upper half and 0.04 in the lower half (2.55 and 10.20 in 8 bit, ratio 4.0), the estimate is 2.533 and 10.214, ratio 4.033 —— both the absolute values and the ratio within 1%.

What this map cannot tell:

• Texture is counted as noise. Immerkær's mask is Laplacian-like, so regions with fine patterns show a high σ. Measured: in an image with the same noise σ=0.01 in both halves and a checkerboard added only to the lower half, pattern side 15.97 / flat side 2.58 = 6.2 times. A step may be "a pattern" rather than "tampering".

• Conversely, smoothed tampering lowers σ and is visible, but a flat object pasted onto a flat background shows no difference.

• JPEG strips noise block by block, so on compressed images, unless block is a multiple of 8, it interferes with the block grid and stripes appear (the default 16 is a multiple of 8).

• ★ A step unrelated to tampering always appears at the image border (measured 2026-09-06). Because the convolution folds back with mode="reflect", the high frequencies in the outer blocks decrease and σ comes out low. On a 256x256 image with zero tampering and uniform noise, the outermost block band gives 8.815 vs. 9.537 in the centre (-7.6 %). The raw local std of the same image differs only between 0.03491 and 0.03563 (-2.0 %), so most of the difference is created by the estimator. In terms of deviation from the median: outermost 0.7173 / centre 0.5948 = 1.21 times. Taking the per-pixel ROC, this alone moves the zero-point AUC away from 0.5 (0.447 measured in examples/poc_forensics_roc.py). Exclude the outer 1 block from the decision, or take the median per location.

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

• The canonical algorithm (author, year) and its uses are named in the family usage guide above.

Runnable examples (verified samples that actually call this op)

• image_forensics_audit — py -3.11 examples/image_forensics_audit.py

• poc_forensics_roc — py -3.11 examples/poc_forensics_roc.py

Ops the type connects to (they accept image2d as input)

perceptual_hash · fingerprint_correlate · error_level_map · jpeg_quality_estimate · jpeg_ghost_map · copy_move_regions · watermark_embed · watermark_extract

Same category (noise)

—


*Provenance: imgforensics.py — IMGFORENSICS operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.