view scripts/image/private/__tiff_imread__.m @ 31217:c09f6a3597e6 default tip gsoc-libtiff

__tiff__.cc: handled unused parameter warning when Tiff is not enabled * libtinerp/corefcn/__tiff__.cc: added appropriate hadnling for unused parameters for cases when Octave is compiled without Tiff support to suppress compiler warnings in that case.
author magedrifaat <magedrifaat@gmail.com>
date Wed, 07 Sep 2022 18:39:10 +0200
parents bbb41a5f377a
children
line wrap: on
line source

function [A, cmap, alpha] = __tiff_imread__ (filename, varargin)
  img = Tiff (filename);
  dir_count = img.numberOfDirectories ();
  
  ## A lot of the input sanitising logic here is copied from
  ## scripts/image/__imread__.m and adapted to the needs of the Tiff
  ## interface

  pages = [1];
  offset = 1;
  if (nargin > 1 && ! ischar (varargin{1}))
    if (! is_valid_index_option (varargin {1}))
      error ("imread: IDX must be a numeric vector");
    endif
    pages = varargin{1};
    offset = 2;
  endif

  if (rem (numel (varargin) - offset + 1, 2) != 0)
    error ("imread: PARAM/VALUE arguments must occur in pairs");
  endif

  ## Check for Index/Frames argument
  idx = strcmpi ("index", varargin) | strcmpi ("frames", varargin);
  if (any (idx))
    if (sum (idx) > 1)
      error ("imread: Index or Frames may only be specified once");
    endif
    val = varargin{circshift (idx, 1)};
    if (! is_valid_index_option (val) && ! strcmpi (val, "all"))
      error ("imread: %s must be a vector or the string 'all'", varargin{idx});
    endif
    if (strcmpi (val, "all"))
      pages = 1:dir_count;
    else
      pages = val;
    endif
  endif
  
  if (any ((pages < 1) | (pages > dir_count)))
    error ("imread: index/frames specified are outside the number of images");
  endif

  img.setDirectory (pages(1));
  info = get_image_info (img);

  ## Verify that all images have the same dimensions, number of channels,
  ## bit-depth, and data type
  for page_idx = 2:numel (pages)
    img.setDirectory (pages(page_idx));
    dir_info = get_image_info (img);
    if (info.width != dir_info.width || info.height != dir_info.height)
      error ("imread: all frames must have the same size but frame %d is different",
             pages(page_idx));
    endif
    if (info.bitdepth != dir_info.bitdepth)
      error ("imread: all frames must have the same bit depth but frame %d is different",
             pages(page_idx));
    endif
    if (info.nchannels != dir_info.nchannels)
      error ("imread: all frames must have the same number of channels but frame %d is different",
             pages(page_idx));
    endif
    if (info.datatype != dir_info.datatype)
      error ("imread: all frames must have the same data type but frame %d is different",
             pages(page_idx));
    endif
    if (info.photometric != dir_info.photometric)
      error ("imread: all frames must have the same photometric interpretation but frame %d is different",
             pages(page_idx));
    endif
  endfor

  region = {1:info.height, 1:info.width};
  for idx = offset:2:(numel (varargin) - offset + 1)
    switch (tolower (varargin{idx}))

      case {"frames", "index"}
        ## Do nothing.  This option was already processed before the loop.

      case "pixelregion"
        region = varargin{idx+1};
        if (! iscell (region) || numel (region) != 2)
          error ("imread: %s must be a 2-element cell array",
                 varargin{idx});
        endif
        for reg_idx = 1:2
          if (numel (region{reg_idx}) == 3)
            ## do nothing
          elseif (numel (region{reg_idx}) == 2)
            region{reg_idx}(3) = region{reg_idx}(2);
            region{reg_idx}(2) = 1;
          else
            error ("imread: range for %s must be a 2 or 3 element vector",
                   varargin{idx});
          endif
          region{reg_idx} = floor (region{reg_idx}(1)): ...
                            floor (region{reg_idx}(2)): ...
                            floor (region{reg_idx}(3));
        endfor
        if (region{1}(end) > info.height)
          error ("imread: end ROWS for PixelRegions option is larger than image height");
        elseif (region{2}(end) > info.width)
          error ("imread: end COLS for PixelRegions option is larger than image width");
        endif

      case "info"
        ## We ignore this option.  This parameter exists in Matlab to
        ## speed up the reading of multipage TIFF by passing a structure
        ## that contains information about the start on the file of each
        ## page.  We can't control it through GraphicsMagic but at least
        ## we allow to load multiple pages with one command.

      otherwise
        error ("imread: invalid PARAMETER '%s'", varargin{idx});

    endswitch
  endfor

  A = [];
  cmap = [];
  alpha = [];
  for page_idx = 1:numel(pages)
    img.setDirectory (pages(page_idx));

    tiled = img.isTiled ();
    if (numel (region{1}) == info.height
        && (! tiled || numel (region{2}) == info.width))
      ## If we need the entire tiled image or all the rows of a stripped
      ## image then the best approach is to read the entire image directly
      data = img.read ();
      if (numel (region{2}) != info.width)
        data = data (:, region{2}, :);
      endif
    elseif (tiled)
      ## Otherwise it should be better to read the specific strips and tiles
      ## that the requested region falls in
      data = read_tiled_data (img, region);
    else
      data = read_stripped_data (img, region);
    endif
    A = cat (4, A, data);
    if (info.photometric == Tiff.Photometric.Palette)
      cmap = cat (3, cmap, img.getTag (Tiff.TagID.ColorMap));
    endif
  endfor

  img.close ();
endfunction

function data = read_tiled_data (img, region)
  ## Get all necessary image info
  info = get_image_info (img);
  planar = img.getTag (Tiff.TagID.PlanarConfiguration);
  tile_width = img.getTag (Tiff.TagID.TileWidth);
  tile_height = img.getTag (Tiff.TagID.TileLength);

  ## Calculate the number of tiles in the two dimensions
  tiles_across = (info.width + tile_width - 1) / tile_width;
  tiles_down = (info.height + tile_height - 1) / tile_height;

  ## Create array to hold output data (we need a tile to infer the datatype)
  tile = img.readEncodedTile (1);
  data = zeros (numel (region{1}), numel (region{2}),
                info.nchannels, class (tile));
  
  ## Calculate the row stride
  rows = region{1};
  row_stride = uint32 (info.height);
  if (numel (rows) > 1)
    row_stride = uint32 (rows(2) - rows(1));
  endif

  ## Calculate the column stride
  cols = region{2};
  col_stride = uint32 (info.width);
  if (numel (cols) > 1)
    col_stride = uint32 (cols(2) - cols(1));
  endif

  tile_idx = 1;
  if (planar == Tiff.PlanarConfiguration.Chunky)
    ## For chunky planes all channels are stores in the same plane
    for tj = 1:tiles_down
      ## Calculate the index of the first row of the tile
      t_rowstart = (tj - 1) * tile_height + 1;
      ## Extract the rows that fall in the tile
      rows_in_tile = rows (rows >= t_rowstart
                          & rows < (t_rowstart + tile_height));
      ## Calculate the indices of these rown in the tile array
      tile_rows = rem (rows_in_tile - 1, tile_height) + 1;
      ## Calculate the indices of the same rows in the output array
      data_rows = idivide (rows_in_tile - rows (1), row_stride, "fix") + 1;
      for ti = 1:tiles_across
        ## Calculate the index of the first column of the tile
        t_colstart = (ti - 1) * tile_width + 1;
        ## Extract the columns that fall in the tile
        cols_in_tile = cols (cols >= t_colstart
                            & cols < (t_colstart + tile_width));
        ## Calculate the indices of these columns in the tile array
        tile_cols = rem (cols_in_tile - 1, tile_width) + 1;
        ## Calculate the indices of the same columns in the output array
        data_cols = idivide (cols_in_tile - cols (1), col_stride, "fix") + 1;

        ## Skip over tiles that are not needed
        if (numel (rows_in_tile) != 0 || numel (cols_in_tile) != 0)
          tile = img.readEncodedTile (tile_idx);
          ## Copy the needed data from the tile array to the output array
          data (data_rows, data_cols, :) = tile (tile_rows, tile_cols, :);
        endif
        tile_idx += 1;
      endfor
    endfor
  else
    for ch = 1:info.nchannels
      ## For separate planes all the tiles of the first channel are found
      ## first then all the tiles of the second channel and so on
      for tj = 1:tiles_down
        ## Calculate the index of the first row of the tile
        t_rowstart = (tj - 1) * tile_height + 1;
        ## Extract the rows that fall in the tile
        rows_in_tile = rows (rows >= t_rowstart
                            & rows < (t_rowstart + tile_height));
        ## Calculate the indices of these rown in the tile array
        tile_rows = rem (rows_in_tile - 1, tile_height) + 1;
        ## Calculate the indices of the same rows in the output array
        data_rows = idivide (rows_in_tile - rows (1), row_stride, "fix") + 1;
        for ti = 1:tiles_across
          ## Calculate the index of the first column of the tile
          t_colstart = (ti - 1) * tile_width + 1;
          ## Extract the columns that fall in the tile
          cols_in_tile = cols (cols >= t_colstart
                              & cols < (t_colstart + tile_width));
          ## Calculate the indices of these columns in the tile array
          tile_cols = rem (cols_in_tile - 1, tile_width) + 1;
          ## Calculate the indices of the same columns in the output array
          data_cols = idivide (cols_in_tile - cols (1), col_stride, "fix") + 1;

          ## Skip over tiles that are not needed
          if (numel (rows_in_tile) != 0 || numel (cols_in_tile) != 0)
            tile = img.readEncodedTile (tile_idx);
            ## Copy the needed data from the tile array to the output array
            data (data_rows, data_cols, ch) = tile (tile_rows, tile_cols);
          endif
          tile_idx += 1;
        endfor
      endfor
    endfor
  endif
endfunction

function data = read_stripped_data (img, region)
  ## Get all necessary image info
  info = get_image_info (img);
  rows_per_strip = img.getTag (Tiff.TagID.RowsPerStrip);
  planar = img.getTag (Tiff.TagID.PlanarConfiguration);
  
  ## Create array to hold the output data
  ## (we need a strip to infer the data type)
  strip = img.readEncodedStrip (1);
  data = zeros (numel (region{1}), numel (region{2}),
                info.nchannels, class (strip));
  last_strip_index = 1;
  if (planar == Tiff.PlanarConfiguration.Chunky)
    ## For chunky planes, we need to iterate over rows only
    data_row = 1;
    for row = region{1}
      strip_index = img.computeStrip (row);
      ## The index of the row in the strip
      strip_row = rem (row - 1, rows_per_strip) + 1;
      if (strip_index != last_strip_index)
        ## If the required strip isn't cached, grab it
        strip = img.readEncodedStrip (strip_index);
        last_strip_index = strip_index;
      endif
      data(data_row, :, :) = strip (strip_row, region{2}, :);
      data_row += 1;
    endfor
  else
    ## For separate planes we need to loop over channels as well as rows
    ## It's better to make the outer loop for channels so we can make use
    ## of strip caching, because otherwise, we would need to read a different
    ## strip each iteration of the inner loop
    for ch = 1:info.nchannels
      data_row = 1;
      for row = region{1}
        strip_index = img.computeStrip (row, ch);
        ## The index of the row in the strip
        strip_row = rem (row - 1, rows_per_strip) + 1;
        if (strip_index != last_strip_index)
          ## If the needed strip is not cached, grab it
          strip = img.readEncodedStrip (strip_index);
          last_strip_index = strip_index;
        endif
        data(data_row, :, ch) = strip (strip_row, region{2});
        data_row += 1;
      endfor
    endfor
  endif
endfunction

function bool = is_valid_index_option (arg)
  bool = isvector (arg) && isnumeric (arg) && isreal (arg);
endfunction

function info = get_image_info (img)
  info.height = img.getTag (Tiff.TagID.ImageLength);
  info.width = img.getTag (Tiff.TagID.ImageWidth);
  info.bitdepth = img.getTag (Tiff.TagID.BitsPerSample);
  info.nchannels = img.getTag (Tiff.TagID.SamplesPerPixel);
  info.datatype = img.getTag (Tiff.TagID.SampleFormat);
  info.photometric = img.getTag (Tiff.TagID.Photometric);
endfunction