Files
FishServer/FishMeasure/process_svo_batch.py
2026-04-08 19:32:23 +08:00

223 lines
7.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Batch process SVO files using ZEDReader
Processes all SVO files in an input folder and saves results to output folders
"""
import argparse
import os
import sys
from pathlib import Path
from zed_reader import ZEDReader
def process_svo_file(svo_path, output_base_dir, record=False, save_all=False, debug=False,
max_depth=1000, max_y=100, min_points=500, use_yolo=False,
yolo_model_path=None, show_yolo_bbox=False):
"""
Process a single SVO file
Args:
svo_path: Path to SVO file
output_base_dir: Base output directory
record: Whether to record
save_all: Whether to save all frames
debug: Enable debug mode
max_depth: Maximum depth value (mm)
max_y: Maximum Y coordinate value (mm)
min_points: Minimum points for fish detection
use_yolo: Whether to use YOLO detector
yolo_model_path: Path to YOLO model file
show_yolo_bbox: Whether to show YOLO bounding boxes
Returns:
bool: True if successful, False otherwise
"""
# Get filename without extension
svo_filename = os.path.basename(svo_path)
svo_name = os.path.splitext(svo_filename)[0]
# Create output directory: output_base_dir/svo_name
output_dir = os.path.join(output_base_dir, svo_name)
print(f"\n{'='*60}")
print(f"Processing: {svo_filename}")
print(f"Output directory: {output_dir}")
if debug:
print(f"Debug mode: Images -> {output_dir}/images/, Point clouds -> {output_dir}/pointcloud/")
if use_yolo:
print(f"YOLO detector: Enabled (model: {yolo_model_path or 'default'})")
print(f"{'='*60}")
try:
# Create ZEDReader instance with SVO file
zed_reader = ZEDReader(svo_path=svo_path, camera_mode=False,
use_yolo_detector=use_yolo,
yolo_model_path=yolo_model_path)
# Open the SVO file
if not zed_reader.open():
print(f"Error: Failed to open SVO file: {svo_path}")
return False
# Process frames
# Note: show_yolo_bbox defaults to True in zed_reader.process_frames()
# If user didn't specify --show_yolo_bbox, args.show_yolo_bbox will be False,
# but we want to use zed_reader's default (True), so we pass True
# If user specified --show_yolo_bbox, args.show_yolo_bbox will be True, so we pass True
bbox_setting = True # Always use True to match zed_reader default behavior
zed_reader.process_frames(
save_path=output_dir,
record=record,
save_all=save_all,
debug=debug,
max_depth=max_depth,
max_y=max_y,
min_points=min_points,
show_yolo_bbox=bbox_setting
)
print(f"\n✓ Successfully processed: {svo_filename}")
return True
except Exception as e:
print(f"\n✗ Error processing {svo_filename}: {str(e)}")
import traceback
traceback.print_exc()
return False
def main():
parser = argparse.ArgumentParser(
description='Batch process SVO files using ZEDReader'
)
parser.add_argument(
'--input_dir', '-i',
type=str,
required=True,
help='Input directory containing SVO files'
)
parser.add_argument(
'--output_dir', '-o',
type=str,
default='output',
help='Base output directory (default: output)'
)
parser.add_argument(
'--save_all',
action='store_true',
help='Save all frames (not just fish detections)'
)
parser.add_argument(
'--debug',
action='store_true',
help='Enable debug mode: save images with bbox and point clouds when fish detected'
)
parser.add_argument(
'--max_depth',
type=float,
default=1000,
help='Maximum depth value (mm), default 1000'
)
parser.add_argument(
'--max_y',
type=float,
default=150,
help='Maximum Y coordinate value (mm), default 100'
)
parser.add_argument(
'--min_points',
type=int,
default=500,
help='Minimum points for fish detection, default 500'
)
parser.add_argument(
'--use_yolo',
action='store_true',
help='Use YOLO detector for fish detection'
)
parser.add_argument(
'--yolo_model',
type=str,
default=None,
help='Path to YOLO model file (.pt), default uses yolov8n.pt'
)
parser.add_argument(
'--show_yolo_bbox',
action='store_true',
help='Draw YOLO detection bounding boxes on saved images (default: True in zed_reader)'
)
args = parser.parse_args()
# show_yolo_bbox defaults to True in zed_reader.process_frames()
# If user didn't specify --show_yolo_bbox, we'll let zed_reader use its default
# If user specified it, we pass True explicitly
# Validate input directory
if not os.path.isdir(args.input_dir):
print(f"Error: Input directory does not exist: {args.input_dir}")
sys.exit(1)
# Create output base directory if it doesn't exist
os.makedirs(args.output_dir, exist_ok=True)
# Find all SVO files in input directory
svo_files = []
for file in os.listdir(args.input_dir):
if file.lower().endswith('.svo2'):
svo_path = os.path.join(args.input_dir, file)
svo_files.append(svo_path)
if not svo_files:
print(f"Error: No SVO files found in: {args.input_dir}")
sys.exit(1)
print(f"Found {len(svo_files)} SVO file(s) to process")
print(f"Input directory: {args.input_dir}")
print(f"Output base directory: {args.output_dir}")
print(f"Settings: max_depth={args.max_depth}mm, max_y={args.max_y}mm, min_points={args.min_points}")
print(f"Debug mode: {args.debug}, Save all: {args.save_all}")
print(f"YOLO detector: {args.use_yolo}, Show bbox: {args.show_yolo_bbox}")
if args.use_yolo and args.yolo_model:
print(f"YOLO model: {args.yolo_model}")
if args.debug:
print(f"Debug mode enabled: Images will be saved to output/{{svo_name}}/images/")
print(f" Point clouds will be saved to output/{{svo_name}}/pointcloud/")
# Process each SVO file
successful = 0
failed = 0
for i, svo_path in enumerate(svo_files, 1):
print(f"\n[{i}/{len(svo_files)}] Processing: {os.path.basename(svo_path)}")
success = process_svo_file(
svo_path=svo_path,
output_base_dir=args.output_dir,
record=False, # Don't record when processing SVO files
save_all=args.save_all,
debug=args.debug,
max_depth=args.max_depth,
max_y=args.max_y,
min_points=args.min_points,
use_yolo=args.use_yolo,
yolo_model_path=args.yolo_model,
show_yolo_bbox=args.show_yolo_bbox
)
if success:
successful += 1
else:
failed += 1
# Summary
print(f"\n{'='*60}")
print(f"Batch processing complete!")
print(f" Successful: {successful}/{len(svo_files)}")
print(f" Failed: {failed}/{len(svo_files)}")
print(f"{'='*60}")
if __name__ == "__main__":
main()