Update app.py
Browse files
app.py
CHANGED
|
@@ -1,179 +1,71 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import shutil
|
| 3 |
-
import subprocess
|
| 4 |
-
import tempfile
|
| 5 |
-
from pathlib import Path
|
| 6 |
-
from typing import List, Tuple
|
| 7 |
-
|
| 8 |
import gradio as gr
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
#
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
#
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
#
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
"-c:v", "libx264",
|
| 70 |
-
"-preset", preset,
|
| 71 |
-
"-crf", str(crf),
|
| 72 |
-
"-pix_fmt", "yuv420p",
|
| 73 |
-
"-movflags", "+faststart",
|
| 74 |
-
str(output_path),
|
| 75 |
-
]
|
| 76 |
-
|
| 77 |
-
run_ffmpeg(cmd)
|
| 78 |
-
return output_path
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def batch_process(
|
| 82 |
-
videos: List[str],
|
| 83 |
-
target_width: int,
|
| 84 |
-
target_height: int,
|
| 85 |
-
target_fps: int,
|
| 86 |
-
crf: int,
|
| 87 |
-
preset: str,
|
| 88 |
-
progress=gr.Progress(track_tqdm=False),
|
| 89 |
-
) -> Tuple[str, str]:
|
| 90 |
-
if not videos:
|
| 91 |
-
raise gr.Error("Envie pelo menos um vídeo.")
|
| 92 |
-
|
| 93 |
-
# Normalize values.
|
| 94 |
-
target_width = int(target_width)
|
| 95 |
-
target_height = int(target_height)
|
| 96 |
-
target_fps = int(target_fps)
|
| 97 |
-
crf = int(crf)
|
| 98 |
-
|
| 99 |
-
if target_width % 2 != 0:
|
| 100 |
-
target_width += 1
|
| 101 |
-
if target_height % 2 != 0:
|
| 102 |
-
target_height += 1
|
| 103 |
-
|
| 104 |
-
workdir = Path(tempfile.mkdtemp(prefix="batch_cinematic_"))
|
| 105 |
-
outdir = workdir / "outputs"
|
| 106 |
-
outdir.mkdir(parents=True, exist_ok=True)
|
| 107 |
-
|
| 108 |
-
processed_files: List[Path] = []
|
| 109 |
-
total = len(videos)
|
| 110 |
-
|
| 111 |
-
for idx, video in enumerate(videos, start=1):
|
| 112 |
-
progress((idx - 1) / total, desc=f"Processando {idx}/{total}: {Path(video).name}")
|
| 113 |
-
processed = process_one_video(
|
| 114 |
-
video,
|
| 115 |
-
outdir,
|
| 116 |
-
target_width=target_width,
|
| 117 |
-
target_height=target_height,
|
| 118 |
-
target_fps=target_fps,
|
| 119 |
-
crf=crf,
|
| 120 |
-
preset=preset,
|
| 121 |
-
)
|
| 122 |
-
processed_files.append(processed)
|
| 123 |
-
|
| 124 |
-
progress(0.95, desc="Montando ZIP...")
|
| 125 |
-
zip_base = workdir / "batch_output"
|
| 126 |
-
zip_path = shutil.make_archive(str(zip_base), "zip", root_dir=outdir)
|
| 127 |
-
progress(1.0, desc="Concluído")
|
| 128 |
-
|
| 129 |
-
summary = (
|
| 130 |
-
f"{len(processed_files)} vídeo(s) processado(s).\n"
|
| 131 |
-
f"Saída: {target_fps} fps | {target_width}x{target_height}\n"
|
| 132 |
-
f"Arquivo ZIP pronto."
|
| 133 |
-
)
|
| 134 |
-
return zip_path, summary
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
with gr.Blocks(title="Batch Cinematic Processor") as demo:
|
| 138 |
-
gr.Markdown(
|
| 139 |
-
"# Batch Cinematic Processor\n"
|
| 140 |
-
"Envie vários vídeos e receba um ZIP com tudo processado em 60 fps e 1440p.\n"
|
| 141 |
-
"Sem reverse automático."
|
| 142 |
-
)
|
| 143 |
-
|
| 144 |
-
with gr.Row():
|
| 145 |
-
videos = gr.File(
|
| 146 |
-
label="Vídeos de entrada",
|
| 147 |
-
file_count="multiple",
|
| 148 |
-
type="filepath",
|
| 149 |
-
file_types=[".mp4", ".mov", ".mkv", ".webm", ".avi"],
|
| 150 |
-
)
|
| 151 |
-
|
| 152 |
-
with gr.Row():
|
| 153 |
-
target_width = gr.Number(label="Largura", value=2560, precision=0)
|
| 154 |
-
target_height = gr.Number(label="Altura", value=1440, precision=0)
|
| 155 |
-
target_fps = gr.Number(label="FPS de saída", value=60, precision=0)
|
| 156 |
-
|
| 157 |
-
with gr.Row():
|
| 158 |
-
crf = gr.Slider(label="Qualidade (CRF menor = melhor)", minimum=14, maximum=24, value=18, step=1)
|
| 159 |
-
preset = gr.Dropdown(
|
| 160 |
-
label="Preset do encoder",
|
| 161 |
-
choices=["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower"],
|
| 162 |
-
value="slow",
|
| 163 |
-
)
|
| 164 |
-
|
| 165 |
-
process_btn = gr.Button("Processar lote")
|
| 166 |
-
|
| 167 |
with gr.Row():
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
if __name__ == "__main__":
|
| 179 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import os
|
| 3 |
+
import zipfile
|
| 4 |
+
import cv2
|
| 5 |
+
import urllib.request
|
| 6 |
+
from basicsr.archs.rrdbnet_arch import RRDBNet
|
| 7 |
+
from realesrgan import RealESRGANer
|
| 8 |
+
|
| 9 |
+
# Baixa o modelo específico para estética anime/ilustração se ele não estiver no servidor
|
| 10 |
+
model_path = 'RealESRGAN_x4plus_anime_6B.pth'
|
| 11 |
+
if not os.path.isfile(model_path):
|
| 12 |
+
url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth'
|
| 13 |
+
urllib.request.urlretrieve(url, model_path)
|
| 14 |
+
|
| 15 |
+
# Configura a arquitetura neural
|
| 16 |
+
model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4)
|
| 17 |
+
|
| 18 |
+
# Inicializa o motor de Upscale
|
| 19 |
+
upsampler = RealESRGANer(
|
| 20 |
+
scale=4,
|
| 21 |
+
model_path=model_path,
|
| 22 |
+
model=model,
|
| 23 |
+
half=False # Essencial manter como False para rodar no plano grátis (CPU)
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
def process_batch(files):
|
| 27 |
+
if not files:
|
| 28 |
+
return None
|
| 29 |
+
|
| 30 |
+
out_dir = "imagens_prontas"
|
| 31 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 32 |
+
|
| 33 |
+
# Limpa a pasta antes de uma nova rodada para não misturar os lotes
|
| 34 |
+
for f in os.listdir(out_dir):
|
| 35 |
+
os.remove(os.path.join(out_dir, f))
|
| 36 |
+
|
| 37 |
+
# Processa cada imagem enviada
|
| 38 |
+
for file in files:
|
| 39 |
+
filename = os.path.basename(file.name)
|
| 40 |
+
img = cv2.imread(file.name, cv2.IMREAD_UNCHANGED)
|
| 41 |
+
|
| 42 |
+
if img is not None:
|
| 43 |
+
# Aplica o upscale 4x
|
| 44 |
+
output, _ = upsampler.enhance(img, outscale=4)
|
| 45 |
+
cv2.imwrite(os.path.join(out_dir, filename), output)
|
| 46 |
+
|
| 47 |
+
# Cria o arquivo ZIP final
|
| 48 |
+
zip_filename = "Imagens_Alta_Resolucao.zip"
|
| 49 |
+
with zipfile.ZipFile(zip_filename, 'w') as zipf:
|
| 50 |
+
for folderName, subfolders, filenames in os.walk(out_dir):
|
| 51 |
+
for filename in filenames:
|
| 52 |
+
filePath = os.path.join(folderName, filename)
|
| 53 |
+
zipf.write(filePath, os.path.basename(filePath))
|
| 54 |
+
|
| 55 |
+
return zip_filename
|
| 56 |
+
|
| 57 |
+
# Constrói a interface visual do celular
|
| 58 |
+
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
| 59 |
+
gr.Markdown("# 🚀 Upscale em Lote")
|
| 60 |
+
gr.Markdown("Envie várias imagens de uma vez. O servidor processará o upscale preservando a arte original e entregará um arquivo ZIP.")
|
| 61 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
with gr.Row():
|
| 63 |
+
with gr.Column():
|
| 64 |
+
image_inputs = gr.File(label="1. Selecione as imagens", file_count="multiple", type="filepath")
|
| 65 |
+
btn_process = gr.Button("2. Iniciar Upscale", variant="primary")
|
| 66 |
+
with gr.Column():
|
| 67 |
+
zip_output = gr.File(label="3. Download do ZIP Pronto")
|
| 68 |
+
|
| 69 |
+
btn_process.click(fn=process_batch, inputs=image_inputs, outputs=zip_output)
|
| 70 |
+
|
| 71 |
+
app.launch()
|
|
|
|
|
|
|
|
|