Быстрый и простой алгоритм хэширования изображений

Мне нужен (желательно простой и быстрый) алгоритм хэширования изображений. Хэш-значение используется в таблице поиска, а не для криптографии.

Некоторые из изображений являются "компьютерной графикой", то есть сплошными цветными прямоугольниками, растрированными текстами и т.д., тогда как есть также "фотографические" изображения, содержащие богатый цветовой спектр, в основном гладкий, с разумной амплитудой шума.

Мне также хотелось бы, чтобы алгоритм хэширования мог применяться к конкретным частям изображения. Я имею в виду, что изображение можно разделить на ячейки сетки, а хэш-функция каждой ячейки должна зависеть только от содержимого этой ячейки. Чтобы можно было быстро обнаружить, если два изображения имеют общие области (в случае, если они выровнены соответственно).

Примечание. Мне нужно знать только, если два изображения (или их части) идентичны. То есть мне не нужно сопоставлять похожие изображения, нет необходимости в распознавании признаков, корреляции и других методах DSP.

Интересно, какой предпочтительный алгоритм хэширования.

Для "фотографических" изображений только XOR-ing все пиксели в ячейке сетки находятся в порядке более или менее. Вероятность того же хэш-значения для разных изображений довольно низка, особенно потому, что наличие (почти белого) шума нарушает все потенциальные симметрии. Плюс спектр такой хэш-функции выглядит хорошо (любое значение возможно с почти такой же вероятностью).

Но такой наивный алгоритм не может использоваться с "искусственной" графикой. Для таких изображений очень характерны одинаковые пиксели, повторяющиеся шаблоны, геометрическая корреляция смещения. XOR-ing всех пикселей даст 0 для любого изображения с четным количеством одинаковых пикселей.

Использование чего-то вроде CRT-32 выглядит несколько обнадеживающим, но я бы хотел найти что-то быстрее. Я думал об итеративной формуле, каждый новый пиксель мутирует текущее значение хэша, например:

hashValue = (hashValue * /*something*/ | newPixelValue) % /* huge prime */

Выполнение простого по модулю числа должно, вероятно, дать хорошую дисперсию, так что я склоняюсь к этому варианту. Но я хотел бы знать, есть ли лучшие варианы.

Спасибо заранее.

Ответ 1

Если вы хотите сделать это очень быстро, вам следует подумать о выборе случайного подмножества пикселей, чтобы избежать прочтения всего изображения. Затем вычислите хэш-функцию в последовательности значений в этих пикселях. Случайное подмножество должно выбираться детерминированным генератором псевдослучайных чисел с фиксированным семенем, чтобы идентичные изображения отображали одинаковые подмножества и, следовательно, одинаковые значения хеширования.

Это должно работать достаточно хорошо даже для искусственных изображений. Однако, если у вас есть изображения, которые отличаются друг от друга небольшим количеством пикселей, это даст хеш-коллизии. Более итерации дают лучшую надежность. Если это так, например, если ваш набор изображений, вероятно, будет иметь пары с одним другим пикселем, вы должны прочитать каждый пиксель, чтобы вычислить значение хэша. Взятие простой линейной комбинации с псевдослучайными коэффициентами было бы достаточно даже для искусственных изображений.

псевдокод простого алгоритма

Random generator = new generator(2847)  // Initialized with fixed seed
int num_iterations = 100

int hash(Image image) {
    generator.reset()   //To ensure consistency on each evaluation
    int value = 0
    for num_iteration steps {
        int nextValue = image.getPixel(generator.nextInt()%image.getSize()).getValue()
        value = value + nextValue*generator.nextInt()
    }
    return value
}