Решили: grayscale-исходники UI проходят через scripts/prep_ui_textures.py и попадают в Art/UI Textures уже годными: поле вырезано в альфу, картинка обесцвечена принудительно, длинная сторона ужата до 1024, обе стороны приведены к кратности четырём. Исходники остаются нетронутыми — обработку можно перегнать заново с другими порогами (--force).

Почему: три дефекта исходников (нет альфы, разброс каналов до 19, размер не кратен 4) чинятся одинаково у всех сорока семи файлов, и делать это руками — работа на день с гарантией расхождения между файлами. Одно число вместо таблицы размеров (LONG_SIDE = 1024) выбрано по тому же правилу, по которому у пластины два числа формы на всю игру: таблица размеров потребовала бы пересчёта на каждую новую пачку.

Грабли: три захода конвейера подряд ушли на дефекты, которых не видно в числах.

  • Заливка фона не работает там, где фона нет. Баннеры сначала попали в вырез вместе с рамками, и от полотнища оставалось 2-3% кадра. Замер объяснил: тон угла 41-66 против тона тела 41-83 — тёмный ободок у баннера это ВИНЬЕТКА ткани, а не поле. Заливка искала границу, которой нет, и растекалась по всему полотну. Признак этого класса ошибки — доля фигуры, ушедшая в единицы процентов; она и вывела на диагноз.
  • Порог по яркости для выреза не годится, нужна заливка от краёв. На рельефе есть блики светлее 200, и порог сделал бы их полупрозрачными — дырами посреди фигуры. Заливка идёт снаружи и внутрь не попадает.
  • Медиана не берёт шахматку 50/50. Нарисованный генератором «прозрачный фон» уезжает у маски прямо в альфу и виден клетками. Медиана размером с клетку кажется правильным инструментом, но у шахматки светлых и тёмных пикселей поровну — медиана выбирает одну из двух групп целиком, и узор остаётся. Пережил и медиану 9, и медиану 21. Гасит его СРЕДНЕЕ по окну ровно в период (BoxBlur).
  • Автокорреляция по сырому сигналу period не находит. Маска — гладкий градиент, поэтому максимум корреляции всегда на наименьшем лаге, и детект уверенно возвращал 6 вместо истинного 21. Лечится переходом на РАЗНОСТИ соседних пикселей: они снимают тренд, и у периодического узора остаётся честный пик. После правки детект отобрал ровно те четыре маски, где клетки были видны глазами.
  • «Периодика 99% от энергии» — не замер, а артефакт метрики. Проверяя результат, я получила 99% на всех масках и чуть не приняла это за провал: у гладкого сигнала автокорреляция близка к единице по определению. Числу без нормировки на соседние лаги верить было нельзя.

Владелец правды: scripts/prep_ui_textures.py, таблица — Reference - UI Texture Inventory.