Jump to content
Compvision.ru
VironZizu

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

Recommended Posts

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

7a7b08b7132f.png

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

[off]

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

[/off]

 

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

Share this post


Link to post
Share on other sites

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

 

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

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

 

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

 

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

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

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

Share this post


Link to post
Share on other sites

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

Smorodov

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

 

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

 

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

Share this post


Link to post
Share on other sites

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

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

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

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

 

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

[off]

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

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

[/off]

Share this post


Link to post
Share on other sites

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

76d3dc11432b.png

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

2)затем Threshold

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

Еще раз 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

Share this post


Link to post
Share on other sites

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

 

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

Share this post


Link to post
Share on other sites

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

 

 

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

 

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

Share this post


Link to post
Share on other sites

Наконец-то дошли руки 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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

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

Вот сюда:

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

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

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

Share this post


Link to post
Share on other sites
Я так понимаю этот метод заключается в том, что высчитывается средний порог и применяется.

 

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

Т.е. гисторамма изображения выглядит как два горба 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

Share this post


Link to post
Share on other sites

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

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

block size = 20.

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

 

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

 

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

×