PVL Image Translation Offset Detector
Measure how far a frame drifted, down to sub-pixels
- original_image
- shifted_image
- mask
- x_offsets_str
- y_offsets_str
Every so often you're handed two images that are almost the same frame - a video that jittered between takes, two passes of a workflow where the crop position shifted, an animation sequence with a camera nudge - and you need to know, in pixels, how much the second one moved. PVL Image Translation Offset Detector is the node for that. It compares a "shifted" image against an "original" and reports the x and y offsets, with sub-pixel precision if you want it.
It's not a daily driver. It's the tool you dig out when you're doing image registration or frame alignment and you'd otherwise be eyeballing it. Given how niche that is, it's a genuinely useful thing for a pack like pvlprk/comfyui-pvl-api-nodes ("ComfyUI Assistant Node") to include.
How it works
Under the hood it's phase_cross_correlation from scikit-image - a well-trodden, library-grade registration algorithm. Here's the part that makes it practical: the mask input. You use it to tell the node which regions changed (a moving object, a person, an edit) so they get ignored when computing the shift. Pixels where the mask is above 0.5 are excluded from the correlation; everything else counts as stationary reference. Feed a white mask over the thing that moved, black everywhere else, and the node measures the background shift instead of chasing your subject.
It converts images to grayscale, sizes the mask to match, computes the shift per image pair, and returns the offsets as strings, joined by a delimiter ([++] by default). Two outputs: x_offsets_str and y_offsets_str. The sign convention is baked in (offset * -1), so a positive x offset means the shifted image moved right relative to the original, roughly - test once against a known example before trusting it blindly.
Inputs that matter
original_image/shifted_image- the twoIMAGEinputs, in that order.mask- aMASK; white = ignore that region. The one input that makes or breaks the result.upsample_factor- default 100. This is your sub-pixel precision dial; higher = finer shift estimates at some cost in time. Drop it to 1 if you only need whole-pixel answers.overlap_ratio- default 0.3. Minimum overlap required between images for the correlation to be trusted.
Installing
cd ComfyUI/custom_nodes
git clone https://github.com/pvlprk/comfyui-pvl-api-nodes
restart, or install via ComfyUI Manager (search "ComfyUI Assistant Node"). This node does have a real extra dependency: scikit-image, which is in the pack's requirements.txt but isn't something vanilla ComfyUI ships. If Manager didn't auto-install requirements, you'll need:
pip install scikit-image
No models to download.
Where people get tripped up
The outputs are strings, not numbers. If you want to feed the offset into a math node or an image transform, run it through PVL String To Number (same pack) first - that's clearly how the author expects you to use it, given the delimiter convention matches the pack's string utilities. And remember the batch behavior: if you feed N pairs, you get N offsets joined together, one per frame. For a single pair you get a single value per output.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| original_image | IMAGE | — | |
| shifted_image | IMAGE | — | |
| mask | MASK | — | |
| upsample_factor | INT | 1001–200 | — |
| overlap_ratio | FLOAT | 0.300.1–0.9 | — |
| delimiter | STRING | [++] | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| x_offsets_str | STRING | — |
| y_offsets_str | STRING | — |