Overview
In this activity, we will explore how to use the while loop to loop an uncertain, indefinite, number of times, and we will use while loops to help us to work with webcam video feeds, or stored video files.
The Github repository for this assignment will contain a starter code file, activ10.py. Put your code in this file, as directed by the TODO comments.
while loops
A while loop starts with a Boolean test expression, and then has a block of indented Python statements in its body. As long as the test expression evaluates to True, the program keeps repeating the statements in the loop body, and then re-checking the test expression. It stops only when the test evaluates to False.
In a for loop the loop variable is very obvious; it is declared as a part of the main line of the loop. The while loop also must have a loop variable, but sometimes it is harder to figure out. Often, it actually follows the accumulator pattern!
There are typically at least three statements involved in the loop that related to the loop variable:
- A statement assigning the loop variable to an initial value before the loop
- The boolean expression that controls the loop, comparing the loop variable to something
- A statement inside the loop that modifies the value of the loop variable
In the example below, the loop variable is count. It is initialized to 1 in the first line, it is compared to total in the boolean test of the while loop, and it is incremented in the last line inside the while loop.
In the sections below, we will see examples of functions that use while loops. These files are in your starter code file. For each function, do the following:
- First, create a sample call with small but non-trivial values for input parameters
- Walk through the function by hand and predict its behavior
- Then, add that call, and some other sample calls, to the code file (in the script at the bottom)
- Verify that the results look correct
- Practice using the debugger to step through the loop one line at a time, watching how the variables change.
First example
This function takes in an integer input, and it prints every other number from that input value down to zero. Then it prints Done!.
- What happens when you put a typical value into this function?
- What happens if you put a negative value into the function?
- What happens if you change the boolean test to be x >= 5?
Work through a call to this function by hand, figuring out the effect of each and every line and repetition; consider a call like printEveryOther(7).
Try this to hand in: Make a copy of printEveryOther function, called printEveryFifth, that prints every fifth value from x down to zero. Below are a couple of sample calls to show how this function should work:
Second example
The next example shows the indeterminate nature of a while loop. In this case, the loop repeats until the user enters a negative number, printing the square of each number the user enters.
Experiment with this function. Notice that the first two lines of the function are repeated again inside the while loop. Why is that?
Accumulator variables
We can use accumulator variables with while loops just like we do with for loops. The accumulator is set to some initial value before the loop, and then updated each time through the loop. See the examples below.
def sumToN(topNum):
"""Takes in a number and computes and returns the sum of the numbers
from zero to the input number."""
currVal = 0 # the loop variable
total = 0 # the accumulator variable
while currVal < topNum:
total = total + currVal # add next value to accumulator
currVal = currVal + 1 # update the loop variable
return totalExperiment with this function. Walk through what happens if you call this with some small value, like 4.
- Walk through the function by hand with your teammate/neighbor
- Try either adding print statements to see what happens in the loop, or using the debugger to watch how
totalandcurrValchange
** Try this to hand in:** Write a function addUserNums that has no input arguments. Here are the steps for this function, written in pseudocode:
- Initialize
sumOfNums, an accumulator variable, to zero - Ask the user for a number (be sure to convert their input from string to number)
- Loop until the user enters the value 0
- Update
sumOfNumsby adding the user’s number to it. - Ask again for the user to enter a number.
- Update
- return
sumOfNums
The break Statement
Sometimes you want the option to stop looping if special circumstances occur, before the natural end of the loop. The break statement causes the current loop to stop immediately at the point where the break occurred. The program continues on with any Python statements that come after the loop.
The program below loops over characters in a string, adding them to an accumulator variable. It breaks out of the for loop when the next character is a space, tab, or newline, and returns the string it has built so far.
def nextWord(text):
"""Takes in a string of text and builds and returns a new string
that is the next "word" in the text. In other words, the next
sequence of characters up to a space, tab, or newline."""
wordStr = ""
i = 0
for ch in text:
if ch in " \t\n": # if character is space, tab (\t), or newline (\n)
break
else:
wordStr = wordStr + ch
return wordStrTry this to hand in: We’re going to rewrite the squareUserNums function from earlier to avoid repeating the two lines that get the next number from the user. To do so, we will use a common: while True combined with break.
Do this first:
- Copy the definition of
squareUserNumsfrom earlier and change its name tosquareUserNums2. - Remove the two lines before the
whileloop (the call toinputand the call toint). - Change the boolean expression test of the
whilestatement to be just the boolean valueTrue, as shown below:
Using True as the test expression is a way to create a loop that never ends. Inside the loop, we can use an if statement to break out of the loop when conditions are right.
Next, move around the pieces inside the while loop, and add an if statement:
- Inside the
whileloop insquareUserNums, move the last two lines (whereuserInpanduserNumare defined) to be the first statements inside thewhileloop. - After those two lines, and before the
printcall, add anifstatement. IfuserNumis less than zero, thenbreak.
This function should behave just like the earlier one, but without the copied lines.
Working with Video
The table below lists the main functions you need in order to access frames from the computer’s camera. First there is the function VideoCapture, which takes one input and creates a VideoCapture object. The one input specifies which camera connected to the computer should be used (zero is the default: the built-in camera if there is one). Three methods belonging to the VideoCapture object are shown, as well.
| Examples | Meaning |
|---|---|
cap = cv2.VideoCapture(0) |
Creates a VideoCapture object connected to specified camera |
cap.isOpened() |
Boolean method returns true if camera connection succeeded |
ret, image = cap.read() |
Returns a boolean (was read successful) and a frame |
cap.release() |
Disconnects from camera |
Start with the script below to get images from a built-in camera. If your computer does not have a built-in webcam, see Susan to get an external webcam!
You are going to improve this script.
Ending the program with key input
Be sure to check the readings for today for how to use waitKey to get the key the user pressed.
- Modify the code above like the example in Chapter 3 of our Vision readings, so that it catches the value returned by
waitKeyand checks it to see if the value is -1. If not, convert it to a character withchrand see if the user typed'q'. Break out of the loop if so. - Add a function called
processImageto the file. It should take in an image and return an image. For now just have it return the image it is passed (we will add functionality in later steps). - Insert a call to
processImagein between thevidCap.read()line and thecv2.imshowline. PassimgtoprocessImage, and assign the returned image toimg2. - Change the displayed image to be
img2.
Now that you have a functioning video streaming program, let’s start making improvements.
Try to hold something up in front of the camera while your program is running, and then move the object to the different corners of the video image. Did you find yourself moving the wrong way? We often have trouble with left and right movements on a video feed, because we are more used to seeing a mirror image of ourselves. We can use the OpenCV function flip to flip the image. This function will flip an image upside down, or left-to-right.
The flip function takes in an image and a flip code which specifies whether to flip around the horizontal axis, or around the vertical one. It returns a new image. A flip code of 0 will flip a picture upside down (around the horizontal axis), and a flip code of 1 will flip a pictures left-to-right (around the vertical axis).
Try this to hand in: For this task, you will add an option of display the flipped version of the video feed.
- Modify your
processImagefunction so that it takes in an extra input:doFlip. WhendoFlipisTrue, the function should call theflipfunction, using flip code 1, and should return the resulting image. WhendoFlipisFalse, the function should return the original image. - In your main program, you will need to set up a boolean flag variable. This is a special kind of accumulator variable that just holds
TrueorFalse. We call it a flag variable because it signals when a certain condition holds- Before the
while Trueloop, set up the flag variable to beFalse - Inside the loop, add the flag variable to the call to
processImage - Add to the
ifstatement that checks the user’s input key: if the user hits the f key, we want to toggle the value of the flag variable: if it wasFalse, it should becomeTrue, and vice versa.
- Before the
Try this to hand in: Suppose that you wanted to be able to save individual frames from the video feed to image files.
- Add to the
ifstatement that checks the user’s input key, so that it checks if the user hits the s key - When the user hits that, save the current image to a file, using the
cv2.imwritefunction. This function takes two inputs: (1) a string for the filename of the file to save to, including.jpgor.pngas the filename extension, and (2) the image you want to save.
For an extra, optional challenge, give each screenshot a unique filename, so that you can save any number you want. (Adding a counter to the loop, and attaching the counter to the filename is a great way to do this.)
Try this to hand in: Let’s apply the color shuffle function from the previous activity to the video feed.
- Find your implementation of
colorShuffleand copy it into this code file (if you didn’t complete it, ask for help from a partner, preceptor, or instructor). - Add a call to
colorShuffleto yourprocessImagefunction. For this one, we won’t require that the user hit a key to trigger it, it should just get called every time.
What happens?
What should have happened was a bit of chaos: the color scheme should flicker from frame to frame into different color patterns. How could we fix this?
Optional fix: A better model than just random shuffling every frame might be to trigger a shuffle pattern every time the user hits a particular key, and then keep that shuffle pattern from then on until the user selects another pattern. A shuffle pattern needs to tell us which of the original channels should go in which new channel position. For example, if the pattern was [0, 2, 1] then the blue channel would stay in the same place, but the red and green channels would be swapped. Every ordering of the values 0, 1, and 2 in the list corresponds to a unique shuffle, and all shuffles can be represented by a pattern. We would need to decode the pattern to determine how to split and re-merge the frame’s channels.
If you have time and interest, here are steps you could implement for this.
- Set up a
patternaccumulator variable before thewhileloop, and initialize it to be[0, 1, 2], which is the normal view of channels (each channel goes back into its normal slot). - Add the
patternas an additional input to theprocessImagefunction. - Respond to the user in the
whileloop: if the user presses the space bar, randomly shufflepattern. - In
processImage, after any other changes to the input image are made, do these steps to reshuffle the color channels:- Call the
cv2.splitfunction, and save the tuple in a variable calledchannels. - Assign three variables,
ch0,ch1, andch2, to hold the channel fromchannelsbased on the index in the shuffle pattern variable:ch0 = channels[pattern[0]], etc. - Build a new tuple to hold these variables:
newChannels = (ch0, ch1, ch2). - Return the result of
cv2.mergecalled onnewChannels.
- Call the
What to hand in
Put all of your function definitions for this activity into a single file to be submitted. Make sure you format your code appropriately:
- At the top of the file is a triple-quoted string describing the file
- Next you include all import statements
- Next you have your function definitions, visually separated by blank lines, and maybe comments with dashed or other visual horizontal lines
- Each function should have a triple-quoted descriptive comment right after the
defline - All calls to all functions should be in a script at the bottom of the file, ideally inside an
if __name__ ...block
Use commit and push to copy your code to Github to submit this work.