comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:d507ce86f0d0
1 import argparse
2 import json
3 import os
4 import re
5 import tempfile
6
7 import numpy as np
8
9 import omero
10 from omero.gateway import BlitzGateway
11 from omero.rtypes import rdouble, rstring
12
13 import pandas as pd
14
15 file_base_name_exportedTIFF = re.compile(r"^.*__(\d+)__0__0__\d+__\d+$")
16 file_base_name_original = re.compile(r"^.*__(\d+)$")
17
18 non_roi_value = -1
19
20 non_numeric_columns = ["Label", "Date", "Version", "IlastikProject",
21 "Preprocess"]
22
23
24 def get_image_id(image_file_name):
25 # Check the file name
26 # corresponds to the expected:
27 match = \
28 file_base_name_exportedTIFF.findall(image_file_name.replace(".tiff",
29 ""))
30 if len(match) == 0:
31 match = \
32 file_base_name_original.findall(image_file_name.replace(".tiff",
33 ""))
34 if len(match) == 0:
35 raise Exception(f"{image_file_name} does not match"
36 "the expected format")
37 # Get the image_id
38 image_id = int(match[0])
39 return image_id
40
41
42 def get_omero_credentials(config_file):
43 if config_file is None: # IDR connection
44 omero_username = "public"
45 omero_password = "public"
46 else: # other omero instance
47 with open(config_file) as f:
48 cfg = json.load(f)
49 omero_username = cfg["username"]
50 omero_password = cfg["password"]
51
52 if omero_username == "" or omero_password == "":
53 omero_username = "public"
54 omero_password = "public"
55 return (omero_username, omero_password)
56
57
58 def clean(image_id, omero_username, omero_password, omero_host, omero_secured,
59 verbose):
60 with BlitzGateway(
61 omero_username, omero_password, host=omero_host, secure=omero_secured
62 ) as conn:
63 roi_service = conn.getRoiService()
64 img = conn.getObject("Image", image_id)
65 rois = roi_service.findByImage(image_id, None, conn.SERVICE_OPTS).rois
66 # Delete existing rois
67 if len(rois) > 0:
68 if verbose:
69 print(f"Removing {len(rois)} existing ROIs.")
70 conn.deleteObjects("Roi", [roi.getId().val
71 for roi
72 in rois],
73 wait=True)
74 # Delete existing table named Results_from_Fiji
75 for ann in img.listAnnotations():
76 if ann.OMERO_TYPE == omero.model.FileAnnotationI:
77 if ann.getFileName() == "Results_from_Fiji":
78 if verbose:
79 print("Removing the table Results_from_Fiji.")
80 conn.deleteObjects("OriginalFile", [ann.getFile().id],
81 wait=True)
82
83
84 def upload(
85 image_id,
86 df,
87 roi_files,
88 omero_username,
89 omero_password,
90 omero_host,
91 omero_secured,
92 verbose,
93 ):
94 with BlitzGateway(
95 omero_username, omero_password, host=omero_host, secure=omero_secured
96 ) as conn:
97 updateService = conn.getUpdateService()
98 img = conn.getObject("Image", image_id)
99 # Create ROIs:
100 roi_ids = []
101 # roi_big_circle_ids = []
102 # roi_spine_ids = []
103 for i, ro_file in enumerate(roi_files):
104 # Create a polygon
105 my_poly = omero.model.PolygonI()
106 # Add the coordinates
107 with open(ro_file, "r") as f:
108 coos = f.readlines()
109 coos_formatted = ", ".join([line.strip().replace("\t", ",")
110 for line in coos])
111 my_poly.setPoints(rstring(coos_formatted))
112 # Add a name
113 my_poly.setTextValue(rstring("ROI" + str(i)))
114 # Create a omero ROI
115 my_new_roi = omero.model.RoiI()
116 my_new_roi.addShape(my_poly)
117 # Attach it to the image
118 my_new_roi.setImage(img._obj)
119 my_new_roi = updateService.saveAndReturnObject(my_new_roi)
120 roi_ids.append(my_new_roi.getId().val)
121 if verbose:
122 print(f"Created ROI{i} {my_new_roi.getId().val}.")
123 # Check if there is an elongation ROI associated:
124 if os.path.exists(ro_file.replace("roi_coordinates",
125 "elongation_rois")):
126 # Get the coordinates
127 with open(
128 ro_file.replace("roi_coordinates", "elongation_rois"),
129 "r"
130 ) as f:
131 all_coos = f.readlines()
132 # Get the circles coos
133 circles_coos = [line for line in all_coos
134 if len(line.split("\t")) == 3]
135 for j, circle_coo in enumerate(circles_coos):
136 # Create an ellipse
137 my_ellipse = omero.model.EllipseI()
138 # Get the characteristics from text file
139 xleft, ytop, width = [
140 float(v) for v in circle_coo.strip().split("\t")
141 ]
142 # Add it to the ellipse
143 my_ellipse.setRadiusX(rdouble(width / 2.0))
144 my_ellipse.setRadiusY(rdouble(width / 2.0))
145 my_ellipse.setX(rdouble(xleft + width / 2))
146 my_ellipse.setY(rdouble(ytop + width / 2))
147 # Add a name
148 my_ellipse.setTextValue(
149 rstring("inscribedCircle" + str(i) + "_" + str(j))
150 )
151 # Create a omero ROI
152 my_new_roi = omero.model.RoiI()
153 my_new_roi.addShape(my_ellipse)
154 # Attach it to the image
155 my_new_roi.setImage(img._obj)
156 my_new_roi = updateService.saveAndReturnObject(my_new_roi)
157 if verbose:
158 print(
159 f"Created ROI inscribedCircle {i}_{j}:"
160 f"{my_new_roi.getId().val}."
161 )
162 # I store the id of the first circle:
163 # if j == 0:
164 # roi_big_circle_ids.append(my_new_roi.getId().val)
165 if len(all_coos) > len(circles_coos):
166 # Create a polyline for the spine
167 my_poly = omero.model.PolylineI()
168 coos_formatted = ", ".join(
169 [
170 line.strip().replace("\t", ",")
171 for line in all_coos[len(circles_coos):]
172 ]
173 )
174 my_poly.setPoints(rstring(coos_formatted))
175 # Add a name
176 my_poly.setTextValue(rstring("spine" + str(i)))
177 # Create a omero ROI
178 my_new_roi = omero.model.RoiI()
179 my_new_roi.addShape(my_poly)
180 # Attach it to the image
181 my_new_roi.setImage(img._obj)
182 my_new_roi = updateService.saveAndReturnObject(my_new_roi)
183 if verbose:
184 print(f"Created ROI spine{i}:"
185 f" {my_new_roi.getId().val}.")
186 # roi_spine_ids.append(my_new_roi.getId().val)
187 else:
188 if verbose:
189 print("No spine found")
190 # roi_spine_ids.append(non_roi_value)
191 # else:
192 # roi_big_circle_ids.append(non_roi_value)
193 # roi_spine_ids.append(non_roi_value)
194
195 # Create the table:
196 table_name = "Results_from_Fiji"
197 columns = []
198 for col_name in df.columns[1:]:
199 if col_name in non_numeric_columns:
200 columns.append(omero.grid.StringColumn(col_name, "", 256, []))
201 else:
202 columns.append(omero.grid.DoubleColumn(col_name, "", []))
203
204 # From Claire's groovy:
205 # table_columns[size] = new TableDataColumn("Roi", size, ROIData)
206 columns.append(omero.grid.RoiColumn("Roi", "", []))
207 # For the moment (20220729),
208 # the table support only one ROI column with link...
209 # if 'Elongation_index' in df.columns[1:]:
210 # columns.append(omero.grid.RoiColumn('Roi_maxCircle', '', []))
211 # columns.append(omero.grid.RoiColumn('Roi_Spine', '', []))
212 # columns.append(omero.grid.RoiColumn('Roi_main', '', []))
213
214 resources = conn.c.sf.sharedResources()
215 repository_id = \
216 resources.repositories().descriptions[0].getId().getValue()
217 table = resources.newTable(repository_id, table_name)
218 table.initialize(columns)
219
220 data = []
221 for col_name in df.columns[1:]:
222 if col_name in non_numeric_columns:
223 data.append(
224 omero.grid.StringColumn(
225 col_name, "", 256,
226 df[col_name].astype("string").to_list()
227 )
228 )
229 else:
230 data.append(
231 omero.grid.DoubleColumn(col_name, "",
232 df[col_name].to_list())
233 )
234 data.append(omero.grid.RoiColumn("Roi", "", roi_ids))
235 # if verbose:
236 # print("Columns are " + " ".join(df.columns[1:]))
237 # if 'Elongation_index' in df.columns[1:]:
238 # if verbose:
239 # print("Adding 2 rois columns")
240 # print(roi_ids)
241 # print(roi_big_circle_ids)
242 # data.append(omero.grid.RoiColumn('Roi_maxCircle', '',
243 # roi_big_circle_ids))
244 # data.append(omero.grid.RoiColumn('Roi_Spine', '',
245 # roi_spine_ids))
246 # data.append(omero.grid.RoiColumn('Roi_main', '', roi_ids))
247
248 table.addData(data)
249 orig_file = table.getOriginalFile()
250 table.close()
251 # when we are done, close.
252
253 # Load the table as an original file
254
255 orig_file_id = orig_file.id.val
256 # ...so you can attach this data to an object e.g. Image
257 file_ann = omero.model.FileAnnotationI()
258 # use unloaded OriginalFileI
259 file_ann.setFile(omero.model.OriginalFileI(orig_file_id, False))
260 file_ann = updateService.saveAndReturnObject(file_ann)
261 link = omero.model.ImageAnnotationLinkI()
262 link.setParent(omero.model.ImageI(image_id, False))
263 link.setChild(omero.model.FileAnnotationI(
264 file_ann.getId().getValue(), False
265 ))
266 updateService.saveAndReturnObject(link)
267 if verbose:
268 print("Successfully created a Table with results.")
269
270
271 def scan_and_upload(
272 roi_directory,
273 summary_results,
274 omero_username,
275 omero_password,
276 omero_host="idr.openmicroscopy.org",
277 omero_secured=False,
278 verbose=False,
279 ):
280 # First get the summary results
281 full_df = pd.read_csv(summary_results)
282 # Loop over the image names
283 for image_file_name in np.unique(full_df["Label"]):
284 # Get the image_id
285 image_id = get_image_id(image_file_name)
286 if verbose:
287 print(f"Image:{image_id} is in the table."
288 " Cleaning old results.")
289 clean(
290 image_id, omero_username, omero_password, omero_host,
291 omero_secured, verbose
292 )
293 # Subset the result to the current image
294 df = full_df[full_df["Label"] == image_file_name]
295 if np.isnan(df["Area"].to_list()[0]):
296 # No ROI has been detected
297 if verbose:
298 print("No ROI was found.")
299 continue
300 n_rois = df.shape[0]
301 if verbose:
302 print(f"I found {n_rois} measurements.")
303 # Check the corresponding rois exists
304 roi_files = [
305 os.path.join(
306 roi_directory,
307 image_file_name.replace(".tiff", "_tiff")
308 + "__"
309 + str(i)
310 + "_roi_coordinates.txt",
311 )
312 for i in range(n_rois)
313 ]
314 for ro_file in roi_files:
315 if not os.path.exists(ro_file):
316 raise Exception(f"Could not find {ro_file}")
317 upload(
318 image_id,
319 df,
320 roi_files,
321 omero_username,
322 omero_password,
323 omero_host,
324 omero_secured,
325 verbose,
326 )
327 # Update the full_df with image id:
328 full_df["id"] = [
329 get_image_id(image_file_name)
330 for image_file_name in full_df["Label"]
331 ]
332 # Attach it to the dataset:
333 with BlitzGateway(
334 omero_username, omero_password, host=omero_host,
335 secure=omero_secured
336 ) as conn:
337 full_df["dataset_id"] = [
338 a.id
339 for id in full_df["id"]
340 for a in conn.getObject("Image", id).getAncestry()
341 if a.OMERO_CLASS == "Dataset"
342 ]
343 dir = tempfile.mkdtemp()
344 for dataset_id in np.unique(full_df["dataset_id"]):
345 df = full_df[full_df["dataset_id"] == dataset_id]
346 first_date = df["Date"].to_list()[0]
347 file_to_upload = os.path.join(
348 dir, "Results_from_Fiji_" + first_date + ".csv"
349 )
350 df.to_csv(file_to_upload, index=False)
351 dataset = conn.getObject("Dataset", dataset_id)
352 # create the original file and file annotation
353 # (uploads the file etc.)
354 # namespace = "my.custom.demo.namespace"
355 file_ann = conn.createFileAnnfromLocalFile(
356 file_to_upload
357 ) # , mimetype="text/plain", ns=namespace, desc=None)
358 print(
359 "Attaching FileAnnotation to Dataset: ",
360 "File ID:",
361 file_ann.getId(),
362 ",",
363 file_ann.getFile().getName(),
364 "Size:",
365 file_ann.getFile().getSize(),
366 )
367 dataset.linkAnnotation(file_ann) # link it to dataset.
368
369
370 if __name__ == "__main__":
371 p = argparse.ArgumentParser()
372 p.add_argument("-oh", "--omero-host", type=str,
373 default="idr.openmicroscopy.org")
374 p.add_argument("--omero-secured", action="store_true", default=True)
375 p.add_argument("-cf", "--config-file", dest="config_file",
376 default=None)
377 p.add_argument("--rois", type=str, default=None)
378 p.add_argument("--summaryResults", type=str, default=None)
379 p.add_argument("--verbose", action="store_true")
380 args = p.parse_args()
381 scan_and_upload(
382 args.rois,
383 args.summaryResults,
384 *get_omero_credentials(args.config_file),
385 omero_host=args.omero_host,
386 omero_secured=args.omero_secured,
387 verbose=args.verbose,
388 )