Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resize and Save Image While Preserving Metadata

I am attempting to resize and save an image, which is fairly easy (for instance, see this example external example no longer valid).

However, using this code strips the metadata information from the image. I can't quite seem to figure out how to preserve the metadata for a jpeg image.

EDIT: Example Code

public static void ResizeMethodThree(string sourceFile, string targetFile)
{
    byte[] baSource = File.ReadAllBytes(sourceFile);

    PropertyItem[] propertyItems = new Bitmap(sourceFile).PropertyItems;

    using (Stream streamPhoto = new MemoryStream(baSource))
    {
        BitmapFrame bfPhoto = ReadBitmapFrame(streamPhoto);
        BitmapMetadata metaData = (BitmapMetadata)bfPhoto.Metadata;
        int nNewPictureSize = 200;
        int nWidth = 0;
        int nHeight = 0;

        if (bfPhoto.Width > bfPhoto.Height)
        {
            nWidth = nNewPictureSize;
            nHeight = (int)(bfPhoto.Height * nNewPictureSize / bfPhoto.Width);
        }
        else
        {
            nHeight = nNewPictureSize;
            nWidth = (int)(bfPhoto.Width * nNewPictureSize / bfPhoto.Height);
        }        

        BitmapFrame bfResize = ResizeHelper(bfPhoto, nWidth, nHeight, BitmapScalingMode.HighQuality);

        byte[] baResize = ToByteArray(bfResize);

        File.WriteAllBytes(targetFile, baResize);

        Image targetImage = new Bitmap(targetFile);
        foreach (var propertyItem in propertyItems)
        {
            targetImage.SetPropertyItem(propertyItem);
        }

        targetImage.Save(targetFile);
    }
}

public static BitmapFrame ResizeHelper(BitmapFrame photo, int width, 
                                       int height, BitmapScalingMode scalingMode)
{

    var group = new DrawingGroup();
    RenderOptions.SetBitmapScalingMode(
        group, scalingMode);
    group.Children.Add(
        new ImageDrawing(photo,
            new Rect(0, 0, width, height)));
    var targetVisual = new DrawingVisual();
    var targetContext = targetVisual.RenderOpen();
    targetContext.DrawDrawing(group);
    var target = new RenderTargetBitmap(
        width, height, 96, 96, PixelFormats.Default);
    targetContext.Close();
    target.Render(targetVisual);
    var targetFrame = BitmapFrame.Create(target);
    return targetFrame;
}

private static byte[] ToByteArray(BitmapFrame bfResize)
{
    using (MemoryStream msStream = new MemoryStream())
    {
        JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
        jpgEncoder.Frames.Add(bfResize);
        jpgEncoder.Save(msStream);
        return msStream.ToArray();
    }
}

private static BitmapFrame ReadBitmapFrame(Stream streamPhoto)
{
    BitmapDecoder bdDecoder = 
        BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
    return bdDecoder.Frames[0];
}
like image 747
Wonko the Sane Avatar asked Aug 20 '10 14:08

Wonko the Sane


1 Answers

Use the Image.PropertyItems property on the source image to get the list of metadata items. Loop through the list, calling Image.SetPropertyItem on the destination image. You should normally avoid resizing and re-compressing a jpeg image, working from an uncompressed original is best to maintain quality and avoid artifacts.

like image 52
Hans Passant Avatar answered Nov 13 '22 04:11

Hans Passant