[Перевод] Пошагово создаём QR-код

В этой статье (оригинал представляет собой интерактивное приложение на JavaScript) подробно описывается, как текстовая строка кодируется в символ QR-кода. Она, по сути, объясняет, как устроена внутри моя библиотека генератора QR-кодов.

Пользовательский ввод

r3bdap6hbyhozwerbekwrdu02ta.jpeg

Результат генерации QR-кода


scing0yi75wgamusgithp-nyv54.png


Пошаговый процесс


0. Анализируем символы Unicode


Количество кодовых точек во входной текстовой строке: 17.

Подробности о каждом из символов:

Index: индекс во входной строке
Char: сам символ
CP hex: значение кодовой точки Unicode в шестнадцатеричном виде
NM: можно ли закодировать в цифровом режиме
AM: можно ли закодировать в алфавитно-цифровом режиме
BM: можно ли закодировать в байтовом режиме
KM: можно ли закодировать в режиме кандзи

mr2yw2jsg7xatxvcuczzggiyih8.png

Можно ли закодировать каждый символ в соответствующем режиме:
2i3cdu8ncg4dtqegrwegla3zjv8.png

Для кодирования всех символов выбран сегментный режим Byte

1. Создаём сегмент данных


Преобразуем каждый символ в биты. В цифровом и алфавитно-цифровом режиме идущие по порядку символы группируются, а затем кодируются в биты. В байтовом режиме символ преобразуется в 8, 16, 24 или 32 бита.
31b2ou6kj-qmvzre-tebycp9_og.png

Созданный единый сегмент:
  • Режим: Byte
  • Количество: 17 байтов
  • Данные: длина 136 байтов

(Для упрощения моё демо всегда создаёт единый сегмент. Но можно сегментировать текст оптимально, чтобы минимизировать общую длину в битах.)

2. Подстраиваемся под номер версии


Общая длина в битах должна представлять список сегментов в зависимости от версии:
wp1umce67ok8hr1kywrmtzv3dai.png

(Примечание: кодовое слово определяется как 8 битов, также известные как байт.)

Ёмкость кодовых слов данных QR-кода для каждой версии, уровень коррекции ошибок и помещаются ли данные (зелёный/красный фон):

ECC L: low
ECC M: medium
ECC Q: quartile
ECC H: high

r7eb1f8pv-0epubemkjyvv48myq.png

Выбранный номер версии: 1

3. Выполняем конкатенацию сегментов, добавляем заполнители, создаём кодовые слова


Объединяем различные битовые строки:
4q059hq6qlab0i8h3y-bbrzdawu.png

Примечание:
  • Описание режима сегмента — это всегда 4-битное поле.
  • Ширина поля количества символов в сегменте зависит от режима и версии.
  • Разграничителем обычно являются четыре бита »0», но их бывает и меньше, если мы достигли предела ёмкости кодового слова данных.
  • Битовый заполнитель имеет ширину от нуля до семи битов »0», чтобы заполнить все неиспользованные биты в последнем байте.
  • Байтовый заполнитель состоит из перемежающихся (шестнадцатеричных) EC и 11, пока не будет заполнена вся ёмкость.

Полная последовательность битов данных:

01000001000101001000011001010110110001101100011011110010110000100000011101110110111101110010011011000110010000100001001000000011000100110010001100110000

Вся последовательность байтов кодовых слов данных (полученная разбиением битовой строки на группы по 8 бит) в шестнадцатеричном виде:

41 14 86 56 C6 C6 F2 C2 07 76 F7 26 C6 42 12 03 13 23 30

4. Разбиваем блоки, добавляем ECC, реализуем чередование


Статистика обо всех блоках:
vxixzqek9sldhd2cv-7ju8fw7i8.png

Разделяем последовательность кодовых слов данных (зелёный фон) на короткие и длинные блоки; затем для каждого блока вычисляем кодовые слова ECC (синий) и добавляем их в конец блока:
o3rxy5u_1jtgltkz-orxxsn0qtc.png

(Примечание: математические расчёты вычислений кодов коррекции ошибок Рида-Соломона пропущены, потому что это долго и скучно.)

Окончательная последовательность кодовых слов, образованная чередованием кодовых слов данных/ECC из разных блоков:

wm0u7avyup0wjuccqomqy6ynh60.png

Окончательная последовательность битов для отрисовки сканирования зигзагом:

0100000100010100100001100101011011000110110001101111001011000010000001110111011011110111001001101100011001000010000100100000001100010011001000110011000010000101101010010101111000000111000010100011011011001001

5. Рисуем фиксированные паттерны


Рисуем горизонтальные и вертикальные паттерны таймингов (в строке 6 и столбце 6, считая с 0 начиная с левого верхнего угла):
ers-dianz-kh-3wz_g2c8ikjusg.png

Рисуем опорные паттерны (finder pattern) в трёх углах, каждый из которых имеет размер 8×8, включая разделитель, перерисовывая некоторые модули таймингов:
vp3z39bio1ydwjgv3mspll35jky.png

Рисуем временные макетные биты формата (format bit) (рядом с finder pattern):
e1_xf0iw0gmryhfztdst7jtl_s0.png

6. Отрисовываем кодовые слова и остаток


Вычисляем сканирование зигзагом (которое начинается с правого нижнего угла) так, чтобы посетить все незаполненные модули (то есть пропуская функциональные модули):
ecs6_k2e5hkko8vharhrheimdjw.png

Рисуем модули данных/ECC согласно порядку сканирования зигзагом и битовые значения из окончательной последовательности кодовых слов:
0j9df4etlnqlnkbtzyy72c1ghsy.png

(Например, байт годового слова C5 (шестнадцатеричный) — это двоичные 11000101, создающие последовательность модулей [тёмный, тёмный, светлый, светлый, светлый, тёмный, светлый, тёмный].)

7. Пробуем применять каждую из масок


Паттерн маски (влияющий только на нефункциональные модули):
gjclpab1i_y93tgtptoch_yg7h0.gif

Ниже показаны примеры для паттерна маски 3.

Выполняем XOR маски с модулями данных, ECC и остатком:

uqgubngdbgp0few6tdwy8npqzjc.png

Отрисовываем сами биты формат (рядом с finder pattern):
-5kagnqgjlzr6m8fj3kjqxecry8.png

8. Находим штрафные паттерны


Горизонтальные последовательности модулей одного цвета (каждый длиной не более 5 битов):
kzcpmlyl1skx1wq9gjzumwl61wk.png

Вертикальные последовательности модулей одного цвета (каждый длиной не более 5 битов):
dqtya2lfnto9o5l5a7hdvk_7noq.png

Прямоугольники 2×2 модулей одного цвета:
jhbssw1vcgtk-qeixrixnea7xpe.png

Горизонтальные паттерны, похожие на finder pattern:
7f0y2q2dymt0dixxaz5dleyadm0.png

Вертикальные паттерны, похожие на finder pattern:
ozhgse__g4a-mvnhlqcicguemno.png

Баланс тёмных/светлых модулей:
sg_i9qxpo0idfpzlmxnd4lmt3di.png

9. Вычисляем штрафные очки, выбираем наилучшую маску


Mask: номер паттерна маски
RunP: штрафные очки линейной последовательности одного цвета
BoxP: штрафные очки прямоугольника 2×2 одного цвета
FindP: штрафные очки паттернов, похожих на finder pattern
BalP: штрафные очки за баланс тёмного/светлого
TotalP: сумма штрафных очков
yqf8htg9gfurbinnv-mtvdkupaa.jpeg

Наименьшая сумма штрафных очков: паттерн маски 3

Как вычисляются штрафы:

  • RunP: 3 очка за каждую линейную последовательность из 5 модулей одного цвета, 4 очка за каждую последовательность из 6 модулей, 5 очков за каждые 7 модулей, 6 очков за каждые 8 модулей и так далее. Последовательности не могут пересекаться.
  • BoxP: 3 очка за каждый прямоугольник 2×2 одного цвета. Прямоугольники могут пересекаться.
  • FindP: 40 очков за каждый паттерн, похожий на finder pattern. Finder pattern могут пересекаться.
  • BalP: 0 очков, если соотношение тёмных модулей находится в интервале [45%, 55%]; 10 очков, если в интервале [40%, 60%]; 20 очков, если в интервале [35%, 65%]; 30 очков, если в интервале [30%, 70%] и так далее.

Вы можете изучить исходный код этого веб-приложения на TypeScript (файл 0, файл 1) и скомпилированный код на JavaScript.

Дополнительная информация


  • Википедия: QR code — Design
  • Thonky.com: QR Code Tutorial
  • YouTube: James Explains — How QR Codes Are Built
  • research! rsc (Russ Cox): QArt Codes
  • QRazyBox — QR Code Analysis and Recovery Toolkit (live app)
  • Piko & blinry: Reading QR codes without a computer!
  • YouTube: Pillazo — How to Decode a QR Code by Hand
  • YouTube: Veritasium — I used to hate QR codes. But they’re actually genius
  • YouTube: The 8 Bit Theory — The C128, VIC-20, C64, Mega65, and Commander X16 generate QR Codes. In Basic, C and 6502 Assembly

© Habrahabr.ru