1 from django.http import HttpResponseRedirect, HttpResponse
2 from django.core.urlresolvers import reverse
3 from django.shortcuts import render_to_response
4 from omeroweb.webgateway.views import getBlitzConnection, _session_logout
5 from omeroweb.webgateway import views as webgateway_views
6 from omeroweb.webclient.views import isUserConnected
7
8 from webtest_utils import getSpimData
9 from cStringIO import StringIO
10
11 import settings
12 import logging
13 import traceback
14 import omero
15 from omero.rtypes import rint, rstring
16 import omero.gateway
17
18 logger = logging.getLogger('webtest')
19
20
21 try:
22 import Image
23 except:
24 try:
25 from PIL import Image
26 except:
27 logger.error('No PIL installed, line plots and split channel will fail!')
28
29
30 @isUserConnected
31 -def dataset(request, datasetId, **kwargs):
32 """ 'Hello World' example from tutorial on http://trac.openmicroscopy.org.uk/ome/wiki/OmeroWeb """
33 conn = kwargs['conn']
34 ds = conn.getObject("Dataset", datasetId)
35 return render_to_response('webtest/dataset.html', {'dataset': ds})
36
58
73
74
75 @isUserConnected
76 -def index(request, **kwargs):
77 conn = kwargs['conn']
78 return render_to_response('webtest/index.html', {'conn': conn})
79
83 """
84 Viewer for overlaying separate channels from the same image or different images
85 and adjusting horizontal and vertical alignment of each
86 """
87 conn = kwargs['conn']
88
89 image = conn.getObject("Image", imageId)
90 default_z = image.getSizeZ()/2
91
92
93 red = None
94 green = None
95 blue = None
96 notAssigned = []
97 channels = []
98 for i, c in enumerate(image.getChannels()):
99 channels.append( {'name':c.getName()} )
100 if c.getColor().getRGB() == (255, 0, 0) and red == None:
101 red = i
102 elif c.getColor().getRGB() == (0, 255, 0) and green == None:
103 green = i
104 elif c.getColor().getRGB() == (0, 0, 255) and blue == None:
105 blue = i
106 else:
107 notAssigned.append(i)
108
109 for i in notAssigned:
110 if red == None: red = i
111 elif green == None: green = i
112 elif blue == None: blue = i
113
114
115
116 ns = "omero.web.channel_overlay.offsets"
117 comment = image.getAnnotation(ns)
118 if comment == None:
119 for ann in image.listAnnotations():
120 if isinstance(ann, omero.gateway.CommentAnnotationWrapper):
121 if ann.getValue().startswith("0|z:"):
122 comment = ann
123 break
124 if comment != None:
125 offsets = comment.getValue()
126 for o in offsets.split(","):
127 index,zxy = o.split("|",1)
128 if int(index) < len(channels):
129 keyVals = zxy.split("_")
130 for kv in keyVals:
131 key, val = kv.split(":")
132 if key == "z": val = int(val) + default_z
133 channels[int(index)][key] = int(val)
134
135 return render_to_response('webtest/demo_viewers/channel_overlay_viewer.html', {
136 'image': image, 'channels':channels, 'default_z':default_z, 'red': red, 'green': green, 'blue': blue})
137
141 """
142 Overlays separate channels (red, green, blue) from the same image or different images
143 manipulating each indepdently (translate, scale, rotate etc? )
144 """
145 conn = kwargs['conn']
146
147
148
149
150 planes = {}
151 p = request.REQUEST.get('planes', None)
152 for plane in p.split(','):
153 infoMap = {}
154 plane_info = plane.split('|')
155 key = plane_info[0].strip()
156 info = plane_info[1].strip()
157 shift = None
158 if info.find('$')>=0:
159 info,shift = info.split('$')
160 imageId,z,c,t = [int(i) for i in info.split(':')]
161 infoMap['imageId'] = imageId
162 infoMap['z'] = z
163 infoMap['c'] = c
164 infoMap['t'] = t
165 if shift != None:
166 for kv in shift.split("_"):
167 k, v = kv.split(":")
168 infoMap[k] = v
169 planes[key] = infoMap
170
171
172
173 red = request.REQUEST.get('red', None)
174 green = request.REQUEST.get('green', None)
175 blue = request.REQUEST.get('blue', None)
176
177
178
179 redImg = None
180
181 def translate(image, deltaX, deltaY):
182
183 xsize, ysize = image.size
184 mode = image.mode
185 bg = Image.new(mode, image.size)
186 x = abs(min(deltaX, 0))
187 pasteX = max(0, deltaX)
188 y = abs(min(deltaY, 0))
189 pasteY = max(0, deltaY)
190
191 part = image.crop((x, y, xsize-deltaX, ysize-deltaY))
192 bg.paste(part, (pasteX, pasteY))
193 return bg
194
195 def getPlane(planeInfo):
196 """ Returns the rendered plane split into a single channel (ready for merging) """
197 img = conn.getObject("Image", planeInfo['imageId'])
198 img.setActiveChannels((planeInfo['c']+1,))
199 img.setGreyscaleRenderingModel()
200 rgb = img.renderImage(planeInfo['z'], planeInfo['t'])
201 r,g,b = rgb.split()
202
203 x,y = 0,0
204 if 'x' in planeInfo:
205 x = int(planeInfo['x'])
206 if 'y' in planeInfo:
207 y = int(planeInfo['y'])
208
209 if x or y:
210 r = translate(r, x, y)
211 return r
212
213 redChannel = None
214 greenChannel = None
215 blueChannel = None
216 if red != None and red in planes:
217 redChannel = getPlane(planes[red])
218 if green != None and green in planes:
219 greenChannel = getPlane(planes[green])
220 if blue != None and blue in planes:
221 blueChannel = getPlane(planes[blue])
222
223 if redChannel != None:
224 size = redChannel.size
225 elif greenChannel != None:
226 size = greenChannel.size
227 elif blueChannel != None:
228 size = blueChannel.size
229
230 black = Image.new('L', size)
231 redChannel = redChannel and redChannel or black
232 greenChannel = greenChannel and greenChannel or black
233 blueChannel = blueChannel and blueChannel or black
234
235 merge = Image.merge("RGB", (redChannel, greenChannel, blueChannel))
236
237 rv = StringIO()
238 compression = 0.9
239 merge.save(rv, 'jpeg', quality=int(compression*100))
240 jpeg_data = rv.getvalue()
241
242 rsp = HttpResponse(jpeg_data, mimetype='image/jpeg')
243 return rsp
244
363
364 @isUserConnected
365 -def roi_viewer(request, roi_library, imageId, **kwargs):
366 """
367 Displays an image, using 'jquery.drawinglibrary.js' to draw ROIs on the image.
368 """
369 conn = kwargs['conn']
370
371 image = conn.getObject("Image", imageId)
372 default_z = image.getSizeZ()/2
373
374 templates = {"processing":'webtest/roi_viewers/processing_viewer.html',
375 "jquery": "webtest/roi_viewers/jquery_drawing.html",
376 "raphael":"webtest/roi_viewers/raphael_viewer.html"}
377
378 template = templates[roi_library]
379
380 return render_to_response(template, {'image':image, 'default_z':default_z})
381
385 """
386 Creates a L{omero.gateway.CommentAnnotationWrapper} and adds it to the images according
387 to variables in the http request.
388
389 @param request: The django L{django.core.handlers.wsgi.WSGIRequest}
390 - imageIds: A comma-delimited list of image IDs
391 - comment: The text to add as a comment to the images
392 - ns: Namespace for the annotation
393 - replace: If "true", try to replace existing annotation with same ns
394
395 @return: A simple html page with a success message
396 """
397
398 conn = kwargs['conn']
399
400 idList = request.REQUEST.get('imageIds', None)
401 if idList:
402 imageIds = [long(i) for i in idList.split(",")]
403 else: imageIds = []
404
405 comment = request.REQUEST.get('comment', None)
406 ns = request.REQUEST.get('ns', None)
407 replace = request.REQUEST.get('replace', False) in ('true', 'True')
408
409 updateService = conn.getUpdateService()
410 ann = omero.model.CommentAnnotationI()
411 ann.setTextValue(rstring( str(comment) ))
412 if ns != None:
413 ann.setNs(rstring( str(ns) ))
414 ann = updateService.saveAndReturnObject(ann)
415 annId = ann.getId().getValue()
416
417 images = []
418 for iId in imageIds:
419 image = conn.getObject("Image", iId)
420 if image == None: continue
421 if replace and ns != None:
422 oldComment = image.getAnnotation(ns)
423 if oldComment != None:
424 oldComment.setTextValue(rstring( str(comment) ))
425 updateService.saveObject(oldComment)
426 continue
427 l = omero.model.ImageAnnotationLinkI()
428 parent = omero.model.ImageI(iId, False)
429 l.setParent(parent)
430 l.setChild(ann)
431 updateService.saveObject(l)
432 images.append(image)
433
434 return render_to_response('webtest/util/add_annotations.html', {'images':images, 'comment':comment})
435
497
498 channels = None
499 images = []
500 for iId in imageIds:
501 image = conn.getObject("Image", iId)
502 if image == None: continue
503 default_z = image.getSizeZ()/2
504
505 images.append({"id":iId, "z":default_z, "name": image.getName() })
506 if channels == None:
507 channels = getChannelData(image)
508 if height == 0:
509 height = image.getSizeY()
510 if width == 0:
511 width = image.getSizeX()
512
513 size = {"height": height, "width": width}
514 c_strs = []
515 if channels:
516 indexes = range(1, len(channels)+1)
517 c_string = ",".join(["-%s" % str(c) for c in indexes])
518 mergedFlags = []
519 for i, c, in enumerate(channels):
520 if c["render_all"]:
521 levels = "%s:%s" % (c["start"], c["end"])
522 else: levels = ""
523 if c["active"]:
524 onFlag = str(i+1) + "|"
525 onFlag += levels
526 if split_grey: onFlag += "$FFFFFF"
527 c_strs.append( c_string.replace("-%s" % str(i+1), onFlag) )
528 if c["merged"]:
529 mergedFlags.append("%s|%s" % (i+1, levels))
530 else: mergedFlags.append("-%s" % (i+1))
531
532 c_strs.append( ",".join(mergedFlags) )
533
534 return render_to_response('webtest/demo_viewers/split_view_figure.html', {'images':images, 'c_strs': c_strs,'imageIds':idList,
535 'channels': channels, 'split_grey':split_grey, 'merged_names': merged_names, 'proj': proj, 'size': size, 'query_string':query_string})
536
540 """
541 Generates a web page that displays a dataset in two panels, with the option to choose different
542 rendering settings (channels on/off) for each panel. It uses the render_image url for each
543 image, generating the full sized image which is scaled down to view.
544
545 The page also includes a form for editing the channel settings and display size of images.
546 This form resubmits to this page and displays the page again with updated parameters.
547
548 @param request: The django L{http request <django.core.handlers.wsgi.WSGIRequest>}
549 @param datasetId: The ID of the dataset.
550 @type datasetId: Number.
551
552 @return: The http response - html page displaying split view figure.
553 """
554
555 conn = kwargs['conn']
556
557 dataset = conn.getObject("Dataset", datasetId)
558
559 try:
560 w = request.REQUEST.get('width', 100)
561 width = int(w)
562 except:
563 width = 100
564 try:
565 h = request.REQUEST.get('height', 100)
566 height = int(h)
567 except:
568 height = 100
569
570
571 def getChannelData(image):
572 channels = []
573 i = 0;
574 for i, c in enumerate(image.getChannels()):
575 name = c.getLogicalChannel().getName()
576
577 if request.REQUEST.get('cStart%s' % i, None):
578 active_left = (None != request.REQUEST.get('cActiveLeft%s' % i, None) )
579 active_right = (None != request.REQUEST.get('cActiveRight%s' % i, None) )
580 else:
581 active_left = True
582 active_right = True
583 colour = c.getColor().getHtml()
584 start = request.REQUEST.get('cStart%s' % i, c.getWindowStart())
585 end = request.REQUEST.get('cEnd%s' % i, c.getWindowEnd())
586 render_all = (None != request.REQUEST.get('cRenderAll%s' % i, None) )
587 channels.append({"name": name, "index": i, "active_left": active_left, "active_right": active_right,
588 "colour": colour, "start": start, "end": end, "render_all": render_all})
589 return channels
590
591 images = []
592 channels = None
593
594 for image in dataset.listChildren():
595 if channels == None:
596 channels = getChannelData(image)
597 default_z = image.getSizeZ()/2
598
599 images.append({"id":image.getId(), "z":default_z, "name": image.getName() })
600
601 size = {'width':width, 'height':height}
602
603 indexes = range(1, len(channels)+1)
604 c_string = ",".join(["-%s" % str(c) for c in indexes])
605
606 leftFlags = []
607 rightFlags = []
608 for i, c, in enumerate(channels):
609 if c["render_all"]:
610 levels = "%s:%s" % (c["start"], c["end"])
611 else: levels = ""
612 if c["active_left"]:
613 leftFlags.append("%s|%s" % (i+1, levels))
614 else: leftFlags.append("-%s" % (i+1))
615 if c["active_right"]:
616 rightFlags.append("%s|%s" % (i+1, levels))
617 else: rightFlags.append("-%s" % (i+1))
618
619 c_left = ",".join(leftFlags)
620 c_right = ",".join(rightFlags)
621
622 return render_to_response('webtest/demo_viewers/dataset_split_view.html', {'dataset': dataset, 'images': images,
623 'channels':channels, 'size': size, 'c_left': c_left, 'c_right': c_right})
624
628 """
629 Prepare data to display various dimensions of a multi-dim image as axes of a grid of image planes.
630 E.g. x-axis = Time, y-axis = Channel.
631 If the image has spim data, then combine images with different SPIM angles to provide an additional
632 dimension. Also get the SPIM data from various XML annotations and display on page.
633 """
634
635 conn = kwargs['conn']
636
637 image = conn.getObject("Image", imageId)
638 if image is None:
639 return render_to_response('webtest/demo_viewers/image_dimensions.html', {})
640
641 mode = request.REQUEST.get('mode', None) and 'g' or 'c'
642 dims = {'Z':image.getSizeZ(), 'C': image.getSizeC(), 'T': image.getSizeT()}
643
644 default_yDim = 'Z'
645
646 spim_data = getSpimData(conn, image)
647 if spim_data is not None:
648 dims['Angle'] = len(spim_data['images'])
649 default_yDim = 'Angle'
650
651 xDim = request.REQUEST.get('xDim', 'T')
652 if xDim not in dims.keys():
653 xDim = 'T'
654
655 yDim = request.REQUEST.get('yDim', default_yDim)
656 if yDim not in dims.keys():
657 yDim = 'Z'
658
659 xFrames = int(request.REQUEST.get('xFrames', 5))
660 xSize = dims[xDim]
661 yFrames = int(request.REQUEST.get('yFrames', 5))
662 ySize = dims[yDim]
663
664 xFrames = min(xFrames, xSize)
665 yFrames = min(yFrames, ySize)
666
667 xRange = range(xFrames)
668 yRange = range(yFrames)
669
670
671 grid = []
672 for y in yRange:
673 grid.append([])
674 for x in xRange:
675 iid, theZ, theC, theT = image.id, 0,None,0
676 if xDim == 'Z':
677 theZ = x
678 if xDim == 'C':
679 theC = x
680 if xDim == 'T':
681 theT = x
682 if xDim == 'Angle':
683 iid = spim_data['images'][x].id
684 if yDim == 'Z':
685 theZ = y
686 if yDim == 'C':
687 theC = y
688 if yDim == 'T':
689 theT = y
690 if yDim == 'Angle':
691 iid = spim_data['images'][y].id
692
693 grid[y].append( (iid, theZ, theC is not None and theC+1 or None, theT) )
694
695
696 size = {"height": 125, "width": 125}
697
698 return render_to_response('webtest/demo_viewers/image_dimensions.html', {'image':image, 'spim_data':spim_data, 'grid': grid,
699 "size": size, "mode":mode, 'xDim':xDim, 'xRange':xRange, 'yRange':yRange, 'yDim':yDim,
700 'xFrames':xFrames, 'yFrames':yFrames})
701
702
703 @isUserConnected
704 -def image_viewer (request, iid, **kwargs):
705 """ This view is responsible for showing pixel data as images """
706
707 conn = kwargs['conn']
708
709 kwargs['viewport_server'] = '/webclient'
710
711 return webgateway_views.full_viewer(request, iid, _conn=conn, **kwargs)
712