cv2.img_hash.blockMeanHash
Two hash lengths, one dropdown that changes everything
- inputArr
- nparray
A block-mean perceptual hash with a mode switch, and that switch is the whole reason to prefer it over averageHash. In mode 0 the image is split into non-overlapping blocks and each block's mean contributes bits - 32 bytes of hash. In mode 1 the blocks overlap, which produces a 121-byte hash: longer, slower to compare, and noticeably more discriminating. The author's tooltip spells out the sizes and the trade.
So the choice is: averageHash when you want the single cheapest fingerprint, pHash when you want the standard robust one, and blockMeanHash in mode 1 when near-duplicates are too near for the others to separate. Image sets from the same prompt with tiny seed variation, upscales that differ in one small region, a batch where everything is 98% the same content - that is where a longer spatial fingerprint earns its keep.
How it works
Per-block mean comparison, essentially a spatial layout of average hashes, with the mode deciding whether the blocks tile the image or slide across it. The bits come from how each block's mean relates to the overall statistics of the image, which is why it stays robust to global rescale and compression in the way the average hash does, while keeping more spatial structure than the 8-byte hashes.
Comparing two hashes is a Hamming bit distance, exactly like pHash. The pack's curated CV Image Hash Compare node exposes "blockMean" as one of its algorithms and reports the distance in bits with the same 0-means-identical convention for every algorithm it supports.
The detail that bites people
You must not compare hashes computed with different modes. Mode 0 and mode 1 produce different lengths (32 versus 121 bytes), so a comparison between them is at best a vector-length error and at worst a meaningless number if something pads or truncates silently. If you are storing hashes - for a dedupe catalogue, say - store the mode alongside them, or standardise on one mode per project. This is the kind of thing that is obvious when you read it and invisible when you inherit a workflow from someone else.
Inputs and output
inputArr- 8-bit, 1/3/4 channels; IMAGE, MASK or NPARRAY socket. In the pack's per-frame list, so a whole batch hashes frame by frame and comes back stacked - one call for a folder's worth of images.mode- the dropdown:BLOCK_MEAN_HASH_MODE_0(non-overlapping, 32 bytes, default) orBLOCK_MEAN_HASH_MODE_1(overlapping, 121 bytes).- Output
nparray- the hash bytes. This is data;Preview CV Arraywill draw you a meaningless little strip. UseCV Array To Textif you want to read or store it.
Comparison: cv2.norm with NORM_HAMMING for the raw arithmetic, or the curated CV Image Hash Compare node if you want a threshold and a boolean without hand-rolling the comparison. Because the longer mode reports distances over more bits, the "same picture" threshold is larger than the pHash-scale numbers - the pack's compare node defaults to 10 bits, which is calibrated for the 64-bit hashes; a 968-bit hash needs a bigger number before it means the same thing. Read actual distances from a known pair rather than guessing.
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 and a recent ComfyUI on the V3 node API. No models; the pinned contrib OpenCV wheel is the only dependency. Without a contrib build there is no img_hash and therefore no node - same story as the rest of this family.
When it goes wrong
- Distance numbers that look absurd. Almost always a mode mismatch between the two hashes you are comparing, or a threshold carried over from pHash. Pin the mode, and re-derive the threshold from a pair you know.
- The distinguishability did not improve. Mode 1 helps with spatially localised differences. A global brightness shift still moves it (the bits are still means), so if that is your problem, you want a different tool.
- The hash array does not connect downstream. It is a byte array with no image semantics; convert only if you actually mean to view bytes.
- Rotation and cropping still break it. Only
colorMomentHashin this pack is rotation-robust; none of the block-based hashes are crop-invariant. - Contrib wheel drift.
img_hashlives in a contrib module, and a non-contrib OpenCV install anywhere in your environment silently strips it.tools/repair_opencv_contrib.py --checkin the pack repo tells you,--applyfixes it.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| inputArr | NPARRAY,IMAGE,MASK | input image want to compute hash value, type should be CV_8UC4, CV_8UC3 or 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. | |
| modeopt | COMBO | BLOCK_MEAN_HASH_MODE_0 | the mode |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |