MathOnco / valis

Virtual Alignment of pathoLogy Image Series
https://valis.readthedocs.io/en/latest/
MIT License
117 stars 28 forks source link

Non-Rigid Registration Error #141

Open ewestlundicr opened 2 months ago

ewestlundicr commented 2 months ago

Hi,

I have a set of 20 czi-images to align and merge with Valis. The majority have been working great, but in 6 of them I get the following error when I reach the non-rigid alignment step.



Preparing images for non-rigid registration:   0%|          | 0/2 [00:00<?, ?image/s]
[c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\valtils.py:25](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:25): UserWarning: 'NoneType' object has no attribute 'pointer'
  warnings.warn(warning_msg, warning_type)
Traceback (most recent call last):
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 4624, in register
    non_rigid_registrar = self.non_rigid_register(rigid_registrar, slide_processors)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 4077, in non_rigid_register
    self.prep_images_for_large_non_rigid_registration(max_img_dim=self.max_non_rigid_registration_dim_px,
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 3850, in prep_images_for_large_non_rigid_registration
    vips_level_img = slide_obj.slide2vips(closest_img_level)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 485, in slide2vips
    vips_img = self.reader.slide2vips(level=level, series=series, xywh=xywh)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\slide_io.py", line 1300, in slide2vips
    vips_slide = pyvips.Image.arrayjoin(
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\vimage.py", line 256, in call_function
    return pyvips.Operation.call(name, *args, **kwargs)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\voperation.py", line 282, in call
    op.set(name, intro.details[name]['flags'], match_image, value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\voperation.py", line 216, in set
    super(Operation, self).set(name, value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\vobject.py", line 117, in set
    gv.set(value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\gvalue.py", line 221, in set
    gobject_lib.g_object_ref(image.pointer)
AttributeError: 'NoneType' object has no attribute 'pointer'

This is the script I've been using


from valis import registration

slide_src_dir = "../TMA Slide 12/TMA Slide 12 Scene 01" # Original files directory
results_dst_dir = "../TMA Slide 12/TMA Slide 12 Scene 01/registration" # Registration results saved here
merged_slide_dst_f = "../TMA Slide 12/TMA Slide 12 Scene 01/merged/TMA Slide 12 Scene 01.ome.tiff" # Where to save merged slide

registrar = registration.Valis(slide_src_dir, results_dst_dir)
rigid_registrar, non_rigid_registrar, error_df = registrar.register()

registrar.warp_and_merge_slides(merged_slide_dst_f, non_rigid=False)

registration.kill_jvm() # Kill the JVM

I'm using Python v3.10 and Valis v1.1.0.

If anyone has any ideas of what is causing the error, I would be very grateful.

Thank you

cdgatenbee commented 2 months ago

Hi @ewestlundicr,

Apologies for taking so long to get back to you. I haven't seen this error before, but could you let me know if the results from the rigid registration look ok? Weird things can sometimes happen when Valis tries to non-rigidly align images that have poor rigid registration. It's difficult to know if that's whats happening here, but might be a first place to check.

Best,

ewestlundicr commented 1 month ago

Hi Chandler,

Thank you very much for your response.

Here are the original and rigid overlaps, which I think looks very good.

TMA_Slide_12_Scene_01_original_overlap TMA_Slide_12_Scene_01_rigid_overlap

I'm happy to share some images that worked and didn't work in case you want to run the alignment yourself.

Best wishes

ewestlundicr commented 1 month ago

I also now realised that I might not have been clear regarding the set of 20 images to align, so just to clarify.

I have 20 regions that have been scanned two times, so for each region I have 2 images that needs to be aligned and merged.

14 out of these regions are aligning the two scans without any issues.

The remaining 6 regions are giving the above error.

cdgatenbee commented 1 month ago

Hi @ewestlundicr, It does look like the rigid registration went well, so something else must be going on. If you could, sharing a problematic image pair would be very useful in figuring out what is going wrong here. Please send a link to download them to my e-mail, Chandler[dot]Gatenbee[at]moffitt[dot].org

Best, -Chandler

cdgatenbee commented 1 month ago

Hi @ewestlundicr,

Thank you for sharing the images. Using them I was able to determine that the problem is valis was trying to access pyramid level = -1 for the images during the non-rigid registration, but the pyramid level needs to be >= 0. I've added some checks to prevent this from happening, and now the non-rigid registration seems to work really well:

images_to_merge_non_rigid_overlap

I'll include this fix in the next update, but unfortunately, I'm going to be out of town for a bit, so it may be a few weeks before it's available. In the meantime, I've come up with 3 different options to get the current version of valis (1.1.0) to work with these images.

The first approach is to use a custom ImageProcesser that slices out the regions to be aligned based solely on the DAPI stain. The default for IF images is to use preprocessing.ChannelGetter, but this uses all channels to create the mask. Usually this is fine, but if one of the channels is noisy, the mask can be much larger than region to be aligned. However, if only the DAPI channel is used, the mask will only cover the nuclei. The reason this approach should work here is because in the latest version of valis (1.1.0), this mask is used to slice out the region to be aligned during both rigid and non-rigid registration. So, slicing out only the DAPI+ regions can result in a more accurate rigid registration, and subsequently non-rigid registration. I believe the error you're experiencing is isolated to the non-rigid registration, but taking this approach manages to get around trying to access a negative pyramid level. Anyway, the code to do this is as follows:


from valis import registration, preprocessing
import numpy as np
from skimage import filters

class DAPIGetter(preprocessing.ChannelGetter):
    """Select DAPI channel from image

    """

    def __init__(self, image, src_f, level, series, *args, **kwargs):
        super().__init__(image=image, src_f=src_f, level=level,
                         series=series, *args, **kwargs)

    def create_mask(self):
        dapi_img = self.process_image()

        fg_t = filters.threshold_otsu(dapi_img)
        fg_mask = 255*(dapi_img > fg_t).astype(np.uint8)

        fg_bbox_mask = preprocessing.mask2bbox_mask(fg_mask)

        return fg_bbox_mask

registrar = registration.Valis(src_dir, dst_dir)
rigid_registrar, non_rigid_registrar, error_df = registrar.register(if_processing_cls=DAPIGetter)

Since the masks are more specific, rigid and non-rigid registration can be done at higher resolution, and the overlap for the rigid registration now looks like this

images_to_merge_rigid_overlap

And non-rigid looks very similar:

images_to_merge_non_rigid_overlap

An additional benefit of using DAPIGetter is that, at least for this image pair, the results are more accurate than when using ChannelGetter, with the error being 0.19um vs 0.28um, respectively. So, it may be worth testing if DAPIGetter works as well on your other images too.

A second way to get around this bug is to set max_non_rigid_registration_dim_px to something fairly low. This works for now because the images themselves are relatively small (approx 1500 x 1600 px), and the region being aligned is even smaller (approx 700 px). For example, the following worked for me, and produced results that were similar to when using the fixed code:

registrar = registration.Valis(src_dir, dst_dir, max_non_rigid_registration_dim_px=100)
rigid_registrar, non_rigid_registrar, error_df = registrar.register()

I also noticed that the improvement when adding non-rigid alignment was fairly small, going from an error of 0.37um with rigid registration, to 0.27um with non-rigid. So, if the other images are similar, and the above approaches don't work for all of your images, skipping non-rigid until registration until the update is available may also be an option.

Again, I'll add this fix to the next update, but please try one of the above solutions to see if it solves your issue in the meantime.

Best, -Chandler

ewestlundicr commented 1 month ago

Thank you very much for taking the time to sort out this issue.

I implemented the DAPIGetter, it worked on the first image that failed before but I got a very similar error again for the second image that worked before.

Preparing images for non-rigid registration:   0%|          | 0/2 [00:00<?, ?image/s]
[c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\valtils.py:25](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:25): UserWarning: 'NoneType' object has no attribute 'pointer'
  warnings.warn(warning_msg, warning_type)
Traceback (most recent call last):
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 4624, in register
    non_rigid_registrar = self.non_rigid_register(rigid_registrar, slide_processors)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 4077, in non_rigid_register
    self.prep_images_for_large_non_rigid_registration(max_img_dim=self.max_non_rigid_registration_dim_px,
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 3850, in prep_images_for_large_non_rigid_registration
    vips_level_img = slide_obj.slide2vips(closest_img_level)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py", line 485, in slide2vips
    vips_img = self.reader.slide2vips(level=level, series=series, xywh=xywh)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\slide_io.py", line 1300, in slide2vips
    vips_slide = pyvips.Image.arrayjoin(
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\vimage.py", line 256, in call_function
    return pyvips.Operation.call(name, *args, **kwargs)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\voperation.py", line 282, in call
    op.set(name, intro.details[name]['flags'], match_image, value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\voperation.py", line 216, in set
    super(Operation, self).set(name, value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\vobject.py", line 117, in set
    gv.set(value)
  File "c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\gvalue.py", line 221, in set
    gobject_lib.g_object_ref(image.pointer)
AttributeError: 'NoneType' object has no attribute 'pointer'

JVM has been killed. If this was due to an error, then a new Python session will need to be started

I tried the second option of setting the pixel dimension to 100, and it work for the 17 first images and then it failed again, but I can't see what the error was. I tried again with pixel dimension to 50 and got the same result. Is there a limit to how low this value should be?

How would I skip the non-rigid registration completely?

Again, thank you very much for your help.

Best wishes

ewestlundicr commented 1 month ago

Hi again,

I find another thread regarding how to skip the non-rigid registration. When initialising the registrar, I added the non_rigid_registrar_cls =None

registrar = registration.Valis(slide_src_dir, results_dst_dir, non_rigid_registrar_cls=None)

Although, I get a new error for image 18, and I just realised that it was the error I got when reducing the pixel dimensions as well.

---------------------------------------------------------------------------
Error                                     Traceback (most recent call last)
Cell In[1], [line 20](vscode-notebook-cell:?execution_count=1&line=20)
     [17](vscode-notebook-cell:?execution_count=1&line=17)     registrar = registration.Valis(slide_src_dir, results_dst_dir, non_rigid_registrar_cls=None)
     [18](vscode-notebook-cell:?execution_count=1&line=18)     rigid_registrar, non_rigid_registrar, error_df = registrar.register()
---> [20](vscode-notebook-cell:?execution_count=1&line=20)     registrar.warp_and_merge_slides(merged_slide_dst_f)
     [22](vscode-notebook-cell:?execution_count=1&line=22) registration.kill_jvm() # Kill the JVM

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\valtils.py:36, in deprecated_args.<locals>.deco.<locals>.wrapper(*args, **kwargs)
     [33](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:33) @functools.wraps(f)
     [34](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:34) def wrapper(*args, **kwargs):
     [35](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:35)     rename_kwargs(f.__name__, kwargs, aliases)
---> [36](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:36)     return f(*args, **kwargs)

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py:5148, in Valis.warp_and_merge_slides(self, dst_f, level, non_rigid, crop, channel_name_dict, src_f_list, colormap, drop_duplicates, tile_wh, interp_method, compression, Q, pyramid)
   [5145](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5145) slide_name = valtils.get_name(os.path.split(f)[1])
   [5146](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5146) slide_obj = self.slide_dict[slide_name]
-> [5148](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5148) warped_slide = slide_obj.warp_slide(level, non_rigid=non_rigid,
   [5149](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5149)                                     crop=crop,
   [5150](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5150)                                     interp_method=interp_method)
   [5152](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5152) keep_idx = list(range(warped_slide.bands))
   [5153](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:5153) slide_channel_names = channel_name_dict_by_name[slide_obj.name]

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\valtils.py:36, in deprecated_args.<locals>.deco.<locals>.wrapper(*args, **kwargs)
     [33](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:33) @functools.wraps(f)
     [34](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:34) def wrapper(*args, **kwargs):
     [35](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:35)     rename_kwargs(f.__name__, kwargs, aliases)
---> [36](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/valtils.py:36)     return f(*args, **kwargs)

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\registration.py:980, in Slide.warp_slide(self, level, non_rigid, crop, src_f, interp_method, reader)
    [977](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:977) if reader is None:
    [978](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:978)     reader = self.reader
--> [980](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:980) warped_slide = slide_tools.warp_slide(src_f, M=self.M,
    [981](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:981)                                       transformation_src_shape_rc=self.processed_img_shape_rc,
    [982](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:982)                                       transformation_dst_shape_rc=self.reg_img_shape_rc,
    [983](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:983)                                       aligned_slide_shape_rc=aligned_slide_shape,
    [984](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:984)                                       dxdy=bk_dxdy, level=level, series=self.series,
    [985](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:985)                                       interp_method=interp_method,
    [986](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:986)                                       bbox_xywh=slide_bbox_xywh,
    [987](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:987)                                       bg_color=bg_color,
    [988](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:988)                                       reader=reader)
    [989](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/registration.py:989) return warped_slide

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\slide_tools.py:346, in warp_slide(src_f, transformation_src_shape_rc, transformation_dst_shape_rc, aligned_slide_shape_rc, M, dxdy, level, series, interp_method, bbox_xywh, bg_color, reader)
    [343](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:343) if M is None and dxdy is None:
    [344](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:344)     return vips_slide
--> [346](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:346) vips_warped = warp_tools.warp_img(img=vips_slide, M=M, bk_dxdy=dxdy,
    [347](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:347)                                   transformation_dst_shape_rc=transformation_dst_shape_rc,
    [348](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:348)                                   out_shape_rc=aligned_slide_shape_rc,
    [349](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:349)                                   transformation_src_shape_rc=transformation_src_shape_rc,
    [350](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:350)                                   bbox_xywh=bbox_xywh,
    [351](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:351)                                   bg_color=bg_color,
    [352](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:352)                                   interp_method=interp_method)
    [354](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/slide_tools.py:354) return vips_warped

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\valis\warp_tools.py:1103, in warp_img(img, M, bk_dxdy, out_shape_rc, transformation_src_shape_rc, transformation_dst_shape_rc, bbox_xywh, bg_color, interp_method)
   [1100](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/warp_tools.py:1100)     warped = affine_warped
   [1102](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/warp_tools.py:1102) if bbox_xywh is not None:
-> [1103](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/warp_tools.py:1103)     warped = warped.extract_area(*bbox_xywh)
   [1105](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/warp_tools.py:1105) if is_array:
   [1106](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/valis/warp_tools.py:1106)     warped = vips2numpy(warped)

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\vimage.py:1354, in Image.__getattr__.<locals>.call_function(*args, **kwargs)
   [1352](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/vimage.py:1352) @_add_doc(name)
   [1353](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/vimage.py:1353) def call_function(*args, **kwargs):
-> [1354](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/vimage.py:1354)     return pyvips.Operation.call(name, self, *args, **kwargs)

File c:\Users\ewestlund\Documents\Python\Nicole_valis_merge\valis_merge_venv\lib\site-packages\pyvips\voperation.py:305, in Operation.call(operation_name, *args, **kwargs)
    [303](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/voperation.py:303) if vop == ffi.NULL:
    [304](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/voperation.py:304)     vips_lib.vips_object_unref_outputs(op.vobject)
--> [305](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/voperation.py:305)     raise Error('unable to call {0}'.format(operation_name))
    [306](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/voperation.py:306) op = Operation(vop)
    [308](file:///C:/Users/ewestlund/Documents/Python/Nicole_valis_merge/valis_merge_venv/lib/site-packages/pyvips/voperation.py:308) # attach all input refs to output x

Error: unable to call extract_area
  extract_area: bad extract area

Would this have something to do with the image, rather than Valis?

I had a look at the overlaps and it looks okay.

Rigid: TMA_Slide_12_Scene_18_rigid_overlap

Non-igid: TMA_Slide_12_Scene_18_non_rigid_overlap

All other images look great and I don't get any errors.

Best wishes

cdgatenbee commented 1 month ago

Hi @ewestlundicr,

Sorry you keep running into all these issues, and thank you for your patience. Unfortunately, the error message in your last comment doesn't have enough info to determine if the error is due to valis or the image. But, if I had to guess, I'd imagine it's probably due to a bug in valis. Could you share the problematic image pair?

On a separate note, I realized that the original issue can be resolved by putting a 1 line bug fix in a subclassed BioFormatsSlideReader, which can be passed to the reader_cls argument of Valis.register. Using the following code should hopefully get around the "AttributeError: 'NoneType' object has no attribute 'pointer'", provide better results using the DAPIGetter image processor, and hopefully avoid this most recent error (fingers crossed)


class DAPIGetter(preprocessing.ChannelGetter):
    """Select DAPI channel from image

    """

    def __init__(self, image, src_f, level, series, *args, **kwargs):
        super().__init__(image=image, src_f=src_f, level=level,
                         series=series, *args, **kwargs)

    def create_mask(self):
        dapi_img = self.process_image()

        fg_t = filters.threshold_otsu(dapi_img)
        fg_mask = 255*(dapi_img > fg_t).astype(np.uint8)

        fg_bbox_mask = preprocessing.mask2bbox_mask(fg_mask)

        return fg_bbox_mask

class FixedBioFormatsSlideReader(slide_io.BioFormatsSlideReader):
    """BioFormats reader that sets negative pyramid level to 0

    """
    def __init__(self, src_f, series=None, *args, **kwargs):
        super().__init__(src_f=src_f, series=series, *args, **kwargs)

    def slide2vips(self, level, series=None, xywh=None, tile_wh=None, z=0, t=0, *args, **kwargs):
        level = max(0, level)
        return super().slide2vips(level=level, series=series, xywh=xywh, tile_wh=tile_wh, z=z, t=t, *args, **kwargs)

    def slide2image(self, level, series=None, xywh=None, tile_wh=None, z=0, t=0, *args, **kwargs):
        level = max(0, level)
        return super().slide2image(level=level, series=series, xywh=xywh, tile_wh=tile_wh, z=z, t=t, *args, **kwargs)

registrar = registration.Valis(src_dir, dst_dir)
rigid_registrar, non_rigid_registrar, error_df = registrar.register(if_processing_cls=DAPIGetter, reader_cls=FixedBioFormatsSlideReader)

Please try that out and let me know how it goes.

Best, -Chandler

ewestlundicr commented 1 month ago

Thank you very much.

Stupid question, but I get that slide_io is not defined.

I assume that I have to import something and I have tried to figure out what but have not been successful.

Best wishes

cdgatenbee commented 1 month ago

Hi @ewestlundicr, Sorry for taking so long to respond, but I had been on vacation and had limited internet access. Anyhow, slide_io is a valis module, and so just needs to be imported, i.e. just add from valis import slide_io in the import section of your script.

Best, -Chandler

ewestlundicr commented 1 month ago

Thank you very much! It seems to work now :D