diff --git a/Chapter08/cases/case_1.png b/Chapter08/cases/case_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f49f90a9a392ab491da1cf805c58a8a8394ec7f3
Binary files /dev/null and b/Chapter08/cases/case_1.png differ
diff --git a/Chapter08/cases/case_2.png b/Chapter08/cases/case_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad68f1c93a8155a3bcd08a5079a32eeb47977071
Binary files /dev/null and b/Chapter08/cases/case_2.png differ
diff --git a/Chapter08/cases/case_3.png b/Chapter08/cases/case_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..15d5694144c6172d2461b370dc292948c8ba414f
Binary files /dev/null and b/Chapter08/cases/case_3.png differ
diff --git a/Chapter08/cases/case_4.png b/Chapter08/cases/case_4.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f9c27aabae7efd3bb5987577a2a82a45ce35f83
Binary files /dev/null and b/Chapter08/cases/case_4.png differ
diff --git a/Chapter08/cases/case_5.png b/Chapter08/cases/case_5.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d4071d3186aa4fb901f3efb8e00ae3267097893
Binary files /dev/null and b/Chapter08/cases/case_5.png differ
diff --git a/Chapter08/cases/case_6.png b/Chapter08/cases/case_6.png
new file mode 100644
index 0000000000000000000000000000000000000000..34c744a3a1caf51295818628d1a0e0639e91cd07
Binary files /dev/null and b/Chapter08/cases/case_6.png differ
diff --git a/Chapter08/cases/case_7.png b/Chapter08/cases/case_7.png
new file mode 100644
index 0000000000000000000000000000000000000000..08195150435c2127b3668d66ccb99a3e0542d935
Binary files /dev/null and b/Chapter08/cases/case_7.png differ
diff --git a/Chapter08/make_data.py b/Chapter08/make_data.py
old mode 100755
new mode 100644
index 1a4419cb7ecf00a718f0e66757ed49fa51828204..442ad69850b828f383812d351e89614634b5d6cf
--- a/Chapter08/make_data.py
+++ b/Chapter08/make_data.py
@@ -1,107 +1,86 @@
-#%%
 
 import numpy as np
+import matplotlib.pyplot as plt
+import skimage.io
+import os
 
-
-def make_data(example_nr, n = 200, noise = 1):
-    '''
-    Generate data for training a simple neural network.
-    
-        Arguments:
-            example_nr: a number 1 to 3 for each example.
-            n: number of points in each class set.
-            noise: noise level, best between 0.5 and 2.
-        Returns:
-            X: 2 x 2n array of points (there are n points in each class).
-            T: 2 x 2n target values.
-            x: grid points for testing the neural network.
-            dim: size of the area covered by the grid points.
-
-        Authors: Vedrana Andersen Dahl and Anders Bjorholm Dahl - 25/3-2020
-        vand@dtu.dk, abda@dtu.dk
-    '''
-
-    rg = np.random.default_rng()
-    
-    dim = (100, 100)
+def make_data(example_nr, n_pts = 200, noise = 1):
+    '''Make data for the neural network. The data is read from a png file in the 
+    cases folder, which must be placed together with your code.
     
-    QX, QY = np.meshgrid(range(0, dim[0]), range(0, dim[1]))
-    x_grid = np.c_[np.ravel(QX), np.ravel(QY)]
-    
-    #  Targets: first half class 0, second half class 1
-    T = np.vstack((np.tile([True, False], (n, 1)), 
-                   np.tile([False, True], (n, 1))))
-    
-    if example_nr == 1 :  # two separated clusters
-
-        X = np.vstack((np.tile([30., 30.], (n, 1)), 
-                       np.tile([70., 70.], (n, 1))))
-        X += rg.normal(size=X.shape, scale=10*noise)  # add noise
-
-    elif example_nr == 2 :  # concentric clusters
-
-        rand_ang = 2 * np.pi * rg.uniform(size=n)
-        X = np.vstack((30 * np.array([np.cos(rand_ang), np.sin(rand_ang)]).T, 
-                       np.tile([0., 0.], (n, 1))))
-        X += [50, 50]  # center
-        X += rg.normal(size=X.shape, scale=5*noise)# add noise
+    Parameters:
+    example_nr : int
+        1-7
+    n_pts : int
+        Number of points in each of the two classes
+    noise : float
+        Standard deviation of the Gaussian noise
+
+    Returns:
+    X : ndarray
+        2 x n_pts array of points
+    T : ndarray
+        2 x n_pts array of boolean values
+    x_grid : ndarray
+        2 x n_pts array of points in regular grid for visualization
+    dim : tuple of int
+        Dimensions of the grid
     
-    elif example_nr == 3 :  # 2x2 checkerboard 
-        n1 = n//2
-        n2 = n//2 + n%2  # if n is odd n2 will have 1 element more
-
-        X = np.vstack((np.tile([30., 30.], (n1, 1)), 
-                       np.tile([70., 70.], (n2, 1)),
-                       np.tile([30. ,70.], (n1, 1)),
-                       np.tile([70., 30.], (n2, 1))))
-        X += rg.normal(size=X.shape, scale=10*noise)  # add noise
-
-    else:
-        print('No data returned - example_nr must be 1, 2, or 3')
-    
-    o = rg.permutation(range(2*n))
-    
-    return X[o].T, T[o].T, x_grid.T, dim
+    Example:
+    example_nr = 1
+    n_pts = 2000
+    noise = 2
+    X, T, x_grid, dim = make_data(example_nr, n_pts, noise)
+
+    fig, ax = plt.subplots()
+    ax.plot(X[0,T[0]], X[1,T[0]], '.r', alpha=0.3)
+    ax.plot(X[0,T[1]], X[1,T[1]], '.g', alpha=0.3)
+    ax.set_xlim(0, 100)
+    ax.set_ylim(0, 100)
+    ax.set_box_aspect(1)
+
+    Authors: Vedrana Andersen Dahl and Anders Bjorholm Dahl - 20/3-2024
+    vand@dtu.dk, abda@dtu.dk
 
+    '''
 
-#%% Test of the data generation
-if __name__ == "__main__":
-    #%%
+    in_dir = 'cases/'
+    file_names = sorted(os.listdir(in_dir))
+    file_names = [f for f in file_names if f.endswith('.png')]
 
-    import matplotlib.pyplot as plt
-    
-    n = 1000
-    noise = 1
-    
-    fig, ax = plt.subplots(1, 3)
-    for i, a in enumerate(ax):
-        example_nr = i + 1
-        X, T, x_grid, dim = make_data(example_nr, n, noise)
-        a.scatter(X[0][T[0]], X[1][T[0]], c='r', alpha=0.3, s=15)
-        a.scatter(X[0][T[1]], X[1][T[1]], c='g', alpha=0.3, s=15)
-        a.set_aspect('equal', 'box')
-        a.set_title(f'Example {i} data')
-    
-    plt.show()
-    
+    im = skimage.io.imread(in_dir + file_names[example_nr-1])
     
-    #%% Before training, you should make data zero mean
-    
-    c = np.mean(X, axis=1, keepdims=True)
-    X_c = X - c
-    
-    fig, ax = plt.subplots(1,1)
-    ax.scatter(X_c[0][T[0]], X_c[1][T[0]], c='r', alpha=0.3, s=15)
-    ax.scatter(X_c[0][T[1]], X_c[1][T[1]], c='g', alpha=0.3, s=15)
-    ax.set_aspect('equal', 'box')
-    plt.title('Zero-mean data')
-    plt.show()
-
-
+    [r_white, c_white] = np.where(im == 255)
+    [r_gray, c_gray] = np.where(im == 127)
+    n_white = np.minimum(r_white.shape[0], n_pts)
+    n_gray = np.minimum(r_gray.shape[0], n_pts)
 
+    rid_white = np.random.permutation(r_white.shape[0])
+    rid_gray = np.random.permutation(r_gray.shape[0])
+    pts_white = np.array([c_white[rid_white[:n_white]], r_white[rid_white[:n_white]]])
+    pts_gray = np.array([c_gray[rid_gray[:n_gray]], r_gray[rid_gray[:n_gray]]])
 
+    X = np.hstack((pts_white, pts_gray))/5 + np.random.randn(2, n_white+n_gray)*noise
+    T = np.zeros((2, n_white+n_gray), dtype=bool)
+    T[0,:n_white] = True
+    T[1,n_white:] = True
 
+    dim = (100, 100)
+    QX, QY = np.meshgrid(range(0, dim[0]), range(0, dim[1]))
+    x_grid = np.vstack((np.ravel(QX), np.ravel(QY)))
 
+    return X, T, x_grid, dim
 
+if __name__ == "__main__":
+    example_nr = 1
+    n_pts = 2000
+    noise = 3
+    X, T, x_grid, dim = make_data(example_nr, n_pts, noise)
+
+    fig, ax = plt.subplots()
+    ax.plot(X[0,T[0]], X[1,T[0]], '.r', alpha=0.3)
+    ax.plot(X[0,T[1]], X[1,T[1]], '.g', alpha=0.3)
+    ax.set_xlim(0, 100)
+    ax.set_ylim(0, 100)
+    ax.set_box_aspect(1)
 
-# %%
diff --git a/Chapter08/make_data_simple.py b/Chapter08/make_data_simple.py
new file mode 100755
index 0000000000000000000000000000000000000000..1a4419cb7ecf00a718f0e66757ed49fa51828204
--- /dev/null
+++ b/Chapter08/make_data_simple.py
@@ -0,0 +1,107 @@
+#%%
+
+import numpy as np
+
+
+def make_data(example_nr, n = 200, noise = 1):
+    '''
+    Generate data for training a simple neural network.
+    
+        Arguments:
+            example_nr: a number 1 to 3 for each example.
+            n: number of points in each class set.
+            noise: noise level, best between 0.5 and 2.
+        Returns:
+            X: 2 x 2n array of points (there are n points in each class).
+            T: 2 x 2n target values.
+            x: grid points for testing the neural network.
+            dim: size of the area covered by the grid points.
+
+        Authors: Vedrana Andersen Dahl and Anders Bjorholm Dahl - 25/3-2020
+        vand@dtu.dk, abda@dtu.dk
+    '''
+
+    rg = np.random.default_rng()
+    
+    dim = (100, 100)
+    
+    QX, QY = np.meshgrid(range(0, dim[0]), range(0, dim[1]))
+    x_grid = np.c_[np.ravel(QX), np.ravel(QY)]
+    
+    #  Targets: first half class 0, second half class 1
+    T = np.vstack((np.tile([True, False], (n, 1)), 
+                   np.tile([False, True], (n, 1))))
+    
+    if example_nr == 1 :  # two separated clusters
+
+        X = np.vstack((np.tile([30., 30.], (n, 1)), 
+                       np.tile([70., 70.], (n, 1))))
+        X += rg.normal(size=X.shape, scale=10*noise)  # add noise
+
+    elif example_nr == 2 :  # concentric clusters
+
+        rand_ang = 2 * np.pi * rg.uniform(size=n)
+        X = np.vstack((30 * np.array([np.cos(rand_ang), np.sin(rand_ang)]).T, 
+                       np.tile([0., 0.], (n, 1))))
+        X += [50, 50]  # center
+        X += rg.normal(size=X.shape, scale=5*noise)# add noise
+    
+    elif example_nr == 3 :  # 2x2 checkerboard 
+        n1 = n//2
+        n2 = n//2 + n%2  # if n is odd n2 will have 1 element more
+
+        X = np.vstack((np.tile([30., 30.], (n1, 1)), 
+                       np.tile([70., 70.], (n2, 1)),
+                       np.tile([30. ,70.], (n1, 1)),
+                       np.tile([70., 30.], (n2, 1))))
+        X += rg.normal(size=X.shape, scale=10*noise)  # add noise
+
+    else:
+        print('No data returned - example_nr must be 1, 2, or 3')
+    
+    o = rg.permutation(range(2*n))
+    
+    return X[o].T, T[o].T, x_grid.T, dim
+
+
+#%% Test of the data generation
+if __name__ == "__main__":
+    #%%
+
+    import matplotlib.pyplot as plt
+    
+    n = 1000
+    noise = 1
+    
+    fig, ax = plt.subplots(1, 3)
+    for i, a in enumerate(ax):
+        example_nr = i + 1
+        X, T, x_grid, dim = make_data(example_nr, n, noise)
+        a.scatter(X[0][T[0]], X[1][T[0]], c='r', alpha=0.3, s=15)
+        a.scatter(X[0][T[1]], X[1][T[1]], c='g', alpha=0.3, s=15)
+        a.set_aspect('equal', 'box')
+        a.set_title(f'Example {i} data')
+    
+    plt.show()
+    
+    
+    #%% Before training, you should make data zero mean
+    
+    c = np.mean(X, axis=1, keepdims=True)
+    X_c = X - c
+    
+    fig, ax = plt.subplots(1,1)
+    ax.scatter(X_c[0][T[0]], X_c[1][T[0]], c='r', alpha=0.3, s=15)
+    ax.scatter(X_c[0][T[1]], X_c[1][T[1]], c='g', alpha=0.3, s=15)
+    ax.set_aspect('equal', 'box')
+    plt.title('Zero-mean data')
+    plt.show()
+
+
+
+
+
+
+
+
+# %%