File size: 5,799 Bytes
9a3172c fc53afe 9a3172c fc53afe 9a3172c fc53afe 9a3172c | 1 2 3 4 5 6 7 8 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | import tempfile
from enum import Enum
import cv2
import numpy as np
from PIL import Image
try:
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
REALESRGAN_AVAILABLE = True
except ImportError:
REALESRGAN_AVAILABLE = False
class TargetResolution(str, Enum):
FOUR_K = "4K (3840×2160)"
TWO_K = "2K (2560×1440)"
TEN_EIGHTY = "1080p (1920×1080)"
TWO_X = "2x Original"
def get_dimensions(orig_w: int, orig_h: int, target: TargetResolution) -> tuple[int, int]:
"""Calculate target dimensions based on original size and target resolution."""
if target == TargetResolution.FOUR_K:
max_w, max_h = 3840, 2160
elif target == TargetResolution.TWO_K:
max_w, max_h = 2560, 1440
elif target == TargetResolution.TEN_EIGHTY:
max_w, max_h = 1920, 1080
else: # TWO_X
return orig_w * 2, orig_h * 2
scale_w = max_w / orig_w
scale_h = max_h / orig_h
scale = min(scale_w, scale_h)
return int(orig_w * scale), int(orig_h * scale)
def load_upscaler(scale: int = 4) -> "RealESRGANer | None":
if not REALESRGAN_AVAILABLE:
return None
model = RRDBNet(
num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale
)
upscaler = RealESRGANer(
scale=scale,
model_path=f"https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x{scale}plus.pth",
model=model,
tile=512,
tile_pad=10,
pre_pad=0,
half=False, # CPU-safe
)
return upscaler
def opencv_upscale(img_np: np.ndarray, target_w: int, target_h: int) -> np.ndarray:
return cv2.resize(img_np, (target_w, target_h), interpolation=cv2.INTER_LANCZOS4)
def upscale_image(image, method, target_str):
if image is None:
return None, "❌ No image provided.", None
if isinstance(image, str):
image = Image.open(image)
try:
target = TargetResolution(target_str)
except ValueError:
target = TargetResolution.FOUR_K
orig_w, orig_h = image.size
img_np = np.array(image)
target_w, target_h = get_dimensions(orig_w, orig_h, target)
if method == "Real-ESRGAN (AI)" and REALESRGAN_AVAILABLE:
try:
upscaler = load_upscaler(scale=4)
if upscaler:
output, _ = upscaler.enhance(img_np, outscale=target_w / orig_w)
output = cv2.resize(output, (target_w, target_h), interpolation=cv2.INTER_LANCZOS4)
else:
output = opencv_upscale(img_np, target_w, target_h)
method = "Lanczos (fallback: ESRGAN uninitialized)"
except Exception as e:
output = opencv_upscale(img_np, target_w, target_h)
method = f"Lanczos (fallback: {str(e)[:60]})"
else:
output = opencv_upscale(img_np, target_w, target_h)
method = "Lanczos (Fast)"
result_img = Image.fromarray(output)
info = f"✅ Upscaled: {orig_w}×{orig_h} → {target_w}×{target_h} | Method: {method}"
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
result_img.save(tmp.name, format="PNG")
tmp_path = tmp.name
return result_img, info, tmp_path
def upscale_video(video_path, method, target_str, progress_callback=None):
if not video_path:
return None, "❌ No video provided."
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return None, "❌ Could not open video."
try:
orig_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
orig_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
try:
target = TargetResolution(target_str)
except ValueError:
target = TargetResolution.FOUR_K
target_w, target_h = get_dimensions(orig_w, orig_h, target)
out_path = tempfile.mktemp(suffix=".mp4")
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
writer = cv2.VideoWriter(out_path, fourcc, fps, (target_w, target_h))
if not writer.isOpened():
return None, "❌ Could not create output video writer."
try:
upscaler = None
if method == "Real-ESRGAN (AI)" and REALESRGAN_AVAILABLE:
try:
upscaler = load_upscaler(scale=4)
except Exception:
upscaler = None
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
if upscaler:
try:
out_frame, _ = upscaler.enhance(frame_rgb, outscale=target_w / orig_w)
out_frame = cv2.resize(
out_frame, (target_w, target_h), interpolation=cv2.INTER_LANCZOS4
)
except Exception:
out_frame = opencv_upscale(frame_rgb, target_w, target_h)
else:
out_frame = opencv_upscale(frame_rgb, target_w, target_h)
writer.write(cv2.cvtColor(out_frame, cv2.COLOR_RGB2BGR))
frame_idx += 1
if total_frames > 0 and progress_callback:
progress_callback(
(frame_idx / total_frames), f"Processing frame {frame_idx}/{total_frames}"
)
info = f"✅ Video upscaled: {orig_w}×{orig_h} → {target_w}×{target_h} | {total_frames} frames @ {fps:.1f}fps"
finally:
writer.release()
finally:
cap.release()
return out_path, info
|