drazulay
drazulay

Reputation: 35

Despite many examples online, I cannot get my MATLAB repmat equivalent working in python

I am trying to do some numpy matrix math because I need to replicate the repmat function from MATLAB. I know there are a thousand examples online, but I cannot seem to get any of them working.

The following is the code I am trying to run:

def getDMap(image, mapSize):
    newSize = (float(mapSize[0]) / float(image.shape[1]), float(mapSize[1]) / float(image.shape[0]))
    sm = cv.resize(image, (0,0), fx=newSize[0], fy=newSize[1])
    for j in range(0, sm.shape[1]):
        for i in range(0, sm.shape[0]):
            dmap = sm[:,:,:]-np.array([np.tile(sm[j,i,:], (len(sm[0]), len(sm[1]))) for k in xrange(len(sm[2]))])
    return dmap 

The function getDMap(image, mapSize) expects an OpenCV2 HSV image as its image argument, which is a numpy array with 3 dimensions: [:,:,:]. It also expects a tuple with 2 elements as its imSize argument, of course making sure the function passing the arguments takes into account that in numpy arrays the rows and colums are swapped (not: x, y, but: y, x).

newSize then contains a tuple containing fracions that are used to resize the input image to a specific scale, and sm becomes a resized version of the input image. This all works fine.

This is my goal:

The following line:

np.array([np.tile(sm[i,j,:], (len(sm[0]), len(sm[1]))) for k in xrange(len(sm[2]))]),

should function equivalent to the MATLAB expression:

repmat(sm(j,i,:),[size(sm,1) size(sm,2)]),

This is my problem:

Testing this, an OpenCV2 image with dimensions 800x479x3 is passed as the image argument, and (64, 48) (a tuple) is passed as the imSize argument. However when testing this, I get the following ValueError:

dmap = sm[:,:,:]-np.array([np.tile(sm[i,j,:], (len(sm[0]), len(sm[1]))) for k in xrange(len(sm[2]))])
ValueError: operands could not be broadcast together with shapes (48,64,3) (64,64,192)

So it seems that the array dimensions do not match and numpy has a problem with that. But my question is what? And how do I get this working?

Upvotes: 2

Views: 475

Answers (2)

Jaime
Jaime

Reputation: 67457

len(sm[0]) and len(sm[1]) are not the sizes of the first and second dimensions of sm. They are the lengths of the first and second row of sm, and should both return the same value. You probably want to replace them with sm.shape[0] and sm.shape[1], which are equivalent to your Matlab code, although I am not sure that it will work as you expect it to.

Upvotes: 2

hpaulj
hpaulj

Reputation: 231530

These 2 calculations match:

octave:26> sm=reshape(1:12,2,2,3)
octave:27> x=repmat(sm(1,2,:),[size(sm,1) size(sm,2)])
octave:28> x(:,:,2)
   7   7
   7   7


In [45]: sm=np.arange(1,13).reshape(2,2,3,order='F')
In [46]: x=np.tile(sm[0,1,:],[sm.shape[0],sm.shape[1],1])
In [47]: x[:,:,1]
Out[47]: 
array([[7, 7],
       [7, 7]])

This runs:

 sm[:,:,:]-np.array([np.tile(sm[0,1,:], (2,2,1)) for k in xrange(3)])

But it produces a (3,2,2,3) array, with replication on the 1st dimension. I don't think you want that k loop.

What's the intent with?

 for i in ...:
     for j in ...:
         data = ...

You'll only get results from the last iteration. Did you want data += ...? If so, this might work (for a (N,M,K) shaped sm)

np.sum(np.array([sm-np.tile(sm[i,j,:], (N,M,1)) for i in xrange(N) for j in xrange(M)]),axis=0)

z = np.array([np.tile(sm[i,j,:], (N,M,1)) for i in xrange(N) for j in xrange(M)]),axis=0)
np.sum(sm - z, axis=0)  # let numpy broadcast sm

Actually I don't even need the tile. Let broadcasting do the work:

 np.sum(np.array([sm-sm[i,j,:] for i in xrange(N) for j in xrange(M)]),axis=0)

I can get rid of the loops with repeat.

sm1 = sm.reshape(N*M,L)  # combine 1st 2 dim to simplify repeat
z1 = np.repeat(sm1, N*M, axis=0).reshape(N*M,N*M,L)
x1 = np.sum(sm1 - z1, axis=0).reshape(N,M,L)

I can also apply broadcasting to the last case

x4 = np.sum(sm1-sm1[:,None,:], 0).reshape(N,M,L)
# = np.sum(sm1[None,:,:]-sm1[:,None,:], 0).reshape(N,M,L)

With sm I have to expand (and sum) 2 dimensions:

x5 = np.sum(np.sum(sm[None,:,None,:,:]-sm[:,None,:,None,:],0),1)

Upvotes: 2

Related Questions