Skip to content

Commit a688f77

Browse files
authored
Merge pull request #993 from luxonis/release_v2.25.0.0
Release v2.25.0.0
2 parents f190ba1 + 0465260 commit a688f77

23 files changed

+1372
-153
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ jobs:
132132
needs: build-docstrings
133133
strategy:
134134
matrix:
135-
rpi-os: [rpi-buster, rpi-bullseye]
135+
rpi-os: [rpi-buster, rpi-bullseye, rpi-bookworm]
136136
runs-on: ${{ matrix.rpi-os }}
137137
steps:
138138
- name: Print home directory
@@ -155,7 +155,7 @@ jobs:
155155
run: python3 -m pip wheel . -w ./wheelhouse/ --verbose
156156
- name: Auditing wheels and adding armv6l tag (Running on RPi, binaries compiled as armv6l)
157157
run: |
158-
python3 -m pip install -U wheel auditwheel
158+
# python3 -m pip install -U wheel auditwheel # Called once when setting up the runner
159159
for whl in wheelhouse/*.whl; do auditwheel repair "$whl" --plat linux_armv7l -w wheelhouse/preaudited/; done
160160
for whl in wheelhouse/preaudited/*.whl; do python3 -m wheel tags --platform-tag +linux_armv6l "$whl"; done
161161
mkdir -p wheelhouse/audited/
@@ -556,8 +556,8 @@ jobs:
556556
# event-type: python-hil-event
557557
# client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
558558

559-
notify_hil_workflow_linux_x86_64:
560-
needs: [build-linux-x86_64]
559+
notify_hil_workflow:
560+
needs: [build-linux-armhf, build-linux-x86_64]
561561
runs-on: ubuntu-latest
562562
steps:
563563
- name: Dispatch an action and get the run ID

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ depthai-core/build/
2222

2323
# builds
2424
_builds/
25-
25+
.cache/
2626
#git
2727
*.orig
2828
*_REMOTE_*
@@ -44,4 +44,4 @@ env.bak/
4444
venv.bak/
4545

4646
# PyCharm
47-
.idea/
47+
.idea/

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pybind11_add_module(${TARGET_NAME}
129129
src/pipeline/node/WarpBindings.cpp
130130
src/pipeline/node/UVCBindings.cpp
131131
src/pipeline/node/ToFBindings.cpp
132+
src/pipeline/node/PointCloudBindings.cpp
132133
src/pipeline/node/SyncBindings.cpp
133134
src/pipeline/node/MessageDemuxBindings.cpp
134135

@@ -154,7 +155,8 @@ pybind11_add_module(${TARGET_NAME}
154155
src/pipeline/datatype/SystemInformationBindings.cpp
155156
src/pipeline/datatype/TrackedFeaturesBindings.cpp
156157
src/pipeline/datatype/TrackletsBindings.cpp
157-
158+
src/pipeline/datatype/PointCloudConfigBindings.cpp
159+
src/pipeline/datatype/PointCloudDataBindings.cpp
158160
)
159161

160162
if(WIN32)

depthai_cli/__init__.py

Whitespace-only changes.

depthai_cli/depthai_cli.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import os
2+
from pathlib import Path
3+
import subprocess
4+
5+
here = os.path.dirname(os.path.realpath(__file__))
6+
7+
if os.path.exists(os.path.join(here, "cam_test.py")): # Installed package
8+
CAM_TEST_PATH = Path(here) / "cam_test.py"
9+
else:
10+
CAM_TEST_PATH = (
11+
Path(here) / ".." / "utilities" / "cam_test.py"
12+
) # Execution from source
13+
CAM_TEST_PATH = str(CAM_TEST_PATH)
14+
15+
16+
def cli() -> int:
17+
import argparse
18+
import sys
19+
import depthai as dai
20+
parser = argparse.ArgumentParser(description="DepthAI CLI", add_help=True)
21+
parser.add_argument("-v", "--version", action="store_true", help="Print version and exit.")
22+
parser.add_argument("-l", "--list-devices", action="store_true", help="List connected devices.")
23+
subparsers = parser.add_subparsers(dest="command", help="Sub-commands")
24+
# Define the parser for the "cam_test" command
25+
cam_test_parser = subparsers.add_parser("cam_test", help="Commands and options for cam_test", add_help=False)
26+
cam_test_parser.add_argument("args", nargs=argparse.REMAINDER, help="Arguments to pass to cam_test")
27+
28+
# subparser REMINDER args would get parsed too if we used parse_args, so we have to handle unknown args manually
29+
args, unknown_args = parser.parse_known_args()
30+
if args.command == "cam_test":
31+
cam_test_path = CAM_TEST_PATH
32+
return subprocess.run([sys.executable, cam_test_path] + cam_test_parser.parse_args().args[1:]).returncode
33+
# Parse other subcommands here
34+
elif unknown_args:
35+
parser.error(f"Unrecognized arguments: {unknown_args}") # handles exit internally
36+
elif args.version:
37+
print(dai.__version__)
38+
return 0
39+
elif args.list_devices:
40+
print(dai.Device.getAllConnectedDevices())
41+
return 0
42+
else:
43+
# No recognized commands, print help
44+
parser.print_help()
45+
return 1
46+
47+
48+
if __name__ == "__main__":
49+
exit(cli())

examples/Camera/thermal_cam.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import depthai as dai
2+
import cv2
3+
import numpy as np
4+
5+
mouseX, mouseY = 0, 0
6+
7+
8+
def onMouse(event, x, y, *args):
9+
global mouseX, mouseY
10+
mouseX = x
11+
mouseY = y
12+
13+
14+
device = dai.Device()
15+
pipeline = dai.Pipeline()
16+
17+
# Thermal camera
18+
thermalCam = pipeline.create(dai.node.Camera)
19+
width, height = -1, -1
20+
thermalFound = False
21+
for features in device.getConnectedCameraFeatures():
22+
if dai.CameraSensorType.THERMAL in features.supportedTypes:
23+
thermalFound = True
24+
thermalCam.setBoardSocket(features.socket)
25+
width, height = features.width, features.height
26+
break
27+
if not thermalFound:
28+
raise RuntimeError("No thermal camera found!")
29+
thermalCam.setPreviewSize(width, height)
30+
31+
# Output raw: FP16 temperature data (degrees Celsius)
32+
xoutRaw = pipeline.create(dai.node.XLinkOut)
33+
xoutRaw.setStreamName("thermal_raw")
34+
thermalCam.raw.link(xoutRaw.input)
35+
36+
# Output preview,video, isp: RGB or NV12 or YUV420 thermal image.
37+
xoutImage = pipeline.create(dai.node.XLinkOut)
38+
xoutImage.setStreamName("image")
39+
thermalCam.preview.link(xoutImage.input)
40+
device.startPipeline(pipeline)
41+
42+
qRaw = device.getOutputQueue("thermal_raw", 2, False)
43+
qImage = device.getOutputQueue("image", 2, False)
44+
45+
46+
RAW_WINDOW_NAME = "temperature"
47+
IMAGE_WINDOW_NAME = "image"
48+
# Scale 4x and position one next to another
49+
cv2.namedWindow(RAW_WINDOW_NAME, cv2.WINDOW_NORMAL)
50+
cv2.namedWindow(IMAGE_WINDOW_NAME, cv2.WINDOW_NORMAL)
51+
cv2.moveWindow(RAW_WINDOW_NAME, 0, 0)
52+
cv2.resizeWindow(RAW_WINDOW_NAME, width * 4, height * 4)
53+
cv2.moveWindow(IMAGE_WINDOW_NAME, width * 4, 0)
54+
cv2.resizeWindow(IMAGE_WINDOW_NAME, width * 4, height * 4)
55+
cv2.setMouseCallback(RAW_WINDOW_NAME, onMouse)
56+
cv2.setMouseCallback(IMAGE_WINDOW_NAME, onMouse)
57+
58+
while True:
59+
inRaw = qRaw.get()
60+
inImg = qImage.get()
61+
62+
# Retrieve one point of fp16 data
63+
frame = inRaw.getCvFrame().astype(np.float32)
64+
colormappedFrame = cv2.normalize(frame, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
65+
colormappedFrame = cv2.applyColorMap(colormappedFrame, cv2.COLORMAP_MAGMA)
66+
if (
67+
mouseX < 0
68+
or mouseY < 0
69+
or mouseX >= frame.shape[1]
70+
or mouseY >= frame.shape[0]
71+
):
72+
mouseX = max(0, min(mouseX, frame.shape[1] - 1))
73+
mouseY = max(0, min(mouseY, frame.shape[0] - 1))
74+
textColor = (255, 255, 255)
75+
# Draw crosshair
76+
cv2.line(
77+
colormappedFrame,
78+
(mouseX - 10, mouseY),
79+
(mouseX + 10, mouseY),
80+
textColor,
81+
1,
82+
)
83+
cv2.line(
84+
colormappedFrame,
85+
(mouseX, mouseY - 10),
86+
(mouseX, mouseY + 10),
87+
textColor,
88+
1,
89+
)
90+
# Draw deg C
91+
text = f"{frame[mouseY, mouseX]:.2f} deg C"
92+
putTextLeft = mouseX > colormappedFrame.shape[1] / 2
93+
cv2.putText(
94+
colormappedFrame,
95+
text,
96+
(mouseX - 100 if putTextLeft else mouseX + 10, mouseY - 10),
97+
cv2.FONT_HERSHEY_SIMPLEX,
98+
0.5,
99+
textColor,
100+
1,
101+
)
102+
cv2.imshow(RAW_WINDOW_NAME, colormappedFrame)
103+
104+
cv2.imshow(IMAGE_WINDOW_NAME, inImg.getCvFrame())
105+
106+
if cv2.waitKey(1) == ord("q"):
107+
break
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import depthai as dai
2+
from time import sleep
3+
import numpy as np
4+
import cv2
5+
import time
6+
import sys
7+
try:
8+
import open3d as o3d
9+
except ImportError:
10+
sys.exit("Critical dependency missing: Open3D. Please install it using the command: '{} -m pip install open3d' and then rerun the script.".format(sys.executable))
11+
12+
FPS = 30
13+
class FPSCounter:
14+
def __init__(self):
15+
self.frameCount = 0
16+
self.fps = 0
17+
self.startTime = time.time()
18+
19+
def tick(self):
20+
self.frameCount += 1
21+
if self.frameCount % 10 == 0:
22+
elapsedTime = time.time() - self.startTime
23+
self.fps = self.frameCount / elapsedTime
24+
self.frameCount = 0
25+
self.startTime = time.time()
26+
return self.fps
27+
28+
pipeline = dai.Pipeline()
29+
camRgb = pipeline.create(dai.node.ColorCamera)
30+
monoLeft = pipeline.create(dai.node.MonoCamera)
31+
monoRight = pipeline.create(dai.node.MonoCamera)
32+
depth = pipeline.create(dai.node.StereoDepth)
33+
pointcloud = pipeline.create(dai.node.PointCloud)
34+
sync = pipeline.create(dai.node.Sync)
35+
xOut = pipeline.create(dai.node.XLinkOut)
36+
xOut.input.setBlocking(False)
37+
38+
39+
camRgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
40+
camRgb.setBoardSocket(dai.CameraBoardSocket.CAM_A)
41+
camRgb.setIspScale(1,3)
42+
camRgb.setFps(FPS)
43+
44+
monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
45+
monoLeft.setCamera("left")
46+
monoLeft.setFps(FPS)
47+
monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
48+
monoRight.setCamera("right")
49+
monoRight.setFps(FPS)
50+
51+
depth.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.HIGH_DENSITY)
52+
depth.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_7x7)
53+
depth.setLeftRightCheck(True)
54+
depth.setExtendedDisparity(False)
55+
depth.setSubpixel(True)
56+
depth.setDepthAlign(dai.CameraBoardSocket.CAM_A)
57+
58+
monoLeft.out.link(depth.left)
59+
monoRight.out.link(depth.right)
60+
depth.depth.link(pointcloud.inputDepth)
61+
camRgb.isp.link(sync.inputs["rgb"])
62+
pointcloud.outputPointCloud.link(sync.inputs["pcl"])
63+
sync.out.link(xOut.input)
64+
xOut.setStreamName("out")
65+
66+
67+
68+
69+
with dai.Device(pipeline) as device:
70+
isRunning = True
71+
def key_callback(vis, action, mods):
72+
global isRunning
73+
if action == 0:
74+
isRunning = False
75+
76+
q = device.getOutputQueue(name="out", maxSize=4, blocking=False)
77+
pc = o3d.geometry.PointCloud()
78+
vis = o3d.visualization.VisualizerWithKeyCallback()
79+
vis.create_window()
80+
vis.register_key_action_callback(81, key_callback)
81+
pcd = o3d.geometry.PointCloud()
82+
coordinateFrame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=1000, origin=[0,0,0])
83+
vis.add_geometry(coordinateFrame)
84+
85+
first = True
86+
fpsCounter = FPSCounter()
87+
while isRunning:
88+
inMessage = q.get()
89+
inColor = inMessage["rgb"]
90+
inPointCloud = inMessage["pcl"]
91+
cvColorFrame = inColor.getCvFrame()
92+
# Convert the frame to RGB
93+
cvRGBFrame = cv2.cvtColor(cvColorFrame, cv2.COLOR_BGR2RGB)
94+
fps = fpsCounter.tick()
95+
# Display the FPS on the frame
96+
cv2.putText(cvColorFrame, f"FPS: {fps:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
97+
cv2.imshow("color", cvColorFrame)
98+
key = cv2.waitKey(1)
99+
if key == ord('q'):
100+
break
101+
if inPointCloud:
102+
t_before = time.time()
103+
points = inPointCloud.getPoints().astype(np.float64)
104+
pcd.points = o3d.utility.Vector3dVector(points)
105+
colors = (cvRGBFrame.reshape(-1, 3) / 255.0).astype(np.float64)
106+
pcd.colors = o3d.utility.Vector3dVector(colors)
107+
if first:
108+
vis.add_geometry(pcd)
109+
first = False
110+
else:
111+
vis.update_geometry(pcd)
112+
vis.poll_events()
113+
vis.update_renderer()
114+
vis.destroy_window()

setup.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
### NAME
1616
MODULE_NAME = 'depthai'
17+
DEPTHAI_CLI_MODULE_NAME = 'depthai_cli'
1718

1819
### VERSION
1920
here = os.path.abspath(os.path.dirname(__file__))
@@ -92,6 +93,20 @@ def run(self):
9293
self.build_extension(ext)
9394

9495
def build_extension(self, ext):
96+
if ext.name == DEPTHAI_CLI_MODULE_NAME:
97+
# Copy cam_test.py and it's dependencies to depthai_cli/
98+
cam_test_path = os.path.join(here, "utilities", "cam_test.py")
99+
cam_test_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "cam_test.py")
100+
cam_test_gui_path = os.path.join(here, "utilities", "cam_test_gui.py")
101+
cam_test_gui_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "cam_test_gui.py")
102+
stress_test_path = os.path.join(here, "utilities", "stress_test.py")
103+
stress_test_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "stress_test.py")
104+
files_to_copy = [(cam_test_path, cam_test_dest), (cam_test_gui_path, cam_test_gui_dest), (stress_test_path, stress_test_dest)]
105+
for src, dst in files_to_copy:
106+
with open(src, "r") as f:
107+
with open(dst, "w") as f2:
108+
f2.write(f.read())
109+
return
95110

96111
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
97112
# required for auto-detection of auxiliary "native" libs
@@ -205,10 +220,11 @@ def build_extension(self, ext):
205220
long_description=long_description,
206221
long_description_content_type="text/markdown",
207222
url="https://github.com/luxonis/depthai-python",
208-
ext_modules=[CMakeExtension(MODULE_NAME)],
223+
ext_modules=[CMakeExtension(MODULE_NAME), Extension(DEPTHAI_CLI_MODULE_NAME, sources=[])],
209224
cmdclass={
210-
'build_ext': CMakeBuild
225+
'build_ext': CMakeBuild,
211226
},
227+
packages=[DEPTHAI_CLI_MODULE_NAME],
212228
zip_safe=False,
213229
classifiers=[
214230
"Development Status :: 4 - Beta",
@@ -235,4 +251,9 @@ def build_extension(self, ext):
235251
"Topic :: Software Development",
236252
],
237253
python_requires='>=3.6',
254+
entry_points={
255+
"console_scripts": [
256+
f'depthai={DEPTHAI_CLI_MODULE_NAME}.depthai_cli:cli'
257+
]
258+
}
238259
)

0 commit comments

Comments
 (0)