2010-08-05 19 views
7

Necesito convertir archivos TIFF bitonales (blanco y negro) a otro formato para mostrarlos en un navegador web, actualmente estamos usando archivos JPG, pero el formato no es crucial. Al leer, parece que .NET no admite fácilmente la escritura de imágenes bitonales, por lo que terminamos con ~ 1MB en lugar de ~ 100K. Estoy considerando usar ImageMagick para hacer esto, pero idealmente me gustaría una solución que no lo requiera si es posible.convertir bitonal TIFF a PNG bitonal en C#

fragmento de código actual (que también hace un cambio de tamaño en la imagen):

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

    } 
} 

¿Hay alguna manera de lograr esto?

Gracias.

Respuesta

7

Creo que el problema se puede resolver comprobando que resized mapa de bits es de PixelFormat.Format1bppIndexed. Si no es así, conviértalo en un mapa de bits de 1 bpp y luego puede guardarlo como png en blanco y negro sin problemas.

En otras palabras, se debe utilizar siguiente código en lugar de 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); 
} 

que utilizar siguiente código para 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(); 
    }   
} 
+0

eso es brillante. Muchas gracias. –

0

Tratando de Jaroslav de profundidad de color no funciona:

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

} 

El JPEG es todavía un jpeg a todo color.

No creo que haya ningún soporte para la escala de grises jpeg en gdi plus. ¿Has intentado buscar en el componente de imágenes de Windows?

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

ejemplo de código: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx

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

+0

Estoy buscando en este .... –

0

Ha intentado PNG con una profundidad de color de 1 bit?

para alcanzar un tamaño similar al de un TIFF CCITT4, creo que su imagen tiene que utilizar una paleta de 1 bit indexada.

Sin embargo, no puede usar el objeto Graphics en .NET para dibujar en una imagen indexada.

Probablemente tendrá que usar LockBits para manipular cada píxel.

Ver Bob Powell's excellent article.

+0

modificar el código de Ed produce los mismos resultados con archivos PNG. –

0

Este es un hilo de edad. Sin embargo, agregaré mis 2 centavos.

Uso AForge.Las bibliotecas de red (código abierto)

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