camerakit 1.0.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.
- camerakit-1.0.0/LICENSE +21 -0
- camerakit-1.0.0/PKG-INFO +104 -0
- camerakit-1.0.0/README.md +48 -0
- camerakit-1.0.0/camerakit/__init__.py +15 -0
- camerakit-1.0.0/camerakit/calibration.py +180 -0
- camerakit-1.0.0/camerakit/capture.py +390 -0
- camerakit-1.0.0/camerakit/core/__init__.py +9 -0
- camerakit-1.0.0/camerakit/core/calibration_data.py +148 -0
- camerakit-1.0.0/camerakit/core/calibration_file.py +286 -0
- camerakit-1.0.0/camerakit/core/camera.py +113 -0
- camerakit-1.0.0/camerakit/utils/__init__.py +13 -0
- camerakit-1.0.0/camerakit/utils/calib.py +106 -0
- camerakit-1.0.0/camerakit/utils/calibration.py +1380 -0
- camerakit-1.0.0/camerakit/utils/common.py +285 -0
- camerakit-1.0.0/camerakit/utils/pose/__init__.py +7 -0
- camerakit-1.0.0/camerakit/utils/pose/from_board_outer.py +75 -0
- camerakit-1.0.0/camerakit/utils/pose/from_keypoints.py +190 -0
- camerakit-1.0.0/camerakit/utils/sync.py +103 -0
- camerakit-1.0.0/camerakit/version.py +1 -0
- camerakit-1.0.0/camerakit.egg-info/PKG-INFO +104 -0
- camerakit-1.0.0/camerakit.egg-info/SOURCES.txt +26 -0
- camerakit-1.0.0/camerakit.egg-info/dependency_links.txt +1 -0
- camerakit-1.0.0/camerakit.egg-info/entry_points.txt +3 -0
- camerakit-1.0.0/camerakit.egg-info/requires.txt +6 -0
- camerakit-1.0.0/camerakit.egg-info/top_level.txt +1 -0
- camerakit-1.0.0/setup.cfg +49 -0
- camerakit-1.0.0/setup.py +4 -0
camerakit-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Saif Khan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
camerakit-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: camerakit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: CLI Tools and Python API for Camera Calibration and Synchronized Capture
|
|
5
|
+
Home-page: https://github.com/saifkhichi96/camerakit
|
|
6
|
+
Author: Saif Khan
|
|
7
|
+
Author-email: saifkhichi96@gmail.com
|
|
8
|
+
License: MIT License
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/saifkhichi96/camerakit/issues
|
|
10
|
+
Keywords: camera,calibration,synchronized capture
|
|
11
|
+
Platform: any
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
22
|
+
Classifier: Topic :: Multimedia :: Video
|
|
23
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: easydict
|
|
28
|
+
Requires-Dist: matplotlib
|
|
29
|
+
Requires-Dist: mpl_interactions
|
|
30
|
+
Requires-Dist: numpy
|
|
31
|
+
Requires-Dist: opencv-python
|
|
32
|
+
Requires-Dist: toml
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# CameraKit: CLI Tools and Python API for Camera Calibration and Synchronized Capture
|
|
36
|
+
|
|
37
|
+
CameraKit is a Python package that provides command-line tools and an API for camera calibration and synchronized capture. It has the following features:
|
|
38
|
+
|
|
39
|
+
- Camera calibration using checkerboard images or video
|
|
40
|
+
- Synchronized capture from multiple cameras
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
You can install CameraKit using pip:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install camerakit
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Command-Line Interface
|
|
53
|
+
|
|
54
|
+
You can use the command-line interface to perform camera calibration and capture images. Here are some examples:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Calibrate a camera using checkerboard images
|
|
58
|
+
ck-calibrate --images path/to/checkerboard/images --output calibration.toml
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Capture images from multiple cameras
|
|
63
|
+
ck-capture --cameras camera1,camera2 --output path/to/output
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Python API
|
|
67
|
+
You can also use CameraKit as a Python library. Here is an example of how to use it:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from camerakit.calibration import calibrate_camera
|
|
71
|
+
calibration_data = calibrate_camera(
|
|
72
|
+
images='path/to/checkerboard/images',
|
|
73
|
+
output='calibration.toml'
|
|
74
|
+
)
|
|
75
|
+
print(calibration_data)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. You can also submit a pull request with your changes.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
CameraKit is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
|
83
|
+
|
|
84
|
+
MIT License
|
|
85
|
+
|
|
86
|
+
Copyright (c) 2025 Saif Khan
|
|
87
|
+
|
|
88
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
89
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
90
|
+
in the Software without restriction, including without limitation the rights
|
|
91
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
92
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
93
|
+
furnished to do so, subject to the following conditions:
|
|
94
|
+
|
|
95
|
+
The above copyright notice and this permission notice shall be included in all
|
|
96
|
+
copies or substantial portions of the Software.
|
|
97
|
+
|
|
98
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
99
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
100
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
101
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
102
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
103
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
104
|
+
SOFTWARE.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CameraKit: CLI Tools and Python API for Camera Calibration and Synchronized Capture
|
|
2
|
+
|
|
3
|
+
CameraKit is a Python package that provides command-line tools and an API for camera calibration and synchronized capture. It has the following features:
|
|
4
|
+
|
|
5
|
+
- Camera calibration using checkerboard images or video
|
|
6
|
+
- Synchronized capture from multiple cameras
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
You can install CameraKit using pip:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install camerakit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Command-Line Interface
|
|
19
|
+
|
|
20
|
+
You can use the command-line interface to perform camera calibration and capture images. Here are some examples:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Calibrate a camera using checkerboard images
|
|
24
|
+
ck-calibrate --images path/to/checkerboard/images --output calibration.toml
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Capture images from multiple cameras
|
|
29
|
+
ck-capture --cameras camera1,camera2 --output path/to/output
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Python API
|
|
33
|
+
You can also use CameraKit as a Python library. Here is an example of how to use it:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from camerakit.calibration import calibrate_camera
|
|
37
|
+
calibration_data = calibrate_camera(
|
|
38
|
+
images='path/to/checkerboard/images',
|
|
39
|
+
output='calibration.toml'
|
|
40
|
+
)
|
|
41
|
+
print(calibration_data)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Contributing
|
|
45
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. You can also submit a pull request with your changes.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
CameraKit is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .calibration import run_calibration
|
|
2
|
+
from .core import CalibrationData, CalibrationFile, CameraInfo
|
|
3
|
+
from .utils import SynchronizedVideoCapture, find_cameras, get_camera_properties
|
|
4
|
+
from .version import __version__
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CalibrationData",
|
|
9
|
+
"CameraInfo",
|
|
10
|
+
"CalibrationFile",
|
|
11
|
+
"SynchronizedVideoCapture",
|
|
12
|
+
"find_cameras",
|
|
13
|
+
"get_camera_properties",
|
|
14
|
+
"run_calibration",
|
|
15
|
+
]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
import toml
|
|
7
|
+
|
|
8
|
+
from .utils import calibrate_cams_all, setup_logging
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def recursive_update(dict_to_update, dict_with_new_values):
|
|
12
|
+
"""
|
|
13
|
+
Update nested dictionaries without overwriting existing keys in any level of nesting
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
dict_to_update = {'key': {'key_1': 'val_1', 'key_2': 'val_2'}}
|
|
17
|
+
dict_with_new_values = {'key': {'key_1': 'val_1_new'}}
|
|
18
|
+
returns {'key': {'key_1': 'val_1_new', 'key_2': 'val_2'}}
|
|
19
|
+
while dict_to_update.update(dict_with_new_values) would return {'key': {'key_1': 'val_1_new'}}
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
for key, value in dict_with_new_values.items():
|
|
23
|
+
if (
|
|
24
|
+
key in dict_to_update
|
|
25
|
+
and isinstance(value, dict)
|
|
26
|
+
and isinstance(dict_to_update[key], dict)
|
|
27
|
+
):
|
|
28
|
+
# Recursively update nested dictionaries
|
|
29
|
+
dict_to_update[key] = recursive_update(dict_to_update[key], value)
|
|
30
|
+
else:
|
|
31
|
+
# Update or add new key-value pairs
|
|
32
|
+
dict_to_update[key] = value
|
|
33
|
+
|
|
34
|
+
return dict_to_update
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def determine_level(config_dir):
|
|
38
|
+
"""
|
|
39
|
+
Determine the level at which the function is called.
|
|
40
|
+
Level = 1: Trial folder
|
|
41
|
+
Level = 2: Root folder
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
len_paths = [
|
|
45
|
+
len(root.split(os.sep))
|
|
46
|
+
for root, dirs, files in os.walk(config_dir)
|
|
47
|
+
if "Config.toml" in files
|
|
48
|
+
]
|
|
49
|
+
if len_paths == []:
|
|
50
|
+
raise FileNotFoundError(
|
|
51
|
+
"You need a Config.toml file in each trial or root folder."
|
|
52
|
+
)
|
|
53
|
+
return max(len_paths) - min(len_paths) + 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_config_files(config):
|
|
57
|
+
"""
|
|
58
|
+
Read Root and Trial configuration files,
|
|
59
|
+
and output a dictionary with all the parameters.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
if isinstance(config, dict):
|
|
63
|
+
level = 2 # log_dir = os.getcwd()
|
|
64
|
+
config_dicts = [config]
|
|
65
|
+
if config_dicts[0].get("project").get("project_dir") is None:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
'Please specify the project directory in config_dict:\n \
|
|
68
|
+
config_dict.get("project").update({"project_dir":"<YOUR_PROJECT_DIRECTORY>"})'
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
71
|
+
# if launched without an argument, config is None, else it is the path to the config directory
|
|
72
|
+
config_dir = ["." if config is None else config][0]
|
|
73
|
+
level = determine_level(config_dir)
|
|
74
|
+
|
|
75
|
+
# Trial level
|
|
76
|
+
if level == 1: # Trial
|
|
77
|
+
try:
|
|
78
|
+
# if batch
|
|
79
|
+
session_config_dict = toml.load(
|
|
80
|
+
os.path.join(config_dir, "..", "Config.toml")
|
|
81
|
+
)
|
|
82
|
+
trial_config_dict = toml.load(os.path.join(config_dir, "Config.toml"))
|
|
83
|
+
session_config_dict = recursive_update(
|
|
84
|
+
session_config_dict, trial_config_dict
|
|
85
|
+
)
|
|
86
|
+
except Exception:
|
|
87
|
+
# if single trial
|
|
88
|
+
session_config_dict = toml.load(os.path.join(config_dir, "Config.toml"))
|
|
89
|
+
session_config_dict.get("project").update({"project_dir": config_dir})
|
|
90
|
+
config_dicts = [session_config_dict]
|
|
91
|
+
|
|
92
|
+
# Root level
|
|
93
|
+
if level == 2:
|
|
94
|
+
session_config_dict = toml.load(os.path.join(config_dir, "Config.toml"))
|
|
95
|
+
config_dicts = []
|
|
96
|
+
# Create config dictionaries for all trials of the participant
|
|
97
|
+
for root, dirs, files in os.walk(config_dir):
|
|
98
|
+
if "Config.toml" in files and root != config_dir:
|
|
99
|
+
trial_config_dict = toml.load(os.path.join(root, files[0]))
|
|
100
|
+
# deep copy, otherwise session_config_dict is modified at each iteration within the config_dicts list
|
|
101
|
+
temp_dict = deepcopy(session_config_dict)
|
|
102
|
+
temp_dict = recursive_update(temp_dict, trial_config_dict)
|
|
103
|
+
temp_dict.get("project").update(
|
|
104
|
+
{"project_dir": os.path.join(config_dir, os.path.relpath(root))}
|
|
105
|
+
)
|
|
106
|
+
if os.path.basename(root) not in temp_dict.get("project").get(
|
|
107
|
+
"exclude_from_batch"
|
|
108
|
+
):
|
|
109
|
+
config_dicts.append(temp_dict)
|
|
110
|
+
|
|
111
|
+
return level, config_dicts
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def run_calibration(config=None):
|
|
115
|
+
"""
|
|
116
|
+
Cameras calibration from checkerboards or from qualisys files.
|
|
117
|
+
|
|
118
|
+
config can be a dictionary,
|
|
119
|
+
or a the directory path of a trial, participant, or session,
|
|
120
|
+
or the function can be called without an argument, in which case it the config directory is the current one.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
level, config_dicts = read_config_files(config)
|
|
124
|
+
config_dict = config_dicts[0]
|
|
125
|
+
try:
|
|
126
|
+
session_dir = os.path.realpath(
|
|
127
|
+
[os.getcwd() if level == 2 else os.path.join(os.getcwd(), "..")][0]
|
|
128
|
+
)
|
|
129
|
+
[
|
|
130
|
+
os.path.join(session_dir, c)
|
|
131
|
+
for c in os.listdir(session_dir)
|
|
132
|
+
if "calib" in c.lower() and not c.lower().endswith(".py")
|
|
133
|
+
][0]
|
|
134
|
+
except Exception:
|
|
135
|
+
session_dir = os.path.realpath(os.getcwd())
|
|
136
|
+
config_dict.get("project").update({"project_dir": session_dir})
|
|
137
|
+
|
|
138
|
+
# Set up logging
|
|
139
|
+
logger = setup_logging(session_dir)
|
|
140
|
+
currentDateAndTime = datetime.now()
|
|
141
|
+
|
|
142
|
+
# Run calibration
|
|
143
|
+
calib_dir = [
|
|
144
|
+
os.path.join(session_dir, c)
|
|
145
|
+
for c in os.listdir(session_dir)
|
|
146
|
+
if os.path.isdir(os.path.join(session_dir, c)) and "calib" in c.lower()
|
|
147
|
+
][0]
|
|
148
|
+
logger.info(
|
|
149
|
+
"\n---------------------------------------------------------------------"
|
|
150
|
+
)
|
|
151
|
+
logger.info("Camera calibration")
|
|
152
|
+
logger.info(f"On {currentDateAndTime.strftime('%A %d. %B %Y, %H:%M:%S')}")
|
|
153
|
+
logger.info(f"Calibration directory: {calib_dir}")
|
|
154
|
+
logger.info(
|
|
155
|
+
"---------------------------------------------------------------------\n"
|
|
156
|
+
)
|
|
157
|
+
start = time.time()
|
|
158
|
+
|
|
159
|
+
calibrate_cams_all(config_dict)
|
|
160
|
+
|
|
161
|
+
end = time.time()
|
|
162
|
+
logger.info(f"\nCalibration took {end - start:.2f} s.\n")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def main():
|
|
166
|
+
import argparse
|
|
167
|
+
|
|
168
|
+
parser = argparse.ArgumentParser(description="Camera calibration script")
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"--config",
|
|
171
|
+
type=str,
|
|
172
|
+
default=None,
|
|
173
|
+
help="Path to the configuration directory or file. If not provided, uses the current directory.",
|
|
174
|
+
)
|
|
175
|
+
args = parser.parse_args()
|
|
176
|
+
run_calibration(args.config)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
main()
|