forked from menpo/menpo
/
functions.py
173 lines (136 loc) · 4.92 KB
/
functions.py
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from __future__ import division
import numpy as np
from menpo.shape import PointCloud
from menpo.transform import Similarity, AlignmentSimilarity
# TODO: document me
def build_sampling_grid(patch_shape):
r"""
"""
patch_shape = np.array(patch_shape)
patch_half_shape = np.require(np.round(patch_shape / 2), dtype=int)
start = -patch_half_shape
end = patch_half_shape + 1
sampling_grid = np.mgrid[start[0]:end[0], start[1]:end[1]]
return sampling_grid.swapaxes(0, 2).swapaxes(0, 1)
# TODO: document me
def extract_local_patches(image, centres, sampling_grid):
r"""
Extract square patches from an image about centres.
Parameters
----------
image : :map:`Image`
The image to extract patches from
centres : :map:`PointCloud`
The centres around which the patches should be extracted
sampling_grid : `ndarray` of shape: ``(patch_shape[0]+1, patch_shape[1]+1,
n_dims)``
The size of the patch in each dimension
Returns
-------
patches : ndarray` of shape: ``n_centres, + patch_shape + n_channels,``
The patches as a single numpy array.
"""
max_x = image.shape[0] - 1
max_y = image.shape[1] - 1
pixels = image.pixels
patch_grid = (sampling_grid[None, ...] +
np.round(centres.points[:, None, None, ...]).astype(int))
X = patch_grid[:, :, :, 0]
Y = patch_grid[:, :, :, 1]
# deal with boundaries
X[X > max_x] = max_x
Y[Y > max_y] = max_y
X[X < 0] = 0
Y[Y < 0] = 0
patches = np.empty(
(centres.n_points,) + sampling_grid.shape[:-1] + (image.n_channels,))
for j, (x, y) in enumerate(zip(X, Y)):
patches[j, ...] = pixels[x, y, :]
return patches
# TODO: Should this be a method on Similarity? AlignableTransforms?
def noisy_align(source, target, noise_std=0.04, rotation=False):
r"""
Constructs and perturbs the optimal similarity transform between source
to the target by adding white noise to its weights.
Parameters
----------
source: :class:`menpo.shape.PointCloud`
The source pointcloud instance used in the alignment
target: :class:`menpo.shape.PointCloud`
The target pointcloud instance used in the alignment
noise_std: float
The standard deviation of the white noise
Default: 0.04
rotation: boolean
If False the second parameter of the Similarity,
which captures captures inplane rotations, is set to 0.
Default:False
Returns
-------
noisy_transform : :class: `menpo.transform.Similarity`
The noisy Similarity Transform
"""
transform = AlignmentSimilarity(source, target, rotation=rotation)
parameters = transform.as_vector()
parameter_range = np.hstack((parameters[:2], target.range()))
noise = (parameter_range * noise_std *
np.random.randn(transform.n_parameters))
return Similarity.identity(source.n_dims).from_vector(parameters + noise)
def align_shape_with_bb(shape, bounding_box):
r"""
Returns the Similarity transform that aligns the provided shape with the
provided bounding box.
Parameters
----------
shape: :class:`menpo.shape.PointCloud`
The shape to be aligned.
bounding_box: (2, 2) ndarray
The bounding box specified as:
np.array([[x_min, y_min], [x_max, y_max]])
Returns
-------
transform : :class: `menpo.transform.Similarity`
The align transform
"""
shape_box = PointCloud(shape.bounds())
bounding_box = PointCloud(bounding_box)
return AlignmentSimilarity(shape_box, bounding_box, rotation=False)
#TODO: Document me
def compute_error(target, ground_truth, error_type='me_norm'):
r"""
"""
gt_points = ground_truth.points
target_points = target.points
if error_type == 'me_norm':
return _compute_me_norm(target_points, gt_points)
elif error_type == 'me':
return _compute_me(target_points, gt_points)
elif error_type == 'rmse':
return _compute_rmse(target_points, gt_points)
else:
raise ValueError("Unknown error_type string selected. Valid options "
"are: me_norm, me, rmse'")
#TODO: Document me
def _compute_rmse(target, ground_truth):
r"""
"""
return np.sqrt(np.mean((target.flatten() - ground_truth.flatten()) ** 2))
#TODO: Document me
def _compute_me(target, ground_truth):
r"""
"""
return np.mean(np.sqrt(np.sum((target - ground_truth) ** 2, axis=-1)))
#TODO: Document me
def _compute_me_norm(target, ground_truth):
r"""
"""
normalizer = np.mean(np.max(ground_truth, axis=0) -
np.min(ground_truth, axis=0))
return _compute_me(target, ground_truth) / normalizer
def compute_cumulative_error(errors, x_axis):
r"""
"""
n_errors = len(errors)
cumulative_error = [np.count_nonzero([errors <= x])
for x in x_axis]
return np.array(cumulative_error) / n_errors