【问题标题】:C# change dpi of an uploaded imageC#更改上传图像的dpi
【发布时间】:2011-06-13 00:06:43
【问题描述】:

我必须使用以下函数来更改图像的分辨率。我想这样做,因此上传的图像(例如 300dpi)将被修改为 72dpi(用于网络)。这个问题与我正在研究的 SO 上的another 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();
    }
}

上面提到的扩展方法在类似于下面情况的函数中被调用;

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);
}

传入的字节数组是作为字节数组的文件。它已经具有正确的尺寸,但我想将分辨率更改为 72dpi。但是执行并保存图像后,分辨率仍然是原始输入的分辨率,即300dpi。我该怎么做?

几个答案后更新:

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();
            }
        }

异常附带的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

与此同时,我还开发了另一个功能(见下文),仅调整位图的大小。这似乎工作正常。我不能在我当前的实现中使用这个函数,因为它只返回一个位图。还是我应该更改所有内容以使用位图?

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;
        }

【问题讨论】:

  • 你知道你有很好的循环代码吗? newBitmap = originalImage 未复制。它仅引用相同的图像。有趣的是它仍然可以工作。
  • 哈哈好一个!已更改;-)
  • 等等..它真的解决了你的问题吗?
  • 不,它没有。将使用当前代码更新问题..

标签: c# asp.net image-processing stream memorystream


【解决方案1】:

花了我一段时间,但我终于找到了问题! 问题在于我使用的 ResizeImage 函数。在 'GetThumbnailImage' 中进行具体说明。我遇到了图像模糊的另一个问题,这是可以解释的,因为 GetThumbnailImage 会将创建的 ThumbNail 拉伸到所需的大小。并且缩略图的分辨率永远不会改变。

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;
        }

通过将上面的函数修改为下面的函数,我能够使用 Graphics.DrawImage 在渲染之前重新绘制新图像来解决问题。 GenerateImageDimensions 也略有修改。这样一来,问题就解决了。

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;
        }

【讨论】:

    【解决方案2】:

    好的,我只对硬盘上的文件进行了尝试,但它也应该适用于流。

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

    【讨论】:

      【解决方案3】:

      Rob,我认为您的代码的问题在于保存图像 - 实际的数字图像数据将是一定数量的点/像素,即 (mxn),并且在位图上设置分辨率不会/不应该更改数字点(因此图像的物理字节大小)。分辨率信息将存储在图像标题中(供程序在打印/编辑图像时使用) - 如果将新位图存储到文件而不是内存流会发生什么

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

      从文件 -> 属性 -> 摘要(高级)检查上述文件的 dpi。它应该是 72 dpi。

      【讨论】:

      • 将尝试将其保存到文件系统。但这不可能是最终的解决方案。我需要它最后保存到数据库中,因为关闭了应用程序开发
      • 我已经尝试过您的建议,但遇到了 GDI+ 异常。我已经更新了问题
      【解决方案4】:

      通过“更改分辨率”,您的意思实际上是要将图像中的像素数减少 72/300? IE。将 4000x3000 的图像更改为 960x720?

      如果是这样,我看不出您的代码实际上是在哪里做的。 overload of DrawImage() you're using 这样做:

      使用其原始物理尺寸在坐标对指定的位置绘制指定的图像。

      这正是正在发生的事情。

      试试one of the other overloads 比如this one

      在指定位置以指定大小绘制指定图像。

      例如:

      // 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);
      

      编辑: 根据 cmets,我了解 OP 希望在不减少像素数的情况下减小文件大小。因此必须重新压缩文件。

      我从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);
      

      【讨论】:

      • 我真正想要的是减少图像中每英寸的像素数。例如,如果我在 300dpi 中上传大小为 1181 x 1181 的图像,我希望它以 1181 x 1181 的原始大小保存,但 72dpi 而不是原始的 300
      • 我刚刚看到在调用 drawimage 函数时我没有提供所需的图像大小。将尝试修改此..
      • 啊,降低文件中的DPI值,而不是图像中实际的总像素数。抱歉,不太确定问题出在哪里。请记住,当图像以数字方式存储时,保存到图像文件中的“分辨率”值实际上并没有任何有意义的概念——这只在图像被复制时才起作用——你确定你真的需要这样做吗?
      • @tomfanning。是的,绝对肯定这是必要的。我的情况是我正在处理的 webapp 包含一个带有图像的库。每种类型的图像都可以上传,并将作为 blob 保存到数据库中,最大尺寸为 7500x7500 像素。在包含这些图像中的一个或多个的页面上,文件在显示给用户之前在服务器端呈现。但是以正确的尺寸和 300dpi 作为源渲染它们会导致我们服务器上的负载问题。这就是为什么我们要在将文件保存为 BLOB 之前降低文件的 DPI。
      • 在保存之前像这样更改分辨率值对文件大小的影响为零。对 SetResolution() 的调用实际上只是在文件中旋转一个数字。如果要在不减少像素数的情况下减小文件大小,则需要更改文件的压缩质量。这里有一个简单的应用程序:geekpedia.com/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-09
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2011-05-24
      • 1970-01-01
      相关资源
      最近更新 更多