Skip to content

Commit 32de55f

Browse files
committed
add support for "big depth" maps
1 parent cc7f08f commit 32de55f

File tree

5 files changed

+206
-103
lines changed

5 files changed

+206
-103
lines changed

doc/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@
5858
# built documents.
5959
#
6060
# The short X.Y version.
61-
version = '0.2.2'
61+
version = '0.2.3'
6262
# The full version, including alpha/beta/rc tags.
63-
release = '0.2.2'
63+
release = '0.2.3'
6464

6565
# The language for content autogenerated by Sphinx. Refer to documentation
6666
# for a list of supported languages.

examples/dump_big_depth.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import json
2+
from freenect2 import Device, FrameType
3+
import numpy as np
4+
5+
def main():
6+
device = Device()
7+
frames = {}
8+
with device.running():
9+
for type_, frame in device:
10+
frames[type_] = frame
11+
if FrameType.Color in frames and FrameType.Depth in frames:
12+
break
13+
14+
rgb, depth = frames[FrameType.Color], frames[FrameType.Depth]
15+
undistorted, registered, big_depth = device.registration.apply(
16+
rgb, depth, with_big_depth=True)
17+
18+
rgb.to_image().save('output_rgb.png')
19+
big_depth.to_image().save('output_depth.tiff')
20+
21+
with open('output_calib.json', 'w') as fobj:
22+
json.dump({
23+
'color': dict(
24+
(k, getattr(device.color_camera_params, k))
25+
for k in 'fx fy cx cy'.split()),
26+
'ir': dict(
27+
(k, getattr(device.ir_camera_params, k))
28+
for k in 'fx fy cx cy k1 k2 k3 p1 p2'.split()),
29+
}, fobj)
30+
31+
if __name__ == '__main__':
32+
main()

examples/dump_pcd.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
# Use the factory calibration to undistort the depth frame and register the RGB
2121
# frame onto it.
2222
rgb, depth = frames[FrameType.Color], frames[FrameType.Depth]
23-
undistorted, registered = device.registration.apply(rgb, depth)
23+
undistorted, registered, big_depth = device.registration.apply(
24+
rgb, depth, with_big_depth=True)
2425

2526
# Combine the depth and RGB data together into a single point cloud.
26-
with open('output.pcd', 'w') as fobj:
27+
with open('output.pcd', 'wb') as fobj:
2728
device.registration.write_pcd(fobj, undistorted, registered)
29+
30+
with open('output_big.pcd', 'wb') as fobj:
31+
device.registration.write_big_pcd(fobj, big_depth, rgb)

freenect2/__init__.py

Lines changed: 165 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,3 @@
1-
"""
2-
.. py:class:: ColorCameraParams
3-
4-
Color camera intrinsic calibration.
5-
6-
.. py:attribute:: fx
7-
8-
Focal length for x-axis (pixels)
9-
10-
.. py:attribute:: fy
11-
12-
Focal length for y-axis (pixels)
13-
14-
.. py:attribute:: cx
15-
16-
Principal point for x-axis (pixels)
17-
18-
.. py:attribute:: cy
19-
20-
Principal point for y-axis (pixels)
21-
22-
.. py:class:: IrCameraParams
23-
24-
IR/depth camera intrinsic calibration.
25-
26-
.. py:attribute:: fx
27-
28-
Focal length for x-axis (pixels)
29-
30-
.. py:attribute:: fy
31-
32-
Focal length for y-axis (pixels)
33-
34-
.. py:attribute:: cx
35-
36-
Principal point for x-axis (pixels)
37-
38-
.. py:attribute:: cy
39-
40-
Principal point for y-axis (pixels)
41-
42-
.. py:attribute:: k1
43-
44-
Radial distortion co-efficient, 1st-order
45-
46-
.. py:attribute:: k2
47-
48-
Radial distortion co-efficient, 2nd-order
49-
50-
.. py:attribute:: k3
51-
52-
Radial distortion co-efficient, 3rd-order
53-
54-
.. py:attribute:: p1
55-
56-
Tangential distortion co-efficient
57-
58-
.. py:attribute:: p2
59-
60-
Tangential distortion co-efficient
61-
62-
"""
631
from __future__ import print_function
642

653
from contextlib import contextmanager
@@ -79,6 +17,8 @@
7917
'FrameFormat',
8018
'Frame',
8119
'Registration',
20+
'IrCameraParams',
21+
'ColorCameraParams'
8222
)
8323

8424
_FREENECT2_SINGLETON = None
@@ -160,6 +100,68 @@ def __call__(self, frame_type, frame):
160100
def get(self, timeout=False):
161101
return self.queue.get(True, timeout)
162102

103+
class ColorCameraParams(object):
104+
"""
105+
Color camera intrinsic calibration.
106+
107+
.. py:attribute:: fx
108+
109+
Focal length for x-axis (pixels)
110+
111+
.. py:attribute:: fy
112+
113+
Focal length for y-axis (pixels)
114+
115+
.. py:attribute:: cx
116+
117+
Principal point for x-axis (pixels)
118+
119+
.. py:attribute:: cy
120+
121+
Principal point for y-axis (pixels)
122+
"""
123+
124+
class IrCameraParams(object):
125+
"""
126+
IR/depth camera intrinsic calibration.
127+
128+
.. py:attribute:: fx
129+
130+
Focal length for x-axis (pixels)
131+
132+
.. py:attribute:: fy
133+
134+
Focal length for y-axis (pixels)
135+
136+
.. py:attribute:: cx
137+
138+
Principal point for x-axis (pixels)
139+
140+
.. py:attribute:: cy
141+
142+
Principal point for y-axis (pixels)
143+
144+
.. py:attribute:: k1
145+
146+
Radial distortion co-efficient, 1st-order
147+
148+
.. py:attribute:: k2
149+
150+
Radial distortion co-efficient, 2nd-order
151+
152+
.. py:attribute:: k3
153+
154+
Radial distortion co-efficient, 3rd-order
155+
156+
.. py:attribute:: p1
157+
158+
Tangential distortion co-efficient
159+
160+
.. py:attribute:: p2
161+
162+
Tangential distortion co-efficient
163+
"""
164+
163165
class Device(object):
164166
"""Control a single device.
165167
@@ -498,6 +500,8 @@ class Registration(object):
498500
499501
"""
500502
def __init__(self, depth_p, rgb_p):
503+
self.depth_p = depth_p
504+
self.rgb_p = rgb_p
501505
self._c_object = ffi.gc(
502506
lib.freenect2_registration_create(depth_p, rgb_p),
503507
lib.freenect2_registration_dispose)
@@ -573,24 +577,47 @@ def get_points_xyz(self, undistorted, rows, cols):
573577
ffi.cast('float*', ys.ctypes.data),
574578
ffi.cast('float*', zs.ctypes.data)
575579
)
576-
return xs, ys, zs
580+
return xs, ys, -zs
577581

578582
def get_points_xyz_array(self, undistorted):
579583
"""Return a 3D array of x, y, z points for each point in an undistorted
580584
frame. Invalid points are Nan-ed.
581585
582586
Args:
583-
undistorted (:py:class:`Frame`): the undistorted depth frame
587+
undistorted (:py:class:`.Frame`): the undistorted depth frame
584588
585589
Returns:
586-
A 512x424x3 array of 3D points. The last dimension corresponding to
590+
A 424x512x3 array of 3D points. The last dimension corresponding to
587591
x, y and z.
588592
589593
"""
590594
cols, rows = np.meshgrid(
591595
np.arange(undistorted.width), np.arange(undistorted.height))
592596
return np.dstack(self.get_points_xyz(undistorted, rows, cols))
593597

598+
def get_big_points_xyz_array(self, big_depth):
599+
"""Like :py:meth:`.get_points_xyz_array` but operates on the "big" depth
600+
map which can be returned from :py:meth:`.apply`.
601+
602+
Args:
603+
big_depth (:py:class:`.Frame`): big 1920x1082 frame returned from
604+
:py:meth:`.apply`.
605+
606+
Returns:
607+
A 1082x1920x3 array of 3D points. The last dimension corresponding
608+
to x, y and z.
609+
610+
"""
611+
points = np.ones((big_depth.height, big_depth.width, 3), dtype=np.float32)
612+
points[..., 2] = 1e-3 * big_depth.to_array()
613+
points[..., 0], points[..., 1] = np.meshgrid(
614+
(np.arange(1920) - self.rgb_p.cx) / self.rgb_p.fx,
615+
(1080-np.arange(-1, 1081) - self.rgb_p.cy) / self.rgb_p.fy)
616+
points[..., 0] *= points[..., 2]
617+
points[..., 1] *= points[..., 2]
618+
619+
return points
620+
594621
def write_pcd(self, file_object, undistorted, registered=None):
595622
"""Write depth map and (optionally) RGB data to libpcl-compatible PCD
596623
format file. If the registered RGB frame is present, each point is
@@ -604,38 +631,78 @@ def write_pcd(self, file_object, undistorted, registered=None):
604631
Args:
605632
file_object (file): A file object to write PCD data to
606633
undistorted (:py:class:`Frame`): the undistorted depth frame
607-
rgb (:py:class:`Frame`): if not-None, the RGB data corresponding to
634+
registered (:py:class:`Frame`): if not-None, the RGB data corresponding to
608635
the depth frame.
609636
"""
610-
cols, rows = np.meshgrid(np.arange(undistorted.width), np.arange(undistorted.height))
611-
xs, ys, zs = self.get_points_xyz(undistorted, rows, cols)
612-
n_points = int(np.product(xs.shape))
613-
614-
file_object.write(b'VERSION .7\n')
615-
616-
if registered is None:
617-
file_object.write(
618-
b'FIELDS x y z\nSIZE 4 4 4\nTYPE F F F\nCOUNT 1 1 1\n')
619-
data = np.zeros((n_points, 3), order='C', dtype=np.float32)
620-
data[:, 0] = -xs.flatten()
621-
data[:, 1] = -ys.flatten()
622-
data[:, 2] = -zs.flatten()
623-
else:
624-
file_object.write(
625-
b'FIELDS x y z rgb\nSIZE 4 4 4 4\nTYPE F F F F\nCOUNT 1 1 1 1\n')
626-
bgrx = registered.to_array().astype(np.uint32)
627-
rgbs = (
628-
bgrx[..., 0] + (bgrx[..., 1] << 8) + (bgrx[..., 2] << 16)
629-
).view(np.float32)
630-
data = np.zeros((n_points, 4), order='C', dtype=np.float32)
631-
data[:, 0] = -xs.flatten()
632-
data[:, 1] = -ys.flatten()
633-
data[:, 2] = -zs.flatten()
634-
data[:, 3] = rgbs.flatten()
635-
636-
file_object.write('WIDTH {}\n'.format(undistorted.width).encode())
637-
file_object.write('HEIGHT {}\n'.format(undistorted.height).encode())
638-
file_object.write(b'VIEWPOINT 0 0 0 1 0 0 0\n')
639-
file_object.write('POINTS {}\n'.format(n_points).encode())
640-
file_object.write(b'DATA binary\n')
641-
file_object.write(data.tobytes())
637+
write_pcd(
638+
file_object, self.get_points_xyz_array(undistorted), registered)
639+
640+
def write_big_pcd(self, file_object, big_depth, rgb=None):
641+
"""Write depth map and (optionally) RGB data to libpcl-compatible PCD
642+
format file. Works like :py:meth:`.write_pcd` except that it works on
643+
the "big" depth map which can be returned from :py:meth:`apply`. If the
644+
RGB frame is present, each point is coloured according to the image
645+
otherwise the points are left uncoloured.
646+
647+
.. note::
648+
649+
Under Python 3 the file object *must* be opened in binary mode.
650+
651+
Args:
652+
file_object (file): A file object to write PCD data to
653+
big_depth (:py:class:`Frame`): the 1920x1082 de[th frame
654+
registered (:py:class:`Frame`): if not-None, the RGB data from the
655+
color camera
656+
"""
657+
write_pcd(
658+
file_object,
659+
self.get_big_points_xyz_array(big_depth)[1:-1, ...], rgb)
660+
661+
def write_pcd(file_object, points, rgb=None):
662+
"""Write 3d points and (optionally) RGB data to libpcl-compatible PCD
663+
format file. If the registered RGB frame is present, each point is
664+
coloured according to the image otherwise the points are left
665+
uncoloured.
666+
667+
.. note::
668+
669+
Under Python 3 the file object *must* be opened in binary mode.
670+
671+
Args:
672+
file_object (file): A file object to write PCD data to
673+
points (array): A NxMx3 array of 3d points.
674+
rgb (:py:class:`Frame`): if not-None, the RGB frame corresponding to
675+
the points array. Assumed to be NxM.
676+
"""
677+
assert len(points.shape) == 3
678+
xs, ys, zs = points[..., 0], points[..., 1], points[..., 2]
679+
n_points = int(np.product(points.shape[:-1]))
680+
681+
file_object.write(b'VERSION .7\n')
682+
683+
if rgb is None:
684+
file_object.write(
685+
b'FIELDS x y z\nSIZE 4 4 4\nTYPE F F F\nCOUNT 1 1 1\n')
686+
data = np.zeros((n_points, 3), order='C', dtype=np.float32)
687+
data[:, 0] = xs.flatten()
688+
data[:, 1] = ys.flatten()
689+
data[:, 2] = zs.flatten()
690+
else:
691+
file_object.write(
692+
b'FIELDS x y z rgb\nSIZE 4 4 4 4\nTYPE F F F F\nCOUNT 1 1 1 1\n')
693+
bgrx = rgb.to_array().astype(np.uint32)
694+
rgbs = (
695+
bgrx[..., 0] + (bgrx[..., 1] << 8) + (bgrx[..., 2] << 16)
696+
).view(np.float32)
697+
data = np.zeros((n_points, 4), order='C', dtype=np.float32)
698+
data[:, 0] = xs.flatten()
699+
data[:, 1] = ys.flatten()
700+
data[:, 2] = zs.flatten()
701+
data[:, 3] = rgbs.flatten()
702+
703+
file_object.write('WIDTH {}\n'.format(points.shape[1]).encode())
704+
file_object.write('HEIGHT {}\n'.format(points.shape[0]).encode())
705+
file_object.write(b'VIEWPOINT 0 0 0 1 0 0 0\n')
706+
file_object.write('POINTS {}\n'.format(n_points).encode())
707+
file_object.write(b'DATA binary\n')
708+
file_object.write(data.tobytes())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
setup(
99
name='freenect2',
1010
description='Python bindings for the libfreenect2 Kinect for Windows driver',
11-
version='0.2.2',
11+
version='0.2.3',
1212
author='Rich Wareham',
1313
author_email='[email protected]',
1414
url='https://github.com/rjw57/freenect2-python',

0 commit comments

Comments
 (0)