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
|