1+ #!/usr/bin/env python3
2+ #
3+ # Copyright (c) FIRST and other WPILib contributors.
4+ # Open Source Software; you can modify and/or share it under the terms of
5+ # the WPILib BSD license file in the root directory of this project.
6+ #
7+
8+
9+ import threading
10+ import time
11+
12+ import cv2
13+ import numpy as np
14+
15+ import wpilib
16+ from cscore import CameraServer
17+
18+
19+ class MyRobot (wpilib .TimedRobot ):
20+ """
21+ This is a sample program showing how to overlay a red rectangle on a camera feed.
22+ The rectangle's position is controlled from robot code during teleop.
23+ This requires at least one USB camera connected to the robot.
24+ Use a dashboard client to view the camera stream. Two streams are available:
25+ - "Annotated Stream": The camera feed with the red rectangle overlay.
26+ - "<your camera name>": The raw camera feed without any annotations.
27+ """
28+
29+ def robotInit (self ):
30+ """Robot initialization function"""
31+
32+ CameraServer .enableLogging ()
33+
34+ # State variables - this will be the center position of the rectangle, in units of pixels.
35+ self .rectX = 0
36+ self .rectY = 0
37+
38+ # Start reading data from the camera, and serving the raw stream
39+ self .camera = CameraServer .startAutomaticCapture ()
40+ self .camera .setResolution (640 , 480 )
41+
42+ # Record a reference to the video source (which should be the first camera connected):
43+ self .source = self .camera .enumerateSources ()
44+
45+ # Set up a second server to provide an annotated stream
46+ self .cvSink = CameraServer .getVideo ()
47+ self .outputStream = CameraServer .putVideo ("Annotated Stream" , 640 , 480 )
48+
49+ # Allocate memory for creating the annotated image.
50+ self .img = np .zeros ((480 , 640 , 3 ), dtype = np .uint8 )
51+
52+ # Start the background thread for image processing
53+ self .processingThread = threading .Thread (target = self .processImages , daemon = True )
54+ self .processingThread .start ()
55+
56+
57+ def processImages (self ):
58+ """Thread to read information from the camera, and provide the overlaid image"""
59+
60+ while True :
61+ # Grab a frame from the camera
62+ frameTime , self .img = self .cvSink .grabFrame (self .img )
63+
64+ if frameTime == 0 :
65+ # If there's an error, skip processing
66+ print (self .cvSink .getError ())
67+ continue
68+
69+ # Draw a red box on the image
70+ width = 50
71+ height = 50
72+ topLeft = (int (self .rectX - width / 2 ), int (self .rectY - height / 2 ))
73+ botRight = (int (self .rectX + width / 2 ), int (self .rectY + height / 2 ))
74+ colorBGR = (0 , 0 , 255 )
75+ lineWidth = 2
76+ cv2 .rectangle (self .img ,
77+ topLeft ,
78+ botRight ,
79+ colorBGR ,
80+ lineWidth )
81+
82+ # Publish the annotated image
83+ self .outputStream .putFrame (self .img )
84+
85+ # Yield execution to other threads
86+ time .sleep (0.001 )
87+
88+ def teleopPeriodic (self ):
89+ # Update the rectangle position.
90+ # For this example, we'll use a joystick as input.
91+ joystick = wpilib .Joystick (0 )
92+ self .rectX += joystick .getX () * 5
93+ self .rectY += joystick .getY () * 5
94+
95+ # Ensure the rectangle stays within bounds
96+ self .rectX = max (0 , min (self .rectX , 640 ))
97+ self .rectY = max (0 , min (self .rectY , 480 ))
0 commit comments