Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
528 changes: 340 additions & 188 deletions README.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions modules/zividsamples/save_load_matrix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""
Save and load Zivid 4x4 transformation matrices from and to YAML files.

The translation part of the matrix must use the same unit as the point cloud it is applied to.
Zivid point clouds are in millimeters by default, so robot poses and transforms are also expected in millimeters.

"""

from pathlib import Path
Expand Down
176 changes: 176 additions & 0 deletions source/applications/advanced/capture_undistort_2d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
"""
Use camera intrinsics to undistort a 2D image.

The example will prompt the user for whether to capture an image (2D) or a point cloud (3D).
In both instances it will operate on an BGRA image. However, in the 3D case it will extract
the BGRA image from the point cloud. The 2D variant is faster.

Note: This example uses experimental SDK features, which may be modified, moved, or deleted in the future without notice.

For more information on lens distortion and undistorting the 2D image, check out this tutorial:
https://support.zivid.com/en/latest/camera/reference-articles/color-spaces-and-output-formats.html

"""

from typing import Tuple

import cv2
import numpy as np
import zivid
import zivid.experimental.calibration
from zividsamples.display import display_bgr, display_pointcloud


def _image_to_bgr(image: zivid.Image) -> np.ndarray:
"""Convert a Zivid BGRA image to an OpenCV BGR image.

Args:
image: Zivid BGRA image

Returns:
bgr: BGR image (HxWx3 ndarray)

"""
bgra = image.copy_data()
return cv2.cvtColor(bgra, cv2.COLOR_BGRA2BGR)


def _get_image_3d(camera: zivid.Camera, settings: zivid.Settings) -> np.ndarray:
"""Capture a point cloud and extract its color image as an OpenCV BGR image.

Args:
camera: Zivid Camera handle
settings: Capture settings

Returns:
bgr: BGR image (HxWx3 ndarray)

"""
print("3D mode")

print("Capturing frame")
frame = camera.capture_2d_3d(settings)

print("Visualizing point cloud")
display_pointcloud(frame)

print("Converting to OpenCV BGRA image")
image = frame.point_cloud().copy_image("bgra_srgb")

image_file = "Image.png"
print(f"Saving 2D color image to file: {image_file}")
image.save(image_file)

return _image_to_bgr(image)


def _get_image_2d(camera: zivid.Camera, settings_2d: zivid.Settings2D) -> np.ndarray:
"""Capture a 2D frame and return its color image as an OpenCV BGR image.

Args:
camera: Zivid Camera handle
settings_2d: 2D capture settings

Returns:
bgr: BGR image (HxWx3 ndarray)

"""
print("2D mode")

print("Capturing 2D frame")
frame_2d = camera.capture_2d(settings_2d)

print("Getting BGRA image")
image = frame_2d.image_bgra_srgb()

print("Converting to OpenCV BGR image")

image_file = "Image.png"
print(f"Saving 2D color image to file: {image_file}")
image.save(image_file)

return _image_to_bgr(image)


def _reformat_camera_intrinsics(camera_intrinsics: zivid.CameraIntrinsics) -> Tuple[np.ndarray, np.ndarray]:
"""Reformat Zivid camera intrinsics into an OpenCV camera matrix and distortion coefficients.

Args:
camera_intrinsics: Zivid camera intrinsics

Returns:
camera_matrix: OpenCV camera matrix (3x3 ndarray)
distortion_coefficients: OpenCV distortion coefficients (1x5 ndarray)

"""
distortion_coefficients = np.zeros((1, 5), dtype=np.float64)
camera_matrix = np.zeros((3, 3), dtype=np.float64)

distortion_coefficients[0, 0] = camera_intrinsics.distortion.k1
distortion_coefficients[0, 1] = camera_intrinsics.distortion.k2
distortion_coefficients[0, 2] = camera_intrinsics.distortion.p1
distortion_coefficients[0, 3] = camera_intrinsics.distortion.p2
distortion_coefficients[0, 4] = camera_intrinsics.distortion.k3

camera_matrix[0, 0] = camera_intrinsics.camera_matrix.fx
camera_matrix[0, 2] = camera_intrinsics.camera_matrix.cx
camera_matrix[1, 1] = camera_intrinsics.camera_matrix.fy
camera_matrix[1, 2] = camera_intrinsics.camera_matrix.cy
camera_matrix[2, 2] = 1

return camera_matrix, distortion_coefficients


def _main() -> None:
app = zivid.Application()

print("Connecting to camera")
camera = app.connect_camera()

command = input('Enter "2d" or "3d" to select mode, then press Enter/Return to confirm\n')
use_2d = command in ("2d", "2D")

settings_2d = zivid.Settings2D(acquisitions=[zivid.Settings2D.Acquisition()])

settings = zivid.Settings(
acquisitions=[zivid.Settings.Acquisition()],
color=settings_2d,
)

bgr = _get_image_2d(camera, settings_2d) if use_2d else _get_image_3d(camera, settings)

print("Undistorting BGR image")

if use_2d:
camera_matrix, distortion_coefficients = _reformat_camera_intrinsics(
zivid.experimental.calibration.intrinsics(camera, settings_2d)
)
else:
camera_matrix, distortion_coefficients = _reformat_camera_intrinsics(
zivid.experimental.calibration.intrinsics(camera, settings)
)

size = (bgr.shape[1], bgr.shape[0])
optimal_camera_matrix = cv2.getOptimalNewCameraMatrix(camera_matrix, distortion_coefficients, size, 1, size)[0]

bgr_undistorted = cv2.undistort(bgr, camera_matrix, distortion_coefficients)
bgr_undistorted_full = cv2.undistort(bgr, camera_matrix, distortion_coefficients, None, optimal_camera_matrix)

image_distorted_file = "ImageDistorted.jpg"
display_bgr(bgr, "Distorted BGR image")
print(f"Visualizing and saving BGR image to file: {image_distorted_file}")
cv2.imwrite(image_distorted_file, bgr)

image_undistorted_file = "ImageUndistorted.jpg"
display_bgr(bgr_undistorted, "Undistorted BGR image")
print(f"Visualizing and saving undistorted BGR image to file: {image_undistorted_file}")
cv2.imwrite(image_undistorted_file, bgr_undistorted)

image_undistorted_full_file = "ImageUndistortedFull.jpg"
display_bgr(bgr_undistorted_full, "Undistorted BGR image - full")
print(f"Visualizing and saving undistorted BGR image (full) to file: {image_undistorted_full_file}")
cv2.imwrite(image_undistorted_full_file, bgr_undistorted_full)


if __name__ == "__main__":
_main()
24 changes: 8 additions & 16 deletions source/applications/advanced/create_depth_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import cv2
import numpy as np
import zivid
from zividsamples.display import display_bgr
from zividsamples.paths import get_sample_data_path


Expand Down Expand Up @@ -50,19 +49,6 @@ def _point_cloud_to_cv_bgr(point_cloud: zivid.PointCloud) -> np.ndarray:
return bgra[:, :, :3]


def _visualize_and_save_image(image: np.ndarray, image_file: str, title: str) -> None:
"""Visualize and save image to file.

Args:
image: BGR image (HxWx3 ndarray)
image_file: File name
title: OpenCV Window name

"""
display_bgr(image, title)
cv2.imwrite(image_file, image)


def _main() -> None:
# Application class must be initialized before using other Zivid classes.
app = zivid.Application() # noqa: F841 # pylint: disable=unused-variable
Expand All @@ -78,14 +64,20 @@ def _main() -> None:

bgr_image_file = "ImageRGB.png"
print(f"Visualizing and saving BGR image to file: {bgr_image_file}")
_visualize_and_save_image(bgr, bgr_image_file, "BGR image")
cv2.imshow("BGR image", bgr)
print("Press any key to continue")
cv2.waitKey(0)
cv2.imwrite(bgr_image_file, bgr)

print("Converting to Depth map in OpenCV format")
z_color_map = _point_cloud_to_cv_z(point_cloud)

depth_map_file = "DepthMap.png"
print(f"Visualizing and saving Depth map to file: {depth_map_file}")
_visualize_and_save_image(z_color_map, depth_map_file, "Depth map")
cv2.imshow("Depth map", z_color_map)
print("Press any key to continue")
cv2.waitKey(0)
cv2.imwrite(depth_map_file, z_color_map)


if __name__ == "__main__":
Expand Down
21 changes: 18 additions & 3 deletions source/applications/advanced/downsample.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from pathlib import Path

import zivid
from zividsamples.display import display_pointcloud
from zividsamples.paths import get_sample_data_path


Expand All @@ -33,6 +32,22 @@ def _options() -> argparse.Namespace:
return parser.parse_args()


def visualize_point_cloud(point_cloud: zivid.PointCloud) -> None:
"""Display point cloud provided either as PointCloud.

Args:
point_cloud: zivid.PointCloud

"""
with zivid.visualization.Visualizer() as visualizer:
visualizer.set_window_title("Zivid Point Cloud Visualizer")
visualizer.colors_enabled = True
visualizer.axis_indicator_enabled = True
visualizer.show(point_cloud)
visualizer.reset_to_fit()
visualizer.run()


def _main() -> None:
user_options = _options()
data_file = user_options.zdf_path
Expand All @@ -45,7 +60,7 @@ def _main() -> None:

print(f"Before downsampling: {point_cloud.width * point_cloud.height} point cloud")

display_pointcloud(point_cloud)
visualize_point_cloud(point_cloud)

print("Downsampling point cloud")
print("This does not modify the current point cloud but returns")
Expand All @@ -60,7 +75,7 @@ def _main() -> None:

print(f"After downsampling: {point_cloud.width * point_cloud.height} point cloud")

display_pointcloud(point_cloud)
visualize_point_cloud(point_cloud)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
Zivid primarily operate with a (4x4) transformation matrix. This example shows how to use Eigen to convert to and from:
AxisAngle, Rotation Vector, Roll-Pitch-Yaw, Quaternion.

Note: the translation part of the transformation matrix is in millimeters (mm), since Zivid point clouds are in
millimeters. If your robot reports translation in meters, multiply it by 1000 before saving the pose.

The convenience functions from this example can be reused in applicable applications. The YAML files for this sample
can be found under the main instructions for Zivid samples.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ def _get_frame_and_transform_matrix(
frame = camera.capture_2d_3d(settings)
robot_pose = np.array(con.receive().actual_TCP_pose)

translation = robot_pose[:3] * 1000
meters_to_millimeters = 1000
translation = robot_pose[:3] * meters_to_millimeters
rotation_vector = robot_pose[3:]
rotation = Rotation.from_rotvec(rotation_vector)
transform = np.eye(4)
Expand Down
3 changes: 3 additions & 0 deletions source/applications/advanced/projector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 2D Image Projection

> **Tutorial:** Read the full [2D Image Projection](https://support.zivid.com/en/latest/camera/academy/camera/2d-image-projection.html) tutorial on Zivid Knowledge Base.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
f"however it might not be available for your Python version: {sys.version_info.major}.{sys.version_info.minor}. "
"See https://pypi.org/project/open3d/ for supported versions."
)
sys.exit(1)
raise


def _create_open3d_point_cloud(point_cloud: zivid.PointCloud) -> o3d.geometry.PointCloud:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Capture point clouds, with color, from the Zivid camera, and visualize them in a loop.

"""

import threading
import time

import zivid


def _main() -> None:
app = zivid.Application()

print("Connecting to camera")
camera = app.connect_camera()

print("Creating default settings")
settings = zivid.Settings(
engine=zivid.Settings.Engine.phase,
acquisitions=[zivid.Settings.Acquisition()],
color=zivid.Settings2D(acquisitions=[zivid.Settings2D.Acquisition()]),
)

print("Capturing frame")
frame = camera.capture_2d_3d(settings)

print("Setting up visualization")
visualizer_running = threading.Event()

print("Visualizing point cloud")
with zivid.visualization.Visualizer() as visualizer:
visualizer.show(frame)
visualizer.reset_to_fit()

def _capture_thread() -> None:
while visualizer_running.is_set():
new_frame = camera.capture_2d_3d(settings)
if visualizer_running.is_set():
visualizer.show(new_frame)
time.sleep(0.01)

capture_thread = threading.Thread(target=_capture_thread)

print("Running visualizer. Blocking until window closes.")
visualizer_running.set()
capture_thread.start()
visualizer.run()
visualizer_running.clear()

capture_thread.join()

print("Visualizer closed")


if __name__ == "__main__":
_main()
Loading
Loading