ddd / app.py
drdudddd's picture
Create app.py
27ab04b verified
Raw History Blame Contribute Delete
5.5 kB
from PIL import Image
import numpy as np
import gradio as gr
import os
import cv2
from nudenet import NudeDetector
import io
import concurrent.futures
# ─── Konstanten ────────────────────────────────────────
DETECTION_MAX_DIM = 768
PIXELS_PER_CM_ESTIMATE = 15
MIN_CONFIDENCE = 0.45
# ─── Hilfsfunktionen ───────────────────────────────────
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)
return resized, 1 / ratio
# Deine bestehenden describe-Funktionen (unverändert)
def describe_breast_precise(crop_pil):
# ... dein Code bleibt gleich ...
return "Form: Rund · Größe: mittel · Nippel: Sichtbar · 9.8×8.4 cm" # Beispiel
def describe_vagina_precise(crop_pil):
# ... dein Code bleibt gleich ...
return "Form: Klassisches Outie · Größe: mittel · Prominenz: sichtbar · Behaart: minimal · 8.2×11.1 cm"
detector = NudeDetector(inference_resolution=640)
def process_single_image(mode: str, path: str):
try:
original_pil = Image.open(path).convert("RGB")
detection_pil, scale = resize_for_detection(original_pil, DETECTION_MAX_DIM)
detections = detector.detect(np.array(detection_pil))
target_class = "FEMALE_BREAST_EXPOSED" if mode == "Brüste" else "FEMALE_GENITALIA_EXPOSED"
relevant = [d for d in detections if d["class"] == target_class and d.get("score", 0) >= MIN_CONFIDENCE]
filename = os.path.basename(path)
markdown = f"**{filename}** — {mode}\n\n"
outputs = []
if not relevant:
markdown += "❌ Keine relevanten Bereiche erkannt.\n"
outputs.append((None, markdown))
return outputs
markdown += f"✅ **{len(relevant)}** {mode.lower()} gefunden\n\n"
for i, det in enumerate(relevant, 1):
x, y, w, h = [int(v * scale) for v in det["box"]]
crop_pil = original_pil.crop((x, y, x + w, y + h))
desc = describe_breast_precise(crop_pil) if mode == "Brüste" else describe_vagina_precise(crop_pil)
# Crop als bytes für Gradio Image-Komponente
crop_bytes = io.BytesIO()
crop_pil.save(crop_bytes, format="PNG")
crop_bytes.seek(0)
markdown_part = f"**{mode} {i}** (Konfidenz: {det['score']:.2f})\n{desc}\n"
outputs.append((crop_bytes, markdown_part))
# Zusammenfassung am Ende
summary_md = "\n".join([md for _, md in outputs]) + f"\n\n**Gesamt: {len(relevant)} Funde**"
outputs.append((None, summary_md))
return outputs
except Exception as e:
return [(None, f"**{filename}** — Fehler: {str(e)}")]
def analyze_images(mode: str, image_paths):
if not image_paths:
return [(None, "**Keine Bilder hochgeladen.**")]
all_outputs = []
max_workers = min(6, len(image_paths), os.cpu_count() or 4)
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(process_single_image, mode, p) for p in image_paths]
for future in concurrent.futures.as_completed(futures):
all_outputs.extend(future.result())
return all_outputs
# ─── Gradio Interface ──────────────────────────────────
with gr.Blocks(theme=gr.themes.Soft(primary_hue="pink", secondary_hue="purple")) as demo:
gr.Markdown("# Nackt-Analyzer – mit Crops")
gr.Markdown("Lädt Bilder → erkennt Brüste / Vulva → zeigt **Text + Crop-Bilder**")
with gr.Row():
mode = gr.Radio(choices=["Brüste", "Vagina"], value="Brüste", label="Modus")
upload = gr.File(file_count="multiple", file_types=["image"], label="Bilder hochladen")
analyze_btn = gr.Button("Analysieren", variant="primary")
output_gallery = gr.Gallery(
label="Ergebnisse (Crops + Beschreibung)",
columns=3,
height="auto",
object_fit="contain",
show_label=True,
elem_id="result-gallery"
)
markdown_output = gr.Markdown(label="Zusammenfassung / Details")
def on_analyze(mode, files):
if not files:
return [], "**Keine Dateien ausgewählt.**"
paths = [f.name for f in files] if hasattr(files[0], 'name') else files
results = analyze_images(mode, paths)
images = []
md_parts = []
for img_bytes, text in results:
if img_bytes is not None:
images.append((img_bytes, text)) # (image, caption)
else:
md_parts.append(text)
combined_md = "\n\n".join(md_parts) if md_parts else ""
return images, combined_md
analyze_btn.click(
fn=on_analyze,
inputs=[mode, upload],
outputs=[output_gallery, markdown_output]
)
if __name__ == "__main__":
demo.launch(share=True)