2010-08-05 7 views
7

J'ai besoin de convertir des fichiers TIFF bitonaux (noir et blanc) dans un autre format pour l'affichage par un navigateur Web, actuellement nous utilisons des fichiers JPG, mais le format n'est pas crucial. De la lecture autour de .NET ne semble pas facilement prendre en charge l'écriture d'images bitonales, nous finissons donc avec ~ 1 Mo de fichiers au lieu de ~ 100 Ko. J'envisage d'utiliser ImageMagick pour ce faire, mais idéalement je voudrais une solution qui ne nécessite pas cela si possible.convertir bitonal TIFF en biton PNG en C#

extrait de code actuel (qui fait aussi un peu Redimensionnement sur l'image):

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

    } 
} 

Y at-il moyen d'y parvenir?

Merci.

Répondre

7

Je crois que le problème peut être résolu en vérifiant que resized bitmap est de PixelFormat.Format1bppIndexed. Si ce n'est pas le cas, vous devriez le convertir en bitmap 1bpp et après cela, vous pouvez l'enregistrer en tant que png noir et blanc sans problèmes.

En d'autres termes, vous devez utiliser le code suivant au lieu 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); 
} 

J'utilise code suivant pour 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

c'est génial. Merci beaucoup. –

0

suggestion de Essayant jaroslav pour la profondeur de couleur ne fonctionne pas:

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

} 

Le jpeg est encore jpeg en couleur.

Je ne pense pas qu'il existe de support pour le jpeg en niveaux de gris dans gdi plus. Avez-vous essayé de regarder dans le composant d'imagerie Windows?

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

exemple de code: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx

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

+0

je suis à la recherche dans ce –

0

Avez-vous essayé PNG avec 1 bit profondeur de couleur?

Pour obtenir une taille similaire à celle d'un TIFF CCITT4, je crois que votre image doit utiliser une palette indexée de 1 bit.

Cependant, vous ne pouvez pas utiliser l'objet Graphics dans .NET pour dessiner sur une image indexée.

Vous devrez probablement utiliser LockBits pour manipuler chaque pixel.

Voir Bob Powell's excellent article.

+0

.... modifiant le code de Ed produit les mêmes résultats avec PNGs. –

0

C'est un vieux fil. Cependant, je vais ajouter mes 2 cents.

J'utilise AForge.Bibliothèques réseau (code source ouvert)

utiliser ces dll. 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); 
}