2010-08-05 7 views
7

Ich muss Bitonal (schwarz und weiß) TIFF-Dateien in ein anderes Format für die Anzeige von einem Webbrowser konvertieren, derzeit verwenden wir JPGs, aber das Format ist nicht entscheidend. Durch das Lesen von .NET scheint es nicht einfach zu sein, bitonale Bilder zu schreiben, also enden wir mit ~ 1MB Dateien anstatt ~ 100K Dateien. Ich überlege mir, ImageMagick dafür zu verwenden, aber im Idealfall möchte ich eine Lösung, die dies, wenn möglich, nicht erfordert.Konvertieren von Bitonal TIFF in Bitonal PNG in C#

Aktuelle Code-Schnipsel (die auch eine gewisse Redimensionierung auf das Bild der Fall ist):

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

    } 
} 

Gibt es eine Möglichkeit, dies zu erreichen?

Danke.

Antwort

7

Ich glaube, das Problem kann gelöst werden, indem Sie überprüfen, dass resized Bitmap von PixelFormat.Format1bppIndexed ist. Wenn dies nicht der Fall ist, sollten Sie es in 1bpp Bitmap umwandeln und danach können Sie es ohne Probleme als Schwarz-Weiß-Png speichern.

Mit anderen Worten, sollten Sie folgenden Code verwenden, statt 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); 
} 

Ich verwende folgenden Code für 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

das ist brilliant. Vielen Dank. –

0

Versuch jaroslav Vorschlag für die Farbtiefe nicht funktioniert:

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

} 

Die JPEG JPEG noch eine Vollfarbe ist.

Ich glaube nicht, dass es keine Unterstützung für Graustufen-JPEG in Gdi plus gibt. Haben Sie versucht, in der Windows-Imaging-Komponente zu suchen?

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

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

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

+0

Ich bin in diese suchen .... –

0

Haben Sie PNG mit 1-Bit-Farbtiefe versucht?

Um eine Größe zu erreichen, die einer CCITT4 TIFF ähnelt, muss Ihr Image eine indizierte 1-Bit-Palette verwenden.

Sie können das Graphics-Objekt in .NET jedoch nicht verwenden, um ein indiziertes Bild zu zeichnen.

Sie müssen wahrscheinlich LockBits verwenden, um jedes Pixel zu manipulieren.

Siehe Bob Powell's excellent article.

+0

Modifizierung Eds Code die gleichen Ergebnisse mit PNGs erzeugt. –

0

Dies ist ein alter Thread. Allerdings werde ich meine 2 Cent hinzufügen.

Ich benutze AForge.Netzbibliotheken (Open Source)

Verwenden Sie diese 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); 
} 
Verwandte Themen