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

у меня есть проблема, чтобы получить мою голову вокруг сглаживания и выборки контуров в OpenCV (C++ API). Допустим, у меня есть последовательность точек, извлеченных из cv::findContours (например, применяется на этом изображении:

enter image description here

в конечном счете, я хочу!--11-->

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

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

enter image description here

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

я прочитал несколько сообщений на нем и наивно подумал, что я мог бы просто преобразовать std::vector(of cv::Point) к cv::Mat а затем функции OpenCV, такие как размытие / изменение размера, сделают работа для меня... но они этого не сделали.

вот что я пробовал:

int main( int argc, char** argv ){

    cv::Mat conv,ori;
    ori=cv::imread(argv[1]);
    ori.copyTo(conv);
    cv::cvtColor(ori,ori,CV_BGR2GRAY);

    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i > hierarchy;

    cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);

    for(int k=0;k<100;k += 2){
        cv::Mat smoothCont;

        smoothCont = cv::Mat(contours[0]);
        std::cout<<smoothCont.rows<<"t"<<smoothCont.cols<<std::endl;
        /* Try smoothing: no modification of the array*/
//        cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
        /* Try sampling: "Assertion failed (func != 0) in resize"*/
//        cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
        std::vector<std::vector<cv::Point> > v(1);
        smoothCont.copyTo(v[0]);
        cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
        std::cout<<k<<std::endl;
        cv::imshow("conv", conv);
        cv::waitKey();
    }
    return 1;
}

может ли кто-нибудь объяснить, как это сделать ?

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

большое спасибо за ваш советы,

Edit:

я тоже пробовал cv::approxPolyDP() но, как вы можете видеть, он имеет тенденцию сохранять экстремальные точки (которые я хочу удалить):

Эпсилон=0

enter image description here

Эпсилон=6

enter image description here

Эпсилон=12

enter image description here

Эпсилон=24

enter image description here

Edit 2: Как предположил Бен, кажется, что cv::GaussianBlur() не поддерживается, но cv::blur() есть. Это выглядит гораздо ближе к моим ожиданиям. Вот мои результаты, используя его:

k=13

enter image description here

k=53

enter image description here

k=103

enter image description here

чтобы обойти эффект границы, я сделал:

    cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
    cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
    result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);

я все еще ищу решения для интерполяции/образец мой контур.

7 ответов


ваше гауссовское размытие не работает, потому что вы размываетесь в направлении столбца, но есть только один столбец. Используя GaussianBlur() приводит к ошибке "функция не реализована" в OpenCV при попытке скопировать вектор обратно в cv::Mat (наверное, поэтому у вас есть этот странный resize() в вашем коде), но все работает нормально, используя cv::blur() не нужно resize(). Попробуйте размер(0,41) например. Используя cv::BORDER_WRAP для пограничного вопроса, похоже, тоже не работает, но здесь другой нить кто-то нашел обходной путь для этого.

Ох... еще одна вещь: вы сказали, что ваши контуры, вероятно, будут намного меньше. Сглаживание вашего контура таким образом уменьшит его. Крайний случай -k = size_of_contour, что приводит к одной точке. Так что не выбирайте k слишком большой.


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

https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/graphics/ofPolyline.cpp#L416-459

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


как о approxPolyDP ()?

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


из раздела 2.1 OpenCV doc Базовые Структуры:

template<typename T>
explicit Mat::Mat(const vector<T>& vec, bool copyData=false)

вы, вероятно, хотите установить 2-й param в true in:

smoothCont = cv::Mat(contours[0]);

и повторите попытку (сюда cv::GaussianBlur должен иметь возможность изменять данные).


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


в основном внезапные изменения контура соответствуют высокочастотному содержанию. Простой способ сгладить контур - найти коэффициенты Фурье, предполагающие, что координаты образуют комплексную плоскость x + iy, а затем исключить высокочастотные коэффициенты.


мой взгляд ... много лет спустя ...!

может быть, два простых способа сделать это:

  • петля несколько раз с расширением, размытием, размыванием. И найдите контуры обновленной фигуры. Я нашел 6-7 раз дает хорошие результаты.
  • создайте ограничивающую рамку контура и нарисуйте эллипс внутри ограниченного прямоугольника.

добавление визуальных результатов ниже:

enter image description here