2010-04-24 18 views
6

Estoy usando C# y tengo una imagen almacenada en el mapa de bits del objeto.Reducción del tamaño de bitmap en C#

Ahora me gustaría convertir esta imagen en escala de grises de 8 bits, luego en una imagen en escala de grises de 4 bits.

¿Tiene alguna sugerencia de cómo se puede hacer esto?

Respuesta

6

En los formatos .NET Bitmap, no existe una imagen en escala de grises de 8 o 4 bits. Los formatos compatibles están enumerados por PixelFormat enumeration. Sin embargo, puede crear una imagen de 4 u 8 bits creando una imagen indexada (8bppIndexed o 4bppIndexed), donde cada entrada en la paleta es un valor de escala de grises.

Este código tiene un mapa de bits y crea una copia como una imagen indexada 8bpp con los valores de escala de grises:

public static Bitmap BitmapToGrayscale(Bitmap source) 
    { 
     // Create target image. 
     int width = source.Width; 
     int height = source.Height; 
     Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed); 
     // Set the palette to discrete shades of gray 
     ColorPalette palette = target.Palette;    
     for(int i = 0 ; i < palette.Entries.Length ; i++) 
     {     
      palette.Entries[i] = Color.FromArgb(0,i,i,i); 
     } 
     target.Palette = palette; 

     // Lock bits so we have direct access to bitmap data 
     BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height), 
               ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); 
     BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height), 
               ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

     unsafe 
     { 
      for(int r = 0 ; r < height ; r++) 
      { 
       byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride); 
       byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride); 
       for(int c = 0 ; c < width ; c++) 
       { 
        byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)); 
        *pTarget = colorIndex; 
        pTarget++; 
        pSource += 3; 
       } 
      } 
     } 

     target.UnlockBits(targetData); 
     source.UnlockBits(sourceData); 
     return target; 
    } 

Con el fin de hacer una imagen de 4bpp lugar, lo que se necesita para crear el objetivo con PixelFormat. Format4bppIndexed, y luego establece la ColorPalette en 16 tonos discretos de gris. Finalmente, en el ciclo, debe normalizar que los valores 2 estén entre 0-15 y que empaquete cada valor de 2 píxeles en un solo byte.

Este es el código modificado para hacer una imagen en escala de grises 4bpp:

public static Bitmap BitmapToGrayscale4bpp(Bitmap source) 
    { 
     // Create target image. 
     int width = source.Width; 
     int height = source.Height; 
     Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed); 
     // Set the palette to discrete shades of gray 
     ColorPalette palette = target.Palette;    
     for(int i = 0 ; i < palette.Entries.Length ; i++) 
     { 
      int cval = 17*i; 
      palette.Entries[i] = Color.FromArgb(0,cval,cval,cval); 
     } 
     target.Palette = palette; 

     // Lock bits so we have direct access to bitmap data 
     BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height), 
               ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed); 
     BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height), 
               ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

     unsafe 
     { 
      for(int r = 0 ; r < height ; r++) 
      { 
       byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride); 
       byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride); 
       byte prevValue = 0; 
       for(int c = 0 ; c < width ; c++) 
       { 
        byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11))/16); 
        if (c % 2 == 0) 
         prevValue = colorIndex; 
        else 
         *(pTarget++) = (byte)(prevValue | colorIndex << 4); 

        pSource += 3; 
       } 
      } 
     } 

     target.UnlockBits(targetData); 
     source.UnlockBits(sourceData); 
     return target; 
    } 
+0

La última parte del 'BitmapToGrayscale4bpp' tiene un error menor:' (byte) (prevValue | ColorIndex << 4) '' debe estar (byte) (prevValue << 4 | colorIndex) '. El mordisco prevValue debe aparecer antes del nibble colorIndex en el byte de salida. – lnmx

Cuestiones relacionadas