dictk
dictk: Digital Image Correlation Toolkit.
CLI vs. API: the dictk command-line entry points (dictk rosta,
dictk checkerboard, dictk astronaut, ...) write image files to disk —
that's their whole job. The corresponding Python API functions
(dictk.rosta, dictk.checkerboard, dictk.astronaut, ...) do not
perform any file I/O; they return NumPy arrays only. This keeps the API
composable in a functional style — arrays
can be piped through further functions (e.g. dictk.imaging.combine_images)
before anything touches disk — and callers who do want a file call
dictk.imaging.write_image explicitly as a separate, deliberate step.
1"""dictk: Digital Image Correlation Toolkit. 2 3CLI vs. API: the `dictk` command-line entry points (`dictk rosta`, 4`dictk checkerboard`, `dictk astronaut`, ...) write image files to disk — 5that's their whole job. The corresponding Python API functions 6(`dictk.rosta`, `dictk.checkerboard`, `dictk.astronaut`, ...) do not 7perform any file I/O; they return NumPy arrays only. This keeps the API 8composable in a functional style — arrays 9can be piped through further functions (e.g. `dictk.imaging.combine_images`) 10before anything touches disk — and callers who do want a file call 11`dictk.imaging.write_image` explicitly as a separate, deliberate step. 12""" 13 14from importlib.metadata import PackageNotFoundError, version 15 16from dictk.core import zero_normalized_cross_correlation 17from dictk.imaging import astronaut, checkerboard 18from dictk.rosta import rosta 19 20try: 21 __version__ = version("dictk") 22except PackageNotFoundError: 23 __version__ = "0.0.0+unknown" 24 25__all__ = [ 26 "astronaut", 27 "checkerboard", 28 "rosta", 29 "zero_normalized_cross_correlation", 30 "__version__", 31]
50def astronaut(width: int = 512, height: int = 512) -> np.ndarray: 51 """Load a bundled real-world grayscale reference image. 52 53 Same parameters and pixel values as the `dictk astronaut` CLI 54 command, minus the file write — see the `dictk` package docstring for 55 why the API layer stops at the array. 56 57 The source is a NASA portrait of astronaut Eileen Collins, from the 58 NASA Great Images database ("No known copyright restrictions, released 59 into the public domain."). It's bundled as a color image and converted 60 with `rgba_to_gray`; unlike `rosta` and `checkerboard`, it isn't 61 procedurally generated, so `width`/`height` other than the native 62 512x512 resize the source image (via `scipy.ndimage.zoom`) rather than 63 computing a fresh pattern at that size. 64 65 Args: 66 width: Image width in pixels. Must be >= 1. 67 height: Image height in pixels. Must be >= 1. 68 69 Returns: 70 A 2D uint8 array of shape (height, width). 71 72 Raises: 73 ValueError: If width or height is less than 1. 74 """ 75 if width < 1: 76 raise ValueError(f"width {width} must be >= 1") 77 if height < 1: 78 raise ValueError(f"height {height} must be >= 1") 79 80 asset_path = importlib.resources.files("dictk") / "data" / "astronaut.png" 81 with importlib.resources.as_file(asset_path) as path: 82 color = read_image(path) 83 gray = rgba_to_gray(color) 84 85 native_height, native_width = gray.shape 86 if (width, height) == (native_width, native_height): 87 return gray 88 89 zoom_factors = (height / native_height, width / native_width) 90 resized = zoom(gray.astype(np.float64), zoom_factors, order=3) 91 return np.clip(resized, 0, 255).astype(np.uint8)
Load a bundled real-world grayscale reference image.
Same parameters and pixel values as the dictk astronaut CLI
command, minus the file write — see the dictk package docstring for
why the API layer stops at the array.
The source is a NASA portrait of astronaut Eileen Collins, from the
NASA Great Images database ("No known copyright restrictions, released
into the public domain."). It's bundled as a color image and converted
with rgba_to_gray; unlike rosta and checkerboard, it isn't
procedurally generated, so width/height other than the native
512x512 resize the source image (via scipy.ndimage.zoom) rather than
computing a fresh pattern at that size.
Args: width: Image width in pixels. Must be >= 1. height: Image height in pixels. Must be >= 1.
Returns: A 2D uint8 array of shape (height, width).
Raises: ValueError: If width or height is less than 1.
15def checkerboard( 16 width: int, height: int, count_x: int = 8, count_y: int = 8 17) -> np.ndarray: 18 """Generate a black-and-white checkerboard test image. 19 20 Same parameters and pixel values as the `dictk checkerboard` CLI 21 command, minus the file write — see the `dictk` package docstring for 22 why the API layer stops at the array. count_x and count_y are named for 23 the rectangles they divide the image into, not "squares": unequal 24 counts (or a width/height ratio that doesn't match count_x/count_y) 25 produce rectangular cells, not square ones. 26 27 Args: 28 width: Image width in pixels. 29 height: Image height in pixels. 30 count_x: Number of rectangles along the width. Must be >= 1. 31 count_y: Number of rectangles along the height. Must be >= 1. 32 33 Returns: 34 A 2D uint8 array of shape (height, width) with values 0 or 255. 35 36 Raises: 37 ValueError: If count_x or count_y is less than 1. 38 """ 39 if count_x < 1: 40 raise ValueError(f"count_x {count_x} must be >= 1") 41 if count_y < 1: 42 raise ValueError(f"count_y {count_y} must be >= 1") 43 44 rows = (np.arange(height) * count_y // height) % 2 45 cols = (np.arange(width) * count_x // width) % 2 46 pattern = np.logical_xor(rows[:, None], cols[None, :]) 47 return (pattern * 255).astype(np.uint8)
Generate a black-and-white checkerboard test image.
Same parameters and pixel values as the dictk checkerboard CLI
command, minus the file write — see the dictk package docstring for
why the API layer stops at the array. count_x and count_y are named for
the rectangles they divide the image into, not "squares": unequal
counts (or a width/height ratio that doesn't match count_x/count_y)
produce rectangular cells, not square ones.
Args: width: Image width in pixels. height: Image height in pixels. count_x: Number of rectangles along the width. Must be >= 1. count_y: Number of rectangles along the height. Must be >= 1.
Returns: A 2D uint8 array of shape (height, width) with values 0 or 255.
Raises: ValueError: If count_x or count_y is less than 1.
100def rosta( 101 width: int = 200, 102 height: int = 200, 103 *, 104 dot_size: float = 4.0, 105 density: float = 0.32, 106 smoothness: float = 2.0, 107 random_seed: int = 42, 108) -> np.ndarray: 109 """Generate a Rosta speckle pattern as a uint8 grayscale image array. 110 111 Same parameters and pixel values as the `dictk rosta` CLI command, minus 112 the file write — see the `dictk` package docstring for why the API 113 layer stops at the array. 114 115 Args: 116 width: Image width in pixels. 117 height: Image height in pixels. 118 dot_size: Size factor for pattern dots (0.0 to 100.0). 119 density: Density of pattern elements (0.0 to 1.0). 120 smoothness: Smoothness factor for final blur (0.0 to 100.0). 121 random_seed: Seed for reproducible random number generation. 122 123 Returns: 124 A 2D uint8 array of shape (height, width), grayscale in [0, 255]. 125 126 Raises: 127 ValueError: If dot_size, density, or smoothness is out of range. 128 """ 129 rp = RostaParameters( 130 image_size=ImageSize(width=width, height=height), 131 dot_size=dot_size, 132 density=density, 133 smoothness=smoothness, 134 random_seed=random_seed, 135 ) 136 pattern = rosta_pattern(rp) 137 return (pattern * 255).astype(np.uint8)
Generate a Rosta speckle pattern as a uint8 grayscale image array.
Same parameters and pixel values as the dictk rosta CLI command, minus
the file write — see the dictk package docstring for why the API
layer stops at the array.
Args: width: Image width in pixels. height: Image height in pixels. dot_size: Size factor for pattern dots (0.0 to 100.0). density: Density of pattern elements (0.0 to 1.0). smoothness: Smoothness factor for final blur (0.0 to 100.0). random_seed: Seed for reproducible random number generation.
Returns: A 2D uint8 array of shape (height, width), grayscale in [0, 255].
Raises: ValueError: If dot_size, density, or smoothness is out of range.
7def zero_normalized_cross_correlation(a: np.ndarray, b: np.ndarray) -> float: 8 """Compute the zero-normalized cross-correlation (ZNCC) between two arrays. 9 10 ZNCC is a similarity metric commonly used as the matching criterion in 11 digital image correlation (DIC) template matching: it compares two 12 equal-shaped patches (e.g. image subsets) while being invariant to 13 linear changes in brightness and contrast. A return value of ``1.0`` 14 indicates a perfect match, ``-1.0`` a perfect inverse match, and ``0.0`` 15 no correlation. 16 17 Args: 18 a: First array (e.g. a reference image subset). 19 b: Second array, same shape as ``a`` (e.g. a deformed image subset). 20 21 Returns: 22 The ZNCC score in the range ``[-1.0, 1.0]``. 23 24 Raises: 25 ValueError: If ``a`` and ``b`` do not have the same shape. 26 """ 27 a = np.asarray(a, dtype=np.float64) 28 b = np.asarray(b, dtype=np.float64) 29 30 if a.shape != b.shape: 31 raise ValueError(f"shape mismatch: a.shape={a.shape}, b.shape={b.shape}") 32 33 a_centered = a - a.mean() 34 b_centered = b - b.mean() 35 36 denominator = np.sqrt(np.sum(a_centered**2) * np.sum(b_centered**2)) 37 if denominator == 0: 38 return 0.0 39 40 return float(np.sum(a_centered * b_centered) / denominator)
Compute the zero-normalized cross-correlation (ZNCC) between two arrays.
ZNCC is a similarity metric commonly used as the matching criterion in
digital image correlation (DIC) template matching: it compares two
equal-shaped patches (e.g. image subsets) while being invariant to
linear changes in brightness and contrast. A return value of 1.0
indicates a perfect match, -1.0 a perfect inverse match, and 0.0
no correlation.
Args:
a: First array (e.g. a reference image subset).
b: Second array, same shape as a (e.g. a deformed image subset).
Returns:
The ZNCC score in the range [-1.0, 1.0].
Raises:
ValueError: If a and b do not have the same shape.