我是阅读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
- 每个像素的采样数:1
- 计划配置:Contig
- 平铺宽度:208
- TileLength:208
- 示例格式:3
提前感谢你们的帮助!
好的,最后我找到了解决方案:我的错误是函数Tiff.ReadEncodedTile中的参数"count"(tile,buffer,offset,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
}
}
}
问候!
这里有一个改进的代码,适用于非方形瓷砖:
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);
}
}
}
}