2014-09-11 5 views
14

В настоящее время я разрабатываю инструмент для Kinect для Windows v2 (аналогично тому, как в XBOX ONE). Я попытался следовать некоторым примерам и иметь рабочий пример, который показывает изображение камеры, изображение глубины и изображение, которое отображает глубину в rgb, используя opencv. Но я вижу, что он дублирует мою руку при выполнении сопоставления, и я думаю, что это связано с чем-то неправильным в части координатора координат.Kinect для Windows v2 глубина цвета для рассогласования изображения

вот пример этого: error

А вот фрагмент кода, который создает изображение (rgbd изображение в примере)

void KinectViewer::create_rgbd(cv::Mat& depth_im, cv::Mat& rgb_im, cv::Mat& rgbd_im){ 
    HRESULT hr = m_pCoordinateMapper->MapDepthFrameToColorSpace(cDepthWidth * cDepthHeight, (UINT16*)depth_im.data, cDepthWidth * cDepthHeight, m_pColorCoordinates); 
    rgbd_im = cv::Mat::zeros(depth_im.rows, depth_im.cols, CV_8UC3); 
    double minVal, maxVal; 
    cv::minMaxLoc(depth_im, &minVal, &maxVal); 
    for (int i=0; i < cDepthHeight; i++){ 
     for (int j=0; j < cDepthWidth; j++){ 
      if (depth_im.at<UINT16>(i, j) > 0 && depth_im.at<UINT16>(i, j) < maxVal * (max_z/100) && depth_im.at<UINT16>(i, j) > maxVal * min_z /100){ 
       double a = i * cDepthWidth + j; 
       ColorSpacePoint colorPoint = m_pColorCoordinates[i*cDepthWidth+j]; 
       int colorX = (int)(floor(colorPoint.X + 0.5)); 
       int colorY = (int)(floor(colorPoint.Y + 0.5)); 
       if ((colorX >= 0) && (colorX < cColorWidth) && (colorY >= 0) && (colorY < cColorHeight)) 
       { 
        rgbd_im.at<cv::Vec3b>(i, j) = rgb_im.at<cv::Vec3b>(colorY, colorX); 
       } 
      } 

     } 
    } 
} 

Кто-нибудь есть понятия о том, как решить это? Как предотвратить это дублирование?

Заранее спасибо

UPDATE:

Если я простой пороговую глубины изображения я получить следующее изображение: thresholding

Это то, что более или менее я ожидал случиться, и не имея дублирующей руки в фоновом режиме. Есть ли способ предотвратить эту дублируемую руку в фоновом режиме?

+1

, где происходит это сопоставление из? скорее всего, вам нужно отредактировать калибровку между изображением глубины и цветным изображением, поскольку предварительные определения не идеальны. Поэтому вы должны выполнить свою собственную калибровку. Посмотрите: http://nicolas.burrus.name/index.php/Research/KinectCalibration – Micka

+0

Он поставляется с kinect SDK v2. Я ожидал использовать тот, который исходит от прошивки/SDK, который использует интрисы камеры для выполнения этих вычислений ... но я думаю, что ошибка огромна по сравнению с прошивкой/программным обеспечением других камер, таких как Primesense с openni. Я ожидал лучших результатов или, по крайней мере, похожих на другие камеры .... Спасибо за ссылку, хотя :) – api55

+0

Afaik данные автоматической калибровки kinect, сохраненные в прошивке, не являются таковыми. Но, возможно, я ошибаюсь. – Micka

ответ

0

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

Давайте начнем с некоторой теории, чтобы понять, что на самом деле происходит, а затем и возможного ответа.

Начнем с того, что мы узнаем, как пройти из облака 3D-точки, где камера глубины является источником координат в изображении на плоскости изображения камеры RGB.Для этого достаточно использовать камеры-обскуры модель:

enter image description here

здесь, u и v координаты в плоскости изображения камеры RGB. первой матрицей в правой части уравнения является матрица камеры, встроенная функция AKA камеры RGB. Следующая матрица - это поворот и перевод внешних характеристик, или, лучше сказать, преобразование, необходимое для перехода от системы координат камеры глубины к системе координат камеры RGB. Последняя часть - это трехмерная точка.

В принципе, что-то вроде этого, что делает Kinect SDK. Итак, что может пойти не так, что рука будет дублироваться? ну, на самом деле, более одного пункта проектируется на один и тот же пиксель.

Чтобы выразить это другими словами и в контексте проблемы в вопросе.

Изображение глубины, представляет собой представление упорядоченного облака точек, и я запрашиваю значения каждого из его пикселей, которые на самом деле могут быть легко преобразованы в 3D-точки. SDK дает вам проекцию, но может указывать на один и тот же пиксель (обычно, большее расстояние в оси z между двумя соседними точками может дать эту проблему довольно легко.

Теперь, большой вопрос, как вы можете избежать это .... ну, я не уверен в использовании Kinect SDK, так как вы не знаете значения Z точек, ПОСЛЕ ПРИМЕНЕНИЯ внешних применений, поэтому невозможно использовать технику, такую ​​как Z buffering .... Однако , вы можете предположить, что значение Z будет очень похожим и использовать те из исходного pointcloud (на свой страх и риск).

Если вы делали это вручную, а не с помощью SDK, вы можете применить Extrinsics к точкам , и использовать их проект в плоскости изображения, отмечая в другой матрице, на какую точку отображается, на какую p ixel, и если имеется уже существующая точка, проверьте значения z и сравните их и всегда оставляйте ближайшую точку к камере. Тогда у вас будет корректное отображение без каких-либо проблем. Этот путь - наивный способ, возможно, вы можете стать лучше, так как проблема теперь ясна :)

Надеюсь, это достаточно ясно.

P.S .: У меня нет Kinect 2 на данный момент, поэтому я не могу попытаться выяснить, есть ли обновление относительно этой проблемы или если все еще происходит одно и то же. Я использовал первую выпущенную версию (не предварительную версию) SDK ... Таким образом, могло произойти много изменений ... Если кто-то знает, было ли это решение, просто оставьте комментарий :)

1

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

Несколько замечаний:

  • Включите источник BodyIndexFrame для читателя кадра
  • Использование MapColorFrameToDepthSpace вместо MapDepthFrameToColorSpace; таким образом, вы получите HD изображение на переднем плане
  • Найти соответствующий DepthSpacePoint и depthX, depthY, вместо ColorSpacePoint и colorX, colorY

Вот мой подход, когда приходит кадр (это в C#):

depthFrame.CopyFrameDataToArray(_depthData); 
colorFrame.CopyConvertedFrameDataToArray(_colorData, ColorImageFormat.Bgra); 
bodyIndexFrame.CopyFrameDataToArray(_bodyData); 

_coordinateMapper.MapColorFrameToDepthSpace(_depthData, _depthPoints); 

Array.Clear(_displayPixels, 0, _displayPixels.Length); 

for (int colorIndex = 0; colorIndex < _depthPoints.Length; ++colorIndex) 
{ 
    DepthSpacePoint depthPoint = _depthPoints[colorIndex]; 

    if (!float.IsNegativeInfinity(depthPoint.X) && !float.IsNegativeInfinity(depthPoint.Y)) 
    { 
     int depthX = (int)(depthPoint.X + 0.5f); 
     int depthY = (int)(depthPoint.Y + 0.5f); 

     if ((depthX >= 0) && (depthX < _depthWidth) && (depthY >= 0) && (depthY < _depthHeight)) 
     { 
      int depthIndex = (depthY * _depthWidth) + depthX; 
      byte player = _bodyData[depthIndex]; 

      // Identify whether the point belongs to a player 
      if (player != 0xff) 
      { 
       int sourceIndex = colorIndex * BYTES_PER_PIXEL; 

       _displayPixels[sourceIndex] = _colorData[sourceIndex++]; // B 
       _displayPixels[sourceIndex] = _colorData[sourceIndex++]; // G 
       _displayPixels[sourceIndex] = _colorData[sourceIndex++]; // R 
       _displayPixels[sourceIndex] = 0xff;       // A 
      } 
     } 
    } 
} 

Вот инициализация массивов:

BYTES_PER_PIXEL = (PixelFormats.Bgr32.BitsPerPixel + 7)/8; 

_colorWidth = colorFrame.FrameDescription.Width; 
_colorHeight = colorFrame.FrameDescription.Height; 
_depthWidth = depthFrame.FrameDescription.Width; 
_depthHeight = depthFrame.FrameDescription.Height; 
_bodyIndexWidth = bodyIndexFrame.FrameDescription.Width; 
_bodyIndexHeight = bodyIndexFrame.FrameDescription.Height; 
_depthData = new ushort[_depthWidth * _depthHeight]; 
_bodyData = new byte[_depthWidth * _depthHeight]; 
_colorData = new byte[_colorWidth * _colorHeight * BYTES_PER_PIXEL]; 
_displayPixels = new byte[_colorWidth * _colorHeight * BYTES_PER_PIXEL]; 
_depthPoints = new DepthSpacePoint[_colorWidth * _colorHeight]; 

Обратите внимание, что массив _depthPoints имеет размер 1920х1080.

Еще раз, самое главное, использовать источник BodyIndexFrame.

+0

Я думаю, что пример изображения с рукой немного вводит в заблуждение. Мы пытаемся внедрить инструмент для записи в разных форматах набора данных. Одна из вещей, которые мы должны уметь делать с этим набором данных, - создать цветное облако точек, но точки в фоновом режиме имеют неправильный цвет (выглядит как дублирующая рука). Есть ли способ удалить только этот «недопустимый» (мы считаем их недопустимыми, поскольку не должно быть сопоставления с цветом, возможно, потому, что эти пиксели не отображаются в цветном изображении). – api55

+0

О, я понял. В вашем примере вы создаете изображение RGB с использованием рамки глубины в качестве базы. Таким образом, вы не сможете проецировать его поверх изображения 1920x1080. В коде, который я вам предоставил, создается растровое изображение RGBA размером 1920x1080. В результате вы можете разместить его поверх другого растрового изображения 1920x1080. Вы попробовали это? – Vangos

+0

Я попробовал, и получил что-то без дубликатов, но я думаю, что много недопустимых пикселей исчезло и заставило меня задуматься о том, насколько хорошо это сопоставление? Если вы видите окно над дверью в примере, в изображении глубины будет много недопустимых пикселей. Когда я делаю это сопоставление, которое вы предлагаете, большинство из них фактически исчезает ... так что мне было интересно, что с ними происходит? возможно, некоторая интерполяционная проблема в рамках? – api55

Смежные вопросы