File size: 4,956 Bytes
af3260e
 
 
d797d24
 
af3260e
3bb9819
af3260e
 
 
 
 
 
b1b16f3
af3260e
 
d797d24
b1b16f3
 
 
 
 
 
 
 
 
 
d797d24
af3260e
 
 
 
 
 
d797d24
3bb9819
d797d24
 
 
 
 
 
 
 
 
3bb9819
b1b16f3
d797d24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af3260e
 
d797d24
3bb9819
b1b16f3
ffc503e
d797d24
af3260e
 
 
 
3bb9819
d797d24
af3260e
 
3bb9819
b1b16f3
d797d24
 
b1b16f3
 
d797d24
b1b16f3
 
 
3bb9819
d797d24
b1b16f3
 
ffc503e
af3260e
d797d24
ffc503e
b1b16f3
af3260e
 
d797d24
b1b16f3
 
 
 
d797d24
b1b16f3
d797d24
 
b1b16f3
af3260e
 
 
 
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
import gradio as gr
import tensorflow as tf
import numpy as np
from PIL import Image, ImageStat
import cv2

# 🧠 Load your TFLite model
interpreter = tf.lite.Interpreter(model_path="stool_model.tflite")
interpreter.allocate_tensors()

input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# 🏷️ Labels of your stool model
labels = ["bloody", "hard stool", "normal", "parasite", "watery"]

# 💬 Diagnosis summaries
diagnosis_advice = {
    "bloody": "Possible hemorrhagic gastroenteritis. ⚠️ Visit a vet immediately as it may indicate internal bleeding or infection.",
    "hard stool": "Your pet may be constipated. 💧 Encourage hydration and increase dietary fiber intake.",
    "normal": "✅ Healthy stool detected. Your pet appears to be in good digestive health.",
    "parasite": "⚠️ Possible tapeworm or other parasite infection. Schedule deworming and a vet check-up.",
    "watery": "Possible diarrhea. 💧 Ensure hydration and monitor for dehydration or weakness.",
    "Not stool image": "❌ This image doesn’t appear to be a stool sample. Please upload a clearer photo of stool.",
    "Uncertain / unclear stool image": "⚠️ The image is unclear or confidence is low. Try retaking the photo in better lighting."
}

# 🧩 Image preprocessing for the TFLite model
def preprocess_image(img: Image.Image):
    img = img.convert("RGB").resize((128, 128))
    arr = np.asarray(img).astype(np.float32) / 255.0
    arr = np.expand_dims(arr, axis=0)
    return arr

# 🚫 Strict non-stool detection
def is_not_stool_image(image):
    """Multi-layer detection for non-stool photos"""
    np_img = np.array(image.convert("RGB"))
    stat = ImageStat.Stat(image)

    # 🧠 1️⃣ Brightness & contrast checks
    brightness = stat.mean[0]
    contrast = stat.stddev[0]
    if brightness > 210 or brightness < 30:
        return True  # too bright or dark
    if contrast < 25:
        return True  # too flat / no texture

    # 🧠 2️⃣ Dominant color detection (stool tends to be brownish)
    avg_color = np.mean(np_img.reshape(-1, 3), axis=0)
    r, g, b = avg_color

    # Detect unnatural colors (too blue, green, or red)
    if (r > 200 and g < 100 and b < 100) or (g > 180 and r < 100) or (b > 180):
        return True

    # 🧠 3️⃣ Color tone validation — real stool is usually brown (RGB ratios)
    brownish_score = (r * 0.5 + g * 0.3 + b * 0.2)
    grayish_score = abs(r - g) + abs(g - b)
    if brownish_score < 60 or grayish_score > 80:
        return True

    # 🧠 4️⃣ Texture detection with OpenCV
    gray = cv2.cvtColor(np_img, cv2.COLOR_RGB2GRAY)
    lap_var = cv2.Laplacian(gray, cv2.CV_64F).var()  # blur measure
    if lap_var < 50:
        return True  # image too smooth or blurry (likely not stool)

    return False  # passes all checks, likely stool

# 🧠 Main classifier
def classify_image(image):
    try:
        # 🧩 Step 1: Reject non-stool images first
        if is_not_stool_image(image):
            return {"Not stool image": 1.0}, diagnosis_advice["Not stool image"]

        # 🧩 Step 2: Run TFLite model
        input_data = preprocess_image(image)
        interpreter.set_tensor(input_details[0]['index'], input_data)
        interpreter.invoke()
        output_data = interpreter.get_tensor(output_details[0]['index'])[0]

        # 🧩 Step 3: Sort predictions
        results = {labels[i]: float(output_data[i]) for i in range(len(labels))}
        sorted_results = dict(sorted(results.items(), key=lambda x: x[1], reverse=True))
        top_label, top_score = list(sorted_results.items())[0]

        # 🧩 Step 4: Handle low-confidence predictions
        if top_score < 0.45:
            return {"Uncertain / unclear stool image": top_score}, diagnosis_advice["Uncertain / unclear stool image"]

        # 🧩 Step 5: Add readable confidence %
        formatted_results = {
            f"{label} ({score * 100:.2f}%)": score for label, score in sorted_results.items()
        }

        # 🩺 Step 6: Generate human-readable advice
        advice = diagnosis_advice.get(top_label, "No advice available for this diagnosis.")
        return formatted_results, advice

    except Exception as e:
        return {"Error": 0.0}, f"⚠️ Error: {str(e)}"

# 🎨 Gradio Interface
demo = gr.Interface(
    fn=classify_image,
    inputs=gr.Image(type="pil", label="📸 Upload Stool Image"),
    outputs=[
        gr.Label(num_top_classes=3, label="Predicted Diagnosis & Confidence"),
        gr.Textbox(label="💬 Diagnosis Summary", lines=3),
    ],
    title="🐾 Pet Stool Diagnosis AI (Strict Validation)",
    description=(
        "Upload a stool image for AI-based diagnosis. The system will first verify if it's a real stool photo, "
        "then predict the stool type and give a simple health summary."
    ),
)

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=7860)