前言
“VTK图形图像开发进阶_张晓东_罗火灵”的学习笔记。
东灵工作室 教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686
学习资料
VTK官网学习地址:https://vtk.org/doc/nightly/html/
学习笔记
VTK图像基本操作
熟练掌握这些基本操作有助于使用VTK进行图 像处理应用程序的快速开发。
图像信息的访问与修改
利用vtklmageData的方法
获取图像维数、图像原点、像素间隔。
vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();
reader->SetFileName ( argv[1] );
reader->Update();
int dims[3];
reader->GetOutput()->GetDimensions(dims);
std::cout<<"图像维数:" <<dims[0]<<" "<<dims[1]<<" "<<dims[2]<<std::endl;
double origin[3];
reader->GetOutput()->GetOrigin(origin);
std::cout<<"图像原点:" <<origin[0]<<" "<<origin[1]<<" "<<origin[2]<<std::endl;
double spaceing[3];
reader->GetOutput()->GetSpacing(spaceing);
std::cout<<"像素间隔:" <<spaceing[0]<<" "<<spaceing[1]<<" "<<spaceing[2]<<std::endl;
利用类 vtkChangelmagelnformation
修改图像的原点、像素间隔以及范围、实现图像平移缩放等功能
vtkSmartPointer<vtkImageChangeInformation> changer = vtkSmartPointer<vtkImageChangeInformation>::New();
changer->SetInputData(reader->GetOutput());
changer->SetOutputOrigin(100, 100, 0);
changer->SetOutputSpacing(5,5,1);
changer->SetCenterImage(1);
changer->Update();
changer->GetOutput()->GetDimensions(dims);
std::cout<<"修改后图像维数:" <<dims[0]<<" "<<dims[1]<<" "<<dims[2]<<std::endl;
changer->GetOutput()->GetOrigin(origin);
std::cout<<"修改后图像原点:" <<origin[0]<<" "<<origin[1]<<" "<<origin[2]<<std::endl;
changer->GetOutput()->GetSpacing(spaceing);
std::cout<<"修改后像素间隔:" <<spaceing[0]<<" "<<spaceing[1]<<" "<<spaceing[2]<<std::endl;
图像像素值的访问与修改
方法一 直接访问vtklmageData的数据数组 GetScalarPointer
vtkSmartPointer<vtkBMPReader> reader =
vtkSmartPointer<vtkBMPReader>::New();
reader->SetFileName(argv[1]);
reader->Update();
int dims[3];
reader->GetOutput()->GetDimensions(dims); //得到图像大小
int nbOfComp;
nbOfComp = reader->GetOutput()->GetNumberOfScalarComponents(); //获取像素的元组组分数(是灰度图、梯度图、RGB图)
for(int k=0; k<dims[2]; k++)
{
for(int j=0; j<dims[1]; j++)
{
for(int i=0; i<dims[0]; i++)
{
if(i<100 && j<100)
{
unsigned char * pixel = (unsigned char *) ( reader->GetOutput()->GetScalarPointer(i, j, k) );
*pixel = 0;
*(pixel+1) = 0;
*(pixel+2) = 0;
}
}
}
}
方法二 vtklmagelterator类实现迭代器方法访问图像像素
vtkSmartPointer<vtkBMPReader> reader =
vtkSmartPointer<vtkBMPReader>::New();
reader->SetFileName ( argv[1] );
reader->Update();
int subRegion[6] = {0,300, 0, 300, 0, 0}; //定义一个子区域(不能超过图像大小范围)
//根据图像类型unsigned char 定义一个图像迭代器it,两个参数(要访问的图像、要访问的图像区域)
vtkImageIterator<unsigned char> it(reader->GetOutput(), subRegion);
while(!it.IsAtEnd())
{
unsigned char *inSI = it.BeginSpan(); //获取第一个组分
unsigned char *inSIEnd = it.EndSpan(); //表示组分迭代完毕
while(inSI != inSIEnd) //判断是否结束
{
*inSI = 255-*inSI;
++inSI; //迭代组分
}
it.NextSpan(); //组分迭代完毕后继续迭代像素it至下一个元祖
}
图像类型转换
常用的图像算子(例如梯度算子) 在计算时出于精度的考虑,会将结果存储为float或double类型,但在图像显示时,一般要求 图像为unsigned char类型,这时就需要对数据类型曲行转换。
vtklmageCast
VTK中最简单的类型转换Filter 就是vtklmageCast。
vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New();
reader->SetFileName(argv[1]);
reader->Update();
vtkSmartPointer<vtkImageCast> imageCast = vtkSmartPointer<vtkImageCast>::New();
imageCast->SetInput((vtkDataObject *)reader->GetOutput());
imageCast->SetOutputScalarTypeToFloat();
只再要把SetOutputScalarTypeToxxx()设置成相应的输出类型即可。
该类有一个变量ClampOverflow用来表示是否需要截断数据,默认情况下,该变量值为0。当设置 其值为1时,输出的像素值不能超过输出类型的最大值,超过时自动截断至最大值。
VTK中也不推荐使用该类.例如一幅double类型的图像,其数值范围为「-1,1],如果需要 图像转换为unsigned char类,无法使用该Filter进行转换。这时就需要用到 vtklmageShiRScale。
vtklmageShiRScale
vtklmageShiRScale可以指定偏移和比例参数来对输入图像数据进行操作例如一幅 double类型的图像,其数值范围为[-1,1],如果将其转换为unsigned char类型,需要设置shift值为+1,比例系数设置为127.5,那么输入数据-1映射为(-1+1)X 127.5 = 0;而+1则为 ( + 1 + 1)X 127.5=255。对应代码如下
vtkSmartPointer<vtkImageShiftScale> shitScaleFilter = vtkSmartPointer<vtkImageShiftScale>::New();
shitScaleFilter->SetInputConnection(reader->GetOutputPort());
shitScaleFilter->SetOutputScalarTypeToUnsignedChar();
shitScaleFilter->SetShift(1); //设置偏移量
shitScaleFilter->SetScale(255/2.0); //设置缩放量
shitScaleFilter->Update();
该类中也有一个変量ClampOverflow其值为1时,如果输出值超过输出类型的最大值时,则自动截断。例如,输出类型为unsigned char, 数值范围为0〜255,当输出像素值为257时,该类会动截断取值为255,默认情况变量ClampOverflow的值为0,此时,当输出值为257,输出类型为unsigned char时,该类不会将其截断,而是会产生溢出,最好取值为2。
图像颜色映射
图像灰度映射
vtklmageLuminance负责将一个RGB彩色图像转换为一个单组分的灰度图像。
映射公式为
vtkSmartPointer<vtkImageLuminance> luminanceFilter = vtkSmartPointer<vtkImageLuminance>::New();
luminanceFilter->SetInput(reader->GetOutput());
luminanceFilter->Update();
提取颜色组分
VTK中利用vtklmageExtractComponents可以方便地提取彩色图像的各个颜色组分。使 用该类时只需要设置要提取的组分序号即可。
vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();
reader->SetFileName (argv[1]);
vtkSmartPointer<vtkImageExtractComponents> extractRedFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
extractRedFilter->SetInputConnection(reader->GetOutputPort());
extractRedFilter->SetComponents(0);
extractRedFilter->Update();
vtkSmartPointer<vtkImageExtractComponents> extractGreenFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
extractGreenFilter->SetInputConnection(reader->GetOutputPort());
extractGreenFilter->SetComponents(1);
extractGreenFilter->Update();
vtkSmartPointer<vtkImageExtractComponents> extractBlueFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
extractBlueFilter->SetInputConnection(reader->GetOutputPort());
extractBlueFilter->SetComponents(2);
extractBlueFilter->Update();
图像彩色映射
图像彩色映射的原理是:先生成一个颜色查找表,然后根据图像像素的一个标量值在颜色查找表中查找对应的颜色,并用新颜色值替代原来的像素值,用vtkLookUpTable 生成颜色查找表,以下代码展示如何进行彩色映射。
参考文献:https://blog.csdn.net/qq_40734628/article/details/88091327
实例一
#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
#include <vtkVersion.h>
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkDelaunay2D.h>
#include <vtkLookupTable.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkXMLPolyDataWriter.h>
// For compatibility with new VTK generic data arrays
#ifdef vtkGenericDataArray_h
#define InsertNextTupleValue InsertNextTypedTuple
#endif
int main(int, char *[])
{
// Create a grid of points (height/terrian map)
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
unsigned int GridSize = 20;
double xx, yy, zz;
//随机生成一个三维坐标,并插入vtkPoints中
for(unsigned int x = 0; x < GridSize; x++)
{
for(unsigned int y = 0; y < GridSize; y++)
{
xx = x + vtkMath::Random(-.2, .2);
yy = y + vtkMath::Random(-.2, .2);
zz = vtkMath::Random(-.5, .5);
points->InsertNextPoint(xx, yy, zz);
}
}
//多边形数据集 Add the grid points to a polydata object
vtkSmartPointer<vtkPolyData> inputPolyData = vtkSmartPointer<vtkPolyData>::New();
inputPolyData->SetPoints(points);
//三角化之后,每个面片都是一个三角形,由三个点确定一个三角形 Triangulate the grid points
vtkSmartPointer<vtkDelaunay2D> delaunay = vtkSmartPointer<vtkDelaunay2D>::New();
//根据vtk的版本来选择数据输入方式
#if VTK_MAJOR_VERSION <= 5
delaunay->SetInput(inputPolyData);
#else
delaunay->SetInputData(inputPolyData);
#endif
delaunay->Update();
vtkPolyData* outputPolyData = delaunay->GetOutput();
//这个数组里面存放了六个坐标范围极值
//分别是x轴方向最小最大坐标值
//y轴方向最小最大坐标值
//Z轴方向最小最大坐标值
double bounds[6];
outputPolyData->GetBounds(bounds);
// Find min and max z
//数组最后两个值存放的是Z轴方向最小最大坐标值
double minz = bounds[4];
double maxz = bounds[5];
std::cout << "minz: " << minz << std::endl;
std::cout << "maxz: " << maxz << std::endl;
//颜色操作表 Create the color map
vtkSmartPointer<vtkLookupTable> colorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
//这里如果不设置大小范围的话,默认的范围是0-1
//这里根据自己的代码需要灵活设置颜色映射的范围值
colorLookupTable->SetTableRange(minz, maxz);
colorLookupTable->Build();
// Generate the colors for each point based on the color map
vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3);
colors->SetName("Colors");
//20*20
std::cout << "There are " << outputPolyData->GetNumberOfPoints() << " points." << std::endl;
for(int i = 0; i < outputPolyData->GetNumberOfPoints(); i++)
{
double p[3];
outputPolyData->GetPoint(i,p);
double dcolor[3];
//根据z轴的坐标值的数据来获得一个颜色标量值
//(使用lookupTable颜色查找表来查找)
//查找到的标量值放入dcolor中
colorLookupTable->GetColor(p[2], dcolor);
std::cout << "dcolor: "
<< dcolor[0] << " "
<< dcolor[1] << " "
<< dcolor[2] << std::endl;
unsigned char color[3];
//把获取到的颜色标量转换为颜色值,并存入颜色标量colors中
for(unsigned int j = 0; j < 3; j++)
{
color[j] = static_cast<unsigned char>(255.0 * dcolor[j]);
}
std::cout << "color: "
<< (int)color[0] << " "
<< (int)color[1] << " "
<< (int)color[2] << std::endl;
colors->InsertNextTupleValue(color);
}
//给每一个点设置一个颜色,这个颜色是根据z轴的大小来设置的
outputPolyData->GetPointData()->SetScalars(colors);
// Create a mapper and actor
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
#if VTK_MAJOR_VERSION <= 5
mapper->SetInputConnection(outputPolyData->GetProducerPort());
#else
mapper->SetInputData(outputPolyData);
#endif
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
// Create a renderer, render window, and interactor
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
// Add the actor to the scene
renderer->AddActor(actor);
renderer->SetBackground(0.1, 0.2, 0.3);
// Render and interact
renderWindow->Render();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
实例二
颜色合成
VTK也支持将多个灰度图像合并成一个彩色图像。vtklmageAppendComponents类实现
#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageAppendComponents.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkJPEGReader.h>
#include <vtkImageActor.h>
int main(int, char *[])
{
vtkSmartPointer<vtkImageCanvasSource2D> red =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
red->SetScalarTypeToUnsignedChar();
red->SetNumberOfScalarComponents(1); //组分数为1
red->SetExtent(0, 100, 0, 100, 0, 0);
red->SetDrawColor(0, 0, 0, 0);
red->FillBox(0,100,0,100);
red->SetDrawColor(255, 0, 0, 0);
red->FillBox(20,40,20,40);
red->Update();
vtkSmartPointer<vtkImageCanvasSource2D> green =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
green->SetScalarTypeToUnsignedChar();
green->SetNumberOfScalarComponents(1);
green->SetExtent(0, 100, 0, 100, 0, 0);
green->SetDrawColor(0, 0, 0, 0);
green->FillBox(0,100,0,100);
green->SetDrawColor(255, 0, 0, 0);
green->FillBox(30,50,30,50);
green->Update();
vtkSmartPointer<vtkImageCanvasSource2D> blue =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
blue->SetScalarTypeToUnsignedChar();
blue->SetNumberOfScalarComponents(1);
blue->SetExtent(0, 100, 0, 100, 0, 0);
blue->SetDrawColor(0, 0, 0, 0);
blue->FillBox(0,100,0,100);
blue->SetDrawColor(255, 0, 0, 0);
blue->FillBox(40,60,40,60);
blue->Update();
vtkSmartPointer<vtkImageAppendComponents> appendFilter =
vtkSmartPointer<vtkImageAppendComponents>::New();
//分别传入r,g,b data 共三个组分
appendFilter->SetInputConnection(0, red->GetOutputPort());
appendFilter->AddInputConnection(0, green->GetOutputPort());
appendFilter->AddInputConnection(0, blue->GetOutputPort());
appendFilter->Update();
vtkSmartPointer<vtkImageActor> redActor =
vtkSmartPointer<vtkImageActor>::New();
redActor->SetInputData(red->GetOutput());
vtkSmartPointer<vtkImageActor> greenActor = vtkSmartPointer<vtkImageActor>::New();
greenActor->SetInputData(green->GetOutput());
vtkSmartPointer<vtkImageActor> blueActor = vtkSmartPointer<vtkImageActor>::New();
blueActor->SetInputData(blue->GetOutput());
vtkSmartPointer<vtkImageActor> combinedActor = vtkSmartPointer<vtkImageActor>::New();
combinedActor->SetInputData(appendFilter->GetOutput());
// Define viewport ranges
// (xmin, ymin, xmax, ymax)
double redViewport[4] = {0.0, 0.0, 0.25, 1.0};
double greenViewport[4] = {0.25, 0.0, 0.5, 1.0};
double blueViewport[4] = {0.5, 0.0, 0.75, 1.0};
double combinedViewport[4] = {0.75, 0.0, 1.0, 1.0};
// Setup renderers
vtkSmartPointer<vtkRenderer> redRenderer =
vtkSmartPointer<vtkRenderer>::New();
redRenderer->SetViewport(redViewport);
redRenderer->AddActor(redActor);
redRenderer->ResetCamera();
redRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> greenRenderer =
vtkSmartPointer<vtkRenderer>::New();
greenRenderer->SetViewport(greenViewport);
greenRenderer->AddActor(greenActor);
greenRenderer->ResetCamera();
greenRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> blueRenderer =
vtkSmartPointer<vtkRenderer>::New();
blueRenderer->SetViewport(blueViewport);
blueRenderer->AddActor(blueActor);
blueRenderer->ResetCamera();
blueRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> combinedRenderer =
vtkSmartPointer<vtkRenderer>::New();
combinedRenderer->SetViewport(combinedViewport);
combinedRenderer->AddActor(combinedActor);
combinedRenderer->ResetCamera();
combinedRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(redRenderer);
renderWindow->AddRenderer(greenRenderer);
renderWindow->AddRenderer(blueRenderer);
renderWindow->AddRenderer(combinedRenderer);
renderWindow->SetSize(1200, 300);
renderWindow->Render();
renderWindow->SetWindowName("ImageAppendComponentsExample");
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}