Downloads · 30 days
0
resoajoe/jpeg-provenance-nano
jpeg-provenance-nano is a machine learning model from resoajoe. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
A 39,974-parameter model that reads a JPEG's quantisation tables and low-frequency DCT statistics and says which pipeline the file came through. Six classes: straight from the phone (nat), Facebook high quality (natFB…
Downloads · 30 days
0
Access
Public
Updated Sep 5, 2026
Repo size
333 KB
Likes
0
Public
Click a slice to open those files.
.pt163 KB · 47%
From the Hugging Face model README
A 39,974-parameter model that reads a JPEG's quantisation tables and low-frequency DCT
statistics and says which pipeline the file came through. Six classes: straight from the phone
(nat), Facebook high quality (natFBH), Facebook low quality (natFBL), WhatsApp (natWA),
resized-then-recompressed with libjpeg (synth_resize), recompressed with the SAME tables as the
original (synth_sameq). ONNX, 157 KB, input is a 588-float feature vector computed by the
bundled jpeg_features.py (Pillow + OpenCV, no other dependency).
Domain measured / deployment domain tested: measured on the VISION dataset (Shullani et al. 2017, CC BY-SA 4.0), devices D01-D08, four REAL pipelines plus two synthetic ones built from the native files; every number below is a transfer to devices held out of training. Deployment domain: no other encoder (mozjpeg, progressive, Photoshop), no header-stripped input and no in-the-wild web image has been tested.
Anything that rewrites the quantisation tables -- Facebook, WhatsApp, libjpeg at a new quality -- is identified from the header alone at AP 0.99-1.00 on devices never seen in training. That is not learning; it is Kornblum (2008) and Kee, Johnson & Farid (2011) confirmed on 2017 phones, and a lookup table would do it. The model earns its keep only on what the header cannot see: the DCT histograms lift same-table recompression from chance (0.17) to 0.36-0.52, and add pipeline evidence on top of the header everywhere else.
| input | balanced acc | nat | natFBH | natFBL | natWA | synth_resize | synth_sameq |
|---|---|---|---|---|---|---|---|
| header only | 0.797 | 0.456 | 1.000 | 1.000 | 0.989 | 0.990 | 0.457 |
| DCT only | 0.591 | 0.409 | 0.759 | 0.860 | 0.610 | 0.677 | 0.363 |
| combined (this model) | 0.804 | 0.533 | 0.999 | 1.000 | 0.990 | 0.979 | 0.517 |
| Benford-divergence scalar | -- | 0.304 | 0.285 | 0.258 | 0.214 | 0.256 | 0.307 |
| IJG-table-distance scalar | -- | 0.378 | 0.246 | 0.192 | 0.319 | 0.238 | 0.374 |
| chance (prevalence) | 0.174 | 0.153 | 0.158 | 0.165 | 0.174 | 0.174 |
Numbers are average precision per class, one-vs-rest.
Baseline + margin. The best single-scalar baseline (Benford first-digit divergence, the classical double-JPEG detector) sits at chance on real social-media pipelines across devices; the DCT-only model beats it by +0.39 macro-AP on the re-encoded classes. The literature's ~92% Benford result (Milani et al. 2014) was UCID with synthetic recompression; it does not survive device shift on real pipelines.
Named failure and its rate. Native vs same-table recompression: AP 0.53 /
0.52 against chance 0.17. The model is better than chance and worse
than useful on that pair; treat a nat / synth_sameq answer as "no table rewrite detected",
nothing more. Facebook high vs WhatsApp are separated by the header, so a pipeline that copied
another's tables would be misattributed with confidence.
Refusal. When the top-2 probabilities are both in {nat, synth_sameq}, report
no_table_rewrite_detected instead of a class. When the file is arithmetic-coded, lossless, 12-bit,
CMYK, or carries no quantisation table, the feature extractor cannot produce the vector and the
model must not be called. Progressive JPEGs are in scope: Facebook's high-quality re-encode
(natFBH) is progressive and was in training; prog is one of the 588 features. (An earlier
version of this card wrongly listed progressive as refused; corrected 2026-09-05 when the
browser demo's parity run exposed the contradiction.)
Provenance. VISION D01-D08 native images and their Facebook/WhatsApp uploads (Shullani,
Fontani, Iuliani, Al Shaya, Piva; EURASIP JIS 2017). Synthetic classes made with Pillow/libjpeg
from the native files. Quantised coefficients are RECOMPUTED from decoded pixels (decode -> 8x8
DCT -> divide by header table -> round), not read from the bitstream, so this reproduces with
no JPEG-internals library. Split by device. Scripts: jpeg_features.py (bundled),
jpeg_dataset.py, jpeg_prov_train.py, jpeg_prov_ship.py in the loglens repo.
import numpy as np, onnxruntime as ort
from jpeg_features import features # bundled in this repo
PIPES = ['nat', 'natFBH', 'natFBL', 'natWA', 'synth_resize', 'synth_sameq']
f = features("photo.jpg") # None if not baseline JPEG -> do not classify
n = np.load("norm.npz"); x = ((np.array(f["x"]) - n["mu"]) / n["sd"])[n["slice"]].astype(np.float32)
logits = ort.InferenceSession("model.onnx").run(None, {"features": x[None]})[0][0]
p = np.exp(logits - logits.max()); p /= p.sum()
top = np.argsort(-p)[:2]
if set(PIPES[i] for i in top) <= {"nat", "synth_sameq"}:
print("no_table_rewrite_detected")
else:
print(PIPES[top[0]], round(float(p[top[0]]), 3))
Trained 2026-09-02 on a Jetson AGX Orin. Part of the loglens nano family; the four-gate verdict
that produced this card is in resoajoe/nanolab.