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

Errors when final frame is incomplete #80

Open
DrLachie opened this issue Sep 2, 2024 · 11 comments · May be fixed by #82
Open

Errors when final frame is incomplete #80

DrLachie opened this issue Sep 2, 2024 · 11 comments · May be fixed by #82

Comments

@DrLachie
Copy link
Contributor

DrLachie commented Sep 2, 2024

Often (relatively) a user will stop an acquisition mid frame, this results in the final timepoing being incomplete.
If we draw rois and are decvonvolving (I haven't tested just cropping yet) we get three cases.

  1. Final roi frame is filled with signal - decon works
  2. Final roi frame is empty - fails with ValueError here:
  3. Final roi frame partially filled - fails with assertion error here:

Screenshot 2024-09-02 113714

I think we have a few options for dealing with this:

  1. Check the final frame is complete, not sure how, maybe check for 0s?
  2. Attempt to process the roi as normal, if it fails in one of these two ways then simply skip it and do final timepoint -1
  3. Allow time-range to have negative values (ie I can do from frame 50 to frame -1 and skip the last frame manually)
  4. Default to always ignoring the last frame

I think my preference would be number 2

@multimeric
Copy link
Collaborator

Thanks, this is a helpful description.

Pradeep's existing code for checking this is in the repo here:

def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int):
"""
Checks for a slice with incomplete data, caused by incomplete acquisition
"""
import numpy as np
if not isinstance(volume, DaskArray):
return volume
orig_shape = volume.shape
raw_vol = volume.compute()
if raw_vol.shape != orig_shape:
logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}")
z_diff, y_diff, x_diff = np.subtract(orig_shape, raw_vol.shape)
logger.info(f"Padding with{z_diff,y_diff,x_diff}")
raw_vol = np.pad(raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff)))
if raw_vol.shape != orig_shape:
raise Exception(f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}")
return raw_vol

However, I need some guidance on when to use it. Conceptually it would be great as a pydantic validation, but that isn't a good idea if it's compute intensive. If it is, then maybe it could become part of the .compute() method.

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 2, 2024

Ahh, I think there is a logic error in the code..
volume.shape and raw_vol.shape are the same thing.
So, its not running any padding..

I think if the latticedata has a variable that stores the shape of the first image slice (i.e, time=0 and ch=0), we can then access that here to verify.

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 2, 2024

We will have to check shape after compute though.. as its possible that as a dask array it may store a different shape.

@multimeric
Copy link
Collaborator

Well after the other validators have run, we're guaranteed to have it as an xarray. I think I can pull out the first and last slice without loading anything else from disk. It depends how "first" and "last" are defined though: which channel, for instance?

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 2, 2024

Actually, is this function (check_incomplete_acquisition) being accessed anywhere?

It depends how "first" and "last" are defined though: which channel, for instance?

first would be time 0 and channel 0

last could be max timepoint and max channel for now.

@multimeric
Copy link
Collaborator

No, the function is currently unused.

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 2, 2024

Ahh, ok.

So, we need to run this when processing

  • last timepoint (max T), and
  • all channels within this timepoint.

Essentially, this function will

  • check if the image is incomplete by verifying the computed image shape (last timepoint) with the original image shape.
  • If so, it will pad it to match original image shape.

Is it easy to implement this only for last timepoint in save_image functions?
OR
would it be easier and computationally inexpensive to run this for all timepoints/channels, i.e., compare the shape of each array to original image. We compute the final array anyway in the save image functions.

@DrLachie
Copy link
Contributor Author

DrLachie commented Sep 2, 2024

Correct me if I'm wrong but that function computes the entire raw (non-deskewed/processed) volume? I just gave this a shot and it's computationally expensive. A simpler check is just to attempt to compute the final timepoint of the raw, this raises a value error if it's an incomplete acquisition.

My proposal is to run this test when first accessing the image (which I believe happens in lls_core.models.deskew.read_image, if the value error is raised, then log an error and redefine the raw data omitting the final timepoint. Testing this now

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 3, 2024

Correct me if I'm wrong but that function computes the entire raw (non-deskewed/processed) volume? I just gave this a shot and it's computationally expensive. A simpler check is just to attempt to compute the final timepoint of the raw, this raises a value error if it's an incomplete acquisition.

You are right it does, but I'm thinking we put this function while processing the data, i.e., when running the saving/workflow processing..

  • when saving, we compute each array anyway.
  • we pass this array to the following function, like:
     def check_incomplete_acquisition(self, raw_volume: ArrayLike, original_shape: tuple, time_point: int, channel: int): 
         """ 
         Checks for a slice with incomplete data, caused by incomplete acquisition 
         """ 
         if raw_volume.shape != original_shape: 
             logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {original_shape}, got {raw_volume.shape}") 
             z_diff, y_diff, x_diff = np.subtract(original_shape, raw_volume.shape) 
             logger.info(f"Padding with{z_diff,y_diff,x_diff}") 
             raw_volume= np.pad(raw_volume, ((0, z_diff), (0, y_diff), (0, x_diff))) 
             if raw_volume.shape != original_shape: 
                 raise Exception(f"Shape of last timepoint still doesn't match. Got { raw_volume.shape}") 
             return  raw_volume

I don't think this will be computationaly expensive as its just checking the shape. The added advantage is if there are any other acquisition issues in any frame (not only last timepoint), we can catch that as well.


Your idea works too, but we are omitting the last frame from processing as a solution.

@DrLachie
Copy link
Contributor Author

DrLachie commented Sep 3, 2024

I think it's unlikely there's an issue (of this type) in anything but the last frame, and if there is it's probably a more pressing one that should be dealt with differently.

I like the idea of running the test up front as it at least throws the error/warning immediately. The problem I was having with debugging is everything looked like it was working unitl the final timepoint and then failed.

My inelegant (but working) solution is here:

#check if final frame is complte. If not, get rid of it

@pr4deepr
Copy link
Collaborator

pr4deepr commented Sep 3, 2024

Could you create a pull request for this?

@DrLachie DrLachie linked a pull request Sep 3, 2024 that will close this issue
@multimeric multimeric linked a pull request Sep 27, 2024 that will close this issue
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 a pull request may close this issue.

3 participants