Hack w/ Limbic Media

Interactive Electronica and IoT

Visit Limbic Media

hack.limbicmedia.ca was created by the developers and engineers at Limbic Media to share their knowledge and experiences with DIY and hacker community-at-large.

by Steven Bjornson

NumPy = Love

Currently I am doing a research internship at Limbic (paid for in part by MITACS). While the details of this research are TOP SECRET! I can reveal that the nature of the work involves creating audio signal processing algorithms and, while may come as a surprise, I use Python for this development.

`PYTHON!?` (you might be thinking) `But it's an interpreted language and is therefore inherently slow. Why would you ever use it for audio signals?`

Well, the answer is fairly simple: NumPy

NumPy is a module for Python which takes advantage of Python's ability to interact with compiled C code (see: CTypes or SWIG for more on this) to suppot multi-dimensional arrays. NumPy also gives a host of methods for operating on these arrays (e.g. dot product, cross product, FFT!). Essentially, C-array data structures with a backend (and high level Python interface) to operate on these arrays.

A bunch of other Python modules can take advantage of this speed: PILLOW, SciPy, and Matplotlib (just for example).

(SciPy is especially sweet because it adds Matlab™ like functionality BUT with some serious speed thanks to the C back end).

I should point out that I do not use Python for Real-Time signal processing but instead take advantage of the Python-NumPy combo to quickly prototype different processes and analyze the outcome. While writing processes in C/C++ is still basically a necessity for Real-Time operations, analysis of signals is annoying. In the end, after I know things are going to work (because Python showed me it will) I can then take the steps port the process into a compiled language.

TLDR; NumPy extends Python with C arrays making it a great tool for research and experimentation.

Here's a an example of an image filter (threshold) using NumPy and PILLOW (and a bit of Matplotlib)

Importing the necessary modules:
``````import numpy as np
import matplotlib.pyplot as plt
``````
``````img = Image.open('me.jpg')
``````

Example Image: Convert PIL datatype to NumPy array:
``````img_array = np.asarray(img)
``````

This is an easy conversion and allows to manipulate the image as a C-type array.

Plot a histogram of the image:
``````width = img_array.shape
height = img_array.shape
pix = img_array.shape #number of pixels

# graph the histogram (256 bins for the 256 different values)
hist_array = np.reshape(img_array, width * height * pix) #this makes the image 1d array.

# hist_array = hist_array.flatten() #alterntive method

plt.hist(hist_array, bins=256, normed=1)
plt.show()
#plt.savefig('hist.png') #you can also save the graph
`````` The histogram can be useful for finding where the energy in the image is. For example it might be useful to keep only the pixels with values between `0 and 25` and values between `180 and 200`.

I'm not using this for this example but it's nice to see how easy analysis can be.

Get some other info:
``````#stats
mean = np.mean(img_array)
std = np.std(img_array)
``````

These are some useful stats about the image.

Make a mask and use it to filter the image:
``````#find all the values above the mean

``````

The `mask` variable is an array with the same shape as `img_array` but values greater than the `mean + standard deviation` set to `1`.
So, when the `img_array` is multiplied by the `mask`, the resulting array is filtered since all values less than `mean + std` are set to 0.

Output the image:
``````#convert output image back to Image type for export
out_img = Image.fromarray(filtered_array)

out_img.save('filtered_image.jpg')
``````

It's easier to save the image as as a regular file using PILLOW, so we convert the NumPy array back into an Image (how convenient that PILLOW has a `fromarray` method!).

Here's the result: Anyone who uses Photoshop will recognize this as the `threshold` function.

Author