From b81094481c6c34d103ab485a2a8cf658411ce780 Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 6 Mar 2026 03:41:50 +0000 Subject: [PATCH] docs: delete Netgrimoire/Nucking-Futz/scripts/vhs_restore --- .../Nucking-Futz/scripts/vhs_restore.md | 381 ------------------ 1 file changed, 381 deletions(-) delete mode 100644 Netgrimoire/Nucking-Futz/scripts/vhs_restore.md diff --git a/Netgrimoire/Nucking-Futz/scripts/vhs_restore.md b/Netgrimoire/Nucking-Futz/scripts/vhs_restore.md deleted file mode 100644 index 4d815d1..0000000 --- a/Netgrimoire/Nucking-Futz/scripts/vhs_restore.md +++ /dev/null @@ -1,381 +0,0 @@ ---- -title: Video Restoration -description: Short Script to fix vhs captures -published: true -date: 2026-03-06T03:40:10.803Z -tags: -editor: markdown -dateCreated: 2026-03-06T03:40:10.803Z ---- - -# Header -Your content here - - - - - - - - - - - - - - - - - - - - - - - - - - -`#!/usr/bin/env bash -# ============================================================================= -# vhs_restore.sh — Automated VHS Video Restoration Pipeline -# Stages: Deinterlace → Denoise → Colour correct → AI Upscale → Reassemble -# -# Changes from v1: -# - Gentle hqdn3d (2:1:2:2) to prevent motion blocking/pixelation -# - Aggressive colour correction for washed-out VHS footage -# - Live FFmpeg progress shown in terminal (no silent hanging) -# - Logs still saved to /tmp/ for error diagnosis -# ============================================================================= -set -euo pipefail - -# ── Colour output helpers ──────────────────────────────────────────────────── -RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' -CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' -info() { echo -e "${CYAN}[INFO]${NC} $*"; } -success() { echo -e "${GREEN}[OK]${NC} $*"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } -header() { echo -e "\n${BOLD}${CYAN}══ $* ══${NC}"; } - -# ── Default configuration ──────────────────────────────────────────────────── -INPUT_DIR="./input" # Folder containing your source VHS videos -OUTPUT_DIR="./output" # Final restored videos land here -WORK_DIR="./work" # Scratch space (frames, temp files) -REALESRGAN_BIN="./realesrgan-ncnn-vulkan" # Path to Real-ESRGAN binary -REALESRGAN_MODEL="realesr-animevideov3" # Best model for home video -UPSCALE_FACTOR=2 # 2x or 4x (4x is very slow on CPU) -OUTPUT_WIDTH=1920 # Target width used in --no-ai mode -OUTPUT_HEIGHT=1080 # Target height used in --no-ai mode -CRF=16 # Output quality 0-51, lower = better -PRESET="slow" # FFmpeg encode preset -SKIP_UPSCALE=false # --no-ai flag sets this true -KEEP_FRAMES=false # --keep flag sets this true - -# ── Parse CLI flags ────────────────────────────────────────────────────────── -usage() { - cat </dev/null; then - success "$1 found" - else - error "$1 not found. Install with: $2" - exit 1 - fi -} - -check_cmd ffmpeg "sudo apt install ffmpeg" -check_cmd ffprobe "sudo apt install ffmpeg" -check_cmd bc "sudo apt install bc" - -if [[ "$SKIP_UPSCALE" == false ]]; then - if [[ ! -x "$REALESRGAN_BIN" ]]; then - warn "Real-ESRGAN binary not found at: $REALESRGAN_BIN" - echo - echo -e "${YELLOW}To install Real-ESRGAN:${NC}" - echo " 1. Download: https://github.com/xinntao/Real-ESRGAN/releases" - echo " -> realesrgan-ncnn-vulkan-*-ubuntu.zip" - echo " 2. Unzip into this directory" - echo " 3. chmod +x realesrgan-ncnn-vulkan" - echo " 4. Re-run this script" - echo - echo "Or run with --no-ai for FFmpeg-only cleanup (no upscaling)." - exit 1 - fi - success "Real-ESRGAN found" -fi - -# ── Locate input files ─────────────────────────────────────────────────────── -header "Scanning input directory: $INPUT_DIR" - -if [[ ! -d "$INPUT_DIR" ]]; then - error "Input directory not found: $INPUT_DIR" - exit 1 -fi - -mapfile -t VIDEO_FILES < <(find "$INPUT_DIR" -maxdepth 1 \ - -type f \( -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mov" \ - -o -iname "*.mkv" -o -iname "*.mpg" -o -iname "*.mpeg" \ - -o -iname "*.wmv" -o -iname "*.m4v" -o -iname "*.ts" \) \ - | sort) - -if [[ ${#VIDEO_FILES[@]} -eq 0 ]]; then - error "No video files found in $INPUT_DIR" - exit 1 -fi - -info "Found ${#VIDEO_FILES[@]} video file(s):" -for f in "${VIDEO_FILES[@]}"; do echo " * $(basename "$f")"; done - -# ── Helpers ────────────────────────────────────────────────────────────────── -probe() { - ffprobe -v error -select_streams v:0 \ - -show_entries "stream=$2" -of csv=p=0 "$1" 2>/dev/null | head -1 -} - -human_time() { - local s="${1%.*}" - printf '%dh %dm %ds' $((s/3600)) $(( (s%3600)/60 )) $((s%60)) -} - -# ── Create directories ─────────────────────────────────────────────────────── -mkdir -p "$OUTPUT_DIR" "$WORK_DIR" - -# ── Overall stats ──────────────────────────────────────────────────────────── -TOTAL_FILES=${#VIDEO_FILES[@]} -PROCESSED=0 -FAILED=0 -PIPELINE_START=$(date +%s) - -# ════════════════════════════════════════════════════════════════════════════ -# MAIN LOOP -# ════════════════════════════════════════════════════════════════════════════ -for INPUT_FILE in "${VIDEO_FILES[@]}"; do - - BASENAME=$(basename "$INPUT_FILE") - STEM="${BASENAME%.*}" - CLEANED="$WORK_DIR/${STEM}_cleaned.mp4" - FRAMES_IN="$WORK_DIR/${STEM}_frames_in" - FRAMES_OUT="$WORK_DIR/${STEM}_frames_out" - FINAL_OUTPUT="$OUTPUT_DIR/${STEM}_restored.mp4" - - header "Processing: $BASENAME ($((PROCESSED+1))/$TOTAL_FILES)" - FILE_START=$(date +%s) - - # ── Probe source ────────────────────────────────────────────────────────── - FPS=$(probe "$INPUT_FILE" "r_frame_rate") - FPS_DEC=$(echo "scale=3; $FPS" | bc 2>/dev/null || echo "25") - WIDTH=$(probe "$INPUT_FILE" "width") - HEIGHT=$(probe "$INPUT_FILE" "height") - FIELD_ORDER=$(probe "$INPUT_FILE" "field_order") - DURATION=$(ffprobe -v error -show_entries format=duration \ - -of csv=p=0 "$INPUT_FILE" 2>/dev/null | head -1) - - info "Source: ${WIDTH}x${HEIGHT} ${FPS_DEC}fps $(human_time "${DURATION%.*}") field_order=${FIELD_ORDER:-unknown}" - - # Always deinterlace for VHS -- safe even if not flagged as interlaced - if [[ "$FIELD_ORDER" =~ ^(tt|tb|bt|bb)$ ]]; then - DEINTERLACE_FILTER="yadif=mode=1," - info "Interlacing detected — applying yadif deinterlacer" - else - DEINTERLACE_FILTER="yadif=mode=1," - warn "Interlacing not confirmed by probe — applying yadif anyway (safe for VHS)" - fi - - # ── Stage 1: FFmpeg cleanup ─────────────────────────────────────────────── - header "Stage 1/3 — FFmpeg cleanup & colour correction" - info "Watch fps= and speed= for live progress." - info "Corrupt frame warnings are normal for old VHS captures." - echo - - if [[ "$SKIP_UPSCALE" == true ]]; then - SCALE_FILTER="scale=${OUTPUT_WIDTH}:${OUTPUT_HEIGHT}:flags=lanczos," - else - SCALE_FILTER="" - fi - - # Filter chain notes: - # hqdn3d=2:1:2:2 -- gentle denoise; low temporal values (3rd/4th) - # prevent the motion blocking seen with higher values - # unsharp -- moderate sharpening to recover edge detail - # eq -- aggressive colour boost for washed-out VHS - # colorbalance -- corrects the green/yellow cast common in aged VHS - VFILTER="${DEINTERLACE_FILTER}\ -hqdn3d=2:1:2:2,\ -unsharp=3:3:0.5:3:3:0.3,\ -eq=contrast=1.2:brightness=0.05:saturation=1.8:gamma=1.1,\ -colorbalance=rs=0.1:gs=0.0:bs=-0.1,\ -${SCALE_FILTER}\ -format=yuv420p" - - if ! ffmpeg -y -i "$INPUT_FILE" \ - -vf "$VFILTER" \ - -c:v libx264 -crf 18 -preset medium \ - -c:a aac -b:a 192k -ac 2 \ - -stats \ - "$CLEANED" 2>&1 | tee /tmp/ffmpeg_stage1.log | \ - grep --line-buffered -E "(frame=|speed=|error|Error|Invalid)"; then - error "FFmpeg stage 1 failed. Full log: /tmp/ffmpeg_stage1.log" - FAILED=$((FAILED+1)) - continue - fi - - echo - success "Stage 1 complete -> $(du -sh "$CLEANED" | cut -f1)" - - if [[ "$SKIP_UPSCALE" == true ]]; then - cp "$CLEANED" "$FINAL_OUTPUT" - success "Output (no AI): $FINAL_OUTPUT" - PROCESSED=$((PROCESSED+1)) - [[ "$KEEP_FRAMES" == false ]] && rm -f "$CLEANED" - continue - fi - - # ── Stage 2: Extract frames ─────────────────────────────────────────────── - header "Stage 2/3 — Extracting frames for AI upscaling" - mkdir -p "$FRAMES_IN" "$FRAMES_OUT" - - FRAME_COUNT=$(ffprobe -v error -count_packets \ - -select_streams v:0 -show_entries stream=nb_read_packets \ - -of csv=p=0 "$CLEANED" 2>/dev/null | head -1) - FRAME_COUNT=${FRAME_COUNT:-0} - info "Extracting ~${FRAME_COUNT} frames..." - - if ! ffmpeg -y -i "$CLEANED" \ - -vsync 0 -stats \ - "$FRAMES_IN/frame%08d.png" 2>&1 | tee /tmp/ffmpeg_extract.log | \ - grep --line-buffered -E "(frame=|speed=|error|Error)"; then - error "Frame extraction failed. Full log: /tmp/ffmpeg_extract.log" - FAILED=$((FAILED+1)) - continue - fi - - ACTUAL_FRAMES=$(find "$FRAMES_IN" -name "*.png" | wc -l) - echo - success "Extracted $ACTUAL_FRAMES frames" - - # ── Stage 3: Real-ESRGAN ────────────────────────────────────────────────── - header "Stage 3/3 — Real-ESRGAN AI upscaling (${UPSCALE_FACTOR}x)" - warn "Slow on CPU — est. $(echo "scale=0; $ACTUAL_FRAMES * 10 / 60" | bc)-$(echo "scale=0; $ACTUAL_FRAMES * 30 / 60" | bc) minutes" - info "Upscaled frames will appear in: $FRAMES_OUT" - echo - - UPSCALE_START=$(date +%s) - if ! "$REALESRGAN_BIN" \ - -i "$FRAMES_IN" \ - -o "$FRAMES_OUT" \ - -n "$REALESRGAN_MODEL" \ - -s "$UPSCALE_FACTOR" \ - -f png 2>&1 | tee /tmp/realesrgan.log; then - error "Real-ESRGAN failed. Full log: /tmp/realesrgan.log" - FAILED=$((FAILED+1)) - continue - fi - - UPSCALE_END=$(date +%s) - UPSCALE_ELAPSED=$((UPSCALE_END - UPSCALE_START)) - success "AI upscaling complete in $(human_time $UPSCALE_ELAPSED)" - - # ── Reassemble ──────────────────────────────────────────────────────────── - REASSEMBLE_FPS=$(ffprobe -v error -select_streams v:0 \ - -show_entries stream=r_frame_rate \ - -of csv=p=0 "$CLEANED" 2>/dev/null | head -1) - - info "Reassembling video from upscaled frames..." - echo - - if ! ffmpeg -y \ - -framerate "$REASSEMBLE_FPS" \ - -i "$FRAMES_OUT/frame%08d.png" \ - -i "$CLEANED" \ - -map 0:v -map 1:a \ - -c:v libx264 -crf "$CRF" -preset "$PRESET" \ - -c:a copy \ - -movflags +faststart \ - -stats \ - "$FINAL_OUTPUT" 2>&1 | tee /tmp/ffmpeg_reassemble.log | \ - grep --line-buffered -E "(frame=|speed=|error|Error)"; then - error "Reassembly failed. Full log: /tmp/ffmpeg_reassemble.log" - FAILED=$((FAILED+1)) - continue - fi - - # ── Cleanup ─────────────────────────────────────────────────────────────── - if [[ "$KEEP_FRAMES" == false ]]; then - rm -rf "$FRAMES_IN" "$FRAMES_OUT" "$CLEANED" - info "Scratch files cleaned up" - else - info "Frames kept in: $FRAMES_IN / $FRAMES_OUT" - fi - - FILE_END=$(date +%s) - FILE_ELAPSED=$((FILE_END - FILE_START)) - PROCESSED=$((PROCESSED+1)) - - OUT_SIZE=$(du -sh "$FINAL_OUTPUT" | cut -f1) - echo - success "Done: $FINAL_OUTPUT" - info " File size : $OUT_SIZE" - info " Time taken: $(human_time $FILE_ELAPSED)" - -done - -# ════════════════════════════════════════════════════════════════════════════ -# Final summary -# ════════════════════════════════════════════════════════════════════════════ -PIPELINE_END=$(date +%s) -PIPELINE_ELAPSED=$((PIPELINE_END - PIPELINE_START)) - -header "Pipeline Complete" -echo -e " ${GREEN}Processed : $PROCESSED / $TOTAL_FILES${NC}" -[[ $FAILED -gt 0 ]] && echo -e " ${RED}Failed : $FAILED${NC}" -echo -e " Total time: $(human_time $PIPELINE_ELAPSED)" -echo -e " Output dir: $OUTPUT_DIR" -echo - -if [[ $PROCESSED -gt 0 ]]; then - echo "Restored files:" - find "$OUTPUT_DIR" -name "*_restored.mp4" | while read -r f; do - SIZE=$(du -sh "$f" | cut -f1) - echo " * $(basename "$f") ($SIZE)" - done -fi` \ No newline at end of file