首页 文章

如何将Ciff.ReadEncodedTile从C#中的高度图转换为高程地形矩阵?

提问于
浏览
1

我是新手阅读tiff图像,我试图通过使用LibTiff从tiff Map 获取高程地形值 . 我需要解码的 Map 是瓷砖组织的 . 根据图书馆文档和网络研究,我正在使用的代码片段下面获取这些值:

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对我不起作用,因为我需要解码的 Map 是有组织的图块,而且这种方法不适合处理这些图像(根据BitMiracle.LibTiff文档) . 方法Tiff.ReadRGBATile也无效,因为tiff图像不是RGB . 我可以用Matlab读取这些值,但我的项目需要用C#构建,所以我可以将预期的结果与我的比较 . 作为参考(我认为它可能会有所帮助),这些是使用LibTiff标签读取方法从其中一个tiff文件中提取的一些数据:

  • ImageWidth:2001

  • ImageLength:2001

  • BitsPerSample:32

  • 压缩:PackBits(又名Macintosh RLE)

  • 光度学:MinIsBlack

  • SamplesPerPixel:1

  • PlanarConfig:Contig

  • TileWidth:208

  • TileLength:208

  • SampleFormat:3

在此之前,感谢您的帮助!

2 回答

  • 0

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

    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);
    
                                }
                            }
                        }
                    }
    
  • 0

    好的,最后我找到了解决方案:我的错误是函数Tiff.ReadEncodedTile(tile,buffer,offset,count)中的参数“count” . Tiff.RawTileSize(int)函数返回tile的压缩字节大小(每个tile的格式不同,具体取决于压缩算法),但Tiff.ReadEncodedTile返回解压缩的字节(所有tile都更大且常量) . 这就是为什么并非所有信息都已正确保存,而只是一部分数据 . 在具有地形高程矩阵的正确代码下方(需要优化但它有效,我认为它可能有帮助)

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

    问候!

相关问题