基于VB/C#的文档扫描字节数组压缩方案咨询
问题:在VB/C#项目中缩减扫描文档生成的字节数组大小
当前项目未考虑扫描仪品牌/类型,单页原始图像转字节数组后大小超过3MB。不想使用付费的LEADTOOLS SDK(功能冗余、成本高,且不愿大幅修改现有代码),现有图像转字节数组的VB代码如下:
Private Shared Function ConvertImageToByteArray(ByVal imageToConvert As System.Drawing.Image, ByVal formatOfImage As Drawing.Imaging.ImageFormat) As Byte() Dim Ret As Byte() Using ms As New IO.MemoryStream() imageToConvert.Save(ms, formatOfImage) Ret = ms.ToArray() End Using Return Ret End Function
编辑:2022/7/27 - 2:48 - 修正拼写
解决方案
以下几种方法无需引入付费SDK,仅需对现有代码做小幅修改,即可显著降低字节数组体积:
1. 改用高压缩率格式并控制质量
现有代码直接使用传入的ImageFormat,若当前用的是BMP、PNG这类无压缩/低压缩格式,换成带质量参数的JPEG或黑白专用的TIFF压缩是最直接的优化方式:
VB代码示例(JPEG带质量控制)
Private Shared Function ConvertImageToByteArray(ByVal imageToConvert As System.Drawing.Image, Optional ByVal quality As Integer = 70) As Byte() Dim Ret As Byte() Using ms As New IO.MemoryStream() ' 获取JPEG编码器 Dim jpegCodec As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders().FirstOrDefault(Function(c) c.FormatID = Imaging.ImageFormat.Jpeg.Guid) If jpegCodec IsNot Nothing Then Dim encoderParams As New Imaging.EncoderParameters(1) ' 质量范围0-100,值越小压缩率越高(70是兼顾质量和体积的常用值) encoderParams.Param(0) = New Imaging.EncoderParameter(Imaging.Encoder.Quality, quality) imageToConvert.Save(ms, jpegCodec, encoderParams) Else ' 编码器获取失败时 fallback 默认保存 imageToConvert.Save(ms, Imaging.ImageFormat.Jpeg) End If Ret = ms.ToArray() End Using Return Ret End Function
TIFF黑白压缩(适合文字类扫描件)
如果是黑白文档扫描件,使用TIFF的CCITT Group 4压缩能达到极高的压缩比,且几乎不损失文字可读性:
Private Shared Function ConvertImageToByteArrayTiff(ByVal imageToConvert As System.Drawing.Image) As Byte() Dim Ret As Byte() Using ms As New IO.MemoryStream() Dim tiffCodec As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders().FirstOrDefault(Function(c) c.FormatID = Imaging.ImageFormat.Tiff.Guid) If tiffCodec IsNot Nothing Then Dim encoderParams As New Imaging.EncoderParameters(2) ' 设置压缩方式为CCITT Group 4 encoderParams.Param(0) = New Imaging.EncoderParameter(Imaging.Encoder.Compression, Imaging.EncoderValue.CompressionCCITT4) ' 转换为1位黑白图像 encoderParams.Param(1) = New Imaging.EncoderParameter(Imaging.Encoder.ColorDepth, 1L) imageToConvert.Save(ms, tiffCodec, encoderParams) Else imageToConvert.Save(ms, Imaging.ImageFormat.Tiff) End If Ret = ms.ToArray() End Using Return Ret End Function
2. 降低图像分辨率
扫描文档通常默认300DPI以上,若无需高精度打印/识别,可将分辨率降至150DPI,同时按比例缩小图像尺寸,进一步减小体积:
Private Shared Function ResizeImageAndConvertToByteArray(ByVal imageToConvert As System.Drawing.Image, ByVal targetDpi As Integer = 150, ByVal quality As Integer = 70) As Byte() ' 计算缩放比例 Dim scaleRatio As Double = targetDpi / imageToConvert.HorizontalResolution Dim newWidth As Integer = CInt(imageToConvert.Width * scaleRatio) Dim newHeight As Integer = CInt(imageToConvert.Height * scaleRatio) ' 创建缩小后的图像并保持目标分辨率 Using resizedImage As New Bitmap(newWidth, newHeight) resizedImage.SetResolution(targetDpi, targetDpi) Using g As Graphics = Graphics.FromImage(resizedImage) ' 保证缩放后图像质量 g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic g.DrawImage(imageToConvert, 0, 0, newWidth, newHeight) End Using ' 调用JPEG压缩逻辑 Return ConvertImageToByteArray(resizedImage, quality) End Using End Function
3. 转换为灰度图像
文档类图像以文字为主,转换为灰度图像可减少色彩通道数量,配合压缩格式能进一步降低体积:
Private Shared Function ConvertToGrayscaleAndSave(ByVal imageToConvert As System.Drawing.Image, ByVal quality As Integer = 70) As Byte() Using grayBitmap As New Bitmap(imageToConvert.Width, imageToConvert.Height) Using g As Graphics = Graphics.FromImage(grayBitmap) ' 灰度转换矩阵 Dim grayMatrix As New Imaging.ColorMatrix(New Single()() { New Single() {0.299F, 0.299F, 0.299F, 0, 0}, New Single() {0.587F, 0.587F, 0.587F, 0, 0}, New Single() {0.114F, 0.114F, 0.114F, 0, 0}, New Single() {0, 0, 0, 1, 0}, New Single() {0, 0, 0, 0, 1} }) Dim imageAttr As New Imaging.ImageAttributes() imageAttr.SetColorMatrix(grayMatrix) ' 绘制灰度图像 g.DrawImage(imageToConvert, New Rectangle(0, 0, imageToConvert.Width, imageToConvert.Height), 0, 0, imageToConvert.Width, imageToConvert.Height, GraphicsUnit.Pixel, imageAttr) End Using ' 调用压缩保存逻辑 Return ConvertImageToByteArray(grayBitmap, quality) End Using End Function
内容的提问来源于stack exchange,提问作者Jesus J. Nieves-Padilla
相关产品推荐
相关产品推荐

