Skip to content

Commit

Permalink
Version 1.5.0 (#199)
Browse files Browse the repository at this point in the history
* 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 <johan.vonforstner@paradoxcat.com>
Co-authored-by: Louis-Philippe Asselin <lpasselin@users.noreply.github.com>
Co-authored-by: David Dunn <qenopster@gmail.com>
Co-authored-by: David Dunn <dunn@qti.qualcomm.com>
Co-authored-by: Ilya Gruzinov <igruzinov@yandex.ru>
Co-authored-by: annStein <43335656+annStein@users.noreply.github.com>
Co-authored-by: steinimoto <as@motognosis.com>
Co-authored-by: Justin <lego.man.123@hotmail.com>
Co-authored-by: CMU <ch.chrismuller@pm.me>
Co-authored-by: Marcel Egle <97447968+EgleM2022@users.noreply.github.com>
  • Loading branch information
11 people authored Feb 3, 2023
1 parent 2791f6e commit 8c15316
Show file tree
Hide file tree
Showing 18 changed files with 142 additions and 41 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Python 3.7
- name: Setup Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.9
- name: Cache PyPI
uses: actions/cache@v2
with:
Expand Down Expand Up @@ -49,8 +49,8 @@ jobs:
test:
strategy:
matrix:
python-version: ['3.6', '3.7', '3.8']
fail-fast: true
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
fail-fast: false
name: Test
runs-on: ubuntu-18.04
steps:
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ Homepage: https://github.com/etiennedub/pyk4a/

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


## Install

### Linux

Linux specific installation instructions [here](https://docs.microsoft.com/en-us/azure/kinect-dk/sensor-sdk-download#linux-installation-instructions)

Install both packages `libk4a<major>.<minor>` and `libk4a<major>.<minor>-dev`. The latter contains the headers and CMake files to build pyk4a.

Make sure your `LD_LIBRARY_PATH` contains the directory of k4a.lib

```shell
Expand Down
14 changes: 9 additions & 5 deletions example/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,31 @@ def parse_args() -> Namespace:
parser.add_argument("--device-id", type=int, default=0, help="Device ID, from zero. Default: 0")
parser.add_argument(
"--color-resolution",
type=ColorResolution,
type=lambda i: ColorResolution(int(i)),
action=EnumActionTuned,
default=ColorResolution.RES_720P,
help="Color sensor resoultion. Default: 720P",
)
parser.add_argument(
"--color-format",
type=ImageFormat,
type=lambda i: ImageFormat(int(i)),
action=EnumActionTuned,
default=ImageFormat.COLOR_BGRA32,
help="Color color_image color_format. Default: BGRA32",
)
parser.add_argument(
"--depth-mode",
type=DepthMode,
type=lambda i: DepthMode(int(i)),
action=EnumAction,
default=DepthMode.NFOV_UNBINNED,
help="Depth sensor mode. Default: NFOV_UNBINNED",
)
parser.add_argument(
"--camera-fps", type=FPS, action=EnumActionTuned, default=FPS.FPS_30, help="Camera FPS. Default: 30"
"--camera-fps",
type=lambda i: FPS(int(i)),
action=EnumActionTuned,
default=FPS.FPS_30,
help="Camera FPS. Default: 30",
)
parser.add_argument(
"--synchronized-images-only",
Expand All @@ -105,7 +109,7 @@ def parse_args() -> Namespace:
parser.set_defaults(synchronized_images_only=True)
parser.add_argument(
"--wired-sync-mode",
type=WiredSyncMode,
type=lambda i: WiredSyncMode(int(i)),
action=EnumActionTuned,
default=WiredSyncMode.STANDALONE,
help="Wired sync mode. Default: STANDALONE",
Expand Down
2 changes: 1 addition & 1 deletion example/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def colorize(
colormap: int = cv2.COLORMAP_HSV,
) -> np.ndarray:
if clipping_range[0] or clipping_range[1]:
img = image.clip(clipping_range[0], clipping_range[1])
img = image.clip(clipping_range[0], clipping_range[1]) # type: ignore
else:
img = image.copy()
img = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
Expand Down
2 changes: 1 addition & 1 deletion example/imu.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import pyplot as plt

import pyk4a
from pyk4a import Config, PyK4A
Expand Down
2 changes: 1 addition & 1 deletion example/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def bench(camera_workers: List[CameraWorker], cpu_workers: List[CpuWorker], dura

def draw(results: Dict[int, Dict[bool, int]]):
try:
import matplotlib.pyplot as plt
from matplotlib import pyplot as plt
except ImportError:
return
plt.figure()
Expand Down
15 changes: 15 additions & 0 deletions pyk4a/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,18 @@ def get_distortion_coefficients(self, camera: CalibrationType) -> np.ndarray:
raise ValueError("Unknown camera calibration type")

return np.array([params[4], params[5], params[13], params[12], *params[6:10]])

def get_extrinsic_parameters(
self, source_camera: CalibrationType, target_camera: CalibrationType
) -> Tuple[np.ndarray, np.ndarray]:
"""
The extrinsic parameters allow 3D coordinate conversions between depth camera, color camera, the IMU's gyroscope and accelerometer.
"""
params = k4a_module.calibration_get_extrinsics(
self._calibration_handle, self.thread_safe, source_camera, target_camera
)

rotation = np.reshape(np.array(params[0]), [3, 3])
translation = np.reshape(np.array(params[1]), [1, 3]) / 1000 # Millimeter to meter conversion

return rotation, translation
18 changes: 16 additions & 2 deletions pyk4a/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(
self._color_timestamp_usec: int = 0
self._color_system_timestamp_nsec: int = 0
self._color_exposure_usec: Optional[int] = None
self._color_iso_speed: Optional[int] = None
self._color_white_balance: Optional[int] = None
self._depth: Optional[np.ndarray] = None
self._depth_timestamp_usec: int = 0
Expand Down Expand Up @@ -73,6 +74,15 @@ def color_exposure_usec(self) -> int:
self._color_exposure_usec = value
return self._color_exposure_usec

@property
def color_iso_speed(self) -> int:
if self._color_iso_speed is None:
value = k4a_module.color_image_get_iso_speed(self._capture_handle)
if value == 0:
raise K4AException("Cannot read iso from color_image")
self._color_iso_speed = value
return self._color_iso_speed

@property
def color_white_balance(self) -> int:
if self._color_white_balance is None:
Expand Down Expand Up @@ -131,14 +141,18 @@ def ir_system_timestamp_nsec(self) -> int:
@property
def transformed_depth(self) -> Optional[np.ndarray]:
if self._transformed_depth is None and self.depth is not None:
self._transformed_depth = depth_image_to_color_camera(self._depth, self._calibration, self.thread_safe)
self._transformed_depth = depth_image_to_color_camera(
self._depth, # type: ignore
self._calibration,
self.thread_safe,
)
return self._transformed_depth

@property
def depth_point_cloud(self) -> Optional[np.ndarray]:
if self._depth_point_cloud is None and self.depth is not None:
self._depth_point_cloud = depth_image_to_point_cloud(
self._depth,
self._depth, # type: ignore
self._calibration,
self.thread_safe,
calibration_type_depth=True,
Expand Down
4 changes: 2 additions & 2 deletions pyk4a/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@


try:
import k4a_module # noqa: F401
import k4a_module # type: ignore # noqa: F401
except BaseException as e:
if sys.platform == "win32":
from .win32_utils import prepare_import_k4a_module

added_dll_dir = prepare_import_k4a_module()
try:
import k4a_module # noqa: F401
import k4a_module # type: ignore # noqa: F401
except BaseException:
raise ImportError(
(
Expand Down
15 changes: 11 additions & 4 deletions pyk4a/playback.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys
import warnings
from enum import IntEnum
from pathlib import Path
from typing import Any, Optional, Tuple, Union
Expand Down Expand Up @@ -57,7 +58,7 @@ def __enter__(self):
self.open()
return self

def __exit__(self):
def __exit__(self, exc_type, exc_value, traceback):
self.close()

@property
Expand All @@ -71,9 +72,9 @@ def path(self) -> Path:
def configuration(self) -> Configuration:
self._validate_is_open()
if self._configuration is None:
res, conf = k4a_module.playback_get_record_configuration(
self._handle, self.thread_safe
) # type: int, Tuple[Any,...]
res: int
conf: Tuple[Any, ...]
res, conf = k4a_module.playback_get_record_configuration(self._handle, self.thread_safe)
_verify_error(res)
self._configuration = Configuration(
color_format=ImageFormat(conf[0]),
Expand Down Expand Up @@ -169,6 +170,12 @@ def get_next_capture(self):
)

def get_previouse_capture(self):
warnings.warn(
"get_previouse_capture() deprecated, please use get_previous_capture() instead", DeprecationWarning
)
return self.get_previous_capture()

def get_previous_capture(self):
self._validate_is_open()
result, capture_handle = k4a_module.playback_get_previous_capture(self._handle, self.thread_safe)
self._verify_stream_error(result)
Expand Down
43 changes: 43 additions & 0 deletions pyk4a/pyk4a.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,23 @@ static PyObject *color_image_get_exposure_usec(PyObject *self, PyObject *args) {
return Py_BuildValue("K", exposure_usec);
}

static PyObject *color_image_get_iso_speed(PyObject *self, PyObject *args) {
k4a_capture_t *capture_handle;
PyObject *capsule;
uint32_t iso_speed = 0;
PyArg_ParseTuple(args, "O", &capsule);
capture_handle = (k4a_capture_t *)PyCapsule_GetPointer(capsule, CAPSULE_CAPTURE_NAME);

k4a_image_t image = k4a_capture_get_color_image(*capture_handle);
if (image == NULL) {
fprintf(stderr, "Color image missed");
return Py_BuildValue("I", iso_speed);
}
iso_speed = k4a_image_get_iso_speed(image);
k4a_image_release(image);
return Py_BuildValue("I", iso_speed);
}

static PyObject *color_image_get_white_balance(PyObject *self, PyObject *args) {
k4a_capture_t *capture_handle;
PyObject *capsule;
Expand Down Expand Up @@ -1090,6 +1107,29 @@ static PyObject *calibration_get_intrinsics(PyObject *self, PyObject *args) {
return Py_BuildValue("N", intrinsics);
}

static PyObject *calibration_get_extrinsics(PyObject *self, PyObject *args) {
k4a_calibration_t *calibration_handle;
PyObject *capsule;
int thread_safe;
k4a_calibration_type_t source_camera;
k4a_calibration_type_t target_camera;
PyThreadState *thread_state;

PyArg_ParseTuple(args, "OpII", &capsule, &thread_safe, &source_camera, &target_camera);
calibration_handle = (k4a_calibration_t *)PyCapsule_GetPointer(capsule, CAPSULE_CALIBRATION_NAME);

thread_state = _gil_release(thread_safe);

k4a_calibration_extrinsics_t calib = calibration_handle->extrinsics[source_camera][target_camera];

_gil_restore(thread_state);

PyObject *rotation = _array_to_list(calib.rotation, 9);
PyObject *translation = _array_to_list(calib.translation, 3);

return Py_BuildValue("NN", rotation, translation);
}

static PyObject *playback_open(PyObject *self, PyObject *args) {
int thread_safe;
PyThreadState *thread_state;
Expand Down Expand Up @@ -1483,6 +1523,8 @@ static PyMethodDef Pyk4aMethods[] = {
"Transform a 3D point of a source coordinate system into a 2D pixel coordinate of the target camera"},
{"calibration_get_intrinsics", calibration_get_intrinsics, METH_VARARGS,
"Gets intrinsic parameters from calibration"},
{"calibration_get_extrinsics", calibration_get_extrinsics, METH_VARARGS,
"Gets extrinsic parameters from calibration"},
{"playback_open", playback_open, METH_VARARGS, "Open file for playback"},
{"playback_close", playback_close, METH_VARARGS, "Close opened playback"},
{"playback_get_recording_length_usec", playback_get_recording_length_usec, METH_VARARGS, "Return recording length"},
Expand All @@ -1499,6 +1541,7 @@ static PyMethodDef Pyk4aMethods[] = {
{"playback_get_next_imu_sample", playback_get_next_imu_sample, METH_VARARGS, "Get next imu sample from playback"},
{"color_image_get_exposure_usec", color_image_get_exposure_usec, METH_VARARGS,
"Get color image exposure in microseconds"},
{"color_image_get_iso_speed", color_image_get_iso_speed, METH_VARARGS, "Get color image ISO speed"},
{"color_image_get_white_balance", color_image_get_white_balance, METH_VARARGS, "Get color image white balance"},
{"record_create", record_create, METH_VARARGS, "Opens a new recording file for writing"},
{"record_close", record_close, METH_VARARGS, "Opens a new recording file for writing"},
Expand Down
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ markers = [
"opengl: Tests require opengl for GPU accelerated depth engine software"
]
addopts = "--cov=pyk4a --cov-report=xml --verbose"

[[tool.mypy.overrides]]
module = [
"cv2",
"matplotlib",
"mpl_toolkits",
"k4a_module"
]
ignore_missing_imports = true
17 changes: 9 additions & 8 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
black==22.3.0
flake8==3.8.3
isort==5.4.2
flake8-isort==4.0.0
mypy==0.782
mypy-extensions==0.4.3
pytest==6.0.1
pytest-cov==2.10.1
black~=23.1.0
flake8~=5.0.4
isort~=5.11.5
flake8-isort~=6.0.0
mypy==0.991
mypy-extensions~=0.4.3
pytest~=7.2.1
pytest-cov~=4.0.0
dataclasses==0.6; python_version<"3.7"
opencv-python-headless~=4.7.0.68
5 changes: 3 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pyk4a
version = 1.4.0
version = 1.5.0
description-file = README.md
description = Python wrapper over Azure Kinect SDK
long_description = file: README.md
Expand All @@ -16,9 +16,9 @@ classifiers =

[options]
packages = pyk4a
python_requires = >= 3.4
install_requires =
numpy
python_version >= "3.4"

[options.package_data]
pyk4a = py.typed
Expand All @@ -29,6 +29,7 @@ max-line-length = 120
extend-ignore =
# See https://github.com/PyCQA/pycodestyle/issues/373
E203,
E501,

[isort]
line_length=120
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

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


# Bypass import numpy before running install_requires
# https://stackoverflow.com/questions/54117786/add-numpy-get-include-argument-to-setuptools-without-preinstalled-numpy
class get_numpy_include:
Expand Down
1 change: 1 addition & 0 deletions tests/functional/test_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ def test_device_capture_images(device: PyK4A):
assert np.array_equal(ir, color) is False
assert capture.color_white_balance is not None
assert capture.color_exposure_usec is not None
assert capture.color_iso_speed is not None
Loading

0 comments on commit 8c15316

Please sign in to comment.