Documentation
¶
Overview ¶
Package fuse is the F5 detection decision layer: it fuses the three detectors (the neural ensemble + recompression gate, the Fridrich β attack, and the structural f5.jar/PixelKnot fingerprint) into one verdict and likelihood score.
It deliberately holds ZERO heavy dependencies — it takes already-computed per-detector signals as plain values (see Signals) and returns a Result. That keeps it importable by both the CLI (cmd/f5) and the browser wasm build (cmd/wasm) so the two share BYTE-FOR-BYTE identical fusion logic: the same weights, thresholds, gates, and §6 soft-struct-override. The wasm detector therefore reaches the same verdict the `f5` binary does on the same image.
The constants are the operating point tuned on the f5val corpus (see the extensive notes on each); changing one changes both surfaces at once.
Index ¶
Constants ¶
const ( WNeural = 0.95 // neural ensemble p_stego (learned detector; dominant) WBeta = 0.05 // Fridrich β detection confidence (corroboration on reliable β) WStruct = 0.00 // structural fingerprint (non-discriminative on laundered corpus) Threshold = 0.59 // fused score ≥ this ⇒ F5_DETECTED (≈1% clean FPR operating point) // BetaFullScale is the β at/above which the Fridrich attack maps to full (1.0) // confidence (β≥0.2 ≈ certain). BetaFullScale = 0.20 // NeuralFloor is the minimum neural p_stego required for F5_DETECTED. Without // it, the 0.95 weight / 0.59 cut let a p in [0.62, 0.71) fire on its own, // promoting out-of-distribution neural false positives. The ensemble's own F5 // threshold is 0.71; we use a softer 0.65 because the structural-plausibility // gate already removes real-world (non-f5-tool) false positives at zero recall // cost, so this floor only needs to temper neural over-firing WITHIN f5 output. // On f5val 0.65 gives clean FPR ≈0.58% (vs 1.02% with no floor, 0.30% at 0.71) // while recovering mid-band recall (β0.05–0.1: 48%→58%). NeuralFloor = 0.65 // StructGateMin is the minimum structural-fingerprint score a frame must reach // to be eligible for F5_DETECTED. F5 payloads exist only in f5.jar/PixelKnot // output, which carries a distinctive encoder fingerprint scoring ~1.0; every // genuine f5val carrier (clean AND stego) scores 1.0, so this gate has ZERO // recall cost there. A frame below this is a plain libjpeg/camera re-encode // that cannot be an f5 carrier — a neural/β positive on it is a recompression // false positive. StructGateMin = 0.50 // StructOverrideNeural is the §6 soft-struct-gate bar for BASELINE low-struct // frames. The hard struct gate vetoes every frame lacking the f5.jar // fingerprint — but the v7 ensemble is encoder-invariant and CAN detect F5 made // by non-f5.jar encoders (mozjpeg/Apple/libjpeg), whose output also lacks that // fingerprint. A low-struct frame is overridden to F5_DETECTED when neural // p_stego clears this bar. Tuned on 824 real-world low-struct clean images: the // BASELINE clean ceiling is 0.922 (n=724), so 0.95 admits a confident // non-standard-F5 detection at ~zero added FP. StructOverrideNeural = 0.95 // StructOverrideProgressiveNeural is the SEPARATE, higher §6 bar for PROGRESSIVE // low-struct frames (the double-compression FP zone). The v7 retrain lowered the // progressive-clean ceiling to ≈0.87 p99 / 0.905 max (n=301) while high-β // progressive F5 sits at median 0.998 — so 0.97 holds real-world // progressive-clean FP at ~0 while still admitting 74% of high-β progressive F5. StructOverrideProgressiveNeural = 0.97 // SuspectNeural is the neural p_stego at/above which a low-struct frame that the // struct gate vetoes (no f5.jar fingerprint, below the §6 override bar) is // reported as SUSPECTED rather than hidden as CLEAN. It mirrors the ensemble's // own F5 decision boundary (gate.F5T = 0.71): at/above it the raw neural detector // WOULD call the frame F5, so collapsing it to a flat CLEAN ("no signature") // throws away a real positive signal. SUSPECTED is NOT a confirmation — the score // in this band cannot separate cross-encoder/non-standard F5 from a // double-compression false positive — but it surfaces the signal honestly instead // of silently dropping it. It never promotes to F5_DETECTED (that still requires // the structural fingerprint or neural ≥ the 0.95/0.97 override bar). SuspectNeural = 0.71 )
Fusion weights + thresholds for the combined F5 likelihood score. These are deliberately explicit and centralized so the scoring system can be fine-tuned in one place. Weights need not sum to 1 (the score is compared to Threshold).
Tuned on a 10k stratified sample of the f5val corpus (5k clean / 5k stego across β-bands), validated on a disjoint 3k held-out set, via scripts/eval/{sample,evaluate}.py. Methodology mirrors the training-time final_report.py (per-β-band AUC + TPR@1%FPR + clean FPR). The objective was max mean TPR over the detectable bands (β≥0.05) at a 1% clean-FPR operating point. Key findings that set these values:
- neural ensemble is the dominant signal (overall AUC 0.835); it carries the decision.
- the Fridrich β attack corroborates on the reliable high-β bands (AUC up to 0.88 at β≥0.2) but adds little marginal lift, hence a small weight.
- the structural fingerprint was CONSTANT (Score≈1.0, AUC 0.500) on this corpus — every image is a laundered re-save, so it carried zero discrimination and was only adding a fixed offset (and false positives). Weighted to 0 here; left tunable because on genuine f5.jar output (which this corpus does not contain) the marker fingerprint is informative. Re-tune if the structural Score is recalibrated to be discriminative.
const ( GateArtifact = "ARTIFACT" // a Stage-1 recompression-gate model fired; F5 unreliable GateSkip = "SKIP" // image below the size minimum / undecodable )
Gate verdicts emitted by the neural stage (gate.Bundle.Scan), passed through in Signals.GateVerdict so the fusion can honor the gate's routing.
Variables ¶
This section is empty.
Functions ¶
func BetaConfidence ¶
BetaConfidence maps the Fridrich β attack to a 0-1 detection confidence. It returns 0 when β is untrustworthy (high quality / double compression / saturated / inconclusive / not detected) so the neural detector carries the decision there; otherwise it scales β toward 1 (β ≈ BetaFullScale is a strong, reliable hit).
Types ¶
type Result ¶
type Result struct {
Verdict string
Basis string
Rationale string
Score float64
NeuralP float64
BetaConf float64
StructScore float64
}
Result is the fused verdict. Verdict ∈ {F5_DETECTED, SUSPECTED, CLEAN, ARTIFACT, SKIP}. SUSPECTED means the neural net flagged F5 (p_stego ≥ SuspectNeural) but there is no structural fingerprint to confirm it — a positive-but-unconfirmed signal, surfaced rather than hidden as CLEAN. The per-signal fields are echoed (rounded to 3 dp) for transparency/tuning.
func Combine ¶
Combine fuses the Fridrich β attack, the structural signatures, and the neural ensemble (+ gate) into one tunable F5 likelihood and verdict. The gate's ARTIFACT / SKIP routing overrides the score: a lossy re-save makes F5 statistically unreliable, and a too-small image cannot be analysed.
This is the single source of truth for the F5 decision, shared verbatim by the CLI and the wasm detector.
type Signals ¶
type Signals struct {
GateVerdict string // neural stage: "" | GateArtifact | GateSkip
NeuralP float64 // mean ensemble p_stego
GateScore float64 // max Stage-1 re-save score (for the ARTIFACT rationale)
BetaConf float64 // Fridrich β attack, mapped to 0-1 (see BetaConfidence)
StructScore float64 // 0-1 f5.jar/PixelKnot encoder fingerprint strength
HasNeural bool // the neural stage ran
HasSignatures bool // the structural signatures ran
Baseline bool // baseline (true) vs progressive (false) — sets the override bar
}
Signals are the per-detector inputs to the fusion, already computed by the caller. HasNeural/HasSignatures gate whether that detector ran (mirroring the CLI's neural==nil / r.Signatures==nil checks), so a β-only run still fuses sensibly.