httomolibgpu 3.1.1__py3-none-any.whl → 5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ---------------------------------------------------------------------------
4
+ # Copyright 2025 Diamond Light Source Ltd.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ---------------------------------------------------------------------------
18
+ # Created By : Tomography Team at DLS <scientificsoftware@diamond.ac.uk>
19
+ # Created Date: 2 December 2025
20
+ # ---------------------------------------------------------------------------
21
+
22
+ # SPDX-FileCopyrightText: 2009-2022 the scikit-image team
23
+ # SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
24
+ # SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
25
+
26
+ """
27
+ Port of Manuel Guizar's code from:
28
+ http://www.mathworks.com/matlabcentral/fileexchange/18401-efficient-subpixel-image-registration-by-cross-correlation
29
+ """
30
+
31
+ import itertools
32
+ import math
33
+ import warnings
34
+
35
+ import cupy as cp
36
+ import cupyx.scipy.ndimage as ndi
37
+ import numpy as np
38
+
39
+ def _upsampled_dft(
40
+ data, upsampled_region_size, upsample_factor=1, axis_offsets=None
41
+ ):
42
+ """
43
+ Upsampled DFT by matrix multiplication.
44
+
45
+ This code is intended to provide the same result as if the following
46
+ operations were performed:
47
+ - Embed the array "data" in an array that is ``upsample_factor`` times
48
+ larger in each dimension. ifftshift to bring the center of the
49
+ image to (1,1).
50
+ - Take the FFT of the larger array.
51
+ - Extract an ``[upsampled_region_size]`` region of the result, starting
52
+ with the ``[axis_offsets+1]`` element.
53
+
54
+ It achieves this result by computing the DFT in the output array without
55
+ the need to zeropad. Much faster and memory efficient than the zero-padded
56
+ FFT approach if ``upsampled_region_size`` is much smaller than
57
+ ``data.size * upsample_factor``.
58
+
59
+ Parameters
60
+ ----------
61
+ data : array
62
+ The input data array (DFT of original data) to upsample.
63
+ upsampled_region_size : integer or tuple of integers, optional
64
+ The size of the region to be sampled. If one integer is provided, it
65
+ is duplicated up to the dimensionality of ``data``.
66
+ upsample_factor : integer, optional
67
+ The upsampling factor. Defaults to 1.
68
+ axis_offsets : tuple of integers, optional
69
+ The offsets of the region to be sampled. Defaults to None (uses
70
+ image center)
71
+
72
+ Returns
73
+ -------
74
+ output : ndarray
75
+ The upsampled DFT of the specified region.
76
+ """
77
+ # if people pass in an integer, expand it to a list of equal-sized sections
78
+ if not hasattr(upsampled_region_size, "__iter__"):
79
+ upsampled_region_size = [upsampled_region_size] * data.ndim
80
+ else:
81
+ if len(upsampled_region_size) != data.ndim:
82
+ raise ValueError(
83
+ "shape of upsampled region sizes must be equal "
84
+ "to input data's number of dimensions."
85
+ )
86
+
87
+ if axis_offsets is None:
88
+ axis_offsets = [0] * data.ndim
89
+ else:
90
+ if len(axis_offsets) != data.ndim:
91
+ raise ValueError(
92
+ "number of axis offsets must be equal to input "
93
+ "data's number of dimensions."
94
+ )
95
+
96
+ im2pi = 1j * 2 * np.pi
97
+
98
+ dim_properties = list(zip(data.shape, upsampled_region_size, axis_offsets))
99
+
100
+ for n_items, ups_size, ax_offset in dim_properties[::-1]:
101
+ kernel = (cp.arange(ups_size) - ax_offset)[:, None] * cp.fft.fftfreq(
102
+ n_items, upsample_factor
103
+ )
104
+ kernel = cp.exp(-im2pi * kernel)
105
+ # CuPy Backend: use kernel of same precision as the data
106
+ kernel = kernel.astype(data.dtype, copy=False)
107
+
108
+ # Equivalent to:
109
+ # data[i, j, k] = kernel[i, :] @ data[j, k].T
110
+ data = cp.tensordot(kernel, data, axes=(1, -1))
111
+ return data
112
+
113
+
114
+ def _compute_phasediff(cross_correlation_max):
115
+ """
116
+ Compute global phase difference between the two images (should be
117
+ zero if images are non-negative).
118
+
119
+ Parameters
120
+ ----------
121
+ cross_correlation_max : complex
122
+ The complex value of the cross correlation at its maximum point.
123
+ """
124
+ return cp.arctan2(cross_correlation_max.imag, cross_correlation_max.real)
125
+
126
+
127
+ def _compute_error(cross_correlation_max, src_amp, target_amp):
128
+ """
129
+ Compute RMS error metric between ``src_image`` and ``target_image``.
130
+
131
+ Parameters
132
+ ----------
133
+ cross_correlation_max : complex
134
+ The complex value of the cross correlation at its maximum point.
135
+ src_amp : float
136
+ The normalized average image intensity of the source image
137
+ target_amp : float
138
+ The normalized average image intensity of the target image
139
+ """
140
+ amp = src_amp * target_amp
141
+ if amp == 0:
142
+ warnings.warn(
143
+ "Could not determine RMS error between images with the normalized "
144
+ f"average intensities {src_amp!r} and {target_amp!r}. Either the "
145
+ "reference or moving image may be empty.",
146
+ UserWarning,
147
+ stacklevel=3,
148
+ )
149
+
150
+ with np.errstate(invalid="ignore"):
151
+ error = 1.0 - cross_correlation_max * cross_correlation_max.conj() / (
152
+ amp
153
+ )
154
+
155
+ return cp.sqrt(cp.abs(error))
156
+
157
+
158
+ def _disambiguate_shift(reference_image, moving_image, shift):
159
+ """Determine the correct real-space shift based on periodic shift.
160
+
161
+ When determining a translation shift from phase cross-correlation in
162
+ Fourier space, the shift is only correct to within a period of the image
163
+ size along each axis, resulting in $2^n$ possible shifts, where $n$ is the
164
+ number of dimensions of the image. This function checks the
165
+ cross-correlation in real space for each of those shifts, and returns the
166
+ one with the highest cross-correlation.
167
+
168
+ The strategy we use is to perform the shift on the moving image *using the
169
+ 'grid-wrap' mode* in `scipy.ndimage`. The moving image's original borders
170
+ then define $2^n$ quadrants, which we cross-correlate with the reference
171
+ image in turn using slicing. The entire operation is thus $O(2^n + m)$,
172
+ where $m$ is the number of pixels in the image (and typically dominates).
173
+
174
+ Parameters
175
+ ----------
176
+ reference_image : numpy array
177
+ The reference (non-moving) image.
178
+ moving_image : numpy array
179
+ The moving image: applying the shift to this image overlays it on the
180
+ reference image. Must be the same shape as the reference image.
181
+ shift : tuple of float
182
+ The shift to apply to each axis of the moving image, *modulo* image
183
+ size. The length of ``shift`` must be equal to ``moving_image.ndim``.
184
+
185
+ Returns
186
+ -------
187
+ real_shift : tuple of float
188
+ The shift disambiguated in real space.
189
+ """
190
+ shape = reference_image.shape
191
+ positive_shift = [shift_i % s for shift_i, s in zip(shift, shape)]
192
+ negative_shift = [shift_i - s for shift_i, s in zip(positive_shift, shape)]
193
+ subpixel = any(s % 1 != 0 for s in shift)
194
+ interp_order = 3 if subpixel else 0
195
+ shifted = ndi.shift(
196
+ moving_image, shift, mode="grid-wrap", order=interp_order
197
+ )
198
+ indices = tuple(round(s) for s in positive_shift)
199
+ splits_per_dim = [(slice(0, i), slice(i, None)) for i in indices]
200
+ max_corr = -1.0
201
+ max_slice = None
202
+ for test_slice in itertools.product(*splits_per_dim):
203
+ reference_tile = cp.reshape(reference_image[test_slice], -1)
204
+ moving_tile = cp.reshape(shifted[test_slice], -1)
205
+ corr = -1.0
206
+ if reference_tile.size > 2:
207
+ corr = float(cp.corrcoef(reference_tile, moving_tile)[0, 1])
208
+ if corr > max_corr:
209
+ max_corr = corr
210
+ max_slice = test_slice
211
+ if max_slice is None:
212
+ warnings.warn(
213
+ "Could not determine real-space shift for periodic shift "
214
+ f"{shift!r} as requested by `disambiguate=True` (disambiguation "
215
+ "is degenerate).",
216
+ stacklevel=3,
217
+ )
218
+ return shift
219
+ real_shift_acc = []
220
+ for sl, pos_shift, neg_shift in zip(
221
+ max_slice, positive_shift, negative_shift
222
+ ):
223
+ real_shift_acc.append(pos_shift if sl.stop is None else neg_shift)
224
+ if not subpixel:
225
+ real_shift = tuple(map(int, real_shift_acc))
226
+ else:
227
+ real_shift = tuple(real_shift_acc)
228
+ return real_shift
229
+
230
+
231
+ def phase_cross_correlation(
232
+ reference_image,
233
+ moving_image,
234
+ *,
235
+ upsample_factor=1,
236
+ space="real",
237
+ disambiguate=False,
238
+ normalization="phase",
239
+ ):
240
+ """Efficient subpixel image translation registration by cross-correlation.
241
+
242
+ This code gives the same precision as the FFT upsampled cross-correlation
243
+ in a fraction of the computation time and with reduced memory requirements.
244
+ It obtains an initial estimate of the cross-correlation peak by an FFT and
245
+ then refines the shift estimation by upsampling the DFT only in a small
246
+ neighborhood of that estimate by means of a matrix-multiply DFT [1]_.
247
+
248
+ Parameters
249
+ ----------
250
+ reference_image : array
251
+ Reference image.
252
+ moving_image : array
253
+ Image to register. Must be same dimensionality as
254
+ ``reference_image``.
255
+ upsample_factor : int, optional
256
+ Upsampling factor. Images will be registered to within
257
+ ``1 / upsample_factor`` of a pixel. For example
258
+ ``upsample_factor == 20`` means the images will be registered
259
+ within 1/20th of a pixel. Default is 1 (no upsampling).
260
+ space : string, one of "real" or "fourier", optional
261
+ Defines how the algorithm interprets input data. "real" means
262
+ data will be FFT'd to compute the correlation, while "fourier"
263
+ data will bypass FFT of input data. Case insensitive.
264
+ disambiguate : bool
265
+ The shift returned by this function is only accurate *modulo* the
266
+ image shape, due to the periodic nature of the Fourier transform. If
267
+ this parameter is set to ``True``, the *real* space cross-correlation
268
+ is computed for each possible shift, and the shift with the highest
269
+ cross-correlation within the overlapping area is returned.
270
+ normalization : {"phase", None}
271
+ The type of normalization to apply to the cross-correlation.
272
+
273
+ Returns
274
+ -------
275
+ shift : tuple
276
+ Shift vector (in pixels) required to register ``moving_image``
277
+ with ``reference_image``. Axis ordering is consistent with
278
+ the axis order of the input array.
279
+ error : float
280
+ Translation invariant normalized RMS error between
281
+ ``reference_image`` and ``moving_image``.
282
+ phasediff : float
283
+ Global phase difference between the two images (should be
284
+ zero if images are non-negative).
285
+
286
+ Notes
287
+ -----
288
+ The use of cross-correlation to estimate image translation has a long
289
+ history dating back to at least [2]_. The "phase correlation"
290
+ method (selected by ``normalization="phase"``) was first proposed in [3]_.
291
+ Publications [1]_ and [2]_ use an unnormalized cross-correlation
292
+ (``normalization=None``). Which form of normalization is better is
293
+ application-dependent. For example, the phase correlation method works
294
+ well in registering images under different illumination, but is not very
295
+ robust to noise. In a high noise scenario, the unnormalized method may be
296
+ preferable.
297
+
298
+ References
299
+ ----------
300
+ .. [1] Manuel Guizar-Sicairos, Samuel T. Thurman, and James R. Fienup,
301
+ "Efficient subpixel image registration algorithms,"
302
+ Optics Letters 33, 156-158 (2008). :DOI:`10.1364/OL.33.000156`
303
+ .. [2] P. Anuta, Spatial registration of multispectral and multitemporal
304
+ digital imagery using fast Fourier transform techniques, IEEE Trans.
305
+ Geosci. Electron., vol. 8, no. 4, pp. 353–368, Oct. 1970.
306
+ :DOI:`10.1109/TGE.1970.271435`.
307
+ .. [3] C. D. Kuglin D. C. Hines. The phase correlation image alignment
308
+ method, Proceeding of IEEE International Conference on Cybernetics
309
+ and Society, pp. 163-165, New York, NY, USA, 1975, pp. 163–165.
310
+ .. [4] James R. Fienup, "Invariant error metrics for image reconstruction"
311
+ Optics Letters 36, 8352-8357 (1997). :DOI:`10.1364/AO.36.008352`
312
+ """
313
+
314
+ # images must be the same shape
315
+ if reference_image.shape != moving_image.shape:
316
+ raise ValueError("images must be same shape")
317
+
318
+ # assume complex data is already in Fourier space
319
+ if space.lower() == "fourier":
320
+ src_freq = reference_image
321
+ target_freq = moving_image
322
+ # real data needs to be fft'd.
323
+ elif space.lower() == "real":
324
+ src_freq = cp.fft.fftn(reference_image)
325
+ target_freq = cp.fft.fftn(moving_image)
326
+ else:
327
+ raise ValueError('space argument must be "real" of "fourier"')
328
+
329
+ # Whole-pixel shift - Compute cross-correlation by an IFFT
330
+ shape = src_freq.shape
331
+ image_product = src_freq * target_freq.conj()
332
+ if normalization == "phase":
333
+ eps = cp.finfo(image_product.real.dtype).eps
334
+ image_product /= cp.maximum(cp.abs(image_product), 100 * eps)
335
+ elif normalization is not None:
336
+ raise ValueError("normalization must be either phase or None")
337
+ cross_correlation = cp.fft.ifftn(image_product)
338
+
339
+ # Locate maximum
340
+ maxima = np.unravel_index(
341
+ int(cp.argmax(cp.abs(cross_correlation))), cross_correlation.shape
342
+ )
343
+ midpoint = tuple(float(axis_size // 2) for axis_size in shape)
344
+ shift = tuple(
345
+ _max - axis_size if _max > mid else _max
346
+ for _max, mid, axis_size in zip(maxima, midpoint, shape)
347
+ )
348
+
349
+ if upsample_factor == 1:
350
+ sabs = cp.abs(src_freq)
351
+ sabs *= sabs
352
+ tabs = cp.abs(target_freq)
353
+ tabs *= tabs
354
+ src_amp = np.sum(sabs) / src_freq.size
355
+ target_amp = np.sum(tabs) / target_freq.size
356
+ CCmax = cross_correlation[maxima]
357
+ # If upsampling > 1, then refine estimate with matrix multiply DFT
358
+ else:
359
+ # Initial shift estimate in upsampled grid
360
+ # shift = cp.around(shift * upsample_factor) / upsample_factor
361
+ upsample_factor = float(upsample_factor)
362
+ shift = tuple(
363
+ round(s * upsample_factor) / upsample_factor for s in shift
364
+ )
365
+ upsampled_region_size = math.ceil(upsample_factor * 1.5)
366
+ # Center of output array at dftshift + 1
367
+ dftshift = float(upsampled_region_size // 2)
368
+ # Matrix multiply DFT around the current shift estimate
369
+ sample_region_offset = tuple(
370
+ dftshift - s * upsample_factor for s in shift
371
+ )
372
+ cross_correlation = _upsampled_dft(
373
+ image_product.conj(),
374
+ upsampled_region_size,
375
+ upsample_factor,
376
+ sample_region_offset,
377
+ ).conj()
378
+
379
+ # Locate maximum and map back to original pixel grid
380
+ maxima = np.unravel_index(
381
+ int(cp.argmax(cp.abs(cross_correlation))), cross_correlation.shape
382
+ )
383
+ CCmax = cross_correlation[maxima]
384
+
385
+ maxima = tuple(float(m) - dftshift for m in maxima)
386
+ shift = tuple(s + m / upsample_factor for s, m in zip(shift, maxima))
387
+
388
+ src_amp = cp.abs(src_freq)
389
+ src_amp *= src_amp
390
+ src_amp = cp.sum(src_amp)
391
+ target_amp = cp.abs(target_freq)
392
+ target_amp *= target_amp
393
+ target_amp = cp.sum(target_amp)
394
+
395
+ # If its only one row or column the shift along that dimension has no
396
+ # effect. We set to zero.
397
+ shift = tuple(
398
+ s if axis_size != 1 else 0 for s, axis_size in zip(shift, shape)
399
+ )
400
+
401
+ if disambiguate:
402
+ if space.lower() != "real":
403
+ reference_image = cp.fft.ifftn(reference_image)
404
+ moving_image = cp.fft.ifftn(moving_image)
405
+ shift = _disambiguate_shift(reference_image, moving_image, shift)
406
+
407
+ # Redirect user to masked_phase_cross_correlation if NaNs are observed
408
+ if cp.isnan(CCmax) or cp.isnan(src_amp) or cp.isnan(target_amp):
409
+ raise ValueError(
410
+ "NaN values found, please remove NaNs from your "
411
+ "input data"
412
+ )
413
+
414
+ return (
415
+ shift,
416
+ _compute_error(CCmax, src_amp, target_amp),
417
+ _compute_phasediff(CCmax),
418
+ )
@@ -18,7 +18,7 @@
18
18
  # Created By : Tomography Team at DLS <scientificsoftware@diamond.ac.uk>
19
19
  # Changes relative to ToMoBAR 2024.01 version
20
20
  # ---------------------------------------------------------------------------
21
- """Module for tomographic reconstruction. For more detailed information see :ref:`image_reconstruction_module`"""
21
+ """Module for tomographic reconstruction. For more detailed information, see :ref:`image_reconstruction_module`"""
22
22
 
23
23
  import numpy as np
24
24
  from httomolibgpu import cupywrapper
@@ -40,8 +40,6 @@ else:
40
40
  from numpy import float32
41
41
  from typing import Optional, Type, Union
42
42
 
43
- from httomolibgpu.misc.supp_func import data_checker
44
-
45
43
 
46
44
  __all__ = [
47
45
  "FBP2d_astra",
@@ -66,7 +64,6 @@ def FBP2d_astra(
66
64
  filter_d: Optional[float] = None,
67
65
  recon_size: Optional[int] = None,
68
66
  recon_mask_radius: float = 0.95,
69
- neglog: bool = False,
70
67
  gpu_id: int = 0,
71
68
  ) -> np.ndarray:
72
69
  """
@@ -98,9 +95,6 @@ def FBP2d_astra(
98
95
  The radius of the circular mask that applies to the reconstructed slice in order to crop
99
96
  out some undesirable artifacts. The values outside the given diameter will be set to zero.
100
97
  To implement the cropping one can use the range [0.7-1.0] or set to 2.0 when no cropping required.
101
- neglog: bool
102
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
103
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
104
98
  gpu_id : int
105
99
  A GPU device index to perform operation on.
106
100
 
@@ -109,7 +103,6 @@ def FBP2d_astra(
109
103
  np.ndarray
110
104
  The FBP reconstructed volume as a numpy array.
111
105
  """
112
- data = data_checker(data, verbosity=True, method_name="FBP2d_astra")
113
106
 
114
107
  data_shape = np.shape(data)
115
108
  if recon_size is None:
@@ -123,8 +116,6 @@ def FBP2d_astra(
123
116
  reconstruction = np.empty(
124
117
  (recon_size, detY_size, recon_size), dtype=float32, order="C"
125
118
  )
126
- _take_neg_log_np(data) if neglog else data
127
-
128
119
  # loop over detY slices
129
120
  for slice_index in range(0, detY_size):
130
121
  reconstruction[:, slice_index, :] = np.flipud(
@@ -148,7 +139,6 @@ def FBP3d_tomobar(
148
139
  filter_freq_cutoff: float = 0.35,
149
140
  recon_size: Optional[int] = None,
150
141
  recon_mask_radius: Optional[float] = 0.95,
151
- neglog: bool = False,
152
142
  gpu_id: int = 0,
153
143
  ) -> cp.ndarray:
154
144
  """
@@ -177,9 +167,6 @@ def FBP3d_tomobar(
177
167
  The radius of the circular mask that applies to the reconstructed slice in order to crop
178
168
  out some undesirable artifacts. The values outside the given diameter will be set to zero.
179
169
  To implement the cropping one can use the range [0.7-1.0] or set to 2.0 when no cropping required.
180
- neglog: bool
181
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
182
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
183
170
  gpu_id : int
184
171
  A GPU device index to perform operation on.
185
172
 
@@ -188,14 +175,13 @@ def FBP3d_tomobar(
188
175
  cp.ndarray
189
176
  FBP reconstructed volume as a CuPy array.
190
177
  """
191
- data = data_checker(data, verbosity=True, method_name="FBP3d_tomobar")
192
178
 
193
179
  RecToolsCP = _instantiate_direct_recon_class(
194
180
  data, angles, center, detector_pad, recon_size, gpu_id
195
181
  )
196
182
 
197
183
  reconstruction = RecToolsCP.FBP(
198
- _take_neg_log(data) if neglog else data,
184
+ data,
199
185
  cutoff_freq=filter_freq_cutoff,
200
186
  recon_mask_radius=recon_mask_radius,
201
187
  data_axes_labels_order=input_data_axis_labels,
@@ -215,10 +201,9 @@ def LPRec3d_tomobar(
215
201
  recon_size: Optional[int] = None,
216
202
  recon_mask_radius: float = 0.95,
217
203
  power_of_2_oversampling: Optional[bool] = True,
218
- power_of_2_cropping: Optional[bool] = True,
219
- min_mem_usage_filter: Optional[bool] = False,
220
- min_mem_usage_ifft2: Optional[bool] = False,
221
- neglog: bool = False,
204
+ power_of_2_cropping: Optional[bool] = False,
205
+ min_mem_usage_filter: Optional[bool] = True,
206
+ min_mem_usage_ifft2: Optional[bool] = True,
222
207
  ) -> cp.ndarray:
223
208
  """
224
209
  Fourier direct inversion in 3D on unequally spaced (also called as Log-Polar) grids using
@@ -247,9 +232,6 @@ def LPRec3d_tomobar(
247
232
  The radius of the circular mask that applies to the reconstructed slice in order to crop
248
233
  out some undesirable artifacts. The values outside the given diameter will be set to zero.
249
234
  To implement the cropping one can use the range [0.7-1.0] or set to 2.0 when no cropping required.
250
- neglog: bool
251
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
252
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
253
235
 
254
236
  Returns
255
237
  -------
@@ -257,14 +239,12 @@ def LPRec3d_tomobar(
257
239
  The Log-polar Fourier reconstructed volume as a CuPy array.
258
240
  """
259
241
 
260
- data = data_checker(data, verbosity=True, method_name="LPRec3d_tomobar")
261
-
262
242
  RecToolsCP = _instantiate_direct_recon_class(
263
243
  data, angles, center, detector_pad, recon_size, 0
264
244
  )
265
245
 
266
246
  reconstruction = RecToolsCP.FOURIER_INV(
267
- _take_neg_log(data) if neglog else data,
247
+ data,
268
248
  recon_mask_radius=recon_mask_radius,
269
249
  data_axes_labels_order=input_data_axis_labels,
270
250
  filter_type=filter_type,
@@ -288,7 +268,6 @@ def SIRT3d_tomobar(
288
268
  recon_mask_radius: float = 0.95,
289
269
  iterations: int = 300,
290
270
  nonnegativity: bool = True,
291
- neglog: bool = False,
292
271
  gpu_id: int = 0,
293
272
  ) -> cp.ndarray:
294
273
  """
@@ -318,10 +297,7 @@ def SIRT3d_tomobar(
318
297
  iterations : int
319
298
  The number of SIRT iterations.
320
299
  nonnegativity : bool
321
- Impose nonnegativity constraint on reconstructed image.
322
- neglog: bool
323
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
324
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
300
+ Impose nonnegativity constraint on the reconstructed image.
325
301
  gpu_id : int
326
302
  A GPU device index to perform operation on.
327
303
 
@@ -330,7 +306,6 @@ def SIRT3d_tomobar(
330
306
  cp.ndarray
331
307
  The SIRT reconstructed volume as a CuPy array.
332
308
  """
333
- data = data_checker(data, verbosity=True, method_name="SIRT3d_tomobar")
334
309
 
335
310
  RecToolsCP = _instantiate_iterative_recon_class(
336
311
  data,
@@ -343,7 +318,7 @@ def SIRT3d_tomobar(
343
318
  )
344
319
 
345
320
  _data_ = {
346
- "projection_norm_data": _take_neg_log(data) if neglog else data,
321
+ "projection_norm_data": data,
347
322
  "data_axes_labels_order": input_data_axis_labels,
348
323
  } # data dictionary
349
324
  _algorithm_ = {
@@ -366,7 +341,6 @@ def CGLS3d_tomobar(
366
341
  recon_mask_radius: float = 0.95,
367
342
  iterations: int = 20,
368
343
  nonnegativity: bool = True,
369
- neglog: bool = False,
370
344
  gpu_id: int = 0,
371
345
  ) -> cp.ndarray:
372
346
  """
@@ -397,9 +371,6 @@ def CGLS3d_tomobar(
397
371
  The number of CGLS iterations.
398
372
  nonnegativity : bool
399
373
  Impose nonnegativity constraint on reconstructed image.
400
- neglog: bool
401
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
402
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
403
374
  gpu_id : int, optional
404
375
  A GPU device index to perform operation on.
405
376
 
@@ -408,14 +379,13 @@ def CGLS3d_tomobar(
408
379
  cp.ndarray
409
380
  The CGLS reconstructed volume as a CuPy array.
410
381
  """
411
- data = data_checker(data, verbosity=True, method_name="CGLS3d_tomobar")
412
382
 
413
383
  RecToolsCP = _instantiate_iterative_recon_class(
414
384
  data, angles, center, detector_pad, recon_size, gpu_id, datafidelity="LS"
415
385
  )
416
386
 
417
387
  _data_ = {
418
- "projection_norm_data": _take_neg_log(data) if neglog else data,
388
+ "projection_norm_data": data,
419
389
  "data_axes_labels_order": input_data_axis_labels,
420
390
  } # data dictionary
421
391
  _algorithm_ = {
@@ -443,7 +413,6 @@ def FISTA3d_tomobar(
443
413
  regularisation_iterations: int = 50,
444
414
  regularisation_half_precision: bool = True,
445
415
  nonnegativity: bool = True,
446
- neglog: bool = False,
447
416
  gpu_id: int = 0,
448
417
  ) -> cp.ndarray:
449
418
  """
@@ -482,9 +451,6 @@ def FISTA3d_tomobar(
482
451
  Perform faster regularisation computation in half-precision with a very minimal sacrifice in quality.
483
452
  nonnegativity : bool
484
453
  Impose nonnegativity constraint on the reconstructed image.
485
- neglog: bool
486
- Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
487
- assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
488
454
  gpu_id : int
489
455
  A GPU device index to perform operation on.
490
456
 
@@ -493,14 +459,12 @@ def FISTA3d_tomobar(
493
459
  cp.ndarray
494
460
  The FISTA reconstructed volume as a CuPy array.
495
461
  """
496
- data = data_checker(data, verbosity=True, method_name="FISTA3d_tomobar")
497
-
498
462
  RecToolsCP = _instantiate_iterative_recon_class(
499
463
  data, angles, center, detector_pad, recon_size, gpu_id, datafidelity="LS"
500
464
  )
501
465
 
502
466
  _data_ = {
503
- "projection_norm_data": _take_neg_log(data) if neglog else data,
467
+ "projection_norm_data": data,
504
468
  "OS_number": subsets_number,
505
469
  "data_axes_labels_order": input_data_axis_labels,
506
470
  }
@@ -659,26 +623,8 @@ def _instantiate_iterative_recon_class(
659
623
  return RecToolsCP
660
624
 
661
625
 
662
- def _take_neg_log(data: cp.ndarray) -> cp.ndarray:
663
- """Taking negative log"""
664
- data[data <= 0] = 1
665
- data = -cp.log(data)
666
- data[cp.isnan(data)] = 6.0
667
- data[cp.isinf(data)] = 0
668
- return data
669
-
670
-
671
- def _take_neg_log_np(data: np.ndarray) -> np.ndarray:
672
- """Taking negative log"""
673
- data[data <= 0] = 1
674
- data = -np.log(data)
675
- data[np.isnan(data)] = 6.0
676
- data[np.isinf(data)] = 0
677
- return data
678
-
679
-
680
626
  def __estimate_detectorHoriz_padding(detX_size) -> int:
681
627
  det_half = detX_size // 2
682
628
  padded_value_exact = int(np.sqrt(2 * (det_half**2))) - det_half
683
- padded_add_margin = int(0.1 * padded_value_exact)
629
+ padded_add_margin = padded_value_exact // 2
684
630
  return padded_value_exact + padded_add_margin