Skip to content

Кодирование изображений ​

Изображение превращается в хеш тремя функциями из hazehash/encode. Они принимают RGBA-пиксели sRGB с прямым (не premultiplied) альфа-каналом, поэтому ImageData из canvas подходит как есть. Кодируйте на сервере или при сборке и отправляйте клиенту только строку: энкодер это большая половина библиотеки (около 6,3 КБ gzip), и браузеру, который только показывает плейсхолдеры, он не нужен.

ts
import { encode, encodeToString, toBase64Url } from 'hazehash/encode'

Вход это обычный объект, одной формы во всех функциях:

ts
interface RgbaImage {
  data: Uint8Array | Uint8ClampedArray // RGBA, длина 4 · width · height
  width: number
  height: number
}

Кодирование в строку ​

encodeToString(image, options?)

string

Кодирует изображение и возвращает хеш в base64url без выравнивания, готовый для текстовой колонки или JSON. Это функция, которая нужна в большинстве случаев.

ts
import { encodeToString } from 'hazehash/encode'

const hash = encodeToString({ data: rgba, width: 1280, height: 959 })
// "Ed7UwRWKKv5znm6a7sC1tziHDNpMuCikxrYpIg"

image ​

RgbaImage

Пиксели для кодирования. В data должно быть ровно 4 · width · height байт, а width и height должны быть целыми числами не меньше 1; иначе вызов бросает PlaceholderError с кодом InvalidInput. Функция описывает одну картинку, поэтому для анимированного изображения передайте один кадр.

options ​

EncodeOptions · необязательно

См. Опции энкодера ниже.

Кодирование в байты ​

encode(image, options?)

Uint8Array

То же, что encodeToString(), но возвращает сырые байты. Используйте для бинарной колонки (BYTEA, BLOB), которая примерно на 25% меньше текстовой формы; см. Хранение хешей.

ts
import { encode } from 'hazehash/encode'

const bytes = encode({ data: rgba, width: 1280, height: 959 })
bytes.length // 28

Преобразование между двумя формами ​

toBase64Url(bytes)

string

Превращает байты хеша в строку base64url. Обратная функция, toBytes(), экспортируется декодером; см. Декодирование хешей.

ts
import { encode, toBase64Url } from 'hazehash/encode'

toBase64Url(encode({ data: rgba, width, height })) // та же строка, что вернёт encodeToString()

Опции энкодера ​

budget ​

number · по умолчанию: 28

Максимальный размер результата в байтах, вместе с заголовком. Это потолок, а не фиксированный размер: простое изображение, например однотонное или с плавным градиентом, занимает меньше байт, а хеш, который уже помещается, никогда не дополняется. Наименьшее рабочее значение 7 байт, или 9, если в хеше есть альфа; всё, что меньше, бросает BudgetTooSmall.

Бенчмарк охватывает бюджеты от 16 до 48 байт. См. Выбор бюджета.

profile ​

'fast' | 'default' | 'high' · по умолчанию: 'default'

Насколько усердно энкодер ищет решение. 'fast' перебирает меньше сеток и занимает около 1 мс для входа 64×48, 'default' перебирает все и занимает около 5 мс (изображения с альфа-каналом медленнее, около 100 мс), а 'high' дополнительно подгоняет коэффициенты под изображение для анализа, давая примерно на 1% меньшую ошибку, и занимает около 14 мс. Время зависит от размера входа только через уменьшение. Любое другое значение бросает InvalidInput.

alpha ​

'auto' | boolean · по умолчанию: 'auto'

Хранит ли хеш прозрачность. 'auto' добавляет блок альфа-канала, только если есть пиксель с альфой ниже 254/255, true добавляет его всегда, а false никогда, и тогда прозрачное изображение кодируется так, как будто оно непрозрачное.

analysisSize ​

number · по умолчанию: 64

Длинная сторона в пикселях сетки, до которой изображение уменьшается перед анализом. Значения округляются и ограничиваются диапазоном 32–128. Более крупная сетка редко помогает, ведь хеш в любом случае очень грубое описание.

weights ​

{ L?: number; C?: number; A?: number } · по умолчанию: { L: 1, C: 1, A: 1 }

Насколько каждый канал учитывается в ошибке, которую минимизирует энкодер: L для яркости, C для двух цветовых каналов и A для альфы. Больший вес тратит на этот канал больше бюджета. Вес должен быть конечным числом не меньше 0, иначе вызов бросает InvalidInput.

Пример — все опции сразу:

ts
const hash = encodeToString(
  { data: rgba, width: 1280, height: 959 },
  {
    budget: 24,
    profile: 'high',
    alpha: false,
    analysisSize: 96,
    weights: { L: 1, C: 1.5, A: 1 },
  },
)

Выбор бюджета ​

Хеш из n байт занимает ceil(4n / 3) символов base64url. Средняя ΔE это перцептивная ошибка в OKLab (умноженная на 100), усреднённая по 513 изображениям; чем меньше, тем лучше.

БюджетСимволовСредняя ΔEКомментарий
16228.41самый компактный практичный хеш
20277.89всё ещё на 14% лучше ThumbHash
24327.54хороший компромисс размера и качества
28387.29по умолчанию
36487.00больше деталей, когда размер не важен

Переход с 28 на 24 байта стоит около 3,4% ошибки за 14% экономии данных, а с 28 на 36 байт даёт 4,0% выигрыша за 29% дополнительных данных. Берите 24 байта, когда важнее хранилище, и 28 в остальных случаях. Методика описана в разделе Бенчмарк.

Заметки ​

  • Ядро считает входные данные sRGB и игнорирует ICC-профили и EXIF-ориентацию. Хелперы для Node.js и командная строка переводят в sRGB и применяют ориентацию через sharp.
  • Результат детерминирован: одни и те же пиксели и опции всегда дают одни и те же байты.
  • Кодирование не предназначено для главного потока браузера при каждом рендере. Посчитайте хеш один раз заранее и сохраните его.