【问题标题】:How to translate Tiff.ReadEncodedTile to elevation terrain matrix from height map in C#?如何将 Tiff.ReadEncodedTile 从 C# 中的高度图转换为高程地形矩阵?
【发布时间】:2016-05-25 13:05:11
【问题描述】:

我是阅读 tiff 图像的新手,我正在尝试使用 LibTiff 从 tiff 地图中获取海拔地形值。我需要解码的地图是平铺的。根据库文档和网络研究,下面是我目前用来获取这些值的代码片段:

    private void getBytes()
    {
        int numBytes = bitsPerSample / 8;           //Number of bytes depending the tiff map
        int stride = numBytes * height;
        byte[] bufferTiff = new byte[stride * height];  // this is the buffer with the tiles data

        int offset = 0;

        for (int i = 0; i < tif.NumberOfTiles() - 1; i++)
        {
            int rawTileSize = (int)tif.RawTileSize(i);
            offset += tif.ReadEncodedTile(i, bufferTiff, offset, rawTileSize); 

        }

        values = new double[height, width];         // this is the matrix to save the heigth values in meters

        int ptr = 0;                    // pointer for saving the each data bytes
        int m = 0;
        int n = 0;

        byte[] byteValues = new byte[numBytes];     // bytes of each height data

        for (int i = 0; i < bufferTiff.Length; i++)
        {
            byteValues[ptr] = bufferTiff[i];

            ptr++;
            if (ptr % numBytes == 0)
            {
                ptr = 0;

                    if (n == height) // tiff map Y pixels
                    {
                        n = 0;
                        m++;
                        if (m == width) // tiff map X pixels
                        {
                            m = 0;
                        }
                    }

                    values[m, n] = BitConverter.ToDouble(byteValues, 0);    // Converts each byte data to the height value in meters. If the map is 32 bps the method I use is BitConverter.ToFloat

                    if (n == height - 1 && m == width - 1)
                        break;
                    n++;

            }
        }
        SaveArrayAsCSV(values, "values.txt");               
    }

    //Only to show results in a cvs file:
    public void SaveArrayAsCSV(double[,] arrayToSave, string fileName)  // source: http://stackoverflow.com/questions/8666518/how-can-i-write-a-general-array-to-csv-file
    {
        using (StreamWriter file = new StreamWriter(fileName))
        {
            WriteItemsToFile(arrayToSave, file);
        }
    }

   //Only to show results in a cvs file:
    private void WriteItemsToFile(Array items, TextWriter file)     // source: http://stackoverflow.com/questions/8666518/how-can-i-write-a-general-array-to-csv-file
    {
        int cont = 0;
        foreach (object item in items)
        {
            if (item is Array)
            {
                WriteItemsToFile(item as Array, file);
                file.Write(Environment.NewLine);
            }
            else {
                file.Write(item + " | ");
                cont++;
                if(cont == width)                       
                {
                    file.Write("\n");
                    cont = 0;
                }
            }
        }
    }

我一直在测试两个不同的映射(每个样本 32 位和 64 位),结果相似:一开始,数据似乎是一致的,但有一点是所有其他值都损坏了(在数据结果的末尾甚至为零)。我推断有一些字节需要忽略,但我不知道如何识别它们以净化我的代码。 Tiff.ReadScanline 方法对我不起作用,因为我需要解码的地图是有组织的图块,并且此方法不适用于处理此类图像(根据 BitMiracle.LibTiff 文档)。 Tiff.ReadRGBATile 方法也无效,因为 tiff 图像不是 RGB。我可以用 Matlab 读取这些值,但是我的项目需要用 C# 构建,所以我可以将预期结果与我的进行比较。作为参考(我认为它可能会有所帮助),这些是从具有 LibTiff 标签读取方法的 tiff 文件之一中提取的一些数据:

  • 图像宽度:2001
  • 图像长度:2001
  • BitsPerSample:32
  • 压缩:PackBits(又名 Macintosh RLE)
  • 光度学:MinIsBlack
  • SamplesPerPixel:1
  • PlanarConfig:Contig
  • 平铺宽度:208
  • 平铺长度:208
  • 样本格式:3

提前感谢你们的帮助!

【问题讨论】:

  • 我对这种格式一无所知,但简单的数学表明 2001 不能被 208 整除。2001 x 2001 图像中 208 x 208 的图块是如何组织的?
  • Here (Open Tiff Toolkit on sourceforge)你可以找到一个c++源码解码tiff瓦片。也许你通过检查找到答案。
  • 感谢@OlivierJacot-Descombes 的回答。对,就是这样。使用 RGB tiff 文件工作和绘图时,我注意到发生了同样的事情:最后一列和最后一行的图块有一个“空白区域”,其中没有图像。我不是该主题的专家,但图像位于此图块内,其像素数量比图像本身多。这可能是问题所在,但我不知道如何系统地忽略没有图像的字节,或者 LibTiff 是否有办法做到这一点

标签: c# tile libtiff geotiff heightmap


【解决方案1】:

好的,终于找到了解决办法:我的错误是函数Tiff.ReadEncodedTile(tile, buffer, offset, count)中的参数“count”。 Tiff.RawTileSize(int) 函数返回瓦片的压缩字节大小(每个瓦片不同,取决于压缩算法),但 Tiff.ReadEncodedTile 返回解压缩字节(所有瓦片更大且恒定)。这就是为什么没有正确保存所有信息,而只是数据的一部分。下面是带有地形高程矩阵的正确代码(需要优化,但它可以工作,我认为它可能会有所帮助)

 private void getBytes()
    {
        int numBytes = bitsPerSample / 8;
        int numTiles = tif.NumberOfTiles();
        int stride = numBytes * height;
        int bufferSize = tileWidth * tileHeight * numBytes * numTiles;
        int bytesSavedPerTile = tileWidth * tileHeight * numBytes; //this is the real size of the decompressed bytes
        byte[] bufferTiff = new byte[bufferSize];

        FieldValue[] value = tif.GetField(TiffTag.TILEWIDTH);
        int tilewidth = value[0].ToInt();

        value = tif.GetField(TiffTag.TILELENGTH);
        int tileHeigth = value[0].ToInt();

        int matrixSide = (int)Math.Sqrt(numTiles); // this works for a square image (for example a tiles organized tiff image)
        int bytesWidth = matrixSide * tilewidth;
        int bytesHeigth = matrixSide * tileHeigth;

        int offset = 0;

        for (int j = 0; j < numTiles; j++)
        {
            offset += tif.ReadEncodedTile(j, bufferTiff, offset, bytesSavedPerTile); //Here was the mistake. Now it works!
        }

        double[,] aux = new double[bytesHeigth, bytesWidth]; //Double for a 64 bps tiff image. This matrix will save the alldata, including the transparency (the "blank zone" I was talking before)

        terrainElevation = new double[height, width]; // Double for a 64 bps tiff image. This matrix will save only the elevation values, without transparency

        int ptr = 0;
        int m = 0;
        int n = -1;
        int contNumTile = 1;
        int contBytesPerTile = 0;
        int i = 0;
        int tileHeigthReference = tileHeigth;
        int tileWidthReference = tileWidth;
        int row = 1;
        int col = 1;

        byte[] bytesHeigthMeters = new byte[numBytes]; // Buffer to save each one elevation value to parse

        while (i < bufferTiff.Length && contNumTile < numTiles + 1)
        {
            for (contBytesPerTile = 0; contBytesPerTile < bytesSavedPerTile; contBytesPerTile++)
            {
                bytesHeigthMeters[ptr] = bufferTiff[i];
                ptr++;
                if (ptr % numBytes == 0 && ptr != 0)
                {
                    ptr = 0;
                    n++;

                    if (n == tileHeigthReference)
                    {
                        n = tileHeigthReference - tileHeigth;
                        m++;
                        if (m == tileWidthReference)
                        {
                            m = tileWidthReference - tileWidth;
                        }
                    }
                    double heigthMeters = BitConverter.ToDouble(bytesHeigthMeters, 0);

                    if (n < bytesWidth)
                    {
                        aux[m, n] = heigthMeters;
                    }
                    else
                    {
                        n = -1;
                    }

                }
                i++;
            }

            if (i % tilewidth == 0)
            {
                col++;
                if (col == matrixSide + 1)
                {
                    col = 1;
                }
            }

            if (contNumTile % matrixSide == 0)
            {
                row++;
                n = -1;
                if (row == matrixSide + 1)
                {
                    row = 1;

                }
            }

            contNumTile++;
            tileHeigthReference = tileHeight * (col);
            tileWidthReference = tileWidth * (row);

            m = tileWidth * (row - 1);

        }

        for (int x = 0; x < height; x++)
        {
            for (int y = 0; y < width; y++)
            {
                terrainElevation[x, y] = aux[x, y]; // Final result. Each position of matrix has saved each pixel terrain elevation of the map
            }
        }

    }

问候!

【讨论】:

    【解决方案2】:

    这是一个改进的代码,适用于非方形瓷砖:

    int imageWidth = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
                    int imageHeight = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
                    int bytesPerSample = (int)tiff.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt() / 8;
                    SampleFormat format = (SampleFormat)tiff.GetField(TiffTag.SAMPLEFORMAT)[0].ToInt();
    
                    //Array to return
                    float[,] decoded = new float[imageHeight, imageWidth];
    
                    //Get decode function (I only want a float array)
                    Func<byte[], int, float> decode = GetConversionFunction(format, bytesPerSample);
                    if (decode == null)
                    {
                        throw new ArgumentException("Unsupported TIFF format:"+format);
                    }
    
                    if(tiff.IsTiled())
                    {
                        //tile dimensions in pixels - the image dimensions MAY NOT be a multiple of these dimensions
                        int tileWidth = tiff.GetField(TiffTag.TILEWIDTH)[0].ToInt();
                        int tileHeight = tiff.GetField(TiffTag.TILELENGTH)[0].ToInt();
    
                        //tile matrix size
                        int numTiles = tiff.NumberOfTiles();
                        int tileMatrixWidth = (int)Math.Ceiling(imageWidth / (float)tileWidth);
                        int tileMatrixHeight = (int)Math.Ceiling(imageHeight / (float)tileHeight);
    
                        //tile dimensions in bytes
                        int tileBytesWidth = tileWidth * bytesPerSample;
                        int tileBytesHeight = tileHeight * bytesPerSample;
    
                        //tile buffer
                        int tileBufferSize = tiff.TileSize();
                        byte[] tileBuffer = new byte[tileBufferSize];
    
                        int imageHeightMinus1 = imageHeight - 1;
    
                        for (int tileIndex = 0 ; tileIndex < numTiles; tileIndex++)
                        {
                            int tileX = tileIndex / tileMatrixWidth;
                            int tileY = tileIndex % tileMatrixHeight;
    
                            tiff.ReadTile(tileBuffer, 0, tileX*tileWidth, tileY*tileHeight, 0, 0);
    
                            int xImageOffset = tileX * tileWidth;
                            int yImageOffset = tileY * tileHeight;
    
                            for (int col = 0; col < tileWidth && xImageOffset+col < imageWidth; col++ )
                            {
                                for(int row = 0; row < tileHeight && yImageOffset+row < imageHeight; row++)
                                {
                                    decoded[imageHeightMinus1-(yImageOffset+row), xImageOffset+col] = decode(tileBuffer, row * tileBytesWidth + col * bytesPerSample);
    
                                }
                            }
                        }
                    }
    

    【讨论】:

    • 欢迎来到 StackOverflow!不要只是将您的代码作为答案转储,请花一些时间来解释它以及它是如何改进的。还需要花一分钟时间阅读How do I write a good answer?上的帮助页面
    猜你喜欢
    • 2021-11-20
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 1970-01-01
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多