├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── environment.yml ├── example-gonciarz.gif ├── example.gif ├── freeze_model.py ├── generate_train_data.py ├── reduce_model.py ├── run_video.py ├── run_webcam.py └── zapytaj-beczke.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # dotenv 81 | .env 82 | 83 | # virtualenv 84 | .venv/ 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | # Custom 95 | .idea/ 96 | face2face-model*/ 97 | face2face-reduced-model*/ 98 | landmarks 99 | original 100 | angela_merkel_speech.mp4 101 | shape_predictor_68_face_landmarks.dat 102 | 103 | *.mp4 104 | *.gif 105 | *.bz2 106 | tmp* 107 | *.pb 108 | photos 109 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pix2pix-tensorflow"] 2 | path = pix2pix-tensorflow 3 | url = https://github.com/karolmajek/pix2pix-tensorflow.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dat Tran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # face2face-demo 2 | 3 | 4 | ![Face2Face Gonciarz](zapytaj-beczke.gif) 5 | 6 | This repo is forked from [https://github.com/datitran/face2face-demo](https://github.com/datitran/face2face-demo). Thanks Dat Tran for awesome work! 7 | 8 | This is a pix2pix demo that learns from facial landmarks and translates this into a face. A webcam-enabled application is also provided that translates your face to the trained face in real-time. 9 | 10 | You just read original description, after this modification it is not working in real-time anymore, thanks to increased resolution to 1024x1024. 11 | 12 | # TODO/Changes 13 | 14 | - [x] Increased resolution to 1024x1024! 15 | - [x] Pix2pix as submodule 16 | - [ ] Use [OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose) for face/skeleton detection 17 | 18 | 19 | ## Getting Started 20 | 21 | ### 1. Prepare Environment 22 | 23 | ``` 24 | # Clone this repo 25 | git clone https://github.com/karolmajek/face2face-demo.git --recursive 26 | 27 | # Create the conda environment from file (Mac OSX) 28 | conda env create -f environment.yml 29 | ``` 30 | 31 | ### 2. Generate Training Data 32 | 33 | ``` 34 | python generate_train_data.py --file angela_merkel_speech.mp4 --num 400 --landmark-model shape_predictor_68_face_landmarks.dat 35 | ``` 36 | 37 | Input: 38 | 39 | - `file` is the name of the video file from which you want to create the data set. 40 | - `num` is the number of train data to be created. 41 | - `landmark-model` is the facial landmark model that is used to detect the landmarks. A pre-trained facial landmark model is provided [here](http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2). 42 | 43 | Output: 44 | 45 | - Two folders `original` and `landmarks` will be created. 46 | 47 | If you want to download my dataset, here is also the [video file](https://u7410512.dl.dropboxusercontent.com/u/7410512/face2face-demo/angela_merkel_speech.mp4) that I used and the generated [training dataset](https://u7410512.dl.dropboxusercontent.com/u/7410512/face2face-demo/dataset.zip) (400 images already split into training and validation). 48 | 49 | ### 3. Train Model 50 | 51 | ``` 52 | # Clone the repo from Christopher Hesse's pix2pix TensorFlow implementation 53 | git clone https://github.com/affinelayer/pix2pix-tensorflow.git 54 | 55 | # Move the original and landmarks folder into the pix2pix-tensorflow folder 56 | mv face2face-demo/landmarks face2face-demo/original pix2pix-tensorflow/photos 57 | 58 | # Go into the pix2pix-tensorflow folder 59 | cd pix2pix-tensorflow/ 60 | 61 | # Resize original images 62 | python tools/process.py \ 63 | --input_dir photos/original \ 64 | --operation resize \ 65 | --size 1024 \ 66 | --output_dir photos/original_resized 67 | 68 | # Resize landmark images 69 | python tools/process.py \ 70 | --input_dir photos/landmarks \ 71 | --operation resize \ 72 | --size 1024 \ 73 | --output_dir photos/landmarks_resized 74 | 75 | # Combine both resized original and landmark images 76 | python tools/process.py \ 77 | --input_dir photos/landmarks_resized \ 78 | --b_dir photos/original_resized \ 79 | --operation combine \ 80 | --output_dir photos/combined 81 | 82 | # Split into train/val set 83 | python tools/split.py \ 84 | --dir photos/combined 85 | 86 | # Train the model on the data 87 | python pix2pix.py \ 88 | --mode train \ 89 | --output_dir face2face-model \ 90 | --max_epochs 200 \ 91 | --input_dir photos/combined/train \ 92 | --which_direction AtoB 93 | ``` 94 | 95 | For more information around training, have a look at Christopher Hesse's [pix2pix-tensorflow](https://github.com/affinelayer/pix2pix-tensorflow) implementation. 96 | 97 | ### 4. Export Model 98 | 99 | 1. First, we need to reduce the trained model so that we can use an image tensor as input: 100 | ``` 101 | python ../reduce_model.py --model-input face2face-model --model-output face2face-reduced-model 102 | ``` 103 | 104 | Input: 105 | 106 | - `model-input` is the model folder to be imported. 107 | - `model-output` is the model (reduced) folder to be exported. 108 | 109 | Output: 110 | 111 | - It returns a reduced model with less weights file size than the original model. 112 | 113 | 2. Second, we freeze the reduced model to a single file. 114 | ``` 115 | python ../freeze_model.py --model-folder face2face-reduced-model 116 | ``` 117 | 118 | Input: 119 | 120 | - `model-folder` is the model folder of the reduced model. 121 | 122 | Output: 123 | 124 | - It returns a frozen model file `frozen_model.pb` in the model folder. 125 | 126 | ### 5. Run Demo 127 | 128 | ``` 129 | python run_webcam.py --source 0 --show 0 --landmark-model shape_predictor_68_face_landmarks.dat --tf-model face2face-reduced-model/frozen_model.pb 130 | ``` 131 | 132 | Input: 133 | 134 | - `source` is the device index of the camera (default=0). 135 | - `show` is an option to either display the normal input (0) or the facial landmark (1) alongside the generated image (default=0). 136 | - `landmark-model` is the facial landmark model that is used to detect the landmarks. 137 | - `tf-model` is the frozen model file. 138 | 139 | 140 | 141 | ## Models 142 | 143 | ### Dat Tran - Angela Merkel 256x256 144 | 145 | ![example](example.gif) 146 | 147 | **THIS MODEL WILL NOT WORK WITH THIS CODE** 148 | 149 | Pre-trained frozen model [here](https://dl.dropboxusercontent.com/s/rzfaoeb3e2ta343/face2face_model_epoch_200.zip). This model is trained on 400 images with epoch 200. 150 | 151 | ### Karol Majek - Krzysztof Gonciarz 1024x1024 152 | 153 | Me, my face and output: 154 | 155 | ![Face2Face Gonciarz](example-gonciarz.gif) 156 | 157 | 158 | Video on youtube: 159 | 160 | [![Face2Face Krzysztof Gonciarz YAPZTAJ KASIĘ CICHOPEK 4 - Zapytaj Beczkę #146](http://img.youtube.com/vi/v5VDJKCrP6A/0.jpg)](http://www.youtube.com/watch?v=v5VDJKCrP6A) 161 | 162 | Frozen model can be downloaded from [here](https://goo.gl/8BgnXA) (1.1GB) 163 | 164 | ## Requirements 165 | - [Anaconda / Python 3.5](https://www.continuum.io/downloads) 166 | - [TensorFlow 1.2](https://www.tensorflow.org/) 167 | - [OpenCV 3.0](http://opencv.org/) 168 | - [Dlib 19.4](http://dlib.net/) 169 | 170 | ## Acknowledgments 171 | 172 | Thanks to [Dat Tran](http://www.dat-tran.com/) for inspiration, code and model! 173 | 174 | ## Copyright 175 | 176 | See [LICENSE](LICENSE) for details. 177 | Copyright (c) 2017 [Dat Tran](http://www.dat-tran.com/). 178 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: face2face-demo 2 | channels: !!python/tuple 3 | - menpo 4 | - defaults 5 | dependencies: 6 | - bzip2=1.0.6=3 7 | - jlaura::opencv3=3.0.0=py35_0 8 | - jpeg=9b=0 9 | - libpng=1.6.27=0 10 | - menpo::boost=1.59.0=py35_0 11 | - menpo::dlib=19.4=py35_0 12 | - menpo::tbb=4.3_20141023=0 13 | - mkl=2017.0.3=0 14 | - numpy=1.13.0=py35_0 15 | - openssl=1.0.2l=0 16 | - pip=9.0.1=py35_1 17 | - python=3.5.3=1 18 | - readline=6.2=2 19 | - setuptools=27.2.0=py35_0 20 | - sqlite=3.13.0=0 21 | - tk=8.5.18=0 22 | - wheel=0.29.0=py35_0 23 | - xz=5.2.2=1 24 | - zlib=1.2.8=3 25 | - pip: 26 | - backports.weakref==1.0rc1 27 | - bleach==1.5.0 28 | - html5lib==0.9999999 29 | - imutils==0.4.3 30 | - markdown==2.6.8 31 | - protobuf==3.3.0 32 | - six==1.10.0 33 | - tensorflow==1.2.1 34 | - werkzeug==0.12.2 35 | prefix: /Users/datitran/anaconda/envs/face2face-demo 36 | 37 | -------------------------------------------------------------------------------- /example-gonciarz.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karolmajek/face2face-demo/7017ec4060136d96a81f659f4209b62e64f9a354/example-gonciarz.gif -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karolmajek/face2face-demo/7017ec4060136d96a81f659f4209b62e64f9a354/example.gif -------------------------------------------------------------------------------- /freeze_model.py: -------------------------------------------------------------------------------- 1 | import os, argparse 2 | import tensorflow as tf 3 | from tensorflow.python.framework import graph_util 4 | 5 | dir = os.path.dirname(os.path.realpath(__file__)) 6 | 7 | 8 | def freeze_graph(model_folder): 9 | # We retrieve our checkpoint fullpath 10 | checkpoint = tf.train.get_checkpoint_state(model_folder) 11 | input_checkpoint = checkpoint.model_checkpoint_path 12 | 13 | # We precise the file fullname of our freezed graph 14 | absolute_model_folder = '/'.join(input_checkpoint.split('/')[:-1]) 15 | output_graph = absolute_model_folder + '/frozen_model.pb' 16 | 17 | # Before exporting our graph, we need to precise what is our output node 18 | # This is how TF decides what part of the Graph he has to keep and what part it can dump 19 | # NOTE: this variable is plural, because you can have multiple output nodes 20 | output_node_names = 'generate_output/output' 21 | 22 | # We clear devices to allow TensorFlow to control on which device it will load operations 23 | clear_devices = True 24 | 25 | # We import the meta graph and retrieve a Saver 26 | saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=clear_devices) 27 | 28 | # We retrieve the protobuf graph definition 29 | graph = tf.get_default_graph() 30 | input_graph_def = graph.as_graph_def() 31 | 32 | # We start a session and restore the graph weights 33 | with tf.Session() as sess: 34 | saver.restore(sess, input_checkpoint) 35 | 36 | # We use a built-in TF helper to export variables to constants 37 | output_graph_def = graph_util.convert_variables_to_constants( 38 | sess, # The session is used to retrieve the weights 39 | input_graph_def, # The graph_def is used to retrieve the nodes 40 | output_node_names.split(",") # The output node names are used to select the usefull nodes 41 | ) 42 | 43 | # Finally we serialize and dump the output graph to the filesystem 44 | with tf.gfile.GFile(output_graph, 'wb') as f: 45 | f.write(output_graph_def.SerializeToString()) 46 | print('%d ops in the final graph.' % len(output_graph_def.node)) 47 | 48 | 49 | if __name__ == '__main__': 50 | parser = argparse.ArgumentParser() 51 | parser.add_argument('--model-folder', type=str, help='Model folder to export') 52 | args = parser.parse_args() 53 | 54 | freeze_graph(args.model_folder) 55 | -------------------------------------------------------------------------------- /generate_train_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import dlib 4 | import time 5 | import argparse 6 | import numpy as np 7 | from imutils import video 8 | 9 | DOWNSAMPLE_RATIO = 2 10 | 11 | 12 | def reshape_for_polyline(array): 13 | return np.array(array, np.int32).reshape((-1, 1, 2)) 14 | 15 | 16 | def main(): 17 | os.makedirs('original', exist_ok=True) 18 | os.makedirs('landmarks', exist_ok=True) 19 | os.makedirs('rendered', exist_ok=True) 20 | 21 | cap = cv2.VideoCapture(args.filename) 22 | fps = video.FPS().start() 23 | 24 | count = 0 25 | while cap.isOpened(): 26 | for tmp in range(10): 27 | ret, frame = cap.read() 28 | if ret is None: 29 | break 30 | 31 | if ret is None: 32 | break 33 | frame_resize = cv2.resize(frame, None, fx=1 / DOWNSAMPLE_RATIO, fy=1 / DOWNSAMPLE_RATIO) 34 | gray = cv2.cvtColor(frame_resize, cv2.COLOR_BGR2GRAY) 35 | faces = detector(gray, 1) 36 | black_image = np.zeros(frame.shape, np.uint8) 37 | 38 | t = time.time() 39 | 40 | # Perform if there is a face detected 41 | if len(faces) == 1: 42 | for face in faces: 43 | detected_landmarks = predictor(gray, face).parts() 44 | landmarks = [[p.x * DOWNSAMPLE_RATIO, p.y * DOWNSAMPLE_RATIO] for p in detected_landmarks] 45 | 46 | jaw = reshape_for_polyline(landmarks[0:17]) 47 | left_eyebrow = reshape_for_polyline(landmarks[22:27]) 48 | right_eyebrow = reshape_for_polyline(landmarks[17:22]) 49 | nose_bridge = reshape_for_polyline(landmarks[27:31]) 50 | lower_nose = reshape_for_polyline(landmarks[30:35]) 51 | left_eye = reshape_for_polyline(landmarks[42:48]) 52 | right_eye = reshape_for_polyline(landmarks[36:42]) 53 | outer_lip = reshape_for_polyline(landmarks[48:60]) 54 | inner_lip = reshape_for_polyline(landmarks[60:68]) 55 | 56 | color = (255, 255, 255) 57 | thickness = 3 58 | 59 | cv2.polylines(black_image, [jaw], False, color, thickness) 60 | cv2.polylines(black_image, [left_eyebrow], False, color, thickness) 61 | cv2.polylines(black_image, [right_eyebrow], False, color, thickness) 62 | cv2.polylines(black_image, [nose_bridge], False, color, thickness) 63 | cv2.polylines(black_image, [lower_nose], True, color, thickness) 64 | cv2.polylines(black_image, [left_eye], True, color, thickness) 65 | cv2.polylines(black_image, [right_eye], True, color, thickness) 66 | cv2.polylines(black_image, [outer_lip], True, color, thickness) 67 | cv2.polylines(black_image, [inner_lip], True, color, thickness) 68 | 69 | # Display the resulting frame 70 | count += 1 71 | print(count) 72 | cv2.imwrite("original/{}.png".format(count), frame) 73 | cv2.imwrite("landmarks/{}.png".format(count), black_image) 74 | tmpimage = frame.copy() 75 | tmpimage[black_image>0]=255 76 | cv2.imwrite("rendered/{}.png".format(count), tmpimage) 77 | # cv2.imshow('img',cv2.resize(cv2.addWeighted(frame, 0.5, black_image, 0.5, 0.0), None, fx=0.5, fy=0.5)) 78 | # cv2.imshow('img',cv2.resize(tmpimage, None, fx=0.5, fy=0.5)) 79 | fps.update() 80 | 81 | print('[INFO] elapsed time: {:.2f}'.format(time.time() - t)) 82 | 83 | if count == args.number: # only take 400 photos 84 | break 85 | elif cv2.waitKey(1) & 0xFF == ord('q'): 86 | break 87 | else: 88 | print("No face detected") 89 | 90 | fps.stop() 91 | print('[INFO] elapsed time (total): {:.2f}'.format(fps.elapsed())) 92 | print('[INFO] approx. FPS: {:.2f}'.format(fps.fps())) 93 | 94 | cap.release() 95 | cv2.destroyAllWindows() 96 | 97 | 98 | if __name__ == '__main__': 99 | parser = argparse.ArgumentParser() 100 | parser.add_argument('--file', dest='filename', type=str, help='Name of the video file.') 101 | parser.add_argument('--num', dest='number', type=int, help='Number of train data to be created.') 102 | parser.add_argument('--landmark-model', dest='face_landmark_shape_file', type=str, help='Face landmark model file.') 103 | args = parser.parse_args() 104 | 105 | # Create the face predictor and landmark predictor 106 | detector = dlib.get_frontal_face_detector() 107 | predictor = dlib.shape_predictor(args.face_landmark_shape_file) 108 | 109 | main() 110 | -------------------------------------------------------------------------------- /reduce_model.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import tensorflow as tf 3 | 4 | CROP_SIZE = 1024 # scale_size = CROP_SIZE 5 | ngf = 64 6 | ndf = 64 7 | 8 | 9 | def preprocess(image): 10 | with tf.name_scope('preprocess'): 11 | # [0, 1] => [-1, 1] 12 | return image * 2 - 1 13 | 14 | 15 | def deprocess(image): 16 | with tf.name_scope('deprocess'): 17 | # [-1, 1] => [0, 1] 18 | return (image + 1) / 2 19 | 20 | 21 | def conv(batch_input, out_channels, stride): 22 | with tf.variable_scope('conv'): 23 | in_channels = batch_input.get_shape()[3] 24 | filter = tf.get_variable('filter', [4, 4, in_channels, out_channels], dtype=tf.float32, 25 | initializer=tf.random_normal_initializer(0, 0.02)) 26 | # [batch, in_height, in_width, in_channels], [filter_width, filter_height, in_channels, out_channels] 27 | # => [batch, out_height, out_width, out_channels] 28 | padded_input = tf.pad(batch_input, [[0, 0], [1, 1], [1, 1], [0, 0]], mode='CONSTANT') 29 | conv = tf.nn.conv2d(padded_input, filter, [1, stride, stride, 1], padding='VALID') 30 | return conv 31 | 32 | 33 | def lrelu(x, a): 34 | with tf.name_scope('lrelu'): 35 | # adding these together creates the leak part and linear part 36 | # then cancels them out by subtracting/adding an absolute value term 37 | # leak: a*x/2 - a*abs(x)/2 38 | # linear: x/2 + abs(x)/2 39 | 40 | # this block looks like it has 2 inputs on the graph unless we do this 41 | x = tf.identity(x) 42 | return (0.5 * (1 + a)) * x + (0.5 * (1 - a)) * tf.abs(x) 43 | 44 | 45 | def batchnorm(input): 46 | with tf.variable_scope('batchnorm'): 47 | # this block looks like it has 3 inputs on the graph unless we do this 48 | input = tf.identity(input) 49 | 50 | channels = input.get_shape()[3] 51 | offset = tf.get_variable('offset', [channels], dtype=tf.float32, initializer=tf.zeros_initializer()) 52 | scale = tf.get_variable('scale', [channels], dtype=tf.float32, 53 | initializer=tf.random_normal_initializer(1.0, 0.02)) 54 | mean, variance = tf.nn.moments(input, axes=[0, 1, 2], keep_dims=False) 55 | variance_epsilon = 1e-5 56 | normalized = tf.nn.batch_normalization(input, mean, variance, offset, scale, variance_epsilon=variance_epsilon) 57 | return normalized 58 | 59 | 60 | def deconv(batch_input, out_channels): 61 | with tf.variable_scope('deconv'): 62 | batch, in_height, in_width, in_channels = [int(d) for d in batch_input.get_shape()] 63 | filter = tf.get_variable('filter', [4, 4, out_channels, in_channels], dtype=tf.float32, 64 | initializer=tf.random_normal_initializer(0, 0.02)) 65 | # [batch, in_height, in_width, in_channels], [filter_width, filter_height, out_channels, in_channels] 66 | # => [batch, out_height, out_width, out_channels] 67 | conv = tf.nn.conv2d_transpose(batch_input, filter, [batch, in_height * 2, in_width * 2, out_channels], 68 | [1, 2, 2, 1], padding='SAME') 69 | return conv 70 | 71 | 72 | def process_image(x): 73 | with tf.name_scope('load_images'): 74 | raw_input = tf.image.convert_image_dtype(x, dtype=tf.float32) 75 | 76 | raw_input.set_shape([None, None, 3]) 77 | 78 | # break apart image pair and move to range [-1, 1] 79 | width = tf.shape(raw_input)[1] # [height, width, channels] 80 | a_images = preprocess(raw_input[:, :width // 2, :]) 81 | b_images = preprocess(raw_input[:, width // 2:, :]) 82 | 83 | inputs, targets = [a_images, b_images] 84 | 85 | # synchronize seed for image operations so that we do the same operations to both 86 | # input and output images 87 | def transform(image): 88 | r = image 89 | 90 | # area produces a nice downscaling, but does nearest neighbor for upscaling 91 | # assume we're going to be doing downscaling here 92 | r = tf.image.resize_images(r, [CROP_SIZE, CROP_SIZE], method=tf.image.ResizeMethod.AREA) 93 | 94 | return r 95 | 96 | with tf.name_scope('input_images'): 97 | input_images = tf.expand_dims(transform(inputs), 0) 98 | 99 | with tf.name_scope('target_images'): 100 | target_images = tf.expand_dims(transform(targets), 0) 101 | 102 | return input_images, target_images 103 | 104 | # Tensor('batch:1', shape=(1, 256, 256, 3), dtype=float32) -> 1 batch size 105 | 106 | 107 | def create_generator(generator_inputs, generator_outputs_channels): 108 | layers = [] 109 | 110 | # encoder_1: [batch, 256, 256, in_channels] => [batch, 128, 128, ngf] 111 | with tf.variable_scope('encoder_1'): 112 | output = conv(generator_inputs, ngf, stride=2) 113 | layers.append(output) 114 | 115 | # layer_specs = [ 116 | # ngf * 2, # encoder_2: [batch, 512, 512, ngf] => [batch, 256, 256, ngf * 2] 117 | # # ngf * 2, # encoder_2: [batch, 128, 128, ngf] => [batch, 64, 64, ngf * 2] 118 | # # ngf * 4, # encoder_3: [batch, 64, 64, ngf * 2] => [batch, 32, 32, ngf * 4] 119 | # ngf * 4, # encoder_3: [batch, 256, 256, ngf * 2] => [batch, 128, 128, ngf * 4] 120 | # ngf * 8, # encoder_4: [batch, 128, 128, ngf * 4] => [batch, 64, 64, ngf * 8] 121 | # ngf * 8, # encoder_4: [batch, 64, 64, ngf * 4] => [batch, 32, 32, ngf * 8] 122 | # ngf * 8, # encoder_4: [batch, 32, 32, ngf * 4] => [batch, 16, 16, ngf * 8] 123 | # ngf * 8, # encoder_5: [batch, 16, 16, ngf * 8] => [batch, 8, 8, ngf * 8] 124 | # ngf * 8, # encoder_6: [batch, 8, 8, ngf * 8] => [batch, 4, 4, ngf * 8] 125 | # ngf * 8, # encoder_7: [batch, 4, 4, ngf * 8] => [batch, 2, 2, ngf * 8] 126 | # ngf * 8, # encoder_8: [batch, 2, 2, ngf * 8] => [batch, 1, 1, ngf * 8] 127 | # ] 128 | 129 | layer_specs = [ 130 | ngf * 2, # encoder_2: [batch, 512, 512, ngf] => [batch, 256, 256, ngf * 2] 131 | # ngf * 2, # encoder_2: [batch, 128, 128, ngf] => [batch, 64, 64, ngf * 2] 132 | # ngf * 4, # encoder_3: [batch, 64, 64, ngf * 2] => [batch, 32, 32, ngf * 4] 133 | ngf * 4, # encoder_3: [batch, 256, 256, ngf * 2] => [batch, 128, 128, ngf * 4] 134 | ngf * 8, # encoder_4: [batch, 128, 128, ngf * 4] => [batch, 64, 64, ngf * 8] 135 | ngf * 8, # encoder_4: [batch, 64, 64, ngf * 4] => [batch, 32, 32, ngf * 8] 136 | ngf * 16, # encoder_4: [batch, 32, 32, ngf * 4] => [batch, 16, 16, ngf * 8] 137 | ngf * 16, # encoder_5: [batch, 16, 16, ngf * 8] => [batch, 8, 8, ngf * 8] 138 | ngf * 16, # encoder_6: [batch, 8, 8, ngf * 8] => [batch, 4, 4, ngf * 8] 139 | ngf * 16, # encoder_7: [batch, 4, 4, ngf * 8] => [batch, 2, 2, ngf * 8] 140 | ngf * 32, # encoder_8: [batch, 2, 2, ngf * 8] => [batch, 1, 1, ngf * 8] 141 | ] 142 | for out_channels in layer_specs: 143 | with tf.variable_scope('encoder_%d' % (len(layers) + 1)): 144 | rectified = lrelu(layers[-1], 0.2) 145 | # [batch, in_height, in_width, in_channels] => [batch, in_height/2, in_width/2, out_channels] 146 | convolved = conv(rectified, out_channels, stride=2) 147 | output = batchnorm(convolved) 148 | layers.append(output) 149 | 150 | # layer_specs = [ 151 | # (ngf * 8, 0.5), # decoder_8: [batch, 1, 1, ngf * 8] => [batch, 2, 2, ngf * 8 * 2] 152 | # (ngf * 8, 0.5), # decoder_7: [batch, 2, 2, ngf * 8 * 2] => [batch, 4, 4, ngf * 8 * 2] 153 | # (ngf * 8, 0.5), # decoder_6: [batch, 4, 4, ngf * 8 * 2] => [batch, 8, 8, ngf * 8 * 2] 154 | # (ngf * 8, 0.0), # decoder_5: [batch, 8, 8, ngf * 8 * 2] => [batch, 16, 16, ngf * 8 * 2] 155 | # (ngf * 8, 0.0), # decoder_5: [batch, 16, 16, ngf * 8 * 2] => [batch, 32, 32, ngf * 8 * 2] 156 | # (ngf * 8, 0.0), # decoder_5: [batch, 32, 32, ngf * 8 * 2] => [batch, 64, 64, ngf * 8 * 2] 157 | # # (ngf * 4, 0.0), # decoder_4: [batch, 16, 16, ngf * 8 * 2] => [batch, 32, 32, ngf * 4 * 2] 158 | # (ngf * 4, 0.0), # decoder_4: [batch, 64, 64, ngf * 8 * 2] => [batch, 128, 128, ngf * 4 * 2] 159 | # # (ngf * 2, 0.0), # decoder_3: [batch, 32, 32, ngf * 4 * 2] => [batch, 64, 64, ngf * 2 * 2] 160 | # (ngf * 2, 0.0), # decoder_3: [batch, 128, 128, ngf * 4 * 2] => [batch, 256, 256, ngf * 2 * 2] 161 | # (ngf, 0.0), # decoder_2: [batch, 256, 256, ngf * 2 * 2] => [batch, 512, 512, ngf * 2] 162 | # ] 163 | layer_specs = [ 164 | (ngf * 16, 0.5), # decoder_8: [batch, 1, 1, ngf * 8] => [batch, 2, 2, ngf * 8 * 2] 165 | (ngf * 16, 0.5), # decoder_7: [batch, 2, 2, ngf * 8 * 2] => [batch, 4, 4, ngf * 8 * 2] 166 | (ngf * 16, 0.5), # decoder_6: [batch, 4, 4, ngf * 8 * 2] => [batch, 8, 8, ngf * 8 * 2] 167 | (ngf * 16, 0.5), # decoder_5: [batch, 8, 8, ngf * 8 * 2] => [batch, 16, 16, ngf * 8 * 2] 168 | (ngf * 8, 0.5), # decoder_5: [batch, 16, 16, ngf * 8 * 2] => [batch, 32, 32, ngf * 8 * 2] 169 | (ngf * 8, 0.5), # decoder_5: [batch, 32, 32, ngf * 8 * 2] => [batch, 64, 64, ngf * 8 * 2] 170 | # (ngf * 4, 0.0), # decoder_4: [batch, 16, 16, ngf * 8 * 2] => [batch, 32, 32, ngf * 4 * 2] 171 | (ngf * 4, 0.0), # decoder_4: [batch, 64, 64, ngf * 8 * 2] => [batch, 128, 128, ngf * 4 * 2] 172 | # (ngf * 2, 0.0), # decoder_3: [batch, 32, 32, ngf * 4 * 2] => [batch, 64, 64, ngf * 2 * 2] 173 | (ngf * 2, 0.0), # decoder_3: [batch, 128, 128, ngf * 4 * 2] => [batch, 256, 256, ngf * 2 * 2] 174 | (ngf, 0.0), # decoder_2: [batch, 256, 256, ngf * 2 * 2] => [batch, 512, 512, ngf * 2] 175 | ] 176 | 177 | num_encoder_layers = len(layers) 178 | for decoder_layer, (out_channels, dropout) in enumerate(layer_specs): 179 | skip_layer = num_encoder_layers - decoder_layer - 1 180 | with tf.variable_scope('decoder_%d' % (skip_layer + 1)): 181 | if decoder_layer == 0: 182 | # first decoder layer doesn't have skip connections 183 | # since it is directly connected to the skip_layer 184 | input = layers[-1] 185 | else: 186 | input = tf.concat([layers[-1], layers[skip_layer]], axis=3) 187 | 188 | rectified = tf.nn.relu(input) 189 | # [batch, in_height, in_width, in_channels] => [batch, in_height*2, in_width*2, out_channels] 190 | output = deconv(rectified, out_channels) 191 | output = batchnorm(output) 192 | 193 | if dropout > 0.0: 194 | output = tf.nn.dropout(output, keep_prob=1 - dropout) 195 | 196 | layers.append(output) 197 | 198 | # decoder_1: [batch, 128, 128, ngf * 2] => [batch, 256, 256, generator_outputs_channels] 199 | with tf.variable_scope('decoder_1'): 200 | input = tf.concat([layers[-1], layers[0]], axis=3) 201 | rectified = tf.nn.relu(input) 202 | output = deconv(rectified, generator_outputs_channels) 203 | output = tf.tanh(output) 204 | layers.append(output) 205 | 206 | return layers[-1] 207 | 208 | 209 | def create_model(inputs, targets): 210 | with tf.variable_scope('generator') as scope: 211 | out_channels = int(targets.get_shape()[-1]) 212 | outputs = create_generator(inputs, out_channels) 213 | 214 | return outputs 215 | 216 | 217 | def convert(image): 218 | return tf.image.convert_image_dtype(image, dtype=tf.uint8, saturate=True, name='output') # output tensor 219 | 220 | 221 | def generate_output(x): 222 | with tf.name_scope('generate_output'): 223 | test_inputs, test_targets = process_image(x) 224 | 225 | # inputs and targets are [batch_size, height, width, channels] 226 | model = create_model(test_inputs, test_targets) 227 | 228 | # deprocess files 229 | outputs = deprocess(model) 230 | 231 | # reverse any processing on images so they can be written to disk or displayed to user 232 | converted_outputs = convert(outputs) 233 | return converted_outputs 234 | 235 | 236 | if __name__ == '__main__': 237 | parser = argparse.ArgumentParser() 238 | parser.add_argument('--model-input', dest='input_folder', type=str, help='Model folder to import.') 239 | parser.add_argument('--model-output', dest='output_folder', type=str, help='Model (reduced) folder to export.') 240 | args = parser.parse_args() 241 | 242 | x = tf.placeholder(tf.uint8, shape=(CROP_SIZE, 2*CROP_SIZE, 3), name='image_tensor') # input tensor 243 | y = generate_output(x) 244 | 245 | with tf.Session() as sess: 246 | # Restore original model 247 | saver = tf.train.Saver() 248 | checkpoint = tf.train.latest_checkpoint(args.input_folder) 249 | saver.restore(sess, checkpoint) 250 | 251 | # Export reduced model used for prediction 252 | saver = tf.train.Saver() 253 | saver.save(sess, '{}/reduced_model'.format(args.output_folder)) 254 | print("Model is exported to {}".format(checkpoint)) 255 | -------------------------------------------------------------------------------- /run_video.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import cv2 3 | import dlib 4 | import numpy as np 5 | import tensorflow as tf 6 | from imutils import video 7 | 8 | CROP_SIZE = 1024 9 | DOWNSAMPLE_RATIO = 1 10 | 11 | 12 | def reshape_for_polyline(array): 13 | """Reshape image so that it works with polyline.""" 14 | return np.array(array, np.int32).reshape((-1, 1, 2)) 15 | 16 | 17 | def resize(image): 18 | """Crop and resize image for pix2pix.""" 19 | height, width, _ = image.shape 20 | if height != width: 21 | # crop to correct ratio 22 | size = min(height, width) 23 | oh = (height - size) // 2 24 | ow = (width - size) // 2 25 | cropped_image = image[oh:(oh + size), ow:(ow + size)] 26 | image_resize = cv2.resize(cropped_image, (CROP_SIZE, CROP_SIZE)) 27 | return image_resize 28 | 29 | 30 | def load_graph(frozen_graph_filename): 31 | """Load a (frozen) Tensorflow model into memory.""" 32 | graph = tf.Graph() 33 | with graph.as_default(): 34 | od_graph_def = tf.GraphDef() 35 | with tf.gfile.GFile(frozen_graph_filename, 'rb') as fid: 36 | serialized_graph = fid.read() 37 | od_graph_def.ParseFromString(serialized_graph) 38 | tf.import_graph_def(od_graph_def, name='') 39 | return graph 40 | 41 | 42 | def main(): 43 | # TensorFlow 44 | graph = load_graph(args.frozen_model_file) 45 | image_tensor = graph.get_tensor_by_name('image_tensor:0') 46 | output_tensor = graph.get_tensor_by_name('generate_output/output:0') 47 | sess = tf.Session(graph=graph) 48 | 49 | # OpenCV 50 | cap = cv2.VideoCapture(args.video_source) 51 | print(cap) 52 | ret,frame = cap.read() 53 | if frame is None: 54 | cap = cv2.VideoCapture(int(args.video_source)) 55 | 56 | fps = video.FPS().start() 57 | counter =0 58 | while True: 59 | # for i in range(10): 60 | ret, frame = cap.read() 61 | 62 | # frame=frame[150:-200,:,:] 63 | 64 | # resize image and detect face 65 | frame_resize = cv2.resize(frame, None, fx=1 / DOWNSAMPLE_RATIO, fy=1 / DOWNSAMPLE_RATIO) 66 | gray = cv2.cvtColor(frame_resize, cv2.COLOR_BGR2GRAY) 67 | faces = detector(gray, 1) 68 | black_image = np.zeros(frame.shape, np.uint8) 69 | 70 | for face in faces: 71 | detected_landmarks = predictor(gray, face).parts() 72 | landmarks = [[p.x * DOWNSAMPLE_RATIO, p.y * DOWNSAMPLE_RATIO] for p in detected_landmarks] 73 | 74 | jaw = reshape_for_polyline(landmarks[0:17]) 75 | left_eyebrow = reshape_for_polyline(landmarks[22:27]) 76 | right_eyebrow = reshape_for_polyline(landmarks[17:22]) 77 | nose_bridge = reshape_for_polyline(landmarks[27:31]) 78 | lower_nose = reshape_for_polyline(landmarks[30:35]) 79 | left_eye = reshape_for_polyline(landmarks[42:48]) 80 | right_eye = reshape_for_polyline(landmarks[36:42]) 81 | outer_lip = reshape_for_polyline(landmarks[48:60]) 82 | inner_lip = reshape_for_polyline(landmarks[60:68]) 83 | 84 | color = (255, 255, 255) 85 | thickness = 3 86 | 87 | cv2.polylines(black_image, [jaw], False, color, thickness) 88 | cv2.polylines(black_image, [left_eyebrow], False, color, thickness) 89 | cv2.polylines(black_image, [right_eyebrow], False, color, thickness) 90 | cv2.polylines(black_image, [nose_bridge], False, color, thickness) 91 | cv2.polylines(black_image, [lower_nose], True, color, thickness) 92 | cv2.polylines(black_image, [left_eye], True, color, thickness) 93 | cv2.polylines(black_image, [right_eye], True, color, thickness) 94 | cv2.polylines(black_image, [outer_lip], True, color, thickness) 95 | cv2.polylines(black_image, [inner_lip], True, color, thickness) 96 | 97 | # generate prediction 98 | combined_image = np.concatenate([resize(black_image), resize(frame_resize)], axis=1) 99 | image_rgb = cv2.cvtColor(combined_image, cv2.COLOR_BGR2RGB) # OpenCV uses BGR instead of RGB 100 | generated_image = sess.run(output_tensor, feed_dict={image_tensor: image_rgb}) 101 | image_bgr = cv2.cvtColor(np.squeeze(generated_image), cv2.COLOR_RGB2BGR) 102 | image_normal = np.concatenate([resize(frame_resize), image_bgr], axis=1) 103 | image_landmark = np.concatenate([resize(black_image), image_bgr], axis=1) 104 | image_all = np.concatenate([resize(frame_resize), resize(black_image), image_bgr], axis=1) 105 | 106 | # if args.display_landmark == 0: 107 | # cv2.imshow('frame', image_normal) 108 | # else: 109 | # cv2.imshow('frame', image_all) 110 | 111 | cv2.imwrite('/tmp/image%09d.jpg'%counter,image_all) 112 | cv2.imwrite('/tmp/face/gen/image%09d.jpg'%counter,image_bgr) 113 | cv2.imwrite('/tmp/face/face/image%09d.jpg'%counter,resize(black_image)) 114 | cv2.imwrite('/tmp/face/input/image%09d.jpg'%counter,resize(frame_resize)) 115 | if len(faces)>0: 116 | cv2.imwrite('/tmp/face/mix/image%09d.jpg'%counter,image_bgr) 117 | else: 118 | cv2.imwrite('/tmp/face/mix/image%09d.jpg'%counter,resize(frame_resize)) 119 | 120 | counter = counter+1 121 | 122 | fps.update() 123 | if cv2.waitKey(1) & 0xFF == ord('q'): 124 | break 125 | 126 | fps.stop() 127 | print('[INFO] elapsed time (total): {:.2f}'.format(fps.elapsed())) 128 | print('[INFO] approx. FPS: {:.2f}'.format(fps.fps())) 129 | 130 | sess.close() 131 | cap.release() 132 | cv2.destroyAllWindows() 133 | 134 | 135 | if __name__ == '__main__': 136 | parser = argparse.ArgumentParser() 137 | parser.add_argument('-src', '--source', dest='video_source', type=str, 138 | default=0, help='Device index of the camera.') 139 | parser.add_argument('--show', dest='display_landmark', type=int, default=0, choices=[0, 1], 140 | help='0 shows the normal input and 1 the facial landmark.') 141 | parser.add_argument('--landmark-model', dest='face_landmark_shape_file', type=str, help='Face landmark model file.') 142 | parser.add_argument('--tf-model', dest='frozen_model_file', type=str, help='Frozen TensorFlow model file.') 143 | 144 | args = parser.parse_args() 145 | 146 | # Create the face predictor and landmark predictor 147 | detector = dlib.get_frontal_face_detector() 148 | predictor = dlib.shape_predictor(args.face_landmark_shape_file) 149 | 150 | main() 151 | -------------------------------------------------------------------------------- /run_webcam.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import cv2 3 | import dlib 4 | import numpy as np 5 | import tensorflow as tf 6 | from imutils import video 7 | 8 | CROP_SIZE = 1024 9 | DOWNSAMPLE_RATIO = 1 10 | 11 | 12 | def reshape_for_polyline(array): 13 | """Reshape image so that it works with polyline.""" 14 | return np.array(array, np.int32).reshape((-1, 1, 2)) 15 | 16 | 17 | def resize(image): 18 | """Crop and resize image for pix2pix.""" 19 | height, width, _ = image.shape 20 | if height != width: 21 | # crop to correct ratio 22 | size = min(height, width) 23 | oh = (height - size) // 2 24 | ow = (width - size) // 2 25 | cropped_image = image[oh:(oh + size), ow:(ow + size)] 26 | image_resize = cv2.resize(cropped_image, (CROP_SIZE, CROP_SIZE)) 27 | return image_resize 28 | 29 | 30 | def load_graph(frozen_graph_filename): 31 | """Load a (frozen) Tensorflow model into memory.""" 32 | graph = tf.Graph() 33 | with graph.as_default(): 34 | od_graph_def = tf.GraphDef() 35 | with tf.gfile.GFile(frozen_graph_filename, 'rb') as fid: 36 | serialized_graph = fid.read() 37 | od_graph_def.ParseFromString(serialized_graph) 38 | tf.import_graph_def(od_graph_def, name='') 39 | return graph 40 | 41 | 42 | def main(): 43 | # TensorFlow 44 | graph = load_graph(args.frozen_model_file) 45 | image_tensor = graph.get_tensor_by_name('image_tensor:0') 46 | output_tensor = graph.get_tensor_by_name('generate_output/output:0') 47 | sess = tf.Session(graph=graph) 48 | 49 | # OpenCV 50 | cap = cv2.VideoCapture(args.video_source) 51 | print(cap) 52 | ret,frame = cap.read() 53 | if frame is None: 54 | cap = cv2.VideoCapture(int(args.video_source)) 55 | 56 | fps = video.FPS().start() 57 | counter =0 58 | while True: 59 | # for i in range(10): 60 | ret, frame = cap.read() 61 | 62 | # frame=frame[150:-200,:,:] 63 | 64 | # resize image and detect face 65 | frame_resize = cv2.resize(frame, None, fx=1 / DOWNSAMPLE_RATIO, fy=1 / DOWNSAMPLE_RATIO) 66 | gray = cv2.cvtColor(frame_resize, cv2.COLOR_BGR2GRAY) 67 | faces = detector(gray, 1) 68 | black_image = np.zeros(frame.shape, np.uint8) 69 | 70 | for face in faces: 71 | detected_landmarks = predictor(gray, face).parts() 72 | landmarks = [[p.x * DOWNSAMPLE_RATIO, p.y * DOWNSAMPLE_RATIO] for p in detected_landmarks] 73 | 74 | jaw = reshape_for_polyline(landmarks[0:17]) 75 | left_eyebrow = reshape_for_polyline(landmarks[22:27]) 76 | right_eyebrow = reshape_for_polyline(landmarks[17:22]) 77 | nose_bridge = reshape_for_polyline(landmarks[27:31]) 78 | lower_nose = reshape_for_polyline(landmarks[30:35]) 79 | left_eye = reshape_for_polyline(landmarks[42:48]) 80 | right_eye = reshape_for_polyline(landmarks[36:42]) 81 | outer_lip = reshape_for_polyline(landmarks[48:60]) 82 | inner_lip = reshape_for_polyline(landmarks[60:68]) 83 | 84 | color = (255, 255, 255) 85 | thickness = 3 86 | 87 | cv2.polylines(black_image, [jaw], False, color, thickness) 88 | cv2.polylines(black_image, [left_eyebrow], False, color, thickness) 89 | cv2.polylines(black_image, [right_eyebrow], False, color, thickness) 90 | cv2.polylines(black_image, [nose_bridge], False, color, thickness) 91 | cv2.polylines(black_image, [lower_nose], True, color, thickness) 92 | cv2.polylines(black_image, [left_eye], True, color, thickness) 93 | cv2.polylines(black_image, [right_eye], True, color, thickness) 94 | cv2.polylines(black_image, [outer_lip], True, color, thickness) 95 | cv2.polylines(black_image, [inner_lip], True, color, thickness) 96 | 97 | # generate prediction 98 | combined_image = np.concatenate([resize(black_image), resize(frame_resize)], axis=1) 99 | image_rgb = cv2.cvtColor(combined_image, cv2.COLOR_BGR2RGB) # OpenCV uses BGR instead of RGB 100 | generated_image = sess.run(output_tensor, feed_dict={image_tensor: image_rgb}) 101 | image_bgr = cv2.cvtColor(np.squeeze(generated_image), cv2.COLOR_RGB2BGR) 102 | image_normal = np.concatenate([resize(frame_resize), image_bgr], axis=1) 103 | image_landmark = np.concatenate([resize(black_image), image_bgr], axis=1) 104 | image_all = np.concatenate([resize(frame_resize), resize(black_image), image_bgr], axis=1) 105 | 106 | if args.display_landmark == 0: 107 | cv2.imshow('frame', image_normal) 108 | else: 109 | cv2.imshow('frame', image_all) 110 | 111 | cv2.imwrite('/tmp/image%09d.jpg'%counter,image_all) 112 | cv2.imwrite('/tmp/face/gen/image%09d.jpg'%counter,image_bgr) 113 | cv2.imwrite('/tmp/face/face/image%09d.jpg'%counter,resize(black_image)) 114 | cv2.imwrite('/tmp/face/input/image%09d.jpg'%counter,resize(frame_resize)) 115 | if len(faces)>0: 116 | cv2.imwrite('/tmp/face/mix/image%09d.jpg'%counter,image_bgr) 117 | else: 118 | cv2.imwrite('/tmp/face/mix/image%09d.jpg'%counter,resize(frame_resize)) 119 | 120 | counter = counter+1 121 | 122 | fps.update() 123 | if cv2.waitKey(1) & 0xFF == ord('q'): 124 | break 125 | 126 | fps.stop() 127 | print('[INFO] elapsed time (total): {:.2f}'.format(fps.elapsed())) 128 | print('[INFO] approx. FPS: {:.2f}'.format(fps.fps())) 129 | 130 | sess.close() 131 | cap.release() 132 | cv2.destroyAllWindows() 133 | 134 | 135 | if __name__ == '__main__': 136 | parser = argparse.ArgumentParser() 137 | parser.add_argument('-src', '--source', dest='video_source', type=int, 138 | default=0, help='Device index of the camera.') 139 | parser.add_argument('--show', dest='display_landmark', type=int, default=0, choices=[0, 1], 140 | help='0 shows the normal input and 1 the facial landmark.') 141 | parser.add_argument('--landmark-model', dest='face_landmark_shape_file', type=str, help='Face landmark model file.') 142 | parser.add_argument('--tf-model', dest='frozen_model_file', type=str, help='Frozen TensorFlow model file.') 143 | 144 | args = parser.parse_args() 145 | 146 | # Create the face predictor and landmark predictor 147 | detector = dlib.get_frontal_face_detector() 148 | predictor = dlib.shape_predictor(args.face_landmark_shape_file) 149 | 150 | main() 151 | -------------------------------------------------------------------------------- /zapytaj-beczke.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karolmajek/face2face-demo/7017ec4060136d96a81f659f4209b62e64f9a354/zapytaj-beczke.gif --------------------------------------------------------------------------------