使用 C# 调整透明图像的大小
-
09-06-2019 - |
题
有谁有调整透明图像(主要是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);
}
}
我正在使用上面的代码来进行索引调整大小。
有人有改进的想法吗?
解决方案
如果缩放后不需要保留文件类型,我建议使用以下方法。
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。看 这篇博文 关于它以获取更多信息
编辑: 我忘记处理示例中的位图;已更正
其他提示
这是我在一些利用 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
我认为问题在于您正在执行基于扫描线的调整大小,无论您如何努力调整它都会导致锯齿。良好的图像调整大小质量需要您做更多的工作来找出调整大小的像素覆盖的预调整大小的像素的平均颜色。
运行该网站的人有一篇博客文章讨论了一些图像调整大小算法。您可能需要双三次图像缩放算法。
对于任何可能尝试使用 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 );
}
虽然 PNG 肯定比 GIF 更好,但偶尔也有需要保留 GIF 格式的用例。
对于 GIF 或 8 位 PNG,您必须解决量化问题。
量化是您选择最能保存和表示图像的 256(或更少)颜色,然后将 RGB 值转回索引。当您执行调整大小操作时,理想的调色板会随着您混合颜色和更改平衡而发生变化。
对于轻微的大小调整,例如 10-30%,您可能可以保留原始调色板。
然而,在大多数情况下,您需要重新量化。
可供选择的主要两种算法是八叉树和 nQuant。八叉树非常快并且做得非常好,特别是如果您可以覆盖智能抖动算法。nQuant 需要至少 80MB 的 RAM 才能执行编码(它构建完整的直方图),并且速度通常慢 20-30 倍(平均图像每次编码 1-5 秒)。然而,它有时会产生比八叉树更高的图像质量,因为它不会“舍入”值以保持一致的性能。
在实现透明 GIF 和动画 GIF 支持时 图像调整大小.net 项目中,我选择了八叉树。一旦您控制了图像调色板,透明度支持就不难了。