camera-client 0.2.4__tar.gz → 0.2.6__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.
- {camera_client-0.2.4/camera_client.egg-info → camera_client-0.2.6}/PKG-INFO +65 -1
- {camera_client-0.2.4 → camera_client-0.2.6}/README.md +64 -0
- camera_client-0.2.6/camera_client/__main__.py +188 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client/client.py +81 -0
- camera_client-0.2.6/camera_client/script.py +7 -0
- {camera_client-0.2.4 → camera_client-0.2.6/camera_client.egg-info}/PKG-INFO +65 -1
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client.egg-info/SOURCES.txt +2 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/pyproject.toml +1 -1
- {camera_client-0.2.4 → camera_client-0.2.6}/LICENSE +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/MANIFEST.in +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client/__init__.py +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client/loading.py +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client.egg-info/dependency_links.txt +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client.egg-info/entry_points.txt +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client.egg-info/requires.txt +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/camera_client.egg-info/top_level.txt +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/requirements.txt +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/setup.cfg +0 -0
- {camera_client-0.2.4 → camera_client-0.2.6}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camera-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
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
|
|
@@ -50,6 +50,18 @@ Install from PyPI:
|
|
|
50
50
|
pip install camera-client
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## CLI Usage
|
|
54
|
+
|
|
55
|
+
Download camera calibration archives from URL:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Download single archive
|
|
59
|
+
python -m camera_client get_camera_archive https://example.com/camera.npz
|
|
60
|
+
|
|
61
|
+
# Download from file with URLs (one per line, non-URL lines ignored)
|
|
62
|
+
python -m camera_client get_camera_archive -f urls.txt -o ./archives
|
|
63
|
+
```
|
|
64
|
+
|
|
53
65
|
## Quick Start
|
|
54
66
|
|
|
55
67
|
```python
|
|
@@ -316,6 +328,58 @@ Get the camera position (key-point) in world space.
|
|
|
316
328
|
**Returns:**
|
|
317
329
|
- `np.ndarray`: Shape (3,) array with [x, y, z] camera position
|
|
318
330
|
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### `get_ctd_points_context(ctd_points)`
|
|
334
|
+
|
|
335
|
+
Get scale context values for corrected (CTD) image points.
|
|
336
|
+
|
|
337
|
+
**Parameters:**
|
|
338
|
+
- `ctd_points` (np.ndarray): Shape (N, 2) array of [x, y] corrected coordinates
|
|
339
|
+
|
|
340
|
+
**Returns:**
|
|
341
|
+
- `dict`: Dictionary with keys:
|
|
342
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
343
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
344
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
345
|
+
|
|
346
|
+
**Note:** Out-of-bounds points will have NaN values
|
|
347
|
+
|
|
348
|
+
**Example:**
|
|
349
|
+
```python
|
|
350
|
+
ctd_points = np.array([[640, 480], [800, 600]])
|
|
351
|
+
context = camera.get_ctd_points_context(ctd_points)
|
|
352
|
+
print(context['wscale']) # Width scale at each point
|
|
353
|
+
print(context['hscale']) # Height scale at each point
|
|
354
|
+
print(context['vangle']) # Vertical angle at each point
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
### `get_src_points_context(src_points)`
|
|
360
|
+
|
|
361
|
+
Get scale context values for source (distorted) image points.
|
|
362
|
+
|
|
363
|
+
**Parameters:**
|
|
364
|
+
- `src_points` (np.ndarray): Shape (N, 2) array of [x, y] source coordinates
|
|
365
|
+
|
|
366
|
+
**Returns:**
|
|
367
|
+
- `dict`: Dictionary with keys:
|
|
368
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
369
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
370
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
371
|
+
|
|
372
|
+
**Note:** Internally converts source points to CTD coordinates first, then retrieves context
|
|
373
|
+
|
|
374
|
+
**Example:**
|
|
375
|
+
```python
|
|
376
|
+
src_points = np.array([[640, 480], [800, 600]])
|
|
377
|
+
context = camera.get_src_points_context(src_points)
|
|
378
|
+
print(context['wscale']) # Width scale at each point
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
319
383
|
## Calibration File Format
|
|
320
384
|
|
|
321
385
|
The calibration file is a NumPy `.npz` archive containing:
|
|
@@ -20,6 +20,18 @@ Install from PyPI:
|
|
|
20
20
|
pip install camera-client
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## CLI Usage
|
|
24
|
+
|
|
25
|
+
Download camera calibration archives from URL:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Download single archive
|
|
29
|
+
python -m camera_client get_camera_archive https://example.com/camera.npz
|
|
30
|
+
|
|
31
|
+
# Download from file with URLs (one per line, non-URL lines ignored)
|
|
32
|
+
python -m camera_client get_camera_archive -f urls.txt -o ./archives
|
|
33
|
+
```
|
|
34
|
+
|
|
23
35
|
## Quick Start
|
|
24
36
|
|
|
25
37
|
```python
|
|
@@ -286,6 +298,58 @@ Get the camera position (key-point) in world space.
|
|
|
286
298
|
**Returns:**
|
|
287
299
|
- `np.ndarray`: Shape (3,) array with [x, y, z] camera position
|
|
288
300
|
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### `get_ctd_points_context(ctd_points)`
|
|
304
|
+
|
|
305
|
+
Get scale context values for corrected (CTD) image points.
|
|
306
|
+
|
|
307
|
+
**Parameters:**
|
|
308
|
+
- `ctd_points` (np.ndarray): Shape (N, 2) array of [x, y] corrected coordinates
|
|
309
|
+
|
|
310
|
+
**Returns:**
|
|
311
|
+
- `dict`: Dictionary with keys:
|
|
312
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
313
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
314
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
315
|
+
|
|
316
|
+
**Note:** Out-of-bounds points will have NaN values
|
|
317
|
+
|
|
318
|
+
**Example:**
|
|
319
|
+
```python
|
|
320
|
+
ctd_points = np.array([[640, 480], [800, 600]])
|
|
321
|
+
context = camera.get_ctd_points_context(ctd_points)
|
|
322
|
+
print(context['wscale']) # Width scale at each point
|
|
323
|
+
print(context['hscale']) # Height scale at each point
|
|
324
|
+
print(context['vangle']) # Vertical angle at each point
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### `get_src_points_context(src_points)`
|
|
330
|
+
|
|
331
|
+
Get scale context values for source (distorted) image points.
|
|
332
|
+
|
|
333
|
+
**Parameters:**
|
|
334
|
+
- `src_points` (np.ndarray): Shape (N, 2) array of [x, y] source coordinates
|
|
335
|
+
|
|
336
|
+
**Returns:**
|
|
337
|
+
- `dict`: Dictionary with keys:
|
|
338
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
339
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
340
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
341
|
+
|
|
342
|
+
**Note:** Internally converts source points to CTD coordinates first, then retrieves context
|
|
343
|
+
|
|
344
|
+
**Example:**
|
|
345
|
+
```python
|
|
346
|
+
src_points = np.array([[640, 480], [800, 600]])
|
|
347
|
+
context = camera.get_src_points_context(src_points)
|
|
348
|
+
print(context['wscale']) # Width scale at each point
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
289
353
|
## Calibration File Format
|
|
290
354
|
|
|
291
355
|
The calibration file is a NumPy `.npz` archive containing:
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Command-line interface for camera-client package."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from urllib.request import urlopen, Request
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_url(text: str) -> bool:
|
|
12
|
+
"""
|
|
13
|
+
Check if a string is a valid URL.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
text: String to check
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
True if the string looks like a URL, False otherwise
|
|
20
|
+
"""
|
|
21
|
+
text = text.strip()
|
|
22
|
+
return text.startswith('http://') or text.startswith('https://')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def download_archive(url: str, output_dir: str = ".", silent: bool = False) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Download camera calibration archive from URL to the specified directory.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
url: URL to download the archive from
|
|
31
|
+
output_dir: Directory to save the downloaded file (default: current directory)
|
|
32
|
+
silent: If True, suppress success messages (errors still printed)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
True if download succeeded, False otherwise
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
# Parse the URL to extract filename from path or Content-Disposition header
|
|
39
|
+
parsed_url = urlparse(url)
|
|
40
|
+
|
|
41
|
+
# Create request with headers
|
|
42
|
+
request = Request(url, headers={'User-Agent': 'camera-client'})
|
|
43
|
+
|
|
44
|
+
# Open URL and get response
|
|
45
|
+
if not silent:
|
|
46
|
+
print(f"Downloading from: {url}")
|
|
47
|
+
with urlopen(request) as response:
|
|
48
|
+
# Try to get filename from Content-Disposition header
|
|
49
|
+
content_disposition = response.headers.get('Content-Disposition', '')
|
|
50
|
+
filename = None
|
|
51
|
+
|
|
52
|
+
if 'filename=' in content_disposition:
|
|
53
|
+
# Extract filename from Content-Disposition header
|
|
54
|
+
parts = content_disposition.split('filename=')
|
|
55
|
+
if len(parts) > 1:
|
|
56
|
+
filename = parts[1].strip('"')
|
|
57
|
+
|
|
58
|
+
# Fallback to URL path if no Content-Disposition
|
|
59
|
+
if not filename:
|
|
60
|
+
path_parts = parsed_url.path.split('/')
|
|
61
|
+
filename = path_parts[-1] if path_parts[-1] else 'camera_archive.npz'
|
|
62
|
+
|
|
63
|
+
# Ensure .npz extension
|
|
64
|
+
if not filename.endswith('.npz'):
|
|
65
|
+
filename += '.npz'
|
|
66
|
+
|
|
67
|
+
# Create output path
|
|
68
|
+
output_path = Path(output_dir) / filename
|
|
69
|
+
|
|
70
|
+
# Download file
|
|
71
|
+
if not silent:
|
|
72
|
+
print(f"Saving to: {output_path}")
|
|
73
|
+
with open(output_path, 'wb') as f:
|
|
74
|
+
f.write(response.read())
|
|
75
|
+
|
|
76
|
+
if not silent:
|
|
77
|
+
print(f"Successfully downloaded: {filename}")
|
|
78
|
+
print(f"File size: {output_path.stat().st_size} bytes")
|
|
79
|
+
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
print(f"Error downloading archive from {url}: {e}", file=sys.stderr)
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def download_from_file(file_path: str, output_dir: str = ".") -> None:
|
|
88
|
+
"""
|
|
89
|
+
Download multiple camera calibration archives from URLs listed in a file.
|
|
90
|
+
|
|
91
|
+
Non-URL lines (comments, notes, etc.) are automatically skipped.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
file_path: Path to file containing URLs (one per line, non-URL lines are ignored)
|
|
95
|
+
output_dir: Directory to save the downloaded files (default: current directory)
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
with open(file_path, 'r') as f:
|
|
99
|
+
# Filter only lines that are valid URLs
|
|
100
|
+
urls = [line.strip() for line in f if is_url(line)]
|
|
101
|
+
|
|
102
|
+
if not urls:
|
|
103
|
+
print(f"No URLs found in {file_path}", file=sys.stderr)
|
|
104
|
+
sys.exit(1)
|
|
105
|
+
|
|
106
|
+
print(f"Found {len(urls)} URL(s) in {file_path}")
|
|
107
|
+
print(f"Downloading to: {output_dir}\n")
|
|
108
|
+
|
|
109
|
+
success_count = 0
|
|
110
|
+
failed_count = 0
|
|
111
|
+
|
|
112
|
+
for i, url in enumerate(urls, 1):
|
|
113
|
+
print(f"[{i}/{len(urls)}] Processing: {url}")
|
|
114
|
+
if download_archive(url, output_dir, silent=False):
|
|
115
|
+
success_count += 1
|
|
116
|
+
else:
|
|
117
|
+
failed_count += 1
|
|
118
|
+
print() # Empty line between downloads
|
|
119
|
+
|
|
120
|
+
# Summary
|
|
121
|
+
print("=" * 50)
|
|
122
|
+
print(f"Download complete: {success_count} succeeded, {failed_count} failed")
|
|
123
|
+
|
|
124
|
+
if failed_count > 0:
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
except FileNotFoundError:
|
|
128
|
+
print(f"Error: File not found: {file_path}", file=sys.stderr)
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"Error reading file {file_path}: {e}", file=sys.stderr)
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def main():
|
|
136
|
+
"""Main entry point for camera-client CLI."""
|
|
137
|
+
parser = argparse.ArgumentParser(
|
|
138
|
+
prog='camera-client',
|
|
139
|
+
description='Camera calibration client utilities'
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
|
143
|
+
|
|
144
|
+
# get_camera_archive command
|
|
145
|
+
download_parser = subparsers.add_parser(
|
|
146
|
+
'get_camera_archive',
|
|
147
|
+
help='Get camera calibration archive(s) from URL or file'
|
|
148
|
+
)
|
|
149
|
+
download_parser.add_argument(
|
|
150
|
+
'url',
|
|
151
|
+
nargs='?',
|
|
152
|
+
help='URL to download the archive from'
|
|
153
|
+
)
|
|
154
|
+
download_parser.add_argument(
|
|
155
|
+
'-f', '--file',
|
|
156
|
+
help='File containing URLs (one per line)'
|
|
157
|
+
)
|
|
158
|
+
download_parser.add_argument(
|
|
159
|
+
'-o', '--output-dir',
|
|
160
|
+
default='.',
|
|
161
|
+
help='Output directory (default: current directory)'
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
args = parser.parse_args()
|
|
165
|
+
|
|
166
|
+
if args.command == 'get_camera_archive':
|
|
167
|
+
# Check that either url or file is provided (but not both)
|
|
168
|
+
if args.url and args.file:
|
|
169
|
+
print("Error: Cannot specify both URL and file. Use either positional URL or -f/--file option.", file=sys.stderr)
|
|
170
|
+
sys.exit(1)
|
|
171
|
+
elif not args.url and not args.file:
|
|
172
|
+
print("Error: Must specify either URL or file with -f/--file option.", file=sys.stderr)
|
|
173
|
+
download_parser.print_help()
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
# Process based on input type
|
|
177
|
+
if args.file:
|
|
178
|
+
download_from_file(args.file, args.output_dir)
|
|
179
|
+
else:
|
|
180
|
+
success = download_archive(args.url, args.output_dir)
|
|
181
|
+
sys.exit(0 if success else 1)
|
|
182
|
+
else:
|
|
183
|
+
parser.print_help()
|
|
184
|
+
sys.exit(1)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == '__main__':
|
|
188
|
+
main()
|
|
@@ -26,6 +26,7 @@ class CameraProjection:
|
|
|
26
26
|
- ctd2src: Corrected to source distortion map (H x W x 2)
|
|
27
27
|
- x_gnd, y_gnd, z_gnd: String expressions for ctd -> ground
|
|
28
28
|
- x_im, y_im: String expressions for ground -> ctd
|
|
29
|
+
- map_scale_h, map_scale_w, map_scale_vang: Scale context maps (H x W)
|
|
29
30
|
"""
|
|
30
31
|
data = cam_archive_data
|
|
31
32
|
|
|
@@ -47,6 +48,11 @@ class CameraProjection:
|
|
|
47
48
|
self.src2ctd_points_map = data["src2ctd"]
|
|
48
49
|
self.ctd2src_points_map = data["ctd2src"]
|
|
49
50
|
|
|
51
|
+
# Store scale context maps
|
|
52
|
+
self.map_scale_h = data["map_scale_h"]
|
|
53
|
+
self.map_scale_w = data["map_scale_w"]
|
|
54
|
+
self.map_scale_vang = data["map_scale_vang"]
|
|
55
|
+
|
|
50
56
|
self.im_size = self.src2ctd_points_map.shape[:2]
|
|
51
57
|
|
|
52
58
|
# Compile transformation expressions for ctd -> gnd
|
|
@@ -294,6 +300,81 @@ class CameraProjection:
|
|
|
294
300
|
ctd_points = self.src_to_ctd(points)
|
|
295
301
|
return self.ctd_to_ray(ctd_points)
|
|
296
302
|
|
|
303
|
+
def get_ctd_points_context(self, ctd_points):
|
|
304
|
+
"""
|
|
305
|
+
Get scale context values for corrected (CTD) image points.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
ctd_points: (N, 2) array of corrected points [[x1, y1], [x2, y2], ...]
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Dictionary with keys:
|
|
312
|
+
- wscale: (N,) array of width scale values
|
|
313
|
+
- hscale: (N,) array of height scale values
|
|
314
|
+
- vangle: (N,) array of vertical angle values
|
|
315
|
+
Out-of-bounds points will have NaN values.
|
|
316
|
+
"""
|
|
317
|
+
ctd_points = np.asarray(ctd_points, dtype=float)
|
|
318
|
+
if ctd_points.ndim != 2 or ctd_points.shape[1] != 2:
|
|
319
|
+
raise ValueError(f"Expected (N, 2) array, got shape {ctd_points.shape}")
|
|
320
|
+
|
|
321
|
+
N = len(ctd_points)
|
|
322
|
+
wscale = np.full(N, np.nan, dtype=float)
|
|
323
|
+
hscale = np.full(N, np.nan, dtype=float)
|
|
324
|
+
vangle = np.full(N, np.nan, dtype=float)
|
|
325
|
+
|
|
326
|
+
# Check for NaN input points
|
|
327
|
+
valid_input = ~np.isnan(ctd_points).any(axis=1)
|
|
328
|
+
|
|
329
|
+
if not valid_input.any():
|
|
330
|
+
return {"wscale": wscale, "hscale": hscale, "vangle": vangle}
|
|
331
|
+
|
|
332
|
+
# Round to integer coordinates
|
|
333
|
+
p_int = np.round(ctd_points[valid_input]).astype(int)
|
|
334
|
+
|
|
335
|
+
# Vectorized bounds checking
|
|
336
|
+
in_bounds = (
|
|
337
|
+
(p_int[:, 0] >= 0)
|
|
338
|
+
& (p_int[:, 0] < self.map_scale_w.shape[1])
|
|
339
|
+
& (p_int[:, 1] >= 0)
|
|
340
|
+
& (p_int[:, 1] < self.map_scale_w.shape[0])
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Create mask for points that are both valid input and in bounds
|
|
344
|
+
valid_indices = np.where(valid_input)[0]
|
|
345
|
+
final_valid_indices = valid_indices[in_bounds]
|
|
346
|
+
valid_p_int = p_int[in_bounds]
|
|
347
|
+
|
|
348
|
+
# Vectorized lookup using advanced indexing (y, x indexing)
|
|
349
|
+
wscale[final_valid_indices] = self.map_scale_w[
|
|
350
|
+
valid_p_int[:, 1], valid_p_int[:, 0]
|
|
351
|
+
]
|
|
352
|
+
hscale[final_valid_indices] = self.map_scale_h[
|
|
353
|
+
valid_p_int[:, 1], valid_p_int[:, 0]
|
|
354
|
+
]
|
|
355
|
+
vangle[final_valid_indices] = self.map_scale_vang[
|
|
356
|
+
valid_p_int[:, 1], valid_p_int[:, 0]
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
return {"wscale": wscale, "hscale": hscale, "vangle": vangle}
|
|
360
|
+
|
|
361
|
+
def get_src_points_context(self, src_points):
|
|
362
|
+
"""
|
|
363
|
+
Get scale context values for source (distorted) image points.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
src_points: (N, 2) array of source points [[x1, y1], [x2, y2], ...]
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Dictionary with keys:
|
|
370
|
+
- wscale: (N,) array of width scale values
|
|
371
|
+
- hscale: (N,) array of height scale values
|
|
372
|
+
- vangle: (N,) array of vertical angle values
|
|
373
|
+
Out-of-bounds points will have NaN values.
|
|
374
|
+
"""
|
|
375
|
+
ctd_points = self.src_to_ctd(src_points)
|
|
376
|
+
return self.get_ctd_points_context(ctd_points)
|
|
377
|
+
|
|
297
378
|
@classmethod
|
|
298
379
|
def load(cls, archive_path):
|
|
299
380
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camera-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
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
|
|
@@ -50,6 +50,18 @@ Install from PyPI:
|
|
|
50
50
|
pip install camera-client
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## CLI Usage
|
|
54
|
+
|
|
55
|
+
Download camera calibration archives from URL:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Download single archive
|
|
59
|
+
python -m camera_client get_camera_archive https://example.com/camera.npz
|
|
60
|
+
|
|
61
|
+
# Download from file with URLs (one per line, non-URL lines ignored)
|
|
62
|
+
python -m camera_client get_camera_archive -f urls.txt -o ./archives
|
|
63
|
+
```
|
|
64
|
+
|
|
53
65
|
## Quick Start
|
|
54
66
|
|
|
55
67
|
```python
|
|
@@ -316,6 +328,58 @@ Get the camera position (key-point) in world space.
|
|
|
316
328
|
**Returns:**
|
|
317
329
|
- `np.ndarray`: Shape (3,) array with [x, y, z] camera position
|
|
318
330
|
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### `get_ctd_points_context(ctd_points)`
|
|
334
|
+
|
|
335
|
+
Get scale context values for corrected (CTD) image points.
|
|
336
|
+
|
|
337
|
+
**Parameters:**
|
|
338
|
+
- `ctd_points` (np.ndarray): Shape (N, 2) array of [x, y] corrected coordinates
|
|
339
|
+
|
|
340
|
+
**Returns:**
|
|
341
|
+
- `dict`: Dictionary with keys:
|
|
342
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
343
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
344
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
345
|
+
|
|
346
|
+
**Note:** Out-of-bounds points will have NaN values
|
|
347
|
+
|
|
348
|
+
**Example:**
|
|
349
|
+
```python
|
|
350
|
+
ctd_points = np.array([[640, 480], [800, 600]])
|
|
351
|
+
context = camera.get_ctd_points_context(ctd_points)
|
|
352
|
+
print(context['wscale']) # Width scale at each point
|
|
353
|
+
print(context['hscale']) # Height scale at each point
|
|
354
|
+
print(context['vangle']) # Vertical angle at each point
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
### `get_src_points_context(src_points)`
|
|
360
|
+
|
|
361
|
+
Get scale context values for source (distorted) image points.
|
|
362
|
+
|
|
363
|
+
**Parameters:**
|
|
364
|
+
- `src_points` (np.ndarray): Shape (N, 2) array of [x, y] source coordinates
|
|
365
|
+
|
|
366
|
+
**Returns:**
|
|
367
|
+
- `dict`: Dictionary with keys:
|
|
368
|
+
- `wscale` (np.ndarray): Shape (N,) width scale values
|
|
369
|
+
- `hscale` (np.ndarray): Shape (N,) height scale values
|
|
370
|
+
- `vangle` (np.ndarray): Shape (N,) vertical angle values (radians)
|
|
371
|
+
|
|
372
|
+
**Note:** Internally converts source points to CTD coordinates first, then retrieves context
|
|
373
|
+
|
|
374
|
+
**Example:**
|
|
375
|
+
```python
|
|
376
|
+
src_points = np.array([[640, 480], [800, 600]])
|
|
377
|
+
context = camera.get_src_points_context(src_points)
|
|
378
|
+
print(context['wscale']) # Width scale at each point
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
319
383
|
## Calibration File Format
|
|
320
384
|
|
|
321
385
|
The calibration file is a NumPy `.npz` archive containing:
|
|
@@ -5,8 +5,10 @@ pyproject.toml
|
|
|
5
5
|
requirements.txt
|
|
6
6
|
setup.py
|
|
7
7
|
camera_client/__init__.py
|
|
8
|
+
camera_client/__main__.py
|
|
8
9
|
camera_client/client.py
|
|
9
10
|
camera_client/loading.py
|
|
11
|
+
camera_client/script.py
|
|
10
12
|
camera_client.egg-info/PKG-INFO
|
|
11
13
|
camera_client.egg-info/SOURCES.txt
|
|
12
14
|
camera_client.egg-info/dependency_links.txt
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "camera-client"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.6"
|
|
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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|