Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

📚 The CoCalc Library - books, templates and other resources

132975 views
License: OTHER
1
2
3
4
```python
5
%matplotlib inline
6
import matplotlib
7
matplotlib.rcParams['image.cmap'] = 'gray'
8
matplotlib.rcParams['image.interpolation'] = 'nearest'
9
```
10
11
# RANSAC
12
13
From WikiPedia:
14
15
> Random sample consensus (RANSAC) is an iterative method to estimate
16
> parameters of a mathematical model from a set of observed data which
17
> contains outliers. Therefore, it also can be interpreted as an
18
> outlier detection method.
19
20
[Gallery example](http://scikit-image.org/docs/dev/auto_examples/plot_matching.html)
21
22
23
24
```python
25
import numpy as np
26
from matplotlib import pyplot as plt
27
28
from skimage.measure import ransac, LineModelND
29
```
30
31
Let's set up some random data points:
32
33
34
35
```python
36
np.random.seed(seed=1)
37
38
# generate coordinates of line
39
x = np.arange(-200, 200)
40
y = 0.2 * x + 20
41
data = np.column_stack([x, y])
42
43
# add faulty data
44
faulty = np.array(30 * [(180., -100)])
45
faulty += 5 * np.random.normal(size=faulty.shape)
46
data[:faulty.shape[0]] = faulty
47
48
# add gaussian noise to coordinates
49
noise = np.random.normal(size=data.shape)
50
data += 0.5 * noise
51
data[::2] += 5 * noise[::2]
52
data[::4] += 20 * noise[::4]
53
54
plt.plot(data[:, 0], data[:, 1], '.');
55
```
56
57
58
![png](1_ransac_files/1_ransac_4_0.png)
59
60
61
Now, fit a line to the data. We start with our model:
62
63
$$\mathbf{y} = m \mathbf{x} + c$$
64
65
Or, in matrix notation:
66
67
$$\mathbf{y} = \left[ \begin{array}{c} \vdots \\ \mathbf{x} \\ \vdots \end{array}
68
\ \begin{array}{c} \vdots \\ \mathbf{1} \\ \vdots \end{array} \right]
69
\left[ \begin{array}{c} m \\ c \end{array} \right]
70
= X \mathbf{p}$$
71
72
TODO: the above is not rendered correctly. p is column vector.
73
74
Since we have an over-determined system, we use least squares to solve:
75
76
77
78
```python
79
x = data[:, 0]
80
y = data[:, 1]
81
82
X = np.column_stack((x, np.ones_like(x)))
83
84
p, _, _, _ = np.linalg.lstsq(X, y)
85
p
86
```
87
88
89
90
91
```output
92
array([ 0.04829453, 12.45526055])
93
```
94
95
96
97
With those parameters in hand, let's plot the resulting line:
98
99
100
101
```python
102
m, c = p
103
plt.plot(x, y, 'b.')
104
105
xx = np.arange(-250, 250)
106
plt.plot(xx, m * xx + c, 'r-');
107
```
108
109
110
![png](1_ransac_files/1_ransac_8_0.png)
111
112
113
Scikit-image provides an N-dimensional LineModel object that encapsulates the above:
114
115
116
117
```python
118
model = LineModelND()
119
model.estimate(data)
120
model.params
121
```
122
123
124
125
126
```output
127
(array([ 27.3093349 , 13.77415203]), array([-0.99847184, -0.05526279]))
128
```
129
130
131
132
Instead of ``m`` and ``c``, it parameterizes the line by ``origin``
133
and ``direction`` --- much safer when dealing with vertical lines,
134
e.g.!
135
136
137
138
```python
139
origin, direction = model.params
140
plt.plot(x, y, 'b.')
141
plt.plot(xx, model.predict_y(xx), 'r-');
142
```
143
144
145
![png](1_ransac_files/1_ransac_12_0.png)
146
147
148
Now, we robustly fit the line using inlier data selecte with the RANSAC algorithm:
149
150
151
152
```python
153
model_robust, inliers = ransac(data, LineModelND, min_samples=2,
154
residual_threshold=10, max_trials=1000)
155
outliers = (inliers == False)
156
157
yy = model_robust.predict_y(xx)
158
159
fig, ax = plt.subplots()
160
161
ax.plot(data[inliers, 0], data[inliers, 1], '.b', alpha=0.6, label='Inlier data')
162
ax.plot(data[outliers, 0], data[outliers, 1], '.r', alpha=0.6, label='Outlier data')
163
ax.plot(xx, yy, '-b', label='Robust line model')
164
165
plt.legend(loc='lower left')
166
plt.show()
167
```
168
169
170
![png](1_ransac_files/1_ransac_14_0.png)
171
172
173
## Going interplanetary
174
175
The sun is one of the most spherical objects in our solar system.
176
According to an [article in Scientific American](http://www.scientificamerican.com/gallery/well-rounded-sun-stays-nearly-spherical-even-when-it-freaks-out/):
177
178
> Earth's closest star is one of the roundest objects humans have
179
> measured. If you shrank the sun down to beach ball size, the
180
> difference between its north-south and the east-west diameters would
181
> be thinner than the width of a human hair, says Jeffery Kuhn, a
182
> physicist and solar researcher at the University of Hawaii at
183
> Manoa. "Not only is it very round, but it's too round," he adds. The
184
> sun is more spherical and more invariable than theories predict.
185
186
If the sun is spherical, we should be able to fit a circle to a 2D
187
slice of it! Let's load an example image:
188
189
190
191
```python
192
from skimage import io
193
194
image = io.imread('../../images/superprom_prev.jpg')
195
196
f, ax = plt.subplots(figsize=(8, 8))
197
ax.imshow(image);
198
```
199
200
201
![png](1_ransac_files/1_ransac_16_0.png)
202
203
204
In this specific image, we got a bit more than we bargained for in the
205
form of magnificently large solar flares. Let's see if some *canny
206
edge detection* will help isolate the sun's boundaries.
207
208
209
210
```python
211
from skimage import feature, color
212
213
f, ax = plt.subplots(figsize=(10, 10))
214
215
edges = feature.canny(color.rgb2gray(image), sigma=2)
216
ax.imshow(edges, cmap='gray')
217
```
218
219
220
221
222
```output
223
<matplotlib.image.AxesImage at 0x7f99be1dbe48>
224
```
225
226
227
228
229
![png](1_ransac_files/1_ransac_18_1.png)
230
231
232
The edges look good, but there's a lot going on inside the sun. We
233
use RANSAC to fit a robust circle model.
234
235
236
237
```python
238
from skimage.measure import CircleModel
239
240
points = np.array(np.nonzero(edges)).T
241
242
model_robust, inliers = ransac(points, CircleModel, min_samples=3,
243
residual_threshold=2, max_trials=1000)
244
```
245
246
The parameters of the circle are center x, y and radius:
247
248
249
250
```python
251
model_robust.params
252
```
253
254
255
256
257
```output
258
array([ 287.07798476, 306.07014353, 225.70127981])
259
```
260
261
262
263
Let's visualize the results, drawing a circle on the sun, and also
264
highlighting inlier vs outlier edge pixels:
265
266
267
268
```python
269
from skimage import draw
270
271
cy, cx, r = model_robust.params
272
273
f, (ax0, ax1) = plt.subplots(1, 2, figsize=(15, 8))
274
275
ax0.imshow(image)
276
ax1.imshow(image)
277
278
ax1.plot(points[inliers, 1], points[inliers, 0], 'b.', markersize=1)
279
ax1.plot(points[~inliers, 1], points[~inliers, 0], 'g.', markersize=1)
280
ax1.axis('image')
281
282
circle = plt.Circle((cx, cy), radius=r, facecolor='none', linewidth=2)
283
ax0.add_patch(circle);
284
```
285
286
287
![png](1_ransac_files/1_ransac_24_0.png)
288
289
290
The circular fit is, indeed, excellent, and rejects all the inner
291
squiggly edges generated by solar turbulence!
292
293
Note a general principle here: algorithms that aggregate across an
294
entire path are often robust against noise. Here, we have *high
295
uncertainty* in the solar edge, but also know that only the solar edge
296
pixels contribute coherently to the full circular path around the
297
solar edge.
298
299
## Exercises
300
301
Your small start-up, CardShark, run from your garage over nights and
302
evenings, takes photos of credit cards and turns them into machine
303
readable information.
304
305
The first step is to identify where in a photo the credit card is
306
located.
307
308
1. Load the photo `../../images/credit_card.jpg`
309
2. Using RANSAC and LineModelND shown above, find the first most
310
prominent edge of the card
311
3. Remove the datapoints belonging to the most prominent edge, and
312
repeat the process to find the second, third, and fourth
313
314
315
316
```python
317
f, ax = plt.subplots()
318
319
image = io.imread('../../images/credit_card.jpg')
320
ax.imshow(image);
321
```
322
323
324
![png](1_ransac_files/1_ransac_26_0.png)
325
326
327
----
328
### Warning: SPOILER ALERT. Solution follows!
329
----
330
331
332
333
```python
334
f, ax = plt.subplots(figsize=(10, 10))
335
336
edges = feature.canny(color.rgb2gray(image), sigma=3)
337
edge_pts = np.array(np.nonzero(edges), dtype=float).T
338
edge_pts_xy = edge_pts[:, ::-1]
339
340
for i in range(4):
341
model_robust, inliers = ransac(edge_pts_xy, LineModelND, min_samples=2,
342
residual_threshold=1, max_trials=1000)
343
344
x = np.arange(800)
345
plt.plot(x, model_robust.predict_y(x))
346
347
edge_pts_xy = edge_pts_xy[~inliers]
348
349
plt.imshow(edges);
350
```
351
352
353
![png](1_ransac_files/1_ransac_28_0.png)
354
355
356