API reference
Sampling moire analysis for displacement and strain measurement.
- class moirestrain.AnalysisResult(reference_phase: ndarray, deformed_phase: ndarray, phase_difference: ndarray, displacement: ndarray)
Bases:
objectResult returned by
analyze().- deformed_phase: ndarray
- displacement: ndarray
- phase_difference: ndarray
- reference_phase: ndarray
- class moirestrain.GratingROI(image_points: ndarray, bounds: tuple[int, int, int, int], mask: ndarray, energy: ndarray)
Bases:
objectDetected grating region in a larger image.
boundsandmaskare the primary detection results.image_pointsare optional rectification hints estimated from the mask; they are not required when the downstream analysis works on an axis-aligned crop.- bounds: tuple[int, int, int, int]
- energy: ndarray
- image_points: ndarray
- mask: ndarray
- class moirestrain.GridAnalysisResult(x: AnalysisResult, y: AnalysisResult, strain: StrainResult, reference_x_component: ndarray, reference_y_component: ndarray, deformed_x_component: ndarray, deformed_y_component: ndarray)
Bases:
objectTwo-direction grid analysis result.
- deformed_x_component: ndarray
- deformed_y_component: ndarray
- reference_x_component: ndarray
- reference_y_component: ndarray
- strain: StrainResult
- class moirestrain.PerspectiveCalibration(image_points: ndarray, world_points: ndarray, output_shape: tuple[int, int], homography: ndarray, pixel_spacing: tuple[float, float])
Bases:
objectPlanar perspective calibration for a grating region.
image_pointsandworld_pointsare four(x, y)corner points in corresponding order. The homography maps image coordinates to rectified pixel coordinates.- classmethod from_points(image_points: ndarray, world_points: ndarray, *, output_shape: tuple[int, int]) PerspectiveCalibration
- homography: ndarray
- image_points: ndarray
- output_shape: tuple[int, int]
- pixel_spacing: tuple[float, float]
- world_points: ndarray
- class moirestrain.StrainResult(exx: ndarray, eyy: ndarray | None, gamma_xy: ndarray | None)
Bases:
objectSmall-strain components calculated from displacement fields.
- exx: ndarray
- eyy: ndarray | None
- gamma_xy: ndarray | None
- class moirestrain.SyntheticExperiment(reference_image: ndarray, deformed_image: ndarray, true_u: ndarray, true_exx: ndarray, period: int, strain_xx: float, rigid_shift: float, noise_std: float)
Bases:
objectSynthetic grating image pair with ground-truth displacement.
- deformed_image: ndarray
- noise_std: float
- period: int
- reference_image: ndarray
- rigid_shift: float
- strain_xx: float
- true_exx: ndarray
- true_u: ndarray
- class moirestrain.SyntheticSquareGridExperiment(reference: ndarray, deformed: ndarray, true_u: ndarray, true_v: ndarray, true_exx: ndarray, true_eyy: ndarray, true_gamma_xy: ndarray, period: int, marker_size: int, strain_xx: float, strain_yy: float, shear_xy: float, rigid_shift: tuple[float, float], noise_std: float)
Bases:
objectSynthetic square-marker grid pair with ground-truth strain.
- deformed: ndarray
- marker_size: int
- noise_std: float
- period: int
- reference: ndarray
- rigid_shift: tuple[float, float]
- shear_xy: float
- strain_xx: float
- strain_yy: float
- true_exx: ndarray
- true_eyy: ndarray
- true_gamma_xy: ndarray
- true_u: ndarray
- true_v: ndarray
- class moirestrain.SyntheticStrainExperiment(reference_x: ndarray, deformed_x: ndarray, reference_y: ndarray, deformed_y: ndarray, true_u: ndarray, true_v: ndarray, true_exx: ndarray, true_eyy: ndarray, true_gamma_xy: ndarray, period: int, strain_xx: float, strain_yy: float, shear_xy: float, rigid_shift: tuple[float, float], noise_std: float)
Bases:
objectSynthetic two-direction grating data with ground-truth strain.
- deformed_x: ndarray
- deformed_y: ndarray
- noise_std: float
- period: int
- reference_x: ndarray
- reference_y: ndarray
- rigid_shift: tuple[float, float]
- shear_xy: float
- strain_xx: float
- strain_yy: float
- true_exx: ndarray
- true_eyy: ndarray
- true_gamma_xy: ndarray
- true_u: ndarray
- true_v: ndarray
- moirestrain.analyze(reference_image: ndarray, deformed_image: ndarray, period: int, *, axis: Literal['x', 'y', 0, 1] = 'x', grating_pitch: float | None = None, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) AnalysisResult
Run the sampling moire pipeline for a reference/deformed image pair.
- moirestrain.analyze_grid(reference_image: ndarray, deformed_image: ndarray, period: int, *, grating_pitch: float | None = None, strain_spacing: float | tuple[float, float] = 1.0, strain_smooth_window: int | tuple[int, int] = 1) GridAnalysisResult
Analyze a two-direction square-marker grid image pair.
The x/y grating components are first separated by directional smoothing. Each component is then analyzed with the phase-shifted sampling moire method, and small-strain components are calculated from the resulting displacement fields.
- moirestrain.apply_homography(points: ndarray, homography: ndarray) ndarray
Apply a homography to
(..., 2)points.
- moirestrain.apply_valid_mask(array: ndarray, valid_mask: ndarray, fill_value: float = nan) ndarray
Return a copy of
arraywith invalid pixels replaced byfill_value.
- moirestrain.crop_grating_roi(image: ndarray, roi: GratingROI, *, margin: int = 0) ndarray
Crop the axis-aligned bounding box of a detected grating ROI.
- moirestrain.crop_roi(image: ndarray, bounds: tuple[int, int, int, int]) ndarray
Crop a rectangular ROI as
(y0, x0, y1, x1).
- moirestrain.crop_to_mask(array: ndarray, valid_mask: ndarray) ndarray
Crop an array to the bounding box of
valid_mask.
- moirestrain.detect_grating_roi(image: ndarray, *, period: int, threshold: float | None = None, min_area: int | None = None, corner_method: str = 'oriented_box') GratingROI
Detect the dominant grating patch by thresholding grating energy.
- moirestrain.displacement(reference_phase: ndarray, deformed_phase: ndarray, grating_pitch: float, *, unwrap: bool = True, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) ndarray
Calculate displacement from phase difference.
The sign convention is
u = (phase_deformed - phase_reference) * pitch / 2pi. Use a negativegrating_pitchif your imaging setup uses the opposite phase-to-displacement convention.
- moirestrain.grating_energy(image: ndarray, *, period: int, window: int | None = None) ndarray
Calculate local high-frequency energy for grating ROI detection.
- moirestrain.homography_from_points(source_points: ndarray, destination_points: ndarray) ndarray
Estimate a projective transform from four point correspondences.
Points are
(x, y)pairs. The returned matrix maps homogeneous source coordinates to destination coordinates.
- moirestrain.inner_valid_mask(shape: tuple[int, int], margin: int) ndarray
Return a mask that excludes a fixed margin from image borders.
- moirestrain.load_grayscale_image(path: str | Path, *, npz_key: str | None = None) ndarray
Load a grayscale image from
.npy,.npz, or common image files.
- moirestrain.make_microstrain_experiment(*, shape: tuple[int, int] = (128, 160), period: int = 8, strain_xx: float = 0.0008, rigid_shift: float = 0.6, noise_std: float = 0.01, contrast: float = 0.42, background: float = 0.5, illumination_gradient: float = 0.15, seed: int | None = 0) SyntheticExperiment
Create an experimental-looking grating image pair.
The deformed image contains a known x-direction displacement field
u = rigid_shift + strain_xx * (x - x_center). Images are normalized to roughly[0, 1]and include smooth illumination variation plus Gaussian sensor noise.
- moirestrain.make_microstrain_square_grid(*, shape: tuple[int, int] = (160, 192), period: int = 8, marker_size: int | None = None, strain_xx: float = 0.0005, strain_yy: float = -0.0002, shear_xy: float = 0.00015, rigid_shift: tuple[float, float] = (0.6, -0.35), supersample: int = 8, blur_window: int = 3, noise_std: float = 0.0, seed: int | None = 0) SyntheticSquareGridExperiment
Create a fronto-parallel square-marker grid with known microstrain.
The target geometry is white background with black square markers on a regular grid. By default,
marker_size = period / 2, so the black square width equals the white gap width. The captured image is simulated by supersampling each pixel, then applying an optional box blur.
- moirestrain.make_strain_distribution_experiment(*, shape: tuple[int, int] = (160, 192), period: int = 8, strain_xx: float = 0.0008, strain_yy: float = -0.00025, shear_xy: float = 0.00035, rigid_shift: tuple[float, float] = (0.6, -0.35), noise_std: float = 0.008, contrast: float = 0.42, background: float = 0.5, illumination_gradient: float = 0.12, seed: int | None = 1) SyntheticStrainExperiment
Create x/y grating images for full small-strain recovery.
The displacement field is linear:
u = u0 + exx * x + 0.5 * gamma_xy * yv = v0 + eyy * y + 0.5 * gamma_xy * xCoordinates are centered, so the rigid shift is easy to interpret.
- moirestrain.mask_bounds(valid_mask: ndarray) tuple[int, int, int, int]
Return
(y0, x0, y1, x1)bounds for true pixels in a mask.
- moirestrain.phase_shifted_sampling_moire(reference_image: ndarray, deformed_image: ndarray, period: int, *, axis: Literal['x', 'y', 0, 1] = 'x', grating_pitch: float | None = None, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) AnalysisResult
Analyze displacement with the phase-shifted sampling moire method.
This is an explicit alias for
analyze(). Internally it generatesperiodphase-shifted moire images by shifting the sampling offset and estimates the wrapped phase using the phase-shifting/DFT formula inwrapped_phase().
- moirestrain.phase_shifted_stack(image: ndarray, period: int, axis: Literal['x', 'y', 0, 1] = 'x') ndarray
Generate phase-shifted moire images from one grating image.
The image is thinned every
periodpixels at each possible offset, then linearly interpolated back to the original size. The returned array has shape(period, height, width).
- moirestrain.pixel_spacing_from_world_points(world_points: ndarray, output_shape: tuple[int, int]) tuple[float, float]
Estimate
(dy, dx)spacing from rectangular world corner points.
- moirestrain.recommended_strain_smoothing_window(period: int, *, cycles: float = 8.0, minimum: int = 9) int
Return an odd smoothing window for strain estimation.
Square-marker targets contain strong harmonics, and displacement fields can retain pixel-periodic ripple after phase analysis. Strain calculation differentiates displacement, so this ripple should be smoothed before gradients are taken.
cyclescontrols the smoothing length in grating periods. Larger values reduce ripple but lower spatial resolution.
- moirestrain.rectangle_points(shape: tuple[int, int]) ndarray
Return rectangle corner points for
shapein(x, y)order.
- moirestrain.rectify_image(image: ndarray, calibration_or_points: PerspectiveCalibration | ndarray, *, output_shape: tuple[int, int] | None = None, world_points: ndarray | None = None, fill_value: float = nan) ndarray
Rectify a planar grating ROI to a fronto-parallel image.
Pass either a
PerspectiveCalibrationor four image corner points. When passing points directly,output_shapeis required. The point order should be top-left, top-right, bottom-right, bottom-left.
- moirestrain.rectify_image_pair(reference_image: ndarray, deformed_image: ndarray, calibration_or_points: PerspectiveCalibration | ndarray, *, output_shape: tuple[int, int] | None = None, world_points: ndarray | None = None, fill_value: float = nan) tuple[ndarray, ndarray]
Rectify reference/deformed images with the same planar transform.
- moirestrain.resample_oblique_grid(image: ndarray, *, origin: tuple[float, float], x_vector: tuple[float, float], y_vector: tuple[float, float], output_shape: tuple[int, int], fill_value: float = nan) ndarray
Sample an image on an oblique grid defined by two image-space vectors.
This is useful when the grating is rotated or sheared but a full perspective model is unnecessary.
origin,x_vector, andy_vectorare image-space(x, y)coordinates. The output x/y axes follow those vectors.
- moirestrain.robust_limits(array: ndarray, *, percentiles: tuple[float, float] = (2.0, 98.0)) tuple[float, float]
Return percentile color limits while ignoring NaNs.
- moirestrain.sample_bilinear(image: ndarray, x: ndarray, y: ndarray, fill_value: float = nan) ndarray
Sample a 2D image at floating-point
x/ycoordinates.
- moirestrain.save_analysis_npz(path: str | Path, *, u: ndarray, v: ndarray, exx: ndarray, eyy: ndarray, gamma_xy: ndarray, valid_mask: ndarray, reference: ndarray | None = None, deformed: ndarray | None = None, extra_arrays: Mapping[str, Any] | None = None) None
Save grid-analysis arrays to a compressed
.npzfile.
- moirestrain.save_experiment_npz(path: str | Path, experiment: SyntheticExperiment) None
Save a synthetic experiment to a compressed
.npzfile.
- moirestrain.save_grayscale_image(path: str | Path, image: ndarray) None
Save a grayscale array to
.npy,.npz, or a common image file.
- moirestrain.save_grid_analysis_figure(path: str | Path, *, reference: ndarray, deformed: ndarray, u: ndarray, exx: ndarray, eyy: ndarray, gamma_xy: ndarray, valid_mask: ndarray) None
Save a compact PNG summary for grid analysis.
- moirestrain.save_grid_truth_comparison_figure(path: str | Path, *, measured: dict[str, ndarray], truth: dict[str, ndarray], valid_mask: ndarray) None
Save measured/truth/error comparison panels for grid analysis.
- moirestrain.save_square_grid_experiment_npz(path: str | Path, experiment: SyntheticSquareGridExperiment) None
Save a square-marker grid experiment to
.npz.
- moirestrain.save_strain_experiment_npz(path: str | Path, experiment: SyntheticStrainExperiment) None
Save a two-direction synthetic strain experiment to
.npz.
- moirestrain.separate_grid_components(image: ndarray, *, period: int) tuple[ndarray, ndarray]
Separate a square 2D grid target into x/y grating components.
Smoothing along y suppresses row-wise markers and leaves the x-periodic component. Smoothing along x suppresses column-wise markers and leaves the y-periodic component.
- moirestrain.smooth_axis(image: ndarray, window: int, axis: Literal['x', 'y', 0, 1]) ndarray
Smooth a 2D image along one axis using a box filter.
- moirestrain.strain_field(u: ndarray, v: ndarray | None = None, *, spacing: float | tuple[float, float] = 1.0, smooth_window: int | tuple[int, int] = 1) StrainResult
Calculate small-strain components from displacement fields.
- Parameters:
u – x-direction displacement field.
v – Optional y-direction displacement field. When omitted, only
exxis returned andeyy/gamma_xyareNone.spacing – Pixel spacing as a scalar or
(dy, dx)tuple. The returned strain is displacement unit divided by spacing unit.smooth_window – Optional box smoothing window applied before differentiation. This is useful because strain estimation differentiates displacement noise.
- moirestrain.unwrap_phase(phase: ndarray, axis: Literal['x', 'y', 0, 1] | None = None) ndarray
Unwrap phase along one axis or both image axes.
- moirestrain.warp_perspective(image: ndarray, homography: ndarray, output_shape: tuple[int, int], *, fill_value: float = nan) ndarray
Warp an image with bilinear interpolation.
homographymaps input image coordinates to output image coordinates. The implementation samples the input image by applying the inverse homography to each output pixel.
- moirestrain.wrapped_phase(images: ndarray) ndarray
Estimate wrapped phase from equally phase-shifted images.
imagesmust have shape(n_shifts, height, width). For synthetic dataI_k = a + b * cos(phi + 2*pi*k/n_shifts), this returnsphiwrapped to[-pi, pi].
Core module
- class moirestrain.core.AnalysisResult(reference_phase: ndarray, deformed_phase: ndarray, phase_difference: ndarray, displacement: ndarray)
Bases:
objectResult returned by
analyze().- deformed_phase: ndarray
- displacement: ndarray
- phase_difference: ndarray
- reference_phase: ndarray
- class moirestrain.core.GridAnalysisResult(x: AnalysisResult, y: AnalysisResult, strain: StrainResult, reference_x_component: ndarray, reference_y_component: ndarray, deformed_x_component: ndarray, deformed_y_component: ndarray)
Bases:
objectTwo-direction grid analysis result.
- deformed_x_component: ndarray
- deformed_y_component: ndarray
- reference_x_component: ndarray
- reference_y_component: ndarray
- strain: StrainResult
- class moirestrain.core.StrainResult(exx: ndarray, eyy: ndarray | None, gamma_xy: ndarray | None)
Bases:
objectSmall-strain components calculated from displacement fields.
- exx: ndarray
- eyy: ndarray | None
- gamma_xy: ndarray | None
- moirestrain.core.analyze(reference_image: ndarray, deformed_image: ndarray, period: int, *, axis: Literal['x', 'y', 0, 1] = 'x', grating_pitch: float | None = None, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) AnalysisResult
Run the sampling moire pipeline for a reference/deformed image pair.
- moirestrain.core.analyze_grid(reference_image: ndarray, deformed_image: ndarray, period: int, *, grating_pitch: float | None = None, strain_spacing: float | tuple[float, float] = 1.0, strain_smooth_window: int | tuple[int, int] = 1) GridAnalysisResult
Analyze a two-direction square-marker grid image pair.
The x/y grating components are first separated by directional smoothing. Each component is then analyzed with the phase-shifted sampling moire method, and small-strain components are calculated from the resulting displacement fields.
- moirestrain.core.displacement(reference_phase: ndarray, deformed_phase: ndarray, grating_pitch: float, *, unwrap: bool = True, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) ndarray
Calculate displacement from phase difference.
The sign convention is
u = (phase_deformed - phase_reference) * pitch / 2pi. Use a negativegrating_pitchif your imaging setup uses the opposite phase-to-displacement convention.
- moirestrain.core.phase_shifted_sampling_moire(reference_image: ndarray, deformed_image: ndarray, period: int, *, axis: Literal['x', 'y', 0, 1] = 'x', grating_pitch: float | None = None, unwrap_axis: Literal['x', 'y', 0, 1] | None = None) AnalysisResult
Analyze displacement with the phase-shifted sampling moire method.
This is an explicit alias for
analyze(). Internally it generatesperiodphase-shifted moire images by shifting the sampling offset and estimates the wrapped phase using the phase-shifting/DFT formula inwrapped_phase().
- moirestrain.core.phase_shifted_stack(image: ndarray, period: int, axis: Literal['x', 'y', 0, 1] = 'x') ndarray
Generate phase-shifted moire images from one grating image.
The image is thinned every
periodpixels at each possible offset, then linearly interpolated back to the original size. The returned array has shape(period, height, width).
- moirestrain.core.recommended_strain_smoothing_window(period: int, *, cycles: float = 8.0, minimum: int = 9) int
Return an odd smoothing window for strain estimation.
Square-marker targets contain strong harmonics, and displacement fields can retain pixel-periodic ripple after phase analysis. Strain calculation differentiates displacement, so this ripple should be smoothed before gradients are taken.
cyclescontrols the smoothing length in grating periods. Larger values reduce ripple but lower spatial resolution.
- moirestrain.core.separate_grid_components(image: ndarray, *, period: int) tuple[ndarray, ndarray]
Separate a square 2D grid target into x/y grating components.
Smoothing along y suppresses row-wise markers and leaves the x-periodic component. Smoothing along x suppresses column-wise markers and leaves the y-periodic component.
- moirestrain.core.smooth_axis(image: ndarray, window: int, axis: Literal['x', 'y', 0, 1]) ndarray
Smooth a 2D image along one axis using a box filter.
- moirestrain.core.strain_field(u: ndarray, v: ndarray | None = None, *, spacing: float | tuple[float, float] = 1.0, smooth_window: int | tuple[int, int] = 1) StrainResult
Calculate small-strain components from displacement fields.
- Parameters:
u – x-direction displacement field.
v – Optional y-direction displacement field. When omitted, only
exxis returned andeyy/gamma_xyareNone.spacing – Pixel spacing as a scalar or
(dy, dx)tuple. The returned strain is displacement unit divided by spacing unit.smooth_window – Optional box smoothing window applied before differentiation. This is useful because strain estimation differentiates displacement noise.
- moirestrain.core.unwrap_phase(phase: ndarray, axis: Literal['x', 'y', 0, 1] | None = None) ndarray
Unwrap phase along one axis or both image axes.
- moirestrain.core.wrapped_phase(images: ndarray) ndarray
Estimate wrapped phase from equally phase-shifted images.
imagesmust have shape(n_shifts, height, width). For synthetic dataI_k = a + b * cos(phi + 2*pi*k/n_shifts), this returnsphiwrapped to[-pi, pi].
Synthetic data
- class moirestrain.synthetic.SyntheticExperiment(reference_image: ndarray, deformed_image: ndarray, true_u: ndarray, true_exx: ndarray, period: int, strain_xx: float, rigid_shift: float, noise_std: float)
Bases:
objectSynthetic grating image pair with ground-truth displacement.
- deformed_image: ndarray
- noise_std: float
- period: int
- reference_image: ndarray
- rigid_shift: float
- strain_xx: float
- true_exx: ndarray
- true_u: ndarray
- class moirestrain.synthetic.SyntheticSquareGridExperiment(reference: ndarray, deformed: ndarray, true_u: ndarray, true_v: ndarray, true_exx: ndarray, true_eyy: ndarray, true_gamma_xy: ndarray, period: int, marker_size: int, strain_xx: float, strain_yy: float, shear_xy: float, rigid_shift: tuple[float, float], noise_std: float)
Bases:
objectSynthetic square-marker grid pair with ground-truth strain.
- deformed: ndarray
- marker_size: int
- noise_std: float
- period: int
- reference: ndarray
- rigid_shift: tuple[float, float]
- shear_xy: float
- strain_xx: float
- strain_yy: float
- true_exx: ndarray
- true_eyy: ndarray
- true_gamma_xy: ndarray
- true_u: ndarray
- true_v: ndarray
- class moirestrain.synthetic.SyntheticStrainExperiment(reference_x: ndarray, deformed_x: ndarray, reference_y: ndarray, deformed_y: ndarray, true_u: ndarray, true_v: ndarray, true_exx: ndarray, true_eyy: ndarray, true_gamma_xy: ndarray, period: int, strain_xx: float, strain_yy: float, shear_xy: float, rigid_shift: tuple[float, float], noise_std: float)
Bases:
objectSynthetic two-direction grating data with ground-truth strain.
- deformed_x: ndarray
- deformed_y: ndarray
- noise_std: float
- period: int
- reference_x: ndarray
- reference_y: ndarray
- rigid_shift: tuple[float, float]
- shear_xy: float
- strain_xx: float
- strain_yy: float
- true_exx: ndarray
- true_eyy: ndarray
- true_gamma_xy: ndarray
- true_u: ndarray
- true_v: ndarray
- moirestrain.synthetic.make_microstrain_experiment(*, shape: tuple[int, int] = (128, 160), period: int = 8, strain_xx: float = 0.0008, rigid_shift: float = 0.6, noise_std: float = 0.01, contrast: float = 0.42, background: float = 0.5, illumination_gradient: float = 0.15, seed: int | None = 0) SyntheticExperiment
Create an experimental-looking grating image pair.
The deformed image contains a known x-direction displacement field
u = rigid_shift + strain_xx * (x - x_center). Images are normalized to roughly[0, 1]and include smooth illumination variation plus Gaussian sensor noise.
- moirestrain.synthetic.make_microstrain_square_grid(*, shape: tuple[int, int] = (160, 192), period: int = 8, marker_size: int | None = None, strain_xx: float = 0.0005, strain_yy: float = -0.0002, shear_xy: float = 0.00015, rigid_shift: tuple[float, float] = (0.6, -0.35), supersample: int = 8, blur_window: int = 3, noise_std: float = 0.0, seed: int | None = 0) SyntheticSquareGridExperiment
Create a fronto-parallel square-marker grid with known microstrain.
The target geometry is white background with black square markers on a regular grid. By default,
marker_size = period / 2, so the black square width equals the white gap width. The captured image is simulated by supersampling each pixel, then applying an optional box blur.
- moirestrain.synthetic.make_strain_distribution_experiment(*, shape: tuple[int, int] = (160, 192), period: int = 8, strain_xx: float = 0.0008, strain_yy: float = -0.00025, shear_xy: float = 0.00035, rigid_shift: tuple[float, float] = (0.6, -0.35), noise_std: float = 0.008, contrast: float = 0.42, background: float = 0.5, illumination_gradient: float = 0.12, seed: int | None = 1) SyntheticStrainExperiment
Create x/y grating images for full small-strain recovery.
The displacement field is linear:
u = u0 + exx * x + 0.5 * gamma_xy * yv = v0 + eyy * y + 0.5 * gamma_xy * xCoordinates are centered, so the rigid shift is easy to interpret.
- moirestrain.synthetic.save_experiment_npz(path: str | Path, experiment: SyntheticExperiment) None
Save a synthetic experiment to a compressed
.npzfile.
- moirestrain.synthetic.save_square_grid_experiment_npz(path: str | Path, experiment: SyntheticSquareGridExperiment) None
Save a square-marker grid experiment to
.npz.
- moirestrain.synthetic.save_strain_experiment_npz(path: str | Path, experiment: SyntheticStrainExperiment) None
Save a two-direction synthetic strain experiment to
.npz.
Geometry
- class moirestrain.geometry.PerspectiveCalibration(image_points: ndarray, world_points: ndarray, output_shape: tuple[int, int], homography: ndarray, pixel_spacing: tuple[float, float])
Bases:
objectPlanar perspective calibration for a grating region.
image_pointsandworld_pointsare four(x, y)corner points in corresponding order. The homography maps image coordinates to rectified pixel coordinates.- classmethod from_points(image_points: ndarray, world_points: ndarray, *, output_shape: tuple[int, int]) PerspectiveCalibration
- homography: ndarray
- image_points: ndarray
- output_shape: tuple[int, int]
- pixel_spacing: tuple[float, float]
- world_points: ndarray
- moirestrain.geometry.apply_homography(points: ndarray, homography: ndarray) ndarray
Apply a homography to
(..., 2)points.
- moirestrain.geometry.crop_roi(image: ndarray, bounds: tuple[int, int, int, int]) ndarray
Crop a rectangular ROI as
(y0, x0, y1, x1).
- moirestrain.geometry.homography_from_points(source_points: ndarray, destination_points: ndarray) ndarray
Estimate a projective transform from four point correspondences.
Points are
(x, y)pairs. The returned matrix maps homogeneous source coordinates to destination coordinates.
- moirestrain.geometry.pixel_spacing_from_world_points(world_points: ndarray, output_shape: tuple[int, int]) tuple[float, float]
Estimate
(dy, dx)spacing from rectangular world corner points.
- moirestrain.geometry.rectangle_points(shape: tuple[int, int]) ndarray
Return rectangle corner points for
shapein(x, y)order.
- moirestrain.geometry.rectify_image(image: ndarray, calibration_or_points: PerspectiveCalibration | ndarray, *, output_shape: tuple[int, int] | None = None, world_points: ndarray | None = None, fill_value: float = nan) ndarray
Rectify a planar grating ROI to a fronto-parallel image.
Pass either a
PerspectiveCalibrationor four image corner points. When passing points directly,output_shapeis required. The point order should be top-left, top-right, bottom-right, bottom-left.
- moirestrain.geometry.rectify_image_pair(reference_image: ndarray, deformed_image: ndarray, calibration_or_points: PerspectiveCalibration | ndarray, *, output_shape: tuple[int, int] | None = None, world_points: ndarray | None = None, fill_value: float = nan) tuple[ndarray, ndarray]
Rectify reference/deformed images with the same planar transform.
- moirestrain.geometry.resample_oblique_grid(image: ndarray, *, origin: tuple[float, float], x_vector: tuple[float, float], y_vector: tuple[float, float], output_shape: tuple[int, int], fill_value: float = nan) ndarray
Sample an image on an oblique grid defined by two image-space vectors.
This is useful when the grating is rotated or sheared but a full perspective model is unnecessary.
origin,x_vector, andy_vectorare image-space(x, y)coordinates. The output x/y axes follow those vectors.
- moirestrain.geometry.sample_bilinear(image: ndarray, x: ndarray, y: ndarray, fill_value: float = nan) ndarray
Sample a 2D image at floating-point
x/ycoordinates.
- moirestrain.geometry.warp_perspective(image: ndarray, homography: ndarray, output_shape: tuple[int, int], *, fill_value: float = nan) ndarray
Warp an image with bilinear interpolation.
homographymaps input image coordinates to output image coordinates. The implementation samples the input image by applying the inverse homography to each output pixel.
ROI detection
- class moirestrain.roi.GratingROI(image_points: ndarray, bounds: tuple[int, int, int, int], mask: ndarray, energy: ndarray)
Bases:
objectDetected grating region in a larger image.
boundsandmaskare the primary detection results.image_pointsare optional rectification hints estimated from the mask; they are not required when the downstream analysis works on an axis-aligned crop.- bounds: tuple[int, int, int, int]
- energy: ndarray
- image_points: ndarray
- mask: ndarray
- moirestrain.roi.crop_grating_roi(image: ndarray, roi: GratingROI, *, margin: int = 0) ndarray
Crop the axis-aligned bounding box of a detected grating ROI.
- moirestrain.roi.detect_grating_roi(image: ndarray, *, period: int, threshold: float | None = None, min_area: int | None = None, corner_method: str = 'oriented_box') GratingROI
Detect the dominant grating patch by thresholding grating energy.
- moirestrain.roi.grating_energy(image: ndarray, *, period: int, window: int | None = None) ndarray
Calculate local high-frequency energy for grating ROI detection.
Masking and display helpers
- moirestrain.masking.apply_valid_mask(array: ndarray, valid_mask: ndarray, fill_value: float = nan) ndarray
Return a copy of
arraywith invalid pixels replaced byfill_value.
- moirestrain.masking.crop_to_mask(array: ndarray, valid_mask: ndarray) ndarray
Crop an array to the bounding box of
valid_mask.
- moirestrain.masking.inner_valid_mask(shape: tuple[int, int], margin: int) ndarray
Return a mask that excludes a fixed margin from image borders.
- moirestrain.masking.mask_bounds(valid_mask: ndarray) tuple[int, int, int, int]
Return
(y0, x0, y1, x1)bounds for true pixels in a mask.
- moirestrain.masking.robust_limits(array: ndarray, *, percentiles: tuple[float, float] = (2.0, 98.0)) tuple[float, float]
Return percentile color limits while ignoring NaNs.