|
75 | 75 | # %% |
76 | 76 | # 5. Refine Spline |
77 | 77 | # ---------------- |
| 78 | +# We refine the spline by minimising an energy function that combines the image |
| 79 | +# energy (the mean pixel value under the spline) and an internal energy that |
| 80 | +# penalises curvy splines. Instead of using a derivative-free optimiser, we |
| 81 | +# supply the analytic derivatives of the energy with respect to the control points, |
| 82 | +# which splinebox computes with ``derivative_wrt_control_points`` and |
| 83 | +# ``derivative_of_norm_squared_wrt_control_points``. |
| 84 | +# The image energy term also requires the spatial gradients of the image. |
| 85 | +# We precompute them on a lightly Gaussian smoothed version of the image to |
| 86 | +# reduce the effect of noise. Note that we interpolate the pixel values on a |
| 87 | +# float version of the image because ``map_coordinates`` truncates interpolated |
| 88 | +# values to integers for integer input images. |
| 89 | +SIGMA = 1 |
| 90 | +img_gy = scipy.ndimage.gaussian_filter(img.astype(float), SIGMA, order=[1, 0], mode="nearest") |
| 91 | +img_gx = scipy.ndimage.gaussian_filter(img.astype(float), SIGMA, order=[0, 1], mode="nearest") |
78 | 92 |
|
79 | 93 |
|
80 | 94 | def loss_function(control_points, alpha): |
81 | 95 | spline.control_points = control_points.reshape((-1, 2)) |
82 | 96 | coords = spline(t) |
83 | | - pixel_values = scipy.ndimage.map_coordinates(img, coords.T) |
| 97 | + pixel_values = scipy.ndimage.map_coordinates(img.astype(float), coords.T) |
84 | 98 | image_energy = np.mean(pixel_values) |
85 | | - internal_energy = np.mean(spline(t, derivative=2) ** 2) |
| 99 | + internal_energy = np.mean(np.sum(spline(t, derivative=2) ** 2, axis=1)) |
86 | 100 | energy = -1 * image_energy + alpha * internal_energy |
87 | 101 | return energy |
88 | 102 |
|
89 | 103 |
|
| 104 | +def jacobian(control_points, alpha): |
| 105 | + spline.control_points = control_points.reshape((-1, 2)) |
| 106 | + coords = spline(t) |
| 107 | + dy = scipy.ndimage.map_coordinates(img_gy, coords.T, order=1) |
| 108 | + dx = scipy.ndimage.map_coordinates(img_gx, coords.T, order=1) |
| 109 | + img_gradients = np.stack([dy, dx], axis=-1) |
| 110 | + image_energy_gradients = spline.derivative_wrt_control_points(t).T @ img_gradients / len(t) |
| 111 | + internal_energy_gradients = np.mean(spline.derivative_of_norm_squared_wrt_control_points(t, derivative=2), axis=0) |
| 112 | + return (-image_energy_gradients + alpha * internal_energy_gradients).flatten() |
| 113 | + |
| 114 | + |
90 | 115 | initial_control_points = initial_spline.control_points |
91 | 116 | spline = initial_spline.copy() |
92 | | -scipy.optimize.minimize( |
| 117 | +result = scipy.optimize.minimize( |
93 | 118 | loss_function, |
94 | 119 | initial_control_points.flatten(), |
95 | | - args=(0.3,), |
96 | | - method="Powell", |
97 | | - tol=0.01, |
| 120 | + args=(0.04,), |
| 121 | + method="L-BFGS-B", |
| 122 | + jac=jacobian, |
98 | 123 | ) |
| 124 | +spline.control_points = result.x.reshape((-1, 2)) |
99 | 125 |
|
100 | 126 | vals = spline(t) |
101 | 127 | knots = spline.knots |
|
0 commit comments