14

Size image placed in byte[] array (don't know the type of image). I have to produce another byte [] array, which size should be up to 50kB. How can I do some kind of scaling?

Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
Fishman
  • 1,737
  • 1
  • 25
  • 42

6 Answers6

40

Unless you want to get into some serious math, you need to load your byte array into a memory stream, load an image from that memory stream, and use the built-in GDI functions in the System.Drawing namespace.

Doing a 25%, or 50% scale is easy. Beyond that, you need to start doing interpolation and differencing to make anything look halfway decent in binary data manipulation. You'll be several days into it before you can match what's already available in GDI.

System.IO.MemoryStream myMemStream = new System.IO.MemoryStream(myBytes);
System.Drawing.Image fullsizeImage = System.Drawing.Image.FromStream(myMemStream);
System.Drawing.Image newImage = fullsizeImage .GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
System.IO.MemoryStream myResult = new System.IO.MemoryStream();
newImage.Save(myResult ,System.Drawing.Imaging.ImageFormat.Gif);  //Or whatever format you want.
return  myResult.ToArray();  //Returns a new byte array.

BTW - if you really need to figure out your source image type, see: How to check if a byte array is a valid image

Community
  • 1
  • 1
Wesley Long
  • 1,708
  • 10
  • 20
  • Other than adding a "Using" statement on the stream functions, yes. This should be the answer. – DFTR Aug 07 '15 at 15:26
  • not "a" using. 4 usings are needed there. Great answer though. – yazanpro Jan 28 '16 at 18:27
  • @Yazanpro - I just threw up the relevant functions. If you want to flesh it out to a production-worthy block, be my guest. – Wesley Long Feb 05 '16 at 17:44
  • Will this approach work in .NET Core for Linux? As far as I know, System.Drawing is a Windows-specific namespace that is not a part of the .net now. It is available only as a separate NuGet. – Grigory Zhadko Jul 12 '21 at 06:48
  • 1
    @GrigoryZhadko Read more here: https://learn.microsoft.com/en-us/dotnet/api/system.drawing?view=net-5.0#remarks System.Drawing depends on windows-specific api GDI+. You can get some functionality on linux by using System.Drawing.Common along with linux package libgdxplus which is not installed on most distros. – Pontus Magnusson Jul 22 '21 at 14:57
9

Ok, so after some experiments, I have something like that:

public static byte[] Resize2Max50Kbytes(byte[] byteImageIn)
{
    byte[] currentByteImageArray = byteImageIn;
    double scale = 1f;

    if (!IsValidImage(byteImageIn))
    {
        return null;
    }

    MemoryStream inputMemoryStream = new MemoryStream(byteImageIn);
    Image fullsizeImage = Image.FromStream(inputMemoryStream);

    while (currentByteImageArray.Length > 50000)
    {
        Bitmap fullSizeBitmap = new Bitmap(fullsizeImage, new Size((int)(fullsizeImage.Width * scale), (int)(fullsizeImage.Height * scale)));
        MemoryStream resultStream = new MemoryStream();

        fullSizeBitmap.Save(resultStream, fullsizeImage.RawFormat);

        currentByteImageArray = resultStream.ToArray();
        resultStream.Dispose();
        resultStream.Close();

        scale -= 0.05f;
    }

    return currentByteImageArray;
}

Has someone another idea? Unfortunatelly Image.GetThumbnailImage() was causing very dirty images.

Fishman
  • 1,737
  • 1
  • 25
  • 42
3

The updated answer below works with Docker SixLabors.ImageSharp.

The solution for .net core 3.1 and greater:

  1. Install System.Drawing.Common nuget:
Install-Package System.Drawing.Common
  1. The code to change the size of the image from a byte array:
byte[] ReduceImage(byte[] bytes)
{
    using var memoryStream = new MemoryStream(bytes);
    using var originalImage = new Bitmap(memoryStream);
    var resized = new Bitmap(newWidth, newHeight);
    using var graphics = Graphics.FromImage(resized);
    graphics.CompositingQuality = CompositingQuality.HighSpeed;
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    graphics.CompositingMode = CompositingMode.SourceCopy;
    graphics.DrawImage(originalImage, 0, 0, reducedWidth, reducedHeight);
    using var stream = new MemoryStream();
    resized.Save(stream, ImageFormat.Png);
    return stream.ToArray();
}

Update: The approach above can don't work for Linux, so the universal solution is:

  1. Install SixLabors.ImageSharp nuget:
Install-Package SixLabors.ImageSharp
  1. Write the following code:
private static byte[] ReduceImage(byte[] bytes)
{
    using var memoryStream = new MemoryStream(bytes);
    using var image = Image.Load(memoryStream);
    image.Mutate(x => x.Resize(ReducedWidth, ReducedHeight));
    using var outputStream = new MemoryStream();
    image.Save(outputStream, new PngEncoder() /*or another encoder*/); 
    return outputStream.ToArray();
}
Mohammed Osman
  • 3,688
  • 2
  • 27
  • 25
Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
1

Suppose you read a file from Drive

 FileStream streamObj = System.IO.File.OpenRead(@"C:\Files\Photo.jpg");

Byte[] newImage=UploadFoto(streamObj); 
 
  public static Byte[] UploadFoto(FileStream fileUpload)
        {
            Byte[] imgByte = null;
            imgByte = lnkUpload(fileUpload);
            return imgByte;
        }
        
        
         private static Byte[] lnkUpload(FileStream img)
        {
            byte[] resizedImage;
            using (Image orginalImage = Image.FromStream(img))
            {
                ImageFormat orginalImageFormat = orginalImage.RawFormat;
                int orginalImageWidth = orginalImage.Width;
                int orginalImageHeight = orginalImage.Height;
                int resizedImageWidth = 60; // Type here the width you want
                int resizedImageHeight = Convert.ToInt32(resizedImageWidth * orginalImageHeight / orginalImageWidth);
                using (Bitmap bitmapResized = new Bitmap(orginalImage, resizedImageWidth, resizedImageHeight))
                {
                    using (MemoryStream streamResized = new MemoryStream())
                    {
                        bitmapResized.Save(streamResized, orginalImageFormat);
                        resizedImage = streamResized.ToArray();
                    }
                }
            }
            return resizedImage;
        }
Diego
  • 2,238
  • 4
  • 31
  • 68
0

I have no definite implementation for you, but I would approach it that way:

You can store 51200 values (uncompressed). And you know the ratio from the original: Calculate the dimensions with the ratio and the size of the new image:

x = y / ratio

size(51200) = x * y
y = size / x

x = (size / x) / ratio;
y = x * ratio

for the resampling of the values I would go for using a filter kernel: http://en.wikipedia.org/wiki/Lanczos_resampling

Haven't used it yet, but sounds promising.

v01pe
  • 1,096
  • 2
  • 11
  • 19
-3

I am used this....

    public static byte[] ImagenToByteArray(System.Drawing.Image imageIn)
    {
        MemoryStream ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
        return ms.ToArray();
    }