NudeNet: ONNX nudity detection that ships a working model in the pip package
Lightweight nudity detection
At a glance
- What is it?
- NudeNet is a Python package and Docker image for detecting 18 nudity-related classes with a YOLOv8-based ONNX model. It is small, easy to call, and maintained on a best-effort basis by a maintainer who is openly looking for help.
- Who is it for?
- NudeNet suits engineers who need a local, offline detector with a small dependency set and a model that arrives with the package, especially for single-image checks or a censor step. It is not a fit for anyone who needs a maintained, actively developed moderation service with documented accuracy guarantees, because the README asks for contributors and the last push was on 2026-06-09.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 114 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What NudeNet actually detects, and who it is for
NudeNet is a nudity detection library written in Python. It is not a general NSFW classifier that returns one score for an image. The README lists 18 labels, and they split into exposed and covered variants of body parts: FEMALE_GENITALIA_EXPOSED and FEMALE_GENITALIA_COVERED, BUTTOCKS_EXPOSED and BUTTOCKS_COVERED, FEMALE_BREAST_EXPOSED, MALE_BREAST_EXPOSED, ANUS_EXPOSED, BELLY_EXPOSED, FEET_EXPOSED, ARMPITS_EXPOSED, plus covered counterparts and FACE_FEMALE and FACE_MALE. Each detection comes back with a class, a score and a box, so the output is a list of regions rather than a verdict. That shape matters: you decide the threshold and which labels count as a violation for your product. The intended users are engineers building image moderation, dataset filtering, or a local censor step. The in-browser demo on the project homepage runs the detector client side, which tells you the model is small enough to ship to a browser. The README also states the maintainer is looking for contributors and maintainers, so treat this as a project you adopt with the expectation of reading the source when something is unclear.
How the detection pipeline works: YOLOv8 weights exported to ONNX
The mechanism is object detection, not classification. The model table in the README lists two models, both based on ultralytics yolov8: 320n, trained at 320x320 and based on yolov8n, and 640m, trained at 640x640 and based on yolov8m. Each is published in two formats, .onnx and .pt, as release assets under the v3.4-weights tag. The Python package depends on onnxruntime, numpy and opencv-python-headless, per setup.py, so inference runs through ONNX Runtime and image loading and resizing go through OpenCV. There is no PyTorch dependency in the installed package, which is why the pip install stays small. The 320n ONNX file is included in the package itself, and the README states it is the default. To use 640m you download the ONNX file yourself and pass its path along with inference_resolution=640, so the higher-resolution path is opt-in and costs you a manual download. detect and detect_batch accept file paths, OpenCV images, image bytes, or a buffereader from open(image_path, 'rb'). That input flexibility is the practical core of the API: you can feed a web request body straight in without writing to disk first. The Docker image exposes the same model behind an HTTP endpoint on port 8080.
Installing NudeNet and running a first detection
The README gives a single install line and pins the minimum version. Run it in the environment where you will do inference.
pip install --upgrade "nudenet>=3.4.2"The package pulls in onnxruntime, numpy and opencv-python-headless, and the 320n model is bundled, so there is no separate weight download for the default path. The next block is the smallest useful program. Instantiate the detector with no arguments to get the bundled 320n model, then call detect on a path.
from nudenet import NudeDetector
detector = NudeDetector()
# the 320n model included with the package will be used
detections = detector.detect('image.jpg')
print(detections)You should see a list of dictionaries. The README shows the shape with a BELLY_EXPOSED entry at score 0.799403190612793 and box [64, 182, 49, 51], and a FACE_FEMALE entry at score 0.7881264686584473. The box is four integers. Nothing in the output is a decision, so filter on class and score yourself. For multiple files, detect_batch takes a list and returns a list of lists, one detection list per input. If you want the censor path, censor('image.jpg') returns the path of the censored output image, and the signature accepts classes and output_path. If you would rather not install Python at all, the README documents a container path.
docker run -it -p8080:8080 ghcr.io/notai-tech/nudenet:latestWith the container running, POST an image to the /infer endpoint as a multipart field. The README's example uses the field name f1.
curl -F f1=@"images.jpeg" "http://localhost:8080/infer"The documented response wraps the same detection list in a prediction key and adds success: true. In the README's sample response the top entries are BELLY_EXPOSED at 0.8511635065078735 and FACE_FEMALE at 0.8033977150917053. Note that the sample scores sit in the 0.68 to 0.85 range, which is a useful hint that a naive 0.9 threshold will drop real detections.
Switching to the 640m model and what that costs you
The default 320n model is trained at 320x320. If your images contain small or distant regions, that resolution limits what the detector can resolve, and the README's answer is the 640m model. The catch is packaging: 640m is not in the pip package. You download 640m.onnx from the v3.4-weights release assets and point the detector at the file, passing the resolution as well. Both arguments are required for the model to behave as documented, because the detector needs to know the inference resolution it should resize inputs to.
detector = NudeDetector(model_path="downloaded_640m.onnx path", inference_resolution=640)The trade is straightforward: a larger download you manage yourself, plus more compute per image, in exchange for a model trained at twice the resolution and based on yolov8m rather than yolov8n. For a batch job over a fixed corpus, that is usually worth it. For a request-time check inside a web handler, the 320n default is the one that ships ready to use.
Where NudeNet is the wrong tool
The most concrete limitation is stated by the maintainer, not by a benchmark: the README opens by asking for contributors and maintainers, saying the author has become busy with other stuff. Releases reflect that rhythm. The v3.4-weights release is dated 2024-06-30, the v3.2 weights 2024-06-15, and before those the v2.0.9 release is dated 2021-02-22. The repository is not archived and the last push was on 2026-06-09, so the code is not frozen, but the weight releases are the part that determines detection quality, and they have not moved since mid-2024. If your requirement is a vendor-supported moderation API with an accuracy contract, this is not that. Second, the label set is body-part oriented, which means it does not answer questions like "is this image sexually explicit overall". You get regions and scores, and the policy layer is yours to build. Third, the output includes FACE_FEMALE and FACE_MALE detections. Those are in the model's label list, and they will appear in your results; if you log detections, you are logging face regions alongside everything else, which is a data-handling decision you should make deliberately. Fourth, the README does not document accuracy figures, per-class precision or recall, or a recommended score threshold, so you cannot pick a cutoff from the documentation alone. You have to measure on your own images. Fifth, the README does not document rollback, model versioning beyond the release tags, or a deprecation policy for the ONNX weights, so pinning a specific model file is on you.
NudeNet compared with a hosted NSFW classifier
The natural alternative is a hosted NSFW classification service or a general image-moderation API, where you send an image and receive a single explicit/not-explicit score. The difference in approach is not just hosting. A classifier collapses the image into one label, so you cannot ask it to ignore covered body parts or to treat only a specific region as a violation. NudeNet returns boxes and per-class scores, which means you can implement that policy yourself, and you can run it with no network call at all. The cost is that you own the threshold tuning, the model file, and the runtime. The other alternative is a general object detection stack you train yourself on your own labels. That gives you control over the label taxonomy, but you supply the annotated data, the training run and the export pipeline; NudeNet hands you a trained ONNX file and a three-dependency install instead. There is also the censor path, which is unusual for a moderation API: censor('image.jpg') writes a censored image and returns its path, so the same package can both flag and redact.
Licence and the cost of keeping it running
NudeNet is licensed AGPL-3.0. That is a copyleft licence with a network clause, and it is the single most consequential fact for commercial adoption. If you modify the code and let users interact with it over a network, the AGPL's source-availability obligation is generally understood to apply to your modified version. This is not legal advice, and the boundary depends on whether you are distributing the software, offering it as a service, or merely using it internally; get a lawyer's read on your specific deployment before you build a product on it. On upgrade cost: the package is versioned in setup.py at 3.4.2, the README's install line requires nudenet>=3.4.2, and the model weights are separate release assets under tags like v3.4-weights. That separation is the maintenance burden. A pip upgrade can move the library while your downloaded 640m.onnx stays where it is, so record which weight file and which inference_resolution your results were produced with. The dependency list is short, which keeps the surface small, but onnxruntime and opencv-python-headless are the two packages most likely to need attention when you move Python versions.
Editorial conclusion
NudeNet suits engineers who need a local, offline detector with a small dependency set and a model that arrives with the package, especially for single-image checks or a censor step. It is not a fit for anyone who needs a maintained, actively developed moderation service with documented accuracy guarantees, because the README asks for contributors and the last push was on 2026-06-09. Before adopting, verify the 320n model's behaviour on your own image distribution, confirm the AGPL-3.0 obligations against how you plan to distribute or host the code, and check whether the Docker image's /infer endpoint fits your throughput needs.
Frequently asked questions
How do I install NudeNet?
The README gives one command: pip install --upgrade "nudenet>=3.4.2". The 320n model is included in the package, so no separate download is needed for the default setup.
Does NudeNet return a single NSFW score for an image?
No. detect returns a list of detections, each with a class, a score and a box, drawn from 18 labels such as FEMALE_BREAST_EXPOSED and BELLY_COVERED. You apply your own threshold and decide which labels count as a violation.
Can I run NudeNet without installing Python?
Yes. The README documents a Docker image at ghcr.io/notai-tech/nudenet:latest that listens on port 8080, and you POST an image to the /infer endpoint as a multipart field named f1 to get the same detection list back.
How do I use the larger 640m model in NudeNet?
Download 640m.onnx from the v3.4-weights release assets, then construct the detector with model_path pointing at that file and inference_resolution=640. The 640m model is not bundled with the pip package.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/notai-tech-nudenet)