diff --git a/.gitignore b/.gitignore index 822f94d..a02b768 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ video_tmp/ result/ nohup.out #./ +/.vscode /pix2pix /pix2pixHD /tmp @@ -183,4 +184,5 @@ nohup.out *.MP4 *.JPEG *.exe -*.npy \ No newline at end of file +*.npy +*.psd \ No newline at end of file diff --git a/README.md b/README.md index 21dd96f..9ebb067 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ -![image](./imgs/hand.gif) -# DeepMosaics -You can use it to automatically remove the mosaics in images and videos, or add mosaics to them.
-This project is based on "semantic segmentation" and "Image-to-Image Translation".
- -* [中文版README](./README_CN.md)
+
+

+ +
-### More Examples +# DeepMosaics +**English | [中文](./README_CN.md)**
+You can use it to automatically remove the mosaics in images and videos, or add mosaics to them.
This project is based on "semantic segmentation" and "Image-to-Image Translation".
Try it at this [website](http://118.89.27.46:5000/)!
+### Examples +![image](./imgs/hand.gif) origin | auto add mosaic | auto clean mosaic :-:|:-:|:-: ![image](./imgs/example/lena.jpg) | ![image](./imgs/example/lena_add.jpg) | ![image](./imgs/example/lena_clean.jpg) @@ -30,18 +32,21 @@ An interesting example:[Ricardo Milos to cat](https://www.bilibili.com/video/BV1 ## Run DeepMosaics You can either run DeepMosaics via a pre-built binary package, or from source.
+### Try it on web +You can simply try to remove the mosaic on the face at this [website](http://118.89.27.46:5000/).
### Pre-built binary package For Windows, we bulid a GUI version for easy testing.
Download this version, and a pre-trained model via [[Google Drive]](https://drive.google.com/open?id=1LTERcN33McoiztYEwBxMuRjjgxh4DEPs) [[百度云,提取码1x0a]](https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ)
-* [[How to use]](./docs/exe_help.md)
+* [[Help document]](./docs/exe_help.md)
+* Video tutorial => [[youtube]](https://www.youtube.com/watch?v=1kEmYawJ_vk) [[bilibili]](https://www.bilibili.com/video/BV1QK4y1a7Av) ![image](./imgs/GUI.png)
Attentions:
- Requires Windows_x86_64, Windows10 is better.
- Different pre-trained models are suitable for different effects.[[Introduction to pre-trained models]](./docs/pre-trained_models_introduction.md)
- - Run time depends on computers performance(The current version does not support gpu, if you need to use gpu please run source).
+ - Run time depends on computers performance (GPU version has better performance but requires CUDA to be installed).
- If output video cannot be played, you can try with [potplayer](https://daumpotplayer.com/download/).
- GUI version updates slower than source.
@@ -67,11 +72,11 @@ You can download pre_trained models and put them into './pretrained_models'.
#### Simple Example * Add Mosaic (output media will save in './result')
```bash -python deepmosaic.py --media_path ./imgs/ruoruo.jpg --model_path ./pretrained_models/mosaic/add_face.pth --use_gpu 0 +python deepmosaic.py --media_path ./imgs/ruoruo.jpg --model_path ./pretrained_models/mosaic/add_face.pth --gpu_id 0 ``` * Clean Mosaic (output media will save in './result')
```bash -python deepmosaic.py --media_path ./result/ruoruo_add.jpg --model_path ./pretrained_models/mosaic/clean_face_HD.pth --use_gpu 0 +python deepmosaic.py --media_path ./result/ruoruo_add.jpg --model_path ./pretrained_models/mosaic/clean_face_HD.pth --gpu_id 0 ``` #### More Parameters If you want to test other images or videos, please refer to this file.
@@ -81,5 +86,4 @@ If you want to test other images or videos, please refer to this file.
If you want to train with your own dataset, please refer to [training_with_your_own_dataset.md](./docs/training_with_your_own_dataset.md) ## Acknowledgements -This code borrows heavily from [[pytorch-CycleGAN-and-pix2pix]](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) [[Pytorch-UNet]](https://github.com/milesial/Pytorch-UNet) [[pix2pixHD]](https://github.com/NVIDIA/pix2pixHD) [[BiSeNet]](https://github.com/ooooverflow/BiSeNet). - +This code borrows heavily from [[pytorch-CycleGAN-and-pix2pix]](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) [[Pytorch-UNet]](https://github.com/milesial/Pytorch-UNet) [[pix2pixHD]](https://github.com/NVIDIA/pix2pixHD) [[BiSeNet]](https://github.com/ooooverflow/BiSeNet) [[DFDNet]](https://github.com/csxmli2016/DFDNet) [[GFRNet_pytorch_new]](https://github.com/sonack/GFRNet_pytorch_new). diff --git a/README_CN.md b/README_CN.md index de37161..090371d 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,9 +1,15 @@ -![image](./imgs/hand.gif) -# DeepMosaics -这是一个通过深度学习自动的为图片/视频添加马赛克,或消除马赛克的项目.
它基于“语义分割”以及“图像翻译”.
+
+

+ +
+ +# DeepMosaics +**[English](./README.md) | 中文**
-### 更多例子 +这是一个通过深度学习自动的为图片/视频添加马赛克,或消除马赛克的项目.
它基于“语义分割”以及“图像翻译”.
现在可以在这个[网站](http://118.89.27.46:5000/)尝试使用该项目清除马赛克!
+### 例子 +![image](./imgs/hand.gif) 原始 | 自动打码 | 自动去码 :-:|:-:|:-: ![image](./imgs/example/lena.jpg) | ![image](./imgs/example/lena_add.jpg) | ![image](./imgs/example/lena_clean.jpg) @@ -26,19 +32,20 @@ ## 如何运行 可以通过我们预编译好的二进制包或源代码运行.
- +### 在网页中运行 +打开[这个网站](http://118.89.27.46:5000/)上传照片,将获得去除马赛克后的结果,受限与当地法律,目前只支持人脸.
### 预编译的程序包 对于Windows用户,我们提供了包含GUI界面的免安装软件包.
可以通过下面两种方式进行下载: [[Google Drive]](https://drive.google.com/open?id=1LTERcN33McoiztYEwBxMuRjjgxh4DEPs) [[百度云,提取码1x0a]](https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ)
-* [[使用教程]](./docs/exe_help_CN.md)
- +* [[帮助文档]](./docs/exe_help_CN.md)
+* [[视频教程]](https://www.bilibili.com/video/BV1QK4y1a7Av)
![image](./imgs/GUI.png)
注意事项:
- 程序的运行要求在64位Windows操作系统,我仅在Windows10运行过,其他版本暂未经过测试
- 请根据需求选择合适的预训练模型进行测试,不同的预期训练模型具有不同的效果.[[预训练模型介绍]](./docs/pre-trained_models_introduction_CN.md)
- - 运行时间取决于电脑性能,对于视频文件,我们建议使用源码并在GPU上运行.
+ - 运行时间取决于电脑性能,对于视频文件,我们建议在GPU上运行.
- 如果输出的视频无法播放,这边建议您尝试[potplayer](https://daumpotplayer.com/download/).
- 相比于源码,该版本的更新将会延后. @@ -62,13 +69,13 @@ cd DeepMosaics [[预训练模型介绍]](./docs/pre-trained_models_introduction_CN.md)
#### 简单的例子 -* 为视频添加马赛克,例子中认为脸是需要打码的区域 ,可以通过切换预训练模型切换自动打码区域(输出结果将储存到 './result')
+* 为视频或照片添加马赛克,例子中认为脸是需要打码的区域 ,可以通过切换预训练模型切换自动打码区域(输出结果将储存到 './result')
```bash -python deepmosaic.py --media_path ./imgs/ruoruo.jpg --model_path ./pretrained_models/mosaic/add_face.pth --use_gpu 0 +python deepmosaic.py --media_path ./imgs/ruoruo.jpg --model_path ./pretrained_models/mosaic/add_face.pth --gpu_id 0 ``` -* 将视频中的马赛克移除,对于不同的打码物体需要使用对应的预训练模型进行马赛克消除(输出结果将储存到 './result')
+* 将视频或照片中的马赛克移除,对于不同的打码物体需要使用对应的预训练模型进行马赛克消除(输出结果将储存到 './result')
```bash -python deepmosaic.py --media_path ./result/ruoruo_add.jpg --model_path ./pretrained_models/mosaic/clean_face_HD.pth --use_gpu 0 +python deepmosaic.py --media_path ./result/ruoruo_add.jpg --model_path ./pretrained_models/mosaic/clean_face_HD.pth --gpu_id 0 ``` #### 更多的参数 如果想要测试其他的图片或视频,请参照以下文件输入参数.
@@ -78,5 +85,5 @@ python deepmosaic.py --media_path ./result/ruoruo_add.jpg --model_path ./pretrai 如果需要使用自己的数据训练模型,请参照 [training_with_your_own_dataset.md](./docs/training_with_your_own_dataset.md) ## 鸣谢 -代码大量的参考了以下项目:[[pytorch-CycleGAN-and-pix2pix]](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) [[Pytorch-UNet]](https://github.com/milesial/Pytorch-UNet) [[pix2pixHD]](https://github.com/NVIDIA/pix2pixHD) [[BiSeNet]](https://github.com/ooooverflow/BiSeNet). +代码大量的参考了以下项目:[[pytorch-CycleGAN-and-pix2pix]](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) [[Pytorch-UNet]](https://github.com/milesial/Pytorch-UNet) [[pix2pixHD]](https://github.com/NVIDIA/pix2pixHD) [[BiSeNet]](https://github.com/ooooverflow/BiSeNet) [[DFDNet]](https://github.com/csxmli2016/DFDNet) [[GFRNet_pytorch_new]](https://github.com/sonack/GFRNet_pytorch_new). diff --git a/cores/core.py b/cores/core.py index db99217..f9cc64c 100644 --- a/cores/core.py +++ b/cores/core.py @@ -1,5 +1,6 @@ import os import time +import torch import numpy as np import cv2 @@ -30,6 +31,7 @@ def video_init(opt,path): continue_flag = True if not continue_flag: + print('Step:1/4 -- Convert video to images') util.file_init(opt) ffmpeg.video2voice(path,opt.temp_dir+'/voice_tmp.mp3',opt.start_time,opt.last_time) ffmpeg.video2image(path,opt.temp_dir+'/video2image/output_%06d.'+opt.tempimage_type,fps,opt.start_time,opt.last_time) @@ -59,7 +61,7 @@ def addmosaic_video(opt,netS): if not opt.no_preview: cv2.namedWindow('preview', cv2.WINDOW_NORMAL) - print('Find ROI location:') + print('Step:2/4 -- Find ROI location') for i,imagepath in enumerate(imagepaths,1): img = impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepath)) mask,x,y,size,area = runmodel.get_ROI_position(img,netS,opt) @@ -77,7 +79,7 @@ def addmosaic_video(opt,netS): mask_index = filt.position_medfilt(np.array(positions), 7) # add mosaic - print('Add Mosaic:') + print('Step:3/4 -- Add Mosaic:') t1 = time.time() for i,imagepath in enumerate(imagepaths,1): mask = impro.imread(os.path.join(opt.temp_dir+'/ROI_mask',imagepaths[mask_index[i-1]]),'gray') @@ -100,6 +102,7 @@ def addmosaic_video(opt,netS): print() if not opt.no_preview: cv2.destroyAllWindows() + print('Step:4/4 -- Convert images to video') ffmpeg.image2video( fps, opt.temp_dir+'/addmosaic_image/output_%06d.'+opt.tempimage_type, opt.temp_dir+'/voice_tmp.mp3', @@ -119,7 +122,7 @@ def styletransfer_video(opt,netG): path = opt.media_path positions = [] fps,imagepaths = video_init(opt,path)[:2] - print('Transfer:') + print('Step:2/4 -- Transfer') t1 = time.time() if not opt.no_preview: cv2.namedWindow('preview', cv2.WINDOW_NORMAL) @@ -142,6 +145,7 @@ def styletransfer_video(opt,netG): if not opt.no_preview: cv2.destroyAllWindows() suffix = os.path.basename(opt.model_path).replace('.pth','').replace('style_','') + print('Step:4/4 -- Convert images to video') ffmpeg.image2video( fps, opt.temp_dir+'/style_transfer/output_%06d.'+opt.tempimage_type, opt.temp_dir+'/voice_tmp.mp3', @@ -156,8 +160,7 @@ def get_mosaic_positions(opt,netM,imagepaths,savemask=True): t1 = time.time() if not opt.no_preview: cv2.namedWindow('mosaic mask', cv2.WINDOW_NORMAL) - - print('Find mosaic location:') + print('Step:2/4 -- Find mosaic location') for i,imagepath in enumerate(imagepaths,1): img_origin = impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepath)) x,y,size,mask = runmodel.get_mosaic_position(img_origin,netM,opt) @@ -186,7 +189,7 @@ def cleanmosaic_img(opt,netG,netM): print('Clean Mosaic:',path) img_origin = impro.imread(path) x,y,size,mask = runmodel.get_mosaic_position(img_origin,netM,opt) - cv2.imwrite('./mask/'+os.path.basename(path), mask) + #cv2.imwrite('./mask/'+os.path.basename(path), mask) img_result = img_origin.copy() if size > 100 : img_mosaic = img_origin[y-size:y+size,x-size:x+size] @@ -199,6 +202,18 @@ def cleanmosaic_img(opt,netG,netM): print('Do not find mosaic') impro.imwrite(os.path.join(opt.result_dir,os.path.splitext(os.path.basename(path))[0]+'_clean.jpg'),img_result) +def cleanmosaic_img_server(opt,img_origin,netG,netM): + x,y,size,mask = runmodel.get_mosaic_position(img_origin,netM,opt) + img_result = img_origin.copy() + if size > 100 : + img_mosaic = img_origin[y-size:y+size,x-size:x+size] + if opt.traditional: + img_fake = runmodel.traditional_cleaner(img_mosaic,opt) + else: + img_fake = runmodel.run_pix2pix(img_mosaic,netG,opt) + img_result = impro.replace_mosaic(img_origin,img_fake,mask,x,y,size,opt.no_feather) + return img_result + def cleanmosaic_video_byframe(opt,netG,netM): path = opt.media_path fps,imagepaths = video_init(opt,path)[:2] @@ -208,7 +223,7 @@ def cleanmosaic_video_byframe(opt,netG,netM): cv2.namedWindow('clean', cv2.WINDOW_NORMAL) # clean mosaic - print('Clean Mosaic:') + print('Step:3/4 -- Clean Mosaic:') length = len(imagepaths) for i,imagepath in enumerate(imagepaths,0): x,y,size = positions[i][0],positions[i][1],positions[i][2] @@ -237,6 +252,7 @@ def cleanmosaic_video_byframe(opt,netG,netM): print() if not opt.no_preview: cv2.destroyAllWindows() + print('Step:4/4 -- Convert images to video') ffmpeg.image2video( fps, opt.temp_dir+'/replace_mosaic/output_%06d.'+opt.tempimage_type, opt.temp_dir+'/voice_tmp.mp3', @@ -244,11 +260,15 @@ def cleanmosaic_video_byframe(opt,netG,netM): def cleanmosaic_video_fusion(opt,netG,netM): path = opt.media_path - N = 25 - if 'HD' in os.path.basename(opt.model_path): - INPUT_SIZE = 256 - else: - INPUT_SIZE = 128 + N,T,S = 2,5,3 + LEFT_FRAME = (N*S) + POOL_NUM = LEFT_FRAME*2+1 + INPUT_SIZE = 256 + FRAME_POS = np.linspace(0, (T-1)*S,T,dtype=np.int64) + img_pool = [] + previous_frame = None + init_flag = True + fps,imagepaths,height,width = video_init(opt,path) positions = get_mosaic_positions(opt,netM,imagepaths,savemask=True) t1 = time.time() @@ -256,39 +276,45 @@ def cleanmosaic_video_fusion(opt,netG,netM): cv2.namedWindow('clean', cv2.WINDOW_NORMAL) # clean mosaic - print('Clean Mosaic:') + print('Step:3/4 -- Clean Mosaic:') length = len(imagepaths) - - img_pool = [] - mosaic_input = np.zeros((INPUT_SIZE,INPUT_SIZE,3*N+1), dtype='uint8') - + for i,imagepath in enumerate(imagepaths,0): x,y,size = positions[i][0],positions[i][1],positions[i][2] - + input_stream = [] # image read stream - mask = cv2.imread(os.path.join(opt.temp_dir+'/mosaic_mask',imagepath),0) - if i==0 : - for j in range(0,N): - img_pool.append(impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepaths[np.clip(i+j-12,0,len(imagepaths)-1)]))) - else: + if i==0 :# init + for j in range(POOL_NUM): + img_pool.append(impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepaths[np.clip(i+j-LEFT_FRAME,0,len(imagepaths)-1)]))) + else: # load next frame img_pool.pop(0) - img_pool.append(impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepaths[np.clip(i+12,0,len(imagepaths)-1)]))) - img_origin = img_pool[12] + img_pool.append(impro.imread(os.path.join(opt.temp_dir+'/video2image',imagepaths[np.clip(i+LEFT_FRAME,0,len(imagepaths)-1)]))) + img_origin = img_pool[LEFT_FRAME] img_result = img_origin.copy() - if size>100: + if size>50: try:#Avoid unknown errors - #reshape to network input shape - for k in range(N): - mosaic_input[:,:,k*3:(k+1)*3] = impro.resize(img_pool[k][y-size:y+size,x-size:x+size], INPUT_SIZE) - mask_input = impro.resize(mask,np.min(img_origin.shape[:2]))[y-size:y+size,x-size:x+size] - mosaic_input[:,:,-1] = impro.resize(mask_input, INPUT_SIZE) - mosaic_input_tensor = data.im2tensor(mosaic_input,bgr2rgb=False,use_gpu=opt.use_gpu,use_transform = False,is0_1 = False) - unmosaic_pred = netG(mosaic_input_tensor) - img_fake = data.tensor2im(unmosaic_pred,rgb2bgr = False ,is0_1 = False) + for pos in FRAME_POS: + input_stream.append(impro.resize(img_pool[pos][y-size:y+size,x-size:x+size], INPUT_SIZE)[:,:,::-1]) + if init_flag: + init_flag = False + previous_frame = input_stream[N] + previous_frame = data.im2tensor(previous_frame,bgr2rgb=True,gpu_id=opt.gpu_id) + + input_stream = np.array(input_stream).reshape(1,T,INPUT_SIZE,INPUT_SIZE,3).transpose((0,4,1,2,3)) + input_stream = data.to_tensor(data.normalize(input_stream),gpu_id=opt.gpu_id) + with torch.no_grad(): + unmosaic_pred = netG(input_stream,previous_frame) + img_fake = data.tensor2im(unmosaic_pred,rgb2bgr = True) + previous_frame = unmosaic_pred + # previous_frame = data.tensor2im(unmosaic_pred,rgb2bgr = True) + mask = cv2.imread(os.path.join(opt.temp_dir+'/mosaic_mask',imagepath),0) img_result = impro.replace_mosaic(img_origin,img_fake,mask,x,y,size,opt.no_feather) except Exception as e: - print('Warning:',e) + init_flag = True + print('Error:',e) + else: + init_flag = True cv2.imwrite(os.path.join(opt.temp_dir+'/replace_mosaic',imagepath),img_result) os.remove(os.path.join(opt.temp_dir+'/video2image',imagepath)) @@ -301,6 +327,7 @@ def cleanmosaic_video_fusion(opt,netG,netM): print() if not opt.no_preview: cv2.destroyAllWindows() + print('Step:4/4 -- Convert images to video') ffmpeg.image2video( fps, opt.temp_dir+'/replace_mosaic/output_%06d.'+opt.tempimage_type, opt.temp_dir+'/voice_tmp.mp3', diff --git a/cores/options.py b/cores/options.py index 7e8c165..bba8bae 100644 --- a/cores/options.py +++ b/cores/options.py @@ -11,7 +11,8 @@ def __init__(self): def initialize(self): #base - self.parser.add_argument('--use_gpu', type=int,default=0, help='if -1, use cpu') + self.parser.add_argument('--debug', action='store_true', help='if specified, start debug mode') + self.parser.add_argument('--gpu_id', type=str,default='0', help='if -1, use cpu') self.parser.add_argument('--media_path', type=str, default='./imgs/ruoruo.jpg',help='your videos or images path') self.parser.add_argument('-ss', '--start_time', type=str, default='00:00:00',help='start position of video, default is the beginning of video') self.parser.add_argument('-t', '--last_time', type=str, default='00:00:00',help='duration of the video, default is the entire video') @@ -59,62 +60,71 @@ def getparse(self, test_flag = False): model_name = os.path.basename(self.opt.model_path) self.opt.temp_dir = os.path.join(self.opt.temp_dir, 'DeepMosaics_temp') - os.environ["CUDA_VISIBLE_DEVICES"] = str(self.opt.use_gpu) - import torch - if torch.cuda.is_available() and self.opt.use_gpu > -1: - pass - else: - self.opt.use_gpu = -1 + if self.opt.gpu_id != '-1': + os.environ["CUDA_VISIBLE_DEVICES"] = str(self.opt.gpu_id) + import torch + if not torch.cuda.is_available(): + self.opt.gpu_id = '-1' + # else: + # self.opt.gpu_id = '-1' if test_flag: if not os.path.exists(self.opt.media_path): - print('Error: Bad media path!') + print('Error: Media does not exist!') input('Please press any key to exit.\n') sys.exit(0) - - if self.opt.mode == 'auto': - if 'clean' in model_name or self.opt.traditional: - self.opt.mode = 'clean' - elif 'add' in model_name: - self.opt.mode = 'add' - elif 'style' in model_name or 'edges' in model_name: - self.opt.mode = 'style' - else: - print('Please input running model!') - input('Please press any key to exit.\n') - sys.exit(0) - - if self.opt.output_size == 0 and self.opt.mode == 'style': - self.opt.output_size = 512 - - if 'edges' in model_name or 'edges' in self.opt.preprocess: - self.opt.edges = True - - if self.opt.netG == 'auto' and self.opt.mode =='clean': - if 'unet_128' in model_name: - self.opt.netG = 'unet_128' - elif 'resnet_9blocks' in model_name: - self.opt.netG = 'resnet_9blocks' - elif 'HD' in model_name and 'video' not in model_name: - self.opt.netG = 'HD' - elif 'video' in model_name: - self.opt.netG = 'video' - else: - print('Type of Generator error!') + if not os.path.exists(self.opt.model_path): + print('Error: Model does not exist!') input('Please press any key to exit.\n') sys.exit(0) - if self.opt.ex_mult == 'auto': - if 'face' in model_name: - self.opt.ex_mult = 1.1 + if self.opt.mode == 'auto': + if 'clean' in model_name or self.opt.traditional: + self.opt.mode = 'clean' + elif 'add' in model_name: + self.opt.mode = 'add' + elif 'style' in model_name or 'edges' in model_name: + self.opt.mode = 'style' + else: + print('Please check model_path!') + input('Please press any key to exit.\n') + sys.exit(0) + + if self.opt.output_size == 0 and self.opt.mode == 'style': + self.opt.output_size = 512 + + if 'edges' in model_name or 'edges' in self.opt.preprocess: + self.opt.edges = True + + if self.opt.netG == 'auto' and self.opt.mode =='clean': + if 'unet_128' in model_name: + self.opt.netG = 'unet_128' + elif 'resnet_9blocks' in model_name: + self.opt.netG = 'resnet_9blocks' + elif 'HD' in model_name and 'video' not in model_name: + self.opt.netG = 'HD' + elif 'video' in model_name: + self.opt.netG = 'video' + else: + print('Type of Generator error!') + input('Please press any key to exit.\n') + sys.exit(0) + + if self.opt.ex_mult == 'auto': + if 'face' in model_name: + self.opt.ex_mult = 1.1 + else: + self.opt.ex_mult = 1.5 else: - self.opt.ex_mult = 1.5 - else: - self.opt.ex_mult = float(self.opt.ex_mult) - - if self.opt.mosaic_position_model_path == 'auto': - _path = os.path.join(os.path.split(self.opt.model_path)[0],'mosaic_position.pth') - self.opt.mosaic_position_model_path = _path - # print(self.opt.mosaic_position_model_path) + self.opt.ex_mult = float(self.opt.ex_mult) + + if self.opt.mosaic_position_model_path == 'auto' and self.opt.mode == 'clean': + _path = os.path.join(os.path.split(self.opt.model_path)[0],'mosaic_position.pth') + if os.path.isfile(_path): + self.opt.mosaic_position_model_path = _path + else: + input('Please check mosaic_position_model_path!') + input('Please press any key to exit.\n') + sys.exit(0) return self.opt \ No newline at end of file diff --git a/deepmosaic.py b/deepmosaic.py index cfc8717..1d5c4e6 100644 --- a/deepmosaic.py +++ b/deepmosaic.py @@ -68,15 +68,18 @@ def main(): print('This type of file is not supported') util.clean_tempfiles(opt, tmp_init = False) - + if __name__ == '__main__': + if opt.debug: + main() + sys.exit(0) try: main() print('Finished!') except Exception as ex: print('--------------------ERROR--------------------') print('--------------Environment--------------') - print('DeepMosaics: 0.4.0') + print('DeepMosaics: 0.5.0') print('Python:',sys.version) import torch print('Pytorch:',torch.__version__) diff --git a/docs/exe_help.md b/docs/exe_help.md index 98ee6a2..d5b96aa 100644 --- a/docs/exe_help.md +++ b/docs/exe_help.md @@ -1,7 +1,9 @@ ## DeepMosaics.exe Instructions -[[中文版]](./exe_help_CN.md) +**[[中文版]](./exe_help_CN.md)** This is a GUI version compiled in Windows.
Download this version and pre-trained model via [[Google Drive]](https://drive.google.com/open?id=1LTERcN33McoiztYEwBxMuRjjgxh4DEPs) [[百度云,提取码1x0a]](https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ)
+Video tutorial => [[youtube]](https://www.youtube.com/watch?v=1kEmYawJ_vk) [[bilibili]](https://www.bilibili.com/video/BV1QK4y1a7Av)
+ Attentions:
- Require Windows_x86_64, Windows10 is better.
@@ -9,11 +11,29 @@ Attentions:
- Run time depends on computer performance.
- If output video cannot be played, you can try with [potplayer](https://daumpotplayer.com/download/).
- GUI version update slower than source.
+ +### How to install +#### CPU version +* 1.Download and install Microsoft Visual C++ + https://aka.ms/vs/16/release/vc_redist.x64.exe +#### GPU version +Only suppport NVidia GPU above gtx1060(Driver:above 460 & CUDA:11.0) +* 1.Download and install Microsoft Visual C++ + https://aka.ms/vs/16/release/vc_redist.x64.exe +* 2.Update your gpu drive to 460(or above) + https://www.nvidia.com/en-us/geforce/drivers/ +* 3.Download and install CUDA 11.0: + https://developer.nvidia.com/cuda-toolkit-archive + +You can also download them on BaiduNetdisk +https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ +Password: 1x0a + ### How to use * step 1: Choose image or video. * step 2: Choose model(Different pre-trained models are suitable for different effects) -* step3: Run program and wait. -* step4: Cheek reult in './result'. +* step 3: Run program and wait. +* step 4: Cheek reult in './result'. ### Introduction to pre-trained models * Mosaic @@ -22,10 +42,10 @@ Attentions:
| :------------------------------: | :---------------------------------------------------------: | | add_face.pth | Add mosaic to all faces in images/videos. | | clean_face_HD.pth | Clean mosaic to all faces in images/video.
(RAM > 8GB). | -| add_youknow.pth | Add mosaic to all (FBI Warning) in images/videos. | -| clean_youknow_resnet_9blocks.pth | Clean mosaic to all (FBI Warning) in images/videos. | -| clean_youknow_video.pth | Clean mosaic to all (FBI Warning) in videos. | -| clean_youknow_video_HD.pth | Clean mosaic to all (FBI Warning) in videos.
(RAM > 8GB) | +| add_youknow.pth | Add mosaic to ... in images/videos. | +| clean_youknow_resnet_9blocks.pth | Clean mosaic to ... in images/videos. | +| clean_youknow_video.pth | Clean mosaic to ... in videos. It is better for processing video mosaics | + * Style Transfer @@ -50,8 +70,8 @@ Attentions:
* 7. More options can be input. * 8. Run program. * 9. Open help file. -* 10. Sponsor our project. -* 11. Version information. +* 10. Sponsor our project. +* 11. Version information. * 12. Open the URL on github. ### Introduction to options @@ -60,7 +80,7 @@ If you need more effects, use '--option your-parameters' to enter what you need | Option | Description | Default | | :----------: | :----------------------------------------: | :-------------------------------------: | -| --use_gpu | if -1, do not use gpu | 0 | +| --gpu_id | if -1, do not use gpu | 0 | | --media_path | your videos or images path | ./imgs/ruoruo.jpg | | --mode | program running mode(auto/clean/add/style) | 'auto' | | --model_path | pretrained model path | ./pretrained_models/mosaic/add_face.pth | diff --git a/docs/exe_help_CN.md b/docs/exe_help_CN.md index a38b6e5..8ec4b06 100644 --- a/docs/exe_help_CN.md +++ b/docs/exe_help_CN.md @@ -1,18 +1,39 @@ ## DeepMosaics.exe 使用说明 下载程序以及预训练模型 [[Google Drive]](https://drive.google.com/open?id=1LTERcN33McoiztYEwBxMuRjjgxh4DEPs) [[百度云,提取码1x0a]](https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ)
+[视频教程](https://www.bilibili.com/video/BV1QK4y1a7Av)
+ 注意事项:
- - 程序的运行要求在64位Windows操作系统,我仅在Windows10运行过,其他版本暂未经过测试
+ + - 程序的运行要求在64位Windows操作系统,我们仅在Windows10运行过,其他版本暂未经过测试
- 请根据需求选择合适的预训练模型进行测试
- - 运行时间取决于电脑性能,对于视频文件,我们建议使用源码以及GPU运行
+ - 运行时间取决于电脑性能,对于视频文件,我们建议使用GPU运行
- 如果输出的视频无法播放,这边建议您尝试[potplayer](https://daumpotplayer.com/download/).
- 相比于源码,该版本的更新将会延后. +### 如何安装 +#### CPU version +* 1.下载安装 Microsoft Visual C++ + https://aka.ms/vs/16/release/vc_redist.x64.exe +#### GPU version +仅支持gtx1060及以上的NVidia显卡(要求460版本以上的驱动以及11.0版本的CUDA, 注意只能是11.0) +* 1.Download and install Microsoft Visual C++ + https://aka.ms/vs/16/release/vc_redist.x64.exe +* 2.Update your gpu drive to 460(or above) + https://www.nvidia.com/en-us/geforce/drivers/ +* 3.Download and install CUDA 11.0: + https://developer.nvidia.com/cuda-toolkit-archive + +当然这些也能在百度云上下载 +https://pan.baidu.com/s/10rN3U3zd5TmfGpO_PEShqQ +提取码: 1x0a + ### 如何使用 + * step 1: 选择需要处理的图片或视频 * step 2: 选择预训练模型(不同的预训练模型有不同的效果) -* step3: 运行程序并等待 -* step4: 查看结果(储存在result文件夹下) +* step 3: 运行程序并等待 +* step 4: 查看结果(储存在result文件夹下) ## 预训练模型说明 当前的预训练模型分为两类——添加/移除马赛克以及风格转换. @@ -23,10 +44,10 @@ | :------------------------------: | :-------------------------------------------: | | add_face.pth | 对图片或视频中的脸部打码 | | clean_face_HD.pth | 对图片或视频中的脸部去码
(要求内存 > 8GB). | -| add_youknow.pth | 对图片或视频中的十八禁内容打码 | -| clean_youknow_resnet_9blocks.pth | 对图片或视频中的十八禁内容去码 | -| clean_youknow_video.pth | 对视频中的十八禁内容去码 | -| clean_youknow_video_HD.pth | 对视频中的十八禁内容去码
(要求内存 > 8GB) | +| add_youknow.pth | 对图片或视频中的...内容打码 | +| clean_youknow_resnet_9blocks.pth | 对图片或视频中的...内容去码 | +| clean_youknow_video.pth | 对视频中的...内容去码,推荐使用带有'video'的模型去除视频中的马赛克 | + * 风格转换 @@ -52,8 +73,8 @@ * 7. 自行输入更多参数,详见下文 * 8. 运行 * 9. 打开帮助文件 -* 10. 支持我们 -* 11. 版本信息 +* 10. 支持我们 +* 11. 版本信息 * 12. 打开项目的github页面 ### 参数说明 @@ -62,7 +83,7 @@ | 选项 | 描述 | 默认 | | :----------: | :------------------------: | :-------------------------------------: | -| --use_gpu | if -1, do not use gpu | 0 | +| --gpu_id | if -1, do not use gpu | 0 | | --media_path | 需要处理的视频或者照片的路径 | ./imgs/ruoruo.jpg | | --mode | 运行模式(auto/clean/add/style) | 'auto' | | --model_path | 预训练模型的路径 | ./pretrained_models/mosaic/add_face.pth | @@ -75,7 +96,7 @@ | --mosaic_mod | 马赛克类型 -> squa_avg/ squa_random/ squa_avg_circle_edge/ rect_avg/random | squa_avg | | --mosaic_size | 马赛克大小,0则为自动 | 0 | | --mask_extend | 拓展马赛克区域 | 10 | -| --mask_threshold | 马赛克区域识别阈值 0~255 | 64 | +| --mask_threshold | 马赛克区域识别阈值 0~255,越小越容易被判断为马赛克区域 | 64 | * 去除马赛克 diff --git a/docs/options_introduction.md b/docs/options_introduction.md index 1947c97..3888eed 100644 --- a/docs/options_introduction.md +++ b/docs/options_introduction.md @@ -5,7 +5,7 @@ If you need more effects, use '--option your-parameters' to enter what you need | Option | Description | Default | | :----------: | :------------------------: | :-------------------------------------: | -| --use_gpu | if -1, do not use gpu | 0 | +| --gpu_id | if -1, do not use gpu | 0 | | --media_path | your videos or images path | ./imgs/ruoruo.jpg | | --start_time | start position of video, default is the beginning of video | '00:00:00' | | --last_time | limit the duration of the video, default is the entire video | '00:00:00' | diff --git a/docs/options_introduction_CN.md b/docs/options_introduction_CN.md index ef775a1..8557d04 100644 --- a/docs/options_introduction_CN.md +++ b/docs/options_introduction_CN.md @@ -5,7 +5,7 @@ | 选项 | 描述 | 默认 | | :----------: | :------------------------: | :-------------------------------------: | -| --use_gpu | if -1, do not use gpu | 0 | +| --gpu_id | if -1, do not use gpu | 0 | | --media_path | 需要处理的视频或者照片的路径 | ./imgs/ruoruo.jpg | | --start_time | 视频开始处理的位置,默认从头开始 | '00:00:00' | | --last_time | 处理的视频时长,默认是整个视频 | '00:00:00' | diff --git a/docs/pre-trained_models_introduction.md b/docs/pre-trained_models_introduction.md index 2c4a3f3..cc74728 100644 --- a/docs/pre-trained_models_introduction.md +++ b/docs/pre-trained_models_introduction.md @@ -10,8 +10,8 @@ Download pre-trained model via [[Google Drive]](https://drive.google.com/open?i | clean_face_HD.pth | Clean mosaic to faces in images/video.
(RAM > 8GB). | | add_youknow.pth | Add mosaic to ... in images/videos. | | clean_youknow_resnet_9blocks.pth | Clean mosaic to ... in images/videos. | -| clean_youknow_video.pth | Clean mosaic to ... in videos. | -| clean_youknow_video_HD.pth | Clean mosaic to ... in videos.
(RAM > 8GB) | +| clean_youknow_video.pth | Clean mosaic to ... in videos. It is better for processing video mosaics | + ### Style Transfer diff --git a/docs/pre-trained_models_introduction_CN.md b/docs/pre-trained_models_introduction_CN.md index 9156391..9b82e2a 100644 --- a/docs/pre-trained_models_introduction_CN.md +++ b/docs/pre-trained_models_introduction_CN.md @@ -10,8 +10,8 @@ | clean_face_HD.pth | 对图片或视频中的脸部去码
(要求内存 > 8GB). | | add_youknow.pth | 对图片或视频中的...内容打码 | | clean_youknow_resnet_9blocks.pth | 对图片或视频中的...内容去码 | -| clean_youknow_video.pth | 对视频中的...内容去码 | -| clean_youknow_video_HD.pth | 对视频中的...内容去码
(要求内存 > 8GB) | +| clean_youknow_video.pth | 对视频中的...内容去码,推荐使用带有'video'的模型去除视频中的马赛克 | + ### 风格转换 diff --git a/docs/training_with_your_own_dataset.md b/docs/training_with_your_own_dataset.md index fa36938..bc2a30c 100644 --- a/docs/training_with_your_own_dataset.md +++ b/docs/training_with_your_own_dataset.md @@ -10,7 +10,11 @@ We will make "face" as an example. If you don't have any picture, you can downlo - [Pytorch 1.0+](https://pytorch.org/) - NVIDIA GPU(with more than 6G memory) + CUDA CuDNN
#### Dependencies -This code depends on opencv-python, torchvision, matplotlib available via pip install. +This code depends on opencv-python, torchvision, matplotlib, tensorboardX, scikit-image available via conda install. +```bash +# or +pip install -r requirements.txt +``` #### Clone this repo ```bash git clone https://github.com/HypoX64/DeepMosaics @@ -32,31 +36,31 @@ python draw_mask.py --datadir 'dir for your pictures' --savedir ../datasets/draw python get_image_from_video.py --datadir 'dir for your videos' --savedir ../datasets/video2image --fps 1 ``` ### Clean mosaic dataset -We provide several methods for generating clean mosaic datasets. However, for better effect, we recommend train a addmosaic model in a small data first and use it to automatically generate datasets in a big data.(recommend: Method 2(for image) & Method 4(for video)) -* Method 1: Use drawn mask to make pix2pix(HD) datasets(Require``` origin_image``` and ```mask```) +We provide several methods for generating clean mosaic datasets. However, for better effect, we recommend train a addmosaic model in a small data first and use it to automatically generate datasets in a big data. (recommend: Method 2(for image) & Method 4(for video)) +* Method 1: Use drawn mask to make pix2pix(HD) datasets (Require``` origin_image``` and ```mask```) ```bash python make_pix2pix_dataset.py --datadir ../datasets/draw/face --hd --outsize 512 --fold 1 --name face --savedir ../datasets/pix2pix/face --mod drawn --minsize 128 --square ``` -* Method 2: Use addmosaic model to make pix2pix(HD) datasets(Require addmosaic pre-trained model) +* Method 2: Use addmosaic model to make pix2pix(HD) datasets (Require addmosaic pre-trained model) ```bash python make_pix2pix_dataset.py --datadir 'dir for your pictures' --hd --outsize 512 --fold 1 --name face --savedir ../datasets/pix2pix/face --mod network --model_path ../pretrained_models/mosaic/add_face.pth --minsize 128 --square --mask_threshold 128 ``` -* Method 3: Use Irregular Masks to make pix2pix(HD) datasets(Require [Irregular Masks](https://nv-adlr.github.io/publication/partialconv-inpainting)) +* Method 3: Use Irregular Masks to make pix2pix(HD) datasets (Require [Irregular Masks](https://nv-adlr.github.io/publication/partialconv-inpainting)) ```bash python make_pix2pix_dataset.py --datadir 'dir for your pictures' --hd --outsize 512 --fold 1 --name face --savedir ../datasets/pix2pix/face --mod irregular --irrholedir ../datasets/Irregular_Holes_mask --square ``` -* Method 4: Use addmosaic model to make video datasets(Require addmosaic pre-trained model. This is better for processing video mosaics) +* Method 4: Use addmosaic model to make video datasets (Require addmosaic pre-trained model. This is better for processing video mosaics) ```bash -python make_video_dataset.py --datadir 'dir for your videos' --model_path ../pretrained_models/mosaic/add_face.pth --mask_threshold 96 --savedir ../datasets/video/face +python make_video_dataset.py --model_path ../pretrained_models/mosaic/add_face.pth --gpu_id 0 --datadir 'dir for your videos' --savedir ../datasets/video/face ``` ## Training ### Add ```bash cd train/add -python train.py --use_gpu 0 --dataset ../../datasets/draw/face --savename face --loadsize 512 --finesize 360 --batchsize 16 +python train.py --gpu_id 0 --dataset ../../datasets/draw/face --savename face --loadsize 512 --finesize 360 --batchsize 16 ``` ### Clean -* For image datasets(generated by ```make_pix2pix_dataset.py```) +* For image datasets (generated by ```make_pix2pix_dataset.py```) We use [pix2pix](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) or [pix2pixHD](https://github.com/NVIDIA/pix2pixHD) to train model. We just take pix2pixHD as an example. ```bash git clone https://github.com/NVIDIA/pix2pixHD @@ -64,10 +68,10 @@ cd pix2pixHD pip install dominate python train.py --name face --resize_or_crop resize_and_crop --loadSize 563 --fineSize 512 --label_nc 0 --no_instance --dataroot ../datasets/pix2pix/face ``` -* For video datasets(generated by ```make_video_dataset.py```) +* For video datasets (generated by ```make_video_dataset.py```) ```bash cd train/clean -python train.py --dataset ../../datasets/video/face --savename face --savefreq 100000 --gan --hd --lr 0.0002 --lambda_gan 1 --use_gpu 0 +python train.py --dataset ../../datasets/video/face --savename face --n_blocks 4 --lambda_GAN 0.01 --loadsize 286 --finesize 256 --batchsize 16 --n_layers_D 2 --num_D 3 --n_epoch 200 --gpu_id 4,5,6,7 --load_thread 16 ``` ## Testing -Put saved network to ```./pretrained_models/mosaic/``` and rename it as ```add_face.pth``` or ```clean_face_HD.pth``` or ```clean_face_video_HD.pth``` +Put saved network to ```./pretrained_models/mosaic/``` and rename it as ```add_face.pth``` or ```clean_face_HD.pth``` or ```clean_face_video_HD.pth```and then run ```deepmosaic.py --model_path ./pretrained_models/mosaic/your_model_name``` diff --git a/imgs/icon.jpg b/imgs/icon.jpg deleted file mode 100644 index 672f6ed..0000000 Binary files a/imgs/icon.jpg and /dev/null differ diff --git a/imgs/logo.ico b/imgs/logo.ico new file mode 100644 index 0000000..75edca6 Binary files /dev/null and b/imgs/logo.ico differ diff --git a/imgs/logo.png b/imgs/logo.png new file mode 100644 index 0000000..41b8ee3 Binary files /dev/null and b/imgs/logo.png differ diff --git a/imgs/logo_withwords.png b/imgs/logo_withwords.png new file mode 100644 index 0000000..cc3290d Binary files /dev/null and b/imgs/logo_withwords.png differ diff --git a/make_datasets/csv/stars_name.csv b/make_datasets/csv/stars_name.csv deleted file mode 100644 index 4c0015b..0000000 --- a/make_datasets/csv/stars_name.csv +++ /dev/null @@ -1 +0,0 @@ -邓紫棋,范玮琪,赫子铭,邵美琪,李易峰,尤浩然,张丰毅,李宗翰,范冰冰,朱亚文,彭丹,蔡琴,杨幂,刘嘉玲,童蕾,张丹峰,Angelababy,闫妮,热依扎,柯受良,唐嫣,张静初,林忆莲,濮存昕,刘诗诗,莫小棋,潘粤明,贾晓晨,周星驰,毛俊杰,万茜,隋俊波,李小璐,赵文卓,潘长江,毛孩,柳岩,应采儿,郑秀文,吕丽萍,周杰伦,聂远,萧淑慎,安雅萍,赵丽颖,毛晓彤,宣萱,黄小柔,张柏芝,张智尧,何美钿,涂黎曼,赵本山,黄秋生,张铁林,印小天,刘德华,唐一菲,于明加,张可颐,姚笛,干露露,李光洁,任容萱,高圆圆,杨丞琳,陈慧琳,余少群,谢霆锋,利智,左小青,徐怀钰,林志颖,六小龄童,郑元畅,朱媛媛,李菲儿,梁朝伟,苗侨伟,赵宝刚,吴镇宇,贾青,吴亚馨,楼南光,林志玲,郑伊健,曾志伟,吴京安,钟汉良,金城武,侯梦莎,王传一,孙俪,黎姿,吴君如,刘洲成,戚薇,张惠妹,周韵,丁小芹,林心如,言承旭,植敬雯,许冠文,房祖名,邱泽,叶玉卿,莫少聪,佟丽娅,张曼玉,辰亦儒,李李仁,吴奇隆,蓝洁瑛,陈百强,田海蓉,郭碧婷,邹兆龙,薛佳凝,戚美珍,李连杰,蒋勤勤,谷智鑫,吴廷烨,汤唯,吴绮莉,朱梓骁,丁春诚,马伊琍,钟丽缇,马景涛,张博宇,陆毅,张嘉倪,狄龙,马浚伟,周润发,金世佳,何超仪,冯宝宝,陈乔恩,杨恭如,周渝民,王思平,吴尊,卓依婷,朱一龙,龙飘飘,陈妍希,欧弟,张雨生,张雅蓓,陈冠希,陈建州,陶慧敏,官晶华,杜汶泽,梁家辉,吕颂贤,衣珊,王祖贤,袁咏仪,张译,鲍国安,伊能静,钟嘉欣,袁洁莹,罗乐林,张学友,李依晓,杨童舒,曹炳琨,罗志祥,杨乐乐,李心洁,姜昕言,赵又廷,刘若英,秦沛,朱铁,王祖蓝,李嘉欣,隋棠,丁嘉丽,吴秀波,颜丹晨,李诗韵,谭耀文,马天宇,莫文蔚,郑则仕,金玉婷,张耀扬,元彪,贺军翔,涂松岩,娄艺潇,徐熙娣,斯琴高丽,萧正楠,蔡依林,杨玏,王君平,王一楠,林青霞,张含韵,程愫,唐笑,彭于晏,蒋劲夫,王茜华,程琤,王丽坤,王若涵,王小利,狄莺,孙红雷,袁弘,卓文萱,郑俊弘,甄子丹,安志杰,冯淬帆,刘畊宏,李小冉,袁立,汤镇业,傅明宪,徐熙媛,余男,闫学晶,吴建飞,马苏,薛凯琪,于小伟,周秀娜,张俪,梁小龙,林子祥,沈卓盈,林依晨,庾澄庆,郭晋安,赵文瑄,巨兴茂,刘晓洁,徐帆,黄子扬,徐若瑄,曾沛慈,董骠,夏一瑶,叶一茜,沈佳妮,陈展鹏,刘思言,张歆艺,宋丹丹,蔡康永,赖琳恩,释小龙,潘玮柏,蒲巴甲,杨钫涵,汪东城,斓曦,杨佑宁,辛柏青,吴映洁,焦恩俊,陈法蓉,颜仟汶,王珞丹,马雅舒,王力可,陆树铭,张智霖,吕一,天心,崔漫莉,关之琳,阮经天,迟重瑞,邬倩倩,江疏影,翁美玲,曾恺玹,寺唯宏正,舒淇,王学兵,刘蓓,丁子峻,张嘉译,钱小豪,王雅捷,邵兵,颖儿,黄磊,郑浩南,王若心,陈意涵,林熙蕾,于荣光,钮承泽,安以轩,林雅诗,小小彬,刘劲,李佳航,蔡少芬,刘品言,王一淼,霍思燕,何润东,张震岳,邢佳栋,徐静蕾,张凯丽,温兆伦,利昂霖,吴京,童瑶,林依轮,陈彦妃,蔡卓妍,徐正曦,何家劲,黎耀祥,吴彦祖,姚星彤,江祖平,谢金燕,赵雅芝,姚芊羽,李保田,叶蕴仪,田朴珺,欧阳震华,马少骅,张艾嘉,尹天照,潘霜霜,韩雨芹,吴家丽,张韶涵,唐禹哲,戚迹,毛舜筠,梅艳芳,侯佩岑,陶晶莹,班嘉佳,贾静雯,钟楚红,邬君梅,谢君豪,俞飞鸿,余文乐,周晓涵,谭松韵,张卫健,关悦,蓝盈莹,曾宝仪,炎亚纶,秦海璐,唐诗咏,方中信,李亚鹏,许冠杰,张文慈,马蹄露,谭咏麟,高露,杨一展,苑琼丹,苏有朋,钟镇涛,徐洪浩,王子子,周海媚,万梓良,吕良伟,杨泽霖,陈若仪,汤加丽,杨明娜,张晨光,陈数,陈志朋,张涵予,赵子惠,郭富城,茹萍,徐熙颜,李幼斌,陈怡蓉,郭珍霓,曹达华,田蕊妮,寇振海,姜大卫,赵子琪,赵传,曾之乔,薛芷伦,寇乃馨,李宇春,张靓颖,周笔畅,何洁,刘亦菲,陈好,尚雯婕,张筱雨,韩雪,孙菲菲,陈紫函,朱雅琼,江一燕,厉娜,许飞,胡灵,郝菲尔,刘力扬,reborn,章子怡,谭维维,魏佳庆,张亚飞,李旭丹,孙艺心,巩贺,艾梦萌,闰妮,王蓉,汤芳,牛萌萌,赵薇,周迅,金莎,纪敏佳,黄雅莉,阿桑,董卿,金铭,徐行,朱妍,夏颖,陈西贝,冯家妹,高娅媛,林爽,郑靖文,陶虹,黄奕,董洁,巩俐,于娜,孟广美,Gameapple,美女奉奉,小龙女彤彤,张子萱,果子,丁贝莉,吸血芭比,公交MM,香香,段思思,二月丫头,刘羽琦,dodolook,拉拉公主,沈丽君,周璟馨,丁叮,谢雅雯,陈嘉琪,宋琳,郭慧敏,卢洁云,佘曼妮,黄景,马艳丽,蒋雯丽,宁静,许晴,瞿颖,张延,闵春晓,蔡飞雨,邓莎,白冰,程媛媛,吴婷,殷叶子,朱伟珊,孙菂,赵梦恬,龚洁,许晚秋,杨舒婷,乔维怡,王海珍,易慧,谢雨欣,陈娟红,舒畅,曹颖,李冰冰,王艳,沈星,阿朵,周洁,杨林,李霞,陈自瑶,李湘,金巧巧,梅婷,刘涛,安又琪,杨钰莹,马伊俐,陈红,鲍蕾,牛莉,胡可,龚蓓苾,田震,吕燕,王姬,苗圃,李欣汝,王小丫,秦岚,彭心怡,邓婕,眉佳,李媛媛,刘晓庆,杨若兮,黄圣依,林熙,斯琴格日乐,宋祖英,郝蕾,乐珈彤,冯婧,盖丽丽,杨澜,沈冰,王宇婕,王希维,姜培琳,何晴,焦媛,白灵,胡静,陈冲,刘怡君,韦唯,龚雪,周彦宏,刘丹,傅艺伟,谢东娜,黑鸭子,周璇,杨欣,陈小艺,伍宇娟,苏瑾,李玲玉,潘虹,沈丹萍,岳红,赵静怡,宋晓英,梁咏琪,李纹,林嘉欣,周丽淇,潘伟柏,梁静茹,周慧敏,杨千桦,林俊杰,孙燕姿,杜雯惠,郑少秋,柯有伦,麦兆辉,林咏伦,苏友朋,李志刚,杜德伟,方力申,刘日曦,刘雅丽,陈炜,孙佳君,崔健邦,陈秀雯,郭霭明,樊亦敏,黄倩,张小娴,袁文杰,连凯,刘伟强,许志安,彭嘉丽,区文诗,汤盈盈,侯湘婷,张慧仪,梁韵蕊,陈颖研,李婉华,许鞍华,向海岚,丘凯敏,刘晓彤,文颂娴,刘心悠,廖安丽,胡彦斌,黄耀明,伦永亮,夏萍,陈芷菁,朱茵,关德辉,韩君婷,张曼伶,林国斌,黄智贤,彭健新,陈浩民,廖碧儿,胡林,梁绰妍,邓蔼霖,八两金,陈文媛,赵学而,林保怡,黄宗泽,黄泽锋,周国丰,李国豪,李小龙,梁家仁,邝美云,蔡一智,卢巧音,车淑梅,梁芷珊,刘钖明,李若彤,郑裕玲,林莉,彭羚,洪欣,徐子淇,叶童,施念慈,嘉碧仪,李心媚,陈加玲,蔡卓研,司徒瑞祈,应昌佑,杨采妮,畲诗曼,李思欣,陈绮贞,杨雪仪,江芷妮,陈采岚,林一峰,潘芝莉,欧阳妙芝,黄凯芹,锺丽缇,活己岚,欧倩怡,姚嘉妮,李玟,蒋怡,马小灵,陈宝珠,缪骞人,吴家乐,罗启新,陈敏婷,王心凌,胡凯欣,刘松仁,陈晓东,李修贤,夏韶声,王杰,胡大为,吴大维,陶吉吉,卢戴维,曾建明,黄子华,吴浩康,何韵诗,叶德娴,麦子豪,张国洪,李浩林,郭政鸿,李迪文,夏健龙,关浩扬,陈海恒,李逸朗,张伟文,唐剑康,朱洁仪,陈嘉熙,陈奕迅,沈傲君,张兆辉,吴国敬,梁汉文,苏志威,姚莹莹,何绮玲,姚子羚,孙泳恩,江美仪,陈洁灵,李美凤,江希文,廖隽嘉,陈奕斯,官恩娜,陈嘉桦,蔡雪敏,陈雅伦,卢凯彤,蔡子健,邓肇欣,萧亚轩,倪晨曦,林二汶,周华健,卢淑仪,关宝慧,黄伊汶,张锦程,周国贤,葛民辉,巫启贤,孙耀威,伍家廉,郭伟亮,李天翔,李敏,袁弥明,关咏荷,陈嘉容,麦包,许慧欣,陈法拉,王菲,黄日华,活希儿,袁彩云,陈慧珊,张天爱,郭少芸,叶丽仪,陈勋奇,李进,海俊杰,罗嘉良,唐奕聪,林苑,锺沛枝,黄淑仪,杨其龙,锺欣桐,陈素莹,利嘉儿,陈嘉上,叶佩雯,李克勤,谭小环,徐濠萦,刘恺威,田馥甄,朱凯婷,欧海伦,马海伦,谭玉瑛,陈玉莲,吕怡慧,温碧霞,黄泆潼,梁佩瑚,陈逸宁,梁洛施,寇鸿萍,王菀之,成龙,陈晓琪,王树熹,吴宗宪,张洁莲,吴倩莲,梁咏琳,关心妍,黄沾,林海峰,李嘉文,卫兰,马诗慧,马阅,张咏恩,曾仕贤,朱永棠,梁琤,房祖明,张信哲,刘镇伟,陈汉诗,甄妮,张洪量,林晓峰,李汉文,卫诗,詹瑞文,少爷占,马楚成,谷祖琳,岑宝儿,唐丽球,陈佩珊,马伟豪,倪震,马德钟,郑敬基,陈任,容祖儿,邓建明,梁荣忠,万绮雯,蔡慧敏,吴宇森,姜戴维,杜琪峰,邓兆尊,颜国梁,阮德锵,李思捷,邓一君,陈苑琪,蔡依琳,李中希,陈小春,萧芳芳,陈彦行,李乐诗,郑欣宜,盖鸣辉,滕丽名,高丽虹,夏利奥,陈琪,麦浚龙,侧田,汤宝如,吴雨霏,郁礼贤,徐子珊,周汶锜,梁佩诗,谢安琪,蔡枫华,朱孝天,吴克群,王合喜,李静怡,郭可盈,马国明,陈启泰,卢惠光,锺镇涛,关智斌,郑嘉颖,林子瑄,商天娥,陈洁仪,麦洁文,阮小仪,王家敏,梁雪湄,周俊伟,吴启华,郑丹瑞,李亚男,唐宁,古巨基,詹志文,黎明,张国荣,陈慧明,何俐恩,李莉莉,康子妮,王馨平,锺嘉欣,汪明荃,郭静,李丽珍,朱玲玲,罗莉萨,古天乐,阮兆祥,樊少皇,张继聪,苏玉华,卢冠廷,盖世宝,沈殿霞,陈美诗,刘思惠,傅佩嘉,蒋雅文,曹敏莉,王秀琳,霍纹希,陈曼娜,许秋怡,魏骏杰,曾华倩,陈妙瑛,锺丽淇,原子鏸,雷颂德,郭耀明,陈霁平,肥妈,刘家聪,张玛莉,韩马利,邹凯光,高钧贤,范晓萱,许美静,罗美薇,吴婉芳,邝文珣,赖雅妍,吴美珩,谢天华,王敏德,周文健,梁继璋,唐文龙,陶大宇,王光良,杨紫琼,叶璇,郑雪儿,米雪,伏明霞,张佳佳,朱健钧,杨爱谨,罗敏庄,朱咪咪,杨千嬅,卢敏仪,董敏莉,陈法容,陈豪,胡杏儿,陈松伶,兰茜,薛家燕,胡定欣,陈百祥,黄纪莹,吴文忻,沈玉薇,谭凯欣,张燊悦,张家辉,林中定,周丽琪,郑文雅,吴佩慈,胡蓓蔚,黄柏高,刘彩玉,刘绰琪,邓建泓,雷有晖,黄贯中,锺保罗,方保罗,谢伟俊,徐小凤,陈宝莲,杨宝玲,任葆琳,陈可辛,黎彼得,文彼得,蓝奕邦,陈国邦,刘美君,朱慧敏,陈莉敏,黄婉伶,傅天颖,郭芯其,陈建颖,林峰,黄百鸣,蔡济文,曹永廉,许同恩,杨洛婷,蔡一杰,戴梦梦,范振锋,许冠英,林振强,任贤齐,韦绮珊,郑健乐,梁奕伦,罗文,吴卓羲,郑中基,于仁泰,黄婉佩,周永恒,姚乐碧,黄卓玲,蔡洁雯,叶蒨文,李璨森,陈宇琛,洪金宝,陈键锋,森美,林青峰,杨英伟,邰正宵,刘永健,林姗姗,任家萱,李丽蕊,王卉,刘青云,周影,谢宁,刘婉君,张敏,陈敏芝,郭秀云,陈淑兰,邓萃雯,邓上文,姚乐怡,张玉珊,关淑怡,杨思琦,张晓,关秀媚,任达华,雷宇扬,伍思凯,梁思浩,李嘉慧,郭羡妮,唐季礼,黄湘怡,冯德伦,颜福伟,陈志云,车沅沅,黎明诗,郑融,王从希,邓丽欣,黄家强,张致恒,黎瑞莲,陈秀茹,陈锦鸿,杨怡,傅颖,李绮红,李茏怡,林敏俐,谢宛婷,洪天明,何宝生,陈德彰,林祖辉,梁靖琪,阮民安,叶翠翠,黄宇诗,周嘉玲,范逸臣,邓颖芝,吴建豪,梁慧嘉,邹琛玮,陈司翰,唐韦琪,蔡淇俊,谷德昭,王浩信,王贤志,陈德容,李明慧,黎瑞恩,骆乐,梁小冰,杨秀惠,雷凯欣,锺汉良,翁慧德,莫华伦,韩毓霞,刘浩龙,苏永康,孔庆翔,陈自强,林颖娴,张咏妍,刘小慧,俞琤,杨婉仪,梁敏仪,黄伟文,唐尧麟,张茵,甄楚倩,蒙嘉慧,郑希怡,刘文娟,谭凯琪,苍井空,川滨奈美,堤莎也加,町田梨乃,二阶堂仁美,饭岛爱,饭田夏帆,饭冢友子,芳本叶月,冈崎结由,冈田丽奈,高木萌美,高田礼子,高原流美,宫本真美,宫岛司,古都光,光月夜也,河村亚季子,河井梨绪,黑崎扇菜,红月流奈,华歌恋,吉川萌,及川奈央,吉川真奈美,吉崎纱南,吉野莎莉,今井明日香,今木翔子,金泽蓝子,进藤玲菜,井上可奈,久保美希,酒井未希,臼井利奈,菊池丽香,菊池英里,菊池智子,橘真央,具志坚阳子,可爱亚织沙,葵小夏,蓝山南,兰望美,里见奈奈子,里美奈奈子,里美由梨香,立花丽华,立木爱,凉白舞,铃川玲理,铃江纹奈,铃木麻奈美,芦屋瞳,麻川美绪,麻生叶子,美里霞,美崎凉香,美雪沙织,美月莲,明日香,木谷麻耶,奈奈见沙织,内藤花苗,内田理沙,鲇川亚美,片濑亚纪,平山朝香,前原优树,前原佑子,浅见伽椰,浅井理,青木琳,青木玲,青野诗织,青羽未来,青沼知朝,秋本玲子,秋菜里子,秋元优奈,如月可怜,若林树里,若月树里,森下理音,纱月结花,杉浦清香,杉山亚来,山下由美子,杉原凉子,上原留华,神城千佳,神崎麻衣,神崎麻子,矢吹丽,手束桃,树本凉子,水城凛,水野朋美,水野茜,水越丽子,四季彩香寺田弥生,松浦梦,松浦唯,松田千夏,松下爱来,松下可怜,松元静香,速水真保,藤彩香,藤代流花,藤崎秋,藤森智子,天衣美津,田村麻衣,望月瞳,舞岛美织,午后野弥生,西泽友里,夏美舞,相川未希,相户爱,相田由美,小仓杏,小川流果,筱宫知世,小栗杏菜,小森美王,小室优奈,小野由佳,筱原凉,小泽菜穗,小泽玛丽亚,筱冢真树子,星爱丽斯,星崎瞳,星野绫香,星野洋子,星野真弥,徐若樱,雪乃小春,岩下美季,遥优衣,野宫美忧,野原奈津美,叶月千穗,伊东美华,一色丽矢,一色鲇美,一条沙希,乙伊沙也加,樱井沙也加,由树莉莉,有川真生,有吉奈生子,有森玲香,雨宫优衣,原千寻,原史奈,原田春奈,远野麻耶,月野静玖,早纪麻未,早乙女舞,泽舞音,长濑爱,长月亚美,真木亚里沙,真山润,中川珠代,中村理央,中根由真,中山美玖,中原绫,仲井美帆,竹田树理,佐伯美奈,佐佐木,幸田梨纱,北原爱子,成膳任,戴文青木,德永千奈美,笛木优子,福原爱,高见美香,高树玛丽亚,宫崎葵,观月雏乃,海江田纯子,后藤理莎,后藤香南子,矶山沙耶香,矶山沙也加,吉冈美穗,吉泽瞳,加纳则子,加藤小雪,菅谷梨沙子,结城翼,井上和香,井上熏,酒井瑛里,久纱野水萌,铃木爱理,玲木美生,泷泽乃南,美依旗由美,木下亚由美,前田知惠,前原爱,浅田真央,清水佐纪,入江纱绫,三尺真奈美,三宅尚子,森下千里,上原绫,石村舞波,矢田亚希子,市川由衣,市井纱耶香,嗣永桃子,松岛菜菜子,松居彩,松元莉绪,樋口真未,细川直美,夏烧雅,相乐纪子,小川熏,小林惠美,小野奈美,小泽真珠,星野亚希,须藤茉麻,亚纪奈,岩田小百合,伊藤步,优香,友崎玲,中泽裕子,佐藤麻纱,安藤沙耶,奥山唯子,白崎令于,柏木奈纯,板谷佑,滨田翔子,朝雾唯,川崎爱,大和抚子,大西由梨香,岛田百合花,二宫优,绀野舞子,岗原厚子,高鸠阳子,古河由摩,谷田未央,河合绫纯美,和久井辛,和希沙,黑田美礼,横仓里奈,后藤亚维梨,户田惠梨香,吉濑美智子,加藤麻依,江纱绫,井上诗织,井上优香,井真理绘,堀井美月,莲沼民子,柳明日,落合玲奈,牧濑奈美,木下亚由,奈良沙绪理,浅丘南,秋本那夜,秋山玲子,秋庭比吕子,三尺真奈,三井保奈美,森下真理,山吹美花,山口纱弥加,杉里香,神代弓子,树梨沙,水谷利加,松鸠永里奈,松山麻美,松屿初音,塔山直美,藤香南子,天使美树,天野理惠,田崎由希,桐岛淳子,尾崎美果,西野美绪,相泽优香,小林里实,小早川洋子,叶山小姬,樱树露衣,樱田佳子,永井绘理香,远藤真纪,早川美波,早川桃香,折原琴,中鸠广香,中泽夏木,竹野内丰,佐藤江梨花,爱内萌,爱田露美,爱田毛毛,安倍夏实,安原真美,奥山香,八木泽,白川美奈美,白鸟智惠子,白亚朱里,北山静香,北原步,北原真裕,仓本安奈,仓田杏里,朝比奈真理,朝仓加穗里,朝仓玛丽亚,持田茜,冲田由加里,川奈由依,大友梨奈,岛田香奈,堤沙也加,渡边香,风间舞,风见京子,福山洋子,冈本夏生,高仓梨奈,高野瞳,宫本瞳,宫本阳子,宫地奈,宫间沙布子,工藤瞳,宫下杏菜,河田纯子,和希结衣,横山千枝,华美月,姬野莉梦,吉田友里,吉野碧,菅野美寿纪,江口美贵,结衣美沙,金城美丽,井上彩菜,井上雅,鸠村熏,酒井美幸,菊池则江,君岛美香,可爱亚芝莎,来栖敦子,蓝原夕妃,蛯原舞,立花优,立原贵美,恋野恋,铃木由美,麻生岬,麻生真美子,麻田子,茂森亚弓,美波志保,木内亚吉拉,内田京香,品田由依,平山绫,前岛美步,前田亚纪,浅见怜,浅野子,青木美里,清木裕子,青山遥,青山叶子,三濑真美子,三崎真绪,三上夕希,三尾安齐,森宏子,森山子,森野雫,山口理惠,山口美纪,山口萌,杉山圭,杉田惠美,山田誉子,杉原爱砂,上原亚也加,神崎美树,神田美沙纪,圣瑛麻,石川恩惠,石川瞳,石川优季奈,矢吹凉子,矢吹麻理奈,矢择优子,水城梓,水希遥,松岛奈奈子,松纱良,速水怜,藤井彩,藤崎弭代,田山真美子,田中梨子,田中美久,瞳理欧,望月七,望月沙香,望月英子,武田真治,夏结玲,相纪美,相乐晴子,相仁泽美,相原里奈,翔巴辉,小池亚弭,小峰由衣,筱宫庆子,小田凉子,小向杏奈,小野濑,幸田李梨,岩崎千鹤,野宫凛子,野野由利加,叶山路易,一宫理绘,伊藤美沙纪,一条小百合,樱庭凉子,永井流奈,优木里绪奈,优木美纱,羽田夕夏,原惠美子,远山雪乃,远藤梨奈,早濑理沙,早乙女香织,长谷川,真纯麻子,织原奈美,柊丽子,中条佳奈子,中野美奈,仲村桃,足立美,佐藤有纪,八木亚希子,朝比奈英里,朝仓仁美,朝仓香乃,朝仓遥,朝美光,朝美绘乃香,朝丘瞳,朝霞佳绘美,赤西凉,川野亚希子,大久保玲,饭干惠子,福光千穗,冈田安希,高以亚希子,和久井由菜,吉木纯菜,吉泽京子,井上华菜,君野梦,堀切子,楠麻耶,南使香,平丸久美子,青木友梨,仁乃庆子,三浦富美子,山本京子,榊彩弥,矢野显子,水野亚美,水泽千夏,太田优,藤森子,相崎琴音,相泽纪美,星美织,杨原京子,早川濑里奈,斋藤朝子,长泽筑实,中谷香织,中森子,中条美华,竹内爱,庄司爱,佐藤春菜,佐藤子,津野田熏,吉井玲奈,阿嘉奈津,安藤希,安田奈央,奥川希美,奥山美夏,白鸟美由纪,百合香,北村夕起,北山英里,朝仓志穗,朝丘南,朝丘纱智,朝日美穗,朝永真弥,朝长真弥,纯名梨沙,村田洋子,大林典子,二瓶有香,芳贺优里雅,妃今日子,福美香织,冈本多绪,刚野凤子,高桥由美子,桂木萌,河合梓,横须贺,吉成香,吉村优,臼井里绘,橘友贺,来栖凉子,濑户美贵子,濑雅子,林绘里,玲樱奈,美月由奈,梦野玛丽亚,秘叉华,木内美步,木尾原真弓,牧野泉,鲇川直美,清水理惠子,萩原舞,泉由香,三浦桃,松树梨,松下真依子,松元伊代,藤宫知世,田村茜,田真潮,桐岛惠理香,梶原真弓,西尾佑里,相泽朝海,相泽沙赖,小阪光,小仓艾莉丝,筱峰爱,野乃原,优里香,由津希,泽绪凛,长曾我部蓉子,真莉亚,真崎麻衣,仲根佳绘美,爱原千芙,绫波优,奥菜千春,奥菜翼,八木原麻优,白川沙也加,白石枫,白石麻梨子,宝来美雪,北原雪,宝生琉璃,草剃纯,长濑茜,赤阪梨乃,赤阪美步,大路惠美,岛津讶子,德井唯,儿岛香绪里,福泽京子,宫泽璃音,吉野美穗,橘琉璃,濑户准,濑名凉子,片濑梨音,齐藤美穗,枪田彩野,桥本美步,三笑明日香,上原绘里香,石井淳笑,松冈理穗,松井夏穗,松元亚璃沙,唐泽美树,小池绘美子,小泉琉美,小山涉,小野谷实穗,星野琉海,续丽子,岩崎美穗,泽山凉子,辰巳奈都子,热田久美,姬野香,榎本瑞穗,榎本瑞怆,榎木加奈子,星野真唯,八木泽莉央,八木泽景一,柏木瞳一郎,坂下千里子,保坂拓见,北原一咲,泷北智子,夏本加奈子,端本千奈美,爱本瑞穗,本树尤真,仓内安奈,小鹿纯子,长坂仁惠,赤坂丽,赤坂七惠,赤咲伶奈,川村美咲,春日咲衣,镰田奈津美,风见里穗,富田梨惠,高坂真由,宫咲志帆,光咲玲奈,黑坂真美,胡桃泽马里奈,吉成香子,吉田亚咲,今井优,井坂绘美,久留须由美,蓝田美丰,笠木彩花,丰岛真千子,铃木美生,吉田亚纪子,瑠川淳子,美咲沙耶,美咲亚弥,美咲亚沙,梦咲亚由,乃木真利子,乃坂绘美,佐藤和沙,金子纱香,片濑梨子,中岛千晶,浅野京子,吉泽有希子,濑间幸美,金子美铃,三田爱里,三田友穂,三咲真绪,咲小雪,沙耶香,相田纱耶香,杉田美园,侍山凉子,山崎亚美,山咲萌,山咲千里,山咲舞,山咲亚香里,山咲一步,长濑美优,长濑美华,樱井美优,坂上友香,神乐坂惠,神田朱未,神田美咲,小岛圣,泽诗奈奈子,石川施恩惠,石坂伊津佳,水咲凉子,水咲亚里美,水野理纱,松坂庆子,松坂纱良,松坂树梨,滩坂舞,藤井树,齐藤小雪,藤咲彩美,藤咲理香,白石美树,品川幸菜,吉崎渚,田代沙织,田岛美美,泽田悠里,田中玲娜,田坂菜月,田坂仁美,舞坂由衣,香坂仁见,咲田葵,薰樱子,冴岛奈绪,野坂惠美,野坂奈津美,观月亚里沙,伊吹美奈裳,音咲洵,真锅薰,冈真里子,向井真理子,松坂季实子,椎名英姬,佐仓真衣,前田亚季,坂本冬美,坂本绘,蔡妍,裴涩琪,全慧彬,宝儿,张英兰,韩彩英尹恩惠,宋慧乔,李多海,成宥利,金泰熙,金喜善,李恩珠,韩佳人,亚由美,蔡琳,bada,张力尹(chinese),李贞贤,崔智友,全智贤,张娜拉,李孝利,梁美京,文根英,林秀晶,李英爱,金静华,张瑞希,林允儿,宋允儿李秀景,郑柔美,郑多彬,简美妍,金善雅,韩智慧,韩惠珍,南相美,黄静茵,金泰妍,郑丽媛,金荷娜,刘荷娜,河利秀,孙艺珍,徐智英,何智媛,崔秀英,stephanie天舞,金贤珠,李美妍,金雅中,朴善英郑秀妍,金智秀,皇甫惠静,韩智敏,秋瓷炫,dana,朴恩惠,韩孝珠,黄美英,金正恩,申敏儿,孔孝真,金素妍,权侑莉,禹喜珍,徐珠贤,韩艺瑟,李真,高雅拉,崔明吉,李智贤,李英雅,尹海英,林智慧,李妍喜,朴喜本,甄美里,seeya,申智(高耀太),李沇熹,金孝渊,金美淑,洪秀儿,金慧成,宋智孝,李瑶媛,朴贞雅,沈恩珍,lina上美,babyvox,崔真实,秋素英,李秀英,sunday智声,jewelry,金度完,申爱,朴信惠,金敏贞,李银珠,金南珠朴志胤,李智雅,姜恩菲,南圭丽,李青儿,高斗心,白智英,朴秀珍,朴艺珍,裴斗娜,闵先艺,赵静林,李太兰,金芝荷,李素妍,河莉秀,宣美,韩恩贞,金允慧,高恩雅,韩惠淑,沈银河,高贤贞崔茹真,李娜英,赵允熙,金莎朗,姜晶花,严正花,朴嘉熙,朴智运,闵孝琳,李宝英,玉珠铉,朴美宣,李惠淑,黄宝拉,朴幼林,吴妍秀,李姬珍,全度妍,徐智慧,李美淑,明世彬,韩艺仁,金南智,姜受延秀爱,李贤智,沈惠珍,赵贞恩,黄秀贞,钱忍和,申恩庆,洪秀贤,车贤静,张熙珍,金敏善,鲜于银淑,李荷娜,金泫雅,金孝珍,艺智苑,廉晶雅,孙泰英,上美lina,郑彩恩,贤真,金智慧,张熙真,朴素美,张真英,高小英,姜惠贞,金允珍,申爱罗,秋尚美,金喜爱,秋相美,车裕利,洪莉娜,金宝美,宋善美,李爱静,姜成妍,yuri,金惠秀,金敏喜,李清儿,郑在英,慧英,吴允儿,朴诗恩朴美善,苏幼真,李海英,崔贞润,韩银贞,金香奇,金孝真,崔志友,朴莎朗,金丽娜,孔孝珍,文素利,金圭莉,池秀媛,徐信爱,郑秀美,李敏贞,林贞恩,宋允雅,韩高恩,金贤雅,尹珍熙,崔允英,金贞兰,许伊在,韩爱莉,闵智慧,李惠英,金善儿,尹智慧,宋孝智,蔡徐坤,陈立农,范丞丞,黄明昊,林彦俊,朱正廷,王子异,王琳凯,尤长靖,毕雯珺,李希侃,黄新淳,李权哲,丁泽仁,秦奋,韩沐伯,徐鹤尼,左叶,罗正,陆定昊,董又霖,董岩磊,钱正昊,韩雍杰,木子洋,灵超,岳岳,卜凡,朱均天,朱均一,朱一文,张晨宇,应智越,万宇贤,吕晨瑜,宋微子,何东东,李长庚,张艺凡,李若天,邓烺怡,靖佩瑶,周腾阳,杨羿,张奕轩,姜京佐,许凯皓,凌崎,周彦辰,朱星杰,Rapen,Glgel,张昕,王宥辰,陈斯琪,于洁,武连杰,徐圣恩,张达源,陈名豪,王梓豪,金逸涵,甘俊,明鹏,侯浩然,余明君,姜祎,娄滋博,胡致邦,高茂桐,叶泓希,伽里,罗杰,李志杰,林浩楷,孙凡杰,于斌,何嘉庚,孙浩然,张晏恺,李俊毅,谭俊毅,黄书豪,陈义夫,闵喆祥,李让,周锐,郑瑞彬,林超泽,赵凌峰,赵俞澈,邱治谐,梁辉,杨非同,李汶翰,胡春杨,胡文煊,林陌,陈宥维,陈涛,李宗霖,嘉羿,邓泽鸣,卡斯柏,杨朝阳,邓超元,王喆,车慧轩,蒙恩,连淮维,夏瀚宇,姚明明,师铭泽,姜圣民,李之繁,管烁,易烊千玺,王俊凯,王源,丁程鑫,马嘉祺,张真源,敖子逸,李天泽,陈玺达,宋亚轩,刘耀文,贺俊霖,刘昊然,吴磊,郑凯,鹿晗,陈赫,李晨,邓超,包贝尔,王宝强,张翰,白敬亭,魏大勋,邓伦,汪苏泷,许嵩,徐良,张艺兴,道枝骏佑,片寄凉太,山崎贤人,黄子韬,吴世勋,边伯贤,朴灿烈,金钟大,金钟仁,都暻秀,金钟国,王嘉尔,刘宪华,杜海涛,沈腾,何炅,李维嘉,薛之谦,杨洋,华晨宇,纪凌尘,陈翔,车银尤,南赫柱,王力宏,胡先煦,马可,任嘉伦,李荣浩,艾芙杰尼,邢昭林,林更新,华少翌,黄晓明,韩寒,韩庚,韩磊,海岩,海清,海鸣威,胡军,胡海泉,胡歌,吴亦凡,张国立,唐国强,姜文,葛优,黄渤,陈坤,张一山,王传君,于和伟,刘欢,林志炫,徐峥,金志文,蒋昌建,品冠,张伟,杜飞,雷佳音,欧豪,高云翔,刘奕君,佟大为,郭京飞,张鲁一,王凯,霍建华,张钧甯,林子闳,马振桓,宋芸桦,邵雨薇,任言恺,欧阳娜娜,宇宙,连晨翔,瑞莎,昆凌,杜天皓,芮娜,苏笠汶,李玉玺,李威,明道,梁又琳,游智翔,寇世勋,雨婷,沈建宏,姚蜜,萧敬腾,向以丞,黄心娣,柯佳嬿,殷悦,邵崇柏,余晋,高以翔,黄郁婷,谢依霖,潘仪君,苏见信,张熙恩,石知田,柯宇纶,寇家瑞,陈威翰,柯家豪,康茵茵,康康,柯雅馨,王耀庆,王大陆,郭采洁,文雨非,蔡芷纭,陈奕,简远信,文淇,李凯馨,陈佩骐,黄维德,张轩睿,蔡颐榛,徐洁儿,王雅慧,陈柏融,王陈怡娴,邱胜翊,陈楚河,戴君竹,Teddy,锦荣,黄伟晋,张震,金士杰,吴中天,陈柏霖,郭雪芙,张庭,凤小岳,方妍心,林柏宏,欧阳妮妮,李鸿其,谢翔雅,杜妍,刘德凯,江语晨,安钧璨,李立群,白歆惠,陈薇,黄柏钧,许名杰,田中千绘,郑靓歆,那维勋,程席榆,纪欣妤,胡因梦,田丽,何海东,林佑威,陈怡真,陈盈燕,郭品超,黄文豪,小甜甜,吴若瑄,邱心志,林宥嘉,安心亚,韩忠羽,倪安东,修杰楷,常铖,禾浩辰,王轶玲,陈德修,倪齐民,易柏辰,丁文琪,李又麟,官鸿,洪小铃,王以纶,李程彬,屈中恒,王阳明,王棠云,葛蕾,郝劭文,邱昊奇,李天柱,狄志杰,陈亦飞,林韦君,温升豪,桂纶镁,张皓明,郑开元,黄少祺,陈敬宣,何恋慈,潘迎紫,阿本,方芳,张立昂,庄濠全,归亚蕾,王思懿,李毓芬,杜姸,张天霖,许玮甯,包小柏,关颖,苟芸慧,简廷芮,杨丽菁,陈庭妮,陈匡怡,魏蔓,张玉嬿,谢欣颖,陈博正,徐嘉苇,邓美恩,陈艾熙,郭书瑶,高凌风,谢和弦,李铨,徐贵樱,许富翔,张榕容,陈玺安,赖煜哲,金燕玲,亚里,连静雯,张国柱,许雅钧,白梓轩,杨世瀚,刘以豪,戴立忍,谢祖武,张孝全,夏若妍,唐国忠,陈乃荣,易恩,秦杨,王宥胜,蔡淑臻,高捷,小煜,刘乐妍,王维琳,刘瑞琪,张睿家,林逸欣,曾佩瑜,郭思辰,安娜,杜孟竹,吴可熙,陈汉典,黄瀞怡,蔡黄汝,叶全真,安晨芯,秦汉,纪亚文,吴心缇,陈尚泽,曲澔浚,李依瑾,陈艾琳,陈语安,纪培慧,立威廉,猪哥亮,杨青倩,陈建豪,是元介,刘冠毅,谢盈萱,郭鑫,孙克杰,张勋杰,张静懿,陈庭萱,俞小凡,詹子晴,郑暐达,风田,赖宗赢,曹华恩,张行,蔡旻佑,张景岚,江宏恩,孟庭丽,宋新妮,刘奕儿,王诗安,海狗,宋达民,吕孔维,蔡维泽,杨谨华,周凯文,卓毓彤,朱主爱,庹宗华,姚凤凤,刘国劭,孙兴,王心嫚,田家达,夏台凤,姚元浩,萧蔷,席惟伦,陈羽凡,陈昭荣,周子瑜,金超群,谢坤达,傅雷,姬天语,费翔,阿喜,周丹莉,李国超,沈彦廷,沈海蓉,曾子余,潘慧如,李志希,黄牛,黄丽玲,陶嫚曼,蓝心湄,纪言恺,刘俊纬,黄立行,方志友,吴思贤,宫以腾,包小松,徐佳莹,杨千霈,陈景炀,胡玮杰,龙隆,李运庆,关诗敏,刘泯廷,许韶洋,谢语恩,施羽,王家梁,谭艾珍,王彩桦,龚继安,艾伟,邓丽君,徐敏,钱韦杉,吴辰君,杨贵媚,安东尼,王灿,林若亚,Selina,张瑀希,周绍栋,刘汉强,古斌,阿雅,郭源元,贺一航,吕雪凤,唐治平,林美秀,莫允雯,李元太,游艾迪,安哲,张佩华,夏靖庭,唐德惠,侯彦西,李罗,周俐葳,陈冠霖,龚海铭,张芯瑜,许雅涵,刘芳芸,BY2,蔡健雅,李维维,李沛旭,许光汉,简嫚书,陈为民,李国毅,林妍柔,涂世旻,林予晞,杨升达,辰烨,李佳颖,周孝安,林秀君,秦风,邓九云,谢雅琳,舒子晨,翁卡特,夏语心,傅传杰,黄腾浩,胡睿儿,陈若萍,张雁名,阿信,李妍憬,翁滋蔓,大飞,江淑娜,蓝正龙,林筱筠,梁如瑄,阿Ken,盛鉴,赵擎,素珠,郑家榆,郑人硕,李康宜,林芯蕾,周丹薇,杨琪,安佑德,黄裕翔,周咏轩,蔡昌宪,钟欣凌,安程希,张立威,郎祖筠,刘谦,司徒颖霜,丁当,陈武康,喻虹渊,王者心,卫子云,迪丽热巴,成毅,陈学冬,安悦溪,姜妍,杨紫,郑爽,关晓彤,何明翰,徐梵溪,李沁,宋轶,乔欣,王鸥,古力娜扎,张馨予,麦迪娜,张铭恩,张檬,吴优,尹正,罗云熙,陈瑶,侯明昊,蒋欣,张云龙,杨烁,胡冰卿,靳东,牟星,袁姗姗,张彬彬,罗晋,秦俊杰,赵圆瑗,唐艺昕,李纯,梁振伦,吴倩,米露,王子文,TFBOYS,陈晓,张若昀,晁然,陈梦希,乔任梁,孙佳雨,李宏毅,林秋楠,茅子俊,唐娜,赖雨蒙,马思纯,于朦胧,徐璐,宋茜,杨菲洋,阚清子,孙耀琦,张萌,田倚凡,徐海乔,王妍之,戴向宇,刘芊含,熊梓淇,蔡文静,杨蓉,王珊,李心艾,何杜娟,王洋,井柏然,张雪迎,赵韩樱子,曹曦月,姜潮,孙怡,周冬雨,米勒,焦俊艳,杨子姗,逯恣祯,李溪芮,刘美含,刘萌萌,景甜,李一桐,杨旭文,梁洁,柳希龙,郑业成,李晟,陈小纭,黄礼格,林允,徐冬冬,宋威龙,袁冰妍,付辛博,沈梦辰,陈钰琪,方文强,鄂博,阎汶宣,牛骏峰,李小萌,白雪,张哲瀚,鞠婧祎,吕佳容,张浩,施诗,刘敏涛,王亭文,范世錡,海陆,马薇,邢菲,郭俊辰,孔垂楠,施予斐,穆婷婷,盛一伦,任天野,杨舒,刘庭羽,严屹宽,金晨,王晓晨,李砚,尹淇,韩栋,康杰,韩东君,于晓光,陈洁,张维娜,杨之楹,郑恺,蒋蕊泽,林源,张芷溪,苏青,蓝波,杨志刚,白百何,李佳奇,晏紫东,李健,高伟光,李进荣,张子枫,孙骁骁,米热,于震,宋小宝,刘芮麟,宋祖儿,李兰迪,康磊,叶祖新,于毅,蒋依依,李林娟,刘波,胡一天,石悦安鑫,娄清,刘娜萍,秦语,刘楚恬,王千源,阮圣文,孙雪宁,李倩,祝绪丹,母其弥雅,刘雅瑟,应昊茗,刘启恒,李梦,宋妍霏,金瀚,尹智玄,虞朗,袁志博,刘敏,任重,吴昊宸,孙艺洲,刘笑歌,毛林林,何泓姗,李郁瞳,袁泉,宋佳,马蜀君,尤靖茹,聂子皓,尚语贤,俞灏明,黄轩,吕卓燃,李飞,周奇奇,张予曦,刘文曲,刘秋实,李蓓蕾,潘之琳,梁超,泓萱,苗驰,卢宇静,童菲,王美人,刘颖伦,熊乃瑾,张碧晨,刘黛希,马秋子,刘烨,宋家腾,买红妹,祖峰,瑛子,江珊,杨雨婷,刘成瑞,李金铭,柯蓝,艾晓琪,刘馨棋,孙铱,白宇,杜淳,马春瑞,雨婷儿,杨帆,隋雨蒙,李颖,何晟铭,梁亦芸,陆子艺,张雨绮,于月仙,孙坚,岳丽娜,袁文康,梁浩,盛冠森,李春嫒,阳蕾,刘恩佑,杨梓墨,李子峰,吴谨言,张睿,孙茜,李茂,荣梓杉,陆妍淇,荣蓉,任帅,黄一琳,原雨,殷桃,辛芷蕾,翟天临,李欣聪,张晓谦,李雨轩,王珂,刘智扬,张楷依,于函冰,柴碧云,刘倩文,于小彤,尹铸胜,于笑,吉克隽逸,贾乃亮,童苡萱,宋奕星,刘雨欣,刘琳,姜鸿,安泳畅,王乐君,杨曦,于越,张晓晨,袁雨萱,马丽,林思意,陆昱霖,宋伊人,甘婷婷,刘天佐,李程,林静,李明德,林永健,冉旭,苏倩薇,叶峰,任柯诺,刘芸,叶青,马程程,卢杉,刘恬汝,丁婷婷,梁晶晶,李乃文,季晨,梁林琳,林江国,杀漠,李帅,张梦露,张峻宁,刘冠翔,刘丛丹,尹馨梓,路晨,米紫安,杨昆,李呈媛,徐申东,虞书欣,余心恬,梁爱琪,申奥,林千鹿,李昕岳,李萌萌,沈航,刘潺,穆乐恩,王彦霖,周雨彤,侍宣如,李嘉雯,王闯,李泽,余皑磊,苑子艺,赵予熙,李芯逸,刘钇彤,侯佩杉,侯梦瑶,姚奕辰,石安妮,施潮锦子,井星文,马晓伟,马灿灿,隋咏良,楼佳悦,陈思成,岳旸,马靓,陈都灵,李茜,卢星宇,李一情,何穗,李超,张炯敏,沙溢,王挺,SNH48,陆骏瑶,陈雅婷,李依伊 diff --git a/make_datasets/make_pix2pix_dataset.py b/make_datasets/make_pix2pix_dataset.py index c9ccb87..c87dc30 100644 --- a/make_datasets/make_pix2pix_dataset.py +++ b/make_datasets/make_pix2pix_dataset.py @@ -16,7 +16,7 @@ from models import runmodel,loadmodel import util.image_processing as impro -from util import util,mosaic,data +from util import degradater, util,mosaic,data opt.parser.add_argument('--datadir',type=str,default='../datasets/draw/face', help='') @@ -87,11 +87,11 @@ mask = mask_drawn if 'irregular' in opt.mod: mask_irr = impro.imread(irrpaths[random.randint(0,12000-1)],'gray') - mask_irr = data.random_transform_single(mask_irr, (img.shape[0],img.shape[1])) + mask_irr = data.random_transform_single_mask(mask_irr, (img.shape[0],img.shape[1])) mask = mask_irr if 'network' in opt.mod: mask_net = runmodel.get_ROI_position(img,net,opt,keepsize=True)[0] - if opt.use_gpu != -1: + if opt.gpu_id != -1: torch.cuda.empty_cache() if not opt.all_mosaic_area: mask_net = impro.find_mostlikely_ROI(mask_net) @@ -107,11 +107,11 @@ saveflag = True if opt.mod == ['drawn','irregular']: - x,y,size,area = impro.boundingSquare(mask_drawn, random.uniform(1.2,1.6)) + x,y,size,area = impro.boundingSquare(mask_drawn, random.uniform(1.1,1.6)) elif opt.mod == ['network','irregular']: - x,y,size,area = impro.boundingSquare(mask_net, random.uniform(1.2,1.6)) + x,y,size,area = impro.boundingSquare(mask_net, random.uniform(1.1,1.6)) else: - x,y,size,area = impro.boundingSquare(mask, random.uniform(1.2,1.6)) + x,y,size,area = impro.boundingSquare(mask, random.uniform(1.1,1.6)) if area < 1000: saveflag = False @@ -130,11 +130,15 @@ if saveflag: # add mosaic img_mosaic = mosaic.addmosaic_random(img, mask) - # random blur + # random degradater if random.random()>0.5: - Q = random.randint(1,15) - img = impro.dctblur(img,Q) - img_mosaic = impro.dctblur(img_mosaic,Q) + degradate_params = degradater.get_random_degenerate_params(mod='weaker_2') + img = degradater.degradate(img,degradate_params) + img_mosaic = degradater.degradate(img_mosaic,degradate_params) + # if random.random()>0.5: + # Q = random.randint(1,15) + # img = impro.dctblur(img,Q) + # img_mosaic = impro.dctblur(img_mosaic,Q) savecnt += 1 diff --git a/make_datasets/make_video_dataset.py b/make_datasets/make_video_dataset.py index b9ce6cb..84ef5fa 100644 --- a/make_datasets/make_video_dataset.py +++ b/make_datasets/make_video_dataset.py @@ -14,7 +14,7 @@ from models import runmodel,loadmodel import util.image_processing as impro -from util import util,mosaic,data,ffmpeg +from util import filt, util,mosaic,data,ffmpeg opt.parser.add_argument('--datadir',type=str,default='your video dir', help='') @@ -56,6 +56,7 @@ ffmpeg.video2image(videopath, opt.temp_dir+'/video2image/%05d.'+opt.tempimage_type,fps=1, start_time = util.second2stamp(cut_point*opt.interval),last_time = util.second2stamp(opt.time)) imagepaths = util.Traversal(opt.temp_dir+'/video2image') + imagepaths = sorted(imagepaths) cnt = 0 for i in range(opt.time): img = impro.imread(imagepaths[i]) @@ -92,30 +93,65 @@ imagepaths = util.Traversal(opt.temp_dir+'/video2image') imagepaths = sorted(imagepaths) imgs=[];masks=[] - mask_flag = False - + # mask_flag = False + # for imagepath in imagepaths: + # img = impro.imread(imagepath) + # mask = runmodel.get_ROI_position(img,net,opt,keepsize=True)[0] + # imgs.append(img) + # masks.append(mask) + # if not mask_flag: + # mask_avg = mask.astype(np.float64) + # mask_flag = True + # else: + # mask_avg += mask.astype(np.float64) + + # mask_avg = np.clip(mask_avg/len(imagepaths),0,255).astype('uint8') + # mask_avg = impro.mask_threshold(mask_avg,20,64) + # if not opt.all_mosaic_area: + # mask_avg = impro.find_mostlikely_ROI(mask_avg) + # x,y,size,area = impro.boundingSquare(mask_avg,Ex_mul=random.uniform(1.1,1.5)) + + # for i in range(len(imagepaths)): + # img = impro.resize(imgs[i][y-size:y+size,x-size:x+size],opt.outsize,interpolation=cv2.INTER_CUBIC) + # mask = impro.resize(masks[i][y-size:y+size,x-size:x+size],opt.outsize,interpolation=cv2.INTER_CUBIC) + # impro.imwrite(os.path.join(origindir,'%05d'%(i+1)+'.jpg'), img) + # impro.imwrite(os.path.join(maskdir,'%05d'%(i+1)+'.png'), mask) + ex_mul = random.uniform(1.2,1.7) + positions = [] for imagepath in imagepaths: img = impro.imread(imagepath) mask = runmodel.get_ROI_position(img,net,opt,keepsize=True)[0] imgs.append(img) masks.append(mask) - if not mask_flag: - mask_avg = mask.astype(np.float64) - mask_flag = True - else: - mask_avg += mask.astype(np.float64) - - mask_avg = np.clip(mask_avg/len(imagepaths),0,255).astype('uint8') - mask_avg = impro.mask_threshold(mask_avg,20,64) - if not opt.all_mosaic_area: - mask_avg = impro.find_mostlikely_ROI(mask_avg) - x,y,size,area = impro.boundingSquare(mask_avg,Ex_mul=random.uniform(1.1,1.5)) - - for i in range(len(imagepaths)): - img = impro.resize(imgs[i][y-size:y+size,x-size:x+size],opt.outsize,interpolation=cv2.INTER_CUBIC) + x,y,size,area = impro.boundingSquare(mask,Ex_mul=ex_mul) + positions.append([x,y,size]) + positions =np.array(positions) + for i in range(3):positions[:,i] = filt.medfilt(positions[:,i],opt.medfilt_num) + + for i,imagepath in enumerate(imagepaths): + x,y,size = positions[i][0],positions[i][1],positions[i][2] + tmp_cnt = i + while sizeopt.minsize//4: + # if not opt.all_mosaic_area: + # mask_avg = impro.find_mostlikely_ROI(mask_avg) + # x,y,size,area = impro.boundingSquare(mask_avg,Ex_mul=ex_mul) + # img = impro.resize(imgs[i][y-size:y+size,x-size:x+size],opt.outsize,interpolation=cv2.INTER_CUBIC) + # mask = impro.resize(masks[i][y-size:y+size,x-size:x+size],opt.outsize,interpolation=cv2.INTER_CUBIC) + # impro.imwrite(os.path.join(origindir,'%05d'%(i+1)+'.jpg'), img) + # impro.imwrite(os.path.join(maskdir,'%05d'%(i+1)+'.png'), mask) + result_cnt+=1 @@ -124,5 +160,5 @@ util.writelog(os.path.join(opt.savedir,'opt.txt'), videopath+'\n'+str(result_cnt)+'\n'+str(e)) video_cnt +=1 - if opt.use_gpu != -1: + if opt.gpu_id != '-1': torch.cuda.empty_cache() diff --git a/models/BVDNet.py b/models/BVDNet.py new file mode 100644 index 0000000..f9ca814 --- /dev/null +++ b/models/BVDNet.py @@ -0,0 +1,198 @@ +import torch +import torch.nn as nn +from .pix2pixHD_model import * +from .model_util import * +from models import model_util + +class UpBlock(nn.Module): + def __init__(self, in_channel, out_channel, kernel_size=3, padding=1): + super().__init__() + + self.convup = nn.Sequential( + nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False), + nn.ReflectionPad2d(padding), + # EqualConv2d(out_channel, out_channel, kernel_size, padding=padding), + SpectralNorm(nn.Conv2d(in_channel, out_channel, kernel_size)), + nn.LeakyReLU(0.2), + # Blur(out_channel), + ) + + def forward(self, input): + outup = self.convup(input) + return outup + +class Encoder2d(nn.Module): + def __init__(self, input_nc, ngf=64, n_downsampling=3, activation = nn.LeakyReLU(0.2)): + super(Encoder2d, self).__init__() + + model = [nn.ReflectionPad2d(3), SpectralNorm(nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0)), activation] + ### downsample + for i in range(n_downsampling): + mult = 2**i + model += [ nn.ReflectionPad2d(1), + SpectralNorm(nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=0)), + activation] + + self.model = nn.Sequential(*model) + + def forward(self, input): + return self.model(input) + +class Encoder3d(nn.Module): + def __init__(self, input_nc, ngf=64, n_downsampling=3, activation = nn.LeakyReLU(0.2)): + super(Encoder3d, self).__init__() + + model = [SpectralNorm(nn.Conv3d(input_nc, ngf, kernel_size=3, padding=1)), activation] + ### downsample + for i in range(n_downsampling): + mult = 2**i + model += [ SpectralNorm(nn.Conv3d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1)), + activation] + self.model = nn.Sequential(*model) + + def forward(self, input): + return self.model(input) + +class BVDNet(nn.Module): + def __init__(self, N=2, n_downsampling=3, n_blocks=4, input_nc=3, output_nc=3,activation=nn.LeakyReLU(0.2)): + super(BVDNet, self).__init__() + ngf = 64 + padding_type = 'reflect' + self.N = N + + ### encoder + self.encoder3d = Encoder3d(input_nc,64,n_downsampling,activation) + self.encoder2d = Encoder2d(input_nc,64,n_downsampling,activation) + + ### resnet blocks + self.blocks = [] + mult = 2**n_downsampling + for i in range(n_blocks): + self.blocks += [ResnetBlockSpectralNorm(ngf * mult, padding_type=padding_type, activation=activation)] + self.blocks = nn.Sequential(*self.blocks) + + ### decoder + self.decoder = [] + for i in range(n_downsampling): + mult = 2**(n_downsampling - i) + self.decoder += [UpBlock(ngf * mult, int(ngf * mult / 2))] + self.decoder += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)] + self.decoder = nn.Sequential(*self.decoder) + self.limiter = nn.Tanh() + + def forward(self, stream, previous): + this_shortcut = stream[:,:,self.N] + stream = self.encoder3d(stream) + stream = stream.reshape(stream.size(0),stream.size(1),stream.size(3),stream.size(4)) + previous = self.encoder2d(previous) + x = stream + previous + x = self.blocks(x) + x = self.decoder(x) + x = x+this_shortcut + x = self.limiter(x) + return x + +def define_G(N=2, n_blocks=1, gpu_id='-1'): + netG = BVDNet(N = N, n_blocks=n_blocks) + netG = model_util.todevice(netG,gpu_id) + netG.apply(model_util.init_weights) + return netG + +################################Discriminator################################ +def define_D(input_nc=6, ndf=64, n_layers_D=1, use_sigmoid=False, num_D=3, gpu_id='-1'): + netD = MultiscaleDiscriminator(input_nc, ndf, n_layers_D, use_sigmoid, num_D) + netD = model_util.todevice(netD,gpu_id) + netD.apply(model_util.init_weights) + return netD + +class MultiscaleDiscriminator(nn.Module): + def __init__(self, input_nc, ndf=64, n_layers=3, use_sigmoid=False, num_D=3): + super(MultiscaleDiscriminator, self).__init__() + self.num_D = num_D + self.n_layers = n_layers + + for i in range(num_D): + netD = NLayerDiscriminator(input_nc, ndf, n_layers, use_sigmoid) + setattr(self, 'layer'+str(i), netD.model) + self.downsample = nn.AvgPool2d(3, stride=2, padding=[1, 1], count_include_pad=False) + + def singleD_forward(self, model, input): + return [model(input)] + + def forward(self, input): + num_D = self.num_D + result = [] + input_downsampled = input + for i in range(num_D): + model = getattr(self, 'layer'+str(num_D-1-i)) + result.append(self.singleD_forward(model, input_downsampled)) + if i != (num_D-1): + input_downsampled = self.downsample(input_downsampled) + return result + +# Defines the PatchGAN discriminator with the specified arguments. +class NLayerDiscriminator(nn.Module): + def __init__(self, input_nc, ndf=64, n_layers=3, use_sigmoid=False): + super(NLayerDiscriminator, self).__init__() + self.n_layers = n_layers + + kw = 4 + padw = int(np.ceil((kw-1.0)/2)) + sequence = [[nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2)]] + + nf = ndf + for n in range(1, n_layers): + nf_prev = nf + nf = min(nf * 2, 512) + sequence += [[ + SpectralNorm(nn.Conv2d(nf_prev, nf, kernel_size=kw, stride=2, padding=padw)), + nn.LeakyReLU(0.2) + ]] + + nf_prev = nf + nf = min(nf * 2, 512) + sequence += [[ + SpectralNorm(nn.Conv2d(nf_prev, nf, kernel_size=kw, stride=1, padding=padw)), + nn.LeakyReLU(0.2) + ]] + + sequence += [[nn.Conv2d(nf, 1, kernel_size=kw, stride=1, padding=padw)]] + + if use_sigmoid: + sequence += [[nn.Sigmoid()]] + + sequence_stream = [] + for n in range(len(sequence)): + sequence_stream += sequence[n] + self.model = nn.Sequential(*sequence_stream) + + def forward(self, input): + return self.model(input) + +class GANLoss(nn.Module): + def __init__(self, mode='D'): + super(GANLoss, self).__init__() + if mode == 'D': + self.lossf = model_util.HingeLossD() + elif mode == 'G': + self.lossf = model_util.HingeLossG() + self.mode = mode + + def forward(self, dis_fake = None, dis_real = None): + if isinstance(dis_fake, list): + if self.mode == 'D': + loss = 0 + for i in range(len(dis_fake)): + loss += self.lossf(dis_fake[i][-1],dis_real[i][-1]) + elif self.mode =='G': + loss = 0 + weight = 2**len(dis_fake) + for i in range(len(dis_fake)): + weight = weight/2 + loss += weight*self.lossf(dis_fake[i][-1]) + return loss + else: + if self.mode == 'D': + return self.lossf(dis_fake[-1],dis_real[-1]) + elif self.mode =='G': + return self.lossf(dis_fake[-1]) diff --git a/models/BiSeNet_model.py b/models/BiSeNet_model.py index b58ea5b..3675141 100644 --- a/models/BiSeNet_model.py +++ b/models/BiSeNet_model.py @@ -2,7 +2,7 @@ import torch.nn as nn import torch import torch.nn.functional as F -from . import components +from . import model_util import warnings warnings.filterwarnings(action='ignore') @@ -43,7 +43,7 @@ def forward(self, output, target): class resnet18(torch.nn.Module): def __init__(self, pretrained=True): super().__init__() - self.features = components.resnet18(pretrained=pretrained) + self.features = model_util.resnet18(pretrained=pretrained) self.conv1 = self.features.conv1 self.bn1 = self.features.bn1 self.relu = self.features.relu @@ -70,7 +70,7 @@ def forward(self, input): class resnet101(torch.nn.Module): def __init__(self, pretrained=True): super().__init__() - self.features = components.resnet101(pretrained=pretrained) + self.features = model_util.resnet101(pretrained=pretrained) self.conv1 = self.features.conv1 self.bn1 = self.features.bn1 self.relu = self.features.relu diff --git a/models/components.py b/models/components.py deleted file mode 100644 index 59cb333..0000000 --- a/models/components.py +++ /dev/null @@ -1,234 +0,0 @@ -import torch.nn as nn -import torch.utils.model_zoo as model_zoo - - -__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', - 'resnet152'] - - -model_urls = { - 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', - 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', - 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', - 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', - 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', -} - - -def conv3x3(in_planes, out_planes, stride=1): - """3x3 convolution with padding""" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -def conv1x1(in_planes, out_planes, stride=1): - """1x1 convolution""" - return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None, norm_layer=None): - super(BasicBlock, self).__init__() - if norm_layer is None: - norm_layer = nn.BatchNorm2d - # Both self.conv1 and self.downsample layers downsample the input when stride != 1 - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = norm_layer(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = norm_layer(planes) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - identity = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, inplanes, planes, stride=1, downsample=None, norm_layer=None): - super(Bottleneck, self).__init__() - if norm_layer is None: - norm_layer = nn.BatchNorm2d - # Both self.conv2 and self.downsample layers downsample the input when stride != 1 - self.conv1 = conv1x1(inplanes, planes) - self.bn1 = norm_layer(planes) - self.conv2 = conv3x3(planes, planes, stride) - self.bn2 = norm_layer(planes) - self.conv3 = conv1x1(planes, planes * self.expansion) - self.bn3 = norm_layer(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - - def forward(self, x): - identity = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - out = self.relu(out) - - return out - - -class ResNet(nn.Module): - - def __init__(self, block, layers, num_classes=1000, zero_init_residual=False, norm_layer=None): - super(ResNet, self).__init__() - if norm_layer is None: - norm_layer = nn.BatchNorm2d - self.inplanes = 64 - self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) - self.bn1 = norm_layer(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - self.layer1 = self._make_layer(block, 64, layers[0], norm_layer=norm_layer) - self.layer2 = self._make_layer(block, 128, layers[1], stride=2, norm_layer=norm_layer) - self.layer3 = self._make_layer(block, 256, layers[2], stride=2, norm_layer=norm_layer) - self.layer4 = self._make_layer(block, 512, layers[3], stride=2, norm_layer=norm_layer) - self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) - self.fc = nn.Linear(512 * block.expansion, num_classes) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - - # Zero-initialize the last BN in each residual branch, - # so that the residual branch starts with zeros, and each residual block behaves like an identity. - # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 - if zero_init_residual: - for m in self.modules(): - if isinstance(m, Bottleneck): - nn.init.constant_(m.bn3.weight, 0) - elif isinstance(m, BasicBlock): - nn.init.constant_(m.bn2.weight, 0) - - def _make_layer(self, block, planes, blocks, stride=1, norm_layer=None): - if norm_layer is None: - norm_layer = nn.BatchNorm2d - downsample = None - if stride != 1 or self.inplanes != planes * block.expansion: - downsample = nn.Sequential( - conv1x1(self.inplanes, planes * block.expansion, stride), - norm_layer(planes * block.expansion), - ) - - layers = [] - layers.append(block(self.inplanes, planes, stride, downsample, norm_layer)) - self.inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append(block(self.inplanes, planes, norm_layer=norm_layer)) - - return nn.Sequential(*layers) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - - x = self.layer1(x) - x = self.layer2(x) - x = self.layer3(x) - x = self.layer4(x) - - x = self.avgpool(x) - x = x.view(x.size(0), -1) - x = self.fc(x) - - return x - - -def resnet18(pretrained=False, **kwargs): - """Constructs a ResNet-18 model. - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['resnet18'])) - return model - - -def resnet34(pretrained=False, **kwargs): - """Constructs a ResNet-34 model. - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['resnet34'])) - return model - - -def resnet50(pretrained=False, **kwargs): - """Constructs a ResNet-50 model. - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['resnet50'])) - return model - - -def resnet101(pretrained=False, **kwargs): - """Constructs a ResNet-101 model. - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['resnet101'])) - return model - - -def resnet152(pretrained=False, **kwargs): - """Constructs a ResNet-152 model. - - Args: - pretrained (bool): If True, returns a model pre-trained on ImageNet - """ - model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) - if pretrained: - model.load_state_dict(model_zoo.load_url(model_urls['resnet152'])) - return model \ No newline at end of file diff --git a/models/loadmodel.py b/models/loadmodel.py index 974064a..7d9b391 100755 --- a/models/loadmodel.py +++ b/models/loadmodel.py @@ -1,49 +1,35 @@ import torch -from .pix2pix_model import define_G -from .pix2pixHD_model import define_G as define_G_HD -from .unet_model import UNet -from .video_model import MosaicNet -from .videoHD_model import MosaicNet as MosaicNet_HD +from . import model_util +from .pix2pix_model import define_G as pix2pix_G +from .pix2pixHD_model import define_G as pix2pixHD_G +# from .video_model import MosaicNet +# from .videoHD_model import MosaicNet as MosaicNet_HD from .BiSeNet_model import BiSeNet +from .BVDNet import define_G as video_G def show_paramsnumber(net,netname='net'): parameters = sum(param.numel() for param in net.parameters()) parameters = round(parameters/1e6,2) print(netname+' parameters: '+str(parameters)+'M') -def __patch_instance_norm_state_dict(state_dict, module, keys, i=0): - """Fix InstanceNorm checkpoints incompatibility (prior to 0.4)""" - key = keys[i] - if i + 1 == len(keys): # at the end, pointing to a parameter/buffer - if module.__class__.__name__.startswith('InstanceNorm') and \ - (key == 'running_mean' or key == 'running_var'): - if getattr(module, key) is None: - state_dict.pop('.'.join(keys)) - if module.__class__.__name__.startswith('InstanceNorm') and \ - (key == 'num_batches_tracked'): - state_dict.pop('.'.join(keys)) - else: - __patch_instance_norm_state_dict(state_dict, getattr(module, key), keys, i + 1) - def pix2pix(opt): # print(opt.model_path,opt.netG) if opt.netG == 'HD': - netG = define_G_HD(3, 3, 64, 'global' ,4) + netG = pix2pixHD_G(3, 3, 64, 'global' ,4) else: - netG = define_G(3, 3, 64, opt.netG, norm='batch',use_dropout=True, init_type='normal', gpu_ids=[]) + netG = pix2pix_G(3, 3, 64, opt.netG, norm='batch',use_dropout=True, init_type='normal', gpu_ids=[]) show_paramsnumber(netG,'netG') netG.load_state_dict(torch.load(opt.model_path)) + netG = model_util.todevice(netG,opt.gpu_id) netG.eval() - if opt.use_gpu != -1: - netG.cuda() return netG def style(opt): if opt.edges: - netG = define_G(1, 3, 64, 'resnet_9blocks', norm='instance',use_dropout=True, init_type='normal', gpu_ids=[]) + netG = pix2pix_G(1, 3, 64, 'resnet_9blocks', norm='instance',use_dropout=True, init_type='normal', gpu_ids=[]) else: - netG = define_G(3, 3, 64, 'resnet_9blocks', norm='instance',use_dropout=False, init_type='normal', gpu_ids=[]) + netG = pix2pix_G(3, 3, 64, 'resnet_9blocks', norm='instance',use_dropout=False, init_type='normal', gpu_ids=[]) #in other to load old pretrain model #https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/models/base_model.py @@ -57,23 +43,19 @@ def style(opt): # patch InstanceNorm checkpoints prior to 0.4 for key in list(state_dict.keys()): # need to copy keys here because we mutate in loop - __patch_instance_norm_state_dict(state_dict, netG, key.split('.')) + model_util.patch_instance_norm_state_dict(state_dict, netG, key.split('.')) netG.load_state_dict(state_dict) - if opt.use_gpu != -1: - netG.cuda() + netG = model_util.todevice(netG,opt.gpu_id) + netG.eval() return netG def video(opt): - if 'HD' in opt.model_path: - netG = MosaicNet_HD(3*25+1, 3, norm='instance') - else: - netG = MosaicNet(3*25+1, 3,norm = 'batch') + netG = video_G(N=2,n_blocks=4,gpu_id=opt.gpu_id) show_paramsnumber(netG,'netG') netG.load_state_dict(torch.load(opt.model_path)) + netG = model_util.todevice(netG,opt.gpu_id) netG.eval() - if opt.use_gpu != -1: - netG.cuda() return netG def bisenet(opt,type='roi'): @@ -86,7 +68,6 @@ def bisenet(opt,type='roi'): net.load_state_dict(torch.load(opt.model_path)) elif type == 'mosaic': net.load_state_dict(torch.load(opt.mosaic_position_model_path)) + net = model_util.todevice(net,opt.gpu_id) net.eval() - if opt.use_gpu != -1: - net.cuda() return net diff --git a/models/model_util.py b/models/model_util.py new file mode 100644 index 0000000..2aa7f9e --- /dev/null +++ b/models/model_util.py @@ -0,0 +1,469 @@ +import functools +from math import exp + +import torch +import torch.nn as nn +from torch.nn import init +from torch.autograd import Variable +import torch.nn.functional as F +import torch.nn.utils.spectral_norm as SpectralNorm +from torchvision import models +import torch.utils.model_zoo as model_zoo + +################################## IO ################################## +def save(net,path,gpu_id): + if isinstance(net, nn.DataParallel): + torch.save(net.module.cpu().state_dict(),path) + else: + torch.save(net.cpu().state_dict(),path) + if gpu_id != '-1': + net.cuda() + +def todevice(net,gpu_id): + if gpu_id != '-1' and len(gpu_id) == 1: + net.cuda() + elif gpu_id != '-1' and len(gpu_id) > 1: + net = nn.DataParallel(net) + net.cuda() + return net + +# patch InstanceNorm checkpoints prior to 0.4 +def patch_instance_norm_state_dict(state_dict, module, keys, i=0): + """Fix InstanceNorm checkpoints incompatibility (prior to 0.4)""" + key = keys[i] + if i + 1 == len(keys): # at the end, pointing to a parameter/buffer + if module.__class__.__name__.startswith('InstanceNorm') and \ + (key == 'running_mean' or key == 'running_var'): + if getattr(module, key) is None: + state_dict.pop('.'.join(keys)) + if module.__class__.__name__.startswith('InstanceNorm') and \ + (key == 'num_batches_tracked'): + state_dict.pop('.'.join(keys)) + else: + patch_instance_norm_state_dict(state_dict, getattr(module, key), keys, i + 1) + +################################## initialization ################################## +def get_norm_layer(norm_type='instance',mod = '2d'): + if norm_type == 'batch': + if mod == '2d': + norm_layer = functools.partial(nn.BatchNorm2d, affine=True) + elif mod == '3d': + norm_layer = functools.partial(nn.BatchNorm3d, affine=True) + elif norm_type == 'instance': + if mod == '2d': + norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=True) + elif mod =='3d': + norm_layer = functools.partial(nn.InstanceNorm3d, affine=False, track_running_stats=True) + elif norm_type == 'none': + norm_layer = None + else: + raise NotImplementedError('normalization layer [%s] is not found' % norm_type) + + return norm_layer + +def init_weights(net, init_type='normal', gain=0.02): + def init_func(m): + classname = m.__class__.__name__ + if hasattr(m, 'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1): + if init_type == 'normal': + init.normal_(m.weight.data, 0.0, gain) + elif init_type == 'xavier': + init.xavier_normal_(m.weight.data, gain=gain) + elif init_type == 'kaiming': + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + elif init_type == 'orthogonal': + init.orthogonal_(m.weight.data, gain=gain) + else: + raise NotImplementedError('initialization method [%s] is not implemented' % init_type) + if hasattr(m, 'bias') and m.bias is not None: + init.constant_(m.bias.data, 0.0) + elif classname.find('BatchNorm2d') != -1: + init.normal_(m.weight.data, 1.0, gain) + init.constant_(m.bias.data, 0.0) + + # print('initialize network with %s' % init_type) + net.apply(init_func) + +################################## Network structure ################################## +################################## ResnetBlock ################################## +class ResnetBlockSpectralNorm(nn.Module): + def __init__(self, dim, padding_type, activation=nn.LeakyReLU(0.2), use_dropout=False): + super(ResnetBlockSpectralNorm, self).__init__() + self.conv_block = self.build_conv_block(dim, padding_type, activation, use_dropout) + + def build_conv_block(self, dim, padding_type, activation, use_dropout): + conv_block = [] + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + + conv_block += [SpectralNorm(nn.Conv2d(dim, dim, kernel_size=3, padding=p)), + activation] + if use_dropout: + conv_block += [nn.Dropout(0.5)] + + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + conv_block += [SpectralNorm(nn.Conv2d(dim, dim, kernel_size=3, padding=p))] + + return nn.Sequential(*conv_block) + + def forward(self, x): + out = x + self.conv_block(x) + return out + +################################## Resnet ################################## +model_urls = { + 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', + 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', + 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', + 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', + 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', +} + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, norm_layer=None): + super(BasicBlock, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = norm_layer(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = norm_layer(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + +class Bottleneck(nn.Module): + expansion = 4 + def __init__(self, inplanes, planes, stride=1, downsample=None, norm_layer=None): + super(Bottleneck, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv1x1(inplanes, planes) + self.bn1 = norm_layer(planes) + self.conv2 = conv3x3(planes, planes, stride) + self.bn2 = norm_layer(planes) + self.conv3 = conv1x1(planes, planes * self.expansion) + self.bn3 = norm_layer(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000, zero_init_residual=False, norm_layer=None): + super(ResNet, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + self.inplanes = 64 + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = norm_layer(64) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0], norm_layer=norm_layer) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2, norm_layer=norm_layer) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2, norm_layer=norm_layer) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2, norm_layer=norm_layer) + self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + # Zero-initialize the last BN in each residual branch, + # so that the residual branch starts with zeros, and each residual block behaves like an identity. + # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 + if zero_init_residual: + for m in self.modules(): + if isinstance(m, Bottleneck): + nn.init.constant_(m.bn3.weight, 0) + elif isinstance(m, BasicBlock): + nn.init.constant_(m.bn2.weight, 0) + + def _make_layer(self, block, planes, blocks, stride=1, norm_layer=None): + if norm_layer is None: + norm_layer = nn.BatchNorm2d + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes * block.expansion, stride), + norm_layer(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample, norm_layer)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes, norm_layer=norm_layer)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + x = self.avgpool(x) + x = x.view(x.size(0), -1) + x = self.fc(x) + + return x + +def resnet18(pretrained=False, **kwargs): + """Constructs a ResNet-18 model. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url(model_urls['resnet18'])) + return model + +def resnet101(pretrained=False, **kwargs): + """Constructs a ResNet-101 model. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + if pretrained: + model.load_state_dict(model_zoo.load_url(model_urls['resnet101'])) + return model + +################################## Loss function ################################## +class HingeLossD(nn.Module): + def __init__(self): + super(HingeLossD, self).__init__() + + def forward(self, dis_fake, dis_real): + loss_real = torch.mean(F.relu(1. - dis_real)) + loss_fake = torch.mean(F.relu(1. + dis_fake)) + return loss_real + loss_fake + +class HingeLossG(nn.Module): + def __init__(self): + super(HingeLossG, self).__init__() + + def forward(self, dis_fake): + loss_fake = -torch.mean(dis_fake) + return loss_fake + +class VGGLoss(nn.Module): + def __init__(self, gpu_id): + super(VGGLoss, self).__init__() + + self.vgg = Vgg19() + if gpu_id != '-1' and len(gpu_id) == 1: + self.vgg.cuda() + elif gpu_id != '-1' and len(gpu_id) > 1: + self.vgg = nn.DataParallel(self.vgg) + self.vgg.cuda() + + self.criterion = nn.MSELoss() + self.weights = [1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0] + + def forward(self, x, y): + x_vgg, y_vgg = self.vgg(x), self.vgg(y) + loss = 0 + for i in range(len(x_vgg)): + loss += self.weights[i] * self.criterion(x_vgg[i], y_vgg[i].detach()) + return loss + +class Vgg19(torch.nn.Module): + def __init__(self, requires_grad=False): + super(Vgg19, self).__init__() + vgg_pretrained_features = models.vgg19(pretrained=True).features + self.slice1 = torch.nn.Sequential() + self.slice2 = torch.nn.Sequential() + self.slice3 = torch.nn.Sequential() + self.slice4 = torch.nn.Sequential() + self.slice5 = torch.nn.Sequential() + for x in range(2): + self.slice1.add_module(str(x), vgg_pretrained_features[x]) + for x in range(2, 7): + self.slice2.add_module(str(x), vgg_pretrained_features[x]) + for x in range(7, 12): + self.slice3.add_module(str(x), vgg_pretrained_features[x]) + for x in range(12, 21): + self.slice4.add_module(str(x), vgg_pretrained_features[x]) + for x in range(21, 30): + self.slice5.add_module(str(x), vgg_pretrained_features[x]) + if not requires_grad: + for param in self.parameters(): + param.requires_grad = False + + def forward(self, X): + h_relu1 = self.slice1(X) + h_relu2 = self.slice2(h_relu1) + h_relu3 = self.slice3(h_relu2) + h_relu4 = self.slice4(h_relu3) + h_relu5 = self.slice5(h_relu4) + out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5] + return out + +################################## Evaluation ################################## +'''https://github.com/Po-Hsun-Su/pytorch-ssim + +img1 = Variable(torch.rand(1, 1, 256, 256)) +img2 = Variable(torch.rand(1, 1, 256, 256)) + +if torch.cuda.is_available(): + img1 = img1.cuda() + img2 = img2.cuda() + +print(pytorch_ssim.ssim(img1, img2)) + +ssim_loss = pytorch_ssim.SSIM(window_size = 11) + +print(ssim_loss(img1, img2)) +''' + +def gaussian(window_size, sigma): + gauss = torch.Tensor([exp(-(x - window_size//2)**2/float(2*sigma**2)) for x in range(window_size)]) + return gauss/gauss.sum() + +def create_window(window_size, channel): + _1D_window = gaussian(window_size, 1.5).unsqueeze(1) + _2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0) + window = Variable(_2D_window.expand(channel, 1, window_size, window_size).contiguous()) + return window + +def _ssim(img1, img2, window, window_size, channel, size_average = True): + mu1 = F.conv2d(img1, window, padding = window_size//2, groups = channel) + mu2 = F.conv2d(img2, window, padding = window_size//2, groups = channel) + + mu1_sq = mu1.pow(2) + mu2_sq = mu2.pow(2) + mu1_mu2 = mu1*mu2 + + sigma1_sq = F.conv2d(img1*img1, window, padding = window_size//2, groups = channel) - mu1_sq + sigma2_sq = F.conv2d(img2*img2, window, padding = window_size//2, groups = channel) - mu2_sq + sigma12 = F.conv2d(img1*img2, window, padding = window_size//2, groups = channel) - mu1_mu2 + + C1 = 0.01**2 + C2 = 0.03**2 + + ssim_map = ((2*mu1_mu2 + C1)*(2*sigma12 + C2))/((mu1_sq + mu2_sq + C1)*(sigma1_sq + sigma2_sq + C2)) + + if size_average: + return ssim_map.mean() + else: + return ssim_map.mean(1).mean(1).mean(1) + +class SSIM(torch.nn.Module): + def __init__(self, window_size = 11, size_average = True): + super(SSIM, self).__init__() + self.window_size = window_size + self.size_average = size_average + self.channel = 1 + self.window = create_window(window_size, self.channel) + + def forward(self, img1, img2): + (_, channel, _, _) = img1.size() + + if channel == self.channel and self.window.data.type() == img1.data.type(): + window = self.window + else: + window = create_window(self.window_size, channel) + + if img1.is_cuda: + window = window.cuda(img1.get_device()) + window = window.type_as(img1) + + self.window = window + self.channel = channel + + + return _ssim(img1, img2, window, self.window_size, channel, self.size_average) + +def ssim(img1, img2, window_size = 11, size_average = True): + (_, channel, _, _) = img1.size() + window = create_window(window_size, channel) + + if img1.is_cuda: + window = window.cuda(img1.get_device()) + window = window.type_as(img1) + + return _ssim(img1, img2, window, window_size, channel, size_average) diff --git a/models/runmodel.py b/models/runmodel.py index 2bdc88d..3e97fee 100755 --- a/models/runmodel.py +++ b/models/runmodel.py @@ -7,11 +7,11 @@ import torch import numpy as np -def run_segment(img,net,size = 360,use_gpu = 0): +def run_segment(img,net,size = 360,gpu_id = '-1'): img = impro.resize(img,size) - img = data.im2tensor(img,use_gpu = use_gpu, bgr2rgb = False,use_transform = False , is0_1 = True) + img = data.im2tensor(img,gpu_id = gpu_id, bgr2rgb = False, is0_1 = True) mask = net(img) - mask = data.tensor2im(mask, gray=True,rgb2bgr = False, is0_1 = True) + mask = data.tensor2im(mask, gray=True, is0_1 = True) return mask def run_pix2pix(img,net,opt): @@ -19,7 +19,7 @@ def run_pix2pix(img,net,opt): img = impro.resize(img,512) else: img = impro.resize(img,128) - img = data.im2tensor(img,use_gpu=opt.use_gpu) + img = data.im2tensor(img,gpu_id=opt.gpu_id) img_fake = net(img) img_fake = data.tensor2im(img_fake) return img_fake @@ -50,18 +50,18 @@ def run_styletransfer(opt, net, img): else: canny_low = opt.canny-int(opt.canny/2) canny_high = opt.canny+int(opt.canny/2) - img = cv2.Canny(img,opt.canny-50,opt.canny+50) + img = cv2.Canny(img,canny_low,canny_high) if opt.only_edges: return img - img = data.im2tensor(img,use_gpu=opt.use_gpu,gray=True,use_transform = False,is0_1 = False) + img = data.im2tensor(img,gpu_id=opt.gpu_id,gray=True) else: - img = data.im2tensor(img,use_gpu=opt.use_gpu,gray=False,use_transform = True) + img = data.im2tensor(img,gpu_id=opt.gpu_id) img = net(img) img = data.tensor2im(img) return img def get_ROI_position(img,net,opt,keepsize=True): - mask = run_segment(img,net,size=360,use_gpu = opt.use_gpu) + mask = run_segment(img,net,size=360,gpu_id = opt.gpu_id) mask = impro.mask_threshold(mask,opt.mask_extend,opt.mask_threshold) if keepsize: mask = impro.resize_like(mask, img) @@ -70,7 +70,7 @@ def get_ROI_position(img,net,opt,keepsize=True): def get_mosaic_position(img_origin,net_mosaic_pos,opt): h,w = img_origin.shape[:2] - mask = run_segment(img_origin,net_mosaic_pos,size=360,use_gpu = opt.use_gpu) + mask = run_segment(img_origin,net_mosaic_pos,size=360,gpu_id = opt.gpu_id) # mask_1 = mask.copy() mask = impro.mask_threshold(mask,ex_mun=int(min(h,w)/20),threshold=opt.mask_threshold) if not opt.all_mosaic_area: diff --git a/models/videoHD_model.py b/models/videoHD_model.py deleted file mode 100644 index 20e901f..0000000 --- a/models/videoHD_model.py +++ /dev/null @@ -1,173 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -from .pix2pixHD_model import * - - -class encoder_2d(nn.Module): - def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_blocks=9, norm_layer=nn.BatchNorm2d, - padding_type='reflect'): - assert(n_blocks >= 0) - super(encoder_2d, self).__init__() - activation = nn.ReLU(True) - - model = [nn.ReflectionPad2d(3), nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0), norm_layer(ngf), activation] - ### downsample - for i in range(n_downsampling): - mult = 2**i - model += [nn.ReflectionPad2d(1),nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=0), - norm_layer(ngf * mult * 2), activation] - - self.model = nn.Sequential(*model) - def forward(self, input): - return self.model(input) - -class decoder_2d(nn.Module): - def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_blocks=9, norm_layer=nn.BatchNorm2d, - padding_type='reflect'): - assert(n_blocks >= 0) - super(decoder_2d, self).__init__() - activation = nn.ReLU(True) - - model = [] - - ### resnet blocks - mult = 2**n_downsampling - for i in range(n_blocks): - model += [ResnetBlock(ngf * mult, padding_type=padding_type, activation=activation, norm_layer=norm_layer)] - - ### upsample - for i in range(n_downsampling): - mult = 2**(n_downsampling - i) - - # model += [ nn.Upsample(scale_factor = 2, mode='nearest'), - # nn.ReflectionPad2d(1), - # nn.Conv2d(ngf * mult, int(ngf * mult / 2),kernel_size=3, stride=1, padding=0), - # norm_layer(int(ngf * mult / 2)), - # nn.ReLU(True)] - model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=2, padding=1, output_padding=1), - norm_layer(int(ngf * mult / 2)), activation] - model += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0), nn.Tanh()] - self.model = nn.Sequential(*model) - - def forward(self, input): - return self.model(input) - - -class conv_3d(nn.Module): - def __init__(self,inchannel,outchannel,kernel_size=3,stride=2,padding=1,norm_layer_3d=nn.BatchNorm3d,use_bias=True): - super(conv_3d, self).__init__() - self.conv = nn.Sequential( - nn.Conv3d(inchannel, outchannel, kernel_size=kernel_size, stride=stride, padding=padding, bias=use_bias), - norm_layer_3d(outchannel), - nn.ReLU(inplace=True), - ) - - def forward(self, x): - x = self.conv(x) - return x - -class conv_2d(nn.Module): - def __init__(self,inchannel,outchannel,kernel_size=3,stride=1,padding=1,norm_layer_2d=nn.BatchNorm2d,use_bias=True): - super(conv_2d, self).__init__() - self.conv = nn.Sequential( - nn.ReflectionPad2d(padding), - nn.Conv2d(inchannel, outchannel, kernel_size=kernel_size, stride=stride, padding=0, bias=use_bias), - norm_layer_2d(outchannel), - nn.ReLU(inplace=True), - ) - - def forward(self, x): - x = self.conv(x) - return x - - -class encoder_3d(nn.Module): - def __init__(self,in_channel,norm_layer_2d,norm_layer_3d,use_bias): - super(encoder_3d, self).__init__() - self.inconv = conv_3d(1, 64, 7, 2, 3,norm_layer_3d,use_bias) - self.down1 = conv_3d(64, 128, 3, 2, 1,norm_layer_3d,use_bias) - self.down2 = conv_3d(128, 256, 3, 2, 1,norm_layer_3d,use_bias) - self.down3 = conv_3d(256, 512, 3, 2, 1,norm_layer_3d,use_bias) - self.down4 = conv_3d(512, 1024, 3, 1, 1,norm_layer_3d,use_bias) - self.pool = nn.AvgPool3d((5,1,1)) - # self.conver2d = nn.Sequential( - # nn.Conv2d(256*int(in_channel/4), 256, kernel_size=3, stride=1, padding=1, bias=use_bias), - # norm_layer_2d(256), - # nn.ReLU(inplace=True), - # ) - - - def forward(self, x): - - x = x.view(x.size(0),1,x.size(1),x.size(2),x.size(3)) - x = self.inconv(x) - x = self.down1(x) - x = self.down2(x) - x = self.down3(x) - x = self.down4(x) - #print(x.size()) - x = self.pool(x) - #print(x.size()) - # torch.Size([1, 1024, 16, 16]) - # torch.Size([1, 512, 5, 16, 16]) - - - x = x.view(x.size(0),x.size(1),x.size(3),x.size(4)) - - # x = self.conver2d(x) - - return x - - # def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_blocks=9, norm_layer=nn.BatchNorm2d, - # padding_type='reflect') - -class ALL(nn.Module): - def __init__(self, in_channel, out_channel,norm_layer_2d,norm_layer_3d,use_bias): - super(ALL, self).__init__() - - self.encoder_2d = encoder_2d(4,3,64,4,norm_layer=norm_layer_2d,padding_type='reflect') - self.encoder_3d = encoder_3d(in_channel,norm_layer_2d,norm_layer_3d,use_bias) - self.decoder_2d = decoder_2d(4,3,64,4,norm_layer=norm_layer_2d,padding_type='reflect') - # self.shortcut_cov = conv_2d(3,64,7,1,3,norm_layer_2d,use_bias) - self.merge1 = conv_2d(2048,1024,3,1,1,norm_layer_2d,use_bias) - # self.merge2 = nn.Sequential( - # conv_2d(128,64,3,1,1,norm_layer_2d,use_bias), - # nn.ReflectionPad2d(3), - # nn.Conv2d(64, out_channel, kernel_size=7, padding=0), - # nn.Tanh() - # ) - - def forward(self, x): - - N = int((x.size()[1])/3) - x_2d = torch.cat((x[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:], x[:,N-1:N,:,:]), 1) - #shortcut_2d = x[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:] - - x_2d = self.encoder_2d(x_2d) - x_3d = self.encoder_3d(x) - #x = x_2d + x_3d - x = torch.cat((x_2d,x_3d),1) - x = self.merge1(x) - - x = self.decoder_2d(x) - #shortcut_2d = self.shortcut_cov(shortcut_2d) - #x = torch.cat((x,shortcut_2d),1) - #x = self.merge2(x) - - return x - -def MosaicNet(in_channel, out_channel, norm='batch'): - - if norm == 'batch': - # norm_layer_2d = nn.BatchNorm2d - # norm_layer_3d = nn.BatchNorm3d - norm_layer_2d = functools.partial(nn.BatchNorm2d, affine=True, track_running_stats=True) - norm_layer_3d = functools.partial(nn.BatchNorm3d, affine=True, track_running_stats=True) - use_bias = False - elif norm == 'instance': - norm_layer_2d = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) - norm_layer_3d = functools.partial(nn.InstanceNorm3d, affine=False, track_running_stats=False) - use_bias = True - - return ALL(in_channel, out_channel, norm_layer_2d, norm_layer_3d, use_bias) diff --git a/models/video_model.py b/models/video_model.py deleted file mode 100644 index 4a095c6..0000000 --- a/models/video_model.py +++ /dev/null @@ -1,216 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -from .pix2pix_model import * - - -class encoder_2d(nn.Module): - """Resnet-based generator that consists of Resnet blocks between a few downsampling/upsampling operations. - - We adapt Torch code and idea from Justin Johnson's neural style transfer project(https://github.com/jcjohnson/fast-neural-style) - """ - - def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, padding_type='reflect'): - """Construct a Resnet-based generator - - Parameters: - input_nc (int) -- the number of channels in input images - output_nc (int) -- the number of channels in output images - ngf (int) -- the number of filters in the last conv layer - norm_layer -- normalization layer - use_dropout (bool) -- if use dropout layers - n_blocks (int) -- the number of ResNet blocks - padding_type (str) -- the name of padding layer in conv layers: reflect | replicate | zero - """ - assert(n_blocks >= 0) - super(encoder_2d, self).__init__() - if type(norm_layer) == functools.partial: - use_bias = norm_layer.func == nn.InstanceNorm2d - else: - use_bias = norm_layer == nn.InstanceNorm2d - - model = [nn.ReflectionPad2d(3), - nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0, bias=use_bias), - norm_layer(ngf), - nn.ReLU(True)] - - n_downsampling = 2 - for i in range(n_downsampling): # add downsampling layers - mult = 2 ** i - model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1, bias=use_bias), - norm_layer(ngf * mult * 2), - nn.ReLU(True)] - #torch.Size([1, 256, 32, 32]) - - self.model = nn.Sequential(*model) - - def forward(self, input): - """Standard forward""" - return self.model(input) - - -class decoder_2d(nn.Module): - """Resnet-based generator that consists of Resnet blocks between a few downsampling/upsampling operations. - - We adapt Torch code and idea from Justin Johnson's neural style transfer project(https://github.com/jcjohnson/fast-neural-style) - """ - - def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, padding_type='reflect'): - """Construct a Resnet-based generator - - Parameters: - input_nc (int) -- the number of channels in input images - output_nc (int) -- the number of channels in output images - ngf (int) -- the number of filters in the last conv layer - norm_layer -- normalization layer - use_dropout (bool) -- if use dropout layers - n_blocks (int) -- the number of ResNet blocks - padding_type (str) -- the name of padding layer in conv layers: reflect | replicate | zero - """ - super(decoder_2d, self).__init__() - if type(norm_layer) == functools.partial: - use_bias = norm_layer.func == nn.InstanceNorm2d - else: - use_bias = norm_layer == nn.InstanceNorm2d - - model = [] - - n_downsampling = 2 - mult = 2 ** n_downsampling - for i in range(n_blocks): # add ResNet blocks - model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout, use_bias=use_bias)] - #torch.Size([1, 256, 32, 32]) - - for i in range(n_downsampling): # add upsampling layers - mult = 2 ** (n_downsampling - i) - # model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), - # kernel_size=3, stride=2, - # padding=1, output_padding=1, - # bias=use_bias), - # norm_layer(int(ngf * mult / 2)), - # nn.ReLU(True)] - #https://distill.pub/2016/deconv-checkerboard/ - #https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/issues/190 - - model += [ nn.Upsample(scale_factor = 2, mode='nearest'), - nn.ReflectionPad2d(1), - nn.Conv2d(ngf * mult, int(ngf * mult / 2),kernel_size=3, stride=1, padding=0), - norm_layer(int(ngf * mult / 2)), - nn.ReLU(True)] - # model += [nn.ReflectionPad2d(3)] - # model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)] - # model += [nn.Tanh()] - # model += [nn.Sigmoid()] - - self.model = nn.Sequential(*model) - - def forward(self, input): - """Standard forward""" - return self.model(input) - - - -class conv_3d(nn.Module): - def __init__(self,inchannel,outchannel,kernel_size=3,stride=2,padding=1,norm_layer_3d=nn.BatchNorm3d,use_bias=True): - super(conv_3d, self).__init__() - self.conv = nn.Sequential( - nn.Conv3d(inchannel, outchannel, kernel_size=kernel_size, stride=stride, padding=padding, bias=use_bias), - norm_layer_3d(outchannel), - nn.ReLU(inplace=True), - ) - - def forward(self, x): - x = self.conv(x) - return x - -class conv_2d(nn.Module): - def __init__(self,inchannel,outchannel,kernel_size=3,stride=1,padding=1,norm_layer_2d=nn.BatchNorm2d,use_bias=True): - super(conv_2d, self).__init__() - self.conv = nn.Sequential( - nn.ReflectionPad2d(padding), - nn.Conv2d(inchannel, outchannel, kernel_size=kernel_size, stride=stride, padding=0, bias=use_bias), - norm_layer_2d(outchannel), - nn.ReLU(inplace=True), - ) - - def forward(self, x): - x = self.conv(x) - return x - - -class encoder_3d(nn.Module): - def __init__(self,in_channel,norm_layer_2d,norm_layer_3d,use_bias): - super(encoder_3d, self).__init__() - self.down1 = conv_3d(1, 64, 3, 2, 1,norm_layer_3d,use_bias) - self.down2 = conv_3d(64, 128, 3, 2, 1,norm_layer_3d,use_bias) - self.down3 = conv_3d(128, 256, 3, 1, 1,norm_layer_3d,use_bias) - self.conver2d = nn.Sequential( - nn.Conv2d(256*int(in_channel/4), 256, kernel_size=3, stride=1, padding=1, bias=use_bias), - norm_layer_2d(256), - nn.ReLU(inplace=True), - ) - - - def forward(self, x): - - x = x.view(x.size(0),1,x.size(1),x.size(2),x.size(3)) - x = self.down1(x) - x = self.down2(x) - x = self.down3(x) - - x = x.view(x.size(0),x.size(1)*x.size(2),x.size(3),x.size(4)) - - x = self.conver2d(x) - - return x - - - -class ALL(nn.Module): - def __init__(self, in_channel, out_channel,norm_layer_2d,norm_layer_3d,use_bias): - super(ALL, self).__init__() - - self.encoder_2d = encoder_2d(4,-1,64,norm_layer=norm_layer_2d,n_blocks=9) - self.encoder_3d = encoder_3d(in_channel,norm_layer_2d,norm_layer_3d,use_bias) - self.decoder_2d = decoder_2d(4,3,64,norm_layer=norm_layer_2d,n_blocks=9) - self.shortcut_cov = conv_2d(3,64,7,1,3,norm_layer_2d,use_bias) - self.merge1 = conv_2d(512,256,3,1,1,norm_layer_2d,use_bias) - self.merge2 = nn.Sequential( - conv_2d(128,64,3,1,1,norm_layer_2d,use_bias), - nn.ReflectionPad2d(3), - nn.Conv2d(64, out_channel, kernel_size=7, padding=0), - nn.Tanh() - ) - - def forward(self, x): - - N = int((x.size()[1])/3) - x_2d = torch.cat((x[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:], x[:,N-1:N,:,:]), 1) - shortcut_2d = x[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:] - - x_2d = self.encoder_2d(x_2d) - - x_3d = self.encoder_3d(x) - x = torch.cat((x_2d,x_3d),1) - x = self.merge1(x) - x = self.decoder_2d(x) - shortcut_2d = self.shortcut_cov(shortcut_2d) - x = torch.cat((x,shortcut_2d),1) - x = self.merge2(x) - - return x - -def MosaicNet(in_channel, out_channel, norm='batch'): - - if norm == 'batch': - # norm_layer_2d = nn.BatchNorm2d - # norm_layer_3d = nn.BatchNorm3d - norm_layer_2d = functools.partial(nn.BatchNorm2d, affine=True, track_running_stats=True) - norm_layer_3d = functools.partial(nn.BatchNorm3d, affine=True, track_running_stats=True) - use_bias = False - elif norm == 'instance': - norm_layer_2d = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) - norm_layer_3d = functools.partial(nn.InstanceNorm3d, affine=False, track_running_stats=False) - use_bias = True - - return ALL(in_channel, out_channel, norm_layer_2d, norm_layer_3d, use_bias) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..23c0f2c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +opencv_python==4.5.1.48 +numpy==1.19.2 +torchvision==0.8.2 +torch==1.7.1 +matplotlib==3.3.2 +tensorboardX==2.2 +scikit-image==0.17.2 \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..f76ca1c --- /dev/null +++ b/server.py @@ -0,0 +1,60 @@ +import os +import sys +import traceback +import cv2 +import numpy as np +try: + from cores import Options,core + from util import util + from util import image_processing as impro + from models import loadmodel +except Exception as e: + print(e) + input('Please press any key to exit.\n') + sys.exit(0) + +# python server.py --gpu_id 0 --model_path ./pretrained_models/mosaic/clean_face_HD.pth +opt = Options() +opt.parser.add_argument('--port',type=int,default=4000, help='') +opt = opt.getparse(True) +netM = loadmodel.bisenet(opt,'mosaic') +netG = loadmodel.pix2pix(opt) + +from flask import Flask, request +import base64 +import shutil + +app = Flask(__name__) + +@app.route("/handle", methods=["POST"]) +def handle(): + result = {} + # to opencv img + try: + imgRec = request.form['img'] + imgByte = base64.b64decode(imgRec) + img_np_arr = np.frombuffer(imgByte, np.uint8) + img = cv2.imdecode(img_np_arr, cv2.IMREAD_COLOR) + except Exception as e: + result['img'] = imgRec + result['info'] = 'readfailed' + return result + + # run model + try: + if max(img.shape)>1080: + img = impro.resize(img,720,interpolation=cv2.INTER_CUBIC) + img = core.cleanmosaic_img_server(opt,img,netG,netM) + except Exception as e: + result['img'] = imgRec + result['info'] = 'procfailed' + return result + + # return + imgbytes = cv2.imencode('.jpg', img)[1] + imgString = base64.b64encode(imgbytes).decode('utf-8') + result['img'] = imgString + result['info'] = 'ok' + return result + +app.run("0.0.0.0", port= opt.port, debug=opt.debug) \ No newline at end of file diff --git a/train/add/train.py b/train/add/train.py index d64063e..a939ee3 100644 --- a/train/add/train.py +++ b/train/add/train.py @@ -54,10 +54,10 @@ util.writelog(os.path.join(dir_checkpoint,'loss.txt'), str(time.asctime(time.localtime(time.time())))+'\n'+util.opt2str(opt)) -def Totensor(img,use_gpu=True): +def Totensor(img,gpu_id=True): size=img.shape[0] img = torch.from_numpy(img).float() - if opt.use_gpu != -1: + if opt.gpu_id != -1: img = img.cuda() return img @@ -68,11 +68,11 @@ def loadimage(imagepaths,maskpaths,opt,test_flag = False): for i in range(len(imagepaths)): img = impro.resize(impro.imread(imagepaths[i]),opt.loadsize) mask = impro.resize(impro.imread(maskpaths[i],mod = 'gray'),opt.loadsize) - img,mask = data.random_transform_image(img, mask, opt.finesize, test_flag) + img,mask = data.random_transform_pair_image(img, mask, opt.finesize, test_flag) images[i] = (img.transpose((2, 0, 1))/255.0) masks[i] = (mask.reshape(1,1,opt.finesize,opt.finesize)/255.0) - images = Totensor(images,opt.use_gpu) - masks = Totensor(masks,opt.use_gpu) + images = data.to_tensor(images,opt.gpu_id) + masks = data.to_tensor(masks,opt.gpu_id) return images,masks @@ -111,7 +111,7 @@ def loadimage(imagepaths,maskpaths,opt,test_flag = False): f = open(os.path.join(dir_checkpoint,'epoch_log.txt'),'r') opt.startepoch = int(f.read()) f.close() -if opt.use_gpu != -1: +if opt.gpu_id != -1: net.cuda() cudnn.benchmark = True @@ -135,7 +135,7 @@ def loadimage(imagepaths,maskpaths,opt,test_flag = False): starttime = datetime.datetime.now() util.writelog(os.path.join(dir_checkpoint,'loss.txt'),'Epoch {}/{}.'.format(epoch + 1, opt.maxepoch),True) net.train() - if opt.use_gpu != -1: + if opt.gpu_id != -1: net.cuda() epoch_loss = 0 for i in range(int(img_num*0.8/opt.batchsize)): diff --git a/train/clean/train.py b/train/clean/train.py index 70efb41..6ea23b7 100644 --- a/train/clean/train.py +++ b/train/clean/train.py @@ -1,310 +1,241 @@ -import os -import sys -sys.path.append("..") -sys.path.append("../..") -from cores import Options -opt = Options() - -import numpy as np -import cv2 -import random -import torch -import torch.nn as nn -import time -from multiprocessing import Process, Queue - -from util import mosaic,util,ffmpeg,filt,data -from util import image_processing as impro -from models import pix2pix_model,pix2pixHD_model,video_model,unet_model,loadmodel,videoHD_model -import matplotlib -matplotlib.use('Agg') -from matplotlib import pyplot as plt -import torch.backends.cudnn as cudnn - -''' ---------------------------Get options-------------------------- -''' -opt.parser.add_argument('--N',type=int,default=25, help='') -opt.parser.add_argument('--lr',type=float,default=0.0002, help='') -opt.parser.add_argument('--beta1',type=float,default=0.5, help='') -opt.parser.add_argument('--gan', action='store_true', help='if specified, use gan') -opt.parser.add_argument('--l2', action='store_true', help='if specified, use L2 loss') -opt.parser.add_argument('--hd', action='store_true', help='if specified, use HD model') -opt.parser.add_argument('--lambda_L1',type=float,default=100, help='') -opt.parser.add_argument('--lambda_gan',type=float,default=1, help='') -opt.parser.add_argument('--finesize',type=int,default=256, help='') -opt.parser.add_argument('--loadsize',type=int,default=286, help='') -opt.parser.add_argument('--batchsize',type=int,default=1, help='') -opt.parser.add_argument('--norm',type=str,default='instance', help='') -opt.parser.add_argument('--num_D', type=int, default=2, help='number of discriminators to use') -opt.parser.add_argument('--n_layers_D', type=int, default=3, help='only used if which_model_netD==n_layers') -opt.parser.add_argument('--lambda_feat', type=float, default=10.0, help='weight for feature matching loss') -opt.parser.add_argument('--image_pool',type=int,default=8, help='number of image load pool') -opt.parser.add_argument('--load_process',type=int,default=4, help='number of process for loading data') - -opt.parser.add_argument('--dataset',type=str,default='./datasets/face/', help='') -opt.parser.add_argument('--maxiter',type=int,default=10000000, help='') -opt.parser.add_argument('--savefreq',type=int,default=10000, help='') -opt.parser.add_argument('--startiter',type=int,default=0, help='') -opt.parser.add_argument('--continue_train', action='store_true', help='') -opt.parser.add_argument('--savename',type=str,default='face', help='') - - -''' ---------------------------Init-------------------------- -''' -opt = opt.getparse() -dir_checkpoint = os.path.join('checkpoints/',opt.savename) -util.makedirs(dir_checkpoint) -util.writelog(os.path.join(dir_checkpoint,'loss.txt'), - str(time.asctime(time.localtime(time.time())))+'\n'+util.opt2str(opt)) -cudnn.benchmark = True - -N = opt.N -loss_sum = [0.,0.,0.,0.,0.,0] -loss_plot = [[],[],[],[]] -item_plot = [] - -# list video dir -videonames = os.listdir(opt.dataset) -videonames.sort() -lengths = [];tmp = [] -print('Check dataset...') -for video in videonames: - if video != 'opt.txt': - video_images = os.listdir(os.path.join(opt.dataset,video,'origin_image')) - lengths.append(len(video_images)) - tmp.append(video) -videonames = tmp -video_num = len(videonames) - -#--------------------------Init network-------------------------- -print('Init network...') -if opt.hd: - netG = videoHD_model.MosaicNet(3*N+1, 3, norm=opt.norm) -else: - netG = video_model.MosaicNet(3*N+1, 3, norm=opt.norm) -netG.cuda() -loadmodel.show_paramsnumber(netG,'netG') - -if opt.gan: - if opt.hd: - netD = pix2pixHD_model.define_D(6, 64, opt.n_layers_D, norm = opt.norm, use_sigmoid=False, num_D=opt.num_D,getIntermFeat=True) - else: - netD = pix2pix_model.define_D(3*2, 64, 'basic', norm = opt.norm) - netD.cuda() - netD.train() - -#--------------------------continue train-------------------------- -if opt.continue_train: - if not os.path.isfile(os.path.join(dir_checkpoint,'last_G.pth')): - opt.continue_train = False - print('can not load last_G, training on init weight.') -if opt.continue_train: - netG.load_state_dict(torch.load(os.path.join(dir_checkpoint,'last_G.pth'))) - if opt.gan: - netD.load_state_dict(torch.load(os.path.join(dir_checkpoint,'last_D.pth'))) - f = open(os.path.join(dir_checkpoint,'iter'),'r') - opt.startiter = int(f.read()) - f.close() - -#--------------------------optimizer & loss-------------------------- -optimizer_G = torch.optim.Adam(netG.parameters(), lr=opt.lr,betas=(opt.beta1, 0.999)) -criterion_L1 = nn.L1Loss() -criterion_L2 = nn.MSELoss() -if opt.gan: - optimizer_D = torch.optim.Adam(netD.parameters(), lr=opt.lr,betas=(opt.beta1, 0.999)) - if opt.hd: - criterionGAN = pix2pixHD_model.GANLoss(tensor=torch.cuda.FloatTensor).cuda() - criterionFeat = pix2pixHD_model.GAN_Feat_loss(opt) - criterionVGG = pix2pixHD_model.VGGLoss([opt.use_gpu]) - else: - criterionGAN = pix2pix_model.GANLoss(gan_mode='lsgan').cuda() - -''' ---------------------------preload data & data pool-------------------------- -''' -print('Preloading data, please wait...') -def preload(pool): - cnt = 0 - input_imgs = torch.rand(opt.batchsize,N*3+1,opt.finesize,opt.finesize) - ground_trues = torch.rand(opt.batchsize,3,opt.finesize,opt.finesize) - while 1: - try: - for i in range(opt.batchsize): - video_index = random.randint(0,video_num-1) - videoname = videonames[video_index] - img_index = random.randint(int(N/2)+1,lengths[video_index]- int(N/2)-1) - input_imgs[i],ground_trues[i] = data.load_train_video(videoname,img_index,opt) - cnt += 1 - pool.put([input_imgs,ground_trues]) - except Exception as e: - print("Error:",videoname,e) -pool = Queue(opt.image_pool) -for i in range(opt.load_process): - p = Process(target=preload,args=(pool,)) - p.daemon = True - p.start() - -''' ---------------------------train-------------------------- -''' -util.copyfile('./train.py', os.path.join(dir_checkpoint,'train.py')) -util.copyfile('../../models/videoHD_model.py', os.path.join(dir_checkpoint,'model.py')) -netG.train() -time_start=time.time() -print("Begin training...") -for iter in range(opt.startiter+1,opt.maxiter): - - inputdata,target = pool.get() - inputdata,target = inputdata.cuda(),target.cuda() - - if opt.gan: - # compute fake images: G(A) - pred = netG(inputdata) - real_A = inputdata[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:] - - # --------------------update D-------------------- - pix2pix_model.set_requires_grad(netD,True) - optimizer_D.zero_grad() - # Fake - fake_AB = torch.cat((real_A, pred), 1) - pred_fake = netD(fake_AB.detach()) - loss_D_fake = criterionGAN(pred_fake, False) - # Real - real_AB = torch.cat((real_A, target), 1) - pred_real = netD(real_AB) - loss_D_real = criterionGAN(pred_real, True) - # combine loss and calculate gradients - loss_D = (loss_D_fake + loss_D_real) * 0.5 - loss_sum[4] += loss_D_fake.item() - loss_sum[5] += loss_D_real.item() - # udpate D's weights - loss_D.backward() - optimizer_D.step() - - # --------------------update G-------------------- - pix2pix_model.set_requires_grad(netD,False) - optimizer_G.zero_grad() - - # First, G(A) should fake the discriminator - fake_AB = torch.cat((real_A, pred), 1) - pred_fake = netD(fake_AB) - loss_G_GAN = criterionGAN(pred_fake, True)*opt.lambda_gan - - # combine loss and calculate gradients - if opt.l2: - loss_G_L1 = (criterion_L1(pred, target)+criterion_L2(pred, target)) * opt.lambda_L1 - else: - loss_G_L1 = criterion_L1(pred, target) * opt.lambda_L1 - - if opt.hd: - real_AB = torch.cat((real_A, target), 1) - pred_real = netD(real_AB) - loss_G_GAN_Feat = criterionFeat(pred_fake,pred_real) - loss_VGG = criterionVGG(pred, target) * opt.lambda_feat - loss_G = loss_G_GAN + loss_G_L1 + loss_G_GAN_Feat + loss_VGG - else: - loss_G = loss_G_GAN + loss_G_L1 - loss_sum[0] += loss_G_L1.item() - loss_sum[1] += loss_G_GAN.item() - loss_sum[2] += loss_G_GAN_Feat.item() - loss_sum[3] += loss_VGG.item() - - # udpate G's weights - loss_G.backward() - optimizer_G.step() - - else: - pred = netG(inputdata) - if opt.l2: - loss_G_L1 = (criterion_L1(pred, target)+criterion_L2(pred, target)) * opt.lambda_L1 - else: - loss_G_L1 = criterion_L1(pred, target) * opt.lambda_L1 - loss_sum[0] += loss_G_L1.item() - - optimizer_G.zero_grad() - loss_G_L1.backward() - optimizer_G.step() - - # save train result - if (iter+1)%1000 == 0: - try: - data.showresult(inputdata[:,int((N-1)/2)*3:(int((N-1)/2)+1)*3,:,:], - target, pred, os.path.join(dir_checkpoint,'result_train.jpg')) - except Exception as e: - print(e) - - # plot - if (iter+1)%1000 == 0: - time_end = time.time() - #if opt.gan: - savestr ='iter:{0:d} L1_loss:{1:.3f} GAN_loss:{2:.3f} Feat:{3:.3f} VGG:{4:.3f} time:{5:.2f}'.format( - iter+1,loss_sum[0]/1000,loss_sum[1]/1000,loss_sum[2]/1000,loss_sum[3]/1000,(time_end-time_start)/1000) - util.writelog(os.path.join(dir_checkpoint,'loss.txt'), savestr,True) - if (iter+1)/1000 >= 10: - for i in range(4):loss_plot[i].append(loss_sum[i]/1000) - item_plot.append(iter+1) - try: - labels = ['L1_loss','GAN_loss','GAN_Feat_loss','VGG_loss'] - for i in range(4):plt.plot(item_plot,loss_plot[i],label=labels[i]) - plt.xlabel('iter') - plt.legend(loc=1) - plt.savefig(os.path.join(dir_checkpoint,'loss.jpg')) - plt.close() - except Exception as e: - print("error:",e) - - loss_sum = [0.,0.,0.,0.,0.,0.] - time_start=time.time() - - # save network - if (iter+1)%(opt.savefreq//10) == 0: - torch.save(netG.cpu().state_dict(),os.path.join(dir_checkpoint,'last_G.pth')) - if opt.gan: - torch.save(netD.cpu().state_dict(),os.path.join(dir_checkpoint,'last_D.pth')) - if opt.use_gpu !=-1 : - netG.cuda() - if opt.gan: - netD.cuda() - f = open(os.path.join(dir_checkpoint,'iter'),'w+') - f.write(str(iter+1)) - f.close() - - if (iter+1)%opt.savefreq == 0: - os.rename(os.path.join(dir_checkpoint,'last_G.pth'),os.path.join(dir_checkpoint,str(iter+1)+'G.pth')) - if opt.gan: - os.rename(os.path.join(dir_checkpoint,'last_D.pth'),os.path.join(dir_checkpoint,str(iter+1)+'D.pth')) - print('network saved.') - - #test - if (iter+1)%opt.savefreq == 0: - if os.path.isdir('./test'): - netG.eval() - - test_names = os.listdir('./test') - test_names.sort() - result = np.zeros((opt.finesize*2,opt.finesize*len(test_names),3), dtype='uint8') - - for cnt,test_name in enumerate(test_names,0): - img_names = os.listdir(os.path.join('./test',test_name,'image')) - img_names.sort() - inputdata = np.zeros((opt.finesize,opt.finesize,3*N+1), dtype='uint8') - for i in range(0,N): - img = impro.imread(os.path.join('./test',test_name,'image',img_names[i])) - img = impro.resize(img,opt.finesize) - inputdata[:,:,i*3:(i+1)*3] = img - - mask = impro.imread(os.path.join('./test',test_name,'mask.png'),'gray') - mask = impro.resize(mask,opt.finesize) - mask = impro.mask_threshold(mask,15,128) - inputdata[:,:,-1] = mask - result[0:opt.finesize,opt.finesize*cnt:opt.finesize*(cnt+1),:] = inputdata[:,:,int((N-1)/2)*3:(int((N-1)/2)+1)*3] - inputdata = data.im2tensor(inputdata,bgr2rgb=False,use_gpu=opt.use_gpu,use_transform = False,is0_1 = False) - pred = netG(inputdata) - - pred = data.tensor2im(pred,rgb2bgr = False, is0_1 = False) - result[opt.finesize:opt.finesize*2,opt.finesize*cnt:opt.finesize*(cnt+1),:] = pred - - cv2.imwrite(os.path.join(dir_checkpoint,str(iter+1)+'_test.jpg'), result) - netG.train() \ No newline at end of file +import os +import sys +sys.path.append("..") +sys.path.append("../..") +from cores import Options +opt = Options() + +import numpy as np +import cv2 +import random +import torch +import torch.nn as nn +import time + +from util import util,data,dataloader +from util import image_processing as impro +from models import BVDNet,model_util +from skimage.metrics import structural_similarity +from tensorboardX import SummaryWriter + +''' +--------------------------Get options-------------------------- +''' +opt.parser.add_argument('--N',type=int,default=2, help='The input tensor shape is H×W×T×C, T = 2N+1') +opt.parser.add_argument('--S',type=int,default=3, help='Stride of 3 frames') +# opt.parser.add_argument('--T',type=int,default=7, help='T = 2N+1') +opt.parser.add_argument('--M',type=int,default=100, help='How many frames read from each videos') +opt.parser.add_argument('--lr',type=float,default=0.0002, help='') +opt.parser.add_argument('--beta1',type=float,default=0.9, help='') +opt.parser.add_argument('--beta2',type=float,default=0.999, help='') +opt.parser.add_argument('--finesize',type=int,default=256, help='') +opt.parser.add_argument('--loadsize',type=int,default=286, help='') +opt.parser.add_argument('--batchsize',type=int,default=1, help='') +opt.parser.add_argument('--no_gan', action='store_true', help='if specified, do not use gan') +opt.parser.add_argument('--n_blocks',type=int,default=4, help='') +opt.parser.add_argument('--n_layers_D',type=int,default=2, help='') +opt.parser.add_argument('--num_D',type=int,default=3, help='') +opt.parser.add_argument('--lambda_L2',type=float,default=100, help='') +opt.parser.add_argument('--lambda_VGG',type=float,default=1, help='') +opt.parser.add_argument('--lambda_GAN',type=float,default=0.01, help='') +opt.parser.add_argument('--lambda_D',type=float,default=1, help='') +opt.parser.add_argument('--load_thread',type=int,default=16, help='number of thread for loading data') + +opt.parser.add_argument('--dataset',type=str,default='./datasets/face/', help='') +opt.parser.add_argument('--dataset_test',type=str,default='./datasets/face_test/', help='') +opt.parser.add_argument('--n_epoch',type=int,default=200, help='') +opt.parser.add_argument('--save_freq',type=int,default=10000, help='') +opt.parser.add_argument('--continue_train', action='store_true', help='') +opt.parser.add_argument('--savename',type=str,default='face', help='') +opt.parser.add_argument('--showresult_freq',type=int,default=1000, help='') +opt.parser.add_argument('--showresult_num',type=int,default=4, help='') + +def ImageQualityEvaluation(tensor1,tensor2,showiter,writer,tag): + batch_len = len(tensor1) + psnr,ssmi = 0,0 + for i in range(len(tensor1)): + img1,img2 = data.tensor2im(tensor1,rgb2bgr=False,batch_index=i), data.tensor2im(tensor2,rgb2bgr=False,batch_index=i) + psnr += impro.psnr(img1,img2) + ssmi += structural_similarity(img1,img2,multichannel=True) + writer.add_scalars('quality/psnr', {tag:psnr/batch_len}, showiter) + writer.add_scalars('quality/ssmi', {tag:ssmi/batch_len}, showiter) + return psnr/batch_len,ssmi/batch_len + +def ShowImage(tensor1,tensor2,tensor3,showiter,max_num,writer,tag): + show_imgs = [] + for i in range(max_num): + show_imgs += [ data.tensor2im(tensor1,rgb2bgr = False,batch_index=i), + data.tensor2im(tensor2,rgb2bgr = False,batch_index=i), + data.tensor2im(tensor3,rgb2bgr = False,batch_index=i)] + show_img = impro.splice(show_imgs, (opt.showresult_num,3)) + writer.add_image(tag, show_img,showiter,dataformats='HWC') + +''' +--------------------------Init-------------------------- +''' +opt = opt.getparse() +opt.T = 2*opt.N+1 +if opt.showresult_num >opt.batchsize: + opt.showresult_num = opt.batchsize +dir_checkpoint = os.path.join('checkpoints',opt.savename) +util.makedirs(dir_checkpoint) +# start tensorboard +localtime = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) +tensorboard_savedir = os.path.join('checkpoints/tensorboard',localtime+'_'+opt.savename) +TBGlobalWriter = SummaryWriter(tensorboard_savedir) +print('Please run "tensorboard --logdir checkpoints/tensorboardX --host=your_server_ip" and input "'+localtime+'" to filter outputs') + +''' +--------------------------Init Network-------------------------- +''' +if opt.gpu_id != '-1' and len(opt.gpu_id) == 1: + torch.backends.cudnn.benchmark = True + +netG = BVDNet.define_G(opt.N,opt.n_blocks,gpu_id=opt.gpu_id) +optimizer_G = torch.optim.Adam(netG.parameters(), lr=opt.lr, betas=(opt.beta1, opt.beta2)) +lossfun_L2 = nn.MSELoss() +lossfun_VGG = model_util.VGGLoss(opt.gpu_id) +if not opt.no_gan: + netD = BVDNet.define_D(n_layers_D=opt.n_layers_D,num_D=opt.num_D,gpu_id=opt.gpu_id) + optimizer_D = torch.optim.Adam(netD.parameters(), lr=opt.lr, betas=(opt.beta1, opt.beta2)) + lossfun_GAND = BVDNet.GANLoss('D') + lossfun_GANG = BVDNet.GANLoss('G') + +''' +--------------------------Init DataLoader-------------------------- +''' +videolist_tmp = os.listdir(opt.dataset) +videolist = [] +for video in videolist_tmp: + if os.path.isdir(os.path.join(opt.dataset,video)): + if len(os.listdir(os.path.join(opt.dataset,video,'mask')))>=opt.M: + videolist.append(video) +sorted(videolist) +videolist_train = videolist[:int(len(videolist)*0.8)].copy() +videolist_eval = videolist[int(len(videolist)*0.8):].copy() + +Videodataloader_train = dataloader.VideoDataLoader(opt, videolist_train) +Videodataloader_eval = dataloader.VideoDataLoader(opt, videolist_eval) + +''' +--------------------------Train-------------------------- +''' +previous_predframe_tmp = 0 +for train_iter in range(Videodataloader_train.n_iter): + t_start = time.time() + # train + ori_stream,mosaic_stream,previous_frame = Videodataloader_train.get_data() + ori_stream = data.to_tensor(ori_stream, opt.gpu_id) + mosaic_stream = data.to_tensor(mosaic_stream, opt.gpu_id) + if previous_frame is None: + previous_frame = data.to_tensor(previous_predframe_tmp, opt.gpu_id) + else: + previous_frame = data.to_tensor(previous_frame, opt.gpu_id) + + ############### Forward #################### + # Fake Generator + out = netG(mosaic_stream,previous_frame) + # Discriminator + if not opt.no_gan: + dis_real = netD(torch.cat((mosaic_stream[:,:,opt.N],ori_stream[:,:,opt.N].detach()),dim=1)) + dis_fake_D = netD(torch.cat((mosaic_stream[:,:,opt.N],out.detach()),dim=1)) + loss_D = lossfun_GAND(dis_fake_D,dis_real) * opt.lambda_GAN * opt.lambda_D + # Generator + loss_L2 = lossfun_L2(out,ori_stream[:,:,opt.N]) * opt.lambda_L2 + loss_VGG = lossfun_VGG(out,ori_stream[:,:,opt.N]) * opt.lambda_VGG + loss_G = loss_L2+loss_VGG + if not opt.no_gan: + dis_fake_G = netD(torch.cat((mosaic_stream[:,:,opt.N],out),dim=1)) + loss_GANG = lossfun_GANG(dis_fake_G) * opt.lambda_GAN + loss_G = loss_G + loss_GANG + + ############### Backward Pass #################### + optimizer_G.zero_grad() + loss_G.backward() + optimizer_G.step() + + if not opt.no_gan: + optimizer_D.zero_grad() + loss_D.backward() + optimizer_D.step() + + previous_predframe_tmp = out.detach().cpu().numpy() + + if not opt.no_gan: + TBGlobalWriter.add_scalars('loss/train', {'L2':loss_L2.item(),'VGG':loss_VGG.item(), + 'loss_D':loss_D.item(),'loss_G':loss_G.item()}, train_iter) + else: + TBGlobalWriter.add_scalars('loss/train', {'L2':loss_L2.item(),'VGG':loss_VGG.item()}, train_iter) + + # save network + if train_iter%opt.save_freq == 0 and train_iter != 0: + model_util.save(netG, os.path.join('checkpoints',opt.savename,str(train_iter)+'_G.pth'), opt.gpu_id) + if not opt.no_gan: + model_util.save(netD, os.path.join('checkpoints',opt.savename,str(train_iter)+'_D.pth'), opt.gpu_id) + + # Image quality evaluation + if train_iter%(opt.showresult_freq//10) == 0: + ImageQualityEvaluation(out,ori_stream[:,:,opt.N],train_iter,TBGlobalWriter,'train') + + # Show result + if train_iter % opt.showresult_freq == 0: + ShowImage(mosaic_stream[:,:,opt.N],out,ori_stream[:,:,opt.N],train_iter,opt.showresult_num,TBGlobalWriter,'train') + + ''' + --------------------------Eval-------------------------- + ''' + if (train_iter)%5 ==0: + ori_stream,mosaic_stream,previous_frame = Videodataloader_eval.get_data() + ori_stream = data.to_tensor(ori_stream, opt.gpu_id) + mosaic_stream = data.to_tensor(mosaic_stream, opt.gpu_id) + if previous_frame is None: + previous_frame = data.to_tensor(previous_predframe_tmp, opt.gpu_id) + else: + previous_frame = data.to_tensor(previous_frame, opt.gpu_id) + with torch.no_grad(): + out = netG(mosaic_stream,previous_frame) + loss_L2 = lossfun_L2(out,ori_stream[:,:,opt.N]) * opt.lambda_L2 + loss_VGG = lossfun_VGG(out,ori_stream[:,:,opt.N]) * opt.lambda_VGG + #TBGlobalWriter.add_scalars('loss/eval', {'L2':loss_L2.item(),'VGG':loss_VGG.item()}, train_iter) + previous_predframe_tmp = out.detach().cpu().numpy() + + # Image quality evaluation + if train_iter%(opt.showresult_freq//10) == 0: + psnr,ssmi = ImageQualityEvaluation(out,ori_stream[:,:,opt.N],train_iter,TBGlobalWriter,'eval') + + # Show result + if train_iter % opt.showresult_freq == 0: + ShowImage(mosaic_stream[:,:,opt.N],out,ori_stream[:,:,opt.N],train_iter,opt.showresult_num,TBGlobalWriter,'eval') + t_end = time.time() + print('iter:{0:d} t:{1:.2f} L2:{2:.4f} vgg:{3:.4f} psnr:{4:.2f} ssmi:{5:.3f}'.format(train_iter,t_end-t_start, + loss_L2.item(),loss_VGG.item(),psnr,ssmi) ) + t_strat = time.time() + + ''' + --------------------------Test-------------------------- + ''' + if train_iter % opt.showresult_freq == 0 and os.path.isdir(opt.dataset_test): + show_imgs = [] + videos = os.listdir(opt.dataset_test) + sorted(videos) + for video in videos: + frames = os.listdir(os.path.join(opt.dataset_test,video,'image')) + sorted(frames) + for step in range(5): + mosaic_stream = [] + for i in range(opt.T): + _mosaic = impro.imread(os.path.join(opt.dataset_test,video,'image',frames[i*opt.S+step]),loadsize=opt.finesize,rgb=True) + mosaic_stream.append(_mosaic) + if step == 0: + previous = impro.imread(os.path.join(opt.dataset_test,video,'image',frames[opt.N*opt.S-1]),loadsize=opt.finesize,rgb=True) + previous = data.im2tensor(previous,bgr2rgb = False, gpu_id = opt.gpu_id, is0_1 = False) + mosaic_stream = (np.array(mosaic_stream).astype(np.float32)/255.0-0.5)/0.5 + mosaic_stream = mosaic_stream.reshape(1,opt.T,opt.finesize,opt.finesize,3).transpose((0,4,1,2,3)) + mosaic_stream = data.to_tensor(mosaic_stream, opt.gpu_id) + with torch.no_grad(): + out = netG(mosaic_stream,previous) + previous = out + show_imgs+= [data.tensor2im(mosaic_stream[:,:,opt.N],rgb2bgr = False),data.tensor2im(out,rgb2bgr = False)] + + show_img = impro.splice(show_imgs, (len(videos),2)) + TBGlobalWriter.add_image('test', show_img,train_iter,dataformats='HWC') \ No newline at end of file diff --git a/util/data.py b/util/data.py index c76dfa3..8a7865e 100755 --- a/util/data.py +++ b/util/data.py @@ -1,20 +1,31 @@ import random import os +from util.mosaic import get_random_parameter import numpy as np import torch import torchvision.transforms as transforms import cv2 from . import image_processing as impro -from . import mosaic -transform = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5)) - ] -) - -def tensor2im(image_tensor, imtype=np.uint8, gray=False, rgb2bgr = True ,is0_1 = False): +from . import degradater + +def to_tensor(data,gpu_id): + data = torch.from_numpy(data) + if gpu_id != '-1': + data = data.cuda() + return data + +def normalize(data): + ''' + normalize to -1 ~ 1 + ''' + return (data.astype(np.float32)/255.0-0.5)/0.5 + +def anti_normalize(data): + return np.clip((data*0.5+0.5)*255,0,255).astype(np.uint8) + +def tensor2im(image_tensor, gray=False, rgb2bgr = True ,is0_1 = False, batch_index=0): image_tensor =image_tensor.data - image_numpy = image_tensor[0].cpu().float().numpy() + image_numpy = image_tensor[batch_index].cpu().float().numpy() if not is0_1: image_numpy = (image_numpy + 1)/2.0 @@ -24,7 +35,7 @@ def tensor2im(image_tensor, imtype=np.uint8, gray=False, rgb2bgr = True ,is0_1 = if gray: h, w = image_numpy.shape[1:] image_numpy = image_numpy.reshape(h,w) - return image_numpy.astype(imtype) + return image_numpy.astype(np.uint8) # output 3ch if image_numpy.shape[0] == 1: @@ -32,11 +43,10 @@ def tensor2im(image_tensor, imtype=np.uint8, gray=False, rgb2bgr = True ,is0_1 = image_numpy = image_numpy.transpose((1, 2, 0)) if rgb2bgr and not gray: image_numpy = image_numpy[...,::-1]-np.zeros_like(image_numpy) - return image_numpy.astype(imtype) + return image_numpy.astype(np.uint8) -def im2tensor(image_numpy, imtype=np.uint8, gray=False,bgr2rgb = True, reshape = True, use_gpu = 0, use_transform = True,is0_1 = True): - +def im2tensor(image_numpy, gray=False,bgr2rgb = True, reshape = True, gpu_id = '-1',is0_1 = False): if gray: h, w = image_numpy.shape image_numpy = (image_numpy/255.0-0.5)/0.5 @@ -47,18 +57,15 @@ def im2tensor(image_numpy, imtype=np.uint8, gray=False,bgr2rgb = True, reshape = h, w ,ch = image_numpy.shape if bgr2rgb: image_numpy = image_numpy[...,::-1]-np.zeros_like(image_numpy) - if use_transform: - image_tensor = transform(image_numpy) + if is0_1: + image_numpy = image_numpy/255.0 else: - if is0_1: - image_numpy = image_numpy/255.0 - else: - image_numpy = (image_numpy/255.0-0.5)/0.5 - image_numpy = image_numpy.transpose((2, 0, 1)) - image_tensor = torch.from_numpy(image_numpy).float() + image_numpy = (image_numpy/255.0-0.5)/0.5 + image_numpy = image_numpy.transpose((2, 0, 1)) + image_tensor = torch.from_numpy(image_numpy).float() if reshape: image_tensor = image_tensor.reshape(1,ch,h,w) - if use_gpu != -1: + if gpu_id != '-1': image_tensor = image_tensor.cuda() return image_tensor @@ -68,53 +75,7 @@ def shuffledata(data,target): np.random.set_state(state) np.random.shuffle(target) -def random_transform_video(src,target,finesize,N): - #random blur - if random.random()<0.2: - h,w = src.shape[:2] - src = src[:8*(h//8),:8*(w//8)] - Q_ran = random.randint(1,15) - src[:,:,:3*N] = impro.dctblur(src[:,:,:3*N],Q_ran) - target = impro.dctblur(target,Q_ran) - - #random crop - h,w = target.shape[:2] - h_move = int((h-finesize)*random.random()) - w_move = int((w-finesize)*random.random()) - target = target[h_move:h_move+finesize,w_move:w_move+finesize,:] - src = src[h_move:h_move+finesize,w_move:w_move+finesize,:] - - #random flip - if random.random()<0.5: - src = src[:,::-1,:] - target = target[:,::-1,:] - - #random color - alpha = random.uniform(-0.1,0.1) - beta = random.uniform(-0.1,0.1) - b = random.uniform(-0.05,0.05) - g = random.uniform(-0.05,0.05) - r = random.uniform(-0.05,0.05) - for i in range(N): - src[:,:,i*3:(i+1)*3] = impro.color_adjust(src[:,:,i*3:(i+1)*3],alpha,beta,b,g,r) - target = impro.color_adjust(target,alpha,beta,b,g,r) - - #random resize blur - if random.random()<0.5: - interpolations = [cv2.INTER_LINEAR,cv2.INTER_CUBIC,cv2.INTER_LANCZOS4] - size_ran = random.uniform(0.7,1.5) - interpolation_up = interpolations[random.randint(0,2)] - interpolation_down =interpolations[random.randint(0,2)] - - tmp = cv2.resize(src[:,:,:3*N], (int(finesize*size_ran),int(finesize*size_ran)),interpolation=interpolation_up) - src[:,:,:3*N] = cv2.resize(tmp, (finesize,finesize),interpolation=interpolation_down) - - tmp = cv2.resize(target, (int(finesize*size_ran),int(finesize*size_ran)),interpolation=interpolation_up) - target = cv2.resize(tmp, (finesize,finesize),interpolation=interpolation_down) - - return src,target - -def random_transform_single(img,out_shape): +def random_transform_single_mask(img,out_shape): out_h,out_w = out_shape img = cv2.resize(img,(int(out_w*random.uniform(1.1, 1.5)),int(out_h*random.uniform(1.1, 1.5)))) h,w = img.shape[:2] @@ -130,90 +91,65 @@ def random_transform_single(img,out_shape): img = cv2.resize(img,(out_w,out_h)) return img -def random_transform_image(img,mask,finesize,test_flag = False): - #random scale - if random.random()<0.5: - h,w = img.shape[:2] - loadsize = min((h,w)) - a = (float(h)/float(w))*random.uniform(0.9, 1.1) - if h B,C,T,H,W + self.ori_stream = self.ori_stream.reshape (1,self.opt.T,opt.finesize,opt.finesize,3).transpose((0,4,1,2,3)) + self.mosaic_stream = self.mosaic_stream.reshape(1,self.opt.T,opt.finesize,opt.finesize,3).transpose((0,4,1,2,3)) + + #Init frist previous frame + self.previous_pred = self.ori_load_pool[self.opt.S*self.opt.N-1].copy() + # previous B,C,H,W + self.previous_pred = self.previous_pred.reshape(1,opt.finesize,opt.finesize,3).transpose((0,3,1,2)) + + def normalize(self,data): + ''' + normalize to -1 ~ 1 + ''' + return (data.astype(np.float32)/255.0-0.5)/0.5 + + def anti_normalize(self,data): + return np.clip((data*0.5+0.5)*255,0,255).astype(np.uint8) + + def next(self): + # random + if np.random.random()<0.05: + self.startpos = [random.randint(0,self.mosaic_size),random.randint(0,self.mosaic_size)] + if np.random.random()<0.02: + self.transform_params['rate']['crop'] = [np.random.random(),np.random.random()] + if np.random.random()<0.02: + self.loadsize = np.random.randint(self.opt.finesize,self.opt.loadsize) + + if self.t != 0: + self.previous_pred = None + self.ori_load_pool [:self.opt.S*self.opt.T-1] = self.ori_load_pool [1:self.opt.S*self.opt.T] + self.mosaic_load_pool[:self.opt.S*self.opt.T-1] = self.mosaic_load_pool[1:self.opt.S*self.opt.T] + #print(os.path.join(self.video_dir,'origin_image','%05d' % (self.opt.S*self.opt.T+self.t)+'.jpg')) + _ori_img = impro.imread(os.path.join(self.video_dir,'origin_image','%05d' % (self.opt.S*self.opt.T+self.t)+'.jpg'),loadsize=self.loadsize,rgb=True) + _mask = impro.imread(os.path.join(self.video_dir,'mask','%05d' % (self.opt.S*self.opt.T+self.t)+'.png' ),mod='gray',loadsize=self.loadsize) + _mosaic_img = mosaic.addmosaic_base(_ori_img, _mask, self.mosaic_size,0, self.mod,self.rect_rat,self.feather,self.startpos) + _ori_img = data.random_transform_single_image(_ori_img,self.opt.finesize,self.transform_params) + _mosaic_img = data.random_transform_single_image(_mosaic_img,self.opt.finesize,self.transform_params) + + _ori_img,_mosaic_img = self.normalize(_ori_img),self.normalize(_mosaic_img) + self.ori_load_pool [self.opt.S*self.opt.T-1] = _ori_img + self.mosaic_load_pool[self.opt.S*self.opt.T-1] = _mosaic_img + + self.ori_stream = self.ori_load_pool [np.linspace(0, (self.opt.T-1)*self.opt.S,self.opt.T,dtype=np.int64)].copy() + self.mosaic_stream = self.mosaic_load_pool[np.linspace(0, (self.opt.T-1)*self.opt.S,self.opt.T,dtype=np.int64)].copy() + + # stream B,T,H,W,C -> B,C,T,H,W + self.ori_stream = self.ori_stream.reshape (1,self.opt.T,self.opt.finesize,self.opt.finesize,3).transpose((0,4,1,2,3)) + self.mosaic_stream = self.mosaic_stream.reshape(1,self.opt.T,self.opt.finesize,self.opt.finesize,3).transpose((0,4,1,2,3)) + + self.t += 1 + +class VideoDataLoader(object): + """VideoDataLoader""" + def __init__(self, opt, videolist, test_flag=False): + super(VideoDataLoader, self).__init__() + self.videolist = [] + self.opt = opt + self.test_flag = test_flag + for i in range(self.opt.n_epoch): + self.videolist += videolist.copy() + random.shuffle(self.videolist) + self.each_video_n_iter = self.opt.M -self.opt.S*(self.opt.T+1) + self.n_iter = len(self.videolist)//self.opt.load_thread//self.opt.batchsize*self.each_video_n_iter*self.opt.load_thread + self.queue = Queue(self.opt.load_thread) + self.ori_stream = np.zeros((self.opt.batchsize,3,self.opt.T,self.opt.finesize,self.opt.finesize),dtype=np.float32)# B,C,T,H,W + self.mosaic_stream = np.zeros((self.opt.batchsize,3,self.opt.T,self.opt.finesize,self.opt.finesize),dtype=np.float32)# B,C,T,H,W + self.previous_pred = np.zeros((self.opt.batchsize,3,self.opt.finesize,self.opt.finesize),dtype=np.float32) + self.load_init() + + def load(self,videolist): + for load_video_iter in range(len(videolist)//self.opt.batchsize): + iter_videolist = videolist[load_video_iter*self.opt.batchsize:(load_video_iter+1)*self.opt.batchsize] + videoloaders = [VideoLoader(self.opt,os.path.join(self.opt.dataset,iter_videolist[i]),self.test_flag) for i in range(self.opt.batchsize)] + for each_video_iter in range(self.each_video_n_iter): + for i in range(self.opt.batchsize): + self.ori_stream[i] = videoloaders[i].ori_stream + self.mosaic_stream[i] = videoloaders[i].mosaic_stream + if each_video_iter == 0: + self.previous_pred[i] = videoloaders[i].previous_pred + videoloaders[i].next() + if each_video_iter == 0: + self.queue.put([self.ori_stream.copy(),self.mosaic_stream.copy(),self.previous_pred]) + else: + self.queue.put([self.ori_stream.copy(),self.mosaic_stream.copy(),None]) + + def load_init(self): + ptvn = len(self.videolist)//self.opt.load_thread #pre_thread_video_num + for i in range(self.opt.load_thread): + p = Process(target=self.load,args=(self.videolist[i*ptvn:(i+1)*ptvn],)) + p.daemon = True + p.start() + + def get_data(self): + return self.queue.get() \ No newline at end of file diff --git a/util/degradater.py b/util/degradater.py new file mode 100644 index 0000000..9e4d227 --- /dev/null +++ b/util/degradater.py @@ -0,0 +1,119 @@ +''' +https://github.com/sonack/GFRNet_pytorch_new +''' +import random +import cv2 +import numpy as np + +def gaussian_blur(img, sigma=3, size=13): + if sigma > 0: + if isinstance(size, int): + size = (size, size) + img = cv2.GaussianBlur(img, size, sigma) + return img + +def down(img, scale, shape): + if scale > 1: + h, w, _ = shape + scaled_h, scaled_w = int(h / scale), int(w / scale) + img = cv2.resize(img, (scaled_w, scaled_h), interpolation = cv2.INTER_CUBIC) + return img + +def up(img, scale, shape): + if scale > 1: + h, w, _ = shape + img = cv2.resize(img, (w, h), interpolation = cv2.INTER_CUBIC) + return img + +def awgn(img, level): + if level > 0: + noise = np.random.randn(*img.shape) * level + img = (img + noise).clip(0,255).astype(np.uint8) + return img + +def jpeg_compressor(img,quality): + if quality > 0: # 0 indicating no lossy compression (i.e losslessly compression) + encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality] + img = cv2.imdecode(cv2.imencode('.jpg', img, encode_param)[1], 1) + return img + +def get_random_degenerate_params(mod='strong'): + ''' + mod : strong | only_downsample | only_4x | weaker_1 | weaker_2 + ''' + params = {} + gaussianBlur_size_list = list(range(3,14,2)) + + if mod == 'strong': + gaussianBlur_sigma_list = [1 + x for x in range(3)] + gaussianBlur_sigma_list += [0] + downsample_scale_list = [1 + x * 0.1 for x in range(0,71)] + awgn_level_list = list(range(1, 8, 1)) + jpeg_quality_list = list(range(10, 41, 1)) + jpeg_quality_list += int(len(jpeg_quality_list) * 0.33) * [0] + + elif mod == 'only_downsample': + gaussianBlur_sigma_list = [0] + downsample_scale_list = [1 + x * 0.1 for x in range(0,71)] + awgn_level_list = [0] + jpeg_quality_list = [0] + + elif mod == 'only_4x': + gaussianBlur_sigma_list = [0] + downsample_scale_list = [4] + awgn_level_list = [0] + jpeg_quality_list = [0] + + elif mod == 'weaker_1': # 0.5 trigger prob + gaussianBlur_sigma_list = [1 + x for x in range(3)] + gaussianBlur_sigma_list += int(len(gaussianBlur_sigma_list)) * [0] # 1/2 trigger this degradation + + downsample_scale_list = [1 + x * 0.1 for x in range(0,71)] + downsample_scale_list += int(len(downsample_scale_list)) * [1] + + awgn_level_list = list(range(1, 8, 1)) + awgn_level_list += int(len(awgn_level_list)) * [0] + + jpeg_quality_list = list(range(10, 41, 1)) + jpeg_quality_list += int(len(jpeg_quality_list)) * [0] + + elif mod == 'weaker_2': # weaker than weaker_1, jpeg [20,40] + gaussianBlur_sigma_list = [1 + x for x in range(3)] + gaussianBlur_sigma_list += int(len(gaussianBlur_sigma_list)) * [0] # 1/2 trigger this degradation + + downsample_scale_list = [1 + x * 0.1 for x in range(0,71)] + downsample_scale_list += int(len(downsample_scale_list)) * [1] + + awgn_level_list = list(range(1, 8, 1)) + awgn_level_list += int(len(awgn_level_list)) * [0] + + jpeg_quality_list = list(range(20, 41, 1)) + jpeg_quality_list += int(len(jpeg_quality_list)) * [0] + + params['blur_sigma'] = random.choice(gaussianBlur_sigma_list) + params['blur_size'] = random.choice(gaussianBlur_size_list) + params['updown_scale'] = random.choice(downsample_scale_list) + params['awgn_level'] = random.choice(awgn_level_list) + params['jpeg_quality'] = random.choice(jpeg_quality_list) + + return params + +def degradate(img,params,jpeg_last = True): + shape = img.shape + if not params: + params = get_random_degenerate_params('original') + + if jpeg_last: + img = gaussian_blur(img,params['blur_sigma'],params['blur_size']) + img = down(img,params['updown_scale'],shape) + img = awgn(img,params['awgn_level']) + img = up(img,params['updown_scale'],shape) + img = jpeg_compressor(img,params['jpeg_quality']) + else: + img = gaussian_blur(img,params['blur_sigma'],params['blur_size']) + img = down(img,params['updown_scale'],shape) + img = awgn(img,params['awgn_level']) + img = jpeg_compressor(img,params['jpeg_quality']) + img = up(img,params['updown_scale'],shape) + + return img \ No newline at end of file diff --git a/util/ffmpeg.py b/util/ffmpeg.py index 4eb668b..2088142 100755 --- a/util/ffmpeg.py +++ b/util/ffmpeg.py @@ -1,5 +1,5 @@ import os,json - +import subprocess # ffmpeg 3.4.6 def args2cmd(args): @@ -32,10 +32,11 @@ def run(args,mode = 0): return sout def video2image(videopath, imagepath, fps=0, start_time='00:00:00', last_time='00:00:00'): - args = ['ffmpeg', '-i', '"'+videopath+'"'] + args = ['ffmpeg'] if last_time != '00:00:00': args += ['-ss', start_time] args += ['-t', last_time] + args += ['-i', '"'+videopath+'"'] if fps != 0: args += ['-r', str(fps)] args += ['-f', 'image2','-q:v','-0',imagepath] diff --git a/util/image_processing.py b/util/image_processing.py index 722a000..6f1dd91 100755 --- a/util/image_processing.py +++ b/util/image_processing.py @@ -8,16 +8,7 @@ if 'Windows' in platform.platform(): system_type = 'Windows' -DCT_Q = np.array([[8,16,19,22,26,27,29,34], - [16,16,22,24,27,29,34,37], - [19,22,26,27,29,34,34,38], - [22,22,26,27,29,34,37,40], - [22,26,27,29,32,35,40,48], - [26,27,29,32,35,40,48,58], - [26,27,29,34,38,46,56,59], - [27,29,35,38,46,56,69,83]]) - -def imread(file_path,mod = 'normal',loadsize = 0): +def imread(file_path,mod = 'normal',loadsize = 0, rgb=False): ''' mod: 'normal' | 'gray' | 'all' loadsize: 0->original @@ -43,6 +34,9 @@ def imread(file_path,mod = 'normal',loadsize = 0): if loadsize != 0: img = resize(img, loadsize, interpolation=cv2.INTER_CUBIC) + if rgb and img.ndim==3: + img = img[:,:,::-1] + return img def imwrite(file_path,img): @@ -110,6 +104,12 @@ def color_adjust(img,alpha=0,beta=0,b=0,g=0,r=0,ran = False): return (np.clip(img,0,255)).astype('uint8') +def CAdaIN(src,dst): + ''' + make src has dst's style + ''' + return np.std(dst)*((src-np.mean(src))/np.std(src))+np.mean(dst) + def makedataset(target_image,orgin_image): target_image = resize(target_image,256) orgin_image = resize(orgin_image,256) @@ -118,34 +118,6 @@ def makedataset(target_image,orgin_image): img[0:256,0:256] = target_image[0:256,int(w/2-256/2):int(w/2+256/2)] img[0:256,256:512] = orgin_image[0:256,int(w/2-256/2):int(w/2+256/2)] return img - -def block_dct_and_idct(g,QQF,QQF_16): - return cv2.idct(np.round(16.0*cv2.dct(g)/QQF)*QQF_16) - -def image_dct_and_idct(I,QF): - h,w = I.shape - QQF = DCT_Q*QF - QQF_16 = QQF/16.0 - for i in range(h//8): - for j in range(w//8): - I[i*8:(i+1)*8,j*8:(j+1)*8] = cv2.idct(np.round(16.0*cv2.dct(I[i*8:(i+1)*8,j*8:(j+1)*8])/QQF)*QQF_16) - #I[i*8:(i+1)*8,j*8:(j+1)*8] = block_dct_and_idct(I[i*8:(i+1)*8,j*8:(j+1)*8],QQF,QQF_16) - return I - -def dctblur(img,Q): - ''' - Q: 1~20, 1->best - ''' - h,w = img.shape[:2] - img = img[:8*(h//8),:8*(w//8)] - img = img.astype(np.float32) - if img.ndim == 2: - img = image_dct_and_idct(img, Q) - if img.ndim == 3: - h,w,ch = img.shape - for i in range(ch): - img[:,:,i] = image_dct_and_idct(img[:,:,i], Q) - return (np.clip(img,0,255)).astype(np.uint8) def find_mostlikely_ROI(mask): contours,hierarchy=cv2.findContours(mask, cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) @@ -211,6 +183,30 @@ def mask_area(mask): except: area = 0 return area +import time +def replace_mosaic(img_origin,img_fake,mask,x,y,size,no_feather): + img_fake = cv2.resize(img_fake,(size*2,size*2),interpolation=cv2.INTER_CUBIC) + if no_feather: + img_origin[y-size:y+size,x-size:x+size]=img_fake + return img_origin + else: + # #color correction + # RGB_origin = img_origin[y-size:y+size,x-size:x+size].mean(0).mean(0) + # RGB_fake = img_fake.mean(0).mean(0) + # for i in range(3):img_fake[:,:,i] = np.clip(img_fake[:,:,i]+RGB_origin[i]-RGB_fake[i],0,255) + #eclosion + eclosion_num = int(size/10)+2 + + mask_crop = cv2.resize(mask,(img_origin.shape[1],img_origin.shape[0]))[y-size:y+size,x-size:x+size] + mask_crop = ch_one2three(mask_crop) + + mask_crop = (cv2.blur(mask_crop, (eclosion_num, eclosion_num))) + mask_crop = mask_crop/255.0 + + img_crop = img_origin[y-size:y+size,x-size:x+size] + img_origin[y-size:y+size,x-size:x+size] = np.clip((img_crop*(1-mask_crop)+img_fake*mask_crop),0,255).astype('uint8') + + return img_origin def Q_lapulase(resImg): @@ -225,31 +221,25 @@ def Q_lapulase(resImg): score = res.var() return score -def replace_mosaic(img_origin,img_fake,mask,x,y,size,no_feather): - img_fake = cv2.resize(img_fake,(size*2,size*2),interpolation=cv2.INTER_LANCZOS4) - if no_feather: - img_origin[y-size:y+size,x-size:x+size]=img_fake - img_result = img_origin - else: - #color correction - RGB_origin = img_origin[y-size:y+size,x-size:x+size].mean(0).mean(0) - RGB_fake = img_fake.mean(0).mean(0) - for i in range(3):img_fake[:,:,i] = np.clip(img_fake[:,:,i]+RGB_origin[i]-RGB_fake[i],0,255) - #eclosion - eclosion_num = int(size/5) - entad = int(eclosion_num/2+2) - - mask = cv2.resize(mask,(img_origin.shape[1],img_origin.shape[0])) - mask = ch_one2three(mask) - - mask = (cv2.blur(mask, (eclosion_num, eclosion_num))) - mask_tmp = np.zeros_like(mask) - mask_tmp[y-size:y+size,x-size:x+size] = mask[y-size:y+size,x-size:x+size]# Fix edge overflow - mask = mask_tmp/255.0 - - img_tmp = np.zeros(img_origin.shape) - img_tmp[y-size:y+size,x-size:x+size]=img_fake - img_result = img_origin.copy() - img_result = (img_origin*(1-mask)+img_tmp*mask).astype('uint8') +def psnr(img1,img2): + mse = np.mean((img1/255.0-img2/255.0)**2) + if mse < 1e-10: + return 100 + psnr_v = 20*np.log10(1/np.sqrt(mse)) + return psnr_v + +def splice(imgs,splice_shape): + '''Stitching multiple images, all imgs must have the same size + imgs : [img1,img2,img3,img4] + splice_shape: (2,2) + ''' + h,w,ch = imgs[0].shape + output = np.zeros((h*splice_shape[0],w*splice_shape[1],ch),np.uint8) + cnt = 0 + for i in range(splice_shape[0]): + for j in range(splice_shape[1]): + if cnt < len(imgs): + output[h*i:h*(i+1),w*j:w*(j+1)] = imgs[cnt] + cnt += 1 + return output - return img_result \ No newline at end of file diff --git a/util/util.py b/util/util.py index 571974e..4952df8 100755 --- a/util/util.py +++ b/util/util.py @@ -1,4 +1,6 @@ import os +import random +import string import shutil def Traversal(filedir): @@ -10,6 +12,9 @@ def Traversal(filedir): Traversal(dir) return file_list +def randomstr(num): + return ''.join(random.sample(string.ascii_letters + string.digits, num)) + def is_img(path): ext = os.path.splitext(path)[1] ext = ext.lower()