/// <summary>
        /// Allows specification of the slice plane, through point, and extent via two points in patient space
        /// </summary>
        public static VolumeSlicerParams Create(IVolumeHeader 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);
        }
 private static float GetIdealSliceSpacing(IVolumeHeader volumeHeader, Vector3D unitSpacingAxis)
 {
     // the ideal spacing is simply the diagonal of the voxel projected on to the spacing axis
     // any larger than this value, and it becomes possible for an entire voxel to fit in between two consecutive output locations (i.e. missed for interpolation)
     // any smaller than this value, and some voxels will have two or more output locations within their bounds
     return(Math.Abs(unitSpacingAxis.Dot(volumeHeader.RotateToPatientOrientation(volumeHeader.VoxelSpacing))));
 }
        private static float GetIdealSliceSpacing(IVolumeHeader volumeHeader, Vector3D unitSpacingAxis)
        {
            // the ideal spacing is simply the diagonal of the voxel projected on to the spacing axis
            // any larger than this value, and it becomes possible for an entire voxel to fit in between two consecutive output locations (i.e. missed for interpolation)
            // any smaller than this value, and some voxels will have two or more output locations within their bounds
            // note: voxel actually has 4 possible diagonals depending on the orientation, so we do this calculation for all diagonals and take the largest one
            var p = volumeHeader.RotateToPatientOrientation(volumeHeader.VoxelSpacing);

            return(new[] { p, new Vector3D(-p.X, p.Y, p.Z), new Vector3D(p.X, -p.Y, p.Z), new Vector3D(-p.X, -p.Y, p.Z) }
                   .Select(v => Math.Abs(unitSpacingAxis.Dot(v))).Max());
        }
 /// <summary>
 /// The effective spacing defines output pixel spacing for slices generated by the VolumeSlicer.
 /// </summary>
 private static float GetEffectiveSpacing(IVolumeHeader 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.
     //
     // N.B.: this behaviour is different than if had asked VTK to render it, because we are
     //  asking directly for pixel data out. If VTK renders it, then we scrape the rendering
     //  for the pixel data, the spacing would be exactly 1mm because the interpolation would
     //  happened during rendering.
     return(volume.GetMinimumSpacing());
 }
        private SizeF GetPixelSpacing(IVolumeHeader volumeHeader)
        {
            var columnHasValue = ColumnSpacing.HasValue;
            var rowHasValue    = RowSpacing.HasValue;

            if (!columnHasValue && !rowHasValue)
            {
                var spacing = GetMinimumComponent(volumeHeader.VoxelSpacing);
                return(new SizeF(spacing, spacing));
            }
            else if (columnHasValue && rowHasValue)
            {
                return(new SizeF(ColumnSpacing.Value, RowSpacing.Value));
            }
            else
            {
                var spacing = columnHasValue ? ColumnSpacing.Value : RowSpacing.Value;
                return(new SizeF(spacing, spacing));
            }
        }
        // Derived from either a specified extent in millimeters or from the volume dimensions (default)
        private static Size GetSliceExtent(IVolumeHeader volume, IVolumeSlicerParams slicerParams)
        {
            var effectiveSpacing      = GetEffectiveSpacing(volume);
            var longOutputDimension   = volume.GetLongAxisMagnitude() / effectiveSpacing;
            var shortOutputDimenstion = volume.GetShortAxisMagnitude() / 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));
        }
        // 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, IVolumeHeader volume, IVolumeSlicerParams slicerParams)
        {
            // This is the center of the output image
            var centerImageCoord = new PointF(frameSize.Width / 2f, frameSize.Height / 2f);

            // These offsets define the x and y vector magnitudes to arrive at our point
            var effectiveSpacing = GetEffectiveSpacing(volume);
            var offsetX          = centerImageCoord.X * effectiveSpacing;
            var offsetY          = centerImageCoord.Y * effectiveSpacing;

            // To determine top left of slice in volume, subtract offset vectors along x and y
            //
            // Our reslice plane x and y vectors
            var resliceAxes = slicerParams.SlicingPlaneRotation;
            var xVec        = new Vector3D(resliceAxes[0, 0], resliceAxes[0, 1], resliceAxes[0, 2]);
            var yVec        = new Vector3D(resliceAxes[1, 0], resliceAxes[1, 1], resliceAxes[1, 2]);

            // Offset along x and y from reslicePoint
            var topLeftOfSliceVolume = throughPoint - (offsetX * xVec + offsetY * yVec);

            // Convert volume point to patient space
            return(volume.ConvertToPatient(topLeftOfSliceVolume));
        }
		private static float GetIdealSliceSpacing(IVolumeHeader volumeHeader, Vector3D unitSpacingAxis)
		{
			// the ideal spacing is simply the diagonal of the voxel projected on to the spacing axis
			// any larger than this value, and it becomes possible for an entire voxel to fit in between two consecutive output locations (i.e. missed for interpolation)
			// any smaller than this value, and some voxels will have two or more output locations within their bounds
			// note: voxel actually has 4 possible diagonals depending on the orientation, so we do this calculation for all diagonals and take the largest one
			var p = volumeHeader.RotateToPatientOrientation(volumeHeader.VoxelSpacing);
			return new[] {p, new Vector3D(-p.X, p.Y, p.Z), new Vector3D(p.X, -p.Y, p.Z), new Vector3D(-p.X, -p.Y, p.Z)}
				.Select(v => Math.Abs(unitSpacingAxis.Dot(v))).Max();
		}
Exemple #9
0
 public static float GetLongAxisMagnitude(this IVolumeHeader volume)
 {
     return(volume.VolumeSize.Max());
 }
		private static float GetIdealSliceSpacing(IVolumeHeader volumeHeader, Vector3D unitSpacingAxis)
		{
			// the ideal spacing is simply the diagonal of the voxel projected on to the spacing axis
			// any larger than this value, and it becomes possible for an entire voxel to fit in between two consecutive output locations (i.e. missed for interpolation)
			// any smaller than this value, and some voxels will have two or more output locations within their bounds
			return Math.Abs(unitSpacingAxis.Dot(volumeHeader.RotateToPatientOrientation(volumeHeader.VoxelSpacing)));
		}
Exemple #11
0
		/// <summary>
		/// The effective spacing defines output pixel spacing for slices generated by the VolumeSlicer.
		/// </summary>
		private static float GetEffectiveSpacing(IVolumeHeader 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.
			//
			// N.B.: this behaviour is different than if had asked VTK to render it, because we are
			//  asking directly for pixel data out. If VTK renders it, then we scrape the rendering
			//  for the pixel data, the spacing would be exactly 1mm because the interpolation would
			//  happened during rendering.
			return volume.GetMinimumSpacing();
		}
Exemple #12
0
		// Derived from either a specified extent in millimeters or from the volume dimensions (default)
		private static Size GetSliceExtent(IVolumeHeader volume, IVolumeSlicerParams slicerParams)
		{
			var effectiveSpacing = GetEffectiveSpacing(volume);
			var longOutputDimension = volume.GetLongAxisMagnitude()/effectiveSpacing;
			var shortOutputDimenstion = volume.GetShortAxisMagnitude()/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);
		}
Exemple #13
0
		// 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, IVolumeHeader volume, IVolumeSlicerParams slicerParams)
		{
			// This is the center of the output image
			var centerImageCoord = new PointF(frameSize.Width/2f, frameSize.Height/2f);

			// These offsets define the x and y vector magnitudes to arrive at our point
			var effectiveSpacing = GetEffectiveSpacing(volume);
			var offsetX = centerImageCoord.X*effectiveSpacing;
			var offsetY = centerImageCoord.Y*effectiveSpacing;

			// To determine top left of slice in volume, subtract offset vectors along x and y
			//
			// Our reslice plane x and y vectors
			var resliceAxes = slicerParams.SlicingPlaneRotation;
			var xVec = new Vector3D(resliceAxes[0, 0], resliceAxes[0, 1], resliceAxes[0, 2]);
			var yVec = new Vector3D(resliceAxes[1, 0], resliceAxes[1, 1], resliceAxes[1, 2]);

			// Offset along x and y from reslicePoint
			var topLeftOfSliceVolume = throughPoint - (offsetX*xVec + offsetY*yVec);

			// Convert volume point to patient space
			return volume.ConvertToPatient(topLeftOfSliceVolume);
		}
Exemple #14
0
 public static float GetShortAxisMagnitude(this IVolumeHeader volume)
 {
     return(volume.VolumeSize.Min());
 }
 private float GetSliceSpacing(IVolumeHeader volumeHeader, Vector3D slicerAxisZ)
 {
     return(SliceSpacing ?? (SliceThickness.HasValue ? SliceThickness.Value : GetIdealSliceSpacing(volumeHeader, slicerAxisZ)));
 }
		private float GetSliceSpacing(IVolumeHeader volumeHeader, Vector3D slicerAxisZ)
		{
			return SliceSpacing ?? (SliceThickness.HasValue ? SliceThickness.Value : GetIdealSliceSpacing(volumeHeader, slicerAxisZ));
		}
		private SizeF GetPixelSpacing(IVolumeHeader volumeHeader)
		{
			var columnHasValue = ColumnSpacing.HasValue;
			var rowHasValue = RowSpacing.HasValue;
			if (!columnHasValue && !rowHasValue)
			{
				var spacing = GetMinimumComponent(volumeHeader.VoxelSpacing);
				return new SizeF(spacing, spacing);
			}
			else if (columnHasValue && rowHasValue)
			{
				return new SizeF(ColumnSpacing.Value, RowSpacing.Value);
			}
			else
			{
				var spacing = columnHasValue ? ColumnSpacing.Value : RowSpacing.Value;
				return new SizeF(spacing, spacing);
			}
		}
		/// <summary>
		/// Allows specification of the slice plane, through point, and extent via two points in patient space
		/// </summary>
		public static VolumeSlicerParams Create(IVolumeHeader 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;
		}
Exemple #19
0
 public static float GetMaximumSpacing(this IVolumeHeader volume)
 {
     return(volume.VoxelSpacing.Max());
 }