001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.sequence;
020
021import icy.image.IcyBufferedImage;
022import icy.type.DataType;
023import icy.util.OMEUtil;
024import icy.util.StringUtil;
025import icy.util.XMLUtil;
026
027import java.awt.Color;
028import java.util.HashSet;
029import java.util.Set;
030
031import org.joda.time.Instant;
032
033import loci.common.services.ServiceException;
034import loci.formats.FormatTools;
035import loci.formats.MetadataTools;
036import loci.formats.ome.OMEXMLMetadataImpl;
037import ome.units.quantity.Time;
038import ome.xml.meta.MetadataRetrieve;
039import ome.xml.meta.OMEXMLMetadata;
040import ome.xml.model.Annotation;
041import ome.xml.model.Channel;
042import ome.xml.model.Dataset;
043import ome.xml.model.Experiment;
044import ome.xml.model.Experimenter;
045import ome.xml.model.ExperimenterGroup;
046import ome.xml.model.Image;
047import ome.xml.model.Instrument;
048import ome.xml.model.OME;
049import ome.xml.model.Pixels;
050import ome.xml.model.Plane;
051import ome.xml.model.ROI;
052import ome.xml.model.StructuredAnnotations;
053import ome.xml.model.XMLAnnotation;
054import ome.xml.model.enums.DimensionOrder;
055import ome.xml.model.primitives.PositiveInteger;
056import ome.xml.model.primitives.Timestamp;
057
058/**
059 * Meta data utilities class.<br>
060 * Basically provide safe access to metadata.
061 * 
062 * @author Stephane
063 */
064public class MetaDataUtil
065{
066    public static final String DEFAULT_CHANNEL_NAME = "ch ";
067
068    /**
069     * Returns OME root element (create it if needed).
070     */
071    public static OME getOME(OMEXMLMetadata metaData)
072    {
073        OME result = (OME) metaData.getRoot();
074
075        if (result == null)
076        {
077            metaData.createRoot();
078            result = (OME) metaData.getRoot();
079        }
080
081        return result;
082    }
083
084    /**
085     * @deprecated Use {@link #getOME(OMEXMLMetadata)} instead
086     */
087    @Deprecated
088    public static OME getOME(OMEXMLMetadataImpl metaData)
089    {
090        return getOME((OMEXMLMetadata) metaData);
091    }
092
093    /**
094     * Returns the number of image series of the specified metaData description.
095     */
096    public static int getNumSeries(OMEXMLMetadata metaData)
097    {
098        return metaData.getImageCount();
099    }
100
101    /**
102     * @deprecated Use {@link #getNumSeries(OMEXMLMetadata)} instead
103     */
104    @Deprecated
105    public static int getNumSerie(loci.formats.ome.OMEXMLMetadata metaData)
106    {
107        return getNumSeries(metaData);
108    }
109
110    /**
111     * @deprecated Use {@link #getNumSeries(OMEXMLMetadata)} instead
112     */
113    @Deprecated
114    public static int getNumSerie(OMEXMLMetadata metaData)
115    {
116        return getNumSeries(metaData);
117    }
118
119    /**
120     * @deprecated Use {@link #getNumSeries(OMEXMLMetadata)} instead
121     */
122    @Deprecated
123    public static int getNumSerie(OMEXMLMetadataImpl metaData)
124    {
125        return getNumSerie((OMEXMLMetadata) metaData);
126    }
127
128    /**
129     * Return image series object at specified index for the specified metaData description.
130     */
131    public static Image getSeries(OMEXMLMetadata metaData, int index)
132    {
133        final OME ome = getOME(metaData);
134
135        if (index < ome.sizeOfImageList())
136            return ome.getImage(index);
137
138        return null;
139    }
140
141    /**
142     * @deprecated Use {@link #getSeries(OMEXMLMetadata, int)} instead
143     */
144    @Deprecated
145    public static Image getSerie(OMEXMLMetadata metaData, int index)
146    {
147        return getSeries(metaData, index);
148    }
149
150    /**
151     * @deprecated Use {@link #getSeries(OMEXMLMetadata, int)} instead
152     */
153    @Deprecated
154    public static Image getSerie(OMEXMLMetadataImpl metaData, int index)
155    {
156        return getSeries(metaData, index);
157    }
158
159    /**
160     * Ensure the image series at specified index exist for the specified metaData description.
161     */
162    public static Image ensureSeries(OME ome, int index)
163    {
164        // create missing image
165        while (ome.sizeOfImageList() <= index)
166        {
167            final Image img = new Image();
168            ome.addImage(img);
169        }
170
171        final Image result = ome.getImage(index);
172
173        if (result.getPixels() == null)
174        {
175            final Pixels pix = new Pixels();
176            // wanted default dimension order
177            pix.setDimensionOrder(DimensionOrder.XYCZT);
178            // create default pixels object
179            result.setPixels(pix);
180        }
181
182        return result;
183    }
184
185    /**
186     * @deprecated Use {@link #ensureSeries(OME, int)} instead
187     */
188    @Deprecated
189    public static Image ensureSerie(OME ome, int index)
190    {
191        return ensureSeries(ome, index);
192    }
193
194    /**
195     * Set the number of image series for the specified metaData description.
196     */
197    public static void setNumSeries(OMEXMLMetadata metaData, int num)
198    {
199        final OME ome = getOME(metaData);
200
201        // keep only desired number of image
202        while (ome.sizeOfImageList() > num)
203            ome.removeImage(ome.getImage(ome.sizeOfImageList() - 1));
204
205        // create missing image
206        ensureSeries(ome, num - 1);
207    }
208
209    /**
210     * @deprecated Use {@link #setNumSeries(OMEXMLMetadata, int)} instead
211     */
212    @Deprecated
213    public static void setNumSerie(OMEXMLMetadata metaData, int num)
214    {
215        setNumSeries(metaData, num);
216    }
217
218    /**
219     * @deprecated Use {@link #setNumSerie(OMEXMLMetadata, int)} instead
220     */
221    @Deprecated
222    public static void setNumSerie(OMEXMLMetadataImpl metaData, int num)
223    {
224        setNumSerie((OMEXMLMetadata) metaData, num);
225    }
226
227    /**
228     * Return pixels object at specified index for the specified metaData description.
229     */
230    public static Pixels getPixels(OME ome, int index)
231    {
232        if (ome != null)
233        {
234            if (index < ome.sizeOfImageList())
235                return ome.getImage(index).getPixels();
236        }
237
238        return null;
239    }
240
241    /**
242     * Return pixels object at specified index for the specified metaData description.
243     */
244    public static Pixels getPixels(OMEXMLMetadata metaData, int index)
245    {
246        return getPixels(getOME(metaData), index);
247    }
248
249    /**
250     * @deprecated Use {@link #getPixels(OMEXMLMetadata, int)} instead
251     */
252    @Deprecated
253    public static Pixels getPixels(OMEXMLMetadataImpl metaData, int index)
254    {
255        return getPixels((OMEXMLMetadata) metaData, index);
256    }
257
258    /**
259     * Return plane index for the specified T, Z, C position.
260     */
261    public static int getPlaneIndex(Pixels pix, int t, int z, int c)
262    {
263        // can't compute plane index --> return 0 by default
264        if ((t < 0) || (z < 0) || (c < 0))
265            return 0;
266        // trivial opti...
267        if ((t == 0) && (z == 0) && (c == 0))
268            return 0;
269
270        final int sizeT = OMEUtil.getValue(pix.getSizeT(), 0);
271        final int sizeZ = OMEUtil.getValue(pix.getSizeZ(), 0);
272        int sizeC = OMEUtil.getValue(pix.getSizeC(), 0);
273
274        // can't compute plane index --> return 0 by default
275        if ((sizeT == 0) || (sizeZ == 0) || (sizeC == 0))
276            return 0;
277
278        int adjC = c;
279
280        if (pix.sizeOfChannelList() > 0)
281        {
282            final Channel channel = pix.getChannel(0);
283            if (channel != null)
284            {
285                final int spp = OMEUtil.getValue(channel.getSamplesPerPixel(), 0);
286                // channel are packed in pixel so consider sizeC = 1
287                if ((spp != 0) && (spp == sizeC))
288                {
289                    sizeC = 1;
290                    adjC = 0;
291                }
292            }
293        }
294
295        // first try to get index from real plan position
296        final int len = pix.sizeOfPlaneList();
297        for (int i = 0; i < len; i++)
298        {
299            final Plane plane = pix.getPlane(i);
300
301            // plane found --> return index
302            if ((OMEUtil.getValue(plane.getTheT(), -1) == t) && (OMEUtil.getValue(plane.getTheZ(), -1) == z)
303                    && (OMEUtil.getValue(plane.getTheC(), -1) == c))
304                return i;
305        }
306
307        DimensionOrder dimOrder = pix.getDimensionOrder();
308        // use default dimension order
309        if (dimOrder == null)
310            dimOrder = DimensionOrder.XYCZT;
311
312        // use computed method
313        return FormatTools.getIndex(dimOrder.getValue(), sizeZ, sizeC, sizeT, sizeZ * sizeC * sizeT, z, adjC, t);
314    }
315
316    public static Plane getPlane(Pixels pix, int index)
317    {
318        if (pix != null)
319        {
320            if (index < pix.sizeOfPlaneList())
321                return pix.getPlane(index);
322        }
323
324        return null;
325    }
326
327    /**
328     * Return plane object for the specified T, Z, C position.
329     */
330    public static Plane getPlane(Pixels pix, int t, int z, int c)
331    {
332        return getPlane(pix, getPlaneIndex(pix, t, z, c));
333    }
334
335    /**
336     * Ensure the plane at specified index exist for the specified Pixels object.
337     */
338    public static Plane ensurePlane(Pixels pix, int index)
339    {
340        // create missing plane
341        while (pix.sizeOfPlaneList() <= index)
342            pix.addPlane(new Plane());
343
344        return pix.getPlane(index);
345    }
346
347    /**
348     * Ensure the plane at specified T, Z, C position exist for the specified Pixels object.
349     */
350    public static Plane ensurePlane(Pixels pix, int t, int z, int c)
351    {
352        return ensurePlane(pix, getPlaneIndex(pix, t, z, c));
353    }
354
355    /**
356     * Remove the plane at specified position.
357     * 
358     * @return <code>true</code> if the operation succeed, <code>false</code> otherwise
359     */
360    public static boolean removePlane(Image img, int index)
361    {
362        final Pixels pix = img.getPixels();
363        if (pix == null)
364            return false;
365
366        final int numPlane = pix.sizeOfPlaneList();
367
368        // single plane information or no plane here --> return false
369        if ((numPlane <= 1) || (index >= numPlane))
370            return false;
371
372        final Plane plane = getPlane(pix, index);
373
374        // remove plane
375        pix.removePlane(plane);
376
377        // remove associated annotation
378        for (int i = 0; i < plane.sizeOfLinkedAnnotationList(); i++)
379            img.unlinkAnnotation(plane.getLinkedAnnotation(i));
380
381        // clean some data
382        if (pix.sizeOfBinDataList() == numPlane)
383            pix.removeBinData(pix.getBinData(index));
384        if (pix.sizeOfTiffDataList() == numPlane)
385            pix.removeTiffData(pix.getTiffData(index));
386
387        return true;
388    }
389
390    /**
391     * Remove the plane at specified position.
392     * 
393     * @return <code>true</code> if the operation succeed, <code>false</code> otherwise
394     */
395    public static boolean removePlane(Image img, int t, int z, int c)
396    {
397        final Pixels pix = img.getPixels();
398        if (pix == null)
399            return false;
400
401        return removePlane(img, getPlaneIndex(pix, t, z, c));
402    }
403
404    /**
405     * Remove the plane at specified position.
406     * 
407     * @return <code>true</code> if the operation succeed, <code>false</code> otherwise
408     */
409    public static boolean removePlane(OMEXMLMetadata metadata, int series, int t, int z, int c)
410    {
411        final Image img = getSeries(metadata, series);
412        if (img == null)
413            return false;
414
415        return removePlane(img, t, z, c);
416    }
417
418    /**
419     * @deprecated Use {@link #removePlane(OMEXMLMetadata, int, int, int, int)} instead
420     */
421    @Deprecated
422    public static boolean removePlane(OMEXMLMetadataImpl metadata, int series, int t, int z, int c)
423    {
424        return removePlane((OMEXMLMetadata) metadata, series, t, z, c);
425    }
426
427    /**
428     * Remove planes at given position
429     * 
430     * @param posT
431     *        T position where we want to remove metadata (-1 for all)
432     * @param posZ
433     *        Z position where we want to remove metadata (-1 for all)
434     * @param posC
435     *        C position where we want to remove metadata (-1 for all)
436     */
437    public static void removePlanes(OMEXMLMetadata metadata, int series, int posT, int posZ, int posC)
438    {
439        final int minT, maxT;
440        final int minZ, maxZ;
441        final int minC, maxC;
442
443        if (posT < 0)
444        {
445            minT = 0;
446            maxT = getSizeT(metadata, series) - 1;
447        }
448        else
449        {
450            minT = posT;
451            maxT = posT;
452        }
453        if (posZ < 0)
454        {
455            minZ = 0;
456            maxZ = getSizeZ(metadata, series) - 1;
457        }
458        else
459        {
460            minZ = posZ;
461            maxZ = posZ;
462        }
463        if (posC < 0)
464        {
465            minC = 0;
466            maxC = getSizeC(metadata, series) - 1;
467        }
468        else
469        {
470            minC = posC;
471            maxC = posC;
472        }
473
474        for (int t = minT; t <= maxT; t++)
475            for (int z = minZ; z <= maxZ; z++)
476                for (int c = minC; c <= maxC; c++)
477                    MetaDataUtil.removePlane(metadata, 0, t, z, c);
478    }
479
480    /**
481     * @deprecated Use {@link #removePlanes(OMEXMLMetadata, int, int, int, int)} instead
482     */
483    @Deprecated
484    public static void removePlanes(OMEXMLMetadataImpl metadata, int series, int posT, int posZ, int posC)
485    {
486        removePlanes((OMEXMLMetadata) metadata, series, posT, posZ, posC);
487    }
488
489    /**
490     * Returns the data type of the specified image series.
491     */
492    public static DataType getDataType(OMEXMLMetadata metaData, int series)
493    {
494        final Pixels pix = getPixels(metaData, series);
495
496        if (pix != null)
497            return DataType.getDataTypeFromPixelType(pix.getType());
498
499        // assume byte by default
500        return DataType.UBYTE;
501    }
502
503    /**
504     * @deprecated Use {@link #getDataType(OMEXMLMetadata, int)} instead
505     */
506    @Deprecated
507    public static DataType getDataType(OMEXMLMetadataImpl metaData, int series)
508    {
509        return getDataType((OMEXMLMetadata) metaData, series);
510    }
511
512    /**
513     * Returns the width (sizeX) of the specified image series.
514     */
515    public static int getSizeX(OMEXMLMetadata metaData, int series)
516    {
517        final Pixels pix = getPixels(metaData, series);
518
519        if (pix != null)
520            return OMEUtil.getValue(pix.getSizeX(), 0);
521
522        return 0;
523    }
524
525    /**
526     * @deprecated Use {@link #getSizeX(OMEXMLMetadata, int)} instead
527     */
528    @Deprecated
529    public static int getSizeX(OMEXMLMetadataImpl metaData, int series)
530    {
531        return getSizeX((OMEXMLMetadata) metaData, series);
532    }
533
534    /**
535     * Returns the height (sizeY) of the specified image series.
536     */
537    public static int getSizeY(OMEXMLMetadata metaData, int series)
538    {
539        final Pixels pix = getPixels(metaData, series);
540
541        if (pix != null)
542            return OMEUtil.getValue(pix.getSizeY(), 0);
543
544        return 0;
545    }
546
547    /**
548     * @deprecated Use {@link #getSizeY(OMEXMLMetadata, int)} instead
549     */
550    @Deprecated
551    public static int getSizeY(OMEXMLMetadataImpl metaData, int series)
552    {
553        return getSizeY((OMEXMLMetadata) metaData, series);
554    }
555
556    /**
557     * Returns the number of channel (sizeC) of the specified image series.
558     */
559    public static int getSizeC(OMEXMLMetadata metaData, int series)
560    {
561        final Pixels pix = getPixels(metaData, series);
562
563        if (pix != null)
564            return OMEUtil.getValue(pix.getSizeC(), 0);
565
566        return 0;
567    }
568
569    /**
570     * @deprecated Use {@link #getSizeC(OMEXMLMetadata, int)} instead
571     */
572    @Deprecated
573    public static int getSizeC(OMEXMLMetadataImpl metaData, int series)
574    {
575        return getSizeC((OMEXMLMetadata) metaData, series);
576    }
577
578    /**
579     * Returns the depth (sizeZ) of the specified image series.
580     */
581    public static int getSizeZ(OMEXMLMetadata metaData, int series)
582    {
583        final Pixels pix = getPixels(metaData, series);
584
585        if (pix != null)
586            return OMEUtil.getValue(pix.getSizeZ(), 0);
587
588        return 0;
589    }
590
591    /**
592     * @deprecated Use {@link #getSizeZ(OMEXMLMetadata, int)} instead
593     */
594    @Deprecated
595    public static int getSizeZ(OMEXMLMetadataImpl metaData, int series)
596    {
597        return getSizeZ((OMEXMLMetadata) metaData, series);
598    }
599
600    /**
601     * Returns the number of frame (sizeT) of the specified Pixels object.
602     */
603    private static int getSizeT(Pixels pix)
604    {
605        return OMEUtil.getValue(pix.getSizeT(), 0);
606    }
607
608    /**
609     * Returns the number of frame (sizeT) of the specified image series.
610     */
611    public static int getSizeT(OMEXMLMetadata metaData, int series)
612    {
613        final Pixels pix = getPixels(metaData, series);
614
615        if (pix != null)
616            return getSizeT(pix);
617
618        return 0;
619    }
620
621    /**
622     * @deprecated Use {@link #getSizeT(OMEXMLMetadata, int)} instead
623     */
624    @Deprecated
625    public static int getSizeT(OMEXMLMetadataImpl metaData, int series)
626    {
627        return getSizeT((OMEXMLMetadata) metaData, series);
628    }
629
630    /**
631     * Returns the total data size (in bytes) of the specified image series.
632     */
633    public static long getDataSize(OMEXMLMetadata metaData, int series)
634    {
635        return getDataSize(metaData, series, 0);
636    }
637
638    /**
639     * Returns the total data size (in bytes) of the specified image series
640     * for the given resolution (0 = full, 1 = 1/2, ...)
641     */
642    public static long getDataSize(OMEXMLMetadata metaData, int series, int resolution)
643    {
644        return getDataSize(metaData, series, resolution, getSizeZ(metaData, series), getSizeT(metaData, series));
645    }
646
647    /**
648     * Returns the total data size (in bytes) of the specified image series
649     * for the given resolution (0 = full, 1 = 1/2, ...) and size informations
650     */
651    public static long getDataSize(OMEXMLMetadata metaData, int series, int resolution, int sizeZ, int sizeT)
652    {
653        return getDataSize(metaData, series, resolution, sizeZ, sizeT, getSizeC(metaData, series));
654    }
655
656    /**
657     * Returns the total data size (in bytes) of the specified image series
658     * for the given resolution (0 = full, 1 = 1/2, ...) and size informations
659     */
660    public static long getDataSize(OMEXMLMetadata metaData, int series, int resolution, int sizeZ, int sizeT, int sizeC)
661    {
662        final Pixels pix = getPixels(metaData, series);
663
664        if (pix != null)
665        {
666            long sizeXY = (long) OMEUtil.getValue(pix.getSizeX(), 0) * (long) OMEUtil.getValue(pix.getSizeY(), 0);
667
668            if (resolution > 0)
669                sizeXY /= Math.pow(4d, resolution);
670
671            return sizeXY * sizeC * sizeZ * sizeT * DataType.getDataTypeFromPixelType(pix.getType()).getSize();
672        }
673
674        return 0L;
675    }
676
677    /**
678     * Sets the data type of the specified image series.
679     */
680    public static void setDataType(OMEXMLMetadata metaData, int series, DataType dataType)
681    {
682        metaData.setPixelsType(dataType.toPixelType(), series);
683    }
684
685    /**
686     * @deprecated Use {@link #setDataType(OMEXMLMetadata, int, DataType)} instead
687     */
688    @Deprecated
689    public static void setDataType(OMEXMLMetadataImpl metaData, int series, DataType dataType)
690    {
691        setDataType((OMEXMLMetadata) metaData, series, dataType);
692    }
693
694    /**
695     * Sets the width (sizeX) of the specified image series (need to be >= 1).
696     */
697    public static void setSizeX(OMEXMLMetadata metaData, int series, int sizeX)
698    {
699        metaData.setPixelsSizeX(OMEUtil.getPositiveInteger(sizeX), series);
700    }
701
702    /**
703     * @deprecated Use {@link #setSizeX(OMEXMLMetadata, int, int)} instead
704     */
705    @Deprecated
706    public static void setSizeX(OMEXMLMetadataImpl metaData, int series, int sizeX)
707    {
708        setSizeX((OMEXMLMetadata) metaData, series, sizeX);
709    }
710
711    /**
712     * Sets the height (sizeY) of the specified image series (need to be >= 1).
713     */
714    public static void setSizeY(OMEXMLMetadata metaData, int series, int sizeY)
715    {
716        metaData.setPixelsSizeY(OMEUtil.getPositiveInteger(sizeY), series);
717    }
718
719    /**
720     * @deprecated Use {@link #setSizeY(OMEXMLMetadata, int, int)} instead
721     */
722    @Deprecated
723    public static void setSizeY(OMEXMLMetadataImpl metaData, int series, int sizeY)
724    {
725        setSizeY((OMEXMLMetadata) metaData, series, sizeY);
726    }
727
728    /**
729     * Sets the number of channel (sizeC) of the specified image series (need to be >= 1).
730     */
731    public static void setSizeC(OMEXMLMetadata metaData, int series, int sizeC)
732    {
733        metaData.setPixelsSizeC(OMEUtil.getPositiveInteger(sizeC), series);
734    }
735
736    /**
737     * @deprecated Use {@link #setSizeC(OMEXMLMetadata, int, int)} instead
738     */
739    @Deprecated
740    public static void setSizeC(OMEXMLMetadataImpl metaData, int series, int sizeC)
741    {
742        setSizeC((OMEXMLMetadata) metaData, series, sizeC);
743    }
744
745    /**
746     * Sets the depth (sizeZ) of the specified image series (need to be >= 1).
747     */
748    public static void setSizeZ(OMEXMLMetadata metaData, int series, int sizeZ)
749    {
750        metaData.setPixelsSizeZ(OMEUtil.getPositiveInteger(sizeZ), series);
751    }
752
753    /**
754     * @deprecated Use {@link #setSizeZ(OMEXMLMetadata, int, int)} instead
755     */
756    @Deprecated
757    public static void setSizeZ(OMEXMLMetadataImpl metaData, int series, int sizeZ)
758    {
759        setSizeZ((OMEXMLMetadata) metaData, series, sizeZ);
760    }
761
762    /**
763     * Sets the number of frame (sizeT) of the specified image series (need to be >= 1).
764     */
765    public static void setSizeT(OMEXMLMetadata metaData, int series, int sizeT)
766    {
767        metaData.setPixelsSizeT(OMEUtil.getPositiveInteger(sizeT), series);
768    }
769
770    /**
771     * @deprecated Use {@link #setSizeT(OMEXMLMetadata, int, int)} instead
772     */
773    @Deprecated
774    public static void setSizeT(OMEXMLMetadataImpl metaData, int series, int sizeT)
775    {
776        setSizeT((OMEXMLMetadata) metaData, series, sizeT);
777    }
778
779    /**
780     * Returns the id of the specified image series.
781     */
782    public static String getImageID(OMEXMLMetadata metaData, int series)
783    {
784        final Image img = getSeries(metaData, series);
785
786        if (img != null)
787            return StringUtil.getValue(img.getID(), "");
788
789        return "";
790    }
791
792    /**
793     * @deprecated Use {@link #getImageID(OMEXMLMetadata, int)} instead
794     */
795    @Deprecated
796    public static String getImageID(OMEXMLMetadataImpl metaData, int series)
797    {
798        return getImageID((OMEXMLMetadata) metaData, series);
799    }
800
801    /**
802     * Set the id of the specified image series.
803     */
804    public static void setImageID(OMEXMLMetadata metaData, int series, String value)
805    {
806        metaData.setImageID(value, series);
807    }
808
809    /**
810     * @deprecated Use {@link #setImageID(OMEXMLMetadata, int, String)} instead
811     */
812    @Deprecated
813    public static void setImageID(OMEXMLMetadataImpl metaData, int series, String value)
814    {
815        setImageID((OMEXMLMetadata) metaData, series, value);
816    }
817
818    /**
819     * Returns the name of the specified image series.
820     */
821    public static String getName(OMEXMLMetadata metaData, int series)
822    {
823        final Image img = getSeries(metaData, series);
824
825        if (img != null)
826            return StringUtil.getValue(img.getName(), "");
827
828        return "";
829    }
830
831    /**
832     * @deprecated Use {@link #getName(OMEXMLMetadata, int)} instead
833     */
834    @Deprecated
835    public static String getName(OMEXMLMetadataImpl metaData, int series)
836    {
837        return getName((OMEXMLMetadata) metaData, series);
838    }
839
840    /**
841     * Set the name of the specified image series.
842     */
843    public static void setName(OMEXMLMetadata metaData, int series, String value)
844    {
845        metaData.setImageName(value, series);
846    }
847
848    /**
849     * @deprecated Use {@link #setName(OMEXMLMetadata, int, String)} instead
850     */
851    @Deprecated
852    public static void setName(OMEXMLMetadataImpl metaData, int series, String value)
853    {
854        setName((OMEXMLMetadata) metaData, series, value);
855    }
856
857    /**
858     * Returns X pixel size (in µm) of the specified image series.
859     */
860    public static double getPixelSizeX(OMEXMLMetadata metaData, int series, double defaultValue)
861    {
862        final Pixels pix = getPixels(metaData, series);
863
864        if (pix != null)
865            return OMEUtil.getValue(pix.getPhysicalSizeX(), defaultValue);
866
867        return defaultValue;
868    }
869
870    /**
871     * @deprecated Use {@link #getPixelSizeX(OMEXMLMetadata, int, double)} instead
872     */
873    @Deprecated
874    public static double getPixelSizeX(OMEXMLMetadataImpl metaData, int series, double defaultValue)
875    {
876        return getPixelSizeX((OMEXMLMetadata) metaData, series, defaultValue);
877    }
878
879    /**
880     * Returns Y pixel size (in µm) of the specified image series.
881     */
882    public static double getPixelSizeY(OMEXMLMetadata metaData, int series, double defaultValue)
883    {
884        final Pixels pix = getPixels(metaData, series);
885
886        if (pix != null)
887            return OMEUtil.getValue(pix.getPhysicalSizeY(), defaultValue);
888
889        return defaultValue;
890    }
891
892    /**
893     * @deprecated Use {@link #getPixelSizeY(OMEXMLMetadata, int, double)} instead
894     */
895    @Deprecated
896    public static double getPixelSizeY(OMEXMLMetadataImpl metaData, int series, double defaultValue)
897    {
898        return getPixelSizeY((OMEXMLMetadata) metaData, series, defaultValue);
899    }
900
901    /**
902     * Returns Z pixel size (in µm) of the specified image series.
903     */
904    public static double getPixelSizeZ(OMEXMLMetadata metaData, int series, double defaultValue)
905    {
906        final Pixels pix = getPixels(metaData, series);
907
908        if (pix != null)
909            return OMEUtil.getValue(pix.getPhysicalSizeZ(), defaultValue);
910
911        return defaultValue;
912    }
913
914    /**
915     * @deprecated Use {@link #getPixelSizeZ(OMEXMLMetadata, int, double)} instead
916     */
917    @Deprecated
918    public static double getPixelSizeZ(OMEXMLMetadataImpl metaData, int series, double defaultValue)
919    {
920        return getPixelSizeZ((OMEXMLMetadata) metaData, series, defaultValue);
921    }
922
923    /**
924     * Computes and returns the T time interval (in second) from internal time positions.<br>
925     * If there is no internal time positions <code>0d</code> is returned.
926     */
927    public static double getTimeIntervalFromTimePositions(OMEXMLMetadata metaData, int series)
928    {
929        final Pixels pix = getPixels(metaData, series);
930
931        // try to compute time interval from time position
932        if (pix != null)
933            return computeTimeIntervalFromTimePosition(pix);
934
935        return 0d;
936    }
937
938    /**
939     * @deprecated Use {@link #getTimeIntervalFromTimePositions(OMEXMLMetadata, int)} instead
940     */
941    @Deprecated
942    public static double getTimeIntervalFromTimePositions(OMEXMLMetadataImpl metaData, int series)
943    {
944        return getTimeIntervalFromTimePositions((OMEXMLMetadata) metaData, series);
945    }
946
947    /**
948     * Returns T time interval (in second) for the specified image series.
949     */
950    private static double getTimeInterval(Pixels pix, double defaultValue)
951    {
952        final Time timeInc = pix.getTimeIncrement();
953
954        if (timeInc != null)
955            return OMEUtil.getValue(timeInc, defaultValue);
956
957        return defaultValue;
958    }
959
960    /**
961     * Returns T time interval (in second) for the specified image series.
962     */
963    public static double getTimeInterval(OMEXMLMetadata metaData, int series, double defaultValue)
964    {
965        final Pixels pix = getPixels(metaData, series);
966
967        if (pix != null)
968            return getTimeInterval(pix, defaultValue);
969
970        return defaultValue;
971    }
972
973    /**
974     * @deprecated Use {@link #getTimeInterval(OMEXMLMetadata, int, double)} instead
975     */
976    @Deprecated
977    public static double getTimeInterval(OMEXMLMetadataImpl metaData, int series, double defaultValue)
978    {
979        return getTimeInterval((OMEXMLMetadata) metaData, series, defaultValue);
980    }
981
982    /**
983     * Set X pixel size (in µm) of the specified image series.
984     */
985    public static void setPixelSizeX(OMEXMLMetadata metaData, int series, double value)
986    {
987        metaData.setPixelsPhysicalSizeX(OMEUtil.getLength(value), series);
988    }
989
990    /**
991     * @deprecated Use {@link #setPixelSizeX(OMEXMLMetadata, int, double)} instead
992     */
993    @Deprecated
994    public static void setPixelSizeX(OMEXMLMetadataImpl metaData, int series, double value)
995    {
996        setPixelSizeX((OMEXMLMetadata) metaData, series, value);
997    }
998
999    /**
1000     * Set Y pixel size (in µm) of the specified image series.
1001     */
1002    public static void setPixelSizeY(OMEXMLMetadata metaData, int series, double value)
1003    {
1004        metaData.setPixelsPhysicalSizeY(OMEUtil.getLength(value), series);
1005    }
1006
1007    /**
1008     * @deprecated Use {@link #setPixelSizeY(OMEXMLMetadata, int, double)} instead
1009     */
1010    @Deprecated
1011    public static void setPixelSizeY(OMEXMLMetadataImpl metaData, int series, double value)
1012    {
1013        setPixelSizeY((OMEXMLMetadata) metaData, series, value);
1014    }
1015
1016    /**
1017     * Set Z pixel size (in µm) of the specified image series.
1018     */
1019    public static void setPixelSizeZ(OMEXMLMetadata metaData, int series, double value)
1020    {
1021        metaData.setPixelsPhysicalSizeZ(OMEUtil.getLength(value), series);
1022    }
1023
1024    /**
1025     * @deprecated Use {@link #setPixelSizeZ(OMEXMLMetadata, int, double)} instead
1026     */
1027    @Deprecated
1028    public static void setPixelSizeZ(OMEXMLMetadataImpl metaData, int series, double value)
1029    {
1030        setPixelSizeZ((OMEXMLMetadata) metaData, series, value);
1031    }
1032
1033    /**
1034     * Set T time resolution (in second) of the specified image series.
1035     */
1036    public static void setTimeInterval(OMEXMLMetadata metaData, int series, double value)
1037    {
1038        metaData.setPixelsTimeIncrement(OMEUtil.getTime(value), series);
1039    }
1040
1041    /**
1042     * @deprecated Use {@link #setTimeInterval(OMEXMLMetadata, int, double)} instead
1043     */
1044    @Deprecated
1045    public static void setTimeInterval(OMEXMLMetadataImpl metaData, int series, double value)
1046    {
1047        setTimeInterval((OMEXMLMetadata) metaData, series, value);
1048    }
1049
1050    /**
1051     * Returns the X field position (in µm) for the image at the specified Z, T, C position.
1052     */
1053    public static double getPositionX(OMEXMLMetadata metaData, int series, int t, int z, int c, double defaultValue)
1054    {
1055        final Pixels pix = getPixels(metaData, series);
1056
1057        if (pix != null)
1058        {
1059            final Plane plane = getPlane(pix, t, z, c);
1060
1061            if (plane != null)
1062                return OMEUtil.getValue(plane.getPositionX(), defaultValue);
1063        }
1064
1065        return defaultValue;
1066    }
1067
1068    /**
1069     * Returns the Y field position (in µm) for the image at the specified Z, T, C position.
1070     */
1071    public static double getPositionY(OMEXMLMetadata metaData, int series, int t, int z, int c, double defaultValue)
1072    {
1073        final Pixels pix = getPixels(metaData, series);
1074
1075        if (pix != null)
1076        {
1077            final Plane plane = getPlane(pix, t, z, c);
1078
1079            if (plane != null)
1080                return OMEUtil.getValue(plane.getPositionY(), defaultValue);
1081        }
1082
1083        return defaultValue;
1084    }
1085
1086    /**
1087     * Returns the Z field position (in µm) for the image at the specified Z, T, C position.
1088     */
1089    public static double getPositionZ(OMEXMLMetadata metaData, int series, int t, int z, int c, double defaultValue)
1090    {
1091        final Pixels pix = getPixels(metaData, series);
1092
1093        if (pix != null)
1094        {
1095            final Plane plane = getPlane(pix, t, z, c);
1096
1097            if (plane != null)
1098                return OMEUtil.getValue(plane.getPositionZ(), defaultValue);
1099        }
1100
1101        return defaultValue;
1102    }
1103
1104    /**
1105     * @same as {@link #getTimeStamp(OMEXMLMetadata, int, long)}
1106     */
1107    public static double getPositionT(OMEXMLMetadata metaData, int series, long defaultValue)
1108    {
1109        return getTimeStamp(metaData, series, defaultValue);
1110    }
1111
1112    /**
1113     * Returns the time stamp (elapsed milliseconds from the Java epoch of 1970-01-01 T00:00:00Z) for the specified image.
1114     */
1115    public static long getTimeStamp(OMEXMLMetadata metaData, int series, long defaultValue)
1116    {
1117        final Image img = getSeries(metaData, series);
1118
1119        if (img != null)
1120        {
1121            final Timestamp time = img.getAcquisitionDate();
1122
1123            if (time != null)
1124                return time.asInstant().getMillis();
1125        }
1126
1127        return defaultValue;
1128    }
1129
1130    /**
1131     * Returns the time position offset (in second) relative to first image for the image at the specified Z, T, C position.
1132     */
1133    private static double getPositionTOffset(Pixels pix, int t, int z, int c, double defaultValue)
1134    {
1135        double result = -1d;
1136        final Plane plane = getPlane(pix, t, z, c);
1137
1138        if (plane != null)
1139            result = OMEUtil.getValue(plane.getDeltaT(), -1d);
1140
1141        // got it from DeltaT
1142        if (result != -1d)
1143            return result;
1144
1145        // try from time interval instead
1146        result = getTimeInterval(pix, -1d);
1147
1148        // we were able to get time interval ? just multiply it by T index
1149        if (result != -1d)
1150            return result * t;
1151
1152        return defaultValue;
1153    }
1154
1155    /**
1156     * Returns the time position offset (in second) relative to first image for the image at the specified Z, T, C position.
1157     */
1158    public static double getPositionTOffset(OMEXMLMetadata metaData, int series, int t, int z, int c,
1159            double defaultValue)
1160    {
1161        final Pixels pix = getPixels(metaData, series);
1162
1163        if (pix != null)
1164            return getPositionTOffset(pix, t, z, c, defaultValue);
1165
1166        return defaultValue;
1167    }
1168
1169    /**
1170     * @deprecated USe {@link #getPositionTOffset(OMEXMLMetadata, int, int, int, int, double)} instead
1171     */
1172    @Deprecated
1173    public static double getPositionT(OMEXMLMetadata metaData, int series, int t, int z, int c, double defaultValue)
1174    {
1175        return getPositionTOffset(metaData, series, t, z, c, defaultValue);
1176    }
1177
1178    /**
1179     * @deprecated USe {@link #getPositionTOffset(OMEXMLMetadata, int, int, int, int, double)} instead
1180     */
1181    @Deprecated
1182    public static double getTimePosition(OMEXMLMetadata metaData, int series, int t, int z, int c, double defaultValue)
1183    {
1184        return getPositionTOffset(metaData, series, t, z, c, defaultValue);
1185    }
1186
1187    /**
1188     * Computes time interval (in second) from the time position informations.<br>
1189     * Returns <code>0d</code> if time position informations are missing ot if we have only 1 frame in the image.
1190     */
1191    private static double computeTimeIntervalFromTimePosition(Pixels pix)
1192    {
1193        final int sizeT = getSizeT(pix);
1194
1195        if (sizeT <= 1)
1196            return 0d;
1197
1198        double result = 0d;
1199        double last = -1d;
1200        int lastT = 0;
1201        int num = 0;
1202
1203        for (int t = 0; t < sizeT; t++)
1204        {
1205            final Plane plane = getPlane(pix, t, 0, 0);
1206
1207            if (plane != null)
1208            {
1209                final double timePos = OMEUtil.getValue(plane.getDeltaT(), Double.NaN);
1210
1211                if (!Double.isNaN(timePos))
1212                {
1213                    if (last != -1d)
1214                    {
1215                        // get delta
1216                        result += (timePos - last) / (t - lastT);
1217                        num++;
1218                    }
1219
1220                    last = timePos;
1221                    lastT = t;
1222                }
1223            }
1224        }
1225
1226        // we need at least 1 delta
1227        if (num == 0)
1228            return 0d;
1229
1230        return result / num;
1231    }
1232
1233    /**
1234     * Sets the X field position (in µm) for the image at the specified Z, T, C position.
1235     */
1236    public static void setPositionX(OMEXMLMetadata metaData, int series, int t, int z, int c, double value)
1237    {
1238        final Pixels pix = getPixels(metaData, series);
1239
1240        if (pix != null)
1241        {
1242            final Plane plane = ensurePlane(pix, t, z, c);
1243
1244            if (plane != null)
1245                plane.setPositionX(OMEUtil.getLength(value));
1246        }
1247    }
1248
1249    /**
1250     * Sets the Y field position (in µm) for the image at the specified Z, T, C position.
1251     */
1252    public static void setPositionY(OMEXMLMetadata metaData, int series, int t, int z, int c, double value)
1253    {
1254        final Pixels pix = getPixels(metaData, series);
1255
1256        if (pix != null)
1257        {
1258            final Plane plane = ensurePlane(pix, t, z, c);
1259
1260            if (plane != null)
1261                plane.setPositionY(OMEUtil.getLength(value));
1262        }
1263    }
1264
1265    /**
1266     * Sets the Z field position (in µm) for the image at the specified Z, T, C position.
1267     */
1268    public static void setPositionZ(OMEXMLMetadata metaData, int series, int t, int z, int c, double value)
1269    {
1270        final Pixels pix = getPixels(metaData, series);
1271
1272        if (pix != null)
1273        {
1274            final Plane plane = ensurePlane(pix, t, z, c);
1275
1276            if (plane != null)
1277                plane.setPositionZ(OMEUtil.getLength(value));
1278        }
1279    }
1280
1281    /**
1282     * Same as {@link #setTimeStamp(OMEXMLMetadata, int, long)}
1283     */
1284    public static void setPositionT(OMEXMLMetadata metaData, int series, long value)
1285    {
1286        setTimeStamp(metaData, series, value);
1287    }
1288
1289    /**
1290     * Sets the time stamp (elapsed milliseconds from the Java epoch of 1970-01-01 T00:00:00Z) for the specified image.
1291     */
1292    public static void setTimeStamp(OMEXMLMetadata metaData, int series, long value)
1293    {
1294        final Image img = getSeries(metaData, series);
1295
1296        if (img != null)
1297            img.setAcquisitionDate(new Timestamp(new Instant(value)));
1298    }
1299
1300    /**
1301     * Sets the time position offset (in second) relative to the first image for the image at the specified Z, T, C position.
1302     */
1303    private static void setPositionTOffset(Pixels pix, int t, int z, int c, double value)
1304    {
1305        final Plane plane = getPlane(pix, t, z, c);
1306
1307        if (plane != null)
1308            plane.setDeltaT(OMEUtil.getTime(value));
1309    }
1310
1311    /**
1312     * Sets the time position offset (in second) relative to the first image for the image at the specified Z, T, C position.
1313     */
1314    public static void setPositionTOffset(OMEXMLMetadata metaData, int series, int t, int z, int c, double value)
1315    {
1316        final Pixels pix = getPixels(metaData, series);
1317
1318        if (pix != null)
1319            setPositionTOffset(pix, t, z, c, value);
1320    }
1321
1322    /**
1323     * Get default name for specified channel.
1324     */
1325    public static String getDefaultChannelName(int channel)
1326    {
1327        return DEFAULT_CHANNEL_NAME + channel;
1328    }
1329
1330    /**
1331     * Returns the number of channel for the specified image series in metaData description.
1332     */
1333    public static int getNumChannel(OMEXMLMetadata metaData, int series)
1334    {
1335        final Pixels pix = getPixels(metaData, series);
1336
1337        if (pix != null)
1338            return pix.sizeOfChannelList();
1339
1340        return 0;
1341    }
1342
1343    /**
1344     * @deprecated Use {@link #getNumChannel(OMEXMLMetadata, int)} instead
1345     */
1346    @Deprecated
1347    public static int getNumChannel(OMEXMLMetadataImpl metaData, int series)
1348    {
1349        return getNumChannel((OMEXMLMetadata) metaData, series);
1350    }
1351
1352    /**
1353     * Return channel object at specified index for the specified image series.
1354     */
1355    public static Channel getChannel(OMEXMLMetadata metaData, int series, int index)
1356    {
1357        final Pixels pix = getPixels(metaData, series);
1358
1359        if ((pix != null) && (index < pix.sizeOfChannelList()))
1360            return pix.getChannel(index);
1361
1362        return null;
1363    }
1364
1365    /**
1366     * @deprecated Use {@link #getChannel(OMEXMLMetadata, int, int)} instead
1367     */
1368    @Deprecated
1369    public static Channel getChannel(OMEXMLMetadataImpl metaData, int series, int index)
1370    {
1371        return getChannel((OMEXMLMetadata) metaData, series, index);
1372    }
1373
1374    /**
1375     * Ensure the channel at specified index exist for the specified image series.
1376     */
1377    public static Channel ensureChannel(OMEXMLMetadata metaData, int series, int index)
1378    {
1379        final Pixels pix = getPixels(metaData, series);
1380
1381        if (pix != null)
1382            return ensureChannel(pix, index);
1383
1384        return null;
1385    }
1386
1387    /**
1388     * @deprecated Use {@link #ensureChannel(OMEXMLMetadata, int, int)} instead
1389     */
1390    @Deprecated
1391    public static Channel ensureChannel(OMEXMLMetadataImpl metaData, int series, int index)
1392    {
1393        return ensureChannel((OMEXMLMetadata) metaData, series, index);
1394    }
1395
1396    /**
1397     * Ensure the channel at specified index exist for the specified image series.
1398     */
1399    public static Channel ensureChannel(Pixels pix, int index)
1400    {
1401        // create missing channel
1402        while (pix.sizeOfChannelList() <= index)
1403            pix.addChannel(new Channel());
1404
1405        return pix.getChannel(index);
1406    }
1407
1408    /**
1409     * Remove a channel for the specified image series.
1410     */
1411    public static void removeChannel(OMEXMLMetadata metaData, int series, int index)
1412    {
1413        final Pixels pix = getPixels(metaData, series);
1414
1415        if (pix != null)
1416            removeChannel(pix, index);
1417    }
1418
1419    /**
1420     * @deprecated Use {@link #removeChannel(OMEXMLMetadata, int, int)} instead
1421     */
1422    @Deprecated
1423    public static void removeChannel(OMEXMLMetadataImpl metaData, int series, int index)
1424    {
1425        removeChannel((OMEXMLMetadata) metaData, series, index);
1426    }
1427
1428    /**
1429     * Remove a channel from the specified Pixels object.
1430     */
1431    public static void removeChannel(Pixels pix, int index)
1432    {
1433        if (pix.sizeOfChannelList() > index)
1434            pix.removeChannel(pix.getChannel(index));
1435    }
1436
1437    /**
1438     * Set the number of channel for the specified image series in metaData description.<br>
1439     * This is different from {@link #getSizeC(OMEXMLMetadata, int)}.
1440     */
1441    public static void setNumChannel(OMEXMLMetadata metaData, int series, int num)
1442    {
1443        final OME ome = getOME(metaData);
1444
1445        ensureSeries(ome, series);
1446
1447        final Image img = ome.getImage(series);
1448        Pixels pix = img.getPixels();
1449
1450        if (pix == null)
1451        {
1452            // create pixels object
1453            pix = new Pixels();
1454            img.setPixels(pix);
1455        }
1456
1457        // keep only desired number of image
1458        while (pix.sizeOfChannelList() > num)
1459            removeChannel(pix, pix.sizeOfChannelList() - 1);
1460
1461        // create missing image
1462        ensureChannel(pix, num - 1);
1463    }
1464
1465    /**
1466     * @deprecated Use {@link #setNumChannel(OMEXMLMetadata, int, int)} instead
1467     */
1468    @Deprecated
1469    public static void setNumChannel(OMEXMLMetadataImpl metaData, int series, int num)
1470    {
1471        setNumChannel((OMEXMLMetadata) metaData, series, num);
1472    }
1473
1474    /**
1475     * Initialize default channel name until specified index if they are missing from the meta data
1476     * description.
1477     */
1478    private static void prepareMetaChannelName(OMEXMLMetadata metaData, int series, int channel)
1479    {
1480        int c = getNumChannel(metaData, series);
1481
1482        while (channel >= c)
1483        {
1484            // set default channel name
1485            metaData.setChannelName(getDefaultChannelName(c), series, c);
1486            c++;
1487        }
1488    }
1489
1490    /**
1491     * Returns name of specified channel image series.
1492     */
1493    public static String getChannelName(OMEXMLMetadata metaData, int series, int channel)
1494    {
1495        // needed as LOCI does not initialize them on read
1496        prepareMetaChannelName(metaData, series, channel);
1497
1498        final String result = StringUtil.getValue(metaData.getChannelName(series, channel),
1499                getDefaultChannelName(channel));
1500        final String cleaned = XMLUtil.filterString(result);
1501
1502        // cleaned string != original value --> set it
1503        if (!cleaned.equals(result))
1504            setChannelName(metaData, series, channel, cleaned);
1505
1506        return cleaned;
1507    }
1508
1509    /**
1510     * @deprecated Use {@link #getChannelName(OMEXMLMetadata, int, int)} instead
1511     */
1512    @Deprecated
1513    public static String getChannelName(OMEXMLMetadataImpl metaData, int series, int channel)
1514    {
1515        return getChannelName((OMEXMLMetadata) metaData, series, channel);
1516    }
1517
1518    /**
1519     * Set name of specified channel image series.
1520     */
1521    public static void setChannelName(OMEXMLMetadata metaData, int series, int channel, String value)
1522    {
1523        // needed as LOCI only add current channel if it's missing
1524        prepareMetaChannelName(metaData, series, channel - 1);
1525
1526        metaData.setChannelName(value, series, channel);
1527    }
1528
1529    /**
1530     * @deprecated Use {@link #setChannelName(OMEXMLMetadata, int, int, String)} instead
1531     */
1532    @Deprecated
1533    public static void setChannelName(OMEXMLMetadataImpl metaData, int series, int channel, String value)
1534    {
1535        setChannelName((OMEXMLMetadata) metaData, series, channel, value);
1536    }
1537
1538    /**
1539     * Returns Color of specified channel image series.
1540     */
1541    public static Color getChannelColor(OMEXMLMetadata metaData, int series, int channel)
1542    {
1543        // needed as LOCI does not initialize them on read
1544        prepareMetaChannelName(metaData, series, channel);
1545
1546        return OMEUtil.getJavaColor(metaData.getChannelColor(series, channel));
1547    }
1548
1549    /**
1550     * @deprecated Use {@link #getChannelColor(OMEXMLMetadata, int, int)} instead
1551     */
1552    @Deprecated
1553    public static Color getChannelColor(OMEXMLMetadataImpl metaData, int series, int channel)
1554    {
1555        return getChannelColor((OMEXMLMetadata) metaData, series, channel);
1556    }
1557
1558    /**
1559     * Create and return a default (OME XML) Metadata object with default image name.
1560     */
1561    public static OMEXMLMetadata createMetadata(String name)
1562    {
1563        final OMEXMLMetadata result = OMEUtil.createOMEXMLMetadata();
1564        final OME ome = getOME(result);
1565
1566        ensureSeries(ome, 0);
1567
1568        result.setImageID(MetadataTools.createLSID("Image", 0), 0);
1569        result.setImageName(name, 0);
1570
1571        return result;
1572    }
1573
1574    /**
1575     * @deprecated Use {@link #createMetadata(String)} instead.
1576     */
1577    @Deprecated
1578    public static OMEXMLMetadataImpl createDefaultMetadata(String name)
1579    {
1580        return (OMEXMLMetadataImpl) createMetadata(name);
1581    }
1582
1583    /**
1584     * @deprecated Use {@link OMEUtil#createOMEXMLMetadata(MetadataRetrieve, int)}
1585     */
1586    @Deprecated
1587    public static OMEXMLMetadata createOMEMetadata(loci.formats.meta.MetadataRetrieve metadata, int series)
1588    {
1589        return OMEUtil.createOMEXMLMetadata(metadata, series);
1590    }
1591
1592    /**
1593     * Set metadata object with the given image properties.
1594     * 
1595     * @param metadata
1596     *        metadata object to fill.
1597     * @param sizeX
1598     *        width in pixels (need to be >= 1)
1599     * @param sizeY
1600     *        height in pixels (need to be >= 1)
1601     * @param sizeC
1602     *        number of channel (need to be >= 1)
1603     * @param sizeZ
1604     *        number of Z slices (need to be >= 1)
1605     * @param sizeT
1606     *        number of T frames (need to be >= 1)
1607     * @param dataType
1608     *        data type.
1609     * @param separateChannel
1610     *        true if we want channel data to be separated.
1611     */
1612    public static void setMetaData(OMEXMLMetadata metadata, int sizeX, int sizeY, int sizeC, int sizeZ, int sizeT,
1613            DataType dataType, boolean separateChannel)
1614    {
1615        OME ome = (OME) metadata.getRoot();
1616
1617        if (ome == null)
1618        {
1619            metadata.createRoot();
1620            ome = (OME) metadata.getRoot();
1621        }
1622
1623        // keep only one image
1624        setNumSeries(metadata, 1);
1625        // clean TiffData metadata (can produce error on reloading)
1626        cleanTiffData(ome.getImage(0));
1627        // clean binData metadata (can produce error on reloading)
1628        cleanBinData(ome.getImage(0));
1629
1630        if (StringUtil.isEmpty(metadata.getImageID(0)))
1631            metadata.setImageID(MetadataTools.createLSID("Image", 0), 0);
1632        if (StringUtil.isEmpty(metadata.getImageName(0)))
1633            metadata.setImageName("Sample", 0);
1634
1635        if (StringUtil.isEmpty(metadata.getPixelsID(0)))
1636            metadata.setPixelsID(MetadataTools.createLSID("Pixels", 0), 0);
1637
1638        // prefer big endian as JVM is big endian
1639        metadata.setPixelsBigEndian(Boolean.TRUE, 0);
1640        metadata.setPixelsBinDataBigEndian(Boolean.TRUE, 0, 0);
1641        // force XYCZT dimension order
1642        metadata.setPixelsDimensionOrder(DimensionOrder.XYCZT, 0);
1643
1644        // adjust pixel type and dimension size
1645        metadata.setPixelsType(dataType.toPixelType(), 0);
1646        metadata.setPixelsSizeX(OMEUtil.getPositiveInteger(sizeX), 0);
1647        metadata.setPixelsSizeY(OMEUtil.getPositiveInteger(sizeY), 0);
1648        metadata.setPixelsSizeC(OMEUtil.getPositiveInteger(sizeC), 0);
1649        metadata.setPixelsSizeZ(OMEUtil.getPositiveInteger(sizeZ), 0);
1650        metadata.setPixelsSizeT(OMEUtil.getPositiveInteger(sizeT), 0);
1651
1652        // clean plane metadata outside allowed range
1653        cleanPlanes(ome.getImage(0));
1654
1655        // get time interval information
1656        double timeInterval = MetaDataUtil.getTimeInterval(metadata, 0, 0d);
1657        // not defined ?
1658        if (timeInterval == 0d)
1659        {
1660            // try to compute it from time positions
1661            timeInterval = getTimeIntervalFromTimePositions(metadata, 0);
1662            // we got something --> set it as the time interval
1663            if (timeInterval != 0d)
1664                MetaDataUtil.setTimeInterval(metadata, 0, timeInterval);
1665        }
1666
1667        // fix channel number depending separate channel flag
1668        if (separateChannel)
1669        {
1670            // set channel number
1671            setNumChannel(metadata, 0, sizeC);
1672
1673            for (int c = 0; c < sizeC; c++)
1674            {
1675                if (StringUtil.isEmpty(metadata.getChannelID(0, c)))
1676                    metadata.setChannelID(MetadataTools.createLSID("Channel", 0, c), 0, c);
1677                metadata.setChannelSamplesPerPixel(new PositiveInteger(Integer.valueOf(1)), 0, c);
1678                // metadata.getChannelName(0, c);
1679            }
1680        }
1681        else
1682        {
1683            // set channel number
1684            setNumChannel(metadata, 0, 1);
1685
1686            if (StringUtil.isEmpty(metadata.getChannelID(0, 0)))
1687                metadata.setChannelID(MetadataTools.createLSID("Channel", 0, 0), 0, 0);
1688            metadata.setChannelSamplesPerPixel(new PositiveInteger(Integer.valueOf(sizeC)), 0, 0);
1689        }
1690    }
1691
1692    /**
1693     * @deprecated Use {@link #setMetaData(OMEXMLMetadata, int, int, int, int, int, DataType, boolean)} instead
1694     */
1695    @Deprecated
1696    public static void setMetaData(OMEXMLMetadataImpl metadata, int sizeX, int sizeY, int sizeC, int sizeZ, int sizeT,
1697            DataType dataType, boolean separateChannel)
1698    {
1699        setMetaData((OMEXMLMetadata) metadata, sizeX, sizeY, sizeC, sizeZ, sizeT, dataType, separateChannel);
1700    }
1701
1702    /**
1703     * Generates meta data for the given image properties.
1704     * 
1705     * @param sizeX
1706     *        width in pixels.
1707     * @param sizeY
1708     *        height in pixels.
1709     * @param sizeC
1710     *        number of channel.
1711     * @param sizeZ
1712     *        number of Z slices.
1713     * @param sizeT
1714     *        number of T frames.
1715     * @param dataType
1716     *        data type.
1717     * @param separateChannel
1718     *        true if we want channel data to be separated.
1719     * @return OMEXMLMetadata
1720     * @throws ServiceException
1721     */
1722    public static OMEXMLMetadata generateMetaData(int sizeX, int sizeY, int sizeC, int sizeZ, int sizeT,
1723            DataType dataType, boolean separateChannel) throws ServiceException
1724    {
1725        final OMEXMLMetadata result = createMetadata("Sample");
1726
1727        setMetaData(result, sizeX, sizeY, sizeC, sizeZ, sizeT, dataType, separateChannel);
1728
1729        return result;
1730    }
1731
1732    /**
1733     * Generates Meta Data for the given arguments.
1734     * 
1735     * @see #setMetaData(OMEXMLMetadata, int, int, int, int, int, DataType, boolean)
1736     */
1737    public static OMEXMLMetadata generateMetaData(int sizeX, int sizeY, int sizeC, DataType dataType,
1738            boolean separateChannel) throws ServiceException
1739    {
1740        return generateMetaData(sizeX, sizeY, sizeC, 1, 1, dataType, separateChannel);
1741    }
1742
1743    /**
1744     * Generates Meta Data for the given BufferedImage.
1745     * 
1746     * @see #setMetaData(OMEXMLMetadata, int, int, int, int, int, DataType, boolean)
1747     */
1748    public static OMEXMLMetadata generateMetaData(IcyBufferedImage image, boolean separateChannel)
1749            throws ServiceException
1750    {
1751        return generateMetaData(image.getSizeX(), image.getSizeY(), image.getSizeC(), image.getDataType_(),
1752                separateChannel);
1753    }
1754
1755    /**
1756     * @deprecated Use {@link #generateMetaData(Sequence, boolean)} instead.
1757     */
1758    @Deprecated
1759    public static OMEXMLMetadata generateMetaData(Sequence sequence, boolean useZ, boolean useT,
1760            boolean separateChannel)
1761    {
1762        return generateMetaData(sequence, separateChannel);
1763    }
1764
1765    /**
1766     * @deprecated Use {@link #generateMetaData(Sequence, boolean)} instead.
1767     */
1768    @Deprecated
1769    public static OMEXMLMetadata generateMetaData(Sequence sequence, int sizeZ, int sizeT, boolean separateChannel)
1770    {
1771        return generateMetaData(sequence, separateChannel);
1772    }
1773
1774    /**
1775     * Generates Meta Data for the given Sequence.
1776     * 
1777     * @see #setMetaData(OMEXMLMetadata, int, int, int, int, int, DataType, boolean)
1778     */
1779    public static OMEXMLMetadata generateMetaData(Sequence sequence, boolean separateChannel)
1780    {
1781        // do a copy as we mean use several time the same source sequence metadata
1782        final OMEXMLMetadata result = OMEUtil.createOMEXMLMetadata(sequence.getOMEXMLMetadata());
1783
1784        setMetaData(result, sequence.getSizeX(), sequence.getSizeY(), sequence.getSizeC(), sequence.getSizeZ(),
1785                sequence.getSizeT(), sequence.getDataType_(), separateChannel);
1786
1787        return result;
1788    }
1789
1790    /**
1791     * Keep only the specified image series.
1792     */
1793    public static void keepSingleSerie(OMEXMLMetadata metaData, int num)
1794    {
1795        final OME ome = getOME(metaData);
1796        final int numSeries = ome.sizeOfImageList();
1797        final Image img = getSeries(metaData, num);
1798
1799        // nothing to do
1800        if (img == null)
1801            return;
1802
1803        // keep only the desired image
1804        for (int i = numSeries - 1; i >= 0; i--)
1805            if (i != num)
1806                ome.removeImage(ome.getImage(i));
1807
1808        final Set<Object> toKeep = new HashSet<Object>();
1809
1810        // try to keep associated dataset only
1811        toKeep.clear();
1812        for (int i = 0; i < img.sizeOfLinkedDatasetList(); i++)
1813            toKeep.add(img.getLinkedDataset(i));
1814        if (!toKeep.isEmpty())
1815        {
1816            for (int i = ome.sizeOfDatasetList() - 1; i >= 0; i--)
1817            {
1818                final Dataset obj = ome.getDataset(i);
1819                if (!toKeep.contains(obj))
1820                    ome.removeDataset(obj);
1821            }
1822        }
1823        // just assume they are indirectly linked
1824        else if (ome.sizeOfDatasetList() == numSeries)
1825        {
1826            for (int i = numSeries - 1; i >= 0; i--)
1827                if (i != num)
1828                    ome.removeDataset(ome.getDataset(i));
1829        }
1830
1831        // try to keep associated ROI only
1832        toKeep.clear();
1833        for (int i = 0; i < img.sizeOfLinkedROIList(); i++)
1834            toKeep.add(img.getLinkedROI(i));
1835        if (!toKeep.isEmpty())
1836        {
1837            for (int i = ome.sizeOfROIList() - 1; i >= 0; i--)
1838            {
1839                final ROI obj = ome.getROI(i);
1840                if (!toKeep.contains(obj))
1841                    ome.removeROI(obj);
1842            }
1843        }
1844        // just assume they are indirectly linked
1845        else if (ome.sizeOfROIList() == numSeries)
1846        {
1847            for (int i = numSeries - 1; i >= 0; i--)
1848                if (i != num)
1849                    ome.removeROI(ome.getROI(i));
1850        }
1851
1852        // try to keep associated experiment only
1853        final Experiment exp = img.getLinkedExperiment();
1854        if (exp != null)
1855        {
1856            for (int i = ome.sizeOfExperimentList() - 1; i >= 0; i--)
1857            {
1858                final Experiment obj = ome.getExperiment(i);
1859                if (obj != exp)
1860                    ome.removeExperiment(obj);
1861            }
1862        }
1863        else if (ome.sizeOfExperimentList() == numSeries)
1864        {
1865            for (int i = numSeries - 1; i >= 0; i--)
1866                if (i != num)
1867                    ome.removeExperiment(ome.getExperiment(i));
1868        }
1869
1870        // try to keep associated experimenter only
1871        final Experimenter expr = img.getLinkedExperimenter();
1872        if (expr != null)
1873        {
1874            for (int i = ome.sizeOfExperimenterList() - 1; i >= 0; i--)
1875            {
1876                final Experimenter obj = ome.getExperimenter(i);
1877                if (obj != expr)
1878                    ome.removeExperimenter(obj);
1879            }
1880        }
1881        else if (ome.sizeOfExperimenterList() == numSeries)
1882        {
1883            for (int i = numSeries - 1; i >= 0; i--)
1884                if (i != num)
1885                    ome.removeExperimenter(ome.getExperimenter(i));
1886        }
1887
1888        // try to keep associated experimenter group only
1889        final ExperimenterGroup exprGroup = img.getLinkedExperimenterGroup();
1890        if (exprGroup != null)
1891        {
1892            for (int i = ome.sizeOfExperimenterGroupList() - 1; i >= 0; i--)
1893            {
1894                final ExperimenterGroup obj = ome.getExperimenterGroup(i);
1895                if (obj != exprGroup)
1896                    ome.removeExperimenterGroup(obj);
1897            }
1898        }
1899        else if (ome.sizeOfExperimenterGroupList() == numSeries)
1900        {
1901            for (int i = numSeries - 1; i >= 0; i--)
1902                if (i != num)
1903                    ome.removeExperimenterGroup(ome.getExperimenterGroup(i));
1904        }
1905
1906        // try to keep associated instrument only
1907        final Instrument instr = img.getLinkedInstrument();
1908        if (instr != null)
1909        {
1910            for (int i = ome.sizeOfInstrumentList() - 1; i >= 0; i--)
1911            {
1912                final Instrument obj = ome.getInstrument(i);
1913                if (obj != instr)
1914                    ome.removeInstrument(obj);
1915            }
1916        }
1917        else if (ome.sizeOfInstrumentList() == numSeries)
1918        {
1919            for (int i = numSeries - 1; i >= 0; i--)
1920                if (i != num)
1921                    ome.removeInstrument(ome.getInstrument(i));
1922        }
1923
1924        // others misc data to clean
1925        if (ome.sizeOfPlateList() == numSeries)
1926        {
1927            for (int i = numSeries - 1; i >= 0; i--)
1928                if (i != num)
1929                    ome.removePlate(ome.getPlate(i));
1930        }
1931        if (ome.sizeOfProjectList() == numSeries)
1932        {
1933            for (int i = numSeries - 1; i >= 0; i--)
1934                if (i != num)
1935                    ome.removeProject(ome.getProject(i));
1936        }
1937        if (ome.sizeOfScreenList() == numSeries)
1938        {
1939            for (int i = numSeries - 1; i >= 0; i--)
1940                if (i != num)
1941                    ome.removeScreen(ome.getScreen(i));
1942        }
1943    }
1944
1945    /**
1946     * Keep only the specified plane metadata.
1947     */
1948    public static void keepSinglePlane(Image img, int index)
1949    {
1950        final Pixels pix = img.getPixels();
1951        if (pix == null)
1952            return;
1953
1954        final int numPlane = pix.sizeOfPlaneList();
1955        final Plane plane = getPlane(pix, index);
1956
1957        // keep only the desired plane
1958        for (int i = numPlane - 1; i >= 0; i--)
1959        {
1960            if (i != index)
1961                pix.removePlane(pix.getPlane(i));
1962        }
1963
1964        final Set<Object> toKeep = new HashSet<Object>();
1965
1966        // try to keep associated annotation only
1967        toKeep.clear();
1968        for (int i = 0; i < plane.sizeOfLinkedAnnotationList(); i++)
1969            toKeep.add(plane.getLinkedAnnotation(i));
1970        if (!toKeep.isEmpty())
1971        {
1972            for (int i = img.sizeOfLinkedAnnotationList() - 1; i >= 0; i--)
1973            {
1974                final Annotation obj = img.getLinkedAnnotation(i);
1975                if (!toKeep.contains(obj))
1976                    img.unlinkAnnotation(obj);
1977            }
1978        }
1979        // just assume they are indirectly linked
1980        else if (img.sizeOfLinkedAnnotationList() == numPlane)
1981        {
1982            for (int i = numPlane - 1; i >= 0; i--)
1983                if (i != index)
1984                    img.unlinkAnnotation(img.getLinkedAnnotation(i));
1985        }
1986
1987        // clean some data
1988        if (pix.sizeOfBinDataList() == numPlane)
1989        {
1990            for (int i = numPlane - 1; i >= 0; i--)
1991                if (i != index)
1992                    pix.removeBinData(pix.getBinData(i));
1993        }
1994        if (pix.sizeOfTiffDataList() == numPlane)
1995        {
1996            for (int i = numPlane - 1; i >= 0; i--)
1997                if (i != index)
1998                    pix.removeTiffData(pix.getTiffData(i));
1999        }
2000    }
2001
2002    /**
2003     * @deprecated Use {@link #keepSingleSerie(OMEXMLMetadata, int)} instead
2004     */
2005    @Deprecated
2006    public static void keepSingleSerie(OMEXMLMetadataImpl metaData, int num)
2007    {
2008        keepSingleSerie((OMEXMLMetadata) metaData, num);
2009    }
2010
2011    /**
2012     * Keep only plane(s) at specified C, Z, T position from the given metadata.
2013     * 
2014     * @param img
2015     *        image metadata to clean plane from
2016     * @param posT
2017     *        keep Plane at given T position (-1 to keep all)
2018     * @param posZ
2019     *        keep Plane at given Z position (-1 to keep all)
2020     * @param posC
2021     *        keep Plane at given C position (-1 to keep all)
2022     */
2023    public static void keepPlanes(Image img, int posT, int posZ, int posC)
2024    {
2025        final Pixels pix = img.getPixels();
2026        if (pix == null)
2027            return;
2028
2029        final int sizeT = OMEUtil.getValue(pix.getSizeT(), 0);
2030        final int sizeZ = OMEUtil.getValue(pix.getSizeZ(), 0);
2031        final int sizeC = OMEUtil.getValue(pix.getSizeC(), 0);
2032
2033        for (int t = 0; t < sizeT; t++)
2034        {
2035            final boolean removeT = (posT != -1) && (posT != t);
2036
2037            for (int z = 0; z < sizeZ; z++)
2038            {
2039                final boolean removeZ = (posZ != -1) && (posZ != z);
2040
2041                for (int c = 0; c < sizeC; c++)
2042                {
2043                    final boolean removeC = (posC != -1) && (posC != c);
2044
2045                    if (removeT || removeZ || removeC)
2046                        removePlane(img, t, z, c);
2047                }
2048            }
2049        }
2050    }
2051
2052    /**
2053     * Keep only plane(s) at specified C, Z, T position from the given metadata.
2054     * 
2055     * @param posT
2056     *        keep Plane at given T position (-1 to keep all)
2057     * @param posZ
2058     *        keep Plane at given Z position (-1 to keep all)
2059     * @param posC
2060     *        keep Plane at given C position (-1 to keep all)
2061     */
2062    public static void keepPlanes(OMEXMLMetadata metadata, int series, int posT, int posZ, int posC)
2063    {
2064        final Image img = getSeries(metadata, series);
2065
2066        if (img != null)
2067            keepPlanes(img, posT, posZ, posC);
2068    }
2069
2070    /**
2071     * Clean plane(s) which are outside the pixel sizeC / sizeZ and sizeT.
2072     * 
2073     * @param img
2074     *        image metadata to clean plane from
2075     */
2076    public static void cleanPlanes(Image img)
2077    {
2078        final Pixels pix = img.getPixels();
2079        if (pix == null)
2080            return;
2081
2082        final int sizeT = OMEUtil.getValue(pix.getSizeT(), 0);
2083        final int sizeZ = OMEUtil.getValue(pix.getSizeZ(), 0);
2084        final int sizeC = OMEUtil.getValue(pix.getSizeC(), 0);
2085        if ((sizeT < 1) || (sizeZ < 1) || (sizeC < 1))
2086            return;
2087
2088        // get allowed maximum plane
2089        final int allowedMaxPlaneIndex = getPlaneIndex(pix, sizeT - 1, sizeZ - 1, sizeC - 1);
2090        // current number of plane
2091        int maxPlaneIndex = pix.sizeOfPlaneList() - 1;
2092
2093        // remove plan outside allowed region
2094        while (maxPlaneIndex > allowedMaxPlaneIndex)
2095            removePlane(img, maxPlaneIndex--);
2096    }
2097
2098    /**
2099     * Clean TiffData packet
2100     * 
2101     * @param img
2102     *        image metadata to clean TiffData from
2103     */
2104    public static void cleanTiffData(Image img)
2105    {
2106        final Pixels pix = img.getPixels();
2107        if (pix == null)
2108            return;
2109
2110        while (pix.sizeOfTiffDataList() > 0)
2111            pix.removeTiffData(pix.getTiffData(pix.sizeOfTiffDataList() - 1));
2112    }
2113
2114    /**
2115     * Clean BinData packet
2116     * 
2117     * @param img
2118     *        image metadata to clean BinData from
2119     */
2120    public static void cleanBinData(Image img)
2121    {
2122        final Pixels pix = img.getPixels();
2123        if (pix == null)
2124            return;
2125
2126        while (pix.sizeOfBinDataList() > 0)
2127            pix.removeBinData(pix.getBinData(pix.sizeOfBinDataList() - 1));
2128    }
2129
2130    /**
2131     * Cleanup the meta data (sometime we have empty data structure sitting there)
2132     */
2133    public static void clean(OMEXMLMetadata metaData)
2134    {
2135        final OME ome = getOME(metaData);
2136        final StructuredAnnotations annotations = ome.getStructuredAnnotations();
2137
2138        if (annotations != null)
2139        {
2140            for (int i = annotations.sizeOfXMLAnnotationList() - 1; i >= 0; i--)
2141            {
2142                final XMLAnnotation xmlAnnotation = annotations.getXMLAnnotation(i);
2143
2144                if (isEmpty(xmlAnnotation))
2145                    annotations.removeXMLAnnotation(xmlAnnotation);
2146            }
2147        }
2148    }
2149
2150    /**
2151     * @deprecated Use {@link #clean(OMEXMLMetadata)} instead.
2152     */
2153    @Deprecated
2154    public static void clean(OMEXMLMetadataImpl metaData)
2155    {
2156        clean((OMEXMLMetadata) metaData);
2157    }
2158
2159    /**
2160     * Returns <code>true</code> if the specified XML annotation are empty.
2161     */
2162    public static boolean isEmpty(XMLAnnotation xmlAnnotation)
2163    {
2164        return StringUtil.isEmpty(xmlAnnotation.getDescription()) && StringUtil.isEmpty(xmlAnnotation.getValue());
2165    }
2166}