Как выровнять RGB и глубину изображения Kinect в OpenCV?

У меня есть проект C ++, где я использую OpenCV и Libfreenect. Я не хочу включать что-то такое большое и тяжелое, как OpenNI, и создавать зависимость установки OpenCV в процессе. Я хочу использовать предоставленную информацию о калибровке Вот чтобы не искажать и выравнивать RGB и изображения глубины.

Удаление изображений по отдельности на основе матрицы камеры и коэффициентов искажения было достаточно простым. Но теперь я запутался в том, как я мог бы использовать матрицы выпрямления и проекции, чтобы выровнять RGB и изображение глубины, чтобы они, по сути, показывали мне одни и те же вещи с одной и той же точки зрения. После поисков в течение достаточно долгого времени я не могу установить, как это должно работать с OpenCV. Это неопределенная оценка, что reprojectImageTo3D () а также warpPerspective () может быть использован, но я не уверен, как.

Как я мог подойти к этой проблеме? Я использую старый XBOX360 Kinect (с необработанным диапазоном значений диспаратности 0-2047).

ОБНОВИТЬ

Вот частичный код, который я написал до сих пор:

// I use callback functions to get RGB (CV_8UC3) and depth (CV_16UC1)
// I undistort them and call the following method
void AlignImages(cv::Mat& pRGB, cv::Mat& pDepth) {

rotationMat = (cv::Mat_<double_t>(3,3) << 9.9984628826577793e-01, 1.2635359098409581e-03, -1.7487233004436643e-02, -1.4779096108364480e-03, 9.9992385683542895e-01, -1.2251380107679535e-02, 1.7470421412464927e-02, 1.2275341476520762e-02, 9.9977202419716948e-01);
translationMat = (cv::Mat_<double_t>(3,1) << 1.9985242312092553e-02, -7.4423738761617583e-04, -1.0916736334336222e-02);// make a copy in float to convert raw depth data to physical distance
cv::Mat tempDst;
pDepth.convertTo(tempDst, CV_32F);

// create a 3 channel image of precision double for the 3D points
cv::Mat tempDst3D = cv::Mat(cv::Size(640, 480), CV_64FC3, double(0));

float_t* tempDstData = (float_t*)tempDst.data;
double_t* tempDst3DData = (double_t*)tempDst3D.data;

size_t pixelSize = tempDst.step / sizeof(float_t);
size_t pixel3DSize = tempDst3D.step / sizeof(double_t);

for (int row=0; row < tempDst.rows; row++) {
for (int col=0; col < tempDst.cols; col++) {

// convert raw depth values to physical distance (in metres)
float_t& pixel = tempDstData[pixelSize * row + col];
pixel = 0.1236 * tanf(pixel/2842.5 + 1.1863);

// reproject physical distance values to 3D space
double_t& pixel3D_X = tempDst3DData[pixel3DSize * row + col];
double_t& pixel3D_Y = tempDst3DData[pixel3DSize * row + col +1];
double_t& pixel3D_Z = tempDst3DData[pixel3DSize * row + col + 2];

pixel3D_X = (row - 3.3930780975300314e+02) * pixel / 5.9421434211923247e+02;
pixel3D_Y = (col - 2.4273913761751615e+02) * pixel / 5.9104053696870778e+02;
pixel3D_Z = pixel;

}
}

tempDst3D = rotationMat * tempDst3D + translationMat;
}

Я непосредственно использовал числа вместо того, чтобы назначать их переменным, но это не должно быть проблемой в понимании логики. На данный момент я должен сделать следующее:

P2D_rgb.x = (P3D'.x * fx_rgb / P3D'.z) + cx_rgb
P2D_rgb.y = (P3D'.y * fy_rgb / P3D'.z) + cy_rgb

Но я не понимаю, как мне это сделать, точно. Возможно, я иду совсем не в том направлении. Но я не могу найти ни одного примера того, как это делается.

7

Решение

По сути, вам необходимо изменить 3D-систему координат, чтобы преобразовать 3D-точки, видимые камерой глубины, в 3D-точки, видимые камерой RGB.

Вы не можете использовать функцию reprojectImageTo3D() потому что он ожидает матрицу Q, которой у вас нет. Вместо этого вы должны преобразовать свою карту диспаратности в карту глубины, используя функцию raw_depth_to_meters на странице, на которую вы ссылаетесь.

Затем для каждого пикселя карты глубины необходимо вычислить связанную трехмерную точку, обозначенную как P3D на странице, на которую вы ссылаетесь (см. § «Отображение пикселей глубины с цветными пикселями»). Затем необходимо применить предоставленную матрицу 3D-поворота R и вектор 3D-трансляции T, которые представляют преобразование от камеры глубины до камеры RGB, к каждой точке 3D P3D для того, чтобы получить связанную новую 3D-точку P3D', Наконец, используя калибровочную матрицу камеры RGB, вы можете проецировать новые 3D-точки в изображение RGB и назначить связанную глубину полученному пикселю, чтобы сгенерировать новую карту глубины, выровненную по изображению RGB.

Обратите внимание, что вы обязательно теряете точность в процессе, поскольку вам необходимо обрабатывать окклюзии (сохраняя только минимальную глубину, видимую каждым пикселем) и интерполяцию изображения (поскольку в общем случае проецируемые 3D-точки не будут связаны с целочисленными пиксельными координатами в изображении RGB). Что касается интерполяции изображений, я рекомендую вам использовать подход ближайшего соседа, в противном случае вы можете столкнуться со странным поведением на границах глубины.

Изменить после обновления вопроса

Вот модель того, что вы должны делать, чтобы переназначить карту глубины Kinect в точку зрения RGB-камеры:

cv::Mat_<float> pt(3,1), R(3,3), t(3,1);
// Initialize R & t here

depthmap_rgbcam = cv::Mat::zeros(height,width,CV_32FC1); // Initialize the depthmap to all zeros
float *depthmap_rgbcam_buffer = (float*)depthmap_rgbcam.data;
for(int row=0; row<height; ++row)
{
for(int col=0; col<width; ++col)
{
// Convert kinect raw disparity to depth
float raw_disparity = kinect_disparity_map_buffer[width*row+col];
float depth_depthcam = disparity_to_depth(raw_disparity);

// Map depthcam depth to 3D point
pt(0) = depth*(col-cx_depthcam)/fx_depthcam;  // No need for a 3D point buffer
pt(1) = depth*(row-cy_depthcam)/fy_depthcam;  // here, unless you need one.
pt(2) = depth;

// Rotate and translate 3D point
pt = R*pt+t;

// If required, apply rgbcam lens distortion to X, Y and Z here.

// Project 3D point to rgbcam
float x_rgbcam = fx_rgbcam*pt(0)/pt(2)+cx_rgbcam;
float y_rgbcam = fy_rgbcam*pt(1)/pt(2)+cy_rgbcam;

// "Interpolate" pixel coordinates (Nearest Neighbors, as discussed above)
int px_rgbcam = cvRound(x_rgbcam);
int py_rgbcam = cvRound(y_rgbcam);

// Handle 3D occlusions
float &depth_rgbcam = depthmap_rgbcam_buffer[width*py_rgbcam+px_rgbcam];
if(depth_rgbcam==0 || depth_depthcam<depth_rgbcam)
depth_rgbcam = depth_depthcam;
}
}

Это идея, по модулю возможных опечаток. Вы также можете изменить последовательно тип данных, как вам нравится. Что касается вашего комментария, я не думаю, что есть какая-либо встроенная функция OpenCV для этой цели.

8

Другие решения

@AldurDisciple, насколько я понимаю, RGB-изображение с камеры хранится в depthmap_rgbcam со значениями RGB, но я не вижу, где и когда изображение берется с камеры и передается в переменную? Для меня это как пустая матрица после инициализации depthmap_rgbcam,

1

В opencv_contrib (модуль rgbd) была добавлена ​​функция регистрации RGBD, которая регистрирует глубину для внешней камеры:
https://github.com/Itseez/opencv_contrib/commit/f5ef071c117817b0e98b2bf509407f0c7a60efd7

1
По вопросам рекламы [email protected]