Как читать плохо пропечатанные кодыВ прошлый раз я
упомянул, что returnErrors в ZXing подготавливает почву и для других препроцессингов. Сегодня разберём ещё один пример препроцессинга для исправления плохой печати.
Речь про коды, у которых модули (те самые чёрные и белые квадратики, из которых состоит DataMatrix) напечатались с разрывами.
1️⃣ Первый шаг кажется максимально контринтуитивным: чтобы прочитать код, картинку сначала придётся намеренно испортить. Для этого накладываем гауссово размытие по вертикали, чтобы склеить разорванные модули.
2️⃣ Далее уменьшаем изображение методом INTER_AREA, то есть с усреднением по площади, до трёх пикселей на модуль. Всё, что мельче модуля (шум матрицы, зернистость бумаги), просто исчезает, а модуль в три пикселя выживает. Порядок здесь важен: размывать нужно именно до уменьшения, после него склеивать уже нечего.
3️⃣ После усреднения вместе с мусором приглушаются и полезные участки, поэтому CLAHE (Contrast Limited Adaptive Histogram Equalization) помогает вернуть контраст. Если по-простому, то картинка делится на 64 клетки, в каждой берутся самый тёмный и самый светлый пиксель, и затем яркость плавно растягивается.
4️⃣ Последний шаг — это перевод серого в чисто чёрно-белое. Алгоритм Otsu сам подбирает уровень, ниже которого всё становится чёрным, а всё, что выше — белым. И уже эта картинка скармливается снова в ZXing.
Нюанс в том, что ни ось размытия, ни нужный размер заранее неизвестны: первое зависит от того, как принтер испортил печать, а второе от расстояния съёмки. Поэтому перебирается сетка: три целевых размера кода на шесть вариантов источника — размытия по вертикали и горизонтали с тремя разными ядрами. Затем пытаемся декодировать каждый вариант.
💡 И тут снова выручает returnErrors. Если до этого код не читался вообще никак, то на некоторых комбинациях ZXing хотя бы находит символ: прочитать модули он всё ещё не может и возвращает ошибку контрольной суммы, но это уже подсказка. Она позволяет отбросить неактуальные комбинации и подбирать размер вокруг удачной, убавляя или прибавляя пиксели.
Таким образом, если повезёт подобрать нужные параметры, код получится прочитать, но стоить это может дорого. Именно поэтому в потоковом режиме сканирования такому перебору нужен жёсткий бюджет по времени, а весь препроцессинг стоит выносить в отдельный поток, иначе будут сильные просадки по FPS.