0

Numpy ravel works well if I need to create a vector by reading by rows or by columns. However, I would like to transform a matrix to a 1d array, by using a method that is often used in image processing. This is an example with initial matrix A and final result B:

A = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11],
              [12, 13, 14, 15]])

B = np.array([[ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15])

Is there an existing function already that could help me with that? If not, can you give me some hints on how to solve this problem? PS. the matrix A is NxN.

1
  • So it's a zigzag diagonal transversal? Is there a technical name for that? What's the purpose? Commented Sep 11, 2016 at 21:18

2 Answers 2

4

I've been using numpy for several years, and I've never seen such a function.

Here's one way you could do it (not necessarily the most efficient):

In [47]: a
Out[47]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

In [48]: np.concatenate([np.diagonal(a[::-1,:], k)[::(2*(k % 2)-1)] for k in range(1-a.shape[0], a.shape[0])])
Out[48]: array([ 0,  1,  4,  8,  5,  2,  3,  6,  9, 12, 13, 10,  7, 11, 14, 15])

Breaking down the one-liner into separate steps:

a[::-1, :] reverses the rows:

In [59]: a[::-1, :]
Out[59]: 
array([[12, 13, 14, 15],
       [ 8,  9, 10, 11],
       [ 4,  5,  6,  7],
       [ 0,  1,  2,  3]])

(This could also be written a[::-1] or np.flipud(a).)

np.diagonal(a, k) extracts the kth diagonal, where k=0 is the main diagonal. So, for example,

In [65]: np.diagonal(a[::-1, :], -3)
Out[65]: array([0])

In [66]: np.diagonal(a[::-1, :], -2)
Out[66]: array([4, 1])

In [67]: np.diagonal(a[::-1, :], 0)
Out[67]: array([12,  9,  6,  3])

In [68]: np.diagonal(a[::-1, :], 2)
Out[68]: array([14, 11])

In the list comprehension, k gives the diagonal to be extracted. We want to reverse the elements in every other diagonal. The expression 2*(k % 2) - 1 gives the values 1, -1, 1, ... as k varies from -3 to 3. Indexing with [::1] leaves the order of the array being indexed unchanged, and indexing with [::-1] reverses the order of the array. So np.diagonal(a[::-1, :], k)[::(2*(k % 2)-1)] gives the kth diagonal, but with every other diagonal reversed:

In [71]: [np.diagonal(a[::-1,:], k)[::(2*(k % 2)-1)] for k in range(1-a.shape[0], a.shape[0])]
Out[71]: 
[array([0]),
 array([1, 4]),
 array([8, 5, 2]),
 array([ 3,  6,  9, 12]),
 array([13, 10,  7]),
 array([11, 14]),
 array([15])]

np.concatenate() puts them all into a single array:

In [72]: np.concatenate([np.diagonal(a[::-1,:], k)[::(2*(k % 2)-1)] for k in range(1-a.shape[0], a.shape[0])])
Out[72]: array([ 0,  1,  4,  8,  5,  2,  3,  6,  9, 12, 13, 10,  7, 11, 14, 15])
Sign up to request clarification or add additional context in comments.

Comments

2

I found discussion of zigzag scan for MATLAB, but not much for numpy. One project appears to use a hardcoded indexing array for 8x8 blocks

https://github.com/lot9s/lfv-compression/blob/master/scripts/our_mpeg/zigzag.py

ZIG = np.array([[0,  1,  5,  6,  14, 15, 27, 28],
               [2,  4,  7,  13, 16, 26, 29, 42],
               [3,  8,  12, 17, 25, 30, 41, 43],
               [9,  11, 18, 24, 31, 40, 44,53],
               [10, 19, 23, 32, 39, 45, 52,54],
               [20, 22, 33, 38, 46, 51, 55,60],
               [21, 34, 37, 47, 50, 56, 59,61],
               [35, 36, 48, 49, 57, 58, 62,63]])

Apparently it's used jpeg and mpeg compression.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.