/// <summary> /// Rotates volume along given axis. /// </summary> /// <param name="volume"></param> /// <param name="angles"></param> public static void RotateData(ref vtkImageData volume, double[] angles) { // Rotation along image center double[] center = volume.GetExtent().Divide(2); // Dimensions of rotated image int[] outExtent = volume.GetExtent().Multiply(1.1).Round().ToInt32(); // Rotation parameters var rotate = new vtkTransform(); rotate.Translate(center[1], center[3], center[5]); rotate.RotateX(angles[0]); rotate.RotateY(angles[1]); rotate.RotateZ(angles[2]); // z angle should be 0 rotate.Translate(-center[1], -center[3], -center[5]); // Perform rotation var slice = new vtkImageReslice(); slice.SetInput(volume); slice.SetResliceTransform(rotate); slice.SetInterpolationModeToCubic(); slice.SetOutputSpacing(volume.GetSpacing()[0], volume.GetSpacing()[1], volume.GetSpacing()[2]); slice.SetOutputOrigin(volume.GetOrigin()[0], volume.GetOrigin()[1], volume.GetOrigin()[2]); slice.SetOutputExtent(outExtent[0], outExtent[1], outExtent[2], outExtent[3], outExtent[4], outExtent[5]); }
private static void ResliceVolume(vtkImageData volume) { using (var reslicer = new vtkImageReslice()) { RegisterVtkErrorEvents(reslicer); reslicer.SetInput(volume); reslicer.SetInformationInput(volume); // Must instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(0); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(1.0, 1.0, 1.0); using (vtkMatrix4x4 resliceAxesMatrix = ConvertToVtkMatrix(new double[, ] { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } })) { reslicer.SetResliceAxes(resliceAxesMatrix); // Clamp the output based on the slice extent const int sliceExtentX = 50; const int sliceExtentY = 50; reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, 0); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 const float originX = -sliceExtentX * 1f / 2; const float originY = -sliceExtentY * 1f / 2; reslicer.SetOutputOrigin(originX, originY, 0); reslicer.SetInterpolationModeToLinear(); using (vtkExecutive exec = reslicer.GetExecutive()) { RegisterVtkErrorEvents(exec); exec.Update(); } using (var output = reslicer.GetOutput()) { // just to give it something to do with the output GC.KeepAlive(output); } } } }
/// <summary> /// Gets the pixel data representing a thick slice (a.k.a. slab) of the volume. /// </summary> private static byte[] GetSlabPixelData(IVolumeReference volumeReference, Matrix resliceAxes, Vector3D stackOrientation, int rows, int columns, int subsamples, float rowSpacing, float columnSpacing, float sliceThickness, VolumeInterpolationMode interpolation, VolumeProjectionMode projection) { if (subsamples == 0) { return(GetSlicePixelData(volumeReference, resliceAxes, rows, columns, rowSpacing, columnSpacing, interpolation)); } var subsampleSpacing = sliceThickness / (subsamples - 1); using (var reslicer = new vtkImageReslice()) using (var resliceAxesMatrix = new vtkMatrix4x4()) using (var executive = reslicer.GetExecutive()) using (var volume = volumeReference.Volume.CreateVtkVolumeHandle()) { // update the reslice axes matrix with the values from the slicing orientation resliceAxesMatrix.SetElements(resliceAxes); // determine offset for start of slab (we centre the slab on the requested slice position, as DICOM defines "image position (patient)" to be centre of the thick slice) var slabOffset = volumeReference.RotateToVolumeOrientation(-sliceThickness / 2f * stackOrientation) + new Vector3D(resliceAxes[0, 3], resliceAxes[1, 3], resliceAxes[2, 3]); resliceAxesMatrix.SetElement(0, 3, slabOffset.X); resliceAxesMatrix.SetElement(1, 3, slabOffset.Y); resliceAxesMatrix.SetElement(2, 3, slabOffset.Z); // register for errors on the reslicer reslicer.RegisterVtkErrorEvents(); // set input to the volume reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // instruct reslicer to output a 3D slab volume reslicer.SetOutputDimensionality(3); // use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(volumeReference.PaddingValue); // ensure the slicer outputs at our desired spacing reslicer.SetOutputSpacing(columnSpacing, rowSpacing, subsampleSpacing); // set the reslice axes reslicer.SetResliceAxes(resliceAxesMatrix); // clamp the output based on the slice extent reslicer.SetOutputExtent(0, columns - 1, 0, rows - 1, 0, subsamples - 1); // set the output origin to the slice through-point (output image will be centered on this location) // VTK output origin is derived from the center image being 0,0 reslicer.SetOutputOrigin(-columns * columnSpacing / 2, -rows * rowSpacing / 2, 0); // select the requested interpolation mode switch (interpolation) { case VolumeInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } // select the requested slab projection mode Action <IntPtr, byte[], int, int, int, bool> slabAggregator; switch (projection) { case VolumeProjectionMode.Maximum: slabAggregator = SlabProjection.AggregateSlabMaximumIntensity; break; case VolumeProjectionMode.Minimum: slabAggregator = SlabProjection.AggregateSlabMinimumIntensity; break; case VolumeProjectionMode.Average: default: slabAggregator = SlabProjection.AggregateSlabAverageIntensity; break; } // register for errors on the reslicer executive executive.RegisterVtkErrorEvents(); executive.Update(); // get the slice output using (var imageData = reslicer.GetOutput()) { var pixelData = SlabVtkImageData(imageData, slabAggregator, volumeReference.BitsPerVoxel, volumeReference.Signed); imageData.ReleaseData(); return(pixelData); } } }
/// <summary> /// Gets the pixel data representing a thin slice of the volume. /// </summary> private static byte[] GetSlicePixelData(IVolumeReference volumeReference, Matrix resliceAxes, int rows, int columns, float rowSpacing, float columnSpacing, VolumeInterpolationMode interpolation) { using (var reslicer = new vtkImageReslice()) using (var resliceAxesMatrix = new vtkMatrix4x4()) using (var executive = reslicer.GetExecutive()) using (var volume = volumeReference.Volume.CreateVtkVolumeHandle()) { // update the reslice axes matrix with the values from the slicing orientation resliceAxesMatrix.SetElements(resliceAxes); // register for errors on the reslicer reslicer.RegisterVtkErrorEvents(); // set input to the volume reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(volumeReference.PaddingValue); // ensure the slicer outputs at our desired spacing reslicer.SetOutputSpacing(columnSpacing, rowSpacing, Math.Min(columnSpacing, rowSpacing)); // set the reslice axes reslicer.SetResliceAxes(resliceAxesMatrix); // clamp the output based on the slice extent reslicer.SetOutputExtent(0, columns - 1, 0, rows - 1, 0, 0); // set the output origin to the slice through-point (output image will be centered on this location) // VTK output origin is derived from the center image being 0,0 reslicer.SetOutputOrigin(-columns * columnSpacing / 2, -rows * rowSpacing / 2, 0); // select the requested interpolation mode switch (interpolation) { case VolumeInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } // register for errors on the reslicer executive executive.RegisterVtkErrorEvents(); executive.Update(); // get the slice output using (var imageData = reslicer.GetOutput()) { var pixelData = ReadVtkImageData(imageData); imageData.ReleaseData(); return(pixelData); } } }
// Extract slab in specified orientation, if slabThickness is 1, this is identical // to GenerateVtkSlice above, so they should be collapsed at some point. // TODO: Tie into Dicom for slice, will need to adjust thickness at least private vtkImageData GenerateVtkSlab(Matrix resliceAxes, int slabThicknessInVoxels) { VtkHelper.StaticInitializationHack(); // Thickness should be at least 1 if (slabThicknessInVoxels < 1) slabThicknessInVoxels = 1; using (vtkImageReslice reslicer = new vtkImageReslice()) { VtkHelper.RegisterVtkErrorEvents(reslicer); // Obtain a pinned VTK volume for the reslicer. We'll release this when // VTK is done reslicing. vtkImageData volumeVtkWrapper = _volume.ObtainPinnedVtkVolume(); reslicer.SetInput(volumeVtkWrapper); reslicer.SetInformationInput(volumeVtkWrapper); if (slabThicknessInVoxels > 1) reslicer.SetOutputDimensionality(3); else reslicer.SetOutputDimensionality(3); // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(_volume.PadValue); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(_volume.Spacing.X, _volume.Spacing.Y, _volume.Spacing.Z); reslicer.SetResliceAxes(VtkHelper.ConvertToVtkMatrix(resliceAxes)); // Clamp the output based on the slice extent int sliceExtentX = GetSliceExtentX(); int sliceExtentY = GetSliceExtentY(); reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, slabThicknessInVoxels-1); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 float originX = -sliceExtentX * EffectiveSpacing / 2; float originY = -sliceExtentY * EffectiveSpacing / 2; reslicer.SetOutputOrigin(originX, originY, 0); switch (_slicing.InterpolationMode) { case InterpolationModes.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case InterpolationModes.Linear: reslicer.SetInterpolationModeToLinear(); break; case InterpolationModes.Cubic: reslicer.SetInterpolationModeToCubic(); break; } using (vtkExecutive exec = reslicer.GetExecutive()) { VtkHelper.RegisterVtkErrorEvents(exec); exec.Update(); _volume.ReleasePinnedVtkVolume(); return reslicer.GetOutput(); } } }
// Extract slice in specified orientation private vtkImageData GenerateVtkSlice(Matrix resliceAxes) { using (vtkImageReslice reslicer = new vtkImageReslice()) { VtkHelper.RegisterVtkErrorEvents(reslicer); // Obtain a pinned VTK volume for the reslicer. We'll release this when // VTK is done reslicing. using (var volume = _volume.Volume.CreateVtkVolumeHandle()) { reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // Must instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(_volume.Volume.PaddingValue); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(_volume.Volume.VoxelSpacing.X, _volume.Volume.VoxelSpacing.Y, _volume.Volume.VoxelSpacing.Z); using (vtkMatrix4x4 resliceAxesMatrix = VtkHelper.ConvertToVtkMatrix(resliceAxes)) { reslicer.SetResliceAxes(resliceAxesMatrix); // Clamp the output based on the slice extent int sliceExtentX = GetSliceExtentX(); int sliceExtentY = GetSliceExtentY(); reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, 0); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 float originX = -sliceExtentX*EffectiveSpacing/2; float originY = -sliceExtentY*EffectiveSpacing/2; reslicer.SetOutputOrigin(originX, originY, 0); switch (_slicerParams.InterpolationMode) { case VolumeSlicerInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeSlicerInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeSlicerInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } using (vtkExecutive exec = reslicer.GetExecutive()) { VtkHelper.RegisterVtkErrorEvents(exec); exec.Update(); } var output = reslicer.GetOutput(); //Just in case VTK uses the matrix internally. return output; } } } }
/// <summary> /// Gets the pixel data representing a thick slice (a.k.a. slab) of the volume. /// </summary> private static byte[] GetSlabPixelData(IVolumeReference volumeReference, Matrix resliceAxes, Vector3D stackOrientation, int rows, int columns, int subsamples, float rowSpacing, float columnSpacing, float sliceThickness, VolumeInterpolationMode interpolation, VolumeProjectionMode projection) { if (subsamples <= 1) return GetSlicePixelData(volumeReference, resliceAxes, rows, columns, rowSpacing, columnSpacing, interpolation); var subsampleSpacing = sliceThickness/(subsamples - 1); using (var reslicer = new vtkImageReslice()) using (var resliceAxesMatrix = new vtkMatrix4x4()) using (var executive = reslicer.GetExecutive()) using (var volume = volumeReference.Volume.CreateVtkVolumeHandle()) { // update the reslice axes matrix with the values from the slicing orientation resliceAxesMatrix.SetElements(resliceAxes); // determine offset for start of slab (we centre the slab on the requested slice position, as DICOM defines "image position (patient)" to be centre of the thick slice) var slabOffset = volumeReference.RotateToVolumeOrientation(-sliceThickness/2f*stackOrientation) + new Vector3D(resliceAxes[0, 3], resliceAxes[1, 3], resliceAxes[2, 3]); resliceAxesMatrix.SetElement(0, 3, slabOffset.X); resliceAxesMatrix.SetElement(1, 3, slabOffset.Y); resliceAxesMatrix.SetElement(2, 3, slabOffset.Z); // register for errors on the reslicer reslicer.RegisterVtkErrorEvents(); // set input to the volume reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // instruct reslicer to output a 3D slab volume reslicer.SetOutputDimensionality(3); // use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(volumeReference.PaddingValue); // ensure the slicer outputs at our desired spacing reslicer.SetOutputSpacing(columnSpacing, rowSpacing, subsampleSpacing); // set the reslice axes reslicer.SetResliceAxes(resliceAxesMatrix); // clamp the output based on the slice extent reslicer.SetOutputExtent(0, columns - 1, 0, rows - 1, 0, subsamples - 1); // set the output origin to the slice through-point (output image will be centered on this location) // VTK output origin is derived from the center image being 0,0 reslicer.SetOutputOrigin(-columns*columnSpacing/2, -rows*rowSpacing/2, 0); // select the requested interpolation mode switch (interpolation) { case VolumeInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } // select the requested slab projection mode Action<IntPtr, byte[], int, int, int, bool> slabAggregator; switch (projection) { case VolumeProjectionMode.Maximum: slabAggregator = SlabProjection.AggregateSlabMaximumIntensity; break; case VolumeProjectionMode.Minimum: slabAggregator = SlabProjection.AggregateSlabMinimumIntensity; break; case VolumeProjectionMode.Average: default: slabAggregator = SlabProjection.AggregateSlabAverageIntensity; break; } // register for errors on the reslicer executive executive.RegisterVtkErrorEvents(); executive.Update(); // get the slice output using (var imageData = reslicer.GetOutput()) { var pixelData = SlabVtkImageData(imageData, slabAggregator, volumeReference.BitsPerVoxel, volumeReference.Signed); imageData.ReleaseData(); return pixelData; } } }
/// <summary> /// Gets the pixel data representing a thin slice of the volume. /// </summary> private static byte[] GetSlicePixelData(IVolumeReference volumeReference, Matrix resliceAxes, int rows, int columns, float rowSpacing, float columnSpacing, VolumeInterpolationMode interpolation) { using (var reslicer = new vtkImageReslice()) using (var resliceAxesMatrix = new vtkMatrix4x4()) using (var executive = reslicer.GetExecutive()) using (var volume = volumeReference.Volume.CreateVtkVolumeHandle()) { // update the reslice axes matrix with the values from the slicing orientation resliceAxesMatrix.SetElements(resliceAxes); // register for errors on the reslicer reslicer.RegisterVtkErrorEvents(); // set input to the volume reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(volumeReference.PaddingValue); // ensure the slicer outputs at our desired spacing reslicer.SetOutputSpacing(columnSpacing, rowSpacing, Math.Min(columnSpacing, rowSpacing)); // set the reslice axes reslicer.SetResliceAxes(resliceAxesMatrix); // clamp the output based on the slice extent reslicer.SetOutputExtent(0, columns - 1, 0, rows - 1, 0, 0); // set the output origin to the slice through-point (output image will be centered on this location) // VTK output origin is derived from the center image being 0,0 reslicer.SetOutputOrigin(-columns*columnSpacing/2, -rows*rowSpacing/2, 0); // select the requested interpolation mode switch (interpolation) { case VolumeInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } // register for errors on the reslicer executive executive.RegisterVtkErrorEvents(); executive.Update(); // get the slice output using (var imageData = reslicer.GetOutput()) { var pixelData = ReadVtkImageData(imageData); imageData.ReleaseData(); return pixelData; } } }
private static void ResliceVolume(vtkImageData volume) { using (var reslicer = new vtkImageReslice()) { RegisterVtkErrorEvents(reslicer); reslicer.SetInput(volume); reslicer.SetInformationInput(volume); // Must instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(0); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(1.0, 1.0, 1.0); using (vtkMatrix4x4 resliceAxesMatrix = ConvertToVtkMatrix(new double[,] {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}})) { reslicer.SetResliceAxes(resliceAxesMatrix); // Clamp the output based on the slice extent const int sliceExtentX = 50; const int sliceExtentY = 50; reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, 0); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 const float originX = -sliceExtentX*1f/2; const float originY = -sliceExtentY*1f/2; reslicer.SetOutputOrigin(originX, originY, 0); reslicer.SetInterpolationModeToLinear(); using (vtkExecutive exec = reslicer.GetExecutive()) { RegisterVtkErrorEvents(exec); exec.Update(); } using (var output = reslicer.GetOutput()) { // just to give it something to do with the output GC.KeepAlive(output); } } } }
// Extract slab in specified orientation, if slabThickness is 1, this is identical // to GenerateVtkSlice above, so they should be collapsed at some point. // TODO: Tie into Dicom for slice, will need to adjust thickness at least private vtkImageData GenerateVtkSlab(Matrix resliceAxes, int slabThicknessInVoxels) { VtkHelper.StaticInitializationHack(); // Thickness should be at least 1 if (slabThicknessInVoxels < 1) { slabThicknessInVoxels = 1; } using (vtkImageReslice reslicer = new vtkImageReslice()) { VtkHelper.RegisterVtkErrorEvents(reslicer); // Obtain a pinned VTK volume for the reslicer. We'll release this when // VTK is done reslicing. vtkImageData volumeVtkWrapper = _volume.ObtainPinnedVtkVolume(); reslicer.SetInput(volumeVtkWrapper); reslicer.SetInformationInput(volumeVtkWrapper); if (slabThicknessInVoxels > 1) { reslicer.SetOutputDimensionality(3); } else { reslicer.SetOutputDimensionality(3); } // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(_volume.PadValue); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(_volume.Spacing.X, _volume.Spacing.Y, _volume.Spacing.Z); reslicer.SetResliceAxes(VtkHelper.ConvertToVtkMatrix(resliceAxes)); // Clamp the output based on the slice extent int sliceExtentX = GetSliceExtentX(); int sliceExtentY = GetSliceExtentY(); reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, slabThicknessInVoxels - 1); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 float originX = -sliceExtentX * EffectiveSpacing / 2; float originY = -sliceExtentY * EffectiveSpacing / 2; reslicer.SetOutputOrigin(originX, originY, 0); switch (_slicing.InterpolationMode) { case InterpolationModes.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case InterpolationModes.Linear: reslicer.SetInterpolationModeToLinear(); break; case InterpolationModes.Cubic: reslicer.SetInterpolationModeToCubic(); break; } using (vtkExecutive exec = reslicer.GetExecutive()) { VtkHelper.RegisterVtkErrorEvents(exec); exec.Update(); _volume.ReleasePinnedVtkVolume(); return(reslicer.GetOutput()); } } }
// Extract slice in specified orientation private vtkImageData GenerateVtkSlice(Matrix resliceAxes) { using (vtkImageReslice reslicer = new vtkImageReslice()) { VtkHelper.RegisterVtkErrorEvents(reslicer); // Obtain a pinned VTK volume for the reslicer. We'll release this when // VTK is done reslicing. using (var volume = _volume.Volume.CreateVtkVolumeHandle()) { reslicer.SetInput(volume.vtkImageData); reslicer.SetInformationInput(volume.vtkImageData); // Must instruct reslicer to output 2D images reslicer.SetOutputDimensionality(2); // Use the volume's padding value for all pixels that are outside the volume reslicer.SetBackgroundLevel(_volume.Volume.PaddingValue); // This ensures VTK obeys the real spacing, results in all VTK slices being isotropic. // Effective spacing is the minimum of these three. reslicer.SetOutputSpacing(_volume.Volume.VoxelSpacing.X, _volume.Volume.VoxelSpacing.Y, _volume.Volume.VoxelSpacing.Z); using (vtkMatrix4x4 resliceAxesMatrix = VtkHelper.ConvertToVtkMatrix(resliceAxes)) { reslicer.SetResliceAxes(resliceAxesMatrix); // Clamp the output based on the slice extent int sliceExtentX = GetSliceExtentX(); int sliceExtentY = GetSliceExtentY(); reslicer.SetOutputExtent(0, sliceExtentX - 1, 0, sliceExtentY - 1, 0, 0); // Set the output origin to reflect the slice through point. The slice extent is // centered on the slice through point. // VTK output origin is derived from the center image being 0,0 float originX = -sliceExtentX * EffectiveSpacing / 2; float originY = -sliceExtentY * EffectiveSpacing / 2; reslicer.SetOutputOrigin(originX, originY, 0); switch (_slicerParams.InterpolationMode) { case VolumeSlicerInterpolationMode.NearestNeighbor: reslicer.SetInterpolationModeToNearestNeighbor(); break; case VolumeSlicerInterpolationMode.Linear: reslicer.SetInterpolationModeToLinear(); break; case VolumeSlicerInterpolationMode.Cubic: reslicer.SetInterpolationModeToCubic(); break; } using (vtkExecutive exec = reslicer.GetExecutive()) { VtkHelper.RegisterVtkErrorEvents(exec); exec.Update(); } var output = reslicer.GetOutput(); //Just in case VTK uses the matrix internally. return(output); } } } }
/// <summary> /// Rotates 3D vtk volume around x and y axes. /// </summary> /// <param name="input"></param> /// <param name="angle"></param> /// <param name="axis"></param> /// <param name="out_extent"></param> /// <returns></returns> public static vtkImageData rotate_sample(vtkImageData input, double angle, int axis, int out_extent = 0) { //get input data dimensions int[] dims = input.GetExtent(); //Compute centers int[] centers = new int[] { (dims[1] + dims[0]) / 2, (dims[3] + dims[2]) / 2, (dims[5] + dims[4]) / 2 }; //Set rotation axis int[] axes = new int[3]; axes[axis] = 1; int[] new_dims = new int[] { dims[0], dims[1], dims[2], dims[3], dims[4], dims[5] }; int[] new_centers = new int[] { centers[0], centers[1], centers[2] }; //Compute new sample dimensions if (axis == 0) { new_dims[3] = (int)(Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[3] + Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_dims[5] = (int)(Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[3] + Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_centers[1] = (Math.Abs(new_dims[3]) + Math.Abs(new_dims[2])) / 2; new_centers[2] = (Math.Abs(new_dims[5]) + Math.Abs(new_dims[4])) / 2; } if (axis == 1) { new_dims[1] = (int)(Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[1] + Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_dims[5] = (int)(Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[1] + Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_centers[0] = (Math.Abs(new_dims[0]) + Math.Abs(new_dims[1])) / 2; new_centers[2] = (Math.Abs(new_dims[5]) + Math.Abs(new_dims[4])) / 2; } //Image transformation vtkTransform transform = vtkTransform.New(); transform.Translate(centers[0], centers[1], centers[2]); transform.RotateWXYZ(angle, axes[0], axes[1], axes[2]); if (out_extent == 0) { transform.Translate(-centers[0], -centers[1], -centers[2]); } else { transform.Translate(-new_centers[0], -new_centers[1], -new_centers[2]); } //Console.ReadKey(); transform.Update(); //Compute new data extent int[] diff = new int[] { new_dims[1] - dims[1], new_dims[3] - dims[3], new_dims[5] - dims[5] }; new_dims[0] += diff[0] / 2; new_dims[1] -= diff[0] / 2; new_dims[2] += diff[1] / 2; new_dims[3] -= diff[1] / 2; new_dims[4] += diff[2] / 2; new_dims[5] -= diff[2] / 2; //Image reslicing vtkImageReslice rotater = vtkImageReslice.New(); rotater.SetInput(input); rotater.SetInformationInput(input); rotater.SetResliceTransform(transform); rotater.SetInterpolationModeToCubic(); //rotater.SetInterpolationModeToLinear(); if (out_extent == 1) { rotater.SetOutputSpacing(input.GetSpacing()[0], input.GetSpacing()[1], input.GetSpacing()[2]); rotater.SetOutputOrigin(input.GetOrigin()[0], input.GetOrigin()[1], input.GetOrigin()[2]); rotater.SetOutputExtent(new_dims[0], new_dims[1], new_dims[2], new_dims[3], new_dims[4], new_dims[5]); } rotater.Update(); vtkImageData output = vtkImageData.New(); output.DeepCopy(rotater.GetOutput()); rotater.Dispose(); transform.Dispose(); return(output); }