{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.9","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":24800,"databundleVersionId":1831594,"sourceType":"competition"},{"sourceId":18613,"sourceType":"datasetVersion","datasetId":5839},{"sourceId":1154184,"sourceType":"datasetVersion","datasetId":652413},{"sourceId":1219292,"sourceType":"datasetVersion","datasetId":648709},{"sourceId":1793625,"sourceType":"datasetVersion","datasetId":1065975},{"sourceId":1793670,"sourceType":"datasetVersion","datasetId":1066006},{"sourceId":1986867,"sourceType":"datasetVersion","datasetId":1187840},{"sourceId":2037801,"sourceType":"datasetVersion","datasetId":1220417},{"sourceId":2065241,"sourceType":"datasetVersion","datasetId":1199045},{"sourceId":2069616,"sourceType":"datasetVersion","datasetId":1237872},{"sourceId":3951115,"sourceType":"datasetVersion","datasetId":1027206},{"sourceId":11382644,"sourceType":"datasetVersion","datasetId":7127269},{"sourceId":224230832,"sourceType":"kernelVersion"}],"dockerImageVersionId":30056,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --no-deps '../input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl' -q\n!pip install '../input/mean-average-precision-for-boxes/map_boxes-1.0.5-py3-none-any.whl' -q\n!pip install '../input/pytorchlightning/pytorch_lightning-1.2.4-py3-none-any.whl' -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:28.314925Z","iopub.execute_input":"2025-04-26T17:59:28.315277Z","iopub.status.idle":"2025-04-26T17:59:40.870276Z","shell.execute_reply.started":"2025-04-26T17:59:28.315248Z","shell.execute_reply":"2025-04-26T17:59:40.869395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\n\nsys.path.insert(0, \"../input/omegaconf/omegaconf-master\")\nsys.path.insert(0, \"../input/efficientdetpytorch/efficientdet-pytorch-master\")\nsys.path.insert(0, \"../input/vbd-chest-xray-script\")\nsys.path.insert(0, \"../input/weightedboxesfusion\")\nsys.path.insert(0, \"../input/timm-pytorch-image-models/pytorch-image-models-master\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:40.872171Z","iopub.execute_input":"2025-04-26T17:59:40.872475Z","iopub.status.idle":"2025-04-26T17:59:40.876966Z","shell.execute_reply.started":"2025-04-26T17:59:40.872435Z","shell.execute_reply":"2025-04-26T17:59:40.876122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport pytorch_lightning as pl\n\nfrom tqdm import tqdm\n\nfrom models import XrayClassifier, XrayDetector\nfrom datamodule import XrayTestDataModule, XrayTestEnsembleDataModule\nfrom ensemble_boxes import *\nimport torchvision.ops as ops\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport numpy as np\nimport pydicom\nimport cv2\nimport torch\nimport torchvision.ops as ops","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:40.878271Z","iopub.execute_input":"2025-04-26T17:59:40.878525Z","iopub.status.idle":"2025-04-26T17:59:40.894094Z","shell.execute_reply.started":"2025-04-26T17:59:40.878501Z","shell.execute_reply":"2025-04-26T17:59:40.893283Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"de htrg3 el image_id --> predictions using model","metadata":{}},{"cell_type":"markdown","source":"-----","metadata":{}},{"cell_type":"code","source":"\ndevice = (\n    torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n)\nprint(f\"device {device}\")\n\ntorch.set_grad_enabled(False)\n\n#  -------------------------------------------\nmodel =  XrayDetector.load_from_checkpoint(\n            \"../input/vbd-final-checkpoint/d4-896-all-aug-bn-nms-v2-5folds-0_VIN-412_checkpoints_xray-detector-epoch046-val_loss0.3329.ckpt\",\n            pretrained=False,\n            pretrained_backbone=False,\n            image_size=896\n        )\n  \n\n# for m in models:\nmodel.to(device)\n#  -------------------------------------------\ndef convert_batch_pred(prediction, height, width, resize_height, resize_width):\n\n    boxes = prediction[:, :, :4].detach().cpu().numpy()\n    scores = prediction[:, :, 4].detach().cpu().numpy()\n    labels = prediction[:, :, 5].detach().cpu().numpy().astype(np.int32)\n    labels -= 1        \n    height = height.detach().cpu().numpy()\n    width = width.detach().cpu().numpy()\n    boxes[:, :, 0] = boxes[:, :, 0] * width / resize_width\n    boxes[:, :, 1] = boxes[:, :, 1] * height / resize_height\n    boxes[:, :, 2] = boxes[:, :, 2] * width / resize_width\n    boxes[:, :, 3] = boxes[:, :, 3] * height / resize_height\n\n    boxes = boxes.astype(np.int32)\n\n    boxes[:, :, 0] = boxes[:, :, 0].clip(min=0, max=width - 1)\n    boxes[:, :, 1] = boxes[:, :, 1].clip(min=0, max=height - 1)\n    boxes[:, :, 2] = boxes[:, :, 2].clip(min=0, max=width - 1)\n    boxes[:, :, 3] = boxes[:, :, 3].clip(min=0, max=height - 1)\n    \n    return boxes, scores, labels\n#  -------------------------------------------\n\n\n# Define the transformation pipeline (same as in XrayTestEnsembleDataset)\ndef transform(image_size=896):\n    return A.Compose([\n        A.Resize(height=image_size, width=image_size),  # Resize to specified size\n        A.Normalize(),  # Normalize using Albumentations' default normalization\n        ToTensorV2()  # Convert to PyTorch tensor and permute to (C, H, W)\n    ])\n\n# Read DICOM file\ndcm_path = \"/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/321c111713c3ee5385db0effb54ff568.dicom\"\ndcm = pydicom.dcmread(dcm_path)\npixel_array = dcm.pixel_array.astype(np.float32)\n\noriginal_height = dcm.Rows\noriginal_width = dcm.Columns\n\n# Create tensors for these dimensions\nheight_tensor = torch.tensor([original_height], dtype=torch.float32).to(device)\nwidth_tensor = torch.tensor([original_width], dtype=torch.float32).to(device)\n# -----------------------------------------------------------------\n# Apply proper DICOM preprocessing\npixel_array = dcm.pixel_array.astype(np.float32)\n\n# Apply Rescale Slope and Intercept if available\nif hasattr(dcm, 'RescaleSlope'):\n    pixel_array *= dcm.RescaleSlope\nif hasattr(dcm, 'RescaleIntercept'):\n    pixel_array += dcm.RescaleIntercept\n\n# Handle photometric interpretation (invert if MONOCHROME1)\nphotometric = dcm.get('PhotometricInterpretation', 'MONOCHROME2')\nif photometric == \"MONOCHROME1\":\n    pixel_array = pixel_array.max() - pixel_array\n\n# Normalize to 0-1 range\npixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min() + 1e-6)\n\n# -----------------------------------------------------------------\n\n\n# Convert grayscale to 3-channel RGB (same as in XrayTestEnsembleDataset)\npixel_array = cv2.cvtColor(pixel_array, cv2.COLOR_GRAY2RGB)\n\n# Apply transformations\ntransform_pipeline = transform(image_size=896)  # Use the same image size as in the dataset\ntransformed = transform_pipeline(image=pixel_array)\ninput_tensor = transformed[\"image\"].unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n# Print final tensor shape\nprint(f\"Input tensor shape: {input_tensor.shape}\")\n\n\n\n#  -------------------------------------------\n\npred = model(input_tensor.to(device))\n\ndef class_wise_nms(boxes, scores, labels, iou_threshold=0.5, score_threshold=0.3):\n    \"\"\"\n    Apply Non-Maximum Suppression per class to preserve multiple diseases\n    \"\"\"\n    # First filter by score threshold\n    mask = scores > score_threshold\n    boxes = boxes[mask]\n    scores = scores[mask]\n    labels = labels[mask]\n    \n    if len(boxes) == 0:\n        return boxes, scores, labels\n    \n    # Get unique classes\n    unique_classes = np.unique(labels)\n    \n    keep_boxes = []\n    keep_scores = []\n    keep_labels = []\n    \n    # Apply NMS for each class separately\n    for cls in unique_classes:\n        cls_mask = labels == cls\n        cls_boxes = boxes[cls_mask]\n        cls_scores = scores[cls_mask]\n        \n        # Convert to tensor for torchvision NMS - ensure float32 type\n        cls_boxes_tensor = torch.from_numpy(cls_boxes).float().to(device)  # Convert to float\n        cls_scores_tensor = torch.from_numpy(cls_scores).float().to(device)  # Convert to float\n        \n        # Handle case where boxes might need to be in [x1, y1, x2, y2] format\n        if cls_boxes_tensor.size(0) > 0:\n            # Make sure boxes are in the correct format [x1, y1, x2, y2]\n            # and ensure x1 < x2 and y1 < y2 which NMS requires\n            x1 = torch.min(cls_boxes_tensor[:, 0], cls_boxes_tensor[:, 2])\n            y1 = torch.min(cls_boxes_tensor[:, 1], cls_boxes_tensor[:, 3])\n            x2 = torch.max(cls_boxes_tensor[:, 0], cls_boxes_tensor[:, 2])\n            y2 = torch.max(cls_boxes_tensor[:, 1], cls_boxes_tensor[:, 3])\n            cls_boxes_tensor = torch.stack([x1, y1, x2, y2], dim=1)\n            \n            # Apply NMS\n            keep_indices = ops.nms(cls_boxes_tensor, cls_scores_tensor, iou_threshold)\n            \n            # Convert back to numpy and add kept boxes to result\n            keep_boxes.append(cls_boxes[keep_indices.cpu().numpy()])\n            keep_scores.append(cls_scores[keep_indices.cpu().numpy()])\n            keep_labels.append(np.full(len(keep_indices), cls))\n    \n    # Combine results from all classes\n    if keep_boxes and any(len(box_array) > 0 for box_array in keep_boxes):\n        return np.concatenate([b for b in keep_boxes if len(b) > 0]), \\\n               np.concatenate([s for s in keep_scores if len(s) > 0]), \\\n               np.concatenate([l for l in keep_labels if len(l) > 0])\n    else:\n        return np.array([]), np.array([]), np.array([])\n\nboxes, scores, labels = convert_batch_pred(pred, height_tensor, width_tensor, 896, 896)\n\nprint(labels.shape)\n# Apply class-wise NMS\nfor i in range(len(boxes)):\n    filtered_boxes, filtered_scores, filtered_labels = class_wise_nms(\n        boxes[i], scores[i], labels[i], iou_threshold=0.5, score_threshold=0.4\n    )\n    \n    # Print all detections\n    for j in range(len(filtered_boxes)):\n        print(f\"Detection {j+1}: Class {filtered_labels[j]} with confidence {filtered_scores[j]:.3f}\")\n        print(f\"Bounding box: {filtered_boxes[j]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:40.895634Z","iopub.execute_input":"2025-04-26T17:59:40.895969Z","iopub.status.idle":"2025-04-26T17:59:43.341045Z","shell.execute_reply.started":"2025-04-26T17:59:40.895936Z","shell.execute_reply":"2025-04-26T17:59:43.34008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"---","metadata":{}},{"cell_type":"markdown","source":"Visualization of detection Results\n","metadata":{}},{"cell_type":"code","source":"\n# Function to preprocess DICOM image\ndef preprocess_dicom(dcm_path):\n    # Read DICOM file\n    dcm = pydicom.dcmread(dcm_path)\n    pixel_array = dcm.pixel_array.astype(np.float32)\n    \n    \n    \n    \n    # Normalize to 0-1 range\n    pixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min() + 1e-6)\n    \n    # Store original dimensions\n    original_height = dcm.Rows\n    original_width = dcm.Columns\n    \n    # Convert to RGB for visualization (keeping a copy of the original)\n    original_img = pixel_array.copy()\n    rgb_img = cv2.cvtColor(pixel_array, cv2.COLOR_GRAY2RGB)\n    \n    return rgb_img, original_img, original_height, original_width, dcm\n\ndef visualize_predictions(original_img, boxes, scores, labels, class_names=None, threshold=0.3):\n    # Convert grayscale to RGB for visualization\n    if len(original_img.shape) == 2:\n        display_img = cv2.cvtColor((original_img * 255).astype(np.uint8), cv2.COLOR_GRAY2RGB)\n    else:\n        display_img = (original_img * 255).astype(np.uint8)\n    \n    # Create figure and axis\n    plt.figure(figsize=(12, 12))\n    plt.imshow(display_img, cmap='gray')\n    ax = plt.gca()\n    \n    # Default class names if not provided\n    if class_names is None:\n        class_names = [f\"Class {i}\" for i in range(15)]  # Assuming max 15 classes\n    \n    # Define colors for different classes\n    colors = plt.cm.rainbow(np.linspace(0, 1, len(class_names)))\n    \n    # Counter for valid detections\n    valid_detections = 0\n    \n    # Draw each bounding box\n    for i, (box, score, label) in enumerate(zip(boxes, scores, labels)):\n        if score < threshold:\n            continue\n            \n        valid_detections += 1\n        \n        # Get coordinates\n        x1, y1, x2, y2 = box\n        \n        # Get color for this class\n        color = colors[int(label) % len(colors)]\n        color = (color[0], color[1], color[2])\n        \n        # Create rectangle patch\n        rect = patches.Rectangle(\n            (x1, y1), x2-x1, y2-y1, \n            linewidth=2, \n            edgecolor=color, \n            facecolor='none'\n        )\n        \n        # Add rectangle to plot\n        ax.add_patch(rect)\n        \n        # Add label text\n        class_name = class_names[int(label)] if int(label) < len(class_names) else f\"Class {label}\"\n        plt.text(\n            x1, y1-5, \n            f\"{class_name}: {score:.2f}\", \n            color='white', \n            fontsize=12, \n            bbox=dict(facecolor=color, alpha=0.7, edgecolor='none', pad=2)\n        )\n    \n    plt.title(f\"Detected {valid_detections} findings\")\n    plt.axis('off')\n    plt.tight_layout()\n    return plt\n\nvinbigdata_class_names = [\n\"Aortic enlargement\", #m4 m3ana\n\"Atelectasis\",\n\"Calcification\", #m4 m3ana\n\"Cardiomegaly\",\n\"Consolidation\",\n\"ILD\", #m4 m3ana\n\"Infiltration\",\n\"Lung Opacity\", # m4 m3ana\n\"Nodule/Mass\",\n\"Other lesion\",\n\"Pleural effusion\",\n\"Pleural thickening\",\n\"Pneumothorax\",\n\"Pulmonary fibrosis\"\n]\nrgb_img, original_img, original_height, original_width, dcm = preprocess_dicom(dcm_path)\nvisualize_predictions(original_img, filtered_boxes, filtered_scores, filtered_labels, class_names=vinbigdata_class_names, threshold=0.3)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:48.975978Z","iopub.execute_input":"2025-04-26T17:59:48.976236Z","iopub.status.idle":"2025-04-26T17:59:49.9554Z","shell.execute_reply.started":"2025-04-26T17:59:48.976214Z","shell.execute_reply":"2025-04-26T17:59:49.95463Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"visualization of ground truth boxes","metadata":{}},{"cell_type":"code","source":"import pandas\nfrom matplotlib.patches import Rectangle\ndef display_bboxes_on_dicom(csv_path, image_id, dcm_path):\n    \"\"\"\n    Display bounding boxes on a DICOM chest X-ray image based on detections in a CSV file.\n    \n    Args:\n        csv_path (str): Path to the CSV file containing ground truth detections\n        image_id (str): The image ID to display\n        dcm_path (str): Path to the DICOM file\n    \"\"\"\n    # Read and preprocess the DICOM image\n    rgb_img, original_img, original_height, original_width, dcm = preprocess_dicom(dcm_path)\n    \n    # Read the CSV file\n    df = pd.read_csv(csv_path)\n    \n    # Filter rows for the specified image_id\n    detections = df[df['image_id'] == image_id]\n    \n    # Check if any detections exist for this image\n    if detections.empty:\n        print(f\"No detections found for image ID: {image_id}\")\n        return\n    \n    # Create figure and axes\n    fig, ax = plt.subplots(1, figsize=(12, 12))\n    \n    # Display the image (using the RGB version for visualization)\n    ax.imshow(rgb_img, cmap='gray')\n    \n    # Dictionary to map class_id to color for consistent coloring\n    class_colors = {}\n    colors = plt.cm.get_cmap('tab20', len(detections['class_id'].unique()))\n    \n    # Draw each bounding box\n    for idx, row in detections.iterrows():\n        # Skip if no bounding box coordinates (like for \"No finding\")\n        if pd.isna(row['x_min']) or pd.isna(row['y_min']) or pd.isna(row['x_max']) or pd.isna(row['y_max']):\n            continue\n            \n        class_id = row['class_id']\n        class_name = row['class_name']\n        \n        # Get or assign a color for this class\n        if class_id not in class_colors:\n            class_colors[class_id] = colors(len(class_colors))\n        \n        # Create a rectangle patch\n        bbox = Rectangle((row['x_min'], row['y_min']),\n                         row['x_max'] - row['x_min'],\n                         row['y_max'] - row['y_min'],\n                         linewidth=2,\n                         edgecolor=class_colors[class_id],\n                         facecolor='none')\n        \n        # Add the patch to the Axes\n        ax.add_patch(bbox)\n        \n        # Add class name as text\n        ax.text(row['x_min'], row['y_min'] - 5, \n                f\"{class_name} (R{row['rad_id']})\", \n                color=class_colors[class_id],\n                fontsize=10,\n                bbox=dict(facecolor='white', alpha=0.7, edgecolor='none'))\n    \n    plt.title(f\"Image ID: {image_id}\\nDetections: {len(detections)}\")\n    plt.axis('off')\n    plt.show()\n\ndef preprocess_dicom(dcm_path):\n    \"\"\"Your provided DICOM preprocessing function\"\"\"\n    # Read DICOM file\n    dcm = pydicom.dcmread(dcm_path)\n    pixel_array = dcm.pixel_array.astype(np.float32)\n    \n    # Normalize to 0-1 range\n    pixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min() + 1e-6)\n    \n    # Store original dimensions\n    original_height = dcm.Rows\n    original_width = dcm.Columns\n    \n    # Convert to RGB for visualization (keeping a copy of the original)\n    original_img = pixel_array.copy()\n    rgb_img = cv2.cvtColor(pixel_array, cv2.COLOR_GRAY2RGB)\n    \n    return rgb_img, original_img, original_height, original_width, dcm\n\n# Example usage:\ndisplay_bboxes_on_dicom('/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train.csv', '321c111713c3ee5385db0effb54ff568', dcm_path)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T17:59:49.956832Z","iopub.execute_input":"2025-04-26T17:59:49.957199Z","iopub.status.idle":"2025-04-26T17:59:51.370836Z","shell.execute_reply.started":"2025-04-26T17:59:49.957165Z","shell.execute_reply":"2025-04-26T17:59:51.369886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip freeze > requirements.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-26T18:05:56.037344Z","iopub.execute_input":"2025-04-26T18:05:56.037729Z","iopub.status.idle":"2025-04-26T18:05:59.484185Z","shell.execute_reply.started":"2025-04-26T18:05:56.037698Z","shell.execute_reply":"2025-04-26T18:05:59.483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}