# HG changeset patch
# User lldelisle
# Date 1702998161 0
# Node ID e1cba36becb2872ad9086fc4d4678278afa53fc4
planemo upload for repository https://github.com/lldelisle/tools-lldelisle/tree/master/tools/incucyte_stack_and_upload_omero commit 4ac9b1d66ba6857357867c8eccb6c9d1ad603364
diff -r 000000000000 -r e1cba36becb2 README.md
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md Tue Dec 19 15:02:41 2023 +0000
@@ -0,0 +1,9 @@
+I don't know why but I need to run the macro once on GUI before being able to run it in CLI...
+```bash
+ssh -Y updubsrv1
+. '/data/galaxy/galaxy/var/dependencies/_conda/bin/activate' /data/galaxy/galaxy/var/dependencies/_conda/envs/mulled-v1-db2fcfa8be636326417c50715ab9fec065adbfde66cca33b4958bee1dd8ad945/
+sudo chmod -R 777 /data/galaxy/galaxy/var/dependencies/_conda/envs/mulled-v1-db2fcfa8be636326417c50715ab9fec065adbfde66cca33b4958bee1dd8ad945/bin/../uprefs/
+ImageJ
+# I need to check what changed in uprefs
+
+```
diff -r 000000000000 -r e1cba36becb2 incucyte_stack_and_upload_omero.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/incucyte_stack_and_upload_omero.xml Tue Dec 19 15:02:41 2023 +0000
@@ -0,0 +1,303 @@
+
+ And upload to omero
+
+ 20231219
+ 0
+
+
+ Fiji
+ omero-py
+ gawk
+
+ Preferences -> Manage Information" 1>&2 &&
+ exit 1 &&
+ #end if
+ #set $incucyteXMLFile = str($upload_omero.incucyteXMLFile)
+ #set $objectiveChoice = str($upload_omero.objectiveChoice)
+ #set $startStage = str($upload_omero.startStage)
+ #set $plateName = str($upload_omero.plateName)
+ #set $ignoreConcentration = str($upload_omero.ignoreConcentration)
+ #set $ignorePassage = str($upload_omero.ignorePassage)
+ #set $ignoreSeeding = str($upload_omero.ignoreSeeding)
+ #else
+ #set $incucyteXMLFile = "inexisting.plateMap"
+ #set $objectiveChoice = "4x"
+ #set $startStage = "72"
+ #set $plateName = "Experiment:0"
+ #set $ignoreConcentration = "true"
+ #set $ignorePassage = "true"
+ #set $ignoreSeeding = "true"
+ #end if
+
+ ## Prefix to directories provided by users
+ #set $prefix = "/data/mount_s3/image_storage/"
+ ## Prepare input and output directories to match groovy expectations
+ mkdir input &&
+ mkdir output &&
+ #if str($structure.structure_type) == "directory":
+ #set $original_file = ""
+ #if str($structure.phase_dir_s3) != "":
+ #if not os.path.isdir($prefix + "/" + str($structure.phase_dir_s3)):
+ echo "'${prefix}/${structure.phase_dir_s3}'" &&
+ echo "Phase dir $structure.phase_dir_s3 does not exists" &&
+ exit 1 &&
+ #end if
+ mkdir input/Phase/ &&
+ ln -s '${prefix}/${structure.phase_dir_s3}/'*'${structure.pattern}'* input/Phase/ &&
+ #set $original_file = str($structure.phase_dir_s3)
+ #end if
+ #if str($structure.green_dir_s3) != "":
+ #if not os.path.isdir($prefix + "/" + str($structure.green_dir_s3)):
+ echo "Green dir $structure.green_dir_s3 does not exists" &&
+ exit 1 &&
+ #end if
+ mkdir input/Green/ &&
+ ln -s '${prefix}/${structure.green_dir_s3}/'*'${structure.pattern}'* input/Green/ &&
+ #set $original_file = $original_file + "," + str($structure.green_dir_s3)
+ #end if
+ #if str($structure.red_dir_s3) != "":
+ #if not os.path.isdir($prefix + "/" + str($structure.red_dir_s3)):
+ echo "Red dir $structure.red_dir_s3 does not exists" &&
+ exit 1 &&
+ #end if
+ mkdir input/Red/ &&
+ ln -s '${prefix}/${structure.red_dir_s3}/'*'${structure.pattern}'* input/Red/ &&
+ #set $original_file = $original_file + "," + str($structure.red_dir_s3)
+ #end if
+ #else:
+ #if not os.path.isdir($prefix + "/" + str($structure.base_dir)):
+ echo "Base dir $structure.base_dir does not exists" &&
+ exit 1 &&
+ #end if
+ #set $original_file = str($structure.base_dir)
+ #if str($structure.phase_key) != "":
+ mkdir input/Phase/ &&
+ ln -s '${prefix}/${structure.base_dir}/'*'${structure.phase_key}'* input/Phase/ &&
+ #end if
+ #if str($structure.green_key) != "":
+ mkdir input/Green/ &&
+ ln -s '${prefix}/${structure.base_dir}/'*'${structure.green_key}'* input/Green/ &&
+ #end if
+ #if str($structure.red_key) != "":
+ mkdir input/Red/ &&
+ ln -s '${prefix}/${structure.base_dir}/'*'${structure.red_key}'* input/Red/ &&
+ #end if
+ #end if
+ ## Run the groovy
+ ImageJ --ij2 --headless --console --run '$__tool_directory__/'stack_buildXml.groovy
+ 'base_dir="input/",output_dir="output/",incucyteXMLFile="$incucyteXMLFile",objectiveChoice="$objectiveChoice",nWells="${nWells}",n_images_per_well="${n_images_per_well}",plateName="$plateName",commonKeyValues="original_file=${original_file};start_stage=$startStage",xmlName="$plateName",ignoreConcentration="$ignoreConcentration",ignorePassage="$ignorePassage",ignoreSeeding="$ignoreSeeding"' > output.log
+ ## Upload to omero
+ #if str($upload_omero.upload_omero_select) == "yes":
+ && bash '$__tool_directory__/'upload_omero.sh '$upload_omero.omero_host' '$credentials' '$upload_omero.screen.screen_create' '$upload_omero.screen.screenNameOrID' >> output.log
+ ## Copy the companion.ome
+ && cp output/*.companion.ome output.companion.ome
+ #end if
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ^[a-zA-Z0-9._-]*$
+ '..' not in value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ upload_omero['get_stacks_in_galaxy']
+
+
+ upload_omero['get_stacks_in_galaxy'] and upload_omero['upload_omero_select'] == "yes"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %%
+
+ **************************************************
+ Relative to the rest of the script *
+ **************************************************
+
+ * = AUTHOR INFORMATION =
+ Code written by Rémy Dornier, EPFL - SV - PTECH - BIOP
+ and Romain Guiet, EPFL - SV - PTECH - BIOP
+ and Lucille Delisle, EPFL - SV - UPDUB
+ and Pierre Osteil, EPFL - SV - UPDUB
+
+ Last modification: 2023-08-24
+
+ = COPYRIGHT =
+ © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2023
+
+ Licensed under the BSD-3-Clause License:
+ 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.
+ 3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ 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.
+ ]]>
+
\ No newline at end of file
diff -r 000000000000 -r e1cba36becb2 stack_buildXml.groovy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stack_buildXml.groovy Tue Dec 19 15:02:41 2023 +0000
@@ -0,0 +1,940 @@
+/*
+ ****************************************************
+ * Relative to the generation of the .companion.ome *
+ ****************************************************
+ * #%L
+ * BSD implementations of Bio-Formats readers and writers
+ * %%
+ * The functions buildXML, makeImage, makePlate, postProcess and asString has been modified and adapted from
+ * https://github.com/ome/bioformats/blob/master/components/formats-bsd/test/loci/formats/utests/SPWModelMock.java
+ *
+ * Copyright (C) 2005 - 2015 Open Microscopy Environment:
+ * - Board of Regents of the University of Wisconsin-Madison
+ * - Glencoe Software, Inc.
+ * - University of Dundee
+ *
+ * @author Chris Allan
+ * %%
+ *
+ ****************************************************
+ * Relative to the rest of the script *
+ ****************************************************
+ *
+ * * = AUTHOR INFORMATION =
+ * Code written by Rémy Dornier, EPFL - SV - PTECH - BIOP
+ * and Romain Guiet, EPFL - SV - PTECH - BIOP
+ * and Lucille Delisle, EPFL - SV - UPDUB
+ * and Pierre Osteil, EPFL - SV - UPDUB
+ *
+ * Last modification: 2023-12-19
+ *
+ * = COPYRIGHT =
+ * © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2023
+ *
+ * Licensed under the BSD-3-Clause License:
+ * 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.
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 purpose of this script is to combine a series of time-lapse images into
+ * one file per well/field with possibly multiple channels and multiple time points
+ * and in addition create a .companion.ome file to create an OMERO plate object,
+ * with a single image per well/field. This .companion.ome file can be directly uploaded on OMERO via
+ * OMERO.insight software or CLI, with screen import option.
+ *
+ * To make the script run
+ * 1. Create a parent folder (base_dir) and a output folder (output_dir)
+ * 2. Create a dir *Phase, *Green, *Red with the corresponding channels
+ * 3. The image names must contains a prefix followed by '_', the name of the well (no 0-pad) followed by '_', followed by the field id followed by '_', the date of the acquisition in YYYYyMMmDDdHHhMMm and the extension '.tif'
+ * 4. The images can be either regular tif or the raw tif from Incucyte which contains multiple series.
+ * 5. You must provide the path of the Incucyte XML file to populate key values
+ *
+ * The expected outputs are:
+ * 1. In the output_dir one tiff per well/field (multi-T and potentially multi-C)
+ * 2. In the output_dir a .companion.ome
+ */
+
+#@ File(style="directory", label="Directory with up to 3 subdirectories ending by Green, Phase and/or Red") base_dir
+#@ File(label="Incucyte XML File (plateMap)") incucyteXMLFile
+#@ File(style="directory", label="Output directory (must exist)") output_dir
+#@ String(label="Final XML file name", value="Test") xmlName
+#@ String(label="Number of well in plate", choices={"96", "384"}, value="96") nWells
+#@ Integer(label="Maximum number of images per well", value=1, min=1) n_images_per_well
+#@ String(label="Objective", choices={"4x","10x","20x"}) objectiveChoice
+#@ String(label="Plate name", value="Experiment:0") plateName
+#@ String(label="common Key Values formatted as key1=value1;key2=value2", value="") commonKeyValues
+#@ Boolean(label="Ignore Compound concentration from plateMap", value=true) ignoreConcentration
+#@ Boolean(label="Ignore Cell passage number from plateMap", value=true) ignorePassage
+#@ Boolean(label="Ignore Cell seeding concentration from plateMap", value=true) ignoreSeeding
+
+
+/**
+ * *****************************************************************************************************************
+ * ********************************************* Final Variables **************************************************
+ * ********************************************* DO NOT MODIFY ****************************************************
+ * ****************************************************************************************************************
+ */
+
+/** objectives and respective pixel sizes */
+objective = 0
+objectives = new String[]{"4x", "10x", "20x"}
+pixelSizes = new double[]{2.82, 1.24, 0.62}
+
+/** pattern for date */
+REGEX_FOR_DATE = ".*_([0-9]{4})y([0-9]{2})m([0-9]{2})d_([0-9]{2})h([0-9]{2})m.tif"
+
+ALTERNATIVE_REGEX_FOR_DATE = ".*_([0-9]{2})d([0-9]{2})h([0-9]{2})m.tif"
+
+/** Image properties keys */
+DIMENSION_ORDER = "dimension_order"
+FILE_NAME = "file_name"
+IMG_POS_IN_WELL = "img_pos_in_well"
+FIRST_ACQUISITION_DATE = "acquisition_date"
+FIRST_ACQUISITION_TIME = "acquisition_time"
+RELATIVE_ACQUISITION_HOUR = "relative_acquisition_hour"
+
+/** global variable for index to letter conversion */
+LETTERS = new String("ABCDEFGHIJKLMNOP")
+
+// Version number = date of last modif
+VERSION = "20231219"
+
+/** Key-Value pairs namespace */
+GENERAL_ANNOTATION_NAMESPACE = "openmicroscopy.org/omero/client/mapAnnotation"
+annotations = new StructuredAnnotations()
+
+/** Plate details and conventions */
+PLATE_ID = "Plate:0"
+PLATE_NAME = plateName
+
+if (nWells == "96") {
+ nRows = 8
+ nCols = 12
+} else if (nWells == "384") {
+ nRows = 16
+ nCols = 24
+}
+
+WELL_ROWS = new PositiveInteger(nRows)
+WELL_COLS = new PositiveInteger(nCols)
+WELL_ROW = NamingConvention.LETTER
+WELL_COL = NamingConvention.NUMBER
+
+/** XML namespace. */
+XML_NS = "http://www.openmicroscopy.org/Schemas/OME/2010-06"
+
+/** XSI namespace. */
+XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"
+
+/** XML schema location. */
+SCHEMA_LOCATION = "http://www.openmicroscopy.org/Schemas/OME/2010-06/ome.xsd"
+
+
+/**
+ * *****************************************************************************************************************
+ * **************************************** Beginning of the script ***********************************************
+ * ****************************************************************************************************************
+ */
+
+try {
+
+ println "Beginning of the script"
+
+ /**
+ * Prepare list of wells name
+ */
+ String[] well = []
+
+ well = [(0..(nRows - 1)),(0..(nCols - 1))].combinations().collect{ r,c -> LETTERS.substring(r, r + 1) +""+ (c+ 1).toString() }
+
+ IJ.run("Close All", "")
+
+ // loop for all the wells
+
+ // Store all merged ImagePlus into a HashMap where
+ // keys are well name (A1, A10)
+ // values are a list of ImagePlus corresponding to different field of view
+ Map> wellSamplesMap = new HashMap<>()
+
+ well.each{ input ->
+ IJ.run("Close All", "")
+
+ List final_imp_list = process_well(base_dir, input, n_images_per_well) //, perform_bc, mediaChangeTime )
+ if (!final_imp_list.isEmpty()) {
+ wellSamplesMap.put(input, final_imp_list)
+ for(ImagePlus final_imp : final_imp_list){
+ final_imp.setTitle(input+"_"+final_imp.getProperty(IMG_POS_IN_WELL))
+ //final_imp.show()
+
+ def fs = new FileSaver(final_imp)
+ File output_path = new File (output_dir ,final_imp.getTitle()+"_merge.tif" )
+ fs.saveAsTiff(output_path.toString() )
+ final_imp.setProperty(FILE_NAME, output_path.getName())
+
+ IJ.run("Close All", "")
+ }
+ } else {
+ println "No match for " + input
+ }
+ }
+
+
+ // get folder and xml file path
+ output_dir_abs = output_dir.getAbsolutePath()
+ incucyteXMLFilePath = incucyteXMLFile.getAbsolutePath()
+
+ if (! new File(incucyteXMLFilePath).exists()) {
+ println "The incucyte file does not exists"
+ return
+ }
+
+ // select the right objective
+ switch (objectiveChoice){
+ case "4x":
+ objective = 0
+ break
+ case "10x":
+ objective = 1
+ break
+ case "20x":
+ objective = 2
+ break
+ }
+
+ // get plate scheme as key-values
+ Map> keyValuesPerWell = parseIncucyteXML(incucyteXMLFilePath, ignoreConcentration, ignorePassage, ignoreSeeding)
+
+ // get global key-values
+ List globalKeyValues = getGlobalKeyValues(objective, commonKeyValues)
+ double pixelSize = pixelSizes[objective]
+
+ // generate OME-XML metadata file
+ OME ome = buildXMLFile(wellSamplesMap, keyValuesPerWell, globalKeyValues, pixelSize)
+
+ // create XML document
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
+ DocumentBuilder parser = factory.newDocumentBuilder()
+ Document document = parser.newDocument()
+
+ // Produce a valid OME DOM element hierarchy
+ Element root = ome.asXMLElement(document)
+ postProcess(root, document)
+
+ // Produce string XML
+ try(OutputStream outputStream = new FileOutputStream(output_dir_abs + File.separator + xmlName + ".companion.ome")){
+ outputStream.write(asString(document).getBytes())
+ } catch(Exception e){
+ e.printStackTrace()
+ }
+ println "End of the script"
+
+} catch (Throwable e) {
+ println("Something went wrong: " + e)
+ e.printStackTrace()
+ throw e
+
+ if (GraphicsEnvironment.isHeadless()){
+ // Force to give exit signal of error
+ System.exit(1)
+ }
+
+}
+
+return
+
+/**
+ * ****************************************************************************************************************
+ * ******************************************* End of the script **************************************************
+ *
+ * ****************************************************************************************************************
+ *
+ * *********************************** Helpers and processing methods *********************************************
+ * ***************************************************************************************************************
+ */
+
+def process_well(baseDir, input_wellId, n_image_per_well){ //, perform_bc, mediaChangeTime){
+ File bf_dir = baseDir.listFiles().find{ it =~ /.*Phase.*/}
+ File green_dir = baseDir.listFiles().find{ it =~ /.*Green.*/}
+ File red_dir = baseDir.listFiles().find{ it =~ /.*Red.*/}
+ //if (verbose) println bf_dir
+ //if (verbose) println green_dir
+
+ // The images are stored in a TreeMap where
+ // keys are wellSampleId = field identifier
+ // values are a TreeMap that we call channelMap where:
+ // keys are colors (Green, Grays, Red)
+ // values are an ImagePlus (T-stack)
+ Map> sampleToChannelMap = new TreeMap<>()
+
+ List folder_list = [bf_dir, green_dir, red_dir]
+ List channels_list = ["Grays", "Green", "Red"]
+
+ // loop over the field and open images
+ for(int wellSampleId = 1; wellSampleId <= n_image_per_well; wellSampleId++) {
+ // nT is the number of time-points for the well input_wellId
+ int nT = 0
+ String first_channel = ""
+ String first_acq_date = ""
+ String first_acq_time = ""
+ String rel_acq_hour = ""
+
+ // Initiate a channel map for the wellSampleId
+ Map channelMap = new TreeMap<>()
+
+ // Checking if there are images in the corresponding dir
+ // which corresponds to the input_wellId
+ // and to the wellSampleId
+ // The image name should be:
+ // Prefix + "_" + input_wellId + "_" + wellSampleId + "_" + year (4 digits) + "y" + month (2 digits) + "m" + day + "d_" + hour + "h" + minute + "m.tif"
+ FileFilter fileFilter = new WildcardFileFilter("*_" + input_wellId + "_" + wellSampleId + "_*")
+ for(int i = 0; i < folder_list.size(); i++){
+ if (folder_list.get(i) != null) {
+ File[] files_matching = folder_list.get(i).listFiles(fileFilter as FileFilter).sort()
+ if (files_matching.size() != 0) {
+ // In order to deal with raw images from Incucyte which are
+ // Multi series images
+ // We open the first image
+ ImagePlus first_imp = Opener.openUsingBioFormats(files_matching[0].getAbsolutePath())
+ // We check if we can read the infos
+ first_image_infos = Opener.getTiffFileInfo(files_matching[0].getAbsolutePath())
+ // We define the imageplus object
+ ImagePlus single_channel_imp
+ if (first_image_infos == null) {
+ // They are raw from incucyte
+ // We need to open images one by one and add them to the stack
+ ImageStack stack = new ImageStack(first_imp.width, first_imp.height);
+ files_matching.each{
+ ImagePlus single_imp = (new Opener()).openUsingBioFormats(it.getAbsolutePath())
+ String new_title = single_imp.getTitle().split(" - ")[0]
+ stack.addSlice(new_title, single_imp.getProcessor())
+ }
+ single_channel_imp = new ImagePlus(FilenameUtils.getBaseName(folder_list.get(i).getAbsolutePath()), stack);
+ } else {
+ // They are regular tif file
+ // We can use FolderOpener to create the stack at once
+ single_channel_imp = FolderOpener.open(folder_list.get(i).getAbsolutePath(), " filter=_"+ input_wellId + "_"+wellSampleId+"_")
+ }
+ // Phase are 8-bit and need to be changed to 16-bit
+ // Other are already 16-bit but it does not hurt
+ IJ.run(single_channel_imp, "16-bit", "")
+
+ // check frame size
+ if (nT == 0) {
+ // This is the first channel with images
+ nT = single_channel_imp.getNSlices()
+ first_channel = channels_list.get(i)
+ // Process all dates:
+ Pattern date_pattern = Pattern.compile(REGEX_FOR_DATE)
+ ImageStack stack = single_channel_imp.getStack()
+ // Go to the first time (which is slice)
+ single_channel_imp.setSlice(1)
+ int currentSlice = single_channel_imp.getCurrentSlice()
+ String label = stack.getSliceLabel(currentSlice)
+ LocalDateTime dateTime_ref = getDate(label, date_pattern)
+ if (dateTime_ref == null) {
+ date_pattern = Pattern.compile(ALTERNATIVE_REGEX_FOR_DATE)
+ dateTime_ref = getDate(label, date_pattern)
+ }
+ if (dateTime_ref != null) {
+ first_acq_date = dateTime_ref.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
+ first_acq_time = dateTime_ref.format(DateTimeFormatter.ofPattern("HH:mm:ss"))
+ for (int ti = 2; ti<= nT; ti++) {
+ // Process each frame starting at 2
+ single_channel_imp.setSlice(ti)
+ int hours = getHoursFromImp(single_channel_imp, stack, dateTime_ref, date_pattern)
+ if (rel_acq_hour == "") {
+ rel_acq_hour = "" + hours
+ } else {
+ rel_acq_hour += "," + hours
+ }
+ }
+ } else {
+ first_acq_date = "NA"
+ first_acq_time = "NA"
+ }
+ } else {
+ assert single_channel_imp.getNSlices() == nT : "The number of "+channels_list.get(i)+" images for well "+input_wellId+" and field " + wellSampleId + " does not match the number of images in " + first_channel + "."
+ }
+ // Set acquisition properties
+ single_channel_imp.setProperty(FIRST_ACQUISITION_DATE, first_acq_date)
+ single_channel_imp.setProperty(FIRST_ACQUISITION_TIME, first_acq_time)
+ single_channel_imp.setProperty(RELATIVE_ACQUISITION_HOUR, rel_acq_hour)
+ println single_channel_imp.getProperty(FIRST_ACQUISITION_DATE)
+ println single_channel_imp.getProperty(FIRST_ACQUISITION_TIME)
+ // add the image stack to the channel map for the corresponding color
+ channelMap.put(channels_list.get(i), single_channel_imp)
+ }
+ }
+ }
+ if (nT != 0) {
+ // add the channelmap to the sampleToChannelMap using the wellSampleId as key
+ sampleToChannelMap.put(wellSampleId, channelMap)
+ }
+ }
+
+ ArrayList final_imp_list = []
+
+ // Now loop over the wellSampleId which have images
+ for(Integer wellSampleId : sampleToChannelMap.keySet()){
+ // get the channel map
+ Map channelsMap = sampleToChannelMap.get(wellSampleId)
+ ArrayList channels = []
+ ArrayList current_images = []
+
+ for(String channel : channelsMap.keySet()){
+ channels.add(channel)
+ current_images.add(channelsMap.get(channel))
+ }
+ // Get number of time:
+ int nT = current_images[0].nSlices
+
+ // Merge all
+ ImagePlus merged_imps = Concatenator.run(current_images as ImagePlus[])
+ // Re-order to make a multi-channel, time-lapse image
+ ImagePlus final_imp
+ if (channels.size() == 1 && nT == 1) {
+ final_imp = merged_imps
+ } else {
+ final_imp = HyperStackConverter.toHyperStack(merged_imps, channels.size() , 1, nT, "xytcz", "Color")
+ }
+ // add properties to the image
+ final_imp.setProperty(DIMENSION_ORDER, DimensionOrder.XYCZT)
+ final_imp.setProperty(IMG_POS_IN_WELL, wellSampleId)
+ final_imp.setProperty(FIRST_ACQUISITION_DATE, current_images[0].getProperty(FIRST_ACQUISITION_DATE))
+ final_imp.setProperty(FIRST_ACQUISITION_TIME, current_images[0].getProperty(FIRST_ACQUISITION_TIME))
+ final_imp.setProperty(RELATIVE_ACQUISITION_HOUR, current_images[0].getProperty(RELATIVE_ACQUISITION_HOUR))
+
+ // set LUTs
+ (0..channels.size()-1).each{
+ final_imp.setC(it + 1)
+ IJ.run(final_imp, channels[it], "")
+ final_imp.resetDisplayRange()
+ }
+
+ final_imp_list.add(final_imp)
+ }
+
+ return final_imp_list
+}
+
+
+/**
+ * create the full XML metadata (plate, images, channels, annotations....)
+ *
+ * @param imagesName
+ * @param keyValuesPerWell
+ * @param globalKeyValues
+ * @param pixelSize
+ * @return OME-XML metadata instance
+ */
+def buildXMLFile(Map> wellToImagesMap, Map> keyValuesPerWell, List globalKeyValues, double pixelSize) {
+ // create a new OME-XML metadata instance
+ OME ome = new OME()
+
+ Map imgInWellPosToListMap = new HashMap<>()
+ int imgCmp = 0
+ for (String wellId: wellToImagesMap.keySet()) {
+ // get well position from image name
+ List imagesWithinWell = wellToImagesMap.get(wellId)
+
+ for (ImagePlus image : imagesWithinWell) {
+ // get KVP corresponding to the current well
+ // Initiate a list of keyValues for the wellId
+ // (or use the existing one)
+ List keyValues = []
+ if(keyValuesPerWell.containsKey(wellId))
+ keyValues = keyValuesPerWell.get(wellId)
+ keyValues.addAll(globalKeyValues)
+
+ // create an Image node in the ome-xml
+ imgInWellPosToListMap.put(wellId+ "_" +image.getProperty(IMG_POS_IN_WELL),imgCmp)
+ ome.addImage(makeImage(imgCmp++, image, keyValues, pixelSize))
+ }
+ }
+
+ // create Plate node
+ ome.addPlate(makePlate(wellToImagesMap, imgInWellPosToListMap, pixelSize, ome))
+
+ // add annotation nodes
+ ome.setStructuredAnnotations(annotations)
+
+ return ome
+}
+
+/**
+ * create an image xml-element, populated with annotations, channel, pixels and path elements
+ *
+ * @param index image ID
+ * @param imageName
+ * @param keyValues
+ * @param pixelSize
+ * @return an image xml-element
+ */
+def makeImage(int index, ImagePlus imagePlus, List keyValues, double pixelSize) {
+ // Create
+ Image image = new Image()
+ image.setID("Image:" + index)
+ // The image name is the name of the file without extension
+ image.setName(((String)imagePlus.getProperty(FILE_NAME)).split("\\.")[0])
+ // Set the acquisitionDate:
+ if (imagePlus.getProperty(FIRST_ACQUISITION_DATE) != "NA") {
+ image.setAcquisitionDate(new Timestamp(imagePlus.getProperty(FIRST_ACQUISITION_DATE) + "T" + imagePlus.getProperty(FIRST_ACQUISITION_TIME)))
+ // Also add it to the key values:
+ keyValues.add(new MapPair("acquisition.day", (String)imagePlus.getProperty(FIRST_ACQUISITION_DATE)))
+ keyValues.add(new MapPair("acquisition.time", (String)imagePlus.getProperty(FIRST_ACQUISITION_TIME)))
+ keyValues.add(new MapPair("relative.acquisition.hours", (String)imagePlus.getProperty(RELATIVE_ACQUISITION_HOUR)))
+ }
+ // Create
+ MapAnnotation mapAnnotation = new MapAnnotation()
+ mapAnnotation.setID("ImageKeyValueAnnotation:" + index)
+ mapAnnotation.setNamespace(GENERAL_ANNOTATION_NAMESPACE)
+ mapAnnotation.setValue(keyValues)
+ annotations.addMapAnnotation(mapAnnotation); // add the KeyValues to the general structured annotation element
+ image.linkAnnotation(mapAnnotation)
+
+ // Create
+ Pixels pixels = new Pixels()
+ pixels.setID("Pixels:" + index)
+ pixels.setSizeX(new PositiveInteger(imagePlus.getWidth()))
+ pixels.setSizeY(new PositiveInteger(imagePlus.getHeight()))
+ pixels.setSizeZ(new PositiveInteger(imagePlus.getNSlices()))
+ pixels.setSizeC(new PositiveInteger(imagePlus.getNChannels()))
+ pixels.setSizeT(new PositiveInteger(imagePlus.getNFrames()))
+ pixels.setDimensionOrder((DimensionOrder) imagePlus.getProperty(DIMENSION_ORDER))
+ pixels.setType(getPixelType(imagePlus))
+ pixels.setPhysicalSizeX(new Length(pixelSize, UNITS.MICROMETER))
+ pixels.setPhysicalSizeY(new Length(pixelSize, UNITS.MICROMETER))
+
+ // Create under
+ TiffData tiffData = new TiffData()
+ tiffData.setFirstC(new NonNegativeInteger(0))
+ tiffData.setFirstT(new NonNegativeInteger(0))
+ tiffData.setFirstZ(new NonNegativeInteger(0))
+ tiffData.setPlaneCount(new NonNegativeInteger(imagePlus.getNSlices()*imagePlus.getNChannels()*imagePlus.getNFrames()))
+
+ // Create under
+ UUID uuid = new UUID()
+ uuid.setFileName((String)imagePlus.getProperty(FILE_NAME))
+ uuid.setValue(java.util.UUID.randomUUID().toString())
+ tiffData.setUUID(uuid)
+
+ // Put under
+ pixels.addTiffData(tiffData)
+
+ // Create under
+ LUT[] luts = imagePlus.getLuts()
+ for (int i = 0; i < luts.length; i++) {
+ Channel channel = new Channel()
+ channel.setID("Channel:" + i)
+ channel.setColor(new Color(luts[i].getRed(255),luts[i].getGreen(255), luts[i].getBlue(255),255))
+ pixels.addChannel(channel)
+ }
+
+ // Put under
+ image.setPixels(pixels)
+
+ return image
+}
+
+
+/**
+ * get pixel type based on the imagePlus type
+ * @param imp
+ * @return pixel type
+ */
+def getPixelType(ImagePlus imp){
+ switch (imp.getType()) {
+ case ImagePlus.GRAY8:
+ return PixelType.UINT8
+ case ImagePlus.GRAY16:
+ return PixelType.UINT16
+ case ImagePlus.GRAY32:
+ return PixelType.FLOAT
+ default:
+ return PixelType.FLOAT
+ }
+}
+
+/**
+ * create a Plate xml-element, populated with wells and their attributes
+ * @param imagesName
+ * @return Plate xml-element
+ */
+def makePlate(Map> wellToImagesMap, Map imgPosInListMap, double pixelSize, OME ome) {
+ // Create
+ Plate plate = new Plate()
+ plate.setName(PLATE_NAME)
+ plate.setID(PLATE_ID)
+ plate.setRows(WELL_ROWS)
+ plate.setColumns(WELL_COLS)
+ plate.setRowNamingConvention(WELL_ROW)
+ plate.setColumnNamingConvention(WELL_COL)
+
+ // for each image (one image per well)
+ for (String wellId: wellToImagesMap.keySet()) {
+ // get well position from image name
+ List imagesWithinWell = wellToImagesMap.get(wellId)
+
+ // get well position from image name
+ int row = convertLetterToNumber(wellId.substring(0, 1))
+ int col = Integer.parseInt(wellId.substring(1)) - 1
+
+ // row and col should correspond to a real well
+ if(row >= 0 && col >= 0 && col < 12) {
+ // Create under
+ Well well = new Well()
+ well.setID(String.format("Well:%d_%d", row, col))
+ well.setRow(new NonNegativeInteger(row))
+ well.setColumn(new NonNegativeInteger(col))
+
+ for (ImagePlus imagePlus : imagesWithinWell) {
+ int wellSampleIndex = imgPosInListMap.get(wellId + "_" + imagePlus.getProperty(IMG_POS_IN_WELL))
+
+ // Create under
+ WellSample sample = new WellSample()
+ sample.setID(String.format("WellSample:%d", wellSampleIndex))
+ sample.setIndex(new NonNegativeInteger(wellSampleIndex))
+ if (imagePlus.getCalibration() != null) {
+ sample.setPositionX(new Length(imagePlus.getCalibration().xOrigin * pixelSize, UNITS.MICROMETER))
+ sample.setPositionY(new Length(imagePlus.getCalibration().yOrigin * pixelSize, UNITS.MICROMETER))
+ }
+ sample.linkImage(ome.getImage(wellSampleIndex))
+
+ // Put under
+ well.addWellSample(sample)
+ }
+
+ // Put under
+ plate.addWell(well)
+ }
+ }
+ return plate
+}
+
+/**
+ * convert the XML metadata document into string
+ *
+ * @param document
+ * @return
+ * @throws TransformerException
+ * @throws UnsupportedEncodingException
+ */
+def asString(Document document) throws TransformerException, UnsupportedEncodingException {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance()
+ Transformer transformer = transformerFactory.newTransformer()
+
+ //Setup indenting to "pretty print"
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes")
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")
+
+ Source source = new DOMSource(document)
+ ByteArrayOutputStream os = new ByteArrayOutputStream()
+ Result result = new StreamResult(new OutputStreamWriter(os, "utf-8"))
+ transformer.transform(source, result)
+
+ return os.toString()
+}
+
+/**
+ * add document header
+ *
+ * @param root
+ * @param document
+ */
+def postProcess(Element root, Document document) {
+ root.setAttribute("xmlns", XML_NS)
+ root.setAttribute("xmlns:xsi", XSI_NS)
+ root.setAttribute("xsi:schemaLocation", XML_NS + " " + SCHEMA_LOCATION)
+ root.setAttribute("UUID", java.util.UUID.randomUUID().toString())
+ document.appendChild(root)
+}
+
+
+/**
+ * read Incucyte plate-scheme XML file, extract attributes per well and convert attributes to OMERO-compatible
+ * keys-value pairs XML elements.
+ *
+ * @param path Incucyte plate-scheme XML file path
+ * @return Map of OME-XML compatible key-values per well
+ */
+def parseIncucyteXML(String path, Boolean ignoreConcentration, Boolean ignorePassage, Boolean ignoreSeeding) {
+ Map> keyValuesPerWell = new HashMap<>()
+
+ final String rowAttribute = "row"
+ final String columnAttribute = "col"
+
+ final String wellNode = "well"
+ final String itemsNode = "items"
+ final String wellItemNode = "wellItem"
+ final String referenceItemNode = "referenceItem"
+
+ // There are 3 types of referenceItem: Compound, CellType, GrowthCondition
+ //
+ // For the Compound, each well can have a concentration and a concentrationUnits
+ // the referenceItem has a displayName
+ // The key should be: displayName (concentrationUnits)
+ // The value should be: concentration
+ // However, if ignoreConcentration is set to true:
+ // The key should be: displayName (NA)
+ // The value should be: 1
+ //
+ // For the CellType, each well can have a passage, a seedingDensity and a seedingDensityUnits
+ // the referenceItem has a displayName
+ // The passage key should be: displayName_passage
+ // The value should be: passage
+ // However, if ignorePassage is set to true no key value should be stored for this
+ // Then:
+ // The seeding key should be: displayName_seedingDensity (seedingDensityUnits)
+ // The value should be: seedingDensity
+ // However, if ignoreSeeding is set to true:
+ // The key should be: displayName
+ // The value should be: "yes"
+ //
+ // For the GrowthCondition, the referenceItem has a displayName
+ // The key should be: displayName
+ // The value should be: "yes"
+
+ try {
+ // create an document
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance()
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder()
+
+ // read the xml file
+ Document doc = dBuilder.parse(new File(path))
+ doc.getDocumentElement().normalize()
+
+ // get all "well" nodes
+ NodeList wellList = doc.getElementsByTagName(wellNode)
+
+ for(int i = 0; i < wellList.getLength(); i++) {
+ Node well = wellList.item(i)
+
+ // extract well attributes
+ String row = well.getAttributes().getNamedItem(rowAttribute).getTextContent()
+ int r = row as int
+ String col = well.getAttributes().getNamedItem(columnAttribute).getTextContent()
+ String wellNumber = LETTERS.substring(r, r + 1) + (Integer.parseInt(col)+ 1)
+
+ // extract items node, under well node
+ Node items = ((Element)well).getElementsByTagName(itemsNode).item(0)
+ if (items != null) {
+ // get all "wellItem" nodes, under items node
+ NodeList wellItemList = ((Element)items).getElementsByTagName(wellItemNode)
+
+ // read referenceItem node's attributes and convert them into key-values
+ List keyValues = new ArrayList<>()
+ for (int j = 0; j < wellItemList.getLength(); j++) {
+ Node wellItem = wellItemList.item(j)
+ // extract referenceItem node, under wellItem node
+ Node referenceItem = ((Element)wellItem).getElementsByTagName(referenceItemNode).item(0)
+ String wellType = wellItem.getAttributes().getNamedItem("type").getTextContent()
+
+ // select the right key-values
+ switch (wellType){
+ case "Compound":
+ String compound_name = referenceItem.getAttributes().getNamedItem("displayName").getTextContent()
+ if (ignoreConcentration) {
+ keyValues.add(new MapPair(compound_name + " (NA)", "1"))
+ } else {
+ String unit = wellItem.getAttributes().getNamedItem("concentrationUnits").getTextContent()
+ unit = unit.replace("\u00B5", "u")
+ String value = wellItem.getAttributes().getNamedItem("concentration").getTextContent()
+ keyValues.add(new MapPair(compound_name + " (" + unit + ")", value))
+ }
+ break
+ case "CellType":
+ String cell_name = referenceItem.getAttributes().getNamedItem("displayName").getTextContent()
+ if (!ignorePassage) {
+ String passage = wellItem.getAttributes().getNamedItem("passage").getTextContent()
+ keyValues.add(new MapPair(cell_name + "_passage", passage))
+ }
+ if (ignoreSeeding) {
+ keyValues.add(new MapPair(cell_name, "yes"))
+ } else {
+ String unit = wellItem.getAttributes().getNamedItem("seedingDensityUnits").getTextContent()
+ unit = unit.replace("\u00B5", "u")
+ String value = wellItem.getAttributes().getNamedItem("seedingDensity").getTextContent()
+ keyValues.add(new MapPair(cell_name + "_seedingDensity (" + unit + ")", value))
+ }
+ break
+ case "GrowthCondition":
+ String growth_condition = referenceItem.getAttributes().getNamedItem("displayName").getTextContent()
+ keyValues.add(new MapPair(growth_condition, "yes"))
+ break
+ }
+ }
+ keyValuesPerWell.put(wellNumber,keyValues)
+
+ }
+ }
+ return keyValuesPerWell
+ } catch (Exception e) {
+ println "XML platemap could not be parsed"
+ e.printStackTrace()
+ return new HashMap<>()
+ }
+}
+
+/**
+ * make a list of all key-values that are common to all images
+ *
+ * @param objective
+ * @param commonKeyValues (a String with the following format: key1=value1;key2=value2)
+ * @return a list of OME-XML key-values
+ */
+def getGlobalKeyValues(int objective, String commonKeyValues){
+ List keyValues = new ArrayList<>()
+ keyValues.add(new MapPair("groovy_version", VERSION))
+ keyValues.add(new MapPair("objective", objectives[objective]))
+ if (commonKeyValues != "") {
+ String[] keyValList = commonKeyValues.split(';')
+ for (int i = 0; i < keyValList.size(); i ++) {
+ String keyval = keyValList[i]
+ String[] keyvalsplit = keyval.split('=')
+ int nPieces = keyvalsplit.size()
+ String value = keyvalsplit[nPieces - 1]
+ String key = keyvalsplit[0]
+ // In case there are '=' in key
+ for (int j = 1; j < nPieces - 1; j++) {
+ key += '=' + keyvalsplit[j]
+ }
+ keyValues.add(new MapPair(key, value))
+ }
+ }
+ return keyValues
+}
+
+/**
+ * convert alphanumeric well position to numeric position
+ *
+ * @param letter
+ * @return
+ */
+def convertLetterToNumber(String letter){
+ for (int i = 0; i < LETTERS.size(); i++) {
+ if (LETTERS.substring(i, i + 1) == letter) {
+ return i
+ }
+ }
+ return -1
+}
+
+// Returns a date from a label and a date_pattern
+def getDate(String label, Pattern date_pattern){
+// println "Trying to get date from " + label
+ Matcher date_m = date_pattern.matcher(label)
+ LocalDateTime dateTime
+ if (date_m.matches()) {
+ if (date_m.groupCount() == 5) {
+ dateTime = LocalDateTime.parse(date_m.group(1) + "-" + date_m.group(2) + "-" + date_m.group(3) + "T" + date_m.group(4) + ":" + date_m.group(5))
+ } else {
+ dateTime = LocalDateTime.parse("1970-01-" + 1 + (date_m.group(1) as int) + "T" + date_m.group(2) + ":" + date_m.group(3))
+ }
+ }
+// println "Found " + dateTime
+ return dateTime
+}
+
+// Returns the number of hours
+def getHoursFromImp(ImagePlus imp, ImageStack stack, LocalDateTime dateTime_ref, Pattern date_pattern){
+ int currentSlice = imp.getCurrentSlice()
+ String label = stack.getSliceLabel(currentSlice)
+ LocalDateTime dateTime = getDate(label, date_pattern)
+ if (dateTime != null) {
+ return ChronoUnit.HOURS.between(dateTime_ref, dateTime) as int
+ } else {
+ return -1
+ }
+}
+
+
+/**
+ * *****************************************************************************************************************
+ * ************************************************* Imports ****************************************************
+ * ****************************************************************************************************************
+ */
+
+
+import ij.IJ
+import ij.ImagePlus
+import ij.ImageStack
+import ij.io.FileSaver
+import ij.io.Opener
+import ij.plugin.Concatenator
+import ij.plugin.FolderOpener
+import ij.plugin.HyperStackConverter
+import ij.process.LUT
+
+import java.awt.GraphicsEnvironment
+import java.io.File
+import java.time.format.DateTimeFormatter
+import java.time.LocalDateTime
+import java.time.temporal.ChronoUnit
+import java.util.stream.Collectors
+import java.util.stream.IntStream
+import java.util.regex.*
+
+import javax.xml.parsers.DocumentBuilder
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.Result
+import javax.xml.transform.Source
+import javax.xml.transform.Transformer
+import javax.xml.transform.TransformerException
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+
+import ome.units.UNITS
+import ome.units.quantity.Length
+
+import ome.xml.model.Channel
+import ome.xml.model.Image
+import ome.xml.model.MapAnnotation
+import ome.xml.model.MapPair
+import ome.xml.model.OME
+import ome.xml.model.Pixels
+import ome.xml.model.Plate
+import ome.xml.model.StructuredAnnotations
+import ome.xml.model.TiffData
+import ome.xml.model.UUID
+import ome.xml.model.Well
+import ome.xml.model.WellSample
+import ome.xml.model.enums.DimensionOrder
+import ome.xml.model.enums.NamingConvention
+import ome.xml.model.enums.PixelType
+import ome.xml.model.primitives.Color
+import ome.xml.model.primitives.NonNegativeInteger
+import ome.xml.model.primitives.PositiveInteger
+import ome.xml.model.primitives.Timestamp
+
+import org.apache.commons.io.filefilter.WildcardFileFilter
+import org.apache.commons.io.FilenameUtils
+
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+import org.w3c.dom.Node
+import org.w3c.dom.NodeList
diff -r 000000000000 -r e1cba36becb2 upload_omero.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/upload_omero.sh Tue Dec 19 15:02:41 2023 +0000
@@ -0,0 +1,17 @@
+#!/bin/bash
+omero_server="$1"
+omero_user="$(cat $2 | awk 'NR==2{print $0}')"
+omero_password="$(cat $2 | awk 'NR==3{print $0}')"
+to_create=$3
+screen_name_or_id=$4
+
+if [ "$to_create" = "create" ]; then
+ # Create a screen:
+ screen_name_or_id=$(omero obj -s ${omero_server} -u ${omero_user} -w ${omero_password} new Screen name="${screen_name_or_id}" | awk -F ":" 'END{print $NF}')
+ echo "Just created the new screen ${screen_name_or_id}"
+fi
+
+echo "Start upload"
+companion_file=$(ls output/*.companion.ome)
+omero import -s ${omero_server} -u ${omero_user} -w ${omero_password} -T Screen:id:"${screen_name_or_id}" "${companion_file}" 2>&1
+echo "Upload finished"