5.7 KiB
5.7 KiB
Detectors
Detectors provide binary classification and object detection for specific content types.
NSFWDetector
Content safety classification using HuggingFace transformer models.
Models
| Model | Accuracy | Notes |
|---|---|---|
| Marqo/nsfw-image-detection-384 | 98.56% | Primary, lightweight |
| Falconsai/nsfw_image_detection | 98.04% | Fallback |
Basic Usage
from PIL import Image
from lilith_content_understanding import NSFWDetector
detector = NSFWDetector()
image = Image.open("photo.jpg")
# Full classification
result = detector.classify(image)
print(f"NSFW: {result.is_nsfw}")
print(f"Confidence: {result.confidence:.2%}")
print(f"Category: {result.category}")
print(f"All scores: {result.all_scores}")
# Quick checks
is_explicit, confidence = detector.is_explicit(image)
is_suggestive, confidence = detector.is_suggestive(image)
Configuration
detector = NSFWDetector(
model_name="Marqo/nsfw-image-detection-384", # HuggingFace model
device="cuda", # Force GPU (None for auto)
nsfw_threshold=0.7, # Threshold for is_nsfw flag
suggestive_threshold=0.4 # Threshold for suggestive content
)
NSFWResult Fields
| Field | Type | Description |
|---|---|---|
is_nsfw |
bool | Whether image exceeds NSFW threshold |
confidence |
float | Confidence score (0-1) |
category |
str | Detected category ("nsfw", "safe", etc.) |
all_scores |
dict[str, float] | All category scores |
Thresholds
- nsfw_threshold (default 0.7): Images with NSFW score >= this are flagged
- suggestive_threshold (default 0.4): For detecting borderline content
Higher thresholds = fewer false positives, more false negatives.
BodyPartDetector
Anatomical detection with bounding boxes using NudeNet.
Requirements
pip install lilith-content-understanding[nudenet]
Basic Usage
from PIL import Image
from lilith_content_understanding import BodyPartDetector
detector = BodyPartDetector()
image = Image.open("photo.jpg")
result = detector.detect(image)
# Quick checks
print(f"Has nudity: {result.has_nudity}")
print(f"Body parts: {result.body_parts_list}")
# Gender presentation (trans-inclusive)
print(f"Gender: {result.gender_presentation}")
print(f"Gender confidence: {result.gender_confidence:.2%}")
# Get bounding boxes for specific parts
breast_regions = result.get_bboxes_for_category("BREASTS")
for bbox in breast_regions:
x1, y1, x2, y2 = bbox # Normalized 0-1 coordinates
print(f"Region: ({x1:.2f}, {y1:.2f}) to ({x2:.2f}, {y2:.2f})")
Configuration
detector = BodyPartDetector(
model_path=None, # Path to custom ONNX model (None = default 320n)
threshold=0.4, # Minimum confidence for detections
)
BodyPartResult Fields
| Field | Type | Description |
|---|---|---|
detections |
list[BodyPartDetection] | All detected body parts |
has_nudity |
bool | Any exposed nudity detected |
has_male_genitalia |
bool | Male genitalia exposed |
has_female_genitalia |
bool | Female genitalia exposed |
has_breasts |
bool | Breasts exposed |
has_buttocks |
bool | Buttocks exposed |
has_face |
bool | Face detected |
face_gender |
str | "male", "female", or None |
gender_presentation |
GenderPresentation | Inferred gender |
gender_confidence |
float | Gender inference confidence |
Gender Presentation
The GenderPresentation enum supports trans-inclusive detection:
| Value | Description |
|---|---|
MALE |
Male face + male anatomy |
FEMALE |
Female face + female anatomy |
TRANS_MTF |
Female presentation + penis |
TRANS_FTM |
Male presentation + vagina |
AMBIGUOUS |
Mixed signals |
UNKNOWN |
No gender indicators |
Detected Categories
| Category | NudeNet Label |
|---|---|
| PENIS | MALE_GENITALIA_EXPOSED |
| VAGINA | FEMALE_GENITALIA_EXPOSED |
| BREASTS | FEMALE_BREAST_EXPOSED |
| BUTTOCKS | BUTTOCKS_EXPOSED |
| ANUS | ANUS_EXPOSED |
| FACE_MALE | FACE_MALE |
| FACE_FEMALE | FACE_FEMALE |
Bounding Box Usage
Bounding boxes are normalized (0-1) coordinates:
# Get all bounding boxes for a category
bboxes = result.get_bboxes_for_category("BREASTS")
# Convert to pixel coordinates
for x1, y1, x2, y2 in bboxes:
pixel_x1 = int(x1 * image.width)
pixel_y1 = int(y1 * image.height)
pixel_x2 = int(x2 * image.width)
pixel_y2 = int(y2 * image.height)
# Useful for inpainting masks
exposed = result.get_exposed_parts() # List of exposed body parts
covered = result.get_covered_parts() # List of covered body parts
Performance Tips
GPU Acceleration
Both detectors auto-detect CUDA availability:
# Check GPU status
print(f"GPU enabled: {detector.is_gpu_enabled}")
# Force specific device
detector = NSFWDetector(device="cuda") # Force GPU
detector = NSFWDetector(device="cpu") # Force CPU
Lazy Loading
Models are loaded on first use, not at initialization:
detector = NSFWDetector() # Fast, no model loaded
result = detector.classify(image) # Model loads here
Batch Processing
For multiple images, reuse detector instances:
detector = NSFWDetector()
# Good - reuses loaded model
for image in images:
result = detector.classify(image)
# Bad - loads model each iteration
for image in images:
detector = NSFWDetector()
result = detector.classify(image)
Health Checks
Get detector status for monitoring:
info = detector.get_info()
print(info)
# {
# "model": "Marqo/nsfw-image-detection-384",
# "device": "cuda",
# "gpu_available": True,
# "gpu_enabled": True,
# "initialized": True,
# "nsfw_threshold": 0.7,
# "suggestive_threshold": 0.4,
# }