1
2
3 """
4 components/tools/OmeroPy/src/omero/util/figureUitl.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 used by Figure scripts for producing
26 publication type of 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
31 <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
32 @author Donald MacDonald
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 try:
43 from PIL import Image, ImageDraw, ImageFont
44 except ImportError:
45 import Image, ImageDraw, ImageFont
46
47 WHITE = (255,255,255)
48
49 SECS_MILLIS = "SECS_MILLIS"
50 SECS = "SECS"
51 MINS = "MINS"
52 HOURS = "HOURS"
53 MINS_SECS = "MINS_SECS"
54 HOURS_MINS = "HOURS_MINS"
55 HOURS_MINS_SECS = "HOURS_MINS_SECS"
56 HOURS_MINS_SECS_MILLIS = "HOURS_MINS_SECS_MILLIS"
57 TIME_UNITS = [SECS_MILLIS, SECS, MINS, HOURS, MINS_SECS, HOURS_MINS, HOURS_MINS_SECS, HOURS_MINS_SECS_MILLIS]
58
60 """
61 Query returns a map where each key is the imageId and the value is a list of (projectName, datasetName) tuples.
62 If the image does not have a Dataset AND Project, the map will hold an empty list for that imageId.
63
64 @param queryService: The Omero query service
65 @param imageIds: A list of image IDs. [long]
66 @return: A map imageId:[(projectName, datasetName)]
67 """
68 ids = ",".join([str(i) for i in imageIds])
69
70 query_string = "select i from Image i join fetch i.datasetLinks idl join fetch idl.parent d join fetch d.projectLinks pl join fetch pl.parent where i.id in (%s)" % ids
71
72 images = queryService.findAllByQuery(query_string, None)
73 results = {}
74
75 for i in images:
76 pdList = []
77 imageId = i.getId().getValue()
78 for link in i.iterateDatasetLinks():
79 dataset = link.parent
80 dName = dataset.getName().getValue()
81 if dataset.sizeOfProjectLinks() == 0:
82 pdList.append(("", dName))
83 for dpLink in dataset.iterateProjectLinks():
84 project = dpLink.parent
85 pName = project.getName().getValue()
86 pdList.append((pName, dName))
87 results[imageId] = pdList
88
89
90 for iId in imageIds:
91 if iId not in results:
92 results[iId] = []
93 return results
94
95
114
115
116 -def getTimes(queryService, pixelsId, tIndexes, theZ=None, theC=None):
117 """
118 Get the time in seconds (float) for the first plane (C = 0 & Z = 0) at
119 each time-point for the defined pixels.
120 Returns a map of tIndex: timeInSecs
121
122 @param queryService: The Omero queryService
123 @param pixelsId: The ID of the pixels object. long
124 @param tIndexes: List of time indexes. [int]
125 @param theZ: The Z plane index. Default is 0
126 @param theC: The Channel index. Default is 0
127 @return: A map of tIndex: timeInSecs
128 """
129 if theZ == None:
130 theZ = 0
131 if theC == None:
132 theC = 0
133 indexes = ",".join([str(t) for t in tIndexes])
134 query = "from PlaneInfo as Info where Info.theT in (%s) and Info.theZ in (%d) and Info.theC in (%d) and pixels.id='%d'" % (indexes, theZ, theC, pixelsId)
135 infoList = queryService.findAllByQuery(query,None)
136 timeMap = {}
137 for info in infoList:
138 tIndex = info.theT.getValue()
139 time = info.deltaT.getValue()
140 timeMap[tIndex] = time
141 return timeMap
142
143
189
190
191 -def getTimeLabels(queryService, pixelsId, tIndexes, sizeT, timeUnits = None, showRoiDuration = False):
192 """
193 Returns a list of time labels e.g. "10", "20" for the first plane at
194 each t-index (C=0 and Z=0). If no planeInfo is available, returns plane number/total e.g "3/10"
195 If time units are not specified, the most suitable units are chosen based on the max time.
196 The list of label returned includes the timeUnits as the last string in the list, in case you didn't specify it.
197
198 @param queryService: The Omero query service
199 @param pixelsId: The ID of the pixels you want info for
200 @param tIndexes: List of t-index to get the times for. Assumed to be in t order.
201 @param sizeT: The T dimension size of the pixels. Used if no plane info
202 @param timeUnits: Format choice of "SECS", "MINS", "HOURS", "MINS_SECS", "HOURS_MINS". String
203 @param showRoiDuration: if true, times shown are from the start of the ROI frames, otherwise use movie timestamp.
204 @return: A list of strings, ordered same as tIndexes
205 """
206 secondsMap = getTimes(queryService, pixelsId, tIndexes)
207
208 if timeUnits == None and len(secondsMap) > 0:
209 maxSecs = max(secondsMap.values())
210 if maxSecs > 3600: timeUnits = HOURS_MINS
211 elif maxSecs > 60: timeUnits = MINS_SECS
212 else: timeUnits = SECS_MILLIS
213
214 labels = []
215 for t in tIndexes:
216 if t in secondsMap:
217 seconds = secondsMap[t]
218 if showRoiDuration:
219 seconds = seconds - secondsMap[tIndexes[0]]
220 labels.append(formatTime(seconds,timeUnits))
221 else:
222 labels.append("%d/%d" % (t+1, sizeT))
223
224 labels.append(timeUnits)
225 return labels
226
227 -def addScalebar(scalebar, xIndent, yIndent, image, pixels, colour):
228 """
229 Adds a scalebar at the bottom right of an image, No text.
230
231 @param scalebar length of scalebar in microns
232 @param xIndent indent from the right of the image
233 @param yIndent indent from the bottom of the image
234 @param image the PIL image to add scalebar to
235 @param pixels the pixels object
236 @param colour colour of the overlay as r,g,b tuple
237 """
238 draw = ImageDraw.Draw(image)
239 if pixels.getPhysicalSizeX() == None:
240 return False, " Failed to add scale bar: Pixel size not defined."
241 pixelSizeX = pixels.getPhysicalSizeX().getValue()
242 if pixelSizeX <= 0:
243 return False, " Failed to add scale bar: Pixel size not defined."
244 iWidth, iHeight = image.size
245 lineThickness = (iHeight//100) + 1
246 scaleBarY = iHeight - yIndent
247 scaleBarX = iWidth - scalebar//pixelSizeX - xIndent
248 scaleBarX2 = iWidth - xIndent
249 if scaleBarX<=0 or scaleBarX2<=0 or scaleBarY<=0 or scaleBarX2>iWidth:
250 return False, " Failed to add scale bar: Scale bar is too large."
251 for l in range(lineThickness):
252 draw.line([(scaleBarX,scaleBarY), (scaleBarX2,scaleBarY)], fill=colour)
253 scaleBarY -= 1
254 return True, " Scalebar added to the image."
255
257 """ Returns an image with the labels written vertically with the given font, black on white background """
258
259 maxWidth = 0
260 height = 0
261 textHeight = font.getsize("testq")[1]
262 for label in labels:
263 maxWidth = max(maxWidth, font.getsize(label)[0])
264 if height > 0: height += textGap
265 height += textHeight
266 size = (maxWidth, height)
267 textCanvas = Image.new("RGB", size, WHITE)
268 textdraw = ImageDraw.Draw(textCanvas)
269 py = 0
270 for label in labels:
271 indent = (maxWidth - font.getsize(label)[0]) / 2
272 textdraw.text((indent, py), label, font=font, fill=(0,0,0))
273 py += textHeight + textGap
274 return textCanvas.rotate(90)
275