Я успешно применил метод cv :: окPolyDP для контуров (cv :: findContours), чтобы представить контур с помощью более простого многоугольника и неявно выполнить шумоподавление.
Я хотел бы сделать то же самое на карте краев, полученной с камеры RGBD (которая в целом очень шумная), но пока без особого успеха, и я не могу найти относительные примеры в Интернете. Причина, по которой мне это нужно, в том, что с помощью карты ребер можно также использовать ребра между пальцами, ребра, созданные окклюзией пальца, или ребра, созданные в ладони.
Применим ли этот метод к общим картам ребер, кроме контуров?
Может ли кто-нибудь указать мне на пример?
Некоторые изображения прилагаются:
Удачный пример для контуров:
Проблемный случай для карт ребер:
Скорее всего, я рисую вещи неправильно, но рисование только пикселей, возвращаемых методом, показывает, что, вероятно, большие области не представлены в конечном результате (и это не сильно меняется в соответствии с параметром epsilon).
Я также прикрепляю изображение глубины, подобное тем, которые я использую в экспериментальном конвейере, описанном выше. Это изображение глубины не было получено камерой глубины, но было синтетически сгенерировано чтением буфера глубины графического процессора, используя OpenGL.
Просто для справки, это также карта краев изображения глубины, полученного прямо с камеры глубины (с использованием необработанного изображения, без сглаживания и т. Д.)
(рука, если смотреть с глубины камеры, ладонь направлена вверх, пальцы «сжимаются» по направлению к ладони)
Ваша проблема с approxPolyDP
связано с форматированием ввода в approxPolyDP
,
approxPolyDP
ожидает, что его вход будет вектором Point
s. Эти точки определяют многоугольную кривую, которая будет обрабатываться approxPolyDP
, Кривая может быть открытой или закрытой, что может контролироваться флагом.
Порядок точек в списке важен. Подобно тому, как каждый вручную вычерчивает многоугольник, каждая последующая точка в векторе должна быть следующей вершиной многоугольника по часовой стрелке или против часовой стрелки.
Если список точек хранится в растровом порядке (отсортировано по Y, а затем по X), то point[k]
а также point[k+1]
не обязательно принадлежат той же кривой. Это причина проблемы.
Эта проблема объясняется с помощью иллюстраций в OpenCV — Как извлечь края из результата Canny Function? . Цитата из Михаил: «Canny не соединяет пиксели в цепочки или сегменты».
Canny
,approxPolyDP
Что вам нужно, так это список «цепочек краевых пикселей». Каждая цепочка должна содержать краевые пиксели, которые находятся рядом друг с другом, точно так же, как кто-то вычерчивает контур объекта карандашом, а кончик карандаша не оставляет бумагу.
Это не то, что возвращается из методов обнаружения краев, таких как Canny
, Дальнейшая обработка необходима для преобразования карты границ в цепочки смежных (непрерывных) краевых пикселей.
(1) Используйте двоичный файл threshold
вместо обнаружения края в качестве входа для findContours
Это будет применимо, если существует пороговое значение, которое отделяет руку от фона, и что это значение работает для всей руки (а не только для части руки).
(2) Сканируйте карту границ и создайте список смежных пикселей, изучая соседей каждого пикселя края.
Это похоже на алгоритм подключенных компонентов, за исключением того, что вместо поиска большого двоичного объекта (где вам нужно знать только членство каждого пикселя) вы пытаетесь найти цепочки пикселей, чтобы можно было различить предыдущий и следующий крайние пиксели вдоль цепочки. ,
(3) Используйте альтернативный алгоритм обнаружения ребер, такой как рисование ребер.
Подробности можно найти на http://ceng.anadolu.edu.tr/cv/EdgeDrawing/
К сожалению, это не предоставляется из коробки OpenCV, поэтому вам, возможно, придется найти реализацию в другом месте.
#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;
}
Других решений пока нет …