Skip to content

Commit

Permalink
Merge branch '51-o3d3xx-o3x1xx-add-python-based-examples' into 'main'
Browse files Browse the repository at this point in the history
Release for O3D and O3X examples

Closes #51

See merge request syntron/support/csr/ifm3d/ifm3d-examples!50
  • Loading branch information
lola-masson committed Sep 16, 2024
2 parents 954e190 + 7d27a0b commit 890c538
Show file tree
Hide file tree
Showing 52 changed files with 1,079 additions and 168 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Changelog for ifm3d-examples
## Unreleased

## 1.2.1
- Changed the Python example on how to activate the CAN interface on the OVP to check if the CAN interface is available instead of checking for a specific firmware version.
- Add Python and C++ examples on how to deserialize information contained in the `TOF_INFO` buffer.
- Add Python and C++ examples on how to retrieve the JSON schema.
- Add a Dockerfile to change the CAN bitrate and sample-point.
- Reset the validator's schema at every call in the ODS configuration example.
- Update the ifm3d playground to use the `Port` function to get the PCIC port.
- Update the Python `update_settings_to_new_fw_schema.py` example to create a `logs` directory if it does not already exist.
- Add Python and C++ examples for the O3D3xx and O3X1xx devices to get data using a callback or a single frame
- Add Python and C++ examples for the O3D3xx to deserialize several buffers and unpack diagnostic data for the O3D3xx camera.
- Add a folder for examples common to the three device types.
- Add Python and C++ examples on how to identify the format of data in a buffer.
- Fix `ssh_key_gen.py` overwrites authorized keys rather than append to them.
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ The examples have been tested in the following combination of versions:

| ifm3d-examples version | O3R firmware | O3D firmware | O3X firmware | ifm3d library | Comment |
| ---------------------- | ------------- | -------------------- | ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- |
| 0.1.0 | NA | 1.80.8656, 1.71.9079 | 1.1.190 | 0.11.0 | |
| 1.0.0 | 1.1.30 | Not supported | Not supported | 1.4.3 | |
| 1.1.0 | 1.1.30, 1.4.X | Not supported | Not supported | 1.4.3, 1.5.3 | Some of the examples are only applicable to the O3R firmware version 1.4.X (for example, the IMU and CAN examples). |
| 1.2.1 | 1.1.X, 1.4.X | 1.71.9079, 1.80.8656 | 1.1.190 | 1.4.3, 1.5.3 | Additional examples for the O3D and O3X devices. |
| 1.2.0 | 1.1.30, 1.4.X | 1.71.9079, 1.80.8656 | 1.1.190 | 1.4.3, 1.5.3 | Support of the O3D3xx and O3X1xx examples |
| 1.1.0 | 1.1.30, 1.4.X | Not supported | Not supported | 1.4.3, 1.5.3 | Some of the examples are only applicable to the O3R firmware version 1.4.X (for example, the IMU and CAN examples). |
| 1.0.0 | 1.1.30 | Not supported | Not supported | 1.4.3 | |
| 0.1.0 | NA | 1.80.8656, 1.71.9079 | 1.1.190 | 0.11.0 | |


Any other version might work but has not been explicitly tested.

Expand All @@ -40,7 +42,7 @@ This folder contains examples that apply to the three device types: the O3R perc

## o3d3xx-o3x1xx

This folder contains examples for the O3D3XX and the O3X1XX camera series.
This folder contains examples for the O3D3XX and the O3X1XX camera series, in Python and C++.

## ovp8xx

Expand All @@ -51,7 +53,7 @@ This folder contains examples for the O3R platform, which is composed of an OVP8
To get started with this project, follow the instructions below:

1. Clone the repository.
2. Navigate to o3d3xx-o3x1xx or ovp8xx, depending on the device you are interested in.
2. Navigate to common, o3d3xx-o3x1xx or ovp8xx, depending on the device you are interested in.
3. Choose a programming language and the example that aligns with your requirements.
4. Follow the instructions provided in the example's README file to set up and run the example, or open up the example file to read through the relevant setup.

Expand Down
4 changes: 3 additions & 1 deletion common/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
ifm3dpy
numpy
numpy
open3d
opencv_python
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# ifm3dpy Viewer

This is an example application for retrieving different kinds of images from an O3R platform.
This is an example application for retrieving different kinds of images from any of the O3R, O3D or O3X devices.

## Download the code
If you built ifm3d from source, you already have the code, in `ifm3d/examples/o3r/viewer`.
If not, you can find the script [here](https://github.com/ifm/ifm3d/tree/main/examples/o3r/viewer).
## Installation

The recommended way is to use a virtual environment.
Expand Down Expand Up @@ -41,7 +38,7 @@ Consider [the Python documentation](../../../doc/sphinx/content/python.md) for m

### Install requirements
```sh
pip install -r examples/python/viewer/requirements.txt
pip install -r requirements.txt
```

## Usage
Expand Down Expand Up @@ -73,7 +70,7 @@ python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image amplit
python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image xyz
```

### Display the JPEG image
### Display the JPEG image (only for the O3R)
```sh
python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50010 --image jpeg
```
194 changes: 194 additions & 0 deletions common/python/viewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
#############################################
# Copyright 2024-present ifm electronic, gmbh
# SPDX-License-Identifier: Apache-2.0
#############################################
"""This program is a small viewer that can
be used to display amplitude, distance, xyz or JPEG
images from any of the supported devices (O3X, O3D and O3R).
The JPEG image is only supported for the O3R platform.
"""

import argparse
import collections
from functools import partial
import logging
import time
from typing import Callable
import cv2
from ifm3dpy.device import Device, O3R
from ifm3dpy.framegrabber import FrameGrabber, buffer_id


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format="%(message)s")

try:
import open3d

OPEN3D_AVAILABLE = True
except ModuleNotFoundError:
OPEN3D_AVAILABLE = False


def get_jpeg(self, img_queue: collections.deque):
"""Get the JPEG image from the frame
and decodes it so it can be displayed.
"""
rgb = cv2.imdecode(self.get_buffer(buffer_id.JPEG_IMAGE), cv2.IMREAD_UNCHANGED)
img_queue.append(rgb)


def get_distance(self, img_queue: collections.deque):
"""Get the distance image from the frame
and normalizes it for display.
"""
img = cv2.normalize(
self.get_buffer(buffer_id.RADIAL_DISTANCE_IMAGE),
None,
0,
255,
cv2.NORM_MINMAX,
cv2.CV_8U,
)
img = cv2.applyColorMap(img, cv2.COLORMAP_JET)
img_queue.append(img)


def get_amplitude(self, img_queue: collections.deque):
"""Returns the amplitude data extracted
from the frame.
"""
img_queue.append(self.get_buffer(buffer_id.NORM_AMPLITUDE_IMAGE))


def get_xyz(self, img_queue: collections.deque):
"""Returns the xyz data extracted
from the frame.
"""
img_queue.append(self.get_buffer(buffer_id.XYZ))


def display_2d(fg: FrameGrabber, getter: Callable, title: str):
"""Display the requested 2D data (distance, amplitude or JPEG)"""
if getter.__name__ == "get_jpeg":
fg.start([buffer_id.JPEG_IMAGE])
else:
fg.start(
[
buffer_id.NORM_AMPLITUDE_IMAGE,
buffer_id.RADIAL_DISTANCE_IMAGE,
]
)
img_queue = collections.deque(maxlen=10)
fg.on_new_frame(partial(getter, img_queue=img_queue))
time.sleep(3)

cv2.startWindowThread()
cv2.namedWindow(title, cv2.WINDOW_NORMAL)
while True:
if img_queue:
cv2.imshow(title, img_queue.pop())
cv2.waitKey(15)

if cv2.getWindowProperty(title, cv2.WND_PROP_VISIBLE) < 1:
break

cv2.destroyAllWindows()


def display_3d(fg: FrameGrabber, getter: Callable, title: str):
"""Stream and display the point cloud.
"""
fg.start(
[buffer_id.XYZ]
)
img_queue = collections.deque(maxlen=10)
fg.on_new_frame(partial(getter, img_queue=img_queue))
time.sleep(3)
vis = open3d.visualization.Visualizer()
vis.create_window(title)

first = True
while True:
if img_queue:
img = img_queue.pop()

img = img.reshape(img.shape[0] * img.shape[1], 3)
pcd = open3d.geometry.PointCloud()
pcd.points = open3d.utility.Vector3dVector(img)

vis.clear_geometries()
vis.add_geometry(pcd, first)
if not vis.poll_events():
break

vis.update_renderer()

first = False

vis.destroy_window()


def main():
image_choices = ["distance", "amplitude", "jpeg"]
if OPEN3D_AVAILABLE:
image_choices += ["xyz"]

parser = argparse.ArgumentParser()

parser.add_argument(
"--image",
help="The image to received (default: distance). The jpeg image is only available for the O3R.",
type=str,
choices=image_choices,
required=True,
)
parser.add_argument(
"--ip",
help="IP address of the sensor (default: 192.168.0.69)",
type=str,
required=False,
default="192.168.0.69",
)
parser.add_argument(
"--xmlrpc-port",
help="XMLRPC port of the sensor (default: 80)",
type=int,
required=False,
default=80,
)
parser.add_argument(
"--port",
help="The port from which images should be received (for the O3R only)",
type=str,
required=False,
)
args = parser.parse_args()

getter = globals()["get_" + args.image]

device = Device(args.ip, args.xmlrpc_port)
device_type = device.who_am_i()
logging.info(f"Device type is: {device_type}")
if device_type == device.device_family.O3R:
if args.port is None:
raise ValueError("A port should be provided.")
o3r = O3R(args.ip)
fg = FrameGrabber(device, pcic_port=o3r.port(args.port).pcic_port)
logging.info(f"Port: {args.port}")
else:
fg = FrameGrabber(device)
if args.image == "jpeg":
raise ValueError("JPEG images are only supported on the O3R platform.")

title = f"{device_type} viewer"

if args.image == "xyz":
display_3d(fg, getter, title)
else:
display_2d(fg, getter, title)


if __name__ == "__main__":
main()
91 changes: 2 additions & 89 deletions o3d3xx-o3x1xx/README.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,3 @@
# O3D3xx and O3X1xx examples

# ifm3d Examples

This project is formerly the `examples` sub-module of the
[ifm3d](https://github.com/ifm/ifm3d) project. It has been moved to a
standalone project to increase its efficacy as a teaching tool. Specifically,
beyond providing concrete code examples for interfacing to `ifm3d` it also
shows how to integrate `ifm3d` into an external project via `cmake`. This
project relies upon `ifm3d` version 1.4.3 or better. The remainder of the old
`README` now follows -- with minor edits.

This directory contains example programs that utilize `ifm3d`. The
intention is to create standalone programs that illustrate one very specific
concept in order to serve the purpose of letting developers ramp up quickly
with using the library. The build infrastructure in this directory is minimal
and the programs are intended to be run in place. Additionally, unless
specifically stated otherwise, things like performance and robust error
handling are not demonstrated. The purpose is to clearly illustrate the task
without clouding it with the details of real-world software engineering --
unless, of course, that was the point of the example.

It is expected that this library of examples will grow over time in response to
common themes we see on the issue tracker.

## Prerequisites
- [fmt](https://github.com/fmtlib/fmt.git)
- [openCV](https://opencv.org/releases/)


## Building the examples

Assuming you are starting from the top-level directory of this source
distribution:

$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .

### Windows examples
For Windows-based target, with Visual Studio 2017, assuming you are starting from the top-level directory of this source
distribution:

$ set IFM3D_CMAKE_GENERATOR="Visual Studio 17 2022"
$ cd ifm3d-examples/o3d3xx-o3x1xx
$ mkdir build
$ cd build
# Note: To show images Opencv is used,hence build path to opencv is added into -DCMAKE_PREFIX_PATH
$ cmake -G %IFM3D_CMAKE_GENERATOR% -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=ON -DCMAKE_PREFIX_PATH=F:\windows\ifm3d_deps\install;F:\opencv-4.9.0-windows\opencv\build ..
$ cmake --build . --config Release --target ALL_BUILD

At this stage, projects are built and you will find *IFM3D_EXAMPLES.sln* in build folder.
Use Release / RelWithDebInfo configuration to run and investigate application examples.
Please add PATH variable to projects :

PATH=%IFM3D_BUILD_DIR%\install\bin;%IFM3D_BUILD_DIR%\install\x64\vc%MSVC_MAJOR_VERSION%.%MSVC_MINOR_VERSION%\bin;%PATH%

For instance, you can fill directly in VS *Project Properties* / *Debugging* / *Environment* with `PATH=C:\ifm3d\install\bin;C:\ifm3d\install\x64\vc14.1\bin;%PATH%`

## What is included?


* [ex-file_io](file_io/ex-file_io.cpp) Shows how to capture data from the camera and
write the images to disk. In this example, the amplitude image is written out as PNG files.
* [ex-getmac](getmac/ex-getmac.cpp)
Request the MAC address from the camera. The MAC address can be used as
a unique identifier.
* [ex-timestamp](timestamp/ex-timestamp.cpp)
Request some frames from the camera and write the timestamps to stdout
* [ex-exposure_times](exposure_time/ex-exposure_times.cpp) Shows how to change imager
exposure times on the fly while streaming in pixel data and validating the
setting of the exposure times registered to the frame data.
* [ex-fast_app_switch](fast_app_switch/ex-fast_app_switch.cpp) Shows how to switch between two
applications on the camera using PCIC
* [ex-pcicclient_async_messages](pcicclient_async_messages/ex-pcicclient_async_messages.cpp) Shows how to
use the PCICClient module to receive asynchronous notification (and error)
messages from the camera.
* [ex-pcicclient_set_io](pcicclient_set_io/ex-pcicclient_set_io.cpp) Shows how to mutate the digial IO pins
on the O3D camera by the PCIC interface.
* [ex-simpleImage_ppm_io](simpleimage/example/ex-simpleImage_ppm_io.cpp) Shows how to write your own
image container which does not depend on PCL nor OpenCV.
* [ex-multi_camera_grabber](multi_camera_grabber/ex-multi_camera_grabber.cpp) demonstrate's how to acquire frames from multiple ifm 3D camera's,
see the example [documentation](doc/ex-multi_camera_grabber.md) for more details.

### Note: Use of `Device` and `LegacyDevice` class

Please note `Device` is the base class and `LegacyDevice` inherits from the `Device` class. Object from `ifm3d::Device` can be created while accessing the device functionalities and `ifm3d::LegacyDevice` object can be created while using the application specific methods of legacy devices like `O3D/O3X`.

## LICENSE
Please see the file called [LICENSE](LICENSE).
This folders container Python and C++ examples for the O3D3xx and O3X1xx series of devices. Head to the `cpp` or to the `python` folder for more details on the individual examples in these languages.
Loading

0 comments on commit 890c538

Please sign in to comment.