private static byte[] MipPixelDataFromVtkSlab(vtkImageData slabImageData) { #if true // Do our own MIP, albeit slowly int[] sliceDimensions = slabImageData.GetDimensions(); int sliceDataSize = sliceDimensions[0] * sliceDimensions[1]; IntPtr slabDataPtr = slabImageData.GetScalarPointer(); byte[] pixelData = MemoryManager.Allocate <byte>(sliceDataSize * sizeof(short)); // Init with first slice Marshal.Copy(slabDataPtr, pixelData, 0, sliceDataSize * sizeof(short)); // Walk through other slices, finding maximum unsafe { short *psSlab = (short *)slabDataPtr; fixed(byte *pbFrame = pixelData) { short *psFrame = (short *)pbFrame; for (int sliceIndex = 1; sliceIndex < sliceDimensions[2]; sliceIndex++) { for (int i = 0; i < sliceDataSize - 1; ++i) { int slabIndex = sliceIndex * sliceDataSize + i; if (psSlab[slabIndex] > psFrame[i]) { psFrame[i] = psSlab[slabIndex]; } } } } } return(pixelData); #else // Ideally we'd use VTK to do the MIP (MinIP, Average...) vtkVolumeRayCastMIPFunction mip = new vtkVolumeRayCastMIPFunction(); vtkVolumeRayCastMapper mapper = new vtkVolumeRayCastMapper(); mapper.SetVolumeRayCastFunction(mip); mapper.SetInput(slabImageData); //TODO: Need to figure out how to use mapper to output vtkImageData vtkImageAlgorithm algo = new vtkImageAlgorithm(); algo.SetInput(mapper.GetOutputDataObject(0)); using (vtkExecutive exec = mapper.GetExecutive()) { VtkHelper.RegisterVtkErrorEvents(exec); exec.Update(); // Note: These report no output port, must have to do something else to get mapper to give us data //return exec.GetOutputData(0); return(mapper.GetOutputDataObject(0)); } #endif }
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); } } } }