Commit c6a22019532ade5b966532f4ad235d7c816d09bc

Authored by Thiago Franco de Moraes
Committed by GitHub
1 parent f4c771e9
Exists in master

Lanczos 3d (#108)

* Testing with lanczos 3D

* Using lanczos to interpolate after applying a reorientation

* More options and pointer to function

* lanczos a=4
invesalius/data/interpolation.pxd
@@ -3,3 +3,6 @@ from .cy_my_types cimport image_t @@ -3,3 +3,6 @@ from .cy_my_types cimport image_t
3 cdef double interpolate(image_t[:, :, :], double, double, double) nogil 3 cdef double interpolate(image_t[:, :, :], double, double, double) nogil
4 cdef double tricub_interpolate(image_t[:, :, :], double, double, double) nogil 4 cdef double tricub_interpolate(image_t[:, :, :], double, double, double) nogil
5 cdef double tricubicInterpolate (image_t[:, :, :], double, double, double) nogil 5 cdef double tricubicInterpolate (image_t[:, :, :], double, double, double) nogil
  6 +cdef double lanczos3 (image_t[:, :, :], double, double, double) nogil
  7 +
  8 +cdef double nearest_neighbour_interp(image_t[:, :, :], double, double, double) nogil
invesalius/data/interpolation.pyx
@@ -4,9 +4,12 @@ import numpy as np @@ -4,9 +4,12 @@ import numpy as np
4 cimport numpy as np 4 cimport numpy as np
5 cimport cython 5 cimport cython
6 6
7 -from libc.math cimport floor, ceil, sqrt, fabs, round 7 +from libc.math cimport floor, ceil, sqrt, fabs, round, sin, M_PI
8 from cython.parallel import prange 8 from cython.parallel import prange
9 9
  10 +DEF LANCZOS_A = 4
  11 +DEF SIZE_LANCZOS_TMP = LANCZOS_A * 2 - 1
  12 +
10 cdef double[64][64] temp = [ 13 cdef double[64][64] temp = [
11 [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 14 [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
12 [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 15 [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
@@ -77,6 +80,12 @@ cdef double[64][64] temp = [ @@ -77,6 +80,12 @@ cdef double[64][64] temp = [
77 @cython.boundscheck(False) # turn of bounds-checking for entire function 80 @cython.boundscheck(False) # turn of bounds-checking for entire function
78 @cython.cdivision(True) 81 @cython.cdivision(True)
79 @cython.wraparound(False) 82 @cython.wraparound(False)
  83 +cdef double nearest_neighbour_interp(image_t[:, :, :] V, double x, double y, double z) nogil:
  84 + return V[<int>round(z), <int>round(y), <int>round(x)]
  85 +
  86 +@cython.boundscheck(False) # turn of bounds-checking for entire function
  87 +@cython.cdivision(True)
  88 +@cython.wraparound(False)
80 cdef double interpolate(image_t[:, :, :] V, double x, double y, double z) nogil: 89 cdef double interpolate(image_t[:, :, :] V, double x, double y, double z) nogil:
81 cdef double xd, yd, zd 90 cdef double xd, yd, zd
82 cdef double c00, c10, c01, c11 91 cdef double c00, c10, c01, c11
@@ -123,6 +132,76 @@ cdef double interpolate(image_t[:, :, :] V, double x, double y, double z) nogil: @@ -123,6 +132,76 @@ cdef double interpolate(image_t[:, :, :] V, double x, double y, double z) nogil:
123 @cython.boundscheck(False) # turn of bounds-checking for entire function 132 @cython.boundscheck(False) # turn of bounds-checking for entire function
124 @cython.cdivision(True) 133 @cython.cdivision(True)
125 @cython.wraparound(False) 134 @cython.wraparound(False)
  135 +cdef inline double lanczos3_L(double x, int a) nogil:
  136 + if x == 0:
  137 + return 1.0
  138 + elif -a <= x < a:
  139 + return (a * sin(M_PI * x) * sin(M_PI * (x / a)))/(M_PI**2 * x**2)
  140 + else:
  141 + return 0.0
  142 +
  143 +
  144 +@cython.boundscheck(False) # turn of bounds-checking for entire function
  145 +@cython.cdivision(True)
  146 +@cython.wraparound(False)
  147 +cdef double lanczos3(image_t[:, :, :] V, double x, double y, double z) nogil:
  148 + cdef int a = LANCZOS_A
  149 +
  150 + cdef int xd = <int>floor(x)
  151 + cdef int yd = <int>floor(y)
  152 + cdef int zd = <int>floor(z)
  153 +
  154 + cdef int xi = xd - a + 1
  155 + cdef int xf = xd + a
  156 +
  157 + cdef int yi = yd - a + 1
  158 + cdef int yf = yd + a
  159 +
  160 + cdef int zi = zd - a + 1
  161 + cdef int zf = zd + a
  162 +
  163 + cdef double lx = 0.0
  164 + cdef double ly = 0.0
  165 + cdef double lz = 0.0
  166 +
  167 + cdef double[SIZE_LANCZOS_TMP][SIZE_LANCZOS_TMP] temp_x
  168 + cdef double[SIZE_LANCZOS_TMP] temp_y
  169 +
  170 + cdef int i, j, k
  171 + cdef int m, n, o
  172 +
  173 + m = 0
  174 + for k in xrange(zi, zf):
  175 + n = 0
  176 + for j in xrange(yi, yf):
  177 + lx = 0
  178 + for i in xrange(xi, xf):
  179 + lx += _G(V, i, j, k) * lanczos3_L(x - i, a)
  180 + temp_x[m][n] = lx
  181 + n += 1
  182 + m += 1
  183 +
  184 + m = 0
  185 + for k in xrange(zi, zf):
  186 + n = 0
  187 + ly = 0
  188 + for j in xrange(yi, yf):
  189 + ly += temp_x[m][n] * lanczos3_L(y - j, a)
  190 + n += 1
  191 + temp_y[m] = ly
  192 + m += 1
  193 +
  194 + m = 0
  195 + for k in xrange(zi, zf):
  196 + lz += temp_y[m] * lanczos3_L(z - k, a)
  197 + m += 1
  198 +
  199 + return lz
  200 +
  201 +
  202 +@cython.boundscheck(False) # turn of bounds-checking for entire function
  203 +@cython.cdivision(True)
  204 +@cython.wraparound(False)
126 cdef image_t _G(image_t[:, :, :] V, int x, int y, int z) nogil: 205 cdef image_t _G(image_t[:, :, :] V, int x, int y, int z) nogil:
127 cdef int dz, dy, dx 206 cdef int dz, dy, dx
128 dz = V.shape[0] - 1 207 dz = V.shape[0] - 1
@@ -311,4 +390,3 @@ def tricub_interpolate2_py(image_t[:, :, :] V, double x, double y, double z): @@ -311,4 +390,3 @@ def tricub_interpolate2_py(image_t[:, :, :] V, double x, double y, double z):
311 390
312 def trilin_interpolate_py(image_t[:, :, :] V, double x, double y, double z): 391 def trilin_interpolate_py(image_t[:, :, :] V, double x, double y, double z):
313 return interpolate(V, x, y, z) 392 return interpolate(V, x, y, z)
314 -  
invesalius/data/slice_.py
@@ -91,6 +91,8 @@ class Slice(object): @@ -91,6 +91,8 @@ class Slice(object):
91 self._type_projection = const.PROJECTION_NORMAL 91 self._type_projection = const.PROJECTION_NORMAL
92 self.n_border = const.PROJECTION_BORDER_SIZE 92 self.n_border = const.PROJECTION_BORDER_SIZE
93 93
  94 + self.interp_method = 2
  95 +
94 self._spacing = (1.0, 1.0, 1.0) 96 self._spacing = (1.0, 1.0, 1.0)
95 self.center = [0, 0, 0] 97 self.center = [0, 0, 0]
96 98
@@ -198,6 +200,8 @@ class Slice(object): @@ -198,6 +200,8 @@ class Slice(object):
198 200
199 Publisher.subscribe(self._fill_holes_auto, 'Fill holes automatically') 201 Publisher.subscribe(self._fill_holes_auto, 'Fill holes automatically')
200 202
  203 + Publisher.subscribe(self._set_interpolation_method, 'Set interpolation method')
  204 +
201 def GetMaxSliceNumber(self, orientation): 205 def GetMaxSliceNumber(self, orientation):
202 shape = self.matrix.shape 206 shape = self.matrix.shape
203 207
@@ -624,7 +628,8 @@ class Slice(object): @@ -624,7 +628,8 @@ class Slice(object):
624 if orientation == 'AXIAL': 628 if orientation == 'AXIAL':
625 tmp_array = np.array(self.matrix[slice_number:slice_number + number_slices]) 629 tmp_array = np.array(self.matrix[slice_number:slice_number + number_slices])
626 if np.any(self.q_orientation[1::]): 630 if np.any(self.q_orientation[1::]):
627 - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, 2, self.matrix.min(), tmp_array) 631 + transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array)
  632 + print ">>>", tmp_array.min(), tmp_array.max()
628 if self._type_projection == const.PROJECTION_NORMAL: 633 if self._type_projection == const.PROJECTION_NORMAL:
629 n_image = tmp_array.squeeze() 634 n_image = tmp_array.squeeze()
630 else: 635 else:
@@ -671,7 +676,7 @@ class Slice(object): @@ -671,7 +676,7 @@ class Slice(object):
671 elif orientation == 'CORONAL': 676 elif orientation == 'CORONAL':
672 tmp_array = np.array(self.matrix[:, slice_number: slice_number + number_slices, :]) 677 tmp_array = np.array(self.matrix[:, slice_number: slice_number + number_slices, :])
673 if np.any(self.q_orientation[1::]): 678 if np.any(self.q_orientation[1::]):
674 - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, 2, self.matrix.min(), tmp_array) 679 + transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array)
675 680
676 if self._type_projection == const.PROJECTION_NORMAL: 681 if self._type_projection == const.PROJECTION_NORMAL:
677 n_image = tmp_array.squeeze() 682 n_image = tmp_array.squeeze()
@@ -721,7 +726,7 @@ class Slice(object): @@ -721,7 +726,7 @@ class Slice(object):
721 elif orientation == 'SAGITAL': 726 elif orientation == 'SAGITAL':
722 tmp_array = np.array(self.matrix[:, :, slice_number: slice_number + number_slices]) 727 tmp_array = np.array(self.matrix[:, :, slice_number: slice_number + number_slices])
723 if np.any(self.q_orientation[1::]): 728 if np.any(self.q_orientation[1::]):
724 - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, 2, self.matrix.min(), tmp_array) 729 + transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array)
725 730
726 if self._type_projection == const.PROJECTION_NORMAL: 731 if self._type_projection == const.PROJECTION_NORMAL:
727 n_image = tmp_array.squeeze() 732 n_image = tmp_array.squeeze()
@@ -953,6 +958,10 @@ class Slice(object): @@ -953,6 +958,10 @@ class Slice(object):
953 tprojection = pubsub_evt.data 958 tprojection = pubsub_evt.data
954 self.SetTypeProjection(tprojection) 959 self.SetTypeProjection(tprojection)
955 960
  961 + def _set_interpolation_method(self, pubsub_evt):
  962 + interp_method = pubsub_evt.data
  963 + self.SetInterpolationMethod(interp_method)
  964 +
956 def SetTypeProjection(self, tprojection): 965 def SetTypeProjection(self, tprojection):
957 if self._type_projection != tprojection: 966 if self._type_projection != tprojection:
958 if self._type_projection == const.PROJECTION_NORMAL: 967 if self._type_projection == const.PROJECTION_NORMAL:
@@ -969,6 +978,13 @@ class Slice(object): @@ -969,6 +978,13 @@ class Slice(object):
969 978
970 Publisher.sendMessage('Check projection menu', tprojection) 979 Publisher.sendMessage('Check projection menu', tprojection)
971 980
  981 + def SetInterpolationMethod(self, interp_method):
  982 + if self.interp_method != interp_method:
  983 + self.interp_method = interp_method
  984 + for buffer_ in self.buffer_slices.values():
  985 + buffer_.discard_buffer()
  986 + Publisher.sendMessage('Reload actual slice')
  987 +
972 def UpdateWindowLevelBackground(self, pubsub_evt): 988 def UpdateWindowLevelBackground(self, pubsub_evt):
973 window, level = pubsub_evt.data 989 window, level = pubsub_evt.data
974 self.window_width = window 990 self.window_width = window
@@ -1423,7 +1439,7 @@ class Slice(object): @@ -1423,7 +1439,7 @@ class Slice(object):
1423 T1 = transformations.translation_matrix((cz, cy, cx)) 1439 T1 = transformations.translation_matrix((cz, cy, cx))
1424 M = transformations.concatenate_matrices(T1, R.T, T0) 1440 M = transformations.concatenate_matrices(T1, R.T, T0)
1425 1441
1426 - transforms.apply_view_matrix_transform(mcopy, self.spacing, M, 0, 'AXIAL', 2, mcopy.min(), self.matrix) 1442 + transforms.apply_view_matrix_transform(mcopy, self.spacing, M, 0, 'AXIAL', self.interp_method, mcopy.min(), self.matrix)
1427 1443
1428 del mcopy 1444 del mcopy
1429 os.remove(temp_file) 1445 os.remove(temp_file)
invesalius/data/transforms.pyx
@@ -3,11 +3,12 @@ cimport numpy as np @@ -3,11 +3,12 @@ cimport numpy as np
3 cimport cython 3 cimport cython
4 4
5 from .cy_my_types cimport image_t 5 from .cy_my_types cimport image_t
6 -from .interpolation cimport interpolate, tricub_interpolate, tricubicInterpolate 6 +from .interpolation cimport interpolate, tricub_interpolate, tricubicInterpolate, lanczos3, nearest_neighbour_interp
7 7
8 from libc.math cimport floor, ceil, sqrt, fabs, round 8 from libc.math cimport floor, ceil, sqrt, fabs, round
9 from cython.parallel import prange 9 from cython.parallel import prange
10 10
  11 +ctypedef double (*interp_function)(image_t[:, :, :], double, double, double) nogil
11 12
12 @cython.boundscheck(False) # turn of bounds-checking for entire function 13 @cython.boundscheck(False) # turn of bounds-checking for entire function
13 @cython.cdivision(True) 14 @cython.cdivision(True)
@@ -25,7 +26,7 @@ cdef void mul_mat4_vec4(double[:, :] M, @@ -25,7 +26,7 @@ cdef void mul_mat4_vec4(double[:, :] M,
25 @cython.boundscheck(False) # turn of bounds-checking for entire function 26 @cython.boundscheck(False) # turn of bounds-checking for entire function
26 @cython.cdivision(True) 27 @cython.cdivision(True)
27 @cython.wraparound(False) 28 @cython.wraparound(False)
28 -cdef image_t coord_transform(image_t[:, :, :] volume, double[:, :] M, int x, int y, int z, double sx, double sy, double sz, short minterpol, image_t cval) nogil: 29 +cdef image_t coord_transform(image_t[:, :, :] volume, double[:, :] M, int x, int y, int z, double sx, double sy, double sz, interp_function f_interp, image_t cval) nogil:
29 30
30 cdef double coord[4] 31 cdef double coord[4]
31 coord[0] = z*sz 32 coord[0] = z*sz
@@ -53,15 +54,21 @@ cdef image_t coord_transform(image_t[:, :, :] volume, double[:, :] M, int x, int @@ -53,15 +54,21 @@ cdef image_t coord_transform(image_t[:, :, :] volume, double[:, :] M, int x, int
53 cdef double v 54 cdef double v
54 55
55 if 0 <= nz <= (dz-1) and 0 <= ny <= (dy-1) and 0 <= nx <= (dx-1): 56 if 0 <= nz <= (dz-1) and 0 <= ny <= (dy-1) and 0 <= nx <= (dx-1):
56 - if minterpol == 0:  
57 - return volume[<int>round(nz), <int>round(ny), <int>round(nx)]  
58 - elif minterpol == 1:  
59 - return <image_t>interpolate(volume, nx, ny, nz)  
60 - else:  
61 - v = tricubicInterpolate(volume, nx, ny, nz)  
62 - if (v < cval):  
63 - v = cval  
64 - return <image_t>v 57 + return <image_t>f_interp(volume, nx, ny, nz)
  58 + # if minterpol == 0:
  59 + # return <image_t>nearest_neighbour_interp(volume, nx, ny, nz)
  60 + # elif minterpol == 1:
  61 + # return <image_t>interpolate(volume, nx, ny, nz)
  62 + # elif minterpol == 2:
  63 + # v = tricubicInterpolate(volume, nx, ny, nz)
  64 + # if (v < cval):
  65 + # v = cval
  66 + # return <image_t>v
  67 + # else:
  68 + # v = lanczos3(volume, nx, ny, nz)
  69 + # if (v < cval):
  70 + # v = cval
  71 + # return <image_t>v
65 else: 72 else:
66 return cval 73 return cval
67 74
@@ -95,23 +102,34 @@ def apply_view_matrix_transform(image_t[:, :, :] volume, @@ -95,23 +102,34 @@ def apply_view_matrix_transform(image_t[:, :, :] volume,
95 sy = spacing[1] 102 sy = spacing[1]
96 sz = spacing[2] 103 sz = spacing[2]
97 104
  105 + cdef interp_function f_interp
  106 +
  107 + if minterpol == 0:
  108 + f_interp = nearest_neighbour_interp
  109 + elif minterpol == 1:
  110 + f_interp = interpolate
  111 + elif minterpol == 2:
  112 + f_interp = tricubicInterpolate
  113 + else:
  114 + f_interp = lanczos3
  115 +
98 if orientation == 'AXIAL': 116 if orientation == 'AXIAL':
99 for z in xrange(n, n+odz): 117 for z in xrange(n, n+odz):
100 for y in prange(dy, nogil=True): 118 for y in prange(dy, nogil=True):
101 for x in xrange(dx): 119 for x in xrange(dx):
102 - out[count, y, x] = coord_transform(volume, M, x, y, z, sx, sy, sz, minterpol, cval) 120 + out[count, y, x] = coord_transform(volume, M, x, y, z, sx, sy, sz, f_interp, cval)
103 count += 1 121 count += 1
104 122
105 elif orientation == 'CORONAL': 123 elif orientation == 'CORONAL':
106 for y in xrange(n, n+ody): 124 for y in xrange(n, n+ody):
107 for z in prange(dz, nogil=True): 125 for z in prange(dz, nogil=True):
108 for x in xrange(dx): 126 for x in xrange(dx):
109 - out[z, count, x] = coord_transform(volume, M, x, y, z, sx, sy, sz, minterpol, cval) 127 + out[z, count, x] = coord_transform(volume, M, x, y, z, sx, sy, sz, f_interp, cval)
110 count += 1 128 count += 1
111 129
112 elif orientation == 'SAGITAL': 130 elif orientation == 'SAGITAL':
113 for x in xrange(n, n+odx): 131 for x in xrange(n, n+odx):
114 for z in prange(dz, nogil=True): 132 for z in prange(dz, nogil=True):
115 for y in xrange(dy): 133 for y in xrange(dy):
116 - out[z, y, count] = coord_transform(volume, M, x, y, z, sx, sy, sz, minterpol,cval) 134 + out[z, y, count] = coord_transform(volume, M, x, y, z, sx, sy, sz, f_interp, cval)
117 count += 1 135 count += 1
invesalius/gui/dialogs.py
@@ -1883,26 +1883,38 @@ class ReorientImageDialog(wx.Dialog): @@ -1883,26 +1883,38 @@ class ReorientImageDialog(wx.Dialog):
1883 self._bind_events_wx() 1883 self._bind_events_wx()
1884 1884
1885 def _init_gui(self): 1885 def _init_gui(self):
  1886 + interp_methods_choices = ((_("Nearest Neighbour"), 0),
  1887 + (_("Trilinear"), 1),
  1888 + (_("Tricubic"), 2),
  1889 + (_("Lanczos"), 3))
  1890 + self.interp_method = wx.ComboBox(self, -1, choices=[], style=wx.CB_READONLY)
  1891 + for txt, im_code in interp_methods_choices:
  1892 + self.interp_method.Append(txt, im_code)
  1893 + self.interp_method.SetValue(interp_methods_choices[2][0])
  1894 +
1886 self.anglex = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY) 1895 self.anglex = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY)
1887 self.angley = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY) 1896 self.angley = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY)
1888 self.anglez = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY) 1897 self.anglez = wx.TextCtrl(self, -1, "0.0", style=wx.TE_READONLY)
1889 1898
1890 self.btnapply = wx.Button(self, -1, _("Apply")) 1899 self.btnapply = wx.Button(self, -1, _("Apply"))
1891 1900
1892 - sizer = wx.BoxSizer(wx.HORIZONTAL) 1901 + sizer = wx.BoxSizer(wx.VERTICAL)
1893 1902
1894 - sizer.Add(wx.StaticText(self, -1, _("Angle X")), 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)  
1895 - sizer.Add(self.anglex, 0, wx.EXPAND | wx.ALL, 5)  
1896 - sizer.AddSpacer(5) 1903 + angles_sizer = wx.FlexGridSizer(3, 2, 5, 5)
  1904 + angles_sizer.AddMany([
  1905 + (wx.StaticText(self, -1, _("Angle X")), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5),
  1906 + (self.anglex, 0, wx.EXPAND | wx.ALL, 5),
1897 1907
1898 - sizer.Add(wx.StaticText(self, -1, _("Angle Y")), 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)  
1899 - sizer.Add(self.angley, 0, wx.EXPAND | wx.ALL, 5)  
1900 - sizer.AddSpacer(5) 1908 + (wx.StaticText(self, -1, _("Angle Y")), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5),
  1909 + (self.angley, 0, wx.EXPAND | wx.ALL, 5),
1901 1910
1902 - sizer.Add(wx.StaticText(self, -1, _("Angle Z")), 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)  
1903 - sizer.Add(self.anglez, 0, wx.EXPAND | wx.ALL, 5)  
1904 - sizer.AddSpacer(5) 1911 + (wx.StaticText(self, -1, _("Angle Z")), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5),
  1912 + (self.anglez, 0, wx.EXPAND | wx.ALL, 5),
  1913 + ])
1905 1914
  1915 + sizer.Add(wx.StaticText(self, -1, _("Interpolation method:")), 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 5)
  1916 + sizer.Add(self.interp_method, 0, wx.EXPAND | wx.ALL, 5)
  1917 + sizer.Add(angles_sizer, 0, wx.EXPAND | wx.ALL, 5)
1906 sizer.Add(self.btnapply, 0, wx.EXPAND | wx.ALL, 5) 1918 sizer.Add(self.btnapply, 0, wx.EXPAND | wx.ALL, 5)
1907 sizer.AddSpacer(5) 1919 sizer.AddSpacer(5)
1908 1920
@@ -1914,6 +1926,7 @@ class ReorientImageDialog(wx.Dialog): @@ -1914,6 +1926,7 @@ class ReorientImageDialog(wx.Dialog):
1914 Publisher.subscribe(self._close_dialog, 'Close reorient dialog') 1926 Publisher.subscribe(self._close_dialog, 'Close reorient dialog')
1915 1927
1916 def _bind_events_wx(self): 1928 def _bind_events_wx(self):
  1929 + self.interp_method.Bind(wx.EVT_COMBOBOX, self.OnSelect)
1917 self.btnapply.Bind(wx.EVT_BUTTON, self.apply_reorientation) 1930 self.btnapply.Bind(wx.EVT_BUTTON, self.apply_reorientation)
1918 self.Bind(wx.EVT_CLOSE, self.OnClose) 1931 self.Bind(wx.EVT_CLOSE, self.OnClose)
1919 1932
@@ -1935,6 +1948,9 @@ class ReorientImageDialog(wx.Dialog): @@ -1935,6 +1948,9 @@ class ReorientImageDialog(wx.Dialog):
1935 Publisher.sendMessage('Enable style', const.STATE_DEFAULT) 1948 Publisher.sendMessage('Enable style', const.STATE_DEFAULT)
1936 self.Destroy() 1949 self.Destroy()
1937 1950
  1951 + def OnSelect(self, evt):
  1952 + im_code = self.interp_method.GetClientData(self.interp_method.GetSelection())
  1953 + Publisher.sendMessage('Set interpolation method', im_code)
1938 1954
1939 1955
1940 class ImportBitmapParameters(wx.Dialog): 1956 class ImportBitmapParameters(wx.Dialog):