ChessAnalysisPipeline 0.0.14__py3-none-any.whl → 0.0.15__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 +9 -0
- CHAP/common/models/map.py +295 -55
- CHAP/common/processor.py +846 -10
- CHAP/common/reader.py +171 -0
- CHAP/common/writer.py +181 -18
- CHAP/edd/__init__.py +10 -3
- CHAP/edd/models.py +822 -451
- CHAP/edd/processor.py +2221 -756
- CHAP/edd/reader.py +672 -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/pipeline.py +1 -1
- CHAP/runner.py +4 -4
- CHAP/tomo/models.py +7 -5
- CHAP/tomo/processor.py +118 -39
- CHAP/utils/__init__.py +1 -0
- CHAP/utils/fit.py +1292 -1315
- CHAP/utils/general.py +393 -53
- CHAP/utils/models.py +567 -0
- CHAP/utils/scanparsers.py +141 -28
- ChessAnalysisPipeline-0.0.15.dist-info/LICENSE +60 -0
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/METADATA +1 -1
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/RECORD +29 -25
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/WHEEL +1 -1
- ChessAnalysisPipeline-0.0.14.dist-info/LICENSE +0 -21
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.14.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/top_level.txt +0 -0
CHAP/foxden/__init__.py
ADDED
CHAP/foxden/processor.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#-*- coding: utf-8 -*-
|
|
3
|
+
#pylint: disable=
|
|
4
|
+
"""
|
|
5
|
+
File : processor.py
|
|
6
|
+
Author : Valentin Kuznetsov <vkuznet AT gmail dot com>
|
|
7
|
+
Description: Processor module for FOXDEN services
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# system modules
|
|
11
|
+
from time import time
|
|
12
|
+
|
|
13
|
+
# local modules
|
|
14
|
+
from CHAP import Processor
|
|
15
|
+
from CHAP.foxden.writer import FoxdenWriter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FoxdenProvenanceProcessor(Processor):
|
|
19
|
+
"""A Processor to communicate with FOXDEN provenance server."""
|
|
20
|
+
# def __init__(self):
|
|
21
|
+
# self.writer = FoxdenWriter()
|
|
22
|
+
|
|
23
|
+
def process(self, data, url, dryRun=False, verbose=False):
|
|
24
|
+
"""process data API"""
|
|
25
|
+
|
|
26
|
+
t0 = time()
|
|
27
|
+
self.logger.info(f'Executing "process" with url {url} data {data} dryrun {dryRun}')
|
|
28
|
+
writer = FoxdenWriter()
|
|
29
|
+
|
|
30
|
+
# data = self.writer.write(data, url, dryRun)
|
|
31
|
+
data = writer.write(data, url, dryRun=dryRun)
|
|
32
|
+
|
|
33
|
+
self.logger.info(f'Finished "process" in {time()-t0:.3f} seconds\n')
|
|
34
|
+
|
|
35
|
+
return data
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == '__main__':
|
|
39
|
+
# local modules
|
|
40
|
+
from CHAP.processor import main
|
|
41
|
+
|
|
42
|
+
main()
|
CHAP/foxden/writer.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""FOXDE command line writer."""
|
|
2
|
+
|
|
3
|
+
# system modules
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Local modules
|
|
7
|
+
from CHAP.writer import main
|
|
8
|
+
|
|
9
|
+
class FoxdenWriter():
|
|
10
|
+
"""FOXDEN writer writes data to specific FOXDEN service
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def write(self, data, url, method="POST", headers={}, timeout=10, dryRun=False):
|
|
14
|
+
"""Write the input data as text to a file.
|
|
15
|
+
|
|
16
|
+
:param data: input data
|
|
17
|
+
:type data: list[PipelineData]
|
|
18
|
+
:param url: url of service
|
|
19
|
+
:type url: str
|
|
20
|
+
:param method: HTTP method to use, POST for creation and PUT for update
|
|
21
|
+
:type method: str
|
|
22
|
+
:param headers: HTTP headers to use
|
|
23
|
+
:type headers: dictionary
|
|
24
|
+
:param timeout: timeout of HTTP request
|
|
25
|
+
:type timeout: str
|
|
26
|
+
:param dryRun: dryRun option to verify HTTP workflow
|
|
27
|
+
:type dryRun: boolean
|
|
28
|
+
:return: contents of the input data
|
|
29
|
+
:rtype: object
|
|
30
|
+
"""
|
|
31
|
+
import requests
|
|
32
|
+
if 'Content-Type' not in headers:
|
|
33
|
+
headers['Content-type'] = 'application/json'
|
|
34
|
+
if 'Accept' not in headers:
|
|
35
|
+
headers['Accept'] = 'application/json'
|
|
36
|
+
if dryRun:
|
|
37
|
+
print("### HTTP writer call", url, headers, data)
|
|
38
|
+
return []
|
|
39
|
+
token = ""
|
|
40
|
+
fname = os.getenv("CHESS_WRITE_TOKEN")
|
|
41
|
+
if not fname:
|
|
42
|
+
msg = f'CHESS_WRITE_TOKEN env variable is not set'
|
|
43
|
+
raise Exception(msg)
|
|
44
|
+
with open(fname, 'r') as istream:
|
|
45
|
+
token = istream.read()
|
|
46
|
+
if token:
|
|
47
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
48
|
+
else:
|
|
49
|
+
msg = f'No valid write token found in CHESS_WRITE_TOKEN env variable'
|
|
50
|
+
raise Exception(msg)
|
|
51
|
+
|
|
52
|
+
# make actual HTTP request to FOXDEN service
|
|
53
|
+
if method.lower() == 'post':
|
|
54
|
+
resp = requests.post(url, headers=headers, timeout=timeout, data=data)
|
|
55
|
+
elif method.lower() == 'put':
|
|
56
|
+
resp = requests.put(url, headers=headers, timeout=timeout, data=data)
|
|
57
|
+
else:
|
|
58
|
+
msg = f"unsupporteed method {method}"
|
|
59
|
+
raise Exception(msg)
|
|
60
|
+
data = resp.content
|
|
61
|
+
return data
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == '__main__':
|
|
65
|
+
main()
|
CHAP/pipeline.py
CHANGED
|
@@ -227,7 +227,7 @@ class MultiplePipelineItem(PipelineItem):
|
|
|
227
227
|
outputdir = os.path.normpath(os.path.join(
|
|
228
228
|
args['outputdir'], item_args.pop('outputdir')))
|
|
229
229
|
if not os.path.isdir(outputdir):
|
|
230
|
-
os.
|
|
230
|
+
os.makedirs(outputdir)
|
|
231
231
|
try:
|
|
232
232
|
tmpfile = NamedTemporaryFile(dir=outputdir)
|
|
233
233
|
except:
|
CHAP/runner.py
CHANGED
|
@@ -37,7 +37,7 @@ class RunConfig():
|
|
|
37
37
|
|
|
38
38
|
# Check if root exists (create it if not) and is readable
|
|
39
39
|
if not os.path.isdir(self.root):
|
|
40
|
-
os.
|
|
40
|
+
os.makedirs(self.root)
|
|
41
41
|
if not os.access(self.root, os.R_OK):
|
|
42
42
|
raise OSError('root directory is not accessible for reading '
|
|
43
43
|
f'({self.root})')
|
|
@@ -57,7 +57,7 @@ class RunConfig():
|
|
|
57
57
|
self.outputdir = os.path.realpath(
|
|
58
58
|
os.path.join(self.root, self.outputdir))
|
|
59
59
|
if not os.path.isdir(self.outputdir):
|
|
60
|
-
os.
|
|
60
|
+
os.makedirs(self.outputdir)
|
|
61
61
|
try:
|
|
62
62
|
tmpfile = NamedTemporaryFile(dir=self.outputdir)
|
|
63
63
|
except:
|
|
@@ -127,7 +127,7 @@ def setLogger(log_level="INFO"):
|
|
|
127
127
|
logger.setLevel(log_level)
|
|
128
128
|
log_handler = logging.StreamHandler()
|
|
129
129
|
log_handler.setFormatter(logging.Formatter(
|
|
130
|
-
'{name:20}: {message}', style='{'))
|
|
130
|
+
'{name:20}: {levelname}: {message}', style='{'))
|
|
131
131
|
logger.addHandler(log_handler)
|
|
132
132
|
return logger, log_handler
|
|
133
133
|
|
|
@@ -170,7 +170,7 @@ def run(
|
|
|
170
170
|
newoutputdir = os.path.normpath(os.path.join(
|
|
171
171
|
kwargs['outputdir'], item_args.pop('outputdir')))
|
|
172
172
|
if not os.path.isdir(newoutputdir):
|
|
173
|
-
os.
|
|
173
|
+
os.makedirs(newoutputdir)
|
|
174
174
|
try:
|
|
175
175
|
tmpfile = NamedTemporaryFile(dir=newoutputdir)
|
|
176
176
|
except:
|
CHAP/tomo/models.py
CHANGED
|
@@ -7,7 +7,6 @@ from typing import (
|
|
|
7
7
|
)
|
|
8
8
|
from pydantic import (
|
|
9
9
|
BaseModel,
|
|
10
|
-
StrictBool,
|
|
11
10
|
conint,
|
|
12
11
|
conlist,
|
|
13
12
|
confloat,
|
|
@@ -168,9 +167,11 @@ class TomoSimConfig(BaseModel):
|
|
|
168
167
|
:type detector: Detector
|
|
169
168
|
:ivar sample_type: Sample type for the tomography simulator.
|
|
170
169
|
:type sample_type: Literal['square_rod', 'square_pipe',
|
|
171
|
-
'hollow_cube', 'hollow_brick']
|
|
170
|
+
'hollow_cube', 'hollow_brick', 'hollow_pyramid']
|
|
172
171
|
:ivar sample_size: Size of each sample dimension in mm (internally
|
|
173
|
-
converted to an integer number of pixels).
|
|
172
|
+
converted to an integer number of pixels). Enter three values
|
|
173
|
+
for sample_type == `'hollow_pyramid'`, the height and the side
|
|
174
|
+
at the respective bottom and the top of the pyramid.
|
|
174
175
|
:type sample_size: list[float]
|
|
175
176
|
:ivar wall_thickness: Wall thickness for pipe, cube, and brick in
|
|
176
177
|
mm (internally converted to an integer number of pixels).
|
|
@@ -192,10 +193,11 @@ class TomoSimConfig(BaseModel):
|
|
|
192
193
|
station: Literal['id1a3', 'id3a', 'id3b']
|
|
193
194
|
detector: Detector.construct()
|
|
194
195
|
sample_type: Literal[
|
|
195
|
-
'square_rod', 'square_pipe', 'hollow_cube', 'hollow_brick'
|
|
196
|
+
'square_rod', 'square_pipe', 'hollow_cube', 'hollow_brick',
|
|
197
|
+
'hollow_pyramid']
|
|
196
198
|
sample_size: conlist(
|
|
197
199
|
item_type=confloat(gt=0, allow_inf_nan=False),
|
|
198
|
-
min_items=1, max_items=
|
|
200
|
+
min_items=1, max_items=3)
|
|
199
201
|
wall_thickness: Optional[confloat(ge=0, allow_inf_nan=False)]
|
|
200
202
|
mu: Optional[confloat(gt=0, allow_inf_nan=False)] = 0.05
|
|
201
203
|
theta_step: confloat(gt=0, allow_inf_nan=False)
|
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
|
|
|
@@ -1395,28 +1393,28 @@ class Tomo:
|
|
|
1395
1393
|
|
|
1396
1394
|
# Resize the combined tomography data stacks
|
|
1397
1395
|
# - combined axis data order: row/-z,y,x
|
|
1398
|
-
if self._interactive:
|
|
1396
|
+
if self._interactive or self._save_figs:
|
|
1399
1397
|
x_bounds, y_bounds, z_bounds = self._resize_reconstructed_data(
|
|
1400
1398
|
tomo_recon_combined, combine_data=True)
|
|
1401
1399
|
else:
|
|
1402
1400
|
x_bounds = tool_config.x_bounds
|
|
1403
1401
|
if x_bounds is None:
|
|
1404
1402
|
self._logger.warning(
|
|
1405
|
-
'x_bounds unspecified,
|
|
1403
|
+
'x_bounds unspecified, combine data for full x-range')
|
|
1406
1404
|
elif not is_int_pair(
|
|
1407
1405
|
x_bounds, ge=0, le=tomo_shape[2]):
|
|
1408
1406
|
raise ValueError(f'Invalid parameter x_bounds ({x_bounds})')
|
|
1409
1407
|
y_bounds = tool_config.y_bounds
|
|
1410
1408
|
if y_bounds is None:
|
|
1411
1409
|
self._logger.warning(
|
|
1412
|
-
'y_bounds unspecified,
|
|
1410
|
+
'y_bounds unspecified, combine data for full y-range')
|
|
1413
1411
|
elif not is_int_pair(
|
|
1414
1412
|
y_bounds, ge=0, le=tomo_shape[1]):
|
|
1415
1413
|
raise ValueError(f'Invalid parameter y_bounds ({y_bounds})')
|
|
1416
1414
|
z_bounds = tool_config.z_bounds
|
|
1417
1415
|
if z_bounds is None:
|
|
1418
1416
|
self._logger.warning(
|
|
1419
|
-
'z_bounds unspecified,
|
|
1417
|
+
'z_bounds unspecified, combine data for full z-range')
|
|
1420
1418
|
elif not is_int_pair(
|
|
1421
1419
|
z_bounds, ge=0, le=tomo_shape[0]):
|
|
1422
1420
|
raise ValueError(f'Invalid parameter z_bounds ({z_bounds})')
|
|
@@ -1706,18 +1704,46 @@ class Tomo:
|
|
|
1706
1704
|
img_row_bounds = calibrate_center_rows
|
|
1707
1705
|
else:
|
|
1708
1706
|
if nxentry.instrument.source.attrs['station'] in ('id1a3', 'id3a'):
|
|
1707
|
+
# System modules
|
|
1708
|
+
from sys import float_info
|
|
1709
|
+
|
|
1710
|
+
# Third party modules
|
|
1711
|
+
from nexusformat.nexus import (
|
|
1712
|
+
NXdata,
|
|
1713
|
+
NXfield,
|
|
1714
|
+
)
|
|
1715
|
+
|
|
1716
|
+
# Local modules
|
|
1717
|
+
from CHAP.utils.fit import FitProcessor
|
|
1718
|
+
|
|
1709
1719
|
pixel_size = float(nxentry.instrument.detector.row_pixel_size)
|
|
1710
1720
|
# Try to get a fit from the bright field
|
|
1711
1721
|
row_sum = np.sum(tbf, 1)
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1722
|
+
num = len(row_sum)
|
|
1723
|
+
fit = FitProcessor()
|
|
1724
|
+
model = {'model': 'rectangle',
|
|
1725
|
+
'parameters': [
|
|
1726
|
+
{'name': 'amplitude',
|
|
1727
|
+
'value': row_sum.max()-row_sum.min(),
|
|
1728
|
+
'min': 0.0},
|
|
1729
|
+
{'name': 'center1', 'value': 0.25*num,
|
|
1730
|
+
'min': 0.0, 'max': num},
|
|
1731
|
+
{'name': 'sigma1', 'value': num/7.0,
|
|
1732
|
+
'min': float_info.min},
|
|
1733
|
+
{'name': 'center2', 'value': 0.75*num,
|
|
1734
|
+
'min': 0.0, 'max': num},
|
|
1735
|
+
{'name': 'sigma2', 'value': num/7.0,
|
|
1736
|
+
'min': float_info.min}]}
|
|
1737
|
+
bounds_fit = fit.process(
|
|
1738
|
+
NXdata(NXfield(row_sum, 'y'),
|
|
1739
|
+
NXfield(np.array(range(num)), 'x')),
|
|
1740
|
+
{'models': [model], 'method': 'trf'})
|
|
1741
|
+
parameters = bounds_fit.best_values
|
|
1716
1742
|
row_low_fit = parameters.get('center1', None)
|
|
1717
1743
|
row_upp_fit = parameters.get('center2', None)
|
|
1718
1744
|
sig_low = parameters.get('sigma1', None)
|
|
1719
1745
|
sig_upp = parameters.get('sigma2', None)
|
|
1720
|
-
have_fit = (
|
|
1746
|
+
have_fit = (bounds_fit.success and row_low_fit is not None
|
|
1721
1747
|
and row_upp_fit is not None and sig_low is not None
|
|
1722
1748
|
and sig_upp is not None
|
|
1723
1749
|
and 0 <= row_low_fit < row_upp_fit <= row_sum.size
|
|
@@ -1787,7 +1813,7 @@ class Tomo:
|
|
|
1787
1813
|
if calibrate_center_rows:
|
|
1788
1814
|
title='Select two detector image row indices to '\
|
|
1789
1815
|
'calibrate rotation axis (in range '\
|
|
1790
|
-
f'[0, {first_image.shape[0]}])'
|
|
1816
|
+
f'[0, {first_image.shape[0]-1}])'
|
|
1791
1817
|
else:
|
|
1792
1818
|
title='Select detector image row bounds for data '\
|
|
1793
1819
|
f'reduction (in range [0, {first_image.shape[0]}])'
|
|
@@ -2452,8 +2478,9 @@ class Tomo:
|
|
|
2452
2478
|
f'{selected_center_offset:.2f}.png'))
|
|
2453
2479
|
plt.close()
|
|
2454
2480
|
|
|
2481
|
+
del recon_planes
|
|
2482
|
+
|
|
2455
2483
|
del sinogram
|
|
2456
|
-
del recon_planes
|
|
2457
2484
|
|
|
2458
2485
|
# Return the center location
|
|
2459
2486
|
if self._interactive:
|
|
@@ -2834,16 +2861,16 @@ class Tomo:
|
|
|
2834
2861
|
# Selecting x an y bounds (in z-plane)
|
|
2835
2862
|
if x_bounds is None:
|
|
2836
2863
|
if not self._interactive:
|
|
2837
|
-
self._logger.warning('x_bounds unspecified,
|
|
2838
|
-
'
|
|
2864
|
+
self._logger.warning('x_bounds unspecified, use data for '
|
|
2865
|
+
'full x-range')
|
|
2839
2866
|
x_bounds = (0, tomo_recon_stacks[0].shape[2])
|
|
2840
2867
|
elif not is_int_pair(
|
|
2841
2868
|
x_bounds, ge=0, le=tomo_recon_stacks[0].shape[2]):
|
|
2842
2869
|
raise ValueError(f'Invalid parameter x_bounds ({x_bounds})')
|
|
2843
2870
|
if y_bounds is None:
|
|
2844
2871
|
if not self._interactive:
|
|
2845
|
-
self._logger.warning('y_bounds unspecified,
|
|
2846
|
-
'
|
|
2872
|
+
self._logger.warning('y_bounds unspecified, use data for '
|
|
2873
|
+
'full y-range')
|
|
2847
2874
|
y_bounds = (0, tomo_recon_stacks[0].shape[1])
|
|
2848
2875
|
elif not is_int_pair(
|
|
2849
2876
|
y_bounds, ge=0, le=tomo_recon_stacks[0].shape[1]):
|
|
@@ -2865,11 +2892,20 @@ class Tomo:
|
|
|
2865
2892
|
tomosum = 0
|
|
2866
2893
|
for i in range(num_tomo_stacks):
|
|
2867
2894
|
tomosum = tomosum + np.sum(tomo_recon_stacks[i], axis=0)
|
|
2868
|
-
|
|
2895
|
+
if self._save_figs:
|
|
2896
|
+
if combine_data:
|
|
2897
|
+
filename = os_path.join(
|
|
2898
|
+
self._outputdir, 'combined_data_xy_roi.png')
|
|
2899
|
+
else:
|
|
2900
|
+
filename = os_path.join(
|
|
2901
|
+
self._outputdir, 'reconstructed_data_xy_roi.png')
|
|
2902
|
+
else:
|
|
2903
|
+
filename = None
|
|
2904
|
+
roi = select_roi_2d(
|
|
2869
2905
|
tomosum, preselected_roi=preselected_roi,
|
|
2870
2906
|
title_a='Reconstructed data summed over z',
|
|
2871
2907
|
row_label='y', column_label='x',
|
|
2872
|
-
interactive=self._interactive)
|
|
2908
|
+
interactive=self._interactive, filename=filename)
|
|
2873
2909
|
if roi is None:
|
|
2874
2910
|
x_bounds = (0, tomo_recon_stacks[0].shape[2])
|
|
2875
2911
|
y_bounds = (0, tomo_recon_stacks[0].shape[1])
|
|
@@ -2878,12 +2914,6 @@ class Tomo:
|
|
|
2878
2914
|
y_bounds = (int(roi[2]), int(roi[3]))
|
|
2879
2915
|
self._logger.debug(f'x_bounds = {x_bounds}')
|
|
2880
2916
|
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
2917
|
|
|
2888
2918
|
# Selecting z bounds (in xy-plane)
|
|
2889
2919
|
# (only valid for a single image stack or when combining a stack)
|
|
@@ -2905,17 +2935,20 @@ class Tomo:
|
|
|
2905
2935
|
tomosum = 0
|
|
2906
2936
|
for i in range(num_tomo_stacks):
|
|
2907
2937
|
tomosum = tomosum + np.sum(tomo_recon_stacks[i], axis=(1,2))
|
|
2908
|
-
|
|
2938
|
+
if self._save_figs:
|
|
2939
|
+
if combine_data:
|
|
2940
|
+
filename = os_path.join(
|
|
2941
|
+
self._outputdir, 'combined_data_z_roi.png')
|
|
2942
|
+
else:
|
|
2943
|
+
filename = os_path.join(
|
|
2944
|
+
self._outputdir, 'reconstructed_data_z_roi.png')
|
|
2945
|
+
else:
|
|
2946
|
+
filename = None
|
|
2947
|
+
z_bounds = select_roi_1d(
|
|
2909
2948
|
tomosum, preselected_roi=z_bounds,
|
|
2910
2949
|
xlabel='z', ylabel='Reconstructed data summed over x and y',
|
|
2911
|
-
interactive=self._interactive)
|
|
2950
|
+
interactive=self._interactive, filename=filename)
|
|
2912
2951
|
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
2952
|
|
|
2920
2953
|
return x_bounds, y_bounds, z_bounds
|
|
2921
2954
|
|
|
@@ -2957,6 +2990,9 @@ class TomoSimFieldProcessor(Processor):
|
|
|
2957
2990
|
sample_size = config.sample_size
|
|
2958
2991
|
if len(sample_size) == 1:
|
|
2959
2992
|
sample_size = (sample_size[0], sample_size[0])
|
|
2993
|
+
if sample_type == 'hollow_pyramid' and len(sample_size) != 3:
|
|
2994
|
+
raise ValueError('Invalid combindation of sample_type '
|
|
2995
|
+
f'({sample_type}) and sample_size ({sample_size}')
|
|
2960
2996
|
wall_thickness = config.wall_thickness
|
|
2961
2997
|
mu = config.mu
|
|
2962
2998
|
theta_step = config.theta_step
|
|
@@ -2993,15 +3029,22 @@ class TomoSimFieldProcessor(Processor):
|
|
|
2993
3029
|
|
|
2994
3030
|
# Get the number of horizontal stacks bases on the diagonal
|
|
2995
3031
|
# of the square and for now don't allow more than one
|
|
2996
|
-
|
|
2997
|
-
|
|
3032
|
+
if (sample_size) == 3:
|
|
3033
|
+
num_tomo_stack = 1 + int(
|
|
3034
|
+
(max(sample_size[1:2])*np.sqrt(2)-pixel_size[1])
|
|
3035
|
+
/ (detector_size[1]*pixel_size[1]))
|
|
3036
|
+
else:
|
|
3037
|
+
num_tomo_stack = 1 + int((sample_size[1]*np.sqrt(2)-pixel_size[1])
|
|
3038
|
+
/ (detector_size[1]*pixel_size[1]))
|
|
2998
3039
|
if num_tomo_stack > 1:
|
|
2999
3040
|
raise ValueError('Sample is too wide for the detector')
|
|
3000
3041
|
|
|
3001
3042
|
# Create the x-ray path length through a solid square
|
|
3002
3043
|
# crosssection for a set of rotation angles.
|
|
3003
|
-
path_lengths_solid =
|
|
3004
|
-
|
|
3044
|
+
path_lengths_solid = None
|
|
3045
|
+
if sample_type != 'hollow_pyramid':
|
|
3046
|
+
path_lengths_solid = self._create_pathlength_solid_square(
|
|
3047
|
+
sample_size[1], thetas, pixel_size[1], detector_size[1])
|
|
3005
3048
|
|
|
3006
3049
|
# Create the x-ray path length through a hollow square
|
|
3007
3050
|
# crosssection for a set of rotation angles.
|
|
@@ -3027,7 +3070,12 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3027
3070
|
num_theta = len(thetas)
|
|
3028
3071
|
vertical_shifts = []
|
|
3029
3072
|
tomo_fields_stack = []
|
|
3030
|
-
|
|
3073
|
+
len_img_y = (detector_size[1]+1)//2
|
|
3074
|
+
if len_img_y%2:
|
|
3075
|
+
len_img_y = 2*len_img_y - 1
|
|
3076
|
+
else:
|
|
3077
|
+
len_img_y = 2*len_img_y
|
|
3078
|
+
img_dim = (len(img_row_coords), len_img_y)
|
|
3031
3079
|
intensities_solid = None
|
|
3032
3080
|
intensities_hollow = None
|
|
3033
3081
|
for n in range(num_tomo_stack):
|
|
@@ -3044,6 +3092,37 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3044
3092
|
beam_intensity * np.exp(-mu*path_lengths_hollow)
|
|
3045
3093
|
for n in range(num_theta):
|
|
3046
3094
|
tomo_field[n,:,:] = intensities_hollow[n]
|
|
3095
|
+
elif sample_type == 'hollow_pyramid':
|
|
3096
|
+
outer_indices = \
|
|
3097
|
+
np.where(abs(img_row_coords) <= sample_size[0]/2)[0]
|
|
3098
|
+
inner_indices = np.where(
|
|
3099
|
+
abs(img_row_coords) < sample_size[0]/2 - wall_thickness)[0]
|
|
3100
|
+
wall_indices = list(set(outer_indices)-set(inner_indices))
|
|
3101
|
+
ratio = abs(sample_size[1]-sample_size[2])/sample_size[0]
|
|
3102
|
+
baselength = max(sample_size[1:2])
|
|
3103
|
+
for i in wall_indices:
|
|
3104
|
+
path_lengths_solid = self._create_pathlength_solid_square(
|
|
3105
|
+
baselength - ratio*(
|
|
3106
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3107
|
+
thetas, pixel_size[1], detector_size[1])
|
|
3108
|
+
intensities_solid = \
|
|
3109
|
+
beam_intensity * np.exp(-mu*path_lengths_solid)
|
|
3110
|
+
for n in range(num_theta):
|
|
3111
|
+
tomo_field[n,i] = intensities_solid[n]
|
|
3112
|
+
for i in inner_indices:
|
|
3113
|
+
path_lengths_hollow = (
|
|
3114
|
+
self._create_pathlength_solid_square(
|
|
3115
|
+
baselength - ratio*(
|
|
3116
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3117
|
+
thetas, pixel_size[1], detector_size[1])
|
|
3118
|
+
- self._create_pathlength_solid_square(
|
|
3119
|
+
baselength - 2*wall_thickness - ratio*(
|
|
3120
|
+
img_row_coords[i] + 0.5*sample_size[0]),
|
|
3121
|
+
thetas, pixel_size[1], detector_size[1]))
|
|
3122
|
+
intensities_hollow = \
|
|
3123
|
+
beam_intensity * np.exp(-mu*path_lengths_hollow)
|
|
3124
|
+
for n in range(num_theta):
|
|
3125
|
+
tomo_field[n,i] = intensities_hollow[n]
|
|
3047
3126
|
else:
|
|
3048
3127
|
intensities_solid = \
|
|
3049
3128
|
beam_intensity * np.exp(-mu*path_lengths_solid)
|
|
@@ -3136,7 +3215,7 @@ class TomoSimFieldProcessor(Processor):
|
|
|
3136
3215
|
"""
|
|
3137
3216
|
# Get the column coordinates
|
|
3138
3217
|
img_y_coords = pixel_size * (0.5 * (1 - detector_size%2)
|
|
3139
|
-
+ np.asarray(range(
|
|
3218
|
+
+ np.asarray(range((detector_size+1)//2)))
|
|
3140
3219
|
|
|
3141
3220
|
# Get the path lenghts for position column coordinates
|
|
3142
3221
|
lengths = np.zeros((len(thetas), len(img_y_coords)), dtype=np.float64)
|
CHAP/utils/__init__.py
CHANGED