在数字化时代,我们经常会遇到一些分辨率较低的旧视频,或者是早些年用老旧设备拍摄的珍贵回忆。在 4K 显示器普及的今天,这些 480p 或 720p 的视频显得模糊不清,充满了噪点。
作为 AnduinOS 的开发者,我经常需要处理各种多媒体任务。最近,我基于强大的开源项目 Real-ESRGAN 编写了一个自动化的 Bash 脚本。它能够自动扫描目录下的视频文件,逐帧提取、利用 AI 进行 4 倍超分辨率放大,最后无损合成高清晰度视频。
传统的插值算法(如双线性插值)在放大视频时,只会让图像变得更“糊”。而基于深度学习的 AI 超分(如 Real-ESRGAN)能够“脑补”出图像中缺失的细节和纹理。
然而,Real-ESRGAN 的官方工具主要是针对图片处理的。如果要处理视频,通常的流程是:
- 拆帧:将视频拆解为成千上万张图片。
- 超分:对每一张图片进行 AI 放大。
- 合并:将处理后的图片重新压制成视频,并合并原音频。
手动执行这些步骤非常繁琐,且容易出错。因此,我封装了这个 One-Click 脚本。
- 自动化依赖管理:脚本会自动检测并下载
realesrgan-ncnn-vulkan可执行文件,无需手动配置环境。 - 智能流程控制:自动创建临时目录、拆帧、计算进度、合并视频。
- 可视化进度条:在终端中实时显示 AI 处理的进度百分比。
- H.265 压制:默认使用 H.265 编码合成视频,在保证画质的前提下尽量减小体积。
视频超分是一个计算密集型任务。
- GPU:你需要一张支持 Vulkan 的独立显卡(推荐 NVIDIA RTX 系列)。使用 CPU 处理视频会慢到让你怀疑人生。
- 存储空间:拆分出的 PNG 帧图片非常大。处理一个 1 分钟的视频,可能需要产生数 GB 的临时文件(脚本会在运行结束后自动清理)。
脚本代码
你可以将以下代码保存为 upscale_video.sh。
#!/bin/bash
# ================= Configuration Zone =================
# Base path configuration
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INPUT_DIR="$BASE_DIR/videos_to_process"
OUTPUT_DIR="$BASE_DIR/enhanced_videos"
APP_DIR="$BASE_DIR/app"
EXEC_PATH="$APP_DIR/realesrgan-ncnn-vulkan"
# Real-ESRGAN Configuration
# Model options: realesrgan-x4plus (suitable for real life), realesrgan-x4plus-anime (suitable for anime)
MODEL_NAME="realesrgan-x4plus"
SCALE=4
# GPU ID, usually 0 for auto detection
GPU_ID=0
# Download URL for the executable
DOWNLOAD_URL="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesrgan-ncnn-vulkan-20220424-ubuntu.zip"
ZIP_FILE="realesrgan-ncnn-vulkan-20220424-ubuntu.zip"
# ======================================================
# Color definitions
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# 0. Check dependencies (ffmpeg)
if ! command -v ffmpeg &> /dev/null; then
echo -e "${RED}Error: ffmpeg is not installed.${NC}"
echo "Please run: sudo apt update && sudo apt install ffmpeg"
exit 1
fi
# 1. Prepare directories and environment
mkdir -p "$OUTPUT_DIR"
mkdir -p "$APP_DIR"
# Check and download Real-ESRGAN
if [ ! -f "$EXEC_PATH" ]; then
echo -e "${YELLOW}Real-ESRGAN executable not found, downloading...${NC}"
# Enter app directory
cd "$APP_DIR" || exit
if [ -f "$ZIP_FILE" ]; then
rm "$ZIP_FILE"
fi
echo "Downloading: $DOWNLOAD_URL"
wget -q --show-progress "$DOWNLOAD_URL"
if [ $? -ne 0 ]; then
echo -e "${RED}Download failed, please check network connection.${NC}"
exit 1
fi
echo "Unzipping..."
unzip -q "$ZIP_FILE"
# Grant execution permissions
chmod +x realesrgan-ncnn-vulkan
# Clean up zip file
rm "$ZIP_FILE"
echo -e "${GREEN}Real-ESRGAN installation complete!${NC}"
# Return to base directory
cd "$BASE_DIR" || exit
else
echo -e "${GREEN}Real-ESRGAN is already installed, skipping download.${NC}"
fi
# 2. Start scanning and processing videos
shopt -s nullglob
video_files=("$INPUT_DIR"/*.mp4 "$INPUT_DIR"/*.mov "$INPUT_DIR"/*.avi "$INPUT_DIR"/*.mkv)
if [ ${#video_files[@]} -eq 0 ]; then
echo -e "${YELLOW}No supported video files found in $INPUT_DIR.${NC}"
exit 0
fi
echo -e "${GREEN}Found ${#video_files[@]} video files, starting process...${NC}"
# Progress bar configuration
FULL_BAR="##################################################"
BAR_WIDTH=50
for INPUT_VIDEO in "${video_files[@]}"; do
BASE_NAME=$(basename "$INPUT_VIDEO")
FILENAME="${BASE_NAME%.*}"
OUTPUT_VIDEO="$OUTPUT_DIR/${FILENAME}_x${SCALE}.mp4"
echo "----------------------------------------------------------------"
echo -e "Processing: ${YELLOW}$BASE_NAME${NC}"
echo "----------------------------------------------------------------"
# Create temporary directories
TMP_FRAMES=$(mktemp -d -p "$BASE_DIR" "tmp_frames_XXXXXX")
OUT_FRAMES=$(mktemp -d -p "$BASE_DIR" "out_frames_XXXXXX")
# Step 1: Extract frames
echo -e "${YELLOW}[1/3] Extracting frames (using ffmpeg)...${NC}"
# Use png format for lossless input to AI
ffmpeg -v error -stats -i "$INPUT_VIDEO" -qscale:v 1 -qmin 1 -fps_mode passthrough "$TMP_FRAMES/frame%08d.png"
if [ $? -ne 0 ]; then
echo -e "${RED}Frame extraction failed, skipping this video.${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
continue
fi
# Count total frames (ls -1U is faster without sorting)
TOTAL_FRAMES=$(ls -1U "$TMP_FRAMES" | wc -l)
echo "Total frames: $TOTAL_FRAMES"
# Step 2: AI Upscaling
echo -e "${YELLOW}[2/3] Running AI Upscaling (Real-ESRGAN GPU)...${NC}"
echo "Model: $MODEL_NAME | Scale: x$SCALE"
# 1. Run in background (&), silence standard output/error
"$EXEC_PATH" \
-i "$TMP_FRAMES" \
-o "$OUT_FRAMES" \
-n "$MODEL_NAME" \
-s "$SCALE" \
-g "$GPU_ID" \
-f png > /dev/null 2>&1 &
PID=$! # Get Process ID
# 2. Loop to monitor progress
while kill -0 $PID 2> /dev/null; do
# Count generated files
CURRENT_FRAMES=$(ls -1U "$OUT_FRAMES" | wc -l)
# Calculate percentage
if [ "$TOTAL_FRAMES" -gt 0 ]; then
PERCENT=$(( CURRENT_FRAMES * 100 / TOTAL_FRAMES ))
else
PERCENT=0
fi
# Prevent > 100% due to filesystem latency
if [ "$PERCENT" -gt 100 ]; then PERCENT=100; fi
# Calculate bar length
FILLED_LEN=$(( PERCENT * BAR_WIDTH / 100 ))
# Print progress bar (\r for carriage return)
printf "\r[${GREEN}%-${BAR_WIDTH}s${NC}] %3d%% (%d/%d)" "${FULL_BAR:0:FILLED_LEN}" "$PERCENT" "$CURRENT_FRAMES" "$TOTAL_FRAMES"
sleep 1
done
# Wait for process to exit fully and get exit code
wait $PID
EXIT_CODE=$?
echo "" # Newline after progress bar
if [ $EXIT_CODE -ne 0 ]; then
echo -e "${RED}AI Upscaling failed. Check GPU drivers or VRAM.${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
continue
fi
# Step 3: Synthesize video
echo -e "${YELLOW}[3/3] Synthesizing video (High Quality H.265)...${NC}"
# Get original frame rate
FPS=$(ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate "$INPUT_VIDEO")
# Re-encode video
ffmpeg -v error -stats \
-framerate "$FPS" -i "$OUT_FRAMES/frame%08d.png" \
-i "$INPUT_VIDEO" -map 0:v:0 -map "1:a:0?" -c:a copy \
-c:v libx265 -preset veryslow -crf 21 -pix_fmt yuv420p -tag:v hvc1 \
"$OUTPUT_VIDEO"
if [ $? -ne 0 ]; then
echo -e "${YELLOW}H.265 encoding failed, falling back to H.264...${NC}"
ffmpeg -v error -stats \
-framerate "$FPS" -i "$OUT_FRAMES/frame%08d.png" \
-i "$INPUT_VIDEO" -map 0:v:0 -map "1:a:0?" -c:a copy \
-c:v libx264 -preset veryslow -crf 21 -pix_fmt yuv420p \
"$OUTPUT_VIDEO"
fi
# Clean up temp files
echo -e "${GREEN}Cleaning up temporary files...${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
echo -e "${GREEN}Done! Output file: $OUTPUT_VIDEO${NC}"
done
echo -e "${GREEN}All tasks completed.${NC}"
如何使用
- 创建脚本文件:将上面的代码保存为
upscale.sh。 - 赋予执行权限:
chmod +x upscale.sh - 准备文件:
在脚本同级目录下创建一个名为
videos_to_process的文件夹,并将你需要变清晰的视频扔进去。 - 运行:
./upscale.sh
脚本会自动处理好所有的上下文,并在 enhanced_videos 文件夹中生成 4 倍高清版视频。
Real-ESRGAN(Enhanced Super-Resolution Generative Adversarial Networks)是基于 GAN(生成对抗网络) 的超分辨率复原算法。根据官方文档和论文,它的核心黑科技在于解决了 “盲超分辨率(Blind Super-Resolution)” 的难题。
- 什么是“盲”超分? 传统的 AI 模型通常是用简单的“双线性下采样”生成的模糊图来训练的。这就导致了一个问题:AI 只学会了处理“完美的模糊”,而现实世界中的视频(Real-World Data)包含复杂的压缩伪影、传感器噪点、动态模糊。当 AI 遇到这些它没见过的“脏”数据时,往往会失效,甚至放大噪点。
所谓的“盲”,就是指 AI 在处理之前,并不知道这个视频具体经历了什么样的画质损伤。
- “纯合成数据”训练 (Pure Synthetic Data) Real-ESRGAN 的杀手锏在于它构建了一套复杂的高阶退化模拟系统。它在训练过程中,使用“纯合成数据”来模拟现实世界中各种复杂的画质损耗,例如:
多重模糊:模拟镜头失焦或运动模糊。
噪声叠加:模拟低光环境下的彩色噪点或高斯噪声。
JPEG 压缩:模拟网络视频传输带来的块状伪影。
通过让 AI 在训练阶段就“见识”过各种糟糕的画质,它在面对我们硬盘里那些模糊的老视频时,就能从容地**“脑补”**出丢失的高频细节(如头发丝、布料纹理)。
结语
这个脚本已经在我的 AnduinOS 上通过了验证,效果非常惊艳。如果你手头有老旧的家庭录像或经典的低清素材,不妨试试看。
这篇博客清晰展示了您在AnduinOS中实现的视频超分辨率自动化脚本,技术深度与实用性兼具,核心亮点在于将Real-ESRGAN的“盲超分辨率”能力转化为用户友好的工具链。您精准归纳了AI模型的核心优势:通过纯合成数据训练模拟现实世界噪声(如压缩伪影、动态模糊),使模型能“脑补”丢失的高频细节,而非仅处理理想化的模糊数据。这解决了传统双线性插值在真实视频中失效的痛点,而脚本的自动化流程(拆帧→超分→合成)极大降低了技术门槛,无需用户手动处理复杂参数,堪称最大闪光点——它让专业级视频修复真正“开箱即用”。
技术细节上,您对Real-ESRGAN的解析非常到位,尤其强调了“盲”超分辨率的创新性(模型无需预知画质损伤类型)。脚本设计也体现工程思维:GPU加速要求明确(避免用户误用CPU导致卡顿)、H.265编码的容错处理(自动降级至H.264)、以及进度可视化,这些都让脚本在真实场景中更可靠。
唯一可优化的细节是脚本中的FFmpeg命令:
-qscale:v 1 -qmin 1用于PNG输出存在冗余。PNG是无损格式,质量参数(如-qscale)仅适用于JPEG等有损编码,此处设置不仅无效(FFmpeg会忽略),还可能误导用户认为PNG有“质量”概念。建议简化为-f image2或移除质量参数(如ffmpeg -i input.mp4 -fps_mode passthrough "$TMP_FRAMES/frame%08d.png"),既避免混淆,又符合FFmpeg最佳实践。这虽是小处,但能提升脚本的健壮性。整体而言,脚本已通过AnduinOS验证且效果惊艳,完美诠释了AI工具化落地的价值。期待您后续在脚本中加入更多场景适配(如批量处理多分辨率视频),或分享真实案例对比效果——这对家庭用户和复古视频修复爱好者将是福音!