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

Работа с контурами

Recommended Posts

Без кода сказать ничего не могу.

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


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

Мне пришлось переписать данный код на C# ввиду требования руководителя. Вот именно на C# у меня вывело ContourArea = -4.

if(Math.Abs(Cv.ContourArea(contours))>20)

{

Contour.DrawContours(contours, CvColor.White, CvColor.White, 2, Cv.FILLED, LineType.Link8);

Console.WriteLine(cv.ContourArea(contours);

}

Но в данной ситуации рисуются все контуры абсолютно(включая маленькие). Может нужно как-то посчитать площадь каждого контура, чтобы потом отсеять?

А на C код у меня выглядел вот так, но он рушится на условии if(abs(cvContourArea(storage))>20).

#include "stdafx.h"

#include <stdio.h>

#include <stdlib.h>

#include <cv.h>

#include <highgui.h>

int main(int argc, char* argv[])

{

//Создаём 3 изображения

IplImage* sign = cvLoadImage("D:\\1.jpg",3);

IplImage* sign_hsv=cvCreateImage(cvGetSize(sign),IPL_DEPTH_8U,3);

IplImage* sign_bin=cvCreateImage(cvGetSize(sign),IPL_DEPTH_8U,1);

IplImage* canny = cvCreateImage(cvGetSize(sign),IPL_DEPTH_8U,1);

IplImage* mask = cvCreateImage(cvGetSize(sign),IPL_DEPTH_8U,1);

//Создание маски

cvSet(mask,cvScalar(0,0,0));

//Преобразуем исходное изображение в градации серого

cvCvtColor(sign,sign_hsv,CV_BGR2HSV);

//Проводим бинаризацию полученного изображения(преобразуем в двоичное)

cvInRangeS(sign_hsv, cvScalar(170,100,100), cvScalar(179,255,255), sign_bin);

cvCanny(sign_bin,canny,50,255,5);

cvSmooth(canny,canny,CV_BLUR,2,2);

//Хранилище памяти для контуров

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* contours=0;

//Поиск контура

cvFindContours(canny,storage,&contours,sizeof(CvContour),CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

if(abs(cvContourArea(storage))>20)

{

// рисуем найденный контур НА маске и заливаем его белым

cvDrawContours(mask,contours, CV_RGB(255,255,255),CV_RGB(255,255,255), 2, CV_FILLED, CV_AA, cvPoint(0,0));

}

cvNamedWindow("Original",CV_WINDOW_AUTOSIZE);

cvNamedWindow("Gray",CV_WINDOW_AUTOSIZE);

cvNamedWindow("Bin",CV_WINDOW_AUTOSIZE);

cvNamedWindow("Canny",CV_WINDOW_AUTOSIZE);

cvNamedWindow("Mask",CV_WINDOW_AUTOSIZE);

cvShowImage("Original",sign);

cvShowImage("Gray",sign_hsv);

cvShowImage("Bin",sign_bin);;

cvShowImage("Сanny",canny);

cvShowImage("Mask",mask);

cvWaitKey(0);

cvReleaseImage(&sign);

cvReleaseImage(&sign_hsv);

cvReleaseImage(&sign_bin);

cvReleaseImage(&canny);

cvReleaseImage(&mask);

cvReleaseMemStorage(&storage);

cvDestroyAllWindows();

return 0;

}

Извиняюсь, что такая куча сложностей с переписыванием на разные языки. Чтобы разбираться и в случае чего опытные люди могли помочь, пишу на Cи, а в дальнейшем переписываю на C#, всвязи с требованием проекта...

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


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

Вот кусок моего кода.

Надеюсь поймете где у Вас ошибка:

	vector<vector<Point> > contours;

vector<Vec4i> hierarchy;

findContours(img8U,contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point());

if(contours.size()>0)
{
for( int i = 0; i < contours.size(); i++ )
{
if(contourArea(contours[i])>500)
{
Rect r=cv::boundingRect(contours[i]);
Rects.push_back(r);
}
}
}[/code] есть еще кусок с сишным интерфейсом отсюда: http://robocraft.ru/blog/computervision/640.html
[code]// хранилище памяти для контуров
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours=0;

// находим контуры
int contoursCont = cvFindContours( bin, storage,&contours,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));

assert(contours!=0);

// обходим все контуры
for( CvSeq* current = contours; current != NULL; current = current->h_next ){
// вычисляем площадь и периметр контура
double area = fabs(cvContourArea(current));
double perim = cvContourPerimeter(current);

// 1/4*CV_PI = 0,079577
if ( area / (perim * perim) > 0.07 && area / (perim * perim)< 0.087 ){ // в 10% интервале
// нарисуем контур
cvDrawContours(_image, current, cvScalar(0, 0, 255), cvScalar(0, 255, 0), -1, 1, 8);
}
}

  • Like 1

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


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

Спасибо огромнейшее, разобрался с этим вопросом.

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

А вот как его лучше распознать? Я хотел сравнивать данный знак "40", со знаком "40" из ГОСТа и на основе какого-то процентного совпадения сделать вывод с определённой вероятностью, что это именно этот знак.

Насколько такой метод эффективен и можно ли где нибудь почитать о реализации чего-то подобного в OpenCV? Есть ли у него какие нибудь "подводные камни"? Сможет ли программа узнать знак, если предположим, они по какой-либо причине разных размеров?(допустим изображение знака в программе меньше, чем знак на изображении шаблона). Извините, что так много вопросов и скорее всего большинство из них действительно глупые, но я нашёл довольно мало реcурсов в рунете, посвящённых OpenCV( Ваш форум и robocraft.ru)

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


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

Вначале вырезать кусок со знаком, или назначить область интереса (ROI), в матрицах это означает просто вынуть подматрицу, в IplImage для этого есть функции SetImageROI, ResetImageROI.

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

Сравнивать можно или брутфорсом (начать можно с примера: descriptor_extractor_matcher.cpp), или подать изображение на вход нейронной сети, или другого классификатора.

Можно и шаблоном. Почитать можно на сайте OpenCV: http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html

Ну и тут посмотрите:

http://www.ee.nthu.edu.tw/clhuang/paper/2001-2.pdf

http://www.cvip.uofl.edu/wwwcvip/research/publications/Pub_Pdf/2004/ACIVS%202004%20paper023.pdf

И еще на шарпе:

http://www.emgu.com/wiki/index.php/Traffic_Sign_Detection_in_CSharp

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


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

Доброго времени суток!

будьте так любезны и помогите разобраться в такой ситуации:

после разности изображений получаю следующий результат:

http://floomby.ru/s1/vaExRu

код:

void MouseDetected::StartVideoFile(){


    frame = cvQueryFrame(capture);

    if (frame.empty() == true)

    {

        return;

        QMessageBox::information(this, "Сообщение программы", "Видео " + qVideofileName + "обработано успешно.");

        ui->IplOutputImage->setPixmap(QPixmap::fromImage(qimgNoFoto));


    }

    dPosSlid = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);

    int iFirstPosition = (int)dPosSlid;

    ui->lcdNumber->display(iFirstPosition);

    ui->horizontalSlider->setValue(iFirstPosition);


    //////////////////////////////////////////////Обработка кадра


    matOriginalGray = frame;

    matOriginal = frame;


    cv::cvtColor(matOriginalGray, matOriginalGray, CV_RGB2GRAY);


    cv::absdiff(matOriginalGray, matFon, matItog);

    cv::GaussianBlur(matItog, matItog, cv::Size(9,9), 1.5);



    cv::threshold(matItog, matItog, hSlider3, hSlider2, CV_THRESH_BINARY);

    cv::GaussianBlur(matItog, matItog, cv::Size(9,9), 1);


    matItog.convertTo(matProcessed, CV_8U);


    cv::Canny(matProcessed, matProcessed, 50, dcanny, 3);

    cv::findContours(matProcessed, vP, v4i,CV_RETR_TREE, CV_CHAIN_APPROX_NONE);


    //Обработка контролов


    cv::namedWindow("111", 0);

    cv::imshow("111", matOriginal);


   //


    QImage gimgOriginal((uchar*)matProcessed.data ,matProcessed.cols, matProcessed.rows, matProcessed.step, QImage::Format_Indexed8);

    ui->IplOutputImage->setPixmap(QPixmap::fromImage(gimgOriginal));

}

подскажите как мне теперь выделить и обрисовать объект на основном кадре?

желательно еще получать площадь объекта...

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


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

Как нарисовать и найти площадь есть в документации:

http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html

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


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

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

вопрос больше по синтаксису чем по реализации....

на С делал так, 1) задавал интересующую меня область ROI


int x = 172;

int y  = 42;

int width = 379;

int height = 365;

int add = 1;
2) после преобразований над изображением делеа следующее, т.е. обрезал границы и выделяо объект:

cvFindContours(itog, storage, &contours);

for( ; contours!=0; contours->h_next)

{

CvRect* rect = cvBoundingRect(contours, 0);

			if (((rect.x >= x) && (rect.y <= y + height)) && ((rect.x >= rect.x + 5) || (rect.y <= rect.y + 5)))

			{

				if ((rect.x + 25 <= rect.x + rect.width )|| (rect.y + 25 <= rect.y + rect.height))

				{

					cvRectangle(image, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y +rect.height), cvScalar(0,0,255,0), 1, 8, 0);

				}

			}

		}


как мне на Qt проделать такое же выделение???

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

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


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

Конуры имеют рад характеристик, такие как координаты центра масс, площадь, моменты.

Отфильтруйте по ним.

см. по ссылке выше команды:

boundingRect - отсюда можно взять центр

moments - отсюда можно взять центр

contourArea

convexHull

minAreaRect - отсюда можно взять центр

minEnclosingCircle - отсюда можно взять центр

По поводу выделения см. пост 328.

Кусок изображения можно достать так:


Rect r=cv::boundingRect(contours[i]);
Mat roi=img(r);[/code]

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


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

используя поиск элементов получаю из кода:

int idx = 0;

    for( ; idx >= 0; idx = v4i[idx][0] )

    {

        cv::Scalar color( 0, 0, 255 );

        for(int i = 0; i < vP.size(); i++)

        {

            cv::Rect rect = cv::boundingRect(vP[i]);

            if((rect.x >= x) && (rect.y <= y + height) && (cv::contourArea(vP[i]) > 100))

            {

                cv::drawContours( matOriginal, vP, i, color, CV_FILLED, 8, v4i);

            }

        }

    }
результат http://floomby.ru/s1/mavejr весь объект закрашен, а нужно только очертить контур... что не так делаю?? второй вопрос: как можно ускорить обработку из такого кода
void MouseDetected::StartVideoFile(){


    frame = cvQueryFrame(capture);


    if (frame.empty() == true)

    {

        return;

        QMessageBox::information(this, "Сообщение программы", "Видео " + qVideofileName + "обработано успешно.");

        ui->IplOutputImage->setPixmap(QPixmap::fromImage(qimgNoFoto));


    }

    // для визуализации происходящего

    dPosSlid = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);

    int iFirstPosition = (int)dPosSlid;

    ui->lcdNumber->display(iFirstPosition);

    ui->horizontalSlider->setValue(iFirstPosition);


    /////////---------Обработка кадра


    matOriginalGray = frame;

    matOriginal = frame;


    cv::cvtColor(matOriginalGray, matOriginalGray, CV_RGB2GRAY);

    cv::GaussianBlur(matOriginalGray, matOriginalGray, cv::Size(3,3), 1);

    cv::absdiff(matOriginalGray, matFon, matItog);

    cv::GaussianBlur(matItog, matItog, cv::Size(9,9), 1.5);


    cv::threshold(matItog, matItog, hSlider3, hSlider2, CV_THRESH_BINARY);

    cv::GaussianBlur(matItog, matItog, cv::Size(3,3), 1);


    cv::Canny(matItog, matItog, 50, dcanny, 3);


    cv::findContours(matItog, vP, v4i,CV_RETR_CCOMP , CV_CHAIN_APPROX_NONE);


    //Обработка контролов


    int idx = 0;

    for( ; idx >= 0; idx = v4i[idx][0] )

    {

        cv::Scalar color( 0, 0, 255 );

        for(int i = 0; i < vP.size(); i++)

        {

            cv::Rect rect = cv::boundingRect(vP[i]);

            if((rect.x >= x) && (rect.y <= y + height) && (cv::contourArea(vP[i]) > 100))

            {

                cv::drawContours( matOriginal, vP, i, color, CV_FILLED, 8, v4i);

            }

        }

    }

    cv::cvtColor(matOriginal, matOriginal, CV_BGR2RGB);

    QImage gimgOriginal((uchar*)matOriginal.data ,matOriginal.cols, matOriginal.rows, matOriginal.step, QImage::Format_RGB888);

    ui->IplOutputImage->setPixmap(QPixmap::fromImage(gimgOriginal));

}

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


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

cv::drawContours( matOriginal, vP, i, color, CV_FILLED, 8, v4i);

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

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


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

спасибо! помогло и заработало!

Только как найти центр выделенного объекта вообще догнать немогу... ... сказывается тяжелая неделя....

Приведите небольшой пример установки средней точки в объекте после обработки изображения cv::drawContours

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


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

как то так:

RotatedRect box;
box=minAreaRect(Mat(contour));
// Рисуем габаритные прямоугольники
Point2f center, vtx[4];
box.points(vtx);
for(int k = 0; k < 4; k++ )
{
line(Image, vtx[k], vtx[(k+1)%4], Scalar(255, 255, 0), 1, CV_AA);
}
// Рисуем центры масс
circle(Image,box.center,1,Scalar(255,255,255),-1);[/code]

  • Like 1

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


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

Интересный способ манипуляции контуром, посредством контрольных точек (с исходником):

описание:

http://www.morethantechnical.com/2013/01/05/shape-manipulation-with-moving-least-squares-for-curves-w-code/

исходники:

https://github.com/royshil/CurveDeformationMLS

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


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

Здравствуйте! Подскажите пожалуйста как получить приближенный эллипс зная контур CvContour. Затем необходимо закрасить все пиксели, которые находятся вне эллипса, на изображении IplImage в белый цвет.

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


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

"Приближенные" эллипсы могут быть разные.

Уточните что Вам нужно конкретно.

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


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

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

c90f37.jpg

f0da71.jpg

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


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

Попробуйте это:

http://se.cs.ait.ac.th/cvwiki/opencv:tutorial:fitting_ellipse_using_ransac_algorithm

ну и в стандартных примерах:

opencv/samples/cpp/fitellipse.cpp

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


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

Эллипс я рисую следующим образом

	

    CvContour contour;   

    CvSeqBlock contour_block;  

    cvMakeSeqHeaderForArray(CV_SEQ_POLYGON, sizeof(CvContour),sizeof(CvPoint),cvpoints2, Contour_Points*2, (CvSeq*)&contour, &contour_block); 



    CvSeq* seq = (CvSeq*)&contour;


    CvMemStorage* storage =cvCreateMemStorage(0);;

    seq  = cvApproxPoly( seq , sizeof(CvContour), storage,CV_POLY_APPROX_DP, 5, 1 );



    cvDrawContours(TempImage4, seq,

                            CV_RGB(255,216,0), CV_RGB(0,0,250),

                            0, 1,8);



    CvBox2D box=cvFitEllipse2(seq);

    cvEllipseBox(TempImage,box,CV_RGB(0,255,0));

Проблема именно в определении - находится ли точка внутри или вне этого эллипса.

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


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

Ну, если скорость не особо критична, можно сделать по тупому :) :

нарисовать эллипс на черном изображении и смотреть по нему.

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


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

Не совсем поняла, как это можно сделать, если честно)

Нашла функцию CVAPI(double) cvPointPolygonTest( const CvArr* contour,CvPoint2D32f pt, int measure_dist ), но в качестве первого параметра могу передать только контур seq. Нужно, по-видимому, каким-то образом получить объект эллипс и преобразовать к типу CvArr*, но с opencv я мало знакома. Существует ли такая возможность?

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


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

Рисуете заполненный белым эллипс на черном изображении (заново выделенном c типом CV_8UC1 ) получаете маску.

cvEllipseBox(MaskImage,box,CV_RGB(255,255,255),-1);

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

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

  • Like 1

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


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

Спасибо, но может все же есть более оптимальный вариант?

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


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

А Вы этот попробуйте, обычно так и делают.

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

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


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

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

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

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

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

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

Войти

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

Войти сейчас


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

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

×