This document is a brief guide to writing new Bio-Formats file format readers.
All format readers should extend either loci.formats.FormatReader or an existing reader.
isSingleFile(java.lang.String) Whether or not the named file is expected to be the only file in the dataset. This only needs to be overridden for formats whose datasets can contain more than one file.
isThisType(loci.common.RandomAccessInputStream) Check the first few bytes of a file to determine if the file can be read by this reader. You can assume that index 0 in the stream corresponds to the index 0 in the file. Return true if the file can be read; false if not (or if there is no way of checking).
fileGroupOption(java.lang.String) Returns an indication of whether or not the files in a multi-file dataset can be handled individually. The return value should be one of the following:
This method only needs to be overridden for formats whose datasets can contain more than one file.
getSeriesUsedFiles(boolean) You only need to override this if your format uses multiple files in a single dataset. This method should return a list of all files associated with the given file name and the current series (i.e. every file needed to display the current series). If the noPixels flag is set, then none of the files returned should contain pixel data. For an example of how this works, see loci.formats.in.PerkinElmerReader. It is recommended that the first line of this method be FormatTools.assertId(currentId, true, 1) - this ensures that the file name is non-null.
openBytes(int, byte[], int, int, int, int) Returns a byte array containing the pixel data for a specified subimage from the given file. The dimensions of the subimage (upper left X coordinate, upper left Y coordinate, width, and height) are specified in the final four int parameters. This should throw a FormatException if the image number is invalid (less than 0 or >= the number of images). The ordering of the array returned by openBytes should correspond to the values returned by isLittleEndian and isInterleaved. Also, the length of the byte array should be [image width * image height * bytes per pixel]. Extra bytes will generally be truncated. It is recommended that the first line of this method be FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h) - this ensures that all of the parameters are valid.
initFile(java.lang.String) The majority of the file parsing logic should be placed in this method. The idea is to call this method once (and only once!) when the file is first opened. Generally, you will want to start by calling super.initFile(String). You will also need to set up the stream for reading the file, as well as initializing any dimension information and metadata. Most of this logic is up to you; however, you should populate the core variable (see loci.formats.CoreMetadata).
Note that each variable is initialized to 0 or null when super.initFile(String) is called. Also, super.initFile(String) constructs a Hashtable called metadata where you should store any relevant metadata.
The most common way to set up the OME-XML metadata for the reader is to initialize the MetadataStore using the makeFilterMetadata() method and populate the Pixels elements of the metadata store from the core variable using the MetadataTools.populatePixels(MetadataStore, FormatReader) method:
# Initialize the OME-XML metadata from the core variable
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this);
If the reader includes metadata at the plane level, you can initialize the Plane elements under the Pixels using MetadataTools.populatePixels(MetadataStore, FormatReader, doPlane):
MetadataTools.populatePixels(store, this, true);
Once the metadatastore has been initialized with the core properties, additional metadata can be added to it using the setter methods. Note that for each of the model components, the setObjectID() method should be called before any of the setObjectProperty() methods, e.g.:
# Add an oil immersion objective with achromat
String objectiveID = MetadataTools.createLSID("Objective", 0, 0);
store.setObjectiveID(objectiveID, 0, 0);
store.setObjectiveImmersion(getImmersion("Oil"), 0, 0);
close(boolean) Cleans up any resources used by the reader. Global variables should be reset to their initial state, and any open files or delegate readers should be closed.
Note that if the new format is a variant of a format currently supported by Bio-Formats, it is more efficient to make the new reader a subclass of the existing reader (rather than subclassing loci.formats.FormatReader). In this case, it is usually sufficient to override initFile(java.lang.String) and isThisType(byte[]).
Every reader also has an instance of loci.formats.CoreMetadata. All readers should populate the fields in CoreMetadata, which are essential to reading image planes.
If you read from a file using something other than loci.common.RandomAccessInputStream or loci.common.Location, you must use the file name returned by Location.getMappedId(String), not the file name passed to the reader. Thus, a stub for initFile(String) might look like this:
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
RandomAccessInputStream in = new RandomAccessInputStream(id);
// alternatively,
//FileInputStream in = new FileInputStream(Location.getMappedId(id));
// read basic file structure and metadata from stream
}
For more details, see loci.common.Location.mapId(java.lang.String, java.lang.String) and loci.common.Location.getMappedId(java.lang.String).
There are a number of global variables defined in loci.formats.FormatReader that should be populated in the constructor of any implemented reader.
These variables are:
Argument | Action |
---|---|
-version | print the library version and exit |
file | the image file to read |
-nopix | read metadata only, not pixels |
-nocore | do not output core metadata |
-nometa | do not parse format-specific metadata table |
-nofilter | do not filter metadata fields |
-thumbs | read thumbnails instead of normal pixels |
-minmax | compute min/max statistics |
-merge | combine separate channels into RGB image |
-nogroup | force multi-file datasets to be read as individual files |
-stitch | stitch files with similar names |
-separate | split RGB image into separate channels |
-expand | expand indexed color to RGB |
-omexml | populate OME-XML metadata |
-normalize | normalize floating point images* |
-fast | paint RGB images as quickly as possible* |
-debug | turn on debugging output |
-range | specify range of planes to read (inclusive) |
-series | specify which image series to read |
-swap | override the default input dimension order |
-shuffle | override the default output dimension order |
-map | specify file on disk to which name should be mapped |
-preload | pre-read entire file into a buffer; significantly reduces the time required to read the images, but requires more memory |
-crop | crop images before displaying; argument is ‘x,y,w,h’ |
-autoscale | used in combination with ‘-fast’ to automatically adjust brightness and contrast |
-novalid | do not perform validation of OME-XML |
-omexml-only | only output the generated OME-XML |
-format | read file with a particular reader (e.g., ZeissZVI) |
* = may result in loss of precision
If you have questions about Bio-Formats, please contact the OME team.