Skip to content
Snippets Groups Projects
Commit dea6582b authored by Stephane Chavin's avatar Stephane Chavin
Browse files

complete readme

parent 64fa6b0b
No related branches found
No related tags found
No related merge requests found
# YOLO-DYNI # YOLO-DYNI
Ce git a été créé avec comme objectif une prise en main de YOLOV5 plus facile. Ce git a été créé avec comme objectif une prise en main de YOLOV5 plus facile.
Il contient notamment un script permettant d'extraire les spectrogrammes de plusieurs enregistrements ([get_spectrogram.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_spectrogram.py)), un script nécessaire à la conversion des annotations LabelMe vers YOLO ([labelme2yolo.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/labelme2yolo.py)), un script pour convertir des annotations d'un dataframe vers YOLO ([get_train_annot_YOLO.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_train_annot_YOLO.py/)), un script permettant de séparer le train et la validation de manière équilibré ([get_train_val_YOLO.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_train_val_YOLO.py)) et un script qui permet de compiler les détections, d'un modèle entrainé, dans un dataframe ([get_yolo_detection.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_yolo_detection.py)). Il contient notamment un script permettant d'extraire les spectrogrammes de plusieurs enregistrements ([get_spectrogram.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_spectrogram.py)), un script nécessaire à la conversion des annotations LabelMe vers YOLO ([labelme2yolo.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/labelme2yolo.py)), un script pour convertir des annotations d'un dataframe vers YOLO ([get_train_annot_YOLO.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_train_annot_YOLO.py/)), un script permettant de séparer le train et la validation de manière équilibré ([get_train_val_YOLO.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_train_val_YOLO.py)), un script qui permet de compiler les détections, d'un modèle entrainé, dans un dataframe ([get_yolo_detection.py](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/get_yolo_detection.py)) et divers autres scripts.
## Install ## Install
...@@ -13,72 +13,82 @@ pip install -r requirements.txt ...@@ -13,72 +13,82 @@ pip install -r requirements.txt
``` ```
## Annoter des images : ## Annoter des images :
<details>
<summary>Extraction des spectrogrammes</summary>
<p>
</p>
```bash ```bash
python3 get_spectrogram.py -f NAME_OF_YOUR_FILE.csv -p PATH_TO_THE_DATA -d DIRECTION_OF_SAVED_DATA -m {unique or multiple} -n NAME_OF_THE_COLUMNS_THAT_CONTAIN_PATH -i {file or folder} python get_spectrogram.py path_to_data directory [-h] [-m {unique,multiple}] [-n COLUMNS_NAME] [-i {file,folder}] [-f FILE]
[--frames FRAMES] [--duration DURATION] [--overlap OVERLAP] [--sr SR] [--window WINDOW] [--hop HOP]
[--cpu CPU]
``` ```
* Le mode unique est utilisé lorsque les enregistrements font quelques secondes et que l'on veut qu'un seul spectrogramme par enregistrement *(mode __unique__, DURATION = durée de l'enregistrement et NB_IMG_PER_REC = 1)*
* Le mode multiple permet de découper l'enregistrements en plusieurs spectrogrammes de quelques secondes
* -i file est à utiliser lorsque les noms de fichiers à traiter sont dans un .csv, tandis que -i folder est à utiliser pour traiter tous les .wav d'un dossier
**WARNING** : Il est important de modifier la valeur de DURATION, OFFSET et NB_IMG_PER_REC dans le code (ligne 35, 36 et 39), la fréquence d'échantillonage des spectrogrammes est celle du fichier .wav -h, --help: Show this help message and exit.
-m, --mode {unique,multiple}: If unique, only 1 image per file (default: multiple).
-n, --columns_name COLUMNS_NAME: Name of the columns that contain the path of the .wav (default: 'Path').
-i, --input {file,folder}: Choose "file" if you have a .csv file or "folder" to export spectrogram from all the .wav of a folder (default: folder).
-f, --file FILE: Name of the file that contains the recording to print (default: None).
--frames FRAMES: Number of spectrogram per file (default: 30).
--duration DURATION: Duration for each spectrogram (default: 8).
--overlap OVERLAP: Overlap between 2 spectrograms (default: 2).
--sr SR: Sampling rate for the spectrogram. If no argument, SR will be original SR of the recording (default: None).
--window WINDOW: Window size for the Fourier Transform (default: 1024).
--hop HOP: Hop length for the Fourier Transform (default: 512).
--cpu CPU: To speed up the process, write 2 or more (default: 1).
```python Exemple :
folder = 'Spectrogram/'
DURATION = 5
OFFSET = 2
if args.mode == 'multiple': ```bash
NB_IMG_PER_REC = 10 python spectrogram_extractor.py /path/to/data /output/directory --mode multiple --input folder --frames 20 --duration 5 --overlap 1 --window 2048 --hop 1024 --cpu 2
``` ```
</details>
<details> # LabelMe
<summary>LabelMe</summary>
<p>
</p>
* Installation de LabelMe * Installation de LabelMe
```bash ```bash
pip install labelme pip install labelme
``` ```
* Annotation
```bash
labelme /output/directory
```
![Demo](demo.gif "Demo")
* Conversion des annotations LabelMe vers YOLO * Conversion des annotations LabelMe vers YOLO
```bash ```bash
python3 labelme2yolo.py -p PATH_TO_THE_DATA -d DIRECTION_OF_THE_CONVERTED_FILES/FOLDER python labelme2yolo.py -p path_to_data -d directory
``` ```
-h, --help: Show this help message and exit.
-p, --path_to_data PATH_TO_DATA: Path to LabelMe annotations (required).
-d, --directory DIRECTORY: Directory to which YOLO annotations will be stored (required).
**WARNING** : Les annotations *.json* et les images *.jpg* doivent être dans le même dossier (**PATH_TO_THE_DATA/**) **WARNING** : Les annotations *.json* et les images *.jpg* doivent être dans le même dossier (**path_to_data/**)
* Préparation à la séparation train/val
Il est important de déplacer les images .jpg dans un dossier **images/all/** et que les labels .txt soient dans le dossier **labels/**
```bash ```bash
##go to the .jpg direction## python labelme_to_yolo_converter.py -p /path/to/labelme_annotations -d /output/yolo_annotations
```
mkdir images #create a new folder ### Séparation train/val/test
mkdir images/all/ #create the folder all in the new folder
mv *.jpg images/all/. #move all the images in the all folder Il est important de vérifier que les images .jpg son dans un dossier **images/all/** et que les labels .txt soient dans le dossier **labels/**
mv images ../. #place the folder images in the main direction ```bash
python get_train_val.py -r ratio -p path_to_data -d directory [--test]
``` ```
-h, --help: Show this help message and exit.
-r, --ratio RATIO: Train ratio (val = 1 - ratio) (default: 0.7).
-p, --path_to_data PATH_TO_DATA: Path of the folder that contains the .txt files (ending with labels/) (required).
-d, --directory DIRECTORY: Directory to which spectrogram and .txt files will be stored (different from -p) (required).
--test TEST: 1 if True. Split into train/test/val. 1 - Ratio / 2 for test and same for validation (default: 0).
On se retrouve donc avec deux dossiers, un portant le nom **images**, contenant les .jpg, et un second portant le nom **labels/** contenant les .txt Exemple :
```bash
python get_train_val.py -r 0.8 -p /path/to/data -d /output/directory --test 1
```
<p> <p>
</p> </p>
![Demo](demo.gif "Demo")
</details> </details>
...@@ -96,51 +106,48 @@ On se retrouve donc avec deux dossiers, un portant le nom **images**, contenant ...@@ -96,51 +106,48 @@ On se retrouve donc avec deux dossiers, un portant le nom **images**, contenant
**see :** [example.csv](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/example.csv) **see :** [example.csv](https://gitlab.lis-lab.fr/stephane.chavin/yolo-dyni/-/blob/main/example.csv)
<details>
<summary>Conversion des annotations</summary> <summary>Conversion des annotations</summary>
<p> <p>
</p> </p>
* Conversion des annotations (**temps fréquence**) au format YOLO (**label x y w h**) * Conversion des annotations (**temps fréquence**) au format YOLO (**label x y w h**)
**WARNING** : Ne pas oublier de modifier la valeur de **DURATION** et **NB_CLASS** (ligne 28 et 29)
```bash ```bash
python3 get_train_annot_YOLO.py -f PATH_TO_THE_FILE.csv -p PATH_TO_DATA -d DIRECTION_OF_THE_TXT_AND_IMG -m {uniform or personalized} -u {unique or multiple} -c NAME_OF_THE_COLUMNS_CONTAINS_PATH python get_train_annot.py filename_path -c columns_name path_to_data directory [-h] [-m {uniform,personalized}] [-u {unique,multiple}] [--export EXPORT] [--duration DURATION] [--overlap OVERLAP] [--sr SR] [--window WINDOW] [--hop HOP] [--cpu CPU]
``` ```
* Vérifier le bon placement des bounding box : filename_path: Path and name of the file containing the annotations (required).
-c, --columns_name COLUMNS_NAME: Name of the column that contains the path (required).
path_to_data: Path of the folder that contains the recordings (required).
directory: Directory to which spectrograms and .txt files will be stored (required).
```bash Optional Arguments:
python3 get_train_annot_YOLO.py -f PATH_TO_THE_FILE.csv -p PATH_TO_DATA -d DIRECTION_OF_THE_TXT_AND_IMG -m {uniform or personalized} -u {unique or multiple} -c NAME_OF_THE_COLUMNS_CONTAINS_PATH --export
```
*Ajouter **--export** permet d'exporter les spectrogrammes avec les bounding box placées par dessus* -h, --help: Show this help message and exit.
-m, --mode {uniform,personalized}: Choose the mode to calculate the y and height value (default: personalized).
**WARNING** : Le mode uniforme permet d'extraire des bounding box avec y = 0.5 et h = 0.8; En utilisant -u unique seul le début des enregistrements est traité tandis que multiple permet de traiter plusieurs spectrogrammes; la fonction -c correspond au nom de la colonne qui contient le path des fichiers -u, --unique {unique,multiple}: 'unique' for only one spectrogram per file, 'multiple' for multiple spectrograms (default: multiple).
--export EXPORT: To export the position of the bounding box on the spectrogram (default: None).
--duration DURATION: Duration for each spectrogram (default: 8).
--overlap OVERLAP: Overlap between 2 spectrograms (default: 2).
--sr SR: Sampling rate for the spectrogram. If no argument, SR will be original SR of the recording (default: None).
--window WINDOW: Window size for the Fourier Transform (default: 1024).
--hop HOP: Hop length for the Fourier Transform (default: 512).
--cpu CPU: To speed up the process, write 2 or more (default: 1).
</details> ## Vérifier le bon placement des bounding box :
<details>
<summary>Séparation train/val</summary>
<p>
</p>
Exemple :
```bash ```bash
python3 get_train_val_YOLO.py -r RATIO -p PATH_TO_DATA -d DIRECTION_OF_THE_RESULT python get_train_annot.py /path/to/filename.txt -c column_name /path/to/data /output/directory -m personalized -u multiple --export annotation_export.txt --duration 10 --overlap 3 --sr 44100 --window 2048 --hop 1024 --cpu 2
``` ```
**WARNING** : Le script get_train_annot_YOLO.py exporte les .txt et .jpg dans deux dossiers nommés **labels_X_Y/** et **images_X_Y/** (X : Jour et Y : Mois). Pour faire tourner le code get_train_val_YOLO.py, il faut d'abord supprimer les 4 derniers caractères du nom de ces dossiers pour obtenir uniquement **images/** et **labels/** *Ajouter **--export** permet d'exporter les spectrogrammes avec les bounding box placées par dessus*
```python **WARNING** : Le mode uniforme permet d'extraire des bounding box avec y = 0.5 et h = 0.8; En utilisant -u unique seul le début des enregistrements est traité tandis que multiple permet de traiter plusieurs spectrogrammes; la fonction -c correspond au nom de la colonne qui contient le path des fichiers
##CHANGE TO MAKE##
path = str(args.path_to_data)
direction = str(args.direction)
NB_CLASS = 50 #CHOOSE THE X MOST REPRESENTED CLASSES
```
</details>
## Entrainement et Détection YOLO ## Entrainement et Détection YOLO
...@@ -152,17 +159,13 @@ cd yolo-dyni ...@@ -152,17 +159,13 @@ cd yolo-dyni
git clone https://github.com/lamipaul/yolov5 git clone https://github.com/lamipaul/yolov5
``` ```
<details>
<summary>Entrainement</summary> <summary>Entrainement</summary>
<p>
</p>
```bash ```bash
python3 train.py --img IMG_SIZE --batch BATCH_SIZE --epochs NB_EPOCHS --data DIRECTION_OF_THE_RESULT/custom_data.yaml --weights yolov5/weights/yolov5s.pt --cache python3 train.py --img IMG_SIZE --batch BATCH_SIZE --epochs NB_EPOCHS --data DIRECTION_OF_THE_RESULT/custom_data.yaml --weights yolov5/weights/yolov5s.pt --cache
``` ```
</details>
<details>
<summary>Détection</summary> <summary>Détection</summary>
<p> <p>
</p> </p>
...@@ -186,9 +189,7 @@ python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_S ...@@ -186,9 +189,7 @@ python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_S
```bash ```bash
python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_SIZE --conf 0.X --source PATH_TO_SPECTROGRAM_TO_DETECT --save-txt --nosave --save-conf python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_SIZE --conf 0.X --source PATH_TO_SPECTROGRAM_TO_DETECT --save-txt --nosave --save-conf
``` ```
</details>
<details>
<summary>Compilation des détections</summary> <summary>Compilation des détections</summary>
<p> <p>
</p> </p>
...@@ -197,7 +198,7 @@ python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_S ...@@ -197,7 +198,7 @@ python3 detect.py --weights yolov5/runs/train/EXP_NB/weights/best.pt --img IMG_S
cd ../ cd ../
python3 get_yolo_detection.py -p PATH_TO_THE_TXT -d DIRECTION_OF_THE_RESULT #PATH_TO_THE_TXT ~ yolov5/runs/detect/expX/labels python3 get_yolo_detection.py -p PATH_TO_THE_TXT -d DIRECTION_OF_THE_RESULT #PATH_TO_THE_TXT ~ yolov5/runs/detect/expX/labels
``` ```
**WARNING** : Il est important d'ajouter sa propre liste de classes (ligne 39) **WARNING** : Il est important d'ajouter sa propre liste de classes (ligne 30)
```python ```python
#put the classes here #put the classes here
...@@ -220,12 +221,8 @@ SR = args.SR ...@@ -220,12 +221,8 @@ SR = args.SR
names = [] names = []
``` ```
</details>
<details>
<summary>Conversion des détection YOLO vers LabelMe</summary> <summary>Conversion des détection YOLO vers LabelMe</summary>
<p>
</p>
```bash ```bash
pip install globox pip install globox
...@@ -239,7 +236,6 @@ globox convert yolov5/runs/detect/exp/labels/ DIRECTORY_TO_WICH_DATA_WILL_BE_STO ...@@ -239,7 +236,6 @@ globox convert yolov5/runs/detect/exp/labels/ DIRECTORY_TO_WICH_DATA_WILL_BE_STO
python3 get_json_file_YOLO.py -p PATH_TO_STORED_DATA -i PATH_TO_IMAGES -d DIRECTORY_TO_WICH_DATA_WILL_BE_STORED python3 get_json_file_YOLO.py -p PATH_TO_STORED_DATA -i PATH_TO_IMAGES -d DIRECTORY_TO_WICH_DATA_WILL_BE_STORED
``` ```
</details>
## Contact ## Contact
......
...@@ -6,6 +6,7 @@ import numpy as np ...@@ -6,6 +6,7 @@ import numpy as np
import pandas as pd import pandas as pd
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from p_tqdm import p_map from p_tqdm import p_map
import soundfile as sf
import warnings import warnings
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore')
...@@ -45,7 +46,8 @@ def process_recordings(data, img_per_rec, args): ...@@ -45,7 +46,8 @@ def process_recordings(data, img_per_rec, args):
for count in range(img_per_rec): for count in range(img_per_rec):
offset = count * (duration - overlap) offset = count * (duration - overlap)
filename = str(i[0]) filename = str(i[0])
file_duration = sf.info().duration
while offset < file_duration:
try: try:
y, _ = librosa.load(filename, offset=offset, duration=duration, sr=args.sr) y, _ = librosa.load(filename, offset=offset, duration=duration, sr=args.sr)
create_spectrogram(y, args.directory, filename, offset, duration, args.window, args.hop) create_spectrogram(y, args.directory, filename, offset, duration, args.window, args.hop)
...@@ -56,7 +58,7 @@ if __name__ == "__main__": ...@@ -56,7 +58,7 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Extract spectrogram for each .wav file') parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Extract spectrogram for each .wav file')
parser.add_argument('path_to_data', type=arg_directory, help='Path of the folder that contains the recordings') parser.add_argument('path_to_data', type=arg_directory, help='Path of the folder that contains the recordings')
parser.add_argument('directory', type=arg_directory, help='Directory to which spectrograms will be stored') parser.add_argument('directory', type=arg_directory, help='Directory to which spectrograms will be stored')
parser.add_argument('-m', '--mode', type=str, choices=['unique', 'multiple'], help='Direction of the saved spectrogram', default='multiple') parser.add_argument('-m', '--mode', type=str, choices=['unique', 'multiple'], help='if unique, only 1 image per file', default='multiple')
parser.add_argument('-n', '--columns_name', type=str, help='Name of the columns that contain the path of the .wav', default='Path') parser.add_argument('-n', '--columns_name', type=str, help='Name of the columns that contain the path of the .wav', default='Path')
parser.add_argument('-i', '--input', type=str, choices=['file', 'folder'], help='Choose "file" if you have a .csv file or "folder" to export ' parser.add_argument('-i', '--input', type=str, choices=['file', 'folder'], help='Choose "file" if you have a .csv file or "folder" to export '
'spectrogram from all the .wav of a folder', default='folder') 'spectrogram from all the .wav of a folder', default='folder')
......
...@@ -16,8 +16,7 @@ def create_directory(directory): ...@@ -16,8 +16,7 @@ def create_directory(directory):
try: try:
os.makedirs(os.path.join(directory,'labels'), exist_ok=True) os.makedirs(os.path.join(directory,'labels'), exist_ok=True)
except Exception as e: except Exception as e:
print(f'Error creating directory {directory}: {e}') print(f'`{path}` has been created')
return (directory) return (directory)
...@@ -29,7 +28,7 @@ def convert_labelme_to_yolo(labelme_annotation_path, yolo_directory): ...@@ -29,7 +28,7 @@ def convert_labelme_to_yolo(labelme_annotation_path, yolo_directory):
# YOLO annotation and image paths # YOLO annotation and image paths
yolo_annotation_path = os.path.join(yolo_directory, 'labels', f'{image_id}.txt') yolo_annotation_path = os.path.join(yolo_directory, 'labels', f'{image_id}.txt')
yolo_image_path = os.path.join(yolo_directory, 'images', f'{image_id}.jpg') yolo_image_path = os.path.join(yolo_directory, 'images/all', f'{image_id}.jpg')
with open(yolo_annotation_path, 'w') as yolo_annotation_file: with open(yolo_annotation_path, 'w') as yolo_annotation_file:
yolo_image_data = base64.b64decode(labelme_annotation['imageData']) yolo_image_data = base64.b64decode(labelme_annotation['imageData'])
...@@ -61,7 +60,9 @@ def convert_labelme_to_yolo(labelme_annotation_path, yolo_directory): ...@@ -61,7 +60,9 @@ def convert_labelme_to_yolo(labelme_annotation_path, yolo_directory):
def main(args): def main(args):
yolo_names = set() yolo_names = set()
create_directory(os.path.join(args.directory, 'images'))
create_directory(os.path.join(args.directory, 'images/all'))
create_directory(os.path.join(args.directory, 'labels'))
for labelme_annotation_path in glob(f'{args.path_to_data}/*.json'): for labelme_annotation_path in glob(f'{args.path_to_data}/*.json'):
convert_labelme_to_yolo(labelme_annotation_path, args.directory) convert_labelme_to_yolo(labelme_annotation_path, args.directory)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment