SomeNewKid/LocalImageModeration

An agent solution which evaluates local models for moderation of image content.

Python

0

3 commits

updated Aug 8, 2026

See the code

README

Local Image Moderation

Local Image Moderation is a Python command-line workbench for comparing image moderation tools on a shared set of local test images. It runs each image through NudeNet, Marqo's NSFW image classifier, ShieldGemma 2, and the OpenAI Moderation API, then records normalized scores and raw model responses.

[!WARNING] This is an experimental project and should not be considered production-ready.

The project was created to explore how locally runnable image-moderation tools compare with an online moderation API. The benchmark is intentionally direct: place images in .images, run the CLI, and inspect the generated JSON files in a timestamped .runs directory.

What It Does

The CLI scans the .images directory for supported image files and evaluates each image with the configured moderation tools:

  • NudeNet, for nudity detection.
  • Marqo nsfw-image-detection-384, for NSFW image classification.
  • ShieldGemma 2 google/shieldgemma-2-4b-it, for sexual, dangerous, and violence/gore policies.
  • OpenAI omni-moderation-latest, through the Moderation API.

Each tool response is normalized into a shared score shape:

  • nudity
  • sexual_content
  • child_sexual_safety
  • violence
  • gore
  • self_harm
  • weapons
  • dangerous_activity
  • hate_extremism

Scores use a 0.0 to 100.0 scale. Categories that a tool does not evaluate are recorded as null rather than 0.0, so missing coverage is not mistaken for a safe result.

For each image and tool, the application records:

  • the image path
  • normalized category scores
  • evaluation time in seconds
  • the raw tool or API response
  • any error captured during evaluation

The command also prints per-image timing progress in a compact format:

Marqo: image-02.jpg (232ms)
ShieldGemma 2: image-02.jpg (20m 14s)

Requirements

  • Python 3.11.
  • PowerShell on Windows.
  • An OPENAI_API_KEY environment variable for OpenAI Moderation API calls.
  • An HF_TOKEN environment variable for gated Hugging Face model access.
  • Accepted Hugging Face license terms for google/shieldgemma-2-4b-it.
  • Sufficient disk space for PyTorch, Transformers, and model downloads.

ShieldGemma 2 is much slower and heavier than NudeNet and Marqo on CPU-only hardware. A full run over many images can take many hours.

Setup

Create the virtual environment and install the project with development dependencies:

.\scripts\setup-dev.ps1

The setup script expects Python 3.11 at the path configured in scripts\setup-dev.ps1.

Before using ShieldGemma 2, ensure your Hugging Face token can read public gated repositories you can access. In Hugging Face token permissions, this is the permission named:

Read contents of public gated repos you can access

Running

Place input images in the .images directory, then run from the repository root:

.\.venv\Scripts\python.exe -m local_image_moderation

The installed console command is equivalent:

.\.venv\Scripts\local-image-moderation.exe

The current default run sends every supported image to all configured tools: NudeNet, Marqo, ShieldGemma 2, and the OpenAI Moderation API.

Output

Each run creates a timestamped folder under .runs:

.runs/
  YYYYMMDD-HHMMSS/
    nudenet.json
    marqo.json
    shieldgemma-2.json
    openai-moderation.json

Each JSON file contains one model-level result object:

model_name
friendly_name
generated_at
images_directory
results[]

Each item in results contains normalized scores and the raw response for a single image. The raw response is retained so score mappings can be refined later without rerunning every model.

Score Mapping

The adapters map each tool's native outputs as closely as possible to the shared score categories.

NudeNet maps exposed-body detections to nudity. Other categories are null.

Marqo maps the model's NSFW probability to sexual_content. Other categories are null.

ShieldGemma 2 maps:

  • sexual to sexual_content
  • dangerous to dangerous_activity
  • violence to both violence and gore

OpenAI Moderation maps image-applicable category scores from omni-moderation-latest:

  • sexual to both nudity and sexual_content
  • sexual/minors to child_sexual_safety when image-applicable
  • violence to violence
  • violence/graphic to gore
  • self-harm subcategories to self_harm
  • illicit or violent-illicit categories to dangerous_activity and weapons when image-applicable
  • hate categories to hate_extremism when image-applicable

The OpenAI adapter checks category_applied_input_types so text-only category scores are not treated as image moderation results.

Development Checks

Run formatting, linting, type checking, and tests:

.\scripts\check.ps1

This runs:

  • ruff format .
  • ruff check .
  • pyright
  • pytest

Project Structure

src/local_image_moderation/
  __main__.py   Package entry point for python -m local_image_moderation
  cli.py        Command-line interface
  detectors.py  NudeNet, Marqo, ShieldGemma 2, and OpenAI adapters
  models.py     Shared dataclasses for normalized results
  runner.py     Image discovery, timing, run folders, and JSON output

tests/
  test_smoke.py

scripts/
  setup-dev.ps1
  check.ps1

Notes

This project is intended for comparison and exploration, not as a moderation policy by itself. A flagged or unflagged score should be interpreted in the context of the model's documented coverage and the test image set.

The tools do not cover the same policy areas. Empty scores in one model's output often mean "not evaluated" rather than "safe."

OpenAI API calls may incur usage costs. Hugging Face and PyTorch model downloads can be large. ShieldGemma 2 can be extremely slow on CPU-only hardware.

Third-Party Notices

This project has direct runtime dependencies on third-party Python packages, including accelerate, nudenet, openai, pillow, timm, torch, and transformers. It also downloads or accesses third-party model artifacts, including Marqo nsfw-image-detection-384 and Google shieldgemma-2-4b-it. See each package and model page for full license, acceptable-use, and notice terms.

License

GNU General Public License v3.0. See the LICENSE file for details.

SomeNewKid/LocalImageModeration

An agent solution which evaluates local models for moderation of image content.

Python

0

3 commits

updated Aug 8, 2026

See the code

README

Local Image Moderation

Local Image Moderation is a Python command-line workbench for comparing image moderation tools on a shared set of local test images. It runs each image through NudeNet, Marqo's NSFW image classifier, ShieldGemma 2, and the OpenAI Moderation API, then records normalized scores and raw model responses.

[!WARNING] This is an experimental project and should not be considered production-ready.

The project was created to explore how locally runnable image-moderation tools compare with an online moderation API. The benchmark is intentionally direct: place images in .images, run the CLI, and inspect the generated JSON files in a timestamped .runs directory.

What It Does

The CLI scans the .images directory for supported image files and evaluates each image with the configured moderation tools:

  • NudeNet, for nudity detection.
  • Marqo nsfw-image-detection-384, for NSFW image classification.
  • ShieldGemma 2 google/shieldgemma-2-4b-it, for sexual, dangerous, and violence/gore policies.
  • OpenAI omni-moderation-latest, through the Moderation API.

Each tool response is normalized into a shared score shape:

  • nudity
  • sexual_content
  • child_sexual_safety
  • violence
  • gore
  • self_harm
  • weapons
  • dangerous_activity
  • hate_extremism

Scores use a 0.0 to 100.0 scale. Categories that a tool does not evaluate are recorded as null rather than 0.0, so missing coverage is not mistaken for a safe result.

For each image and tool, the application records:

  • the image path
  • normalized category scores
  • evaluation time in seconds
  • the raw tool or API response
  • any error captured during evaluation

The command also prints per-image timing progress in a compact format:

Marqo: image-02.jpg (232ms)
ShieldGemma 2: image-02.jpg (20m 14s)

Requirements

  • Python 3.11.
  • PowerShell on Windows.
  • An OPENAI_API_KEY environment variable for OpenAI Moderation API calls.
  • An HF_TOKEN environment variable for gated Hugging Face model access.
  • Accepted Hugging Face license terms for google/shieldgemma-2-4b-it.
  • Sufficient disk space for PyTorch, Transformers, and model downloads.

ShieldGemma 2 is much slower and heavier than NudeNet and Marqo on CPU-only hardware. A full run over many images can take many hours.

Setup

Create the virtual environment and install the project with development dependencies:

.\scripts\setup-dev.ps1

The setup script expects Python 3.11 at the path configured in scripts\setup-dev.ps1.

Before using ShieldGemma 2, ensure your Hugging Face token can read public gated repositories you can access. In Hugging Face token permissions, this is the permission named:

Read contents of public gated repos you can access

Running

Place input images in the .images directory, then run from the repository root:

.\.venv\Scripts\python.exe -m local_image_moderation

The installed console command is equivalent:

.\.venv\Scripts\local-image-moderation.exe

The current default run sends every supported image to all configured tools: NudeNet, Marqo, ShieldGemma 2, and the OpenAI Moderation API.

Output

Each run creates a timestamped folder under .runs:

.runs/
  YYYYMMDD-HHMMSS/
    nudenet.json
    marqo.json
    shieldgemma-2.json
    openai-moderation.json

Each JSON file contains one model-level result object:

model_name
friendly_name
generated_at
images_directory
results[]

Each item in results contains normalized scores and the raw response for a single image. The raw response is retained so score mappings can be refined later without rerunning every model.

Score Mapping

The adapters map each tool's native outputs as closely as possible to the shared score categories.

NudeNet maps exposed-body detections to nudity. Other categories are null.

Marqo maps the model's NSFW probability to sexual_content. Other categories are null.

ShieldGemma 2 maps:

  • sexual to sexual_content
  • dangerous to dangerous_activity
  • violence to both violence and gore

OpenAI Moderation maps image-applicable category scores from omni-moderation-latest:

  • sexual to both nudity and sexual_content
  • sexual/minors to child_sexual_safety when image-applicable
  • violence to violence
  • violence/graphic to gore
  • self-harm subcategories to self_harm
  • illicit or violent-illicit categories to dangerous_activity and weapons when image-applicable
  • hate categories to hate_extremism when image-applicable

The OpenAI adapter checks category_applied_input_types so text-only category scores are not treated as image moderation results.

Development Checks

Run formatting, linting, type checking, and tests:

.\scripts\check.ps1

This runs:

  • ruff format .
  • ruff check .
  • pyright
  • pytest

Project Structure

src/local_image_moderation/
  __main__.py   Package entry point for python -m local_image_moderation
  cli.py        Command-line interface
  detectors.py  NudeNet, Marqo, ShieldGemma 2, and OpenAI adapters
  models.py     Shared dataclasses for normalized results
  runner.py     Image discovery, timing, run folders, and JSON output

tests/
  test_smoke.py

scripts/
  setup-dev.ps1
  check.ps1

Notes

This project is intended for comparison and exploration, not as a moderation policy by itself. A flagged or unflagged score should be interpreted in the context of the model's documented coverage and the test image set.

The tools do not cover the same policy areas. Empty scores in one model's output often mean "not evaluated" rather than "safe."

OpenAI API calls may incur usage costs. Hugging Face and PyTorch model downloads can be large. ShieldGemma 2 can be extremely slow on CPU-only hardware.

Third-Party Notices

This project has direct runtime dependencies on third-party Python packages, including accelerate, nudenet, openai, pillow, timm, torch, and transformers. It also downloads or accesses third-party model artifacts, including Marqo nsfw-image-detection-384 and Google shieldgemma-2-4b-it. See each package and model page for full license, acceptable-use, and notice terms.

License

GNU General Public License v3.0. See the LICENSE file for details.