import fnmatch import math import os import sys import time from operator import itemgetter import gc import numpy as np import torch import torch.optim as optim import torch.nn as nn import torch.nn.functional as F from PIL import Image from torch.utils.data import Dataset from torchvision import transforms # from darknet import Darknet from median_pool import MedianPool2d # print('starting test read') # im = Image.open('data/horse.jpg').convert('RGB') # print('img read!') class MaxProbExtractor(nn.Module): """MaxProbExtractor: extracts max class probability for class from YOLO output. Module providing the functionality necessary to extract the max class probability for one class from YOLO output. """ def __init__(self, cls_id, num_cls, config): super(MaxProbExtractor, self).__init__() self.cls_id = cls_id self.num_cls = num_cls self.config = config self.anchor_num = 3 def forward(self, YOLOoutput): # get values neccesary for transformation if YOLOoutput.dim() == 3: YOLOoutput = YOLOoutput.unsqueeze(0) batch = YOLOoutput.size(0) assert (YOLOoutput.size(1) == (5 + self.num_cls) * self.anchor_num) h = YOLOoutput.size(2) w = YOLOoutput.size(3) # transform the output tensor from [batch, 425, 19, 19] to [batch, 80, 1805] output = YOLOoutput.view(batch, self.anchor_num, 5 + self.num_cls, h * w) # [batch, 5, 85, 361] output = output.transpose(1, 2).contiguous() # [batch, 85, 5, 361] output = output.view(batch, 5 + self.num_cls, self.anchor_num * h * w) # [batch, 85, 1805] output_objectness = torch.sigmoid(output[:, 4, :]) # [batch, 1805] # 是否有物体 output = output[:, 5:5 + self.num_cls, :] # [batch, 80, 1805] # perform softmax to normalize probabilities for object classes to [0,1] normal_confs = torch.nn.Softmax(dim=1)(output) # 物体类别 # we only care for probabilities of the class of interest (person) confs_for_class = normal_confs[:, self.cls_id, :] # 类别 序号对应的为人 confs_if_object = output_objectness # confs_for_class * output_objectness confs_if_object = confs_for_class * output_objectness confs_if_object = self.config.loss_target(output_objectness, confs_for_class) # find the max probability for person max_conf, max_conf_idx = torch.max(confs_if_object, dim=1) return max_conf class NPSCalculator(nn.Module): """NMSCalculator: calculates the non-printability score of a patch. Module providing the functionality necessary to calculate the non-printability score (NMS) of an adversarial patch. """ def __init__(self, printability_file, patch_side): super(NPSCalculator, self).__init__() self.printability_array = nn.Parameter(self.get_printability_array(printability_file, patch_side), requires_grad=False) def forward(self, adv_patch): # calculate euclidian distance between colors in patch and colors in printability_array # square root of sum of squared difference color_dist = (adv_patch - self.printability_array + 0.000001) color_dist = color_dist ** 2 color_dist = torch.sum(color_dist, 1) + 0.000001 color_dist = torch.sqrt(color_dist) # only work with the min distance color_dist_prod = torch.min(color_dist, 0)[0] # test: change prod for min (find distance to closest color) # calculate the nps by summing over all pixels nps_score = torch.sum(color_dist_prod, 0) nps_score = torch.sum(nps_score, 0) return nps_score / torch.numel(adv_patch) def get_printability_array(self, printability_file, side): printability_list = [] # read in printability triplets and put them in a list with open(printability_file) as f: for line in f: printability_list.append(line.split(",")) printability_array = [] for printability_triplet in printability_list: printability_imgs = [] red, green, blue = printability_triplet printability_imgs.append(np.full((side, side), red)) printability_imgs.append(np.full((side, side), green)) printability_imgs.append(np.full((side, side), blue)) printability_array.append(printability_imgs) printability_array = np.asarray(printability_array) printability_array = np.float32(printability_array) pa = torch.from_numpy(printability_array) return pa class TotalVariation(nn.Module): """TotalVariation: calculates the total variation of a patch. Module providing the functionality necessary to calculate the total Variation (TV) of an adversarial patch. TotalVariation:计算补丁的总变化。 该模块提供了计算对抗性补丁的总变化 (TV) 所需的功能。 """ def __init__(self): super(TotalVariation, self).__init__() def forward(self, adv_patch): # bereken de total variation van de adv_patch tvcomp1 = torch.sum(torch.abs(adv_patch[:, :, 1:] - adv_patch[:, :, :-1] + 0.000001), 0) tvcomp1 = torch.sum(torch.sum(tvcomp1, 0), 0) tvcomp2 = torch.sum(torch.abs(adv_patch[:, 1:, :] - adv_patch[:, :-1, :] + 0.000001), 0) tvcomp2 = torch.sum(torch.sum(tvcomp2, 0), 0) tv = tvcomp1 + tvcomp2 return tv / torch.numel(adv_patch) class PatchTransformer(nn.Module): """PatchTransformer: transforms batch of patches Module providing the functionality necessary to transform a batch of patches, randomly adjusting brightness and contrast, adding random amount of noise, and rotating randomly. Resizes-patches according to as size based on the batch of labels, and pads them to the dimension of an image. 变换一批补丁,随机调整亮度和对比度,添加随机数量的噪声,随机旋转。 根据标签批次的大小调整补丁大小,并将它们填充到图像的尺寸中。 """ def __init__(self): super(PatchTransformer, self).__init__() self.min_contrast = 0.8 self.max_contrast = 1.2 self.min_brightness = -0.1 self.max_brightness = 0.1 self.noise_factor = 0.10 self.minangle = -20 / 180 * math.pi self.maxangle = 20 / 180 * math.pi self.medianpooler = MedianPool2d(7, same=True) # 中值池化 ''' kernel = torch.cuda.FloatTensor([[0.003765, 0.015019, 0.023792, 0.015019, 0.003765], [0.015019, 0.059912, 0.094907, 0.059912, 0.015019], [0.023792, 0.094907, 0.150342, 0.094907, 0.023792], [0.015019, 0.059912, 0.094907, 0.059912, 0.015019], [0.003765, 0.015019, 0.023792, 0.015019, 0.003765]]) self.kernel = kernel.unsqueeze(0).unsqueeze(0).expand(3,3,-1,-1) ''' def forward(self, adv_patch, lab_batch, img_size, do_rotate=True, rand_loc=True): # adv_patch = F.conv2d(adv_patch.unsqueeze(0),self.kernel,padding=(2,2)) adv_patch = self.medianpooler(adv_patch.unsqueeze(0)) # Determine size of padding pad = (img_size - adv_patch.size(-1)) / 2 # Make a batch of patches adv_patch = adv_patch.unsqueeze(0) # .unsqueeze(0) # 这里又扩大一维,变成5维 1, 1, 3, 300, 300 adv_batch = adv_patch.expand(lab_batch.size(0), lab_batch.size(1), -1, -1, -1) # adv_batch !! 不是adv_patch!! 8, 14, 3, 300, 300 batch_size = torch.Size((lab_batch.size(0), lab_batch.size(1))) # 8, 14 # Contrast, brightness and noise transforms # Create random contrast tensor contrast = torch.cuda.FloatTensor(batch_size).uniform_(self.min_contrast, self.max_contrast) contrast = contrast.unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) contrast = contrast.expand(-1, -1, adv_batch.size(-3), adv_batch.size(-2), adv_batch.size(-1)) contrast = contrast.cuda() # Create random brightness tensor brightness = torch.cuda.FloatTensor(batch_size).uniform_(self.min_brightness, self.max_brightness) brightness = brightness.unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) brightness = brightness.expand(-1, -1, adv_batch.size(-3), adv_batch.size(-2), adv_batch.size(-1)) brightness = brightness.cuda() # Create random noise tensor noise = torch.cuda.FloatTensor(adv_batch.size()).uniform_(-1, 1) * self.noise_factor # Apply contrast/brightness/noise, clamp adv_batch = adv_batch * contrast + brightness + noise adv_batch = torch.clamp(adv_batch, 0.000001, 0.99999) # 限制到0到1之间 # Where the label class_id is 1 we don't want a patch (padding) --> fill mask with zero's cls_ids = torch.narrow(lab_batch, 2, 0, 1) # torch.narrow(input,dim,start,length) 从dim开始,返回共享内存的数据start到start+length-1 cls_mask = cls_ids.expand(-1, -1, 3) # 接上,这里取出 lab_batch的代表id那列,相当于现在的lab_batch[..., 0] cls_mask = cls_mask.unsqueeze(-1) cls_mask = cls_mask.expand(-1, -1, -1, adv_batch.size(3)) cls_mask = cls_mask.unsqueeze(-1) cls_mask = cls_mask.expand(-1, -1, -1, -1, adv_batch.size(4)) # cls_mask 的大小是 8, 14, 3, 300, 300 数据是类别 msk_batch = torch.cuda.FloatTensor(cls_mask.size()).fill_(1) - cls_mask # 这里取出有人所对应的msk # Pad patch and mask to image dimensions mypad = nn.ConstantPad2d((int(pad + 0.5), int(pad), int(pad + 0.5), int(pad)), 0) # (padding_left、padding_right、padding_top、padding_bottom) 填充0 adv_batch = mypad(adv_batch) # 用0填充到416 msk_batch = mypad(msk_batch) # Rotation and rescaling transforms anglesize = (lab_batch.size(0) * lab_batch.size(1)) # 这里是旋转的数量 if do_rotate: angle = torch.cuda.FloatTensor(anglesize).uniform_(self.minangle, self.maxangle) else: angle = torch.cuda.FloatTensor(anglesize).fill_(0) # Resizes and rotates current_patch_size = adv_patch.size(-1) lab_batch_scaled = torch.cuda.FloatTensor(lab_batch.size()).fill_(0) # lab_batch_scaled是在原图上的尺寸? lab_batch_scaled[:, :, 1] = lab_batch[:, :, 1] * img_size lab_batch_scaled[:, :, 2] = lab_batch[:, :, 2] * img_size lab_batch_scaled[:, :, 3] = lab_batch[:, :, 3] * img_size lab_batch_scaled[:, :, 4] = lab_batch[:, :, 4] * img_size target_size = torch.sqrt( ((lab_batch_scaled[:, :, 3].mul(0.2)) ** 2) + ((lab_batch_scaled[:, :, 4].mul(0.2)) ** 2)) target_x = lab_batch[:, :, 1].view(np.prod(batch_size)) target_y = lab_batch[:, :, 2].view(np.prod(batch_size)) targetoff_x = lab_batch[:, :, 3].view(np.prod(batch_size)) targetoff_y = lab_batch[:, :, 4].view(np.prod(batch_size)) if (rand_loc): off_x = targetoff_x * (torch.cuda.FloatTensor(targetoff_x.size()).uniform_(-0.4, 0.4)) target_x = target_x + off_x off_y = targetoff_y * (torch.cuda.FloatTensor(targetoff_y.size()).uniform_(-0.4, 0.4)) target_y = target_y + off_y target_y = target_y - 0.05 scale = target_size / current_patch_size # 原图相对于补丁大小的缩放因子? scale = scale.view(anglesize) s = adv_batch.size() adv_batch = adv_batch.view(s[0] * s[1], s[2], s[3], s[4]) msk_batch = msk_batch.view(s[0] * s[1], s[2], s[3], s[4]) tx = (-target_x + 0.5) * 2 ty = (-target_y + 0.5) * 2 sin = torch.sin(angle) cos = torch.cos(angle) # Theta = rotation,rescale matrix theta = torch.cuda.FloatTensor(anglesize, 2, 3).fill_(0) theta[:, 0, 0] = cos / scale theta[:, 0, 1] = sin / scale theta[:, 0, 2] = tx * cos / scale + ty * sin / scale theta[:, 1, 0] = -sin / scale theta[:, 1, 1] = cos / scale theta[:, 1, 2] = -tx * sin / scale + ty * cos / scale b_sh = adv_batch.shape grid = F.affine_grid(theta, adv_batch.shape) adv_batch_t = F.grid_sample(adv_batch, grid) msk_batch_t = F.grid_sample(msk_batch, grid) ''' # Theta2 = translation matrix theta2 = torch.cuda.FloatTensor(anglesize, 2, 3).fill_(0) theta2[:, 0, 0] = 1 theta2[:, 0, 1] = 0 theta2[:, 0, 2] = (-target_x + 0.5) * 2 theta2[:, 1, 0] = 0 theta2[:, 1, 1] = 1 theta2[:, 1, 2] = (-target_y + 0.5) * 2 grid2 = F.affine_grid(theta2, adv_batch.shape) adv_batch_t = F.grid_sample(adv_batch_t, grid2) msk_batch_t = F.grid_sample(msk_batch_t, grid2) ''' adv_batch_t = adv_batch_t.view(s[0], s[1], s[2], s[3], s[4]) msk_batch_t = msk_batch_t.view(s[0], s[1], s[2], s[3], s[4]) adv_batch_t = torch.clamp(adv_batch_t, 0.000001, 0.999999) # img = msk_batch_t[0, 0, :, :, :].detach().cpu() # img = transforms.ToPILImage()(img) # img.show() # exit() return adv_batch_t * msk_batch_t class PatchApplier(nn.Module): """PatchApplier: applies adversarial patches to images. Module providing the functionality necessary to apply a patch to all detections in all images in the batch. PatchApplier:对图像应用对抗补丁。 """ def __init__(self): super(PatchApplier, self).__init__() def forward(self, img_batch, adv_batch): advs = torch.unbind(adv_batch, 1) # 沿1维解开 for adv in advs: img_batch = torch.where((adv == 0), img_batch, adv) # 对图像相应的坐标位置替换其像素?好像还没到图像的环节 return img_batch ''' class PatchGenerator(nn.Module): """PatchGenerator: network module that generates adversarial patches. Module representing the neural network that will generate adversarial patches. """ def __init__(self, cfgfile, weightfile, img_dir, lab_dir): super(PatchGenerator, self).__init__() self.yolo = Darknet(cfgfile).load_weights(weightfile) self.dataloader = torch.utils.data.DataLoader(InriaDataset(img_dir, lab_dir, shuffle=True), batch_size=5, shuffle=True) self.patchapplier = PatchApplier() self.nmscalculator = NMSCalculator() self.totalvariation = TotalVariation() def forward(self, *input): pass ''' class InriaDataset(Dataset): """InriaDataset: representation of the INRIA person dataset. Internal representation of the commonly used INRIA person dataset. Available at: http://pascal.inrialpes.fr/data/human/ Attributes: len: An integer number of elements in the img_dir: Directory containing the images of the INRIA dataset. lab_dir: Directory containing the labels of the INRIA dataset. img_names: List of all image file names in img_dir. shuffle: Whether or not to shuffle the dataset. """ def __init__(self, img_dir, lab_dir, max_lab, imgsize, shuffle=True): n_png_images = len(fnmatch.filter(os.listdir(img_dir), '*.png')) # 614 fnmatch.filter返回一个list n_jpg_images = len(fnmatch.filter(os.listdir(img_dir), '*.jpg')) # 0 n_images = n_png_images + n_jpg_images # 图像的总数 n_labels = len(fnmatch.filter(os.listdir(lab_dir), '*.txt')) assert n_images == n_labels, "Number of images and number of labels don't match" self.len = n_images self.img_dir = img_dir self.lab_dir = lab_dir self.imgsize = imgsize self.img_names = fnmatch.filter(os.listdir(img_dir), '*.png') + fnmatch.filter(os.listdir(img_dir), '*.jpg') self.shuffle = shuffle self.img_paths = [] for img_name in self.img_names: self.img_paths.append(os.path.join(self.img_dir, img_name)) self.lab_paths = [] for img_name in self.img_names: lab_path = os.path.join(self.lab_dir, img_name).replace('.jpg', '.txt').replace('.png', '.txt') self.lab_paths.append(lab_path) self.max_n_labels = max_lab # label的长度 def __len__(self): return self.len def __getitem__(self, idx): assert idx <= len(self), 'index range error' img_path = os.path.join(self.img_dir, self.img_names[idx]) lab_path = os.path.join(self.lab_dir, self.img_names[idx]).replace('.jpg', '.txt').replace('.png', '.txt') image = Image.open(img_path).convert('RGB') if os.path.getsize(lab_path): # check to see if label file contains data. label = np.loadtxt(lab_path) else: label = np.ones([5]) label = torch.from_numpy(label).float() if label.dim() == 1: label = label.unsqueeze(0) image, label = self.pad_and_scale(image, label) transform = transforms.ToTensor() image = transform(image) label = self.pad_lab(label) # print("image size :", image.shape) # print("label size :", label.shape) return image, label def pad_and_scale(self, img, lab): """ Args: img: Returns: """ w, h = img.size if w == h: padded_img = img else: dim_to_pad = 1 if w < h else 2 if dim_to_pad == 1: padding = (h - w) / 2 padded_img = Image.new('RGB', (h, h), color=(127, 127, 127)) padded_img.paste(img, (int(padding), 0)) lab[:, [1]] = (lab[:, [1]] * w + padding) / h lab[:, [3]] = (lab[:, [3]] * w / h) else: padding = (w - h) / 2 padded_img = Image.new('RGB', (w, w), color=(127, 127, 127)) padded_img.paste(img, (0, int(padding))) lab[:, [2]] = (lab[:, [2]] * h + padding) / w lab[:, [4]] = (lab[:, [4]] * h / w) resize = transforms.Resize((self.imgsize, self.imgsize)) padded_img = resize(padded_img) # choose here return padded_img, lab def pad_lab(self, lab): pad_size = self.max_n_labels - lab.shape[0] if (pad_size > 0): padded_lab = F.pad(lab, (0, 0, 0, pad_size), value=1) # (左边填充数, 右边填充数, 上边填充数, 下边填充数) else: padded_lab = lab return padded_lab if __name__ == '__main__': if len(sys.argv) == 3: img_dir = sys.argv[1] lab_dir = sys.argv[2] else: print('Usage: ') print(' python load_data.py img_dir lab_dir') sys.exit() test_loader = torch.utils.data.DataLoader(InriaDataset(img_dir, lab_dir, shuffle=True), batch_size=3, shuffle=True) cfgfile = "cfg/yolov2.cfg" weightfile = "weights/yolov2.weights" printfile = "non_printability/30values.txt" patch_size = 400 darknet_model = Darknet(cfgfile) darknet_model.load_weights(weightfile) darknet_model = darknet_model.cuda() patch_applier = PatchApplier().cuda() patch_transformer = PatchTransformer().cuda() prob_extractor = MaxProbExtractor(0, 80).cuda() nms_calculator = NMSCalculator(printfile, patch_size) total_variation = TotalVariation() ''' img = Image.open('data/horse.jpg').convert('RGB') img = img.resize((darknet_model.width, darknet_model.height)) width = img.width height = img.height img = torch.ByteTensor(torch.ByteStorage.from_buffer(img.tobytes())) img = img.view(height, width, 3).transpose(0, 1).transpose(0, 2).contiguous() img = img.view(1, 3, height, width) img = img.float().div(255.0) img = torch.autograd.Variable(img) output = darknet_model(img) ''' optimizer = torch.optim.Adam(model.parameters(), lr=0.0001) tl0 = time.time() tl1 = time.time() for i_batch, (img_batch, lab_batch) in enumerate(test_loader): tl1 = time.time() print('time to fetch items: ', tl1 - tl0) img_batch = img_batch.cuda() lab_batch = lab_batch.cuda() adv_patch = Image.open('data/horse.jpg').convert('RGB') adv_patch = adv_patch.resize((patch_size, patch_size)) transform = transforms.ToTensor() adv_patch = transform(adv_patch).cuda() img_size = img_batch.size(-1) print('transforming patches') t0 = time.time() adv_batch_t = patch_transformer.forward(adv_patch, lab_batch, img_size) print('applying patches') t1 = time.time() img_batch = patch_applier.forward(img_batch, adv_batch_t) img_batch = torch.autograd.Variable(img_batch) img_batch = F.interpolate(img_batch, (darknet_model.height, darknet_model.width)) print('running patched images through model') t2 = time.time() for obj in gc.get_objects(): try: if torch.is_tensor(obj) or (hasattr(obj, 'data') and torch.is_tensor(obj.data)): try: print(type(obj), obj.size()) except: pass except: pass print(torch.cuda.memory_allocated()) output = darknet_model(img_batch) print('extracting max probs') t3 = time.time() max_prob = prob_extractor(output) t4 = time.time() nms = nms_calculator.forward(adv_patch) tv = total_variation(adv_patch) print('---------------------------------') print(' patch transformation : %f' % (t1 - t0)) print(' patch application : %f' % (t2 - t1)) print(' darknet forward : %f' % (t3 - t2)) print(' probability extraction : %f' % (t4 - t3)) print('---------------------------------') print(' total forward pass : %f' % (t4 - t0)) del img_batch, lab_batch, adv_patch, adv_batch_t, output, max_prob torch.cuda.empty_cache() tl0 = time.time()