Skip to content
Snippets Groups Projects
gtTIFVolWriter.m 9.43 KiB
function gtTIFVolWriter(vol, filename, varargin)
% GTTIFVOLWRITER  Writes volume data to TIFF file with compression modes.
%                 Either single file of multiple files stack can be produced.
%
%     gtTIFVolWriter(vol, filename, varargin)
%     -------------------------------------------------------------------------
% 
%     INPUT:
%       vol          = <3Dimage> Input 3D volume
%       filename     = <string>  Path to TIFF file to write
%                                The path may be relative or absolute.
%                                If extension is missing, it will be append.
%                                In the case of stack output, this will be the 
%                                stack filename prefix to which the slice 
%                                number and the extension will be append.
%
%     OPTIONAL INPUT (varargin as a list of pairs, see parse_pv_pairs.m):
%       'xrange'     = <range>   Index range of output in X direction
%       'yrange'     = <range>               "            Y     "
%       'zrange'     = <range>               "            Z     "
%
%       'type'       = <string>  Force type of data written in the TIFF file
%                                it can be 'uint8', 'uint16' or 'logical'
%                                default is 'uint8'
%
%       'compress'   = <string>  Compression mode, by default 'none', can be
%                                'packbits', 'lzm', 'deflate', 'ccitt', 'fax3'
%                                or 'fax4'
%
%       'mode'       = <string>  Output type which is 'single' or 'stack'
%                                default is a single TIFF file
%       'digits'     = <int>     Number of digits in stack filenames
%       'startindex' = <int>     Starting index for stack filenames
%       'filext'     = <string>  Output TIFF file extension ('tif' or 'tiff')
%                                default is 'tif'
%       'newMethod'  = <bool>    Use new TIFF writing method (faster)
%
%     TODO :
%     - RGB data output
%
%     Version 003 30-10-2012 by YGuilhem, yoann.guilhem@esrf.fr
%       Use tifflib for fast reading
%       Add compression option
%       Some bugfixes
%
%     Version 002 19-07-2012 by YGuilhem, yoann.guilhem@esrf.fr
%       Add the options to output the volume as a stack of multiple TIFF files.
%
%     Version 001 15-02-2012 by YGuilhem, yoann.guilhem@esrf.fr

% Set default parameters and parse optional arguments
params.newMethod = true;
params.type = '';
params.xrange = 'all';
params.yrange = 'all';
params.zrange = 'all';
params.compress = 'none';
params.mode = 'single';
params.filext = 'tif';
params.digits = 4;
params.startindex = 1;
params = parse_pv_pairs(params, varargin);

% Check if tifflib is present, if not -> old reading method
if params.newMethod && ~exist('tifflib', 'file')
    disp('Cannot use new TIFF writing method because ''tifflib'' not found!');
    params.newMethod = false;
end

% Translating range parameters
if strcmp(params.xrange, 'all')
    rangeX = 1:size(vol, 1);
else
    rangeX = params.xrange;
end
if strcmp(params.yrange, 'all')
    rangeY = 1:size(vol, 2);
else
    rangeY = params.yrange;
end
if strcmp(params.zrange, 'all')
    rangeZ = 1:size(vol, 3);
else
    rangeZ = params.zrange;
end
outVolSizeX = length(rangeX);
outVolSizeY = length(rangeY);

% Checking input/output types and decide whether casting/rescaling operations 
doCast  = false;
rescale = 0;
inputType = class(vol);
if ~isempty(params.type) && ~any(strcmp(params.type, {'logical', 'uint8', 'uint16'}))
    gtError('gtTIFVolWriter:wrong_output_type', ...
        ['Output type ''' params.type ''' is not supported!']);
elseif any(strcmp(inputType, {'logical', 'uint8', 'uint16'}))
    if isempty(params.type)
        params.type = class(vol);
    elseif strcmp(params.type, inputType)
        doCast  = false;
    elseif strcmp(params.type, 'uint8')
        doCast  = true;
        rescale = 255;
    else
        doCast  = true;
    end
else
    doCast = true;
    switch params.type
    case 'logical'
        rescale = 0;
    case 'uint8'
        rescale = 255;
    otherwise
        rescale = 65535;
    end
end

% Cast and rescale if needed
if doCast
    if rescale
        disp(['Rescaling input volume to [0 ' num2str(rescale) ...
            '] and casting it to ' params.type ' ...']);
        outVol = cast(mat2gray(vol) * rescale, params.type);
    else
        disp(['Casting input volume to ' params.type ' ...']);
        outVol = cast(vol, params.type);
    end
else
    outVol = vol;
end

% Get Bit per sample
switch class(outVol)
case 'uint16'
    tifTag.BitsPerSample = 16;
    tifTag.SampleFormat  = Tiff.SampleFormat.UInt;
    tifTag.SamplesPerPixel = 1;
case 'uint8'
    tifTag.BitsPerSample = 8;
    tifTag.SampleFormat  = Tiff.SampleFormat.UInt;
    tifTag.SamplesPerPixel = 1;
case 'logical'
    tifTag.BitsPerSample = 1;
    tifTag.SampleFormat  = Tiff.SampleFormat.UInt;
    tifTag.SamplesPerPixel = 1;
end

% Check compression mode
if isempty(params.compress) || (islogical(params.compress) && ~params.compress)
    params.compress = 'none';
elseif (islogical(params.compress) && params.compress)
    if islogical(outVol)
        params.compress = 'fax4';
    else
        params.compress = 'lzw';
    end
elseif any(strcmp(params.compress, {'ccitt', 'fax3', 'fax4'}))
    if ~islogical(outVol)
        gtError('gtTIFVolWriter:wrong_compression_mode', ...
               ['Compression mode ''' params.compress ...
               ''' is only available for logical volumes!']);
    end
elseif ~any(strcmp(params.compress, {'none', 'packbits', 'deflate', 'lzw', ...
                                     'ccitt', 'fax3', 'fax4'}))
    warning('gtTIFVolWriter:unknown_compression_mode', ...
        ['Unknown compression mode: ' params.compress ...
        ' -> Setting it to ''none''...']);
    params.compress = 'none';
end

switch params.compress
case 'none'    , tifTag.Compression = Tiff.Compression.None;
case 'lzw'     , tifTag.Compression = Tiff.Compression.LZW;
case 'packbits', tifTag.Compression = Tiff.Compression.PackBits;
case 'deflate' , tifTag.Compression = Tiff.Compression.Deflate;  % Slow but efficient
case 'ccitt'   , tifTag.Compression = Tiff.Compression.CCITTRLE;
case 'fax3'    , tifTag.Compression = Tiff.Compression.CCITTFax3;
case 'fax4'    , tifTag.Compression = Tiff.Compression.CCITTFax4;
otherwise      , tifTag.Compression = Tiff.Compression.None;
end

% Set TIFF file tags
tifTag.Photometric  = Tiff.Photometric.MinIsBlack;
tifTag.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
tifTag.ImageWidth  = outVolSizeX;
tifTag.ImageLength = outVolSizeY;
tifTag.Software = 'matlabDCT';

% Guessing output directory, filename and extension
[fpath, fname, fext] = fileparts(filename);
% Trick to handle ~ path in unix
if fpath(1)=='~' && isunix()
    fpath(1) = '';
    fpath = fullfile(getenv('HOME'), fpath);
end

if strcmpi(params.mode, 'single')
    % Setting output filename
    if isempty(fext)
        filename = fullfile(fpath, [fname '.' params.filext]);
    else
        filename = fullfile(fpath, [fname fext]);
    end

    % Check existence of output file to delete it before writing
    if exist(filename, 'file')
        delete(filename);
    end
    % Writing volume in TIFF file
    if params.newMethod
        tif = Tiff(filename, 'a');
        for iz=1:length(rangeZ)
            tif.setTag(tifTag);
            tif.write(outVol(rangeX, rangeY, rangeZ(iz))');
            tif.writeDirectory();
        end
        tif.close();
    else
        % Old method
        for iz=1:length(rangeZ)
            imwrite(outVol(rangeX(1):rangeX(end), rangeY(1):rangeY(end), ...
                rangeZ(iz))', ...
                filename, 'tif', 'Compression', params.compress, ...
                'writemode', 'append');
        end
    end

elseif strcmpi(params.mode, 'stack')

    % Check stack starting index
    if isnumeric(params.startindex)
        if mod(params.startindex, 1) ~= 0
            params.startindex = round(params.startindex);
            disp(['Argument ''startindex'' convert to ' ...
                num2str(params.startindex)]);
        end
    else
        gtError('gtTIFVolWriter:wrong_argument_type', ...
            'The argument ''startindex'' should be an integer!');
    end

    % Check stack number of digits
    if isnumeric(params.digits)
        if mod(params.digits, 1) ~= 0
            params.digits = round(params.digits);
            disp(['Argument ''digits'' convert to ' num2str(params.digits)]);
        end
    else
        gtError('gtTIFVolWriter:wrong_argument_type', ...
            'The argument ''digits'' should be an integer!');
    end

    % Create directory
    if ~isempty(fpath) && ~exist(fpath, 'dir')
        mkdir(fpath);
    end

    % Output file output format
    fprefix = fullfile(fpath, fname);
    filenameFMT = sprintf('%s%%0%dd.%s', fprefix, params.digits, params.filext);

    % Set output index offset
    offset = params.startindex - 1;

    % Writing volume in TIFF stack files
    if params.newMethod 
        for iz=1:length(rangeZ)
            outFile = sprintf(filenameFMT, iz+offset);
            tif = Tiff(outFile, 'w');
            tif.setTag(tifTag);
            tif.write(outVol(rangeX, rangeY, rangeZ(iz))');
            tif.close();
        end
    else
        % Old method
        for iz=1:length(rangeZ)
            outFile = sprintf(filenameFMT, iz+offset);
            imwrite(outVol(rangeX, rangeY, rangeZ(iz))', outFile, 'tif', ...
                    'Compression', params.compress, 'writemode', 'overwrite');
        end
    end
end

end % end of function