Skip to main content

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.

License: Apache-2.0 PyPI Python

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, matches opennsfw-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, and numpy.ndarray as input,
  • supports batch inference and hardware acceleration (CUDA, TensorRT, DirectML, CoreML) via onnxruntime execution 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, NSFWClassifier auto-selects the fastest available onnxruntime execution provider, preferring CUDAExecutionProviderTensorrtExecutionProviderDmlExecutionProviderCoreMLExecutionProviderCPUExecutionProvider, filtered to whatever's actually installed. Pass providers=[...] to pin a specific provider or ordering.
  • Fidelity vs. providers: on CPUExecutionProvider, opennsfw-onnx matches the original opennsfw-standalone scores 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 pin providers=["CPUExecutionProvider"] if you need to reproduce the legacy scores. See docs/performance.md for details.
  • jpeg_reencode (default True): applies the reference JPEG round-trip during preprocessing for exact score reproduction. Set jpeg_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_workers configurable) and runs them through the model as a single stacked batch.
  • warmup(): call clf.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=N to 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):

  1. an explicit model_path argument,
  2. the OPENNSFW_ONNX_MODEL_PATH environment variable,
  3. the model bundled inside the installed wheel,
  4. as a last resort, downloading and caching it (requires the download extra).

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.compat API are derived from opennsfw-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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opennsfw_onnx-0.1.0.tar.gz (22.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opennsfw_onnx-0.1.0-py3-none-any.whl (22.0 MB view details)

Uploaded Python 3

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

Hashes for opennsfw_onnx-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9419f0ef76d2cd28538476a0cc9d9d928e65e6929660f6f9f1b7447567a2d96d
MD5 71d9578ae22aa272a4eeef4e5315567c
BLAKE2b-256 f27ce7bd2a51f491a811eef6a85bb8cccf69d6e9f159d14d2475cf5783061ba0

See more details on using hashes here.

Provenance

The following attestation bundles were made for opennsfw_onnx-0.1.0.tar.gz:

Publisher: release.yml on gawryco/opennsfw-onnx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for opennsfw_onnx-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1d060aaba48c92c0f510f2ae29ddbbd98d43daf00877f5b5c253e34e6502f5bd
MD5 934b018bfa003e45638e19dc5074024d
BLAKE2b-256 9f6cd86945ffa011d97e149d15cc7f22fb8eea5f79fd15db5f5f22c09b964ec6

See more details on using hashes here.

Provenance

The following attestation bundles were made for opennsfw_onnx-0.1.0-py3-none-any.whl:

Publisher: release.yml on gawryco/opennsfw-onnx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page