Вечером вернулись к Хичкоковскому плану — висящие ноги слева, закрытая дверь справа. Задача: собрать mockup «дверь + ноги», прогнать через Сидрим, получить финальный кадр shot1_door_with_foot.
Пользователь с утра отметил, что ноги в прошлом наброске были мелкие:
«Мелкие очень ноги, можешь сам посмотреть»
Оказалось, shot1_door_closed.png по факту был 892×382 — агент перепутал числа и собрал mockup в неправильном масштабе. Переделал: кроп только ноги и ботинки из body_ref.png, без пола.

Mockup собрал: тело висящее слева, дверь справа, туловище уходит вверх за кадр. Шов вокруг ног видно — это фича, layout-инструкция.
Отправил в Seedream 5.0-lite через Evolink: shot1_door_closed.png + mockup как два рефа. 37 секунд, получили shot1_door_with_foot.png (первая версия):
✅ Ноги висят в левой части, туловище за кадром.
✅ Дверь справа, закрытая, ожидающая.
✅ Обои, тумбочка, паркет — всё на месте.
✅ Ботинки и брюки тёмные, как костюм в shot1.
Хичкоковский приём работает — зритель знает что в комнате, дверь становится зловещей.
«Не, херня, ботинки же должны быть немного под другим ракурсом»
body_ref.png снят анфас, а наш кадр на дверь — с 45° слева. Ноги должны быть в ¾, не прямо. Добавил в промпт инструкцию «three-quarter angle, not head-on» — Сидрим ожидаемо проигнорировал: 3D-вращение из текста он не умеет, копирует ориентацию из рефа.
Первый запуск с инструкцией "three-quarter angle" → shot1_door_with_foot_v2.png: ноги остались фронтальными (Сидрим не умеет 3D-вращение текстом):

Нужен новый pose-ref — «висящее тело под нужным углом». Text-to-image, без рефов:
body_ref_34angle.png — профиль сбоку-сзади, тело смотрит направо (к двери). «Не в ту сторону» — сказал пользователь. Зеркалю в PIL, бесплатно.body_ref_34front.png — попытка «фронт-¾». Носки смотрят вправо — снова не то.body_ref_34front_v2.png — добавил явное «STRICTLY VERTICAL — torso, arms, legs hang straight down under gravity as dead weight. Only the head is slumped forward at the neck». Сработало: тело вертикально, голова склонена, ¾ поворот к камере.Пользователь:
«Зачем флип? Без флипа и так влево носки»
Да, прав. Используем как есть. Кроплю ноги, собираю mockup, отправляю — и получаю shot1_door_with_foot_v3 / _v4. Но в кадре снова фронтальные ноги, не ¾.
После нескольких итераций, где Сидрим упорно выдавал старые ноги, пользователь сформулировал гипотезу:
«Тут важный момент, он берёт не абстрактные ноги фронтальные, а буквально те же самые, что в первом мокапе. Есть теория, что он видит то же название файла? И даёт тебе старый URL»
URL у Evolink действительно содержит имя файла:
https://files.evolink.ai/00551HC8TAUP94NFJS/images/png/shot1_door_with_foot_mockup.png
Когда shot1_door_with_foot_mockup.png перезаписывается локально новым содержимым и загружается — Evolink дедуплицирует по имени и возвращает старый URL с кешем первой версии. Сидрим получает самый первый mockup (с фронтальными ногами), а не свежий.
Фикс в seedream_gen.py — в имя при загрузке добавился SHA-256 первых 12 символов контента:
hash_prefix = hashlib.sha256(raw).hexdigest()[:12]
filename = f"{path.stem}_{hash_prefix}{path.suffix}"
Перезапуск — и ноги реально в ¾ углу, с правильной асимметрией (правая нога вперёд, левая чуть сзади). Критический баг найден пользователем. Все предыдущие итерации Сидрим получал первый mockup, как бы мы ни меняли локальный файл.
«Да, и ты кропнул немного ноги снизу. И сами ноги чуть левее надо»
«Чуть крупнее ноги, на 20%»
v6: ноги крупнее, на самом левом краю кадра, ботинки целиком в ¾ ракурсе. Но появилась новая проблема — вокруг ног Сидрим насыпал грязи на обоях, интерпретируя резкую кромку силуэта как «что-то испачкано».
Первая попытка фикса: кроп до самой подошвы без захвата серого пола из pose-ref'а (v7). Результат — «фон исчез, только концов ботинок тоже нет». v8 — золотая середина, но пользователь:
«Неправда, носки отрезало, а фон нет»
Агент понял: Сидрим ленив — тянет всё, что в mockup'е. Серый прямоугольник вокруг ног в коллаже → серый пятно в выводе.
Решение — силуэт по маске яркости (тёмные пиксели = ноги, светлые = прозрачно). Порог 110 → v9: силуэт на обоях Сталинки, серого прямоугольника нет. Но:
«Фон не отрезало (почему не отрезает?). И носки тоже отрезаны чуточку»
Увеличил порог, убрал блюр. Mockup выглядит чисто. Отправил — SSL-ошибка на Evolink (SSLEOFError), пять попыток подряд. Подождал минуту, прошло.
v10: модель насыпала визуального мусора вокруг ног. Промптом явно запретил debris, grime, dark smudges (v11) — чище, но артефакты всё ещё есть. Попробовал с мягкой маской и blur на краях (v12):
✅ Обои Сталинки за ногами без серых патчей.
⚠️ Тёмные пятна на стене выглядят как часть узора, но...«Не, грязь вокруг ног, просто она теперь размытая»
И вот фундаментальный вывод:
pip install rembg — библиотека alpha matting на U2Net. Через 30 секунд — 176MB модели скачано, установлено. Переписал сборку mockup'а: вместо порога по яркости — remove(legs_crop) (RGBA с чистой alpha), paste с alpha-маской.

v13: обои вокруг ног чистые — никакого halo, никакой грязи. Ноги висят в левой части, ботинки в ¾, дверь справа, локация M1 сохранена.
«Ну ладно, давай»
Второй утверждённый кадр. Ключевой урок: для composition mockup'ов — ровно U2Net-сегментация, пороговые маски не работают.
Пользователь сразу перевёл разговор на видео:
«А потом на основе этого кадра давай сгенерим Seedance-видео: сначала пару секунд ноги качаются, потом дверь выбивают с ноги и заходит бандит (на него есть карточка персонажа)»
Написал seedance_video.py под endpoint /v1/videos/generations, модель seedance-2.0-reference-to-video, до 9 рефов + промпт.
v1: 5с, 720p, 21:9. Рефы: shot1_door_with_foot.png + char_bandit.jpg. Без @image тегов — агент пропустил конвенцию из гайда.
v2: тот же запуск, но с явными тегами — @image1 as first frame, @image2 as character. По гайду Seedance роли рефов маркируются в тексте промпта.
«Вышло неплохо. Только теперь нужна версия с таймингом»
Пользователь прислал пример формата из MindStudio:
Global style: cinematic, 4K, cold colors.
[0s–4s]: Wide shot, static.
[4s–7s]: Medium shot, slow push in.
[7s–10s]: Close-up.
И ТЗ: 10 секунд, 3 секунды до выбивания двери, потом всё как было, взгляд подольше.
v3 с таймкодами. 458 сек генерации, 3.2MB.
«В 10с видео не сработала задержка перед началом, увы. И выбивание хуже чем на коротком. Пока v2 лучшее»
Агент переписал по гайду: префикс Style:/Duration:/Camera:, одна камерная директива (static locked-off), явные роли рефов, таймкоды в формате [MM:SS-MM:SS], negative prompts в конце.
v4, 10 сек, 346 сек генерации, 3.4MB:
Style: cinematic black and white 35mm film, grainy, deep blacks, high contrast.
Duration: 10s.
Camera: fixed, static locked-off wide shot throughout.
@Image1 as first frame and background environment.
@Image2 as character reference.
[00:00-00:03] dead silence, hanging legs sway slightly.
[00:03-00:05] door kicked open, slamming inward.
[00:05-00:10] bandit stands frozen, breathing heavily.
Avoid jitter. Avoid bent limbs. Avoid identity drift. Avoid camera movement.
Пользователь:
«3с пауза есть, но дверь выбивается недостаточно резко и после выбивания бандит слишком быстро входит. И у него появился пистолет в руках — это лишнее»
Правка: сжал удар двери до 1 секунды ([00:03-00:04]), дал 3 секунды на силуэт в проёме до первого шага, явно в промпте и в character-реф: «hands empty, no weapons, no gun».
Запуск → HTTP 402 insufficient_quota. Один 10-сек запрос стоит 135 кредитов Evolink, осталось 30.
«Варианты: пополнить Evolink; WaveSpeed ($1 триал, возможно потрачен); fast-режим (
seedance-2.0-fast-reference-to-video). Пока не пополнишь — ничего не прогоню. Промпт v5 в чате выше, скажешь когда — продолжу»
За полтора часа:
- shot1_door_with_foot.png — утверждён как canon, через rembg + composition mockup.
- Найден CDN-кэш баг Evolink: дедупликация по имени файла → новое содержимое не долетает до модели. Фикс: SHA-256 префикс в имени.
- rembg / U2Net вошёл в инструментарий — для чистой сегментации вместо пороговых масок.
- Написан seedance_video.py — reference-to-video пайплайн, до 9 рефов.
- Выведена форма Seedance промпта из гайда: Style/Duration/Camera + явные роли @image + таймкоды + negatives.
- v2 (5 сек, без таймкодов) — бенчмарк. v4 (10 сек, по гайду) даёт 3-сек паузу, но выбивание мягче.
- День остановлен на insufficient_quota, промпт v5 ждёт пополнения.