{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.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":97984,"databundleVersionId":14096757,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":14296668,"sourceType":"datasetVersion","datasetId":9126054},{"sourceId":288482841,"sourceType":"kernelVersion"}],"dockerImageVersionId":31236,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip -q install ultralytics --no-deps --upgrade\n!pip -q install \"decord\"\n!pip -q install \"ftfy==6.1.1\"\n!pip -q install \"iopath>=0.1.10\"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from kaggle_secrets import UserSecretsClient\nimport wandb\n\n\n#### login to wandb\n!yolo settings wandb=True\nuser_secrets = UserSecretsClient()\nwandb_key = user_secrets.get_secret(\"wandbKey\")\nwandb.login(key=wandb_key)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport os\n\nRANDOM_SEED = 8486234\nSRC_ANNOTATION_PATH = \"/kaggle/input/ecg-singal-detection-coco/ECG_annotations_coco.json\"\nSRC_IMAGE_DIR = Path(\"/kaggle/input/physionet-ecg-image-digitization/train\")\n\nDATASET_BASE = Path(\"/kaggle/working/ecg_detection_dataset\")\nTRAIN_IMAGE_DIR = DATASET_BASE / \"images\" / \"train\"\nTRAIN_LABEL_DIR = DATASET_BASE / \"labels\" / \"train\"\nVAL_IMAGE_DIR = DATASET_BASE / \"images\" / \"val\"\nVAL_LABEL_DIR = DATASET_BASE / \"labels\" / \"val\"\n\nDATA_CONFIG = \"./dataset_config.yml\"\nPROJECT_NAME = \"PhysiNet - Digitization of ECG Images Detection models\"\nEXPERIMENT_NAME = \"speicific_use_gentle_augmentations_training\"\n\n# create the directoris\nos.makedirs(TRAIN_IMAGE_DIR, exist_ok=True)\nos.makedirs(TRAIN_LABEL_DIR, exist_ok=True)\nos.makedirs(VAL_IMAGE_DIR, exist_ok=True)\nos.makedirs(VAL_LABEL_DIR, exist_ok=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data preperation","metadata":{}},{"cell_type":"code","source":"from concurrent.futures import ThreadPoolExecutor, as_completed\nfrom typing import List, Callable, Union, Dict\nfrom tqdm.notebook import tqdm\nimport pandas as pd\nimport shutil\nimport os\n\nimport cv2\nimport albumentations as A\nimport matplotlib.pyplot as plt\n\n\ndef convert_to_yolo_txts(images_data: List[Dict], annotations: List[Dict], out_dir: Path):\n    for image_data in images_data:\n        image_file = image_data['file_name']\n        image_id = image_file.split('.')[0]\n        img_w = image_data['width']\n        img_h = image_data['height']\n        image_annotations = [ann for ann in annotations if ann['image_id'] == image_data['id']]\n        for ann in image_annotations:\n            class_id = ann['category_id']\n            xmin, ymin, bw, bh = ann['bbox']\n            x_center = (xmin + bw / 2) / img_w\n            y_center = (ymin + bh / 2) / img_h\n            w_norm = bw / img_w\n            h_norm = bh / img_h\n            \n            # write the line to respective files\n            line = f\"{class_id-1} {x_center} {y_center} {w_norm} {h_norm}\"\n            txt_path = out_dir / f\"{image_id}.txt\"\n            with open(txt_path, \"a\") as f:\n                f.write(line + \"\\n\")\n    \n\ndef copy_images(images_data: List[Dict], dest_path: Path):\n    with ThreadPoolExecutor() as executor:\n        futures = []\n        for image_data in images_data:\n            file_name = image_data['file_name']\n            folder_name = file_name.split('-')[0]\n            src_image = SRC_IMAGE_DIR / folder_name /  file_name\n            dst_image = dest_path / file_name\n            if not dst_image.exists():\n                future = executor.submit(shutil.copy, src_image, dst_image)\n                futures.append(future)\n\n        with tqdm(total=len(futures)) as pbar:\n            for future in as_completed(futures):\n                try:\n                    future.result()\n                    pbar.update()\n                except Exception as err:\n                    executor.shutdown(wait=False, cancel_futures=True)\n                    raise err\n\n    return True\n\n\ndef visualize_albumentations_pipeline(image_path, transforms_list):\n    # Load image\n    image = cv2.imread(image_path)\n    if image is None:\n        raise ValueError(\"Image not found at the given path\")\n\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n    # Compose transforms\n    transform = A.Compose(transforms_list)\n\n    # Create figure\n    fig, axes = plt.subplots(4, 4, figsize=(20, 20))\n    axes = axes.flatten()\n\n    for i in range(16):\n        augmented = transform(image=image)[\"image\"]\n        axes[i].imshow(augmented)\n        axes[i].axis(\"off\")\n        axes[i].set_title(f\"Augmented {i+1}\")\n\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport json\nimport yaml\n\n# load full annotations\nwith open(SRC_ANNOTATION_PATH, 'r') as file:\n    coco_anns = json.load(file)\n\n# split the data frame int\ntrain_image_data, val_image_data = train_test_split(coco_anns['images'], test_size=0.2, random_state=RANDOM_SEED)\n\n# copy the images to the respective directories\ncopy_images(train_image_data, dest_path=TRAIN_IMAGE_DIR)\ncopy_images(val_image_data, dest_path=VAL_IMAGE_DIR)\n\n# convert annotations to txts\nconvert_to_yolo_txts(train_image_data, coco_anns['annotations'], out_dir=TRAIN_LABEL_DIR)\nconvert_to_yolo_txts(val_image_data, coco_anns['annotations'], out_dir=VAL_LABEL_DIR)\n\n# save data config file\ndata_config = {\n    \"path\": str(DATASET_BASE),\n    \"train\": \"images/train\",\n    \"val\": \"images/val\",\n    \"names\":  {\n        cat['id']-1: cat['name']\n        for cat in coco_anns['categories'][:-1]\n    }\n}\n\nwith open(DATA_CONFIG, \"w\") as f:\n    yaml.dump(data_config, f, sort_keys=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import random\n\n# albumentation_train_transforms = [\n#     A.AdditiveNoise(\n#         noise_type='gaussian', \n#         spatial_mode='shared', \n#         noise_params={\"mean_range\": [0, 0], \"std_range\": [0.05, 0.2]}, \n#         p=0.1\n#     ),\n#     A.OneOf(\n#         [\n#             A.ColorJitter(\n#                 brightness=(0.8, 1.2), \n#                 contrast=(0.8, 1.2), \n#                 saturation=(0.8, 1.2), \n#                 hue=(-0.1, 0.1), \n#                 p=0.7\n#             ),\n#             A.ToGray(p=1.0)\n#         ],\n#         p=0.7\n#     ),\n#     A.OneOf(\n#         [\n#             A.CLAHE(clip_limit=16.0, tile_grid_size=(16, 16), p=0.5),\n#             A.Emboss(alpha=[0.9, 0.9], strength=[0.9, 0.9], p=0.5),\n#             A.Sharpen(p=0.5)\n#         ],\n#         p=0.5\n#     )\n# ]\n\n# image_data = random.choice(train_image_data)\n# folder_name = image_data['file_name'].split('-')[0]\n# image_path = SRC_IMAGE_DIR / folder_name / image_data['file_name']\n# visualize_albumentations_pipeline(image_path, albumentation_train_transforms)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train the model","metadata":{}},{"cell_type":"code","source":"from ultralytics import YOLO\n\nmodel = YOLO(\"yolo11s.pt\")\nresults = model.train(\n    data=DATA_CONFIG, \n    project=PROJECT_NAME, \n    name=EXPERIMENT_NAME,\n    seed=RANDOM_SEED,\n    pretrained=True,\n    half=True, \n    exist_ok=True,\n    amp=True,\n    imgsz=1024,\n    freeze=None,\n    single_cls=False,\n    batch=16,\n    epochs=300,\n    patience=300,\n    warmup_epochs=50,\n    lr0=1e-4,\n    optimizer='Adam',\n    weight_decay=0.01,\n    mosaic=0.5,\n    close_mosaic=150,\n    translate=0.1,\n    hsv_s=0.3,\n    hsv_v=0.2,\n    # disable rest of the augmentation\n    # augmentations=albumentation_train_transforms,\n    hsv_h=0,\n    degrees=0,\n    shear=0,\n    scale=0,\n    flipud=0,\n    fliplr=0,\n    perspective=0,\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}