This notebook contains an excerpt from the book Machine Learning for OpenCV by Michael Beyeler. The code is released under the MIT license, and is available on GitHub.

Note that this excerpt contains only the raw code - the book is rich with additional explanations and illustrations. If you find this content useful, please consider supporting the work by buying the book!

Dealing with Data Using Python's NumPy Package

If you followed the advice outlined in the previous chapter and installed the Python Anaconda stack, you already have NumPy installed and are ready to go. If you are more the do-it-yourself type, you can go http://www.numpy.org and follow the installation instructions found there.

I informed you earlier that it would be okay if you weren't a Python expert yet. Who knows, perhaps you're just now switching from OpenCV's C++ API. This is all fine. I wanted to give you a quick overview on how to get started with NumPy. If you are a more advanced Python user, you may simply skip this subsection.

Once you are familiar with NumPy, you will find that most scientific computing tools in the Python world are built around it. This includes OpenCV, so the time spent on learning NumPy is guaranteed to benefit you in the end.

Importing NumPy

Once you start a new IPython or Jupyter session, you can import the NumPy module and verify its version as follows:


In [1]:
import numpy

Recall that in the Jupyter Notebook you can hit Ctrl + Enter to execute a cell once you have typed the command. Alternatively, Shift + Enter executes the cell and automatically inserts or selects the cell below it. Check out all the keyboard shortcuts by clicking on Help > Keyboard Shortcut, or take a quick tour by clicking on Help > User Interface Tour.


In [2]:
numpy.__version__


Out[2]:
'1.12.1'

For the pieces of the package discussed here, I would recommend using NumPy version 1.8 or later. By convention, you'll find that most people in the scientific Python world will import NumPy using np as an alias:


In [3]:
import numpy as np

In [4]:
np.__version__


Out[4]:
'1.12.1'

Understanding NumPy arrays

Python is a weakly-typed language. This means that you do not have to specify a data type when you create a new variable. For example, the following will automatically be represented as an integer:


In [5]:
a = 5

You can double-check the variable type as follows:


In [6]:
type(a)


Out[6]:
int

Going a step further, we can create a list of integers as follows:


In [7]:
int_list = list(range(10))
int_list


Out[7]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Similary, we can create a list of strings:


In [8]:
str_list = [str(i) for i in int_list]
str_list


Out[8]:
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

However, lists are not very flexible to do math on. Let's say, for example, we wanted to multiply every element in int_list by a factor of 2. A naive approach might be to do the following – but see what happens to the output:


In [9]:
int_list * 2


Out[9]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

That's not really what we wanted!

Instead, operating on every element in the list gets really easy with NumPy:


In [10]:
int_arr = np.array(int_list)
int_arr


Out[10]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [11]:
int_arr * 2


Out[11]:
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

In addition, every NumPy array comes with a list of attributes:


In [12]:
print("int_arr ndim: ", int_arr.ndim)
print("int_arr shape: ", int_arr.shape)
print("int_arr size: ", int_arr.size)
print("int_arr dtype: ", int_arr.dtype)


int_arr ndim:  1
int_arr shape:  (10,)
int_arr size:  10
int_arr dtype:  int64

You can display all attributes and methods of a NumPy array by typing out the name of the array, add a period, and then hit <TAB>:

In [X]: int_arr.<TAB>

This will display a dropdown menu with a whole lot of attributes, such as the ones mentioned above.

Accessing single array elements by indexing

If you are familiar with Python's standard list indexing, indexing in NumPy will feel quite familiar. In a one-dimensional array, the i-th value (counting from zero) can be accessed by specifying the desired index in square brackets, just as with Python lists:


In [13]:
int_arr


Out[13]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [14]:
int_arr[0]


Out[14]:
0

In [15]:
int_arr[3]


Out[15]:
3

To index from the back of the array, you can use negative indices:


In [16]:
int_arr[-1]


Out[16]:
9

In [17]:
int_arr[-2]


Out[17]:
8

There are a few other cool tricks.

For example, return all elements from index 2 up to index 5, but don't include index 5:


In [18]:
int_arr[2:5]


Out[18]:
array([2, 3, 4])

Return all elements from the beginning of the array up to index 5 - 1:


In [19]:
int_arr[:5]


Out[19]:
array([0, 1, 2, 3, 4])

Return all elements from index 5 up to the end of the array:


In [20]:
int_arr[5:]


Out[20]:
array([5, 6, 7, 8, 9])

Return every other element, starting at index 0:


In [21]:
int_arr[::2]


Out[21]:
array([0, 2, 4, 6, 8])

Return all elements of the array, but in reverse order:


In [22]:
int_arr[::-1]


Out[22]:
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

Creating multi-dimensional arrays

Arrays can be N-dimensional. In machine learning, we will often deal with at least 2-D arrays, where the column index stands for the values of a particular feature, and the rows contain the actual feature values.

Let's say we want to create an array with 3 rows and 5 columns, with all elements initialized to zero. If we don't specify a data type, NumPy will default to using floats:


In [23]:
arr_2d = np.zeros((3, 5))
arr_2d


Out[23]:
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])

As you probably know from your OpenCV days, this could be interpreted as a 3x5 grayscale image with all pixels set to 0 (black). Analogously, if we wanted to create a tiny 2x4 pixel image with 3 color channels (R, G, B), but all pixels set to white, we would use NumPy to create a 3-D array with dimensions 2x4x3:


In [24]:
arr_float_3d = np.ones((3, 2, 4))
arr_float_3d


Out[24]:
array([[[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]],

       [[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]],

       [[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]]])

Here, the first dimension defines the color channel (R, G, B). Thus, if this were real image data, we could easily grab the red color information by slicing the array:


In [25]:
arr_float_3d[0, :, :]


Out[25]:
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])

In OpenCV, images either come as 32-bit float arrays with values between 0 and 1, or they come as 8-bit integer arrays with values between 0 and 255. Hence, we can also create a 2x4 pixel, all-white RGB image using 8-bit integers, by specifying the dtype attribute of the NumPy array, and by multiplying all the 1's in the array with 255:


In [26]:
arr_uint_3d = np.ones((3, 2, 4), dtype=np.uint8) * 255
arr_uint_3d


Out[26]:
array([[[255, 255, 255, 255],
        [255, 255, 255, 255]],

       [[255, 255, 255, 255],
        [255, 255, 255, 255]],

       [[255, 255, 255, 255],
        [255, 255, 255, 255]]], dtype=uint8)

Reminder about built-in documentation

As you read through this chapter, don't forget that IPython gives you the ability to quickly explore the contents of a package (by using the tab-completion feature), as well as the documentation of various functions (using the ? character)

For example, to display all the contents of the numpy namespace, you can type this:

$ ipython
In [1]: import numpy as np
In [2]: np.<TAB>

Try it out yourself in the empty cell below:


In [ ]:

And to display NumPy's built-in documentation, you can use this:

In [3]: np?

Then hit Shift+Enter to execute the cell.

Try it out yourself in the empty cell below:


In [ ]:

Here's another neat trick. Let's say you're typing away at a command to create linearly spaced values in an interval using NumPy's arange function, but you forgot the exact syntax. What you do is you start typing the function's name, then use Shift+TAB to display the function signature: In [4]: np.arange(<Shift+TAB> Try it out yourself in the empty cell below:


In [ ]:

More detailed documentation, along with tutorials and other resources, can be found at http://www.numpy.org.