fuse

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 2 Imported by: 0

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

View Source
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.
View Source
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

func BetaConfidence(beta float64, unreliable, saturated, inconclusive, detected bool) float64

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

func Combine(s Signals) Result

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL