生成低位深度的图像文件?

Posted

技术标签:

【中文标题】生成低位深度的图像文件?【英文标题】:Generate image file with low bit depths? 【发布时间】:2010-10-03 17:42:59 【问题描述】:

bpp = 每像素位数,因此 32bpp 表示 R/G/B/A 为 8/8/8/8。

就像 .NET 对这些“System.Drawing.Imaging.PixelFormat”有一个枚举。

现在,一旦我有一个带有我的图形的 位图图像 对象,我将如何将其保存 到文件/我将采用什么格式用吗?

什么图像文件格式 (JPEG/GIF/PNG) 支持 16bpp 或 8bpp 等低位深度 (而不是通常的 32bpp 或 24bpp)

【问题讨论】:

【参考方案1】:

我不认为其他人的回答测试了他们的代码,因为 GDI+ PNG 不支持 Encoder.BitDepth EncoderParameter。事实上,唯一能做的 Codec 就是 TIFF。

您需要在保存之前更改图像的 PixelFormat 才能对输出产生任何影响。这不会总是产生您期望的 PixelFormat。请参阅my post here 了解更多关于哪些 PixelFormat 会变成什么的信息。

至于 PixelFormat 转换,类似以下的方法将起作用:

private Bitmap ChangePixelFormat(Bitmap inputImage, PixelFormat newFormat)

  Bitmap bmp = new Bitmap(inputImage.Width, inputImage.Height, newFormat);
  using (Graphics g = Graphics.FromImage(bmp))
  
    g.DrawImage(inputImage, 0, 0);
  
  return bmp;

不幸的是,这些转换会产生一些非常糟糕的输出。在执行有损转换(从高位深度到低位)的情况下尤其如此。

【讨论】:

这很有帮助,尽管我目前的经验是,拥有正确 PixelFormat 的位图不会导致它保存为您想要的那种 png... 不幸的是,在我 7 年前离开 Atalasoft 之后,他们切换到了不同的博客平台,并且没有迁移我的大部分帖子。对不起! 我得到了一个 16bpp 位图对象,但如果我将它保存为 png,则无论我做什么,生成的文件总是 32bpp... 没关系,好​​像 PNG 不支持 16 位 RGB。它的“16 位”格式指的是 16 位颜色分量,使图像深度为每像素 48 位。【参考方案2】:

每像素一位

这仅从 .Net 生成尽可能小的 PNG。请注意,它是黑白的 - 甚至不是灰度。对文档很有用。

消费者代码:

Dim src = (the original bitmap)
Using img = New Bitmap(src.Width, src.Height, PixelFormat.Format16bppRgb555) ' Provided Make1bpp function requires this
  img.SetResolution(src.HorizontalResolution, src.VerticalResolution)
  Using g = Graphics.FromImage(img)
    g.Clear(Color.White) ' remove transparancy
    g.DrawImage(src, 0, 0, src.Width, src.Height)
  End Using

  Using img2 As Bitmap = Make1bpp(img)
    img2.SetResolution(src.HorizontalResolution, src.VerticalResolution)

    Dim myencoder = (From parm In ImageCodecInfo.GetImageEncoders() Where parm.MimeType = "image/png").First()
    Dim encoderParams = New EncoderParameters(1)
    encoderParams.Param(0) = New EncoderParameter(Encoder.ColorDepth, 8L)
    If IO.File.Exists(pngName) Then
      IO.File.Delete(pngName)
    End If
    img2.Save(pngName, myencoder, encoderParams)
  End Using
End Using

制作1bpp

这就是 PNG 编码器所关心的

Function Make1bpp(ByVal bmpIN As Bitmap) As Bitmap
    Dim bmpOUT As Bitmap
    bmpOUT = NewBitmap(bmpIN.Width, bmpIN.Height, PixelFormat.Format1bppIndexed)
    bmpOUT.SetResolution(bmpIN.HorizontalResolution, bmpIN.VerticalResolution)

    ' seems like I've got this crap in this program about 100x.
    If bmpIN.PixelFormat <> PixelFormat.Format16bppRgb555 Then
      Throw New ApplicationException("hand-coded routine can only understand image format of Format16bppRgb555 but this image is " & _
        bmpIN.PixelFormat.ToString & ". Either change the format or code this sub to handle that format, too.")
    End If

    ' lock image bytes
    Dim bmdIN As BitmapData = bmpIN.LockBits(New Rectangle(0, 0, bmpIN.Width, bmpIN.Height), _
        Imaging.ImageLockMode.ReadWrite, bmpIN.PixelFormat)
    ' lock image bytes
    Dim bmdOUT As BitmapData = bmpOUT.LockBits(New Rectangle(0, 0, bmpOUT.Width, bmpOUT.Height), _
        Imaging.ImageLockMode.ReadWrite, bmpOUT.PixelFormat)

    ' Allocate room for the data.
    Dim bytesIN(bmdIN.Stride * bmdIN.Height) As Byte
    Dim bytesOUT(bmdOUT.Stride * bmdOUT.Height) As Byte
    ' Copy the data into the PixBytes array. 
    Marshal.Copy(bmdIN.Scan0, bytesIN, 0, CInt(bmdIN.Stride * bmpIN.Height))
    ' > this val = white pix. (each of the 3 pix in the rgb555 can hold 32 levels... 2^5 huh.)
    Dim bThresh As Byte = CByte((32 * 3) * 0.66)
    ' transfer the pixels
    For y As Integer = 0 To bmpIN.Height - 1
      Dim outpos As Integer = y * bmdOUT.Stride
      Dim instart As Integer = y * bmdIN.Stride
      Dim byteval As Byte = 0
      Dim bitpos As Byte = 128
      Dim pixval As Integer
      Dim pixgraylevel As Integer
      For inpos As Integer = instart To instart + bmdIN.Stride - 1 Step 2
        pixval = 256 * bytesIN(inpos + 1) + bytesIN(inpos) ' DEPENDANT ON Format16bppRgb555
        pixgraylevel = ((pixval) And 31) + ((pixval >> 5) And 31) + ((pixval >> 10) And 31)
        If pixgraylevel > bThresh Then ' DEPENDANT ON Format16bppRgb555
          byteval = byteval Or bitpos
        End If
        bitpos = bitpos >> 1
        If bitpos = 0 Then
          bytesOUT(outpos) = byteval
          byteval = 0
          bitpos = 128
          outpos += 1
        End If
      Next
      If bitpos <> 0 Then ' stick a fork in any unfinished busines.
        bytesOUT(outpos) = byteval
      End If
    Next
    ' unlock image bytes
    ' Copy the data back into the bitmap. 
    Marshal.Copy(bytesOUT, 0, _
        bmdOUT.Scan0, bmdOUT.Stride * bmdOUT.Height)
    ' Unlock the bitmap.
    bmpIN.UnlockBits(bmdIN)
    bmpOUT.UnlockBits(bmdOUT)
    ' futile attempt to free memory.
    ReDim bytesIN(0)
    ReDim bytesOUT(0)
    ' return new bmp.
    Return bmpOUT
  End Function

【讨论】:

【参考方案3】:

试试这个:

ImageCodecInfo pngCodec = ImageCodecInfo.GetImageEncoders().Where(codec => codec.FormatID.Equals(ImageFormat.Png.Guid)).FirstOrDefault();
if (pngCodec != null)

    EncoderParameters parameters = new EncoderParameters();
    parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
    myImage.Save(myStream, pngCodec, parameters);

【讨论】:

PNG编解码器似乎没有使用Encoder.ColorDepth参数。【参考方案4】:

所有图像格式都有效地支持低位深度。如果不需要,您只需保留最后几位未使用。 GIF 只支持低色;您只能使用 256 种颜色。

【讨论】:

【参考方案5】:

未经测试的代码 -

Image myImage = new Image();
EncoderParameters parameters = new EncoderParameters(1);   
parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
myImage.Save(somestream, ImageFormat.Png, parameters);

查看 System.Drawing.Imaging 命名空间,并尝试使用 Encoder.xxx 参数设置和 image.Save 方法。 HTH。

更新 还值得注意的是,如果您想要一个小图像(低字节数),您可以尝试使用 Encoder.Compression 压缩保存为 JPEG,但会以图像质量为代价。

【讨论】:

以上是关于生成低位深度的图像文件?的主要内容,如果未能解决你的问题,请参考以下文章

生成 1 位深度的 BMP 图像

深度卷积生成对抗网络(DCGAN)简介及图像生成仿真(附代码)

Keras深度学习实战(38)——图像字幕生成

如何从点云创建深度图像

如何将 16 位深度图像保存到 Arcore(java)中的文件

matlab图像处理篇--生成8bit深度图像