3

I'm looking for an efficient way to perform submatrix operations over a larger matrix without resorting to for loops.

I'm currently doing the operation (for a 3x3 window):

newMatrix = numpy.zeros([numRows, numCols])
for i in range(1, numRows-1):
    for j in range(1, numCols-1):
        sub = matrix[i-1:i+2, j-1:j+2]
        newMatrix[i][j] = ... #do things with sub matrix

This is considerably slower than normal operations with numpy matrices. Is there anything numpy has to offer to solve this, or is that hoping for too much?

Edit: Specific example

xWeight = numpy.array([[-1./8, 0, 1./8], [-2./8, 0, 2./8], [-1./8, 0, 1./8]])
yWeight = numpy.array([[1./8, 2./8, 1./8], [0, 0, 0], [-1./8, -2./8, -1./8]])

Inside loop:

        dz_dx = numpy.sum(xWeight * sub)
        dz_dy = numpy.sum(yWeight * sub)
6
  • 2
    Are you trying to do image processing with numpy? Commented Aug 15, 2013 at 21:18
  • Is that matrix on line 4 supposed to be newMatrix? Commented Aug 15, 2013 at 21:19
  • 3
    What exactly do you do in "do things with sub matrix"? If you do not specify what are you doing, we cannot help you vectorize your code. Commented Aug 15, 2013 at 21:22
  • Jim: No, but I imagine there's cross over. Brionius: No, that the old matrix the submatrix is extracted from. Viktor: I intentionally left it vague, because I'm looking for general guidance on this type of problem, of which there are several, not looking for a specific fix. But for example see edits. Commented Aug 15, 2013 at 22:05
  • Where do the dz elements go? Stackoverflow is really center on discrete examples and specific fixes. Commented Aug 15, 2013 at 22:21

4 Answers 4

3

It seems to me that you're trying to do a simple convolution?

def do(m):
    rows, cols = m.shape
    newMatrix = np.zeros_like(m)
    for i in range(1, rows-1):
        for j in range(1, cols-1):
            sub = matrix[i-1:i+2, j-1:j+2]
            newMatrix[i][j] = numpy.sum(xWeight * sub)
    return newMatrix[1:-1, 1:-1]
>>> res1 = do(matrix)
>>> res2 = scipy.signal.convolve2d(matrix, xWeight)[2:-2,2:-2]
>>> np.allclose(np.abs(res1), np.abs(res2))
True

Didn't went into details about the sign, but that should hopefully put you on the right track.

Sign up to request clarification or add additional context in comments.

1 Comment

This looks like a neater solution for this specific problem.
3

I found a solution in numpy.lib.stride_tricks

from numpy.lib.stride_tricks import as_strided

In the method:

    expansion = stride.as_strided(matrix, shape = (numRows-2, numCols-2, 3, 3), strides = matrix.strides * 2)
    xWeight = numpy.array([[-1./8, 0, 1./8], [-2./8, 0, 2./8], [-1./8, 0, 1./8]])
    yWeight = numpy.array([[1./8, 2./8, 1./8], [0, 0, 0], [-1./8, -2./8, -1./8]])

    dx = xWeight * expansion
    dy = yWeight * expansion

    dx = numpy.sum(numpy.sum(dx, axis=3), axis=2)
    dy = numpy.sum(numpy.sum(dy, axis=3), axis=2)

There may well be a better solution, but this is sufficiently simple and general purpose for what I was after. This went through a 1600x1200 matrix in 3.41 seconds, vs 188.47 seconds using for loops.

(Feel free to offer said better solution, if you have it)

Comments

2

Use scipy instead for image processing operations:

http://scipy-lectures.github.io/advanced/image_processing/

Comments

1

It seems you can use np.ix_, see this example from the documentation:

a = np.arange(10).reshape(2, 5)
#array([[0, 1, 2, 3, 4],
#       [5, 6, 7, 8, 9]])

ixgrid = np.ix_([0,1], [2,4])

a[ixgrid]
#array([[2, 4],
#       [7, 9]])

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.