[Insight-users] Re: MultResMIRegistration using CT and US

Jon Harald Kaspersen Jon.H.Kaspersen@unimed.sintef.no
Thu, 16 May 2002 14:46:06 +0200


--Apple-Mail-7-296293506
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

Hi again Luis,

And again thanks for you very relevant comments/answers.

I did use most of my workday to explore the MI example using different 
configurations of
the CT and US volumes.  I have stripped the CT volume to be of the same 
size as the US volume, mostly to reduce the CPU time.  As I mentioned in 
my last mail the data we are talking about are from patients with 
abdominal aortic aneurysms (AAA) who are treated with an endovascular 
technique.  This means that they have an stent-graft implanted (you can 
find out more at our home page www.us.sintef.unimed.no).  The AAA and 
the stent-graft is the main structure in the US volume and hopefully 
that structure can be used as the "landmark".  The CT data is acquired 
with contrast (injected) enhancement.  The metal structure in the 
stent-graft gives high amplitude echo in the US images (shows up as 
bright pixels), and so does the aortic wall.  This may lead to confusion 
with structures that are bright in the CT volume, like the bone 
structure.  In general, the color map for US and CT is not the same, as 
you probably are aware of.  I guess what I am trying to say is that 
structures that are bright in the CT volume might be less bright or even 
close to black in the US volume.  Thereby the MI algorithm will be 
confused and don't know which structures to match.


Regards
Jon

On Wednesday, May 15, 2002, at 04:10 PM, Luis Ibanez wrote:

>
> Hi Jon,
>
>
> A) About starting the registration with an
>    initial "offset", this can easily be done
>    with the method:
>
>     SetInitialTransformParameters();
>
> This methods receives an itkArray with the set of
> values that define the initial transformation applied
> to the moving image. This is like serializing the
> values that fully define a transform. For the particular
> case of the itkAffineTransform, the parameters are arranged
> as follows:
>
> 1) the matrix is stored line by line in the array
> 2) the offset of the transform (translation component)
>    is put at the end of the array of parameters.
>
> You may see the details on :
>
> Insight/Code/Common/itkAffineTransform.txx: line 578
> in the "GetParameters()" method, as well as line 611
> in the "SetParameters()" method.
>
>
>
>
>
> B) About getting feedback from the execution
>    of the registration method:
>
> ITK has a mechanism for communicating with other
> pieces of software. It is based on the
> Command/Observer design pattern. It similar to
> the concept of Observers in Java.
>
> You can use this mechanism in order to get
> information about the progresion of the registration
> method. (and about the state of any ITK filter too)
>
>
> Here is a brief description of how it
> works:
>
>
> 1) There is a hierarchy of ITK "Events"
>    that you can find defined in :
>
>    Insight/Code/Common/itkEventObject.h
>
>    in particular after line 132.
>
> 2) itkObjects can send these events in
>    order to notify others about their
>    internal state. For example, every
>    ITK filter sends a "StartEvent" when
>    it starts excecution. They also send
>    "UpdateProgress" events every once in
>    a while so you can for example update
>    a progress bar in a GUI. (an example
>    of this can be seen in:
>
> http://www.itk.org/HTML/GaussianFilter.htm
>
>
> 3) In order to catch this events you can
>    setup "Observers" which in ITK have
>    been defined following the "Command"
>    design pattern.  Observers register
>    themselves with an itkObject and declare
>    its interest on being notified when a
>    particular event happens.  Given that
>    events are organized in a hierarchy,
>    an observer can register for a particular
>    event in the hierarchy and it will be
>    notified for this event and all of its
>    subclasses. (pretty much as Exceptions
>    work in C++ and Java).
>
> 4) The Event that you may be interested on
>    is the "IterationEvent". This event is
>    sent by Optimizers at each iteration.
>    The execution of the registration method
>    is driven by its optimizer.
>
> 5) By writing a customized observer/command
>    class and registering with the Registration
>    method class you can make sure that a method
>    or function that you define will be called
>    at each iteration.  In this function you
>    may want to query the Registration method
>    and for example print data to the standard
>    output, write it to a file or update a GUI.
>
> 6) For the MultiResolution registration example
>    that you are using now, you will see that
>    an Observer is defined in the file:
>
>     Insight/Examples/MultiResMiRegistration/Common
>
>     MIMRegistrator.txx
>
>     It is in line: 57
>
>   typename CommandType::Pointer command = CommandType::New();
>
> a callback function is attached to this command in line 58:
>
>   command->SetCallbackFunction( this, &Self::StartNewLevel );
>
> In this particular case, the callback can be a member method
> of a class.
>
> There is a variety of Command classes that you may use.
>
> They are defined in:   Insight/Common/itkCommand.h
>
> Some of them execute actions themselves, others are intended
> to delegate execution to C++ methods, others allow you to
> call plain C functions.
>
>
> 7) In the case of the example, you will find that
>    the command is delegating to the C++ method:
>    StartNewLevel() implemented in line : 179.
>
>    So, If you want more information about the state
>    of the Registration, you can modify this method,
>    or create your own customized Command class.
>
> 8) Note that because this is the Multiresolution
>    Registration method, an Iteration of this class
>    corresponds to a full execution of a registration
>    for one level of the pyramid.  That may be too
>    high level for what you are trying to do now,
>    which is figure out good values for the learning
>    rates.
>
> 9) You may want to go a level lower and get the optimizer
>    that is used by the registration method. Plug an
>    observer to the "IterationEvent" on this optimizer
>    and define a callback that will print the status of
>    the optimizer.  You can see examples of this in the
>    Testing directory:
>
>    Insight/Testing/Code/Algorithms/itkImageRegistrationTest_*.cxx
>
> a Command helper class is defined in:
>
>    Insight/Testing/Code/Algorithms/itkCommandIterationUpdate.h
>
>
> 10) In order to get the current optimizer from inside the
>     MIMRegistrator.txx file you can do something like add
>     around line 60 the following code:
>
>    m_Registration->GetOptimizer()->AddObserver(
>                                       IterationEvent(),myCommand);
>
> And declare "myCommand" like in the itkCommandIterationUpdate.h used
> in Testing.
>
> This combination will print to std::cout the values of the
> transform parameter at each iteration of the optimizer.
>
> By following the progression of these values you can figure
> out the appropriated values for the LearnigRates.
>
>
>
> Please let us know if you encounter any problem,
>
>
>
>   Thanks
>
>
>    Luis
>
>
> ==================================
> Jon Harald Kaspersen wrote:
>> Hi again Luis,
>> Thanks for making things a lot clearer !!
>> I have no problem with copying these mails to the user list.
>> I have started the iterative procedure to find a set of parameters 
>> that suits my data.
>> Some additional questions comes to my mind.
>> 1. Does the standard output give any information about how much the 
>> source data moves between iterations or levels ? I guess that little 
>> movement between iterations means that the data is quite close, or a 
>> good registration has been found ?
>> 2. My data is from the abdomen (in fact from patients with abdominal 
>> aortic aneurysms). The CT scan covers a much bigger part of the body 
>> than the US. Is there a way to "offset" the US, a sort of a starting 
>> point for the iteration ? I could of cause just use the overlapping 
>> data regions in the axial direction and cut away the CT data that is 
>> not covered by the US.
>> Regards
>> Jon
>
>
>
========================================================
Jon Harald Kaspersen			Tel: 		+47 73 59 75 89
Ph.D. Mechanical Engineering		Mob:		+47 93 03 65 90
Senior Scientist				Pager	+47 96 84 29 94
SINTEF Unimed - Ultralyd		Fax: 		+47 73 59 78 73
N-7465 Trondheim
NORWAY			e-mail:	Jon.H.Kaspersen@unimed.sintef.no
				WEB:	http://www.us.unimed.sintef.no/
========================================================

--Apple-Mail-7-296293506
Content-Transfer-Encoding: 7bit
Content-Type: text/enriched;
	charset=US-ASCII

Hi again Luis,


And again thanks for you very relevant comments/answers.


I did use most of my workday to explore the MI example using different
configurations of

the CT and US volumes.  I have stripped the CT volume to be of the
same size as the US volume, mostly to reduce the CPU time.  As I
mentioned in my last mail the data we are talking about are from
patients with abdominal aortic aneurysms (AAA) who are treated with an
endovascular technique.  This means that they have an stent-graft
implanted (you can find out more at our home page
www.us.sintef.unimed.no).  The AAA and the stent-graft is the main
structure in the US volume and hopefully that structure can be used as
the "landmark".  The CT data is acquired with contrast (injected)
enhancement.  The metal structure in the stent-graft gives high
amplitude echo in the US images (shows up as bright pixels), and so
does the aortic wall.  This may lead to confusion with structures that
are bright in the CT volume, like the bone structure.  In general, the
color map for US and CT is not the same, as you probably are aware of. 
I guess what I am trying to say is that structures that are bright in
the CT volume might be less bright or even close to black in the US
volume.  Thereby the MI algorithm will be confused and don't know
which structures to match.



Regards

Jon


On Wednesday, May 15, 2002, at 04:10 PM, Luis Ibanez wrote:


<excerpt>

Hi Jon,



A) About starting the registration with an

   initial "offset", this can easily be done

   with the method:


    SetInitialTransformParameters();


This methods receives an itkArray with the set of

values that define the initial transformation applied

to the moving image. This is like serializing the

values that fully define a transform. For the particular

case of the itkAffineTransform, the parameters are arranged

as follows:


1) the matrix is stored line by line in the array

2) the offset of the transform (translation component)

   is put at the end of the array of parameters.


You may see the details on :


Insight/Code/Common/itkAffineTransform.txx: line 578

in the "GetParameters()" method, as well as line 611

in the "SetParameters()" method.






B) About getting feedback from the execution

   of the registration method:


ITK has a mechanism for communicating with other

pieces of software. It is based on the

Command/Observer design pattern. It similar to

the concept of Observers in Java.


You can use this mechanism in order to get

information about the progresion of the registration

method. (and about the state of any ITK filter too)



Here is a brief description of how it

works:



1) There is a hierarchy of ITK "Events"

   that you can find defined in :


   Insight/Code/Common/itkEventObject.h


   in particular after line 132.


2) itkObjects can send these events in

   order to notify others about their

   internal state. For example, every

   ITK filter sends a "StartEvent" when

   it starts excecution. They also send

   "UpdateProgress" events every once in

   a while so you can for example update

   a progress bar in a GUI. (an example

   of this can be seen in:


http://www.itk.org/HTML/GaussianFilter.htm



3) In order to catch this events you can

   setup "Observers" which in ITK have

   been defined following the "Command"

   design pattern.  Observers register

   themselves with an itkObject and declare

   its interest on being notified when a

   particular event happens.  Given that

   events are organized in a hierarchy,

   an observer can register for a particular

   event in the hierarchy and it will be

   notified for this event and all of its

   subclasses. (pretty much as Exceptions

   work in C++ and Java).


4) The Event that you may be interested on

   is the "IterationEvent". This event is

   sent by Optimizers at each iteration.

   The execution of the registration method

   is driven by its optimizer.


5) By writing a customized observer/command

   class and registering with the Registration

   method class you can make sure that a method

   or function that you define will be called

   at each iteration.  In this function you

   may want to query the Registration method

   and for example print data to the standard

   output, write it to a file or update a GUI.


6) For the MultiResolution registration example

   that you are using now, you will see that

   an Observer is defined in the file:


    Insight/Examples/MultiResMiRegistration/Common


    MIMRegistrator.txx


    It is in line: 57


  typename CommandType::Pointer command = CommandType::New();


a callback function is attached to this command in line 58:


  command->SetCallbackFunction( this, &Self::StartNewLevel );


In this particular case, the callback can be a member method

of a class.


There is a variety of Command classes that you may use.


They are defined in:   Insight/Common/itkCommand.h


Some of them execute actions themselves, others are intended

to delegate execution to C++ methods, others allow you to

call plain C functions.



7) In the case of the example, you will find that

   the command is delegating to the C++ method:

   StartNewLevel() implemented in line : 179.


   So, If you want more information about the state

   of the Registration, you can modify this method,

   or create your own customized Command class.


8) Note that because this is the Multiresolution

   Registration method, an Iteration of this class

   corresponds to a full execution of a registration

   for one level of the pyramid.  That may be too

   high level for what you are trying to do now,

   which is figure out good values for the learning

   rates.


9) You may want to go a level lower and get the optimizer

   that is used by the registration method. Plug an

   observer to the "IterationEvent" on this optimizer

   and define a callback that will print the status of

   the optimizer.  You can see examples of this in the

   Testing directory:


   Insight/Testing/Code/Algorithms/itkImageRegistrationTest_*.cxx


a Command helper class is defined in:


   Insight/Testing/Code/Algorithms/itkCommandIterationUpdate.h



10) In order to get the current optimizer from inside the

    MIMRegistrator.txx file you can do something like add

    around line 60 the following code:


   m_Registration->GetOptimizer()->AddObserver(

                                      IterationEvent(),myCommand);


And declare "myCommand" like in the itkCommandIterationUpdate.h used

in Testing.


This combination will print to std::cout the values of the

transform parameter at each iteration of the optimizer.


By following the progression of these values you can figure

out the appropriated values for the LearnigRates.




Please let us know if you encounter any problem,




  Thanks



   Luis



==================================

Jon Harald Kaspersen wrote:

<excerpt>Hi again Luis,

Thanks for making things a lot clearer !!

I have no problem with copying these mails to the user list.

I have started the iterative procedure to find a set of parameters
that suits my data.

Some additional questions comes to my mind.

1. Does the standard output give any information about how much the
source data moves between iterations or levels ? I guess that little
movement between iterations means that the data is quite close, or a
good registration has been found ?

2. My data is from the abdomen (in fact from patients with abdominal
aortic aneurysms). The CT scan covers a much bigger part of the body
than the US. Is there a way to "offset" the US, a sort of a starting
point for the iteration ? I could of cause just use the overlapping
data regions in the axial direction and cut away the CT data that is
not covered by the US.

Regards

Jon

</excerpt>



</excerpt>========================================================

Jon Harald Kaspersen			Tel: 		+47 73 59 75 89

Ph.D. Mechanical Engineering		Mob:		+47 93 03 65 90

Senior Scientist				Pager	+47 96 84 29 94

SINTEF Unimed - Ultralyd		Fax: 		+47 73 59 78 73

N-7465 Trondheim

NORWAY			e-mail:	Jon.H.Kaspersen@unimed.sintef.no

				WEB:	<underline><color><param>1A1A,1A1A,FFFF</param>http://www.us.unimed.sintef.no/</color></underline>

========================================================
--Apple-Mail-7-296293506--