Creates an animated morph by computing optimal pixel assignment from image A to image B, then rendering intermediate frames showing the transport.
pixel_morph_animate(
imgA,
imgB,
n_frames = 16L,
fps = 10L,
format = c("gif", "webp", "mp4"),
outfile = NULL,
show = interactive(),
mode = c("color_walk", "exact", "recursive"),
lap_method = "jv",
maximize = FALSE,
quantize_bits = 5L,
downscale_steps = 0L,
alpha = 1,
beta = 0,
patch_size = 1L,
upscale = 1
)Invisibly returns a list with animation object and metadata:
magick animation object
Image width in pixels
Image height in pixels
Integer vector of 1-based assignment indices (R convention)
Total number of pixels
Mode used for matching
Upscaling factor applied
Source image (file path or magick image object)
Target image (file path or magick image object)
Integer number of animation frames (default: 16)
Frames per second for playback (default: 10)
Output format: "gif", "webp", or "mp4"
Optional output file path
Logical, display animation in viewer (default: interactive())
Assignment algorithm: "color_walk" (default), "exact", or "recursive"
LAP solver method (default: "jv")
Logical, maximize instead of minimize cost (default: FALSE)
Color quantization for "color_walk" mode (default: 5)
Number of 2x reductions before computing assignment (default: 0)
Weight for color distance in cost function (default: 1)
Weight for spatial distance in cost function (default: 0)
Tile size for tiled modes (default: 1)
Post-rendering upscaling factor (default: 1)
CRITICAL: This function has two separate phases with different semantics:
Phase 1 - Assignment Computation:
The assignment is computed by minimizing:
cost(i,j) = alpha * color_distance(A[i], B[j]) +
beta * spatial_distance(pos_i, pos_j)
This means B's COLORS influence which pixels from A map to which positions.
Phase 2 - Rendering (Transport-Only):
The renderer uses ONLY A's colors:
Intermediate frames: A's pixels move along paths with motion blur
Final frame: A's pixels at their assigned positions (sharp, no blur)
B's colors NEVER appear in the output
Result: You get A's colors rearranged to match B's geometry/layout.
B influences WHERE pixels go (via similarity in cost function)
B does NOT determine WHAT COLORS appear in output
Final image has A's palette arranged to mimic B's structure
For pure spatial rearrangement (ignore B's colors in assignment):
pixel_morph_animate(A, B, alpha = 0, beta = 1)
For color-similarity matching (default):
pixel_morph_animate(A, B, alpha = 1, beta = 0)
For hybrid (color + spatial):
pixel_morph_animate(A, B, alpha = 1, beta = 0.2)
Assignment is guaranteed to be a bijection (permutation) ONLY when:
downscale_steps = 0 (no resolution changes)
mode = "exact" with patch_size = 1
With downscaling or tiled modes, assignment may have:
Overlaps: Multiple source pixels map to same destination (last write wins)
Holes: Some destinations never filled (remain transparent)
A warning is issued if overlaps/holes are detected in the final frame.
if (requireNamespace("magick", quietly = TRUE)) {
imgA <- system.file("extdata/icons/circleA_40.png", package = "couplr")
imgB <- system.file("extdata/icons/circleB_40.png", package = "couplr")
if (nzchar(imgA) && nzchar(imgB)) {
outfile <- tempfile(fileext = ".gif")
pixel_morph_animate(imgA, imgB, outfile = outfile, n_frames = 4, show = FALSE)
}
}
Run the code above in your browser using DataLab