/* * Copyright (C) 2015 Patryk Strach * * This file is part of Virtual Slide Viewer. * * Virtual Slide Viewer is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * Virtual Slide Viewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with Virtual Slide Viewer. * If not, see <http://www.gnu.org/licenses/>. */ package virtualslideviewer.bioformats; import java.util.ArrayList; import java.util.List; import java.util.UUID; import loci.formats.MetadataTools; import loci.formats.meta.IMetadata; import loci.formats.meta.MetadataConverter; import ome.xml.meta.OMEXMLMetadataRoot; import ome.xml.model.*; import ome.xml.model.enums.DimensionOrder; import ome.xml.model.enums.PixelType; import ome.xml.model.primitives.PositiveInteger; import virtualslideviewer.core.VirtualSlide; import virtualslideviewer.core.VirtualSlideImage; /** * A class whose responsibility is to construct valid OME-XML metadata for given image. */ public class MetadataConstructor { public final static String LOWER_RESOLUTION_IMAGE_TAG_VALUE = "LOWER_RESOLUTION"; /** * Constructs OME-XML metadata for a virtual slide. * * If the slide already contains OME-XML metadata, any metadata entries unused by the application are preserved in their original form. */ public static IMetadata constructMetadata(VirtualSlide slide) { IMetadata metadata = MetadataTools.createOMEXMLMetadata(); IMetadata sourceMetadata = retrieveSourceMetadata(slide); if(sourceMetadata != null) { MetadataConverter.convertMetadata(sourceMetadata, metadata); } String lowerResolutionTagID = addLowerResolutionTag(metadata); int seriesIndex = 0; for(VirtualSlideImage image : slide.getImageList()) { for(int resIndex = image.getResolutionCount() - 1; resIndex >= 0 ;--resIndex) { copyImageMetadata(sourceMetadata, image, metadata, seriesIndex); metadata.setUUID(UUID.randomUUID().toString()); metadata.setImageName(image.getName(), seriesIndex); metadata.setImageID(MetadataTools.createLSID("Image", seriesIndex), seriesIndex); metadata.setPixelsID(MetadataTools.createLSID("Pixels", seriesIndex), seriesIndex); metadata.setPixelsBigEndian(false, seriesIndex); metadata.setPixelsInterleaved(true, seriesIndex); metadata.setPixelsSignificantBits(new PositiveInteger(8), seriesIndex); metadata.setPixelsType(PixelType.UINT8, seriesIndex); metadata.setPixelsDimensionOrder(DimensionOrder.XYTZC, seriesIndex); metadata.setPixelsSizeX(new PositiveInteger(image.getImageSize(resIndex).width), seriesIndex); metadata.setPixelsSizeY(new PositiveInteger(image.getImageSize(resIndex).height), seriesIndex); metadata.setPixelsSizeC(new PositiveInteger(image.getChannelCount() * (image.isRGB() ? 3 : 1)), seriesIndex); metadata.setPixelsSizeZ(new PositiveInteger(image.getZPlaneCount()), seriesIndex); metadata.setPixelsSizeT(new PositiveInteger(image.getTimePointCount()), seriesIndex); for(int channelIndex = 0; channelIndex < image.getChannelCount() ;++channelIndex) { metadata.setChannelID(MetadataTools.createLSID("Channel", seriesIndex, channelIndex), seriesIndex, channelIndex); metadata.setChannelSamplesPerPixel(new PositiveInteger(image.isRGB() ? 3 : 1), seriesIndex, channelIndex); } if(resIndex != image.getResolutionCount() - 1) { int resLevel = image.getResolutionCount() - 1 - resIndex; metadata.setImageName(String.format("%s res level %d", image.getName(), resLevel), seriesIndex); metadata.setImageAnnotationRef(lowerResolutionTagID, seriesIndex, metadata.getImageAnnotationRefCount(seriesIndex) + 1); } seriesIndex++; } } removeEntriesForRemovedImages(metadata, seriesIndex); return metadata; } private static IMetadata retrieveSourceMetadata(VirtualSlide sourceSlide) { if(sourceSlide instanceof BioformatsVirtualSlide) { return ((BioformatsVirtualSlide)sourceSlide).getBioformatsMetadata(); } return null; } /** * @return The Tag ID. */ private static String addLowerResolutionTag(IMetadata metadata) { int lowerResolutionTagIndex = getLowerResolutionTagIndex(metadata); if(lowerResolutionTagIndex == -1) { lowerResolutionTagIndex = getTagAnnocationCount(metadata); } metadata.setTagAnnotationID(MetadataTools.createLSID("TagAnnotation", lowerResolutionTagIndex), lowerResolutionTagIndex); metadata.setTagAnnotationValue(LOWER_RESOLUTION_IMAGE_TAG_VALUE, lowerResolutionTagIndex); metadata.setTagAnnotationDescription("Indicates that an image referencing this tag is a lower resolution version of a previous one." , lowerResolutionTagIndex); return metadata.getTagAnnotationID(lowerResolutionTagIndex); } /** * Copies metadata of an image. * * Required to preserve original image metadata in the case of image reordering. */ private static void copyImageMetadata(IMetadata sourceMetadata, VirtualSlideImage source, IMetadata destinationMetadata, int destinationIndex) { if(!(source instanceof BioformatsVirtualSlideImage)) return; OMEXMLMetadataRoot sourceMetadataRoot = (OMEXMLMetadataRoot)sourceMetadata.getRoot(); Image sourceImage = sourceMetadataRoot.getImage(((BioformatsVirtualSlideImage)source).getSeriesIndex()); Image destinationImage = new Image(); destinationImage.setAcquisitionDate(sourceImage.getAcquisitionDate()); destinationImage.setDescription(sourceImage.getDescription()); destinationImage.setID(sourceImage.getID()); destinationImage.setName(sourceImage.getName()); if(sourceImage.getImagingEnvironment() != null) { destinationImage.setImagingEnvironment(new ImagingEnvironment(sourceImage.getImagingEnvironment())); } if(sourceImage.getObjectiveSettings() != null) { destinationImage.setObjectiveSettings(new ObjectiveSettings(sourceImage.getObjectiveSettings())); } if(sourceImage.getPixels() != null) { destinationImage.setPixels(new Pixels(sourceImage.getPixels())); } if(sourceImage.getStageLabel() != null) { destinationImage.setStageLabel(new StageLabel(sourceImage.getStageLabel())); } destinationImage.linkExperiment(sourceImage.getLinkedExperiment()); destinationImage.linkExperimenter(sourceImage.getLinkedExperimenter()); destinationImage.linkExperimenterGroup(sourceImage.getLinkedExperimenterGroup()); destinationImage.linkInstrument(sourceImage.getLinkedInstrument()); for(Annotation linkedAnnotation : sourceImage.copyLinkedAnnotationList()) { destinationImage.linkAnnotation(linkedAnnotation); } for(Dataset linkedDataset : sourceImage.copyLinkedDatasetList()) { destinationImage.linkDataset(linkedDataset); } for(MicrobeamManipulation linkedMicrobeamManipulation : sourceImage.copyLinkedMicrobeamManipulationList()) { destinationImage.linkMicrobeamManipulation(linkedMicrobeamManipulation); } for(ROI linkedROI : sourceImage.copyLinkedROIList()) { destinationImage.linkROI(linkedROI); } for(WellSample linkedWellSample : sourceImage.copyLinkedWellSampleList()) { destinationImage.linkWellSample(linkedWellSample); } OMEXMLMetadataRoot destinationMetadataRoot = (OMEXMLMetadataRoot)destinationMetadata.getRoot(); while(destinationIndex >= destinationMetadataRoot.sizeOfImageList()) { destinationMetadataRoot.addImage(destinationImage); } destinationMetadataRoot.setImage(destinationIndex, destinationImage); } private static void removeEntriesForRemovedImages(IMetadata metadata, int imageCount) { OMEXMLMetadataRoot metadataRoot = (OMEXMLMetadataRoot)metadata.getRoot(); List<Image> imagesToRemove = new ArrayList<>(); for(int i = imageCount; i < metadata.getImageCount() ;++i) { imagesToRemove.add(metadataRoot.getImage(i)); } for(Image imageToRemove : imagesToRemove) { metadataRoot.removeImage(imageToRemove); } } /** * Gets the index of tag representing that given image is a lower resolution. * * @return The index of tag or -1 if there is no lower resolution tag in the metadata. */ public static int getLowerResolutionTagIndex(IMetadata metadata) { for(int tagAnnotationIndex = 0; tagAnnotationIndex < getTagAnnocationCount(metadata) ;++tagAnnotationIndex) { if(metadata.getTagAnnotationValue(tagAnnotationIndex).equals(LOWER_RESOLUTION_IMAGE_TAG_VALUE)) { return tagAnnotationIndex; } } return -1; } private static int getTagAnnocationCount(IMetadata metadata) { try { return metadata.getTagAnnotationCount(); } catch(NullPointerException e) { // getTagAnnotationCount() throws NullPointerException() when there are no tags... return 0; } } }