Week 7: Acquiring and Manipulating Videos#
Laboratory 4
Last updated July 25, 2023
00. Content #
Mathematics
N/A
Programming Skills
Using Software Documentation
OpenCV (cv2)
Embedded Systems
Thonny and MicroPython
0. Required Hardware #
Raspberry Pi Pico
Breadboard
USB connector
Camera (Arducam HM01B0)
8 Wires
Write your name and email below:
Name:
Email:
import numpy as np
import matplotlib.pyplot as plt
import cv2
1. Reading Videos with OpenCV #
In this lab, we will be using a Python library called OpenCV (cv2). According to the official documentation, “OpenCV is an open-source library that includes several hundreds of computer vision algorithms.” Today, we will primarily utilize it to read and display modified video frames, and we will manually handle the “vision” aspect of computer vision with our own functions.
Run the following code cell to read the video file. The vid.read()
function returns two values: a Boolean variable success
, which is True
if the frame was read without any errors, and frame
, which represents the captured image from the video. Since the camera hardware connected to your Pico can only provide grayscale images, we will convert each frame to grayscale using the cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
function. To improve computation speed, we have scaled down each frame by a factor of 4.
Once you run the cell, a separate window will pop up (potentially appearing behind other windows on your screen), displaying the smaller, grayscale video. The separate window will automatically close after reading the entire video. However, you can override this behavior by pressing the Q
key on your keyboard.
To begin, download the video file we’ll be using for this lab: test_vid.mov. This video is sourced from YouTube and depicts a bouncing ball.
vid = cv2.VideoCapture('test_vid.mov')
height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
width = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
scale = 0.25
new_size = (int(width*scale),int(height*scale)) # new frame dimensions, 4 times smaller than the original
while vid.isOpened():
success, frame = vid.read() # get the current frame (if there is one)
if not success:
print("Unable to read frame. Exiting ...")
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # change the video from color to grayscale
frame = cv2.resize(frame,dsize=new_size) # resize the frame to the new dimensions
cv2.imshow('frame', frame) # display 'frame' in the popup window
if cv2.waitKey(25) == ord('q'): # press Q on keyboard to stop
break
vid.release()
cv2.destroyAllWindows() # closes the popup when the video is over
Exercise 1 #
What type of variable is frame
? What kind of datatype is stored in frame
?
Write Answers for Exercise 1 Below
2. Modifying Videos #
Exercise 2 #
Read the documentation for the waitKey
function.
Part 1
Play the video at a slower speed than its original playback speed.
Write Answers for Exercise 2 Part 1 Below
Part 2
Play the video at a faster speed than its original playback speed.
Write Answers for Exercise 2 Part 2 Below
Exercise 3 #
Play the video in reverse (backward). You don’t need to refer to any additional documentation for this exercise.
Write Answers for Exercise 3 Below
Exercise 4 #
Part 1
Flip the visuals of the video over the y-axis.
Hint: There are multiple ways to accomplish this, but you can explore the flip functions in NumPy or OpenCV.
Write Answers for Exercise 4 Part 1 Below
Part 2
Flip the visuals of the video over the x-axis.
Write Answers for Exercise 4 Part 2 Below
Being able to extract specific frames from a video is a useful skill. It can be used, for example, to trim the ends of the video or to remove a section in the middle.
Exercise 5 #
Play only the first 200 frames of the video.
Write Answers for Exercise 5 Below
3. Connecting the Camera #
This time, we will record our own videos using the Arducam HM01B0, which is a small camera that can be connected to the Pico.
Wiring Instructions#
Please ensure that your microcontroller is not connected to the computer while you are wiring components together. If you are unsure about your wiring, please consult the instructor. Use your jumper wires to establish the following connections:
HM01B0 |
Pico |
---|---|
VCC |
3V3 |
SCL |
GP5 |
SDA |
GP4 |
VSYNC |
GP16 |
HREF |
GP15 |
PCLK |
GP14 |
DO |
GP6 |
GND |
GND |
Here is an image of the completed breadboard:
To find the names of the pins on the Raspberry Pi Pico, you can refer to its pinout diagram located here or in the Extra Materials section. The HM01B0, on the other hand, should have its pins labeled.
After confirming that the wiring is correct, press and hold the BOOTSEL button on the Pico while plugging it in. Download the arducam.uf2 file and copy it onto the Pico’s drive using your computer’s file manager (it should be listed as an external drive: “RPI-RP2”) and not with Thonny. Once the file transfer is complete, the Pico will automatically disconnect, and its LED will start blinking rapidly.
Once the Pico has been successfully connected, please execute the following cell to ensure that we have successfully detected the Pico.
import time
import serial
from serial.tools import list_ports
PICO_HWID = "2E8A"
def get_pico_port():
pico_ports = list(list_ports.grep(PICO_HWID))
if len(pico_ports) == 0:
raise Exception(
"No Raspberry Pi Pico was detected. Check to make sure it is plugged in, and that no other programs are accessing it"
)
return pico_ports[0].device
print("Here are all the serial devices detected:")
for port in list_ports.comports():
print(port.device, port.hwid)
port = get_pico_port()
print(f"\nselected port {port} as most likely to have a raspberry pi pico")
Capturing a still image#
Now that the Pico and camera have been connected, execute the following cell to capture a still image.
buffer = bytearray(96 * 96)
img = np.zeros(shape=(96, 96), dtype="uint8")
with serial.Serial(port, timeout=1) as s:
s.read_until(b"\x55\xAA")
s.readinto(buffer)
img.flat[::-1] = buffer
plt.imshow(img, cmap="gray")
plt.show()
Exercise 6 #
Use the camera to capture an image of yourself (or a neighbor) with your hand up and another image of yourself (or your neighbor) with your hands down. You are welcome to use more than two frames if desired. Use these frames to create a video where you move your hands up and down, lasting a few seconds.
Write Answers for Exercise 6 Below
Streaming video#
Now that we can capture a still image, the next goal is to stream a video. Both cells need to be executed before the video is streamed to your screen. After running the first cell, a still image should pop up. Once you run the second cell, it will start streaming real-time video from your camera.
%matplotlib widget
fig, ax = plt.subplots()
render = ax.imshow(img, cmap='gray')
plt.show(block=False)
try:
with serial.Serial(port, timeout=1) as s:
while True:
s.read_until(b"\x55\xAA")
s.readinto(buffer)
img.flat[::-1] = buffer
render.set_data(img)
fig.canvas.draw()
except KeyboardInterrupt:
pass
When you are finished, you will need to click the “Interrupt Kernel” button in Jupyter, which can be found at the top of the screen (the stop symbol) or under the Kernel menu.
4. Modifying Your Own Video #
Exercise 7 #
Capture and save a list of frames using the camera.
Part 1: Repeat Exercise 3 and Exercise 4 with your own video.
Write Answers for Exercise 7 Part 1 Below
Part 2: Create an Instagram boomerang effect using a small portion or the entire video. Here’s an example of what a boomerang effect looks like:
Write Answers for Exercise 7 Part 2 Below
Part 3: Choose an editing function we haven’t mentioned yet (e.g., adding text, video filters, etc.) and give it a try on either the pre-recorded video or the ones you recorded using the camera.
Write Answers for Exercise 7 Part 3 Below
Reflection #
What was your favorite exercise in this lab?
Which part of the lab did you find the most challenging?
Which part of the lab was the easiest?