p.sh
· 6.3 KiB · Bash
Bruto
#!/bin/bash
# =================配置区域=================
# 基础路径配置
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 配置
# 模型可选: realesrgan-x4plus (适合真实影像), realesrgan-x4plus-anime (适合动漫)
MODEL_NAME="realesrgan-x4plus"
SCALE=4
# 显卡ID,自动探测一般填0,如果多卡可修改
GPU_ID=0
# 下载链接
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"
# =========================================
# 颜色定义
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# 0. 检查依赖 (ffmpeg)
if ! command -v ffmpeg &> /dev/null; then
echo -e "${RED}Error: ffmpeg 未安装。${NC}"
echo "请运行: sudo apt update && sudo apt install ffmpeg"
exit 1
fi
# 1. 准备目录和环境
mkdir -p "$OUTPUT_DIR"
mkdir -p "$APP_DIR"
# 检查并下载 Real-ESRGAN
if [ ! -f "$EXEC_PATH" ]; then
echo -e "${YELLOW}Real-ESRGAN 可执行文件未找到,准备下载...${NC}"
# 进入 app 目录下载
cd "$APP_DIR" || exit
if [ -f "$ZIP_FILE" ]; then
rm "$ZIP_FILE"
fi
echo "正在下载: $DOWNLOAD_URL"
wget -q --show-progress "$DOWNLOAD_URL"
if [ $? -ne 0 ]; then
echo -e "${RED}下载失败,请检查网络连接。${NC}"
exit 1
fi
echo "正在解压..."
unzip -q "$ZIP_FILE"
# 赋予执行权限
chmod +x realesrgan-ncnn-vulkan
# 清理压缩包
rm "$ZIP_FILE"
echo -e "${GREEN}Real-ESRGAN 安装完成!${NC}"
# 回到基础目录
cd "$BASE_DIR" || exit
else
echo -e "${GREEN}Real-ESRGAN 已安装,跳过下载。${NC}"
fi
# 2. 开始扫描视频并处理
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}在 $INPUT_DIR 中没有找到支持的视频文件。${NC}"
exit 0
fi
echo -e "${GREEN}找到 ${#video_files[@]} 个视频文件,开始处理...${NC}"
# 进度条绘制用的全长字符串
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 "正在处理: ${YELLOW}$BASE_NAME${NC}"
echo "----------------------------------------------------------------"
# 创建临时目录
TMP_FRAMES=$(mktemp -d -p "$BASE_DIR" "tmp_frames_XXXXXX")
OUT_FRAMES=$(mktemp -d -p "$BASE_DIR" "out_frames_XXXXXX")
# Step 1: 拆帧
echo -e "${YELLOW}[1/3] 正在提取帧 (使用 ffmpeg)...${NC}"
# 使用 png 格式以保证无损画质传入 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}拆帧失败,跳过此视频。${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
continue
fi
# 统计总帧数 (ls -1U 速度较快,不排序)
TOTAL_FRAMES=$(ls -1U "$TMP_FRAMES" | wc -l)
echo "总帧数: $TOTAL_FRAMES"
# Step 2: AI 超分
echo -e "${YELLOW}[2/3] 正在进行 AI 超分 (Real-ESRGAN GPU)...${NC}"
echo "模型: $MODEL_NAME | 放大倍数: x$SCALE"
# 1. 以后台方式启动 (&),并将标准输出和错误输出都扔进黑洞
"$EXEC_PATH" \
-i "$TMP_FRAMES" \
-o "$OUT_FRAMES" \
-n "$MODEL_NAME" \
-s "$SCALE" \
-g "$GPU_ID" \
-f png > /dev/null 2>&1 &
PID=$! # 获取进程ID
# 2. 循环监控进度
while kill -0 $PID 2> /dev/null; do
# 统计已生成的文件数
CURRENT_FRAMES=$(ls -1U "$OUT_FRAMES" | wc -l)
# 计算百分比
if [ "$TOTAL_FRAMES" -gt 0 ]; then
PERCENT=$(( CURRENT_FRAMES * 100 / TOTAL_FRAMES ))
else
PERCENT=0
fi
# 防止文件系统统计延迟导致超过 100%
if [ "$PERCENT" -gt 100 ]; then PERCENT=100; fi
# 计算进度条填充长度
FILLED_LEN=$(( PERCENT * BAR_WIDTH / 100 ))
# 打印进度条 (\r 回车不换行)
printf "\r[${GREEN}%-${BAR_WIDTH}s${NC}] %3d%% (%d/%d)" "${FULL_BAR:0:FILLED_LEN}" "$PERCENT" "$CURRENT_FRAMES" "$TOTAL_FRAMES"
sleep 1
done
# 等待进程彻底退出并获取退出码
wait $PID
EXIT_CODE=$?
echo "" # 进度条跑完换个行
if [ $EXIT_CODE -ne 0 ]; then
echo -e "${RED}AI 超分失败,请检查显卡驱动或显存是否足够。${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
continue
fi
# Step 3: 合成视频
echo -e "${YELLOW}[3/3] 正在合成视频 (High Quality H.265)...${NC}"
# 获取原视频帧率
FPS=$(ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate "$INPUT_VIDEO")
# 修复点:给 map 参数加上双引号 "1:a:0?",防止 nullglob 导致参数丢失
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 18 -pix_fmt yuv420p -tag:v hvc1 \
"$OUTPUT_VIDEO"
if [ $? -ne 0 ]; then
echo -e "${YELLOW}H.265 编码失败,尝试回退到 H.264...${NC}"
# 修复点:fallback 命令同样加上引号
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 18 -pix_fmt yuv420p \
"$OUTPUT_VIDEO"
fi
# 清理临时文件
echo -e "${GREEN}清理临时文件...${NC}"
rm -rf "$TMP_FRAMES" "$OUT_FRAMES"
echo -e "${GREEN}完成! 输出文件: $OUTPUT_VIDEO${NC}"
done
echo -e "${GREEN}所有任务已完成。${NC}"
| 1 | #!/bin/bash |
| 2 | |
| 3 | # =================配置区域================= |
| 4 | # 基础路径配置 |
| 5 | BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 6 | INPUT_DIR="$BASE_DIR/videos_to_process" |
| 7 | OUTPUT_DIR="$BASE_DIR/enhanced_videos" |
| 8 | APP_DIR="$BASE_DIR/app" |
| 9 | EXEC_PATH="$APP_DIR/realesrgan-ncnn-vulkan" |
| 10 | |
| 11 | # Real-ESRGAN 配置 |
| 12 | # 模型可选: realesrgan-x4plus (适合真实影像), realesrgan-x4plus-anime (适合动漫) |
| 13 | MODEL_NAME="realesrgan-x4plus" |
| 14 | SCALE=4 |
| 15 | # 显卡ID,自动探测一般填0,如果多卡可修改 |
| 16 | GPU_ID=0 |
| 17 | |
| 18 | # 下载链接 |
| 19 | DOWNLOAD_URL="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesrgan-ncnn-vulkan-20220424-ubuntu.zip" |
| 20 | ZIP_FILE="realesrgan-ncnn-vulkan-20220424-ubuntu.zip" |
| 21 | |
| 22 | # ========================================= |
| 23 | |
| 24 | # 颜色定义 |
| 25 | GREEN='\033[0;32m' |
| 26 | YELLOW='\033[1;33m' |
| 27 | RED='\033[0;31m' |
| 28 | NC='\033[0m' # No Color |
| 29 | |
| 30 | # 0. 检查依赖 (ffmpeg) |
| 31 | if ! command -v ffmpeg &> /dev/null; then |
| 32 | echo -e "${RED}Error: ffmpeg 未安装。${NC}" |
| 33 | echo "请运行: sudo apt update && sudo apt install ffmpeg" |
| 34 | exit 1 |
| 35 | fi |
| 36 | |
| 37 | # 1. 准备目录和环境 |
| 38 | mkdir -p "$OUTPUT_DIR" |
| 39 | mkdir -p "$APP_DIR" |
| 40 | |
| 41 | # 检查并下载 Real-ESRGAN |
| 42 | if [ ! -f "$EXEC_PATH" ]; then |
| 43 | echo -e "${YELLOW}Real-ESRGAN 可执行文件未找到,准备下载...${NC}" |
| 44 | |
| 45 | # 进入 app 目录下载 |
| 46 | cd "$APP_DIR" || exit |
| 47 | |
| 48 | if [ -f "$ZIP_FILE" ]; then |
| 49 | rm "$ZIP_FILE" |
| 50 | fi |
| 51 | |
| 52 | echo "正在下载: $DOWNLOAD_URL" |
| 53 | wget -q --show-progress "$DOWNLOAD_URL" |
| 54 | |
| 55 | if [ $? -ne 0 ]; then |
| 56 | echo -e "${RED}下载失败,请检查网络连接。${NC}" |
| 57 | exit 1 |
| 58 | fi |
| 59 | |
| 60 | echo "正在解压..." |
| 61 | unzip -q "$ZIP_FILE" |
| 62 | |
| 63 | # 赋予执行权限 |
| 64 | chmod +x realesrgan-ncnn-vulkan |
| 65 | |
| 66 | # 清理压缩包 |
| 67 | rm "$ZIP_FILE" |
| 68 | |
| 69 | echo -e "${GREEN}Real-ESRGAN 安装完成!${NC}" |
| 70 | |
| 71 | # 回到基础目录 |
| 72 | cd "$BASE_DIR" || exit |
| 73 | else |
| 74 | echo -e "${GREEN}Real-ESRGAN 已安装,跳过下载。${NC}" |
| 75 | fi |
| 76 | |
| 77 | # 2. 开始扫描视频并处理 |
| 78 | shopt -s nullglob |
| 79 | video_files=("$INPUT_DIR"/*.mp4 "$INPUT_DIR"/*.mov "$INPUT_DIR"/*.avi "$INPUT_DIR"/*.mkv) |
| 80 | |
| 81 | if [ ${#video_files[@]} -eq 0 ]; then |
| 82 | echo -e "${YELLOW}在 $INPUT_DIR 中没有找到支持的视频文件。${NC}" |
| 83 | exit 0 |
| 84 | fi |
| 85 | |
| 86 | echo -e "${GREEN}找到 ${#video_files[@]} 个视频文件,开始处理...${NC}" |
| 87 | |
| 88 | # 进度条绘制用的全长字符串 |
| 89 | FULL_BAR="##################################################" |
| 90 | BAR_WIDTH=50 |
| 91 | |
| 92 | for INPUT_VIDEO in "${video_files[@]}"; do |
| 93 | BASE_NAME=$(basename "$INPUT_VIDEO") |
| 94 | FILENAME="${BASE_NAME%.*}" |
| 95 | OUTPUT_VIDEO="$OUTPUT_DIR/${FILENAME}_x${SCALE}.mp4" |
| 96 | |
| 97 | echo "----------------------------------------------------------------" |
| 98 | echo -e "正在处理: ${YELLOW}$BASE_NAME${NC}" |
| 99 | echo "----------------------------------------------------------------" |
| 100 | |
| 101 | # 创建临时目录 |
| 102 | TMP_FRAMES=$(mktemp -d -p "$BASE_DIR" "tmp_frames_XXXXXX") |
| 103 | OUT_FRAMES=$(mktemp -d -p "$BASE_DIR" "out_frames_XXXXXX") |
| 104 | |
| 105 | # Step 1: 拆帧 |
| 106 | echo -e "${YELLOW}[1/3] 正在提取帧 (使用 ffmpeg)...${NC}" |
| 107 | # 使用 png 格式以保证无损画质传入 AI |
| 108 | ffmpeg -v error -stats -i "$INPUT_VIDEO" -qscale:v 1 -qmin 1 -fps_mode passthrough "$TMP_FRAMES/frame%08d.png" |
| 109 | |
| 110 | if [ $? -ne 0 ]; then |
| 111 | echo -e "${RED}拆帧失败,跳过此视频。${NC}" |
| 112 | rm -rf "$TMP_FRAMES" "$OUT_FRAMES" |
| 113 | continue |
| 114 | fi |
| 115 | |
| 116 | # 统计总帧数 (ls -1U 速度较快,不排序) |
| 117 | TOTAL_FRAMES=$(ls -1U "$TMP_FRAMES" | wc -l) |
| 118 | echo "总帧数: $TOTAL_FRAMES" |
| 119 | |
| 120 | # Step 2: AI 超分 |
| 121 | echo -e "${YELLOW}[2/3] 正在进行 AI 超分 (Real-ESRGAN GPU)...${NC}" |
| 122 | echo "模型: $MODEL_NAME | 放大倍数: x$SCALE" |
| 123 | |
| 124 | # 1. 以后台方式启动 (&),并将标准输出和错误输出都扔进黑洞 |
| 125 | "$EXEC_PATH" \ |
| 126 | -i "$TMP_FRAMES" \ |
| 127 | -o "$OUT_FRAMES" \ |
| 128 | -n "$MODEL_NAME" \ |
| 129 | -s "$SCALE" \ |
| 130 | -g "$GPU_ID" \ |
| 131 | -f png > /dev/null 2>&1 & |
| 132 | |
| 133 | PID=$! # 获取进程ID |
| 134 | |
| 135 | # 2. 循环监控进度 |
| 136 | while kill -0 $PID 2> /dev/null; do |
| 137 | # 统计已生成的文件数 |
| 138 | CURRENT_FRAMES=$(ls -1U "$OUT_FRAMES" | wc -l) |
| 139 | |
| 140 | # 计算百分比 |
| 141 | if [ "$TOTAL_FRAMES" -gt 0 ]; then |
| 142 | PERCENT=$(( CURRENT_FRAMES * 100 / TOTAL_FRAMES )) |
| 143 | else |
| 144 | PERCENT=0 |
| 145 | fi |
| 146 | |
| 147 | # 防止文件系统统计延迟导致超过 100% |
| 148 | if [ "$PERCENT" -gt 100 ]; then PERCENT=100; fi |
| 149 | |
| 150 | # 计算进度条填充长度 |
| 151 | FILLED_LEN=$(( PERCENT * BAR_WIDTH / 100 )) |
| 152 | |
| 153 | # 打印进度条 (\r 回车不换行) |
| 154 | printf "\r[${GREEN}%-${BAR_WIDTH}s${NC}] %3d%% (%d/%d)" "${FULL_BAR:0:FILLED_LEN}" "$PERCENT" "$CURRENT_FRAMES" "$TOTAL_FRAMES" |
| 155 | |
| 156 | sleep 1 |
| 157 | done |
| 158 | |
| 159 | # 等待进程彻底退出并获取退出码 |
| 160 | wait $PID |
| 161 | EXIT_CODE=$? |
| 162 | |
| 163 | echo "" # 进度条跑完换个行 |
| 164 | |
| 165 | if [ $EXIT_CODE -ne 0 ]; then |
| 166 | echo -e "${RED}AI 超分失败,请检查显卡驱动或显存是否足够。${NC}" |
| 167 | rm -rf "$TMP_FRAMES" "$OUT_FRAMES" |
| 168 | continue |
| 169 | fi |
| 170 | |
| 171 | # Step 3: 合成视频 |
| 172 | echo -e "${YELLOW}[3/3] 正在合成视频 (High Quality H.265)...${NC}" |
| 173 | |
| 174 | # 获取原视频帧率 |
| 175 | FPS=$(ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate "$INPUT_VIDEO") |
| 176 | |
| 177 | # 修复点:给 map 参数加上双引号 "1:a:0?",防止 nullglob 导致参数丢失 |
| 178 | ffmpeg -v error -stats \ |
| 179 | -framerate "$FPS" -i "$OUT_FRAMES/frame%08d.png" \ |
| 180 | -i "$INPUT_VIDEO" -map 0:v:0 -map "1:a:0?" -c:a copy \ |
| 181 | -c:v libx265 -preset veryslow -crf 18 -pix_fmt yuv420p -tag:v hvc1 \ |
| 182 | "$OUTPUT_VIDEO" |
| 183 | |
| 184 | if [ $? -ne 0 ]; then |
| 185 | echo -e "${YELLOW}H.265 编码失败,尝试回退到 H.264...${NC}" |
| 186 | # 修复点:fallback 命令同样加上引号 |
| 187 | ffmpeg -v error -stats \ |
| 188 | -framerate "$FPS" -i "$OUT_FRAMES/frame%08d.png" \ |
| 189 | -i "$INPUT_VIDEO" -map 0:v:0 -map "1:a:0?" -c:a copy \ |
| 190 | -c:v libx264 -preset veryslow -crf 18 -pix_fmt yuv420p \ |
| 191 | "$OUTPUT_VIDEO" |
| 192 | fi |
| 193 | |
| 194 | # 清理临时文件 |
| 195 | echo -e "${GREEN}清理临时文件...${NC}" |
| 196 | rm -rf "$TMP_FRAMES" "$OUT_FRAMES" |
| 197 | |
| 198 | echo -e "${GREEN}完成! 输出文件: $OUTPUT_VIDEO${NC}" |
| 199 | done |
| 200 | |
| 201 | echo -e "${GREEN}所有任务已完成。${NC}" |