---
title: "HDR-фотографии: что находится внутри JPEG с gain map"
date: 2026-08-18T10:01:00
tags: [фото, hdr, изображения, xiaomi, tech]
description: "Как JPEG хранит пиксели, EXIF, XMP, MPF и HDR gain map, и почему снимок с Xiaomi 17 может светиться ярче белого интерфейса."
---

На HDR-дисплее солнце или блик на фотографии иногда выглядит ярче белого фона страницы. Разберём без магии, откуда экран получает команду сделать отдельные пиксели ярче. Пример - файл `IMG_20260816_183557.jpg` с Xiaomi 17.

![Фотография, снятая на Xiaomi 17 и сохранённая как HDR JPEG с gain map](/assets/files/IMG_20260816_183557.jpg)

<!-- truncate -->

## Пиксель, канал и биты

Экран составляет цвет пикселя из трёх **каналов**: красного R, зелёного G и синего B. Канал - одно число, которое задаёт вклад соответствующего цвета.

У 8-битного канала есть `2⁸ = 256` возможных кодов, от 0 до 255:

```text
один декодированный RGB-пиксель

R: 11010110  = 214
G: 10011100  = 156       всего 8 + 8 + 8 = 24 бита
B: 00110101  =  53       без учёта прозрачности
```

Чёрный кодируется как `(0, 0, 0)`, белый как `(255, 255, 255)`. Это код цвета, а не значение яркости в нитах. Какой свет физически выдаст экран, определяют цветовой профиль, передаточная функция, программа, настройки и сам дисплей.

Больше битов дают больше промежуточных кодов на канал:

| Разрядность | Значений канала | Вариантов RGB-пикселя |
|---|---:|---:|
| 8 бит | 256 | 16,7 млн |
| 10 бит | 1024 | 1,07 млрд |
| 12 бит | 4096 | 68,7 млрд |

Это уменьшает ступеньки на плавных градиентах и оставляет больше запаса для обработки. Но фраза «8 бит на канал» не означает, что JPEG буквально хранит подряд по три байта на каждый пиксель.

## Как JPEG лежит в файле и в памяти

После декодирования программа действительно может держать изображение как массив RGB-пикселей. Для кадра 4096 × 3072 при 8 битах на три канала такой несжатый массив занимал бы:

```text
4096 × 3072 × 3 байта = 37 748 736 байт ≈ 36 МиБ
```

JPEG на диске устроен иначе. При типичном кодировании RGB сначала преобразуется в яркостную составляющую Y и две цветоразностные Cb/Cr. Цветовые каналы часто уменьшаются в разрешении, изображение делится на блоки, значения переводятся в частотные коэффициенты дискретным косинусным преобразованием, округляются и кодируются без лишних повторов.

```mermaid
flowchart TB
    subgraph memory[В памяти редактора: около 36 МиБ]
        RGB[Пиксели RGB<br/>4096 × 3072 × 3 байта]
    end

    subgraph encoder[Сохранение JPEG]
        direction LR
        YCC[Преобразование<br/>RGB → YCbCr]
        SUB[Уменьшение<br/>цветовых каналов]
        DCT[Блоки и DCT]
        Q[Округление<br/>коэффициентов]
    end

    FILE[(Сжатый JPEG<br/>около 4,77 МБ)]
    DEC[Декодирование]
    SCREEN[Пиксели<br/>на экране]

    RGB --> YCC --> SUB --> DCT --> Q --> FILE
    FILE --> DEC --> SCREEN
```

Главная потеря качества происходит при округлении коэффициентов. Поэтому JPEG компактный, но повторное пересохранение может добавить артефакты. В исследуемом файле основной кадр занимает около 4,77 МБ вместо примерно 36 МиБ декодированного RGB-массива.

Число 8 в заголовке JPEG означает точность отсчётов базового изображения. Оно не запрещает приложить к файлу второе изображение и метаданные, которые вместе описывают HDR.

## Из каких частей состоит этот файл

JPEG - последовательность секций, или **сегментов**. Диапазон маркеров `APP0`...`APP15` задан в [приложении B рекомендации ITU-T T.81](https://www.itu.int/rec/T-REC-T.81/en), совместном тексте с ISO/IEC 10918-1, а правила применения APPn описаны в действующей [рекомендации ITU-T T.86](https://www.itu.int/rec/T-REC-T.86/en). Маркеры `APP1`, `APP2` и другие позволяют помещать рядом со сжатыми пикселями служебные данные. Разбор файла Xiaomi дал такую упрощённую карту:

```mermaid
flowchart LR
    FILE["IMG_20260816_183557.jpg<br/>5 704 671 байт"]
    META["Служебные сегменты<br/>XMP · цветовой профиль · данные камеры<br/>MPF · ISO 21496-1"]
    BASE["Основной JPEG<br/>SDR · 4096 × 3072 · 8 бит · цвет<br/>около 4,77 МБ"]
    GAIN["Второй JPEG · gain map<br/>2048 × 1536 · 8 бит · 1 канал<br/>около 0,93 МБ"]

    FILE --> META --> BASE --> GAIN
```

Старый декодер читает первый JPEG до маркера конца изображения и показывает нормальную SDR-фотографию. Совместимый декодер находит второе изображение, читает параметры gain map и строит HDR-представление. Поэтому расширение `.jpg` не противоречит HDR.

### EXIF: чем и как снято

**EXIF** описан в официальной спецификации [CIPA DC-008-Translation-2026, Exif 3.1](https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-008-2026-E). Это стандартная таблица фотографических метаданных: модель камеры, выдержка, диафрагма, ISO-чувствительность, ориентация, время и иногда GPS. Эти поля описывают происхождение снимка, но не содержат сами видимые пиксели.

У Xiaomi часть сведений находится в фирменном блоке камеры: модель `Xiaomi 17` и `Hdr: auto`. Здесь `Hdr: auto` говорит, что HDR-режим камеры был автоматическим. Этого недостаточно, чтобы доказать наличие воспроизводимого HDR: производитель мог свести несколько экспозиций в обычный SDR JPEG. Доказательство находится в gain map и её метаданных.

**ISO в EXIF** и **ISO 21496-1** - разные значения слова ISO. В EXIF ISO 100, 400 или 1600 означает настройку светочувствительности при съёмке. ISO 21496-1 - номер международного технического стандарта хранения gain map.

### XMP: расширяемые инструкции

**XMP** - текстовые метаданные в формате, похожем на XML. EXIF имеет заранее определённый набор фотографических полей, а XMP допускает пространства имён производителей и программ. Поэтому Adobe может добавить поля `hdrgm:*`, не меняя сам JPEG-декодер.

Во втором изображении этого файла записано:

```xml
hdrgm:Version="1.0"
hdrgm:GainMapMin="0"
hdrgm:GainMapMax="2.32193"
hdrgm:Gamma="1"
hdrgm:OffsetSDR="0"
hdrgm:OffsetHDR="0"
hdrgm:HDRCapacityMin="0"
hdrgm:HDRCapacityMax="2.32193"
hdrgm:BaseRenditionIsHDR="False"
```

Метаданные сами по себе почти не занимают места. Основной дополнительный объём создаёт JPEG с картой усиления.

### MPF: где искать вторую картинку

**MPF**, Multi-Picture Format, хранит в сегменте `APP2` индекс нескольких JPEG-изображений: их размеры, смещения и типы. Это похоже на оглавление архива. MPF не делает изображение HDR и не описывает математику яркости. Он помогает декодеру найти дополнительный JPEG внутри того же файла.

Рядом присутствует метка `urn:iso:std:iso:ts:21496:-1` и бинарные данные ISO 21496-1. Стандарт определяет gain map как способ преобразования между двумя представлениями динамического диапазона. В этом файле есть и Adobe XMP-поля, и ISO-вариант метаданных, а MPF связывает основной кадр с дополнительным.

### Rendition: представление одной фотографии

**Rendition** здесь означает не новый сюжет и не копию другого размера, а одно визуальное представление той же сцены. Например:

- SDR rendition рассчитано на обычный диапазон дисплея;
- HDR rendition сохраняет более яркие света;
- gain map содержит разницу, необходимую для перехода между ними.

`BaseRenditionIsHDR="False"` означает, что основной JPEG является SDR-представлением. Если программа игнорирует всё после него, фотография всё равно выглядит корректно.

## Что кодирует gain map

Gain map, или карта усиления, похожа на чёрно-белую маску. Каждая её точка сообщает не готовую яркость, а множитель для соответствующего участка базового изображения:

```mermaid
flowchart LR
    SDR[SDR-база<br/>цвет и мелкие детали]
    MAP[Gain map<br/>тёмное: около ×1<br/>светлое: до ×5]
    META[Метаданные<br/>min · max · gamma<br/>capacity · offsets]
    HEAD[Запас дисплея<br/>HDR white / SDR white]
    DEC[HDR-декодер<br/>растянуть карту<br/>вычислить weight<br/>усилить линейный RGB]
    OUT[Представление для экрана<br/>SDR · промежуточное · полное HDR]

    SDR --> DEC
    MAP --> DEC
    META --> DEC
    HEAD --> DEC
    DEC --> OUT
```

Карта может быть меньше основного кадра, потому что усиление меняется плавнее, чем мелкие детали фотографии. В нашем файле каждая сторона карты ровно вдвое меньше стороны базы. При показе декодер растягивает и плавно интерполирует её. Одного серого канала достаточно, когда R, G и B усиливаются одинаково. Формат также допускает отдельные значения для цветовых каналов.

Упрощённая математика для SDR-базы выглядит так:

```text
map       = значение пикселя карты от 0 до 1
logGain   = lerp(GainMapMin, GainMapMax, map ^ (1 / Gamma))
boost     = log2(HDR-белый дисплея / SDR-белый дисплея)
weight    = clamp((boost - HDRCapacityMin) /
                  (HDRCapacityMax - HDRCapacityMin), 0, 1)
HDR       = (SDR + OffsetSDR) × 2 ^ (logGain × weight) - OffsetHDR
```

`lerp(a, b, t)` выбирает положение между минимумом и максимумом, а `clamp` ограничивает результат диапазоном 0...1. Если `Gamma` равна 1, как в этом файле, степень не меняет карту. Реальная операция выполняется над линейными цветовыми значениями, а не прямо над числами 0...255 из JPEG.

Поля означают следующее:

| Поле | Смысл в декодировании | Значение в файле |
|---|---|---:|
| `GainMapMin` | минимальный логарифм усиления | 0, то есть `2⁰ = ×1` |
| `GainMapMax` | максимальный логарифм усиления | 2,32193, то есть `2²·³²¹⁹³ ≈ ×5` |
| `Gamma` | показатель кодирования карты, который декодер обращает степенью `1 / Gamma` | 1, без дополнительного изгиба |
| `OffsetSDR/HDR` | малые смещения до и после умножения, полезные около чёрного | 0 и 0 |
| `HDRCapacityMin/Max` | при каком запасе дисплея начинать и заканчивать применение карты | от 0 до 2,32193 ступени |
| `BaseRenditionIsHDR` | является ли базовый JPEG HDR-стороной преобразования | `False`, база SDR |

Числа заданы в **ступенях экспозиции**, то есть как логарифмы по основанию 2. Поэтому `GainMapMax="2.32193"` кодирует верхнюю границу примерно ×5, а не 2,32-кратное усиление. Это не обещает, что любой пиксель станет в пять раз ярче: значение зависит от самой карты, а `weight` ограничивает эффект возможностями экрана.

### Почему `Gamma` - не та же gamma экрана

Слово gamma используется в двух местах:

1. `hdrgm:Gamma` изгибает **значения внутри gain map**, чтобы точнее распределить её ограниченные коды;
2. передаточная функция изображения, например приближённая gamma sRGB, связывает **код цвета с линейным светом**.

Это разные этапы. В данном файле ImageMagick сообщает gamma базового изображения около `0.4545`, а поле gain map равно `1`. Первое относится к интерпретации цвета, второе - к декодированию карты усиления. Gain map также не равна обычной gamma-коррекции: gamma применяет одну кривую ко всем пикселям с одинаковым входным значением, а карта может задать разное усиление двум участкам одинаковой исходной яркости.

## Как изображение становится ярче белого

Обычный белый интерфейса служит точкой отсчёта SDR. HDR-дисплей имеет **headroom**, или запас яркости выше этой точки. Декодер узнаёт доступный запас, вычисляет `weight`, восстанавливает HDR-пиксели по карте и передаёт их оконной системе. Поэтому блик может физически излучать больше света, хотя базовый JPEG всё ещё ограничен значением 255.

Нужны одновременно три условия:

1. файл содержит базу, gain map и корректные метаданные;
2. приложение умеет их декодировать;
3. дисплей и режим системы предоставляют HDR-запас.

На SDR-мониторе или в старой программе показывается только базовое rendition. При промежуточном запасе яркости карта применяется частично, а не просто включается или выключается.

## Что делает Extra Bright Images и где его предел

[Extra Bright Images](https://extrabrightimages.com/) принимает обычное изображение и запускает обработку локально в браузере. У сервиса есть общие для всего кадра настройки `Make brights brighter`, `Gain` и `Highlight threshold`. Порог позволяет сильнее затронуть света, поэтому результат не является равномерным умножением каждого пикселя. Но алгоритм всё равно автоматически проходит по всей картинке.

Выбрать кистью одну лампу, нарисовать маску вокруг окна или задать отдельное усиление лицу в этом интерфейсе нельзя. Для ручной работы нужен HDR-редактор с локальными масками. Например, Lightroom и Adobe Camera Raw позволяют включить HDR-редактирование, выделить объект, градиент или область кистью, изменить экспозицию локально, а затем экспортировать JPEG с gain map. Там редактируется HDR-изображение, а карта строится при экспорте автоматически.

Сервис предлагает два результата:

- **Ultra HDR JPEG** с SDR-базой и gain map для совместимых социальных сетей;
- **Native HDR PNG**, где HDR закодирован непосредственно, для указанного на сайте сценария с LinkedIn.

Если исходник уже потерял детали в пересвеченной области, усиление их не восстановит. Оно изменит яркость имеющихся данных.

## Как не потерять HDR при публикации

Обычный обработчик изображений может прочитать только первый JPEG, уменьшить его и сохранить новый файл. Тогда MPF, XMP, ISO-метаданные и сама gain map исчезнут. Наличие `.jpg` и даже копирование EXIF не гарантируют сохранение HDR.

Практическая проверка после обработки:

```text
1. Есть ли внутри второй JPEG?
2. Остались ли MPF и метка ISO 21496-1 или XMP hdrgm?
3. Совпадает ли SDR-вид в старом просмотрщике?
4. Появляются ли дополнительные света в HDR-просмотрщике?
```

Именно поэтому снимок в этой статье подключён как исходный статический JPEG, а не как автоматически созданная WebP-миниатюра.

## Короткий итог

Исследуемый файл содержит не «8-битный HDR-пиксель». Он содержит совместимый 8-битный SDR JPEG, второй одноканальный JPEG с пространственной картой усиления и инструкции по их объединению. EXIF рассказывает о съёмке, XMP и ISO 21496-1 описывают преобразование, MPF указывает расположение изображений, а HDR-декодер собирает из них rendition под возможности конкретного дисплея.

Источники для дальнейшего чтения:

- [Ultra HDR Image Format v1.1, Android Developers](https://developer.android.com/media/platform/hdr-image-format)
- [ISO 21496-1:2025, Gain map metadata for image conversion](https://www.iso.org/standard/86775.html)
- [Gain Map in Adobe Camera Raw](https://helpx.adobe.com/camera-raw/desktop/hdr-and-advanced-output/gain-map.html)
- [HDR Optimization in Lightroom](https://helpx.adobe.com/lightroom/desktop/edit-photos/hdr-output.html)
