Skip to content

Quick start

From nothing to the hash of your first image, and then to comparing two images.

Install

The shortest route for each kind of build; Installing has every route, the difference between the Full and Minimal builds, and how to check a download.

Each release publishes static and shared archives for linux-x86_64, linux-arm64, macos-arm64 and windows-x86_64 on the releases page. Unpack one and point pkg-config at it:

tar -xzf libphash-2.0.2-linux-x86_64.tar.gz
export PKG_CONFIG_PATH="$PWD/libphash-2.0.2-linux-x86_64/lib/pkgconfig"

The recommended build: the bundled libjpeg-turbo, libpng, libwebp and zlib-ng decoders, fetched as git submodules.

git clone --recursive https://github.com/gudoshnikovn/libphash.git
cd libphash
cmake --preset release
cmake --build --preset release
cmake --install build/release --prefix "$HOME/.local"

The portable build: no dependencies, every format decoded by the bundled stb_image.

git clone https://github.com/gudoshnikovn/libphash.git
cd libphash
make -j8
make install PREFIX="$HOME/.local"

Hash one image

examples/basic_hash.c
/* Loads an image and prints its pHash. Build/run:
 *   cc basic_hash.c -o basic_hash $(pkg-config --cflags --libs libphash)
 *   ./basic_hash path/to/image.jpg
 */
#include <libphash.h>
#include <stdio.h>

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s <image>\n", argv[0]);
        return 1;
    }

    ph_context_t *ctx = NULL;
    if (ph_create(&ctx) != PH_SUCCESS) {
        fprintf(stderr, "ph_create failed\n");
        return 1;
    }

    /* Fast grayscale loading: skips the RGB-to-gray conversion pass. Fine here since
     * pHash only ever operates on grayscale; skip this call to load in color if you
     * also want a color hash (ph_compute_color_hash(), ph_compute_color_moments_hash())
     * from the same load. */
    ph_context_set_load_grayscale(ctx, 1);

    ph_error_t err = ph_load_from_file(ctx, argv[1]);
    if (err != PH_SUCCESS) {
        fprintf(stderr, "failed to load %s: %s (%s)\n", argv[1], ph_get_error_string(err),
                ph_get_last_error_message(ctx));
        ph_free(ctx);
        return 1;
    }

    uint64_t hash = 0;
    err = ph_compute_phash(ctx, &hash);
    if (err != PH_SUCCESS) {
        fprintf(stderr, "ph_compute_phash failed: %s\n", ph_get_error_string(err));
        ph_free(ctx);
        return 1;
    }

    char hex[17];
    if (ph_hash_to_hex(hash, hex, sizeof(hex)) != PH_SUCCESS) {
        fprintf(stderr, "ph_hash_to_hex failed\n");
        ph_free(ctx);
        return 1;
    }
    printf("pHash: %s\n", hex);

    ph_free(ctx);
    return 0;
}
cc basic_hash.c -o basic_hash $(pkg-config --cflags --libs libphash)
./basic_hash photo.jpg

Compare two images

examples/compare_two_images.c
/* Compares two images with pHash and prints a normalized similarity score. Build/run:
 *   cc compare_two_images.c -o compare_two_images $(pkg-config --cflags --libs libphash)
 *   ./compare_two_images a.jpg b.jpg
 */
#include <libphash.h>
#include <stdio.h>

static int hash_file(const char *path, uint64_t *out_hash) {
    ph_context_t *ctx = NULL;
    if (ph_create(&ctx) != PH_SUCCESS) {
        fprintf(stderr, "ph_create failed\n");
        return 1;
    }
    ph_context_set_load_grayscale(ctx, 1);

    ph_error_t err = ph_load_from_file(ctx, path);
    if (err != PH_SUCCESS) {
        fprintf(stderr, "failed to load %s: %s (%s)\n", path, ph_get_error_string(err),
                ph_get_last_error_message(ctx));
        ph_free(ctx);
        return 1;
    }

    err = ph_compute_phash(ctx, out_hash);
    ph_free(ctx);
    if (err != PH_SUCCESS) {
        fprintf(stderr, "ph_compute_phash failed for %s: %s\n", path, ph_get_error_string(err));
        return 1;
    }
    return 0;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: %s <image-a> <image-b>\n", argv[0]);
        return 1;
    }

    uint64_t hash_a, hash_b;
    if (hash_file(argv[1], &hash_a) != 0 || hash_file(argv[2], &hash_b) != 0) {
        return 1;
    }

    int distance = ph_hamming_distance(hash_a, hash_b);
    double similarity = ph_similarity(hash_a, hash_b);

    printf("Hamming distance: %d/64 bits\n", distance);
    printf("Similarity:       %.4f (1.0 = identical, 0.0 = every bit differs)\n", similarity);

    return 0;
}

Two copies of one picture land a few bits apart, and two unrelated pictures about 32 of the 64. Where to draw the line between the two depends on the algorithm and on the collection; Comparing hashes measures it.

Next