OpenCV-approxPolyDP для краевых карт (не контуров)

Я успешно применил метод cv::approxPolyDP на контурах (cv:: findContours), чтобы представить контур с более простым многоугольником и неявно сделать некоторую деноизацию.

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

применим ли этот метод к общим краевым картам, кроме контуров?

может ли кто-нибудь указать мне на пример?

некоторые изображения прилагаются:

удачный пример для контуров: enter image description here

проблемный случай для краевых карт:

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

enter image description here

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

enter image description here

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

enter image description here

(рука как viewd от камеры глубины, ладони вверх, пальцы "закрывая" к ладони)

1 ответов


Ваш вопрос approxPolyDP связано с форматированием ввода в approxPolyDP.

объяснение

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

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

если список точек хранится в растровом порядке (сортируется по Y, а затем X), то point[k] и point[k+1] Не обязательно принадлежат к той же кривой. Это и есть причина проблемы.

этот вопрос объясняется иллюстрациями в OpenCV - как извлечь ребра из результата функции Canny? . Цитата из Михаил: "Canny не соединяет пиксели в цепи или сегменты."


Иллюстрация "растрового порядка", который генерируется Canny.

Raster order


Иллюстрация "порядка контура", который ожидается approxPolyDP

Contour order


что нужно

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

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

предлагаемые решения

(1) Использовать binary threshold вместо обнаружения края как входной сигнал к findContours

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

(2) сканируйте карту края и создайте список соседних пикселей, изучив соседи каждого пикселя края.

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

(3) Используйте альтернативный алгоритм обнаружения ребер, такой как рисование ребер.

на http://ceng.anadolu.edu.tr/cv/EdgeDrawing/

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


пример кода для варианта #1.

#include <stdint.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat matInput = imread("~/Data/mA9EE.png", false);

    // ---- Preprocessing of depth map. (Optional.) ----

    GaussianBlur(matInput, matInput, cv::Size(9, 9), 4.0);

    // ---- Here, we use cv::threshold instead of cv::Canny as explained above ----

    Mat matEdge;

    //Canny(matInput, matEdge, 0.1, 1.0);

    threshold(matInput, matEdge, 192.0, 255.0, THRESH_BINARY_INV);

    // ---- Use findContours to find chains of consecutive edge pixels ----

    vector<vector<Point> > contours;
    findContours(matEdge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // ---- Code below is only used for visualizing the result. ----

    Mat matContour(matEdge.size(), CV_8UC1);

    for (size_t k = 0; k < contours.size(); ++k)
    {
        const vector<Point>& contour = contours[k];
        for (size_t k2 = 0; k2 < contour.size(); ++k2)
        {
            const Point& p = contour[k2];
            matContour.at<uint8_t>(p) = 255;
        }
    }

    imwrite("~/Data/output.png", matContour);
    cout << "Done!" << endl;
    return 0;
}