1.設置vtkCamera內參
已知相機內參爲:fx、fy、cx、cy
,圖像尺寸爲:width、height
。
相機相對於世界座標系的外參矩陣爲extrinsicMatrix4x4
,即相對於世界座標系的旋轉平移。如果不存在外參,則該矩陣爲單位矩陣。
首先保證renderWindow
的尺寸也設置爲width、height
。
// 將相機相對世界座標系的外參賦值給相機
camera->SetModelTransformMatrix(extrinsicMatrix4x4);
// 由於SetModelTransformMatrix已經將規定了相機與世界座標系的關係,此時相機設置Position的參照系爲相機自己的參照系,此時相機應當處於原點
camera->SetPosition(0, 0, 0);
// 按照深度相機定義,相機應當看向Z+方向
camera->SetFocalPoint(0, 0, 1);
// 相機座標系x沿u方向,y沿v方向,因此y是向下的
camera->SetViewUp(0,-1,0);
// 設置深度範圍,按照相機默認參數即可,米單位
camera->SetClippingRange(depth_min, depth_max);
// 將主點轉化爲歸一化的圖像座標系
double wcx = -2*(cx - double(width)/2) / width;
double wcy = 2*(cy - double(height)/2) / height;
camera->SetWindowCenter(wcx, wcy);
// 將焦距轉化爲視場角
double view_angle = (2.0 * std::atan2( height/2.0, fy)) * 180.d / PI;
camera->SetViewAngle( view_angle );
2.拍攝彩色圖和深度圖
2.1.拍攝彩色圖
使用vtkWindowToImageFilter
和vtkJPEGWriter
即可將renderWindow
中的內容保存爲圖片。主要代碼如下:
m_renderWindow->Render();
vtkSmartPointer<vtkWindowToImageFilter> wti = vtkSmartPointer<vtkWindowToImageFilter>::New();
wti->SetInput(m_renderWindow);
vtkSmartPointer<vtkJPEGWriter> jpegWriter = vtkSmartPointer<vtkJPEGWriter>::New();
jpegWriter->SetFileName(filename);
jpegWriter->SetInputConnection(wti->GetOutputPort());
jpegWriter->Write();
m_renderWindow->Finalize();
測試拍攝的圖片如下:
2.2.拍攝深度圖
第一節中已經將相機的內參賦值給vtkCamera
,則使用ZBuffer
和DisplayToWorld
轉化,完成深度圖的採集。主要代碼如下:
// 使用OpenCV的CV_16UC1保存深度圖
cv::Mat depthImage(cv::Size(width, height), CV_16UC1);
// 獲取到renderWindow的ZBuffer(ZBuffer是0~1的深度值,範圍在vtkCamera.SetClippingRange中規定)
float * data = m_renderWindow->GetZbufferData(0, 0, width- 1, height - 1);
double wPos[4];
// 用於將世界座標系轉化爲相機座標系下深度值,外參矩陣的第三行。
double m20 = extrinsicMatrix4x4(2, 0);
double m21 = extrinsicMatrix4x4(2, 1);
double m22 = extrinsicMatrix4x4(2, 2);
double m23 = extrinsicMatrix4x4(2, 3);
for (int y = 0; y < height; y++)
{
uint16_t * dData = depthImage.ptr<uint16_t>(y);
for (int x = 0; x < width; x++)
{
// 獲取ZBuffer對應數值
float depth = data[y*width+ x];
// 爲1時表示無窮遠,無深度
if (depth == 1)
{
dData[x] = 0;
}
else
{
// 將zBuffer轉化爲世界座標系點座標
vtkInteractorObserver::ComputeDisplayToWorld(m_render, x, y, depth, wPos);
// 將世界座標系點座標,轉化爲相機座標系下Z,即深度
float z = wPos[0] * m20 + wPos[1] * m21 + wPos[2] * m22 + m23;
// 將米單位轉化爲毫米單位uint16_t進行存儲
dData[x] = (uint16_t)(z * 1000);
}
}
}
delete data;
// 由於vtkRenderWindow的x、y方向與相機的不一致,需要進行flip。
cv::flip(depthImage, depthImage, 0);
// 保存深度圖
cv::imwrite(filename, depthImage);
保存的深度圖如下所示(與上述彩色圖對應):
3.驗證
使用相機內參,將採集到的深度圖換算爲相機座標系下三維點雲,如下所示:
使用外參進行旋轉平移,得到如下結果:
可以看到換算到世界座標系下的三維點雲與之前拍攝的模型完全重合。
以上完成了使用VTK模擬深度相機拍攝RGB-D圖像的全過程。