Diff of /gi-bleeding-detection.py [000000] .. [aa8880]

Switch to side-by-side view

--- a
+++ b/gi-bleeding-detection.py
@@ -0,0 +1,337 @@
+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()