learning-loop-node 0.14.0__tar.gz → 0.15.0__tar.gz

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.

Files changed (104) hide show
  1. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/PKG-INFO +3 -1
  2. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/README.md +2 -0
  3. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/__init__.py +2 -2
  4. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/image_metadata.py +5 -0
  5. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/training.py +3 -2
  6. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_exchanger.py +3 -3
  7. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/detector_logic.py +6 -1
  8. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/detector_node.py +63 -16
  9. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/test_relevance_filter.py +3 -3
  10. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/conftest.py +2 -2
  11. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/trainer_logic_generic.py +16 -4
  12. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/pyproject.toml +1 -1
  13. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/__init__.py +0 -0
  14. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/annotation/__init__.py +0 -0
  15. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/annotation/annotator_logic.py +0 -0
  16. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/annotation/annotator_node.py +0 -0
  17. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/annotations.py +0 -0
  18. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/detections.py +0 -0
  19. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/general.py +0 -0
  20. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/data_classes/socket_response.py +0 -0
  21. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/__init__.py +0 -0
  22. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/exceptions.py +0 -0
  23. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/inbox_filter/__init__.py +0 -0
  24. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/inbox_filter/cam_observation_history.py +0 -0
  25. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/inbox_filter/relevance_filter.py +0 -0
  26. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/outbox.py +0 -0
  27. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/__init__.py +0 -0
  28. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/about.py +0 -0
  29. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/backdoor_controls.py +0 -0
  30. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/detect.py +0 -0
  31. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/model_version_control.py +0 -0
  32. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/operation_mode.py +0 -0
  33. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/outbox_mode.py +0 -0
  34. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/detector/rest/upload.py +0 -0
  35. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/enums/__init__.py +0 -0
  36. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/enums/annotator.py +0 -0
  37. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/enums/detector.py +0 -0
  38. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/enums/general.py +0 -0
  39. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/enums/trainer.py +0 -0
  40. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/examples/novelty_score_updater.py +0 -0
  41. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/globals.py +0 -0
  42. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/__init__.py +0 -0
  43. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/background_tasks.py +0 -0
  44. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/environment_reader.py +0 -0
  45. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/gdrive_downloader.py +0 -0
  46. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/log_conf.py +0 -0
  47. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/misc.py +0 -0
  48. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/helpers/run.py +0 -0
  49. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/loop_communication.py +0 -0
  50. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/node.py +0 -0
  51. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/py.typed +0 -0
  52. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/rest.py +0 -0
  53. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/__init__.py +0 -0
  54. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/annotator/__init__.py +0 -0
  55. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/annotator/conftest.py +0 -0
  56. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/annotator/pytest.ini +0 -0
  57. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/annotator/test_annotator_node.py +0 -0
  58. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/__init__.py +0 -0
  59. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/conftest.py +0 -0
  60. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/inbox_filter/__init__.py +0 -0
  61. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/inbox_filter/test_observation.py +0 -0
  62. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/inbox_filter/test_relevance_group.py +0 -0
  63. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/inbox_filter/test_unexpected_observations_count.py +0 -0
  64. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/pytest.ini +0 -0
  65. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/test.jpg +0 -0
  66. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/test_client_communication.py +0 -0
  67. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/test_detector_node.py +0 -0
  68. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/test_outbox.py +0 -0
  69. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/detector/testing_detector.py +0 -0
  70. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/__init__.py +0 -0
  71. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/conftest.py +0 -0
  72. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/pytest.ini +0 -0
  73. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_data/file_1.txt +0 -0
  74. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_data/file_2.txt +0 -0
  75. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_data/model.json +0 -0
  76. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_data_classes.py +0 -0
  77. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_downloader.py +0 -0
  78. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/general/test_learning_loop_node.py +0 -0
  79. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/test_helper.py +0 -0
  80. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/__init__.py +0 -0
  81. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/pytest.ini +0 -0
  82. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/state_helper.py +0 -0
  83. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/__init__.py +0 -0
  84. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_cleanup.py +0 -0
  85. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_detecting.py +0 -0
  86. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_download_train_model.py +0 -0
  87. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_prepare.py +0 -0
  88. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_sync_confusion_matrix.py +0 -0
  89. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_train.py +0 -0
  90. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_upload_detections.py +0 -0
  91. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/states/test_state_upload_model.py +0 -0
  92. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/test_errors.py +0 -0
  93. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/test_trainer_states.py +0 -0
  94. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/tests/trainer/testing_trainer_logic.py +0 -0
  95. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/__init__.py +0 -0
  96. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/downloader.py +0 -0
  97. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/exceptions.py +0 -0
  98. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/executor.py +0 -0
  99. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/io_helpers.py +0 -0
  100. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/rest/__init__.py +0 -0
  101. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/rest/backdoor_controls.py +0 -0
  102. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/test_executor.py +0 -0
  103. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/trainer_logic.py +0 -0
  104. {learning_loop_node-0.14.0 → learning_loop_node-0.15.0}/learning_loop_node/trainer/trainer_node.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: learning-loop-node
3
- Version: 0.14.0
3
+ Version: 0.15.0
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
@@ -100,6 +100,8 @@ You can additionally provide the following camera parameters:
100
100
  - `autoupload`: configures auto-submission to the learning loop; `filtered` (default), `all`, `disabled` (example curl parameter `-H 'autoupload: all'`)
101
101
  - `camera-id`: a string which groups images for submission together (example curl parameter `-H 'camera-id: front_cam'`)
102
102
 
103
+ To use the socketio interface, the caller needs to connect to the detector node's socketio server and emit the `detect` or `batch_detect` event with the image data and image metadata. Example code can be found [in the rosys implementation](https://github.com/zauberzeug/rosys/blob/main/rosys/vision/detector_hardware.py).
104
+
103
105
  The detector also has a sio **upload endpoint** that can be used to upload images and detections to the learning loop. The function receives a json dictionary, with the following entries:
104
106
 
105
107
  - `image`: the image data in jpg format
@@ -60,6 +60,8 @@ You can additionally provide the following camera parameters:
60
60
  - `autoupload`: configures auto-submission to the learning loop; `filtered` (default), `all`, `disabled` (example curl parameter `-H 'autoupload: all'`)
61
61
  - `camera-id`: a string which groups images for submission together (example curl parameter `-H 'camera-id: front_cam'`)
62
62
 
63
+ To use the socketio interface, the caller needs to connect to the detector node's socketio server and emit the `detect` or `batch_detect` event with the image data and image metadata. Example code can be found [in the rosys implementation](https://github.com/zauberzeug/rosys/blob/main/rosys/vision/detector_hardware.py).
64
+
63
65
  The detector also has a sio **upload endpoint** that can be used to upload images and detections to the learning loop. The function receives a json dictionary, with the following entries:
64
66
 
65
67
  - `image`: the image data in jpg format
@@ -3,7 +3,7 @@ from .detections import (BoxDetection, ClassificationDetection, Detections, Obse
3
3
  SegmentationDetection, Shape)
4
4
  from .general import (AboutResponse, AnnotationNodeStatus, Category, Context, DetectionStatus, ErrorConfiguration,
5
5
  ModelInformation, ModelVersionResponse, NodeState, NodeStatus)
6
- from .image_metadata import ImageMetadata
6
+ from .image_metadata import ImageMetadata, ImagesMetadata
7
7
  from .socket_response import SocketResponse
8
8
  from .training import Errors, PretrainedModel, Training, TrainingError, TrainingOut, TrainingStateData, TrainingStatus
9
9
 
@@ -12,7 +12,7 @@ __all__ = [
12
12
  'BoxDetection', 'ClassificationDetection', 'ImageMetadata', 'Observation', 'Point', 'PointDetection',
13
13
  'SegmentationDetection', 'Shape', 'Detections',
14
14
  'AnnotationNodeStatus', 'Category', 'Context', 'DetectionStatus', 'ErrorConfiguration',
15
- 'ModelInformation', 'NodeState', 'NodeStatus', 'ModelVersionResponse',
15
+ 'ModelInformation', 'NodeState', 'NodeStatus', 'ModelVersionResponse', 'ImagesMetadata',
16
16
  'SocketResponse',
17
17
  'Errors', 'PretrainedModel', 'Training',
18
18
  'TrainingError', 'TrainingOut', 'TrainingStateData', 'TrainingStatus',
@@ -35,3 +35,8 @@ class ImageMetadata():
35
35
 
36
36
  def __len__(self):
37
37
  return len(self.box_detections) + len(self.point_detections) + len(self.segmentation_detections) + len(self.classification_detections)
38
+
39
+
40
+ @dataclass(**KWONLY_SLOTS)
41
+ class ImagesMetadata():
42
+ items: List[ImageMetadata] = field(default_factory=list, metadata={'description': 'List of image metadata'})
@@ -8,6 +8,7 @@ from uuid import uuid4
8
8
 
9
9
  from ..enums import TrainerState
10
10
  from ..helpers.misc import create_image_folder, create_training_folder
11
+
11
12
  # pylint: disable=no-name-in-module
12
13
  from .general import Category, Context
13
14
 
@@ -52,7 +53,7 @@ class Training():
52
53
  training_folder: str # f'{project_folder}/trainings/{trainings_id}'
53
54
 
54
55
  categories: List[Category]
55
- hyperparameters: dict
56
+ hyperparameters: Dict[str, Any]
56
57
 
57
58
  training_number: int
58
59
  training_state: str
@@ -63,7 +64,7 @@ class Training():
63
64
  base_model_uuid: Optional[str] = None # model uuid to continue training (is loaded from loop)
64
65
 
65
66
  # NOTE: these are set later after the model has been uploaded
66
- image_data: Optional[List[dict]] = None
67
+ image_data: Optional[List[Dict]] = None
67
68
  skipped_image_count: Optional[int] = None
68
69
  model_uuid_for_detecting: Optional[str] = None # Model uuid to load from the loop after training and upload
69
70
 
@@ -7,7 +7,7 @@ from glob import glob
7
7
  from http import HTTPStatus
8
8
  from io import BytesIO
9
9
  from time import time
10
- from typing import Dict, List, Optional
10
+ from typing import Any, Dict, List, Optional
11
11
 
12
12
  import aiofiles # type: ignore
13
13
 
@@ -68,7 +68,7 @@ class DataExchanger():
68
68
  assert response.status_code == 200, response
69
69
  return (response.json())['image_ids']
70
70
 
71
- async def download_images_data(self, image_uuids: List[str], chunk_size: int = 100) -> List[Dict]:
71
+ async def download_images_data(self, image_uuids: List[str], chunk_size: int = 100) -> List[Dict[str, Any]]:
72
72
  """Download image annotations, tags, set and other information for the given image uuids."""
73
73
  logging.info('Fetching annotations, tags, sets, etc. for %s images..', len(image_uuids))
74
74
 
@@ -78,7 +78,7 @@ class DataExchanger():
78
78
  return []
79
79
 
80
80
  progress_factor = 0.5 / num_image_ids # first 50% of progress is for downloading data
81
- images_data: List[Dict] = []
81
+ images_data: List[Dict[str, Any]] = []
82
82
  for i in range(0, num_image_ids, chunk_size):
83
83
  self.progress = i * progress_factor
84
84
  chunk_ids = image_uuids[i:i+chunk_size]
@@ -2,7 +2,7 @@ import logging
2
2
  from abc import abstractmethod
3
3
  from typing import List, Optional
4
4
 
5
- from ..data_classes import ImageMetadata, ModelInformation
5
+ from ..data_classes import ImageMetadata, ImagesMetadata, ModelInformation
6
6
  from ..globals import GLOBALS
7
7
  from .exceptions import NodeNeedsRestartError
8
8
 
@@ -52,3 +52,8 @@ class DetectorLogic():
52
52
  def evaluate(self, image: bytes) -> ImageMetadata:
53
53
  """Evaluate the image and return the detections.
54
54
  The object should return empty detections if it is not initialized"""
55
+
56
+ @abstractmethod
57
+ def batch_evaluate(self, images: List[bytes]) -> ImagesMetadata:
58
+ """Evaluate a batch of images and return the detections.
59
+ The object should return empty detections if it is not initialized"""
@@ -6,24 +6,15 @@ import subprocess
6
6
  import sys
7
7
  from dataclasses import asdict
8
8
  from datetime import datetime
9
- from typing import Dict, List, Optional, cast
9
+ from typing import Dict, List, Optional
10
10
 
11
- import numpy as np
12
11
  import socketio
13
12
  from dacite import from_dict
14
13
  from fastapi.encoders import jsonable_encoder
15
14
  from socketio import AsyncClient
16
15
 
17
- from ..data_classes import (
18
- AboutResponse,
19
- Category,
20
- Context,
21
- DetectionStatus,
22
- ImageMetadata,
23
- ModelInformation,
24
- ModelVersionResponse,
25
- Shape,
26
- )
16
+ from ..data_classes import (AboutResponse, Category, Context, DetectionStatus, ImageMetadata, ImagesMetadata,
17
+ ModelInformation, ModelVersionResponse, Shape)
27
18
  from ..data_classes.socket_response import SocketResponse
28
19
  from ..data_exchanger import DataExchanger, DownloadError
29
20
  from ..enums import OperationMode, VersionMode
@@ -238,8 +229,29 @@ class DetectorNode(Node):
238
229
  return detection_dict
239
230
  except Exception as e:
240
231
  self.log.exception('could not detect via socketio')
241
- with open('/tmp/bad_img_from_socket_io.jpg', 'wb') as f:
242
- f.write(data['image'])
232
+ # with open('/tmp/bad_img_from_socket_io.jpg', 'wb') as f:
233
+ # f.write(data['image'])
234
+ return {'error': str(e)}
235
+
236
+ @self.sio.event
237
+ async def batch_detect(sid, data: Dict) -> Dict:
238
+ try:
239
+ det = await self.get_batch_detections(
240
+ raw_images=data['images'],
241
+ tags=data.get('tags', []),
242
+ camera_id=data.get('camera-id', None) or data.get('mac', None),
243
+ source=data.get('source', None),
244
+ autoupload=data.get('autoupload', None),
245
+ creation_date=data.get('creation_date', None)
246
+ )
247
+ if det is None:
248
+ return {'error': 'no model loaded'}
249
+ detection_dict = jsonable_encoder(asdict(det))
250
+ return detection_dict
251
+ except Exception as e:
252
+ self.log.exception('could not detect via socketio')
253
+ # with open('/tmp/bad_img_from_socket_io.jpg', 'wb') as f:
254
+ # f.write(data['image'])
243
255
  return {'error': str(e)}
244
256
 
245
257
  @self.sio.event
@@ -479,13 +491,14 @@ class DetectorNode(Node):
479
491
 
480
492
  async def get_detections(self,
481
493
  raw_image: bytes,
482
- camera_id: Optional[str],
483
494
  tags: List[str],
495
+ *,
496
+ camera_id: Optional[str] = None,
484
497
  source: Optional[str] = None,
485
498
  autoupload: Optional[str] = None,
486
499
  creation_date: Optional[str] = None) -> ImageMetadata:
487
500
  """ Main processing function for the detector node when an image is received via REST or SocketIO.
488
- This function infers the detections from the image, cares about uploading to the loop and returns the detections as a dictionary.
501
+ This function infers the detections from the image, cares about uploading to the loop and returns the detections as ImageMetadata object.
489
502
  Note: raw_image is a numpy array of type uint8, but not in the correct shape!
490
503
  It can be converted e.g. using cv2.imdecode(raw_image, cv2.IMREAD_COLOR)"""
491
504
 
@@ -511,6 +524,40 @@ class DetectorNode(Node):
511
524
  self.log.error('unknown autoupload value %s', autoupload)
512
525
  return detections
513
526
 
527
+ async def get_batch_detections(self,
528
+ raw_images: List[bytes],
529
+ tags: List[str],
530
+ *,
531
+ camera_id: Optional[str] = None,
532
+ source: Optional[str] = None,
533
+ autoupload: Optional[str] = None,
534
+ creation_date: Optional[str] = None) -> ImagesMetadata:
535
+ """ Processing function for the detector node when a a batch inference is requested via SocketIO.
536
+ This function infers the detections from all images, cares about uploading to the loop and returns the detections as a list of ImageMetadata."""
537
+
538
+ await self.detection_lock.acquire()
539
+ all_detections = await run.io_bound(self.detector_logic.batch_evaluate, raw_images)
540
+ self.detection_lock.release()
541
+
542
+ for detections, raw_image in zip(all_detections.items, raw_images):
543
+ fix_shape_detections(detections)
544
+ n_bo, n_cl = len(detections.box_detections), len(detections.classification_detections)
545
+ n_po, n_se = len(detections.point_detections), len(detections.segmentation_detections)
546
+ self.log.debug('Detected: %d boxes, %d points, %d segs, %d classes', n_bo, n_po, n_se, n_cl)
547
+
548
+ autoupload = autoupload or 'filtered'
549
+ if autoupload == 'filtered' and camera_id is not None:
550
+ background_tasks.create(self.relevance_filter.may_upload_detections(
551
+ detections, camera_id, raw_image, tags, source, creation_date
552
+ ))
553
+ elif autoupload == 'all':
554
+ background_tasks.create(self.outbox.save(raw_image, detections, tags, source, creation_date))
555
+ elif autoupload == 'disabled':
556
+ pass
557
+ else:
558
+ self.log.error('unknown autoupload value %s', autoupload)
559
+ return all_detections
560
+
514
561
  async def upload_images(
515
562
  self, *,
516
563
  images: List[bytes],
@@ -30,10 +30,10 @@ async def test_filter_is_used_by_node(test_detector_node: DetectorNode, autouplo
30
30
  assert test_detector_node.outbox.path.startswith('/tmp')
31
31
  assert len(get_outbox_files(test_detector_node.outbox)) == 0
32
32
 
33
- image = np.fromfile(file=test_image_path, dtype=np.uint8)
34
- _ = await test_detector_node.get_detections(image, '00:.....', tags=[], autoupload=autoupload)
33
+ image = bytes(np.fromfile(file=test_image_path, dtype=np.uint8))
34
+ _ = await test_detector_node.get_detections(image, tags=[], camera_id='00:.....', autoupload=autoupload)
35
35
  # NOTE adding second images with identical detections
36
- _ = await test_detector_node.get_detections(image, '00:.....', tags=[], autoupload=autoupload)
36
+ _ = await test_detector_node.get_detections(image, tags=[], camera_id='00:.....', autoupload=autoupload)
37
37
  await asyncio.sleep(.5) # files are stored asynchronously
38
38
 
39
39
  assert len(get_outbox_files(test_detector_node.outbox)) == expected_file_count, \
@@ -35,7 +35,7 @@ async def test_initialized_trainer_node():
35
35
  'training_number': 0,
36
36
  'model_variant': '',
37
37
  'hyperparameters': {
38
- 'resolution': 800,
38
+ 'resolution': 832,
39
39
  'fliplr': 0.5,
40
40
  'flipud': 0.5}
41
41
  })
@@ -58,7 +58,7 @@ async def test_initialized_trainer():
58
58
  'training_number': 0,
59
59
  'model_variant': '',
60
60
  'hyperparameters': {
61
- 'resolution': 800,
61
+ 'resolution': 832,
62
62
  'fliplr': 0.5,
63
63
  'flipud': 0.5}
64
64
  })
@@ -6,13 +6,25 @@ import sys
6
6
  import time
7
7
  from abc import ABC, abstractmethod
8
8
  from dataclasses import asdict
9
- from typing import TYPE_CHECKING, Callable, Coroutine, Dict, List, Optional
9
+ from typing import TYPE_CHECKING, Any, Callable, Coroutine, Dict, List, Optional
10
10
 
11
11
  from fastapi.encoders import jsonable_encoder
12
12
 
13
- from ..data_classes import Context, Errors, PretrainedModel, Training, TrainingOut, TrainingStateData, TrainingStatus
13
+ from ..data_classes import (
14
+ Context,
15
+ Errors,
16
+ PretrainedModel,
17
+ Training,
18
+ TrainingOut,
19
+ TrainingStateData,
20
+ TrainingStatus,
21
+ )
14
22
  from ..enums import TrainerState
15
- from ..helpers.misc import create_project_folder, delete_all_training_folders, is_valid_uuid4
23
+ from ..helpers.misc import (
24
+ create_project_folder,
25
+ delete_all_training_folders,
26
+ is_valid_uuid4,
27
+ )
16
28
  from .downloader import TrainingsDownloader
17
29
  from .exceptions import CriticalError, NodeNeedsRestartError
18
30
  from .io_helpers import ActiveTrainingIO, EnvironmentVars, LastTrainingIO
@@ -66,7 +78,7 @@ class TrainerLogicGeneric(ABC):
66
78
  return self._training
67
79
 
68
80
  @property
69
- def hyperparameters(self) -> dict:
81
+ def hyperparameters(self) -> Dict[str, Any]:
70
82
  assert self._training is not None, 'Training should have data'
71
83
  return self._training.hyperparameters
72
84
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "learning_loop_node"
3
- version = "v0.14.0"
3
+ version = "v0.15.0"
4
4
  description = "Python Library for Nodes which connect to the Zauberzeug Learning Loop"
5
5
  authors = ["Zauberzeug GmbH <info@zauberzeug.com>"]
6
6
  license = "MIT"