Nodes/comfyui-nsfw-detector/NSFW Detector + Save Image
ComfyUI Node

NSFW Detector + Save Image

The NSFW detector that tags your filenames instead of blocking

By newraina·Created 6 months ago·Updated 2 months ago· 0
NSFW Detector + Save Image
  • images
  • images
  • nsfw_level
  • nsfw_score
filename_prefixComfyUI
nsfw_thresholdmedium
formatpng
quality90
losslessfalse

Most NSFW detectors you meet inside ComfyUI are gates. ReActor ships one that blanks out output the moment it sees something it doesn't like, and half the threads about it are people editing the score threshold so their images survive. This node is the opposite animal: NSFW Detector + Save Image never blocks anything. It runs a classifier over every image, then just tells you the verdict - by baking it into the filename. job_00001_.png becomes job_flagged_high_00001_.png. That's the whole trick, and it's surprisingly useful.

The use case that makes sense is batch work. Fire off a long run, come back, and instead of eyeballing two hundred PNGs or running a second pass with a viewer, you have a file listing that already separates the flagged ones. People also use it to keep a content policy honest on a shared/gallery machine, and to triage datasets - the flagged prefix plus the embedded score makes a decent cheap pre-filter before you hand a folder to a captioner. It's a small niche, and the pack is honest about being one node.

How it works

Under the hood it's a Hugging Face image-classification pipeline loaded from Freepik/nsfw_image_detector, an EVA-based model around 86MB. It classifies each image into one of four levels - neutral, low, medium, high - and then, this is the detail to remember, it uses the worst result in the whole batch. One flagged frame in a batch of five and the nsfw_level output is that frame's verdict, and every saved file in the batch gets the _flagged_ prefix. Great for "did anything in this run cross the line," surprising if you expected per-image flags.

The verdict only determines the filename prefix. The save itself is a faithful re-implementation of ComfyUI's own SaveImage: it writes to ComfyUI/output/, uses the same auto-incrementing counter, and keeps the filename_prefix templating (%date:yyyy-MM-dd% and friends) working because it calls the same get_save_image_path helper. It also embeds the usual prompt/workflow chunks plus two extra ones, nsfw_level and nsfw_score, so drag-back-still-works and the classification travels with the file. Keep PNG if that matters - JPEG and WebP can't carry those text chunks.

The inputs that matter

Three required, and you'll mostly touch two:

  • images - feed it whatever your sampler outputs, same as SaveImage.
  • filename_prefix - base name; flagged files get _flagged_{level} appended.
  • nsfw_threshold - where the flagging line sits:
    • low - flags anything from low up. Strictest.
    • medium - flags medium/high, lets low pass. Balanced default.
    • high - flags only the worst. Most lenient.

The optional format (png/webp/jpeg) and quality (1–100) mirror ComfyUI's save options; quality only applies to webp/jpeg and is ignored for png. lossless is WebP-only - flip it on and quality becomes compression effort.

Outputs: images (the batch, passed through so you can chain more nodes after), plus nsfw_level (STRING) and nsfw_score (FLOAT, the confidence of the detected level). If you want to branch on the result - route flagged batches to a different save folder, say - wire those into a logic/switch node rather than reading the filename.

Installing it

ComfyUI Manager is the easy path: search "comfyui-nsfw-detector" and install. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/newraina/comfyui-nsfw-detector.git
pip install -r comfyui-nsfw-detector/requirements.txt

Then restart ComfyUI. First run downloads the model from Hugging Face automatically (needs network), or you can pre-drop config.json and model.safetensors into ComfyUI/models/nsfw_detector/ to skip that.

Where people get burned

The dependencies are the real gotcha. requirements.txt pulls in transformers>=4.40 and timm, which are chunky and install into the same Python environment as every other custom node. If another pack pins a different transformers version, pip resolves whatever it resolves and one of them can quietly break - the classic custom-node dependency fight. If you're on CPU it still runs (it falls back to device=-1), just slower per image, and a full batch is classified image-by-image so big runs add real time.

Keep in mind what this node is not: a content filter. It doesn't delete, blur, or gate anything - it renames files and reports scores. If you need enforcement rather than labeling, this isn't it. For labeling, it's a clean, single-node replacement for SaveImage that does its one job.

Categoryimage

Inputs (6)

NameTypeDefaultDescription
imagesIMAGE
filename_prefixSTRINGComfyUI
nsfw_thresholdCOMBOmedium3 options: low, medium, high
formatoptCOMBOpng3 options: png, webp, jpeg
qualityoptINT901–100
losslessoptBOOLEANfalse

Outputs (3)

NameTypeDescription
imagesIMAGE
nsfw_levelSTRING
nsfw_scoreFLOAT