将每采样8位图像转换为每采样1位图像

本文关键字:图像 采样 1位 8位 转换 | 更新日期: 2023-09-27 18:24:06

我有每个样本8位的图像灰度。我想使用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();
                        }
                    }
                }
            }

如何获取8位数组到1位数组的图像?我想在不将字节数组转换为位图对象的情况下完成此操作。什么是正确的方法?感谢

将每采样8位图像转换为每采样1位图像

首先,如果您选择每个像素1位,请注意,您得到的图像具有纯黑白像素

如果您的源是灰度图像,则可以将其上限设置为127,并将所有>127的值设置为"1"和all<=127个值"0",但为了获得更准确的表示,研究抖动函数可能会很有用。

不过,对于127截止方式,您只需制作一个8位数据的数组,该数组的每个像素的亮度应在每个字节的0-255范围内。转换为黑白是相当琐碎的:

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

然后,要将其转换为1位数据的数组,可以使用此函数。"步幅"是您在代码中获得的scanlineSize。请注意,此函数只为步幅分配所需的最小字节数。

    /// <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;
}

然后,只需要创建一个新的具有正确尺寸的1位tiff,获取其步幅,并将这些字节逐行写入tiff,记住步幅。

注意:我不知道tiff是怎么回事,但在.Net中,1位图像调色板,所以你可能需要给新图像一个包含黑白的调色板。