{"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":30822,"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-05T10:02:00.641565Z","iopub.execute_input":"2025-01-05T10:02:00.641902Z","iopub.status.idle":"2025-01-05T10:02:05.366390Z","shell.execute_reply.started":"2025-01-05T10:02:00.641874Z","shell.execute_reply":"2025-01-05T10:02:05.365724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2 as cv\nimport numpy as np\nfrom matplotlib import pyplot as plt\nimport pandas as pd\nimport pydicom\nfrom skimage.transform import resize\nimport matplotlib.patches as patches","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:05.367483Z","iopub.execute_input":"2025-01-05T10:02:05.367753Z","iopub.status.idle":"2025-01-05T10:02:05.371814Z","shell.execute_reply.started":"2025-01-05T10:02:05.367732Z","shell.execute_reply":"2025-01-05T10:02:05.371037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_label = pd.read_csv('/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')\ntrain_label.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:05.373184Z","iopub.execute_input":"2025-01-05T10:02:05.373381Z","iopub.status.idle":"2025-01-05T10:02:05.413317Z","shell.execute_reply.started":"2025-01-05T10:02:05.373362Z","shell.execute_reply":"2025-01-05T10:02:05.412700Z"}},"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-05T10:02:05.414566Z","iopub.execute_input":"2025-01-05T10:02:05.414832Z","iopub.status.idle":"2025-01-05T10:02:05.420097Z","shell.execute_reply.started":"2025-01-05T10:02:05.414812Z","shell.execute_reply":"2025-01-05T10:02:05.419387Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dcm_path = '/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_images/00436515-870c-4b36-a041-de91049b9ab4.dcm'\n\nimage_array = pydicom.dcmread(dcm_path).pixel_array\n# image_array = cv.resize(image_array, (224, 224))\n\nprint(image_array.shape)\n\n# 繪圖\nfig, ax = plt.subplots(1, 1, figsize=(6, 6))  # 建立圖表與子圖\nax.imshow(image_array, cmap='bone')          # 顯示影像\n\n# 繪製標註框\nrect = patches.Rectangle((264.0, 152.0), 213.0, 379.0, \n                         edgecolor='r', facecolor='none', linewidth=2)\nax.add_patch(rect)                           # 在軸上新增標註框\n\nplt.show()    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:05.420885Z","iopub.execute_input":"2025-01-05T10:02:05.421142Z","iopub.status.idle":"2025-01-05T10:02:05.773868Z","shell.execute_reply.started":"2025-01-05T10:02:05.421123Z","shell.execute_reply":"2025-01-05T10:02:05.772974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndatapath = '/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_images/00436515-870c-4b36-a041-de91049b9ab4.dcm'\ntemp_img = pydicom.dcmread(datapath).pixel_array\ntemp_box = [264.0, 152.0, 213.0, 379.0]\n\ntemp_img_formated, box = format_image(temp_img, temp_box)\nprint(box)\ntemp_color_img = cv.cvtColor(temp_img_formated, cv.COLOR_GRAY2RGB)\n\ncv.rectangle(temp_color_img, box, (0, 255, 0), 1)\n\nplt.imshow(temp_color_img)\n# plt.axis(\"off\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:05.774793Z","iopub.execute_input":"2025-01-05T10:02:05.775079Z","iopub.status.idle":"2025-01-05T10:02:06.011551Z","shell.execute_reply.started":"2025-01-05T10:02:05.775054Z","shell.execute_reply":"2025-01-05T10:02:06.010581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # disabling verbose tf logging\n\n# uncomment the following line if you want to force CPU\n# os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"-1\"\n\nimport tensorflow as tf\nprint(tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:06.012389Z","iopub.execute_input":"2025-01-05T10:02:06.012677Z","iopub.status.idle":"2025-01-05T10:02:06.017438Z","shell.execute_reply.started":"2025-01-05T10:02:06.012635Z","shell.execute_reply":"2025-01-05T10:02:06.016584Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels = pd.read_csv('/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')\ntrain_labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:06.018514Z","iopub.execute_input":"2025-01-05T10:02:06.018838Z","iopub.status.idle":"2025-01-05T10:02:06.064696Z","shell.execute_reply.started":"2025-01-05T10:02:06.018806Z","shell.execute_reply":"2025-01-05T10:02:06.063920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm  # 引入 tqdm\nimport os\nimport pydicom\nimport numpy as np\nimport tensorflow as tf\nimport math\n\ndef data_load(dataset, batch_size=3, full_data_path=\"/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_images/\", image_ext=\".dcm\",ds_type='not_trian'):\n    X = []\n    Y = []\n\n    # 使用 tqdm 包裝迭代器，顯示進度條\n    for index, row in tqdm(dataset.iterrows(), total=len(dataset), desc=\"Loading data\"):\n        filename = row['patientId']  # 根據欄位名稱取值\n\n        # 讀取 DICOM 影像\n        temp_img = pydicom.dcmread(os.path.join(full_data_path, filename + image_ext)).pixel_array\n        \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        img = img.astype(float) / 255.\n        box = np.asarray(box, dtype=float) / input_size\n        \n        # 合併標註與目標標籤\n        label = np.append(box, row['Target'])\n\n        # 將資料加入 X 和 Y\n        X.append(img)\n        Y.append(label)\n    # print(len(X))\n    # print(len(Y))\n    \n    # 將資料轉換為 TensorFlow 格式\n    X = np.array(X)\n    # if ds_type==\"train\":\n    #     X = np.tile(X, (3, 1, 1))  # 重複 3 次，沿第 0 軸 (樣本數量) 增加\n    #     Y = np.array(Y)  \n    #     Y = np.tile(Y,(3 ,1))\n    #     np.random.shuffle(X)\n    #     print(len(X))\n    data_X_len = len(X)\n    X = np.expand_dims(X, axis=3)\n    X = tf.convert_to_tensor(X, dtype=tf.float32)\n    Y = tf.convert_to_tensor(Y, dtype=tf.float32)\n    \n    # 建立 TensorFlow 資料集\n    result = tf.data.Dataset.from_tensor_slices((X, Y))\n\n    return result,data_X_len\nraw_train_ds,train_len = data_load(train_labels[:6001],ds_type=\"train\")\nprint(train_len)\nraw_valid_ds,valid_len = data_load(train_labels[6001:6301],ds_type=\"not train\")\nraw_test_ds, test_len = data_load(train_labels[6301:6501],ds_type=\"not train\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:06.067246Z","iopub.execute_input":"2025-01-05T10:02:06.067486Z","iopub.status.idle":"2025-01-05T10:02:48.798627Z","shell.execute_reply.started":"2025-01-05T10:02:06.067465Z","shell.execute_reply":"2025-01-05T10:02:48.797959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# plt.figure(figsize=(20, 10))\n# BATCH_SIZE = 32\n# i = 0\n# for images, labels in raw_train_ds:\n        \n#         print(labels)\n#         ax = plt.subplot(4, BATCH_SIZE//4, i + 1)\n#         label = labels[4]\n#         box = (labels[:4] * input_size)\n#         box = tf.cast(box, tf.int32)\n\n#         image = images.numpy().astype(\"float\") * 255.0\n#         image = image.astype(np.uint8)\n#         image_color = cv.cvtColor(image, cv.COLOR_GRAY2RGB)\n\n#         color = (0, 0, 255)\n#         if label > 0:\n#             color = (0, 255, 0)\n\n#         cv.rectangle(image_color, box.numpy(), color, 2)\n\n#         plt.imshow(image_color)\n#         plt.axis(\"off\")\n#         i += 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.800227Z","iopub.execute_input":"2025-01-05T10:02:48.800437Z","iopub.status.idle":"2025-01-05T10:02:48.804118Z","shell.execute_reply.started":"2025-01-05T10:02:48.800418Z","shell.execute_reply":"2025-01-05T10:02:48.803076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\ncpu_count = os.cpu_count()\nprint(f\"Available CPU cores: {cpu_count}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.805207Z","iopub.execute_input":"2025-01-05T10:02:48.805522Z","iopub.status.idle":"2025-01-05T10:02:48.821978Z","shell.execute_reply.started":"2025-01-05T10:02:48.805490Z","shell.execute_reply":"2025-01-05T10:02:48.821183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CLASSES = 2\n\ndef format_instance(image, label):\n    return image, (tf.one_hot(int(label[4]), CLASSES), [label[0], label[1], label[2], label[3]])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.822852Z","iopub.execute_input":"2025-01-05T10:02:48.823162Z","iopub.status.idle":"2025-01-05T10:02:48.836251Z","shell.execute_reply.started":"2025-01-05T10:02:48.823125Z","shell.execute_reply":"2025-01-05T10:02:48.835337Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BATCH_SIZE = 32\n\n# see https://www.tensorflow.org/guide/data_performance\n\ndef tune_training_ds(dataset):\n    dataset = dataset.map(format_instance, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.shuffle(1024, reshuffle_each_iteration=True)\n    dataset = dataset.repeat() # The dataset be repeated indefinitely.\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(tf.data.AUTOTUNE)\n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.837188Z","iopub.execute_input":"2025-01-05T10:02:48.837515Z","iopub.status.idle":"2025-01-05T10:02:48.849241Z","shell.execute_reply.started":"2025-01-05T10:02:48.837486Z","shell.execute_reply":"2025-01-05T10:02:48.848490Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def tune_validation_ds(dataset):\n    dataset = dataset.map(format_instance, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.batch(len(dataset) // 4)\n    dataset = dataset.repeat()\n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.850081Z","iopub.execute_input":"2025-01-05T10:02:48.850359Z","iopub.status.idle":"2025-01-05T10:02:48.863451Z","shell.execute_reply.started":"2025-01-05T10:02:48.850326Z","shell.execute_reply":"2025-01-05T10:02:48.862580Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = tune_training_ds(raw_train_ds)\nvalidation_ds = tune_validation_ds(raw_valid_ds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.864431Z","iopub.execute_input":"2025-01-05T10:02:48.864786Z","iopub.status.idle":"2025-01-05T10:02:48.935446Z","shell.execute_reply.started":"2025-01-05T10:02:48.864751Z","shell.execute_reply":"2025-01-05T10:02:48.934625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(20, 10))\nfor images, labels in train_ds.take(1):\n    for i in range(BATCH_SIZE):\n        # print(labels.shape)\n        ax = plt.subplot(4, BATCH_SIZE//4, i + 1)\n        label = labels[0][i]\n        box = (labels[1][i] * input_size)\n        box = tf.cast(box, tf.int32)\n\n        image = images[i].numpy().astype(\"float\") * 255.0\n        image = image.astype(np.uint8)\n        image_color = cv.cvtColor(image, cv.COLOR_GRAY2RGB)\n\n        color = (0, 0, 255)\n        if label[0] > 0:\n            color = (0, 255, 0)\n\n        cv.rectangle(image_color, box.numpy(), color, 2)\n\n        plt.imshow(image_color)\n        plt.axis(\"off\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:48.936474Z","iopub.execute_input":"2025-01-05T10:02:48.936793Z","iopub.status.idle":"2025-01-05T10:02:51.755162Z","shell.execute_reply.started":"2025-01-05T10:02:48.936761Z","shell.execute_reply":"2025-01-05T10:02:51.754315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DROPOUT_FACTOR = 0.5\n\ndef build_feature_extractor(inputs):\n\n    x = tf.keras.layers.Conv2D(16, kernel_size=3, activation='relu', input_shape=(input_size, input_size, 1))(inputs)\n    x = tf.keras.layers.AveragePooling2D(2,2)(x)\n\n    x = tf.keras.layers.Conv2D(32, kernel_size=3, activation = 'relu')(x)\n    x = tf.keras.layers.AveragePooling2D(2,2)(x)\n\n    x = tf.keras.layers.Conv2D(64, kernel_size=3, activation = 'relu')(x)\n    x = tf.keras.layers.Dropout(DROPOUT_FACTOR)(x)\n    x = tf.keras.layers.AveragePooling2D(2,2)(x)\n\n    return x\n\ndef build_model_adaptor(inputs):\n    x = tf.keras.layers.Flatten()(inputs)\n    x = tf.keras.layers.Dense(64, activation='relu')(x)\n    return x\n\ndef build_classifier_head(inputs):\n    return tf.keras.layers.Dense(CLASSES, activation='softmax', name = 'classifier_head')(inputs)\n\ndef build_regressor_head(inputs):\n    return tf.keras.layers.Dense(units = 4, name = 'regressor_head')(inputs)\n\ndef build_model(inputs):\n    \n    feature_extractor = build_feature_extractor(inputs)\n\n    model_adaptor = build_model_adaptor(feature_extractor)\n\n    classification_head = build_classifier_head(model_adaptor)\n\n    regressor_head = build_regressor_head(model_adaptor)\n\n    model = tf.keras.Model(inputs = inputs, outputs = [classification_head, regressor_head])\n\n    model.compile(optimizer=tf.keras.optimizers.Adam(), \n              loss = {'classifier_head' : 'categorical_crossentropy', 'regressor_head' : 'mse' }, \n              metrics = {'classifier_head' : 'accuracy', 'regressor_head' : 'mse' })\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:51.756264Z","iopub.execute_input":"2025-01-05T10:02:51.756628Z","iopub.status.idle":"2025-01-05T10:02:51.768059Z","shell.execute_reply.started":"2025-01-05T10:02:51.756597Z","shell.execute_reply":"2025-01-05T10:02:51.767091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = build_model(tf.keras.layers.Input(shape=(input_size, input_size, 1,)))\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:51.768902Z","iopub.execute_input":"2025-01-05T10:02:51.769154Z","iopub.status.idle":"2025-01-05T10:02:51.848707Z","shell.execute_reply.started":"2025-01-05T10:02:51.769134Z","shell.execute_reply":"2025-01-05T10:02:51.848020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# plot_model requires graphviz & pydot\n# see https://github.com/XifengGuo/CapsNet-Keras/issues/7#issuecomment-370745440\nfrom tensorflow.keras.utils import plot_model\n\nplot_model(model, show_shapes=True, show_layer_names=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:51.849487Z","iopub.execute_input":"2025-01-05T10:02:51.849780Z","iopub.status.idle":"2025-01-05T10:02:52.323856Z","shell.execute_reply.started":"2025-01-05T10:02:51.849749Z","shell.execute_reply":"2025-01-05T10:02:52.322757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 100\n\nhistory = model.fit(train_ds,\n                    steps_per_epoch=(6000 // BATCH_SIZE),\n                    validation_data=validation_ds, validation_steps=1, \n                    epochs=EPOCHS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:02:52.324783Z","iopub.execute_input":"2025-01-05T10:02:52.325010Z","iopub.status.idle":"2025-01-05T10:07:22.586275Z","shell.execute_reply.started":"2025-01-05T10:02:52.324991Z","shell.execute_reply":"2025-01-05T10:07:22.585318Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(history.history['classifier_head_accuracy'])\nplt.plot(history.history['val_classifier_head_accuracy'])\nplt.title('Model Accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'validation'], loc='upper left')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:22.587392Z","iopub.execute_input":"2025-01-05T10:07:22.587653Z","iopub.status.idle":"2025-01-05T10:07:22.803140Z","shell.execute_reply.started":"2025-01-05T10:07:22.587632Z","shell.execute_reply":"2025-01-05T10:07:22.802348Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluation","metadata":{}},{"cell_type":"code","source":"# # adapted from: https://pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/\n# def intersection_over_union(boxA, boxB):\n# \txA = max(boxA[0], boxB[0])\n# \tyA = max(boxA[1], boxB[1])\n# \txB = min(boxA[0] + boxA[2], boxB[0] + boxB[2])\n# \tyB = min(boxA[1] + boxA[3], boxB[1] + boxB[3])\n# \tinterArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)\n# \tboxAArea = (boxA[2] + 1) * (boxA[3] + 1)\n# \tboxBArea = (boxB[2] + 1) * (boxB[3] + 1)\n# \tiou = interArea / float(boxAArea + boxBArea - interArea)\n# \treturn iou\ndef intersection_over_union(boxA, boxB):\n    # 提取座標\n    xA = max(boxA[0], boxB[0])\n    yA = max(boxA[1], boxB[1])\n    xB = min(boxA[0] + boxA[2], boxB[0] + boxB[2])\n    yB = min(boxA[1] + boxA[3], boxB[1] + boxB[3])\n\n    # 計算交集區域\n    interWidth = max(0, xB - xA)\n    interHeight = max(0, yB - yA)\n    interArea = interWidth * interHeight\n\n    # 計算各框面積\n    boxAArea = boxA[2] * boxA[3]  # 預測框面積\n    boxBArea = boxB[2] * boxB[3]  # 實際框面積\n\n    # 若有任何框面積為 0，直接返回 IoU = 0\n    if boxAArea == 0 or boxBArea == 0:\n        return 0.0  # 空框情況\n\n    # 計算 IoU\n    iou = interArea / float(boxAArea + boxBArea - interArea)\n    return iou\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:22.803937Z","iopub.execute_input":"2025-01-05T10:07:22.804161Z","iopub.status.idle":"2025-01-05T10:07:22.809574Z","shell.execute_reply.started":"2025-01-05T10:07:22.804131Z","shell.execute_reply":"2025-01-05T10:07:22.808803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def tune_test_ds(dataset):\n    dataset = dataset.map(format_instance, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.batch(1) \n    dataset = dataset.repeat()\n    return dataset\n\ntest_ds = tune_test_ds(raw_test_ds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:22.810466Z","iopub.execute_input":"2025-01-05T10:07:22.810779Z","iopub.status.idle":"2025-01-05T10:07:22.854118Z","shell.execute_reply.started":"2025-01-05T10:07:22.810749Z","shell.execute_reply":"2025-01-05T10:07:22.853479Z"}},"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]\n\n    image = image[0]\n\n    actual_label = labels[0][0]\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] > 0.5 and actual_label[0] > 0) or (predicted_label[0] < 0.5 and actual_label[0] == 0):\n        color = (0, 255, 0)\n\n    img_label = \"unmasked\"\n    if predicted_label[0] > 0.5:\n        img_label = \"masked\"\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())\n\n    plt.title(\"IoU:\" + format(IoU, '.4f'))\n    plt.imshow(image_color)\n    plt.axis(\"off\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:22.855173Z","iopub.execute_input":"2025-01-05T10:07:22.855463Z","iopub.status.idle":"2025-01-05T10:07:24.608023Z","shell.execute_reply.started":"2025-01-05T10:07:22.855432Z","shell.execute_reply":"2025-01-05T10:07:24.606887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# 建立儲存比較圖的資料夾\noutput_dir = \"output_predictions\"\nos.makedirs(output_dir, exist_ok=True)\n\nplt.figure(figsize=(12, 10))\n\n# 將 test_ds 資料轉換為可迭代的列表\ntest_list = list(test_ds.take(200).as_numpy_iterator())\nprint(f\"Test Data Size: {len(test_list)}\")\n\n# 初始化計算變數\ncorrect_count = 0\ntotal_count = 0\niou_list = []\n\n# 開始處理每張圖片\nfor i in range(len(test_list)):\n\n    # ax = plt.subplot(4, 5, i + 1)\n\n    # 取得影像與標籤\n    image, labels = test_list[i]\n    predictions = model(image)\n\n    # 預測標籤與框\n    predicted_box = predictions[1][0] * input_size\n    predicted_box = tf.cast(predicted_box, tf.int32)\n    predicted_label = predictions[0][0]\n\n    # 取得實際標籤與框\n    image = image[0]\n    actual_label = labels[0][0]\n    actual_box = labels[1][0] * input_size\n    actual_box = tf.cast(actual_box, tf.int32)\n\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    # 比較預測標籤與實際標籤\n    color = (255, 0, 0)  # 預設紅色\n    if (predicted_label[0] > 0.5 and actual_label[0] > 0) or (predicted_label[0] < 0.5 and actual_label[0] == 0):\n        color = (0, 255, 0)  # 預測正確顯示綠色\n        correct_count += 1\n\n    total_count += 1\n\n    # 繪製預測標籤\n    img_label = \"unmasked\"\n    if predicted_label[0] > 0.5:\n        img_label = \"masked\"\n\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), \n                 (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), \n               cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0))\n\n    # 計算 IoU\n    IoU = intersection_over_union(predicted_box.numpy(), actual_box.numpy())\n    iou_list.append(IoU)\n\n    # 顯示圖片與 IoU 值\n    # plt.title(f\"IoU: {IoU:.4f}\")\n    # plt.imshow(image_color)\n    # plt.axis(\"off\")\n\n    # 儲存圖片到資料夾\n    output_path = os.path.join(output_dir, f\"prediction_{i + 1}.png\")\n    cv.imwrite(output_path, cv.cvtColor(image_color, cv.COLOR_RGB2BGR))  # OpenCV 儲存格式為 BGR\n\n# 計算準確率與 IoU 平均值\naccuracy = correct_count / total_count\naverage_iou = np.mean(iou_list)\n\nprint(f\"準確率 (Accuracy): {accuracy:.4f}\")\nprint(f\"平均 IoU (Mean IoU): {average_iou:.4f}\")\n\n# 儲存圖表\nplt.savefig(os.path.join(output_dir, \"all_predictions.png\"))\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:24.608954Z","iopub.execute_input":"2025-01-05T10:07:24.609262Z","iopub.status.idle":"2025-01-05T10:07:27.826635Z","shell.execute_reply.started":"2025-01-05T10:07:24.609235Z","shell.execute_reply":"2025-01-05T10:07:27.825930Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\n\n# 壓縮 output_predictions 資料夾為 predictions.zip\nshutil.make_archive('predictions', 'zip', output_dir)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T10:07:27.827483Z","iopub.execute_input":"2025-01-05T10:07:27.827820Z","iopub.status.idle":"2025-01-05T10:07:28.629005Z","shell.execute_reply.started":"2025-01-05T10:07:27.827787Z","shell.execute_reply":"2025-01-05T10:07:28.628074Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}