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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/483014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 05:10:52  来源:igfitidea点击:

Generate image file with low bit depths?

c#vb.netbitmapimaging

提问by TheFlash

bpp= bits per pixel, so 32bpp means 8/8/8/8 for R/G/B/A.

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

Like .NET has an enum for these "System.Drawing.Imaging.PixelFormat".

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

Now once I have a Bitmapor Imageobject with my graphics, how would I save itto a file / what format would I use?

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

What image file format (JPEG/GIF/PNG) supports low bit-depths like 16bpp or 8bpp (instead of the usual 32bpp or 24bpp)

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

采纳答案by Vojislav Stojkovic

Try this:

尝试这个:

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

回答by Dead account

Untested code -

未经测试的代码 -

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

Look at the System.Drawing.Imaging namespace and have a play with the Encoder.xxx parameter settings and the image.Save method. HTH.

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

Updatealso worth noting that if you want a small image (low byte count) you can try saving as JPEG an using Encoder.Compression compression but at image quality cost.

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

回答by MSalters

All image formats effectively support low bit depths. You just leave the last bits unused, if not needed. GIF only supports low-color; you're restricted to 256 colors.

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

回答by Rick Minerich

I don't think the other's answering tested their code as GDI+ PNG does not support the Encoder.BitDepth EncoderParameter. In fact, the only Codec which does is TIFF.

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

You need to change your image's PixelFormat before saving in order to have any effect out the output. This won't always produce the PixelFormat you expect. See my post herefor more information on which PixelFormats turn into what.

您需要在保存之前更改图像的 PixelFormat,以便对输出产生任何影响。这不会总是产生您期望的 PixelFormat。有关哪些 PixelFormats 变成什么的更多信息,请参阅我的帖子

As for PixelFormat conversions, something like the following will work:

至于 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;
}

Unfortunately, these conversions can produce some really bad output. This is especially true in the case where you are performing a lossy conversion (high bit depth to lower).

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

回答by FastAl

One bit per pixel

每像素一位

This produces the smallest possible PNG from .Net only. Note that it is b&w - not even grayscale. Useful for documents.

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

Consumer code:

消费者代码:

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

制作1bpp

This is what the PNG encoder cares about

这就是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