From 9b28099df8d2bc334830ff86f3737ca2e283fa12 Mon Sep 17 00:00:00 2001 From: Fares Date: Tue, 9 Jan 2024 17:19:00 +0100 Subject: [PATCH 01/18] first part of READMEs --- ovp8xx/README.md | 20 +++++ ovp8xx/python/README.md | 13 ++++ ovp8xx/python/core/2d_data/2d_data.md | 76 ------------------- ovp8xx/python/core/deserialize/deserialize.md | 13 ---- 4 files changed, 33 insertions(+), 89 deletions(-) create mode 100644 ovp8xx/README.md create mode 100644 ovp8xx/python/README.md delete mode 100644 ovp8xx/python/core/2d_data/2d_data.md diff --git a/ovp8xx/README.md b/ovp8xx/README.md new file mode 100644 index 0000000..85b4dbe --- /dev/null +++ b/ovp8xx/README.md @@ -0,0 +1,20 @@ +# ifm3d Examples for OVP8xx + +This directory contains example codes in `python` and `c++` to help you start working with the O3R system. The available example codes are divided into three subdirectories: + +1. **Core** +2. **ODS** +3. **Toolbox** (only for the `python` examples) + +## Core +In the core examples, you will learn how to use the `ifm3dpy` library to obtain image data (2D, 3D, distance image, etc.), configure parameters, update firmware, and more. + +## ODS +In the ODS examples, you will find demonstrations of how to work with the Obstacle Detection System (ODS). This includes streaming data, analyzing the data, visualizing results, configuring the ODS, and using diagnostic scripts. + +## Toolbox +Within the Toolbox, you will find helper scripts, including: +* Angle converter +* Extrinsic calibration +* H5 to ifm3d lib converter +* 2D-3D registration script \ No newline at end of file diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md new file mode 100644 index 0000000..7501729 --- /dev/null +++ b/ovp8xx/python/README.md @@ -0,0 +1,13 @@ +# Python Examples: +in this Branch you will learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: +1. **Core:** containing general scripts for O3R system +2. **ODS:** containing ODS example scripts + +## Core: +In this directory you will find multiple scripts that will be explained in the following: + +## 2d_data.py: +Receiving RGB data with ifm3d is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved (see full code). +The important part is how to access the RGB image and how to decode it for further use. +The decoded image can then be displayed, for instance with OpenCV. + diff --git a/ovp8xx/python/core/2d_data/2d_data.md b/ovp8xx/python/core/2d_data/2d_data.md deleted file mode 100644 index d7f047b..0000000 --- a/ovp8xx/python/core/2d_data/2d_data.md +++ /dev/null @@ -1,76 +0,0 @@ -# How to: receive and use the 2D rgb image - -Receiving RGB data with ifm3d is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved (see full code below). -The important part is how to access the RGB image and how to decode it for further use. - -## Access the data -The RGB image is stored in JPEG format and can be retrieved as follows: -:::::{tabs} -::::{group-tab} Python -:::python -jpeg = frame.get_buffer(buffer_id.JPEG_IMAGE) -::: -:::: -::::{group-tab} C++ -:::cpp -auto jpeg = frame->GetBuffer(ifm3d::buffer_id::JPEG_IMAGE); -::: -:::: -::::: - -## Decode the data -Once accessed, the RGB image has to be decoded. We use OpenCV in this example: -:::::{tabs} -::::{group-tab} Python -:::python -rgb_decode = cv2.imdecode(jpeg, cv2.IMREAD_UNCHANGED) -::: -:::: -::::{group-tab} C++ -:::cpp -auto rgb_decode = cv::imdecode(jpeg, cv::IMREAD_UNCHANGED); -::: -:::: -::::: - -## Display (optional) -The decoded image can then be displayed, for instance with OpenCV. -> Note that in c++, the image first has to be converted to a cv::Mat. -> Follow the full example for the conversion to cv::Mat with or without copy. -:::::{tabs} -::::{group-tab} Python -:::python -cv2.startWindowThread() -cv2.namedWindow("2D image", cv2.WINDOW_NORMAL) -# get frame -# ... -... -cv2.imshow('RGB image', rgb_decode) -cv2.waitKey(0) -::: -:::: -::::{group-tab} C++ -:::cpp -cv::startWindowThread(); -cv2.namedWindow("RGB image", cv2::WINDOW_NORMAL) - -cv::imshow("RGB image", rgb_decode); -cv::waitKey(0); -::: -:::: -::::: - -## The full example -:::::{tabs} -::::{group-tab} Python -:::{literalinclude} 2d_data.py -:language: python -::: -:::: - -::::{group-tab} C++ -:::{literalinclude} 2d_data.cpp -:language: cpp -::: -:::: -::::: \ No newline at end of file diff --git a/ovp8xx/python/core/deserialize/deserialize.md b/ovp8xx/python/core/deserialize/deserialize.md index 615f370..a0d4153 100644 --- a/ovp8xx/python/core/deserialize/deserialize.md +++ b/ovp8xx/python/core/deserialize/deserialize.md @@ -10,16 +10,3 @@ Some of the data provided by the O3R platform needs to be deserialized to be use For more information on the data structures of each buffer please refer to the [python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html) or the [c++ API documentation]. The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserlize function. Follow the example below for an example on deserialializing the `RGBInfoV1` buffer. - -:::::{tabs} -:::: {group-tab} Python -:::{literalinclude} deserialize.py -:language: python -::: -:::: -:::: {group-tab} C++ -:::{literalinclude} deserialize.cpp -:language: cpp -::: -:::: -::::: \ No newline at end of file From b6b67dc0905f129aa8b69385dfc4413a2eb0ba58 Mon Sep 17 00:00:00 2001 From: Fares Date: Wed, 10 Jan 2024 11:53:37 +0100 Subject: [PATCH 02/18] last update --- ovp8xx/README.md | 4 +- ovp8xx/python/README.md | 83 +++++++- .../core/configuration/configuration.md | 101 ---------- ovp8xx/python/core/deserialize/deserialize.md | 12 -- .../python/core/getting_data/getting_data.md | 188 ------------------ ovp8xx/python/ods/bootup_monitor.py | 2 - .../ods/extrinsic_calib_verification.py | 2 +- ovp8xx/python/ods/ods_config.py | 2 +- ovp8xx/python/ods/ods_data_analyze.py | 4 +- 9 files changed, 83 insertions(+), 315 deletions(-) delete mode 100644 ovp8xx/python/core/configuration/configuration.md delete mode 100644 ovp8xx/python/core/deserialize/deserialize.md delete mode 100644 ovp8xx/python/core/getting_data/getting_data.md diff --git a/ovp8xx/README.md b/ovp8xx/README.md index 85b4dbe..80606f0 100644 --- a/ovp8xx/README.md +++ b/ovp8xx/README.md @@ -1,4 +1,4 @@ -# ifm3d Examples for OVP8xx +# ifm3d examples for OVP8xx This directory contains example codes in `python` and `c++` to help you start working with the O3R system. The available example codes are divided into three subdirectories: @@ -7,7 +7,7 @@ This directory contains example codes in `python` and `c++` to help you start wo 3. **Toolbox** (only for the `python` examples) ## Core -In the core examples, you will learn how to use the `ifm3dpy` library to obtain image data (2D, 3D, distance image, etc.), configure parameters, update firmware, and more. +In the core examples, you will learn how to use the `ifm3d` library to obtain image data (2D, 3D, distance image, etc.), configure parameters, update firmware, and more. ## ODS In the ODS examples, you will find demonstrations of how to work with the Obstacle Detection System (ODS). This includes streaming data, analyzing the data, visualizing results, configuring the ODS, and using diagnostic scripts. diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 7501729..7d7c3bd 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -1,13 +1,84 @@ # Python Examples: in this Branch you will learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: -1. **Core:** containing general scripts for O3R system -2. **ODS:** containing ODS example scripts +1. **Core:** containing general scripts for O3R system. +2. **ODS:** containing ODS example scripts. +3. **Toobox** containing helper scripts. ## Core: -In this directory you will find multiple scripts that will be explained in the following: +In this directory you will find multiple general O3R scripts that will be explained in the following: -## 2d_data.py: -Receiving RGB data with ifm3d is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved (see full code). +### 2D data: +Receiving RGB data with `ifm3dpy` is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved. The important part is how to access the RGB image and how to decode it for further use. -The decoded image can then be displayed, for instance with OpenCV. +Once decoded, the image can be displayed using tools such as OpenCV. The example code in `2d_data.py` illustrates the explained process. + + +### Configuration + +The O3R has multiple parameters that have an influence on the point cloud. Some of them affect the raw measurement and others modify how the data is converted into x,y,z, etc values. These parameters can be changed to better fit your applications and the script `configuration.py` presents how. You can refer to [this page](https://ifm3d.com/latest/Technology/3D/index_3d.html) for a detailed description of each parameter. + +The ifm3d API provides functions to read and set the configuration of the device. Note that JSON formatting is used for all the configurations. + +### How to: deserialize O3R data + +Some of the data provided by the O3R platform needs to be deserialized to be used. This is the case for: +- the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, +- the extrinsic calibration (optics to user) parameters (` ifm3dpy.deserialize.ExtrinsicOpticToUser`), which provides the transformations between the optical system and the reference point on the camera housing, +- the ODS zone information (`ifm3dpy.deserialize.ODSInfoV1`), which contains the zone id being used and the occupancy of the zones, +- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transformation matrix, +- the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. + +For more information on the data structures of each buffer please refer to the [python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html). + +The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example code, `deserialize.py` for an example on deserializing the `RGBInfoV1` buffer. + +### Getting data: + +The primary objective of `ifm3d` is to make it as simple and performant as possible to acquire pixel data from an ifm 3D camera of the O3xxxx series. +Additionally, the data should be encoded in a useful format for performing computer vision and/or robotics perception tasks. +A typical `ifm3d` client program follows the structure of a control loop whereby images are continuously acquired from the camera and acted upon in some application-specific way. + +`ifm3dpy` provides three main classes: +- `O3R` holds the configuration of the camera heads, handles the connection, etc; +- `FrameGrabber` receives frames (images); +- `Frame` stores the image buffers. + +The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It inherits its name from previous ifm 3D devices that only used one camera, with no distinction between sensing and computing units. + +The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. +Its inputs: +- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; +- `port`: PCIC port number of the camera head to grab data from (not the physical port number); + +Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. +Simply access the image by calling `get_buffer` passing the `buffer_id` of the required image as a parameter. + +The recommended way to receive a frame 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. + +### viewer +in the `ifm3dpy_viewer.py` python script a full demonstration of how to view the different images is done. + +### Firmware update + +The script `fw_update_utils.py` demonstrates how to perform a firmware update for your O3R system. Additionally, the script includes several utility functions that provide information, such as determining the current firmware version. + +### timestamps + +The script `timestamps.py` demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. + + +## ODS +The ODS python scripts will be briefly described below: + +* `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. +* `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. +* `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. +* `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. +* `ods_data_analyze.py`: ods data analyzer. ?? +* `ods_demo.py` +* `ods_queue.py` +* `ods_stream.py` +* `ods_visualization.py` + + diff --git a/ovp8xx/python/core/configuration/configuration.md b/ovp8xx/python/core/configuration/configuration.md deleted file mode 100644 index 3917e8b..0000000 --- a/ovp8xx/python/core/configuration/configuration.md +++ /dev/null @@ -1,101 +0,0 @@ -# How to: configure the camera - -The O3R has multiple parameters that have an influence on the point cloud. Some of them affect the raw measurement and others modify how the data is converted into x,y,z, etc values. These parameters can be changed to better fit your applications and this document presents how. You can refer to [this page](https://ifm3d.com/documentation/Technology/3D/index_3d.html) for a detailed description of each parameter. - -The ifm3d API provides functions to read and set the configuration of the device. Note that JSON formatting is used for all the configurations. - -For this process, an O3R object has to be initialized to establish a connection with the device (please have a look at the code example provided for full details of the imported libraries). -:::::{tabs} -::::{group-tab} Python -:::python -o3r = O3R() -::: -:::: -::::{group-tab} C++ -:::cpp -auto o3r = std::make_shared(); -::: -:::: -::::: - -Note: if you are using multiple ifm devices (O3D, O3X, O3R), you can use the `Device` class. -:::::{tabs} -::::{group-tab} Python -:::python -dev = Device() -::: -:::: -::::{group-tab} C++ - -If you need to use functions specific to the concrete `Device` subclass, you can cast the pointer to the relevant class: - -:::cpp -auto dev = ifm3d::Device::MakeShared(); -// Only do this if you're sure the `Device` is an instance of `O3R`: -auto dev_O3R = std::static_pointer_cast(dev); -auto init_status = dev_O3R->GetInitStatus(); - - -// Otherwise use dynamic_pointer_cast and check the value for nullptr: -auto dev_O3R = std::dynamic_pointer_cast(dev); -if (dev_O3R) -{ - auto ports = dev_O3R->Ports(); -} -::: -:::: -::::: - -:::{note} -The `GetInitStatus` returns the status of the device's initialization process. Use it to ensure the device is properly booted up before querying for data. -::: - -## Read the current configuration - -The first provided function outputs the current configuration of the device (the VPU and each head currently attached). This function outputs the full configuration, including the parameters set for each camera head, but also other aspects like MAC and IP addresses, etc. -:::::{tabs} -::::{group-tab} Python -:::python -conf = o3r.get(); -::: -:::: -::::{group-tab} C++ -:::cpp -json conf = o3r->Get(); -::: -:::: -::::: - -## Write a new configuration - -To set a new configuration, you need to provide said configuration in JSON formatting. The provided configuration can be a subset or the full configuration, as long as it follows the proper JSON hierarchy. - -:::::{tabs} -::::{group-tab} Python -:::python -o3r.set({'device':{'info':{'name':'great_o3r'}}}) -::: -:::: -::::{group-tab} C++ -:::cpp -o3r->Set(R"({"device":{"info": {"name": "my_o3r"}}})"); -::: -:::: -::::: - -Note: we use [string literals](https://en.cppreference.com/w/cpp/language/string_literal) for easier readability. - -## The full example -:::::{tabs} -::::{group-tab} Python -:::{literalinclude} configuration.py -:language: python -::: -:::: - -::::{group-tab} C++ -:::{literalinclude} configuration.cpp -:language: cpp -::: -:::: -::::: diff --git a/ovp8xx/python/core/deserialize/deserialize.md b/ovp8xx/python/core/deserialize/deserialize.md deleted file mode 100644 index a0d4153..0000000 --- a/ovp8xx/python/core/deserialize/deserialize.md +++ /dev/null @@ -1,12 +0,0 @@ -# How to: deserialize O3R data - -Some of the data provided by the O3R platform needs to be deserialized to be used. This is the case for: -- the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, -- the extrinsic calibration (optics to user) parameters (` ifm3dpy.deserialize.ExtrinsicOpticToUser`), which provides the transformations between the optical system and the reference point on the camera housing, -- the ODS zone information (`ifm3dpy.deserialize.ODSInfoV1`), which contains the zone id being used and the occupancy of the zones, -- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transormation matrix, -- the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. - -For more information on the data structures of each buffer please refer to the [python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html) or the [c++ API documentation]. - -The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserlize function. Follow the example below for an example on deserialializing the `RGBInfoV1` buffer. diff --git a/ovp8xx/python/core/getting_data/getting_data.md b/ovp8xx/python/core/getting_data/getting_data.md deleted file mode 100644 index c84b726..0000000 --- a/ovp8xx/python/core/getting_data/getting_data.md +++ /dev/null @@ -1,188 +0,0 @@ -# How to: receive an image - -The primary objective of `ifm3d` is to make it as simple and performant as possible to acquire pixel data from an ifm 3D camera of the O3xxxx series. -Additionally, the data should be encoded in a useful format for performing computer vision and/or robotics perception tasks. -A typical `ifm3d` client program follows the structure of a control loop whereby images are continuously acquired from the camera and acted upon in some application-specific way. - -At the end of this 'how to', you should be able to receive images and know the basic usage of the `O3R`, `FrameGrabber` and `Frame` classes. - ->Note: for O3D or O3X devices, simply use the `O3D`/`O3X` class in place of the `O3R` in the following code. - -## O3R, FrameGrabber and Frame - -ifm3d provides three main classes: -- `O3R` holds the configuration of the camera heads, handles the connection, etc; -- `FrameGrabber` receives frames (images); -- `Frame` stores the image buffers. - -Instantiating these objects is as follows: -:::::{tabs} -::::{group-tab} Python -:::python -o3r = O3R() -fg = FrameGrabber(o3r, pcic_port=50012) -::: -:::: -::::{group-tab} C++ -:::cpp -auto o3r = std::make_shared(); -auto fg = std::make_shared(o3r, 50012); -::: -:::: -::::: - ->Note: The example above assumes that an O3R camera head is connected to the VPU at the physical port 2, which has the PCIC TCP port 50012. The PCIC TCP port number can be retrieved via `o3r.get([f"/ports/portX/data/pcicTCPPort"])` with `portX` being a valid port (eg "port0","port1",etc.). - -The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It inherits its name from previous ifm 3D devices that only used one camera, with no distinction between sensing and computing units. -You can input: -- `ip`: the IP address of the device; -- `xmlrpc_port`: the XML_RPC port (it is fixed at the moment); -- `password`: the password to connect to the device (unused for the O3R). - -The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. -Its inputs: -- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; -- `port`: PCIC port number of the camera head to grab data from (not the physical port number); - -> Note: instantiating the objects is done the same way for any imager type (2D, 3D, different resolutions, etc). - -Note that `ifm3d` encourages the use of `std::shared_ptr` as a means to manage the ownership and lifetimes of the core actors in an `ifm3d` program. -Indeed, you will notice that there is no need to explicitly allocate and deallocate memory. -To instantiate the `ifm3d::O3R` object (or `ifm3d::O3D`/`ifm3d::O3X`), a call to `ifm3d::Device::MakeShared()` is made, rather than calling `std::make_shared` directly. -This wrapper function is used to handle direct hardware probing to determine the type of camera that is connected. -For example, this may be an O3R, O3D303, O3X, or something else. Regardless, a `std::shared_ptr` is returned from this call. - -## Set the schema and start the FrameGrabber - -To start the data stream, the `Start` function must be used. This `Start` needs a schema which is a `std::vector` and contains the `buffer_id` of the data needed for the application: -:::::{tabs} -::::{group-tab} Python -:::python -fg.start([buffer_id.NORM_AMPLITUDE_IMAGE,buffer_id.RADIAL_DISTANCE_IMAGE,buffer_id.XYZ]) -::: -:::: -::::{group-tab} C++ -:::cpp -fg->Start({ifm3d::buffer_id::AMPLITUDE_IMAGE, ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE,ifm3d::buffer_id::XYZ}); -::: -:::: -::::: - -The device provides many types of buffers. All the buffers might not be required for application. Setting the schema provides a way to enable only `buffer_id`s required for the application. -This also reduce the bandwidth required to get the data from the device. - -## Receive an image - -### Register a callback -The recommended way to receive a frame is to use the callback function. You can register a callback function that will be executed for every received frame, until the program exits. - -:::::{tabs} -::::{group-tab} Python -:::python -def callback(frame): - # Read the distance image and display a pixel in the center - dist = frame.get_buffer(buffer_id.RADIAL_DISTANCE_IMAGE) - (width, height) = dist.shape - print(dist[width//2,height//2]) - pass - -... -fg.start([buffer_id.RADIAL_DISTANCE_IMAGE]) -fg.on_new_frame(callback) -... -fg.stop() -::: -:::: -::::{group-tab} c++ -:::cpp -void Callback(ifm3d::Frame::Ptr frame){ - auto dist = frame->GetBuffer(ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE); - std::cout << dist.height() << " " << dist.width() << std::endl; -} - -int -main() -{ - ... - fg->Start({ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE}); - fg->OnNewFrame(&Callback); - ... - fg->Stop(); -} -::: -:::: -::::: - -### Alternatively: wait for a frame -You just need to call the `WaitForFrame` function. This `Framegrabber` method returns `std::Future`. The `Frame` class stores all the received data. Make sure the camera head is in "RUN" mode. -:::::{tabs} -::::{group-tab} Python -:::python -frame = fg.wait_for_frame().wait() # wait without timeout - -[ok, frame] = fg.wait_for_frame().wait_for(500) # wait with 500ms timeout - -frame = await fg.wait_for_frame() # using asyncio -::: -:::: -::::{group-tab} C++ -:::cpp -auto frame = fg->WaitForFrame().get(); -::: -:::: -::::: - -## Access the data - -Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. -Simply access the image by calling `get_buffer` passing the `buffer_id` of the required image as a parameter. -:::::{tabs} -::::{group-tab} Python -:::python -# For 3D data: -dist = frame.get_buffer(buffer_id.RADIAL_DISTANCE_IMAGE) -# For 2D data: -rgb = frame.get_buffer(buffer_id.JPEG_IMAGE) -::: -:::: -::::{group-tab} C++ -:::cpp -// For 3D data: -auto dist = frame->get_buffer(ifm3d::buffer_id::RADIAL_DISTANCE_IMAGE); -// For 2D data: -auto rgb = frame->get_buffer(ifm3d::buffer_id::JPEG_IMAGE); -::: -:::: -::::: - -## The full example - -### Using a callback -:::::{tabs} -::::{group-tab} Python -:::{literalinclude} getting_data_callback.py -:language: python -::: -:::: - -::::{group-tab} C++ -:::{literalinclude} getting_data_callback.cpp -:language: cpp -::: -:::: -::::: - -### Using the polling mode -:::::{tabs} -::::{group-tab} Python -:::{literalinclude} getting_data.py -:language: python -::: -:::: - -::::{group-tab} C++ -:::{literalinclude} getting_data.cpp -:language: cpp -::: -:::: -::::: diff --git a/ovp8xx/python/ods/bootup_monitor.py b/ovp8xx/python/ods/bootup_monitor.py index 151d7b0..8fc3101 100644 --- a/ovp8xx/python/ods/bootup_monitor.py +++ b/ovp8xx/python/ods/bootup_monitor.py @@ -143,5 +143,3 @@ def main(): if __name__ == "__main__": main() - -# %% diff --git a/ovp8xx/python/ods/extrinsic_calib_verification.py b/ovp8xx/python/ods/extrinsic_calib_verification.py index c9ed8c1..d985d85 100755 --- a/ovp8xx/python/ods/extrinsic_calib_verification.py +++ b/ovp8xx/python/ods/extrinsic_calib_verification.py @@ -207,7 +207,7 @@ def get_rotated_distances(self, R: np.ndarray, RR: np.ndarray) -> np.ndarray: RR (np.ndarray): rotation matrix Returns: - np.ndarray: distance image in rotated in user space + np.ndarray: distance image in user space """ ux, uy, uz = intrinsic_projection( self.modelID3D, self.intrinsics3D, *self.imager_size diff --git a/ovp8xx/python/ods/ods_config.py b/ovp8xx/python/ods/ods_config.py index 0fad013..d374119 100644 --- a/ovp8xx/python/ods/ods_config.py +++ b/ovp8xx/python/ods/ods_config.py @@ -127,7 +127,7 @@ def load_config_from_file(config_file: pathlib.Path) -> dict: config_snippet = { "device": {"info": {"name": "my_favorite_o3r"}}, "ports": {"port0": {"info": {"name": "my_favorite_port"}}}, - } # Assume port connected in port5 + } # Assume port connected in port0 try: validate_json(schema, config_snippet) o3r.set(config_snippet) diff --git a/ovp8xx/python/ods/ods_data_analyze.py b/ovp8xx/python/ods/ods_data_analyze.py index 9b23a54..088a2e2 100644 --- a/ovp8xx/python/ods/ods_data_analyze.py +++ b/ovp8xx/python/ods/ods_data_analyze.py @@ -134,7 +134,7 @@ def distance_tracker(data_ods: np.ndarray, frames: list, roi: list = [85, 115]): tuple: list of frames, coordinates of nearest object of interest """ - check_bbox = True + check_box = True check_centroid = False frame_detected = [] coordinates = [] @@ -145,7 +145,7 @@ def distance_tracker(data_ods: np.ndarray, frames: list, roi: list = [85, 115]): object_features = skimage.measure.regionprops(labeled_image) for obj in object_features: - if check_bbox: + if check_box: # (min_row, min_col, max_row, max_col) # https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.regionprops From 4444a0b85a820b51ecdc4161947e4936c90a62ff Mon Sep 17 00:00:00 2001 From: Fares Date: Wed, 10 Jan 2024 16:28:48 +0100 Subject: [PATCH 03/18] changed the structure, deleted extra layer under core --- ovp8xx/python/README.md | 17 +++++--- ovp8xx/python/core/{2d_data => }/2d_data.py | 0 ovp8xx/python/core/__init__.py | 0 .../core/{configuration => }/configuration.py | 0 .../core/{deserialize => }/deserialize.py | 0 .../{firmware_update => }/fw_update_utils.py | 0 .../core/{getting_data => }/getting_data.py | 0 .../getting_data_callback.py | 0 .../core/{viewer => }/ifm3dpy_viewer.py | 0 .../core/{timestamps => }/timestamps.py | 0 ovp8xx/python/core/viewer/README.md | 42 ------------------- ovp8xx/python/ods/diagnostic.py | 2 +- .../ods/extrinsic_calib_verification.py | 2 +- ovp8xx/python/ods/ods_demo.py | 4 +- 14 files changed, 16 insertions(+), 51 deletions(-) rename ovp8xx/python/core/{2d_data => }/2d_data.py (100%) create mode 100644 ovp8xx/python/core/__init__.py rename ovp8xx/python/core/{configuration => }/configuration.py (100%) rename ovp8xx/python/core/{deserialize => }/deserialize.py (100%) rename ovp8xx/python/core/{firmware_update => }/fw_update_utils.py (100%) rename ovp8xx/python/core/{getting_data => }/getting_data.py (100%) rename ovp8xx/python/core/{getting_data => }/getting_data_callback.py (100%) rename ovp8xx/python/core/{viewer => }/ifm3dpy_viewer.py (100%) rename ovp8xx/python/core/{timestamps => }/timestamps.py (100%) delete mode 100644 ovp8xx/python/core/viewer/README.md diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 7d7c3bd..7896fc6 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -74,11 +74,18 @@ The ODS python scripts will be briefly described below: * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. * `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. * `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. -* `ods_data_analyze.py`: ods data analyzer. ?? -* `ods_demo.py` -* `ods_queue.py` -* `ods_stream.py` -* `ods_visualization.py` +* `ods_data_analyze.py`: ods data analyzer script from a h5 file. +* `ods_queue.py` : This script handles the data queues of an ODS application. +* `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform +* `ods_visualization.py`: is a script used for ODS visualization. +* `ods_demo.py`: is using the described scripts to do a full demonstration of the ODS application. + +## Toolbox: +Within the Toolbox, you will find helper scripts, including: +* Angle converter +* Extrinsic calibration +* H5 to ifm3d lib converter +* 2D-3D registration script diff --git a/ovp8xx/python/core/2d_data/2d_data.py b/ovp8xx/python/core/2d_data.py similarity index 100% rename from ovp8xx/python/core/2d_data/2d_data.py rename to ovp8xx/python/core/2d_data.py diff --git a/ovp8xx/python/core/__init__.py b/ovp8xx/python/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ovp8xx/python/core/configuration/configuration.py b/ovp8xx/python/core/configuration.py similarity index 100% rename from ovp8xx/python/core/configuration/configuration.py rename to ovp8xx/python/core/configuration.py diff --git a/ovp8xx/python/core/deserialize/deserialize.py b/ovp8xx/python/core/deserialize.py similarity index 100% rename from ovp8xx/python/core/deserialize/deserialize.py rename to ovp8xx/python/core/deserialize.py diff --git a/ovp8xx/python/core/firmware_update/fw_update_utils.py b/ovp8xx/python/core/fw_update_utils.py similarity index 100% rename from ovp8xx/python/core/firmware_update/fw_update_utils.py rename to ovp8xx/python/core/fw_update_utils.py diff --git a/ovp8xx/python/core/getting_data/getting_data.py b/ovp8xx/python/core/getting_data.py similarity index 100% rename from ovp8xx/python/core/getting_data/getting_data.py rename to ovp8xx/python/core/getting_data.py diff --git a/ovp8xx/python/core/getting_data/getting_data_callback.py b/ovp8xx/python/core/getting_data_callback.py similarity index 100% rename from ovp8xx/python/core/getting_data/getting_data_callback.py rename to ovp8xx/python/core/getting_data_callback.py diff --git a/ovp8xx/python/core/viewer/ifm3dpy_viewer.py b/ovp8xx/python/core/ifm3dpy_viewer.py similarity index 100% rename from ovp8xx/python/core/viewer/ifm3dpy_viewer.py rename to ovp8xx/python/core/ifm3dpy_viewer.py diff --git a/ovp8xx/python/core/timestamps/timestamps.py b/ovp8xx/python/core/timestamps.py similarity index 100% rename from ovp8xx/python/core/timestamps/timestamps.py rename to ovp8xx/python/core/timestamps.py diff --git a/ovp8xx/python/core/viewer/README.md b/ovp8xx/python/core/viewer/README.md deleted file mode 100644 index ff090b3..0000000 --- a/ovp8xx/python/core/viewer/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# ifm3dpy Viewer - -This is an example application for retrieving different kinds of image data from an O3R platform. - -### Install requirements -```sh -$ python -m pip install -r examples/python/requirements.txt -``` - -## Usage -```sh -usage: ifm3dpy_viewer.py [-h] --pcic-port PORT --image {jpeg,distance,amplitude,xyz} [--ip IP] [--xmlrpc-port XMLRPC_PORT] - -optional arguments: - -h, --help show this help message and exit - --pcic-port PORT The pcic port from which images should be received - --image {jpeg,distance,amplitude,xyz} - The image to received (default: distance) - --ip IP IP address of the sensor (default: 192.168.0.69) - --xmlrpc-port XMLRPC_PORT - XMLRPC port of the sensor (default: 80) -``` - -### Display the distance image -```sh -$ python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image distance -``` - -### Display the amplitude image -```sh -$ python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image amplitude -``` - -### Display the point cloud (requeires open3d) -```sh -$ python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image xyz -``` - -### Display the jpeg image -```sh -$ python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50010 --image jpeg -``` diff --git a/ovp8xx/python/ods/diagnostic.py b/ovp8xx/python/ods/diagnostic.py index 7695da4..39436f1 100644 --- a/ovp8xx/python/ods/diagnostic.py +++ b/ovp8xx/python/ods/diagnostic.py @@ -103,7 +103,7 @@ def main(): # Note that this function should # be customized to handle specific # error handling in the user - # applaition + # application ############################# o3r_diagnostic.start_async_diag() diff --git a/ovp8xx/python/ods/extrinsic_calib_verification.py b/ovp8xx/python/ods/extrinsic_calib_verification.py index d985d85..bdc420a 100755 --- a/ovp8xx/python/ods/extrinsic_calib_verification.py +++ b/ovp8xx/python/ods/extrinsic_calib_verification.py @@ -207,7 +207,7 @@ def get_rotated_distances(self, R: np.ndarray, RR: np.ndarray) -> np.ndarray: RR (np.ndarray): rotation matrix Returns: - np.ndarray: distance image in user space + np.ndarray: distance image in rotated user space """ ux, uy, uz = intrinsic_projection( self.modelID3D, self.intrinsics3D, *self.imager_size diff --git a/ovp8xx/python/ods/ods_demo.py b/ovp8xx/python/ods/ods_demo.py index a3a0ce1..04708e4 100644 --- a/ovp8xx/python/ods/ods_demo.py +++ b/ovp8xx/python/ods/ods_demo.py @@ -51,13 +51,13 @@ schema = o3r.get_schema() # Set the camera extrinsics so that ODS knows where everything is -config_snippet_extrinsics =load_config_from_file("configs/extrinsic_two_heads.json") +config_snippet_extrinsics =load_config_from_file("configs/extrinsic_one_head.json") validate_json(schema, config_snippet_extrinsics) o3r.set(config_snippet_extrinsics) # Initialize the app config_snippet_new_ods_app = load_config_from_file( - "configs/ods_two_heads_config.json") + "configs/ods_one_head_config.json") validate_json(schema, config_snippet_new_ods_app) o3r.set(config_snippet_new_ods_app) From b5df61f1ebb0452a31a01f5a370b073d4ee8b50d Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 09:48:36 +0100 Subject: [PATCH 04/18] updated according to lolas comments --- ovp8xx/python/README.md | 27 +++---- ovp8xx/python/core/__init__.py | 0 ovp8xx/python/core/viewer.md | 79 +++++++++++++++++++ .../core/{ifm3dpy_viewer.py => viewer.py} | 0 ovp8xx/python/ods/ods_demo.py | 4 +- ovp8xx/python/test.py | 34 -------- .../extrinsic_calib_verification.py | 0 7 files changed, 93 insertions(+), 51 deletions(-) delete mode 100644 ovp8xx/python/core/__init__.py create mode 100644 ovp8xx/python/core/viewer.md rename ovp8xx/python/core/{ifm3dpy_viewer.py => viewer.py} (100%) delete mode 100644 ovp8xx/python/test.py rename ovp8xx/python/{ods => toolbox/extrinsic_calibration/static_camera_calibration}/extrinsic_calib_verification.py (100%) diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 7896fc6..d3ccf95 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -2,12 +2,12 @@ in this Branch you will learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: 1. **Core:** containing general scripts for O3R system. 2. **ODS:** containing ODS example scripts. -3. **Toobox** containing helper scripts. +3. **Toobox** containing useful scripts. -## Core: -In this directory you will find multiple general O3R scripts that will be explained in the following: +## Core +In this directory you will find multiple general O3R scripts that will be explained below: -### 2D data: +### 2D data Receiving RGB data with `ifm3dpy` is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved. The important part is how to access the RGB image and how to decode it for further use. Once decoded, the image can be displayed using tools such as OpenCV. The example code in `2d_data.py` illustrates the explained process. @@ -19,7 +19,7 @@ The O3R has multiple parameters that have an influence on the point cloud. Some The ifm3d API provides functions to read and set the configuration of the device. Note that JSON formatting is used for all the configurations. -### How to: deserialize O3R data +### Deserialization Some of the data provided by the O3R platform needs to be deserialized to be used. This is the case for: - the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, @@ -32,7 +32,7 @@ For more information on the data structures of each buffer please refer to the [ The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example code, `deserialize.py` for an example on deserializing the `RGBInfoV1` buffer. -### Getting data: +### Getting data The primary objective of `ifm3d` is to make it as simple and performant as possible to acquire pixel data from an ifm 3D camera of the O3xxxx series. Additionally, the data should be encoded in a useful format for performing computer vision and/or robotics perception tasks. @@ -53,16 +53,16 @@ Its inputs: Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. Simply access the image by calling `get_buffer` passing the `buffer_id` of the required image as a parameter. -The recommended way to receive a frame 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. +The recommended way to receive a frame 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. -### viewer -in the `ifm3dpy_viewer.py` python script a full demonstration of how to view the different images is done. +### Viewer +In the `ifm3dpy_viewer.py` python script a full demonstration of how to view the different images is done. ### Firmware update The script `fw_update_utils.py` demonstrates how to perform a firmware update for your O3R system. Additionally, the script includes several utility functions that provide information, such as determining the current firmware version. -### timestamps +### Timestamps The script `timestamps.py` demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. @@ -72,7 +72,6 @@ The ODS python scripts will be briefly described below: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. -* `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. * `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. * `ods_data_analyze.py`: ods data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. @@ -83,9 +82,7 @@ The ODS python scripts will be briefly described below: ## Toolbox: Within the Toolbox, you will find helper scripts, including: * Angle converter -* Extrinsic calibration +* Extrinsic calibration: + * `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. * H5 to ifm3d lib converter * 2D-3D registration script - - - diff --git a/ovp8xx/python/core/__init__.py b/ovp8xx/python/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/ovp8xx/python/core/viewer.md b/ovp8xx/python/core/viewer.md new file mode 100644 index 0000000..43f5ad8 --- /dev/null +++ b/ovp8xx/python/core/viewer.md @@ -0,0 +1,79 @@ +# ifm3dpy Viewer + +This is an example application for retrieving different kinds of images from an O3R platform. + +## 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 venv. + +### Create a new venv + +```sh +python3 -m venv my_venv +``` + +### Activate the venv + +| Platform | Shell | Command to activate virtual environment | +| -------- | --------------- | --------------------------------------- | +| POSIX | bash/zsh | $ source /bin/activate | +| | fish | $ source /bin/activate.fish | +| | csh/tcsh | $ source /bin/activate.csh | +| | PowerShell Core | $ /bin/Activate.ps1 | +| Windows | cmd.exe | C:\> \Scripts\activate.bat | +| | PowerShell | PS C:\> \Scripts\Activate.ps1 | + +### Install ifm3dpy +#### From pip +```sh +pip install ifm3dpy +``` +#### From Source +```sh +# In the ifm3d root folder +pip install -r requirements.txt +pip install . +``` +Consider [the python documentation](../../../doc/sphinx/content/python.md) for more details. + +### Install requirements +```sh +pip install -r examples/python/viewer/requirements.txt +``` + +## Usage +```sh +usage: ifm3dpy_viewer.py [-h] --pcic-port PORT --image {jpeg,distance,amplitude,xyz} [--ip IP] [--xmlrpc-port XMLRPC_PORT] + +optional arguments: + -h, --help show this help message and exit + --pcic-port PORT The pcic port from which images should be received + --image {jpeg,distance,amplitude,xyz} + The image to received (default: distance) + --ip IP IP address of the sensor (default: 192.168.0.69) + --xmlrpc-port XMLRPC_PORT + XMLRPC port of the sensor (default: 80) +``` + +### Display the distance image +```sh +python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image distance +``` + +### Display the amplitude image +```sh +python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image amplitude +``` + +### Display the point cloud +```sh +python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50012 --image xyz +``` + +### Display the jpeg image +``` +python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50010 --image jpeg +``` diff --git a/ovp8xx/python/core/ifm3dpy_viewer.py b/ovp8xx/python/core/viewer.py similarity index 100% rename from ovp8xx/python/core/ifm3dpy_viewer.py rename to ovp8xx/python/core/viewer.py diff --git a/ovp8xx/python/ods/ods_demo.py b/ovp8xx/python/ods/ods_demo.py index 04708e4..a3a0ce1 100644 --- a/ovp8xx/python/ods/ods_demo.py +++ b/ovp8xx/python/ods/ods_demo.py @@ -51,13 +51,13 @@ schema = o3r.get_schema() # Set the camera extrinsics so that ODS knows where everything is -config_snippet_extrinsics =load_config_from_file("configs/extrinsic_one_head.json") +config_snippet_extrinsics =load_config_from_file("configs/extrinsic_two_heads.json") validate_json(schema, config_snippet_extrinsics) o3r.set(config_snippet_extrinsics) # Initialize the app config_snippet_new_ods_app = load_config_from_file( - "configs/ods_one_head_config.json") + "configs/ods_two_heads_config.json") validate_json(schema, config_snippet_new_ods_app) o3r.set(config_snippet_new_ods_app) diff --git a/ovp8xx/python/test.py b/ovp8xx/python/test.py deleted file mode 100644 index 645315f..0000000 --- a/ovp8xx/python/test.py +++ /dev/null @@ -1,34 +0,0 @@ - -""" -This module provides functions to convert Euler angles between O3R2XX format and human-readable format. - -The module performs the following tasks: -- Retrieves the current calibration for a specific camera port of an O3R device. -- Converts the O3R2XX Euler angles to human-readable angles. -- Converts human-readable angles to O3R2XX Euler angles. -- Sets the new calibration for the camera port. - -To use this module, you need to edit the IP address and camera port for your device. - -Example usage: - # Convert O3R2XX Euler angles to human-readable angles - human_read_angles = o3rCalibAnglesToHumanReadable(*euler_rot) - - # Convert human-readable angles to O3R2XX Euler angles - euler_rot = humanReadableToO3RCalibAngles(roll=ROLL, pitch=PITCH, yaw=YAW) - - # Set the new calibration for the camera port - o3r.set({ - "ports": { - CAMERA_PORT: { - "processing": { - "extrinsicHeadToUser": { - "rotX": euler_rot[0], - "rotY": euler_rot[1], - "rotZ": euler_rot[2], - } - } - } - } - }) -""" diff --git a/ovp8xx/python/ods/extrinsic_calib_verification.py b/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/extrinsic_calib_verification.py similarity index 100% rename from ovp8xx/python/ods/extrinsic_calib_verification.py rename to ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/extrinsic_calib_verification.py From ebab4ac2bd8df2b92c30c4a76081dc4311d4dba0 Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 10:08:13 +0100 Subject: [PATCH 05/18] added a multi_head.py script --- ovp8xx/python/README.md | 3 ++ ovp8xx/python/core/multi_head.py | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 ovp8xx/python/core/multi_head.py diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index d3ccf95..cfa6e1e 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -55,6 +55,9 @@ Simply access the image by calling `get_buffer` passing the `buffer_id` of the r The recommended way to receive a frame 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. +### Multi head +The "multi_head.py" script demonstrates how to know the connected heads to the VPU and their types. + ### Viewer In the `ifm3dpy_viewer.py` python script a full demonstration of how to view the different images is done. diff --git a/ovp8xx/python/core/multi_head.py b/ovp8xx/python/core/multi_head.py new file mode 100644 index 0000000..b5f0cf5 --- /dev/null +++ b/ovp8xx/python/core/multi_head.py @@ -0,0 +1,53 @@ +""" + * Copyright 2024-present ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + + How to: receive data from multiple heads + One feature of the O3R platform is to enable the use of multiple camera heads + of different types (2D, 3D, various resolutions, etc). In this example, we + show how to retrieve the pcic port number for each head connected to the VPU + along with its type, create `FrameGrabber` objects and get a frame for each. +""" + +from ifm3dpy.device import O3R +from ifm3dpy.framegrabber import FrameGrabber, buffer_id + +o3r = O3R() + +fgs = [] +types = [] +print("Available connections:") +for port in o3r.get(["/ports"])["ports"]: + # exclude port 6, since it is the IMU port + if port != "port6": + pcic = o3r.get(["/ports/" + port + "/data/pcicTCPPort"])["ports"][port]["data"][ + "pcicTCPPort" + ] + data_type = o3r.get(["/ports/" + port + "/info/features/type"])["ports"][port][ + "info" + ]["features"]["type"] + fg = FrameGrabber(o3r, pcic_port=pcic) + if data_type == "2D": + types.append("2D") + fg.start([buffer_id.JPEG_IMAGE]) + print("Port: {} PCIC: {} Type: {}".format(port, pcic, data_type)) + elif data_type == "3D": + types.append("3D") + fg.start([buffer_id.RADIAL_DISTANCE_IMAGE]) + print("Port: {} PCIC: {} Type: {}".format(port, pcic, data_type)) + fgs.append(fg) +# Grab frames from each head +for fg in fgs: + [ok, frame] = fg.wait_for_frame().wait_for(3000) + if ok: + print( + "Timestamp of frame {}: {}".format( + frame.frame_count(), frame.timestamps()[0] + ) + ) + else: + print("Timeout waiting for camera!") + +# Stop the framegrabbers +for fg in fgs: + fg.stop() From eaf2cfd78b8f8bc0a88dce80b7076f996745f3b9 Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 15:56:02 +0100 Subject: [PATCH 06/18] updated README file to present tense and python noted as python3 --- ovp8xx/README.md | 10 +++++----- ovp8xx/python/README.md | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ovp8xx/README.md b/ovp8xx/README.md index 80606f0..1e31f71 100644 --- a/ovp8xx/README.md +++ b/ovp8xx/README.md @@ -1,19 +1,19 @@ # ifm3d examples for OVP8xx -This directory contains example codes in `python` and `c++` to help you start working with the O3R system. The available example codes are divided into three subdirectories: +This directory contains example codes in `python3` and `c++` to help you start working with the O3R system. The available example codes are divided into three subdirectories: 1. **Core** 2. **ODS** -3. **Toolbox** (only for the `python` examples) +3. **Toolbox** (only `Python3` examples available atm) ## Core -In the core examples, you will learn how to use the `ifm3d` library to obtain image data (2D, 3D, distance image, etc.), configure parameters, update firmware, and more. +The core examples, shows you how to use the `ifm3d` API to obtain image data (2D, 3D, distance image, etc.), configure camera parameters, update the embedded firmware, and more. ## ODS -In the ODS examples, you will find demonstrations of how to work with the Obstacle Detection System (ODS). This includes streaming data, analyzing the data, visualizing results, configuring the ODS, and using diagnostic scripts. +The ODS examples, demonstrates how to work with the Obstacle Detection System (ODS). This includes streaming data, analyzing the data, visualizing results, configuring the ODS, and using diagnostic scripts. ## Toolbox -Within the Toolbox, you will find helper scripts, including: +Within the Toolbox, you find helper scripts, including: * Angle converter * Extrinsic calibration * H5 to ifm3d lib converter diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index cfa6e1e..8c50fc5 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -1,11 +1,11 @@ -# Python Examples: -in this Branch you will learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: +# Python3 Examples: +in this Branch you learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: 1. **Core:** containing general scripts for O3R system. 2. **ODS:** containing ODS example scripts. 3. **Toobox** containing useful scripts. ## Core -In this directory you will find multiple general O3R scripts that will be explained below: +In this directory you find multiple general O3R scripts that are explained below: ### 2D data Receiving RGB data with `ifm3dpy` is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved. @@ -28,7 +28,7 @@ Some of the data provided by the O3R platform needs to be deserialized to be use - the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transformation matrix, - the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. -For more information on the data structures of each buffer please refer to the [python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html). +For more information on the data structures of each buffer please refer to the [python3 API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html). The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example code, `deserialize.py` for an example on deserializing the `RGBInfoV1` buffer. @@ -59,7 +59,7 @@ The recommended way to receive a frame is to use the callback function, as shown The "multi_head.py" script demonstrates how to know the connected heads to the VPU and their types. ### Viewer -In the `ifm3dpy_viewer.py` python script a full demonstration of how to view the different images is done. +In the `ifm3dpy_viewer.py` python3 script a full demonstration of how to view the different images is done. ### Firmware update @@ -71,7 +71,7 @@ The script `timestamps.py` demonstrate how to get the timestamps and the effect ## ODS -The ODS python scripts will be briefly described below: +The ODS python3 scripts will be briefly described below: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. @@ -83,7 +83,7 @@ The ODS python scripts will be briefly described below: * `ods_demo.py`: is using the described scripts to do a full demonstration of the ODS application. ## Toolbox: -Within the Toolbox, you will find helper scripts, including: +Within the Toolbox, you find helper scripts, including: * Angle converter * Extrinsic calibration: * `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. From 97253716490ae2eaad99a4155dabc59d1eed495b Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 16:09:49 +0100 Subject: [PATCH 07/18] updated multi_head.py to display images --- ovp8xx/python/core/multi_head.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ovp8xx/python/core/multi_head.py b/ovp8xx/python/core/multi_head.py index b5f0cf5..4d9f21d 100644 --- a/ovp8xx/python/core/multi_head.py +++ b/ovp8xx/python/core/multi_head.py @@ -11,6 +11,8 @@ from ifm3dpy.device import O3R from ifm3dpy.framegrabber import FrameGrabber, buffer_id +import cv2 +import matplotlib.pyplot as plt o3r = O3R() @@ -48,6 +50,17 @@ else: print("Timeout waiting for camera!") +#display the images. +for i, fg in enumerate(fgs): + [ok, frame] = fg.wait_for_frame().wait_for(3000) + if ok: + plt.figure() + if types[i] == '2D': + img = cv2.imdecode(frame.get_buffer(buffer_id.JPEG_IMAGE), cv2.IMREAD_UNCHANGED) + else: + img = frame.get_buffer(buffer_id.RADIAL_DISTANCE_IMAGE) + plt.imshow(img) + # Stop the framegrabbers for fg in fgs: fg.stop() From bd48d4d10deb79e9b9ed727e467c2542410e723c Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 16:18:08 +0100 Subject: [PATCH 08/18] updated python scripts with SPDX license header --- ovp8xx/python/core/configuration.py | 5 +++++ ovp8xx/python/core/deserialize.py | 5 ++++- ovp8xx/python/core/getting_data.py | 4 ++++ ovp8xx/python/core/getting_data_callback.py | 4 ++++ ovp8xx/python/core/viewer.py | 2 +- ovp8xx/python/ods/ods_demo.py | 5 ++++- .../static_camera_calibration/calib_cam.py | 5 ++++- 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ovp8xx/python/core/configuration.py b/ovp8xx/python/core/configuration.py index 0c0df8c..75c7053 100644 --- a/ovp8xx/python/core/configuration.py +++ b/ovp8xx/python/core/configuration.py @@ -1,3 +1,8 @@ +############################################# +# Copyright 2021-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# + import json # Define the ifm3d objects for the communication diff --git a/ovp8xx/python/core/deserialize.py b/ovp8xx/python/core/deserialize.py index d208f73..599e76c 100644 --- a/ovp8xx/python/core/deserialize.py +++ b/ovp8xx/python/core/deserialize.py @@ -1,4 +1,7 @@ -################################################################# +############################################# +# Copyright 2021-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# # This examples shows how to use the deserializer module # to extract data from the RGBInfoV1 buffer. # The same principles can be applied to deserialize data from diff --git a/ovp8xx/python/core/getting_data.py b/ovp8xx/python/core/getting_data.py index 8d931d4..2081a48 100644 --- a/ovp8xx/python/core/getting_data.py +++ b/ovp8xx/python/core/getting_data.py @@ -1,3 +1,7 @@ +############################################# +# Copyright 2023-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# from ifm3dpy.device import O3R from ifm3dpy.framegrabber import FrameGrabber, buffer_id diff --git a/ovp8xx/python/core/getting_data_callback.py b/ovp8xx/python/core/getting_data_callback.py index 7810b37..7656719 100644 --- a/ovp8xx/python/core/getting_data_callback.py +++ b/ovp8xx/python/core/getting_data_callback.py @@ -1,3 +1,7 @@ +############################################# +# Copyright 2023-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# import time from ifm3dpy.device import O3R from ifm3dpy.framegrabber import FrameGrabber, buffer_id diff --git a/ovp8xx/python/core/viewer.py b/ovp8xx/python/core/viewer.py index 528e32e..07265e8 100644 --- a/ovp8xx/python/core/viewer.py +++ b/ovp8xx/python/core/viewer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# # SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: Apache-2.0 # Copyright (C) 2020 ifm electronic gmbh # # THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. diff --git a/ovp8xx/python/ods/ods_demo.py b/ovp8xx/python/ods/ods_demo.py index a3a0ce1..dde9ecd 100644 --- a/ovp8xx/python/ods/ods_demo.py +++ b/ovp8xx/python/ods/ods_demo.py @@ -1,4 +1,7 @@ -# %%################################################################### +############################################# +# Copyright 2023-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# # Showcases a typical sequence of an ODS application running # from the initial configuration to the "while true" streaming of data ###################################################################### diff --git a/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/calib_cam.py b/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/calib_cam.py index f652b21..4ee7fd9 100644 --- a/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/calib_cam.py +++ b/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/calib_cam.py @@ -1,4 +1,7 @@ -# %% +############################################# +# Copyright 2021-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# from datetime import datetime import json import logging From a163a701f49b2f455c514a83685f1813542769a4 Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 16:48:00 +0100 Subject: [PATCH 09/18] restructured the READMEs for each subdirectory --- ovp8xx/python/README.md | 76 +++++---------------------------- ovp8xx/python/core/2d_data.py | 5 +++ ovp8xx/python/core/README.md | 65 ++++++++++++++++++++++++++++ ovp8xx/python/ods/README.md | 11 +++++ ovp8xx/python/toolbox/README.md | 7 +++ 5 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 ovp8xx/python/core/README.md create mode 100644 ovp8xx/python/ods/README.md create mode 100644 ovp8xx/python/toolbox/README.md diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 8c50fc5..ff19387 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -5,74 +5,18 @@ in this Branch you learn how to work with `ifm3dpy` library. The script examples 3. **Toobox** containing useful scripts. ## Core -In this directory you find multiple general O3R scripts that are explained below: - -### 2D data -Receiving RGB data with `ifm3dpy` is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved. -The important part is how to access the RGB image and how to decode it for further use. -Once decoded, the image can be displayed using tools such as OpenCV. The example code in `2d_data.py` illustrates the explained process. - - -### Configuration - -The O3R has multiple parameters that have an influence on the point cloud. Some of them affect the raw measurement and others modify how the data is converted into x,y,z, etc values. These parameters can be changed to better fit your applications and the script `configuration.py` presents how. You can refer to [this page](https://ifm3d.com/latest/Technology/3D/index_3d.html) for a detailed description of each parameter. - -The ifm3d API provides functions to read and set the configuration of the device. Note that JSON formatting is used for all the configurations. - -### Deserialization - -Some of the data provided by the O3R platform needs to be deserialized to be used. This is the case for: -- the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, -- the extrinsic calibration (optics to user) parameters (` ifm3dpy.deserialize.ExtrinsicOpticToUser`), which provides the transformations between the optical system and the reference point on the camera housing, -- the ODS zone information (`ifm3dpy.deserialize.ODSInfoV1`), which contains the zone id being used and the occupancy of the zones, -- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transformation matrix, -- the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. - -For more information on the data structures of each buffer please refer to the [python3 API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html). - -The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example code, `deserialize.py` for an example on deserializing the `RGBInfoV1` buffer. - -### Getting data - -The primary objective of `ifm3d` is to make it as simple and performant as possible to acquire pixel data from an ifm 3D camera of the O3xxxx series. -Additionally, the data should be encoded in a useful format for performing computer vision and/or robotics perception tasks. -A typical `ifm3d` client program follows the structure of a control loop whereby images are continuously acquired from the camera and acted upon in some application-specific way. - -`ifm3dpy` provides three main classes: -- `O3R` holds the configuration of the camera heads, handles the connection, etc; -- `FrameGrabber` receives frames (images); -- `Frame` stores the image buffers. - -The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It inherits its name from previous ifm 3D devices that only used one camera, with no distinction between sensing and computing units. - -The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. -Its inputs: -- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; -- `port`: PCIC port number of the camera head to grab data from (not the physical port number); - -Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. -Simply access the image by calling `get_buffer` passing the `buffer_id` of the required image as a parameter. - -The recommended way to receive a frame 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. - -### Multi head -The "multi_head.py" script demonstrates how to know the connected heads to the VPU and their types. - -### Viewer -In the `ifm3dpy_viewer.py` python3 script a full demonstration of how to view the different images is done. - -### Firmware update - -The script `fw_update_utils.py` demonstrates how to perform a firmware update for your O3R system. Additionally, the script includes several utility functions that provide information, such as determining the current firmware version. - -### Timestamps - -The script `timestamps.py` demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. - +In the Core directory you find multiple general O3R scripts, for instance: +* `2d_data.py`: shows how to receive 2d data. +* `configuration.py`: presents how to configure the O3R parameters +* `deserialize.py`: presents an example on how to deserialize the o3r data. +* `getting_data.py`: presents how to get the data. +* `multi_head.py`: demonstrates how to know the connected heads to the VPU. +* `ifm3dpy_viewer.py`: presents a full demonstration for viewing different images. +* `fw_update_utils.py`: demonstrates how to perform a firmware update for your O3R system. +* `timestamps.py`: demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. ## ODS -The ODS python3 scripts will be briefly described below: - +The ODS directory contains python3 scripts including: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. * `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. diff --git a/ovp8xx/python/core/2d_data.py b/ovp8xx/python/core/2d_data.py index 1a91fc8..d5fa036 100644 --- a/ovp8xx/python/core/2d_data.py +++ b/ovp8xx/python/core/2d_data.py @@ -1,3 +1,8 @@ +############################################# +# Copyright 2023-present ifm electronic, gmbh +# SPDX-License-Identifier: Apache-2.0 +############################################# + import time import cv2 from ifm3dpy.device import O3R diff --git a/ovp8xx/python/core/README.md b/ovp8xx/python/core/README.md new file mode 100644 index 0000000..753faea --- /dev/null +++ b/ovp8xx/python/core/README.md @@ -0,0 +1,65 @@ +# Core +In this directory you find multiple general O3R scripts that are explained below: + +## 2D data +Receiving RGB data with `ifm3dpy` is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved. +The important part is how to access the RGB image and how to decode it for further use. +Once decoded, the image can be displayed using tools such as OpenCV. The example code in `2d_data.py` illustrates the explained process. + + +## Configuration + +The O3R has multiple parameters that have an influence on the point cloud. Some of them affect the raw measurement and others modify how the data is converted into x,y,z, etc values. These parameters can be changed to better fit your applications and the script `configuration.py` presents how. You can refer to [this page](https://ifm3d.com/latest/Technology/3D/index_3d.html) for a detailed description of each parameter. + +The ifm3d API provides functions to read and set the configuration of the device. Note that JSON formatting is used for all the configurations. + +## Deserialization + +Some of the data provided by the O3R platform needs to be deserialized to be used. This is the case for: +- the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, +- the extrinsic calibration (optics to user) parameters (` ifm3dpy.deserialize.ExtrinsicOpticToUser`), which provides the transformations between the optical system and the reference point on the camera housing, +- the ODS zone information (`ifm3dpy.deserialize.ODSInfoV1`), which contains the zone id being used and the occupancy of the zones, +- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transformation matrix, +- the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. + +For more information on the data structures of each buffer please refer to the [python3 API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html). + +The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example code, `deserialize.py` for an example on deserializing the `RGBInfoV1` buffer. + +## Getting data + +The primary objective of `ifm3d` is to make it as simple and performant as possible to acquire pixel data from an ifm 3D camera of the O3xxxx series. +Additionally, the data should be encoded in a useful format for performing computer vision and/or robotics perception tasks. +A typical `ifm3d` client program follows the structure of a control loop whereby images are continuously acquired from the camera and acted upon in some application-specific way. + +`ifm3dpy` provides three main classes: +- `O3R` holds the configuration of the camera heads, handles the connection, etc; +- `FrameGrabber` receives frames (images); +- `Frame` stores the image buffers. + +The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It inherits its name from previous ifm 3D devices that only used one camera, with no distinction between sensing and computing units. + +The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. +Its inputs: +- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; +- `port`: PCIC port number of the camera head to grab data from (not the physical port number); + +Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. +Simply access the image by calling `get_buffer` passing the `buffer_id` of the required image as a parameter. + +The recommended way to receive a frame 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. + +## Multi head +The "multi_head.py" script demonstrates how to know the connected heads to the VPU and their types. + +## Viewer +In the `ifm3dpy_viewer.py` python3 script a full demonstration of how to view the different images is done. + +## Firmware update + +The script `fw_update_utils.py` demonstrates how to perform a firmware update for your O3R system. Additionally, the script includes several utility functions that provide information, such as determining the current firmware version. + +## Timestamps + +The script `timestamps.py` demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. + diff --git a/ovp8xx/python/ods/README.md b/ovp8xx/python/ods/README.md new file mode 100644 index 0000000..ca3d6a2 --- /dev/null +++ b/ovp8xx/python/ods/README.md @@ -0,0 +1,11 @@ +# ODS +The ODS python3 scripts will be briefly described below: + +* `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. +* `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. +* `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. +* `ods_data_analyze.py`: ods data analyzer script from a h5 file. +* `ods_queue.py` : This script handles the data queues of an ODS application. +* `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform +* `ods_visualization.py`: is a script used for ODS visualization. +* `ods_demo.py`: is using the described scripts to do a full demonstration of the ODS application. \ No newline at end of file diff --git a/ovp8xx/python/toolbox/README.md b/ovp8xx/python/toolbox/README.md new file mode 100644 index 0000000..19aad68 --- /dev/null +++ b/ovp8xx/python/toolbox/README.md @@ -0,0 +1,7 @@ +# Toolbox: +Within the Toolbox, you find helper scripts, including: +* Angle converter +* Extrinsic calibration: + * `extrinsic_calib_verification.py`: is a script to verify the extrinsic calibration from h5 data. +* H5 to ifm3d lib converter +* 2D-3D registration script \ No newline at end of file From c26d66f7a7b1a55f63aa3a8cb867a0c2722c36b0 Mon Sep 17 00:00:00 2001 From: Fares Date: Thu, 11 Jan 2024 17:22:20 +0100 Subject: [PATCH 10/18] moved diagnostic.py and bootup_monitor.py under core and changed READMEs accordingly --- ovp8xx/python/README.md | 2 ++ ovp8xx/python/core/README.md | 5 +++++ ovp8xx/python/{ods => core}/bootup_monitor.py | 0 ovp8xx/python/{ods => core}/diagnostic.py | 0 ovp8xx/python/ods/README.md | 2 -- ovp8xx/python/ods/ods_demo.py | 10 +++++++--- 6 files changed, 14 insertions(+), 5 deletions(-) rename ovp8xx/python/{ods => core}/bootup_monitor.py (100%) rename ovp8xx/python/{ods => core}/diagnostic.py (100%) diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index ff19387..899ee17 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -14,6 +14,8 @@ In the Core directory you find multiple general O3R scripts, for instance: * `ifm3dpy_viewer.py`: presents a full demonstration for viewing different images. * `fw_update_utils.py`: demonstrates how to perform a firmware update for your O3R system. * `timestamps.py`: demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. +* `diagnostic.py` contains helper functions for retrieving diagnostics when requested or asynchronously. +* `bootup_monitor.py` checks that the VPU completes it's boot sequence. ## ODS The ODS directory contains python3 scripts including: diff --git a/ovp8xx/python/core/README.md b/ovp8xx/python/core/README.md index 753faea..56c8879 100644 --- a/ovp8xx/python/core/README.md +++ b/ovp8xx/python/core/README.md @@ -63,3 +63,8 @@ The script `fw_update_utils.py` demonstrates how to perform a firmware update fo The script `timestamps.py` demonstrate how to get the timestamps and the effect of `sNTP` on the timestamps. +## Diagnostic +The script `diagnostic.py` contains helper functions for retrieving diagnostics when requested or asynchronously. + +## Bootup monitor: +The script `bootup_monitor.py` checks that the VPU completes it's boot sequence before attempting to initialize an application. diff --git a/ovp8xx/python/ods/bootup_monitor.py b/ovp8xx/python/core/bootup_monitor.py similarity index 100% rename from ovp8xx/python/ods/bootup_monitor.py rename to ovp8xx/python/core/bootup_monitor.py diff --git a/ovp8xx/python/ods/diagnostic.py b/ovp8xx/python/core/diagnostic.py similarity index 100% rename from ovp8xx/python/ods/diagnostic.py rename to ovp8xx/python/core/diagnostic.py diff --git a/ovp8xx/python/ods/README.md b/ovp8xx/python/ods/README.md index ca3d6a2..616b4c4 100644 --- a/ovp8xx/python/ods/README.md +++ b/ovp8xx/python/ods/README.md @@ -1,8 +1,6 @@ # ODS The ODS python3 scripts will be briefly described below: -* `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. -* `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. * `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. * `ods_data_analyze.py`: ods data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. diff --git a/ovp8xx/python/ods/ods_demo.py b/ovp8xx/python/ods/ods_demo.py index dde9ecd..8f47ccb 100644 --- a/ovp8xx/python/ods/ods_demo.py +++ b/ovp8xx/python/ods/ods_demo.py @@ -9,12 +9,16 @@ # Imports from ods_visualization import OCVWindow, ODSViz import logging - import numpy as np from ifm3dpy.device import O3R +import sys +import os + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.dirname(SCRIPT_DIR)) -from bootup_monitor import BootUpMonitor -from diagnostic import O3RDiagnostic +from core.diagnostic import O3RDiagnostic +from core.bootup_monitor import BootUpMonitor from ods_config import validate_json, load_config_from_file from ods_stream import ODSStream From f3f6c6528b39512d7054a626b007308de3fbadc2 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Mon, 15 Jan 2024 13:26:18 -0500 Subject: [PATCH 11/18] Add supported languages --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f9b900e..a02f20a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ For the Python library, install using pip: `pip install ifm3dpy`. For more details refer to [the ifm3d documentation on api.ifm3d.com](https://api.ifm3d.com/stable/index.html). +## Supported languages + +Currently, we support the following languages: +| Name | Versions | +| --------- | --------------------- | +| Python | 3.8, 3.9, 3.10, 3.11 | +| C++ | 14, 17, 20 | + ## o3d3xx-o3x1xx This folder contains examples for the O3D3XX and the O3X1XX camera series. From 9bac4870f10fc9faf8147361b863f6e7bb43f9d2 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Tue, 16 Jan 2024 10:59:42 -0500 Subject: [PATCH 12/18] Fix c++ compiler versions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a02f20a..63b1469 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Currently, we support the following languages: | Name | Versions | | --------- | --------------------- | | Python | 3.8, 3.9, 3.10, 3.11 | -| C++ | 14, 17, 20 | +| C++ | GCC 7.5+, MSVC 2019+ | ## o3d3xx-o3x1xx From ba161ffbc964f84f043aa7dc5efc9bc2a579f055 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Tue, 16 Jan 2024 11:52:37 -0500 Subject: [PATCH 13/18] Replace Python3 with Python --- ovp8xx/README.md | 2 +- ovp8xx/python/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ovp8xx/README.md b/ovp8xx/README.md index 1e31f71..1a66cdc 100644 --- a/ovp8xx/README.md +++ b/ovp8xx/README.md @@ -4,7 +4,7 @@ This directory contains example codes in `python3` and `c++` to help you start w 1. **Core** 2. **ODS** -3. **Toolbox** (only `Python3` examples available atm) +3. **Toolbox** (only `Python` examples available atm) ## Core The core examples, shows you how to use the `ifm3d` API to obtain image data (2D, 3D, distance image, etc.), configure camera parameters, update the embedded firmware, and more. diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 899ee17..7aaa9a9 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -1,4 +1,4 @@ -# Python3 Examples: +# Python Examples in this Branch you learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: 1. **Core:** containing general scripts for O3R system. 2. **ODS:** containing ODS example scripts. From 088edbdf2aee919ceb596b57b967348b1ea32484 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Tue, 16 Jan 2024 14:59:06 -0500 Subject: [PATCH 14/18] Typos --- ovp8xx/cpp/README.md | 2 +- ovp8xx/cpp/core/2d_data/2d_data.md | 2 +- ovp8xx/cpp/core/deserialize/deserialize.md | 6 +++--- ovp8xx/cpp/core/getting_data/getting_data.md | 4 ++-- ovp8xx/python/README.md | 8 ++++---- ovp8xx/python/core/viewer.md | 14 +++++++------- ovp8xx/python/ods/README.md | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ovp8xx/cpp/README.md b/ovp8xx/cpp/README.md index 24ee890..052a9b4 100644 --- a/ovp8xx/cpp/README.md +++ b/ovp8xx/cpp/README.md @@ -8,7 +8,7 @@ ## Build the examples -Follow the instructions below to build the examples. The commands might need to ce updated to run on Windows. +Follow the instructions below to build the examples. The commands might need to be updated to run on Windows. ```bash $ cd Cpp diff --git a/ovp8xx/cpp/core/2d_data/2d_data.md b/ovp8xx/cpp/core/2d_data/2d_data.md index d7f047b..e76bb87 100644 --- a/ovp8xx/cpp/core/2d_data/2d_data.md +++ b/ovp8xx/cpp/core/2d_data/2d_data.md @@ -1,4 +1,4 @@ -# How to: receive and use the 2D rgb image +# How to: receive and use the 2D RGB image Receiving RGB data with ifm3d is done similarly as 3D data: the core objects have to be instantiated, and a frame has to be retrieved (see full code below). The important part is how to access the RGB image and how to decode it for further use. diff --git a/ovp8xx/cpp/core/deserialize/deserialize.md b/ovp8xx/cpp/core/deserialize/deserialize.md index 615f370..2cc6c89 100644 --- a/ovp8xx/cpp/core/deserialize/deserialize.md +++ b/ovp8xx/cpp/core/deserialize/deserialize.md @@ -4,12 +4,12 @@ Some of the data provided by the O3R platform needs to be deserialized to be use - the intrinsic calibration parameters (`ifm3dpy.deserialize.Calibration`), which provides details like which optical model is used (Fisheye, pinhole) and the values for each of the model's parameters, - the extrinsic calibration (optics to user) parameters (` ifm3dpy.deserialize.ExtrinsicOpticToUser`), which provides the transformations between the optical system and the reference point on the camera housing, - the ODS zone information (`ifm3dpy.deserialize.ODSInfoV1`), which contains the zone id being used and the occupancy of the zones, -- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transormation matrix, +- the ODS occupancy grid information (`ifm3dpy.deserialize.ODSOccupancyGridV1`), which contains occupancy grid data and the transformation matrix, - the RGB information (`ifm3dpy.deserialize.RGBInfoV1`), which provides exposure times and calibration parameters for the O3R RGB cameras. -For more information on the data structures of each buffer please refer to the [python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html) or the [c++ API documentation]. +For more information on the data structures of each buffer please refer to the [Python API documentation](https://api.ifm3d.com/latest/_autosummary/ifm3dpy.deserialize.html) or the [c++ API documentation]. -The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserlize function. Follow the example below for an example on deserialializing the `RGBInfoV1` buffer. +The usage of the deserializer is the same for all the buffers mentioned above: create the object, and call the deserialize function. Follow the example below for an example on deserializing the `RGBInfoV1` buffer. :::::{tabs} :::: {group-tab} Python diff --git a/ovp8xx/cpp/core/getting_data/getting_data.md b/ovp8xx/cpp/core/getting_data/getting_data.md index c84b726..d52b3ff 100644 --- a/ovp8xx/cpp/core/getting_data/getting_data.md +++ b/ovp8xx/cpp/core/getting_data/getting_data.md @@ -31,7 +31,7 @@ auto fg = std::make_shared(o3r, 50012); :::: ::::: ->Note: The example above assumes that an O3R camera head is connected to the VPU at the physical port 2, which has the PCIC TCP port 50012. The PCIC TCP port number can be retrieved via `o3r.get([f"/ports/portX/data/pcicTCPPort"])` with `portX` being a valid port (eg "port0","port1",etc.). +>Note: The example above assumes that an O3R camera head is connected to the VPU at the physical port 2, which has the PCIC TCP port 50012. The PCIC TCP port number can be retrieved via `o3r.get([f"/ports/portX/data/pcicTCPPort"])` with `portX` being a valid port (for example `port0`, `port1`, etc). The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It inherits its name from previous ifm 3D devices that only used one camera, with no distinction between sensing and computing units. You can input: @@ -41,7 +41,7 @@ You can input: The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. Its inputs: -- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; +- `o3r`: The O3R instance (the image processing platform) that handles the connection to the camera heads; - `port`: PCIC port number of the camera head to grab data from (not the physical port number); > Note: instantiating the objects is done the same way for any imager type (2D, 3D, different resolutions, etc). diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 7aaa9a9..a18d9e2 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -8,7 +8,7 @@ in this Branch you learn how to work with `ifm3dpy` library. The script examples In the Core directory you find multiple general O3R scripts, for instance: * `2d_data.py`: shows how to receive 2d data. * `configuration.py`: presents how to configure the O3R parameters -* `deserialize.py`: presents an example on how to deserialize the o3r data. +* `deserialize.py`: presents an example on how to deserialize the O3R data. * `getting_data.py`: presents how to get the data. * `multi_head.py`: demonstrates how to know the connected heads to the VPU. * `ifm3dpy_viewer.py`: presents a full demonstration for viewing different images. @@ -18,17 +18,17 @@ In the Core directory you find multiple general O3R scripts, for instance: * `bootup_monitor.py` checks that the VPU completes it's boot sequence. ## ODS -The ODS directory contains python3 scripts including: +The ODS directory contains Python scripts including: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. -* `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. +* `ods_config.py`: demonstrates how to set JSON configurations to the O3R system following the O3R schema. * `ods_data_analyze.py`: ods data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. * `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform * `ods_visualization.py`: is a script used for ODS visualization. * `ods_demo.py`: is using the described scripts to do a full demonstration of the ODS application. -## Toolbox: +## Tool box Within the Toolbox, you find helper scripts, including: * Angle converter * Extrinsic calibration: diff --git a/ovp8xx/python/core/viewer.md b/ovp8xx/python/core/viewer.md index 43f5ad8..51a91bb 100644 --- a/ovp8xx/python/core/viewer.md +++ b/ovp8xx/python/core/viewer.md @@ -3,19 +3,19 @@ This is an example application for retrieving different kinds of images from an O3R platform. ## Download the code -If you built ifm3d from source, you already have the code, in ifm3d/examples/o3r/viewer. +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 venv. +The recommended way is to use a virtual environment. -### Create a new venv +### Create a new virtual environment ```sh python3 -m venv my_venv ``` -### Activate the venv +### Activate the virtual environment | Platform | Shell | Command to activate virtual environment | | -------- | --------------- | --------------------------------------- | @@ -37,7 +37,7 @@ pip install ifm3dpy pip install -r requirements.txt pip install . ``` -Consider [the python documentation](../../../doc/sphinx/content/python.md) for more details. +Consider [the Python documentation](../../../doc/sphinx/content/python.md) for more details. ### Install requirements ```sh @@ -73,7 +73,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 +```sh python examples/python/viewer/ifm3dpy_viewer.py --pcic-port 50010 --image jpeg ``` diff --git a/ovp8xx/python/ods/README.md b/ovp8xx/python/ods/README.md index 616b4c4..077c951 100644 --- a/ovp8xx/python/ods/README.md +++ b/ovp8xx/python/ods/README.md @@ -1,8 +1,8 @@ # ODS -The ODS python3 scripts will be briefly described below: +The ODS Python scripts will be briefly described below: -* `ods_config.py`: demonstrates how to set json configs to the o3r system following the o3r schema. -* `ods_data_analyze.py`: ods data analyzer script from a h5 file. +* `ods_config.py`: demonstrates how to set JSON configurations to the O3R system following the O3R schema. +* `ods_data_analyze.py`: ODS data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. * `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform * `ods_visualization.py`: is a script used for ODS visualization. From 6a78c7ca31d5aa3178e5879ebf760ca106d80bf0 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Thu, 18 Jan 2024 10:40:11 -0500 Subject: [PATCH 15/18] Simplify pipeline syntax and ignore o3d/o3x files --- .gitlab-ci.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ea5625..ffe6f17 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,12 +14,8 @@ vale-linting: before_script: - apk update && apk add git script: - - git config --global user.email "support.robotics@ifm.com" - - git config --global user.name "ifm-csr" - - rm -rf /builds/syntron/support/csr/o3r/formatting-tools /builds/syntron/support/csr/o3r/vale - - mkdir -p /builds/syntron/support/csr/o3r/formatting-tools - - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab-ee.dev.ifm/syntron/support/csr/formatting-tools.git /builds/syntron/support/csr/o3r/formatting-tools - - cd /builds/syntron/support/csr/o3r/formatting-tools - - vale --minAlertLevel error /builds/syntron/support/csr/ifm3d/ifm3d-examples/. + - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab-ee.dev.ifm/syntron/support/csr/formatting-tools.git ${CI_PROJECT_DIR}/formatting-tools + - cd ${CI_PROJECT_DIR}/formatting-tools + - vale --minAlertLevel error ../ovp8xx ../README.md From ce1ebafd6abfe72968adb478bb1bb545126d8527 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Thu, 18 Jan 2024 10:45:49 -0500 Subject: [PATCH 16/18] Fix syntax --- ovp8xx/python/README.md | 4 ++-- ovp8xx/python/core/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index a18d9e2..28898bf 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -2,7 +2,7 @@ in this Branch you learn how to work with `ifm3dpy` library. The script examples are divided depending on the use case: 1. **Core:** containing general scripts for O3R system. 2. **ODS:** containing ODS example scripts. -3. **Toobox** containing useful scripts. +3. **Toolbox** containing useful scripts. ## Core In the Core directory you find multiple general O3R scripts, for instance: @@ -22,7 +22,7 @@ The ODS directory contains Python scripts including: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. * `ods_config.py`: demonstrates how to set JSON configurations to the O3R system following the O3R schema. -* `ods_data_analyze.py`: ods data analyzer script from a h5 file. +* `ods_data_analyze.py`: ODS data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. * `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform * `ods_visualization.py`: is a script used for ODS visualization. diff --git a/ovp8xx/python/core/README.md b/ovp8xx/python/core/README.md index 56c8879..934b8d0 100644 --- a/ovp8xx/python/core/README.md +++ b/ovp8xx/python/core/README.md @@ -41,7 +41,7 @@ The `O3R` class, counter-intuitively, refers to the computing unit (the VPU). It The `FrameGrabber` stores a reference to the passed in camera shared pointer and starts a worker thread to stream in pixel data from the device. Its inputs: -- `o3r`: The o3r instance (the image processing platform) that handles the connection the the camera heads; +- `o3r`: The O3R instance (the image processing platform) that handles the connection to the camera heads; - `port`: PCIC port number of the camera head to grab data from (not the physical port number); Accessing the received data is done through the `Frame`. Different data types are available depending on whether the camera is a 2D or a 3D camera. From 30682634d28bab6caa8e69b9bd8938cc7a8e4633 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Thu, 18 Jan 2024 10:46:41 -0500 Subject: [PATCH 17/18] Syntax fix --- .../extrinsic_calibration/static_camera_calibration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/README.md b/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/README.md index 56ae0f8..d8c90ef 100644 --- a/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/README.md +++ b/ovp8xx/python/toolbox/extrinsic_calibration/static_camera_calibration/README.md @@ -41,7 +41,7 @@ Before proceeding to perform the calibration process we assume that the user: - Move the Robot such that the Y-Axis of a Robot Coordinate System is exactly parallel to the checkerboard. ```{image} _resources/TOP_VIEW.png -:alt: top_view +:alt: top view :width: 400px :align: center ``` From ff4827dfb6133ec06c16834539d4cc366a06c787 Mon Sep 17 00:00:00 2001 From: "Masson, Lola" Date: Thu, 18 Jan 2024 11:21:22 -0500 Subject: [PATCH 18/18] Remove ods_data_analyze.py example --- ovp8xx/python/README.md | 1 - ovp8xx/python/ods/README.md | 1 - ovp8xx/python/ods/ods_data_analyze.py | 266 -------------------------- 3 files changed, 268 deletions(-) delete mode 100644 ovp8xx/python/ods/ods_data_analyze.py diff --git a/ovp8xx/python/README.md b/ovp8xx/python/README.md index 28898bf..c32aaed 100644 --- a/ovp8xx/python/README.md +++ b/ovp8xx/python/README.md @@ -22,7 +22,6 @@ The ODS directory contains Python scripts including: * `bootup_monitor.py`: Checks that the VPU completes it's boot sequence before attempting to initialize an application. * `diagnostic.py`: Contains helper functions for retrieving diagnostics when requested or asynchronously. * `ods_config.py`: demonstrates how to set JSON configurations to the O3R system following the O3R schema. -* `ods_data_analyze.py`: ODS data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. * `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform * `ods_visualization.py`: is a script used for ODS visualization. diff --git a/ovp8xx/python/ods/README.md b/ovp8xx/python/ods/README.md index 077c951..4dec4e5 100644 --- a/ovp8xx/python/ods/README.md +++ b/ovp8xx/python/ods/README.md @@ -2,7 +2,6 @@ The ODS Python scripts will be briefly described below: * `ods_config.py`: demonstrates how to set JSON configurations to the O3R system following the O3R schema. -* `ods_data_analyze.py`: ODS data analyzer script from a h5 file. * `ods_queue.py` : This script handles the data queues of an ODS application. * `ods_stream.py` : Provides functions showcasing how to receive data from the O3R platform * `ods_visualization.py`: is a script used for ODS visualization. diff --git a/ovp8xx/python/ods/ods_data_analyze.py b/ovp8xx/python/ods/ods_data_analyze.py deleted file mode 100644 index 088a2e2..0000000 --- a/ovp8xx/python/ods/ods_data_analyze.py +++ /dev/null @@ -1,266 +0,0 @@ -############################################# -# Copyright 2021-present ifm electronic, gmbh -# SPDX-License-Identifier: Apache-2.0 -############################################# - -# %% Import Libraries - -import math -import logging -from pathlib import Path, PosixPath - -import h5py -import numpy as np -from matplotlib import pyplot as plt -from scipy.spatial import ConvexHull -import skimage.measure -import skimage.color -import skimage.filters - - -logger = logging.getLogger(__name__) -# %% - - -def get_data_from_h5(filepath: PosixPath) -> tuple: - - with h5py.File(filepath, "r") as data: - streams = list(data["streams"]) - logger.info(f'available streams: {list(data["streams"])}') - - stream_2d_left = data["streams"]["o3r_rgb_0"] - stream_2d_right = data["streams"]["o3r_rgb_1"] - stream_3d_left = data["streams"]["o3r_tof_0"] - stream_3d_right = data["streams"]["o3r_tof_1"] - stream_ods = data["streams"]["o3r_app_ods_0"] - - data_2d_left = np.asarray(stream_2d_left) - data_2d_right = np.asarray(stream_2d_right) - data_3d_left = np.asarray(stream_3d_left) - data_3d_right = np.asarray(stream_3d_right) - data_ods = np.asarray(stream_ods) - - for s in streams: - logger.info(f"stream: {s}") - logger.info(f"stream content: {data['streams'][s].dtype} \n") - - return data_2d_left, data_2d_right, data_3d_left, data_3d_right, data_ods - - -def get_distance_map_data(data_ods: np.ndarray, roi: list = [85, 115]) -> tuple: - """ - Retrieves the distance map of a recorded iVA dataset - - Args: - stream_ods (np.ndarray): input data stream ODS - - Returns: - tuple: 'time vs distance' of a recorded data - """ - - data_ods = np.array(data_ods[:]["image"]) - total_occupancy_grids = data_ods.shape[0] - rows_in_occupancy_grid = data_ods.shape[1] - - # We are interested in the frames where the object is detected inside in ROI - frames = [] - distance_map = np.zeros(data_ods[:, 0, :].shape) - - for occupancy_grid in range(total_occupancy_grids): - for row in range(rows_in_occupancy_grid): - - idx = np.nonzero(data_ods[occupancy_grid, row, :] > 127)[ - 0 - ] # non-zero values per row - - if idx.size == 0: # Non non-zero value found in a row of occupancy grid - distance_map[occupancy_grid, row] = 200 - else: - distance_map[occupancy_grid, row] = idx[0] - if ( - roi[0] <= row <= roi[1] - ): # ROI is considered as 85 to 115 rows in occupancy grid - frames.append(occupancy_grid) - - distance_map = -distance_map # invert map - frames = sorted(list(set(frames))) - return distance_map, frames - - -def connected_components(gray_image: np.ndarray) -> tuple: - """evaluate the connected components on a gray scale image - - Args: - gray_image (np.ndarray): input image - - Returns: - tuple: label images and counter - """ - binary_mask = gray_image > 127 - labeled_image, count = skimage.measure.label(binary_mask, return_num=True) - return labeled_image, count - - -def get_obj_pos(obj): - coords = obj["coords"] - x_coord, y_coord = None, None - - try: - hull = ConvexHull(coords) - for v in hull.vertices: - if x_coord == None and y_coord == None: - x_coord = coords[v][0] - y_coord = coords[v][1] - else: - if ( - coords[v][1] < y_coord - ): # check difference in y coordinates only not Euclidean distance - x_coord = coords[v][0] - y_coord = coords[v][1] - except Exception as e: - logger.warning(e) - pass - return x_coord, y_coord - - -def distance_tracker(data_ods: np.ndarray, frames: list, roi: list = [85, 115]): - """Distance from user coordinate frame origin to the first object detected in ROI vs Time - - Args: - data_ods (np.ndarray): input data stream ODS - frames (list): frames where object is detected in ROI - - Returns: - tuple: list of frames, coordinates of nearest object of interest - """ - - check_box = True - check_centroid = False - frame_detected = [] - coordinates = [] - - for f in frames: - occ_frame = data_ods[f]["image"] - labeled_image, _ = connected_components(occ_frame) - object_features = skimage.measure.regionprops(labeled_image) - - for obj in object_features: - if check_box: - # (min_row, min_col, max_row, max_col) - # https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.regionprops - - min_row, min_col, max_row, max_col = obj["bbox"] - if min_row > roi[0] and max_row < roi[1]: # bbox inside the roi - frame_detected.append(f) - x_coord, y_coord = get_obj_pos(obj) - - if check_centroid: - c_row, c_col = obj["centroid"][0] - if roi[0] < c_row < roi[1]: # centroid inside the roi - frame_detected.append(f) - x_coord, y_coord = get_obj_pos(obj) - - coordinates.append([x_coord, y_coord]) - - return frame_detected, coordinates - - -# %% - - -def main(): - logging.basicConfig(level=logging.DEBUG, format="%(message)s") - logging.getLogger("matplotlib").setLevel(logging.WARNING) - logging.getLogger("skimage").setLevel(logging.WARNING) - - FILENAME = "8. pallet_jack_105deg.h5" - FILEPATH = Path("/path/to/local/file/directory/") - FP = Path.joinpath(FILEPATH, FILENAME) - - ( - data_2d_left, - data_2d_right, - data_3d_left, - data_3d_right, - data_ods, - ) = get_data_from_h5(FP) - distance_map, frames = get_distance_map_data(data_ods) - - # Plot the distance map - plt.figure(100) - plt.imshow(distance_map.T, cmap="jet", interpolation="none") - plt.colorbar() - plt.axhline(85, color="r", linestyle="dashed") - plt.axhline(115, color="r", linestyle="dashed") - plt.xlabel("Frame counter #frame") - plt.ylabel("Y-coordinates in occ pixel space") - plt.title("Distance Map of Recorded Data") - - # Plot a selection of occupancy grids before and after the detection - plt.figure(200) - range_occ_grids = np.arange(frames[0] - 8, frames[0] + 8) - for i, frame in enumerate(range_occ_grids): - - plt.subplot(4, 4, i + 1) - plt.imshow(data_ods[frame]["image"], cmap="gray", interpolation="none") - plt.axhline(85, color="r", linestyle="dashed") - plt.axhline(115, color="r", linestyle="dashed") - plt.colorbar() - if frame == frames[0]: - plt.title(f"Object detected in frame {frame}") - else: - plt.title(f"Frame {frame}") - - # Distance Tracker - frame_detected, coordinates = distance_tracker(data_ods, frames) - trans_x, trans_y, trans_z = data_3d_right["extrinsicOpticToUserTrans"][ - 0 - ] # get extrinsic calibration values - - user_origin = [100, 100] - trans_x_grid_space = (trans_x * 1000) / 50 - trans_y_grid_space = (trans_y * 1000) / 50 - - distances = [] - for coord in coordinates: - if coord[0] is not None: - distances.append( - math.sqrt( - (coord[0] - user_origin[0] - trans_y_grid_space) ** 2 - + (coord[1] - user_origin[1] - trans_x_grid_space) ** 2 - ) - ) - - distances = np.convolve( - distances, np.ones(5) / 5, mode="valid" - ) # smooth distance information - - plt.figure(301) - for d, f in zip(distances, frame_detected): - plt.plot(f, d * 50, "b+") - plt.xlim(0, len(data_ods) + 1) - plt.xlabel("frame counter") - plt.ylabel("Distance in mm") - dist_y_max = max(distances) * 50 - plt.title( - f"detected object: distance tracking over time\n maximum distance: {int(dist_y_max)} mm" - ) - - plt.figure() - pos = np.arange(130, 166) - for n in range(len(pos)): - plt.subplot(6, 6, n + 1) - plt.imshow(data_ods[pos[n]]["image"] > 127, - cmap="gray", interpolation="none") - try: - idx = frame_detected.index(pos[n]) - plt.plot(coordinates[idx][1], coordinates[idx][0], "r+") - except ValueError as e: - pass - plt.colorbar() - plt.title(f"frame {pos[n]}") - - -if __name__ == "__main__": - main() -# %%