We're Hiring!

[C++ API] Creating OME-TIFF with multiple series

Historical discussions about the Bio-Formats library. Please look for and ask new questions at https://forum.image.sc/tags/bio-formats
Please note:
Historical discussions about the Bio-Formats library. Please look for and ask new questions at https://forum.image.sc/tags/bio-formats

If you are having trouble with image files, there is information about reporting bugs in the Bio-Formats documentation. Please send us the data and let us know what version of Bio-Formats you are using. For issues with your code, please provide a link to a public repository, ideally GitHub.

[C++ API] Creating OME-TIFF with multiple series

Postby thomas_braun » Wed Feb 03, 2016 12:34 pm

Hi,

I'm a OME-TIFF beginner. And I want to create a OME-TIFF with multiple images, these images don't form something like planes, so I thought I use series for that.

I adapted the shipped example `metadata-formatwriter.org` but always get a debug assertion deep down in the library guts:

Code: Select all
Image count: 2
        Plane count: 1
PixelBuffer PixelType is uint16
VariantPixelBuffer PixelType is uint16
Wrote 786432 uint16 pixels
        Plane count: 1
PixelBuffer PixelType is uint16
VariantPixelBuffer PixelType is uint16
Wrote 786432 uint16 pixels
Caught exception: vector::_M_range_check: __n (which is 1) >= this->size() (which is 0)



The adapted example code is attached, my changes were:

Code: Select all
$diff -Nur metadata-formatwriter-orig.cpp metadata-formatwriter.cpp
--- metadata-formatwriter-orig.cpp      2016-01-31 00:18:14.795891269 +0100
+++ metadata-formatwriter.cpp   2016-02-03 13:25:59.402170072 +0100
@@ -91,6 +91,7 @@
     core->bitsPerPixel = 12U;
     core->dimensionOrder = DimensionOrder::XYZTC;
     seriesList.push_back(core);
+    seriesList.push_back(core);

     fillMetadata(*meta, seriesList);

@@ -104,7 +105,7 @@
                  std::ostream& stream)
   {
     // Total number of images (series)
-    dimension_size_type ic = 1;
+    dimension_size_type ic = 2;
     stream << "Image count: " << ic << '\n';

     // Loop over images


Did I understand the documentation right?

I'm using bioformats 5.1.7 with a C++11 compiler.

Thanks in advance,
Thomas

Here is the full code of the example:

Code: Select all
/*
* #%L
* OME-BIOFORMATS C++ library for image IO.
* Copyright © 2015 Open Microscopy Environment:
*   - Massachusetts Institute of Technology
*   - National Institutes of Health
*   - University of Dundee
*   - Board of Regents of the University of Wisconsin-Madison
*   - Glencoe Software, Inc.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing official
* policies, either expressed or implied, of any organization.
* #L%
*/

#include <iostream>

#include <ome/bioformats/CoreMetadata.h>
#include <ome/bioformats/MetadataTools.h>
#include <ome/bioformats/VariantPixelBuffer.h>
#include <ome/bioformats/out/OMETIFFWriter.h>
#include <ome/xml/meta/OMEXMLMetadata.h>

#include <ome/compat/memory.h>

#include <ome/common/filesystem.h>

using boost::filesystem::path;
using ome::compat::make_shared;
using ome::compat::shared_ptr;
using ome::bioformats::dimension_size_type;
using ome::bioformats::fillMetadata;
using ome::bioformats::CoreMetadata;
using ome::bioformats::DIM_SPATIAL_X;
using ome::bioformats::DIM_SPATIAL_Y;
using ome::bioformats::DIM_SUBCHANNEL;
using ome::bioformats::FormatWriter;
using ome::bioformats::MetadataMap;
using ome::bioformats::out::OMETIFFWriter;
using ome::bioformats::PixelBuffer;
using ome::bioformats::PixelBufferBase;
using ome::bioformats::PixelProperties;
using ome::bioformats::VariantPixelBuffer;
using ome::xml::model::enums::PixelType;
using ome::xml::model::enums::DimensionOrder;

namespace
{

  /* write-example-start */
  shared_ptr< ::ome::xml::meta::OMEXMLMetadata>
  createMetadata()
  {
    // OME-XML metadata store.
    shared_ptr< ::ome::xml::meta::OMEXMLMetadata> meta(make_shared< ::ome::xml::meta::OMEXMLMetadata>());

    // Create simple CoreMetadata and use this to set up the OME-XML
    // metadata.  This is purely for convenience in this example; a
    // real writer would typically set up the OME-XML metadata from an
    // existing MetadataRetrieve instance or by hand.
    std::vector<shared_ptr<CoreMetadata> > seriesList;
    shared_ptr<CoreMetadata> core(make_shared<CoreMetadata>());
    core->sizeX = 512U;
    core->sizeY = 512U;
    core->sizeC.clear(); // defaults to 1 channel with 1 subchannel; clear this
    core->sizeC.push_back(3U); // replace with single RGB channel
    core->pixelType = ome::xml::model::enums::PixelType::UINT16;
    core->interleaved = false;
    core->bitsPerPixel = 12U;
    core->dimensionOrder = DimensionOrder::XYZTC;
    seriesList.push_back(core);
    seriesList.push_back(core);

    fillMetadata(*meta, seriesList);

    return meta;
  }
  /* write-example-end */

  /* pixel-example-start */
  void
  writePixelData(FormatWriter& writer,
                 std::ostream& stream)
  {
    // Total number of images (series)
    dimension_size_type ic = 2;
    stream << "Image count: " << ic << '\n';

    // Loop over images
    for (dimension_size_type i = 0 ; i < ic; ++i)
      {
        // Change the current series to this index
        writer.setSeries(i);

        // Total number of planes.
        dimension_size_type pc = 1;
        stream << "\tPlane count: " << pc << '\n';

        // Loop over planes (for this image index)
        for (dimension_size_type p = 0 ; p < pc; ++p)
          {
            // Pixel buffer; size 512 × 512 with 3 subchannels of type
            // uint16_t.  It uses the native endianness and has a
            // storage order of XYZTC without interleaving
            // (subchannels are planar).
            shared_ptr<PixelBuffer<PixelProperties<PixelType::UINT16>::std_type> >
              buffer(make_shared<PixelBuffer<PixelProperties<PixelType::UINT16>::std_type> >
                     (boost::extents[512][512][1][1][1][3][1][1][1],
                      PixelType::UINT16, ome::bioformats::ENDIAN_NATIVE,
                      PixelBufferBase::make_storage_order(DimensionOrder::XYZTC, false)));

            // Fill each subchannel with a different intensity ramp in
            // the 12-bit range.  In a real program, the pixel data
            // would typically be obtained from data acquisition or
            // another image.
            for (dimension_size_type x = 0; x < 512; ++x)
              for (dimension_size_type y = 0; y < 512; ++y)
                {
                  PixelBufferBase::indices_type idx;
                  std::fill(idx.begin(), idx.end(), 0);
                  idx[DIM_SPATIAL_X] = x;
                  idx[DIM_SPATIAL_Y] = y;

                  idx[DIM_SUBCHANNEL] = 0;
                  buffer->at(idx) = (static_cast<float>(x) / 512.0f) * 4096.0f;
                  idx[DIM_SUBCHANNEL] = 1;
                  buffer->at(idx) = (static_cast<float>(y) / 512.0f) * 4096.0f;
                  idx[DIM_SUBCHANNEL] = 2;
                  buffer->at(idx) = (static_cast<float>(x+y) / 1024.0f) * 4096.0f;
                }

            VariantPixelBuffer vbuffer(buffer);
            stream << "PixelBuffer PixelType is " << buffer->pixelType() << '\n';
            stream << "VariantPixelBuffer PixelType is " << vbuffer.pixelType() << '\n';
            stream << std::flush;

            // Write the the entire pixel buffer to the plane.
            writer.saveBytes(p, vbuffer);

            stream << "Wrote " << buffer->num_elements() << ' ' << buffer->pixelType() << " pixels\n";
          }
      }
  }
  /* pixel-example-end */

}

int
main(int argc, char *argv[])
{
  try
    {
      if (argc > 1)
        {
          // Portable path
          path filename(argv[1]);

          /* writer-example-start */
          // Create metadata for the file to be written.
          shared_ptr< ::ome::xml::meta::MetadataRetrieve> meta(createMetadata());

          // Create TIFF writer
          shared_ptr<FormatWriter> writer(make_shared<OMETIFFWriter>());

          // Set writer options before opening a file
          writer->setMetadataRetrieve(meta);
          writer->setInterleaved(false);

          // Open the file
          writer->setId(filename);

          // Write pixel data
          writePixelData(*writer, std::cout);

          // Explicitly close writer
          writer->close();
          /* writer-example-end */
        }
      else
        {
          std::cerr << "Usage: " << argv[0] << " ome-xml.xml\n";
          std::exit(1);
        }
    }
  catch (const std::exception& e)
    {
      std::cerr << "Caught exception: " << e.what() << '\n';
      std::exit(1);
    }
  catch (...)
    {
      std::cerr << "Caught unknown exception\n";
      std::exit(1);
    }
}
thomas_braun
 
Posts: 7
Joined: Tue Feb 02, 2016 7:38 pm

Re: [C++ API] Creating OME-TIFF with multiple series

Postby rleigh » Wed Feb 03, 2016 3:31 pm

Dear Thomas,

I'm sorry you encountered a problem when trying to use the library. I've investigated this and was able to reproduce your problem and get a backtrace with gdb. I filed a ticket in our trac system (https://trac.openmicroscopy.org/ome/ticket/13154) and you should have been CCd on the ticket.

I will look into this in more detail to determine where and why the failure is occuring and will respond to the ticket once I have some more information and/or a fix.


Kind regards,
Roger Leigh
User avatar
rleigh
 
Posts: 217
Joined: Tue Mar 13, 2012 11:45 am

Re: [C++ API] Creating OME-TIFF with multiple series

Postby rleigh » Thu Feb 04, 2016 1:38 pm

Dear Thomas,

I have found and fixed the underlying issue with the metadata (UUID setting) which caused the exception. This is in https://github.com/openmicroscopy/bioformats/pull/2231 (against 5.1.7, which you were using) and https://github.com/openmicroscopy/bioformats/pull/2230 (our current development release). These will now be reviewed and tested before merging; the former should hopefully make it into the 5.1.8 release, expected sometime next week. In the meantime, the commits can be used to patch your copy of the source to get things working if you wanted to fetch them.

The C++ port of the Bio-Formats API for writing OME-TIFF is still quite new, and we don't yet have much in the way of usage data to focus our development efforts. If you're willing to share it, it would be very useful to know which operating system and compiler version you are using, and any which you plan to use, so that we can use this information to ensure we have sufficient testing in place for the platforms and compilers which are in active use.

It would also be useful if you have any information about your C++11/14 requirements; we have some provisional branches which switch the codebase from C++98 to C++11, but we haven't yet got a timeline for integrating these--this in large part depends upon the operating systems and platforms in active use which is one reason it would be useful to know what you are using.

It would also be useful to know how you obtained and built the code, so that we can also prioritise our support for various methods. Currently we have
- git repository and release tags
- the release source archive
- superbuild which builds the needed dependencies and then the release from the source archive (or git) (also available as a source archive or as a git repository)

For the upcoming 5.2.x releases over the next few months, we'll be splitting out the C++ codebase from the bioformats git repository into a small number of dedicated repositories; this might potentially include some API changes, though this won't happen immediately. I'm mentioning this in case you were also looking at the develop branch as well as 5.1.7.


Regards,
Roger
User avatar
rleigh
 
Posts: 217
Joined: Tue Mar 13, 2012 11:45 am

Re: [C++ API] Creating OME-TIFF with multiple series

Postby thomas_braun » Fri Feb 05, 2016 12:22 pm

Dear Roger,

many thanks for the thorough investigation and fixes. (I have been CC'ed on the trac ticket)

Here are some of my development facts:
- Compiled from zip package, not the superbuild
- Developing on debian jessie, target system is ubuntu LTS
- Compiled with
Code: Select all
cmake -DCMAKE_BUILD_TYPE=Release CMAKE_CXX_COMPILER=g++-4.9 -Dcxxstd-autodetect=ON ..

- I'm using C++11, C++14 not yet
- Now thinking about it I actually should have preferred a 5.1.x maintenance branch via git for getting the source code. Would that be https://github.com/openmicroscopy/biofo ... ee/dev_5_1?

For the upcoming 5.2.x releases over the next few months, we'll be splitting out the C++ codebase from the bioformats git repository into a small number of dedicated repositories; this might potentially include some API changes, though this won't happen immediately. I'm mentioning this in case you were also looking at the develop branch as well as 5.1.7.


I will be sticking to 5.1.x for the forseeable future.

Many thanks again,
Thomas
thomas_braun
 
Posts: 7
Joined: Tue Feb 02, 2016 7:38 pm

Re: [C++ API] Creating OME-TIFF with multiple series

Postby rleigh » Mon Feb 08, 2016 3:25 pm

Thanks for the information about the system you're developing on, it's really useful to know this.

Regarding git, yes the dev_5_1 branch is the correct one if you want to use it; the pull request mentioned in this thread has now been merged there. Or you can use the v5.1.8 release tag which will appear when we make the release in a few days.


Kind regards,
Roger
User avatar
rleigh
 
Posts: 217
Joined: Tue Mar 13, 2012 11:45 am


Return to User Discussion [Legacy]

Who is online

Users browsing this forum: Google [Bot] and 1 guest