0

Is there a way to get the size of an image without having to load an entire Image object from the bytes?

I'm doing it like this right now:

private Size GetImageSize(byte[] data)
{
     using(Stream dataStream = new MemoryStream(data))
     using(Image image = Image.FromStream(dataStream))
         return image.Size;
}

But as you can see I am creating a new MemoryStream and copying all the image data, wasting space, just to get the size of the image.

Matias Cicero
  • 25,439
  • 13
  • 82
  • 154

2 Answers2

0

Although you are creating the new image object to get it's size, it will be immediately destroyed and the memory will be released. Your using statements will destroy them both when done.
Are these very large images that you need to worry about the size? I would think not since you already have the bytes stored in memory prior to calling this method.

Looks like there may be another method here using a BinaryReader Getting image dimensions without reading the entire file

Community
  • 1
  • 1
hawkstrider
  • 4,141
  • 16
  • 27
  • It won't be necessarily destroyed immediately. Garbage collector will collect the image object when it wishes. – wingerse Oct 08 '15 at 21:09
  • Let's suppose the image is 1MB. To get the size of the image I would need to allocate a new 1MB. This means that I have to spend 2MB of RAM just to get the size. Consider now more than 1000 users trying to get the size of the image at the same time, instead of requiring 1GB of RAM, I would need 2GB! – Matias Cicero Oct 09 '15 at 03:59
0

I think your code is the correct way to go, just use the Image.FromStream Method (Stream, Boolean, Boolean) overload in order to skip image data validation like this

static Size GetImageSize(byte[] data)
{
     using(var dataStream = new MemoryStream(data))
     using(var image = Image.FromStream(dataStream, false, false))
         return image.Size;
}

But as you can see I am creating a new MemoryStream and copying all the image data, wasting space, just to get the size of the image.

First, the MemoryStream constructor used does not copy any data, but just creates a Stream wrapper (view) of the passed []byte.

Second, although not guaranteed, you can rely on GDI+ image optimizations of delayed data processing/buffer allocation with a little hint of passing validateImageData=false.

Here is a code that can be used to try to see what's going on:

public struct ReadInfo
{
    public long Position;
    public int Count;
    public override string ToString() { return "Pos: " + Position + " Count: " + Count; }
}
class TrackingMemoryStream : MemoryStream
{
    public TrackingMemoryStream(byte[] buffer) : base(buffer) { }
    public int TotalReadCount;
    public List<ReadInfo> ReadInfo = new List<ReadInfo>();
    public override int Read(byte[] buffer, int offset, int count)
    {
        var info = new ReadInfo { Position = Position };
        info.Count = base.Read(buffer, offset, count);
        ReadInfo.Add(info);
        TotalReadCount += info.Count;
        return info.Count;
    }
}

static Size GetImageSize(byte[] data)
{
    using (var dataStream = new TrackingMemoryStream(data))
    using (var image = Image.FromStream(dataStream, false, false))
    {
        var size = image.Size;
        return size;
    }
}

You can put a breakpoint at return size; line and examine the dataStream tracking info. Depending of the decoder (format), you'll see a different information, but in any case there are quite small chunk reads compared to the data size. Of course there is no guarantee that the internal buffer for holding the whole image pixel data has not been allocated, but there is a big chance of that being deferred. Note that if you expand the image variable in the debugger, you'll see a very different tracking stream information, so at that point most probably the pixel data buffer has been allocated.

To conclude, in this case I would rely on decoders to get correctly the information I need and will not be concerned about memory allocation.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343