Код с паролем от Wi-Fi на холодильнике рано или поздно обрастает кофе, брызгами и потёртостями. Считается, что самый стойкий QR-код восстанавливает около 30% содержимого, — так мы и писали сами. Мы испортили больше тысячи кодов. Пятно в центре такой код правда переносит почти на пятую часть площади, а 69 точек вразброс — всего 4% — ломают его наглухо.

📋 Что мы намеряли

Мы сделали QR-коды одной и той же ссылки на всех четырёх уровнях защиты и портили их тремя способами. Одно сплошное пятно в центре код уровня H выдержал на 19% площади. Двадцать мелких брызг — на 9%. А отдельные точки вразброс сломали его уже на 4%: хватило 69 квадратиков из 1681. Закрытый угловой квадрат убивал код любого уровня сразу. В конце — как сделать свой QR-код для гостей или визитки, чтобы он пережил наклейку, кофе и потёртости.

Как мы проверяли

Взяли ссылку на ту самую инструкцию — 49 символов — и сделали из неё четыре QR-кода, по одному на каждый уровень защиты. Уровни обозначаются буквами: L восстанавливает 7%, M — 15%, Q — 25%, H — 30%. Это цифры разработчика стандарта, японской компании Denso Wave, которая представила QR-код в 1994 году.

Первое, что бросилось в глаза: коды получились разного размера. Ссылка одна, а квадратиков становится больше. На уровне L код собран из сетки 29×29, на уровне H — уже 41×41. Защита занимает место: чем больше запасных данных, тем плотнее рисунок.

Четыре QR-кода одной и той же ссылки на уровнях защиты L, M, Q и H: от сетки 29×29 до 41×41 Одна ссылка, четыре уровня защиты. Чем выше уровень, тем мельче рисунок. Замер «Техпросто»

Дальше каждый код портили тремя способами:

  • одно пятно — белый квадрат в центре, как наклейка или логотип; его увеличивали, пока код не переставал читаться;
  • брызги — от 2 до 30 мелких белых клякс размером 3×3 квадратика, как грязь и капли;
  • точки вразброс — меняли цвет отдельных квадратиков в случайных местах, белый на чёрный и наоборот, как бывает при царапинах и износе бумаги; угловые квадраты не трогали.

Случайные повреждения повторяли по 20 раз с разной раскладкой, иначе одна удачная попытка дала бы ложную картину. Коды читали три программы-сканера: открытая библиотека ZXing, на основе которой сделаны многие сканеры штрихкодов, и два распознавателя из библиотеки OpenCV. Часть опытов прогнали ещё и через имитацию телефонного снимка: наклон, размытие, шум и сжатие JPEG. Считал всё обычный мини-ПК — тот же, на котором мы проверяли, за сколько подбирается пароль. Тысяча с лишним испорченных кодов заняла у него меньше минуты.

Оговоримся сразу: настоящим телефоном мы не сканировали ни одного кода, всё считали распознаватели на картинках. Как поведёт себя дешёвая камера на мятой печати, мы не знаем. Поэтому наши пределы — это потолок, в жизни запас стоит брать меньше.

Пятно: код переносит заметно больше, чем кажется

Белый квадрат посередине — это логотип или наклейка. Результаты совпали у всех трёх распознавателей до процента:

  • уровень L читается с пятном на 4,3% площади, а на 5,8% уже нет;
  • уровень M — 9,2%, ломается на 11%;
  • уровень Q — 16,4%, ломается на 18,7%;
  • уровень H — 19,3%, ломается на 21,5%.

Пятая часть кода под заплаткой — и телефон открывает ссылку. Впечатляет, но до обещанных 30% не дотягивает. Дело в том, что стандарт считает свои 30% не от площади, а от байтов данных, — об этом ниже.

На имитации телефонного снимка пределы почти не изменились. Код Q прочитался с пятном на 16,4% во всех десяти попытках, а на 18,7% — ни разу. Разница вышла только на пределе: код H с пятном на 19,3% ZXing прочитал 10 раз из 10, а распознаватели OpenCV — 6 и 4. Разные телефоны в таком случае, скорее всего, тоже поведут себя по-разному.

Точки вразброс: 69 квадратиков — и всё

Здесь картина перевернулась. Код уровня H, который пережил пятно на 324 квадратика, сдался, когда мы перекрасили всего 69 отдельных точек. Это 4% площади. При 35 испорченных точках он читался всегда, при 58 — в 8 попытках из 20, при 69 — ни разу.

С другими уровнями ещё хуже. Код L уверенно читается при 4 испорченных точках из 841, а при 11 — ни разу. Это чуть больше одного процента площади.

Брызги легли посередине. Код H выдержал двадцать клякс 3×3 во всех попытках. Кляксы ложились случайно и местами перекрывались, так что закрыли они 9,4% площади. На тридцати кляксах код читался уже через раз.

График: сколько площади QR-кода можно испортить пятном, брызгами и отдельными точками на уровнях L, M, Q и H Серые столбцы — обещание стандарта, цветные — что вышло у нас. Чем мельче и разбросаннее повреждение, тем раньше сдаётся код. Замер «Техпросто»

🔢 Сколько можно испортить, чтобы код читался 20 раз из 20
Уровень Обещано стандартом Одно пятно Брызги 3×3 Точки вразброс
L 7% 4,3% 2,1% 0,5%
M 15% 9,2% 4,6% 1%
Q 25% 16,4% 6% 1,2%
H 30% 19,3% 9,4% 2,1%

Стандарт считает долю испорченных байтов, наши столбцы — долю площади кода. Распознаватели ZXing и OpenCV, цифровые картинки.

Три QR-кода уровня H: с пятном на 19% площади читается, с 69 точками вразброс и с закрытым угловым квадратом — нет Левый код можно отсканировать прямо с экрана — он откроет нашу инструкцию. Средний и правый ни один из трёх распознавателей не прочитал. Замер «Техпросто»

Почему пятно безопаснее россыпи точек

Разгадка в том, как QR-код хранит данные. Данные он пишет не по одному квадратику, а байтами — порциями по восемь квадратиков, которые лежат рядом. И исправляет тоже байтами. Защита работает по коду Рида — Соломона, тому же математическому способу, что спасает музыку на поцарапанных CD. По словам Denso Wave, изначально этот способ защищал связь со спутниками и космическими зондами от помех.

Код Рида — Соломона не различает, сколько квадратиков в байте испорчено: один или все восемь. Байт либо цел, либо его надо чинить. Отсюда и вся разница:

  • пятно в 324 квадратика задело 48 байтов: квадратики внутри пятна принадлежат одним и тем же байтам;
  • 69 точек вразброс задели в среднем 58 байтов — почти каждая точка испортила свой.

Мы посчитали это по карте кода: какой квадратик к какому байту относится. У нашего кода 172 байта. Они разложены по четырём коробкам по 43 штуки, и в каждой коробке чинится не больше 14. Пятно на 19% испортило в коробках 14, 13, 9 и 12 байтов — ровно на пределе. Стоило увеличить его на один ряд квадратиков, и в первой коробке испорченных стало 15: код перестал читаться. Россыпь из 69 точек переполняет хотя бы одну коробку почти всегда, в наших 20 попытках — каждый раз.

Почему 14, а не больше? Распознаватель не знает, где именно ошибка: перекрашенный квадратик выглядит таким же чистым, как соседи. Поэтому на каждый испорченный байт уходят два запасных: один нужен, чтобы найти место ошибки, второй — чтобы восстановить значение. Это правило Denso Wave приводит у себя на сайте и там же объясняет, что код Рида — Соломона исправляет на уровне байтов и хорошо справляется со сплошными повреждениями.

Отсюда же и разница между обещанными 30% и нашими 19%. Пятно на 19% площади испортило 48 байтов из 172 — это 28%, почти ровно обещание стандарта. Просто пятно режет байты по краям, и каждый обрезок считается целым испорченным байтом. Площадь и байты — разные мерки: в процентах площади запас всегда выходит меньше, чем обещает стандарт.

Угловой квадрат закрывать нельзя ни на каком уровне

Последний опыт оказался самым жёстким. Мы закрыли белым один из трёх угловых квадратов — от 3 до 6% площади, смотря по уровню. Код не прочитал ни один распознаватель, на всех четырёх уровнях. Закрыли половину квадрата — то же самое.

Код Рида — Соломона здесь бессилен, потому что угловые квадраты не данные. По ним сканер находит код в кадре и понимает, как тот повёрнут. Команда Масахиро Хары, создателя QR-кода, подбирала для этих меток такое соотношение чёрного и белого, которое реже всего встречается на печатной продукции — листовках, журналах, коробках. Так сканер не спутает метку с картинкой вокруг. Положение и поворот кода он определяет по трём квадратам сразу: без одного он не может собрать код и до исправления ошибок не доходит.

⚠️ Логотип и наклейку — только в центр

Наш опыт показал: закрытый угловой квадрат ломает код любого уровня, даже если закрыта половина. Поэтому логотип ставят только в середину, а наклейку, скотч или ценник клеят мимо углов. Это касается и кода, который вы распечатали для гостей: если его приклеили так, что край загнулся на угловой квадрат, — переклейте.

📲 Проверяем технику на практике

Такие опыты мы ставим регулярно: сами портим технику и считаем, где предел. Подпишитесь на «Техпросто» в MAX, Telegram или Дзене, и новые эксперименты будут приходить сами.

Что это значит для вашего собственного QR-кода

Сделать свой QR-код можно за минуту в любом бесплатном генераторе, а сетью Wi-Fi телефон умеет делиться через QR и сам — как это сделать, мы рассказывали в статье как узнать пароль от Wi-Fi. Если код пойдёт в печать, вот что стоит учесть.

💡 Как сделать QR-код, который не подведёт
  1. В генераторе найдите пункт «Коррекция ошибок» (по-английски Error correction) и выберите H, если код будет с логотипом, на улице или там, где его трогают руками. Для кода на экране или в чистом месте хватит M — у него меньше квадратиков, они крупнее, и такой код читается издалека. Если такого пункта в генераторе нет, логотип сделайте поменьше или не ставьте вовсе.
  2. Логотип — не больше 15% площади кода. Наш предел для H — 19%, но это на идеальной картинке. На глаз: логотип шириной в треть кода закрывает около 11% площади.
  3. Угловые квадраты не закрывайте и не обрезайте — ни логотипом, ни рамкой. Сильную стилизацию углов проверяйте сканером: часть генераторов ломает их пропорции.
  4. Проверьте напечатанный код. Поцарапайте пробный отпечаток ногтем или брызните на него водой и отсканируйте двумя-тремя телефонами. Читается — запас есть. Палец на экране такого запаса не покажет: он закрывает код одним пятном, а с пятном код справляется лучше всего.
  5. Код H печатайте крупнее: квадратиков в нём больше, и при той же стороне каждый выходит мельче.

Многие генераторы по умолчанию ставят уровень M. Это уровень под экран, а не под дверь подъезда. По нашим замерам, M безотказно читается лишь при 1% испорченных точек — это около 11 квадратиков, — а брызги переносит до 4,6% площади. Потёртости и капли выглядят именно так. Если код уходит в печать, настройка по умолчанию работает не на вас.

Если код уже напечатан и плохо читается, сначала протрите его и уберите блик. Остальные причины, по которым телефон не видит код, мы собрали в инструкции по сканированию.

Частые вопросы

Почему QR-код с логотипом в центре читается? Потому что логотип — это одно сплошное пятно, а с таким повреждением код справляется лучше всего. Наш код уровня H пережил заплатку на 19% площади. Но угловые квадраты закрывать нельзя ни на одном уровне.

Сколько можно закрыть у QR-кода? Зависит от уровня защиты и от того, как именно закрыть. Одно пятно в центре: от 4% площади на уровне L до 19% на H. Отдельные точки вразброс: от 0,5% до 2% площади.

Как узнать уровень защиты готового QR-кода? Сканер уровень обычно не показывает, хотя он записан в самом коде, рядом с угловыми квадратами. На глаз его не угадать: код с логотипом на 6% площади может быть и M, и H. Если код ваш, проще пересоздать его на уровне H, чем гадать.

Можно ли починить код, который не читается? Свой — проще перепечатать: это минута в генераторе. Чужой код на табличке или упаковке иногда оживает, если протереть его и убрать блик, — грязь и отражение сканер принимает за испорченные квадратики. Если же стёрт угловой квадрат или большой кусок, чистка не поможет.

QR-код хорошо держит одно большое пятно, заметно хуже — мелкую россыпь и совсем не прощает закрытых углов. Поэтому логотип в центре ему не страшен, а дождь по всей наклейке и загнутый уголок — страшны.

Если статья помогла — поделитесь с теми, кому может пригодиться. И заходите в наш канал в MAX, Telegram и Дзен — там анонсы новых материалов.