@@ -59,12 +59,13 @@ class RobustDPatch(EvasionAttack):
5959 "learning_rate" ,
6060 "max_iter" ,
6161 "batch_size" ,
62- "verbose" ,
6362 "patch_location" ,
6463 "crop_range" ,
6564 "brightness_range" ,
6665 "rotation_weights" ,
6766 "sample_size" ,
67+ "targeted" ,
68+ "verbose" ,
6869 ]
6970
7071 _estimator_requirements = (BaseEstimator , LossGradientsMixin , ObjectDetectorMixin )
@@ -81,6 +82,7 @@ def __init__(
8182 learning_rate : float = 5.0 ,
8283 max_iter : int = 500 ,
8384 batch_size : int = 16 ,
85+ targeted : bool = False ,
8486 verbose : bool = True ,
8587 ):
8688 """
@@ -96,6 +98,7 @@ def __init__(
9698 :param learning_rate: The learning rate of the optimization.
9799 :param max_iter: The number of optimization steps.
98100 :param batch_size: The size of the training batch.
101+ :param targeted: Indicates whether the attack is targeted (True) or untargeted (False).
99102 :param verbose: Show progress bars.
100103 """
101104
@@ -120,9 +123,10 @@ def __init__(
120123 self .brightness_range = brightness_range
121124 self .rotation_weights = rotation_weights
122125 self .sample_size = sample_size
126+ self ._targeted = targeted
123127 self ._check_params ()
124128
125- def generate (self , x : np .ndarray , y : Optional [np .ndarray ] = None , ** kwargs ) -> np .ndarray :
129+ def generate (self , x : np .ndarray , y : Optional [List [ Dict [ str , np .ndarray ]] ] = None , ** kwargs ) -> np .ndarray :
126130 """
127131 Generate RobustDPatch.
128132
@@ -133,7 +137,9 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
133137 channel_index = 1 if self .estimator .channels_first else x .ndim - 1
134138 if x .shape [channel_index ] != self .patch_shape [channel_index - 1 ]:
135139 raise ValueError ("The color channel index of the images and the patch have to be identical." )
136- if y is not None :
140+ if y is None and self .targeted :
141+ raise ValueError ("The targeted version of RobustDPatch attack requires target labels provided to `y`." )
142+ if y is not None and not self .targeted :
137143 raise ValueError ("The RobustDPatch attack does not use target labels." )
138144 if x .ndim != 4 :
139145 raise ValueError ("The adversarial patch can only be applied to images." )
@@ -183,9 +189,14 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
183189 i_batch_start = i_batch * self .batch_size
184190 i_batch_end = min ((i_batch + 1 ) * self .batch_size , x .shape [0 ])
185191
192+ if y is None :
193+ y_batch = y
194+ else :
195+ y_batch = y [i_batch_start :i_batch_end ]
196+
186197 # Sample and apply the random transformations:
187198 patched_images , patch_target , transforms = self ._augment_images_with_patch (
188- x [i_batch_start :i_batch_end ], self ._patch , channels_first = self .estimator .channels_first
199+ x [i_batch_start :i_batch_end ], y_batch , self ._patch , channels_first = self .estimator .channels_first
189200 )
190201
191202 gradients = self .estimator .loss_gradient (
@@ -206,7 +217,7 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
206217
207218 patch_gradients_old = patch_gradients
208219
209- self ._patch = self ._patch + np .sign (patch_gradients ) * self .learning_rate
220+ self ._patch = self ._patch + np .sign (patch_gradients ) * ( 1 - 2 * int ( self . targeted )) * self .learning_rate
210221
211222 if self .estimator .clip_values is not None :
212223 self ._patch = np .clip (
@@ -218,12 +229,13 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
218229 return self ._patch
219230
220231 def _augment_images_with_patch (
221- self , x : np .ndarray , patch : np .ndarray , channels_first : bool
232+ self , x : np .ndarray , y : Optional [ List [ Dict [ str , np . ndarray ]]], patch : np .ndarray , channels_first : bool
222233 ) -> Tuple [np .ndarray , List [Dict [str , np .ndarray ]], Dict [str , Union [int , float ]]]:
223234 """
224235 Augment images with patch.
225236
226237 :param x: Sample images.
238+ :param y: Target labels.
227239 :param patch: The patch to be applied.
228240 :param channels_first: Set channels first or last.
229241 """
@@ -269,9 +281,6 @@ def _augment_images_with_patch(
269281 y_b = y [i_image ]["boxes" ].copy ()
270282 image_width = x .shape [2 ]
271283 image_height = x .shape [1 ]
272- # TODO: fix. This assumes y_b is in Pytorch box format [xmin,ymin,xmax,ymax],
273- # where 0 <= x < W and 0 <= y < H. Tensorflow box format [ymin,xmin,ymax,xmax],
274- # where 0 <= x < 1 and 0 <= y < 1, is not supported
275284 x_1_arr = y_b [:, 0 ]
276285 y_1_arr = y_b [:, 1 ]
277286 x_2_arr = y_b [:, 2 ]
@@ -486,3 +495,6 @@ def _check_params(self) -> None:
486495 raise ValueError ("The EOT sample size must be of type int." )
487496 if self .sample_size <= 0 :
488497 raise ValueError ("The EOT sample size must be greater than 0." )
498+
499+ if not isinstance (self .targeted , bool ):
500+ raise ValueError ("The argument `targeted` has to be of type bool." )
0 commit comments