{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":99552,"databundleVersionId":13851420,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Step 1: Explore Segmentation Data","metadata":{}},{"cell_type":"code","source":"\"\"\"\nStep 1: Explore Segmentation Data\n===================================\nSimple functions to explore the segmentation dataset.\nFocus on understanding the data before proceeding.\n\"\"\"\n\nfrom pathlib import Path\nimport nibabel as nib\nimport numpy as np\nimport pandas as pd\n\n\ndef explore_segmentation_directory(segmentations_dir):\n    \"\"\"\n    Explore the segmentation directory structure.\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n    \n    Returns:\n        dict: Summary information\n    \"\"\"\n    seg_dir = Path(segmentations_dir)\n    \n    if not seg_dir.exists():\n        print(f\"❌ Directory not found: {segmentations_dir}\")\n        return None\n    \n    # Find image files (exclude _cowseg.nii)\n    image_files = sorted([f for f in seg_dir.glob('*.nii') \n                         if not f.name.endswith('_cowseg.nii')])\n    \n    # Find label files\n    label_files = sorted(seg_dir.glob('*_cowseg.nii'))\n    \n    print(\"=\" * 60)\n    print(\"Segmentation Directory Exploration\")\n    print(\"=\" * 60)\n    print(f\"📁 Directory: {segmentations_dir}\")\n    print(f\"📊 Image files: {len(image_files)}\")\n    print(f\"📊 Label files: {len(label_files)}\")\n    \n    if len(image_files) != len(label_files):\n        print(f\"⚠️  Warning: Image and label counts don't match!\")\n    \n    return {\n        'image_files': image_files,\n        'label_files': label_files,\n        'count': len(image_files)\n    }\n\n\ndef load_sample_segmentation(segmentations_dir, sample_index=0):\n    \"\"\"\n    Load a sample image and label pair.\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n        sample_index: Which sample to load (0 = first)\n    \n    Returns:\n        dict: Sample data\n    \"\"\"\n    seg_dir = Path(segmentations_dir)\n    image_files = sorted([f for f in seg_dir.glob('*.nii') \n                         if not f.name.endswith('_cowseg.nii')])\n    \n    if sample_index >= len(image_files):\n        print(f\"❌ Sample index {sample_index} out of range (max: {len(image_files)-1})\")\n        return None\n    \n    # Get file paths\n    image_path = image_files[sample_index]\n    label_path = seg_dir / f\"{image_path.stem}_cowseg.nii\"\n    \n    if not label_path.exists():\n        print(f\"❌ Label file not found: {label_path}\")\n        return None\n    \n    print(\"=\" * 60)\n    print(f\"Loading Sample {sample_index}\")\n    print(\"=\" * 60)\n    print(f\"📄 Image: {image_path.name}\")\n    print(f\"📄 Label: {label_path.name}\")\n    \n    # Load files\n    try:\n        image_nii = nib.load(image_path)\n        label_nii = nib.load(label_path)\n        \n        image_data = image_nii.get_fdata()\n        label_data = label_nii.get_fdata()\n        \n        # Print information\n        print(f\"\\n📊 Image Information:\")\n        print(f\"  Shape: {image_data.shape}\")\n        print(f\"  Data type: {image_data.dtype}\")\n        print(f\"  Spacing: {image_nii.header.get_zooms()[:3]}\")\n        print(f\"  Intensity range: [{image_data.min():.2f}, {image_data.max():.2f}]\")\n        print(f\"  Mean intensity: {image_data.mean():.2f}\")\n        print(f\"  Std intensity: {image_data.std():.2f}\")\n        \n        print(f\"\\n📊 Label Information:\")\n        print(f\"  Shape: {label_data.shape}\")\n        print(f\"  Data type: {label_data.dtype}\")\n        print(f\"  Unique labels: {sorted(np.unique(label_data).astype(int))}\")\n        print(f\"  Label count: {len(np.unique(label_data))}\")\n        \n        # Count voxels per label\n        unique_labels, counts = np.unique(label_data, return_counts=True)\n        print(f\"\\n📊 Label Distribution:\")\n        for label, count in zip(unique_labels, counts):\n            percentage = (count / label_data.size) * 100\n            print(f\"  Label {int(label)}: {count:,} voxels ({percentage:.2f}%)\")\n        \n        return {\n            'image_path': image_path,\n            'label_path': label_path,\n            'image_nii': image_nii,\n            'label_nii': label_nii,\n            'image_data': image_data,\n            'label_data': label_data,\n            'image_shape': image_data.shape,\n            'label_shape': label_data.shape,\n            'unique_labels': np.unique(label_data).astype(int),\n            'spacing': image_nii.header.get_zooms()[:3]\n        }\n        \n    except Exception as e:\n        print(f\"❌ Error loading files: {e}\")\n        return None\n\n\ndef understand_vessel_classes():\n    \"\"\"\n    Print information about the 13 vessel classes.\n    \"\"\"\n    classes = {\n        0: \"Background\",\n        1: \"Other Posterior Circulation\",\n        2: \"Basilar Tip\",\n        3: \"Right Posterior Communicating Artery\",\n        4: \"Left Posterior Communicating Artery\",\n        5: \"Right Infraclinoid Internal Carotid Artery\",\n        6: \"Left Infraclinoid Internal Carotid Artery\",\n        7: \"Right Supraclinoid Internal Carotid Artery\",\n        8: \"Left Supraclinoid Internal Carotid Artery\",\n        9: \"Right Middle Cerebral Artery\",\n        10: \"Left Middle Cerebral Artery\",\n        11: \"Right Anterior Cerebral Artery\",\n        12: \"Left Anterior Cerebral Artery\",\n        13: \"Anterior Communicating Artery\"\n    }\n    \n    print(\"=\" * 60)\n    print(\"13 Vessel Classes\")\n    print(\"=\" * 60)\n    for label_id, class_name in classes.items():\n        print(f\"  {label_id:2d}: {class_name}\")\n    \n    return classes\n\n\ndef quick_summary(segmentations_dir):\n    \"\"\"\n    Quick summary of the segmentation dataset.\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n    \"\"\"\n    # Explore directory\n    info = explore_segmentation_directory(segmentations_dir)\n    \n    if info is None:\n        return\n    \n    print()\n    \n    # Load first sample\n    sample = load_sample_segmentation(segmentations_dir, sample_index=2)\n    \n    print()\n    \n    # Show vessel classes\n    understand_vessel_classes()\n    \n    print(\"\\n\" + \"=\" * 60)\n    print(\"✅ Exploration Complete!\")\n    print(\"=\" * 60)\n\n\nif __name__ == \"__main__\":\n    # Example usage\n    segmentations_dir = '/kaggle/input/rsna-intracranial-aneurysm-detection/segmentations'\n    quick_summary(segmentations_dir)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:00:04.327929Z","iopub.execute_input":"2025-12-08T11:00:04.328145Z","iopub.status.idle":"2025-12-08T11:00:12.368624Z","shell.execute_reply.started":"2025-12-08T11:00:04.328127Z","shell.execute_reply":"2025-12-08T11:00:12.367959Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 2: Create nnU-Net Dataset Format (Standalone Version)","metadata":{}},{"cell_type":"markdown","source":"![image.png](attachment:bb6e9ba2-c320-434f-bfea-8fc8ebbee40d.png)","metadata":{},"attachments":{"bb6e9ba2-c320-434f-bfea-8fc8ebbee40d.png":{"image/png":"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"}}},{"cell_type":"code","source":"\"\"\"\nStep 2: Create nnU-Net Dataset Format (Standalone Version)\n==========================================================\nConvert segmentation data to nnU-Net format - NO repository code needed!\n\nThis version works independently and doesn't require the GitHub repository.\n\"\"\"\n\nimport os\nimport json\nimport shutil\nfrom pathlib import Path\nimport nibabel as nib\nimport numpy as np\nfrom tqdm import tqdm\n\n\ndef setup_nnunet_environment():\n    \"\"\"Set up nnU-Net environment variables.\"\"\"\n    os.environ['nnUNet_raw'] = '/kaggle/working/nnUNet_raw'\n    os.environ['nnUNet_preprocessed'] = '/kaggle/working/nnUNet_preprocessed'\n    os.environ['nnUNet_results'] = '/kaggle/working/nnUNet_results'\n    \n    # Create directories\n    for dir_path in [os.environ['nnUNet_raw'], \n                     os.environ['nnUNet_preprocessed'], \n                     os.environ['nnUNet_results']]:\n        Path(dir_path).mkdir(parents=True, exist_ok=True)\n    \n    print(\"✅ nnU-Net environment variables set\")\n\n\ndef normalize_image(img: np.ndarray) -> np.ndarray:\n    \"\"\"\n    Normalize image intensities (percentile clipping + scaling).\n    \n    Args:\n        img: Input image array\n    \n    Returns:\n        Normalized image array\n    \"\"\"\n    p1, p99 = np.percentile(img, (1, 99))\n    img = np.clip(img, p1, p99)\n    if img.max() > img.min():\n        img = (img - img.min()) / (img.max() - img.min())\n    return img\n\n\ndef process_case_simple(\n    uid: str,\n    segmentation_dir: Path,\n    output_images_dir: Path,\n    output_labels_dir: Path,\n    label_mapping=None\n):\n    \"\"\"\n    Process a single case - simple version without modality discovery.\n    \n    Args:\n        uid: Case UID (filename without .nii)\n        segmentation_dir: Path to segmentations directory\n        output_images_dir: Output directory for images\n        output_labels_dir: Output directory for labels\n        label_mapping: Optional label mapping dict (for Dataset 3)\n    \n    Returns:\n        bool: Success flag\n    \"\"\"\n    try:\n        # File paths\n        image_path = segmentation_dir / f\"{uid}.nii\"\n        label_path = segmentation_dir / f\"{uid}_cowseg.nii\"\n        \n        # Check files exist\n        if not image_path.exists():\n            print(f\"  ⚠️  Image not found: {image_path.name}\")\n            return False\n        if not label_path.exists():\n            print(f\"  ⚠️  Label not found: {label_path.name}\")\n            return False\n        \n        # Load NIfTI files\n        img_nii = nib.load(str(image_path))\n        label_nii = nib.load(str(label_path))\n        print(nib.aff2axcodes(img_nii.affine))  # e.g., ('R', 'A', 'S')\n        \n        # Enforce RAS orientation (canonical)\n        img_nii = nib.as_closest_canonical(img_nii)\n        print(nib.aff2axcodes(img_nii.affine))  # e.g., ('R', 'A', 'S')\n        label_nii = nib.as_closest_canonical(label_nii)\n        \n        # Extract arrays\n        img_data = img_nii.get_fdata()\n        label_data = label_nii.get_fdata().astype(np.uint8)\n        \n        # Apply label mapping if provided (for Dataset 3)\n        if label_mapping:\n            remapped_label = np.zeros_like(label_data, dtype=np.uint8)\n            for src_value, dst_value in label_mapping.items():\n                remapped_label[label_data == src_value] = dst_value\n            label_data = remapped_label\n        \n        # Normalize image\n        img_data = normalize_image(img_data)\n        \n        # Create new NIfTI objects\n        affine = img_nii.affine.copy()\n        img_nii_new = nib.Nifti1Image(img_data, affine)\n        label_nii_new = nib.Nifti1Image(label_data, affine)\n        \n        # nnU-Net filename format: {uid}_0000.nii.gz for images\n        img_filename = output_images_dir / f\"{uid}_0000.nii.gz\"\n        label_filename = output_labels_dir / f\"{uid}.nii.gz\"\n        \n        # Save\n        nib.save(img_nii_new, str(img_filename))\n        nib.save(label_nii_new, str(label_filename))\n        \n        return True\n        \n    except Exception as e:\n        print(f\"  ❌ Error processing {uid}: {e}\")\n        return False\n\n\ndef create_dataset_1_standalone(segmentations_dir, max_samples=None):\n    \"\"\"\n    Create Dataset 1: 13 vessel classes (standalone version).\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n        max_samples: Maximum number of samples to process (None = all)\n    \"\"\"\n    print(\"=\" * 60)\n    print(\"Creating Dataset 1: 13 Vessel Classes\")\n    if max_samples:\n        print(f\"⚠️  TEST MODE: Processing only {max_samples} samples\")\n    print(\"=\" * 60)\n    \n    seg_dir = Path(segmentations_dir)\n    output_dir = Path('/kaggle/working/nnUNet_raw/Dataset001_VesselSegmentation')\n    \n    # Create output directories\n    images_dir = output_dir / 'imagesTr'\n    labels_dir = output_dir / 'labelsTr'\n    images_dir.mkdir(parents=True, exist_ok=True)\n    labels_dir.mkdir(parents=True, exist_ok=True)\n    \n    # Find all image files\n    image_files = sorted([f for f in seg_dir.glob('*.nii') \n                         if not f.name.endswith('_cowseg.nii')])\n    uids = [f.stem for f in image_files]\n    \n    # Limit samples if specified\n    if max_samples:\n        uids = uids[:max_samples]\n        print(f\"📊 Limiting to first {max_samples} samples for testing\")\n    \n    print(f\"📁 Found {len(uids)} cases\")\n    print(f\"📁 Output: {output_dir}\")\n    print()\n    \n    # Process each case\n    successful = 0\n    failed = []\n    \n    for uid in tqdm(uids, desc=\"Processing cases\"):\n        success = process_case_simple(\n            uid=uid,\n            segmentation_dir=seg_dir,\n            output_images_dir=images_dir,\n            output_labels_dir=labels_dir,\n            label_mapping=None  # No mapping for Dataset 1\n        )\n        if success:\n            successful += 1\n        else:\n            failed.append(uid)\n    \n    print(f\"\\n✅ Processed: {successful}/{len(uids)} cases\")\n    if failed:\n        print(f\"⚠️  Failed: {len(failed)} cases\")\n    \n    # Create dataset.json\n    labels = {\n        \"background\": 0,\n        \"Other Posterior Circulation\": 1,\n        \"Basilar Tip\": 2,\n        \"Right Posterior Communicating Artery\": 3,\n        \"Left Posterior Communicating Artery\": 4,\n        \"Right Infraclinoid Internal Carotid Artery\": 5,\n        \"Left Infraclinoid Internal Carotid Artery\": 6,\n        \"Right Supraclinoid Internal Carotid Artery\": 7,\n        \"Left Supraclinoid Internal Carotid Artery\": 8,\n        \"Right Middle Cerebral Artery\": 9,\n        \"Left Middle Cerebral Artery\": 10,\n        \"Right Anterior Cerebral Artery\": 11,\n        \"Left Anterior Cerebral Artery\": 12,\n        \"Anterior Communicating Artery\": 13,\n    }\n    \n    dataset_json = {\n        \"channel_names\": {\n            \"0\": \"CT\"  # We'll use CT as default (modality not critical for training)\n        },\n        \"labels\": labels,\n        \"numTraining\": successful,\n        \"file_ending\": \".nii.gz\",\n        \"datasetName\": \"VesselSegmentation\",\n        \"reference\": \"RSNA 2025 Intracranial Aneurysm Detection\",\n        \"release\": \"1.0\"\n    }\n    \n    json_path = output_dir / 'dataset.json'\n    with open(json_path, 'w') as f:\n        json.dump(dataset_json, f, indent=2)\n    \n    print(f\"✅ Created dataset.json\")\n    print(f\"✅ Dataset 1 complete: {output_dir}\")\n    \n    return successful\n\n\ndef create_dataset_3_standalone(segmentations_dir, max_samples=10):\n    \"\"\"\n    Create Dataset 3: 3 vessel groups (standalone version).\n    \n    Groups the 13 classes into 3 groups for Model 1 (coarse localization).\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n        max_samples: Maximum number of samples to process (None = all)\n    \"\"\"\n    print(\"=\" * 60)\n    print(\"Creating Dataset 3: 3 Vessel Groups\")\n    if max_samples:\n        print(f\"⚠️  TEST MODE: Processing only {max_samples} samples\")\n    print(\"=\" * 60)\n    \n    seg_dir = Path(segmentations_dir)\n    output_dir = Path('/kaggle/working/nnUNet_raw/Dataset003_VesselGrouping')\n    \n    # Create output directories\n    images_dir = output_dir / 'imagesTr'\n    labels_dir = output_dir / 'labelsTr'\n    images_dir.mkdir(parents=True, exist_ok=True)\n    labels_dir.mkdir(parents=True, exist_ok=True)\n    \n    # Label mapping: 13 classes -> 3 groups\n    label_mapping = {\n        0: 0,  # background -> background\n        1: 1,  # Other Posterior Circulation -> Group 1\n        2: 1,  # Basilar Tip -> Group 1\n        3: 3,  # Right Posterior Communicating -> Group 3\n        4: 3,  # Left Posterior Communicating -> Group 3\n        5: 3,  # Right Infraclinoid ICA -> Group 3\n        6: 3,  # Left Infraclinoid ICA -> Group 3\n        7: 3,  # Right Supraclinoid ICA -> Group 3\n        8: 3,  # Left Supraclinoid ICA -> Group 3\n        9: 2,  # Right MCA -> Group 2\n        10: 2, # Left MCA -> Group 2\n        11: 3, # Right ACA -> Group 3\n        12: 3, # Left ACA -> Group 3\n        13: 3, # Anterior Communicating -> Group 3\n    }\n    \n    # Find all image files\n    image_files = sorted([f for f in seg_dir.glob('*.nii') \n                         if not f.name.endswith('_cowseg.nii')])\n    uids = [f.stem for f in image_files]\n    \n    # Limit samples if specified\n    if max_samples:\n        uids = uids[:max_samples]\n        print(f\"📊 Limiting to first {max_samples} samples for testing\")\n    \n    print(f\"📁 Found {len(uids)} cases\")\n    print(f\"📁 Output: {output_dir}\")\n    print()\n    \n    # Process each case\n    successful = 0\n    failed = []\n    \n    for uid in tqdm(uids, desc=\"Processing cases\"):\n        success = process_case_simple(\n            uid=uid,\n            segmentation_dir=seg_dir,\n            output_images_dir=images_dir,\n            output_labels_dir=labels_dir,\n            label_mapping=label_mapping\n        )\n        if success:\n            successful += 1\n        else:\n            failed.append(uid)\n    \n    print(f\"\\n✅ Processed: {successful}/{len(uids)} cases\")\n    if failed:\n        print(f\"⚠️  Failed: {len(failed)} cases\")\n    \n    # Create dataset.json\n    labels = {\n        \"background\": 0,\n        \"Posterior_Circulation_and_Basilar\": 1,\n        \"Middle_Cerebral_Arteries\": 2,\n        \"Other_Locations\": 3,\n    }\n    \n    dataset_json = {\n        \"channel_names\": {\n            \"0\": \"CT\"\n        },\n        \"labels\": labels,\n        \"numTraining\": successful,\n        \"file_ending\": \".nii.gz\",\n        \"datasetName\": \"VesselGrouping\",\n        \"reference\": \"RSNA 2025 Intracranial Aneurysm Detection\",\n        \"release\": \"1.0\"\n    }\n    \n    json_path = output_dir / 'dataset.json'\n    with open(json_path, 'w') as f:\n        json.dump(dataset_json, f, indent=2)\n    \n    print(f\"✅ Created dataset.json\")\n    print(f\"✅ Dataset 3 complete: {output_dir}\")\n    \n    return successful\n\n\ndef verify_dataset(dataset_id):\n    \"\"\"Verify a dataset was created correctly.\"\"\"\n    if dataset_id == 1:\n        dataset_path = Path('/kaggle/working/nnUNet_raw/Dataset001_VesselSegmentation')\n        dataset_name = \"Dataset 1\"\n    elif dataset_id == 3:\n        dataset_path = Path('/kaggle/working/nnUNet_raw/Dataset003_VesselGrouping')\n        dataset_name = \"Dataset 3\"\n    else:\n        print(f\"❌ Unknown dataset ID: {dataset_id}\")\n        return False\n    \n    if not dataset_path.exists():\n        print(f\"❌ {dataset_name} directory not found!\")\n        return False\n    \n    print(f\"\\n📊 {dataset_name} Verification:\")\n    print(f\"  Path: {dataset_path}\")\n    \n    # Check images\n    images_dir = dataset_path / 'imagesTr'\n    if images_dir.exists():\n        image_files = list(images_dir.glob('*.nii.gz'))\n        print(f\"  ✅ Images: {len(image_files)}\")\n    else:\n        print(f\"  ❌ imagesTr not found\")\n        return False\n    \n    # Check labels\n    labels_dir = dataset_path / 'labelsTr'\n    if labels_dir.exists():\n        label_files = list(labels_dir.glob('*.nii.gz'))\n        print(f\"  ✅ Labels: {len(label_files)}\")\n    else:\n        print(f\"  ❌ labelsTr not found\")\n        return False\n    \n    # Check dataset.json\n    dataset_json = dataset_path / 'dataset.json'\n    if dataset_json.exists():\n        with open(dataset_json, 'r') as f:\n            data = json.load(f)\n        print(f\"  ✅ dataset.json exists\")\n        print(f\"  📄 Training cases: {data.get('numTraining', 'N/A')}\")\n        print(f\"  📄 Labels: {len(data.get('labels', {}))}\")\n    else:\n        print(f\"  ❌ dataset.json not found\")\n        return False\n    \n    if len(image_files) != len(label_files):\n        print(f\"  ⚠️  Warning: Image and label counts don't match!\")\n    \n    print(f\"  ✅ {dataset_name} verification complete!\")\n    return True\n\n\ndef create_both_datasets_standalone(segmentations_dir, max_samples=10):\n    \"\"\"\n    Create both Dataset 1 and Dataset 3 (standalone version).\n    \n    Args:\n        segmentations_dir: Path to segmentations directory\n        max_samples: Maximum number of samples to process (None = all)\n                     Use small number like 5 or 10 for testing\n    \"\"\"\n    # Setup environment\n    setup_nnunet_environment()\n    print()\n    \n    # Create Dataset 1\n    create_dataset_1_standalone(segmentations_dir, max_samples=max_samples)\n    verify_dataset(1)\n    \n    print(\"\\n\" + \"=\" * 60)\n    print()\n    \n    # Create Dataset 3\n    create_dataset_3_standalone(segmentations_dir, max_samples=max_samples)\n    verify_dataset(3)\n    \n    print(\"\\n\" + \"=\" * 60)\n    print(\"✅ Both datasets created successfully!\")\n    if max_samples:\n        print(f\"⚠️  Note: Only {max_samples} samples processed (TEST MODE)\")\n    print(\"=\" * 60)\n\n\nif __name__ == \"__main__\":\n    # Example usage\n    segmentations_dir = '/kaggle/input/rsna-intracranial-aneurysm-detection/segmentations'\n    create_both_datasets_standalone(segmentations_dir)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:00:12.370233Z","iopub.execute_input":"2025-12-08T11:00:12.370554Z","iopub.status.idle":"2025-12-08T11:02:16.469113Z","shell.execute_reply.started":"2025-12-08T11:00:12.370537Z","shell.execute_reply":"2025-12-08T11:02:16.4683Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 4: nnU-Net Preprocessing","metadata":{}},{"cell_type":"code","source":"\"\"\"\nStep 4: nnU-Net Preprocessing\n==============================\nRun nnU-Net planning and preprocessing on the prepared dataset.\n\nThis step:\n1. Analyzes your dataset (spacing, sizes, etc.)\n2. Creates an optimal training plan\n3. Preprocesses data (normalization, resampling)\n4. Saves preprocessed data ready for training\n\nIMPORTANT: You need to install nnU-Net first!\nSee instructions in the function below.\n\"\"\"\n\nimport os\nimport json\nfrom pathlib import Path\n\n\ndef setup_nnunet_environment():\n    \"\"\"\n    Set up nnU-Net environment variables.\n    \n    These must be set before running any nnU-Net commands.\n    \"\"\"\n    os.environ['nnUNet_raw'] = '/kaggle/working/nnUNet_raw'\n    os.environ['nnUNet_preprocessed'] = '/kaggle/working/nnUNet_preprocessed'\n    os.environ['nnUNet_results'] = '/kaggle/working/nnUNet_results'\n    \n    # Create directories\n    for dir_path in [os.environ['nnUNet_raw'], \n                     os.environ['nnUNet_preprocessed'], \n                     os.environ['nnUNet_results']]:\n        Path(dir_path).mkdir(parents=True, exist_ok=True)\n    \n    print(\"✅ nnU-Net environment variables set:\")\n    print(f\"   nnUNet_raw: {os.environ['nnUNet_raw']}\")\n    print(f\"   nnUNet_preprocessed: {os.environ['nnUNet_preprocessed']}\")\n    print(f\"   nnUNet_results: {os.environ['nnUNet_results']}\")\n\n\ndef verify_dataset_exists(dataset_id):\n    \"\"\"\n    Verify that the dataset exists in nnUNet_raw.\n    \n    Args:\n        dataset_id: Dataset ID (1 or 3)\n    \n    Returns:\n        bool: True if dataset exists\n    \"\"\"\n    if dataset_id == 1:\n        dataset_name = \"Dataset001_VesselSegmentation\"\n    elif dataset_id == 3:\n        dataset_name = \"Dataset003_VesselGrouping\"\n    else:\n        print(f\"❌ Unknown dataset ID: {dataset_id}\")\n        return False\n    \n    dataset_path = Path(os.environ['nnUNet_raw']) / dataset_name\n    \n    if not dataset_path.exists():\n        print(f\"❌ Dataset not found: {dataset_path}\")\n        print(\"   Make sure you've run Step 2 first!\")\n        return False\n    \n    # Check for required files\n    images_dir = dataset_path / 'imagesTr'\n    labels_dir = dataset_path / 'labelsTr'\n    dataset_json = dataset_path / 'dataset.json'\n    \n    if not images_dir.exists():\n        print(f\"❌ Images directory not found: {images_dir}\")\n        return False\n    if not labels_dir.exists():\n        print(f\"❌ Labels directory not found: {labels_dir}\")\n        return False\n    if not dataset_json.exists():\n        print(f\"❌ dataset.json not found: {dataset_json}\")\n        return False\n    \n    # Count files\n    num_images = len(list(images_dir.glob('*.nii.gz')))\n    num_labels = len(list(labels_dir.glob('*.nii.gz')))\n    \n    print(f\"✅ Dataset {dataset_id} verified:\")\n    print(f\"   Name: {dataset_name}\")\n    print(f\"   Images: {num_images}\")\n    print(f\"   Labels: {num_labels}\")\n    \n    if num_images == 0 or num_labels == 0:\n        print(\"⚠️  WARNING: No files found in dataset!\")\n        return False\n    \n    return True\n\n\ndef check_preprocessing_results(dataset_id):\n    \"\"\"\n    Check and display preprocessing results.\n    \n    Args:\n        dataset_id: Dataset ID (1 or 3)\n    \"\"\"\n    if dataset_id == 1:\n        dataset_name = \"Dataset001_VesselSegmentation\"\n    elif dataset_id == 3:\n        dataset_name = \"Dataset003_VesselGrouping\"\n    else:\n        print(f\"❌ Unknown dataset ID: {dataset_id}\")\n        return\n    \n    preprocessed_path = Path(os.environ['nnUNet_preprocessed']) / dataset_name\n    \n    if not preprocessed_path.exists():\n        print(f\"❌ Preprocessed data not found: {preprocessed_path}\")\n        print(\"   Preprocessing may not have completed successfully.\")\n        return\n    \n    print(\"\\n\" + \"=\" * 60)\n    print(\"Preprocessing Results\")\n    print(\"=\" * 60)\n    \n    # Check for plans file\n    plans_file = preprocessed_path / 'nnUNetPlans.json'\n    if plans_file.exists():\n        print(f\"✅ Plans file found: {plans_file.name}\")\n        \n        # Try to load and show key info\n        try:\n            with open(plans_file, 'r') as f:\n                plans = json.load(f)\n            \n            if 'configurations' in plans:\n                print(f\"\\n📋 Available configurations:\")\n                for config_name in plans['configurations'].keys():\n                    print(f\"   - {config_name}\")\n                    \n                    config = plans['configurations'][config_name]\n                    if 'patch_size' in config:\n                        print(f\"     Patch size: {config['patch_size']}\")\n                    if 'spacing' in config:\n                        print(f\"     Spacing: {config['spacing']}\")\n        except Exception as e:\n            print(f\"   (Could not parse plans file: {e})\")\n    else:\n        print(f\"⚠️  Plans file not found: {plans_file}\")\n    \n    # Check for preprocessed data\n    preprocessed_dirs = [d for d in preprocessed_path.iterdir() if d.is_dir()]\n    if preprocessed_dirs:\n        print(f\"\\n📁 Preprocessed data directories:\")\n        for d in preprocessed_dirs:\n            num_files = len(list(d.glob('*.npz')))\n            if num_files > 0:\n                print(f\"   {d.name}: {num_files} files\")\n    \n    # Check for gt_segmentations (copied ground truth)\n    gt_dir = preprocessed_path / 'gt_segmentations'\n    if gt_dir.exists():\n        num_gt = len(list(gt_dir.glob('*.nii.gz')))\n        print(f\"\\n✅ Ground truth segmentations: {num_gt} files\")\n    \n    print(f\"\\n✅ Preprocessing complete! Data ready for training.\")\n\n\ndef print_preprocessing_instructions(dataset_id=1, planner='nnUNetPlannerResEncM'):\n    \"\"\"\n    Print instructions for running preprocessing.\n    \n    Args:\n        dataset_id: Dataset ID (1 or 3)\n        planner: Planner class name\n    \"\"\"\n    print(\"=\" * 60)\n    print(\"nnU-Net Preprocessing Instructions\")\n    print(\"=\" * 60)\n    \n    # Setup environment\n    setup_nnunet_environment()\n    \n    # Verify dataset\n    if not verify_dataset_exists(dataset_id):\n        return\n    \n    print(\"\\n\" + \"=\" * 60)\n    print(\"HOW TO RUN PREPROCESSING\")\n    print(\"=\" * 60)\n    \n    print(\"\\n📋 Step 1: Install nnU-Net\")\n    print(\"   Choose ONE of the following options:\")\n    print(\"\\n   Option A: Install from PyPI (Recommended)\")\n    print(\"   !pip install nnunetv2\")\n    print(\"\\n   Option B: Install from repository (if you have the repo)\")\n    print(\"   !cd /kaggle/input/rsna2025-1st-place/rsna2025_1st_place-main/nnUNet\")\n    print(\"   !pip install -e .\")\n    \n    print(\"\\n📋 Step 2: Run Preprocessing Command\")\n    print(\"   After installing nnU-Net, run this command:\")\n    \n    if dataset_id == 1:\n        cmd = f\"!nnUNetv2_plan_and_preprocess -d {dataset_id} --verify_dataset_integrity -pl {planner}\"\n    elif dataset_id == 3:\n        cmd = f\"!nnUNetv2_plan_and_preprocess -d {dataset_id} --verify_dataset_integrity -pl nnUNetPlannerResEncMForcedLowres -overwrite_target_spacing 1.0 1.0 1.0 -c 3d_fullres\"\n    else:\n        cmd = f\"!nnUNetv2_plan_and_preprocess -d {dataset_id} --verify_dataset_integrity -pl {planner}\"\n    \n    print(f\"   {cmd}\")\n    \n    print(\"\\n⏳ This will take 10-30 minutes depending on dataset size.\")\n    print(\"   nnU-Net will:\")\n    print(\"   1. Extract dataset fingerprint\")\n    print(\"   2. Create an optimal training plan\")\n    print(\"   3. Preprocess all images and labels\")\n    \n    print(\"\\n✅ Step 3: Verify Results\")\n    print(\"   After preprocessing completes, check:\")\n    print(f\"   - Plans file: /kaggle/working/nnUNet_preprocessed/Dataset{dataset_id:03d}_*/nnUNetPlans.json\")\n    print(\"   - Preprocessed data directories with .npz files\")\n    \n    print(\"\\n\" + \"=\" * 60)\n\n\ndef preprocess_dataset(dataset_id=1, planner='nnUNetPlannerResEncM'):\n    \"\"\"\n    Main function - prints instructions for running preprocessing.\n    \n    Args:\n        dataset_id: Dataset ID (1 or 3)\n        planner: Planner class name\n    \n    Usage:\n        # For Dataset 1 (13 vessel classes)\n        preprocess_dataset(dataset_id=1)\n        \n        # For Dataset 3 (3 vessel groups)\n        preprocess_dataset(dataset_id=3)\n    \"\"\"\n    print_preprocessing_instructions(dataset_id, planner)\n\n\nif __name__ == \"__main__\":\n    # Example: Show instructions for Dataset 1\n    print(\"Preprocessing Dataset 1 (13 vessel classes)...\")\n    preprocess_dataset(dataset_id=1)\n    \n    # Uncomment to also show instructions for Dataset 3\n    # print(\"\\n\" + \"=\" * 60)\n    # print(\"Preprocessing Dataset 3 (3 vessel groups)...\")\n    # preprocess_dataset(dataset_id=3)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:02:16.470159Z","iopub.execute_input":"2025-12-08T11:02:16.470374Z","iopub.status.idle":"2025-12-08T11:02:16.48895Z","shell.execute_reply.started":"2025-12-08T11:02:16.470356Z","shell.execute_reply":"2025-12-08T11:02:16.488236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!git clone https://github.com/uchiyama33/rsna2025_1st_place.git","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:02:16.489601Z","iopub.execute_input":"2025-12-08T11:02:16.489858Z","iopub.status.idle":"2025-12-08T11:02:17.388631Z","shell.execute_reply.started":"2025-12-08T11:02:16.48983Z","shell.execute_reply":"2025-12-08T11:02:17.387941Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"pip install -e . this command is to setup install from /kaggle/working/rsna2025_1st_place/nnUNet/setup.py","metadata":{}},{"cell_type":"code","source":"!cd /kaggle/working/rsna2025_1st_place/nnUNet && pip install -e .","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:02:17.389538Z","iopub.execute_input":"2025-12-08T11:02:17.389734Z","iopub.status.idle":"2025-12-08T11:03:46.873215Z","shell.execute_reply.started":"2025-12-08T11:02:17.389712Z","shell.execute_reply":"2025-12-08T11:03:46.872205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!nnUNetv2_plan_and_preprocess -d 1 --verify_dataset_integrity -pl nnUNetPlannerResEncM","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:03:46.875881Z","iopub.execute_input":"2025-12-08T11:03:46.876122Z","iopub.status.idle":"2025-12-08T11:15:11.633772Z","shell.execute_reply.started":"2025-12-08T11:03:46.876097Z","shell.execute_reply":"2025-12-08T11:15:11.632855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:15:11.634879Z","iopub.execute_input":"2025-12-08T11:15:11.635156Z","iopub.status.idle":"2025-12-08T11:15:11.639669Z","shell.execute_reply.started":"2025-12-08T11:15:11.635131Z","shell.execute_reply":"2025-12-08T11:15:11.639027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n!nnUNetv2_plan_and_preprocess -d 3 --verify_dataset_integrity \\\n  -pl nnUNetPlannerResEncM \\\n  -overwrite_target_spacing 1.0 1.0 1.0 \\\n  -c 3d_fullres","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:15:11.640329Z","iopub.execute_input":"2025-12-08T11:15:11.640514Z","iopub.status.idle":"2025-12-08T11:17:08.696768Z","shell.execute_reply.started":"2025-12-08T11:15:11.640498Z","shell.execute_reply":"2025-12-08T11:17:08.695981Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Re-set environment variables\n# import os\n# os.environ['nnUNet_raw'] = '/kaggle/working/nnUNet_raw'\n# os.environ['nnUNet_preprocessed'] = '/kaggle/working/nnUNet_preprocessed'\n# os.environ['nnUNet_results'] = '/kaggle/working/nnUNet_results'\n\n# # Verify trainer\n# from nnunetv2.training.nnUNetTrainer.project_specific.rsna2025.more_DAv7 import RSNA2025Trainer_moreDAv7\n# print(\"✅ Ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:17:08.697726Z","iopub.execute_input":"2025-12-08T11:17:08.69806Z","iopub.status.idle":"2025-12-08T11:17:08.701646Z","shell.execute_reply.started":"2025-12-08T11:17:08.698025Z","shell.execute_reply":"2025-12-08T11:17:08.701099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\nStep 4.5: Modify Dataset 3 Patch Size (Before Training Model 1)\n=================================================================\nModify the patch size in Dataset 3 plans.json to [128, 128, 128] and re-preprocess.\n\nThis is required before training Model 1!\n\"\"\"\n\nimport json\nfrom pathlib import Path\nimport os\n\n\ndef find_plans_file(dataset_dir: Path):\n    \"\"\"\n    Find the plans file in the dataset directory.\n    Looks for files ending with 'Plans.json' (e.g., nnUNetPlans.json, nnUNetResEncUNetMPlans.json).\n    \"\"\"\n    # Common plans file names\n    possible_names = [\n        'nnUNetResEncUNetMPlans.json',  # Custom planner\n        'nnUNetPlans.json',  # Standard planner\n    ]\n    \n    # Try specific names first\n    for name in possible_names:\n        plans_file = dataset_dir / name\n        if plans_file.exists():\n            return plans_file\n    \n    # Auto-detect: find any file ending with 'Plans.json'\n    for file in dataset_dir.glob('*Plans.json'):\n        return file\n    \n    return None\n\n\ndef modify_dataset3_patch_size():\n    \"\"\"\n    Modify patch_size in Dataset 3 plans.json to [128, 128, 128].\n    \"\"\"\n    print(\"=\" * 70)\n    print(\"Modifying Dataset 3 Patch Size\")\n    print(\"=\" * 70)\n    \n    # Get preprocessed path\n    nnunet_preprocessed = os.environ.get('nnUNet_preprocessed', '/kaggle/working/nnUNet_preprocessed')\n    dataset_dir = Path(f'{nnunet_preprocessed}/Dataset003_VesselGrouping')\n    \n    if not dataset_dir.exists():\n        print(f\"❌ Dataset directory not found: {dataset_dir}\")\n        print(\"   Make sure you've preprocessed Dataset 3 first!\")\n        return None\n    \n    # Find plans file\n    plans_file = find_plans_file(dataset_dir)\n    \n    if not plans_file:\n        print(f\"❌ Plans file not found in: {dataset_dir}\")\n        print(\"   Looking for files ending with 'Plans.json'\")\n        print(\"   Found files:\")\n        for f in dataset_dir.glob('*.json'):\n            print(f\"     - {f.name}\")\n        print(\"\\n   Make sure you've preprocessed Dataset 3 first!\")\n        return None\n    \n    print(f\"\\n📄 Found plans file: {plans_file.name}\")\n    print(f\"   Full path: {plans_file}\")\n    \n    # Load plans\n    with open(plans_file, 'r') as f:\n        plans = json.load(f)\n    \n    # Check current patch size\n    if 'configurations' in plans:\n        if '3d_fullres' in plans['configurations']:\n            current_patch_size = plans['configurations']['3d_fullres'].get('patch_size', 'Not set')\n            print(f\"\\n📊 Current patch_size: {current_patch_size}\")\n            \n            # Modify patch size\n            plans['configurations']['3d_fullres']['patch_size'] = [128, 128, 128]\n            print(f\"✅ Modified patch_size to: [128, 128, 128]\")\n        else:\n            print(\"❌ Configuration '3d_fullres' not found in plans!\")\n            return None\n    else:\n        print(\"❌ 'configurations' not found in plans!\")\n        return None\n    \n    # Save modified plans\n    print(f\"\\n💾 Saving modified plans file...\")\n    with open(plans_file, 'w') as f:\n        json.dump(plans, f, indent=2)\n    \n    print(f\"✅ Plans file updated: {plans_file}\")\n    \n    # Verify\n    with open(plans_file, 'r') as f:\n        plans_verify = json.load(f)\n        new_patch_size = plans_verify['configurations']['3d_fullres']['patch_size']\n        print(f\"✅ Verified: patch_size = {new_patch_size}\")\n    \n    return plans_file  # Return plans_file path on success\n\n\ndef print_repreprocess_instructions(plans_file_name: str):\n    \"\"\"\n    Print instructions for re-preprocessing Dataset 3.\n    \"\"\"\n    # Extract plans name from file name (remove .json extension)\n    plans_name = plans_file_name.replace('.json', '')\n    \n    print(\"\\n\" + \"=\" * 70)\n    print(\"Next Step: Re-preprocess Dataset 3\")\n    print(\"=\" * 70)\n    \n    print(\"\\n📋 After modifying patch_size, you need to re-preprocess:\")\n    print(f\"\\n   nnUNetv2_preprocess -d 3 -plans_name {plans_name} -c 3d_fullres\")\n    \n    print(\"\\n📋 Command Parameters:\")\n    print(\"   - -d 3 = Dataset ID (Dataset 3)\")\n    print(f\"   - -plans_name {plans_name} = Plans name (from your plans file)\")\n    print(\"   - -c 3d_fullres = Configuration\")\n    \n    print(\"\\n⏱️  Time Estimate:\")\n    print(\"   - ~5-15 minutes (faster than initial preprocessing)\")\n    \n    print(\"\\n💡 Why re-preprocess?\")\n    print(\"   - Preprocessing uses the patch_size from plans.json\")\n    print(\"   - We changed patch_size to [128, 128, 128]\")\n    print(\"   - Need to re-preprocess with new patch size\")\n    \n    print(\"\\n\" + \"=\" * 70)\n\n\ndef main():\n    \"\"\"\n    Main function - modify patch size and show instructions.\n    \"\"\"\n    # Modify patch size (returns plans_file path on success, None on failure)\n    plans_file = modify_dataset3_patch_size()\n    \n    if plans_file:\n        print_repreprocess_instructions(plans_file.name)\n        print(\"\\n✅ Patch size modified! Now run the re-preprocessing command above.\")\n    else:\n        print(\"\\n❌ Failed to modify patch size. Check the error messages above.\")\n\n\nif __name__ == \"__main__\":\n    main()\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:17:08.70263Z","iopub.execute_input":"2025-12-08T11:17:08.702917Z","iopub.status.idle":"2025-12-08T11:17:08.719535Z","shell.execute_reply.started":"2025-12-08T11:17:08.702892Z","shell.execute_reply":"2025-12-08T11:17:08.718986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!nnUNetv2_preprocess -d 3 -plans_name nnUNetResEncUNetMPlans -c 3d_fullres","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:17:08.720285Z","iopub.execute_input":"2025-12-08T11:17:08.720507Z","iopub.status.idle":"2025-12-08T11:18:10.374875Z","shell.execute_reply.started":"2025-12-08T11:17:08.720486Z","shell.execute_reply":"2025-12-08T11:18:10.373884Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Done')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.376091Z","iopub.execute_input":"2025-12-08T11:18:10.376874Z","iopub.status.idle":"2025-12-08T11:18:10.381031Z","shell.execute_reply.started":"2025-12-08T11:18:10.37684Z","shell.execute_reply":"2025-12-08T11:18:10.380398Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from nnunetv2.training.nnUNetTrainer.project_specific.rsna2025.more_DAv7 import RSNA2025Trainer_moreDAv7\nprint(\"✅ Custom trainer imported successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.381725Z","iopub.execute_input":"2025-12-08T11:18:10.381987Z","iopub.status.idle":"2025-12-08T11:18:10.489543Z","shell.execute_reply.started":"2025-12-08T11:18:10.381963Z","shell.execute_reply":"2025-12-08T11:18:10.488239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/working/rsna2025_1st_place/nnUNet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.490153Z","iopub.status.idle":"2025-12-08T11:18:10.490446Z","shell.execute_reply.started":"2025-12-08T11:18:10.490281Z","shell.execute_reply":"2025-12-08T11:18:10.490297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip list | grep nnunet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.491937Z","iopub.status.idle":"2025-12-08T11:18:10.492202Z","shell.execute_reply.started":"2025-12-08T11:18:10.492083Z","shell.execute_reply":"2025-12-08T11:18:10.492097Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos._exit(0)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.492967Z","iopub.status.idle":"2025-12-08T11:18:10.493195Z","shell.execute_reply.started":"2025-12-08T11:18:10.493089Z","shell.execute_reply":"2025-12-08T11:18:10.493098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nnunetv2\nprint(f\"✅ nnunetv2 installed at: {nnunetv2.__file__}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.494192Z","iopub.status.idle":"2025-12-08T11:18:10.494926Z","shell.execute_reply.started":"2025-12-08T11:18:10.494713Z","shell.execute_reply":"2025-12-08T11:18:10.494732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom pathlib import Path\n\nos.environ['nnUNet_raw'] = '/kaggle/working/nnUNet_raw'\nos.environ['nnUNet_preprocessed'] = '/kaggle/working/nnUNet_preprocessed'\nos.environ['nnUNet_results'] = '/kaggle/working/nnUNet_results'\n\n# Create directories\nfor var in ['nnUNet_raw', 'nnUNet_preprocessed', 'nnUNet_results']:\n    Path(os.environ[var]).mkdir(parents=True, exist_ok=True)\n\nprint(\"✅ Environment variables set!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.495869Z","iopub.status.idle":"2025-12-08T11:18:10.496345Z","shell.execute_reply.started":"2025-12-08T11:18:10.496225Z","shell.execute_reply":"2025-12-08T11:18:10.496237Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from nnunetv2.training.nnUNetTrainer.project_specific.rsna2025.more_DAv7 import RSNA2025Trainer_moreDAv7\nprint(\"✅ Custom trainer imported successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.49719Z","iopub.status.idle":"2025-12-08T11:18:10.497473Z","shell.execute_reply.started":"2025-12-08T11:18:10.497317Z","shell.execute_reply":"2025-12-08T11:18:10.497332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\nStep 5: Train Model 1 (Coarse Localization)\n===========================================\nTrain Model 1 on Dataset 3 for fast ROI detection.\n\"\"\"\n\nimport os\nfrom pathlib import Path\n\n\ndef find_plans_file(dataset_dir: Path):\n    \"\"\"\n    Find the plans file in the dataset directory.\n    Looks for files ending with 'Plans.json' (e.g., nnUNetPlans.json, nnUNetResEncUNetMPlans.json).\n    \"\"\"\n    # Common plans file names\n    possible_names = [\n        'nnUNetResEncUNetMPlans.json',  # Custom planner\n        'nnUNetPlans.json',  # Standard planner\n    ]\n    \n    # Try specific names first\n    for name in possible_names:\n        plans_file = dataset_dir / name\n        if plans_file.exists():\n            return plans_file\n    \n    # Auto-detect: find any file ending with 'Plans.json'\n    for file in dataset_dir.glob('*Plans.json'):\n        return file\n    \n    return None\n\n\ndef check_prerequisites():\n    \"\"\"\n    Check if prerequisites are met before training.\n    \"\"\"\n    print(\"=\" * 70)\n    print(\"Checking Prerequisites for Model 1 Training\")\n    print(\"=\" * 70)\n    \n    # Check environment variables\n    print(\"\\n📋 Environment Variables:\")\n    nnunet_raw = os.environ.get('nnUNet_raw', None)\n    nnunet_preprocessed = os.environ.get('nnUNet_preprocessed', None)\n    nnunet_results = os.environ.get('nnUNet_results', None)\n    \n    if nnunet_raw:\n        print(f\"   ✅ nnUNet_raw: {nnunet_raw}\")\n    else:\n        print(f\"   ❌ nnUNet_raw: NOT SET\")\n    \n    if nnunet_preprocessed:\n        print(f\"   ✅ nnUNet_preprocessed: {nnunet_preprocessed}\")\n    else:\n        print(f\"   ❌ nnUNet_preprocessed: NOT SET\")\n    \n    if nnunet_results:\n        print(f\"   ✅ nnUNet_results: {nnunet_results}\")\n    else:\n        print(f\"   ❌ nnUNet_results: NOT SET\")\n    \n    # Check Dataset 3 preprocessing\n    print(\"\\n📋 Dataset 3 Preprocessing:\")\n    preprocessed_path = Path(f'{nnunet_preprocessed}/Dataset003_VesselGrouping')\n    if preprocessed_path.exists():\n        print(f\"   ✅ Preprocessed directory exists: {preprocessed_path}\")\n        \n        # Find plans file\n        plans_file = find_plans_file(preprocessed_path)\n        if plans_file:\n            print(f\"   ✅ Plans file found: {plans_file.name}\")\n            # Extract plans name for later use\n            plans_name = plans_file.name.replace('.json', '')\n        else:\n            print(f\"   ❌ Plans file not found!\")\n            print(\"   Looking for files ending with 'Plans.json'\")\n            print(\"   Found files:\")\n            for f in preprocessed_path.glob('*.json'):\n                print(f\"     - {f.name}\")\n            return None\n    else:\n        print(f\"   ❌ Preprocessed directory not found: {preprocessed_path}\")\n        print(\"   Make sure you've run preprocessing for Dataset 3!\")\n        return None\n    \n    # Check custom trainer\n    print(\"\\n📋 Custom Trainer:\")\n    try:\n        # Try direct import from project_specific\n        from nnunetv2.training.nnUNetTrainer.project_specific.rsna2025.more_DAv7 import RSNA2025Trainer_moreDAv7\n        print(\"   ✅ RSNA2025Trainer_moreDAv7 available\")\n    except ImportError:\n        # Fallback: try the standard import (for backward compatibility)\n        try:\n            from nnunetv2.training.nnUNetTrainer import RSNA2025Trainer_moreDAv7\n            print(\"   ✅ RSNA2025Trainer_moreDAv7 available (via standard import)\")\n        except ImportError:\n            print(\"   ❌ RSNA2025Trainer_moreDAv7 not found!\")\n            print(\"\\n   📋 Installation Steps:\")\n            print(\"   1. Find your repository location (common paths):\")\n            print(\"      - /kaggle/working/rsna2025_1st_place\")\n            print(\"      - /kaggle/working/rsna2025_1st_place-main\")\n            print(\"\\n   2. Run this command (NOTE the '.' at the end!):\")\n            print(\"      !cd /kaggle/working/rsna2025_1st_place/nnUNet && pip install -e .\")\n            print(\"\\n   3. ⚠️  RESTART THE KERNEL after installation!\")\n            print(\"      In Kaggle: Kernel → Restart Session\")\n            print(\"      In Jupyter: Kernel → Restart\")\n            print(\"\\n   4. Re-run this script to verify installation\")\n            print(\"\\n   💡 Tip: Use scripts/install_nnunet_from_repo.py for help\")\n            return None\n    \n    # Check results directory\n    print(\"\\n📋 Results Directory:\")\n    results_path = Path(f'{nnunet_results}/Dataset003_VesselGrouping')\n    results_path.mkdir(parents=True, exist_ok=True)\n    print(f\"   ✅ Results directory ready: {results_path}\")\n    \n    print(\"\\n\" + \"=\" * 70)\n    print(\"✅ All prerequisites met! Ready to train Model 1.\")\n    print(\"=\" * 70)\n    \n    # Return plans_name on success\n    return plans_name\n\n\ndef print_training_instructions(test_mode=False, plans_name='nnUNetResEncUNetMPlans'):\n    \"\"\"\n    Print instructions for training Model 1.\n    \n    Args:\n        test_mode: If True, train only fold 0 (for testing)\n        plans_name: Name of the plans file (without .json extension)\n    \"\"\"\n    print(\"\\n\" + \"=\" * 70)\n    print(\"Model 1 Training Instructions\")\n    print(\"=\" * 70)\n    \n    print(\"\\n📋 Model 1 Details:\")\n    print(\"   - Dataset: Dataset 3 (3 vessel groups)\")\n    print(\"   - Purpose: Coarse localization (fast ROI detection)\")\n    print(\"   - Spacing: 1.0mm (low resolution, fast)\")\n    print(\"   - Trainer: RSNA2025Trainer_moreDAv7\")\n    print(f\"   - Plans: {plans_name}\")\n    \n    print(\"\\n📋 Training Command:\")\n    if test_mode:\n        print(\"   # Test mode: Train only fold 0 (faster, for testing)\")\n        cmd = f\"nnUNetv2_train 3 3d_fullres 0 -p {plans_name} -tr RSNA2025Trainer_moreDAv7\"\n    else:\n        print(\"   # Full training: Train all 5 folds\")\n        cmd = f\"nnUNetv2_train 3 3d_fullres all -p {plans_name} -tr RSNA2025Trainer_moreDAv7\"\n    \n    print(f\"\\n   {cmd}\")\n    \n    print(\"\\n📋 Command Parameters:\")\n    print(\"   - 3 = Dataset ID (Dataset 3)\")\n    print(\"   - 3d_fullres = Configuration (full 3D resolution)\")\n    print(\"   - all (or 0) = Folds (all 5 folds, or fold 0 for testing)\")\n    print(f\"   - -p {plans_name} = Plans name (from your plans file)\")\n    print(\"   - -tr RSNA2025Trainer_moreDAv7 = Custom trainer\")\n    \n    print(\"\\n⏱️  Time Estimate:\")\n    if test_mode:\n        print(\"   - Single fold: ~2-3 hours on RTX 4090\")\n    else:\n        print(\"   - All 5 folds: ~8-12 hours on RTX 4090\")\n        print(\"   - Longer on Kaggle T4 GPUs\")\n    \n    print(\"\\n📁 Model Output:\")\n    results_dir = os.environ.get('nnUNet_results', '/kaggle/working/nnUNet_results')\n    print(f\"   Models saved to:\")\n    print(f\"   {results_dir}/Dataset003_VesselGrouping/\")\n    print(f\"   RSNA2025Trainer_moreDAv7__{plans_name}__3d_fullres/\")\n    print(f\"   ├── fold_0/\")\n    print(f\"   │   ├── checkpoint_final.pth\")\n    print(f\"   │   └── checkpoint_latest.pth\")\n    print(f\"   ├── fold_1/\")\n    print(f\"   └── ...\")\n    \n    print(\"\\n💡 Tips:\")\n    print(\"   - Start with fold 0 to test (faster)\")\n    print(\"   - Monitor training progress in terminal\")\n    print(\"   - Checkpoints saved automatically\")\n    print(\"   - Best model saved as checkpoint_final.pth\")\n    \n    print(\"\\n\" + \"=\" * 70)\n    print(\"Ready to train! Copy the command above and run it.\")\n    print(\"=\" * 70)\n\n\ndef train_model1(test_mode=False):\n    \"\"\"\n    Main function - checks prerequisites and prints training instructions.\n    \n    Args:\n        test_mode: If True, train only fold 0 (for testing)\n    \"\"\"\n    plans_name = check_prerequisites()\n    \n    if plans_name:\n        print_training_instructions(test_mode=test_mode, plans_name=plans_name)\n    else:\n        print(\"\\n❌ Prerequisites not met. Please fix the issues above.\")\n\n\nif __name__ == \"__main__\":\n    # Check prerequisites and show instructions\n    train_model1(test_mode=False)\n    \n    # Uncomment to show test mode instructions\n    # print(\"\\n\\n\")\n    # train_model1(test_mode=True)\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.498168Z","iopub.status.idle":"2025-12-08T11:18:10.49839Z","shell.execute_reply.started":"2025-12-08T11:18:10.498281Z","shell.execute_reply":"2025-12-08T11:18:10.498293Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!nnUNetv2_train 3 3d_fullres 0 -p nnUNetResEncUNetMPlans -tr RSNA2025Trainer_moreDAv7","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.499306Z","iopub.status.idle":"2025-12-08T11:18:10.499663Z","shell.execute_reply.started":"2025-12-08T11:18:10.499518Z","shell.execute_reply":"2025-12-08T11:18:10.499538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T11:18:10.500376Z","iopub.status.idle":"2025-12-08T11:18:10.500654Z","shell.execute_reply.started":"2025-12-08T11:18:10.500489Z","shell.execute_reply":"2025-12-08T11:18:10.5005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}