InsightSoftwareConsortium / ITKElastix

An ITK Python interface to elastix, a toolbox for rigid and nonrigid registration of images
Apache License 2.0
192 stars 23 forks source link

Encountering segmentation fault when running `elastix_registration_method()` on both 2D and 3D images #295

Open tieneupin opened 3 weeks ago

tieneupin commented 3 weeks ago

Hi there, I have been introduced to Elastix as a possible solution for some of my image alignment needs, and have been running into the aforementioned segmentation fault issue when I try implementing multi-threading on a 3D job.

Using the example elastix data provided, this is the test code that I've written that runs into that segmentation fault:

import numpy as np

from itk import imread
from itk.itkElastixRegistrationMethodPython import elastix_registration_method
from pathlib import Path
from tifffile import imwrite

# Load images
file_static = Path("/tutorials/elastix-examples/data/CT_3D_lung_fixed.mha")
img_static = np.asarray(imread(file_static))
dtype = str(img_static.dtype)
print(f"Using {file_static.name!r} as the reference image")

file_moving = Path("/tutorials/elastix-examples/data/CT_3D_lung_moving.mha")
img_moving = np.asarray(imread(file_moving))

# Register image while implementing multithreading
img_aligned, params = elastix_registration_method(
    img_static, img_moving,
    number_of_threads=2,
    log_to_console=False,
    )  # <-- CODE FAILS AT THIS STEP

I am running this on a Linux RHEL8 system using python v3.10.14 and itk-elastix v0.20.0

Any insight into what went wrong would be much appreciated!

thewtex commented 2 weeks ago

Hi, what version of Linux are you using?

tieneupin commented 2 weeks ago

Hi, I'm running it on RHEL8. Have updated my question to reflect this.

thewtex commented 2 weeks ago

Something happened with our manylinux2014 packages (for older glibc). Are you able to reproduce the issue on a newer Linux that has glibc 2.28 or newer? Perhaps it is easy to do a quick test in a Docker image?

tieneupin commented 1 week ago

Thanks for getting back to me, @thewtex ! Unfortunately, I do not have the know-how at present to set up and run a Docker image of a different Linux distribution. However, if you could keep in mind this issue as encountered on RHEL8 and inform me of a solution in due time, that would be much appreciated!

tieneupin commented 1 week ago

Hi @thewtex , just an update to this matter: I have opted to perform the image analysis in two dimensions on a per-slice basis, and I surprisingly still run into this segmentation fault issue. Same Linux OS (RHEL8) and ITK-Elastix version (0.20.0).

This was the relevant bit of code I was running:

import numpy as np

from itk.elxParameterObjectPython import elastixParameterObject
from itk.itkElastixRegistrationMethodPython import elastix_registration_method
from tifffile import TiffFile

def run():
    # Load TIFF files
    print("Loading TIFF files")
    ref_tiff = TiffFile("reference_image.tiff")
    mov_tiff = TiffFile("moving_image.tiff")
    # These are ImageJ-compatible TIFF images of shape (85, 2048, 2048)

    # Load metadata
    ref_series = ref_tiff.series[0]
    mov_series = mov_tiff.series[0]
    num_frames = mov_series.shape[0]  # slices in stack

    # Set up parameter object
    print("Setting up initial registration parameters")
    params_init: elastixParameterObject = elastixParameterObject.New()
    params_map = params_init.GetDefaultParameterMap(
        # Uncomment as needed
        "rigid",  # Translation + rotation
        # "affine",  # Translation + rotation + scaling + shearing
        numberOfResolutions=1,
    )
    params_init.AddParameterMap(params_map)

    # Align in xy plane
    for f in range(num_frames):
        ref_img = ref_series.pages[f].asarray()
        if invert_reference is True:
            ref_img = (2**bit_depth - 1) - ref_img  # Order of subtraction is important
        mov_img = mov_series.pages[f].asarray()

        print(f"Aligning frame {f}")
        reg_img, params_reg = elastix_registration_method(
            ref_img, mov_img,
            parameter_object=params_init,
            log_to_console=False,
            number_of_threads=1,
        )
        # Convert to NumPy array
        reg_img = np.asarray(reg_img)

        # Append to image stack
        if f == 0:
            reg_stk = [reg_img]
        else:
            reg_stk = np.append(
                reg_stk,
                [reg_img],
                axis=0,
            )

if __name__ == "__main__":
    run()

The segmentation fault appears to occur randomly at different points when iterating through the stack. It also occurs even when the number_of_threads parameter is set to 1. It looks like this is not just an issue with the dimensionality, but with the program trying to (to the best of my current limited computing knowledge) access forbidden bits of the memory in the process of trying to align the image.

Would appreciate your comments on this matter when you have the time. Thanks!

thewtex commented 1 week ago

@tieneupin thanks for the report and script.

It may be worth trying itkwasm-elastix.