2323import logging
2424import math
2525import random
26- from typing import Dict , List , Optional , Tuple , TYPE_CHECKING
26+ from typing import Dict , List , Optional , Tuple , Union , TYPE_CHECKING
2727
2828import numpy as np
2929from tqdm import trange
@@ -78,15 +78,26 @@ def __init__(
7878 self .learning_rate = learning_rate
7979 self .max_iter = max_iter
8080 self .batch_size = batch_size
81- self ._patch = np .ones (shape = patch_shape ) * (self .estimator .clip_values [1 ] + self .estimator .clip_values [0 ]) / 2.0
81+ self ._patch = np .random .randint (
82+ self .estimator .clip_values [0 ], self .estimator .clip_values [1 ], size = patch_shape
83+ ).astype (np .float32 )
8284 self ._check_params ()
8385
84- def generate (self , x : np .ndarray , y : Optional [np .ndarray ] = None , ** kwargs ) -> np .ndarray :
86+ self .target_label = []
87+
88+ def generate (
89+ self ,
90+ x : np .ndarray ,
91+ y : Optional [np .ndarray ] = None ,
92+ target_label : Optional [Union [int , List [int ], np .ndarray ]] = None ,
93+ ** kwargs
94+ ) -> np .ndarray :
8595 """
8696 Generate DPatch.
8797
8898 :param x: Sample images.
8999 :param y: Target labels for object detector.
100+ :param target_label: The target label of the DPatch attack.
90101 :return: Adversarial patch.
91102 """
92103 channel_index = 1 if self .estimator .channels_first else x .ndim - 1
@@ -96,6 +107,17 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
96107 raise ValueError ("The DPatch attack does not use target labels." )
97108 if x .ndim != 4 :
98109 raise ValueError ("The adversarial patch can only be applied to images." )
110+ if target_label is not None :
111+ if isinstance (target_label , int ):
112+ self .target_label = [target_label ] * x .shape [0 ]
113+ elif isinstance (target_label , np .ndarray ):
114+ if not (target_label .shape == (x .shape [0 ], 1 ) or target_label .shape == (x .shape [0 ],)):
115+ raise ValueError ("The target_label has to be a 1-dimensional array." )
116+ self .target_label = target_label .tolist ()
117+ else :
118+ if not len (target_label ) == x .shape [0 ] or not isinstance (target_label , list ):
119+ raise ValueError ("The target_label as list of integers needs to of length number of images in `x`." )
120+ self .target_label = target_label
99121
100122 for i_step in trange (self .max_iter , desc = "DPatch iteration" ):
101123 if i_step == 0 or (i_step + 1 ) % 100 == 0 :
@@ -106,19 +128,32 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
106128 )
107129 patch_target : List [Dict [str , np .ndarray ]] = list ()
108130
109- for i_image in range (patched_images .shape [0 ]):
131+ if self .target_label :
132+
133+ for i_image in range (patched_images .shape [0 ]):
134+ i_x_1 = transforms [i_image ]["i_x_1" ]
135+ i_x_2 = transforms [i_image ]["i_x_2" ]
136+ i_y_1 = transforms [i_image ]["i_y_1" ]
137+ i_y_2 = transforms [i_image ]["i_y_2" ]
110138
111- i_x_1 = transforms [ i_image ][ "i_x_1" ]
112- i_x_2 = transforms [ i_image ][ " i_x_2" ]
113- i_y_1 = transforms [ i_image ][ "i_y_1" ]
114- i_y_2 = transforms [ i_image ][ "i_y_2" ]
139+ target_dict = dict ()
140+ target_dict [ "boxes" ] = np . asarray ([[ i_x_1 , i_y_1 , i_x_2 , i_y_2 ]])
141+ target_dict [ "labels" ] = np . asarray ([ self . target_label [ i_image ],])
142+ target_dict [ "scores" ] = np . asarray ([ 1.0 ,])
115143
116- target_dict = dict ()
117- target_dict ["boxes" ] = np .asarray ([[i_x_1 , i_y_1 , i_x_2 , i_y_2 ]])
118- target_dict ["labels" ] = np .asarray ([1 ,])
119- target_dict ["scores" ] = np .asarray ([1.0 ,])
144+ patch_target .append (target_dict )
120145
121- patch_target .append (target_dict )
146+ else :
147+
148+ predictions = self .estimator .predict (x = patched_images )
149+
150+ for i_image in range (patched_images .shape [0 ]):
151+ target_dict = dict ()
152+ target_dict ["boxes" ] = predictions [i_image ]["boxes" ].detach ().cpu ().numpy ()
153+ target_dict ["labels" ] = predictions [i_image ]["labels" ].detach ().cpu ().numpy ()
154+ target_dict ["scores" ] = predictions [i_image ]["scores" ].detach ().cpu ().numpy ()
155+
156+ patch_target .append (target_dict )
122157
123158 num_batches = math .ceil (x .shape [0 ] / self .batch_size )
124159 patch_gradients = np .zeros_like (self ._patch )
@@ -131,7 +166,7 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
131166 x = patched_images [i_batch_start :i_batch_end ], y = patch_target [i_batch_start :i_batch_end ],
132167 )
133168
134- for i_image in range (self . batch_size ):
169+ for i_image in range (patched_images . shape [ 0 ] ):
135170
136171 i_x_1 = transforms [i_batch_start + i_image ]["i_x_1" ]
137172 i_x_2 = transforms [i_batch_start + i_image ]["i_x_2" ]
@@ -143,9 +178,13 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
143178 else :
144179 patch_gradients_i = gradients [i_image , i_x_1 :i_x_2 , i_y_1 :i_y_2 , :]
145180
146- patch_gradients += patch_gradients_i
181+ patch_gradients = patch_gradients + patch_gradients_i
182+
183+ if self .target_label :
184+ self ._patch = self ._patch - np .sign (patch_gradients ) * self .learning_rate
185+ else :
186+ self ._patch = self ._patch + np .sign (patch_gradients ) * self .learning_rate
147187
148- self ._patch -= patch_gradients * self .learning_rate
149188 self ._patch = np .clip (
150189 self ._patch , a_min = self .estimator .clip_values [0 ], a_max = self .estimator .clip_values [1 ],
151190 )
0 commit comments