From 87bd4dbfd165277b9197af9a19a0e2ed27204f62 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 22 Jan 2016 11:48:16 +0000 Subject: [PATCH 1/7] Add render_shape_mask to webgateway --- .../OmeroWeb/omeroweb/webgateway/urls.py | 7 +++ .../OmeroWeb/omeroweb/webgateway/views.py | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/urls.py b/components/tools/OmeroWeb/omeroweb/webgateway/urls.py index 367699cf885..531204c8c23 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/urls.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/urls.py @@ -146,6 +146,12 @@ L{views.render_shape_thumbnail}. Uses current rendering settings. """ +render_shape_mask = (r'^render_shape_mask/(?P[1-9]+)/$', + 'webgateway.views.render_shape_mask') +""" +Returns a mask for the specified shape +""" + render_birds_eye_view = ( r'^render_birds_eye_view/(?P[^/]+)/(?:(?P[^/]+)/)?$', 'webgateway.views.render_birds_eye_view') @@ -409,6 +415,7 @@ render_col_plot, render_roi_thumbnail, render_shape_thumbnail, + render_shape_mask, render_thumbnail, render_birds_eye_view, render_ome_tiff, diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py index d28765fe74c..5651797d131 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py @@ -78,6 +78,13 @@ except: logger.error('No Pillow installed') +try: + import numpy + numpyInstalled = True +except ImportError: + logger.error('No numpy installed') + numpyInstalled = False + def index(request): """ /webgateway/ index placeholder """ @@ -648,6 +655,55 @@ def resizeXY(xy): return HttpResponse(jpeg, content_type='image/jpeg') +@login_required() +def render_shape_mask(request, shapeId, conn=None, **kwargs): + """ Returns mask as a png (supports transparency) """ + + if not numpyInstalled: + raise NotImplementedError("numpy not installed") + params = omero.sys.Parameters() + params.map = {'id': rlong(shapeId)} + shape = conn.getQueryService().findByQuery( + "select s from Shape s where s.id = :id", params, + conn.SERVICE_OPTS) + width = int(shape.getWidth().getValue()) + height = int(shape.getHeight().getValue()) + mask_packed = shape.getBytes() + # convert bytearray into something we can use + intarray = numpy.fromstring(mask_packed, dtype=numpy.uint8) + binarray = numpy.unpackbits(intarray) + + # Couldn't get the 'proper' way of doing this to work, + # Maybe look at this again later? + # pixels = "" + # steps = len(binarray) / 8 + # for i in range(steps): + # b = binarray[i*8: (i+1)*8] + # pixels += struct.pack("8B", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]) + # for b in binarray: + # pixels += struct.pack("1B", b) + # print 'pixels', repr(pixels) + # im = Image.frombytes("1", size=(4,2), data=pixels) + + # Simple approach - Just set each pixel in turn + img = Image.new("RGBA", size=(width, height), color=(0, 0, 0, 0)) + x = 0 + y = 0 + fill = (255, 0, 0, 255) + for pix in binarray: + if pix == 1: + img.putpixel((x, y), fill) + x += 1 + if x > width - 1: + x = 0 + y += 1 + rv = StringIO() + # return a png (supports transparency) + img.save(rv, 'png', quality=int(100)) + png = rv.getvalue() + return HttpResponse(png, content_type='image/png') + + def _get_signature_from_request(request): """ returns a string that identifies this image, along with the settings From 3786957f8bf7a73ab8d39357396ba8f257534c19 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 22 Jan 2016 13:00:11 +0000 Subject: [PATCH 2/7] Show mask in image viewer --- .../webgateway/static/webgateway/js/ome.roidisplay.js | 5 +++++ .../templates/webgateway/viewport/omero_image.html | 1 + components/tools/OmeroWeb/omeroweb/webgateway/views.py | 2 ++ 3 files changed, 8 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/js/ome.roidisplay.js b/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/js/ome.roidisplay.js index 8aad5d4172e..96ca628fd57 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/js/ome.roidisplay.js +++ b/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/js/ome.roidisplay.js @@ -31,6 +31,7 @@ $.fn.roi_display = function(options) { if (options != null) { var orig_width = options.width; var orig_height = options.height; + var webgateway_index = options.webgateway_index; // base url var json_url = options.json_url; } @@ -102,6 +103,10 @@ $.fn.roi_display = function(options) { var draw_shape = function(shape) { var newShape = null; + if (shape['type'] == 'Mask') { + var src = webgateway_index + 'render_shape_mask/' + shape['id'] + '/'; + newShape = paper.image(src, shape['x'], shape['y'], shape['width'], shape['height']); + } if (shape['type'] == 'Ellipse') { newShape = paper.ellipse(shape['cx'], shape['cy'], shape['rx'], shape['ry']); } diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html b/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html index 512749279f2..19959a4da38 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html +++ b/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html @@ -362,6 +362,7 @@ if (!viewport.viewportimg.get(0).show_rois) { var options = {'width':{{ image.getSizeX }}, 'height':{{ image.getSizeY }}, + 'webgateway_index':'{% url "webgateway" %}', 'json_url':'{% url 'webgateway_get_rois_json' image.id %}'}; if (viewport.loadedImg.tiles) { options['tiles'] = true; diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py index 5651797d131..28e8df70aae 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py @@ -666,6 +666,8 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): shape = conn.getQueryService().findByQuery( "select s from Shape s where s.id = :id", params, conn.SERVICE_OPTS) + if shape is None: + raise Http404("Shape ID: %s not found" % shapeId) width = int(shape.getWidth().getValue()) height = int(shape.getHeight().getValue()) mask_packed = shape.getBytes() From df697e12325cfaa87b49790d1c28b280fb6e4e62 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 25 Jan 2016 13:45:57 +0000 Subject: [PATCH 3/7] Fix render_shape_mask urls regex --- components/tools/OmeroWeb/omeroweb/webgateway/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/urls.py b/components/tools/OmeroWeb/omeroweb/webgateway/urls.py index 531204c8c23..4ff6c414814 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/urls.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/urls.py @@ -146,7 +146,7 @@ L{views.render_shape_thumbnail}. Uses current rendering settings. """ -render_shape_mask = (r'^render_shape_mask/(?P[1-9]+)/$', +render_shape_mask = (r'^render_shape_mask/(?P[0-9]+)/$', 'webgateway.views.render_shape_mask') """ Returns a mask for the specified shape From 6dedd8da35f178c5b9f3ef9b9be83a9d3fed64df Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 25 Jan 2016 14:36:54 +0000 Subject: [PATCH 4/7] flake8 fix --- components/tools/OmeroWeb/omeroweb/webgateway/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py index 28e8df70aae..6b16629cc8f 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py @@ -681,7 +681,8 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): # steps = len(binarray) / 8 # for i in range(steps): # b = binarray[i*8: (i+1)*8] - # pixels += struct.pack("8B", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]) + # pixels += struct.pack("8B", b[0], b[1], b[2], b[3], b[4], + # b[5], b[6], b[7]) # for b in binarray: # pixels += struct.pack("1B", b) # print 'pixels', repr(pixels) From e06a081245996e5cb41f42cd51630a48552b9b8d Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 28 Jan 2016 11:02:01 +0000 Subject: [PATCH 5/7] Handle mask fillColor --- .../tools/OmeroWeb/omeroweb/webgateway/marshal.py | 14 +++++++++++++- .../tools/OmeroWeb/omeroweb/webgateway/views.py | 8 ++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/marshal.py b/components/tools/OmeroWeb/omeroweb/webgateway/marshal.py index 2effa680dd8..fbb094acca8 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/marshal.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/marshal.py @@ -318,7 +318,8 @@ def stringToSvg(string): def rgb_int2css(rgbint): """ - converts a bin int number into css colour, E.g. -1006567680 to '#00ff00' + converts a bin int number into css colour and alpha fraction. + E.g. -1006567680 to '#00ff00', 0.5 """ alpha = rgbint // 256 // 256 // 256 % 256 alpha = float(alpha) / 256 @@ -326,6 +327,17 @@ def rgb_int2css(rgbint): return "#%02x%02x%02x" % (r, g, b), alpha +def rgb_int2rgba(rgbint): + """ + converts a bin int number into (r, g, b, alpha) tuple. + E.g. 1694433280 to (255, 0, 0, 0.390625) + """ + alpha = rgbint // 256 // 256 // 256 % 256 + alpha = float(alpha) / 256 + r, g, b = (rgbint // 256 // 256 % 256, rgbint // 256 % 256, rgbint % 256) + return (r, g, b, alpha) + + def chgrpMarshal(conn, rsp): """ Helper for marshalling a Chgrp response. diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py index 6b16629cc8f..b272f156327 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py @@ -31,7 +31,7 @@ from omero.util.ROI_utils import pointsStringToXYlist, xyListToBbox from plategrid import PlateGrid from omero_version import build_year -from marshal import imageMarshal, shapeMarshal +from marshal import imageMarshal, shapeMarshal, rgb_int2rgba try: from hashlib import md5 @@ -670,6 +670,11 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): raise Http404("Shape ID: %s not found" % shapeId) width = int(shape.getWidth().getValue()) height = int(shape.getHeight().getValue()) + color = unwrap(shape.getFillColor()) + fill = (255, 255, 0, 255) + if color is not None: + color = rgb_int2rgba(color) + fill = (color[0], color[1], color[2], int(color[3] * 255)) mask_packed = shape.getBytes() # convert bytearray into something we can use intarray = numpy.fromstring(mask_packed, dtype=numpy.uint8) @@ -692,7 +697,6 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): img = Image.new("RGBA", size=(width, height), color=(0, 0, 0, 0)) x = 0 y = 0 - fill = (255, 0, 0, 255) for pix in binarray: if pix == 1: img.putpixel((x, y), fill) From 258115be0c05b0add08ca0709c182c780a5ac879 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 28 Jan 2016 11:17:58 +0000 Subject: [PATCH 6/7] Show mask icon in ROI table --- .../webgateway/static/webgateway/img/mask16.png | Bin 0 -> 2875 bytes .../webgateway/viewport/omero_image.html | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/img/mask16.png diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/img/mask16.png b/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/img/mask16.png new file mode 100644 index 0000000000000000000000000000000000000000..b057c2510c88c1a0865ea1356335e0162df49713 GIT binary patch literal 2875 zcmV-B3&iw^P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001HNklV1002ovPDHLkV1gOMSb+cl literal 0 HcmV?d00001 diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html b/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html index 19959a4da38..5e742abc97f 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html +++ b/components/tools/OmeroWeb/omeroweb/webgateway/templates/webgateway/viewport/omero_image.html @@ -451,7 +451,8 @@ $roi_table.append($(roi_html)); get_shape_icon_src = function(type, klass) { - var shape_icons = {'Line':'line16.png', 'Ellipse':'ellipse16.png', 'Polygon':'polygon16.png', 'Rectangle':'rectangle16.png', + var shape_icons = {'Line':'line16.png', 'Ellipse':'ellipse16.png', + 'Polygon':'polygon16.png', 'Rectangle':'rectangle16.png', 'Mask': 'mask16.png', 'Point':'point16.png', 'Label':'text16.png', 'PolyLine':'line16.png'} if (type in shape_icons) { return ""; From 2c01a57a0bce8fbff3a418788726acd501e5bea0 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 24 Feb 2016 11:24:13 +0000 Subject: [PATCH 7/7] Add 'TODO' with more info for mask creation --- components/tools/OmeroWeb/omeroweb/webgateway/views.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py index b272f156327..46c867d5723 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py +++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py @@ -681,7 +681,8 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): binarray = numpy.unpackbits(intarray) # Couldn't get the 'proper' way of doing this to work, - # Maybe look at this again later? + # TODO: look at this again later. Faster than simple way below: + # E.g. takes ~2 seconds for 1984 x 1984 mask # pixels = "" # steps = len(binarray) / 8 # for i in range(steps): @@ -690,10 +691,11 @@ def render_shape_mask(request, shapeId, conn=None, **kwargs): # b[5], b[6], b[7]) # for b in binarray: # pixels += struct.pack("1B", b) - # print 'pixels', repr(pixels) - # im = Image.frombytes("1", size=(4,2), data=pixels) + # im = Image.frombytes("1", size=(width, height), data=pixels) # Simple approach - Just set each pixel in turn + # E.g. takes ~12 seconds for 1984 x 1984 mask with most pixels '1' + # Or ~5 seconds for same size mask with most pixels "0" img = Image.new("RGBA", size=(width, height), color=(0, 0, 0, 0)) x = 0 y = 0