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

High Point Density

Simple Stretch Revisited capped out at 250 points — the most x values that stay integer-safe at factor_x = 1.02, within the image's own margins. Subpixel Accuracy removed that ceiling: once tracking doesn't need its answer to be a whole pixel, x doesn't need to be a multiple of 50 either. This page pushes all the way to VIC-2D's own density — 5 pixels apart, the same 53x54, 2862-point grid Verification Against VIC-2D and Subpixel Accuracy both already used.

Tracking at Full Density

from dictk.grid import generate, locate_subpixel

points = generate(
    origin=PixelCoordinate(x=18, y=16),
    count_x=53,
    count_y=54,
    spacing_x=5,
    spacing_y=5,
)
found = locate_subpixel(
    reference_image=reference_image,
    current_image=current_image,
    reference_points=points,
    kernel_margin_width=20,
    kernel_margin_height=20,
    search_margin_width=48,
    search_margin_height=52,
    upsample_factor=10,
)

2862 points tracked

Strain at Full Density

Same recipe as Simple Stretch Revisited: dictk.grid.elements for connectivity (2756 elements this time, not 196), then gauss_point_log_strains/gauss_point_coordinates at each of the resulting 11024 Gauss points. Node numbers stay off — 2862 of them would be unreadable:

from dictk.element import gauss_point_coordinates, gauss_point_log_strains
from dictk.grid import elements
from dictk.plot import element_strain_plot

element_indices = elements(count_x=53, count_y=54)
values = []
coordinates = []
for element in element_indices:
    reference_corners = [points[i] for i in element]
    current_corners = [found[i] for i in element]
    strains = gauss_point_log_strains(
        reference_points=reference_corners, current_points=current_corners
    )
    values.extend(strain[0, 0] for strain in strains)
    coordinates.extend(gauss_point_coordinates(points=current_corners))

element_strain_plot(
    points=found,
    elements=element_indices,
    coordinates=coordinates,
    values=values,
    label=r"Log Strain, $E_{11}$",
    path="high_point_density_strain_gauss_points.png",
)
element_strain_plot(
    points=found,
    elements=element_indices,
    coordinates=coordinates,
    values=values,
    label=r"Log Strain, $E_{11}$",
    image=current_image,
    path="high_point_density_strain_on_current.png",
)
a dense 53x54 mesh with 4 Gauss points per element, colored by log strain E11, no node numbers, no background image
The full 2862-point mesh, colored by log strain .
the same dense mesh and colored Gauss points overlaid on current_image, the stretched astronaut photo
The same dense mesh, overlaid on current_image.

A Real Trade-Off, Not a Bug

2862-point, 5px-spacing mesh: mean = 0.0200 (true value 0.0198), but std = 0.0155, range [-0.0160, 0.0771]

The mean is accurate. The spread is not small. Unlike Simple Stretch Revisited's perfectly uniform result, individual elements here scatter well beyond the true value — some report negative strain, some report nearly 4 times the true value.

This isn't a tracking bug. Log strain is, in effect, a finite difference: , a displacement difference divided by element size . Subpixel Accuracy's own measurement found locate_subpixel's residual error is small in absolute terms — a few hundredths of a pixel, on average — but at 5 pixels of element spacing, that same absolute error is a much larger fraction of than it was at Simple Stretch Revisited's 50-pixel spacing. The smaller the element, the more a fixed amount of tracking noise gets amplified into strain noise. Checked directly, not just argued:

Element spacingMean E11Std E11
5px0.019880.01544
10px0.019970.01247
20px0.020030.00988
40px0.019990.00318

Standard deviation falls as element spacing grows — the same tracking noise, spread over a larger , moves less of the resulting strain. This is exactly why VIC-2D and other commercial DIC packages offer a strain window — averaging displacement over several subsets before computing strain, trading spatial resolution for strain precision. dictk doesn't implement that averaging yet. This page's own dense mesh is accurate on average and honestly noisy point to point, not silently smoothed into looking better than the underlying tracking supports.

Point count, tracking accuracy, and now strain precision have all been free variables throughout Simple Stretch, Subpixel Accuracy, and this page. How dictk's own tracking time scales as point count grows — across sequential, threaded, and multi-process execution — is Parallelization's own question, still not attempted here either.