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

Source Code for Module omero.util.imageUtil

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  """ 
  4   components/tools/OmeroPy/src/omero/util/imageUitl.py 
  5   
  6  ----------------------------------------------------------------------------- 
  7    Copyright (C) 2006-2009 University of Dundee. All rights reserved. 
  8   
  9   
 10    This program is free software; you can redistribute it and/or modify 
 11    it under the terms of the GNU General Public License as published by 
 12    the Free Software Foundation; either version 2 of the License, or 
 13    (at your option) any later version. 
 14    This program is distributed in the hope that it will be useful, 
 15    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 17    GNU General Public License for more details. 
 18     
 19    You should have received a copy of the GNU General Public License along 
 20    with this program; if not, write to the Free Software Foundation, Inc., 
 21    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 22   
 23  ------------------------------------------------------------------------------ 
 24   
 25  A collection of utility methods based on the Python Imaging Library (PIL) 
 26  used for making figures.  
 27   
 28  @author  William Moore      
 29  <a href="mailto:will@lifesci.dundee.ac.uk">will@lifesci.dundee.ac.uk</a> 
 30  @author  Jean-Marie Burel &nbsp;&nbsp;&nbsp;&nbsp; 
 31  <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> 
 32  @author Donald MacDonald &nbsp;&nbsp;&nbsp;&nbsp; 
 33  <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> 
 34  @version 3.0 
 35  <small> 
 36  (<b>Internal version:</b> $Revision: $Date: $) 
 37  </small> 
 38  @since 3.0-Beta4.1 
 39    
 40  """ 
 41   
 42   
 43  try: 
 44      from PIL import Image, ImageDraw, ImageFont # see ticket:2597 
 45  except ImportError: 
 46      import Image, ImageDraw, ImageFont # see ticket:2597 
 47   
 48  import os.path 
 49  import omero.gateway 
 50  import StringIO 
 51  from omero.rtypes import * 
 52   
 53  GATEWAYPATH = omero.gateway.THISPATH 
 54   
55 -def getFont(fontsize):
56 """ 57 Returns a PIL ImageFont Sans-serif true-type font of the specified size 58 or a pre-compiled font of fixed size if the ttf font is not found 59 60 @param fontsize: The size of the font you want 61 @return: A PIL Font 62 """ 63 64 fontPath = os.path.join(GATEWAYPATH, "pilfonts", "FreeSans.ttf") 65 try: 66 font = ImageFont.truetype ( fontPath, fontsize ) 67 except: 68 font = ImageFont.load('%s/pilfonts/B%0.2d.pil' % (GATEWAYPATH, 24) ) 69 return font
70 71
72 -def pasteImage(image, canvas, x, y):
73 """ 74 Pastes the image onto the canvas at the specified coordinates 75 Image and canvas are instances of PIL 'Image' 76 77 @param image: The PIL image to be pasted. Image 78 @param canvas: The PIL image on which to paste. Image 79 @param x: X coordinate (left) to paste 80 @param y: Y coordinate (top) to paste 81 """ 82 83 xRight = image.size[0] + x 84 yBottom = image.size[1] + y 85 # make a tuple of topleft-x, topleft-y, bottomRight-x, bottomRight-y 86 pasteBox = (x, y, xRight, yBottom) 87 canvas.paste(image, pasteBox)
88 89
90 -def getThumbnail(thumbnailStore, pixelsId, length):
91 """ 92 Returns a thumbnail (as string) from the pixelsId, the longest side is 'length' 93 94 @param thumbnailStore: The Omero thumbnail store 95 @param pixelsId: The ID of the pixels. long 96 @param length: Length of longest side. int 97 @return: The thumbnail as a String, or None if not found (invalid image) 98 """ 99 if not thumbnailStore.setPixelsId(pixelsId): 100 thumbnailStore.needDefaults() 101 thumbnailStore.setPixelsId(pixelsId) 102 try: 103 return thumbnailStore.getThumbnailByLongestSide(rint(length)) # returns string (api says Ice::ByteSeq) 104 except: 105 return None
106
107 -def getThumbnailSet(thumbnailStore, length, pixelIds):
108 """ 109 Returns map of thumbnails whose keys are the pixels id and the values are the image, the longest side is 'length' 110 111 @param thumbnailStore: The Omero thumbnail store 112 @param pixelIds: The collection of pixels ID. 113 @param length: Length of longest side. int 114 @return: See above 115 """ 116 try: 117 return thumbnailStore.getThumbnailByLongestSideSet(rint(length), pixelIds) # returns string (api says Ice::ByteSeq) 118 except: 119 return None
120
121 -def paintThumbnailGrid(thumbnailStore, length, spacing, pixelIds, colCount, bg=(255,255,255), 122 leftLabel=None, textColour=(0,0,0), fontsize=None, topLabel=None):
123 """ 124 Retrieves thumbnails for each pixelId, and places them in a grid, with White background. 125 Option to add a vertical label to the left of the canvas 126 Creates a PIL 'Image' which is returned 127 128 @param thumbnailStore: The omero thumbnail store. 129 @param length: Length of longest thumbnail side. int 130 @param spacing: The spacing between thumbnails and around the edges. int 131 @param pixelIds: List of pixel IDs. [long] 132 @param colCount: The number of columns. int 133 @param bg: Background colour as (r,g,b). Default is white (255,255,255) 134 @param leftLabel: Optional string to display vertically to the left. 135 @param textColour: The colour of the text as (r,g,b). Default is black (0,0,0) 136 @param fontsize: Size of the font. Defualt is calculated based on thumbnail length. int 137 @return: The PIL Image canvas. 138 """ 139 mode = "RGB" 140 # work out how many rows and columns are needed for all the images 141 imgCount = len(pixelIds) 142 143 rowCount = (imgCount/colCount) 144 while (colCount * rowCount) < imgCount: # check that we have enough rows and cols... 145 rowCount += 1 146 147 leftSpace = topSpace = spacing 148 minWidth = 0 149 150 textHeight = 0 151 if leftLabel or topLabel: 152 # if no images (no rows), need to make at least one row to show label 153 if leftLabel is not None and rowCount == 0: rowCount = 1 154 if fontsize == None: 155 fontsize = length/10 + 5 156 font = getFont(fontsize) 157 if leftLabel: 158 textWidth, textHeight = font.getsize(leftLabel) 159 leftSpace = spacing + textHeight + spacing 160 if topLabel: 161 textWidth, textHeight = font.getsize(topLabel) 162 topSpace = spacing + textHeight + spacing 163 minWidth = leftSpace + textWidth + spacing 164 165 # work out the canvas size needed, and create a white canvas 166 colsNeeded = min(colCount, imgCount) 167 canvasWidth = max(minWidth, (leftSpace + colsNeeded * (length+spacing))) 168 canvasHeight = topSpace + rowCount * (length+spacing) + spacing 169 mode = "RGB" 170 size = (canvasWidth, canvasHeight) 171 canvas = Image.new(mode, size, bg) 172 173 # to write text up the left side, need to write it on horizontal canvas and rotate. 174 if leftLabel: 175 labelCanvasWidth = canvasHeight 176 labelCanvasHeight = textHeight + spacing 177 labelSize = (labelCanvasWidth, labelCanvasHeight) 178 textCanvas = Image.new(mode, labelSize, bg) 179 draw = ImageDraw.Draw(textCanvas) 180 textWidth = font.getsize(leftLabel)[0] 181 textX = (labelCanvasWidth - textWidth) / 2 182 draw.text((textX, spacing), leftLabel, font=font, fill=textColour) 183 verticalCanvas = textCanvas.rotate(90) 184 pasteImage(verticalCanvas, canvas, 0, 0) 185 del draw 186 187 if topLabel is not None: 188 labelCanvasWidth = canvasWidth 189 labelCanvasHeight = textHeight + spacing 190 labelSize = (labelCanvasWidth, labelCanvasHeight) 191 textCanvas = Image.new(mode, labelSize, bg) 192 draw = ImageDraw.Draw(textCanvas) 193 draw.text((spacing, spacing), topLabel, font=font, fill=textColour) 194 pasteImage(textCanvas, canvas, leftSpace, 0) 195 del draw 196 197 # loop through the images, getting a thumbnail and placing it on a new row and column 198 r = 0 199 c = 0 200 thumbnailMap = getThumbnailSet(thumbnailStore, length, pixelIds) 201 for pixelsId in pixelIds: 202 if pixelsId in thumbnailMap: 203 thumbnail = thumbnailMap[pixelsId]#getThumbnail(thumbnailStore, pixelsId, length) 204 if thumbnail: # check we have a thumbnail (won't get one if image is invalid) 205 thumbImage = Image.open(StringIO.StringIO(thumbnail)) # make an "Image" from the string-encoded thumbnail 206 # paste the image onto the canvas at the correct coordinates for the current row and column 207 x = c*(length+spacing) + leftSpace 208 y = r*(length+spacing) + topSpace 209 pasteImage(thumbImage, canvas, x, y) 210 211 # increment the column, and if we're at the last column, start a new row 212 c = c + 1 213 if c == colCount: 214 c = 0 215 r = r + 1 216 217 return canvas
218 219
220 -def checkRGBRange(value):
221 """ 222 Checks that the value is between 0 and 255. Returns integer value 223 If the value is not valid, return 255 (better to see something than nothing! ) 224 225 @param value: The value to check. 226 @return: An integer between 0 and 255 227 """ 228 try: 229 v = int(value) 230 if 0 <= v <= 255: 231 return v 232 except: 233 return 255
234 235
236 -def RGBIntToRGBA(RGB):
237 """ 238 Returns a tuple of (r,g,b,a) from an integer colour 239 r, g, b, a are 0-255. 240 241 @param RGB: A colour as integer. Int 242 @return: A tuple of (r,g,b,a) 243 """ 244 r = checkRGBRange((RGB >> 16) & 0xFF) 245 g = checkRGBRange((RGB >> 8) & 0xFF) 246 b = checkRGBRange((RGB >> 0) & 0xFF) 247 a = checkRGBRange((RGB >> 24) & 0xFF) 248 if a == 0: 249 a = 255 250 return (r,g,b,a)
251 252
253 -def RGBIntToRGB(RGB):
254 """ 255 Returns a tuple of (r,g,b) from an integer colour 256 r, g, b are 0-255. 257 258 @param RGB: A colour as integer. Int 259 @return: A tuple of (r,g,b) 260 """ 261 r,g,b,a = RGBIntToRGBA(RGB) 262 return (r,g,b)
263 264
265 -def getZoomFactor(imageSize, maxW, maxH):
266 """ 267 Returns the factor by which the Image has to be shrunk so that it's dimensions are less that maxW and maxH 268 E.g. If the image must be half-sized, this method returns 2.0 (float) 269 270 @param imageSize: Size of the image as tuple (width, height) 271 @param maxW: The max width after zooming 272 @param maxH: The max height after zooming 273 @return: The factor by which to shrink the image to be within max width and height 274 """ 275 imageW, imageH = imageSize 276 zoomW = float(imageW) / float(maxW) 277 zoomH = float(imageH) / float(maxH) 278 return max(zoomW, zoomH)
279 280
281 -def resizeImage(image, maxW, maxH):
282 """ 283 Resize the image so that it is as big as possible, within the dimensions maxW, maxH 284 285 @param image: The PIL Image to zoom 286 @param maxW: The max width of the zoomed image 287 @param maxH: The max height of the zoomed image 288 @return: The zoomed image. PIL Image. 289 """ 290 imageW, imageH = image.size 291 if imageW == maxW and imageH == maxH: 292 return image 293 # find which axis requires the biggest zoom (smallest relative max dimension) 294 zoomW = float(imageW) / float(maxW) 295 zoomH = float(imageH) / float(maxH) 296 zoom = max(zoomW, zoomH) 297 if zoomW >= zoomH: # size is defined by width 298 maxH = int(imageH//zoom) # calculate the new height 299 else: 300 maxW = int(imageW//zoom) 301 return image.resize((maxW, maxH))
302