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

Aggregate data #11

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Conversation

jo-mueller
Copy link

Fixes #4

Hi @haesleinhuepf ,

this PR attempts to start on expanding the functionality of the time-slicer to more Layer types and hopefully allow other functions that use the time-slicer to be a bit more agnostic to whether something is 4D or 3D or 2D +t or whatever.

Summary

In short, I brought the TimelapseConverter class from napari-stress (originally here) to the time-slicer and re-factored it a bit to be cleaner and better readable.

The class has two major methods: convert_4d_data_to_list and convert_list_to_4d_data which convert 4D to a list of 3D data or vice versa. The methods only require the to-be-converted data and the type of the input layer. The correct conversion function is then looked up from a dictionary that holds the respective conversion function for each kind of layer.

In the time-slicer, I created a new function aggregate, which basically drags the slider and aggregates all the data in memory. The aggregation only requires the convert_list_to_4d_data from the TimelapseConverter, but maybe the other step may also be helpful somewhere in the future if the first step in a workflow is a 4D surface or whatever.

Tests

I wrote unit tests and tested the aggregation from the napari viewer, which worked well. The only use case I didn't get to work was to get some measurements with regionprops aggregate those for all timeframes, which is probably because regionprops is currently not decorated (compatible?) with the time slicer.

This is probably not complete but maybe a step in the right direction :)

Future talk

  • I would love to somehow submit the execution of a workflow step to a dask client rather than having to wait for each frame's computation.
  • If the measurement functions would be compatible with the time-slicer, this would make the existence of multiple measurement functions (measurement vs meansurement_of_all_frames) redundant.

@haesleinhuepf
Copy link
Owner

Hi Johannes @jo-mueller ,

awesome, thanks for working on this!

I presume the function you're proposing here does the tecnically same as this one:

@register_function(menu="Utilities > Convert on-the-fly processed timelapse to 4D stack (time-slicer)")
def convert_to_stack4d(layer : LayerInput, viewer: napari.Viewer) -> Layer:
"""
Go through time (by moving the time-slider in napari) and copy 3D frames of a given layer
and store them in a new 4D layer in napari.
"""
# in case of 4D-data (timelapse) crop out the current 3D timepoint
if len(viewer.dims.current_step) != 4:
raise NotImplementedError("Processing all frames only supports 4D-data")
if len(layer.data.shape) >= 4:
raise NotImplementedError("Processing all frames only supports on-the-fly processed 2D and 3D data")
current_timepoint = viewer.dims.current_step[0]
max_time = int(viewer.dims.range[-4][1])
result = None
for f in range(max_time):
print("Processing frame", f)
# go to a specific time point
_set_timepoint(viewer, f)
# get the layer data at a specific time point
result_single_frame = np.asarray(layer.data).copy()
if len(result_single_frame.shape) == 2:
result_single_frame = np.asarray([result_single_frame])
if result is None:
result = [result_single_frame]
else:
result.append(result_single_frame)
output_data = np.asarray(result)
print("Output:", output_data.shape)
# go back to the time point selected before
_set_timepoint(viewer, current_timepoint)
if isinstance(layer, Labels):
return Labels(output_data, name="Stack 4D " + layer.name)
else:
return Image(output_data, name="Stack 4D " + layer.name)

Do you think it would be possible to merge both functions?

  • I would love to somehow submit the execution of a workflow step to a dask client rather than having to wait for each frame's computation.

I've started working on this here:

Feel free to try it out and see if this in compatible wit points and surfaces. Feedback is very welcome.

Best,
Robery

@jo-mueller
Copy link
Author

@haesleinhuepf thanks for looking into it.

Do you think it would be possible to merge both functions?

I could simply merge my code into that function, jup. They are pretty identical, that's true ^^ I'll update the PR.

@jo-mueller
Copy link
Author

@haesleinhuepf I merged the two functions with identical scope into a single function (the one that already existed).

@jo-mueller jo-mueller marked this pull request as ready for review October 18, 2023 11:52
@jo-mueller
Copy link
Author

Hi @haesleinhuepf , thanks for taking the time to look at this. Just a side note on how I did the interactive testing: I created some 2D+t sample data like this:

from skimage import data
sample_data = np.stack([data.binary_blobs()] for i in range(5))

and added it to a napari viewer. I then created a small workflow (connected components, label erosion) and used the convert_to_stack4d function from the tools menu. I would also like to add this test routine as an actual test to the PR, but I'm not sure how I can recreate the interactively assembled workflow from code...

Otherwise, I think it's also functional as it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Aggregate points and surfaces in 4D
2 participants