Skip to content

Digest hashes

mHash, BMH, Radial, ColorHash and ColorMoments, returned as a ph_digest_t.

Types
ph_digest_kind_t What a digest's bytes mean, and therefore how two of them may be compared.
ph_digest_t A flat structure representing a hash digest.
Constants
PH_DIGEST_MAX_BYTES
PH_VECTOR16_SCALE Units of a PH_DIGEST_KIND_VECTOR16 feature: 1/128 of a channel level.
Functions
ph_compute_bmh() Computes Block Mean Hash (BMH). Returns a block_size * block_size-bit digest (256 bits / 32 bytes at the default block_size 16; see ph_context_set_block_params()). See BMH — Block Mean Hash. On an error out_digest is left untouched.
ph_compute_color_moments_hash() Computes the color moments hash. Returns an 18-byte digest.
ph_compute_color_hash() Computes the color histogram hash. Returns a 108-byte digest.
ph_compute_mhash() Computes the Marr-Hildreth hash. Returns a 72-byte (576-bit) digest.
ph_compute_radial_hash() Computes the Radial variance hash. Returns a 40-byte digest.

Types

ph_digest_kind_t

What a digest's bytes mean, and therefore how two of them may be compared.

Not every digest is a bit vector. Comparing quantized DCT coefficients by Hamming distance, or a histogram by L2, returns a plausible number that means nothing. This tag exists so that such a call fails instead: it is never used to pick a metric for you, only to refuse the wrong one. It survives storage: ph_digest_to_hex() writes it into the text and ph_digest_from_hex() restores it, so two digests read back from a database are protected exactly as two freshly computed ones are.

PH_DIGEST_KIND_VECTOR16 is PH_DIGEST_KIND_VECTOR with two bytes per feature and a sign, which ColorMoments needs because the third moment is a direction, not a size. The two are separate tags rather than one because they are not the same bytes: comparing a 16-bit vector as if it were a byte vector reads each feature's high and low halves as two independent features, and returns a plausible number that means nothing.

PH_DIGEST_KIND_UNSPECIFIED is what a hand-filled struct contains, since it is zero. Every comparison function accepts it — an FFI binding that fills in data and size and nothing else is accepted by every comparison, and gets no protection.

Name Value Description
PH_DIGEST_KIND_UNSPECIFIED 0 Not stated. Accepted by every comparison.
PH_DIGEST_KIND_BITS 1 A bit vector. Hamming distance, similarity. BMH, mHash.
PH_DIGEST_KIND_COEFFICIENTS 2 Quantized transform coefficients. Radial.
PH_DIGEST_KIND_VECTOR 3 Real-valued features, one unsigned byte each. No algorithm here produces it: it tags a feature vector the caller builds, compared with ph_l2_distance().
PH_DIGEST_KIND_HISTOGRAM 4 Bin counts. Histogram intersection. ColorHash.
PH_DIGEST_KIND_VECTOR16 5 Real-valued features, signed 16-bit. ColorMoments.
PH_DIGEST_KIND_FORCE_INT32_ PH_ENUM_FORCE_INT32_VALUE Not a kind – see "Enum width".

ph_digest_t

A flat structure representing a hash digest.

Note

This structure is FFI-safe and can be allocated on the stack. It does not own any heap memory.

Note

The struct is 136 bytes: 128 data bytes, size, kind, and 6 bytes of padding.

Type Field Description
uint8_t data[PH_DIGEST_MAX_BYTES] The raw hash bytes.
uint8_t size Number of valid bytes in 'data'.
uint8_t kind A ph_digest_kind_t. 0 when not stated.
uint8_t reserved[6] Zero. Room for future fields; makes the size a multiple of 8. Not alignment: every field is a byte, so _Alignof(ph_digest_t) is 1.

Constants

PH_DIGEST_MAX_BYTES

#define PH_DIGEST_MAX_BYTES 128

Maximum size in bytes for any digest supported by the library (128 bytes = 1024 bits).

The widest digest the library produces is the Marr-Hildreth hash at 576 bits (72 bytes); the rest is headroom for the color histogram's bin count, reserved so that the size stays fixed for the whole 2.x series. ph_digest_t is passed and stored by value, so it costs 136 bytes wherever one lives.

PH_VECTOR16_SCALE

#define PH_VECTOR16_SCALE 128

Units of a PH_DIGEST_KIND_VECTOR16 feature: 1/128 of a channel level.

A PH_DIGEST_KIND_VECTOR16 digest holds signed 16-bit big-endian fixed-point numbers, two bytes per feature, most significant byte first. Divide by this to get the value back. A caller that only compares digests never needs it — ph_l2_distance() decodes them — but a caller that wants the moments themselves does.

Functions

ph_compute_bmh()

ph_error_t ph_compute_bmh(ph_context_t *ctx, ph_digest_t *out_digest);

nodiscard · returns ph_error_t

Computes Block Mean Hash (BMH). Returns a block_size * block_size-bit digest (256 bits / 32 bytes at the default block_size 16; see ph_context_set_block_params()). See BMH — Block Mean Hash. On an error out_digest is left untouched.

Returns PH_SUCCESS, PH_ERR_INVALID_ARGUMENT for a NULL argument, PH_ERR_EMPTY_IMAGE if no image is loaded, or PH_ERR_ALLOCATION_FAILED.

ph_compute_color_moments_hash()

ph_error_t ph_compute_color_moments_hash(ph_context_t *ctx, ph_digest_t *out_digest);

nodiscard · returns ph_error_t

Computes the color moments hash. Returns an 18-byte digest.

The mean, the standard deviation and the cube root of the third central moment of each of the R, G and B channels, after Stricker & Orengo (1995): nine signed 16-bit big-endian numbers in units of 1/PH_VECTOR16_SCALE of a channel level. Compare with ph_l2_distance(), not with the bit metrics. See ColorMoments — Color Moments.

Requires a color image: the loaded image must have at least 3 channels. On a single-channel image — one loaded while ph_context_set_load_grayscale() was enabled, or handed to ph_load_from_pixels() with channels = 1 — this returns PH_ERR_REQUIRES_COLOR and leaves out_digest untouched.

Returns PH_SUCCESS, PH_ERR_INVALID_ARGUMENT for a NULL argument, PH_ERR_EMPTY_IMAGE if no image is loaded, or PH_ERR_REQUIRES_COLOR.

ph_compute_color_hash()

ph_error_t ph_compute_color_hash(ph_context_t *ctx, ph_digest_t *out_digest);

nodiscard · returns ph_error_t

Computes the color histogram hash. Returns a 108-byte digest.

The image's pixels are counted into 108 bins of the opponent color space — red against green, blue against yellow, light against dark, at 6 x 6 x 3 — and each bin is scaled against the largest. This is a color histogram with histogram intersection, after Swain & Ballard (1991), implemented from secondary descriptions of the paper; no conformance to the paper itself is claimed. The quantization is this library's own, chosen by measurement.

Compare with ph_histogram_intersection(), not with the bit or vector metrics.

Requires a color image: the loaded image must have at least 3 channels. On a single-channel image — one loaded while ph_context_set_load_grayscale() was enabled, or handed to ph_load_from_pixels() with channels = 1 — this returns PH_ERR_REQUIRES_COLOR and leaves out_digest untouched.

Note

Like every color histogram it discards spatial layout entirely: an image and a shuffling of its pixels hash identically. Use it alongside a structural hash, not instead of one. See ColorHash — Color Histogram.

Returns PH_SUCCESS, PH_ERR_INVALID_ARGUMENT for a NULL argument, PH_ERR_EMPTY_IMAGE if no image is loaded, or PH_ERR_REQUIRES_COLOR.

ph_compute_mhash()

ph_error_t ph_compute_mhash(ph_context_t *ctx, ph_digest_t *out_digest);

nodiscard · returns ph_error_t

Computes the Marr-Hildreth hash. Returns a 72-byte (576-bit) digest.

The image is blurred at sigma 1, normalized to size x size (512 by default) and histogram-equalized over 256 levels; the Laplacian-of-Gaussian operator of Marr and Hildreth (alpha = 2, level = 1 by default; see ph_context_set_mhash_params()) is correlated with it; the response is summed into a 31x31 grid of blocks (16x16 pixels at the default size); and nine bits are emitted per 3x3 window of that grid at stride 4, each thresholded against its window's mean. That construction is pHash's ph_mh_imagehash(), which is its primary source — there is no paper for it. Values are close in spirit but not identical to pHash's; see Algorithm provenance, section 5.

The digest is a bit vector: compare it with ph_hamming_distance_digest() or ph_similarity_digest(). See mHash — Marr–Hildreth Hash.

On an error out_digest is left untouched.

Returns PH_SUCCESS, PH_ERR_INVALID_ARGUMENT for a NULL argument, PH_ERR_EMPTY_IMAGE if no image is loaded, or PH_ERR_ALLOCATION_FAILED.

ph_compute_radial_hash()

ph_error_t ph_compute_radial_hash(ph_context_t *ctx, ph_digest_t *out_digest);

nodiscard · returns ph_error_t

Computes the Radial variance hash. Returns a 40-byte digest.

The variance of the pixels along each of radial_projections lines through the image center (default 180, one per degree), reduced by a 1-D DCT to its first 40 coefficients, which are quantized into the digest. Uses the context gamma, and nothing else does.

An image with no angular structure – blank, or with nearly the same variance along every line, as a radially symmetric one has – gets an all-zero digest and PH_SUCCESS; ph_radial_similarity() answers any comparison with it by PH_ERR_NO_STRUCTURE.

Warning

Compare these digests with ph_radial_similarity(), not with ph_hamming_distance_digest(), ph_l2_distance() or ph_similarity_digest(): the bytes are quantized coefficients, not bits or a point in space. The digest tolerates a rotation of a few degrees and an exact half turn, not an arbitrary one. See Radial — Radial Variance Hash.

On an error out_digest is left untouched.

Returns PH_SUCCESS (the all-zero digest included), PH_ERR_INVALID_ARGUMENT for a NULL argument, PH_ERR_EMPTY_IMAGE if no image is loaded, or PH_ERR_ALLOCATION_FAILED.