如何在C#和.NET 3.5中减小图像的大小?

11

我在我的移动应用程序中拍了一张截屏,将其保存为PNG格式后,在磁盘上大约需要32 KB的空间。

我将这些截屏发送到一个中央SQL Server,并且32 KB对于我每天需要存储的截屏数量(大约2500次)来说太大了。

有没有什么技巧可以让它保存得更小?

这是我现在使用的代码,将它从 Bitmap 转换为字节(以便发送到服务器进行存储):

MemoryStream stream = new MemoryStream();
 _signatureImage.Save(stream, ImageFormat.Png);
 return stream.ToArray();

_signatureImage 是一个 Bitmap,也就是所讨论的屏幕截图。

这是我保存的屏幕截图的示例:

Screen Shot Image

以下是一些想法(但我不知道如何实现):

  1. 减小图片的实际高度和宽度(但希望不会扭曲图片形状)。
  2. 将其转换为黑白图像(不确定是否会有实质性的空间节省)。
  3. 更多地压缩它(我不太喜欢这个方法,因为这样从数据库中读取时不易读)。

注意,所有这些都必须通过程序完成,且不能花费太长时间,因此复杂的图像处理操作不可行。

感谢任何帮助。


我无法看到您发布的图片(工作中受到 http 过滤器限制)。它的尺寸是多少? - Kendrick
7个回答

11
    private static Image ResizeImage(int newSize, Image originalImage)
    {
        if (originalImage.Width <= newSize)
            newSize = originalImage.Width;

        var newHeight = originalImage.Height * newSize / originalImage.Width;

        if (newHeight > newSize)
        {
            // Resize with height instead
            newSize = originalImage.Width * newSize / originalImage.Height;
            newHeight = newSize;
        }

        return originalImage.GetThumbnailImage(newSize, newHeight, null, IntPtr.Zero);
    }

对于您的位图对象类型,这应该可以工作,并调整高度或宽度,具体取决于您的图像尺寸。同时也会保持比例。

编辑:

您可以创建一个新的位图对象,并将原始图像调整大小到该位图对象中。

Bitmap b = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;

g.DrawImage(imgToResize, 0, 0, newWidth, newHeight);
g.Dispose();

return (Image)b;

我没有安装紧凑框架,但似乎这个对你有用。


看起来不错。但我无法轻松使用它。我应该指出这一点,但是这个应用程序是在紧凑框架上的,而GetThumbnailImage不在紧凑框架中。如果必要的话,我可以在服务器端重新调整图像大小,但如果可能的话,我宁愿通过空气发送一个较小的图像。 - Vaccano
无论如何都应该避免使用GetThumbnailImage - 它只适用于40x40或更小的图像。 - Lilith River

3
如果颜色深度不是问题,您可以将其更改为黑白或16色模式。与PNG的24bpp(或带alpha的32bpp)存储要求相比,应该会有显着的节省。另一件需要考虑的事情是将其保存为256色模式的.gif文件。
由于您正在保存签名数据,我认为更改图像的尺寸可能不是一个好主意。您还可以考虑应用无损压缩,例如Zip,但这可能比您需要的计算密集度更高。

有关如何在C#中实现此操作的任何提示? - Vaccano
不是我个人的经验,但快速搜索发现这个链接:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=554acd82-d87d-43c6-a984-8c625387b198&displaylang=en 选引如下:“在.NET Compact Framework中,没有一种简单的方法来保存位图对象或从位图对象获取位图句柄,因此需要使用一些巧妙的技巧来访问位图的像素数据。” - Matt Mills
我的公司的产品,DotImage Photo,可以做到这一点,并且是一个免费的SDK。只需在AtalaImage上调用GetChangedPixelFormat() -- 传入索引格式,然后使用AtalaImage.Save()保存。https://www.atalasoft.com/photofree - Lou Franco

2
将颜色调色板更改为较低的颜色深度。 参考这个问题(实际上看答案),其中显示了将图像转换为1bpp。 您可以选择将其转换为8bpp而不是1bpp,并获得显着的大小节省。 转换机制与所示相同。
编辑
跳出思维定势,您还可以考虑仅发送屏幕上部分的数据点和签名向量,并让服务器为您重新创建屏幕。 如果大小确实是一个大问题,我可能会研究一下这个解决方案。
编辑2
如果简单地调整图像大小是一个合理的解决方案(您显然会丢失数据),那么您可以使用SDF中的Imaging命名空间创建缩略图,如Alex在此处解释

1
如果单色图像可以接受,您可以尝试使用TIFF G4(无损压缩)。TIFF G4以文件大小存储黑白图像而闻名。不幸的是,我没有任何关于它与PNG相比的信息,但值得调查一下。 这里提供了有关如何在C#中执行此操作的信息和示例。

0

尝试转换为纯单色位图。这些非常小,因为它们每个像素只使用一个位。

编辑:我是个骗子。刚在画图中尝试了一下,大小约为40k,而原始大小为20k。我记得以前用它们时有巨大的空间节省...

编辑编辑:好吧,显然它们确实有巨大的节省空间。只是对于这些图像来说,PNG有更大的节省空间。尝试了一个16色位图,结果变成了150k。

编辑编辑编辑:但是,单色位图压缩后只有9k字节,而PNG即使压缩后仍保持在20k。


所以,如果你想采用压缩的方式,看起来你可以先将图像转换为单色位图,然后对大小约为PNG一半的图像进行压缩。

你也可以剥离图像中的一些信息。例如,在那个屏幕上,唯一需要保留为图像的是签名。上下的条形码可以被剥离出来,数字信息可以直接发送并分别存储。


我不确定Windows如何存储其位图文件,但我知道它们是完全未压缩的。至少.png有一些压缩。 - Matt Mills

0
用照片点燃烟花,虽然不会完全相同但应该能够给你一个想法。
66% quality JPG with no smoothing is 17.5k
PNG8 with 256 colours is 58K (down to 42 using websnap palette)
PNG8 with black and white palette is 14k

请注意, 由于相同颜色的大片区域,许多图像文件在截屏后会更小,而且黑白图像尤其如此。它可能还取决于用于转换图像的算法,但您可能只想使用找到的任何内容...

0

就编程而言,将图像物理存储在 SQL Server 中并不是最优解。如果您使用的是 Sql Server 2008,则应查看文件流支持,它将允许您将物理图像存储在磁盘上,并像内部 SQL Server 一样访问它们。这很可能会减轻您对文件大小的担忧。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接