Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Experimental Dislocation

Synthetic Dislocation found a clean signature on a known ground truth: a straddling window's correlation surface shows two comparably-tall peaks, not one. A synthetic image is generous, though. It has no camera noise, no lighting variation, no unknown displacement field. This section repeats the same experiment on a real crack, to check whether the signature survives outside a synthetic setup.

Data Download

Both images below are real 512x512 experimental micrographs of a crack, included unmodified rather than generated by this book.

FileDescriptionSize
experimental_dislocation_reference.tiffReference configuration, 512x512 pixels256 KB
experimental_dislocation_current.tiffDeformed configuration, 512x512 pixels256 KB
from dictk.image import read, write

reference_image = read(path="experimental_dislocation_reference.tiff")
current_image = read(path="experimental_dislocation_current.tiff")

write(arr=reference_image, path="experimental_dislocation_reference_preview.png")
write(arr=current_image, path="experimental_dislocation_current_preview.png")
Saved: experimental_dislocation_reference_preview.png, experimental_dislocation_current_preview.png
Reference configuration (left) and deformed configuration (right), the same real image pair downloaded above (click either for the full-size version). No crack is visible in the reference image at all. It only appears in the deformed image, as a diagonal band running through the surface. Closed and invisible before loading, open and visible after.

A Window Straddling the Real Crack

Unlike the synthetic case, the true displacement field here isn't known in advance. It isn't purely vertical either: a real crack can open at an angle, not just split into a clean up/down jump. Center a window directly on the crack, at x=218, y=186, with kernel_margin=25 and search_margin=65 (generous enough that the true match can't fall outside the search area and get clipped):

from dictk.image import subimage, PixelCoordinate
from dictk.correlation import zncc
from dictk.plot import spatial_correlation_quadrant_plot, phase_correlation_quadrant_plot

p0 = PixelCoordinate(x=218, y=186)
kernel_margin, search_margin = 25, 65
kernel = subimage(
    image=reference_image,
    origin=PixelCoordinate(x=p0.x - kernel_margin, y=p0.y - kernel_margin),
    width=2 * kernel_margin, height=2 * kernel_margin,
)
search = subimage(
    image=current_image,
    origin=PixelCoordinate(x=p0.x - search_margin, y=p0.y - search_margin),
    width=2 * search_margin, height=2 * search_margin,
)

spatial_correlation_quadrant_plot(
    kernel=kernel, search=search,
    correlation_surface=zncc(kernel=kernel, search=search),
    title="Zero-mean Normalized Cross-Correlation (ZNCC)",
    path="experimental_dislocation_zncc.png",
)
Saved: experimental_dislocation_zncc.png, experimental_dislocation_phase.png
ZNCC quadrant plot on the real crack image pair: a bumpy, noisy correlation surface with two clearly-distinct bright peaks along the same column, well above a third, smaller local maximum and the general noise floor
ZNCC's Correlation Surface panel on real data: noisier throughout than Synthetic Dislocation's near-flat background, as expected for real texture, but the same signature still stands out. Along the column through the tallest value, three local maxima appear at Δy = 9, 31, and 53 pixels, with heights 0.09, 0.19, and 0.28. The two tallest, at Δy = 31 and 53, sit 22 pixels apart. Both clearly exceed the third peak and the surrounding noise, and both are comparably tall (0.19 vs. 0.28). They straddle the crack the same way Synthetic Dislocation's did.

FFT on Real Texture: Noisier, Not Just Smaller

phase_correlation_quadrant_plot(
    kernel=kernel, search=search,
    title="Phase Correlation (FFT)",
    path="experimental_dislocation_phase.png",
)
Phase correlation quadrant plot on the same real crack image pair: a speckled, noisy correlation surface with no clean isolated peak, values an order of magnitude smaller than ZNCC's, and its single reported maximum sitting at a different position than ZNCC's own tallest peak
Phase correlation's Correlation Surface panel on the same window: speckled with noise across its entire extent, not the "flat except one sharp peak" pattern Phase Correlation and Synthetic Dislocation both showed. Its single reported maximum, 0.04, is an order of magnitude below ZNCC's 0.28, and lands at a different position than ZNCC's own tallest peak.

Both criteria agreed exactly on synthetic data. They don't here. Real, non-periodic texture is exactly the case phase_correlation's own docstring already warns about: the raw FFT surface is far more sensitive to noise than a spatial-domain criterion computed the same window. ZNCC's two-peak signature is the one worth trusting on real data. This evidence puts the FFT surface itself in doubt as a diagnostic.

What Carries Over From the Synthetic Case

The core finding survives: a window straddling a real discontinuity still shows two comparably-tall peaks, not one, matching Synthetic Dislocation's result. What changes on real data is how cleanly the signature shows up. Here it's a bumpy, noisy background rather than a flat one, plus a real gap between the criteria that a synthetic, noise-free image can't reveal.

Discontinuities named the actual open problem: an algorithm that finds this signature on its own. Nothing here does that. Continue to Discontinuity Localization, which tries a few ways to build one.

experimental_dislocation_quadrant.py

"""Plot the correlation surface a window straddling a real experimental
crack produces, ZNCC and FFT side by side. The two source images are
real experimental micrographs, copied in unmodified.
"""

from dictk.image import read, subimage, PixelCoordinate
from dictk.correlation import zncc
from dictk.plot import (
    spatial_correlation_quadrant_plot,
    phase_correlation_quadrant_plot,
)

KERNEL_MARGIN = 25
SEARCH_MARGIN = 65

reference_image = read(path="experimental_dislocation_reference.tiff")
current_image = read(path="experimental_dislocation_current.tiff")

p0 = PixelCoordinate(x=218, y=186)
kernel = subimage(
    image=reference_image,
    origin=PixelCoordinate(x=p0.x - KERNEL_MARGIN, y=p0.y - KERNEL_MARGIN),
    width=2 * KERNEL_MARGIN,
    height=2 * KERNEL_MARGIN,
)
search = subimage(
    image=current_image,
    origin=PixelCoordinate(x=p0.x - SEARCH_MARGIN, y=p0.y - SEARCH_MARGIN),
    width=2 * SEARCH_MARGIN,
    height=2 * SEARCH_MARGIN,
)

spatial_correlation_quadrant_plot(
    kernel=kernel,
    search=search,
    correlation_surface=zncc(kernel=kernel, search=search),
    title="Zero-mean Normalized Cross-Correlation (ZNCC)",
    path="experimental_dislocation_zncc.png",
)
phase_correlation_quadrant_plot(
    kernel=kernel,
    search=search,
    title="Phase Correlation (FFT)",
    path="experimental_dislocation_phase.png",
)

print("Saved: experimental_dislocation_zncc.png, experimental_dislocation_phase.png")