import numpy as np
import cv2
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import os
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class GIBleedingDetector:
def __init__(self):
self.original_image = None
self.processed_image = None
self.mask = None
self.bleeding_percentage = 0
self.bleeding_status = "No image analyzed"
def load_image(self, image_path):
"""Load and prepare the image for processing"""
self.original_image = cv2.imread(image_path)
if self.original_image is None:
return False
return True
def process_image(self):
"""Process the image to detect GI bleeding"""
if self.original_image is None:
return False
# Convert to RGB for better color analysis
image_rgb = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB)
# Resize image for faster processing if needed
resized = cv2.resize(image_rgb, (0, 0), fx=0.5, fy=0.5) if image_rgb.shape[0] > 1000 else image_rgb
# Reshape the image for K-means
pixels = resized.reshape(-1, 3).astype(np.float32)
# Define criteria and apply K-means
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
k = 5 # Number of clusters
_, labels, centers = cv2.kmeans(pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Convert back to uint8
centers = np.uint8(centers)
segmented_image = centers[labels.flatten()]
segmented_image = segmented_image.reshape(resized.shape)
# Detect red regions (potential bleeding)
# Convert to HSV for better color segmentation
hsv_img = cv2.cvtColor(segmented_image, cv2.COLOR_RGB2HSV)
# Define range for red color in HSV
lower_red1 = np.array([0, 120, 70])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70])
upper_red2 = np.array([180, 255, 255])
# Create masks for red regions
mask1 = cv2.inRange(hsv_img, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv_img, lower_red2, upper_red2)
# Combine masks
self.mask = cv2.bitwise_or(mask1, mask2)
# Calculate bleeding percentage
total_pixels = self.mask.size
bleeding_pixels = cv2.countNonZero(self.mask)
self.bleeding_percentage = (bleeding_pixels / total_pixels) * 100
# Determine bleeding status
if self.bleeding_percentage > 5:
self.bleeding_status = "High probability of bleeding"
elif self.bleeding_percentage > 1:
self.bleeding_status = "Moderate probability of bleeding"
else:
self.bleeding_status = "Low probability of bleeding"
# Create visual result
self.processed_image = cv2.bitwise_and(segmented_image, segmented_image, mask=self.mask)
# Resize the mask to match the original image size if needed
if resized.shape != image_rgb.shape:
self.mask = cv2.resize(self.mask, (image_rgb.shape[1], image_rgb.shape[0]))
self.processed_image = cv2.resize(self.processed_image, (image_rgb.shape[1], image_rgb.shape[0]))
return True
def get_overlay_image(self):
"""Create an overlay of original image with bleeding highlighted"""
if self.original_image is None or self.mask is None:
return None
# Convert original image to RGB
original_rgb = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB)
# Create a mask with same dimensions as original
mask_resized = cv2.resize(self.mask, (original_rgb.shape[1], original_rgb.shape[0]))
# Create overlay
overlay = original_rgb.copy()
overlay[mask_resized > 0] = [255, 0, 0] # Mark bleeding areas in red
# Create blended image - 70% original, 30% overlay
blended = cv2.addWeighted(original_rgb, 0.7, overlay, 0.3, 0)
return blended
def get_analysis_result(self):
"""Return analysis results as a dictionary"""
return {
"bleeding_percentage": round(self.bleeding_percentage, 2),
"bleeding_status": self.bleeding_status
}
class GIBleedingDetectionApp:
def __init__(self, root):
self.root = root
self.root.title("GI Bleeding Detection Tool")
self.root.geometry("1200x800")
self.root.configure(bg="#f0f0f0")
self.detector = GIBleedingDetector()
self.create_widgets()
def create_widgets(self):
# Top frame for buttons
self.top_frame = tk.Frame(self.root, bg="#f0f0f0")
self.top_frame.pack(pady=10, fill=tk.X)
# Load button
self.load_btn = tk.Button(self.top_frame, text="Load Image",
command=self.load_image,
bg="#4CAF50", fg="white",
font=("Arial", 12), padx=10, pady=5)
self.load_btn.pack(side=tk.LEFT, padx=10)
# Analyze button
self.analyze_btn = tk.Button(self.top_frame, text="Analyze Image",
command=self.analyze_image,
bg="#2196F3", fg="white",
font=("Arial", 12), padx=10, pady=5,
state=tk.DISABLED)
self.analyze_btn.pack(side=tk.LEFT, padx=10)
# Save button
self.save_btn = tk.Button(self.top_frame, text="Save Results",
command=self.save_results,
bg="#FFC107", fg="white",
font=("Arial", 12), padx=10, pady=5,
state=tk.DISABLED)
self.save_btn.pack(side=tk.LEFT, padx=10)
# Main content frame
self.content_frame = tk.Frame(self.root, bg="#f0f0f0")
self.content_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# Original image frame
self.original_frame = tk.LabelFrame(self.content_frame, text="Original Image",
bg="#f0f0f0", font=("Arial", 12))
self.original_frame.grid(row=0, column=0, padx=10, pady=10, sticky=tk.NSEW)
self.original_canvas = tk.Label(self.original_frame, bg="black")
self.original_canvas.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Processed image frame
self.processed_frame = tk.LabelFrame(self.content_frame, text="Bleeding Detection Result",
bg="#f0f0f0", font=("Arial", 12))
self.processed_frame.grid(row=0, column=1, padx=10, pady=10, sticky=tk.NSEW)
self.processed_canvas = tk.Label(self.processed_frame, bg="black")
self.processed_canvas.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Results frame
self.results_frame = tk.LabelFrame(self.content_frame, text="Analysis Results",
bg="#f0f0f0", font=("Arial", 12))
self.results_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky=tk.NSEW)
# Results display
self.results_text = tk.Label(self.results_frame, text="No image analyzed",
font=("Arial", 14), bg="#f0f0f0", justify=tk.LEFT)
self.results_text.pack(side=tk.LEFT, padx=20, pady=20)
# Graph frame for visualization
self.graph_frame = tk.Frame(self.results_frame, bg="#f0f0f0")
self.graph_frame.pack(side=tk.RIGHT, padx=20, pady=20, fill=tk.BOTH, expand=True)
# Configure the grid
self.content_frame.grid_columnconfigure(0, weight=1)
self.content_frame.grid_columnconfigure(1, weight=1)
self.content_frame.grid_rowconfigure(0, weight=3)
self.content_frame.grid_rowconfigure(1, weight=1)
# Status bar
self.status_bar = tk.Label(self.root, text="Ready", bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def load_image(self):
file_path = filedialog.askopenfilename(
title="Select Image",
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")])
if not file_path:
return
self.status_bar.config(text=f"Loading image: {file_path}")
if self.detector.load_image(file_path):
# Convert for display
img = cv2.cvtColor(self.detector.original_image, cv2.COLOR_BGR2RGB)
img = self.resize_for_display(img, 500)
# Display image
self.tk_img = ImageTk.PhotoImage(Image.fromarray(img))
self.original_canvas.config(image=self.tk_img)
# Update UI
self.analyze_btn.config(state=tk.NORMAL)
self.results_text.config(text="Image loaded. Click 'Analyze Image' to detect bleeding.")
self.status_bar.config(text=f"Image loaded: {os.path.basename(file_path)}")
else:
messagebox.showerror("Error", "Could not load the image. Please try again with a different file.")
self.status_bar.config(text="Error loading image")
def analyze_image(self):
if self.detector.original_image is None:
messagebox.showinfo("Info", "Please load an image first.")
return
self.status_bar.config(text="Analyzing image for GI bleeding...")
# Process the image
if self.detector.process_image():
# Get the overlay image with bleeding highlighted
overlay_img = self.detector.get_overlay_image()
overlay_img = self.resize_for_display(overlay_img, 500)
# Display processed image
self.processed_tk_img = ImageTk.PhotoImage(Image.fromarray(overlay_img))
self.processed_canvas.config(image=self.processed_tk_img)
# Get analysis results
results = self.detector.get_analysis_result()
# Update results text
result_text = f"Bleeding Detection Analysis:\n\n"
result_text += f"Bleeding Area: {results['bleeding_percentage']}% of image\n"
result_text += f"Assessment: {results['bleeding_status']}"
self.results_text.config(text=result_text)
# Create visualization graph
self.create_results_graph(results['bleeding_percentage'])
# Update UI
self.save_btn.config(state=tk.NORMAL)
self.status_bar.config(text="Analysis complete")
else:
messagebox.showerror("Error", "Could not analyze the image. Please try again.")
self.status_bar.config(text="Error during analysis")
def create_results_graph(self, bleeding_percentage):
# Clear previous graph
for widget in self.graph_frame.winfo_children():
widget.destroy()
# Create figure and axis
fig, ax = plt.subplots(figsize=(4, 3))
# Data for the gauge chart
categories = ['Healthy', 'Bleeding']
values = [100 - bleeding_percentage, bleeding_percentage]
colors = ['#4CAF50', '#F44336']
# Create the pie chart
ax.pie(values, labels=categories, colors=colors, autopct='%1.1f%%',
startangle=90, wedgeprops={'edgecolor': 'white', 'linewidth': 1})
ax.set_title("Tissue Analysis", fontsize=12)
# Add to tkinter window
canvas = FigureCanvasTkAgg(fig, self.graph_frame)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
def resize_for_display(self, image, max_dim):
"""Resize image while maintaining aspect ratio"""
h, w = image.shape[:2]
if h > w:
new_h = max_dim
new_w = int(w * (max_dim / h))
else:
new_w = max_dim
new_h = int(h * (max_dim / w))
return cv2.resize(image, (new_w, new_h))
def save_results(self):
if self.detector.processed_image is None:
messagebox.showinfo("Info", "Please analyze an image first.")
return
# Ask for directory to save results
save_dir = filedialog.askdirectory(title="Select Directory to Save Results")
if not save_dir:
return
try:
# Save the overlay image
overlay_img = self.detector.get_overlay_image()
cv2.imwrite(os.path.join(save_dir, "bleeding_detection_result.jpg"),
cv2.cvtColor(overlay_img, cv2.COLOR_RGB2BGR))
# Save the analysis results as text
results = self.detector.get_analysis_result()
with open(os.path.join(save_dir, "analysis_results.txt"), "w") as f:
f.write(f"GI Bleeding Detection Analysis Results\n")
f.write(f"=====================================\n\n")
f.write(f"Bleeding Area: {results['bleeding_percentage']}% of image\n")
f.write(f"Assessment: {results['bleeding_status']}\n")
f.write(f"\nAnalysis performed on {os.path.basename(save_dir)}\n")
messagebox.showinfo("Success", f"Results saved to {save_dir}")
self.status_bar.config(text=f"Results saved to {save_dir}")
except Exception as e:
messagebox.showerror("Error", f"Could not save results: {str(e)}")
self.status_bar.config(text="Error saving results")
if __name__ == "__main__":
# Create main window
root = tk.Tk()
app = GIBleedingDetectionApp(root)
# Run the application
root.mainloop()