2

I need to make a function that takes in a vector of indices in high resolution, e.g hr, and outputs their corresponding indices when sampled in low resolution lr.

My thoughts were to create a translation matrix as follows:

In the following matrix, whose high resolution is (6, 12), and whose low resolution is (2, 4)

enter image description here

If the input vector is

v = [0, 1, 4, 24, 36, 42]

I would achieve my translation as

w = m[v] which I would expect to output [0,0,1,0,4,6]

Questions:

  1. Is this the right way to go?
  2. If so, how can I create that m ndarray in numpy?

Also, if there is a better name for this question, please let me know so I could change it.

Gulzar
  • 23,452
  • 27
  • 113
  • 201

1 Answers1

2

Space-efficient way:

import numpy as np

hires = np.array((6, 12))
lowres = np.array((2,4))
h, w = hires // lowres

m = np.arange(np.prod(lowres)).reshape(lowres)
print(m)
# [[0 1 2 3]
#  [4 5 6 7]]

v = [0, 1, 4, 24, 36, 42]
i, j = np.unravel_index(v, hires)
w = m[i // h, j // w]
print(w)
# [0 0 1 0 4 6]

Space-inefficient way:

import numpy as np
hires = np.array((6, 12))
lowres = np.array((2,4))
h, w = hires // lowres

# DON'T DO THIS. INEFFICIENT
m = np.kron(np.arange(np.prod(lowres)).reshape(lowres), np.ones(h, w), )
print(m)
# [[0. 0. 0. 1. 1. 1. 2. 2. 2. 3. 3. 3.]
#  [0. 0. 0. 1. 1. 1. 2. 2. 2. 3. 3. 3.]
#  [0. 0. 0. 1. 1. 1. 2. 2. 2. 3. 3. 3.]
#  [4. 4. 4. 5. 5. 5. 6. 6. 6. 7. 7. 7.]
#  [4. 4. 4. 5. 5. 5. 6. 6. 6. 7. 7. 7.]
#  [4. 4. 4. 5. 5. 5. 6. 6. 6. 7. 7. 7.]]

v = [0, 1, 4, 24, 36, 42]
w = m[np.unravel_index(v, hires)]
print(w)
# [0. 0. 1. 0. 4. 6.]

The main idea here is to use np.unravel_index to convert a "flat index" into a tuple of coordinates given the shape of the array you intend to index into.

For example,

In [446]: np.unravel_index([0, 1, 4, 24, 36, 42], (6, 12))
Out[446]: (array([0, 0, 0, 2, 3, 3]), array([0, 1, 4, 0, 0, 6]))

It returns two indexing arrays which together give the coordinates of the 0th, 1st, 4th, etc. "flattened" elements in an array of shape (6, 12).

The space-inefficient method constructs the big m array and then finds w by indexing m with those coordinates: w = m[np.unravel_index(v, hires)].

The more space-efficent method simply integer-divides the coordinates by the block size (in this case, 3-by-3) to generate low-resolution coordinates. This avoids the need to generate the big matrix m. We can instead use a smaller matrix

In [447]: m = np.arange(np.prod(lowres)).reshape(lowres); m
Out[447]: 
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

and index into that: w = m[i // h, j // w].


You might also be interested in np.ravel_multi_index, which is the inverse of np.unravel_index:

In [451]: np.ravel_multi_index((np.array([0, 0, 0, 2, 3, 3]), np.array([0, 1, 4, 0, 0, 6])), (6, 12))
Out[451]: array([ 0,  1,  4, 24, 36, 42])

It converts the coordinate arrays, i and j, back into v.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Since `hires` is a NumPy array, `//` is equivalent to [`numpy.floor_divide`](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.floor_divide.html), which, as you guessed, does element-wise integer division. – unutbu Dec 31 '18 at 14:21
  • @unbutu hanks for your (wow) quick reply! When trying out your code, I'm getting ` i, j = np.unravel_index(line_hr, hires) TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int64') according to the rule 'same_kind'`. `line_hr` is a ndarray of shape `(138,1)`. What didn't I get? – Gulzar Dec 31 '18 at 18:22
  • `np.unravel_index` expects its first argument to be an *integer* array of indices -- since, after all, indices are integers. So you need to convert `line_hr` from having dtype `float64` to having an integer-valued dtype. You could use `line_hr = line_hr.astype('int64')` for example, which will floor the floats. – unutbu Dec 31 '18 at 18:29
  • It may pay to track down how `line_hr` became a floating-point valued array and correct the problem upstream. Otherwise, beware that `np.array([3.9999999]).astype(int)` equals `array([3])`, so you may need `line_hr.round().astype(int)` to protect against slightly under-valued floats being floored to the wrong int. – unutbu Dec 31 '18 at 18:37