File size: 5,708 Bytes
36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 a898069 36baa61 fe17941 36baa61 2a6e433 36baa61 2a6e433 36baa61 2a6e433 36baa61 2a6e433 fe17941 36baa61 2a6e433 36baa61 fe17941 36baa61 fe17941 2a6e433 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 2a6e433 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 fe17941 36baa61 2a6e433 36baa61 2a6e433 36baa61 fe17941 36baa61 2a6e433 36baa61 2a6e433 fe17941 36baa61 fe17941 36baa61 2a6e433 fe17941 f97665b 00d0c1d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import cv2
import numpy as np
import aiofiles
from pathlib import Path
import uuid
import logging
# Import the smart sequential moderator
from sequential_moderation import SmartSequentialModerator
# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Initialize FastAPI
app = FastAPI(
title="Content Detection API",
description="Simple API for detecting inappropriate content",
version="2.0.0"
)
# Add CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Configuration
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
MAX_IMAGE_SIZE = 50 * 1024 * 1024 # 50MB
MAX_VIDEO_SIZE = 500 * 1024 * 1024 # 500MB
# Global moderator
moderator = None
# ============== Response Model ==============
class DetectionResponse(BaseModel):
"""Simple response with counts and safety status"""
nude: int = 0
gun: int = 0
knife: int = 0
fight: int = 0
is_safe: bool = True
# ============== Startup ==============
@app.on_event("startup")
async def startup_event():
global moderator
try:
logger.info("π Initializing Smart Sequential Moderator...")
moderator = SmartSequentialModerator()
logger.info("β
Ready to process requests")
logger.info("π Pipeline: NSFW (0.75) β Weapons/Fights")
except Exception as e:
logger.error(f"Failed to initialize: {e}")
moderator = None
# ============== API Endpoints ==============
@app.post("/detect/image", response_model=DetectionResponse)
async def detect_image(file: UploadFile = File(...)):
"""
Detect inappropriate content in image
Sequential processing:
1. NSFW check (threshold: 0.75)
2. If NSFW detected β stop and return
3. If clean β check weapons & fights
Returns counts and safety status
"""
if moderator is None:
raise HTTPException(status_code=503, detail="Service not ready")
try:
# Validate extension
allowed = {'.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp'}
ext = Path(file.filename).suffix.lower()
if ext not in allowed:
raise HTTPException(400, f"Invalid type. Allowed: {allowed}")
# Read file
content = await file.read()
# Check size
if len(content) > MAX_IMAGE_SIZE:
raise HTTPException(400, f"File too large (max {MAX_IMAGE_SIZE // 1024 // 1024}MB)")
# Decode image
nparr = np.frombuffer(content, np.uint8)
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if image is None:
raise HTTPException(400, "Cannot decode image")
# Process
logger.info(f"Processing image: {file.filename}")
result = moderator.process_image(image)
# Return
return DetectionResponse(
nude=result.nude_count,
gun=result.gun_count,
knife=result.knife_count,
fight=result.fight_count,
is_safe=result.is_safe
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error: {e}")
raise HTTPException(500, str(e))
@app.post("/detect/video", response_model=DetectionResponse)
async def detect_video(file: UploadFile = File(...)):
"""
Detect inappropriate content in video
Features:
- AUTO frame skipping based on duration
- Early stop after 3 NSFW detections
- Sequential processing per frame
Returns total counts and safety status
"""
if moderator is None:
raise HTTPException(status_code=503, detail="Service not ready")
video_path = None
try:
# Validate extension
allowed = {'.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv'}
ext = Path(file.filename).suffix.lower()
if ext not in allowed:
raise HTTPException(400, f"Invalid type. Allowed: {allowed}")
# Save temporarily
video_id = f"vid_{uuid.uuid4().hex[:8]}"
video_path = UPLOAD_DIR / f"{video_id}{ext}"
async with aiofiles.open(video_path, 'wb') as f:
content = await file.read()
await f.write(content)
# Check size
size = video_path.stat().st_size
if size > MAX_VIDEO_SIZE:
video_path.unlink()
raise HTTPException(400, f"File too large (max {MAX_VIDEO_SIZE // 1024 // 1024}MB)")
# Process with auto settings
logger.info(f"Processing video: {file.filename} ({size // 1024 // 1024}MB)")
result = moderator.process_video(str(video_path))
# Clean up
try:
video_path.unlink()
except:
pass
# Return
return DetectionResponse(
nude=result['nude'],
gun=result['gun'],
knife=result['knife'],
fight=result['fight'],
is_safe=result['is_safe']
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error: {e}")
# Clean up on error
if video_path and video_path.exists():
try:
video_path.unlink()
except:
pass
raise HTTPException(500, str(e))
if __name__ == "__main__":
import os
port = int(os.environ.get("PORT", 7860))
options = {
"bind": f"0.0.0.0:{port}",
"workers": 2,
"worker_class": "uvicorn.workers.UvicornWorker",
}
StandaloneApplication(app, options).run()
|