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:
The recommended build: the bundled libjpeg-turbo, libpng, libwebp and zlib-ng decoders, fetched as git submodules.
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;
}
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¶
- Which algorithm to use, the nine measured side by side.
- How far apart two copies may be, and which function compares which hash.
- Hashing many files at once, across threads.
- The functions these programs call, in the API reference:
ph_create(),ph_load_from_file(),ph_compute_phash(),ph_hamming_distance(),ph_get_error_string()andph_free(). - More programs — digests and metrics, error handling, batches — are in
examples/; CI builds and runs every one of them.