/// <summary> /// Creates a <see cref="VolumeSlice"/> representing the slice at the specified position in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="position">The position, in patient coordinates, of the slice to be created.</param> /// <returns>A <see cref="VolumeSlice"/> instance representing the requested slice.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="position"/> is null.</exception> public VolumeSlice CreateSlice(IVolumeReference volumeReference, Vector3D position) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(position, "position"); return(CreateSlicesCore(volumeReference, position, null, 1).Single()); }
public void Unload() { if (_volumeReference == null) { return; } if (_largeObjectContainerData.IsLocked) { return; } lock (_syncRoot) { if (_volumeReference == null) { return; } if (_largeObjectContainerData.IsLocked) { return; } Progress = 0; MemoryManager.Remove(this); _largeObjectContainerData.LargeObjectCount = 0; _largeObjectContainerData.BytesHeldCount = 0; // in general, this would be the only transient reference to the volume // we can't stop external code from calling CreateTransientReference() too // but if they did, the volume wouldn't really release here anyway, so that external code would still work _volumeReference.Dispose(); _volumeReference = null; } }
private static VolumeSlice CreateSlice(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, float thicknessAndSpacing, Vector3D throughPoint) { // compute Rows and Columns to reflect actual output size var frameSize = GetSliceExtent(volumeReference, slicerParams); // compute Pixel Spacing var effectiveSpacing = GetEffectiveSpacing(volumeReference); // compute Image Orientation (Patient) var matrix = new Matrix(slicerParams.SlicingPlaneRotation); matrix[3, 0] = throughPoint.X; matrix[3, 1] = throughPoint.Y; matrix[3, 2] = throughPoint.Z; var resliceAxesPatientOrientation = volumeReference.RotateToPatientOrientation(matrix); // compute Image Position (Patient) var topLeftOfSlicePatient = GetTopLeftOfSlicePatient(frameSize, throughPoint, volumeReference, slicerParams); var args = new VolumeSliceArgs(frameSize.Height, frameSize.Width, effectiveSpacing, effectiveSpacing, new Vector3D(resliceAxesPatientOrientation[0, 0], resliceAxesPatientOrientation[0, 1], resliceAxesPatientOrientation[0, 2]), new Vector3D(resliceAxesPatientOrientation[1, 0], resliceAxesPatientOrientation[1, 1], resliceAxesPatientOrientation[1, 2]), thicknessAndSpacing, Convert(slicerParams.InterpolationMode)); return(new VolumeSlice(volumeReference, true, args, topLeftOfSlicePatient, thicknessAndSpacing)); }
/// <summary> /// Called when all references to the cached item are destroyed, and thus all held source frames and volume can be released. /// </summary> private void Dispose() { MemoryManager.Remove(this); // this method is executed on a worker thread after no one else has a reference to this except maybe the memory manager // so we lock it so that we don't accidentally dispose the real volume simultaneously from different threads // blocking here isn't a big deal since we're also on a worker thread lock (_syncRoot) { if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } } if (_frames != null) { foreach (var frameReference in _frames) { frameReference.Dispose(); } _frames.Clear(); _frames = null; } }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing slices around a specified position in a <see cref="Volume"/>. /// </summary> /// <remarks> /// If extent of the slices is not fixed in any way (e.g. via specifying <see cref="Columns"/>, <see cref="SliceWidth"/>, etc.), /// then <paramref name="position"/> is effectively ignored and the slices will cover the entirety of the volume. /// </remarks> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="position">Reference position, in patient coordinates, around which slices will be created.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="position"/> is null.</exception> public IList <VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D position) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(position, "position"); return(CreateSlicesCore(volumeReference, position, null, 0).ToList()); }
/// <summary> /// Initializes the instance of <see cref="VolumeSlicerCore"/>. /// </summary> /// <param name="volumeReference">The <see cref="IVolumeReference"/> of the volume.</param> /// <param name="args">The requested slicer algorithm parameters.</param> /// <exception cref="ArgumentNullException">Thrown if either <paramref name="volumeReference"/> or <paramref name="args"/> are null.</exception> protected VolumeSlicerCore(IVolumeReference volumeReference, VolumeSliceArgs args) { Platform.CheckForNullReference(volumeReference, "volume"); Platform.CheckForNullReference(args, "args"); _volumeReference = volumeReference; _args = args; }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing a number of slices starting from the specified position in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="startPosition">Position, in patient coordinates, of the first slice.</param> /// <param name="count">The number of slices to be created.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="startPosition"/> is null.</exception> /// <exception cref="ArgumentException">Thrown if <paramref name="count"/> is less than or equal to zero.</exception> public IList <VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D startPosition, int count) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(startPosition, "startPosition"); Platform.CheckPositive(count, "count"); return(CreateSlicesCore(volumeReference, startPosition, null, count).ToList()); }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing slices between two positions in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="startPosition">Position, in patient coordinates, of the first slice.</param> /// <param name="endPosition">Position, in patient coordinates, of the last slice.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="startPosition"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="endPosition"/> is null.</exception> public IList <VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D startPosition, Vector3D endPosition) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(startPosition, "startPosition"); Platform.CheckForNullReference(endPosition, "endPosition"); return(CreateSlicesCore(volumeReference, startPosition, endPosition, 0).ToList()); }
internal VolumeSliceSopDataSource(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, IList <Vector3D> throughPoints) { Platform.CheckForNullReference(throughPoints, "throughPoints"); Platform.CheckTrue(throughPoints.Count > 0, "At least one through point must be specified."); var volume = volumeReference.Volume; _volumeReference = volumeReference; _slicerParams = slicerParams; _resliceMatrix = new Matrix(slicerParams.SlicingPlaneRotation); _resliceMatrix[3, 0] = throughPoints[0].X; _resliceMatrix[3, 1] = throughPoints[0].Y; _resliceMatrix[3, 2] = throughPoints[0].Z; _throughPoints = new List <Vector3D>(throughPoints).AsReadOnly(); // keep a direct reference to the prototype, so that attribute values are available even if the referenced volume is unloaded via memory management _prototypeDataSet = volume.DataSet; _instanceDataSet = new DicomAttributeCollection(); // JY: ideally, each slicing plane is represented by a single multiframe SOP where the individual slices are the frames. // We need to support multi-valued Slice Location in the base viewer first. // When that is implemented, the SOPs should be created on the first frame of the slicing (i.e. one of the end slices) // and the Slice Location Vector will simply store the slice locations relative to that defined in these attributes. // Also, the rows and columns will have to be computed to be the MAX possible size (all frames must have same size) // assign Rows and Columns to reflect actual output size Size frameSize = GetSliceExtent(volume, slicerParams); _instanceDataSet[DicomTags.Columns].SetInt32(0, frameSize.Width); _instanceDataSet[DicomTags.Rows].SetInt32(0, frameSize.Height); // assign Image Orientation (Patient) Matrix resliceAxesPatientOrientation = _volumeReference.Volume.RotateToPatientOrientation(_resliceMatrix); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(0, resliceAxesPatientOrientation[0, 0]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(1, resliceAxesPatientOrientation[0, 1]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(2, resliceAxesPatientOrientation[0, 2]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(3, resliceAxesPatientOrientation[1, 0]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(4, resliceAxesPatientOrientation[1, 1]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(5, resliceAxesPatientOrientation[1, 2]); // assign Image Position (Patient) Vector3D topLeftOfSlicePatient = GetTopLeftOfSlicePatient(frameSize, throughPoints[0], volume, slicerParams); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(0, topLeftOfSlicePatient.X); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(1, topLeftOfSlicePatient.Y); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(2, topLeftOfSlicePatient.Z); // assign Number of Frames _instanceDataSet[DicomTags.NumberOfFrames].SetInt32(0, throughPoints.Count); // assign a new SOP instance UID _instanceDataSet[DicomTags.SopInstanceUid].SetString(0, DicomUid.GenerateUid().UID); }
internal VolumeSlice(IVolumeReference volumeReference, VolumeSliceArgs sliceArgs, Vector3D imagePositionPatient, float?spacingBetweenSlices) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(sliceArgs, "slicerArgs"); Platform.CheckForNullReference(imagePositionPatient, "imagePositionPatient"); _volumeReference = volumeReference; _sliceArgs = sliceArgs; _imagePositionPatient = imagePositionPatient; // compute Rows and Columns to reflect actual output size Columns = sliceArgs.Columns; Rows = sliceArgs.Rows; // compute Slice Thickness var sliceThickness = sliceArgs.SliceThickness; SliceThickness = new DicomAttributeDS(DicomTags.SliceThickness) { Values = sliceThickness }.ToString(); SpacingBetweenSlices = spacingBetweenSlices.HasValue ? new DicomAttributeDS(DicomTags.SpacingBetweenSlices) { Values = spacingBetweenSlices.Value }.ToString() : string.Empty; // compute Pixel Spacing var spacing = new DicomAttributeDS(DicomTags.PixelSpacing); spacing.SetFloat32(0, sliceArgs.RowSpacing); spacing.SetFloat32(1, sliceArgs.ColumnSpacing); PixelSpacing = spacing.ToString(); // compute Image Orientation (Patient) var rowOrientation = sliceArgs.RowOrientationPatient; var columnOrientation = sliceArgs.ColumnOrientationPatient; var orientation = new DicomAttributeDS(DicomTags.ImageOrientationPatient); orientation.SetFloat32(0, rowOrientation.X); orientation.SetFloat32(1, rowOrientation.Y); orientation.SetFloat32(2, rowOrientation.Z); orientation.SetFloat32(3, columnOrientation.X); orientation.SetFloat32(4, columnOrientation.Y); orientation.SetFloat32(5, columnOrientation.Z); ImageOrientationPatient = orientation.ToString(); // compute Image Position (Patient) var position = new DicomAttributeDS(DicomTags.ImagePositionPatient); position.SetFloat32(0, _imagePositionPatient.X); position.SetFloat32(1, _imagePositionPatient.Y); position.SetFloat32(2, _imagePositionPatient.Z); ImagePositionPatient = position.ToString(); }
protected MprSliceSet(Volume volume) { Platform.CheckForNullReference(volume, "volume"); _volume = volume.CreateTransientReference(); _sliceSops = new ObservableDisposableList <MprSliceSop>(); _sliceSops.EnableEvents = true; _sliceSops.ItemAdded += OnItemAdded; _sliceSops.ItemChanged += OnItemChanged; _sliceSops.ItemChanging += OnItemChanging; _sliceSops.ItemRemoved += OnItemRemoved; }
protected MprSliceSet(Volumes.Volume volume) { Platform.CheckForNullReference(volume, "volume"); _volume = volume.CreateReference(); _sliceSops = new ObservableDisposableList<MprSliceSop>(); _sliceSops.EnableEvents = true; _sliceSops.ItemAdded += OnItemAdded; _sliceSops.ItemChanged += OnItemChanged; _sliceSops.ItemChanging += OnItemChanging; _sliceSops.ItemRemoved += OnItemRemoved; }
internal VolumeSlice(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, Vector3D throughPoint) { Platform.CheckForNullReference(throughPoint, "throughPoint"); var volume = volumeReference.Volume; _volumeReference = volumeReference; _slicerParams = slicerParams; _throughPoint = throughPoint; // keep a direct reference to the prototype, so that attribute values are available even if the referenced volume is unloaded via memory management _prototypeDataSet = volume.DataSet; // JY: ideally, each slicing plane is represented by a single multiframe SOP where the individual slices are the frames. // We need to support multi-valued Slice Location in the base viewer first. // When that is implemented, the SOPs should be created on the first frame of the slicing (i.e. one of the end slices) // and the Slice Location Vector will simply store the slice locations relative to that defined in these attributes. // Also, the rows and columns will have to be computed to be the MAX possible size (all frames must have same size) // compute Rows and Columns to reflect actual output size var frameSize = GetSliceExtent(volume, slicerParams); Colums = frameSize.Width; Rows = frameSize.Height; // compute Image Orientation (Patient) var matrix = new Matrix(slicerParams.SlicingPlaneRotation); matrix[3, 0] = _throughPoint.X; matrix[3, 1] = _throughPoint.Y; matrix[3, 2] = _throughPoint.Z; var resliceAxesPatientOrientation = _volumeReference.Volume.RotateToPatientOrientation(matrix); var orientation = new DicomAttributeDS(DicomTags.ImageOrientationPatient); orientation.SetFloat32(0, resliceAxesPatientOrientation[0, 0]); orientation.SetFloat32(1, resliceAxesPatientOrientation[0, 1]); orientation.SetFloat32(2, resliceAxesPatientOrientation[0, 2]); orientation.SetFloat32(3, resliceAxesPatientOrientation[1, 0]); orientation.SetFloat32(4, resliceAxesPatientOrientation[1, 1]); orientation.SetFloat32(5, resliceAxesPatientOrientation[1, 2]); ImageOrientationPatient = orientation.ToString(); // compute Image Position (Patient) var topLeftOfSlicePatient = GetTopLeftOfSlicePatient(frameSize, _throughPoint, volume, slicerParams); var position = new DicomAttributeDS(DicomTags.ImagePositionPatient); position.SetFloat32(0, topLeftOfSlicePatient.X); position.SetFloat32(1, topLeftOfSlicePatient.Y); position.SetFloat32(2, topLeftOfSlicePatient.Z); ImagePositionPatient = position.ToString(); }
internal VolumeSliceSopDataSource(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, IList<Vector3D> throughPoints) { Platform.CheckForNullReference(throughPoints, "throughPoints"); Platform.CheckTrue(throughPoints.Count > 0, "At least one through point must be specified."); var volume = volumeReference.Volume; _volumeReference = volumeReference; _slicerParams = slicerParams; _resliceMatrix = new Matrix(slicerParams.SlicingPlaneRotation); _resliceMatrix[3, 0] = throughPoints[0].X; _resliceMatrix[3, 1] = throughPoints[0].Y; _resliceMatrix[3, 2] = throughPoints[0].Z; _throughPoints = new List<Vector3D>(throughPoints).AsReadOnly(); // keep a direct reference to the prototype, so that attribute values are available even if the referenced volume is unloaded via memory management _prototypeDataSet = volume.DataSet; _instanceDataSet = new DicomAttributeCollection(); // JY: ideally, each slicing plane is represented by a single multiframe SOP where the individual slices are the frames. // We need to support multi-valued Slice Location in the base viewer first. // When that is implemented, the SOPs should be created on the first frame of the slicing (i.e. one of the end slices) // and the Slice Location Vector will simply store the slice locations relative to that defined in these attributes. // Also, the rows and columns will have to be computed to be the MAX possible size (all frames must have same size) // assign Rows and Columns to reflect actual output size Size frameSize = GetSliceExtent(volume, slicerParams); _instanceDataSet[DicomTags.Columns].SetInt32(0, frameSize.Width); _instanceDataSet[DicomTags.Rows].SetInt32(0, frameSize.Height); // assign Image Orientation (Patient) Matrix resliceAxesPatientOrientation = _volumeReference.Volume.RotateToPatientOrientation(_resliceMatrix); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(0, resliceAxesPatientOrientation[0, 0]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(1, resliceAxesPatientOrientation[0, 1]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(2, resliceAxesPatientOrientation[0, 2]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(3, resliceAxesPatientOrientation[1, 0]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(4, resliceAxesPatientOrientation[1, 1]); _instanceDataSet[DicomTags.ImageOrientationPatient].SetFloat32(5, resliceAxesPatientOrientation[1, 2]); // assign Image Position (Patient) Vector3D topLeftOfSlicePatient = GetTopLeftOfSlicePatient(frameSize, throughPoints[0], volume, slicerParams); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(0, topLeftOfSlicePatient.X); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(1, topLeftOfSlicePatient.Y); _instanceDataSet[DicomTags.ImagePositionPatient].SetFloat32(2, topLeftOfSlicePatient.Z); // assign Number of Frames _instanceDataSet[DicomTags.NumberOfFrames].SetInt32(0, throughPoints.Count); // assign a new SOP instance UID _instanceDataSet[DicomTags.SopInstanceUid].SetString(0, DicomUid.GenerateUid().UID); }
private Volume LoadCore(VolumeLoadProgressCallback callback) { // TODO (CR Apr 2013): Same comment as with Fusion - shouldn't actually need to lock for // the duration of volume creation. Should be enough to set a _loading flag, exit the lock, // then re-enter the lock to reset the _loading flag and set any necessary fields. // Locking for the duration means the memory manager could get hung up while trying to unload // a big volume that is in the process of being created. lock (_syncRoot) { if (_volumeReference != null) { return(_volumeReference.Volume); } Progress = 0; using (var volume = Volume.Create(_frames, (n, total) => { Progress = Math.Min(100f, 100f * n / total); if (callback != null) { callback.Invoke(this, n, total); } _largeObjectContainerData.UpdateLastAccessTime(); #if UNIT_TESTS if (ThrowAsyncVolumeLoadException) { ThrowAsyncVolumeLoadException = false; throw new CreateVolumeException("User manually triggered exception"); } #endif })) { _volumeReference = volume.CreateReference(); _largeObjectContainerData.LargeObjectCount = 1; _largeObjectContainerData.BytesHeldCount = 2 * volume.ArrayLength; _largeObjectContainerData.UpdateLastAccessTime(); MemoryManager.Add(this); } Progress = 100f; return(_volumeReference.Volume); } }
protected virtual void Dispose(bool disposing) { if (!disposing) { return; } if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } _sliceArgs = null; _imagePositionPatient = null; }
protected void Dispose(bool disposing) { if (!disposing) { return; } if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } _prototypeDataSet = null; _slicerParams = null; _throughPoint = null; }
/// <summary> /// Creates an implementation of <see cref="IVolumeSlicerCore"/> for slicing the requested volume. /// </summary> /// <param name="volumeReference">Reference to the <see cref="Volume"/> to be resliced.</param> /// <param name="args">The requested reslicing parameters.</param> public static IVolumeSlicerCore Create(IVolumeReference volumeReference, VolumeSliceArgs args) { if (_coreProviders == null) { try { _coreProviders = new VolumeSlicerCoreProviderExtensionPoint().CreateExtensions().Cast <IVolumeSlicerCoreProvider>().ToList(); } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "Failed to initialize implementations on VolumeSlicerCoreProviderExtensionPoint"); } } if (_coreProviders == null || !_coreProviders.Any()) { #if UNIT_TESTS // ensures the VTK version is loaded if possible TypeRef.Get("ClearCanvas.ImageViewer.Vtk.VtkVolumeSlicerCoreProvider, ClearCanvas.ImageViewer.VTK").Resolve(); // ensures that unit tests running under a custom extension factory will always have a slicer implementation where available _coreProviders = Platform.PluginManager.ExtensionPoints .First(e => e.ExtensionPointClass == typeof(VolumeSlicerCoreProviderExtensionPoint)) .ListExtensions().Select(x => Activator.CreateInstance(x.ExtensionClass.Resolve())) .Cast <IVolumeSlicerCoreProvider>().ToList(); if (_coreProviders == null || !_coreProviders.Any()) { throw new NotSupportedException("No implementations of IVolumeSlicerCoreProvider were found."); } #else throw new NotSupportedException("No implementations of IVolumeSlicerCoreProvider were found."); #endif } try { return(_coreProviders.First(c => c.IsSupported(args)).CreateSlicerCore(volumeReference, args)); } catch (Exception ex) { throw new NotSupportedException("No suitable implementation of IVolumeSlicerCoreProvider was found.", ex); } }
internal VolumeSlice(IVolumeReference volumeReference, VolumeSliceArgs sliceArgs, Vector3D imagePositionPatient, float? spacingBetweenSlices) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(sliceArgs, "slicerArgs"); Platform.CheckForNullReference(imagePositionPatient, "imagePositionPatient"); _volumeReference = volumeReference; _sliceArgs = sliceArgs; _imagePositionPatient = imagePositionPatient; // compute Rows and Columns to reflect actual output size Columns = sliceArgs.Columns; Rows = sliceArgs.Rows; // compute Slice Thickness var sliceThickness = sliceArgs.SliceThickness; SliceThickness = new DicomAttributeDS(DicomTags.SliceThickness) {Values = sliceThickness}.ToString(); SpacingBetweenSlices = spacingBetweenSlices.HasValue ? new DicomAttributeDS(DicomTags.SpacingBetweenSlices) {Values = spacingBetweenSlices.Value}.ToString() : string.Empty; // compute Pixel Spacing var spacing = new DicomAttributeDS(DicomTags.PixelSpacing); spacing.SetFloat32(0, sliceArgs.RowSpacing); spacing.SetFloat32(1, sliceArgs.ColumnSpacing); PixelSpacing = spacing.ToString(); // compute Image Orientation (Patient) var rowOrientation = sliceArgs.RowOrientationPatient; var columnOrientation = sliceArgs.ColumnOrientationPatient; var orientation = new DicomAttributeDS(DicomTags.ImageOrientationPatient); orientation.SetFloat32(0, rowOrientation.X); orientation.SetFloat32(1, rowOrientation.Y); orientation.SetFloat32(2, rowOrientation.Z); orientation.SetFloat32(3, columnOrientation.X); orientation.SetFloat32(4, columnOrientation.Y); orientation.SetFloat32(5, columnOrientation.Z); ImageOrientationPatient = orientation.ToString(); // compute Image Position (Patient) var position = new DicomAttributeDS(DicomTags.ImagePositionPatient); position.SetFloat32(0, _imagePositionPatient.X); position.SetFloat32(1, _imagePositionPatient.Y); position.SetFloat32(2, _imagePositionPatient.Z); ImagePositionPatient = position.ToString(); }
/// <summary> /// Called when all references to the cached item are destroyed, and thus all held source frames and volume can be released. /// </summary> private void Dispose() { MemoryManager.Remove(this); if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } if (_frames != null) { foreach (var frameReference in _frames) { frameReference.Dispose(); } _frames.Clear(); _frames = null; } }
protected virtual void Dispose(bool disposing) { if (disposing) { if (_sliceSops != null) { _sliceSops.ItemAdded -= OnItemAdded; _sliceSops.ItemChanged -= OnItemChanged; _sliceSops.ItemChanging -= OnItemChanging; _sliceSops.ItemRemoved -= OnItemRemoved; _sliceSops.Dispose(); _sliceSops = null; } if (_volume != null) { _volume.Dispose(); _volume = null; } } }
private Volume LoadCore(VolumeLoadProgressCallback callback) { if (_volumeReference != null) { return(_volumeReference.Volume); } lock (_syncRoot) { if (_volumeReference != null) { return(_volumeReference.Volume); } Progress = 0; using (var volume = Volume.Create(_frames, (n, total) => { Progress = Math.Min(100f, 100f * n / total); if (callback != null) { callback.Invoke(this, n, total); } })) { _volumeReference = volume.CreateTransientReference(); _largeObjectContainerData.LargeObjectCount = 1; _largeObjectContainerData.BytesHeldCount = 2 * volume.SizeInVoxels; _largeObjectContainerData.UpdateLastAccessTime(); MemoryManager.Add(this); } Progress = 100f; return(_volumeReference.Volume); } }
private static VolumeSlice CreateSlice(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, float thicknessAndSpacing, Vector3D throughPoint) { // compute Rows and Columns to reflect actual output size var frameSize = GetSliceExtent(volumeReference, slicerParams); // compute Pixel Spacing var effectiveSpacing = GetEffectiveSpacing(volumeReference); // compute Image Orientation (Patient) var matrix = new Matrix(slicerParams.SlicingPlaneRotation); matrix[3, 0] = throughPoint.X; matrix[3, 1] = throughPoint.Y; matrix[3, 2] = throughPoint.Z; var resliceAxesPatientOrientation = volumeReference.RotateToPatientOrientation(matrix); // compute Image Position (Patient) var topLeftOfSlicePatient = GetTopLeftOfSlicePatient(frameSize, throughPoint, volumeReference, slicerParams); var args = new VolumeSliceArgs(frameSize.Height, frameSize.Width, effectiveSpacing, effectiveSpacing, new Vector3D(resliceAxesPatientOrientation[0, 0], resliceAxesPatientOrientation[0, 1], resliceAxesPatientOrientation[0, 2]), new Vector3D(resliceAxesPatientOrientation[1, 0], resliceAxesPatientOrientation[1, 1], resliceAxesPatientOrientation[1, 2]), thicknessAndSpacing, Convert(slicerParams.InterpolationMode)); return new VolumeSlice(volumeReference, true, args, topLeftOfSlicePatient, thicknessAndSpacing); }
/// <summary> /// Creates an implementation of <see cref="IVolumeSlicerCore"/> for slicing the requested volume. /// </summary> /// <param name="volumeReference">Reference to the <see cref="Volume"/> to be resliced.</param> /// <param name="args">The requested reslicing parameters.</param> public static IVolumeSlicerCore Create(IVolumeReference volumeReference, VolumeSliceArgs args) { if (_coreProviders == null) { try { _coreProviders = new VolumeSlicerCoreProviderExtensionPoint().CreateExtensions().Cast<IVolumeSlicerCoreProvider>().ToList(); } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "Failed to initialize implementations on VolumeSlicerCoreProviderExtensionPoint"); } } if (_coreProviders == null || !_coreProviders.Any()) { #if UNIT_TESTS // ensures that unit tests running under a custom extension factory will always have a slicer implementation where available _coreProviders = Platform.PluginManager.ExtensionPoints .First(e => e.ExtensionPointClass == typeof (VolumeSlicerCoreProviderExtensionPoint)) .ListExtensions().Select(x => Activator.CreateInstance(x.ExtensionClass.Resolve())) .Cast<IVolumeSlicerCoreProvider>().ToList(); if (_coreProviders == null || !_coreProviders.Any()) throw new NotSupportedException("No implementations of IVolumeSlicerCoreProvider were found."); #else throw new NotSupportedException("No implementations of IVolumeSlicerCoreProvider were found."); #endif } try { return _coreProviders.First(c => c.IsSupported(args)).CreateSlicerCore(volumeReference, args); } catch (Exception ex) { throw new NotSupportedException("No suitable implementation of IVolumeSlicerCoreProvider was found.", ex); } }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing slices in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> public IList<VolumeSlice> CreateSlices(IVolumeReference volumeReference) { Platform.CheckForNullReference(volumeReference, "volumeReference"); return CreateSlicesCore(volumeReference, null, null, 0).ToList(); }
public IVolumeSlicerCore CreateSlicerCore(IVolumeReference volumeReference, VolumeSliceArgs args) { return new VtkVolumeSlicerCore(volumeReference, args); }
public VolumeSlicer(Volume volume, IVolumeSlicerParams slicerParams) { _volume = volume.CreateTransientReference(); _slicerParams = slicerParams; }
/// <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; } } }
public VolumeSlicer(Volumes.Volume volume, IVolumeSlicerParams slicerParams) { _volume = volume.CreateReference(); _slicerParams = slicerParams; }
/// <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); } } }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing slices around a specified position in a <see cref="Volume"/>. /// </summary> /// <remarks> /// If extent of the slices is not fixed in any way (e.g. via specifying <see cref="Columns"/>, <see cref="SliceWidth"/>, etc.), /// then <paramref name="position"/> is effectively ignored and the slices will cover the entirety of the volume. /// </remarks> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="position">Reference position, in patient coordinates, around which slices will be created.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="position"/> is null.</exception> public IList<VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D position) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(position, "position"); return CreateSlicesCore(volumeReference, position, null, 0).ToList(); }
protected void Dispose(bool disposing) { if (!disposing) return; if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } _prototypeDataSet = null; _slicerParams = null; _throughPoint = null; }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing a number of slices starting from the specified position in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="startPosition">Position, in patient coordinates, of the first slice.</param> /// <param name="count">The number of slices to be created.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="startPosition"/> is null.</exception> /// <exception cref="ArgumentException">Thrown if <paramref name="count"/> is less than or equal to zero.</exception> public IList<VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D startPosition, int count) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(startPosition, "startPosition"); Platform.CheckPositive(count, "count"); return CreateSlicesCore(volumeReference, startPosition, null, count).ToList(); }
/// <summary> /// Creates <see cref="VolumeSlice"/>s representing slices between two positions in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="startPosition">Position, in patient coordinates, of the first slice.</param> /// <param name="endPosition">Position, in patient coordinates, of the last slice.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="startPosition"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="endPosition"/> is null.</exception> public IList<VolumeSlice> CreateSlices(IVolumeReference volumeReference, Vector3D startPosition, Vector3D endPosition) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(startPosition, "startPosition"); Platform.CheckForNullReference(endPosition, "endPosition"); return CreateSlicesCore(volumeReference, startPosition, endPosition, 0).ToList(); }
public VolumeSlicer(Volume vol, IVolumeSlicerParams slicerParams, string seriesInstanceUid) { _volume = vol.CreateTransientReference(); _slicerParams = slicerParams; _seriesInstanceUid = seriesInstanceUid; }
public IVolumeSlicerCore CreateSlicerCore(IVolumeReference volumeReference, VolumeSliceArgs args) { return(new VtkVolumeSlicerCore(volumeReference, args)); }
public VolumeSlicer(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams, string seriesInstanceUid) { _volume = volumeReference.Clone(); _slicerParams = slicerParams; _seriesInstanceUid = seriesInstanceUid; }
public VolumeSlicer(IVolumeReference volumeReference, IVolumeSlicerParams slicerParams) { _volume = volumeReference.Clone(); _slicerParams = slicerParams; }
/// <summary> /// Creates a <see cref="VolumeSlice"/> representing the slice at the specified position in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <param name="position">The position, in patient coordinates, of the slice to be created.</param> /// <returns>A <see cref="VolumeSlice"/> instance representing the requested slice.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="position"/> is null.</exception> public VolumeSlice CreateSlice(IVolumeReference volumeReference, Vector3D position) { Platform.CheckForNullReference(volumeReference, "volumeReference"); Platform.CheckForNullReference(position, "position"); return CreateSlicesCore(volumeReference, position, null, 1).Single(); }
/// <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> /// Initializes a new instance of <see cref="VtkVolumeSlicerCore"/>. /// </summary> /// <param name="volumeReference"></param> /// <param name="args"></param> public VtkVolumeSlicerCore(IVolumeReference volumeReference, VolumeSliceArgs args) : base(volumeReference, args) {}
private IEnumerable <VolumeSlice> CreateSlicesCore(IVolumeReference volumeReference, Vector3D startPosition, Vector3D endPosition, int count) { // get the axes of the output plane and its normal in patient coordinates - these are the axes of the slicer frame var slicerAxisX = (RowOrientationPatient ?? volumeReference.VolumeOrientationPatientX).Normalize(); var slicerAxisY = (ColumnOrientationPatient ?? volumeReference.VolumeOrientationPatientY).Normalize(); var slicerAxisZ = (StackOrientationPatient ?? slicerAxisX.Cross(slicerAxisY)).Normalize(); // get the pixel spacing (defaults to isotropic spacing based on smallest volume spacing dimension) var pixelSpacing = GetPixelSpacing(volumeReference); // get the spacing between slices (defaults to smallest volume spacing dimension) var sliceSpacing = GetSliceSpacing(volumeReference, slicerAxisZ); // get the thickness of each slice (defaults to slice spacing) var sliceThickness = SliceThickness ?? sliceSpacing; // get the ideal subsampling for the slice thickness var sliceSubsamples = Math.Max(1, (int)(sliceThickness / GetIdealSliceSpacing(volumeReference, slicerAxisZ) + 0.5)); // project the corners of the volume on to the slicer axes to determine the bounds of the volume in the slicer frame float minBoundsX = float.MaxValue, minBoundsY = float.MaxValue, minBoundsZ = float.MaxValue; float maxBoundsX = float.MinValue, maxBoundsY = float.MinValue, maxBoundsZ = float.MinValue; var volumeDimensions = volumeReference.VolumeSize; foreach (var corner in new[] { new Vector3D(0, 0, 0), new Vector3D(0, 0, volumeDimensions.Z), new Vector3D(0, volumeDimensions.Y, 0), new Vector3D(0, volumeDimensions.Y, volumeDimensions.Z), new Vector3D(volumeDimensions.X, 0, 0), new Vector3D(volumeDimensions.X, 0, volumeDimensions.Z), new Vector3D(volumeDimensions.X, volumeDimensions.Y, 0), new Vector3D(volumeDimensions.X, volumeDimensions.Y, volumeDimensions.Z) }.Select(volumeReference.ConvertToPatient)) { var projection = corner.Dot(slicerAxisX); if (minBoundsX > projection) { minBoundsX = projection; } if (maxBoundsX < projection) { maxBoundsX = projection; } projection = corner.Dot(slicerAxisY); if (minBoundsY > projection) { minBoundsY = projection; } if (maxBoundsY < projection) { maxBoundsY = projection; } projection = corner.Dot(slicerAxisZ); if (minBoundsZ > projection) { minBoundsZ = projection; } if (maxBoundsZ < projection) { maxBoundsZ = projection; } } // get the origin of the slicer frame in patient coordinates var slicerOrigin = minBoundsX * slicerAxisX + minBoundsY * slicerAxisY + minBoundsZ * slicerAxisZ; // get the dimensions (in patient units) of the region that bounds the volume projected to the slicer frame - i.e. the dimensions of the stack of output slices var stackWidth = SliceWidth.HasValue ? SliceWidth.Value : maxBoundsX - minBoundsX; var stackHeight = SliceHeight.HasValue ? SliceHeight.Value : maxBoundsY - minBoundsY; var stackDepth = maxBoundsZ - minBoundsZ; // get the rows and columns of the slice output var sliceColumns = Columns ?? (int)(Math.Abs(1.0 * stackWidth / pixelSpacing.Width) + 0.5); var sliceRows = Rows ?? (int)(Math.Abs(1.0 * stackHeight / pixelSpacing.Height) + 0.5); // capture all the slicer parameters in an args object var args = new VolumeSliceArgs(sliceRows, sliceColumns, pixelSpacing.Height, pixelSpacing.Width, slicerAxisX, slicerAxisY, sliceThickness, sliceSubsamples, Interpolation, Projection); // compute the image position patient for each output slice and create the slices return(GetSlicePositions(slicerOrigin, slicerAxisX, slicerAxisY, slicerAxisZ, stackDepth, sliceRows, sliceColumns, pixelSpacing, sliceSpacing, sliceThickness, startPosition, endPosition, count) .Select(p => new VolumeSlice(volumeReference.Clone(), true, args, p, sliceSpacing))); }
private IEnumerable<VolumeSlice> CreateSlicesCore(IVolumeReference volumeReference, Vector3D startPosition, Vector3D endPosition, int count) { // get the axes of the output plane and its normal in patient coordinates - these are the axes of the slicer frame var slicerAxisX = (RowOrientationPatient ?? volumeReference.VolumeOrientationPatientX).Normalize(); var slicerAxisY = (ColumnOrientationPatient ?? volumeReference.VolumeOrientationPatientY).Normalize(); var slicerAxisZ = (StackOrientationPatient ?? slicerAxisX.Cross(slicerAxisY)).Normalize(); // get the pixel spacing (defaults to isotropic spacing based on smallest volume spacing dimension) var pixelSpacing = GetPixelSpacing(volumeReference); // get the spacing between slices (defaults to projecting voxel diagonal on to normal axis) var sliceSpacing = GetSliceSpacing(volumeReference, slicerAxisZ); // get the thickness of each slice (defaults to slice spacing) var sliceThickness = SliceThickness ?? sliceSpacing; // get the ideal subsampling for the slice thickness (if thickness exceeds threshold, create thin slices and aggregate them as a slab) var sliceSubsamples = Math.Max(1, (int) (sliceThickness/GetIdealSliceSpacing(volumeReference, slicerAxisZ) + 0.5)); if (sliceSubsamples > 1) sliceSubsamples *= 2; // if slabbing required, double the subsampling rate to achieve Nyquist sampling and ensure we don't miss any data // project the corners of the volume on to the slicer axes to determine the bounds of the volume in the slicer frame float minBoundsX = float.MaxValue, minBoundsY = float.MaxValue, minBoundsZ = float.MaxValue; float maxBoundsX = float.MinValue, maxBoundsY = float.MinValue, maxBoundsZ = float.MinValue; var volumeDimensions = volumeReference.VolumeSize; foreach (var corner in new[] { new Vector3D(0, 0, 0), new Vector3D(0, 0, volumeDimensions.Z), new Vector3D(0, volumeDimensions.Y, 0), new Vector3D(0, volumeDimensions.Y, volumeDimensions.Z), new Vector3D(volumeDimensions.X, 0, 0), new Vector3D(volumeDimensions.X, 0, volumeDimensions.Z), new Vector3D(volumeDimensions.X, volumeDimensions.Y, 0), new Vector3D(volumeDimensions.X, volumeDimensions.Y, volumeDimensions.Z) }.Select(volumeReference.ConvertToPatient)) { var projection = corner.Dot(slicerAxisX); if (minBoundsX > projection) minBoundsX = projection; if (maxBoundsX < projection) maxBoundsX = projection; projection = corner.Dot(slicerAxisY); if (minBoundsY > projection) minBoundsY = projection; if (maxBoundsY < projection) maxBoundsY = projection; projection = corner.Dot(slicerAxisZ); if (minBoundsZ > projection) minBoundsZ = projection; if (maxBoundsZ < projection) maxBoundsZ = projection; } // get the origin of the slicer frame in patient coordinates var slicerOrigin = minBoundsX*slicerAxisX + minBoundsY*slicerAxisY + minBoundsZ*slicerAxisZ; // get the dimensions (in patient units) of the region that bounds the volume projected to the slicer frame - i.e. the dimensions of the stack of output slices var stackWidth = SliceWidth.HasValue ? SliceWidth.Value : maxBoundsX - minBoundsX; var stackHeight = SliceHeight.HasValue ? SliceHeight.Value : maxBoundsY - minBoundsY; var stackDepth = maxBoundsZ - minBoundsZ; // get the rows and columns of the slice output var sliceColumns = Columns ?? (int) (Math.Abs(1.0*stackWidth/pixelSpacing.Width) + 0.5); var sliceRows = Rows ?? (int) (Math.Abs(1.0*stackHeight/pixelSpacing.Height) + 0.5); // capture all the slicer parameters in an args object var args = new VolumeSliceArgs(sliceRows, sliceColumns, pixelSpacing.Height, pixelSpacing.Width, slicerAxisX, slicerAxisY, sliceThickness, sliceSubsamples, Interpolation, Projection); // compute the image position patient for each output slice and create the slices var slicePositions = GetSlicePositions(slicerOrigin, slicerAxisX, slicerAxisY, slicerAxisZ, stackDepth, sliceRows, sliceColumns, pixelSpacing, sliceSpacing, sliceThickness, startPosition, endPosition, count).ToList(); return slicePositions.Select(p => new VolumeSlice(volumeReference.Clone(), true, args, p, slicePositions.Count > 1 ? (float?) sliceSpacing : null)); }
/// <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> /// Creates <see cref="VolumeSlice"/>s representing slices in a <see cref="Volume"/>. /// </summary> /// <param name="volumeReference">A <see cref="IVolumeReference"/> for the <see cref="Volume"/>.</param> /// <returns>A list of <see cref="VolumeSlice"/> instances representing the slices.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="volumeReference"/> is null.</exception> public IList <VolumeSlice> CreateSlices(IVolumeReference volumeReference) { Platform.CheckForNullReference(volumeReference, "volumeReference"); return(CreateSlicesCore(volumeReference, null, null, 0).ToList()); }
/// <summary> /// Initializes a new instance of <see cref="VtkVolumeSlicerCore"/>. /// </summary> /// <param name="volumeReference"></param> /// <param name="args"></param> public VtkVolumeSlicerCore(IVolumeReference volumeReference, VolumeSliceArgs args) : base(volumeReference, args) { }
public VolumeSlice(IVolumeReference volumeReference, bool ownReference, VolumeSliceArgs sliceArgs, Vector3D imagePositionPatient, float? spacingBetweenSlices = null) : this(ownReference ? volumeReference : volumeReference.Clone(), sliceArgs, imagePositionPatient, spacingBetweenSlices) {}
public VolumeSlice(IVolumeReference volumeReference, bool ownReference, VolumeSliceArgs sliceArgs, Vector3D imagePositionPatient, float?spacingBetweenSlices = null) : this(ownReference ? volumeReference : volumeReference.Clone(), sliceArgs, imagePositionPatient, spacingBetweenSlices) { }
protected virtual void Dispose(bool disposing) { if (!disposing) return; if (_volumeReference != null) { _volumeReference.Dispose(); _volumeReference = null; } _sliceArgs = null; _imagePositionPatient = null; }