Skip to content

Instantly share code, notes, and snippets.

@paprikodlak
Last active March 25, 2025 09:15
Show Gist options
  • Select an option

  • Save paprikodlak/0f55438ec000157a43e1d3612a00b919 to your computer and use it in GitHub Desktop.

Select an option

Save paprikodlak/0f55438ec000157a43e1d3612a00b919 to your computer and use it in GitHub Desktop.
This is python conversion of the GScrop script https://gist.github.com/Hermann-SW/e6049fe1a24fc2b5a53c654e0e9f6b9c by @Hermann-SW
"""
GScrop converted to python
"""
import subprocess
import os
import re
def set_camera_crop(width, height, x_offset=None, y_offset=None, media_device=None):
"""
Set camera crop settings using media-ctl.
Args:
width (int): Width of the crop window (must be even)
height (int): Height of the crop window (must be even)
x_offset (int, optional): X offset for crop. If None, centers the crop.
y_offset (int, optional): Y offset for crop. If None, centers the crop.
media_device (int, optional): Media device number. If None, tries devices 0-5.
Returns:
bool: True if successful, False otherwise
"""
# Validate width and height are even numbers
if width % 2 != 0 or height % 2 != 0:
raise ValueError("Width and height must be even numbers")
# Detect if we're on a Raspberry Pi 5 with CM4 camera (like the script does)
is_pi_5 = False
device_id = "10" # Default device ID
try:
with open("/proc/cpuinfo", "r") as f:
cpuinfo = f.read()
if re.search(r"Revision.*: ...17.$", cpuinfo):
is_pi_5 = True
# Check for cam1 environment variable as in original script
if os.environ.get("cam1"):
device_id = "11"
else:
device_id = "10"
except:
pass
# Calculate crop offsets if not provided (center crop)
if x_offset is None:
x_offset = (1440 - width) // 2
if y_offset is None:
y_offset = (1088 - height) // 2
# Determine which media device to use
if media_device is not None:
media_devices = [media_device]
else:
media_devices = range(6) # Try devices 0-5 as in the original script
# Format the media-ctl command
crop_fmt = f"'imx296 {device_id}-001a':0 [fmt:SBGGR10_1X10/{width}x{height} crop:({x_offset},{y_offset})/{width}x{height}]"
# Try to set the crop on each media device until success
for m in media_devices:
cmd = ["media-ctl", "-d", f"/dev/media{m}", "--set-v4l2", crop_fmt]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(
f"Successfully set crop on /dev/media{m}: {width}x{height} at offset ({x_offset},{y_offset})"
)
return True
except Exception as e:
print(f"Error setting crop on /dev/media{m}: {e}")
print("Failed to set crop on any media device")
return False
def list_cameras():
"""List available cameras using libcamera-hello"""
try:
subprocess.run(["libcamera-hello", "--list-cameras"], check=True)
return True
except Exception as e:
print(f"Error listing cameras: {e}")
return False
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Set camera crop settings")
parser.add_argument(
"width", type=int, help="Width of the crop window (must be even)"
)
parser.add_argument(
"height", type=int, help="Height of the crop window (must be even)"
)
parser.add_argument(
"--x-offset", type=int, help="X offset for crop (default: centered)"
)
parser.add_argument(
"--y-offset", type=int, help="Y offset for crop (default: centered)"
)
parser.add_argument(
"--media-device", type=int, help="Media device number (default: auto-detect)"
)
parser.add_argument(
"--list-cameras", action="store_true", help="List available cameras"
)
args = parser.parse_args()
if args.list_cameras:
list_cameras()
set_camera_crop(
args.width, args.height, args.x_offset, args.y_offset, args.media_device
)
"""
Raspberry Pi Camera High-Speed Video Capture with Cropping
This script captures high-speed video using the Raspberry Pi Camera and
saves it in slow-motion format with configurable cropped region.
It demonstrates how to:
1. Configure a specific crop area for the camera
2. Calculate and set frame rate and exposure time for high-speed capture
3. Capture frames and measure actual FPS
4. Record H.264 video with the high-speed configuration
5. Convert the raw H.264 video to a 30FPS MP4 using ffmpeg
Requirements:
- Raspberry Pi with picamera2 library installed
- libcamera Python bindings
- FFmpeg for video conversion
- Custom GSCrop module for setting camera crop
The camera is configured with a square crop region and uses the maximum
available frame rate from the sensor. The exposure time is automatically
calculated as a fraction of the frame time to ensure proper exposure.
The output video can be viewed at different playback speeds using mplayer
or similar video players to observe the slow-motion effect.
"""
from picamera2 import Picamera2
from picamera2.encoders import H264Encoder
from libcamera import Transform
import time
from pprint import pprint
from GSCrop import set_camera_crop
import subprocess
crop = 500
set_camera_crop(crop, crop)
camera = Picamera2()
pprint(camera.sensor_modes)
frame_time = 1 / camera.sensor_modes[0]["fps"] * 1e6
pprint(f"Frame time: {frame_time}us")
exposure_to_fps = 16
exposure_fps = int(1 / camera.sensor_modes[0]["fps"] * 1e6 / exposure_to_fps)
pprint(f"Exposure time: {exposure_fps}us")
config = camera.create_preview_configuration(
{
"size": (crop, crop),
},
raw=None,
controls={"FrameRate": camera.sensor_modes[0]["fps"], "ExposureTime": exposure_fps},
)
pprint(config)
camera.configure(config)
# Start camera
camera.start()
time.sleep(1)
# Capture 100 frames and calculate FPS
startTime = time.time()
for i in range(100):
camera.capture_array("main")
print(1 / (time.time() - startTime) * 100)
camera.stop()
config = camera.create_video_configuration(
{
"size": (crop, crop),
},
raw=None,
controls={"FrameRate": camera.sensor_modes[0]["fps"]},
transform=Transform(vflip=1, hflip=1),
)
# Configure camera for video
camera.configure(config)
# Start recording
output_file = "slow_mo_video.h264"
camera.start_recording(
encoder=H264Encoder(), output=output_file, pts=output_file + "timestamp.txt"
)
# Record for 5 seconds
print("Recording slow motion video...")
time.sleep(5)
# Stop recording
camera.stop_recording()
print(f"Slow motion video saved as {output_file}")
# Re-export the video to 30FPS using ffmpeg
output_30fps = "slow_mo_video_30fps.mp4"
cmd = f"ffmpeg -i {output_file} -r 30 -c:v libx264 -preset fast -crf 22 {output_30fps}"
print(f"Converting to 30FPS: {cmd}")
try:
subprocess.run(cmd, shell=True, check=True)
print(f"30FPS video saved as {output_30fps}")
except subprocess.CalledProcessError as e:
print(f"Error converting video: {e}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment