Автоматическое выравнивание сканов документов
Запись от Storm23 размещена 01.11.2015 в 19:54
Показов 32369
Комментарии 12
|
Автоматическое выравнивание сканов документов Часто отсканированные или сфотографированные документы имеют перекос: От этого перекоса хотелось бы избавиться. Причем сделать это простым и максимально быстрым способом, без использования тяжелых библиотек типа OpenCV или AForge.NET. Идея алгоритма Разобьем исходное изображение на несколько вертикальных полосок и посчитаем среднюю яркость пикселов в каждой строке каждой полоски: Каждая строка текста оставляет характерную темную линию. Если бы перекоса не было, то темные линии в правой полоске совпадали бы с линиями в левой полоске. Но из-за перекоса происходит сдвиг линий: Теперь, если мы найдем такой сдвиг, при котором максимальное число линий в полосках совпадет, то мы сможем посчитать угол, на который нужно повернуть исходное изображение, что бы убрать перекос. Нужный сдвиг можно найти если сдвигать одну из полосок на 1, 2, 3 и т.д. пикселов по вертикали, и считать коэффициент корреляции между полосками. Где корреляция достигнет максимального значения – такой сдвиг и является оптимальным. Реализация Сперва нужно найти усредненные значения пикселов в вертикальных полосках исходного изображении. Это трудоемкая задача, потому что исходное изображение может быть большим, а попиксельный перебор – долгий процесс. Для того что бы сделать это быстро, сделаем хитрость – используем встроенный в GDI+ механизм. Создадим новый битмап, высота которого будет равна высоте исходного изображения, а ширина – равна 2 пикселам. Затем отрисуем исходное изображение на новом битмапе, сжимая изображение по горизонтали до 2 пикселов:
Поскольку качество интерполяции нам не важно, выбираем максимально быстрый режим интерполяции - InterpolationMode.Low. На этом же этапе изображение можно немного сжать и по вертикали, для уменьшения длины полосок и повышения быстродействия дальнейших расчетов. Далее, нужно перенести яркость найденных пикселов в массив int[] для каждой полоски. Для этого нужно использовать метод Bitmap.LockBits для быстрого доступа к пикселам. Яркость пиксела можно вычислить методом Color.GetBrightness(), но я использовал более быстрый аналог – просто брал зеленый канал цвета Color.G в качестве яркости. После того как мы получили усредненные яркости каждой полоски в массивах int[], будем считать взаимнокорреляционную функцию между ними:
Проведя эти расчеты для разных величин shift, найдем такой сдвиг, при котором корреляция – максимальна. Теперь осталось только найти угол поворота. Для этого создадим вектор, соединяющий центры полосок, с учетом сдвига. По горизонтали расстояние между центрами полосок будет
Теперь найдем угол поворота по формуле
Сохраняем изображение в файл. Радуемся результату. Замечания Выше приведена упрощенная реализация. В реальном коде все немного сложнее:
Примеры работы алгоритма Исходные коды и тестовое приложение: SkewCorrection.zip Код распространяется под лицензией GPL3. | |||||||||||||||||||||||||
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 12
Комментарии
-
Запись от HighPredator размещена 02.11.2015 в 16:47
-
Интересный алгоритм.
HighPredator, а как можно определить угол поворота прямоугольника? (бегло погуглил, но ничего не нашел)Запись от castaway размещена 02.11.2015 в 17:01
-
Вот смотрите: допустим вы нашли восточную границу документа (т.е. любые две точки на ней). Ну и все. Угол между прямыми. В нормально ориентированном документе граница должна образовывать с вертикальной прямой угол ноль градусов плюс-минус дельта. Все, можно условно говоря схватить за эту границу и довернуть изображение до вертикали. Стопроцентным же решением было бы определить левый верхний и правый нижний углы, аналитически найти точку пересечения диагоналей (достроив до прямоугольника) и относительно нее довернуть.Запись от HighPredator размещена 02.11.2015 в 17:40
-
Вы наверно говорите про метод описанный на хабре, здесь http://habrahabr.ru/post/223507/
Сообщение от HighPredator
Так как же найти этот самый прямоугольник? Если бы он был, то все можно было бы сделать по другому. Только вот его нет на сканах. Если вы отсканите чек, то он будет белым прямоугольником на белом фоне. То есть его не будет видно. Вот пример реального скана чека: https://www.dropbox.com/s/tobv... 1.jpg?dl=0
Нет там прямоугольника.
К тому же, даже если найти прямоугольник, то во-первых сам текст может быть напечатан кривовато относительно бумаги, а во-вторых сам край тоже может быть неровным. На том примере на хабре, видите, там край пожеван?
А вообще описанный мною алгоритм - лишь один из многих. Просто он самый простой и быстрый. И кроме того достаточно точный. Этим он мне нравится. Кроме него еще я знаю несколько методов: ваш метод прямоугольника, метод преобразований Хафа, метод БПФ.
А разве это легче? Для этого нужно найти границы листа (при чем будут мешаться еще линии на самом документе), а потом еще как-то искать прямоугольник. Это не просто. Кроме того, для этого полюбому вам нужно бегать по пикселам, а в моем методе - пикселы не перебираются вообще, если вы прочитали про реализацию. Поэтому моя реализация самая быстрая так точно.Запись от Storm23 размещена 02.11.2015 в 19:13
-
Интересный проект.
И решение тоже.
Захожу иногда в "этот" магазин.
(=имя сетевых магазинов. Джинсы Levi's покупал там на подарки домой, на распродаже.)Запись от politov01 размещена 03.11.2015 в 00:48
-
Нет, мой пример не с хабра, а из личного прошлого опыта. Но вы немного отошли, причем здесь край бумаги? Он нас не интересует вообще. Да собственно это и не важно. Я прицепился не потому, что думаю, что ваш метод кривой или еще какой-то. Нет, новизна есть. Меня интересует математическая составляющая. А именно доказательство того, что данный метод справедлив не для узкого множества образцов. Вот например, как с вашей точки зрения будет работать данный метод для образцов, содержащих изображения произвольной формы? А что если процент занимаемого изображением пространства на скане варьируется у разных образцов? Ну и совсем упоротый пример: что если на скане присутствует изображение чего-то близкого к белому шуму в ргб-палитре допустим круглой формы?
Или вот нечто более реальное. Вот вы утверждаете:
А что если срока состоит из слешей или бэкслешей например? Это символы сами по себе "перекошенные". Вот вам уже естественный псевдо-перекос строки.Запись от HighPredator размещена 03.11.2015 в 10:39
-
HighPredator, точно. Как то сам с ходу и не сообразил...Запись от castaway размещена 03.11.2015 в 11:05
-
Запись от HighPredator размещена 03.11.2015 в 11:22
-
Так, давайте по порядку
Ну как при чем здесь край бумаги? Вы предложили альтернативный метод. Я так понял, что этот метод определяет край бумаги. Если нет - поправьте меня и покажите ту самую линию, которую можно взять за основу для поворота. Желательно показать это на том чеке, который я вам скинул.
Сообщение от HighPredator
А то получается как то некрасиво, я вам даю полностью рабочее приложение, а вы его критикуете и предлагаете некий алгоритм, но без всякой конкретики.
Да нормально будет работать, если есть строки текста идущие на всю ширину страницы. Ну так он для выравнивания текста и предназначен, если нет текста, то нечего и выравнивать.
Сообщение от HighPredator
Если там есть еще и текст, то совершенно нормально будет работать алгоритм. Алгоритм устойчив к шуму, потому что работает с усредненными яркостями, а корреляция от равномерного шума вообще не зависит.
Сообщение от HighPredator
А если там только изображение, и там текста нет, то непонятно что там выравнивать.
В целом - конечно алгоритм имеет недостатки, и есть случаи, когда он лажает. Причины две - либо полоса попала в пустую область документа, где нет текста. Либо же интеркорреляционная функция имеет несколько максимумов. И то и другое решается выбором более удачной пары полосок.
Но никто и не ожидает 100% робастности.
Если посмотреть на альтернативные алгоритмы, то они работают ничуть не лучше. Например, метод преобразований Хафа по сути делает то же самое что и данный алгоритм, но при этом имеет большую погрешность, из-за того, что строка символов у него будет представлена множеством линий с разным наклоном.
Метод БПФ - более точен, но имеет такой же недостаток - как и Хафа. Если ему подсунуть одну строку с большим шрифтом, он скорее всего не сможет ее выровнять.
В общем я не вижу причин по которой какой-то из перечисленных алгоритмов работал бы лучше, чем данный. По крайней мере по быстродействию - так точно.
Что бы не быть голословным, вот для интереса подсунул ему изображения с разными шрифтами, с картинками:
https://www.dropbox.com/s/49ka... 1.png?dl=0
https://www.dropbox.com/s/3j5k... 5.png?dl=0
https://www.dropbox.com/s/bbmy... 4.png?dl=0
А здесь вот сильно зашумленное изображение:
https://www.dropbox.com/s/8c4g... 8.png?dl=0
Все точно работает.
Строка - это набор символов расположенных на одной линии. Алгоритму все равно какие символы там будут - хоть слеши, хоть прямые хоть обратные. Ведь алгоритм усредняет яркость пикселов горизонтально, и ищет строку символов целиком.
Сообщение от HighPredator
Теперь по сути. Ну какое доказательство вы ожидаете?
Сообщение от HighPredator

Во-первых математический бекгруанд там довольно прозрачен. Текст, если он есть расположен в виде линий. Эти линии дают характерный "горб" на интегральной гистограмме. Алгоритм строит вертикальную гистограмму и ищет максимум интеркорреляционной функции между гистограммой левой и правой частей изображения.
Во-вторых, эта программа - не научная диссертация, это решение вполне конкретной задачи. И тем, кто будет пользоваться этим решением - их интересует эффективность метода, а не математические доказательства. Метод прост и эффективен. Это главное.
Не подумайте что у меня математический нигилизм. У меня высшее математическое образование. У меня есть научные статьи, и там все красиво и с доказательствами. Если хотите доказательств, могу скинуть ссылки
Просто на практике нужны простые и эффективные решения, а не теории. Я встретил эту задачу в реальном приложении, нашел алгоритм, он мне показался интересным (в основном из-за простоты и скорости, люблю я такие алгоритмы) - и я решил им поделиться.
Разрабатывать математическую теорию в мои планы не входило, да и я уверен, что это довольно тривиальный алгоритм, и где-то он рассматривается в спец литературе наверняка.Запись от Storm23 размещена 03.11.2015 в 12:33
-
Вот, отметил границу: http://tinyurl.com/o4gk3nv
Спасибо за комментарии и иллюстрации. Собственного этого в статье и не хватало.
Вот! Вот это и стоило в самом начале написать
Я-то понял откуда взялись пресловутые "полоски", но не всем читателям это может быть так очевидно
Просто когда описывается какой-то алгоритм, считается хорошим тоном описывать не только сам алгоритм, но и теорбазу и примеры работы на сложных случаях. Потому, что алгоритм, равно как и любой метод в любой точной науке, должен не только отвечать на вопрос "как сделать?", но и на вопрос "почему так?".Запись от HighPredator размещена 03.11.2015 в 13:57
-
Запись от Storm23 размещена 03.11.2015 в 17:39
-
К сожалению всё-таки страдает, хоть и при единственном повороте и не видно будет, но если изображение вращать несколько раз, то даже используя InterpolationMode.HighQualityBicubic, четкость теряется.Запись от _Radik_ размещена 08.08.2017 в 12:16



