diff --git a/README.md b/README.md index 83e7c23..698905a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # faceit_live3 -This is an update to http://github.com/faceit_live using [first order model](https://github.com/AliaksandrSiarohin/first-order-model) by Aliaksandr Siarohin to generate the images. This model only requires a single image, so no training is needed and things are much easier. +This is an update to http://github.com/faceit_live using [first order model](https://github.com/AliaksandrSiarohin/first-order-model) by Aliaksandr Siarohin to generate the images. This model only requires a single image, so no training is needed and things are much easier. I've included instructions on how to set it up under **Windows 10** and **Linux**. # Demo @@ -11,17 +11,76 @@ Here is a video of the program running. It uses a single page I took from partne # Setup ## Requirements -This has been tested on **Ubuntu 18.04 with a Titan RTX/X GPU**. +This has only been tested on **Ubuntu 18.04 and Win 10 with a Titan RTX/X GPU**. You will need the following to make it work: - Linux host OS + Linux host OS / Win 10 NVidia fast GPU (GTX 1080, GTX 1080i, Titan, etc ...) Fast Desktop CPU (Quad Core or more) - NVidia CUDA 10 and cuDNN 7 libraries installed + Webcam + Anaconda Environment (https://www.anaconda.com/distribution/) -## Setup Host System +# Clone this repository +Don't forget to use the *--recurse-submodules* parameter to checkout all dependencies. In Windows you might need to install a [Git Client](https://git-scm.com/download/win). + + $ git clone --recurse-submodules https://github.com/alew3/faceit_live3.git + +## Download 'vox-adv-cpk.pth.tar' to /model folder + +You can find it at: [google-drive](https://drive.google.com/open?id=1PyQJmkdCsAkOYwUyaj_l-l0as-iLDgeH) or [yandex-disk](https://yadi.sk/d/lEw8uRm140L_eQ). + +# Install Nvidia Deep Learning Drivers / Libs +Install the latest Nvidia video driver then the Deep Learning infrastructure: + +* NVidia [CUDA 10.1 driver](https://developer.nvidia.com/cuda-downloads) - 2.6GB Download! +* [cuDNN](https://developer.nvidia.com/cudnn) version for CUDA 10.1 - you will need to register to download it. + +Other versions might work, but I haven't tested them. + + +## Usage + +Put in the `/media` directory the images in jpg/png you want to play with. Squared images that have just a face filling most of the space will work better. + + +# Setup Windows Version + +## Create an Anaconda environment and install requirements +``` +$ conda create -n faceit_live3 python=3.8 +$ conda activate faceit_live3 +$ conda install pytorch=1.4 torchvision=0.5 cudatoolkit=10.1 -c pytorch +$ pip install -r requirements.txt +``` + +## Setup Virtual Camera for streaming + +Download [OBS Studio for Win](https://obsproject.com/download) and install it, afterwards install the [OBS Virtual CAM plugin](https://github.com/CatxFish/obs-virtual-cam/releases) by following instructions on the page. + +After you install Virtual CAM. +- Create a Scene +- Add a Window Capture item to Sources and select the "Stream Window" +- Add a Filter to the Window Capture by right clicking and selecting Filters, then "+" and choose Virtual CAM +- Start the Virtual CAM from the Tools Menu + +[![Select the OBSCAM](https://raw.githubusercontent.com/alew3/faceit_live3/master/docsobs.png)] + +Open Firefox and joing Google Hangout to test it, don't forget to choose the OBS CAM from the camera options under settings. + +[![Select the OBSCAM](https://raw.githubusercontent.com/alew3/faceit_live3/master/docs/obscam.png)] + +# Setup Linux Version + +## Create an Anaconda environment and install requirements +``` +$ conda create -n faceit_live3 python=3.8 +$ source activate faceit_live3 +$ conda install pytorch=1.4 torchvision=0.5 cudatoolkit=10.1 -c pytorch +$ pip install -r requirements.txt +``` + To use the fake webcam feature to enter conferences with our stream we need to insert the **v4l2loopback** kernel module in order to create */dev/video1*. Follow the install instructions at (https://github.com/umlaeute/v4l2loopback), then let's setup our fake webcam: ``` @@ -65,61 +124,50 @@ $ v4l2-ctl --list-formats -d /dev/video1 ``` -If you have more than one GPU, you might need to set some environment variables: -``` -# specify which display to use for rendering -$ export DISPLAY=:1 - -# which CUDA DEVICE to use (run nvidia-smi to discover the ID) -$ export CUDA_VISIBLE_DEVICES=0 - -``` - -## Clone this repository -Don't forget to use the *--recurse-submodules* parameter to checkout all dependencies. - - $ git clone --recurse-submodules https://github.com/alew3/faceit_live3.git /local_path/ - -## Create an Anaconda environment and install requirements -``` -$ conda create -n faceit_live3 python=3.8 -$ source activate faceit_live3 -$ conda install pytorch=1.4 torchvision=0.5 cudatoolkit=10.1 -c pytorch -$ pip install -r requirements.txt - -``` - -## Download 'vox-adv-cpk.pth.tar' to /model folder - -You can find it at: [google-drive](https://drive.google.com/open?id=1PyQJmkdCsAkOYwUyaj_l-l0as-iLDgeH) or [yandex-disk](https://yadi.sk/d/lEw8uRm140L_eQ). - - -# Usage - -Put in the `./media/` directory the images in jpg/png you want to play with. - - -# Run the program +# Run the program ``` $ python faceit_live.py ``` ## Parameters + --system # win or linux (default is win) --webcam_id # the videoid of the Webcam e.g. 0 if /dev/video0 (default is 0) - --stream_id # the /dev/video number to stream to (default is 1) + --stream_id # only used in Linux. Set the /dev/video number to stream to (default is 1) + --gpu_id # for multiple GPU setups, select which GPU to use (default is 0) ## Example ``` $ python faceit_live.py --webcam_id 0 --stream_id 1 ``` -## Shortcuts when running +## Key Shortcuts when running ``` N - cycle next image in media folder C - recenter webcam and create a new base image T - option to alter between 'Relative' and 'Absolute' transformations mode +Q - to quit and close all Windows ``` # Tip -For better results, look into the webcam when starting the program or when pressing C, as this will create a base image from your face that is used for the transformation. Move away and closer to the webcam to find the ideal distance for better results. \ No newline at end of file +For better results, look into the webcam when starting the program or when pressing C, as this will create a base image from your face that is used for the transformation. Move away and closer to the webcam to find the ideal distance for better results. + +## Troubleshooting + +### Slow +If it is running slow, check that it is running on the GPU by looking at the TASK MANAGER under Windows and NVidia Control Panel for Linux. + + +### Multiple GPU + +If you have more than one GPU, you might need to set some environment variables: +``` +# specify which display to use for rendering (Linux) +$ export DISPLAY=:1 + +# which CUDA DEVICE to use (run nvidia-smi to discover the ID) +$ export CUDA_VISIBLE_DEVICES=0 (LINUX) +or +$ SET CUDA_VISIBLE_DEVICES=0,1 (WIN) + +``` \ No newline at end of file diff --git a/docs/obs.png b/docs/obs.png new file mode 100644 index 0000000..9b3a42e Binary files /dev/null and b/docs/obs.png differ diff --git a/docs/obscam.png b/docs/obscam.png new file mode 100644 index 0000000..bb1dee7 Binary files /dev/null and b/docs/obscam.png differ diff --git a/faceit_live.py b/faceit_live.py index d26493a..9a9e43d 100644 --- a/faceit_live.py +++ b/faceit_live.py @@ -10,13 +10,13 @@ import PIL.Image as Image import PIL.ImageFilter import io from io import BytesIO -import pyfakewebcam import pyautogui import os import glob from argparse import Namespace import argparse import timeit +import torch warnings.filterwarnings("ignore") ############## setup #### @@ -25,16 +25,24 @@ media_path = './media/' model_path = 'model/' parser = argparse.ArgumentParser() -parser.add_argument('--webcam_id', type = int, default = 2) +parser.add_argument('--webcam_id', type = int, default = 0) parser.add_argument('--stream_id', type = int, default = 1) -args = parser.parse_args() - +parser.add_argument('--gpu_id', type = int, default = 0) +parser.add_argument('--system', type = str, default = "win") +args = parser.parse_args() webcam_id = args.webcam_id +gpu_id = args.gpu_id + webcam_height = 480 webcam_width = 640 screen_width, screen_height = pyautogui.size() +system = args.system +if system=="linux": + import pyfakewebcam + + stream_id = args.stream_id first_order_path = 'first-order-model/' sys.path.insert(0,first_order_path) @@ -47,13 +55,25 @@ from demo import load_checkpoints, make_animation, tqdm # prevent tqdm from outputting to console demo.tqdm = lambda *i, **kwargs: i[0] +print("CUDA is available: ",torch.cuda.is_available()) +if (torch.cuda.is_available()): + torch.cuda.device("cuda:" + str(gpu_id)) + print("Device Name:",torch.cuda.get_device_name(gpu_id)) + print("Device Count:",torch.cuda.device_count()) + print("CUDA: ",torch.version.cuda) + print("cuDNN",torch.backends.cudnn.version()) + print("Device",torch.cuda.current_device()) + + + img_list = [] +print("Scanning /media folder for images to use...") for filename in os.listdir(media_path): if filename.endswith(".jpg") or filename.endswith(".jpeg") or filename.endswith(".png"): img_list.append(os.path.join(media_path, filename)) print(os.path.join(media_path, filename)) -print(img_list, len(img_list)) +#print(img_list, len(img_list)) @@ -66,9 +86,10 @@ def main(): source_image = readnextimage(0) # start streaming - camera = pyfakewebcam.FakeWebcam(f'/dev/video{stream_id}', webcam_width, webcam_height) - camera.print_capabilities() - print(f"Fake webcam created on /dev/video{stream_id}. Use Firefox and join a Google Meeting to test.") + if system=="linux": + camera = pyfakewebcam.FakeWebcam(f'/dev/video{stream_id}', webcam_width, webcam_height) + camera.print_capabilities() + print(f"Fake webcam created on /dev/video{stream_id}. Use Firefox and join a Google Meeting to test.") # capture webcam video_capture = cv2.VideoCapture(webcam_id) @@ -135,7 +156,8 @@ def main(): stream_v = (stream_v*255).astype(np.uint8) # stream to fakewebcam - camera.schedule_frame(stream_v) + if system=="linux": + camera.schedule_frame(stream_v) k = cv2.waitKey(1) @@ -160,8 +182,9 @@ def main(): # transform face with first-order-model def process_image(source_image,base,current,net, generator,kp_detector,relative): - predictions = make_animation(source_image, [base,current], generator, kp_detector, relative=relative, adapt_movement_scale=False) - + predictions = make_animation(source_image, [base,current], generator, kp_detector, relative=relative, adapt_movement_scale=False, cpu=False) + #print("Device",torch.cuda.current_device()) + #print("Device Name:",torch.cuda.get_device_name(gpu_id)) # predictions = [1]# predictions[..., ::-1] # predictions = (np.clip(predictions, 0, 1) * 255).astype(np.uint8) return predictions[1]