Skip to content

Commit

Permalink
Desktop/input data shapes (#181)
Browse files Browse the repository at this point in the history
* no save_as_trk option for only 1 frame

* let caliban open 3 and 5D npzs in some cases

3D npzs can have a dimension added and removed upon save (a single "frame").
5D npzs can be opened as 4D if the first ("batch") dimension just has a size of 1.

* don't display "slices" info if the file only has one slice/frame

* initial Mode2D class, subclassed from Mode

Making prompt text more modular and specific to use-case

* consolidate prompt text into helper function

Text doesn't depend on number of frames, so only need to implement once

* further refactoring and subclassing Mode

- self.simple_answer instead of answer string
- string formatting to make some questions more generalizable across classes
- pulling more cases into helper functions for modularity
- Mode3D class

* further refactoring of Mode to use Mode2D and Mode3D

* use ModeTrack to extend Mode3D for working with trk files

* add "single_frame" attribute to ZStackReview for 2D/3D bookkeeping

Instead of checking self.num_frames == 1 each time

* fix prompt typos in Mode

* restrict ZStackReview single-frame keybinds to match Mode

ie, if Mode prompt only says "space to confirm/esc to cancel", pressing
"s" should not trigger the "single-frame" version of the action,
only space should trigger the action. "Predict" option should not be
triggered by P keybind for single-frame files.

* use self.raw.shape instead of raw.shape to get num_frames

self.raw has been converted into 4D if needed, raw has not

* simplify self.single_frame assignment

Co-authored-by: willgraf <7930703+willgraf@users.noreply.github.com>

* fix save typo

* recommended change for generating self.display_info

* text formatting, spacing fixes

* minor improvement in self.display_info generation

* add warning print statements if 3D or 5D npz arrays are opened

Co-authored-by: willgraf <7930703+willgraf@users.noreply.github.com>
  • Loading branch information
geneva-miller and willgraf committed Aug 3, 2020
1 parent d2dd161 commit d87d1a6
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 123 deletions.
106 changes: 60 additions & 46 deletions desktop/caliban.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# limitations under the License.
# ==============================================================================
"""Displaying and Curating annotations tracked over time in multiple frames."""
from mode import Mode
from mode import Mode, Mode2D, Mode3D, ModeTrack

import cv2
import json
Expand Down Expand Up @@ -1938,10 +1938,6 @@ class TrackReview(CalibanWindow):
possible_keys = {"label", "daughters", "frames", "parent", "frame_div",
"capped"}

replace_prompt = ("\nReplace {} with {}?"
"\nSPACE = REPLACE IN ALL FRAMES"
"\nESC = CANCEL")

def __init__(self, filename, lineage, raw, tracked):
self.filename = filename
self.tracks = lineage
Expand All @@ -1968,8 +1964,7 @@ def __init__(self, filename, lineage, raw, tracked):
self.max_intensity = None
self.x = 0
self.y = 0
self.mode = Mode.none()
self.mode.update_prompt_additions = self.custom_prompt
self.mode = ModeTrack.none()
self.adjustment = 0
self.highlight = False
self.highlighted_cell_one = -1
Expand Down Expand Up @@ -2631,12 +2626,6 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):
self.action_parent()
self.mode.clear()

def custom_prompt(self):
if self.mode.kind == "QUESTION":
if self.mode.action == "REPLACE":
self.mode.text = TrackReview.replace_prompt.format(self.mode.label_2,
self.mode.label_1)

def get_raw_current_frame(self):
return self.raw[self.current_frame,:,:,0]

Expand Down Expand Up @@ -3068,11 +3057,6 @@ def save(self):

class ZStackReview(CalibanWindow):

save_prompt_text = ("\nSave current file?"
"\nSPACE = SAVE"
"\nT = SAVE AS .TRK FILE"
"\nESC = CANCEL")

def __init__(self, filename, raw, annotated, save_vars_mode):
'''
Set object attributes to store raw and annotated images (arrays),
Expand Down Expand Up @@ -3103,8 +3087,23 @@ def __init__(self, filename, raw, annotated, save_vars_mode):
# file opens to the first channel
self.channel = 0

# should be robust to 3D, 4D, and some cases of 5D array sizes
self.dims = raw.ndim
if self.dims == 3:
print('Warning: Caliban is intended to open 4D arrays.'
' Did you mean to open a 3D file?')
self.raw = np.expand_dims(self.raw, axis=0)
self.annotated = np.expand_dims(self.annotated, axis=0)

elif self.dims == 5:
print('Warning: Caliban is intended to open 4D arrays.'
' Did you mean to open a 5D file?')
self.raw = np.squeeze(self.raw, axis=0)
self.annotated = np.squeeze(self.annotated, axis=0)

# unpack the shape of the raw array
self.num_frames, self.height, self.width, self.channel_max = raw.shape
self.num_frames, self.height, self.width, self.channel_max = self.raw.shape
self.single_frame = self.num_frames == 1

# info dictionaries that will be populated with info about labels for
# each feature of annotation array
Expand All @@ -3120,10 +3119,17 @@ def __init__(self, filename, raw, annotated, save_vars_mode):
try:
first_key = list(self.cell_info[0])[0]
display_info_types = self.cell_info[0][first_key]
self.display_info = [*sorted(set(display_info_types) - {'frames'})]
if self.single_frame:
self.display_info = list(sorted(set(display_info_types) - {'frames', 'slices'}))
else:
self.display_info = list(sorted(set(display_info_types) - {'frames'}))

# if there are no labels in the feature, hardcode the display info
except:
self.display_info = ['label', 'slices']
if self.single_frame:
self.display_info = ['label']
else:
self.display_info = ['label', 'slices']

# open file to first frame of annotation stack
self.current_frame = 0
Expand Down Expand Up @@ -3155,8 +3161,10 @@ def __init__(self, filename, raw, annotated, save_vars_mode):
# self.mode keeps track of selected labels, pending actions, displaying
# prompts and confirmation dialogue, using Mode class; start with Mode.none()
# (nothing selected, no actions pending)
self.mode = Mode.none()
self.mode.update_prompt_additions = self.custom_prompt
if self.single_frame:
self.mode = Mode2D.none()
else:
self.mode = Mode3D.none()

# start with highlighting option turned off and no labels highlighted
self.highlight = False
Expand Down Expand Up @@ -3185,11 +3193,6 @@ def __init__(self, filename, raw, annotated, save_vars_mode):
# start pyglet event loop
pyglet.app.run()

def custom_prompt(self):
if self.mode.kind == "QUESTION":
if self.mode.action == "SAVE":
self.mode.text = ZStackReview.save_prompt_text

def handle_threshold(self):
'''
Helper function to do pre- and post-action bookkeeping for thresholding.
Expand Down Expand Up @@ -3637,7 +3640,7 @@ def edit_mode_misc_keypress_helper(self, symbol, modifiers):
if symbol == key.SPACE:
self.save()
self.mode.clear()
if symbol == key.T:
if symbol == key.T and self.num_frames > 1:
self.save_as_trk()
self.mode.clear()

Expand Down Expand Up @@ -3792,7 +3795,7 @@ def label_mode_none_keypress_helper(self, symbol, modifiers):
self.mode.update("QUESTION", action="SAVE")

# PREDICT
if symbol == key.P:
if symbol == key.P and not self.single_frame:
self.mode.update("QUESTION", action="PREDICT", **self.mode.info)

# RELABEL
Expand Down Expand Up @@ -3889,7 +3892,7 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):
'''
# RESPOND TO SAVE QUESTION
if self.mode.action == "SAVE":
if symbol == key.T:
if symbol == key.T and not self.single_frame:
self.save_as_trk()
self.mode.clear()
if symbol == key.SPACE:
Expand All @@ -3898,18 +3901,19 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):

# RESPOND TO RELABEL QUESTION
elif self.mode.action == "RELABEL":
if symbol == key.U:
self.action_relabel_unique()
self.mode.clear()
if symbol == key.P:
self.action_relabel_preserve()
self.mode.clear()
if symbol == key.S:
self.action_relabel_frame()
self.mode.clear()
if symbol == key.SPACE:
self.action_relabel_all_frames()
self.mode.clear()
if not self.single_frame:
if symbol == key.U:
self.action_relabel_unique()
self.mode.clear()
if symbol == key.P:
self.action_relabel_preserve()
self.mode.clear()
if symbol == key.S:
self.action_relabel_frame()
self.mode.clear()

# RESPOND TO PREDICT QUESTION
elif self.mode.action == "PREDICT":
Expand All @@ -3922,7 +3926,7 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):

# RESPOND TO CREATE QUESTION
elif self.mode.action == "CREATE NEW":
if symbol == key.S:
if symbol == key.S and not self.single_frame:
self.action_new_single_cell()
self.mode.clear()
if symbol == key.SPACE:
Expand All @@ -3931,7 +3935,7 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):

# RESPOND TO REPLACE QUESTION
elif self.mode.action == "REPLACE":
if symbol == key.S:
if symbol == key.S and not self.single_frame:
self.action_replace_single()
self.mode.clear()
if symbol == key.SPACE:
Expand All @@ -3940,7 +3944,7 @@ def label_mode_question_keypress_helper(self, symbol, modifiers):

# RESPOND TO SWAP QUESTION
elif self.mode.action == "SWAP":
if symbol == key.S:
if symbol == key.S and not self.single_frame:
self.action_swap_single_frame()
self.mode.clear()
if symbol == key.SPACE:
Expand Down Expand Up @@ -4695,16 +4699,27 @@ def save(self):
self.raw and self.annotated are arrays to save in npz (self.raw should always
remain unmodified, but self.annotated may be modified)
'''
# make sure has same dims as original
if self.dims == 3:
raw = np.squeeze(self.raw, axis=0)
ann = np.squeeze(self.annotated, axis=0)
elif self.dims == 4:
raw = self.raw
ann = self.annotated
elif self.dims == 5:
raw = np.expand_dims(self.raw, axis=0)
ann = np.expand_dims(self.annotated, axis=0)

# create filename to save as
save_file = self.filename + "_save_version_{}.npz".format(self.save_version)

try:
# if file was opened with variable names raw and annotated, save them that way
if self.save_vars_mode == 0:
np.savez(save_file, raw = self.raw, annotated = self.annotated)
np.savez(save_file, raw=raw, annotated=ann)
# otherwise, save as X and y
else:
np.savez(save_file, X = self.raw, y = self.annotated)
np.savez(save_file, X=raw, y=ann)
# keep track of which version of the file this is
self.save_version += 1

Expand Down Expand Up @@ -5200,4 +5215,3 @@ def review(filename):

if __name__ == "__main__":
review(sys.argv[1])

0 comments on commit d87d1a6

Please sign in to comment.