Skip to content

Commit

Permalink
Merge pull request #581 from Tom-TBT/lut_view
Browse files Browse the repository at this point in the history
Dynamic generation of the LUTs png
  • Loading branch information
knabar authored Nov 20, 2024
2 parents 2bd2a55 + eb48769 commit 4f05b5e
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@
// Load the Lookup Tables straight off - need them ASAP
if (!OME.LUTS) {
$.getJSON("{% url 'webgateway_listLuts_json' %}", function(data){
// Since 5.28.0, LUTs list and image is generated dynamically
OME.LUTS = data.luts;
OME.PNG_LUTS = data.png_luts;
OME.PNG_LUTS = data.png_luts_new;
});
}

Expand Down Expand Up @@ -390,7 +391,7 @@

<!-- VIEWER "Preview"-->
<div class="right_tab_inner">

<!-- open-image link -->

<div>
Expand All @@ -399,7 +400,7 @@
class="btn silver btn_text" alt="View"
title="Open full viewer" rel="{% content_identifier 'preview' manager.image.id %}"> <!-- rel is used by robot framework, do not remove it! -->
<span>
{% trans "Full viewer" %}
{% trans "Full viewer" %}
</span>
</a>
</div>
Expand Down Expand Up @@ -447,7 +448,7 @@
<img src="{% static "webclient/image/icon_redo16.png" %}" /><br>
Redo
</button></li>

<li class="seperator"></li>

<li><button id="rdef-copy-btn" class="button" title="Copy Rendering Settings">
Expand Down
27 changes: 13 additions & 14 deletions omeroweb/webgateway/static/webgateway/css/ome.viewport.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* weblitz_viewport - style definitions
*
*
* Copyright (c) 2007, 2008, 2009 Glencoe Software, Inc. All rights reserved.
*
*
* This software is distributed under the terms described by the LICENCE file
* you can find at the root of the distribution bundle, which states you are
* free to use it only for non commercial purposes.
Expand All @@ -26,8 +26,8 @@ div.weblitz-viewport-tiles img {
padding: 0;
position: absolute;
}*/


.weblitz-viewport-vp {
position: absolute;
display: block;
Expand All @@ -42,12 +42,12 @@ div.weblitz-viewport-tiles img {
left: 15px;
}

.weblitz-viewport-top {
.weblitz-viewport-top {
position: relative;
height: 100%;
}

.weblitz-viewport-bot {
.weblitz-viewport-bot {
position: relative;
left: 15px;
height: 15px;
Expand Down Expand Up @@ -86,11 +86,11 @@ div.weblitz-viewport-tiles img {

/* Customizing the jquery-plugin-viewportImage classes */

.image-viewer {
.image-viewer {
position: relative;
}

.vslider {
.vslider {
position: absolute;
width: 13px;
height: 100%;
Expand All @@ -99,24 +99,24 @@ div.weblitz-viewport-tiles img {
left: 1px;
}

.hslider {
.hslider {
position: relative;
height: 13px;
/*bottom: -3px;*/
display: block;
font-size: 0.1em;
}

.slider-line {
.slider-line {
/* width: 100%; */
background: #FFF;
font-size: 0.1em;
}

.slider-handle {
.slider-handle {
background: #184A89;
font-size: 0.1em;
left: -1px;
left: -1px;
top: -1px;
}

Expand Down Expand Up @@ -185,11 +185,10 @@ div.weblitz-viewport-tiles img {
}

.lutBackground {
background-image: url('../img/luts_10.png');
background-image: url('../../../webgateway/luts_png/');
background-repeat: no-repeat;
image-rendering: pixelated;
}
button>.lutBackground {
opacity: 0.3;
}

4 changes: 2 additions & 2 deletions omeroweb/webgateway/static/webgateway/js/ome.colorbtn.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Author: C. Neves <[email protected]>
*
* Copyright (c) 2007, 2008 Glencoe Software, Inc. All rights reserved.
*
*
* This software is distributed under the terms described by the LICENCE file
* you can find at the root of the distribution bundle, which states you are
* free to use it only for non commercial purposes.
Expand Down Expand Up @@ -104,7 +104,7 @@ $.fn.colorbtn = function(cfg) {
if (OME && OME.LUTS) {
for (var l=0; l<OME.LUTS.length; l++) {
if (OME.LUTS[l].name === lutName) {
return OME.LUTS[l].png_index;
return OME.LUTS[l].png_index_new;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion omeroweb/webgateway/static/webgateway/js/omero_image.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
if (OME && OME.LUTS) {
for (var l=0; l<OME.LUTS.length; l++) {
if (OME.LUTS[l].name === lutName) {
return OME.LUTS[l].png_index;
return OME.LUTS[l].png_index_new;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
<script type="text/javascript" src="{% static "3rdparty/jquery.selectboxes-2.2.6.js" %}"></script>
<script type="text/javascript" src="{% static "3rdparty/farbtastic-1.2/farbtastic.js" %}"></script>
<script type="text/javascript" src="{% static "webgateway/js/ome.gs_utils.js"|add:url_suffix %}"></script>

<script type="text/javascript" src="{% static "webgateway/js/ome.scalebardisplay.js"|add:url_suffix %}"></script>
<!-- for display of ROIs over the image -->
<script type="text/javascript" src="{% static "webgateway/js/ome.roidisplay.js"|add:url_suffix %}"></script>
Expand Down Expand Up @@ -116,7 +116,7 @@

<!-- hammer.js for tablet gestures -->
<script type="text/javascript" src="{% static "3rdparty/hammer-2.0.8/hammer.min.js" %}"></script>

{% endblock %}


Expand Down Expand Up @@ -346,16 +346,16 @@
}
viewport.viewportimg.scalebar_display(options);
}

viewport.viewportimg.get(0).setScalebarZoom(viewport.getZoom()/100 );
viewport.viewportimg.get(0).show_scalebar();
{% endif %}
}

var hide_scalebar = function () {
viewport.viewportimg.get(0).hide_scalebar();
}

/**
* ROI load & table methods
*/
Expand Down Expand Up @@ -1038,7 +1038,7 @@ <h1>ROI Count: {{ roiCount }}</h1>
// Load Lookup Tables - need them ASAP
$.getJSON("{% url 'webgateway_listLuts_json' %}", function(data){
OME.LUTS = data.luts;
OME.PNG_LUTS = data.png_luts;
OME.PNG_LUTS = data.png_luts_new;
});

/* Prepare the viewport */
Expand Down Expand Up @@ -1499,11 +1499,11 @@ <h1>ROI Count: {{ roiCount }}</h1>
nextPrevImage(1);
});
}

// we can bind events to viewportimg for roi_display-plugin to trigger (plugin not created yet)
viewport.viewportimg.on("shape_click", handle_shape_selection);
viewport.viewportimg.on("rois_loaded", handle_rois_loaded);

// 'Scalebar' checkbox to left of image
$("#wblitz-scalebar").on('change', function() {
if(this.checked) {
Expand All @@ -1512,7 +1512,7 @@ <h1>ROI Count: {{ roiCount }}</h1>
hide_scalebar();
}
});

// set up handlers for 'Save' button
$("#rdef-setdef-btn").on('click', function(){
setImageDefaults(viewport, this, function() {
Expand Down
6 changes: 6 additions & 0 deletions omeroweb/webgateway/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,11 @@
E.g. list of {path: "/luts/", size: 800, id: 37, name: "cool.lut"},
"""

luts_png = re_path(r"^luts_png/$", views.luts_png, name="webgateway_luts_png")
"""
returning a png of all LUTs on server sorted by name
"""

list_compatible_imgs_json = re_path(
r"^compatImgRDef/(?P<iid>[0-9]+)/$",
views.list_compatible_imgs_json,
Expand Down Expand Up @@ -666,6 +671,7 @@
get_image_rdef_json,
get_image_rdefs_json,
listLuts_json,
luts_png,
list_compatible_imgs_json,
copy_image_rdef_json,
reset_rdef_json,
Expand Down
104 changes: 96 additions & 8 deletions omeroweb/webgateway/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
HttpResponseNotFound,
)

from django.core.cache import cache
from django.views.decorators.http import require_POST
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.decorators import method_decorator
Expand Down Expand Up @@ -2072,29 +2073,116 @@ def save_image_rdef_json(request, iid, conn=None, **kwargs):
@jsonp
def listLuts_json(request, conn=None, **kwargs):
"""
Lists lookup tables 'LUTs' availble for rendering
Lists lookup tables 'LUTs' availble for rendering.
This list is dynamic and will change if users add LUTs to their server.
We include 'png_index' which is the index of each LUT within the
static/webgateway/img/luts_10.png or -1 if LUT is not found.
Since 5.28.0, the list of LUTs is also generated dynamically.
The new LUT indexes and LUT list were added to
the response with the suffix '_new' (png_index_new and png_luts_new)
The png matching the new indexes and list of LUT is obtained from
this url: /webgateway/luts_png/ (views.luts_png)
"""
scriptService = conn.getScriptService()
luts = scriptService.getScriptsByMimetype("text/x-lut")
rv = []
for lut in luts:
luts.sort(key=lambda x: x.name.val.lower())
rv, all_luts = [], []
for i, lut in enumerate(luts):
lutsrc = lut.path.val + lut.name.val
png_index = LUTS_IN_PNG.index(lutsrc) if lutsrc in LUTS_IN_PNG else -1
all_luts.append(lutsrc)
idx = LUTS_IN_PNG.index(lutsrc) if lutsrc in LUTS_IN_PNG else -1
rv.append(
{
"id": lut.id.val,
"path": lut.path.val,
"name": lut.name.val,
"size": unwrap(lut.size),
"png_index": png_index,
"png_index": idx,
"png_index_new": i,
}
)
rv.sort(key=lambda x: x["name"].lower())
return {"luts": rv, "png_luts": LUTS_IN_PNG}
all_luts.append("gradient.png")

return {"luts": rv, "png_luts": LUTS_IN_PNG, "png_luts_new": all_luts}


@login_required()
def luts_png(request, conn=None, **kwargs):
"""
Generates the LUT png used for preview and selection of LUT. The png is
256px wide, and each LUT is 10px in height. The last portion of the png
is the channel sliders transparent gradient.
LUTs are listed in alphabetical order (lut name only from filename).
LUT files on the server are read with the script service, and
file content is parsed with a custom implementation.
This uses caching to prevent generating the png each time a LUT
menu is opened. The cache key is a hash of all LUTs path.
Change in the LUT name or path will force the generation of a new
png.
"""
scriptService = conn.getScriptService()
luts = scriptService.getScriptsByMimetype("text/x-lut")
luts.sort(key=lambda x: x.name.val)
luts_path = []
for lut in luts:
luts_path.append(lut.path.val + lut.name.val)
luts_hash = hash("\n".join(luts_path))
cache_key = f"lut_hash_{luts_hash}"

cached_image = cache.get(cache_key)
if cached_image:
return HttpResponse(cached_image, content_type="image/png")

# Generate the LUT, fourth png channel set to 255
new_img = numpy.zeros((10 * (len(luts) + 1), 256, 4), dtype="uint8") + 255
for i, lut in enumerate(luts):
orig_file = conn.getObject("OriginalFile", lut.getId()._val)
lut_data = bytearray()
# Collect the LUT data in byte form
for chunk in orig_file.getFileInChunks():
lut_data.extend(chunk)

if len(lut_data) in [768, 800]:
lut_arr = numpy.array(lut_data, dtype="uint8")[-768:]
new_img[(i * 10) : (i + 1) * 10, :, :3] = lut_arr.reshape(3, 256).T
else:
lut_data = lut_data.decode()
r, g, b = [], [], []

lines = lut_data.split("\n")
sep = None
if "\t" in lines[0]:
sep = "\t"
for line in lines:
val = line.split(sep)
if len(val) < 3 or not val[-1].isnumeric():
continue
r.append(int(val[-3]))
g.append(int(val[-2]))
b.append(int(val[-1]))
new_img[(i * 10) : (i + 1) * 10, :, 0] = numpy.array(r)
new_img[(i * 10) : (i + 1) * 10, :, 1] = numpy.array(g)
new_img[(i * 10) : (i + 1) * 10, :, 2] = numpy.array(b)

# Set the last row for the channel sliders transparent gradient
new_img[-10:] = 0
new_img[-10:, :180, 3] = numpy.linspace(255, 0, 180, dtype="uint8")

image = Image.fromarray(new_img)
# Save the image to a BytesIO stream
buffer = BytesIO()
image.save(buffer, format="PNG")
buffer.seek(0)

# Cache the image using the version-based key
# Cache timeout set to None (no timeout)
cache.set(cache_key, buffer.getvalue(), None)

return HttpResponse(buffer.getvalue(), content_type="image/png")


@login_required()
Expand Down

0 comments on commit 4f05b5e

Please sign in to comment.