Я вчера заморочился на отличненько. Мне показалось скучным и грустным вводить цифры счёта за квартиру в окошко «Госуслуг» и решил сделать автоматическую распознавалку чисел, попутно изучив работу с камерой из браузера.
Доступ к камере — экспериментальный стандарт, поэтому мало кто из браузеров может им похвастаться, я использовал специальный билд «Opera-Labs-Camera», который можно взять с сайта снапшотов «Оперы». Работать мой пример, соответственно, будет только там, если хотите попробовать, нужно его скачать.
Вообще говоря, аналогичное API есть и в «Хроме», нужно только включить его в конфиге, я поддержку его сделал, но не испытывал.
АПИ оказалось несложным. Всё, что нужно сделать — соединить специальным образом тег VIDEO с источником стримового видео; потом я 10 раз в секунд забираю кадр с видео и кладу в CANVAS (на скриншоте он цветной), оттуда вырезаю небольшую область, перевожу в градации серого, беру за порог 75% от усреднённого цвета и перевожу всё в ч/б.
var vid = document.getElementById('video-stream');
// для ВебКита — переопределяем метод
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
if (navigator.getUserMedia) {
// вообще getUserMedia имеет ещё один параметр — callback, который вызывается, когда
// произошла ошибка
navigator.getUserMedia('video', function (stream) {
// всё прошло успешно, можно привязать к нашему тегу VIDEO поток
vid.src = window.webkitURL ?
window.webkitURL.createObjectURL(stream) :
stream;
});
}
Больше всего из трёх часов, которые у меня ушли на это, я делал распознавание цифр. Алгоритмы, которые я нашёл в интернете, были очень монструозными, да и переписывать готовое было скучно, поэтом я перепробовал несколько вариантов, которые пришли мне в голову.
В итоге, лучше всех распознаёт следующий алгоритм.
Сначала я очевидным образом разбиваю изображение на цифры, это совершенно неинтересно, но вкратце опишу. Двигаясь снизу, ищу нижнюю границу по появлению чёрных пикселей, потом продолжаю движение до их исчезновения. То же слева и справа. Потом примерно так же ищу границы цифр, отбрасывая слишком короткие последовательности и прерывая слишком длинные (если на бумаге есть пятна или цифры слиплись).
Так получаются прямоугольники цифр.
Дальнейшее нарисовано на картинке — каждая цифра условно разделяется вертикальной линией по середине. Слева и справа от линий считаю количество переходов между чёрным и белым, двигаясь сверху вниз. Получаются комбинации из пар чисел.
Например, на картинке слева и справа от линий будет по одному переходу, кроме линии №4, где справа перехода не будет.
Я их прореживаю — одинаковые последовательно идущие заменяю на одно значение. В итоге, запись для «шестёрки» будет выглядеть вот так: 1|1, 1|0, 1|1. Вертикальная черта тут символизирует мою разделительную линию.
Получаются цепочки, которые уже достаточно хорошо характеризуют цифру, правда некоторые цифры этот паттерн не в состоянии распознать. Поэтому я считаю количество ч/б переходов ещё и по моей умозрительной вертикальной линии.
Например, «0» и «8» дают комбинацию «1|1», но отличаются количеством переходов на средней линии: два и три, соответственно.
Собственно, на верхнем скриншоте в большом текстовом поле как и видно эти комбинации. Первым там записан номер по порядку, потом показатель для средней линии и, через двоеточие, двойки цифр по левой и правой части.
Алгоритм «шумит», из-за того, что дрожит рука с бумагой, из-за освещения. Поэтому я накапливаю статистику и указываю ту цифру, которая в этом позиции распознавалась чаще других. Статистику сбрасываю, если количество объектов на экране изменилось.
Далеко не идеальный алгоритм, но у меня и не было цели сделать продакшн-решение.
Код не привожу, он довольно объёмный, доступен по ссылке.
Автор: bolk