File size: 4,646 Bytes
b2f0d83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import cv2
from nudenet import NudeDetector

# --- Konstanten ---
DETECTION_MAX_DIM = 768
PIXELS_PER_CM_ESTIMATE = 15
MIN_CONFIDENCE = 0.45

# --- NudeNet Detector ---
detector = NudeDetector(inference_resolution=640)

# --- 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)
    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)  # <--- korrigiert
    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"

# --- Bildverarbeitung ---
def process_image(image):
    try:
        original_pil = Image.fromarray(image).convert("RGB") if isinstance(image,np.ndarray) else image.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)
        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-15 if y>15 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 np.array(original_pil)

    except Exception as e:
        print(f"Fehler: {e}")
        return None

# --- Gradio App ---
css = """
body { background: #0f0f1a; color: #e0e0ff; }
.gradio-container { max-width: 900px !important; margin: auto; }
h1 { color: #ff2e82; text-align: center; }
"""

with gr.Blocks(css=css) as demo:
    gr.Markdown("# 👙 Automatischer Nackt-Analyzer")
    gr.Markdown("Lade ein Bild hoch und erhalte direkt das analysierte Bild mit Annotationen.")
    input_image = gr.Image(type="numpy", label="Bild hochladen")
    output_image = gr.Image(label="Analyse-Ergebnis")
    input_image.change(fn=process_image, inputs=input_image, outputs=output_image)

demo.launch()