From 28bf98838dd2d63d963ac33141fe062dd2d6787c Mon Sep 17 00:00:00 2001 From: Hans Meine Date: Mon, 12 Jan 2015 19:02:25 +0100 Subject: [PATCH 1/4] extract multi_impex impl. from imageinfo.cxx move implementation of stuff declared in multi_impex.hxx into multi_impex.cxx (was in imageinfo.cxx) (manually cherry-picked from 2011 improved_tiff_loader branch) --- include/vigra/multi_impex.hxx | 2 +- src/impex/CMakeLists.txt | 1 + src/impex/imageinfo.cxx | 1085 +-------------------------------- src/impex/multi_impex.cxx | 543 +++++++++++++++++ 4 files changed, 546 insertions(+), 1085 deletions(-) create mode 100644 src/impex/multi_impex.cxx diff --git a/include/vigra/multi_impex.hxx b/include/vigra/multi_impex.hxx index 9eeae13ec..788f4e155 100644 --- a/include/vigra/multi_impex.hxx +++ b/include/vigra/multi_impex.hxx @@ -550,7 +550,7 @@ void VolumeImportInfo::importImpl(MultiArrayView <3, T, Stride> &volume) const // import the image ImageImportInfo info (filename.c_str ()); - // generate a basic image view to the current layer + // generate a 2D image view to the current layer MultiArrayView <2, T, Stride> view (volume.bindOuter (i)); vigra_precondition(view.shape() == info.shape(), "importVolume(): the images have inconsistent sizes."); diff --git a/src/impex/CMakeLists.txt b/src/impex/CMakeLists.txt index bef54a41d..1d4262e09 100644 --- a/src/impex/CMakeLists.txt +++ b/src/impex/CMakeLists.txt @@ -53,6 +53,7 @@ ADD_LIBRARY(vigraimpex ${LIBTYPE} hdf5_rf_impex.cxx iccjpeg.c imageinfo.cxx + multi_impex.cxx jpeg.cxx lz4.c png.cxx diff --git a/src/impex/imageinfo.cxx b/src/impex/imageinfo.cxx index 2420347a5..e0742a98f 100644 --- a/src/impex/imageinfo.cxx +++ b/src/impex/imageinfo.cxx @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -61,8 +62,6 @@ #include "vigra/array_vector.hxx" #include "vigra/imageinfo.hxx" #include "codecmanager.hxx" -#include "vigra/multi_impex.hxx" -#include "vigra/sifImport.hxx" #if defined(_MSC_VER) # include "vigra/windows.h" @@ -79,195 +78,6 @@ namespace vigra { -namespace detail -{ - -struct NumberCompare -{ - bool operator()(std::string const & l, std::string const & r) const - { - return atoi(l.c_str()) < atoi(r.c_str()); - } -}; - -bool splitString( - const std::string &s, char separator, std::string &a, std::string &b, - bool reverse = false) -{ - std::size_t splitPos = (reverse ? s.rfind(separator) : s.find(separator)); - if(splitPos >= s.size()) - return false; - a = std::string(s.begin(), s.begin() + splitPos); - b = std::string(s.begin() + splitPos + 1, s.end()); - return true; -} - -std::string trimString(const std::string &s) -{ - unsigned int begin = 0; - while(begin < s.size() && ((s[begin] == ' ') || (s[begin] == '\t'))) - ++begin; - std::size_t end = s.size(); - while(end > 0 && ((s[end-1] == ' ') || (s[end-1] == '\t'))) - --end; - return std::string(s.begin() + begin, s.begin() + end); -} - -} // namespace detail - - -// find filenames matching the pattern "/base[0-9]+ext" -#ifdef _MSC_VER -void splitPathFromFilename(const std::string &pathAndName, - std::string &path, std::string &name) -{ - // on Windows, both '/' and '\' are valid path separators - // note: std::basic_string.rfind() may return 'unsigned int', so explicitely cast to 'int' - int split = std::max(static_cast(pathAndName.rfind('/')), static_cast(pathAndName.rfind('\\'))); - if(split == static_cast(std::string::npos)) - { - path = "."; - name = pathAndName; - } - else - { - for(int i=0; i & numbers) -{ - // find out how many images we have - BOOL fFinished; - HANDLE hList; - TCHAR szDir[MAX_PATH+1]; - WIN32_FIND_DATA FileData; - - std::string path, base; - splitPathFromFilename(name_base, path, base); - - std::vector result; - char numbuf[21], extbuf[1024]; - std::string pattern = base + "%20[0-9]%1023s"; - - // Get the proper directory path - sprintf(szDir, "%s\\%s*%s", path.c_str(), base.c_str(), name_ext.c_str()); - - // Get the first file - hList = FindFirstFile(szDir, &FileData); - if (hList == INVALID_HANDLE_VALUE) - { - std::string message("importVolume(): No files matching '"); - message = message + szDir + "'."; - vigra_fail(message.c_str()); - } - else - { - // Traverse through the directory structure - fFinished = FALSE; - while (!fFinished) - { - if(sscanf(FileData.cFileName, pattern.c_str(), numbuf, extbuf) == 2) - { - if(strcmp(name_ext.c_str(), extbuf) == 0) - { - std::string num(numbuf); - std::string name = name_base + num + name_ext; - // skip matching files names that are not images - if(isImage(name.c_str())) - result.push_back(num); - } - } - if (!FindNextFile(hList, &FileData)) - { - if (GetLastError() == ERROR_NO_MORE_FILES) - { - fFinished = TRUE; - } - } - } - } - - FindClose(hList); - - std::sort(result.begin(), result.end(), detail::NumberCompare()); - numbers.swap(result); -} - -#else // not _MSC_VER - -void splitPathFromFilename(const std::string &pathAndName, - std::string &path, std::string &name) -{ - int split = pathAndName.rfind('/'); - if(split == -1) - { - path = "."; - name = pathAndName; - } - else - { - path.append(pathAndName, 0, split); - name.append(pathAndName, split+1, pathAndName.size() - split - 1); - } -} - -void findImageSequence(const std::string &name_base, - const std::string &name_ext, - std::vector & numbers) -{ - // find out how many images we have - std::string path, base; - splitPathFromFilename(name_base, path, base); - - DIR * dir = opendir(path.c_str()); - if(!dir) - { - std::string message("importVolume(): Unable to open directory '"); - message = message + path + "'."; - vigra_fail(message.c_str()); - } - - std::vector result; - dirent * dp; - errno = 0; - char numbuf[21], extbuf[1024]; - std::string pattern = base + "%20[0-9]%1023s"; - while ((dp = readdir(dir)) != NULL) - { - if(sscanf(dp->d_name, pattern.c_str(), numbuf, extbuf) == 2) - { - if(strcmp(name_ext.c_str(), extbuf) == 0) - { - std::string num(numbuf); - std::string name = name_base + num + name_ext; - // skip matching files names that are not images - if(isImage(name.c_str())) - result.push_back(num); - } - } - } - - closedir(dir); - - vigra_precondition(errno == 0, - "importVolume(): I/O error while searching for images."); - - std::sort(result.begin(), result.end(), detail::NumberCompare()); - numbers.swap(result); -} - -#endif // _MSC_VER - // build a string from a sequence. #if defined(_MSC_VER) && (_MSC_VER < 1300) template @@ -303,12 +113,6 @@ std::string stringify (const iterator &start, const iterator &end) #endif // _MSC_VER < 1300 -void validate_filetype( std::string filetype ) -{ - vigra_precondition( codecManager().fileTypeSupported(filetype), - "given file type is not supported" ); -} - std::string impexListFormats() { std::vector ft = codecManager().supportedFileTypes(); @@ -330,891 +134,4 @@ bool isImage(char const * filename) #endif } -// class ImageExportInfo - -ImageExportInfo::ImageExportInfo( const char * filename, const char * mode ) - : m_filename(filename), m_mode(mode), - m_x_res(0), m_y_res(0), - fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) -{} - -ImageExportInfo::~ImageExportInfo() -{ -} - -ImageExportInfo & ImageExportInfo::setFileType( const char * filetype ) -{ - m_filetype = filetype; - return *this; -} - -ImageExportInfo & ImageExportInfo::setForcedRangeMapping(double fromMin, double fromMax, - double toMin, double toMax) -{ - fromMin_ = fromMin; - fromMax_ = fromMax; - toMin_ = toMin; - toMax_ = toMax; - return *this; -} - -bool ImageExportInfo::hasForcedRangeMapping() const -{ - return (fromMax_ > fromMin_) || (toMax_ > toMin_); -} - -double ImageExportInfo::getFromMin() const -{ - return fromMin_; -} - -double ImageExportInfo::getFromMax() const -{ - return fromMax_; -} - -double ImageExportInfo::getToMin() const -{ - return toMin_; -} - -double ImageExportInfo::getToMax() const -{ - return toMax_; -} - -ImageExportInfo & ImageExportInfo::setCompression( const char * comp ) -{ - m_comp = comp; - return *this; -} - -ImageExportInfo & ImageExportInfo::setFileName(const char * name) -{ - m_filename = name; - return *this; -} - -const char * ImageExportInfo::getFileName() const -{ - return m_filename.c_str(); -} - -const char * ImageExportInfo::getMode() const -{ - return m_mode.c_str(); -} - -const char * ImageExportInfo::getFileType() const -{ - return m_filetype.c_str(); -} - -ImageExportInfo & ImageExportInfo::setPixelType( const char * s ) -{ - m_pixeltype = s; - return *this; -} - -const char * ImageExportInfo::getPixelType() const -{ - return m_pixeltype.c_str(); -} - -const char * ImageExportInfo::getCompression() const -{ - return m_comp.c_str(); -} - -float ImageExportInfo::getXResolution() const -{ - return m_x_res; -} - -float ImageExportInfo::getYResolution() const -{ - return m_y_res; -} - -ImageExportInfo & ImageExportInfo::setXResolution( float val ) -{ - m_x_res = val; - return *this; -} - -ImageExportInfo & ImageExportInfo::setYResolution( float val ) -{ - m_y_res = val; - return *this; -} - -ImageExportInfo & ImageExportInfo::setPosition(const vigra::Diff2D & pos) -{ - m_pos = pos; - return *this; -} - -vigra::Size2D ImageExportInfo::getCanvasSize() const -{ - return m_canvas_size ; -} - -ImageExportInfo & ImageExportInfo::setCanvasSize(const Size2D & size) -{ - m_canvas_size = size; - return *this; -} - -vigra::Diff2D ImageExportInfo::getPosition() const -{ - return m_pos; -} - -const ImageExportInfo::ICCProfile & ImageExportInfo::getICCProfile() const -{ - return m_icc_profile; -} - -ImageExportInfo & ImageExportInfo::setICCProfile( - const ImageExportInfo::ICCProfile &profile) -{ - m_icc_profile = profile; - return *this; -} - -// return an encoder for a given ImageExportInfo object -VIGRA_UNIQUE_PTR encoder( const ImageExportInfo & info ) -{ - VIGRA_UNIQUE_PTR enc; - - std::string filetype = info.getFileType(); - if ( filetype != "" ) { - validate_filetype(filetype); - VIGRA_UNIQUE_PTR enc2 = getEncoder( std::string( info.getFileName() ), filetype, std::string( info.getMode() ) ); - std::swap(enc, enc2); - } else { - VIGRA_UNIQUE_PTR enc2 = getEncoder( std::string( info.getFileName() ), "undefined", std::string( info.getMode() ) ); - std::swap(enc, enc2); - } - - std::string comp = info.getCompression(); - if ( comp != "" ) { - - // check for quality parameter of JPEG compression - int quality = 0; - - // possibility 1: quality specified as "JPEG QUALITY=N" or "JPEG-ARITH QUALITY=N" - // possibility 2 (deprecated): quality specified as just a number "10" - std::string sq(" QUALITY="), parsed_comp; - std::string::size_type pos = comp.rfind(sq), start = 0; - - if(pos != std::string::npos) - { - start = pos + sq.size(); - parsed_comp = comp.substr(0, pos); - } - - std::istringstream compstream(comp.substr(start)); - compstream >> quality; - if ( quality != 0 ) - { - if(parsed_comp == "") - parsed_comp = "JPEG"; - enc->setCompressionType( parsed_comp, quality ); - } - else - { - // leave any other compression type to the codec - enc->setCompressionType(comp); - } - } - - std::string pixel_type = info.getPixelType(); - if ( pixel_type != "" ) { - if(!isPixelTypeSupported( enc->getFileType(), pixel_type )) - { - std::string msg("exportImage(): file type "); - msg += enc->getFileType() + " does not support requested pixel type " - + pixel_type + "."; - vigra_precondition(false, msg.c_str()); - } - enc->setPixelType(pixel_type); - } - - // set other properties - enc->setXResolution(info.getXResolution()); - enc->setYResolution(info.getYResolution()); - enc->setPosition(info.getPosition()); - enc->setCanvasSize(info.getCanvasSize()); - - if ( info.getICCProfile().size() > 0 ) { - enc->setICCProfile(info.getICCProfile()); - } - - return enc; -} - -// class ImageImportInfo - -ImageImportInfo::ImageImportInfo( const char * filename, unsigned int imageIndex ) - : m_filename(filename), m_image_index(imageIndex) -{ - readHeader_(); -} - -ImageImportInfo::~ImageImportInfo() { -} - -const char * ImageImportInfo::getFileName() const -{ - return m_filename.c_str(); -} - -const char * ImageImportInfo::getFileType() const -{ - return m_filetype.c_str(); -} - -const char * ImageImportInfo::getPixelType() const -{ - return m_pixeltype.c_str(); -} - -ImageImportInfo::PixelType ImageImportInfo::pixelType() const -{ - const std::string pixeltype=ImageImportInfo::getPixelType(); - if (pixeltype == "UINT8") - return UINT8; - if (pixeltype == "INT16") - return INT16; - if (pixeltype == "UINT16") - return UINT16; - if (pixeltype == "INT32") - return INT32; - if (pixeltype == "UINT32") - return UINT32; - if (pixeltype == "FLOAT") - return FLOAT; - if (pixeltype == "DOUBLE") - return DOUBLE; - vigra_fail( "internal error: unknown pixel type" ); - return ImageImportInfo::PixelType(); -} - -int ImageImportInfo::width() const -{ - return m_width; -} - -int ImageImportInfo::height() const -{ - return m_height; -} - -int ImageImportInfo::numBands() const -{ - return m_num_bands; -} - -int ImageImportInfo::numExtraBands() const -{ - return m_num_extra_bands; -} - -int ImageImportInfo::numImages() const -{ - return m_num_images; -} - -void ImageImportInfo::setImageIndex(int index) -{ - m_image_index = index; - readHeader_(); -} - -int ImageImportInfo::getImageIndex() const -{ - return m_image_index; -} - -Size2D ImageImportInfo::size() const -{ - return Size2D( m_width, m_height ); -} - -MultiArrayShape<2>::type ImageImportInfo::shape() const -{ - return MultiArrayShape<2>::type( m_width, m_height ); -} - -bool ImageImportInfo::isGrayscale() const -{ - return (m_num_bands - m_num_extra_bands) == 1; -} - -bool ImageImportInfo::isColor() const -{ - return (m_num_bands - m_num_extra_bands) == 3; -} - -bool ImageImportInfo::isByte() const -{ - return m_pixeltype == "UINT8"; -} - -Diff2D ImageImportInfo::getPosition() const -{ - return m_pos; -} - -Size2D ImageImportInfo::getCanvasSize() const -{ - return m_canvas_size; -} - -float ImageImportInfo::getXResolution() const -{ - return m_x_res; -} - -float ImageImportInfo::getYResolution() const -{ - return m_y_res; -} - -const ImageImportInfo::ICCProfile & ImageImportInfo::getICCProfile() const -{ - return m_icc_profile; -} - -void ImageImportInfo::readHeader_() -{ - VIGRA_UNIQUE_PTR decoder = getDecoder(m_filename, "undefined", m_image_index); - m_num_images = decoder->getNumImages(); - - m_filetype = decoder->getFileType(); - m_pixeltype = decoder->getPixelType(); - m_width = decoder->getWidth(); - m_height = decoder->getHeight(); - m_num_bands = decoder->getNumBands(); - m_num_extra_bands = decoder->getNumExtraBands(); - m_pos = decoder->getPosition(); - m_canvas_size = decoder->getCanvasSize(); - m_x_res = decoder->getXResolution(); - m_y_res = decoder->getYResolution(); - - m_icc_profile = decoder->getICCProfile(); - - decoder->abort(); // there probably is no better way than this -} - -// return a decoder for a given ImageImportInfo object -VIGRA_UNIQUE_PTR decoder( const ImageImportInfo & info ) -{ - std::string filetype = info.getFileType(); - validate_filetype(filetype); - return getDecoder( std::string( info.getFileName() ), filetype, info.getImageIndex() ); -} - -// class VolumeExportInfo - -VolumeExportInfo::VolumeExportInfo( const char * filename ) : - m_x_res(0), m_y_res(0), m_z_res(0), - m_filetype("MULTIPAGE"), - m_filename_base(filename), m_filename_ext(".tif"), - fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) -{ -} - -VolumeExportInfo::VolumeExportInfo( const char * name_base, const char * name_ext ) : - m_x_res(0), m_y_res(0), m_z_res(0), - m_filename_base(name_base), m_filename_ext(name_ext), - fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) -{ - if(m_filename_ext == "") - { - m_filename_ext = ".tif"; - m_filetype = "MULTIPAGE"; - } -} - -VolumeExportInfo::~VolumeExportInfo() -{ -} - -VolumeExportInfo & VolumeExportInfo::setFileType( const char * filetype ) -{ - m_filetype = filetype; - return *this; -} - -VolumeExportInfo & VolumeExportInfo::setForcedRangeMapping(double fromMin, double fromMax, - double toMin, double toMax) -{ - fromMin_ = fromMin; - fromMax_ = fromMax; - toMin_ = toMin; - toMax_ = toMax; - return *this; -} - -bool VolumeExportInfo::hasForcedRangeMapping() const -{ - return (fromMax_ > fromMin_) || (toMax_ > toMin_); -} - -double VolumeExportInfo::getFromMin() const -{ - return fromMin_; -} - -double VolumeExportInfo::getFromMax() const -{ - return fromMax_; -} - -double VolumeExportInfo::getToMin() const -{ - return toMin_; -} - -double VolumeExportInfo::getToMax() const -{ - return toMax_; -} - -VolumeExportInfo & VolumeExportInfo::setCompression( const char * comp ) -{ - m_comp = comp; - return *this; -} - -VolumeExportInfo & VolumeExportInfo::setFileNameExt(const char * name_ext) -{ - m_filename_ext = name_ext; - return *this; -} - -const char * VolumeExportInfo::getFileNameExt() const -{ - return m_filename_ext.c_str(); -} - -VolumeExportInfo & VolumeExportInfo::setFileNameBase(const char * name_base) -{ - m_filename_base = name_base; - return *this; -} - -const char * VolumeExportInfo::getFileNameBase() const -{ - return m_filename_base.c_str(); -} - -const char * VolumeExportInfo::getFileType() const -{ - return m_filetype.c_str(); -} - -VolumeExportInfo & VolumeExportInfo::setPixelType( const char * s ) -{ - m_pixeltype = s; - return *this; -} - -const char * VolumeExportInfo::getPixelType() const -{ - return m_pixeltype.c_str(); -} - -const char * VolumeExportInfo::getCompression() const -{ - return m_comp.c_str(); -} - -float VolumeExportInfo::getXResolution() const -{ - return m_x_res; -} - -float VolumeExportInfo::getYResolution() const -{ - return m_y_res; -} - -VolumeExportInfo & VolumeExportInfo::setXResolution( float val ) -{ - m_x_res = val; - return *this; -} - -VolumeExportInfo & VolumeExportInfo::setYResolution( float val ) -{ - m_y_res = val; - return *this; -} - -VolumeExportInfo & VolumeExportInfo::setZResolution( float val ) -{ - m_z_res = val; - return *this; -} - -VolumeExportInfo & VolumeExportInfo::setPosition(const vigra::Diff2D & pos) -{ - m_pos = pos; - return *this; -} - -vigra::Diff2D VolumeExportInfo::getPosition() const -{ - return m_pos; -} - -const VolumeExportInfo::ICCProfile & VolumeExportInfo::getICCProfile() const -{ - return m_icc_profile; -} - -VolumeExportInfo & VolumeExportInfo::setICCProfile( - const VolumeExportInfo::ICCProfile &profile) -{ - m_icc_profile = profile; - return *this; -} - -VolumeImportInfo::VolumeImportInfo(const std::string &filename) -: shape_(0, 0, 0), - resolution_(1.f, 1.f, 1.f), - numBands_(0) -{ - std::string message; - -#if 0 // defined(HasHDF5) - // Deactivate this code because it effectively forces multi_impex.hxx to be compiled with HDF5 only. - - // try reading from HDF5 - // (do this first because it uses mangled 'filename/dataset_name' format) - { - // split the filename into external (true filename) and internal (dataset name) part - // FIXME: at present, the delimiter must be the extension '.h5' or '.hdf5' - define a more robust rule - std::string name, datasetName; - std::size_t ext = filename.rfind(".h5"); - if(ext != std::string::npos) - { - name = filename.substr(0, ext+3); - datasetName = filename.substr(ext+3); - } - else - { - ext = filename.rfind(".hdf5"); - if(ext != std::string::npos) - { - name = filename.substr(0, ext+5); - datasetName = filename.substr(ext+5); - } - else - { - name = filename; - } - } - - if(H5Fis_hdf5(name.c_str())) - { - message = std::string("VolumeImportInfo(): File '"); - message += name + "' is HDF5, but filename doesn't contain an internal dataset path."; - vigra_precondition(datasetName.size() > 0, message.c_str()); - - HDF5File hdf5file(name, HDF5File::OpenReadOnly); - - message = std::string("VolumeImportInfo(): Dataset '"); - message += datasetName + "' not found in HDF5 file '" + name + "'."; - vigra_precondition(hdf5file.existsDataset(datasetName), message.c_str()); - - ArrayVector shape(hdf5file.getDatasetShape(datasetName)); - message = std::string("VolumeImportInfo(): Dataset '"); - message += datasetName + "' in HDF5 file '" + name + "' is not a volume."; - vigra_precondition(shape.size() == 3 || shape.size() == 4, message.c_str()); - - shape_[0] = shape[0]; - shape_[1] = shape[1]; - shape_[2] = shape[2]; - pixelType_ = hdf5file.getDatasetType(datasetName); - numBands_ = shape.size() == 4 - ? shape[3] - : 1; - baseName_ = name; - extension_ = datasetName; - fileType_ = "HDF5"; - return; - } - } -#endif // HasHDF5 - - // check if file exists - message = std::string("VolumeImportInfo(): File '"); - message += filename + "' not found."; -#ifdef _MSC_VER - vigra_precondition(_access(filename.c_str(), 0) != -1, message.c_str()); -#else - vigra_precondition(access(filename.c_str(), F_OK) == 0, message.c_str()); -#endif - - // try Andor SIF format - { - std::string magic_string; - { - std::ifstream siffile (filename.c_str()); - if( !siffile.is_open() ) - { - message = std::string("VolumeImportInfo(): Unable to open file '"); - message += filename + "'."; - vigra_precondition(false, message.c_str()); - } - - getline(siffile, magic_string); - } - if(magic_string == "Andor Technology Multi-Channel File") - { - SIFImportInfo info(filename.c_str()); - shape_[0] = info.shapeOfDimension(0); - shape_[1] = info.shapeOfDimension(1); - shape_[2] = info.shapeOfDimension(2); - pixelType_ = "FLOAT"; - numBands_ = 1; - baseName_ = filename; - fileType_ = "SIF"; - return; - } - } - - // try multi-page TIFF or image stack - if(isImage(filename.c_str())) - { - ImageImportInfo info(filename.c_str()); - shape_[0] = info.width(); - shape_[1] = info.height(); - resolution_[1] = -1.f; // assume images to be right-handed - pixelType_ = info.getPixelType(); - numBands_ = info.numBands(); - - if(info.numImages() > 1) - { - // must be a multi-page TIFF - splitPathFromFilename(filename, path_, name_); - baseName_ = filename; - fileType_ = "MULTIPAGE"; - shape_[2] = info.numImages(); - return; - } - else - { - // try image stack loading - std::string::const_reverse_iterator - numBeginIt(filename.rbegin()), numEndIt(numBeginIt); - - do - { - numEndIt = std::find_if(numBeginIt, filename.rend(),(int (*)(int)) &isdigit); - numBeginIt = std::find_if(numEndIt, filename.rend(), not1(std::ptr_fun((int (*)(int))&isdigit))); - - if(numEndIt != filename.rend()) - { - std::string - baseName(filename.begin(), - filename.begin() + (filename.rend()-numBeginIt)), - extension(filename.begin() + (filename.rend()-numEndIt), - filename.end()); - - std::vector numbers; - - findImageSequence(baseName, extension, numbers); - if(numbers.size() > 0) - { - splitPathFromFilename(baseName, path_, name_); - baseName_ = baseName; - extension_ = extension; - shape_[2] = numbers.size(); - std::swap(numbers, numbers_); - fileType_ = "STACK"; - return; - } - } - } - while(numEndIt != filename.rend()); - } - - message = std::string("VolumeImportInfo(): File '"); - message += filename + "' is neither a multi-page TIFF nor a valid image stack."; - vigra_precondition(false, message.c_str()); - } - - { - static std::string pixelTypes[] = { std::string("UNSIGNED_CHAR"), - std::string("UNSIGNED_BYTE"), - std::string("UINT8"), - std::string("INT16"), - std::string("UINT16"), - std::string("INT32"), - std::string("UINT32"), - std::string("FLOAT"), - std::string("DOUBLE"), - std::string() }; - - // try .info file loading - std::ifstream stream(filename.c_str()); - - while(stream.good()) - { - char rawline[1024]; - stream.getline(rawline, 1024); - - // split off comments starting with '#': - std::string line, comment; - if(!detail::splitString(rawline, '#', line, comment)) - line = rawline; - - std::string key, value; - if(detail::splitString(line, '=', key, value)) - { - key = detail::trimString(key); - value = detail::trimString(value); - - if(key == "width") - shape_[0] = atoi(value.c_str()); - else if(key == "height") - shape_[1] = atoi(value.c_str()); - else if(key == "depth") - shape_[2] = atoi(value.c_str()); - else if(key == "datatype") - { - std::string * type = pixelTypes; - while(*type != "") - { - if(*type == value) - { - pixelType_ = value; - break; - } - ++type; - } - vigra_precondition(*type != "", - "VolumeImportInfo(): Invalid datatype '" + value +"' in .info file."); - if(pixelType_ == "UNSIGNED_CHAR" || pixelType_ == "UNSIGNED_BYTE") - pixelType_ = "UINT8"; - } - else if(key == "description") - description_ = value; - else if(key == "name") - name_ = value; - else if(key == "filename") - rawFilename_ = value; - else - { - std::cerr << "VolumeImportInfo(): WARNING: Unknown key '" << key - << "' (value '" << value << "') in info file!\n"; - } - } - else - { - if(line[0]) // non-empty line? - std::cerr << "VolumeImportInfo(): WARNING: could not parse line '" << line << "'!\n"; - } - } - - if((shape_[0]*shape_[1]*shape_[2] > 0) && (rawFilename_.size() > 0)) - { - numBands_ = 1; - - baseName_ = filename; - if(name_.size() > 0) - { - std::string nameDummy; - splitPathFromFilename(baseName_, path_, nameDummy); - } - else - { - splitPathFromFilename(baseName_, path_, name_); - } - fileType_ = "RAW"; - return; - } - } - - message = std::string("VolumeImportInfo(): Unable to load file '"); - message += filename + "' - not a recognized format."; - vigra_precondition(false, message.c_str()); -} - -VolumeImportInfo::VolumeImportInfo(const std::string &baseName, const std::string &extension) -: shape_(0, 0, 0), - resolution_(1.f, 1.f, 1.f), - numBands_(0) -{ - std::vector numbers; - findImageSequence(baseName, extension, numbers); - - std::string message("VolumeImportInfo(): No files matching '"); - message += baseName + "[0-9]+" + extension + "' found."; - vigra_precondition(numbers.size() > 0, message.c_str()); - - getVolumeInfoFromFirstSlice(baseName + numbers[0] + extension); - - splitPathFromFilename(baseName, path_, name_); - baseName_ = baseName; - extension_ = extension; - shape_[2] = numbers.size(); - std::swap(numbers, numbers_); - fileType_ = "STACK"; -} - -void VolumeImportInfo::getVolumeInfoFromFirstSlice(const std::string &filename) -{ - ImageImportInfo info(filename.c_str()); - shape_[0] = info.width(); - shape_[1] = info.height(); - resolution_[1] = -1.f; // assume images to be right-handed - pixelType_ = info.pixelType(); - numBands_ = info.numBands(); -} - -VolumeImportInfo::ShapeType VolumeImportInfo::shape() const { return shape_; } -VolumeImportInfo::Resolution VolumeImportInfo::resolution() const { return resolution_; } -VolumeImportInfo::PixelType VolumeImportInfo::pixelType() const -{ - const std::string pixeltype=VolumeImportInfo::getPixelType(); - if (pixeltype == "UINT8") - return ImageImportInfo::UINT8; - if (pixeltype == "INT16") - return ImageImportInfo::INT16; - if (pixeltype == "UINT16") - return ImageImportInfo::UINT16; - if (pixeltype == "INT32") - return ImageImportInfo::INT32; - if (pixeltype == "UINT32") - return ImageImportInfo::UINT32; - if (pixeltype == "FLOAT") - return ImageImportInfo::FLOAT; - if (pixeltype == "DOUBLE") - return ImageImportInfo::DOUBLE; - vigra_fail( "internal error: unknown pixel type" ); - return VolumeImportInfo::PixelType(); -} -const char * VolumeImportInfo::getPixelType() const -{ - return pixelType_.c_str(); -} -const char * VolumeImportInfo::getFileType() const -{ - return fileType_.c_str(); -} -MultiArrayIndex VolumeImportInfo::numBands() const { return numBands_; } -bool VolumeImportInfo::isGrayscale() const { return numBands_ == 1; } -bool VolumeImportInfo::isColor() const { return numBands_ > 1; } -MultiArrayIndex VolumeImportInfo::width() const { return shape_[0]; } -MultiArrayIndex VolumeImportInfo::height() const { return shape_[1]; } -MultiArrayIndex VolumeImportInfo::depth() const { return shape_[2]; } -const std::string & VolumeImportInfo::name() const { return name_; } -const std::string & VolumeImportInfo::description() const { return description_; } - } // namespace vigra diff --git a/src/impex/multi_impex.cxx b/src/impex/multi_impex.cxx new file mode 100644 index 000000000..9719913df --- /dev/null +++ b/src/impex/multi_impex.cxx @@ -0,0 +1,543 @@ +#include "vigra/multi_impex.hxx" + +#include +#include +#include +#include +#include +#include + +namespace vigra +{ + +namespace detail +{ + +struct NumberCompare +{ + bool operator()(std::string const & l, std::string const & r) const + { + return atoi(l.c_str()) < atoi(r.c_str()); + } +}; + +bool splitString( + const std::string &s, char separator, std::string &a, std::string &b, + bool reverse = false) +{ + std::size_t splitPos = (reverse ? s.rfind(separator) : s.find(separator)); + if(splitPos >= s.size()) + return false; + a = std::string(s.begin(), s.begin() + splitPos); + b = std::string(s.begin() + splitPos + 1, s.end()); + return true; +} + +std::string trimString(const std::string &s) +{ + unsigned int begin = 0; + while(begin < s.size() && ((s[begin] == ' ') || (s[begin] == '\t'))) + ++begin; + std::size_t end = s.size(); + while(end > 0 && ((s[end-1] == ' ') || (s[end-1] == '\t'))) + --end; + return std::string(s.begin() + begin, s.begin() + end); +} + +} // namespace detail + + +// find filenames matching the pattern "/base[0-9]+ext" +#ifdef _WIN32 +void splitPathFromFilename(const std::string &pathAndName, + std::string &path, std::string &name) +{ + // on Windows, both '/' and '\' are valid path separators + // note: std::basic_string.rfind() may return 'unsigned int', so explicitely cast to 'int' + int split = std::max(static_cast(pathAndName.rfind('/')), static_cast(pathAndName.rfind('\\'))); + if(split == static_cast(std::string::npos)) + { + path = "."; + name = pathAndName; + } + else + { + for(int i=0; i & numbers) +{ + // find out how many images we have + BOOL fFinished; + HANDLE hList; + TCHAR szDir[MAX_PATH+1]; + WIN32_FIND_DATA FileData; + + std::string path, base; + splitPathFromFilename(name_base, path, base); + + std::vector result; + char numbuf[21], extbuf[1024]; + std::string pattern = base + "%20[0-9]%1023s"; + + // Get the proper directory path + sprintf(szDir, "%s\\%s*%s", path.c_str(), base.c_str(), name_ext.c_str()); + + // Get the first file + hList = FindFirstFile(szDir, &FileData); + if (hList == INVALID_HANDLE_VALUE) + { + std::string message("importVolume(): No files matching '"); + message = message + szDir + "'."; + vigra_fail(message.c_str()); + } + else + { + // Traverse through the directory structure + fFinished = FALSE; + while (!fFinished) + { + if(sscanf(FileData.cFileName, pattern.c_str(), numbuf, extbuf) == 2) + { + if(strcmp(name_ext.c_str(), extbuf) == 0) + { + std::string num(numbuf); + std::string name = name_base + num + name_ext; + // skip matching files names that are not images + if(isImage(name.c_str())) + result.push_back(num); + } + } + if (!FindNextFile(hList, &FileData)) + { + if (GetLastError() == ERROR_NO_MORE_FILES) + { + fFinished = TRUE; + } + } + } + } + + FindClose(hList); + + std::sort(result.begin(), result.end(), detail::NumberCompare()); + numbers.swap(result); +} + +#else // _WIN32 + +void splitPathFromFilename(const std::string &pathAndName, + std::string &path, std::string &name) +{ + int split = pathAndName.rfind('/'); + if(split == -1) + { + path = "."; + name = pathAndName; + } + else + { + path.append(pathAndName, 0, split); + name.append(pathAndName, split+1, pathAndName.size() - split - 1); + } +} + +void findImageSequence(const std::string &name_base, + const std::string &name_ext, + std::vector & numbers) +{ + // find out how many images we have + std::string path, base; + splitPathFromFilename(name_base, path, base); + + DIR * dir = opendir(path.c_str()); + if(!dir) + { + std::string message("importVolume(): Unable to open directory '"); + message = message + path + "'."; + vigra_fail(message.c_str()); + } + + std::vector result; + dirent * dp; + errno = 0; + char numbuf[21], extbuf[1024]; + std::string pattern = base + "%20[0-9]%1023s"; + while ((dp = readdir(dir)) != NULL) + { + if(sscanf(dp->d_name, pattern.c_str(), numbuf, extbuf) == 2) + { + if(strcmp(name_ext.c_str(), extbuf) == 0) + { + std::string num(numbuf); + std::string name = name_base + num + name_ext; + // skip matching files names that are not images + if(isImage(name.c_str())) + result.push_back(num); + } + } + } + + closedir(dir); + + vigra_precondition(errno == 0, + "importVolume(): I/O error while searching for images."); + + std::sort(result.begin(), result.end(), detail::NumberCompare()); + numbers.swap(result); +} + +#endif // _WIN32 + +// class VolumeExportInfo + +VolumeExportInfo::VolumeExportInfo( const char * name_base, const char * name_ext ) : + m_x_res(0), m_y_res(0), m_z_res(0), + m_filename_base(name_base), m_filename_ext(name_ext), + fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) +{ +} + +VolumeExportInfo::~VolumeExportInfo() +{ +} + +VolumeExportInfo & VolumeExportInfo::setFileType( const char * filetype ) +{ + m_filetype = filetype; + return *this; +} + +VolumeExportInfo & VolumeExportInfo::setForcedRangeMapping(double fromMin, double fromMax, + double toMin, double toMax) +{ + fromMin_ = fromMin; + fromMax_ = fromMax; + toMin_ = toMin; + toMax_ = toMax; + return *this; +} + +bool VolumeExportInfo::hasForcedRangeMapping() const +{ + return (fromMax_ > fromMin_) || (toMax_ > toMin_); +} + +double VolumeExportInfo::getFromMin() const +{ + return fromMin_; +} + +double VolumeExportInfo::getFromMax() const +{ + return fromMax_; +} + +double VolumeExportInfo::getToMin() const +{ + return toMin_; +} + +double VolumeExportInfo::getToMax() const +{ + return toMax_; +} + +VolumeExportInfo & VolumeExportInfo::setCompression( const char * comp ) +{ + m_comp = comp; + return *this; +} + +VolumeExportInfo & VolumeExportInfo::setFileNameExt(const char * name_ext) +{ + m_filename_ext = name_ext; + return *this; +} + +const char * VolumeExportInfo::getFileNameExt() const +{ + return m_filename_ext.c_str(); +} + +VolumeExportInfo & VolumeExportInfo::setFileNameBase(const char * name_base) +{ + m_filename_base = name_base; + return *this; +} + +const char * VolumeExportInfo::getFileNameBase() const +{ + return m_filename_base.c_str(); +} + +const char * VolumeExportInfo::getFileType() const +{ + return m_filetype.c_str(); +} + +VolumeExportInfo & VolumeExportInfo::setPixelType( const char * s ) +{ + m_pixeltype = s; + return *this; +} + +const char * VolumeExportInfo::getPixelType() const +{ + return m_pixeltype.c_str(); +} + +const char * VolumeExportInfo::getCompression() const +{ + return m_comp.c_str(); +} + +float VolumeExportInfo::getXResolution() const +{ + return m_x_res; +} + +float VolumeExportInfo::getYResolution() const +{ + return m_y_res; +} + +VolumeExportInfo & VolumeExportInfo::setXResolution( float val ) +{ + m_x_res = val; + return *this; +} + +VolumeExportInfo & VolumeExportInfo::setYResolution( float val ) +{ + m_y_res = val; + return *this; +} + +VolumeExportInfo & VolumeExportInfo::setZResolution( float val ) +{ + m_z_res = val; + return *this; +} + +VolumeExportInfo & VolumeExportInfo::setPosition(const vigra::Diff2D & pos) +{ + m_pos = pos; + return *this; +} + +vigra::Diff2D VolumeExportInfo::getPosition() const +{ + return m_pos; +} + +const VolumeExportInfo::ICCProfile & VolumeExportInfo::getICCProfile() const +{ + return m_icc_profile; +} + +VolumeExportInfo & VolumeExportInfo::setICCProfile( + const VolumeExportInfo::ICCProfile &profile) +{ + m_icc_profile = profile; + return *this; +} + +VolumeImportInfo::VolumeImportInfo(const std::string &filename) +: shape_(0, 0, 0), + resolution_(1.f, 1.f, 1.f), + numBands_(0) +{ + // first try image sequence loading + std::string::const_reverse_iterator + numBeginIt(filename.rbegin()), numEndIt(numBeginIt); + + do + { + numEndIt = std::find_if(numBeginIt, filename.rend(),(int (*)(int)) &isdigit); + numBeginIt = std::find_if(numEndIt, filename.rend(), not1(std::ptr_fun((int (*)(int))&isdigit))); + + if(numEndIt != filename.rend()) + { + std::string + baseName(filename.begin(), + filename.begin() + (filename.rend()-numBeginIt)), + extension(filename.begin() + (filename.rend()-numEndIt), + filename.end()); + + std::vector numbers; + + findImageSequence(baseName, extension, numbers); + if(numbers.size() > 0) + { + getVolumeInfoFromFirstSlice(baseName + numbers[0] + extension); + splitPathFromFilename(baseName, path_, name_); + baseName_ = baseName; + extension_ = extension; + shape_[2] = numbers.size(); + std::swap(numbers, numbers_); + + break; + } + } + } + while(numEndIt != filename.rend()); + + // no numbered images found, try .info file loading + if(!numbers_.size()) + { + std::ifstream stream(filename.c_str()); + + while(stream.good()) + { + char rawline[1024]; + stream.getline(rawline, 1024); + + // split off comments starting with '#': + std::string line, comment; + if(!detail::splitString(rawline, '#', line, comment)) + line = rawline; + + std::string key, value; + if(detail::splitString(line, '=', key, value)) + { + key = detail::trimString(key); + value = detail::trimString(value); + + if(key == "width") + shape_[0] = atoi(value.c_str()); + else if(key == "height") + shape_[1] = atoi(value.c_str()); + else if(key == "depth") + shape_[2] = atoi(value.c_str()); + else if(key == "datatype") + { + // FUTURE: store bit depth / signedness + if((value == "UNSIGNED_CHAR") || (value == "UNSIGNED_BYTE")) + numBands_ = 1; + else + { + std::cerr << "Unknown datatype '" << value << "'!\n"; + break; + } + } + else if(key == "description") + description_ = value; + else if(key == "name") + name_ = value; + else if(key == "filename") + rawFilename_ = value; + else + { + std::cerr << "WARNING: Unknown key '" << key + << "' (value '" << value << "') in info file!\n"; + } + } + else + { + if(line[0]) // non-empty line? + std::cerr << "WARNING: could not parse line '" << line << "'!\n"; + } + } + + if((shape_[0]*shape_[1]*shape_[2] > 0) && (rawFilename_.size() > 0)) + { + if(!numBands_) + numBands_ = 1; // default to UNSIGNED_CHAR datatype + + baseName_ = filename; + if(name_.size() > 0) + { + std::string nameDummy; + splitPathFromFilename(baseName_, path_, nameDummy); + } + else + { + splitPathFromFilename(baseName_, path_, name_); + } + return; + } + + std::string message("VolumeImportInfo(): Unable to load volume '"); + message += filename + "'."; + vigra_fail(message.c_str()); + } +} + +VolumeImportInfo::VolumeImportInfo(const std::string &baseName, const std::string &extension) +: shape_(0, 0, 0), + resolution_(1.f, 1.f, 1.f), + numBands_(0) +{ + std::vector numbers; + findImageSequence(baseName, extension, numbers); + + std::string message("VolumeImportInfo(): No files matching '"); + message += baseName + "[0-9]+" + extension + "' found."; + vigra_precondition(numbers.size() > 0, message.c_str()); + + getVolumeInfoFromFirstSlice(baseName + numbers[0] + extension); + + splitPathFromFilename(baseName, path_, name_); + baseName_ = baseName; + extension_ = extension; + shape_[2] = numbers.size(); + std::swap(numbers, numbers_); +} + +void VolumeImportInfo::getVolumeInfoFromFirstSlice(const std::string &filename) +{ + ImageImportInfo info(filename.c_str()); + shape_[0] = info.width(); + shape_[1] = info.height(); + resolution_[1] = -1.f; // assume images to be right-handed + pixelType_ = info.pixelType(); + numBands_ = info.numBands(); +} + +VolumeImportInfo::ShapeType VolumeImportInfo::shape() const { return shape_; } +VolumeImportInfo::Resolution VolumeImportInfo::resolution() const { return resolution_; } +VolumeImportInfo::PixelType VolumeImportInfo::pixelType() const +{ + const std::string pixeltype=VolumeImportInfo::getPixelType(); + if (pixeltype == "UINT8") + return ImageImportInfo::UINT8; + if (pixeltype == "INT16") + return ImageImportInfo::INT16; + if (pixeltype == "UINT16") + return ImageImportInfo::UINT16; + if (pixeltype == "INT32") + return ImageImportInfo::INT32; + if (pixeltype == "UINT32") + return ImageImportInfo::UINT32; + if (pixeltype == "FLOAT") + return ImageImportInfo::FLOAT; + if (pixeltype == "DOUBLE") + return ImageImportInfo::DOUBLE; + vigra_fail( "internal error: unknown pixel type" ); + return VolumeImportInfo::PixelType(); +} +const char * VolumeImportInfo::getPixelType() const +{ + return pixelType_.c_str(); +} +MultiArrayIndex VolumeImportInfo::numBands() const { return numBands_; } +bool VolumeImportInfo::isGrayscale() const { return numBands_ == 1; } +bool VolumeImportInfo::isColor() const { return numBands_ > 1; } +MultiArrayIndex VolumeImportInfo::width() const { return shape_[0]; } +MultiArrayIndex VolumeImportInfo::height() const { return shape_[1]; } +MultiArrayIndex VolumeImportInfo::depth() const { return shape_[2]; } +const std::string & VolumeImportInfo::name() const { return name_; } +const std::string & VolumeImportInfo::description() const { return description_; } + +} // namespace vigra From 6bdd788bac789e70c23cdf28a656928724c5e06a Mon Sep 17 00:00:00 2001 From: Hans Meine Date: Mon, 12 Jan 2015 19:22:49 +0100 Subject: [PATCH 2/4] merge 40 months of changesets into multi_impex.cxx --- src/impex/multi_impex.cxx | 647 +++++++++++++++++++++++++++++++++++--- 1 file changed, 600 insertions(+), 47 deletions(-) diff --git a/src/impex/multi_impex.cxx b/src/impex/multi_impex.cxx index 9719913df..81c4b6a48 100644 --- a/src/impex/multi_impex.cxx +++ b/src/impex/multi_impex.cxx @@ -1,4 +1,5 @@ #include "vigra/multi_impex.hxx" +#include "codecmanager.hxx" #include #include @@ -48,7 +49,7 @@ std::string trimString(const std::string &s) // find filenames matching the pattern "/base[0-9]+ext" -#ifdef _WIN32 +#ifdef _MSC_VER void splitPathFromFilename(const std::string &pathAndName, std::string &path, std::string &name) { @@ -134,7 +135,7 @@ VIGRA_EXPORT void findImageSequence(const std::string &name_base, numbers.swap(result); } -#else // _WIN32 +#else // not _MSC_VER void splitPathFromFilename(const std::string &pathAndName, std::string &path, std::string &name) @@ -197,15 +198,420 @@ void findImageSequence(const std::string &name_base, numbers.swap(result); } -#endif // _WIN32 +#endif // _MSC_VER + +void validate_filetype( std::string filetype ) +{ + vigra_precondition( codecManager().fileTypeSupported(filetype), + "given file type is not supported" ); +} + +// class ImageExportInfo + +ImageExportInfo::ImageExportInfo( const char * filename, const char * mode ) + : m_filename(filename), m_mode(mode), + m_x_res(0), m_y_res(0), + fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) +{} + +ImageExportInfo::~ImageExportInfo() +{ +} + +ImageExportInfo & ImageExportInfo::setFileType( const char * filetype ) +{ + m_filetype = filetype; + return *this; +} + +ImageExportInfo & ImageExportInfo::setForcedRangeMapping(double fromMin, double fromMax, + double toMin, double toMax) +{ + fromMin_ = fromMin; + fromMax_ = fromMax; + toMin_ = toMin; + toMax_ = toMax; + return *this; +} + +bool ImageExportInfo::hasForcedRangeMapping() const +{ + return (fromMax_ > fromMin_) || (toMax_ > toMin_); +} + +double ImageExportInfo::getFromMin() const +{ + return fromMin_; +} + +double ImageExportInfo::getFromMax() const +{ + return fromMax_; +} + +double ImageExportInfo::getToMin() const +{ + return toMin_; +} + +double ImageExportInfo::getToMax() const +{ + return toMax_; +} + +ImageExportInfo & ImageExportInfo::setCompression( const char * comp ) +{ + m_comp = comp; + return *this; +} + +ImageExportInfo & ImageExportInfo::setFileName(const char * name) +{ + m_filename = name; + return *this; +} + +const char * ImageExportInfo::getFileName() const +{ + return m_filename.c_str(); +} + +const char * ImageExportInfo::getMode() const +{ + return m_mode.c_str(); +} + +const char * ImageExportInfo::getFileType() const +{ + return m_filetype.c_str(); +} + +ImageExportInfo & ImageExportInfo::setPixelType( const char * s ) +{ + m_pixeltype = s; + return *this; +} + +const char * ImageExportInfo::getPixelType() const +{ + return m_pixeltype.c_str(); +} + +const char * ImageExportInfo::getCompression() const +{ + return m_comp.c_str(); +} + +float ImageExportInfo::getXResolution() const +{ + return m_x_res; +} + +float ImageExportInfo::getYResolution() const +{ + return m_y_res; +} + +ImageExportInfo & ImageExportInfo::setXResolution( float val ) +{ + m_x_res = val; + return *this; +} + +ImageExportInfo & ImageExportInfo::setYResolution( float val ) +{ + m_y_res = val; + return *this; +} + +ImageExportInfo & ImageExportInfo::setPosition(const vigra::Diff2D & pos) +{ + m_pos = pos; + return *this; +} + +vigra::Size2D ImageExportInfo::getCanvasSize() const +{ + return m_canvas_size ; +} + +ImageExportInfo & ImageExportInfo::setCanvasSize(const Size2D & size) +{ + m_canvas_size = size; + return *this; +} + +vigra::Diff2D ImageExportInfo::getPosition() const +{ + return m_pos; +} + +const ImageExportInfo::ICCProfile & ImageExportInfo::getICCProfile() const +{ + return m_icc_profile; +} + +ImageExportInfo & ImageExportInfo::setICCProfile( + const ImageExportInfo::ICCProfile &profile) +{ + m_icc_profile = profile; + return *this; +} + +// return an encoder for a given ImageExportInfo object +VIGRA_UNIQUE_PTR encoder( const ImageExportInfo & info ) +{ + VIGRA_UNIQUE_PTR enc; + + std::string filetype = info.getFileType(); + if ( filetype != "" ) { + validate_filetype(filetype); + VIGRA_UNIQUE_PTR enc2 = getEncoder( std::string( info.getFileName() ), filetype, std::string( info.getMode() ) ); + std::swap(enc, enc2); + } else { + VIGRA_UNIQUE_PTR enc2 = getEncoder( std::string( info.getFileName() ), "undefined", std::string( info.getMode() ) ); + std::swap(enc, enc2); + } + + std::string comp = info.getCompression(); + if ( comp != "" ) { + + // check for quality parameter of JPEG compression + int quality = 0; + + // possibility 1: quality specified as "JPEG QUALITY=N" or "JPEG-ARITH QUALITY=N" + // possibility 2 (deprecated): quality specified as just a number "10" + std::string sq(" QUALITY="), parsed_comp; + std::string::size_type pos = comp.rfind(sq), start = 0; + + if(pos != std::string::npos) + { + start = pos + sq.size(); + parsed_comp = comp.substr(0, pos); + } + + std::istringstream compstream(comp.substr(start)); + compstream >> quality; + if ( quality != 0 ) + { + if(parsed_comp == "") + parsed_comp = "JPEG"; + enc->setCompressionType( parsed_comp, quality ); + } + else + { + // leave any other compression type to the codec + enc->setCompressionType(comp); + } + } + + std::string pixel_type = info.getPixelType(); + if ( pixel_type != "" ) { + if(!isPixelTypeSupported( enc->getFileType(), pixel_type )) + { + std::string msg("exportImage(): file type "); + msg += enc->getFileType() + " does not support requested pixel type " + + pixel_type + "."; + vigra_precondition(false, msg.c_str()); + } + enc->setPixelType(pixel_type); + } + + // set other properties + enc->setXResolution(info.getXResolution()); + enc->setYResolution(info.getYResolution()); + enc->setPosition(info.getPosition()); + enc->setCanvasSize(info.getCanvasSize()); + + if ( info.getICCProfile().size() > 0 ) { + enc->setICCProfile(info.getICCProfile()); + } + + return enc; +} + +// class ImageImportInfo + +ImageImportInfo::ImageImportInfo( const char * filename, unsigned int imageIndex ) + : m_filename(filename), m_image_index(imageIndex) +{ + readHeader_(); +} + +ImageImportInfo::~ImageImportInfo() { +} + +const char * ImageImportInfo::getFileName() const +{ + return m_filename.c_str(); +} + +const char * ImageImportInfo::getFileType() const +{ + return m_filetype.c_str(); +} + +const char * ImageImportInfo::getPixelType() const +{ + return m_pixeltype.c_str(); +} + +ImageImportInfo::PixelType ImageImportInfo::pixelType() const +{ + const std::string pixeltype=ImageImportInfo::getPixelType(); + if (pixeltype == "UINT8") + return UINT8; + if (pixeltype == "INT16") + return INT16; + if (pixeltype == "UINT16") + return UINT16; + if (pixeltype == "INT32") + return INT32; + if (pixeltype == "UINT32") + return UINT32; + if (pixeltype == "FLOAT") + return FLOAT; + if (pixeltype == "DOUBLE") + return DOUBLE; + vigra_fail( "internal error: unknown pixel type" ); + return ImageImportInfo::PixelType(); +} + +int ImageImportInfo::width() const +{ + return m_width; +} + +int ImageImportInfo::height() const +{ + return m_height; +} + +int ImageImportInfo::numBands() const +{ + return m_num_bands; +} + +int ImageImportInfo::numExtraBands() const +{ + return m_num_extra_bands; +} + +int ImageImportInfo::numImages() const +{ + return m_num_images; +} + +void ImageImportInfo::setImageIndex(int index) +{ + m_image_index = index; + readHeader_(); +} + +int ImageImportInfo::getImageIndex() const +{ + return m_image_index; +} + +Size2D ImageImportInfo::size() const +{ + return Size2D( m_width, m_height ); +} + +MultiArrayShape<2>::type ImageImportInfo::shape() const +{ + return MultiArrayShape<2>::type( m_width, m_height ); +} + +bool ImageImportInfo::isGrayscale() const +{ + return (m_num_bands - m_num_extra_bands) == 1; +} + +bool ImageImportInfo::isColor() const +{ + return (m_num_bands - m_num_extra_bands) == 3; +} + +bool ImageImportInfo::isByte() const +{ + return m_pixeltype == "UINT8"; +} + +Diff2D ImageImportInfo::getPosition() const +{ + return m_pos; +} + +Size2D ImageImportInfo::getCanvasSize() const +{ + return m_canvas_size; +} + +float ImageImportInfo::getXResolution() const +{ + return m_x_res; +} + +float ImageImportInfo::getYResolution() const +{ + return m_y_res; +} + +const ImageImportInfo::ICCProfile & ImageImportInfo::getICCProfile() const +{ + return m_icc_profile; +} + +void ImageImportInfo::readHeader_() +{ + VIGRA_UNIQUE_PTR decoder = getDecoder(m_filename, "undefined", m_image_index); + m_num_images = decoder->getNumImages(); + + m_filetype = decoder->getFileType(); + m_pixeltype = decoder->getPixelType(); + m_width = decoder->getWidth(); + m_height = decoder->getHeight(); + m_num_bands = decoder->getNumBands(); + m_num_extra_bands = decoder->getNumExtraBands(); + m_pos = decoder->getPosition(); + m_canvas_size = decoder->getCanvasSize(); + m_x_res = decoder->getXResolution(); + m_y_res = decoder->getYResolution(); + + m_icc_profile = decoder->getICCProfile(); + + decoder->abort(); // there probably is no better way than this +} + +// return a decoder for a given ImageImportInfo object +VIGRA_UNIQUE_PTR decoder( const ImageImportInfo & info ) +{ + std::string filetype = info.getFileType(); + validate_filetype(filetype); + return getDecoder( std::string( info.getFileName() ), filetype, info.getImageIndex() ); +} // class VolumeExportInfo +VolumeExportInfo::VolumeExportInfo( const char * filename ) : + m_x_res(0), m_y_res(0), m_z_res(0), + m_filetype("MULTIPAGE"), + m_filename_base(filename), m_filename_ext(".tif"), + fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) +{ +} + VolumeExportInfo::VolumeExportInfo( const char * name_base, const char * name_ext ) : m_x_res(0), m_y_res(0), m_z_res(0), m_filename_base(name_base), m_filename_ext(name_ext), fromMin_(0.0), fromMax_(0.0), toMin_(0.0), toMax_(0.0) { + if(m_filename_ext == "") + { + m_filename_ext = ".tif"; + m_filetype = "MULTIPAGE"; + } } VolumeExportInfo::~VolumeExportInfo() @@ -358,44 +764,180 @@ VolumeImportInfo::VolumeImportInfo(const std::string &filename) resolution_(1.f, 1.f, 1.f), numBands_(0) { - // first try image sequence loading - std::string::const_reverse_iterator - numBeginIt(filename.rbegin()), numEndIt(numBeginIt); - - do + std::string message; + +#if 0 // defined(HasHDF5) + // Deactivate this code because it effectively forces multi_impex.hxx to be compiled with HDF5 only. + + // try reading from HDF5 + // (do this first because it uses mangled 'filename/dataset_name' format) { - numEndIt = std::find_if(numBeginIt, filename.rend(),(int (*)(int)) &isdigit); - numBeginIt = std::find_if(numEndIt, filename.rend(), not1(std::ptr_fun((int (*)(int))&isdigit))); - - if(numEndIt != filename.rend()) + // split the filename into external (true filename) and internal (dataset name) part + // FIXME: at present, the delimiter must be the extension '.h5' or '.hdf5' - define a more robust rule + std::string name, datasetName; + std::size_t ext = filename.rfind(".h5"); + if(ext != std::string::npos) { - std::string - baseName(filename.begin(), - filename.begin() + (filename.rend()-numBeginIt)), - extension(filename.begin() + (filename.rend()-numEndIt), - filename.end()); - - std::vector numbers; - - findImageSequence(baseName, extension, numbers); - if(numbers.size() > 0) + name = filename.substr(0, ext+3); + datasetName = filename.substr(ext+3); + } + else + { + ext = filename.rfind(".hdf5"); + if(ext != std::string::npos) { - getVolumeInfoFromFirstSlice(baseName + numbers[0] + extension); - splitPathFromFilename(baseName, path_, name_); - baseName_ = baseName; - extension_ = extension; - shape_[2] = numbers.size(); - std::swap(numbers, numbers_); - - break; + name = filename.substr(0, ext+5); + datasetName = filename.substr(ext+5); + } + else + { + name = filename; } } + + if(H5Fis_hdf5(name.c_str())) + { + message = std::string("VolumeImportInfo(): File '"); + message += name + "' is HDF5, but filename doesn't contain an internal dataset path."; + vigra_precondition(datasetName.size() > 0, message.c_str()); + + HDF5File hdf5file(name, HDF5File::OpenReadOnly); + + message = std::string("VolumeImportInfo(): Dataset '"); + message += datasetName + "' not found in HDF5 file '" + name + "'."; + vigra_precondition(hdf5file.existsDataset(datasetName), message.c_str()); + + ArrayVector shape(hdf5file.getDatasetShape(datasetName)); + message = std::string("VolumeImportInfo(): Dataset '"); + message += datasetName + "' in HDF5 file '" + name + "' is not a volume."; + vigra_precondition(shape.size() == 3 || shape.size() == 4, message.c_str()); + + shape_[0] = shape[0]; + shape_[1] = shape[1]; + shape_[2] = shape[2]; + pixelType_ = hdf5file.getDatasetType(datasetName); + numBands_ = shape.size() == 4 + ? shape[3] + : 1; + baseName_ = name; + extension_ = datasetName; + fileType_ = "HDF5"; + return; + } } - while(numEndIt != filename.rend()); +#endif // HasHDF5 + + // check if file exists + message = std::string("VolumeImportInfo(): File '"); + message += filename + "' not found."; +#ifdef _MSC_VER + vigra_precondition(_access(filename.c_str(), 0) != -1, message.c_str()); +#else + vigra_precondition(access(filename.c_str(), F_OK) == 0, message.c_str()); +#endif + + // try Andor SIF format + { + std::string magic_string; + { + std::ifstream siffile (filename.c_str()); + if( !siffile.is_open() ) + { + message = std::string("VolumeImportInfo(): Unable to open file '"); + message += filename + "'."; + vigra_precondition(false, message.c_str()); + } + + getline(siffile, magic_string); + } + if(magic_string == "Andor Technology Multi-Channel File") + { + SIFImportInfo info(filename.c_str()); + shape_[0] = info.shapeOfDimension(0); + shape_[1] = info.shapeOfDimension(1); + shape_[2] = info.shapeOfDimension(2); + pixelType_ = "FLOAT"; + numBands_ = 1; + baseName_ = filename; + fileType_ = "SIF"; + return; + } + } + + // try multi-page TIFF or image stack + if(isImage(filename.c_str())) + { + ImageImportInfo info(filename.c_str()); + shape_[0] = info.width(); + shape_[1] = info.height(); + resolution_[1] = -1.f; // assume images to be right-handed + pixelType_ = info.getPixelType(); + numBands_ = info.numBands(); + + if(info.numImages() > 1) + { + // must be a multi-page TIFF + splitPathFromFilename(filename, path_, name_); + baseName_ = filename; + fileType_ = "MULTIPAGE"; + shape_[2] = info.numImages(); + return; + } + else + { + // try image stack loading + std::string::const_reverse_iterator + numBeginIt(filename.rbegin()), numEndIt(numBeginIt); + + do + { + numEndIt = std::find_if(numBeginIt, filename.rend(),(int (*)(int)) &isdigit); + numBeginIt = std::find_if(numEndIt, filename.rend(), not1(std::ptr_fun((int (*)(int))&isdigit))); + + if(numEndIt != filename.rend()) + { + std::string + baseName(filename.begin(), + filename.begin() + (filename.rend()-numBeginIt)), + extension(filename.begin() + (filename.rend()-numEndIt), + filename.end()); - // no numbered images found, try .info file loading - if(!numbers_.size()) + std::vector numbers; + + findImageSequence(baseName, extension, numbers); + if(numbers.size() > 0) + { + splitPathFromFilename(baseName, path_, name_); + baseName_ = baseName; + extension_ = extension; + shape_[2] = numbers.size(); + std::swap(numbers, numbers_); + fileType_ = "STACK"; + return; + } + } + } + while(numEndIt != filename.rend()); + } + + message = std::string("VolumeImportInfo(): File '"); + message += filename + "' is neither a multi-page TIFF nor a valid image stack."; + vigra_precondition(false, message.c_str()); + } + { + static std::string pixelTypes[] = { std::string("UNSIGNED_CHAR"), + std::string("UNSIGNED_BYTE"), + std::string("UINT8"), + std::string("INT16"), + std::string("UINT16"), + std::string("INT32"), + std::string("UINT32"), + std::string("FLOAT"), + std::string("DOUBLE"), + std::string() }; + + // try .info file loading std::ifstream stream(filename.c_str()); while(stream.good()) @@ -422,14 +964,20 @@ VolumeImportInfo::VolumeImportInfo(const std::string &filename) shape_[2] = atoi(value.c_str()); else if(key == "datatype") { - // FUTURE: store bit depth / signedness - if((value == "UNSIGNED_CHAR") || (value == "UNSIGNED_BYTE")) - numBands_ = 1; - else + std::string * type = pixelTypes; + while(*type != "") { - std::cerr << "Unknown datatype '" << value << "'!\n"; - break; + if(*type == value) + { + pixelType_ = value; + break; + } + ++type; } + vigra_precondition(*type != "", + "VolumeImportInfo(): Invalid datatype '" + value +"' in .info file."); + if(pixelType_ == "UNSIGNED_CHAR" || pixelType_ == "UNSIGNED_BYTE") + pixelType_ = "UINT8"; } else if(key == "description") description_ = value; @@ -439,21 +987,20 @@ VolumeImportInfo::VolumeImportInfo(const std::string &filename) rawFilename_ = value; else { - std::cerr << "WARNING: Unknown key '" << key + std::cerr << "VolumeImportInfo(): WARNING: Unknown key '" << key << "' (value '" << value << "') in info file!\n"; } } else { if(line[0]) // non-empty line? - std::cerr << "WARNING: could not parse line '" << line << "'!\n"; + std::cerr << "VolumeImportInfo(): WARNING: could not parse line '" << line << "'!\n"; } } if((shape_[0]*shape_[1]*shape_[2] > 0) && (rawFilename_.size() > 0)) { - if(!numBands_) - numBands_ = 1; // default to UNSIGNED_CHAR datatype + numBands_ = 1; baseName_ = filename; if(name_.size() > 0) @@ -465,13 +1012,14 @@ VolumeImportInfo::VolumeImportInfo(const std::string &filename) { splitPathFromFilename(baseName_, path_, name_); } + fileType_ = "RAW"; return; } - - std::string message("VolumeImportInfo(): Unable to load volume '"); - message += filename + "'."; - vigra_fail(message.c_str()); } + + message = std::string("VolumeImportInfo(): Unable to load file '"); + message += filename + "' - not a recognized format."; + vigra_precondition(false, message.c_str()); } VolumeImportInfo::VolumeImportInfo(const std::string &baseName, const std::string &extension) @@ -493,6 +1041,7 @@ VolumeImportInfo::VolumeImportInfo(const std::string &baseName, const std::strin extension_ = extension; shape_[2] = numbers.size(); std::swap(numbers, numbers_); + fileType_ = "STACK"; } void VolumeImportInfo::getVolumeInfoFromFirstSlice(const std::string &filename) @@ -531,6 +1080,10 @@ const char * VolumeImportInfo::getPixelType() const { return pixelType_.c_str(); } +const char * VolumeImportInfo::getFileType() const +{ + return fileType_.c_str(); +} MultiArrayIndex VolumeImportInfo::numBands() const { return numBands_; } bool VolumeImportInfo::isGrayscale() const { return numBands_ == 1; } bool VolumeImportInfo::isColor() const { return numBands_ > 1; } From b11399514543892ff6e3a306881fd6a8d3952960 Mon Sep 17 00:00:00 2001 From: Hans Meine Date: Wed, 24 Aug 2011 11:00:45 +0200 Subject: [PATCH 3/4] add basic implementation of a TIFFFile class --- include/vigra/tiff_file.hxx | 58 +++++++++++++++++++++++++++++++++++++ src/impex/tiff_file.cxx | 38 ++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 include/vigra/tiff_file.hxx create mode 100644 src/impex/tiff_file.cxx diff --git a/include/vigra/tiff_file.hxx b/include/vigra/tiff_file.hxx new file mode 100644 index 000000000..a6fedb022 --- /dev/null +++ b/include/vigra/tiff_file.hxx @@ -0,0 +1,58 @@ +#include +#include "vigra/tinyvector.hxx" + +namespace vigra { + +class TIFFFile +{ +public: + typedef TinyVector 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_; + } + +private: + TIFF *h_; // TIFF file handle (cf. libtiff library) + + TinyVector imageSize_, tileSize_; + FileType fileType_; + bool hasDepth_; +}; + +} // namespace vigra diff --git a/src/impex/tiff_file.cxx b/src/impex/tiff_file.cxx new file mode 100644 index 000000000..1710d3086 --- /dev/null +++ b/src/impex/tiff_file.cxx @@ -0,0 +1,38 @@ +#include "vigra/tiff_file.hxx" + +namespace vigra { + +TIFFFile::TIFFFile(const char *filename, const char *mode) + : h_(NULL), + fileType_(INVALID), + hasDepth_(false) +{ + h_ = TIFFOpen(filename, mode); + + TIFFGetField(h_, TIFFTAG_IMAGEWIDTH, &imageSize_[0]); + TIFFGetField(h_, TIFFTAG_IMAGELENGTH, &imageSize_[1]); + hasDepth_ = TIFFGetField(h_, TIFFTAG_IMAGEDEPTH, &imageSize_[2]); + + bool + hasTileWidth = TIFFGetField(h_, TIFFTAG_TILEWIDTH, &tileSize_[0]), + hasTileLength = TIFFGetField(h_, TIFFTAG_TILELENGTH, &tileSize_[1]), + hasTileDepth = TIFFGetField(h_, TIFFTAG_TILEDEPTH, &tileSize_[2]); + + if(hasTileWidth && hasTileLength && (hasTileDepth || !hasDepth_)) + { + fileType_ = TILED; + } + else if(!hasTileWidth && !hasTileLength && !hasTileDepth) + { + fileType_ = STRIPED; + } + + if(!hasDepth_) + { + imageSize_[2] = 1; + if(fileType() == TILED) + tileSize_[2] = 1; + } +} + +} // namespace vigra From 55bdf52c941834f1c5f5a123da852b947ebf3407 Mon Sep 17 00:00:00 2001 From: Hans Meine Date: Mon, 12 Jan 2015 20:00:08 +0100 Subject: [PATCH 4/4] =?UTF-8?q?first=20working=20version=20of=203D=20(tile?= =?UTF-8?q?d)=20TIFF=20loader=20-=20added=20TIFFFile::pixelType()=20-=20ad?= =?UTF-8?q?ded=20TIFFFile::readTile()=20(very=20thin=20wrapper=20around=20?= =?UTF-8?q?libtiff)=20-=20added=20VolumeInfo::fileType=20of=20=E2=80=9ETIL?= =?UTF-8?q?EDTIFF=E2=80=9C=20-=20FIXME:=20only=20works=20with=20correct=20?= =?UTF-8?q?target=20datatype=20-=20FIXME:=20does=20not=20cope=20with=20mul?= =?UTF-8?q?ti-channel=20data=20yet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/vigra/multi_impex.hxx | 50 +++++++++++++++- include/vigra/tiff_file.hxx | 92 ++++++++++++++-------------- src/impex/CMakeLists.txt | 3 +- src/impex/multi_impex.cxx | 22 ++++++- src/impex/tiff_file.cxx | 110 ++++++++++++++++++++++++++++++++++ 5 files changed, 228 insertions(+), 49 deletions(-) diff --git a/include/vigra/multi_impex.hxx b/include/vigra/multi_impex.hxx index 788f4e155..04e9fbad9 100644 --- a/include/vigra/multi_impex.hxx +++ b/include/vigra/multi_impex.hxx @@ -50,6 +50,7 @@ #include "multi_array.hxx" #include "multi_pointoperators.hxx" #include "sifImport.hxx" +#include "tiff_file.hxx" #ifdef _MSC_VER # include @@ -168,8 +169,9 @@ class VolumeImportInfo Possible values are:
"MULTIPAGE"
Multiple 2D images in a single file (currently only supported by TIFF). +
"TILEDTIFF"
Tiled TIFF (standard conform, but extremly rare, used by MeVisLab).
"SIF"
Andor Technology's .sif format. -
"RAW"
Raw data file, accompanied by a .info file +
"RAW"
Raw data file, accompanied by an .info file
"STACK"
A numbered set of 2D image files, one per slice of the volume.
**/ @@ -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]; diff --git a/include/vigra/tiff_file.hxx b/include/vigra/tiff_file.hxx index a6fedb022..6140896ec 100644 --- a/include/vigra/tiff_file.hxx +++ b/include/vigra/tiff_file.hxx @@ -6,53 +6,57 @@ namespace vigra { class TIFFFile { public: - typedef TinyVector 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 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 imageSize_, tileSize_; - FileType fileType_; - bool hasDepth_; + TinyVector imageSize_, tileSize_; + FileType fileType_; + bool hasDepth_; }; } // namespace vigra diff --git a/src/impex/CMakeLists.txt b/src/impex/CMakeLists.txt index 1d4262e09..ab680d9fc 100644 --- a/src/impex/CMakeLists.txt +++ b/src/impex/CMakeLists.txt @@ -53,7 +53,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 diff --git a/src/impex/multi_impex.cxx b/src/impex/multi_impex.cxx index 81c4b6a48..fac2b1361 100644 --- a/src/impex/multi_impex.cxx +++ b/src/impex/multi_impex.cxx @@ -48,7 +48,6 @@ std::string trimString(const std::string &s) } // namespace detail -// find filenames matching the pattern "/base[0-9]+ext" #ifdef _MSC_VER void splitPathFromFilename(const std::string &pathAndName, std::string &path, std::string &name) @@ -74,6 +73,7 @@ void splitPathFromFilename(const std::string &pathAndName, } } +// find filenames matching the pattern "/base[0-9]+ext" (Windows version) VIGRA_EXPORT void findImageSequence(const std::string &name_base, const std::string &name_ext, std::vector & numbers) @@ -153,6 +153,7 @@ void splitPathFromFilename(const std::string &pathAndName, } } +// find filenames matching the pattern "/base[0-9]+ext" (Unix version) void findImageSequence(const std::string &name_base, const std::string &name_ext, std::vector & numbers) @@ -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())) { diff --git a/src/impex/tiff_file.cxx b/src/impex/tiff_file.cxx index 1710d3086..7ae56d2b2 100644 --- a/src/impex/tiff_file.cxx +++ b/src/impex/tiff_file.cxx @@ -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 ""; +} + } // namespace vigra