6.6 数据集的存储与表达
可視化數據自身的特點決定了數據對象內存的分配與管理必須謹慎處理才有可能創建出高效的可視化系統。VTK中對絕大多數的數據對象的內存分配采用連續內存,連續內存的結構可被快速地創建、刪除和遍歷,稱為Data Array (數據數組),用類vtkDataArray實現。
6.6.1 vtkDataArray
數據數組的訪問是基于索引的,從零開始計數。我們以vtkFloatArray類來說明如何在VTK中實現連續內存的數據數組。如圖6.13所示,變量Array是一個指向浮點型數組的指針,數組的長度由變量Size指定,由于數組的長度是動態地增加的,所以當存儲數據的數組長度超出指定的長度時,會自動觸發Resize()操作來調整數組的長度,使數組的長度變成原來的兩倍,MaxId是一個整型的偏移量,用來定義最后一個插入的數據的索引。如果沒有數據插入,MaxId等于-1,否則,MaxId的值介于0到Size之間,即0≤MaxId<Size。
?
圖6.13連續數組的實現
6.6.2 Tuple(元組)的概念
許多可視化數據是由多個數據分量組成的,如RGB顏色數據由紅、綠、藍三個分量組成,為了在連續數組中表達這一類數據,VTK引入了元組(Tuple)的概念。元組是數據數組的子數組,用于存儲數據類型相同的分量數據,圖6.13所示的NumberOfComponents,表示的就是數據數組里元組的個數。元組的大小在給定后不會改變,圖6.14所示的數據數組由n個元組組成,每個元組存儲三個分量數據。
?
圖6.14數據數組結構
vtkDataArray存儲的是數值數據,如屬性數據(Attribute Data)和點數據(Point)等。有些屬性數據,如點、矢量、法向量和張量等,在定義時就需要指定元組的大小。例如,點、矢量和法向量等屬性數據,元組的大小是3,而張量屬性數據的元組大小是9 (即3×3的矩陣),標量屬性數據對于元組的大小則沒有任何要求,對于處理標量屬性數據的算法,通常都是只處理標量每一個元組數據的第一個分量。VTK提供了將多分量的數據數組分離成單一分量的數據數組,以及將單一分量的數據數組合并成多分量的數據數組的類,即vtkSplitField和vtkMergeFields。
下列代碼演示了如何創建固定長度及動態的數據數組,以加深對Data Array及Tuple概念的理解:
?
/**************************************************************************
固定長度的數據數組(DataArray)。下列代碼創建了容量為20個元組(Tuple)的數據數組,
每個元組的分量個數為1,通過方法SetComponent()和GetComponent()設置及獲取相應
的元組的值。
**************************************************************************/
vtkSmartPointer<vtkFloatArray > arr = vtkSmartPointer< vtkFloatArray >::New();
arr->SetNumberOfComponents(1);//設置元組的分量個數為1
arr->SetNumberOfTuples(20);//指定數據數組的長度為20個元組
arr->SetComponent(10,0, 10.0); //指定第10個元組的第0個分量的值為10.0
arr->SetTuple1(11,9.0); //指定第11個元組的值為9.0
doubleb = arr->GetComponent(10, 0); //獲取第10個Tuple的第0個分量的值
?
/**************************************************************************
動態長度的數據數組。下列代碼創建了一個具有動態長度的數據數組,每個元組的分
量個數為1,通過方法InsertNextTuple1()插入一個單分量的元組。與InsertNextTuple1()
類似的還有InsertNextTuple2()/InsertNextTuple3()/InsertNextTuple4()/InsertNextTuple9()等。
**************************************************************************/
vtkSmartPointer<vtkFloatArray > arr = vtkSmartPointer< vtkFloatArray >::New();
arr->SetNumberOfComponents(1);//設置元組的分量個數為1
arr->InsertNextTuple1(5);? //插入一個單分量的元組,其值為5
arr->InsertNextTuple1(10);
doubleb = arr->GetComponent(1, 0);
6.6.3 抽象/具體數據數組對象
可視化數據有各種各樣的類型,如簡單的浮點型、整型、字節型和雙精度型等,再復雜一點的類型,如特征字符串和多維標識符等。既然有這么多種數據類型,那么數據數組是如何操作和表達這些數據的呢?VTK通過對數據對象的抽象(AbstractData Object)提供運行時解決方案以及使用C++編譯時動態綁定的方法(模板類)來解決這個問題的。如圖6.15所示,vtkDataArray是一個抽象基類,其子類實現特定類型的數據數組及相關操作。
?
圖6.15 數據數組對象(只列出部分數據數組類)
抽象數據對象通過動態綁定的方式使用統一的接口來創建、操作和刪除數據,C++中用virtual關鍵字來聲明動態綁定方法。動態綁定允許我們通過操作抽象父類的方法來調用具體子類對象的實現。以vtkDataArray為例,我們可以調用該抽象父類的方法GetTuple1(129),來獲取ID為129的點數據值。因為GetTuple1()方法是在抽象父類vtkDataArray中定義的虛函數,且返回的數據類型是double,因此每一個從vtkDataArray派生的子類,都必須實現該方法,返回一個double型的數據。
虛函數的使用有其特定的優勢之處,例如不用考慮具體的數據類型,而寫出更加通用的可視化算法。但大量虛函數使用會導致程序性能的下降。為了處理各種各樣的數據類型,vtkDataArray還采用了模板類的方法。
要使用模板類,需要知道原始數據的類型以及指向該數據的指針。一般vtkDataArray使用類似以下的代碼:
switch(outData->GetScalarType() )
{
??? case VTK_CHAR
??? {?typedef char VTK_TT;
?????? func (arg1, arg2, arg3, VTK_TT* arg4,VTK_TT* arg5); }
??? break;
??? case VTK_UNSIGNED_CHAR
??? {?typedef unsigned char VTK_TT;
??????func (arg1, arg2, arg3, VTK_TT* arg4, VTK_TT* arg5); }
??? break;
??? … for all types …
}
實際的VTK代碼使用了宏以及C++的操作符static_cast<>來進行類型的轉換。注意以上代碼中的func()是一個模板函數。例如(摘自vtkImageCursor3D.cxx127-134行):
? switch (outData->GetScalarType())
??? {
??? vtkTemplateMacro(
????? vtkImageCursor3DExecute(this,outData,static_cast<VTK_TT *>(ptr)));
??? default:
????? vtkErrorMacro(<< "Execute:Unknown ScalarType");
????? return 1;
??? }
模板函數的使用相較于虛函數而言,雖然能在一定程度上提高程序的性能,但同時也增加了代碼的復雜性。
6.6.4 數據對象和數據集的表達
vtkDataArray及其子類是建立VTK數據對象(DataObject)的基礎。以vtkPolyData為例,該類含有存儲幾何結構的數據數組(在vtkPoints類內),拓撲結構(存儲在vtkCellArray內)和屬性數據(vtkField,vtkPointData和vtkCellData類內)等同樣有數據數組。vtkDataObject是一種通用的可視化數據的表達形式,內部封裝了與可視化管線的執行相關的變量和方法,在vtkDataObject內部有一個vtkFieldData(場數據)的實例,負責對數據的表達。如圖6.16所示,場數據(FieldData)可以看作是數組的數組,數組里的每一個元素都一個數組,數組的類型、長度、元組的大小和名稱等都可以各不相同。VTK里的可視化算法很少有直接對vtkDataObject作處理,大多數的算法更關心的是待處理數據的組織結構(OrganizingStructure)等信息。
?
圖6.16vtkDataObject數據對象的表達
圖6.17是類vtkFieldData的繼承圖,從類的名字我們能夠推斷出,vtkFieldData存儲的數據是與數據對象的屬性數據相關的。以vtkPolyData為例,vtkPolyData內部存儲了三種類型的數據,分別是vtkPointData、vtkCellData和vtkFieldData。vtkPointData是與每一個點相關聯的數據,如某點上的溫度值;vtkCellData是與每一個單元相關聯的數據,如某個三角形單元的面積;除點和單元數據以外的數據,應該使用vtkFieldData,如數據的質心(Centerof mass)。
?
圖6.17vtkFieldData類的繼承圖
關于vtkFieldData的使用,請參考下列代碼。
int main(int, char *[])
{
?vtkSmartPointer<vtkSphereSource> source =vtkSmartPointer<vtkSphereSource>::New();
? source->Update();
?
? // Extract thepolydata
? vtkSmartPointer<vtkPolyData>polydata = vtkSmartPointer<vtkPolyData>::New();
?polydata->ShallowCopy(source->GetOutput());
?
? vtkSmartPointer<vtkDoubleArray>location = vtkSmartPointer<vtkDoubleArray>::New();
?
? // Create the data tostore (here we just use (0,0,0))
? doublelocationValue[3] = {0,0,0};
?location->SetNumberOfComponents(3);
?location->SetName("MyDoubleArray");
?location->InsertNextTuple(locationValue);
?
? // The data is addedto FIELD data (rather than POINT data as usual)
?polydata->GetFieldData()->AddArray(location);
?
? vtkSmartPointer<vtkIntArray>intValue = vtkSmartPointer<vtkIntArray>::New();
?intValue->SetNumberOfComponents(1);
?intValue->SetName("MyIntValue");
?intValue->InsertNextValue(5);
?
?polydata->GetFieldData()->AddArray(intValue);
?
? return 0;
}
?
這一章有幾個概念比較容易混淆,包括數據對象(vtkDataObject)、數據集(vtkDataSet)、數據數組(vtkDataArray)等,理解它們之間的關系,有助于我們更容易地看懂VTK的代碼,寫出高效的VTK可視化程序。其中數據集DataSet與數據數組DataArray之間的關系,在《TheVTK User’s Guide – 11th Edition 》這本書的第16章第1段有如下的內容:“In this section we provide detailed information describingthe interface to the many data objects in VTK. This ranges fromdatasets,which are processed by the filterobjects in visualization pipelines, todata arrays, whichare used to represent a portion of a dataset (e.g., the scalar data).”
從上面這段話我們大概能夠搞清楚Dataset (數據集)與DataArray (數據數組)這兩個概念的區別。簡單地理解為,數據集是VTK可視化管線所處理的對象;而數據數組是用于表達數值數據的內存組織形式,比如用數據數組來表示數據集里的標量數據。后續章節將會以程序示例的形式來加深對這些概念的理解。
==========歡迎轉載,轉載時請保留該聲明信息==========
版權歸@東靈工作室所有,更多信息請訪問東靈工作室
教程系列導航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
總結
以上是生活随笔為你收集整理的6.6 数据集的存储与表达的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 卷积神经网络CNN(5)—— Dilat
- 下一篇: 感染新冠后为啥会丧失嗅觉?最新《细胞》