69 lines
3.3 KiB
Python
69 lines
3.3 KiB
Python
import numpy as np
|
|
from backend.data_types import DataField
|
|
from tests.node_tests._shared import make_field
|
|
|
|
|
|
def test_facet_level():
|
|
from backend.node_registry import get_node_info
|
|
from backend.nodes.level_facet import FacetLevelField
|
|
from backend.nodes.level_plane import PlaneLevelField
|
|
|
|
def fit_pixel_plane(data, region):
|
|
yy, xx = np.mgrid[0:data.shape[0], 0:data.shape[1]]
|
|
A = np.column_stack([np.ones(int(np.count_nonzero(region))), xx[region].astype(np.float64), yy[region].astype(np.float64)])
|
|
coeffs, _, _, _ = np.linalg.lstsq(A, data[region].ravel().astype(np.float64), rcond=None)
|
|
return float(coeffs[0]), float(coeffs[1]), float(coeffs[2])
|
|
|
|
node = FacetLevelField()
|
|
plane_node = PlaneLevelField()
|
|
assert get_node_info("FacetLevelField")["category"] == "Level & Correct"
|
|
|
|
N = 96
|
|
yy, xx = np.mgrid[0:N, 0:N]
|
|
base = 0.055 * xx + 0.028 * yy
|
|
terraces = np.zeros((N, N), dtype=np.float64)
|
|
terraces[:, 54:] += 6.0
|
|
terraces[18:70, 68:88] += 3.5
|
|
field = make_field(data=base + terraces)
|
|
|
|
plane_leveled, = plane_node.process(field)
|
|
facet_leveled, = node.process(field, masking="ignore")
|
|
|
|
left_region = xx < 48
|
|
right_region = (xx > 60) & ~((yy >= 18) & (yy < 70) & (xx >= 68) & (xx < 88))
|
|
_, plane_left_bx, plane_left_by = fit_pixel_plane(plane_leveled.data, left_region)
|
|
_, plane_right_bx, plane_right_by = fit_pixel_plane(plane_leveled.data, right_region)
|
|
_, facet_left_bx, facet_left_by = fit_pixel_plane(facet_leveled.data, left_region)
|
|
_, facet_right_bx, facet_right_by = fit_pixel_plane(facet_leveled.data, right_region)
|
|
plane_slope = float(max(np.hypot(plane_left_bx, plane_left_by), np.hypot(plane_right_bx, plane_right_by)))
|
|
facet_slope = float(max(np.hypot(facet_left_bx, facet_left_by), np.hypot(facet_right_bx, facet_right_by)))
|
|
assert facet_slope < plane_slope * 1e-6
|
|
|
|
mask = np.zeros((N, N), dtype=np.uint8)
|
|
mask[24:72, 24:72] = 255
|
|
base_only = 0.035 * xx + 0.014 * yy
|
|
masked_facet = 5.0 - 0.065 * xx + 0.045 * yy
|
|
competing = np.where(mask > 0, masked_facet, base_only)
|
|
competing_field = make_field(data=competing)
|
|
|
|
excluded, = node.process(competing_field, masking="exclude", mask=mask)
|
|
included, = node.process(competing_field, masking="include", mask=mask)
|
|
|
|
outer_region = (mask == 0) & (xx > 4) & (xx < N - 4) & (yy > 4) & (yy < N - 4)
|
|
inner_region = (mask > 0) & (xx > 28) & (xx < 68) & (yy > 28) & (yy < 68)
|
|
_, excl_outer_bx, excl_outer_by = fit_pixel_plane(excluded.data, outer_region)
|
|
_, excl_inner_bx, excl_inner_by = fit_pixel_plane(excluded.data, inner_region)
|
|
_, incl_outer_bx, incl_outer_by = fit_pixel_plane(included.data, outer_region)
|
|
_, incl_inner_bx, incl_inner_by = fit_pixel_plane(included.data, inner_region)
|
|
|
|
assert float(np.hypot(excl_outer_bx, excl_outer_by)) < float(np.hypot(incl_outer_bx, incl_outer_by)) * 0.2
|
|
assert float(np.hypot(incl_inner_bx, incl_inner_by)) < float(np.hypot(excl_inner_bx, excl_inner_by)) * 0.2
|
|
|
|
bad_units = DataField(data=np.zeros((16, 16), dtype=np.float64), xreal=1e-6, yreal=1e-6, si_unit_xy="nm", si_unit_z="V")
|
|
try:
|
|
node.process(bad_units, masking="ignore")
|
|
except ValueError as exc:
|
|
assert "compatible XY and Z units" in str(exc)
|
|
else:
|
|
assert False, "Facet level should reject incompatible XY/Z units."
|