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
29 <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
30 @author Donald MacDonald
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
43 except ImportError:
44 import Image, ImageDraw, ImageFont
45
46 import os.path
47 import omero.gateway
48 import StringIO
49 from omero.rtypes import *
50
51 GATEWAYPATH = omero.gateway.THISPATH
52
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
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
84 pasteBox = (x, y, xRight, yBottom)
85 canvas.paste(image, pasteBox)
86
87
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))
102 except:
103 return None
104
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)
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
139 imgCount = len(pixelIds)
140
141 rowCount = (imgCount/colCount)
142 while (colCount * rowCount) < imgCount:
143 rowCount += 1
144
145 leftSpace = spacing
146
147 textHeight = 0
148 if leftLabel:
149
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
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
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
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]
186 if thumbnail:
187 thumbImage = Image.open(StringIO.StringIO(thumbnail))
188
189 x = c*(length+spacing) + leftSpace
190 y = r*(length+spacing) + spacing
191 pasteImage(thumbImage, canvas, x, y)
192
193
194 c = c + 1
195 if c == colCount:
196 c = 0
197 r = r + 1
198
199 return canvas
200
201
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
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
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
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
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
276 zoomW = float(imageW) / float(maxW)
277 zoomH = float(imageH) / float(maxH)
278 zoom = max(zoomW, zoomH)
279 if zoomW >= zoomH:
280 maxH = int(imageH//zoom)
281 else:
282 maxW = int(imageW//zoom)
283 return image.resize((maxW, maxH))
284