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

我是新手阅读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文件中提取的一些数据:

  • ImageWidth:2001
  • ImageLength:2001
  • BitsPerSample:32
  • 压缩:PackBits(又名Macintosh RLE)
  • 光度学:MinIsBlack
  • SamplesPerPixel:1
  • PlanarConfig:重叠群
  • TileWidth:208
  • TileLength:208
  • SampleFormat:3

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

好的,最后我找到了解决方案:我的错误是函数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 } } } 

问候!

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

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