2010-08-05 52 views
7

Tôi cần chuyển đổi các tệp TIFF bitmap (đen và trắng) sang định dạng khác để hiển thị bằng trình duyệt web, hiện tại chúng tôi đang sử dụng JPG, nhưng định dạng không quan trọng. Từ đọc xung quanh .NET dường như không dễ dàng hỗ trợ viết hình ảnh bitonal, vì vậy chúng tôi sẽ kết thúc với ~ 1MB tệp thay vì ~ 100 nghìn tệp. Tôi đang xem xét sử dụng ImageMagick để làm điều này, nhưng lý tưởng tôi muốn một giải pháp mà không yêu cầu này nếu có thể.chuyển đổi bitonal TIFF sang PNG dạng bit trong C#

đoạn mã hiện tại (mà cũng hiện một số thay đổi kích thước trên hình ảnh):

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

    } 
} 

Có cách nào để đạt được điều này?

Cảm ơn.

Trả lời

7

Tôi tin rằng sự cố có thể được giải quyết bằng cách kiểm tra rằng bitmap resizedPixelFormat.Format1bppIndexed. Nếu không, bạn nên chuyển đổi nó thành bitmap 1bpp và sau đó bạn có thể lưu nó dưới dạng png đen và trắng mà không gặp vấn đề gì.

Nói cách khác, bạn nên sử dụng đoạn mã sau thay vì 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); 
} 

tôi sử dụng sau mã cho 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

thật tuyệt vời. Cảm ơn nhiều. –

0

Cố Jaroslav cho độ sâu màu không hoạt động:

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

} 

Các jpeg vẫn là một màu jpeg đầy đủ.

Tôi không nghĩ rằng có bất kỳ hỗ trợ nào cho jpeg thang độ xám trong gdi plus. Bạn đã thử tìm kiếm trong thành phần hình ảnh cửa sổ?

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

mã ví dụ: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx

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

+0

tôi đang nhìn vào điều này .... –

0

Các bạn đã thử PNG với độ sâu màu 1 chút?

Để đạt được kích thước tương tự như TIFF CCITT4, tôi tin rằng hình ảnh của bạn cần sử dụng bảng màu được lập chỉ mục 1 bit.

Tuy nhiên, bạn không thể sử dụng đối tượng Đồ họa trong .NET để vẽ trên hình ảnh được lập chỉ mục.

Có thể bạn sẽ phải sử dụng LockBits để thao tác từng pixel.

Xem Bob Powell's excellent article.

+0

thay đổi mã của Ed tạo ra kết quả tương tự với PNG. –

0

Đây là một chủ đề cũ. Tuy nhiên, tôi sẽ thêm 2 xu của tôi.

Tôi sử dụng AForge.Thư viện Net (mã nguồn mở)

sử dụng các dll này. 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); 
} 
Các vấn đề liên quan