Necesito una función de C#, que tendrá un Byte[] de 8 bits en escala de grises en formato TIFF, y devolver un Byte[] de 1 bit (negro & blanco) TIFF.

Soy bastante nuevo para trabajar con archivos Tiff, pero la idea general es que tenemos la necesidad de convertir en escala de grises o en color a blanco y negro/monocromático/binario de formato de imagen.

Recibimos las imágenes a través de un WCF como un Byte[], entonces tenemos que hacer esta conversión negro & blanco con el fin de enviarlos a un componente que hace su posterior procesamiento. No tenemos planes en este punto, nunca guardarlas como archivos.

De referencia, en nuestro cliente de prueba, esta es la forma de crear el Byte[]:

        FileStream fs = new FileStream("test1.tif", FileMode.Open, FileAccess.Read);
        this.image = new byte[fs.Length];
        fs.Read(this.image, 0, System.Convert.ToInt32(fs.Length));
        fs.Close();

——–actualización———

Creo que puede haber más de 1 buena respuesta aquí, pero terminamos utilizando el código de la CodeProject sitio con el siguiente método agregado a la sobrecarga de la función de conversión a aceptar Byte[] así como de mapa de bits:

public static Byte[] ConvertToBitonal(Byte[] original)
    {
        Bitmap bm = new Bitmap(new System.IO.MemoryStream(original, false));
        bm = ConvertToBitonal(bm);
        System.IO.MemoryStream s = new System.IO.MemoryStream();
        bm.Save(s, System.Drawing.Imaging.ImageFormat.Tiff);
        return s.ToArray();
    }
  • Esta no es la manera que usted desea hacer esto. Para hacer este trabajo, es necesario analizar todo el TIFF. TIFF es un no-trivial formato de archivo. El uso de un kit de herramientas que pueden leer y escribir archivos TIFF.
  • Estoy buscando una función de c# que analizar todo el TIFF como usted menciona. Hasta ahora he visto un par en VB, y actualmente estoy revisando uno en c#. Nos gustaría que la fuente en lugar de un componente sellado.
InformationsquelleAutor alchemical | 2009-07-23

7 Comentarios

  1. 4

    Hay un artículo en CodeProject aquí que describe lo que usted necesita.

    • Esta solución parece haber funcionado! He añadido una función que permite a aceptar Byte[] así como de mapa de bits.
  2. 3

    @neodimio tiene una buena respuesta, pero GetPixel/SetPixel va a matar rendimiento. Bob Powell tiene un gran método aquí: http://www.bobpowell.net/onebit.htm

    C#:

        private Bitmap convertTo1bpp(Bitmap img)
    {
    BitmapData bmdo = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),
    ImageLockMode.ReadOnly, 
    img.PixelFormat);
    //and the new 1bpp bitmap
    Bitmap bm = new Bitmap(img.Width, img.Height, PixelFormat.Format1bppIndexed);
    BitmapData bmdn = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
    ImageLockMode.ReadWrite, 
    PixelFormat.Format1bppIndexed);
    //scan through the pixels Y by X
    for(int y = 0; y < img.Height; y++)
    {
    for(int x = 0; x < img.Width; x++)
    {
    //generate the address of the colour pixel
    int index = y * bmdo.Stride + x * 4;
    //check its brightness
    if(Color.FromArgb(Marshal.ReadByte(bmdo.Scan0, index + 2), 
    Marshal.ReadByte(bmdo.Scan0, index + 1), 
    Marshal.ReadByte(bmdo.Scan0, index)).GetBrightness() > 0.5F)
    {
    setIndexedPixel(x, y, bmdn, true); //set it if its bright.
    }
    }
    }
    //tidy up
    bm.UnlockBits(bmdn);
    img.UnlockBits(bmdo);
    return bm;
    }
    private void setIndexedPixel(int x, int y, BitmapData bmd, bool pixel)
    {
    int index = y * bmd.Stride + (x >> 3);
    byte p = Marshal.ReadByte(bmd.Scan0, index);
    byte mask = (byte)(0x80 >> (x & 0x7));
    if (pixel)
    {
    p |= mask;
    }
    else
    {
    p &= (byte)(mask ^ 0xFF);
    }
    Marshal.WriteByte(bmd.Scan0, index, p);
    }
    • Esto es útil, pero el código es en VB en lugar de c#.
    • Este método es el uso de simples umbral para hacer la conversión.
    • Que curioso, no me había dado cuenta que estaba en VB! Aquí es en C#…
    • OK, que parece interesante. ¿Cómo puedo obtener mi Byte[] en el objeto de mapa de bits a enviar a esta función?
    • No estoy seguro de que usted será capaz de. De acuerdo a la msdn.microsoft.com/en-us/library/…, 8 bits en escala de grises no es compatible PixelFormat.
    • si usted ya tiene un Byte[], usted incluso no necesita leer el mapa de bits de origen a través de una instancia de BitmapData. Sólo el uso de la matriz de Bytes – sólo asegúrese de que usted sabe lo que la zancada es.

  3. 2

    puede ser que desee comprobar hacia fuera ‘de Craig Utilidad de la Biblioteca’ yo creo que él tiene esa funcionalidad en su lugar.
    Craig Biblioteca de Utilidad

    • Este es un fresco de la biblioteca, sin embargo, parece centrarse en los mapas de bits en lugar de tifs.
  4. 1

    Mis productos de la empresa, dotImage, va a hacer esto.

    Dada una imagen, usted puede convertir de multi-bit a bit mediante varios métodos, incluyendo una simple umbral, umbral global, local umbral, umbral de adaptación, el tramado (ordenado y Floyd Steinberg), y la dinámica de umbral. La elección correcta depende de el tipo de la imagen de entrada (documento, imagen, gráfico).

    El código típico se parece a esto:

    AtalaImage image = new AtalaImage("path-to-tiff", null);
    ImageCommand threshold = SomeFactoryToConstructAThresholdCommand();
    AtalaImage finalImage = threshold.Apply(image).Image;

    SomeFactoryToConstructAThresholdcommand() es un método que devolverá un nuevo comando que se va a procesar la imagen. Podría ser tan simple como

    return new DynamicThresholdCommand();

    o

    return new GlobalThresholdCommand();

    Y, por lo general, si usted está buscando para convertir todo un tiff de varias páginas en blanco y negro, tendría que hacer algo como esto:

    //open a sequence of images
    FileSystemImageSource source = new FileSystemImageSource("path-to-tiff", true);
    using (FileStream outstm = new FileStream("outputpath", FileMode.Create)) {
    //make an encoder and a threshold command
    TiffEncoder encoder = new TiffEncoder(TiffCompression.Auto, true);
    //dynamic is good for documents -- needs the DocumentImaging SDK
    ImageCommand threshold = new DynamicThreshold();
    while (source.HasMoreImages()) {
    //get next image
    AtalaImage image = source.AcquireNext();
    AtalaImage final = threshold.Apply(image).Image;
    try {
    encoder.Save(outstm, final, null);
    }
    finally {
    //free memory from current image
    final.Dispose();
    //release the source image back to the image source
    source.Release(image);
    }
    }
    }
    • Yo no puedo upvote esto, pero me estoy dando un virtual +1 por ser un código de chulo.
    • él big pimpin
  5. 0

    Primer lugar, usted necesita saber cómo una X,Y posición de píxel se asigna a un valor de índice en el que la matriz.
    Esto va a depender de cómo su Byte[] fue construido.
    Usted necesita saber los detalles de su formato de la imagen – por ejemplo, ¿cuál es la zancada?

    No veo de 8 bits en escala de grises TIFF en el PixelFormat enumeración. Si estaba allí, ella le dirá lo que usted necesita saber.

    Entonces, iterar a través de cada píxel y mirar a su valor de color.
    Usted tendrá que decidir sobre un valor de umbral – si el color de los píxeles por encima del umbral, hacer que el nuevo color blanco; de lo contrario, que sea negro.

    Si quieres simular la escala de grises de sombreado con 1BPP, se podía ver en las técnicas más avanzadas, como tramado.

  6. 0

    Algo como esto podría funcionar, yo no lo he probado. (Debe ser fácil de C# es.)

        Dim bmpGrayscale As Bitmap = Bitmap.FromFile("Grayscale.tif")
    Dim bmpMonochrome As New Bitmap(bmpGrayscale.Width, bmpgrayscale.Height, Imaging.PixelFormat.Format1bppIndexed)
    Using gfxMonochrome As Graphics = Graphics.FromImage(bmpMonochrome)
    gfxMonochrome.Clear(Color.White)
    End Using
    For y As Integer = 0 To bmpGrayscale.Height - 1
    For x As Integer = 0 To bmpGrayscale.Width - 1
    If bmpGrayscale.GetPixel(x, y) <> Color.White Then
    bmpMonochrome.SetPixel(x, y, Color.Black)
    End If
    Next
    Next
    bmpMonochrome.Save("Monochrome.tif")

    Esto podría ser una manera mejor aún:

    Using bmpGrayscale As Bitmap = Bitmap.FromFile("Grayscale.tif")
    Using bmpMonochrome As New Bitmap(bmpGrayscale.Width, bmpgrayscale.Height, Imaging.PixelFormat.Format1bppIndexed)
    Using gfxMonochrome As Graphics = Graphics.FromImage(bmpMonochrome)
    gfxMonochrome.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
    gfxMonochrome.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    gfxMonochrome.DrawImage(bmpGrayscale, new Rectangle(0, 0, bmpMonochrome.Width, bmpMonochrome.Height)
    End Using
    bmpMonochrome.Save("Monochrome.tif")
    End Using
    End Using

    Creo que el término que se busca es «remuestreo».

    • Esto convertirá a cualquier no-píxel blanco negro, para todos los tonos de gris se convertirá en negro – probablemente no es lo que él quiere.
    • Yup. Pero tienes que admitir que sería bastante fácil de aplicar un umbral. 🙂
  7. 0

    píxel por píxel de la manipulación es extremadamente lento. 40 veces más lento que el del Sistema.DrawImage.
    Sistema.Dibujar la imagen es la mitad de la solución, daña la imagen (300dpi–>96 ppp) y produce a 300 dpi de origen de 200-400kb grandes archivos de resultados.

            public static Image GetBlackAndWhiteImage(Image SourceImage)
    {
    Bitmap bmp = new Bitmap(SourceImage.Width, SourceImage.Height);
    using (Graphics gr = Graphics.FromImage(bmp)) //SourceImage is a Bitmap object
    {
    var gray_matrix = new float[][] {
    new float[] { 0.299f, 0.299f, 0.299f, 0, 0 },
    new float[] { 0.587f, 0.587f, 0.587f, 0, 0 },
    new float[] { 0.114f, 0.114f, 0.114f, 0, 0 },
    new float[] { 0,      0,      0,      1, 0 },
    new float[] { 0,      0,      0,      0, 1 }
    };
    var ia = new System.Drawing.Imaging.ImageAttributes();
    ia.SetColorMatrix(new System.Drawing.Imaging.ColorMatrix(gray_matrix));
    ia.SetThreshold(float.Parse(Settings.Default["Threshold"].ToString())); //Change this threshold as needed
    var rc = new Rectangle(0, 0, SourceImage.Width, SourceImage.Height);
    gr.DrawImage(SourceImage, rc, 0, 0, SourceImage.Width, SourceImage.Height, GraphicsUnit.Pixel, ia);
    }
    return bmp;
    }

    La manera perfecta es simplemente convertir a CCITT decodificado tif, que contiene sólo BW. Mucho más eficiente método con 30-50 kb archivo del resultado, 300dpi también sigue siendo correcta así:

            public void toCCITT(string tifURL)
    {
    byte[] imgBits = File.ReadAllBytes(tifURL);
    using (MemoryStream ms = new MemoryStream(imgBits))
    {
    using (Image i = Image.FromStream(ms))
    {
    EncoderParameters parms = new EncoderParameters(1);
    ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders()
    .FirstOrDefault(decoder => decoder.FormatID == ImageFormat.Tiff.Guid);
    parms.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
    i.Save(@"c:\test\result.tif", codec, parms);
    }
    }
    }

    Buena Suerte Bro,

Dejar respuesta

Please enter your comment!
Please enter your name here