1212from typing_extensions import Self
1313
1414from phaser .types import IsVersion
15+ from phaser .utils .image import apply_flips
16+ from phaser .utils .num import to_numpy
1517
1618
1719def _get_dir (f : pane .io .FileOrPath ) -> t .Optional [Path ]:
@@ -45,9 +47,20 @@ def __post_init__(self):
4547 version : t .Annotated [str , IsVersion (exactly = "2.0" )] = "2.0"
4648 """Metadata version"""
4749
50+ empad_version : t .Optional [int ] = None
51+ """Empad version used. Defaults to v1 if not specified."""
52+
4853 raw_filename : str
4954 """Raw 4DSTEM data filename, relative to metadata location."""
5055
56+ det_flips : t .Optional [t .Tuple [bool , bool , bool ]] = None
57+ """
58+ Flips to apply to the raw diffraction patterns, (flip_y, flip_x, transpose).
59+ Defaults to `(True, False, False)` (appears to be the most common orientation).
60+ """
61+ det_rotation : float = 0.0
62+ """Detector rotation (degrees)."""
63+
5164 orig_path : t .Optional [Path ] = None
5265 """Original path to experimental folder."""
5366
@@ -76,7 +89,7 @@ def __post_init__(self):
7689 diff_step : t .Optional [float ] = None
7790 """Diffraction pixel size (mrad/px)."""
7891
79- scan_rotation : float
92+ scan_rotation : float = 0.0
8093 """Scan rotation (degrees)."""
8194 scan_shape : t .Tuple [int , int ]
8295 """Scan shape (x, y)."""
@@ -112,8 +125,8 @@ def is_simulated(self) -> bool:
112125 return self .file_type == "pyMultislicer_metadata"
113126
114127
115- def load_4d (path : t .Union [str , Path ], scan_shape : t .Optional [t .Tuple [int , int ]] = None ,
116- memmap : bool = False ) -> NDArray [numpy .float32 ]:
128+ def load_4d (path : t .Union [str , Path ], scan_shape : t .Optional [t .Tuple [int , int ]] = None , * ,
129+ memmap : bool = False , flips : t . Optional [ t . Tuple [ bool , bool , bool ]] = None ) -> NDArray [numpy .float32 ]:
117130 """
118131 Load a raw EMPAD dataset into memory.
119132
@@ -126,6 +139,8 @@ def load_4d(path: t.Union[str, Path], scan_shape: t.Optional[t.Tuple[int, int]]
126139 - `path`: Path to file to load
127140 - `scan_shape`: Scan shape of dataset. Will be inferred from the filename if not specified.
128141 - `memmap`: If specified, memmap the file as opposed to loading it eagerly.
142+ - `flips`: Flips to apply to the diffraction patterns, `(flip_y, flip_x, transpose)`.
143+ Defaults to `(True, False, False)` (appears to be the most common orientation).
129144
130145 Returns a numpy array (or `numpy.memmap`)
131146 """
@@ -148,26 +163,28 @@ def load_4d(path: t.Union[str, Path], scan_shape: t.Optional[t.Tuple[int, int]]
148163 if not a .size % (130 * 128 ) == 0 :
149164 raise ValueError (f"File not divisible by 130x128 (size={ a .size } )." )
150165 a .shape = (- 1 , 130 , 128 )
151- #a = a[:, :128, :]
152166
153167 if a .shape [0 ] != n_x * n_y :
154168 raise ValueError (f"Got { a .shape [0 ]} probes, expected { n_x } x{ n_y } = { n_x * n_y } ." )
155169 a .shape = (n_y , n_x , * a .shape [1 :])
156- a = a [..., 127 ::- 1 , :] # flip reciprocal y space, crop junk rows
157170
158- return a
171+ a = a [..., :128 , :] # crop junk rows
172+ return apply_flips (a , flips or (True , False , False )) # defaults to typical EMPAD orientation
159173
160174
161175@t .overload
162- def save_4d (arr : NDArray [numpy .float32 ], * , path : t .Union [str , Path ], folder : None = None , name : None = None ):
176+ def save_4d (arr : NDArray [numpy .float32 ], * , path : t .Union [str , Path ], folder : None = None , name : None = None ,
177+ flips : t .Optional [t .Tuple [bool , bool , bool ]] = None ):
163178 ...
164179
165180@t .overload
166- def save_4d (arr : NDArray [numpy .float32 ], * , path : None = None , folder : t .Union [str , Path ], name : t .Optional [str ] = None ):
181+ def save_4d (arr : NDArray [numpy .float32 ], * , path : None = None , folder : t .Union [str , Path ], name : t .Optional [str ] = None ,
182+ flips : t .Optional [t .Tuple [bool , bool , bool ]] = None ):
167183 ...
168184
169185def save_4d (arr : NDArray [numpy .float32 ], * , path : t .Union [str , Path , None ] = None ,
170- folder : t .Union [str , Path , None ] = None , name : t .Optional [str ] = None ): #):
186+ folder : t .Union [str , Path , None ] = None , name : t .Optional [str ] = None ,
187+ flips : t .Optional [t .Tuple [bool , bool , bool ]] = None ):
171188 """
172189 Save a raw EMPAD dataset.
173190
@@ -183,6 +200,8 @@ def save_4d(arr: NDArray[numpy.float32], *, path: t.Union[str, Path, None] = Non
183200 - `folder`: Folder to save dataset inside.
184201 - `name`: When `folder` is specified, format to use to determine filename. Defaults to `"scan_x{x}_y{y}.raw"`.
185202 Will be formatted using the scan shape `{'x': n_x, 'y': n_y}`.
203+ - `flips`: Flips to apply to the diffraction patterns, `(flip_y, flip_x, transpose)`.
204+ Defaults to `(True, False, False)` (appears to be the most common orientation).
186205 """
187206
188207 try :
@@ -206,7 +225,7 @@ def save_4d(arr: NDArray[numpy.float32], *, path: t.Union[str, Path, None] = Non
206225 out_shape [2 ] = 130 # dead rows
207226
208227 out = numpy .zeros (out_shape , dtype = numpy .float32 )
209- out [..., 127 ::- 1 , :] = arr .astype (numpy .float32 )
228+ out [..., 127 ::- 1 , :] = apply_flips ( to_numpy ( arr .astype (numpy .float32 )), flips or ( True , False , False ) )
210229
211230 with open (path , 'wb' ) as f :
212231 out .tofile (f )
0 commit comments