2016-05-25 11 views
1

Я новичок в работе с чтением tiff-изображений, и я пытаюсь получить значения рельефа местности с tiff-карты с помощью LibTiff. Карты, которые мне нужно декодировать, являются структурированными. Ниже фрагмент кода я использую в настоящее время, чтобы получить эти значения, на основе библиотечной документации и исследований в Интернете:Как перевести Tiff.ReadEncodedTile в матрицу рельефа местности из карты высот в C#?

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#, поэтому я могу сравнить ожидаемые результаты с моими. Как ссылки (я думаю, что это может быть полезно), вот некоторые данные, извлеченные из одного из TIFF-файлов с LibTIFF методами чтения тегов:

  • ImageWidth 2001
  • ImageLength: 2001
  • BitsPerSample: 32
  • сжатия: PackBits (ака Macintosh RLE)
  • Фотометрический: MinIsBlack
  • SamplesPerPixel: 1
  • PlanarConfig: Contig
  • TileWidth: 208
  • TileLength: 208
  • SampleFormat: 3

Спасибо заранее по вашей помощи, ребята!

+0

Я ничего об этом формате, не знаю, но простая математика показывает, что 2001 не делится на 208. Как 208 х 208 фрагментов, созданных в 2001 году в 2001 году? –

+0

[Здесь (Open Tiff Toolkit on sourceforge)] (https://sourceforge.net/projects/opentiff/) вы можете найти tiff-фрагменты кода исходного кода C++. Возможно, вы найдете ответ, проверив его. –

+0

Спасибо, что ответил @ OlivierJacot-Descombes. Да, точно. Работая и работая с файлами TIF ​​RGB, я заметил, что происходит то же самое: плитки последнего столбца и последней строки имеют «пустую зону», в которой нет изображения.Я не эксперт в этой теме, но изображение внутри этих плит, которые имеют большое количество пикселей, чем само изображение. Это может быть проблема, но я не знаю, как систематически игнорировать байты, у которых нет изображения, или если у LibTiff есть способ сделать это – tofraguzbel

ответ

0

Итак, я нашел решение: Моей ошибкой был параметр «count» в функции Tiff.ReadEncodedTile (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 
      } 
     } 

    } 

Привет!

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

Добро пожаловать в StackOverflow! Вместо того, чтобы просто демпинг кода в качестве ответа, пожалуйста, уделите некоторое время, чтобы объяснить это и как это улучшение. Также найдите минутку, чтобы прочитать страницу справки на странице [Как написать хороший ответ?] (Http://stackoverflow.com/help/how-to-answer) – avojak