◂ База знанийRYA.AEScene LUTs — проявка, экспозиция, покраска: три ступени цветаобновлено 23 сен 2026
Цвет — не один куб, а три ступени: проявка (log → Rec.709), экспозиция и покраска. Проявку решает камера — точнее, гамма, в которой снят клип; покраску решает вкус, и она одна на весь канал; экспозиция — вообще не лут, а число в стопах, своё у каждого клипа. Машина считает кандидатов и рекомендует числами, подписывает человек, и его подпись живёт в двух адресах: ДНК канала — в профиле канала, свойства съёмочного дня — в проекте.
Слой scripts/15_color/ — сквозной, по требованию: не часть run_pipeline, как 3.8 Sync и 0.2 Media Prep. Живой пример — съёмочный день YTEVO03: 163 клипа, три камеры, две гаммы.
Красная линия: неизвестная гамма — это падение, а не «покрасим как обычно». Гамма берётся только из измеренных метаданных клипа (сайдкар Sony M01.XML · тег контейнера DJI com.dji.camera.ColorGammaSxS · запись прошлого замера в {CODE}_lut_plan.json) — никаких догадок по имени камеры. Не измерилась — список кандидатов пуст и прогон встаёт. Ровно эта снисходительность стоила дня: 61 клип из 163 (37 %) красился чужой математикой, потому что старый код ставил флаг «mismatch» и ехал дальше.
🧪 гамма из метаданных→ 📐 кандидаты по серому клину→ 🖼 витрина на кадрах дня→ 🙋 подписывает человек↺ 🧬 профиль канала + выбор дня

📉3.3 · 1Зачем разделять — и сколько стоила одна ступень вместо трёх

Пока цвет был одной ступенью, в одном кубе жили и проявка, и покраска — и всё это под S-Log3. Снятое в другой гамме красилось чужой математикой.

⚠️ Вред был не в цвете, а в экспозиции. S-Log3-лут на D-Log2 не «обесцвечивает» — он топит картинку: luma 58 → 23, 80 → 47. И это же порождало вырождение подборщика: счёт «яркость + насыщенность» видел утопленные кадры тёмными и ставил им лут, который вытягивает. Отсюда 03_dark_scene у 115 клипов из 163 — не выбор, а петля обратной связи на собственной ошибке.

Критерий, который работает, — зажатые тени. Исходники дня проверены: чистого чёрного в них 0,00 %, значит всё зажатое создаёт лут, а не съёмка. Зажатую тень не вернуть ничем.

Кандидат проявкиСерый клинТёмный кадр дняКадров зажато
Sony S-Log3 · neutral12,3 %14,9 %32 из 132
Sony S-Log3 · neutral legacy ✓ выбран0,0 %0,01 %0 из 132
DJI D-Log2 · plus9,2 %55,7 %—
DJI D-Log2 · minus ✓ выбран6,2 %0,11 %—
прежний единственный куб канала9,23 %—62 из 132
Число — доля кадра, которую лут утапливает в чистый чёрный: не «темнее», а «детали там больше нет». neutral и neutral legacy — один и тот же проявочный лут в двух авторских вариантах, legacy контрастнее; у DJI та же пара называется plus / minus.
✅ Разделение окупилось на первом же дне. Две гаммы получили каждая свою проявку, покраска стала одним решением на канал вместо трёх кубов «на все случаи», а экспозиция перестала быть побочным эффектом выбора лута и стала числом, которое видно и можно подвинуть.
163 клипа дня2 гаммы3 камеры162 клипа с экспозицией18 поправлено против машины

🪜3.3 · 2Три ступени — кто что решает и на каком уровне

исходник в LOG
   ├─ 1. PRE-LUT (необязательно) ── буст динамического диапазона
   ├─ 2. ПРОЯВОЧНЫЙ ── log → Rec.709, ПО КАМЕРЕ И ГАММЕ. Технический шаг.
   └─ 3. ПОКРАСОЧНЫЙ ── look поверх нормальной картинки. ДНК канала.
ступень 1 · pre
Pre-LUT

Буст динамического диапазона до проявки. Необязательная: в библиотеке два куба, в витрине выбора не участвует.

ступень 2 · develop
Проявка

log → Rec.709. Решает не вкус, а гамма клипа. Машина даёт кандидатов и рекомендацию числами, человек подписывает.

ступень 3 · look
Покраска

Характер картинки поверх нормального Rec.709. Единственное по-настоящему вкусовое решение — и одно на весь канал.

СтупеньЧто решаетКто решаетУровеньГде живёт решение
develop проявкаlog → Rec.709машина считает, подписывает человекпо гаммеYTs/{КАНАЛ}/color_profile.json → develop.{гамма}.lut
look покраскахарактер картинкичеловекодин на каналcolor_profile.json → look.id
экспозициясдвиг в стопахмашина считает промах, человек правитпо клипу{проект}/01_Source/00_LUT/_build/{CODE}_color_choice.json → exposure
Профиль пишется по гамме, а не по камере: завтра в парке появится третья тушка на S-Log3 — и она должна получить ту же проявку без правок. Камеры внутри записи лежат списком.
💡 Два адреса — потому что у решений разный срок жизни. Проявка и look — ДНК канала, они переживают проект. Экспозиция — свойство конкретного съёмочного дня: другой свет, другие числа. Смешивать их в одном файле значит через месяц не знать, что можно переносить на следующую съёмку.
⚠️ Порядок обработки ≠ порядок решений. Внутри Lumetri экспозиция живёт ровно между ступенями — так устроен шаблон проекта:
Basic Correction:  LUT (проявка) → BasicCorrection3 (здесь Exposure)
Creative:          LUT (покраска)
А решаем мы в обратном порядке: сначала две настройки на весь канал, и только потом экспозиция по каждому клипу под ними. Выбирать экспозицию, не зная look, бессмысленно — он сдвигает яркость. Превью обязано повторять порядок Lumetri, иначе оно врёт про результат в Premiere.
⚠️ Кадры рендерятся цепочкой из двух lut3d, а не склеенным кубом. Склейка проявки с покраской точна только для гладких look'ов: замер на реальной паре — Baza 0,98/255, Film 1 23,9/255, GOLD 54,3/255, и уплотнение сетки до 129³ (49 МБ на куб) GOLD не лечит. Критерий «≤ 1/255» из старого хэндоффа недостижим, поэтому склейка — условный путь, а не основной; вопрос решается после пробы Input LUT в Premiere.

🎞3.3 · 3Проявка — решает камера, а не вкус

Одна гамма — один проявочный лут на канале. Клип не «подбирают на глаз»: его гамму читают, по гамме берут запись из профиля канала, из записи — куб. Человек подписывает профиль, а не каждый клип.

Сквозной пример — как клип RYA-ZVE1-1928.MP4 получил свою проявку

1. КЛЮЧ ПЛАНА   12_Site_Blessing/RYA-ZVE1-1928.MP4
   слаг         12_Site_Blessing_RYA_ZVE1_1928     # имя без разделителей — ключ выбора человека
   камера       CAM-B_ZVE1

2. ГАММА        сырая строка "s-log3-cine"  →  нормализовано в S-Log3
   источник     кэш (legacy_lut_plan)              # не замер: оригинал — симлинк на карту

3. ПРОФИЛЬ      YTs/YTEVO/color_profile.json → develop["S-Log3"].lut
   develop_source: "профиль: гамма S-Log3"       # откуда взялось — записано рядом

4. КУБ          sony__slog3_sgamut3cine__rec709__neutral__legacy
   в проекте    YTAI_sony__slog3_sgamut3cine__rec709__neutral__legacy.cube

5. ДАЛЬШЕ       экспозиция: витрина +2.0 → Lumetri +4.8  (×2,4)
   покраска     look__newstar                      # одна на канал, к камере не привязана
Шаги 1–4 детерминированы: одна и та же строка гаммы всегда даёт один и тот же куб. Вкус появляется только на шаге 5 — и то не в выборе лута, а в числе: машина предложила этому клипу +1, Роман поставил +2. Это один из 18 случаев дня, где человек пошёл против машины. Сама экспозиция — 3.8 · 4.
Что именно читается на шаге 2 — запись кэша гамм, как она лежит на диске:
"12_Site_Blessing/RYA-ZVE1-1928.MP4": {
  "gamma": "S-Log3", "gamma_raw": "s-log3-cine", "source": "legacy_lut_plan",
  "cam": "CAM-B_ZVE1", "pix_fmt": "yuv422p10le", "size": null, "mtime": null,
  "measured": "2026-09-21T10:32:49+0300"
}
size и mtime равны null не по ошибке: оригиналы дня — симлинки на съёмную карту, и пока том не примонтирован, файла для замера нет. Поэтому запись прошлого замера — не кэш «для скорости», а единственный источник гаммы на размонтированной карте. Источник печатается рядом с клипом, чтобы «откуда мы это знаем» читалось глазом, а не лежало в логе.

Гаммы дня — две проявки и один пропуск

ГаммаПроявочный лутКамерыКлипов дня
S-Log3sony__slog3_sgamut3cine__rec709__neutral__legacyCAM-A_FX3 CAM-B_ZVE1101
D-Log2dji_pocket4p__dlog2__rec709__minusCAM-C_Pocket61
Rec.709— проявка не нужнаCAM-B_ZVE11 пропуск
163 клипа дня = 162 в плане + 1 пропуск. Парк камер — Gear; кто чем снимал в конкретный день — 3.1 Shoot Day Ingest. Читаемая подпись в витрине — neutral legacy, minus: полный id нужен манифесту, не глазу. Имя файла в проекте выводится из id с префиксом: YTAI_<id>.cube.

ДНК канала — фрагмент color_profile.json как есть

# ~/YTAI/YTs/YTEVO/color_profile.json
"develop": {
  "S-Log3": {
    "lut": "sony__slog3_sgamut3cine__rec709__neutral__legacy",
    "cameras": ["CAM-A_FX3", "CAM-B_ZVE1"],
    "decided_by": "roman", "decided_at": "2026-09-22"
  },
  "D-Log2": {
    "lut": "dji_pocket4p__dlog2__rec709__minus",
    "cameras": ["CAM-C_Pocket"],
    "decided_by": "roman", "decided_at": "2026-09-22"
  }
},
"look": { "id": "look__newstar", "decided_by": "roman" },
"exposure": { "target_face_luma": 134 }
Ключ словаря — гамма, а не камера. Камеры лежат внутри записи списком и нужны только для чтения человеком: завтра в парке появится третья тушка на S-Log3 — и она получит ту же проявку без правок профиля. Цель по лицу 134 — не догадка, а выученное по выбору Романа число; она живёт здесь же, потому что переживает проект.

Почему именно эти кубы — замеры ущерба

Критерий один и измеримый: сколько картинки лут утапливает в чистый чёрный. Зажатую тень не вернуть ничем.

КубСерый клинРеальные кадры дняКадров с зажимом > 1 %
DJI D-Log2 plus9,23 %23,39 %52 из 76
DJI D-Log2 minus ✓ выбран6,15 %0,04 %0 из 76
Sony neutral12,31 %2,01 %32 из 132
Sony neutral legacy ✓ выбран0,00 %0,00 %0 из 132
старый 02_normal_scene15,38 %9,23 %62 из 132
старый 03_dark_scene9,23 %0,15 %1 из 132
Выбор DJI читается только по третьей колонке: на клине minus выигрывает у plus всего 6,15 % против 9,23 %, а на реальных кадрах разрыв — 0,04 % против 23,39 %, и 52 испорченных кадра из 76 против нуля.
⚠️ Клин и реальные кадры — разные метрики, и число 9,23 стоит в таблице трижды. 9,23 % у DJI plus — это клин; 9,23 % у 02_normal_scene — это реальные кадры; ещё один 9,23 % — клин у 03_dark_scene. Совпадение случайное, колонки разные. Сравнивать кубы можно только внутри одной колонки: 03_dark_scene с клином 9,23 % портит 1 кадр из 132, а 02_normal_scene с теми же 9,23 % по другой колонке — 62 из 132.

Пропуск и отказ — это разные исходы

ИсходКогдаЧто делает слойНа дне YTEVO03
plannedгамма известна, в профиле есть записьзаписывает в план проявку, экспозицию и look162
skipped штатнопроявлять нечего — клип уже Rec.709пропускает с причиной, прогон идёт дальше1
refused стопгамма незнакомая или её нет в профиле каналаназывает клип и останавливается — чужой математикой не красит0
# единственный пропуск дня, как он записан в плане
"skipped": {
  "12_Site_Blessing/RYA-ZVE1-1927.MP4": "снято в Rec.709 — проявка не нужна"
},
"refused": {}
Соседний по номеру клип RYA-ZVE1-1928.MP4 с той же камеры CAM-B_ZVE1 проявку получил, а 1927 — нет. Это и есть доказательство, что гамма — свойство клипа, а не камеры: одна тушка в один день дала и s-log3-cine, и rec709.
⚠️ Незнакомая гамма = отказ с именем, а не «поставим что-нибудь». Пустой список кандидатов обязан ронять прогон. Молчаливая подстановка ведущей проявки — ровно та ошибка, ради которой слой и разделили: S-Log3-лут на D-Log2 не «обесцвечивает», он топит картинку. Отказ читается как «человек, посмотри на этот клип»; пропуск — как «здесь работы нет». Путать их нельзя: обнулённый счётчик отказов должен что-то значить, а если отказ научился превращаться в пропуск, ноль в колонке refused перестаёт быть доказательством.

Где лежит результат

{проект}/01_Source/00_LUT/                       # рабочие кубы ПЛОСКО, без подпапок
   YTAI_sony__slog3_sgamut3cine__rec709__neutral__legacy.cube
   YTAI_dji_pocket4p__dlog2__rec709__minus.cube
   YTAI_look__newstar.cube
   README.md
   _build/                                  # кухня выбора
      {CODE}_color_choice.json                  # выбор человека по дню
      {CODE}_color_plan.json                    # раскладка на таймлайн
      {CODE}_gamma_cache.json                   # замер гамм
      {CODE}_lut_board.html  + _files/          # витрина

{проект}/00_Setup/01_Ingest/{CODE}_lut_plan.json   # ⚠️ ЛЕГАСИ, НЕ переезжает

~/YTAI/YTs/{КАНАЛ}/color_profile.json                          # ДНК канала
~/Library/Application Support/Adobe/Common/LUTs/Input/YTAI/     # проявочные
~/Library/Application Support/Adobe/Common/LUTs/Creative/YTAI/  # покрасочные
  • 1Кубы внутри проекта лежат плоско. Только эта копия уезжает к монтажёру; папки Adobe остаются на машине.
  • 2Папки Adobe — Creative, Input, Output. Папки Technical не существует — на этом уже обожглись.
  • 3{CODE}_lut_plan.json в 00_Setup/01_Ingest/ не переезжает. Это легаси-файл, его читает UXP-панель; он же служит запасным источником гаммы (source: "legacy_lut_plan" в примере выше).
  • 4Байты кубов не в git. В git — только manifest.json, 169 записей (develop 137 · look 27 · pre 2 · legacy 3); store/ — 559 МБ, восстанавливается с Shared Drive YTAI (_color/lut_library/). Подробности — 3.8 · 6.
⚠️ Раскладка на таймлайн пока не ложится. План YTEVO03_color_plan.json собран и подписан, но Input LUT и Exposure в Premiere программно ни разу не ставились — проба не сделана. Всё выше описывает выбор проявки, а не покрашенный таймлайн. Сам множитель 2,4 тоже выведен из блоба шаблона Lumetri, а не замерен на живой программе — в плане это помечено флагом exposure_verified_against_premiere: false. Писать «у нас автоцветокор на таймлайне» рано.
V=~/YTAI/environment/.venv_vlm/bin/python3

# витрина: кадры + кандидаты
$V ~/YTAI/scripts/15_color/1502_lut_pick/lut_board.py --project "<путь>" --frames-from <прокси> --jobs 6
# вернуть выбор человека
$V ~/YTAI/scripts/15_color/1502_lut_pick/lut_board.py --project "<путь>" --feedback <файл.json>

# раскладка: без --apply ничего не пишется
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "<путь>"
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "<путь>" --apply
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "<путь>" --install --apply

# свести луты проекта в один дом 00_LUT
python3 ~/YTAI/scripts/15_color/lut_home.py --project YTEVO03 --apply
--install копирует кубы в общие папки Adobe. ⚠️ Общая папка читается при старте Premiere: копировать нужно до запуска, изнутри программы уже поздно.

🎚3.3 · 4Экспозиция — число между ступенями, а не лут

Экспозиция — не куб, а число в стопах, и стоит оно между двумя кубами: lut3d(проявка) → exposure → lut3d(look). Проявка одна на гамму, look один на канал, а это число — своё у каждого клипа. В Premiere это слайдер Exposure в Basic Correction: куб непрозрачен, слайдер видно — монтажёр может подвинуть.

Главное число раздела — множитель 2,4

⚠️ Стоп витрины и стоп Lumetri — разные величины. Превью считает экспозицию фильтром exposure в ffmpeg поверх гамма-кодированного Rec.709: это простое умножение кода на 2^X. Lumetri по блобу шаблона идёт цепочкой LUT → Linearize «Gamma 2.4 to Linear» → BasicCorrection3 (Exposure), то есть линеаризует ДО экспозиции. Один и тот же «+1» даёт разную картинку.
# один пиксель клипа RYA-ZVE1-1928, код 64, ступень витрины +1 EV

витрина (ffmpeg)    64 · 2^1                                  = 128   # умножение кода
Lumetri, +1         ((64/255)^2.4 · 2^1)^(1/2.4) · 255        ≈ 85    # линеаризация съедает больше половины стопа
Lumetri, +2.4       ((64/255)^2.4 · 2^(2.4·1))^(1/2.4) · 255  = 128   # ровно то, что человек видел

# тождество, из которого берётся множитель:
code · 2^X  ==  ((code/255)^2.4 · 2^(2.4X))^(1/2.4) · 255
  • Связь точная: стопы Lumetri = 2,4 × стопы витрины — проверено на 30 комбинациях кодов и стопов, расхождений нет. Кадры, которые человек смотрел и утверждал, верны как изображение: расходится только число, и пересчитывается оно одной константой при раскладке. Перевыбирать день не нужно.
  • ⚠️ Гамма 2,4 прочитана, а не замерена. Она взята из строки "Gamma 2.4 to Linear" внутри блоба шаблона — это чтение файла, а не замер на живой программе. В раскладке это записано прямо: "exposure_verified_against_premiere": false. До пробы Premiere так и считать.

Таблица пересчёта — и весь день YTEVO03 в ней

Ступень витриныЧисло в LumetriКлипов дняЧто это за клипы
−1,0−2,401единственный за день, который пришлось темнить на полный стоп
−0,5−1,2011лицо выше цели — половина ступени вниз
+0,00,0060экспозицию не трогаем: лицо уже на цели либо лица на кадре нет
+0,5+1,2033базовая лестница
+1,0+2,4040самая частая ненулевая поправка дня
+1,5+3,608доборная ступень
+2,0+4,803здесь лежит RYA-ZVE1-1928 — разбор ниже
+2,5+6,006максимум дня; в Lumetri это всё ещё внутри ±7
Сумма — 162 записи «клип → стопы» при 163 клипах в дне: 12_Site_Blessing/RYA-ZVE1-1927.MP4 пропущен с причиной «снято в Rec.709 — проявка не нужна» и в план не попал. Отказов — 0. Доборные ступени берутся не по порядку: +2.5 встретилась 6 раз, а +2.0 — 3, потому что ladder_for(промах) достраивает лестницу под конкретный промах, а не проходит её подряд.

Предел ползунка ±7 — край лестницы недостижим

⚠️ Предел слайдера в Lumetri — ±7 (LUMETRI_EXPOSURE_LIMIT = 7.0, из таблицы параметров шаблона). Витринный потолок ±3 — это EXPOSURE_MAX = 3.0, предел фильтра exposure в ffmpeg; после множителя он превращается в ±7,2, то есть край лестницы в Premiere поставить нельзя. На этом дне пронесло: максимум +2.5 → +6,0, запас есть. Но клип, которому диапазона не хватит, обязан стать отказом с именем, а не молча зажатым числом на границе.

Разбор одного клипа: RYA-ZVE1-1928

12_Site_Blessing/RYA-ZVE1-1928.MP4
слаг 12_Site_Blessing_RYA_ZVE1_1928
CAM-B_ZVE1гамма S-Log3источник: кэш (legacy_lut_plan)машина +1.0человек +2.0Lumetri +4.8look__newstar
проявка sony__slog3_sgamut3cine__rec709__neutral__legacy — выбрана не по клипу, а по гамме: в раскладке это записано как develop_source: "профиль: гамма S-Log3", и профиль канала держит эту проявку для всей пары CAM-A_FX3 · CAM-B_ZVE1
  • Машина предложила +1.0, человек поставил +2.0 — это один из 18 случаев из 162, где подпись разошлась с рекомендацией; в файле выбора пара лежит целиком: corrected["12_Site_Blessing_RYA_ZVE1_1928"] = [1, 2]. Машина мерит медиану яркости лица и считает промах до цели; человек смотрит на кадр целиком.
  • На таймлайн это число уедет как +4.8 — руками его никто не набирает, пересчёт делает код по константе 2,4.
  • Гамма взята из кэша, а не измерена заново. Запись замера:
{"gamma":"S-Log3","gamma_raw":"s-log3-cine","source":"legacy_lut_plan",
 "cam":"CAM-B_ZVE1","pix_fmt":"yuv422p10le","size":null,"mtime":null,
 "measured":"2026-09-21T10:32:49+0300"}
size и mtime — null: оригинал лежит симлинком на съёмную карту, и пока том не примонтирован, сверять нечего. Сырая строка s-log3-cine нормализуется в S-Log3 — в манифесте гамма пишется одним способом.
{проект}/01_Source/00_LUT/_build/
    YTEVO03_color_choice.json     # exposure[слаг] = 2, corrected[слаг] = [1, 2]
    YTEVO03_color_plan.json       # plan["12_Site_Blessing/RYA-ZVE1-1928.MP4"].exposure = 4.8
    YTEVO03_gamma_cache.json      # запись выше
    YTEVO03_lut_board.html        # витрина; кадры рядом — YTEVO03_lut_board_files/
    YTEVO03_color_plan.20260922_234159.bak.json   # снимок прошлой раскладки, --apply их копит

{проект}/00_Setup/01_Ingest/YTEVO03_lut_plan.json
    # ЛЕГАСИ и НЕ переезжает: это он подписан в кэше как source "legacy_lut_plan",
    # и его же читает UXP-панель
V=~/YTAI/environment/.venv_vlm/bin/python3
# так +2.0 попал в файл выбора: витрина сохраняет и выходит, ничего не рендерит
$V ~/YTAI/scripts/15_color/1502_lut_pick/lut_board.py --project "{путь}" --feedback {файл.json}

Кто решает что

ВеличинаКто решаетГде живёт
Цель по лицу target_face_lumaчеловек — 134, выучена по выбору Романа; TARGET_FACE_LUMA_DEFAULT = 150.0 было стартовой догадкой, именно догадкой~/YTAI/YTs/YTEVO/color_profile.json — ДНК канала, переживает проект
Промах в стопахмашина — log2(цель / медиана яркости лица) на проявленном кадренигде: считается на проб-кадрах и умирает
Ступень витрины −1.0 … +2.5машина предлагает, человек подписывает — 18 правок из 162_build/YTEVO03_color_choice.json — ключи exposure и corrected
Множитель 2,4код — константа пересчёта, руками не набирается1502_lut_pick/color_plan.py — LUMETRI_GAMMA = 2.4; color_apply.py только пишет её в раскладку как exposure_multiplier
Число в Lumetri +4.8код — выводится из ступени, в выборе человека не хранится_build/YTEVO03_color_plan.json — секция plan, поле exposure
Правка после раскладкимонтажёр — слайдер открыт, куб непрозраченпроект Premiere
Разделение не косметическое: цель по лицу и look — ДНК канала и лежат в профиле, ступени и пересчёт — свойство съёмочного дня и лежат в проекте.
⚠️ Ни одно из этих чисел ещё не доехало до таймлайна. Input LUT и Exposure программно не ставились ни разу, проба Premiere не сделана, и раскладка сама это признаёт полем "exposure_verified_against_premiere": false. Раскладка существует как файл, а не как покрашенная секвенция — писать «у нас автоцветокор на таймлайне» рано.
# сухо — только печатает раскладку, в проект не пишет
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "{путь}"
# с записью и с установкой кубов в Adobe — на живом Premiere ещё не доказано
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "{путь}" --install --apply

🎨3.3 · 5Покраска — ДНК канала, одна на канал

Look ложится на уже нормальный Rec.709 и к камере не привязан. Выбор YTEVO — look__newstar, подписан 22.09.2026.

  • Показываются ВСЕ покрасочные, без урезания. Первая версия резала до восьми «репрезентативных» — и человек выбрал look, не увидев девятнадцати остальных. Покраска — единственное по-настоящему вкусовое решение; урезать выбор за человека тут нельзя.
  • Машина только отсекает вред: прочь всё, что режет картинку — пережог > 2 % или зажатое чёрное > 1 %. Из выживших рекомендуется самый насыщенный: насыщенность и есть тот характер, ради которого look ставят.
  • Один канал — один look. Он записан в YTs/{КАНАЛ}/color_profile.json вместе с проявками и целью по лицу, и переживает проект.
⚠️ Папка «покрасочные» — это замысел автора, а не поведение куба. Валидатор ловит расхождение и просит человека посмотреть: look__film_1 лежит в покрасочных, а ведёт себя как проявка (наклон 1,47 при пороге 1,41); look__baza сам топит 6,2 % серого клина в чёрное. Ступень берётся из папки-источника, метрика ставит флаг — и только.
⚠️ Lumetri Look ссылается на ИМЯ файла .cube. Переименовал куб — у всех проектов, где этот Look выбран, он «не тот». Поэтому файлы в библиотеке кладутся байт-в-байт и ставятся chmod 0444: они не редактируются руками и не переименовываются. Новое имя — это новый куб, а не правка старого.

📚3.3 · 6Библиотека — store, манифест, Drive, канон имён

Этап scripts/15_color/1501_lut_library/. 169 кубов на полке, 3 доехали до дня YTEVO03. Байты неизменяемы, в git лежит только манифест, а имя куба проходит три превращения: имя автора → id → имя при установке.

169 записейdevelop 137look 27pre 2legacy 3store 559 МБ
СтупеньКубовГде лежитЖивой пример — idВ git?
develop137store/develop/sony__slog3_sgamut3cine__rec709__neutral__legacy — 101 клип дня YTEVO03
dji_pocket4p__dlog2__rec709__minus — 61 клип
байты НЕТ · запись в manifest.json да
look27store/look/look__newstar — покраска канала YTEVO, одна на все 162 клипабайты НЕТ · запись да
pre2store/pre/pre__applelog__wdr и pre__any__wdr — приводят к общему знаменателю до проявки; в дне YTEVO03 ни один не участвовалбайты НЕТ · запись да
legacy3store/legacy/{author_slug}/ — сейчас один автор: arthur_popovlegacy_arthur_popov__slog3__rec709__pull ← 01_bright_scene
…__flat ← 02_normal_scene · …__push ← 03_dark_scene
старые имена живут в поле aliases, рядом лежит ATTRIBUTION.md
байты НЕТ · запись да
итого1691501_lut_library/store/ — 559 МБиз них в проект YTEVO03 уехало 3: две проявки и один lookmanifest.json в git · store/ и */state/ в .gitignore
Манифест lut-manifest-v1 держит на каждый куб sha256, размер, сетку, диалект, вход/выход, вендора и камеру, семейство и вариант, aliases, метрики, evidence, conflicts и confidence. История правок манифеста и есть история библиотеки — байты восстановимы, решения нет.

Где что лежит — раскладка после переезда 23.09.2026

{проект}/01_Source/00_LUT/                   # дом лутов: рабочие кубы ПЛОСКО, без подпапок
├── YTAI_sony__slog3_sgamut3cine__rec709__neutral__legacy.cube   # проявка Sony · 101 клип · chmod 0444
├── YTAI_dji_pocket4p__dlog2__rec709__minus.cube                 # проявка DJI · 61 клип · chmod 0444
├── YTAI_look__newstar.cube                                      # покраска канала · 162 клипа
├── 01_bright_scene.cube · 02_normal_scene.cube · 03_dark_scene.cube
│                                            # ⚠️ одноступенчатая тройка прошлого поколения — ЕЩЁ ЛЕЖИТ РЯДОМ
├── README.md                                # пишет lut_home; ⚠️ в v1.0 «рабочей тройкой» названа ещё старая
└── _build/                                  # кухня выбора — монтажёру не нужна, нам через полгода нужна
    ├── YTEVO03_color_choice.json            # выбор человека: 162 экспозиции, 18 правок, look
    ├── YTEVO03_color_plan.json  (+ .bak)    # раскладка: клип → проявка + стопы + look
    ├── YTEVO03_gamma_cache.json             # гаммы 163 клипов; переживает размонтированную карту
    ├── YTEVO03_lut_board.html + _files/     # витрина, по которой выбирали
    └── YTEVO03_lut_review.html + _files/    # витрина прошлого поколения — оставлена как история

{проект}/00_Setup/01_Ingest/YTEVO03_lut_plan.json   # ⚠️ ЛЕГАСИ — НЕ переезжает: его читает UXP-панель

~/YTAI/YTs/YTEVO/color_profile.json              # ДНК канала: проявки по гаммам, look, цель по лицу 134
~/YTAI/scripts/15_color/1501_lut_library/
    ├── manifest.json                        # 169 записей — В GIT
    └── store/                               # 559 МБ байтов — НЕ в git, тянется с Drive

~/Library/Application Support/Adobe/Common/LUTs/Input/YTAI/      # проявочные → Basic Correction → Input LUT
~/Library/Application Support/Adobe/Common/LUTs/Creative/YTAI/   # покрасочные → Lumetri → Creative → Look
  • Один дом на проект — 01_Source/00_LUT/. Всё, как выбирали, — в _build/, рядом с кубами, а не в 00_Setup, куда никто не пойдёт. Правило папки: ничего не удаляем, лишнее — в _build/.
  • В YTEVO03 в папке пока шесть кубов, а не три. Рядом с новыми YTAI_* лежит одноступенчатая тройка 01_bright_scene · 02_normal_scene · 03_dark_scene, и README.md версии lut_home v1.0 называет рабочей именно её. Пока это не разведено, «открыл папку и взял» не работает: монтажёру надо говорить, какие три из шести.
  • К монтажёру уезжает только копия в проекте. Папки Adobe остаются на этой машине: они — способ показать кубы Premiere, а не способ их передать. Так и записано в плане: install.project_dir — единственный путь, который едет.
  • {CODE}_lut_plan.json остаётся в 00_Setup/01_Ingest/. Это легаси-файл, его читают UXP-панель и lut_board.py; план ссылается на него как на sources.legacy_lut_plan. Перенести его «для порядка» — сломать чужой работающий код.
python3 ~/YTAI/scripts/15_color/lut_home.py --project YTEVO03            # сухо: расскажет, что сделал бы
python3 ~/YTAI/scripts/15_color/lut_home.py --project YTEVO03 --apply    # свести луты в один дом
⚠️ У Adobe папки называются Creative / Input / Output. Папки «Technical» НЕ СУЩЕСТВУЕТ — на этом уже обожглись: план выписывается в несуществующий путь, и обнаруживается это только в Premiere. Сверено с диском: в Input/ рядом с нашими уже лежат peresvet.cube и nedosvet.cube прошлого поколения — байт в байт та же пара, что 01_bright_scene и 03_dark_scene. Второе: общая папка Adobe читается при старте Premiere — копировать нужно до запуска, изнутри программы панель уже опоздала.
⚠️ Гаммы дня НЕ ЗАМЕРЕНЫ — они прочитаны из файла. В YTEVO03_gamma_cache.json все 163 из 163 записей несут "source": "legacy_lut_plan": гамма взята из старого YTEVO03_lut_plan.json, ffprobe по клипам не ходил. Раскладка по гаммам — S-Log3 101 · D-Log2 61 · Rec.709 1 — ровно повторяет старый план, а не измерение. Поле measured в записи — это когда занесли в кэш, а не чем померили.

Канон имён — разбор на живом кубе

develop/  {vendor}[_{camera}]__{gamma}_{gamut}__rec709__{family}[__{variant}].cube
look/     look__{name}[__{variant}].cube
pre/      pre__{scope}__{target}.cube
legacy/   {author_slug}/legacy_{author_slug}__{gamma}__rec709__{role}.cube

# герой дня YTEVO03 — проявка, которой покрашен 101 клип из 162
sony__slog3_sgamut3cine__rec709__neutral__legacy
│     │     │            │       │        └───── variant : авторский вариант
│     │     │            │       └────────────── family  : семейство, характер проявки
│     │     │            └────────────────────── выход   : всегда Rec.709 у проявки
│     │     └─────────────────────────────────── gamut   : S-Gamut3.Cine
│     └───────────────────────────────────────── gamma   : S-Log3 — вход
└─────────────────────────────────────────────── vendor  : Sony
БлокВ нашем примереЧто значит и откуда взялось
sonyвендорИз архива-источника Sony SLOG3.zip, не из имени файла: путь архива — доказательство рангом выше имени. В манифесте это видно в evidence: archive:SLOG3 → S-Log3.
тушкаеё в id нетОригинал называется Neutral A7s3-Legacy.cube, в манифесте стоит camera: ["a7s3"] — но S-Log3 одинаков у всех тушек Sony, поэтому тушка в id не пишется. У DJI наоборот, тушка обязательна: dji_pocket4__dlog__rec709__plus и dji_pocket4p__dlog__rec709__plus — два разных куба из двух разных архивов (DJI Pocket 4.zip и DJI Pocket 4p.zip). Выброси тушку — и они схлопнутся в один id.
slog3_sgamut3cineвход: S-Log3 / S-Gamut3.CineВнутри блока разделители выбрасываются, а не заменяются на _: S-Gamut3.Cine → sgamut3cine, D-Log2 → dlog2. Иначе _ внутри блока размывает границу блоков, которую держит __.
rec709выходКуда куб приводит картинку. У ступени develop это всегда Rec.709 — проявка кончается нормальным изображением, а не полуфабрикатом.
neutralсемействоЗамысел автора: нейтральная проявка без характера. Характер — дело ступени look. Семейство может и отсутствовать: у героя-соседа dji_pocket4p__dlog2__rec709__minus в манифесте family: null, и minus стоит уже в слоте варианта.
legacyвариантУ Sony их два: neutral и neutral__legacy. В профиль YTEVO встал именно legacy — он единственный дал 0,00 % на клине и 0,00 % на реальных кадрах, против 12,31 % / 2,01 % у соседа neutral.
Правило разведения: push / pull — наша лестница экспозиции (что лут делает), plus / minus — авторский вариант контраста. У одного id не может стоять и то, и другое. Имя описывает, что лут делает, а не каким снят кадр: архивный 01_bright_scene (пересвеченный кадр) лежит в библиотеке как …__pull, потому что он тянет вниз.

Три имени одного куба — и почему при установке появляется YTAI_

# 1 · имя автора — как пришло из архива, живёт в манифесте и больше нигде
orig_name      = "Neutral A7s3-Legacy.cube"
origin.archive = "Проявочные LUTs для LOG (sony, canon, panasonic, apple)/Sony SLOG3.zip"
origin.member  = "SLOG3/Neutral A7s3-Legacy.cube"

# 2 · id — вечный адрес: по нему ссылаются профиль канала, план и витрина
sony__slog3_sgamut3cine__rec709__neutral__legacy
store/develop/sony__slog3_sgamut3cine__rec709__neutral__legacy.cube   # байты, chmod 0444

# 3 · имя при установке — выводится из id префиксом YTAI_
YTAI_sony__slog3_sgamut3cine__rec709__neutral__legacy.cube
  → {проект}/01_Source/00_LUT/                                        # уезжает к монтажёру
  → ~/Library/…/Adobe/Common/LUTs/Input/YTAI/                         # видит Premiere

YTAI_look__newstar.cube
  → ~/Library/…/Adobe/Common/LUTs/Creative/YTAI/
  • Префикс обязателен, потому что Lumetri ссылается на ИМЯ ФАЙЛА. look__newstar.cube в общей Creative-папке рядом с чужими кубами — столкновение, ждущее своего часа: у проекта «тот же Look», а куб под ним другой.
  • Имя выводится из id, а не из orig_name. Переименовывать луты в сданных проектах нельзя никогда, значит имя должно быть выводимым и вечным: из id оно получается одной строкой — YTAI_{id}.cube, — а из авторского Neutral A7s3-Legacy.cube не получается ниоткуда.
  • Новое имя — это новый куб, а не правка старого. Байты кладутся в библиотеку неизменяемыми (chmod 0444): руками не редактируются и не переименовываются. В проекте у трёх YTAI_* права тоже 0444 — это проверяется глазом в ls -l.
Байты живут на Drive, решения — в git. store/ — 559 МБ, в репозиторий не кладётся; зеркало — rclone copy (не bisync) в _color/lut_library/ на Shared Drive YTAI. На Drive уезжает весь store целиком — это резерв для новой машины; персональная выборка канала — это те 3 куба, что color_apply кладёт в проект.
V=~/YTAI/environment/.venv_vlm/bin/python3
L=~/YTAI/scripts/15_color/1501_lut_library/lut_lib.py

$V $L --validate --deep        # проверить store и манифест, посчитать метрики
$V $L --list --stage develop --camera pocket4p   # фильтр по камере — по СЛАГУ манифеста, не по маркетинговому имени
$V $L --sync --push --apply    # зеркало store → Shared Drive YTAI (--push — режим по умолчанию)
$V $L --sync --pull --apply    # восстановить 559 МБ на новой машине

python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "<путь>"            # сухо
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "<путь>" --install --apply
У lut_lib.py режимы --import · --validate · --list · --legacy · --sync взаимоисключающие и обязательные — ровно один за вызов; без --apply ничего не записывается. --camera — подстрока по слагу из манифеста (pocket4p, a7s3): «DJI Pocket 4 Pro» не совпадёт ни с чем и вернёт пустой список. Про квоты и режимы Drive — 0.4 Drive Quota.
⚠️ Библиотека готова — раскладка на таймлайн ПОКА НЕ ЛОЖИТСЯ. Кубы именуются, ставятся в папки и уезжают в проект, но Input LUT и Exposure ни разу не ставились программно, и проба в Premiere не сделана. В самом плане это записано полем exposure_verified_against_premiere: false. Пока оно false, {CODE}_color_plan.json — это намерение, а не покрашенный день.

🖼3.3 · 7Витрина выбора и раскладка — что считает машина, что решает человек

Два этапа, а не один. 1502_lut_pick/lut_board.py рисует кадры дня и принимает подпись человека; 1503_color_apply/color_apply.py превращает подпись в раскладку по клипам. Витрина ничего в проекте не меняет, кроме своей папки кадров и своей страницы, и обе живут в доме лутов — {проект}/01_Source/00_LUT/_build/.

YTEVO03 · Плечкоклипов 163в плане 162пропущен 1отказов 0правок человека 18три камерыдве проявки
Камеры дня: CAM-A_FX3 49 клипов · CAM-B_ZVE1 53 · CAM-C_Pocket 61. Гаммы: S-Log3 101 · D-Log2 61 · Rec.709 1 — этот один и есть пропуск.

Путь дня — четыре шага

V=~/YTAI/environment/.venv_vlm/bin/python3
P="/Volumes/T9-Black-RYA/YTEVO/YTEVO03_Plechko_day"

# 1 — витрина: кадры дня через каждого кандидата
$V ~/YTAI/scripts/15_color/1502_lut_pick/lut_board.py \
    --project "$P" --frames-from ~/Desktop/YTEVO03_01_Source_Proxy --jobs 6

# 2 — подпись: кнопка «скопировать» в витрине отдаёт JSON
$V ~/YTAI/scripts/15_color/1502_lut_pick/lut_board.py \
    --project "$P" --feedback "~/Downloads/choice.json"

# 3 — раскладка: СНАЧАЛА сухо, файл не пишется
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "$P"
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "$P" --apply

# 4 — кубы по местам: папки Adobe + дом лутов проекта
python3 ~/YTAI/scripts/15_color/1503_color_apply/color_apply.py --project "$P" --install --apply
python3 ~/YTAI/scripts/15_color/lut_home.py --project YTEVO03 --apply   # свести луты в один дом
ШагЧто делаетЧто появляется на диске
1 витринакадр на каждый клип через каждого кандидата, цепочкой проявка → экспозиция → look_build/YTEVO03_lut_board.html + _build/YTEVO03_lut_board_files/ — 192 кадра, 53 МБ. Имя кадра несёт id проявки:
01_Morning_Run_RYA_FX3_1213__sony__slog3_sgamut3cine__rec709__neutral__legacy__exp00.jpg
2 подписьсохраняет выбор и выходит — ничего не рендерит_build/YTEVO03_color_choice.json (схема color-choice-v1) и ДНК канала ~/YTAI/YTs/YTEVO/color_profile.json. Печатает: «клипов: 162 · поправлено против машины: 18»
3 раскладкасводит выбор дня и профиль канала в план по клипам, пересчитывает экспозицию ×2,4без --apply — НИЧЕГО, только отчёт в консоль. С --apply: _build/YTEVO03_color_plan.json, прошлая версия уезжает в YTEVO03_color_plan.20260923_100231.bak.json, doc_version растёт (сейчас 3), и всегда переписывается _build/YTEVO03_gamma_cache.json
4 кубыкопирует три куба плана в две папки Adobe и в дом лутов проектаInput/YTAI/ — две проявочные, Creative/YTAI/ — YTAI_look__newstar.cube, и та же тройка плоско в 01_Source/00_LUT/ рядом с README.md
⚠️ Общая папка Adobe читается при старте Premiere — копировать нужно до запуска; панель изнутри программы уже опоздала. Два адреса не дублируют друг друга: папки Adobe остаются на этой машине, а копия в 01_Source/00_LUT/ уезжает к монтажёру с проектом. ⚠️ У Adobe есть Creative, Input и Output — папки Technical не существует, на ней уже обожглись.

Что лежит в доме лутов после дня

{проект}/01_Source/00_LUT/
├── YTAI_sony__slog3_sgamut3cine__rec709__neutral__legacy.cube   # рабочие кубы ПЛОСКО
├── YTAI_dji_pocket4p__dlog2__rec709__minus.cube
├── YTAI_look__newstar.cube
├── README.md                          # что это, откуда, почему выбрано
└── _build/                            # кухня выбора, монтажёру не нужна
    ├── YTEVO03_color_choice.json      # выбор человека по дню
    ├── YTEVO03_color_plan.json        # раскладка по клипам, ждёт панель
    ├── YTEVO03_gamma_cache.json       # гаммы клипов, 163 записи
    ├── YTEVO03_lut_board.html + _files/
    └── *.bak.json                     # прошлые версии плана и кэша

{проект}/00_Setup/01_Ingest/YTEVO03_lut_plan.json   # ⚠️ ЛЕГАСИ, НЕ переезжает
~/YTAI/YTs/YTEVO/color_profile.json                 # ДНК канала
~/Library/Application Support/Adobe/Common/LUTs/Input/YTAI/      # проявочные
~/Library/Application Support/Adobe/Common/LUTs/Creative/YTAI/   # покрасочные
Имя куба в проекте выводится из id с префиксом: YTAI_<id>.cube. Сами байты в git не лежат — store/ это 559 МБ, восстанавливается с Shared Drive YTAI (_color/lut_library/); в git только manifest.json, 169 записей (137 develop · 27 look · 2 pre · 3 legacy).

Артефакты — кто пишет, кто читает

ФайлЧто внутриПишетЧитает
_build/{CODE}_color_choice.jsoncolor-choice-v1: проявка по камерам, look__newstar, target_face_luma 134, экспозиция на 162 клипа, corrected — 18 пар «машина, человек», decided_by: romanlut_board.py --feedbackcolor_apply.py
~/YTAI/YTs/{КАНАЛ}/color_profile.jsoncolor-profile-v1, ДНК канала: проявка по гамме (не по камере), один look, цель по лицуlut_board.py --feedbackcolor_apply.py для всех будущих дней канала
_build/{CODE}_gamma_cache.jsongamma-cache-v1: гамма каждого из 163 клипов, сырая строка, камера, pix_fmt, дата записиlut_board.py при замере и color_apply.py --apply — всегдаcolor_apply.py, когда оригиналы недоступны
_build/{CODE}_color_plan.jsoncolor-plan-v1: plan (что ставить), clips (паспорт клипа), develops, look, install, skipped, refused, stats, doc_versioncolor_apply.py --applyUXP-панель — когда её напишут
_build/{CODE}_lut_board.html + _files/витрина: кадры дня под каждым кандидатом, кнопка «скопировать выбор»lut_board.pyчеловек, глазами
00_Setup/01_Ingest/{CODE}_lut_plan.json⚠️ легаси. Единственный источник гамм этого дня, пока кэш не лёг на дискинструмент, который уже в архивеUXP-панель и lut_board.py — поэтому файл не переезжает
01_Source/00_LUT/README.mdчто за кубы лежат в папке, откуда взялись, почему выбраныlut_home.py --applyмонтажёр

Один клип от начала до конца

Герой — 12_Site_Blessing/RYA-ZVE1-1928.MP4, один из 18 клипов дня, где человек пошёл против машины.

// YTEVO03_gamma_cache.json → clips — откуда мы знаем гамму
"12_Site_Blessing/RYA-ZVE1-1928.MP4": {
  "gamma": "S-Log3", "gamma_raw": "s-log3-cine", "source": "legacy_lut_plan",
  "cam": "CAM-B_ZVE1", "pix_fmt": "yuv422p10le",
  "size": null, "mtime": null,        // оригинал — симлинк на снятую карту
  "measured": "2026-09-21T10:32:49+0300"
}

// YTEVO03_color_plan.json → clips — паспорт клипа: что мы про него выяснили
"12_Site_Blessing/RYA-ZVE1-1928.MP4": {
  "scene": "12_Site_Blessing", "clip": "RYA-ZVE1-1928.MP4",
  "slug": "12_Site_Blessing_RYA_ZVE1_1928",
  "cam": "CAM-B_ZVE1", "gamma": "S-Log3",
  "gamma_source": "кэш (legacy_lut_plan)",
  "develop_source": "профиль: гамма S-Log3",
  "exposure_preview": 2.0, "exposure_source": "roman",
  "src": ".../01_Source/12_Site_Blessing/CAM-B_ZVE1/RYA-ZVE1-1928.MP4"
}

// YTEVO03_color_plan.json → plan — ровно то, что панель поставит на клип
"12_Site_Blessing/RYA-ZVE1-1928.MP4": {
  "develop": "sony__slog3_sgamut3cine__rec709__neutral__legacy",
  "exposure": 4.8,        // 2.0 витрины × 2,4 = стопы Lumetri
  "look": "look__newstar"
}
  • clips отвечает «почему», plan — «что ставить». Панели нужен второй блок, человеку через полгода — первый. Разделены намеренно: plan читается машиной и не разрастается, clips хранит происхождение каждого решения.
  • Проявку не выбирали на этот клип — её принёс профиль канала. develop_source: "профиль: гамма S-Log3", и в color_profile.json у S-Log3 стоит sony__slog3_sgamut3cine__rec709__neutral__legacy с камерами CAM-A_FX3 и CAM-B_ZVE1. Решает гамма, камера идёт справкой.
  • Экспозицию выбрал человек, и в файле это видно: corrected["12_Site_Blessing_RYA_ZVE1_1928"] = [1, 2] — машина предлагала +1, Роман поставил +2. Пара хранится целиком, а не одним итогом: иначе не на чем учить цель target_face_luma 134, которая и выучена по таким расхождениям.
  • ⚠️ Гамма не измерялась ни у одного клипа дня. Все 163 записи кэша имеют source: "legacy_lut_plan", живых замеров 0: оригиналы — симлинки на съёмную карту, и пока карта не в слоте, size и mtime остаются null. Отсюда правило: легаси-файл 00_Setup/01_Ingest/{CODE}_lut_plan.json не переносят и не удаляют.

Числа дня — распределение, которое подписал человек

Витрина, стопыLumetri, ×2,4КлиповДоля дня
−1.0−2.4010,6 %
−0.5−1.20116,8 %
+0.006037,0 %
+0.5+1.203320,4 %
+1.0+2.404024,7 %
+1.5+3.6084,9 %
+2.0+4.8031,9 %
+2.5+6.0063,7 %
Итого в плане162100 %
Множитель 2,4 — не коэффициент вкуса: витрина считала экспозицию фильтром ffmpeg поверх гамма-кодированного Rec.709, а Lumetri линеаризует до экспозиции, см. карточку 4. Кадры, которые человек смотрел и утверждал, верны как изображение — в Premiere переносится другое число. Потолок ползунка ±7 не достигнут: витринный максимум дня +2,5 даёт +6,0.
ПроявкаКлиповОткуда
sony__slog3_sgamut3cine__rec709__neutral__legacy101профиль канала, гамма S-Log3 (CAM-A_FX3, CAM-B_ZVE1)
dji_pocket4p__dlog2__rec709__minus61профиль канала, гамма D-Log2 (CAM-C_Pocket)
пропущен 12_Site_Blessing/RYA-ZVE1-1927.MP41skipped: «снято в Rec.709 — проявка не нужна»
Пропуск — не ошибка, а запись в skipped с человеческой причиной. Отказы (refused) — другое: их на этом дне 0.
⚠️ Отказ громче тишины: непустой refused — и файл не пишется вовсе. Клип, которому не из чего собрать проявку или чья экспозиция не влезает в ползунок ±7, уходит в refused, и тогда color_apply.py падает с кодом 3, а {CODE}_color_plan.json НЕ ЗАПИСАН — ни целиком, ни частично.
  • Почему не «записать остальные, а этот пропустить». Клип без слоя в Premiere выглядит как обычный клип — не красный, не пустой, просто чуть другой. Частичный план отличается от полного ровно тем, что никто не заметит.
  • Обойти можно только явно: --allow-refused. Флаг есть, он не по умолчанию, и он означает «я посмотрел на причины выше».
  • Куб, не прошедший сверку, тоже отказ. Ключи вида __lut__<id> в refused — это «куба нет в манифесте» или «не сошёлся sha256»; блок такого лута в план не попадает вообще, чтобы --allow-refused не записал план с дырой на месте проявки.
⚠️ Раскладка на таймлайн пока НЕ ложится. План YTEVO03_color_plan.json собран, подписан и лежит на диске — но Input LUT и Exposure ни разу не ставились программно, проба в Premiere не сделана. В самом файле это записано честно: "exposure_verified_against_premiere": false.
  • Множитель 2,4 прочитан из блоба шаблона, а не замерен на живой программе. Замер витрины есть и сходится с арифметикой (код 64 при +1 EV даёт 128; через линеаризацию Lumetri тот же +1 дал бы ≈85), тождество code·2^X == ((code/255)^2.4 · 2^(2.4X))^(1/2.4)·255 — но это расчёт, а не проверка на Premiere.
  • Пока цвет заканчивается подписанным планом, а не покрашенным таймлайном. Писать «у нас автоцветокор на таймлайне» рано — сначала проба, потом панель.

⚠️3.3 · 8Грабли, оплаченные этой коллекцией

Имена и доказательства

  • Имя файла — не доказательство. DJI Action basic.cube содержит внутри # Mavic 3 Pro, D-Log M, и байт-в-байт тот же файл лежит как DJI OSMO Osmo Nano D-Log M.cube. Модель принимается, только если её подтверждает комментарий внутри куба или человек.
  • Вендора искать по basename архива и внутреннему пути, а не по полному пути. Верхняя папка коллекции перечисляет вендоров в своём названии — поиск по пути пометит все панасониковские кубы как Canon.
  • Pre-LUT в коллекции физически один: семь копий под именами Dji / Slog2 / Slog3 / Pocket 4 байт-в-байт идентичны, имена врут про вендора.
  • Дефис внутри слова ≠ отдельно стоящий знак. S-Log3 → slog3, D-Log2 → dlog2; но «Contr −» и «Contr +» — часть названия, и дописываются явно. Через общую таблицу замен «S-Log3» превращается в sminuslog3.
  • Кириллицу в именах считать ДО дедупликации: единственный кириллический файл коллекции — байт-дубль, после дедупа счётчик молча покажет ноль.
  • Один zip со снятым UTF-8-флагом даёт мохибейк 4╤Ç вместо 4р; лечится name.encode('cp437').decode('utf-8'). Имена папок macOS приходят в NFD — любое сравнение только после normalize('NFC', …).
  • AppleDouble ._*.cube внутри архивов имеют собственные sha256 и без фильтра по префиксу заводят фантомные записи в манифест.
  • *.cube в .gitignore глобально — нельзя: под git уже лежат 12 кубов (шаблоны папок проектов и бандл UXP-панели). Игнорируется только store/.
  • store не может лежать внутри коллекции-источника — импорт читал бы собственный вывод; проверка стоит на старте.

Измерение

  • Автоклассификатор ступени отвергнут как решающий. Ни одна метрика не делит коллекцию чисто: identity_rmse255 — перекрытие 158 из 166, neutral_drift255 — полное, black_share — 141, slope_mid — 43. В папке «проявочные для DJI» лежат 19 кубов с наклоном 0,97–1,38, то есть по поведению look'и. Папка говорит о замысле автора, метрики — о поведении, и это разные вещи.
  • В .cube быстрее всего меняется КРАСНЫЙ: reshape(N,N,N,3) даёт индексацию [b,g,r]. Перепутать — получить куб, переставляющий каналы в тенях; на глаз ловится не сразу, поэтому самопроверка гоняется на каждом запуске.
  • Два диалекта .cube: Resolve (пробелы, LF) и LUT Editor (таб после LUT_3D_SIZE, CRLF, без TITLE). line.split() без аргумента ест оба.
  • white_share для проявки — не флаг: проявки уходят в белое со входа 0,855–0,867, это осознанный ролловф. Предупреждение остаётся только для look'ов.
  • Монотонность — величина, а не булев флаг: настоящие кубы дрожат на доли кодового значения (Canon Vision — просадка 0,59/255, это квантование), а перевёрнутая кривая теряет десятки. Отсюда порог 2,0/255.
  • Выход за [0,1] у pre — смысл, а не ошибка: Alexa709 to WDR доходит до 1,051, 3,03 % значений вне диапазона. Для develop и look это ошибка.

Витрина и кадры

  • Серый клин не заменяет реальные кадры. Пара DJI + / − различается на клине на 3 пункта (9,2 % против 6,2 %), а на материале дня — на 23 (23,4 % против 0,04 %, замер по 76 кадрам). Клин отбирает кандидатов, решают кадры.
  • Имя проб-кадра несёт id проявки. Иначе кадры переживают смену рекомендации и остаются от прошлого прогона — а в самом первом прогоне гамма ещё не определялась, и вся экспозиция считалась бы по логарифмическим кадрам.
  • Целость кадра — не по весу файла. Вес JPEG зависит от детальности, а не от целости: тёмный интерьер — 1234 байта при luma 17, ровная светлая стена — 1504 байта при luma 161, оба кадра идеальны, а порог по весу забраковал шесть таких из 163. Настоящая проверка — im.load().
  • 1110 картинок на одной странице — без content-visibility:auto и contain-intrinsic-size браузер считает раскладку для всех сразу, и прокрутка дёргается.
Цвет решён, когда у каждой гаммы есть своя проявка, у канала — один look, а у клипа — число в стопах, и под всем этим стоит подпись человека. Примеры: YTEVO03 (163 клипа, две гаммы, 22.09.2026) · предыдущий YTCH13. Дальше → 3.10 Pre-Edit Word Precision.