1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 import getopt, sys, os, subprocess
38 import numpy;
39 from struct import *
40
41 import omero.clients
42 import omero_Constants_ice
43 from omero.rtypes import *
44 import omero.util.pixelstypetopython as pixelstypetopython
45 from omero.util.OmeroPopo import EllipseData as EllipseData
46 from omero.util.OmeroPopo import RectData as RectData
47 from omero.util.OmeroPopo import MaskData as MaskData
48 from omero.util.OmeroPopo import WorkflowData as WorkflowData
49 from omero.util.OmeroPopo import ROIData as ROIData
50
51
52 try:
53 import hashlib
54 hash_sha1 = hashlib.sha1
55 except:
56 import sha
57 hash_sha1 = sha.new
58
59
60 COLOURS = {'Red': (255,0,0,255), 'Green': (0,255,0,255), 'Blue': (0,0,255,255), 'Yellow': (255,255,0,255),
61 'White': (255,255,255,255), }
62
63 EXTRA_COLOURS = {'Violet': (238,133,238,255), 'Indigo': (79,6,132,255),
64 'Black': (0,0,0,255), 'Orange': (254,200,6,255), 'Gray': (130,130,130,255),}
65
66 CSV_NS = 'text/csv';
67 CSV_FORMAT = 'text/csv';
68
69 -def drawTextOverlay(draw, x, y, text, colour='0xffffff'):
70 """
71 Draw test on image.
72 @param draw The PIL Draw class.
73 @param x The x-coord to draw.
74 @param y The y-coord to draw.
75 @param text The text to render.
76 @param colour The colour as a PIL colour string to draw the text in.
77 """
78 draw.text((x,y),text, fill=colour)
79
81 """
82 Draw line on image.
83 @param draw The PIL Draw class.
84 @param x0 The x0-coord of line.
85 @param y0 The y0-coord of line.
86 @param x1 The x1-coord of line.
87 @param y1 The y1-coord of line.
88 @param colour The colour as a PIL colour string to draw the text in.
89 """
90 draw.line([(x0, y0),(x1,y1)], text, fill=colour)
91
93 """
94 Convert an R,G,B value to an int.
95 @param R the Red value.
96 @param G the Green value.
97 @param B the Blue value.
98 @return See above.
99 """
100 RGBInt = (red<<16)+(green<<8)+blue;
101 return int(RGBInt);
102
104 """
105 Convert an RGB value to a PIL colour value.
106 @param RGB the RGB value.
107 @return See above.
108 """
109 hexval = hex(int(RGB));
110 return '#'+(6-len(hexval[2:]))*'0'+hexval[2:];
111
113 """
114 Map a range to a string of numbers
115 @param range See above.
116 @return See above.
117 """
118 first = 1;
119 string = "";
120 for value in range:
121 if(first==1):
122 string = str(value);
123 first = 0;
124 else:
125 string = string + ','+str(value)
126 return string;
127
129 for name in os.listdir(dir):
130 full_name = os.path.join(dir, name)
131
132
133 if not os.access(full_name, os.W_OK):
134 os.chmod(full_name, 0600)
135 if os.path.isdir(full_name):
136 rmdir_recursive(full_name)
137 else:
138 os.remove(full_name)
139 os.rmdir(dir)
140
142 """
143 Returns a hash of the file identified by filename
144
145 @param filename: pathName of the file
146 @return: The hash of the file
147 """
148
149 fileHandle = open(filename)
150 h = hash_sha1()
151 h.update(fileHandle.read())
152 hash = h.hexdigest()
153 fileHandle.close()
154 return hash;
155
157 """
158 Calculate the Sha1 Hash from a data array
159 @param data The data array.
160 @return The Hash
161 """
162 h = hash_sha1()
163 h.update(data)
164 hash = h.hexdigest()
165 return hash;
166
169
170
171 -def createFile(updateService, filename, mimetype=None, origFilePathName=None):
172 """
173 Creates an original file, saves it to the server and returns the result
174
175 @param queryService: The query service E.g. session.getQueryService()
176 @param updateService: The update service E.g. session.getUpdateService()
177 @param filename: The file path and name (or name if in same folder). String
178 @param mimetype: The mimetype (string) or Format object representing the file format
179 @param origFilePathName: Optional path/name for the original file
180 @return: The saved OriginalFileI, as returned from the server
181 """
182
183 originalFile = omero.model.OriginalFileI();
184 if(origFilePathName == None):
185 origFilePathName = filename;
186 path, name = os.path.split(origFilePathName)
187 originalFile.setName(omero.rtypes.rstring(name));
188 originalFile.setPath(omero.rtypes.rstring(path));
189
190 try:
191 v = mimetype.getValue()
192 mt = v.getValue()
193 except:
194
195 mt = mimetype
196 if mt:
197 originalFile.mimetype = omero.rtypes.rstring(mt)
198 originalFile.setSize(omero.rtypes.rlong(os.path.getsize(filename)));
199 originalFile.setSha1(omero.rtypes.rstring(calcSha1(filename)));
200 return updateService.saveAndReturnObject(originalFile);
201
202
203 -def uploadFile(rawFileStore, originalFile, filePath=None):
204 """
205 Uploads an OriginalFile to the server
206
207 @param rawFileStore: The Omero rawFileStore
208 @param originalFile: The OriginalFileI
209 @param filePath: Where to find the file to upload. If None, use originalFile.getName().getValue()
210 """
211 rawFileStore.setFileId(originalFile.getId().getValue());
212 fileSize = originalFile.getSize().getValue();
213 increment = 10000;
214 cnt = 0;
215 if filePath == None:
216 filePath = originalFile.getName().getValue()
217 fileHandle = open(filePath, 'rb');
218 done = 0
219 while(done!=1):
220 if(increment+cnt<fileSize):
221 blockSize = increment;
222 else:
223 blockSize = fileSize-cnt;
224 done = 1;
225 fileHandle.seek(cnt);
226 block = fileHandle.read(blockSize);
227 rawFileStore.write(block, cnt, blockSize);
228 cnt = cnt+blockSize;
229 fileHandle.close();
230
231
232 -def downloadFile(rawFileStore, originalFile, filePath=None):
233 """
234 Downloads an OriginalFile from the server.
235
236 @param rawFileStore: The Omero rawFileStore
237 @param originalFile: The OriginalFileI
238 @param filePath: Where to download the file. If None, use originalFile.getName().getValue()
239 """
240 fileId = originalFile.getId().getValue()
241 rawFileStore.setFileId(fileId)
242 fileSize = originalFile.getSize().getValue()
243 maxBlockSize = 10000
244 cnt = 0
245 if filePath == None:
246 filePath = originalFile.getName().getValue()
247
248 i = 1
249 path, ext = filePath.rsplit(".", 1)
250 while os.path.exists(filePath):
251 filePath = "%s_%s.%s" % (path,i,ext)
252 i +=1
253 fileHandle = open(filePath, 'w')
254 data = '';
255 cnt = 0;
256 fileSize = originalFile.getSize().getValue()
257 while(cnt<fileSize):
258 blockSize = min(maxBlockSize, fileSize)
259 block = rawFileStore.read(cnt, blockSize)
260 cnt = cnt+blockSize
261 fileHandle.write(block)
262 fileHandle.close()
263 return filePath
264
265
266 -def attachFileToParent(updateService, parent, originalFile, description=None, namespace=None):
267 """
268 Attaches the original file (file) to a Project, Dataset or Image (parent)
269
270 @param updateService: The update service
271 @param parent: A ProjectI, DatasetI or ImageI to attach the file to
272 @param originalFile: The OriginalFileI to attach
273 @param description: Optional description for the file annotation. String
274 @param namespace: Optional namespace for file annotataion. String
275 @return: The saved and returned *AnnotationLinkI (* = Project, Dataset or Image)
276 """
277 fa = omero.model.FileAnnotationI();
278 fa.setFile(originalFile);
279 if description:
280 fa.setDescription(omero.rtypes.rstring(description))
281 if namespace:
282 fa.setNs(omero.rtypes.rstring(namespace))
283 if type(parent) == omero.model.DatasetI:
284 l = omero.model.DatasetAnnotationLinkI()
285 elif type(parent) == omero.model.ProjectI:
286 l = omero.model.ProjectAnnotationLinkI()
287 elif type(parent) == omero.model.ImageI:
288 l = omero.model.ImageAnnotationLinkI()
289 else:
290 return
291 parent = parent.__class__(parent.id.val, False)
292 l.setParent(parent);
293 l.setChild(fa);
294 return updateService.saveAndReturnObject(l);
295
296
297 -def uploadAndAttachFile(queryService, updateService, rawFileStore, parent, localName, mimetype, description=None, namespace=None, origFilePathName=None):
298 """
299 Uploads a local file to the server, as an Original File and attaches it to the
300 parent (Project, Dataset or Image)
301
302 @param queryService: The query service
303 @param updateService: The update service
304 @param rawFileStore: The rawFileStore
305 @param parent: The ProjectI or DatasetI or ImageI to attach file to
306 @param localName: Full Name (and path) of the file location to upload. String
307 @param mimetype: The original file mimetype. E.g. "PNG". String
308 @param description: Optional description for the file annotation. String
309 @param namespace: Namespace to set for the original file
310 @param origFilePathName: The /path/to/file/fileName.ext you want on the server. If none, use output as name
311 @return: The originalFileLink child. (FileAnnotationI)
312 """
313
314 filename = localName
315 if origFilePathName == None:
316 origFilePathName = localName
317 originalFile = createFile(updateService, filename, mimetype, origFilePathName);
318 uploadFile(rawFileStore, originalFile, localName)
319 fileLink = attachFileToParent(updateService, parent, originalFile, description, namespace)
320 return fileLink.getChild()
321
322
324 """
325 Add the annotation to an image.
326 @param updateService The update service to create the annotation link.
327 @param image The ImageI object that should be annotated.
328 @param annotation The annotation object
329 @return The new annotationlink object
330 """
331 l = omero.model.ImageAnnotationLinkI();
332 l.setParent(image);
333 l.setChild(annotation);
334 return updateService.saveAndReturnObject(l);
335
337 """
338 Read the OriginalFile with fileId and return it as a string.
339 @param rawFileService The RawFileService service to read the originalfile.
340 @param iQuery The Query Service.
341 @param fileId The id of the originalFile object.
342 @param maxBlockSize The block size of each read.
343 @return The OriginalFile object contents as a string
344 """
345 fileDetails = iQuery.findByQuery("from OriginalFile as o where o.id = " + str(fileId) , None);
346 rawFileService.setFileId(fileId);
347 data = '';
348 cnt = 0;
349 fileSize = fileDetails.getSize().getValue();
350 while(cnt<fileSize):
351 blockSize = min(maxBlockSize, fileSize);
352 block = rawFileService.read(cnt, blockSize);
353 data = data + block;
354 cnt = cnt+blockSize;
355 return data;
356
357 -def readFileAsArray(rawFileService, iQuery, fileId, row, col, separator = ' '):
358 """
359 Read an OriginalFile with id and column separator and return it as an array.
360 @param rawFileService The RawFileService service to read the originalfile.
361 @param iQuery The Query Service.
362 @param fileId The id of the originalFile object.
363 @param row The number of rows in the file.
364 @param col The number of columns in the file.
365 @param sep the column separator.
366 @return The file as an NumPy array.
367 """
368 textBlock = readFromOriginalFile(rawFileService, iQuery, fileId);
369 arrayFromFile = numpy.fromstring(textBlock,sep = separator);
370 return numpy.reshape(arrayFromFile, (row, col));
371
373 """
374 Read the RawImageFlimFile with fileId and return it as an array [c, x, y]
375 @param rawPixelsStore The rawPixelStore service to get the image.
376 @param pixels The pixels of the image.
377 @return The Contents of the image for z = 0, t = 0, all channels;
378 """
379 sizeC = pixels.getSizeC().getValue();
380 sizeX = pixels.getSizeX().getValue();
381 sizeY = pixels.getSizeY().getValue();
382 id = pixels.getId().getValue();
383 pixelsType = pixels.getPixelsType().getValue().getValue();
384 rawPixelsStore.setPixelsId(id , False);
385 cRange = range(0, sizeC);
386 stack = numpy.zeros((sizeC, sizeX, sizeY),dtype=pixelstypetopython.toNumpy(pixelsType));
387 for c in cRange:
388 plane = downloadPlane(rawPixelsStore, pixels, 0, c, 0);
389 stack[c,:,:]=plane;
390 return stack;
391
393 """
394 Download the plane [z,c,t] for image pixels.
395 @param rawPixelsStore The rawPixelStore service to get the image.
396 @param pixels The pixels of the image.
397 @param z The Z-Section to retrieve.
398 @param c The C-Section to retrieve.
399 @param t The T-Section to retrieve.
400 @return The Plane of the image for z, c, t
401 """
402 rawPlane = rawPixelsStore.getPlane(z, c, t);
403 sizeX = pixels.getSizeX().getValue();
404 sizeY = pixels.getSizeY().getValue();
405 pixelsId = pixels.getId().getValue();
406 pixelType = pixels.getPixelsType().getValue().getValue();
407 convertType ='>'+str(sizeX*sizeY)+pixelstypetopython.toPython(pixelType);
408 convertedPlane = unpack(convertType, rawPlane);
409 numpyType = pixelstypetopython.toNumpy(pixelType)
410 remappedPlane = numpy.array(convertedPlane, numpyType);
411 remappedPlane.resize(sizeY, sizeX);
412 return remappedPlane;
413
415 """
416 Create a file from the data of type format, setting sha1, ..
417 @param updateService The updateService to create the annotation link.
418 @param filename The name of the file.
419 @param data The data to save.
420 @param format The Format of the file.
421 @return The newly created OriginalFile.
422 """
423 tempFile = omero.model.OriginalFileI();
424 tempFile.setName(omero.rtypes.rstring(filename));
425 tempFile.setPath(omero.rtypes.rstring(filename));
426 tempFile.setMimetype(omero.rtypes.rstring(CSV_FORMAT));
427 tempFile.setSize(omero.rtypes.rlong(len(data)));
428 tempFile.setSha1(omero.rtypes.rstring(calcSha1FromData(data)));
429 return updateService.saveAndReturnObject(tempFile);
430
432 """
433 Attach an array, stored as a csv file to an image.
434 @param updateService The updateService to create the annotation link.
435 @param image The image to attach the data to.
436 @param filename The name of the file.
437 @param namespace The namespace of the file.
438 """
439 fa = omero.model.FileAnnotationI();
440 fa.setFile(file);
441 fa.setNs(omero.rtypes.rstring(nameSpace))
442 l = omero.model.ImageAnnotationLinkI();
443 l.setParent(image);
444 l.setChild(fa);
445 l = updateService.saveAndReturnObject(l);
446
447 -def uploadArray(rawFileStore, updateService, queryService, image, filename, array):
448 """
449 Upload the data to the server, creating the OriginalFile Object and attaching it to the image.
450 @param rawFileStore The rawFileStore used to create the file.
451 @param updateService The updateService to create the annotation link.
452 @param image The image to attach the data to.
453 @param filename The name of the file.
454 @param data The data to save.
455 """
456 data = arrayToCSV(array);
457 file = createFileFromData(updateService, queryService, filename, data);
458 rawFileStore.setFileId(file.getId().getValue());
459 fileSize = len(data);
460 increment = 10000;
461 cnt = 0;
462 done = 0
463 while(done!=1):
464 if(increment+cnt<fileSize):
465 blockSize = increment;
466 else:
467 blockSize = fileSize-cnt;
468 done = 1;
469 block = data[cnt:cnt+blockSize];
470 rawFileStore.write(block, cnt, blockSize);
471 cnt = cnt+blockSize;
472 attachArrayToImage(updateService, image, file, CSV_NS)
473
475 """
476 Convert the numpy array data to a csv file.
477 @param data the Numpy Array
478 @return The CSV string.
479 """
480 size = data.shape;
481 row = size[0];
482 col = size[1];
483 strdata ="";
484 for r in range(0,row):
485 for c in range(0, col):
486 strdata = strdata + str(data[r,c])
487 if(c<col-1):
488 strdata = strdata+',';
489 strdata = strdata + '\n';
490 return strdata;
491
492
494 """
495 Upload the plane to the server attching it to the current z,c,t of the already instantiated rawPixelStore.
496 @param rawPixelsStore The rawPixelStore which is already pointing to the data.
497 @param plane The data to upload
498 @param z The Z-Section of the plane.
499 @param c The C-Section of the plane.
500 @param t The T-Section of the plane.
501 """
502 byteSwappedPlane = plane.byteswap();
503 convertedPlane = byteSwappedPlane.tostring();
504 rawPixelsStore.setPlane(convertedPlane, z, c, t)
505
506
508 """
509 Upload the plane to the server one row at a time,
510 attching it to the current z,c,t of the already instantiated rawPixelStore.
511 @param rawPixelsStore The rawPixelStore which is already pointing to the data.
512 @param plane The data to upload
513 @param z The Z-Section of the plane.
514 @param c The C-Section of the plane.
515 @param t The T-Section of the plane.
516 """
517 byteSwappedPlane = plane.byteswap()
518
519 rowCount, colCount = plane.shape
520 for y in range(rowCount):
521 row = byteSwappedPlane[y:y+1, :]
522 convertedRow = row.tostring()
523 rawPixelsStore.setRow(convertedRow, y, z, c, t)
524
525
527 """
528 Create the renderingEngine for the pixelsId.
529 @param session The current session to create the renderingEngine from.
530 @return The renderingEngine Service for the pixels.
531 """
532 renderingEngine = session.createRenderingEngine();
533 renderingEngine.lookupPixels(pixelsId);
534 if(renderingEngine.lookupRenderingDef(pixelsId)==0):
535 renderingEngine.resetDefaults();
536 renderingEngine.lookupRenderingDef(pixelsId);
537 renderingEngine.load();
538 return renderingEngine;
539
540
542 """
543 Create the plane rendering def, for z,t
544 @param Z the Z-Section
545 @param T The T-Point.
546 @return The RenderingDef Object.
547 """
548 planeDef = omero.romio.PlaneDef()
549 planeDef.t = t;
550 planeDef.z = z;
551 planeDef.x = 0;
552 planeDef.y = 0;
553 planeDef.slice = 0;
554 return planeDef;
555
557 """
558 Get the rendered Image of the plane for the z, t with the default channels.
559 @param renderingEngine The already instantiated renderEngine.
560 @param z The Z-section.
561 @param t The Timepoint.
562 """
563 planeDef = createPlaneDef(z, t);
564 return renderingEngine.renderAsPackedInt(planeDef);
565
567 """
568 Get the rawPixelsStore for the Image with pixelsId
569 @param pixelsId The pixelsId of the object to retrieve.
570 @return The rawPixelsStore service.
571 """
572 rawPixelsStore = session.createRawPixelsStore();
573 rawPixelsStore.setPixelsId(pixelsId);
574 return rawPixelsStore;
575
577 """
578 Get the rawFileStore for the file with fileId
579 @param fileId The fileId of the object to retrieve.
580 @return The rawFileStore service.
581 """
582 rawFileStore = session.createRawFileStore();
583 rawFileStore.setFileId(fileId);
584 return rawFileStore;
585
587 """
588 Get the plane info for the pixels object returning it in order of z,t,c
589 @param iQuery The query service.
590 @param pixelsId The pixels for Id.
591 @param asOrderedList
592 @return list of planeInfoTimes or map["z:t:c:]
593 """
594 query = "from PlaneInfo as Info where pixels.id='"+str(pixelsId)+"' orderby info.deltaT"
595 infoList = queryService.findAllByQuery(query,None)
596
597 if(asOrderedList):
598 map = {}
599 for info in infoList:
600 key = "z:"+str(info.theZ.getValue())+"t:"+str(info.theT.getValue())+"c:"+str(info.theC.getValue());
601 map[key] = info.deltaT.getValue();
602 return map;
603 else:
604 return infoList;
605
608
609
611 """
612 Simply resests the rendering settings for a pixel set, according to the min and max values
613 The rendering engine does NOT have to be primed with pixelsId, as that is handled by this method.
614
615 @param renderingEngine The OMERO rendering engine
616 @param pixelsId The Pixels ID
617 @param minValue Minimum value of rendering window
618 @param maxValue Maximum value of rendering window
619 @param rgba Option to set the colour of the channel. (r,g,b,a) tuple.
620 """
621
622 renderingEngine.lookupPixels(pixelsId)
623 if not renderingEngine.lookupRenderingDef(pixelsId):
624 renderingEngine.resetDefaults()
625 if rgba == None:
626 rgba=(255,255,255,255)
627
628 if not renderingEngine.lookupRenderingDef(pixelsId):
629 raise "Still No Rendering Def"
630
631 renderingEngine.load()
632 renderingEngine.setChannelWindow(cIndex, float(minValue), float(maxValue))
633 if rgba:
634 red, green, blue, alpha = rgba
635 renderingEngine.setRGBA(cIndex, red, green, blue, alpha)
636 renderingEngine.saveCurrentSettings()
637
638
639 -def createNewImage(pixelsService, rawPixelStore, renderingEngine, pixelsType, gateway, plane2Dlist, imageName, description, dataset=None):
640 """
641 Creates a new single-channel, single-timepoint image from the list of 2D numpy arrays in plane2Dlist
642 with each numpy 2D plane becoming a Z-section.
643
644 @param pixelsService The OMERO pixelsService
645 @param rawPixelStore The OMERO rawPixelsStore
646 @param renderingEngine The OMERO renderingEngine
647 @param pixelsType The pixelsType object omero::model::PixelsType
648 @param gateway The OMERO gateway service
649 @param plane2Dlist A list of numpy 2D arrays, corresponding to Z-planes of new image.
650 @param imageName Name of new image
651 @param description Description for the new image
652 @param dataset If specified, put the image in this dataset. omero.model.Dataset object
653
654 @return The new OMERO image: omero.model.ImageI
655 """
656 theC, theT = (0,0)
657
658
659 shape = plane2Dlist[0].shape
660 sizeY, sizeX = shape
661 minValue = plane2Dlist[0].min()
662 maxValue = plane2Dlist[0].max()
663
664
665 channelList = [theC]
666 sizeZ, sizeT = (len(plane2Dlist),1)
667 iId = pixelsService.createImage(sizeX, sizeY, sizeZ, sizeT, channelList, pixelsType, imageName, description)
668 imageId = iId.getValue()
669 image = gateway.getImage(imageId)
670
671
672 pixelsId = image.getPrimaryPixels().getId().getValue()
673 rawPixelStore.setPixelsId(pixelsId, True)
674 for theZ, plane2D in enumerate(plane2Dlist):
675 minValue = min(minValue, plane2D.min())
676 maxValue = max(maxValue, plane2D.max())
677 if plane2D.size > 1000000:
678 uploadPlaneByRow(rawPixelStore, plane2D, theZ, theC, theT)
679 else:
680 uploadPlane(rawPixelStore, plane2D, theZ, theC, theT)
681 pixelsService.setChannelGlobalMinMax(pixelsId, theC, float(minValue), float(maxValue))
682 resetRenderingSettings(renderingEngine, pixelsId, theC, minValue, maxValue)
683
684
685 if dataset:
686 link = omero.model.DatasetImageLinkI()
687 link.parent = omero.model.DatasetI(dataset.id.val, False)
688 link.child = omero.model.ImageI(image.id.val, False)
689 gateway.saveAndReturnObject(link)
690
691 return image
692
693
707
708
710 """
711 Get the ROI from the server for the image with the namespace
712 @param iROIService The iROIService object
713 @param imageId The imageId to retreive ROI from.
714 @param namespace The namespace of the ROI.
715 @return See above.
716 """
717 roiOpts = ROIOptions();
718 if(namespace!=None):
719 roiOpts.namespace = namespace;
720 return iROIService.findByImage(imageId, roiOpts);
721
723 """
724 Convert a list to a Comma Separated Value string.
725 @param list The list to convert.
726 @return See above.
727 """
728 lenList = len(list);
729 cnt = 0;
730 str = "";
731 for item in list:
732 str = str + item;
733 if(cnt < lenList-1):
734 str = str + ",";
735 cnt = cnt +1;
736 return str;
737
739 """
740 Convert a csv string to a list of strings
741 @param csvString The CSV string to convert.
742 @return See above.
743 """
744 list = csvString.split(',');
745 for index in range(len(list)):
746 list[index] = list[index].strip();
747 return list;
748
750 """
751 Register a workflow with the server, if the workflow does not exist create it and returns it,
752 otherwise it returns the already created workflow.
753 @param iQuery The query service.
754 @param iUpdate The update service.
755 @param namespace The namespace of the workflow.
756 @param keywords The keywords associated with the workflow.
757 @return see above.
758 """
759 workflow = iQuery.findByQuery("from Namespace as n where n.name = '" + namespace+"'", None);
760 workflowData = None;
761 if(workflow!=None):
762 return;
763 workflowData = WorkflowData();
764 workflowData.setNamespace(namespace);
765 workflowData.setKeywords(keywords);
766 workflow = iUpdate.saveAndReturnObject(workflowData.asIObject());
767 return WorkflowData(workflow);
768
770 roiOptions = omero.api.RoiOptions();
771 roiOptions.namespace = omero.rtypes.rstring(namespace);
772 results = roiService.findByImage(image, roiOptions);
773 roiList = [];
774 for roi in results.rois:
775 roiList.append(ROIData(roi));
776 return roiList;
777