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:
parent
c1faa2088c
commit
8fece16ca7
67
main.py
67
main.py
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user