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

发布于 2024-07-04 14:54:03 字数 1884 浏览 7 评论 0原文

有没有人有秘密公式来调整透明图像(主要是 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);
  }
}

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

有人有改进的想法吗?

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

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

And then the scaled image:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

悲凉≈ 2024-07-11 14:54:03

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

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

更好的图像大小调整

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

情泪▽动烟 2024-07-11 14:54:03

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

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。 请参阅此博文< /a> 有关它的更多信息

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

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 post about it for more information

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

在梵高的星空下 2024-07-11 14:54:03

这是我在一些利用 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

This is a basic resize function I've used for a few of my applications that leverages 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.

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

携余温的黄昏 2024-07-11 14:54:03

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

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

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

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

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

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

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

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

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

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.

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

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.

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

铁憨憨 2024-07-11 14:54:03

对于任何可能尝试使用 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 );
}

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

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 );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文