camera-client 0.3.0__tar.gz → 0.3.2__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.2}/PKG-INFO +20 -12
  2. {camera_client-0.3.0 → camera_client-0.3.2}/README.md +19 -11
  3. camera_client-0.3.2/camera_client/__init__.py +5 -0
  4. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/camera_network.py +55 -25
  5. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/error_model.py +6 -0
  6. {camera_client-0.3.0 → camera_client-0.3.2/camera_client.egg-info}/PKG-INFO +20 -12
  7. {camera_client-0.3.0 → camera_client-0.3.2}/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.2}/LICENSE +0 -0
  10. {camera_client-0.3.0 → camera_client-0.3.2}/MANIFEST.in +0 -0
  11. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/__main__.py +0 -0
  12. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/client.py +0 -0
  13. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/loading.py +0 -0
  14. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/script.py +0 -0
  15. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client/triangulation.py +0 -0
  16. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client.egg-info/SOURCES.txt +0 -0
  17. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client.egg-info/dependency_links.txt +0 -0
  18. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client.egg-info/entry_points.txt +0 -0
  19. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client.egg-info/requires.txt +0 -0
  20. {camera_client-0.3.0 → camera_client-0.3.2}/camera_client.egg-info/top_level.txt +0 -0
  21. {camera_client-0.3.0 → camera_client-0.3.2}/requirements.txt +0 -0
  22. {camera_client-0.3.0 → camera_client-0.3.2}/setup.cfg +0 -0
  23. {camera_client-0.3.0 → camera_client-0.3.2}/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.2
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,20 @@ 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 all camera IDs in the network |
251
+ | `result.visible_camera_ids(i=0)` | List of camera IDs that see point i |
252
+
245
253
  ### `triangulation` module
246
254
 
247
255
  | 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,20 @@ 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 all camera IDs in the network |
221
+ | `result.visible_camera_ids(i=0)` | List of camera IDs that see point i |
222
+
215
223
  ### `triangulation` module
216
224
 
217
225
  | 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,37 @@ 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 visible_camera_ids(self, point_index=0):
56
+ """Return list of camera_ids that see the given point."""
57
+ return [cid for cid, covs in self._cameras.items() if covs[point_index] is not None]
58
+
59
+ def __repr__(self):
60
+ n = len(self.fused)
61
+ n_visible = sum(1 for c in self.fused if c is not None)
62
+ return f"NetworkCovariance(points={n}, visible={n_visible}, cameras={self.camera_ids})"
63
+
64
+
34
65
  class CameraNetwork:
35
66
  """A network of cameras with spatial covariance models.
36
67
 
@@ -88,52 +119,49 @@ class CameraNetwork:
88
119
  visible.append(cid)
89
120
  return visible
90
121
 
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.
122
+ def get_covariance(self, points, detection_sigma=0.0, sigma_binding=0.0, use_efov=True):
123
+ """Compute per-camera and fused covariance for 3D points.
94
124
 
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).
125
+ Visibility is always checked:
126
+ - use_efov=True (default): point's xy projection must fall inside the
127
+ camera's ground EFOV polygon
128
+ - use_efov=False: point must project onto valid image area
98
129
 
99
130
  Args:
100
131
  points: (N, 3) array of 3D points [x, y, z]
101
132
  detection_sigma: detection uncertainty (fraction of image width)
102
133
  sigma_binding: spatial binding uncertainty in meters
103
134
  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
135
+ if False, use image projection bounds
106
136
 
107
137
  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
138
+ NetworkCovariance with:
139
+ result[camera_id] — list of N per-camera covariances (or None)
140
+ result.fused — list of N fused covariances (or None)
110
141
  """
111
142
  points = np.asarray(points, dtype=np.float64)
112
143
  if points.ndim != 2 or points.shape[1] != 3:
113
144
  raise ValueError(f"Expected (N, 3) array, got shape {points.shape}")
114
145
 
115
146
  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
147
+ per_camera = {cid: [None] * N for cid in self.cameras}
148
+ fused = [None] * N
123
149
 
124
150
  for i in range(N):
125
151
  p = points[i]
126
152
  visible_ids = self._visible_camera_ids(p, use_efov)
127
- if not visible_ids:
128
- continue
129
153
 
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)
154
+ covs_for_fusion = []
155
+ for cid in visible_ids:
156
+ cov = self.covariances[cid].get_covariance(p, detection_sigma, sigma_binding)
157
+ if cov is not None:
158
+ per_camera[cid][i] = cov
159
+ covs_for_fusion.append(cov)
160
+
161
+ if covs_for_fusion:
162
+ fused[i] = triangulation.fuse_covariances(covs_for_fusion)
135
163
 
136
- return result
164
+ return NetworkCovariance(per_camera, fused)
137
165
 
138
166
  def triangulate(self, observations, detection_sigma=0.0, sigma_binding=0.0, n_sigma=3.0):
139
167
  """Triangulate a 3D point from observations in multiple cameras.
@@ -187,6 +215,8 @@ class CameraNetwork:
187
215
  cov = self.covariances[cid].get_covariance(
188
216
  p_on_ray, detection_sigma, sigma_binding
189
217
  )
218
+ if cov is None:
219
+ return None
190
220
  ray_points.append(p_on_ray)
191
221
  ray_covs.append(cov)
192
222
 
@@ -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.2
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,20 @@ 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 all camera IDs in the network |
251
+ | `result.visible_camera_ids(i=0)` | List of camera IDs that see point i |
252
+
245
253
  ### `triangulation` module
246
254
 
247
255
  | 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.2"
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