ITK配准每步迭代输出一个矩阵迭代法

ITK Viola-Wells 互信息测度配准示例
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
/*************************************************************************
*多模态图像配准示例 ----- 使用 Viola-Wells Mutual Information
*多模态图像配准是医学图像配准中最具挑战性的工作之一, 无法应用基于直接比较
*灰度级别的测度. 基于互信息的测度是解决多模态图像配准的有效手段
*与其前面使用刚体变换的单模态图像配准示例的不同之处为:
*1.使用 itk::MutualInformationImageToImageMetric 互信息测度
*2.本例使用了 itk::GradientDescentOptimizer 梯度下降法进行优化.
*由于互信息测度计算的随机性,使用 itk::RegularStepGradientDescentOptimizer
*优化法无法成功. 因此这里使用更简单的 GradientDescentOptimizer
*该优化带有一用户自定义的 learning rate
*************************************************************************/
#include "itkImageRegistrationMethod.h"
#include "itkTranslationTransform.h"
#include "itkMutualInformationImageToImageMetric.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkGradientDescentOptimizer.h"
#include "itkImage.h"
//一种使互信息的计算简单化的方法是归一化两幅输入图像的统计分布
//过滤器 itk::NormalizeImageFilter 正适合此任务
#include "itkNormalizeImageFilter.h"
//低通滤波的图像也会增加对噪音的健壮性.
//本例使用的是 itk::DiscreteGaussianImageFilter
#include "itkDiscreteGaussianImageFilter.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkResampleImageFilter.h"
#include "itkCastImageFilter.h"
#include "itkCheckerBoardImageFilter.h"
#include "itkCommand.h"
//Command/Observer 设计模式,本例中简单地用于监视配准过程的执行
class CommandIterationUpdate : public itk::Command
CommandIterationUpdate
itk::Command
itk::SmartPointer&Self&
itkNewMacro( Self );
protected:
CommandIterationUpdate() {};
itk::GradientDescentOptimizer
OptimizerT
const OptimizerType
OptimizerP
void Execute(itk::Object *caller, const itk::EventObject & event)
Execute( (const itk::Object *)caller, event);
//每次迭代都执行该方法,输出我们想要观察的参数
void Execute(const itk::Object * object, const itk::EventObject & event)
OptimizerPointer optimizer = dynamic_cast& OptimizerPointer &( object );
if( ! itk::IterationEvent().CheckEvent( &event ) )
std::cout && optimizer-&GetCurrentIteration() && "
std::cout && optimizer-&GetValue() && "
std::cout && optimizer-&GetCurrentPosition() && std::
int main( int argc, char *argv[] )
if( argc & 4 )
std::cerr && "Missing Parameters " && std::
std::cerr && "Usage: " && argv[0];
std::cerr && " fixedImageFile
movingImageFile ";
std::cerr && "outputImagefile ";
std::cerr && "[checkerBoardBefore] [checkerBoardAfter]" && std::
return EXIT_FAILURE;
//1.定义待配准图像类型: 维数, 像素类型
unsigned int
Dimension = 2;
unsigned short
typedef itk::Image& PixelType, Dimension &
FixedImageT
typedef itk::Image& PixelType, Dimension &
MovingImageT
//内部图像类型, 像素类型为 float, 有利于互信息测度的计算
InternalPixelT
itk::Image& InternalPixelType, Dimension & InternalImageT
//2.定义配准框架的基本组件: 变换, 优化, 测度, 插值, 配准组件
typedef itk::TranslationTransform& double, Dimension & TransformT
typedef itk::GradientDescentOptimizer
OptimizerT
typedef itk::LinearInterpolateImageFunction& InternalImageType,
double & InterpolatorT
typedef itk::ImageRegistrationMethod& InternalImageType,
InternalImageType &
RegistrationT
//互信息测度
typedef itk::MutualInformationImageToImageMetric&
InternalImageType, InternalImageType & MetricT
TransformType::Pointer
= TransformType::New();
OptimizerType::Pointer
= OptimizerType::New();
InterpolatorType::Pointer
interpolator
= InterpolatorType::New();
RegistrationType::Pointer
registration
= RegistrationType::New();
//3.使用配准组件将:变换, 优化, 测度, 插值 四个基本组件连接至一起
registration-&SetOptimizer(
registration-&SetTransform(
registration-&SetInterpolator(
interpolator
MetricType::Pointer metric = MetricType::New();
registration-&SetMetric( metric );
//设置测度组件的部分参数
//测度要求选择若干参数, 包括用于固定及浮动图密度估计的高斯核的
//标准差(the standard deviation of the Gaussian kernel)
//以及用于计算密度及熵值(entropy)的样本数目
//经验表明对于归一化为零均值单位方差的图像, 0.4 工作的很好.
//此处使用该经验值------比较复杂的测度,其参数的选取非常关键***
metric-&SetFixedImageStandardDeviation(
metric-&SetMovingImageStandardDeviation( 0.4 );
//4.设置待配准图像及变换区域,并进行适当处理
typedef itk::ImageFileReader& FixedImageType
& FixedImageReaderT
typedef itk::ImageFileReader& MovingImageType & MovingImageReaderT
FixedImageReaderType::Pointer
fixedImageReader
= FixedImageReaderType::New();
MovingImageReaderType::Pointer movingImageReader
= MovingImageReaderType::New();
fixedImageReader-&SetFileName(
argv[1] );
movingImageReader-&SetFileName( argv[2] );
//实例化 normalization filters
//输入为 "固定图像" 或 "浮动图像", 输出为 "内部图像"
typedef itk::NormalizeImageFilter& FixedImageType,
InternalImageType & FixedNormalizeFilterT
typedef itk::NormalizeImageFilter& MovingImageType,
InternalImageType & MovingNormalizeFilterT
FixedNormalizeFilterType::Pointer fixedNormalizer
= FixedNormalizeFilterType::New();
MovingNormalizeFilterType::Pointer movingNormalizer
= MovingNormalizeFilterType::New();
//模糊过滤器(blurring filters)
typedef itk::DiscreteGaussianImageFilter&InternalImageType,
InternalImageType& GaussianFilterT
GaussianFilterType::Pointer fixedSmoother
= GaussianFilterType::New();
GaussianFilterType::Pointer movingSmoother = GaussianFilterType::New();
//本例, 两个 blurring filters 的方差都设置为 2.0
fixedSmoother-&SetVariance( 2.0 );
movingSmoother-&SetVariance( 2.0 );
//连接各个过滤器
//readers 的输出作为 normalization filters 的输入
//并将其输出连接至 blurring filters 的输入端
fixedNormalizer-&SetInput(
fixedImageReader-&GetOutput() );
movingNormalizer-&SetInput( movingImageReader-&GetOutput() );
fixedSmoother-&SetInput( fixedNormalizer-&GetOutput() );
movingSmoother-&SetInput( movingNormalizer-&GetOutput() );
//连接 blurring filters 的输出至 registration method 的输入端
registration-&SetFixedImage(
fixedSmoother-&GetOutput()
registration-&SetMovingImage( movingSmoother-&GetOutput() );
//设置需要考虑的图像区域
fixedNormalizer-&Update();
FixedImageType::RegionType fixedImageRegion
= fixedNormalizer-&GetOutput()-&GetBufferedRegion();
registration-&SetFixedImageRegion( fixedImageRegion );
//5.设置各组件的参数,变换、测度、优化,不同的方法需要不同的设置
//设置变换组件的参数
typedef RegistrationType::ParametersType ParametersT
ParametersType initialParameters( transform-&GetNumberOfParameters() );
initialParameters[0] = 0.0;
// Initial offset in mm along X
initialParameters[1] = 0.0;
// Initial offset in mm along Y
registration-&SetInitialTransformParameters( initialParameters );
//设置测度组件参数,另一部分
//现在定义测度计算考虑的空间样本数目.
//该值通常很小,可取固定图像像素总数目的 1%
//增加样本数目可以改进测度值在两次迭代间的平滑度,在与依赖连续测度值的
//优化器协作工作时非常有利. 但在每次评估测度值时的计算量会增加.
//经验表明,样本数量对配准过程并不是最关键的,通常可以使用比较大的
//数目如 20%, 50% 进行测试,当成功配准后再减少样本数量,直至找到合适的值.
//通常需要研究迭代过程测度值的变化,以求得最佳值
const unsigned int numberOfPixels
= fixedImageRegion.GetNumberOfPixels();
const unsigned int numberOfSamples
= static_cast& unsigned int &( numberOfPixels * 0.01 );
metric-&SetNumberOfSpatialSamples( numberOfSamples );
//设置优化组件的参数
//较大的互信息值表示两幅图像匹配的更好, 因此需要最大化代价函数.
//默认情况下, GradientDescentOptimizer class 用来最小化代价函数值.
//因此有必要通过调用 MaximizeOn() 方法修改其默认行为.
//另外, 需要定义 optimizer 的步长
//注意,learning rate的值大将导致optimizer不稳定.
//值小,可能需要迭代过多的次数.通常需要进行实验才能选取合适的值
//理想的值为,迭代次数小,并且优化参数空间保持稳定. 需要注意的是,
//该参数对梯度测度的效果是成倍增加的,
//因此对优化器步长的影响是与测度值成正比的.
//测度值大, 则需要使用较小的 learning rate 以保持相似的优化行为
optimizer-&SetLearningRate( 15.0 );
optimizer-&SetNumberOfIterations( 200 );
optimizer-&MaximizeOn();
//6.实例化一 Command/Observer 对象,监视配准过程的执行,并触发配准过程的执行.
//监视配准过程的执行
CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New();
optimizer-&AddObserver( itk::IterationEvent(), observer );
registration-&StartRegistration();
catch( itk::ExceptionObject & err )
std::cout && "ExceptionObject caught !" && std::
std::cout && err && std::
return EXIT_FAILURE;
//取得配准执行完毕的最终参数
ParametersType finalParameters
= registration-&GetLastTransformParameters();
double TranslationAlongX = finalParameters[0];
double TranslationAlongY = finalParameters[1];
unsigned int numberOfIterations = optimizer-&GetCurrentIteration();
double bestValue = optimizer-&GetValue();
std::cout && std::
std::cout && " Result = " && std::
std::cout && " Translation X = " && TranslationAlongX
std::cout && " Translation Y = " && TranslationAlongY
std::cout && " Iterations
= " && numberOfIterations && std::
std::cout && " Metric value
= " && bestValue
std::cout && " Numb. Samples = " && numberOfSamples
//7.重采样, 将变换后的浮动图像映射到固定图像空间中,保存配准结果
//一般情况,最后一步为: 利用最终的变换结果将浮动图像映射到固定图像空间
//可通过 itk::ResampleImageFilter 完成
typedef itk::ResampleImageFilter& MovingImageType,
FixedImageType & ResampleFilterT
TransformType::Pointer finalTransform = TransformType::New();
finalTransform-&SetParameters( finalParameters );
finalTransform-&SetFixedParameters( transform-&GetFixedParameters() );
ResampleFilterType::Pointer resample = ResampleFilterType::New();
resample-&SetTransform( finalTransform );
resample-&SetInput( movingImageReader-&GetOutput() );
FixedImageType::Pointer fixedImage = fixedImageReader-&GetOutput();
resample-&SetSize(
fixedImage-&GetLargestPossibleRegion().GetSize() );
resample-&SetOutputOrigin(
fixedImage-&GetOrigin() );
resample-&SetOutputSpacing( fixedImage-&GetSpacing() );
resample-&SetOutputDirection( fixedImage-&GetDirection() );
resample-&SetDefaultPixelValue( 100 );
unsigned char
OutputPixelT
typedef itk::Image& OutputPixelType, Dimension & OutputImageT
typedef itk::CastImageFilter& FixedImageType,
OutputImageType & CastFilterT
typedef itk::ImageFileWriter& OutputImageType &
WriterType::Pointer
WriterType::New();
CastFilterType::Pointer
CastFilterType::New();
writer-&SetFileName( argv[3] );
caster-&SetInput( resample-&GetOutput() );
writer-&SetInput( caster-&GetOutput()
writer-&Update();
typedef itk::CheckerBoardImageFilter&
FixedImageType & CheckerBoardFilterT
CheckerBoardFilterType::Pointer checker = CheckerBoardFilterType::New();
checker-&SetInput1( fixedImage );
checker-&SetInput2( resample-&GetOutput() );
caster-&SetInput( checker-&GetOutput() );
writer-&SetInput( caster-&GetOutput()
//保存配准前浮动图像与固定图像的差异
TransformType::Pointer identityTransform = TransformType::New();
identityTransform-&SetIdentity();
resample-&SetTransform( identityTransform );
if( argc & 4 )
writer-&SetFileName( argv[4] );
writer-&Update();
//配准后的浮动图像与固定图像的差异
resample-&SetTransform( finalTransform );
if( argc & 5 )
writer-&SetFileName( argv[5] );
writer-&Update();
return EXIT_SUCCESS;
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!ITK图像配准01-学习
图像配准的定义:
图像配准是将一幅图像上的点映射到另一幅图像上同源点的空间转换过程。eg:平移、旋转、缩放等变换。下图为镜像变换。
配准框架:
配准框架的基本成员:两个输入图像、一个变换、一个路径选择、一个校对机和一个优化器。
过程:待配准图像通过 变换函数(T(x)) 到参考图像的映射过程。
T(X)表示从参考图像上的点到待配准图像上的点的空间映射关系。校对机被用来评估待配准图像在非网格位置的程度。成员路径选择
S(f, m×T)提供了一种参考图像被待配准图像配准的程度。这种尺度形成了数量上的标准,这种标准可以被优化器通过寻找被传递的参数定义的空间去达到最优化。
一个配准方法要求下面几条:两个输入的图像、转换函数、度量、校对机和优化器。
在配准方法中的成分的每种类型都应该首先被实例化。
1.两个输入的图像const unsigned int Dimension = 2;//图像数据类型参数,维度
typedef float PixelT//图像数据类型参数,像素类型输入数据的类型通过下面几行表达:typedef itk::Image& PixelType, Dimension & FixedImageT
typedef itk::Image& PixelType, Dimension & MovingImageT
2.图像转换
把参考图像空间映射到待配准图像空间的转换如下:typedef itk::TranslationTransform& double, Dimension & TransformT//转换类型
衡量标准为比较两幅图像的搭配质量。衡量标准通常已经参数化,例如说下面对图像类型的声明:typedef itk::MeanSquaresImageToImageMetric&FixedImageType,MovingImageType & MetricT
4.选择校对机的类型
typedef itk:: LinearInterpolateImageFunction&MovingImageType,double & InterpolatorT//选择插值类型
5.配准方法
配准方法的类型是通过参考和待配准图像的类型来表示的。
typedef itk::ImageRegistrationMethod&FixedImageType,MovingImageType & RegistrationT
配准的要素以上所有要素:转换,度量,校对机,优化方法。以下为要素:
每一个配准要素都是通过它的New( )创建的,并且通过各自的itk::SmartPointer赋值。MetricType::Pointer metric = MetricType::New( );
TransformType::Pointer transform = TransformType::New( );优化器Type::Pointer
优化器 = 优化器Type::New( );
InterpolatorType::Pointer interpolator = InterpolatorType::New( );
RegistrationType::Pointer registration = RegistrationType::New( );每一个要素被连接到配准方法的程序中:registration-&SetMetric( metric );
registration-&SetOptimitor( optimitor);
registration-&SetTransform( transform );
registration-&SetInterpolator( interpolator );
配准需要的输入图像读取示例:
参考和待配准图像从文件里读取。 这就需要itk::ImageRegistrationMethod从readers的输出中获得它的输入数据。registration-&SetFixedImage( fixedImageReader-&GetOutput( ) );
registration-&SetMovingImage( movingImageReader-&GetOutput( ) );
需要配准的图像可能只是图像中的部分区域,以下为设置指定区域操作:
这样的区域通过参考图像的BufferedRegion定义。注意,在这个区域首先要调用它的Update(
)方法:fixedImageReader-&Update( );
registration-&SetFixedImageRegion(fixedImageReader-&GetOutput( )-&GetBufferedRegion( ) );
变换参数设置示例(平移):用于变换的参数队列由沿着每一维的方向的平移值构成。设置参数值到零以便将变换初始成恒等变换。
typedef RegistrationType::ParametersType//变换类型
ParametersTParametersTypeinitialParameters(transform-&GetNumberOfParameters( ) );
initialParameters[0] = 0.0; // Initial offset in mm along X
initialParameters[1] = 0.0; // Initial offset in mm along Y
registration-&SetInitialTransformParameters( initialParameters );//变换参数设置 准备执行配准方法。优化器用来驱动配准的执行。然而, ImageRegistrationMethod类协调整体以确保在传递给优化器之前一切都到位了。优化器用于收敛得到配准的参数,需要设置。
以下是配准样例的框架结构:
以下是转自的配准总结:
配准就是在基于上述四个部分,针对不同的图形情况,如单模,多模,去用不同的准则,变换,优化器和插值方法。在这个配准框架下,不同的部分在设置参数时有变换,这个也是难点。不同的优化器,往往参数设置不同,这个就需要了解优化器和配准方法的算法。如放射变换有旋转变换的矩阵,平移量。muti-solutionregistration(多分辨率配准)要改变步长等等。
配准步骤,设置待配准图像和参考图像的像素类型,维数。实例化上述四个部分,并将其组合到registration中,设置各部分的参数,并触发配准开始。通过最终得到的参数重采样带配准图像,得到配准结果。比较配准前后和配准过程后的差异。实例化往往先定义——tyoedef,再指向智能指针,然后通过指针调用set*()函数设置参数。
在配准过程中,有时会根据具体情况先用*filter对图像进行处理。如在配准中对精度要求高的话,先用cast*filter将图像转化为float型,配准后再转化为char,或int型存储。如果图像噪声比较明显,也可以先用*filter除噪在配准。有的也用filter保留边缘,在配准。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!ITK 配准框架中的 Subject/Observer 模式及优化过程模拟演示-1
最近正式开始研究 ITK 的配准框架及其过程,先把自己理解到的一些东西写出来记下。
ITK 中的医学图像配准框架主要由以下几部分组成:几何变换组件、图像插值组件、相似性测度组件、优化组件、以及连接各个组件的配准方法组件。对于多分辨策略还会多出两个图像金字塔组件。
图像配准的本质是一个迭代优化的过程,关于图像配准框架示例可参考:,或者参考 ITK 文档。简单的说,用户指定一个几何变换策略、一个插值函数、一个相似性测度策略、一个优化策略。优化函数每迭代一次,便将计算出来的几何变换参数传递给相似性测度组件,相似性测度组件通过计算得出一个测度值。如果该测度值达到预期的目标,则指示优化函数目标达到,停止迭代。如果迭代次数超出用户设置的最大限制,同样停止迭代。对于
中的测度组件,测度值越小,则表明经过几何变换的浮动图像与固定图像的配准程度越高,当测度值达到预设的目标时,停止迭代,图像配准过程结束。
在 ITK 的图像配准框架中,通过顶层类 Object 实现了 Subject/Observer 设计模式,或者称 Command/Observer 模式,就是简单地将 Observer 模式与 Command 模式相结合。用户可以注册优化过程的迭代事件 IterationEvent(),并通过继承 Command 类,并实现其 Execute() 成员函数完成监视配准过程。其中,Command 类相当于面向过程设计中的回调函数(callback),所以用户应该只完成一些简单的任务,如输出优化程序迭代过程的一些参数值以进行观察, 正是这样做的。当然,在使用多分辨策略的配准框架中,用户可以使用 Command 类完成在不同层间使用不同优化策略的任务。ITK 中使用 Subject/Observer 监视的简单的图像配准执行过程如下图所示:
本文为了演示 Subject/Observer 模式,以及如何通过注册该模式监视优化函数的迭代过程。为了便于演示,这里只涉及了优化组件、相似性测度组件以及用于连接各个组件的配准方法组件。去掉了几何变换组件、插值函数组件,并且没有使用 ITK 中大量运用的智能指针(SmartPointer)、对象工厂(ObjectFactory)、以及管道技术。对于智能指针以及对象工厂,会找时间另外进行研究演示。
如下为代码示例,我会在每个代码文件中给出适当的说明,这里大部分的代码都直接来自 ITK 源码,尤其是程序结构几乎是超级简化的 ITK 配准框架:
1.Command 类,Command 模式参考:。在实际使用中,没有必要完全实现模式中的每一个角色,ITK 中的 Command/Observer 模式将 Command 与 Observer 相结合,通过代码可以看到其实现是比较简单的。
//MyCommand.h
#pragma once
#include "MyTypeMacro.h"
#include "MyObject.h"
//用于实现 Subject/Observer 模式, 或者称 Command/Observer 模式
//核心代码在 Execute() 中实现, 其功能相当于面向过程设计中的回调函数(callback)
//所以它只应该完成一些简单的任务, 客户实现具体的 Command 子类, 用于监视优化过程的执行
//Command: 抽象基类
MyCommand : public MyObject
typedef MyCommand
typedef MyObject
typedef MyCommand*
typedef const MyCommand*
//运行时类型识别
MyTypeMacro(MyCommand, MyObject);
//当观察者注册的事件发生时,最终调用的便是该函数
//客户继承 MyCommand 类, 实现 Execute 函数, 输出想要观察的数据
//注意: MyCommand 相当于一个回调函数的面向对象的实现
//所以 Execute 应该只进行一些简单的功能, 还要用于复杂的任务
virtual void Execute(MyObject *caller, const MyEventObject & event ) = 0;
virtual void Execute(const MyObject *caller, const MyEventObject & event ) = 0;
protected:
MyCommand(){
~MyCommand(){
MyCommand(const Self&);
//将拷贝构造函数和赋值函数设为私有
void operator=(const Self&);
2. EventObject 类,表示某个事件的类,ITK 中的主要事件有:StartEvent()、EndEvent()、IterationEvent() 分别表示优化开始、结束、一次迭代事件。还有 ProgressEvent()、ModifiedEvent()、ExitEvent() 等等其它一些重要的事件。实际上我们完全可以使用一些简单的变量来表示某个事件的发生与否,但这们会使程序的结构非常混乱,不易维护,注意这里使用的了宏生成相应的事件类,非常方便且不易出错。
//MyEventObject.h
#pragma once
#include "MyIndent.h"
//事件类, 用来表示某事件发生
//事实上, 在面向过程设计中我们可以用一些简单的 bool 变量来表示某些事件的发生与否
//在 ITK 的配准框架中, 客户通过注册 IterationEvent 迭代事件观察配准过程的执行
//图像配准本质上是一个优化过程, 优化函数每进行一次优化便触发一次 IterationEvent 事件
//客户通过实现一个具体的 Command 类, 观察配准过程的重要数据
//EventObject: 抽象基类
class MyEventObject
MyEventObject() {}
MyEventObject(const MyEventObject&){};
virtual ~MyEventObject() {}
//创建事件对象
virtual MyEventObject* MakeObject() const=0;
//打印事件信息
virtual void Print(std::ostream& os) const;
//返回该事件名称
virtual const char* GetEventName(void) const=0;
//检查参数传入的 Event 是否与该事件匹配, 相同或是其子类
virtual bool CheckEvent(const MyEventObject*) const=0;
protected:
//打印该事件的不同信息
virtual void PrintSelf(std::ostream& os, MyIndent indent) const;
virtual void PrintHeader(std::ostream& os, MyIndent indent) const;
virtual void PrintTrailer(std::ostream& os, MyIndent indent) const;
MyEventObject* EventFactoryFunction();
void operator=(const MyEventObject&);
//Generic inserter operator for EventObject and its subclasses.
inline std::ostream& operator&&(std::ostream& os, MyEventObject &e)
(&e)-&Print(os);
//宏定义: 用来创建新的事件, 避免写过多相同的代码
//这种方法非常有用, 在自己的程序中可以经常使用
#define MyEventMacro( classname , super ) /
classname : public super{ /
typedef classname
typedef super
classname() {} /
virtual ~classname() {} /
virtual const char * GetEventName() const { return # } /
virtual bool CheckEvent(const MyEventObject* e) const /
return dynamic_cast&const Self*&(e);
virtual MyEventObject* MakeObject() const /
return new S
classname(const Self&s) :super(s){}; /
private: /
void operator=(const Self&); /
//利用上面的宏定义一些常用的事件:
MyEventMacro( MyNoEvent
, MyEventObject )
MyEventMacro( MyAnyEvent
, MyEventObject )
MyEventMacro( MyStartEvent
, MyAnyEvent )
MyEventMacro( MyEndEvent
, MyAnyEvent )
MyEventMacro( MyModifiedEvent
, MyAnyEvent )
MyEventMacro( MyIterationEvent
, MyAnyEvent )
MyEventMacro( MyUserEvent
, MyAnyEvent )
3.Indent 类,用来控制打印时的缩排格式,这个类非常简单,但非常有用,虽然这对我要演示的内容没有任务联系,不过我还是将它列了出来,并进行了一些测试。因为我觉得这个类非常有用,尤其是它的方法,实际应用中会非常方便。
//MyIndent.h
#pragma once
#include &iostream&
//MyIndent 类,用来控制缩排(indentation)输出
//一个类的继承层次可能很深, 一个事件可能有多个观察者...
//输出类自身信息时, 使用 MyIndent 来进行控制, 以使输出信息层次结构明了易懂
class MyIndent
typedef MyIndent
//创建一个 MyIndent 实例
static Self* New();
//销毁该 MyIndent 实例
void Delete() {
delete this; }
//有参构造函数
MyIndent(int ind = 0) { m_Indent = }
//返回类名
static const char *GetNameOfClass() {return "MyIndent";}
//设置下一级缩进量
MyIndent GetNextIndent();
//以锯齿状输出缩排格式
friend std::ostream& operator&&(std::ostream& os, const MyIndent& o);
//控制缩排量
4.Object 类,ITK 中 Object 继承自 LightObject,在 LightObject 中使用对象工厂与智能指针实现了引用计数,使得我们在程序中同一个类的对象实例只在内存中存在一份,并且不须考虑指针的释放。ITK 中,绝大部分类都要继承自 Object,还有一些继承自 LightObject。这里,我并没有使用智能指针与对象工厂。演示程序中的绝大部分类都要从 Object 继承。
重要的是:Object 类实现了 这里要演示的 Command/Observer 模式:
//MyObject.h
#pragma once
#include "MyEventObject.h"
// Subject/Command 设计模式
//Object: 基类, 模仿 ITK 中的 Object 类
//这里省略了 Object 的基类 LightObject 类, 以及对象工厂与智能指针,这些另外进行演示
class MyObject
typedef MyObject
typedef MyObject*
typedef const MyObject*
static Pointer New();
//Delete()
virtual void Delete();
// Subject/Observer 设计模式
// AddObserver,InvokeEvent 等函数,都是调用了 subject 的同名函数
unsigned long AddObserver(const MyEventObject & event, MyCommand *);
unsigned long AddObserver(const MyEventObject & event, MyCommand *) const;
MyCommand* GetCommand(unsigned long tag);
//对所有观察该事件的 Command 调用其 Execute
void InvokeEvent( const MyEventObject & );
void InvokeEvent( const MyEventObject & ) const;
//移除观察者
void RemoveObserver(unsigned long tag);
void RemoveAllObservers();
//是否有观察者在观察该事件
bool HasObserver( const MyEventObject & event ) const;
virtual const char *GetNameOfClass() const
return "Object";
//Print(), 客户调用, 打印相关信息
//由 PrintHeader, PrintSelf, PrintTrailer 三部分组成
void Print(std::ostream& os, MyIndent indent = 0) const;
protected:
MyObject();
virtual ~MyObject();
//打印自身的相关信息
virtual void PrintSelf(std::ostream& os, MyIndent indent) const;
virtual void PrintHeader(std::ostream& os, MyIndent indent) const;
virtual void PrintTrailer(std::ostream& os, MyIndent indent) const;
//打印所有观察者的信息
bool PrintObservers(std::ostream& os, MyIndent indent) const;
MyObject(const Self&);
//将拷贝构造函数和赋值函数设为私有且不实现
void operator=(const Self&);
Subject* m_S
//Subject/Observer 模式; Object 维护一个指向 Subject 的指针
5.下面来看 Subject 与 Observer 的实现:
其中,Observer 由 一个指向 Command 的指针、及一个指向 Event 的指针组成。Subject 维护一个观察者 Observer 的列表。程序中:
1.用户首先通过 MyCommandIterationUpdate::Pointer observer = MyCommandIterationUpdate::New(); 创建一个观察者;MyCommandIterationUpdate 继承自 MyCommand 并实现自己的 Execute() 方法。
2.然后使用 optimizer-&AddObserver(MyIterationEvent(), observer); 添加观者者到 Subject 列表中,并且注册 MyIterationEvent() 迭代事件为该观察者感兴趣的事件。optimizer 继承自 MyObject,MyObject 维护一个指向 Subject 的指针,AddObserver() 则调用了 Subject 中的同名函数。
3.优化函数每迭代一次,便触发一次 MyIterationEvent() 事件,于是会调用 InvokeEvent(MyIterationEvent()),InvokeEvent() 方法继承自 MyObject,它又会调用 Subject 中的同名函数。于是,Subject 实例通过遍历它所维护的 Observer 列表,找到对 MyIterationEvent() 事件感兴趣的观察者(程序中为 MyCommandIterationUpdate 实例),并调用它的 Execute() 函数完成用户指定的任务。程序中输出了当前迭代次数,当前参数值以及当前测度值。具体可以看代码实现。
//SubjectObserver.h
#pragma once
#include &list&
// Subject/Observer 观察者模式
// Observer:
//1. 一个指向 Command 的指针;客户定义具体的 Command 类, 实现相应的 Execute() 操作.
//2. 一个指向 Event 的指针;
客户想要观察的事件.
//3.Observer 标识;
用于标识该 Observer 的身份, 这样可以快速查询.
class Observer
Observer(MyCommand* c,const MyEventObject * event, unsigned long tag)
: m_Command(c),m_Event(event), m_Tag(tag)
virtual ~Observer(){ delete m_E }
MyCommand::Pointer
//1.Observer 维护指向 Command 和 Event 的指针
const MyEventObject *
unsigned long
//Observer 的标识,也就是 Subject 中的 m_Count, 即第几个添加进来的 Observer
//Subject: 维护一个 Observer 指针的列表
class Subject
Subject() {
m_Count = 0;
~Subject();
//event 和 command 组合成一个 Observer 的实例
unsigned long AddObserver(const MyEventObject & event, MyCommand* cmd);
unsigned long AddObserver(const MyEventObject & event, MyCommand* cmd) const;
void RemoveObserver(unsigned long tag);
void RemoveAllObservers();
//InvokeEvent: 触发事件的执行, 检查观察者链, 通知观察该事件的观察者
void InvokeEvent( const MyEventObject & event, MyObject* self);
void InvokeEvent( const MyEventObject & event, const MyObject* self);
MyCommand *GetCommand(unsigned long tag);
bool HasObserver(const MyEventObject & event) const;
bool PrintObservers(std::ostream& os, MyIndent indent) const;
std::list&Observer* & m_O
//维护一个 Observer 指针的列表
unsigned long m_C
//////////////////////////////////////////////////
//Subject 类成员函数实现:
Subject::~Subject()
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
delete (*i);
m_Observers.clear();
//添加一个 Observer 至 观察者链 list
unsigned long Subject::AddObserver(const MyEventObject & event,MyCommand* cmd)
//由传入的 event 和 command 创建一个新的 Observer
//cmd 是具体的 Command 实例, 用于执行用户自定义的命令
//event 是客户所要观察的事件, 如迭代事件: IterationEvent()
Observer* ptr = new Observer(cmd, event.MakeObject(), m_Count);
m_Observers.push_back(ptr);
m_Count++;
// + 1, 下一个 Observer 的标识
return ptr-&m_T
unsigned long Subject::AddObserver(const MyEventObject & event,MyCommand* cmd) const
Observer* ptr = new Observer(cmd, event.MakeObject(), m_Count);
//将 Subject 的常量属性去掉!!!
Subject * me = const_cast&Subject *&( this );
me-&m_Observers.push_back(ptr);
me-&m_Count++;
return ptr-&m_T
//移除指定的观察者
void Subject::RemoveObserver(unsigned long tag)
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
if((*i)-&m_Tag == tag)
//通过比较 Observer 的标识进行判断,速度快
delete (*i);
m_Observers.erase(i);
//移除所有的观察者
void Subject::RemoveAllObservers()
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
delete (*i);
m_Observers.clear();
//触发指定事件, 检查观察者链, 如果某个观察者已经注册观察该事件,则通知该观察者
//该函数会调用具体的 Command 实例的 Execute() 方法, 客户实现该方法,以输出想要的数据
void Subject::InvokeEvent( const MyEventObject & event, MyObject* self)
//Subject:
std::list&Observer* & m_O
//MyCommand:
Execute(const MyObject *caller, const MyEventObject & event );
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
const MyEventObject * e =
//检查该观察者注册的事件是否是参数传入的 event, 或者继承自 e
if(e-&CheckEvent(&event))
//m_Command 是 Observer 数据成员, 维护一个 Observer 列表
(*i)-&m_Command-&Execute(self, event);
void Subject::InvokeEvent( const MyEventObject & event,const MyObject* self)
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
const MyEventObject * e =
if(e-&CheckEvent(&event))
(*i)-&m_Command-&Execute(self, event);
//返回 subject 列表中标识 m_Tag == tag 的 Observer
MyCommand* Subject::GetCommand(unsigned long tag)
for(std::list&Observer* &::iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
if ( (*i)-&m_Tag == tag)
return (*i)-&m_C
//返回是否有正在观察 event 事件的 Observer
bool Subject::HasObserver(const MyEventObject & event) const
for(std::list&Observer* &::const_iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
const MyEventObject * e =
if(e-&CheckEvent(&event))
return true;
return false;
//打印观察者链中的所有观察者信息
bool Subject::PrintObservers(std::ostream& os, MyIndent indent) const
if(m_Observers.empty())
return false;
for(std::list&Observer* &::const_iterator i = m_Observers.begin();
i != m_Observers.end(); ++i)
const MyEventObject * e =
const MyCommand* c = (*i)-&m_C
os && indent && e-&GetEventName() && "(" && c-&GetNameOfClass() && ")/n";
return true;
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!

我要回帖

更多关于 高斯赛德尔迭代矩阵 的文章

 

随机推荐