Skip to content

Commit

Permalink
Remove code using now obsolete xmap
Browse files Browse the repository at this point in the history
  • Loading branch information
atomprobe-tc committed Dec 7, 2023
1 parent 23c28d4 commit c2ce3a0
Showing 1 changed file with 4 additions and 273 deletions.
277 changes: 4 additions & 273 deletions pynxtools/dataconverter/readers/em/subparsers/nxs_pyxem.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ def __init__(self, entry_id: int = 1, input_file_name: str = ""):
self.entry_id = 1
self.file_path = input_file_name
self.cache = {"is_filled": False}
self.xmap = None

def parse(self, template: dict) -> dict:
hfive_parser_type = self.identify_hfive_type()
Expand Down Expand Up @@ -328,7 +327,8 @@ def onthefly_process_roi_ipfs_phases_twod(self,
inp: dict,
roi_id: int,
template: dict) -> dict:
print("Parse crystal_structure_models aka phases (no xmap) 2D version...")
dimensionality = inp["dimensionality"]
print(f"Parse crystal_structure_models aka phases {dimensionality}D version...")
nxem_phase_id = 0
prfx = f"/ENTRY[entry{self.entry_id}]/ROI[roi{roi_id}]/ebsd/indexing"
# bookkeeping is always reported for the original grid
Expand Down Expand Up @@ -486,7 +486,8 @@ def onthefly_process_roi_ipfs_phases_threed(self,
template: dict) -> dict:
# this function is almost the same as its twod version we keep it for
# now an own function until the rediscretization also works for the 3D grid
print("Parse crystal_structure_models aka phases (no xmap) 3D version...")
dimensionality = inp["dimensionality"]
print(f"Parse crystal_structure_models aka phases {dimensionality}D version...")
# see comments in twod version of this function
nxem_phase_id = 0
prfx = f"/ENTRY[entry{self.entry_id}]/ROI[roi{roi_id}]/ebsd/indexing"
Expand Down Expand Up @@ -631,273 +632,3 @@ def process_roi_phase_ipfs_threed(self,
= f"Pixel along {dim[0]}-axis"
template[f"{lgd}/AXISNAME[axis_{dim[0]}]/@units"] = "px"
return template


"""
def prepare_roi_ipfs_phases_twod(self, inp: dict, roi_id: int, template: dict) -> dict:
# Process crystal orientation map from normalized orientation data.
# for NeXus to create a default representation of the EBSD map to explore
# get rid of this xmap at some point it is really not needed in my option
# one can work with passing the set of EulerAngles to the IPF mapper directly
# the order of the individual per scan point results arrays anyway are assumed
# to have the same sequence of scan points and thus the same len along the scan axes
self.xmap = None
self.axis_x = None
self.axis_y = None
print(f"Unique phase_identifier {np.unique(inp['phase_id'])}")
min_phase_id = np.min(np.unique(inp["phase_id"]))
if np.max((inp["n_x"], inp["n_y"])) > HFIVE_WEB_MAXIMUM_RGB:
# assume center of mass of the scan points
# TODO::check if mapping correct for hexagonal and square grid
aabb = [np.min(inp["scan_point_x"]) - 0.5 * inp["s_x"],
np.max(inp["scan_point_x"]) + 0.5 * inp["s_x"],
np.min(inp["scan_point_y"]) - 0.5 * inp["s_y"],
np.max(inp["scan_point_y"]) + 0.5 * inp["s_y"]]
print(f"{aabb}")
if aabb[1] - aabb[0] >= aabb[3] - aabb[2]:
sqr_step_size = (aabb[1] - aabb[0]) / HFIVE_WEB_MAXIMUM_RGB
nxy = [HFIVE_WEB_MAXIMUM_RGB,
int(np.ceil((aabb[3] - aabb[2]) / sqr_step_size))]
else:
sqr_step_size = (aabb[3] - aabb[2]) / HFIVE_WEB_MAXIMUM_RGB
nxy = [int(np.ceil((aabb[1] - aabb[0]) / sqr_step_size)),
HFIVE_WEB_MAXIMUM_RGB]
print(f"H5Web default plot generation, scaling nxy0 {[inp['n_x'], inp['n_y']]}, nxy {nxy}")
# the above estimate is not exactly correct (may create a slight real space shift)
# of the EBSD map TODO:: regrid the real world axis-aligned bounding box aabb with
# a regular tiling of squares or hexagons
# https://stackoverflow.com/questions/18982650/differences-between-matlab-and-numpy-and-pythons-round-function
# MTex/Matlab round not exactly the same as numpy round but reasonably close
# scan point positions were normalized by tech partner subparsers such that they
# always build on pixel coordinates calibrated for step size not by giving absolute positions
# in the sample surface frame of reference as this is typically not yet consistently documented
# because we assume in addition that we always start at the top left corner the zeroth/first
# coordinate is always 0., 0. !
xy = np.column_stack(
(np.tile(np.linspace(0, nxy[0] - 1, num=nxy[0], endpoint=True) * sqr_step_size, nxy[1]),
np.repeat(np.linspace(0, nxy[1] - 1, num=nxy[1], endpoint=True) * sqr_step_size, nxy[0])))
print(f"xy {xy}, shape {np.shape(xy)}")
tree = KDTree(np.column_stack((inp["scan_point_x"], inp["scan_point_y"])))
d, idx = tree.query(xy, k=1)
if np.sum(idx == tree.n) > 0:
raise ValueError(f"kdtree query left some query points without a neighbor!")
del d
del tree
pyxem_euler = np.zeros((np.shape(xy)[0], 3), np.float32)
pyxem_euler = np.nan
pyxem_euler = inp["euler"][idx, :]
if np.isnan(pyxem_euler).any() is True:
raise ValueError(f"Downsampling of the EBSD map left pixels without euler!")
phase_new = np.zeros((np.shape(xy)[0],), np.int32) - 2
phase_new = inp["phase_id"][idx]
if np.sum(phase_new == -2) > 0:
raise ValueError(f"Downsampling of the EBSD map left pixels without euler!")
del xy
if min_phase_id > 0:
pyxem_phase_id = phase_new - min_phase_id
elif min_phase_id == 0:
pyxem_phase_id = phase_new - 1
else:
raise ValueError(f"Unable how to deal with unexpected phase_identifier!")
del phase_new
coordinates, _ = create_coordinate_arrays(
(nxy[1], nxy[0]), (sqr_step_size, sqr_step_size))
xaxis = coordinates["x"]
yaxis = coordinates["y"]
print(f"coordinates" \
f"xmi {np.min(xaxis)}, xmx {np.max(xaxis)}, " \
f"ymi {np.min(yaxis)}, ymx {np.max(yaxis)}")
del coordinates
self.axis_x = np.linspace(0, nxy[0] - 1, num=nxy[0], endpoint=True) * sqr_step_size
self.axis_y = np.linspace(0, nxy[1] - 1, num=nxy[1], endpoint=True) * sqr_step_size
else:
# can use the map discretization as is
coordinates, _ = create_coordinate_arrays(
(inp["n_y"], inp["n_x"]), (inp["s_y"], inp["s_x"]))
xaxis = coordinates["x"]
yaxis = coordinates["y"]
print(f"xmi {np.min(xaxis)}, xmx {np.max(xaxis)}, " \
f"ymi {np.min(yaxis)}, ymx {np.max(yaxis)}")
del coordinates
self.axis_x = self.get_named_axis(inp, "x")
self.axis_y = self.get_named_axis(inp, "y")
pyxem_euler = inp["euler"]
# TODO::there was one example 093_0060.h5oina
# where HitRate was 75% but no pixel left unidentified ??
if min_phase_id > 0:
pyxem_phase_id = inp["phase_id"] - min_phase_id
elif min_phase_id == 0:
pyxem_phase_id = inp["phase_id"] - 1
else:
raise ValueError(f"Unable how to deal with unexpected phase_identifier!")
# inp["phase_id"] - (np.min(inp["phase_id"]) - (-1))
# for pyxem the non-indexed has to be -1 instead of 0 which is what NeXus uses
# -1 always because content of inp["phase_id"] is normalized
# to NeXus NXem_ebsd_crystal_structure concept already!
print(f"Unique pyxem_phase_id {np.unique(pyxem_phase_id)}")
self.xmap = CrystalMap(rotations=Rotation.from_euler(euler=pyxem_euler,
direction='lab2crystal',
degrees=False),
x=xaxis, y=yaxis,
phase_id=pyxem_phase_id,
phase_list=PhaseList(space_groups=inp["space_group"],
structures=inp["phase"]),
prop={},
scan_unit=inp["s_unit"])
del xaxis
del yaxis
# "bc": inp["band_contrast"]}, scan_unit=inp["s_unit"])
print(self.xmap)
return template
def process_roi_ipfs_phases_twod(self,
inp: dict,
roi_id: int,
template: dict) -> dict:
print("Parse crystal_structure_models aka phases (use xmap)...")
phase_id = 0
prfx = f"/ENTRY[entry{self.entry_id}]/ROI[roi{roi_id}]/ebsd/indexing"
n_pts = inp["n_x"] * inp["n_y"]
n_pts_indexed = np.sum(inp["phase_id"] != 0)
print(f"n_pts {n_pts}, n_pts_indexed {n_pts_indexed}")
template[f"{prfx}/number_of_scan_points"] = np.uint32(n_pts)
template[f"{prfx}/indexing_rate"] = np.float64(100. * n_pts_indexed / n_pts)
template[f"{prfx}/indexing_rate/@units"] = f"%"
grp_name = f"{prfx}/EM_EBSD_CRYSTAL_STRUCTURE_MODEL[phase{phase_id}]"
template[f"{grp_name}/number_of_scan_points"] = np.uint32(0)
template[f"{grp_name}/phase_identifier"] = np.uint32(phase_id)
template[f"{grp_name}/phase_name"] = f"notIndexed"
for pyxem_phase_id in np.arange(0, np.max(self.xmap.phase_id) + 1):
# this loop is implicitly ignored as when xmap is None
print(f"inp[phases].keys(): {inp['phases'].keys()}")
if (pyxem_phase_id + 1) not in inp["phases"].keys():
raise ValueError(f"{pyxem_phase_id + 1} is not a key in inp['phases'] !")
# phase_id of pyxem notIndexed is -1 while for NeXus
# it is 0 so add + 1 in naming schemes
trg = f"{prfx}/EM_EBSD_CRYSTAL_STRUCTURE_MODEL[phase{pyxem_phase_id + 1}]"
min_phase_id = np.min(np.unique(inp["phase_id"]))
if min_phase_id > 0:
pyx_phase_id = inp["phase_id"] - min_phase_id
elif min_phase_id == 0:
pyx_phase_id = inp["phase_id"] - 1
else:
raise ValueError(f"Unable how to deal with unexpected phase_identifier!")
del min_phase_id
template[f"{trg}/number_of_scan_points"] \
= np.uint32(np.sum(pyx_phase_id == pyxem_phase_id))
del pyx_phase_id
# not self.xmap.phase_id because in NeXus the number_of_scan_points is always
# accounting for the original map size and not the potentially downscaled version
# of the map as the purpose of the later one is exclusively to show a plot at all
# because of a technical limitation of H5Web if there would be a tool that
# could show larger RGB plots we would not need to downscale the EBSD map resolution!
template[f"{trg}/phase_identifier"] = np.uint32(pyxem_phase_id + 1)
template[f"{trg}/phase_name"] \
= f"{inp['phases'][pyxem_phase_id + 1]['phase_name']}"
self.process_roi_phase_ipfs_twod(roi_id, pyxem_phase_id, template)
return template
def process_roi_phase_ipfs_twod(self, roi_id: int, pyxem_phase_id: int, template: dict) -> dict:
# Parse inverse pole figures (IPF) mappings for specific phase.
phase_name = self.xmap.phases[pyxem_phase_id].name
print(f"Generate 2D IPF map for {pyxem_phase_id}, {phase_name}...")
for idx in np.arange(0, len(PROJECTION_VECTORS)):
ipf_key = plot.IPFColorKeyTSL(
self.xmap.phases[pyxem_phase_id].point_group.laue,
direction=PROJECTION_VECTORS[idx])
img = get_ipfdir_legend(ipf_key)
rgb_px_with_phase_id = np.asarray(
np.asarray(ipf_key.orientation2color(
self.xmap[phase_name].rotations) * 255., np.uint32), np.uint8)
print(f"idx {idx}, phase_name {phase_name}, shape {self.xmap.shape}")
ipf_rgb_map = np.asarray(
np.uint8(np.zeros((self.xmap.shape[0] * self.xmap.shape[1], 3)) * 255.))
# background is black instead of white (which would be more pleasing)
# but IPF color maps have a whitepoint which encodes in fact an orientation
# and because of that we may have a single crystal with an orientation
# close to the whitepoint which become a fully white seemingly "empty" image
ipf_rgb_map[self.xmap.phase_id == pyxem_phase_id, :] = rgb_px_with_phase_id
ipf_rgb_map = np.reshape(
ipf_rgb_map, (self.xmap.shape[0], self.xmap.shape[1], 3), order="C")
# 0 is y while 1 is x !
trg = f"/ENTRY[entry{self.entry_id}]/ROI[roi{roi_id}]/ebsd/indexing" \
f"/EM_EBSD_CRYSTAL_STRUCTURE_MODEL[phase{pyxem_phase_id + 1}]" \
f"/MS_IPF[ipf{idx + 1}]"
template[f"{trg}/projection_direction"] \
= np.asarray(PROJECTION_VECTORS[idx].data.flatten(), np.float32)
# add the IPF color map
mpp = f"{trg}/DATA[map]"
template[f"{mpp}/title"] \
= f"Inverse pole figure {PROJECTION_DIRECTIONS[idx][0]} {phase_name}"
template[f"{mpp}/@NX_class"] = f"NXdata" # TODO::writer should decorate automatically!
template[f"{mpp}/@signal"] = "data"
dims = ["x", "y"]
template[f"{mpp}/@axes"] = []
for dim in dims[::-1]:
template[f"{mpp}/@axes"].append(f"axis_{dim}")
enum = 0
for dim in dims:
template[f"{mpp}/@AXISNAME_indices[axis_{dim}_indices]"] = np.uint32(enum)
enum += 1
template[f"{mpp}/DATA[data]"] = {"compress": ipf_rgb_map, "strength": 1}
hfive_web_decorate_nxdata(f"{mpp}/DATA[data]", template)
scan_unit = self.xmap.scan_unit
if scan_unit == "um":
scan_unit = "µm"
template[f"{mpp}/AXISNAME[axis_x]"] = {"compress": self.axis_x, "strength": 1}
template[f"{mpp}/AXISNAME[axis_x]/@long_name"] \
= f"Coordinate along x-axis ({scan_unit})"
template[f"{mpp}/AXISNAME[axis_x]/@units"] = f"{scan_unit}"
template[f"{mpp}/AXISNAME[axis_y]"] = {"compress": self.axis_y, "strength": 1}
template[f"{mpp}/AXISNAME[axis_y]/@long_name"] \
= f"Coordinate along y-axis ({scan_unit})"
template[f"{mpp}/AXISNAME[axis_y]/@units"] = f"{scan_unit}"
# add the IPF color map legend/key
lgd = f"{trg}/DATA[legend]"
template[f"{lgd}/title"] \
= f"Inverse pole figure {PROJECTION_DIRECTIONS[idx][0]} {phase_name}"
# template[f"{trg}/title"] = f"Inverse pole figure color key with SST"
template[f"{lgd}/@NX_class"] = f"NXdata" # TODO::writer should decorate automatically!
template[f"{lgd}/@signal"] = "data"
template[f"{lgd}/@axes"] = []
for dim in dims[::-1]:
template[f"{lgd}/@axes"].append(f"axis_{dim}")
enum = 0
for dim in dims:
template[f"{lgd}/@AXISNAME_indices[axis_{dim}_indices]"] = np.uint32(enum)
enum += 1
template[f"{lgd}/data"] = {"compress": img, "strength": 1}
hfive_web_decorate_nxdata(f"{lgd}/data", template)
dims = [("x", 1), ("y", 0)]
for dim in dims:
template[f"{lgd}/AXISNAME[axis_{dim[0]}]"] \
= {"compress": np.asarray(np.linspace(0,
np.shape(img)[dim[1]] - 1,
num=np.shape(img)[dim[1]],
endpoint=True), np.uint32),
"strength": 1}
template[f"{lgd}/AXISNAME[axis_{dim[0]}]/@long_name"] \
= f"Pixel along {dim[0]}-axis"
template[f"{lgd}/AXISNAME[axis_{dim[0]}]/@units"] = "px"
# call process_roi_ipf_color_key
return template
"""

0 comments on commit c2ce3a0

Please sign in to comment.