Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
UltralyticsAssistant committed Feb 25, 2024
1 parent b90fc4d commit 6ac383a
Show file tree
Hide file tree
Showing 15 changed files with 66 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""
Convert VisDrone annotation format to YOLO labels format
Works for training of YOLOv5 and YOLOv7.
YOLOv7 requires an additional txt file (Same name as the first parent directory) with paths to the images for the train, val & test splits
Convert VisDrone annotation format to YOLO labels format Works for training of YOLOv5 and YOLOv7.
YOLOv7 requires an additional txt file (Same name as the first parent directory) with paths to the images for the train,
val & test splits
"""
import os
import os.path as osp
Expand Down
16 changes: 4 additions & 12 deletions adv_patch_gen/utils/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
Common utils
"""
"""Common utils."""
import socket
from typing import Tuple, Union

Expand Down Expand Up @@ -30,17 +28,13 @@ class BColors:


def is_port_in_use(port: int) -> bool:
"""
Checks if a port is free for use
"""
"""Checks if a port is free for use."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as stream:
return stream.connect_ex(("localhost", int(port))) == 0


def pad_to_square(img: Image, pad_rgb: Tuple[int, int, int] = (127, 127, 127)) -> Image:
"""
Pads a PIL image to a square with pad_rgb values to the longest side
"""
"""Pads a PIL image to a square with pad_rgb values to the longest side."""
w, h = img.size
if w == h:
padded_img = img
Expand All @@ -57,9 +51,7 @@ def pad_to_square(img: Image, pad_rgb: Tuple[int, int, int] = (127, 127, 127)) -


def calc_mean_and_std_err(arr: Union[list, np.ndarray]) -> Tuple[float, float]:
""" "
Calculate mean and standard error
"""
"""" Calculate mean and standard error."""
mean = np.mean(arr)
std_err = np.std(arr, ddof=1) / np.sqrt(len(arr))
return mean, std_err
12 changes: 3 additions & 9 deletions adv_patch_gen/utils/config_parser.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
"""
Create argparse options for config files
"""
"""Create argparse options for config files."""
import json
import argparse
from easydict import EasyDict as edict


def load_config_object(cfg_path: str) -> edict:
"""
Loads a config json and returns a edict object
"""
"""Loads a config json and returns a edict object."""
with open(cfg_path, "r", encoding="utf-8") as json_file:
cfg_dict = json.load(json_file)

return edict(cfg_dict)


def get_argparser(desc="Config file load for training adv patches") -> argparse.ArgumentParser:
"""
Get parser with the default config argument
"""
"""Get parser with the default config argument."""
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
"--cfg",
Expand Down
18 changes: 7 additions & 11 deletions adv_patch_gen/utils/dataset.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""
Dataset Class for loading YOLO format datasets where the source data dir has the image and labels subdirs
where each image must have a corresponding label file with the same name
"""Dataset Class for loading YOLO format datasets where the source data dir has the image and labels subdirs where each
image must have a corresponding label file with the same name.
"""
import glob
import os.path as osp
Expand All @@ -18,7 +17,8 @@


class YOLODataset(Dataset):
"""Create a dataset for adversarial-yolt.
"""
Create a dataset for adversarial-yolt.
Attributes:
image_dir: Directory containing the images of the YOLO format dataset.
Expand Down Expand Up @@ -106,10 +106,8 @@ def __getitem__(self, idx):
return image, label

def pad_and_scale(self, img, lab):
"""
Pad image and adjust label
img is a PIL image
lab is of fmt class x_center y_center width height with normalized coords
"""Pad image and adjust label img is a PIL image lab is of fmt class x_center y_center width height with
normalized coords.
"""
img_w, img_h = img.size
if img_w == img_h:
Expand All @@ -132,9 +130,7 @@ def pad_and_scale(self, img, lab):
return padded_img, lab

def pad_label(self, label: torch.Tensor) -> torch.Tensor:
"""
Pad labels with zeros if fewer labels than max_n_labels present
"""
"""Pad labels with zeros if fewer labels than max_n_labels present."""
pad_size = self.max_n_labels - label.shape[0]
if pad_size > 0:
padded_lab = F.pad(label, (0, 0, 0, pad_size), value=0)
Expand Down
12 changes: 5 additions & 7 deletions adv_patch_gen/utils/loss.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
Loss functions used in patch generation
"""
"""Loss functions used in patch generation."""
from typing import Tuple

import torch
Expand All @@ -19,9 +17,7 @@ def __init__(self, config):
self.config = config

def forward(self, output: torch.Tensor):
"""
output must be of the shape [batch, -1, 5 + num_cls]
"""
"""Output must be of the shape [batch, -1, 5 + num_cls]"""
# get values necessary for transformation
assert output.size(-1) == (5 + self.config.n_classes)

Expand All @@ -43,7 +39,9 @@ def forward(self, output: torch.Tensor):


class SaliencyLoss(nn.Module):
"""Implementation of the colorfulness metric as the saliency loss.
"""
Implementation of the colorfulness metric as the saliency loss.
The smaller the value, the less colorful the image.
Reference: https://infoscience.epfl.ch/record/33994/files/HaslerS03.pdf
"""
Expand Down
3 changes: 2 additions & 1 deletion adv_patch_gen/utils/median_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@


class MedianPool2d(nn.Module):
"""Median pool (usable as median filter when stride=1) module.
"""
Median pool (usable as median filter when stride=1) module.
Args:
kernel_size: size of pooling kernel, int or 2-tuple
Expand Down
2 changes: 1 addition & 1 deletion adv_patch_gen/utils/patch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Modules for creating adversarial object patch """
"""Modules for creating adversarial object patch."""
import math
from typing import Union, Tuple

Expand Down
5 changes: 3 additions & 2 deletions adv_patch_gen/utils/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@


class GradualWarmupScheduler(_LRScheduler):
"""Sets the learning rate of parameter group to gradually increase for num_epochs from start_lr
to the original lr set for the optimizer
"""
Sets the learning rate of parameter group to gradually increase for num_epochs from start_lr to the original lr set
for the optimizer.
Args:
optimizer (Optimizer): Wrapped optimizer.
Expand Down
12 changes: 3 additions & 9 deletions adv_patch_gen/utils/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ def ffmpeg_create_video_from_image_dir(
title_text: Optional[str] = None,
framerate: int = 25,
) -> None:
"""
Create a video from images in source_image_dir
"""
"""Create a video from images in source_image_dir."""
source = osp.join(source_image_dir, f"*{etxn}")
cmd = ["ffmpeg", "-y", "-framerate", str(framerate), "-pattern_type", "glob", "-i", source]
if title_text is not None:
Expand All @@ -23,17 +21,13 @@ def ffmpeg_create_video_from_image_dir(


def ffmpeg_combine_two_vids(vid1: str, vid2: str, target_video_path: str) -> None:
"""
Attaches two videos side by side horizontally and saves to target_video_path
"""
"""Attaches two videos side by side horizontally and saves to target_video_path."""
cmd = ["ffmpeg", "-y", "-i", vid1, "-i", vid2, "-filter_complex", "hstack", target_video_path]
output, error = Popen(cmd, universal_newlines=True, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()


def ffmpeg_combine_three_vids(vid1: str, vid2: str, vid3: str, target_video_path: str) -> None:
"""
Attaches three videos side by side horizontally and saves to target_video_path
"""
"""Attaches three videos side by side horizontally and saves to target_video_path."""
filter_pat = "[1:v][0:v]scale2ref=oh*mdar:ih[1v][0v];[2:v][0v]scale2ref=oh*mdar:ih[2v][0v];[0v][1v][2v]hstack=3,scale='2*trunc(iw/2)':'2*trunc(ih/2)'"
cmd = ["ffmpeg", "-y", "-i", vid1, "-i", vid2, "-i", vid3, "-filter_complex", filter_pat, target_video_path]
output, error = Popen(cmd, universal_newlines=True, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()
9 changes: 6 additions & 3 deletions detect_oak.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def inference(
debug: bool = False,
**kwrags,
) -> None:
"""Run Object Detection Application
"""
Run Object Detection Application.
Args:
weights: str = path to yolov5 model
Expand Down Expand Up @@ -234,7 +235,8 @@ def inference_threaded(
debug: bool = False,
**kwrags,
) -> None:
"""Run Object Detection Application
"""
Run Object Detection Application.
Args:
weights: str = path to yolov5 model
Expand Down Expand Up @@ -319,7 +321,8 @@ def inference_threaded_with_defense(
cam_fps: int = 20,
debug: bool = False,
) -> None:
"""Run Object Detection Application
"""
Run Object Detection Application.
Args:
weights: str = path to yolov5 model
Expand Down
5 changes: 2 additions & 3 deletions explainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,8 @@ def __init__(self, model):
self.model = model

def forward(self, x):
"""
first one is a 3 dim array which contains predictions
second one is a list of heads with their corresponding predictions
"""First one is a 3 dim array which contains predictions second one is a list of heads with their corresponding
predictions.
"""
total_prediction, _ = self.model(x)
return total_prediction
Expand Down
21 changes: 7 additions & 14 deletions test_patch.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""
Testing code for evaluating Adversarial patches against object detection
"""
"""Testing code for evaluating Adversarial patches against object detection."""
import io
import os
import os.path as osp
Expand Down Expand Up @@ -47,9 +45,7 @@


def eval_coco_metrics(anno_json: str, pred_json: str, txt_save_path: str, w_mode: str = "a") -> np.ndarray:
"""
Compare and eval pred json producing coco metrics
"""
"""Compare and eval pred json producing coco metrics."""
anno = COCO(anno_json) # init annotations api
pred = anno.loadRes(pred_json) # init predictions api
evaluator = COCOeval(anno, pred, "bbox")
Expand All @@ -69,9 +65,7 @@ def eval_coco_metrics(anno_json: str, pred_json: str, txt_save_path: str, w_mode


class PatchTester:
"""
Module for testing patches on dataset against object detection models
"""
"""Module for testing patches on dataset against object detection models."""

def __init__(self, cfg: edict) -> None:
self.cfg = cfg
Expand All @@ -96,8 +90,9 @@ def calc_asr(
recompute_asr_all: bool = False,
) -> Tuple[float, float, float, float]:
"""
Calculate attack success rate (How many bounding boxes were hidden from the detector)
for all predictions and for different bbox areas.
Calculate attack success rate (How many bounding boxes were hidden from the detector) for all predictions and
for different bbox areas.
Note cls_id is None, misclassifications are ignored and only missing detections are considered attack success.
Args:
boxes: torch.Tensor, first pass boxes (gt unpatched boxes) [class, x1, y1, x2, y2]
Expand Down Expand Up @@ -172,9 +167,7 @@ def calc_asr(

@staticmethod
def draw_bbox_on_pil_image(bbox: np.ndarray, padded_img_pil: Image, class_list: List[str]) -> Image:
"""
Draw bounding box on a PIL image and return said image after drawing
"""
"""Draw bounding box on a PIL image and return said image after drawing."""
padded_img_np = np.ascontiguousarray(padded_img_pil)
label_2_class = dict(enumerate(class_list))

Expand Down
7 changes: 5 additions & 2 deletions train.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictio
prefix=colorstr("train: "),
shuffle=True,
seed=opt.seed,
patch_dir=opt.patch_dir)
patch_dir=opt.patch_dir,
)
labels = np.concatenate(dataset.labels, 0)
mlc = int(labels[:, 0].max()) # max label class
assert mlc < nc, f"Label class {mlc} exceeds nc={nc} in {data}. Possible class labels are 0-{nc - 1}"
Expand Down Expand Up @@ -544,7 +545,9 @@ def parse_opt(known=False):
parser.add_argument("--freeze", nargs="+", type=int, default=[0], help="Freeze layers: backbone=10, first3=0 1 2")
parser.add_argument("--save-period", type=int, default=-1, help="Save checkpoint every x epochs (disabled if < 1)")
parser.add_argument("--seed", type=int, default=0, help="Global training seed")
parser.add_argument('--patch_dir', type=str, default='', help='Directory with images to apply as patch augmentation in train imgs')
parser.add_argument(
"--patch_dir", type=str, default="", help="Directory with images to apply as patch augmentation in train imgs"
)
parser.add_argument("--local_rank", type=int, default=-1, help="Automatic DDP Multi-GPU argument, do not modify")

# Logger arguments
Expand Down
20 changes: 6 additions & 14 deletions train_patch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Training code for Adversarial patch training
Training code for Adversarial patch training.
python train_patch.py --cfg config_json_file
"""
Expand Down Expand Up @@ -48,9 +48,7 @@


class PatchTrainer:
"""
Module for training on dataset to generate adv patches
"""
"""Module for training on dataset to generate adv patches."""

def __init__(self, cfg: edict):
self.cfg = cfg
Expand Down Expand Up @@ -111,9 +109,7 @@ def __init__(self, cfg: edict):
self.epoch_length = len(self.train_loader)

def init_tensorboard(self, log_dir: str = None, port: int = 6006, run_tb=True):
"""
Initialize tensorboard with optional name
"""
"""Initialize tensorboard with optional name."""
if run_tb:
while is_port_in_use(port) and port < 65535:
port += 1
Expand Down Expand Up @@ -146,7 +142,7 @@ def generate_patch(self, patch_type: str, pil_img_mode: str = "RGB") -> torch.Te

def read_image(self, path, pil_img_mode: str = "RGB") -> torch.Tensor:
"""
Read an input image to be used as a patch
Read an input image to be used as a patch.
Arguments:
path: Path to the image to be read.
Expand All @@ -157,9 +153,7 @@ def read_image(self, path, pil_img_mode: str = "RGB") -> torch.Tensor:
return adv_patch_cpu

def train(self) -> None:
"""
Optimize a patch to generate an adversarial example.
"""
"""Optimize a patch to generate an adversarial example."""
# make output dirs
patch_dir = osp.join(self.cfg.log_dir, "patches")
os.makedirs(patch_dir, exist_ok=True)
Expand Down Expand Up @@ -284,9 +278,7 @@ def train(self) -> None:
print(f"Total training time {time.time() - start_time:.2f}s")

def val(self, epoch: int, patchfile: str, conf_thresh: float = 0.4, nms_thresh: float = 0.4) -> None:
"""
Calculates the attack success rate according for the patch with respect to different bounding box areas
"""
"""Calculates the attack success rate according for the patch with respect to different bounding box areas."""
# load patch from file
patch_img = Image.open(patchfile).convert(self.cfg.patch_img_mode)
patch_img = T.Resize(self.cfg.patch_size)(patch_img)
Expand Down
Loading

0 comments on commit 6ac383a

Please sign in to comment.