7 / app.py
drdudddd's picture
Update app.py
4e27788 verified
Raw
History Blame Contribute Delete
5.89 kB
import gradio as gr
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import cv2
import os
import tempfile
from nudenet import NudeDetector
# --- Konstanten ---
DETECTION_MAX_DIM = 768
PIXELS_PER_CM_ESTIMATE = 15
MIN_CONFIDENCE = 0.45
detector = NudeDetector(inference_resolution=640)
def resize_for_detection(img_pil, max_dim):
if max(img_pil.width, img_pil.height) <= max_dim:
return img_pil, 1.0
ratio = max_dim / max(img_pil.width, img_pil.height)
new_size = (int(img_pil.width * ratio), int(img_pil.height * ratio))
resized = img_pil.resize(new_size, Image.Resampling.LANCZOS)
scale = 1 / ratio
return resized, scale
def describe_breast_precise(crop_pil):
w, h = crop_pil.size
if w * h == 0:
return "Fehler: leeres Crop"
gray = cv2.cvtColor(np.array(crop_pil), cv2.COLOR_RGB2GRAY)
_, thresh = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
nipple_detected = any(
40 < cv2.contourArea(c) < (w * h / 4)
and (p := cv2.arcLength(c, True)) > 0
and (4 * np.pi * cv2.contourArea(c) / (p * p)) > 0.55
for c in contours
)
ratio = w / h
shape = "Breit" if ratio > 1.15 else "Hoch" if ratio < 0.85 else "Rund"
size = "klein" if w * h < 28000 else "mittel" if w * h < 75000 else "groß" if w * h < 140000 else "sehr groß"
w_cm = round(w / PIXELS_PER_CM_ESTIMATE, 1)
h_cm = round(h / PIXELS_PER_CM_ESTIMATE, 1)
return f"Brust: {shape}, {size}, Nippel: {'Ja' if nipple_detected else 'Nein'}, {w_cm}x{h_cm}cm"
def describe_vagina_precise(crop_pil):
w, h = crop_pil.size
if w * h == 0:
return "Fehler: leeres Crop"
gray = cv2.cvtColor(np.array(crop_pil), cv2.COLOR_RGB2GRAY)
hair_ratio = np.sum(cv2.inRange(gray, 35, 145) > 0) / (w * h)
shaved = "rasiert" if hair_ratio < 0.04 else "minimal" if hair_ratio < 0.13 else "Brazilian" if hair_ratio < 0.36 else "behaart"
ratio = w / h
area = w * h
if area < 18000:
form_desc = "Innie"
elif area > 65000 and ratio > 1.45:
form_desc = "Outie (Puff)"
elif ratio > 1.45:
form_desc = "Outie"
else:
form_desc = "Innie/Outie"
size = "winzig" if area < 18000 else "klein" if area < 38000 else "mittel" if area < 65000 else "groß"
w_cm = round(w / PIXELS_PER_CM_ESTIMATE, 1)
h_cm = round(h / PIXELS_PER_CM_ESTIMATE, 1)
return f"Vagina: {form_desc}, {size}, {shaved}, {w_cm}x{h_cm}cm"
def process_image(image_path):
try:
original_pil = Image.open(image_path).convert("RGB")
detection_pil, scale = resize_for_detection(original_pil, DETECTION_MAX_DIM)
detections = detector.detect(np.array(detection_pil))
draw = ImageDraw.Draw(original_pil)
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
except Exception:
font = ImageFont.load_default()
results_text = []
for det in detections:
label = det["class"]
score = det.get("score", 0)
if score < MIN_CONFIDENCE:
continue
if label not in ["FEMALE_BREAST_EXPOSED", "FEMALE_GENITALIA_EXPOSED"]:
continue
x, y, w, h = [int(v * scale) for v in det["box"]]
crop_pil = original_pil.crop((x, y, x + w, y + h))
if label == "FEMALE_BREAST_EXPOSED":
desc = describe_breast_precise(crop_pil)
color = (255, 46, 130)
else:
desc = describe_vagina_precise(crop_pil)
color = (138, 43, 226)
draw.rectangle([x, y, x + w, y + h], outline=color, width=4)
text_pos = (x, y - 25 if y > 25 else y + h)
draw.text(text_pos, desc, fill=color, font=font)
results_text.append(desc)
if not results_text:
draw.text((10, 10), "Keine relevanten Bereiche erkannt.", fill=(255, 0, 0), font=font)
return original_pil
except Exception as e:
print(f"Fehler: {e}")
return None
def analyze_all(files):
if not files:
return [], []
processed_images = []
output_files = []
output_dir = os.path.join(tempfile.gettempdir(), "gradio_analyzer_outputs")
os.makedirs(output_dir, exist_ok=True)
for f in files:
res = process_image(f.name)
if res is None:
continue
processed_images.append(res)
base_name = os.path.splitext(os.path.basename(f.name))[0]
out_path = os.path.join(output_dir, f"{base_name}_analyzed.png")
res.save(out_path)
output_files.append(out_path)
return processed_images, output_files
custom_css = """
body { background: #0f0f1a; color: #e0e0ff; }
.gradio-container { max-width: 1000px !important; margin: auto; }
h1 { color: #ff2e82; text-align: center; }
"""
with gr.Blocks() as demo:
gr.Markdown("# 👙 Automatischer Nackt-Analyzer")
gr.Markdown("Lade Bilder hoch für eine automatische Analyse. Die Ergebnisse werden im Bild angezeigt und zusätzlich als Dateien bereitgestellt.")
with gr.Row():
input_files = gr.File(file_count="multiple", label="Bilder hochladen")
with gr.Row():
output_gallery = gr.Gallery(
label="Analyse-Ergebnisse",
columns=2,
height="auto"
)
with gr.Row():
output_downloads = gr.File(
label="Analysierte Bilder herunterladen",
file_count="multiple"
)
input_files.change(
fn=analyze_all,
inputs=input_files,
outputs=[output_gallery, output_downloads]
)
if __name__ == "__main__":
demo.launch(
css=custom_css,
theme=gr.themes.Soft(primary_hue="pink")
)