<jupyter_start><jupyter_text>Download yolo<jupyter_code>!git clone https://github.com/ultralytics/yolov5  # clone repo
%cd yolov5
!git pull
%pip install -qr requirements.txt  # install dependencies
# %pip install opencv-python

import torch
from IPython.display import Image, clear_output  # to display images
import os

clear_output()
print(f"Working directory: {os.getcwd()}")
print(f"Setup complete. Using torch {torch.__version__} ({torch.cuda.get_device_properties(0).name if torch.cuda.is_available() else 'CPU'})")<jupyter_output>Working directory: /home/joshdw/Documents/src/FlockSentry/training/yolov5
Setup complete. Using torch 1.10.0+cu102 (NVIDIA GeForce GTX 1650 Ti with Max-Q Design)<jupyter_text>Download our training data<jupyter_code>%cd yolov5
!pwd
input("Download training data from drive and unpack into `all_image_data`.  Press ENTER when done.")
CUSTOM_DATA_PATH = '../all_image_data/'
print(f"all_image_data file count: {len(os.listdir(CUSTOM_DATA_PATH))}")
COCO_DATA_PATH = "../datasets/coco/"
COCO_LABELS_PATH = COCO_DATA_PATH + "labels/val2017/"
COCO_IMAGES_PATH = COCO_DATA_PATH + "images/val2017/"
YOLO_DATA_PATH = "./data/"<jupyter_output>/home/joshdw/Documents/src/FlockSentry/training/yolov5
/home/joshdw/Documents/src/FlockSentry/training/yolov5
all_image_data file count: 619<jupyter_text>Download coco data<jupyter_code>download_coco = True
if os.path.exists(COCO_DATA_PATH):
    response = input(f"Replace coco dataset? [y/N]")
    download_coco = response.lower() in ["y", "yes"]

if download_coco:
    torch.hub.download_url_to_file('https://ultralytics.com/assets/coco2017val.zip', 'tmp.zip')
!unzip -oq tmp.zip -d ../datasets
#person, bird, cat, dog
keep = ["0", "14", "15", "16"]
replace_dict = {"0": "3", "14": "2", "15": "4", "16": "5",}

for filename in os.listdir(COCO_LABELS_PATH):
  #lines we want to keep
  useful_lines = []
  rIdx = 0
  wIdx = 0 
  #read all the lines in the file
  with open(COCO_LABELS_PATH + filename, "r") as f:
    lines = f.readlines()
    for line in lines:
      items = line.split(" ") #items[0] = class
      if(items[0] in keep):
        useful_lines.append(rIdx) #note that line (all other lines will be removed)
      rIdx = rIdx + 1
  #copy only the lines we want to keep back to the file 
  with open(COCO_LABELS_PATH + filename, "w") as f:
    for line in lines:
        if wIdx in useful_lines:
            items = line.split(" ") #items[0] = class
            items[0] = replace_dict[items[0]]
            new_line = " ".join(items)
            f.write(new_line)
        wIdx = wIdx + 1
#remove empty text files and the images that are associated with them
for filename in os.listdir(COCO_LABELS_PATH):
    label_path = COCO_LABELS_PATH + filename
    if(os.path.getsize(label_path) == 0):
      os.remove(label_path)
      image_path = COCO_IMAGES_PATH + filename.replace(".txt", ".jpg")
      os.remove(image_path)<jupyter_output><empty_output><jupyter_text>Remove duplicate images from our training dataIt is possible that two team members downloaded and annotated the same image from the internet.  This should clean that up.We don't need to run it most of the time, so we skip it.<jupyter_code># import cv2
# import os
# import numpy as np
# from matplotlib import pyplot as plt


# duplicate_images = []

# # Gather filenames of all images
# img_fnames = [fname for fname in os.listdir(CUSTOM_DATA_PATH)
    # if fname.endswith(".jpg") or fname.endswith(".png") or fname.endswith(".jpeg")]

# chicken_images = []
# i = 0
# for img_fname in img_fnames:  # Open, normalize, and append all images
    # img = cv2.resize(cv2.imread(CUSTOM_DATA_PATH + img_fname), (640, 480))
    # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # chicken_images.append(img)
# Find images that look like the exact same image and nominate them for deletion
# for i, img in enumerate(chicken_images):
    # for j, other_img in enumerate(chicken_images[i+1:]):
        # # Test similarity with root means squared
        # diff = img - other_img
        # distance = np.sqrt(np.sum(np.square(diff)))
        # if distance < 100:
            # # Display both images
            # plt.imshow(img)
            # plt.show()
            # plt.imshow(other_img)
            # plt.show()
            # other_fname = img_fnames[i+1+j]
            # # Ask if the second image should be deleted ...
            # print(f"Duplicate images: {img_fnames[i]} and {other_fname}")
            # should_delete = input(f"Delete {other_fname}? [Y/n]")
            # # ... and delete the second image if yes
            # if should_delete in ["y", "Y", ""]:
                # print(f"Deleting {other_fname} and matching label file...")
                # os.remove(CUSTOM_DATA_PATH + "".join(other_fname.split(".")[:-1]) + ".txt")
                # os.remove(CUSTOM_DATA_PATH + other_fname)
# del chicken_images<jupyter_output><empty_output><jupyter_text>Allocate a certain amount for each category to not flood the chicken images<jupyter_code>"""
Moves a random sample of one tenth of images and labels to the validation data
folder, and the rest to the training data folder.
"""

import shutil
from glob import glob
import os
import random

# Tuneable parameters for data amounts
VALIDATION_PROPORTION = 0.2
COCO_PROPORTION = 0.9
MIN_COCO_DOG_IMAGES = 200
MIN_COCO_BIRD_IMAGES = 200
MIN_COCO_CAT_IMAGES = 200
MAX_COCO_PERSON_IMAGES = 350

# Find all of the custom data label files
custom_label_fnames = glob(CUSTOM_DATA_PATH + "*.txt")
random.shuffle(custom_label_fnames)

# Find all of the coco data label files
coco_fnames = glob(COCO_DATA_PATH + "labels/val2017/*.txt")
random.shuffle(coco_fnames)
eventual_num_coco_images = int(len(custom_label_fnames)*COCO_PROPORTION/(1-COCO_PROPORTION))

# Clear the yolov5/data/images and yolov5/data/labels folders
try:
    shutil.rmtree(YOLO_DATA_PATH + "images/")
    shutil.rmtree(YOLO_DATA_PATH + "labels/")
except FileNotFoundError as e:  # We have already deleted the folders
    print(f"Not deleting nonexistent images/ and labels/ folders in {YOLO_DATA_PATH}.")
os.mkdir(YOLO_DATA_PATH + "images/")
os.mkdir(YOLO_DATA_PATH + "labels/")
os.mkdir(YOLO_DATA_PATH + "images/val")
os.mkdir(YOLO_DATA_PATH + "images/train")
os.mkdir(YOLO_DATA_PATH + "labels/val")
os.mkdir(YOLO_DATA_PATH + "labels/train")

def get_matching_img_fname(label_fname):
    """Takes a .txt label filename and returns its matching image filename"""
    label_fname = label_fname.replace('coco/labels/val2017/', 'coco/images/val2017/')
    img_fname = ""
    label_fname_root = label_fname.replace(".txt", "")
    if os.path.exists(f"{label_fname_root}.jpg"):
        img_fname = f"{label_fname_root}.jpg"
    elif os.path.exists(f"{label_fname_root}.jpeg"):
        img_fname = f"{label_fname_root}.jpeg"
    elif os.path.exists(f"{label_fname_root}.png"):
        img_fname = f"{label_fname_root}.png"
    else:
        print(f"No matching .jpg or .png file for {label_fname}.  Continuing...")
        return None
    return img_fname

# TODO: resume commenting here
for i, label_fname in enumerate(custom_label_fnames):
    img_fname = get_matching_img_fname(label_fname)
    if not img_fname:
        continue

    if i % int(1 / VALIDATION_PROPORTION) == 0:
        shutil.copy2(img_fname, YOLO_DATA_PATH + "images/val/")
        shutil.copy2(label_fname, YOLO_DATA_PATH + "labels/val/")
    else:
        shutil.copy2(img_fname, YOLO_DATA_PATH + "images/train/")
        shutil.copy2(label_fname, YOLO_DATA_PATH + "labels/train/")

people = 0
birds = 0
cats = 0
dogs = 0
train_images = 0
val_images = 0
i = 0
for label_fname in coco_fnames:
    img_fname = get_matching_img_fname(label_fname)
    if not img_fname:
        continue
    
    person = False
    bird = False
    cat = False
    dog = False
    with open(label_fname, "r") as f:
        for line in f.readlines():
            if int(line.split()[0]) == 3:
                person = True
            elif  int(line.split()[0]) == 2:
                bird = True
            elif  int(line.split()[0]) == 4:
                cat = True
            elif  int(line.split()[0]) == 5:
                dog = True
    
    if train_images + val_images > eventual_num_coco_images:
        break

    if (
        (cat and cats < MIN_COCO_CAT_IMAGES) or \
        (bird and birds < MIN_COCO_BIRD_IMAGES) or \
        (dog and dogs < MIN_COCO_DOG_IMAGES) or \
        (person and people < MAX_COCO_PERSON_IMAGES) or \
        not person
    ):
        if i % int(1 / VALIDATION_PROPORTION) == 0:
            shutil.copy2(img_fname, YOLO_DATA_PATH + "images/val/")
            shutil.copy2(label_fname, YOLO_DATA_PATH + "labels/val/")
            val_images += 1
        else:
            shutil.copy2(img_fname, YOLO_DATA_PATH + "images/train/")
            shutil.copy2(label_fname, YOLO_DATA_PATH + "labels/train/")
            train_images += 1
        if person:
            people += 1
        if cat:
            cats += 1
        if bird:
            birds += 1
        if dog:
            dogs += 1
        i += 1

if train_images < eventual_num_coco_images * (1 - VALIDATION_PROPORTION):
    print(f"not enough training images: needed {eventual_num_coco_images * (1 - VALIDATION_PROPORTION)}, got {train_images}")
if val_images < eventual_num_coco_images * VALIDATION_PROPORTION:
    print(f"not enough validation images: needed {eventual_num_coco_images * VALIDATION_PROPORTION}, got {val_images}")<jupyter_output>No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_6780.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_3220.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_1434.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_6568.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_4243.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_1976.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_2596.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_5220.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/labels.txt.  Continuing...
No matching .jpg or .png file for ../all_image_data/37GNSw_i3y8_5920.txt.  Continuing...
not enough training images: needed 2260.8, got 642
not enough validation images: needed 565.2, got 161<jupyter_text>Train the model with our custom dataset<jupyter_code>!python3 train.py --img 640 --batch 24 --epochs 1000 --data ../custom.yaml --cfg models/yolov5n.yaml --weights yolov5n.pt --cache --patience 250<jupyter_output>[34m[1mtrain: [0mweights=yolov5n.pt, cfg=models/yolov5n.yaml, data=../custom.yaml, hyp=data/hyps/hyp.scratch-low.yaml, epochs=1000, batch_size=24, imgsz=640, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, evolve=None, bucket=, cache=ram, image_weights=False, device=, multi_scale=False, single_cls=False, optimizer=SGD, sync_bn=False, workers=8, project=runs/train, name=exp, exist_ok=False, quad=False, cos_lr=False, label_smoothing=0.0, patience=250, freeze=[0], save_period=-1, local_rank=-1, entity=None, upload_dataset=False, bbox_interval=-1, artifact_alias=latest
[34m[1mgithub: [0mup to date with https://github.com/ultralytics/yolov5 ✅
YOLOv5 🚀 v6.1-135-g7926afc torch 1.10.0+cu102 CUDA:0 (NVIDIA GeForce GTX 1650 Ti with Max-Q Design, 3912MiB)

[34m[1mhyperparameters: [0mlr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=0.05, cls=0.5, cls_pw=1.0, obj=1.0, obj_pw=1.0, iou_t=0.2, anc[...]<jupyter_text>Run model on something probably a video with chickens**NOTE:** You will need to modify `exp` to the latest exp folder generated from the training command above.<jupyter_code>!python3 detect.py --weights runs/train/exp28/weights/best.pt --img 640 --conf 0.1 --source ../test.mp4<jupyter_output>[34m[1mdetect: [0mweights=['runs/train/exp28/weights/best.pt'], source=../test.mp4, data=data/coco128.yaml, imgsz=[640, 640], conf_thres=0.1, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs/detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False
YOLOv5 🚀 v6.1-135-g7926afc torch 1.10.0+cu102 CUDA:0 (NVIDIA GeForce GTX 1650 Ti with Max-Q Design, 3912MiB)

Fusing layers... 
YOLOv5n summary: 213 layers, 1765930 parameters, 0 gradients, 4.2 GFLOPs
video 1/1 (1/5344) /home/joshdw/Documents/src/FlockSentry/training/test.mp4: 384x640 1 person, Done. (0.005s)
video 1/1 (2/5344) /home/joshdw/Documents/src/FlockSentry/training/test.mp4: 384x640 1 person, Done. (0.005s)
video 1/1 (3/5344) /home/joshdw/Documents/src/FlockSentry/training/test.mp4: 384x640 1 person, Done. (0.005s)
vi[...]
{'fraction_non_alphanumeric': 0.12257169287696577, 'fraction_numerical': 0.03669441874807277, 'mean_word_length': 3.6233071988595866, 'pattern_counts': {'":': 4, '<': 31, '<?xml version=': 0, '>': 25, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '465567', 'license': 'MIT', 'n_tokens_mistral': 5012, 'n_tokens_neox': 4716, 'n_words': 1123, 'path': 'training/flocksentry.ipynb', 'repo_name': 'EdwinEstep/FlockSentry', 'size': '12972'}