# Working with Images in NumPy

This tutorial will walk through a short introduction to NumPy with emphasis on the tools that can be used for working with images. For more details, see NumPy’s excellent documentation.

At their core, images are just two dimensional arrays or matrices of pixels. These pixels might have one value representing their gray value where 0 is black and some upper bound (usually 255) is white or they might have three values representing their red, green, and blue values respectively. Hence, in working with images, it is natural to want a tool that allows us to create and manipulate large arrays of numbers. NumPy is the premier package in Python for doing just that.

This tutorial is structured similarly to the Python tutorial in which each of
the follow sections corresponds to a script which introduces some concept. As
an important note, using NumPy in a script requires `import numpy as np`

at
the beginning.

## NumPy Array

The NumPy array is very similar to the list. To create a NumPy array, we can
pass a list to `np.array()`

. We can access values in the array the same way
we did in a list. However, the NumPy array does not have the `.append()`

method. Additionally, the `+`

operator has a different meaning when applied
to two NumPy arrays: if the arrays are of the same size, it adds arrays
together element-wise. Two other helpful methods for initializing arrays are
`np.zeros()`

and `np.ones()`

which initializes arrays of all zeros or ones
in the shape given.

```
# numpy_array.py
import numpy as np
x = [1, 2, 3]
y = np.array([1, 2, 3])
print(x) # [1, 2, 3]
print(y) # [1 2 3]
print(x[0]) # 1
print(y[0]) # 1
print(x + x) # [1, 2, 3, 1, 2, 3]
print(y + y) # [2 4 6]
w = np.zeros(3)
z = np.ones((2, 2))
print(w) # [0. 0. 0.]
print(z)
# [[1. 1.]
# [1. 1.]]
```

## Array Attributes

The three most common attributes of an array are the dimension `ndim`

, size
`size`

, and shape `shape`

. The script below gives all three attributes of
two example arrays.

```
# array_attributes.py
import numpy as np
A = np.array([[1, 2, 3],[4, 5, 6]])
print(A)
# [[1 2 3]
# [4 5 6]]
print(A.ndim) # 2
print(A.size) # 6
print(A.shape) # (2, 3)
B = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
print(B)
# [[[1 2]
# [3 4]]
#
# [[5 6]
# [7 8]]]
print(B.ndim) # 3
print(B.size) # 8
print(B.shape) # (2, 2, 2)
```

## Indexing and Slicing

A common operation you will want to do is access part of an array. The
notation `x[i:j]`

gives the `i`

th through `j`

th (not including `j`

)
values in the array `x`

. We can use `x[i:]`

or `x[:i]`

to get all the
values after or before the `i`

th value respectively. It should be noted that
this notation also works with the Python list.

```
# indexing.py
import numpy as np
A = np.array([0, 1, 2, 3, 4])
print(A[2:4]) # [2 3]
print(A[2:]) # [2 3 4]
print(A[:2]) # [0 1]
print(A[-2:]) # [3 4]
B = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(B)
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
print(B[1:])
# [[4 5 6]
# [7 8 9]]
print(B[:, 1:])
# [[2 3]
# [5 6]
# [8 9]]
print(B[0:2, 0:2])
# [[1 2]
# [4 5]]
C = np.zeros((3,3))
C[0:2, 0:2] = np.ones((2,2))
print(C)
# [[1. 1. 0.]
# [1. 1. 0.]
# [0. 0. 0.]]
```

## Conditional Array

Another way to slice an array is with a condition. The syntax for this is
`x[condition]`

. If we just look at the result of the condition, it returns
an array of boolean values where the value is `True`

if the corresponding
element satisfied the condition and `False`

otherwise. Passing this boolean
array to the slicing notation indicates which values to keep.

```
# condition_array.py
import numpy as np
A = np.array([1, 2, 3, 4, 5])
print(A > 2) # [False False True True True]
print(A[A > 2]) # [3 4 5]
B = np.array([[1, 2], [3, 4]])
print(B < 4)
# [[ True True]
# [ True False]]
print(B[B < 4]) # [1 2 3]
```

## Array Math

The addition, subtraction, multiplication, and division operations for values correspond to the element-wise operations for arrays. Element-wise meaning that the operation is applied to corresponding elements in the two arrays. We can also apply more advanced mathematical functions to an array using the NumPy implmentation.

```
# array_math.py
import numpy as np
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])
print(A + B) # [5 7 9]
print(B - A) # [3 3 3]
print(A * B) # [ 4 10 18]
print(B / A) # [4. 2.5 2. ]
print(np.power(A,2)) # [1 4 9]
print(np.sin(A)) # [0.84147098 0.90929743 0.14112001]
```

## Miscellaneous Operations

There are some additional array operations that may be useful. `.max()`

and
`.min()`

can be used to get the minimum or maximum element in an array
respectively. An array can be transposed (axes swapped) with `.T`

. Lastly,
`.vstack()`

and `.hstack()`

can be used to vertically or horizontally stack
a pair of arrays.

```
# misc_operations.py
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A.min()) # 1
print(A.max()) # 4
print(A.T)
# [[1 3]
# [2 4]]
print(np.hstack((A,B)))
# [[1 2 5 6]
# [3 4 7 8]]
print(np.vstack((A,B)))
# [[1 2]
# [3 4]
# [5 6]
# [7 8]]
```

That concludes the tutorial! There is an endless amount to learn about the NumPy Python package. Feel free to explore the documentation further to learn more neat capabilities.