Add timestamp overlay feature for video mode

- Add --timestamp / --ts flag to embed frame count on bottom left corner
- Implement add_timestamp_overlay() function with large, visible text
- Yellow text on black background for good visibility
- Shows 'Frame: X/Y' format with current and total frame counts
- Works with both column and row video modes
- Applied after rotation for proper positioning
- Update documentation with timestamp examples and parameter descriptions
- Tested successfully with sample video files
This commit is contained in:
yair-mv 2025-11-08 12:26:11 +02:00
parent c1faa2088c
commit 8fece16ca7
2 changed files with 70 additions and 4 deletions

67
main.py
View File

@ -475,7 +475,49 @@ def extract_row_strip(video_path, y_row, output_path, change_threshold=0.01, rel
cv2.imwrite(str(output_path), strip_image) cv2.imwrite(str(output_path), strip_image)
def extract_column_strip_video(video_path, x_column, output_path, change_threshold=0.005, relax=0, start_frame=0, end_frame=None, fps=30): def add_timestamp_overlay(frame, frame_count, total_frames):
"""
Add frame count overlay to the bottom left corner of the frame.
Args:
frame: The video frame to add overlay to
frame_count: Current frame number (1-based)
total_frames: Total number of frames
Returns:
Frame with timestamp overlay
"""
overlay = frame.copy()
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1.2
font_thickness = 2
text_color = (0, 255, 255) # Yellow for visibility
bg_color = (0, 0, 0) # Black background
# Create timestamp text
timestamp_text = f"Frame: {frame_count}/{total_frames}"
# Get text size for background rectangle
(text_width, text_height), baseline = cv2.getTextSize(timestamp_text, font, font_scale, font_thickness)
# Position at bottom left with some padding
x_pos = 10
y_pos = frame.shape[0] - 10 # Bottom of frame minus padding
# Draw background rectangle
cv2.rectangle(overlay,
(x_pos - 5, y_pos - text_height - baseline - 5),
(x_pos + text_width + 5, y_pos + baseline + 5),
bg_color, -1)
# Draw text
cv2.putText(overlay, timestamp_text, (x_pos, y_pos - baseline),
font, font_scale, text_color, font_thickness, cv2.LINE_AA)
return overlay
def extract_column_strip_video(video_path, x_column, output_path, change_threshold=0.005, relax=0, start_frame=0, end_frame=None, fps=30, timestamp=False):
""" """
Extract vertical strip at x_column from each frame and create an MJPEG video. Extract vertical strip at x_column from each frame and create an MJPEG video.
Each frame of the output video shows the accumulated scan lines up to that point. Each frame of the output video shows the accumulated scan lines up to that point.
@ -489,6 +531,7 @@ def extract_column_strip_video(video_path, x_column, output_path, change_thresho
start_frame: First frame to process (0-based) start_frame: First frame to process (0-based)
end_frame: Last frame to process (None = until end) end_frame: Last frame to process (None = until end)
fps: Output video frame rate fps: Output video frame rate
timestamp: If True, embed frame count on bottom left corner
""" """
cap = cv2.VideoCapture(str(video_path)) cap = cv2.VideoCapture(str(video_path))
@ -611,6 +654,10 @@ def extract_column_strip_video(video_path, x_column, output_path, change_thresho
# Convert to numpy array and create the frame # Convert to numpy array and create the frame
strip_frame = np.stack(accumulated_columns, axis=1) strip_frame = np.stack(accumulated_columns, axis=1)
# Add timestamp overlay if requested
if timestamp:
strip_frame = add_timestamp_overlay(strip_frame, frame_idx + 1, len(significant_columns))
# Write frame to video # Write frame to video
out.write(strip_frame) out.write(strip_frame)
@ -625,7 +672,7 @@ def extract_column_strip_video(video_path, x_column, output_path, change_thresho
print(f"Total duration: {len(significant_columns)/fps:.2f} seconds") print(f"Total duration: {len(significant_columns)/fps:.2f} seconds")
def extract_row_strip_video(video_path, y_row, output_path, change_threshold=0.01, relax=0, start_frame=0, end_frame=None, fps=30): def extract_row_strip_video(video_path, y_row, output_path, change_threshold=0.01, relax=0, start_frame=0, end_frame=None, fps=30, timestamp=False):
""" """
Extract horizontal strip at y_row from each frame and create an MJPEG video. Extract horizontal strip at y_row from each frame and create an MJPEG video.
Each frame of the output video shows the accumulated scan lines up to that point. Each frame of the output video shows the accumulated scan lines up to that point.
@ -639,6 +686,7 @@ def extract_row_strip_video(video_path, y_row, output_path, change_threshold=0.0
start_frame: First frame to process (0-based) start_frame: First frame to process (0-based)
end_frame: Last frame to process (None = until end) end_frame: Last frame to process (None = until end)
fps: Output video frame rate fps: Output video frame rate
timestamp: If True, embed frame count on bottom left corner
""" """
cap = cv2.VideoCapture(str(video_path)) cap = cv2.VideoCapture(str(video_path))
@ -766,6 +814,10 @@ def extract_row_strip_video(video_path, y_row, output_path, change_threshold=0.0
# Rotate counter-clockwise 90 degrees to match image mode orientation # Rotate counter-clockwise 90 degrees to match image mode orientation
strip_frame = cv2.rotate(strip_frame, cv2.ROTATE_90_COUNTERCLOCKWISE) strip_frame = cv2.rotate(strip_frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
# Add timestamp overlay if requested (after rotation)
if timestamp:
strip_frame = add_timestamp_overlay(strip_frame, frame_idx + 1, len(significant_rows))
# Write frame to video # Write frame to video
out.write(strip_frame) out.write(strip_frame)
@ -862,6 +914,13 @@ def main():
help="Output video frame rate (default: 30.0, only used with --video)" help="Output video frame rate (default: 30.0, only used with --video)"
) )
parser.add_argument(
"--timestamp",
"--ts",
action="store_true",
help="Embed frame count on bottom left corner (video mode only)"
)
args = parser.parse_args() args = parser.parse_args()
# Validate input file # Validate input file
@ -973,11 +1032,11 @@ def main():
if args.xcolumn is not None: if args.xcolumn is not None:
print(f"Column mode: Extracting vertical line at x={args.xcolumn}") print(f"Column mode: Extracting vertical line at x={args.xcolumn}")
extract_column_strip_video(video_path, args.xcolumn, output_path, args.threshold, args.relax, extract_column_strip_video(video_path, args.xcolumn, output_path, args.threshold, args.relax,
args.start, args.end, args.fps) args.start, args.end, args.fps, args.timestamp)
else: else:
print(f"Row mode: Extracting horizontal line at y={args.yrow}") print(f"Row mode: Extracting horizontal line at y={args.yrow}")
extract_row_strip_video(video_path, args.yrow, output_path, args.threshold, args.relax, extract_row_strip_video(video_path, args.yrow, output_path, args.threshold, args.relax,
args.start, args.end, args.fps) args.start, args.end, args.fps, args.timestamp)
print("MJPEG video generation completed successfully!") print("MJPEG video generation completed successfully!")
else: else:

View File

@ -36,6 +36,11 @@ uv run main.py .\line500fps32pix.mp4 --video --fps 30
``` ```
Output: `results/video/line500fps32pix_a3f2_t0_01_fps30_0.avi` Output: `results/video/line500fps32pix_a3f2_t0_01_fps30_0.avi`
**Video with Timestamp** - Add frame count overlay to video:
```bash
uv run main.py .\line500fps32pix.mp4 --video --fps 30 --timestamp
```
**Debug Mode** - Analyze changes and generate threshold recommendations: **Debug Mode** - Analyze changes and generate threshold recommendations:
```bash ```bash
uv run main.py .\line500fps32pix.mp4 --debug uv run main.py .\line500fps32pix.mp4 --debug
@ -68,6 +73,7 @@ uv sync
- `--debug` - Analyze changes without creating strip image, outputs to `results/debug/` - `--debug` - Analyze changes without creating strip image, outputs to `results/debug/`
- `--video` - Generate MJPEG video showing accumulated scan lines over time - `--video` - Generate MJPEG video showing accumulated scan lines over time
- `--fps N` - Output video frame rate (default: 30.0, only used with `--video`) - `--fps N` - Output video frame rate (default: 30.0, only used with `--video`)
- `--timestamp` / `--ts` - Embed frame count on bottom left corner (video mode only)
- `--timeline` - Overlay frame numbers as timeline/ruler on output image (image mode only) - `--timeline` - Overlay frame numbers as timeline/ruler on output image (image mode only)
- `--start N` - Start frame number (0-based, default: 0) - `--start N` - Start frame number (0-based, default: 0)
- `--end N` - End frame number (0-based, default: last frame) - `--end N` - End frame number (0-based, default: last frame)
@ -112,6 +118,7 @@ uv sync
- Creates MJPEG AVI files showing scan line accumulation over time - Creates MJPEG AVI files showing scan line accumulation over time
- Each video frame shows the progressive build-up of the strip photography effect - Each video frame shows the progressive build-up of the strip photography effect
- Configurable frame rate with `--fps` parameter - Configurable frame rate with `--fps` parameter
- Optional timestamp overlay with `--timestamp` / `--ts` showing frame count in bottom left corner
- Video dimensions automatically calculated based on input video and scan line count - Video dimensions automatically calculated based on input video and scan line count
- Compatible with both row and column extraction modes - Compatible with both row and column extraction modes
- Timeline overlay not supported in video mode (use image mode with `--timeline` instead) - Timeline overlay not supported in video mode (use image mode with `--timeline` instead)