преобразуйте TIFF в двух тональностях в PNG в двух тональностях в C#

Я должен преобразовать в двух тональностях (черный и белый) файлы TIFF в другой формат для дисплея веб-браузером, в настоящее время мы используем JPGs, но формат не крайне важен. От чтения вокруг.NET, кажется, легко не поддерживает двухцветные изображения записи, таким образом, мы заканчиваем с ~1MB файлами вместо ~100K. Я рассматриваю использование ImageMagick, чтобы сделать это, но идеально я хотел бы решение, которое не требует этого, если это возможно.

Текущий фрагмент кода (который также делает некоторое изменение размеров на изображении):

using (Image img = Image.FromFile(imageName))
{
    using (Bitmap resized = new Bitmap(resizedWidth, resizedHeight)
    {
        using (Graphics g = Graphics.FromImage(resized))
        {
            g.DrawImage(img, new Rectangle(0, 0, resized.Width, resized.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
        }

        resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);

    }
}

Там какой-либо путь состоит в том, чтобы достигнуть этого?

Спасибо.

7
задан Rob Rob 5 August 2010 в 11:08
поделиться

4 ответа

Я считаю, что проблема может быть решена с помощью проверка того, что растровое изображение с измененным размером имеет значение PixelFormat.Format1bppIndexed . Если это не так, вы должны преобразовать его в растровое изображение 1bpp, и после этого вы можете без проблем сохранить его как черно-белый png.

Другими словами, вы должны использовать следующий код вместо 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);
}

Я использую следующий код для 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();
    }        
}
7
ответ дан 7 December 2019 в 07:38
поделиться

Вы пытались сохранить с помощью перегрузки Image.Save с параметрами Encoder ?
Как Параметр Encoder.ColorDepth ?

0
ответ дан 7 December 2019 в 07:38
поделиться

Вы пробовали PNG с 1-битной глубиной цвета?

Я считаю, что для достижения размера, подобного CCITT4 TIFF, ваше изображение должно использовать 1-битную проиндексированную палитру.

Однако вы не можете использовать объект Graphics в .NET для рисования на индексированном изображении.

Вам, вероятно, придется использовать LockBits для управления каждым пикселем.

См. отличную статью Боба Пауэлла .

0
ответ дан 7 December 2019 в 07:38
поделиться

Предложение Ярослава по глубине цвета не работает:

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

}

Jpeg по-прежнему является полноцветным JPEG.

Я не думаю, что в gdi plus есть поддержка jpeg в градациях серого. Пробовали ли вы искать в компоненте обработки изображений Windows?

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

пример кода: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx

википедия: http://en.wikipedia.org/wiki/Windows_Imaging_Component

0
ответ дан 7 December 2019 в 07:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: