forked from nipraxis/textbook
-
Notifications
You must be signed in to change notification settings - Fork 0
/
arrays_and_images.Rmd
237 lines (181 loc) · 6.48 KB
/
arrays_and_images.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
---
jupyter:
jupytext:
notebook_metadata_filter: all,-language_info
split_at_heading: true
text_representation:
extension: .Rmd
format_name: rmarkdown
format_version: '1.2'
jupytext_version: 1.11.5
kernelspec:
display_name: Python 3
language: python
name: python3
---
# Arrays as images, images as arrays
You can consider arrays as images, and images as arrays.
We start off with the imports that we need.
```{python}
# The standard library for working with arrays.
import numpy as np
# Standard library for plotting.
import matplotlib.pyplot as plt
```
Let’s make an array of numbers between 0 through 99:
```{python}
an_array = np.array([[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 9, 99, 99, 94, 0],
[ 0, 0, 0, 25, 99, 99, 79, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 56, 99, 99, 49, 0],
[ 0, 0, 0, 73, 99, 99, 31, 0],
[ 0, 0, 0, 91, 99, 99, 13, 0],
[ 0, 0, 9, 99, 99, 94, 0, 0],
[ 0, 0, 27, 99, 99, 77, 0, 0],
[ 0, 0, 45, 99, 99, 59, 0, 0],
[ 0, 0, 63, 99, 99, 42, 0, 0],
[ 0, 0, 80, 99, 99, 24, 0, 0],
[ 0, 1, 96, 99, 99, 6, 0, 0],
[ 0, 16, 99, 99, 88, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0]])
an_array.shape
```
In fact this array represents a monochrome picture of a letter.
We can show arrays as images using the `plt.imshow` command from
[matplotlib](http://matplotlib.org/). Here is the default output:
```{python}
plt.imshow(an_array)
```
The image is weirdly colorful. That is because matplotlib is using the default
*colormap*. A colormap is a mapping from values in the array to colors. The default Matplotlib colormap is called `viridis` and maps low numbers
in the image (0 in our case) to purple, and high numbers (99 in our case) to
yellow.
We can see the relationship of the numbers to the colors by asking matplotlib
to show the colormap:
```{python}
plt.imshow(an_array)
plt.colorbar()
```
In our case, our image would make more sense as grayscale, so we use the
`gray` colormap, like this:
```{python}
plt.imshow(an_array, cmap='gray')
plt.colorbar()
```
A grayscale image is an array containing numbers giving the pixel intensity
values - in our case between 0 and 99.
Here we set `gray` to the default colormap for the rest of our plots:
```{python}
# Set 'gray' as the default colormap
plt.rcParams['image.cmap'] = 'gray'
```
This setting means that, by default, Matplotlib will use the `gray` colormap for the *rest of this session*.
```{python}
# The default colormap is now gray.
plt.imshow(an_array)
plt.colorbar()
```
Matplotlib does not save this change, so we would have to rerun this command
for each session we want to change the default. There are ways of making
[Matplotlib remember default
changes](https://matplotlib.org/stable/tutorials/introductory/customizing.html),
but we won't go into those here.
We can also plot *lines* in matplotlib. For example, here are the values from
the row at position 8 in this array. Because Python indices start at 0, this is
the 9th row of the array.
```{python}
# Row position 8, all the columns.
an_array[8, :]
```
Actually, we can omit the second `:`, Numpy will assume we mean all the columns, if we just specify the row:
```{python}
# Also, row position 8, all the columns.
an_array[8]
```
We might want to plot the values in row 8. To do this, we use the `plt.plot` function. The first argument to this function is the `x` values for the plot, and the second argument is the `y` values. We will use the column positions as for the `x` values:
```{python}
col_positions = np.arange(8)
col_positions
```
```{python}
# Specifying x and y values.
plt.plot(col_positions, an_array[8])
```
The x axis is the column position in the array (0 through 7) and the y axis is
the value of the array row at that position.
In fact, we can even omit the `x` values in this case, and Matplotlib will assume we mean the x values to be `np.arange(8)`, where 8 is the length of the `y` values array.
```{python}
# Just specifying the y values, we get the x values default.
plt.plot(an_array[8])
```
The plot shows us the 0 values at the edges of the bar of the “i”, and the ramp
up to the peak at the middle of the bar of the “i”, in columns number 3 and 4.
A transpose in numpy uses the `.T` method on the array. This has the effect
of flipping the rows and columns (in 2D):
```{python}
an_array.T
```
```{python}
# Defaults to gray colormap
plt.imshow(an_array.T)
```
We can also reshape the original array to a 1D array, by stacking all the rows
end to end.
Here's the original array:
```{python}
an_array
```
Here is the array, reshaped to 1D
```{python}
a_1d_array = np.reshape(an_array, 15 * 8)
a_1d_array
```
```{python}
a_1d_array.shape
```
Reshaping the array to one dimension is a common operation, so there is a
separate Numpy command for that, `np.ravel`:
```{python}
np.ravel(an_array)
```
One use of the 1D version of the array, is for making a histogram of the
distribution of values in the array:
```{python}
plt.hist(a_1d_array)
```
By default, the `plt.hist` function uses 50 bins, but you can specify how
many bins you want with the `bins` keyword:
```{python}
plt.hist(a_1d_array, bins=75)
```
As you can imagine, it's not hard to go back to the 2D shape, by splitting the
1D array back into 15 rows of 8 values each (and therefore 8 columns):
```{python}
array_back = np.reshape(a_1d_array, (15, 8))
array_back
plt.imshow(array_back)
```
In numpy, basic operations like multiplication, addition, comparison, are
always elementwise. For example, this multiplies every array value by 10:
```{python}
an_array * 10
```
Comparison is also elementwise. For example, this gives True for every value >
50, and False for every value <= 50:
```{python}
an_array > 50
```
Matplotlib will treat False as 0 and True as 1, so this is one way of
binarizing the image at a threshold (of 50 in this case):
```{python}
plt.imshow(an_array > 50)
```
We can slice arrays as we slice strings or lists. The difference for arrays is
that we can slice in any or all dimensions at the same time. For example, to
get the dot of the “i” it looks (from the numbers at the sides of the ploat)
that we want to the top 4 rows, and the last 5 columns:
```{python}
an_array[0:4, 3:]
plt.imshow(an_array[0:4, 3:])
```