public void Dispose() { if (_volume != null) { _volume.OnReferenceDisposed(); _volume = null; } }
public MprStandardSliceSet(Volume volume, IVolumeSlicerParams slicerParams) : base(volume) { Platform.CheckForNullReference(slicerParams, "slicerParams"); _slicerParams = slicerParams; base.Description = slicerParams.Description; this.Reslice(); }
//TODO (CR Sept 2010): same comment as with the ProgressGraphic stuff; the API is unclear //as to what it is doing (return value) because it's trying to account for the async loading. /// <summary> /// Attempts to start loading the overlay data asynchronously, if not already loaded. /// </summary> /// <param name="progress">A value between 0 and 1 indicating the progress of the asynchronous loading operation.</param> /// <param name="message">A string message detailing the progress of the asynchronous loading operation.</param> /// <returns></returns> public bool BeginLoad(out float progress, out string message) { // update the last access time _largeObjectData.UpdateLastAccessTime(); // if the data is already available without blocking, return success immediately //TODO (CR Sept 2010): because unloading the volume involves disposing it, if every operation that uses it //isn't in the same lock (e.g. lock(_syncVolumeDataLock)) you could be getting a disposed/null volume here. VolumeData volume = _volume; if (volume != null) { message = SR.MessageFusionComplete; progress = 1f; return(true); } lock (_syncLoaderLock) { message = SR.MessageFusionInProgress; progress = 0; if (_volumeLoaderTask == null) { // if the data is available now, return success volume = _volume; if (volume != null) { message = SR.MessageFusionComplete; progress = 1f; return(true); } _volumeLoaderTask = new BackgroundTask(c => this.LoadVolume(c), false, null) { ThreadUICulture = Application.CurrentUICulture }; _volumeLoaderTask.Run(); _volumeLoaderTask.Terminated += OnVolumeLoaderTaskTerminated; } else { // TODO (CR Apr 2013): See comment in OnVolumeLoaderTaskTerminated; if the task were not created // on the UI thread, _volumeLoaderTask could be set to null before hitting this instruction. if (_volumeLoaderTask.LastBackgroundTaskProgress != null) { message = _volumeLoaderTask.LastBackgroundTaskProgress.Progress.Message; progress = _volumeLoaderTask.LastBackgroundTaskProgress.Progress.Percent / 100f; } } } return(false); }
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; }
private static IEnumerable<IMprSliceSet> CreateDefaultSliceSets(Volume volume) { // The default slice sets consist of a fixed view of the original image plane, // and three mutable slice sets showing the other two planes perpendicular to the original // plus one oblique slice set halfway in between these two perpendicular planes. if (volume != null) { yield return MprStaticSliceSet.CreateIdentitySliceSet(volume); yield return new MprStandardSliceSet(volume, VolumeSlicerParams.OrthogonalX); yield return new MprStandardSliceSet(volume, new VolumeSlicerParams(90, 0, 270)); yield return new MprStandardSliceSet(volume, new VolumeSlicerParams(90, 0, 315)); } }
private VolumeData LoadVolume(IBackgroundTaskContext context) { // TODO (CR Apr 2013): Ideally, loading and unloading could be done with minimal locking; this way, // Unload actually has to wait for Load to finish and vice versa. I think a quicker way would be to // have a _loading field - have this method set it inside the lock, then proceed to do the load, // then set the _volume field when done. Have Unload check _loading and just return, otherwise set _volume to null. // Basically, you don't need to lock the entire load operation - you only need to guarantee that multiple loads // can't occur at once, and that Unload actually unloads it. // wait for synchronized access lock (_syncVolumeDataLock) { _largeObjectData.Lock(); try { // if the data is now available, return it immediately // (i.e. we were blocked because we were already reading the data) if (_volume != null) { return(_volume); } // load the volume data if (context == null) { _volume = VolumeData.Create(_frames); } else { _volume = VolumeData.Create(_frames, (n, count) => context.ReportProgress(new BackgroundTaskProgress(n, count, SR.MessageFusionInProgress))); } // update our stats _largeObjectData.BytesHeldCount = 2 * _volume.SizeInVoxels; _largeObjectData.LargeObjectCount = 1; _largeObjectData.UpdateLastAccessTime(); // regenerating the volume data takes a few seconds _largeObjectData.RegenerationCost = LargeObjectContainerData.PresetComputedData; // register with memory manager MemoryManager.Add(this); return(_volume); } finally { _largeObjectData.Unlock(); } } }
public VolumeSliceSopDataSource(Volume volume, IVolumeSlicerParams slicerParams, IList<Vector3D> throughPoints) { Platform.CheckForNullReference(throughPoints, "throughPoints"); Platform.CheckTrue(throughPoints.Count > 0, "At least one through point must be specified."); _volumeReference = volume.CreateTransientReference(); _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(); _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 void UnloadVolume() { // wait for synchronized access lock (_syncVolumeDataLock) { // dump our data if (_volume != null) { _volume.Dispose(); _volume = null; } // update our stats _largeObjectData.BytesHeldCount = 0; _largeObjectData.LargeObjectCount = 0; // unregister with memory manager MemoryManager.Remove(this); } this.OnUnloaded(); }
private VolumeData LoadVolume(IBackgroundTaskContext context) { // wait for synchronized access lock (_syncVolumeDataLock) { // if the data is now available, return it immediately // (i.e. we were blocked because we were already reading the data) if (_volume != null) { return(_volume); } // load the volume data if (context == null) { _volume = VolumeData.Create(_frames); } else { _volume = VolumeData.Create(_frames, (n, count) => context.ReportProgress(new BackgroundTaskProgress(n, count, SR.MessageFusionInProgress))); } // update our stats _largeObjectData.BytesHeldCount = 2 * _volume.SizeInVoxels; _largeObjectData.LargeObjectCount = 1; _largeObjectData.UpdateLastAccessTime(); // regenerating the volume data is easy when the source frames are already in memory! _largeObjectData.RegenerationCost = RegenerationCost.Low; // register with memory manager MemoryManager.Add(this); return(_volume); } }
public MprVolume(Volume volume, IEnumerable<IMprSliceSet> sliceSets) { Platform.CheckForNullReference(volume, "volume"); // MprVolume is the de jure owner of the Volume // Everything else (like the SOPs) just hold transient references _volume = volume; _sliceSets = new ObservableDisposableList<IMprSliceSet>(); if (sliceSets != null) { foreach (IMprSliceSet sliceSet in sliceSets) { if (sliceSet is IInternalMprSliceSet) ((IInternalMprSliceSet) sliceSet).Parent = this; _sliceSets.Add(sliceSet); } } _sliceSets.EnableEvents = true; _sliceSets.ItemAdded += OnItemAdded; _sliceSets.ItemChanged += OnItemAdded; _sliceSets.ItemChanging += OnItemRemoved; _sliceSets.ItemRemoved += OnItemRemoved; }
private VolumeData LoadVolume(IBackgroundTaskContext context) { // wait for synchronized access lock (_syncVolumeDataLock) { // if the data is now available, return it immediately // (i.e. we were blocked because we were already reading the data) if (_volume != null) return _volume; // load the volume data if (context == null) _volume = VolumeData.Create(_frames); else _volume = VolumeData.Create(_frames, (n, count) => context.ReportProgress(new BackgroundTaskProgress(n, count, SR.MessageFusionInProgress))); // update our stats _largeObjectData.BytesHeldCount = 2*_volume.SizeInVoxels; _largeObjectData.LargeObjectCount = 1; _largeObjectData.UpdateLastAccessTime(); // regenerating the volume data is easy when the source frames are already in memory! _largeObjectData.RegenerationCost = RegenerationCost.Low; // register with memory manager MemoryManager.Add(this); return _volume; } }
public VolumeSlicer(Volume volume, IVolumeSlicerParams slicerParams) { _volume = volume.CreateTransientReference(); _slicerParams = slicerParams; }
public VolumeSlicer(Volume vol, IVolumeSlicerParams slicerParams, string seriesInstanceUid) { _volume = vol.CreateTransientReference(); _slicerParams = slicerParams; _seriesInstanceUid = seriesInstanceUid; }
/// <summary> /// Creates and populates a <see cref="Volume"/> from the builder's source frames. /// </summary> public Volume Build() { PrepareFrames(_frames); // this also sorts the frames into order by slice location // Construct a model SOP data source based on the first frame's DICOM header var sopDataSourcePrototype = VolumeSopDataSourcePrototype.Create(_frames[0].Sop.DataSource, 16, 16, false); // compute normalized modality LUT double normalizedSlope, normalizedIntercept; ComputeNormalizedModalityLut(_frames, out normalizedSlope, out normalizedIntercept); sopDataSourcePrototype[DicomTags.RescaleSlope].SetFloat64(0, normalizedSlope); sopDataSourcePrototype[DicomTags.RescaleIntercept].SetFloat64(0, normalizedIntercept); sopDataSourcePrototype[DicomTags.RescaleType] = _frames[0].Sop.DataSource[DicomTags.RescaleType].Copy(); sopDataSourcePrototype[DicomTags.Units] = _frames[0].Sop.DataSource[DicomTags.Units].Copy(); // PET series use this attribute to designate rescale units // compute normalized VOI windows VoiWindow.SetWindows(ComputeNormalizedVoiWindows(_frames, normalizedSlope, normalizedIntercept), sopDataSourcePrototype); // compute the volume padding value var pixelPaddingValue = ComputePixelPaddingValue(_frames, normalizedSlope, normalizedIntercept); int minVolumeValue, maxVolumeValue; var volumeArray = BuildVolumeArray(pixelPaddingValue, normalizedSlope, normalizedIntercept, out minVolumeValue, out maxVolumeValue); var volume = new Volume(null, volumeArray, VolumeSize, VoxelSpacing, ImagePositionPatient, ImageOrientationPatient, sopDataSourcePrototype, pixelPaddingValue, _frames[0].Frame.SeriesInstanceUid, minVolumeValue, maxVolumeValue); return volume; }
private static IEnumerable<IMprSliceSet> CreateStandardSliceSets(Volume volume, IEnumerable<IVolumeSlicerParams> slicerParams) { if (volume != null && slicerParams != null) { foreach (IVolumeSlicerParams slicerParam in slicerParams) yield return new MprStandardSliceSet(volume, slicerParam); } }
public static MprStaticSliceSet CreateIdentitySliceSet(Volume volume) { return new MprStaticSliceSet(volume, VolumeSlicerParams.Identity); }
public MprVolume(Volume volume, IEnumerable<IVolumeSlicerParams> slicerParams) : this(volume, CreateStandardSliceSets(volume, slicerParams)) {}
// Derived from either a specified extent in millimeters or from the volume dimensions (default) private static Size GetSliceExtent(Volume volume, IVolumeSlicerParams slicerParams) { var effectiveSpacing = GetEffectiveSpacing(volume); var longOutputDimension = volume.LongAxisMagnitude/effectiveSpacing; var shortOutputDimenstion = volume.ShortAxisMagnitude/effectiveSpacing; var diagonalDimension = (int) Math.Sqrt(longOutputDimension*longOutputDimension + shortOutputDimenstion*shortOutputDimenstion); var columns = diagonalDimension; if (!FloatComparer.AreEqual(slicerParams.SliceExtentXMillimeters, 0f)) columns = (int) (slicerParams.SliceExtentXMillimeters/effectiveSpacing + 0.5f); var rows = diagonalDimension; if (!FloatComparer.AreEqual(slicerParams.SliceExtentYMillimeters, 0f)) rows = (int) (slicerParams.SliceExtentYMillimeters/effectiveSpacing + 0.5f); return new Size(columns, rows); }
public VolumeSliceSopDataSource(Volume volume, IVolumeSlicerParams slicerParams, IList<Vector3D> throughPoints) : this(volume.CreateTransientReference(), slicerParams, throughPoints) {}
/// <summary> /// The effective spacing defines output spacing for slices generated by the VolumeSlicer. /// </summary> private static float GetEffectiveSpacing(Volume volume) { // Because we supply the real spacing to the VTK reslicer, the slices are interpolated // as if the volume were isotropic. This results in an effective spacing that is the // minimum spacing for the volume. return volume.MinimumSpacing; }
//TODO (cr Oct 2009): can be factored out into Slice class. // VTK treats the reslice point as the center of the output image. Given the plane orientation // and size of the output image, we can derive the top left of the output image in patient space private static Vector3D GetTopLeftOfSlicePatient(Size frameSize, Vector3D throughPoint, Volume volume, IVolumeSlicerParams slicerParams) { // This is the center of the output image PointF centerImageCoord = new PointF(frameSize.Width/2f, frameSize.Height/2f); // These offsets define the x and y vector magnitudes to arrive at our point float effectiveSpacing = GetEffectiveSpacing(volume); float offsetX = centerImageCoord.X*effectiveSpacing; float offsetY = centerImageCoord.Y*effectiveSpacing; // To determine top left of slice in volume, subtract offset vectors along x and y // // Our reslice place x and y vectors Matrix resliceAxes = slicerParams.SlicingPlaneRotation; Vector3D xVec = new Vector3D(resliceAxes[0, 0], resliceAxes[0, 1], resliceAxes[0, 2]); Vector3D yVec = new Vector3D(resliceAxes[1, 0], resliceAxes[1, 1], resliceAxes[1, 2]); // Offset along x and y from reslicePoint Vector3D topLeftOfSliceVolume = throughPoint - (offsetX*xVec + offsetY*yVec); // Convert volume point to patient space return volume.ConvertToPatient(topLeftOfSliceVolume); }
public VolumeSlice(Volume volume, IVolumeSlicerParams slicerParams, Vector3D throughPoint) : this(volume.CreateTransientReference(), slicerParams, throughPoint) {}
public VolumeReference(Volume volume) { _volume = volume; _volume.OnReferenceCreated(); }
/// <summary> /// Allows specification of the slice plane, through point, and extent via two points in patient space /// </summary> public static VolumeSlicerParams Create(Volume volume, Vector3D sourceOrientationColumnPatient, Vector3D sourceOrientationRowPatient, Vector3D startPointPatient, Vector3D endPointPatient) { Vector3D sourceOrientationNormalPatient = sourceOrientationColumnPatient.Cross(sourceOrientationRowPatient); Vector3D normalLinePatient = (endPointPatient - startPointPatient).Normalize(); Vector3D normalPerpLinePatient = sourceOrientationNormalPatient.Cross(normalLinePatient); Vector3D slicePlanePatientX = normalLinePatient; Vector3D slicePlanePatientY = sourceOrientationNormalPatient; Vector3D slicePlanePatientZ = normalPerpLinePatient; Matrix slicePlanePatientOrientation = Math3D.OrientationMatrixFromVectors(slicePlanePatientX, slicePlanePatientY, slicePlanePatientZ); Matrix _resliceAxes = volume.RotateToVolumeOrientation(slicePlanePatientOrientation); Vector3D lineMiddlePointPatient = new Vector3D( (startPointPatient.X + endPointPatient.X)/2, (startPointPatient.Y + endPointPatient.Y)/2, (startPointPatient.Z + endPointPatient.Z)/2); VolumeSlicerParams slicerParams = new VolumeSlicerParams(_resliceAxes); slicerParams.SliceThroughPointPatient = new Vector3D(lineMiddlePointPatient); slicerParams.SliceExtentXMillimeters = (endPointPatient - startPointPatient).Magnitude; return slicerParams; }
// Derived frome either a specified extent in millimeters or from the volume dimensions (default) private static Size GetSliceExtent(Volume volume, IVolumeSlicerParams slicerParams) { int rows, columns; float effectiveSpacing = GetEffectiveSpacing(volume); float longOutputDimension = volume.LongAxisMagnitude/effectiveSpacing; float shortOutputDimenstion = volume.ShortAxisMagnitude/effectiveSpacing; rows = columns = (int) Math.Sqrt(longOutputDimension*longOutputDimension + shortOutputDimenstion*shortOutputDimenstion); if (slicerParams.SliceExtentXMillimeters != 0f) columns = (int) (slicerParams.SliceExtentXMillimeters/effectiveSpacing + 0.5f); if (slicerParams.SliceExtentYMillimeters != 0f) rows = (int) (slicerParams.SliceExtentYMillimeters/effectiveSpacing + 0.5f); return new Size(columns, rows); }
protected virtual void Dispose(bool disposing) { if (disposing) { if (_sliceSets != null) { _sliceSets.ItemAdded -= OnItemAdded; _sliceSets.ItemChanged -= OnItemAdded; _sliceSets.ItemChanging -= OnItemRemoved; _sliceSets.ItemRemoved -= OnItemRemoved; _sliceSets.Dispose(); _sliceSets = null; } if (_volume != null) { _volume.Dispose(); _volume = null; } } }
//TODO (cr Oct 2009): pass in a Slice, generated by slicer, then can just call GetPixelData() on the slice. public VolumeSliceSopDataSource(Volume volume, IVolumeSlicerParams slicerParams, Vector3D throughPoint) : this(volume, slicerParams, new Vector3D[] {throughPoint}) {}
public MprVolume(Volume volume) : this(volume, CreateDefaultSliceSets(volume)) {}
public MprViewerComponent(Volume volume) : this() { _volumes.Add(new MprVolume(volume)); }
private VolumeData LoadVolume(IBackgroundTaskContext context) { // TODO (CR Apr 2013): Ideally, loading and unloading could be done with minimal locking; this way, // Unload actually has to wait for Load to finish and vice versa. I think a quicker way would be to // have a _loading field - have this method set it inside the lock, then proceed to do the load, // then set the _volume field when done. Have Unload check _loading and just return, otherwise set _volume to null. // Basically, you don't need to lock the entire load operation - you only need to guarantee that multiple loads // can't occur at once, and that Unload actually unloads it. // wait for synchronized access lock (_syncVolumeDataLock) { _largeObjectData.Lock(); try { // if the data is now available, return it immediately // (i.e. we were blocked because we were already reading the data) if (_volume != null) return _volume; // load the volume data if (context == null) _volume = VolumeData.Create(_frames); else _volume = VolumeData.Create(_frames, (n, count) => context.ReportProgress(new BackgroundTaskProgress(n, count, SR.MessageFusionInProgress))); // update our stats _largeObjectData.BytesHeldCount = 2*_volume.SizeInVoxels; _largeObjectData.LargeObjectCount = 1; _largeObjectData.UpdateLastAccessTime(); // regenerating the volume data takes a few seconds _largeObjectData.RegenerationCost = LargeObjectContainerData.PresetComputedData; // register with memory manager MemoryManager.Add(this); return _volume; } finally { _largeObjectData.Unlock(); } } }