a428tm
a428tm

Reputation: 13

How to replace scipy.spatial.distance with np.linalg.norm in Python

I am following this tutorial to implement object tracking for my project - https://www.pyimagesearch.com/2018/07/23/simple-object-tracking-with-opencv/

Method is to find centroids of detected objects in the initial frame, and then calculate the shortest distance to the other centroids of detected objects that show up on the next frame. Assumption is that a centroid that is closest would be a same object.

In the tutorial -

from scipy.spatial import distance as dist
...
D = dist.cdist(np.array(objectCentroids), newCentroids)

is used to calculate the distance (Euclidean Distance). Unfortunately, I cannot use scipy module as I am trying to deploy this to AWS Lambda (size limit). In this case, the recommendation is to use this - https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.norm.html

D = np.linalg.norm(objectCentroids – newCentroids)

The issue with this is that, unlike dist.cdist, where it computes all and any matrix, np.linalg.norm only outputs 1 value, which is calculated after newCentroids is subtracted from objectCentroids matrix. I am about to loop over n times (however big the matrix is) and append to another matrix to construct the result I need. However, I wasn't sure if my understanding of this concept is correct or not, so I wanted to seek out some help. If anyone knows of a better way, I would appreciate any pointer.

UPDATE

Based on the feedback/answer I got, I updated the code a bit, and well... it seems to be working -

n = arrayObjectCentroids.shape[0]
m = inputCentroids.shape[0]

T = []

for i in range(0,n):
    for z in range(0,m):
        Tv = np.linalg.norm(arrayObjectCentroids[i] - inputCentroids[z])
        # print(f'Tv is \n {Tv}')
        T = np.append(T, Tv)
        # print(f'T is \n {T}')
    print(f'new T is \n {T}')   
D = np.reshape(T, (n, m))
print(f'D is \n {D}')

In this case, if there is one object and moving a little -

newCentroids is [[224 86]], and the shape of it is (1, 2)... objectCentroids is [[224 86]], and the shape objectCentroids is (1, 2)

D is[[0.]]

If I have 3 objects, -

new Centroids is

 [[228  79]
 [ 45 127]
 [103 123]]

shape of inputCentroids is (3, 2)

objectCentroids is

 [[228  79]
 [ 45 127]
 [103 123]]

shape objectCentroids is (3, 2)

D is

 [[  0.         189.19038031 132.51792332]
 [189.19038031   0.          58.13776741]
 [132.51792332  58.13776741   0.        ]]

Great that it works, but I feel like this may not be the best solution out there, and if you have any pointer, I would appreciate it! Thanks!

Upvotes: 1

Views: 444

Answers (2)

DrBwts
DrBwts

Reputation: 3657

EDIT: Edited code to address comments below

If in your case you have vectors in Euclidean space then np.linalg.norm will return the length of that vector.

So objectCentroid – newCentroid will give you the vector between the point at objectCentroid and the point at newCentroid. Note that is between 2 points and not an array containing ALL points.

To get all combinations of points I've used itertools & then reshaped the array to give the same output as dist

import numpy as np
from scipy.spatial import distance as dist
import itertools

# Example data
objectCentroids = np.array([[0,0,0],[1,1,1],[2,2,2], [3,3,3]])
newCentroids    = np.array([[4,4,4],[5,5,5],[6,6,6],[7,7,7]])
comb            = list(itertools.product(objectCentroids, newCentroids))
all_dist        = [] 

for pair in comb:

    dis = np.linalg.norm((pair[0] - pair[1]))
    all_dist.append(dis)

all_dist = np.reshape(all_dist, (len(objectCentroids), len(objectCentroids)))
D        = dist.cdist(objectCentroids, newCentroids)   

print(D)
print(" ")
print(all_dist)

Upvotes: 1

Joe
Joe

Reputation: 7121

You can use Numpy broadcasting to create a distance matrix.

Read about it here and here.

The basic idea is:

Stack (reshape) your centroids as (1, n, 3) and (n, 1, 3) where the last dimension with shape 3 is (x,y,z). Then subtract the arrays and use np.linalg.norm to calculate the distance along axis ... hm ... probably the last one. That should yield a square (n,n) distance matrix.

Upvotes: 0

Related Questions