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 all 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):
stack_group_ids = StackStackGroup.objects \
.filter(stack=stack_id) \
.values_list('stack_group_id', flat=True)

result = {
'stack_group_ids': stack_group_ids
}

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 (managing_stack_id !== undefined) {
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