Generate image file with low bit depths?

冷暖自知 提交于 2020-01-21 06:27:10

问题


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

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

Now once I have a Bitmap or Image object with my graphics, how would I save it to 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)


回答1:


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



回答2:


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.

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 here for more information on which PixelFormats turn into what.

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

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).




回答3:


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.

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

This is what the PNG encoder cares about

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



回答4:


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.




回答5:


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.

Update also 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.



来源:https://stackoverflow.com/questions/483014/generate-image-file-with-low-bit-depths

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!