The reading of tiled images is straightforward and can be done in much the same way as reading a full image. In this case, to read an individual tile, we pass to the reader parameters for the x and y coordinates of the tile to read and the height and width of tile desired.
byte[] tile = reader.openBytes(image, tileX, tileY, tileSizeX, tileSizeY);
For TIFF-based readers, if the image has been written using tiles, then the tile width and height used can be found as below. These values can then be used with the above command to read the correct tiles individually.
IFD tileIFd = reader.getIFDs().get(0);
int tileHeight = tileIFd.getIFDIntValue(IFD.TILE_LENGTH);
int tileWidth = tileIFd.getIFDIntValue(IFD.TILE_WIDTH);
Tiled writing is currently supported for TIFF-based formats. To set up an image writer to use tiling the following 2 API functions are provided:
public int setTileSizeX(int tileSize) throws FormatException
public int setTileSizeY(int tileSize) throws FormatException
Each function takes in an integer parameter for the desired tile size. As not all tile sizes are supported, the image writer will round the requested value to the nearest supported tile size. The return value will contain the actual tiling size which will be used by the writer.
To find out the tiling size currently being used at any point there are 2 further API functions to get the current tile size for a writer. If tiling is not being used or is not supported then the full image height and width will be returned.
public int getTileSizeX() throws FormatException
public int getTileSizeY() throws FormatException
The tiling parameters for writers must be set after the image metadata is set. An example of initializing a writer for tiling is shown below.
// set up the writer and associate it with the output file
writer = new OMETiffWriter();
writer.setMetadataRetrieve(omexml);
writer.setInterleaved(reader.isInterleaved());
// set the tile size height and width for writing
this.tileSizeX = writer.setTileSizeX(tileSizeX);
this.tileSizeY = writer.setTileSizeY(tileSizeY);
writer.setId(outputFile);
The simplest way to write a tiled image is to set the tiling parameters on your image writer as above and have the writer automatically handle the tiling. Once the tile sizes have been set you may simply read and write your image files as normal.
// set up the writer and associate it with the output file
writer = new OMETiffWriter();
writer.setMetadataRetrieve(omexml);
writer.setInterleaved(reader.isInterleaved());
// set the tile size height and width for writing
this.tileSizeX = writer.setTileSizeX(tileSizeX);
this.tileSizeY = writer.setTileSizeY(tileSizeY);
writer.setId(outputFile);
For the most efficient reading and writing of tiles you may instead wish to read in and write out the individual image tiles one at a time.
To do this you can set up the reader and writer as in the previous example above. In this case, when setting the tile height and width used it is important to store the return values which will be the valid tile size used by the writer.
// set the tile height and width and store the actual values used by the writer
int tileSizeX = writer.setTileSizeX(tileSizeX);
int tileSizeY = writer.setTileSizeY(tileSizeY);
This time for each image you must determined the number of tiles using the actual tile height and width being used.
int width = reader.getSizeX();
int height = reader.getSizeY();
// Determined the number of tiles to read and write
int nXTiles = width / tileSizeX;
int nYTiles = height / tileSizeY;
if (nXTiles * tileSizeX != width) nXTiles++;
if (nYTiles * tileSizeY != height) nYTiles++;
Now each tile can be read and written individually.
for (int y=0; y<nYTiles; y++) {
for (int x=0; x<nXTiles; x++) {
// The x and y coordinates for the current tile
int tileX = x * tileSizeX;
int tileY = y * tileSizeY;
// Read tiles from the input file and write them to the output OME-Tiff
buf = reader.openBytes(image, tileX, tileY, tileSizeX, tileSizeY);
writer.saveBytes(image, buf, tileX, tileY, tileSizeX, tileSizeY);
}
}
It may not always be the case that the tile size divides evenly into the image height and width. In these scenarios in which the last column or row of tiles overlaps with the image boundary, a smaller tile is instead read or written for the final row or column. If a full sized tile with padding is written then a loci.formats.FormatException will be thrown for Invalid tile size.
To deal with this we can modify the previous tiled writing example to check if the tile size will overlap the image boundaries. If it will not overlap then the full tile size is used, if it does then the tile size for that tile is modified to reflect the partial tile.
// If the last tile row or column overlaps the image size then only a partial tile
// is read or written. The tile size used is adjusted to account for any overlap.
int effTileSizeX = (tileX + tileSizeX) < width ? tileSizeX : width - tileX;
int effTileSizeY = (tileY + tileSizeY) < height ? tileSizeY : height - tileY;
// Read tiles from the input file and write them to the output OME-Tiff
buf = reader.openBytes(image, tileX, tileY, effTileSizeX, effTileSizeY);
writer.saveBytes(image, buf, tileX, tileY, effTileSizeX, effTileSizeY);