ifm3d::Buffer- Basic C++ STL container for ifm3d

ifm3d::Buffer is designed to provide a C++ STL container that can hold buffer data of different types provided by ifm3d vision devices. Data is stored in sequential memory layout and ifm3d::Buffer provides a function template to access the pixel. The pixel data type is defined in an enumeration ifm3d::pixel_format. ifm3d::Buffer class does memory management and the user is free from the memory allocation and deallocation. The assignment operator and the copy constructor only copy the attributes and data is shared across the object.

Creating Buffer Object:

This can be done by calling ifm3d::Buffer() and ifm3d::Buffer(rows, cols, nchannel, ifm3d::pixel_format). The default Constructor only creates the attributes and the allocation of memory is done on create(rows, cols, nchannel, ifm3d::pixel_format) call. ifm3d::Buffer::create(rows, cols, nchannel, ifm3d::pixel_format) will allocate the required memory only if the previous memory is not sufficient to store the buffer data.

//a 100 x 100 Buffer of type 8U

ifm3d::Buffer buffer;
buffer.create(100,100,1,ifm3d::FORMAT_8U)

//OR
//can also be created  with parameter construction

ifm3d::Buffer buffer(100,100,1,ifm3d::FORMAT_8U);
 
//and now turn buffer to a 10 x10 3-channel 8-bit matrix.
// The old content will be deallocated
buffer.create(10,10,3,ifm3d::FORMAT_8U);
	  

Accessing the Pixel

The ifm3d::Buffer provides a template function to access data. Use at<T>(index) or at<T>(i,j) to access the pixel. This functions returns the reference to the pixel. A pixel is defined as a structure of n-channel values of type T at a given index or position in 2D array.

// To access a pixel in ifm3d::Buffer I ( 100,100,1,ifm3d::FORMAT_8U) at  50,50
// position

auto pixel = I.at<uint8_t>(50,50);
// if working as Index array then

auto index = 50*100 + 50 ;
auto pixel = I.at<uint8_t>(index);

// Changing the pixel value can be done as follow :
// Writing 255 at pixel postion 50,50

I.at<uint8_t>(50,50) = 255;
I.at<uint8_t>(index) = 255;

To access a pixel in n-channel ifm3d::Buffer I ( 100,100,3,ifm3d::FORMAT_8U) at (50,50) position, follow the example below (for a 3 channel Buffer), as the pixel is a structure of the values of n-channel at a given position.

// User define struct 
struct pixel{
uint8_t x;
uint8_t y;
uint8_t z;
}pixel_t;

// Accessing the value at 50, 50 position can be done as 

auto pixel = I.at<pixel_t>(50,50);
auto x = pixel.x;
auto y = pixel.y;
auto z = pixel.z;

Buffer Templates

ifm3d::Buffer_<T> extends ifm3d::Buffer where T defines the data type in which the user wants to access the pixel. This removes the need for providing the template type T while using the Buffer functionality and working with std::algorithms.

Compatibility with C++ STL Algorithms

The ifm3d::Buffer provides templated iterator to access data and to work with std::algorithms. The user can use ifm3d::IteratorAdapter<T>(ifm3d::Buffer) which forwards type T to templated iterators of the ifm3d::Buffer.

ifm3d::Buffer buffer(10,10,1,ifm3d::FORMAT_16U);

for (auto value : ifm3d::IteratorAdapter<std::uint16_t>(buffer) )
{
// use value 
}

// For multi channel buffer 
ifm3d::Buffer buffer(10,10,3,ifm3d::FORMAT_16U);

for (auto value : ifm3d::IteratorAdapter<ifm3d::Point3D<uint16_t>>(buffer) )
  {
    std::cout << "(" << value.val[0]  << ",  " << value.val[1] << ", " << value.val[2] << ")" ;
  }
  

The user can convert ifm3d::Buffer to ifm3d::Buffer_<T>, where T will define the type in which the user wants to access the pixel. For ifm3d::Buffer_<T> containers, the ifm3d::pixel_format is deduced internally.

ifm3d::Buffer buffer(10,10,1,ifm3d::FORMAT_16U);
ifm3d::Buffer_<std::uint16_t> buffer_ = buffer;
for (auto value :buffer_ )
{
// use value 
}
// For multi channel buffer 
ifm3d::Buffer buffer(10,10,3,ifm3d::FORMAT_16U);
ifm3d::Buffer_<ifm3d::Point3D<uint16_t>> buffer_ = buffer;
for (auto value : buffer_ )
  {
     std::cout << "(" << value.val[0]  << ",  " << value.val[1] << ", " << value.val[2] << ")" ;
  }

This way, ifm3d::Buffer container can be used with any of the std::algorithm

Example grabbing distance and amplitude in ifm3d::Buffer

#include <iostream>
#include <ifm3d/device.h>
#include <ifm3d/fg.h>
#include <ifm3d/fg/buffer.h>

int main(int argc, const char** argv)
{

  auto dev = ifm3d::Device::MakeShared();

  uint16_t pcic_port = (dev->WhoAmI() == ifm3d::Device::device_family::O3R) ? 50012 : 50010;

  auto fg = std::make_shared<ifm3d::FrameGrabber>(
    dev, pcic_port); // 50012 for O3R use default value for other devices

  fg->Start({ ifm3d::buffer_id::AMPLITUDE,ifm3d::buffer_id::XYZ, ifm3d::buffer_id::RADIAL_DISTANCE });

  //polling the device 
  auto frame = fg->WaitForFrame().get();
  
  ifm3d::Buffer amp_buffer = frame->GetBuffer(ifm3d::buffer_id::AMPLITUDE);
  ifm3d::Buffer dist_buffer = frame->GetBuffer(ifm3d::buffer_id::RADIAL_DISTANCE);

  // Accessing the pixels from ifm3d::Buffer 
  // Query the data format and channel.
  std::cout << amp_buffer.nchannels() << std::endl;
  std::cout << static_cast<int>(amp_buffer.dataFormat()) << std::endl;

  // For O3D devices Amplitude and Distance data is `ifm3d::FORMAT_16U`
  // whereas for O3X and O3R Device, the Amplitude buffer type is `ifm3d::FORMAT_32F`
  // Refer to the section below for the full list of data types. 

  // With a 1 channel and float type buffer, 
  // the user can access data:
  ifm3d::Buffer_<float> amp = amp_buffer;

  for (auto& val : amp) {

    //do something with val value
  }
  return 0;
}

Example grabbing XYZ or n-channel ifm3d::Buffer

#include <iostream>
#include <ifm3d/device.h>
#include <ifm3d/fg.h>
#include <ifm3d/fg/buffer.h>

int main(int argc, const char** argv)
{
  auto dev = ifm3d::Device::MakeShared();

  uint16_t pcic_port = (dev->WhoAmI() == ifm3d::Device::device_family::O3R) ? 50012 : 50010;

  auto fg = std::make_shared<ifm3d::FrameGrabber>(
    dev, pcic_port); // 50012 for O3R use default value for other devices

  fg->Start({ ifm3d::buffer_id::AMPLITUDE,ifm3d::buffer_id::XYZ, ifm3d::buffer_id::RADIAL_DISTANCE });

  //polling the device 
  auto frame = fg->WaitForFrame().get();

  ifm3d::Buffer xyz = frame->GetBuffer(ifm3d::buffer_id::XYZ);

  // Accessing the pixels from ifm3d::Buffer 
  // Query the data format and channel.
  std::cout << xyz.nchannels() << std::endl;
  std::cout << static_cast<int>(xyz.dataFormat()) << std::endl;

  // For O3D devices XYZ data is`ifm3d::FORMAT_16S`
  // whereas for O3X,O3R device XYZ buffer type is `ifm3d::FORMAT_32F
  // Refer to the section below for the full list of data types.

  // With 3 channel and float type
  // using point3D_32F
  ifm3d::Buffer_< ifm3d::Point3D_32F>  xyz_data = xyz; // no copy of data 

  // With a range based `for` loop
  for (const auto& point : xyz_data)
  {
    std::cout << "(" << point.val[1] << "," << point.val[2] << "," << point.val[0] << ")";
  }

  // With a pointer 
  for (int i = 0; i < xyz.height(); i++)
  {
    for (int j = 0; j < xyz.width(); j++)
    {
      auto ptr = xyz.ptr<float>(i, j);
      auto z = ptr[0];
      auto x = ptr[1];
      auto y = ptr[2];

      std::cout << "(" << x << "," << y << "," << z << ")";

    }
  }
  return 0;
}