Skip to content

Commit 6970f98

Browse files
committed
Refactor dataset preparation
1 parent 31126d6 commit 6970f98

10 files changed

Lines changed: 98 additions & 101 deletions

File tree

abraia/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from dotenv import load_dotenv
33
load_dotenv()
44

5-
__version__ = '0.25.15'
5+
__version__ = '0.26.0'
66

77
from . import config
88
from .client import Abraia, APIError

abraia/client.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,15 @@ def move_file(self, old_path, new_path):
8989
resp = resp.json()
9090
return file_path(resp['file']['source'], self.userid)
9191

92-
def download_file(self, path, dest='', cache=False):
93-
# TODO: Remove BytesIO download option
92+
def download_file(self, path, dest, cache=False):
9493
url = f"{API_URL}/files/{self.userid}/{path}"
95-
if cache and dest == '':
96-
dest = temporal_src(path)
97-
if os.path.exists(dest):
98-
return dest
94+
if cache and os.path.exists(dest):
95+
return dest
9996
resp = requests.get(url, stream=True, auth=self.auth)
10097
if resp.status_code != 200:
10198
raise APIError(resp.text, resp.status_code)
102-
if dest:
103-
save_data(dest, resp.content)
104-
return dest
105-
return BytesIO(resp.content)
99+
save_data(dest, resp.content)
100+
return dest
106101

107102
def remove_file(self, path):
108103
url = f"{API_URL}/files/{self.userid}/{path}"
@@ -141,11 +136,14 @@ def transform_image(self, path, dest, params={'quality': 'auto'}):
141136
save_data(dest, resp.content)
142137

143138
def load_file(self, path):
144-
stream = self.download_file(path)
139+
dest = temporal_src(path)
140+
self.download_file(path, dest, cache=True)
145141
try:
146-
return stream.getvalue().decode('utf-8')
142+
with open(dest, 'r') as f:
143+
return f.read()
147144
except:
148-
return stream
145+
with open(dest, 'rb') as f:
146+
return BytesIO(f.read())
149147

150148
def save_file(self, path, stream):
151149
stream = BytesIO(bytes(stream, 'utf-8')) if isinstance(stream, str) else stream
@@ -158,7 +156,8 @@ def save_json(self, path, values):
158156
return self.save_file(path, json.dumps(values))
159157

160158
def load_image(self, path):
161-
return Image.open(self.download_file(path))
159+
dest = temporal_src(path)
160+
return Image.open(self.download_file(path, dest, cache=True))
162161

163162
def save_image(self, path, im):
164163
src = temporal_src(path)

abraia/demo.py

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,39 @@
1616
DEMOS = {
1717
'apple': {
1818
'src': '5479199-hd_1920_1080_25fps.mp4',
19-
'label': 'apple'
19+
'labels': ['apple']
2020
},
2121
'strawberry': {
2222
'model': 'multiple/strawberry/yolov8n.onnx',
2323
'src': '9710983-hd_1920_1080_30fps.mp4',
24-
'label': 'strawberry'
24+
'labels': ['strawberry']
2525
},
2626
'grapes': {
2727
'model': 'multiple/grapes/yolov8n.onnx',
2828
'src': '5658544-hd_1366_720_24fps.mp4',
29-
'label': 'grapes'
29+
'labels': ['grapes']
3030
},
3131
'tomato': {
3232
'model': 'multiple/tomato/yolov8n_v6.onnx',
3333
'src': '10179855-hd_1920_1080_30fps.mp4',
34-
'label': 'tomato',
35-
'line': [(1440, 0), (1440, 1080)]
34+
'labels': ['tomato'],
35+
'counter': [(1440, 0), (1440, 1080)]
3636
},
3737
'people': {
3838
'src': '853889-hd_1920_1080_25fps.mp4',
39-
'label': 'person',
40-
'line': [(0, 650), (1920, 650)],
39+
'labels': ['person'],
40+
'counter': [(0, 650), (1920, 650)],
4141
'region': [(0, 600), (1920, 600), (1920, 700), (0, 700)]
4242
},
4343
'queue': {
4444
'src': '4775505-hd_1920_1080_30fps.mp4',
45-
'label': 'person',
45+
'labels': ['person'],
4646
'timer': [(10, 600), (1690, 600), (1690, 700), (10, 700)]
4747
},
4848
'escalator': {
4949
'src': '14393755-hd_1920_1080_30fps.mp4',
50-
'label': 'person',
51-
'line': [(950, 670), (270, 895)],
50+
'labels': ['person'],
51+
'counter': [(950, 670), (270, 895)],
5252
'region': [[0, 245], [350, 1080], [1200, 1080], [530, 0], [0, 0]],
5353
'timer': [[0, 245], [350, 1080], [1200, 1080], [530, 0], [0, 0]]
5454
}
@@ -67,32 +67,27 @@ def monitor_objects(src=None, demo='detect', resolution=(1280, 720)):
6767
tracker = Tracker(frame_rate=video.frame_rate)
6868
model = Model(selected.get('model', 'multiple/models/yolov8n.onnx'))
6969

70-
line_counter = LineCounter(selected['line']) if selected.get('line') else None
70+
line_counter = LineCounter(selected['counter']) if selected.get('counter') else None
7171
region_filter = RegionFilter(selected['region']) if selected.get('region') else None
7272
region_timer = RegionTimer(selected['timer']) if selected.get('timer') else None
7373

74-
labels = [selected.get('label')] if selected.get('label') else None
74+
labels = selected.get('labels')
7575
for k, frame in enumerate(video):
7676
frame_time = round(k / video.frame_rate, 2)
7777
t0 = time.time()
7878
results = model.run(frame, labels=labels)
79-
8079
if region_filter:
8180
results, _ = region_filter.update(results)
8281
results = tracker.update(results)
83-
8482
out = frame.copy()
8583
if line_counter:
8684
in_count, out_count = line_counter.update(results)
87-
out = render_counter(out, line_counter.line, f"In: {in_count} | Out: {out_count}" if demo in ['people', 'escalator'] else f"Count: {out_count}")
88-
85+
out = render_counter(out, line_counter.line, f"In: {in_count} | Out: {out_count}")
8986
if region_timer:
9087
in_objects, out_objects = region_timer.update(results, frame_time)
9188
out = render_region(out, region_timer.region, f"Count: {len(in_objects)}", color=(255, 255, 0))
92-
out = render_results(out, in_objects)
93-
else:
94-
out = render_results(out, results)
95-
89+
results = in_objects
90+
out = render_results(out, results)
9691
print(f"#{k} {round((time.time() - t0) * 1000, 1)}ms {count_objects(results)}")
9792
video.show(out)
9893

abraia/multiple/__init__.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ def __init__(self, folder=''):
139139
super(Multiple, self).__init__()
140140

141141
def load_header(self, path):
142-
dest = self.download_file(path, cache=True)
142+
dest = temporal_src(path)
143+
self.download_file(path, dest, cache=True)
143144
return spectral.io.envi.read_envi_header(dest)
144145

145146
def load_metadata(self, path):
@@ -148,14 +149,18 @@ def load_metadata(self, path):
148149
return super(Multiple, self).load_metadata(path)
149150

150151
def load_envi(self, path):
151-
dest = self.download_file(path, cache=True)
152-
raw = self.download_file(f"{path.split('.')[0]}.raw", cache=True)
153-
return np.array(spectral.io.envi.open(dest, raw)[:, :, :])
152+
dest = temporal_src(path)
153+
self.download_file(path, dest, cache=True)
154+
raw_path = f"{path.split('.')[0]}.raw"
155+
raw_dest = temporal_src(raw_path)
156+
self.download_file(raw_path, raw_dest, cache=True)
157+
return np.array(spectral.io.envi.open(dest, raw_dest)[:, :, :])
154158

155159
def load_image(self, path):
156160
if path.lower().endswith('.hdr'):
157161
return self.load_envi(path)
158-
return load_image(self.download_file(path, cache=True))
162+
dest = temporal_src(path)
163+
return load_image(self.download_file(path, dest, cache=True))
159164

160165
def save_envi(self, path, img, metadata={}):
161166
src = temporal_src(path)

abraia/training/__init__.py

Lines changed: 41 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,47 @@
66
from typing import Dict, Any
77
from tqdm.contrib.concurrent import process_map
88

9+
from ..utils import save_text
910
from .dataset import list_datasets, load_dataset, search_images, list_models, download_file
10-
from ..utils import make_dirs
1111

1212

1313
def load_tasks(task):
14-
tasks = ['classify', 'detect', 'segment']
1514
if task:
16-
idx = tasks.index(task)
17-
return tasks[:idx+1]
15+
tasks = ['classify', 'detect', 'segment']
16+
return tasks[:tasks.index(task)+1]
1817
return []
1918

2019

2120
def save_annotation(annotation, folder, classes, task):
21+
im = Image.open(os.path.join(folder, 'images', annotation['filename']))
22+
label_lines = []
23+
for object in annotation.get('objects', []):
24+
label, box, polygon = object.get('label'), object.get('box'), object.get('polygon')
25+
if task == 'segment':
26+
if polygon:
27+
label_line = f"{classes.index(label)} " + ' '.join([f"{point[0] / im.width} {point[1] / im.height}" for point in polygon])
28+
label_lines.append(label_line)
29+
elif task == 'detect':
30+
if polygon:
31+
xx, yy = [point[0] for point in polygon], [point[1] for point in polygon]
32+
x1, y1, x2, y2 = min(xx), min(yy), max(xx), max(yy)
33+
box = [x1, y1, x2 - x1, y2 - y1]
34+
if box:
35+
label_line = f"{classes.index(label)} {(box[0] + box[2] / 2) / im.width} {(box[1] + box[3] / 2) / im.height} {box[2] / im.width} {box[3] / im.height}"
36+
label_lines.append(label_line)
37+
label_path = os.path.join(folder, 'labels', f"{os.path.splitext(annotation['filename'])[0]}.txt")
38+
save_text(label_path, '\n'.join(label_lines))
39+
40+
41+
def save_data(annotation, folder, classes, task):
42+
path = annotation['path']
43+
dest = folder if task == 'classify' else os.path.join(folder, 'images')
2244
if task == 'classify':
23-
for object in annotation.get('objects', []):
24-
label = object.get('label')
25-
if label:
26-
src = os.path.join(folder, annotation['filename'])
27-
dest = os.path.join(folder, label, annotation['filename'])
28-
shutil.move(src, dest)
29-
break
30-
else:
31-
im = Image.open(os.path.join(folder, 'images', annotation['filename']))
32-
label_lines = []
33-
for object in annotation.get('objects', []):
34-
label, box, polygon = object.get('label'), object.get('box'), object.get('polygon')
35-
# Convert polygon or box to yolo format
36-
if task == 'segment':
37-
if polygon:
38-
label_line = f"{classes.index(label)} " + ' '.join([f"{point[0] / im.width} {point[1] / im.height}" for point in polygon])
39-
label_lines.append(label_line)
40-
elif task == 'detect':
41-
if polygon:
42-
xx, yy = [point[0] for point in polygon], [point[1] for point in polygon]
43-
x1, y1, x2, y2 = min(xx), min(yy), max(xx), max(yy)
44-
box = [x1, y1, x2 - x1, y2 - y1]
45-
if box:
46-
label_line = f"{classes.index(label)} {(box[0] + box[2] / 2) / im.width} {(box[1] + box[3] / 2) / im.height} {box[2] / im.width} {box[3] / im.height}"
47-
label_lines.append(label_line)
48-
label_path = os.path.join(folder, 'labels', f"{os.path.splitext(annotation['filename'])[0]}.txt")
49-
with open(label_path, 'w') as f:
50-
f.write('\n'.join(label_lines))
51-
52-
53-
def save_data(annotations, folder, classes, task):
54-
if (task == 'classify'):
55-
make_dirs(os.path.join(folder, ''))
56-
paths = [annotation['path'] for annotation in annotations]
57-
process_map(download_file, paths, itertools.repeat(folder), max_workers=5)
58-
for label in classes:
59-
make_dirs(os.path.join(folder, label, ''))
60-
for annotation in annotations:
61-
save_annotation(annotation, folder, classes, task)
62-
else:
63-
make_dirs(os.path.join(folder, 'images', ''))
64-
paths = [annotation['path'] for annotation in annotations]
65-
process_map(download_file, paths, itertools.repeat(os.path.join(folder, 'images')), max_workers=5)
66-
make_dirs(os.path.join(folder, 'labels', ''))
67-
for annotation in annotations:
68-
save_annotation(annotation, folder, classes, task)
45+
label = next((obj.get('label', '') for obj in annotation.get('objects', [])), '')
46+
dest = os.path.join(dest, label)
47+
download_file(path, dest)
48+
if task != 'classify':
49+
save_annotation(annotation, folder, classes, task)
6950

7051

7152
def save_config(dataset, classes):
@@ -76,17 +57,20 @@ def save_config(dataset, classes):
7657
names: {classes}
7758
'''
7859
path = os.path.join(dataset, 'data.yaml')
79-
with open(path, 'w') as f:
80-
f.write(yaml_content)
60+
save_text(path, yaml_content)
8161

8262

8363
def prepare_dataset(dataset, force = False):
8464
if force or not os.path.exists(dataset.project):
85-
train, val, test = dataset.split()
86-
data_annotations = {'train': train, 'val': val, 'test': test}
87-
for x in ['train', 'val', 'test']:
88-
save_data(data_annotations[x], f"{dataset.project}/{x}", dataset.classes, dataset.task)
89-
save_config(dataset.project, dataset.classes)
65+
splits = list(zip(['train', 'val', 'test'], dataset.split()))
66+
all_annotations, all_folders = [], []
67+
for x, annotations in splits:
68+
folder = os.path.join(dataset.project, x)
69+
all_annotations.extend(annotations)
70+
all_folders.extend([folder] * len(annotations))
71+
process_map(save_data, all_annotations, all_folders, itertools.repeat(dataset.classes), itertools.repeat(dataset.task), max_workers=5, chunksize=1, desc="Downloading images")
72+
if dataset.task != 'classify':
73+
save_config(dataset.project, dataset.classes)
9074

9175

9276
class ModelTrainer:

abraia/training/detect.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import os
44
import io
5+
import shutil
56
import contextlib
6-
import numpy as np
77
#os.environ['YOLO_VERBOSE'] = 'False'
88

99
from ultralytics import YOLO
@@ -61,7 +61,8 @@ def save(self, project, classes, device='cpu', half=False):
6161
out = io.StringIO()
6262
with contextlib.redirect_stdout(out):
6363
model_src = self.model.export(format="onnx", device=device, opset=11, half=half)
64-
abraia.upload_file(model_src, f"{project}/{self.model_name}.onnx")
64+
shutil.copy(model_src, f"{self.model_name}.onnx")
65+
abraia.upload_file(f"{self.model_name}.onnx", f"{project}/{self.model_name}.onnx")
6566
abraia.save_json(f"{project}/{self.model_name}.json",
6667
{'task': self.task, 'inputShape': [1, 3, self.imgsz, self.imgsz],
6768
'classes': classes, 'metrics': self.metrics})
@@ -83,6 +84,7 @@ def run(self, img):
8384
return objects
8485

8586
def compile(self, project, classes, device='hailo8'):
87+
abraia.download_file(f"{project}/{self.model_name}.onnx", f"{self.model_name}.onnx")
8688
print("Compile model for edge deployment to hailo hef format...")
8789
print(f"hailomz compile yolov8n --ckpt yolov8n.onnx --calib-path {project}/train/images --classes {len(classes)} --hw-arch {device} --performance")
8890

abraia/utils/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ def save_json(data, dest, gz=False):
122122
return dest
123123

124124

125+
def load_text(src, gz=False):
126+
with gzip.open(src, 'rt') if gz else open(src, 'r') as f:
127+
return f.read()
128+
129+
130+
def save_text(dest, text, gz=False):
131+
make_dirs(dest)
132+
with gzip.open(dest, 'wt') if gz else open(dest, 'w') as f:
133+
f.write(text)
134+
return dest
135+
136+
125137
def load_data(src, gz=False):
126138
with gzip.open(src, 'rb') if gz else open(src, 'rb') as f:
127139
return f.read()

images/screenshot.jpg

841 Bytes
Loading

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ click>=8.0.3
22
numpy>=1.21.0
33
Pillow>=8.0.0
44
filetype>=1.0.7
5-
onnxruntime==1.18.0
5+
onnxruntime>=1.18.0
66
opencv-python-headless>=4.10.0.84
77
python-dotenv>=0.21.1
88
requests>=2.27.1

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
extras_require = {
1616
'multiple': ['spectral>=0.23.1', 'scipy>=1.14.1', 'tifffile>=2024.8.30'],
17-
'dev': ['opencv-python>=4.10.0.84', 'ImageHash>=4.3.2', 'tifffile>=2024.8.30', 'ultralytics==8.3.230', 'onnx>=1.18.0', 'transformers>=4.57.1'],#, 'onnxsim>=0.4.36'],
17+
'dev': ['opencv-python>=4.10.0.84', 'ImageHash>=4.3.2', 'tifffile>=2024.8.30', 'ultralytics==8.3.230', 'onnx>=1.16.0', 'transformers>=4.57.1'],
1818
}
1919

2020
setup(

0 commit comments

Comments
 (0)