Source code for perceptron.benchmarks.motion_blur

# Copyright 2019 Baidu Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Metric that tests models against motion blurs."""

from .base import call_decorator
from .base import Metric
from scipy.ndimage.filters import gaussian_filter
import math
from collections import Iterable
from tqdm import tqdm
import numpy as np


[docs]class MotionBlurMetric(Metric): """Motion blurs the image until it is misclassified."""
[docs] @call_decorator def __call__(self, adv, annotation=None, unpack=True, abort_early=True, motion_angle=0, epsilons=10000): """Blurs the image until it is misclassified. Parameters ---------- adv : `numpy.ndarray` The original, unperturbed input as a `numpy.ndarray`. annotation : int The reference label of the original input. unpack : bool If true, returns the adversarial input, otherwise returns the Adversarial object. abort_early : bool If true, returns when got first adversarial, otherwise returns when all the iterations are finished. motion_angle : float Motion angle in degree between 0 and 180. epsilons : int or Iterable[float] Either Iterable of kernel size that should be tried. """ import cv2 a = adv del adv del annotation del unpack image = a.original_image min_, max_ = a.bounds() axis = a.channel_axis(batch=False) hw = [image.shape[i] for i in range(image.ndim) if i != axis] h, w = hw size_min = min(h, w) if axis == 0: image_cv = np.transpose(image, (1, 2, 0)) elif axis == 2: image_cv = np.copy(image) else: raise ValueError('Invalid axis.') if epsilons > size_min: epsilons = size_min if not isinstance(epsilons, Iterable): epsilons = np.linspace(0, size_min, num=epsilons + 1)[1:] for epsilon in tqdm(epsilons): kernel = self._Kernel((epsilon, epsilon), motion_angle) blurred = cv2.filter2D(image_cv, -1, kernel) blurred = np.clip(blurred, min_, max_) if axis == 0: blurred = np.transpose(blurred, (2, 0, 1)) _, is_adversarial = a.predictions(blurred) if is_adversarial and abort_early: return
def _Kernel(self, dim, angle): if isinstance(dim, int): dim = (dim, dim) dim = np.array(dim).astype(int) center = (dim - 1) / 2 kernel = np.zeros(dim) angle = math.fmod(angle, 180.0) if angle == 90: kernel[:, int(center[1])] = 1 return kernel delta_x = 0 delta_y = 0 while True: if (center[1] + delta_x).is_integer(): if int(round(center[0] + delta_y)) < 0 or int(round(center[0] + delta_y)) >= dim[0] or int( center[1] + delta_x) < 0 or int(center[1] + delta_x) >= dim[1]: break kernel[int(round(center[0] - delta_y)), int(center[1] + delta_x)] = 1 kernel[int(round(center[0] + delta_y)), int(center[1] - delta_x)] = 1 delta_x += 0.5 delta_y = delta_x * math.tan(angle / 180 * np.pi) normalizationFactor = np.count_nonzero(kernel) kernel /= normalizationFactor return kernel