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:  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章