C#图像调整大小 - 丢失EXIF

8

是的,我看到了其他与此问题相关的帖子,也搜索过了。

但是到目前为止,我还没有得到我需要的结果。

我正在加载一个以300 dpi拍摄的大图像,并且需要调整其大小。

我知道...我知道...dpi是相对的,实际上并不重要...重要的是像素尺寸:

DPI本质上是图像打印时每英寸对应的像素数,而不是在屏幕上查看时。因此,通过增加图像的DPI,您不会增加屏幕上的图像大小。您只会提高打印质量。

尽管存储在图像EXIF中的DPI信息有些无用,但它给我带来了问题。

我正在调整大小的图像丢失了原始的exif信息,包括水平和垂直分辨率(dpi),因此它将默认保存为96 dpi。可能的原因是只有JPEG和另一种格式可以保存元数据信息。

最终的图像结果应该看起来像这样:275x375,300dpi
而实际上却是这样的:275x375,96dpi

你可以说它们是相同的,我也同意,但我们有一个Corel Draw脚本用于加载这些图像,由于dpi信息不同,它会在文档中放置不同的大小。

以下是我用于调整大小的代码:

public System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
    {
        Bitmap result = new Bitmap(width, height);

        // set the resolutions the same to avoid cropping due to resolution differences
        result.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        //use a graphics object to draw the resized image into the bitmap
        using (Graphics graphics = Graphics.FromImage(result))
        {
            //set the resize quality modes to high quality
            graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            //draw the image into the target bitmap
            graphics.DrawImage(image, 0, 0, result.Width, result.Height);
        }

        //return the resulting bitmap
        return result;
    }

这种方法可以很好地完成工作,但会丢失EXIF信息。

将SetResolution设置为SetResolution(300, 300)无效!

我查看了读取和更改图像的EXIF信息,并尝试了以下方法:

public void setImageDpi(string Filename, string NewRes)
    {
        Image Pic;
        PropertyItem[] PropertyItems;
        byte[] bDescription = new Byte[NewRes.Length];
        int i;
        string FilenameTemp;
        System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
        EncoderParameters EncParms = new EncoderParameters(1);
        EncoderParameter EncParm;
        ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");

        // copy description into byte array
        for (i = 0; i < NewRes.Length; i++) bDescription[i] = (byte)NewRes[i];

        // load the image to change
        Pic = Image.FromFile(Filename);

        foreach (PropertyItem item in Pic.PropertyItems)
        {
            if (item.Id == 282 || item.Id == 283)
            {
                PropertyItem myProperty = item;
                myProperty.Value = bDescription;
                myProperty.Type = 2;
                myProperty.Len = NewRes.Length;
                Pic.SetPropertyItem(item);
                Console.WriteLine(item.Type);
            }
        }

        // we cannot store in the same image, so use a temporary image instead
        FilenameTemp = Filename + ".temp";

        // for lossless rewriting must rotate the image by 90 degrees!
        EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
        EncParms.Param[0] = EncParm;

        // now write the rotated image with new description
        Pic.Save(FilenameTemp, CodecInfo, EncParms);

        // for computers with low memory and large pictures: release memory now
        Pic.Dispose();
        Pic = null;
        GC.Collect();

        // delete the original file, will be replaced later
        System.IO.File.Delete(Filename);

        // now must rotate back the written picture
        Pic = Image.FromFile(FilenameTemp);
        EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate270);
        EncParms.Param[0] = EncParm;
        Pic.Save(Filename, CodecInfo, EncParms);

        // release memory now
        Pic.Dispose();
        Pic = null;
        GC.Collect();

        // delete the temporary picture
        System.IO.File.Delete(FilenameTemp);
    }

这也没有起作用。

我尝试在后续过程中查找并更改DPI的EXIF信息(282和283),方法如下:

        Encoding _Encoding = Encoding.UTF8;
        Image theImage = Image.FromFile("somepath");

        PropertyItem propItem282 = theImage.GetPropertyItem(282);
        propItem282.Value = _Encoding.GetBytes("300" + '\0');
        theImage.SetPropertyItem(propItem282);

        PropertyItem propItem283 = theImage.GetPropertyItem(283);
        propItem283.Value = _Encoding.GetBytes("300" + '\0');
        theImage.SetPropertyItem(propItem283);

        theImage.Save("somepath");

但程序崩溃并提示“找不到属性”。 如果该属性不存在,显然我无法添加它: 一个PropertyItem对象不是用作独立对象的。 PropertyItem对象旨在由Image派生类使用。 PropertyItem对象用于检索和更改现有图像文件的元数据,而不是创建元数据。 因此,PropertyItem类没有定义公共构造函数,并且您不能创建PropertyItem对象的实例。 我陷入了困境...我只需要一个分辨率为300 dpi的调整大小后的图像,这应该不难。 任何帮助都会受到赞赏。谢谢
1个回答

21

以下代码对我有效:

const string InputFileName = "test_input.jpg";
const string OutputFileName = "test_output.jpg";
var newSize = new Size(640, 480);

using (var bmpInput = Image.FromFile(InputFileName))
{
    using (var bmpOutput = new Bitmap(bmpInput, newSize))
    {
        foreach (var id in bmpInput.PropertyIdList)
            bmpOutput.SetPropertyItem(bmpInput.GetPropertyItem(id));
        bmpOutput.SetResolution(300.0f, 300.0f);
        bmpOutput.Save(OutputFileName, ImageFormat.Jpeg);
    }
}

我查看输出文件时发现EXIF数据已更改,DPI已更改为300。


足够简单。工作得很好。我不喜欢必须有一个输出文件这一事实,但显然没有其他办法。非常感谢,我已经找了一段时间这个解决方案。 - Naner
太好了。运行良好。谢谢。 - Muhamad Jafarnejad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接