Spaces:
Running
Running
Upload 12 files
Browse files- .gitattributes +2 -0
- anime_upscaler.py +125 -0
- app.py +7 -0
- frame_esrgan.py +72 -0
- readme.md +43 -0
- requirements.txt +9 -0
- test_img/glpa_01_cut_1.mp4 +0 -0
- test_img/random_test_frame.jpg +0 -0
- test_img/random_test_frame_out.jpg +0 -0
- test_img/test_clip.mp3 +0 -0
- test_img/test_clip.mp4 +0 -0
- test_img/test_clip_upscaled.mp4 +3 -0
- test_img/test_clip_upscaled_copy.mp4 +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
test_img/test_clip_upscaled_copy.mp4 filter=lfs diff=lfs merge=lfs -text
|
37 |
+
test_img/test_clip_upscaled.mp4 filter=lfs diff=lfs merge=lfs -text
|
anime_upscaler.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import frame_esrgan
|
2 |
+
import cv2
|
3 |
+
from tqdm import tqdm
|
4 |
+
import os
|
5 |
+
import argparse
|
6 |
+
import shutil
|
7 |
+
import image_slicer
|
8 |
+
from image_slicer import join
|
9 |
+
import numpy as np
|
10 |
+
from PIL import Image
|
11 |
+
import matplotlib.pyplot as plt
|
12 |
+
import matplotlib.image as mpimg
|
13 |
+
import subprocess
|
14 |
+
|
15 |
+
|
16 |
+
parser = argparse.ArgumentParser()
|
17 |
+
parser.add_argument('-m', '--model_path', type=str, help='REQUIRED: specify path of the model being used')
|
18 |
+
parser.add_argument('-i', '--input', type=str, help='REQUIRED: specify path of the image you want to upscale')
|
19 |
+
parser.add_argument('-o', '--output', type=str, help='REQUIRED: specify path where you want to save image')
|
20 |
+
parser.add_argument('-s', '--slice', nargs='?', type=int, const=4, help='OPTIONAL: specify weather to split frames, recommended to use to help with VRAM unless you got a fucken quadro or something' )
|
21 |
+
parser.add_argument('-a', '--audio', action='store_true', help='OPTIONAL: specify weather you want to copy audio from source as well')
|
22 |
+
parser.add_argument('-c', '--clear_temp', action='store_true', help='OPTIONAL: specify weather you want to clear temporary folder with upscaled frames after you are finished with final video')
|
23 |
+
args = parser.parse_args()
|
24 |
+
|
25 |
+
def extract_frames(vid_path, save=''):
|
26 |
+
vid = cv2.VideoCapture(vid_path)
|
27 |
+
images = []
|
28 |
+
count = 0
|
29 |
+
success, image = vid.read()
|
30 |
+
while success:
|
31 |
+
if not save:
|
32 |
+
images.append(image)
|
33 |
+
else:
|
34 |
+
print('saving frame {}...'.format(count))
|
35 |
+
cv2.imwrite(save.format(count), image)
|
36 |
+
print('done saving frame {}...'.format(count))
|
37 |
+
success, image = vid.read()
|
38 |
+
count += 1
|
39 |
+
return images
|
40 |
+
|
41 |
+
def get_fps(vid_path):
|
42 |
+
vid = cv2.VideoCapture(vid_path)
|
43 |
+
return vid.get(cv2.CAP_PROP_FPS)
|
44 |
+
|
45 |
+
def create_temp_folder(vid_path):
|
46 |
+
if os.path.exists('tmp'):
|
47 |
+
folder_name = vid_path.split('/')[-1].split('.')[0]
|
48 |
+
os.mkdir('tmp/{}'.format(folder_name))
|
49 |
+
else:
|
50 |
+
os.mkdir('tmp')
|
51 |
+
create_temp_folder(vid_path)
|
52 |
+
|
53 |
+
def get_dir(path):
|
54 |
+
if not os.path.exists(path):
|
55 |
+
os.mkdir(path)
|
56 |
+
return path
|
57 |
+
|
58 |
+
def setup_frames(vid_path, slice=None):
|
59 |
+
folder_name = vid_path.split('/')[-1].split('.')[0]
|
60 |
+
images = extract_frames(vid_path)
|
61 |
+
create_temp_folder(vid_path)
|
62 |
+
os.mkdir('tmp/{}/original'.format(folder_name))
|
63 |
+
slices = []
|
64 |
+
for i in tqdm(range(len(images))):
|
65 |
+
cv2.imwrite('tmp/{}/original'.format(folder_name)+'/frame_{}.png'.format(i), images[i])
|
66 |
+
os.mkdir('tmp/{}/upscaled'.format(folder_name))
|
67 |
+
|
68 |
+
def upscale(vid_path, slice=None):
|
69 |
+
folder_name = vid_path.split('/')[-1].split('.')[0]
|
70 |
+
print('extracting frames...')
|
71 |
+
setup_frames(vid_path)
|
72 |
+
print('upscaling...')
|
73 |
+
for i in tqdm(os.listdir('tmp/{}/original'.format(folder_name))):
|
74 |
+
if slice:
|
75 |
+
out = frame_esrgan.upscale_slice(args.model_path, 'tmp/{}/original/{}'.format(folder_name, i), slice)
|
76 |
+
else:
|
77 |
+
out = frame_esrgan.upscale(args.model_path, 'tmp/{}/original/{}'.format(folder_name, i))
|
78 |
+
cv2.imwrite('tmp/{}/upscaled/{}'.format(folder_name, i), out)
|
79 |
+
|
80 |
+
def combine_frames(video_path, new_video_path):
|
81 |
+
folder_name = video_path.split('/')[-1].split('.')[0]
|
82 |
+
images = [img for img in os.listdir('tmp/{}/upscaled'.format(folder_name))]
|
83 |
+
height, width, layers = cv2.imread('tmp/{}/upscaled/frame_0.png'.format(folder_name)).shape
|
84 |
+
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
|
85 |
+
fps = get_fps(video_path)
|
86 |
+
video = cv2.VideoWriter(new_video_path, fourcc, fps, (width, height))
|
87 |
+
for i in tqdm(range(len(images))):
|
88 |
+
video.write(cv2.imread('tmp/{}/upscaled/frame_{}.png'.format(folder_name, i)))
|
89 |
+
cv2.destroyAllWindows()
|
90 |
+
video.release()
|
91 |
+
|
92 |
+
def copy_audio(original_video_path, new_video_path, new_name=''):
|
93 |
+
#ffmpeg -i input_0.mp4 -i input_1.mp4 -c copy -map 0:v:0 -map 1:a:0 -shortest out.mp4
|
94 |
+
tmp_name = new_video_path.split('.')[0] + '_tmp.' + new_video_path.split('.')[-1]
|
95 |
+
subprocess.run([
|
96 |
+
'ffmpeg',
|
97 |
+
'-i',
|
98 |
+
new_video_path,
|
99 |
+
'-i',
|
100 |
+
original_video_path,
|
101 |
+
'-c',
|
102 |
+
'copy',
|
103 |
+
'-map',
|
104 |
+
'0:v:0',
|
105 |
+
'-map',
|
106 |
+
'1:a:0',
|
107 |
+
'-shortest',
|
108 |
+
tmp_name
|
109 |
+
])
|
110 |
+
|
111 |
+
os.replace(tmp_name, new_video_path)
|
112 |
+
|
113 |
+
|
114 |
+
if __name__ == '__main__':
|
115 |
+
if args.model_path and args.input and args.output:
|
116 |
+
try:
|
117 |
+
upscale(args.input, slice=args.slice)
|
118 |
+
combine_frames(args.input, args.output)
|
119 |
+
if args.audio:
|
120 |
+
copy_audio(args.input, args.output)
|
121 |
+
if args.clear_temp:
|
122 |
+
shutil.rmtree('tmp')
|
123 |
+
except Exception as e:
|
124 |
+
print(e)
|
125 |
+
shutil.rmtree('tmp')
|
app.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
def greet(name):
|
4 |
+
return "Hello " + name + "!!"
|
5 |
+
|
6 |
+
iface = gr.Interface(fn=greet, inputs="text", outputs="text")
|
7 |
+
iface.launch()
|
frame_esrgan.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torchvision
|
3 |
+
from basicsr.archs.rrdbnet_arch import RRDBNet
|
4 |
+
from realesrgan import RealESRGANer
|
5 |
+
import cv2
|
6 |
+
import argparse
|
7 |
+
import matplotlib.pyplot as plt
|
8 |
+
import matplotlib.image as mpimg
|
9 |
+
import image_slicer
|
10 |
+
from image_slicer import join
|
11 |
+
from PIL import Image
|
12 |
+
import numpy as np
|
13 |
+
from tqdm import tqdm
|
14 |
+
|
15 |
+
def convert_from_image_to_cv2(img: Image) -> np.ndarray:
|
16 |
+
# return np.asarray(img)
|
17 |
+
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
18 |
+
|
19 |
+
def upscale(model_path, im_path):
|
20 |
+
model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4)
|
21 |
+
upsampler = RealESRGANer(scale=4, model_path=model_path, model=model, tile=0, tile_pad=10, pre_pad=0, half=False)
|
22 |
+
img = cv2.imread(im_path, cv2.IMREAD_UNCHANGED)
|
23 |
+
output, _ = upsampler.enhance(img, outscale=4)
|
24 |
+
return output
|
25 |
+
|
26 |
+
def upscale_slice(model_path, image, slice):
|
27 |
+
width, height = Image.open(image).size
|
28 |
+
tiles = image_slicer.slice(image, slice, save=False)
|
29 |
+
model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4)
|
30 |
+
upsampler = RealESRGANer(scale=4, model_path=model_path, model=model, tile=0, tile_pad=10, pre_pad=0, half=False)
|
31 |
+
for tile in tiles:
|
32 |
+
output, _ = upsampler.enhance(np.array(tile.image), outscale=4)
|
33 |
+
tile.image = Image.fromarray(output)
|
34 |
+
tile.coords = (tile.coords[0]*4, tile.coords[1]*4)
|
35 |
+
return convert_from_image_to_cv2(join(tiles, width=width*4, height=height*4))
|
36 |
+
|
37 |
+
if __name__ == '__main__':
|
38 |
+
parser = argparse.ArgumentParser()
|
39 |
+
parser.add_argument('-m', '--model_path', type=str, help='REQUIRED: specify path of the model being used')
|
40 |
+
parser.add_argument('-i', '--input', type=str, help='REQUIRED: specify path of the image you want to upscale')
|
41 |
+
parser.add_argument('-o', '--output', type=str, help='REQUIRED: specify path where you want to save image')
|
42 |
+
parser.add_argument('-v', '--visualize', action='store_true', help='OPTIONAL: add this to see how image looks before and after upscale')
|
43 |
+
parser.add_argument('-s', '--slice', nargs='?', type=int, const=4, help='OPTIONAL: specify weather to split frames, recommended to use to help with VRAM unless you got a fucken quadro or something')
|
44 |
+
parser.add_argument('-r', '--resize', nargs='?', type=str, const='1920x1080', help="OPTIONAL: specify whether to resize image to a specific resolution. Specify with widthxheight, for example 1920x1080")
|
45 |
+
args = parser.parse_args()
|
46 |
+
|
47 |
+
|
48 |
+
if args.model_path and args.input and args.output:
|
49 |
+
if args.slice:
|
50 |
+
output = upscale_slice(args.model_path, args.input, args.slice)
|
51 |
+
else:
|
52 |
+
output = upscale(args.model_path, args.input)
|
53 |
+
if args.visualize:
|
54 |
+
plt.imshow(mpimg.imread(args.input))
|
55 |
+
plt.show()
|
56 |
+
plt.imshow(output)
|
57 |
+
plt.show()
|
58 |
+
if args.resize:
|
59 |
+
size = tuple(int(i) for i in args.resize.split('x'))
|
60 |
+
output = cv2.resize(output, size)
|
61 |
+
cv2.imwrite(args.output, output)
|
62 |
+
else:
|
63 |
+
print('Error: Missing arguments, check -h, --help for details')
|
64 |
+
|
65 |
+
|
66 |
+
# tiles = image_slicer.slice('tmp/{}/original/{}'.format(folder_name, i), slice, save=False)
|
67 |
+
# print(tiles)
|
68 |
+
# for tile in tiles:
|
69 |
+
# up = frame_esrgan.upscale_slice(args.model_path, np.array(tile.image))
|
70 |
+
# tile.image = Image.fromarray(up, 'RGB')
|
71 |
+
# out = join(tiles)
|
72 |
+
# out.save('tmp/{}/upscaled/{}'.format(folder_name, i.replace('jpg', 'png')))
|
readme.md
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# anime upscaler
|
2 |
+
|
3 |
+
My usage of [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) to upscale anime.
|
4 |
+
|
5 |
+
# Some results:
|
6 |
+
|
7 |
+
## Picture
|
8 |
+
|
9 |
+
Before | After
|
10 |
+
:-------------------------:|:-------------------------:
|
11 |
+
 | 
|
12 |
+
|
13 |
+
## Video
|
14 |
+
|
15 |
+
### Before
|
16 |
+
|
17 |
+
https://user-images.githubusercontent.com/56494763/148880280-fb6488c1-e87c-47e4-81ec-aecc3654c9d6.mp4
|
18 |
+
|
19 |
+
### After
|
20 |
+
|
21 |
+
https://user-images.githubusercontent.com/56494763/148880247-a99d3bd5-eb7e-4371-8de2-68d55cab801e.mp4
|
22 |
+
|
23 |
+
# How To Use
|
24 |
+
|
25 |
+
## Download anime model:
|
26 |
+
|
27 |
+
https://github.com/xinntao/Real-ESRGAN/blob/master/docs/anime_model.md
|
28 |
+
|
29 |
+
## For a single image:
|
30 |
+
|
31 |
+
`python3 frame_esrgan.py -m /path/to/RealESRGAN_x4plus_anime_6B.pth -i path/to/img -o path/to/img.png`
|
32 |
+
|
33 |
+
If memory usage is too high and you need to split frames and stitch back together use the `-s, --split` argument. Default number of splits is 4, but you can make as many as you want.
|
34 |
+
|
35 |
+
If you want to change the final size, one can now add the `-r` or the `--resize` argument, followed by a string indicating the desired resolution, for example we would add `-r 1920x1080` to resize the output to that size.
|
36 |
+
|
37 |
+
Please note that the output of a single image must be a PNG due to alpha channel. Will be fixed in the future hopefully
|
38 |
+
|
39 |
+
## For a video:
|
40 |
+
|
41 |
+
`python3 anime_upscaler.py -m /path/to/RealESRGAN_x4plus_anime_6B.pth -i path/to/video -o path/to/desired/output -s -a`
|
42 |
+
|
43 |
+
If you do not want audio, remove the `-a` at the end
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
basicsr==1.4.2
|
2 |
+
image_slicer==2.1.1
|
3 |
+
matplotlib==3.5.1
|
4 |
+
moviepy==1.0.3
|
5 |
+
opencv_python==4.8.1.78
|
6 |
+
Pillow==10.1.0
|
7 |
+
realesrgan==0.3.0
|
8 |
+
torchvision==0.15.2
|
9 |
+
tqdm==4.66.1
|
test_img/glpa_01_cut_1.mp4
ADDED
Binary file (308 kB). View file
|
|
test_img/random_test_frame.jpg
ADDED
![]() |
test_img/random_test_frame_out.jpg
ADDED
![]() |
test_img/test_clip.mp3
ADDED
Binary file (100 kB). View file
|
|
test_img/test_clip.mp4
ADDED
Binary file (569 kB). View file
|
|
test_img/test_clip_upscaled.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1a9bdd8fe9aeef53b68add9a027e390faf92f0823dc0c26099bbca451c24ce6f
|
3 |
+
size 15765842
|
test_img/test_clip_upscaled_copy.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3c0c0f1875fdd41ea351f8a29b363260987916c5a8fbda0ce4bb3c86436b8f51
|
3 |
+
size 15849639
|