简体   繁体   English

C#更改上传图像的dpi

[英]C# change dpi of an uploaded image

I've got to following function which is called to change the resolution of an image. 我必须遵循被调用来改变图像分辨率的函数。 I want to do this so uploaded image with for example 300dpi will be modified to 72dpi (for web). 我想这样做,所以上传的图像例如300dpi将被修改为72dpi(对于web)。 This question is related to another question here on SO where i'm working on. 这个问题与我在哪里工作的另一个问题有关。

I'm creation an extension method for this to be able to use this function on more places in my application, instead of only when uploading new files. 我创建了一个扩展方法,可以在我的应用程序中的更多位置使用此功能,而不是仅在上载新文件时。 (See above mentioned question) (见上述问题)

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
{    
    using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
    {
        memoryStream.Write(imageToFit, 0, imageToFit.Length);
        var originalImage = new Bitmap(memoryStream);

        using (var canvas = Graphics.FromImage(originalImage))
        {
            canvas.SmoothingMode = SmoothingMode.AntiAlias;
            canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
            canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
            canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);

            newBitmap.SetResolution(72, 72);
            newBitmap.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), null);
        }
        return newMemoryStream.ToArray();
    }
}

The mentioned extension methode is being called in a function similar to the situation below; 提到的扩展方法在类似于下面情况的函数中被调用;

if (newSize.Width > originalImage.Width && newSize.Height > originalImage.Height)
{
     newSize.Width = originalImage.Width;
     newSize.Height = originalImage.Height;

     uploadedFileBuffer = uploadedFileBuffer.SetDpiTo72(uploadedFile.ContentType, newSize);

     return CreateFile(newSize, uploadedFile, uploadedFileBuffer);
}

The bytearray coming in is the file as an bytearray. 进入的bytearray是一个bytearray文件。 It already has the correct size, but I want to change the resolution to 72dpi. 它已经具有正确的大小,但我想将分辨率更改为72dpi。 However after exectution and saving the image the resolution is still the originale entered resolution, which is 300dpi. 但是在执行完并保存图像后,分辨率仍然是原始输入的分辨率,即300dpi。 How can I do this? 我怎样才能做到这一点?

UPDATE AFTER SEVERAL ANSWERS: 几个答案后的更新:

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
        {
            using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
            {
                memoryStream.Write(imageToFit, 0, imageToFit.Length);
                var originalImage = new Bitmap(memoryStream);

                using (var canvas = Graphics.FromImage(originalImage))
                {
                    canvas.SmoothingMode = SmoothingMode.AntiAlias;
                    canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);

                    originalImage.SetResolution(72, 72);

                    var epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);
                    var epParameters = new EncoderParameters(1);
                    epParameters.Param[0] = epQuality;

                    Image newimg = Image.FromStream(memoryStream);

                    //Getting an GDI+ exception after the execution of this line.
                    newimg.Save("C:\\test1234.jpg", ImageFunctions.GetEncoderInfo(mimeType), epParameters);

                    originalImage.Save("test.jpg", ImageFormat.Jpeg);

                    //This line give me an Argumentexception - Parameter is not valid.
                    //originalImage.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), epParameters);
                    //newMemoryStream.Close();
                }
                return newMemoryStream.ToArray();
            }
        }

The stackstrace which comes with the exception is telling me the following; 除了异常之外的stackstrace告诉我以下内容;

   at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
   at Extensions.ByteArrayExtensions.SetDpiTo72(Byte[] imageToFit, String mimeType, Size newSize) in C:\Website\Project\Extensions\ByteArrayExtensions.cs:line 356
   at CMS.Presentation.FileFunctions.CreateFullsizeImage(HttpPostedFileBase uploadedFile, Size newSize, Byte[] uploadedFileBuffer) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 197
   at CMS.Presentation.FileFunctions.CreateFile(HttpPostedFileBase uploadedFile, INodeService nodeservice, Guid userId, Node parentNode) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 53

In the mean time I've also developed another function (see below) resizing just a bitmap. 与此同时,我还开发了另一个函数(见下文),仅调整位图大小。 And this seem to work correctly. 而这似乎工作正常。 I can't use this function with my current implementation though because it returns just an Bitmap. 我不能在我当前的实现中使用此函数,因为它只返回一个Bitmap。 Or should i change everything to work with bitmaps? 或者我应该改变一切以使用位图?

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
            Bitmap resizedImage;

            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {
                resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
            }

            resizedImage.SetResolution(72,72);

            return resizedImage;
        }

Ok, I tried it only on files on harddrive, but it should work with streams too. 好的,我只尝试过硬盘上的文件,但它也适用于流。

        Bitmap bitmap = new Bitmap(loadFrom);
        Bitmap newBitmap = new Bitmap(bitmap);
        newBitmap.SetResolution(72, 72);
        newBitmap.Save(saveTo);

Took me a while, but I finally found the problem! 花了我一会儿,但我终于找到了问题! The problem lied in the ResizeImage function I used. 问题在于我使用的ResizeImage函数。 In the 'GetThumbnailImage' to be specific. 在'GetThumbnailImage'中具体。 I ran into another problem with blurry images, which was explainable because GetThumbnailImage would stretch up the created ThumbNail to the desired size. 我遇到了另一个模糊图像的问题,这是可以解释的,因为GetThumbnailImage会将创建的ThumbNail拉伸到所需的大小。 And the resolution off the thumbnail never changes. 缩略图的分辨率永远不会改变。

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
            Bitmap resizedImage;

            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {
                resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
            }

            resizedImage.SetResolution(72,72);

            return resizedImage;
        }

By modifying the function above to the function below I was able to solve the problem using Graphics.DrawImage to redraw the new image before rendering it. 通过将上面的函数修改为下面的函数,我能够使用Graphics.DrawImage在渲染之前重绘新图像来解决问题。 Also the GenerateImageDimensions was slightly modified. GenerateImageDimensions也略有修改。 This taken together the problem was solved. 这个问题一起解决了。

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);

            var resizedImage = new Bitmap(newDimensions.Width, newDimensions.Height);
            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {

                //we have a normal image
                using (var gfx = Graphics.FromImage(resizedImage))
                {
                    gfx.SmoothingMode = SmoothingMode.HighQuality;
                    gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;

                    var targRectangle = new Rectangle(0, 0, newDimensions.Width, newDimensions.Height);
                    var srcRectangle = new Rectangle(0, 0, image.Width, image.Height);

                    gfx.DrawImage(image, targRectangle, srcRectangle, GraphicsUnit.Pixel);
                }
            }

            return resizedImage;
        }

By "changing the resolution", do you actually mean you want to reduce the number of pixels in the image by 72/300? 通过“更改分辨率”,您是否真的想要将图像中的像素数减少72/300? Ie change a 4000x3000 image to 960x720? 即将4000x3000图像更改为960x720?

If so, I can't see where your code actually does that. 如果是这样,我无法看到您的代码实际执行的位置。 The overload of DrawImage() you're using does this: 您正在使用的DrawImage()重载执行此操作:

Draws the specified image, using its original physical size, at the location specified by a coordinate pair. 使用其原始物理大小在坐标对指定的位置绘制指定的图像。

Which is exactly what is happening. 这正是发生的事情。

Try one of the other overloads such as this one : 尝试其他的重载之一,比如这一个

Draws the specified Image at the specified location and with the specified size. 在指定位置以指定大小绘制指定的Image。

for example: 例如:

// Create image.
Image newImage = Image.FromFile("SampImag.jpg");

// Create coordinates for upper-left corner of image and for size of image.
int x = 0;
int y = 0;
int width = 450;
int height = 150;

// Draw image to screen.
e.Graphics.DrawImage(newImage, x, y, width, height);

EDIT: per the comments, I understand the OP wants to reduce file size without reducing pixel count. 编辑:根据评论,我知道OP希望减少文件大小而不减少像素数。 Therefore the files must be recompressed. 因此必须重新压缩文件。

I've borrowed some sample code from here : 我从这里借了一些示例代码:

ImageCodecInfo iciJpegCodec = null;

// This will specify the image quality to the encoder. Change the value of 75 from 0 to 100, where 100 is best quality, but highest file size.
EncoderParameter epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);

// Get all image codecs that are available
ImageCodecInfo[] iciCodecs = ImageCodecInfo.GetImageEncoders();

// Store the quality parameter in the list of encoder parameters
EncoderParameters epParameters = new EncoderParameters(1);
epParameters.Param[0] = epQuality;

// Loop through all the image codecs
for (int i = 0; i < iciCodecs.Length; i++)
{
    // Until the one that we are interested in is found, which is image/jpeg
    if (iciCodecs[i].MimeType == "image/jpeg")
    {
        iciJpegCodec = iciCodecs[i];
        break;
    }
}

// Create a new Image object from the current file
Image newImage = Image.FromFile(strFile);

// Get the file information again, this time we want to find out the extension
FileInfo fiPicture = new FileInfo(strFile);

// Save the new file at the selected path with the specified encoder parameters, and reuse the same file name
newImage.Save(outputPath + "\\" + fiPicture.Name, iciJpegCodec, epParameters);

Rob, I believe that issue with your code is at saving the image - the actual digital image data would be certain number of dots/pixels ie (mxn) and setting resolution at bitmap wouldn't/shouldn't change the number dots (and hence physical byte size of image). Rob,我相信你的代码的问题在于保存图像 - 实际的数字图像数据是一定数量的点/像素,即(mxn)和位图的设置分辨率不会/不应该改变点数(和因此,图像的物理字节大小)。 The resolution information will be stored in the image header (to be used by programs while printing/editing images) - what happens if you store the new bitmap to file instead of mem stream 分辨率信息将存储在图像标题中(在打印/编辑图像时由程序使用) - 如果将新位图存储到文件而不是存储流,会发生什么情况

newBitmap.Save("c:\test.png", ImageFormat.Png);

Check dpi for above file from file -> properties -> summary (advanced). 从文件 - >属性 - >摘要(高级)检查上述文件的dpi。 It should be 72 dpi. 它应该是72 dpi。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM