The ImageIO Plugin API#

Here you can find documentation on how to write your own plugin to allow ImageIO to access a new backend. Plugins are quite object oriented, and the relevant classes and their interaction are documented here:

imageio.core.Format(name, description[, ...])

Represents an implementation to read/write a particular file format

imageio.core.Request(uri, mode, *[, ...])

ImageResource handling utility.

Note

You can always check existing plugins if you want to see examples.

What methods to implement#

To implement a new plugin, create a new class that inherits from imageio.core.Format. and implement the following functions:

imageio.core.Format.__init__(name, description)

Initialize the Plugin.

imageio.core.Format._can_read(request)

Check if Plugin can read from ImageResource.

imageio.core.Format._can_write(request)

Check if Plugin can write to ImageResource.

Further, each format contains up to two nested classes; one for reading and one for writing. To support reading and/or writing, the respective classes need to be defined.

For reading, create a nested class that inherits from imageio.core.Format.Reader and that implements the following functions:

  • Implement _open(**kwargs) to initialize the reader. Deal with the

    user-provided keyword arguments here.

  • Implement _close() to clean up.

  • Implement _get_length() to provide a suitable length based on what

    the user expects. Can be inf for streaming data.

  • Implement _get_data(index) to return an array and a meta-data dict.

  • Implement _get_meta_data(index) to return a meta-data dict. If index

    is None, it should return the ‘global’ meta-data.

For writing, create a nested class that inherits from imageio.core.Format.Writer and implement the following functions:

  • Implement _open(**kwargs) to initialize the writer. Deal with the

    user-provided keyword arguments here.

  • Implement _close() to clean up.

  • Implement _append_data(im, meta) to add data (and meta-data).

  • Implement _set_meta_data(meta) to set the global meta-data.

Example / template plugin#

  1# -*- coding: utf-8 -*-
  2
  3# imageio is distributed under the terms of the (new) BSD License.
  4
  5
  6
  7"""Example plugin. You can use this as a template for your own plugin."""
  8
  9
 10
 11import numpy as np
 12
 13
 14
 15from .. import formats
 16
 17from ..core import Format
 18
 19
 20
 21
 22
 23class DummyFormat(Format):
 24
 25    """The dummy format is an example format that does nothing.
 26
 27    It will never indicate that it can read or write a file. When
 28
 29    explicitly asked to read, it will simply read the bytes. When
 30
 31    explicitly asked to write, it will raise an error.
 32
 33
 34
 35    This documentation is shown when the user does ``help('thisformat')``.
 36
 37
 38
 39    Parameters for reading
 40
 41    ----------------------
 42
 43    Specify arguments in numpy doc style here.
 44
 45
 46
 47    Parameters for saving
 48
 49    ---------------------
 50
 51    Specify arguments in numpy doc style here.
 52
 53
 54
 55    """
 56
 57
 58
 59    def _can_read(self, request):
 60
 61        # This method is called when the format manager is searching
 62
 63        # for a format to read a certain image. Return True if this format
 64
 65        # can do it.
 66
 67        #
 68
 69        # The format manager is aware of the extensions and the modes
 70
 71        # that each format can handle. It will first ask all formats
 72
 73        # that *seem* to be able to read it whether they can. If none
 74
 75        # can, it will ask the remaining formats if they can: the
 76
 77        # extension might be missing, and this allows formats to provide
 78
 79        # functionality for certain extensions, while giving preference
 80
 81        # to other plugins.
 82
 83        #
 84
 85        # If a format says it can, it should live up to it. The format
 86
 87        # would ideally check the request.firstbytes and look for a
 88
 89        # header of some kind.
 90
 91        #
 92
 93        # The request object has:
 94
 95        # request.filename: a representation of the source (only for reporting)
 96
 97        # request.firstbytes: the first 256 bytes of the file.
 98
 99        # request.mode[0]: read or write mode
100
101
102
103        if request.extension in self.extensions:
104
105            return True
106
107
108
109    def _can_write(self, request):
110
111        # This method is called when the format manager is searching
112
113        # for a format to write a certain image. It will first ask all
114
115        # formats that *seem* to be able to write it whether they can.
116
117        # If none can, it will ask the remaining formats if they can.
118
119        #
120
121        # Return True if the format can do it.
122
123
124
125        # In most cases, this code does suffice:
126
127        if request.extension in self.extensions:
128
129            return True
130
131
132
133    # -- reader
134
135
136
137    class Reader(Format.Reader):
138
139        def _open(self, some_option=False, length=1):
140
141            # Specify kwargs here. Optionally, the user-specified kwargs
142
143            # can also be accessed via the request.kwargs object.
144
145            #
146
147            # The request object provides two ways to get access to the
148
149            # data. Use just one:
150
151            #  - Use request.get_file() for a file object (preferred)
152
153            #  - Use request.get_local_filename() for a file on the system
154
155            self._fp = self.request.get_file()
156
157            self._length = length  # passed as an arg in this case for testing
158
159            self._data = None
160
161
162
163        def _close(self):
164
165            # Close the reader.
166
167            # Note that the request object will close self._fp
168
169            pass
170
171
172
173        def _get_length(self):
174
175            # Return the number of images. Can be np.inf
176
177            return self._length
178
179
180
181        def _get_data(self, index):
182
183            # Return the data and meta data for the given index
184
185            if index >= self._length:
186
187                raise IndexError("Image index %i > %i" % (index, self._length))
188
189            # Read all bytes
190
191            if self._data is None:
192
193                self._data = self._fp.read()
194
195            # Put in a numpy array
196
197            im = np.frombuffer(self._data, "uint8")
198
199            im.shape = len(im), 1
200
201            # Return array and dummy meta data
202
203            return im, {}
204
205
206
207        def _get_meta_data(self, index):
208
209            # Get the meta data for the given index. If index is None, it
210
211            # should return the global meta data.
212
213            return {}  # This format does not support meta data
214
215
216
217    # -- writer
218
219
220
221    class Writer(Format.Writer):
222
223        def _open(self, flags=0):
224
225            # Specify kwargs here. Optionally, the user-specified kwargs
226
227            # can also be accessed via the request.kwargs object.
228
229            #
230
231            # The request object provides two ways to write the data.
232
233            # Use just one:
234
235            #  - Use request.get_file() for a file object (preferred)
236
237            #  - Use request.get_local_filename() for a file on the system
238
239            self._fp = self.request.get_file()
240
241
242
243        def _close(self):
244
245            # Close the reader.
246
247            # Note that the request object will close self._fp
248
249            pass
250
251
252
253        def _append_data(self, im, meta):
254
255            # Process the given data and meta data.
256
257            raise RuntimeError("The dummy format cannot write image data.")
258
259
260
261        def set_meta_data(self, meta):
262
263            # Process the given meta data (global for all images)
264
265            # It is not mandatory to support this.
266
267            raise RuntimeError("The dummy format cannot write meta data.")
268
269
270
271
272
273# Register. You register an *instance* of a Format class. Here specify:
274
275format = DummyFormat(
276
277    "dummy",  # short name
278
279    "An example format that does nothing.",  # one line descr.
280
281    ".foobar .nonexistentext",  # list of extensions
282
283    "iI",  # modes, characters in iIvV
284
285)
286
287formats.add_format(format)