I have a camera that shoots Mount Rainier all of the time. This morning’s coffee tinker was automatically color grading the stream

import cv2
import numpy as np
from datetime import datetime
import os

class RTSPGrader:
    def __init__(self):
        # RTSP connection settings - fill these in
        self.username = "user"
        self.password = "password"
        self.ip = "192.168.1.2"
        self.port = "554"  # Default RTSP port
        
        # Create control window
        cv2.namedWindow('Controls')
        
        # Create trackbars
        cv2.createTrackbar('Contrast', 'Controls', 120, 300, lambda x: None)  # 1.2 = 120
        cv2.createTrackbar('Brightness', 'Controls', 50, 100, lambda x: None) # 0 = 50 (middle)
        cv2.createTrackbar('Saturation', 'Controls', 110, 300, lambda x: None) # 1.1 = 110
        cv2.createTrackbar('Temperature', 'Controls', 50, 100, lambda x: None) # 0 = 50 (middle)
        cv2.createTrackbar('Scale %', 'Controls', 50, 100, lambda x: None)    # 50%
        
        # Output settings
        self.output_dir = "timelapse_frames"
        self.save_interval = 30  # Save frame every 30 seconds
        self.last_save = datetime.now()
        
    def get_rtsp_url(self):
        return f"rtsp://{self.username}:{self.password}@{self.ip}:{self.port}/cam/realmonitor?channel=1&subtype=0"
    
    def get_controls(self):
        # Get current positions of trackbars
        contrast = cv2.getTrackbarPos('Contrast', 'Controls') / 100.0
        brightness = (cv2.getTrackbarPos('Brightness', 'Controls') - 50) / 50.0  # -1 to 1
        saturation = cv2.getTrackbarPos('Saturation', 'Controls') / 100.0
        temperature = cv2.getTrackbarPos('Temperature', 'Controls') - 50  # -50 to 50
        scale = cv2.getTrackbarPos('Scale %', 'Controls') / 100.0  # 0 to 1
        
        return contrast, brightness, saturation, temperature, scale
    
    def scale_frame(self, frame, scale_factor):
        width = int(frame.shape[1] * scale_factor)
        height = int(frame.shape[0] * scale_factor)
        return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)
    
    def apply_color_grade(self, frame, contrast, brightness, saturation, temperature):
        # Convert to float32 for processing
        frame_float = frame.astype(np.float32) / 255.0
        
        # Apply contrast
        frame_float = cv2.pow(frame_float, contrast)
        
        # Apply brightness
        frame_float += brightness
        
        # Apply saturation
        hsv = cv2.cvtColor(frame_float, cv2.COLOR_BGR2HSV)
        hsv[:,:,1] = hsv[:,:,1] * saturation
        frame_float = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        
        # Apply color temperature
        if temperature > 0:  # Warmer
            frame_float[:,:,0] *= (1 - temperature/50)  # Less blue
            frame_float[:,:,2] *= (1 + temperature/50)  # More red
        elif temperature < 0:  # Cooler
            frame_float[:,:,0] *= (1 + abs(temperature)/50)  # More blue
            frame_float[:,:,2] *= (1 - abs(temperature)/50)  # Less red
            
        # Clip values and convert back to uint8
        frame_float = np.clip(frame_float, 0, 1)
        return (frame_float * 255).astype(np.uint8)
    
    def save_frame(self, frame, original_frame):
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
        
        # Apply current grading to full resolution frame
        contrast, brightness, saturation, temperature, _ = self.get_controls()
        graded_original = self.apply_color_grade(original_frame, contrast, brightness, 
                                               saturation, temperature)
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{self.output_dir}/frame_{timestamp}.jpg"
        cv2.imwrite(filename, graded_original)
        print(f"Saved frame: {filename}")
    
    def run(self):
        # Create capture object
        cap = cv2.VideoCapture(self.get_rtsp_url())
        
        if not cap.isOpened():
            print("Error: Could not connect to camera")
            return
        
        print("Controls:")
        print("- Adjust sliders in 'Controls' window")
        print("- Press 's' to save current frame")
        print("- Press 'q' to quit")
        print("- Press 'r' to reset all controls")
        
        try:
            while True:
                ret, original_frame = cap.read()
                if not ret:
                    print("Error reading frame")
                    break
                
                # Get current control values
                contrast, brightness, saturation, temperature, scale = self.get_controls()
                
                # Scale the frame
                frame = self.scale_frame(original_frame, scale)
                
                # Apply color grading
                graded_frame = self.apply_color_grade(frame, contrast, brightness, 
                                                    saturation, temperature)
                
                # Show the frame
                cv2.imshow('Graded Feed', graded_frame)
                
                # Save frame at interval
                if (datetime.now() - self.last_save).seconds >= self.save_interval:
                    self.save_frame(graded_frame, original_frame)
                    self.last_save = datetime.now()
                
                # Handle keypresses
                key = cv2.waitKey(1) & 0xFF
                if key == ord('q'):
                    break
                elif key == ord('s'):
                    self.save_frame(graded_frame, original_frame)
                elif key == ord('r'):
                    # Reset controls to defaults
                    cv2.setTrackbarPos('Contrast', 'Controls', 120)
                    cv2.setTrackbarPos('Brightness', 'Controls', 50)
                    cv2.setTrackbarPos('Saturation', 'Controls', 110)
                    cv2.setTrackbarPos('Temperature', 'Controls', 50)
                    cv2.setTrackbarPos('Scale %', 'Controls', 50)
                
        finally:
            cap.release()
            cv2.destroyAllWindows()

if __name__ == "__main__":
    grader = RTSPGrader()
    grader.run()