convert bitonal TIFF to bitonal PNG in C#

我只是一个虾纸丫 提交于 2019-12-01 04:02:18

I believe the problem can be solved by checking that resized bitmap is of PixelFormat.Format1bppIndexed. If it's not, you should convert it to 1bpp bitmap and after that you can save it as black and white png without problems.

In other words, you should use following code instead of resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);

if (resized.PixelFormat != PixelFormat.Format1bppIndexed)
{
    using (Bitmap bmp = convertToBitonal(resized))
        bmp.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}
else
{
    resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}

I use following code for convertToBitonal :

private static Bitmap convertToBitonal(Bitmap original)
{
    int sourceStride;
    byte[] sourceBuffer = extractBytes(original, out sourceStride);

    // Create destination bitmap
    Bitmap destination = new Bitmap(original.Width, original.Height,
        PixelFormat.Format1bppIndexed);

    destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);

    // Lock destination bitmap in memory
    BitmapData destinationData = destination.LockBits(
        new Rectangle(0, 0, destination.Width, destination.Height),
        ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

    // Create buffer for destination bitmap bits
    int imageSize = destinationData.Stride * destinationData.Height;
    byte[] destinationBuffer = new byte[imageSize];

    int sourceIndex = 0;
    int destinationIndex = 0;
    int pixelTotal = 0;
    byte destinationValue = 0;
    int pixelValue = 128;
    int height = destination.Height;
    int width = destination.Width;
    int threshold = 500;

    for (int y = 0; y < height; y++)
    {
        sourceIndex = y * sourceStride;
        destinationIndex = y * destinationData.Stride;
        destinationValue = 0;
        pixelValue = 128;

        for (int x = 0; x < width; x++)
        {
            // Compute pixel brightness (i.e. total of Red, Green, and Blue values)
            pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] +
                sourceBuffer[sourceIndex + 3];

            if (pixelTotal > threshold)
                destinationValue += (byte)pixelValue;

            if (pixelValue == 1)
            {
                destinationBuffer[destinationIndex] = destinationValue;
                destinationIndex++;
                destinationValue = 0;
                pixelValue = 128;
            }
            else
            {
                pixelValue >>= 1;
            }

            sourceIndex += 4;
        }

        if (pixelValue != 128)
            destinationBuffer[destinationIndex] = destinationValue;
    }

    Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
    destination.UnlockBits(destinationData);
    return destination;
}

private static byte[] extractBytes(Bitmap original, out int stride)
{
    Bitmap source = null;

    try
    {
        // If original bitmap is not already in 32 BPP, ARGB format, then convert
        if (original.PixelFormat != PixelFormat.Format32bppArgb)
        {
            source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
            source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
            using (Graphics g = Graphics.FromImage(source))
            {
                g.DrawImageUnscaled(original, 0, 0);
            }
        }
        else
        {
            source = original;
        }

        // Lock source bitmap in memory
        BitmapData sourceData = source.LockBits(
            new Rectangle(0, 0, source.Width, source.Height),
            ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        // Copy image data to binary array
        int imageSize = sourceData.Stride * sourceData.Height;
        byte[] sourceBuffer = new byte[imageSize];
        Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);

        // Unlock source bitmap
        source.UnlockBits(sourceData);

        stride = sourceData.Stride;
        return sourceBuffer;
    }
    finally
    {
        if (source != original)
            source.Dispose();
    }        
}

Have you tried saving using the Image.Save overload with Encoder parameters?
Like the Encoder.ColorDepth Parameter?

Trying jaroslav's suggestion for color depth doesn't work:

static void Main(string[] args)
{
        var list = ImageCodecInfo.GetImageDecoders();
        var jpegEncoder = list[1]; // i know this is the jpeg encoder by inspection
        Bitmap bitmap = new Bitmap(500, 500);
        Graphics g = Graphics.FromImage(bitmap);
        g.DrawRectangle(new Pen(Color.Red), 10, 10, 300, 300);
        var encoderParams = new EncoderParameters();
        encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 2);
        bitmap.Save(@"c:\newbitmap.jpeg", jpegEncoder, encoderParams);

}

The jpeg is still a full color jpeg.

I don't think there is any support for grayscale jpeg in gdi plus. Have you tried looking in windows imaging component?

http://www.microsoft.com/downloads/details.aspx?FamilyID=8e011506-6307-445b-b950-215def45ddd8&displaylang=en

code example: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx

wikipedia: http://en.wikipedia.org/wiki/Windows_Imaging_Component

Have you tried PNG with 1 bit color depth?

To achieve a size similar to a CCITT4 TIFF, I believe your image needs to use a 1-bit indexed pallette.

However, you can't use the Graphics object in .NET to draw on an indexed image.

You will probably have to use LockBits to manipulate each pixel.

See Bob Powell's excellent article.

Todd Shearer

This is an old thread. However, I'll add my 2 cents.

I use AForge.Net libraries (open source)

use these dlls. Aforge.dll, AForge.Imaging.dll

using AForge.Imaging.Filters;

private void ConvertBitmap()
{
    markedBitmap = Grayscale.CommonAlgorithms.RMY.Apply(markedBitmap);
    ApplyFilter(new FloydSteinbergDithering());
}
private void ApplyFilter(IFilter filter)
{
    // apply filter
    convertedBitmap = filter.Apply(markedBitmap);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!