weiren119 commited on
Commit
34acdd0
1 Parent(s): 66d391d

Feat: app.py

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. app.py +115 -0
  2. examples/audiogram_example01.png +0 -0
  3. models/audiograms/audiograms_detection.yaml +9 -0
  4. models/audiograms/audiograms_detection_test.yaml +8 -0
  5. models/audiograms/format_dataset.py +151 -0
  6. models/audiograms/latest/hyp.yaml +23 -0
  7. models/audiograms/latest/labels.png +0 -0
  8. models/audiograms/latest/opt.yaml +30 -0
  9. models/audiograms/latest/results.png +0 -0
  10. models/audiograms/latest/results.txt +50 -0
  11. models/audiograms/latest/test_batch0_gt.jpg +0 -0
  12. models/audiograms/latest/test_batch0_pred.jpg +0 -0
  13. models/audiograms/latest/train_batch0.jpg +0 -0
  14. models/audiograms/latest/train_batch1.jpg +0 -0
  15. models/audiograms/latest/train_batch2.jpg +0 -0
  16. models/audiograms/yolov5s.yaml +48 -0
  17. models/labels/format_dataset.py +183 -0
  18. models/labels/labels_detection.yaml +32 -0
  19. models/labels/labels_detection_test.yaml +32 -0
  20. models/labels/latest/hyp.yaml +23 -0
  21. models/labels/latest/labels.png +0 -0
  22. models/labels/latest/opt.yaml +30 -0
  23. models/labels/latest/results.png +0 -0
  24. models/labels/latest/results.txt +100 -0
  25. models/labels/latest/train_batch0.jpg +0 -0
  26. models/labels/latest/train_batch1.jpg +0 -0
  27. models/labels/latest/train_batch2.jpg +0 -0
  28. models/labels/yolov5s.yaml +48 -0
  29. models/symbols/format_dataset.py +208 -0
  30. models/symbols/latest/hyp.yaml +23 -0
  31. models/symbols/latest/labels.png +0 -0
  32. models/symbols/latest/opt.yaml +30 -0
  33. models/symbols/latest/results.png +0 -0
  34. models/symbols/latest/results.txt +50 -0
  35. models/symbols/latest/test_batch0_gt.jpg +0 -0
  36. models/symbols/latest/test_batch0_pred.jpg +0 -0
  37. models/symbols/latest/train_batch0.jpg +0 -0
  38. models/symbols/latest/train_batch1.jpg +0 -0
  39. models/symbols/latest/train_batch2.jpg +0 -0
  40. models/symbols/symbols_detection.yaml +18 -0
  41. models/symbols/symbols_detection_test.yaml +17 -0
  42. models/symbols/yolov5s.yaml +48 -0
  43. requirements.txt +15 -0
  44. src/__pycache__/interfaces.cpython-38.pyc +0 -0
  45. src/digitize_report.py +55 -0
  46. src/digitizer/__pycache__/digitization.cpython-38.pyc +0 -0
  47. src/digitizer/assets/fonts/arial.ttf +0 -0
  48. src/digitizer/assets/symbols/left_air_masked.png +0 -0
  49. src/digitizer/assets/symbols/left_air_masked.svg +9 -0
  50. src/digitizer/assets/symbols/left_air_unmasked.png +0 -0
app.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+
3
+ Original Algorithm:
4
+ - https://github.com/GreenCUBIC/AudiogramDigitization
5
+
6
+ Source:
7
+ - huggingface app
8
+ - https://huggingface.co/spaces/aravinds1811/neural-style-transfer/blob/main/app.py
9
+ - https://huggingface.co/spaces/keras-io/ocr-for-captcha/blob/main/app.py
10
+ - https://huggingface.co/spaces/hugginglearners/image-style-transfer/blob/main/app.py
11
+ - https://tmabraham.github.io/blog/gradio_hf_spaces_tutorial
12
+ - huggingface push
13
+ - https://huggingface.co/welcome
14
+ """
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ from PIL import Image
20
+ from matplotlib.offsetbox import OffsetImage, AnnotationBbox
21
+ import matplotlib.pyplot as plt
22
+ import pandas as pd
23
+ import numpy as np
24
+ import gradio as gr
25
+
26
+ sys.path.append(os.path.join(os.path.dirname(__file__), "src"))
27
+ from digitizer.digitization import generate_partial_annotation, extract_thresholds
28
+
29
+ EXAMPLES_PATH = Path('./examples')
30
+
31
+
32
+ max_length = 5
33
+ img_width = 200
34
+ img_height = 50
35
+
36
+
37
+ def load_image(path, zoom=1):
38
+ return OffsetImage(plt.imread(path), zoom=zoom)
39
+
40
+ def plot_audiogram(digital_result):
41
+ thresholds = pd.DataFrame(digital_result)
42
+
43
+ # Figure
44
+ fig = plt.figure()
45
+ ax = fig.add_subplot(111)
46
+
47
+ # x axis
48
+ axis = [250, 500, 1000, 2000, 4000, 8000, 16000]
49
+ ax.set_xscale('log')
50
+ ax.xaxis.tick_top()
51
+ ax.xaxis.set_major_formatter(plt.FuncFormatter('{:.0f}'.format))
52
+ ax.set_xlabel('Frequency (Hz)')
53
+ ax.xaxis.set_label_position('top')
54
+ ax.set_xlim(125,16000)
55
+ plt.xticks(axis)
56
+
57
+ # y axis
58
+ ax.set_ylim(-20, 120)
59
+ ax.invert_yaxis()
60
+ ax.set_ylabel('Threshold (dB HL)')
61
+
62
+ plt.grid()
63
+
64
+ for conduction in ("air", "bone"):
65
+ for masking in (True, False):
66
+ for ear in ("left", "right"):
67
+ symbol_name = f"{ear}_{conduction}_{'unmasked' if not masking else 'masked'}"
68
+ selection = thresholds[(thresholds.conduction == conduction) & (thresholds.ear == ear) & (thresholds.masking == masking)]
69
+ selection = selection.sort_values("frequency")
70
+
71
+ # Plot the symbols
72
+ for i, threshold in selection.iterrows():
73
+ ab = AnnotationBbox(load_image(f"src/digitizer/assets/symbols/{symbol_name}.png", zoom=0.1), (threshold.frequency, threshold.threshold), frameon=False)
74
+ ax.add_artist(ab)
75
+
76
+ # Add joining line for air conduction thresholds
77
+ if conduction == "air":
78
+ plt.plot(selection.frequency, selection.threshold, color="red" if ear == "right" else "blue", linewidth=0.5)
79
+
80
+ return plt.gcf()
81
+ # Save audiogram plot to nparray
82
+ # get image as np.array
83
+ # canvas = plt.gca().figure.canvas
84
+ # canvas.draw()
85
+ # image = np.frombuffer(canvas.tostring_rgb(), dtype=np.uint8)
86
+ # return Image.fromarray(image)
87
+
88
+
89
+ # Function for Audiogram Digit Recognition
90
+ def audiogram_digit_recognition(img_path):
91
+ digital_result = extract_thresholds(img_path, gpu=False)
92
+ return plot_audiogram(digital_result)
93
+
94
+
95
+ output = gr.Plot()
96
+ examples = [f'{EXAMPLES_PATH}/audiogram_example01.png']
97
+
98
+ iface = gr.Interface(
99
+ fn=audiogram_digit_recognition,
100
+ inputs = gr.inputs.Image(type='filepath'),
101
+ outputs = output , #"image",
102
+ title=" AudiogramDigitization",
103
+ description = "facilitate the digitization of audiology reports based on pytorch",
104
+ article = "Algorithm Authors: <a href=\"francoischarih@sce.carleton.ca\">Francois Charih \
105
+ and <a href=\"jrgreen@sce.carleton.ca\"> James R. Green </a>. \
106
+ Based on the AudiogramDigitization <a href=\"https://github.com/GreenCUBIC/AudiogramDigitization\">github repo</a>",
107
+ examples = examples,
108
+ allow_flagging='never',
109
+ cache_examples=False,
110
+ )
111
+
112
+
113
+ iface.launch(
114
+ enable_queue=True, debug=False, inbrowser=False
115
+ )
examples/audiogram_example01.png ADDED
models/audiograms/audiograms_detection.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ train: models/audiograms/dataset/images/train/
3
+ val: models/audiograms/dataset/images/validation/
4
+
5
+ # number of classes
6
+ nc: 1
7
+
8
+ # class names
9
+ names: ["AUDIOGRAM"]
models/audiograms/audiograms_detection_test.yaml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ val: data/audiograms/test/images/
3
+
4
+ # number of classes
5
+ nc: 1
6
+
7
+ # class names
8
+ names: ["AUDIOGRAM"]
models/audiograms/format_dataset.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2020 Carleton University Biomedical Informatics Collaboratory
4
+
5
+ This source code is licensed under the MIT license found in the
6
+ LICENSE file in the root directory of this source tree.
7
+ """
8
+ from typing import List
9
+ from types import SimpleNamespace
10
+ import argparse, os, json, shutil
11
+ from tqdm import tqdm
12
+ import os.path as path
13
+ import numpy as np
14
+ from PIL import Image
15
+
16
+ def extract_audiograms(annotation: dict, image: Image) -> List[tuple]:
17
+ """Extracts the bounding boxes of audiograms into a tuple compatible
18
+ the YOLOv5 format.
19
+
20
+ Parameters
21
+ ----------
22
+ annotation : dict
23
+ A dictionary containing the annotations for the audiograms in a report.
24
+
25
+ image : Image
26
+ The image in PIL format corresponding to the annotation.
27
+
28
+ Returns
29
+ -------
30
+ tuple
31
+ A tuple of the form
32
+ (class index, x_center, y_center, width, height) where all coordinates
33
+ and dimensions are normalized to the width/height of the image.
34
+ """
35
+ audiogram_label_tuples = []
36
+ image_width, image_height = image.size
37
+ for audiogram in annotation:
38
+ bounding_box = audiogram["boundingBox"]
39
+ x_center = (bounding_box["x"] + bounding_box["width"] / 2) / image_width
40
+ y_center = (bounding_box["y"] + bounding_box["height"] / 2) / image_height
41
+ box_width = bounding_box["width"] / image_width
42
+ box_height = bounding_box["height"] / image_width
43
+ audiogram_label_tuples.append((0, x_center, y_center, box_width, box_height))
44
+ return audiogram_label_tuples
45
+
46
+ def create_yolov5_file(bboxes: List[tuple], filename: str):
47
+ # Turn the bounding boxes into a string with a bounding box
48
+ # on each line
49
+ file_content = "\n".join([
50
+ f"{bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} {bbox[4]}"
51
+ for bbox in bboxes
52
+ ])
53
+
54
+ # Save to a file
55
+ with open(filename, "w") as output_file:
56
+ output_file.write(file_content)
57
+
58
+ def create_directory_structure(data_dir: str):
59
+ try:
60
+ shutil.rmtree(path.join(data_dir))
61
+ except:
62
+ pass
63
+ os.mkdir(path.join(data_dir))
64
+ os.mkdir(path.join(data_dir, "images"))
65
+ os.mkdir(path.join(data_dir, "images", "train"))
66
+ os.mkdir(path.join(data_dir, "images", "validation"))
67
+ os.mkdir(path.join(data_dir, "labels"))
68
+ os.mkdir(path.join(data_dir, "labels", "train"))
69
+ os.mkdir(path.join(data_dir, "labels", "validation"))
70
+
71
+ def all_labels_valid(labels: List[tuple]):
72
+ for label in labels:
73
+ for value in label[1:]:
74
+ if value < 0 or value > 1:
75
+ return False
76
+ return True
77
+
78
+ def main(args: SimpleNamespace):
79
+ # Find all the JSON files in the input directory
80
+ report_ids = [
81
+ filename.rstrip(".json")
82
+ for filename in os.listdir(path.join(args.annotations_dir))
83
+ if filename.endswith(".json")
84
+ and path.exists(path.join(args.images_dir, filename.rstrip(".json") + ".jpg"))
85
+ ]
86
+
87
+ # Shuffle
88
+ np.random.seed(seed=42) # for reproducibility of the shuffle
89
+ np.random.shuffle(report_ids)
90
+
91
+ # Create the directory structure in which the images and annotations
92
+ # are to be stored
93
+ create_directory_structure(args.data_dir)
94
+
95
+ # Iterate through the report ids, extract the annotations in YOLOv5 format
96
+ # and place the file in the correct directory, and the image in the correct
97
+ # directory.
98
+ for i, report_id in enumerate(tqdm(report_ids)):
99
+ # Decide if the image is going into the training set or validation set
100
+ directory = (
101
+ "train" if i < args.train_frac * len(report_ids) else "validation"
102
+ )
103
+
104
+ # Load the annotation`
105
+ annotation_content = open(
106
+ path.join(args.annotations_dir, f"{report_id}.json")
107
+ )
108
+ annotation = json.load(annotation_content)
109
+
110
+ # Open the corresponding image to get its dimensions
111
+ image = Image.open(os.path.join(args.images_dir, f"{report_id}.jpg"))
112
+ width, height = image.size
113
+
114
+ # Audiogram labels
115
+ audiogram_labels = extract_audiograms(annotation, image)
116
+
117
+ if not all_labels_valid(audiogram_labels):
118
+ continue
119
+
120
+ create_yolov5_file(
121
+ audiogram_labels,
122
+ path.join(args.data_dir, "labels", directory, f"{report_id}.txt")
123
+ )
124
+ image.save(
125
+ path.join(args.data_dir, "images", directory, f"{report_id}.jpg")
126
+ )
127
+
128
+ if __name__ == "__main__":
129
+ import argparse
130
+
131
+ parser = argparse.ArgumentParser(description=(
132
+ "Script that formats the training set for transfer learning via "
133
+ "the YOLOv5 model."
134
+ ))
135
+ parser.add_argument("-d", "--data_dir", type=str, required=True, help=(
136
+ "Path to the directory containing the data. It should have 3 "
137
+ "subfolders named `images`, `annotations` and `labels`."
138
+ ))
139
+ parser.add_argument("-a", "--annotations_dir", type=str, required=True, help=(
140
+ "Path to the directory containing the annotations in the JSON format."
141
+ ))
142
+ parser.add_argument("-i", "--images_dir", type=str, required=True, help=(
143
+ "Path to the directory containing the images."
144
+ ))
145
+ parser.add_argument("-f", "--train_frac", type=float, required=True, help=(
146
+ "Fraction of images to be used for training. (e.g. 0.8)"
147
+ ))
148
+ args = parser.parse_args()
149
+
150
+ main(args)
151
+
models/audiograms/latest/hyp.yaml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lr0: 0.01
2
+ lrf: 0.2
3
+ momentum: 0.937
4
+ weight_decay: 0.0005
5
+ giou: 0.05
6
+ cls: 0.5
7
+ cls_pw: 1.0
8
+ obj: 1.0
9
+ obj_pw: 1.0
10
+ iou_t: 0.2
11
+ anchor_t: 4.0
12
+ fl_gamma: 0.0
13
+ hsv_h: 0.015
14
+ hsv_s: 0.7
15
+ hsv_v: 0.4
16
+ degrees: 0.0
17
+ translate: 0.1
18
+ scale: 0.5
19
+ shear: 0.0
20
+ perspective: 0.0
21
+ flipud: 0.0
22
+ fliplr: 0.5
23
+ mixup: 0.0
models/audiograms/latest/labels.png ADDED
models/audiograms/latest/opt.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ weights: yolov5/weights/yolov5s.pt
2
+ cfg: yolov5/models/yolov5s.yaml
3
+ data: ./data/dataset.yaml
4
+ hyp: ./yolov5/data/hyp.scratch.yaml
5
+ epochs: 50
6
+ batch_size: 16
7
+ img_size:
8
+ - 1024
9
+ - 1024
10
+ rect: true
11
+ resume: false
12
+ nosave: false
13
+ notest: false
14
+ noautoanchor: false
15
+ evolve: false
16
+ bucket: ''
17
+ cache_images: false
18
+ image_weights: false
19
+ name: ''
20
+ device: '0'
21
+ multi_scale: false
22
+ single_cls: false
23
+ adam: false
24
+ sync_bn: false
25
+ local_rank: -1
26
+ logdir: runs/
27
+ workers: 8
28
+ total_batch_size: 16
29
+ world_size: 1
30
+ global_rank: -1
models/audiograms/latest/results.png ADDED
models/audiograms/latest/results.txt ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0/49 7.24G 0.03451 0.01234 0 0.04685 18 768 0.386 0.8519 0.6314 0.2382 0.01455 0.007371 0
2
+ 1/49 7.25G 0.02255 0.005412 0 0.02796 18 768 0.7806 0.9831 0.9762 0.7314 0.0121 0.003704 0
3
+ 2/49 7.25G 0.01706 0.003746 0 0.0208 18 768 0.8866 0.9896 0.9793 0.741 0.009516 0.002951 0
4
+ 3/49 7.25G 0.01726 0.003265 0 0.02052 18 768 0.8734 0.9961 0.9858 0.691 0.01019 0.002468 0
5
+ 4/49 7.25G 0.01645 0.003014 0 0.01946 18 768 0.8789 0.9961 0.9885 0.8278 0.009626 0.002319 0
6
+ 5/49 7.25G 0.01519 0.002852 0 0.01805 18 768 0.9051 0.9961 0.984 0.778 0.01027 0.002376 0
7
+ 6/49 7.25G 0.01463 0.002948 0 0.01758 18 768 0.9223 0.9922 0.9857 0.8341 0.008192 0.002504 0
8
+ 7/49 7.25G 0.01365 0.0028 0 0.01645 18 768 0.7791 0.9935 0.9828 0.8224 0.007701 0.002257 0
9
+ 8/49 7.25G 0.01187 0.002558 0 0.01443 18 768 0.8548 0.9987 0.9807 0.843 0.007083 0.002018 0
10
+ 9/49 7.25G 0.01161 0.002421 0 0.01403 18 768 0.9653 0.9961 0.9884 0.8421 0.007059 0.001885 0
11
+ 10/49 7.25G 0.01053 0.002392 0 0.01292 18 768 0.9588 0.9974 0.9875 0.8607 0.005953 0.001821 0
12
+ 11/49 7.25G 0.009697 0.002255 0 0.01195 18 768 0.9591 0.9935 0.9851 0.8387 0.006067 0.001839 0
13
+ 12/49 7.25G 0.009541 0.002229 0 0.01177 18 768 0.9619 0.9987 0.9872 0.8725 0.005808 0.0017 0
14
+ 13/49 7.25G 0.009704 0.002185 0 0.01189 18 768 0.9693 0.9961 0.9907 0.8767 0.005385 0.001728 0
15
+ 14/49 7.25G 0.009079 0.002169 0 0.01125 18 768 0.9691 0.9974 0.9841 0.8531 0.006121 0.001715 0
16
+ 15/49 7.25G 0.008323 0.002161 0 0.01048 18 768 0.9579 0.9948 0.9882 0.8838 0.004668 0.001631 0
17
+ 16/49 7.25G 0.008653 0.002083 0 0.01074 18 768 0.9768 0.9987 0.9913 0.8845 0.004903 0.001578 0
18
+ 17/49 7.25G 0.007987 0.002011 0 0.009997 18 768 0.9786 0.9974 0.9895 0.8873 0.005501 0.001565 0
19
+ 18/49 7.25G 0.008903 0.001963 0 0.01087 18 768 0.9506 0.9987 0.9913 0.8784 0.006132 0.0016 0
20
+ 19/49 7.25G 0.008407 0.001971 0 0.01038 18 768 0.9722 0.9999 0.9908 0.8825 0.004867 0.001558 0
21
+ 20/49 7.25G 0.007909 0.001966 0 0.009875 18 768 0.9649 0.9974 0.9905 0.8781 0.005032 0.001563 0
22
+ 21/49 7.25G 0.00755 0.001911 0 0.00946 18 768 0.9733 1 0.9916 0.8879 0.005532 0.001542 0
23
+ 22/49 7.25G 0.007531 0.001892 0 0.009423 18 768 0.9359 1 0.9881 0.8821 0.005925 0.001577 0
24
+ 23/49 7.25G 0.007707 0.001834 0 0.009541 18 768 0.9757 1 0.9881 0.8889 0.004511 0.001455 0
25
+ 24/49 7.25G 0.00729 0.00182 0 0.00911 18 768 0.9608 1 0.9889 0.8925 0.004974 0.001477 0
26
+ 25/49 7.25G 0.007106 0.001769 0 0.008875 18 768 0.9686 0.9987 0.9907 0.8887 0.004586 0.001471 0
27
+ 26/49 7.25G 0.00679 0.001756 0 0.008546 18 768 0.9862 0.9974 0.993 0.8934 0.004579 0.001438 0
28
+ 27/49 7.25G 0.006621 0.001754 0 0.008375 18 768 0.9812 1 0.993 0.8953 0.004354 0.001446 0
29
+ 28/49 7.25G 0.006727 0.001672 0 0.008399 18 768 0.9821 1 0.9919 0.8892 0.004686 0.0014 0
30
+ 29/49 7.25G 0.006892 0.001684 0 0.008577 18 768 0.9817 0.9987 0.993 0.8939 0.004496 0.001401 0
31
+ 30/49 7.25G 0.006629 0.001684 0 0.008313 18 768 0.9834 1 0.9932 0.8978 0.004209 0.001381 0
32
+ 31/49 7.25G 0.006299 0.001594 0 0.007892 18 768 0.973 1 0.9916 0.8962 0.004526 0.001369 0
33
+ 32/49 7.25G 0.006212 0.00162 0 0.007831 18 768 0.9842 1 0.9909 0.8979 0.004059 0.001345 0
34
+ 33/49 7.25G 0.006012 0.001572 0 0.007583 18 768 0.9817 1 0.9905 0.8964 0.004181 0.001342 0
35
+ 34/49 7.25G 0.006232 0.001605 0 0.007837 18 768 0.9831 1 0.9922 0.9055 0.004233 0.001335 0
36
+ 35/49 7.25G 0.005956 0.001566 0 0.007522 18 768 0.9866 1 0.9924 0.9064 0.004158 0.001314 0
37
+ 36/49 7.25G 0.005979 0.001521 0 0.0075 18 768 0.9813 1 0.9915 0.8951 0.004402 0.00133 0
38
+ 37/49 7.25G 0.005544 0.001476 0 0.00702 18 768 0.9859 1 0.9931 0.906 0.004128 0.001315 0
39
+ 38/49 7.25G 0.00582 0.0015 0 0.007321 18 768 0.9808 1 0.9931 0.9066 0.004205 0.00132 0
40
+ 39/49 7.25G 0.005607 0.001494 0 0.007101 18 768 0.9867 1 0.9926 0.9073 0.004047 0.001323 0
41
+ 40/49 7.25G 0.005674 0.001459 0 0.007133 18 768 0.9863 1 0.9923 0.9064 0.004046 0.001307 0
42
+ 41/49 7.25G 0.005712 0.001452 0 0.007165 18 768 0.9863 1 0.9924 0.9051 0.00421 0.001297 0
43
+ 42/49 7.25G 0.005655 0.001459 0 0.007113 18 768 0.9823 1 0.9931 0.9089 0.004065 0.001296 0
44
+ 43/49 7.25G 0.005481 0.001445 0 0.006926 18 768 0.9847 0.9987 0.9933 0.91 0.004256 0.001298 0
45
+ 44/49 7.25G 0.005456 0.001474 0 0.00693 18 768 0.9844 0.9987 0.9938 0.9111 0.004057 0.001314 0
46
+ 45/49 7.25G 0.005749 0.00145 0 0.0072 18 768 0.9863 0.9987 0.9938 0.9076 0.004 0.001296 0
47
+ 46/49 7.25G 0.005401 0.001397 0 0.006798 18 768 0.985 1 0.9937 0.9114 0.004002 0.001283 0
48
+ 47/49 7.25G 0.005416 0.001401 0 0.006818 18 768 0.9839 1 0.9936 0.9074 0.004271 0.001285 0
49
+ 48/49 7.25G 0.005382 0.001397 0 0.00678 18 768 0.9857 0.9987 0.9939 0.9101 0.00399 0.00127 0
50
+ 49/49 7.25G 0.00526 0.001375 0 0.006635 18 768 0.9754 1 0.994 0.9117 0.004094 0.001291 0
models/audiograms/latest/test_batch0_gt.jpg ADDED
models/audiograms/latest/test_batch0_pred.jpg ADDED
models/audiograms/latest/train_batch0.jpg ADDED
models/audiograms/latest/train_batch1.jpg ADDED
models/audiograms/latest/train_batch2.jpg ADDED
models/audiograms/yolov5s.yaml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # parameters
2
+ nc: 1 # number of classes
3
+ depth_multiple: 0.33 # model depth multiple
4
+ width_multiple: 0.50 # layer channel multiple
5
+
6
+ # anchors
7
+ anchors:
8
+ - [10,13, 16,30, 33,23] # P3/8
9
+ - [30,61, 62,45, 59,119] # P4/16
10
+ - [116,90, 156,198, 373,326] # P5/32
11
+
12
+ # YOLOv5 backbone
13
+ backbone:
14
+ # [from, number, module, args]
15
+ [[-1, 1, Focus, [64, 3]], # 0-P1/2
16
+ [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
17
+ [-1, 3, BottleneckCSP, [128]],
18
+ [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
19
+ [-1, 9, BottleneckCSP, [256]],
20
+ [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
21
+ [-1, 9, BottleneckCSP, [512]],
22
+ [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
23
+ [-1, 1, SPP, [1024, [5, 9, 13]]],
24
+ [-1, 3, BottleneckCSP, [1024, False]], # 9
25
+ ]
26
+
27
+ # YOLOv5 head
28
+ head:
29
+ [[-1, 1, Conv, [512, 1, 1]],
30
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
31
+ [[-1, 6], 1, Concat, [1]], # cat backbone P4
32
+ [-1, 3, BottleneckCSP, [512, False]], # 13
33
+
34
+ [-1, 1, Conv, [256, 1, 1]],
35
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
36
+ [[-1, 4], 1, Concat, [1]], # cat backbone P3
37
+ [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small)
38
+
39
+ [-1, 1, Conv, [256, 3, 2]],
40
+ [[-1, 14], 1, Concat, [1]], # cat head P4
41
+ [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium)
42
+
43
+ [-1, 1, Conv, [512, 3, 2]],
44
+ [[-1, 10], 1, Concat, [1]], # cat head P5
45
+ [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large)
46
+
47
+ [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
48
+ ]
models/labels/format_dataset.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2020 Carleton University Biomedical Informatics Collaboratory
4
+
5
+ This source code is licensed under the MIT license found in the
6
+ LICENSE file in the root directory of this source tree.
7
+ """
8
+ from typing import List
9
+ from types import SimpleNamespace
10
+ import argparse, os, json, shutil
11
+ from tqdm import tqdm
12
+ import os.path as path
13
+ import numpy as np
14
+ from PIL import Image
15
+
16
+ LABEL_CLASS_INDICES = {
17
+ "250": 0,
18
+ ".25": 1,
19
+ "500": 2,
20
+ ".5": 3,
21
+ "1000": 4,
22
+ "1": 5,
23
+ "1K": 6,
24
+ "2000": 7,
25
+ "2": 8,
26
+ "2K": 9,
27
+ "4000": 10,
28
+ "4": 11,
29
+ "4K": 12,
30
+ "8000": 13,
31
+ "8": 14,
32
+ "8K": 15,
33
+ "0": 16,
34
+ "20": 17,
35
+ "40": 18,
36
+ "60": 19,
37
+ "80": 20,
38
+ "100": 21,
39
+ "120": 22
40
+ }
41
+
42
+ def extract_labels(annotation: dict, image: Image) -> List[tuple]:
43
+ """Extracts the bounding boxes of labels into a tuple compatible
44
+ the YOLOv5 format.
45
+
46
+ Parameters
47
+ ----------
48
+ annotation : dict
49
+ A dictionary containing the annotations for the audiograms in a report.
50
+
51
+ image : Image
52
+ The image in PIL format corresponding to the annotation.
53
+
54
+ Returns
55
+ -------
56
+ tuple
57
+ A tuple of the form
58
+ (class index, x_center, y_center, width, height) where all coordinates
59
+ and dimensions are normalized to the width/height of the image.
60
+ """
61
+ label_label_tuples = []
62
+ image_width, image_height = image.size
63
+ for audiogram in annotation:
64
+ for label in audiogram["labels"]:
65
+ bounding_box = label["boundingBox"]
66
+ x_center = (bounding_box["x"] + bounding_box["width"] / 2) / image_width
67
+ y_center = (bounding_box["y"] + bounding_box["height"] / 2) / image_height
68
+ box_width = bounding_box["width"] / image_width
69
+ box_height = bounding_box["height"] / image_width
70
+ try:
71
+ label_label_tuples.append((LABEL_CLASS_INDICES[label["value"]], x_center, y_center, box_width, box_height))
72
+ except:
73
+ continue
74
+ return label_label_tuples
75
+
76
+ def create_directory_structure(data_dir: str):
77
+ try:
78
+ shutil.rmtree(path.join(data_dir))
79
+ except:
80
+ pass
81
+ os.mkdir(path.join(data_dir))
82
+ os.mkdir(path.join(data_dir, "images"))
83
+ os.mkdir(path.join(data_dir, "images", "train"))
84
+ os.mkdir(path.join(data_dir, "images", "validation"))
85
+ os.mkdir(path.join(data_dir, "labels"))
86
+ os.mkdir(path.join(data_dir, "labels", "train"))
87
+ os.mkdir(path.join(data_dir, "labels", "validation"))
88
+
89
+ def create_yolov5_file(bboxes: List[tuple], filename: str):
90
+ # Turn the bounding boxes into a string with a bounding box
91
+ # on each line
92
+ file_content = "\n".join([
93
+ f"{bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} {bbox[4]}"
94
+ for bbox in bboxes
95
+ ])
96
+
97
+ # Save to a file
98
+ with open(filename, "w") as output_file:
99
+ output_file.write(file_content)
100
+
101
+ def all_labels_valid(labels: List[tuple]):
102
+ for label in labels:
103
+ for value in label[1:]:
104
+ if value < 0 or value > 1:
105
+ return False
106
+ return True
107
+
108
+
109
+ def main(args: SimpleNamespace):
110
+
111
+ # Find all the JSON files in the input directory
112
+ report_ids = [
113
+ filename.rstrip(".json")
114
+ for filename in os.listdir(path.join(args.annotations_dir))
115
+ if filename.endswith(".json")
116
+ and path.exists(path.join(args.images_dir, filename.rstrip(".json") + ".jpg"))
117
+ ]
118
+
119
+ # Shuffle
120
+ np.random.seed(seed=42) # for reproducibility of the shuffle
121
+ np.random.shuffle(report_ids)
122
+
123
+ # Create the directory structure in which the images and annotations
124
+ # are to be stored
125
+ create_directory_structure(args.data_dir)
126
+
127
+ # Iterate through the report ids, extract the annotations in YOLOv5 format
128
+ # and place the file in the correct directory, and the image in the correct
129
+ # directory.
130
+ for i, report_id in enumerate(tqdm(report_ids)):
131
+
132
+ # Decide if the image is going into the training set or validation set
133
+ directory = (
134
+ "train" if i < args.train_frac * len(report_ids) else "validation"
135
+ )
136
+
137
+ # Load the annotation`
138
+ annotation_content = open(
139
+ path.join(args.annotations_dir, f"{report_id}.json")
140
+ )
141
+
142
+ image = Image.open(os.path.join(args.images_dir, f"{report_id}.jpg"))
143
+
144
+ annotation = json.load(annotation_content)
145
+ bounding_boxes = extract_labels(annotation, image)
146
+
147
+ if not all_labels_valid(bounding_boxes):
148
+ continue
149
+
150
+ # Open the corresponding image to get its dimensions
151
+ image = Image.open(os.path.join(args.images_dir, f"{report_id}.jpg"))
152
+
153
+ create_yolov5_file(
154
+ bounding_boxes,
155
+ path.join(args.data_dir, "labels", directory, f"{report_id}.txt")
156
+ )
157
+ image.save(
158
+ path.join(args.data_dir, "images", directory, f"{report_id}.jpg")
159
+ )
160
+
161
+ if __name__ == "__main__":
162
+ import argparse
163
+
164
+ parser = argparse.ArgumentParser(description=(
165
+ "Script that formats the training set for transfer learning of labels detection via "
166
+ "the YOLOv5 model."
167
+ ))
168
+ parser.add_argument("-d", "--data_dir", type=str, required=True, help=(
169
+ "Path to the directory where the training set should be created."
170
+ ))
171
+ parser.add_argument("-a", "--annotations_dir", type=str, required=True, help=(
172
+ "Path to the directory containing the annotations in the JSON format."
173
+ ))
174
+ parser.add_argument("-i", "--images_dir", type=str, required=True, help=(
175
+ "Path to the directory containing the images."
176
+ ))
177
+ parser.add_argument("-f", "--train_frac", type=float, required=True, help=(
178
+ "Fraction of images to be used for training. (e.g. 0.8)"
179
+ ))
180
+ args = parser.parse_args()
181
+
182
+ main(args)
183
+
models/labels/labels_detection.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ train: models/labels/dataset/images/train/
3
+ val: models/labels/dataset/images/validation/
4
+
5
+ # number of classes
6
+ nc: 21
7
+
8
+ # class names
9
+ names: [
10
+ #frequencies
11
+ "250",
12
+ ".25",
13
+ "500",
14
+ ".5",
15
+ "1000",
16
+ "1",
17
+ "1K",
18
+ "2000",
19
+ "2",
20
+ "2K",
21
+ "4000",
22
+ "4",
23
+ "4K",
24
+ "8000",
25
+ "8",
26
+ "8K",
27
+ "20",
28
+ "40",
29
+ "60",
30
+ "80",
31
+ "100",
32
+ ]
models/labels/labels_detection_test.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ val: data/labels/test/images/
3
+
4
+
5
+ # number of classes
6
+ nc: 21
7
+
8
+ # class names
9
+ names: [
10
+ #frequencies
11
+ "250",
12
+ ".25",
13
+ "500",
14
+ ".5",
15
+ "1000",
16
+ "1",
17
+ "1K",
18
+ "2000",
19
+ "2",
20
+ "2K",
21
+ "4000",
22
+ "4",
23
+ "4K",
24
+ "8000",
25
+ "8",
26
+ "8K",
27
+ "20",
28
+ "40",
29
+ "60",
30
+ "80",
31
+ "100",
32
+ ]
models/labels/latest/hyp.yaml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lr0: 0.01
2
+ lrf: 0.2
3
+ momentum: 0.937
4
+ weight_decay: 0.0005
5
+ giou: 0.05
6
+ cls: 0.5
7
+ cls_pw: 1.0
8
+ obj: 1.0
9
+ obj_pw: 1.0
10
+ iou_t: 0.2
11
+ anchor_t: 4.0
12
+ fl_gamma: 0.0
13
+ hsv_h: 0.015
14
+ hsv_s: 0.7
15
+ hsv_v: 0.4
16
+ degrees: 0.0
17
+ translate: 0.1
18
+ scale: 0.5
19
+ shear: 0.0
20
+ perspective: 0.0
21
+ flipud: 0.0
22
+ fliplr: 0.5
23
+ mixup: 0.0
models/labels/latest/labels.png ADDED
models/labels/latest/opt.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ weights: src/digitizer/yolov5/weights/yolov5s.pt
2
+ cfg: models/labels/yolov5s.yaml
3
+ data: models/labels/labels_detection.yaml
4
+ hyp: models/labels/latest/hyp.yaml
5
+ epochs: 100
6
+ batch_size: 16
7
+ img_size:
8
+ - 1024
9
+ - 1024
10
+ img_weights: false
11
+ rect: true
12
+ resume: false
13
+ nosave: false
14
+ notest: false
15
+ noautoanchor: false
16
+ evolve: false
17
+ bucket: ''
18
+ cache_images: false
19
+ name: ''
20
+ device: '0'
21
+ multi_scale: false
22
+ single_cls: false
23
+ adam: false
24
+ sync_bn: false
25
+ local_rank: -1
26
+ logdir: models/labels/model_2022-02-13T23:42:43.525128
27
+ workers: 8
28
+ total_batch_size: 16
29
+ world_size: 1
30
+ global_rank: -1
models/labels/latest/results.png ADDED
models/labels/latest/results.txt ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0/99 6.21G 0.1343 0.03079 0.08276 0.2479 11 768 0 0 7.281e-07 7.281e-08 0.1162 0.04079 0.07292
2
+ 1/99 7.53G 0.1242 0.03164 0.07888 0.2347 21 768 0 0 6.771e-07 1.354e-07 0.108 0.04562 0.07046
3
+ 2/99 7.54G 0.1144 0.03571 0.07596 0.2261 11 768 0 0 1.755e-05 3.077e-06 0.103 0.04908 0.06943
4
+ 3/99 7.54G 0.0974 0.03939 0.07176 0.2086 21 768 0 0 0.002906 0.0006505 0.08903 0.05595 0.06616
5
+ 4/99 7.54G 0.08186 0.04142 0.06885 0.1921 21 768 0.06358 0.04671 0.009447 0.002094 0.07272 0.05272 0.0647
6
+ 5/99 7.54G 0.07081 0.03785 0.06656 0.1752 16 768 0.0354 0.1326 0.02577 0.007177 0.07022 0.04713 0.06211
7
+ 6/99 7.54G 0.06693 0.03364 0.06371 0.1643 21 768 0.08782 0.1313 0.0288 0.00777 0.06502 0.04441 0.05983
8
+ 7/99 7.54G 0.07509 0.03042 0.06239 0.1679 21 768 0.09319 0.08421 0.03582 0.009998 0.07345 0.04032 0.05778
9
+ 8/99 7.54G 0.07271 0.03005 0.06178 0.1645 21 768 0.03703 0.08961 0.02776 0.007952 0.07044 0.03962 0.05648
10
+ 9/99 7.54G 0.06547 0.03006 0.06104 0.1566 11 768 0.0528 0.1506 0.05014 0.01864 0.06365 0.0403 0.05501
11
+ 10/99 7.54G 0.06298 0.02965 0.05742 0.15 21 768 0.1122 0.1863 0.04733 0.01125 0.0575 0.03968 0.05371
12
+ 11/99 7.54G 0.06355 0.02845 0.05799 0.15 21 768 0.02575 0.2244 0.0501 0.01571 0.06196 0.03754 0.05244
13
+ 12/99 7.54G 0.06407 0.02723 0.05621 0.1475 15 768 0.09019 0.2198 0.07907 0.02394 0.05992 0.03758 0.05161
14
+ 13/99 7.54G 0.06065 0.02711 0.05451 0.1423 21 768 0.1057 0.2525 0.08484 0.02365 0.05797 0.03694 0.05046
15
+ 14/99 7.54G 0.06177 0.02657 0.05381 0.1421 15 768 0.121 0.2575 0.1265 0.04451 0.04893 0.03792 0.04975
16
+ 15/99 7.54G 0.05702 0.02646 0.05244 0.1359 21 768 0.1032 0.17 0.05749 0.0109 0.0623 0.03332 0.04756
17
+ 16/99 7.54G 0.05945 0.0258 0.05168 0.1369 21 768 0.0545 0.3034 0.1181 0.03307 0.05174 0.03514 0.04698
18
+ 17/99 7.54G 0.05679 0.02512 0.049 0.1309 21 768 0.07869 0.2468 0.08314 0.01848 0.06121 0.03403 0.04614
19
+ 18/99 7.54G 0.05769 0.02475 0.04614 0.1286 21 768 0.1255 0.3201 0.1718 0.05578 0.0461 0.03457 0.04471
20
+ 19/99 7.54G 0.05541 0.02479 0.04628 0.1265 16 768 0.0921 0.3097 0.1579 0.04787 0.05198 0.0329 0.0437
21
+ 20/99 7.54G 0.05562 0.02399 0.04531 0.1249 21 768 0.1884 0.2838 0.2334 0.09286 0.04295 0.03469 0.04319
22
+ 21/99 7.54G 0.05902 0.02642 0.0444 0.1298 21 768 0.08879 0.3126 0.1348 0.04382 0.05467 0.03429 0.0427
23
+ 22/99 7.54G 0.05915 0.02351 0.04213 0.1248 21 768 0.1733 0.2864 0.1863 0.05512 0.0518 0.03256 0.04173
24
+ 23/99 7.54G 0.05873 0.02303 0.04268 0.1244 21 768 0.1824 0.2923 0.2162 0.08527 0.04832 0.03428 0.04204
25
+ 24/99 7.54G 0.05814 0.02323 0.04174 0.1231 21 768 0.2552 0.2882 0.2324 0.08491 0.04881 0.03281 0.04108
26
+ 25/99 7.54G 0.0542 0.02279 0.04018 0.1172 21 768 0.1997 0.3405 0.2469 0.09714 0.04412 0.03141 0.03916
27
+ 26/99 7.54G 0.0565 0.02218 0.03958 0.1183 21 768 0.182 0.3361 0.264 0.1002 0.04624 0.03252 0.03926
28
+ 27/99 7.54G 0.05618 0.02221 0.03895 0.1173 21 768 0.1902 0.3454 0.2715 0.1179 0.04296 0.03168 0.0382
29
+ 28/99 7.54G 0.05488 0.02226 0.03859 0.1157 16 768 0.2451 0.3586 0.2861 0.1166 0.04555 0.03129 0.03693
30
+ 29/99 7.54G 0.05608 0.02212 0.0377 0.1159 21 768 0.2855 0.3915 0.3077 0.1391 0.04094 0.03167 0.03626
31
+ 30/99 7.54G 0.05181 0.02179 0.03597 0.1096 21 768 0.1752 0.4288 0.3178 0.1396 0.04052 0.03098 0.03552
32
+ 31/99 7.54G 0.05332 0.02198 0.03573 0.111 21 768 0.1629 0.4209 0.3078 0.142 0.04067 0.03126 0.03505
33
+ 32/99 7.54G 0.05079 0.02127 0.03426 0.1063 16 768 0.2647 0.4019 0.3205 0.1273 0.04228 0.0313 0.03469
34
+ 33/99 7.54G 0.0494 0.02121 0.03287 0.1035 16 768 0.2514 0.4279 0.3248 0.1422 0.04237 0.03096 0.03346
35
+ 34/99 7.54G 0.05187 0.02088 0.03268 0.1054 21 768 0.1209 0.4301 0.1953 0.05969 0.06217 0.03133 0.03224
36
+ 35/99 7.54G 0.05873 0.02148 0.03242 0.1126 21 768 0.2324 0.4623 0.3573 0.1543 0.04246 0.0305 0.03147
37
+ 36/99 7.54G 0.05398 0.02115 0.031 0.1061 21 768 0.2775 0.3886 0.3195 0.1042 0.05145 0.02996 0.03089
38
+ 37/99 7.54G 0.05629 0.02095 0.03121 0.1084 21 768 0.2928 0.4967 0.3996 0.191 0.03951 0.03025 0.03075
39
+ 38/99 7.54G 0.05136 0.02113 0.03045 0.1029 16 768 0.2254 0.4235 0.2982 0.08752 0.05017 0.02989 0.02975
40
+ 39/99 7.54G 0.05178 0.02102 0.03069 0.1035 18 768 0.2775 0.5149 0.4013 0.178 0.0392 0.02998 0.03019
41
+ 40/99 7.54G 0.05023 0.02042 0.02886 0.09951 21 768 0.303 0.5104 0.4054 0.1795 0.03967 0.03003 0.03028
42
+ 41/99 7.54G 0.05038 0.02051 0.02829 0.09918 21 768 0.3075 0.4837 0.3993 0.1842 0.03969 0.03067 0.03043
43
+ 42/99 7.54G 0.04899 0.0214 0.02776 0.09815 21 768 0.263 0.5256 0.3875 0.1401 0.04635 0.02994 0.02939
44
+ 43/99 7.54G 0.05134 0.02092 0.02657 0.09883 21 768 0.3034 0.5298 0.4287 0.1998 0.03948 0.02972 0.02878
45
+ 44/99 7.54G 0.04718 0.02077 0.02645 0.0944 21 768 0.28 0.5414 0.3957 0.1446 0.04332 0.02932 0.02855
46
+ 45/99 7.54G 0.04808 0.02024 0.0247 0.09302 21 768 0.3138 0.583 0.4504 0.2132 0.0362 0.02902 0.02797
47
+ 46/99 7.54G 0.04708 0.01997 0.02575 0.0928 16 768 0.3195 0.5838 0.4504 0.2127 0.0376 0.02911 0.02815
48
+ 47/99 7.54G 0.0468 0.01994 0.02458 0.09131 21 768 0.2988 0.5071 0.389 0.1299 0.04774 0.0288 0.02772
49
+ 48/99 7.54G 0.04817 0.01988 0.02352 0.09157 21 768 0.344 0.5396 0.452 0.2008 0.03903 0.02954 0.0275
50
+ 49/99 7.54G 0.04504 0.01988 0.02324 0.08816 21 768 0.3524 0.5616 0.4528 0.1937 0.03945 0.02953 0.02615
51
+ 50/99 7.54G 0.04447 0.01977 0.0229 0.08714 21 768 0.331 0.5934 0.468 0.2189 0.03849 0.02878 0.0256
52
+ 51/99 7.54G 0.04404 0.01981 0.02222 0.08607 21 768 0.3857 0.6196 0.4866 0.2108 0.03942 0.02866 0.02547
53
+ 52/99 7.54G 0.04531 0.01942 0.02096 0.08569 21 768 0.3871 0.6105 0.4906 0.2136 0.04076 0.02887 0.02571
54
+ 53/99 7.54G 0.04653 0.01982 0.02123 0.08759 16 768 0.4044 0.6181 0.5071 0.2409 0.03791 0.029 0.02615
55
+ 54/99 7.54G 0.0447 0.0197 0.02206 0.08646 19 768 0.3528 0.5916 0.4848 0.1903 0.04306 0.02868 0.02565
56
+ 55/99 7.54G 0.04564 0.01984 0.02034 0.08581 15 768 0.3561 0.6169 0.5131 0.253 0.03622 0.02889 0.02553
57
+ 56/99 7.54G 0.04329 0.01954 0.02013 0.08295 21 768 0.3473 0.5974 0.4971 0.1924 0.0421 0.02892 0.02461
58
+ 57/99 7.54G 0.04636 0.02079 0.02197 0.08912 21 768 0.4038 0.6035 0.5051 0.2287 0.03948 0.02927 0.02535
59
+ 58/99 7.54G 0.04345 0.02027 0.0214 0.08512 21 768 0.3235 0.5661 0.4657 0.1647 0.04387 0.0298 0.02548
60
+ 59/99 7.54G 0.04443 0.02014 0.02079 0.08536 16 768 0.3375 0.6146 0.5142 0.2345 0.03623 0.02925 0.02474
61
+ 60/99 7.54G 0.03998 0.01952 0.01991 0.07941 21 768 0.3307 0.61 0.529 0.2253 0.03825 0.02906 0.02412
62
+ 61/99 7.54G 0.04457 0.01975 0.01874 0.08306 16 768 0.4053 0.6245 0.529 0.248 0.03673 0.02878 0.02379
63
+ 62/99 7.54G 0.04051 0.01926 0.01898 0.07875 21 768 0.3944 0.6172 0.5391 0.2481 0.03759 0.02885 0.02325
64
+ 63/99 7.54G 0.04078 0.0187 0.02034 0.07983 15 768 0.4068 0.5992 0.542 0.2405 0.03861 0.02906 0.02271
65
+ 64/99 7.54G 0.04194 0.01933 0.01778 0.07905 21 768 0.4722 0.6268 0.5746 0.2853 0.03541 0.02915 0.02239
66
+ 65/99 7.54G 0.04337 0.0192 0.01906 0.08163 15 768 0.4341 0.5795 0.5289 0.1987 0.04128 0.02902 0.02316
67
+ 66/99 7.54G 0.04128 0.01879 0.01742 0.07749 21 768 0.4445 0.6163 0.5647 0.2619 0.03696 0.02837 0.0229
68
+ 67/99 7.54G 0.03981 0.01887 0.01654 0.07521 21 768 0.429 0.6053 0.5656 0.2208 0.04042 0.02861 0.02219
69
+ 68/99 7.54G 0.04166 0.01899 0.01693 0.07757 21 768 0.4739 0.6238 0.5834 0.2604 0.0365 0.02828 0.02249
70
+ 69/99 7.54G 0.03884 0.01799 0.01664 0.07348 21 768 0.4778 0.6301 0.5943 0.2901 0.03459 0.02814 0.02168
71
+ 70/99 7.54G 0.03924 0.01881 0.01617 0.07422 16 768 0.4868 0.6183 0.5752 0.2273 0.03829 0.02862 0.02152
72
+ 71/99 7.54G 0.04033 0.01877 0.01641 0.0755 16 768 0.5021 0.6403 0.5829 0.2733 0.03533 0.02833 0.02202
73
+ 72/99 7.54G 0.0377 0.01832 0.01589 0.0719 21 768 0.5 0.6262 0.5632 0.2426 0.03893 0.02869 0.02321
74
+ 73/99 7.54G 0.04055 0.01906 0.01569 0.0753 21 768 0.5057 0.634 0.587 0.2631 0.03679 0.02905 0.02234
75
+ 74/99 7.54G 0.03823 0.01857 0.01563 0.07243 21 768 0.5011 0.628 0.593 0.2608 0.03783 0.02941 0.02249
76
+ 75/99 7.54G 0.03925 0.01877 0.01487 0.07289 21 768 0.5047 0.6283 0.5913 0.2768 0.0361 0.02957 0.0222
77
+ 76/99 7.54G 0.03875 0.01839 0.01479 0.07193 16 768 0.4463 0.599 0.5736 0.2188 0.0413 0.02932 0.02138
78
+ 77/99 7.54G 0.03812 0.01841 0.01509 0.07162 21 768 0.4509 0.6188 0.6065 0.2811 0.03619 0.02859 0.02097
79
+ 78/99 7.54G 0.03728 0.01822 0.01423 0.06973 21 768 0.4983 0.6299 0.6103 0.2777 0.0357 0.02857 0.02159
80
+ 79/99 7.54G 0.03603 0.01836 0.01353 0.06792 21 768 0.4991 0.6373 0.6051 0.2815 0.03612 0.02848 0.02253
81
+ 80/99 7.54G 0.03777 0.01839 0.01383 0.06999 21 768 0.513 0.6229 0.5937 0.263 0.03608 0.02879 0.02299
82
+ 81/99 7.54G 0.03728 0.01836 0.01279 0.06842 21 768 0.514 0.6186 0.5827 0.2583 0.03696 0.02926 0.02275
83
+ 82/99 7.54G 0.03713 0.01859 0.01255 0.06827 21 768 0.5083 0.6211 0.5856 0.2528 0.03705 0.02936 0.02303
84
+ 83/99 7.54G 0.03855 0.0181 0.01291 0.06957 21 768 0.4544 0.6131 0.6127 0.2791 0.0352 0.02904 0.02008
85
+ 84/99 7.54G 0.03822 0.01856 0.01275 0.06954 21 768 0.5615 0.6229 0.598 0.2552 0.0372 0.02896 0.02022
86
+ 85/99 7.54G 0.03704 0.01768 0.01312 0.06783 11 768 0.5044 0.6311 0.6029 0.2736 0.03652 0.02857 0.01919
87
+ 86/99 7.54G 0.03856 0.01819 0.01323 0.06998 16 768 0.5074 0.635 0.5867 0.2737 0.03738 0.0284 0.02141
88
+ 87/99 7.54G 0.03727 0.01812 0.01262 0.06801 21 768 0.499 0.6212 0.59 0.2568 0.03755 0.02851 0.01913
89
+ 88/99 7.54G 0.03734 0.01828 0.01226 0.06787 21 768 0.4615 0.6179 0.5588 0.2472 0.03796 0.02839 0.02268
90
+ 89/99 7.54G 0.03812 0.01831 0.01117 0.0676 21 768 0.4669 0.614 0.5726 0.2468 0.03694 0.02835 0.02175
91
+ 90/99 7.54G 0.0374 0.01776 0.01215 0.06732 21 768 0.4729 0.6058 0.5507 0.2349 0.03751 0.02848 0.02446
92
+ 91/99 7.54G 0.0367 0.018 0.01198 0.06668 21 768 0.4756 0.5957 0.5559 0.2342 0.03869 0.02888 0.02457
93
+ 92/99 7.54G 0.03697 0.01809 0.01169 0.06676 21 768 0.5018 0.5968 0.5633 0.2202 0.03954 0.02893 0.02166
94
+ 93/99 7.54G 0.03864 0.01795 0.01148 0.06806 21 768 0.5179 0.6163 0.5839 0.2593 0.03644 0.02868 0.02088
95
+ 94/99 7.54G 0.03675 0.01788 0.01188 0.06651 21 768 0.5553 0.6344 0.6026 0.2615 0.03735 0.02848 0.01651
96
+ 95/99 7.54G 0.03669 0.01775 0.01129 0.06573 21 768 0.4619 0.6223 0.6031 0.2763 0.03487 0.02825 0.01765
97
+ 96/99 7.54G 0.03597 0.01817 0.01073 0.06487 21 768 0.4948 0.6346 0.6214 0.2883 0.0345 0.02828 0.01605
98
+ 97/99 7.54G 0.03609 0.01831 0.01066 0.06506 21 768 0.6066 0.6309 0.5749 0.246 0.03606 0.02862 0.02427
99
+ 98/99 7.54G 0.03695 0.01775 0.01158 0.06628 21 768 0.5525 0.629 0.6034 0.2737 0.0357 0.02861 0.01991
100
+ 99/99 7.54G 0.03731 0.01756 0.0107 0.06556 21 768 0.5626 0.6219 0.5937 0.2479 0.03711 0.02889 0.02241
models/labels/latest/train_batch0.jpg ADDED
models/labels/latest/train_batch1.jpg ADDED
models/labels/latest/train_batch2.jpg ADDED
models/labels/yolov5s.yaml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # parameters
2
+ nc: 23 # number of classes
3
+ depth_multiple: 0.33 # model depth multiple
4
+ width_multiple: 0.50 # layer channel multiple
5
+
6
+ # anchors
7
+ anchors:
8
+ - [10,13, 16,30, 33,23] # P3/8
9
+ - [30,61, 62,45, 59,119] # P4/16
10
+ - [116,90, 156,198, 373,326] # P5/32
11
+
12
+ # YOLOv5 backbone
13
+ backbone:
14
+ # [from, number, module, args]
15
+ [[-1, 1, Focus, [64, 3]], # 0-P1/2
16
+ [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
17
+ [-1, 3, BottleneckCSP, [128]],
18
+ [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
19
+ [-1, 9, BottleneckCSP, [256]],
20
+ [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
21
+ [-1, 9, BottleneckCSP, [512]],
22
+ [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
23
+ [-1, 1, SPP, [1024, [5, 9, 13]]],
24
+ [-1, 3, BottleneckCSP, [1024, False]], # 9
25
+ ]
26
+
27
+ # YOLOv5 head
28
+ head:
29
+ [[-1, 1, Conv, [512, 1, 1]],
30
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
31
+ [[-1, 6], 1, Concat, [1]], # cat backbone P4
32
+ [-1, 3, BottleneckCSP, [512, False]], # 13
33
+
34
+ [-1, 1, Conv, [256, 1, 1]],
35
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
36
+ [[-1, 4], 1, Concat, [1]], # cat backbone P3
37
+ [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small)
38
+
39
+ [-1, 1, Conv, [256, 3, 2]],
40
+ [[-1, 14], 1, Concat, [1]], # cat head P4
41
+ [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium)
42
+
43
+ [-1, 1, Conv, [512, 3, 2]],
44
+ [[-1, 10], 1, Concat, [1]], # cat head P5
45
+ [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large)
46
+
47
+ [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
48
+ ]
models/symbols/format_dataset.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2020 Carleton University Biomedical Informatics Collaboratory
4
+
5
+ This source code is licensed under the MIT license found in the
6
+ LICENSE file in the root directory of this source tree.
7
+ """
8
+ from typing import List
9
+ from types import SimpleNamespace
10
+ import argparse, os, json, shutil
11
+ from tqdm import tqdm
12
+ import os.path as path
13
+ import numpy as np
14
+ from PIL import Image
15
+
16
+ # The different types of symbols that appear on audiograms
17
+ SYMBOL_CLASS_INDICES = {
18
+ "AIR_UNMASKED_LEFT": 0,
19
+ "AIR_UNMASKED_RIGHT": 1,
20
+ "AIR_MASKED_LEFT": 2,
21
+ "AIR_MASKED_RIGHT": 3,
22
+ "BONE_UNMASKED_LEFT": 4,
23
+ "BONE_UNMASKED_RIGHT": 5,
24
+ "BONE_MASKED_LEFT": 6,
25
+ "BONE_MASKED_RIGHT": 7,
26
+ }
27
+
28
+ def extract_audiograms(annotation: dict, image: Image) -> List[tuple]:
29
+ """Extracts the bounding boxes of audiograms into a tuple compatible
30
+ the YOLOv5 format.
31
+
32
+ Parameters
33
+ ----------
34
+ annotation : dict
35
+ A dictionary containing the annotations for the audiograms in a report.
36
+
37
+ image : Image
38
+ The image in PIL format corresponding to the annotation.
39
+
40
+ Returns
41
+ -------
42
+ tuple
43
+ A tuple of the form
44
+ (class index, x_center, y_center, width, height) where all coordinates
45
+ and dimensions are normalized to the width/height of the image.
46
+ """
47
+ audiogram_label_tuples = []
48
+ image_width, image_height = image.size
49
+ for audiogram in annotation:
50
+ bounding_box = audiogram["boundingBox"]
51
+ x_center = (bounding_box["x"] + bounding_box["width"] / 2) / image_width
52
+ y_center = (bounding_box["y"] + bounding_box["height"] / 2) / image_height
53
+ box_width = bounding_box["width"] / image_width
54
+ box_height = bounding_box["height"] / image_width
55
+ audiogram_label_tuples.append((0, x_center, y_center, box_width, box_height))
56
+ return audiogram_label_tuples
57
+
58
+ def extract_symbols(annotation: dict, image: Image) -> List[tuple]:
59
+ """Extracts the bounding boxes of the symbols into tuples
60
+ compatible the YOLOv5 format.
61
+
62
+ Parameters
63
+ ----------
64
+ annotation : dict
65
+ A dictionary containing the annotations for the audiograms in a report.
66
+
67
+ image: Image
68
+ The corresponding image.
69
+
70
+ Returns
71
+ -------
72
+ List[List[tuple]]
73
+ A list of lists of tuples, where the inner lists correspond to the different
74
+ audiograms that may appear in the report and the tuples are the symbols.
75
+ """
76
+ symbol_labels_tuples = []
77
+ for audiogram in annotation:
78
+ left = audiogram["boundingBox"]["x"]
79
+ top = audiogram["boundingBox"]["y"]
80
+ image_width = audiogram["boundingBox"]["width"]
81
+ image_height = audiogram["boundingBox"]["height"]
82
+ audiogram_labels = []
83
+ for symbol in audiogram["symbols"]:
84
+ bounding_box = symbol["boundingBox"]
85
+ x_center = (bounding_box["x"] - left + bounding_box["width"] / 2) / image_width
86
+ y_center = (bounding_box["y"] - top + bounding_box["height"] / 2) / image_height
87
+ box_width = bounding_box["width"] / image_width
88
+ box_height = bounding_box["height"] / image_width
89
+ audiogram_labels.append((SYMBOL_CLASS_INDICES[symbol["measurementType"]], x_center, y_center, box_width, box_height))
90
+ symbol_labels_tuples.append(audiogram_labels)
91
+ return symbol_labels_tuples
92
+
93
+ def create_yolov5_file(bboxes: List[tuple], filename: str):
94
+ # Turn the bounding boxes into a string with a bounding box
95
+ # on each line
96
+ file_content = "\n".join([
97
+ f"{bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} {bbox[4]}"
98
+ for bbox in bboxes
99
+ ])
100
+
101
+ # Save to a file
102
+ with open(filename, "w") as output_file:
103
+ output_file.write(file_content)
104
+
105
+ def create_directory_structure(data_dir: str):
106
+ try:
107
+ shutil.rmtree(path.join(data_dir))
108
+ except:
109
+ pass
110
+ os.mkdir(path.join(data_dir))
111
+ os.mkdir(path.join(data_dir, "images"))
112
+ os.mkdir(path.join(data_dir, "images", "train"))
113
+ os.mkdir(path.join(data_dir, "images", "validation"))
114
+ os.mkdir(path.join(data_dir, "labels"))
115
+ os.mkdir(path.join(data_dir, "labels", "train"))
116
+ os.mkdir(path.join(data_dir, "labels", "validation"))
117
+
118
+
119
+ def all_labels_valid(labels: List[tuple]):
120
+ for label in labels:
121
+ for value in label[1:]:
122
+ if value < 0 or value > 1:
123
+ return False
124
+ return True
125
+
126
+ def main(args: SimpleNamespace):
127
+ # Find all the JSON files in the input directory
128
+ report_ids = [
129
+ filename.rstrip(".json")
130
+ for filename in os.listdir(path.join(args.annotations_dir))
131
+ if filename.endswith(".json")
132
+ and path.exists(path.join(args.images_dir, filename.rstrip(".json") + ".jpg"))
133
+ ]
134
+
135
+ # Shuffle
136
+ np.random.seed(seed=42) # for reproducibility of the shuffle
137
+ np.random.shuffle(report_ids)
138
+
139
+ # Create the directory structure in which the images and annotations
140
+ # are to be stored
141
+ create_directory_structure(args.data_dir)
142
+
143
+ # Iterate through the report ids, extract the annotations in YOLOv5 format
144
+ # and place the file in the correct directory, and the image in the correct
145
+ # directory.
146
+ for i, report_id in enumerate(tqdm(report_ids)):
147
+ # Decide if the image is going into the training set or validation set
148
+ directory = (
149
+ "train" if i < args.train_frac * len(report_ids) else "validation"
150
+ )
151
+
152
+ # Load the annotation`
153
+ annotation_content = open(
154
+ path.join(args.annotations_dir, f"{report_id}.json")
155
+ )
156
+ annotation = json.load(annotation_content)
157
+
158
+ # Open the corresponding image to get its dimensions
159
+ image = Image.open(os.path.join(args.images_dir, f"{report_id}.jpg"))
160
+ width, height = image.size
161
+
162
+ # Audiogram labels
163
+ audiogram_labels = extract_audiograms(annotation, image)
164
+
165
+ if not all_labels_valid(audiogram_labels):
166
+ continue
167
+
168
+ # Symbol labels
169
+ symbol_labels = extract_symbols(annotation, image)
170
+ for i, plot_symbols in enumerate(symbol_labels):
171
+ if not all_labels_valid(plot_symbols):
172
+ continue
173
+ x1 = annotation[i]["boundingBox"]["x"]
174
+ y1 = annotation[i]["boundingBox"]["y"]
175
+ x2 = annotation[i]["boundingBox"]["x"] + annotation[i]["boundingBox"]["width"]
176
+ y2 = annotation[i]["boundingBox"]["y"] + annotation[i]["boundingBox"]["height"]
177
+ create_yolov5_file(
178
+ plot_symbols,
179
+ path.join(args.data_dir, "labels", directory, f"{report_id}_{i+1}.txt")
180
+ )
181
+ image.crop((x1, y1, x2, y2)).save(
182
+ path.join(args.data_dir, "images", directory, f"{report_id}_{i+1}.jpg")
183
+ )
184
+
185
+ if __name__ == "__main__":
186
+ import argparse
187
+
188
+ parser = argparse.ArgumentParser(description=(
189
+ "Script that formats the training set for transfer learning via "
190
+ "the YOLOv5 model."
191
+ ))
192
+ parser.add_argument("-d", "--data_dir", type=str, required=True, help=(
193
+ "Path to the directory containing the data. It should have 3 "
194
+ "subfolders named `images`, `annotations` and `labels`."
195
+ ))
196
+ parser.add_argument("-a", "--annotations_dir", type=str, required=True, help=(
197
+ "Path to the directory containing the annotations in the JSON format."
198
+ ))
199
+ parser.add_argument("-i", "--images_dir", type=str, required=True, help=(
200
+ "Path to the directory containing the images."
201
+ ))
202
+ parser.add_argument("-f", "--train_frac", type=float, required=True, help=(
203
+ "Fraction of images to be used for training. (e.g. 0.8)"
204
+ ))
205
+ args = parser.parse_args()
206
+
207
+ main(args)
208
+
models/symbols/latest/hyp.yaml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lr0: 0.01
2
+ lrf: 0.2
3
+ momentum: 0.937
4
+ weight_decay: 0.0005
5
+ giou: 0.05
6
+ cls: 0.5
7
+ cls_pw: 1.0
8
+ obj: 1.0
9
+ obj_pw: 1.0
10
+ iou_t: 0.2
11
+ anchor_t: 4.0
12
+ fl_gamma: 0.0
13
+ hsv_h: 0.015
14
+ hsv_s: 0.7
15
+ hsv_v: 0.4
16
+ degrees: 0.0
17
+ translate: 0.1
18
+ scale: 0.5
19
+ shear: 0.0
20
+ perspective: 0.0
21
+ flipud: 0.0
22
+ fliplr: 0.5
23
+ mixup: 0.0
models/symbols/latest/labels.png ADDED
models/symbols/latest/opt.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ weights: yolov5/weights/yolov5s.pt
2
+ cfg: yolov5/models/yolov5s.yaml
3
+ data: symbol_detection.yaml
4
+ hyp: ./yolov5/data/hyp.scratch.yaml
5
+ epochs: 50
6
+ batch_size: 16
7
+ img_size:
8
+ - 1024
9
+ - 1024
10
+ rect: true
11
+ resume: false
12
+ nosave: false
13
+ notest: false
14
+ noautoanchor: false
15
+ evolve: false
16
+ bucket: ''
17
+ cache_images: false
18
+ image_weights: false
19
+ name: ''
20
+ device: '0'
21
+ multi_scale: false
22
+ single_cls: false
23
+ adam: false
24
+ sync_bn: false
25
+ local_rank: -1
26
+ logdir: runs/
27
+ workers: 8
28
+ total_batch_size: 16
29
+ world_size: 1
30
+ global_rank: -1
models/symbols/latest/results.png ADDED
models/symbols/latest/results.txt ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0/49 7.49G 0.07765 0.05224 0.04646 0.1763 7 576 0.04767 0.2132 0.1067 0.0261 0.0709 0.04132 0.03329
2
+ 1/49 7.53G 0.0574 0.03939 0.03081 0.1276 7 576 0.1938 0.3598 0.2717 0.06994 0.05514 0.03799 0.02433
3
+ 2/49 7.53G 0.05059 0.03685 0.02426 0.1117 7 576 0.2269 0.3609 0.2415 0.05457 0.05992 0.03802 0.0213
4
+ 3/49 7.53G 0.04991 0.03578 0.02254 0.1082 7 576 0.2634 0.3865 0.2704 0.0603 0.06173 0.03594 0.02003
5
+ 4/49 7.53G 0.04831 0.03514 0.02124 0.1047 6 576 0.2995 0.5582 0.3703 0.107 0.05316 0.03657 0.01871
6
+ 5/49 7.53G 0.04502 0.03474 0.01945 0.09921 7 576 0.2936 0.5627 0.3964 0.11 0.05526 0.03665 0.01701
7
+ 6/49 7.53G 0.04389 0.03427 0.01799 0.09615 7 576 0.2852 0.5298 0.3742 0.1009 0.0553 0.0355 0.01604
8
+ 7/49 7.53G 0.04235 0.03403 0.01705 0.09343 7 576 0.2816 0.4995 0.3463 0.08569 0.05838 0.03602 0.01547
9
+ 8/49 7.53G 0.0421 0.03383 0.01663 0.09256 7 576 0.4374 0.5792 0.4381 0.1241 0.05197 0.03575 0.01499
10
+ 9/49 7.53G 0.04017 0.03327 0.01569 0.08912 7 576 0.4495 0.6056 0.4835 0.141 0.05317 0.03561 0.01422
11
+ 10/49 7.53G 0.03991 0.0332 0.01515 0.08826 7 576 0.5538 0.5598 0.4569 0.1217 0.05581 0.03582 0.01345
12
+ 11/49 7.53G 0.03979 0.03298 0.01464 0.08741 7 576 0.4092 0.633 0.5494 0.1704 0.05083 0.03614 0.01397
13
+ 12/49 7.53G 0.03885 0.03274 0.01418 0.08577 7 576 0.4398 0.6878 0.5847 0.1786 0.05148 0.03602 0.01281
14
+ 13/49 7.53G 0.03938 0.0328 0.01332 0.0855 7 576 0.4823 0.7566 0.6708 0.2135 0.05016 0.03501 0.01239
15
+ 14/49 7.53G 0.03869 0.0325 0.0126 0.08379 7 576 0.5036 0.7224 0.6623 0.2015 0.05224 0.03578 0.01118
16
+ 15/49 7.53G 0.03856 0.03249 0.0121 0.08314 7 576 0.4471 0.7152 0.6042 0.1691 0.05259 0.03617 0.01143
17
+ 16/49 7.53G 0.03844 0.03263 0.01164 0.08271 7 576 0.4748 0.7556 0.6721 0.2094 0.0505 0.03606 0.01165
18
+ 17/49 7.53G 0.03824 0.03244 0.01134 0.08202 7 576 0.4454 0.8108 0.6782 0.2079 0.05038 0.03572 0.01091
19
+ 18/49 7.53G 0.0378 0.03225 0.01097 0.08103 7 576 0.4437 0.7971 0.6736 0.1934 0.052 0.03567 0.01057
20
+ 19/49 7.53G 0.03797 0.03225 0.01065 0.08087 7 576 0.437 0.7995 0.6595 0.202 0.04957 0.03569 0.01094
21
+ 20/49 7.53G 0.03772 0.03216 0.01053 0.08041 7 576 0.4631 0.8147 0.713 0.2205 0.04931 0.03605 0.01022
22
+ 21/49 7.53G 0.03769 0.03196 0.01036 0.08001 7 576 0.4799 0.8187 0.7363 0.2399 0.04898 0.03545 0.0103
23
+ 22/49 7.53G 0.03721 0.0318 0.01005 0.07905 7 576 0.4458 0.8343 0.7107 0.2057 0.05059 0.03609 0.009893
24
+ 23/49 7.53G 0.0373 0.03188 0.01003 0.0792 7 576 0.462 0.8368 0.7502 0.2398 0.04766 0.03542 0.01006
25
+ 24/49 7.53G 0.03706 0.03175 0.009794 0.07861 7 576 0.4662 0.848 0.7574 0.2366 0.04742 0.03576 0.01004
26
+ 25/49 7.53G 0.037 0.03154 0.009702 0.07824 7 576 0.4671 0.8519 0.7534 0.2438 0.04799 0.03574 0.00978
27
+ 26/49 7.53G 0.03664 0.03155 0.009522 0.07771 7 576 0.4553 0.8399 0.7258 0.2114 0.04911 0.03574 0.009557
28
+ 27/49 7.53G 0.03663 0.03134 0.009434 0.07741 6 576 0.4576 0.8515 0.7464 0.2495 0.046 0.03549 0.01006
29
+ 28/49 7.53G 0.03649 0.03125 0.009326 0.07707 7 576 0.4592 0.8557 0.7699 0.2423 0.04658 0.03556 0.009594
30
+ 29/49 7.53G 0.03631 0.03119 0.009154 0.07665 7 576 0.4649 0.8459 0.7688 0.2489 0.04703 0.03547 0.009588
31
+ 30/49 7.53G 0.03638 0.03128 0.009051 0.07672 7 576 0.4404 0.8472 0.7481 0.2368 0.04745 0.03581 0.01008
32
+ 31/49 7.53G 0.03626 0.03109 0.009029 0.07638 5 576 0.4682 0.8503 0.7648 0.2715 0.0447 0.03521 0.009697
33
+ 32/49 7.53G 0.03619 0.03078 0.008949 0.07592 7 576 0.4727 0.8656 0.7748 0.2627 0.04556 0.03557 0.009528
34
+ 33/49 7.53G 0.03582 0.03089 0.008738 0.07544 7 576 0.4715 0.8527 0.7524 0.2595 0.04565 0.03552 0.009686
35
+ 34/49 7.53G 0.03597 0.03074 0.008762 0.07547 6 576 0.4748 0.8733 0.7652 0.2553 0.04547 0.03571 0.009658
36
+ 35/49 7.53G 0.03585 0.03071 0.00865 0.07521 7 576 0.5028 0.8653 0.7807 0.2915 0.04359 0.03525 0.009554
37
+ 36/49 7.53G 0.03551 0.03053 0.008564 0.07461 6 576 0.4858 0.8738 0.7824 0.2899 0.04349 0.0353 0.009391
38
+ 37/49 7.53G 0.03535 0.03063 0.008459 0.07443 7 576 0.4847 0.8664 0.7892 0.2833 0.04479 0.03553 0.009333
39
+ 38/49 7.53G 0.03561 0.03047 0.008462 0.07454 7 576 0.4791 0.8654 0.7652 0.2747 0.04421 0.03549 0.009689
40
+ 39/49 7.53G 0.03544 0.03056 0.008445 0.07445 7 576 0.4997 0.8772 0.7985 0.3108 0.04193 0.0351 0.009489
41
+ 40/49 7.53G 0.03495 0.03029 0.008376 0.07361 7 576 0.4959 0.8783 0.8073 0.3197 0.04251 0.03527 0.009663
42
+ 41/49 7.53G 0.03491 0.03027 0.008229 0.0734 6 576 0.4818 0.8804 0.7962 0.3107 0.043 0.03533 0.009612
43
+ 42/49 7.53G 0.03491 0.03008 0.008287 0.07328 7 576 0.5063 0.8718 0.8105 0.3125 0.0428 0.03537 0.009683
44
+ 43/49 7.53G 0.03472 0.03013 0.008238 0.07309 6 576 0.5203 0.8777 0.81 0.3442 0.04114 0.03507 0.009757
45
+ 44/49 7.53G 0.03416 0.02998 0.008148 0.07229 7 576 0.4981 0.8905 0.8147 0.3268 0.04171 0.03498 0.009373
46
+ 45/49 7.53G 0.03424 0.03003 0.008063 0.07233 7 576 0.5018 0.8824 0.8067 0.3253 0.04174 0.03512 0.009696
47
+ 46/49 7.53G 0.03433 0.02986 0.008007 0.07219 7 576 0.5216 0.8748 0.8155 0.3331 0.04193 0.03524 0.009595
48
+ 47/49 7.53G 0.03398 0.0299 0.00801 0.07189 7 576 0.5127 0.8726 0.8061 0.3483 0.04056 0.03489 0.009472
49
+ 48/49 7.53G 0.03383 0.02974 0.008026 0.0716 7 576 0.5021 0.8772 0.8126 0.3428 0.04086 0.035 0.009722
50
+ 49/49 7.53G 0.03385 0.02983 0.00794 0.07161 6 576 0.5133 0.878 0.8072 0.3366 0.04112 0.03492 0.009682
models/symbols/latest/test_batch0_gt.jpg ADDED
models/symbols/latest/test_batch0_pred.jpg ADDED
models/symbols/latest/train_batch0.jpg ADDED
models/symbols/latest/train_batch1.jpg ADDED
models/symbols/latest/train_batch2.jpg ADDED
models/symbols/symbols_detection.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ train: models/symbols/dataset/images/train/
3
+ val: models/symbols/dataset/images/validation/
4
+
5
+ # number of classes
6
+ nc: 8
7
+
8
+ # class names
9
+ names: [
10
+ "AIR_UNMASKED_LEFT",
11
+ "AIR_UNMASKED_RIGHT",
12
+ "AIR_MASKED_LEFT",
13
+ "AIR_MASKED_RIGHT",
14
+ "BONE_UNMASKED_LEFT",
15
+ "BONE_UNMASKED_RIGHT",
16
+ "BONE_MASKED_LEFT",
17
+ "BONE_MASKED_RIGHT"
18
+ ]
models/symbols/symbols_detection_test.yaml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
2
+ val: data/symbols/test/images/
3
+
4
+ # number of classes
5
+ nc: 8
6
+
7
+ # class names
8
+ names: [
9
+ "AIR_UNMASKED_LEFT",
10
+ "AIR_UNMASKED_RIGHT",
11
+ "AIR_MASKED_LEFT",
12
+ "AIR_MASKED_RIGHT",
13
+ "BONE_UNMASKED_LEFT",
14
+ "BONE_UNMASKED_RIGHT",
15
+ "BONE_MASKED_LEFT",
16
+ "BONE_MASKED_RIGHT"
17
+ ]
models/symbols/yolov5s.yaml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # parameters
2
+ nc: 8 # number of classes
3
+ depth_multiple: 0.33 # model depth multiple
4
+ width_multiple: 0.50 # layer channel multiple
5
+
6
+ # anchors
7
+ anchors:
8
+ - [10,13, 16,30, 33,23] # P3/8
9
+ - [30,61, 62,45, 59,119] # P4/16
10
+ - [116,90, 156,198, 373,326] # P5/32
11
+
12
+ # YOLOv5 backbone
13
+ backbone:
14
+ # [from, number, module, args]
15
+ [[-1, 1, Focus, [64, 3]], # 0-P1/2
16
+ [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
17
+ [-1, 3, BottleneckCSP, [128]],
18
+ [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
19
+ [-1, 9, BottleneckCSP, [256]],
20
+ [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
21
+ [-1, 9, BottleneckCSP, [512]],
22
+ [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
23
+ [-1, 1, SPP, [1024, [5, 9, 13]]],
24
+ [-1, 3, BottleneckCSP, [1024, False]], # 9
25
+ ]
26
+
27
+ # YOLOv5 head
28
+ head:
29
+ [[-1, 1, Conv, [512, 1, 1]],
30
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
31
+ [[-1, 6], 1, Concat, [1]], # cat backbone P4
32
+ [-1, 3, BottleneckCSP, [512, False]], # 13
33
+
34
+ [-1, 1, Conv, [256, 1, 1]],
35
+ [-1, 1, nn.Upsample, [None, 2, 'nearest']],
36
+ [[-1, 4], 1, Concat, [1]], # cat backbone P3
37
+ [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small)
38
+
39
+ [-1, 1, Conv, [256, 3, 2]],
40
+ [[-1, 14], 1, Concat, [1]], # cat head P4
41
+ [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium)
42
+
43
+ [-1, 1, Conv, [512, 3, 2]],
44
+ [[-1, 10], 1, Concat, [1]], # cat head P5
45
+ [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large)
46
+
47
+ [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
48
+ ]
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ matplotlib
2
+ pandas
3
+ Pillow
4
+ pytesseract
5
+ PyYAML
6
+ requests
7
+ scipy
8
+ toml
9
+ torch==1.9.0
10
+ torchvision==0.10.0
11
+ tqdm
12
+ typed-ast
13
+ typing-extensions
14
+ opencv-python
15
+ gradio
src/__pycache__/interfaces.cpython-38.pyc ADDED
Binary file (4.9 kB). View file
 
src/digitize_report.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2020, Carleton University Biomedical Informatics Collaboratory
4
+
5
+ This source code is licensed under the MIT license found in the
6
+ LICENSE file in the root directory of this source tree.
7
+ """
8
+
9
+ import json
10
+ import os
11
+
12
+ from tqdm import tqdm
13
+
14
+ from digitizer.digitization import generate_partial_annotation, extract_thresholds
15
+
16
+ if __name__ == "__main__":
17
+ import argparse
18
+ parser = argparse.ArgumentParser(description=("Digitization script "
19
+ "for the conversion of audiology reports to JSON documents."))
20
+ parser.add_argument("-i", "--input", type=str, required=True,
21
+ help=("Path to the audiology report (or directory) to be digitized."))
22
+ parser.add_argument("-o", "--output_dir", type=str, required=False,
23
+ help="Path to the directory in which the result is to be saved (file will have same base name as input file, but with .json extension). If not provided, result is printed to the console.")
24
+ parser.add_argument("-a", "--annotation_mode", action="store_true",
25
+ help="Whether the script should be run in `annotation mode`, i.e. return results similar in format to those of a human-made annotation. If not given, a list of thresholds is computed.")
26
+ parser.add_argument("-g", "--gpu", action="store_true",
27
+ help="Use the GPU.")
28
+ args = parser.parse_args()
29
+
30
+ input_files = []
31
+ if os.path.isfile(args.input):
32
+ input_files += [os.path.abspath(args.input)]
33
+ else:
34
+ input_files += [os.path.join(args.input, filename) for filename in os.listdir(args.input)]
35
+
36
+ with tqdm(total=len(input_files)) as pbar:
37
+ for input_file in input_files:
38
+ pbar.set_description(f"{os.path.basename(input_file)}")
39
+
40
+ result = None
41
+ if args.annotation_mode:
42
+ result = generate_partial_annotation(input_file, gpu=args.gpu)
43
+ else:
44
+ result = extract_thresholds(input_file, gpu=args.gpu)
45
+
46
+ result_as_string = json.dumps(result, indent=4, separators=(',', ': '))
47
+
48
+ if args.output_dir:
49
+ predictions_filename = os.path.basename(input_file).split(".")[0] + ".json"
50
+ with open(os.path.join(args.output_dir, predictions_filename), "w") as ofile:
51
+ ofile.write(result_as_string)
52
+ else:
53
+ print(result_as_string)
54
+
55
+ pbar.update(1) # increment the progress bar
src/digitizer/__pycache__/digitization.cpython-38.pyc ADDED
Binary file (13.6 kB). View file
 
src/digitizer/assets/fonts/arial.ttf ADDED
Binary file (773 kB). View file
 
src/digitizer/assets/symbols/left_air_masked.png ADDED
src/digitizer/assets/symbols/left_air_masked.svg ADDED
src/digitizer/assets/symbols/left_air_unmasked.png ADDED