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

时间:2008-08-27 16:16:47

标签: c# .net image resize image-scaling

有没有人有秘密配方来调整透明图像(主要是GIF)的尺寸没有任何质量损失 - 这是怎么回事?

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

看一下我的主要形象:

http://www.thewallcompany.dk/test/main.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);
  }
}

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

有没有人有改进的想法?

5 个答案:

答案 0 :(得分:51)

如果在缩放后没有要求保留文件类型,我建议采用以下方法。

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

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

  • 删除了已被广告替换的图像小屋图片

如果您必须以gif格式导出图像,那么您可以使用它; GDI +与gif不兼容。有关详细信息,请参阅this blog post

编辑:我忘记了处理示例中的位图;它已被纠正

答案 1 :(得分:5)

这是我用于一些利用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; 
}

如果它可以与GIF配合使用,我不记得我的头脑,但你可以尝试一下。

注意:我不能完全赞成这个功能。我从网上的其他一些样本中拼凑了一些东西,并使其符合我的需求8 ^ D

答案 2 :(得分:3)

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

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

Better Image Resizing

答案 3 :(得分:1)

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

这不起作用:

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

但这会:

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

    stream.WriteTo( Response.OutputStream );
}

答案 4 :(得分:0)

虽然PNG肯定比GIF更好,但偶尔会有一个需要保留GIF格式的用例。

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

量化是指您选择哪种颜色(或更少)最能保留和表示图像,然后将RGB值转换回索引的位置。当您执行调整大小操作时,理想的调色板会更改,因为您正在混合颜色和更改余额。

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

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

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

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