BioimageAnalysisCoreWEHI / napari_lattice

Napari plugin for custom analysis and visualization of lattice lightsheet and Oblique Plane Microscopy data. The plugin is optimized for data from the Zeiss lattice lightsheet microscope.
https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/wiki
GNU General Public License v3.0
12 stars 4 forks source link

Errors when final frame is incomplete #80

Open DrLachie opened 2 weeks ago

DrLachie commented 2 weeks ago

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: https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/blob/f57b4895fe0479e4fb35cd78a68484dd24445c38/core/lls_core/deconvolution.py#L145 3) Final roi frame partially filled - fails with assertion error here: https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/blob/f57b4895fe0479e4fb35cd78a68484dd24445c38/core/lls_core/deconvolution.py#L210

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 commented 2 weeks ago

Thanks, this is a helpful description.

Pradeep's existing code for checking this is in the repo here: https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/blob/f2c4824e38880b964de3312bcefe658525acea01/core/lls_core/models/lattice_data.py#L332-L348

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 commented 2 weeks ago

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 commented 2 weeks ago

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

multimeric commented 2 weeks ago

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 commented 2 weeks ago

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 commented 2 weeks ago

No, the function is currently unused.

pr4deepr commented 2 weeks ago

Ahh, ok.

So, we need to run this when processing

Essentially, this function will

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 commented 1 week ago

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 commented 1 week ago

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..

     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 commented 1 week ago

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: https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/blob/317eb5a91ac30cf21b09a40d3e94bae5416b3c12/core/lls_core/models/deskew.py#L196

pr4deepr commented 1 week ago

Could you create a pull request for this?