diff --git a/CHANGELOG.md b/CHANGELOG.md index 232227a..5ad916b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # 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. @@ -7,6 +8,8 @@ - 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. diff --git a/README.md b/README.md index dc5211d..869c9bc 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 @@ -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. diff --git a/common/python/requirements.txt b/common/python/requirements.txt index 00d2aeb..26cc6e3 100644 --- a/common/python/requirements.txt +++ b/common/python/requirements.txt @@ -1,2 +1,4 @@ ifm3dpy -numpy \ No newline at end of file +numpy +open3d +opencv_python \ No newline at end of file diff --git a/ovp8xx/python/ovp8xxexamples/core/viewer.md b/common/python/viewer.md similarity index 86% rename from ovp8xx/python/ovp8xxexamples/core/viewer.md rename to common/python/viewer.md index 51a91bb..d1998dc 100644 --- a/ovp8xx/python/ovp8xxexamples/core/viewer.md +++ b/common/python/viewer.md @@ -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. @@ -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 @@ -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 ``` diff --git a/common/python/viewer.py b/common/python/viewer.py new file mode 100644 index 0000000..3f1867e --- /dev/null +++ b/common/python/viewer.py @@ -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() diff --git a/o3d3xx-o3x1xx/README.md b/o3d3xx-o3x1xx/README.md index 0c28ed7..47ba336 100644 --- a/o3d3xx-o3x1xx/README.md +++ b/o3d3xx-o3x1xx/README.md @@ -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. \ No newline at end of file diff --git a/o3d3xx-o3x1xx/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/CMakeLists.txt similarity index 86% rename from o3d3xx-o3x1xx/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/CMakeLists.txt index 6032051..f4580e3 100644 --- a/o3d3xx-o3x1xx/CMakeLists.txt +++ b/o3d3xx-o3x1xx/cpp/CMakeLists.txt @@ -21,6 +21,9 @@ option(BUILD_EXAMPLE_PCICCLIENT_ASYNC_MESSAGES "Build pcicclient-async-messages option(BUILD_EXAMPLE_PCICCLIENT_SET_IO "Build pcicclient-set-io example" ON) option(BUILD_EXAMPLE_TIMESTAMP "Build timestamp example" ON) option(BUILD_EXAMPLE_GET_JSON_MODEL "Build get_json_model example" ON) +option(BUILD_EXAMPLE_DIAGNOSTIC "Build o3d_diagnostic example" ON) +option(BUILD_EXAMPLE_GET_DATA "Build the get_data examples" ON) +option(BUILD_EXAMPLE_DESERIALIZE "Build the deserializer example" ON) ################################################ ## Bring in dependent projects @@ -84,3 +87,14 @@ if(TARGET ifm3d::pcicclient) endif() endif() +if(BUILD_EXAMPLE_DIAGNOSTIC) + add_subdirectory(diagnostic) +endif() + +if(BUILD_EXAMPLE_GET_DATA) + add_subdirectory(get_data) +endif() + +if(BUILD_EXAMPLE_DESERIALIZE) + add_subdirectory(deserialize) +endif() \ No newline at end of file diff --git a/o3d3xx-o3x1xx/cpp/README.md b/o3d3xx-o3x1xx/cpp/README.md new file mode 100644 index 0000000..9e54015 --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/README.md @@ -0,0 +1,77 @@ + +# ifm3d C++ examples + +Beyond providing concrete code examples for interfacing to `ifm3d`, this project also +shows how to integrate `ifm3d` into an external project via `cmake`. This +project relies upon `ifm3d` version 1.4.3 or better. + +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 `cpp` 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? + +- [deserialize](./deserialize/o3d_deserialize.cpp) shows how to extract data from a selected number of buffers, for the O3D camera. +- [diagnostic](./diagnostic/o3d_diagnostic.cpp) shows how ro receive and unpack diagnostic data, for the O3D camera. +- [exposure_time](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. +- [fast_app_switch](fast_app_switch/ex-fast_app_switch.cpp) shows how to switch between two applications on the camera using PCIC. +- [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. +- [get_data_callback](./get_data/get_data_callback.cpp) and [get_data_trigger](./get_data/get_data_tigger.cpp) show how to receive data from the device, either continuously using a callback or a single frame using he software trigger. +- [get_json_model](./get_json_model/ex-get_json_model.cpp) shows how to retrieve the JSON model for the device configuration. +- [getmac](getmac/ex-getmac.cpp) request the MAC address from the camera. The MAC address can be used as a unique identifier. +- [multi_camera_grabber](multi_camera_grabber/ex-multi_camera_grabber.cpp) demonstrates how to acquire frames from multiple ifm 3D camera's, see the example [documentation](./multi_camera_grabber/doc/ex-multi_camera_grabber.md) for more details. +- [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. +- [pcicclient_set_io](pcicclient_set_io/ex-pcicclient_set_io.cpp) shows how to mutate the digital IO pins on the O3D camera by the PCIC interface. +- [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. +- [timestamp](timestamp/ex-timestamp.cpp) requests some frames from the camera and write the timestamps to stdout. + +### 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). diff --git a/o3d3xx-o3x1xx/cmake/modules/MacroOutOfSourceBuild.cmake b/o3d3xx-o3x1xx/cpp/cmake/modules/MacroOutOfSourceBuild.cmake similarity index 100% rename from o3d3xx-o3x1xx/cmake/modules/MacroOutOfSourceBuild.cmake rename to o3d3xx-o3x1xx/cpp/cmake/modules/MacroOutOfSourceBuild.cmake diff --git a/o3d3xx-o3x1xx/cpp/deserialize/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/deserialize/CMakeLists.txt new file mode 100644 index 0000000..9449a84 --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/deserialize/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.11) + +# Global compiler flags +set(CMAKE_BUILD_TYPE Release) # Release or Debug +set(CMAKE_CXX_EXTENSIONS OFF) # OFF -> -std=c++17, ON -> -std=gnu++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED true) + +############################################################## +## finding the ifm3d lib. +############################################################## +find_package(ifm3d 1.5.3 CONFIG + REQUIRED COMPONENTS device framegrabber deserialize + ) + +add_executable(o3d_deserialize o3d_deserialize.cpp) +target_link_libraries(o3d_deserialize + ifm3d::deserialize + ifm3d::device + ifm3d::framegrabber + ) diff --git a/o3d3xx-o3x1xx/cpp/deserialize/o3d_deserialize.cpp b/o3d3xx-o3x1xx/cpp/deserialize/o3d_deserialize.cpp new file mode 100644 index 0000000..effd57f --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/deserialize/o3d_deserialize.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ + /* This example shows how to deserialize the + EXPOSURE_TIME, EXTRINSIC_CALIB, INTRINSIC_CALIB, + INVERSE_INTRINSIC_CALIBRATION and ILLUMINATION_TEMP + buffers to extract data. This example is only relevant + for the O3D3xx devices.*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +int main(){ + // Get the IP from the environment if defined + const char *IP = std::getenv("IFM3D_IP") ? std::getenv("IFM3D_IP") : ifm3d::DEFAULT_IP.c_str(); + std::clog << "IP: " << IP << std::endl; + + ////////////////////////// + // Declare the objects + ////////////////////////// + // Declare the device object + auto o3d = std::make_shared(IP); + + /////////////////////////////// + // Get a frame. Make sure + // the device is in continuous + // mode + /////////////////////////////// + auto fg = std::make_shared(o3d); + fg->Start({ifm3d::buffer_id::EXPOSURE_TIME, + ifm3d::buffer_id::EXTRINSIC_CALIB, + ifm3d::buffer_id::INTRINSIC_CALIB, + ifm3d::buffer_id::INVERSE_INTRINSIC_CALIBRATION, + ifm3d::buffer_id::ILLUMINATION_TEMP}); + std::this_thread::sleep_for(1s); //Grace period after starting the data stream + + auto future = fg->WaitForFrame(); + if (future.wait_for(3s) != std::future_status::ready) { + std::cerr << "Timeout waiting for camera!" << std::endl; + return -1; + } + auto frame = future.get(); + + /////////////////////////////////// + // Extract the data from the frame + // and deserialize it into usable + // chunks. + /////////////////////////////////// + auto exposure_time_buffer = frame->GetBuffer(ifm3d::buffer_id::EXPOSURE_TIME); + auto exposure_time = ifm3d::O3DExposureTimes::Deserialize(exposure_time_buffer); + + auto extrinsic_calib_buffer = frame->GetBuffer(ifm3d::buffer_id::EXTRINSIC_CALIB); + auto extrinsic_calib = ifm3d::O3DExtrinsicCalibration::Deserialize(extrinsic_calib_buffer); + + auto intrinsic_calib_buffer = frame->GetBuffer(ifm3d::buffer_id::INTRINSIC_CALIB); + auto intrinsic_calib = ifm3d::O3DInstrinsicCalibration::Deserialize(intrinsic_calib_buffer); + + auto inv_intrinsic_calib_buffer = frame->GetBuffer(ifm3d::buffer_id::INVERSE_INTRINSIC_CALIBRATION); + auto inv_intrinsic_calib = ifm3d::O3DInverseInstrinsicCalibration::Deserialize(inv_intrinsic_calib_buffer); + + auto illu_temp_buffer = frame->GetBuffer(ifm3d::buffer_id::ILLUMINATION_TEMP); + auto illu_temp = ifm3d::O3DILLUTemperature::Deserialize(illu_temp_buffer); + + //////////////////////////////// + // Display a sample of the data + //////////////////////////////// + std::cout << "Exposure times (ms): " << std::endl; + for(int i = 0; i < exposure_time.data.size(); i++){ + std::cout << exposure_time.data[i] << std::endl; + } + + std::cout << "Extrinsic calibration: " << std::endl; + std::cout << " Translations: " + << extrinsic_calib.data[0] << ", " + << extrinsic_calib.data[1] << ", " + << extrinsic_calib.data[2] << std::endl; + std::cout << " Rotations: " + << extrinsic_calib.data[3] << ", " + << extrinsic_calib.data[4] << ", " + << extrinsic_calib.data[5] << std::endl; + + std::cout << "Intrinsic calibration: " << std::endl; + for(int i = 0; i < intrinsic_calib.data.size(); i++){ + std::cout << intrinsic_calib.data[i] << std::endl; + } + std::cout << "Inverse intrinsic calibration: " << std::endl; + for(int i = 0; i < inv_intrinsic_calib.data.size(); i++){ + std::cout << inv_intrinsic_calib.data[i] << std::endl; + } + + std::cout << "Illumination temperature: " << std::endl; + for(int i = 0; i < illu_temp.data.size(); i++){ + std::cout << illu_temp.data[i] << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/o3d3xx-o3x1xx/cpp/diagnostic/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/diagnostic/CMakeLists.txt new file mode 100644 index 0000000..e7c3b69 --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/diagnostic/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.11) + +#Global compiler flags +set(CMAKE_BUILD_TYPE Release) # Release or Debug +set(CMAKE_CXX_EXTENSIONS OFF) # OFF -> -std=c++14, ON -> -std=gnu++14 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED true) + +#find ifm3d on the system +find_package(ifm3d 1.5.3 CONFIG + REQUIRED COMPONENTS device framegrabber + ) +## Configuration example +add_executable(o3d_diagnostic o3d_diagnostic.cpp) +target_link_libraries(o3d_diagnostic + ifm3d::device + ifm3d::framegrabber + ) \ No newline at end of file diff --git a/o3d3xx-o3x1xx/cpp/diagnostic/o3d_diagnostic.cpp b/o3d3xx-o3x1xx/cpp/diagnostic/o3d_diagnostic.cpp new file mode 100644 index 0000000..ad945a1 --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/diagnostic/o3d_diagnostic.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ + +// This example shows how to unpack diagnostic +// data from the O3D camera. +// The diagnostic data provides important +// information about the camera's internal state, +// such as the temperature. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; +using namespace ifm3d::literals; + +std::tuple unpackData(ifm3d::Buffer_& data) { + int32_t i1, i2, i3, i4; + uint32_t u1, u2; + + // Assuming data is at least 24 bytes: 4 ints (4*4 bytes) + 2 uints (2*4 bytes) + std::memcpy(&i1, data.ptr(0), sizeof(i1)); + std::memcpy(&i2, data.ptr(0) + 4, sizeof(i2)); + std::memcpy(&i3, data.ptr(0) + 8, sizeof(i3)); + std::memcpy(&i4, data.ptr(0) + 12, sizeof(i4)); + std::memcpy(&u1, data.ptr(0) + 16, sizeof(u1)); + std::memcpy(&u2, data.ptr(0) + 20, sizeof(u2)); + + return std::make_tuple(i1, i2, i3, i4, u1, u2); +} + +int main() { + ////////////////////////// + // Declare the O3D objects + // and start the data stream. + ////////////////////////// + // Get the IP from the environment if defined + const char *IP = std::getenv("IFM3D_IP") ? std::getenv("IFM3D_IP") : ifm3d::DEFAULT_IP.c_str(); + std::clog << "IP: " << IP << std::endl; + auto o3d = std::make_shared(IP); + auto fg = std::make_shared(o3d, std::stoi(o3d->DeviceParameter("PcicTcpPort"))); + + // Start the grabber + fg->Start({ifm3d::buffer_id::DIAGNOSTIC}); + // Grace period before trying to get a frame + std::this_thread::sleep_for(std::chrono::seconds(5)); + + ////////////////////////// + // Get a frame + ////////////////////////// + auto future = fg->WaitForFrame(); + if (future.wait_for(3s) != std::future_status::ready) { + std::cerr << "Timeout waiting for camera!" << std::endl; + return -1; + } + auto frame = future.get(); + + ////////////////////////// + // Get the data + ////////////////////////// + auto diagnostic_buffer = frame->GetBuffer(ifm3d::buffer_id::DIAGNOSTIC); + // The diagnostic data is of format unsigned int8 + ifm3d::Buffer_ diag = diagnostic_buffer; + auto unpacked_diagnostic = unpackData(diag); + std::cout << "Illumination temperature (0.1 °C), invalid = 32767: " + << std::get<0>(unpacked_diagnostic) + << std::endl; + std::cout << "Frontend temperature 1 (0.1 °C), invalid = 32767: " + << std::get<1>(unpacked_diagnostic) + << std::endl; + std::cout << "Frontend temperature 2 (0.1 °C), invalid = 32767: " + << std::get<2>(unpacked_diagnostic) + << std::endl; + std::cout << "i.mx6 Temperature (0.1 °C), invalid = 32767: " + << std::get<3>(unpacked_diagnostic) + << std::endl; + std::cout << "Frame duration: " + << std::get<4>(unpacked_diagnostic) + << std::endl; + std::cout << "Framerate: " + << std::get<5>(unpacked_diagnostic) + << std::endl; + return 0; +} \ No newline at end of file diff --git a/o3d3xx-o3x1xx/exposure_time/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/exposure_time/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/exposure_time/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/exposure_time/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/exposure_time/ex-exposure_times.cpp b/o3d3xx-o3x1xx/cpp/exposure_time/ex-exposure_times.cpp similarity index 88% rename from o3d3xx-o3x1xx/exposure_time/ex-exposure_times.cpp rename to o3d3xx-o3x1xx/cpp/exposure_time/ex-exposure_times.cpp index f212a58..db194ff 100644 --- a/o3d3xx-o3x1xx/exposure_time/ex-exposure_times.cpp +++ b/o3d3xx-o3x1xx/cpp/exposure_time/ex-exposure_times.cpp @@ -1,17 +1,6 @@ /* - * Copyright (C) 2016 Love Park Robotics, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distribted on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 */ // diff --git a/o3d3xx-o3x1xx/fast_app_switch/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/fast_app_switch/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/fast_app_switch/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/fast_app_switch/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/fast_app_switch/ex-fast_app_switch.cpp b/o3d3xx-o3x1xx/cpp/fast_app_switch/ex-fast_app_switch.cpp similarity index 92% rename from o3d3xx-o3x1xx/fast_app_switch/ex-fast_app_switch.cpp rename to o3d3xx-o3x1xx/cpp/fast_app_switch/ex-fast_app_switch.cpp index cac3bbe..db94cbd 100644 --- a/o3d3xx-o3x1xx/fast_app_switch/ex-fast_app_switch.cpp +++ b/o3d3xx-o3x1xx/cpp/fast_app_switch/ex-fast_app_switch.cpp @@ -1,17 +1,6 @@ /* - * Copyright (C) 2017 Love Park Robotics, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distribted on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 */ // diff --git a/o3d3xx-o3x1xx/file_io/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/file_io/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/file_io/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/file_io/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/file_io/ex-file_io.cpp b/o3d3xx-o3x1xx/cpp/file_io/ex-file_io.cpp similarity index 67% rename from o3d3xx-o3x1xx/file_io/ex-file_io.cpp rename to o3d3xx-o3x1xx/cpp/file_io/ex-file_io.cpp index 6154a37..96c5816 100644 --- a/o3d3xx-o3x1xx/file_io/ex-file_io.cpp +++ b/o3d3xx-o3x1xx/cpp/file_io/ex-file_io.cpp @@ -1,18 +1,6 @@ /* - * Copyright (C) 2016 Love Park Robotics, LLC - * Copyright (C) 2017 ifm syntron gmbh - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distribted on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 */ // diff --git a/o3d3xx-o3x1xx/cpp/get_data/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/get_data/CMakeLists.txt new file mode 100644 index 0000000..8b0effa --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/get_data/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.11) + +# Global compiler flags +set(CMAKE_BUILD_TYPE Release) # Release or Debug +set(CMAKE_CXX_EXTENSIONS OFF) # OFF -> -std=c++17, ON -> -std=gnu++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED true) + + +############################################################## +## finding the ifm3d lib. +############################################################## +find_package(ifm3d 1.5.3 CONFIG + REQUIRED COMPONENTS device framegrabber common + ) + +add_executable(get_data_callback get_data_callback.cpp) +target_link_libraries(get_data_callback + ifm3d::device + ifm3d::framegrabber + ) + +add_executable(get_data_trigger get_data_trigger.cpp) +target_link_libraries(get_data_trigger + ifm3d::device + ifm3d::framegrabber + ifm3d::common + ) diff --git a/o3d3xx-o3x1xx/cpp/get_data/get_data_callback.cpp b/o3d3xx-o3x1xx/cpp/get_data/get_data_callback.cpp new file mode 100644 index 0000000..c5b0591 --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/get_data/get_data_callback.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ +// This code example showcases how to retrieve data +// continuously using a callback function. +// This example can be used for both the O3D and +// the O3X cameras. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; +using namespace ifm3d::literals; + +void Callback(ifm3d::Frame::Ptr frame) { + // Get every image from the frame + // and display a sample. + std::cout << "Sample data from the frame (taken at pixel [50, 50] for each image): " << std::endl; + + // Refer to the get_data_format to find out + // the format for each image type. + auto conf = frame->GetBuffer(ifm3d::buffer_id::CONFIDENCE_IMAGE); + // The confidence image is of format unsigned int8 + std::cout << "Conf: " << std::to_string(conf.at(50, 50)) << std::endl; + + auto dist = frame->GetBuffer(ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE); + // The distance image is of format float 32 + std::cout << "Dist: " << std::to_string(dist.at(50, 50)) << std::endl; + + + auto noise = frame ->GetBuffer(ifm3d::buffer_id::RADIAL_DISTANCE_NOISE); + // The noise image format is unsigned int16 + std::cout << "Noise: " << noise.at(50,50) << std::endl; + + auto xyz = frame->GetBuffer(ifm3d::buffer_id::XYZ); + // The XYZ image has three channels and its format is float + auto xyz_ptr = xyz.ptr(50, 50); + std::cout << "X: " << xyz_ptr[0] << std::endl; + std::cout << "Y: " << xyz_ptr[1] << std::endl; + std::cout << "Z: " << xyz_ptr[2] << std::endl; +} + +int main() { + // Get the IP from the environment if defined + const char *IP = std::getenv("IFM3D_IP") ? std::getenv("IFM3D_IP") : ifm3d::DEFAULT_IP.c_str(); + std::clog << "IP: " << IP << std::endl; + + ////////////////////////// + // Declare the objects + ////////////////////////// + // Declare the device object + auto device = ifm3d::Device::MakeShared(IP); + + auto fg = std::make_shared(device); + + ////////////////////////// + // Start the framegrabber + // and register the callback + ////////////////////////// + // Set Schema and start the grabber. + // Here we are requesting all the most common + // types of buffers that are available + // for the two cameras. + // Note that we are not requesting the + // amplitude image as it comes in two + // different buffers for the two cameras. + fg->Start({ifm3d::buffer_id::CONFIDENCE_IMAGE, + ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE, + ifm3d::buffer_id::RADIAL_DISTANCE_NOISE, + ifm3d::buffer_id::XYZ}); + + // Register callback function + fg->OnNewFrame(&Callback); + + // This sleep is to prevent the program from before the + // callback has time to execute. + std::this_thread::sleep_for(5s); + fg->Stop(); + + return 0; +} \ No newline at end of file diff --git a/o3d3xx-o3x1xx/cpp/get_data/get_data_trigger.cpp b/o3d3xx-o3x1xx/cpp/get_data/get_data_trigger.cpp new file mode 100644 index 0000000..bd382eb --- /dev/null +++ b/o3d3xx-o3x1xx/cpp/get_data/get_data_trigger.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ +// This code example showcases how to retrieve a +// frame using the software trigger. +// The example applies to both the O3D and O3X +// devices. +// The O3X only allows a single connection, so +// any other application, for example the Vision +// Assistant, must be closed before running this example. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +int main(){ + // Get the IP from the environment if defined + const char *IP = std::getenv("IFM3D_IP") ? std::getenv("IFM3D_IP") : ifm3d::DEFAULT_IP.c_str(); + std::clog << "IP: " << IP << std::endl; + + ////////////////////////// + // Declare the objects + ////////////////////////// + // Declare the device object + auto device = ifm3d::Device::MakeShared(IP); + // Enable the trigger mode. + device->FromJSONStr(R"({"Apps":[{"TriggerMode":"2"}]})"); + + + auto fg = std::make_shared(device); + fg->Start({ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE}); + std::this_thread::sleep_for(1s); //Grace period after starting the data stream + + // Trigger the frame acquisition. The frame will be + // caught by the WaitForFrame function. + fg->SWTrigger(); + auto future = fg->WaitForFrame(); + + if (future.wait_for(3s) != std::future_status::ready) { + std::cerr << "Timeout waiting for camera!" << std::endl; + return -1; + } + auto frame = future.get(); + + auto distance = frame->GetBuffer(ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE); + std::cout << "Sample data from the frame, at index [50, 50]: " << std::endl; + std::cout << distance.at(50,50) << std::endl; + + + return 0; +} \ No newline at end of file diff --git a/o3d3xx-o3x1xx/get_json_model/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/get_json_model/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/get_json_model/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/get_json_model/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/get_json_model/ex-get_json_model.cpp b/o3d3xx-o3x1xx/cpp/get_json_model/ex-get_json_model.cpp similarity index 95% rename from o3d3xx-o3x1xx/get_json_model/ex-get_json_model.cpp rename to o3d3xx-o3x1xx/cpp/get_json_model/ex-get_json_model.cpp index 3d7ee6e..eab0f6d 100644 --- a/o3d3xx-o3x1xx/get_json_model/ex-get_json_model.cpp +++ b/o3d3xx-o3x1xx/cpp/get_json_model/ex-get_json_model.cpp @@ -1,8 +1,7 @@ /* - * Copyright 2022-present ifm electronic, gmbh + * Copyright 2024-present ifm electronic, gmbh * SPDX-License-Identifier: Apache-2.0 */ - // // ex-get_json_model.cpp // diff --git a/o3d3xx-o3x1xx/getmac/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/getmac/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/getmac/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/getmac/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/getmac/ex-getmac.cpp b/o3d3xx-o3x1xx/cpp/getmac/ex-getmac.cpp similarity index 92% rename from o3d3xx-o3x1xx/getmac/ex-getmac.cpp rename to o3d3xx-o3x1xx/cpp/getmac/ex-getmac.cpp index 470b3c2..9215ba5 100644 --- a/o3d3xx-o3x1xx/getmac/ex-getmac.cpp +++ b/o3d3xx-o3x1xx/cpp/getmac/ex-getmac.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022-present ifm electronic, gmbh + * Copyright 2024-present ifm electronic, gmbh * SPDX-License-Identifier: Apache-2.0 */ diff --git a/o3d3xx-o3x1xx/multi_camera_grabber/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/multi_camera_grabber/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/multi_camera_grabber/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/multi_camera_grabber/ex-multi_camera_grabber.cpp b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.cpp similarity index 98% rename from o3d3xx-o3x1xx/multi_camera_grabber/ex-multi_camera_grabber.cpp rename to o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.cpp index bd3eb2d..49e140d 100644 --- a/o3d3xx-o3x1xx/multi_camera_grabber/ex-multi_camera_grabber.cpp +++ b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.cpp @@ -1,8 +1,7 @@ /* - * Copyright 2022-present ifm electronic, gmbh + * Copyright 2024-present ifm electronic, gmbh * SPDX-License-Identifier: Apache-2.0 */ - // // ex-multi_camera_grabber.cpp // diff --git a/o3d3xx-o3x1xx/doc/ex-multi_camera_grabber.md b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.md similarity index 93% rename from o3d3xx-o3x1xx/doc/ex-multi_camera_grabber.md rename to o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.md index f22a8a8..e318a9f 100644 --- a/o3d3xx-o3x1xx/doc/ex-multi_camera_grabber.md +++ b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/ex-multi_camera_grabber.md @@ -1,10 +1,11 @@ ex-multi_camera_grabber ========================= -The primary objective of this example is to demonstrate how to accquire frames from multiple ifm 3D camera's. +The primary objective of this example is to demonstrate how to acquire frames from multiple ifm 3D camera's. The code in this example capture's frames from multiple 3D cameras which are configured to be triggered through software, -and display's the time stamp of the frame received. It also measues the time taken to receive the set of frames. +and display's the time stamp of the frame received. It also measures the time taken to receive the set of frames. +The cameras are triggered one after the other and the frames are received sequentially. The below diagram is representational image of the setup used for this example consisting of a PC and three 3D-camera all on the same network. This can be used as a reference to setup your PC/3D-camera network. diff --git a/o3d3xx-o3x1xx/doc/img/ex-multi_camera_grabber_setup.jpg b/o3d3xx-o3x1xx/cpp/multi_camera_grabber/img/ex-multi_camera_grabber_setup.jpg similarity index 100% rename from o3d3xx-o3x1xx/doc/img/ex-multi_camera_grabber_setup.jpg rename to o3d3xx-o3x1xx/cpp/multi_camera_grabber/img/ex-multi_camera_grabber_setup.jpg diff --git a/o3d3xx-o3x1xx/pcicclient_async_messages/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/pcicclient_async_messages/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/pcicclient_async_messages/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/pcicclient_async_messages/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/pcicclient_async_messages/ex-pcicclient_async_messages.cpp b/o3d3xx-o3x1xx/cpp/pcicclient_async_messages/ex-pcicclient_async_messages.cpp similarity index 100% rename from o3d3xx-o3x1xx/pcicclient_async_messages/ex-pcicclient_async_messages.cpp rename to o3d3xx-o3x1xx/cpp/pcicclient_async_messages/ex-pcicclient_async_messages.cpp diff --git a/o3d3xx-o3x1xx/pcicclient_set_io/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/pcicclient_set_io/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/pcicclient_set_io/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/pcicclient_set_io/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/pcicclient_set_io/ex-pcicclient_set_io.cpp b/o3d3xx-o3x1xx/cpp/pcicclient_set_io/ex-pcicclient_set_io.cpp similarity index 100% rename from o3d3xx-o3x1xx/pcicclient_set_io/ex-pcicclient_set_io.cpp rename to o3d3xx-o3x1xx/cpp/pcicclient_set_io/ex-pcicclient_set_io.cpp diff --git a/o3d3xx-o3x1xx/simpleimage/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/simpleimage/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/simpleimage/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/simpleimage/README.md b/o3d3xx-o3x1xx/cpp/simpleimage/README.md similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/README.md rename to o3d3xx-o3x1xx/cpp/simpleimage/README.md diff --git a/o3d3xx-o3x1xx/simpleimage/ex-simpleImage_ppm_io.cpp b/o3d3xx-o3x1xx/cpp/simpleimage/ex-simpleImage_ppm_io.cpp similarity index 89% rename from o3d3xx-o3x1xx/simpleimage/ex-simpleImage_ppm_io.cpp rename to o3d3xx-o3x1xx/cpp/simpleimage/ex-simpleImage_ppm_io.cpp index f4b43de..710c1f4 100644 --- a/o3d3xx-o3x1xx/simpleimage/ex-simpleImage_ppm_io.cpp +++ b/o3d3xx-o3x1xx/cpp/simpleimage/ex-simpleImage_ppm_io.cpp @@ -1,18 +1,6 @@ /* - * Copyright (C) 2018 ifm electronic, gmbh - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distribted on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 */ // // ex-simpleimage_ppm_io.cpp diff --git a/o3d3xx-o3x1xx/simpleimage/ppm-io-master/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/ppm-io-master/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/simpleimage/ppm-io-master/LICENSE b/o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/LICENSE similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/ppm-io-master/LICENSE rename to o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/LICENSE diff --git a/o3d3xx-o3x1xx/simpleimage/ppm-io-master/README.md b/o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/README.md similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/ppm-io-master/README.md rename to o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/README.md diff --git a/o3d3xx-o3x1xx/simpleimage/ppm-io-master/include/thinks/ppm.hpp b/o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/include/thinks/ppm.hpp similarity index 100% rename from o3d3xx-o3x1xx/simpleimage/ppm-io-master/include/thinks/ppm.hpp rename to o3d3xx-o3x1xx/cpp/simpleimage/ppm-io-master/include/thinks/ppm.hpp diff --git a/o3d3xx-o3x1xx/timestamp/CMakeLists.txt b/o3d3xx-o3x1xx/cpp/timestamp/CMakeLists.txt similarity index 100% rename from o3d3xx-o3x1xx/timestamp/CMakeLists.txt rename to o3d3xx-o3x1xx/cpp/timestamp/CMakeLists.txt diff --git a/o3d3xx-o3x1xx/timestamp/ex-timestamp.cpp b/o3d3xx-o3x1xx/cpp/timestamp/ex-timestamp.cpp similarity index 97% rename from o3d3xx-o3x1xx/timestamp/ex-timestamp.cpp rename to o3d3xx-o3x1xx/cpp/timestamp/ex-timestamp.cpp index f582af2..79d8f3e 100644 --- a/o3d3xx-o3x1xx/timestamp/ex-timestamp.cpp +++ b/o3d3xx-o3x1xx/cpp/timestamp/ex-timestamp.cpp @@ -1,6 +1,5 @@ - /* - * Copyright 2022-present ifm electronic, gmbh + * Copyright 2024-present ifm electronic, gmbh * SPDX-License-Identifier: Apache-2.0 */ diff --git a/o3d3xx-o3x1xx/python/core/o3d_diagnostic.py b/o3d3xx-o3x1xx/python/core/o3d_diagnostic.py new file mode 100644 index 0000000..2a76d0d --- /dev/null +++ b/o3d3xx-o3x1xx/python/core/o3d_diagnostic.py @@ -0,0 +1,60 @@ +############################################# +# Copyright 2024-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# + +# This example shows how to unpack diagnostic +# data from the O3D camera. +# The diagnostic data provides important +# information about the camera's internal state, +# such as the temperature. + +# Necessary imports +import logging +import struct +import time +from ifm3dpy.device import O3D +from ifm3dpy.framegrabber import FrameGrabber, buffer_id + + +logger = logging.getLogger(__name__) + + +def main(ip): + # Create the objects + o3d = O3D(ip=ip) + fg = FrameGrabber(o3d, int(o3d.device_parameter("PcicTcpPort"))) + + # Collect a frame + fg.start( + [ + buffer_id.DIAGNOSTIC, + ] + ) + time.sleep(5) # Grace period after initialization of the data stream + [ok, frame] = fg.wait_for_frame().wait_for(500) + if not ok: + raise Exception("Timeout waiting for frame") + + # Get the data from the collected frame + data = frame.get_buffer(buffer_id.DIAGNOSTIC) + + # Unpack the data + unpacked_data = struct.unpack("<4i2I", data) + logger.info(f"Diagnostic data:") + logger.info( + f"Illumination temperature (0.1 °C), invalid = 32767: {unpacked_data[0]}" + ) + logger.info(f"Frontend temperature 1 (0.1 °C), invalid = 32767: {unpacked_data[1]}") + logger.info(f"Frontend temperature 2 (0.1 °C), invalid = 32767: {unpacked_data[2]}") + logger.info(f"i.mx6 Temperature (0.1 °C), invalid = 32767: {unpacked_data[3]}") + logger.info(f"Frame duration: {unpacked_data[4]}") + logger.info(f"Framerate: {unpacked_data[5]}") + fg.stop() + + +if __name__ == "__main__": + # EDIT here when using a non-default IP address + IP = "192.168.0.69" + logging.basicConfig(level=logging.INFO) + main(IP) diff --git a/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/__init__.py b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/config.py b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/config.py new file mode 100644 index 0000000..7bd7cc9 --- /dev/null +++ b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/config.py @@ -0,0 +1,12 @@ +# Configurations for the O3D3xx and +# O3X1xx examples. +# Edit this file to match your setup. +from pathlib import Path +import os + +CURRENT_DIR = Path(__file__).parent.resolve().as_posix() + +############################################ +# Device configuration +############################################ +IP: str = os.environ.get("IFM3D_IP", "192.168.0.22") diff --git a/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/__init__.py b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/__init__.py new file mode 100644 index 0000000..c3091a4 --- /dev/null +++ b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +import os + + +def set_noProxy(ip): + os.environ["NO_PROXY"] = ip + return diff --git a/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/get_data.py b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/get_data.py new file mode 100644 index 0000000..ba96539 --- /dev/null +++ b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/get_data.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +############################################# +# Copyright 2024-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# +import os +import logging +import time + +import matplotlib.pyplot as plt +from matplotlib import gridspec + +from ifm3dpy.device import Device +from ifm3dpy.framegrabber import FrameGrabber, buffer_id + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format="%(message)s") + + +def main(ip): + # Add IP address of the camera to no-proxy environment variables + os.environ["NO_PROXY"] = ip + + # Initialize the objects + device = Device(ip) + fg = FrameGrabber(device) + + # Set schema and start FrameGrabber + # Due to a known issue, for the O3X device both the + # NORM_AMPLITUDE_IMAGE and the AMPLITUDE_IMAGE buffers + # have to be requested. + fg.start( + [ + buffer_id.NORM_AMPLITUDE_IMAGE, + buffer_id.RADIAL_DISTANCE_IMAGE, + buffer_id.CONFIDENCE_IMAGE, + buffer_id.AMPLITUDE_IMAGE, + ] + ) + time.sleep(3) # Grace period after starting the data stream + + trigger_mode = device.to_json()["ifm3d"]["Apps"][0]["TriggerMode"] + + if trigger_mode == "1": + logger.info("Camera is in Continuous Trigger Mode") + elif trigger_mode == "2": + logger.info("Camera is in Software Trigger Mode") + # Software Trigger the camera + fg.sw_trigger() + + [ok, frame] = fg.wait_for_frame().wait_for(1500) + + if not ok: + raise RuntimeError("Timeout while waiting for a frame.") + + radial_distance = frame.get_buffer(buffer_id.RADIAL_DISTANCE_IMAGE) + amplitude = frame.get_buffer(buffer_id.NORM_AMPLITUDE_IMAGE) + confidence = frame.get_buffer(buffer_id.CONFIDENCE_IMAGE) + + # Create a figure + fig = plt.figure(figsize=(10, 8)) + + # Define the grid layout + gs = gridspec.GridSpec(2, 2, height_ratios=[1, 2]) + ax1 = fig.add_subplot(gs[0, 0]) + ax2 = fig.add_subplot(gs[0, 1]) + ax3 = fig.add_subplot(gs[1, :]) + + ax1.imshow(radial_distance, cmap="jet") + ax1.set_title("Radial Distance Image") + ax1.axis("off") + + ax2.imshow(amplitude, cmap="gray") + ax2.set_title("Amplitude Image") + ax2.axis("off") + + ax3.imshow(confidence, cmap="jet") + ax3.set_title("Confidence Image") + ax3.axis("off") + + plt.tight_layout() + plt.show() + + fg.stop() + + +if __name__ == "__main__": + try: + # If the example python package was build, import the configuration + from o3d3xx_o3x1xx_examples import config + + IP = config.IP + + except ImportError: + # Otherwise, use default values + logger.info( + "Unable to import the configuration.\nPlease run 'pip install -e .' from the python root directory" + ) + logger.info("Defaulting to the default configuration.") + IP = "192.168.0.69" + main(ip=IP) diff --git a/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/o3d_deserialize.py b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/o3d_deserialize.py new file mode 100644 index 0000000..8e959d3 --- /dev/null +++ b/o3d3xx-o3x1xx/python/o3d3xx_o3x1xx_examples/core/o3d_deserialize.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +############################################# +# Copyright 2024-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# +# This examples shows how to use the +# deserializer modules to extract data from +# the O3D frame +############################################# +# Import the relevant modules and configure +# the logger +import logging +from ifm3dpy.device import O3D +from ifm3dpy.framegrabber import FrameGrabber, buffer_id, Frame +from ifm3dpy.deserialize import ( + ExtrinsicOpticToUser, + O3DExposureTimes, + O3DILLUTemperature, + O3DExtrinsicCalibration, + O3DInstrinsicCalibration, + O3DInverseInstrinsicCalibration, +) + + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format="%(message)s") +################################################################# + + +# Define a function to extract the data from the frame +def extract_data( + frame: Frame, +) -> tuple[ + O3DExposureTimes, + ExtrinsicOpticToUser, + O3DInstrinsicCalibration, + O3DInverseInstrinsicCalibration, + O3DILLUTemperature, +]: + """Extract the data from the frame object + + Args: + frame (_type_): ifm3d frame object + """ + # Get buffers from frame object for deserialization + if frame is not None: + exposure_times_buffer = frame.get_buffer(buffer_id.EXPOSURE_TIME) + exposure_times = O3DExposureTimes().deserialize(exposure_times_buffer) + + extrinsic_calib_buffer = frame.get_buffer(buffer_id.EXTRINSIC_CALIB) + extrinsic_calib = O3DExtrinsicCalibration().deserialize(extrinsic_calib_buffer) + + intrinsic_calib_buffer = frame.get_buffer(buffer_id.INTRINSIC_CALIB) + intrinsic_calib = O3DInstrinsicCalibration().deserialize(intrinsic_calib_buffer) + + inv_intrinsic_calib_buffer = frame.get_buffer( + buffer_id.INVERSE_INTRINSIC_CALIBRATION + ) + inv_intrinsic_calib = O3DInverseInstrinsicCalibration().deserialize( + inv_intrinsic_calib_buffer + ) + + illumination_temperature_buffer = frame.get_buffer(buffer_id.ILLUMINATION_TEMP) + illumination_temperature = O3DILLUTemperature().deserialize( + illumination_temperature_buffer + ) + + return ( + exposure_times, + extrinsic_calib, + intrinsic_calib, + inv_intrinsic_calib, + illumination_temperature, + ) + raise RuntimeError("Empty frame object") + + +def main(IP: str) -> None: + ########################### + # Create the O3D and FrameGrabber + # and choose which images to receive. + # In this example we only receive one frame. + ########################### + o3d = O3D(IP) + + pcic_port = int(o3d.device_parameter("PcicTcpPort")) + fg = FrameGrabber(cam=o3d, pcic_port=pcic_port) + + # Define the images to receive when starting the data stream + fg.start( + [ + buffer_id.EXPOSURE_TIME, + buffer_id.EXTRINSIC_CALIB, + buffer_id.INTRINSIC_CALIB, + buffer_id.INVERSE_INTRINSIC_CALIBRATION, + buffer_id.ILLUMINATION_TEMP, + ] + ) + + trigger_mode = o3d.to_json()["ifm3d"]["Apps"][0]["TriggerMode"] + + if trigger_mode == "1": + logger.info("Camera is in Continuous Trigger Mode") + elif trigger_mode == "2": + logger.info("Camera is in Software Trigger Mode") + # Software Trigger the camera + fg.sw_trigger() + + # Get a frame + [ok, frame] = fg.wait_for_frame().wait_for(1500) # wait with 1500ms timeout + + # Check that a frame was received + if not ok: + raise RuntimeError("Timeout while waiting for a frame.") + ############################### + # Extract data from the buffer + # and print the data + ############################### + ( + exposure_times, + extrinsic_calib, + intrinsic_calib, + inv_intrinsic_calib, + illu_temp, + ) = extract_data(frame=frame) + logger.info(f"Exposure Time: {exposure_times.data}") + logger.info( + f"Extrinsic Calibration [trans_x, trans_y, trans_z, rot_x, rot_y, rot_z]: {extrinsic_calib}" + ) + logger.info(f"Intrinsic Calibration: {intrinsic_calib}") + logger.info(f"Inverse Intrinsic Calibration: {inv_intrinsic_calib}") + logger.info(f"Illumination Temperature: {illu_temp.data}") + + fg.stop() + + +if __name__ == "__main__": + try: + # If the example python package was build, import the configuration + from o3d3xx_o3x1xx_examples import config + + IP = config.IP + except ImportError: + # Otherwise, use default values + logger.warning( + "Unable to import the configuration.\nPlease run 'pip install -e .' from the python root directory" + ) + logger.warning("Defaulting to the default configuration.") + IP = "192.168.0.70" + + main(IP=IP) diff --git a/o3d3xx-o3x1xx/python/requirements.txt b/o3d3xx-o3x1xx/python/requirements.txt new file mode 100644 index 0000000..16dfe5f --- /dev/null +++ b/o3d3xx-o3x1xx/python/requirements.txt @@ -0,0 +1,5 @@ +h5py==3.10.0 +ifm3dpy==1.5.3 +jsonpointer==2.4 +jsonschema==3.2.0 +matplotlib==3.8.0 \ No newline at end of file diff --git a/o3d3xx-o3x1xx/python/setup.py b/o3d3xx-o3x1xx/python/setup.py new file mode 100644 index 0000000..6f7a46f --- /dev/null +++ b/o3d3xx-o3x1xx/python/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup, find_packages + +setup( + name="o3d3xx_o3x1xx_examples", + version="0.1", + description="Python examples for ifm O3D3xx devices", + packages=find_packages(), # finds packages under 'src' +) \ No newline at end of file diff --git a/ovp8xx/python/ovp8xxexamples/core/README.md b/ovp8xx/python/ovp8xxexamples/core/README.md index 55e289e..515d7dc 100644 --- a/ovp8xx/python/ovp8xxexamples/core/README.md +++ b/ovp8xx/python/ovp8xxexamples/core/README.md @@ -46,9 +46,6 @@ The script `fw_update_utils.py` demonstrates how to perform a firmware update fo The recommended way to receive data is to use the callback function, as shown in the `getting_data_callback.py` script. You can register a callback function that will be executed for every received frame, until the program exits. Alternatively, wait for a frame: you just need to call the `WaitForFrame` function, as shown in the `getting_data.py` script. -## `ifm3dpy_viewer.py` -In the `ifm3dpy_viewer.py` Python script, a full demonstration of how to view the different images is done. For more details refer to the [viewer documentation](viewer.md). - ## `multi_head.py` The `multi_head.py` script demonstrates how to retrieve the list of camera heads connected to the VPU and their types.