{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.10","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":117682,"databundleVersionId":14443416,"sourceType":"competition"}],"dockerImageVersionId":30512,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Surface Segmentation RGB Mask UNET Callback\n","metadata":{"papermill":{"duration":0.007345,"end_time":"2023-06-18T13:10:15.218791","exception":false,"start_time":"2023-06-18T13:10:15.211446","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"https://www.kaggle.com/code/stpeteishii/surface-segmentation-rgb-mask-unet-callback<br/>\nhttps://www.kaggle.com/code/stpeteishii/surface-segmentation-check-callback-images","metadata":{}},{"cell_type":"markdown","source":"An effective method to find the optimal number of epochs.Save the predicted mask images generated every 3 epoch and try arranging them later. You can see the best number of epochs at a glance. If the epoch number is too large, the mask image will be lost.","metadata":{}},{"cell_type":"code","source":"!pip install imantics --quiet","metadata":{"papermill":{"duration":14.622585,"end_time":"2023-06-18T13:10:29.87525","exception":false,"start_time":"2023-06-18T13:10:15.252665","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:00.774615Z","iopub.execute_input":"2025-11-15T04:49:00.774969Z","iopub.status.idle":"2025-11-15T04:49:08.382285Z","shell.execute_reply.started":"2025-11-15T04:49:00.774942Z","shell.execute_reply":"2025-11-15T04:49:08.381208Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nimport tensorflow as tf\nimport json\nimport os\nimport imantics\nfrom PIL import Image\nfrom skimage.transform import resize\nimport random\nfrom sklearn.model_selection import train_test_split\n%matplotlib inline","metadata":{"papermill":{"duration":8.494547,"end_time":"2023-06-18T13:10:38.377093","exception":false,"start_time":"2023-06-18T13:10:29.882546","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:08.384740Z","iopub.execute_input":"2025-11-15T04:49:08.385143Z","iopub.status.idle":"2025-11-15T04:49:08.392935Z","shell.execute_reply.started":"2025-11-15T04:49:08.385087Z","shell.execute_reply":"2025-11-15T04:49:08.392106Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mpaths0=[]\ntpaths0=[]\nmdirname='/kaggle/input/vesuvius-challenge-surface-detection/train_labels'\nfor dirname, _, filenames in os.walk('/kaggle/input/vesuvius-challenge-surface-detection/train_images'):\n    for filename in filenames:\n        if filename[-4:]=='.tif':\n            tpaths0+=[(os.path.join(dirname, filename))]\n            mpaths0+=[(os.path.join(mdirname, filename))]\ntpaths=sorted(tpaths0)[0:100]\nmpaths=sorted(mpaths0)[0:100]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-15T04:49:08.393963Z","iopub.execute_input":"2025-11-15T04:49:08.394200Z","iopub.status.idle":"2025-11-15T04:49:09.304041Z","shell.execute_reply.started":"2025-11-15T04:49:08.394181Z","shell.execute_reply":"2025-11-15T04:49:09.303115Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"timages=[]\nfor path in tpaths:\n    timages+=[cv2.imread(path)]\nmimages=[]\nfor path in mpaths:\n    mimages+=[cv2.imread(path)]","metadata":{"papermill":{"duration":0.432947,"end_time":"2023-06-18T13:10:38.83966","exception":false,"start_time":"2023-06-18T13:10:38.406713","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:09.306544Z","iopub.execute_input":"2025-11-15T04:49:09.307301Z","iopub.status.idle":"2025-11-15T04:49:10.490361Z","shell.execute_reply.started":"2025-11-15T04:49:09.307275Z","shell.execute_reply":"2025-11-15T04:49:10.489437Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_size=512\ninput_image_size=(512,512)","metadata":{"papermill":{"duration":0.014184,"end_time":"2023-06-18T13:10:38.861008","exception":false,"start_time":"2023-06-18T13:10:38.846824","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:10.491622Z","iopub.execute_input":"2025-11-15T04:49:10.492286Z","iopub.status.idle":"2025-11-15T04:49:10.496464Z","shell.execute_reply.started":"2025-11-15T04:49:10.492250Z","shell.execute_reply":"2025-11-15T04:49:10.495652Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_image(path):\n    img = cv2.imread(path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, (image_size, image_size))\n    return img","metadata":{"papermill":{"duration":0.014613,"end_time":"2023-06-18T13:10:38.88257","exception":false,"start_time":"2023-06-18T13:10:38.867957","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:10.497560Z","iopub.execute_input":"2025-11-15T04:49:10.497889Z","iopub.status.idle":"2025-11-15T04:49:10.508665Z","shell.execute_reply.started":"2025-11-15T04:49:10.497858Z","shell.execute_reply":"2025-11-15T04:49:10.508011Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Input images ","metadata":{"papermill":{"duration":0.006797,"end_time":"2023-06-18T13:10:38.896166","exception":false,"start_time":"2023-06-18T13:10:38.889369","status":"completed"},"tags":[]}},{"cell_type":"code","source":"rows = 3\ncols = 3\nfig, ax = plt.subplots(rows, cols, figsize = (8,8))\nfor i, ax in enumerate(ax.flat):\n    if i < len(timages):\n        img = read_image(f\"{tpaths[i]}\")\n        #print(img.shape)\n        title=tpaths[i].split('/')[-1]\n        ax.set_title(title)\n        ax.imshow(img)\n        ax.axis('off')","metadata":{"papermill":{"duration":1.626193,"end_time":"2023-06-18T13:10:40.529542","exception":false,"start_time":"2023-06-18T13:10:38.903349","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:10.509670Z","iopub.execute_input":"2025-11-15T04:49:10.510206Z","iopub.status.idle":"2025-11-15T04:49:11.597316Z","shell.execute_reply.started":"2025-11-15T04:49:10.510184Z","shell.execute_reply":"2025-11-15T04:49:11.596416Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Ground truth masks","metadata":{"papermill":{"duration":0.013274,"end_time":"2023-06-18T13:10:40.556208","exception":false,"start_time":"2023-06-18T13:10:40.542934","status":"completed"},"tags":[]}},{"cell_type":"code","source":"print(len(mimages))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-15T04:49:36.369525Z","iopub.execute_input":"2025-11-15T04:49:36.369849Z","iopub.status.idle":"2025-11-15T04:49:36.374794Z","shell.execute_reply.started":"2025-11-15T04:49:36.369821Z","shell.execute_reply":"2025-11-15T04:49:36.373940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(rows, cols, figsize = (8,8))\nfor i, ax in enumerate(ax.flat):\n    if i < len(mimages):\n        img = read_image(mpaths[i])\n        #img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        title=mpaths[i].split('/')[-1]\n        ax.set_title(title)\n        ax.imshow(img*100)\n        ax.axis('off')","metadata":{"papermill":{"duration":1.305902,"end_time":"2023-06-18T13:10:41.875653","exception":false,"start_time":"2023-06-18T13:10:40.569751","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:50:13.061828Z","iopub.execute_input":"2025-11-15T04:50:13.062327Z","iopub.status.idle":"2025-11-15T04:50:14.089197Z","shell.execute_reply.started":"2025-11-15T04:50:13.062290Z","shell.execute_reply":"2025-11-15T04:50:14.088318Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#MASKS=np.zeros((1,image_size, image_size, 1), dtype=bool) \nMASKS=np.zeros((1,image_size, image_size, 3),dtype=np.uint8) #####\nIMAGES=np.zeros((1,image_size, image_size, 3),dtype=np.uint8)\n\nfor j,path in enumerate(tpaths[0:81]): ##the smaller, the faster\n    #print(j)\n    image = read_image(path)\n    image_ex = np.expand_dims(image, axis=0)\n    IMAGES = np.vstack([IMAGES, image_ex])\n    \n    mask = read_image(mpaths[j])\n    #mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) #####\n    mask_ex = np.expand_dims(mask*100, axis=0)    \n    MASKS = np.vstack([MASKS, mask_ex])\n","metadata":{"papermill":{"duration":13.544982,"end_time":"2023-06-18T13:10:55.435252","exception":false,"start_time":"2023-06-18T13:10:41.89027","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:50:58.216728Z","iopub.execute_input":"2025-11-15T04:50:58.217450Z","iopub.status.idle":"2025-11-15T04:51:00.709835Z","shell.execute_reply.started":"2025-11-15T04:50:58.217419Z","shell.execute_reply":"2025-11-15T04:51:00.709167Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images=np.array(IMAGES)[1:81]\nmasks=np.array(MASKS)[1:81]\nprint(images.shape,masks.shape)","metadata":{"papermill":{"duration":0.096946,"end_time":"2023-06-18T13:10:55.547008","exception":false,"start_time":"2023-06-18T13:10:55.450062","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:51:33.111203Z","iopub.execute_input":"2025-11-15T04:51:33.112001Z","iopub.status.idle":"2025-11-15T04:51:33.162277Z","shell.execute_reply.started":"2025-11-15T04:51:33.111974Z","shell.execute_reply":"2025-11-15T04:51:33.161441Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images_train, images_test, masks_train, masks_test = train_test_split(\n    images, masks, test_size=0.4, random_state=42)","metadata":{"_kg_hide-input":true,"papermill":{"duration":0.054954,"end_time":"2023-06-18T13:10:55.616592","exception":false,"start_time":"2023-06-18T13:10:55.561638","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:51:37.846428Z","iopub.execute_input":"2025-11-15T04:51:37.847094Z","iopub.status.idle":"2025-11-15T04:51:37.891188Z","shell.execute_reply.started":"2025-11-15T04:51:37.847067Z","shell.execute_reply":"2025-11-15T04:51:37.890502Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(images_train), len(masks_train))\nprint(len(images_test), len(masks_test))","metadata":{"papermill":{"duration":0.023401,"end_time":"2023-06-18T13:10:55.654576","exception":false,"start_time":"2023-06-18T13:10:55.631175","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:12.623380Z","iopub.status.idle":"2025-11-15T04:49:12.623688Z","shell.execute_reply.started":"2025-11-15T04:49:12.623545Z","shell.execute_reply":"2025-11-15T04:49:12.623559Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# U-Net","metadata":{"papermill":{"duration":0.014068,"end_time":"2023-06-18T13:10:55.682581","exception":false,"start_time":"2023-06-18T13:10:55.668513","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def conv_block(input, num_filters):\n    conv = tf.keras.layers.Conv2D(num_filters, 3, padding=\"same\")(input)\n    conv = tf.keras.layers.BatchNormalization()(conv)\n    conv = tf.keras.layers.Activation(\"relu\")(conv)\n    conv = tf.keras.layers.Conv2D(num_filters, 3, padding=\"same\")(conv)\n    conv = tf.keras.layers.BatchNormalization()(conv)\n    conv = tf.keras.layers.Activation(\"relu\")(conv)\n    return conv\n\ndef encoder_block(input, num_filters):\n    skip = conv_block(input, num_filters)\n    pool = tf.keras.layers.MaxPool2D((2, 2))(skip)\n    return skip, pool\n\ndef decoder_block(input, skip, num_filters):\n    up_conv = tf.keras.layers.Conv2DTranspose(num_filters, (2,2), strides=2, padding=\"same\")(input)\n    conv = tf.keras.layers.Concatenate()([up_conv, skip])\n    conv = conv_block(conv, num_filters)\n    return conv\n\ndef Unet(input_shape):\n    inputs = tf.keras.layers.Input(input_shape)\n\n    skip1, pool1 = encoder_block(inputs, 64)\n    skip2, pool2 = encoder_block(pool1, 128)\n    skip3, pool3 = encoder_block(pool2, 256)\n    skip4, pool4 = encoder_block(pool3, 512)\n\n    bridge = conv_block(pool4, 1024)\n\n    decode1 = decoder_block(bridge, skip4, 512)\n    decode2 = decoder_block(decode1, skip3, 256)\n    decode3 = decoder_block(decode2, skip2, 128)\n    decode4 = decoder_block(decode3, skip1, 64)\n\n    outputs = tf.keras.layers.Conv2D(3, 1, padding=\"same\", activation=\"sigmoid\")(decode4) #####\n\n    model = tf.keras.models.Model(inputs, outputs, name=\"U-Net\")\n    return model\n\nunet_model = Unet((512,512,3))\nunet_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\nunet_model.summary()","metadata":{"_kg_hide-output":true,"papermill":{"duration":3.445869,"end_time":"2023-06-18T13:10:59.142513","exception":false,"start_time":"2023-06-18T13:10:55.696644","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:12.624867Z","iopub.status.idle":"2025-11-15T04:49:12.625194Z","shell.execute_reply.started":"2025-11-15T04:49:12.625012Z","shell.execute_reply":"2025-11-15T04:49:12.625025Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import gc\ngc.collect()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Callback","metadata":{}},{"cell_type":"code","source":"#!rm -rf output_masks","metadata":{"execution":{"iopub.status.busy":"2025-11-15T04:49:12.626511Z","iopub.status.idle":"2025-11-15T04:49:12.626774Z","shell.execute_reply.started":"2025-11-15T04:49:12.626641Z","shell.execute_reply":"2025-11-15T04:49:12.626653Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow import keras\n\nclass SaveMaskImagesCallback(keras.callbacks.Callback):\n    def __init__(self, output_dir, images, masks, save_every_n_epochs=4):\n        super(SaveMaskImagesCallback, self).__init__()\n        self.output_dir = output_dir\n        self.images = images\n        self.masks = masks\n        self.save_every_n_epochs = save_every_n_epochs\n\n    def on_epoch_end(self, epoch, logs=None):\n        if (epoch + 1) % self.save_every_n_epochs == 0:\n            predictions = self.model.predict(self.images)\n            for i, pred_mask in enumerate(predictions):\n                output_path = os.path.join(self.output_dir, f\"mask_{i:02d}_ep_{epoch + 1:03d}.png\")\n                pred_mask = (pred_mask * 255).astype(np.uint8)\n                keras.preprocessing.image.save_img(output_path, pred_mask)\n\noutput_directory = \"output_masks\"  \nos.makedirs(output_directory, exist_ok=True)\nmask_callback = SaveMaskImagesCallback(output_directory, images_train, masks_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-15T04:49:12.628380Z","iopub.status.idle":"2025-11-15T04:49:12.628789Z","shell.execute_reply.started":"2025-11-15T04:49:12.628578Z","shell.execute_reply":"2025-11-15T04:49:12.628597Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train","metadata":{"papermill":{"duration":0.028949,"end_time":"2023-06-18T13:10:59.201642","exception":false,"start_time":"2023-06-18T13:10:59.172693","status":"completed"},"tags":[]}},{"cell_type":"code","source":"unet_result = unet_model.fit(\n    images_train, masks_train, \n    validation_split=0.2, batch_size=4, epochs=120,\n    callbacks=[mask_callback]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-15T04:49:12.630409Z","iopub.status.idle":"2025-11-15T04:49:12.630816Z","shell.execute_reply.started":"2025-11-15T04:49:12.630603Z","shell.execute_reply":"2025-11-15T04:49:12.630622Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"    unet_result = unet_model.fit(\n        images_train, masks_train, \n        validation_split = 0.2, batch_size = 4, epochs = 75)","metadata":{"papermill":{"duration":755.257056,"end_time":"2023-06-18T13:23:34.486848","exception":false,"start_time":"2023-06-18T13:10:59.229792","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2023-09-20T16:22:20.30432Z","iopub.status.idle":"2023-09-20T16:22:20.304785Z","shell.execute_reply.started":"2023-09-20T16:22:20.304576Z","shell.execute_reply":"2023-09-20T16:22:20.304596Z"}}},{"cell_type":"markdown","source":"# Predict","metadata":{"papermill":{"duration":0.156292,"end_time":"2023-06-18T13:23:34.802866","exception":false,"start_time":"2023-06-18T13:23:34.646574","status":"completed"},"tags":[]}},{"cell_type":"code","source":"unet_predict = unet_model.predict(images_test)","metadata":{"papermill":{"duration":5.983347,"end_time":"2023-06-18T13:23:40.943134","exception":false,"start_time":"2023-06-18T13:23:34.959787","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:12.631942Z","iopub.status.idle":"2025-11-15T04:49:12.632378Z","shell.execute_reply.started":"2025-11-15T04:49:12.632163Z","shell.execute_reply":"2025-11-15T04:49:12.632183Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_result(idx, og, unet, target, p):\n    \n    fig, axs = plt.subplots(1, 3, figsize=(12,12))\n    axs[0].set_title(\"Original \"+str(idx))\n    axs[0].imshow(og)\n    axs[0].axis('off')\n    \n    axs[1].set_title(\"U-Net: p>\"+str(p))\n    axs[1].imshow(unet*0.9)\n    axs[1].axis('off')\n    \n    axs[2].set_title(\"Ground Truth\")\n    axs[2].imshow(target)\n    axs[2].axis('off')\n\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2025-11-15T04:49:12.633821Z","iopub.status.idle":"2025-11-15T04:49:12.634271Z","shell.execute_reply.started":"2025-11-15T04:49:12.634044Z","shell.execute_reply":"2025-11-15T04:49:12.634064Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"r1,r2,r3,r4=0.7,0.8,0.9,0.99","metadata":{"execution":{"iopub.status.busy":"2025-11-15T04:49:12.635270Z","iopub.status.idle":"2025-11-15T04:49:12.635548Z","shell.execute_reply.started":"2025-11-15T04:49:12.635415Z","shell.execute_reply":"2025-11-15T04:49:12.635428Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unet_predict1 = (unet_predict > r1).astype(np.uint8)\nunet_predict2 = (unet_predict > r2).astype(np.uint8)\nunet_predict3 = (unet_predict > r3).astype(np.uint8)\nunet_predict4 = (unet_predict > r4).astype(np.uint8)","metadata":{"papermill":{"duration":0.16669,"end_time":"2023-06-18T13:23:41.265224","exception":false,"start_time":"2023-06-18T13:23:41.098534","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:12.636936Z","iopub.status.idle":"2025-11-15T04:49:12.637239Z","shell.execute_reply.started":"2025-11-15T04:49:12.637072Z","shell.execute_reply":"2025-11-15T04:49:12.637084Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_test_idx = random.sample(range(len(unet_predict)), 3)\nfor idx in show_test_idx: \n    show_result(idx, images_test[idx], unet_predict1[idx], masks_test[idx], r1)\n    show_result(idx, images_test[idx], unet_predict2[idx], masks_test[idx], r2)\n    show_result(idx, images_test[idx], unet_predict3[idx], masks_test[idx], r3)\n    show_result(idx, images_test[idx], unet_predict4[idx], masks_test[idx], r4)\n    print()","metadata":{"papermill":{"duration":1.291922,"end_time":"2023-06-18T13:23:43.348556","exception":false,"start_time":"2023-06-18T13:23:42.056634","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-11-15T04:49:12.638527Z","iopub.status.idle":"2025-11-15T04:49:12.638926Z","shell.execute_reply.started":"2025-11-15T04:49:12.638720Z","shell.execute_reply":"2025-11-15T04:49:12.638739Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}