Extracting all series from proprietary files into individual OME-TIFF files (without connections to each other)

Hi,
I’m trying to save one OME-TIFF file for each series that is in a file. I’m loading the proprietary format and then want to save individual OME-TIFFs - that are “not connected anymore”.

I checked the code in “.\bioformats\components\bio-formats-tools\src\loci\formats\tools\ImageConverter.java” (which converts everything or only one series)
and some previous topics (e.g. Set Bioformats metadata on existing TIFF) but haven’t been succesfull to get something working.
(Looping through the converter tool (bfconvert) on the command line for each series is not my preferred approach.)

Below I have some code that compiles and runs for the first series, but then hits an exception when access the “original” root again. The OME-TIFF for the first series is also not complete and fails to be opened. It seems I’m missing something important here!

I create the newRoot and remove images from it, but this might also change the original root?

(Note: calling Bioformats from C# code)

        private void ReadWrite(string inId, string outId, Action<int, int> callBackProgress = null)
        {
            this.IsCanceled = false;

            var factory = new ServiceFactory();
            var service = (OMEXMLService)factory.getInstance(typeof(OMEXMLService));
            var omeMeta = service.createOMEXMLMetadata();

            using (var reader = new ImageReader())
            {
                reader.setMetadataStore(omeMeta);
                reader.setId(inId);
                var numberSeries = reader.getSeriesCount();

                for (int seriesIndex = 0; seriesIndex < numberSeries; seriesIndex++)
                {
                    using (var writer = new ImageWriter())
                    {
                        string fileName = omeMeta.getImageName(seriesIndex);

                        reader.setSeries(seriesIndex);
                        var imageCount = reader.getImageCount();
                        if (seriesIndex >= 0)
                        {
                            OMEXMLMetadataRoot root = (OMEXMLMetadataRoot) omeMeta.getRoot();
                            Image exportImage = new Image(root.getImage(seriesIndex));
                            Pixels exportPixels = new Pixels(root.getImage(seriesIndex).getPixels());
                            exportImage.setPixels(exportPixels);
                            OMEXMLMetadataRoot newRoot = (OMEXMLMetadataRoot) omeMeta.getRoot();

                            while (newRoot.sizeOfImageList() > 0)
                            {
                                newRoot.removeImage(newRoot.getImage(0));
                            }

                            while (newRoot.sizeOfPlateList() > 0)
                            {
                                newRoot.removePlate(newRoot.getPlate(0));
                            }

                            newRoot.addImage(exportImage);

                            var omeMeta2 = service.createOMEXMLMetadata();
                            omeMeta2.setRoot(newRoot);
                            writer.setMetadataRetrieve((loci.formats.meta.MetadataRetrieve) omeMeta2);
                        }

                        writer.setSeries(0);
                        writer.setWriteSequentially(true);

                        fileName = omeMeta.getImageName(seriesIndex);
                        string outFileName = Path.GetDirectoryName(outId) + "\\" + fileName + ".ome.tif";
                        outFileName = outFileName.Replace("/", "_");
                        writer.changeOutputFile(outFileName);

                        byte[] planeBytes = null;
                        for (var i = 0; i < imageCount; i++)
                        {
                            if (planeBytes == null)
                            {
                                planeBytes = reader.openBytes(i);
                            }
                            else
                            {
                                reader.openBytes(i, planeBytes);
                            }

                            if (this.IsCanceled)
                            {
                                break;
                            }

                            writer.saveBytes(i, planeBytes);
                            callBackProgress?.Invoke(i, imageCount);
                        }
                    }
                }
            }
        }

I was also wondering how to address such a use-case (being: save all series in a separate OME-TIFF file - removing all connections between the files.)
Is there another way of making this work? (Converting only the Metadata belonging to one Image series to OME-XML from the original Metadata?)

Take a look at this thread.
https://www.openmicroscopy.org/community/viewtopic.php?f=13&t=8632
NOTE: It may not longer be active, because OME forums moved officially here to image.sc after that post was opened.

In that thread, I had two simultaneous problems. One, which concerns mainly to the first posts, may not be relevant for you… But the other is applicable, I believe. Study the code posted there, mainly the trick of the OME Root, plus the idea of removing and adding back images’ OME metadata.

Sorry, mate :man_facepalming: I must confess I focused on your problem description and only looked very quickly at your code… Now I realize you’re already doing what I suggest. I’ll try to go deeper later today, perhaps I can help

1 Like

Thank you anyway! I wasn’t aware of that thread and will try to tackle it once more with information from there!

If this is your suspicion (and it seems a reasonable one to me), you can try to do a duplicate of the OME Metadata, then get its Root:

omeMetaDuplic = OMEXMLServiceImpl().createOMEXMLMetadata(omeMetaOrig.dumpXML());

This code of mine is in Jython (but it should be straightforward to translate to C#). I found it a kind-of-brute-force way to copy the omeMeta by value instead of by reference.
Hope it helps!

1 Like

The issue with the original root is indeed because it is being modified. Instead you will have to duplicate it as mentioned, or as you are only really interested in the data for a single image then creating a new empty root and only adding the desired image should also work. I have modified the code you provided (converted to Java for testing), so the below should run without exception and generate a separate image for each series:

    ServiceFactory factory = new ServiceFactory();
    OMEXMLService service = (OMEXMLService)factory.getInstance(OMEXMLService.class);
    OMEXMLMetadata omeMeta = service.createOMEXMLMetadata();

    ImageReader reader = new ImageReader();
    reader.setMetadataStore(omeMeta);
    reader.setId(inId);
    int numberSeries = reader.getSeriesCount();

    for (int seriesIndex = 0; seriesIndex < numberSeries; seriesIndex++) {
       ImageWriter writer = new ImageWriter();
       String fileName = omeMeta.getImageName(seriesIndex);
       reader.setSeries(seriesIndex);
       int imageCount = reader.getImageCount();
       
       OMEXMLMetadataRoot root = (OMEXMLMetadataRoot) omeMeta.getRoot();
       OMEXMLMetadataRoot newRoot = new OMEXMLMetadataRoot();
       Image exportImage = new Image(root.getImage(seriesIndex));
       Pixels exportPixels = new Pixels(root.getImage(seriesIndex).getPixels());
       exportImage.setPixels(exportPixels);

       newRoot.addImage(exportImage);
       OMEXMLMetadata omeMeta2 = service.createOMEXMLMetadata();
       omeMeta2.setRoot(newRoot);
       writer.setMetadataRetrieve((loci.formats.meta.MetadataRetrieve) omeMeta2);

       fileName = omeMeta.getImageName(seriesIndex);
       String outFileName = outId + fileName + "-" + seriesIndex + ".ome.tif";
       writer.setId(outFileName);
       
       writer.setSeries(0);
       writer.setWriteSequentially(true);

       byte[] planeBytes = null;
       for (int i = 0; i < imageCount; i++)
       {
          if (planeBytes == null)
          {
             planeBytes = reader.openBytes(i);
          }
          else
          {
             reader.openBytes(i, planeBytes);
          }
          writer.saveBytes(i, planeBytes);
        }
       writer.close();
     }
     reader.close();
3 Likes

That’s great! Thank you both! I will give it a try and update this thread with the outcome.