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

Source Code for Module omero.util.imageUtil

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