Я пытаюсь работать с датчиком глубины, чтобы добавить позиционное отслеживание в комплект для разработки Oculus Rift. Однако у меня возникли проблемы с последовательностью операций, которые дают полезный результат.
Я начинаю с 16-битного изображения глубиной, где значения вроде (но не совсем) соответствуют миллиметрам. Неопределенные значения в изображении уже были установлены в 0.
Сначала я устраняю все, что находится за пределами определенного ближнего и дальнего расстояния, обновляя изображение маски, чтобы исключить их.
cv::Mat result = cv::Mat::zeros(depthImage.size(), CV_8UC3);
cv::Mat depthMask;
depthImage.convertTo(depthMask, CV_8U);
for_each_pixel<DepthImagePixel, uint8_t>(depthImage, depthMask,
[&](DepthImagePixel & depthPixel, uint8_t & maskPixel){
if (!maskPixel) {
return;
}
static const uint16_t depthMax = 1200;
static const uint16_t depthMin = 200;
if (depthPixel < depthMin || depthPixel > depthMax) {
maskPixel = 0;
}
});
Далее, поскольку требуемая функция, вероятно, будет ближе к камере, чем общее среднее значение сцены, я снова обновляю маску, чтобы исключить все, что не находится в определенном диапазоне от медианного значения:
const float depthAverage = cv::mean(depthImage, depthMask)[0];
const uint16_t depthMax = depthAverage * 1.0;
const uint16_t depthMin = depthAverage * 0.75;
for_each_pixel<DepthImagePixel, uint8_t>(depthImage, depthMask,
[&](DepthImagePixel & depthPixel, uint8_t & maskPixel){
if (!maskPixel) {
return;
}
if (depthPixel < depthMin || depthPixel > depthMax) {
maskPixel = 0;
}
});
Наконец, я обнуляю все, что не в маске, и масштабирую оставшиеся значения до 10 & 255 до преобразования формата изображения в 8 бит
cv::Mat outsideMask;
cv::bitwise_not(depthMask, outsideMask);
// Zero out outside the mask
cv::subtract(depthImage, depthImage, depthImage, outsideMask);
// Within the mask, normalize to the range + X
cv::subtract(depthImage, depthMin, depthImage, depthMask);
double minVal, maxVal;
minMaxLoc(depthImage, &minVal, &maxVal);
float range = depthMax - depthMin;
float scale = (((float)(UINT8_MAX - 10) / range));
depthImage *= scale;
cv::add(depthImage, 10, depthImage, depthMask);
depthImage.convertTo(depthImage, CV_8U);
Результаты выглядят так:
Я очень доволен этим разделом кода, поскольку он дает довольно четкие визуальные функции.
Затем я применяю несколько операций сглаживания, чтобы избавиться от нелепого количества шума от глубинной камеры:
cv::medianBlur(depthImage, depthImage, 9);
cv::Mat blurred;
cv::bilateralFilter(depthImage, blurred, 5, 250, 250);
depthImage = blurred;
cv::Mat result = cv::Mat::zeros(depthImage.size(), CV_8UC3);
cv::insertChannel(depthImage, result, 0);
Опять же, функции выглядят довольно четко визуально, но мне интересно, если они не могут быть улучшены как-то:
Далее я использую canny для обнаружения краев:
cv::Mat canny_output;
{
cv::Canny(depthImage, canny_output, 20, 80, 3, true);
cv::insertChannel(canny_output, result, 1);
}
Линии, которые я ищу, есть, но не очень хорошо представлены в углах:
Наконец, я использую вероятностный Хо для определения линий:
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(canny_output, lines, pixelRes, degreeRes * CV_PI / 180, hughThreshold, hughMinLength, hughMaxGap);
for (size_t i = 0; i < lines.size(); i++)
{
cv::Vec4i l = lines[i];
glm::vec2 a((l[0], l[1]));
glm::vec2 b((l[2], l[3]));
float length = glm::length(a - b);
cv::line(result, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 3, CV_AA);
}
Это приводит к этому изображению
В этот момент я чувствую, что сошел с рельсов, потому что не могу найти хороший набор параметров для Hough, чтобы произвести разумное количество строк-кандидатов для поиска моей фигуры, и я не уверен, что Я должен был возиться с Хо или смотреть на улучшение результатов предыдущих шагов.
Есть ли хороший способ объективной проверки моих результатов на каждом этапе, вместо того, чтобы просто возиться с входными значениями, пока я не думаю, что это «выглядит хорошо»? Существует ли лучший подход к поиску прямоугольника по заданному изображению (и учитывая, что он не обязательно будет ориентирован в определенном направлении?
Очень классный проект!
Хотя я чувствую, что ваш подход не использует всю информацию, которую вы могли бы получить из карты глубины (например, 3D-точки, нормали и т. Д.), Что очень помогло бы.
Библиотека Point Cloud Library (PCL), представляющая собой библиотеку C ++, предназначенную для обработки данных RGB-D, имеет руководство на сегментации самолета с использованием RANSAC, который может вдохновить вас. Возможно, вы не захотите использовать PCL в своей программе из-за многочисленных зависимостей, однако, поскольку она имеет открытый исходный код, вы можете найти реализацию алгоритма на Github (PCL SAC сегментация). Однако RANSAC может быть медленным и давать нежелательные результаты в зависимости от сцены.
Вы также можете попробовать использовать подход, представленный в разделе «Сегментация плоскости в реальном времени».
с использованием камер RGB-D «, Holz, Holzer, Rusu and Behnke, 2011 (PDF), что предполагает быструю оценку нормалей с использованием интегральных изображений с последующим обнаружением плоскостей с использованием кластеризации нормалей.
Других решений пока нет …