• 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

98.55
/foolbox/attacks/spatial_attack.py
1
from typing import Union, Any, Tuple, Generator
10✔
2
import eagerpy as ep
10✔
3
import numpy as np
10✔
4

5
from ..devutils import atleast_kd
10✔
6

7
from ..criteria import Criterion
10✔
8

9
from .base import Model
10✔
10
from .base import T
10✔
11
from .base import get_is_adversarial
10✔
12
from .base import get_criterion
10✔
13
from .base import Attack
10✔
14
from .spatial_attack_transformations import rotate_and_shift
10✔
15
from .base import raise_if_kwargs
10✔
16
from .base import verify_input_bounds
10✔
17

18

19
class SpatialAttack(Attack):
10✔
20
    """Adversarially chosen rotations and translations. [#Engs]
21
    This implementation is based on the reference implementation by
22
    Madry et al.: https://github.com/MadryLab/adversarial_spatial
23

24
    References:
25
    .. [#Engs] Logan Engstrom*, Brandon Tran*, Dimitris Tsipras*,
26
           Ludwig Schmidt, Aleksander MÄ…dry: "A Rotation and a
27
           Translation Suffice: Fooling CNNs with Simple Transformations",
28
           http://arxiv.org/abs/1712.02779
29
    """
30

31
    def __init__(
10✔
32
        self,
33
        max_translation: float = 3,
34
        max_rotation: float = 30,
35
        num_translations: int = 5,
36
        num_rotations: int = 5,
37
        grid_search: bool = True,
38
        random_steps: int = 100,
39
    ):
40

41
        self.max_trans = max_translation
10✔
42
        self.max_rot = max_rotation
10✔
43

44
        self.grid_search = grid_search
10✔
45

46
        # grid search true
47
        self.num_trans = num_translations
10✔
48
        self.num_rots = num_rotations
10✔
49

50
        # grid search false
51
        self.random_steps = random_steps
10✔
52

53
    def __call__(  # type: ignore
10✔
54
        self,
55
        model: Model,
56
        inputs: T,
57
        criterion: Any,
58
        **kwargs: Any,
59
    ) -> Tuple[T, T, T]:
60
        x, restore_type = ep.astensor_(inputs)
4✔
61
        del inputs
4✔
62
        criterion = get_criterion(criterion)
4✔
63

64
        is_adversarial = get_is_adversarial(criterion, model)
4✔
65

66
        if x.ndim != 4:
4✔
67
            raise NotImplementedError(
68
                "only implemented for inputs with two spatial dimensions (and one channel and one batch dimension)"
69
            )
70

71
        xp = self.run(model, x, criterion)
4✔
72
        success = is_adversarial(xp)
4✔
73

74
        xp_ = restore_type(xp)
4✔
75
        return xp_, xp_, restore_type(success)  # twice to match API
4✔
76

77
    def run(
10✔
78
        self,
79
        model: Model,
80
        inputs: T,
81
        criterion: Union[Criterion, T],
82
        **kwargs: Any,
83
    ) -> T:
84
        raise_if_kwargs(kwargs)
4✔
85

86
        x, restore_type = ep.astensor_(inputs)
4✔
87
        del inputs, kwargs
4✔
88

89
        verify_input_bounds(x, model)
4✔
90

91
        criterion = get_criterion(criterion)
4✔
92
        is_adversarial = get_is_adversarial(criterion, model)
4✔
93

94
        found = is_adversarial(x)
4✔
95
        results = x
4✔
96

97
        def grid_search_generator() -> Generator[Any, Any, Any]:
4✔
98
            dphis = np.linspace(-self.max_rot, self.max_rot, self.num_rots)
4✔
99
            dxs = np.linspace(-self.max_trans, self.max_trans, self.num_trans)
4✔
100
            dys = np.linspace(-self.max_trans, self.max_trans, self.num_trans)
4✔
101
            for dphi in dphis:
4✔
102
                for dx in dxs:
4✔
103
                    for dy in dys:
4✔
104
                        yield dphi, dx, dy
4✔
105

106
        def random_search_generator() -> Generator[Any, Any, Any]:
4✔
107
            dphis = np.random.uniform(-self.max_rot, self.max_rot, self.random_steps)
4✔
108
            dxs = np.random.uniform(-self.max_trans, self.max_trans, self.random_steps)
4✔
109
            dys = np.random.uniform(-self.max_trans, self.max_trans, self.random_steps)
4✔
110
            for dphi, dx, dy in zip(dphis, dxs, dys):
4✔
111
                yield dphi, dx, dy
4✔
112

113
        gen = grid_search_generator() if self.grid_search else random_search_generator()
4✔
114
        for dphi, dx, dy in gen:
4✔
115
            # TODO: reduce the batch size to the ones that haven't been successful
116

117
            x_p = rotate_and_shift(x, translation=(dx, dy), rotation=dphi)
4✔
118
            is_adv = is_adversarial(x_p)
4✔
119
            new_adv = ep.logical_and(is_adv, found.logical_not())
4✔
120

121
            results = ep.where(atleast_kd(new_adv, x_p.ndim), x_p, results)
4✔
122
            found = ep.logical_or(new_adv, found)
4✔
123
            if found.all():
4✔
124
                break  # all images in batch misclassified
4✔
125
        return restore_type(results)
4✔
126

127
    def repeat(self, times: int) -> Attack:
10✔
128
        if self.grid_search:
8✔
129
            raise ValueError(
×
130
                "repeat is not supported if attack is deterministic"
131
            )  # attack is deterministic
132
        else:
133
            random_steps = self.random_steps * times
8✔
134
            return SpatialAttack(
8✔
135
                max_translation=self.max_trans,
136
                max_rotation=self.max_rot,
137
                num_translations=self.num_trans,
138
                num_rotations=self.num_rots,
139
                grid_search=self.grid_search,
140
                random_steps=random_steps,
141
            )
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