pyemaps 2.0.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.
- pyemaps/COPYING +798 -0
- pyemaps/CifFile/CifFile_module.py +3117 -0
- pyemaps/CifFile/StarFile.py +2496 -0
- pyemaps/CifFile/TypeContentsParser.py +82 -0
- pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
- pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
- pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
- pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
- pyemaps/CifFile/__init__.py +7 -0
- pyemaps/CifFile/drel/__init__.py +1 -0
- pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
- pyemaps/CifFile/drel/drel_lex.py +228 -0
- pyemaps/CifFile/drel/drel_runtime.py +126 -0
- pyemaps/CifFile/drel/parsetab.py +185 -0
- pyemaps/CifFile/drel/py_from_ast.py +628 -0
- pyemaps/CifFile/parsetab.py +185 -0
- pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
- pyemaps/README.md +201 -0
- pyemaps/__config__.py +91 -0
- pyemaps/__init__.py +233 -0
- pyemaps/__main__.py +219 -0
- pyemaps/__version__.py +1 -0
- pyemaps/cdata/Aluminium.xtl +4 -0
- pyemaps/cdata/AluminiumOxide.xtl +5 -0
- pyemaps/cdata/Aluminium_FCC.xtl +4 -0
- pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
- pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
- pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
- pyemaps/cdata/BiMnO3.xtl +27 -0
- pyemaps/cdata/Boron_Tetra.xtl +8 -0
- pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
- pyemaps/cdata/Chromium_BCC.xtl +4 -0
- pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
- pyemaps/cdata/CopperOxide.xtl +5 -0
- pyemaps/cdata/Copper_FCC.xtl +4 -0
- pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
- pyemaps/cdata/Diamond.xtl +4 -0
- pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
- pyemaps/cdata/FePd_Tetra.xtl +5 -0
- pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
- pyemaps/cdata/GalliumAntimonide.xtl +5 -0
- pyemaps/cdata/GalliumArsenide.xtl +5 -0
- pyemaps/cdata/GalliumNitride.xtl +5 -0
- pyemaps/cdata/Germanium.xtl +4 -0
- pyemaps/cdata/Gold_FCC.xtl +4 -0
- pyemaps/cdata/IndiumArsenide.xtl +5 -0
- pyemaps/cdata/LaMnO3.xtl +7 -0
- pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
- pyemaps/cdata/Li2MnO3.xtl +13 -0
- pyemaps/cdata/NaFeO2.xtl +6 -0
- pyemaps/cdata/Nb3Sn.xtl +5 -0
- pyemaps/cdata/Pentacene.xtl +39 -0
- pyemaps/cdata/Silicon.xtl +4 -0
- pyemaps/cdata/StrontiumTitanate.xtl +6 -0
- pyemaps/cdata/TelluriumDioxide.xtl +5 -0
- pyemaps/cdata/TinDioxide_RT.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
- pyemaps/cdata/TungstenDiselenide.xtl +5 -0
- pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
- pyemaps/cdata/ZincOxide.xtl +5 -0
- pyemaps/cdata/Zinc_HCP.xtl +4 -0
- pyemaps/cdata/ZirconiumNitride.xtl +5 -0
- pyemaps/cdata/limno2.xtl +6 -0
- pyemaps/crystals.py +1415 -0
- pyemaps/ddiffs.py +147 -0
- pyemaps/diffract/__init__.py +70 -0
- pyemaps/diffract/bloch_dec.py +779 -0
- pyemaps/diffract/csf_dec.py +242 -0
- pyemaps/diffract/dif_dec.py +519 -0
- pyemaps/diffract/dpgen_dec.py +175 -0
- pyemaps/diffract/mxtal_dec.py +201 -0
- pyemaps/diffract/powder_dec.py +137 -0
- pyemaps/diffract/stereo_dec.py +111 -0
- pyemaps/display.py +791 -0
- pyemaps/emcontrols.py +1017 -0
- pyemaps/errors.py +266 -0
- pyemaps/fileutils.py +1001 -0
- pyemaps/kdiffs.py +1221 -0
- pyemaps/license.txt +42 -0
- pyemaps/samples/adf.py +137 -0
- pyemaps/samples/al.img +0 -0
- pyemaps/samples/al_dpgen.py +57 -0
- pyemaps/samples/al_ediom.py +94 -0
- pyemaps/samples/convert_image.py +88 -0
- pyemaps/samples/dm_diff.py +225 -0
- pyemaps/samples/mask.im3 +0 -0
- pyemaps/samples/powder.py +51 -0
- pyemaps/samples/raw.img +0 -0
- pyemaps/samples/si_bloch.py +120 -0
- pyemaps/samples/si_constructor.py +51 -0
- pyemaps/samples/si_csf.py +59 -0
- pyemaps/samples/si_dif.py +134 -0
- pyemaps/samples/si_lacbed.py +59 -0
- pyemaps/samples/si_pyemaps.py +80 -0
- pyemaps/samples/si_scm.py +156 -0
- pyemaps/samples/si_stereo.py +116 -0
- pyemaps/scattering/__init__.py +26 -0
- pyemaps/scattering/sct_dec.py +31 -0
- pyemaps/spg/__init__.py +25 -0
- pyemaps/spg/spg_dec.py +60 -0
- pyemaps/stackimg.py +1031 -0
- pyemaps/xtal.py +222 -0
- pyemaps-2.0.0.dist-info/METADATA +231 -0
- pyemaps-2.0.0.dist-info/RECORD +112 -0
- pyemaps-2.0.0.dist-info/WHEEL +5 -0
- pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
- pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
- pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
- pyemaps-2.0.0.dist-info/top_level.txt +1 -0
pyemaps/stackimg.py
ADDED
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
'''
|
|
2
|
+
.. This file is part of pyEMAPS
|
|
3
|
+
|
|
4
|
+
.. ----
|
|
5
|
+
|
|
6
|
+
.. pyEMAPS is free software. You can redistribute it and/or modify
|
|
7
|
+
.. it under the terms of the GNU General Public License as published
|
|
8
|
+
.. by the Free Software Foundation, either version 3 of the License,
|
|
9
|
+
.. or (at your option) any later version..
|
|
10
|
+
|
|
11
|
+
.. pyEMAPS is distributed in the hope that it will be useful,
|
|
12
|
+
.. but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
.. GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
.. You should have received a copy of the GNU General Public License
|
|
17
|
+
.. along with pyEMAPS. If not, see `<https://www.gnu.org/licenses/>`_.
|
|
18
|
+
|
|
19
|
+
.. Contact supprort@emlabsoftware.com for any questions and comments.
|
|
20
|
+
|
|
21
|
+
.. ----
|
|
22
|
+
|
|
23
|
+
'''
|
|
24
|
+
product_name = "pyEMAPS"
|
|
25
|
+
from . import stem4d, send, ediom
|
|
26
|
+
|
|
27
|
+
from . import (E_INT, EM_INT,
|
|
28
|
+
E_FLOAT, EM_FLOAT,
|
|
29
|
+
E_DOUBLE, EM_DOUBLE,
|
|
30
|
+
|
|
31
|
+
MAX_IMAGESIZE, MIN_IMAGESIZE,
|
|
32
|
+
MAX_IMAGESTACK, MIN_IMAGESTACK,
|
|
33
|
+
DEF_FILTER_THRESHOLD, DEF_SEARCH_THRESHOLD,
|
|
34
|
+
|
|
35
|
+
DEF_RMIN, DEF_BOXSIZE,
|
|
36
|
+
DEF_CC, DEF_SIGMA, DEF_ICENTER,
|
|
37
|
+
DEF_XSCALE, DEF_TSCALE,
|
|
38
|
+
|
|
39
|
+
E_SH,
|
|
40
|
+
E_RAW,
|
|
41
|
+
E_NPY,
|
|
42
|
+
# imageloading mode
|
|
43
|
+
EL_ONE, #STEM4D image loading only one particular stack
|
|
44
|
+
EL_MORE #STEM4D image loading all stacks
|
|
45
|
+
)
|
|
46
|
+
import numpy as np
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
from .errors import XDPImageError
|
|
50
|
+
|
|
51
|
+
from .display import normalizeImage, displayXImage
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _getDateTimeString():
|
|
55
|
+
'''
|
|
56
|
+
Helper function to create a unique name for the image file name.
|
|
57
|
+
|
|
58
|
+
'''
|
|
59
|
+
from datetime import datetime
|
|
60
|
+
|
|
61
|
+
# Get the current date and time
|
|
62
|
+
current_time = datetime.now()
|
|
63
|
+
|
|
64
|
+
# Format the date-time string (up to seconds)
|
|
65
|
+
date_time_string = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
66
|
+
|
|
67
|
+
# Remove all whitespace characters using join and a generator
|
|
68
|
+
date_time_string_no_spaces = ''.join(char for char in date_time_string if not char.isspace())
|
|
69
|
+
|
|
70
|
+
print("Current date-time string without spaces:", date_time_string_no_spaces)
|
|
71
|
+
|
|
72
|
+
return date_time_string_no_spaces
|
|
73
|
+
|
|
74
|
+
class StackImage():
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
Simple wrapper class for experimental images used in pyemaps
|
|
78
|
+
Stem4d analysis:
|
|
79
|
+
|
|
80
|
+
- Diffraction pattern indexing
|
|
81
|
+
- Annular Drak Field image generation
|
|
82
|
+
- Masked images
|
|
83
|
+
|
|
84
|
+
All image files must be of raw bitmap format, or known as headered raw format:
|
|
85
|
+
|
|
86
|
+
- **Header**: stores metadata about the image, often including three numbers
|
|
87
|
+
for dimensions (e.g. width, height, and number of bit depth, or stacks).
|
|
88
|
+
|
|
89
|
+
- **Data Offset**: After the header, the actual image or pixel data starts.
|
|
90
|
+
|
|
91
|
+
- **Pixel Data**: Stored in a row-major order, meaning pixels are arranged
|
|
92
|
+
row-byrow across columns.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
The image can also be represented by a numpy array directly. In this case,
|
|
96
|
+
the StackImage object created will ignore its file name member.
|
|
97
|
+
|
|
98
|
+
and more to come.
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
def __init__(self,
|
|
102
|
+
imgfn,
|
|
103
|
+
nformat=E_SH,
|
|
104
|
+
ndtype = E_FLOAT,
|
|
105
|
+
dim = (MIN_IMAGESIZE,
|
|
106
|
+
MIN_IMAGESIZE,
|
|
107
|
+
MIN_IMAGESTACK),
|
|
108
|
+
noffset = 8):
|
|
109
|
+
|
|
110
|
+
self._data = None # current image data
|
|
111
|
+
self._npdata = None # numpy input image (E_NPY)
|
|
112
|
+
setattr(self, 'nformat', nformat) # image file type
|
|
113
|
+
setattr(self, 'fname', imgfn) # image file name
|
|
114
|
+
setattr(self, 'noffset', noffset) # image header offset to image data
|
|
115
|
+
setattr(self, 'ndtype', ndtype) # image data offset
|
|
116
|
+
setattr(self, 'dim', dim) # dimension of the image
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def fname(self):
|
|
120
|
+
"""
|
|
121
|
+
Experimental diffraction pattern image input file full path.
|
|
122
|
+
In case of numpy array, this field is ignored.
|
|
123
|
+
"""
|
|
124
|
+
return self._fname
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def nformat(self):
|
|
128
|
+
"""
|
|
129
|
+
The type of the image file
|
|
130
|
+
|
|
131
|
+
- E_RAW: raw image files with image data at an `noffset` from the beginning of the file.
|
|
132
|
+
- E_SH: small header (pyEMAPS propietory). Header is a tuple of three short integers
|
|
133
|
+
and data type represented also by a short integer. Total bytes of the header or the offset
|
|
134
|
+
is 8)
|
|
135
|
+
- E_NPY: numpy array.
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
return self._nformat
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def ndtype(self):
|
|
142
|
+
"""
|
|
143
|
+
The type of the image data - int, float, double are supported.
|
|
144
|
+
The constants to used to represent the types are:
|
|
145
|
+
|
|
146
|
+
- E_INT and EM_INT: single and multiple stack integral image data type.
|
|
147
|
+
- E_FLOAT and EM_FLOAT: single and multiple stack real image data type.
|
|
148
|
+
- E_DOUBLE and EM_DOUBLE: single and multiple stack 8-byte real image data type.
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
return self._ndtype
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def noffset(self):
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
image data offset from start of the file. Normally, it is the size of the
|
|
158
|
+
header in bytes.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
return self._noffset
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def dim(self):
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
image dimension, support 3 dimensional images with input of
|
|
168
|
+
3 integer tuple of integers (width, height, stack).
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
return self._dim
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def data(self):
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
Raw image data in numpy ndarray when it is read from a numpy array
|
|
178
|
+
Otherwise, the data will be generated from Stem4d when the input image
|
|
179
|
+
file is loaded.
|
|
180
|
+
|
|
181
|
+
With `nformat` set to E_NPY, set this to a 2-D numpy array of any
|
|
182
|
+
boolean, integer or floating-point type, 100 to 512 pixels per side,
|
|
183
|
+
before calling `loadImage`. It is converted to float32 when loaded.
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
return self._data
|
|
187
|
+
|
|
188
|
+
@fname.setter
|
|
189
|
+
def fname(self, fn):
|
|
190
|
+
if self._nformat != E_NPY:
|
|
191
|
+
if not isinstance(fn, str) or len(fn) == 0:
|
|
192
|
+
raise XDPImageError('Input image file name invalid')
|
|
193
|
+
|
|
194
|
+
self._fname = fn
|
|
195
|
+
|
|
196
|
+
@nformat.setter
|
|
197
|
+
def nformat(self, fmt):
|
|
198
|
+
if not isinstance(fmt, int):
|
|
199
|
+
raise XDPImageError('Invalid image file type')
|
|
200
|
+
|
|
201
|
+
if fmt != E_SH and fmt != E_RAW and fmt != E_NPY:
|
|
202
|
+
raise XDPImageError("Unsuported image file type")
|
|
203
|
+
|
|
204
|
+
self._nformat = fmt
|
|
205
|
+
|
|
206
|
+
@ndtype.setter
|
|
207
|
+
def ndtype(self, dty):
|
|
208
|
+
if not isinstance(dty, int):
|
|
209
|
+
raise XDPImageError('Invalid image data type')
|
|
210
|
+
|
|
211
|
+
if dty != E_INT and dty != EM_INT and \
|
|
212
|
+
dty != E_FLOAT and dty != EM_FLOAT and \
|
|
213
|
+
dty != E_DOUBLE and dty != EM_DOUBLE:
|
|
214
|
+
raise XDPImageError("Unsuported image data type")
|
|
215
|
+
|
|
216
|
+
self._ndtype = dty
|
|
217
|
+
|
|
218
|
+
@noffset.setter
|
|
219
|
+
def noffset(self, offset):
|
|
220
|
+
if not isinstance(offset, int):
|
|
221
|
+
raise XDPImageError('Invalid image data offset')
|
|
222
|
+
|
|
223
|
+
self._noffset = offset
|
|
224
|
+
|
|
225
|
+
@dim.setter
|
|
226
|
+
def dim(self, d):
|
|
227
|
+
if not isinstance(d, tuple) or len(d) !=3 or \
|
|
228
|
+
not all(isinstance(x, (int, float)) for x in d) or \
|
|
229
|
+
d[0] < MIN_IMAGESIZE or d[1] < MIN_IMAGESIZE or d[2] < MIN_IMAGESTACK or \
|
|
230
|
+
d[0] > MAX_IMAGESIZE or d[1] > MAX_IMAGESIZE or d[2] > MAX_IMAGESTACK:
|
|
231
|
+
|
|
232
|
+
raise XDPImageError('Invalid image data offset')
|
|
233
|
+
|
|
234
|
+
self._dim = d
|
|
235
|
+
|
|
236
|
+
@data.setter
|
|
237
|
+
def data(self, idata):
|
|
238
|
+
if not isinstance(idata, np.ndarray):
|
|
239
|
+
raise XDPImageError('Raw image data must be a numpy array')
|
|
240
|
+
|
|
241
|
+
# loadImage reads the input from _npdata; _data is replaced by
|
|
242
|
+
# whichever image is loaded or generated later
|
|
243
|
+
self._npdata = idata
|
|
244
|
+
self._data = idata
|
|
245
|
+
|
|
246
|
+
def __del__(self):
|
|
247
|
+
|
|
248
|
+
stem4d.freeStem4d()
|
|
249
|
+
|
|
250
|
+
def loadImage(self, rmode= EL_ONE, stack = 1):
|
|
251
|
+
"""
|
|
252
|
+
Loads image into stem4d module for analysis.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
img_format = self.nformat
|
|
256
|
+
if img_format != E_NPY and img_format != E_SH and img_format != E_RAW:
|
|
257
|
+
raise XDPImageError("Invalid image file type")
|
|
258
|
+
|
|
259
|
+
ximage = stem4d.cvar.ximg
|
|
260
|
+
ret = -1
|
|
261
|
+
if img_format == E_NPY:
|
|
262
|
+
if self._npdata is None:
|
|
263
|
+
raise XDPImageError("No image data: set the data property to a 2-D numpy array before loading")
|
|
264
|
+
try:
|
|
265
|
+
ret = ximage.setImage_from_numpy(self._npdata)
|
|
266
|
+
except (TypeError, ValueError, MemoryError) as e:
|
|
267
|
+
raise XDPImageError(f"Invalid numpy image: {e}") from e
|
|
268
|
+
|
|
269
|
+
# load proprietory small header formatted image file
|
|
270
|
+
if img_format == E_SH:
|
|
271
|
+
ret = stem4d.loadXImage(self.fname, rmode = rmode, stack = stack)
|
|
272
|
+
|
|
273
|
+
# load raw image file
|
|
274
|
+
if img_format == E_RAW:
|
|
275
|
+
ret = stem4d.loadRawImage(self.fname,
|
|
276
|
+
self.noffset, self.ndtype,
|
|
277
|
+
self.dim[0], self.dim[1], self.dim[2],
|
|
278
|
+
rmode = rmode, stack=stack)
|
|
279
|
+
|
|
280
|
+
# spell out license issues here before bailing out
|
|
281
|
+
if ret == stem4d.LICENSE_EXPIRED:
|
|
282
|
+
print(f"Your {product_name} license has expired. ")
|
|
283
|
+
print("Upgrade your license by purchasing a full license token by contacting support@emlabsoftware.com.")
|
|
284
|
+
print("Contact support@emlabsoftware.com if you think you have a valid license and the problem persists.")
|
|
285
|
+
elif ret == stem4d.LICENSE_NONEXISTENT:
|
|
286
|
+
print(f"Fail to find {product_name} license.")
|
|
287
|
+
print("Contact support@emlabsoftware.com if you think you have a valid license and the problem persists.")
|
|
288
|
+
print(f"Run \"pyemaps -l trial\" to obtain a free 7-day trial {product_name} license.")
|
|
289
|
+
print(f"Or, contact support@emlabsoftware.com to purchase a {product_name} license activation token.")
|
|
290
|
+
elif ret == stem4d.LICENSE_OTHERS:
|
|
291
|
+
print(f'Unknown error retrieving {product_name} license info!')
|
|
292
|
+
print(f'If persists, contact support@emlabsoftware.com')
|
|
293
|
+
|
|
294
|
+
if ret != 0:
|
|
295
|
+
raise XDPImageError("Failed to import the image into pyEMAPS")
|
|
296
|
+
|
|
297
|
+
self._dim = (ximage.h.ncol,ximage.h.nrow,ximage.h.nlayer)
|
|
298
|
+
self._data = ximage.getImageData(stack)
|
|
299
|
+
|
|
300
|
+
def viewExpImage(self, peakDP = False, iStack=1, title = "", iShow = False):
|
|
301
|
+
"""
|
|
302
|
+
Helper function in stem4d module to display experimental diffraction database
|
|
303
|
+
diffraction pattern currently loaded.
|
|
304
|
+
|
|
305
|
+
:param peakDP: kinematic diffraction with miller index. This parameter is only valid for EDIOM operations
|
|
306
|
+
:type peakDP: Boolean, optional
|
|
307
|
+
|
|
308
|
+
:param Indexed: show the kinematic patterns after indexing is completed.
|
|
309
|
+
:type Indexed: boolean, optional, default to `False`
|
|
310
|
+
|
|
311
|
+
:param iStack: Image stack number to show. For single stack image, this parameter takes default value of 1
|
|
312
|
+
:type iStack: integer, optional, default to 1
|
|
313
|
+
|
|
314
|
+
:param iShow: whether to show miller indexes
|
|
315
|
+
:type iShow: boolean, optional, default to `False`
|
|
316
|
+
|
|
317
|
+
:return:
|
|
318
|
+
:rtype: None
|
|
319
|
+
|
|
320
|
+
This function is typically used to display experimental image after diffraction
|
|
321
|
+
patterns matched and indexed. It can also be used to display intermediate
|
|
322
|
+
search results after peaks are found but before indexes are matched. The latter
|
|
323
|
+
display is turned on only by *bDebug* flag in
|
|
324
|
+
`loadDPDB <pyemaps.crystals.html#pyemaps.crystals.Crystal.loadDPDB>`_ call.
|
|
325
|
+
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
xnc, xnr, nl = self._dim[0], self._dim[1], self._dim[2]
|
|
329
|
+
|
|
330
|
+
if iStack < 1 or iStack > nl+1:
|
|
331
|
+
print(f'The stack number of the image is invalid')
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
if self._data is None:
|
|
335
|
+
print(f'Experimental image is not loaded or invalid')
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
yi = np.ascontiguousarray(self._data,
|
|
340
|
+
dtype=np.float32).reshape(xnc, xnr)
|
|
341
|
+
except Exception as e:
|
|
342
|
+
print("Error converting image data to numpy array")
|
|
343
|
+
raise XDPImageError from e
|
|
344
|
+
else:
|
|
345
|
+
xkdisk = None
|
|
346
|
+
if peakDP:
|
|
347
|
+
nps = ediom.getExpImagePeaks()
|
|
348
|
+
|
|
349
|
+
if nps <= 0:
|
|
350
|
+
print(f'Image is not indexed or indexing data is invalid')
|
|
351
|
+
return
|
|
352
|
+
|
|
353
|
+
ret, xkdisk = ediom.getXKDif(nps+1)
|
|
354
|
+
|
|
355
|
+
if ret != 0:
|
|
356
|
+
print(f'Python ----Failed to get diffraction patterns for the experimental image')
|
|
357
|
+
return ret
|
|
358
|
+
|
|
359
|
+
displayXImage(yi,
|
|
360
|
+
fsize=(xnc, xnr),
|
|
361
|
+
iShow = iShow,
|
|
362
|
+
ds = xkdisk,
|
|
363
|
+
suptitle = title)
|
|
364
|
+
@staticmethod
|
|
365
|
+
def showMatchedDBDP():
|
|
366
|
+
"""
|
|
367
|
+
Helper function in stem4d module to display diffraction database diffraction pattern
|
|
368
|
+
that best matches that of the experimental diffraction image pattern.
|
|
369
|
+
|
|
370
|
+
This function is typically used to confirm its match with experimental diffraction
|
|
371
|
+
pattern. See sample code *al_ediom.py* for usage of this function.
|
|
372
|
+
|
|
373
|
+
"""
|
|
374
|
+
nr, nc = MAX_IMAGESIZE, MAX_IMAGESIZE
|
|
375
|
+
|
|
376
|
+
img1d = np.ascontiguousarray(np.zeros(shape=(nc,nr)), dtype=np.float32)
|
|
377
|
+
|
|
378
|
+
nPeaks = ediom.DPGetCurrentImage(img1d)
|
|
379
|
+
if nPeaks <=0:
|
|
380
|
+
print(f'Python ----Failed to get selected database image')
|
|
381
|
+
return -1
|
|
382
|
+
|
|
383
|
+
if not normalizeImage(img1d):
|
|
384
|
+
print("Python ----Error normalizing the image for display")
|
|
385
|
+
return -1
|
|
386
|
+
|
|
387
|
+
knum = nPeaks+1
|
|
388
|
+
ret, kdisk = ediom.getDPListKDif(knum)
|
|
389
|
+
|
|
390
|
+
if ret != 0:
|
|
391
|
+
print(f'Python ----Failed to get diffraction disks list')
|
|
392
|
+
exit(1)
|
|
393
|
+
displayXImage(img1d,
|
|
394
|
+
fsize=(nc,nr),
|
|
395
|
+
iShow=True,
|
|
396
|
+
ds=kdisk,
|
|
397
|
+
suptitle = 'Matching Database Diffraction Pattern')
|
|
398
|
+
|
|
399
|
+
staticmethod
|
|
400
|
+
def displayFitImage(nr, nc):
|
|
401
|
+
"""
|
|
402
|
+
Internal intermediate helper function for display fit images.
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
filen = nr*nc
|
|
406
|
+
ret, fimg = ediom.getFitImage(filen)
|
|
407
|
+
if ret != 0:
|
|
408
|
+
print(f'Python ----Failed to get fit image')
|
|
409
|
+
return ret
|
|
410
|
+
|
|
411
|
+
displayXImage(fimg.reshape(nr, nc), fsize=(nr, nc))
|
|
412
|
+
return 0
|
|
413
|
+
|
|
414
|
+
@staticmethod
|
|
415
|
+
def displaySearchMask(nr, nc):
|
|
416
|
+
"""
|
|
417
|
+
Internal stem4d helper function displaying image mask image
|
|
418
|
+
generated in peak search.
|
|
419
|
+
|
|
420
|
+
:return:
|
|
421
|
+
:rtype: None
|
|
422
|
+
|
|
423
|
+
"""
|
|
424
|
+
len = nr*nc
|
|
425
|
+
ret = 1
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
ret, img1d = ediom.getXMask(len)
|
|
429
|
+
|
|
430
|
+
if ret != 0 :
|
|
431
|
+
print(f'Python ----Error getting current image slice')
|
|
432
|
+
|
|
433
|
+
displayXImage(img1d.reshape((nr,nc)), fsize = (nr,nc), suptitle='Search Mask')
|
|
434
|
+
return 0
|
|
435
|
+
|
|
436
|
+
@staticmethod
|
|
437
|
+
def displaySearchKernel(nr, nc):
|
|
438
|
+
"""
|
|
439
|
+
Internal stem4d helper function displaying image kernel image
|
|
440
|
+
generated in peak search.
|
|
441
|
+
|
|
442
|
+
:return:
|
|
443
|
+
:rtype: None
|
|
444
|
+
|
|
445
|
+
"""
|
|
446
|
+
len = nr*nc
|
|
447
|
+
ret = -1
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
ret, img1d = ediom.getXTemplateMask(len)
|
|
451
|
+
|
|
452
|
+
if ret != 0 :
|
|
453
|
+
print(f'Python ----Error getting current image slice')
|
|
454
|
+
return -1
|
|
455
|
+
|
|
456
|
+
displayXImage(img1d.reshape((nr,nc)),
|
|
457
|
+
fsize = (nr,nc),
|
|
458
|
+
isMask=True,
|
|
459
|
+
suptitle = 'Search Kernel')
|
|
460
|
+
return 0
|
|
461
|
+
|
|
462
|
+
@staticmethod
|
|
463
|
+
def showDPDBMMask(mr, mc):
|
|
464
|
+
|
|
465
|
+
"""
|
|
466
|
+
Internal function to display loaded DP database stereo projection map
|
|
467
|
+
as explained in the following
|
|
468
|
+
|
|
469
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/stereoprojectionmap.png
|
|
470
|
+
:width: 75%
|
|
471
|
+
:align: center
|
|
472
|
+
|
|
473
|
+
"""
|
|
474
|
+
len = mr*mc
|
|
475
|
+
|
|
476
|
+
mask = np.ascontiguousarray(np.zeros(len), dtype=np.short)
|
|
477
|
+
|
|
478
|
+
ret= ediom.DPGetMask(mask)
|
|
479
|
+
|
|
480
|
+
if ret != 0:
|
|
481
|
+
print(f'Python ----Error getting the mask data')
|
|
482
|
+
return -1
|
|
483
|
+
|
|
484
|
+
displayXImage(mask.reshape(mr,mc),
|
|
485
|
+
fsize =(mr, mc),
|
|
486
|
+
isMask = True,
|
|
487
|
+
suptitle="Stereo Projection Map")
|
|
488
|
+
|
|
489
|
+
@staticmethod
|
|
490
|
+
def loadDPDB(dbfn, bShowDBMap=False):
|
|
491
|
+
"""
|
|
492
|
+
Loads the diffraction database file *dbfn* into stem4d for diffraction peak searching and indexing.
|
|
493
|
+
|
|
494
|
+
See sample code *si_dpgen.py* for how to generate a diffraction pattern database with
|
|
495
|
+
pyemaps `generateDPDB <pyemaps.crystals.html#pyemaps.crystals.Crystal.generateDPDB>`_.
|
|
496
|
+
|
|
497
|
+
The database file can also be saved and reused.
|
|
498
|
+
|
|
499
|
+
:param dbfn: DP database file name.
|
|
500
|
+
:type dbfn: string, required
|
|
501
|
+
|
|
502
|
+
:param bShowDBMap: weather to display the stereodigram project map after successful
|
|
503
|
+
loading of the database file
|
|
504
|
+
|
|
505
|
+
:type bShowDBMap: boolean, optional, default to `False`
|
|
506
|
+
|
|
507
|
+
:return: status code, stereo projection map dimension - two integers tuple
|
|
508
|
+
:rtype: tuple
|
|
509
|
+
|
|
510
|
+
See the following for stereo projection map representing the DP database:
|
|
511
|
+
|
|
512
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/stereoprojectionmap.png
|
|
513
|
+
:width: 75%
|
|
514
|
+
:align: center
|
|
515
|
+
|
|
516
|
+
"""
|
|
517
|
+
# load DP database into stem4d and return database fitmap dimensions
|
|
518
|
+
ret, mrow, mcol = ediom.readDPDB(dbfn)
|
|
519
|
+
if ret != 0: #need to replace this with stem4d return value
|
|
520
|
+
print(f'Error loading image file')
|
|
521
|
+
return ret, 0, 0
|
|
522
|
+
|
|
523
|
+
print(f'Diffraction Database loaded successfully.')
|
|
524
|
+
print(f'Dimension of stereo projection map: width ={mcol}, height={mrow}')
|
|
525
|
+
|
|
526
|
+
if bShowDBMap:
|
|
527
|
+
StackImage.showDPDBMMask(mrow, mcol)
|
|
528
|
+
|
|
529
|
+
return ret, mrow, mcol
|
|
530
|
+
|
|
531
|
+
@staticmethod
|
|
532
|
+
def showMatchingIndexMap(fr, fc):
|
|
533
|
+
|
|
534
|
+
"""
|
|
535
|
+
Helper function in stem4d module to display matching indexes map. The colored
|
|
536
|
+
heat map with the peak indicates the orientation in which experimental and
|
|
537
|
+
database diffraction patterns best match.
|
|
538
|
+
|
|
539
|
+
:param fr: stereo projection map height. This number is obtained from
|
|
540
|
+
`loadDPDB <pyemaps.crystals.html#pyemaps.crystals.Crystal.loadDPDB>`_ call.
|
|
541
|
+
:type fr: int, required
|
|
542
|
+
|
|
543
|
+
:param fc: stereo projection map width. This number is obtained from
|
|
544
|
+
`loadDPDB <pyemaps.crystals.html#pyemaps.crystals.Crystal.loadDPDB>`_ call.
|
|
545
|
+
:type: int, required
|
|
546
|
+
|
|
547
|
+
:return: status code
|
|
548
|
+
:rtype: int
|
|
549
|
+
|
|
550
|
+
"""
|
|
551
|
+
|
|
552
|
+
flen = fc*fr
|
|
553
|
+
|
|
554
|
+
ret, fmask = ediom.getFitMap(flen)
|
|
555
|
+
if ret != 0:
|
|
556
|
+
print(f'Python ----Failed to get fit map')
|
|
557
|
+
return ret
|
|
558
|
+
|
|
559
|
+
displayXImage(fmask.reshape(fr,fc),
|
|
560
|
+
fsize =(fr, fc),
|
|
561
|
+
isMask = True,
|
|
562
|
+
bColor=True,
|
|
563
|
+
suptitle = 'Matching Index Map')
|
|
564
|
+
|
|
565
|
+
def indexImage(self,
|
|
566
|
+
dpdbfn,
|
|
567
|
+
cc = DEF_CC,
|
|
568
|
+
sigma = DEF_SIGMA,
|
|
569
|
+
img_center = DEF_ICENTER,
|
|
570
|
+
rmin = DEF_RMIN,
|
|
571
|
+
search_box = DEF_BOXSIZE,
|
|
572
|
+
scaling_option = (DEF_XSCALE, DEF_TSCALE),
|
|
573
|
+
filter_threshold = DEF_FILTER_THRESHOLD,
|
|
574
|
+
peak_threshold = DEF_SEARCH_THRESHOLD,
|
|
575
|
+
ssel = 1,
|
|
576
|
+
bDebug = False
|
|
577
|
+
):
|
|
578
|
+
"""
|
|
579
|
+
Searches and indexes an experimental diffraction image. The experimental diffraction image will be loaded
|
|
580
|
+
with StackImage class constructor and its `loadImage <pyemaps.stackimg.html#pyemaps.stackimg.StackImage.loadImage>`_
|
|
581
|
+
method before the search and indexes are performed.
|
|
582
|
+
|
|
583
|
+
In addition, a diffraction database also needs to be loaded preceeding this call with
|
|
584
|
+
`loadDPDB <pyemaps.crystals.html#pyemaps.crystals.Crystal.loadDPDB>`_
|
|
585
|
+
|
|
586
|
+
:param dpdbfn: diffraction pattern database file name. The file is generated by pyemaps dp_gen module
|
|
587
|
+
or database file name saved from previous runs.
|
|
588
|
+
:type dpdbfn: string, required
|
|
589
|
+
|
|
590
|
+
:param cc: Camera constant in 1/Angstrom/pixel.
|
|
591
|
+
:type cc: float, optional
|
|
592
|
+
|
|
593
|
+
:param sigma: Diffraction peak width - estimated image peak width.
|
|
594
|
+
:type sigma: float, optional.
|
|
595
|
+
|
|
596
|
+
:param img_center: diffraction pattern center in (col, row) coordinate.
|
|
597
|
+
:type img_center: float tuple, optional
|
|
598
|
+
|
|
599
|
+
:param rmin: low cutoff radius for excluding center beam and diffuse intensities.
|
|
600
|
+
:type rmin: float, optional
|
|
601
|
+
|
|
602
|
+
:param search_box: Parameter in pixels used to define peak search. Recommended value
|
|
603
|
+
of 3 to 4 times of sigma value for selected area diffraction.
|
|
604
|
+
:type search_box: float, optional
|
|
605
|
+
|
|
606
|
+
:param filter_threshold: Diffraction pattern screening threshold. Between 0.0 and 1.0
|
|
607
|
+
use 0 to include all database patterns for search.
|
|
608
|
+
:type filter_threshold: float, optional
|
|
609
|
+
|
|
610
|
+
:param peak_threshold: Peak threshold. value between 0.0 and 1.0. used to remove noisy peaks.
|
|
611
|
+
:type peak_threshold: float, optional
|
|
612
|
+
|
|
613
|
+
:param ssel: image stack selection. The value must be in range of the input image layers.
|
|
614
|
+
:type ssel: integer, optional. Default value to 1, the first image layer in the input image stack.
|
|
615
|
+
|
|
616
|
+
:param bDebug: debug option. This is for displaying intermediate searching and masking images used.
|
|
617
|
+
:type bDebug: Boolean, optional. Default value to `False`.
|
|
618
|
+
|
|
619
|
+
:return: status code, 0 for successful run
|
|
620
|
+
:rtype: integer
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
The image control parameters cc - rmin above are image meaurements controls. The accuraccies
|
|
624
|
+
of these measurements can greatly affect the peak search and eventually indexing results.
|
|
625
|
+
|
|
626
|
+
The remaining input parameters directly control the search and indexing functions and its results.
|
|
627
|
+
Use these parameters to tune your results.
|
|
628
|
+
|
|
629
|
+
The following is a typical sequence of displays during the execution:
|
|
630
|
+
|
|
631
|
+
1. Once the DP database is loaded successfully, the steoreo projection map associated
|
|
632
|
+
with the DP database will be shown.
|
|
633
|
+
|
|
634
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/stereoprojmap.png
|
|
635
|
+
:width: 75%
|
|
636
|
+
:align: center
|
|
637
|
+
|
|
638
|
+
2. Successful experimental DP image importing will also show the image. This gives users
|
|
639
|
+
the opportunity to verify the image:
|
|
640
|
+
|
|
641
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/expimagoriginal.png
|
|
642
|
+
:width: 75%
|
|
643
|
+
:align: center
|
|
644
|
+
|
|
645
|
+
3. Following the display of the experimental DP image and if *bDebug* is not set (by default),
|
|
646
|
+
the indexing result will be displayed on top of the experimental image:
|
|
647
|
+
|
|
648
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/expimgindexed.png
|
|
649
|
+
:width: 75%
|
|
650
|
+
:align: center
|
|
651
|
+
|
|
652
|
+
4. Once the indexing results are generated, users have the option to call more display
|
|
653
|
+
*STEM4D* functions to show other results generated by this call. For examples,
|
|
654
|
+
the matching database DP image and DP database matching index map as shown below:
|
|
655
|
+
|
|
656
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/matchingDPDB.png
|
|
657
|
+
:width: 75%
|
|
658
|
+
:align: center
|
|
659
|
+
|
|
660
|
+
.. image:: https://github.com/emlab-solutions/imagepypy/raw/main/matchingindexmap.png
|
|
661
|
+
:width: 75%
|
|
662
|
+
:align: center
|
|
663
|
+
|
|
664
|
+
The latter map presents the likely matching location (in red) between the DP in database,
|
|
665
|
+
also considered the theoretical diffraction patterns, and the experimental DP image.
|
|
666
|
+
|
|
667
|
+
5. Finally, in addition to the above visual representation of the run and results,
|
|
668
|
+
more verbose results are also shown in standard output:
|
|
669
|
+
|
|
670
|
+
.. code-block:: console
|
|
671
|
+
|
|
672
|
+
Number of peaks found: 45
|
|
673
|
+
|
|
674
|
+
-----------------Diffraction Pattern(DP) Indexing Results--------------------
|
|
675
|
+
***** DP stack loaded and indexed: 1
|
|
676
|
+
***** Best matching database DP: 1715
|
|
677
|
+
***** DP rotation angle: 284.191 (degrees)
|
|
678
|
+
***** Number of diffraction peaks indexed: 11
|
|
679
|
+
***** Experimental and database DP correlation maximum: 0.988308
|
|
680
|
+
***** Beam direction: 0.0575418 0.0599394 0.232559
|
|
681
|
+
***** Reliability index (> 5.0): 30.5407
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
-----------------Indexed Diffraction Pattern Details------------------
|
|
685
|
+
DP # Miller Index Distance DP Loc Intensity
|
|
686
|
+
1 2 -2 0 18.805359 114.912613 109.021492 14.05505
|
|
687
|
+
2 -2 2 0 20.888754 81.346603 87.833191 100.00000
|
|
688
|
+
3 3 1 -1 22.201900 95.978333 120.995316 28.63353
|
|
689
|
+
4 -1 -3 1 22.830843 117.979164 86.309898 9.12775
|
|
690
|
+
5 -3 -1 1 23.202806 101.336617 75.915146 17.33628
|
|
691
|
+
6 1 3 -1 23.803307 78.843224 111.661034 97.01090
|
|
692
|
+
7 5 -1 -1 36.265705 112.098000 132.817802 2.99402
|
|
693
|
+
8 -5 1 1 36.616173 85.120682 65.116249 4.92946
|
|
694
|
+
9 -1 5 -1 36.805595 62.239502 100.821404 33.52511
|
|
695
|
+
10 -4 4 0 39.269745 66.114388 77.537254 18.77301
|
|
696
|
+
11 4 4 -2 42.499973 76.063011 134.779076 2.83128
|
|
697
|
+
|
|
698
|
+
where Reliability index greater than 5.0 is considered successful run. DP stands for
|
|
699
|
+
diffraction pattern. If your results do not fit in a run, adjusting search and indexing paramemers
|
|
700
|
+
such as filter_threshold and peak threshold can also help stem4d module to search and match peaks.
|
|
701
|
+
|
|
702
|
+
.. note::
|
|
703
|
+
|
|
704
|
+
When *bDebug* flag is set (to 'True'), users are offered to chance to display more information of
|
|
705
|
+
stem4d's intermediate execution steps. For example, peak discovery step will show results
|
|
706
|
+
of all found peaks by image peak matching algorithms before indexing is done.
|
|
707
|
+
These intermediate images are mainly for debugging purposes.
|
|
708
|
+
|
|
709
|
+
"""
|
|
710
|
+
|
|
711
|
+
if scaling_option[0] < 0 or scaling_option[0] > 5:
|
|
712
|
+
raise XDPImageError('Experimental scaling factor out of range')
|
|
713
|
+
|
|
714
|
+
if scaling_option[1] < 0 or scaling_option[1] > 5:
|
|
715
|
+
raise XDPImageError('Theoretical scaling factor out of range')
|
|
716
|
+
|
|
717
|
+
soption = scaling_option[0] * 10 + scaling_option[1]
|
|
718
|
+
if soption == 0:
|
|
719
|
+
raise XDPImageError('Both scaling factors out of range')
|
|
720
|
+
|
|
721
|
+
# loading theoretical diffraction image database
|
|
722
|
+
# generated from pyemaps dpgen module
|
|
723
|
+
ret, mr, mc = StackImage.loadDPDB(dpdbfn, bShowDBMap=True)
|
|
724
|
+
if ret != 0:
|
|
725
|
+
raise XDPImageError("Error loading theoretical diffraction pattern datanase")
|
|
726
|
+
|
|
727
|
+
try:
|
|
728
|
+
self.loadImage(rmode = 1, stack = ssel) #indexing one stack image at a time
|
|
729
|
+
except Exception as e:
|
|
730
|
+
raise XDPImageError(f"Error loading image for stem4d analysis: {e}") from e
|
|
731
|
+
|
|
732
|
+
self.viewExpImage(title='Experimental Diffraction Image - input')
|
|
733
|
+
|
|
734
|
+
edc = stem4d.cvar.edc
|
|
735
|
+
|
|
736
|
+
# setting image control parameters
|
|
737
|
+
edc.cc = cc
|
|
738
|
+
edc.sigma = sigma
|
|
739
|
+
|
|
740
|
+
# validate image center
|
|
741
|
+
|
|
742
|
+
if img_center[0] < 0 or img_center[0] > self.dim[0] or \
|
|
743
|
+
img_center[1] < 0 or img_center[1] >self.dim[1]:
|
|
744
|
+
raise ValueError('Point of interest on Experimental image invalid')
|
|
745
|
+
|
|
746
|
+
edc.set_center(img_center[0], img_center[1])
|
|
747
|
+
|
|
748
|
+
# load stem4d control parameters with which the image is generated
|
|
749
|
+
# and search and return search kernel dimensions
|
|
750
|
+
ret, kc, kr = stem4d.prepareSearch(img_center[0], img_center[1], rmin)
|
|
751
|
+
if ret != 0 or kc <= 0 or kr <= 0:
|
|
752
|
+
print(f'Error preparing for experimental image peak search')
|
|
753
|
+
return -1
|
|
754
|
+
|
|
755
|
+
if bDebug:
|
|
756
|
+
StackImage.displaySearchMask(self._dim[1], self._dim[0])
|
|
757
|
+
StackImage.displaySearchKernel(kr, kc)
|
|
758
|
+
|
|
759
|
+
# Start peaks search...
|
|
760
|
+
xpeaks = ediom.searchXPeaks(search_box, threshold=peak_threshold)
|
|
761
|
+
|
|
762
|
+
if xpeaks == -1:
|
|
763
|
+
print(f'Peaks search failed')
|
|
764
|
+
return -1
|
|
765
|
+
|
|
766
|
+
if xpeaks <= 3:
|
|
767
|
+
print(f'Insufficient number of peaks found for indexing')
|
|
768
|
+
return -1
|
|
769
|
+
|
|
770
|
+
if bDebug:
|
|
771
|
+
self.viewExpImage(peakDP=True, title = 'Experimental Diffraction Image', iShow = False)
|
|
772
|
+
|
|
773
|
+
ret = ediom.indexXPeaks(rmin, soption, filter_threshold)
|
|
774
|
+
|
|
775
|
+
if ret != 0:
|
|
776
|
+
print(f'Error indexing experimental image for DP')
|
|
777
|
+
return ret
|
|
778
|
+
|
|
779
|
+
self.viewExpImage(peakDP=True, title = 'Experimental Diffraction Image - Indexed', iShow = True)
|
|
780
|
+
|
|
781
|
+
StackImage.showMatchingIndexMap(mr,mc)
|
|
782
|
+
|
|
783
|
+
print("\n")
|
|
784
|
+
print("-----------------Indexed Diffraction Pattern Details------------------")
|
|
785
|
+
|
|
786
|
+
print(f"""{'DP #':^7}{'Miller Index':^15}{'Distance':^15}{'DP Loc':^23}{'Intensity':^11}""")
|
|
787
|
+
|
|
788
|
+
ediom.printIndexDetails()
|
|
789
|
+
return 0
|
|
790
|
+
|
|
791
|
+
def generateBDF(self,
|
|
792
|
+
center=(0.0, 0.0),
|
|
793
|
+
rads = (0.0, 0.0),
|
|
794
|
+
scol = 0.0,
|
|
795
|
+
bShow=False):
|
|
796
|
+
"""
|
|
797
|
+
This method generates an (Annular) Bright/Dark Field image from an experimental stack image.
|
|
798
|
+
It is a wrapper for the function `getBDF`function in the new SEND module.
|
|
799
|
+
|
|
800
|
+
:param center: The center of detector.
|
|
801
|
+
:type center: tuple of floating point numbers
|
|
802
|
+
|
|
803
|
+
:param rads: a pair of radius for inner and outer field detectors.
|
|
804
|
+
When the inner radius is set to zero, the resulting image will be either
|
|
805
|
+
Bright Field(BF) or Dark Field(DF) depending on if the center is on
|
|
806
|
+
the bright spot of the image or not. Otherwise, a positive inner radius
|
|
807
|
+
will result in Annular Bright Field (ABF) or Annular Dark Field (ADF).
|
|
808
|
+
:type rads: tuple, optional.
|
|
809
|
+
|
|
810
|
+
:param scol: The size of the output image along column direction.
|
|
811
|
+
:type scol: floating point numbers, optional
|
|
812
|
+
|
|
813
|
+
:param bShow: whether to display the resulting image.
|
|
814
|
+
:type bShow: boolean, optional, default `False`
|
|
815
|
+
|
|
816
|
+
:return: (status code, image data), status code of 0 successful, otherwise failed
|
|
817
|
+
:rtype: tuple of a short integer and image data array.
|
|
818
|
+
|
|
819
|
+
.. note::
|
|
820
|
+
|
|
821
|
+
The experimental image loaded must have more than one stack.
|
|
822
|
+
|
|
823
|
+
"""
|
|
824
|
+
if not isinstance(scol, (int, float)) or scol <= 0:
|
|
825
|
+
print(f'The size of the output image along column direction must be numeral and positive')
|
|
826
|
+
return -1, None
|
|
827
|
+
|
|
828
|
+
if not isinstance(center, tuple) or len(center) !=2 or \
|
|
829
|
+
not all(isinstance(x, (int, float)) for x in center):
|
|
830
|
+
print(f'Invalid annular dark or bright field detector center')
|
|
831
|
+
return -2, None
|
|
832
|
+
|
|
833
|
+
if not isinstance(rads, tuple) or len(rads) !=2 or \
|
|
834
|
+
not all(isinstance(x, (int, float)) for x in rads) or \
|
|
835
|
+
rads[0] == rads[1]:
|
|
836
|
+
print(f'Invalid annular dark or bright field detector inner and outer radius, it must be a pair of numerals')
|
|
837
|
+
return -3, None
|
|
838
|
+
|
|
839
|
+
try:
|
|
840
|
+
self.loadImage(rmode = EL_MORE)
|
|
841
|
+
except Exception as e:
|
|
842
|
+
print(f'Error loading the experimental image with message: {e}')
|
|
843
|
+
return -4, None
|
|
844
|
+
|
|
845
|
+
ret = send.getBDF(center[0], center[1],
|
|
846
|
+
rads[0], rads[1],
|
|
847
|
+
scol)
|
|
848
|
+
if ret != 0:
|
|
849
|
+
print(f'Failed to generate annular dark or bright field image for experimental image {self.fname}')
|
|
850
|
+
return -4, None
|
|
851
|
+
|
|
852
|
+
adfimg = stem4d.cvar.ximg
|
|
853
|
+
self._dim = (adfimg.h.ncol, adfimg.h.nrow, 1)
|
|
854
|
+
self._data = adfimg.getImageData(1)
|
|
855
|
+
|
|
856
|
+
if bShow:
|
|
857
|
+
self.viewExpImage(title='Annular Dark or Bright Field Image' if rads[0] > 0.0 else \
|
|
858
|
+
'Dark or Bright Field Image')
|
|
859
|
+
|
|
860
|
+
return 0, self._data
|
|
861
|
+
|
|
862
|
+
def generateMaskedImage(self,
|
|
863
|
+
maskfn,
|
|
864
|
+
scancol = 0.0,
|
|
865
|
+
bShow=False):
|
|
866
|
+
"""
|
|
867
|
+
generates an Bright Field(ADF) image from an experimental stack image input.
|
|
868
|
+
and a mask image.
|
|
869
|
+
|
|
870
|
+
:param maskfn: mask image file name.
|
|
871
|
+
:type maskfn: string, required
|
|
872
|
+
|
|
873
|
+
:param scancol: The center of ADF detector. default value 0.0
|
|
874
|
+
:type scancol: float
|
|
875
|
+
|
|
876
|
+
:param bShow: whether to display the resulting ADF image.
|
|
877
|
+
:type bShow: boolean, optional, default `False`
|
|
878
|
+
|
|
879
|
+
:return: status code, 0 successful, otherwise failed
|
|
880
|
+
:rtype: integer
|
|
881
|
+
|
|
882
|
+
.. note::
|
|
883
|
+
"""
|
|
884
|
+
if not isinstance(scancol, (int, float)) or scancol <= 0:
|
|
885
|
+
print(f'The scancol input must be numeral and positive')
|
|
886
|
+
return -1, None
|
|
887
|
+
|
|
888
|
+
if not isinstance(maskfn, str) and maskfn.strip():
|
|
889
|
+
print(f'The mask image file name must be string and not empty ')
|
|
890
|
+
return -2, None
|
|
891
|
+
|
|
892
|
+
try:
|
|
893
|
+
self.loadImage(rmode = EL_MORE)
|
|
894
|
+
except Exception as e:
|
|
895
|
+
return -4, None
|
|
896
|
+
|
|
897
|
+
ret = send.getMaskedImage(maskfn, scancol)
|
|
898
|
+
|
|
899
|
+
if ret != 0:
|
|
900
|
+
print(f'Failed to generate masked image for experimental image {self.fname}')
|
|
901
|
+
return -3, None
|
|
902
|
+
|
|
903
|
+
maskedimg = stem4d.cvar.ximg
|
|
904
|
+
self._dim = (maskedimg.h.ncol, maskedimg.h.nrow, 1)
|
|
905
|
+
self._data = maskedimg.getImageData(1)
|
|
906
|
+
|
|
907
|
+
if bShow:
|
|
908
|
+
self.viewExpImage(title = "Experimental Diffraction Image - Masked")
|
|
909
|
+
|
|
910
|
+
return 0, self._data
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
@staticmethod
|
|
914
|
+
def convertImage(
|
|
915
|
+
imgfn,
|
|
916
|
+
nformat=E_RAW,
|
|
917
|
+
ndtype = E_FLOAT,
|
|
918
|
+
dim = (MIN_IMAGESIZE,
|
|
919
|
+
MIN_IMAGESIZE,
|
|
920
|
+
MIN_IMAGESTACK),
|
|
921
|
+
noffset = 8):
|
|
922
|
+
'''
|
|
923
|
+
|
|
924
|
+
converts any other suported image formats into pyEMAPS' proprietory small
|
|
925
|
+
header format.
|
|
926
|
+
|
|
927
|
+
This static method is very useful for all auxillary images to be used with
|
|
928
|
+
stem4d module and are required to have the proprietory image format by the
|
|
929
|
+
pyEMAPS simulation backend.
|
|
930
|
+
|
|
931
|
+
:param imgfn: input image file path
|
|
932
|
+
:type imgfn: string, required. Ignored when nformat is E_NPY
|
|
933
|
+
|
|
934
|
+
:param nformat: format of the image file. Support all bitmap formatted image format
|
|
935
|
+
:type nformat: integer, optional, default to `E_RAW`
|
|
936
|
+
|
|
937
|
+
:param ndtype: image date types. It can be integer, float or double
|
|
938
|
+
:type ndtype: integer, optional, default to E_INT
|
|
939
|
+
|
|
940
|
+
:param dim: three integer tuple for the dimension of the image data (width, height, stack/depth)
|
|
941
|
+
:type dim: integer tuple, optional, default to (MIN_IMAGESIZE, MIN_IMAGESIZE, MIN_IMAGESTACK)
|
|
942
|
+
|
|
943
|
+
:param noffset: header offset or starting byte of the image data.
|
|
944
|
+
:type noffset: integer, optional, default to 8.
|
|
945
|
+
|
|
946
|
+
:return:
|
|
947
|
+
:rtype: None
|
|
948
|
+
|
|
949
|
+
.. note::
|
|
950
|
+
|
|
951
|
+
If the input raw image file name does not contain directory,
|
|
952
|
+
this method will search the file in pyEMAPS data diretcory
|
|
953
|
+
set by environment variable PYEMAPS_DATA or current working
|
|
954
|
+
directory in that order.
|
|
955
|
+
|
|
956
|
+
If the input file contains the fully qualified file path,
|
|
957
|
+
pyEMAPS will be using the folder that contains the file
|
|
958
|
+
to place the output image file. So, please make sure that
|
|
959
|
+
the input directory is writable.
|
|
960
|
+
|
|
961
|
+
The result pyEMAPS image file with small header will be saved
|
|
962
|
+
in a folder name `sh_converted` in the dierctory that
|
|
963
|
+
contains the original raw image file.
|
|
964
|
+
|
|
965
|
+
'''
|
|
966
|
+
|
|
967
|
+
if nformat==E_SH or (nformat != E_RAW and nformat !=E_NPY):
|
|
968
|
+
raise ValueError('pyEmaps only converts raw and numpy images to its proprietory format.')
|
|
969
|
+
|
|
970
|
+
oExt = '.im3'
|
|
971
|
+
iname = ''
|
|
972
|
+
idir=''
|
|
973
|
+
imgFile = imgfn
|
|
974
|
+
if nformat !=E_NPY:
|
|
975
|
+
import os
|
|
976
|
+
|
|
977
|
+
if not imgfn or imgfn is None:
|
|
978
|
+
raise ValueError("Input image file error!")
|
|
979
|
+
|
|
980
|
+
# first, split the image path
|
|
981
|
+
idir, ifile = os.path.split(imgfn)
|
|
982
|
+
|
|
983
|
+
if not ifile:
|
|
984
|
+
raise ValueError("Input image file error!")
|
|
985
|
+
|
|
986
|
+
if not idir:
|
|
987
|
+
if 'PYEMAPS_DATA' in os.environ:
|
|
988
|
+
idir = os.environ['PYEMAPS_DATA']
|
|
989
|
+
else:
|
|
990
|
+
idir = os.getcwd()
|
|
991
|
+
|
|
992
|
+
imgFile = os.path.join(idir, ifile)
|
|
993
|
+
if not os.path.exists(imgFile):
|
|
994
|
+
raise FileNotFoundError("Input image file not found!")
|
|
995
|
+
|
|
996
|
+
iname, inxt = os.path.splitext(ifile)
|
|
997
|
+
|
|
998
|
+
if not iname or not inxt:
|
|
999
|
+
raise ValueError('Input file name error!')
|
|
1000
|
+
|
|
1001
|
+
else:
|
|
1002
|
+
if 'PYEMAPS_DATA' in os.environ:
|
|
1003
|
+
idir = os.environ['PYEMAPS_DATA']
|
|
1004
|
+
else:
|
|
1005
|
+
idir = os.getcwd()
|
|
1006
|
+
iname = "shimage_from_numpy" + _getDateTimeString()
|
|
1007
|
+
|
|
1008
|
+
odir = os.path.join(idir, 'sh_converted')
|
|
1009
|
+
|
|
1010
|
+
os.makedirs(odir, exist_ok=True)
|
|
1011
|
+
oImageFn = os.path.join(odir, f"{iname}{oExt}")
|
|
1012
|
+
|
|
1013
|
+
# now create a StackImage object.
|
|
1014
|
+
|
|
1015
|
+
img = StackImage(imgFile,
|
|
1016
|
+
nformat=nformat,
|
|
1017
|
+
ndtype = ndtype,
|
|
1018
|
+
dim = dim,
|
|
1019
|
+
noffset = noffset)
|
|
1020
|
+
try:
|
|
1021
|
+
img.loadImage()
|
|
1022
|
+
stem4d.saveImage(oImageFn)
|
|
1023
|
+
|
|
1024
|
+
except Exception as e:
|
|
1025
|
+
print(f'Error converting image into pyEMAPS proprietory image file: {e}')
|
|
1026
|
+
|
|
1027
|
+
else:
|
|
1028
|
+
print(f"The image file {imgFile} was successfully converted to pyEMAPS image format and saved as {oImageFn}")
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
|