mirror of
https://github.com/Shawn-Shan/fawkes.git
synced 2024-12-22 07:09:33 +05:30
0.0.6
Former-commit-id: 14c0173d9f573e7ccb275b3e366505057ac2c9b1 [formerly e359682d967212b4b3f27923fd659bbade7880e5] Former-commit-id: a44577686ff64da031231ea323c681185daa8b0d
This commit is contained in:
parent
30fa1635a5
commit
e9f1a50653
@ -6,11 +6,11 @@ We published an academic paper to summary our work "[Fawkes: Protecting Personal
|
|||||||
|
|
||||||
|
|
||||||
### BEFORE YOU RUN OUR CODE
|
### BEFORE YOU RUN OUR CODE
|
||||||
|
|
||||||
|
If you would like to use Fawkes to protect your images, please check out our binary implementation on the [website](http://sandlab.cs.uchicago.edu/fawkes/#code).
|
||||||
|
|
||||||
If you are a developer or researcher planning to customize and modify on our existing code. Please refer to [fawkes_dev](https://github.com/Shawn-Shan/fawkes/tree/master/fawkes_dev).
|
If you are a developer or researcher planning to customize and modify on our existing code. Please refer to [fawkes_dev](https://github.com/Shawn-Shan/fawkes/tree/master/fawkes_dev).
|
||||||
|
|
||||||
### How to protect my image
|
|
||||||
|
|
||||||
|
|
||||||
### Citation
|
### Citation
|
||||||
```
|
```
|
||||||
@inproceedings{shan2020fawkes,
|
@inproceedings{shan2020fawkes,
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
Metadata-Version: 2.1
|
|
||||||
Name: fawkes
|
|
||||||
Version: 0.0.1
|
|
||||||
Summary: Fawkes protect user privacy
|
|
||||||
Home-page: https://github.com/Shawn-Shan/fawkes
|
|
||||||
Author: Shawn Shan
|
|
||||||
Author-email: shansixiong@cs.uchicago.edu
|
|
||||||
License: UNKNOWN
|
|
||||||
Description: # Fawkes
|
|
||||||
Code implementation of the paper "[Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models](https://arxiv.org/pdf/2002.08327.pdf)", at *USENIX Security 2020*.
|
|
||||||
|
|
||||||
### BEFORE YOU RUN OUR CODE
|
|
||||||
We appreciate your interest in our work and for trying out our code. We've noticed several cases where incorrect configuration leads to poor performances of protection. If you also observe low detection performance far away from what we presented in the paper, please feel free to open an issue in this repo or contact any of the authors directly. We are more than happy to help you debug your experiment and find out the correct configuration.
|
|
||||||
|
|
||||||
### ABOUT
|
|
||||||
|
|
||||||
This repository contains code implementation of the paper "[Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models](https://arxiv.org/pdf/2002.08327.pdf)", at *USENIX Security 2020*.
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
|
||||||
|
|
||||||
Our code is implemented and tested on Keras with TensorFlow backend. Following packages are used by our code.
|
|
||||||
|
|
||||||
- `keras==2.3.1`
|
|
||||||
- `numpy==1.18.4`
|
|
||||||
- `tensorflow-gpu==1.13.1`
|
|
||||||
|
|
||||||
Our code is tested on `Python 3.6.8`
|
|
||||||
|
|
||||||
### HOWTO
|
|
||||||
|
|
||||||
#### Download and Config Datasets
|
|
||||||
The first step is to download several datasets for protection and target selection.
|
|
||||||
1. Download the following dataset to your local machine. After downloading the datasets, restructure it the same way as the FaceScrub dataset downloaded.
|
|
||||||
- FaceScrub -- used for protection evaluation (link)
|
|
||||||
- VGGFace1 -- used for target select (link)
|
|
||||||
- VGGFace2 -- used for target select (link)
|
|
||||||
- WebFace -- used for target select (link)
|
|
||||||
|
|
||||||
2. Config datasets
|
|
||||||
open `fawkes/config.py` and update the `DATASETS` dictionary with the path to each dataset. Then run `python fawkes/config.py`. Every time the datasets are updated or moved, remember to rerun the command with the updated path.
|
|
||||||
|
|
||||||
3. Calculate embeddings using feature extractor.
|
|
||||||
Run `python3 fawkes/prepare_feature_extractor.py --candidate-datasets scrub vggface1 vggface2 webface`. This will calculate and cache the embeddings using the default feature extractor we provide. To use a customized feature extractor, please look at the Advance section at the end.
|
|
||||||
|
|
||||||
#### Generate Cloak for Images
|
|
||||||
To generate cloak, run
|
|
||||||
`python3 fawkes/protection.py --gpu 0 --dataset scrub --feature-extractor webface_dense_robust_extract`
|
|
||||||
For more information about the detailed parameters, please read `fawkes/protection.py`.
|
|
||||||
The code will output a directory in `results/` with `cloak_data.p` inside. You can check the cloaked images or inspect the changes in `this notebook`.
|
|
||||||
|
|
||||||
#### Evaluate Cloak Effectiveness
|
|
||||||
To evaluate the cloak, run `python3 fawkes/eval_cloak.py --gpu 0 --cloak_data PATH-TO-RESULT-DIRECTORY --transfer_model vggface2_inception_extract`.
|
|
||||||
|
|
||||||
The code will print out the tracker model accuracy on uncloaked/original test images of the protected user, which should be close to 0.
|
|
||||||
|
|
||||||
|
|
||||||
### Citation
|
|
||||||
```
|
|
||||||
@inproceedings{shan2020fawkes,
|
|
||||||
title={Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models},
|
|
||||||
author={Shan, Shawn and Wenger, Emily and Zhang, Jiayun and Li, Huiying and Zheng, Haitao and Zhao, Ben Y},
|
|
||||||
booktitle="Proc. of USENIX Security",
|
|
||||||
year={2020}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: License :: OSI Approved :: MIT License
|
|
||||||
Classifier: Operating System :: OS Independent
|
|
||||||
Requires-Python: >=3.5
|
|
||||||
Description-Content-Type: text/markdown
|
|
@ -1,10 +0,0 @@
|
|||||||
README.md
|
|
||||||
setup.py
|
|
||||||
fawkes/__init__.py
|
|
||||||
fawkes/differentiator.py
|
|
||||||
fawkes/protection.py
|
|
||||||
fawkes/utils.py
|
|
||||||
fawkes.egg-info/PKG-INFO
|
|
||||||
fawkes.egg-info/SOURCES.txt
|
|
||||||
fawkes.egg-info/dependency_links.txt
|
|
||||||
fawkes.egg-info/top_level.txt
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1 +0,0 @@
|
|||||||
fawkes
|
|
@ -1 +0,0 @@
|
|||||||
58d500da850206b845bdd0150fa182a0ff8c50f0
|
|
@ -4,21 +4,17 @@
|
|||||||
# @Link : https://www.shawnshan.com/
|
# @Link : https://www.shawnshan.com/
|
||||||
|
|
||||||
|
|
||||||
__version__ = '0.0.2'
|
__version__ = '0.0.5'
|
||||||
|
|
||||||
|
from .detect_faces import create_mtcnn, run_detect_face
|
||||||
from .differentiator import FawkesMaskGeneration
|
from .differentiator import FawkesMaskGeneration
|
||||||
from .utils import load_extractor, init_gpu, select_target_label, dump_image, reverse_process_cloaked, \
|
|
||||||
Faces
|
|
||||||
from .protection import main
|
from .protection import main
|
||||||
import logging
|
from .utils import load_extractor, init_gpu, select_target_label, dump_image, reverse_process_cloaked, Faces, get_file
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
logging.getLogger('tensorflow').disabled = True
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'__version__',
|
'__version__', 'create_mtcnn', 'run_detect_face',
|
||||||
'FawkesMaskGeneration', 'load_extractor',
|
'FawkesMaskGeneration', 'load_extractor',
|
||||||
'init_gpu',
|
'init_gpu',
|
||||||
'select_target_label', 'dump_image', 'reverse_process_cloaked', 'Faces', 'main'
|
'select_target_label', 'dump_image', 'reverse_process_cloaked',
|
||||||
|
'Faces', 'get_file', 'main',
|
||||||
)
|
)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +1,6 @@
|
|||||||
from .detect_face import detect_face, create_mtcnn
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from fawkes import create_mtcnn, run_detect_face
|
||||||
|
|
||||||
# modify the default parameters of np.load
|
|
||||||
np_load_old = np.load
|
np_load_old = np.load
|
||||||
np.load = lambda *a, **k: np_load_old(*a, allow_pickle=True, **k)
|
np.load = lambda *a, **k: np_load_old(*a, allow_pickle=True, **k)
|
||||||
|
|
||||||
@ -30,7 +29,7 @@ def align(orig_img, aligner, margin=0.8, detect_multiple_faces=True):
|
|||||||
orig_img = to_rgb(orig_img)
|
orig_img = to_rgb(orig_img)
|
||||||
orig_img = orig_img[:, :, 0:3]
|
orig_img = orig_img[:, :, 0:3]
|
||||||
|
|
||||||
bounding_boxes, _ = detect_face(orig_img, minsize, pnet, rnet, onet, threshold, factor)
|
bounding_boxes, _ = run_detect_face(orig_img, minsize, pnet, rnet, onet, threshold, factor)
|
||||||
nrof_faces = bounding_boxes.shape[0]
|
nrof_faces = bounding_boxes.shape[0]
|
||||||
if nrof_faces > 0:
|
if nrof_faces > 0:
|
||||||
det = bounding_boxes[:, 0:4]
|
det = bounding_boxes[:, 0:4]
|
||||||
@ -66,14 +65,6 @@ def align(orig_img, aligner, margin=0.8, detect_multiple_faces=True):
|
|||||||
cropped = orig_img[bb[1]:bb[3], bb[0]:bb[2], :]
|
cropped = orig_img[bb[1]:bb[3], bb[0]:bb[2], :]
|
||||||
cropped_arr.append(cropped)
|
cropped_arr.append(cropped)
|
||||||
bounding_boxes_arr.append([bb[0], bb[1], bb[2], bb[3]])
|
bounding_boxes_arr.append([bb[0], bb[1], bb[2], bb[3]])
|
||||||
# scaled = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
|
|
||||||
return cropped_arr, bounding_boxes_arr
|
return cropped_arr, bounding_boxes_arr
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
#
|
|
||||||
# if __name__ == '__main__':
|
|
||||||
# orig_img = misc.imread('orig_img.jpeg')
|
|
||||||
# cropped_arr, bounding_boxes_arr = align(orig_img)
|
|
||||||
# misc.imsave('test_output.jpeg', cropped_arr[0])
|
|
||||||
# print(bounding_boxes_arr)
|
|
||||||
#
|
|
||||||
|
@ -1,38 +1,15 @@
|
|||||||
""" Tensorflow implementation of the face detection / alignment algorithm found at
|
""" Tensorflow implementation of the face detection / alignment algorithm found at
|
||||||
https://github.com/kpzhang93/MTCNN_face_detection_alignment
|
https://github.com/kpzhang93/MTCNN_face_detection_alignment
|
||||||
"""
|
"""
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 David Sandberg
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
|
import gzip
|
||||||
import os
|
import os
|
||||||
|
import pickle
|
||||||
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import tensorflow as tf
|
import tensorflow as tf
|
||||||
from six import string_types, iteritems
|
from six import string_types, iteritems
|
||||||
|
from skimage.transform import resize
|
||||||
|
|
||||||
|
|
||||||
def layer(op):
|
def layer(op):
|
||||||
@ -78,13 +55,12 @@ class Network(object):
|
|||||||
"""Construct the network. """
|
"""Construct the network. """
|
||||||
raise NotImplementedError('Must be implemented by the subclass.')
|
raise NotImplementedError('Must be implemented by the subclass.')
|
||||||
|
|
||||||
def load(self, data_path, session, ignore_missing=False):
|
def load(self, data_dict, session, ignore_missing=False):
|
||||||
"""Load network weights.
|
"""Load network weights.
|
||||||
data_path: The path to the numpy-serialized network weights
|
data_path: The path to the numpy-serialized network weights
|
||||||
session: The current TensorFlow session
|
session: The current TensorFlow session
|
||||||
ignore_missing: If true, serialized weights for missing layers are ignored.
|
ignore_missing: If true, serialized weights for missing layers are ignored.
|
||||||
"""
|
"""
|
||||||
data_dict = np.load(data_path, encoding='latin1').item() # pylint: disable=no-member
|
|
||||||
|
|
||||||
for op_name in data_dict:
|
for op_name in data_dict:
|
||||||
with tf.variable_scope(op_name, reuse=True):
|
with tf.variable_scope(op_name, reuse=True):
|
||||||
@ -280,21 +256,27 @@ class ONet(Network):
|
|||||||
|
|
||||||
|
|
||||||
def create_mtcnn(sess, model_path):
|
def create_mtcnn(sess, model_path):
|
||||||
if not model_path:
|
model_dir = os.path.join(os.path.expanduser('~'), '.fawkes')
|
||||||
model_path, _ = os.path.split(os.path.realpath(__file__))
|
os.makedirs(model_dir, exist_ok=True)
|
||||||
|
|
||||||
|
fp = gzip.open(os.path.join(model_dir, "mtcnn.p.gz"), 'rb')
|
||||||
|
dnet_weights = pickle.load(fp)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
with tf.variable_scope('pnet'):
|
with tf.variable_scope('pnet'):
|
||||||
data = tf.placeholder(tf.float32, (None, None, None, 3), 'input')
|
data = tf.placeholder(tf.float32, (None, None, None, 3), 'input')
|
||||||
pnet = PNet({'data': data})
|
pnet = PNet({'data': data})
|
||||||
pnet.load(os.path.join(model_path, 'weights/det1.npy'), sess)
|
|
||||||
|
# data_dict = np.load(data_path, encoding='latin1').item() # pylint: disable=no-member
|
||||||
|
pnet.load(dnet_weights[0], sess)
|
||||||
with tf.variable_scope('rnet'):
|
with tf.variable_scope('rnet'):
|
||||||
data = tf.placeholder(tf.float32, (None, 24, 24, 3), 'input')
|
data = tf.placeholder(tf.float32, (None, 24, 24, 3), 'input')
|
||||||
rnet = RNet({'data': data})
|
rnet = RNet({'data': data})
|
||||||
rnet.load(os.path.join(model_path, 'weights/det2.npy'), sess)
|
rnet.load(dnet_weights[1], sess)
|
||||||
with tf.variable_scope('onet'):
|
with tf.variable_scope('onet'):
|
||||||
data = tf.placeholder(tf.float32, (None, 48, 48, 3), 'input')
|
data = tf.placeholder(tf.float32, (None, 48, 48, 3), 'input')
|
||||||
onet = ONet({'data': data})
|
onet = ONet({'data': data})
|
||||||
onet.load(os.path.join(model_path, 'weights/det3.npy'), sess)
|
onet.load(dnet_weights[2], sess)
|
||||||
|
|
||||||
pnet_fun = lambda img: sess.run(('pnet/conv4-2/BiasAdd:0', 'pnet/prob1:0'), feed_dict={'pnet/input:0': img})
|
pnet_fun = lambda img: sess.run(('pnet/conv4-2/BiasAdd:0', 'pnet/prob1:0'), feed_dict={'pnet/input:0': img})
|
||||||
rnet_fun = lambda img: sess.run(('rnet/conv5-2/conv5-2:0', 'rnet/prob1:0'), feed_dict={'rnet/input:0': img})
|
rnet_fun = lambda img: sess.run(('rnet/conv5-2/conv5-2:0', 'rnet/prob1:0'), feed_dict={'rnet/input:0': img})
|
||||||
@ -303,7 +285,7 @@ def create_mtcnn(sess, model_path):
|
|||||||
return pnet_fun, rnet_fun, onet_fun
|
return pnet_fun, rnet_fun, onet_fun
|
||||||
|
|
||||||
|
|
||||||
def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
def run_detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
||||||
"""Detects faces in an image, and returns bounding boxes and points for them.
|
"""Detects faces in an image, and returns bounding boxes and points for them.
|
||||||
img: input image
|
img: input image
|
||||||
minsize: minimum faces' size
|
minsize: minimum faces' size
|
||||||
@ -367,11 +349,15 @@ def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
|||||||
tempimg = np.zeros((24, 24, 3, numbox))
|
tempimg = np.zeros((24, 24, 3, numbox))
|
||||||
for k in range(0, numbox):
|
for k in range(0, numbox):
|
||||||
tmp = np.zeros((int(tmph[k]), int(tmpw[k]), 3))
|
tmp = np.zeros((int(tmph[k]), int(tmpw[k]), 3))
|
||||||
|
# try:
|
||||||
tmp[dy[k] - 1:edy[k], dx[k] - 1:edx[k], :] = img[y[k] - 1:ey[k], x[k] - 1:ex[k], :]
|
tmp[dy[k] - 1:edy[k], dx[k] - 1:edx[k], :] = img[y[k] - 1:ey[k], x[k] - 1:ex[k], :]
|
||||||
|
# except ValueError:
|
||||||
|
# continue
|
||||||
if tmp.shape[0] > 0 and tmp.shape[1] > 0 or tmp.shape[0] == 0 and tmp.shape[1] == 0:
|
if tmp.shape[0] > 0 and tmp.shape[1] > 0 or tmp.shape[0] == 0 and tmp.shape[1] == 0:
|
||||||
tempimg[:, :, :, k] = imresample(tmp, (24, 24))
|
tempimg[:, :, :, k] = imresample(tmp, (24, 24))
|
||||||
else:
|
else:
|
||||||
return np.empty()
|
return np.empty()
|
||||||
|
|
||||||
tempimg = (tempimg - 127.5) * 0.0078125
|
tempimg = (tempimg - 127.5) * 0.0078125
|
||||||
tempimg1 = np.transpose(tempimg, (3, 1, 0, 2))
|
tempimg1 = np.transpose(tempimg, (3, 1, 0, 2))
|
||||||
out = rnet(tempimg1)
|
out = rnet(tempimg1)
|
||||||
@ -776,18 +762,20 @@ def rerec(bboxA):
|
|||||||
|
|
||||||
|
|
||||||
def imresample(img, sz):
|
def imresample(img, sz):
|
||||||
im_data = cv2.resize(img, (sz[1], sz[0]), interpolation=cv2.INTER_AREA) # @UndefinedVariable
|
from keras.preprocessing import image
|
||||||
|
# im_data = resize(img, (sz[0], sz[1]))
|
||||||
|
im_data = image.array_to_img(img).resize((sz[1], sz[0]))
|
||||||
|
im_data = image.img_to_array(im_data)
|
||||||
return im_data
|
return im_data
|
||||||
|
|
||||||
# This method is kept for debugging purpose
|
# def imresample(img, sz):
|
||||||
# h=img.shape[0]
|
# import cv2
|
||||||
# w=img.shape[1]
|
# im_data = cv2.resize(img, (sz[1], sz[0]), interpolation=cv2.INTER_AREA) # @UndefinedVariable
|
||||||
# hs, ws = sz
|
|
||||||
# dx = float(w) / ws
|
|
||||||
# dy = float(h) / hs
|
|
||||||
# im_data = np.zeros((hs,ws,3))
|
|
||||||
# for a1 in range(0,hs):
|
|
||||||
# for a2 in range(0,ws):
|
|
||||||
# for a3 in range(0,3):
|
|
||||||
# im_data[a1,a2,a3] = img[int(floor(a1*dy)),int(floor(a2*dx)),a3]
|
|
||||||
# return im_data
|
# return im_data
|
||||||
|
|
||||||
|
|
||||||
|
def to_rgb(img):
|
||||||
|
w, h = img.shape
|
||||||
|
ret = np.empty((w, h, 3), dtype=np.uint8)
|
||||||
|
ret[:, :, 0] = ret[:, :, 1] = ret[:, :, 2] = img
|
||||||
|
return ret
|
@ -11,7 +11,7 @@ from decimal import Decimal
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import tensorflow as tf
|
import tensorflow as tf
|
||||||
|
|
||||||
from .utils import preprocess, reverse_preprocess
|
from fawkes.utils import preprocess, reverse_preprocess
|
||||||
|
|
||||||
|
|
||||||
class FawkesMaskGeneration:
|
class FawkesMaskGeneration:
|
||||||
|
@ -8,13 +8,16 @@ import os
|
|||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import tensorflow as tf
|
||||||
|
import logging
|
||||||
|
logging.getLogger('tensorflow').disabled = True
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from fawkes.differentiator import FawkesMaskGeneration
|
||||||
from .differentiator import FawkesMaskGeneration
|
from fawkes.utils import load_extractor, init_gpu, select_target_label, dump_image, reverse_process_cloaked, \
|
||||||
from .utils import load_extractor, init_gpu, select_target_label, dump_image, reverse_process_cloaked, \
|
|
||||||
Faces
|
Faces
|
||||||
|
|
||||||
|
|
||||||
random.seed(12243)
|
random.seed(12243)
|
||||||
np.random.seed(122412)
|
np.random.seed(122412)
|
||||||
|
|
||||||
@ -63,11 +66,11 @@ def main(*argv):
|
|||||||
parser.add_argument('--directory', '-d', type=str,
|
parser.add_argument('--directory', '-d', type=str,
|
||||||
help='directory that contain images for cloaking', default='imgs/')
|
help='directory that contain images for cloaking', default='imgs/')
|
||||||
|
|
||||||
parser.add_argument('--gpu', type=str,
|
parser.add_argument('--gpu', '-g', type=str,
|
||||||
help='GPU id', default='0')
|
help='GPU id', default='0')
|
||||||
|
|
||||||
parser.add_argument('--mode', type=str,
|
parser.add_argument('--mode', '-m', type=str,
|
||||||
help='cloak generation mode', default='high')
|
help='cloak generation mode', default='mid')
|
||||||
parser.add_argument('--feature-extractor', type=str,
|
parser.add_argument('--feature-extractor', type=str,
|
||||||
help="name of the feature extractor used for optimization",
|
help="name of the feature extractor used for optimization",
|
||||||
default="high_extract")
|
default="high_extract")
|
||||||
@ -88,12 +91,12 @@ def main(*argv):
|
|||||||
if args.mode == 'low':
|
if args.mode == 'low':
|
||||||
args.feature_extractor = "high_extract"
|
args.feature_extractor = "high_extract"
|
||||||
args.th = 0.003
|
args.th = 0.003
|
||||||
args.max_step = 100
|
args.max_step = 20
|
||||||
args.lr = 15
|
args.lr = 20
|
||||||
elif args.mode == 'mid':
|
elif args.mode == 'mid':
|
||||||
args.feature_extractor = "high_extract"
|
args.feature_extractor = "high_extract"
|
||||||
args.th = 0.005
|
args.th = 0.004
|
||||||
args.max_step = 100
|
args.max_step = 50
|
||||||
args.lr = 15
|
args.lr = 15
|
||||||
elif args.mode == 'high':
|
elif args.mode == 'high':
|
||||||
args.feature_extractor = "high_extract"
|
args.feature_extractor = "high_extract"
|
||||||
@ -101,10 +104,14 @@ def main(*argv):
|
|||||||
args.max_step = 100
|
args.max_step = 100
|
||||||
args.lr = 10
|
args.lr = 10
|
||||||
elif args.mode == 'ultra':
|
elif args.mode == 'ultra':
|
||||||
|
if not tf.test.is_gpu_available():
|
||||||
|
print("Please enable GPU for ultra setting...")
|
||||||
|
sys.exit(1)
|
||||||
|
# args.feature_extractor = ["high_extract", 'high2_extract']
|
||||||
args.feature_extractor = "high_extract"
|
args.feature_extractor = "high_extract"
|
||||||
args.th = 0.01
|
args.th = 0.015
|
||||||
args.max_step = 1000
|
args.max_step = 2000
|
||||||
args.lr = 5
|
args.lr = 8
|
||||||
elif args.mode == 'custom':
|
elif args.mode == 'custom':
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -115,20 +122,23 @@ def main(*argv):
|
|||||||
args.format = 'jpeg'
|
args.format = 'jpeg'
|
||||||
|
|
||||||
sess = init_gpu(args.gpu)
|
sess = init_gpu(args.gpu)
|
||||||
fs_names = [args.feature_extractor]
|
|
||||||
feature_extractors_ls = [load_extractor(name) for name in fs_names]
|
|
||||||
|
|
||||||
image_paths = glob.glob(os.path.join(args.directory, "*"))
|
image_paths = glob.glob(os.path.join(args.directory, "*"))
|
||||||
image_paths = [path for path in image_paths if "_cloaked" not in path.split("/")[-1]]
|
image_paths = [path for path in image_paths if "_cloaked" not in path.split("/")[-1]]
|
||||||
if not image_paths:
|
if not image_paths:
|
||||||
print("No images in the directory")
|
raise Exception("No images in the directory")
|
||||||
exit(1)
|
|
||||||
|
|
||||||
faces = Faces(image_paths, sess, verbose=1)
|
faces = Faces(image_paths, sess, verbose=1)
|
||||||
|
|
||||||
orginal_images = faces.cropped_faces
|
orginal_images = faces.cropped_faces
|
||||||
orginal_images = np.array(orginal_images)
|
orginal_images = np.array(orginal_images)
|
||||||
|
|
||||||
|
fs_names = [args.feature_extractor]
|
||||||
|
if isinstance(args.feature_extractor, list):
|
||||||
|
fs_names = args.feature_extractor
|
||||||
|
|
||||||
|
feature_extractors_ls = [load_extractor(name) for name in fs_names]
|
||||||
|
|
||||||
if args.separate_target:
|
if args.separate_target:
|
||||||
target_embedding = []
|
target_embedding = []
|
||||||
for org_img in orginal_images:
|
for org_img in orginal_images:
|
||||||
@ -154,6 +164,7 @@ def main(*argv):
|
|||||||
|
|
||||||
elapsed_time = time.time() - start_time
|
elapsed_time = time.time() - start_time
|
||||||
print('attack cost %f s' % (elapsed_time))
|
print('attack cost %f s' % (elapsed_time))
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
163
fawkes/utils.py
163
fawkes/utils.py
@ -27,8 +27,7 @@ from keras.preprocessing import image
|
|||||||
from skimage.transform import resize
|
from skimage.transform import resize
|
||||||
from sklearn.metrics import pairwise_distances
|
from sklearn.metrics import pairwise_distances
|
||||||
|
|
||||||
|
from fawkes.align_face import align, aligner
|
||||||
from .align_face import align, aligner
|
|
||||||
from six.moves.urllib.request import urlopen
|
from six.moves.urllib.request import urlopen
|
||||||
|
|
||||||
if sys.version_info[0] == 2:
|
if sys.version_info[0] == 2:
|
||||||
@ -89,6 +88,12 @@ def load_image(path):
|
|||||||
|
|
||||||
class Faces(object):
|
class Faces(object):
|
||||||
def __init__(self, image_paths, sess, verbose=1):
|
def __init__(self, image_paths, sess, verbose=1):
|
||||||
|
model_dir = os.path.join(os.path.expanduser('~'), '.fawkes')
|
||||||
|
if not os.path.exists(os.path.join(model_dir, "mtcnn.p.gz")):
|
||||||
|
os.makedirs(model_dir, exist_ok=True)
|
||||||
|
get_file("mtcnn.p.gz", "http://sandlab.cs.uchicago.edu/fawkes/files/mtcnn.p.gz", cache_dir=model_dir,
|
||||||
|
cache_subdir='')
|
||||||
|
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.aligner = aligner(sess)
|
self.aligner = aligner(sess)
|
||||||
self.org_faces = []
|
self.org_faces = []
|
||||||
@ -102,6 +107,10 @@ class Faces(object):
|
|||||||
cur_img = load_image(p)
|
cur_img = load_image(p)
|
||||||
self.org_faces.append(cur_img)
|
self.org_faces.append(cur_img)
|
||||||
align_img = align(cur_img, self.aligner, margin=0.7)
|
align_img = align(cur_img, self.aligner, margin=0.7)
|
||||||
|
if align_img is None:
|
||||||
|
print("Find 0 face(s) in {}".format(p.split("/")[-1]))
|
||||||
|
continue
|
||||||
|
|
||||||
cur_faces = align_img[0]
|
cur_faces = align_img[0]
|
||||||
|
|
||||||
cur_shapes = [f.shape[:-1] for f in cur_faces]
|
cur_shapes = [f.shape[:-1] for f in cur_faces]
|
||||||
@ -327,6 +336,7 @@ def load_extractor(name):
|
|||||||
if os.path.exists(model_file):
|
if os.path.exists(model_file):
|
||||||
model = keras.models.load_model(model_file)
|
model = keras.models.load_model(model_file)
|
||||||
else:
|
else:
|
||||||
|
print("Download models...")
|
||||||
get_file("{}.h5".format(name), "http://sandlab.cs.uchicago.edu/fawkes/files/{}.h5".format(name),
|
get_file("{}.h5".format(name), "http://sandlab.cs.uchicago.edu/fawkes/files/{}.h5".format(name),
|
||||||
cache_dir=model_dir, cache_subdir='')
|
cache_dir=model_dir, cache_subdir='')
|
||||||
model = keras.models.load_model(model_file)
|
model = keras.models.load_model(model_file)
|
||||||
@ -568,152 +578,3 @@ def _makedirs_exist_ok(datadir):
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
os.makedirs(datadir, exist_ok=True) # pylint: disable=unexpected-keyword-arg
|
os.makedirs(datadir, exist_ok=True) # pylint: disable=unexpected-keyword-arg
|
||||||
|
|
||||||
# class CloakData(object):
|
|
||||||
# def __init__(self, protect_directory=None, img_shape=(224, 224)):
|
|
||||||
#
|
|
||||||
# self.img_shape = img_shape
|
|
||||||
# # self.train_data_dir, self.test_data_dir, self.number_classes, self.number_samples = get_dataset_path(dataset)
|
|
||||||
# # self.all_labels = sorted(list(os.listdir(self.train_data_dir)))
|
|
||||||
# self.protect_directory = protect_directory
|
|
||||||
#
|
|
||||||
# self.protect_X = self.load_label_data(self.protect_directory)
|
|
||||||
#
|
|
||||||
# self.cloaked_protect_train_X = None
|
|
||||||
#
|
|
||||||
# self.label2path_train, self.label2path_test, self.path2idx = self.build_data_mapping()
|
|
||||||
# self.all_training_path = self.get_all_data_path(self.label2path_train)
|
|
||||||
# self.all_test_path = self.get_all_data_path(self.label2path_test)
|
|
||||||
# self.protect_class_path = self.get_class_image_files(os.path.join(self.train_data_dir, self.protect_class))
|
|
||||||
#
|
|
||||||
# def get_class_image_files(self, path):
|
|
||||||
# return [os.path.join(path, f) for f in os.listdir(path)]
|
|
||||||
#
|
|
||||||
# def extractor_ls_predict(self, feature_extractors_ls, X):
|
|
||||||
# feature_ls = []
|
|
||||||
# for extractor in feature_extractors_ls:
|
|
||||||
# cur_features = extractor.predict(X)
|
|
||||||
# feature_ls.append(cur_features)
|
|
||||||
# concated_feature_ls = np.concatenate(feature_ls, axis=1)
|
|
||||||
# concated_feature_ls = normalize(concated_feature_ls)
|
|
||||||
# return concated_feature_ls
|
|
||||||
#
|
|
||||||
# def load_embeddings(self, feature_extractors_names):
|
|
||||||
# dictionaries = []
|
|
||||||
# for extractor_name in feature_extractors_names:
|
|
||||||
# path2emb = pickle.load(open("../feature_extractors/embeddings/{}_emb_norm.p".format(extractor_name), "rb"))
|
|
||||||
# dictionaries.append(path2emb)
|
|
||||||
#
|
|
||||||
# merge_dict = {}
|
|
||||||
# for k in dictionaries[0].keys():
|
|
||||||
# cur_emb = [dic[k] for dic in dictionaries]
|
|
||||||
# merge_dict[k] = np.concatenate(cur_emb)
|
|
||||||
# return merge_dict
|
|
||||||
#
|
|
||||||
# def select_target_label(self, feature_extractors_ls, feature_extractors_names, metric='l2'):
|
|
||||||
# original_feature_x = self.extractor_ls_predict(feature_extractors_ls, self.protect_train_X)
|
|
||||||
#
|
|
||||||
# path2emb = self.load_embeddings(feature_extractors_names)
|
|
||||||
# items = list(path2emb.items())
|
|
||||||
# paths = [p[0] for p in items]
|
|
||||||
# embs = [p[1] for p in items]
|
|
||||||
# embs = np.array(embs)
|
|
||||||
#
|
|
||||||
# pair_dist = pairwise_distances(original_feature_x, embs, metric)
|
|
||||||
# max_sum = np.min(pair_dist, axis=0)
|
|
||||||
# sorted_idx = np.argsort(max_sum)[::-1]
|
|
||||||
#
|
|
||||||
# highest_num = 0
|
|
||||||
# paired_target_X = None
|
|
||||||
# final_target_class_path = None
|
|
||||||
# for idx in sorted_idx[:5]:
|
|
||||||
# target_class_path = paths[idx]
|
|
||||||
# cur_target_X = self.load_dir(target_class_path)
|
|
||||||
# cur_target_X = np.concatenate([cur_target_X, cur_target_X, cur_target_X])
|
|
||||||
# cur_tot_sum, cur_paired_target_X = self.calculate_dist_score(self.protect_train_X, cur_target_X,
|
|
||||||
# feature_extractors_ls,
|
|
||||||
# metric=metric)
|
|
||||||
# if cur_tot_sum > highest_num:
|
|
||||||
# highest_num = cur_tot_sum
|
|
||||||
# paired_target_X = cur_paired_target_X
|
|
||||||
# final_target_class_path = target_class_path
|
|
||||||
#
|
|
||||||
# np.random.shuffle(paired_target_X)
|
|
||||||
# return final_target_class_path, paired_target_X
|
|
||||||
#
|
|
||||||
# def calculate_dist_score(self, a, b, feature_extractors_ls, metric='l2'):
|
|
||||||
# features1 = self.extractor_ls_predict(feature_extractors_ls, a)
|
|
||||||
# features2 = self.extractor_ls_predict(feature_extractors_ls, b)
|
|
||||||
#
|
|
||||||
# pair_cos = pairwise_distances(features1, features2, metric)
|
|
||||||
# max_sum = np.min(pair_cos, axis=0)
|
|
||||||
# max_sum_arg = np.argsort(max_sum)[::-1]
|
|
||||||
# max_sum_arg = max_sum_arg[:len(a)]
|
|
||||||
# max_sum = [max_sum[i] for i in max_sum_arg]
|
|
||||||
# paired_target_X = [b[j] for j in max_sum_arg]
|
|
||||||
# paired_target_X = np.array(paired_target_X)
|
|
||||||
# return np.min(max_sum), paired_target_X
|
|
||||||
#
|
|
||||||
# def get_all_data_path(self, label2path):
|
|
||||||
# all_paths = []
|
|
||||||
# for k, v in label2path.items():
|
|
||||||
# cur_all_paths = [os.path.join(k, cur_p) for cur_p in v]
|
|
||||||
# all_paths.extend(cur_all_paths)
|
|
||||||
# return all_paths
|
|
||||||
#
|
|
||||||
# def load_label_data(self, label):
|
|
||||||
# train_label_path = os.path.join(self.train_data_dir, label)
|
|
||||||
# test_label_path = os.path.join(self.test_data_dir, label)
|
|
||||||
# train_X = self.load_dir(train_label_path)
|
|
||||||
# test_X = self.load_dir(test_label_path)
|
|
||||||
# return train_X, test_X
|
|
||||||
#
|
|
||||||
# def load_dir(self, path):
|
|
||||||
# assert os.path.exists(path)
|
|
||||||
# x_ls = []
|
|
||||||
# for file in os.listdir(path):
|
|
||||||
# cur_path = os.path.join(path, file)
|
|
||||||
# im = image.load_img(cur_path, target_size=self.img_shape)
|
|
||||||
# im = image.img_to_array(im)
|
|
||||||
# x_ls.append(im)
|
|
||||||
# raw_x = np.array(x_ls)
|
|
||||||
# return preprocess_input(raw_x)
|
|
||||||
#
|
|
||||||
# def build_data_mapping(self):
|
|
||||||
# label2path_train = {}
|
|
||||||
# label2path_test = {}
|
|
||||||
# idx = 0
|
|
||||||
# path2idx = {}
|
|
||||||
# for label_name in self.all_labels:
|
|
||||||
# full_path_train = os.path.join(self.train_data_dir, label_name)
|
|
||||||
# full_path_test = os.path.join(self.test_data_dir, label_name)
|
|
||||||
# label2path_train[full_path_train] = list(os.listdir(full_path_train))
|
|
||||||
# label2path_test[full_path_test] = list(os.listdir(full_path_test))
|
|
||||||
# for img_file in os.listdir(full_path_train):
|
|
||||||
# path2idx[os.path.join(full_path_train, img_file)] = idx
|
|
||||||
# for img_file in os.listdir(full_path_test):
|
|
||||||
# path2idx[os.path.join(full_path_test, img_file)] = idx
|
|
||||||
# idx += 1
|
|
||||||
# return label2path_train, label2path_test, path2idx
|
|
||||||
#
|
|
||||||
# def generate_data_post_cloak(self, sybil=False):
|
|
||||||
# assert self.cloaked_protect_train_X is not None
|
|
||||||
# while True:
|
|
||||||
# batch_X = []
|
|
||||||
# batch_Y = []
|
|
||||||
# cur_batch_path = random.sample(self.all_training_path, 32)
|
|
||||||
# for p in cur_batch_path:
|
|
||||||
# cur_y = self.path2idx[p]
|
|
||||||
# if p in self.protect_class_path:
|
|
||||||
# cur_x = random.choice(self.cloaked_protect_train_X)
|
|
||||||
# elif sybil and (p in self.sybil_class):
|
|
||||||
# cur_x = random.choice(self.cloaked_sybil_train_X)
|
|
||||||
# else:
|
|
||||||
# im = image.load_img(p, target_size=self.img_shape)
|
|
||||||
# im = image.img_to_array(im)
|
|
||||||
# cur_x = preprocess_input(im)
|
|
||||||
# batch_X.append(cur_x)
|
|
||||||
# batch_Y.append(cur_y)
|
|
||||||
# batch_X = np.array(batch_X)
|
|
||||||
# batch_Y = to_categorical(np.array(batch_Y), num_classes=self.number_classes)
|
|
||||||
# yield batch_X, batch_Y
|
|
||||||
|
@ -46,7 +46,8 @@ def detect_face(image_url):
|
|||||||
conn.request("POST", "/face/v1.0/detect?%s" % params, body, headers)
|
conn.request("POST", "/face/v1.0/detect?%s" % params, body, headers)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
data = json.loads(response.read())
|
data = json.loads(response.read())
|
||||||
print(data)
|
#
|
||||||
|
# print(data)
|
||||||
conn.close()
|
conn.close()
|
||||||
return data[0]["faceId"]
|
return data[0]["faceId"]
|
||||||
|
|
||||||
@ -229,7 +230,6 @@ def eval(original_faceIds, personGroupId, protect_personId):
|
|||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
data = json.loads(response.read())
|
data = json.loads(response.read())
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
face = data[0]
|
face = data[0]
|
||||||
print(face)
|
print(face)
|
||||||
if len(face["candidates"]) and face["candidates"][0]["personId"] == protect_personId:
|
if len(face["candidates"]) and face["candidates"][0]["personId"] == protect_personId:
|
||||||
@ -270,8 +270,8 @@ def get_trainStatus(personGroupId):
|
|||||||
conn.request("GET", "/face/v1.0/persongroups/{}/training?%s".format(personGroupId) % params, body, headers)
|
conn.request("GET", "/face/v1.0/persongroups/{}/training?%s".format(personGroupId) % params, body, headers)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
data = response.read()
|
data = response.read()
|
||||||
print(data)
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def test_cloak():
|
def test_cloak():
|
||||||
@ -279,12 +279,12 @@ def test_cloak():
|
|||||||
total_idx = range(0, 82)
|
total_idx = range(0, 82)
|
||||||
TRAIN_RANGE = random.sample(total_idx, NUM_TRAIN)
|
TRAIN_RANGE = random.sample(total_idx, NUM_TRAIN)
|
||||||
|
|
||||||
TEST_RANGE = TRAIN_RANGE
|
TEST_RANGE = random.sample([i for i in total_idx if i not in TRAIN_RANGE], 20)
|
||||||
|
|
||||||
personGroupId = 'all'
|
personGroupId = 'all'
|
||||||
|
|
||||||
# delete_personGroup(personGroupId)
|
# delete_personGroup(personGroupId)
|
||||||
create_personGroupId(personGroupId, personGroupId)
|
# create_personGroupId(personGroupId, personGroupId)
|
||||||
|
|
||||||
with open("protect_personId.txt", 'r') as f:
|
with open("protect_personId.txt", 'r') as f:
|
||||||
protect_personId = f.read()
|
protect_personId = f.read()
|
||||||
@ -305,22 +305,25 @@ def test_cloak():
|
|||||||
print("Unable to add {}-th image of protect person".format(idx))
|
print("Unable to add {}-th image of protect person".format(idx))
|
||||||
|
|
||||||
# add other people
|
# add other people
|
||||||
for idx_person in range(500):
|
# for idx_person in range(1300, 5000):
|
||||||
personId = create_personId(personGroupId, str(idx_person))
|
# personId = create_personId(personGroupId, str(idx_person))
|
||||||
print("Created personId: {}".format(idx_person))
|
# print("Created personId: {}".format(idx_person))
|
||||||
for idx_image in range(10):
|
# for idx_image in range(10):
|
||||||
image_url = "http://sandlab.cs.uchicago.edu/fawkes/files/target_data/{}/{}.jpg".format(
|
# image_url = "http://sandlab.cs.uchicago.edu/fawkes/files/target_data/{}/{}.jpg".format(
|
||||||
idx_person, idx_image)
|
# idx_person, idx_image)
|
||||||
r = add_persistedFaceId(personGroupId, personId, image_url)
|
# r = add_persistedFaceId(personGroupId, personId, image_url)
|
||||||
if r is not None:
|
# if r is not None:
|
||||||
print("Added {}".format(idx_image))
|
# print("Added {}".format(idx_image))
|
||||||
else:
|
# else:
|
||||||
print("Unable to add {}-th image".format(idx_image))
|
# print("Unable to add {}-th image".format(idx_image))
|
||||||
|
|
||||||
# train model based on personGroup
|
# train model based on personGroup
|
||||||
|
|
||||||
train_personGroup(personGroupId)
|
train_personGroup(personGroupId)
|
||||||
time.sleep(4)
|
|
||||||
get_trainStatus(personGroupId)
|
while json.loads(get_trainStatus(personGroupId))['status'] != 'succeeded':
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
# list_personGroupPerson(personGroupId)
|
# list_personGroupPerson(personGroupId)
|
||||||
|
|
||||||
# test original image
|
# test original image
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
scikit-image
|
|
||||||
argparse
|
|
||||||
glob
|
|
||||||
numpy==1.18.4
|
|
6
setup.py
6
setup.py
@ -75,13 +75,15 @@ class DeployCommand(Command):
|
|||||||
setup_requires = []
|
setup_requires = []
|
||||||
|
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'numpy>=1.16.4',
|
'numpy==1.16.4',
|
||||||
'tensorflow>=1.13.1',
|
# 'tensorflow-gpu>=1.13.1, <=1.14.0',
|
||||||
|
'tensorflow>=1.13.1, <=1.14.0',
|
||||||
'argparse',
|
'argparse',
|
||||||
'keras==2.2.5',
|
'keras==2.2.5',
|
||||||
'scikit-image',
|
'scikit-image',
|
||||||
'pillow>=7.0.0',
|
'pillow>=7.0.0',
|
||||||
'opencv-python>=4.2.0.34',
|
'opencv-python>=4.2.0.34',
|
||||||
|
'sklearn',
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
Loading…
Reference in New Issue
Block a user