Curved Reformat

<< Click to Display Table of Contents >>

Navigation:  XStream® HDVR® SDK > Advanced Functionality >

Curved Reformat

Previous pageReturn to chapter overviewNext page

C++ C++ Java Java .NET .NET

Summary

 

The XStream® HDVR® Curved Reformat creates a new dataset by applying a set of transformations to an existing dataset, extending functionality as described in the Curved MPR chapter. The Curved MPR feature renders an image by applying a transformation to a number of planar slices through the dataset. The slices are then aligned along a common axis.

 

The Curved MPR feature is used to straighten the visualization of a curved structure to build a view that can provide better diagnostic imaging and analysis. A Curved MPR image can be thought of as a single dataset slice. The Curved Reformat feature generates a new dataset by taking multiple Curved MPR slices and stacking them together to form a new dataset. This dataset can then be manipulated and rendered as any other dataset using the XStream® HDVR® SDK features.

 

Review the Curved MPR feature for a better understanding of how it relates to Curved Reformat.

 

CurvedReformat_Before

Trace of a curved vessel

CurvedReformat_After

Recomputed volume with Curved Reformat

 

Curved Reformat

 

To create a new reformatted series along this path, multiple groups of planar slices through the dataset are specified, along with details about how to align the slices with a vector axis. Each group of planar slices, once realigned, forms a new dataset slice. Each of the newly reformatted dataset slices are then stacked together to form a new dataset using the IRenderEngineContext::GetCurvedReformat() and IRenderQueue::GetCurvedReformat() methods based on the following parameters:

 

IVolumeDataContext **pData

The address of an IVolumeDataContext pointer that will receive the newly generated dataset.

const h_uint32 numProjPlanesPerSlice

The number of MATRIX44D entries that define the planar slices that compose each new dataset slice.

const h_uint32 numSlices

The number of slices that will make up the new dataset.

const h_uint32 ppWidth

Width of the newly generated dataset.

const h_uint32 ppHeight

Height of the newly generated dataset.

const h_uint32 measurementSlice

The index (0-based) of the slice to specify for accurate measurement. For example, when 3 is specified, the fourth image in the volume will have the property that pixels in the X and Y directions will be accurately represented in millimeters.

const h_float32 zoom

The number of pixels per voxel in the non-curved dimension. If this is -1, the pixels per voxel value is calculated based upon the length of the curve.

MATRIX44D **arrProjPlanes

A two dimensional array composed of MATRIX44D objects. The dimensions are arrProjPlanes[numSlices][numProjPlanesPerSlice]. These are the matrices that define each Curved MPR slice that is assembled into the Curved Reformat dataset volume.

ENUM_CURVED_MPR_TYPE

Describes which axis is non-linear (and which is linear) during the curve straightening process, as well as the interpolation type used to render the image.

 

// Parameters for new dataset

int thickness = 50;

float spacing = 0.25f;

int height = 50;

int numSlices = (int)(thickness / spacing);

 

MATRIX44D **arr2ProjPlanes = new (MATRIX44D *)[numSlices];

 

RENDER_PARAMS rp;

BCOM_ZEROMEMORY(&rp);

 

rp.Transform.setIdentity();

VECTOR3D vecZ = rp.Transform.getZVector();

vecZ.normalize();

 

if(vecZ.dot(new VECTOR3D(0.0, 0.0, 1.0)) > 0.0) {

   vecZ = vecZ.scale(-1.0);

}

 

// get the points ahead of time for the tangent calculation

VECTOR3D *points = new VECTOR3D[m_NumPoints];

for(int i = 0; i < m_NumPoints; i++) {

   points[i] = m_points[i].vector3D;

}

 

for(int j = 0; j < numSlices; j++) {

   // for each slice

   arr2ProjPlanes[j] = new MATRIX44D[m_NumPoints];

 

   // now calculate the points for this slice                                                

   for (int i = 0; i < m_NumPoints; i++) {

      // for each point on the slice

      C3DHelpers 3dHelpers;

      VECTOR3D tangent

      3dHelpers.GetLineTangent(&tangent, &points, i, i == m_NumPoints - 1 ? i : i + 1, 0.0);

      VECTOR3D normal = tangent.cross(rp.Transform.getZVector());

      normal.normalize();

 

      double offset = ((double)j) - (((double)numSlices) / 2.0);

      VECTOR3D vecLoc = points[i].add(normal.scale(spacing * offset));

 

      arr2ProjPlanes[j][i] = new MATRIX44D(vecZ, new VECTOR3D(), new VECTOR3D(), vecLoc);

   }

}

 

double curvedLength = getCurvedLength(points);

 

int volWidth = (int)((curvedLength) / spacing);

int volHeight = (int)(height / spacing);

if (volWidth > 4096 || volHeight > 4096) {

   // "Volume width or height ends up being greater than 4096. This is currently unsupported.");

}

 

VOLUME_DATA_PARAMS oldVDP; 

m_vol.GetVolumeDataParams(&oldVDP, true);

 

IVolumeDataContext pVolumeData;

m_crVolume.GetCurvedReformat(&pVolumeData, m_NumPoints, numSlices, volWidth, volHeight,

   m_NumPoints / 2, (float)(oldVDP.Spacing.y / spacing), arr2ProjPlanes, CURVED_MPR_TYPE_X_IS_CURVED_TRICUBIC);

 

To create a new reformatted series along this path, multiple groups of planar slices through the dataset are specified, along with details about how to align the slices with a vector axis. Each group of planar slices, once realigned, forms a new dataset slice. Each of the newly reformatted dataset slices are then stacked together to form a new dataset using the hdrcRenderEngineContext::getCurvedReformat() and hdrcRenderQueue::getCurvedReformat() methods based on the following parameters:

 

int numProjPlanesPerSlice

The number of MATRIX44D entries that define the planar slices that compose each new dataset slice.

int numSlices

The number of slices that will make up the new dataset.

int ppWidth

Width of the newly generated dataset.

int ppHeight

Height of the newly generated dataset.

int measurementSlice

The the index (0-based) of the slice to specify for accurate measurement. For example, when three is specified, the fourth image in the volume will have the property that pixels in the X and Y directions will be accurately represented in millimeters.

float zoom

The number of pixels per voxel in the non-curved dimension. If this is -1, the pixels per voxel value is calculated based upon the length of the curve.

MATRIX44D arrProjPlanes[][]

A two dimensional array composed of MATRIX44D objects. The dimensions are arrProjPlanes[numSlices][numProjPlanesPerSlice]. These are the matrices that define each Curved MPR slice that is assembled into the Curved Reformat dataset volume.

ENUM_CURVED_MPR_TYPE

Describes which axis is non-linear (and which is linear) during the curve straightening process, as well as the interpolation type used to render the image.

 

// Parameters for new dataset

int thickness = 50;

float spacing = 0.25f;

int height = 50;

int numSlices = (int)(thickness / spacing);

 

MATRIX44D[][] arr2ProjPlanes = new MATRIX44D[slices][];

 

RENDER_PARAMS rp = new RENDER_PARAMS();

rp.Transform.setIdentity();

VECTOR3D vecZ = rp.Transform.getZVector();

vecZ.normalize();

 

if(vecZ.dot(new VECTOR3D(0.0, 0.0, 1.0)) > 0.0) {

   vecZ = vecZ.scale(-1.0);

}

 

// get the points ahead of time for the tangent calculation

VECTOR3D[] points = new VECTOR3D[m_points.length];

for(int i = 0; i < m_points.length; i++) {

   points[i] = m_points[i].vector3D;

}

 

for(int j = 0; j < arr2ProjPlanes.length; j++) {

   // for each slice

   arr2ProjPlanes[j] = new MATRIX44D[m_points.length];

 

   // now calculate the points for this slice                                                

   for (int i = 0; i < m_points.length; i++) {

      // for each point on the slice

      VECTOR3D tangent = hdrc3DHelpers.getLineTangent(points, i, i == arr2ProjPlanes[j].length - 1 ? i : i + 1, 0.0);

      VECTOR3D normal = tangent.cross(rp.Transform.getZVector());

      normal.normalize();

 

      double offset = ((double)j) - (((double)arr2ProjPlanes.length) / 2.0);

      VECTOR3D vecLoc = points[i].add(normal.scale(spacing * offset));

 

      arr2ProjPlanes[j][i] = new MATRIX44D(vecZ, new VECTOR3D(), new VECTOR3D(), vecLoc);

   }

}

 

double curvedLength = getCurvedLength(points);

 

int volWidth = (int)((curvedLength) / spacing);

int volHeight = (int)(height / spacing);

if (volWidth > 4096 || volHeight > 4096) {

   // "Volume width or height ends up being greater than 4096. This is currently unsupported.");

}

 

VOLUME_DATA_PARAMS oldVDP = m_vol.getVolumeDataParams(true);

 

hdrcVolumeDataContext vdc = m_crVolume.getCurvedReformat( m_points.length, arr2ProjPlanes.length, volWidth, volHeight,

   m_points.Count / 2, (float)(oldVDP.Spacing.y / spacing), arr2ProjPlanes, hdrcDefines.CURVED_MPR_TYPE_X_IS_CURVED_TRICUBIC);

 

The .NET example code is included below:

 

// Parameters for new dataset

int thickness = 50;

float spacing = 0.25f;

int height = 50;

int numSlices = (int)(thickness / spacing);

 

MATRIX44D[][] arr2ProjPlanes = new MATRIX44D[slices][];

 

RENDER_PARAMS rp = new RENDER_PARAMS();

rp.Transform.setIdentity();

VECTOR3D vecZ = rp.Transform.getZVector();

vecZ.normalize();

 

if(vecZ.dot(new VECTOR3D(0.0, 0.0, 1.0)) > 0.0) {

   vecZ = vecZ.scale(-1.0);

}

 

// get the points ahead of time for the tangent calculation

VECTOR3D[] points = new VECTOR3D[m_points.length];

for(int i = 0; i < m_points.length; i++) {

   points[i] = m_points[i].vector3D;

}

 

for(int j = 0; j < arr2ProjPlanes.length; j++) {

   // for each slice

   arr2ProjPlanes[j] = new MATRIX44D[m_points.length];

 

   // now calculate the points for this slice                                                

   for (int i = 0; i < m_points.length; i++) {

      // for each point on the slice

      VECTOR3D tangent = hdrc3DHelpers.getLineTangent(points, i, i == arr2ProjPlanes[j].length - 1 ? i : i + 1, 0.0);

      VECTOR3D normal = tangent.cross(rp.Transform.getZVector());

      normal.normalize();

 

      double offset = ((double)j) - (((double)arr2ProjPlanes.length) / 2.0);

      VECTOR3D vecLoc = points[i].add(normal.scale(spacing * offset));

 

      arr2ProjPlanes[j][i] = new MATRIX44D(vecZ, new VECTOR3D(), new VECTOR3D(), vecLoc);

   }

}

 

double curvedLength = getCurvedLength(points);

 

int volWidth = (int)((curvedLength) / spacing);

int volHeight = (int)(height / spacing);

if (volWidth > 4096 || volHeight > 4096) {

   // "Volume width or height ends up being greater than 4096. This is currently unsupported.");

}

 

VOLUME_DATA_PARAMS oldVDP = m_vol.getVolumeDataParams(true);

 

hdrcVolumeDataContext vdc = m_crVolume.getCurvedReformat( m_points.length, arr2ProjPlanes.length, volWidth, volHeight,

   m_points.Count / 2, (float)(oldVDP.Spacing.y / spacing), arr2ProjPlanes, hdrcDefines.__Fields.CURVED_MPR_TYPE_X_IS_CURVED_TRICUBIC);