2

I have a linear array which I need to reshape as of a stack of 2D data. In this particular case, the stack only contains one element so the output should be an array with dimensions (height, width, 1).

This is related to a previous question, where I was asking about the same operation in the other direction (3D to 1D).

What is the fastest way to map this data to a 3D array?

I was planning to take the following approach:

    public static byte[, ,] ToBuffer3D<TDepth>(this byte[] buffer, int w, int h)
    {
        byte[, ,] buff3D = new byte[h, w, 1];

        for (int i = 0; i < buffer.Length; i++)
        {
            buff3D[(int)Math.Floor(i / (double)w), i % w, 0] = buffer[i];
        }

        return buff3D;
    }

But it seems like it might be possible to take advantage of how the data is already stored in memory to copy more than one element at a time. Is there some other mapping approach that could be employed in C#?

Community
  • 1
  • 1
dionys
  • 151
  • 8
  • 1
    If it goes one way it goes the other. Do the same thing as your prior question just reverse the direction – Hogan Nov 04 '14 at 12:36
  • 1
    possible duplicate of [How to map a 3D array to a linear array?](http://stackoverflow.com/questions/26705659/how-to-map-a-3d-array-to-a-linear-array) – Hogan Nov 04 '14 at 12:36
  • @dionys Lucky you, you got just that! If you followed my advice instead of making a snarky comment you would already have the best possible solution – Hogan Nov 04 '14 at 12:47
  • 1
    @jodrell a jagged improves memory usage not performance – Hogan Nov 04 '14 at 12:49
  • 1
    If this another API is .NET then there is no way to avoid copying data. In safe C# you can't just cast something to something different, like you could in C. I don't know about *unsafe* code, though. – Dialecticus Nov 04 '14 at 12:51
  • @dionys alternatively, you could not copy at all. – Jodrell Nov 04 '14 at 15:17
  • 1
    @Hogan MD arrays are slower than 0 based jagged arrays. http://stackoverflow.com/questions/468832/why-are-multi-dimensional-arrays-in-net-slower-than-normal-arrays – Jodrell Nov 04 '14 at 15:20

2 Answers2

2

This is likely to be somewhat faster:

public static byte[,,] ToBuffer3Da(this byte[] buffer, int w, int h)
{
    byte[,,] buff3D = new byte[h, w, 1];
    Buffer.BlockCopy(buffer, 0, buff3D, 0, h*w);
    return buff3D;
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

if you use the base class and implementation defined below, you have a class that supports both linear and dimensional indexing at the same time. No conversion or copying is necessary.

Use it like this,

var matrix = new DecomposedMatrix<byte>(10, 10, 10)

foreach(var b int matrix)
{
    ...
}

for (var i = 0; i < matrix.Count; i++)
{
    ...
    var item = matrix[i];
    ...
}

for (var x = 0; x < matrix.H; x++)
for (var y = 0; y < matrix.W; y++)
for (var z = 0; z < matrix.D; z++)
{
    ...
    var item = matrix[x, y, z];
    ...
}

classes follow ...

public abstract class DecomposedMatrix
{
    private readonly int h;
    private readonly int w;
    private readonly int d;

    protected DecomposedMatrix(int h, int w, int d)
    {
        this.h = h;
        this.w = w;
        this.d = d;
    }

    public int W
    {
        get
        {
            return this.w;
        }
    }

    public int D
    {
        get
        {
            return this.d;
        }
    }

    public int H
    {
        get
        {
            return this.h;
        }
    }

    protected int DereferenceCoordinates(int x, int y, int z)
    {
        if (x >= this.H || y >= this.W || z >= this.D)
        {
            throw new IndexOutOfRangeException();
        }

        if (x < 0 || y < 0 || z < 0)
        {
            throw new IndexOutOfRangeException();
        }

        return z + (y * this.D) + (x * this.W * this.D);
    }
}

and the implementation

public class DecomposedMatrix<T> : DecomposedMatrix, IReadOnlyList<T>
{
    private readonly IList<T> data;

    public DecomposedMatrix(int h, int w, int d)
            : base(h, w, d)
    {
        this.data = new T[h * w * d];
    }

    public T this[int index]
    {
        get
        {
            return this.data[index];
        }

        set
        {
            this.data[index] = value;
        }
    }

    public T this[int x, int y, int z]
    {
        get
        {
            return this.data[this.DereferenceCoordinates(x, y, z)];
        }

        set
        {
            this.data[this.DereferenceCoordinates(x, y, z)] = value;
        }
    }

    public int Count
    {
        get
        {
            return this.data.Count;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this.data.GetEnumerator();
    }

    public IEnumerator IEnumerable.GetEnumerator()
    {
        return this.data.GetEnumerator();
    }
}
Jodrell
  • 34,946
  • 5
  • 87
  • 124