Implementation from MATLAB to Python without itera

2019-08-15 14:38发布

问题:

Suppose you have the following code in MATLAB:

    [xi yi imv] = find(imagee+0.1);
    imv = imv - 0.1;
    wv = abs(imv*ones(1,length(imv)) - ones(length(imv),1)*imv');

And you want to implement this code in Python. Imagee is a predefined, synthetic image represented by a 10x10 array with values 0,1,2. What would be the most efficient method of accomplishing this? I know you could iteratively walk through the entire matrix and modify values as you go, but I'm sure python has a faster method than that.

EDIT: to clarify on imagee:(I have translated this to python already)

    C= np.zeros((L,L), int)
    C[:L/2,:L/2]=1
    C[L/2:L,:L/2]=2

回答1:

I see you're already using numpy, which is a step in the right direction. Now, let's go through each statement one at a time and get the numpy equivalent of what you're after. It says that your matrix is 10 x 10, and so I'm going to assume that L = 10. Here's what we'll start with (in IPython):

In [2]: import numpy as np

In [3]: L = 10

In [4]: C= np.zeros((L,L), int)

In [5]: C[:L/2,:L/2]=1

In [6]: C[L/2:L,:L/2]=2

In [7]: C
Out[7]:
array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 0, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]])

Now, let's go through each line one at a time.


[xi yi imv] = find(imagee+0.1);

imv basically gives you a vector of all values in imagee+0.1 that are non-zero. However, what you need to keep in mind is that MATLAB will return these values in column-major order whereas numpy will do the same operation in row-major order. If you want to replicate the same behaviour in Python, you'll need to transpose the matrix first. As such, I'll create a new matrix which transposes imagee and adds 0.1 to every entry for convenience. However, I'm a bit confused because if imagee already consists of 0,1,2, if you add every value in this matrix by 0.1, imv will return all values in imagee+0.1.... and that seems pointless to me. Nevertheless, you can use numpy.nonzero to give you the locations of the non-zero elements. Once you find these non-zero elements, you can simply index into the transpose of C added with 0.1 to get the values you want. numpy.nonzero will return a tuple of two elements where the first element is an array that tells you the row locations of those values that were non-zero in C+0.1 and the second element is an array that tells you the column locations that were non-zero in C+0.1:

In [9]: CT = C.T + 0.1

In [10]: ind = CT.nonzero()

In [11]: imv = CT[ind[0], ind[1]]

In [12]: imv
Out[12]:
array([ 1.1,  1.1,  1.1,  1.1,  1.1,  2.1,  2.1,  2.1,  2.1,  2.1,  1.1,
    1.1,  1.1,  1.1,  1.1,  2.1,  2.1,  2.1,  2.1,  2.1,  1.1,  1.1,
    1.1,  1.1,  1.1,  2.1,  2.1,  2.1,  2.1,  2.1,  1.1,  1.1,  1.1,
    1.1,  1.1,  2.1,  2.1,  2.1,  2.1,  2.1,  1.1,  1.1,  1.1,  1.1,
    1.1,  2.1,  2.1,  2.1,  2.1,  2.1,  0.1,  0.1,  0.1,  0.1,  0.1,
    0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,
    0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,
    0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,
    0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,
    0.1])

If you did the same operation in MATLAB, you'll notice that imv in both Python and MATLAB give the same order of values.


imv = imv - 0.1;

That's easy:

In [22]: imv = imv - 0.1

In [23]: imv
Out[23]:
array([ 1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  2.,  2.,  1.,  1.,  1.,
        1.,  1.,  2.,  2.,  2.,  2.,  2.,  1.,  1.,  1.,  1.,  1.,  2.,
        2.,  2.,  2.,  2.,  1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  2.,
        2.,  1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  2.,  2.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

wv = abs(imv*ones(1,length(imv)) - ones(length(imv),1)*imv');

The first part of this statement (inside the abs call) is performing the outer product of two vectors. In MATLAB, imv would be N x 1, and you are multiplying this with a 1 x N vector of ones. You can use numpy.outer to help you do this outer product step. Take note that for 1D arrays, numpy does not distinguish between row vectors and column vectors and so multiplying a vector with the transpose of another unfortunately won't give you what you expect. However, if you want this behaviour, you must explicitly define a 2D matrix that has a singleton dimension of 1 (either the rows or the columns), but let's put that aside for this post.

The second part of this statement also performs an outer product but on the transposed version of the first part of the statement.

Therefore:

In [24]: ones_vector = np.ones(len(imv))

In [25]: wv = np.abs(np.outer(imv, ones_vector) - np.outer(ones_vector, imv))

In [26]: wv
Out[26]:
array([[ 0.,  0.,  0., ...,  1.,  1.,  1.],
   [ 0.,  0.,  0., ...,  1.,  1.,  1.],
   [ 0.,  0.,  0., ...,  1.,  1.,  1.],
   ...,
   [ 1.,  1.,  1., ...,  0.,  0.,  0.],
   [ 1.,  1.,  1., ...,  0.,  0.,  0.],
   [ 1.,  1.,  1., ...,  0.,  0.,  0.]])

The first part of the code declares a vector of ones for convenience. After that, we calculate what you desire.


Hope this helps!