upxo.interfaces.defdap.ebsd_reader module
upxo.interfaces.defdap.ebsd_reader
Thin, UPXO-native wrapper around DefDAP for loading 2D EBSD maps from Oxford Instruments files (.ctf / .crc) and exposing the data as plain NumPy arrays that the rest of UPXO (repgen2d, gsan2d, etc.) can consume directly.
Supported file formats
.ctf Oxford Instruments text -> data_type=’OxfordText’ .crc Oxford Instruments binary -> data_type=’OxfordBinary’
Extracted arrays (all stored as plain NumPy — no DefDAP objects leak out)
- lfi_ebsdnp.ndarray, int, shape (ny, nx)
Grain label field. Values >= 1 are grain IDs. -1 = remnant grain-boundary pixel (not assigned to any grain). -2 = pixel belonging to a grain smaller than min_grain_size. 0 = non-indexed point.
- euler_ebsdnp.ndarray, float, shape (ny, nx, 3)
Bunge Euler angles (phi1, Phi, phi2) in radians. Transposed from DefDAP’s native (3, ny, nx) to image-convention (ny, nx, 3) so that euler_ebsd[row, col] gives a 3-vector.
- quat_ebsdnp.ndarray, float, shape (ny, nx, 4)
Unit quaternion per pixel (q0, q1, q2, q3) extracted from DefDAP’s object array. Positive-hemisphere convention (q0 >= 0).
- step_sizefloat
Pixel step size in microns, read directly from the file metadata.
Usage
from upxo.interfaces.defdap.ebsd_reader import EBSDReader
rdr = EBSDReader.from_file(‘scan.ctf’, min_grain_size=10) # or rdr = EBSDReader.from_file(‘scan.crc’, min_grain_size=10)
rdr.lfi_ebsd # (ny, nx) int array rdr.euler_ebsd # (ny, nx, 3) float array, radians rdr.quat_ebsd # (ny, nx, 4) float array rdr.step_size # float, microns
- class upxo.interfaces.defdap.ebsd_reader.EBSDReader[source]
Bases:
objectUPXO-native EBSD file reader backed by DefDAP.
Parameters are not passed directly; use the class-methods as constructors.
- lfi_ebsd
Grain label field (see module docstring for value conventions).
- euler_ebsd
Bunge Euler angles phi1, Phi, phi2 in radians.
- quat_ebsd
Unit quaternion coefficients q0, q1, q2, q3 per pixel.
- classmethod from_file(file_path, min_grain_size=10, misori_tol=10, data_type=None)[source]
Load an EBSD map from a .ctf or .crc file and extract all UPXO-relevant arrays.
- Parameters:
file_path (str or pathlib.Path) – Path to the EBSD file. Extension must be .ctf or .crc unless data_type is supplied explicitly.
min_grain_size (int, optional) – Minimum grain size in pixels passed to DefDAP’s
find_grains(). Grains smaller than this are labelled -2 inlfi_ebsd. Default 10.misori_tol (float, optional) – Misorientation tolerance in degrees for grain boundary detection inside DefDAP. Default 10.
data_type (str or None, optional) – Override the DefDAP data_type string. When None (default) the format is inferred from the file extension: ‘.ctf’ -> ‘OxfordText’ ‘.crc’ -> ‘OxfordBinary’
- Returns:
Populated instance with
lfi_ebsd,euler_ebsd,quat_ebsd,step_size,file_path,shape.- Return type:
- Raises:
ImportError – If DefDAP is not installed.
FileNotFoundError – If
file_pathdoes not exist.ValueError – If the file extension is not recognised and
data_typeis not provided.
- classmethod from_file_subsampled(src_path, dst_path, stride_x: int = 2, stride_y: int = 2, min_grain_size: int = 10, misori_tol: float = 10, data_type=None)[source]
Sub-sample a CTF file, write the result to dst_path, then load it via the normal
from_filepipeline.This is Method C sub-sampling: the file is reduced before DefDAP ever reads it, so grain detection runs on the smaller map and memory usage is proportional to the sub-sampled size.
- Parameters:
src_path (str or pathlib.Path) – Source
.ctffile.dst_path (str or pathlib.Path) – Destination for the written sub-sampled CTF file. The parent directory must already exist.
stride_x (int) – Keep every stride_x-th pixel along X. Default 2.
stride_y (int) – Keep every stride_y-th pixel along Y. Default 2.
min_grain_size (int) – Forwarded to
from_file(). Default 10.misori_tol (float) – Forwarded to
from_file(). Default 10.data_type (str or None) – Forwarded to
from_file(). Default None (auto-detected).
- Returns:
Instance loaded from the written sub-sampled CTF file.
- Return type:
- property ny
Number of rows (y pixels).
- property nx
Number of columns (x pixels).
- property n_grains
Number of grains detected (label values >= 1).
- crop(region, inplace=False)[source]
Crop the EBSD map to a rectangular sub-region specified as percentage extents of the full map.
- Parameters:
region (sequence of 4 numbers) –
[xstart%, ystart%, xend%, yend%]— all values in the range [0, 100].xstart%/xend%are measured along the column (x) axis (i.e. nx dimension).ystart%/yend%are measured along the row (y) axis (i.e. ny dimension).
Example:
[10, 10, 80, 80]keeps the central 70 % of the map in both directions, discarding a 10 % border on each side.inplace (bool, optional) – If
True, modifyselfand returnNone. IfFalse(default), return a newEBSDReaderwith the cropped arrays;selfis left unchanged.
- Returns:
Cropped reader (when
inplace=False) orNone(wheninplace=True).- Return type:
EBSDReader or None
- Raises:
ValueError – If region does not have exactly 4 elements, any value is outside [0, 100], or start >= end in either axis.
Notes
DefDAP (0.93.x) has no built-in crop, so the crop is performed entirely on the extracted NumPy arrays.
step_sizeis unchanged (cropping does not affect pixel pitch). Grain IDs inlfi_ebsdare preserved as-is — they are not re-numbered after cropping.
- characterise(connectivity=4, min_grain_size=0)[source]
Run the complete post-load characterisation pipeline on
selfand return a result dict ready for consumption byrepgen2d.rechar()or directly by user code.Steps
rechar_lfi(connectivity)— fill non-positive pixels, updateeuler_ebsd/quat_ebsdin-place._char_lfi(lfi_ebsd, step_size, min_grain_size)— compute per-grain morphological properties; grains smaller thanmin_grain_sizepixels are excluded.find_neighs2d(lfi_ebsd, conn)— build first-order neighbour dict.
- param connectivity:
cc3d connectivity for
rechar_lfiandfind_neighs2d. Valid 2D values: 4 or 8. Default 4.- type connectivity:
int
- param min_grain_size:
Minimum grain size in pixels. Grains with fewer pixels are excluded from the returned
'prop'dict. Default 0 (all grains included).- type min_grain_size:
int, optional
- returns:
'lfi'np.ndarray int32 (ny, nx) cleaned label field'euler'np.ndarray float64 (ny, nx, 3) Bunge angles, radians'quat'np.ndarray float64 (ny, nx, 4) unit quaternions'neigh_gid'dict grain_id -> list[int] first-order neighbours'prop'dict grain_id -> property dict (see_char_lfi)'step_size'float pixel step size in microns- rtype:
dict with keys
- rechar_lfi(connectivity=4)[source]
Re-characterise
lfi_ebsdby filling every non-positive pixel (values <= 0, including DefDAP’s -1 remnant-boundary and -2 sub-minimum-size codes, and non-indexed 0) with the label of the spatially largest grain that borders that connected region.euler_ebsdandquat_ebsdfor the filled pixels are then updated with the grain-average orientation of the assigned grain.Algorithm
Identify all non-positive pixels as the unknown mask.
Use cc3d to label connected components of the unknown mask (connectivity 4 or 8, matching the supplied parameter).
Assign each connected component a temporary unique label
n_grains + cc_idin a working copy oflfi_ebsd.Call
find_neighs2don the augmented label field to obtain the adjacency list for every label (standard + temporary).For each temporary CC label, find the neighbouring valid grain with the greatest pixel count and assign that grain’s ID to all pixels of the CC.
Compute per-grain mean Euler angles and mean (normalised) quaternion from the original valid pixels of each grain.
Write those averages into
euler_ebsdandquat_ebsdat the newly filled pixel positions.Store the updated arrays back to
self.
- param connectivity:
cc3d connectivity for labelling the unknown region. Valid 2D values: 4 (edge-only) or 8 (edge+corner). Default 4.
- type connectivity:
int
- raises ValueError:
If
connectivityis not 4 or 8.
Notes
After calling this method
lfi_ebsdshould contain only positive grain labels (>= 1).euler_ebsdandquat_ebsdare updated in-place onself. The grain-average orientation used for filling is the arithmetic mean of the original valid pixels — adequate for intra-grain spread; it is not a proper quaternion mean but sufficient for neighbourhood-assignment.
- plot_grain_map(**kwargs)[source]
Plot the grain label field (lfi_ebsd) with optional boundary overlay.
All keyword arguments are forwarded to
upxo.viz.ebsdviz.plot_grain_labels.- Return type:
fig, ax
- plot_euler_maps(**kwargs)[source]
Plot phi1, Phi, phi2 Euler angle maps side-by-side.
All keyword arguments are forwarded to
upxo.viz.ebsdviz.plot_euler_maps.- Return type:
fig, axes
- plot_grain_size_histogram(**kwargs)[source]
Plot a grain area histogram in physical units (µm²).
All keyword arguments are forwarded to
upxo.viz.ebsdviz.plot_grain_size_histogram.- Return type:
fig, ax
- plot_grain_structure_with_boundaries(**kwargs)[source]
Plot grain label map and Euler channel side-by-side with boundaries.
All keyword arguments are forwarded to
upxo.viz.ebsdviz.plot_grain_structure_with_boundaries.- Return type:
fig, axes
- see_distr(prop='area', nbins=40, vis='hist', show_kde=True, show_stats=True, color='steelblue', figsize=(7, 4), log_scale=False)[source]
Visualise the distribution of a grain morphological property.
Requires
characterise()to have been called first (it populatesself.propwith per-grain property dicts).- Parameters:
prop (str) – Grain property to plot. Valid names (all scaled to physical units via
step_size):'area','perimeter','eq_diameter','aspect_ratio','major_axis_length','minor_axis_length','eccentricity','solidity','npixels'.nbins (int) – Number of histogram bins. Default 40.
vis (str) – Plot style:
'hist'— histogram with optional KDE overlay;'kde'— pure kernel density estimate;'hist_kde'— density-normalised histogram + KDE. Default'hist'.show_kde (bool) – Overlay a KDE curve on the histogram (
vis='hist'only). Default True.show_stats (bool) – Annotate mean and median lines. Default True.
color (str) – Histogram / KDE fill colour. Default
'steelblue'.figsize (tuple) – Figure size (width, height) in inches. Default
(7, 4).log_scale (bool) – Apply log scale to the x-axis. Default False.
- Returns:
fig, ax
- Return type:
matplotlib Figure and Axes
- Raises:
RuntimeError – If
self.propis not populated (characterise not yet called).KeyError – If prop is not present in the property dicts.
ValueError – If vis is not one of the supported values.
Examples
>>> fig, ax = rdr.see_distr(prop='area', nbins=40) >>> plt.show() >>> fig, ax = rdr.see_distr(prop='aspect_ratio', vis='hist_kde') >>> plt.show()
- lfi_ebsd
- euler_ebsd
- quat_ebsd
- step_size
- file_path
- shape
- prop
- upxo.interfaces.defdap.ebsd_reader.write_subsampled_ctf(src_path, dst_path, stride_x: int = 2, stride_y: int = 2) Path[source]
Read a CTF file, sub-sample every stride_x-th column and stride_y-th row, update the header metadata, and write a new valid CTF file to dst_path.
The X / Y physical-coordinate values in the kept data rows are left unchanged — they already carry the correct micron positions. Only the
XCells,YCells,XStep, andYStepheader fields are updated to reflect the new pixel pitch and map dimensions.- Parameters:
src_path (str or pathlib.Path) – Source
.ctffile. AValueErroris raised for other extensions.dst_path (str or pathlib.Path) – Destination path for the sub-sampled CTF file. The parent directory must already exist.
stride_x (int) – Keep every stride_x-th pixel along the X (column) axis. Default 2.
stride_y (int) – Keep every stride_y-th pixel along the Y (row) axis. Default 2.
- Returns:
Absolute path to the written file (dst_path), so the caller can chain directly into
EBSDReader.from_file().- Return type:
- Raises:
ValueError – If src_path does not have a
.ctfextension.FileNotFoundError – If src_path does not exist.