scikit-imageを読み解く 2（Cythonの実例)

Pythonを拡張するCythonは、Pythonベースの型付けでの最適化だけではなく、
C/C++言語で書かれたライブラリをPythonから使えるようにwrapすることもできる。
scikit-imageはその両方を用いて実装されているPython用のライブラリである。

今回は、C/C++言語で書かれたライブラリをPythonから使えるようにwrapする様子を眺めてみようと思う。ますはpxdファイルに着目してみる。

C/C++言語をCythonでwrapしている場合には、pxdファイルが必ずある。
しかし、pxdファイルはpyxファイルのうち宣言部分を取り出したもののようで必ずしも外部Cコードをwrapしているかどうかは関係がない。

###**Cythonにおける宣言部分のコード(拡張子 .pxd)**

./skimage/filters/rank/core_cy.pxd
./skimage/graph/heap.pxd
./skimage/graph/_mcp.pxd
./skimage/measure/_ccomp.pxd
./skimage/_shared/geometry.pxd
./skimage/_shared/interpolation.pxd
./skimage/_shared/transform.pxd


```py:_ccomp.pxd
"""Export fast union find in Cython"""
cimport numpy as cnp

DTYPE = cnp.intp
ctypedef cnp.intp_t DTYPE_t

cdef DTYPE_t find_root(DTYPE_t *forest, DTYPE_t n) nogil
cdef void set_root(DTYPE_t *forest, DTYPE_t n, DTYPE_t root) nogil
cdef void join_trees(DTYPE_t *forest, DTYPE_t n, DTYPE_t m) nogil
```

「cimport は、定義ファイルや実装ファイルの中で、他の定義ファイル中で宣言した名前にアクセスするときに使います。」



pxdファイルがある場合、対応するpyxファイルがある。
（pxdファイル、pyxファイルはC/C++のwrapでなくてもある。）
./skimage/filters/rank/core\_cy.pyx
./skimage/graph/\_mcp.pyx
./skimage/graph/heap.pyx
./skimage/measure/\_ccomp.pyx
./skimage/\_shared/geometry.pyx
./skimage/\_shared/transform.pyx


```py:_ccomp.pyx
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False

import numpy as np
import warnings

cimport numpy as cnp

"""
See also:

  Christophe Fiorio and Jens Gustedt,
  "Two linear time Union-Find strategies for image processing",
  Theoretical Computer Science 154 (1996), pp. 165-181.

  Kensheng Wu, Ekow Otoo and Arie Shoshani,
  "Optimizing connected component labeling algorithms",
  Paper LBNL-56864, 2005,
  Lawrence Berkeley National Laboratory
  (University of California),
  http://repositories.cdlib.org/lbnl/LBNL-56864

"""

DTYPE = np.intp

# Short int - could be more graceful to the CPU cache
ctypedef cnp.int32_t INTS_t

cdef struct s_shpinfo

ctypedef s_shpinfo shape_info
ctypedef int (* fun_ravel)(int, int, int, shape_info *)


# For having stuff concerning background in one place
ctypedef struct bginfo:
    ## The value in the image (i.e. not the label!) that identifies
    ## the background.
    DTYPE_t background_val
    DTYPE_t background_node
    ## Identification of the background in the labelled image
    DTYPE_t background_label


cdef void get_bginfo(background_val, bginfo *ret) except *:
    if background_val is None:
        warnings.warn(DeprecationWarning(
                'The default value for `background` will change to 0 in v0.12'
            ))
        ret.background_val = -1
    else:
        ret.background_val = background_val

    # The node -999 doesn't exist, it will get subsituted by a meaningful value
    # upon the first background pixel occurence
    ret.background_node = -999
    ret.background_label = -1

(後略)
```


C/C++のソースコードは
以下が見つかった。
./skimage/external/tifffile/tifffile.c
./skimage/restoration/unwrap_2d_ljmu.c
./skimage/restoration/unwrap_3d_ljmu.c
./skimage/_shared/vectorized_ops.h


これらのCのソースコードのファイル名を含んでいるsetup.py は次のとおりでした。
それ以外のファイルは、C言語の関数をwrapしたものではなく、pyxファイルからcythonによって生じたC言語のファイルのようです。

\> egrep (tifffile.c|unwrap_[23]d_ljmu.c) ./check_bento_build.py 
./skimage/external/setup.py:                         sources=['tifffile/tifffile.c'],
./skimage/restoration/setup.py:    unwrap_sources_2d = ['_unwrap_2d.c', 'unwrap_2d_ljmu.c']
./skimage/restoration/setup.py:    unwrap_sources_3d = ['_unwrap_3d.c', 'unwrap_3d_ljmu.c']

また、外部のファイルから型定義を参照する構文としてcdef externがあるようです。
この中で、../_shared/vectorized_ops.hがcythonでwrapするのに使われているらしいことがわかりました。

$ grep 'cdef extern' `find . -name '*.py*' -print`
./skimage/feature/_texture.pyx:cdef extern from "numpy/npy_math.h":
./skimage/filters/_ctmf.pyx:cdef extern from "../_shared/vectorized_ops.h":
./skimage/graph/heap.pyx:cdef extern from "pyport.h":
./skimage/restoration/_unwrap_2d.pyx:cdef extern from *:
./skimage/restoration/_unwrap_3d.pyx:cdef extern from *:

###**まとめ：**
・cythonを実行する前のC/C++言語のソースコードがある場合だけが、cythonでC/C++言語をwrapしているコードであるようだ。
・pyxファイルをcythonで実行すると.cの拡張子を持つファイルが作られるので、setup.pyファイル中に*.cのファイルが含まれているからといってC/C++言語でwrapしているわけではない。
・cimportはC/C++言語中の宣言をcython中で利用可能にするものであって、C/C++言語のwrapだけに固有の機能というわけではない。

(早くcythonを使って、自作のwrapを行いたいものです。)

###**参考情報**
[Cython モジュール間で宣言を共有する](
http://omake.accense.com/static/doc-ja/cython/src/userguide/sharing_declarations.html)


[「Cython――Cとの融合によるPythonの高速化」]
(http://www.oreilly.co.jp/books/9784873117270/)
10章　Cython、Numpy、型付きメモリビュー