2010-08-05 69 views
7

我需要將雙色調(黑白)TIFF文件轉換爲另一種格式以供Web瀏覽器顯示,目前我們使用的是JPG格式,但格式並不重要。從圍繞.NET閱讀看起來似乎很難支持編寫雙色調圖像,所以我們最終得到了〜1MB的文件而不是〜100K的文件。我正在考慮使用ImageMagick來做到這一點,但理想情況下,我想要一個解決方案,如果可能的話不需要這個。在C中將雙色調TIFF轉換爲雙色調PNG

當前的代碼段(其中也沒有像一些大小調整):

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

我相信這個問題可以通過檢查resized位圖是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(); 
    }   
} 
+0

這太棒了。非常感謝你。 – 2010-08-05 15:52:52

0

試圖雅羅對顏色深度的建議不工作:

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

我在找這個.... – 2010-08-05 13:20:07

0

你試過PNG與1位色彩深度?

要獲得與CCITT4 TIFF相似的尺寸,我相信您的圖片需要使用1位索引托盤。

但是,您不能使用.NET中的圖形對象在索引圖像上繪圖。

您可能必須使用LockBits來操作每個像素。

參見Bob Powell's excellent article

+0

修改版的代碼生成與PNG格式相同的結果。 – 2010-08-05 13:19:43

0

這是一個古老的線程。不過,我會加2美分。

我使用AForge。網絡庫(開源)

使用這些dll。 Aforge.dllAForge.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); 
}