| IOFormat 1.8.0

Loading...
Searching...
No Matches
Create a Custom Plugin

Custom plugins can be written in C++ only.

If you want to create your own Extractor or Writer plugin, you can by following the API contained in the IOPluginAPI project:

- ExtractorPlugin
- WriterPlugin

IOPlugin APIs are versioned and each version of IOFormat is only compatible with one version of IOPlugin. All plugins developed with a different IOPlugin version of the one used by IOFormat won't be loaded.

See configuration to know the IOPlugin version compatible with this version of IOFormat.

You must add the IOPlugin Conan package to your plugin dependencies, to get the included and logging libraries. You must link against this package using the classic Conan way. If you want your plugin to be used in IOFormat, it must be declared as a `SHARED` library in CMake.

add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES})
target_link_libraries(${PROJECT_NAME} CONAN_PKG::IOPlugin)

An IOFormat plugin must implement the matching plugin interface: `ExtractorPlugin` or `WriterPlugin`, depending on the type of plugin you want to make. When the class is done, you must use a plugin-defining macro in order to make it usable by the plugin system of IOFormat. These macros depend on the plugin type:

- `IOPLUGIN_DECLARE_EXTRACTOR_PLUGIN(PluginClass)` for Extractor plugins.
- `IOPLUGIN_DECLARE_WRITER_PLUGIN(PluginClass)` for Writer plugins.

The plugin class should be done in a source file (*.cpp or *.cxx) instead of a header file, because we only want to export the instance.

Example for an extractor plugin:

using namespace iolink;
class TestPlugin : public ExtractorPlugin
{
// override the interface's methods
};
IOPLUGIN_DECLARE_EXTRACTOR_PLUGIN(TestPlugin)

`TestPlugin`, in this example, is the entry point for the extractor plugin. It's up to the methods `openDataAccess` and `openFile` of this class to instanciate and return the appropriate view according to the format of the opened file.

Have a look on the code example provided with the IOPlugin API.

For example: calling `openDataAccess` onto PNG plugin will require the instanciation of an object derived from `ImageView`, since PNG is only able to manage 2D Images. On the other hand, a TIFF plugin could possibly be either an object derived from `ImageView` or from `MultiImageView`, depending of the file content. A TIFF file can contain either an image, thus an ImageView, or a list of independant frames, thus a `MultiImageView`.

# Implementation guidelines

There are some guidelines common to the two types of plugins:

- Never override the `api` method, it is already returning the API version of the IOplugin version you are using to create your plugin.
- Logging methods have a default implementation, so if you do not override them, `initLogger` will do nothing, and `logLevel` will always return `LogLevel::OFF`.

# Extractors

## Factories

There are two different `View` factories in the `ExtractorPlugin` interface that you can implement in your custom plugin. First, `openFile` that takes a path to the source file, and `openDataAccess` that takes a `DataAccess` from which to decode from. The `openDataAccess` variant also takes a `format` argument that enables the selection of the format to use to decode the accessor's data. This format is automatically selected using the path's extension in the `openFile` variant.

When one of these variants is not supported by your custom plugin, just make the method throw an `Error` describing that the feature is not supported.

## Preliminary checks and operations

For both cases, a serie of checks and operations should be done immediately:

- Check that the given `format` string parameter to adapt your extraction method to the current situation (i.e. when a plugin handles many different formats).
- Check that the given `path` or `dataAccess` is valid. Data must exist and be available for reading and most of the time for seeking in the case of streams.
- Check the data content by starting to extract some basic information which will help to determine the type of `View` that your plugin should create and return:
  - type of the data (UINT8, UINT16, UINT32, ...)
  - number of frames (if supported by the format)
  - number of dimensions of the frame(s)
  - shape of the frame(s)
- Extract the information to fill the `properties` immediately. You should extract these informations as soon as possible, and not "on the fly", for thread-safety reasons.
- Extract the tree of metadata. For some cases it is better to extract all metadata at the opening. And for other cases it is better to build it on-demand.

If any of these checks/operations fails, an exception will be raised.

The table below should help you determine the kind of `View` to return:

View type | Case where this type of view should be created
--- | ---
`ImageView` | A N-dimension array (e.g a 2D Image, a volume, a sequence of image...)
`MultiImageView` | A set of images which may or may be related with each other (shape and type can be different)
`LodImageView` | One multi-resolution image (many different resolutions of the same N-Dimensional image)
`DataFrameView` | Tabular Data

Up to you to return the view type corresponding the better to extracted data and which matches your needs.

# Writers

## Factories

There are two `Writer` factories in the `WriterPlugin` interface, all taking the source `View` as the first argument. The `openFile` one uses a path to specify the writer's output, and will infer the format to use from the file's extension. The `openDataAccess` variant use a `DataAccess` as its output with its format specified with a `format` parameter.

When one of these variants is not supported by your custom plugin, just make the method throw an `Error` describing that the feature is not supported.

## Input adaptation

Ideally, your custom writer should support any kind of `View` as an entry, as long as it fits the underlying format. For example a plugin encoding to an image format could take any `ImageView` as input. In the image case, the underlying format usually support one particular data layout, but a good plugin should accept different layouts, as long as they are easily adaptable, without damaging the content and without guessing the user intentions. For example, when a plugin uses a format that stores color data in an interleaved order, if the user gives a planar `ImageView` (one with a `CHANNEL` dimension) as input, it should adapt it, instead of throwing an error saying that the view is unsupported. Another example is `MultiImageView` instances, that can be stacked to feed a volume or sequence format, if it is possible.

As long as there is one possibility to adapt the data to match the writer inputs, the plugin should try. However, when there is more than one possibility to adapt the data, the plugin shall raise an exception to ask the user to make a decision.

## Performances

In order to provide better features and performances, writers created by your custom plugin should not write all the output in one call. An approach using chunked write calls should be preferred. It will avoid hangs-on and blocking operations when using non-filesystem accessors.