QR-код из фотографии: как дизеринг превращает снимок в сканируемый код
Большинство «художественных» QR-кодов устроены просто: берут картинку и ставят поверх неё код. QR-код с дизерингом — другое дело: изображение здесь одно, и код является частью его зерна. Ниже разобрано, что коду на самом деле необходимо, как работает рассеивание ошибки, какая единственная поправка позволяет коду и снимку делить один квадрат, какие две настройки решают всё и какими тремя способами эта техника ломается.
Начните с того, что коду действительно нужно
QR-код — это сетка модулей, маленьких квадратов, каждый из которых либо тёмный, либо светлый. Часть из них структурная и не может сдвинуться: три больших квадрата по углам, по которым сканер находит код и понимает его ориентацию, тонкая чередующаяся линия между ними, маленькие выравнивающие квадраты и полосы, кодирующие формат и версию. Всё остальное — данные плюс байты коррекции ошибок. Именно коррекция ошибок и есть та щель, в которую пролезает картинка. На высшем уровне можно разрушить примерно 30% кода, и читатель всё равно восстановит содержимое. То есть код не обязан быть безупречным. Он обязан быть безупречным там, где это важно, и достаточно близким к правильному во всём остальном. Держите это различие в голове, потому что на нём стоит вся техника: структурные модули считаются неприкосновенными, а область данных — тем, что должно быть верным лишь в среднем, на том масштабе, каким её видит камера. Любой генератор, помещающий картинку в код, тратит часть этих 30%; вопрос только в том, насколько аккуратно он их тратит и заметен ли глазу этот расход.
Дизеринг в одном абзаце
Дизеринг Флойда—Стейнберга превращает полутоновую фотографию в чистые чёрное и белое. Пиксели обходятся по порядку. Для каждого выбирается то из двух значений, которое ближе, а затем сделанная ошибка — разница между нужным серым и напечатанным значением — распределяется по ещё не посещённым соседям с весами 7/16 для пикселя справа и 3/16, 5/16 и 1/16 для трёх пикселей снизу. Каждый из этих соседей, когда доходит его очередь, стартует уже со слегка поправленного значения, поэтому ошибка никогда не теряется — она только перемещается. Картинка выходит полем крапа, локальная плотность которого повторяет исходную яркость: средне-серая область становится примерно наполовину чёрными точками, светлое место — редкими точками по белому, тень — редкими белыми точками по чёрному. С расстояния глаз усредняет крап обратно в тон, поэтому изображение с дизерингом читается как фотография, хотя каждый его пиксель имеет один из двух цветов. Ровно то же усреднение делает камера, когда смотрит на небольшой отпечаток, — и именно на нём техника и играет.
Сам приём
Код рисуется с фиксированным числом пикселей на модуль — SWAPQR использует девять, то есть каждый модуль это блок 9×9. Затем фотография подвергается дизерингу по всему квадрату, с одной поправкой: там, где код требует конкретного значения, порог не применяется. Пиксель принудительно ставится в то, что нужно коду, измеряется вызванная этим ошибка и рассеивается точно так же, как любая другая. Последнее предложение и есть вся идея. Принудительные точки не нарисованы поверх картинки — за них платят окружающие пиксели, которые сдвигаются, компенсируя. Они попадают внутрь той же зернистой текстуры, что и всё прочее, и глазу нечем отделить их от неё. Структурные модули — поисковые узоры, синхрополоса, выравнивание, формат — рисуются сплошными и не трогаются вовсе, потому что сканер обязан сначала найти код, и только потом коррекция ошибок сможет ему помочь. Описанный подход следует работе Эндрю Т., опубликовавшего разбор генерации QR-кодов с дизерингом в 2026 году; реализация SWAPQR написана по описанию, а не портирована из его исходников.
Две ручки, которые решают всё
Первая — какая доля каждого модуля ставится принудительно. Заставьте только центральный пиксель, и картинка почти не пострадает, но код станет хрупким: один пиксель на модуль должен пережить каждое пересэмплирование между принтером и матрицей камеры. Заставьте блок 7×7 из девяти на девять — код станет устойчивым, а картинка бледной, потому что большая часть каждого модуля теперь сплошная. SWAPQR выносит это прямо в интерфейс как баланс «Фото ↔ Код» с положениями «Фото», «Середина» и «Код», так что компромисс становится вашим выбором, а не константой, спрятанной в генераторе. Вторая ручка — насколько сильно подталкивается каждый модуль. Перед дизерингом все пиксели тёмного модуля чуть затемняются, а светлого — чуть осветляются. Картинка сохраняет детали, но средний тон модуля кренится в нужную сторону, а это ровно то, что видит камера, глядя на небольшой отпечаток с другого конца комнаты. Вдвоём эти настройки решают, получится ли фотография со слабым кодом внутри или код со слабой фотографией, и где именно на этой прямой должен стоять конкретный отпечаток, зависит от того, как его будут сканировать.
Где ломается и стоит ли оно того
Три режима отказа, все настоящие и ни один не загадочный. Плоская фотография: равномерно освещённое однотонное изображение превращается дизерингом в правильную шахматку — ровно ту текстуру, что конкурирует с кодом, поэтому снимку нужен подлинный диапазон от света к тени, и студия предупреждает, когда его нет. Длинная ссылка: больше символов означает больше модулей в том же квадрате, значит каждому модулю достаётся меньше картинки; после примерно восьмой версии — 49×49 модулей — фотографии становится негде жить, и лечится это коротким адресом перенаправления. И крайне близкое сканирование на части читателей — настолько неочевидный случай, что в этом руководстве ему отведена отдельная статья. Художественный код всегда обменивает надёжность на вид. Он оправдан там, где код является частью оформления и его собираются сканировать намеренно: афиша, конверт пластинки, упаковка, визитка. Ему не место на табличке аварийного выхода. В любом случае проверьте напечатанный образец настоящим телефоном, прежде чем заказывать тысячу штук; студия говорит то же самое рядом с кнопкой экспорта и говорит это всерьёз.
| Часть кода | Как обрабатывается в коде с дизерингом | Почему |
|---|---|---|
| Поисковые узоры (три угловых квадрата) | Рисуются сплошными, дизеринг не применяется | Сканер обязан сначала найти код и определить его ориентацию |
| Синхрополоса, выравнивающие квадраты, полосы формата и версии | Рисуются сплошными, дизеринг не применяется | Структура; коррекция ошибок не спасёт код, который не найден |
| Модули данных и коррекции ошибок | Центральный блок модуля ставится принудительно, остальное несёт фотографию | Камера на дистанции сканирования видит средний тон модуля |
| Пиксели вокруг принудительного блока | Сдвигаются рассеиванием ошибки в качестве компенсации | Держат правильным локальное среднее, и принудительные точки прячутся в крапе |
| Зона тишины | Светлая, не трогается | По пустому полю сканер находит границу кода |
Часто задаваемые вопросы
Фотография просто подложена под код?
Нет. Код и снимок получаются за один проход: картинка проходит дизеринг, и там, где коду нужно значение, пиксель ставится принудительно, а его ошибка рассеивается по соседям. Изображение одно, а не слой поверх слоя.
Почему девять пикселей на модуль?
Так каждому модулю достаётся блок 9×9 — достаточно большой, чтобы вместить принудительное ядро изменяемого размера и всё же оставить пиксели картинке. Насколько велика принудительная часть блока, решает настройка баланса.
Сканируется ли он обычными телефонами?
Да, для тех объёмов данных и изображений, на которые он рассчитан; каждая сборка проверяется двумя независимыми декодерами на семи смоделированных дистанциях. Это всё равно обмен надёжности на вид, поэтому напечатайте один образец и проверьте своим телефоном.
Почему длинная ссылка портит картинку?
Более длинный текст поднимает версию, значит модулей становится больше и они мельче в том же квадрате. Каждому модулю остаётся меньше пикселей под фотографию, а после примерно 49×49 модулей картинка исчезает. Используйте короткую ссылку перенаправления.
Напечатайте квадрат один раз. Куда он ведёт — решите потом. SWAPQR создаёт статические QR-коды бесплатно, со всеми вариантами оформления, без аккаунта и без водяного знака. Платный план делает код динамическим: напечатанный квадрат остаётся прежним, а вы меняете его адрес назначения и видите, как часто его сканировали, по дням и по устройствам.
Создать QR-код в браузере