Nodes/ComfyUI CV/cv2.img_hash.pHash
ComfyUI Node

cv2.img_hash.pHash

The perceptual hash you should reach for first

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.img_hash.pHash
  • inputArr
  • nparray

pHash is the perceptual hash people mean when they say "perceptual hash." Instead of comparing pixel cells to a mean, it takes a low-frequency DCT of the image and encodes which coefficients sit above the median. The payoff is robustness: it survives rescaling, mild re-cropping-free edits, and heavy JPEG compression far better than the average hash, and it is still 8 bytes and still cheap. If you are going to pick one of the five hash nodes in this pack and stop thinking about it, pick this one. The pack's own curated compare node makes it the default for exactly that reason.

The use cases are the mundane, high-value ones. Deduping a batch you generated with a wide seed sweep. Deciding whether an upscale actually changed the image or just re-rendered the same picture with a bit more contrast. Making a stable cache key for "have I processed this input before?" Checking whether an incoming reference is a variant of one you already have. All of it becomes an integer comparison instead of an image comparison (image-io-metadata.md covers the other, structured route to the same "is this the same asset" question).

How it works

The image is reduced to a small fixed size, the low-frequency part of its DCT is extracted, and each coefficient is compared against the median to emit a bit. Comparing two hashes is a Hamming distance: how many bits differ. The author's tooltip is direct about the consequence - pHash "uses the low-frequency DCT, so it is the most robust of the family to rescaling and compression - the usual choice for near-duplicate detection. Compare two hashes with cv2.norm (NORM_HAMMING): a small distance means visually similar."

Scale of that distance, from the pack's curated compare node: around 2 bits apart for a rescaled copy, around 10 bits for "same scene, clearly different image", and over 20 for unrelated pictures. That node's default threshold of 10 is a reasonable "same picture" line.

Input and output

  • inputArr - 8-bit 1/3/4-channel image (IMAGE, MASK or NPARRAY). It is in the pack's per-frame list, so a batch is hashed frame by frame and comes back as stacked hashes - one node, one call, a hundred fingerprints. That is the shape you want for dedupe work.
  • Output nparray - the 8-byte hash. Data, not pixels.

Comparison routes, best first: the curated CV Image Hash Compare node (hashes two images, threshold, similar boolean, both raw hashes, and it normalises the one algorithm whose return direction differs in cv2); the raw cv2.norm node with NORM_HAMMING when you already hold two hashes; CV Array To Text when you want the hash out of the graph as a string to grep later. Since the hash is 8 bytes, a thousand images is 8 KB of fingerprints - you can keep them in a text file forever.

Install

Manager → search comfyui_cv (bmad4ever), or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Python ≥ 3.12, a recent ComfyUI on the V3 node API, one pinned dependency, no models. img_hash is a contrib module, so a non-contrib OpenCV build means no node at all - and because all four OpenCV distributions share one site-packages/cv2, another pack installing plain opencv-python can take these nodes away silently. The repo's tools/repair_opencv_contrib.py --check / --apply is the repair.

When it goes wrong

  • Two near-identical images come back as unrelated. pHash is not crop-invariant and not rotation-invariant. A small crop shifts the whole low-frequency signature; a 90° rotation is a different hash entirely. For rotated variants, colorMomentHash is the one in this pack that survives rotation.
  • The distance units confuse you. It is a bit count over 64 bits, not a percentage. Do not "normalise" it by dividing by 255 - the correct denominator is 64 if you want a fraction.
  • You threshold at the wrong number. The pack's node defaults to 10, and the useful band for "same picture" is single digits. If everything looks similar, your threshold is too generous; run a known pair through CV Image Hash Compare and read the actual distance before you set a policy.
  • You expected semantic similarity. This is a fingerprint, not an embedding. A cat and a different cat are far apart; two renders of the same prompt 3 steps apart are close. Embedding-space matching is a different tool.
  • Hashing a 16-bit or floating-point array. The tooltip wants 8-bit 1/3/4 channels; cast first with CV Cast Array.
Categoryimage/CV/low-level/img_hash

Inputs (1)

NameTypeDefaultDescription
inputArrNPARRAY,IMAGE,MASKinput image want to compute hash value, type should be CV_8UC4, CV_8UC3, CV_8UC1. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.

Outputs (1)

NameTypeDescription
nparrayNPARRAY—