使用颜色映射标量数据是数据可视化中为了表现多个维度的数据时常用的一种手段
要作出这种图,数据类型一般为一个空间中的平面数据或者三维模型数据再加上一个或者多个的标量数据,在VTK中使用这样的数据类型需要了解两个关键类,vtkDataSetAttributes和vtkDataSet。
vtkDataSet
vtkDataSet是一个抽象类,不能直接实例化,它是VTK所有数据集类型的父类,主要为数据集类型声明了一些通用接口以及成员。
从这个继承树中可以看到vtkDataSet所有的子类。
实现颜色标量映射主要关注的只有两个接口:
这个两个数据分别对应的是单元数据集点数据集。
单元数据集中的单元指得是计算机图形的图元集合,如果是多边形数据集那么它就是一个三角网格中所有三角形的集合,如果使用结构或者非结构化网格数据,那么它就是网格单元数据的集合。
点数据集合是单元集合中所用到所有的点的集合,根据数据集的类型不同,包含的数据也略微有点不同。
vtkDataSetAttributes
上述的vtkCellData和vtkPointData都继承于vtkDataSetAttributes,它可以用于包含标量、向量、法线、纹理坐标、张量、全局 id、谱系 id 和字段数据等数据。
// Description: // Set/Get the scalar data. int SetScalars(vtkDataArray* da); int SetActiveScalars(const char* name); vtkDataArray* GetScalars();// Description: // Set/Get the vector data. int SetVectors(vtkDataArray* da); int SetActiveVectors(const char* name); vtkDataArray* GetVectors();// Description: // Set/get the normal data. int SetNormals(vtkDataArray* da); int SetActiveNormals(const char* name); vtkDataArray* GetNormals();// Description: // Set/Get the texture coordinate data. int SetTCoords(vtkDataArray* da); int SetActiveTCoords(const char* name); vtkDataArray* GetTCoords();// Description: // Set/Get the tensor data. int SetTensors(vtkDataArray* da); int SetActiveTensors(const char* name); vtkDataArray* GetTensors();// Description: // Set/Get the global id data. int SetGlobalIds(vtkDataArray* da); int SetActiveGlobalIds(const char* name); vtkDataArray* GetGlobalIds();// Description: // Set/Get the pedigree id data. int SetPedigreeIds(vtkAbstractArray* da); int SetActivePedigreeIds(const char* name); vtkAbstractArray* GetPedigreeIds();
以上接口对应了每种数据的获取与设置,每种数据似乎可以添加多个序列,然后可以使用序列名称调整当前使用的数据,序列名称通过vtkDataArray类setName函数进行设置。序列数据中的顺序需要对应实际数据的顺序。这里的顺序指的是ID,如Point数据,每个点都会有一个ID,那么为这些点数据附加标量数据的时候,标量数据的传入顺序就需要与id一一对应,如果是cellData那么也同理。
vtkLookupTable
这个类提供一个从标量到颜色的映射表
//设置颜色的过度模式 void SetRamp(int); void SetRampToLinear();//线性 void SetRampToSCurve();//s曲线 void SetRampToSQRT();//y=sqrt(x) int GetRamp(); //设置颜色的与标量的映射模式 void SetScale(int); void SetScaleToLinear();//线性 void SetScaleToLog10();//对数 int GetScale(); void SetTableRang(double r[2]);//设置标量的范围(最小,最大值) void SetHueRang(double,double);//设置色调范围,取[0,1]之间 void SetSaturationRange(double,double);//设置饱和度,取[0,1]之间 void SetValueRange(double,double);//设置颜色值的取值范围,取[0,1]之间 void SetAlphaRange();//设置透明的取值范围,取[0,1]之间 void SetNanColor(double,double,double,double);//设置遇到Nan(非数)时使用的颜色 void SetBelowRangeColor(double,double,double,double);//设置当低于范围使用的颜色 void SetAboveRangeColor(double,double,double,double);//设置当超出范围时使用的颜色 void SetNumberOfTableValues(vtkIdType number);//设置颜色数量(色块) void SetTableValue(vtkIdType indx,double r, double g, double b, double a=1.0);//直接设置颜色值 //根据各种参数生成颜色表 void Build();
这是它的一些关键接口,通过这些结构可以构造一个完整的颜色表,然后再调用映射器对象的SetLookupTable (vtkScalarsToColors *lut)函数设置即可。
vtkScalarBarActor
这个类不是颜色映射时必须要的类,但是它可以直观的将颜色映射表显示到窗口上,非常的方便。
使用起来也很简单,它继承于vtkActor,只需要调用SetLookupTable (vtkScalarsToColors *)函数设置好对应的颜色表,然后将Actor添加到渲染器中即可
示例
//为三角形准备三个顶点 vtkNew<vtkPoints> points; points->InsertNextPoint(1, 0, 0); points->InsertNextPoint(0, 0, 0); points->InsertNextPoint(0, 1, 0); //生成点的标量数据 vtkNew<vtkIntArray> scalar; scalar->InsertNextTuple1(30); scalar->InsertNextTuple1(50); scalar->InsertNextTuple1(100); //使用顶点构建一个三角形 点使用ID传入 vtkNew<vtkTriangle> triangle; triangle->GetPointIds()->SetId(0, 0); triangle->GetPointIds()->SetId(1, 1); triangle->GetPointIds()->SetId(2, 2); //将三角形传入单元集 vtkNew<vtkCellArray> cellArray; cellArray->InsertNextCell(triangle); //为多边形数据添加单元集和点集 vtkNew<vtkPolyData> polydata ; polydata->SetPolys(cellArray); polydata->SetPoints(points); //为点数据添加标量 polydata->GetPointData()->SetScalars(scalar); vtkNew<vtkPolyDataMapper> mapper; mapper->SetInputData(polydata); mapper->SetScalarRange(scalar->GetRange()); mapper->Update(); //为映射器添加一个颜色映射表 vtkNew<vtkLookupTable> lookupTable; lookupTable->SetTableRange(scalar->GetRange()); lookupTable->Build(); mapper->SetLookupTable(lookupTable); //为窗口添加一个图例 vtkNew<vtkScalarBarActor> scalarBarActor; scalarBarActor->SetLookupTable(lookupTable); vtkNew<vtkActor> actor; actor->SetMapper(mapper); actor->GetProperty()->SetColor(255, 0, 0); vtkNew<vtkRenderer> renderer; vtkNew<vtkRenderWindow> window; window->AddRenderer(renderer); vtkNew<vtkRenderWindowInteractor> interactor; interactor->SetRenderWindow(window); renderer->AddActor(actor); renderer->AddActor(scalarBarActor); window->Render(); interactor->Start();
代码实现了一个三角形的构建,并为三角形的三个顶点添加标量,然后在显示时使用标量进行颜色映射,可以说这是一个最简单的标量映射的一个例子。
需要注意的是因为代码中是为point添加的标量,除了顶点以外的其他位置的标量值都是经过线性插值得到的。
再构建mapper对象和lookupTable对象是一定要为对象设置标量范围,否则会导致图例的范围与标量范围不一致,然后绘制出错误的图标。(我在这里翻了不少车)
最后的碎碎念
颜色映射的本质就是将标量数据附加到单元或者点上,然后再绘制的时候通过点集或者单元集的数据在颜色表中查找标量值对应的颜色,然后进行绘制。因为点是离散的,所以在标量绘制时可以选择为数据进行插值,这样就可以得到类似文章开头所展示的效果。
另外欢迎看到博客的有缘人留言一起讨论。
文章评论
您好,请我为什么我用QT5.14.2+VTK8.2的时候,加入VTKScalarbar显示图例程序直接异常结束?
@CGerald 可能是你忘记了初始化VKT的一些模块,尝试将以下代码添加到你的工程。
#ifndef INIT_VTK_OPENGL_AND_FRNT //防止多次初始化模块
#define INIT_VTK_OPENGL_AND_FRNT
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2) //初始化opengl渲染器
VTK_MODULE_INIT(vtkRenderingFreeType) //初始化字体渲染器
VTK_MODULE_INIT(vtkInteractionStyle)
#endif //INIT_VTK_OPENGL_AND_FRNT
可以将这段代码放到main.cpp里
@大脸猫 非常感谢非常感谢!请问可以加我wx吗C-GERALD,想跟你请教!
我想让颜色条的数字格式化显示,请问有接口吗
@OTzcppdebugz23 数字格式化显示是啥意思 ?
@大脸猫 就是说颜色条的数字小数点后位数固定,比如精确到小数点后三位。
@OTzcppdebugz23 可以使用vtkScalarBarActor类的GetCustomLabels ()函数,这个虚函数可以自定bar显示的标签,在使用之前记得调用 UseCustomLabelsOn ()开启自定义标签
我想在一个有限元网格中给每个三角形单元设置标量来表示不同单元的电导率,请问应该怎样设置?
@zoe 我不知道你用于作图的数据形式,所以无法给出准确的建议,如果你用于绘制有限元网格结构的数据是三角面数据,那么你需要将每个网格上的数据对应到数据点上,然后把数据传入即可。
@大脸猫 就是已经用GMSH 生成了网格,我想给这些网格中的每一个三角形赋值然后显示,就是你文章中三角形顶点赋值了,我想要这个三角形单元赋值,该怎么做?
@zoe 将代码中的GetPointData()函数换成GetCellData()即可,不过需要注意标量的数量和单元的数量一一对应
@大脸猫 请问如果我用vtkUnstructuredGridReader 读入了数据 reader,该怎样GetCellData()呢?
请问可以将一个三角形单元给标量吗?
@zoe 你需要使用vtkUnstructuredGridReader的GetOutput ()函数得到一个vtkUnstructuredGrid对象,然后就可以使用GetCellData()函数了
@大脸猫 可是读入以后vtkUnstructuredGrid不就已经有三角形单元信息了吗,还需要再用GetCellData()函数了吗?
@zoe 是的,GetCellData()只是获取(并不是重新创建)这些单元对象,获取到这些单元对象之后,你才能在这些数据上添加标量。
@大脸猫 对不起昨天的我搞懂了,我想问一下标量值是否可以是是负数?
@大脸猫 reader->GetOutput()->GetCellData()->SetScalars(sigma);这样就会报错提示不允许指针指向不完整的类类型 ,sigmas是我设置的标量数据,reader是读入的Grid对象
@zoe 这是一个关于C++的基础问题,当你没有得到对象的完整声明的时候,可以获取对象指针,也可以传递赋值,但是不能调用对象的任何成员,否则编译时就会报这个错误。你检查一下,你调用的这个几个函数的返回值类型,是否漏掉了引入他们的头文件!
@zoe 可以为负数,使用时要注意vtkScalarBarActor也用给负数值映射对应的颜色表