diff --git a/scikits/image/transform/_project.pyx b/scikits/image/transform/_project.pyx new file mode 100644 index 00000000..5cbbb608 --- /dev/null +++ b/scikits/image/transform/_project.pyx @@ -0,0 +1,150 @@ +#cython: cdivison=True boundscheck=False + +cimport cython +cimport numpy as np + +import numpy as np +import cython + +np.import_array() + +cdef extern from "math.h": + double floor(double) + double fmod(double, double) + +cdef double get_pixel(np.ndarray image, int r, int c, char mode, + double cval=0): + cdef np.ndarray[dtype=np.double_t, ndim=2] img = image + cdef int rows = img.shape[0] + cdef int cols = img.shape[1] + + if mode == 'C': + if (r < 0) or (r >= cols) or (c < 0) or (c >= cols): + return cval + else: + return img[r, c] + else: + return img[coord_map(rows, r, mode), + coord_map(cols, c, mode)] + +cdef int coord_map(int dim, int coord, char mode): + dim = dim - 1 + if mode == 'M': # mirror + if (coord < 0): + return (-coord % dim) + elif (coord > dim): + return (dim - (coord % dim)) + elif mode == 'W': # wrap + if (coord < 0): + return (dim - (-coord % dim)) + elif (coord > dim): + return (coord % dim) + + return coord + +cdef tf(double x, double y, H): + cdef np.ndarray[np.double_t, ndim=2] M = H + cdef double xx, yy, zz + + xx = M[0, 0] * x + M[0, 1] * y + M[0, 2] + yy = M[1, 0] * x + M[1, 1] * y + M[1, 2] + zz = M[2, 0] * x + M[2, 1] * y + M[2, 2] + + xx /= zz + yy /= zz + + return xx, yy + +@cython.boundscheck(False) +def homography(np.ndarray image, np.ndarray H, output_shape=None, + mode='C', double cval=0): + """ + Projective transformation (homography). + + Perform a projective transformation (homography) of a + floating point image, using bi-linear interpolation. + + For each pixel, given its homogeneous coordinate :math:`\mathbf{x} + = [x, y, 1]^T`, its target position is calculated by multiplying + with the given matrix, :math:`H`, to give :math:`H \mathbf{x}`. + E.g., to rotate by theta degrees clockwise, the matrix should be + + :: + + [[cos(theta) -sin(theta) 0] + [sin(theta) cos(theta) 0] + [0 0 1]] + + or, to translate x by 10 and y by 20, + + :: + + [[1 0 10] + [0 1 20] + [0 0 1 ]]. + + Parameters + ---------- + image : 2-D array + Input image. + H : array of shape ``(3, 3)`` + Transformation matrix H that defines the homography. + output_shape : tuple (rows, cols) + Shape of the output image generated. + order : int + Order of splines used in interpolation. + mode : {'C', 'M', 'W'} + How to handle values outside the image borders. + Constant, Mirror or Wrap. + cval : string + Used in conjunction with mode 'C' (constant), the value + outside the image boundaries. + + """ + + cdef np.ndarray[dtype=np.double_t, ndim=2] img = image + cdef np.ndarray[dtype=np.double_t, ndim=2] M = np.linalg.inv(H) + + if mode not in ('C', 'W', 'M'): + raise ValueError("Invalid mode specified. Please use " + "C [constant], W [wrap] or M [mirror].") + cdef char mode_c = ord(mode[0]) + + cdef int out_r, out_c, columns, rows + if output_shape is None: + out_r = img.shape[0] + out_c = img.shape[1] + else: + out_r = output_shape[0] + out_c = output_shape[1] + + rows = img.shape[0] + columns = img.shape[1] + + cdef np.ndarray[dtype=np.double_t, ndim=2] out = \ + np.zeros((out_r, out_c), dtype=np.float64) + + cdef int tfr, tfc, r_int, c_int + cdef double y0, y1, y2, y3 + cdef double r, c, z, t, u + + for tfr in range(out_r): + for tfc in range(out_c): + c, r = tf(tfc, tfr, M) + r_int = floor(r) + c_int = floor(c) + + t = r - r_int + u = c - c_int + + y0 = get_pixel(img, r_int, c_int, mode_c) + y1 = get_pixel(img, r_int + 1, c_int, mode_c) + y2 = get_pixel(img, r_int + 1, c_int + 1, mode_c) + y3 = get_pixel(img, r_int, c_int + 1, mode_c) + + out[tfr, tfc] = \ + (1 - t) * (1 - u) * y0 + \ + t * (1 - u) * y1 + \ + t * u * y2 + (1 - t) * u * y3; + + return out diff --git a/scikits/image/transform/setup.py b/scikits/image/transform/setup.py index f15dd08c..c5629eb0 100644 --- a/scikits/image/transform/setup.py +++ b/scikits/image/transform/setup.py @@ -15,10 +15,14 @@ def configuration(parent_package='', top_path=None): config.add_data_dir('tests') cython(['_hough_transform.pyx'], working_path=base_path) + cython(['_project.pyx'], working_path=base_path) config.add_extension('_hough_transform', sources=['_hough_transform.c'], include_dirs=[get_numpy_include_dirs()]) + config.add_extension('_project', sources=['_project.c'], + include_dirs=[get_numpy_include_dirs()]) + return config if __name__ == '__main__':