view scripts/image/private/__tiff_imwrite__.m @ 31198:93eb0d6e7f62

__tiff_imwrite__: converted to private octave function * libinterp/corefcn/__tiff__.cc: removed internal function __tiff_imwrite__. * scripts/image/private/__tiff_imwrite__.m: added private function __tiff_imwrite__. * scripts/image/module.mk: added entry for __tiff_imwrite__.m.
author magedrifaat <magedrifaat@gmail.com>
date Fri, 02 Sep 2022 03:06:29 +0200
parents
children 30b28458bb06
line wrap: on
line source

function __tiff_imwrite__ (img, varargin)
  ## A lot of the input sanitising logic here is copied from
  ## scripts/image/__imwrite__.m and adapted to the needs of the Tiff
  ## interface

  if (nargin < 2 || ! (isnumeric (img) || islogical (img)))
    print_usage ("imwrite");
  endif

  [filename, ext, map, param_list] = imwrite_filename (varargin{:});
  
  if (isempty (img))
    error ("imwrite: invalid empty image");
  elseif (issparse (img) || issparse (map))
    error ("imwrite: sparse images are not supported");
  endif

  if (rem (numel (param_list), 2) != 0)
    error ("imwrite: no pair for all arguments (odd number left)");
  endif

  writemode = "w";
  compression = Tiff.Compression.None;
  rows_per_strip = -1;
  res = [72, 72];
  description = "";
  alpha = [];

  for idx = 1:2:numel (param_list)
    switch (tolower (param_list{idx}))
      case "alpha"
        alpha = param_list{idx + 1};
        if (! isnumeric (alpha))
          error ("imwrite: value for %s option must be a numeric matrix",
                 param_list{idx});
        elseif (size (alpha, 3) != 1)
          error ("imwrite: 3rd dimension of matrix for %s must be singleton",
                 param_list{idx});
        elseif (ndims (alpha) > 4
                || any (size (alpha)([1 2]) != size (img)([1 2]))
                || size (alpha, 4) != size (img, 4))
          error ("imwrite: matrix for %s must have same dimension as image",
                 param_list{idx});
        endif
      
      case "colorspace"
        ## Matlab specifies three colorspaces: RGB, CIELAB and ICCLAB.
        ## Of the three, only RGB is currently supported so this tag
        ## can be ignored.
      
      case "compression"
        switch (tolower (param_list{idx + 1}))
          case "packbits"
            compression = Tiff.Compression.PackBits;
          case "lzw"
            compression = Tiff.Compression.LZW;
          case "deflate"
            compression = Tiff.Compression.Deflate;
          case "jpeg"
            compression = Tiff.Compression.JPEG;
          case "ccitt"
            compression = Tiff.Compression.CCITTRLE;
          case "fax3"
            compression = Tiff.Compression.CCITTFax3;
          case "fax4"
            compression = Tiff.Compression.CCITTFax4;
          otherwise
            error ("imwrite: invalid compression '%s'", compression);
        endswitch
      
      case "description"
        if (! ischar (param_list{idx + 1}))
            error ("imwrite: value for %s option must be a string",
                   param_list{idx});
        endif
        description = param_list{idx + 1};
      
      case "resolution"
        if (! isnumeric (param_list{idx + 1})
            || all (numel (param_list) != [1, 2]))
          error ("imwrite: value for %s option must be either a scalar value or a two-element vector",
                 param_list{idx});
        endif
        resolution = param_list{idx + 1};
        if (numel (resolution) == 1)
          resolution(2) = resolution(1);
        endif
      
      case "rowsperstrip"
        if (! isnumeric (param_list{idx + 1})
            || ! isscalar (param_list{idx + 1}))
          error ("imwrite: value for %s option must be a numeric scalar",
                 param_list{idx});
        endif
        rows_per_strip = param_list{idx + 1};
      
      case "writemode"
        if (! ischar (param_list{idx + 1})
            || ! any (strcmpi (param_list{idx + 1}, {"append", "overwrite"})))
          error ('imwrite: value for %s option must be "append" or "overwrite"',
                 param_list{idx});
        endif
        if (strcmpi (param_list{idx + 1}, {"append"}))
          writemode = "a";
        endif
      
      otherwise
        error ("imwrite: invalid PARAMETER '%s'", param_list{idx});
    endswitch
  endfor

  if (! isempty (map))
    if (! iscolormap (map))
      error ("imwrite: invalid MAP for indexed image");
    elseif (ndims (img) != 2)
      error ("imwrite: indexed image must have 2 (found %i)", ndims (img));
    endif

    ## If the image is floating point, then we convert it to integer because
    ## it represents indices of the color map which must be integers.  Also,
    ## if it's floating point, it has an offset of 1
    if (isfloat (img))
      if (rows (map) <= 256)
        img = uint8 (img - 1);
      else
        img = uint16 (img - 1);
      endif
    endif

    ## Fill in the colormap as required with rgb (0, 0, 0)
    nColors = rows (map);
    if (islogical (img))
      required_colors = 2;
    else
      required_colors = double (intmax (class (img))) + 1;
    endif

    if (nColors < required_colors)
      warning ("imwrite: MAP has not enough colors.  Filling with black");
      map(nColors+1:required_colors,:) = 0;
    endif
  endif

  if (ndims (img) > 4)
    error ("imwrite: invalid %d-dimensional image data", ndims (img));
  elseif (all (size (img, 3) != [1 3 4]))
    ## 1, 3, or 4 for grayscle, RGB, and CMYK respectively
    error ("imwrite: IMG 3rd dimension must be 1, 3, or 4");
  endif
  
  ## Matlab specifies that JPEG compression must also specify RowsPerStrip
  ## and must be a value divisible by 8
  if (compression == Tiff.Compression.JPEG
      && (rows_per_strip == -1 || rem (rows_per_strip, 8) != 0))
    error ("imwrite: RowsPerStrip option must be specified for jpeg compression and must be divisible by 8");
  endif
  
  ## These compression schemes are only available for binary images
  if (! (islogical (img))
      && any (compression == [Tiff.Compression.CCITTRLE,
                              Tiff.Compression.CCITTFax3,
                              Tiff.Compression.CCITTFax4]))
    error ("imwrite: the specified compression scheme is for binary images only");
  endif

  ## Set the image tag data
  tags.ImageLength = size (img, 1);
  tags.ImageWidth = size (img, 2);

  ## See if SamplesPerPixel should account for the alpha channel
  if (! isempty (alpha))
    tags.SamplesPerPixel = size (img, 3) + 1;
    tags.ExtraSamples = Tiff.ExtraSamples.AssociatedAlpha;
  else
    tags.SamplesPerPixel = size (img, 3);
  endif

  tags.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
  tags.Compression = compression;
  tags.XResolution = res(1);
  tags.YResolution = res(2);
  tags.ImageDescription = description;
  tags.BitsPerSample = infer_bitdepth (class (img));
  
  ## Infer the photometric interpretation of the image from the color map
  ## and the number of channels of the input
  tags.Photometric = Tiff.Photometric.MinIsBlack;
  if (! isempty (map))
    tags.Photometric = Tiff.Photometric.Palette;
    tags.ColorMap = map;
  elseif (size(img, 3) == 3)
    tags.Photometric = Tiff.Photometric.RGB;
  elseif (size(img, 3) == 4)
    tags.Photometric = Tiff.Photometric.CMYK;
  endif
  
  ## Set the correct sample format for floating-point and signed types
  if (isfloat (img))
    tags.SampleFormat = Tiff.SampleFormat.IEEEFP;
  elseif (any (strcmp (class (img), {"int8", "int16", "int32"})))
    tags.SampleFormat = Tiff.SampleFormat.Int;
  endif

  ## Add the alpha channel to the image data
  if (! isempty (alpha))
    img = cat (3, img, alpha);
  endif

  tif = Tiff (filename, writemode);

  for dir_idx = 1:size(img, 4)
    tif.setTag (tags);
    ## Try to get a reasonable size for RowsPerStrip if not set
    ## This must be done after the other tags are set so LibTIFF
    ## can make an informed calculation.
    % if (rows_per_strip == -1)
    %   rows_per_strip = tif.getDefaultStripLength ();
    % endif
    if (rows_per_strip != -1)
      tif.setTag (Tiff.TagID.RowsPerStrip, rows_per_strip);
    endif

    tif.write (img(:,:,:, dir_idx));

    tif.writeDirectory ();
  endfor

  tif.close ();

endfunction

function bitdepth = infer_bitdepth (img_class)
  switch (img_class)
    case "logical"
      bitdepth = 1;
    case {"int8", "uint8"}
      bitdepth = 8;
    case {"int16", "uint16"}
      bitdepth = 16;
    case {"int32", "uint32", "single"}
      bitdepth = 32;
    case "double"
      bitdepth = 64;
    otherwise
      error ("imwrite: Unsupported data type for image data");
  endswitch
endfunction