Camera API

The Camera API is a low level API built on top of the different vendor SDKs and provides a uniform interface to the camera configuration and frame acquisition. A Camera Plugin is an implementation of this interface built on top of the various vendor SDKs.

Synopsis

class camera
{  
  // Define the supported image types (a vector of types)
  typedef ... images_t;

  // Define the type of the initialization parameters
  typedef ... init_params_t;

  // Define the type of the acquisition parameters
  typedef ... acq_params_t;

  // Returns the description of the layout
  layout_t get_layout();
  
  // Control part of the camera
  struct control
  {
      // Initialize the camera
      void control(const init_params_t& params);

      // Introspect the capabilities of the camera
      template <typename Capability>
      std::optional<Capability> get_capability();

      // Prepare the camera for acquisition
      void prepare(const acq_params_t& params);

      // Start the acquisition
      void start();

      // Software trigger if the camera supports it
      void trigger();

      // Stop the acquisition
      void stop();

      // Returns the effective acquisition parameters
      acq_params_t acq_params() const;
  };

  // Acquisition part of the camera
  struct acquisition
  {
      // Initialize the camera
      void acquisition(const init_params_t& params);
      
      // Returns an allocator associated with the buffers
      allocator_t get_allocator();

#if !defined(LIMA_IS_MULTI_DATA_CHANNELS)
      // Returns a newly acquired set of images
      // \warning this call might block and should be called asynchronously
      images_t get_images();
#else
      // Returns a newly acquired set of images for a given data channel
      // \warning this call might block and should be called asynchronously
      images_t get_images(int channel = 0);
      
      // Returns the number of data channels
      int nb_channels() const;
#endif // !defined(LIMA_IS_MULTI_DATA_CHANNELS)
  };
};

State machine

Because different camera SDKs require different steps to the implement the transition from one state to another (e.g. prepared -> running), the FSM’s actions are fully overridable.

TODO

Detector modules layout

A detector layout is composed of modular items. Each item can be rotated and/or flipped due to detector readout electronics design. In such cases, the isometric transformation necessary to reconstruct the data in the final layout must be defined. See :doc:geometry_transformations.

Further operations can be performed on the full layout, affecting all the items. One specific operation is image crop (RoI), which may result in the exclusion of one or more items from the layout. Those “not-in-layout” items are nevertheless referenced by the layout structure in order to not change the item index list.

The layout is just a description of how the modular items are arranged. In order to assemble the final image the source data must be provided. The implementation expects one source view per layout item, even “not-in-layout” items.

An affine matrix transforming from sensor coordinates to final image coordinates is kept by the layout. This is useful when the user wants to keep the same region-of-interest in the sensor while adding/removing geometric transformations.

struct layout_item
{
  // Position in the destination
  point_t dst_topleft;
  
  // Selection within the source
  point_t src_topleft;
  point_t src_dimensions;
  
  // Transformation to be applied on item to reconstruct the layout
  any_isometric_xform_t xform;	// default is none
  
  bool in_layout() const;

  image_rect get_src_bounding_box() const;
  image_rect get_dst_bounding_box() const;
  point_t get_dst_dimensions() const;
};

// A layout is basically a container of items
struct layout
{
  // Add/retreive a layout item
  void add_item(layout_item i);
  const layout_item& get_item(int i) const;

  std::size_t size() const;
	
  // Return/set  the dimension of the full image
  const point_t& get_dims() const;
  void set_dims(point_t dims);

  // converts sensor pixel coordinates into layout coordinates
  affine_t get_sensor_coordinates_matrix() const;

  ...
};

Transformation

The layout implements the following transformations:

  // Image algorithms
  void vert_flip();
  void horz_flip();
  void rotate90cw();
  void rotate90ccw();
  void rotate180();
  void crop(point_t topleft, point_t dimensions);

Each transformation updates the item state as well as the layout dimensions and the sensor-coordinates-matrix.

See :doc:geometry_transformations.

Reconstruction

The layout reconstruction can be performed in two variants

  • Memory. This implementation uses serial Boost.Gil algorithms to reconstruct the layout items into a single image:

    // Reconstruction to memory
    template <typename Range, typename Image, typename Value>
    void assemble(const Range& views, Image& out, Value fillvalue) const;
    
  • Virtual data set: TODO

    // Reconstruction to file
    template <typename Range, typename T>
    void generate_vds(const std::string& filename, Range h5_sources, T fillvalue) const;
    

Usage

Camera construction

// Set the initialization parameters
camera::init_params_t init_params;
init.ip = "127.0.0.1";
init.port = 80;

// Construct the camera
camera cam(init_params);
// Or construct the camera directly with initialization list
camera cam({"127.0.0.1", 8080});

Acquisition

// Set the acquisition parameters
camera::acq_params_t acq_params;
acq.shutter_speed = 5;
acq.binning = {2, 2};

cam.prepare(acq_params);

cam.start();

for (int i : )

Implementation

CRTP

Delegates part of the implementation to the base class.

class config
{
  // Define the supported image types (a vector of types)
  typedef ... images_t;

  // Define the type of the initialization parameters
  typedef ... init_params_t;

  // Define the type of the acquisition parameters
  typedef ... acq_params_t;
};

class camera : lima::camera<camera, config>
{  
}

Reflection / Introspection

Use reflection for parameters so that serialization and visitation could be generated.

with Boost.Hana:

struct init_params {
  BOOST_HANA_DEFINE_STRUCT(Person,
    (std::string, ip),
    (int, port)
  );
};

using namespace hana = boost::hana;

hana::for_each(john, [](auto pair) {
  std::cout << hana::to<char const*>(hana::first(pair)) << ": "
            << hana::second(pair) << std::endl;
});

with Boost.PFR:

struct init_params {
  BOOST_HANA_DEFINE_STRUCT(Person,
    (std::string, ip),
    (int, port)
  );
};

init_params params;

boost::pfr::for_each_field(params, [](const auto& field, std::size_t idx) {
    std::cout << idx << ": " << boost::typeindex::type_id_runtime(field) << '\n';
});

External acquisition loop