internal VolumeSliceRangeCollector(VolumeMetadata metaData, Direction direction, IReadOnlyCollection <VolumeSliceRangeDefinition> ranges, IProgress <VolumeSliceDefinition> progressNotifier = null, CancellationToken ct = default(CancellationToken)) { if (direction != Direction.Z && ranges.Any(r => r.Direction != direction)) { throw new ArgumentException("Collecting slices ranges for different directions than input is only allowed for z-directed input"); } _Direction = direction; _ProgressNotifier = progressNotifier; _Ct = ct; _X = metaData.SizeX; _Y = metaData.SizeY; _Z = metaData.SizeZ; foreach (var range in ranges) { var set = GetSlices(range.Direction); for (var i = range.First; i <= range.Last; i++) { metaData.GetSliceSize(range.Direction, out var x, out var y); set[i] = new byte[x * y]; } } Interop = new InteropSliceWriter { WriteSlice = WriteSlice }; }
internal PreviewCreator(VolumeMetadata metadata, ushort minification, IProgress <VolumeSliceDefinition> progress = null, CancellationToken ct = default(CancellationToken)) { _Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); _Minification = minification; _Progress = progress; _Ct = ct; _PreviewSizeX = ( ushort )((metadata.SizeX - 1) / minification + 1); _PreviewSizeY = ( ushort )((metadata.SizeY - 1) / minification + 1); _PreviewSizeZ = ( ushort )((metadata.SizeZ - 1) / minification + 1); if (_PreviewSizeX < 2 || _PreviewSizeY < 2 || _PreviewSizeZ < 2) { throw new ArgumentOutOfRangeException(nameof(minification)); } _PreviewData = new byte[_PreviewSizeZ][]; long sliceSize = _PreviewSizeX * _PreviewSizeY; for (var z = 0; z < _PreviewSizeZ; z++) { _PreviewData[z] = new byte[sliceSize]; } Interop = new InteropSliceWriter { WriteSlice = WriteSlice }; }
internal FullVolumeWriter(VolumeMetadata metadata, Direction direction, IProgress <ushort> progressNotifier = null, CancellationToken ct = default(CancellationToken)) { if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } _ProgressNotifier = progressNotifier; _Ct = ct; metadata.GetSliceSize(direction, out _SizeX, out _SizeY); _SizeZ = metadata.GetSize(direction); _Data = new byte[_SizeZ][]; for (var z = 0; z < _SizeZ; z++) { _Data[z] = new byte[_SizeX * _SizeY]; } Interop = new InteropSliceWriter { WriteSlice = WriteSlice }; }
/// <summary> /// Creates a new, decompressed volume from the specified data /// </summary> /// <param name="metadata">Holds additional information about a volume</param> /// <param name="stream">A stream to the slice data</param> /// <param name="options">Codec settings</param> /// <param name="progress">A progress indicator, which reports the current slice number.</param> /// <param name="ct"></param> /// <exception cref="VolumeException">Error during encoding</exception> public static Volume CreateCompressed(VolumeMetadata metadata, Stream stream, VolumeCompressionOptions options, IProgress <VolumeSliceDefinition> progress = null, CancellationToken ct = default(CancellationToken)) { var compressedData = CompressStream(stream, Direction.Z, metadata, options, progress, ct); var dirctionMap = new DirectionMap { [Direction.Z] = compressedData }; return(new CompressedVolume(metadata, options, dirctionMap)); }
internal CompressedVolume(VolumeMetadata metadata, VolumeCompressionOptions options, DirectionMap compressedData) : base(metadata) { if (compressedData[Direction.Z] == null) { throw new NotSupportedException(Resources.GetResource <Volume>("CompressedDataMissing_ErrorText")); } _CompressedData = compressedData; CompressionOptions = options; }
private static byte[] CompressStream(Stream stream, Direction direction, VolumeMetadata metadata, VolumeCompressionOptions options, IProgress <VolumeSliceDefinition> progress = null, CancellationToken ct = default(CancellationToken)) { using (var outputStream = new MemoryStream()) { GetEncodedSliceSize(metadata, direction, out var encodingSizeX, out var encodingSizeY); var inputStreamWrapper = new SliceStreamReader(metadata, stream, progress, ct); var outputStreamWrapper = new StreamWrapper(outputStream); var error = ( VolumeError )CompressVolume(inputStreamWrapper.Interop, outputStreamWrapper.Interop, encodingSizeX, encodingSizeY, options.Encoder, options.PixelFormat, options.GetOptionsString(), options.Bitrate); if (error != VolumeError.Success) { throw new VolumeException(error, Resources.FormatResource <Volume>("Compression_ErrorText", error)); } return(outputStream.ToArray()); } }
internal SliceStreamReader(VolumeMetadata metadata, Stream stream, IProgress <VolumeSliceDefinition> progressNotifier = null, CancellationToken ct = default(CancellationToken)) { _Stream = stream; _ProgressNotifier = progressNotifier; _Ct = ct; _CurrentSlice = 0; _SizeX = metadata.SizeX; _SizeY = metadata.SizeY; _SizeZ = metadata.SizeZ; _Buffer = new byte[0]; Interop = new InteropSliceReader { ReadSlice = ReadSlice }; }
internal SliceReader(VolumeMetadata metadata, byte[][] data, Direction readDirection = Direction.Z, IProgress <VolumeSliceDefinition> progressNotifier = null, CancellationToken ct = default(CancellationToken)) { _Data = data; _ReadDirection = readDirection; _ProgressNotifier = progressNotifier; _Ct = ct; _CurrentSlice = 0; _SizeX = metadata.SizeX; _SizeY = metadata.SizeY; _SizeZ = metadata.SizeZ; _Buffer = new byte[0]; Interop = new InteropSliceReader { ReadSlice = ReadSlice }; }
/// <summary> /// Extracts the specified direction. /// </summary> /// <param name="direction">The direction.</param> /// <param name="index">The index.</param> /// <param name="volumeMetadata">The volume metadata.</param> /// <param name="data">The data.</param> /// <returns></returns> /// <exception cref="System.ArgumentOutOfRangeException">direction - null</exception> internal static VolumeSlice Extract(Direction direction, ushort index, VolumeMetadata volumeMetadata, byte[][] data) { var sx = volumeMetadata.SizeX; var sy = volumeMetadata.SizeY; var sz = volumeMetadata.SizeZ; switch (direction) { case Direction.X: { var result = new byte[sy * sz]; Parallel.For(0, sz, z => { for (var y = 0; y < sy; y++) { result[z * sy + y] = data[z][y * sx + index]; } }); return(new VolumeSlice(direction, index, result)); } case Direction.Y: { var result = new byte[sx * sz]; Parallel.For(0, sz, z => { Array.Copy(data[z], index * sx, result, z * sx, sx); }); return(new VolumeSlice(direction, index, result)); } case Direction.Z: { var result = new byte[sx * sy]; Array.Copy(data[index], 0, result, 0, sx * sy); return(new VolumeSlice(direction, index, result)); } default: throw new ArgumentOutOfRangeException(nameof(direction), direction, null); } }
internal static void GetEncodedSliceSize(VolumeMetadata metadata, Direction direction, out ushort x, out ushort y) { switch (direction) { case Direction.X: x = ( ushort )Math.Max((metadata.SizeY + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); y = ( ushort )Math.Max((metadata.SizeZ + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); break; case Direction.Y: x = ( ushort )Math.Max((metadata.SizeX + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); y = ( ushort )Math.Max((metadata.SizeZ + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); break; case Direction.Z: x = ( ushort )Math.Max((metadata.SizeX + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); y = ( ushort )Math.Max((metadata.SizeY + Constants.EncodingBlockSize - 1) / Constants.EncodingBlockSize * Constants.EncodingBlockSize, Constants.MinimumEncodingSize); break; default: throw new ArgumentOutOfRangeException(nameof(direction), direction, null); } }
internal static UncompressedVolume CreatePreview(byte[][] data, VolumeMetadata metadata, ushort minification) { var sizeX = metadata.SizeX; var sizeY = metadata.SizeY; var sizeZ = metadata.SizeZ; var previewSizeX = ( ushort )((metadata.SizeX - 1) / minification + 1); var previewSizeY = ( ushort )((metadata.SizeY - 1) / minification + 1); var previewSizeZ = ( ushort )((metadata.SizeZ - 1) / minification + 1); if (previewSizeX < 2 || previewSizeY < 2 || previewSizeZ < 2) { throw new ArgumentOutOfRangeException(nameof(minification)); } var result = new byte[previewSizeZ][]; long sliceSize = previewSizeX * previewSizeY; for (var z = 0; z < previewSizeZ; z++) { result[z] = new byte[sliceSize]; } for (ushort z = 0, oz = 0; z < previewSizeZ && oz < sizeZ; z++, oz += minification) { var position = 0; for (ushort y = 0, oy = 0; y < previewSizeY && oy < sizeY; y++, oy += minification) { for (ushort x = 0, ox = 0; x < previewSizeX && ox < sizeX; x++, ox += minification) { result[z][position++] = data[oz][oy * sizeX + ox]; } } } return(Volume.CreateUncompressed(new VolumeMetadata(previewSizeX, previewSizeY, previewSizeZ, metadata.ResolutionX * minification, metadata.ResolutionY * minification, metadata.ResolutionZ * minification), result)); }
internal Volume(VolumeMetadata metadata) { Metadata = metadata; }
/// <summary> /// Loads volume data from the specified data stream. /// </summary> /// <param name="stream">The stream.</param> /// <returns></returns> /// <exception cref="VolumeException">Error during decoding</exception> /// <exception cref="NotSupportedException">The volume has no compressed data</exception> /// <exception cref="InvalidOperationException">One or more entries are missing in the archive.</exception> public static CompressedVolume Load(Stream stream) { if (!stream.CanSeek) { stream = new MemoryStream(stream.StreamToArray()); } VolumeMetadata metaData; DirectionMap directionMap = new DirectionMap(); VolumeCompressionOptions compressionOptions; using (var archive = new ZipArchive(stream)) { #region Metadata var metaDataEntry = archive.GetEntry("Metadata.xml"); if (metaDataEntry == null) { throw new InvalidOperationException(Resources.FormatResource <Volume>("InvalidFormatMissingFile_ErrorText", "Metadata.xml")); } using (var entryStream = metaDataEntry.Open()) { metaData = VolumeMetadata.Deserialize(entryStream); if (metaData.FileVersion > FileVersion) { throw new InvalidOperationException(Resources.FormatResource <Volume>("FileVersionError_Text", metaData.FileVersion, FileVersion)); } } #endregion #region CompressionOptions var compressionOptionsEntry = archive.GetEntry("CompressionOptions.xml"); if (compressionOptionsEntry != null) { using (var entryStream = compressionOptionsEntry.Open()) { compressionOptions = VolumeCompressionOptions.Deserialize(entryStream); } } else { compressionOptions = null; } #endregion #region Voxels var dataEntry = archive.GetEntry("Voxels.dat"); if (dataEntry == null) { dataEntry = archive.GetEntry("VoxelsZ.dat"); if (dataEntry == null) { throw new InvalidOperationException(Resources.FormatResource <Volume>("InvalidFormatMissingFile_ErrorText", "Voxels.dat")); } using (var entryStream = dataEntry.Open()) { directionMap[Direction.Z] = entryStream.StreamToArray(); } dataEntry = archive.GetEntry("VoxelsY.dat"); if (dataEntry != null) { using (var entryStream = dataEntry.Open()) { directionMap[Direction.Y] = entryStream.StreamToArray(); } } dataEntry = archive.GetEntry("VoxelsX.dat"); if (dataEntry != null) { using (var entryStream = dataEntry.Open()) { directionMap[Direction.X] = entryStream.StreamToArray(); } } } else { using (var entryStream = dataEntry.Open()) { directionMap[Direction.Z] = entryStream.StreamToArray(); } } #endregion } return(new CompressedVolume(metaData, compressionOptions, directionMap)); }
/// <summary> /// Extracts the specified definition. /// </summary> /// <param name="definition">The definition.</param> /// <param name="volumeMetadata">The volume metadata.</param> /// <param name="data">The data.</param> /// <returns></returns> internal static VolumeSliceRange Extract(VolumeSliceRangeDefinition definition, VolumeMetadata volumeMetadata, byte[][] data) { var slices = new List <VolumeSlice>(); for (var i = definition.First; i <= definition.Last; i++) { slices.Add(VolumeSlice.Extract(definition.Direction, i, volumeMetadata, data)); } return(new VolumeSliceRange(definition, slices)); }
/// <summary> /// Creates a new, decompressed volume from the specified data /// </summary> /// <param name="metadata">Describes the volumes size and resolution</param> /// <param name="data">The decompressed volume as 8-Bit grayscale values. The array dimensions must match the specified <paramref name="metadata"/> (byte[z][x*y]).</param> /// <exception cref="IndexOutOfRangeException">The specified data did not match the dimensions of the specified <paramref name="metadata"/>.</exception> public static UncompressedVolume CreateUncompressed(VolumeMetadata metadata, byte[][] data) { return(new UncompressedVolume(metadata, data)); }
/// <summary> /// Creates a new, decompressed volume from the specified data /// </summary> /// <param name="metadata">Describes the volumes size and resolution</param> /// <param name="data">The decompressed volume as 8-Bit grayscale values. The array dimensions must match the specified <paramref name="metadata"/> (byte[z][x*y]).</param> /// <param name="multiDirection"></param> /// <param name="progress">A progress indicator, which reports the current slice number.</param> /// <param name="options">Codec settings</param> /// <param name="ct"></param> /// <exception cref="IndexOutOfRangeException">The specified data did not match the dimensions of the specified <paramref name="metadata"/>.</exception> /// <exception cref="VolumeException">Error during encoding</exception> public static CompressedVolume CreateCompressed(VolumeMetadata metadata, byte[][] data, VolumeCompressionOptions options, bool multiDirection = false, IProgress <VolumeSliceDefinition> progress = null, CancellationToken ct = default(CancellationToken)) { var volume = new UncompressedVolume(metadata, data); return(volume.Compress(options, multiDirection, progress, ct)); }
/// <summary> /// Initializes a new instance of the <see cref="UncompressedVolume" /> class. /// </summary> /// <param name="metadata">The metadata.</param> /// <param name="data">The grayscale slice data.</param> public UncompressedVolume(VolumeMetadata metadata, byte[][] data) : base(metadata) { Data = data; CheckForIntegrity(); }