/* * #%L * OME-FILES C++ library for image IO. * Copyright © 2015–2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using boost::filesystem::path; using std::make_shared; using std::shared_ptr; using std::static_pointer_cast; using ome::files::createID; using ome::files::dimension_size_type; using ome::files::fillMetadata; using ome::files::CoreMetadata; using ome::files::DIM_SPATIAL_X; using ome::files::DIM_SPATIAL_Y; using ome::files::DIM_CHANNEL; using ome::files::FormatWriter; using ome::files::MetadataMap; using ome::files::out::OMETIFFWriter; using ome::files::PixelBuffer; using ome::files::PixelBufferBase; using ome::files::PixelProperties; using ome::files::VariantPixelBuffer; using ome::xml::model::enums::Binning; using ome::xml::model::enums::Immersion; using ome::xml::model::enums::PixelType; using ome::xml::model::enums::DetectorType; using ome::xml::model::enums::DimensionOrder; using ome::xml::model::enums::UnitsLength; using ome::xml::model::primitives::Quantity; using ome::xml::meta::MetadataRetrieve; using ome::xml::meta::MetadataStore; using ome::xml::meta::Metadata; using ome::xml::meta::OMEXMLMetadata; namespace { shared_ptr createMetadata() { /* create-metadata-start */ // OME-XML metadata store. auto meta = make_shared(); // 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> seriesList; shared_ptr core(make_shared()); core->sizeX = 512U; core->sizeY = 512U; core->sizeC.clear(); // defaults to 1 channel with 1 subchannel; clear this core->sizeC.push_back(1); core->sizeC.push_back(1); core->sizeC.push_back(1); core->pixelType = PixelType::UINT16; core->interleaved = false; core->bitsPerPixel = 12U; core->dimensionOrder = DimensionOrder::XYZTC; seriesList.push_back(core); seriesList.push_back(core); // add two identical series fillMetadata(*meta, seriesList); /* create-metadata-end */ return meta; } void addExtendedMetadata(shared_ptr store) { /* extended-metadata-start */ // Get root OME object std::shared_ptr root(std::dynamic_pointer_cast (store->getRoot())); if (!root) return; MetadataStore::index_type annotation_idx = 0; // Create an Instrument. auto instrument = make_shared(); instrument->setID(createID("Instrument", 0)); root->addInstrument(instrument); // Create an Objective for this Instrument. auto objective = make_shared(); objective->setID(createID("Objective", 0)); auto objective_manufacturer = std::make_shared("InterFocal"); objective->setManufacturer(objective_manufacturer); auto objective_nominal_mag = std::make_shared(40.0); objective->setNominalMagnification(objective_nominal_mag); auto objective_na = std::make_shared(0.4); objective->setLensNA(objective_na); auto objective_immersion = std::make_shared(Immersion::OIL); objective->setImmersion(objective_immersion); auto objective_wd = std::make_shared> (0.34, UnitsLength::MILLIMETER); objective->setWorkingDistance(objective_wd); instrument->addObjective(objective); // Create a Detector for this Instrument. auto detector = make_shared(); std::string detector_id = createID("Detector", 0); detector->setID(detector_id); auto detector_manufacturer = std::make_shared("MegaCapture"); detector->setManufacturer(detector_manufacturer); auto detector_type = std::make_shared(DetectorType::CCD); detector->setType(detector_type); instrument->addDetector(detector); // Get Image and Channel for future use. Note for your own code, // you should check that the elements exist before accessing them; // here we know they are valid because we created them above. auto image = root->getImage(0); auto pixels = image->getPixels(); auto channel0 = pixels->getChannel(0); auto channel1 = pixels->getChannel(1); auto channel2 = pixels->getChannel(2); // Create Settings for this Detector for each Channel on the Image. auto detector_settings0 = make_shared(); { detector_settings0->setID(detector_id); auto detector_binning = std::make_shared(Binning::TWOBYTWO); detector_settings0->setBinning(detector_binning); auto detector_gain = std::make_shared(83.81); detector_settings0->setGain(detector_gain); channel0->setDetectorSettings(detector_settings0); } auto detector_settings1 = make_shared(); { detector_settings1->setID(detector_id); auto detector_binning = std::make_shared(Binning::TWOBYTWO); detector_settings1->setBinning(detector_binning); auto detector_gain = std::make_shared(56.89); detector_settings1->setGain(detector_gain); channel1->setDetectorSettings(detector_settings1); } auto detector_settings2 = make_shared(); { detector_settings2->setID(detector_id); auto detector_binning = std::make_shared(Binning::FOURBYFOUR); detector_settings2->setBinning(detector_binning); auto detector_gain = std::make_shared(12.93); detector_settings2->setGain(detector_gain); channel2->setDetectorSettings(detector_settings2); } /* extended-metadata-end */ /* annotations-start */ // Add Structured Annotations. auto sa = std::make_shared(); root->setStructuredAnnotations(sa); // Create a MapAnnotation. auto map_ann0 = std::make_shared(); std::string annotation_id = createID("Annotation", annotation_idx); map_ann0->setID(annotation_id); auto map_ann0_ns = std::make_shared ("https://microscopy.example.com/colour-balance"); map_ann0->setNamespace(map_ann0_ns); ome::xml::model::primitives::OrderedMultimap map; map.push_back({"white-balance", "5,15,8"}); map.push_back({"black-balance", "112,140,126"}); map_ann0->setValue(map); sa->addMapAnnotation(map_ann0); // Link MapAnnotation to Detector. detector->linkAnnotation(map_ann0); // Create a LongAnnotation. auto long_ann0 = std::make_shared(); ++annotation_idx; annotation_id = createID("Annotation", annotation_idx); long_ann0->setID(annotation_id); auto long_ann0_ns = std::make_shared ("https://microscopy.example.com/trigger-delay"); long_ann0->setNamespace(long_ann0_ns); long_ann0->setValue(239423); sa->addLongAnnotation(long_ann0); // Link LongAnnotation to Image. image->linkAnnotation(long_ann0); // Create a second LongAnnotation. auto long_ann1 = std::make_shared(); ++annotation_idx; annotation_id = createID("Annotation", annotation_idx); long_ann1->setID(annotation_id); auto long_ann1_ns = std::make_shared ("https://microscopy.example.com/sample-number"); long_ann1->setNamespace(long_ann1_ns); long_ann1->setValue(934223); sa->addLongAnnotation(long_ann1); // Link second LongAnnotation to Image. image->linkAnnotation(long_ann1); /* annotations-end */ } void writePixelData(FormatWriter& writer, std::ostream& stream) { /* pixel-example-start */ // Total number of images (series) dimension_size_type ic = writer.getMetadataRetrieve()->getImageCount(); 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 = 1U; pc *= writer.getMetadataRetrieve()->getPixelsSizeZ(i); pc *= writer.getMetadataRetrieve()->getPixelsSizeT(i); pc *= writer.getMetadataRetrieve()->getChannelCount(i); stream << "\tPlane count: " << pc << '\n'; // Loop over planes (for this image index) for (dimension_size_type p = 0 ; p < pc; ++p) { // Change the current plane to this index. writer.setPlane(p); // Pixel buffer; size 512 × 512 with 3 channels of type // uint16_t. It uses the native endianness and has a // storage order of XYZTC without interleaving // (subchannels are planar). shared_ptr::std_type>> buffer(make_shared::std_type>> (boost::extents[512][512][1][1][1][1][1][1][1], PixelType::UINT16, ome::files::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_CHANNEL] = 0; switch(p) { case 0: buffer->at(idx) = (static_cast(x) / 512.0f) * 4096.0f; break; case 1: buffer->at(idx) = (static_cast(y) / 512.0f) * 4096.0f; break; case 2: buffer->at(idx) = (static_cast(x+y) / 1024.0f) * 4096.0f; break; default: break; } } 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[]) { // This is the default, but needs setting manually on Windows. ome::common::setLogLevel(ome::logging::trivial::warning); try { if (argc > 1) { // Portable path path filename(argv[1]); /* writer-example-start */ // Create metadata for the file to be written. auto meta = createMetadata(); // Add extended metadata. addExtendedMetadata(meta); // Create TIFF writer auto writer = make_shared(); // Set writer options before opening a file auto retrieve = static_pointer_cast(meta); writer->setMetadataRetrieve(retrieve); 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.ome.tiff\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); } }