Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

enter image description here

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

enter image description here

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

enter image description here

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

4b9b3361

Ответ 1

Ваша проблема с approxPolyDP обусловлена ​​форматированием ввода в approxPolyDP.

Описание

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

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

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

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


Иллюстрация "растрового порядка", создаваемого Canny.

Raster order


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

Contour order


Что необходимо

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

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

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

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

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

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

Это похоже на алгоритм связанных компонентов, за исключением того, что вместо того, чтобы находить 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;
}