changeset 814:a6294bb53bee

im2col: N-dimensional support for, and vectorization of, "distinct" option. * im2col.m: implement rearraging of distinct N-dimensional blocks with N-dimensional images into columns. Vectorize the "distinct" option for a huge performace increase (dependent on the number of blocks).
author Carnë Draug <carandraug@octave.org>
date Tue, 29 Oct 2013 12:15:08 +0000
parents d26a8681dd22
children ad3e5b849ade
files inst/im2col.m
diffstat 1 files changed, 73 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/inst/im2col.m
+++ b/inst/im2col.m
@@ -1,4 +1,5 @@
 ## Copyright (C) 2004 Josep Mones i Teixidor <jmones@puntbarra.com>
+## Copyright (C) 2013 Carnë Draug <carandraug@octave.org>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free Software
@@ -14,12 +15,12 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {@var{B} =} im2col (@var{A}, [@var{m}, @var{n}], @var{block_type})
-## @deftypefnx {Function File} {@var{B} =} im2col (@var{A}, [@var{m}, @var{n}])
-## @deftypefnx {Function File} {@var{B} =} im2col (@var{A}, 'indexed', @dots{})
-## Rearranges image blocks into columns.
+## @deftypefn  {Function File} {} im2col (@var{A}, @var{block_size})
+## @deftypefnx {Function File} {} im2col (@var{A}, v, @var{block_type})
+## @deftypefnx {Function File} {} im2col (@var{A}, "indexed", @dots{})
+## Rearrange image blocks into columns.
 ##
-## @code{B=im2col(A, [m, n], blocktype)} rearranges blocks in @var{A}
+## @code{B=im2col(A, block_size, blocktype)} rearranges blocks in @var{A}
 ## into columns in a way that's determined by @var{block_type}, which
 ## can take the following values:
 ##
@@ -44,7 +45,7 @@
 ## the complementary function @code{col2im} expects.
 ## @end table
 ##
-## @code{B=im2col(A,[m,n])} takes @code{distinct} as a default value for
+## @code{B=im2col(A, block_size)} takes @code{distinct} as a default value for
 ## @var{block_type}. 
 ##
 ## @code{B=im2col(A,'indexed', @dots{})} will treat @var{A} as an indexed
@@ -59,92 +60,88 @@
 ## @end deftypefn
 
 function B = im2col (A, varargin)
-  if(nargin < 2 || nargin > 4)
-    print_usage;
+  if (nargin < 2 || nargin > 4)
+    print_usage ();
+  elseif (! ismatrix (A) || ! (isnumeric (A) || islogical (A)))
+    error ("im2col: A must be a numeric of logical matrix");
   endif
+  p = 1;  # varargin param being processsed
+
+  ## Defaults
+  block_type  = "sliding";
+  padval      = 0;
+  indexed     = false;
 
-  ## check 'indexed' presence
-  indexed=false;
-  p=1;
-  if(ischar(varargin{1}) && strcmp(varargin{1}, "indexed"))
-    if(nargin<3)
-      print_usage;
+  ## Check for 'indexed' presence
+  if (ischar (varargin{p}) && strcmpi (varargin{p}, "indexed"))
+    indexed = true;
+    if (nargin < 3)
+      print_usage ();
     endif
-    indexed=true;
-    p+=1;
-    if(isa(A,"uint8") || isa(A,"uint16"))
-      padval=0;
-    else
-      padval=1;
+    if (isfloat (A))
+      ## We pad with value of 1 for indexed images of floating point class,
+      ## because lowest index is 1 for them (it's 0 for integer indexed images).
+      padval = 1;
     endif
-  else
-    padval=0;
+    p++;
+  elseif (nargin > 3)
+    ## If we didn't have "indexed" but had 4 parameters there's an error
+    print_usage ();
   endif
 
   ## check [m,n]
-  if(!isvector(varargin{p}))
-    error("im2col: expected [m,n] but param is not a vector.");
-  endif
-  if(length(varargin{p})!=2)
-    error("im2col: expected [m,n] but param has wrong length.");
+  block_size = varargin{p};
+  if (! isnumeric (block_size) || ! isvector (block_size) ||
+      any (block_size(:) < 1))
+    error ("im2col: BLOCK_SIZE must be a vector of positive elements.");
+  elseif (numel (block_size) > ndims (A))
+    error ("im2col: BLOCK_SIZE can't have more elements than the dimensions of A");
   endif
-  m=varargin{p}(1);
-  n=varargin{p}(2);
-  p+=1;
+  block_size(end+1:ndims(A)) = 1; # expand singleton dimensions
+  block_size = block_size(:).'; # make sure it's a row vector
+  p++;
 
-  block_type='sliding';
-  if(nargin>p)
+  if (nargin > p)
     ## we have block_type param
-    if(!ischar(varargin{p}))
+    if (! ischar (varargin{p}))
       error("im2col: invalid parameter block_type.");
     endif
-    block_type=varargin{p};
-    p+=1;
-  endif
-
-  ## if we didn't have 'indexed' but had 4 parameters there's an error
-  if(nargin>p)
-    print_usage;
-  endif
-  
-  ## common checks
-  if(!ismatrix(A))
-    error("im2col: A should be a matrix (or vector).");
+    block_type = varargin{p};
   endif
 
-  switch(block_type)
-    case('distinct')
-      ## calc needed padding
-      sp=mod(-size(A)',[m;n]);
-
-      if(any(sp))
-        A=padarray(A,sp,padval,'post');
+  switch (tolower (block_type))
+    case "distinct"
+      ## Calculate needed padding
+      sp = mod (-size (A), block_size);
+      if (any (sp))
+        A = padarray (A, sp, padval, "post");
       endif
 
-      ## iterate through all blocks
-      B=[];
-      for i=1:m:size(A,1) ## up to bottom
-        for j=1:n:size(A,2) ## left to right
-          ## TODO: check if we can horzcat([],uint8([10;11])) in a
-          ## future Octave version > 2.1.58
-          if(isempty(B))
-            B=A(i:i+m-1,j:j+n-1)(:);
-          else
-            B=horzcat(B, A(i:i+m-1,j:j+n-1)(:));
-          endif
-        endfor
-      endfor
-      
-    case('sliding')
-      if(indexed)
-        disp("WARNING: 'indexed' has no sense when using sliding.");
+      ## Create the dimensions arguments for mat2cell (one per dimension
+      ## with the length of that dimension repeated the number of blocks
+      ## for that dimension)
+      n_blocks   = size (A) ./ block_size;
+      cell_split = arrayfun (@(x) repmat (block_size(x), [1 n_blocks(x)]),
+                             1:numel (block_size), "UniformOutput", false);
+
+      ## This functions may be a good candidate to rewrite in C++, making
+      ## use of the code in mat2cell without the need to convert between
+      ## cell and matrix and transposing.
+      B_cell = mat2cell (A, cell_split{:})';
+      B = cell2mat (cellfun (@(x) x(:), B_cell(:), "UniformOutput", false)');
+
+    case "sliding"
+      if (any (size (A) < block_size))
+        error("im2col: no dimension of A can be greater than BLOCK_SIZE in sliding");
+      elseif (ndims (A) > 2)
+        ## TODO: implement n-dimensional sliding
+        error ("im2col: only 2 dimensional are supported for sliding");
       endif
-      if(m>size(A,1) || n>size(A,2))
-        error("im2col: block size can't be greater than image size in sliding");
-      endif
+      m = block_size(1);
+      n = block_size(2);
       ## TODO: check if matlab uses top-down and left-right order
-      B=[];
-      for j=1:1:size(A,2)-n+1 ## left to right
+      B = [];
+      for j = 1:1:size(A,2)-n+1 ## left to right
         for i=1:1:size(A,1)-m+1 ## up to bottom
           ## TODO: check if we can horzcat([],uint8([10;11])) in a
           ## future Octave version > 2.1.58
@@ -155,9 +152,9 @@
           endif
         endfor
       endfor
-      
+
     otherwise
-      error("im2col: invalid block_type.");
+      error ("im2col: invalid BLOCK_TYPE `%s'.", block_type);
   endswitch
 
 endfunction