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()