skimage.measure.block_reduce with lambda function

Viewed 716

I have an array:

import numpy as np
arr = np.random.randint(0,10,size=[8,8])

arr is:

array([[9, 1, 8, 2, 0, 4],
       [0, 7, 6, 9, 7, 5],
       [0, 7, 1, 6, 6, 2],
       [3, 6, 3, 3, 8, 1]])

I want to reduce the size of this array using skimage.measure.block_reduce. I do

from skimage import measure as sm
reduced_arr = sm.block_reduce(arr, block_size=(4,6), func=np.max)

reduced_arr is:

array([[6, 7],
       [9, 7]])

I try to achieve the same thing using a lambda function:

reduced_arr = sm.block_reduce(arr, block_size=(2,4), func= lambda block: np.max(block))

Then I get an error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-115-a11dc12d3db0> in <module>()
----> 1 reduced_arr = sm.block_reduce(arr, block_size=(2,4), func= lambda block: np.max(block))

/usr/local/lib/python3.7/dist-packages/skimage/measure/block.py in block_reduce(image, block_size, func, cval, func_kwargs)
     85 
     86     return func(blocked, axis=tuple(range(image.ndim, blocked.ndim)),
---> 87                 **func_kwargs)

TypeError: <lambda>() got an unexpected keyword argument 'axis'

How to use block_reduce with lambda functions?

My real world use case is more complicated than just a premade numpy function, that is why I need it.

1 Answers

Not sure if this question still need an answer, but I saw another one, so let's give a try.

Following the doc of skimage, it's specified the function given in argument should "implement an axis parameter". Quickly looking for np.mean in the numpy doc and you see that indeed it implements an axis parameter.

Therefore in the source of block reduce itself, axis parameter is called on func :

def block_reduce(image, block_size=2, func=np.sum, cval=0, func_kwargs=None):
      ...
      return func(blocked, axis=tuple(range(image.ndim, blocked.ndim)),
                         **func_kwargs)

That being said, your custom function should then implement such behaviour. With a quick fix, your example will correctly work now :


import numpy as np
arr = np.random.randint(0,10,size=[8,8])
arr
Out[53]: 
array([[3, 3, 2, 8, 0, 4, 2, 6],
       [8, 9, 8, 5, 1, 6, 1, 0],
       [9, 2, 6, 9, 7, 9, 1, 6],
       [3, 6, 8, 2, 8, 2, 1, 3],
       [8, 0, 3, 3, 9, 6, 5, 4],
       [5, 2, 9, 3, 8, 5, 9, 8],
       [2, 9, 0, 7, 0, 0, 5, 8],
       [6, 4, 7, 1, 9, 9, 6, 9]])

from skimage import measure as sm
reduced_arr = sm.block_reduce(arr, block_size=(4,6), func=np.max)
reduced_arr
Out[55]: 
array([[9, 6],
       [9, 9]])

reduced_arr_with_lambda = sm.block_reduce(arr, block_size=(4,6), 
                                          func= lambda block, axis: np.max(block, axis))
reduced_arr_with_lambda
Out[57]: 
array([[9, 6],
       [9, 9]])

np.testing.assert_array_equal(reduced_arr, reduced_arr_with_lambda)

Finally, for a more spicy function, you need to understand how tuple axis work, and for non-numpy function implement a way to use axis. To be honest, it's tedious, so my advice is to stick with numpy function.

Related