vtkImageBlend
VTK中的图像融合是利用图像的不透明度来合成图像,使用类vtkImageBlend实现图像的融合;vtkImageBlend使用alpha或opacity将图像融合在一起;vtkImageBlend可以接收多个图像输入,其输出为融合图像。输出图像的间距、原点、范围和组件数与第一个输入的相同。如果输入有一个alpha分量,那么这个分量会原封不动地复制到输出中。此外,如果第一个输入有一个或两个分量,即如果是L(灰度)或LA(灰度+α),则所有其他输入也必须是L或LA。
vtkImageBlend类支持L(灰度图像)、LA(灰度+透明度)、RGB或RGBA(RGB+透明度)图像作为输入。
vtkImageBlend类支持两种图像融合模式:标准模式(Normal)和混合模式(Compound)。
枚举 | 值 |
---|---|
VTK_IMAGE_BLEND_MODE_NORMAL | 0 |
VTK_IMAGE_BLEND_MODE_COMPOUND | 1 |
标准模式是默认模式,这是OpenGL和其他图形包使用的标准混合模式。输出总是具有与第一个输入相同的组件数量和范围。第一个输入的alpha值不用于混合计算,而是直接复制到输出。
混合模式,将图像合成在一起,每个组件按alpha/opacity值之和进行缩放。使用SetCompoundThreshold方法设置并指定复合模式下的阈值。不透明度*alpha小于或等于此阈值的像素将被忽略。第一个输入的alpha值(如果存在)不会复制到输出的alpha值。输出总是具有与第一个输入相同的组件数量和范围。如果设置了CompoundAlpha,则还将使用alpha加权混合计算来计算输出的alpha值。
接口说明
数据源
void SetInputData(int num, vtkDataObject* input);
void SetInputData(vtkDataObject* input) { this->SetInputData(0, input); }
vtkDataObject* GetInput(int num);
vtkDataObject* GetInput() { return this->GetInput(0); }
以上接口已经废弃,使用 SetInputConnection()和vtkAlgorithm::GetInputConnection(0, num);
SetInputConnection根据ID号来设置输入图像;
当vtkImageBlend的inputData大于2个对象时,需要通过AddInputData来设置InputData;
透明度
void SetOpacity(int idx, double opacity);
double GetOpacity(int idx);
SetOpacity和GetOpacity根据ID号来设置对应id号的图像的不透明度的大小或者获取不透明度的大小;
融合模式:
vtkSetClampMacro(BlendMode, int, VTK_IMAGE_BLEND_MODE_NORMAL, VTK_IMAGE_BLEND_MODE_COMPOUND);
vtkGetMacro(BlendMode, int);
void SetBlendModeToNormal() { this->SetBlendMode(VTK_IMAGE_BLEND_MODE_NORMAL); }
void SetBlendModeToCompound() { this->SetBlendMode(VTK_IMAGE_BLEND_MODE_COMPOUND); }
const char* GetBlendModeAsString(void);
阈值:
vtkSetMacro(CompoundThreshold, double);
vtkGetMacro(CompoundThreshold, double);
示例
代码
#include "vtkSmartPointer.h"
#include "vtkJPEGReader.h"
#include "vtkImageBlend.h"
#include "vtkImageCanvasSource2D.h"
#include "vtkImageActor.h"
#include "vtkImageMapper3D.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkInteractorStyleImage.h"
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
using namespace std;
int main()
{
vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName("D:\\test.jpg");
reader->Update();
vtkSmartPointer<vtkImageCanvasSource2D> imageSource = vtkSmartPointer<vtkImageCanvasSource2D>::New();
imageSource->SetNumberOfScalarComponents(1);
imageSource->SetScalarTypeToUnsignedChar();
imageSource->SetExtent(0, 512, 0, 512, 0, 0);
imageSource->SetDrawColor(0.0);
imageSource->FillBox(0, 512, 0, 512);
imageSource->SetDrawColor(255.0);
imageSource->FillBox(100, 400, 100, 400);
imageSource->Update();
vtkSmartPointer<vtkImageBlend> imageBlend = vtkSmartPointer<vtkImageBlend>::New();
imageBlend->SetInputConnection(0, reader->GetOutputPort());
imageBlend->SetInputConnection(1, imageSource->GetOutputPort());
imageBlend->SetOpacity(0, 0.4);
imageBlend->SetOpacity(1, 0.6);
imageBlend->Update();
// Create actors
vtkSmartPointer<vtkImageActor> originalActor1 = vtkSmartPointer<vtkImageActor>::New();
originalActor1->GetMapper()->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer<vtkImageActor> originalActor2 = vtkSmartPointer<vtkImageActor>::New();
originalActor2->GetMapper()->SetInputConnection(imageSource->GetOutputPort());
vtkSmartPointer<vtkImageActor> blendActor = vtkSmartPointer<vtkImageActor>::New();
blendActor->GetMapper()->SetInputConnection(imageBlend->GetOutputPort());
// Define viewport ranges
// (xmin, ymin, xmax, ymax)
double leftViewport[4] = { 0.0, 0.0, 0.33, 1.0 };
double midViewport[4] = { 0.33, 0.0, 0.66, 1.0 };
double rightViewport[4] = { 0.66, 0.0, 1.0, 1.0 };
// Setup renderers
vtkSmartPointer<vtkRenderer> originalRenderer1 = vtkSmartPointer<vtkRenderer>::New();
originalRenderer1->SetViewport(leftViewport);
originalRenderer1->AddActor(originalActor1);
originalRenderer1->ResetCamera();
originalRenderer1->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> originalRenderer2 = vtkSmartPointer<vtkRenderer>::New();
originalRenderer2->SetViewport(midViewport);
originalRenderer2->AddActor(originalActor2);
originalRenderer2->ResetCamera();
originalRenderer2->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> blendRenderer = vtkSmartPointer<vtkRenderer>::New();
blendRenderer->SetViewport(rightViewport);
blendRenderer->AddActor(blendActor);
blendRenderer->ResetCamera();
blendRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(originalRenderer1);
renderWindow->AddRenderer(originalRenderer2);
renderWindow->AddRenderer(blendRenderer);
renderWindow->SetSize(640, 320);
renderWindow->Render();
renderWindow->SetWindowName("ImageBlendExample");
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return 0;
}
下图为不同渲染器所在的窗口范围;一个窗口分为了三部分,前两窗口为输入图像,最后一个窗口为输出的融合图像;
运行的效果图为:
遇到的问题
代码中使用书上的内容:
imageBlend->SetInputConnection(0, reader->GetOutputPort());
imageBlend->SetInputConnection(1, imageSource->GetOutputPort());
编译无错,运行时,没有展示出融合图像,VTK有错误消息窗口弹出;
VTK错误:
ERROR: In I:\v-vtk\VTK-8.2.0\Common\ExecutionModel\vtkDemandDrivenPipeline.cxx, line 809
vtkCompositeDataPipeline (005D17D0): Input for connection index 0 on input port index 1 for algorithm vtkImageBlend(00646E30) is of type vtkImageData, but a vtkImageStencilData is required.
从网上找到资料:
// 图像融合
vtkSmartPointer<vtkImageBlend> pImageBlend = vtkSmartPointer<vtkImageBlend>::New();
// 原始写法:不报错,融合不正确
// pImageBlend->SetInputData(pJpegReader->GetOutput());
// pImageBlend->SetInputData(pImageCanvas->GetOutput());
// 不报错,融合不正确
// pImageBlend->SetInputData( pJpegReader->GetOutput());
// pImageBlend->SetInputData( pImageStencil->GetOutput());
// 不报错,融合不正确
// pImageBlend->SetInputData(0, pJpegReader->GetOutput());
// pImageBlend->SetInputData(1, pImageStencil->GetStencil());
// 正确
pImageBlend->AddInputData( pJpegReader->GetOutput());
修改后使用
imageBlend->AddInputData(reader->GetOutput());
imageBlend->AddInputData(imageSource->GetOutput());
编译报错:
error C2664: “void vtkImageAlgorithm::AddInputData(int,vtkDataObject *)”: 无法将参数 1 从“vtkImageData *”转换为“vtkDataObject *”
强转为(vtkDataObject*)类型后:
imageBlend->AddInputData((vtkDataObject*)reader->GetOutput());
imageBlend->AddInputData((vtkDataObject*)imageSource->GetOutput());
编译正常,融合图像正常:
新的问题
群里有朋友提问,使用一个vtkDICOMImageReader
解析的dicom文件和上文的白色方块进行融合,效果还是dicom的灰度图像;
原因是什么呢,我开始认为vtkImageBlend
的输入都是vtkImageData
应该不会区分什么文件,后来在看了vtkImageBlend
的说明后,知道“vtkImageBlend类支持L(灰度图像)、LA(灰度+透明度)、RGB或RGBA(RGB+透明度)图像作为输入”,dicom文件的图像为既不是灰度图像也不是RGB彩色图像,自然就不是vtkImageBlend
的输入;
要想实现两者的融合图像,就需要把dicom转换为灰度图像在使用vtkImageBlend
;亦或者是根据融合的原理更改dicom图像里的CT值;