惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
Securelist
S
Security Affairs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 热门话题
博客园 - 三生石上(FineUI控件)
T
Threatpost
T
The Blog of Author Tim Ferriss
C
CERT Recently Published Vulnerability Notes
IT之家
IT之家
P
Palo Alto Networks Blog
Microsoft Azure Blog
Microsoft Azure Blog
Spread Privacy
Spread Privacy
Cyberwarzone
Cyberwarzone
腾讯CDC
L
LangChain Blog
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
T
Tor Project blog
AWS News Blog
AWS News Blog
T
Tenable Blog
NISL@THU
NISL@THU
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
美团技术团队
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Нечестный обзор ИИ-агентов. Кто действительно смог реализовать Depixelizing Pixel Art?
Вячеслав · 2026-06-25 · via Все публикации подряд на Хабре

Средний

9 мин

2

Лето, скоро отпуск — захотелось написать статью, которую просто кайф прочитать, и заодно попробовать что‑то новое. Для ИИ есть бенчмарки вроде HumanEval, где модель просят написать функцию на пару строк, есть задачи уровня «сделай мне todo‑лист на React». А что будет, если дать современным ИИ‑агентам по‑настоящему наукоёмкую задачу — реализовать алгоритм из статьи SIGGRAPH на Swift, без сторонних библиотек, — и потом честно сравнить, что получилось на выходе?

Для этого я взял алгоритм «Depixelizing Pixel Art» (Johannes Kopf, Dani Lischinski, SIGGRAPH 2011) — тот, который я когда‑то давно реализовывал на C++. Поставил одинаковую задачу реализовать на языке Swift разным агентам (Claude, Codex, Cursor, Cline, Antigravity, Kimi, Grok — на разных моделях). Условия просты — один промпт = одна реализация, без уточнений, указаний недочетов и итераций правок

Вот полный текст промпта:

Скрытый текст
## Задание

Ты выполняешь готовый план реализации алгоритма **«Depixelizing Pixel Art»**
(J. Kopf, D. Lischinski, SIGGRAPH 2011). Статья лежит рядом: `pixel.pdf`.
Следуй плану шаг за шагом, не пропускай шаги и не заменяй алгоритм другим.
Если шаг невозможно выполнить точно — реализуй указанное в нём упрощение и
зафиксируй отклонение в `solution/NOTES.md`.

### Шаг 0. Контракт

- Прочитай `config.txt`: `language` — язык реализации, `os` — целевая ОС
  (для кроссплатформенного языка игнорируется), `upscale` — целочисленный
  множитель увеличения.
- Весь код помести в папку `solution/`.
- Обработай **каждый** файл из `input/`. Результат сохрани в `output/`,
  заменив в имени суффикс `_input` на `_output`
  (`smw_boo_input.png` → `smw_boo_output.png`), формат PNG.
- Размер каждого результата: `(W*upscale) x (H*upscale)`, где `W x H` — размер оригинала.
- Запуск — одной командой; команду опиши в `solution/README.md`.

### Шаг 1. Подготовка проекта

- Модули: конфиг, ввод/вывод изображений, граф похожести, эвристики,
  перестройка ячеек, сплайны, оптимизация, рендеринг, точка входа.
- Зависимости — только базовые: чтение/запись PNG и математика.
  **Запрещены** готовые реализации апскейла/векторизации (hqx, xBR, Scale2x/ScaleNx,
  EPX, 2xSaI, potrace, функции масштабирования из opencv/PIL как итоговый результат).
- Загрузка изображения в RGB. Если есть альфа-канал: полностью прозрачные
  пиксели считай отдельным самостоятельным «цветом».

### Шаг 2. Граф похожести (статья, §3.2, начало)

- Узел на каждый пиксель, рёбра ко всем 8 соседям (включая диагонали).
- Переведи цвета в YUV. Ребро удаляется, если цвета «непохожи»:
  `|dY| > 48/255` **или** `|dU| > 7/255` **или** `|dV| > 6/255`.
- Самопроверка: на однотонной картинке граф остаётся полным;
  на шахматной доске 1x1 остаются только диагональные рёбра внутри каждого цвета.

### Шаг 3. Разрешение пересекающихся диагоналей (§3.2)

Для каждого блока 2x2, в котором присутствуют обе диагонали:

- Если блок полностью связан (все четыре пикселя взаимно похожи) —
  удали обе диагонали: это плоско закрашенная область.
- Иначе подсчитай голоса трёх эвристик за каждую из диагоналей:
  - **Кривые.** Если диагональ — часть цепочки узлов валентности 2, она
    принадлежит «кривой». Вычисли длины двух кривых, проходящих через две
    диагонали; голос за более длинную, вес = разность длин.
  - **Разреженные пиксели.** В окне 8x8 с центром в блоке сравни размеры
    компонент связности, к которым подключены концы каждой диагонали.
    Голос за диагональ компоненты **меньшего** размера (разреженный цвет —
    передний план), вес = разность размеров компонент.
  - **Острова.** Если у одного из концов диагонали валентность 1, её удаление
    создаст одинокий пиксель-«остров». Голос за сохранение этой диагонали
    с фиксированным весом **5**.
- Оставь диагональ с большей суммой весов; при равенстве удали обе.
- Результат: планарный граф.
- Самопроверка: визуализируй граф для `smw_boo_input.png` и сравни со схемой
  Figure 3(c) статьи.

### Шаг 4. Перестройка пиксельных ячеек (§3.2, конец)

- Построй **упрощённую обобщённую диаграмму Вороного**: проход окном 3x3 по
  графу, подстановка готовых шаблонов формы ячеек (формы перечислимы, потому
  что граф планарен).
- Координаты узлов ячеек квантованы к **четвертям пикселя**.
- Схлопни узлы валентности 2 для упрощения диаграммы.
- Самопроверка: похожие пиксели, соседние по диагонали, теперь делят общее
  ребро ячейки; сравни с Figure 3(d).

### Шаг 5. Извлечение сплайнов (§3.3)

- Ребро ячейки **видимое**, если цвета по его сторонам различны
  (критерий из шага 2).
- Последовательности видимых рёбер, проходящие через узлы валентности 2
  (по видимым рёбрам), преврати в **квадратичные B-сплайны**;
  контрольные точки — узлы ячеек.
- T-стыки (три видимых ребра в одном узле): классифицируй каждое ребро как
  «теневое» (shading), если YUV-расстояние цветов с двух сторон `<= 100/255`,
  иначе «контурное». Если теневое ровно одно — соедини два контурных в один
  сплайн. Иначе соедини пару рёбер с углом, ближайшим к 180 градусам.
  Конец третьей кривой спроецируй на продолжающуюся кривую (B-сплайн не
  интерполирует контрольные точки).
- Самопроверка: отрисуй сплайны поверх сетки, сравни с Figure 3(e).

### Шаг 6. Оптимизация кривых (§3.4)

- Минимизируй сумму поузловых энергий `E = Es + Ep`:
  - `Es` — гладкость: интеграл `|kappa| ds` (модуль кривизны) по участку кривой,
    на который влияет узел; считай численно, сэмплированием с фиксированным шагом.
  - `Ep` — позиция: `||p - p_hat||^4`, где `p_hat` — исходное положение узла
    (четвёртая степень: свободно в малом радиусе, резкий штраф дальше).
- Релаксация: случайный обход узлов; для каждого пробуй несколько случайных
  смещений в малом радиусе, оставляй лучшее. Несколько итераций.
  **Зафиксируй seed** — результат должен быть воспроизводим.
- Углы: найди в перестроенном графе ячеек шаблоны углов (Figure 7, плюс их
  повороты и отражения) и исключи участки кривой между узлами шаблона из
  интеграла гладкости — намеренно острые углы сглаживать нельзя.
- Допустимое упрощение: вместо гармонических отображений для узлов, не
  лежащих на сплайнах, — локальное усреднение соседей или пропуск
  перепозиционирования (выбор зафиксируй в NOTES.md).
- Самопроверка: «лесенка» на длинных дугах исчезает, сравни с Figure 6(b)→(c).

### Шаг 7. Рендеринг (§3.5)

- Растеризуй результат в `(W*upscale) x (H*upscale)`.
- Эталонный способ: цвет точки — взвешенное среднее цветов ячеек с усечёнными
  гауссовыми влияниями (`sigma = 1`, радиус 2 ячейки), размещёнными в центроидах
  ячеек; влияние **не распространяется через контурные сплайны** (edge-aware).
- Допустимое упрощение: залить каждую область, ограниченную сплайнами, цветом
  её ячеек, с антиалиасингом по границам (суперсэмплинг не менее 4x).
- **Запрещено**: nearest neighbor / bilinear / bicubic как итоговый способ
  масштабирования.

### Шаг 8. Прогон и самопроверка

- Прогони все файлы из `input/`.
- Проверь: все выходные файлы существуют, размеры верны, повторный запуск
  даёт идентичный результат.
- Сравни выходы с эталонами в статье: `smw_dolphin` — Figure 1,
  `smw_boo` — Figure 3(f), `invaders_03` — Figure 9, `sma_toad` — Figure 9,
  `win31_keyboard` — Figure 9, `smw2_yoshi_*` — Figure 9 (Yoshi).
- Все отклонения от плана зафиксируй в `solution/NOTES.md`.

### Критерии готовности

- [ ] В `output/` лежит результат для каждого входного файла, размеры `(W,H) * upscale`.
- [ ] Контуры гладкие, без «лесенки»; тонкие линии не разорваны; одиночные
      пиксели (глаза персонажей) не потеряны.
- [ ] Язык реализации соответствует `config.txt`; запуск одной командой по `solution/README.md`.
- [ ] Повторный запуск даёт байт-в-байт тот же результат.

Оценивать результаты я «пригласил» тоже три разных ИИ — Claude, Codex и Antigravity (Gemini).

Что за задача и почему она сложная

«Depixelizing Pixel Art» — это алгоритм, который восстанавливает намерение художника: где у спрайта непрерывный контур, где тонкая диагональная линия в один пиксель, а где одиночная точка-блик, которую нельзя терять.

Figure 3 из статьи Kopf & Lischinski: (a) вход, (b) граф похожести, (c) граф после разрешения диагоналей, (d) перестроенные ячейки, (e) сплайны, (f) финальный рендер.

Figure 3 из статьи Kopf & Lischinski: (a) вход, (b) граф похожести, (c) граф после разрешения диагоналей, (d) перестроенные ячейки, (e) сплайны, (f) финальный рендер.

Оценка на верность производилась по этим пяти этапам:

  1. Граф похожести. 8-связность, сравнение пикселей в пространстве YUV с порогами из статьи (48/255 по яркости, 7/255 и 6/255 по цветности).

  2. Разрешение диагоналей. В полносвязном блоке 2×2 надо выбрать одну из двух диагоналей по трём эвристикам (продолжение кривых, разреженные пиксели с окном 8×8, «острова» с весом 5); ничья — удалить обе.

  3. Перестройка ячеек. Упрощённая обобщённая диаграмма Вороного, узлы квантуются к четвертям пикселя.

  4. Сплайны. Видимые рёбра превращаются в квадратичные B-сплайны; отдельно обрабатываются T-стыки (теневые/контурные рёбра, порог 100/255, угол ближе к 180°).

  5. Оптимизация кривых. Минимизация энергии «гладкость + позиция», при этом узловые шаблоны углов из сглаживания исключаются (углы должны остаться углами).

Входной спрайт smw_boo, 18×18 пикселей.

Входной спрайт smw_boo, 18×18 пикселей.

Вот как выглядит вход — 18×18 пикселей, на которые без увеличения и не взглянешь:

А вот эталон, к которому все стремились, — Figure 3(f) из статьи: гладкий контур, два глаза (правый — характерный «крючок»), намеренно «волнистый» розовый рот и мягкая тень.

А судьи кто? Главный тест всего бенчмарка

Оценка шла по 100-балльной шкале, пять категорий: контракт (25), верность алгоритму (30), визуальное качество (20), инженерное качество (15), процесс (10).

Но самое полезное в проверке оказалось почти тривиальным. Это тест на даунсэмплинг:

Взять выход 288×288, усреднить его обратно до 18×18 методом BOX, сравнить с оригиналом. Если средняя дельта около нуля — значит каждый блок 16×16 в выходе постоянен и выход практически полностью состоит из однородных блоков. А это и есть обычный nearest-neighbour, то есть депикселизации не было вообще.

import numpy as np
from PIL import Image

up = 16
src = Image.open("input/smw_boo_input.png").convert("RGB")
out = Image.open("output/smw_boo_output.png").convert("RGB")
ds  = out.resize(src.size, Image.BOX)          # свернуть выход обратно к 18×18
d   = np.abs(np.float32(src) - np.float32(ds)).mean()
print(f"средняя дельта: {d:.1f}")             # 10–25 — норма; 0.0 — это resize

Десять строк Python — и стало видно то, что не видно при беглом просмотре «красивых» исходников. Нормальное сглаживание «съедает» углы и даёт дельту в районе 10–25. Ноль означает, что модель просто растянула картинку.

Модель / агент

Размер

Δ к входу

Пикселей ≠ NN

Что это значит

Claude Fable 5

288²

24.2

39.98%

норма, есть сглаживание

Codex 5.5

288²

40.2 → 21.3 при v-flip

47.17%

обработка есть, но кадр перевёрнут

Cursor Auto

288²

27.8

25.77%

обработка есть, но рендер битый

Kimi Code 2.7

288²

42.7

31.24%

обработка есть (рендер по Вороному) сплайны в рендере не участвуют

Claude Sonnet 4.6

288²

19.0

13.55%

норма, есть сглаживание

Antigravity (Gemini 3.5 Flash)

288²

0.0

0.00%

чистый nearest ×16

Cline + DeepSeek v4 Pro

288²

0.0

0.00%

чистый nearest ×16

Cline + Qwen 3.7 Max

288²

0.0

0.00%

nearest ×16

Grok

4608²

0.0

1.85%

nearest ×256 — и неверный размер

Все агенты Интересные особенности и впечатления:

  • Fable — идеальный результат (его разберем далее)

  • Codex честно выполнил все инструкции и получил перевернутое изображение (помню, когда я реализовывал этот алгоритм, также перевернутое изображение)

  • Kimi Code 2.7 высадил 5-часовой лимит за один промпт, застрял в петле из постоянных улучшений

  • Antigravity — отправился гуглить реализацию на питоне, открыл браузер и начал серфить

  • DeepSeek v4 Pro — прогон стоил всего $0,08 на токены

Общий зачёт (Оценка ИИ)

Ниже — сводка. Поскольку оценивали три разных ИИ-судьи (Claude, Codex, Antigravity/Gemini), я привожу баллы каждого: расхождения сами по себе любопытны (об этом — в разделе про судей).

Модель / агент

Claude

Codex

Gemini

Вердикт

🥇

Claude Fable 5

97

97

100

Отлично

🥈

Sonnet 4.6

81

85

88

Хорошо

🥉

Codex 5.5

85

83

84

Хорошо (кадр перевёрнут)

4

Kimi Code 2.7

76

78

80

Хорошо — зачтено (рендер по Вороному)

5

Cursor Auto

68

66

81.5

Удовлетворительно

Antigravity (Gemini 3.5 Flash)

-

44

47

Не зачтено (nearest)

Cline + DeepSeek v4 Pro

-

53

47

Не зачтено (nearest)

Cline + Qwen 3.7 Max

-

40

42

Не зачтено (nearest)

Grok

-

38

19

Не зачтено (nearest ×256)

А вот как это выглядит глазами — та самая картинка, ради которой стоит читать статью. Восемь результатов одного и того же призрака Boo:

Kimi Code 2.7

Kimi Code 2.7

Разница видна невооружённым глазом

Мой разбор призёров

Claude Fable — Однозначный лидер

Единственная реализация, которая одновременно и хорошо выглядит, и почти буквально покрывает все пять этапов алгоритма. Корректные YUV-пороги, все три эвристики диагоналей, четверть-пиксельные ячейки, квадратичные B-сплайны, полная логика T-стыков, шаблоны углов из Figure 7

Отдельно порадовала инженерия: фиксированный seed (SplitMix64), собственный PNG-кодер ради байт-в-байт воспроизводимости (два запуска → идентичный SHA-256), многопоточный рендер и режим --debug, выгружающий промежуточные этапы. Эти выгрузки можно прямо сопоставить со статьёй:

2. Перестроенные ячейки Вороного, узлы квантованы к ¼ пикселя (Figure 3d).

3. Сплайны на видимых рёбрах (Figure 3e): «лесенка» становится кривыми.

4. Финальный рендер — практически эталон.

Codex — моё второе место

По исходникам это одна из самых полных реализаций: B1–B5 разнесены по модулям, есть self-test графа, T-стыки с проекцией по 32 точкам сплайна, защита тонких компонент, гауссов рендер и три диагностических картинки.

финальный PNG перевёрнут по вертикали (рассогласование «начала отсчёта Y» между чтением и записью буфера) — поэтому все «ИИ-судьи» снизили баллы

Claude Sonnet

Результат: контур получился заметно гранёным — сплайны не убрали «лесенку», а превратили её в многоугольные фасетки; глаза стали ромбовидными, рот потерял волну. Плюс из-за ошибок округления на границах полигонов появились микро-зазоры (полупрозрачные пиксели). Цветовая верность при этом хорошая.

Kimi Code 2.7 —маньяк перфекционист

Мой приз зрительских симпатий, модель действительно старалась, когда я увидел, что потрачено 20% 5-часового лимита — я улыбнулся, 50% — я уже ждал "ошеломляющий" результат, и дальше с упоением наблюдал как заполняется 100%

Результат оказался далёк от идеала, но именно Kimi показал самое интересное агентное поведение: вместо решения задачи он начал бесконечно улучшать собственное решение и в итоге сжёг весь доступный лимит.

Выводы

  1. Claude Fable — чистый победитель. Единственный результат уровня эталона статьи, ещё и с диагностикой этапов и байт-в-байт воспроизводимостью. Пример когда модель понимает задачу, а не имитирует понимание

  2. Главный вывод: для AI-агентов нужен численный критерий. Это вероятностные системы, и статическое ревью «красивого кода» не показывает, работает ли алгоритм на самом деле.

  3. Много кода — не всегда работающий результат. Antigravity и Cline+DeepSeek написали полноценный каркас алгоритма с графами и сплайнами — и из-за одной логической ошибки (схлопывание коллинеарных вершин) всё выродилось в просто ресайз. Сквозные smoke-тесты на синтетике обязательны прямо в цикле генерации.

  4. LLM-судьи воспроизводимы ровно настолько, насколько объективны критерии. По числам — консенсус, по «вкусу» — разброс в 15 баллов. Это работающий рецепт для любого, кто строит автоматическую оценку на LLM.

Если кто-то захочет прогнать этот же тест на Kotlin, Rust, C#, Zig или любом другом языке — вот репозиторий с полным промптом, исходными артефактами и структурой, которую я использовал для эксперимента.