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

Распознавание номерного знака, расп. текста.

Recommended Posts

оп, необновил страничку помоему Smorodov как раз на это ссылку дал.

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


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

Добавил сглаживание, не скажу что уж очень сгладилось но больших пиков уже нет

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

	cvNamedWindow("Org");

	cvNamedWindow("Gray");

	IplImage* frame = NULL;	


	frame = cvLoadImage("c:/test.jpg");


	IplImage* gray = NULL;


	gray = cvCreateImage(cvGetSize(frame), 8, 1);


	cvConvertImage(frame, gray, CV_BGR2GRAY);




	CvPoint ptLast = cvPoint(0, 0);


	DWORD dwStart = GetTickCount();

	int nLastYPos = 0;



	for (int nY = 0; nY < gray->height; nY+=10)

	{

		ptLast = cvPoint(0, nY);

		for (int nX = 2; nX < gray->widthStep - 2; nX++)

		{

			int nAv = 0;

			for (int nPos = -2; nPos < 3; nPos++)

			{

				nAv += gray->imageData[nY * gray->widthStep + nX + nPos];

			}	

			nAv = abs(nAv);

			nAv /= 50;

			//int nBr1 =  gray->imageData[nY * gray->widthStep + nX + 1] / 10;

			//nBr = abs(nBr1 - nBr);

			cvLine(frame, ptLast, cvPoint(nX, nY - nAv) , cvScalar(0), 1);

			ptLast = cvPoint(nX, nY - nAv);

		}

	}


	dwStart = GetTickCount() - dwStart;


	std::cout << dwStart;



	cvShowImage("Gray", gray);

	cvShowImage("Org", frame);


	cvReleaseImage(&gray);

	cvReleaseImage(&frame);


	gray = NULL;

	frame = NULL;


	char c = cvWaitKey(0);

	if (c == 27) 

	{ 

		return 0;

	}

	return 0;

}

post-1451-0-53395400-1310559346_thumb.jp

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


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

Не то. Надо найти сумму модулей разниц соседних точек и её сглаживать. А у Вас, по моему, просто разницы нарисованы.

На 21 странице формула гласит:

float f(float x)
{
float _f=0;
for(int i=0;i<x-2;i++)
{
_f+=fabs(I[i+1]-I[i]);
}
return _f;
}[/code]

вот это надо сглаживать и брать производную.

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


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

вот это надо сглаживать и брать производную.

Спасибо большое!

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


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

Так если я правильно понял то float x - это позиция пикселя?

тогда скажем для конечнего пикселя к примеру 640-го будет произведено 638 итераций?

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


Ссылка на сообщение
Поделиться на других сайтах
Добавил сглаживание, не скажу что уж очень сгладилось но больших пиков уже нет

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

хотя вот интересно какой результат производной будет без сглаживания.

Так если я правильно понял то float x - это позиция пикселя?

тогда скажем для конечнего пикселя к примеру 640-го будет произведено 638 итераций?

т.к. там производится суммирование, то да для последней точки надо просчитать N-1 предыдущих

for(int i=0;i<x-2;i++) не очень понял почему -2 мне кажется -1 должно быть.

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


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

-2 не разбирался, как в документе было так и написал. Вычислительные затраты, естественно можно сильно сократить, прогнав f(x) только для конечного пикселя, и занося результат каждой итерации в выходной массив.

Это я просто формулу из документа один в один привел.

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


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

так ну вроде получилось заставить работать функцию, спасибо Smorodov, сейчас осталось взять от неё производные, и работать с локализацией пластины.

Можно еще поработать со сглаживанием, но изначальная оптимизация ЗЛО! :D Хотя и в данном варианте быстродействие меня вполне устраивает.


class _Line

{

	int m_nSize;	

public:

	_Line()

		:m_nSize(0)

		,m_nYPosition(0)		

	{};

	virtual ~_Line(){};

	CArray<int>	m_arData;

	int GetSize() const {return m_nSize;};	

	void SetSize(int nSize)

	{

		if(nSize <= 0)

			return;

		m_nSize = nSize;

		m_arData.SetSize(m_nSize, 10);

	}

	_Line& operator = (const _Line& src)

	{

		SetSize(src.m_nSize);

		m_nYPosition = src.m_nYPosition;

		m_arData.Copy(src.m_arData);

		return *this;

	}


	int m_nYPosition;

};


void SmoothFunc(const CArray<_Line>& arSrc, CArray<_Line>& arDest)

{

	arDest.RemoveAll();	

	for (int nPosY = 0; nPosY < arSrc.GetCount(); nPosY++)

	{

		_Line line;

		line.SetSize(arSrc[nPosY].GetSize());

		line.m_nYPosition = arSrc[nPosY].m_nYPosition;		

		for (int nPosX = 0; nPosX < arSrc[nPosY].GetSize() - 2; nPosX++)

		{

			int nSmooth = 0;

			for (int nSmoothPos = -2; nSmoothPos < 3; nSmoothPos++)

			{

				if(nPosX < 2 && nSmoothPos < 0)

					continue;

				nSmooth += arSrc[nPosY].m_arData[nPosX + nSmoothPos];

			}

			nSmooth /= 5;

			line.m_arData[nPosX] = nSmooth;

		}

		arDest.Add(line);

	}

}


void Func(IplImage* pImg, int nPosY, _Line& line)

{

	unsigned int _f = 0;

	int nLinePos = nPosY * pImg->widthStep;

	line.SetSize( pImg->widthStep - 1);	

	for(int nPos = 0; nPos < pImg->widthStep - 1; nPos++)

	{

		_f += abs(pImg->imageData[nLinePos + nPos + 1] - pImg->imageData[nLinePos + nPos]);

		line.m_arData[nPos] = _f;

	}

}



int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

	cvNamedWindow("Org");

	cvNamedWindow("Gray");

	IplImage* frame = NULL;

	frame = cvLoadImage("c:/test.jpg");


	IplImage* gray = NULL;


	gray = cvCreateImage(cvGetSize(frame), 8, 1);


	cvConvertImage(frame, gray, CV_BGR2GRAY);




	CvPoint ptLast = cvPoint(0, 0);


	DWORD dwStart = GetTickCount();

	int nLastYPos = 0;


	CArray<_Line>	arLines;

	CArray<_Line> arLinesSmoth;


	for (int nY = 0; nY < gray->height; nY += 10)

	{

		//ptLast = cvPoint(0, nY);

		_Line line;

		line.m_nYPosition = nY;

		Func(gray, nY, line);

		arLines.Add(line);		

	}

	SmoothFunc(arLines, arLinesSmoth);


	CArray<_Line> arDraw;

	arDraw.Copy(arLinesSmoth);


	for (int nPosY = 0; nPosY < arDraw.GetCount(); nPosY++)

	{

		if(nPosY != 46)     //Убрать!!!!

			continue;   //Убрать!!!!

		ptLast = cvPoint(0, arDraw[nPosY].m_nYPosition);

		for (int nPosX = 0; nPosX < arDraw[nPosY].GetSize(); nPosX++)

		{

			int f = arDraw[nPosY].m_arData[nPosX] / 50;

			cvLine(frame, ptLast, cvPoint(nPosX, arDraw[nPosY].m_nYPosition - f) , cvScalar(0), 1);

			ptLast = cvPoint(nPosX, arDraw[nPosY].m_nYPosition - f);

		}		

	}



	dwStart = GetTickCount() - dwStart;


	std::cout << dwStart;



	cvShowImage("Gray", gray);

	cvShowImage("Org", frame);



	cvReleaseImage(&gray);

	cvReleaseImage(&frame);


	gray = NULL;

	frame = NULL;


	char c = cvWaitKey(0);

	if (c == 27) 

	{ 

		return 0;

	}

	return 0;

}

post-1451-0-65047100-1310594207_thumb.jp

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


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

ну производную, то в дискретном случае можно взять как (y2-y1)/(x2-x1).

как сглаживать и зачем не знаю.

Поясните пжл, что в данном случае y2 y1, x2 x1?

Спасибо за рание!

Утро вечера мудренее :)


void GetFirstDiff(const CArray<_Line>& arSrc, CArray<_Line>& arDest)

{

	arDest.RemoveAll();     

	for (int nPosY = 0; nPosY < arSrc.GetCount(); nPosY++)

	{

		_Line line;

		line.SetSize(arSrc[nPosY].GetSize());

		line.m_nYPosition = arSrc[nPosY].m_nYPosition;          

		for (int nPosX = 0; nPosX < arSrc[nPosY].GetSize() - 2; nPosX++)

		{

			int nDiff = 0;

			int nY = arSrc[nPosY].m_nYPosition;

			if(nPosX + 1 > arSrc[nPosY].GetSize())

				return;

			int ndY = (nY - arSrc[nPosY].m_arData[nPosX + 1]) - (nY - arSrc[nPosY].m_arData[nPosX]);

			int ndX = arSrc[nPosY].m_arData[nPosX + 1] - arSrc[nPosY].m_arData[nPosX];

			if(ndX)

				nDiff = ndY / ndX;


			line.m_arData[nPosX] = nDiff;

		}

		arDest.Add(line);

	}

}

правда получается сплошная линия, толи я опять что то не так сделал, толи флрмула производной не та.

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


Ссылка на сообщение
Поделиться на других сайтах
Поясните пжл, что в данном случае y2 y1, x2 x1?

ну вот вы получили функцию F(x).

если брать шаг =1 то, y2=F(i+1) y1=F(i) и x2-x1=1

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


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

ну вот вы получили функцию F(x).

если брать шаг =1 то, y2=F(i+1) y1=F(i) и x2-x1=1

втом то и дело, что я реализовал, но график производной - прямая

т.е. разница приращения y2-y1 всего 1 пиксель

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


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

значит ошибка где то в коде, т.к. график у вас получился правильный и там видно, что 2 участка где ф-ия почти не возрастает (по бокам), и "лесенка" (в середине).

попробуйте еще шаг поменять, т.е. шаг =step то, y2=F(i+step) y1=F(i) и x2-x1=step, хотя не думаю, что это поможет.

по большому счету вы уже сейчас можете определить границы таблички.

взять какой то отступ(в % или константой), и пройтись от начала т.е. до точки 0+отступ (это будет начало таблички) и от конца до точки конец-отступ (это будет конец таблички).

вообще метод кажется не особо надежным, обычно делают детектор движения(определяют движущийся объект-машину)+ Канни(или что то похожее) + поиск прямоугольника(таблички).

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


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

значит ошибка где то в коде, т.к. график у вас получился правильный и там видно, что 2 участка где ф-ия почти не возрастает (по бокам), и "лесенка" (в середине).

попробуйте еще шаг поменять, т.е. шаг =step то, y2=F(i+step) y1=F(i) и x2-x1=step, хотя не думаю, что это поможет.

по большому счету вы уже сейчас можете определить границы таблички.

взять какой то отступ(в % или константой), и пройтись от начала т.е. до точки 0+отступ (это будет начало таблички) и от конца до точки конец-отступ (это будет конец таблички).

вообще метод кажется не особо надежным, обычно делают детектор движения(определяют движущийся объект-машину)+ Канни(или что то похожее) + поиск прямоугольника(таблички).

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

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


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

Значения функции и её производной, в данном случае лучше считать в действительных числах (float), т.к. оно действительно получается небольшим. Производную лучше брать на участке хотя-бы 10 пикселей длинной тогда она получится точнее (до 0.1). Слишком большие длины приведут к потере точности локализации номера. Так что выбирайте оптимум.

ЗЫ: Это все равно что сначала сгладить функцию, а потом искать производную.

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


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

Нашел ошибку в реализации


void GetFirstDiff(const CArray<_Line>& arSrc, CArray<_Line>& arDest)

{

        arDest.RemoveAll();     

        for (int nPosY = 0; nPosY < arSrc.GetCount(); nPosY++)

        {

                _Line line;

                line.SetSize(arSrc[nPosY].GetSize());

                line.m_nYPosition = arSrc[nPosY].m_nYPosition;          

                for (int nPosX = 0; nPosX < arSrc[nPosY].GetSize() - 2; nPosX++)

                {

                        int nDiff = 0;

                        int nY = arSrc[nPosY].m_nYPosition;

                        if(nPosX + 1 > arSrc[nPosY].GetSize())

                                return;

                        int ndY = (nY - arSrc[nPosY].m_arData[nPosX + 1]) - (nY - arSrc[nPosY].m_arData[nPosX]);

                        int ndX = nPosX + 1 - nPosX;  //Можно и не делить

                        if(ndX)                       //Так как шаг всегда 1

                                nDiff = ndY / ndX;


                        line.m_arData[nPosX] = nDiff;

                }

                arDest.Add(line);

        }

}

да сейчас придется поработать над сглаживанием

post-1451-0-08639000-1310626694_thumb.jp

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


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

Только int-ы на float или double поменяйте, иначе ничего гладкого не получится.

С целочисленной арифметикой надо очень аккуратно работать иначе очень долго можно потом ошибку искать.

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


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

Только int-ы на float или double поменяйте, иначе ничего гладкого не получится.

С целочисленной арифметикой надо очень аккуратно работать иначе очень долго можно потом ошибку искать.

Вот результат того же алгоритма но с вещественными значениями, имхо с интами было лучше)

ЗЫ вывод надо менять алгоритм сглаживания.

post-1451-0-59973400-1310628310_thumb.jp

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


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

А CArray<int> m_arData; тоже на float поменяли? (int nSmooth, кстати тоже)

Да, и фильтр можно считать попроще:

AvgX=(I+AvgX)/(i+1);

где AvgX - результат усреднения, I - усредняемая величина, i - количество точек по которым считается фильтр.

Cumulative Average (CA) в материале wiki

ЗЫ: 5 точек, маловато, я думаю надо примерно от четверти до половины номера в длину брать.

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


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

ЗЫ: 5 точек, маловато, я думаю надо примерно от четверти до половины номера в длину брать.

так а как определить длинну номера тогда?

К томуже если при движение размеры плитки будут меняться?

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


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

Вероятно нужно принять минимальные и максимальные возможные размеры номера и проводить несколько проверок с разными параметрами фильтра.

А еще можно пирамиду сделать и по ней искать.

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


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

на графике производной где первый сильный скачок там начало номера, где последний конец номера.

К томуже если при движение размеры плитки будут меняться?

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

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


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

даже обычный пример из squares.c работает неплохо.

thumb.png

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


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

даже обычный пример из squares.c работает неплохо.

thumb.png

работает то неплохо, но далек от реального времени.

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


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

так тут походу разбора полетов возник вопрос

интенсивность яркости у нас от 0 до 255, значения IplImage::imageData у нас char т.е. от -127 до 128, правильно ли будет для получения функции приобразовывать char к unsigned char? или это надуманно?

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


Ссылка на сообщение
Поделиться на других сайтах
For a multi-channel byte image:

IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);

((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B

((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G

((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R

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


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

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

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

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

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

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

Войти

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

Войти сейчас


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

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

×