cabouman / mbircone

BSD 3-Clause "New" or "Revised" License
11 stars 9 forks source link

demo_mace3d.py generates inf values #73

Closed cabouman closed 2 years ago

cabouman commented 2 years ago

The demo script demo_mace3D.py does not run correctly. The QGGMRF part appears to generate inf values. This needs to be tracked down and fixed.

************************** Iteration 0  (max. 20) **************************
*  Cost                   = inf
*  Rel. Update            = 0.0000000000e+00 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = -nan       % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 1.0000000000e+02 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = inf        = 1/0.0000000000
*  weightScaler_value     = 3.5543020203e-05 = 1/28134.9199218750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 0.0000000000e+00
*  time icd update        = -1.3449177146e-01 s
*  ratioUpdated           = 0.0000000000e+00 %
*  totalEquits            = 0.0000000000e+00
******************************************************************************

************************** Iteration 1  (max. 20) **************************
*  Cost                   = inf
*  Rel. Update            = 1.0000000000e+02 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = -nan       % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 7.1919616699e+01 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = inf        = 1/0.0000000000
*  weightScaler_value     = 3.5543020203e-05 = 1/28134.9199218750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 9.7759289062e+04
*  time icd update        = 7.2860288620e+00 s
*  ratioUpdated           = 1.0000000000e+02 %
*  totalEquits            = 1.0000000000e+00
******************************************************************************

************************** Iteration 2  (max. 20) **************************
*  Cost                   = inf
*  Rel. Update            = 6.1694126129e+01 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = -nan       % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 6.6280868530e+01 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = inf        = 1/0.0000000000
*  weightScaler_value     = 3.5543020203e-05 = 1/28134.9199218750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 9.7759289062e+04
*  time icd update        = 6.7889585495e+00 s
*  ratioUpdated           = 1.0000000000e+02 %
*  totalEquits            = 2.0000000000e+00
******************************************************************************

************************** Iteration 3  (max. 20) **************************
*  Cost                   = inf
*  Rel. Update            = 3.0164772034e+01 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = -nan       % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 6.5200088501e+01 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = inf        = 1/0.0000000000
*  weightScaler_value     = 3.5543020203e-05 = 1/28134.9199218750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 1.1405250000e+05
*  time icd update        = 6.3575387001e+00 s
*  ratioUpdated           = 1.0000000000e+02 %
*  totalEquits            = 3.0000000000e+00
******************************************************************************
dyang37 commented 2 years ago

Ok I think I found out the cause of the nan problem. It is because of the sinogram weight. The way demo worked is that we added transmission noise to the synthetic sinogram, and perform recon with transmission weight type calculated from noisy sinogram:

sino = mbircone.cone3D.project(phantom, angles,
                               num_det_rows, num_det_channels,
                               dist_source_detector, magnification)
sino_weights = mbircone.cone3D.calc_weights(sino, weight_type='transmission')
sino_noise_sigma = 0.01      # transmission noise level
noise = sino_noise_sigma * 1. / np.sqrt(sino_weights) * np.random.normal(size=(num_views, num_det_rows, num_det_channels))
sino_noisy = sino + noise
recon_qGGMRF = mbircone.cone3D.recon(sino_noisy, angles, dist_source_detector, magnification,
                                     sharpness=sharpness, weight_type='unweighted',
                                     verbose=1)

It appears that the transmission weight calculated from noisy sinogram has some extreme values. The maximum values of the calculated weight is 7.845908754559244e+96, and that caused the nan problem we saw above. (Before we changed the detector pixel pitch, the max weights value was 0.9602327231700871) As a sanity check, I simply clipped the maximum value of weights to be 10.0, and the nan problem goes away:

************************** Iteration 0  (max. 20) **************************
*  Cost                   = 2.4964202496e+11
*  Rel. Update            = 0.0000000000e+00 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = 1.0000000000e+02 % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 1.0000000000e+02 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = 3.5201702118e+01 = 1/0.0284077171
*  weightScaler_value     = 3.5534198105e-05 = 1/28141.9042968750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 0.0000000000e+00
*  time icd update        = -3.8534309715e-02 s
*  ratioUpdated           = 0.0000000000e+00 %
*  totalEquits            = 0.0000000000e+00
******************************************************************************

************************** Iteration 1  (max. 20) **************************
*  Cost                   = 2.4892886221e+11
*  Rel. Update            = 1.0000000000e+02 % (threshold = 1.9999999553e-02 %)
*  RWFE = ||e||_W/||y||_W = 9.9853851318e+01 % (threshold = 0.0000000000e+00 %)
*  RUFE = ||e|| / ||y||   = 7.2232589722e+01 % (threshold = 0.0000000000e+00 %)
* ----------------------------------------------------------------------------
*  1/M ||e||^2_W          = 3.5098880768e+01 = 1/0.0284909364
*  weightScaler_value     = 3.5534198105e-05 = 1/28141.9042968750
* ----------------------------------------------------------------------------
*  voxelsPerSecond        = 6.9297718750e+04
*  time icd update        = 9.8886108398e+00 s
*  ratioUpdated           = 1.0000000000e+02 %
*  totalEquits            = 1.0000000000e+00
*****************************************************************************

Potential approach to fix the issue: Previously the transmission noise level is hard coded to be sino_noise_sigma = 0.01, and transmission noise is calculated as

noise = sino_noise_sigma * 1. / np.sqrt(sino_weights) * np.random.normal(size=(num_views, num_det_rows, num_det_channels))

This noise should be scaled w.r.t. the detector pixel pitch as well, since by changing detector pixel pitch from 0.25 to 1, the sinogram value is also scaled up:

Below is the clean synthetic sinogram using pixel pitch = 0.25 clean_sinogram_view0

Below is the clean synthetic sinogram u clean_sinogram_view0 sing pixel pitch = 1.0

As a result, the sino_noise_sigma value should also be scaled down by 4x, so that the sinogram noise level noise = sino_noise_sigma * 1. / np.sqrt(sino_weights) * np.random.normal(size=(num_views, num_det_rows, num_det_channels)) stays the same.

dyang37 commented 2 years ago

Closing this since we removed transmission noise from MACE demo.