6
\$\begingroup\$

I wish to locate the closest matching NxN block within a WxW window centred at location (x,y) of a larger 2D array. The code below works fine but is very slow for my needs as I need to run this operation many times.

Is there a better way to do this?

Here N = 3, W = 15, x =15, y = 15 and (bestx, besty) is the centre of the best matching block

import numpy as np

## Generate some test data
CurPatch = np.random.randint(20, size=(3, 3))
Data = np.random.randint(20,size=(30,30))

# Current Location 
x,y = 15,15
# Initialise Best Match
bestcost = 999.0
bestx = 0;besty=0

for Wy in xrange(-7,8):
    for Wx in xrange(-7,8):
            Ywj,Ywi = y+Wy,x+Wx 

            cost = 0.0
            for py in xrange(3):
                for px in xrange(3):
                    cost += abs(Data[Ywj+py-1,Ywi+px-1] - CurPatch[py,px]) 
                    if cost >= bestcost:
                       break

            if cost < bestcost:
                bestcost = cost
                besty,bestx = Ywj,Ywi

print besty,bestx
\$\endgroup\$
6
  • \$\begingroup\$ What constitutes the 'closet matching block'? \$\endgroup\$ Commented Jun 19, 2014 at 11:49
  • \$\begingroup\$ Here I am using the sum of absolute difference. Pixel by pixel comparison in the NxN blocks. \$\endgroup\$ Commented Jun 19, 2014 at 12:30
  • \$\begingroup\$ Could you give an approximation of your current running time and give us a target to achieve ? It would help to better compare future reviews. \$\endgroup\$ Commented Jun 19, 2014 at 12:32
  • \$\begingroup\$ Do you consider using numpy an option? \$\endgroup\$ Commented Jun 19, 2014 at 13:57
  • \$\begingroup\$ @Marc-Andre This function within my program takes around 8 minutes to run on my machine. Note that this function is called 30,744 times. I've used a C++ implementation that is able to achieve this in mere seconds. \$\endgroup\$ Commented Jun 19, 2014 at 14:10

2 Answers 2

3
\$\begingroup\$

The following outlines a couple variations of a numpy solution. For the problem sizes in your post, they all clock faster than 100us, so you should get your 30,744 calls in about 3 seconds.

import numpy as np

N = 3

W = 15
x, y = 15, 15

data = np.random.randint(20, size=(30, 30))
current_patch = np.random.randint(20, size=(N, N))

# isolate the patch
x_start = x - W//2
y_start = y - W//2

patch = data[x_start:x_start+W, y_start:y_start+W]

# take a windowed view of the array
from numpy.lib.stride_tricks import as_strided
shape = tuple(np.subtract(patch.shape, N-1)) + (N, N)
windowed_patch = as_strided(patch, shape=shape, strides=patch.strides*2)

# this works, but creates a large intermediate array
cost = np.abs(windowed_patch - current_patch).sum(axis=(-1, -2))

# this is much more memory efficient, but uses squared differences,
# and the fact that (a - b)^2 = a^2 + b^2 - 2*a*b
patch2 = patch*patch
ssqd =  as_strided(patch2, shape=shape,
                   strides=patch2.strides*2).sum(axis=(-1, -2),
                                                 dtype=np.double)
ssqd += np.sum(current_patch * current_patch, dtype=np.double)
ssqd -= 2 * np.einsum('ijkl,kl->ij', windowed_patch, current_patch,
                      dtype=np.double)

# for comparison with the other method
cost2 = windowed_patch - current_patch
cost2 = np.sum(cost2*cost2, axis=(-1, -2))

# with any method, to find the location of the max in cost:
best_X, best_y = np.unravel_index(np.argmax(cost), cost.shape)
\$\endgroup\$
1
\$\begingroup\$

Using sum, you can make your code more concise and slightly more efficient :

cost = sum(abs(Data[Wy+py-1,Wx+px-1] - CurPatch[py,px]) for py in xrange(3) for px in xrange(3))

You can avoid manipulation of Ywj,Ywi by sliding the ranges you are using :

for Wy in xrange(y-7,y+8):
    for Wx in xrange(x-7,x+8):

By doing this, I got tiny tiny performance improvements.

Then, there are a few details of style (mostly about whitespaces) you could change to make your code more pythonic.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.