使用 C# 调整透明图像的大小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 08:25:33  来源:igfitidea点击:

Resize transparent images using C#

提问by MartinHN

Does anyone have the secret formula to resizing transparent images (mainly GIFs) withoutANY quality loss - what so ever?

有没有人有在没有任何质量损失的情况下调整透明图像(主要是 GIF)大小的秘密公式- 什么都这样?

I've tried a bunch of stuff, the closest I get is not good enough.

我已经尝试了很多东西,我得到的最接近的还不够好。

Take a look at my main image:

看看我的主图:

http://www.thewallcompany.dk/test/main.gif

http://www.thewallcompany.dk/test/main.gif

And then the scaled image:

然后是缩放图像:

http://www.thewallcompany.dk/test/ScaledImage.gif

http://www.thewallcompany.dk/test/ScaledImage.gif

//Internal resize for indexed colored images
void IndexedRezise(int xSize, int ySize)
{
  BitmapData sourceData;
  BitmapData targetData;

  AdjustSizes(ref xSize, ref ySize);

  scaledBitmap = new Bitmap(xSize, ySize, bitmap.PixelFormat);
  scaledBitmap.Palette = bitmap.Palette;
  sourceData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
    ImageLockMode.ReadOnly, bitmap.PixelFormat);
  try
  {
    targetData = scaledBitmap.LockBits(new Rectangle(0, 0, xSize, ySize),
      ImageLockMode.WriteOnly, scaledBitmap.PixelFormat);
    try
    {
      xFactor = (Double)bitmap.Width / (Double)scaledBitmap.Width;
      yFactor = (Double)bitmap.Height / (Double)scaledBitmap.Height;
      sourceStride = sourceData.Stride;
      sourceScan0 = sourceData.Scan0;
      int targetStride = targetData.Stride;
      System.IntPtr targetScan0 = targetData.Scan0;
      unsafe
      {
        byte* p = (byte*)(void*)targetScan0;
        int nOffset = targetStride - scaledBitmap.Width;
        int nWidth = scaledBitmap.Width;
        for (int y = 0; y < scaledBitmap.Height; ++y)
        {
          for (int x = 0; x < nWidth; ++x)
          {
            p[0] = GetSourceByteAt(x, y);
            ++p;
          }
          p += nOffset;
        }
      }
    }
    finally
    {
      scaledBitmap.UnlockBits(targetData);
    }
  }
  finally
  {
    bitmap.UnlockBits(sourceData);
  }
}

I'm using the above code, to do the indexed resizing.

我正在使用上面的代码来进行索引调整大小。

Does anyone have improvement ideas?

有没有人有改进的想法?

采纳答案by Markus Olsson

If there's no requirement on preserving file type after scaling I'd recommend the following approach.

如果在缩放后不需要保留文件类型,我建议使用以下方法。

using (Image src = Image.FromFile("main.gif"))
using (Bitmap dst = new Bitmap(100, 129))
using (Graphics g = Graphics.FromImage(dst))
{
   g.SmoothingMode = SmoothingMode.AntiAlias;
   g.InterpolationMode = InterpolationMode.HighQualityBicubic;
   g.DrawImage(src, 0, 0, dst.Width, dst.Height);
   dst.Save("scale.png", ImageFormat.Png);
}

The result will have really nice anti aliased edges

结果将具有非常好的抗锯齿边缘

  • removed image shack image that had been replaced by an advert
  • 已被广告取代的已删除图像小屋图像

If you must export the image in gif you're in for a ride; GDI+ doesn't play well with gif. See this blog postabout it for more information

如果您必须以 gif 格式导出图像,那么您就在兜风;GDI+ 不能很好地与 gif 配合使用。有关更多信息,请参阅此博客文章

Edit:I forgot to dispose of the bitmaps in the example; it's been corrected

编辑:我忘记处理示例中的位图;已更正

回答by Jonathan

I think the problem is that you're doing a scan line-based resize, which is going to lead to jaggies no matter how hard you tweak it. Good image resize quality requires you to do some more work to figure out the average color of the pre-resized pixels that your resized pixel covers.

我认为问题在于您正在执行基于扫描线的调整大小,无论您多么努力地调整它都会导致锯齿。良好的图像调整质量要求您做更多的工作来计算调整后像素覆盖的预调整像素的平均颜色。

The guy who runs this website has a blog post that discusses a few image resizing algorithms. You probably want a bicubic image scaling algorithm.

运行这个网站的人有一篇博客文章,讨论了一些图像大小调整算法。您可能想要一个双三次图像缩放算法。

Better Image Resizing

更好地调整图像大小

回答by Dillie-O

This is a basic resize function I've used for a few of my applications that leverages GDI+

这是我在一些利用 GDI+ 的应用程序中使用的基本调整大小功能

/// <summary>
///    Resize image with GDI+ so that image is nice and clear with required size.
/// </summary>
/// <param name="SourceImage">Image to resize</param>
/// <param name="NewHeight">New height to resize to.</param>
/// <param name="NewWidth">New width to resize to.</param>
/// <returns>Image object resized to new dimensions.</returns>
/// <remarks></remarks>
public static Image ImageResize(Image SourceImage, Int32 NewHeight, Int32 NewWidth)
{
   System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(NewWidth, NewHeight, SourceImage.PixelFormat);

   if (bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format1bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format4bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format8bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Undefined | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.DontCare | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format16bppArgb1555 | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format16bppGrayScale) 
   {
      throw new NotSupportedException("Pixel format of the image is not supported.");
   }

   System.Drawing.Graphics graphicsImage = System.Drawing.Graphics.FromImage(bitmap);

   graphicsImage.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality;
   graphicsImage.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
   graphicsImage.DrawImage(SourceImage, 0, 0, bitmap.Width, bitmap.Height);
   graphicsImage.Dispose();
   return bitmap; 
}

I don't remember off the top of my head if it will work with GIFs, but you can give it a try.

我不记得它是否适用于 GIF,但您可以尝试一下。

Note: I can't take full credit for this function. I pieced a few things together from some other samples online and made it work to my needs 8^D

注意:我不能完全相信这个功能。我从网上的其他一些示例中拼凑了一些东西,使其满足我的需求 8^D

回答by Bela

For anyone that may be trying to use Markus Olsson's solution to dynamically resize images and write them out to the Response Stream.

对于可能尝试使用 Markus Olsson 的解决方案来动态调整图像大小并将其写入响应流的任何人。

This will not work:

这将不起作用:

Response.ContentType = "image/png";
dst.Save( Response.OutputStream, ImageFormat.Png );

But this will:

但这将:

Response.ContentType = "image/png";
using (MemoryStream stream = new MemoryStream())
{
    dst.Save( stream, ImageFormat.Png );

    stream.WriteTo( Response.OutputStream );
}

回答by Lilith River

While PNG is definitely better that GIF, occasionally there is a use case for needing to stay in GIF format.

虽然 PNG 肯定比 GIF 更好,但偶尔也有需要保持 GIF 格式的用例。

With GIF or 8-bit PNG, you have to address the problem of quantization.

对于 GIF 或 8 位 PNG,您必须解决量化问题。

Quantization is where you choose which 256 (or fewer) colors will best preserve and represent the image, and then turn the RGB values back into indexes. When you perform a resize operation, the ideal color palette changes, as you are mixing colors and changing balances.

量化是您选择哪 256 种(或更少)颜色最能保留和表示图像的地方,然后将 RGB 值转换回索引。当您执行调整大小操作时,理想的调色板会随着您混合颜色和改变平衡而改变。

For slight resizes, like 10-30%, you may be OK preserving the original color palette.

对于轻微调整大小,如 10-30%,您可能可以保留原始调色板。

However, in most instances you'll need to re-quantize.

但是,在大多数情况下,您需要重新量化。

The primary two algorithms to pick from are Octree and nQuant. Octree is very fast and does a very good job, especially if you can overlay a smart dithering algorithm. nQuant requires at least 80MB of RAM to perform an encode (it builds a complete histogram), and is typically 20-30X slower (1-5 seconds per encode on an average image). However, it sometimes produces higher image quality that Octree since it doesn't 'round' values to maintain consistent performance.

要选择的主要两种算法是 Octree 和 nQuant。Octree 速度非常快并且做得非常好,特别是如果您可以叠加智能抖动算法。nQuant 至少需要 80MB 的 RAM 来执行编码(它构建完整的直方图),并且通常慢 20-30 倍(平均图像每次编码 1-5 秒)。但是,它有时会产生比八叉树更高的图像质量,因为它不会“舍入”值以保持一致的性能。

When implementing transparent GIF and animated GIF support in the imageresizing.netproject, I chose Octree. Transparency support isn't hard once you have control of the image palette.

imageresizing.net项目中实现透明 GIF 和动画 GIF 支持时,我选择了 Octree。一旦您控制了图像调色板,透明度支持就不难了。