mrgloom 242 Report post Posted September 2, 2011 ну я имел ввиду что (abs(H1i-H2i))/(H1i+H2i) Инварианты Ху имеют разную величину. Некоторые примерно равны единице, другие же ближе к 100 или к 100000. В связи с этим обычная Евклидова метрика, нечувствительна к "первым" моментам Ху, поскольку наибольший вклад в метрику дают более большие по модулю компоненты расстояние Махаланобиса - это такая метрика, при которой компоненты вектора взвешиваются по-разному, обратно пропорционально матрице ковариации http://courses.graphicon.ru/main/vision2009 . я все таки думаю что ху моменты довольно грубый метод сравнения.(если сравнивать чистые образы то норм наверно ,а если с шумом или как то деформированные то тут должно быть все хуже) Share this post Link to post Share on other sites
nagoHok 4 Report post Posted September 2, 2011 Автор, ну так что у Вас получилось можете показать пример и исходник? пока все в процессе Сразу попутный вопрос, можно ли произвести фильтрацию изначального изображения КИХ фильтром? Share this post Link to post Share on other sites
DirecTwiX 4 Report post Posted September 3, 2011 ну я имел ввиду что (abs(H1i-H2i))/(H1i+H2i) Лол... У меня эта формула в первом посте) Инварианты Ху имеют разную величину. Некоторые примерно равны единице, другие же ближе к 100 или к 100000. В связи с этим обычная Евклидова метрика, нечувствительна к "первым" моментам Ху, поскольку наибольший вклад в метрику дают более большие по модулю компоненты расстояние Махаланобиса - это такая метрика, при которой компоненты вектора взвешиваются по-разному, обратно пропорционально матрице ковариации http://courses.graphicon.ru/main/vision2009 . Вроде бы они все меньше единицы. Поэтому я вытащил метод сравнения из cvMatchShapes. Там сначала моменты п реобразуются как (1/log(hu)) я все таки думаю что ху моменты довольно грубый метод сравнения.(если сравнивать чистые образы то норм наверно ,а если с шумом или как то деформированные то тут должно быть все хуже) Да... Я наивный поверил чистой теории... И вот во что это вылилось... Если маленько изменить контур, то моменты поменяются координально =\ Сегодня начну писать через скалярное произведение контуров. Share this post Link to post Share on other sites
mrgloom 242 Report post Posted September 5, 2011 Лол... У меня эта формула в первом посте) да чо то не осознал. Вроде бы они все меньше единицы. Поэтому я вытащил метод сравнения из cvMatchShapes. Там сначала моменты п реобразуются как (1/log(hu)) ну да наверно лучше пользоваться ихними готовыми. Share this post Link to post Share on other sites
Smorodov 579 Report post Posted September 5, 2011 Да, они использовали сжимающую функцию. Такие вещи применяют в нейронных сетях для того, чтобы сеть адекватно воспринимала величины различного масштаба. Если интересно, продолжение здесь. 1 Share this post Link to post Share on other sites
DirecTwiX 4 Report post Posted September 5, 2011 Инетересно. Потом почитаю. Спасибо Share this post Link to post Share on other sites
SilAs 0 Report post Posted November 1, 2011 Автор, ну так что у Вас получилось можете показать пример и исходник? Share this post Link to post Share on other sites
striker 0 Report post Posted January 29, 2012 Ага, так он и показал исходник Сейчас двигаюсь в том же направлении, в сторону карты контрастностей. Задумка интересная, однако скорость работы такого метода уж очень маленькая, далеко до real-time. У меня python 2.7 + opencv 2.3 Думаю, язык программирования тут ни при чем, ведь на каждом языке там будет перебор width*height пикселей Как это всё дело можно ускорить? Share this post Link to post Share on other sites
Nuzhny 243 Report post Posted January 29, 2012 Сначала надо узнать, что тормозит. Язык может быть причём, так как в С/С++ можно получать значения пикселей прямым проходом по памяти. Share this post Link to post Share on other sites
striker 0 Report post Posted January 30, 2012 Сначала надо узнать, что тормозит. Язык может быть причём, так как в С/С++ можно получать значения пикселей прямым проходом по памяти. Что значит "прямым переходом по памяти"? Изображение уже загружено в память как матрица width*height и от перебора всех пикселей изображения никуда не уйти. Или я что-то упускаю из виду? Я пока что придумал только ресайз изображения, но скорость все равно маленькая. Значит ли это, что этот метод не подходит для real-time? Share this post Link to post Share on other sites
mrgloom 242 Report post Posted January 30, 2012 что такое карта контрастности? что просто поиск рамки не работает в риалтайме? Share this post Link to post Share on other sites
Nuzhny 243 Report post Posted January 30, 2012 Что значит "прямым переходом по памяти"? Изображение уже загружено в память как матрица width*height и от перебора всех пикселей изображения никуда не уйти. Или я что-то упускаю из виду? Чтобы последовательно пройти по всем пикселям изображения в С/С++ можно написать такой код: for (uchar* pbuf = (uchar*)img->imageData, *pend = (uchar*)(img->imageData + img->imageSize); pbuf != pend; pbuf += img->nChannels) { func(pbuf); } Всё будет работать быстро: 1. обращения к памяти последовательны, а не случайны; 2. переход к следующему пикселю производится всего за одну операцию сложения (pbuf += img->nChannels). Или можно делать обращения к каждому пикселю с помощью функции: for (int y = 0; y < img->height; ++y) { for (int x = 0; x < img->width; ++x, ++pl) { func(cvGet2D(img, y, x)); } } В этому случае будет каждый раз: 1. вызываться проверка на попадание координат в пределы изображения; 2. высчитываться адрес пикселя в памяти с помощью 2 умножений и сложения (img->imgData + y * img->widthStep + x * img->nChannels); 3. не будет известно, что обращения к памяти последовательны, поэтому процессор может не оптимизировать подгрузку памяти в кэш. Если ты в Питоне проходишь по изображению, то вызовется питон-аналог именно второго варианта, который будет намного медленней рукописного первого. Если ты из Питона вызываешь какую-нибудь функцию из OpenCV, то внутри себя она будет работать быстро, по первому варианту. Я пока что придумал только ресайз изображения, но скорость все равно маленькая. Значит ли это, что этот метод не подходит для real-time? Нет, не значит. Надо сперва провести профилирование. 1 Share this post Link to post Share on other sites
striker 0 Report post Posted January 30, 2012 что такое карта контрастности? что просто поиск рамки не работает в риалтайме? Насколько я понял, это тот метод, которым воспользовался nagoHok для локализации номера. Суть такова, что номерной знак автомобиля имеет резкие переходы от черного к белому и наоборот. Строим вертикальные и горизонтальные гистрограммы и выбираем зоны наибольших скачков. Вроде так. Если что, поправьте меня. А как предлагаете искать рамку? Я уже пробовал искать параллелограммы в контурах изображения с помощью Canny и findContours, однако точность такого метода оставляет желать лучшего, по крайней мере, в моем исполнении. Чтобы последовательно пройти по всем пикселям изображения в С/С++ можно написать такой код: Здорово! Спасибо за информацию. Узнаю у гуру питона, есть ли у них что-нибудь подобное и в случае чего всё-таки откажусь от питон обертки Share this post Link to post Share on other sites
mrgloom 242 Report post Posted January 30, 2012 из примера squares.c через поиск линий. для гистограмм есть cv::calcHist Share this post Link to post Share on other sites
Pavia00 32 Report post Posted January 30, 2012 Язык, как раз очень при чём. Дело в том, что на циклах как раз и проявляется тормознуть языка. Интерпритируемые языках тормазили бы жутко, если бы не замена циклов на функции из библиотек, в которых эти функции оптимизированны. В результате тормоза языка а вернее компилятора/интерпретатора будут не всегда заметны. Не очень понял о коком алгоритме идёт речь. Но для поиска контрастных пикселей требуется 4 прохода по изображению. Все проходы простые. Ориентировочно скорость 50 мс. Исходное изображение в градациях серого. Находим 2 вспомогательных изображения первое сглаженное. Второе возводим в квадрат и сглаженное. И четвёртый проход нужен чтобы посчитать дисперсию и сравнить с некоторым порогом. Дисперсия получается путём вычитания из второго первое. Там где дисперсия большая там контрастность большая Share this post Link to post Share on other sites
mrgloom 242 Report post Posted January 31, 2012 http://opencv.itseez.com/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#howtoscanimagesopencv про доступ к пикселям в новом интерфейсе. Share this post Link to post Share on other sites
striker 0 Report post Posted January 31, 2012 из примера squares.c через поиск линий. для гистограмм есть cv::calcHist Поиск контуров не всегда работает хорошо Про гистограммы я Вас ввел в заблуждение. В этом методе строятся не просто гистограммы, а гистограммы изменений градаций ЧБ изображения. Грубо говоря, насколько X пиксель темнее/светлее X+1 пикселя. Вот та зона, где количество и "сила" таких скачков велика, там теоретически находится наш номерной знак Share this post Link to post Share on other sites
striker 0 Report post Posted January 31, 2012 Не очень понял о коком алгоритме идёт речь. Но для поиска контрастных пикселей требуется 4 прохода по изображению. Все проходы простые. Ориентировочно скорость 50 мс. Исходное изображение в градациях серого. Находим 2 вспомогательных изображения первое сглаженное. Второе возводим в квадрат и сглаженное. И четвёртый проход нужен чтобы посчитать дисперсию и сравнить с некоторым порогом. Дисперсия получается путём вычитания из второго первое. Там где дисперсия большая там контрастность большая а есть где почитать/посмотреть про данный метод? Или, примеры кода? Не очень ясно, о чем речь Share this post Link to post Share on other sites
mrgloom 242 Report post Posted February 1, 2012 Поиск контуров не всегда работает хорошо там не поиск контуров, а поиск линий, да он не всегда работает хорошо, но можно отфильтровать ложные прямоугольники. Грубо говоря, насколько X пиксель темнее/светлее X+1 пикселя. Вот та зона, где количество и "сила" таких скачков велика, там теоретически находится наш номерной знак ну это можно оператор собеля (cvSobel) применить и найти "максимальные скачки". Share this post Link to post Share on other sites
striker 0 Report post Posted February 1, 2012 там не поиск контуров, а поиск линий, да он не всегда работает хорошо, но можно отфильтровать ложные прямоугольники. Посмотрю этот пример, думал там то же, что и в squares.py. На питоне там поиск контуров, апроксимация их до прямоугольников ну это можно оператор собеля (cvSobel) применить и найти "максимальные скачки". а в чем смысл тогда лишнюю операцию делать? Share this post Link to post Share on other sites
mrgloom 242 Report post Posted February 1, 2012 операция не лишняя, а эквивалентная, можно не делать для всех пикселей, а взять полоски через одну например, хотя и без этого все быстро. Share this post Link to post Share on other sites
slicktail 1 Report post Posted February 25, 2012 Продолжу тему. В OpenCV реализованы моменты Ху. Насколько я выяснил: "Продолжением было опробование других множеств полиномов в качестве базисных функций. Ч. Х. Тех и Р. Т. Чин предложили в качестве базисных функций использовать ортогональные полиномы псевдо- Зернике и полиномы Лежандра [Teh & Chin, 1988], Ю. Шенг и Л. Шен ввели ортогональные моменты Фурье-Меллина [sheng & Shen, 1994], Р. Мукундан и др. предложили моменты на основе полиномов Чебышева [Munkundan et. al., 2001], П.Т. Яп и др. предложили использовать моменты на основе полиномов Кравчука [Yap et. al, 2003], Х.К. Жу и др. ввели в обращение моменты Рака [Zhu et. al., 2007]." Источник Ссылка Вот человек пытается реализовать моменты Зернике в OpenCV: Ссылка Насколько это правильно, реализовывать для своей задачи методы моментов, приведенные выше? Моя подзадача стоит именно в новизне (хоть какой-то) моментных методов, применительно к нахождению инвариантного ансамбля векторов движения. Грубо говоря, машинка на видеопоследовательности повернула на какой-то угол, а тут я кадр проанализировал моментами, и отследил ее. Вопрос, стоит ли реализовывать существующие новые методы моментов в OpenCV, если да, то какие? 1 Share this post Link to post Share on other sites
nagoHok 4 Report post Posted March 14, 2012 Оч сырой код, но более 50% локализует, предлагаю довести его до ума) #include "stdafx.h" #include "ContrastMap.h" #pragma warning (disable:4996) #include <cv.h> #include <opencv2\highgui\highgui_c.h> #include <math.h> #pragma comment(lib,"opencv_core220d.lib") #pragma comment(lib,"opencv_highgui220d.lib") #pragma comment(lib,"opencv_imgproc220d.lib") #pragma comment(lib,"opencv_video220d.lib") #pragma comment(lib,"opencv_legacy220d.lib") #ifdef _DEBUG #define new DEBUG_NEW #endif // The one and only application object CWinApp theApp; using namespace std; class CBlock { public: int m_nStartPoin; int m_nEndPoint; int m_nBlockLen; CBlock(void) { m_nStartPoin = 0; m_nEndPoint = 0; m_nBlockLen = 0; } virtual ~CBlock(void) { } void Empty() { m_nStartPoin = 0; m_nEndPoint = 0; m_nBlockLen = 0; } bool IsNull(){return m_nBlockLen ? false : true;} }; void SummRow(const IplImage* pSrc, CArray<double>& arRow) { arRow.RemoveAll(); arRow.SetSize(pSrc->height); for (int nPosY = 0; nPosY < pSrc->height; nPosY++) { int nOffset = pSrc->widthStep * nPosY; for (int nPosX = 0; nPosX < pSrc->widthStep; nPosX++) { arRow[nPosY] += (uchar)pSrc->imageData[nOffset + nPosX]; } } } void DrawGraf(IplImage* pSrc, const CArray<double>& arRow) { CvPoint ptLastPoint = cvPoint(0, 0); for (int nPos = 0; nPos < arRow.GetCount(); nPos++) { cvDrawLine(pSrc, ptLastPoint, cvPoint((int)arRow[nPos] / 200, nPos) , cvScalar(0), 1); ptLastPoint = cvPoint((int)arRow[nPos] / 200, nPos); } } void AplyFilter(const IplImage* pSrc, IplImage* pDest) { double arMask[9] = {0,0,0,0,0.5,0,0,0,0}; //double arSharp[9] = {0.1111, -0.8889, 0.1111, -0.8889, 4.1111, -0.8889, 0.1111, -0.8889, 0.1111}; CvMat matMask = cvMat(3, 3, CV_64FC1, arMask); cvFilter2D(pSrc, pDest, &matMask, cvPoint(-1,-1)); } void AplySobel(const IplImage* pScr, IplImage* pDest) { IplImage* pDst = cvCreateImage( cvSize(pScr->width, pScr->height), IPL_DEPTH_16S, pScr->nChannels); // применяем оператор Собеля cvSobel(pScr, pDst, 1, 0, 3); // преобразуем изображение к 8-битному cvConvertScale(pDst, pDest); cvReleaseImage(&pDst); } inline double GSmooth(double nDist, double nParam, double nParam2) { return exp((-(nDist * nDist)/nParam)) / nParam2; } void GauseSmooth(const CArray<double>& arSrc, CArray<double>& arDest) { arDest.RemoveAll(); double nParam = 3.; int nAperture = 5; double nParam1 = 2* (nParam * nParam); double nParam2 = sqrt(2 * CV_PI) * nParam; arDest.SetSize(arSrc.GetCount()); for (int nPos = 0; nPos < arSrc.GetCount() - 1; nPos++) { double s = 0.; for (int nK = max(nPos - nAperture, 0); nK < min(arSrc.GetCount() - 1, nPos + nAperture); nK++) { s += arSrc[nK] * GSmooth(abs(nPos - nK), nParam1, nParam2); } arDest.SetAt(nPos, s); } } void Func(IplImage* pImg, int nPosY, CArray<double>& arFunc) { arFunc.RemoveAll(); double _f = 0.; int nLinePos = nPosY * pImg->widthStep; arFunc.SetSize( pImg->widthStep - 1); for(int nPos = 0; nPos < pImg->widthStep - 1; nPos++) { _f += abs(pImg->imageData[nLinePos + nPos + 1] - pImg->imageData[nLinePos + nPos]); arFunc.SetAt(nPos, _f); } } void GetFirstDiff(const CArray<double>& arSrc, CArray<double>& arDest) { CArray<double> arTmp; arTmp.SetSize(arSrc.GetCount()); for (int nPos = 1; nPos < arSrc.GetCount(); nPos++) { if(nPos + 1 > arSrc.GetCount() - 1) continue; double ndY = arSrc[nPos + 1] - arSrc[nPos]; arTmp.SetAt(nPos, ndY); } arDest.RemoveAll(); arDest.Copy(arTmp); arTmp.RemoveAll(); } int GetMaxPosition(CArray<double>& arGraf) { int nMax = 0; for (int nPos = 0; nPos < arGraf.GetCount(); nPos++) { if(arGraf[nPos] > arGraf[nMax]) nMax = nPos; } return nMax; } void GetLocalMinMax(const CArray<double>& arData, CArray<int>& arPositionsMin, CArray<int>& arPositionsMax) { int nCount = 0; double nMin = 0, nMax = 0; bool bIsMaxF = false; bool bIsMinF = false; for(int nPos = 1; nPos < arData.GetCount() - 1; nPos++) { //Отслеживаем уменьшение. if ((nPos == 1) || (arData[nPos - 1] < arData[nPos])) { nMax = arData[nPos]; bIsMaxF = true; } if ((nPos == 1) || (arData[nPos - 1] > arData[nPos])) { nMin = arData[nPos]; bIsMinF = true; } //Если ранее было обнаружено уменьшение. if(bIsMaxF) { //Отслеживаем увеличение. if ((nPos == arData.GetCount() - 1) || (arData[nPos - 1] > arData[nPos])) { //Очередной локальный max найден. arPositionsMax.Add(nPos); nCount++; if(nCount == 1) nMin = nMax; else if(nMax < nMin) nMin = nMax; bIsMaxF = false; } } if(bIsMinF) { //Отслеживаем увеличение. if ((nPos == arData.GetCount() - 1) || (arData[nPos - 1] < arData[nPos])) { //Очередной локальный минимум найден. arPositionsMin.Add(nPos); nCount++; if(nCount == 1) nMax = nMin; else if(nMin > nMax) nMax = nMin; bIsMinF = false; } } } } void CalcBlocks(int nMinBlockLen, CArray<CBlock>& m_arWidthBlock, double nAvVal, const CArray<double>& arData) { m_arWidthBlock.RemoveAll(); CBlock block; int nSpaces = 0; for (int nPos = 0; nPos < arData.GetCount(); nPos++) { if(arData[nPos] >= nAvVal) { if(block.m_nStartPoin) { block.m_nBlockLen++; nSpaces = 0; } else { block.m_nStartPoin = nPos; block.m_nBlockLen++; nSpaces = 0; } } else { if(block.m_nStartPoin) { if(nSpaces >= nMinBlockLen) { block.m_nEndPoint = nPos - nSpaces; block.m_nBlockLen -= nSpaces; nSpaces = 0; m_arWidthBlock.Add(block); block.Empty(); continue; } nSpaces++; } } } if(block.m_nStartPoin) { block.m_nEndPoint = arData.GetCount() - nSpaces; m_arWidthBlock.Add(block); } } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { IplImage* pFrame = NULL; //Захваченый фрейм IplImage* pGray = NULL; //Фрейм приведенный к серому цвету IplImage* pGrath = NULL; //Тут рисуем графики CFileFind finder; //BOOL bWorking = finder.FindFile(_T("c:/Utils/Работа/Мое/Распознание/Саша/Day/*.*")); BOOL bWorking = finder.FindFile(_T("C:/Users/Я/Desktop/Avto-Control Demo/Images/Day/*.*")); while (bWorking) { bWorking = finder.FindNextFile(); if(!bWorking) break; CStringA strFileName(finder.GetFilePath()); if(finder.IsDots()) continue; TRACE(strFileName.GetString()); TRACE(_T("\n")); pFrame = cvLoadImage(strFileName); if(!pFrame) continue; DWORD dwStart = GetTickCount(); cvSmooth(pFrame, pFrame, CV_GAUSSIAN, 3, 3); //Создаем серое изображение pGray = cvCreateImage(cvGetSize(pFrame), 8, 1); cvConvertImage(pFrame, pGray, CV_BGR2GRAY); //Выравниваем яркость изображения cvEqualizeHist(pGray, pGray); CArray<double> arGraf; AplySobel(pGray, pGray); SummRow(pGray, arGraf); int nMaxPos = GetMaxPosition(arGraf); CArray<double> arFunc; Func(pGray, nMaxPos, arFunc); CArray<double> arSmooth; //GauseSmooth(arFunc, arSmooth); CArray<double> arDif; GetFirstDiff(arFunc, arDif); CArray<int> arMins; CArray<int> arMaxs; GetLocalMinMax(arDif, arMins, arMaxs); int nMaxFromMax = GetMaxPosition(arDif); double nAvValMax = arDif[nMaxFromMax] * 0.2; CArray<CBlock> arBlocks; CalcBlocks(20, arBlocks, nAvValMax, arDif); /* for (int nPos = 0; nPos < arMaxs.GetCount(); nPos++) { nAvValMax += arDif[arMaxs[nPos]]; }*/ //nAvValMax = nAvValMax / arMaxs.GetCount(); //nAvValMax = nAvValMax + nAvValMax * 0.2; pGrath = cvCreateImage(cvGetSize(pFrame), 8, 1); DrawGraf(pFrame, arGraf); CvPoint ptLast = cvPoint(0, nMaxPos); int nCount = 0; // for (int nPos = 0; nPos < arDif.GetCount(); nPos++) // { // //if(arDif[nPos] < nAvValMax) // //{ // // //cvDrawLine(pGrath, cvPoint(nPos, nMaxPos), cvPoint(nPos, nMaxPos - (int)(arDif[nPos])), cvScalar(0), 1); // // continue; // //} // // nCount++; // cvDrawLine(pFrame, cvPoint(nPos, nMaxPos), cvPoint(nPos, nMaxPos + (int)(arDif[nPos] / 5)), cvScalar(0), 1); // ///* // int nPosY = (nMaxPos + (int)arDif[nPos] / 5); // cvDrawLine(pGrath, ptLast, cvPoint(nPos, nPosY), cvScalar(0), 1); // ptLast = cvPoint(nPos, nPosY); //*/ // } for (int nPos = 0; nPos < arBlocks.GetCount(); nPos++) { CBlock &block = arBlocks[nPos]; int nHigh = (int)(block.m_nEndPoint - block.m_nStartPoin) / 9.2; if(arBlocks[nPos].m_nBlockLen < 10 || arBlocks[nPos].m_nBlockLen > 100) continue; cvDrawRect(pFrame, cvPoint(arBlocks[nPos].m_nStartPoin - 10, nMaxPos - nHigh), cvPoint(arBlocks[nPos].m_nEndPoint + 10, nMaxPos + nHigh), cvScalar(255), 2); } std::cout << nCount << " " << GetTickCount() - dwStart << std::endl ; cvNamedWindow("Graf"); cvShowImage("Graf", pFrame); cvReleaseImage(&pGray); cvReleaseImage(&pFrame); cvReleaseImage(&pGrath); char c = cvWaitKey(0); if (c == 27) { break; } } return 0; } 1 Share this post Link to post Share on other sites
Red Green Blue 0 Report post Posted April 1, 2012 Друзья, а у Вас есть какая-либо выборка состоящая из изображений авто с номерными знаками? хотяб пару сотен обучающих изображений? Share this post Link to post Share on other sites
Smorodov 579 Report post Posted April 1, 2012 нет, но собрать не сложно: в Google поиск картинок набираем "номерной знак автомобиля" и получаем много автомобильных номеров. Правда без ручного труда тут не получится обойтись. Share this post Link to post Share on other sites