learning-loop-node 0.8.6__py3-none-any.whl → 0.8.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of learning-loop-node might be problematic. Click here for more details.
- learning_loop_node/data_exchanger.py +16 -2
- learning_loop_node/detector/detector_logic.py +1 -1
- learning_loop_node/detector/detector_node.py +2 -0
- learning_loop_node/tests/test_downloader.py +24 -1
- learning_loop_node/trainer/trainer_logic.py +3 -3
- {learning_loop_node-0.8.6.dist-info → learning_loop_node-0.8.8.dist-info}/METADATA +4 -4
- {learning_loop_node-0.8.6.dist-info → learning_loop_node-0.8.8.dist-info}/RECORD +8 -8
- {learning_loop_node-0.8.6.dist-info → learning_loop_node-0.8.8.dist-info}/WHEEL +0 -0
|
@@ -59,10 +59,23 @@ class DataExchanger():
|
|
|
59
59
|
logging.warning('context was not set yet')
|
|
60
60
|
return
|
|
61
61
|
|
|
62
|
+
await self.delete_corrupt_images(image_folder)
|
|
62
63
|
new_image_ids = await asyncio.get_event_loop().run_in_executor(None, DataExchanger.filter_existing_images, image_ids, image_folder)
|
|
63
64
|
paths, ids = create_resource_paths(self.context.organization, self.context.project, new_image_ids)
|
|
64
65
|
await self._download_images(paths, ids, image_folder)
|
|
65
66
|
|
|
67
|
+
@staticmethod
|
|
68
|
+
async def delete_corrupt_images(image_folder: str) -> None:
|
|
69
|
+
logging.info('deleting corrupt images')
|
|
70
|
+
n_deleted = 0
|
|
71
|
+
for image in glob(f'{image_folder}/*.jpg'):
|
|
72
|
+
if not await DataExchanger.is_valid_image(image):
|
|
73
|
+
logging.debug(f' deleting image {image}')
|
|
74
|
+
os.remove(image)
|
|
75
|
+
n_deleted += 1
|
|
76
|
+
|
|
77
|
+
logging.info(f'deleted {n_deleted} images')
|
|
78
|
+
|
|
66
79
|
@staticmethod
|
|
67
80
|
def filter_existing_images(all_image_ids, image_folder) -> List[str]:
|
|
68
81
|
logging.info(f'### Going to filter {len(all_image_ids)} images ids')
|
|
@@ -131,8 +144,9 @@ class DataExchanger():
|
|
|
131
144
|
if not await self.is_valid_image(filename):
|
|
132
145
|
os.remove(filename)
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
@staticmethod
|
|
148
|
+
async def is_valid_image(filename: str) -> bool:
|
|
149
|
+
if not os.path.isfile(filename) or os.path.getsize(filename) == 0:
|
|
136
150
|
return False
|
|
137
151
|
if not check_jpeg:
|
|
138
152
|
return True
|
|
@@ -46,7 +46,7 @@ class DetectorLogic():
|
|
|
46
46
|
|
|
47
47
|
@abstractmethod
|
|
48
48
|
def init(self):
|
|
49
|
-
"""Initialize the model.
|
|
49
|
+
"""Called when a (new) model was loaded. Initialize the model. Model information available via `self.model_info`"""
|
|
50
50
|
|
|
51
51
|
@abstractmethod
|
|
52
52
|
def evaluate(self, image: np.ndarray) -> Detections:
|
|
@@ -295,6 +295,8 @@ class DetectorNode(Node):
|
|
|
295
295
|
self.log.error('could not reload app')
|
|
296
296
|
|
|
297
297
|
async def get_detections(self, raw_image: np.ndarray, camera_id: Optional[str], tags: List[str], autoupload: Optional[str] = None) -> Optional[Dict]:
|
|
298
|
+
"""Note: raw_image is a numpy array of type uint8, but not in the correrct shape!
|
|
299
|
+
It can be converted e.g. using cv2.imdecode(raw_image, cv2.IMREAD_COLOR)"""
|
|
298
300
|
loop = asyncio.get_event_loop()
|
|
299
301
|
detections: Detections = await loop.run_in_executor(None, self.detector_logic.evaluate, raw_image)
|
|
300
302
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
1
4
|
from learning_loop_node.data_classes import Context
|
|
2
|
-
from learning_loop_node.data_exchanger import DataExchanger
|
|
5
|
+
from learning_loop_node.data_exchanger import DataExchanger, check_jpeg
|
|
3
6
|
from learning_loop_node.globals import GLOBALS
|
|
4
7
|
|
|
5
8
|
from . import test_helper
|
|
@@ -46,3 +49,23 @@ async def test_download_training_data(data_exchanger: DataExchanger):
|
|
|
46
49
|
image_ids = await data_exchanger.fetch_image_ids()
|
|
47
50
|
image_data = await data_exchanger.download_images_data(image_ids)
|
|
48
51
|
assert len(image_data) == 3
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def test_removal_of_corrupted_images(data_exchanger: DataExchanger):
|
|
55
|
+
image_ids = await data_exchanger.fetch_image_ids()
|
|
56
|
+
|
|
57
|
+
shutil.rmtree('/tmp/img_folder', ignore_errors=True)
|
|
58
|
+
os.makedirs('/tmp/img_folder', exist_ok=True)
|
|
59
|
+
await data_exchanger.download_images(image_ids, '/tmp/img_folder')
|
|
60
|
+
num_images = len(os.listdir('/tmp/img_folder'))
|
|
61
|
+
|
|
62
|
+
# Generate two corrupted images
|
|
63
|
+
with open('/tmp/img_folder/c0.jpg', 'w') as f:
|
|
64
|
+
f.write('')
|
|
65
|
+
with open('/tmp/img_folder/c1.jpg', 'w') as f:
|
|
66
|
+
f.write('I am no image')
|
|
67
|
+
|
|
68
|
+
await data_exchanger.delete_corrupt_images('/tmp/img_folder')
|
|
69
|
+
|
|
70
|
+
assert len(os.listdir('/tmp/img_folder')) == num_images if check_jpeg else num_images - 1
|
|
71
|
+
shutil.rmtree('/tmp/img_folder', ignore_errors=True)
|
|
@@ -54,8 +54,8 @@ class TrainerLogic():
|
|
|
54
54
|
self._training: Optional[Training] = None
|
|
55
55
|
self._active_training_io: Optional[ActiveTrainingIO] = None
|
|
56
56
|
self._node: Optional[TrainerNode] = None
|
|
57
|
-
self.restart_after_training =
|
|
58
|
-
self.keep_old_trainings =
|
|
57
|
+
self.restart_after_training = os.environ.get('RESTART_AFTER_TRAINING', 'FALSE').lower() in ['true', '1']
|
|
58
|
+
self.keep_old_trainings = os.environ.get('KEEP_OLD_TRAININGS', 'FALSE').lower() in ['true', '1']
|
|
59
59
|
self.inference_batch_size = int(os.environ.get('INFERENCE_BATCH_SIZE', '10'))
|
|
60
60
|
logging.info(f'INFERENCE_BATCH_SIZE: {self.inference_batch_size}')
|
|
61
61
|
|
|
@@ -335,7 +335,7 @@ class TrainerLogic():
|
|
|
335
335
|
try:
|
|
336
336
|
new_model_id = await self._upload_model_return_new_id(self.training.context)
|
|
337
337
|
if new_model_id is None:
|
|
338
|
-
raise Exception('could not upload model')
|
|
338
|
+
raise Exception('could not upload model - maybe training failed')
|
|
339
339
|
assert new_model_id is not None, 'uploaded_model must be set'
|
|
340
340
|
logging.info(f'successfully uploaded model and received new model id: {new_model_id}')
|
|
341
341
|
self.training.model_id_for_detecting = new_model_id
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: learning-loop-node
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.8
|
|
4
4
|
Summary: Python Library for Nodes which connect to the Zauberzeug Learning Loop
|
|
5
5
|
Home-page: https://github.com/zauberzeug/learning_loop_node
|
|
6
6
|
License: MIT
|
|
@@ -18,8 +18,8 @@ Requires-Dist: aiofiles (>=0.7.0,<0.8.0)
|
|
|
18
18
|
Requires-Dist: aiohttp (>=3.8.4,<4.0.0)
|
|
19
19
|
Requires-Dist: async_generator (>=1.10,<2.0)
|
|
20
20
|
Requires-Dist: dacite (>=1.8.1,<2.0.0)
|
|
21
|
-
Requires-Dist: fastapi (>=0.
|
|
22
|
-
Requires-Dist: fastapi-socketio (>=0.0.
|
|
21
|
+
Requires-Dist: fastapi (>=0.93,<0.109)
|
|
22
|
+
Requires-Dist: fastapi-socketio (>=0.0.10,<0.0.11)
|
|
23
23
|
Requires-Dist: fastapi-utils (>=0.2.1,<0.3.0)
|
|
24
24
|
Requires-Dist: httpx (>=0.24.1,<0.25.0)
|
|
25
25
|
Requires-Dist: icecream (>=2.1.0,<3.0.0)
|
|
@@ -33,7 +33,7 @@ Requires-Dist: python-socketio (>=5.7.2,<6.0.0)
|
|
|
33
33
|
Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
34
34
|
Requires-Dist: simplejson (>=3.17.2,<4.0.0)
|
|
35
35
|
Requires-Dist: tqdm (>=4.63.0,<5.0.0)
|
|
36
|
-
Requires-Dist: uvicorn (>=0.
|
|
36
|
+
Requires-Dist: uvicorn[standard] (>=0.22.0,<0.23.0)
|
|
37
37
|
Requires-Dist: werkzeug (>=2.0.1,<3.0.0)
|
|
38
38
|
Project-URL: Repository, https://github.com/zauberzeug/learning_loop_node
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
@@ -14,10 +14,10 @@ learning_loop_node/data_classes/detections.py,sha256=J-8z6OrUw_QMyaUOdgODxMF8Z97
|
|
|
14
14
|
learning_loop_node/data_classes/general.py,sha256=3LYglqOMvQIfNn0_UUwbmdyb905Fzna1yf5p55cnPmE,4635
|
|
15
15
|
learning_loop_node/data_classes/socket_response.py,sha256=tIdt-oYf6ULoJIDYQCecNM9OtWR6_wJ9tL0Ksu83Vko,655
|
|
16
16
|
learning_loop_node/data_classes/training.py,sha256=VLGnZTJXh6p7K16lO41lPNlMl6EAIO_kVPfBu8w96ug,4870
|
|
17
|
-
learning_loop_node/data_exchanger.py,sha256=
|
|
17
|
+
learning_loop_node/data_exchanger.py,sha256=YAGn7laOZpVIwcK6O0WX-6ec36ypvxlW-u7UfCgblDQ,9876
|
|
18
18
|
learning_loop_node/detector/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
19
|
-
learning_loop_node/detector/detector_logic.py,sha256=
|
|
20
|
-
learning_loop_node/detector/detector_node.py,sha256=
|
|
19
|
+
learning_loop_node/detector/detector_logic.py,sha256=qjnFzrVO020-kvA1dcI5zoZLANynMx3RR61zEIS0udo,1741
|
|
20
|
+
learning_loop_node/detector/detector_node.py,sha256=1kUFLIa_S1mZ0PqfeqsNdcrGfJYcR09mhLSp6XciDZY,16559
|
|
21
21
|
learning_loop_node/detector/inbox_filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
learning_loop_node/detector/inbox_filter/cam_observation_history.py,sha256=5iNR2cEPujjY83sfQug97-hZ-T8iFc44gi1s_yd7GO0,3437
|
|
23
23
|
learning_loop_node/detector/inbox_filter/relevance_filter.py,sha256=s2FuwZ-tD_5obkSutstjc8pE_hLGbrv9WjrEO9t8rJ8,1011
|
|
@@ -53,7 +53,7 @@ learning_loop_node/tests/test_data/file_1.txt,sha256=Lis06nfvbFPVCBZyEgQlfI_Nle2
|
|
|
53
53
|
learning_loop_node/tests/test_data/file_2.txt,sha256=Xp8EETGhZBdVAgb4URowSSpOytwwwJdV0Renkdur7R8,19
|
|
54
54
|
learning_loop_node/tests/test_data/model.json,sha256=_xNDucGOWila8gWnu8yFfrqmQ45Xq-_39eLKzjRtvpE,516
|
|
55
55
|
learning_loop_node/tests/test_data_classes.py,sha256=m8LEk1quGErxuPzNdW_ExqQjkwE4u7ribwnTdyeiHR8,788
|
|
56
|
-
learning_loop_node/tests/test_downloader.py,sha256=
|
|
56
|
+
learning_loop_node/tests/test_downloader.py,sha256=sMhpQspkzw3VrYG2ws064SRCucf_9ceD_e3qkgzJxtk,2755
|
|
57
57
|
learning_loop_node/tests/test_executor.py,sha256=ILBKfX-A2zlxfot0xoz3jpG3XcEMo3kxQKkPgoz_aT4,1948
|
|
58
58
|
learning_loop_node/tests/test_helper.py,sha256=fFcmTqgw1RJtuQHWk5_Mn9hc-wXIFcQow2JphLFwrlI,2412
|
|
59
59
|
learning_loop_node/tests/test_learning_loop_node.py,sha256=4qWi1ovBzebUAbvw8ecSa-TBGKYuJvlKe2AMnMZ-Qs8,701
|
|
@@ -79,9 +79,9 @@ learning_loop_node/trainer/tests/states/test_state_upload_model.py,sha256=3BzIpA
|
|
|
79
79
|
learning_loop_node/trainer/tests/test_errors.py,sha256=QHZK1YVSXqQskUZbl0cU1sjJeDv3dz_2iF0LFhMwe58,1918
|
|
80
80
|
learning_loop_node/trainer/tests/test_trainer_states.py,sha256=HeIFdAStf6KBT9lQGpVdeLQbLCgb0cqDO2Z_FJ1IO8Y,1144
|
|
81
81
|
learning_loop_node/trainer/tests/testing_trainer_logic.py,sha256=DCKNhBFsV2uN-MNCpkKx8XNoAwWc_7Kfrj1kRpJEEVk,3995
|
|
82
|
-
learning_loop_node/trainer/trainer_logic.py,sha256=
|
|
82
|
+
learning_loop_node/trainer/trainer_logic.py,sha256=_Z5LAVRRL9PFmWgWng3zqdrOymFQuv8AeZHGxLHcSno,31864
|
|
83
83
|
learning_loop_node/trainer/trainer_node.py,sha256=-bJ6xoOdqG9aZL5Ok2Z5IHGrQIbnZ4KRy323CngYnNI,7780
|
|
84
84
|
learning_loop_node/trainer/training_syncronizer.py,sha256=qzJbv9WqApbM1aNQNg98IjTWed2z__UKocQ_MJP9gz8,1884
|
|
85
|
-
learning_loop_node-0.8.
|
|
86
|
-
learning_loop_node-0.8.
|
|
87
|
-
learning_loop_node-0.8.
|
|
85
|
+
learning_loop_node-0.8.8.dist-info/METADATA,sha256=Y4BnHrKUWV2i8IpBCG2HeW30hk63ddZwMxxPRgfKY0U,9157
|
|
86
|
+
learning_loop_node-0.8.8.dist-info/WHEEL,sha256=WGfLGfLX43Ei_YORXSnT54hxFygu34kMpcQdmgmEwCQ,88
|
|
87
|
+
learning_loop_node-0.8.8.dist-info/RECORD,,
|
|
File without changes
|