ChessAnalysisPipeline 0.0.14__py3-none-any.whl → 0.0.16__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.
Potentially problematic release.
This version of ChessAnalysisPipeline might be problematic. Click here for more details.
- CHAP/__init__.py +1 -1
- CHAP/common/__init__.py +13 -0
- CHAP/common/models/integration.py +29 -26
- CHAP/common/models/map.py +395 -224
- CHAP/common/processor.py +1725 -93
- CHAP/common/reader.py +265 -28
- CHAP/common/writer.py +191 -18
- CHAP/edd/__init__.py +9 -2
- CHAP/edd/models.py +886 -665
- CHAP/edd/processor.py +2592 -936
- CHAP/edd/reader.py +889 -0
- CHAP/edd/utils.py +846 -292
- CHAP/foxden/__init__.py +6 -0
- CHAP/foxden/processor.py +42 -0
- CHAP/foxden/writer.py +65 -0
- CHAP/giwaxs/__init__.py +8 -0
- CHAP/giwaxs/models.py +100 -0
- CHAP/giwaxs/processor.py +520 -0
- CHAP/giwaxs/reader.py +5 -0
- CHAP/giwaxs/writer.py +5 -0
- CHAP/pipeline.py +48 -10
- CHAP/runner.py +161 -72
- CHAP/tomo/models.py +31 -29
- CHAP/tomo/processor.py +169 -118
- CHAP/utils/__init__.py +1 -0
- CHAP/utils/fit.py +1292 -1315
- CHAP/utils/general.py +411 -53
- CHAP/utils/models.py +594 -0
- CHAP/utils/parfile.py +10 -2
- ChessAnalysisPipeline-0.0.16.dist-info/LICENSE +60 -0
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.16.dist-info}/METADATA +1 -1
- ChessAnalysisPipeline-0.0.16.dist-info/RECORD +62 -0
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.16.dist-info}/WHEEL +1 -1
- CHAP/utils/scanparsers.py +0 -1431
- ChessAnalysisPipeline-0.0.14.dist-info/LICENSE +0 -21
- ChessAnalysisPipeline-0.0.14.dist-info/RECORD +0 -54
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.16.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.16.dist-info}/top_level.txt +0 -0
CHAP/tomo/processor.py
CHANGED
|
@@ -8,7 +8,6 @@ Description: Module for Processors used only by tomography experiments
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# System modules
|
|
11
|
-
from os import mkdir
|
|
12
11
|
from os import path as os_path
|
|
13
12
|
from sys import exit as sys_exit
|
|
14
13
|
from time import time
|
|
@@ -31,7 +30,6 @@ from CHAP.utils.general import (
|
|
|
31
30
|
quick_imshow,
|
|
32
31
|
nxcopy,
|
|
33
32
|
)
|
|
34
|
-
from CHAP.utils.fit import Fit
|
|
35
33
|
from CHAP.processor import Processor
|
|
36
34
|
from CHAP.reader import main
|
|
37
35
|
|
|
@@ -148,6 +146,8 @@ class TomoCHESSMapConverter(Processor):
|
|
|
148
146
|
|
|
149
147
|
# Validate map
|
|
150
148
|
map_config = MapConfig(**loads(str(tomofields.map_config)))
|
|
149
|
+
assert len(map_config.spec_scans) == 1
|
|
150
|
+
num_tomo_stack = len(map_config.spec_scans[0].scan_numbers)
|
|
151
151
|
|
|
152
152
|
# Check available independent dimensions
|
|
153
153
|
independent_dimensions = tomofields.data.attrs['axes']
|
|
@@ -167,8 +167,6 @@ class TomoCHESSMapConverter(Processor):
|
|
|
167
167
|
f'({rotation_angle_data_type})')
|
|
168
168
|
matched_dimensions.pop(matched_dimensions.index('rotation_angles'))
|
|
169
169
|
if 'x_translation' in independent_dimensions:
|
|
170
|
-
x_translation_index = \
|
|
171
|
-
tomofields.data.axes.index('x_translation')
|
|
172
170
|
x_translation_data_type = \
|
|
173
171
|
tomofields.data.x_translation.attrs['data_type']
|
|
174
172
|
x_translation_name = \
|
|
@@ -180,8 +178,6 @@ class TomoCHESSMapConverter(Processor):
|
|
|
180
178
|
else:
|
|
181
179
|
x_translation_data_type = None
|
|
182
180
|
if 'z_translation' in independent_dimensions:
|
|
183
|
-
z_translation_index = \
|
|
184
|
-
tomofields.data.axes.index('z_translation')
|
|
185
181
|
z_translation_data_type = \
|
|
186
182
|
tomofields.data.z_translation.attrs['data_type']
|
|
187
183
|
z_translation_name = \
|
|
@@ -226,17 +222,12 @@ class TomoCHESSMapConverter(Processor):
|
|
|
226
222
|
|
|
227
223
|
# Add an NXdetector to the NXinstrument
|
|
228
224
|
# (do not fill in data fields yet)
|
|
225
|
+
detector_names = list(np.asarray(tomofields.detector_names, dtype=str))
|
|
229
226
|
detector_prefix = detector_config.prefix
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
f'unavailable (available detectors: {detectors})')
|
|
235
|
-
tomo_stacks = tomofields.data[detector_prefix]
|
|
236
|
-
tomo_stack_shape = tomo_stacks.shape
|
|
237
|
-
assert len(tomo_stack_shape) == 2+len(independent_dimensions)
|
|
238
|
-
assert tomo_stack_shape[-2] == detector_config.rows
|
|
239
|
-
assert tomo_stack_shape[-1] == detector_config.columns
|
|
227
|
+
if detector_prefix not in detector_names:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
f'Data for detector {detector_prefix} is unavailable '
|
|
230
|
+
f'(available detectors: {detector_names})')
|
|
240
231
|
nxdetector = NXdetector()
|
|
241
232
|
nxinstrument.detector = nxdetector
|
|
242
233
|
nxdetector.local_name = detector_prefix
|
|
@@ -274,10 +265,9 @@ class TomoCHESSMapConverter(Processor):
|
|
|
274
265
|
x_translations = []
|
|
275
266
|
z_translations = []
|
|
276
267
|
if darkfield is not None:
|
|
277
|
-
nxentry.dark_field_config = darkfield.
|
|
268
|
+
nxentry.dark_field_config = darkfield.config
|
|
278
269
|
for scan_name, scan in darkfield.spec_scans.items():
|
|
279
270
|
for scan_number, nxcollection in scan.items():
|
|
280
|
-
scan_columns = loads(str(nxcollection.scan_columns))
|
|
281
271
|
data_shape = nxcollection.data[detector_prefix].shape
|
|
282
272
|
assert len(data_shape) == 3
|
|
283
273
|
assert data_shape[1] == detector_config.rows
|
|
@@ -286,7 +276,7 @@ class TomoCHESSMapConverter(Processor):
|
|
|
286
276
|
image_keys += num_image*[2]
|
|
287
277
|
sequence_numbers += list(range(num_image))
|
|
288
278
|
image_stacks.append(
|
|
289
|
-
nxcollection.data[detector_prefix])
|
|
279
|
+
nxcollection.data[detector_prefix].nxdata)
|
|
290
280
|
rotation_angles += num_image*[0.0]
|
|
291
281
|
if (x_translation_data_type == 'spec_motor' or
|
|
292
282
|
z_translation_data_type == 'spec_motor'):
|
|
@@ -314,10 +304,9 @@ class TomoCHESSMapConverter(Processor):
|
|
|
314
304
|
num_image*[smb_pars[z_translation_name]]
|
|
315
305
|
|
|
316
306
|
# Collect bright field data
|
|
317
|
-
nxentry.bright_field_config = brightfield.
|
|
307
|
+
nxentry.bright_field_config = brightfield.config
|
|
318
308
|
for scan_name, scan in brightfield.spec_scans.items():
|
|
319
309
|
for scan_number, nxcollection in scan.items():
|
|
320
|
-
scan_columns = loads(str(nxcollection.scan_columns))
|
|
321
310
|
data_shape = nxcollection.data[detector_prefix].shape
|
|
322
311
|
assert len(data_shape) == 3
|
|
323
312
|
assert data_shape[1] == detector_config.rows
|
|
@@ -326,7 +315,7 @@ class TomoCHESSMapConverter(Processor):
|
|
|
326
315
|
image_keys += num_image*[1]
|
|
327
316
|
sequence_numbers += list(range(num_image))
|
|
328
317
|
image_stacks.append(
|
|
329
|
-
nxcollection.data[detector_prefix])
|
|
318
|
+
nxcollection.data[detector_prefix].nxdata)
|
|
330
319
|
rotation_angles += num_image*[0.0]
|
|
331
320
|
if (x_translation_data_type == 'spec_motor' or
|
|
332
321
|
z_translation_data_type == 'spec_motor'):
|
|
@@ -354,65 +343,46 @@ class TomoCHESSMapConverter(Processor):
|
|
|
354
343
|
num_image*[smb_pars[z_translation_name]]
|
|
355
344
|
|
|
356
345
|
# Collect tomography fields data
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
# if len(list(tomofields.data.z_translation)):
|
|
365
|
-
# z_trans = list(tomofields.data.z_translation)
|
|
366
|
-
# else:
|
|
367
|
-
# z_trans = [float(tomofields.data.z_translation)]
|
|
368
|
-
if rotation_angles_index < z_translation_index:
|
|
369
|
-
tomo_stacks = np.swapaxes(
|
|
370
|
-
tomo_stacks, rotation_angles_index,
|
|
371
|
-
z_translation_index)
|
|
372
|
-
tomo_stacks = np.expand_dims(tomo_stacks, z_translation_index)
|
|
373
|
-
elif z_translation_data_type is None:
|
|
374
|
-
z_trans = [0.0]
|
|
375
|
-
if rotation_angles_index < x_translation_index:
|
|
376
|
-
tomo_stacks = np.swapaxes(
|
|
377
|
-
tomo_stacks, rotation_angles_index, x_translation_index)
|
|
378
|
-
tomo_stacks = np.expand_dims(tomo_stacks, 0)
|
|
379
|
-
else:
|
|
380
|
-
x_trans = tomofields.data.x_translation.nxdata
|
|
381
|
-
z_trans = tomofields.data.z_translation.nxdata
|
|
382
|
-
#if tomofields.data.x_translation.size > 1:
|
|
383
|
-
# x_trans = list(tomofields.data.x_translation)
|
|
384
|
-
#else:
|
|
385
|
-
# x_trans = [float(tomofields.data.x_translation)]
|
|
386
|
-
#if len(list(tomofields.data.z_translation)):
|
|
387
|
-
# z_trans = list(tomofields.data.z_translation)
|
|
388
|
-
#else:
|
|
389
|
-
# z_trans = [float(tomofields.data.z_translation)]
|
|
390
|
-
if (rotation_angles_index
|
|
391
|
-
< max(x_translation_index, z_translation_index)):
|
|
392
|
-
tomo_stacks = np.swapaxes(
|
|
393
|
-
tomo_stacks, rotation_angles_index,
|
|
394
|
-
max(x_translation_index, z_translation_index))
|
|
395
|
-
if x_translation_index < z_translation_index:
|
|
396
|
-
tomo_stacks = np.swapaxes(
|
|
397
|
-
tomo_stacks, x_translation_index, z_translation_index)
|
|
346
|
+
tomo_stacks = tomofields.data.detector_data.nxdata[
|
|
347
|
+
detector_names.index(detector_prefix)]
|
|
348
|
+
tomo_stack_shape = tomo_stacks.shape
|
|
349
|
+
assert len(tomo_stack_shape) == 3
|
|
350
|
+
assert tomo_stack_shape[-2] == detector_config.rows
|
|
351
|
+
assert tomo_stack_shape[-1] == detector_config.columns
|
|
352
|
+
assert not tomo_stack_shape[0] % num_tomo_stack
|
|
398
353
|
# Restrict to 180 degrees set of data for now to match old code
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
354
|
+
thetas_stacks = tomofields.data.rotation_angles.nxdata
|
|
355
|
+
num_theta = tomo_stack_shape[0] // num_tomo_stack
|
|
356
|
+
assert num_theta > 2
|
|
357
|
+
thetas = thetas_stacks[0:num_theta]
|
|
358
|
+
delta_theta = thetas[1] - thetas[0]
|
|
359
|
+
if thetas[num_theta-1] - thetas[0] > 180 - delta_theta:
|
|
360
|
+
image_end = index_nearest(thetas, thetas[0] + 180)
|
|
404
361
|
else:
|
|
405
|
-
image_end =
|
|
362
|
+
image_end = thetas.size
|
|
406
363
|
thetas = thetas[:image_end]
|
|
407
|
-
num_image =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
364
|
+
num_image = thetas.size
|
|
365
|
+
n_start = 0
|
|
366
|
+
image_keys += num_tomo_stack * num_image * [0]
|
|
367
|
+
sequence_numbers += num_tomo_stack * list(range(num_image))
|
|
368
|
+
if x_translation_data_type is None:
|
|
369
|
+
x_translations += num_tomo_stack * num_image * [0.0]
|
|
370
|
+
if z_translation_data_type is None:
|
|
371
|
+
z_translations += num_tomo_stack * num_image * [0.0]
|
|
372
|
+
for i in range(num_tomo_stack):
|
|
373
|
+
image_stacks.append(tomo_stacks[n_start:n_start+num_image])
|
|
374
|
+
if not np.array_equal(
|
|
375
|
+
thetas, thetas_stacks[n_start:n_start+num_image]):
|
|
376
|
+
raise RuntimeError(
|
|
377
|
+
'Inconsistent thetas among tomography image stacks')
|
|
378
|
+
rotation_angles += list(thetas)
|
|
379
|
+
if x_translation_data_type is not None:
|
|
380
|
+
x_translations += list(
|
|
381
|
+
tomofields.data.x_translation[n_start:n_start+num_image])
|
|
382
|
+
if z_translation_data_type is not None:
|
|
383
|
+
z_translations += list(
|
|
384
|
+
tomofields.data.z_translation[n_start:n_start+num_image])
|
|
385
|
+
n_start += num_theta
|
|
416
386
|
|
|
417
387
|
# Add image data to NXdetector
|
|
418
388
|
nxinstrument.detector.image_key = image_keys
|
|
@@ -458,7 +428,7 @@ class TomoDataProcessor(Processor):
|
|
|
458
428
|
for tomographic image reduction.
|
|
459
429
|
:type data: list[PipelineData]
|
|
460
430
|
:param outputdir: Output folder name, defaults to '.'.
|
|
461
|
-
:type outputdir
|
|
431
|
+
:type outputdir: str, optional
|
|
462
432
|
:param interactive: Allows for user interactions,
|
|
463
433
|
defaults to False.
|
|
464
434
|
:type interactive: bool, optional
|
|
@@ -666,7 +636,7 @@ class Tomo:
|
|
|
666
636
|
:param num_core: Number of processors.
|
|
667
637
|
:type num_core: int
|
|
668
638
|
:param outputdir: Output folder name, defaults to '.'.
|
|
669
|
-
:type outputdir
|
|
639
|
+
:type outputdir: str, optional
|
|
670
640
|
:param save_figs: Safe figures to file ('yes' or 'only') and/or
|
|
671
641
|
display figures ('yes' or 'no'), defaults to 'no'.
|
|
672
642
|
:type save_figs: Literal['yes', 'no', 'only'], optional
|
|
@@ -1395,28 +1365,28 @@ class Tomo:
|
|
|
1395
1365
|
|
|
1396
1366
|
# Resize the combined tomography data stacks
|
|
1397
1367
|
# - combined axis data order: row/-z,y,x
|
|
1398
|
-
if self._interactive:
|
|
1368
|
+
if self._interactive or self._save_figs:
|
|
1399
1369
|
x_bounds, y_bounds, z_bounds = self._resize_reconstructed_data(
|
|
1400
1370
|
tomo_recon_combined, combine_data=True)
|
|
1401
1371
|
else:
|
|
1402
1372
|
x_bounds = tool_config.x_bounds
|
|
1403
1373
|
if x_bounds is None:
|
|
1404
1374
|
self._logger.warning(
|
|
1405
|
-
'x_bounds unspecified,
|
|
1375
|
+
'x_bounds unspecified, combine data for full x-range')
|
|
1406
1376
|
elif not is_int_pair(
|
|
1407
1377
|
x_bounds, ge=0, le=tomo_shape[2]):
|
|
1408
1378
|
raise ValueError(f'Invalid parameter x_bounds ({x_bounds})')
|
|
1409
1379
|
y_bounds = tool_config.y_bounds
|
|
1410
1380
|
if y_bounds is None:
|
|
1411
1381
|
self._logger.warning(
|
|
1412
|
-
'y_bounds unspecified,
|
|
1382
|
+
'y_bounds unspecified, combine data for full y-range')
|
|
1413
1383
|
elif not is_int_pair(
|
|
1414
1384
|
y_bounds, ge=0, le=tomo_shape[1]):
|
|
1415
1385
|
raise ValueError(f'Invalid parameter y_bounds ({y_bounds})')
|
|
1416
1386
|
z_bounds = tool_config.z_bounds
|
|
1417
1387
|
if z_bounds is None:
|
|
1418
1388
|
self._logger.warning(
|
|
1419
|
-
'z_bounds unspecified,
|
|
1389
|
+
'z_bounds unspecified, combine data for full z-range')
|
|
1420
1390
|
elif not is_int_pair(
|
|
1421
1391
|
z_bounds, ge=0, le=tomo_shape[0]):
|
|
1422
1392
|
raise ValueError(f'Invalid parameter z_bounds ({z_bounds})')
|
|
@@ -1706,18 +1676,46 @@ class Tomo:
|
|
|
1706
1676
|
img_row_bounds = calibrate_center_rows
|
|
1707
1677
|
else:
|
|
1708
1678
|
if nxentry.instrument.source.attrs['station'] in ('id1a3', 'id3a'):
|
|
1679
|
+
# System modules
|
|
1680
|
+
from sys import float_info
|
|
1681
|
+
|
|
1682
|
+
# Third party modules
|
|
1683
|
+
from nexusformat.nexus import (
|
|
1684
|
+
NXdata,
|
|
1685
|
+
NXfield,
|
|
1686
|
+
)
|
|
1687
|
+
|
|
1688
|
+
# Local modules
|
|
1689
|
+
from CHAP.utils.fit import FitProcessor
|
|
1690
|
+
|
|
1709
1691
|
pixel_size = float(nxentry.instrument.detector.row_pixel_size)
|
|
1710
1692
|
# Try to get a fit from the bright field
|
|
1711
1693
|
row_sum = np.sum(tbf, 1)
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1694
|
+
num = len(row_sum)
|
|
1695
|
+
fit = FitProcessor()
|
|
1696
|
+
model = {'model': 'rectangle',
|
|
1697
|
+
'parameters': [
|
|
1698
|
+
{'name': 'amplitude',
|
|
1699
|
+
'value': row_sum.max()-row_sum.min(),
|
|
1700
|
+
'min': 0.0},
|
|
1701
|
+
{'name': 'center1', 'value': 0.25*num,
|
|
1702
|
+
'min': 0.0, 'max': num},
|
|
1703
|
+
{'name': 'sigma1', 'value': num/7.0,
|
|
1704
|
+
'min': float_info.min},
|
|
1705
|
+
{'name': 'center2', 'value': 0.75*num,
|
|
1706
|
+
'min': 0.0, 'max': num},
|
|
1707
|
+
{'name': 'sigma2', 'value': num/7.0,
|
|
1708
|
+
'min': float_info.min}]}
|
|
1709
|
+
bounds_fit = fit.process(
|
|
1710
|
+
NXdata(NXfield(row_sum, 'y'),
|
|
1711
|
+
NXfield(np.array(range(num)), 'x')),
|
|
1712
|
+
{'models': [model], 'method': 'trf'})
|
|
1713
|
+
parameters = bounds_fit.best_values
|
|
1716
1714
|
row_low_fit = parameters.get('center1', None)
|
|
1717
1715
|
row_upp_fit = parameters.get('center2', None)
|
|
1718
1716
|
sig_low = parameters.get('sigma1', None)
|
|
1719
1717
|
sig_upp = parameters.get('sigma2', None)
|
|
1720
|
-
have_fit = (
|
|
1718
|
+
have_fit = (bounds_fit.success and row_low_fit is not None
|
|
1721
1719
|
and row_upp_fit is not None and sig_low is not None
|
|
1722
1720
|
and sig_upp is not None
|
|
1723
1721
|
and 0 <= row_low_fit < row_upp_fit <= row_sum.size
|
|
@@ -1787,7 +1785,7 @@ class Tomo:
|
|
|
1787
1785
|
if calibrate_center_rows:
|
|
1788
1786
|
title='Select two detector image row indices to '\
|
|
1789
1787
|
'calibrate rotation axis (in range '\
|
|
1790
|
-
f'[0, {first_image.shape[0]}])'
|
|
1788
|
+
f'[0, {first_image.shape[0]-1}])'
|
|
1791
1789
|
else:
|
|
1792
1790
|
title='Select detector image row bounds for data '\
|
|
1793
1791
|
f'reduction (in range [0, {first_image.shape[0]}])'
|
|
@@ -2052,7 +2050,7 @@ class Tomo:
|
|
|
2052
2050
|
evaluate('-log(tomo_stack)', out=tomo_stack)
|
|
2053
2051
|
|
|
2054
2052
|
# Get rid of nans/infs that may be introduced by normalization
|
|
2055
|
-
np.where(np.isfinite(tomo_stack), tomo_stack, 0.)
|
|
2053
|
+
tomo_stack = np.where(np.isfinite(tomo_stack), tomo_stack, 0.)
|
|
2056
2054
|
|
|
2057
2055
|
# Downsize tomography stack to smaller size
|
|
2058
2056
|
tomo_stack = tomo_stack.astype('float32', copy=False)
|
|
@@ -2452,8 +2450,9 @@ class Tomo:
|
|
|
2452
2450
|
f'{selected_center_offset:.2f}.png'))
|
|
2453
2451
|
plt.close()
|
|
2454
2452
|
|
|
2453
|
+
del recon_planes
|
|
2454
|
+
|
|
2455
2455
|
del sinogram
|
|
2456
|
-
del recon_planes
|
|
2457
2456
|
|
|
2458
2457
|
# Return the center location
|
|
2459
2458
|
if self._interactive:
|
|
@@ -2834,16 +2833,16 @@ class Tomo:
|
|
|
2834
2833
|
# Selecting x an y bounds (in z-plane)
|
|
2835
2834
|
if x_bounds is None:
|
|
2836
2835
|
if not self._interactive:
|
|
2837
|
-
self._logger.warning('x_bounds unspecified,
|
|
2838
|
-
'
|
|
2836
|
+
self._logger.warning('x_bounds unspecified, use data for '
|
|
2837
|
+
'full x-range')
|
|
2839
2838
|
x_bounds = (0, tomo_recon_stacks[0].shape[2])
|
|
2840
2839
|
elif not is_int_pair(
|
|
2841
2840
|
x_bounds, ge=0, le=tomo_recon_stacks[0].shape[2]):
|
|
2842
2841
|
raise ValueError(f'Invalid parameter x_bounds ({x_bounds})')
|
|
2843
2842
|
if y_bounds is None:
|
|
2844
2843
|
if not self._interactive:
|
|
2845
|
-
self._logger.warning('y_bounds unspecified,
|
|
2846
|
-
'
|
|
2844
|
+
self._logger.warning('y_bounds unspecified, use data for '
|
|
2845
|
+
'full y-range')
|
|
2847
2846
|
y_bounds = (0, tomo_recon_stacks[0].shape[1])
|
|
2848
2847
|
elif not is_int_pair(
|
|
2849
2848
|
y_bounds, ge=0, le=tomo_recon_stacks[0].shape[1]):
|
|
@@ -2865,11 +2864,20 @@ class Tomo:
|
|
|
2865
2864
|
tomosum = 0
|
|
2866
2865
|
for i in range(num_tomo_stacks):
|
|
2867
2866
|
tomosum = tomosum + np.sum(tomo_recon_stacks[i], axis=0)
|
|
2868
|
-
|
|
2867
|
+
if self._save_figs:
|
|
2868
|
+
if combine_data:
|
|
2869
|
+
filename = os_path.join(
|
|
2870
|
+
self._outputdir, 'combined_data_xy_roi.png')
|
|
2871
|
+
else:
|
|
2872
|
+
filename = os_path.join(
|
|
2873
|
+
self._outputdir, 'reconstructed_data_xy_roi.png')
|
|
2874
|
+
else:
|
|
2875
|
+
filename = None
|
|
2876
|
+
roi = select_roi_2d(
|
|
2869
2877
|
tomosum, preselected_roi=preselected_roi,
|
|
2870
2878
|
title_a='Reconstructed data summed over z',
|
|
2871
2879
|
row_label='y', column_label='x',
|
|
2872
|
-
interactive=self._interactive)
|
|
2880
|
+
interactive=self._interactive, filename=filename)
|
|
2873
2881
|
if roi is None:
|
|
2874
2882
|
x_bounds = (0, tomo_recon_stacks[0].shape[2])
|
|
2875
2883
|
y_bounds = (0, tomo_recon_stacks[0].shape[1])
|
|
@@ -2878,12 +2886,6 @@ class Tomo:
|
|
|
2878
2886
|
y_bounds = (int(roi[2]), int(roi[3]))
|
|
2879
2887
|
self._logger.debug(f'x_bounds = {x_bounds}')
|
|
2880
2888
|
self._logger.debug(f'y_bounds = {y_bounds}')
|
|
2881
|
-
# Plot results
|
|
2882
|
-
if self._save_figs:
|
|
2883
|
-
fig.savefig(
|
|
2884
|
-
os_path.join(
|
|
2885
|
-
self._outputdir, 'reconstructed_data_xy_roi.png'))
|
|
2886
|
-
plt.close()
|
|
2887
2889
|
|
|
2888
2890
|
# Selecting z bounds (in xy-plane)
|
|
2889
2891
|
# (only valid for a single image stack or when combining a stack)
|
|
@@ -2905,17 +2907,20 @@ class Tomo:
|
|
|
2905
2907
|
tomosum = 0
|
|
2906
2908
|
for i in range(num_tomo_stacks):
|
|
2907
2909
|
tomosum = tomosum + np.sum(tomo_recon_stacks[i], axis=(1,2))
|
|
2908
|
-
|
|
2910
|
+
if self._save_figs:
|
|
2911
|
+
if combine_data:
|
|
2912
|
+
filename = os_path.join(
|
|
2913
|
+
self._outputdir, 'combined_data_z_roi.png')
|
|
2914
|
+
else:
|
|
2915
|
+
filename = os_path.join(
|
|
2916
|
+
self._outputdir, 'reconstructed_data_z_roi.png')
|
|
2917
|
+
else:
|
|
2918
|
+
filename = None
|
|
2919
|
+
z_bounds = select_roi_1d(
|
|
2909
2920
|
tomosum, preselected_roi=z_bounds,
|
|
2910
2921
|
xlabel='z', ylabel='Reconstructed data summed over x and y',
|
|
2911
|
-
interactive=self._interactive)
|
|
2922
|
+
interactive=self._interactive, filename=filename)
|
|
2912
2923
|
self._logger.debug(f'z_bounds = {z_bounds}')
|
|
2913
|
-
# Plot results
|
|
2914
|
-
if self._save_figs:
|
|
2915
|
-
fig.savefig(
|
|
2916
|
-
os_path.join(
|
|
2917
|
-
self._outputdir, 'reconstructed_data_z_roi.png'))
|
|
2918
|
-
plt.close()
|
|
2919
2924
|
|
|
2920
2925
|
return x_bounds, y_bounds, z_bounds
|
|
2921
2926
|
|
|
@@ -2957,6 +2962,9 @@ class TomoSimFieldProcessor(Processor):
|
|
|
2957
2962
|
sample_size = config.sample_size
|
|
2958
2963
|
if len(sample_size) == 1:
|
|
2959
2964
|
sample_size = (sample_size[0], sample_size[0])
|
|
2965
|
+
if sample_type == 'hollow_pyramid' and len(sample_size) != 3:
|
|
2966
|
+
raise ValueError('Invalid combindation of sample_type '
|
|
2967
|
+
f'({sample_type}) and sample_size ({sample_size}')
|
|
2960
2968
|
wall_thickness = config.wall_thickness
|
|
2961
2969
|
mu = config.mu
|
|
2962
2970
|
theta_step = config.theta_step
|
|
@@ -2993,15 +3001,22 @@ class TomoSimFieldProcessor(Processor):
|
|
|
2993
3001
|
|
|
2994
3002
|
# Get the number of horizontal stacks bases on the diagonal
|
|
2995
3003
|
# of the square and for now don't allow more than one
|
|
2996
|
-
|
|
2997
|
-
|
|
3004
|
+
if (sample_size) == 3:
|
|
3005
|
+
num_tomo_stack = 1 + int(
|
|
3006
|
+
(max(sample_size[1:2])*np.sqrt(2)-pixel_size[1])
|
|
3007
|
+
/ (detector_size[1]*pixel_size[1]))
|
|
3008
|
+
else:
|
|
3009
|
+
num_tomo_stack = 1 + int((sample_size[1]*np.sqrt(2)-pixel_size[1])
|
|
3010
|
+
/ (detector_size[1]*pixel_size[1]))
|
|
2998
3011
|
if num_tomo_stack > 1:
|
|
2999
3012
|
raise ValueError('Sample is too wide for the detector')
|
|
3000
3013
|
|
|
3001
3014
|
# Create the x-ray path length through a solid square
|
|
3002
3015
|
# crosssection for a set of rotation angles.
|
|
3003
|
-
path_lengths_solid =
|
|
3004
|
-
|
|
3016
|
+
path_lengths_solid = None
|
|
3017
|
+
if sample_type != 'hollow_pyramid':
|
|
3018
|
+
path_lengths_solid = self._create_pathlength_solid_square(
|
|
3019
|
+
sample_size[1], thetas, pixel_size[1], detector_size[1])
|
|
3005
3020
|
|
|
3006
3021
|
# Create the x-ray path length through a hollow square
|
|
3007
3022
|
# crosssection for a set of rotation angles.
|
|
@@ -3027,7 +3042,12 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3027
3042
|
num_theta = len(thetas)
|
|
3028
3043
|
vertical_shifts = []
|
|
3029
3044
|
tomo_fields_stack = []
|
|
3030
|
-
|
|
3045
|
+
len_img_y = (detector_size[1]+1)//2
|
|
3046
|
+
if len_img_y%2:
|
|
3047
|
+
len_img_y = 2*len_img_y - 1
|
|
3048
|
+
else:
|
|
3049
|
+
len_img_y = 2*len_img_y
|
|
3050
|
+
img_dim = (len(img_row_coords), len_img_y)
|
|
3031
3051
|
intensities_solid = None
|
|
3032
3052
|
intensities_hollow = None
|
|
3033
3053
|
for n in range(num_tomo_stack):
|
|
@@ -3044,6 +3064,37 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3044
3064
|
beam_intensity * np.exp(-mu*path_lengths_hollow)
|
|
3045
3065
|
for n in range(num_theta):
|
|
3046
3066
|
tomo_field[n,:,:] = intensities_hollow[n]
|
|
3067
|
+
elif sample_type == 'hollow_pyramid':
|
|
3068
|
+
outer_indices = \
|
|
3069
|
+
np.where(abs(img_row_coords) <= sample_size[0]/2)[0]
|
|
3070
|
+
inner_indices = np.where(
|
|
3071
|
+
abs(img_row_coords) < sample_size[0]/2 - wall_thickness)[0]
|
|
3072
|
+
wall_indices = list(set(outer_indices)-set(inner_indices))
|
|
3073
|
+
ratio = abs(sample_size[1]-sample_size[2])/sample_size[0]
|
|
3074
|
+
baselength = max(sample_size[1:2])
|
|
3075
|
+
for i in wall_indices:
|
|
3076
|
+
path_lengths_solid = self._create_pathlength_solid_square(
|
|
3077
|
+
baselength - ratio*(
|
|
3078
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3079
|
+
thetas, pixel_size[1], detector_size[1])
|
|
3080
|
+
intensities_solid = \
|
|
3081
|
+
beam_intensity * np.exp(-mu*path_lengths_solid)
|
|
3082
|
+
for n in range(num_theta):
|
|
3083
|
+
tomo_field[n,i] = intensities_solid[n]
|
|
3084
|
+
for i in inner_indices:
|
|
3085
|
+
path_lengths_hollow = (
|
|
3086
|
+
self._create_pathlength_solid_square(
|
|
3087
|
+
baselength - ratio*(
|
|
3088
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3089
|
+
thetas, pixel_size[1], detector_size[1])
|
|
3090
|
+
- self._create_pathlength_solid_square(
|
|
3091
|
+
baselength - 2*wall_thickness - ratio*(
|
|
3092
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3093
|
+
thetas, pixel_size[1], detector_size[1]))
|
|
3094
|
+
intensities_hollow = \
|
|
3095
|
+
beam_intensity * np.exp(-mu*path_lengths_hollow)
|
|
3096
|
+
for n in range(num_theta):
|
|
3097
|
+
tomo_field[n,i] = intensities_hollow[n]
|
|
3047
3098
|
else:
|
|
3048
3099
|
intensities_solid = \
|
|
3049
3100
|
beam_intensity * np.exp(-mu*path_lengths_solid)
|
|
@@ -3136,7 +3187,7 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3136
3187
|
"""
|
|
3137
3188
|
# Get the column coordinates
|
|
3138
3189
|
img_y_coords = pixel_size * (0.5 * (1 - detector_size%2)
|
|
3139
|
-
+ np.asarray(range(
|
|
3190
|
+
+ np.asarray(range((detector_size+1)//2)))
|
|
3140
3191
|
|
|
3141
3192
|
# Get the path lenghts for position column coordinates
|
|
3142
3193
|
lengths = np.zeros((len(thetas), len(img_y_coords)), dtype=np.float64)
|
CHAP/utils/__init__.py
CHANGED