Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Label stack support #1816

Merged
merged 16 commits into from
Dec 3, 2018
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion django/applications/catmaid/control/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.shortcuts import get_object_or_404

from ..models import UserRole, Project, Stack, ProjectStack, \
BrokenSlice, StackMirror
BrokenSlice, StackMirror, StackStackGroup
from .authentication import requires_user_role

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -152,3 +152,15 @@ def stacks(request, project_id=None):
'sort_keys': True,
'indent': 4
})

@requires_user_role([UserRole.Annotate, UserRole.Browse])
def stack_groups(request, project_id=None, stack_id=None):
stackgroup_links = StackStackGroup.objects \
.filter(stack=stack_id) \
.select_related('group_relation')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is group_relation joined here? It seems below only the stack group ID is needed and we could basically use .values_list('id', flat=True) to get the result list of IDs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's vestigial from including the relation type with the group links, before I decided to just retrieve the groups explicitly anyway (thinking I might put the raw/label enum on groups). Will remove.


result = {
'stack_group_ids': [l.stack_group_id for l in stackgroup_links]
}

return JsonResponse(result)
1 change: 0 additions & 1 deletion django/applications/catmaid/control/stackgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def get_stackgroup_info(request, project_id, stackgroup_id):
.filter(stack_group=stackgroup_id) \
.order_by('position') \
.select_related('group_relation')
stacks = [l.stack_id for l in stackgroup_links]

result = {
'id': stackgroup.id,
Expand Down
12 changes: 6 additions & 6 deletions django/applications/catmaid/static/css/stack.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,16 @@ div.smallMapView_hidden div.smallMapRect div {
}

div.LayerControl {
border: solid 1px #00F;
border-right: solid 1px #00F;
margin: 0;
padding: 8px;
position: relative;
top: 10px;
left: 10px;
width: 40em;
top: 0;
left: 0;
width: 42em;
min-width: 13em;
max-width: 80%;
height: 65%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
color: #FFFFFF;
Expand Down Expand Up @@ -207,7 +207,7 @@ div.LayerControl li.highlight {

.layerControl .layerFilterControl input.remove {
position: absolute;
left: 0.5em;
left: 2em;
}

.layerControl .layerFilterControl h5 {
Expand Down
35 changes: 20 additions & 15 deletions django/applications/catmaid/static/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -1362,13 +1362,13 @@ var project;
stacks: stacks
};
CATMAID.layoutStackViewers();
// Make all tile layers visible, they have been initialized
// Make all stack layers visible, they have been initialized
// invisible.
for (var i=0; i<loadedStackViewers.length; ++i) {
var sv = loadedStackViewers[i];
var tileLayers = sv.getLayersOfType(CATMAID.TileLayer);
for (var j=0; j<tileLayers.length; ++j) {
var tl = tileLayers[j];
var stackLayers = sv.getLayersOfType(CATMAID.StackLayer);
for (var j=0; j<stackLayers.length; ++j) {
var tl = stackLayers[j];
tl.setOpacity(1.0);
tl.redraw();
}
Expand Down Expand Up @@ -1517,7 +1517,7 @@ var project;
* @param {StackViewer} stackViewer Viewer to which to add the stack.
* @param {number} mirrorIndex Optional mirror index, defaults to
* the first available.
* @param {Boolean} hide The stack's tile layer will initially be
* @param {Boolean} hide The stack's layer will initially be
* hidden.
* @return {Promise} A promise yielding the stack viewer
* containing the new stack.
Expand Down Expand Up @@ -1590,40 +1590,45 @@ var project;
});
}

function loadStack(e, stackViewer, hideTileLayer) {
function loadStack(e, stackViewer, hideStackLayer) {
var useExistingViewer = typeof stackViewer !== 'undefined';

var stack = new CATMAID.Stack.fromStackInfoJson(e);

// If this is a label stack, not a raw stack, create a label annotation
// manager.
// TODO: should eventually use a backend image label space instead.
if (!!stack.labelMetadata()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, using the metadata field of a stack is reasonable for now. Once the whole infrastructure is there and becomes clearer how everything works together, we can think about the label space implementation again.

CATMAID.LabelAnnotations.get(stack);
}

if (!useExistingViewer) {
stackViewer = new CATMAID.StackViewer(project, stack);
}

document.getElementById( "toolbox_project" ).style.display = "block";

var tilelayerConstructor = CATMAID.TileLayer.Settings.session.prefer_webgl ?
CATMAID.PixiTileLayer :
CATMAID.TileLayer;
var tilelayer = new tilelayerConstructor(
var stackLayerConstructor = CATMAID.StackLayer.preferredConstructorForStack();
var stackLayer = new stackLayerConstructor(
stackViewer,
"Image data (" + stack.title + ")",
stack,
mirrorIndex,
!hideTileLayer,
hideTileLayer ? 0 : 1,
!hideStackLayer,
hideStackLayer ? 0 : 1,
!useExistingViewer,
CATMAID.TileLayer.Settings.session.linear_interpolation,
CATMAID.StackLayer.INTERPOLATION_MODES.INHERIT,
true);

if (!useExistingViewer) {
stackViewer.addLayer( "TileLayer", tilelayer );
stackViewer.addLayer("StackLayer", stackLayer);

project.addStackViewer( stackViewer );

// refresh the overview handler to also register the mouse events on the buttons
stackViewer.layercontrol.refresh();
} else {
stackViewer.addStackLayer(stack, tilelayer);
stackViewer.addStackLayer(stack, stackLayer);
}

CATMAID.ui.releaseEvents();
Expand Down
171 changes: 171 additions & 0 deletions django/applications/catmaid/static/js/label-annotations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
(function (CATMAID) {

"use strict";

class LabelAnnotations {
constructor() {
this.groupManagingStack = new Map();
this.managers = new Map();
this.active = undefined;
}

activate(stackID) {
this.active = stackID;
}

clear() {
for (const [_stack, manager] of this.managers.entries()) manager.unregister();
this.managers.clear();
this.groupManagingStack.clear();
this.active = undefined;
}

get(stack) {
let manager = this.managers.get(stack.id);

if (!manager) {
// Find all stack groups to which this stack belongs.
return CATMAID.fetch(project.id + '/stack/' + stack.id + '/groups')
.then(response =>
// Get the info of all of these groups.
Promise.all(response.stack_group_ids.map(
sg_id => CATMAID.fetch(project.id + '/stackgroup/' + sg_id + '/info')))
)
.then(sg_infos => {
// Find stack groups where this stack is an (ortho)view.
let ortho_sgs = sg_infos
.filter(sg_info => sg_info.stacks
.find(s => s.id === stack.id)
.relation === 'view');

// Find if an existing manager for any of these stack groups exists.
let managing_stack_id = ortho_sgs.find(sg => this.groupManagingStack.get(sg.id));

if (typeof managing_stack_id !== 'undefined') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity: Is there any benefit in using this over managing_stack_id !== undefined (which seems easier to read to me)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since managing_stack_id is declared in this scope, no benefit. Will fix.

manager = this.managers.get(managing_stack_id);
} else {
// If no manager exists, create a new one.
managing_stack_id = stack.id;
manager = new LabelStackAnnotations(stack);
ortho_sgs.forEach(sg => this.groupManagingStack.set(sg.id, managing_stack_id));
// Add all other stack (ortho)views in all (ortho)view groups
// to this manager.
ortho_sgs.forEach(sg => sg.stacks
.filter(s => s.relation === 'view')
.forEach(s => manager.addStackID(s.id)));
this.managers.set(stack.id, manager);
}

return manager;
});
}

return Promise.resolve(manager);
}
}

CATMAID.LabelAnnotations = new LabelAnnotations();

CATMAID.Init.on(CATMAID.Init.EVENT_PROJECT_CHANGED, CATMAID.LabelAnnotations.clear, CATMAID.LabelAnnotations);


const LABEL_FILTER_KEY = 'Object Label Color Map';

const SPECIAL_LABELS = {
background: 0,
};

class LabelStackAnnotations {
constructor(
primaryStack
) {
this.primaryStack = primaryStack;
this.stackIDs = new Set([this.primaryStack.id]);
this.activeLabelID = undefined;

this.specialLabels = Object.assign({}, SPECIAL_LABELS, this.primaryStack.labelMetadata());
this.stackLayerFilters = new Map();

project.on(CATMAID.Project.EVENT_STACKVIEW_ADDED,
this.registerStackViewerLayers, this);
CATMAID.StackViewer.on(CATMAID.StackViewer.EVENT_STACK_LAYER_ADDED,
this.registerStackLayer, this);
CATMAID.StackViewer.on(CATMAID.StackViewer.EVENT_STACK_LAYER_REMOVED,
this.unregisterStackLayer, this);

this.registerAllStackLayers();
}

addStackID(stackID) {
this.stackIDs.add(stackID);
}

unregister() {
project.off(CATMAID.Project.EVENT_STACKVIEW_ADDED,
this.registerStackViewerLayers, this);
CATMAID.StackViewer.off(CATMAID.StackViewer.EVENT_STACK_LAYER_ADDED,
this.registerStackLayer, this);
CATMAID.StackViewer.off(CATMAID.StackViewer.EVENT_STACK_LAYER_REMOVED,
this.unregisterStackLayer, this);
this.stackLayerFilters.clear();
}

activateLabel(labelID) {
this.activeLabelID = labelID;

for (const [stackLayer, filter] of this.stackLayerFilters.entries()) {
this.updateFilter(filter);
stackLayer.redraw();
stackLayer.stackViewer.layercontrol.refresh();
}
}

registerAllStackLayers() {
for (const stackViewer of project.getStackViewers()) {
this.registerStackViewerLayers(stackViewer);
}
}

registerStackViewerLayers(stackViewer) {
for (const stackLayer of stackViewer.getLayersOfType(CATMAID.StackLayer)) {
this.registerStackLayer(stackLayer, stackViewer);
}
}

registerStackLayer(stackLayer, stackViewer) {
if (!this.stackIDs.has(stackLayer.stack.id)) return;

let layerFilters = stackLayer.getAvailableFilters ? stackLayer.getAvailableFilters() : [];
if (LABEL_FILTER_KEY in layerFilters && !this.stackLayerFilters.has(stackLayer)) {
stackLayer.setBlendMode('add');
stackLayer.setInterpolationMode(CATMAID.StackLayer.INTERPOLATION_MODES.NEAREST);
stackLayer.isHideable = true;
let filter = new (layerFilters[LABEL_FILTER_KEY])();
this.updateFilter(filter);
stackLayer.addFilter(filter);
this.stackLayerFilters.set(stackLayer, filter);

// TODO: coupling state refresh with stack viewer.
stackLayer.redraw();
stackViewer.layercontrol.refresh();
}
}

unregisterStackLayer(stackLayer, stackViewer) {
if (!this.stackIDs.has(stackLayer.stack.id)) return;

this.stackLayerFilters.delete(stackLayer);
}

updateFilter(filter) {
filter.pixiFilter.backgroundLabel = CATMAID.PixiLayer.Filters.int2arr(
this.specialLabels.background);
filter.pixiFilter.unknownLabel = typeof this.activeLabelID === 'undefined' ?
[-1, -1, -1, -1] :
CATMAID.PixiLayer.Filters.int2arr(this.activeLabelID);
}
}

CATMAID.LabelStackAnnotations = LabelStackAnnotations;

})(CATMAID);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
this.view = document.createElement( "div" );
this.view.className = "LayerControl";
this.view.id = "LayerControl" + stackViewer.id;
this.view.style.zIndex = 8;
this.view.style.zIndex = 6;

stackViewer.getView().appendChild( this.view );
};
Expand Down
Loading