{"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":"markdown","source":"# YOLOv8m Self-Training for Object Segmentation in Semi-Supervised Learning\n\n**Welcome to our Kaggle Notebook!** This project showcases the power of YOLOv8m for object segmentation using a self-training approach in the realm of semi-supervised learning. With a limited set of 1200 labeled images and an extensive pool of over 5000 unlabeled data, we embark on a captivating journey to enhance object segmentation using YOLOv8m in the field of computer vision.\n\n**🌟 Key Highlights 🌟**\n\n- **YOLOv8m:** Our model of choice, YOLOv8m, is renowned for real-time object detection and segmentation, making it a powerful tool for our project.\n- **Semi-Supervised Learning:** We leverage semi-supervised learning principles to maximize the utility of our scarce labeled data.\n- **Self-Training:** Through an iterative self-training process, we gradually expand our labeled dataset by incorporating the model's confident predictions for improved object segmentation.\n\n> **⚠️ Warning - Training Duration:**\n> This notebook may take up to 9 hours to train on regular Kaggle GPUs, depending on the number of self training iterations. It is highly recommended to try this out on GPUs that can run without any restrictions, allowing you to experiment with larger models and more iterations for optimal results and in-depth exploration.\n\n> **📣 Note - Competition Submission:**\n> Please be aware that the competition submission component has not yet been implemented. It will be added in the near future, so stay tuned for updates.\n\nJoin us as we explore the world of object segmentation and semi-supervised learning with YOLOv8m, uncovering valuable insights and practical techniques along the way!\n","metadata":{}},{"cell_type":"markdown","source":"# 1. Importing Libraries","metadata":{}},{"cell_type":"code","source":"!pip install ultralytics==8.0.176 -q\n!pip install pycocotools -q","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-10-15T10:20:56.228038Z","iopub.execute_input":"2023-10-15T10:20:56.229005Z","iopub.status.idle":"2023-10-15T10:21:14.827338Z","shell.execute_reply.started":"2023-10-15T10:20:56.228962Z","shell.execute_reply":"2023-10-15T10:21:14.826134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport os\nos.environ['WANDB_DISABLED'] = 'true'\n\nimport shutil\nimport json\nimport random\nfrom tqdm import tqdm\n\nfrom PIL import Image\n\nfrom ultralytics import YOLO","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:14.829446Z","iopub.execute_input":"2023-10-15T10:21:14.829793Z","iopub.status.idle":"2023-10-15T10:21:18.545006Z","shell.execute_reply.started":"2023-10-15T10:21:14.829747Z","shell.execute_reply":"2023-10-15T10:21:18.544113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Creating Directories","metadata":{}},{"cell_type":"code","source":"# Creating Directories\n\nparent_dirpath = \"/kaggle/working/yolov8\"\n\nos.mkdir(parent_dirpath)\nos.mkdir(\"/kaggle/working/temp_images\")\nos.mkdir(\"/kaggle/working/temp_labels\")\n\nos.mkdir(os.path.join(parent_dirpath, \"train\"))\nos.mkdir(os.path.join(parent_dirpath, \"train\", \"images\"))\nos.mkdir(os.path.join(parent_dirpath, \"train\", \"labels\"))\n\nos.mkdir(os.path.join(parent_dirpath, \"test\"))\nos.mkdir(os.path.join(parent_dirpath, \"test\", \"images\"))\nos.mkdir(os.path.join(parent_dirpath, \"test\", \"labels\"))\n","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:18.546323Z","iopub.execute_input":"2023-10-15T10:21:18.547305Z","iopub.status.idle":"2023-10-15T10:21:18.554667Z","shell.execute_reply.started":"2023-10-15T10:21:18.547272Z","shell.execute_reply":"2023-10-15T10:21:18.553833Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Defining Functions\n\n- **tiff_to_jpg**: Converts .tiff images to .jpg format and stores them in kaggle's working directory for future use.\n- **vertices_to_txt**: Converts COCO labels to YOLO labels.","metadata":{}},{"cell_type":"code","source":"def tiff_to_jpg(file_name):\n    \n    tiff_image_path = \"/kaggle/input/hubmap-hacking-the-human-vasculature/train/\" + str(file_name) + \".tif\"\n    tiff_image = Image.open(tiff_image_path)\n    destination_path = \"/kaggle/working/temp_images/\" + file_name + \".jpg\"\n    tiff_image.save(destination_path, 'JPEG')\n    \n    return 0\n\ndef vertices_to_txt(file_id, annotations, list_of_vertices):\n    \n    file_contents = []\n\n    for i in range(len(annotations)):\n\n        yolo_format = []\n        flag = 1\n\n        if annotations[i]['type'] == 'glomerulus':\n            yolo_format.append(str(1))\n            flag = 1\n        elif annotations[i]['type'] == 'blood_vessel':\n            yolo_format.append(str(0))\n            flag = 1\n        else:\n            flag = 0\n\n\n        if (flag):\n\n            list_of_vertices = annotations[i]['coordinates'][0]\n            for vertex in list_of_vertices:\n                yolo_format.append(str(vertex[0]/512))\n                yolo_format.append(str(vertex[1]/512))\n\n        yolo_format = \" \".join(yolo_format)\n\n        file_contents.append(yolo_format)\n\n    file_name = \"/kaggle/working/temp_labels/\" + str(file_id) + \".txt\"\n\n    with open(file_name, \"w\") as file:\n        if (len(file_contents) == 0):\n            pass\n        elif (len(file_contents) == 1):\n            file.write(str(file_contents[-1]))\n        else:\n            for k in range(len(file_contents)-1):\n                file.write(str(file_contents[k]) + \"\\n\")\n\n            file.write(str(file_contents[-1]))\n            \n    return 0","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:18.557357Z","iopub.execute_input":"2023-10-15T10:21:18.559795Z","iopub.status.idle":"2023-10-15T10:21:18.569578Z","shell.execute_reply.started":"2023-10-15T10:21:18.559745Z","shell.execute_reply":"2023-10-15T10:21:18.568675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Data Processing","metadata":{}},{"cell_type":"code","source":"train_filepath = \"/kaggle/input/hubmap-hacking-the-human-vasculature/train\"\n\nall_images = os.listdir(train_filepath)\nprint(\"No. of images:\", len(all_images))","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:18.571077Z","iopub.execute_input":"2023-10-15T10:21:18.571531Z","iopub.status.idle":"2023-10-15T10:21:19.148995Z","shell.execute_reply.started":"2023-10-15T10:21:18.571502Z","shell.execute_reply":"2023-10-15T10:21:19.148079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Use the above-defined functions to process the data.\n\njson_filepath = \"/kaggle/input/hubmap-hacking-the-human-vasculature/polygons.jsonl\"\nfile_ids = []\n\nwith open(json_filepath, 'r') as file:\n    \n    for line in file:\n        data = json.loads(line)\n        file_id = data['id']\n        annotations = data['annotations']\n        list_of_vertices = annotations[0]['coordinates'][0]\n        tiff_to_jpg(file_id)\n        vertices_to_txt(file_id, annotations, list_of_vertices)\n        file_ids.append(file_id)","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:19.150120Z","iopub.execute_input":"2023-10-15T10:21:19.150895Z","iopub.status.idle":"2023-10-15T10:21:59.658904Z","shell.execute_reply.started":"2023-10-15T10:21:19.150865Z","shell.execute_reply":"2023-10-15T10:21:59.657984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a list of all file-ids which are unlabelled.\n\nall_file_ids = []\n\nfor file_name in all_images:\n    file_name = file_name.split('.')\n    all_file_ids.append(file_name[0])\n\nunlabelled_images = list(set(all_file_ids).difference(file_ids))","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:59.660231Z","iopub.execute_input":"2023-10-15T10:21:59.660741Z","iopub.status.idle":"2023-10-15T10:21:59.670047Z","shell.execute_reply.started":"2023-10-15T10:21:59.660699Z","shell.execute_reply":"2023-10-15T10:21:59.669096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Randomly split the labelled dataset into train and test sets with a ratio of 0.8.\n\nrandom.shuffle(file_ids)\n\n# Create the Train directory\nfor i in range(0, int(0.8*len(file_ids))):\n    \n    old_path_img = \"/kaggle/working/temp_images/\" + str(file_ids[i]) + \".jpg\"\n    new_path_img = \"/kaggle/working/yolov8/train/images/\" + str(file_ids[i]) + \".jpg\"\n    shutil.copy(old_path_img, new_path_img)\n    \n    old_path_txt = \"/kaggle/working/temp_labels/\" + str(file_ids[i]) + \".txt\"\n    new_path_txt = \"/kaggle/working/yolov8/train/labels/\" + str(file_ids[i]) + \".txt\"\n    shutil.copy(old_path_txt, new_path_txt)\n\n# Create the Test directory\nfor i in range(int(0.8*len(file_ids)), len(file_ids)):\n    \n    old_path_img = \"/kaggle/working/temp_images/\" + str(file_ids[i]) + \".jpg\"\n    new_path_img = \"/kaggle/working/yolov8/test/images/\" + str(file_ids[i]) + \".jpg\"\n    shutil.copy(old_path_img, new_path_img)\n    \n    old_path_txt = \"/kaggle/working/temp_labels/\" + str(file_ids[i]) + \".txt\"\n    new_path_txt = \"/kaggle/working/yolov8/test/labels/\" + str(file_ids[i]) + \".txt\"\n    shutil.copy(old_path_txt, new_path_txt)\n    \nshutil.rmtree(\"/kaggle/working/temp_images\")\nshutil.rmtree(\"/kaggle/working/temp_labels\")","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:21:59.671517Z","iopub.execute_input":"2023-10-15T10:21:59.672513Z","iopub.status.idle":"2023-10-15T10:22:00.272031Z","shell.execute_reply.started":"2023-10-15T10:21:59.672483Z","shell.execute_reply":"2023-10-15T10:22:00.271118Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create the custom_config.yaml file for YOLO model.\n\nwith open(\"/kaggle/working/custom_config.yaml\", \"w\") as file:\n    file.write(\"path: /kaggle/working/yolov8\" + \"\\n\")\n    file.write(\"train: train/images\" + \"\\n\")\n    file.write(\"val: test/images\" + \"\\n\")\n    file.write(\"test: test/images\" + \"\\n\")\n    file.write(\"nc: 2\" + \"\\n\")\n    file.write(\"names: ['blood_vessel','glomerulus']\")","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:22:00.273883Z","iopub.execute_input":"2023-10-15T10:22:00.274176Z","iopub.status.idle":"2023-10-15T10:22:00.279123Z","shell.execute_reply.started":"2023-10-15T10:22:00.274155Z","shell.execute_reply":"2023-10-15T10:22:00.278246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a list of all file-ids in the test set.\n\ntest_set_file_ids = []\ntest_set_filepath = \"/kaggle/working/yolov8/test/images\"\ntest_set_images_filepaths = os.listdir(test_set_filepath)\n\nfor file_name in test_set_images_filepaths:\n    file_name = file_name.split('.')\n    test_set_file_ids.append(file_name[0])\n        \nlen(test_set_file_ids)","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:22:00.282116Z","iopub.execute_input":"2023-10-15T10:22:00.283113Z","iopub.status.idle":"2023-10-15T10:22:00.294912Z","shell.execute_reply.started":"2023-10-15T10:22:00.283083Z","shell.execute_reply":"2023-10-15T10:22:00.293756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Self training\n\nSelf-training is an iterative approach where a model is trained on a small amount of labeled data, and then it's used to make predictions on unlabeled data.\n\n- **Pseudo-Labeling**: Predictions on unlabeled data are used to create pseudo-labels, treating these predictions as if they were true labels for the unlabeled data.\n\n- **Confidence Thresholding**: Typically, a confidence threshold is applied to select only highly confident predictions, reducing the risk of including incorrect labels. Here the confidence score is set to **0.4** as it gives very good results at this score.\n\n- **Iterative Refinement**: The model's pseudo-labeled dataset is gradually expanded and refined over multiple iterations, allowing the model to learn from its own predictions. We set the number of iterations to **10**.\n\n- **Benefits**: Self-training can improve model performance in semi-supervised learning by leveraging the large pool of unlabeled data, making it a resource-efficient approach for training machine learning models.\n\n- **Risks**: Incorrect predictions with high confidence are used as pseudo-labels, potentially degrading the model's performance over iterations. Careful selection and verification of pseudo-labeled data and setting appropriate confidence thresholds are essential to mitigate this risk.","metadata":{}},{"cell_type":"code","source":"for iteration in range(10):\n    \n    # Model training, we enable data augmentation\n    model = YOLO('yolov8m-seg.pt')\n    results = model.train(data='/kaggle/working/custom_config.yaml',\n                          epochs=15, imgsz=512, optimizer='Adam',\n                          seed=42, close_mosaic=0, mask_ratio=1, val=True,\n                          degrees=90, translate=0.1, scale=0.5, flipud=0.5, fliplr=0.5)\n\n    images_added = 0\n    \n    # Update unlabelled image list here.\n    \n    # 1. Get all file_ids from train_set\n    train_set_file_ids = []\n    train_set_filepath = \"/kaggle/working/yolov8/train/images\"\n    train_set_images_filepaths = os.listdir(train_set_filepath)\n\n    for file_name in train_set_images_filepaths:\n        file_name = file_name.split('.')\n        train_set_file_ids.append(file_name[0])\n        \n    # 2. Add file_ids from test_set\n    all_labelled_image_file_ids = train_set_file_ids + test_set_file_ids\n    \n    # 3. Find differences from inputs and combined list & create new unlabelled image file_id list.\n    unlabelled_images = list(set(all_file_ids).difference(all_labelled_image_file_ids))\n\n    for file_id in tqdm(unlabelled_images):\n\n        # Create a temporary image for prediction\n        tiff_image_path = \"/kaggle/input/hubmap-hacking-the-human-vasculature/train/\" + str(file_id) + \".tif\"\n        tiff_image = Image.open(tiff_image_path)\n        destination_path = \"/kaggle/working/temp_image.jpg\"\n        tiff_image.save(destination_path, 'JPEG')\n\n        results = model.predict(destination_path, verbose=False)\n\n        # This flag determines whether the predicted image is added to the training set. If set to 0, the image will not be added.\n        flag = 1\n        file_contents = []\n\n        # If no detections, set flag to 0.\n        for result in results:\n            boxes = result.boxes.conf\n            if len(boxes) != 0:\n                classes = result.boxes.cls\n                masks = result.masks.xyn\n            else:\n                flag = 0\n        \n        # If model's confidence score is less than 0.4, set flag to 0.\n        if (flag):\n            for i in range(len(boxes)):\n                if boxes[i] < 0.4:\n                    flag=0\n                    break\n\n        # Use the image for training, if flag set to 1.            \n        if(flag):\n            \n            # Copy image\n            des_img_filepath = os.path.join(\"/kaggle/working/yolov8/train/images/\" + str(file_id) + \".jpg\")\n            shutil.copy(destination_path, des_img_filepath)\n\n            for i in range(len(boxes)):\n\n                yolo_format = []\n\n                if classes[i] == 1:\n                    yolo_format.append(str(1))\n                else:\n                    yolo_format.append(str(0))\n\n                list_of_vertices = masks[i]\n                for vertex in list_of_vertices:\n                    yolo_format.append(str(vertex[0]))\n                    yolo_format.append(str(vertex[1]))\n\n                yolo_format = \" \".join(yolo_format)\n\n                file_contents.append(yolo_format)\n            \n            # Create YOLO labels\n            file_name = os.path.join(\"/kaggle/working/yolov8/train/labels/\" + str(file_id) + \".txt\")\n\n            with open(file_name, \"w\") as file:\n                if (len(file_contents) == 1):\n                    file.write(str(file_contents[-1]))\n                else:\n                    for k in range(len(file_contents)-1):\n                        file.write(str(file_contents[k]) + \"\\n\")\n\n                    file.write(str(file_contents[-1]))\n\n            images_added += 1\n\n        flag = 1\n        del model\n\n    print(\"Images added to training set:\", images_added)    ","metadata":{"execution":{"iopub.status.busy":"2023-10-15T09:29:07.010533Z","iopub.execute_input":"2023-10-15T09:29:07.010848Z","iopub.status.idle":"2023-10-15T10:13:21.256204Z","shell.execute_reply.started":"2023-10-15T09:29:07.010821Z","shell.execute_reply":"2023-10-15T10:13:21.254979Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Final length of Train dataset:\", len(list(os.listdir(\"/kaggle/working/yolov8/train/labels\"))))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# results = model.predict(\"/kaggle/working/temp_image.jpg\")\n# count = 0\n\n# for result in results:\n#     boxes = result.boxes.conf # confidence scores\n#     classes = result.boxes.cls # class in float\n#     masks = result.masks.xyn # location of each segment, normalised","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:15:28.443535Z","iopub.execute_input":"2023-10-15T10:15:28.443887Z","iopub.status.idle":"2023-10-15T10:15:28.518809Z","shell.execute_reply.started":"2023-10-15T10:15:28.443863Z","shell.execute_reply":"2023-10-15T10:15:28.517658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# masks[0].shape","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:18:51.934373Z","iopub.execute_input":"2023-10-15T10:18:51.935013Z","iopub.status.idle":"2023-10-15T10:18:51.941986Z","shell.execute_reply.started":"2023-10-15T10:18:51.934984Z","shell.execute_reply":"2023-10-15T10:18:51.940911Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Final model training\n- The model is trained on the final training dataset created after 10 loops of self-training.\n","metadata":{}},{"cell_type":"code","source":"model = YOLO('yolov8x-seg.pt')\nresults = model.train(data='/kaggle/working/custom_config.yaml',\n                      epochs=50, imgsz=512, optimizer='Adam',\n                      seed=42, close_mosaic=0, mask_ratio=1, val=True,\n                      degrees=90, translate=0.1, scale=0.5, flipud=0.5, fliplr=0.5)","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:22:11.984480Z","iopub.execute_input":"2023-10-15T10:22:11.984828Z","iopub.status.idle":"2023-10-15T12:33:55.800882Z","shell.execute_reply.started":"2023-10-15T10:22:11.984798Z","shell.execute_reply":"2023-10-15T12:33:55.799473Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 7. Competition Submission\n- NOT IMPLEMENTED YET","metadata":{}},{"cell_type":"code","source":"# import base64\n# import numpy as np\n# from pycocotools import _mask as coco_mask\n# import typing as t\n# import zlib\n\n# def encode_binary_mask(mask: np.ndarray) -> t.Text:\n#     \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n\n#     # check input mask --\n#     if mask.dtype != np.bool:\n#     raise ValueError(\n#         \"encode_binary_mask expects a binary mask, received dtype == %s\" %\n#         mask.dtype)\n\n#     mask = np.squeeze(mask)\n#     if len(mask.shape) != 2:\n#     raise ValueError(\n#         \"encode_binary_mask expects a 2d mask, received shape == %s\" %\n#         mask.shape)\n\n#     # convert input mask to expected COCO API input --\n#     mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1)\n#     mask_to_encode = mask_to_encode.astype(np.uint8)\n#     mask_to_encode = np.asfortranarray(mask_to_encode)\n\n#     # RLE encode mask --\n#     encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n\n#     # compress and base64 encoding --\n#     binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n#     base64_str = base64.b64encode(binary_str)\n#     return base64_str","metadata":{"execution":{"iopub.status.busy":"2023-10-15T10:18:00.874662Z","iopub.execute_input":"2023-10-15T10:18:00.875004Z","iopub.status.idle":"2023-10-15T10:18:00.886015Z","shell.execute_reply.started":"2023-10-15T10:18:00.874973Z","shell.execute_reply":"2023-10-15T10:18:00.884621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# test_filepath = \"/kaggle/input/hubmap-hacking-the-human-vasculature/test\"\n\n# df_submission = pd.DataFrame(columns=['id', 'height', 'width', 'prediction'])\n\n# for file in file_list:\n    \n#     file_name = file_name.split('.')\n#     file_name = file_name[0]\n    \n#     tiff_image_path = \"/kaggle/input/hubmap-hacking-the-human-vasculature/test/\" + str(file)\n#     tiff_image = Image.open(tiff_image_path)\n#     destination_path = \"/kaggle/working/temp_images/\" + file_name + \".jpg\"\n#     tiff_image.save(destination_path, 'JPEG')\n    \n#     results = model.predict(destination_path)\n    \n#     prediction = []\n    \n#     for result in results:\n#           for i in range(len(result.boxes.cls)):\n    #         prediction.append(str(result.boxes.cls[i]))\n    #         prediction.append(str(result.boxes.conf[i]))\n    #         prediction.append(encode_binary_mask(\"ndarray_mask\"))\n    \n#     prediction = \" \".join(prediction)\n\n#     row1 = pd.Series([file_name, 512, 512, prediction])\n#     df_submission.append(row1, ignore_index=True)\n\n# df_submission.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# df_submission.to_csv(\"submission.csv\", ignore_index=True)","metadata":{"execution":{"iopub.status.busy":"2023-10-15T09:22:34.292486Z","iopub.status.idle":"2023-10-15T09:22:34.297983Z","shell.execute_reply.started":"2023-10-15T09:22:34.297722Z","shell.execute_reply":"2023-10-15T09:22:34.297746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# for result in results:\n#     boxes = result.boxes.conf\n#     classes = result.boxes.cls\n#     masks = result.masks.xyn","metadata":{},"execution_count":null,"outputs":[]}]}