Merlimhhs commited on
Commit
43404dd
·
verified ·
1 Parent(s): b31c534

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -177
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
- # Batch Cinematic Processor
13
- # - Upload many videos
14
- # - Interpolate to 60 fps using FFmpeg minterpolate
15
- # - Upscale to 1440p
16
- # - Return a ZIP with all processed videos
17
- #
18
- # Requirements:
19
- # pip install gradio
20
- # ffmpeg installed and available in PATH
21
- #
22
- # Notes:
23
- # - No reverse is applied here.
24
- # - This is designed for CPU-friendly, stable batch processing.
25
- # =========================================================
26
-
27
-
28
- def run_ffmpeg(cmd: List[str]) -> None:
29
- proc = subprocess.run(
30
- cmd,
31
- stdout=subprocess.PIPE,
32
- stderr=subprocess.PIPE,
33
- text=True,
34
- )
35
- if proc.returncode != 0:
36
- raise RuntimeError(proc.stderr[-5000:])
37
-
38
-
39
- def safe_stem(path: str) -> str:
40
- return Path(path).stem.replace(" ", "_")
41
-
42
-
43
- def process_one_video(
44
- input_path: str,
45
- output_dir: Path,
46
- target_width: int,
47
- target_height: int,
48
- target_fps: int,
49
- crf: int,
50
- preset: str,
51
- ) -> Path:
52
- input_path = str(Path(input_path).resolve())
53
- name = safe_stem(input_path)
54
- output_path = output_dir / f"{name}_{target_fps}fps_{target_height}p.mp4"
55
-
56
- # Minterpolate first, then upscale.
57
- # This tends to preserve motion continuity better than upscaling first.
58
- vf = (
59
- f"minterpolate=fps={target_fps}:mi_mode=mci:mc_mode=aobmc:"
60
- f"me_mode=bidir:me=epzs:vsbmc=1:scd=fdiff,"
61
- f"scale={target_width}:{target_height}:flags=lanczos"
62
- )
63
-
64
- cmd = [
65
- "ffmpeg",
66
- "-y",
67
- "-i", input_path,
68
- "-vf", vf,
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
- zip_out = gr.File(label="ZIP final")
169
- info = gr.Textbox(label="Status", lines=4)
170
-
171
- process_btn.click(
172
- fn=batch_process,
173
- inputs=[videos, target_width, target_height, target_fps, crf, preset],
174
- outputs=[zip_out, info],
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()