From 0cfcacb7275e540ac0042166d17391bbfb6d463c Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 21 Jun 2024 11:47:16 +0200 Subject: [PATCH] Simplify and fix PixImage/Volume copy and conversion This commit removes some redundant conversion utilties and fixes the format conversion to work with subimage windows. --- .../PixImage/PixImage.cs | 208 ++++------ .../PixImage/PixVolume.cs | 196 ++++------ .../TensorExtensions.cs | 363 ------------------ .../Images/PixImageTests.cs | 37 +- .../Images/PixVolumeTests.cs | 34 +- 5 files changed, 210 insertions(+), 628 deletions(-) diff --git a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs index 930d8820..f28568e5 100644 --- a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs +++ b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Text; using System.Threading; +using System.Drawing; namespace Aardvark.Base { @@ -804,57 +805,10 @@ public static PixImageInfo GetInfoFromStream(Stream stream, IPixLoader loader = #region Conversions - protected static Dictionary<(Type, Type), Func> - s_copyFunMap = - new Dictionary<(Type, Type), Func>() - { - { (typeof(byte), typeof(byte)), v => ((Volume)v).CopyWindow() }, - { (typeof(byte), typeof(ushort)), v => ((Volume)v).ToUShortColor() }, - { (typeof(byte), typeof(uint)), v => ((Volume)v).ToUIntColor() }, - { (typeof(byte), typeof(Half)), v => ((Volume)v).ToHalfColor() }, - { (typeof(byte), typeof(float)), v => ((Volume)v).ToFloatColor() }, - { (typeof(byte), typeof(double)), v => ((Volume)v).ToDoubleColor() }, - - { (typeof(ushort), typeof(byte)), v => ((Volume)v).ToByteColor() }, - { (typeof(ushort), typeof(ushort)), v => ((Volume)v).CopyWindow() }, - { (typeof(ushort), typeof(uint)), v => ((Volume)v).ToUIntColor() }, - { (typeof(ushort), typeof(Half)), v => ((Volume)v).ToHalfColor() }, - { (typeof(ushort), typeof(float)), v => ((Volume)v).ToFloatColor() }, - { (typeof(ushort), typeof(double)), v => ((Volume)v).ToDoubleColor() }, - - { (typeof(uint), typeof(byte)), v => ((Volume)v).ToByteColor() }, - { (typeof(uint), typeof(ushort)), v => ((Volume)v).ToUShortColor() }, - { (typeof(uint), typeof(uint)), v => ((Volume)v).CopyWindow() }, - { (typeof(uint), typeof(Half)), v => ((Volume)v).ToHalfColor() }, - { (typeof(uint), typeof(float)), v => ((Volume)v).ToFloatColor() }, - { (typeof(uint), typeof(double)), v => ((Volume)v).ToDoubleColor() }, - - { (typeof(Half), typeof(byte)), v => ((Volume)v).ToByteColor() }, - { (typeof(Half), typeof(ushort)), v => ((Volume)v).ToUShortColor() }, - { (typeof(Half), typeof(uint)), v => ((Volume)v).ToUIntColor() }, - { (typeof(Half), typeof(Half)), v => ((Volume)v).CopyWindow() }, - { (typeof(Half), typeof(float)), v => ((Volume)v).ToFloatColor() }, - { (typeof(Half), typeof(double)), v => ((Volume)v).ToDoubleColor() }, - - { (typeof(float), typeof(byte)), v => ((Volume)v).ToByteColor() }, - { (typeof(float), typeof(ushort)), v => ((Volume)v).ToUShortColor() }, - { (typeof(float), typeof(uint)), v => ((Volume)v).ToUIntColor() }, - { (typeof(float), typeof(Half)), v => ((Volume)v).ToHalfColor() }, - { (typeof(float), typeof(float)), v => ((Volume)v).CopyWindow() }, - { (typeof(float), typeof(double)), v => ((Volume)v).ToDoubleColor() }, - - { (typeof(double), typeof(byte)), v => ((Volume)v).ToByteColor() }, - { (typeof(double), typeof(ushort)), v => ((Volume)v).ToUShortColor() }, - { (typeof(double), typeof(uint)), v => ((Volume)v).ToUIntColor() }, - { (typeof(double), typeof(Half)), v => ((Volume)v).ToHalfColor() }, - { (typeof(double), typeof(float)), v => ((Volume)v).ToFloatColor() }, - { (typeof(double), typeof(double)), v => ((Volume)v).CopyWindow() }, - }; - public abstract PixImage ToPixImage(); - public abstract PixImage Transformed(ImageTrafo trafo); - public PixImage AsPixImage() => this as PixImage; + public PixImage ToPixImage() => AsPixImage() ?? new PixImage(this); + public PixImage ToPixImage(Col.Format format) { if (this is PixImage castImage && castImage.Format == format && castImage.ChannelCount == format.ChannelCount()) @@ -869,7 +823,7 @@ public PixImage ToPixImage(Col.Format format) public abstract PixFormat PixFormat { get; } public abstract VolumeInfo VolumeInfo { get; } - + public abstract V2i Size { get; } public abstract V2l SizeL { get; } @@ -882,6 +836,8 @@ public PixImage ToPixImage(Col.Format format) public abstract void CopyChannelTo(long channelIndex, Matrix target); + public abstract void CopyVolumeTo(Volume target); + public abstract PixImage ToPixImage(Col.Format format); public abstract PixImage CopyToPixImage(); @@ -893,11 +849,13 @@ public PixImage ToPixImage(Col.Format format) public abstract Array Data { get; } public abstract T Visit(IPixImageVisitor visitor); - + #endregion #region Image Manipulation (abstract) + public abstract PixImage Transformed(ImageTrafo trafo); + public abstract PixImage RemappedPixImage( Matrix xMap, Matrix yMap, ImageInterpolation ip = ImageInterpolation.Cubic); @@ -1106,75 +1064,84 @@ public PixImage(Col.Format format, PixImage pixImage) ); } - var size = pixImage.Size; - var srcChannels = Col.ChannelsOfFormat(pixImage.Format); + var srcInfo = pixImage.VolumeInfo; var dstChannels = Col.ChannelsOfFormat(format); - var volume = CreateVolume(size.X, size.Y, dstChannels.Length); + var volume = CreateVolume(srcInfo.Size.X, srcInfo.Size.Y, dstChannels.Length); + volume.F = srcInfo.F; - for (int dstIndex = 0; dstIndex < dstChannels.Length; dstIndex++) + if (format == pixImage.Format) + { + pixImage.CopyVolumeTo(volume); + } + else { - var channel = dstChannels[dstIndex]; - var matrix = volume.SubXYMatrix(dstIndex); - var srcIndex = srcChannels.IndexOf(channel); + var srcChannels = Col.ChannelsOfFormat(pixImage.Format); - // If we have an RGB channel, we may also just copy a Gray or BW channel - if (srcIndex == -1 && (channel == Col.Channel.Red || channel == Col.Channel.Green || channel == Col.Channel.Blue)) + for (int dstIndex = 0; dstIndex < dstChannels.Length; dstIndex++) { - var bw = srcChannels.IndexOf(Col.Channel.BW); - var gray = srcChannels.IndexOf(Col.Channel.Gray); - srcIndex = Fun.Max(bw, gray); - } + var channel = dstChannels[dstIndex]; + var matrix = volume.SubXYMatrix(dstIndex); + var srcIndex = srcChannels.IndexOf(channel); - if (srcIndex > -1) - { - // Channel exists in source image, just copy - if (pixImage is PixImage pi) + // If we have an RGB channel, we may also just copy a Gray or BW channel + if (srcIndex == -1 && (channel == Col.Channel.Red || channel == Col.Channel.Green || channel == Col.Channel.Blue)) { - matrix.Set(pi.GetChannelInFormatOrder(srcIndex)); + var bw = srcChannels.IndexOf(Col.Channel.BW); + var gray = srcChannels.IndexOf(Col.Channel.Gray); + srcIndex = Fun.Max(bw, gray); } - else + + if (srcIndex > -1) { - var order = pixImage.Format.ChannelOrder(); - pixImage.CopyChannelTo(order[srcIndex], matrix); // CopyChannelTo uses canonical order + // Channel exists in source image, just copy + if (pixImage is PixImage pi) + { + matrix.Set(pi.GetChannelInFormatOrder(srcIndex)); + } + else + { + var order = pixImage.Format.ChannelOrder(); + pixImage.CopyChannelTo(order[srcIndex], matrix); // CopyChannelTo uses canonical order + } } - } - else if (channel == Col.Channel.Alpha || channel == Col.Channel.PremultipliedAlpha) - { - // Alpha channel does not exist in source image, fill with max value - matrix.Set(Col.Info.MaxValue); - } - else if (channel == Col.Channel.Gray && - srcChannels.Contains(Col.Channel.Red) && - srcChannels.Contains(Col.Channel.Green) && - srcChannels.Contains(Col.Channel.Blue)) - { - var t1 = pixImage.PixFormat.Type; - var t2 = typeof(T); - - if (s_rgbToGrayMap.TryGetValue((t1, t2), out var toGray)) + else if (channel == Col.Channel.Alpha || channel == Col.Channel.PremultipliedAlpha) + { + // Alpha channel does not exist in source image, fill with max value + matrix.Set(Col.Info.MaxValue); + } + else if (channel == Col.Channel.Gray && + srcChannels.Contains(Col.Channel.Red) && + srcChannels.Contains(Col.Channel.Green) && + srcChannels.Contains(Col.Channel.Blue)) + { + var t1 = pixImage.PixFormat.Type; + var t2 = typeof(T); + + if (s_rgbToGrayMap.TryGetValue((t1, t2), out var toGray)) + { + toGray(pixImage, matrix); + } + else + { + throw new NotImplementedException( + $"Conversion from {t1} image with format {pixImage.Format} to {t2} grayscale not implemented." + ); + } + } + else if (channel == Col.Channel.Blue && + pixImage.Format == Col.Format.RG && + dstChannels.Contains(Col.Channel.Red) && + dstChannels.Contains(Col.Channel.Green)) { - toGray(pixImage, matrix); + // Allow expanding from RG to RGB formats, blue channel is set to zero } else { - throw new NotImplementedException( - $"Conversion from {t1} image with format {pixImage.Format} to {t2} grayscale not implemented." + throw new NotSupportedException( + $"Conversion from format {pixImage.Format} to format {format} is not supported." ); } } - else if (channel == Col.Channel.Blue && - pixImage.Format == Col.Format.RG && - dstChannels.Contains(Col.Channel.Red) && - dstChannels.Contains(Col.Channel.Green)) - { - // Allow expanding from RG to RGB formats, blue channel is set to zero - } - else - { - throw new NotSupportedException( - $"Conversion from format {pixImage.Format} to format {format} is not supported." - ); - } } Volume = volume; @@ -1295,7 +1262,7 @@ public static PixImage Create(Col.Format format, params Matrix[] c var order = format.ChannelOrder(); if (ch != format.ChannelCount()) - throw new ArgumentException("the specified format needs a different number of channels"); + throw new ArgumentException("the specified format needs a different number of channels"); channels.ForEach((channel, ci) => volume.SubXYMatrix(order[ci]).Set(channel)); @@ -1307,7 +1274,7 @@ public static PixImage Create(Col.Format format, params Matrix[] c #region Properties public override VolumeInfo VolumeInfo => Volume.Info; - + public override V2i Size => (V2i)Volume.Info.Size.XY; public override V2l SizeL => Volume.Info.Size.XY; @@ -1352,6 +1319,9 @@ public IEnumerable> Channels #region Image Manipulation + public override PixImage Transformed(ImageTrafo trafo) + => new PixImage(Format, Volume.Transformed(trafo)); + public override PixImage RemappedPixImage( Matrix xMap, Matrix yMap, ImageInterpolation ip = ImageInterpolation.Cubic @@ -1443,7 +1413,7 @@ public static void SetScaledFun(Func, V2d, ImageInterpolation, Volume< { s_scaledFun = scaledFun; } - + public PixImage Scaled( double scaleFactor, ImageInterpolation ip = ImageInterpolation.Cubic @@ -1605,22 +1575,6 @@ public void SetResized( public PixImage ToFormat(Col.Format format) => Format == format ? this : new PixImage(format, this); - public override PixImage ToPixImage() - { - var castImage = this as PixImage; - if (castImage != null) return castImage; - var format = typeof(T1).FormatDefaultOf(ChannelCount); - if (Format == format) - { - var copy = s_copyFunMap[(typeof(T), typeof(T1))]; - return new PixImage(format, (Volume)copy(Volume)); - } - return new PixImage(this); - } - - public override PixImage Transformed(ImageTrafo trafo) - => new PixImage(Format, Volume.Transformed(trafo)); - #endregion #region Obtaining Matrices @@ -1666,7 +1620,7 @@ public Matrix GetChannelInFormatOrder(long formatChannelIndex) matrix.Accessors = TensorAccessors.Get(TensorAccessors.Intent.ColorChannel, Volume.DeltaArray); return matrix; } - + public Matrix GetMatrix() => Volume.GetMatrix(Format.GetIntent()); #endregion @@ -1681,6 +1635,14 @@ public override void CopyChannelTo(long channelIndex, Matrix target) target.Set(subMatrix); } + public override void CopyVolumeTo(Volume target) + { + if (Volume is Volume source) + target.Set(source); + else + target.Set(Volume.AsVolume()); + } + public override PixImage ToPixImage(Col.Format format) { if (Format == format && ChannelCount == format.ChannelCount()) diff --git a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixVolume.cs b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixVolume.cs index f0d9535d..0e968926 100644 --- a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixVolume.cs +++ b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixVolume.cs @@ -54,60 +54,12 @@ public static Tensor4 CreateTensor4(long sizeX, long sizeY, long sizeZ, lo #region Conversions - protected static Dictionary<(Type, Type), Func> - s_copyFunMap = - new Dictionary<(Type, Type), Func>() - { - { (typeof(byte), typeof(byte)), v => ((Tensor4)v).CopyWindow() }, - { (typeof(byte), typeof(ushort)), v => ((Tensor4)v).ToUShortColor() }, - { (typeof(byte), typeof(uint)), v => ((Tensor4)v).ToUIntColor() }, - { (typeof(byte), typeof(Half)), v => ((Tensor4)v).ToHalfColor() }, - { (typeof(byte), typeof(float)), v => ((Tensor4)v).ToFloatColor() }, - { (typeof(byte), typeof(double)), v => ((Tensor4)v).ToDoubleColor() }, - - { (typeof(ushort), typeof(byte)), v => ((Tensor4)v).ToByteColor() }, - { (typeof(ushort), typeof(ushort)), v => ((Tensor4)v).CopyWindow() }, - { (typeof(ushort), typeof(uint)), v => ((Tensor4)v).ToUIntColor() }, - { (typeof(ushort), typeof(Half)), v => ((Tensor4)v).ToHalfColor() }, - { (typeof(ushort), typeof(float)), v => ((Tensor4)v).ToFloatColor() }, - { (typeof(ushort), typeof(double)), v => ((Tensor4)v).ToDoubleColor() }, - - { (typeof(uint), typeof(byte)), v => ((Tensor4)v).ToByteColor() }, - { (typeof(uint), typeof(ushort)), v => ((Tensor4)v).ToUShortColor() }, - { (typeof(uint), typeof(uint)), v => ((Tensor4)v).CopyWindow() }, - { (typeof(uint), typeof(Half)), v => ((Tensor4)v).ToHalfColor() }, - { (typeof(uint), typeof(float)), v => ((Tensor4)v).ToFloatColor() }, - { (typeof(uint), typeof(double)), v => ((Tensor4)v).ToDoubleColor() }, - - { (typeof(Half), typeof(byte)), v => ((Tensor4)v).ToByteColor() }, - { (typeof(Half), typeof(ushort)), v => ((Tensor4)v).ToUShortColor() }, - { (typeof(Half), typeof(uint)), v => ((Tensor4)v).ToUIntColor() }, - { (typeof(Half), typeof(Half)), v => ((Tensor4)v).CopyWindow() }, - { (typeof(Half), typeof(float)), v => ((Tensor4)v).ToFloatColor() }, - { (typeof(Half), typeof(double)), v => ((Tensor4)v).ToDoubleColor() }, - - { (typeof(float), typeof(byte)), v => ((Tensor4)v).ToByteColor() }, - { (typeof(float), typeof(ushort)), v => ((Tensor4)v).ToUShortColor() }, - { (typeof(float), typeof(uint)), v => ((Tensor4)v).ToUIntColor() }, - { (typeof(float), typeof(Half)), v => ((Tensor4)v).ToHalfColor() }, - { (typeof(float), typeof(float)), v => ((Tensor4)v).CopyWindow() }, - { (typeof(float), typeof(double)), v => ((Tensor4)v).ToDoubleColor() }, - - { (typeof(double), typeof(byte)), v => ((Tensor4)v).ToByteColor() }, - { (typeof(double), typeof(ushort)), v => ((Tensor4)v).ToUShortColor() }, - { (typeof(double), typeof(uint)), v => ((Tensor4)v).ToUIntColor() }, - { (typeof(double), typeof(Half)), v => ((Tensor4)v).ToHalfColor() }, - { (typeof(double), typeof(float)), v => ((Tensor4)v).ToFloatColor() }, - { (typeof(double), typeof(double)), v => ((Tensor4)v).CopyWindow() }, - }; - private static void ToGray(PixVolume src, object dst, Func toGray) { ((Volume)dst).SetMap(src.AsPixVolume().GetVolume(), toGray); } - protected static Dictionary<(Type, Type), Action> s_rgbToGrayMap = - new Dictionary<(Type, Type), Action>() + protected static readonly Dictionary<(Type, Type), Action> s_rgbToGrayMap = new() { { (typeof(byte), typeof(byte)), (src, dst) => ToGray(src, dst, Col.ToGrayByte) }, { (typeof(ushort), typeof(ushort)), (src, dst) => ToGray(src, dst, Col.ToGrayUShort) }, @@ -116,17 +68,13 @@ private static void ToGray(PixVolume src, object dst, Func toGr { (typeof(double), typeof(double)), (src, dst) => ToGray(src, dst, Col.ToGrayDouble) }, }; - public abstract PixVolume ToPixVolume(); + public PixVolume AsPixVolume() => this as PixVolume; - public PixVolume AsPixVolume() - { - return this as PixVolume; - } + public PixVolume ToPixVolume() => AsPixVolume() ?? new PixVolume(this); public PixVolume ToPixVolume(Col.Format format) { - if (this is PixVolume castVolume && castVolume.Format == format - && castVolume.ChannelCount == format.ChannelCount()) + if (this is PixVolume castVolume && castVolume.Format == format && castVolume.ChannelCount == format.ChannelCount()) return castVolume; return new PixVolume(format, this); } @@ -149,6 +97,8 @@ public PixVolume ToPixVolume(Col.Format format) public abstract void CopyChannelTo(long channelIndex, Volume target); + public abstract void CopyTensor4To(Tensor4 target); + public abstract PixVolume ToPixVolume(Col.Format format); public abstract PixVolume CopyToPixVolume(); @@ -225,75 +175,84 @@ public PixVolume(Col.Format format, PixVolume pixVolume) ); } - var size = pixVolume.Size; - var srcChannels = Col.ChannelsOfFormat(pixVolume.Format); + var srcInfo = pixVolume.Tensor4Info; var dstChannels = Col.ChannelsOfFormat(format); - var tensor4 = CreateTensor4(size.X, size.Y, size.Z, dstChannels.Length); + var tensor4 = CreateTensor4(srcInfo.Size.X, srcInfo.Size.Y, srcInfo.Size.Z, dstChannels.Length); + tensor4.F = pixVolume.Tensor4Info.F; - for (int dstIndex = 0; dstIndex < dstChannels.Length; dstIndex++) + if (format == pixVolume.Format) + { + pixVolume.CopyTensor4To(tensor4); + } + else { - var channel = dstChannels[dstIndex]; - var volume = tensor4.SubXYZVolume(dstIndex); - var srcIndex = srcChannels.IndexOf(channel); + var srcChannels = Col.ChannelsOfFormat(pixVolume.Format); - // If we have an RGB channel, we may also just copy a Gray or BW channel - if (srcIndex == -1 && (channel == Col.Channel.Red || channel == Col.Channel.Green || channel == Col.Channel.Blue)) + for (int dstIndex = 0; dstIndex < dstChannels.Length; dstIndex++) { - var bw = srcChannels.IndexOf(Col.Channel.BW); - var gray = srcChannels.IndexOf(Col.Channel.Gray); - srcIndex = Fun.Max(bw, gray); - } + var channel = dstChannels[dstIndex]; + var volume = tensor4.SubXYZVolume(dstIndex); + var srcIndex = srcChannels.IndexOf(channel); - if (srcIndex > -1) - { - // Channel exists in source image, just copy - if (pixVolume is PixVolume pi) + // If we have an RGB channel, we may also just copy a Gray or BW channel + if (srcIndex == -1 && (channel == Col.Channel.Red || channel == Col.Channel.Green || channel == Col.Channel.Blue)) { - volume.Set(pi.GetChannelInFormatOrder(srcIndex)); + var bw = srcChannels.IndexOf(Col.Channel.BW); + var gray = srcChannels.IndexOf(Col.Channel.Gray); + srcIndex = Fun.Max(bw, gray); } - else + + if (srcIndex > -1) { - var order = pixVolume.Format.ChannelOrder(); - pixVolume.CopyChannelTo(order[srcIndex], volume); // CopyChannelTo uses canonical order + // Channel exists in source image, just copy + if (pixVolume is PixVolume pi) + { + volume.Set(pi.GetChannelInFormatOrder(srcIndex)); + } + else + { + var order = pixVolume.Format.ChannelOrder(); + pixVolume.CopyChannelTo(order[srcIndex], volume); // CopyChannelTo uses canonical order + } } - } - else if (channel == Col.Channel.Alpha || channel == Col.Channel.PremultipliedAlpha) - { - // Alpha channel does not exist in source image, fill with max value - volume.Set(Col.Info.MaxValue); - } - else if (channel == Col.Channel.Gray && - srcChannels.Contains(Col.Channel.Red) && - srcChannels.Contains(Col.Channel.Green) && - srcChannels.Contains(Col.Channel.Blue)) - { - var t1 = pixVolume.PixFormat.Type; - var t2 = typeof(T); - - if (s_rgbToGrayMap.TryGetValue((t1, t2), out var toGray)) + else if (channel == Col.Channel.Alpha || channel == Col.Channel.PremultipliedAlpha) { - toGray(pixVolume, volume); + // Alpha channel does not exist in source image, fill with max value + volume.Set(Col.Info.MaxValue); + } + else if (channel == Col.Channel.Gray && + srcChannels.Contains(Col.Channel.Red) && + srcChannels.Contains(Col.Channel.Green) && + srcChannels.Contains(Col.Channel.Blue)) + { + var t1 = pixVolume.PixFormat.Type; + var t2 = typeof(T); + + if (s_rgbToGrayMap.TryGetValue((t1, t2), out var toGray)) + { + toGray(pixVolume, volume); + } + else + { + throw new NotImplementedException( + $"Conversion from {t1} image with format {pixVolume.Format} to {t2} grayscale not implemented." + ); + } + } + else if (channel == Col.Channel.Blue && + pixVolume.Format == Col.Format.RG && + dstChannels.Contains(Col.Channel.Red) && + dstChannels.Contains(Col.Channel.Green)) + { + // Allow expanding from RG to RGB formats, blue channel is set to zero } else { - throw new NotImplementedException( - $"Conversion from {t1} image with format {pixVolume.Format} to {t2} grayscale not implemented." + throw new NotSupportedException( + $"Conversion from format {pixVolume.Format} to format {format} is not supported." ); } } - else if (channel == Col.Channel.Blue && - pixVolume.Format == Col.Format.RG && - dstChannels.Contains(Col.Channel.Red) && - dstChannels.Contains(Col.Channel.Green)) - { - // Allow expanding from RG to RGB formats, blue channel is set to zero - } - else - { - throw new NotSupportedException( - $"Conversion from format {pixVolume.Format} to format {format} is not supported." - ); - } } Tensor4 = tensor4; @@ -429,19 +388,6 @@ public PixVolume ToFormat(Col.Format format) return Format == format ? this : new PixVolume(format, this); } - public override PixVolume ToPixVolume() - { - var castVolume = this as PixVolume; - if (castVolume != null) return castVolume; - var format = typeof(T1).FormatDefaultOf(ChannelCount); - if (Format == format) - { - var copy = s_copyFunMap[(typeof(T), typeof(T1))]; - return new PixVolume(format, (Tensor4)copy(Tensor4)); - } - return new PixVolume(this); - } - #endregion #region Obtaining Volumes @@ -517,6 +463,14 @@ public override void CopyChannelTo(long channelIndex, Volume target) target.Set(subMatrix); } + public override void CopyTensor4To(Tensor4 target) + { + if (Tensor4 is Tensor4 source) + target.Set(source); + else + target.Set(Tensor4.AsTensor4()); + } + public override PixVolume ToPixVolume(Col.Format format) { if (Format == format && ChannelCount == format.ChannelCount()) diff --git a/src/Aardvark.Base.Tensors.CSharp/TensorExtensions.cs b/src/Aardvark.Base.Tensors.CSharp/TensorExtensions.cs index fd3073c8..e3e29e95 100644 --- a/src/Aardvark.Base.Tensors.CSharp/TensorExtensions.cs +++ b/src/Aardvark.Base.Tensors.CSharp/TensorExtensions.cs @@ -177,369 +177,6 @@ public static PixImage ToPixImage(this IMatrix matrix) #endregion - #region Conversions Volume to Volume (byte, ushort, uint, float, double) [CSharp (internal)] - - // All the following conversions are internal only, since we do not - // know about the channel order at this level. Only PixImages know - // about channel order. - - #region Byte - - internal static Volume ToUShortColor(this Volume volume) - { - return volume.MapToImageWindow(Col.ByteToUShort); - } - - internal static Volume ToUIntColor(this Volume volume) - { - return volume.MapToImageWindow(Col.ByteToUInt); - } - - internal static Volume ToHalfColor(this Volume volume) - { - return volume.MapToImageWindow(Col.ByteToHalf); - } - - internal static Volume ToFloatColor(this Volume volume) - { - return volume.MapToImageWindow(Col.ByteToFloat); - } - - internal static Volume ToDoubleColor(this Volume volume) - { - return volume.MapToImageWindow(Col.ByteToDouble); - } - - #endregion - - #region UShort - - internal static Volume ToByteColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UShortToByte); - } - - internal static Volume ToUIntColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UShortToUInt); - } - - internal static Volume ToHalfColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UShortToHalf); - } - - internal static Volume ToFloatColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UShortToFloat); - } - - internal static Volume ToDoubleColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UShortToDouble); - } - - #endregion - - #region UInt - - internal static Volume ToByteColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UIntToByte); - } - - internal static Volume ToUShortColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UIntToUShort); - } - - internal static Volume ToHalfColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UIntToHalf); - } - - internal static Volume ToFloatColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UIntToFloat); - } - - internal static Volume ToDoubleColor(this Volume volume) - { - return volume.MapToImageWindow(Col.UIntToDouble); - } - - #endregion - - #region Half - - internal static Volume ToByteColor(this Volume volume) - { - return volume.MapToImageWindow(Col.HalfToByte); - } - - internal static Volume ToUShortColor(this Volume volume) - { - return volume.MapToImageWindow(Col.HalfToUShort); - } - - internal static Volume ToUIntColor(this Volume volume) - { - return volume.MapToImageWindow(Col.HalfToUInt); - } - - internal static Volume ToFloatColor(this Volume volume) - { - return volume.MapToImageWindow(Col.HalfToFloat); - } - - internal static Volume ToDoubleColor(this Volume volume) - { - return volume.MapToImageWindow(Col.HalfToDouble); - } - - #endregion - - #region Float - - internal static Volume ToByteColor(this Volume volume) - { - return volume.MapToImageWindow(Col.FloatToByte); - } - - internal static Volume ToUShortColor(this Volume volume) - { - return volume.MapToImageWindow(Col.FloatToUShort); - } - - internal static Volume ToUIntColor(this Volume volume) - { - return volume.MapToImageWindow(Col.FloatToUInt); - } - - internal static Volume ToHalfColor(this Volume volume) - { - return volume.MapToImageWindow(Col.FloatToHalf); - } - - internal static Volume ToDoubleColor(this Volume volume) - { - return volume.MapToImageWindow(Col.FloatToDouble); - } - - #endregion - - #region Double - - internal static Volume ToByteColor(this Volume volume) - { - return volume.MapToImageWindow(Col.DoubleToByte); - } - - internal static Volume ToUShortColor(this Volume volume) - { - return volume.MapToImageWindow(Col.DoubleToUShort); - } - - internal static Volume ToUIntColor(this Volume volume) - { - return volume.MapToImageWindow(Col.DoubleToUInt); - } - - internal static Volume ToHalfColor(this Volume volume) - { - return volume.MapToImageWindow(Col.DoubleToHalf); - } - - internal static Volume ToFloatColor(this Volume volume) - { - return volume.MapToImageWindow(Col.DoubleToFloat); - } - - #endregion - - #endregion - - #region Conversions Tensor4 to Tensor4 (byte, ushort, uint, float, double) [CSharp (internal)] - - // All the following conversions are internal only, since we do not - // know about the channel order at this level. Only PixVolumes know - // about channel order. - - #region Byte - - internal static Tensor4 ToUShortColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.ByteToUShort); - } - - internal static Tensor4 ToUIntColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.ByteToUInt); - } - - internal static Tensor4 ToHalfColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.ByteToHalf); - } - - internal static Tensor4 ToFloatColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.ByteToFloat); - } - - internal static Tensor4 ToDoubleColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.ByteToDouble); - } - - #endregion - - #region UShort - - internal static Tensor4 ToByteColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UShortToByte); - } - - internal static Tensor4 ToUIntColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UShortToUInt); - } - - internal static Tensor4 ToHalfColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UShortToHalf); - } - - internal static Tensor4 ToFloatColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UShortToFloat); - } - - internal static Tensor4 ToDoubleColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UShortToDouble); - } - - #endregion - - #region UInt - - internal static Tensor4 ToByteColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UIntToByte); - } - - internal static Tensor4 ToUShortColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UIntToUShort); - } - - internal static Tensor4 ToHalfColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UIntToHalf); - } - - internal static Tensor4 ToFloatColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UIntToFloat); - } - - internal static Tensor4 ToDoubleColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.UIntToDouble); - } - - #endregion - - #region Half - - internal static Tensor4 ToByteColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.HalfToByte); - } - - internal static Tensor4 ToUShortColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.HalfToUShort); - } - - internal static Tensor4 ToUIntColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.HalfToUInt); - } - - internal static Tensor4 ToFloatColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.HalfToFloat); - } - - internal static Tensor4 ToDoubleColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.HalfToDouble); - } - - #endregion - - #region Float - - internal static Tensor4 ToByteColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.FloatToByte); - } - - internal static Tensor4 ToUShortColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.FloatToUShort); - } - - internal static Tensor4 ToUIntColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.FloatToUInt); - } - - internal static Tensor4 ToHalfColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.FloatToHalf); - } - - internal static Tensor4 ToDoubleColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.FloatToDouble); - } - - #endregion - - #region Double - - internal static Tensor4 ToByteColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.DoubleToByte); - } - - internal static Tensor4 ToUShortColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.DoubleToUShort); - } - - internal static Tensor4 ToUIntColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.DoubleToUInt); - } - internal static Tensor4 ToHalfColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.DoubleToHalf); - } - - internal static Tensor4 ToFloatColor(this Tensor4 tensor4) - { - return tensor4.MapToImageWindow(Col.DoubleToFloat); - } - - #endregion - - #endregion - #region Get/Set Matrix Rows/Cols [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Tests/Aardvark.Base.Tests/Images/PixImageTests.cs b/src/Tests/Aardvark.Base.Tests/Images/PixImageTests.cs index ecc0c187..1624182b 100644 --- a/src/Tests/Aardvark.Base.Tests/Images/PixImageTests.cs +++ b/src/Tests/Aardvark.Base.Tests/Images/PixImageTests.cs @@ -1,9 +1,6 @@ using NUnit.Framework; using System; -using System.Collections.Generic; -using System.Text; using Aardvark.Base; -using System.Runtime.InteropServices; namespace Aardvark.Tests.Images { @@ -107,11 +104,15 @@ private static T GetColor(PixImage pi, V2l coord) private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func, V2l, T1> getInput, Func, V2l, T2> getActual, - Func expectedConversion) + Func expectedConversion, + bool subImageWindow) { var src = new PixImage(sourceFormat, 43, 81); src.Volume.Data.SetByIndex((_) => (byte)rnd.UniformInt(256)); + if (subImageWindow) + src = new PixImage(sourceFormat, src.Volume.SubImageWindow(2, 3, 33, 67)); + var dst = src.ToFormat(targetFormat); src.GetChannel(0L).ForeachCoord((coord) => @@ -122,17 +123,17 @@ private static void FormatConversion(Col.Format sourceFormat, Col.Format }); } - private static void FormatConversionArrays(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetArray, GetArray, expectedConversion); + private static void FormatConversionArrays(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetArray, GetArray, expectedConversion, subImageWindow); - private static void FormatConversionFromArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetArray, GetColor, expectedConversion); + private static void FormatConversionFromArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetArray, GetColor, expectedConversion, subImageWindow); - private static void FormatConversionToArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetColor, GetArray, expectedConversion); + private static void FormatConversionToArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetColor, GetArray, expectedConversion, subImageWindow); - private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetColor, GetColor, expectedConversion); + private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetColor, GetColor, expectedConversion, subImageWindow); #region From Gray @@ -200,10 +201,22 @@ public void FormatConversionGrayAlphaToRG() #region From RGBA + [Test] + public void FormatConversionRGBAToRGBA() + => FormatConversion(Col.Format.RGBA, Col.Format.RGBA, (color => color)); + + [Test] + public void FormatConversionRGBAToRGBAWindow() + => FormatConversion(Col.Format.RGBA, Col.Format.RGBA, (color => color), true); + [Test] public void FormatConversionRGBAToBGRA() => FormatConversion(Col.Format.RGBA, Col.Format.BGRA, color => color); + [Test] + public void FormatConversionRGBAToBGRAWindow() + => FormatConversion(Col.Format.RGBA, Col.Format.BGRA, (color => color), true); + [Test] public void FormatConversionRGBAToRGB() => FormatConversion(Col.Format.RGBA, Col.Format.RGB, color => color.RGB); diff --git a/src/Tests/Aardvark.Base.Tests/Images/PixVolumeTests.cs b/src/Tests/Aardvark.Base.Tests/Images/PixVolumeTests.cs index feac4683..3bfcc260 100644 --- a/src/Tests/Aardvark.Base.Tests/Images/PixVolumeTests.cs +++ b/src/Tests/Aardvark.Base.Tests/Images/PixVolumeTests.cs @@ -24,11 +24,15 @@ private static T GetColor(PixVolume pi, V3l coord) private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func, V3l, T1> getInput, Func, V3l, T2> getActual, - Func expectedConversion) + Func expectedConversion, + bool subImageWindow) { var src = new PixVolume(sourceFormat, 43, 31, 23); src.Tensor4.Data.SetByIndex((_) => (byte)rnd.UniformInt(256)); + if (subImageWindow) + src = new PixVolume(sourceFormat, src.Tensor4.SubImageWindow(11, 7, 3, 27, 13, 8)); + var dst = src.ToFormat(targetFormat); src.GetChannel(0L).ForeachCoord((coord) => @@ -39,17 +43,17 @@ private static void FormatConversion(Col.Format sourceFormat, Col.Format }); } - private static void FormatConversionArrays(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetArray, GetArray, expectedConversion); + private static void FormatConversionArrays(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetArray, GetArray, expectedConversion, subImageWindow); - private static void FormatConversionFromArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetArray, GetColor, expectedConversion); + private static void FormatConversionFromArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetArray, GetColor, expectedConversion, subImageWindow); - private static void FormatConversionToArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetColor, GetArray, expectedConversion); + private static void FormatConversionToArray(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetColor, GetArray, expectedConversion, subImageWindow); - private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion) - => FormatConversion(sourceFormat, targetFormat, GetColor, GetColor, expectedConversion); + private static void FormatConversion(Col.Format sourceFormat, Col.Format targetFormat, Func expectedConversion, bool subImageWindow = false) + => FormatConversion(sourceFormat, targetFormat, GetColor, GetColor, expectedConversion, subImageWindow); #region From Gray @@ -117,10 +121,22 @@ public void FormatConversionGrayAlphaToRG() #region From RGBA + [Test] + public void FormatConversionRGBAToRGBA() + => FormatConversion(Col.Format.RGBA, Col.Format.RGBA, color => color); + + [Test] + public void FormatConversionRGBAToRGBAWindow() + => FormatConversion(Col.Format.RGBA, Col.Format.RGBA, color => color, true); + [Test] public void FormatConversionRGBAToBGRA() => FormatConversion(Col.Format.RGBA, Col.Format.BGRA, color => color); + [Test] + public void FormatConversionRGBAToBGRAWindow() + => FormatConversion(Col.Format.RGBA, Col.Format.BGRA, color => color, true); + [Test] public void FormatConversionRGBAToRGB() => FormatConversion(Col.Format.RGBA, Col.Format.RGB, color => color.RGB);