{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":10338,"databundleVersionId":862042,"sourceType":"competition"}],"dockerImageVersionId":30823,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        os.path.join(dirname, filename)\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:53:44.624414Z","iopub.execute_input":"2025-01-07T08:53:44.624721Z","iopub.status.idle":"2025-01-07T08:54:00.212517Z","shell.execute_reply.started":"2025-01-07T08:53:44.6247Z","shell.execute_reply":"2025-01-07T08:54:00.211848Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data import Dataset\nimport torchvision\nfrom torchvision.models.detection import fasterrcnn_resnet50_fpn\nfrom torchvision.datasets import ImageFolder\nfrom torchvision import transforms\nimport torchvision.transforms as T\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nimport pydicom\nimport math\nimport cv2 as cv\nimport tensorflow as tf\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:00.213387Z","iopub.execute_input":"2025-01-07T08:54:00.213601Z","iopub.status.idle":"2025-01-07T08:54:10.036435Z","shell.execute_reply.started":"2025-01-07T08:54:00.213583Z","shell.execute_reply":"2025-01-07T08:54:10.035752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load the pre-trained Faster R-CNN model with a ResNet-50 backbone\nmodel = fasterrcnn_resnet50_fpn(weights=True)\n\n# Number of classes (your dataset classes + 1 for background)\nnum_classes = 2  # For example, 2 classes + background\n\n# Get the number of input features for the classifier\nin_features = model.roi_heads.box_predictor.cls_score.in_features\n\n# Replace the head of the model with a new one (for the number of classes in your dataset)\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:10.037672Z","iopub.execute_input":"2025-01-07T08:54:10.038176Z","iopub.status.idle":"2025-01-07T08:54:11.624805Z","shell.execute_reply.started":"2025-01-07T08:54:10.038153Z","shell.execute_reply":"2025-01-07T08:54:11.624076Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Formatting Data","metadata":{}},{"cell_type":"code","source":"input_size = 244\n\ndef format_image(img, box):\n    height, width = img.shape \n    max_size = max(height, width)\n    r = max_size / input_size\n    new_width = int(width / r)\n    new_height = int(height / r)\n    new_size = (new_width, new_height)\n    resized = cv.resize(img, new_size, interpolation= cv.INTER_LINEAR)\n    new_image = np.zeros((input_size, input_size), dtype=np.uint8)\n    new_image[0:new_height, 0:new_width] = resized\n\n    x, y, w, h = (box[0], box[1], box[2], box[3]) if box[0] else (0.0,0.0,0.0,0.0)\n    new_box = [int((x)/ r), int((y)/ r), int(w/ r), int(h/ r)] if box[0] else [0.0,0.0,0.0,0.0]\n\n    return new_image, new_box","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.62598Z","iopub.execute_input":"2025-01-07T08:54:11.626218Z","iopub.status.idle":"2025-01-07T08:54:11.631665Z","shell.execute_reply.started":"2025-01-07T08:54:11.626197Z","shell.execute_reply":"2025-01-07T08:54:11.63087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torchvision import transforms as T\n\n# 定義轉換器\ntransform = T.Compose([\n    T.ToTensor(),                     # 確保影像轉換為 PyTorch Tensor\n    T.Normalize(mean=[0.5], std=[0.5]) # 正規化\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.632568Z","iopub.execute_input":"2025-01-07T08:54:11.632823Z","iopub.status.idle":"2025-01-07T08:54:11.648191Z","shell.execute_reply.started":"2025-01-07T08:54:11.63277Z","shell.execute_reply":"2025-01-07T08:54:11.6474Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define transformations (e.g., resizing, normalization)\ntransform = T.Compose([\n    T.ToTensor(),\n])\n# Custom Dataset class or using an existing one\nclass PneumoniaDataset(Dataset):\n    def __init__(self, dataframe, transforms=None):\n        dataframe = dataframe.reset_index(drop=True)\n        # Initialize dataset paths and annotations here\n        self.transforms = transforms\n        # Your dataset logic (image paths, annotations, etc.)\n        self.dataframe = dataframe\n\n    def __getitem__(self, idx):\n        row = self.dataframe.iloc[idx]\n        img_path = row['patientId']\n        img_path = \"/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_images/\"+img_path+\".dcm\"\n        # Load DICOM image\n        temp_img = pydicom.dcmread(img_path).pixel_array\n        # 確認標註框是否有效\n        temp_box = [row['x'], row['y'], row['width'], row['height']] if not math.isnan(row['x']) else [0.0, 0.0, 0.0, 0.0]\n\n        # 格式化影像與標註框\n        img, box = format_image(temp_img, temp_box)\n        \n        # 修正邊界框的格式\n        xmin, ymin, width, height = box\n        # print('width = ', width)\n        # print('height = ', height)\n        xmax = xmin + width\n        ymax = ymin + height\n\n        # 處理有效框或空框\n        if width <= 0.0 or height <= 0.0:\n            # print('空框處理')\n            boxes = torch.zeros((0, 4), dtype=torch.float32)  # 空框\n            labels = torch.zeros((0,), dtype=torch.int64)  # 空標籤\n        else:\n            if xmin > xmax or ymin > ymax:\n                print('wrong coordination')\n        \n            # 構建有效框\n            boxes = torch.tensor([[xmin, ymin, xmax, ymax]], dtype=torch.float32)\n            labels = torch.tensor([row['Target']], dtype=torch.int64)\n\n        \n        \n\n        # 影像正規化\n        # 影像正規化並轉換為 float32\n        img = img.astype(np.float32) / 255.  # 明確指定 float32\n\n\n        \n        \n    \n\n       # 建立目標字典\n        target = {}\n        target[\"boxes\"] = boxes.clone().detach().float()  # 確保格式正確\n        target[\"labels\"] = labels.clone().detach().long() # labels 保持為 int64\n        # Apply transforms\n        if self.transforms is not None:\n            img = self.transforms(img)\n        return img, target\n    def __len__(self):\n        # Return the length of your dataset\n        return len(self.dataframe)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.648984Z","iopub.execute_input":"2025-01-07T08:54:11.64924Z","iopub.status.idle":"2025-01-07T08:54:11.6633Z","shell.execute_reply.started":"2025-01-07T08:54:11.649221Z","shell.execute_reply":"2025-01-07T08:54:11.662501Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels = pd.read_csv('/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')\ndataframe = train_labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.663968Z","iopub.execute_input":"2025-01-07T08:54:11.664177Z","iopub.status.idle":"2025-01-07T08:54:11.739687Z","shell.execute_reply.started":"2025-01-07T08:54:11.664159Z","shell.execute_reply":"2025-01-07T08:54:11.739092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 檢查 DataFrame 長度和索引\nprint(f\"DataFrame 長度: {len(dataframe)}\")\nprint(f\"DataFrame 索引: {dataframe.index}\")\n\n# 確認是否存在索引不連續問題\nprint(f\"索引是否連續: {dataframe.index.is_monotonic_increasing}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.741483Z","iopub.execute_input":"2025-01-07T08:54:11.741678Z","iopub.status.idle":"2025-01-07T08:54:11.746534Z","shell.execute_reply.started":"2025-01-07T08:54:11.741661Z","shell.execute_reply":"2025-01-07T08:54:11.745646Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels[:1001]['Target'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.747448Z","iopub.execute_input":"2025-01-07T08:54:11.747744Z","iopub.status.idle":"2025-01-07T08:54:11.771719Z","shell.execute_reply.started":"2025-01-07T08:54:11.747716Z","shell.execute_reply":"2025-01-07T08:54:11.771063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = PneumoniaDataset(train_labels[:6001],transform)\n\nvalid_dataset = PneumoniaDataset(train_labels[6001:6201],transform)\n\n# Create data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=4, shuffle=True, \n                                   collate_fn=lambda x: tuple(zip(*x)))\nvalid_loader = DataLoader(valid_dataset, batch_size=4, shuffle=False, \n                                    collate_fn=lambda x: tuple(zip(*x)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.77253Z","iopub.execute_input":"2025-01-07T08:54:11.772822Z","iopub.status.idle":"2025-01-07T08:54:11.782393Z","shell.execute_reply.started":"2025-01-07T08:54:11.772769Z","shell.execute_reply":"2025-01-07T08:54:11.781589Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Move model to GPU if available\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nmodel.to(device)\n\n# Set up the optimizer\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, \n                                                   weight_decay=0.0005)\n# Learning rate scheduler\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, \n                                                               gamma=0.1)\n# Train the model\nnum_epochs = 3\nfor epoch in range(num_epochs):\n    model.train()\n    train_loss = 0.0\n\n   # Training loop\n    for images, targets in tqdm(train_loader):\n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\n        # Zero the gradients\n        optimizer.zero_grad()\n\n        # Forward pass\n        loss_dict = model(images, targets)\n        losses = sum(loss for loss in loss_dict.values())\n\n        # Backward pass\n        losses.backward()\n        optimizer.step()\n        train_loss += losses.item()\n\n    # Update the learning rate\n    lr_scheduler.step()\n    print(f'Epoch: {epoch + 1}, Loss: {train_loss / len(train_loader)}')\nprint(\"Training complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T08:54:11.783219Z","iopub.execute_input":"2025-01-07T08:54:11.783495Z","iopub.status.idle":"2025-01-07T09:26:56.678336Z","shell.execute_reply.started":"2025-01-07T08:54:11.783467Z","shell.execute_reply":"2025-01-07T09:26:56.677585Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 儲存完整模型\ntorch.save(model, '/kaggle/working/fasterrcnn_model.pth')\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T10:00:44.373865Z","iopub.execute_input":"2025-01-07T10:00:44.374165Z","iopub.status.idle":"2025-01-07T10:00:44.654625Z","shell.execute_reply.started":"2025-01-07T10:00:44.374144Z","shell.execute_reply":"2025-01-07T10:00:44.653982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def calculate_iou(box1, box2):\n#     \"\"\"\n#     計算兩個框的 IoU (Intersection over Union)\n#     \"\"\"\n#     # 計算交集\n#     x1 = max(box1[0], box2[0])\n#     y1 = max(box1[1], box2[1])\n#     x2 = min(box1[2], box2[2])\n#     y2 = min(box1[3], box2[3])\n\n#     # 如果無交集\n#     if x2 <= x1 or y2 <= y1:\n#         return 0.0\n\n#     intersection = (x2 - x1) * (y2 - y1)\n\n#     # 計算聯集\n#     area_box1 = (box1[2] - box1[0]) * (box1[3] - box1[1])\n#     area_box2 = (box2[2] - box2[0]) * (box2[3] - box2[1])\n#     union = area_box1 + area_box2 - intersection\n\n#     return intersection / union\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T09:56:00.469004Z","iopub.execute_input":"2025-01-07T09:56:00.469283Z","iopub.status.idle":"2025-01-07T09:56:00.473057Z","shell.execute_reply.started":"2025-01-07T09:56:00.469262Z","shell.execute_reply":"2025-01-07T09:56:00.472282Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Set the model to evaluation mode\nmodel.eval()\n# Test on a new image\nwith torch.no_grad():\n    for images, targets in tqdm(valid_loader):\n        images = list(img.to(device) for img in images)\n        predictions = model(images)\n        # Example: print the bounding boxes and labels for the first image\n        for i in range(len(predictions)):\n            print('pred_box: ',predictions[i]['boxes'])\n        \n            print('pred_labels: ',predictions[i]['labels'])\n            print('pred_scores: ',predictions[i]['scores'])\n            print('actual_labels: ',targets[i]['labels'])\n            \n            print('----------------------------------------------')\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T10:17:47.406455Z","iopub.execute_input":"2025-01-07T10:17:47.406695Z","iopub.status.idle":"2025-01-07T10:17:58.329118Z","shell.execute_reply.started":"2025-01-07T10:17:47.406674Z","shell.execute_reply":"2025-01-07T10:17:58.328331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accuracy = evaluate_model(model, valid_loader, device, iou_threshold=0.5)\nprint(f\"Accuracy: {accuracy * 100:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T10:05:45.51887Z","iopub.execute_input":"2025-01-07T10:05:45.519248Z","iopub.status.idle":"2025-01-07T10:05:45.780209Z","shell.execute_reply.started":"2025-01-07T10:05:45.519216Z","shell.execute_reply":"2025-01-07T10:05:45.779087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 10))\n\ntest_list = list(test_ds.take(20).as_numpy_iterator())\n\nprint(len(test_list))\n\nimage, labels = test_list[0]\n\nfor i in range(len(test_list)):\n\n    ax = plt.subplot(4, 5, i + 1)\n    image, labels = test_list[i]\n\n    predictions = model(image)\n\n    predicted_box = predictions[1][0] * input_size\n    predicted_box = tf.cast(predicted_box, tf.int32)\n\n    predicted_label = predictions[0][0][1]\n\n    image = image[0]\n\n    actual_label = labels[0][0][1]\n    actual_box = labels[1][0] * input_size\n    actual_box = tf.cast(actual_box, tf.int32)\n\n    image = image.astype(\"float\") * 255.0\n    image = image.astype(np.uint8)\n    image_color = cv.cvtColor(image, cv.COLOR_GRAY2RGB)\n\n    color = (255, 0, 0)\n    # print box red if predicted and actual label do not match\n    if (predicted_label > 0.5 and actual_label > 0) or (predicted_label < 0.5 and actual_label == 0):\n        color = (0, 255, 0)\n\n    img_label = \"negative\"\n    if predicted_label > 0.5:\n        img_label = \"positive\"\n\n    predicted_box_n = predicted_box.numpy()\n    cv.rectangle(image_color, predicted_box_n, color, 2)\n    cv.rectangle(image_color, actual_box.numpy(), (0, 0, 255), 2)\n    cv.rectangle(image_color, (predicted_box_n[0], predicted_box_n[1] + predicted_box_n[3] - 20), (predicted_box_n[0] + predicted_box_n[2], predicted_box_n[1] + predicted_box_n[3]), color, -1)\n    cv.putText(image_color, img_label, (predicted_box_n[0] + 5, predicted_box_n[1] + predicted_box_n[3] - 5), cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0))\n\n    IoU = intersection_over_union(predicted_box.numpy(), actual_box.numpy())[0]\n\n    plt.title(\"IoU:\" + format(IoU, '.4f'))\n    plt.imshow(image_color)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T09:54:52.236528Z","iopub.execute_input":"2025-01-07T09:54:52.236903Z","iopub.status.idle":"2025-01-07T09:54:52.265754Z","shell.execute_reply.started":"2025-01-07T09:54:52.236866Z","shell.execute_reply":"2025-01-07T09:54:52.264679Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\nprint(len(test_list))\n\nimage, labels = test_list[0]\n\nfor i in range(len(test_list)):\n\n    ax = plt.subplot(4, 5, i + 1)\n    image, labels = test_list[i]\n\n    predictions = model(image)\n\n    predicted_box = predictions[1][0] * input_size\n    predicted_box = tf.cast(predicted_box, tf.int32)\n\n    predicted_label = predictions[0][0][1]\n\n    image = image[0]\n\n    actual_label = labels[0][0][1]\n    actual_box = labels[1][0] * input_size\n    actual_box = tf.cast(actual_box, tf.int32)\n\n    image = image.astype(\"float\") * 255.0\n    image = image.astype(np.uint8)\n    image_color = cv.cvtColor(image, cv.COLOR_GRAY2RGB)\n\n    color = (255, 0, 0)\n    # print box red if predicted and actual label do not match\n    if (predicted_label > 0.5 and actual_label > 0) or (predicted_label < 0.5 and actual_label == 0):\n        color = (0, 255, 0)\n\n    img_label = \"negative\"\n    if predicted_label > 0.5:\n        img_label = \"positive\"\n\n    predicted_box_n = predicted_box.numpy()\n    cv.rectangle(image_color, predicted_box_n, color, 2)\n    cv.rectangle(image_color, actual_box.numpy(), (0, 0, 255), 2)\n    cv.rectangle(image_color, (predicted_box_n[0], predicted_box_n[1] + predicted_box_n[3] - 20), (predicted_box_n[0] + predicted_box_n[2], predicted_box_n[1] + predicted_box_n[3]), color, -1)\n    cv.putText(image_color, img_label, (predicted_box_n[0] + 5, predicted_box_n[1] + predicted_box_n[3] - 5), cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0))\n\n    IoU = intersection_over_union(predicted_box.numpy(), actual_box.numpy())[0]\n\n    plt.title(\"IoU:\" + format(IoU, '.4f'))\n    plt.imshow(image_color)\n    plt.axis(\"off\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nfrom PIL import Image\n# Load image\nimg = Image.open(\"path/to/your/image.jpg\")\n# Apply the same transformation as for training\nimg = transform(img)\nimg = img.unsqueeze(0).to(device)\n# Model prediction\nmodel.eval()\nwith torch.no_grad():\n    prediction = model([img])\n# Print the predicted bounding boxes and labels\nprint(prediction[0]['boxes'])\nprint(prediction[0]['labels'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T09:27:08.675695Z","iopub.execute_input":"2025-01-07T09:27:08.67603Z","iopub.status.idle":"2025-01-07T09:27:08.942307Z","shell.execute_reply.started":"2025-01-07T09:27:08.675996Z","shell.execute_reply":"2025-01-07T09:27:08.941294Z"}},"outputs":[],"execution_count":null}]}