使用LibTIFF.NET C#将32位灰度Tiff与浮点像素值连接到数组

时间:2015-06-06 16:28:11

标签: c# floating-point 32-bit grayscale libtiff.net

我刚开始在我的c#应用程序中使用LibTIFF.NET来读取从ArcGIS服务器获取的高度图的Tiff图像。我需要的是使用图像的像素值填充数组,以便基于平滑渐变生成地形。该图像是LZW压缩的32位灰度Tiff,浮点像素值表示以米为单位的elevaion。

现在已经有几天我很难回归正确的值,但我得到的只是“0”值,假设它是一个完整的黑色或白色图像!

以下是目前的代码:(更新 - 阅读更新1)

using (Tiff inputImage = Tiff.Open(fileName, "r"))
        {
            int width = inputImage.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
            int height = inputImage.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
            int bytesPerPixel = 4;
            int count = (int)inputImage.RawTileSize(0); //Has to be: "width * height * bytesPerPixel" ?
            int resolution = (int)Math.Sqrt(count);
            byte[] inputImageData = new byte[count]; //Has to be: byte[] inputImageData = new byte[width * height * bytesPerPixel];
            int offset = 0;

            for (int i = 0; i < inputImage.NumberOfTiles(); i++)
            {
                offset += inputImage.ReadEncodedTile(i, inputImageData, offset, (int)inputImage.RawTileSize(i));
            }

            float[,] outputImageData = new float[resolution, resolution]; //Has to be: float[,] outputImageData = new float[width * height];
            int length = inputImageData.Length;
            Buffer.BlockCopy(inputImageData, 0, outputImageData, 0, length);

            using (StreamWriter sr = new StreamWriter(fileName.Replace(".tif", ".txt"))) {
                string row = "";

                for(int i = 0; i < resolution; i++) { //Change "resolution" to "width" in order to have correct array size
                    for(int j = 0; j < resolution; j++) { //Change "resolution" to "height" in order to have correct array size
                        row += outputImageData[i, j] + " ";
                    }
                    sr.Write(row.Remove(row.Length - 1) + Environment.NewLine);
                    row = "";
                }
            }
        }

示例文件&amp;结果:http://terraunity.com/SampleElevationTiff_Results.zip

已在互联网上的所有地方搜索过,无法找到针对此特定问题的解决方案。所以我非常感谢帮助也使其对其他人有用。

更新1:

根据Antti Leppänen的答案更改了代码但得到了奇怪的结果,这似乎是一个错误或我错过了什么?请参阅上传的zip文件,以便在此处查看带有新32x32 tiff图像的结果:

http://terraunity.com/SampleElevationTiff_Results.zip

结果:

  • LZW Compressed:RawStripSize = ArraySize = 3081 = 55x55 grid
  • 未压缩:RawStripSize = ArraySize = 65536 = 256x256网格

必须:RawStripSize = ArraySize = 4096 = 32x32 grid

当您看到结果时,LibTIFF会跳过一些行并提供无关的排序,如果图像大小不是2的幂,它甚至会变得更糟!

3 个答案:

答案 0 :(得分:0)

您的示例文件似乎是平铺tiff而不是剥离。控制台说:

  

ElevationMap.tif:无法从平铺图像中读取扫描线

我更改了您的代码以读取切片。这种方式似乎是在阅读数据。

for (int i = 0; i < inputImage.NumberOfTiles(); i++)
{
    offset += inputImage.ReadEncodedTile(i, inputImageData, offset, (int)inputImage.RawTileSize(i));
}

答案 1 :(得分:0)

我知道可能会迟到,但最近我遇到了同样的错误,我找到了解决方案,所以它可能会有所帮助。错误在函数count的参数Tiff.ReadEncodedTile(tile, buffer, offset, count)中。它必须是解压缩的字节大小,而不是压缩的字节大小。这就是您没有所有信息的原因,因为您没有将整个数据保存在缓冲区中。请参阅how-to-translate-tiff-readencodedtile-to-elevation-terrain-matrix-from-height

答案 2 :(得分:0)

一种读取浮点tiff的快速方法。

public static unsafe float[,] ReadTiff(Tiff image)
{
    const int pixelStride = 4; // bytes per pixel

    int imageWidth = image.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
    int imageHeight = image.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
    float[,] result = new float[imageWidth, imageHeight];

    int tileCount = image.NumberOfTiles();
    int tileWidth = image.GetField(TiffTag.TILEWIDTH)[0].ToInt();
    int tileHeight = image.GetField(TiffTag.TILELENGTH)[0].ToInt();
    int tileStride = (imageWidth + tileWidth - 1) / tileWidth;

    int bufferSize = tileWidth * tileHeight * pixelStride;
    byte[] buffer = new byte[bufferSize];
    fixed (byte* bufferPtr = buffer)
    {
        float* array = (float*)bufferPtr;

        for (int t = 0; t < tileCount; t++)
        {
            image.ReadEncodedTile(t, buffer, 0, buffer.Length);

            int x = tileWidth * (t % tileStride);
            int y = tileHeight * (t / tileStride);
            var copyWidth = Math.Min(tileWidth, imageWidth - x);
            var copyHeight = Math.Min(tileHeight, imageHeight - y);

            for (int j = 0; j < copyHeight; j++)
            {
                for (int i = 0; i < copyWidth; i++)
                {
                    result[x + i, y + j] = array[j * tileWidth + i];
                }
            }
        }
    }

    return result;
}