mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1517 lines
46 KiB
C++
1517 lines
46 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkTIFFReader.cxx,v
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkTIFFReader.h"
|
|
|
|
#include "vtkDataArray.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkErrorCode.h"
|
|
#include "vtkObjectFactory.h"
|
|
|
|
#include "vtksys/SystemTools.hxx"
|
|
|
|
#include <sys/stat.h>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
extern "C" {
|
|
#include "vtk_tiff.h"
|
|
}
|
|
|
|
namespace {
|
|
struct FlipTrue {};
|
|
struct FlipFalse {};
|
|
|
|
int GetFileRow(int row, int height, FlipTrue)
|
|
{
|
|
return height - row - 1;
|
|
}
|
|
|
|
int GetFileRow(int row, int, FlipFalse)
|
|
{
|
|
return row;
|
|
}
|
|
|
|
// This is inverse of GetFileRow(), which is same as calling GetFileRow()
|
|
// again.
|
|
template <class Flip>
|
|
int GetImageRow(int file_row, int height, Flip flip)
|
|
{
|
|
return GetFileRow(file_row, height, flip);
|
|
}
|
|
|
|
bool SupportsRandomAccess(TIFF* image)
|
|
{
|
|
unsigned int rowsPerStrip;
|
|
unsigned short compression;
|
|
TIFFGetFieldDefaulted(image, TIFFTAG_COMPRESSION, &compression);
|
|
TIFFGetFieldDefaulted(image, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip);
|
|
return (compression == COMPRESSION_NONE || rowsPerStrip == 1);
|
|
}
|
|
|
|
bool PurgeInitialScanLinesIfNeeded(int fileStartRow, TIFF* image)
|
|
{
|
|
if (fileStartRow == 0 || SupportsRandomAccess(image))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// File doesn't support random access and we want to start at a non-0 row. We
|
|
// read (and discard) initial scanlines.
|
|
unsigned int isize = TIFFScanlineSize(image);
|
|
tdata_t buf = _TIFFmalloc(isize);
|
|
for (int i = 0; i < fileStartRow; ++i)
|
|
{
|
|
if (TIFFReadScanline(image, buf, i, 0) <= 0)
|
|
{
|
|
_TIFFfree(buf);
|
|
return false;
|
|
}
|
|
}
|
|
_TIFFfree(buf);
|
|
return true;
|
|
}
|
|
|
|
// Simple scan line copy of a slice in a volume with tightly packed memory.
|
|
template<typename T, typename Flip>
|
|
bool ReadTemplatedImage(T* out, Flip flip,
|
|
int startCol, int endCol,
|
|
int startRow, int endRow,
|
|
int yIncrements,
|
|
unsigned int height,
|
|
TIFF *image)
|
|
{
|
|
int fileStartRow = GetFileRow(startRow, height, flip);
|
|
int fileEndRow = GetFileRow(endRow, height, flip);
|
|
int minFileRow = std::min(fileStartRow, fileEndRow);
|
|
int maxFileRow = std::max(fileStartRow, fileEndRow);
|
|
|
|
if (!PurgeInitialScanLinesIfNeeded(minFileRow, image))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
unsigned int isize = TIFFScanlineSize(image);
|
|
size_t scanLineSize = endCol - startCol + 1;
|
|
if (scanLineSize * sizeof(T) == isize)
|
|
{
|
|
// We can copy straight into the image data output.
|
|
for (int fi = minFileRow; fi <= maxFileRow; ++fi)
|
|
{
|
|
int i = GetImageRow(fi, height, flip);
|
|
T* tmp = out + (i - startRow) * yIncrements;
|
|
if (TIFFReadScanline(image, tmp, fi, 0) <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Copy into a buffer of the appropriate size, then subset into the output.
|
|
tdata_t buf = _TIFFmalloc(isize);
|
|
for (int fi = minFileRow; fi <= maxFileRow; ++fi)
|
|
{
|
|
int i = GetImageRow(fi, height, flip);
|
|
T* tmp = out + (i - startRow) * yIncrements;
|
|
if (TIFFReadScanline(image, buf, fi, 0) <= 0)
|
|
{
|
|
_TIFFfree(buf);
|
|
return false;
|
|
}
|
|
memcpy(tmp, static_cast<T*>(buf) + startCol, sizeof(T) * scanLineSize);
|
|
}
|
|
_TIFFfree(buf);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
vtkStandardNewMacro(vtkTIFFReader)
|
|
|
|
class vtkTIFFReader::vtkTIFFReaderInternal
|
|
{
|
|
public:
|
|
vtkTIFFReaderInternal();
|
|
bool Initialize();
|
|
void Clean();
|
|
bool CanRead();
|
|
bool Open(const char *filename);
|
|
TIFF *Image;
|
|
bool IsOpen;
|
|
unsigned int Width;
|
|
unsigned int Height;
|
|
unsigned short NumberOfPages;
|
|
unsigned short CurrentPage;
|
|
unsigned short SamplesPerPixel;
|
|
unsigned short Compression;
|
|
unsigned short BitsPerSample;
|
|
unsigned short Photometrics;
|
|
bool HasValidPhotometricInterpretation;
|
|
unsigned short PlanarConfig;
|
|
unsigned short Orientation;
|
|
unsigned long int TileDepth;
|
|
unsigned int TileRows;
|
|
unsigned int TileColumns;
|
|
unsigned int TileWidth;
|
|
unsigned int TileHeight;
|
|
unsigned short NumberOfTiles;
|
|
unsigned int SubFiles;
|
|
unsigned int ResolutionUnit;
|
|
float XResolution;
|
|
float YResolution;
|
|
short SampleFormat;
|
|
static void ErrorHandler(const char* module, const char* fmt, va_list ap);
|
|
};
|
|
|
|
extern "C" {
|
|
static void vtkTIFFReaderInternalErrorHandler(const char* vtkNotUsed(module),
|
|
const char* vtkNotUsed(fmt),
|
|
va_list vtkNotUsed(ap))
|
|
{
|
|
// Do nothing
|
|
// Ignore errors
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool vtkTIFFReader::vtkTIFFReaderInternal::Open(const char *filename)
|
|
{
|
|
this->Clean();
|
|
struct stat fs;
|
|
if (stat(filename, &fs))
|
|
{
|
|
return false;
|
|
}
|
|
this->Image = TIFFOpen(filename, "r");
|
|
if (!this->Image)
|
|
{
|
|
this->Clean();
|
|
return false;
|
|
}
|
|
if (!this->Initialize())
|
|
{
|
|
this->Clean();
|
|
return false;
|
|
}
|
|
|
|
this->IsOpen = true;
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void vtkTIFFReader::vtkTIFFReaderInternal::Clean()
|
|
{
|
|
if (this->Image)
|
|
{
|
|
TIFFClose(this->Image);
|
|
this->Image = NULL;
|
|
}
|
|
this->Width = 0;
|
|
this->Height = 0;
|
|
this->SamplesPerPixel = 0;
|
|
this->Compression = 0;
|
|
this->BitsPerSample = 0;
|
|
this->Photometrics = 0;
|
|
this->HasValidPhotometricInterpretation = false;
|
|
this->PlanarConfig = 0;
|
|
this->TileDepth = 0;
|
|
this->CurrentPage = 0;
|
|
this->NumberOfPages = 0;
|
|
this->NumberOfTiles = 0;
|
|
this->TileRows = 0;
|
|
this->TileColumns = 0;
|
|
this->TileWidth = 0;
|
|
this->TileHeight = 0;
|
|
this->XResolution = 1;
|
|
this->YResolution = 1;
|
|
this->SubFiles = 0;
|
|
this->SampleFormat = 1;
|
|
this->ResolutionUnit = 1; // none
|
|
this->IsOpen = false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
vtkTIFFReader::vtkTIFFReaderInternal::vtkTIFFReaderInternal()
|
|
{
|
|
this->Image = NULL;
|
|
// Note that this suppresses all error/warning output from libtiff!
|
|
TIFFSetErrorHandler(&vtkTIFFReaderInternalErrorHandler);
|
|
TIFFSetWarningHandler(&vtkTIFFReaderInternalErrorHandler);
|
|
this->Clean();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool vtkTIFFReader::vtkTIFFReaderInternal::Initialize()
|
|
{
|
|
if (this->Image)
|
|
{
|
|
if (!TIFFGetField(this->Image, TIFFTAG_IMAGEWIDTH, &this->Width) ||
|
|
!TIFFGetField(this->Image, TIFFTAG_IMAGELENGTH, &this->Height))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the resolution in each direction
|
|
TIFFGetField(this->Image, TIFFTAG_XRESOLUTION, &this->XResolution);
|
|
TIFFGetField(this->Image, TIFFTAG_YRESOLUTION, &this->YResolution);
|
|
TIFFGetField(this->Image, TIFFTAG_RESOLUTIONUNIT, &this->ResolutionUnit);
|
|
|
|
// Check the number of pages. First by looking at the number of directories.
|
|
this->NumberOfPages = TIFFNumberOfDirectories(this->Image);
|
|
if (this->NumberOfPages == 0)
|
|
{
|
|
if (!TIFFGetField(this->Image, TIFFTAG_PAGENUMBER, &this->CurrentPage,
|
|
&this->NumberOfPages))
|
|
{
|
|
// Check the Image Description tag to know the number of images
|
|
// This is used by ImageJ
|
|
char** description = new char*[255];
|
|
if (TIFFGetField(this->Image, TIFFTAG_IMAGEDESCRIPTION, description))
|
|
{
|
|
// look for the number of images
|
|
std::string desc = description[0];
|
|
int pos = desc.find("images=");
|
|
int pos2 = desc.find("\n");
|
|
if ((pos != -1) && (pos2 != -1))
|
|
{
|
|
this->NumberOfPages = atoi(desc.substr(pos+7,pos2-pos-7).c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the number of pages is still zero we look if the image is tiled.
|
|
if (this->NumberOfPages <= 1 && TIFFIsTiled(this->Image))
|
|
{
|
|
this->NumberOfTiles = TIFFNumberOfTiles(this->Image);
|
|
|
|
if (!TIFFGetField(this->Image, TIFFTAG_TILEWIDTH, &this->TileWidth) ||
|
|
!TIFFGetField(this->Image, TIFFTAG_TILELENGTH, &this->TileHeight))
|
|
{
|
|
cerr << "Cannot read tile width and height from file" << endl;
|
|
}
|
|
else
|
|
{
|
|
TileRows = this->Height / this->TileHeight;
|
|
TileColumns = this->Width / this->TileWidth;
|
|
}
|
|
}
|
|
|
|
// Checking if the TIFF contains subfiles
|
|
if (this->NumberOfPages > 1)
|
|
{
|
|
this->SubFiles = 0;
|
|
|
|
for (unsigned int page = 0; page<this->NumberOfPages; ++page)
|
|
{
|
|
long subfiletype = 6;
|
|
if (TIFFGetField(this->Image, TIFFTAG_SUBFILETYPE, &subfiletype))
|
|
{
|
|
if (subfiletype == 0)
|
|
{
|
|
this->SubFiles += 1;
|
|
}
|
|
}
|
|
TIFFReadDirectory(this->Image);
|
|
}
|
|
|
|
// Set the directory to the first image
|
|
TIFFSetDirectory(this->Image, 0);
|
|
}
|
|
|
|
// TIFFTAG_ORIENTATION tag from the image data and use it if available.
|
|
// If the tag is not found in the image data, use ORIENTATION_BOTLEFT by
|
|
// default.
|
|
int status = TIFFGetField(this->Image, TIFFTAG_ORIENTATION,
|
|
&this->Orientation);
|
|
if (!status)
|
|
{
|
|
this->Orientation = ORIENTATION_BOTLEFT;
|
|
}
|
|
|
|
TIFFGetFieldDefaulted(this->Image, TIFFTAG_SAMPLESPERPIXEL,
|
|
&this->SamplesPerPixel);
|
|
TIFFGetFieldDefaulted(this->Image, TIFFTAG_COMPRESSION, &this->Compression);
|
|
TIFFGetFieldDefaulted(this->Image, TIFFTAG_BITSPERSAMPLE,
|
|
&this->BitsPerSample);
|
|
TIFFGetFieldDefaulted(this->Image, TIFFTAG_PLANARCONFIG, &this->PlanarConfig);
|
|
TIFFGetFieldDefaulted(this->Image, TIFFTAG_SAMPLEFORMAT, &this->SampleFormat);
|
|
|
|
// If SamplesPerPixel is one, then PlanarConfig has no meaning and some
|
|
// files have it set arbitrarily. Therefore, set it to CONTIG so that
|
|
// the reader will not refuse to read the file on a technicality.
|
|
if (this->SamplesPerPixel == 1)
|
|
{
|
|
this->PlanarConfig = PLANARCONFIG_CONTIG;
|
|
}
|
|
|
|
// If TIFFGetField returns false, there's no Photometric Interpretation
|
|
// set for this image, but that's a required field so we set a warning flag.
|
|
// (Because the "Photometrics" field is an enum, we can't rely on setting
|
|
// this->Photometrics to some signal value.)
|
|
if (TIFFGetField(this->Image, TIFFTAG_PHOTOMETRIC, &this->Photometrics))
|
|
{
|
|
this->HasValidPhotometricInterpretation = true;
|
|
}
|
|
else
|
|
{
|
|
this->HasValidPhotometricInterpretation = false;
|
|
}
|
|
if (!TIFFGetField(this->Image, TIFFTAG_TILEDEPTH, &this->TileDepth))
|
|
{
|
|
this->TileDepth = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool vtkTIFFReader::vtkTIFFReaderInternal::CanRead()
|
|
{
|
|
return ( this->Image && ( this->Width > 0 ) && ( this->Height > 0 ) &&
|
|
( this->SamplesPerPixel > 0 ) &&
|
|
( this->Compression == COMPRESSION_NONE ||
|
|
this->Compression == COMPRESSION_PACKBITS ||
|
|
this->Compression == COMPRESSION_LZW ||
|
|
this->Compression == COMPRESSION_ADOBE_DEFLATE
|
|
) &&
|
|
( this->HasValidPhotometricInterpretation ) &&
|
|
( this->Photometrics == PHOTOMETRIC_RGB ||
|
|
this->Photometrics == PHOTOMETRIC_MINISWHITE ||
|
|
this->Photometrics == PHOTOMETRIC_MINISBLACK ||
|
|
this->Photometrics == PHOTOMETRIC_PALETTE ) &&
|
|
( this->PlanarConfig == PLANARCONFIG_CONTIG ) &&
|
|
( !this->TileDepth ) &&
|
|
( this->BitsPerSample == 8 || this->BitsPerSample == 16 ||
|
|
this->BitsPerSample == 32) );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
vtkTIFFReader::vtkTIFFReader()
|
|
{
|
|
|
|
this->Initialize();
|
|
this->InternalImage = new vtkTIFFReader::vtkTIFFReaderInternal;
|
|
this->OutputExtent[0] = 0;
|
|
this->OutputExtent[1] = 0;
|
|
this->OutputExtent[2] = 0;
|
|
this->OutputExtent[3] = 0;
|
|
this->OutputExtent[4] = 0;
|
|
this->OutputExtent[5] = 0;
|
|
this->OutputIncrements[0] = 0;
|
|
this->OutputIncrements[1] = 0;
|
|
this->OutputIncrements[2] = 0;
|
|
|
|
this->OrientationTypeSpecifiedFlag = false;
|
|
this->OriginSpecifiedFlag = false;
|
|
this->SpacingSpecifiedFlag = false;
|
|
|
|
//Make the default orientation type to be ORIENTATION_BOTLEFT
|
|
this->OrientationType = 4;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
vtkTIFFReader::~vtkTIFFReader()
|
|
{
|
|
delete this->InternalImage;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void vtkTIFFReader::ExecuteInformation()
|
|
{
|
|
this->Initialize();
|
|
this->ComputeInternalFileName(this->DataExtent[4]);
|
|
if (this->InternalFileName == NULL)
|
|
{
|
|
vtkErrorMacro("Need to specify a filename");
|
|
this->SetErrorCode(vtkErrorCode::NoFileNameError);
|
|
return;
|
|
}
|
|
|
|
if (!this->InternalImage->Open(this->InternalFileName))
|
|
{
|
|
vtkErrorMacro("Unable to open file "
|
|
<< this->InternalFileName
|
|
<< " Reason: "
|
|
<< vtksys::SystemTools::GetLastSystemError());
|
|
this->SetErrorCode(vtkErrorCode::CannotOpenFileError);
|
|
this->DataExtent[0] = 0;
|
|
this->DataExtent[1] = 0;
|
|
this->DataExtent[2] = 0;
|
|
this->DataExtent[3] = 0;
|
|
this->DataExtent[4] = 0;
|
|
this->DataExtent[5] = 0;
|
|
this->SetNumberOfScalarComponents(1);
|
|
this->vtkImageReader2::ExecuteInformation();
|
|
return;
|
|
}
|
|
|
|
// If orientation information is provided, overwrite the value
|
|
// read from the tiff image
|
|
if (this->OrientationTypeSpecifiedFlag)
|
|
{
|
|
this->InternalImage->Orientation = OrientationType;
|
|
}
|
|
|
|
if (!SpacingSpecifiedFlag)
|
|
{
|
|
this->DataSpacing[0] = 1.0;
|
|
this->DataSpacing[1] = 1.0;
|
|
|
|
// If we have some spacing information we use it
|
|
if (this->InternalImage->ResolutionUnit > 0 &&
|
|
this->InternalImage->XResolution > 0 &&
|
|
this->InternalImage->YResolution > 0)
|
|
{
|
|
if (this->InternalImage->ResolutionUnit == 2) // inches
|
|
{
|
|
this->DataSpacing[0] = 25.4 / this->InternalImage->XResolution;
|
|
this->DataSpacing[1] = 25.4 / this->InternalImage->YResolution;
|
|
}
|
|
else if (this->InternalImage->ResolutionUnit == 3) // cm
|
|
{
|
|
this->DataSpacing[0] = 10.0 / this->InternalImage->XResolution;
|
|
this->DataSpacing[1] = 10.0 / this->InternalImage->YResolution;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!OriginSpecifiedFlag)
|
|
{
|
|
this->DataOrigin[0] = 0.0;
|
|
this->DataOrigin[1] = 0.0;
|
|
}
|
|
|
|
// Pull out the width/height, etc.
|
|
this->DataExtent[0] = 0;
|
|
this->DataExtent[1] = this->InternalImage->Width - 1;
|
|
this->DataExtent[2] = 0;
|
|
this->DataExtent[3] = this->InternalImage->Height - 1;
|
|
|
|
switch (this->GetFormat())
|
|
{
|
|
case vtkTIFFReader::GRAYSCALE:
|
|
case vtkTIFFReader::PALETTE_GRAYSCALE:
|
|
this->SetNumberOfScalarComponents(1);
|
|
break;
|
|
case vtkTIFFReader::RGB:
|
|
this->SetNumberOfScalarComponents(this->InternalImage->SamplesPerPixel);
|
|
break;
|
|
case vtkTIFFReader::PALETTE_RGB:
|
|
this->SetNumberOfScalarComponents(3);
|
|
break;
|
|
default:
|
|
this->SetNumberOfScalarComponents(4);
|
|
}
|
|
|
|
if (!this->InternalImage->CanRead())
|
|
{
|
|
this->SetNumberOfScalarComponents(4);
|
|
}
|
|
|
|
// Figure out the appropriate scalar type for the data.
|
|
int scalarType = VTK_CHAR;
|
|
short sampleFormat = this->InternalImage->SampleFormat;
|
|
if (this->InternalImage->BitsPerSample <= 8)
|
|
{
|
|
scalarType = sampleFormat == 2 ? VTK_CHAR : VTK_UNSIGNED_CHAR;
|
|
}
|
|
else if (this->InternalImage->BitsPerSample <= 16)
|
|
{
|
|
scalarType = sampleFormat == 2 ? VTK_SHORT : VTK_UNSIGNED_SHORT;
|
|
}
|
|
else if (this->InternalImage->BitsPerSample <= 32 && sampleFormat <= 2)
|
|
{
|
|
scalarType = sampleFormat == 2 ? VTK_INT : VTK_UNSIGNED_INT;
|
|
}
|
|
else if (this->InternalImage->BitsPerSample <= 32 && sampleFormat == 3)
|
|
{
|
|
scalarType = VTK_FLOAT;
|
|
}
|
|
else
|
|
{
|
|
vtkErrorMacro("Unhandled Bit Per Sample: " << this->InternalImage->BitsPerSample);
|
|
return;
|
|
}
|
|
this->SetDataScalarType(scalarType);
|
|
|
|
// We check if we have a Zeiss image.
|
|
// Meaning that the SamplesPerPixel is 2 but the image should be treated as
|
|
// an RGB image.
|
|
if (this->InternalImage->SamplesPerPixel == 2)
|
|
{
|
|
this->SetNumberOfScalarComponents(3);
|
|
}
|
|
|
|
// If the tiff file is multi-pages series of tiff images (3D volume)
|
|
if (this->InternalImage->NumberOfPages > 1)
|
|
{
|
|
if (this->InternalImage->SubFiles > 0)
|
|
{
|
|
this->DataExtent[4] = 0;
|
|
this->DataExtent[5] = this->InternalImage->SubFiles - 1;
|
|
}
|
|
else
|
|
{
|
|
this->DataExtent[4] = 0;
|
|
this->DataExtent[5] = this->InternalImage->NumberOfPages - 1;
|
|
}
|
|
|
|
if (!SpacingSpecifiedFlag)
|
|
{
|
|
this->DataSpacing[2] = this->DataSpacing[0];
|
|
}
|
|
if (!OriginSpecifiedFlag)
|
|
{
|
|
this->DataOrigin[2] = 0.0;
|
|
}
|
|
}
|
|
|
|
// If the tiff is tiled
|
|
if (this->InternalImage->NumberOfTiles > 1)
|
|
{
|
|
this->DataExtent[0] = 0;
|
|
this->DataExtent[1] = this->InternalImage->Width - 1;
|
|
this->DataExtent[2] = 0;
|
|
this->DataExtent[3] = this->InternalImage->Height - 1;
|
|
this->DataExtent[4] = 0;
|
|
this->DataExtent[5] = 0;
|
|
if (!SpacingSpecifiedFlag)
|
|
{
|
|
this->DataSpacing[2] = 1.0;
|
|
}
|
|
if (!OriginSpecifiedFlag)
|
|
{
|
|
this->DataOrigin[2] = 0.0;
|
|
}
|
|
}
|
|
|
|
this->vtkImageReader2::ExecuteInformation();
|
|
// Don't close the file yet, since we need the image internal
|
|
// parameters such as NumberOfPages, NumberOfTiles to decide
|
|
// how to read in the image.
|
|
}
|
|
|
|
|
|
// Set orientation type
|
|
//
|
|
//ORIENTATION_TOPLEFT 1 (row 0 top, col 0 lhs)
|
|
//ORIENTATION_TOPRIGHT 2 (row 0 top, col 0 rhs)
|
|
//ORIENTATION_BOTRIGHT 3 (row 0 bottom, col 0 rhs)
|
|
//ORIENTATION_BOTLEFT 4 (row 0 bottom, col 0 lhs)
|
|
//ORIENTATION_LEFTTOP 5 (row 0 lhs, col 0 top)
|
|
//ORIENTATION_RIGHTTOP 6 (row 0 rhs, col 0 top)
|
|
//ORIENTATION_RIGHTBOT 7 (row 0 rhs, col 0 bottom)
|
|
//ORIENTATION_LEFTBOT 8 (row 0 lhs, col 0 bottom) */
|
|
void vtkTIFFReader::SetOrientationType( unsigned int orientationType )
|
|
{
|
|
if (orientationType < 1 || orientationType > 8)
|
|
{
|
|
vtkErrorMacro( "Invalid Orientation type specified" );
|
|
return;
|
|
}
|
|
|
|
if (this->OrientationType != orientationType)
|
|
{
|
|
this->OrientationType = orientationType;
|
|
this->Modified();
|
|
}
|
|
if (!this->OrientationTypeSpecifiedFlag)
|
|
{
|
|
this->Modified();
|
|
}
|
|
// To preserve backward compatibility OrientationTypeSpecifiedFlag would
|
|
// always be set to true whatever user input...
|
|
this->OrientationTypeSpecifiedFlag = true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <class OT>
|
|
void vtkTIFFReader::Process2(OT *outPtr, int *)
|
|
{
|
|
if (!this->InternalImage->Open(this->GetInternalFileName()))
|
|
{
|
|
return;
|
|
}
|
|
// if orientation information is provided, overwrite the value
|
|
// read from the tiff image
|
|
if (this->GetOrientationTypeSpecifiedFlag())
|
|
{
|
|
this->InternalImage->Orientation = this->GetOrientationType();
|
|
}
|
|
|
|
this->Initialize();
|
|
this->ReadImageInternal(outPtr);
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This function reads in one data of data.
|
|
// templated to handle different data types.
|
|
template <class OT>
|
|
void vtkTIFFReader::Process(OT *outPtr, int outExtent[6], vtkIdType outIncr[3])
|
|
{
|
|
// multiple number of pages
|
|
if (this->InternalImage->NumberOfPages > 1)
|
|
{
|
|
this->ReadVolume(outPtr);
|
|
// close the TIFF file
|
|
this->InternalImage->Clean();
|
|
return;
|
|
}
|
|
|
|
// tiled image
|
|
if (this->InternalImage->NumberOfTiles > 0)
|
|
{
|
|
this->ReadTiles(outPtr);
|
|
// close the TIFF file
|
|
this->InternalImage->Clean();
|
|
return;
|
|
}
|
|
|
|
// The input tiff dataset is neither multiple pages and nor
|
|
// tiled. Hence close the image and start reading each TIFF
|
|
// file
|
|
this->InternalImage->Clean();
|
|
|
|
OT *outPtr2 = outPtr;
|
|
for (int idx2 = outExtent[4]; idx2 <= outExtent[5]; ++idx2)
|
|
{
|
|
this->ComputeInternalFileName(idx2);
|
|
// read in a TIFF file
|
|
this->Process2(outPtr2, outExtent);
|
|
// close the TIFF file
|
|
this->InternalImage->Clean();
|
|
|
|
this->UpdateProgress((idx2 - outExtent[4])/
|
|
(outExtent[5] - outExtent[4] + 1.0));
|
|
outPtr2 += outIncr[2];
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This function reads a data from a file. The datas extent/axes
|
|
// are assumed to be the same as the file extent/order.
|
|
void vtkTIFFReader::ExecuteDataWithInformation(vtkDataObject *output,
|
|
vtkInformation *outInfo)
|
|
{
|
|
if (this->InternalFileName == NULL)
|
|
{
|
|
vtkErrorMacro("Either a FileName or FilePrefix must be specified.");
|
|
return;
|
|
}
|
|
|
|
this->ComputeDataIncrements();
|
|
|
|
// Get the data
|
|
vtkImageData *data = this->AllocateOutputData(output, outInfo);
|
|
data->GetExtent(this->OutputExtent);
|
|
data->GetIncrements(this->OutputIncrements);
|
|
|
|
// Call the correct templated function for the input
|
|
void *outPtr = data->GetScalarPointer();
|
|
|
|
switch (data->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(this->Process((VTK_TT *)(outPtr),
|
|
this->OutputExtent, this->OutputIncrements));
|
|
default:
|
|
vtkErrorMacro("UpdateFromFile: Unknown data type");
|
|
}
|
|
data->GetPointData()->GetScalars()->SetName("Tiff Scalars");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned int vtkTIFFReader::GetFormat()
|
|
{
|
|
if (this->ImageFormat != vtkTIFFReader::NOFORMAT)
|
|
{
|
|
return this->ImageFormat;
|
|
}
|
|
|
|
switch (this->InternalImage->Photometrics)
|
|
{
|
|
case PHOTOMETRIC_RGB:
|
|
case PHOTOMETRIC_YCBCR:
|
|
this->ImageFormat = vtkTIFFReader::RGB;
|
|
return this->ImageFormat;
|
|
case PHOTOMETRIC_MINISWHITE:
|
|
case PHOTOMETRIC_MINISBLACK:
|
|
this->ImageFormat = vtkTIFFReader::GRAYSCALE;
|
|
return this->ImageFormat;
|
|
case PHOTOMETRIC_PALETTE:
|
|
for (unsigned int cc = 0; cc < 256; ++cc)
|
|
{
|
|
unsigned short red, green, blue;
|
|
this->GetColor(cc, &red, &green, &blue);
|
|
if (red != green || red != blue)
|
|
{
|
|
this->ImageFormat = vtkTIFFReader::PALETTE_RGB;
|
|
return this->ImageFormat;
|
|
}
|
|
}
|
|
this->ImageFormat = vtkTIFFReader::PALETTE_GRAYSCALE;
|
|
return this->ImageFormat;
|
|
}
|
|
this->ImageFormat = vtkTIFFReader::OTHER;
|
|
return this->ImageFormat;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkTIFFReader::GetColor(int index, unsigned short *red,
|
|
unsigned short *green, unsigned short *blue)
|
|
{
|
|
*red = 0;
|
|
*green = 0;
|
|
*blue = 0;
|
|
if (index < 0)
|
|
{
|
|
vtkErrorMacro("Color index has to be greater than 0");
|
|
return;
|
|
}
|
|
if (this->TotalColors > 0 &&
|
|
this->ColorRed && this->ColorGreen && this->ColorBlue )
|
|
{
|
|
if (index >= this->TotalColors)
|
|
{
|
|
vtkErrorMacro("Color index has to be less than number of colors ("
|
|
<< this->TotalColors << ")");
|
|
return;
|
|
}
|
|
*red = *(this->ColorRed + index);
|
|
*green = *(this->ColorGreen + index);
|
|
*blue = *(this->ColorBlue + index);
|
|
return;
|
|
}
|
|
|
|
unsigned short photometric;
|
|
|
|
if (!TIFFGetField(this->InternalImage->Image, TIFFTAG_PHOTOMETRIC, &photometric))
|
|
{
|
|
if (this->InternalImage->Photometrics != PHOTOMETRIC_PALETTE)
|
|
{
|
|
vtkErrorMacro("You can only access colors for palette images");
|
|
return;
|
|
}
|
|
}
|
|
|
|
unsigned short *red_orig, *green_orig, *blue_orig;
|
|
|
|
switch (this->InternalImage->BitsPerSample)
|
|
{
|
|
case 1: case 2: case 4:
|
|
case 8: case 16:
|
|
break;
|
|
default:
|
|
vtkErrorMacro( "Sorry, can not image with "
|
|
<< this->InternalImage->BitsPerSample
|
|
<< "-bit samples" );
|
|
return;
|
|
}
|
|
if (!TIFFGetField(this->InternalImage->Image, TIFFTAG_COLORMAP,
|
|
&red_orig, &green_orig, &blue_orig))
|
|
{
|
|
vtkErrorMacro("Missing required \"Colormap\" tag");
|
|
return;
|
|
}
|
|
this->TotalColors = (1L << this->InternalImage->BitsPerSample);
|
|
|
|
if (index >= this->TotalColors)
|
|
{
|
|
vtkErrorMacro("Color index has to be less than number of colors ("
|
|
<< this->TotalColors << ")");
|
|
return;
|
|
}
|
|
this->ColorRed = red_orig;
|
|
this->ColorGreen = green_orig;
|
|
this->ColorBlue = blue_orig;
|
|
|
|
*red = *(red_orig + index);
|
|
*green = *(green_orig + index);
|
|
*blue = *(blue_orig + index);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void vtkTIFFReader::Initialize()
|
|
{
|
|
this->ColorRed = 0;
|
|
this->ColorGreen = 0;
|
|
this->ColorBlue = 0;
|
|
this->TotalColors = -1;
|
|
this->ImageFormat = vtkTIFFReader::NOFORMAT;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
template<typename T>
|
|
void vtkTIFFReader::ReadVolume(T* buffer)
|
|
{
|
|
int width = this->InternalImage->Width;
|
|
int height = this->InternalImage->Height;
|
|
int samplesPerPixel = this->InternalImage->SamplesPerPixel;
|
|
unsigned int npages = this->InternalImage->NumberOfPages;
|
|
|
|
// counter for slices (not every page is a slice)
|
|
unsigned int slice = 0;
|
|
for (unsigned int page = 0; page < npages; ++page)
|
|
{
|
|
this->UpdateProgress(static_cast<double>(page + 1) / npages);
|
|
if (this->InternalImage->SubFiles > 0)
|
|
{
|
|
long subfiletype = 6;
|
|
if (TIFFGetField(this->InternalImage->Image, TIFFTAG_SUBFILETYPE,
|
|
&subfiletype))
|
|
{
|
|
if (subfiletype != 0)
|
|
{
|
|
TIFFReadDirectory(this->InternalImage->Image);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we have a Zeiss image meaning that the SamplesPerPixel is 2
|
|
if (samplesPerPixel == 2)
|
|
{
|
|
T* volume = buffer;
|
|
volume += width * height * slice * samplesPerPixel;
|
|
this->ReadTwoSamplesPerPixelImage(volume, width, height);
|
|
break;
|
|
}
|
|
else if (!this->InternalImage->CanRead())
|
|
{
|
|
uint32 *tempImage = new uint32[width * height];
|
|
if (!TIFFReadRGBAImage(this->InternalImage->Image,
|
|
width, height,
|
|
tempImage, 1))
|
|
{
|
|
vtkErrorMacro( << "Cannot read TIFF image or as a TIFF RGBA image" );
|
|
delete [] tempImage;
|
|
return;
|
|
}
|
|
|
|
const bool flip = this->InternalImage->Orientation != ORIENTATION_TOPLEFT;
|
|
T* fimage = buffer;
|
|
fimage += width * height * 4 * slice;
|
|
for (int yy = 0; yy < height; ++yy)
|
|
{
|
|
uint32* ssimage;
|
|
if (flip)
|
|
{
|
|
ssimage = tempImage + yy * width;
|
|
}
|
|
else
|
|
{
|
|
ssimage = tempImage + (height - yy - 1) * width;
|
|
}
|
|
for (int xx = 0; xx < width; ++xx)
|
|
{
|
|
*(fimage ) = static_cast<T>(TIFFGetR(*ssimage)); // Red
|
|
*(fimage + 1) = static_cast<T>(TIFFGetG(*ssimage)); // Green
|
|
*(fimage + 2) = static_cast<T>(TIFFGetB(*ssimage)); // Blue
|
|
*(fimage + 3) = static_cast<T>(TIFFGetA(*ssimage)); // Alpha
|
|
fimage += 4;
|
|
++ssimage;
|
|
}
|
|
}
|
|
delete [] tempImage;
|
|
tempImage = 0;
|
|
}
|
|
else
|
|
{
|
|
unsigned int format = this->GetFormat();
|
|
switch (format)
|
|
{
|
|
case vtkTIFFReader::GRAYSCALE:
|
|
case vtkTIFFReader::RGB:
|
|
case vtkTIFFReader::PALETTE_RGB:
|
|
case vtkTIFFReader::PALETTE_GRAYSCALE:
|
|
{
|
|
T* volume = buffer;
|
|
volume += width * height * slice * samplesPerPixel;
|
|
this->ReadGenericImage(volume, width, height);
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
// advance to next slice
|
|
slice++;
|
|
TIFFReadDirectory(this->InternalImage->Image);
|
|
}
|
|
}
|
|
|
|
/** Read a tiled tiff */
|
|
void vtkTIFFReader::ReadTiles(void* buffer)
|
|
{
|
|
unsigned char* volume =
|
|
reinterpret_cast<unsigned char*>(buffer);
|
|
unsigned char *tile
|
|
= new unsigned char[TIFFTileSize(this->InternalImage->Image)];
|
|
|
|
const unsigned int width = this->InternalImage->Width;
|
|
const unsigned int height = this->InternalImage->Height;
|
|
const unsigned int tileWidth = this->InternalImage->TileWidth;
|
|
const unsigned int tileHeight = this->InternalImage->TileHeight;
|
|
const unsigned int pixelSize = this->InternalImage->SamplesPerPixel;
|
|
const bool rowMultiple = (height % tileHeight == 0 ) ? true : false;
|
|
const bool colMultiple = (width % tileWidth == 0 ) ? true : false;
|
|
const bool flip = this->InternalImage->Orientation != ORIENTATION_TOPLEFT;
|
|
|
|
for (unsigned int slice = 0; slice < this->InternalImage->NumberOfPages;
|
|
++slice)
|
|
{
|
|
for (unsigned int row = 0;
|
|
row < (rowMultiple ? height : height - tileHeight);
|
|
row += tileHeight)
|
|
{
|
|
const unsigned int r = flip ? height - row - tileHeight : row;
|
|
for (unsigned int col = 0;
|
|
col < (colMultiple ? width : width - tileWidth);
|
|
col += tileWidth)
|
|
{
|
|
if (TIFFReadTile(this->InternalImage->Image, tile, col, r, slice, 0) < 0)
|
|
{
|
|
vtkErrorMacro(<< "Cannot read tile : "<< r << "," << col << " from file");
|
|
delete [] tile;
|
|
return;
|
|
}
|
|
// Currently not using tile depth
|
|
unsigned int zz = 0;
|
|
for (unsigned int yy = 0; yy < tileHeight; ++yy)
|
|
{
|
|
const unsigned int y = flip ? tileHeight + height % tileHeight - yy - 1 : yy;
|
|
memcpy (
|
|
volume + (((slice + zz) * height + row + y) * width + col) * pixelSize,
|
|
tile + (zz * tileHeight + yy) * tileWidth * pixelSize,
|
|
tileWidth * pixelSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Fill the boundaries
|
|
if (!colMultiple)
|
|
{
|
|
const unsigned int lenx = width % tileWidth;
|
|
const unsigned int col = width - lenx;
|
|
const unsigned int tilexWidth = lenx;
|
|
for (unsigned int row = 0;
|
|
row < (rowMultiple ? height : height - tileHeight);
|
|
row += tileHeight)
|
|
{
|
|
const unsigned int r = flip ? height - row - tileHeight - 1 : row;
|
|
if (TIFFReadTile(this->InternalImage->Image, tile, col, r, 0, 0) < 0)
|
|
{
|
|
vtkErrorMacro(<< "Cannot read tile : "<< r << "," << col << " from file");
|
|
delete [] tile;
|
|
return;
|
|
}
|
|
const unsigned int zz = 0;
|
|
for (unsigned int yy = 0; yy < tileHeight; ++yy)
|
|
{
|
|
const unsigned int y = flip ? tileHeight + height % tileHeight - yy - 1 : yy;
|
|
memcpy (
|
|
volume + (((0 + zz) * height + row + y) * width + col) * pixelSize,
|
|
tile + (zz * tileHeight + yy) * tileWidth * pixelSize,
|
|
tilexWidth * pixelSize);
|
|
}
|
|
}
|
|
}
|
|
if (!rowMultiple)
|
|
{
|
|
const unsigned int leny = height % tileHeight;
|
|
const unsigned int row = height - leny;
|
|
const unsigned int r = flip ? 0 : row;
|
|
for (unsigned int col = 0;
|
|
col < (colMultiple ? width : width - tileWidth);
|
|
col += tileWidth)
|
|
{
|
|
if (TIFFReadTile(this->InternalImage->Image, tile, col, row, 0, 0) < 0)
|
|
{
|
|
vtkErrorMacro(<< "Cannot read tile : "<< r << "," << col << " from file");
|
|
delete [] tile;
|
|
return;
|
|
}
|
|
const unsigned int zz = 0;
|
|
for (unsigned int yy = 0; yy < leny; ++yy)
|
|
{
|
|
const unsigned int y = flip ? leny - yy - 1 : yy;
|
|
memcpy (
|
|
volume + (((0 + zz) * height + r + y) * width + col) * pixelSize,
|
|
tile + (zz * tileHeight + yy) * tileWidth * pixelSize,
|
|
tileWidth * pixelSize);
|
|
}
|
|
}
|
|
}
|
|
if (!colMultiple && !rowMultiple)
|
|
{
|
|
const unsigned int lenx = width % tileWidth;
|
|
const unsigned int col = width - lenx;
|
|
|
|
const unsigned int leny = height % tileHeight;
|
|
const unsigned int row = height - leny;
|
|
const unsigned int tilexWidth = lenx;
|
|
|
|
const unsigned int r = flip ? 0 : row;
|
|
if (TIFFReadTile(this->InternalImage->Image, tile, col, row, 0, 0) < 0)
|
|
{
|
|
vtkErrorMacro(<< "Cannot read tile : "<< r << "," << col << " from file");
|
|
delete [] tile;
|
|
return;
|
|
}
|
|
const unsigned int zz = 0;
|
|
unsigned int y;
|
|
for (unsigned int yy = 0; yy < leny; ++yy)
|
|
{
|
|
y = flip ? leny - yy - 1 : yy;
|
|
memcpy (
|
|
volume + (((0 + zz) * height + r + y) * width + col) * pixelSize,
|
|
tile + (zz * tileHeight + yy) * tileWidth * pixelSize,
|
|
tilexWidth * pixelSize);
|
|
}
|
|
}
|
|
delete [] tile;
|
|
}
|
|
|
|
/** To Support Zeiss images that contains only 2 samples per pixel but are actually
|
|
* RGB images */
|
|
void vtkTIFFReader::ReadTwoSamplesPerPixelImage(void *out,
|
|
unsigned int width,
|
|
unsigned int height)
|
|
{
|
|
unsigned int isize = TIFFScanlineSize(this->InternalImage->Image);
|
|
unsigned int cc;
|
|
int row;
|
|
tdata_t buf = _TIFFmalloc(isize);
|
|
|
|
int inc = 1;
|
|
|
|
if(this->GetDataScalarType() == VTK_UNSIGNED_CHAR)
|
|
{
|
|
unsigned char* image;
|
|
if (this->InternalImage->PlanarConfig == PLANARCONFIG_CONTIG)
|
|
{
|
|
for ( row = 0; row < (int)height; row ++ )
|
|
{
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, row, 0) <= 0)
|
|
{
|
|
vtkErrorMacro( << "Problem reading the row: " << row );
|
|
break;
|
|
}
|
|
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
image = reinterpret_cast<unsigned char*>(out) + row * width * inc;
|
|
}
|
|
else
|
|
{
|
|
image = reinterpret_cast<unsigned char*>(out) + width * inc * (height - (row + 1));
|
|
}
|
|
|
|
for (cc = 0; cc < isize;
|
|
cc += this->InternalImage->SamplesPerPixel )
|
|
{
|
|
inc = this->EvaluateImageAt( image,
|
|
static_cast<unsigned char *>(buf) +
|
|
cc );
|
|
image += inc;
|
|
}
|
|
}
|
|
}
|
|
else if(this->InternalImage->PlanarConfig == PLANARCONFIG_SEPARATE)
|
|
{
|
|
unsigned long s;
|
|
unsigned long nsamples = 0;
|
|
TIFFGetField(this->InternalImage->Image, TIFFTAG_SAMPLESPERPIXEL, &nsamples);
|
|
for (s = 0; s < nsamples; s++)
|
|
{
|
|
for ( row = 0; row < (int)height; row ++ )
|
|
{
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, row, s) <= 0)
|
|
{
|
|
vtkErrorMacro( << "Problem reading the row: " << row );
|
|
break;
|
|
}
|
|
|
|
inc = 3;
|
|
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
image = reinterpret_cast<unsigned char*>(out) + row * width * inc;
|
|
}
|
|
else
|
|
{
|
|
image = reinterpret_cast<unsigned char*>(out) + width * inc * (height - (row + 1));
|
|
}
|
|
|
|
// We translate the output pixel to be on the right RGB
|
|
image += s;
|
|
for (cc = 0; cc < isize;
|
|
cc += 1)
|
|
{
|
|
(*image) = *(static_cast<unsigned char *>(buf) + cc);
|
|
inc = 3;
|
|
image += inc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(this->GetDataScalarType() == VTK_UNSIGNED_SHORT)
|
|
{
|
|
isize /= 2;
|
|
unsigned short* image;
|
|
if (this->InternalImage->PlanarConfig == PLANARCONFIG_CONTIG)
|
|
{
|
|
for ( row = 0; row < (int)height; row ++ )
|
|
{
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, row, 0) <= 0)
|
|
{
|
|
vtkErrorMacro( << "Problem reading the row: " << row );
|
|
break;
|
|
}
|
|
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
image = reinterpret_cast<unsigned short*>(out) + row * width * inc;
|
|
}
|
|
else
|
|
{
|
|
image = reinterpret_cast<unsigned short*>(out) + width * inc * (height - (row + 1));
|
|
}
|
|
|
|
for (cc = 0; cc < isize;
|
|
cc += this->InternalImage->SamplesPerPixel )
|
|
{
|
|
inc = this->EvaluateImageAt( image,
|
|
static_cast<unsigned short *>(buf) +
|
|
cc );
|
|
image += inc;
|
|
}
|
|
}
|
|
}
|
|
else if(this->InternalImage->PlanarConfig == PLANARCONFIG_SEPARATE)
|
|
{
|
|
unsigned long s, nsamples;
|
|
TIFFGetField(this->InternalImage->Image, TIFFTAG_SAMPLESPERPIXEL, &nsamples);
|
|
for (s = 0; s < nsamples; s++)
|
|
{
|
|
for ( row = 0; row < (int)height; row ++ )
|
|
{
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, row, s) <= 0)
|
|
{
|
|
vtkErrorMacro( << "Problem reading the row: " << row );
|
|
break;
|
|
}
|
|
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
image = reinterpret_cast<unsigned short*>(out) + row * width * inc;
|
|
}
|
|
else
|
|
{
|
|
image = reinterpret_cast<unsigned short*>(out) + width * inc * (height - (row + 1));
|
|
}
|
|
// We translate the output pixel to be on the right RGB
|
|
image += s;
|
|
for (cc = 0; cc < isize;
|
|
cc += 1)
|
|
{
|
|
(*image) = *(static_cast<unsigned short *>(buf) + cc);
|
|
inc = 3;
|
|
image += inc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_TIFFfree(buf);
|
|
}
|
|
|
|
template<typename T>
|
|
void vtkTIFFReader::ReadGenericImage(T* out, unsigned int, unsigned int height)
|
|
{
|
|
// Fast path for simple images
|
|
unsigned int format = this->GetFormat();
|
|
if (this->InternalImage->PlanarConfig == PLANARCONFIG_CONTIG &&
|
|
this->OutputIncrements[0] == 1 &&
|
|
(format == vtkTIFFReader::GRAYSCALE &&
|
|
this->InternalImage->Photometrics == PHOTOMETRIC_MINISBLACK &&
|
|
this->InternalImage->SamplesPerPixel == 1))
|
|
{
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
FlipFalse flip;
|
|
if (!ReadTemplatedImage(out, flip,
|
|
this->OutputExtent[0], this->OutputExtent[1],
|
|
this->OutputExtent[2], this->OutputExtent[3],
|
|
this->OutputIncrements[1],
|
|
height, this->InternalImage->Image))
|
|
{
|
|
vtkErrorMacro(<< "Problem reading slice of volume in TIFF file.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FlipTrue flip;
|
|
if (!ReadTemplatedImage(out, flip,
|
|
this->OutputExtent[0], this->OutputExtent[1],
|
|
this->OutputExtent[2], this->OutputExtent[3],
|
|
this->OutputIncrements[1],
|
|
height, this->InternalImage->Image))
|
|
{
|
|
vtkErrorMacro(<< "Problem reading slice of volume in TIFF file.");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
unsigned int isize = TIFFScanlineSize(this->InternalImage->Image);
|
|
tdata_t buf = _TIFFmalloc(isize);
|
|
|
|
if (this->InternalImage->PlanarConfig != PLANARCONFIG_CONTIG)
|
|
{
|
|
vtkErrorMacro(<< "This reader can only do PLANARCONFIG_CONTIG");
|
|
return;
|
|
}
|
|
|
|
T* image;
|
|
if (this->InternalImage->PlanarConfig == PLANARCONFIG_CONTIG)
|
|
{
|
|
int fileRow = 0;
|
|
for (int row = this->OutputExtent[2]; row <= this->OutputExtent[3]; ++row)
|
|
{
|
|
// Flip from lower left origin to upper left if necessary.
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
fileRow = row;
|
|
}
|
|
else
|
|
{
|
|
fileRow = height - row - 1;
|
|
}
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, fileRow, 0) <= 0)
|
|
{
|
|
vtkErrorMacro(<< "Problem reading the row: " << fileRow);
|
|
break;
|
|
}
|
|
image = out + (row - this->OutputExtent[2]) * this->OutputIncrements[1];
|
|
|
|
// Copy the pixels into the output buffer
|
|
unsigned int cc = this->OutputExtent[0] * this->InternalImage->SamplesPerPixel;
|
|
for (int ix = this->OutputExtent[0]; ix <= this->OutputExtent[1]; ++ix)
|
|
{
|
|
this->EvaluateImageAt(image, static_cast<T*>(buf) + cc);
|
|
image += this->OutputIncrements[0];
|
|
cc += this->InternalImage->SamplesPerPixel;
|
|
}
|
|
}
|
|
}
|
|
else if(this->InternalImage->PlanarConfig == PLANARCONFIG_SEPARATE)
|
|
{
|
|
int fileRow = 0;
|
|
unsigned long nsamples;
|
|
TIFFGetField(this->InternalImage->Image, TIFFTAG_SAMPLESPERPIXEL, &nsamples);
|
|
for (unsigned long s = 0; s < nsamples; ++s)
|
|
{
|
|
for (int row = this->OutputExtent[2]; row <= this->OutputExtent[3]; ++row)
|
|
{
|
|
if (this->InternalImage->Orientation == ORIENTATION_TOPLEFT)
|
|
{
|
|
fileRow = row;
|
|
}
|
|
else
|
|
{
|
|
fileRow = height - row - 1;
|
|
}
|
|
if (TIFFReadScanline(this->InternalImage->Image, buf, fileRow, s) <= 0)
|
|
{
|
|
vtkErrorMacro( << "Problem reading the row: " << fileRow );
|
|
break;
|
|
}
|
|
|
|
image = out + (row-this->OutputExtent[2])*this->OutputIncrements[1];
|
|
unsigned int cc = this->OutputExtent[0] * this->InternalImage->SamplesPerPixel;
|
|
for (int ix = this->OutputExtent[0]; ix <= this->OutputExtent[1]; ++ix)
|
|
{
|
|
this->EvaluateImageAt(image, static_cast<T*>(buf) + cc);
|
|
image += this->OutputIncrements[0];
|
|
cc += this->InternalImage->SamplesPerPixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_TIFFfree(buf);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template<typename T>
|
|
void vtkTIFFReader::ReadImageInternal(T* outPtr)
|
|
{
|
|
int width = this->InternalImage->Width;
|
|
int height = this->InternalImage->Height;
|
|
|
|
if (!this->InternalImage->CanRead())
|
|
{
|
|
// Why do we read the image for the ! CanRead case?
|
|
uint32 *tempImage = reinterpret_cast<uint32*>(outPtr);
|
|
|
|
if (this->OutputExtent[0] != 0 ||
|
|
this->OutputExtent[1] != width - 1 ||
|
|
this->OutputExtent[2] != 0 ||
|
|
this->OutputExtent[3] != height - 1)
|
|
{
|
|
tempImage = new uint32[ width * height ];
|
|
}
|
|
// This should really be fixed to read only the rows necessary.
|
|
if (!TIFFReadRGBAImage(this->InternalImage->Image,
|
|
width, height,
|
|
tempImage, 0))
|
|
{
|
|
vtkErrorMacro("Problem reading RGB image");
|
|
if (tempImage != reinterpret_cast<uint32*>(outPtr))
|
|
{
|
|
delete [] tempImage;
|
|
}
|
|
return;
|
|
}
|
|
uint32* ssimage = tempImage;
|
|
T* fimage = outPtr;
|
|
for (int yy = 0; yy < height; ++yy)
|
|
{
|
|
for (int xx = 0; xx < width; ++xx)
|
|
{
|
|
if (xx >= this->OutputExtent[0] &&
|
|
xx <= this->OutputExtent[1] &&
|
|
yy >= this->OutputExtent[2] &&
|
|
yy <= this->OutputExtent[3])
|
|
{
|
|
*(fimage ) = static_cast<T>(TIFFGetR(*ssimage)); // Red
|
|
*(fimage + 1) = static_cast<T>(TIFFGetG(*ssimage)); // Green
|
|
*(fimage + 2) = static_cast<T>(TIFFGetB(*ssimage)); // Blue
|
|
*(fimage + 3) = static_cast<T>(TIFFGetA(*ssimage)); // Alpha
|
|
fimage += 4;
|
|
}
|
|
++ssimage;
|
|
}
|
|
}
|
|
|
|
if (tempImage != reinterpret_cast<uint32*>(outPtr))
|
|
{
|
|
delete [] tempImage;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (this->GetFormat())
|
|
{
|
|
case vtkTIFFReader::GRAYSCALE:
|
|
case vtkTIFFReader::RGB:
|
|
case vtkTIFFReader::PALETTE_RGB:
|
|
case vtkTIFFReader::PALETTE_GRAYSCALE:
|
|
this->ReadGenericImage(outPtr, width, height);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
template<typename T>
|
|
int vtkTIFFReader::EvaluateImageAt(T* out, T* in)
|
|
{
|
|
unsigned char *image = reinterpret_cast<unsigned char *>(out);
|
|
unsigned char *source =reinterpret_cast<unsigned char *>(in);
|
|
unsigned short red, green, blue;
|
|
switch (this->GetFormat())
|
|
{
|
|
case vtkTIFFReader::GRAYSCALE:
|
|
if (this->InternalImage->Photometrics == PHOTOMETRIC_MINISBLACK)
|
|
{
|
|
*out = *in;
|
|
}
|
|
else
|
|
{
|
|
*image = ~(*source);
|
|
}
|
|
return 1;
|
|
case vtkTIFFReader::PALETTE_GRAYSCALE:
|
|
this->GetColor(*source, &red, &green, &blue);
|
|
*image = static_cast<unsigned char>(red); // red >> 8
|
|
return 1;
|
|
case vtkTIFFReader::RGB:
|
|
*(image ) = *(source ); // red
|
|
*(image + 1) = *(source + 1); // green
|
|
*(image + 2) = *(source + 2); // blue
|
|
if (this->InternalImage->SamplesPerPixel == 4)
|
|
{
|
|
*(image + 3) = 255 - *(source + 3); // alpha
|
|
}
|
|
return this->InternalImage->SamplesPerPixel;
|
|
case vtkTIFFReader::PALETTE_RGB:
|
|
this->GetColor(static_cast<int>(*in), &red, &green, &blue);
|
|
*(out ) = red << 8;
|
|
*(out + 1) = green << 8;
|
|
*(out + 2) = blue << 8;
|
|
if (this->GetDataScalarType() == VTK_SHORT ||
|
|
this->GetDataScalarType() == VTK_UNSIGNED_SHORT)
|
|
{
|
|
this->GetColor(static_cast<int>(*in), &red, &green, &blue);
|
|
*(out ) = red << 8;
|
|
*(out + 1) = green << 8;
|
|
*(out + 2) = blue << 8;
|
|
}
|
|
else
|
|
{
|
|
this->GetColor(static_cast<int>(*in), &red, &green, &blue);
|
|
*(out ) = red >> 8;
|
|
*(out + 1) = green >> 8;
|
|
*(out + 2) = blue >> 8;
|
|
}
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int vtkTIFFReader::CanReadFile(const char* fname)
|
|
{
|
|
vtkTIFFReaderInternal tf;
|
|
int res = tf.Open(fname);
|
|
tf.Clean();
|
|
if (res)
|
|
{
|
|
return 3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkTIFFReader::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
os << indent << "OrientationType: " << this->OrientationType << endl;
|
|
os << indent << "OrientationTypeSpecifiedFlag: " << this->OrientationTypeSpecifiedFlag << endl;
|
|
os << indent << "OriginSpecifiedFlag: " << this->OriginSpecifiedFlag << endl;
|
|
os << indent << "SpacingSpecifiedFlag: " << this->SpacingSpecifiedFlag << endl;
|
|
}
|