• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bethgelab / foolbox / 8137716344

22 Jan 2024 10:53PM UTC coverage: 98.47%. Remained the same
8137716344

push

github

web-flow
Bump pillow from 10.1.0 to 10.2.0 in /tests (#718)

Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.1.0...10.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

3475 of 3529 relevant lines covered (98.47%)

7.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/foolbox/attacks/spatial_attack_transformations.py
1
from typing import Tuple, Any
10✔
2
import numpy as np
10✔
3
import math
10✔
4
from eagerpy import astensor, Tensor
10✔
5
from eagerpy.tensor import TensorFlowTensor, PyTorchTensor
10✔
6

7

8
def rotate_and_shift(
10✔
9
    inputs: Tensor,
10
    translation: Tuple[float, float] = (0.0, 0.0),
11
    rotation: float = 0.0,
12
) -> Any:
13
    rotation = rotation * math.pi / 180.0
4✔
14
    if isinstance(inputs, TensorFlowTensor):
4✔
15
        transformed_tensor = transform_tf(inputs, translation, rotation)
2✔
16
    elif isinstance(inputs, PyTorchTensor):
2✔
17
        transformed_tensor = transform_pt(inputs, translation, rotation)
2✔
18
    else:
19
        raise NotImplementedError()
20

21
    return transformed_tensor
4✔
22

23

24
def transform_pt(
10✔
25
    x_e: Tensor,
26
    translation: Tuple[float, float] = (0.0, 0.0),
27
    rotation: float = 0.0,
28
) -> Any:
29
    import torch
2✔
30

31
    # x_e shape: (bs, nch, x, y)
32
    # rotation in rad, translation in pixel
33
    # angles: scalar or Tensor with (bs,)
34
    bs = x_e.shape[0]
2✔
35
    theta = np.zeros((2, 3)).astype(np.float32)
2✔
36
    theta[0, :] = [np.cos(rotation), -np.sin(rotation), translation[0]]
2✔
37
    theta[1, :] = [np.sin(rotation), np.cos(rotation), translation[1]]
2✔
38
    theta = np.tile(theta[None], (bs, 1, 1)).reshape(bs, 2, 3)
2✔
39
    # convert from pixels to relative translation, (bs, n_ch, x, y)
40
    theta[:, 0, 2] /= x_e.shape[2] / 2.0
2✔
41
    theta[:, 1, 2] /= x_e.shape[3] / 2.0
2✔
42

43
    # to pt
44
    x = x_e.raw
2✔
45
    theta_t = torch.tensor(theta, device=x.device)
2✔
46

47
    assert len(x.shape) == 4
2✔
48
    assert theta_t.shape[1:] == (2, 3)
2✔
49

50
    (
2✔
51
        bs,
52
        _,
53
        n_x,
54
        n_y,
55
    ) = x.shape
56

57
    def create_meshgrid(x: torch.Tensor) -> torch.Tensor:
2✔
58
        space_x = torch.linspace(-1, 1, n_x, device=x.device)
2✔
59
        space_y = torch.linspace(-1, 1, n_y, device=x.device)
2✔
60
        meshgrid = torch.meshgrid([space_x, space_y])
2✔
61
        ones = torch.ones(meshgrid[0].shape, device=x.device)
2✔
62
        gridder = torch.stack([meshgrid[1], meshgrid[0], ones], dim=2)
2✔
63
        grid = gridder[None, ...].repeat(bs, 1, 1, 1)[..., None]
2✔
64
        return grid
2✔
65

66
    meshgrid = create_meshgrid(x)
2✔
67
    theta_t = theta_t[:, None, None, :, :].repeat(1, n_x, n_y, 1, 1)
2✔
68
    new_coords = torch.matmul(theta_t, meshgrid)
2✔
69
    new_coords = new_coords.squeeze_(-1)
2✔
70

71
    # align_corners=True to match tf implementation
72
    transformed_images = torch.nn.functional.grid_sample(
2✔
73
        x, new_coords, mode="bilinear", padding_mode="zeros", align_corners=True
74
    )
75
    return astensor(transformed_images)
2✔
76

77

78
# adapted adapted from
79
# https://github.com/kevinzakka/spatial-transformer-network/blob/master/stn/transformer.py
80
# state @375f990 on 3 Jun 2018
81
def transform_tf(
10✔
82
    x_e: Tensor,
83
    translation: Tuple[float, float] = (0.0, 0.0),
84
    rotation: float = 0.0,
85
) -> Any:
86
    """
87
    Input
88
    - x: Ep tensor of shape (bs, n_x, n_y, C).
89
    - translation: tuple of x, y translation in pixels
90
    - rotation: rotation in rad
91

92
    Returns
93
    - out_fmap: transformed input feature map. Tensor of size (bs, n_x, n_y, C).
94
    Notes
95

96
    References:
97
    [#Jade]: 'Spatial Transformer Networks', Jaderberg et. al,
98
         (https://arxiv.org/abs/1506.02025)
99
    """
100
    import tensorflow as tf
2✔
101

102
    bs = x_e.shape[0]
2✔
103
    theta = np.zeros((2, 3)).astype(np.float32)
2✔
104

105
    theta[0, :] = [np.cos(rotation), -np.sin(rotation), translation[0]]
2✔
106
    theta[1, :] = [np.sin(rotation), np.cos(rotation), translation[1]]
2✔
107
    theta = np.tile(theta[None], (bs, 1, 1)).reshape(bs, 2, 3)
2✔
108
    # convert from pixels to relative translation (bs, x, y, n_ch)
109
    theta[:, 0, 2] /= x_e.shape[1] / 2.0
2✔
110
    theta[:, 1, 2] /= x_e.shape[2] / 2.0
2✔
111

112
    # to tf
113
    theta = tf.convert_to_tensor(theta)
2✔
114
    x = x_e.raw
2✔
115

116
    # grab input dimensions
117
    assert theta.shape[1:] == (2, 3)
2✔
118
    assert len(x.shape) == 4
2✔
119
    bs = tf.shape(x)[0]
2✔
120
    n_x = tf.shape(x)[1]  # height matrix
2✔
121
    n_y = tf.shape(x)[2]  # width matrix
2✔
122

123
    def get_pixel_value(img: Any, x: Any, y: Any) -> Any:
2✔
124
        """
125
        Utility function to get pixel value for coordinate
126
        vectors x and y from a  4D tensor image.
127

128
        Args:
129
        - img: tensor of shape (bs, n_x, n_y, C)
130
        - x: flattened tensor of shape (bs*n_x*n_y,)
131
        - y: flattened tensor of shape (bs*n_x*n_y,)
132

133
        Returns:
134
        - output: tensor of shape (bs, n_x, n_y, C)
135
        """
136
        batch_idx = tf.range(0, bs)
2✔
137
        batch_idx = tf.reshape(batch_idx, (bs, 1, 1))
2✔
138
        b = tf.tile(batch_idx, (1, n_x, n_y))
2✔
139
        indices = tf.stack([b, y, x], 3)
2✔
140
        return tf.gather_nd(img, indices)
2✔
141

142
    def bilinear_sampler(img: Any, x: Any, y: Any) -> Any:
2✔
143
        """
144
        Performs bilinear sampling of the input images according to the
145
        normalized coordinates provided by the sampling grid. Note that
146
        the sampling is done identically for each channel of the input.
147
        To test if the function works properly, output image should be
148
        identical to input image when theta is initialized to identity
149
        transform.
150

151
        Args:
152
        - img: batch of images in (bs, n_x, n_y, C) layout.
153
        - grid: x, y which is the output of affine_grid_generator.
154

155
        Returns:
156
        - out: interpolated images according to grids. Same size as grid.
157
        """
158
        max_y = tf.cast(n_x - 1, "int32")
2✔
159
        max_x = tf.cast(n_y - 1, "int32")
2✔
160

161
        # rescale x and y to [0, n_y-1/n_x-1]
162
        x = tf.cast(x, "float32")
2✔
163
        y = tf.cast(y, "float32")
2✔
164
        x = 0.5 * ((x + 1.0) * tf.cast(max_x, "float32"))
2✔
165
        y = 0.5 * ((y + 1.0) * tf.cast(max_y, "float32"))
2✔
166

167
        # grab 4 nearest corner points for each (x_i, y_i)
168
        x0 = tf.cast(tf.floor(x), "int32")
2✔
169
        x1 = x0 + 1
2✔
170
        y0 = tf.cast(tf.floor(y), "int32")
2✔
171
        y1 = y0 + 1
2✔
172

173
        # clip to range [0, n_x-1/n_y-1] to not violate img boundaries
174
        min_val = 0
2✔
175
        x0 = tf.clip_by_value(x0, min_val, max_x)
2✔
176
        x1 = tf.clip_by_value(x1, min_val, max_x)
2✔
177
        y0 = tf.clip_by_value(y0, min_val, max_y)
2✔
178
        y1 = tf.clip_by_value(y1, min_val, max_y)
2✔
179

180
        # get pixel value at corner coords
181
        Ia = get_pixel_value(img, x0, y0)
2✔
182
        Ib = get_pixel_value(img, x0, y1)
2✔
183
        Ic = get_pixel_value(img, x1, y0)
2✔
184
        Id = get_pixel_value(img, x1, y1)
2✔
185

186
        # recast as float for delta calculation
187
        x0 = tf.cast(x0, "float32")
2✔
188
        x1 = tf.cast(x1, "float32")
2✔
189
        y0 = tf.cast(y0, "float32")
2✔
190
        y1 = tf.cast(y1, "float32")
2✔
191

192
        # calculate deltas
193
        wa = (x1 - x) * (y1 - y)
2✔
194
        wb = (x1 - x) * (y - y0)
2✔
195
        wc = (x - x0) * (y1 - y)
2✔
196
        wd = (x - x0) * (y - y0)
2✔
197

198
        # add dimension for addition
199
        wa = tf.expand_dims(wa, axis=3)
2✔
200
        wb = tf.expand_dims(wb, axis=3)
2✔
201
        wc = tf.expand_dims(wc, axis=3)
2✔
202
        wd = tf.expand_dims(wd, axis=3)
2✔
203

204
        # compute output
205
        out = tf.add_n([wa * Ia, wb * Ib, wc * Ic, wd * Id])
2✔
206

207
        return out
2✔
208

209
    def affine_grid_generator(height: Any, width: Any, theta: Any) -> Any:
2✔
210
        """
211
        This function returns a sampling grid, which when
212
        used with the bilinear sampler on the input feature
213
        map, will create an output feature map that is an
214
        affine transformation [1] of the input feature map.
215

216
        Args:
217
        - height: desired height of grid/output. Used
218
          to downsample or upsample.
219
        - width: desired width of grid/output. Used
220
          to downsample or upsample.
221
        - theta: affine transform matrices of shape (num_batch, 2, 3).
222
          For each image in the batch, we have 6 theta parameters of
223
          the form (2x3) that define the affine transformation T.
224

225
        Returns:
226
        - normalized grid (-1, 1) of shape (num_batch, 2, n_x, n_y).
227
          The 2nd dimension has 2 components: (x, y) which are the
228
          sampling points of the original image for each point in the
229
          target image.
230
        Note
231
        ----
232
        [1]: the affine transformation allows cropping, translation,
233
             and isotropic scaling.
234
        """
235
        num_batch = tf.shape(theta)[0]
2✔
236

237
        # create normalized 2D grid
238
        x_l = tf.linspace(-1.0, 1.0, width)
2✔
239
        y_l = tf.linspace(-1.0, 1.0, height)
2✔
240
        x_t, y_t = tf.meshgrid(x_l, y_l)
2✔
241

242
        # flatten
243
        x_t_flat = tf.reshape(x_t, [-1])
2✔
244
        y_t_flat = tf.reshape(y_t, [-1])
2✔
245

246
        # reshape to [x_t, y_t , 1] - (homogeneous form)
247
        ones = tf.ones_like(x_t_flat)
2✔
248
        sampling_grid = tf.stack([x_t_flat, y_t_flat, ones])
2✔
249

250
        # repeat grid num_batch times
251
        sampling_grid = tf.expand_dims(sampling_grid, axis=0)
2✔
252
        sampling_grid = tf.tile(sampling_grid, tf.stack([num_batch, 1, 1]))
2✔
253

254
        # cast to float32 (required for matmul)
255
        theta = tf.cast(theta, "float32")
2✔
256
        sampling_grid = tf.cast(sampling_grid, "float32")
2✔
257

258
        # transform the sampling grid - batch multiply
259
        batch_grids = tf.matmul(theta, sampling_grid)
2✔
260
        # batch grid has shape (num_batch, 2, n_x*n_y)
261

262
        # reshape to (num_batch, n_x, n_y, 2)
263
        batch_grids = tf.reshape(batch_grids, [num_batch, 2, height, width])
2✔
264

265
        return batch_grids
2✔
266

267
    batch_grids = affine_grid_generator(n_x, n_y, theta)
2✔
268

269
    x_s = batch_grids[:, 0, :, :]
2✔
270
    y_s = batch_grids[:, 1, :, :]
2✔
271

272
    # sample input with grid to get output
273
    transformed_images = bilinear_sampler(x, x_s, y_s)
2✔
274

275
    return astensor(transformed_images)
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc