-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 07f0c03
Showing
73 changed files
with
13,235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
checkpoints/ | ||
*.py[cod] | ||
*.pyc.* | ||
*$py.class | ||
.vscode/ | ||
output/ | ||
examples/*.pth | ||
*/__pycache__/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Exploring Temporal Coherence for More General Video Face Forgery Detection(FTCN) | ||
|
||
Yinglin Zheng, Jianmin Bao, Dong Chen, Ming Zeng, Fang Wen | ||
### [Paper](https://arxiv.org/abs/2108.06693) | ||
|
||
## Abstract | ||
> Although current face manipulation techniques achieve impressive performance regarding quality and controllability, they are struggling to generate temporal coherent face videos. In this work, we explore to take full advantage of the temporal coherence for video face forgery detection. To achieve this, we propose a novel end-to-end framework, which consists of two major stages. The first stage is a fully temporal convolution network (FTCN). The key insight of FTCN is to reduce the spatial convolution kernel size to 1, while maintaining the temporal convolution kernel size unchanged. We surprisingly find this special design can benefit the model for extracting the temporal features as well as improve the generalization capability. The second stage is a Temporal Transformer network, which aims to explore the long-term temporal coherence. The proposed framework is general and flexible, which can be directly trained from scratch without any pre-training models or external datasets. Extensive experiments show that our framework outperforms existing methods and remains effective when applied to detect new sorts of face forgery videos. | ||
|
||
|
||
# Setup | ||
First setup python environment with pytorch 1.4.0 installed, **it's highly recommended to use docker image [pytorch/pytorch:1.4-cuda10.1-cudnn7-devel](https://hub.docker.com/layers/pytorch/pytorch/1.4-cuda10.1-cudnn7-devel/images/sha256-c612782acc39256aac0637d58d297644066c62f6f84f0b88cfdc335bb25d0d22), as the pretrained model and the code might be incompatible with higher version pytorch.** | ||
|
||
then install dependencies for the experiment: | ||
|
||
``` | ||
pip install -r requirements.txt | ||
``` | ||
|
||
# Test | ||
|
||
## Inference Using Pretrained Model on Raw Video | ||
Download pretrained `FTCN+TT` model from [here]() and place it under `./checkpoints` folder | ||
```bash | ||
python test_on_raw_video.py examples/shining.mp4 output | ||
``` | ||
the output will be a video under folder `output` named `shining.avi` | ||
|
||
![](./examples/shining.gif) | ||
|
||
# TODO | ||
|
||
- [x] Release inference code. | ||
- [ ] Release training code. | ||
- [ ] Code cleaning. | ||
|
||
|
||
# Acknowledgments | ||
|
||
This code borrows heavily from [SlowFast](https://github.com/facebookresearch/SlowFast). | ||
|
||
The face detection network comes from [biubug6/Pytorch_Retinaface](https://github.com/biubug6/Pytorch_Retinaface). | ||
|
||
The face alignment network comes from [cunjian/pytorch_face_landmark](https://github.com/cunjian/pytorch_face_landmark). | ||
|
||
|
||
|
||
# Citation | ||
If you use this code for your research, please cite our paper. | ||
``` | ||
@article{zheng2021exploring, | ||
title={Exploring Temporal Coherence for More General Video Face Forgery Detection}, | ||
author={Zheng, Yinglin and Bao, Jianmin and Chen, Dong and Zeng, Ming and Wen, Fang}, | ||
journal={arXiv preprint arXiv:2108.06693}, | ||
year={2021} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#!/usr/bin/python | ||
# Borrow from tensorpack,credits goes to yuxin wu | ||
|
||
# the loaded sequnce is | ||
# default config in this file | ||
# -> provided setting file (you can not add new config after this) | ||
# -> manully overrided config | ||
# -> computed config in finalize config (you can change config after this) | ||
|
||
import os | ||
import pprint | ||
import yaml | ||
|
||
__all__ = ["config", "finalize_configs"] | ||
|
||
|
||
class AttrDict: | ||
_freezed = False | ||
""" Avoid accidental creation of new hierarchies. """ | ||
|
||
def __getattr__(self, name): | ||
if self._freezed: | ||
raise AttributeError(name) | ||
ret = AttrDict() | ||
setattr(self, name, ret) | ||
return ret | ||
|
||
def __setattr__(self, name, value): | ||
if self._freezed and name not in self.__dict__: | ||
raise AttributeError("Cannot create new attribute!") | ||
super().__setattr__(name, value) | ||
|
||
def __str__(self): | ||
return pprint.pformat(self.to_dict(), indent=1) | ||
|
||
__repr__ = __str__ | ||
|
||
def to_dict(self): | ||
"""Convert to a nested dict. """ | ||
return { | ||
k: v.to_dict() if isinstance(v, AttrDict) else v | ||
for k, v in self.__dict__.items() | ||
if not k.startswith("_") | ||
} | ||
|
||
def update_args(self, args): | ||
"""Update from command line args. """ | ||
for cfg in args: | ||
keys, v = cfg.split("=", maxsplit=1) | ||
keylist = keys.split(".") | ||
dic = self | ||
# print(keylist) | ||
if len(keylist) == 1: | ||
assert keylist[0] in dir(dic), "Unknown config key: {}".format( | ||
keylist[0] | ||
) | ||
for i, k in enumerate(keylist[:-1]): | ||
assert k in dir(dic), "Unknown config key: {}".format(k) | ||
dic = getattr(dic, k) | ||
key = keylist[-1] | ||
assert key in dir(dic), "Unknown config key: {}".format(key) | ||
oldv = getattr(dic, key) | ||
if not isinstance(oldv, str): | ||
v = eval(v) | ||
setattr(dic, key, v) | ||
|
||
def update_with_yaml(self, rel_path): | ||
base_path = os.path.dirname(os.path.abspath(__file__)) | ||
setting_path = os.path.normpath(os.path.join(base_path, "setting", rel_path)) | ||
setting_name = os.path.basename(setting_path).split(".")[0] | ||
|
||
with open(setting_path, "r") as f: | ||
overrided_setting = yaml.load(f,Loader=yaml.FullLoader) | ||
# if 'setting_name' not in overrided_setting: | ||
# raise RuntimeError('you must provide a setting name for non root_setting: {}'.format(rel_path)) | ||
self.update_with_dict(overrided_setting) | ||
setattr(self, "setting_name", setting_name) | ||
|
||
def init_with_yaml(self): | ||
base_path = os.path.dirname(os.path.abspath(__file__)) | ||
setting_path = os.path.normpath(os.path.join(base_path, "root_setting.yaml")) | ||
with open(setting_path, "r") as f: | ||
overrided_setting = yaml.load(f,Loader=yaml.FullLoader) | ||
self.update_with_dict(overrided_setting) | ||
|
||
def update_with_text(self,text): | ||
overrided_setting = yaml.load(text, Loader=yaml.FullLoader) | ||
self.update_with_dict(overrided_setting) | ||
|
||
def update_with_dict(self, dicts): | ||
for k, v in dicts.items(): | ||
if isinstance(v, dict): | ||
getattr(self, k).update_with_dict(v) | ||
else: | ||
setattr(self, k, v) | ||
|
||
def freeze(self): | ||
self._freezed = True | ||
for v in self.__dict__.values(): | ||
if isinstance(v, AttrDict): | ||
v.freeze() | ||
|
||
# avoid silent bugs | ||
def __eq__(self, _): | ||
raise NotImplementedError() | ||
|
||
def __ne__(self, _): | ||
raise NotImplementedError() | ||
|
||
|
||
config = AttrDict() | ||
_C = config # short alias to avoid coding | ||
|
||
|
||
# you can directly write setting here as _C.model_dir='.\checkpoint' or in root_setting.yaml | ||
|
||
|
||
# | ||
|
||
|
||
def finalize_configs(input_cfg=_C, freeze=True, verbose=True): | ||
|
||
# _C.base_path = os.path.dirname(os.path.abspath(__file__)) | ||
input_cfg.base_path = os.path.dirname(__file__) | ||
|
||
# for running in remote server | ||
# for k, v in input_cfg.path.__dict__.items(): | ||
# v = os.path.normpath(os.path.join(input_cfg.base_path, v)) | ||
# setattr(input_cfg.path, k, v) | ||
if freeze: | ||
input_cfg.freeze() | ||
# if verbose: | ||
# logger.info("Config: ------------------------------------------\n" + str(_C)) | ||
|
||
|
||
if __name__ == "__main__": | ||
print("?") | ||
print(os.path.dirname(__file__)) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/usr/bin/python | ||
# -*- coding: UTF-8 -*- | ||
|
||
|
Oops, something went wrong.