{"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":"## 📒 Notebooks:\n* Install darknet: [Build YOLOv4 with darknet 🔨](https://www.kaggle.com/gimarcecaml/build-darknet-yolo4)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# 🚩 Version Info\n| Version | Model | mAP@0.50| Comment\n|---|---|---|---|\n| v0 | YOLOv4 | 33.14%| fold 2 / 20 |","metadata":{}},{"cell_type":"markdown","source":"# ❗️ Inputs needed\n- `libcuda.so` imported from [here](https://www.kaggle.com/denispotapov/libcuda)\n- Don't forget to turn on your GPU!\n- For visualizing predictions (see at the bottom) a custom trained YOLOv4 model in COTS dataset is provided [here](https://www.kaggle.com/gimarcecaml/yolov4-cots)","metadata":{}},{"cell_type":"markdown","source":"### Please if this kernel is useful, <font color='red'>please upvote !!</font>","metadata":{}},{"cell_type":"markdown","source":"# 🛠 Install Libraries","metadata":{}},{"cell_type":"code","source":"!apt update\n!apt install --yes python-opencv\n!apt install --yes libopencv-dev\n!/bin/bash -c 'echo \"/opt/conda/lib/\" > /etc/ld.so.conf.d/opencv.conf'\n!ldconfig\n!pip install imagesize","metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-12-05T16:57:53.867576Z","iopub.execute_input":"2021-12-05T16:57:53.868408Z","iopub.status.idle":"2021-12-05T16:58:53.016516Z","shell.execute_reply.started":"2021-12-05T16:57:53.868289Z","shell.execute_reply":"2021-12-05T16:58:53.015416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 📚 Import Libraries","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\nimport pickle\nimport matplotlib.pyplot as plt\nimport ast\nimport glob\nimport shutil\nimport sys\nimport numpy as np\nimport imagesize\nimport cv2\nfrom tqdm.notebook import tqdm","metadata":{"execution":{"iopub.status.busy":"2021-12-05T16:58:53.020093Z","iopub.execute_input":"2021-12-05T16:58:53.021145Z","iopub.status.idle":"2021-12-05T16:58:53.289040Z","shell.execute_reply.started":"2021-12-05T16:58:53.021093Z","shell.execute_reply":"2021-12-05T16:58:53.288107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Install 🛠 darknet framework","metadata":{}},{"cell_type":"markdown","source":"- More details in [Build YOLOv4 with darknet 🔨](https://www.kaggle.com/gimarcecaml/build-darknet-yolo4)","metadata":{}},{"cell_type":"code","source":"!git clone https://github.com/AlexeyAB/darknet.git\n\n%cd darknet\n\n!cp '../../input/libcuda/libcuda.so' .\n\n!sed -i 's/OPENCV=0/OPENCV=1/g' Makefile\n!sed -i 's/GPU=0/GPU=1/g' Makefile\n!sed -i 's/CUDNN=0/CUDNN=1/g' Makefile\n!sed -i 's/CUDNN_HALF=0/CUDNN_HALF=1/g' Makefile\n!sed -i 's/LIBSO=0/LIBSO=1/' Makefile\n!sed -i \"s/ARCH= -gencode arch=compute_60,code=sm_60/ARCH= ${ARCH_VALUE}/g\" Makefile\n\n!sed -i 's/LDFLAGS+= -L\\/usr\\/local\\/cuda\\/lib64 -lcuda -lcudart -lcublas -lcurand/LDFLAGS+= -L\\/usr\\/local\\/cuda\\/lib64 -lcudart -lcublas -lcurand -L\\/kaggle\\/working\\/darknet -lcuda/' Makefile\n!make &> compile.log","metadata":{"execution":{"iopub.status.busy":"2021-12-05T16:58:53.290734Z","iopub.execute_input":"2021-12-05T16:58:53.291025Z","iopub.status.idle":"2021-12-05T17:00:55.369658Z","shell.execute_reply.started":"2021-12-05T16:58:53.290987Z","shell.execute_reply":"2021-12-05T17:00:55.367848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check there were no error\n!tail compile.log","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-05T17:00:55.373644Z","iopub.execute_input":"2021-12-05T17:00:55.374683Z","iopub.status.idle":"2021-12-05T17:00:56.115478Z","shell.execute_reply.started":"2021-12-05T17:00:55.374580Z","shell.execute_reply":"2021-12-05T17:00:56.114242Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!./darknet detector train","metadata":{"execution":{"iopub.status.busy":"2021-11-29T20:42:38.823092Z","iopub.execute_input":"2021-11-29T20:42:38.823753Z","iopub.status.idle":"2021-11-29T20:42:42.287208Z","shell.execute_reply.started":"2021-11-29T20:42:38.823687Z","shell.execute_reply":"2021-11-29T20:42:42.28632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Please Upvote if you find this Helpful","metadata":{}},{"cell_type":"markdown","source":"# 📖 Meta Data\n* `train_images/` - Folder containing training set photos of the form `video_{video_id}/{video_frame}.jpg`.\n\n* `[train/test].csv` - Metadata for the images. As with other test files, most of the test metadata data is only available to your notebook upon submission. Just the first few rows available for download.\n\n* `video_id` - ID number of the video the image was part of. The video ids are not meaningfully ordered.\n* `video_frame` - The frame number of the image within the video. Expect to see occasional gaps in the frame number from when the diver surfaced.\n* `sequence` - ID of a gap-free subset of a given video. The sequence ids are not meaningfully ordered.\n* `sequence_frame` - The frame number within a given sequence.\n* `image_id` - ID code for the image, in the format `{video_id}-{video_frame}`\n* `annotations` - The bounding boxes of any starfish detections in a string format that can be evaluated directly with Python. Does not use the same format as the predictions you will submit. Not available in test.csv. A bounding box is described by the pixel coordinate `(x_min, y_min)` of its lower left corner within the image together with its `width` and `height` in pixels --> (COCO format).","metadata":{}},{"cell_type":"code","source":"ROOT_DIR  = '/kaggle/input'\nWORKING_DIR  = '/kaggle/working'\ndef get_path(row):\n    row['image_path'] = f'{ROOT_DIR}/tensorflow-great-barrier-reef/train_images/video_{row.video_id}/{row.video_frame}.jpg'\n    row['label_path'] = f'{WORKING_DIR}/darknet/data/obj/video_{row.video_id}_{row.video_frame}.txt'\n    return row","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:00:56.117364Z","iopub.execute_input":"2021-12-05T17:00:56.121736Z","iopub.status.idle":"2021-12-05T17:00:56.127788Z","shell.execute_reply.started":"2021-12-05T17:00:56.121703Z","shell.execute_reply":"2021-12-05T17:00:56.126136Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(f'{ROOT_DIR}/tensorflow-great-barrier-reef/train.csv')\ndf = df.apply(get_path, axis=1)\ndf['annotations'] = df['annotations'].apply(lambda x: ast.literal_eval(x))\ndisplay(df.head(2))","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:00:56.130469Z","iopub.execute_input":"2021-12-05T17:00:56.131476Z","iopub.status.idle":"2021-12-05T17:01:30.813025Z","shell.execute_reply.started":"2021-12-05T17:00:56.131332Z","shell.execute_reply":"2021-12-05T17:01:30.812101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['num_bbox'] = df['annotations'].apply(lambda x: len(x))\ndata = (df.num_bbox>0).value_counts()/len(df)*100\nprint('% images without annotations: {}'.format(data[0]))\nprint('% images with annotations: {} '.format(data[1]))","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:01:30.814562Z","iopub.execute_input":"2021-12-05T17:01:30.815505Z","iopub.status.idle":"2021-12-05T17:01:30.843066Z","shell.execute_reply.started":"2021-12-05T17:01:30.815461Z","shell.execute_reply":"2021-12-05T17:01:30.842142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Remove data without boxes\ndf = df.query(\"num_bbox>0\")","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:01:30.844453Z","iopub.execute_input":"2021-12-05T17:01:30.845319Z","iopub.status.idle":"2021-12-05T17:01:30.872255Z","shell.execute_reply.started":"2021-12-05T17:01:30.845276Z","shell.execute_reply":"2021-12-05T17:01:30.871106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# COCO <--> YOLO conversion\ndef coco2yolo(image_height, image_width, bboxes):\n    \"\"\"\n    coco => [xmin, ymin, w, h]\n    yolo => [xmid, ymid, w, h] (normalized)\n    \"\"\"\n    bboxes = bboxes.copy().astype(float) # otherwise all value will be 0 as voc_pascal dtype is np.int\n    \n    # normalizinig\n    bboxes[..., [0, 2]]= bboxes[..., [0, 2]]/ image_width\n    bboxes[..., [1, 3]]= bboxes[..., [1, 3]]/ image_height\n    \n    # converstion (xmin, ymin) => (xmid, ymid)\n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] + bboxes[..., [2, 3]]/2\n    \n    return bboxes\n\ndef yolo2coco(image_height, image_width, bboxes):\n    \"\"\"\n    yolo => [xmid, ymid, w, h] (normalized)\n    coco => [xmin, ymin, w, h]\n    \n    \"\"\" \n    bboxes = bboxes.copy().astype(float) # otherwise all value will be 0 as voc_pascal dtype is np.int\n    \n    # denormalizing\n    bboxes[..., [0, 2]]= bboxes[..., [0, 2]]* image_width\n    bboxes[..., [1, 3]]= bboxes[..., [1, 3]]* image_height\n    \n    # converstion (xmid, ymid) => (xmin, ymin) \n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] - bboxes[..., [2, 3]]/2\n    \n    return bboxes\n\ndef load_image(image_path):\n    return cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)\n\n\ndef plot_one_box(x, img, color=None, label=None, line_thickness=None):\n    # Plots one bounding box on image img\n    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness\n    color = color or [random.randint(0, 255) for _ in range(3)]\n    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))\n    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)\n    if label:\n        tf = max(tl - 1, 1)  # font thickness\n        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]\n        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3\n        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled\n        cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)\n\ndef draw_bboxes(img, bboxes, classes, class_ids, colors = None, show_classes = None, bbox_format = 'yolo', class_name = False, line_thickness = 2):  \n     \n    image = img.copy()\n    show_classes = classes if show_classes is None else show_classes\n    colors = (0, 255 ,0) if colors is None else colors\n    \n    if bbox_format == 'yolo':\n        \n        for idx in range(len(bboxes)):  \n            \n            bbox  = bboxes[idx]\n            cls   = classes[idx]\n            cls_id = class_ids[idx]\n            color = colors[cls_id] if type(colors) is list else colors\n            \n            if cls in show_classes:\n            \n                x1 = round(float(bbox[0])*image.shape[1])\n                y1 = round(float(bbox[1])*image.shape[0])\n                w  = round(float(bbox[2])*image.shape[1]/2) #w/2 \n                h  = round(float(bbox[3])*image.shape[0]/2)\n\n                voc_bbox = (x1-w, y1-h, x1+w, y1+h)\n                plot_one_box(voc_bbox, \n                             image,\n                             color = color,\n                             label = cls if class_name else str(get_label(cls)),\n                             line_thickness = line_thickness)\n    else:\n        raise ValueError('wrong bbox format')\n\n    return image\n\ndef get_bbox(annots):\n    bboxes = [list(annot.values()) for annot in annots]\n    return bboxes\n\ndef get_imgsize(row):\n    row['width'], row['height'] = imagesize.get(row['image_path'])\n    return row","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:01:30.874002Z","iopub.execute_input":"2021-12-05T17:01:30.874586Z","iopub.status.idle":"2021-12-05T17:01:30.899853Z","shell.execute_reply.started":"2021-12-05T17:01:30.874542Z","shell.execute_reply":"2021-12-05T17:01:30.898742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['bboxes'] = df.annotations.apply(get_bbox)\ndf = df.apply(get_imgsize,axis=1)\ndisplay(df.width.unique(), df.height.unique())\ndisplay(df.head(2))","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:01:30.904838Z","iopub.execute_input":"2021-12-05T17:01:30.905540Z","iopub.status.idle":"2021-12-05T17:03:11.187713Z","shell.execute_reply.started":"2021-12-05T17:01:30.905490Z","shell.execute_reply":"2021-12-05T17:03:11.186639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Directory Structure","metadata":{}},{"cell_type":"markdown","source":"We need to have the following dir structure according to [YOLOv4 tutorial](https://colab.research.google.com/drive/1_GdoqCJWXsChrOiY8sZMr_zbr_fH-0Fg#scrollTo=POozxsvFdXTu)\n```\n/Kaggle/working/darknet\n    /data\n         /obj\n             /video_X_XXX.jpg\n             /video_X_XXX.txt\n         /test\n             /video_X_XXX.jpg\n             /video_X_XXX.txt\n         /train.txt\n         /test.txt\n        /obj.data\n        /obj.names\n    /cfg\n        /yolov4-custom.cfg\n```\n- `video_X_XXX.txt`: contains the YOLO normalized annotations (one per line)\n- `train(test).txt`: contains the filenames of the images `data/obj(test)/video_X_XXX.jpg`\n- `yolov4-custom.cfg`: YOLO config provided by darknet. We updated some values accordingly for this challenge.","metadata":{}},{"cell_type":"code","source":"%cd data/\n!mkdir obj test\n\ncnt = 0\nfor row_idx in tqdm(range(df.shape[0])):\n    row = df.iloc[row_idx]\n    image_height = row.height\n    image_width = row.width\n    bboxes_coco = np.asarray(row.bboxes).astype(np.float32).copy()\n    num_bbox = len(bboxes_coco)\n    labels = [0]*num_bbox\n  \n    f = open(row.label_path, 'w')\n\n    if num_bbox < 1:\n        annot = ''\n        f.write(annot)\n        f.close()\n        cnt += 1\n        continue\n  \n    bboxes_yolo  = coco2yolo(image_height, image_width, bboxes_coco)\n\n    for i in range(len(bboxes_yolo)):\n        annot = [str(labels[i])] + list(bboxes_yolo[i].astype(str)) + (['\\n'] if num_bbox!=(i+1) else [''])\n        annot = ' '.join(annot)\n        annot = annot.strip(' ')\n        f.write(annot)\n    f.close()\n\nprint('Missing boxes ', cnt)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:11.189710Z","iopub.execute_input":"2021-12-05T17:03:11.190346Z","iopub.status.idle":"2021-12-05T17:03:14.659264Z","shell.execute_reply.started":"2021-12-05T17:03:11.190297Z","shell.execute_reply":"2021-12-05T17:03:14.658004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cat obj/video_0_1000.txt","metadata":{"execution":{"iopub.status.busy":"2021-12-04T13:49:14.508592Z","iopub.execute_input":"2021-12-04T13:49:14.509071Z","iopub.status.idle":"2021-12-04T13:49:15.172731Z","shell.execute_reply.started":"2021-12-04T13:49:14.509025Z","shell.execute_reply":"2021-12-04T13:49:15.171867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Split dataset","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import GroupKFold\nkf = GroupKFold(n_splits = 5) \ndf = df.reset_index(drop=True)\ndf['fold'] = -1\nfor fold, (train_idx, val_idx) in enumerate(kf.split(df, y = df.video_id.tolist(), groups=df.sequence)):\n    df.loc[val_idx, 'fold'] = fold\ndisplay(df.fold.value_counts())","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:16:41.962000Z","iopub.execute_input":"2021-12-05T17:16:41.962531Z","iopub.status.idle":"2021-12-05T17:16:41.990734Z","shell.execute_reply.started":"2021-12-05T17:16:41.962481Z","shell.execute_reply":"2021-12-05T17:16:41.989834Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_df = df[df['fold']==2]\ntrain_df = df[df['fold']!=2]\nprint(train_df.shape)\nprint(val_df.shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:32:41.247071Z","iopub.execute_input":"2021-12-05T17:32:41.248022Z","iopub.status.idle":"2021-12-05T17:32:41.260234Z","shell.execute_reply.started":"2021-12-05T17:32:41.247988Z","shell.execute_reply":"2021-12-05T17:32:41.258853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # Move labels from obj/ to test/ directory\ndef mv_labels (row):\n    old_path = row.label_path\n    filename = row.label_path.split('/')[-1]\n    new_path = '/'.join(row.label_path.split('/')[:-2]) + '/test/' + filename\n    row['label_path'] = new_path\n    shutil.move(old_path, new_path)\n    return row\n\nval_df = val_df.apply(lambda x: mv_labels(x), axis=1)\nval_df.head(2)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:15.477118Z","iopub.execute_input":"2021-12-05T17:03:15.477798Z","iopub.status.idle":"2021-12-05T17:03:15.678143Z","shell.execute_reply.started":"2021-12-05T17:03:15.477737Z","shell.execute_reply":"2021-12-05T17:03:15.677146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_df.head(2)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:17:33.166505Z","iopub.execute_input":"2021-12-05T17:17:33.166795Z","iopub.status.idle":"2021-12-05T17:17:33.186748Z","shell.execute_reply.started":"2021-12-05T17:17:33.166767Z","shell.execute_reply":"2021-12-05T17:17:33.185539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Copy images to working dir\n'''\nLabels and images must have the same name:\nImages: obj/image_XX.jpg\nLabels: obj/image_XX.txt\n'''\ndef copy_images (row):\n    old_path = row.image_path\n    filename = row.label_path.split('/')[-1][:-4] + '.jpg'\n    new_path = '/'.join(row.label_path.split('/')[:-1]) + '/' + filename\n    shutil.copy(old_path, new_path)\nval_df.apply(lambda x: copy_images(x), axis=1)\ntrain_df.apply(lambda x: copy_images(x), axis=1)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:18:28.826320Z","iopub.execute_input":"2021-12-05T17:18:28.826977Z","iopub.status.idle":"2021-12-05T17:18:52.413280Z","shell.execute_reply.started":"2021-12-05T17:18:28.826945Z","shell.execute_reply":"2021-12-05T17:18:52.412207Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls obj/*.jpg | wc -l\n!ls obj/*.txt | wc -l\n!ls test/*.jpg | wc -l\n!ls test/*.txt | wc -l","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:28.314356Z","iopub.execute_input":"2021-12-05T17:03:28.317393Z","iopub.status.idle":"2021-12-05T17:03:31.552502Z","shell.execute_reply.started":"2021-12-05T17:03:28.317345Z","shell.execute_reply":"2021-12-05T17:03:31.551396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generate train.txt and test.txt\n%cd ../\ntrain_images = glob.glob('data/obj/*.jpg')\nf = open('./data/train.txt', 'w')\nannot = [os.path.join(os.getcwd(),t) + ('\\n' if i<len(train_images)-1 else '') for i, t in enumerate(train_images)]\nannot = ''.join(annot)\nannot = annot.strip()\nf.write(annot)\n\nval_images = glob.glob('data/test/*.jpg')\nf = open('./data/test.txt', 'w')  \nannot = [os.path.join(os.getcwd(),t) + ('\\n' if i<len(val_images)-1 else '') for i, t in enumerate(val_images)]\nannot = ''.join(annot)\nannot = annot.strip()\nf.write(annot)","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:31.555968Z","iopub.execute_input":"2021-12-05T17:03:31.556346Z","iopub.status.idle":"2021-12-05T17:03:31.623331Z","shell.execute_reply.started":"2021-12-05T17:03:31.556294Z","shell.execute_reply":"2021-12-05T17:03:31.622129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cat data/train.txt | wc -l\n!cat data/test.txt | wc -l","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:31.625308Z","iopub.execute_input":"2021-12-05T17:03:31.625670Z","iopub.status.idle":"2021-12-05T17:03:33.153856Z","shell.execute_reply.started":"2021-12-05T17:03:31.625628Z","shell.execute_reply":"2021-12-05T17:03:33.152423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualization","metadata":{}},{"cell_type":"code","source":"\nnp.random.seed(32)\ncolors = [(np.random.randint(255), np.random.randint(255), np.random.randint(255))\\\n          for idx in range(1)]\n\ndf2 = train_df[(train_df.num_bbox>0)].sample(100) # takes samples with bbox\n\nfor idx in range(10):\n    row = df2.iloc[idx]\n    img           = load_image(row.image_path)\n    image_height  = row.height\n    image_width   = row.width\n    f = open(row.label_path)\n    bboxes_yolo = np.asarray([[float(a) for a in l[1:].strip().split(' ')] for l in f.readlines()])\n\n    names         = ['starfish']*len(bboxes_yolo)\n    labels        = [0]*len(bboxes_yolo)\n\n    plt.figure(figsize = (12, 8))\n    plt.imshow(draw_bboxes(img = img,\n                           bboxes = bboxes_yolo, \n                           classes = names,\n                           class_ids = labels,\n                           class_name = True, \n                           colors = colors, \n                           bbox_format = 'yolo',\n                           line_thickness = 2))\n    plt.axis('OFF')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-04T13:58:02.753992Z","iopub.execute_input":"2021-12-04T13:58:02.754271Z","iopub.status.idle":"2021-12-04T13:58:06.935786Z","shell.execute_reply.started":"2021-12-04T13:58:02.754238Z","shell.execute_reply":"2021-12-04T13:58:06.935143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config files update & creation","metadata":{}},{"cell_type":"markdown","source":"We modify `yolov4-custom.cfg` provided by darknet to adapt it to one-class classification","metadata":{}},{"cell_type":"code","source":"# Adapt yolov4-custom.cfg to one-class model\n# If subdivisions=16 runs into memory issues use 32, otherwise 16 is the optimal\n!sed -i 's/subdivisions=16/subdivisions=32/g' ./cfg/yolov4-custom.cfg\n# To avoid memory issues with downsized image size from 608 to 416. \n!sed -i 's/width=608/width=416/g' ./cfg/yolov4-custom.cfg\n!sed -i 's/height=608/height=416/g' ./cfg/yolov4-custom.cfg\n\n# Make the rest of the changes to the cfg based on how many classes you are training your detector on.\n'''\nheight = 416 (these can be any multiple of 32, 416 is standard, you can sometimes \nimprove results by making value larger like 608 but will slow down training)\n\nmax_batches = (# of classes) * 2000 (but no less than 6000 so if you are training \nfor 1, 2, or 3 classes it will be 6000, however detector for 5 classes would have max_batches=10000)\n\nsteps = (80% of max_batches), (90% of max_batches) \n(so if your max_batches = 10000, then steps = 8000, 9000)\n\nfilters = (# of classes + 5) * 3 (so if you are training for one class then your \nfilters = 18, but if you are training for 4 classes then your filters = 27)\n'''\n!sed -i 's/max_batches = 500500/max_batches = 6000/g' ./cfg/yolov4-custom.cfg\n!sed -i 's/steps=400000,450000/steps=4800,5400/g' ./cfg/yolov4-custom.cfg\n!sed -i 's/classes=80/classes=1/g' ./cfg/yolov4-custom.cfg\n!sed -i 's/filters=255/filters=18/g' ./cfg/yolov4-custom.cfg\n\n# Let's build obj.data and obj.names needed by darknet\nf = open('./data/obj.data', 'w')\nf.write('classes = 1\\ntrain = data/train.txt\\nvalid = data/test.txt\\nnames = data/obj.names\\nbackup = backup\\n')\nf.close()\nf = open('./data/obj.names', 'w')\nf.write('starfish')\nf.close()","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:33.156052Z","iopub.execute_input":"2021-12-05T17:03:33.156752Z","iopub.status.idle":"2021-12-05T17:03:44.737266Z","shell.execute_reply.started":"2021-12-05T17:03:33.156702Z","shell.execute_reply":"2021-12-05T17:03:44.735975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🚀 Train a pre-trained model","metadata":{}},{"cell_type":"code","source":"# Download a pre-trained model\n!wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137","metadata":{"execution":{"iopub.status.busy":"2021-12-04T13:10:00.617681Z","iopub.execute_input":"2021-12-04T13:10:00.61808Z","iopub.status.idle":"2021-12-04T13:10:17.811488Z","shell.execute_reply.started":"2021-12-04T13:10:00.618041Z","shell.execute_reply":"2021-12-04T13:10:17.810679Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Start training from pre-trained model\n!./darknet detector train data/obj.data cfg/yolov4-custom.cfg yolov4.conv.137 -dont_show -map","metadata":{"execution":{"iopub.status.busy":"2021-12-04T13:58:25.043137Z","iopub.execute_input":"2021-12-04T13:58:25.043395Z","iopub.status.idle":"2021-12-04T13:58:52.983281Z","shell.execute_reply.started":"2021-12-04T13:58:25.043365Z","shell.execute_reply":"2021-12-04T13:58:52.982515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🔮 Inference\nA custom trained model is provided as an example [here](https://www.kaggle.com/gimarcecaml/yolov4-cots) to test some predictions","metadata":{}},{"cell_type":"code","source":"# need to set our custom cfg to test mode \n%cd cfg\n!sed -i 's/batch=64/batch=1/' yolov4-custom.cfg\n!sed -i 's/subdivisions=16/subdivisions=1/' yolov4-custom.cfg\n%cd ..","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:03:58.489287Z","iopub.execute_input":"2021-12-05T17:03:58.489605Z","iopub.status.idle":"2021-12-05T17:03:59.946810Z","shell.execute_reply.started":"2021-12-05T17:03:58.489574Z","shell.execute_reply":"2021-12-05T17:03:59.945643Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image = './data/obj/video_1_3921.jpg'\n\n!./darknet detector test data/obj.data cfg/yolov4-custom.cfg {ROOT_DIR}/yolov4-cots/yolov4-custom_last.weights {test_image} -thresh 0.1","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🔎 Predictions vs ground-truth","metadata":{}},{"cell_type":"code","source":"from PIL import Image\n\nnp.random.seed(32)\ncolors = [(np.random.randint(255), np.random.randint(255), np.random.randint(255))\\\n          for idx in range(1)]\n\nIMAGEPATH = test_image\n\ndef change_path(row):\n    filename = row.image_path.split('/')[-1]\n    videoname = row.image_path.split('/')[-2]\n    return os.path.join('./data/obj', videoname + '_' + filename)\n\ntmp_df = train_df.copy()\ntmp_df['image_path'] = tmp_df.apply(lambda x: change_path(x), 1)\n\ndf2 = tmp_df[(tmp_df.image_path==IMAGEPATH)]\nrow = df2.iloc[0]\nimg           = load_image(row.image_path)\nimage_height  = row.height\nimage_width   = row.width\nf = open(row.image_path[:-4] + '.txt')\nbboxes_yolo = np.asarray([[float(a) for a in l[1:].strip().split(' ')] for l in f.readlines()])\n\nnames         = ['starfish']*len(bboxes_yolo)\nlabels        = [0]*len(bboxes_yolo)\n\n\n# 2. Plot in same line, on two rows\nplt.figure(figsize = (19, 8))\nplt.subplot(1, 2, 1)\n\nplt.imshow(draw_bboxes(img = img,\n                      bboxes = bboxes_yolo, \n                      classes = names,\n                      class_ids = labels,\n                      class_name = True, \n                      bbox_format = 'yolo',\n                       colors = colors,\n                      line_thickness = 2))\n\nplt.axis('OFF')\nplt.title('Ground truth test set')\n\nplt.subplot(1, 2, 2)\n#plt.figure(figsize = (12, 8))\nplt.axis('OFF')\nplt.title('Prediction test set')\nimg = Image.open('predictions.jpg')\nplt.imshow(img)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-05T17:33:09.551280Z","iopub.execute_input":"2021-12-05T17:33:09.551612Z","iopub.status.idle":"2021-12-05T17:33:10.250165Z","shell.execute_reply.started":"2021-12-05T17:33:09.551578Z","shell.execute_reply":"2021-12-05T17:33:10.249101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}