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)