tip modelling and deconvolution
This commit is contained in:
63
backend/nodes/gradient.py
Normal file
63
backend/nodes/gradient.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from backend.node_registry import register_node
|
||||
from backend.data_types import DataField
|
||||
|
||||
|
||||
@register_node(display_name="Gradient")
|
||||
class Gradient:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"field": ("DATA_FIELD",),
|
||||
"component": (["magnitude", "x", "y", "azimuth"],),
|
||||
}
|
||||
}
|
||||
|
||||
OUTPUTS = (
|
||||
('DATA_FIELD', 'gradient'),
|
||||
)
|
||||
FUNCTION = "process"
|
||||
|
||||
DESCRIPTION = (
|
||||
"Compute the spatial gradient using a Sobel operator. "
|
||||
"'x'/'y' give the physical gradient components (z_unit/xy_unit); "
|
||||
"'magnitude' gives sqrt(gx²+gy²); "
|
||||
"'azimuth' gives the local slope direction in radians via atan2(gy, gx). "
|
||||
"Equivalent to gwy_data_field_filter_sobel in Gwyddion (gradient.c)."
|
||||
)
|
||||
|
||||
def process(self, field: DataField, component: str) -> tuple:
|
||||
from scipy.ndimage import sobel
|
||||
|
||||
data = field.data
|
||||
# Sobel kernel sums to ±8 over 2-pixel span; divide by 8·dx to get z/xy slope.
|
||||
gx = sobel(data, axis=1) / (8.0 * field.dx)
|
||||
gy = sobel(data, axis=0) / (8.0 * field.dy)
|
||||
|
||||
if component == "magnitude":
|
||||
result = np.hypot(gx, gy)
|
||||
z = str(field.si_unit_z or "").strip()
|
||||
xy = str(field.si_unit_xy or "").strip()
|
||||
out_unit_z = f"{z}/{xy}" if z and xy else (z or xy)
|
||||
elif component == "x":
|
||||
result = gx
|
||||
z = str(field.si_unit_z or "").strip()
|
||||
xy = str(field.si_unit_xy or "").strip()
|
||||
out_unit_z = f"{z}/{xy}" if z and xy else (z or xy)
|
||||
elif component == "y":
|
||||
result = gy
|
||||
z = str(field.si_unit_z or "").strip()
|
||||
xy = str(field.si_unit_xy or "").strip()
|
||||
out_unit_z = f"{z}/{xy}" if z and xy else (z or xy)
|
||||
elif component == "azimuth":
|
||||
# Azimuth: local slope direction, radians, matches Gwyddion's filter_azimuth
|
||||
result = np.arctan2(gy, gx)
|
||||
out_unit_z = "rad"
|
||||
else:
|
||||
raise ValueError(f"Unknown gradient component: {component!r}")
|
||||
|
||||
return (field.replace(data=result, si_unit_z=out_unit_z),)
|
||||
Reference in New Issue
Block a user