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 8×8 grid (4×4 here) ● = pixel ≥ mean → bit 1 bit = pixel ≥ grid mean One pass: resize, compute the mean, threshold. The fastest of the three. Fragile: raise the brightness of the whole image and the bits stay… mostly the same. dHash (8+1)×8 grid (5×4 here) → = compared with the pixel to its right bit = pixel < next pixel It encodes the gradient — the direction of the change — not the absolute level. Robust to brightness and contrast: the sign of the slope does not move. pHash DCT over 32×32, 8×8 block (8×8 → 4×4 here) low-frequency block DC term: excluded from the median bit = coefficient > median Low frequencies describe structure; high ones carry noise and fine detail. The most robust to compression and scale. Also the costliest: it needs a 2D DCT. The real grid is 8×8 (64 bits); shrunk here for legibility. Luminance values are illustrative.
Three ways of turning a grid of greys into bits. What changes is the question asked of each cell — and with it, what the fingerprint is blind to.

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.

dHash distance LOW HIGH pHash distance LOW pHash distance HIGH Same image Re-compression, resizing, format change. No surprises here. Micro-structure altered Global structure intact, local gradients not: local retouching, sharpening, denoise, noise. Global structure altered Local detail consistent, layout not: crop, very different scale, added borders. Different images Both fingerprints uncorrelated. No visual kinship. These are working hypotheses, not verdicts: they say where to look, and must be confirmed by inspecting the images.
The value of computing all three algorithms is not redundancy but disagreement: that is where the information hides.

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

AlgorithmGridBit ruleRobust toFragile to
aHash8×8pixel ≥ meanmild re-compression, resizingbrightness, gamma, contrast
dHash9×8pixel < pixel to its rightbrightness, contrastlocal retouching, sharpening, noise
pHash32×32 → 8×8 blockDCT coefficient > mediancompression, scale, noisecrop, 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

  1. Always compute all three. The cost is negligible and the disagreement is the most interesting datum.
  2. Never trust a single algorithm to assert that two images are the same. Require agreement.
  3. If aHash alone is high, suspect a tonal adjustment, not a manipulation of content.
  4. Always state the bit depth in the report: «distance 4» means nothing until you know whether the denominator is 64 or 256.
  5. 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.