camera-client 0.3.0__tar.gz → 0.3.1__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.
Files changed (23) hide show
  1. {camera_client-0.3.0/camera_client.egg-info → camera_client-0.3.1}/PKG-INFO +19 -12
  2. {camera_client-0.3.0 → camera_client-0.3.1}/README.md +18 -11
  3. camera_client-0.3.1/camera_client/__init__.py +5 -0
  4. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/camera_network.py +51 -25
  5. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/error_model.py +6 -0
  6. {camera_client-0.3.0 → camera_client-0.3.1/camera_client.egg-info}/PKG-INFO +19 -12
  7. {camera_client-0.3.0 → camera_client-0.3.1}/pyproject.toml +1 -1
  8. camera_client-0.3.0/camera_client/__init__.py +0 -5
  9. {camera_client-0.3.0 → camera_client-0.3.1}/LICENSE +0 -0
  10. {camera_client-0.3.0 → camera_client-0.3.1}/MANIFEST.in +0 -0
  11. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/__main__.py +0 -0
  12. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/client.py +0 -0
  13. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/loading.py +0 -0
  14. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/script.py +0 -0
  15. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client/triangulation.py +0 -0
  16. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client.egg-info/SOURCES.txt +0 -0
  17. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client.egg-info/dependency_links.txt +0 -0
  18. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client.egg-info/entry_points.txt +0 -0
  19. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client.egg-info/requires.txt +0 -0
  20. {camera_client-0.3.0 → camera_client-0.3.1}/camera_client.egg-info/top_level.txt +0 -0
  21. {camera_client-0.3.0 → camera_client-0.3.1}/requirements.txt +0 -0
  22. {camera_client-0.3.0 → camera_client-0.3.1}/setup.cfg +0 -0
  23. {camera_client-0.3.0 → camera_client-0.3.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camera-client
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Python SDK for camera calibration and projection transformations - handle lens distortion, coordinate transformations, and 3D ray casting with symbolic expressions.
5
5
  Author-email: Alexander Abramov <extremal.ru@gmail.com>
6
6
  License: MIT
@@ -133,9 +133,9 @@ net = CameraNetwork(cameras)
133
133
 
134
134
  ### Spatial covariance
135
135
 
136
- `get_covariance` computes 3x3 spatial covariance matrices for 3D points.
137
- The matrix encodes how pixel-level uncertainty (distortion + geometric calibration + detection)
138
- propagates into world-space uncertainty through the camera's ray geometry.
136
+ `get_covariance` computes 3x3 spatial covariance matrices for 3D points —
137
+ both per-camera and fused across all visible cameras in a single call.
138
+ Returns a `NetworkCovariance` object.
139
139
 
140
140
  ```python
141
141
  points = np.array([
@@ -143,15 +143,15 @@ points = np.array([
143
143
  [18.0, 6.0, 1.5],
144
144
  ])
145
145
 
146
- # Per-camera covariance (no fusion, no visibility check)
147
- covs_cam = net.get_covariance(points, camera_id=1177, detection_sigma=0.01)
148
- # covs_cam[i] is a (3, 3) covariance matrix from camera 1177
146
+ result = net.get_covariance(points, detection_sigma=0.01)
147
+
148
+ # Per-camera covariance (None if camera doesn't see the point)
149
+ cov_cam = result[1177][0] # camera 1177, point 0
149
150
 
150
151
  # Fused covariance from all visible cameras (information fusion)
151
- covs_fused = net.get_covariance(points, detection_sigma=0.01)
152
- # covs_fused[i] is (3, 3) fused covariance, or None if not visible to any camera
152
+ cov_fused = result.fused[0] # fused, point 0 (or None if no camera sees it)
153
153
 
154
- for i, cov in enumerate(covs_fused):
154
+ for i, cov in enumerate(result.fused):
155
155
  if cov is not None:
156
156
  stds = np.sqrt(np.linalg.eigvalsh(cov))
157
157
  print(f"Point {i}: σ = {stds[0]:.3f}m, {stds[1]:.3f}m, {stds[2]:.3f}m")
@@ -236,12 +236,19 @@ Out-of-bounds points return NaN. Height `h` can be a scalar or per-point (N,) ar
236
236
  | Method | Description |
237
237
  |--------|-------------|
238
238
  | `CameraNetwork(cameras)` | Create network from list of `CameraProjection` instances |
239
- | `get_covariance(points, ...)` | Fused 3x3 covariance for (N, 3) points from all visible cameras |
240
- | `get_covariance(points, camera_id=id, ...)` | Per-camera 3x3 covariance (no fusion, no visibility check) |
239
+ | `get_covariance(points, ...)` | Returns `NetworkCovariance` with per-camera and fused 3x3 covariances |
241
240
  | `triangulate(observations, ...)` | 3D point + covariance from `{camera_id: src_point}` observations |
242
241
 
243
242
  Common parameters: `detection_sigma` (float), `sigma_binding` (float), `use_efov` (bool), `n_sigma` (float).
244
243
 
244
+ ### `NetworkCovariance`
245
+
246
+ | Access | Description |
247
+ |--------|-------------|
248
+ | `result[camera_id]` | List of N per-camera covariances (3x3 or None) |
249
+ | `result.fused` | List of N fused covariances (3x3 or None) |
250
+ | `result.camera_ids` | List of camera IDs in the result |
251
+
245
252
  ### `triangulation` module
246
253
 
247
254
  | Function | Description |
@@ -103,9 +103,9 @@ net = CameraNetwork(cameras)
103
103
 
104
104
  ### Spatial covariance
105
105
 
106
- `get_covariance` computes 3x3 spatial covariance matrices for 3D points.
107
- The matrix encodes how pixel-level uncertainty (distortion + geometric calibration + detection)
108
- propagates into world-space uncertainty through the camera's ray geometry.
106
+ `get_covariance` computes 3x3 spatial covariance matrices for 3D points —
107
+ both per-camera and fused across all visible cameras in a single call.
108
+ Returns a `NetworkCovariance` object.
109
109
 
110
110
  ```python
111
111
  points = np.array([
@@ -113,15 +113,15 @@ points = np.array([
113
113
  [18.0, 6.0, 1.5],
114
114
  ])
115
115
 
116
- # Per-camera covariance (no fusion, no visibility check)
117
- covs_cam = net.get_covariance(points, camera_id=1177, detection_sigma=0.01)
118
- # covs_cam[i] is a (3, 3) covariance matrix from camera 1177
116
+ result = net.get_covariance(points, detection_sigma=0.01)
117
+
118
+ # Per-camera covariance (None if camera doesn't see the point)
119
+ cov_cam = result[1177][0] # camera 1177, point 0
119
120
 
120
121
  # Fused covariance from all visible cameras (information fusion)
121
- covs_fused = net.get_covariance(points, detection_sigma=0.01)
122
- # covs_fused[i] is (3, 3) fused covariance, or None if not visible to any camera
122
+ cov_fused = result.fused[0] # fused, point 0 (or None if no camera sees it)
123
123
 
124
- for i, cov in enumerate(covs_fused):
124
+ for i, cov in enumerate(result.fused):
125
125
  if cov is not None:
126
126
  stds = np.sqrt(np.linalg.eigvalsh(cov))
127
127
  print(f"Point {i}: σ = {stds[0]:.3f}m, {stds[1]:.3f}m, {stds[2]:.3f}m")
@@ -206,12 +206,19 @@ Out-of-bounds points return NaN. Height `h` can be a scalar or per-point (N,) ar
206
206
  | Method | Description |
207
207
  |--------|-------------|
208
208
  | `CameraNetwork(cameras)` | Create network from list of `CameraProjection` instances |
209
- | `get_covariance(points, ...)` | Fused 3x3 covariance for (N, 3) points from all visible cameras |
210
- | `get_covariance(points, camera_id=id, ...)` | Per-camera 3x3 covariance (no fusion, no visibility check) |
209
+ | `get_covariance(points, ...)` | Returns `NetworkCovariance` with per-camera and fused 3x3 covariances |
211
210
  | `triangulate(observations, ...)` | 3D point + covariance from `{camera_id: src_point}` observations |
212
211
 
213
212
  Common parameters: `detection_sigma` (float), `sigma_binding` (float), `use_efov` (bool), `n_sigma` (float).
214
213
 
214
+ ### `NetworkCovariance`
215
+
216
+ | Access | Description |
217
+ |--------|-------------|
218
+ | `result[camera_id]` | List of N per-camera covariances (3x3 or None) |
219
+ | `result.fused` | List of N fused covariances (3x3 or None) |
220
+ | `result.camera_ids` | List of camera IDs in the result |
221
+
215
222
  ### `triangulation` module
216
223
 
217
224
  | Function | Description |
@@ -0,0 +1,5 @@
1
+ from camera_client.client import CameraProjection
2
+ from camera_client.camera_network import CameraNetwork, NetworkCovariance
3
+ from camera_client import triangulation
4
+
5
+ __all__ = ["CameraProjection", "CameraNetwork", "NetworkCovariance", "triangulation"]
@@ -31,6 +31,33 @@ def _points_in_polygon(points_xy, polygon_xy):
31
31
  return np.bitwise_xor.reduce(crosses, axis=1)
32
32
 
33
33
 
34
+ class NetworkCovariance:
35
+ """Result of CameraNetwork.get_covariance().
36
+
37
+ Access per-camera covariance lists by camera_id:
38
+ result[1177] — list of N covariance matrices (or None) for camera 1177
39
+
40
+ Access fused covariance:
41
+ result.fused — list of N fused covariance matrices (or None)
42
+ """
43
+
44
+ def __init__(self, cameras, fused):
45
+ self._cameras = cameras # dict {camera_id: [cov_or_none, ...]}
46
+ self.fused = fused # list [cov_or_none, ...]
47
+
48
+ def __getitem__(self, camera_id):
49
+ return self._cameras[camera_id]
50
+
51
+ @property
52
+ def camera_ids(self):
53
+ return list(self._cameras.keys())
54
+
55
+ def __repr__(self):
56
+ n = len(self.fused)
57
+ n_visible = sum(1 for c in self.fused if c is not None)
58
+ return f"NetworkCovariance(points={n}, visible={n_visible}, cameras={self.camera_ids})"
59
+
60
+
34
61
  class CameraNetwork:
35
62
  """A network of cameras with spatial covariance models.
36
63
 
@@ -88,52 +115,49 @@ class CameraNetwork:
88
115
  visible.append(cid)
89
116
  return visible
90
117
 
91
- def get_covariance(self, points, detection_sigma=0.0, sigma_binding=0.0,
92
- use_efov=True, camera_id=None):
93
- """Compute covariance for 3D points.
118
+ def get_covariance(self, points, detection_sigma=0.0, sigma_binding=0.0, use_efov=True):
119
+ """Compute per-camera and fused covariance for 3D points.
94
120
 
95
- If camera_id is None (default), fuses covariances from all visible cameras
96
- via information fusion. If camera_id is specified, computes covariance
97
- from that single camera (no visibility check, no fusion).
121
+ Visibility is always checked:
122
+ - use_efov=True (default): point's xy projection must fall inside the
123
+ camera's ground EFOV polygon
124
+ - use_efov=False: point must project onto valid image area
98
125
 
99
126
  Args:
100
127
  points: (N, 3) array of 3D points [x, y, z]
101
128
  detection_sigma: detection uncertainty (fraction of image width)
102
129
  sigma_binding: spatial binding uncertainty in meters
103
130
  use_efov: if True, use EFOV ground polygons for visibility;
104
- if False, use image projection bounds (ignored when camera_id is set)
105
- camera_id: if set, compute covariance from this camera only
131
+ if False, use image projection bounds
106
132
 
107
133
  Returns:
108
- list of N elements, each either a (3, 3) covariance matrix
109
- or None if the point is not visible to any camera
134
+ NetworkCovariance with:
135
+ result[camera_id] — list of N per-camera covariances (or None)
136
+ result.fused — list of N fused covariances (or None)
110
137
  """
111
138
  points = np.asarray(points, dtype=np.float64)
112
139
  if points.ndim != 2 or points.shape[1] != 3:
113
140
  raise ValueError(f"Expected (N, 3) array, got shape {points.shape}")
114
141
 
115
142
  N = len(points)
116
- result = [None] * N
117
-
118
- if camera_id is not None:
119
- cov_model = self.covariances[camera_id]
120
- for i in range(N):
121
- result[i] = cov_model.get_covariance(points[i], detection_sigma, sigma_binding)
122
- return result
143
+ per_camera = {cid: [None] * N for cid in self.cameras}
144
+ fused = [None] * N
123
145
 
124
146
  for i in range(N):
125
147
  p = points[i]
126
148
  visible_ids = self._visible_camera_ids(p, use_efov)
127
- if not visible_ids:
128
- continue
129
149
 
130
- covs = [
131
- self.covariances[cid].get_covariance(p, detection_sigma, sigma_binding)
132
- for cid in visible_ids
133
- ]
134
- result[i] = triangulation.fuse_covariances(covs)
150
+ covs_for_fusion = []
151
+ for cid in visible_ids:
152
+ cov = self.covariances[cid].get_covariance(p, detection_sigma, sigma_binding)
153
+ if cov is not None:
154
+ per_camera[cid][i] = cov
155
+ covs_for_fusion.append(cov)
156
+
157
+ if covs_for_fusion:
158
+ fused[i] = triangulation.fuse_covariances(covs_for_fusion)
135
159
 
136
- return result
160
+ return NetworkCovariance(per_camera, fused)
137
161
 
138
162
  def triangulate(self, observations, detection_sigma=0.0, sigma_binding=0.0, n_sigma=3.0):
139
163
  """Triangulate a 3D point from observations in multiple cameras.
@@ -187,6 +211,8 @@ class CameraNetwork:
187
211
  cov = self.covariances[cid].get_covariance(
188
212
  p_on_ray, detection_sigma, sigma_binding
189
213
  )
214
+ if cov is None:
215
+ return None
190
216
  ray_points.append(p_on_ray)
191
217
  ray_covs.append(cov)
192
218
 
@@ -197,6 +197,9 @@ class CameraSpatialCovariance:
197
197
  p: 3D point [x, y, z]
198
198
  detection_sigma: detection uncertainty (fraction of image width)
199
199
  sigma_binding: spatial binding uncertainty in meters
200
+
201
+ Returns:
202
+ (3, 3) covariance matrix, or None if the point is outside the image
200
203
  """
201
204
  p = np.asarray(p, dtype=np.float64)
202
205
  cam = self.camera
@@ -204,6 +207,9 @@ class CameraSpatialCovariance:
204
207
  ctd = cam.gnd_to_ctd(p.reshape(1, 3))[0]
205
208
  x_ctd, y_ctd = float(ctd[0]), float(ctd[1])
206
209
 
210
+ if not (0 <= x_ctd < cam.im_width and 0 <= y_ctd < cam.im_height):
211
+ return None
212
+
207
213
  d = np.linalg.norm(p - self.key_point)
208
214
  e_ray = cam.ctd_to_ray(np.array([[x_ctd, y_ctd]]))[0]
209
215
  J = cam.ctd_to_ray_jacobian(x_ctd, y_ctd)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camera-client
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Python SDK for camera calibration and projection transformations - handle lens distortion, coordinate transformations, and 3D ray casting with symbolic expressions.
5
5
  Author-email: Alexander Abramov <extremal.ru@gmail.com>
6
6
  License: MIT
@@ -133,9 +133,9 @@ net = CameraNetwork(cameras)
133
133
 
134
134
  ### Spatial covariance
135
135
 
136
- `get_covariance` computes 3x3 spatial covariance matrices for 3D points.
137
- The matrix encodes how pixel-level uncertainty (distortion + geometric calibration + detection)
138
- propagates into world-space uncertainty through the camera's ray geometry.
136
+ `get_covariance` computes 3x3 spatial covariance matrices for 3D points —
137
+ both per-camera and fused across all visible cameras in a single call.
138
+ Returns a `NetworkCovariance` object.
139
139
 
140
140
  ```python
141
141
  points = np.array([
@@ -143,15 +143,15 @@ points = np.array([
143
143
  [18.0, 6.0, 1.5],
144
144
  ])
145
145
 
146
- # Per-camera covariance (no fusion, no visibility check)
147
- covs_cam = net.get_covariance(points, camera_id=1177, detection_sigma=0.01)
148
- # covs_cam[i] is a (3, 3) covariance matrix from camera 1177
146
+ result = net.get_covariance(points, detection_sigma=0.01)
147
+
148
+ # Per-camera covariance (None if camera doesn't see the point)
149
+ cov_cam = result[1177][0] # camera 1177, point 0
149
150
 
150
151
  # Fused covariance from all visible cameras (information fusion)
151
- covs_fused = net.get_covariance(points, detection_sigma=0.01)
152
- # covs_fused[i] is (3, 3) fused covariance, or None if not visible to any camera
152
+ cov_fused = result.fused[0] # fused, point 0 (or None if no camera sees it)
153
153
 
154
- for i, cov in enumerate(covs_fused):
154
+ for i, cov in enumerate(result.fused):
155
155
  if cov is not None:
156
156
  stds = np.sqrt(np.linalg.eigvalsh(cov))
157
157
  print(f"Point {i}: σ = {stds[0]:.3f}m, {stds[1]:.3f}m, {stds[2]:.3f}m")
@@ -236,12 +236,19 @@ Out-of-bounds points return NaN. Height `h` can be a scalar or per-point (N,) ar
236
236
  | Method | Description |
237
237
  |--------|-------------|
238
238
  | `CameraNetwork(cameras)` | Create network from list of `CameraProjection` instances |
239
- | `get_covariance(points, ...)` | Fused 3x3 covariance for (N, 3) points from all visible cameras |
240
- | `get_covariance(points, camera_id=id, ...)` | Per-camera 3x3 covariance (no fusion, no visibility check) |
239
+ | `get_covariance(points, ...)` | Returns `NetworkCovariance` with per-camera and fused 3x3 covariances |
241
240
  | `triangulate(observations, ...)` | 3D point + covariance from `{camera_id: src_point}` observations |
242
241
 
243
242
  Common parameters: `detection_sigma` (float), `sigma_binding` (float), `use_efov` (bool), `n_sigma` (float).
244
243
 
244
+ ### `NetworkCovariance`
245
+
246
+ | Access | Description |
247
+ |--------|-------------|
248
+ | `result[camera_id]` | List of N per-camera covariances (3x3 or None) |
249
+ | `result.fused` | List of N fused covariances (3x3 or None) |
250
+ | `result.camera_ids` | List of camera IDs in the result |
251
+
245
252
  ### `triangulation` module
246
253
 
247
254
  | Function | Description |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "camera-client"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Python SDK for camera calibration and projection transformations - handle lens distortion, coordinate transformations, and 3D ray casting with symbolic expressions."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -1,5 +0,0 @@
1
- from camera_client.client import CameraProjection
2
- from camera_client.camera_network import CameraNetwork
3
- from camera_client import triangulation
4
-
5
- __all__ = ["CameraProjection", "CameraNetwork", "triangulation"]
File without changes
File without changes
File without changes
File without changes