ITK 配准框架中的 Subject/Observer 模式及优化过程模拟演示-1

最近正式开始研究 ITK 的配准框架及其过程,先把自己理解到的一些东西写出来记下。

   ITK 中的医学图像配准框架主要由以下几部分组成:几何变换组件、图像插值组件、相似性测度组件、优化组件、以及连接各个组件的配准方法组件。对于多分辨策略还会多出两个图像金字塔组件。

   图像配准的本质是一个迭代优化的过程,关于图像配准框架示例可参考:ITK 配准框架示例,或者参考 ITK 文档。简单的说,用户指定一个几何变换策略、一个插值函数、一个相似性测度策略、一个优化策略。优化函数每迭代一次,便将计算出来的几何变换参数传递给相似性测度组件,相似性测度组件通过计算得出一个测度值。如果该测度值达到预期的目标,则指示优化函数目标达到,停止迭代。如果迭代次数超出用户设置的最大限制,同样停止迭代。对于ITK 配准框架示例 中的测度组件,测度值越小,则表明经过几何变换的浮动图像与固定图像的配准程度越高,当测度值达到预设的目标时,停止迭代,图像配准过程结束。

   在 ITK 的图像配准框架中,通过顶层类 Object 实现了 Subject/Observer 设计模式,或者称 Command/Observer 模式,就是简单地将 Observer 模式与 Command 模式相结合。用户可以注册优化过程的迭代事件 IterationEvent(),并通过继承 Command 类,并实现其 Execute() 成员函数完成监视配准过程。其中,Command 类相当于面向过程设计中的回调函数(callback),所以用户应该只完成一些简单的任务,如输出优化程序迭代过程的一些参数值以进行观察,ITK 配准框架示例 正是这样做的。当然,在使用多分辨策略的配准框架中,用户可以使用 Command 类完成在不同层间使用不同优化策略的任务。ITK 中使用 Subject/Observer 监视的简单的图像配准执行过程如下图所示:

ITKObserver 执行图

     本文为了演示 Subject/Observer 模式,以及如何通过注册该模式监视优化函数的迭代过程。为了便于演示,这里只涉及了优化组件、相似性测度组件以及用于连接各个组件的配准方法组件。去掉了几何变换组件、插值函数组件,并且没有使用 ITK 中大量运用的智能指针(SmartPointer)、对象工厂(ObjectFactory)、以及管道技术。对于智能指针以及对象工厂,会找时间另外进行研究演示。

如下为代码示例,我会在每个代码文件中给出适当的说明,这里大部分的代码都直接来自 ITK 源码,尤其是程序结构几乎是超级简化的 ITK 配准框架:

1.Command 类,Command 模式参考:Command 命令模式在实际使用中,没有必要完全实现模式中的每一个角色,ITK 中的 Command/Observer 模式将 Command 与 Observer 相结合,通过代码可以看到其实现是比较简单的。

   1:  //MyCommand.h
   2:  #pragma once
   3:  #include "MyTypeMacro.h"
   4:  #include "MyObject.h"
   5:   
   6:  //用于实现 Subject/Observer 模式, 或者称 Command/Observer 模式
   7:  //核心代码在 Execute() 中实现, 其功能相当于面向过程设计中的回调函数(callback)
   8:  //所以它只应该完成一些简单的任务, 客户实现具体的 Command 子类, 用于监视优化过程的执行
   9:  //Command: 抽象基类
  10:  class  MyCommand : public MyObject
  11:  {
  12:  public:
  13:      typedef MyCommand                   Self;
  14:      typedef MyObject                      Superclass;
  15:      typedef MyCommand*                    Pointer;
  16:      typedef const MyCommand*              ConstPointer;
  17:   
  18:      //运行时类型识别
  19:      MyTypeMacro(MyCommand, MyObject);
  20:   
  21:      //当观察者注册的事件发生时,最终调用的便是该函数
  22:      //客户继承 MyCommand 类, 实现 Execute 函数, 输出想要观察的数据
  23:      //注意: MyCommand 相当于一个回调函数的面向对象的实现
  24:      //所以 Execute 应该只进行一些简单的功能, 还要用于复杂的任务
  25:      virtual void Execute(MyObject *caller, const MyEventObject & event ) = 0;
  26:      virtual void Execute(const MyObject *caller, const MyEventObject & event ) = 0;
  27:  protected:
  28:      MyCommand(){    };
  29:      ~MyCommand(){    };
  30:  private:
  31:      MyCommand(const Self&);         //将拷贝构造函数和赋值函数设为私有
  32:      void operator=(const Self&);  
  33:  };

2. EventObject 类,表示某个事件的类,ITK 中的主要事件有:StartEvent()、EndEvent()、IterationEvent() 分别表示优化开始、结束、一次迭代事件。还有 ProgressEvent()、ModifiedEvent()、ExitEvent() 等等其它一些重要的事件。实际上我们完全可以使用一些简单的变量来表示某个事件的发生与否,但这们会使程序的结构非常混乱,不易维护,注意这里使用的了宏生成相应的事件类,非常方便且不易出错。

   1:  //MyEventObject.h
   2:  #pragma once
   3:  #include "MyIndent.h"
   4:   
   5:  //事件类, 用来表示某事件发生
   6:  //事实上, 在面向过程设计中我们可以用一些简单的 bool 变量来表示某些事件的发生与否
   7:   
   8:  //在 ITK 的配准框架中, 客户通过注册 IterationEvent 迭代事件观察配准过程的执行
   9:  //图像配准本质上是一个优化过程, 优化函数每进行一次优化便触发一次 IterationEvent 事件
  10:  //客户通过实现一个具体的 Command 类, 观察配准过程的重要数据
  11:   
  12:  //EventObject: 抽象基类
  13:  class MyEventObject
  14:  {
  15:  public:
  16:      MyEventObject() {}
  17:      MyEventObject(const MyEventObject&){};
  18:      virtual ~MyEventObject() {}
  19:   
  20:      //创建事件对象
  21:      virtual MyEventObject* MakeObject() const=0;  
  22:   
  23:      //打印事件信息
  24:      virtual void Print(std::ostream& os) const;
  25:   
  26:      //返回该事件名称
  27:      virtual const char* GetEventName(void) const=0;
  28:   
  29:      //检查参数传入的 Event 是否与该事件匹配, 相同或是其子类
  30:      virtual bool CheckEvent(const MyEventObject*) const=0;
  31:  protected:
  32:      //打印该事件的不同信息
  33:      virtual void PrintSelf(std::ostream& os, MyIndent indent) const;
  34:      virtual void PrintHeader(std::ostream& os, MyIndent indent) const;
  35:      virtual void PrintTrailer(std::ostream& os, MyIndent indent) const;
  36:  private:
  37:      typedef  MyEventObject* EventFactoryFunction();
  38:      void operator=(const MyEventObject&);
  39:  };
  40:   
  41:  //Generic inserter operator for EventObject and its subclasses. 
  42:  inline std::ostream& operator<<(std::ostream& os, MyEventObject &e)
  43:  {
  44:      (&e)->Print(os);
  45:      return os;
  46:  }
  47:   
  48:  //
  49:  //宏定义: 用来创建新的事件, 避免写过多相同的代码
  50:  //这种方法非常有用, 在自己的程序中可以经常使用
  51:  #define MyEventMacro( classname , super ) /
  52:  class  classname : public super{ /
  53:  public: /
  54:      typedef classname    Self; /
  55:      typedef super        Superclass; /
  56:      classname() {} /
  57:      virtual ~classname() {} /
  58:      virtual const char * GetEventName() const { return #classname; } /
  59:      virtual bool CheckEvent(const MyEventObject* e) const /
  60:      {     return dynamic_cast<const Self*>(e);    } /
  61:      virtual MyEventObject* MakeObject() const /
  62:          {     return new Self;    } /
  63:      classname(const Self&s) :super(s){}; /
  64:  private: /
  65:      void operator=(const Self&); /
  66:  };
  67:   
  68:  //利用上面的宏定义一些常用的事件:
  69:  MyEventMacro( MyNoEvent            , MyEventObject )
  70:  MyEventMacro( MyAnyEvent           , MyEventObject )
  71:  MyEventMacro( MyStartEvent         , MyAnyEvent )
  72:  MyEventMacro( MyEndEvent           , MyAnyEvent )
  73:  MyEventMacro( MyModifiedEvent      , MyAnyEvent )
  74:  MyEventMacro( MyIterationEvent     , MyAnyEvent )
  75:   
  76:  MyEventMacro( MyUserEvent          , MyAnyEvent )

  

3.Indent 类,用来控制打印时的缩排格式,这个类非常简单,但非常有用,虽然这对我要演示的内容没有任务联系,不过我还是将它列了出来,并进行了一些测试。因为我觉得这个类非常有用,尤其是它的方法,实际应用中会非常方便。

   1:  //MyIndent.h
   2:  #pragma once
   3:  #include <iostream>
   4:   
   5:  //MyIndent 类,用来控制缩排(indentation)输出
   6:  //一个类的继承层次可能很深, 一个事件可能有多个观察者...
   7:  //输出类自身信息时, 使用 MyIndent 来进行控制, 以使输出信息层次结构明了易懂
   8:  class MyIndent
   9:  {
  10:  public:
  11:      typedef MyIndent  Self;
  12:   
  13:      //创建一个 MyIndent 实例
  14:      static Self* New();
  15:   
  16:      //销毁该 MyIndent 实例 
  17:      void Delete() {    delete this; }
  18:   
  19:      //有参构造函数
  20:      MyIndent(int ind = 0) { m_Indent = ind; }
  21:   
  22:      //返回类名
  23:      static const char *GetNameOfClass() {return "MyIndent";}
  24:   
  25:      //设置下一级缩进量
  26:      MyIndent GetNextIndent();
  27:   
  28:      //以锯齿状输出缩排格式
  29:      friend std::ostream& operator<<(std::ostream& os, const MyIndent& o); 
  30:   
  31:  private:
  32:      int m_Indent;    //控制缩排量
  33:  };

4.Object 类,ITK 中 Object 继承自 LightObject,在 LightObject 中使用对象工厂与智能指针实现了引用计数,使得我们在程序中同一个类的对象实例只在内存中存在一份,并且不须考虑指针的释放。ITK 中,绝大部分类都要继承自 Object,还有一些继承自 LightObject。这里,我并没有使用智能指针与对象工厂。演示程序中的绝大部分类都要从 Object 继承。

重要的是:Object 类实现了 这里要演示的 Command/Observer 模式:

   1:   
   2:  //MyObject.h
   3:  #pragma once
   4:  #include "MyEventObject.h"
   5:   
   6:  // Subject/Command 设计模式
   7:  class Subject;
   8:  class MyCommand;
   9:   
  10:  //Object: 基类, 模仿 ITK 中的 Object 类
  11:  //这里省略了 Object 的基类 LightObject 类, 以及对象工厂与智能指针,这些另外进行演示
  12:  class MyObject
  13:  {
  14:  public:
  15:      typedef MyObject            Self;
  16:      typedef MyObject*            Pointer;    
  17:      typedef const MyObject*        ConstPointer;
  18:   
  19:      //New()
  20:      static Pointer New();
  21:   
  22:      //Delete()
  23:      virtual void Delete();
  24:   
  25:      // Subject/Observer 设计模式
  26:      // AddObserver,InvokeEvent 等函数,都是调用了 subject 的同名函数
  27:      unsigned long AddObserver(const MyEventObject & event, MyCommand *);
  28:      unsigned long AddObserver(const MyEventObject & event, MyCommand *) const;
  29:   
  30:      MyCommand* GetCommand(unsigned long tag);
  31:   
  32:      //对所有观察该事件的 Command 调用其 Execute 
  33:      void InvokeEvent( const MyEventObject & );
  34:      void InvokeEvent( const MyEventObject & ) const;
  35:   
  36:      //移除观察者
  37:      void RemoveObserver(unsigned long tag);
  38:      void RemoveAllObservers();
  39:   
  40:      //是否有观察者在观察该事件
  41:      bool HasObserver( const MyEventObject & event ) const;
  42:   
  43:      virtual const char *GetNameOfClass() const 
  44:      {    return "Object";    }
  45:      
  46:      //Print(), 客户调用, 打印相关信息
  47:      //由 PrintHeader, PrintSelf, PrintTrailer 三部分组成
  48:      void Print(std::ostream& os, MyIndent indent = 0) const;
  49:   
  50:  protected:
  51:      MyObject(); 
  52:      virtual ~MyObject(); 
  53:      
  54:      //打印自身的相关信息
  55:      virtual void PrintSelf(std::ostream& os, MyIndent indent) const;
  56:      virtual void PrintHeader(std::ostream& os, MyIndent indent) const;
  57:      virtual void PrintTrailer(std::ostream& os, MyIndent indent) const;
  58:      
  59:      //打印所有观察者的信息
  60:      bool PrintObservers(std::ostream& os, MyIndent indent) const;
  61:   
  62:  private:
  63:      MyObject(const Self&);        //将拷贝构造函数和赋值函数设为私有且不实现
  64:      void operator=(const Self&);
  65:   
  66:      Subject* m_Subject;    //Subject/Observer 模式; Object 维护一个指向 Subject 的指针
  67:  };

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() 函数完成用户指定的任务。程序中输出了当前迭代次数,当前参数值以及当前测度值。具体可以看代码实现。

   1:   
   2:  //SubjectObserver.h
   3:  #pragma once
   4:  #include <list>
   5:   
   6:  // Subject/Observer 观察者模式 
   7:  // Observer: 
   8:  //1. 一个指向 Command 的指针;客户定义具体的 Command 类, 实现相应的 Execute() 操作.
   9:  //2. 一个指向 Event 的指针;   客户想要观察的事件.
  10:  //3.Observer 标识;              用于标识该 Observer 的身份, 这样可以快速查询.
  11:  class Observer
  12:  {
  13:  public:
  14:      Observer(MyCommand* c,const MyEventObject * event, unsigned long tag) 
  15:          : m_Command(c),m_Event(event), m_Tag(tag)
  16:      {        }
  17:   
  18:      virtual ~Observer(){ delete m_Event; }
  19:   
  20:      MyCommand::Pointer     m_Command;        //1.Observer 维护指向 Command 和 Event 的指针
  21:      const MyEventObject *  m_Event;
  22:      unsigned long          m_Tag;            //Observer 的标识,也就是 Subject 中的 m_Count, 即第几个添加进来的 Observer
  23:  };
  24:   
  25:  //Subject: 维护一个 Observer 指针的列表
  26:  class Subject
  27:  {
  28:  public:
  29:      Subject() {    m_Count = 0;    }
  30:      ~Subject();
  31:   
  32:      //event 和 command 组合成一个 Observer 的实例
  33:      unsigned long AddObserver(const MyEventObject & event, MyCommand* cmd);
  34:      unsigned long AddObserver(const MyEventObject & event, MyCommand* cmd) const;
  35:      void RemoveObserver(unsigned long tag);
  36:      void RemoveAllObservers();
  37:   
  38:      //InvokeEvent: 触发事件的执行, 检查观察者链, 通知观察该事件的观察者
  39:      void InvokeEvent( const MyEventObject & event, MyObject* self);
  40:      void InvokeEvent( const MyEventObject & event, const MyObject* self);
  41:      MyCommand *GetCommand(unsigned long tag);
  42:      bool HasObserver(const MyEventObject & event) const;
  43:      bool PrintObservers(std::ostream& os, MyIndent indent) const;
  44:  private:
  45:      std::list<Observer* > m_Observers;        //维护一个 Observer 指针的列表
  46:      unsigned long m_Count;
  47:  };
  48:   
  49:  //
  50:  //Subject 类成员函数实现:
  51:  Subject::~Subject()
  52:  {
  53:      for(std::list<Observer* >::iterator i = m_Observers.begin();
  54:          i != m_Observers.end(); ++i)
  55:      {
  56:          delete (*i);
  57:      }
  58:      m_Observers.clear();
  59:  }
  60:   
  61:  //添加一个 Observer 至 观察者链 list
  62:  unsigned long Subject::AddObserver(const MyEventObject & event,MyCommand* cmd)
  63:  {
  64:      //由传入的 event 和 command 创建一个新的 Observer
  65:      //cmd 是具体的 Command 实例, 用于执行用户自定义的命令
  66:      //event 是客户所要观察的事件, 如迭代事件: IterationEvent()
  67:      Observer* ptr = new Observer(cmd, event.MakeObject(), m_Count);
  68:      m_Observers.push_back(ptr);
  69:      m_Count++;                    // + 1, 下一个 Observer 的标识
  70:      return ptr->m_Tag;
  71:  }
  72:  //
  73:  unsigned long Subject::AddObserver(const MyEventObject & event,MyCommand* cmd) const
  74:  {
  75:      Observer* ptr = new Observer(cmd, event.MakeObject(), m_Count);
  76:      //将 Subject 的常量属性去掉!!!
  77:      Subject * me = const_cast<Subject *>( this );
  78:      me->m_Observers.push_back(ptr);
  79:      me->m_Count++;
  80:      return ptr->m_Tag;
  81:  }
  82:  //移除指定的观察者
  83:  void Subject::RemoveObserver(unsigned long tag)
  84:  {
  85:      for(std::list<Observer* >::iterator i = m_Observers.begin();
  86:          i != m_Observers.end(); ++i)
  87:      {
  88:          if((*i)->m_Tag == tag)    //通过比较 Observer 的标识进行判断,速度快
  89:              delete (*i);
  90:          m_Observers.erase(i);
  91:          return;
  92:      }
  93:  }
  94:  //移除所有的观察者
  95:  void Subject::RemoveAllObservers()
  96:  {
  97:      for(std::list<Observer* >::iterator i = m_Observers.begin();
  98:          i != m_Observers.end(); ++i)
  99:      {
 100:          delete (*i);
 101:      }
 102:      m_Observers.clear();
 103:  }
 104:   
 105:  //
 106:  //触发指定事件, 检查观察者链, 如果某个观察者已经注册观察该事件,则通知该观察者
 107:  //该函数会调用具体的 Command 实例的 Execute() 方法, 客户实现该方法,以输出想要的数据
 108:  void Subject::InvokeEvent( const MyEventObject & event, MyObject* self)
 109:  {
 110:      //Subject:    std::list<Observer* > m_Observers;
 111:      //MyCommand:  Execute(const MyObject *caller, const MyEventObject & event );
 112:      for(std::list<Observer* >::iterator i = m_Observers.begin();
 113:          i != m_Observers.end(); ++i)
 114:      {
 115:          const MyEventObject * e =  (*i)->m_Event;
 116:          //检查该观察者注册的事件是否是参数传入的 event, 或者继承自 e
 117:          if(e->CheckEvent(&event))                
 118:          {
 119:              //m_Command 是 Observer 数据成员, 维护一个 Observer 列表
 120:              (*i)->m_Command->Execute(self, event);
 121:          }
 122:      }
 123:  }
 124:   
 125:  //
 126:  void Subject::InvokeEvent( const MyEventObject & event,const MyObject* self)
 127:  {
 128:      for(std::list<Observer* >::iterator i = m_Observers.begin();
 129:          i != m_Observers.end(); ++i)
 130:      {
 131:          const MyEventObject * e =  (*i)->m_Event;
 132:          if(e->CheckEvent(&event))
 133:          {
 134:              (*i)->m_Command->Execute(self, event);
 135:          }
 136:      }
 137:  }
 138:  //返回 subject 列表中标识 m_Tag == tag 的 Observer
 139:  MyCommand* Subject::GetCommand(unsigned long tag)
 140:  {
 141:      for(std::list<Observer* >::iterator i = m_Observers.begin();
 142:          i != m_Observers.end(); ++i)
 143:      {
 144:          if ( (*i)->m_Tag == tag)
 145:          {
 146:              return (*i)->m_Command;
 147:          }
 148:      }
 149:      return 0;
 150:  }
 151:  //返回是否有正在观察 event 事件的 Observer
 152:  bool Subject::HasObserver(const MyEventObject & event) const
 153:  {
 154:      for(std::list<Observer* >::const_iterator i = m_Observers.begin();
 155:          i != m_Observers.end(); ++i)
 156:      {
 157:          const MyEventObject * e =  (*i)->m_Event;
 158:          if(e->CheckEvent(&event))
 159:          {
 160:              return true;
 161:          }
 162:      }
 163:      return false;
 164:  }
 165:  //打印观察者链中的所有观察者信息
 166:  bool Subject::PrintObservers(std::ostream& os, MyIndent indent) const
 167:  {
 168:      if(m_Observers.empty())
 169:      {
 170:          return false;
 171:      }
 172:   
 173:      for(std::list<Observer* >::const_iterator i = m_Observers.begin();
 174:          i != m_Observers.end(); ++i)
 175:      {
 176:          const MyEventObject * e =  (*i)->m_Event;
 177:          const MyCommand* c = (*i)->m_Command;
 178:          os << indent << e->GetEventName() << "(" << c->GetNameOfClass() << ")/n";
 179:      }
 180:      return true;
 181:  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值