Average Hashを使って画像の類似度を計算する方法。

Average Hashとは

  • 簡易化された画像の類似度検出アルゴリズム。
  • それなりの精度で類似した画像を探すことができる。

アルゴリズム

  1. グレースケール化。
  2. 画像を8×8に縮小。(16×16などでも良い。)
  3. 画素の平均値を調べる。
  4. 各画素を走査して平均値より上なら1、下なら0という風にハッシュ化していく。

こーど

// Average Hash
// clang -Os -o ahash ahash.c -lm

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"

#define AHASH_SIZE	16

void imgp_gray(uint8_t *s, int sx, int sy, int stride, uint8_t *p, int gstride)
{
	for (int y=0; y<sy; y++) {
		for (int x=0; x<sx; x++) {
			p[x] = 0.3 * s[x*3+0] + 0.59 * s[x*3+1] + 0.11*s[x*3+2];
		}

		s += stride*3;
		p += gstride;
	}
}

int main(int argc, char* argv[])
{
	char *name = argv[1];

	uint8_t *pixels;
	int w, h, bpp;
	pixels = stbi_load(name, &w, &h, &bpp, 3);
	assert(pixels);

	uint8_t *gray = malloc(w*h+AHASH_SIZE*AHASH_SIZE);
	uint8_t *resize = gray+w*h;
	imgp_gray(pixels, w, h, w, gray, w);
	//stbi_write_jpg("gray.jpg", w, h, 1, gray, 0);
	stbir_resize_uint8(gray, w, h, 0, resize, AHASH_SIZE, AHASH_SIZE, 0, 1);
	//stbi_write_jpg("resize.jpg", AHASH_SIZE, AHASH_SIZE, 1, resize, 0);

	double avg = 0;
	for (int i=0; i<AHASH_SIZE*AHASH_SIZE; i++) {
		avg += resize[i];
	}
	avg /= AHASH_SIZE*AHASH_SIZE;
	printf("avg: %f\n", avg);

	uint8_t ahash[AHASH_SIZE*AHASH_SIZE/8];
	for (int i=0; i<AHASH_SIZE*AHASH_SIZE; i++) {
		ahash[i/8] <<= 1;
		if (avg <= (double)resize[i]) ahash[i/8] |= 1;
		else ahash[i/8] &= ~1;
	}

	for (int i=0; i<AHASH_SIZE*AHASH_SIZE/8; i++) {
		printf("%02x", ahash[i]);
	}
	printf("\n");

	free(gray);
	stbi_image_free(pixels);
}

参考

  • https://tech.unifa-e.com/entry/2017/11/27/111546
  • http://hideack.hatenablog.com/entry/2015/03/16/194336