Package omero :: Package util :: Module script_utils
[hide private]
[frames] | no frames]

Source Code for Module omero.util.script_utils

   1  #!/usr/bin/env python 
   2  # -*- coding: utf-8 -*- 
   3  # 
   4  # Copyright (C) 2006-2013 University of Dundee & Open Microscopy Environment. 
   5  # All rights reserved. 
   6  # 
   7  # This program is free software; you can redistribute it and/or modify 
   8  # it under the terms of the GNU General Public License as published by 
   9  # the Free Software Foundation; either version 2 of the License, or 
  10  # (at your option) any later version. 
  11  # 
  12  # This program is distributed in the hope that it will be useful, 
  13  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  15  # GNU General Public License for more details. 
  16  # 
  17  # You should have received a copy of the GNU General Public License along 
  18  # with this program; if not, write to the Free Software Foundation, Inc., 
  19  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
  20   
  21  """ 
  22  Utility methods for dealing with scripts. 
  23  """ 
  24   
  25  import logging 
  26  import os 
  27   
  28  from struct import unpack 
  29   
  30  import omero.clients 
  31  from omero.rtypes import rdouble 
  32  from omero.rtypes import rint 
  33  from omero.rtypes import rstring 
  34  from omero.rtypes import unwrap 
  35  import omero.util.pixelstypetopython as pixelstypetopython 
  36   
  37  try: 
  38      import hashlib 
  39      hash_sha1 = hashlib.sha1 
  40  except: 
  41      import sha 
  42      hash_sha1 = sha.new 
  43   
  44  # r,g,b,a colours for use in scripts. 
  45  COLOURS = {'Red': (255,0,0,255), 'Green': (0,255,0,255), 'Blue': (0,0,255,255), 'Yellow': (255,255,0,255), 
  46      'White': (255,255,255,255), } 
  47   
  48  EXTRA_COLOURS = {'Violet': (238,133,238,255), 'Indigo': (79,6,132,255), 
  49      'Black': (0,0,0,255), 'Orange': (254,200,6,255), 'Gray': (130,130,130,255),} 
  50   
  51  CSV_NS = 'text/csv'; 
  52  CSV_FORMAT = 'text/csv'; 
  53  SU_LOG = logging.getLogger("omero.util.script_utils") 
  54   
55 -def drawTextOverlay(draw, x, y, text, colour='0xffffff'):
56 """ 57 Draw test on image. 58 @param draw The PIL Draw class. 59 @param x The x-coord to draw. 60 @param y The y-coord to draw. 61 @param text The text to render. 62 @param colour The colour as a PIL colour string to draw the text in. 63 """ 64 draw.text((x,y),text, fill=colour)
65
66 -def drawLineOverlay(draw, x0, y0, x1, y1, colour='0xffffff'):
67 """ 68 Draw line on image. 69 @param draw The PIL Draw class. 70 @param x0 The x0-coord of line. 71 @param y0 The y0-coord of line. 72 @param x1 The x1-coord of line. 73 @param y1 The y1-coord of line. 74 @param colour The colour as a PIL colour fill in the line. 75 """ 76 draw.line([(x0, y0),(x1,y1)], fill=colour)
77
78 -def rgbToRGBInt(red, green, blue):
79 """ 80 Convert an R,G,B value to an int. 81 @param R the Red value. 82 @param G the Green value. 83 @param B the Blue value. 84 @return See above. 85 """ 86 RGBInt = (red<<16)+(green<<8)+blue; 87 return int(RGBInt);
88
89 -def RGBToPIL(RGB):
90 """ 91 Convert an RGB value to a PIL colour value. 92 @param RGB the RGB value. 93 @return See above. 94 """ 95 hexval = hex(int(RGB)); 96 return '#'+(6-len(hexval[2:]))*'0'+hexval[2:];
97
98 -def rangeToStr(range):
99 """ 100 Map a range to a string of numbers 101 @param range See above. 102 @return See above. 103 """ 104 first = 1; 105 string = ""; 106 for value in range: 107 if(first==1): 108 string = str(value); 109 first = 0; 110 else: 111 string = string + ','+str(value) 112 return string;
113
114 -def rmdir_recursive(dir):
115 for name in os.listdir(dir): 116 full_name = os.path.join(dir, name) 117 # on Windows, if we don't have write permission we can't remove 118 # the file/directory either, so turn that on 119 if not os.access(full_name, os.W_OK): 120 os.chmod(full_name, 0600) 121 if os.path.isdir(full_name): 122 rmdir_recursive(full_name) 123 else: 124 os.remove(full_name) 125 os.rmdir(dir)
126
127 -def calcSha1(filename):
128 """ 129 Returns a hash of the file identified by filename 130 131 @param filename: pathName of the file 132 @return: The hash of the file 133 """ 134 135 fileHandle = open(filename) 136 h = hash_sha1() 137 h.update(fileHandle.read()) 138 hash = h.hexdigest() 139 fileHandle.close() 140 return hash;
141
142 -def calcSha1FromData(data):
143 """ 144 Calculate the Sha1 Hash from a data array 145 @param data The data array. 146 @return The Hash 147 """ 148 h = hash_sha1() 149 h.update(data) 150 hash = h.hexdigest() 151 return hash;
152
153 -def getFormat(queryService, format):
154 return queryService.findByQuery("from Format as f where f.value='"+format+"'", None)
155 156
157 -def createFile(updateService, filename, mimetype=None, origFilePathName=None):
158 """ 159 Creates an original file, saves it to the server and returns the result 160 161 @param queryService: The query service E.g. session.getQueryService() 162 @param updateService: The update service E.g. session.getUpdateService() 163 @param filename: The file path and name (or name if in same folder). String 164 @param mimetype: The mimetype (string) or Format object representing the file format 165 @param origFilePathName: Optional path/name for the original file 166 @return: The saved OriginalFileI, as returned from the server 167 """ 168 169 originalFile = omero.model.OriginalFileI(); 170 if(origFilePathName == None): 171 origFilePathName = filename; 172 path, name = os.path.split(origFilePathName) 173 originalFile.setName(omero.rtypes.rstring(name)); 174 originalFile.setPath(omero.rtypes.rstring(path)); 175 # just in case we are passed a FormatI object 176 try: 177 v = mimetype.getValue() 178 mt = v.getValue() 179 except: 180 # handle the string we expect 181 mt = mimetype 182 if mt: 183 originalFile.mimetype = omero.rtypes.rstring(mt) 184 originalFile.setSize(omero.rtypes.rlong(os.path.getsize(filename))); 185 originalFile.setSha1(omero.rtypes.rstring(calcSha1(filename))); 186 return updateService.saveAndReturnObject(originalFile);
187 188
189 -def uploadFile(rawFileStore, originalFile, filePath=None):
190 """ 191 Uploads an OriginalFile to the server 192 193 @param rawFileStore: The Omero rawFileStore 194 @param originalFile: The OriginalFileI 195 @param filePath: Where to find the file to upload. If None, use originalFile.getName().getValue() 196 """ 197 rawFileStore.setFileId(originalFile.getId().getValue()); 198 fileSize = originalFile.getSize().getValue(); 199 increment = 10000; 200 cnt = 0; 201 if filePath == None: 202 filePath = originalFile.getName().getValue() 203 fileHandle = open(filePath, 'rb'); 204 done = 0 205 while(done!=1): 206 if(increment+cnt<fileSize): 207 blockSize = increment; 208 else: 209 blockSize = fileSize-cnt; 210 done = 1; 211 fileHandle.seek(cnt); 212 block = fileHandle.read(blockSize); 213 rawFileStore.write(block, cnt, blockSize); 214 cnt = cnt+blockSize; 215 fileHandle.close();
216 217
218 -def downloadFile(rawFileStore, originalFile, filePath=None):
219 """ 220 Downloads an OriginalFile from the server. 221 222 @param rawFileStore: The Omero rawFileStore 223 @param originalFile: The OriginalFileI 224 @param filePath: Where to download the file. If None, use originalFile.getName().getValue() 225 """ 226 fileId = originalFile.getId().getValue() 227 rawFileStore.setFileId(fileId) 228 fileSize = originalFile.getSize().getValue() 229 maxBlockSize = 10000 230 cnt = 0 231 if filePath == None: 232 filePath = originalFile.getName().getValue() 233 # don't overwrite. Add number before extension 234 i = 1 235 path, ext = filePath.rsplit(".", 1) 236 while os.path.exists(filePath): 237 filePath = "%s_%s.%s" % (path,i,ext) 238 i +=1 239 fileHandle = open(filePath, 'w') 240 cnt = 0; 241 fileSize = originalFile.getSize().getValue() 242 while(cnt<fileSize): 243 blockSize = min(maxBlockSize, fileSize) 244 block = rawFileStore.read(cnt, blockSize) 245 cnt = cnt+blockSize 246 fileHandle.write(block) 247 fileHandle.close() 248 return filePath
249 250
251 -def attachFileToParent(updateService, parent, originalFile, description=None, namespace=None):
252 """ 253 Attaches the original file (file) to a Project, Dataset or Image (parent) 254 255 @param updateService: The update service 256 @param parent: A ProjectI, DatasetI or ImageI to attach the file to 257 @param originalFile: The OriginalFileI to attach 258 @param description: Optional description for the file annotation. String 259 @param namespace: Optional namespace for file annotataion. String 260 @return: The saved and returned *AnnotationLinkI (* = Project, Dataset or Image) 261 """ 262 fa = omero.model.FileAnnotationI(); 263 fa.setFile(originalFile); 264 if description: 265 fa.setDescription(omero.rtypes.rstring(description)) 266 if namespace: 267 fa.setNs(omero.rtypes.rstring(namespace)) 268 if type(parent) == omero.model.DatasetI: 269 l = omero.model.DatasetAnnotationLinkI() 270 elif type(parent) == omero.model.ProjectI: 271 l = omero.model.ProjectAnnotationLinkI() 272 elif type(parent) == omero.model.ImageI: 273 l = omero.model.ImageAnnotationLinkI() 274 else: 275 return 276 parent = parent.__class__(parent.id.val, False) # use unloaded object to avoid update conflicts 277 l.setParent(parent); 278 l.setChild(fa); 279 return updateService.saveAndReturnObject(l);
280 281
282 -def uploadAndAttachFile(queryService, updateService, rawFileStore, parent, localName, mimetype, description=None, namespace=None, origFilePathName=None):
283 """ 284 Uploads a local file to the server, as an Original File and attaches it to the 285 parent (Project, Dataset or Image) 286 287 @param queryService: The query service 288 @param updateService: The update service 289 @param rawFileStore: The rawFileStore 290 @param parent: The ProjectI or DatasetI or ImageI to attach file to 291 @param localName: Full Name (and path) of the file location to upload. String 292 @param mimetype: The original file mimetype. E.g. "PNG". String 293 @param description: Optional description for the file annotation. String 294 @param namespace: Namespace to set for the original file 295 @param origFilePathName: The /path/to/file/fileName.ext you want on the server. If none, use output as name 296 @return: The originalFileLink child. (FileAnnotationI) 297 """ 298 299 filename = localName 300 if origFilePathName == None: 301 origFilePathName = localName 302 originalFile = createFile(updateService, filename, mimetype, origFilePathName); 303 uploadFile(rawFileStore, originalFile, localName) 304 fileLink = attachFileToParent(updateService, parent, originalFile, description, namespace) 305 return fileLink.getChild()
306
307 -def createLinkFileAnnotation(conn, localPath, parent, output="Output", parenttype="Image", mimetype=None, desc=None, ns=None, origFilePathAndName=None):
308 """ 309 Uploads a local file to the server, as an Original File and attaches it to the 310 parent (Project, Dataset or Image) 311 312 @param conn: The L{omero.gateway.BlitzGateway} connection. 313 @param parent: The ProjectI or DatasetI or ImageI to attach file to 314 @param localPath: Full Name (and path) of the file location to upload. String 315 @param mimetype: The original file mimetype. E.g. "PNG". String 316 @param description: Optional description for the file annotation. String 317 @param namespace: Namespace to set for the original file 318 @param 319 @param origFilePathName: The /path/to/file/fileName.ext you want on the server. If none, use output as name 320 @return: The originalFileLink child (FileAnnotationI) and a log message 321 """ 322 if os.path.exists(localPath): 323 fileAnnotation = conn.createFileAnnfromLocalFile(localPath, origFilePathAndName=origFilePathAndName, mimetype=mimetype, ns=ns, desc=desc) 324 message = "%s created" % output 325 if parent is not None: 326 if parent.canAnnotate(): 327 parentClass = parent.OMERO_CLASS 328 message += " and attached to %s%s %s." % (parentClass[0].lower(), parentClass[1:], parent.getName()) 329 parent.linkAnnotation(fileAnnotation) 330 else: 331 message += " but could not be attached." 332 else: 333 message = "%s not created." % output 334 fileAnnotation = None 335 return fileAnnotation, message
336
337 -def getObjects(conn, params):
338 """ 339 Get the objects specified by the script parameters. 340 Assume the parameters contain the keys IDs and Data_Type 341 342 @param conn: The L{omero.gateway.BlitzGateway} connection. 343 @param params: The script parameters 344 @return: The valid objects and a log message 345 """ 346 347 dataType = params["Data_Type"] 348 ids = params["IDs"] 349 objects = list(conn.getObjects(dataType,ids)) 350 351 message = "" 352 if not objects: 353 message += "No %s%s found. " % (dataType[0].lower(), dataType[1:]) 354 else: 355 if not len(objects) == len(ids): 356 message += "Found %s out of %s %s%s(s). " % (len(objects), len(ids), dataType[0].lower(), dataType[1:]) 357 358 # Sort the objects according to the order of IDs 359 idMap = dict( [(o.id, o) for o in objects] ) 360 objects = [idMap[i] for i in ids if i in idMap] 361 362 return objects, message
363
364 -def addAnnotationToImage(updateService, image, annotation):
365 """ 366 Add the annotation to an image. 367 @param updateService The update service to create the annotation link. 368 @param image The ImageI object that should be annotated. 369 @param annotation The annotation object 370 @return The new annotationlink object 371 """ 372 l = omero.model.ImageAnnotationLinkI(); 373 l.setParent(image); 374 l.setChild(annotation); 375 return updateService.saveAndReturnObject(l);
376
377 -def readFromOriginalFile(rawFileService, iQuery, fileId, maxBlockSize = 10000):
378 """ 379 Read the OriginalFile with fileId and return it as a string. 380 @param rawFileService The RawFileService service to read the originalfile. 381 @param iQuery The Query Service. 382 @param fileId The id of the originalFile object. 383 @param maxBlockSize The block size of each read. 384 @return The OriginalFile object contents as a string 385 """ 386 fileDetails = iQuery.findByQuery("from OriginalFile as o where o.id = " + str(fileId) , None); 387 rawFileService.setFileId(fileId); 388 data = ''; 389 cnt = 0; 390 fileSize = fileDetails.getSize().getValue(); 391 while(cnt<fileSize): 392 blockSize = min(maxBlockSize, fileSize); 393 block = rawFileService.read(cnt, blockSize); 394 data = data + block; 395 cnt = cnt+blockSize; 396 return data[0:fileSize];
397
398 -def readFileAsArray(rawFileService, iQuery, fileId, row, col, separator = ' '):
399 """ 400 Read an OriginalFile with id and column separator and return it as an array. 401 @param rawFileService The RawFileService service to read the originalfile. 402 @param iQuery The Query Service. 403 @param fileId The id of the originalFile object. 404 @param row The number of rows in the file. 405 @param col The number of columns in the file. 406 @param sep the column separator. 407 @return The file as an NumPy array. 408 """ 409 from numpy import fromstring, reshape 410 textBlock = readFromOriginalFile(rawFileService, iQuery, fileId); 411 arrayFromFile = fromstring(textBlock,sep = separator); 412 return reshape(arrayFromFile, (row, col));
413
414 -def readFlimImageFile(rawPixelsStore, pixels):
415 """ 416 Read the RawImageFlimFile with fileId and return it as an array [c, x, y] 417 @param rawPixelsStore The rawPixelStore service to get the image. 418 @param pixels The pixels of the image. 419 @return The Contents of the image for z = 0, t = 0, all channels; 420 """ 421 from numpy import zeros 422 sizeC = pixels.getSizeC().getValue(); 423 sizeX = pixels.getSizeX().getValue(); 424 sizeY = pixels.getSizeY().getValue(); 425 id = pixels.getId().getValue(); 426 pixelsType = pixels.getPixelsType().getValue().getValue(); 427 rawPixelsStore.setPixelsId(id , False); 428 cRange = range(0, sizeC); 429 stack = zeros((sizeC, sizeX, sizeY),dtype=pixelstypetopython.toNumpy(pixelsType)); 430 for c in cRange: 431 plane = downloadPlane(rawPixelsStore, pixels, 0, c, 0); 432 stack[c,:,:]=plane; 433 return stack;
434
435 -def downloadPlane(rawPixelsStore, pixels, z, c, t):
436 """ 437 Download the plane [z,c,t] for image pixels. Pixels must have pixelsType loaded. 438 N.B. The rawPixelsStore must have already been initialised by setPixelsId() 439 @param rawPixelsStore The rawPixelStore service to get the image. 440 @param pixels The pixels of the image. 441 @param z The Z-Section to retrieve. 442 @param c The C-Section to retrieve. 443 @param t The T-Section to retrieve. 444 @return The Plane of the image for z, c, t 445 """ 446 from numpy import array 447 rawPlane = rawPixelsStore.getPlane(z, c, t); 448 sizeX = pixels.getSizeX().getValue(); 449 sizeY = pixels.getSizeY().getValue(); 450 pixelType = pixels.getPixelsType().getValue().getValue(); 451 convertType ='>'+str(sizeX*sizeY)+pixelstypetopython.toPython(pixelType); 452 convertedPlane = unpack(convertType, rawPlane); 453 numpyType = pixelstypetopython.toNumpy(pixelType) 454 remappedPlane = array(convertedPlane, numpyType); 455 remappedPlane.resize(sizeY, sizeX); 456 return remappedPlane;
457 458
459 -def getPlaneFromImage(imagePath, rgbIndex=None):
460 """ 461 Reads a local image (E.g. single plane tiff) and returns it as a numpy 2D array. 462 463 @param imagePath Path to image. 464 """ 465 from numpy import asarray 466 try: 467 from PIL import Image # see ticket:2597 468 except ImportError: 469 import Image # see ticket:2597 470 471 i = Image.open(imagePath) 472 a = asarray(i) 473 if rgbIndex == None: 474 return a 475 else: 476 return a[:, :, rgbIndex]
477 478
479 -def uploadDirAsImages(sf, queryService, updateService, pixelsService, path, dataset = None):
480 """ 481 Reads all the images in the directory specified by 'path' and uploads them to OMERO as a single 482 multi-dimensional image, placed in the specified 'dataset' 483 Uses regex to determine the Z, C, T position of each image by name, 484 and therefore determines sizeZ, sizeC, sizeT of the new Image. 485 486 @param path the path to the directory containing images. 487 @param dataset the OMERO dataset, if we want to put images somewhere. omero.model.DatasetI 488 """ 489 490 import re 491 from numpy import zeros 492 493 regex_token = re.compile(r'(?P<Token>.+)\.') 494 regex_time = re.compile(r'T(?P<T>\d+)') 495 regex_channel = re.compile(r'_C(?P<C>.+?)(_|$)') 496 regex_zslice = re.compile(r'_Z(?P<Z>\d+)') 497 498 # assume 1 image in this folder for now. 499 # Make a single map of all images. key is (z,c,t). Value is image path. 500 imageMap = {} 501 channelSet = set() 502 tokens = [] 503 504 # other parameters we need to determine 505 sizeZ = 1 506 sizeC = 1 507 sizeT = 1 508 zStart = 1 # could be 0 or 1 ? 509 tStart = 1 510 511 fullpath = None 512 513 rgb = False 514 # process the names and populate our imagemap 515 for f in os.listdir(path): 516 fullpath = os.path.join(path, f) 517 tSearch = regex_time.search(f) 518 cSearch = regex_channel.search(f) 519 zSearch = regex_zslice.search(f) 520 tokSearch = regex_token.search(f) 521 522 if f.endswith(".jpg"): 523 rgb = True 524 525 if tSearch == None: 526 theT = 0 527 else: 528 theT = int(tSearch.group('T')) 529 530 if cSearch == None: 531 cName = "0" 532 else: 533 cName = cSearch.group('C') 534 535 if zSearch == None: 536 theZ = 0 537 else: 538 theZ = int(zSearch.group('Z')) 539 540 channelSet.add(cName) 541 sizeZ = max(sizeZ, theZ) 542 zStart = min(zStart, theZ) 543 sizeT = max(sizeT, theT) 544 tStart = min(tStart, theT) 545 if tokSearch != None: 546 tokens.append(tokSearch.group('Token')) 547 imageMap[(theZ, cName, theT)] = fullpath 548 549 colourMap = {} 550 if not rgb: 551 channels = list(channelSet) 552 # see if we can guess what colour the channels should be, based on name. 553 for i, c in enumerate(channels): 554 if c == 'rfp': 555 colourMap[i] = COLOURS["Red"] 556 if c == 'gfp': 557 colourMap[i] = COLOURS["Green"] 558 else: 559 channels = ("red", "green", "blue") 560 colourMap[0] = COLOURS["Red"] 561 colourMap[1] = COLOURS["Green"] 562 colourMap[2] = COLOURS["Blue"] 563 564 sizeC = len(channels) 565 566 # use the common stem as the image name 567 imageName = os.path.commonprefix(tokens).strip('0T_') 568 description = "Imported from images in %s" % path 569 SU_LOG.info("Creating image: %s" % imageName) 570 571 # use the last image to get X, Y sizes and pixel type 572 if rgb: 573 plane = getPlaneFromImage(fullpath, 0) 574 else: 575 plane = getPlaneFromImage(fullpath) 576 pType = plane.dtype.name 577 # look up the PixelsType object from DB 578 pixelsType = queryService.findByQuery(\ 579 "from PixelsType as p where p.value='%s'" % pType, None) # omero::model::PixelsType 580 if pixelsType == None and pType.startswith("float"): # e.g. float32 581 pixelsType = queryService.findByQuery(\ 582 "from PixelsType as p where p.value='%s'" % "float", None) # omero::model::PixelsType 583 if pixelsType == None: 584 SU_LOG.warn("Unknown pixels type for: %s" % pType) 585 return 586 sizeY, sizeX = plane.shape 587 588 SU_LOG.debug("sizeX: %s sizeY: %s sizeZ: %s sizeC: %s sizeT: %s" % (sizeX, sizeY, sizeZ, sizeC, sizeT)) 589 590 # code below here is very similar to combineImages.py 591 # create an image in OMERO and populate the planes with numpy 2D arrays 592 channelList = range(sizeC) 593 imageId = pixelsService.createImage(sizeX, sizeY, sizeZ, sizeT, channelList, pixelsType, imageName, description) 594 params = omero.sys.ParametersI() 595 params.addId(imageId) 596 pixelsId = queryService.projection(\ 597 "select p.id from Image i join i.pixels p where i.id = :id",\ 598 params)[0][0].val 599 600 rawPixelStore = sf.createRawPixelsStore() 601 rawPixelStore.setPixelsId(pixelsId, True) 602 try: 603 for theC in range(sizeC): 604 minValue = 0 605 maxValue = 0 606 for theZ in range(sizeZ): 607 zIndex = theZ + zStart 608 for theT in range(sizeT): 609 tIndex = theT + tStart 610 if rgb: 611 c = "0" 612 else: 613 c = channels[theC] 614 if (zIndex, c, tIndex) in imageMap: 615 imagePath = imageMap[(zIndex, c, tIndex)] 616 if rgb: 617 SU_LOG.debug("Getting rgb plane from: %s" % imagePath) 618 plane2D = getPlaneFromImage(imagePath, theC) 619 else: 620 SU_LOG.debug("Getting plane from: %s" % imagePath) 621 plane2D = getPlaneFromImage(imagePath) 622 else: 623 SU_LOG.debug("Creating blank plane for .", theZ, channels[theC], theT) 624 plane2D = zeros((sizeY, sizeX)) 625 SU_LOG.debug("Uploading plane: theZ: %s, theC: %s, theT: %s" % (theZ, theC, theT)) 626 627 uploadPlane(rawPixelStore, plane2D, theZ, theC, theT) 628 minValue = min(minValue, plane2D.min()) 629 maxValue = max(maxValue, plane2D.max()) 630 pixelsService.setChannelGlobalMinMax(pixelsId, theC, float(minValue), float(maxValue)) 631 rgba = None 632 if theC in colourMap: 633 rgba = colourMap[theC] 634 try: 635 renderingEngine = sf.createRenderingEngine() 636 resetRenderingSettings(renderingEngine, pixelsId, theC, minValue, maxValue, rgba) 637 finally: 638 renderingEngine.close() 639 finally: 640 rawPixelStore.close() 641 642 # add channel names 643 pixels = pixelsService.retrievePixDescription(pixelsId) 644 i = 0 645 for c in pixels.iterateChannels(): # c is an instance of omero.model.ChannelI 646 lc = c.getLogicalChannel() # returns omero.model.LogicalChannelI 647 lc.setName(rstring(channels[i])) 648 updateService.saveObject(lc) 649 i += 1 650 651 # put the image in dataset, if specified. 652 if dataset: 653 link = omero.model.DatasetImageLinkI() 654 link.parent = omero.model.DatasetI(dataset.id.val, False) 655 link.child = omero.model.ImageI(imageId, False) 656 updateService.saveAndReturnObject(link) 657 658 return imageId
659 660 661
662 -def uploadCecogObjectDetails(updateService, imageId, filePath):
663 """ 664 Parses a single line of cecog output and saves as a roi. 665 666 Adds a Rectangle (particle) to the current OMERO image, at point x, y. 667 Uses the self.image (OMERO image) and self.updateService 668 """ 669 670 objects = {} 671 roi_ids = [] 672 673 import fileinput 674 for line in fileinput.input([filePath]): 675 676 theT = None 677 x = None 678 y = None 679 680 parts = line.split("\t") 681 names = ("frame", "objID", "primaryClassLabel", "primaryClassName", "centerX", "centerY", "mean", "sd", "secondaryClassabel", "secondaryClassName", "secondaryMean", "secondarySd") 682 values = {} 683 for idx, name in enumerate(names): 684 if len(parts) >= idx: 685 values[name] = parts[idx] 686 687 frame = values["frame"] 688 try: 689 frame = long(frame) 690 except ValueError: 691 SU_LOG.debug("Non-roi line: %s " % line) 692 continue 693 694 theT = frame - 1 695 objID = values["objID"] 696 className = values["primaryClassName"] 697 x = float(values["centerX"]) 698 y = float(values["centerY"]) 699 700 description = "" 701 for name in names: 702 description += ("%s=%s\n" % (name, values.get(name, "(missing)"))) 703 704 if theT and x and y: 705 SU_LOG.debug("Adding point '%s' to frame: %s, x: %s, y: %s" % (className, theT, x, y)) 706 try: 707 shapes = objects[objID] 708 except KeyError: 709 shapes = [] 710 objects[objID] = shapes 711 shapes.append( (theT, className, x, y, values, description) ) 712 713 for object, shapes in objects.items(): 714 715 # create an ROI, add the point and save 716 roi = omero.model.RoiI() 717 roi.setImage(omero.model.ImageI(imageId, False)) 718 roi.setDescription(omero.rtypes.rstring("objID: %s" % object)) 719 720 # create and save a point 721 for shape in shapes: 722 723 theT, className, x, y, values, description = shape 724 725 point = omero.model.PointI() 726 point.cx = rdouble(x) 727 point.cy = rdouble(y) 728 point.theT = rint(theT) 729 point.theZ = rint(0) # Workaround for shoola:ticket:1596 730 if className: 731 point.setTextValue(rstring(className)) # for display only 732 733 # link the point to the ROI and save it 734 roi.addShape(point) 735 736 roi = updateService.saveAndReturnObject(point) 737 roi_ids.append(roi.id.val) 738 739 return roi_ids
740 741
742 -def split_image(client, imageId, dir, unformattedImageName = "tubulin_P037_T%05d_C%s_Z%d_S1.tif", dims = ('T', 'C', 'Z')):
743 """ 744 Splits the image into component planes, which are saved as local tiffs according to unformattedImageName. 745 E.g. myLocalDir/tubulin_P037_T%05d_C%s_Z%d_S1.tif which will be formatted according to dims, E.g. ('T', 'C', 'Z') 746 Channel will be formatted according to channel name, not index. 747 @param rawPixelsStore The rawPixelStore 748 @param queryService 749 @param c The C-Section to retrieve. 750 @param t The T-Section to retrieve. 751 @param imageName the local location to save the image. 752 """ 753 754 unformattedImageName = os.path.join(dir, unformattedImageName) 755 756 session = client.getSession() 757 queryService = session.getQueryService() 758 rawPixelsStore = session.createRawPixelsStore() 759 pixelsService = session.getPixelsService() 760 761 try: 762 from PIL import Image # see ticket:2597 763 except: 764 import Image # see ticket:2597 765 766 query_string = "select p from Pixels p join fetch p.image as i join fetch p.pixelsType where i.id='%s'" % imageId 767 pixels = queryService.findByQuery(query_string, None) 768 sizeZ = pixels.getSizeZ().getValue() 769 sizeC = pixels.getSizeC().getValue() 770 sizeT = pixels.getSizeT().getValue() 771 rawPixelsStore.setPixelsId(pixels.getId().getValue(), True) 772 773 channelMap = {} 774 cIndex = 0 775 pixels = pixelsService.retrievePixDescription(pixels.id.val) # load channels 776 for c in pixels.iterateChannels(): 777 lc = c.getLogicalChannel() 778 channelMap[cIndex] = lc.getName() and lc.getName().getValue() or str(cIndex) 779 cIndex += 1 780 781 def formatName(unformatted, z, c, t): 782 # need to turn dims E.g. ('T', 'C', 'Z') into tuple, E.g. (t, c, z) 783 dimMap = {'T': t, 'C':channelMap[c], 'Z': z} 784 dd = tuple([dimMap[d] for d in dims]) 785 return unformatted % dd
786 787 # cecog does this, but other formats may want to start at 0 788 zStart = 1 789 tStart = 1 790 791 # loop through dimensions, saving planes as tiffs. 792 for z in range(sizeZ): 793 for c in range(sizeC): 794 for t in range(sizeT): 795 imageName = formatName(unformattedImageName, z+zStart, c, t+tStart) 796 SU_LOG.debug("downloading plane z: %s c: %s t: %s to %s" % (z, c, t, imageName)) 797 plane = downloadPlane(rawPixelsStore, pixels, z, c, t) 798 i = Image.fromarray(plane) 799 i.save(imageName) 800 801
802 -def createFileFromData(updateService, queryService, filename, data):
803 """ 804 Create a file from the data of type format, setting sha1, .. 805 @param updateService The updateService to create the annotation link. 806 @param filename The name of the file. 807 @param data The data to save. 808 @param format The Format of the file. 809 @return The newly created OriginalFile. 810 """ 811 tempFile = omero.model.OriginalFileI(); 812 tempFile.setName(omero.rtypes.rstring(filename)); 813 tempFile.setPath(omero.rtypes.rstring(filename)); 814 tempFile.setMimetype(omero.rtypes.rstring(CSV_FORMAT)); 815 tempFile.setSize(omero.rtypes.rlong(len(data))); 816 tempFile.setSha1(omero.rtypes.rstring(calcSha1FromData(data))); 817 return updateService.saveAndReturnObject(tempFile);
818
819 -def attachArrayToImage(updateService, image, file, nameSpace):
820 """ 821 Attach an array, stored as a csv file to an image. Returns the annotation. 822 @param updateService The updateService to create the annotation link. 823 @param image The image to attach the data to. 824 @param filename The name of the file. 825 @param namespace The namespace of the file. 826 @return 827 """ 828 fa = omero.model.FileAnnotationI(); 829 fa.setFile(file); 830 fa.setNs(omero.rtypes.rstring(nameSpace)) 831 l = omero.model.ImageAnnotationLinkI(); 832 l.setParent(image); 833 l.setChild(fa); 834 l = updateService.saveAndReturnObject(l); 835 return l.getChild();
836
837 -def uploadArray(rawFileStore, updateService, queryService, image, filename, namespace, array):
838 """ 839 Upload the data to the server, creating the OriginalFile Object and attaching it to the image. 840 @param rawFileStore The rawFileStore used to create the file. 841 @param updateService The updateService to create the annotation link. 842 @param image The image to attach the data to. 843 @param filename The name of the file. 844 @param namespace The name space associated to the annotation. 845 @param data The data to save. 846 @return The newly created file. 847 """ 848 data = arrayToCSV(array); 849 file = createFileFromData(updateService, queryService, filename, data); 850 rawFileStore.setFileId(file.getId().getValue()); 851 fileSize = len(data); 852 increment = 10000; 853 cnt = 0; 854 done = 0 855 while(done!=1): 856 if(increment+cnt<fileSize): 857 blockSize = increment; 858 else: 859 blockSize = fileSize-cnt; 860 done = 1; 861 block = data[cnt:cnt+blockSize]; 862 rawFileStore.write(block, cnt, blockSize); 863 cnt = cnt+blockSize; 864 return attachArrayToImage(updateService, image, file, namespace);
865
866 -def arrayToCSV(data):
867 """ 868 Convert the numpy array data to a csv file. 869 @param data the Numpy Array 870 @return The CSV string. 871 """ 872 size = data.shape; 873 row = size[0]; 874 col = size[1]; 875 strdata =""; 876 for r in range(0,row): 877 for c in range(0, col): 878 strdata = strdata + str(data[r,c]) 879 if(c<col-1): 880 strdata = strdata+','; 881 strdata = strdata + '\n'; 882 return strdata;
883 884
885 -def uploadPlane(rawPixelsStore, plane, z, c, t):
886 """ 887 Upload the plane to the server attching it to the current z,c,t of the already instantiated rawPixelStore. 888 @param rawPixelsStore The rawPixelStore which is already pointing to the data. 889 @param plane The data to upload 890 @param z The Z-Section of the plane. 891 @param c The C-Section of the plane. 892 @param t The T-Section of the plane. 893 """ 894 byteSwappedPlane = plane.byteswap(); 895 convertedPlane = byteSwappedPlane.tostring(); 896 rawPixelsStore.setPlane(convertedPlane, z, c, t)
897 898
899 -def uploadPlaneByRow(rawPixelsStore, plane, z, c, t):
900 """ 901 Upload the plane to the server one row at a time, 902 attching it to the current z,c,t of the already instantiated rawPixelStore. 903 @param rawPixelsStore The rawPixelStore which is already pointing to the data. 904 @param plane The data to upload 905 @param z The Z-Section of the plane. 906 @param c The C-Section of the plane. 907 @param t The T-Section of the plane. 908 """ 909 byteSwappedPlane = plane.byteswap() 910 911 rowCount, colCount = plane.shape 912 for y in range(rowCount): 913 row = byteSwappedPlane[y:y+1, :] # slice y axis into rows 914 convertedRow = row.tostring() 915 rawPixelsStore.setRow(convertedRow, y, z, c, t)
916 917
918 -def getRenderingEngine(session, pixelsId):
919 """ 920 Create the renderingEngine for the pixelsId. 921 @param session The current session to create the renderingEngine from. 922 @return The renderingEngine Service for the pixels. 923 """ 924 renderingEngine = session.createRenderingEngine(); 925 renderingEngine.lookupPixels(pixelsId); 926 if(renderingEngine.lookupRenderingDef(pixelsId)==0): 927 renderingEngine.resetDefaults(); 928 renderingEngine.lookupRenderingDef(pixelsId); 929 renderingEngine.load(); 930 return renderingEngine;
931 932
933 -def createPlaneDef(z,t):
934 """ 935 Create the plane rendering def, for z,t 936 @param Z the Z-Section 937 @param T The T-Point. 938 @return The RenderingDef Object. 939 """ 940 planeDef = omero.romio.PlaneDef() 941 planeDef.t = t; 942 planeDef.z = z; 943 planeDef.x = 0; 944 planeDef.y = 0; 945 planeDef.slice = 0; 946 return planeDef;
947
948 -def getPlaneAsPackedInt(renderingEngine, z, t):
949 """ 950 Get the rendered Image of the plane for the z, t with the default channels. 951 @param renderingEngine The already instantiated renderEngine. 952 @param z The Z-section. 953 @param t The Timepoint. 954 """ 955 planeDef = createPlaneDef(z, t); 956 return renderingEngine.renderAsPackedInt(planeDef);
957
958 -def getRawPixelsStore(session, pixelsId):
959 """ 960 Get the rawPixelsStore for the Image with pixelsId 961 @param pixelsId The pixelsId of the object to retrieve. 962 @return The rawPixelsStore service. 963 """ 964 rawPixelsStore = session.createRawPixelsStore(); 965 rawPixelsStore.setPixelsId(pixelsId); 966 return rawPixelsStore;
967
968 -def getRawFileStore(session, fileId):
969 """ 970 Get the rawFileStore for the file with fileId 971 @param fileId The fileId of the object to retrieve. 972 @return The rawFileStore service. 973 """ 974 rawFileStore = session.createRawFileStore(); 975 rawFileStore.setFileId(fileId); 976 return rawFileStore;
977
978 -def getPlaneInfo(iQuery, pixelsId, asOrderedList = True):
979 """ 980 Get the plane info for the pixels object returning it in order of z,t,c 981 @param iQuery The query service. 982 @param pixelsId The pixels for Id. 983 @param asOrderedList 984 @return list of planeInfoTimes or map["z:t:c:] 985 """ 986 query = "from PlaneInfo as Info where pixels.id='"+str(pixelsId)+"' orderby info.deltaT" 987 infoList = iQuery.findAllByQuery(query,None) 988 989 if(asOrderedList): 990 map = {} 991 for info in infoList: 992 key = "z:"+str(info.theZ.getValue())+"t:"+str(info.theT.getValue())+"c:"+str(info.theC.getValue()); 993 map[key] = info.deltaT.getValue(); 994 return map; 995 else: 996 return infoList;
997
998 -def IdentityFn(commandArgs):
999 return commandArgs;
1000 1001
1002 -def resetRenderingSettings(renderingEngine, pixelsId, cIndex, minValue, maxValue, rgba=None):
1003 """ 1004 Simply resests the rendering settings for a pixel set, according to the min and max values 1005 The rendering engine does NOT have to be primed with pixelsId, as that is handled by this method. 1006 1007 @param renderingEngine The OMERO rendering engine 1008 @param pixelsId The Pixels ID 1009 @param minValue Minimum value of rendering window 1010 @param maxValue Maximum value of rendering window 1011 @param rgba Option to set the colour of the channel. (r,g,b,a) tuple. 1012 """ 1013 1014 renderingEngine.lookupPixels(pixelsId) 1015 if not renderingEngine.lookupRenderingDef(pixelsId): 1016 renderingEngine.resetDefaults() 1017 if rgba == None: 1018 rgba=COLOURS["White"] # probably don't want E.g. single channel image to be blue! 1019 1020 if not renderingEngine.lookupRenderingDef(pixelsId): 1021 raise Exception("Still No Rendering Def") 1022 1023 renderingEngine.load() 1024 renderingEngine.setChannelWindow(cIndex, float(minValue), float(maxValue)) 1025 if rgba: 1026 red, green, blue, alpha = rgba 1027 renderingEngine.setRGBA(cIndex, red, green, blue, alpha) 1028 renderingEngine.saveCurrentSettings()
1029 1030
1031 -def createNewImage(session, plane2Dlist, imageName, description, dataset=None):
1032 """ 1033 Creates a new single-channel, single-timepoint image from the list of 2D numpy arrays in plane2Dlist 1034 with each numpy 2D plane becoming a Z-section. 1035 1036 @param session An OMERO service factory or equivalent with getQueryService() etc. 1037 @param plane2Dlist A list of numpy 2D arrays, corresponding to Z-planes of new image. 1038 @param imageName Name of new image 1039 @param description Description for the new image 1040 @param dataset If specified, put the image in this dataset. omero.model.Dataset object 1041 1042 @return The new OMERO image: omero.model.ImageI 1043 """ 1044 queryService = session.getQueryService() 1045 pixelsService = session.getPixelsService() 1046 rawPixelStore = session.createRawPixelsStore() 1047 renderingEngine = session.createRenderingEngine() 1048 containerService = session.getContainerService() 1049 1050 pType = plane2Dlist[0].dtype.name 1051 pixelsType = queryService.findByQuery("from PixelsType as p where p.value='%s'" % pType, None) # omero::model::PixelsType 1052 1053 theC, theT = (0,0) 1054 1055 # all planes in plane2Dlist should be same shape. 1056 shape = plane2Dlist[0].shape 1057 sizeY, sizeX = shape 1058 minValue = plane2Dlist[0].min() 1059 maxValue = plane2Dlist[0].max() 1060 1061 # get some other dimensions and create the image. 1062 channelList = [theC] # omero::sys::IntList 1063 sizeZ, sizeT = (len(plane2Dlist),1) 1064 iId = pixelsService.createImage(sizeX, sizeY, sizeZ, sizeT, channelList, pixelsType, imageName, description) 1065 imageId = iId.getValue() 1066 image = containerService.getImages("Image", [imageId], None)[0] 1067 1068 # upload plane data 1069 pixelsId = image.getPrimaryPixels().getId().getValue() 1070 rawPixelStore.setPixelsId(pixelsId, True) 1071 for theZ, plane2D in enumerate(plane2Dlist): 1072 minValue = min(minValue, plane2D.min()) 1073 maxValue = max(maxValue, plane2D.max()) 1074 if plane2D.size > 1000000: 1075 uploadPlaneByRow(rawPixelStore, plane2D, theZ, theC, theT) 1076 else: 1077 uploadPlane(rawPixelStore, plane2D, theZ, theC, theT) 1078 pixelsService.setChannelGlobalMinMax(pixelsId, theC, float(minValue), float(maxValue)) 1079 resetRenderingSettings(renderingEngine, pixelsId, theC, minValue, maxValue) 1080 1081 # put the image in dataset, if specified. 1082 if dataset: 1083 link = omero.model.DatasetImageLinkI() 1084 link.parent = omero.model.DatasetI(dataset.id.val, False) 1085 link.child = omero.model.ImageI(image.id.val, False) 1086 session.getUpdateService().saveObject(link) 1087 1088 renderingEngine.close() 1089 rawPixelStore.close() 1090 return image
1091 1092
1093 -def parseInputs(client, session, processFn=IdentityFn):
1094 """ 1095 parse the inputs from the client object and map it to some other form, values may be transformed by function. 1096 @param client The client object 1097 @param session The current session. 1098 @param processFn A function to transform data to some other form. 1099 @return Parsed inputs as defined by ProcessFn. 1100 """ 1101 inputKeys = client.getInputKeys(); 1102 commandArgs = {}; 1103 for key in inputKeys: 1104 commandArgs[key]=client.getInput(key).getValue(); 1105 return processFn(commandArgs);
1106 1107
1108 -def getROIFromImage(iROIService, imageId, namespace=None):
1109 """ 1110 Get the ROI from the server for the image with the namespace 1111 @param iROIService The iROIService object 1112 @param imageId The imageId to retreive ROI from. 1113 @param namespace The namespace of the ROI. 1114 @return See above. 1115 """ 1116 roiOpts = omero.api.RoiOptions() 1117 if(namespace!=None): 1118 roiOpts.namespace = namespace; 1119 return iROIService.findByImage(imageId, roiOpts);
1120
1121 -def toCSV(list):
1122 """ 1123 Convert a list to a Comma Separated Value string. 1124 @param list The list to convert. 1125 @return See above. 1126 """ 1127 lenList = len(list); 1128 cnt = 0; 1129 str = ""; 1130 for item in list: 1131 str = str + item; 1132 if(cnt < lenList-1): 1133 str = str + ","; 1134 cnt = cnt +1; 1135 return str;
1136
1137 -def toList(csvString):
1138 """ 1139 Convert a csv string to a list of strings 1140 @param csvString The CSV string to convert. 1141 @return See above. 1142 """ 1143 list = csvString.split(','); 1144 for index in range(len(list)): 1145 list[index] = list[index].strip(); 1146 return list;
1147
1148 -def registerNamespace(iQuery, iUpdate, namespace, keywords):
1149 """ 1150 Register a workflow with the server, if the workflow does not exist create it and returns it, 1151 otherwise it returns the already created workflow. 1152 @param iQuery The query service. 1153 @param iUpdate The update service. 1154 @param namespace The namespace of the workflow. 1155 @param keywords The keywords associated with the workflow. 1156 @return see above. 1157 """ 1158 from omero.util.OmeroPopo import WorkflowData as WorkflowData 1159 1160 # Support rstring and str namespaces 1161 namespace = unwrap(namespace) 1162 keywords = unwrap(keywords) 1163 1164 workflow = iQuery.findByQuery("from Namespace as n where n.name = '" + namespace+"'", None); 1165 workflowData = WorkflowData(); 1166 if(workflow!=None): 1167 workflowData = WorkflowData(workflow); 1168 else: 1169 workflowData.setNamespace(namespace); 1170 splitKeywords = keywords.split(','); 1171 1172 SU_LOG.debug(workflowData.asIObject()) 1173 for keyword in splitKeywords: 1174 workflowData.addKeyword(keyword); 1175 SU_LOG.debug(workflowData.asIObject()) 1176 workflow = iUpdate.saveAndReturnObject(workflowData.asIObject()); 1177 return WorkflowData(workflow);
1178
1179 -def findROIByImage(roiService, image, namespace):
1180 """ 1181 Finds the ROI with the given namespace linked to the image. Returns a collection of ROIs. 1182 @param roiService The ROI service. 1183 @param image The image the ROIs are linked to . 1184 @param namespace The namespace of the ROI. 1185 @return see above. 1186 """ 1187 from omero.util.OmeroPopo import ROIData as ROIData 1188 roiOptions = omero.api.RoiOptions(); 1189 roiOptions.namespace = omero.rtypes.rstring(namespace); 1190 results = roiService.findByImage(image, roiOptions); 1191 roiList = []; 1192 for roi in results.rois: 1193 roiList.append(ROIData(roi)); 1194 return roiList;
1195