{"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":36363,"databundleVersionId":4050810,"sourceType":"competition"},{"sourceId":10679907,"sourceType":"datasetVersion","datasetId":6616020}],"dockerImageVersionId":30840,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --upgrade gdcm pylibjpeg pylibjpeg-libjpeg pylibjpeg-openjpeg\n!pip install gdcm\n!pip install pylibjpeg pylibjpeg-libjpeg\n!pip install pandas==1.3.5 numpy==1.21.6\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-22T04:25:51.752561Z","iopub.execute_input":"2025-02-22T04:25:51.752872Z","iopub.status.idle":"2025-02-22T04:26:13.239879Z","shell.execute_reply.started":"2025-02-22T04:25:51.752849Z","shell.execute_reply":"2025-02-22T04:26:13.238794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\n# 檢查 GPU 是否可用\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# 定義一個簡單的模型\nclass SimpleModel(nn.Module):\n    def __init__(self):\n        super(SimpleModel, self).__init__()\n        self.fc = nn.Linear(128, 64)\n\n    def forward(self, x):\n        return self.fc(x)\n\nmodel = SimpleModel()  # 創建模型實例\nmodel.to(device)  # 將模型移動到 GPU 或 CPU\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-08T12:49:22.189783Z","iopub.execute_input":"2025-02-08T12:49:22.19011Z","iopub.status.idle":"2025-02-08T12:49:22.412601Z","shell.execute_reply.started":"2025-02-08T12:49:22.190082Z","shell.execute_reply":"2025-02-08T12:49:22.411718Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport tensorflow as tf\nprint(tf.config.list_physical_devices('GPU'))  # 顯示可用 GPU\n\nprint(torch.cuda.is_available())  # True 表示 GPU 可用\nprint(torch.cuda.device_count())  # 顯示可用 GPU 數量\nprint(torch.cuda.get_device_name(0))  # 顯示 GPU 型號\ntf.config.experimental.set_memory_growth(tf.config.list_physical_devices('GPU')[0], True)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-08T12:49:25.65412Z","iopub.execute_input":"2025-02-08T12:49:25.654463Z","iopub.status.idle":"2025-02-08T12:49:25.662776Z","shell.execute_reply.started":"2025-02-08T12:49:25.654434Z","shell.execute_reply":"2025-02-08T12:49:25.661935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pydicom\nimport matplotlib.pyplot as plt\n\n# 設定影像資料夾路徑\ndata_dir = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/train_images\"\n\n# 獲取第一個病人的資料夾\nfirst_patient_folder = sorted(os.listdir(data_dir))[0]\n\n# 取得該病人資料夾中的所有 .dcm 檔案\ndcm_files = sorted([f for f in os.listdir(os.path.join(data_dir, first_patient_folder)) if f.endswith(\".dcm\")])\n\n# 初始化圖像展示\nfig, axes = plt.subplots(2, 4, figsize=(16, 8))\n\n# 顯示前 8 張 CT 影像\nfor idx, dcm_file in enumerate(dcm_files[:8]):\n    # 讀取 .dcm 檔案\n    dcm_path = os.path.join(data_dir, first_patient_folder, dcm_file)\n    dicom_data = pydicom.dcmread(dcm_path)\n    \n    # 顯示影像\n    ax = axes[idx // 4, idx % 4]\n    ax.imshow(dicom_data.pixel_array)\n    ax.set_title(f\"{dcm_file}\")\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-19T12:32:47.287116Z","iopub.execute_input":"2025-03-19T12:32:47.287384Z","iopub.status.idle":"2025-03-19T12:32:49.280164Z","shell.execute_reply.started":"2025-03-19T12:32:47.287353Z","shell.execute_reply":"2025-03-19T12:32:49.278881Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"試****","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport cv2\nimport pydicom\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom pydicom.pixel_data_handlers.util import convert_color_space\n\nfalsees=0\n# 讀取 CSV 檔案，篩選有骨折的影像\ncsv_path = \"/kaggle/input/final-csv/vertebrae_labels_with_fracture.csv\"\ndf = pd.read_csv(csv_path)\nfracture_df = df[(df.iloc[:, 2:9] == 1).any(axis=1)]  # 篩選有骨折的影像\n\n# **隨機選取 20% 的資料**\nfracture_df = fracture_df.sample(frac=0.2, random_state=42).reset_index(drop=True)\n\n# 影像預處理函數\ndef preprocess_image(image):\n    image = cv2.resize(image, (128, 128), interpolation=cv2.INTER_AREA)\n    denoised = cv2.fastNlMeansDenoising(image, None, h=20, templateWindowSize=7, searchWindowSize=21)\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    enhanced = clahe.apply(denoised)\n    gamma = 1.4\n    gamma_corrected = np.power(enhanced / 255.0, gamma) * 255\n    return np.clip(gamma_corrected, 0, 255).astype(np.uint8)\n\n# 邊緣檢測函數\ndef edge_detection(image):\n    edges = cv2.Canny(image, 50, 150)\n    laplacian = np.clip(np.abs(cv2.Laplacian(image, cv2.CV_64F)), 0, 255).astype(np.uint8)\n    return cv2.bitwise_or(edges, laplacian)\n\n# 分水嶺分割\ndef watershed_segmentation(image, edges):\n    _, binary_edges = cv2.threshold(edges, 0, 255, cv2.THRESH_BINARY)\n    kernel = np.ones((3, 3), np.uint8)\n    opening = cv2.morphologyEx(binary_edges, cv2.MORPH_CLOSE, kernel, iterations=2)\n    dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)\n    _, sure_fg = cv2.threshold(dist_transform, 0.4 * dist_transform.max(), 255, 0)\n    sure_fg = np.uint8(sure_fg)\n    sure_bg = np.uint8(cv2.dilate(opening, kernel, iterations=3))\n    unknown = cv2.subtract(sure_bg, sure_fg)\n    _, markers = cv2.connectedComponents(sure_fg)\n    markers = markers + 1\n    markers[unknown == 255] = 0\n    markers = cv2.watershed(cv2.cvtColor(image, cv2.COLOR_GRAY2BGR), markers)\n    return np.where(markers > 1, 255, 0).astype('uint8')\n\n# GrabCut 分割\ndef apply_grabcut_with_watershed(image, mask):\n    grabcut_mask = np.zeros(image.shape[:2], np.uint8)\n    grabcut_mask[mask == 255] = cv2.GC_PR_FGD\n    grabcut_mask[mask == 0] = cv2.GC_BGD\n    bgd_model = np.zeros((1, 65), np.float64)\n    fgd_model = np.zeros((1, 65), np.float64)\n    cv2.grabCut(image, grabcut_mask, None, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_MASK)\n    final_mask = np.where((grabcut_mask == 2) | (grabcut_mask == 0), 0, 1).astype('uint8')\n    return image * cv2.merge([final_mask, final_mask, final_mask])\n\n# 設定輸入與輸出目錄\ninput_dir = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/train_images\"\noutput_dir = \"/kaggle/working/npy_output\"\nos.makedirs(output_dir, exist_ok=True)\n\n# 遍歷 CSV，處理符合條件的影像（只處理 20%）\nfor _, row in fracture_df.iterrows():\n    spine_id = row[\"SpineID\"]\n    slice_number = row[\"SliceNumber\"]\n    dicom_path = os.path.join(input_dir, spine_id, f\"{slice_number}.dcm\")\n    \n    if os.path.exists(dicom_path):\n        try:\n            dicom_data = pydicom.dcmread(dicom_path)\n            image = dicom_data.pixel_array\n\n            if dicom_data.PhotometricInterpretation == \"YBR_FULL\":\n                image = convert_color_space(image, \"YBR_FULL\", \"RGB\")\n\n            if image.dtype != np.uint8:\n                image = ((image - np.min(image)) / (np.max(image) - np.min(image)) * 255).astype(np.uint8)\n\n            # 預處理與分割\n            preprocessed_image = preprocess_image(image)\n            edges = edge_detection(preprocessed_image)\n            watershed_result = watershed_segmentation(preprocessed_image, edges)\n            preprocessed_bgr = cv2.cvtColor(preprocessed_image, cv2.COLOR_GRAY2BGR)\n            grabcut_result = apply_grabcut_with_watershed(preprocessed_bgr, watershed_result)\n\n            # 建立輸出路徑並儲存 GrabCut 處理後的影像為 .npy 檔案\n            output_path = os.path.join(output_dir, spine_id)\n            os.makedirs(output_path, exist_ok=True)\n            npy_output_path = os.path.join(output_path, f\"{slice_number}.npy\")\n            np.save(npy_output_path, grabcut_result)\n\n        except Exception as e:\n            falsees+=1\n\nprint(\"已完成 20% 影像的處理並存儲為 .npy 檔案。\")\nprint(\"錯誤處理數量:\",falsees)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-22T04:29:45.08185Z","iopub.execute_input":"2025-02-22T04:29:45.082204Z","iopub.status.idle":"2025-02-22T04:30:03.260379Z","shell.execute_reply.started":"2025-02-22T04:29:45.082173Z","shell.execute_reply":"2025-02-22T04:30:03.258913Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 影像分割技術（預處理、分水嶺、GrabCut）(目前)","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport cv2\nimport pydicom\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom pydicom.pixel_data_handlers.util import convert_color_space\n\n# 讀取 CSV 檔案，篩選有骨折的影像\ncsv_path = \"/kaggle/input/final-csv/vertebrae_labels_with_fracture.csv\"\ndf = pd.read_csv(csv_path)\nfracture_df = df[(df.iloc[:, 2:9] == 1).any(axis=1)]  # 篩選有骨折的影像\n\n# CPU 版本的影像預處理函數\ndef preprocess_image(image):\n    # 降噪\n    denoised = cv2.fastNlMeansDenoising(image, None, h=10, templateWindowSize=7, searchWindowSize=21)\n    \n    # CLAHE 增強\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    enhanced = clahe.apply(denoised)\n    \n    # Gamma 校正 (使用 NumPy 在 CPU 上計算)\n    gamma = 1.4\n    gamma_corrected = np.power(enhanced / 255.0, gamma) * 255\n    gamma_corrected = np.clip(gamma_corrected, 0, 255)\n    return gamma_corrected.astype(np.uint8)\n\n# CPU 版本的邊緣檢測函數\ndef edge_detection(image):\n    # Canny 邊緣檢測\n    edges = cv2.Canny(image, 50, 150)\n    \n    # Laplacian 邊緣運算，注意取絕對值以避免負值影響\n    laplacian = cv2.Laplacian(image, cv2.CV_64F)\n    laplacian = np.clip(np.abs(laplacian), 0, 255).astype(np.uint8)\n    \n    # 組合邊緣資訊\n    combined_edges = cv2.bitwise_or(edges, laplacian)\n    return combined_edges\n\n# 分水嶺分割 (CPU 執行)\ndef watershed_segmentation(image, edges):\n    _, binary_edges = cv2.threshold(edges, 0, 255, cv2.THRESH_BINARY)\n    kernel = np.ones((3, 3), np.uint8)\n    opening = cv2.morphologyEx(binary_edges, cv2.MORPH_CLOSE, kernel, iterations=2)\n    dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)\n    _, sure_fg = cv2.threshold(dist_transform, 0.4 * dist_transform.max(), 255, 0)\n    sure_fg = np.uint8(sure_fg)\n    sure_bg = np.uint8(cv2.dilate(opening, kernel, iterations=3))\n    unknown = cv2.subtract(sure_bg, sure_fg)\n    _, markers = cv2.connectedComponents(sure_fg)\n    markers = markers + 1\n    markers[unknown == 255] = 0\n    markers = cv2.watershed(cv2.cvtColor(image, cv2.COLOR_GRAY2BGR), markers)\n    return np.where(markers > 1, 255, 0).astype('uint8')\n\n# GrabCut 分割 (CPU 執行)\ndef apply_grabcut_with_watershed(image, mask):\n    grabcut_mask = np.zeros(image.shape[:2], np.uint8)\n    grabcut_mask[mask == 255] = cv2.GC_PR_FGD\n    grabcut_mask[mask == 0] = cv2.GC_BGD\n    bgd_model = np.zeros((1, 65), np.float64)\n    fgd_model = np.zeros((1, 65), np.float64)\n    cv2.grabCut(image, grabcut_mask, None, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_MASK)\n    final_mask = np.where((grabcut_mask == 2) | (grabcut_mask == 0), 0, 1).astype('uint8')\n    return image * cv2.merge([final_mask, final_mask, final_mask])\n\n# 設定輸入與輸出目錄\ninput_dir = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/train_images\"\noutput_dir = \"/kaggle/working/npy_output\"\nos.makedirs(output_dir, exist_ok=True)\n\n# 遍歷 CSV，處理符合條件的影像\nfor _, row in fracture_df.iterrows():\n    spine_id = row[\"SpineID\"]\n    slice_number = row[\"SliceNumber\"]\n    dicom_path = os.path.join(input_dir, spine_id, f\"{slice_number}.dcm\")\n    \n    if os.path.exists(dicom_path):\n        dicom_data = pydicom.dcmread(dicom_path)\n        image = dicom_data.pixel_array\n\n        if dicom_data.PhotometricInterpretation == \"YBR_FULL\":\n            image = convert_color_space(image, \"YBR_FULL\", \"RGB\")\n\n        if image.dtype != np.uint8:\n            image = ((image - np.min(image)) / (np.max(image) - np.min(image)) * 255).astype(np.uint8)\n\n        # 預處理與分割\n        preprocessed_image = preprocess_image(image)\n        edges = edge_detection(preprocessed_image)\n        watershed_result = watershed_segmentation(preprocessed_image, edges)\n        preprocessed_bgr = cv2.cvtColor(preprocessed_image, cv2.COLOR_GRAY2BGR)\n        grabcut_result = apply_grabcut_with_watershed(preprocessed_bgr, watershed_result)\n\n        # 建立輸出路徑並儲存 GrabCut 處理後的影像為 .npy 檔案\n        output_path = os.path.join(output_dir, spine_id)\n        os.makedirs(output_path, exist_ok=True)\n        npy_output_path = os.path.join(output_path, f\"{slice_number}.npy\")\n        np.save(npy_output_path, grabcut_result)\n\nprint(\"所有符合條件的影像處理完成並存儲為 `.npy` 檔案。\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-12T13:30:07.638617Z","iopub.execute_input":"2025-02-12T13:30:07.638848Z","execution_failed":"2025-02-12T14:10:05.64Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 資料集劃分（訓練集與測試集）(目前)","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D, Concatenate\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import CategoricalCrossentropy\nfrom tensorflow.keras.metrics import Accuracy\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\n\n# 讀取 CSV 標註檔案\ncsv_path = \"/mnt/data/vertebrae_labels_with_fracture.csv\"\ndf = pd.read_csv(csv_path)\n\n# 設定影像資料夾\nnpy_dir = \"/kaggle/working/npy_output\"\n\n# 載入影像與標籤\nimage_data, labels = [], []\nfor _, row in df.iterrows():\n    spine_id = row[\"SpineID\"]\n    slice_number = row[\"SliceNumber\"]\n    npy_path = os.path.join(npy_dir, spine_id, f\"{slice_number}.npy\")\n\n    if os.path.exists(npy_path):\n        img = np.load(npy_path)\n        image_data.append(img)\n        labels.append(row.iloc[2:9].values)  # 取 C1-C7 的骨折標註\n\n# 轉換為 NumPy 陣列\nimage_data = np.array(image_data).astype(np.float32) / 255.0  # 正規化\nlabels = np.array(labels, dtype=np.uint8)  # 轉換為整數\n\n# One-hot 編碼標籤 (C1-C7 為 7 類 + 背景 1 類 = 共 8 類)\nnum_classes = 8\nlabels = tf.keras.utils.to_categorical(labels, num_classes=num_classes)\n\n# 確保影像維度正確\nimage_data = np.expand_dims(image_data, axis=-1)  # (batch, 128, 128, 1)\n\n# 切分訓練集與驗證集 (80% 訓練，20% 驗證)\nX_train, X_val, y_train, y_val = train_test_split(image_data, labels, test_size=0.2, random_state=42)\n\n# U-Net 模型架構\ndef build_unet(input_shape=(128, 128, 1), num_classes=8):\n    inputs = Input(input_shape)\n\n    # Encoder\n    c1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)\n    c1 = Conv2D(32, (3, 3), activation='relu', padding='same')(c1)\n    p1 = MaxPooling2D((2, 2))(c1)\n\n    c2 = Conv2D(64, (3, 3), activation='relu', padding='same')(p1)\n    c2 = Conv2D(64, (3, 3), activation='relu', padding='same')(c2)\n    p2 = MaxPooling2D((2, 2))(c2)\n\n    c3 = Conv2D(128, (3, 3), activation='relu', padding='same')(p2)\n    c3 = Conv2D(128, (3, 3), activation='relu', padding='same')(c3)\n    p3 = MaxPooling2D((2, 2))(c3)\n\n    # Bottleneck\n    c4 = Conv2D(256, (3, 3), activation='relu', padding='same')(p3)\n    c4 = Conv2D(256, (3, 3), activation='relu', padding='same')(c4)\n\n    # Decoder\n    u5 = UpSampling2D((2, 2))(c4)\n    u5 = Concatenate()([u5, c3])\n    c5 = Conv2D(128, (3, 3), activation='relu', padding='same')(u5)\n    c5 = Conv2D(128, (3, 3), activation='relu', padding='same')(c5)\n\n    u6 = UpSampling2D((2, 2))(c5)\n    u6 = Concatenate()([u6, c2])\n    c6 = Conv2D(64, (3, 3), activation='relu', padding='same')(u6)\n    c6 = Conv2D(64, (3, 3), activation='relu', padding='same')(c6)\n\n    u7 = UpSampling2D((2, 2))(c6)\n    u7 = Concatenate()([u7, c1])\n    c7 = Conv2D(32, (3, 3), activation='relu', padding='same')(u7)\n    c7 = Conv2D(32, (3, 3), activation='relu', padding='same')(c7)\n\n    outputs = Conv2D(num_classes, (1, 1), activation='softmax')(c7)\n\n    model = Model(inputs, outputs)\n    return model\n\n# 編譯模型\nmodel = build_unet()\nmodel.compile(optimizer=Adam(learning_rate=1e-3),\n              loss=CategoricalCrossentropy(),\n              metrics=[\"accuracy\"])\n\n# 設定回調函數 (儲存最佳模型)\ncheckpoint = ModelCheckpoint(\"/kaggle/working/unet_best_model.h5\", save_best_only=True, monitor=\"val_loss\")\nreduce_lr = ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=5, verbose=1)\n\n# 訓練模型\nhistory = model.fit(X_train, y_train, \n                    validation_data=(X_val, y_val),\n                    epochs=50,\n                    batch_size=16,\n                    callbacks=[checkpoint, reduce_lr])\n\n# 儲存最終模型\nmodel.save(\"/kaggle/working/unet_final_model.h5\")\n\n# 繪製訓練曲線\nplt.figure(figsize=(12, 4))\nplt.subplot(1, 2, 1)\nplt.plot(history.history[\"loss\"], label=\"train_loss\")\nplt.plot(history.history[\"val_loss\"], label=\"val_loss\")\nplt.title(\"Loss\")\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history[\"accuracy\"], label=\"train_acc\")\nplt.plot(history.history[\"val_accuracy\"], label=\"val_acc\")\nplt.title(\"Accuracy\")\nplt.legend()\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-24T05:49:27.82717Z","iopub.execute_input":"2025-01-24T05:49:27.827648Z","iopub.status.idle":"2025-01-24T05:53:02.688296Z","shell.execute_reply.started":"2025-01-24T05:49:27.827592Z","shell.execute_reply":"2025-01-24T05:53:02.687237Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# U-Net 模型建置","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D, Concatenate, Flatten, Dense, Dropout, GlobalAveragePooling2D, BatchNormalization\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.regularizers import l2\n\ndef unet_classification_model(input_size=(128, 128, 1)):\n    inputs = Input(input_size)\n    \n    # 下採樣部分 (Encoder)\n    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(inputs)\n    conv1 = BatchNormalization()(conv1)\n    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(conv1)\n    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n\n    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(pool1)\n    conv2 = BatchNormalization()(conv2)\n    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(conv2)\n    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n\n    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(pool2)\n    conv3 = BatchNormalization()(conv3)\n    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(conv3)\n    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n\n    # 底層\n    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(pool3)\n    conv4 = BatchNormalization()(conv4)\n    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same', kernel_regularizer=l2(0.001))(conv4)\n\n    # 上採樣部分 (Decoder)\n    up5 = UpSampling2D(size=(2, 2))(conv4)\n    merge5 = Concatenate()([conv3, up5])\n    conv5 = Conv2D(128, (3, 3), activation='relu', padding='same')(merge5)\n\n    up6 = UpSampling2D(size=(2, 2))(conv5)\n    merge6 = Concatenate()([conv2, up6])\n    conv6 = Conv2D(64, (3, 3), activation='relu', padding='same')(merge6)\n\n    up7 = UpSampling2D(size=(2, 2))(conv6)\n    merge7 = Concatenate()([conv1, up7])\n    conv7 = Conv2D(32, (3, 3), activation='relu', padding='same')(merge7)\n\n    # 分類層 (全局池化+二元分類)\n    global_avg_pool = GlobalAveragePooling2D()(conv7)\n    dropout = Dropout(0.5)(global_avg_pool)\n    output = Dense(1, activation='sigmoid')(dropout)\n\n    model = Model(inputs, output)\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-24T05:53:02.689903Z","iopub.execute_input":"2025-01-24T05:53:02.690152Z","iopub.status.idle":"2025-01-24T05:53:02.702443Z","shell.execute_reply.started":"2025-01-24T05:53:02.690127Z","shell.execute_reply":"2025-01-24T05:53:02.701787Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 模型編譯與訓練","metadata":{}},{"cell_type":"code","source":"# 構建 U-Net 二分類模型\nmodel = unet_classification_model()\n\n# 編譯模型\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n\n# 訓練模型\nhistory = model.fit(\n    X_train, y_train,\n    epochs=20,\n    batch_size=16,\n    validation_data=(X_val, y_val),\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-24T05:53:02.703337Z","iopub.execute_input":"2025-01-24T05:53:02.703645Z","iopub.status.idle":"2025-01-24T05:57:28.335505Z","shell.execute_reply.started":"2025-01-24T05:53:02.703615Z","shell.execute_reply":"2025-01-24T05:57:28.334582Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 可視化訓練成果","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 繪製準確率曲線\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.title('U-Net Classification Model Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()\n\n# 繪製損失曲線\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.title('U-Net Classification Model Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()\n\nloss, accuracy = model.evaluate(X_val, y_val)\nprint(f\"測試集準確率: {accuracy:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-24T05:57:28.33686Z","iopub.execute_input":"2025-01-24T05:57:28.337184Z","iopub.status.idle":"2025-01-24T05:57:35.481867Z","shell.execute_reply.started":"2025-01-24T05:57:28.337152Z","shell.execute_reply":"2025-01-24T05:57:35.481048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport pydicom\nfrom tqdm import tqdm\n\nBASE_PATH = '/kaggle/input/rsna-2022-cervical-spine-fracture-detection'\nSEGMENTATIONS_PATH = os.path.join(BASE_PATH, 'segmentations')\n\nsegmentation_files = os.listdir(SEGMENTATIONS_PATH)\nprint(f\"\\nNumber of segmentation files: {len(segmentation_files)}\")\nprint(\"Sample segmentation file names:\")\nprint(segmentation_files[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-01T07:26:28.451577Z","iopub.execute_input":"2025-02-01T07:26:28.451894Z","iopub.status.idle":"2025-02-01T07:26:28.477704Z","shell.execute_reply.started":"2025-02-01T07:26:28.451869Z","shell.execute_reply":"2025-02-01T07:26:28.477053Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install nibabel\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-01T07:36:41.954674Z","iopub.execute_input":"2025-02-01T07:36:41.955006Z","iopub.status.idle":"2025-02-01T07:36:46.111466Z","shell.execute_reply.started":"2025-02-01T07:36:41.954979Z","shell.execute_reply":"2025-02-01T07:36:46.110606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# 讀取 NIfTI 檔案\nnii_path = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/segmentations/1.2.826.0.1.3680043.10633.nii\"\nnii_image = nib.load(nii_path)\nimage_data = nii_image.get_fdata()\n\n# 顯示某一切片\nplt.imshow(image_data[:, :, image_data.shape[2] // 2], cmap=\"gray\")\nplt.title(\"NIfTI 切片影像\")\nplt.axis(\"off\")\nplt.show()\n\nnp.load(\"/mnt/data/ct_image.npz\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport numpy as np\n\n# 設定 NIfTI 檔案路徑\nnii_file_path = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/segmentations/1.2.826.0.1.3680043.10633.nii\"\n\n# 讀取 NIfTI 檔案\nnii_image = nib.load(nii_file_path)\n\n# 取得影像數據（3D NumPy 陣列）\nimage_data = nii_image.get_fdata()\n\n# 取得影像的頭部資訊\nheader = nii_image.header\n\n# 顯示影像的基本資訊\nprint(f\"影像尺寸: {image_data.shape}\")\nprint(f\"體素間距: {header.get_zooms()}\")\nprint(f\"資料類型: {image_data.dtype}\")\n\n# 檢查影像的數值範圍（可能標註區域）\nprint(f\"影像最小值: {np.min(image_data)}, 影像最大值: {np.max(image_data)}\")\n\n# 嘗試識別可能的標註區域（如 C1~C7）\nunique_values = np.unique(image_data)\nprint(f\"影像中的唯一值: {unique_values[:20]}\")  # 列出前 20 個唯一數值\n\n# 如果影像內有離散標籤（如 1, 2, 3, 4, 5, 6, 7），可能是 C1~C7 的標註\nc1_c7_labels = [val for val in unique_values if 1 <= val <= 7]\nprint(f\"可能的 C1~C7 標註值: {c1_c7_labels}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-01T07:54:27.801496Z","iopub.execute_input":"2025-02-01T07:54:27.801811Z","iopub.status.idle":"2025-02-01T07:54:38.500661Z","shell.execute_reply.started":"2025-02-01T07:54:27.801783Z","shell.execute_reply":"2025-02-01T07:54:38.499918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport numpy as np\nimport pandas as pd\nfrom collections import Counter\n\n# 讀取 .nii 影像數據\npath = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/segmentations/1.2.826.0.1.3680043.10633.nii\"\nimgs = nib.load(path).get_fdata()\n\nprint(f\"NIfTI 影像尺寸: {imgs.shape}\")  # (512, 512, 339)\n\n# 計算像素頻率函數\ndef count_pixel_frequency(slice_array):\n    \"\"\"\n    計算單張切片中各像素值的頻率，僅保留 C1~C7 (1.0 ~ 7.0)\n    \"\"\"\n    flat_array = slice_array.flatten()\n    freq_dict = dict(Counter(flat_array))\n    \n    # 過濾掉非 C1~C7 的值\n    filtered_freq = {k: v for k, v in freq_dict.items() if 1.0 <= k <= 7.0}\n    return filtered_freq\n\n# 儲存結果\nresults = []\n\n# 逐張切片計算\nfor idx in range(imgs.shape[2]):\n    freq_result = count_pixel_frequency(imgs[:, :, idx])\n    \n    if freq_result:  # 只保留含有 C1~C7 的切片\n        df = pd.DataFrame(list(freq_result.items()), columns=[\"Pixel Value\", \"Frequency\"])\n        df[\"Slice\"] = idx + 1  # 記錄切片索引\n        results.append(df)\n        print(f\"切片 {idx+1}: {freq_result}\")\n    \n    # 控制批次數量，防止過多輸出\n    if len(results) >= 400:\n        break\n\n# 合併所有結果為 DataFrame\nfinal_df = pd.concat(results, ignore_index=True)\n\n# 顯示結果\nimport ace_tools as tools\ntools.display_dataframe_to_user(name=\"C1~C7 Pixel Frequency Analysis\", dataframe=final_df)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-05T14:01:58.244077Z","iopub.execute_input":"2025-02-05T14:01:58.244431Z","iopub.status.idle":"2025-02-05T14:02:15.750655Z","shell.execute_reply.started":"2025-02-05T14:01:58.244404Z","shell.execute_reply":"2025-02-05T14:02:15.749516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport numpy as np\nimport pandas as pd\nimport os\nimport cv2\nimport pydicom\nimport matplotlib.pyplot as plt\nfrom collections import Counter\n\n# 設定路徑\nnii_path = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/segmentations/1.2.826.0.1.3680043.10633.nii\"\ndcm_folder = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/train_images/1.2.826.0.1.3680043.10633\"\ncsv_output_path = \"/kaggle/working/segment_labels.csv\"\n\n# 讀取 .nii 影像數據\nimgs = nib.load(nii_path).get_fdata()\n\nprint(f\"NIfTI 影像尺寸: {imgs.shape}\")  # (512, 512, 339)\n\n# 計算像素頻率函數\ndef count_pixel_frequency(slice_array):\n    \"\"\"\n    計算單張切片中各像素值的頻率，僅保留 C1~C7 (1.0 ~ 7.0)\n    \"\"\"\n    flat_array = slice_array.flatten()\n    freq_dict = dict(Counter(flat_array))\n    \n    # 過濾掉非 C1~C7 的值\n    filtered_freq = {k: v for k, v in freq_dict.items() if 1.0 <= k <= 7.0}\n    return filtered_freq\n\n# 影像處理函數\ndef preprocess_image(image):\n    denoised = cv2.fastNlMeansDenoising(image, None, h=10, templateWindowSize=7, searchWindowSize=21)\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    enhanced = clahe.apply(denoised)\n    gamma = 1.2\n    gamma_corrected = np.power(enhanced / 255.0, gamma) * 255\n    return gamma_corrected.astype(np.uint8)\n\ndef edge_detection(image):\n    edges = cv2.Canny(image, 50, 150)\n    laplacian = cv2.Laplacian(image, cv2.CV_64F)\n    laplacian = np.clip(laplacian, 0, 255).astype(np.uint8)\n    return cv2.bitwise_or(edges, laplacian)\n\ndef watershed_segmentation(image, edges):\n    _, binary_edges = cv2.threshold(edges, 0, 255, cv2.THRESH_BINARY)\n    kernel = np.ones((3, 3), np.uint8)\n    opening = cv2.morphologyEx(binary_edges, cv2.MORPH_CLOSE, kernel, iterations=2)\n    dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)\n    _, sure_fg = cv2.threshold(dist_transform, 0.4 * dist_transform.max(), 255, 0)\n    sure_fg = np.uint8(sure_fg)\n    sure_bg = cv2.dilate(opening, kernel, iterations=3)\n    sure_bg = np.uint8(sure_bg)\n    unknown = cv2.subtract(sure_bg, sure_fg)\n    _, markers = cv2.connectedComponents(sure_fg)\n    markers = markers + 1\n    markers[unknown == 255] = 0\n    markers = cv2.watershed(cv2.cvtColor(image, cv2.COLOR_GRAY2BGR), markers)\n    return np.where(markers > 1, 255, 0).astype('uint8')\n\n# 儲存結果\nresults = []\n\n# 逐張切片計算\nfor idx in range(imgs.shape[2]):\n    freq_result = count_pixel_frequency(imgs[:, :, idx])\n    \n    if freq_result:  # 只保留含有 C1~C7 的切片\n        dicom_path = os.path.join(dcm_folder, f\"{idx+1}.dcm\")\n        dicom_data = pydicom.dcmread(dicom_path)\n        image = dicom_data.pixel_array\n        if image.dtype != np.uint8:\n            image = ((image - np.min(image)) / (np.max(image) - np.min(image)) * 255).astype(np.uint8)\n        \n        preprocessed_image = preprocess_image(image)\n        edges = edge_detection(preprocessed_image)\n        watershed_result = watershed_segmentation(preprocessed_image, edges)\n        \n        for label, count in freq_result.items():\n            results.append({\n                \"DICOM Path\": dicom_path,\n                \"Slice\": idx + 1,\n                \"Label\": int(label),\n                \"Pixel Count\": count\n            })\n        print(f\"切片 {idx+1}: {freq_result}\")\n    \n    if len(results) >= 400:\n        break\n\n# 轉換為 DataFrame 並儲存為 CSV\nfinal_df = pd.DataFrame(results)\nfinal_df.to_csv(csv_output_path, index=False)\n\n# 顯示 CSV 內容\nimport ace_tools as tools\ntools.display_dataframe_to_user(name=\"Segment Labels\", dataframe=final_df)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}