diff --git a/.ipynb_checkpoints/Untitled0-checkpoint.ipynb b/.ipynb_checkpoints/Untitled0-checkpoint.ipynb new file mode 100644 index 0000000..64fab9d --- /dev/null +++ b/.ipynb_checkpoints/Untitled0-checkpoint.ipynb @@ -0,0 +1,9 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:75ae90dd9efb8c17f74ffbcf0b84c75bf22ce3964745d68eb6461b00d14cc1b7" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [] +} \ No newline at end of file diff --git a/Untitled0.ipynb b/Untitled0.ipynb new file mode 100644 index 0000000..3cd86be --- /dev/null +++ b/Untitled0.ipynb @@ -0,0 +1,60 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:f811fc625a7ce723c171d5490cda4d9b8501943811abe69feb9e028db8bdf7fc" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [ + "data_dir = '/Users/d/Desktop/data/Alyssa/Scan1/021/000001'\n", + "import os\n" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 12 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "level = 0\n", + "for root, dirs, files in os.walk(data_dir):\n", + " if 'image_coordinates.txt' in files:\n", + " print level\n", + " level += 1\n", + " \n", + " " + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n" + ] + } + ], + "prompt_number": 13 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] + } + ], + "metadata": {} + } + ] +} \ No newline at end of file diff --git a/_dojo/__init__.py b/_dojo/__init__.py index 0c8597a..5934b96 100644 --- a/_dojo/__init__.py +++ b/_dojo/__init__.py @@ -5,3 +5,4 @@ from websockets import Websockets from controller import Controller from database import Database +from neuroblocks import Neuroblocks diff --git a/_dojo/controller.py b/_dojo/controller.py index e7935bf..a8dd5ce 100644 --- a/_dojo/controller.py +++ b/_dojo/controller.py @@ -1,3 +1,4 @@ +import datetime import h5py import os, errno import json @@ -11,7 +12,7 @@ class Controller(object): - def __init__(self, mojo_dir, out_dir, tmp_dir, database): + def __init__(self, mojo_dir, out_dir, tmp_dir, database, neuroblocks=None, neuroblocks_project_id=None): ''' ''' self.__websocket = None @@ -32,6 +33,10 @@ def __init__(self, mojo_dir, out_dir, tmp_dir, database): self.__database = database + self.__neuroblocks = neuroblocks + + self.__neuroblocks_project_id = neuroblocks_project_id + if self.__database: self.__largest_id = self.__database.get_largest_id() else: @@ -63,7 +68,12 @@ def send_welcome(self): output = {} output['name'] = 'WELCOME' output['origin'] = 'SERVER' - output['value'] = '' + + config = {} + config['neuroblocks'] = self.__neuroblocks != None + config['neuroblocks_project_id'] = self.__neuroblocks_project_id + + output['value'] = config self.__websocket.send(json.dumps(output)) @@ -127,6 +137,19 @@ def send_problem_table(self, origin): self.__websocket.send(json.dumps(output)) + def send_restore_state(self, origin, state_id): + ''' + ''' + + output = {} + output['name'] = 'RESTORE_STATE' + output['origin'] = origin + output['value'] = self.__neuroblocks.get_state(state_id) + + self.__websocket.send(json.dumps(output)) + + + def on_message(self, message): ''' ''' @@ -137,6 +160,14 @@ def on_message(self, message): self.__users.append(input['origin']) + # + # let's check if we should restore a neuroblocks state + # + state_id = input['value']['neuroblocks_state_id'] + if state_id: + # we do have a state restore request + self.send_restore_state(input['origin'], state_id) + elif input['name'] == 'MERGETABLE': self.__merge_table = input['value'] @@ -178,6 +209,44 @@ def on_message(self, message): elif input['name'] == 'SAVE': self.save(input) + elif input['name'] == 'SAVE_STATE': + self.save_state(input) + + elif input['name'] == 'SAVE_PICK2D': + self.save_pick2d(input) + + elif input['name'] == 'SAVE_ACTION': + self.save_action(input) + + def save_action(self, input): + ''' + ''' + values = input['value'] + values['on'] = datetime.datetime.utcnow() + + self.__neuroblocks.save_action(values) + + + def save_pick2d(self, input): + ''' + ''' + values = input['value'] + values['on'] = datetime.datetime.utcnow() + + self.__neuroblocks.save_pick2d(values) + + + def save_state(self, input): + ''' + ''' + values = input['value'] + values['on'] = datetime.datetime.utcnow() + + + new_id = self.__neuroblocks.save_state(values) + + print 'Stored state', new_id + def adjust(self, input): ''' diff --git a/_dojo/datasource.py b/_dojo/datasource.py index 9fcf785..16dc42b 100644 --- a/_dojo/datasource.py +++ b/_dojo/datasource.py @@ -124,9 +124,16 @@ def get_volume(self, zoomlevel): tile_files = [] for d in dirs: + if d.startswith('.'): + continue + files = os.listdir(os.path.join(w_path,d)) for f in files: + + if f.startswith('.'): + continue + # check if we have an updated version for this tile if os.path.exists(os.path.join(w_path_tmp, d, f)): tile_files.append(os.path.join(w_path_tmp, d, f)) diff --git a/_dojo/neuroblocks.py b/_dojo/neuroblocks.py new file mode 100644 index 0000000..d340dc2 --- /dev/null +++ b/_dojo/neuroblocks.py @@ -0,0 +1,98 @@ +import pymongo +from bson.objectid import ObjectId + +class Neuroblocks(object): + + DATABASE = 'meteor' + + def __init__(self, server): + ''' + ''' + if server: + try: + self._connection = pymongo.Connection('mongodb://'+server+'/meteor') + self._db = self._connection[Neuroblocks.DATABASE] + print 'Connected to Neuroblocks.' + except: + print 'Connection to Neuroblocks failed.' + + + else: + self._connection = None + + def get_segmentation(self, id): + ''' + ''' + + return self._db.segmentation.find_one({"_id": id}) + + def get_segmentation_log(self, id): + ''' + ''' + return self._db.segmentationLog.find_one({"_id": id}) + + def save_state(self, state): + ''' + ''' + + # first store the screenshot + screenshot = state['screenshot'] + # delete state['screenshot']; + state.pop('screenshot', 0) + + screenshot_id = self._db.screenshotsApps.insert({'img':screenshot, 'type':''}) + + state['img_id'] = screenshot_id + + return self._db.appStates.insert(state) + + def save_pick2d(self, values): + ''' + ''' + return self._db.appPickSegment.insert(values) + + def save_action(self, values): + ''' + ''' + + db = self._db + + project_id = values['projectId'] + user_id = values['userId'] + segmentId2 = values['values'][0] + segmentId1 = values['values'][1] + date = values['on'] + + + segment1 = db.segmentation.find_one({"projectId":project_id, "id":segmentId1}); + + segment2 = db.segmentation.find_one({"projectId":project_id, "id":segmentId2}); + + db.segmentationLog.insert({'objId':segment1['_id'], + 'projectId':project_id, + 'operation':'merge', + 'by':user_id, + 'on': date, + 'obj': segment1, + 'objId2': segment2['_id'], + 'obj2':segment2 }); + + voxelsSum = segment1['voxels'] + segment2['voxels']; + db.segmentation.update({'_id':segment1['_id']}, + {'$set':{'voxels': voxelsSum, 'merged':2, 'lastUpdateOn': date, 'lastUpdateBy':user_id}}); + db.segmentation.update({'_id':segment2['_id']}, + {'$set':{ 'merged':1, 'lastUpdateOn': date, 'lastUpdateBy':user_id}}); + + print 'stored neuroblocks action' + + + def get_state(self, state_id): + ''' + ''' + + state = self._db.appStates.find_one({"_id": ObjectId(state_id)}) + state['_id'] = str(state['_id']) + state['img_id'] = str(state['img_id']) + state['on'] = str(state['on']) + + return state diff --git a/_dojo/scripts/iframe.html b/_dojo/scripts/iframe.html new file mode 100644 index 0000000..ee5a1cf --- /dev/null +++ b/_dojo/scripts/iframe.html @@ -0,0 +1,8 @@ + + +
+ + + + + \ No newline at end of file diff --git a/_dojo/scripts/sub.py b/_dojo/scripts/sub.py new file mode 100644 index 0000000..67eb8ff --- /dev/null +++ b/_dojo/scripts/sub.py @@ -0,0 +1,29 @@ +import cv2 +import numpy as np +import sys + + +factor = int(sys.argv[2]) + +out = np.zeros((16384/factor,16384/factor), dtype=np.uint8) + + +img = cv2.imread(sys.argv[1], cv2.CV_LOAD_IMAGE_GRAYSCALE) + +k = 0 +l = 0 + +for i in range(16384): + if i % factor == 0: + + for j in range(16384): + if j % factor == 0: + + out[k][l] = img[i][j] + + l += 1 + + l = 0 + k += 1 + +cv2.imwrite('/tmp/subplain.jpg', out) diff --git a/_dojo/scripts/subcv.py b/_dojo/scripts/subcv.py new file mode 100644 index 0000000..7f44fc4 --- /dev/null +++ b/_dojo/scripts/subcv.py @@ -0,0 +1,11 @@ +import cv2 +import numpy as np +import sys + + +factor = int(sys.argv[2]) + + +img = cv2.imread(sys.argv[1], cv2.CV_LOAD_IMAGE_GRAYSCALE) +# img = cv2.resize(img, (16384/factor, 16384/factor)) +# cv2.imwrite('/tmp/subcv.jpg', img) diff --git a/_dojo/viewer.py b/_dojo/viewer.py index ea08568..6ee7f4a 100644 --- a/_dojo/viewer.py +++ b/_dojo/viewer.py @@ -9,7 +9,7 @@ def __init__(self): ''' self.__query_viewer_regex = re.compile('^/dojo/.*$') - self.__web_dir = '_web/' + self.__web_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../_web/') def content_type(self, extension): ''' @@ -45,8 +45,6 @@ def handle(self, request): requested_file = self.__web_dir + url.replace('/dojo/', '') extension = os.path.splitext(requested_file)[1] - - if not os.path.exists(requested_file): return 'Error 404', 'text/html' diff --git a/_web/camera.js b/_web/camera.js index 930ce56..2f6b4a9 100644 --- a/_web/camera.js +++ b/_web/camera.js @@ -81,7 +81,7 @@ J.camera.prototype.zoom = function(x, y, delta) { // don't zoom when using adjust or split if (this._viewer._controller._split_mode != -1) return; - if (this._viewer._controller._adjust_mode != -1) return; + if (this._viewer._controller._adjust_mode != -1 && !DOJO.single_segment) return; // perform linear zooming until a new image zoom level is reached // then reset scale to 1 and show the image @@ -186,8 +186,10 @@ J.camera.prototype.slice_up = function() { if (this._z == this._viewer._image.max_z_tiles-1) return; // dont slice when using tools - if (this._viewer._controller._split_mode != -1) return; - if (this._viewer._controller._adjust_mode != -1) return; + if (!DOJO.single_segment) { + if (this._viewer._controller._split_mode != -1) return; + if (this._viewer._controller._adjust_mode != -1) return; + } this._viewer._controller.clear_exclamationmarks(); this._viewer._controller.reset_cursors(); @@ -207,8 +209,11 @@ J.camera.prototype.slice_down = function() { if (this._z == 0) return; // dont slice when using tile tools - if (this._viewer._controller._split_mode != -1) return; - if (this._viewer._controller._adjust_mode != -1) return; + if (!DOJO.single_segment) { + if (this._viewer._controller._split_mode != -1) return; + if (this._viewer._controller._adjust_mode != -1) return; + } + this._viewer._controller.clear_exclamationmarks(); this._viewer._controller.reset_cursors(); diff --git a/_web/controller.js b/_web/controller.js index 0b1076c..3b7fef8 100644 --- a/_web/controller.js +++ b/_web/controller.js @@ -45,6 +45,12 @@ J.controller = function(viewer) { this._brush_size = 3; this._brush_ijs = []; + this._neuroblocks = false; + this._neuroblocks_project_id = null; + this._neuroblocks_task_id = null; + this._neuroblocks_user_id = null; + this._neuroblocks_state_id = null; + this.create_gl_3d_labels(); }; @@ -86,7 +92,6 @@ J.controller.prototype.receive = function(data) { // we are the sender or the requester if (input.name == 'SPLITRESULT') { - console.log(input) this.show_split_line(input.value); return; } else if (input.name == 'SPLITDONE') { @@ -95,6 +100,8 @@ J.controller.prototype.receive = function(data) { this.finish_adjust(input.value); } else if (input.name == 'SAVED') { console.log('All saved. Yahoo!'); + } else if (input.name == 'RESTORE_STATE') { + this.restore_state(input.value); } return; @@ -102,7 +109,15 @@ J.controller.prototype.receive = function(data) { if (input.name == 'WELCOME') { - this.send('WELCOME', {}); + // check for the config + var config = input.value; + if (config['neuroblocks'] == true) { + this._neuroblocks = true; + this._neuroblocks_project_id = config['neuroblocks_project_id']; + DOJO.init_save_state_button(); + } + + this.send('WELCOME', {'neuroblocks_state_id': this._neuroblocks_state_id}); } else if (input.name == 'MERGETABLE') { @@ -134,8 +149,7 @@ J.controller.prototype.receive = function(data) { console.log('force reload'); this.reload_tiles(input.value); - - } + } }; @@ -1064,6 +1078,23 @@ J.controller.prototype.merge = function(id) { this.highlight(this._last_id); + // at this point, let's check for neuroblocks + if (this._neuroblocks) { + + // we want to store some extra information + var data = {}; + data['operation'] = 'merge'; + data['projectId'] = this._neuroblocks_project_id; + data['app'] = 'dojo'; + data['userId'] = this._neuroblocks_user_id; + // data['on'] = new Date(); <-- now on the server + data['taskId'] = this._neuroblocks_task_id; + data['values'] = [id, this._last_id]; + + this.send('SAVE_ACTION', data); + + } + }; J.controller.prototype.undo = function(x, y) { @@ -1314,3 +1345,92 @@ J.controller.prototype.end = function() { this.activate(null); }; + +J.controller.prototype.save_pick2d = function(id) { + + console.log('Picked segment', id); + + var data = {}; + data['projectId'] = this._neuroblocks_project_id; + data['taskId'] = this._neuroblocks_task_id; + data['userId'] = this._neuroblocks_user_id; + data['segmentId'] = id; + + this.send('SAVE_PICK2D', data); + +}; + +J.controller.prototype.save_state = function() { + + console.log('Saving state..'); + + // we need the merge table and the viewport + + // the merge table + var merge_table = this._merge_table; + + // the viewport + var camera = this._viewer._camera; + + var viewport = { + 'x': camera._x, + 'y': camera._y, + 'z': camera._z, + 'w': camera._w, + 'i_j': camera._i_j, + 'view': camera._view + } + + var screenshot = this._viewer._canvas.toDataURL('image/jpeg'); + + if (this._viewer._width >= this._viewer._height) { + var new_width = 200; + var new_height = this._viewer._height / (this._viewer._width / new_width); + } else { + var new_height = 200; + var new_width = this._viewer._width / (this._viewer._height / new_height); + } + + // resize the screenshot + resize_data_uri(screenshot, new_width, new_height, function(screenshot) { + + var data = {}; + data['projectId'] = this._neuroblocks_project_id; + data['mergeTable'] = merge_table; + data['viewPort'] = viewport; + data['screenshot'] = screenshot; + data['app'] = 'dojo'; + data['userId'] = this._neuroblocks_user_id; + // data['on'] = new Date(); <-- now on the server + data['taskId'] = this._neuroblocks_task_id; + + this.send('SAVE_STATE', data); + + + setTimeout(function() { + + DOJO.save_state_done(); + + }, 500); + + + }.bind(this)); + +}; + +J.controller.prototype.restore_state = function(state) { + + // the viewport + var camera = this._viewer._camera; + camera._x = state.viewPort.x; + camera._y = state.viewPort.y; + camera._z = state.viewPort.z; + camera._w = state.viewPort.w; + camera._i_j = state.viewPort.i_j; + camera._view = state.viewPort.view; + + this.update_merge_table(state.mergeTable) + + this._viewer.redraw(); + +}; diff --git a/_web/css/dojo.css b/_web/css/dojo.css index 3b1c047..4af145b 100644 --- a/_web/css/dojo.css +++ b/_web/css/dojo.css @@ -49,6 +49,17 @@ body { z-index: 1000; } +.threeDpanelLarge { + position: absolute; + left: 0px; + top: 0px; + width:100%; + height:100%; + background-color:black; + opacity: 1; + z-index: 1000; +} + #slicenumber { font-weight: bold; float: left; diff --git a/_web/dojo.js b/_web/dojo.js index 370780b..37f2657 100644 --- a/_web/dojo.js +++ b/_web/dojo.js @@ -12,14 +12,73 @@ DOJO.modes = { DOJO.threeD_active = false; DOJO.link_active = false; DOJO.mousemove_timeout = null; +DOJO.save_state_active = false; +DOJO.picking_active = false; +DOJO.single_segment = false; +DOJO.neuroblocks_threed_link = false; +DOJO.initial_threeD_id = null; -DOJO.init = function() { +DOJO.init = function(threeD) { + + var threeD = threeD || false; DOJO.viewer = new J.viewer('dojo1'); + + // check for neuroblocks args + args = parse_args(); + if (typeof(args['userName']) != 'undefined') { + DOJO.viewer._controller._origin = args['userName']; + } + if (typeof(args['userId']) != 'undefined') { + DOJO.viewer._controller._neuroblocks_user_id = args['userId']; + } + if (typeof(args['taskId']) != 'undefined') { + DOJO.viewer._controller._neuroblocks_task_id = args['taskId']; + } + if (typeof(args['appStateId']) != 'undefined') { + DOJO.viewer._controller._neuroblocks_state_id = args['appStateId']; + } + if (typeof(args['pick2d']) != 'undefined') { + DOJO.picking_active = true; + } + + DOJO.viewer.init(function() { DOJO.update_slice_number(1); + if (typeof(args['activeId']) != 'undefined') { + + var id = args['activeId']; + DOJO.viewer._controller._adjust_mode = 1; + DOJO.viewer._controller._adjust_id = id; + + DOJO.viewer._controller.activate(id); + DOJO.viewer._controller.highlight(id); + + DOJO.single_segment = true; + } + + if (threeD) { + + if (typeof(args['threeD_id']) != 'undefined') { + + DOJO.initial_threeD_id = args['threeD_id']; + + } + + if (typeof(args['link']) != 'undefined') { + + DOJO.neuroblocks_threed_link = true; + DOJO.viewer._websocket_nb = new J.websocketNB(); + } + + + DOJO.init_threeD(); + } + + + }); DOJO.setup_buttons(); @@ -35,7 +94,8 @@ DOJO.setup_buttons = function() { if (DOJO.mode != DOJO.modes.merge) { - DOJO.reset_tools(); + if (!DOJO.single_segment) + DOJO.reset_tools(); merge.style.display = 'none'; merge_selected.style.display = 'block'; @@ -163,6 +223,35 @@ DOJO.setup_buttons = function() { }; +DOJO.init_save_state_button = function() { + + var save_state = document.getElementById('save_state'); + var save_state_selected = document.getElementById('save_state_selected'); + + save_state.onclick = save_state_selected.onclick = function() { + + // link.style.border = '1px solid white'; + save_state.style.display = 'none'; + save_state_selected.style.display = 'block'; + + DOJO.save_state_active = true; + + DOJO.viewer._controller.save_state(); + + }; + +}; + +DOJO.save_state_done = function() { + + save_state.style.display = 'block'; + save_state_selected.style.display = 'none'; + + DOJO.save_state_active = false; + +}; + + DOJO.reset_tools = function() { DOJO.mode = DOJO.modes.pan_zoom; @@ -227,6 +316,20 @@ DOJO.onleftclick = function(x, y) { DOJO.viewer._controller.remove_3d_label(id); } + } else if (DOJO.picking_active) { + + // picking of segment in 2d + DOJO.viewer._controller.save_pick2d(id); + + // now show only the picked segment + DOJO.viewer._controller._adjust_mode = 1; + DOJO.viewer._controller._adjust_id = id; + + DOJO.viewer._controller.activate(id); + DOJO.viewer._controller.highlight(id); + + DOJO.single_segment = true; + } } @@ -487,6 +590,24 @@ DOJO.init_threeD = function() { // we also need to redraw the problem table DOJO.viewer._controller.redraw_exclamationmarks(); + if (DOJO.viewer._controller._neuroblocks && DOJO.neuroblocks_threed_link) { + console.log('ThreeD view for neuroblocks is ready.', 'pid:'+DOJO.viewer._controller._neuroblocks_project_id); + + // send the project id + DOJO.viewer._websocket_nb.send('pid:'+DOJO.viewer._controller._neuroblocks_project_id); + + } + + if (DOJO.initial_threeD_id) { + + // directly show one segment + DOJO.viewer._controller._use_3d_labels = true; + + DOJO.viewer._controller.add_fixed_3d_label(parseInt(DOJO.initial_threeD_id,10)); + DOJO.viewer._controller.add_3d_label(parseInt(DOJO.initial_threeD_id,10)); + + } + } r.interactor.onMouseDown = function() { diff --git a/_web/gfx/save_state.png b/_web/gfx/save_state.png new file mode 100644 index 0000000..d166bab Binary files /dev/null and b/_web/gfx/save_state.png differ diff --git a/_web/gfx/save_state_selected.png b/_web/gfx/save_state_selected.png new file mode 100644 index 0000000..2c4bebb Binary files /dev/null and b/_web/gfx/save_state_selected.png differ diff --git a/_web/index.html b/_web/index.html index 74bb0f5..67abf9e 100644 --- a/_web/index.html +++ b/_web/index.html @@ -343,6 +343,8 @@
+
+
>>0;a.o=(e[g++]|
+e[g++]<<8|e[g++]<<16|e[g++]<<24)>>>0;a.w=e[g++]|e[g++]<<8;a.v=w?e.subarray(g,g+a.w):e.slice(g,g+a.w)}d=a.o;h=0;for(k=a.ba;h