Downloads · 30 days
0
darshan12222/lucid-nsfw-4class
lucid-nsfw-4class is a image classification model from darshan12222. Use it when you need a label for an image. It is set up for onnx. The card lists the license as apache-2.0.
An on-device image classifier that flags explicit, gore, neutral, and suggestive content in short-form social feeds (Instagram / TikTok thumbnails). It is the model behind the Lucid browser extension, which blurs sens…
Downloads · 30 days
0
Access
Public
Updated Aug 16, 2026
Repo size
45 MB
Likes
3
Public
Click a slice to open those files.
.onnx22.5 MB · 100%
From the Hugging Face model README
An on-device image classifier that flags explicit, gore, neutral, and suggestive content in short-form social feeds (Instagram / TikTok thumbnails). It is the model behind the Lucid browser extension, which blurs sensitive and distracting posts entirely on the user's device — no server, no cloud inference.
Marqo/nsfw-image-detection-384 (ViT-tiny, 384×384)The output layer follows alphabetical class order (the training ImageFolder
order). This mapping is fixed — do not reorder it:
| Index | Class | Meaning |
|---|---|---|
| 0 | explicit | Nudity / sexual content |
| 1 | gore | Graphic violence, gore, accidents, injury, blood |
| 2 | neutral | Everything safe / ordinary |
| 3 | suggestive | Revealing / borderline-sexual but not explicit |
class_map.json in this repo carries the same mapping plus the input spec.
explicit 0.55 · gore 0.55 · suggestive 0.70).Preprocessing (must match training): resize to 384×384, RGB, scale to [0,1],
normalize with ImageNet mean [0.485, 0.456, 0.406] / std [0.229, 0.224, 0.225],
channels-first, batch of 1.
import numpy as np, onnxruntime as ort
from PIL import Image
CLASSES = ["explicit", "gore", "neutral", "suggestive"] # index order — fixed
MEAN = np.array([0.485, 0.456, 0.406], np.float32)
STD = np.array([0.229, 0.224, 0.225], np.float32)
def preprocess(path):
img = Image.open(path).convert("RGB").resize((384, 384))
x = (np.asarray(img, np.float32) / 255.0 - MEAN) / STD
return x.transpose(2, 0, 1)[None].astype(np.float32) # (1, 3, 384, 384)
sess = ort.InferenceSession("lucid_nsfw_4class_fp32.onnx", providers=["CPUExecutionProvider"])
logits = sess.run(["logits"], {"pixel_values": preprocess("image.jpg")})[0][0]
probs = np.exp(logits - logits.max()); probs /= probs.sum()
print(dict(zip(CLASSES, probs.round(3))), "→", CLASSES[int(probs.argmax())])
pixel_values — Output name: logits (shape [1, 4]; apply softmax).skip audit-sink is excluded). ~15% held out for validation.Reproduce or fine-tune with your own data — the Colab notebook, labeling toolkit, and a step-by-step guide are in this repo: TRAINING.md · finetune.ipynb.
Held-out validation set (736 images):
| Class | Precision | Recall | F1 |
|---|---|---|---|
| explicit | 0.820 | 0.892 | 0.854 |
| gore | 0.921 | 0.944 | 0.933 |
| neutral | 0.800 | 0.762 | 0.780 |
| suggestive | 0.743 | 0.719 | 0.731 |
| macro avg | 0.821 | 0.829 | 0.825 |
| accuracy | 0.815 |
gore is the most cleanly separated class (one leak in the whole val set). The
suggestive ↔ neutral ↔ explicit cluster is the fuzzy nudity boundary and the
main source of error.
explicit occasionally fires with high confidence
on benign frames — notably video cover frames that differ from the visible tile.
This is the model's precision budget, not a bug; the fix is harder neutral
negatives in a future retrain.suggestive is the softest class (F1 0.73) — the first place to look if
coverage feels off.Because of these, deployments should keep a human-reversible action (e.g. a "reveal" button) rather than hard-blocking.
apache-2.0, consistent with the Marqo base model.
Weights may be used freely under those terms; the training data is not included.
@software{lucid_nsfw_4class_2026,
author = {Bajgain, Darshan},
title = {Lucid — 4-class on-device content classifier},
year = {2026},
url = {https://huggingface.co/darshan12222/lucid-nsfw-4class}
}
Links: Lucid extension · base model