0. Background
很久很久以前記錄了一下使用WPF進行三維重建的一些探索,后來了解到了VTK這個開發包,
覺得功能很強大,因此后續都在基於VTK進行三維重建,在前文中對於VTK的一些相關網站進行了介紹
http://www.cnblogs.com/dawnWind/archive/2013/01/14/3D_04.html
這里就不再累贅了,感興趣的Google一下即可。
對於VTK感興趣的還可能會認識到與之相關的幾個開發包。
其一是ITK:http://www.itk.org/
ITK始於著名的(VHP)Visible Human Project
http://www.nlm.nih.gov/research/visible/visible_gallery.html
簡要地說VHP就是使用現代技術使用醫學掃描等方式(如X光、CT、CMR等)獲得人體二維圖片,並根據這些圖片構建出三維模型,
當然這里說要求的精度以及細度都非常高。人體的三維模型在我們頭腦中可能立馬會閃現很多電影里面有的場景,
一個組織脈絡都清晰可見的人體呈現在我們面前。可以說在那個時代美國提出的這一科技項目不僅是高屋建瓴實際上
也給后續相關技術的發展帶來了巨大的推動。
不過ITK說關注的更多是三維數據的測量、切割等,如果要進行三維呈現通常可以和VTK進行協作,這兩者之間的數據
互通也很方便。不過對於ITK的了解我也就到此為止,並沒有安裝並使用過。
國內對於ITK、VTK開發包類似的研究主要由田捷帶領開發的MITK,它整合ITK、VTK,為了達到一致簡潔的開發,主要針對的領域是醫學處理。
背景知識就這么多,接下來說一下使用VTK進行三維重建的基本步驟。
1. 3D Reconstruction with VTK
先上碼。
// 讀取文件夾下圖片,將圖像進行輪廓提取后再進行三維重建 int build3DViewFull() { vtkSmartPointer<vtkRenderer> aRenderer = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New(); renWin->AddRenderer(aRenderer); vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New(); iren->SetRenderWindow(renWin); // 新建文件讀取對象,常見的有vtkBMPReader、vtkDICOMImageReader、vtkJPEGReader等 vtkSmartPointer<vtkJPEGReader> jpegReader = vtkSmartPointer<vtkJPEGReader>::New(); // 不同的reader需要設置的參數是不同的 因此本例僅適合jpegreader jpegReader->SetFilePrefix("C:/Users/DawnWind/Desktop/000/"); // 要打開的路徑 jpegReader->SetFilePattern("%s%d.jpg"); // 圖片文件名格式,此處為 0.jpg 1.jpg ... jpegReader->SetDataByteOrderToLittleEndian(); jpegReader->SetDataSpacing(1, 1, 1.4); // 設置圖片中像素比,我理解得不清楚,具體請百度之 jpegReader->SetFileNameSliceSpacing(1); jpegReader->SetDataExtent(0, 209, 0, 209, 0, 29); // 這里因為在000文件夾里面有0.jpg ~ 29.jpg,所以設置為 0,29 // 每張圖片的長寬為210 * 210 因此設置為0,209 jpegReader->Update(); // update這里要注意一下,對於VTK在默認情況下是在最后操作時候才一次性刷新 // 也就是說如果沒有自動刷新的話,在一些中間過程中是無法獲得到數據的,因為沒update進去 vtkSmartPointer<vtkContourFilter> skinExtractor = vtkSmartPointer<vtkContourFilter>::New(); skinExtractor->SetInputConnection(jpegReader->GetOutputPort()); skinExtractor->SetValue(200, 100); //值越大,保留的部分越少。 //重新計算法向量 vtkSmartPointer<vtkPolyDataNormals> skinNormals = vtkSmartPointer<vtkPolyDataNormals>::New(); skinNormals->SetInputConnection(skinExtractor->GetOutputPort()); skinNormals->SetFeatureAngle(60.0); //Specify the angle that defines a sharp edge. //If the difference in angle across neighboring polygons is greater than this value, //the shared edge is considered "sharp". //create triangle strips and/or poly-lines 為了更快的顯示速度 vtkSmartPointer<vtkStripper> skinStripper = vtkSmartPointer<vtkStripper>::New(); skinStripper->SetInputConnection(skinNormals->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> skinMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); skinMapper->SetInputConnection(skinStripper->GetOutputPort()); skinMapper->ScalarVisibilityOff(); //這樣不會帶顏色 vtkSmartPointer<vtkActor> skin = vtkSmartPointer<vtkActor>::New(); skin->SetMapper(skinMapper); // An outline provides context around the data. // 一個圍繞在物體的立體框,可以先忽略 /* vtkSmartPointer<vtkOutlineFilter> outlineData = vtkSmartPointer<vtkOutlineFilter>::New(); outlineData->SetInputConnection(dicomReader->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> mapOutline = vtkSmartPointer<vtkPolyDataMapper>::New(); mapOutline->SetInputConnection(outlineData->GetOutputPort()); vtkSmartPointer<vtkActor> outline = vtkSmartPointer<vtkActor>::New(); outline->SetMapper(mapOutline); outline->GetProperty()->SetColor(0,0,0); aRenderer->AddActor(outline); */ // It is convenient to create an initial view of the data. The FocalPoint // and Position form a vector direction. Later on (ResetCamera() method) // this vector is used to position the camera to look at the data in // this direction. vtkSmartPointer<vtkCamera> aCamera = vtkSmartPointer<vtkCamera>::New(); aCamera->SetViewUp (0, 0, -1); aCamera->SetPosition (0, 1, 0); aCamera->SetFocalPoint (0, 0, 0); aCamera->ComputeViewPlaneNormal(); aCamera->Azimuth(30.0); aCamera->Elevation(30.0); // Actors are added to the renderer. An initial camera view is created. // The Dolly() method moves the camera towards the FocalPoint, // thereby enlarging the image. aRenderer->AddActor(skin); aRenderer->SetActiveCamera(aCamera); aRenderer->ResetCamera (); aCamera->Dolly(1.5); // Set a background color for the renderer and set the size of the // render window (expressed in pixels). aRenderer->SetBackground(.2, .3, .4); renWin->SetSize(640, 480); // Note that when camera movement occurs (as it does in the Dolly() // method), the clipping planes often need adjusting. Clipping planes // consist of two planes: near and far along the view direction. The // near plane clips out objects in front of the plane; the far plane // clips out objects behind the plane. This way only what is drawn // between the planes is actually rendered. aRenderer->ResetCameraClippingRange (); // Initialize the event loop and then start it. iren->Initialize(); iren->Start(); return 0; }
通過上面代碼的可以看出,對於一個三維重建是有一些必須步驟要走的。
一是要有輸入源(上文中是reader讀入的數據)通過處理構成的模型actor、二是要有相機(camera)、三要有用於展示的窗口(window)
其他的就是將其中的脈絡理清了,可以仿照上面代碼所述進行理解,具體理論可以去查找一下OpenGL的三維模型結構,其實與我前面說的
WPF三維模型相關理論是相通的。
可以獲得類似下圖結果:
號外:關於VTK的設置之類就沒有多說了,假設大家已經可以運行VTK的示例