OpenCV-approxPolyDP для краевых карт (не контуров)
Я успешно применил метод cv::approxPolyDP на контурах (cv:: findContours), чтобы представить контур с более простым многоугольником и неявно сделать некоторую деноизацию.
Я хотел бы сделать то же самое на карте края, полученной от камеры RGBD (которая в целом очень шумная), но с небольшим успехом до сих пор, и я не могу найти относительные примеры в интернете. Причина, по которой мне это нужно, заключается в том, что с помощью карты edge можно также использовать края между пальцами, края, созданные окклюзией пальцев или краями, созданными в ладони.
применим ли этот метод к общим краевым картам, кроме контуров?
может ли кто-нибудь указать мне на пример?
некоторые изображения прилагаются:
удачный пример для контуров:
проблемный случай для краевых карт:
скорее всего, я рисую вещи неправильно, но рисование только пикселей, возвращаемых методом, показывает, что, вероятно, большой области не представлены в конечном результате (и это не сильно меняется в соответствии с epsilon-параметром).
Я прикрепляю также изображение глубины, подобное тем, которые я использую в экспериментальном трубопроводе, описанном выше. Это изображение глубины не было получено глубинной камерой, но было синтетически сгенерировано путем чтения буфера глубины gpu с помощью OpenGL.
просто для справки, это также карта края глубины изображение, полученное прямо с камеры глубины (с использованием raw-изображения, без сглаживания и т. д.)
(рука как viewd от камеры глубины, ладони вверх, пальцы "закрывая" к ладони)
1 ответов
Ваш вопрос approxPolyDP
связано с форматированием ввода в approxPolyDP
.
объяснение
approxPolyDP
ожидает, что его вход должен быть вектором Point
s. Эти точки определяют полигональную кривую, которая будет обрабатываться approxPolyDP
. Кривая может быть открытой или закрытой, которая может управляться флагом.
порядок пунктов в списке важных. Так же, как один трассирует многоугольник вручную, каждая последующая точка в векторе должна быть следующей вершины многоугольника по часовой стрелке или против часовой стрелки.
если список точек хранится в растровом порядке (сортируется по Y, а затем X), то point[k]
и point[k+1]
Не обязательно принадлежат к той же кривой. Это и есть причина проблемы.
этот вопрос объясняется иллюстрациями в OpenCV - как извлечь ребра из результата функции Canny? . Цитата из Михаил: "Canny не соединяет пиксели в цепи или сегменты."
Иллюстрация "растрового порядка", который генерируется Canny
.
Иллюстрация "порядка контура", который ожидается approxPolyDP
что нужно
вам нужен список "цепочек краевых пикселей". Каждая цепочка должна содержать граничные пиксели, которые соседствуют друг с другом, так же, как кто-то прослеживает контур объекта карандашом, не отрывая кончика карандаша от бумаги.
это не то, что возвращается из методов обнаружения края, таких как 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;
}