first changes to original scripts
This commit is contained in:
12
README.md
12
README.md
@@ -31,17 +31,7 @@ python scene_cutter.py "path/to/your/video.mkv"
|
||||
```
|
||||
|
||||
### 3. Encode Segments
|
||||
Choose one of the encoder scripts to process the files from the `cuts/` directory. Encoded files will be placed in the `segments/` directory.
|
||||
|
||||
**Option A: VMAF-based Encoding (Recommended)**
|
||||
Use [`vmaf_encoder.py`](vmaf_encoder.py) to encode each segment to a target VMAF quality level.
|
||||
|
||||
```bash
|
||||
python vmaf_encoder.py --target-vmaf 96.0
|
||||
```
|
||||
|
||||
**Option B: Static CRF Encoding**
|
||||
Use [`static_encoder.py`](static_encoder.py) to encode all segments with a single, fixed CRF value.
|
||||
Use [`static_encoder.py`](static_encoder.py) to encode all segments from the `cuts/` directory with a single, fixed CRF value. Encoded files will be placed in the `segments/` directory.
|
||||
|
||||
```bash
|
||||
python static_encoder.py --crf 27
|
||||
|
||||
@@ -66,7 +66,44 @@ def get_video_duration(video_path):
|
||||
|
||||
# --- Core Logic Functions ---
|
||||
|
||||
def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
||||
def detect_crop(video_path, hwaccel=None):
|
||||
"""
|
||||
Detects black bars using FFmpeg's cropdetect filter and returns the crop filter string.
|
||||
Analyzes the first 60 seconds of the video for efficiency.
|
||||
"""
|
||||
print("\nStarting crop detection...")
|
||||
command = ['ffmpeg', '-hide_banner']
|
||||
if hwaccel:
|
||||
command.extend(['-hwaccel', hwaccel])
|
||||
|
||||
# Analyze a portion of the video to find crop values
|
||||
command.extend(['-i', video_path, '-t', '60', '-vf', 'cropdetect', '-f', 'null', '-'])
|
||||
|
||||
try:
|
||||
# Using Popen to read stderr line by line
|
||||
process = subprocess.Popen(command, stderr=subprocess.PIPE, text=True, encoding='utf-8')
|
||||
|
||||
last_crop_line = ""
|
||||
for line in iter(process.stderr.readline, ''):
|
||||
if 'crop=' in line:
|
||||
last_crop_line = line.strip()
|
||||
|
||||
process.wait()
|
||||
|
||||
if last_crop_line:
|
||||
# Extract the crop filter part, e.g., "crop=1920:800:0:140"
|
||||
crop_filter = last_crop_line.split('] ')[-1]
|
||||
print(f"Crop detection finished. Recommended filter: {crop_filter}")
|
||||
return crop_filter
|
||||
else:
|
||||
print("Could not determine crop. No black bars detected or an error occurred.")
|
||||
return None
|
||||
|
||||
except (FileNotFoundError, Exception) as e:
|
||||
print(f"\nAn error occurred during crop detection: {e}")
|
||||
return None
|
||||
|
||||
def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4, crop_filter=None):
|
||||
"""Uses FFmpeg to detect scene changes and saves timestamps to a JSON file."""
|
||||
print(f"\nStarting scene detection for: {os.path.basename(video_path)}")
|
||||
command = ['ffmpeg', '-hide_banner']
|
||||
@@ -74,7 +111,13 @@ def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
||||
print(f"Attempting to use hardware acceleration: {hwaccel}")
|
||||
command.extend(['-hwaccel', hwaccel])
|
||||
|
||||
filter_string = f"select='gt(scene,{threshold})',showinfo"
|
||||
filters = []
|
||||
if crop_filter:
|
||||
print(f"Applying crop filter during scene detection: {crop_filter}")
|
||||
filters.append(crop_filter)
|
||||
filters.append(f"select='gt(scene,{threshold})',showinfo")
|
||||
filter_string = ",".join(filters)
|
||||
|
||||
command.extend(['-i', video_path, '-vf', filter_string, '-f', 'null', '-'])
|
||||
|
||||
try:
|
||||
@@ -105,7 +148,7 @@ def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
||||
print(f"\nAn error occurred during scene detection: {e}")
|
||||
return False
|
||||
|
||||
def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=None):
|
||||
def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=None, crop_filter=None):
|
||||
"""Cuts a video into segments, ensuring no segment exceeds a maximum length."""
|
||||
print(f"\nStarting segment cutting for: {os.path.basename(video_path)}")
|
||||
try:
|
||||
@@ -148,7 +191,13 @@ def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=Non
|
||||
if hwaccel:
|
||||
command.extend(['-hwaccel', hwaccel])
|
||||
|
||||
command.extend(['-i', video_path, '-c:v', 'utvideo', '-an', '-sn', '-dn', '-map_metadata', '-1', '-map_chapters', '-1', '-f', 'segment', '-segment_times', segment_times_str, '-segment_start_number', '1', '-reset_timestamps', '1', output_pattern])
|
||||
command.extend(['-i', video_path])
|
||||
|
||||
if crop_filter:
|
||||
print(f"Applying crop filter during cutting: {crop_filter}")
|
||||
command.extend(['-vf', crop_filter])
|
||||
|
||||
command.extend(['-c:v', 'utvideo', '-an', '-sn', '-dn', '-map_metadata', '-1', '-map_chapters', '-1', '-f', 'segment', '-segment_times', segment_times_str, '-segment_start_number', '1', '-reset_timestamps', '1', output_pattern])
|
||||
|
||||
print("\nStarting FFmpeg to cut all segments in a single pass...")
|
||||
try:
|
||||
@@ -167,6 +216,11 @@ def main():
|
||||
formatter_class=argparse.RawTextHelpFormatter
|
||||
)
|
||||
parser.add_argument("video_path", help="Path to the input video file.")
|
||||
parser.add_argument(
|
||||
"--autocrop",
|
||||
action='store_true',
|
||||
help="Automatically detect and apply cropping to remove black bars. Default: False"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--so", "--sceneonly",
|
||||
action='store_true',
|
||||
@@ -195,21 +249,24 @@ def main():
|
||||
json_path = f"{base_name}.scenes.json"
|
||||
|
||||
hwaccel_method = get_best_hwaccel()
|
||||
crop_filter = None
|
||||
if args.autocrop:
|
||||
crop_filter = detect_crop(args.video_path, hwaccel_method)
|
||||
|
||||
if args.sceneonly:
|
||||
detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold)
|
||||
detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold, crop_filter)
|
||||
sys.exit(0)
|
||||
|
||||
# --- Full Workflow (Detect if needed, then Cut) ---
|
||||
if not os.path.isfile(json_path):
|
||||
print("--- Scene file not found, running detection first ---")
|
||||
if not detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold):
|
||||
if not detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold, crop_filter):
|
||||
print("\nScene detection failed. Aborting process.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"--- Found existing scene file: {os.path.basename(json_path)} ---")
|
||||
|
||||
cut_video_into_scenes(args.video_path, json_path, args.segtime, hwaccel_method)
|
||||
cut_video_into_scenes(args.video_path, json_path, args.segtime, hwaccel_method, crop_filter)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user