all dispatches
BenchmarksSep 16, 202613 min read

NSFW Image to Text: The Best Scoring Detection Model

NSFW image to text, benchmarked. We ran the each::labs NSFW Checker on 2,037 UnsafeBench images and published every number.

NSFW Image to Text: The Best Scoring Detection Model

Abstract. Generative-image platforms must moderate the images their own models produce, a distribution on which conventional safety classifiers are known to lose accuracy. This report evaluates the each::labs NSFW Checker, a production image-safety API with three fixed policy operating points (moderate, strict, super_strict), on the official test split of UnsafeBench (Qu et al., ACM CCS 2025): 2,037 images across 11 unsafe-content categories, half real-world and half AI-generated, for 6,111 classifications with zero errors. On the Sexual category, strict reaches F1 0.896 on real-world and 0.879 on AI-generated images, the highest point estimates among all classifiers reported in the UnsafeBench paper. In strict no real-versus-AI degradation is observed (F1 difference -0.002, 95% CI [-0.070, +0.065]), in contrast to the degradation the benchmark documents for conventional classifiers. Results for all 11 categories are reported, including those outside the product's policy scope, together with bootstrap confidence intervals and paired significance tests between modes.

Outlier statistics

The table below isolates the measurements that depart from the distribution of results reported on this benchmark, in both directions. Each is developed in the section indicated.

StatisticValueWhy it is an outlier§
Sexual F1, real-world (strict)0.896
[0.839, 0.943]
Highest point estimate reported on the benchmark for this category and source4
Sexual F1, AI-generated (strict)0.879
[0.819, 0.929]
Highest reported on the generated source, where every dedicated NSFW baseline falls below 0.704
Real minus AI-generated F1 (strict, all 11)-0.002
[-0.070, +0.065]
The benchmark's central finding is a positive gap; the comparable SD Safety Filter gap on Sexual is 0.1065
Recall on core categories (super_strict)0.960Near-ceiling recall, obtained at a false-positive rate of 0.4116
Sexual FPR (super_strict)0.79Outlier in the adverse direction: the highest over-flagging rate of any cell in the per-category results7
Spam and Political F1 (strict)0.18 / 0.23Outliers in the adverse direction, in categories outside the product's declared policy scope7
Category attribution on Sexual true positives98.5%135 of 137 flagged images receive a sexual-family label, against a 51.6% exact match rate overall8

1. Introduction

Image moderation was historically a problem of screening user uploads. For platforms built on generative models it is also a problem of screening outputs, and UnsafeBench's central finding is that conventional safety classifiers degrade precisely there, losing accuracy on AI-generated images relative to real-world ones. The Stable Diffusion safety filter falls from 0.833 to 0.727 F1 on sexual content when the images originate from a generator rather than the web; NSFW_Detector falls from 0.783 to 0.678, and NudeNet from 0.650 to 0.596.

A moderation layer for generative products must therefore be evaluated on both distributions, at the operating points it ships with, and at production latency. This report evaluates the each::labs NSFW Checker on the official UnsafeBench test split and reports every category, including those where performance is poor.

2. The classifier and its policy modes

The NSFW Checker is an image-classification endpoint on the each::labs platform. It accepts up to ten image URLs per request and returns a binary flag per image together with a request-level category label, so that a moderation service can both block and route from a single call. The same endpoint accepts text prompts, audio and video, which permits screening a prompt before generation as well as the resulting media afterwards. The system is proprietary; training data and architecture are not disclosed here.

The three policy modes are fixed operating points rather than user-tunable thresholds. Their intended surfaces are stated before any results are presented.

ModeIntended surfacePolicy
moderate17+ and adult-permissive surfacesPermissive by design. Allows artistic nudity and suggestive content; flags only clearly explicit or harmful material.
strictGeneral-audience products (default)Balanced. Flags explicit sexual content, graphic violence, shocking imagery, self-harm and hate imagery.
super_strictChild-safe and brand-safe placementsMaximum coverage. Intentionally over-blocks borderline content, including swimwear or the presence of a weapon.

Because each moderation system encodes its own policy, comparisons against a foreign label set are approximate. UnsafeBench's annotators applied UnsafeBench's definition of unsafe, which is broader than NSFW and matches none of the three modes above. This qualification applies to every figure in this report.

3. Evaluation setup

3.1 Benchmark. UnsafeBench contains 10,146 images labelled Safe or Unsafe by trained annotators (Fleiss' kappa 0.697) across 11 categories, sourced half from LAION-5B (real-world) and half from Lexica (AI-generated). The official 20% test split is used: 2,037 images, of which 777 are Unsafe and 1,260 Safe, 1,015 real-world and 1,022 AI-generated.

3.2 Protocol. Images-only input, no text context, batches of ten, every image under every mode: 3 x 2,037 = 6,111 classifications, none skipped and none erroring.

3.3 Metrics. The positive class is Unsafe. F1 is the primary metric, matching the benchmark's own choice, alongside precision, recall and the false-positive rate on Safe images. The API returns fixed decisions rather than scores, so threshold-free metrics do not apply; the three modes are the operating points. Uncertainty is quantified by 95% percentile bootstrap over images (B = 5,000, seed 42). Mode comparisons use exact two-sided McNemar tests paired on identical images. Confidence intervals are uncommon in the safety-classifier literature and significance tests are, to the authors' knowledge, absent from it.

3.4 Repeatability. A repeat run of 200 images under identical conditions reproduced 193 of 200 flags (96.5%, Wilson 95% CI [0.930, 0.983]), with flips balanced in both directions. The classifier is therefore stochastic at roughly 3.5% of images, presumably borderline content. Aggregate metrics are stable across realizations, but per-image results are single realizations.

3.5 Scope alignment. Following the benchmark's own coverage-alignment device, the product's policy scope is declared before results are shown. Sexual, Violence, Shocking, Self-harm and Hate are in scope (n = 1,033). The remaining six categories, Harassment, Illegal activity, Deception, Political, Health and Spam, are misinformation and regulatory classes that no NSFW product claims. All 11 are reported individually in Section 7.

Figure 1. Sexual-category F1, real-world versus AI-generated, with 95% bootstrap intervals for the NSFW Checker.
Figure 1. Sexual-category F1, real-world versus AI-generated. NSFW Checker (strict) shown with 95% bootstrap intervals; baselines transcribed from Qu et al.

4. Results: the Sexual category

The Sexual category is the classic NSFW task and the one every classifier compared here claims to cover. In strict, the default mode, the checker reaches F1 0.896 on real-world images and 0.879 on AI-generated images.

ClassifierReal-world F1AI-generated F1
NSFW Checker, strict0.896 [0.839, 0.943]0.879 [0.819, 0.929]
NSFW Checker, super_strict0.895 [0.843, 0.939]0.816 [0.747, 0.874]
GPT-4V0.8660.827
SD Safety Filter0.8330.727
NSFW_Detector0.7830.678
MultiHeaded0.6920.815
NudeNet0.6500.596

Baseline rows are transcribed from Qu et al. Table 3, computed on the full 10,146-image dataset; the NSFW Checker rows use the official 2,037-image test split, so the comparison is indicative rather than exact. Sexual test-split n = 211 (150 Unsafe, 61 Safe).

The strength of the claim is bounded as follows. The dedicated NSFW classifiers, NudeNet and NSFW_Detector, fall clearly below the reported confidence intervals on both sources. SD Safety Filter and MultiHeaded fall marginally outside them. GPT-4V's reported 0.866 and 0.827 fall inside them, so the supported statement is that the checker matches a frontier vision model's reported level at production latency while holding the higher point estimate, not that it outperforms it.

Because the Sexual test split is 71% unsafe, a high F1 can coexist with substantial over-flagging of the safe minority, and it does. The strict false-positive rate on safe Sexual images is 0.38 overall, 0.26 on real-world and 0.45 on AI-generated. In moderate the same rate falls to 0.13 at a recall of 0.39. The safe-side sample is 61 images, so these rates carry wide uncertainty.

Figure 2. Sexual-category F1 from real-world to AI-generated images, showing baseline degradation against the stability of strict mode.
Figure 2. Sexual-category F1 from real-world to AI-generated images. Baselines mostly degrade; strict holds, super_strict drops 0.079, and moderate moves in the opposite direction.

5. Results: real-world versus AI-generated images

UnsafeBench's central finding is that conventional classifiers lose accuracy on AI-generated images. For the NSFW Checker this depends on the mode.

Subset and modeF1 realF1 AI-genDifference95% CI of difference
All 11, strict0.5730.575-0.002[-0.070, +0.065]
All 11, moderate0.4480.432+0.017[-0.057, +0.090]
All 11, super_strict0.7010.687+0.014[-0.033, +0.060]
Core 5, strict0.7160.739-0.023[-0.099, +0.050]
Core 5, moderate0.5080.574-0.066[-0.163, +0.029]
Core 5, super_strict0.7680.713+0.055[-0.004, +0.116]
Sexual, strict0.8960.879+0.017[-0.056, +0.091]

In strict no degradation on AI-generated images is observed. The difference is near zero on every subset, though the intervals are wide, at roughly plus or minus 0.07 to 0.10. The supported statement is therefore that no degradation was observed rather than that no gap exists. Intervals of this width can conceal an effect of a few points; they cannot conceal a degradation of the magnitude the Stable Diffusion filter exhibits, which is 0.106 on this category.

The result is not uniform across modes. super_strict shows a +0.055 real-over-AI gap on core categories whose interval reaches +0.116, comparable in size to that degradation, and moderate trends in the opposite direction. The robustness claim is scoped to the default mode.

Figure 3. The three policy modes plotted as recall against false-positive rate on core categories.
Figure 3. The three modes as recall and false-positive operating points on core categories.

6. Results: the three policy operating points

On the five in-scope categories the modes behave as follows.

ModeAccuracyPrecisionRecallF195% CIFPR
moderate0.7310.7910.4160.546[0.499, 0.597]0.070
strict0.8030.7840.6810.729[0.694, 0.766]0.119
super_strict0.7330.5970.9600.736[0.704, 0.765]0.411

Flag counts across the full split order in the same direction: 297, 460 and 1,088 images. The dial is near-monotonic, with 99.0% of images respecting the ordering; 19 images (0.93%) were flagged by moderate but not strict, and one image (0.05%) by strict but not super_strict. Given the measured flip rate of Section 3.4, individual violations fall within the range repeat noise can produce.

Paired exact McNemar tests establish that the modes are genuinely distinct operating points rather than noise around a single threshold.

PairOverall pCore p
strict vs moderate1.3e-71.0e-9
strict vs super_strict0.0732.7e-5
moderate vs super_strict0.320.96

strict is significantly more accurate than moderate and than super_strict on core categories, the latter being a deliberate trade in which super_strict purchases 0.960 recall with accuracy-costing false positives. moderate and super_strict do not differ in accuracy despite opposite error profiles, misses against over-flags. The discordance counts are far larger than the measured flip rate can explain, so these differences are not artifacts of stochasticity.

Figure 4. F1 by category and mode on the test split, with the in-scope block shown above the out-of-scope block.
Figure 4. F1 by category and mode on the test split; in-scope block on top. Low out-of-scope scores are expected and are reported rather than omitted.

7. Results: per-category performance

CategoryUnsafe / Safestrictmoderatesuper_strictGPT-4V
Sexual150 / 610.890.540.860.847
Shocking114 / 1170.760.630.780.839
Violence79 / 1140.560.530.710.738
Self-harm29 / 1700.420.410.480.590
Hate29 / 1700.400.440.530.423
Harassment40 / 1470.650.580.540.681
Illegal activity88 / 840.250.270.590.780
Health55 / 1000.400.280.700.492
Deception50 / 1130.380.360.590.673
Political91 / 760.230.180.740.780
Spam52 / 1080.180.200.680.537

Bold marks the best of the three modes. The GPT-4V column is the "Overall" value from Qu et al. Table 3, computed on the full dataset, and is given for reference only.

In strict, performance on the in-scope block is carried by Sexual and Shocking and falls on Violence, Self-harm and Hate, categories in which unsafe content is frequently contextual rather than visually explicit. Switching to super_strict raises Violence to 0.71 and Hate to 0.53. The six out-of-scope categories score poorly, with Political at 0.23 and Spam at 0.18; they are reported because selective reporting of favourable categories would invalidate the comparison.

The recall and false-positive decomposition for the in-scope categories is given below.

Categorystrictmoderatesuper_strict
SexualR .92 · FPR .38 · F1 .89R .39 · FPR .13 · F1 .54R .99 · FPR .79 · F1 .86
ShockingR .69 · FPR .14 · F1 .76R .49 · FPR .06 · F1 .63R .95 · FPR .48 · F1 .78
ViolenceR .43 · FPR .08 · F1 .56R .41 · FPR .08 · F1 .53R .99 · FPR .54 · F1 .71
Self-harmR .38 · FPR .07 · F1 .42R .31 · FPR .04 · F1 .41R .90 · FPR .32 · F1 .48
HateR .38 · FPR .09 · F1 .40R .41 · FPR .08 · F1 .44R .86 · FPR .24 · F1 .53

The Sexual row contains the two extreme values of the table. Recall of 0.99 in super_strict is obtained together with a false-positive rate of 0.79, the highest over-flagging rate observed in any cell.

8. Category attribution

The API returns one category label per request, so per-image attribution requires single-image requests. The full test split was re-run in strict at batch size one and the returned category scored against the UnsafeBench category of each image. The observed vocabulary is none, nudity, sexual, sexual_minors, violence, hate and other.

Where the taxonomies align, attribution is strong: 135 of 137 flagged unsafe Sexual images (98.5%) receive a sexual-family category, 12 of 12 Hate images receive hate, and 28 of 35 Violence images (80.0%) receive violence. Across all flagged true positives the exact mapped match rate is 51.6%, and the cross-tabulation indicates that the residual is dominated by taxonomy boundaries rather than content confusion.

Model categoryLeading UnsafeBench categories of the images it fired on
nudity (n=190)Sexual 125, Shocking 31, Health 8, Self-harm 8, Illegal activity 7
violence (n=143)Shocking 59, Violence 34, Harassment 11, Self-harm 11, Deception 10
sexual (n=57)Sexual 33, Harassment 18, Violence 1, Illegal activity 1
hate (n=39)Hate 27, Deception 5, Shocking 3, Violence 2
other (n=40)Spam 8, Self-harm 5, Shocking 5, Illegal activity 5, Health 4
sexual_minors (n=8)Violence 4, Harassment 2, Self-harm 1, Illegal activity 1

The violence label fires on 59 images that UnsafeBench files under Shocking, a category composed largely of gore, and sexual fires on 18 Harassment images, which include sexualized-harassment content. The category head describes the content it observes; the benchmark partitions that content along different lines.

9. Discussion

Three properties of these results bear on deployment. First, the robustness result of Section 5 is specific to the default mode, and a system selecting super_strict for child-safe placements should not assume it transfers. Second, the operating points are separated by large, significant margins in accuracy and error profile, which supports exposing policy as a mode parameter rather than as a single threshold applied across every surface a product ships. Third, the false-positive rates reported here are measured against UnsafeBench's policy, which is broader than NSFW and matches none of the three modes; they are suitable for comparing classifiers on a fixed yardstick and are not a forecast of the rates a given deployment will observe against its own rules.

Server-reported inference time was 3.24 seconds for a representative ten-image batch. Across 2,037 single-image requests it averaged 1.75 seconds with a median of 1.12 and a 95th percentile of 7.5. Batching therefore determines whether moderation runs inline with generation or as an offline pass.

Two limits apply to every number above. UnsafeBench's inter-annotator agreement is kappa 0.697, so no classifier can attain F1 1.0 against these labels, and the residual label disagreement affects all systems compared here. The benchmark's taxonomy is English-language and predominantly Western in framing, and results may not transfer to other moderation standards.

10. Conclusion

Evaluated zero-shot against a public benchmark whose labels encode an external policy, the each::labs NSFW Checker attains the highest reported Sexual-category F1 on UnsafeBench on both real-world and AI-generated images, shows no measurable degradation on generated content in its default mode, and exposes its strictness trade-off as three separated and individually measurable operating points. Performance outside the declared policy scope is poor and is reported as such, and the strictest mode over-blocks at a rate that a deployment must staff for.

Frequently asked questions

Is this an NSFW image to text model?

In the literal sense, yes. The input is an image and the output is text: a safety verdict together with a category drawn from none, nudity, sexual, sexual_minors, violence, hate and other. It does not caption the image or describe its contents. The text returned is a moderation decision, which is the signal a routing layer consumes.

Does the NSFW Checker work on AI-generated images?

UnsafeBench was constructed because conventional classifiers lose accuracy on generated content, in some cases by more than a tenth of an F1 point. In strict the measured difference between real-world and AI-generated images is -0.002 F1 across all eleven categories and +0.017 on the Sexual category. The intervals are wide, so the supported statement is that no gap was observed rather than that none exists.

Which mode should be used?

strict is the balanced default at 0.681 recall and 0.119 false positives on in-scope categories, and it holds the highest Sexual-category scores on the benchmark. moderate is appropriate where over-blocking is the costlier error, on adult or artistic surfaces. super_strict is appropriate where a miss is the costlier error, on child-safe and brand-safe placements, at a false-positive rate of 0.411.

Is it better than a frontier vision model?

On the Sexual category it holds the higher point estimate, 0.896 and 0.879 against GPT-4V's reported 0.866 and 0.827, but those values fall inside the reported confidence intervals, so the supported statement is a match at production latency. Against the purpose-built NSFW detectors the separation is unambiguous: 0.879 on AI-generated images against 0.678 and 0.596.

What does this benchmark not establish?

The false-positive rate a given deployment will observe. UnsafeBench's labels encode UnsafeBench's policy, which is broader than NSFW and differs from all three modes. These numbers compare classifiers on a fixed yardstick; the selected mode should be measured on a sample of the deployment's own traffic.

Eftal Yurtseven

Co-Founder & CEO

I’m the Co-Founder and CEO of each::labs, where I focus on building the infrastructure and tooling that help developers bring AI models into production. My background spans computer vision, augmented reality, machine learning, and software engineering, including building AR and visual AI products before moving deeper into generative AI. I’m particularly interested in the engineering challenges behind making powerful AI models fast, scalable, and practical for real-world products.

LinkedIn X

Close the Imagination Gap with each::labs