{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "0e5d69ea", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:42.493644Z", "iopub.status.busy": "2024-11-20T11:00:42.493065Z", "iopub.status.idle": "2024-11-20T11:00:43.552823Z", "shell.execute_reply": "2024-11-20T11:00:43.551225Z" }, "papermill": { "duration": 1.070286, "end_time": "2024-11-20T11:00:43.556805", "exception": false, "start_time": "2024-11-20T11:00:42.486519", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "# Import necessary libraries\n", "import os\n", "import numpy as np\n", "from PIL import Image\n", "from skimage.color import rgb2gray\n", "from skimage.filters import sobel\n", "import plotly.graph_objects as go\n", "from ipywidgets import interact, IntRangeSlider\n", "import logging\n", "\n", "# Configure logging\n", "logging.basicConfig(level=logging.INFO, format=\"%(asctime)s - %(levelname)s - %(message)s\")\n", "logger = logging.getLogger(__name__)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "d3dc0ba9", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:43.568054Z", "iopub.status.busy": "2024-11-20T11:00:43.567521Z", "iopub.status.idle": "2024-11-20T11:00:43.577333Z", "shell.execute_reply": "2024-11-20T11:00:43.576040Z" }, "papermill": { "duration": 0.01954, "end_time": "2024-11-20T11:00:43.580568", "exception": false, "start_time": "2024-11-20T11:00:43.561028", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def load_data_with_masks(folder_path):\n", " \"\"\"\n", " Load MRI slices and corresponding masks from the specified folder.\n", "\n", " Parameters:\n", " folder_path (str): Path to folder containing slices and masks.\n", "\n", " Returns:\n", " list: List of tuples (slice_array, mask_array).\n", " \"\"\"\n", " logger.info(\"Loading data from folder...\")\n", " data = []\n", " files = sorted(os.listdir(folder_path))\n", " for file in files:\n", " if file.endswith(\".tif\") and not file.endswith(\"_mask.tif\"):\n", " slice_path = os.path.join(folder_path, file)\n", " mask_path = os.path.join(folder_path, file.replace(\".tif\", \"_mask.tif\"))\n", " if os.path.exists(mask_path):\n", " slice_img = Image.open(slice_path)\n", " mask_img = Image.open(mask_path)\n", " data.append((np.array(slice_img), np.array(mask_img)))\n", " else:\n", " logger.warning(f\"Mask file missing for slice: {file}\")\n", " logger.info(f\"Loaded {len(data)} slice-mask pairs.\")\n", " return data\n" ] }, { "cell_type": "code", "execution_count": null, "id": "b2e5c6f4", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:43.589883Z", "iopub.status.busy": "2024-11-20T11:00:43.589474Z", "iopub.status.idle": "2024-11-20T11:00:43.599897Z", "shell.execute_reply": "2024-11-20T11:00:43.597593Z" }, "papermill": { "duration": 0.019714, "end_time": "2024-11-20T11:00:43.604032", "exception": false, "start_time": "2024-11-20T11:00:43.584318", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def preprocess_data(data, target_shape=(256, 256)):\n", " \"\"\"\n", " Preprocess slice-mask pairs: normalize and pad to a uniform size.\n", "\n", " Parameters:\n", " data (list): List of tuples (slice, mask).\n", " target_shape (tuple): Target shape for all slices and masks.\n", "\n", " Returns:\n", " list: List of preprocessed tuples (slice, mask).\n", " \"\"\"\n", " processed = []\n", " for i, (slice_img, mask_img) in enumerate(data):\n", " logger.info(f\"Preprocessing slice {i + 1}/{len(data)}\")\n", " if len(slice_img.shape) == 3: # Handle RGB images\n", " slice_img = slice_img[:, :, :3] # Ensure only 3 channels\n", " \n", " slice_img = np.pad(slice_img, ((0, target_shape[0] - slice_img.shape[0]),\n", " (0, target_shape[1] - slice_img.shape[1]),\n", " (0, 0)), mode=\"constant\", constant_values=0)\n", " processed.append((slice_img, mask_img))\n", " logger.info(f\"Preprocessed {len(processed)} slices.\")\n", " return processed\n" ] }, { "cell_type": "code", "execution_count": null, "id": "8cb427d2", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:43.615574Z", "iopub.status.busy": "2024-11-20T11:00:43.615150Z", "iopub.status.idle": "2024-11-20T11:00:44.175043Z", "shell.execute_reply": "2024-11-20T11:00:44.173538Z" }, "papermill": { "duration": 0.569835, "end_time": "2024-11-20T11:00:44.178886", "exception": false, "start_time": "2024-11-20T11:00:43.609051", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "# Path to folder containing MRI slices and masks\n", "folder_path = \"/kaggle/input/lgg-mri-segmentation/kaggle_3m/TCGA_CS_4941_19960909\"\n", "\n", "# Load and preprocess the data\n", "slice_mask_pairs = load_data_with_masks(folder_path)\n", "processed_slice_mask_pairs = preprocess_data(slice_mask_pairs)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "de953d89", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.190080Z", "iopub.status.busy": "2024-11-20T11:00:44.189512Z", "iopub.status.idle": "2024-11-20T11:00:44.199697Z", "shell.execute_reply": "2024-11-20T11:00:44.198014Z" }, "papermill": { "duration": 0.019089, "end_time": "2024-11-20T11:00:44.202545", "exception": false, "start_time": "2024-11-20T11:00:44.183456", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def filter_with_sobel(slice_img):\n", " \"\"\"\n", " Apply Sobel filtering to remove white space from each RGB layer.\n", "\n", " Parameters:\n", " slice_img (np.ndarray): RGB slice image.\n", "\n", " Returns:\n", " np.ndarray: Filtered RGB slice.\n", " \"\"\"\n", " grayscale = rgb2gray(slice_img)\n", " sobel_mask = sobel(grayscale) > 0.1 # Binary mask (thresholded Sobel edges)\n", " \n", " # Apply the Sobel mask to each RGB layer\n", " filtered_img = np.zeros_like(slice_img)\n", " for channel in range(3): # Apply to R, G, and B layers\n", " filtered_img[:, :, channel] = slice_img[:, :, channel] * sobel_mask\n", "\n", " return filtered_img\n" ] }, { "cell_type": "code", "execution_count": null, "id": "382a8a64", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.214342Z", "iopub.status.busy": "2024-11-20T11:00:44.213279Z", "iopub.status.idle": "2024-11-20T11:00:44.220822Z", "shell.execute_reply": "2024-11-20T11:00:44.219570Z" }, "papermill": { "duration": 0.017025, "end_time": "2024-11-20T11:00:44.223468", "exception": false, "start_time": "2024-11-20T11:00:44.206443", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def apply_sobel_to_slices(data):\n", " \"\"\"\n", " Apply Sobel filtering to all RGB slices in the dataset.\n", "\n", " Parameters:\n", " data (list): Preprocessed slice-mask pairs.\n", "\n", " Returns:\n", " list: Filtered RGB slices.\n", " \"\"\"\n", " filtered_data = []\n", " for i, (slice_img, mask_img) in enumerate(data):\n", " logger.info(f\"Applying Sobel filtering to slice {i + 1}/{len(data)}\")\n", " filtered_img = filter_with_sobel(slice_img)\n", " filtered_data.append((filtered_img, mask_img))\n", " return filtered_data\n" ] }, { "cell_type": "code", "execution_count": null, "id": "3d16e7c9", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.235404Z", "iopub.status.busy": "2024-11-20T11:00:44.234949Z", "iopub.status.idle": "2024-11-20T11:00:44.244325Z", "shell.execute_reply": "2024-11-20T11:00:44.241890Z" }, "papermill": { "duration": 0.018919, "end_time": "2024-11-20T11:00:44.247232", "exception": false, "start_time": "2024-11-20T11:00:44.228313", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def create_cube4d(data):\n", " \"\"\"\n", " Create a Cube4D structure by stacking filtered RGB slices.\n", "\n", " Parameters:\n", " data (list): Filtered slice-mask pairs.\n", "\n", " Returns:\n", " np.ndarray: Cube4D matrix (z, height, width, channels).\n", " \"\"\"\n", " z_slices = len(data)\n", " height, width, channels = data[0][0].shape\n", " cube4d = np.zeros((z_slices, height, width, channels), dtype=np.uint8)\n", "\n", " for z, (slice_img, _) in enumerate(data):\n", " cube4d[z] = slice_img\n", "\n", " return cube4d\n" ] }, { "cell_type": "code", "execution_count": null, "id": "06671995", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.257093Z", "iopub.status.busy": "2024-11-20T11:00:44.256628Z", "iopub.status.idle": "2024-11-20T11:00:44.271850Z", "shell.execute_reply": "2024-11-20T11:00:44.268820Z" }, "papermill": { "duration": 0.02385, "end_time": "2024-11-20T11:00:44.275143", "exception": false, "start_time": "2024-11-20T11:00:44.251293", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "from ipywidgets import interact, IntRangeSlider\n", "\n", "def visualize_with_layer_range_filter_dynamic(rgb_slices, purple_threshold=(130, 80, 140)):\n", " \"\"\"\n", " Visualize the Cube4D structure dynamically with a range slider to control visible layers.\n", "\n", " Parameters:\n", " rgb_slices (list): List of processed RGB slices.\n", " purple_threshold (tuple): RGB threshold to identify and exclude \"purple\" areas (R, G, B values).\n", " \"\"\"\n", " def plot_layers(slice_range):\n", " \"\"\"\n", " Sub-function to dynamically plot layers within the specified range.\n", "\n", " Parameters:\n", " slice_range (tuple): Start and end indices for the slice range to display.\n", " \"\"\"\n", " fig = go.Figure()\n", " r_thresh, g_thresh, b_thresh = purple_threshold\n", "\n", " for z in range(slice_range[0], slice_range[1] + 1):\n", " rgb_image = rgb_slices[z]\n", "\n", " # Create a boolean mask for \"purple\" regions\n", " purple_mask = (\n", " (rgb_image[..., 0] >= r_thresh - 10) & (rgb_image[..., 0] <= r_thresh + 10) &\n", " (rgb_image[..., 1] >= g_thresh - 10) & (rgb_image[..., 1] <= g_thresh + 10) &\n", " (rgb_image[..., 2] >= b_thresh - 10) & (rgb_image[..., 2] <= b_thresh + 10)\n", " )\n", "\n", " # Replace purple areas with NaN\n", " filtered_image = np.mean(rgb_image, axis=2).astype(float)\n", " filtered_image[purple_mask] = np.nan\n", "\n", " # Add slice to the figure\n", " height, width = filtered_image.shape\n", " fig.add_trace(go.Surface(\n", " z=np.full((height, width), z),\n", " x=np.arange(width),\n", " y=np.arange(height),\n", " surfacecolor=filtered_image,\n", " colorscale=\"Viridis\",\n", " opacity=0.8,\n", " showscale=False\n", " ))\n", "\n", " # Customize layout\n", " fig.update_layout(\n", " title=f\"3D RGB Visualization (Slices {slice_range[0]}-{slice_range[1]})\",\n", " scene=dict(\n", " zaxis_title=\"Slices\",\n", " xaxis_title=\"Width\",\n", " yaxis_title=\"Height\"\n", " )\n", " )\n", " fig.show()\n", "\n", " # Interactive range slider for layer range\n", " interact(plot_layers, slice_range=IntRangeSlider(\n", " value=(0, len(rgb_slices) - 1), # Default range\n", " min=0,\n", " max=len(rgb_slices) - 1,\n", " step=1,\n", " continuous_update=True, # Enable live updates\n", " description=\"Layer Range\"\n", " ))\n" ] }, { "cell_type": "code", "execution_count": null, "id": "48b7a658", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.284603Z", "iopub.status.busy": "2024-11-20T11:00:44.284175Z", "iopub.status.idle": "2024-11-20T11:00:44.292718Z", "shell.execute_reply": "2024-11-20T11:00:44.290913Z" }, "papermill": { "duration": 0.016501, "end_time": "2024-11-20T11:00:44.295300", "exception": false, "start_time": "2024-11-20T11:00:44.278799", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def generate_composite_masks(rgb_slices, threshold=10):\n", " \"\"\"\n", " Generate composite masks for all slices to represent areas of content.\n", "\n", " Parameters:\n", " rgb_slices (list): List of RGB slices (H, W, 3).\n", " threshold (int): Minimum intensity to consider as content.\n", "\n", " Returns:\n", " list: Composite masks for each slice (H, W), where 0 = content and 1 = empty.\n", " \"\"\"\n", " composite_masks = []\n", " for i, rgb_image in enumerate(rgb_slices):\n", " logger.info(f\"Generating composite mask for slice {i + 1}/{len(rgb_slices)}\")\n", "\n", " # Convert RGB slice to grayscale intensity\n", " intensity = np.mean(rgb_image, axis=2)\n", "\n", " # Create a boolean mask (1 = no content, 0 = content)\n", " mask = (intensity < threshold).astype(np.uint8)\n", " composite_masks.append(mask)\n", "\n", " return composite_masks\n" ] }, { "cell_type": "code", "execution_count": null, "id": "0adc6c5d", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.305718Z", "iopub.status.busy": "2024-11-20T11:00:44.305324Z", "iopub.status.idle": "2024-11-20T11:00:44.312676Z", "shell.execute_reply": "2024-11-20T11:00:44.311494Z" }, "papermill": { "duration": 0.017587, "end_time": "2024-11-20T11:00:44.316592", "exception": false, "start_time": "2024-11-20T11:00:44.299005", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def overlay_composite_masks_with_rgb(rgb_slices, composite_masks, opacity=0.8):\n", " \"\"\"\n", " Overlay composite masks with RGB intensity for visualization.\n", "\n", " Parameters:\n", " rgb_slices (list): List of RGB slices (H, W, 3).\n", " composite_masks (list): List of composite masks (H, W).\n", " opacity (float): Default opacity for the brain material.\n", "\n", " Returns:\n", " list: Processed slices for visualization.\n", " \"\"\"\n", " processed_slices = []\n", " for i, (rgb_image, mask) in enumerate(zip(rgb_slices, composite_masks)):\n", " logger.info(f\"Overlaying mask on slice {i + 1}/{len(rgb_slices)}\")\n", "\n", " # Normalize RGB intensity to a range of [0, 1]\n", " intensity = np.mean(rgb_image, axis=2) / 255.0\n", "\n", " # Apply mask: Keep intensity only where mask allows (content regions)\n", " filtered_intensity = intensity * (1 - mask) # Invert mask (0 = content)\n", "\n", " # Scale intensity with opacity\n", " filtered_intensity *= opacity\n", " processed_slices.append(filtered_intensity)\n", "\n", " return processed_slices\n" ] }, { "cell_type": "code", "execution_count": null, "id": "12c31e0c", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.327149Z", "iopub.status.busy": "2024-11-20T11:00:44.326642Z", "iopub.status.idle": "2024-11-20T11:00:44.337833Z", "shell.execute_reply": "2024-11-20T11:00:44.336480Z" }, "papermill": { "duration": 0.019256, "end_time": "2024-11-20T11:00:44.340402", "exception": false, "start_time": "2024-11-20T11:00:44.321146", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "from ipywidgets import interact, FloatSlider\n", "\n", "def visualize_composite_masks(processed_slices):\n", " \"\"\"\n", " Visualize the composite mask overlay in 3D.\n", "\n", " Parameters:\n", " processed_slices (list): List of processed slices (H, W).\n", " \"\"\"\n", " def plot_layers(opacity):\n", " fig = go.Figure()\n", "\n", " for z, slice_image in enumerate(processed_slices):\n", " # Scale intensity by current opacity\n", " visual_intensity = slice_image * opacity\n", "\n", " # Add slice to the figure\n", " height, width = visual_intensity.shape\n", " fig.add_trace(go.Surface(\n", " z=np.full((height, width), z),\n", " x=np.arange(width),\n", " y=np.arange(height),\n", " surfacecolor=visual_intensity,\n", " colorscale=\"Greys\", # Black and white visualization\n", " opacity=opacity,\n", " showscale=False\n", " ))\n", "\n", " # Customize layout\n", " fig.update_layout(\n", " title=f\"3D Composite Mask Visualization (Opacity: {opacity:.2f})\",\n", " scene=dict(\n", " zaxis_title=\"Slices\",\n", " xaxis_title=\"Width\",\n", " yaxis_title=\"Height\"\n", " )\n", " )\n", " fig.show()\n", "\n", " # Interactive slider for opacity adjustment\n", " interact(plot_layers, opacity=FloatSlider(\n", " value=0.8, # Default opacity\n", " min=0.1,\n", " max=1.0,\n", " step=0.1,\n", " description=\"Opacity\"\n", " ))\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1bcb0b20", "metadata": { "execution": { "iopub.execute_input": "2024-11-20T11:00:44.351736Z", "iopub.status.busy": "2024-11-20T11:00:44.350766Z", "iopub.status.idle": "2024-11-20T11:00:45.656533Z", "shell.execute_reply": "2024-11-20T11:00:45.654476Z" }, "papermill": { "duration": 1.537387, "end_time": "2024-11-20T11:00:45.882282", "exception": false, "start_time": "2024-11-20T11:00:44.344895", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "def filter_purple(slice_img, purple_rgb=(131, 84, 141), threshold=50):\n", " \"\"\"\n", " Replace purple regions with NaN for transparency.\n", "\n", " Parameters:\n", " slice_img (np.ndarray): RGB slice image.\n", " purple_rgb (tuple): RGB values of the purple color to filter out.\n", " threshold (int): Allowable difference from the purple color.\n", "\n", " Returns:\n", " np.ndarray: RGB slice with purple regions replaced by NaN.\n", " \"\"\"\n", " # Calculate the Euclidean distance from the purple color\n", " distance = np.sqrt(\n", " (slice_img[..., 0] - purple_rgb[0]) ** 2 +\n", " (slice_img[..., 1] - purple_rgb[1]) ** 2 +\n", " (slice_img[..., 2] - purple_rgb[2]) ** 2\n", " )\n", " \n", " # Create a mask for pixels close to purple\n", " purple_mask = distance < threshold\n", " \n", " # Replace purple pixels with NaN\n", " filtered_slice = slice_img.astype(float)\n", " for channel in range(3):\n", " filtered_slice[..., channel][purple_mask] = np.nan # Set to NaN for transparency\n", "\n", " return filtered_slice\n", "\n", "def apply_purple_filter_to_slices(data, purple_rgb=(131, 84, 141), threshold=50):\n", " \"\"\"\n", " Apply purple filtering to all slices in the dataset.\n", "\n", " Parameters:\n", " data (list): List of RGB slices.\n", " purple_rgb (tuple): RGB values of the purple color to filter out.\n", " threshold (int): Allowable difference from the purple color.\n", "\n", " Returns:\n", " list: List of slices with purple regions made transparent.\n", " \"\"\"\n", " filtered_slices = []\n", " for i, (slice_img, _) in enumerate(data):\n", " logger.info(f\"Filtering purple for slice {i + 1}/{len(data)}\")\n", " filtered_img = filter_purple(slice_img, purple_rgb, threshold)\n", " filtered_slices.append(filtered_img)\n", " return filtered_slices\n", "\n", "# Apply the purple filter to slices\n", "filtered_rgb_slices = apply_purple_filter_to_slices(processed_slice_mask_pairs)\n", "\n", "def visualize_no_purple_cube(rgb_slices):\n", " \"\"\"\n", " Visualize the RGB slices as a 3D cube with purple regions made transparent.\n", "\n", " Parameters:\n", " rgb_slices (list): List of RGB slices with purple regions filtered out.\n", " \"\"\"\n", " z_slices = len(rgb_slices)\n", " height, width, _ = rgb_slices[0].shape\n", "\n", " fig = go.Figure()\n", "\n", " # Render each slice with purple removed\n", " for z, rgb_image in enumerate(rgb_slices):\n", " # Normalize to [0, 1] for visualization\n", " normalized_rgb = rgb_image / 255.0\n", "\n", " # Use the mean intensity as the surface color\n", " surfacecolor = np.nanmean(normalized_rgb, axis=2) # Use nanmean to handle NaNs\n", "\n", " fig.add_trace(go.Surface(\n", " z=np.full((height, width), z), # Set slice depth\n", " x=np.arange(width),\n", " y=np.arange(height),\n", " surfacecolor=surfacecolor, # Use filtered surface color\n", " colorscale=\"Viridis\",\n", " opacity=0.8,\n", " showscale=False\n", " ))\n", "\n", " fig.update_layout(\n", " title=\"3D RGB Visualization Without Purple\",\n", " scene=dict(\n", " zaxis_title=\"Slices\",\n", " xaxis_title=\"Width\",\n", " yaxis_title=\"Height\"\n", " )\n", " )\n", "\n", " fig.show()\n", "\n", "# Visualize the cube with purple regions removed\n", "visualize_no_purple_cube(filtered_rgb_slices)\n" ] } ], "metadata": { "kaggle": { "accelerator": "none", "dataSources": [ { "datasetId": 181273, "sourceId": 407317, "sourceType": "datasetVersion" } ], "dockerImageVersionId": 30786, "isGpuEnabled": false, "isInternetEnabled": true, "language": "python", "sourceType": "notebook" }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.14" }, "papermill": { "default_parameters": {}, "duration": 7.928442, "end_time": "2024-11-20T11:00:46.672407", "environment_variables": {}, "exception": null, "input_path": "__notebook__.ipynb", "output_path": "__notebook__.ipynb", "parameters": {}, "start_time": "2024-11-20T11:00:38.743965", "version": "2.6.0" } }, "nbformat": 4, "nbformat_minor": 5 }