-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdistance.py
More file actions
87 lines (74 loc) · 2.81 KB
/
Copy pathdistance.py
File metadata and controls
87 lines (74 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
'''
Commonly used metrics for evaluating saliency map performance.
'''
import numpy as np
from scipy import stats
def normalize(x, method='standard', axis=None):
'''Normalizes the input with specified method.
Parameters
----------
x : array-like
method : string, optional
Valid values for method are:
- 'standard': mean=0, std=1
- 'range': min=0, max=1
- 'sum': sum=1
axis : int, optional
Axis perpendicular to which array is sliced and normalized.
If None, array is flattened and normalized.
Returns
-------
res : numpy.ndarray
Normalized array.
'''
# TODO: Prevent divided by zero if the map is flat
x = np.array(x, copy=False)
if axis is not None:
y = np.rollaxis(x, axis).reshape([x.shape[axis], -1])
shape = np.ones(len(x.shape))
shape[axis] = x.shape[axis]
if method == 'standard':
res = (x - np.mean(y, axis=1).reshape(shape)) / np.std(y, axis=1).reshape(shape)
elif method == 'range':
res = (x - np.min(y, axis=1).reshape(shape)) / (np.max(y, axis=1) - np.min(y, axis=1)).reshape(shape)
elif method == 'sum':
res = x / np.float_(np.sum(y, axis=1).reshape(shape))
else:
raise ValueError('method not in {"standard", "range", "sum"}')
else:
if method == 'standard':
res = (x - np.mean(x)) / np.std(x)
elif method == 'range':
res = (x - np.min(x)) / (np.max(x) - np.min(x))
elif method == 'sum':
res = x / float(np.sum(x))
else:
raise ValueError('method not in {"standard", "range", "sum"}')
return res
def sliced_wasserstein(X, Y, num_proj=50):
'''Takes:
X: 2d (or nd) histogram
Y: 2d (or nd) histogram
num_proj: Number of random projections to compute the mean over
---
returns:
mean_emd_dist'''
#% Implementation of the (non-generalized) sliced wasserstein (EMD) for 2d distributions as described here: https://arxiv.org/abs/1902.00434 %#
# X and Y should be a 2d histogram
# Code adapted from stackoverflow user: Dougal - https://stats.stackexchange.com/questions/404775/calculate-earth-movers-distance-for-two-grayscale-images
dim = X.shape[1]
ests = []
#X = normalize(X, method='range')
#Y = normalize(Y, method='range')
#X = normalize(X, method='sum')
#Y = normalize(Y, method='sum')
for x in range(num_proj):
# sample uniformly from the unit sphere
dir = np.random.rand(dim)
dir /= np.linalg.norm(dir)
# project the data
X_proj = X @ dir
Y_proj = Y @ dir
# compute 1d wasserstein
ests.append(stats.wasserstein_distance(np.arange(dim), np.arange(dim), X_proj, Y_proj))
return np.mean(ests)