在数字化时代,我们经常会遇到一些分辨率较低的旧视频,或者是早些年用老旧设备拍摄的珍贵回忆。在 4K 显示器普及的今天,这些 480p 或 720p 的视频显得模糊不清,充满了噪点。

作为 AnduinOS 的开发者,我经常需要处理各种多媒体任务。最近,我基于强大的开源项目 Real-ESRGAN 编写了一个自动化的 Bash 脚本。它能够自动扫描目录下的视频文件,逐帧提取、利用 AI 进行 4 倍超分辨率放大,最后无损合成高清晰度视频。

传统的插值算法(如双线性插值)在放大视频时,只会让图像变得更“糊”。而基于深度学习的 AI 超分(如 Real-ESRGAN)能够“脑补”出图像中缺失的细节和纹理。

然而,Real-ESRGAN 的官方工具主要是针对图片处理的。如果要处理视频,通常的流程是:

  1. 拆帧:将视频拆解为成千上万张图片。
  2. 超分:对每一张图片进行 AI 放大。
  3. 合并:将处理后的图片重新压制成视频,并合并原音频。

手动执行这些步骤非常繁琐,且容易出错。因此,我封装了这个 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}"

如何使用

  1. 创建脚本文件:将上面的代码保存为 upscale.sh
  2. 赋予执行权限
    chmod +x upscale.sh
    
  3. 准备文件: 在脚本同级目录下创建一个名为 videos_to_process 的文件夹,并将你需要变清晰的视频扔进去。
  4. 运行
    ./upscale.sh
    

脚本会自动处理好所有的上下文,并在 enhanced_videos 文件夹中生成 4 倍高清版视频。

Real-ESRGAN(Enhanced Super-Resolution Generative Adversarial Networks)是基于 GAN(生成对抗网络) 的超分辨率复原算法。根据官方文档和论文,它的核心黑科技在于解决了 “盲超分辨率(Blind Super-Resolution)” 的难题。

  1. 什么是“盲”超分? 传统的 AI 模型通常是用简单的“双线性下采样”生成的模糊图来训练的。这就导致了一个问题:AI 只学会了处理“完美的模糊”,而现实世界中的视频(Real-World Data)包含复杂的压缩伪影、传感器噪点、动态模糊。当 AI 遇到这些它没见过的“脏”数据时,往往会失效,甚至放大噪点。

所谓的“盲”,就是指 AI 在处理之前,并不知道这个视频具体经历了什么样的画质损伤。

  1. “纯合成数据”训练 (Pure Synthetic Data) Real-ESRGAN 的杀手锏在于它构建了一套复杂的高阶退化模拟系统。它在训练过程中,使用“纯合成数据”来模拟现实世界中各种复杂的画质损耗,例如:

多重模糊:模拟镜头失焦或运动模糊。

噪声叠加:模拟低光环境下的彩色噪点或高斯噪声。

JPEG 压缩:模拟网络视频传输带来的块状伪影。

通过让 AI 在训练阶段就“见识”过各种糟糕的画质,它在面对我们硬盘里那些模糊的老视频时,就能从容地**“脑补”**出丢失的高频细节(如头发丝、布料纹理)。

结语

这个脚本已经在我的 AnduinOS 上通过了验证,效果非常惊艳。如果你手头有老旧的家庭录像或经典的低清素材,不妨试试看。