[ome-devel] [ome-files-cpp] Cannot Write Multiple ROIs to Image Plane Using SaveBytes

Dennis Ai dennis.ai at sri.com
Thu Jun 28 22:55:19 BST 2018


Hi Roger,

If I understand what you are saying, it is that if you only write a ROI of
the entire plane, it will write that ROI to the directory and won't set all
the proper tile offsets and sizes in the directory, correct?  I am thinking
a workaround could be to simply write zeroed data to the entire plane, and
then during image acquisition, write the first tile to multiple planes, then
the 2nd tile, and so on.  I've pasted a C# example using BitMiracle's
LibTIFF
wrapper, which works fine when I tested it.

However, when I tried to the same thing using OMETIFFWriter, it demonstrates
the strange behavior that I described in my previous email.  Doing some more
debugging into it (see my C# Example with OME Files code example below):

1. After Writing Zero to All Planes: I have a TIFF with two directories
(no_rois.ome.tiff, attached).
2. After Writing the First ROI to All Planes: I have a TIFF with three
directories (first_roi.ome.tiff, attached).  The first directory looks like
it's all zeroes, and the other two look like they have the first ROI
written.
3. After Writing the Second ROI to All Planes: I have a TIFF with five
directories (first_second_roi.ome.tiff, attached).  The first directory
looks like it's all zeros, the second directory and third directories have
the first ROI written, and the 4th and 5th directories have what I believe
is the first ROI, except now instead of being the first tiles in the
directory, they are written at the 7th tile in the directory.
4. After Writing the Third ROI to All Planes: I have a TIFF with seven
directories (first_second_third_roi.ome.tiff, attached).  The first
directory looks like it's all zeros, the second and third directories have
the first ROI written, the 4th and 5th directories have the first ROI
written to the 7th tiles in their respective directories, and the 6th and
7th directories have the first ROI written to the 14th tile in the
directory.

This seems to be very strange behavior.  I would expect writing ROIs to not
create new directories, and simply update existing tiles in existing
directories.

C# Example with OME Files

            OMETIFFWriter writer = new OMETIFFWriter();
            OMEXMLMetadata metadata = new OMEXMLMetadata();
            CoreMetadata coreMetadata = new CoreMetadata();
            PixelBuffer pixelBuffer =
                new PixelBuffer(439, 167, 1, 1, 1, "int8", "XYZTC");

            coreMetadata.SizeX = 439;
            coreMetadata.SizeY = 167;
            coreMetadata.SizeZ = 2;
            coreMetadata.SizeT = 1;
            coreMetadata.SizeC = 1;
            coreMetadata.PixelType = "int8";
            coreMetadata.BitsPerPixel = 8;

            metadata.SetCoreMetadata(new CoreMetadata[] { coreMetadata });
            writer.FileMetadata = metadata;
            writer.CompressionType = "Deflate";
            writer.TileSizeX = 64;
            writer.TileSizeY = 64;
            
            writer.Open(TestTIFFFilePath);
            
            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, pixelBuffer);
            }

            PixelBuffer strip1 =
                new PixelBuffer(256, 64, 1, 1, 1, "int8", "XYZTC");
            PixelBuffer stripEnd1 =
                new PixelBuffer(256, 39, 1, 1, 1, "int8", "XYZTC");
            PixelBuffer strip2 =
                new PixelBuffer(183, 64, 1, 1, 1, "int8", "XYZTC");
            PixelBuffer stripEnd2 =
                new PixelBuffer(183, 39, 1, 1, 1, "int8", "XYZTC");

            // Pixel buffer is copied to ROIs on the writer's plane.
            CopyGradientToPixelBuffer(strip1);
            CopyGradientToPixelBuffer(stripEnd1);
            CopyGradientToPixelBuffer(strip2);
            CopyGradientToPixelBuffer(stripEnd2);
            writer.Series = 0;
            
            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, strip1, 0, 0, 256, 64);
            }

            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, strip1, 0, 64, 256, 64);
            }

            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, stripEnd1, 0, 128, 256, 39);
            }

            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, strip2, 256, 0, 183, 64);
            }

            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, strip2, 256, 64, 183, 64);
            }

            for (int i = 0; i < writer.PlaneCount; i++)
            {
                writer.SaveBytes(i, stripEnd2, 256, 128, 183, 39);
            }

            writer.Close();

C# Example with BitMiracle LibTIFF

            string filePath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.tiff");
            Tiff tiff = Tiff.Open(filePath, "w");
            byte[] buffer = Enumerable.Range(0, 128 * 128).Select(i =>
(byte)(i % 255)).ToArray();
            byte[] zero = Enumerable.Repeat((byte)0, 128 * 128).ToArray();
            
            for (short i = 0; i < 2; i++)
            {
                tiff.SetField(TiffTag.IMAGEWIDTH, 256);
                tiff.SetField(TiffTag.IMAGELENGTH, 256);
                tiff.SetField(TiffTag.TILEWIDTH, 128);
                tiff.SetField(TiffTag.TILELENGTH, 128);
                tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1);
                tiff.SetField(TiffTag.BITSPERSAMPLE, 8);
                tiff.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT);
                tiff.SetField(TiffTag.COMPRESSION, Compression.NONE);
                tiff.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISBLACK);
                tiff.SetField(TiffTag.COMPRESSION, Compression.NONE);
                tiff.SetField(TiffTag.FILLORDER, FillOrder.MSB2LSB);
                tiff.SetField(TiffTag.SAMPLEFORMAT, SampleFormat.UINT);
                tiff.SetField(TiffTag.SUBFILETYPE, FileType.PAGE);
                tiff.SetField(TiffTag.PAGENUMBER, i, 2);

                tiff.WriteRawTile(0, zero, buffer.Length);
                tiff.WriteRawTile(1, zero, buffer.Length);
                tiff.WriteRawTile(2, zero, buffer.Length);
                tiff.WriteRawTile(3, zero, buffer.Length);

                tiff.WriteDirectory();
                Console.WriteLine(tiff.CurrentDirectory());
            }
            
            for (short i = 0; i < 2; i++)
            {
                tiff.SetDirectory(i);
                tiff.WriteRawTile(0, buffer, buffer.Length);
                tiff.WriteDirectory();
            }

            for (short i = 0; i < 2; i++)
            {
                tiff.SetDirectory(i);
                tiff.WriteRawTile(1, buffer, buffer.Length);
                tiff.WriteDirectory();
            }

            for (short i = 0; i < 2; i++)
            {
                tiff.SetDirectory(i);
                tiff.WriteRawTile(2, buffer, buffer.Length);
                tiff.WriteDirectory();
            }

            for (short i = 0; i < 2; i++)
            {
                tiff.SetDirectory(i);
                tiff.WriteRawTile(3, buffer, buffer.Length);
                tiff.WriteDirectory();
            }

> --------
> |C|D|
> --------
> 
> Plane 1
> --------
> |A|B|
> --------
> |C|D|
> --------
> 
> If I write tiles A, B, C, D to plane 0, then A, B, C, D to plane 1, it 
> works correctly (see "one_plane_at_a_time.ome.tiff").  However, If I 
> write tile A to plane 0 and then plane 1, followed by tile B to plane 
> 0 and then plane 1, followed by tile C to plane 0 and then something 
> completely bizarre happens where only one of the ROIs get written 
> properly (one_tile_to_multiple_planes.ome.tiff).
> 
> Any idea why this might be happening?

This comes down to the XY plane for each (Z, T, C) combination having a
separate TIFF directory for that plane.  It's a requirement that each plane
be written completely before moving to the next; the plane order is set by
the DimensionOrder enumeration, and it is expected that ordering be
followed.

What I think is happening is that when you switch to writing out a different
plane, it's flushing the current partially written IFD, and moving onto the
next, which leaves some tiles unwritten.

Unless libtiff allows revisiting of directories and updating of the tile
offsets and sizes in place, this limitation is quite difficult to work
around.  However, should it be possible we can certainly look into updating
the TIFF writers to permit this.

One option would be to use TIFFRewriteDirectory().  However, this duplicates
the entire IFD including all tile offsets and sizes.  This can end up being
very costly--the metadata could occupy more space than the pixel data
depending upon the write pattern.  For images with tens of thousands of
tiles, this gets even more costly.

In the interim, we could certainly document these requirements more clearly,
and we could also throw an exception if writing out of the required order.

In the longer term, supporting HDF-based, rather than TIFF-based containers
would allow these constraints to be completely relaxed since it supports
arbitrary updates to the underlying chunked storage. 
Similar improvements might also be possible with other container formats.


Regards,
Roger
_______________________________________________
ome-devel mailing list
ome-devel at lists.openmicroscopy.org.uk
https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.openmi
croscopy.org.uk%2Fmailman%2Flistinfo%2Fome-devel&data=01%7C01%7Cdennis.ai%40
sri.com%7Cee9f2c848a154ca2d44208d5dcf7389a%7C40779d3379c44626b8bf140c4d5e907
5%7C1&sdata=Rii862E83ezbHSBzlAnoc9Ze%2Fl3jmjigxg31mcu8njQ%3D&reserved=0
-------------- next part --------------
A non-text attachment was scrubbed...
Name: first_roi.ome.tiff
Type: image/tiff
Size: 6337 bytes
Desc: not available
URL: <http://lists.openmicroscopy.org.uk/pipermail/ome-devel/attachments/20180628/caa6ef83/attachment.tiff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: no_rois.ome.tiff
Type: image/tiff
Size: 3751 bytes
Desc: not available
URL: <http://lists.openmicroscopy.org.uk/pipermail/ome-devel/attachments/20180628/caa6ef83/attachment-0001.tiff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: first_second_roi.ome.tiff
Type: image/tiff
Size: 9557 bytes
Desc: not available
URL: <http://lists.openmicroscopy.org.uk/pipermail/ome-devel/attachments/20180628/caa6ef83/attachment-0002.tiff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: first_second_third_roi.ome.tiff
Type: image/tiff
Size: 12329 bytes
Desc: not available
URL: <http://lists.openmicroscopy.org.uk/pipermail/ome-devel/attachments/20180628/caa6ef83/attachment-0003.tiff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 5124 bytes
Desc: not available
URL: <http://lists.openmicroscopy.org.uk/pipermail/ome-devel/attachments/20180628/caa6ef83/attachment.p7s>


More information about the ome-devel mailing list