Matrix “Zigzag” Reordering

后端 未结 6 1366
名媛妹妹
名媛妹妹 2020-11-30 04:35

I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:

相关标签:
6条回答
  • 2020-11-30 05:04

    Here's a non-loop solution zig_zag.m. It looks ugly but it works!:

    function [M,index] = zig_zag(M)
      [r,c] = size(M);
      checker = rem(hankel(1:r,r-1+(1:c)),2);
      [rEven,cEven] = find(checker);
      [cOdd,rOdd] = find(~checker.'); %'#
      rTotal = [rEven; rOdd];
      cTotal = [cEven; cOdd];
      [junk,sortIndex] = sort(rTotal+cTotal);
      rSort = rTotal(sortIndex);
      cSort = cTotal(sortIndex);
      index = sub2ind([r c],rSort,cSort);
      M = M(index);
    end
    

    And a test matrix:

    >> M = [magic(4) zeros(4,1)];
    
    M =
    
        16     2     3    13     0
         5    11    10     8     0
         9     7     6    12     0
         4    14    15     1     0
    
    >> newM = zig_zag(M)    %# Zig-zag sampled elements
    
    newM =
    
        16
         2
         5
         9
        11
         3
        13
        10
         7
         4
        14
         6
         8
         0
         0
        12
        15
         1
         0
         0
    
    0 讨论(0)
  • 2020-11-30 05:07

    Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.

    I think this should be generalizeable to a non-square array.

    % for a 3x3 array 
    n=3;
    
    numElementsPerDiagonal = [1:n,n-1:-1:1];
    hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
    array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
    
    % loop through the hankel array and add numbers counting either up or down
    % if they are even or odd
    for d = 1:(2*n-1)
       if floor(d/2)==d/2
          % even, count down
          array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
       else
          % odd, count up
          array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
       end
    end
    
    % now flip to get the result
    indexMatrix = fliplr(array2add)
    
    result =
         1     2     6
         3     5     7
         4     8     9
    

    Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.

    EDIT

    Ok, from your comment it looks like you need to use sort like Marc suggested.

    indexMatrixT = indexMatrix';   % ' SO formatting
    [dummy,sortedIdx] = sort(indexMatrixT(:));
    
    sortedIdx =
         1     2     4     7     5     3     6     8     9
    

    Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.

    0 讨论(0)
  • Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -

    [m,n] = size(X);
    nlim = m*n;
    n = n+mod(n-m,2);
    mask = bsxfun(@le,[1:m]',[n:-1:1]);
    start_vec = m:m-1:m*(m-1)+1;
    a = bsxfun(@plus,start_vec',[0:n-1]*m);
    offset_startcol = 2- mod(m+1,2);
    [~,idx] = min(mask,[],1);
    idx = idx - 1;
    idx(idx==0) = m;
    end_ind = a([0:n-1]*m + idx);
    
    offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
    a(:,offset_startcol:2:end) = bsxfun(@minus,offsets,a(:,offset_startcol:2:end));
    out = a(mask);
    out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
    result = X([out2 ; out(out<=nlim)]);
    

    Quick runtime tests against Luis's approach -

    Datasize: 500 x 2000
    ------------------------------------- With Proposed Approach
    Elapsed time is 0.037145 seconds.
    ------------------------------------- With Luis Approach
    Elapsed time is 0.045900 seconds.
    
    
    Datasize: 5000 x 20000
    ------------------------------------- With Proposed Approach
    Elapsed time is 3.947325 seconds.
    ------------------------------------- With Luis Approach
    Elapsed time is 6.370463 seconds.
    
    0 讨论(0)
  • 2020-11-30 05:14

    Consider the code:

    M = randi(100, [3 4]);                      %# input matrix
    
    ind = reshape(1:numel(M), size(M));         %# indices of elements
    ind = fliplr( spdiags( fliplr(ind) ) );     %# get the anti-diagonals
    ind(:,1:2:end) = flipud( ind(:,1:2:end) );  %# reverse order of odd columns
    ind(ind==0) = [];                           %# keep non-zero indices
    
    M(ind)                                      %# get elements in zigzag order
    

    An example with a 4x4 matrix:

    » M
    M =
        17    35    26    96
        12    59    51    55
        50    23    70    14
        96    76    90    15
    
    » M(ind)
    ans =
        17  35  12  50  59  26  96  51  23  96  76  70  55  14  90  15
    

    and an example with a non-square matrix:

    M =
        69     9    16   100
        75    23    83     8
        46    92    54    45
    ans =
        69     9    75    46    23    16   100    83    92    54     8    45
    
    0 讨论(0)
  • 2020-11-30 05:14

    Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be

    [~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
    reorderedim = im(I);
    

    I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.

    0 讨论(0)
  • 2020-11-30 05:25

    This approach is pretty fast:

    X = randn(500,2000); %// example input matrix
    [r, c] = size(X);
    M = bsxfun(@plus, (1:r).', 0:c-1);
    M = M + bsxfun(@times, (1:r).'/(r+c), (-1).^M);
    [~, ind] = sort(M(:));
    y = X(ind).'; %'// output row vector
    

    Benchmarking

    The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).

    %// Amro's approach
    function y = zigzag_Amro(M)
    ind = reshape(1:numel(M), size(M));
    ind = fliplr( spdiags( fliplr(ind) ) );     
    ind(:,1:2:end) = flipud( ind(:,1:2:end) );
    ind(ind==0) = [];
    y = M(ind);
    
    %// Luis' approach
    function y = zigzag_Luis(X)
    [r, c] = size(X);
    M = bsxfun(@plus, (1:r).', 0:c-1);
    M = M + bsxfun(@times, (1:r).'/(r+c), (-1).^M);
    [~, ind] = sort(M(:));
    y = X(ind).';
    
    %// Benchmarking code:
    S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
    f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
    %// f = [0.5 2]; %// plotted with '--'
    %// f = [2 0.5]; %// plotted with ':'
    t_Amro = NaN(size(S));
    t_Luis = NaN(size(S));
    for n = 1:numel(S)
        X = rand(f(1)*S(n), f(2)*S(n));
        f_Amro = @() zigzag_Amro(X);
        f_Luis = @() zigzag_Luis(X);
        t_Amro(n) = timeit(f_Amro);
        t_Luis(n) = timeit(f_Luis);
    end
    loglog(S.^2*prod(f), t_Amro, '.b-');
    hold on
    loglog(S.^2*prod(f), t_Luis, '.r-');
    xlabel('number of matrix entries')
    ylabel('time')
    

    The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.

    enter image description here

    0 讨论(0)
提交回复
热议问题