将多频段16位tiff图像转换为8位tiff图像

时间:2015-04-08 02:12:50

标签: c# arrays image-processing tiff gdal

我从16位(范围0-65535)tif图像中得到一些像素数据作为整数数组。我使用gdal读取器获得了值。如何将它们转换为8位(0-225)并将其(数组)转换为8位tif图像?

以下是我的一些代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OSGeo.GDAL;
using OSGeo.OSR;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            Gdal.AllRegister();
            Dataset data1;
            int xsize, ysize;
            int bandsize;

            data1 = Gdal.Open("F:\\po_1473547_bgrn_0000000.tif", Access.GA_ReadOnly);

            bandsize = data1.RasterCount;

            xsize = data1.RasterXSize; //cols
            ysize = data1.RasterYSize; //rows

            Console.WriteLine("cols : "+xsize+", rows : "+ysize);

            Band[] bands = new Band[bandsize];
            for (int i = 0; i < bandsize; i++) {
                bands[i] = data1.GetRasterBand(i+1);
            }


            int[,,] pixel = new int[bandsize,xsize,ysize]; 
            int[] pixtemp = new int[xsize * ysize];


            for (int i = 0; i < bandsize; i++)
            {
                bands[i].ReadRaster(0, 0, xsize, ysize, pixtemp, xsize, ysize, 0, 0);

                for (int j = 0; j < xsize; j++)
                {
                    for (int k = 0; k < ysize; k++)
                    {
                        pixel[i,j,k] = pixtemp[j + k * xsize];
                    }
                }
            }


            Console.WriteLine("");

            for (int i = 0; i < bandsize; i++)
            {
                Console.WriteLine("some pixel from band " + (i+1));
                for (int j = 0; j < 100; j++)
                {
                    Console.Write(" " + pixel[i,100,j]);
                }

                Console.WriteLine("\n\n");
            }
        }
    }
}

我正在谷歌搜索如何做到这一点但我只发现如果数据类型是一个字节如何做到这一点。有人请给我一个提示。

2 个答案:

答案 0 :(得分:2)

我不知道GEO Tiff格式,但要将常规16位tiff图像文件转换为8位图像文件,您需要将16位通道值缩放到8位。下面的示例显示了如何在灰度图像中实现此目的。

public static class TiffConverter
{
    private static IEnumerable<BitmapSource> Load16BitTiff(Stream source)
    {
        var decoder = new TiffBitmapDecoder(source, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
        for (int i = 0; i < decoder.Frames.Count; i++)
            // return all frames that are present in the input.
            yield return decoder.Frames[i];
    }

    private static BitmapSource NormalizeTiffTo8BitImage(BitmapSource source)
    {
        // allocate buffer & copy image bytes.
        var rawStride = source.PixelWidth * source.Format.BitsPerPixel / 8;
        var rawImage = new byte[rawStride * source.PixelHeight];
        source.CopyPixels(rawImage, rawStride, 0);

        // get both max values of first & second byte of pixel as scaling bounds.
        var max1 = 0; 
        int max2 = 1; 
        for (int i = 0; i < rawImage.Length; i++)
        {
            if ((i & 1) == 0)
            {
                if (rawImage[i] > max1)
                    max1 = rawImage[i];
            }
            else if (rawImage[i] > max2)
                max2 = rawImage[i];
        }

        // determine normalization factors.
        var normFactor = max2 == 0 ? 0.0d : 128.0d / max2;
        var factor = max1 > 0 ? 255.0d / max1 : 0.0d;
        max2 = Math.Max(max2, 1);

        // normalize each pixel to output buffer.
        var buffer8Bit = new byte[rawImage.Length / 2];
        for (int src = 0, dst = 0; src < rawImage.Length; dst++)
        {
            int value16 = rawImage[src++];
            double value8 = ((value16 * factor) / max2) - normFactor;

            if (rawImage[src] > 0)
            {
                int b = rawImage[src] << 8;
                value8 = ((value16 + b) / max2) - normFactor;
            }
            buffer8Bit[dst] = (byte)Math.Min(255, Math.Max(value8, 0));
            src++;
        }

        // return new bitmap source.
        return BitmapSource.Create(
            source.PixelWidth, source.PixelHeight,
            source.DpiX, source.DpiY, 
            PixelFormats.Gray8, BitmapPalettes.Gray256,
            buffer8Bit, rawStride / 2);
    }

    private static void SaveTo(IEnumerable<BitmapSource> src, string fileName)
    {
        using (var stream = File.Create(fileName))
        {
            var encoder = new TiffBitmapEncoder();
            foreach (var bms in src)
                encoder.Frames.Add(BitmapFrame.Create(bms));
            encoder.Save(stream);
        }
    }

    public static void Convert(string inputFileName, string outputFileName)
    {
        using (var inputStream = File.OpenRead(inputFileName))
            SaveTo(Load16BitTiff(inputStream).Select(NormalizeTiffTo8BitImage), outputFileName);
    }
}

用法:

TiffConverter.Convert(@"c:\temp\16bit.tif", @"c:\temp\8bit.tif");

答案 1 :(得分:0)

将像素从16位插值到8位,某些重采样方法可以执行。

Linear Interpolation可能有所帮助。

//Convert tiff from 16-bit to 8-bit
byte[,,] ConvertBytes(int[,,] pixel, bandsize, xsize, ysize)
{
    byte[,,] trgPixel = new byte[bandsize,xsize,ysize];

    for (int i = 0; i < bandsize; i++)
    {
        for (int j = 0; j < xsize; j++)
        {
            for (int k = 0; k < ysize; k++)
            {
                //Linear Interpolation
                trgPixel[i,j,k] = (byte)((65535-pixel[i,j,k])/65536.0*256);
            }
        }
    }

    return trgPixel;
}

//Save 8-bit tiff to file
void SaveBytesToTiff(string destPath, byte[,,] pixel, bandsize, xsize, ysize)
{
    string fileformat = "GTiff";
    Driver dr = Gdal.getDriverByName(fileformat);
    Dataset newDs = dr.Create(destPath, xsize, ysize, bandsize, DateType.GDT_Byte, null);

    for(int i=0; i< bandsize;i++)
    {
        byte[] buffer = new byte[xsize * ysize];
        for (int j = 0; j < xsize; j++)
        {
            for (int k = 0; k < ysize; k++)
            {
                buffer[j+k*xsize] = pixel[i,j,k];
            }
        }
        newDs.WriteRaster(0, 0, xsize, ysize, buffer, xsize, ysize, i+1, null, 0, 0, 0);
        newDs.FlushCache();      
    }

    newDs.Dispose();            
}