0

I have 8 bit per sample image grayscale. I want to convert it using Libtiff.net:

 using (Tiff input = Tiff.Open(fileIn, "r"))
            {
                using (Tiff output = Tiff.Open(fileOut, "w"))
                {
                    for (short page = 0; page < input.NumberOfDirectories(); page++)
                    {
                        input.SetDirectory(page);
                        output.SetDirectory(page);

                        if (input.GetField(TiffTag.COMPRESSION)[0].ToInt() == (int)Compression.LZW)
                        {
                            int width = input.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
                            int height = input.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
                            int xres = input.GetField(TiffTag.XRESOLUTION)[0].ToInt();
                            int yres = input.GetField(TiffTag.YRESOLUTION)[0].ToInt();

                            int samplesPerPixel = input.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToInt();
                            int bitsPerSample = input.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt();
                            int photo = input.GetField(TiffTag.PHOTOMETRIC)[0].ToInt();

                            int scanlineSize = input.ScanlineSize();

                            byte[][] buffer = new byte[height][];


                            for (int i = 0; i < height; ++i){
                                buffer[i] = new byte[scanlineSize];
                                input.ReadScanline(buffer[i], i);    
                            }

                            output.SetField(TiffTag.IMAGEWIDTH, width);
                            output.SetField(TiffTag.IMAGELENGTH, height);
                            output.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4);
                            output.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISBLACK);
                            output.SetField(TiffTag.SAMPLESPERPIXEL, 1);
                            output.SetField(TiffTag.BITSPERSAMPLE, 1);
                            output.SetField(TiffTag.XRESOLUTION, xres);
                            output.SetField(TiffTag.YRESOLUTION, yres);

                            for (int i = 0; i < height; ++i)
                                output.WriteScanline(buffer[i], i);

                            output.WriteDirectory();

                        }
                    }
                }

            }

How can i get 8 bit array to 1 bit array image? I want to do it without converting byte array to bitmap object. What is the right way? Thanks

davymartu
  • 1,393
  • 3
  • 15
  • 34

1 Answers1

0

First of all, if you go for 1 bit per pixel, note that you get an image with pure black and white pixels.

If your source is a grayscale image, you could just cap it off at 127, and make all > 127 values '1' and all <= 127 values '0', but to get a more accurate representation it might be useful to look into dithering functions.

For the 127-cutoff way, though, you just make an array of your 8-bit data, which should just have the brightness of each pixel as 0-255 range on each byte. The conversion to black and white is pretty trivial:

for (Int32 i=0; i > imagedata.Length; i++)
    imagedata[i] = imagedata[i] > 127 ? 1 : 0;

Then, to convert it to an array of 1-bit data, you can use this function. The "stride" is what you got as scanlineSize in your code. Note that this function only allocates the minimum needed amount of bytes for the stride.

    /// <summary>
    /// Converts given raw image data for a paletted 8-bit image to lower amount of bits per pixel.
    /// </summary>
    /// <param name="data8bit">The eight bit per pixel image data</param>
    /// <param name="width">The width of the image</param>
    /// <param name="height">The height of the image</param>
    /// <param name="newBpp">The new amount of bits per pixel</param>
    /// <param name="stride">Stride used in the original image data. Will be adjusted to the new stride value.</param>
    /// <param name="bigEndian">Values inside a single byte are read from the largest to the smallest bit.</param>
    /// <returns>The image data converted to the requested amount of bits per pixel.</returns>
private static Byte[] ConvertFrom8Bit(Byte[] data8bit, Int32 width, Int32 height, Int32 bitsLength, Boolean bigEndian)
    {
        if (newBpp > 8)
            throw new ArgumentException("Cannot convert to bit format greater than 8!", "newBpp");
        if (stride < width)
            throw new ArgumentException("Stride is too small for the given width!", "stride");
        if (data8bit.Length < stride * height)
            throw new ArgumentException("Data given data is too small to contain an 8-bit image of the given dimensions", "data8bit");
    Int32 parts = 8 / bitsLength;
    // Amount of bytes to write per width
    Int32 stride = ((bpp * width) + 7) / 8;
    // Bit mask for reducing original data to actual bits maximum.
    // Should not be needed if data is correct, but eh.
    Int32 bitmask = (1 << bitsLength) - 1;
    Byte[] dataXbit = new Byte[stride * height];
    // Actual conversion porcess.
    for (Int32 y = 0; y < height; y++)
    {
        for (Int32 x = 0; x < width; x++)
        {
            // This will hit the same byte multiple times
            Int32 indexXbit = y * stride + x / parts;
            // This will always get a new index
            Int32 index8bit = y * width + x;
            // Amount of bits to shift the data to get to the current pixel data
            Int32 shift = (x % parts) * bitsLength;
            // Reversed for big-endian
            if (bigEndian)
                shift = 8 - shift - bitsLength;
            // Get data, reduce to bit rate, shift it and store it.
            dataXbit[indexXbit] |= (Byte)((data8bit[index8bit] & bitmask) << shift);
        }
    }
    return dataXbit;
}

Then it's just a matter of creating a new 1-bit tiff of the correct dimensions, getting its stride, and writing these resulting bytes into the tiff, line by line, keeping the stride in mind.

Note: I don't know how this is with tiff, but in .Net, 1-bit images are paletted, so you might need to give the new image a palette containing that black and white.

Nyerguds
  • 5,360
  • 1
  • 31
  • 63