[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--