aHash, dHash and pHash compared
aHash, dHash and pHash begin with the same gesture — shrinking the image to a grid of greys — and end with sixty-four bits. What changes is the question asked of each cell. Three different questions produce three fingerprints with three different blind spots, and that is precisely why Probatio computes all of them.
The common trunk
All three algorithms start by resizing the image to a tiny grid and converting it to greyscale. At that point each cell holds a single number: its luminance. The difference lies in the comparison that follows.
At the classic depth of 64 bits the grid side is 8 — the square root of the requested bits. At 144 bits it becomes 12; at 256 bits, 16.
aHash — compared with the mean
The simplest one. Resize to 8×8, compute the mean of the 64 luminances, then write one bit per cell: 1 if the cell is greater than or equal to the mean, 0 otherwise.
It is very fast and rests on a sound intuition: the map of «where the image is brighter than its own average grey» is a signature of the composition. But the mean is a moving reference. If someone raises the brightness of the whole image, every cell rises and the mean rises with them: nearly all bits stay put. If instead the adjustment is non-linear — a gamma curve, an app filter — cells that sat just above the mean drop just below, and the bits flip.
Blind spot: aHash is the most sensitive to brightness and contrast manipulation. When aHash diverges far more than the other two, you have a clue: someone touched the tone curves.
dHash — compared with the neighbour
A change of perspective. dHash resizes to a grid one pixel wider — (8+1)×8 — and then, for every row, compares each pixel with the one immediately to its right: the bit is 1 if the pixel is darker than its successor.
What it encodes is not the level of light but its slope: where the image brightens from left to right and where it darkens. It is the horizontal gradient, reduced to its sign.
And the sign of a slope is remarkably stable. Raise the brightness of the whole image, and every pixel rises together with its neighbour: the slope is unchanged. Increase the contrast, and slopes steepen but do not invert. That is why dHash holds where aHash gives way.
Blind spot: it looks only horizontally, and only between immediate neighbours. A local retouch that disturbs micro-gradients — sharpening, noise reduction, an eraser over one detail — moves many bits, even though the photograph is, overall, the same.
pHash — compared with the median of the coefficients
The most refined, and the only one that does not work on pixels. pHash resizes to a grid four times wider — 32×32 when the fingerprint is 64 bits — and applies a two-dimensional DCT-II to it, the same transform that lives inside JPEG.
The DCT rewrites the image as a sum of waves of increasing frequency. The top-left of the resulting matrix holds the low frequencies: slow variations, that is, structure, compositional layout, the large volumes of light. The bottom-right holds high frequencies: fine detail, sensor noise, compression artefacts.
pHash keeps only the top-left 8×8 block and discards everything else. It then computes the median of those coefficients — excluding the constant term at position (0,0), the so-called DC, which represents average brightness and would drag the median without saying anything about structure — and writes one bit per coefficient: 1 if it is above the median.
The result is a fingerprint of the low-frequency structure. Re-compressing a JPEG damages high frequencies and leaves the low ones intact: pHash does not notice. Resizing does the same. Hence its robustness.
Blind spot: it is the costliest — a 2D transform over 1024 pixels rather than a mean over 64 — and it is sensitive to anything that shifts the composition: a crop, an added border, a rotation.
When they disagree: reading the divergence
If the three algorithms always agreed, computing three would be waste. They do not agree, and their disagreement is information.
The reasoning is always the same: given that this algorithm is blind to X and sees Y, its distance tells me something about Y. If pHash is low and dHash is high, global structure matches but local detail does not — and the cheapest explanation is a local edit, not a different subject. If pHash is high and dHash is low, the detail lines up but the composition changed: a crop explains everything.
These are working hypotheses. They say where to look, not what to conclude. The conclusion arrives when you open the two images and see the crop.
Summary table
| Algorithm | Grid | Bit rule | Robust to | Fragile to |
|---|---|---|---|---|
| aHash | 8×8 | pixel ≥ mean | mild re-compression, resizing | brightness, gamma, contrast |
| dHash | 9×8 | pixel < pixel to its right | brightness, contrast | local retouching, sharpening, noise |
| pHash | 32×32 → 8×8 block | DCT coefficient > median | compression, scale, noise | crop, rotation, borders |
Two things you will not find
wHash, the wavelet-transform variant, is often cited alongside the other three. Probatio does not implement it: if you read a comparison that mentions it, it did not come from here.
ssdeep, which Probatio computes during file analysis, is not a perceptual hash: it works on bytes, not on perception, and in Probatio it is displayed but not compared. Different tools for different problems.
Using them in practice
- Always compute all three. The cost is negligible and the disagreement is the most interesting datum.
- Never trust a single algorithm to assert that two images are the same. Require agreement.
- If aHash alone is high, suspect a tonal adjustment, not a manipulation of content.
- Always state the bit depth in the report: «distance 4» means nothing until you know whether the denominator is 64 or 256.
- Report the distance, not just the percentage. «6 bits out of 64» is a fact; «90.6%» is its translation, and it hides the denominator.