Код с паролем от 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. Защита занимает место: чем больше запасных данных, тем плотнее рисунок.
Одна ссылка, четыре уровня защиты. Чем выше уровень, тем мельче рисунок. Замер «Техпросто»
Дальше каждый код портили тремя способами:
- одно пятно — белый квадрат в центре, как наклейка или логотип; его увеличивали, пока код не переставал читаться;
- брызги — от 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% площади. На тридцати кляксах код читался уже через раз.
Серые столбцы — обещание стандарта, цветные — что вышло у нас. Чем мельче и разбросаннее повреждение, тем раньше сдаётся код. Замер «Техпросто»
| Уровень | Обещано стандартом | Одно пятно | Брызги 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-код хранит данные. Данные он пишет не по одному квадратику, а байтами — порциями по восемь квадратиков, которые лежат рядом. И исправляет тоже байтами. Защита работает по коду Рида — Соломона, тому же математическому способу, что спасает музыку на поцарапанных 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. Если код пойдёт в печать, вот что стоит учесть.
- В генераторе найдите пункт «Коррекция ошибок» (по-английски Error correction) и выберите H, если код будет с логотипом, на улице или там, где его трогают руками. Для кода на экране или в чистом месте хватит M — у него меньше квадратиков, они крупнее, и такой код читается издалека. Если такого пункта в генераторе нет, логотип сделайте поменьше или не ставьте вовсе.
- Логотип — не больше 15% площади кода. Наш предел для H — 19%, но это на идеальной картинке. На глаз: логотип шириной в треть кода закрывает около 11% площади.
- Угловые квадраты не закрывайте и не обрезайте — ни логотипом, ни рамкой. Сильную стилизацию углов проверяйте сканером: часть генераторов ломает их пропорции.
- Проверьте напечатанный код. Поцарапайте пробный отпечаток ногтем или брызните на него водой и отсканируйте двумя-тремя телефонами. Читается — запас есть. Палец на экране такого запаса не покажет: он закрывает код одним пятном, а с пятном код справляется лучше всего.
- Код 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 и Дзен — там анонсы новых материалов.