One of the nice perks about working at a University is the opportunity to go to a wide variety of all kinds of classes and workshops. I just went to a really great workshop about Image Analysis in Python, given by Brian Keating, a UChicago RCC staff member. The materials for the class are on the lecturer’s Github:
https://github.com/brikeats/Image-Analysis-in-Python.
The post below is the code I wrote to count change in a picture Brian provided as a hands-on exercise. Please note that a blank copy of the guided steps (marked here in bold/italics), as well as another sample solution, are independently and originally available from Brian’s course materials- I’m just wanting to share here the code I filled in for each step (and heavily cribbed from the intro workshop materials).
This is the initial picture for analysis, containing only quarters, dimes and pennies (no nickels).
Step 1: Reading & displaying the image. Use matplotlib to read the file “sample-images/quarters_dimes_pennies.png
”. Convert it to grayscale and display both the original and grayscale images.
Step 2: Thresholding. Create a binary mask by thresholding the grayscale image.
You can use a histogram and some guesswork to determine the threshold or use one of the threshold functions in skimage.filters
. I’m using Ostu’s method.
Step 3: Cleaning up the mask. Your mask will inevitably have some noise. Use morphology operators to clean up the mask. It doesn’t have to be 100% perfect, but you should be able to get rid of the specks.
Step 4: Masking. It will be convenient to set the background to black. Use the coin mask that you created to set the backgrounds of both the original color image, and the grayscale image to zero. You should be able to see the coins in color, but the counter (and the reflections in the counter) should be black. While you’re at it, set the background of the grayscale image to zero as well. From now on, we don’t have to worry about the background affecting our results because we’ve masked it out.
Step 5: Watershed Segmentation Now that we’ve segmented the foreground from the background, we want to distinguish the coins from each other. Use the watershed-based segmentation that was introduced in the cell counting demo to create a label image for the coins. Print the number of coins in the image.
distance transform: (475, 649) float64
number of coins: 30
Step 6: Quantifying & displaying the object sizes. Look up the documentation for scikit-image function regionprops
online. Use this function with the labelled coins image to compute the area of each coin and the location of each coin’s center (the center is called the “centroid”). Display the image and use matplotlib’s text
function to write the area of each coin at its center.
Step 7: Separate coins by size & count. It is possible to sort the coins on the basis of size. By trial and error, select size thresholds that can use a region’s area to determine the coin’s denomination. Count the number of each denomination, and print the total value of the coins in the image.
[ 3902. 2351. 3892. 2525. 3882. 2215. 2165. 3923. 3983. 2486.
2224. 2187. 4044. 2532. 4046. 4007. 2194. 2407. 3993. 4064.
2520. 2298. 2212. 4067. 2257. 2254. 2632. 2288. 2430. 4134.]
number of dimes: 10
number of pennies: 8
number of quarters: 12
Total value in image: $4.08
So $4.08 in the image! Looks about right. The thresholding on coin sizes at the end could be improved by perhaps either using colors of the coins or by doing some sort of clustering algorithm on the regions, knowing that there are only 3 types of coins. Good start anyway and a really great hands-on workshop exercise.