C# 生成具有低位深度的图像文件?

C# 生成具有低位深度的图像文件?,c#,vb.net,bitmap,imaging,C#,Vb.net,Bitmap,Imaging,bpp=每像素位数,因此32bpp表示R/G/B/A的8/8/8 与.NET类似,它也为这些“System.Drawing.Imaging.PixelFormat”提供了枚举 现在,一旦我有了一个带有图形的位图或图像对象,我将如何将它保存到文件中/我将使用什么格式 什么图像文件格式(JPEG/GIF/PNG)支持像16bpp或8bpp这样的低位深度(而不是通常的32bpp或24bpp)未测试的代码- Image myImage = new Image(); EncoderParameters p

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

与.NET类似,它也为这些“System.Drawing.Imaging.PixelFormat”提供了枚举

现在,一旦我有了一个带有图形的位图图像对象,我将如何将它保存到文件中/我将使用什么格式

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

未测试的代码-

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方法。嗯

更新 还值得注意的是,如果您想要小图像(低字节数),可以尝试使用编码器将其另存为JPEG。压缩压缩但要以图像质量为代价。

尝试以下方法:

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);
}

所有图像格式都有效地支持低位深度。如果不需要的话,您只需保留最后一个未使用的位。GIF只支持低颜色;您被限制为256色。

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

保存前需要更改图像的像素格式,以便对输出产生任何影响。这并不总是产生您期望的像素格式。有关哪些像素格式转换为什么格式的详细信息,请参见

对于像素格式转换,类似以下的操作将起作用:

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;
}
不幸的是,这些转换可能会产生一些非常糟糕的输出。在执行有损转换(从高位深度到低位深度)的情况下尤其如此

每像素一位 这将仅从.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
Make1bpp 这就是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
函数将1bpp(ByVal bmpIN作为位图)作为位图
将bmpOUT变暗为位图
bmpOUT=NewBitmap(bmpIN.Width、bmpIN.Height、PixelFormat.format1bpindexed)
bmpOUT.SetResolution(bmpIN.HorizontalResolution,bmpIN.VerticalResolution)
“好像我在这个节目里有100倍的垃圾。
如果bmpIN.PixelFormat PixelFormat.Format16bppRgb555,则
抛出新的ApplicationException(“手工编码的例程只能理解Format16bppRgb555的图像格式,但此图像是”&_
bmpIN.PixelFormat.ToString&“。请更改格式或对此sub进行编码以处理该格式。”)
如果结束
'锁定图像字节
按位图数据调整bmdIN的尺寸=bmpIN.LockBits(新矩形(0,0,bmpIN.Width,bmpIN.Height)_
Imaging.ImageLockMode.ReadWrite,bmpIN.PixelFormat)
'锁定图像字节
将bmdOUT设置为位图数据=bmpOUT.LockBits(新矩形(0,0,bmpOUT.Width,bmpOUT.Height)_
Imaging.ImageLockMode.ReadWrite,bmpOUT.PixelFormat)
'为数据分配空间。
Dim字节数(bmdIN.Stride*bmdIN.Height)作为字节
变暗字节输出(bmdOUT.Stride*bmdOUT.Height)作为字节
'将数据复制到PixBytes数组中。
封送员副本(bmdIN.Scan0,bytesIN,0,CInt(bmdIN.Stride*bmpIN.Height))
“>此val=白色像素。(rgb555中的3个像素中的每一个都可以容纳32个级别…2^5 huh。)
尺寸bThresh为字节=CByte((32*3)*0.66)
'转移像素
对于y,作为整数=0到bmpIN.Height-1
输出调宽为整数=y*bmdOUT.Stride
Dim instart作为整数=y*bmdIN.步长
作为字节=0的Dim字节值
Dim bitpos作为字节=128
Dim pixval作为整数
Dim pixgraylevel为整数
对于inpos,整数=instart到instart+bmdIN.跨步-1步骤2
pixval=256*bytesIN(inpos+1)+bytesIN(inpos)”取决于格式16bpprgb555
像素灰度=((像素值)和31)+((像素值>>5)和31)+((像素值>>10)和31)
如果pixgraylevel>bThresh,则“取决于格式16BPPRGB555
字节值=字节值或比特值
如果结束
bitpos=bitpos>>1
如果bitpos=0,则
字节输出(输出)=字节值
字节=0
比特位置=128
输出+=1
如果结束
下一个
如果bitpos为0,则“在任何未完成的业务中插入叉子”。
字节输出(输出)=字节值
如果结束
下一个
'解锁图像字节
'将数据复制回位图。
封送处理副本(字节输出,0_
bmdOUT.Scan0,bmdOUT.Stride*bmdOUT.Height)
'解锁位图。
bmpIN.解锁位(bmdIN)
bmpOUT.解锁位(bmdOUT)
'释放内存的徒劳尝试。
重拨字节数(0)
重拨字节输出(0)
'返回新的bmp。
回击
端函数

这很有帮助,尽管我目前的经验是,使用正确像素格式的位图不会导致它保存为您想要的png格式……不幸的是,在我7年前离开Atalasoft后,他们切换到了另一个博客平台,并且没有迁移我的大部分帖子。很抱歉我得到了一个16bpp的位图对象,但如果我将其保存为png,结果文件总是32bpp,无论我做什么…不管怎样,png似乎不支持16位RGB。它的“16位”格式是指16位颜色分量,使图像深度为每像素48位。PNG编解码器似乎没有使用Encoder.ColorDepth参数。