Yahoo open_nsfw NSFW image classifier on onnxruntime — a maintained, Apache-2.0 drop-in for opennsfw-standalone.
Project description
opennsfw-onnx
Yahoo open_nsfw NSFW image classification, running on onnxruntime — no TensorFlow required.
What & why
opennsfw-onnx runs Yahoo's open_nsfw ResNet-50 model through onnxruntime, giving you a small, modern classifier for whether an image is safe-for-work with no TensorFlow dependency and no GPU required (though GPU/CoreML acceleration is supported).
It exists because the previously-maintained opennsfw-standalone package has stalled — it pins ancient, CVE-affected dependencies (pillow<9, numpy<2) and hasn't been updated in years. opennsfw-onnx is a maintained, Apache-2.0 replacement that:
- under
CPUExecutionProvider, matchesopennsfw-standalone's scores to within 1e-6 (observed 0.0 on the test fixtures) (see Performance), - is a near drop-in migration — one import line changes,
- ships a modern API (
NSFWClassifier,Prediction) plus a CLI, - accepts paths, bytes, file-like objects,
PIL.Image, andnumpy.ndarrayas input, - supports batch inference and hardware acceleration (CUDA, TensorRT, DirectML, CoreML) via
onnxruntimeexecution providers.
Install
pip install opennsfw-onnx
# with the CLI (typer)
pip install "opennsfw-onnx[cli]"
# with runtime model download-and-cache support (platformdirs)
pip install "opennsfw-onnx[download]"
Or with uv:
uv add opennsfw-onnx
uv add "opennsfw-onnx[cli]"
The ONNX model weight (~22.5 MB) ships bundled inside the wheel by default, so no separate download is required for typical installs.
Quickstart
from opennsfw_onnx import NSFWClassifier
clf = NSFWClassifier()
# Any of these work: a path, raw bytes, a file-like object, a PIL.Image, or an ndarray.
pred = clf.classify("photo.jpg")
print(pred.nsfw, pred.sfw) # P(nsfw), P(sfw) — floats that sum to ~1.0
print(pred.is_nsfw) # bool, using the classifier's threshold (default 0.5)
print(float(pred)) # Prediction supports __float__ -> P(nsfw)
# Classify many images at once (preprocessing is parallelized across a thread pool).
preds = clf.classify_batch(["a.jpg", "b.jpg", "c.jpg"])
# One-off classification without managing a classifier instance.
from opennsfw_onnx import classify
pred = classify("photo.jpg")
Prediction is a frozen dataclass with fields sfw: float, nsfw: float, threshold: float = 0.5, a computed is_nsfw: bool property, and a __float__ method returning nsfw.
Drop-in migration from opennsfw-standalone
If you're using opennsfw-standalone, migrating is a one-line import change — .load() and .infer(bytes) -> float behave identically:
# before
from opennsfw_standalone import OpenNSFWInferenceRunner
# after
from opennsfw_onnx.compat import OpenNSFWInferenceRunner
runner = OpenNSFWInferenceRunner.load()
score = runner.infer(image_bytes) # float, P(nsfw)
opennsfw-standalone |
opennsfw-onnx |
|---|---|
from opennsfw_standalone import OpenNSFWInferenceRunner |
from opennsfw_onnx.compat import OpenNSFWInferenceRunner |
OpenNSFWInferenceRunner.load(model_path=None) |
OpenNSFWInferenceRunner.load(model_path=None) (identical signature) |
runner.infer(image_bytes: bytes) -> float |
runner.infer(image_bytes: bytes) -> float (identical signature) |
| TensorFlow backend | onnxruntime backend (CPU by default, accelerable) |
pinned pillow<9, numpy<2 |
current pillow, numpy |
Everything else about your code stays the same. For new code, prefer the richer NSFWClassifier API described above.
CLI
Install with the cli extra, then:
opennsfw-onnx classify photo.jpg
# nsfw sfw verdict path
# 0.0123 0.9877 sfw photo.jpg
opennsfw-onnx classify "photos/*.jpg" --json
opennsfw-onnx classify photo.jpg --threshold 0.3
opennsfw-onnx classify photo.jpg --fast # skip the JPEG round-trip for speed
opennsfw-onnx classify photo.jpg --provider CPUExecutionProvider
Flags:
| Flag | Description |
|---|---|
-t, --threshold FLOAT |
NSFW decision threshold (default 0.5). |
-j, --json |
Emit a JSON array of {path, nsfw, sfw, is_nsfw} instead of a text table. |
--fast |
Skip the JPEG re-encode round-trip (jpeg_reencode=False) for faster, slightly less exact scores. |
--provider |
Restrict/override the onnxruntime execution provider(s); repeatable. |
Arguments accept file paths and shell-style globs (expanded internally, so quote globs to avoid premature shell expansion). Exit codes: 0 if no image is NSFW, 1 if any image is NSFW, 2 if no files matched.
Performance
- Provider selection: by default,
NSFWClassifierauto-selects the fastest availableonnxruntimeexecution provider, preferringCUDAExecutionProvider→TensorrtExecutionProvider→DmlExecutionProvider→CoreMLExecutionProvider→CPUExecutionProvider, filtered to whatever's actually installed. Passproviders=[...]to pin a specific provider or ordering. - Fidelity vs. providers: on
CPUExecutionProvider,opennsfw-onnxmatches the originalopennsfw-standalonescores to within 1e-6 (observed 0.0 on the test fixtures, verified against committed reference scores). Accelerated providers (CoreML, CUDA) can differ by up to ~5e-4 due to backend math differences — negligible for a 0–1 score decided at a 0.5 threshold, but pinproviders=["CPUExecutionProvider"]if you need to reproduce the legacy scores. Seedocs/performance.mdfor details. jpeg_reencode(defaultTrue): applies the reference JPEG round-trip during preprocessing for exact score reproduction. Setjpeg_reencode=False(or CLI--fast) to skip it — faster, with scores shifting by less than 1e-2.- Batch inference:
classify_batch()preprocesses images in parallel across a thread pool (max_workersconfigurable) and runs them through the model as a single stacked batch. warmup(): callclf.warmup()after constructing a classifier to run one dummy inference and avoid paying first-call initialization cost during real workloads.- Thread pinning: pass
intra_op_num_threads=Nto the constructor to control onnxruntime's intra-op thread pool size.
See docs/performance.md for a runnable benchmark snippet.
Model resolution
NSFWClassifier resolves the ONNX model file in this order, verifying its sha256 at every step (hard failure on mismatch):
- an explicit
model_pathargument, - the
OPENNSFW_ONNX_MODEL_PATHenvironment variable, - the model bundled inside the installed wheel,
- as a last resort, downloading and caching it (requires the
downloadextra).
See docs/api.md for the full reference.
Provenance & credits
- The model weights are Yahoo's
open_nsfw(BSD-2-Clause, Copyright (c) 2016 Yahoo Inc.), converted to ONNX. - The image preprocessing pipeline and the
opennsfw_onnx.compatAPI are derived fromopennsfw-standalone(MIT, Copyright (c) 2017 Sector Labs).
Full license texts and lineage are in NOTICE and THIRD_PARTY_LICENSES/. See also docs/provenance.md for the model's origin, extraction chain, and checksum.
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file opennsfw_onnx-0.1.0.tar.gz.
File metadata
- Download URL: opennsfw_onnx-0.1.0.tar.gz
- Upload date:
- Size: 22.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9419f0ef76d2cd28538476a0cc9d9d928e65e6929660f6f9f1b7447567a2d96d
|
|
| MD5 |
71d9578ae22aa272a4eeef4e5315567c
|
|
| BLAKE2b-256 |
f27ce7bd2a51f491a811eef6a85bb8cccf69d6e9f159d14d2475cf5783061ba0
|
Provenance
The following attestation bundles were made for opennsfw_onnx-0.1.0.tar.gz:
Publisher:
release.yml on gawryco/opennsfw-onnx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opennsfw_onnx-0.1.0.tar.gz -
Subject digest:
9419f0ef76d2cd28538476a0cc9d9d928e65e6929660f6f9f1b7447567a2d96d - Sigstore transparency entry: 2064664246
- Sigstore integration time:
-
Permalink:
gawryco/opennsfw-onnx@1cbe63c555cb147fb04668e2568517c9bd2379e2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gawryco
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1cbe63c555cb147fb04668e2568517c9bd2379e2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file opennsfw_onnx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: opennsfw_onnx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d060aaba48c92c0f510f2ae29ddbbd98d43daf00877f5b5c253e34e6502f5bd
|
|
| MD5 |
934b018bfa003e45638e19dc5074024d
|
|
| BLAKE2b-256 |
9f6cd86945ffa011d97e149d15cc7f22fb8eea5f79fd15db5f5f22c09b964ec6
|
Provenance
The following attestation bundles were made for opennsfw_onnx-0.1.0-py3-none-any.whl:
Publisher:
release.yml on gawryco/opennsfw-onnx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opennsfw_onnx-0.1.0-py3-none-any.whl -
Subject digest:
1d060aaba48c92c0f510f2ae29ddbbd98d43daf00877f5b5c253e34e6502f5bd - Sigstore transparency entry: 2064664264
- Sigstore integration time:
-
Permalink:
gawryco/opennsfw-onnx@1cbe63c555cb147fb04668e2568517c9bd2379e2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gawryco
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1cbe63c555cb147fb04668e2568517c9bd2379e2 -
Trigger Event:
release
-
Statement type: