LLNL / LEAP

comprehensive library of 3D transmission Computed Tomography (CT) algorithms with Python API and fully integrated with PyTorch
https://leapct.readthedocs.io
MIT License
74 stars 9 forks source link

Different results on CPU and GPU #3

Closed henrfr closed 7 months ago

henrfr commented 8 months ago

Hello! I have tested the code and observed that I get different results when forward projecting on the CPU vs the GPU. See the image below. Is this expected behavior?

test_diff

Code to reproduce:

import torch
from leaptorch import Projector

def main():
    device = torch.device("cuda:0")

    proj = Projector(use_gpu=True, gpu_device=device)

    dimx = 128
    dimy = 128
    dimz = 64
    offsetx = 0
    offsety = 0
    offsetz = 0
    nangles = 180
    nrows = dimz
    ncols = max(dimx, dimy)
    sdd = 1350
    sod = 930
    pheight = sdd/sod
    pwidth = sdd/sod
    width = 1 
    height = 1 
    arange = 180
    crow = 0.5*float(nrows - 1)
    ccol = 0.5*float(ncols - 1)
    phis = torch.arange(0, arange, arange//nangles).float()

    proj.set_volume(dimx=dimx,
                    dimy=dimy,
                    dimz=dimz,
                    width=width,
                    height=height,
                    offsetx=offsetx,
                    offsety=offsety,
                    offsetz=offsetz)

    proj.set_cone_beam(nangles=nangles,
                    nrows=nrows,
                    ncols=ncols,
                    pheight=pheight,
                    pwidth=pwidth,
                    crow=crow,
                    ccol=ccol,
                    arange=arange,
                    phis=phis,
                    sod=sod,
                    sdd=sdd)

    volume = torch.ones((1,64,128,128)).float().to(device)
    volume[..., 40:80, 40:80] = 3.0  # Create a cuboid

    sinogram_gpu = proj(volume)

    device = torch.device("cpu")
    proj_cpu = Projector(use_gpu=False)
    proj_cpu.set_volume(dimx=dimx,
                    dimy=dimy,
                    dimz=dimz,
                    width=width,
                    height=height,
                    offsetx=offsetx,
                    offsety=offsety,
                    offsetz=offsetz)

    proj_cpu.set_cone_beam(nangles=nangles,
                    nrows=nrows,
                    ncols=ncols,
                    pheight=pheight,
                    pwidth=pwidth,
                    crow=crow,
                    ccol=ccol,
                    arange=arange,
                    phis=phis,
                    sod=sod,
                    sdd=sdd)

    sinogram_cpu = proj_cpu(volume.to(device))
    import matplotlib.pyplot as plt

    fig, axs = plt.subplots(1,2)
    axs[0].imshow(sinogram_gpu[0,:,32,:].detach().cpu().numpy())
    axs[0].set_title("GPU")
    axs[1].imshow(sinogram_cpu[0,:,32,:].numpy())
    axs[1].set_title("CPU")
    fig.show()
    fig.savefig("test_diff.jpg")
if __name__ == "__main__":
    main()
kylechampley commented 8 months ago

Hi Henrik,

Thanks for bringing this to our attention. This is definitely a bug in our code. LEAP is designed so that there are no differences between running the CPU versus GPU implementations of these algorithms.

I don't know if you noticed some of the recent activity on this repo (in other branches), but Hyojin and I are working on resolving several bugs/ issues, and feature improvements to LEAP. We hope to have a new release in the next couple of days. Just wait for a change to the main branch. Thanks for your patience.

~Kyle

kylechampley commented 7 months ago

This issue has been resolved and the changes commited to the main branch.

There were basically two issues here. The first and main issue was that I changed the API in the C code, but forgot to change it in the Python code. One was not supposed to give the "arange" parameter to the set_cone_beam function.

The second issue was some discrepancies between between the CPU and GPU projectors. These have all been resolved now and CPU and GPU operations should agree to within five or six digits of accuracy.