Last active
March 25, 2025 09:15
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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 | |
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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