benedictpepper/Brain-Tumor-Segmentation
0
1import os2import numpy as np3import cv24import skfuzzy as fuzz5from sklearn.metrics import silhouette_score, davies_bouldin_score6import streamlit as st7from PIL import Image8import io9 10 11st.set_page_config(12 page_title="Brain Tumor Segmentation",13 page_icon="๐ง ",14 layout="wide"15)16 17 18class FCMSegmenter:19 """Fuzzy C-Means based MRI tumor segmentation with optional texture features."""20 21 def __init__(self, n_clusters=4, fuzziness=2.0, max_iter=1000, error=0.005, use_texture=False):22 self.n_clusters = n_clusters23 self.fuzziness = fuzziness24 self.max_iter = max_iter25 self.error = error26 self.use_texture = use_texture27 self.cntr = None28 self.u = None29 self.fpc = None30 self.iterations = None31 self.objective_value = None32 33 def load_image_array(self, img_rgb):34 """Preprocess numpy RGB image array for segmentation."""35 img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) if len(img_rgb.shape) > 2 else img_rgb36 original_shape = img_gray.shape37 38 39 pixel_intensity = img_gray.flatten().astype(np.float32)40 41 if self.use_texture:42 43 local_mean = cv2.blur(img_gray, (5, 5)).flatten().astype(np.float32)44 sq_img = img_gray.astype(np.float32) ** 245 local_sq_mean = cv2.blur(sq_img, (5, 5)).flatten()46 local_std = np.sqrt(np.maximum(local_sq_mean - local_mean**2, 0))47 48 49 pixel_data = np.vstack((pixel_intensity, local_mean, local_std))50 else:51 52 pixel_data = pixel_intensity.reshape((1, -1))53 54 return img_rgb, img_gray, pixel_data, original_shape55 56 def segment(self, pixel_data):57 """Apply Fuzzy C-Means clustering to pixel data."""58 cntr, u, _, _, jm, p, fpc = fuzz.cluster.cmeans(59 data=pixel_data,60 c=self.n_clusters,61 m=self.fuzziness,62 error=self.error,63 maxiter=self.max_iter,64 init=None65 )66 67 self.cntr = cntr68 self.u = u69 self.fpc = fpc70 self.iterations = p71 self.objective_value = jm[-1]72 73 return np.argmax(u, axis=0)74 75 def create_segmented_image(self, cluster_labels, original_shape):76 """Convert cluster labels to color-coded segmented RGB image."""77 segmented_labels = cluster_labels.reshape(original_shape)78 segmented_color = np.zeros((original_shape[0], original_shape[1], 3), dtype=np.uint8)79 80 81 intensity_centers = self.cntr[:, 0]82 sorted_centers = np.argsort(intensity_centers)83 84 85 color_map = {86 sorted_centers[0]: [0, 0, 0], # Background (Black)87 sorted_centers[1]: [85, 85, 85], # Healthy tissue (Gray)88 sorted_centers[2]: [0, 255, 0], # Fluid/Edema (Green)89 sorted_centers[3] if self.n_clusters > 3 else -1: [255, 0, 0] # Tumor (Red)90 }91 92 for i in range(self.n_clusters):93 mask = segmented_labels == i94 color = color_map.get(i, [255, 255, 255])95 segmented_color[mask] = color96 97 return segmented_labels, segmented_color98 99 def evaluate(self, pixel_data, cluster_labels):100 """Compute clustering quality metrics safely without freezing."""101 labels_flat = cluster_labels.flatten()102 data_t = pixel_data.T 103 104 metrics = {105 'fpc': self.fpc,106 'iterations': self.iterations,107 'objective_value': self.objective_value108 }109 110 if len(np.unique(labels_flat)) > 1:111 try:112 113 sample_size = min(10000, len(labels_flat))114 indices = np.random.choice(len(labels_flat), sample_size, replace=False)115 116 metrics['silhouette'] = silhouette_score(data_t[indices], labels_flat[indices])117 metrics['davies_bouldin'] = davies_bouldin_score(data_t[indices], labels_flat[indices])118 except Exception as e:119 st.warning(f"Detailed metric calculation skipped: {e}")120 121 return metrics122 123 def process(self, img_rgb):124 """Full segmentation pipeline."""125 img_rgb, img_gray, pixel_data, original_shape = self.load_image_array(img_rgb)126 cluster_labels = self.segment(pixel_data)127 segmented_labels, segmented_color = self.create_segmented_image(cluster_labels, original_shape)128 metrics = self.evaluate(pixel_data, cluster_labels)129 130 return {131 'original': img_rgb,132 'gray': img_gray,133 'segmented_labels': segmented_labels,134 'segmented_color': segmented_color,135 'metrics': metrics136 }137 138 139def main():140 st.title("๐ง Brain Tumor Segmentation using FCM")141 st.markdown("Upload an MRI scan to automatically segment and highlight potential tumor regions using the **Fuzzy C-Means** algorithm.")142 143 st.sidebar.header("โ๏ธ Advanced Configuration")144 n_clusters = st.sidebar.slider("Number of Clusters (Tissue Types)", min_value=2, max_value=8, value=4, step=1)145 fuzziness = st.sidebar.slider("Fuzziness Parameter (m)", min_value=1.1, max_value=5.0, value=2.0, step=0.1)146 use_texture = st.sidebar.checkbox("Use Texture Features (Local Mean & Variance)", value=False)147 148 st.sidebar.markdown("---")149 150 with st.sidebar.expander("โน๏ธ About this Program", expanded=False):151 st.markdown("""152 **Developer:** Benedict Pepper153 154 **About this Program:**155 This is an automated medical image analysis tool that applies the Fuzzy C-Means (FCM) soft clustering algorithm to segment brain MRI scans. By clustering pixel data into distinct tissue groups, this tool helps isolate potential tumor regions from healthy tissue, fluids, and background.156 157 **What is Fuzzy C-Means (FCM)?**158 Unlike traditional K-Means clustering where each pixel belongs strictly to ONE group, FCM allows a pixel to have a "degree of belonging" (membership) to multiple groups at the same time. This is particularly useful in medical imaging because tissue boundaries are often blurred and overlap (the partial volume effect).159 160 **The Objective Function (Formula):**161 The algorithm minimizes:162 $J_m = \sum_{i=1}^{N} \sum_{j=1}^{C} (u_{ij}^m) \cdot ||x_i - c_j||^2$163 164 Where:165 * $N$: total number of pixels.166 * $C$: total number of clusters.167 * $u_{ij}$: degree of membership of pixel $x_i$ in cluster $j$.168 * $m$: Fuzziness parameter.169 * $c_j$: center of the cluster.170 """)171 172 # Main area execution - User Input173 st.markdown("### 1. Upload MRI Scan")174 uploaded_file = st.file_uploader("Choose an MRI image file", type=["jpg", "jpeg", "png", "bmp", "tif"])175 176 if uploaded_file is not None:177 178 file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)179 img_bgr = cv2.imdecode(file_bytes, 1)180 img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)181 182 st.markdown("### 2. Run Segmentation")183 run_btn = st.button("๐ Run Segmentation", type="primary", use_container_width=True)184 185 186 col1, col2 = st.columns(2)187 with col1:188 st.image(img_rgb, caption="Original MRI Image", use_container_width=True)189 190 with col2:191 if not run_btn:192 st.info("๐ Click **Run Segmentation** above to process this image. (You can adjust advanced settings in the sidebar first if desired)")193 else:194 with st.spinner("Processing... Applying Fuzzy C-Means (This might take a few seconds)"):195 segmenter = FCMSegmenter(196 n_clusters=n_clusters, 197 fuzziness=fuzziness, 198 use_texture=use_texture199 )200 results = segmenter.process(img_rgb)201 202 st.image(results['segmented_color'], caption="Segmented Output", use_container_width=True)203 204 205 if run_btn:206 st.markdown("---")207 st.markdown("### ๐ Evaluation Metrics")208 m = results['metrics']209 210 metric_cols = st.columns(4)211 metric_cols[0].metric("FPC Score", f"{m.get('fpc', 0):.4f}", help="Fuzzy Partition Coefficient (closer to 1.0 is better)")212 metric_cols[1].metric("Iterations", f"{m.get('iterations', 0)}", help="Cycles taken to converge")213 metric_cols[2].metric("Objective Val", f"{m.get('objective_value', 0):.2e}")214 if 'silhouette' in m:215 metric_cols[3].metric("Silhouette Score", f"{m['silhouette']:.4f}", help="Cluster separation quality (closer to 1.0 is better)")216 217 st.markdown("### ๐พ Export Results")218 219 220 seg_img_pil = Image.fromarray(results['segmented_color'])221 buf = io.BytesIO()222 seg_img_pil.save(buf, format="PNG")223 byte_im = buf.getvalue()224 225 st.download_button(226 label="Download Segmented Image",227 data=byte_im,228 file_name="segmented_tumor.png",229 mime="image/png"230 )231 else:232 st.info("๐ Please upload an MRI scan to get started.")233 234if __name__ == '__main__':235 main()