diff upload_omero_roi_results.py @ 0:d507ce86f0d0 draft default tip

planemo upload for repository https://github.com/lldelisle/tools-lldelisle/tree/master/tools/upload_roi_and_measures_to_omero commit 68de74426a3f93a240d64cb416f608ba7caca6eb
author lldelisle
date Fri, 16 Dec 2022 21:02:41 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upload_omero_roi_results.py	Fri Dec 16 21:02:41 2022 +0000
@@ -0,0 +1,388 @@
+import argparse
+import json
+import os
+import re
+import tempfile
+
+import numpy as np
+
+import omero
+from omero.gateway import BlitzGateway
+from omero.rtypes import rdouble, rstring
+
+import pandas as pd
+
+file_base_name_exportedTIFF = re.compile(r"^.*__(\d+)__0__0__\d+__\d+$")
+file_base_name_original = re.compile(r"^.*__(\d+)$")
+
+non_roi_value = -1
+
+non_numeric_columns = ["Label", "Date", "Version", "IlastikProject",
+                       "Preprocess"]
+
+
+def get_image_id(image_file_name):
+    # Check the file name
+    # corresponds to the expected:
+    match = \
+        file_base_name_exportedTIFF.findall(image_file_name.replace(".tiff",
+                                                                    ""))
+    if len(match) == 0:
+        match = \
+            file_base_name_original.findall(image_file_name.replace(".tiff",
+                                                                    ""))
+        if len(match) == 0:
+            raise Exception(f"{image_file_name} does not match"
+                            "the expected format")
+    # Get the image_id
+    image_id = int(match[0])
+    return image_id
+
+
+def get_omero_credentials(config_file):
+    if config_file is None:  # IDR connection
+        omero_username = "public"
+        omero_password = "public"
+    else:  # other omero instance
+        with open(config_file) as f:
+            cfg = json.load(f)
+            omero_username = cfg["username"]
+            omero_password = cfg["password"]
+
+            if omero_username == "" or omero_password == "":
+                omero_username = "public"
+                omero_password = "public"
+    return (omero_username, omero_password)
+
+
+def clean(image_id, omero_username, omero_password, omero_host, omero_secured,
+          verbose):
+    with BlitzGateway(
+        omero_username, omero_password, host=omero_host, secure=omero_secured
+    ) as conn:
+        roi_service = conn.getRoiService()
+        img = conn.getObject("Image", image_id)
+        rois = roi_service.findByImage(image_id, None, conn.SERVICE_OPTS).rois
+        # Delete existing rois
+        if len(rois) > 0:
+            if verbose:
+                print(f"Removing {len(rois)} existing ROIs.")
+            conn.deleteObjects("Roi", [roi.getId().val
+                                       for roi
+                                       in rois],
+                               wait=True)
+        # Delete existing table named Results_from_Fiji
+        for ann in img.listAnnotations():
+            if ann.OMERO_TYPE == omero.model.FileAnnotationI:
+                if ann.getFileName() == "Results_from_Fiji":
+                    if verbose:
+                        print("Removing the table Results_from_Fiji.")
+                    conn.deleteObjects("OriginalFile", [ann.getFile().id],
+                                       wait=True)
+
+
+def upload(
+    image_id,
+    df,
+    roi_files,
+    omero_username,
+    omero_password,
+    omero_host,
+    omero_secured,
+    verbose,
+):
+    with BlitzGateway(
+        omero_username, omero_password, host=omero_host, secure=omero_secured
+    ) as conn:
+        updateService = conn.getUpdateService()
+        img = conn.getObject("Image", image_id)
+        # Create ROIs:
+        roi_ids = []
+        # roi_big_circle_ids = []
+        # roi_spine_ids = []
+        for i, ro_file in enumerate(roi_files):
+            # Create a polygon
+            my_poly = omero.model.PolygonI()
+            # Add the coordinates
+            with open(ro_file, "r") as f:
+                coos = f.readlines()
+            coos_formatted = ", ".join([line.strip().replace("\t", ",")
+                                        for line in coos])
+            my_poly.setPoints(rstring(coos_formatted))
+            # Add a name
+            my_poly.setTextValue(rstring("ROI" + str(i)))
+            # Create a omero ROI
+            my_new_roi = omero.model.RoiI()
+            my_new_roi.addShape(my_poly)
+            # Attach it to the image
+            my_new_roi.setImage(img._obj)
+            my_new_roi = updateService.saveAndReturnObject(my_new_roi)
+            roi_ids.append(my_new_roi.getId().val)
+            if verbose:
+                print(f"Created ROI{i} {my_new_roi.getId().val}.")
+            # Check if there is an elongation ROI associated:
+            if os.path.exists(ro_file.replace("roi_coordinates",
+                                              "elongation_rois")):
+                # Get the coordinates
+                with open(
+                    ro_file.replace("roi_coordinates", "elongation_rois"),
+                    "r"
+                ) as f:
+                    all_coos = f.readlines()
+                # Get the circles coos
+                circles_coos = [line for line in all_coos
+                                if len(line.split("\t")) == 3]
+                for j, circle_coo in enumerate(circles_coos):
+                    # Create an ellipse
+                    my_ellipse = omero.model.EllipseI()
+                    # Get the characteristics from text file
+                    xleft, ytop, width = [
+                        float(v) for v in circle_coo.strip().split("\t")
+                    ]
+                    # Add it to the ellipse
+                    my_ellipse.setRadiusX(rdouble(width / 2.0))
+                    my_ellipse.setRadiusY(rdouble(width / 2.0))
+                    my_ellipse.setX(rdouble(xleft + width / 2))
+                    my_ellipse.setY(rdouble(ytop + width / 2))
+                    # Add a name
+                    my_ellipse.setTextValue(
+                        rstring("inscribedCircle" + str(i) + "_" + str(j))
+                    )
+                    # Create a omero ROI
+                    my_new_roi = omero.model.RoiI()
+                    my_new_roi.addShape(my_ellipse)
+                    # Attach it to the image
+                    my_new_roi.setImage(img._obj)
+                    my_new_roi = updateService.saveAndReturnObject(my_new_roi)
+                    if verbose:
+                        print(
+                            f"Created ROI inscribedCircle {i}_{j}:"
+                            f"{my_new_roi.getId().val}."
+                        )
+                    # I store the id of the first circle:
+                    # if j == 0:
+                    #     roi_big_circle_ids.append(my_new_roi.getId().val)
+                if len(all_coos) > len(circles_coos):
+                    # Create a polyline for the spine
+                    my_poly = omero.model.PolylineI()
+                    coos_formatted = ", ".join(
+                        [
+                            line.strip().replace("\t", ",")
+                            for line in all_coos[len(circles_coos):]
+                        ]
+                    )
+                    my_poly.setPoints(rstring(coos_formatted))
+                    # Add a name
+                    my_poly.setTextValue(rstring("spine" + str(i)))
+                    # Create a omero ROI
+                    my_new_roi = omero.model.RoiI()
+                    my_new_roi.addShape(my_poly)
+                    # Attach it to the image
+                    my_new_roi.setImage(img._obj)
+                    my_new_roi = updateService.saveAndReturnObject(my_new_roi)
+                    if verbose:
+                        print(f"Created ROI spine{i}:"
+                              f" {my_new_roi.getId().val}.")
+                    # roi_spine_ids.append(my_new_roi.getId().val)
+                else:
+                    if verbose:
+                        print("No spine found")
+                    # roi_spine_ids.append(non_roi_value)
+            # else:
+            #     roi_big_circle_ids.append(non_roi_value)
+            #     roi_spine_ids.append(non_roi_value)
+
+        # Create the table:
+        table_name = "Results_from_Fiji"
+        columns = []
+        for col_name in df.columns[1:]:
+            if col_name in non_numeric_columns:
+                columns.append(omero.grid.StringColumn(col_name, "", 256, []))
+            else:
+                columns.append(omero.grid.DoubleColumn(col_name, "", []))
+
+        # From Claire's groovy:
+        # table_columns[size] = new TableDataColumn("Roi", size, ROIData)
+        columns.append(omero.grid.RoiColumn("Roi", "", []))
+        # For the moment (20220729),
+        # the table support only one ROI column with link...
+        # if 'Elongation_index' in df.columns[1:]:
+        #     columns.append(omero.grid.RoiColumn('Roi_maxCircle', '', []))
+        #     columns.append(omero.grid.RoiColumn('Roi_Spine', '', []))
+        # columns.append(omero.grid.RoiColumn('Roi_main', '', []))
+
+        resources = conn.c.sf.sharedResources()
+        repository_id = \
+            resources.repositories().descriptions[0].getId().getValue()
+        table = resources.newTable(repository_id, table_name)
+        table.initialize(columns)
+
+        data = []
+        for col_name in df.columns[1:]:
+            if col_name in non_numeric_columns:
+                data.append(
+                    omero.grid.StringColumn(
+                        col_name, "", 256,
+                        df[col_name].astype("string").to_list()
+                    )
+                )
+            else:
+                data.append(
+                    omero.grid.DoubleColumn(col_name, "",
+                                            df[col_name].to_list())
+                )
+        data.append(omero.grid.RoiColumn("Roi", "", roi_ids))
+        # if verbose:
+        #     print("Columns are " + " ".join(df.columns[1:]))
+        # if 'Elongation_index' in df.columns[1:]:
+        #     if verbose:
+        #         print("Adding 2 rois columns")
+        #         print(roi_ids)
+        #         print(roi_big_circle_ids)
+        #     data.append(omero.grid.RoiColumn('Roi_maxCircle', '',
+        #                                      roi_big_circle_ids))
+        #     data.append(omero.grid.RoiColumn('Roi_Spine', '',
+        #                                      roi_spine_ids))
+        # data.append(omero.grid.RoiColumn('Roi_main', '', roi_ids))
+
+        table.addData(data)
+        orig_file = table.getOriginalFile()
+        table.close()
+        # when we are done, close.
+
+        # Load the table as an original file
+
+        orig_file_id = orig_file.id.val
+        # ...so you can attach this data to an object e.g. Image
+        file_ann = omero.model.FileAnnotationI()
+        # use unloaded OriginalFileI
+        file_ann.setFile(omero.model.OriginalFileI(orig_file_id, False))
+        file_ann = updateService.saveAndReturnObject(file_ann)
+        link = omero.model.ImageAnnotationLinkI()
+        link.setParent(omero.model.ImageI(image_id, False))
+        link.setChild(omero.model.FileAnnotationI(
+            file_ann.getId().getValue(), False
+            ))
+        updateService.saveAndReturnObject(link)
+        if verbose:
+            print("Successfully created a Table with results.")
+
+
+def scan_and_upload(
+    roi_directory,
+    summary_results,
+    omero_username,
+    omero_password,
+    omero_host="idr.openmicroscopy.org",
+    omero_secured=False,
+    verbose=False,
+):
+    # First get the summary results
+    full_df = pd.read_csv(summary_results)
+    # Loop over the image names
+    for image_file_name in np.unique(full_df["Label"]):
+        # Get the image_id
+        image_id = get_image_id(image_file_name)
+        if verbose:
+            print(f"Image:{image_id} is in the table."
+                  " Cleaning old results.")
+        clean(
+            image_id, omero_username, omero_password, omero_host,
+            omero_secured, verbose
+        )
+        # Subset the result to the current image
+        df = full_df[full_df["Label"] == image_file_name]
+        if np.isnan(df["Area"].to_list()[0]):
+            # No ROI has been detected
+            if verbose:
+                print("No ROI was found.")
+            continue
+        n_rois = df.shape[0]
+        if verbose:
+            print(f"I found {n_rois} measurements.")
+        # Check the corresponding rois exists
+        roi_files = [
+            os.path.join(
+                roi_directory,
+                image_file_name.replace(".tiff", "_tiff")
+                + "__"
+                + str(i)
+                + "_roi_coordinates.txt",
+            )
+            for i in range(n_rois)
+        ]
+        for ro_file in roi_files:
+            if not os.path.exists(ro_file):
+                raise Exception(f"Could not find {ro_file}")
+        upload(
+            image_id,
+            df,
+            roi_files,
+            omero_username,
+            omero_password,
+            omero_host,
+            omero_secured,
+            verbose,
+        )
+    # Update the full_df with image id:
+    full_df["id"] = [
+        get_image_id(image_file_name)
+        for image_file_name in full_df["Label"]
+    ]
+    # Attach it to the dataset:
+    with BlitzGateway(
+        omero_username, omero_password, host=omero_host,
+        secure=omero_secured
+    ) as conn:
+        full_df["dataset_id"] = [
+            a.id
+            for id in full_df["id"]
+            for a in conn.getObject("Image", id).getAncestry()
+            if a.OMERO_CLASS == "Dataset"
+        ]
+        dir = tempfile.mkdtemp()
+        for dataset_id in np.unique(full_df["dataset_id"]):
+            df = full_df[full_df["dataset_id"] == dataset_id]
+            first_date = df["Date"].to_list()[0]
+            file_to_upload = os.path.join(
+                dir, "Results_from_Fiji_" + first_date + ".csv"
+            )
+            df.to_csv(file_to_upload, index=False)
+            dataset = conn.getObject("Dataset", dataset_id)
+            # create the original file and file annotation
+            # (uploads the file etc.)
+            # namespace = "my.custom.demo.namespace"
+            file_ann = conn.createFileAnnfromLocalFile(
+                file_to_upload
+            )  # , mimetype="text/plain", ns=namespace, desc=None)
+            print(
+                "Attaching FileAnnotation to Dataset: ",
+                "File ID:",
+                file_ann.getId(),
+                ",",
+                file_ann.getFile().getName(),
+                "Size:",
+                file_ann.getFile().getSize(),
+            )
+            dataset.linkAnnotation(file_ann)  # link it to dataset.
+
+
+if __name__ == "__main__":
+    p = argparse.ArgumentParser()
+    p.add_argument("-oh", "--omero-host", type=str,
+                   default="idr.openmicroscopy.org")
+    p.add_argument("--omero-secured", action="store_true", default=True)
+    p.add_argument("-cf", "--config-file", dest="config_file",
+                   default=None)
+    p.add_argument("--rois", type=str, default=None)
+    p.add_argument("--summaryResults", type=str, default=None)
+    p.add_argument("--verbose", action="store_true")
+    args = p.parse_args()
+    scan_and_upload(
+        args.rois,
+        args.summaryResults,
+        *get_omero_credentials(args.config_file),
+        omero_host=args.omero_host,
+        omero_secured=args.omero_secured,
+        verbose=args.verbose,
+    )