{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":99552,"databundleVersionId":13851420,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":13152693,"sourceType":"datasetVersion","datasetId":8333354},{"sourceId":263469464,"sourceType":"kernelVersion"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Sharing my Vessel segmentation model weights + code here. Model trained on 128x128x128 volumes (no patches) , and directly infer in the same way. I used https://arxiv.org/abs/2411.17386 pretrained weights, and finetuned on this competition's data. Dice score was about ~0.63. You can follow the data processing as I do here (pretty much copied from @yosukeyama 's public notebook- https://www.kaggle.com/code/yosukeyama/rsna2025-32ch-img-infer-lb-0-69-share) and model loading and inference as in my code below. Apologies for the messy code,  with the deadline approaching, I have been quite caught up finishing my work. Feel free to ask any questions! Model weights here - https://www.kaggle.com/datasets/p4rallax/rsna-segm-1kepochs-128","metadata":{}},{"cell_type":"code","source":"!pip install monai","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:28:27.064898Z","iopub.execute_input":"2025-09-24T02:28:27.065281Z","iopub.status.idle":"2025-09-24T02:29:44.982293Z","shell.execute_reply.started":"2025-09-24T02:28:27.065249Z","shell.execute_reply":"2025-09-24T02:29:44.98122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from monai.inferers import sliding_window_inference\nimport numpy as np\nimport os\nfrom torch.utils.data import Dataset\nimport torch\nfrom monai.transforms import (\n    Compose, LoadImaged, EnsureChannelFirstd, EnsureTyped,ConcatItemsd,ToTensord,SpatialPadd,Lambda,\n    Spacingd, Resized, RandFlipd, RandRotate90d, RandScaleIntensityd ,RandCropByLabelClassesd, MapTransform\n)\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\nimport numpy as np\nfrom monai.networks.nets import DynUNet\nimport sys\nfrom pathlib import Path\nfrom typing import Tuple\nimport pandas as pd \nimport SimpleITK as sitk\nfrom concurrent.futures import ThreadPoolExecutor\nfrom collections import Counter\nimport nibabel as nib\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:29:44.983471Z","iopub.execute_input":"2025-09-24T02:29:44.983811Z","iopub.status.idle":"2025-09-24T02:30:18.178201Z","shell.execute_reply.started":"2025-09-24T02:29:44.983773Z","shell.execute_reply":"2025-09-24T02:30:18.177395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from collections import OrderedDict\nfrom typing import Tuple, List","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.186814Z","iopub.execute_input":"2025-09-24T02:30:18.187074Z","iopub.status.idle":"2025-09-24T02:30:18.652236Z","shell.execute_reply.started":"2025-09-24T02:30:18.187051Z","shell.execute_reply":"2025-09-24T02:30:18.651439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pydicom\nimport cv2\nfrom pathlib import Path\nfrom typing import List, Tuple, Dict, Optional\nfrom scipy import ndimage\nimport warnings\nimport gc\nimport pandas as pd\nwarnings.filterwarnings('ignore')\n\nclass DICOMPreprocessorKaggle:\n    \"\"\"\n    DICOM preprocessing system for Kaggle Code Competition\n    Converts original DICOMPreprocessor logic to single series processing\n    \"\"\"\n    \n    def __init__(self, target_shape: Tuple[int, int, int] = (128, 128, 128)):\n        self.target_depth, self.target_height, self.target_width = target_shape\n        \n    def load_dicom_series(self, series_path: str) -> Tuple[List[pydicom.Dataset], str]:\n        \"\"\"\n        Load DICOM series\n        \"\"\"\n        series_path = Path(series_path)\n        series_name = series_path.name\n        \n        # Search for DICOM files\n        dicom_files = []\n        for root, _, files in os.walk(series_path):\n            for file in files:\n                if file.endswith('.dcm'):\n                    dicom_files.append(os.path.join(root, file))\n        \n        if not dicom_files:\n            raise ValueError(f\"No DICOM files found in {series_path}\")\n        \n        #print(f\"Found {len(dicom_files)} DICOM files in series {series_name}\")\n        \n        # Load DICOM datasets\n        datasets = []\n        for filepath in dicom_files:\n            try:\n                ds = pydicom.dcmread(filepath, force=True)\n                datasets.append(ds)\n            except Exception as e:\n                #print(f\"Failed to load {filepath}: {e}\")\n                continue\n        \n        if not datasets:\n            raise ValueError(f\"No valid DICOM files in {series_path}\")\n        \n        return datasets, series_name\n    \n    def extract_slice_info(self, datasets: List[pydicom.Dataset]) -> List[Dict]:\n        \"\"\"\n        Extract position information for each slice\n        \"\"\"\n        slice_info = []\n        \n        for i, ds in enumerate(datasets):\n            info = {\n                'dataset': ds,\n                'index': i,\n                'instance_number': getattr(ds, 'InstanceNumber', i),\n            }\n            \n            # Get z-coordinate from ImagePositionPatient\n            try:\n                position = getattr(ds, 'ImagePositionPatient', None)\n                if position is not None and len(position) >= 3:\n                    info['z_position'] = float(position[2])\n                else:\n                    # Fallback: use InstanceNumber\n                    info['z_position'] = float(info['instance_number'])\n                    #print(\"ImagePositionPatient not found, using InstanceNumber\")\n            except Exception as e:\n                info['z_position'] = float(i)\n                #print(f\"Failed to extract position info: {e}\")\n            \n            slice_info.append(info)\n        \n        return slice_info\n    \n    def sort_slices_by_position(self, slice_info: List[Dict]) -> List[Dict]:\n        \"\"\"\n        Sort slices by z-coordinate\n        \"\"\"\n        # Sort by z-coordinate\n        sorted_slices = sorted(slice_info, key=lambda x: x['z_position'])\n        \n        #print(f\"Sorted {len(sorted_slices)} slices by z-position\")\n        #print(f\"Z-range: {sorted_slices[0]['z_position']:.2f} to {sorted_slices[-1]['z_position']:.2f}\")\n        \n        return sorted_slices\n    \n    def get_windowing_params(self, ds: pydicom.Dataset, img: np.ndarray = None) -> Tuple[Optional[float], Optional[float]]:\n        \"\"\"\n        Get windowing parameters based on modality\n        \"\"\"\n        modality = getattr(ds, 'Modality', 'CT')\n        \n        if modality == 'CT':\n            # For CT, apply CTA (angiography) settings\n            center, width = (50, 350)\n            #print(f\"Using CTA windowing for CT: Center={center}, Width={width}\")\n            # return center, width\n            return None, None\n            \n        elif modality == 'MR':\n            # For MR, skip windowing (statistical normalization only)\n            #print(\"MR modality detected: skipping windowing, using statistical normalization\")\n            return None, None\n            \n        else:\n            # Unexpected modality (safety measure)\n            #print(f\"Unexpected modality '{modality}', using CTA windowing\")\n            #return (50, 350)\n            return None, None\n    \n    def apply_windowing_or_normalize(self, img: np.ndarray, center: Optional[float], width: Optional[float]) -> np.ndarray:\n        \"\"\"\n        Apply windowing or statistical normalization\n        \"\"\"\n        p1, p99 = np.percentile(img, [1, 99])\n        volume = np.clip(img, p1, p99)\n        volume = (volume - p1) / (p99 - p1 + 1e-7)\n        volume = (volume * 255).astype(np.uint8)\n        return volume\n        # if center is not None and width is not None:\n        #     # # Windowing processing (for CT/CTA)\n        #     # img_min = center - width / 2\n        #     # img_max = center + width / 2\n            \n        #     # windowed = np.clip(img, img_min, img_max)\n        #     # windowed = (windowed - img_min) / (img_max - img_min + 1e-7)\n        #     # result = (windowed * 255).astype(np.uint8)\n            \n        #     # #print(f\"Applied windowing: [{img_min:.1f}, {img_max:.1f}] → [0, 255]\")\n        #     # return result\n            \n        #     # Statistical normalization (for CT as well)\n        #     # Normalize using 1-99 percentiles\n        #     p1, p99 = np.percentile(img, [1, 99])\n        #     # p1, p99 = 0, 500\n            \n        #     if p99 > p1:\n        #         normalized = np.clip(img, p1, p99)\n        #         normalized = (normalized - p1) / (p99 - p1)\n        #         result = (normalized * 255).astype(np.uint8)\n                \n        #         #print(f\"Applied statistical normalization: [{p1:.1f}, {p99:.1f}] → [0, 255]\")\n        #         return result\n        #     else:\n        #         # Fallback: min-max normalization\n        #         img_min, img_max = img.min(), img.max()\n        #         if img_max > img_min:\n        #             normalized = (img - img_min) / (img_max - img_min)\n        #             result = (normalized * 255).astype(np.uint8)\n        #             #print(f\"Applied min-max normalization: [{img_min:.1f}, {img_max:.1f}] → [0, 255]\")\n        #             return result\n        #         else:\n        #             # If image has no variation\n        #             #print(\"Image has no variation, returning zeros\")\n        #             return np.zeros_like(img, dtype=np.uint8)\n        \n        # else:\n        #     # Statistical normalization (for MR)\n        #     # Normalize using 1-99 percentiles\n        #     p1, p99 = np.percentile(img, [1, 99])\n            \n        #     if p99 > p1:\n        #         normalized = np.clip(img, p1, p99)\n        #         normalized = (normalized - p1) / (p99 - p1)\n        #         result = (normalized * 255).astype(np.uint8)\n                \n        #         #print(f\"Applied statistical normalization: [{p1:.1f}, {p99:.1f}] → [0, 255]\")\n        #         return result\n        #     else:\n        #         # Fallback: min-max normalization\n        #         img_min, img_max = img.min(), img.max()\n        #         if img_max > img_min:\n        #             normalized = (img - img_min) / (img_max - img_min)\n        #             result = (normalized * 255).astype(np.uint8)\n        #             #print(f\"Applied min-max normalization: [{img_min:.1f}, {img_max:.1f}] → [0, 255]\")\n        #             return result\n        #         else:\n        #             # If image has no variation\n        #             #print(\"Image has no variation, returning zeros\")\n        #             return np.zeros_like(img, dtype=np.uint8)\n    \n    def extract_pixel_array(self, ds: pydicom.Dataset) -> np.ndarray:\n        \"\"\"\n        Extract 2D pixel array from DICOM and apply preprocessing (for 2D DICOM series)\n        \"\"\"\n        # Get pixel data\n        img = ds.pixel_array.astype(np.float32)\n        \n        # For 3D volume case (multiple frames) - select middle frame\n        if img.ndim == 3:\n            #print(f\"3D DICOM in 2D processing - using middle frame from shape: {img.shape}\")\n            frame_idx = img.shape[0] // 2\n            img = img[frame_idx]\n            #print(f\"Selected frame {frame_idx} from 3D DICOM\")\n        \n        # Convert color image to grayscale\n        # if img.ndim == 3 and img.shape[-1] == 3:\n            # img = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_RGB2GRAY).astype(np.float32)\n            #print(\"Converted color image to grayscale\")\n        \n        # Apply RescaleSlope and RescaleIntercept\n        slope = getattr(ds, 'RescaleSlope', 1)\n        intercept = getattr(ds, 'RescaleIntercept', 0)\n        # slope, intercept = 1, 0\n        if slope != 1 or intercept != 0:\n            img = img * float(slope) + float(intercept)\n            #print(f\"Applied rescaling: slope={slope}, intercept={intercept}\")\n        \n        return img\n    \n    def resize_volume_3d(self, volume: np.ndarray) -> np.ndarray:\n        \"\"\"\n        Resize 3D volume to target size\n        \"\"\"\n        current_shape = volume.shape\n        target_shape = (self.target_depth, self.target_height, self.target_width)\n        \n        if current_shape == target_shape:\n            return volume\n        \n        #print(f\"Resizing volume from {current_shape} to {target_shape}\")\n        \n        # 3D resizing using scipy.ndimage\n        zoom_factors = [\n            target_shape[i] / current_shape[i] for i in range(3)\n        ]\n        \n        # Resize with linear interpolation\n        resized_volume = ndimage.zoom(volume, zoom_factors, order=1, mode='nearest')\n        \n        # Clip to exact size just in case\n        resized_volume = resized_volume[:self.target_depth, :self.target_height, :self.target_width]\n        \n        # Padding if necessary\n        pad_width = [\n            (0, max(0, self.target_depth - resized_volume.shape[0])),\n            (0, max(0, self.target_height - resized_volume.shape[1])),\n            (0, max(0, self.target_width - resized_volume.shape[2]))\n        ]\n        \n        if any(pw[1] > 0 for pw in pad_width):\n            resized_volume = np.pad(resized_volume, pad_width, mode='edge')\n        \n        #print(f\"Final volume shape: {resized_volume.shape}\")\n        return resized_volume.astype(np.uint8)\n    \n    def process_series(self, series_path: str) -> np.ndarray:\n        \"\"\"\n        Process DICOM series and return as NumPy array (for Kaggle: no file saving)\n        \"\"\"\n        try:\n            # 1. Load DICOM files\n            datasets, series_name = self.load_dicom_series(series_path)\n            \n            # Check first DICOM to determine 3D/2D\n            first_ds = datasets[0]\n            first_img = first_ds.pixel_array\n            \n            if len(datasets) == 1 and first_img.ndim == 3:\n                # Case 1: Single 3D DICOM file\n                #print(f\"Processing single 3D DICOM with shape: {first_img.shape}\")\n                return self._process_single_3d_dicom(first_ds, series_name)\n            else:\n                # Case 2: Multiple 2D DICOM files\n                #print(f\"Processing {len(datasets)} 2D DICOM files\")\n                return self._process_multiple_2d_dicoms(datasets, series_name)\n            \n        except Exception as e:\n            #print(f\"Failed to process series {series_path}: {e}\")\n            raise\n    \n    def _process_single_3d_dicom(self, ds: pydicom.Dataset, series_name: str) -> np.ndarray:\n        \"\"\"\n        Process single 3D DICOM file (for Kaggle: no file saving)\n        \"\"\"\n        # Get pixel array\n        volume = ds.pixel_array.astype(np.float32)\n        \n        # Apply RescaleSlope and RescaleIntercept\n        slope = getattr(ds, 'RescaleSlope', 1)\n        intercept = getattr(ds, 'RescaleIntercept', 0)\n        slope, intercept = 1, 0\n        if slope != 1 or intercept != 0:\n            volume = volume * float(slope) + float(intercept)\n            # #print(f\"Applied rescaling: slope={slope}, intercept={intercept}\")\n        \n        # Get windowing settings\n        window_center, window_width = self.get_windowing_params(ds)\n        \n        # Apply windowing to each slice\n        processed_slices = []\n        for i in range(volume.shape[0]):\n            slice_img = volume[i]\n            processed_img = self.apply_windowing_or_normalize(slice_img, window_center, window_width)\n            processed_slices.append(processed_img)\n        \n        volume = np.stack(processed_slices, axis=0)\n        ##print(f\"3D volume shape after windowing: {volume.shape}\")\n        \n        # 3D resize\n        final_volume = self.resize_volume_3d(volume)\n        \n        ##print(f\"Successfully processed 3D DICOM series {series_name}\")\n        return final_volume\n    \n    def _process_multiple_2d_dicoms(self, datasets: List[pydicom.Dataset], series_name: str) -> np.ndarray:\n        \"\"\"\n        Process multiple 2D DICOM files (for Kaggle: no file saving)\n        \"\"\"\n        slice_info = self.extract_slice_info(datasets)\n        sorted_slices = self.sort_slices_by_position(slice_info)\n        first_img = self.extract_pixel_array(sorted_slices[0]['dataset'])\n        window_center, window_width = self.get_windowing_params(sorted_slices[0]['dataset'], first_img)\n        processed_slices = []\n        \n        for slice_data in sorted_slices:\n            ds = slice_data['dataset']\n            img = self.extract_pixel_array(ds)\n            processed_img = self.apply_windowing_or_normalize(img, window_center, window_width)\n            resized_img = cv2.resize(processed_img, (self.target_width, self.target_height))\n            \n            processed_slices.append(resized_img)\n\n        volume = np.stack(processed_slices, axis=0)\n        ##print(f\"2D slices stacked to volume shape: {volume.shape}\")\n        final_volume = self.resize_volume_3d(volume)\n        \n        ##print(f\"Successfully processed 2D DICOM series {series_name}\")\n        return final_volume\n\ndef process_dicom_series_kaggle(series_path: str, target_shape: Tuple[int, int, int] = (32, 384, 384)) -> np.ndarray:\n    \"\"\"\n    DICOM processing function for Kaggle inference (single series)\n    \n    Args:\n        series_path: Path to DICOM series\n        target_shape: Target volume size (depth, height, width)\n    \n    Returns:\n        np.ndarray: Processed volume\n    \"\"\"\n    preprocessor = DICOMPreprocessorKaggle(target_shape=target_shape)\n    return preprocessor.process_series(series_path)\n\n# Safe processing function with memory cleanup\ndef process_dicom_series_safe(series_path: str, target_shape: Tuple[int, int, int] = (32, 384, 384)) -> np.ndarray:\n    \"\"\"\n    Safe DICOM processing with memory cleanup\n    \n    Args:\n        series_path: Path to DICOM series\n        target_shape: Target volume size (depth, height, width)\n    \n    Returns:\n        np.ndarray: Processed volume\n    \"\"\"\n    try:\n        volume = process_dicom_series_kaggle(series_path, target_shape)\n        return volume\n    finally:\n        # Memory cleanup\n        gc.collect()\n\n# Test function\ndef test_single_series(series_path: str, target_shape: Tuple[int, int, int] = (32, 384, 384)):\n    \"\"\"\n    Test processing for single series\n    \"\"\"\n    try:\n        #print(f\"Testing single series: {series_path}\")\n        \n        # Execute processing\n        volume = process_dicom_series_safe(series_path, target_shape)\n        \n        # Display results\n        #print(f\"✓ Successfully processed series\")\n        #print(f\"  Volume shape: {volume.shape}\")\n        #print(f\"  Volume dtype: {volume.dtype}\")\n        #print(f\"  Volume range: [{volume.min()}, {volume.max()}]\")\n        \n        return volume\n        \n    except Exception as e:\n        #print(f\"✗ Failed to process series: {e}\")\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.653215Z","iopub.execute_input":"2025-09-24T02:30:18.653446Z","iopub.status.idle":"2025-09-24T02:30:18.682448Z","shell.execute_reply.started":"2025-09-24T02:30:18.653426Z","shell.execute_reply":"2025-09-24T02:30:18.681749Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nfrom typing import Tuple, Optional\nimport numpy as np\nimport os\nimport pydicom\n\ndef load_dicom_series(series_dir: Path) -> Optional[np.ndarray]:\n    \"\"\"\n    Load a DICOM series as a 3D volume (D,H,W) in HU.\n    Skips series that do not have required metadata.\n\n    Args:\n        series_dir (Path): path to folder containing DICOM files\n\n    Returns:\n        volume (np.ndarray) or None: 3D volume, or None if metadata missing\n    \"\"\"\n    dcm_paths = [Path(series_dir) / f for f in os.listdir(series_dir) if f.lower().endswith(\".dcm\")]\n    if not dcm_paths:\n        return None\n\n    slices = [pydicom.dcmread(str(p), force=True) for p in dcm_paths]\n\n    # Skip if metadata missing\n    if not (hasattr(slices[0], \"ImagePositionPatient\") and hasattr(slices[0], \"ImageOrientationPatient\")):\n        return None\n\n    try:\n        orientation = np.array(slices[0].ImageOrientationPatient).reshape(2, 3)\n        row_cos, col_cos = orientation\n        normal = np.cross(row_cos, col_cos)\n        slices.sort(key=lambda ds: np.dot(np.array(ds.ImagePositionPatient), normal))\n    except Exception:\n        return None\n\n    # Apply HU scaling\n    slope = float(getattr(slices[0], \"RescaleSlope\", 1.0))\n    intercept = float(getattr(slices[0], \"RescaleIntercept\", 0.0))\n    slice_arrays = [ds.pixel_array.astype(np.float32) * slope + intercept for ds in slices]\n\n    # Ensure all slices have the same shape\n    shapes = [s.shape for s in slice_arrays]\n    if len(set(shapes)) > 1:\n        max_h = max(s[0] for s in shapes)\n        max_w = max(s[1] for s in shapes)\n        slice_arrays = [np.pad(s, ((0,max_h-s.shape[0]), (0,max_w-s.shape[1])), mode='constant') \n                        if s.shape != (max_h,max_w) else s \n                        for s in slice_arrays]\n\n    volume = np.stack(slice_arrays, axis=0)  # (D,H,W)\n    volume=np.transpose(volume,(2,1,0))\n    return volume","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.683553Z","iopub.execute_input":"2025-09-24T02:30:18.683828Z","iopub.status.idle":"2025-09-24T02:30:18.703774Z","shell.execute_reply.started":"2025-09-24T02:30:18.683809Z","shell.execute_reply":"2025-09-24T02:30:18.703125Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.704603Z","iopub.execute_input":"2025-09-24T02:30:18.704862Z","iopub.status.idle":"2025-09-24T02:30:18.722883Z","shell.execute_reply.started":"2025-09-24T02:30:18.704841Z","shell.execute_reply":"2025-09-24T02:30:18.722258Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DicomSeriesDataset(Dataset):\n    def __init__(self, series_dir, load_dicom_series, transform=None,modalities=['CTA']):\n        self.series_dir = series_dir\n        self.load_dicom_series = load_dicom_series\n        self.transform = transform\n\n        self.df = pd.read_csv('/kaggle/input/rsna-intracranial-aneurysm-detection/train.csv')\n        self.series_uids = []\n        for modality in modalities:\n            self.series_uids.extend(self.df[self.df['Modality'] == modality]['SeriesInstanceUID'].to_list())\n        \n\n    def __len__(self):\n        return len(self.series_uids)\n\n    def __getitem__(self, idx):\n        uid = self.series_uids[idx]\n        series_path = os.path.join(self.series_dir, uid)\n        volume = self.load_dicom_series(series_path)\n        modality= self.df[self.df['SeriesInstanceUID'] == uid]['Modality'].iloc[0]\n        # volume = apply_dicom_windowing(volume,modality)\n        data_dict = {\"Image\": volume}\n        data_dict = self.transform(data_dict)\n\n        return data_dict[\"Image\"], uid\n\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.723486Z","iopub.execute_input":"2025-09-24T02:30:18.723661Z","iopub.status.idle":"2025-09-24T02:30:18.738842Z","shell.execute_reply.started":"2025-09-24T02:30:18.723646Z","shell.execute_reply":"2025-09-24T02:30:18.738127Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class NpzDataset(Dataset):\n    def __init__(self, series_dir, transform=None):\n        self.series_dir = series_dir\n        self.load_dicom_series = load_dicom_series\n        self.transform = transform\n\n        self.df = pd.read_csv('/kaggle/input/rsna-intracranial-aneurysm-detection/train.csv')\n        self.series_uids = [os.path.splitext(os.path.basename(f))[0] for f in glob.glob(os.path.join(series_dir,'*.npz'))]\n\n        \n\n    def __len__(self):\n        return len(self.series_uids)\n\n    def __getitem__(self, idx):\n        uid = self.series_uids[idx]\n        series_path = os.path.join(self.series_dir, uid +'.npz')\n        data = np.load(series_path)\n        volume = data['volume']\n        modality= self.df[self.df['SeriesInstanceUID'] == uid]['Modality'].iloc[0]\n        # volume = apply_dicom_windowing(volume,modality)\n        data_dict = {\"Image\": volume}\n        data_dict = self.transform(data_dict)\n\n        return data_dict[\"Image\"], uid\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.741253Z","iopub.execute_input":"2025-09-24T02:30:18.741489Z","iopub.status.idle":"2025-09-24T02:30:18.757932Z","shell.execute_reply.started":"2025-09-24T02:30:18.741463Z","shell.execute_reply":"2025-09-24T02:30:18.757267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport matplotlib.pyplot as plt\n\ndef show_mip(image, mask=None, uid=None):\n    # image, mask: torch.Tensor or numpy.ndarray [C, D, H, W] or [D, H, W]\n    if isinstance(image, torch.Tensor):\n        image = image.cpu().numpy()\n    if mask is not None and isinstance(mask, torch.Tensor):\n        mask = mask.cpu().numpy()\n\n    if image.ndim == 4:  # [C,D,H,W]\n        image = image[0]  # take first channel\n\n    mips = [\n        image.max(axis=0),  # axial (D projection)\n        image.max(axis=1),  # coronal (H projection)\n        image.max(axis=2),  # sagittal (W projection)\n    ]\n\n    fig, axes = plt.subplots(1, 3, figsize=(20, 8))\n    for i, mip in enumerate(mips):\n        axes[i].imshow(mip, cmap=\"gray\")\n        axes[i].set_title([\"Axial MIP\", \"Coronal MIP\", \"Sagittal MIP\"][i])\n        axes[i].axis(\"off\")\n\n    if mask is not None:\n        mask_mips = [\n            mask.max(axis=0),\n            mask.max(axis=1),\n            mask.max(axis=2),\n        ]\n        for i, m in enumerate(mask_mips):\n            axes[i].imshow(m, cmap='jet', alpha=0.4)\n\n    if uid is not None:\n        plt.suptitle(f\"Series UID: {uid}\")\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.758582Z","iopub.execute_input":"2025-09-24T02:30:18.758752Z","iopub.status.idle":"2025-09-24T02:30:18.775497Z","shell.execute_reply.started":"2025-09-24T02:30:18.758738Z","shell.execute_reply":"2025-09-24T02:30:18.774893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_classes=14\ndevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nmodel = DynUNet(\n    spatial_dims=3,\n    in_channels=1,\n    out_channels=num_classes,  # same as before\n            strides= [[1, 1, 1], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],\n        kernel_size= [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]],\n        upsample_kernel_size= [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],\n        filters= [32, 64, 128, 256, 320, 320],\n    \n    \n    res_block = True\n)\nchkpt = torch.load('/kaggle/input/rsna-segm-1kepochs-128/best_model.pth',map_location=device)\nstate_dict = chkpt\n\n# Remove 'module.' prefix if it exists\nnew_state_dict = {}\nfor k, v in state_dict.items():\n    if k.startswith(\"module.\"):\n        new_state_dict[k.replace(\"module.\", \"\")] = v\n    else:\n        new_state_dict[k] = v\n\nmodel.load_state_dict(new_state_dict, strict=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:18.776082Z","iopub.execute_input":"2025-09-24T02:30:18.776274Z","iopub.status.idle":"2025-09-24T02:30:21.155385Z","shell.execute_reply.started":"2025-09-24T02:30:18.77626Z","shell.execute_reply":"2025-09-24T02:30:21.154702Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:31:25.634667Z","iopub.execute_input":"2025-09-24T02:31:25.634939Z","iopub.status.idle":"2025-09-24T02:31:25.639114Z","shell.execute_reply.started":"2025-09-24T02:31:25.634919Z","shell.execute_reply":"2025-09-24T02:31:25.638377Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"series_dir = \"/kaggle/input/save-3d-volumes-as-npz-all-data-128/processed_train/\"\ntfms =Compose([\n            EnsureChannelFirstd(keys=[\"Image\"], channel_dim=\"no_channel\"),\n            EnsureTyped(keys=[\"Image\"],dtype=[torch.float32]),\n            # Resized(keys=[\"Image\"], spatial_size=(384,384,128), mode=[\"trilinear\"]),\n            \n            ToTensord(keys=[\"Image\"]),\n        ])\nmo = ['CTA' , 'MRA' , 'MRI T1post', 'MRI T2']\nprocessor = DICOMPreprocessorKaggle((128,128,128))\ndataset = NpzDataset(series_dir, tfms)\n\n# dataset=\ndataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:33:23.349215Z","iopub.execute_input":"2025-09-24T02:33:23.349697Z","iopub.status.idle":"2025-09-24T02:33:23.514829Z","shell.execute_reply.started":"2025-09-24T02:33:23.349677Z","shell.execute_reply":"2025-09-24T02:33:23.514274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport pydicom\nfrom scipy import ndimage","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:21.19561Z","iopub.execute_input":"2025-09-24T02:30:21.19589Z","iopub.status.idle":"2025-09-24T02:30:21.199534Z","shell.execute_reply.started":"2025-09-24T02:30:21.195872Z","shell.execute_reply":"2025-09-24T02:30:21.198822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = model.to(device)\nmodel.eval()\nwith torch.no_grad():\n    for vol, uid in tqdm(dataloader):\n        vol = vol.to(device)  # [B,1,D,H,W]\n        # pred = sliding_window_inference(\n        #     inputs=vol,\n        #     roi_size=(64, 64, 64),  # patch size\n        #     sw_batch_size=8,\n        #     predictor=model,\n        #     overlap=0.5)\n        pred= model(vol)\n        # pred_labels = torch.sigmoid(pred)\n        # pred_labels = pred_labels > 0.5\n        pred = torch.softmax(pred, dim=1)  # convert logits → probabilities\n        # pred_labels = torch.argmax(pred, dim=1, keepdim=True) \n        pred_labels = torch.argmax(pred, dim=1)\n        print(pred.shape)\n        show_mip(vol.cpu().numpy().squeeze(),pred_labels.cpu().numpy().squeeze())\n        np.savez(f\"{uid}_pred.npz\",volume=vol.cpu().numpy().squeeze(),mask=pred_labels.cpu().numpy().squeeze())\n        # print('After postproc-')\n        # show_mip(vol.cpu().numpy().squeeze(),postprocess_vessels(pred_labels.cpu().numpy().squeeze()))\n\n        print(f'UID : {uid}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:37:06.488649Z","iopub.execute_input":"2025-09-24T02:37:06.489382Z","iopub.status.idle":"2025-09-24T02:37:16.702123Z","shell.execute_reply.started":"2025-09-24T02:37:06.489351Z","shell.execute_reply":"2025-09-24T02:37:16.70092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# path = \"/kaggle/working/('1.2.826.0.1.3680043.8.498.10004044428023505108375152878107656647',)_pred.npz\"\n# mask = np.load(path)['mask']\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:26.7098Z","iopub.status.idle":"2025-09-24T02:30:26.710119Z","shell.execute_reply.started":"2025-09-24T02:30:26.70997Z","shell.execute_reply":"2025-09-24T02:30:26.709986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import gc\n\ngc.collect()\ntorch.cuda.empty_cache()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-24T02:30:26.713369Z","iopub.status.idle":"2025-09-24T02:30:26.713574Z","shell.execute_reply.started":"2025-09-24T02:30:26.713475Z","shell.execute_reply":"2025-09-24T02:30:26.713484Z"}},"outputs":[],"execution_count":null}]}