Skip to content

Commit

Permalink
[Tensors] Cleanup of PixImage and PixVolume
Browse files Browse the repository at this point in the history
- Deprecated Array in favor of Data
- Deprecated PixImage.IntStride in favor of Stride and StrideL
- PixVolume implements IPixImage3d
- Added PixVolume.BytesPerChannel
- Added PixVolume.ToCanonicalDenseLayout
  • Loading branch information
hyazinthh committed Jun 24, 2024
1 parent b966304 commit e28203c
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 264 deletions.
8 changes: 7 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
- Fixed return type of LengthSquared for integer-based vectors. Now returns an integer instead of double.
- Added missing readonly modifiers for structs
- Renamed CIeLuvf to CieLuvf
- Cleanup of `PixImage` and `PixVolume`:
- Deprecated `Array` in favor of `Data`
- Deprecated `PixImage.IntStride` in favor of `Stride` and `StrideL`
- `PixVolume` implements `IPixImage3d`
- Added `PixVolume.BytesPerChannel`
- Added `PixVolume.ToCanonicalDenseLayout`
- Removed obsolete loading API
- Removed obsolete API:
- [Color] obsolete conversion functions
- [Color] `Parse()` overload with IFormatProvider parameter
- [PixImage] obsolete loading API
- `Async.AwaitTask` (already in FSharp.Core)
- `Caching.cacheFunction`
- `IDictionary.GetValueOrDefault`
Expand Down
243 changes: 123 additions & 120 deletions src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Reflection;
using System.Text;
using System.Threading;
using System.Drawing;

namespace Aardvark.Base
{
Expand Down Expand Up @@ -179,7 +178,7 @@ public static List<IPixLoader> GetLoaders()
{
lock (s_loaders)
{
var list = s_loaders.ToList();
var list = s_loaders.ToList();
list.Sort((x, y) => y.Value - x.Value);
return list.Map(x => x.Key);
}
Expand Down Expand Up @@ -299,17 +298,47 @@ public PixImage(Col.Format format)

#region Properties

public PixImageInfo Info { get { return new PixImageInfo(PixFormat, Size); } }
public abstract Array Data { get; }

public abstract PixFormat PixFormat { get; }

public abstract VolumeInfo VolumeInfo { get; }

public abstract int BytesPerChannel { get; }

public PixImageInfo Info => new PixImageInfo(PixFormat, Size);

/// <summary>
/// Size.X * Size.Y.
/// </summary>
public int NumberOfPixels { get { return Size.X * Size.Y; } }
public int NumberOfPixels => Size.X * Size.Y;

/// <summary>
/// Width/height.
/// </summary>
public double AspectRatio { get { return Size.X / (double)Size.Y; } }
public double AspectRatio => Size.X / (double)Size.Y;

public V2i Size => (V2i)VolumeInfo.Size.XY;

public V2l SizeL => VolumeInfo.Size.XY;

public int ChannelCount => (int)VolumeInfo.Size.Z;

public long ChannelCountL => VolumeInfo.Size.Z;

public int Stride => BytesPerChannel * (int)VolumeInfo.DY;

public long StrideL => BytesPerChannel * VolumeInfo.DY;

#region Obsolete

[Obsolete("Use Data instead.")]
public Array Array => Data;

[Obsolete("Use Stride instead.")]
public int IntStride => Stride;

#endregion

#endregion

Expand Down Expand Up @@ -650,7 +679,7 @@ public void SaveAsJpeg(string filename, int quality = PixJpegSaveParams.DefaultQ
/// <param name="loader">The loader to use, or null if no specific loader is to be used.</param>
/// <exception cref="ImageLoadException">if the image could not be saved.</exception>
public void SaveAsPng(string filename, int compressionLevel = PixPngSaveParams.DefaultCompressionLevel,
bool normalizeFilename = true, IPixLoader loader = null)
bool normalizeFilename = true, IPixLoader loader = null)
=> Save(filename, new PixPngSaveParams(compressionLevel), normalizeFilename, loader);

#endregion
Expand Down Expand Up @@ -809,67 +838,43 @@ public static PixImageInfo GetInfoFromStream(Stream stream, IPixLoader loader =

public PixImage<T1> ToPixImage<T1>() => AsPixImage<T1>() ?? new PixImage<T1>(this);

public abstract PixImage ToPixImage(Col.Format format);

public PixImage<T> ToPixImage<T>(Col.Format format)
{
if (this is PixImage<T> castImage && castImage.Format == format && castImage.ChannelCount == format.ChannelCount())
return castImage;
return new PixImage<T>(format, this);
}

#endregion

#region Abstract Methods

public abstract PixFormat PixFormat { get; }

public abstract VolumeInfo VolumeInfo { get; }

public abstract V2i Size { get; }
public abstract V2l SizeL { get; }

public abstract int ChannelCount { get; }
public abstract long ChannelCountL { get; }
public abstract PixImage ToCanonicalDenseLayout();

public abstract Array Array { get; }
#endregion

public abstract int IntStride { get; }
#region Copy

public abstract void CopyChannelTo<Tv>(long channelIndex, Matrix<Tv> target);

public abstract void CopyVolumeTo<Tv>(Volume<Tv> target);

public abstract PixImage ToPixImage(Col.Format format);

public abstract PixImage CopyToPixImage();

public abstract PixImage CopyToPixImageWithCanonicalDenseLayout();

public abstract PixImage ToCanonicalDenseLayout();

public abstract Array Data { get; }

public abstract T Visit<T>(IPixImageVisitor<T> visitor);

#endregion

#region Image Manipulation (abstract)
#region Image Manipulation

public abstract PixImage Transformed(ImageTrafo trafo);

public abstract PixImage RemappedPixImage(
Matrix<float> xMap, Matrix<float> yMap,
ImageInterpolation ip = ImageInterpolation.Cubic);
public abstract PixImage RemappedPixImage(Matrix<float> xMap, Matrix<float> yMap, ImageInterpolation ip = ImageInterpolation.Cubic);

public abstract PixImage ResizedPixImage(
V2i size, ImageInterpolation ip = ImageInterpolation.Cubic);
public abstract PixImage ResizedPixImage(V2i size, ImageInterpolation ip = ImageInterpolation.Cubic);

public abstract PixImage RotatedPixImage(
double angleInRadiansCCW, bool resize = true,
ImageInterpolation ip = ImageInterpolation.Cubic);
public abstract PixImage RotatedPixImage(double angleInRadiansCCW, bool resize = true, ImageInterpolation ip = ImageInterpolation.Cubic);

public abstract PixImage ScaledPixImage(
V2d scaleFactor,
ImageInterpolation ip = ImageInterpolation.Cubic);
public abstract PixImage ScaledPixImage(V2d scaleFactor, ImageInterpolation ip = ImageInterpolation.Cubic);

#endregion

Expand Down Expand Up @@ -915,6 +920,12 @@ public static void ExpandPixels(PixImage<byte> bitImage, PixImage<byte> pixImage

#endregion

#region IPixImageVisitor

public abstract T Visit<T>(IPixImageVisitor<T> visitor);

#endregion

#region IPix

public Tr Op<Tr>(IPixOp<Tr> op) { return op.PixImage(this); }
Expand All @@ -927,7 +938,7 @@ public static void ExpandPixels(PixImage<byte> bitImage, PixImage<byte> pixImage
/// is specified as type parameter.
/// </summary>
[Serializable]
public partial class PixImage<T> : PixImage //, IPixImage2d
public partial class PixImage<T> : PixImage
{
public Volume<T> Volume;

Expand Down Expand Up @@ -1148,46 +1159,6 @@ public PixImage(Col.Format format, PixImage pixImage)
Format = format;
}

public PixImage<T> CopyToImageLayout()
{
if (Volume.HasImageLayout())
return new PixImage<T>(Format, Volume.CopyToImage());
return new PixImage<T>(this);
}

public PixImage<T> Copy() => new PixImage<T>(Format, Volume.CopyToImageWindow());

public override PixImage CopyToPixImage() => Copy();

public override PixImage CopyToPixImageWithCanonicalDenseLayout() => CopyToImageLayout();

public override PixImage ToCanonicalDenseLayout() => ToImageLayout();

/// <summary>
/// Copy function for color conversions.
/// </summary>
/// <typeparam name="Tv"></typeparam>
/// <param name="fun"></param>
/// <returns></returns>
public PixImage<T> Copy<Tv>(Func<Tv, Tv> fun) => Copy<Tv>(fun, Format);

/// <summary>
/// Copy function for color conversions. Note that the
/// new color format must have the same number of channels
/// as the old one, and the result of the supplied conversion
/// function is reinterpreted as a color in the new format.
/// </summary>
/// <typeparam name="Tv"></typeparam>
/// <param name="fun"></param>
/// <param name="format"></param>
/// <returns></returns>
public PixImage<T> Copy<Tv>(Func<Tv, Tv> fun, Col.Format format)
{
var mat = GetMatrix<Tv>().MapWindow(fun);
var vol = new Volume<T>(mat.Data, Volume.Info);
return new PixImage<T>(format, vol);
}

#endregion

#region Constructors from File / Stream
Expand Down Expand Up @@ -1273,15 +1244,13 @@ public static PixImage<T> Create<Td>(Col.Format format, params Matrix<Td, T>[] c

#region Properties

public override VolumeInfo VolumeInfo => Volume.Info;

public override V2i Size => (V2i)Volume.Info.Size.XY;
public override Array Data => Volume.Data;

public override V2l SizeL => Volume.Info.Size.XY;
public override PixFormat PixFormat => new PixFormat(typeof(T), Format);

public override int ChannelCount => (int)Volume.Info.Size.Z;
public override VolumeInfo VolumeInfo => Volume.Info;

public override long ChannelCountL => Volume.Info.Size.Z;
public override int BytesPerChannel => typeof(T).GetCLRSize();

/// <summary>
/// Returns the channels of the image in canonical order: red, green,
Expand All @@ -1303,12 +1272,6 @@ public IEnumerable<Matrix<T>> Channels
/// </summary>
public Matrix<T>[] ChannelArray => Channels.ToArray();

public int BytesPerChannel => System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));

public override Array Array => Volume.Data;

public override int IntStride => BytesPerChannel * (int)Volume.DY;

/// <summary>
/// Returns the matrix representation of the volume if there is only
/// one channel. Fails if there are multiple channels.
Expand Down Expand Up @@ -1571,10 +1534,75 @@ public void SetResized(

#region Conversions

public PixImage<T> ToImageLayout() => !Volume.HasImageLayout() ? new PixImage<T>(Format, this) : this;
public override PixImage ToPixImage(Col.Format format)
{
if (Format == format && ChannelCount == format.ChannelCount())
return this;
return new PixImage<T>(format, this);
}

public PixImage<T> ToFormat(Col.Format format) => Format == format ? this : new PixImage<T>(format, this);

public PixImage<T> ToImageLayout() => !Volume.HasImageLayout() ? new PixImage<T>(Format, this) : this;

public override PixImage ToCanonicalDenseLayout() => ToImageLayout();

#endregion

#region Copy

public override void CopyChannelTo<Tv>(long channelIndex, Matrix<Tv> target)
{
var subMatrix = GetChannel<Tv>(channelIndex);
target.Set(subMatrix);
}

public override void CopyVolumeTo<Tv>(Volume<Tv> target)
{
if (Volume is Volume<Tv> source)
target.Set(source);
else
target.Set(Volume.AsVolume<T, Tv>());
}

public PixImage<T> CopyToImageLayout()
{
if (Volume.HasImageLayout())
return new PixImage<T>(Format, Volume.CopyToImage());
return new PixImage<T>(this);
}

public PixImage<T> Copy() => new PixImage<T>(Format, Volume.CopyToImageWindow());

public override PixImage CopyToPixImage() => Copy();

public override PixImage CopyToPixImageWithCanonicalDenseLayout() => CopyToImageLayout();

/// <summary>
/// Copy function for color conversions.
/// </summary>
/// <typeparam name="Tv"></typeparam>
/// <param name="fun"></param>
/// <returns></returns>
public PixImage<T> Copy<Tv>(Func<Tv, Tv> fun) => Copy<Tv>(fun, Format);

/// <summary>
/// Copy function for color conversions. Note that the
/// new color format must have the same number of channels
/// as the old one, and the result of the supplied conversion
/// function is reinterpreted as a color in the new format.
/// </summary>
/// <typeparam name="Tv"></typeparam>
/// <param name="fun"></param>
/// <param name="format"></param>
/// <returns></returns>
public PixImage<T> Copy<Tv>(Func<Tv, Tv> fun, Col.Format format)
{
var mat = GetMatrix<Tv>().MapWindow(fun);
var vol = new Volume<T>(mat.Data, Volume.Info);
return new PixImage<T>(format, vol);
}

#endregion

#region Obtaining Matrices
Expand Down Expand Up @@ -1625,32 +1653,7 @@ public Matrix<T, Tv> GetChannelInFormatOrder<Tv>(long formatChannelIndex)

#endregion

#region Concrete Implementation Of Abstract Functions

public override PixFormat PixFormat => new PixFormat(typeof(T), Format);

public override void CopyChannelTo<Tv>(long channelIndex, Matrix<Tv> target)
{
var subMatrix = GetChannel<Tv>(channelIndex);
target.Set(subMatrix);
}

public override void CopyVolumeTo<Tv>(Volume<Tv> target)
{
if (Volume is Volume<Tv> source)
target.Set(source);
else
target.Set(Volume.AsVolume<T, Tv>());
}

public override PixImage ToPixImage(Col.Format format)
{
if (Format == format && ChannelCount == format.ChannelCount())
return this;
return new PixImage<T>(format, this);
}

public override Array Data => Volume.HasImageLayout() ? Volume.Data : throw new ArgumentException(nameof(Volume));
#region IPixImageVisitor

public override TResult Visit<TResult>(IPixImageVisitor<TResult> visitor) => visitor.Visit<T>(this);

Expand Down
Loading

0 comments on commit e28203c

Please sign in to comment.