Start preparing for Ethics and Sci-Fi 4 (for this Friday)
Coding quiz today!!
There will be four questions on the quiz today
You will have opportunities to redo the topics of this quiz several more times this semester
The quiz will be completed on paper, not on the computer: bring pens or pencils and an eraser
You should prepare a handwritten, one page (one side of the paper), page of notes
See the study guide for the topics and kinds of questions you might face
Wednesday, September 24
Announcements
Work on Ethics and Sci-Fi 4 for Friday
Library Information Fluency Session TODAY!
Meet in the Library for this session (room TBA)
Friday, September 26
Announcements
Start looking at Homework 2 this weekend… if I have it ready
Be prepared to discuss Ethics and Sci-Fi 4 today
Today’s topics
List and string operations
List and string methods
Tuples
List comprehensions
List and string operations
Functions: len, list, str
Accessing elements or substrings/lists: lst[3], myName[0:5]
Concatenation with + or *: "sun" + "flower"
Looping over list or string contents
String methods
Immutable, a key idea:
Strings are “immutable,” which means “unchangeable.” You can build strings, but you can’t modify them once they are built. You can just build new strings out of an existing string.
Other data types that are immutable: numbers, tuples, (functions and modules, too, but then they’re weird data anyway)
Think about string operators and functions we’ve already seen: + and * build new strings, len doesn’t produce a string, and accessing characters or slicing build new strings, too!
String methods
Remember what a method is: it is like a function, but it is attached to a piece of data.
Aliasing refers to a situation when more than one variable name references the same actual data in the computer’s memory. When that happens, the data has its original name, plus an “alias”. Confusion can arise if we don’t realize when using the alias that we are also referring to the original variable. We care about these things because lists are mutable. If a list is shared across multiple variables, then changing the list through one variable makes the other variable ’s value change as well! This is an easy mistake to make when modifying lists. Here is an example:
lstA = ['a', 'b', 'c', 'd']lstB = lstA.copy() # makes a copy of lstAlstC = lstA # makes lstC be an alias of lstAprint("A:", lstA)print("B:", lstB)print("C:", lstC)lstA[2] ='zzzz'print("A:", lstA)print("B:", lstB)print("C:", lstC)
Used when returning multiple values from a function
More memory efficient than a list
Has no methods!
List comprehensions
A shorthand notation for a for loop that builds one list from the contents of another. It does the same work Abbreviated notation, still does the same work. You can always do what a list comprehension does with loops and accumulator variables
ls = [5, 2, 4, 7, 1, 9, 6] # set up the initial listnewls = [2*x for x in ls]ls3 = [int(s) for s in ['35', '12', '103', '-3']]ls4 = [x for x in ls if x <6]
Week 5
Monday, September 29
Announcements
Homework 2 is due next Monday: Start right away if you haven’t yet.
Be prepared to discuss Ethics and Sci-Fi 5, The Machine Stops, and AI ethics resources this Friday!
Coding Quiz 1 Redos available this week (through next Monday)
Done in/near my office during office hours or by appointment (Sunday afternoons included)
You can do one question at a time and return multiple times, or try 2-4 questions in one sitting
After this week, the next opportunity will be October 15, our 2nd coding quiz date
I recommend coming in to see me and/or preceptors before trying a redo of a question: make sure you understand!
Teams for Week 5
Sabrina, Patrick, Ryder, Elabe
Wheaton, Farihatou, Skye, SydneyP
Lunga, Ethan, Mohamud
Wyatt, Lindsay, Fiona
Bowen, SydneyG, Tene
Today’s topics
Numpy operations
Image arithmetic
Number types
Python has one integer type, any size of integer is just called an int
Underneath, integers really have fixed size, but Python converts between representations for us
Python has one floating-point type: float
Much like with integers, the representation underneath may change, but we don’t have to worry about it
Fixed-length numbers: The hardware of the computer implements integers of specific lengths, typically 8 bits, 16 bits, 32 bits, and 64 bits. Flaoting point numbers are either 32 or 64 bits long. It can also distinguish between unsigned integers and signed integers. Unsigned integers are either zero or positive only; signed integers include both positive and negative values.
Numpy defines numeric types for all these fixed-size numbers. The table below lists the different numeric types in Numpy:
Signed
Unsigned
Float
8 bits
int8
uint8
16 bits
int16
uint16
32 bits
int32
uint32
float32
64 bits
int64
uint64
float64
Arrays in Numpy
Numpy arrays:
Are similar to vectors and matrices in mathematics
Are implemented as contiguous blocks of memory, giving efficient, fast access and operations
Hold just one kind of data, its dtype (typically)
Have a fixed number of dimensions (any number)
Have a fixed size, called its shape
Images as Numpy arrays
Color images: Three dimensional arrays, with height, width, and depth, depth = channels
Grayscaxle images: Two-dimensional arrays, height and width, brightness values in each pixel
Normal images use uint8 for each pixel/channel value
Here are two images of very different sizes. One, we convert to grayscale as well, and print the sizes and data types for each array.
np.array converts an input list, array, string, tuple into an array of the same dimensions
np.asarray similar, but doesn’t always copy the data
array.astype an array method, produces a view or copy with a new type
np.zeros creates an array of an input size and type, filled with zeros
np.ones similar, but fills with ones
np.arange, given a starting value, ending value, and step size, builds an array with those values (works on floats!)
np.random.rand, creates an array of a given size and data type, filled with random values in a specified range
No nested loops on arrays (if we can help it)
Rather than writing code that loops over rows and columns of a Numpy matrix, we usually try to perform matrix/vector arithmetic. We can add two arrays together, if they are the same shape. This adds corresponding values together and builds an array of the result. We can do subtraction, multiplication, and division similarly. We can also compute dot product and cross produce, if needed.
Slicing on arrays works similarly to slicing on strings or lists. But we can specify multiple dimensions at one time. Slicing weirdness: if the bound on the slice goes beyond the limits of the list, string, or array, Python doesn’t given an error. It just treats that as a “go to the end” marker and returns up to the end of the current dimensions.
Schedule slowdown: I decided to slow down the pace for a bit, to let those of you who have been behind a chance to catch up. Moodle and the Course Plan document have been updated.
Today we will continue with Monday’s topics and focus on image arithmetic, and maybe a little bit of the split and merge functions.
Friday we will continue that, along with Numpy array slicing used to create regions of interest (ROIse).
Monday next week we have folks from the Hamre Center coming to do a health and wellness workshop for part of the day, then the rest of the day will be a catch-up day.
On any of these days, you can get started on Homework 2 and ask questions
Download BallFinding.zip for the current activity in new section at the top of our Moodle page
Be prepared to discuss Ethics and Sci-Fi 5 this Friday!
Teams for Week 5
Sabrina, Patrick, Ryder, Elabe
Wheaton, Farihatou, Skye, SydneyP
Lunga, Ethan, Mohamud
Wyatt, Lindsay, Fiona
Bowen, SydneyG, Tene
Today’s topics
Image arithmetic
Splitting and merging channels
Image arithmetic
Some typical uses for image arithmetic
Brighten or darken an image by adding to channel values
#| eval: false
img1 = cv2.imread("SampleImages/mightyMidway.jpg")
img2 = cv2.imread("SampleImages/antiqueTractors.jpg")
# Find the smallest dimensions across both images
(h1, w1, d1) = img1.shape
(h2, w2, d2) = img2.shape
wid = min(w1, w2)
hgt = min(h1, h2)
# Crop the two images to the sizes they share
newImg1 = img1[:hgt, :wid]
newImg2 = img2[:hgt, :wid]
# Blend the resulting images
blendIm = cv2.addWeighted(newImg1, 0.5, newImg2, 0.5, 0)
cv2.imshow("Blended", blendIm)
cv2.waitKey()
The split and merge functions
We can pull images apart using Numpy commands, but sometimes OpenCV’s are easier to remember.
The split function takes in a color image, and returns a tuple of its channels as separate arrays The merge function takes in a tuple of channels, and combines them together.
The code example below creates various negative versions of an image. In film cameras, the film stores a negative version of the image, where light parts are dark, and vice versa. This is turned into correct colors or brightness during film process.
We can create negatives by reversing the values in one or all channels of an image. So if a pixel had the value 0 originally it will become 255, if it had the value 10, it will become 245, and if it had the value 200, it would become
This might seem difficult to compute, but actually it is very simple. If we subtract the image array from 255, it gives us this negative effect.
The code example below demonstrates both negative images, and the split and merge functions.
First it reads in a colorful mountain picture
It converts it to grayscale and produces the negative of the grayscale (understanding negatives is easier with grayscale images)
It also converts all three channels to be a negative and displays it
Finally, it splits the image into three channels, and shows the effect of converting just one channel at a time to a negative
import cv2# Read original imageimg = cv2.imread("SampleImages/landscape1MuchSmaller.jpg")cv2.imshow("orig", img)# Grayscale, and gray negativegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)grayNeg =255- graycv2.imshow("Gray original", gray)cv2.imshow("Negative gray", grayNeg)cv2.waitKey()# Overall negativeimg2 =255- imgcv2.imshow("OverallNegative", img2)cv2.waitKey()(bc, gc, rc) = cv2.split(img)negGC =255- gcnegBC =255- bcnegRC =255- rc# Only blue channel negativenewIm1 = cv2.merge((negBC, gc, rc))cv2.imshow("newBlue", newIm1)cv2.waitKey()# Only green channel negativenewIm2 = cv2.merge((bc, negGC, rc))cv2.imshow("newGreen", newIm2)cv2.waitKey()# Only red channel negativenewIm3 = cv2.merge((bc, gc, negRC))cv2.imshow("newRed", newIm3)cv2.waitKey()
Check the Changed Files section and look for comments on lines of code
Check the “Conversation” option
Most of you have not yet submitted Activiy 8, the String/List activity. I recommend you submit what you have and move on to Activity 9 today. Remember:
You can submit ICAs late, at any point
You get full credit even if you only partially complete the activity
Homework 2 due date moved to Friday, October 10
See notes above about the change in topics…
Be prepared to discuss Ethics and Sci-Fi 5 today!
Regions of interest
A region of interest is just a rectangular section of an image that we want to isolate and work with.
We can make regions of interest using Numpy’s slicing operator. But we must remember that by default we get a view to the original data, and not a copy of the data. Changes made to the ROI usually show in the original image.
mushIm = cv2.imread("SampleImages/mushrooms.jpg")cv2.imshow("Mushrooms", mushIm)# Two regions of interest centered on mushroom topsroi1 = mushIm[135:230, 50:170]roi2 = mushIm[120:215, 255:390]cv2.imshow("roi1", roi1)cv2.imshow("roi2", roi2)cv2.waitKey()
Notice that with slicing we put the row range first, and then the column range. If we wanted to draw a rectangle around the region on the image, we would swap the order of the indices to give the upper left and lower right points (as in the code below).
Explain some examples of pseudocode that you have already seen
Work through examples showing how to write pseudocode
What is an algorithm?
An algorithm is an idea, a concept. It is a description, in any form, of a process for solving a problem. Remember examples of daily-life algorithms we discussed at the beginning of the semester:
Cooking recipes
Assembly instructions
Driving directions
Arithmetic operations (how to add two multi-digit numbers, for instance)
In Computer Science, we have whole courses that study algorithms.
We want to think about algorithms separately from the peculiar specifics of any particular programming language
We would like to be able to translate an algorithm into any programming language
Programming languages are designed to be understandable by computers
We’d like to be able to describe algorithms in a way easier for humans to understand
Thus, the invention of pseudocode!
What is pseudocode?
Pseudocode is a step-by-step description of an algorithm, written for human use, not machine use.
What does that mean, exactly?
Pseudocode describes an algorithm, typically thought of like a function
Pseudocode uses a combination of:
Numbered steps
English and math notation for what to do
Code-like control structures like conditionals and loops
Indentation or begin/end statements to help clarify the parts of control structures
Pseudocode focuses on the logic and meaning of each step, not how it is done in any particular language
Each step in pseudocode should be something a program could do, realistically
We avoid writing pseudocode using the notation of any particular programming language
There are no fixed rules for what pseudocode should look like (ignore online sources that suggest otherwise)
Why is pseudocode useful?
Reason one: It is easier to read and write pseudocode as people than to read and write code, especially if you are not fluent in the specific language the code is written in.
Reason two: Coding works better if we take time to design our code before we start writing it in our programming language.
It’s easier to describe something in English than to write accurate, syntactically correct Python code
We can work out our thinking about how to solve the problem on paper, or a whiteboard, and talk about it with other people
Once we have a good design, the task becomes translating that design into code, which is easier than coding from scratch.
An analogy: Imagine you needed to express something complicated in a language you know a little bit, but are not completely fluent in. Writing down what you want to say in your native language, and then translating it into the new language, might be a sensible way to proceed. That’s what we are doing when we write out what we want the computer to do in English, as pseudocode, first.
Examples of pseudocode you’ve seen already
From Homework 1:
Algorithm drawCenteredSquares(image, color)--- This takes an image and a color, and draws 3 squares with sizes 50, 100, 150,--- centered in the image1. Find out the width and height of the image--- Calculate the midpoint of the image2. Let midX = width / 23. Let midY = height / 24. Repeat for size = [50, 100, 150] --- Compute the upper-left and lower-right corners of the square5. Let ulx = midX - (size / 2)6. Let uly = midY - (size / 2)7. Let lrx = midX + (size / 2)8. Let lry = midY + (size / 2) --- Draw the square9. Draw the square on the image using OpenCV, with tuples (ulx, uly), (lrx, lry), color, and thickness of 2
From ICA 4:
You should replace the TODO comment with a for loop that repeats six time with i as its loop variable. Inside the loop:
Define a variable to hold the ith color in the colors list
Calculate the center x value according to this formula: \(x = 100 \cdot (i + 1)\)
Draw a filled-in circle on the image with the calculated x value, and a y value of 150, a radius of 50, and the selected color
Also from ICA 4:
Import the cv2 module
Read in the grandTeton.jpg image and save it in a variable
Repeat the following 50 times
Assign a new variable to hold a copy of the original image
Calculate the (x, y) coordinates for the center of the ellipse as: \(x = 10i+10\) and \(y=5i+10\)
Draw an ellipse on the new copy, with (x, y) for the centerpoint, and with axes of size 50 and 15, and color (245, 245, 175) (determine the other input values based on the example output below)
Display the copy
Wait for 30-50 milliseconds
(After the loop) Display the image again
Wait, as normal, for the user to hit a key
From ICA 5:
Start by importing cv2 and the os module
Use os.listdir("SampleImages") to get a list of the files and folders in SampleImages. Save the returned value into a variable called allFiles.
Before moving on, temporarily add a print statement, and print the value of allFiles. Examine it, make sense of what the list contains, and make sure it is doing the right thing (ask for help if it isn’t)
Next, write a for loop that loops over the strings in allFiles: call the loop variable name.
Again, before moving on, temporarily add a print statement and print the loop variable. It should be a file or folder name from SampleImages
Inside the loop, add an if statement to check if the current name is an image file:
Use this string slicing to extract the last three characters: name[-3:]
Use or to perform two comparisons: Compare the sliced substring to jpg, and separately compare it to png
If the test of the if is True, then:
Attach the folder name to the file name like this: "SampleImages/" + name
Read in the image using the path string you created
Display the image in the "Slideshow" window
Wait for the user to type a key
How to write an algorithm in pseudocode… examples
Example 1
Suppose I have a list of names, and I want to make a new list that has all the names that start with a certain letter.
Thinking of it as an algorithm, the data I want the algorithm to operate on is the letter, and a list of names
So I will make those inputs to the algorithm, and write the starting line:
Algorithm selectNames(letter, nameList)
Will do the rest live in the video and post the result here:
Algorithm selectNames(letter, nameList)
1. Set up answerList as an empty list
2. Loop over names in nameList, let name be the next name each time
3. If the first letter in name equals letter (from above) then
4. Add name to answerList
5. Return/output answerList
Example 2
Suppose I am taking a road trip, and I want to calculate how many hours I spend driving on a given day, excluding the time I might spend off the road: at a rest stop, eating, getting gas/charging my car, visiting roadside attractions. So I write down all the times when I started or stopped driving.
For simplicity, all times will be given in 24-hour time (so 1:00pm will be written as 13:00)
I won’t ever drive past 11:00pm (23:00 in 24-hour time), so we only have to worry about one day at a time
For example, this might be my record for one day of my trip:
Started driving at 8:10
Stopped for gas at 10:45
Started driving again at 11:05
Stopped for lunch at 13:20
Started driving again at 14:00
Stopped driving to see giant fish statue at 16:50
Started driving again at 18:00
Stopped driving for the evening at 20:35
We want to compute how many hours I actually spent on the road.
Step one: Representing the data (this will often be given to you). I want to represent the start and stop times in Python.
Each time has an hour of the day, and a number of minutes. These are different units, so we can’t combine them easily
I will use a list that has exactly 2 items in it (could also be a tuple). So 10:45 would be [10, 45]
For the whole day’s record, I would make a list holding the times:
Now we have refined the problem to something we can tackle: I want to write an algorithm that takes in this list of sublists, pairs up adjacent times that represent starting and stopping times, and for each calculates the time difference, and then adds them all up.
(I’ll do the rest of this live in the video, and will add the final product here)
Algorithm computeDriveTime(startStopList)
totalMins = 0
Repeat over pairs from startStopList, looking at 1st and 2nd, then 3rd and 4th, etc.
Let start be the first time of the current pair
Let stop be the second time of the current pair
Convert start to be in minutes ( start[0] * 60 + start[1] )
Convert stop to be in minutes ( stop[0] * 60 + stop[1] )
Compute elapsedMins as stop - start
Add elapsedMins to totalMins
Return totalMins / 60
Week 6
Monday, October 6
Announcements
Homework 2 due date moved to Wednesday, October 15
See notes above about the change in topics…
Be prepared to discuss Ethics and Sci-Fi 6 on Friday!
Starting today you will pick your own partners for working in class!
Today’s topics
Visit from Hamre Center for workshop on living healthily as a college student
Catch up on ICAs or work on Homework 2
Wednesday, October 8
Announcements
Choose your own team starting today
Questions for Miro for today:
Look at Comprehension quiz, can you answer those questions?
What are you working on today?
Questions from Homework 2
Homework 2 is due next week, Wednesday!
Coding quiz 2 coming up next week: See study guide posted under next Wednesday
Be prepared to discuss Ethics and Sci-Fi 6 Friday!
Today’s topics
While loops
Working with the web-cam
While loops
Indefinite looping is when the loop continues for an unknown number of repetitions. The number of repetitions depends on a boolean test: the loop continues until the test becomes False.
While loops are a more “primitive” kind of loop, more simple, leaving more responsibility on the programmer. As the programmer, you are responsible for setting up and updating the loop variable as well as any ordinary accumulator variables.
Below is a version of the Numpy arange function, but for lists. Given a starting value, and ending value, and a step size, all floating-point numbers, it produces a list containing the sequence of values starting with the starting value and increasing by step size each time, up to but not including the end point.
Processing frames from a live webcam works the same as processing saved video files (.avi or .mp4 formats). Videos are just sequences of images, but we have to talk to the operating system for permission to connect with the camera or to get the frames from a saved video.
OpenCV provides a VideoCapture object that knows how to connect to cameras or video files. We first create an instance of a VideoCapture object, and then use its read method to get the images from the video.
VideoCapture has one required input: either the number of the webcam on your system, or a string that gives the path to a saved video file. The built-in webcam is (usually) number zero.
To see the frames of the video in real-time, we need to change how we use waitKey and use more of its capabilities.
Notes about waitkey:
waitKey is responsible for actually displaying the windows with images
waitKey takes in a number, if 0, then it pauses indefinitely to wait for the user to hit a key, if the input is a positive integer, it pauses only for that many milliseconds and then returns and lets the program continue
waitKey returns a number that indicates the user’s input: -1 if they didn’t input anything, and the numeric code for the key the entered, otherwise.
vidCap = cv2.VideoCapture(0)whileTrue: gotFrame, img = vidCap.read()ifnot gotFrame:print("Got no frame")break cv2.imshow("Webcam", img) x = cv2.waitKey(10)if x >0:ifchr(x) =='q':break
Friday, October 10
Announcements
Discuss Ethics and Sci-Fi 6 today
Today’s topics
Masking images
Making threshold images
Threshold images as masks
Overall context
We started with image arithmetic and ROIs learning about how we can manipulate images. We can brighten, darken, and blend images together. We can separate an image into separate channels and manipulate those, and we can crop out regions of an image using Numpy array slicing.
Starting today, we are going to explore more image processing methods, ones that transform the image to simplify it in one way or another. We need to simplify images in order for the computer to be able to extract information from them.
Extracting information from images is the foundational goal of computer vision. By that we mean:
Separating objects in an image from each other (which pixels belong to a person, to a chair, to a tree)
Identifying a specific object in an image
Being able to recreate a 3d approximation of a 2d image
Recognizing when two images are of the same location
And so on…
What is a mask, and how can we make them?
A mask is a black and white image (every pixel is either black or white) that we use to block out part of an image and let other parts show through.
Mask image pixels are almost always either full black or full white
A mask image is the same size and shape as the image it is meant to apply to
When we apply the mask to the image, we get a new image where each pixel’s color is determined by:
if a mask pixel is black, then the same pixel in the new image is black
if a mask pixel is white, then the same pixel in the new image has the color from the same pixel in the original
A metaphor:
Consider a printed photograph
A mask for that photograph would be a black piece of paper the same size as the photograph, with holes cut in it
We apply the mask by placing the black paper over the photo paper
The new image is what we see as a result
Making a mask:
There are two main ways to make a mask:
By hand: we create a black image of the right size and shape, and then we use drawing functions to draw white shapes on it
By programming: we use a program to generate a black and white image from the original, then use it as a mask
The second method is what we do with thresholding: generate black and white images
Converting images from one color representation to another
OpenCV provides a function, cvtColor, for converting from one color representation to another. The table below shows three examples of uses of this function, and when we might want to use it.
cvtColor Example
Description
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Converts img from color to grayscale
cv2.cvtColor(img, cv2.COLOR_BGR2HSV')
Converts img from BGR color to HSV color
cv2.cvtColor(img, cv2.COLOR_GRAY2BGR')
Converts img from grayscale to BGR
In particular, we will need to convert color images to grayscale for some of the thresholding functions we will be using in the next sections. We also could create a mask with just width and height, and then turn it into a color shape using cvtColor, as well.
What is thresholding an image?
When we threshold an image, we separate the pixels in the image according to whether their values are above or below some threshold, or within some range. Most often, the result is a black and white image, although some threshold functions can be used in other ways.