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.

@@ -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
- async def is_valid_image(self, filename: str) -> bool:
135
- if not os.path.isfile(filename):
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. Note that `model_info` is available as `self.model_info`"""
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 = bool(int(os.environ.get('RESTART_AFTER_TRAINING', '0')))
58
- self.keep_old_trainings = bool(int(os.environ.get('KEEP_OLD_TRAININGS', '0')))
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.6
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.70.1,<0.71.0)
22
- Requires-Dist: fastapi-socketio (>=0.0.6,<0.0.7)
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.13.3,<0.14.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=6KUrc290fDeT5dnca-eT-WmTowXfZjjS3sSFgCgesbo,9332
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=vVEJsGK6LuJLJxBk-Rfwa3KBbm5LDKrJa2CH9hSbDWc,1710
20
- learning_loop_node/detector/detector_node.py,sha256=g_2C186EM1RzOANEznKnP9PyoR0HfZNaHpdxFjHJio8,16385
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=SxFMAvK1y0ul_TuMl5oP-TDIMTu6ChRO-2Xa5Fkc1mU,1944
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=ggwlJTv4EXhxy1KEtnZ2tv-NvnIhNOTDeUWFuPFCmUE,31804
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.6.dist-info/METADATA,sha256=SQaUsoqNm2vbwKQsUWGQGbq26-qA8l1UE-Hponz-iEo,9148
86
- learning_loop_node-0.8.6.dist-info/WHEEL,sha256=WGfLGfLX43Ei_YORXSnT54hxFygu34kMpcQdmgmEwCQ,88
87
- learning_loop_node-0.8.6.dist-info/RECORD,,
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,,