即使在反转图像数据后,也会出现Photometric Interpretation标记问题

时间:2014-11-28 13:47:54

标签: jpeg tiff libtiff.net

注意:再次提供我之前问题的背景,以便在一个来源找到所有相关内容。

我正在从Android移动设备捕获图像,它是JPEG格式的。图像为72X72DPI和24位。当我尝试使用LibTiff.Net将此JPEG图像转换为TIFF并为MinIsWhite设置标签Photometric Interpretation = 0时,图像变为负值(白色变为黑色,黑色变为白色)。环境是Windows 8.1 64位,Visual Studio 2012.标记必须具有值0,其中0 =白色为零。

我绝对必须在图像中使用Photometric.MINISWHITE,所以尝试在将图像数据写入TIFF之前将其反转,如下面的代码所示。但随后压缩变为LZW而不是CCITT4,Photometric从MINISWHITE更改为MINISBLACK,FIllorder标记被删除,PlanarConfig标记被删除,新标记Predictor添加值1,图像再次变为负值。

public partial class Form1 : Form
    {
        private const TiffTag TIFFTAG_ASCIITAG = (TiffTag)666;
        private const TiffTag TIFFTAG_LONGTAG = (TiffTag)667;
        private const TiffTag TIFFTAG_SHORTTAG = (TiffTag)668;
        private const TiffTag TIFFTAG_RATIONALTAG = (TiffTag)669;
        private const TiffTag TIFFTAG_FLOATTAG = (TiffTag)670;
        private const TiffTag TIFFTAG_DOUBLETAG = (TiffTag)671;
        private const TiffTag TIFFTAG_BYTETAG = (TiffTag)672;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (Bitmap bmp = new Bitmap(@"D:\Projects\ITests\images\IMG_2.jpg"))
            {
                // convert jpg image to tiff
                byte[] tiffBytes = GetTiffImageBytes(bmp, false);
                File.WriteAllBytes(@"D:\Projects\ITests\images\output.tif", tiffBytes);

                //Invert the tiff image
                Bitmap bmpTiff = new Bitmap(@"D:\Projects\ITests\images\output.tif");
                Bitmap FBitmap = Transform(bmpTiff);
                FBitmap.Save(@"D:\Projects\ITests\images\invOutput1.tif");

            }
        }

        public static byte[] GetTiffImageBytes(Bitmap img, bool byScanlines)
        {
            try
            {
                byte[] raster = GetImageRasterBytes(img);

                using (MemoryStream ms = new MemoryStream())
                {
                    using (Tiff tif = Tiff.ClientOpen("InMemory", "w", ms, new TiffStream()))
                    {
                        if (tif == null)
                            return null;

                        tif.SetField(TiffTag.IMAGEWIDTH, img.Width);
                        tif.SetField(TiffTag.IMAGELENGTH, img.Height);
                        tif.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4);
                        tif.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISWHITE);
                        tif.SetField(TiffTag.ROWSPERSTRIP, img.Height);
                        tif.SetField(TiffTag.XRESOLUTION, 200);
                        tif.SetField(TiffTag.YRESOLUTION, 200);
                        tif.SetField(TiffTag.SUBFILETYPE, 0);
                        tif.SetField(TiffTag.BITSPERSAMPLE, 1);
                        tif.SetField(TiffTag.FILLORDER, FillOrder.LSB2MSB);
                        tif.SetField(TiffTag.ORIENTATION, BitMiracle.LibTiff.Classic.Orientation.TOPLEFT);
                        tif.SetField(TiffTag.SAMPLESPERPIXEL, 1);
                        tif.SetField(TiffTag.RESOLUTIONUNIT, ResUnit.INCH);
                        tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
                        int tiffStride = tif.ScanlineSize();
                        int stride = raster.Length / img.Height;
                        if (byScanlines)
                        {
                            // raster stride MAY be bigger than TIFF stride (due to padding in raster bits) 
                            for (int i = 0, offset = 0; i < img.Height; i++)
                            {
                                bool res = tif.WriteScanline(raster, offset, i, 0);
                                if (!res)
                                    return null;

                                offset += stride;
                            }
                        }
                        else
                        {
                            if (tiffStride < stride)
                            {
                                // raster stride is bigger than TIFF stride 
                                // this is due to padding in raster bits 
                                // we need to create correct TIFF strip and write it into TIFF 
                                byte[] stripBits = new byte[tiffStride * img.Height];
                                for (int i = 0, rasterPos = 0, stripPos = 0; i < img.Height; i++)
                                {
                                    System.Buffer.BlockCopy(raster, rasterPos, stripBits, stripPos, tiffStride);
                                    rasterPos += stride;
                                    stripPos += tiffStride;
                                }

                                // Write the information to the file 
                                int n = tif.WriteEncodedStrip(0, stripBits, stripBits.Length);
                                if (n <= 0)
                                    return null;
                            }
                            else
                            {
                                // Write the information to the file 
                                int n = tif.WriteEncodedStrip(0, raster, raster.Length);
                                if (n <= 0)
                                    return null;
                            }
                        }
                    }

                    return ms.GetBuffer();
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static byte[] GetImageRasterBytes(Bitmap img)
        {
            // Specify full image
            Rectangle rect = new Rectangle(0, 0, img.Width, img.Height);

            Bitmap bmp = img;
            byte[] bits = null;

            try
            {
                // Lock the managed memory 
                if (img.PixelFormat != PixelFormat.Format1bppIndexed)
                    bmp = convertToBitonal(img);

                BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);

                // Declare an array to hold the bytes of the bitmap.
                bits = new byte[bmpdata.Stride * bmpdata.Height];

                // Copy the sample values into the array.
                Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);

                // Release managed memory
                bmp.UnlockBits(bmpdata);
            }
            finally
            {
                if (bmp != img)
                    bmp.Dispose();
            }

            return bits;
        }

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

        }

        public Bitmap Transform(Bitmap bitmapImage)
        {
            var bitmapRead = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
            var bitmapLength = bitmapRead.Stride * bitmapRead.Height;
            var bitmapBGRA = new byte[bitmapLength];
            Marshal.Copy(bitmapRead.Scan0, bitmapBGRA, 0, bitmapLength);
            bitmapImage.UnlockBits(bitmapRead);
            for (int i = 0; i < bitmapLength; i += 4)
            {
                bitmapBGRA[i] = (byte)(255 - bitmapBGRA[i]);
                bitmapBGRA[i + 1] = (byte)(255 - bitmapBGRA[i + 1]);
                bitmapBGRA[i + 2] = (byte)(255 - bitmapBGRA[i + 2]);
                //        [i + 3] = ALPHA.
            }

            var bitmapWrite = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
            Marshal.Copy(bitmapBGRA, 0, bitmapWrite.Scan0, bitmapLength);
            bitmapImage.UnlockBits(bitmapWrite);

            return bitmapImage;
        }
    }

1 个答案:

答案 0 :(得分:0)

在将它们写入TIFF之前,您应该在GetTiffImageBytes方法中反转图像字节。此外,Transform方法将双级图像转换为32bpp,这就是最终获得LZW压缩图像的原因。

所以,添加以下代码

for (int k = 0; k < raster.Length; k++)
    raster[k] = (byte)(~raster[k]);
byte[] raster = GetImageRasterBytes(img);方法中GetTiffImageBytes之后

。这将反转图像字节。并且不要使用以下代码

//Invert the tiff image
Bitmap bmpTiff = new Bitmap(@"D:\Projects\ITests\images\output.tif");
Bitmap FBitmap = Transform(bmpTiff);
FBitmap.Save(@"D:\Projects\ITests\images\invOutput1.tif");