SliceOrder
Description¶
Permute image and other geometric data to maintain proper orientation regardless of the acquisition order.
For examples showing the usage of this snippet please see FroggieSurface and FroggieView.
To use the snippet, click the Copy to clipboard at the upper right of the code blocks.
Declaration Section¶
#include <vtkMatrix4x4.h>
#include <vtkNew.h>
#include <vtkSmartPointer.h>
#include <vtkStringFormatter.h>
#include <vtkTransform.h>
#include <array>
#include <map>
#include <string>
#include <vector>
namespace {
class SliceOrder
{
// clang-format off
/*
These transformations permute image and other geometric data to maintain
proper orientation regardless of the acquisition order. After applying
these transforms with vtkTransformFilter, a view up of 0, -1, 0 will
result in the body part facing the viewer.
NOTE: Some transformations have a -1 scale factor for one of the components.
To ensure proper polygon orientation and normal direction,
you must apply the vtkPolyDataNormals filter.
Naming (the nomenclature is medical):
si - superior to inferior (top to bottom)
is - inferior to superior (bottom to top)
ap - anterior to posterior (front to back)
pa - posterior to anterior (back to front)
lr - left to right
rl - right to left
*/
// clang-format on
public:
/**
* Generate the transforms corresponding to the slice order.
*/
SliceOrder();
virtual ~SliceOrder() = default;
public:
/**
* Returns the vtkTransform corresponding to the slice order.
*
* @param sliceOrder: The slice order.
* @return The vtkTransform corresponding to the slice order.
*/
vtkSmartPointer<vtkTransform> Get(std::string const& sliceOrder);
/**
* Print the homogenous matrix corresponding to the slice order.
*
* @param order: The slice order.
*/
void PrintTransform(std::string const& order);
/**
* Print all the homogenous matrices corresponding to the slice orders.
*
*/
void PrintAlltransforms();
private:
std::map<std::string, vtkSmartPointer<vtkTransform>> transform;
private:
// Used to genrate the the homogenous matrices corresponding to the slice
// orders.
std::array<double, 16> si_arr{1, 0, 0, 0, 0, 0, 1, 0,
0, -1, 0, 0, 0, 0, 0, 1};
std::array<double, 16> is_arr{1, 0, 0, 0, 0, 0, -1, 0,
0, -1, 0, 0, 0, 0, 0, 1};
std::array<double, 16> lr_arr{0, 0, -1, 0, 0, -1, 0, 0,
1, 0, 0, 0, 0, 0, 0, 1};
std::array<double, 16> rl_arr{0, 0, 1, 0, 0, -1, 0, 0,
1, 0, 0, 0, 0, 0, 0, 1};
std::array<double, 16> hf_arr{-1, 0, 0, 0, 0, 1, 0, 0,
0, 0, -1, 0, 0, 0, 0, 1};
// Used to print out the homogenous matrices in the order
// entered into the transform map.
std::vector<std::string> sort_order{};
};
}
Implementation Section¶
namespace {
SliceOrder::SliceOrder()
{
vtkNew<vtkMatrix4x4> si_mat;
si_mat->SetData(this->si_arr.data());
vtkNew<vtkMatrix4x4> is_mat;
is_mat->SetData(this->is_arr.data());
vtkNew<vtkMatrix4x4> lr_mat;
lr_mat->SetData(this->lr_arr.data());
vtkNew<vtkMatrix4x4> rl_mat;
rl_mat->SetData(this->rl_arr.data());
// The previous transforms assume radiological views of the slices
// (viewed from the feet).
// Other modalities such as physical sectioning may view from the head.
// The following transforms modify the original with a 180° rotation
// about the y-axis.
vtkNew<vtkMatrix4x4> hf_mat;
hf_mat->SetData(this->hf_arr.data());
vtkNew<vtkTransform> si_trans;
si_trans->SetMatrix(si_mat);
this->transform["si"] = si_trans;
this->sort_order.push_back("si");
vtkNew<vtkTransform> is_trans;
is_trans->SetMatrix(is_mat);
this->transform["is"] = is_trans;
this->sort_order.push_back("is");
vtkNew<vtkTransform> ap_trans;
ap_trans->Scale(1, -1, 1);
this->transform["ap"] = ap_trans;
this->sort_order.push_back("ap");
vtkNew<vtkTransform> pa_trans;
pa_trans->Scale(1, -1, -1);
this->transform["pa"] = pa_trans;
this->sort_order.push_back("pa");
vtkNew<vtkTransform> lr_trans;
lr_trans->SetMatrix(lr_mat);
this->transform["lr"] = lr_trans;
this->sort_order.push_back("lr");
vtkNew<vtkTransform> rl_trans;
lr_trans->SetMatrix(rl_mat);
this->transform["rl"] = rl_trans;
this->sort_order.push_back("rl");
vtkNew<vtkTransform> hf_trans;
hf_trans->SetMatrix(hf_mat);
this->transform["hf"] = hf_trans;
this->sort_order.push_back("hf");
vtkNew<vtkTransform> hf_si_trans;
hf_si_trans->SetMatrix(hf_mat);
hf_si_trans->Concatenate(si_mat);
this->transform["hfsi"] = hf_si_trans;
this->sort_order.push_back("hfsi");
vtkNew<vtkTransform> hf_is_trans;
hf_is_trans->SetMatrix(hf_mat);
hf_is_trans->Concatenate(is_mat);
this->transform["hfis"] = hf_is_trans;
this->sort_order.push_back("hfis");
vtkNew<vtkTransform> hf_ap_trans;
hf_ap_trans->SetMatrix(hf_mat);
hf_ap_trans->Scale(1, -1, 1);
this->transform["hfap"] = hf_ap_trans;
this->sort_order.push_back("hfap");
vtkNew<vtkTransform> hf_pa_trans;
hf_pa_trans->SetMatrix(hf_mat);
hf_pa_trans->Scale(1, -1, -1);
this->transform["hfpa"] = hf_pa_trans;
this->sort_order.push_back("hfpa");
vtkNew<vtkTransform> hf_lr_trans;
hf_lr_trans->SetMatrix(hf_mat);
hf_lr_trans->Concatenate(lr_mat);
this->transform["hflr"] = hf_lr_trans;
this->sort_order.push_back("hflr");
vtkNew<vtkTransform> hf_rl_trans;
hf_rl_trans->SetMatrix(hf_mat);
hf_rl_trans->Concatenate(rl_mat);
this->transform["hfrl"] = hf_rl_trans;
this->sort_order.push_back("hfrl");
// Identity
this->transform["I"] = vtkNew<vtkTransform>();
this->sort_order.push_back("I");
// Zero
vtkNew<vtkTransform> z_trans;
z_trans->Scale(0, 0, 0);
this->transform["Z"] = z_trans;
this->sort_order.push_back("Z");
}
void SliceOrder::PrintTransform(std::string const& order)
{
auto m = this->transform[order]->GetMatrix();
vtk::println(order);
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
if (j < 3)
{
vtk::print("{:>6.2g}", m->GetElement(i, j));
}
else
{
vtk::println("{:>6.2g}", m->GetElement(i, j));
}
}
}
}
void SliceOrder::PrintAlltransforms()
{
for (auto const& k : this->sort_order)
{
PrintTransform(k);
}
}
vtkSmartPointer<vtkTransform> SliceOrder::Get(std::string const& sliceOrder)
{
return this->transform[sliceOrder];
}
} // namespace
Usage¶
SliceOrder so;
// so.PrintAlltransforms();