Перейти к содержимому
Compvision.ru
VironZizu

Распознавание текста паспорта, OpenCV!

Recommended Posts

В твоём случае - да. Каскады есть смысл применять, когда, например, ты подносишь паспорт к веб-камере, он каскадами обнаруживается и только потом в области обнаружения ищутся стороны и распознаётся текст.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ребят, а есть еще идеи по удалению мусора с картинки, кроме как по цвету и по размеру отсортировывать? А то этот алгоритм срабатывает только конкретно с моим паспортом, стоит взять что-нибудь другое и все!) начинаются косяки! Как то все таки нужно мух от котлет отделять, а именно буквы... Пока у меня плохо сортируются контуры которые по размеру совпадают с контурами букв и я вот не знаю как к ним еще подлезть!?))

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ты не пробовал перед началом поиска контуров делать медианную фильтрацию?

Вообще, тяжело что-то конкретное посоветовать, когда не видно проблемы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да, наверное я не так спрашиваю, медианную фильтрация делаю и так, в частности размытие Гаусса. Получается я нахожу позиции паспорта, и потом нарезаю примерно ту область где находится нужный мне текст, естественно для разных паспортов эта область не получается пиксель в пиксель, и получается что в кадр попадают не нужные предметы, а тессеракт при распозновании очень болезненно на них реагирует. Лучше для примера я выложу сами картинки, допустим с полями Фамилия, Имя, Отчество:

7a7b08b7132f.png

Так вот, в область попала верхняя кромка, она на паспорте идет чуть ниже сердцевины, по свойствам она похожа на буквы, как удалять её, вообще не понятно мне)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А, вот оно как. Обычно такие штуки убирают с помощью мат. морфологии: пару раз делают эрозию, смотрят, что осталось и распознают на исходном изображении (до эрозии!) только эти области.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Взял для тестов несколько паспортов, результат жутко расстроил, положил паспорт с обложкой, мой алгоритм с ним не отработал, кароче из 5 штук, 2 нормально отработали...

В связи с этим вопрос обнаружения границ паспорта снова актуален) ибо я уже без сил, как его границы еще находить вообще не понимаю, там столько ньюансов получается, что очуметьstartle.gif

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

[off]

Ну, я точно уверен, что Smorodov сможет сделать это с помощью ASM, например.

[/off]

 

А я бы нашёл отрезки (например с помощью алгоритма LSD), а по ним с помощью RANSAC построил уравнения прямых - сторон паспорта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

По поводу ASM, думаю должен хорошо работать, но это крайний случай smile.png

 

Еще можно попробовать идеологию фильтра частиц, где частица это прямоугольник и координаты частицы (X,Y,W,H,поворот), а в качестве функции правдоподобия взять сумму градиентов вдоль границ этого прямоугольника.

Для пущей крутости можно еще учитывать направление градиента, и считать сумму проекций на перпендикуляр к стороне.

 

Но это тоже "из пушки по воробьям".

 

Может будет чуток времени, попробую что-нибудь набросать, есть одна мысль, хочу проверить.

Мысль: метод на основе максимизации отношения количества "правильных" пикселей к площади повернутого прямоугольника. Если прямоугольник "правильно" закрывает область, то это отношение максимально.

Как то пытался объяснить иностранцам, не получилось ), наверное мой английский хромает ).  

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Nuzhny пробовал загуглить lsd) как и ожидалось вылезла целая туча статей про наркоту))) можно по подробней по нему? тяжело реализовывается? средставами OpenCV?

Smorodov

Я прочитал и общей картины не получил)) Видимо это действительно пушка, которую я с трудом осилю))

 

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

 

Оказалось я немного подтупил, мой метод находит все таки нужную мне страничку, оказалось я неправильно отсортировываю! Но остается маленький ньюанс, как отличить верхнуюю страничку от нижней?)) размеры и площади то у них одинаковые!)))

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Nuzhny пробовал загуглить lsd) как и ожидалось вылезла целая туча статей про наркоту))) можно по подробней по нему? тяжело реализовывается? средставами OpenCV?

LSD же! Там есть статья, исходники и demo, куда ты можешь загрузить свою картинку и посмотреть на результат работы онлайн.

 Реализация там без OpenCV.

Но такой хороший алгоритм реализован и с OpenCV. Во-первых, легко гуглится проект, созданный независимым разработчиком. А во-вторых LSD есть уже в OpenCV 3.0, можно его взять и оттуда (просто скопировать исходники).

 

Там же есть ссылки на примеры работы алгоритма. Вот кто-то ищет границы купюры: исходник и результат. Вполне себе ничего.

[off]

Кстати! Можно сделать приложение для смартфона для туристов: поднести к камере смартфона любую купюру или монету, распознать и выдать курс к любой валюте, к евро, например. Поднести несколько купюр - посчитать их сумму в любой валюте. Эдакий продвинутый визуальный валютный калькулятор.

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

[/off]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Nuzhny, по тестил LCD метод интересный, пример с купюрой нашел целый скоп прямых, как только их потом объединить в квадраты и выбрать еще нужный?) Мне кажется сложноватый способ для моей задачи) Пробую дальше через квадраты, выяснил почему не может найти полный контур паспорта, когда он лежит рядом с кромкой, получается при Tresholding граница сканируемого изображения немного засвечивается и поэтому алгоритм уже не может нужный внешний контур найти, сейчас покажу пример, может по советуете чего:

76d3dc11432b.png

Как я ищу: 1) делаю небольшое размытие

2)затем Threshold

3)затем FindContours ищу конутры

4)далее Cv.ApproxPoly и у меня повялется куча наборов точек(контуры)

5)Далее сортируем нужные мне с нужными углами и размерами.

6) Повторяем процесс с другими параметрами в Threshold

И та кручу этот цикл и области отлично находятся, вот единственная проблема с краями и засветами, пробовал Канни, а он долго отрабатывает, циклов 10 если поставить так вообще жуть)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Чтобы писать минимум кода, ты можешь попробовать на чёрном изображении нарисовать, найденные с помощью LSD отрезки, белым цветом и применить преобразование Хафа (Hough transform, есть в OpenCV).

Или скинь цветное изображение паспорта (с затёртыми данными), я покажу результат.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Сдается мне что опять бинанизация там не та применяется.

Еще раз http://fiji.sc/Auto_Local_Threshold

image->type->8 bit color->8bit

image->adjust Auto threshold\Auto local threshold.

Тут стоит учитывать, что я применял алгоритмы на дефолтных настройках, хотя и так получилось ничего. Кстати я так думаю, что 

вместо фильтрации(сглаживание и медианная) изображение можно уменьшить в несколько раз и обрабатываться будет быстрее(но это все на уровне покрутить параметры).

И еще что интересно орлы выводятся очень хорошо.

 

п.с.  не знаю как тут вставлять  изображения так чтобы они были ужаты, а при открытии полностью.

поэтому просто ссылками.

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/init.jpg

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/scan.png

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/global.png

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/local.png

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/Phansalkar.PNG

https://dl.dropboxusercontent.com/u/8841028/passport%20threshold/sauvola.PNG

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

mrgloom Идея про уменьшение кстати очень даже хороша, взял на вооружение, заметно шустрей отрабатывает! Я понимаю, что у меня бинаризация в печали, немного не понял что за пример вы скинули, я так понимаю это не OpenCV? Смотрю ваши примеры, очень хороши бинаризация получилась! такие контуры я думаю бы в легкую нашлись, жалко я пока повторить не могу)) image->type->8 bit color->8bit image->adjust Auto threshold\Auto local threshold. Вот это, что имеется ввиду? не совсем уловил)

 

Есть ещё такой ньюанс, если положить паспорт прямо у кромки, то получается, что кромка как бы обрезается, и этот метод совсем уже не срабатывает, пробовал как вариант у скана дорисовывать рамку в 2 пикселя, но это тоже не помогло,у него тогда не получается все нужные прямоугольники найти!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

да это не opencv но там формулы рассчёта приведены можно самому написать.

 

 

 image->type->8 bit color->8bit image->adjust Auto threshold\Auto local threshold. 

 

это то что надо в fiji нажать для получения результата.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Наконец-то дошли руки smile.png

Оптимизации не делал, просто проверил идею.

 

Пример кода максимизирует расстояние между средним цветом внутри и снаружи прямоугольной области. Максимизирует он это расстояние при помощи подбора параметров этого прямоугольника (методом градиентного спуска).

 

Вот что я имел ввиду, когда говорил непонятные вещи smile.png

 

Результат работы программы (изображение может быть и цветным):

 

post-1-0-39093800-1412883040_thumb.png

#include "opencv2/opencv.hpp"
#include <vector>
using namespace std;
using namespace cv;
//----------------------------------------------------------
// Это и есть вычисление расстояния между средними цветами
//----------------------------------------------------------
double getLikelihood(Mat& img,cv::RotatedRect& rr)
{
	double likelihood=0;
	Mat mask=Mat::zeros(img.size(),CV_8UC1);
	// rotated rectangle
	Point2f rect_points[4];
	rr.points( rect_points );
	vector<cv::Point> pts(4); 
	for(int i=0;i<4;++i)
	{
		pts[i]=rect_points[i];
	}
	cv::fillConvexPoly(mask,pts,Scalar::all(255));
	imshow("mask",255-mask);
	Scalar cc1,cc2;
	cc1=cv::mean(img,mask);
	cc2=cv::mean(img,255-mask);
	likelihood=norm(cc1,cc2,cv::NORM_L2);
	return likelihood;
}
//----------------------------------------------------------
// Градиент, чтобы знать куда менять параметры
//----------------------------------------------------------
void getLikelihoodGradient(Mat& img,cv::RotatedRect& rr,cv::RotatedRect& drr)
{
	cv::RotatedRect rrdx=rr;
	rrdx.center.x+=1;
	cv::RotatedRect rrdy=rr;
	rrdy.center.y+=1;
	cv::RotatedRect rrdw=rr;
	rrdw.size.width+=1;
	cv::RotatedRect rrdh=rr;
	rrdh.size.height+=1;
	cv::RotatedRect rrdang=rr;
	rrdang.angle+=1;

	cv::RotatedRect rrdxn=rr;
	rrdxn.center.x-=1;
	cv::RotatedRect rrdyn=rr;
	rrdyn.center.y-=1;
	cv::RotatedRect rrdwn=rr;
	rrdwn.size.width-=1;
	cv::RotatedRect rrdhn=rr;
	rrdhn.size.height-=1;
	cv::RotatedRect rrdangn=rr;
	rrdangn.angle-=1;

	float l0=getLikelihood(img,rr);
	cout << l0 << endl;
	float dlx=getLikelihood(img,rrdx)-getLikelihood(img,rrdxn);
	float dly=getLikelihood(img,rrdy)-getLikelihood(img,rrdyn);
	float dlw=getLikelihood(img,rrdw)-getLikelihood(img,rrdwn);
	float dlh=getLikelihood(img,rrdh)-getLikelihood(img,rrdhn);
	float dlang=getLikelihood(img,rrdang)-getLikelihood(img,rrdangn);

	float scale=sqrt(dlx*dlx+dly*dly+dlw*dlw+dlh*dlh+dlang*dlang);

	dlx/=scale;
	dly/=scale;
	dlw/=scale;
	dlh/=scale;
	dlang/=scale;

	drr.center.x=dlx;
	drr.center.y=dly;
	drr.size.width=dlw;
	drr.size.height=dlh;
	drr.angle=dlang;
}
//----------------------------------------------------------
//  Генерируем тестовое зашумленное изображение
//----------------------------------------------------------
void generateTestImage(Mat& img)
{
	img=Mat(512,512,CV_8UC3);
	cv::RotatedRect rr(cv::Point2f(200,300),Size(140,180),67);
	img=Scalar::all(0);
	// rotated rectangle
	Point2f rect_points[4];
	rr.points( rect_points );
	vector<cv::Point> pts(4); 
	for(int i=0;i<4;++i)
	{
		pts[i]=rect_points[i];
	}
	cv::fillConvexPoly(img,pts,Scalar(255,255,255));
	for(int i=0;i<100000;++i)
	{
		int x=rand()%512;
		int y=rand()%512;
		img.at<Vec3b>(y,x)=Vec3b(255,255,255);
	}
	for(int i=0;i<105000;++i)
	{
		int x=rand()%512;
		int y=rand()%512;
		img.at<Vec3b>(y,x)=Vec3b(0,0,0);
	}
}
//----------------------------------------------------------
// 
//----------------------------------------------------------
int main(int argc, char* argv[])
{
	Mat img,img_cpy;
	generateTestImage(img);

	imshow("testimg",img);
	cv::waitKey(0);

	cv::RotatedRect rr(cv::Point2f((float)img.cols/2.0,(float)img.rows/2.0),Size(img.cols-100,img.rows-100),0);
	cv::RotatedRect drr;
	while(1)
	{
		img_cpy=img.clone();
		getLikelihoodGradient(img,rr,drr);
		// Меняем параметры в сторону увеличения расстояния между средними цветами
                rr.center+=drr.center;
		rr.size+=drr.size;
		rr.angle+=drr.angle;

		// rotated rectangle
		Point2f rect_points[4];
		rr.points( rect_points );

		for( int j = 0; j < 4; j++ )
		{
			line( img_cpy, rect_points[j], rect_points[(j+1)%4], Scalar(0,255,0), 2, CV_AA );
		}

		imshow("img_cpy",img_cpy);
		waitKey(10);
	}
	cv::destroyAllWindows();
	return 0;
}
  • Like 3

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Smorodov, Обалденно!!! На выходных засяду этот код под мой C# врапер подгонять!) Просто огонь!)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Еще небольшой вопрос, вот у меня есть бинаризация Отсу

grayClone.Threshold(grayClone, 0, 255, ThresholdType.Otsu);

Я так понимаю этот метод заключается в том, что высчитывается средний порог и применяется. А если мне нужно высчитать средний порог и потом его применить к обычной бинаризации, как это можно сделать?

Вот сюда:

grayClone.Threshold(grayClone, 0, 255, ThresholdType.Binary); 

На сколько я понимаю этот порог задается в обычной Binary там где у меня 0!?

З.ы. Для чего это мне нужно, чтобы можно было средний порог в небольших пределах по варировать! И сделать скоп разных бинаризаций отталкивающихся от среднего!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Я так понимаю этот метод заключается в том, что высчитывается средний порог и применяется.

 

Бинаризация Отсу работает с бимодальными гистограммами.

Т.е. гисторамма изображения выглядит как два горба smile.png порог, это значение яркости, находящееся между этими горбами.

Если гистограмма имеет больше/меньше двух горбов, то метод не работает как должен.

 

 

 

На сколько я понимаю этот порог задается в обычной Binary там где у меня 0!?

Дык, да, вместо нуля ( threshold_value ).

Доки здесь можно посмотреть: http://docs.opencv.org/doc/tutorials/imgproc/threshold/threshold.html

Это, мне кажется, тоже может быть полезно: http://www.bogotobogo.com/python/OpenCV_Python/python_opencv3_Image_Global_Thresholding_Adaptive_Thresholding_Otsus_Binarization_Segmentations.php

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

По пробовал Adaptive Threshold результаты лучше! Но там я параметры задаю в ручную допустим

srcGray.AdaptiveThreshold(dst, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 20);

block size = 20.

Для некоторых вариантов этот параметров хорошо отрабатывает, но для некоторых нужен немного другой, как можно для адаптивной бинаризации выщитывать этот блок сайз? к чему можно привязатся? Потому что я смотрю, он лучше справляется чем обычный Threshold. Некоторые еле видные грани не сжираются, а некоторые слишком жирные не становятся жирнее...

 

з.ы. Работаю с областью "кем, где выдан"

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Как вариант, можно попробовать использовать информацию о гистограммах для каждого из блоков изображения (способ маньячный конечно :) ).

Можно собрать образцы таких гистограмм, для хорошо бинаризированного изображения (или средние для множества вариантов), и в дальнейшем подбирать  параметры бинаризации ориентируясь на эти гистограммы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Интересно) П о работал с гистограммами, ничего полезного из них вытащить не смог) всял значение константу, вроде для всех вариантов отрабатывает неплохо!)

 

Есть ещё одна идея, но не знаю как реализовать в коде, у меня есть контуры, как закрасить всю область кроме внутринностей этих конутров?) инверсию контуров как то сделать или как?)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Нарисовать контуры черным цветом, предварительно залив изображение белым.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

ну немного не то, если я контуры залью черным в них полезные грани замажутся. Мне именно нужно чтобы я замазал все изображение, но содержимое контуров осталось не тронутым!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×