Skip to content

Commit 8c15316

Browse files
lpasselinjohan12345qenopsDavid Dunnshagren
authored
Version 1.5.0 (#199)
* Color image ISO speed for Capture (#176) * Fixing the context manager for playback objects (#181) * Fix: setuptools 67 needs python_requires instead of install_requires (#196) * Added method for getting the extrinsic transformation parameters (#192) * update python versions for github actions (#197) * Update black, flake8 and mypy (#198) --------- Co-authored-by: Johan von Forstner <[email protected]> Co-authored-by: Louis-Philippe Asselin <[email protected]> Co-authored-by: David Dunn <[email protected]> Co-authored-by: David Dunn <[email protected]> Co-authored-by: Ilya Gruzinov <[email protected]> Co-authored-by: annStein <[email protected]> Co-authored-by: steinimoto <[email protected]> Co-authored-by: Justin <[email protected]> Co-authored-by: CMU <[email protected]> Co-authored-by: Marcel Egle <[email protected]>
1 parent 2791f6e commit 8c15316

18 files changed

+142
-41
lines changed

.github/workflows/ci.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ jobs:
1717
steps:
1818
- name: Checkout
1919
uses: actions/checkout@v2
20-
- name: Setup Python 3.7
20+
- name: Setup Python 3.9
2121
uses: actions/setup-python@v2
2222
with:
23-
python-version: 3.7
23+
python-version: 3.9
2424
- name: Cache PyPI
2525
uses: actions/cache@v2
2626
with:
@@ -49,8 +49,8 @@ jobs:
4949
test:
5050
strategy:
5151
matrix:
52-
python-version: ['3.6', '3.7', '3.8']
53-
fail-fast: true
52+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
53+
fail-fast: false
5454
name: Test
5555
runs-on: ubuntu-18.04
5656
steps:

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ Homepage: https://github.com/etiennedub/pyk4a/
1717

1818
## Prerequisites
1919
The [Azure-Kinect-Sensor-SDK](https://github.com/microsoft/Azure-Kinect-Sensor-SDK) is required to build this library.
20-
To use the SDK, refer to the installation instructions [here](https://github.com/microsoft/Azure-Kinect-Sensor-SDK).
20+
To use the SDK, refer to the installation instructions [here](https://github.com/microsoft/Azure-Kinect-Sensor-SDK/blob/develop/docs/usage.md).
2121

2222

2323
## Install
2424

2525
### Linux
2626

27+
Linux specific installation instructions [here](https://docs.microsoft.com/en-us/azure/kinect-dk/sensor-sdk-download#linux-installation-instructions)
28+
29+
Install both packages `libk4a<major>.<minor>` and `libk4a<major>.<minor>-dev`. The latter contains the headers and CMake files to build pyk4a.
30+
2731
Make sure your `LD_LIBRARY_PATH` contains the directory of k4a.lib
2832

2933
```shell

example/benchmark.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,31 @@ def parse_args() -> Namespace:
6868
parser.add_argument("--device-id", type=int, default=0, help="Device ID, from zero. Default: 0")
6969
parser.add_argument(
7070
"--color-resolution",
71-
type=ColorResolution,
71+
type=lambda i: ColorResolution(int(i)),
7272
action=EnumActionTuned,
7373
default=ColorResolution.RES_720P,
7474
help="Color sensor resoultion. Default: 720P",
7575
)
7676
parser.add_argument(
7777
"--color-format",
78-
type=ImageFormat,
78+
type=lambda i: ImageFormat(int(i)),
7979
action=EnumActionTuned,
8080
default=ImageFormat.COLOR_BGRA32,
8181
help="Color color_image color_format. Default: BGRA32",
8282
)
8383
parser.add_argument(
8484
"--depth-mode",
85-
type=DepthMode,
85+
type=lambda i: DepthMode(int(i)),
8686
action=EnumAction,
8787
default=DepthMode.NFOV_UNBINNED,
8888
help="Depth sensor mode. Default: NFOV_UNBINNED",
8989
)
9090
parser.add_argument(
91-
"--camera-fps", type=FPS, action=EnumActionTuned, default=FPS.FPS_30, help="Camera FPS. Default: 30"
91+
"--camera-fps",
92+
type=lambda i: FPS(int(i)),
93+
action=EnumActionTuned,
94+
default=FPS.FPS_30,
95+
help="Camera FPS. Default: 30",
9296
)
9397
parser.add_argument(
9498
"--synchronized-images-only",
@@ -105,7 +109,7 @@ def parse_args() -> Namespace:
105109
parser.set_defaults(synchronized_images_only=True)
106110
parser.add_argument(
107111
"--wired-sync-mode",
108-
type=WiredSyncMode,
112+
type=lambda i: WiredSyncMode(int(i)),
109113
action=EnumActionTuned,
110114
default=WiredSyncMode.STANDALONE,
111115
help="Wired sync mode. Default: STANDALONE",

example/helpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def colorize(
2929
colormap: int = cv2.COLORMAP_HSV,
3030
) -> np.ndarray:
3131
if clipping_range[0] or clipping_range[1]:
32-
img = image.clip(clipping_range[0], clipping_range[1])
32+
img = image.clip(clipping_range[0], clipping_range[1]) # type: ignore
3333
else:
3434
img = image.copy()
3535
img = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)

example/imu.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import matplotlib.pyplot as plt
21
import numpy as np
2+
from matplotlib import pyplot as plt
33

44
import pyk4a
55
from pyk4a import Config, PyK4A

example/threads.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def bench(camera_workers: List[CameraWorker], cpu_workers: List[CpuWorker], dura
8484

8585
def draw(results: Dict[int, Dict[bool, int]]):
8686
try:
87-
import matplotlib.pyplot as plt
87+
from matplotlib import pyplot as plt
8888
except ImportError:
8989
return
9090
plt.figure()

pyk4a/calibration.py

+15
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,18 @@ def get_distortion_coefficients(self, camera: CalibrationType) -> np.ndarray:
196196
raise ValueError("Unknown camera calibration type")
197197

198198
return np.array([params[4], params[5], params[13], params[12], *params[6:10]])
199+
200+
def get_extrinsic_parameters(
201+
self, source_camera: CalibrationType, target_camera: CalibrationType
202+
) -> Tuple[np.ndarray, np.ndarray]:
203+
"""
204+
The extrinsic parameters allow 3D coordinate conversions between depth camera, color camera, the IMU's gyroscope and accelerometer.
205+
"""
206+
params = k4a_module.calibration_get_extrinsics(
207+
self._calibration_handle, self.thread_safe, source_camera, target_camera
208+
)
209+
210+
rotation = np.reshape(np.array(params[0]), [3, 3])
211+
translation = np.reshape(np.array(params[1]), [1, 3]) / 1000 # Millimeter to meter conversion
212+
213+
return rotation, translation

pyk4a/capture.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(
2727
self._color_timestamp_usec: int = 0
2828
self._color_system_timestamp_nsec: int = 0
2929
self._color_exposure_usec: Optional[int] = None
30+
self._color_iso_speed: Optional[int] = None
3031
self._color_white_balance: Optional[int] = None
3132
self._depth: Optional[np.ndarray] = None
3233
self._depth_timestamp_usec: int = 0
@@ -73,6 +74,15 @@ def color_exposure_usec(self) -> int:
7374
self._color_exposure_usec = value
7475
return self._color_exposure_usec
7576

77+
@property
78+
def color_iso_speed(self) -> int:
79+
if self._color_iso_speed is None:
80+
value = k4a_module.color_image_get_iso_speed(self._capture_handle)
81+
if value == 0:
82+
raise K4AException("Cannot read iso from color_image")
83+
self._color_iso_speed = value
84+
return self._color_iso_speed
85+
7686
@property
7787
def color_white_balance(self) -> int:
7888
if self._color_white_balance is None:
@@ -131,14 +141,18 @@ def ir_system_timestamp_nsec(self) -> int:
131141
@property
132142
def transformed_depth(self) -> Optional[np.ndarray]:
133143
if self._transformed_depth is None and self.depth is not None:
134-
self._transformed_depth = depth_image_to_color_camera(self._depth, self._calibration, self.thread_safe)
144+
self._transformed_depth = depth_image_to_color_camera(
145+
self._depth, # type: ignore
146+
self._calibration,
147+
self.thread_safe,
148+
)
135149
return self._transformed_depth
136150

137151
@property
138152
def depth_point_cloud(self) -> Optional[np.ndarray]:
139153
if self._depth_point_cloud is None and self.depth is not None:
140154
self._depth_point_cloud = depth_image_to_point_cloud(
141-
self._depth,
155+
self._depth, # type: ignore
142156
self._calibration,
143157
self.thread_safe,
144158
calibration_type_depth=True,

pyk4a/module.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33

44
try:
5-
import k4a_module # noqa: F401
5+
import k4a_module # type: ignore # noqa: F401
66
except BaseException as e:
77
if sys.platform == "win32":
88
from .win32_utils import prepare_import_k4a_module
99

1010
added_dll_dir = prepare_import_k4a_module()
1111
try:
12-
import k4a_module # noqa: F401
12+
import k4a_module # type: ignore # noqa: F401
1313
except BaseException:
1414
raise ImportError(
1515
(

pyk4a/playback.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
import warnings
23
from enum import IntEnum
34
from pathlib import Path
45
from typing import Any, Optional, Tuple, Union
@@ -57,7 +58,7 @@ def __enter__(self):
5758
self.open()
5859
return self
5960

60-
def __exit__(self):
61+
def __exit__(self, exc_type, exc_value, traceback):
6162
self.close()
6263

6364
@property
@@ -71,9 +72,9 @@ def path(self) -> Path:
7172
def configuration(self) -> Configuration:
7273
self._validate_is_open()
7374
if self._configuration is None:
74-
res, conf = k4a_module.playback_get_record_configuration(
75-
self._handle, self.thread_safe
76-
) # type: int, Tuple[Any,...]
75+
res: int
76+
conf: Tuple[Any, ...]
77+
res, conf = k4a_module.playback_get_record_configuration(self._handle, self.thread_safe)
7778
_verify_error(res)
7879
self._configuration = Configuration(
7980
color_format=ImageFormat(conf[0]),
@@ -169,6 +170,12 @@ def get_next_capture(self):
169170
)
170171

171172
def get_previouse_capture(self):
173+
warnings.warn(
174+
"get_previouse_capture() deprecated, please use get_previous_capture() instead", DeprecationWarning
175+
)
176+
return self.get_previous_capture()
177+
178+
def get_previous_capture(self):
172179
self._validate_is_open()
173180
result, capture_handle = k4a_module.playback_get_previous_capture(self._handle, self.thread_safe)
174181
self._verify_stream_error(result)

pyk4a/pyk4a.cpp

+43
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,23 @@ static PyObject *color_image_get_exposure_usec(PyObject *self, PyObject *args) {
587587
return Py_BuildValue("K", exposure_usec);
588588
}
589589

590+
static PyObject *color_image_get_iso_speed(PyObject *self, PyObject *args) {
591+
k4a_capture_t *capture_handle;
592+
PyObject *capsule;
593+
uint32_t iso_speed = 0;
594+
PyArg_ParseTuple(args, "O", &capsule);
595+
capture_handle = (k4a_capture_t *)PyCapsule_GetPointer(capsule, CAPSULE_CAPTURE_NAME);
596+
597+
k4a_image_t image = k4a_capture_get_color_image(*capture_handle);
598+
if (image == NULL) {
599+
fprintf(stderr, "Color image missed");
600+
return Py_BuildValue("I", iso_speed);
601+
}
602+
iso_speed = k4a_image_get_iso_speed(image);
603+
k4a_image_release(image);
604+
return Py_BuildValue("I", iso_speed);
605+
}
606+
590607
static PyObject *color_image_get_white_balance(PyObject *self, PyObject *args) {
591608
k4a_capture_t *capture_handle;
592609
PyObject *capsule;
@@ -1090,6 +1107,29 @@ static PyObject *calibration_get_intrinsics(PyObject *self, PyObject *args) {
10901107
return Py_BuildValue("N", intrinsics);
10911108
}
10921109

1110+
static PyObject *calibration_get_extrinsics(PyObject *self, PyObject *args) {
1111+
k4a_calibration_t *calibration_handle;
1112+
PyObject *capsule;
1113+
int thread_safe;
1114+
k4a_calibration_type_t source_camera;
1115+
k4a_calibration_type_t target_camera;
1116+
PyThreadState *thread_state;
1117+
1118+
PyArg_ParseTuple(args, "OpII", &capsule, &thread_safe, &source_camera, &target_camera);
1119+
calibration_handle = (k4a_calibration_t *)PyCapsule_GetPointer(capsule, CAPSULE_CALIBRATION_NAME);
1120+
1121+
thread_state = _gil_release(thread_safe);
1122+
1123+
k4a_calibration_extrinsics_t calib = calibration_handle->extrinsics[source_camera][target_camera];
1124+
1125+
_gil_restore(thread_state);
1126+
1127+
PyObject *rotation = _array_to_list(calib.rotation, 9);
1128+
PyObject *translation = _array_to_list(calib.translation, 3);
1129+
1130+
return Py_BuildValue("NN", rotation, translation);
1131+
}
1132+
10931133
static PyObject *playback_open(PyObject *self, PyObject *args) {
10941134
int thread_safe;
10951135
PyThreadState *thread_state;
@@ -1483,6 +1523,8 @@ static PyMethodDef Pyk4aMethods[] = {
14831523
"Transform a 3D point of a source coordinate system into a 2D pixel coordinate of the target camera"},
14841524
{"calibration_get_intrinsics", calibration_get_intrinsics, METH_VARARGS,
14851525
"Gets intrinsic parameters from calibration"},
1526+
{"calibration_get_extrinsics", calibration_get_extrinsics, METH_VARARGS,
1527+
"Gets extrinsic parameters from calibration"},
14861528
{"playback_open", playback_open, METH_VARARGS, "Open file for playback"},
14871529
{"playback_close", playback_close, METH_VARARGS, "Close opened playback"},
14881530
{"playback_get_recording_length_usec", playback_get_recording_length_usec, METH_VARARGS, "Return recording length"},
@@ -1499,6 +1541,7 @@ static PyMethodDef Pyk4aMethods[] = {
14991541
{"playback_get_next_imu_sample", playback_get_next_imu_sample, METH_VARARGS, "Get next imu sample from playback"},
15001542
{"color_image_get_exposure_usec", color_image_get_exposure_usec, METH_VARARGS,
15011543
"Get color image exposure in microseconds"},
1544+
{"color_image_get_iso_speed", color_image_get_iso_speed, METH_VARARGS, "Get color image ISO speed"},
15021545
{"color_image_get_white_balance", color_image_get_white_balance, METH_VARARGS, "Get color image white balance"},
15031546
{"record_create", record_create, METH_VARARGS, "Opens a new recording file for writing"},
15041547
{"record_close", record_close, METH_VARARGS, "Opens a new recording file for writing"},

pyproject.toml

+9
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@ markers = [
1010
"opengl: Tests require opengl for GPU accelerated depth engine software"
1111
]
1212
addopts = "--cov=pyk4a --cov-report=xml --verbose"
13+
14+
[[tool.mypy.overrides]]
15+
module = [
16+
"cv2",
17+
"matplotlib",
18+
"mpl_toolkits",
19+
"k4a_module"
20+
]
21+
ignore_missing_imports = true

requirements-dev.txt

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
black==22.3.0
2-
flake8==3.8.3
3-
isort==5.4.2
4-
flake8-isort==4.0.0
5-
mypy==0.782
6-
mypy-extensions==0.4.3
7-
pytest==6.0.1
8-
pytest-cov==2.10.1
1+
black~=23.1.0
2+
flake8~=5.0.4
3+
isort~=5.11.5
4+
flake8-isort~=6.0.0
5+
mypy==0.991
6+
mypy-extensions~=0.4.3
7+
pytest~=7.2.1
8+
pytest-cov~=4.0.0
99
dataclasses==0.6; python_version<"3.7"
10+
opencv-python-headless~=4.7.0.68

setup.cfg

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pyk4a
3-
version = 1.4.0
3+
version = 1.5.0
44
description-file = README.md
55
description = Python wrapper over Azure Kinect SDK
66
long_description = file: README.md
@@ -16,9 +16,9 @@ classifiers =
1616

1717
[options]
1818
packages = pyk4a
19+
python_requires = >= 3.4
1920
install_requires =
2021
numpy
21-
python_version >= "3.4"
2222

2323
[options.package_data]
2424
pyk4a = py.typed
@@ -29,6 +29,7 @@ max-line-length = 120
2929
extend-ignore =
3030
# See https://github.com/PyCQA/pycodestyle/issues/373
3131
E203,
32+
E501,
3233

3334
[isort]
3435
line_length=120

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
site.ENABLE_USER_SITE = "--user" in sys.argv[1:]
1717

18+
1819
# Bypass import numpy before running install_requires
1920
# https://stackoverflow.com/questions/54117786/add-numpy-get-include-argument-to-setuptools-without-preinstalled-numpy
2021
class get_numpy_include:

tests/functional/test_capture.py

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ def test_device_capture_images(device: PyK4A):
2323
assert np.array_equal(ir, color) is False
2424
assert capture.color_white_balance is not None
2525
assert capture.color_exposure_usec is not None
26+
assert capture.color_iso_speed is not None

0 commit comments

Comments
 (0)