{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"#@title\nfrom IPython.display import Image\nImage(url='https://i.imgur.com/DeB62Nl.png', width=500)","metadata":{"cellView":"form","id":"JcO15PWJ4buE","outputId":"ae9c55bb-9879-476c-d032-c98326dd87d3","_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-06-09T08:25:06.284117Z","iopub.status.idle":"2023-06-09T08:25:06.285145Z","shell.execute_reply.started":"2023-06-09T08:25:06.284878Z","shell.execute_reply":"2023-06-09T08:25:06.284902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* [Kaggle Competition Page](https://www.kaggle.com/competitions/leeds-sciml-sea-ice-segmentation)\n* [Weights & Biases SciML Leeds team](https://wandb.ai/sciml-leeds)","metadata":{"id":"IYasnTqQ4FCZ"}},{"cell_type":"code","source":"#@title\nfrom IPython.display import HTML\n\nHTML('<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/U4amljFGkiw\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>')","metadata":{"cellView":"form","id":"BydyQmZdGwiJ","outputId":"6555450f-b2b5-42db-a1f2-630bf07a1703","execution":{"iopub.status.busy":"2023-06-09T08:25:06.286549Z","iopub.status.idle":"2023-06-09T08:25:06.288350Z","shell.execute_reply.started":"2023-06-09T08:25:06.288096Z","shell.execute_reply":"2023-06-09T08:25:06.288120Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Setup\n\nFirst, let's set up the environment by installing the required libraries and importing the necessary modules.","metadata":{"id":"NbXUeSmjgKoC"}},{"cell_type":"markdown","source":"# 🚀 Installing and importing","metadata":{"id":"GMNpTVDY35go"}},{"cell_type":"code","source":"#import all the necessary libraries for this notebook\n\n#weights and biases\n!pip install wandb --quiet\nimport wandb\nfrom wandb.keras import WandbCallback\n\n#maths and plotting\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n#machine learning\nfrom keras.models import Model\nfrom keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D, concatenate, Dropout\nfrom keras.optimizers import adam\nfrom keras.callbacks import EarlyStopping\nfrom keras.metrics import MeanIoU\nfrom sklearn.model_selection import train_test_split\n\n#file system\nimport os, glob\nimport tifffile\nfrom tqdm import tqdm","metadata":{"id":"db04d9de-1357-4a3b-a80c-f8e6f938440d","outputId":"072a4636-6c23-41e4-eee8-f38d607efb6a","execution":{"iopub.status.busy":"2023-06-09T08:29:34.925206Z","iopub.execute_input":"2023-06-09T08:29:34.925661Z","iopub.status.idle":"2023-06-09T08:30:06.454629Z","shell.execute_reply.started":"2023-06-09T08:29:34.925624Z","shell.execute_reply":"2023-06-09T08:30:06.453634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The `patch_paths` variables are arrays containing the names of the patch files.\nEach patch patch has a corresponding SAR image, multispectral image, and label. (Except we've not given you the labels for the test patch paths).\n","metadata":{"id":"b70ItMfw_jWi"}},{"cell_type":"code","source":"DATA_DIR = '/kaggle/input/leeds-sciml-sea-ice-segmentation/sciml'\ntrain_patch_paths = np.array([os.path.basename(i[:-9]) for i in glob.glob(f'{DATA_DIR}/train/*_sar.tiff')])\ntest_patch_paths = np.array([os.path.basename(i[:-9]) for i in glob.glob(f'{DATA_DIR}/test/*_sar.tiff')])\n\n#display patch paths\ntrain_patch_paths","metadata":{"id":"9a7f52ed","outputId":"3849a116-1f3d-427f-820c-333020fb018b","execution":{"iopub.status.busy":"2023-06-09T08:32:12.484680Z","iopub.execute_input":"2023-06-09T08:32:12.485375Z","iopub.status.idle":"2023-06-09T08:32:12.748759Z","shell.execute_reply.started":"2023-06-09T08:32:12.485341Z","shell.execute_reply":"2023-06-09T08:32:12.747864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#@title We use a data generator generates batches of data on-the-fly, reducing the memory requirements and enabling training on large datasets that may not fit entirely into memory.\n\nfrom tensorflow.keras.utils import Sequence\n\ndef load_img(path):\n    img = tifffile.imread(path)\n    return img\n\nclass DataGenerator(Sequence):\n    def __init__(self, patch_paths, data_dir, batch_size, split='train'):\n        self.patch_paths = patch_paths\n        self.data_dir = data_dir\n        self.batch_size = batch_size\n        self.split = split\n\n    def __len__(self):\n        return len(self.patch_paths) // self.batch_size\n\n    def __getitem__(self, index):\n        batch_paths = self.patch_paths[index * self.batch_size:(index + 1) * self.batch_size]\n        X_sar, X_ms, y = [], [], []\n\n        for patch_path in batch_paths:\n            try:\n                sar_path = os.path.join(self.data_dir, self.split, patch_path + '_sar.tiff')\n                ms_path = os.path.join(self.data_dir, self.split, patch_path + '_vis.tiff')\n                if self.split != 'test':\n                  label_path = os.path.join(self.data_dir, self.split, patch_path + '_ref.tiff')\n                  label_arr = load_img(label_path)\n                  y.append(label_arr)\n\n                sar_arr = load_img(sar_path)\n                ms_arr = load_img(ms_path)\n\n                X_sar.append(sar_arr)\n                X_ms.append(ms_arr)\n                \n            except Exception as e:\n                print(e)\n                continue\n\n        X_sar = np.expand_dims(np.array(X_sar), -1)[:, ::3, ::3, :]\n        X_ms = np.array(X_ms)\n        if self.split != 'test':\n          y = np.expand_dims(np.array(y), -1)\n          return [X_sar, X_ms], y\n        else:\n          return [X_sar, X_ms], [None]*len(X_sar)\n    \nbatch_size = 32\n\n# Split data into train and test sets\ntrain_patch_paths, val_patch_paths = train_test_split(train_patch_paths, test_size=0.2, random_state=42)\ntrain_generator = DataGenerator(train_patch_paths, DATA_DIR, batch_size)\nval_generator = DataGenerator(val_patch_paths, DATA_DIR, batch_size)\ntest_generator = DataGenerator(test_patch_paths, DATA_DIR, batch_size, split='test')","metadata":{"cellView":"form","id":"Ns1toXTi1TPH","execution":{"iopub.status.busy":"2023-06-09T08:32:13.309680Z","iopub.execute_input":"2023-06-09T08:32:13.310039Z","iopub.status.idle":"2023-06-09T08:32:13.326942Z","shell.execute_reply.started":"2023-06-09T08:32:13.310007Z","shell.execute_reply":"2023-06-09T08:32:13.325743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The U-Net model architecture is a convolutional neural network that consists of an encoder and a decoder.\nThe encoder extracts features from the input images, while the decoder upsamples the features to generate the final segmentation map.\n\nThe model is compiled with the Adam optimiser and binary cross-entropy loss.\nDuring training, the validation data is used to monitor the performance of the model and early stopping is applied to prevent overfitting.\n\nThe training progress can be logged using the wandb library, which allows tracking and visualisation of metrics in a web interface.","metadata":{"id":"n6emsiJwAjsq"}},{"cell_type":"code","source":"def unet():\n    # Input layers\n    input_sar = Input((240, 240, 1))\n    input_ms = Input((240, 240, 3))\n    \n    # Resizing input_sar\n    # resized_ms = UpSampling2D(size=(3, 3))(input_ms)\n\n    # Concatenate inputs\n    concat = concatenate([input_sar, input_ms], axis=-1)\n    \n    # Encoding path\n    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(concat)\n    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1)\n    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n    \n    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1)\n    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2)\n    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n    \n    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2)\n    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3)\n    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n    \n    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool3)\n    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv4)\n    drop4 = Dropout(0.5)(conv4)\n    pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)\n    \n    # Bridge\n    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool4)\n    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv5)\n    drop5 = Dropout(0.5)(conv5)\n    \n    # Decoding path\n    up6 = UpSampling2D(size=(2, 2))(drop5)\n    up6 = Conv2D(512, 2, activation='relu', padding='same', kernel_initializer='he_normal')(up6)\n    merge6 = concatenate([drop4, up6], axis=3)\n    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge6)\n    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv6)\n    \n    up7 = UpSampling2D(size=(2, 2))(conv6)\n    up7 = Conv2D(256, 2, activation='relu', padding='same', kernel_initializer='he_normal')(up7)\n    merge7 = concatenate([conv3, up7], axis=3)\n    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge7)\n    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv7)\n\n    # Output layers\n    output = Conv2D(1, 1, activation = 'sigmoid')(conv7)\n    output = UpSampling2D(size=(4, 4))(output)\n\n    # Model\n    model = Model(inputs=[input_sar, input_ms], outputs=output)\n\n    return model\n\n# Create model\nmodel = unet()","metadata":{"id":"1b099c85-bcb8-4d7b-9dd3-7f0559988ee1","execution":{"iopub.status.busy":"2023-06-09T08:32:13.722157Z","iopub.execute_input":"2023-06-09T08:32:13.722538Z","iopub.status.idle":"2023-06-09T08:32:19.751723Z","shell.execute_reply.started":"2023-06-09T08:32:13.722509Z","shell.execute_reply":"2023-06-09T08:32:19.750746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=adam.Adam(learning_rate=1e-4), loss='binary_crossentropy', metrics=['accuracy', MeanIoU(num_classes=2)])","metadata":{"id":"db75cd17-64a9-4d0f-969b-213044600b84","execution":{"iopub.status.busy":"2023-06-09T08:32:19.754372Z","iopub.execute_input":"2023-06-09T08:32:19.755074Z","iopub.status.idle":"2023-06-09T08:32:19.776776Z","shell.execute_reply.started":"2023-06-09T08:32:19.755040Z","shell.execute_reply":"2023-06-09T08:32:19.775898Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ✅ Sign Up\n\nSign up to a free [Weights & Biases account here](https://wandb.ai/signup)\n\n[Weights and Biases docs](https://docs.wandb.ai/quickstart)","metadata":{"id":"d_rn880_3fwQ"}},{"cell_type":"code","source":"# Initialise a new W&B run\nwandb_enabled = False\n\nearlystopper = EarlyStopping(patience=5, verbose=1)\nif wandb_enabled:\n    wandb.init(entity='sciml-leeds', project='sea-ice-segmentation')\n    history = model.fit(train_generator,\n          validation_data=val_generator,\n          epochs=10,\n          use_multiprocessing=True,\n          workers=6,\n          callbacks=[earlystopper, WandbCallback()])\n    wandb.finish()\nelse:\n    history = model.fit(train_generator,\n          validation_data=val_generator,\n          epochs=10,\n          use_multiprocessing=True,\n          workers=6,\n          callbacks=[earlystopper])","metadata":{"id":"fa83f318-0dae-4745-b31e-b202cf3f3028","outputId":"4a00403c-d3a6-46ec-bcff-7ccd3df123f7","execution":{"iopub.status.busy":"2023-06-09T08:32:19.778328Z","iopub.execute_input":"2023-06-09T08:32:19.778669Z","iopub.status.idle":"2023-06-09T08:43:55.522290Z","shell.execute_reply.started":"2023-06-09T08:32:19.778638Z","shell.execute_reply":"2023-06-09T08:43:55.518196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#@title Now plot the results of the training run:\n\nmetric_to_plot = 'loss' #@param ['loss', 'accuracy', 'mean_io_u']\n\nys = history.history[metric_to_plot]\nys_val = history.history['val_'+metric_to_plot]\nnum_epochs = len(ys)\nplt.plot(range(num_epochs), ys, label='train')\nplt.plot(range(num_epochs), ys_val, label='val')\nplt.legend()\nplt.ylabel(metric_to_plot)","metadata":{"id":"qFKBK4tuEqf4","outputId":"cd2d03ef-e03e-4221-f959-c1e81bbeff42","execution":{"iopub.status.busy":"2023-06-09T08:56:29.705620Z","iopub.execute_input":"2023-06-09T08:56:29.706029Z","iopub.status.idle":"2023-06-09T08:56:30.008881Z","shell.execute_reply.started":"2023-06-09T08:56:29.705979Z","shell.execute_reply":"2023-06-09T08:56:30.007889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's dive into the visualization of our sea ice segmentation results using the validation dataset. By running the code snippet provided, we obtain valuable insights into the model's performance and its ability to accurately identify sea ice boundaries.\n\nFirst, the model makes predictions on the validation dataset using the model.predict() function. The resulting predictions are stored in the variable preds. These predictions represent the segmentation masks, indicating the presence or absence of sea ice in the images.\n\nNext, we extract a single batch of data from the validation generator using next(iter(val_generator)). This batch consists of SAR (Synthetic Aperture Radar) images, multispectral (MS) images, and their corresponding ground truth segmentation masks. These components are unpacked into the variables X_sar_test, X_ms_test, and y_test, respectively.\n\nNow, we proceed to visualize the results. Within a loop, we iterate over the predictions stored in preds. For each prediction, we create a figure with a grid layout of four subplots.\n\nThe first subplot, labeled \"SAR Image,\" displays the SAR image from the validation set. SAR imagery provides valuable information about the surface characteristics of sea ice.\n\nThe second subplot, labeled \"MS Image,\" showcases the multispectral image captured during the validation process. Multispectral data enables us to analyze sea ice from different spectral perspectives.\n\nMoving on to the third subplot, titled \"Segmentation Mask,\" we observe the model's predicted segmentation mask. The thresholding operation (`preds[i,:,:,0]>threshold`) helps us visualize the predicted sea ice boundaries clearly.\n\nLastly, the fourth subplot, labeled \"Ground Truth,\" presents the actual ground truth segmentation mask obtained from the validation dataset. This serves as a reference to assess the accuracy of our model's predictions.\n\nBy carefully examining these subplots, we can compare the model's predictions against the ground truth and gain insights into the performance and limitations of our sea ice segmentation model.","metadata":{"id":"wyQ1RUjkCYk8"}},{"cell_type":"code","source":"#@markdown You need to specify the threshold value, which determines the threshold for classifying pixels as foreground or background. Adjusting this value may affect the performance of the segmentation.\n\nthreshold = 0.5 #@param {type:\"slider\", min:0, max:1, step:0.1}","metadata":{"id":"1dd545dd","execution":{"iopub.status.busy":"2023-06-09T08:56:32.945500Z","iopub.execute_input":"2023-06-09T08:56:32.945877Z","iopub.status.idle":"2023-06-09T08:56:32.950560Z","shell.execute_reply.started":"2023-06-09T08:56:32.945846Z","shell.execute_reply":"2023-06-09T08:56:32.949644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = model.predict(val_generator,steps=1)\n\n(X_sar_test, X_ms_test), y_test = next(iter(val_generator))\n\nfor i in range(preds.shape[0]):\n    plt.figure(figsize=(10, 5))\n    plt.subplot(141)\n    plt.imshow(X_sar_test[i,:,:,:])\n    plt.title('SAR Image')\n    plt.axis('off')\n    plt.subplot(142)\n    plt.imshow(X_ms_test[i,:,:,:])\n    plt.title('MS Image')\n    plt.axis('off')\n    plt.subplot(143)\n    plt.imshow(preds[i,:,:,0]>threshold)\n    plt.title('Segmentation Mask')\n    plt.axis('off')\n    plt.subplot(144)\n    plt.imshow(y_test[i,:,:,0])\n    plt.title('Ground Truth')\n    plt.axis('off')\n    plt.tight_layout()\n    plt.show()\n","metadata":{"id":"05cb5504-13b0-40f0-87f8-956eee759e23","outputId":"bee3187e-def8-4c24-b319-ed1e0d5270db","execution":{"iopub.status.busy":"2023-06-09T08:56:36.195513Z","iopub.execute_input":"2023-06-09T08:56:36.195941Z","iopub.status.idle":"2023-06-09T08:56:55.104382Z","shell.execute_reply.started":"2023-06-09T08:56:36.195910Z","shell.execute_reply":"2023-06-09T08:56:55.103416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Are there any notable differences between the predicted segmentation mask and the ground truth?\n* How does adjusting the threshold value impact the visualisation of the segmentation mask?\n* Can you identify any challenging areas where the model struggles to accurately predict sea ice boundaries?\n* What potential implications can accurate sea ice segmentation have on environmental research and decision-making processes?","metadata":{"id":"CKj__AgcCp7j"}},{"cell_type":"markdown","source":"# Generate Submission\nTa-da! 📝🎉 Our model is trained and ready, and it's time to run our predictions on the test set and create the submission file.\n\nA submission file named 'submission.csv' is created to store the predictions.\nFor each image in the test set, the image ID and run-length encoded pixels are written to the file.","metadata":{"id":"8tne9Y6E4AYh"}},{"cell_type":"code","source":"import pandas as pd\n\n#ref:https://www.kaggle.com/paulorzp/run-length-encode-and-decode.\ndef rle_encode(mask):\n    '''\n    mask: numpy array binary mask \n    1 - mask \n    0 - background\n    Returns encoded run length \n    '''\n    pixels = mask.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n# Predict on test data\npredictions = model.predict(test_generator)\n\n# Generate submission DataFrame\nsubmission_df = pd.DataFrame(columns=['ImageId', 'EncodedPixels'])\n\nfor i, patch_path in enumerate(test_patch_paths):\n    image_id = os.path.basename(patch_path)\n    mask = predictions[i, :, :, 0] > threshold\n    encoded_pixels = rle_encode(mask)\n    submission_df.loc[i] = [image_id, encoded_pixels]\n\n# Save submission DataFrame to CSV file\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"id":"2e4ZJjVt8Nik","outputId":"98e160dc-485f-4715-ccb5-018aa6ad2a1a","execution":{"iopub.status.busy":"2023-06-09T08:57:06.720662Z","iopub.execute_input":"2023-06-09T08:57:06.721065Z","iopub.status.idle":"2023-06-09T08:57:23.275223Z","shell.execute_reply.started":"2023-06-09T08:57:06.721034Z","shell.execute_reply":"2023-06-09T08:57:23.273341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"GTUt7j6K9RH3"},"execution_count":null,"outputs":[]}]}