In [1]:
# It seems that, for now, Azure Notebook does not support '.pyx' file compiling-and-importing.

In [ ]:
# OK! After reading the documention, I find how to use Cython in notebook: magic command %%cython

In [1]:
%load_ext Cython

Now, Try the convolve sample in Cython tutorial.


In [2]:
%%cython

import numpy as np
cimport numpy as np

DTYPE = np.int
ctypedef np.int_t DTYPE_t

# cimport cython
# @cython.boundscheck(False)
# @cython.wraparound(False)
def naive_con1(np.ndarray f, np.ndarray g):
    if g.shape[0] % 2 != 1 and f.shape[0] % 2 != 1:
        raise ValueError("Only odd dimensions on filter supported!")
    assert f.dtype == DTYPE and g.dtype == DTYPE
    
    cdef int vmax = f.shape[0]
    cdef int wmax = f.shape[1]
    cdef int smax = g.shape[0]
    cdef int tmax = g.shape[1]
    cdef int smid = smax // 2
    cdef int tmid = tmax // 2
    cdef int xmax = vmax + 2*smid
    cdef int ymax = wmax + 2*tmid
    
    cdef np.ndarray h = np.zeros([xmax, ymax], dtype=DTYPE)

    cdef int x, y, s, t, v, w
    cdef int s_from, s_to, t_from, t_to

    cdef DTYPE_t value

    # Do convolution
    for x in range(xmax):
        for y in range(ymax):
            # Calculate pixel value for h at (x,y). Sum one component
            # for each pixel (s, t) of the filter g.
            s_from = max(smid - x, -smid)
            s_to = min((xmax - x) - smid, smid + 1)
            t_from = max(tmid - y, -tmid)
            t_to = min((ymax - y) - tmid, tmid + 1)
            value = 0
            for s in range(s_from, s_to):
                for t in range(t_from, t_to):
                    v = x - smid + s
                    w = y - tmid + t
                    value += g[smid - s, tmid - t] * f[v, w]
            h[x, y] = value
    return h

In [3]:
%%cython

import numpy as np
cimport numpy as np

DTYPE = np.int
ctypedef np.int_t DTYPE_t

# cimport cython
# @cython.boundscheck(False)
# @cython.wraparound(False)
def naive_con2(np.ndarray[DTYPE_t, ndim=2] f, np.ndarray[DTYPE_t, ndim=2] g):
    if g.shape[0] % 2 != 1 and f.shape[0] % 2 != 1:
        raise ValueError("Only odd dimensions on filter supported!")
    assert f.dtype == DTYPE and g.dtype == DTYPE
    
    cdef int vmax = f.shape[0]
    cdef int wmax = f.shape[1]
    cdef int smax = g.shape[0]
    cdef int tmax = g.shape[1]
    cdef int smid = smax // 2
    cdef int tmid = tmax // 2
    cdef int xmax = vmax + 2*smid
    cdef int ymax = wmax + 2*tmid
    
    cdef np.ndarray[DTYPE_t, ndim=2] h = np.zeros([xmax, ymax], dtype=DTYPE)

    cdef int x, y, s, t, v, w
    cdef int s_from, s_to, t_from, t_to

    cdef DTYPE_t value

    # Do convolution
    for x in range(xmax):
        for y in range(ymax):
            # Calculate pixel value for h at (x,y). Sum one component
            # for each pixel (s, t) of the filter g.
            s_from = max(smid - x, -smid)
            s_to = min((xmax - x) - smid, smid + 1)
            t_from = max(tmid - y, -tmid)
            t_to = min((ymax - y) - tmid, tmid + 1)
            value = 0
            for s in range(s_from, s_to):
                for t in range(t_from, t_to):
                    v = x - smid + s
                    w = y - tmid + t
                    value += g[smid - s, tmid - t] * f[v, w]
            h[x, y] = value
    return h

In [4]:
%%cython

import numpy as np
cimport numpy as np

DTYPE = np.int
ctypedef np.int_t DTYPE_t

cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def naive_con3(np.ndarray[DTYPE_t, ndim=2] f, np.ndarray[DTYPE_t, ndim=2] g):
    if g.shape[0] % 2 != 1 and f.shape[0] % 2 != 1:
        raise ValueError("Only odd dimensions on filter supported!")
    assert f.dtype == DTYPE and g.dtype == DTYPE
    
    cdef int vmax = f.shape[0]
    cdef int wmax = f.shape[1]
    cdef int smax = g.shape[0]
    cdef int tmax = g.shape[1]
    cdef int smid = smax // 2
    cdef int tmid = tmax // 2
    cdef int xmax = vmax + 2*smid
    cdef int ymax = wmax + 2*tmid
    
    cdef np.ndarray[DTYPE_t, ndim=2] h = np.zeros([xmax, ymax], dtype=DTYPE)

    cdef int x, y, s, t, v, w
    cdef int s_from, s_to, t_from, t_to

    cdef DTYPE_t value

    # Do convolution
    for x in range(xmax):
        for y in range(ymax):
            # Calculate pixel value for h at (x,y). Sum one component
            # for each pixel (s, t) of the filter g.
            s_from = max(smid - x, -smid)
            s_to = min((xmax - x) - smid, smid + 1)
            t_from = max(tmid - y, -tmid)
            t_to = min((ymax - y) - tmid, tmid + 1)
            value = 0
            for s in range(s_from, s_to):
                for t in range(t_from, t_to):
                    v = x - smid + s
                    w = y - tmid + t
                    value += g[smid - s, tmid - t] * f[v, w]
            h[x, y] = value
    return h

In [5]:
import numpy as np
N = 300
f = np.arange(N*N, dtype=np.int).reshape((N, N))
g = np.arange(9*9, dtype=np.int).reshape((9, 9))

In [6]:
%timeit -n3 -r3 naive_con1(f, g)


3.69 s ± 43.2 ms per loop (mean ± std. dev. of 3 runs, 3 loops each)

In [7]:
%timeit -n3 -r3 naive_con2(f, g)


15.8 ms ± 467 µs per loop (mean ± std. dev. of 3 runs, 3 loops each)

In [8]:
%timeit -n3 -r3 naive_con3(f, g)


9.78 ms ± 188 µs per loop (mean ± std. dev. of 3 runs, 3 loops each)