Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runtime stereo calib reconfig #1017

Merged
merged 6 commits into from
Sep 26, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge remote-tracking branch 'origin/develop' into HEAD
SzabolcsGergely committed Sep 26, 2024
commit c9a23c406482f60d67785e361f1dc066623a0baf
24 changes: 19 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -75,6 +75,8 @@ jobs:
- name: Setup cmake
if: matrix.os == 'macos-latest'
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.29.x'
- name: Cache .hunter folder
if: matrix.os != 'windows-latest'
uses: actions/cache@v3
@@ -136,6 +138,10 @@ jobs:
matrix:
rpi-os: [rpi-buster, rpi-bullseye, rpi-bookworm]
runs-on: ${{ matrix.rpi-os }}
env:
# workaround required for cache@v3, https://github.com/actions/cache/issues/1428
# to be removed when upgrading the manylinux image
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
steps:
- name: Print home directory
run: echo Home directory inside container $HOME
@@ -182,7 +188,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12']
python-version: [3.7, 3.8, 3.9, '3.10', '3.11', '3.12']
python-architecture: [x64, x86]
fail-fast: false
steps:
@@ -298,8 +304,12 @@ jobs:
build-linux-x86_64:
needs: build-docstrings
runs-on: ubuntu-latest
env:
# workaround required for cache@v3, https://github.com/actions/cache/issues/1428
# to be removed when upgrading the manylinux image
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
container:
image: quay.io/pypa/manylinux2014_x86_64:2024-01-08-eb135ed
image: quay.io/pypa/manylinux2014_x86_64:latest
env:
PLAT: manylinux2014_x86_64
steps:
@@ -342,7 +352,7 @@ jobs:
/opt/python/cp38-cp38/bin/python3.8 setup.py sdist --formats=gztar
mv dist/* wheelhouse/audited/
- name: Build wheels
run: for PYBIN in /opt/python/cp3{6..12}*/bin; do "${PYBIN}/pip" wheel . -w ./wheelhouse/ --verbose; done
run: for PYBIN in /opt/python/cp3{7..12}*/bin; do "${PYBIN}/pip" wheel . -w ./wheelhouse/ --verbose; done
- name: Audit wheels
run: for whl in wheelhouse/*.whl; do auditwheel repair "$whl" --plat $PLAT -w wheelhouse/audited/; done
- name: Archive wheel artifacts
@@ -362,8 +372,12 @@ jobs:
build-linux-arm64:
needs: build-docstrings
runs-on: [self-hosted, linux, ARM64]
env:
# workaround required for cache@v3, https://github.com/actions/cache/issues/1428
# to be removed when upgrading the manylinux image
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
container:
image: quay.io/pypa/manylinux2014_aarch64:2024-01-08-eb135ed
image: quay.io/pypa/manylinux2014_aarch64:latest
env:
PLAT: manylinux2014_aarch64
# Mount local hunter cache directory, instead of transfering to Github and back
@@ -399,7 +413,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') != true
run: echo "BUILD_COMMIT_HASH=${{github.sha}}" >> $GITHUB_ENV
- name: Building wheels
run: for PYBIN in /opt/python/cp3{6..12}*/bin; do "${PYBIN}/pip" wheel . -w ./wheelhouse/ --verbose; done
run: for PYBIN in /opt/python/cp3{7..12}*/bin; do "${PYBIN}/pip" wheel . -w ./wheelhouse/ --verbose; done
- name: Auditing wheels
run: for whl in wheelhouse/*.whl; do auditwheel repair "$whl" --plat $PLAT -w wheelhouse/audited/; done
- name: Archive wheel artifacts
2 changes: 1 addition & 1 deletion .github/workflows/test-install-dependencies.yml
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@
test_macos:
strategy:
matrix:
os: ["macos-11", "macos-12", "macos-13", "macos-14"]
os: ["macos-12", "macos-13", "macos-14"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -23,8 +23,8 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated/Hunter/config.cmake ${final_hun

include("cmake/HunterGate.cmake")
HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.25.5.tar.gz"
SHA1 "a20151e4c0740ee7d0f9994476856d813cdead29"
URL "https://github.com/cpp-pm/hunter/archive/9d9242b60d5236269f894efd3ddd60a9ca83dd7f.tar.gz"
SHA1 "16cc954aa723bccd16ea45fc91a858d0c5246376"
FILEPATH ${CMAKE_CURRENT_BINARY_DIR}/generated/Hunter/config.cmake # Combined config
)

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![](https://img.shields.io/pypi/v/depthai.svg)](https://pypi.org/project/depthai/)
[![Python Wheel CI](https://github.com/luxonis/depthai-python/actions/workflows/main.yml/badge.svg?branch=gen2_develop)](https://github.com/luxonis/depthai-python/actions/workflows/main.yml)
[![Python Wheel CI](https://github.com/luxonis/depthai-python/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/luxonis/depthai-python/actions/workflows/main.yml)

Python bindings for C++ depthai-core library

8 changes: 4 additions & 4 deletions cmake/Hunter/config.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pybind11 2.9.2
# Pybind11 2.12.0
hunter_config(
pybind11
VERSION "2.9.2"
URL "https://github.com/pybind/pybind11/archive/refs/tags/v2.9.2.tar.gz"
SHA1 "5e05583a210282c3251281b6ee5677915f0cbf95"
VERSION "2.12.0"
URL "https://github.com/pybind/pybind11/archive/refs/tags/v2.12.0.tar.gz"
SHA1 "e70610cba7b6b7d7a57827d5357c016ad2155c0f"
)
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion examples/ImageAlign/depth_align.py
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ def getFps(self):

out.setStreamName("out")

sync.setSyncThreshold(timedelta(seconds=(1 / FPS) * 0.5))
sync.setSyncThreshold(timedelta(seconds=0.5 / FPS))

# Linking
camRgb.isp.link(sync.inputs["rgb"])
18 changes: 6 additions & 12 deletions examples/ImageAlign/image_align.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@

RGB_SOCKET = dai.CameraBoardSocket.CAM_A
LEFT_SOCKET = dai.CameraBoardSocket.CAM_B
RIGHT_SOCKET = dai.CameraBoardSocket.CAM_C
ALIGN_SOCKET = LEFT_SOCKET

COLOR_RESOLUTION = dai.ColorCameraProperties.SensorResolution.THE_1080_P
@@ -19,7 +18,6 @@
# Define sources and outputs
camRgb = pipeline.create(dai.node.ColorCamera)
left = pipeline.create(dai.node.MonoCamera)
right = pipeline.create(dai.node.MonoCamera)
sync = pipeline.create(dai.node.Sync)
out = pipeline.create(dai.node.XLinkOut)
align = pipeline.create(dai.node.ImageAlign)
@@ -29,18 +27,14 @@
left.setBoardSocket(LEFT_SOCKET)
left.setFps(FPS)

right.setResolution(LEFT_RIGHT_RESOLUTION)
right.setBoardSocket(RIGHT_SOCKET)
right.setFps(FPS)

camRgb.setBoardSocket(RGB_SOCKET)
camRgb.setResolution(COLOR_RESOLUTION)
camRgb.setFps(FPS)
camRgb.setIspScale(1, 3)

out.setStreamName("out")

sync.setSyncThreshold(timedelta(seconds=(1 / FPS) * 0.5))
sync.setSyncThreshold(timedelta(seconds=0.5 / FPS))

cfgIn.setStreamName("config")

@@ -57,19 +51,19 @@


rgbWeight = 0.4
depthWeight = 0.6
leftWeight = 0.6


def updateBlendWeights(percentRgb):
"""
Update the rgb and depth weights used to blend depth/rgb image
Update the rgb and left weights used to blend rgb/left image

@param[in] percent_rgb The rgb weight expressed as a percentage (0..100)
"""
global depthWeight
global leftWeight
global rgbWeight
rgbWeight = float(percentRgb) / 100.0
depthWeight = 1.0 - rgbWeight
leftWeight = 1.0 - rgbWeight

def updateDepthPlane(depth):
global staticDepthPlane
@@ -118,7 +112,7 @@ def updateDepthPlane(depth):
if leftCv.shape != frameRgbCv.shape:
leftCv = cv2.resize(leftCv, (frameRgbCv.shape[1], frameRgbCv.shape[0]))

blended = cv2.addWeighted(frameRgbCv, rgbWeight, leftCv, depthWeight, 0)
blended = cv2.addWeighted(frameRgbCv, rgbWeight, leftCv, leftWeight, 0)
cv2.imshow(windowName, blended)

key = cv2.waitKey(1)
2 changes: 1 addition & 1 deletion examples/ImageAlign/thermal_align.py
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ def getFps(self):

out.setStreamName("out")

sync.setSyncThreshold(timedelta(seconds=1/FPS * 0.5))
sync.setSyncThreshold(timedelta(seconds=0.5 / FPS))

cfgIn.setStreamName("config")

2 changes: 1 addition & 1 deletion examples/ImageAlign/tof_align.py
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ def getFps(self):

out.setStreamName("out")

sync.setSyncThreshold(timedelta(seconds=(1 / FPS)))
sync.setSyncThreshold(timedelta(seconds=0.5 / FPS))

# Linking
camRgb.isp.link(sync.inputs["rgb"])
31 changes: 31 additions & 0 deletions examples/Sync/sync_all_cameras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env python3

import cv2
import depthai as dai

with dai.Device() as device:
pipeline = dai.Pipeline()
cams = device.getConnectedCameraFeatures()
sync = pipeline.create(dai.node.Sync)
for cam in cams:
print(str(cam), str(cam.socket), cam.socket)
cam_node = pipeline.create(dai.node.Camera)
cam_node.setBoardSocket(cam.socket)
cam_node.isp.link(sync.inputs[str(cam.socket)])

xout = pipeline.create(dai.node.XLinkOut)
xout.setStreamName('sync')
sync.out.link(xout.input)

# Start pipeline
device.startPipeline(pipeline)
q = device.getOutputQueue('sync', maxSize=10, blocking=False)
while not device.isClosed():
msgs = q.get()
for (name, imgFrame) in msgs:
print(f'Cam {name}, seq {imgFrame.getSequenceNum()}, tiemstamp {imgFrame.getTimestamp()}')
cv2.imshow(name, imgFrame.getCvFrame())
print('----')

if cv2.waitKey(1) == ord('q'):
break
4 changes: 2 additions & 2 deletions examples/install_requirements.py
Original file line number Diff line number Diff line change
@@ -55,12 +55,12 @@ def hasWhitespace(string):
requireOpenCv = True

if requireOpenCv:
DEPENDENCIES.append('numpy')
DEPENDENCIES.append('numpy<3.0')
# 4.5.4.58 package is broken for python 3.9
if sys.version_info[0] == 3 and sys.version_info[1] == 9:
DEPENDENCIES.append('opencv-python!=4.5.4.58')
else:
DEPENDENCIES.append('opencv-python')
DEPENDENCIES.append('opencv-python<5.0')



3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -238,7 +238,6 @@ def build_extension(self, ext):
"Operating System :: Unix",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
@@ -250,7 +249,7 @@ def build_extension(self, ext):
"Topic :: Scientific/Engineering",
"Topic :: Software Development",
],
python_requires='>=3.6',
python_requires='>=3.7',
entry_points={
"console_scripts": [
f'depthai={DEPTHAI_CLI_MODULE_NAME}.depthai_cli:cli'
152 changes: 10 additions & 142 deletions src/DeviceBindings.cpp
Original file line number Diff line number Diff line change
@@ -20,141 +20,6 @@
PYBIND11_MAKE_OPAQUE(std::unordered_map<std::int8_t, dai::BoardConfig::GPIO>);
PYBIND11_MAKE_OPAQUE(std::unordered_map<std::int8_t, dai::BoardConfig::UART>);

// Patch for bind_map naming
// Remove if it gets mainlined in pybind11
namespace pybind11 {

template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
class_<Map, holder_type> bind_map_patched(handle scope, const std::string &name, Args &&...args) {
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;
using KeysView = detail::keys_view<Map>;
using ValuesView = detail::values_view<Map>;
using ItemsView = detail::items_view<Map>;
using Class_ = class_<Map, holder_type>;

// If either type is a non-module-local bound type then make the map binding non-local as well;
// otherwise (e.g. both types are either module-local or converting) the map will be
// module-local.
auto *tinfo = detail::get_type_info(typeid(MappedType));
bool local = !tinfo || tinfo->module_local;
if (local) {
tinfo = detail::get_type_info(typeid(KeyType));
local = !tinfo || tinfo->module_local;
}

Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
class_<KeysView> keys_view(
scope, ("KeysView_" + name).c_str(), pybind11::module_local(local));
class_<ValuesView> values_view(
scope, ("ValuesView_" + name).c_str(), pybind11::module_local(local));
class_<ItemsView> items_view(
scope, ("ItemsView_" + name).c_str(), pybind11::module_local(local));

cl.def(init<>());

// Register stream insertion operator (if possible)
detail::map_if_insertion_operator<Map, Class_>(cl, name);

cl.def(
"__bool__",
[](const Map &m) -> bool { return !m.empty(); },
"Check whether the map is nonempty");

cl.def(
"__iter__",
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
);

cl.def(
"keys",
[](Map &m) { return KeysView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);

cl.def(
"values",
[](Map &m) { return ValuesView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);

cl.def(
"items",
[](Map &m) { return ItemsView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);

cl.def(
"__getitem__",
[](Map &m, const KeyType &k) -> MappedType & {
auto it = m.find(k);
if (it == m.end()) {
throw key_error();
}
return it->second;
},
return_value_policy::reference_internal // ref + keepalive
);

cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
auto it = m.find(k);
if (it == m.end()) {
return false;
}
return true;
});
// Fallback for when the object is not of the key type
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });

// Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl);

cl.def("__delitem__", [](Map &m, const KeyType &k) {
auto it = m.find(k);
if (it == m.end()) {
throw key_error();
}
m.erase(it);
});

cl.def("__len__", &Map::size);

keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
keys_view.def(
"__iter__",
[](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
auto it = view.map.find(k);
if (it == view.map.end()) {
return false;
}
return true;
});
// Fallback for when the object is not of the key type
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });

values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
values_view.def(
"__iter__",
[](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);

items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
items_view.def(
"__iter__",
[](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);

return cl;
}

} // namespace pybind11


// Searches for available devices (as Device constructor)
// but pooling, to check for python interrupts, and releases GIL in between
@@ -247,7 +112,10 @@ static void bindConstructors(ARG& arg){
py::gil_scoped_release release;
return std::make_unique<D>(pipeline, deviceInfo, pathToCmd);
}), py::arg("pipeline"), py::arg("devInfo"), py::arg("pathToCmd"), DOC(dai, DeviceBase, DeviceBase, 8))

.def(py::init([](const Pipeline& pipeline, const dai::DeviceInfo info){
py::gil_scoped_release release;
return std::make_unique<D>(pipeline, info);
}), py::arg("pipeline"), py::arg("deviceInfo"), DOC(dai, DeviceBase, DeviceBase, 9))
// DeviceBase constructor - OpenVINO version
.def(py::init([](OpenVINO::Version version){
auto dev = deviceSearchHelper<D>();
@@ -348,8 +216,8 @@ void DeviceBindings::bind(pybind11::module& m, void* pCallstack){
py::class_<PyClock> clock(m, "Clock");


py::bind_map_patched<std::unordered_map<std::int8_t, dai::BoardConfig::GPIO>>(boardConfig, "GPIOMap");
py::bind_map_patched<std::unordered_map<std::int8_t, dai::BoardConfig::UART>>(boardConfig, "UARTMap");
py::bind_map<std::unordered_map<std::int8_t, dai::BoardConfig::GPIO>>(boardConfig, "GPIOMap");
py::bind_map<std::unordered_map<std::int8_t, dai::BoardConfig::UART>>(boardConfig, "UARTMap");


// pybind11 limitation of having actual classes as exceptions
@@ -685,12 +553,12 @@ void DeviceBindings::bind(pybind11::module& m, void* pCallstack){
// Bind the rest
device
.def("__enter__", [](Device& d) -> Device& { return d; })
.def("getOutputQueue", static_cast<std::shared_ptr<DataOutputQueue>(Device::*)(const std::string&)>(&Device::getOutputQueue), py::arg("name"), DOC(dai, Device, getOutputQueue))
.def("getOutputQueue", static_cast<std::shared_ptr<DataOutputQueue>(Device::*)(const std::string&, unsigned int, bool)>(&Device::getOutputQueue), py::arg("name"), py::arg("maxSize"), py::arg("blocking") = true, DOC(dai, Device, getOutputQueue, 2))
.def("getOutputQueue", py::overload_cast<const std::string&>(&Device::getOutputQueue), py::arg("name"), DOC(dai, Device, getOutputQueue))
.def("getOutputQueue", py::overload_cast<const std::string&, unsigned int, bool>(&Device::getOutputQueue), py::arg("name"), py::arg("maxSize"), py::arg("blocking") = true, DOC(dai, Device, getOutputQueue, 2))
.def("getOutputQueueNames", &Device::getOutputQueueNames, DOC(dai, Device, getOutputQueueNames))

.def("getInputQueue", static_cast<std::shared_ptr<DataInputQueue>(Device::*)(const std::string&)>(&Device::getInputQueue), py::arg("name"), DOC(dai, Device, getInputQueue))
.def("getInputQueue", static_cast<std::shared_ptr<DataInputQueue>(Device::*)(const std::string&, unsigned int, bool)>(&Device::getInputQueue), py::arg("name"), py::arg("maxSize"), py::arg("blocking") = true, DOC(dai, Device, getInputQueue, 2))
.def("getInputQueue", py::overload_cast<const std::string&>(&Device::getInputQueue), py::arg("name"), DOC(dai, Device, getInputQueue))
.def("getInputQueue", py::overload_cast<const std::string&, unsigned int, bool>(&Device::getInputQueue), py::arg("name"), py::arg("maxSize"), py::arg("blocking") = true, DOC(dai, Device, getInputQueue, 2))
.def("getInputQueueNames", &Device::getInputQueueNames, DOC(dai, Device, getInputQueueNames))

.def("getQueueEvents", [](Device& d, const std::vector<std::string>& queueNames, std::size_t maxNumEvents, std::chrono::microseconds timeout) {
5 changes: 5 additions & 0 deletions src/pipeline/datatype/CameraControlBindings.cpp
Original file line number Diff line number Diff line change
@@ -242,8 +242,13 @@ std::vector<const char *> camCtrlAttr;
.def("setEffectMode", &CameraControl::setEffectMode, py::arg("mode"), DOC(dai, CameraControl, setEffectMode))
.def("setControlMode", &CameraControl::setControlMode, py::arg("mode"), DOC(dai, CameraControl, setControlMode))
.def("setCaptureIntent", &CameraControl::setCaptureIntent, py::arg("mode"), DOC(dai, CameraControl, setCaptureIntent))
.def("setMisc", py::overload_cast<std::string, std::string>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc))
.def("setMisc", py::overload_cast<std::string, int>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc, 2))
.def("setMisc", py::overload_cast<std::string, float>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc, 3))
.def("clearMiscControls", &CameraControl::clearMiscControls, DOC(dai, CameraControl, clearMiscControls))
.def("set", &CameraControl::set, py::arg("config"), DOC(dai, CameraControl, set))
// getters
.def("getMiscControls", &CameraControl::getMiscControls, DOC(dai, CameraControl, getMiscControls))
.def("getCaptureStill", &CameraControl::getCaptureStill, DOC(dai, CameraControl, getCaptureStill))
.def("getExposureTime", &CameraControl::getExposureTime, DOC(dai, CameraControl, getExposureTime))
.def("getSensitivity", &CameraControl::getSensitivity, DOC(dai, CameraControl, getSensitivity))
1 change: 1 addition & 0 deletions src/pipeline/datatype/ToFConfigBindings.cpp
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ void bind_tofconfig(pybind11::module& m, void* pCallstack){
.def_readwrite("median", &RawToFConfig::median, DOC(dai, RawToFConfig, median))
.def_readwrite("enablePhaseShuffleTemporalFilter", &RawToFConfig::enablePhaseShuffleTemporalFilter, DOC(dai, RawToFConfig, enablePhaseShuffleTemporalFilter))
.def_readwrite("enableBurstMode", &RawToFConfig::enableBurstMode, DOC(dai, RawToFConfig, enableBurstMode))
.def_readwrite("enableDistortionCorrection", &RawToFConfig::enableDistortionCorrection, DOC(dai, RawToFConfig, enableDistortionCorrection))
.def_readwrite("phaseUnwrappingLevel", &RawToFConfig::phaseUnwrappingLevel, DOC(dai, RawToFConfig, phaseUnwrappingLevel))
.def_readwrite("enableFPPNCorrection", &RawToFConfig::enableFPPNCorrection, DOC(dai, RawToFConfig, enableFPPNCorrection))
.def_readwrite("enableOpticalCorrection", &RawToFConfig::enableOpticalCorrection, DOC(dai, RawToFConfig, enableOpticalCorrection))
9 changes: 8 additions & 1 deletion src/pipeline/node/CameraBindings.cpp
Original file line number Diff line number Diff line change
@@ -124,7 +124,14 @@ void bind_camera(pybind11::module& m, void* pCallstack){
// .def("setResolution", &Camera::setResolution, py::arg("resolution"), DOC(dai, node, Camera, setResolution))
// .def("getResolution", &Camera::getResolution, DOC(dai, node, Camera, getResolution))
.def("setFps", &Camera::setFps, py::arg("fps"), DOC(dai, node, Camera, setFps))
.def("setIsp3aFps", &Camera::setIsp3aFps, DOC(dai, node, Camera, setIsp3aFps))
.def("setIsp3aFps", [&camera](Camera &c, int isp3aFps) {
PyErr_WarnEx(PyExc_DeprecationWarning,
"setIsp3aFps is unstable", 1);
HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
c.setIsp3aFps(isp3aFps);
HEDLEY_DIAGNOSTIC_POP
}, py::arg("isp3aFps"), DOC(dai, node, Camera, setIsp3aFps))
.def("getFps", &Camera::getFps, DOC(dai, node, Camera, getFps))
.def("getPreviewSize", &Camera::getPreviewSize, DOC(dai, node, Camera, getPreviewSize))
.def("getPreviewWidth", &Camera::getPreviewWidth, DOC(dai, node, Camera, getPreviewWidth))
9 changes: 8 additions & 1 deletion src/pipeline/node/ColorCameraBindings.cpp
Original file line number Diff line number Diff line change
@@ -126,7 +126,14 @@ void bind_colorcamera(pybind11::module& m, void* pCallstack){
.def("setResolution", &ColorCamera::setResolution, py::arg("resolution"), DOC(dai, node, ColorCamera, setResolution))
.def("getResolution", &ColorCamera::getResolution, DOC(dai, node, ColorCamera, getResolution))
.def("setFps", &ColorCamera::setFps, py::arg("fps"), DOC(dai, node, ColorCamera, setFps))
.def("setIsp3aFps", &ColorCamera::setIsp3aFps, DOC(dai, node, ColorCamera, setIsp3aFps))
.def("setIsp3aFps", [](ColorCamera& cam, int isp3aFps){
// Issue a deprecation warning
PyErr_WarnEx(PyExc_DeprecationWarning, "setIsp3aFps is unstable", 1);
HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
cam.setIsp3aFps(isp3aFps);
HEDLEY_DIAGNOSTIC_POP
}, py::arg("isp3aFps"), DOC(dai, node, ColorCamera, setIsp3aFps))
.def("getFps", &ColorCamera::getFps, DOC(dai, node, ColorCamera, getFps))
.def("setFrameEventFilter", &ColorCamera::setFrameEventFilter, py::arg("events"), DOC(dai, node, ColorCamera, setFrameEventFilter))
.def("getFrameEventFilter", &ColorCamera::getFrameEventFilter, DOC(dai, node, ColorCamera, getFrameEventFilter))
9 changes: 8 additions & 1 deletion src/pipeline/node/MonoCameraBindings.cpp
Original file line number Diff line number Diff line change
@@ -81,7 +81,14 @@ void bind_monocamera(pybind11::module& m, void* pCallstack){
.def("setFrameEventFilter", &MonoCamera::setFrameEventFilter, py::arg("events"), DOC(dai, node, MonoCamera, setFrameEventFilter))
.def("getFrameEventFilter", &MonoCamera::getFrameEventFilter, DOC(dai, node, MonoCamera, getFrameEventFilter))
.def("setFps", &MonoCamera::setFps, py::arg("fps"), DOC(dai, node, MonoCamera, setFps))
.def("setIsp3aFps", &MonoCamera::setIsp3aFps, DOC(dai, node, MonoCamera, setIsp3aFps))
.def("setIsp3aFps", [&monoCamera](MonoCamera &c, int isp3aFps) {
PyErr_WarnEx(PyExc_DeprecationWarning,
"setIsp3aFps is unstable", 1);
HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
c.setIsp3aFps(isp3aFps);
HEDLEY_DIAGNOSTIC_POP
}, py::arg("isp3aFps"), DOC(dai, node, MonoCamera, setIsp3aFps))
.def("getFps", &MonoCamera::getFps, DOC(dai, node, MonoCamera, getFps))
.def("getResolutionSize", &MonoCamera::getResolutionSize, DOC(dai, node, MonoCamera, getResolutionSize))
.def("getResolutionWidth", &MonoCamera::getResolutionWidth, DOC(dai, node, MonoCamera, getResolutionWidth))
1 change: 1 addition & 0 deletions src/pipeline/node/ToFBindings.cpp
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ void bind_tof(pybind11::module& m, void* pCallstack){
.def_readwrite("initialConfig", &ToFProperties::initialConfig, DOC(dai, ToFProperties, initialConfig))
.def_readwrite("numFramesPool", &ToFProperties::numFramesPool, DOC(dai, ToFProperties, numFramesPool))
.def_readwrite("numShaves", &ToFProperties::numShaves, DOC(dai, ToFProperties, numShaves))
.def_readwrite("warpHwIds", &ToFProperties::warpHwIds, DOC(dai, ToFProperties, warpHwIds))
;

// Node
50 changes: 46 additions & 4 deletions utilities/cam_test.py
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@
from pathlib import Path
import sys
import signal
import math
from stress_test import stress_test, YOLO_LABELS, create_yolo


@@ -66,6 +67,8 @@ def socket_type_pair(arg):
is_thermal = True if type in ['th', 'thermal'] else False
return [socket, is_color, is_tof, is_thermal]

def string_pair(arg):
return arg.split('=')

parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-cams', '--cameras', type=socket_type_pair, nargs='+',
@@ -104,6 +107,10 @@ def socket_type_pair(arg):
help="Show RGB `preview` stream instead of full size `isp`")
parser.add_argument('-show', '--show-meta', action='store_true',
help="List frame metadata (seqno, timestamp, exp, iso etc). Can also toggle with `\`")
parser.add_argument('-misc', '--misc-controls', type=string_pair, nargs='+',
default=[],
help="List of miscellaneous camera controls to set initially, "
"as pairs: key1=value1 key2=value2 ...")

parser.add_argument('-d', '--device', default="", type=str,
help="Optional MX ID of the device to connect to.")
@@ -198,8 +205,6 @@ def clamp(num, v0, v1):
return max(v0, min(num, v1))

# Calculates FPS over a moving window, configurable


class FPS:
def __init__(self, window_size=30):
self.dq = collections.deque(maxlen=window_size)
@@ -360,6 +365,15 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
# cam[c].initialControl.setManualExposure(15000, 400) # exposure [us], iso
# When set, takes effect after the first 2 frames
# cam[c].initialControl.setManualWhiteBalance(4000) # light temperature in K, 1000..12000
# cam[c].initialControl.setAutoExposureLimit(5000) # can also be updated at runtime
# cam[c].initialControl.setMisc("downsampling-mode", "binning") # default: "scaling"
# cam[c].initialControl.setMisc("binning-mode", "sum") # default: "avg"
# cam[c].initialControl.setMisc("manual-exposure-handling", "fast") # default: "default"
# cam[c].initialControl.setMisc("hdr-exposure-ratio", 4) # enables HDR when set `> 1`, current options: 2, 4, 8
# cam[c].initialControl.setMisc("hdr-local-tone-weight", 75) # default 75, range 0..100
# cam[c].initialControl.setMisc("high-conversion-gain", 0) # 1 to enable (default on supported sensors)
for kvPair in args.misc_controls:
cam[c].initialControl.setMisc(*kvPair)
control.out.link(cam[c].inputControl)
if rotate[c]:
cam[c].setImageOrientation(
@@ -534,6 +548,13 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
chroma_denoise = 0
control = 'none'
show = args.show_meta
high_conversion_gain = 1
print(args.misc_controls)
args_misc_dict = dict(args.misc_controls)

hdr_exp_ratio = int(math.log2(float(args_misc_dict.get('hdr-exposure-ratio', 1))))
hdr_local_tone_weight = int(32 * float(args_misc_dict.get('hdr-local-tone-weight', 0.75)))
hdr_on = (hdr_exp_ratio > 0)

jet_custom = cv2.applyColorMap(
np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET)
@@ -664,6 +685,12 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
elif key == ord('c'):
capture_list = streams.copy()
capture_time = time.strftime('%Y%m%d_%H%M%S')
elif key == ord('h'):
high_conversion_gain = 1 - high_conversion_gain
print("High conversion gain:", high_conversion_gain)
ctrl = dai.CameraControl()
ctrl.setMisc("high-conversion-gain", high_conversion_gain)
controlQueue.send(ctrl)
elif key == ord('t'):
print("Autofocus trigger (and disable continuous)")
ctrl = dai.CameraControl()
@@ -742,7 +769,7 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
floodIntensity = 0
device.setIrFloodLightIntensity(floodIntensity)
print(f'IR Flood intensity:', floodIntensity)
elif key >= 0 and chr(key) in '34567890[]p\\;\'':
elif key >= 0 and chr(key) in '34567890[]\\;\'rg':
if key == ord('3'):
control = 'awb_mode'
elif key == ord('4'):
@@ -771,6 +798,11 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
control = 'chroma_denoise'
elif key == ord('p'):
control = 'tof_amplitude_min'
elif key == ord('r') or key == ord('g'):
if hdr_on:
control = 'hdr_exp_ratio' if key == ord('r') else 'hdr_local_tone_weight'
else:
print("HDR was not enabled, start with `-misc hdr-exposure-ratio=2` or higher to enable")
print("Selected control:", control)
elif key in [ord('-'), ord('_'), ord('+'), ord('=')]:
change = 0
@@ -780,7 +812,7 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
change = 1
ctrl = dai.CameraControl()
if control == 'none':
print("Please select a control first using keys 3..9 0 [ ] \\ ; \'")
print("Please select a control first using keys 3..9 0 [ ] \\ ; \' r g")
elif control == 'ae_comp':
ae_comp = clamp(ae_comp + change, -9, 9)
print("Auto exposure compensation:", ae_comp)
@@ -833,6 +865,16 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
chroma_denoise = clamp(chroma_denoise + change, 0, 4)
print("Chroma denoise:", chroma_denoise)
ctrl.setChromaDenoise(chroma_denoise)
elif control == 'hdr_exp_ratio':
hdr_exp_ratio = clamp(hdr_exp_ratio + change, 0, 3)
value = pow(2, hdr_exp_ratio)
print("HDR exposure ratio:", value)
ctrl.setMisc("hdr-exposure-ratio", value)
elif control == 'hdr_local_tone_weight':
hdr_local_tone_weight = clamp(hdr_local_tone_weight + change, 0, 32)
value = hdr_local_tone_weight / 32
print(f"HDR local tone weight (normalized): {value:.2f}")
ctrl.setMisc("hdr-local-tone-weight", value)
controlQueue.send(ctrl)

print()
6 changes: 4 additions & 2 deletions utilities/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
PySimpleGUI==4.60.3
Pillow==9.3.0
psutil==5.9.3
numpy>=1.21.4 # For RPi Buster (last successful build) and macOS M1 (first build). But allow for higher versions, to support Python3.11 (not available in 1.21.4 yet)
numpy>=1.21.4,<3 # For RPi Buster (last successful build) and macOS M1 (first build). But allow for higher versions, to support Python3.11 (not available in 1.21.4 yet). Likely won't work with numpy 3.x when it comes out.
opencv-contrib-python==4.5.5.62 # Last successful RPi build, also covers M1 with above pinned numpy (otherwise 4.6.0.62 would be required, but that has a bug with charuco boards). Python version not important, abi3 wheels
pyqt5>5,<5.15.6 ; platform_machine != "armv6l" and platform_machine != "armv7l" and platform_machine != "aarch64" and platform_machine != "arm64"
--extra-index-url https://artifacts.luxonis.com/artifactory/luxonis-python-snapshot-local/
# Latest version (note - it's yanked)
PySimpleGUI-4-foss==4.60.4.1 # Mirror of final open-source release.
depthai
You are viewing a condensed version of this merge commit. You can view the full changes here.