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

подготовка изображения к преобразованию Хафа

Recommended Posts

Пытаюсь выделить линии на изображении методом Хафа(cvHoughLines2)

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

есть идея проводить бинаризацию cvThreshold.

так вот вопрос как после бинаризации убрать мусор (объекты слишком маленькие\большие элементы по кол-ву пикселей или слишком длинные\короткие по максимальной длине)?

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

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

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

image.png

  • Like 1

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


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

cvErode

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


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

cvErode не подходит,т.к. во первых не угадаешь число требуемых итераций ,а во вторых, т.к. он делает линии разрывными ,т.к. они имеют не одинаковую толщину по всей длине.

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


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

А что за изображение? Если на нем контуры, то можно найти контуры, и проводить их анализ.

Что касается второй части, то существует обобщенное преобразование Хафа (Generalised Hough transform), позволяющее найти любые кривые, только кривые эти надо предварительно параметризовать. И делать это надо руками, встроенной функции нет.

Вот ссылка с теорией: http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm

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


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

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

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

image.png

вообщем если возможно рассматривать такой объект как контур и получить такие характеристики то все норм, а так я уже смотрю в сторону блобов (а в opencv они сделаны отдельно? http://opencv.willowgarage.com/wiki/cvBlobsLib или http://code.google.com/p/cvblob/)

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

вообщем все упирается в вопрос как сделать проще и быстрее либо Хафом, либо блобы, либо как то еще.

Что касается второй части, то существует обобщенное преобразование Хафа (Generalised Hough transform), позволяющее найти любые кривые, только кривые эти надо предварительно параметризовать. И делать это надо руками, встроенной функции нет.

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

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


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

еще вопрос может есть какая то морфологическая операция чтобы убрать маленькие элементы на бинарном изображении? и при это не затронуть большие объекты?

пример на увеличении(соответственно весь мусор надо убрать, полоски оставить)

image.png

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


Ссылка на сообщение
Поделиться на других сайтах
еще вопрос может есть какая то морфологическая операция чтобы убрать маленькие элементы на бинарном изображении? и при это не затронуть большие объекты?

Я думаю вашу задачу можно решить с помощью операции размыкания (англ OPEN). В общем случае она сглаживает контуры, обрывает узкие перешейки и ликвидирует выступы небольшой ширины. Фактически размыкание это дилатация эрозии.

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

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


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

как-то так...


int an=4;

IplConvKernel* element = cvCreateStructuringElementEx(an*an+1, an*an+1, (an*an+1)/2, (an*an+1)/2, CV_SHAPE_ELLIPSE);

cvMorphologyEx(image,image,NULL,element,CV_MOP_OPEN,1);

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


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

хмм это может быть критично.

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

нельзя ли сделать такую эрозию чтобы скажем есть объект и он по краям объедается по 1 пикселю и так чтобы по середине осталась толщина в 1 пиксель?

(хотя я тут возможно не учел, что если так объедать скажем вертикальный или горизонтальный объект, то получим правильный его "скелет" просто линию, а если какой то наклонный скажем овал с бугристыми краями то мы не сможем получить "правильную" наклонную линию)

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

и еще дополнительно как убрать маленькие элементы не затронув больших линий? (выделено на картинке)

thumb.png

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


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

можно, это называется скелетизация. Обсуждали здесь http://www.compvision.ru/forum/index.php?showtopic=30&st=0&

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

Как я уже сказал, зависит от того, какой резултат вы хотите получить. Объекты размер которых больше ядра удалены не будут. С формой ядра сложнее, зависит от обработываемых блобов, думаю нужно поэксперементировать.

и еще дополнительно как убрать маленькие элементы не затронув больших линий?

Морфологически никак. Придется выделять блобы и считать площадь каждого

  • Like 1

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


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

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

http://www.mathworks.com/help/toolbox/images/ref/bwareaopen.html

использую

// Function to remove small blobs from the binary image

IplImage* remove_small_objects( IplImage* img_in, int size )

{

    IplImage* img_out       = cvCloneImage( img_in );  // return image

    CvMemStorage* storage   = cvCreateMemStorage( 0 ); // container of retrieved contours

    CvSeq* contours         = NULL;

    CvScalar black          = CV_RGB( 0, 0, 0 ); // black color

    CvScalar white          = CV_RGB( 255, 255, 255 );   // white color

    double area=0;


    // find contours in binary image

    cvFindContours( img_in, storage, &contours, sizeof( CvContour ), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE );  


    while( contours )   // loop over all the contours

    {

        area = cvContourArea( contours, CV_WHOLE_SEQ );

        if( fabs( area ) <= size )  // if the area of the contour is less than threshold remove it

        {

            // draws the contours into a new image

            cvDrawContours( img_out, contours, black, black, -1, CV_FILLED, 8 ); // removes white dots

        }

		//else

  //      {

  //          cvDrawContours( img_out, contours, white, white, -1, CV_FILLED, 8 ); // fills in holes

  //      }

        contours = contours->h_next;    // jump to the next contour

    }


    cvReleaseMemStorage( &storage );

    return img_out;

}
IplImage* img = cvLoadImage("img.bmp", 1);

IplImage* grey = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);

	cvCvtColor(img, grey, CV_BGR2GRAY);

cvThreshold(grey, grey, 100, 255, CV_THRESH_OTSU);

IplImage* out= remove_small_objects(grey,20);

cvSaveImage("img.png",img);

cvSaveImage("grey.png",grey);

cvSaveImage("out.png",out);

получаю результат.

исходное

image.png

грей (но испорченное после процедуры)

image.png

конечное

image.png

во-первых непонятно можно ли выбирать связность компонентов используя cvFindContours (и какая там используется по умолчанию?)

как данный алгоритм умудряется "выедать" дополнительно пиксели в буквах e,a?

почему портится изображение которое подается в remove_small_objects?

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


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

По поводу связности: связность определяется младшими битами слова флагов. Может быть 4 или 8.

flags

The operation flags. Lower bits contain connectivity value, 4 (by default) or 8, used within the function.

А если здесь вместо black, black поставить что то другое (разные цвета заполнения и контура), то скорее всего прояснится проблема "выедания".

cvDrawContours( img_out, contours, black, black, -1, CV_FILLED, 8 ); // removes white dots

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


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

кстати у меня теперь почему то

 IplImage* img_out= cvCreateImage(cvSize(img_in->width,img_in->height), IPL_DEPTH_8U, 3);

выдает

image.png

приходится делать cvZero(img_out);

возможно это как то связано с тем что бьется изображение.

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

image.png

image.png

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


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

Первая картинка вполне нормальная, система выделяет память, но никто не говорит что она заполнена нулями.

Что касается второго, то можно попробовать использовать иерархию контуров, как в примере contours с рожицами.

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


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

можно еще смотреть наверно boundingRect на пересечение\вхождение, хотя это наверно медленней чем иерархия контуров.

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

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


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

еще вопрос про морфологию

как сделать так чтобы удалялись только горизональные перешейки, а вертикальные не трогались?

вертикальные перешейки

image.png

горизонтальные перешейки

image.png

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


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

еще вопросы

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

т.е. более интеллектуальная бинаризация.

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

image.png

пробовал находить моменты Ху и искать тела похожие на прямоугольники

CvMoments moments;

		CvHuMoments huMoments;

		cvMoments( contours, &moments );

		cvGetHuMoments( &moments, &huMoments );


		//только прямоугольные области

		//порог

		int thresh= 100;

		int hu1= (int)(huMoments.hu1*thresh);

		int hu2= (int)(huMoments.hu2*thresh);

		int hu3= (int)(huMoments.hu3*thresh);

		int hu4= (int)(huMoments.hu4*thresh);

		int hu5= (int)(huMoments.hu5*thresh);

		int hu6= (int)(huMoments.hu6*thresh);

		int hu7= (int)(huMoments.hu7*thresh);


		if(hu1>0&&

			hu2>0&&

			hu3==0&&

			hu4==0&&

			hu5==0&&

			hu6==0&&

			hu7==0)

                {...}

но то ли я не правильно делаю, то ли метод сам не точный, в зависимости от thresh выдает совсем уж различные результаты.

хотя возможно я не правильно понял у прямоугольника Ху первые 2 момента должны быть положительны, а остальные по нулям?

3.хотелось бы уметь разрешать такие ситуации,т.е. видно что много маленьких разрывных участков составляют линию, как их соединить в одно? по какому критерию?

image.png

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


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

матлабовский bwareaopen на opencv

/**

* OpenCV equivalent of Matlab's bwareaopen.

* image must be 8 bits, 1 channel, black and white (objects)

* with values 0 and 255 respectively

*/

void bwareaopen(IplImage* image, int size)

{

CvMemStorage *storage;

CvSeq *contour;

IplImage *input;

double area;

if (image == NULL || size == 0)

return;

input = cvCloneImage(image);

storage = cvCreateMemStorage(0);

cvFindContours(input, storage, &contour, sizeof (CvContour),

CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

while(contour)

{

area = cvContourArea(contour, CV_WHOLE_SEQ, 1);

if (-size <= area && area <= 0)

{

// removes white dots

cvDrawContours(image, contour, CV_RGB(0,0,0), CV_RGB(0,0,0),

-1, CV_FILLED, 8, cvPoint(0, 0));

}

else if (0 < area && area <= size)

{

// fills in black holes

cvDrawContours(image, contour, CV_RGB(0xff,0xff,0xff),

CV_RGB(0xff,0xff,0xff), -1, CV_FILLED, 8, cvPoint(0,0));

}

contour = contour->h_next;

}

cvReleaseMemStorage(&storage);

cvReleaseImage(&input);

}

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


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

матлабовский bwareaopen на opencv

/**

* OpenCV equivalent of Matlab's bwareaopen.

* image must be 8 bits, 1 channel, black and white (objects)

* with values 0 and 255 respectively

*/

void bwareaopen(IplImage* image, int size)

{

CvMemStorage *storage;

CvSeq *contour;

IplImage *input;

double area;

if (image == NULL || size == 0)

return;

input = cvCloneImage(image);

storage = cvCreateMemStorage(0);

cvFindContours(input, storage, &contour, sizeof (CvContour),

CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

while(contour)

{

area = cvContourArea(contour, CV_WHOLE_SEQ, 1);

if (-size <= area && area <= 0)

{

// removes white dots

cvDrawContours(image, contour, CV_RGB(0,0,0), CV_RGB(0,0,0),

-1, CV_FILLED, 8, cvPoint(0, 0));

}

else if (0 < area && area <= size)

{

// fills in black holes

cvDrawContours(image, contour, CV_RGB(0xff,0xff,0xff),

CV_RGB(0xff,0xff,0xff), -1, CV_FILLED, 8, cvPoint(0,0));

}

contour = contour->h_next;

}

cvReleaseMemStorage(&storage);

cvReleaseImage(&input);

}

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


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

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

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

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

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

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

Войти

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

Войти сейчас


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

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

×