Skip to content

Commit

Permalink
first working version of 3D (tiled) TIFF loader
Browse files Browse the repository at this point in the history
- added TIFFFile::pixelType()
- added TIFFFile::readTile() (very thin wrapper around libtiff)
- added VolumeInfo::fileType of „TILEDTIFF“
- FIXME: only works with correct target datatype
- FIXME: does not cope with multi-channel data yet
  • Loading branch information
hmeine committed Jan 12, 2015
1 parent 5c343e5 commit 56f78c0
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 49 deletions.
50 changes: 48 additions & 2 deletions include/vigra/multi_impex.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "multi_array.hxx"
#include "multi_pointoperators.hxx"
#include "sifImport.hxx"
#include "tiff_file.hxx"

#ifdef _MSC_VER
# include <direct.h>
Expand Down Expand Up @@ -168,8 +169,9 @@ class VolumeImportInfo
Possible values are:
<DL>
<DT>"MULTIPAGE"<DD> Multiple 2D images in a single file (currently only supported by TIFF).
<DT>"TILEDTIFF"<DD> Tiled TIFF (standard conform, but extremly rare, used by MeVisLab).
<DT>"SIF"<DD> <a href="http://www.andor.com">Andor Technology's</a> .sif format.
<DT>"RAW"<DD> Raw data file, accompanied by a .info file
<DT>"RAW"<DD> Raw data file, accompanied by an .info file
<DT>"STACK"<DD> A numbered set of 2D image files, one per slice of the volume.
</DL>
**/
Expand Down Expand Up @@ -491,7 +493,51 @@ void VolumeImportInfo::importImpl(MultiArrayView <3, T, Stride> &volume) const
{
vigra_precondition(this->shape() == volume.shape(), "importVolume(): Output array must be shaped according to VolumeImportInfo.");

if(fileType_ == "RAW")
if(fileType_ == "TILEDTIFF")
{
TIFFFile f(path_.c_str(), "r");

MultiArrayShape<3>::type
zero3D(0, 0, 0),
one3D(1, 1, 1),
imageSize = f.imageSize3D(),
tileSize = f.tileSize3D(),
tileCount = (imageSize + tileSize - one3D) / tileSize;

MultiArray<3, T> tileBuffer(tileSize);

// read all tiles; important background info on the libtiff API:
// - all tiles have the same size, i.e. we need to clip at the
// upper boundaries in general
// - although the tiles are also numbered, the can only be
// read by passing an arbitrary(!) coordinate pointing into
// that slice...
MultiArrayShape<3>::type copyPos;
for(copyPos[0] = 0; copyPos[0] < imageSize[0]; copyPos[0] += tileSize[0])
{
for(copyPos[1] = 0; copyPos[1] < imageSize[1]; copyPos[1] += tileSize[1])
{
for(copyPos[2] = 0; copyPos[2] < imageSize[2]; copyPos[2] += tileSize[2])
{
// clip by not always copying tileSize voxels into target volume
MultiArrayShape<3>::type copySize(tileSize);
for(char dim = 0; dim < 3; ++dim)
if(copySize[dim] > imageSize[dim] - copyPos[dim])
copySize[dim] = imageSize[dim] - copyPos[dim];

MultiArrayView<3, T, Stride>
targetVOI(volume.subarray(copyPos, imageSize));

// now read and copy (TODO: handle T != pixelType()!)
f.readTile(tileBuffer.data(), copyPos[0], copyPos[1], copyPos[2], 0);

copyMultiArray(srcMultiArrayRange(tileBuffer.subarray(zero3D, copySize)),
destMultiArray(targetVOI));
}
}
}
}
else if(fileType_ == "RAW")
{
std::string dirName, baseName;
char oldCWD[2048];
Expand Down
92 changes: 48 additions & 44 deletions include/vigra/tiff_file.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,57 @@ namespace vigra {
class TIFFFile
{
public:
typedef TinyVector<uint32, 3> VolumeSize;

TIFFFile(const char *filename, const char *mode);

/**
* There are two possible TIFF file types that require different
* I/O APIs: striped files (which you know well) and tiled files
* (veeeery esoteric).
*/
enum FileType { INVALID = 0, STRIPED = 1, TILED = 2 };

/**
* For usual TIFF files (no tile size fields), returns STRIPED.
* Returns TILED iff the TIFF file contains both tile width and
* height tags (and the tile depth tag for 3D files, i.e. if
* there's an image depth tag), INVALID otherwise (some, but not
* all extents specified).
*/
FileType fileType() const
{
return fileType_;
}

/**
* Returns true for 3D TIFFs, i.e. those with the SGI image/tile depth fields.
*/
bool hasDepth() const
{
return hasDepth_;
}

VolumeSize imageSize3D() const
{
return imageSize_;
}

VolumeSize tileSize3D() const
{
return tileSize_;
}
typedef TinyVector<uint32, 3> VolumeSize;

TIFFFile(const char *filename, const char *mode);

/**
* There are two possible TIFF file types that require different
* I/O APIs: striped files (which you know well) and tiled files
* (veeeery esoteric).
*/
enum FileType { INVALID = 0, STRIPED = 1, TILED = 2 };

/**
* For usual TIFF files (no tile size fields), returns STRIPED.
* Returns TILED iff the TIFF file contains both tile width and
* height tags (and the tile depth tag for 3D files, i.e. if
* there's an image depth tag), INVALID otherwise (some, but not
* all extents specified).
*/
FileType fileType() const
{
return fileType_;
}

/**
* Returns true for 3D TIFFs, i.e. those with the SGI image/tile depth fields.
*/
bool hasDepth() const
{
return hasDepth_;
}

VolumeSize imageSize3D() const
{
return imageSize_;
}

VolumeSize tileSize3D() const
{
return tileSize_;
}

tsize_t readTile(tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t sample);

std::string pixelType() const;

private:
TIFF *h_; // TIFF file handle (cf. libtiff library)
TIFF *h_; // TIFF file handle (cf. libtiff library)

TinyVector<uint32, 3> imageSize_, tileSize_;
FileType fileType_;
bool hasDepth_;
TinyVector<uint32, 3> imageSize_, tileSize_;
FileType fileType_;
bool hasDepth_;
};

} // namespace vigra
3 changes: 2 additions & 1 deletion src/impex/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ ADD_LIBRARY(vigraimpex ${LIBTYPE}
hdf5_rf_impex.cxx
iccjpeg.c
imageinfo.cxx
multi_impex.cxx
multi_impex.cxx
tiff_file.cxx
jpeg.cxx
lz4.c
png.cxx
Expand Down
22 changes: 20 additions & 2 deletions src/impex/multi_impex.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ std::string trimString(const std::string &s)
} // namespace detail


// find filenames matching the pattern "<path>/base[0-9]+ext"
#ifdef _MSC_VER
void splitPathFromFilename(const std::string &pathAndName,
std::string &path, std::string &name)
Expand All @@ -74,6 +73,7 @@ void splitPathFromFilename(const std::string &pathAndName,
}
}

// find filenames matching the pattern "<path>/base[0-9]+ext" (Windows version)
VIGRA_EXPORT void findImageSequence(const std::string &name_base,
const std::string &name_ext,
std::vector<std::string> & numbers)
Expand Down Expand Up @@ -153,6 +153,7 @@ void splitPathFromFilename(const std::string &pathAndName,
}
}

// find filenames matching the pattern "<path>/base[0-9]+ext" (Unix version)
void findImageSequence(const std::string &name_base,
const std::string &name_ext,
std::vector<std::string> & numbers)
Expand Down Expand Up @@ -863,7 +864,24 @@ VolumeImportInfo::VolumeImportInfo(const std::string &filename)
return;
}
}


// for TIFF files, check for single-file, 3D data, tiled TIFFs first:
if(codecManager().getFileTypeByMagicString(filename) == "TIFF")
{
TIFFFile f(filename.c_str(), "r");

shape_ = f.imageSize3D();

pixelType_ = f.pixelType();
numBands_ = 1; // FIXME

path_ = filename;

baseName_ = filename;
fileType_ = "TILEDTIFF";
return;
}

// try multi-page TIFF or image stack
if(isImage(filename.c_str()))
{
Expand Down
110 changes: 110 additions & 0 deletions src/impex/tiff_file.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,114 @@ TIFFFile::TIFFFile(const char *filename, const char *mode)
}
}

tsize_t TIFFFile::readTile(tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t sample)
{
return TIFFReadTile(h_, buf, x, y, z, sample);
}

std::string TIFFFile::pixelType() const
{
// get bits per sample, default to 8
uint16 bits_per_sample;
if(!TIFFGetField( h_, TIFFTAG_BITSPERSAMPLE, &bits_per_sample))
bits_per_sample = 8;

// get pixeltype
if(bits_per_sample == 1)
return "BILEVEL";

// try the sampleformat tag
uint16 sampleformat;
if(TIFFGetField(h_, TIFFTAG_SAMPLEFORMAT, &sampleformat))
{
switch (sampleformat)
{
case SAMPLEFORMAT_UINT:
// added by dangelo, support for UINT 16 & 32 bit
switch(bits_per_sample)
{
case 8:
return "UINT8";
case 16:
return "UINT16";
case 32:
return "UINT32";
}
break;

case SAMPLEFORMAT_INT:
switch (bits_per_sample)
{
case 8:
return "INT8";
case 16:
return "INT16";
case 32:
return "INT32";
}
break;

case SAMPLEFORMAT_IEEEFP:
switch (bits_per_sample)
{
case 32:
return "FLOAT";
case 64:
return "DOUBLE";
}
break;

default:
;
}
}

// fall back to the (obsolete) datatype tag
uint16 datatype;
if(TIFFGetField(h_, TIFFTAG_DATATYPE, &datatype))
{
// dangelo: correct parsing of INT/UINT (given in tiff.h)
switch (datatype)
{
case TIFF_BYTE:
return "UINT8";
case TIFF_SBYTE:
return "INT8";
case TIFF_SHORT:
return "UINT16";
case TIFF_SSHORT:
return "INT16";
case TIFF_LONG:
return "UINT32";
case TIFF_SLONG:
return "INT32";
case TIFF_FLOAT:
return "FLOAT";
case TIFF_DOUBLE:
return "DOUBLE";
default:
;
}
}

// ugly: no useable pixeltype found..
// e.g. imagemagick writes files without it; try to guess a suitable one here:
switch(bits_per_sample)
{
case 8:
return "UINT8";
case 16:
return "UINT16";
case 32:
return "UINT32"; // prefer int over float
case 64:
return "DOUBLE";
default:
;
}

vigra_fail( "TIFFDecoderImpl::init(): Sampleformat or Datatype tag undefined and guessing sampletype from Bits per Sample failed." );
return "<unreached>";
}

} // namespace vigra

0 comments on commit 56f78c0

Please sign in to comment.