Mahotas内部结构

如果您试图了解mahotas是如何工作的,以便修复、扩展它(补丁总是受欢迎的),或者在您的项目中使用它的一些技术,这一节是很有意义的。

哲理

Mahotas不应该一团糟。

这是我的主要开发目标,如果我实现了这一目标,仅此一项就应该会使Mahotas进入软件包的前10%到1%。

Mahotas应该没有虫子。没有。永远不会。

当然,有些人悄悄地进来了。所以,我们满足于下一个最好的事情: Mahotas should have no * *known bugs **。无论何时发现漏洞,当务之急都是消除它。

请阅读 principles of mahotas

C++/Python分部

Mahotas在很大程度上是用C++编写的,但几乎所有的情况下,您都会调用一个检查类型的Python函数,然后调用内部函数。这稍微慢了一点,但以这种方式显影更容易(而且,除了最小的图像外,对所有图像来说,这都不重要)。

所以每个人 module.py 将与其关联的 _module.cpp

C++模板

Mahotas使用C++(而不是纯C)的主要原因是使用模板。几乎所有的C++函数实际上都是2个函数:

  1. A py_function 它使用PythonC/API来获取参数&c。这几乎总是纯C。

  2. 一个模板 function<dtype> 它适用于 dtype 执行实际操作。

举个例子,这就是为什么 erode 已经实施了。 py_erode 是通用的::

PyObject* py_erode(PyObject* self, PyObject* args) {
    PyArrayObject* array;
    PyArrayObject* Bc;
    if (!PyArg_ParseTuple(args,"OO", &array, &Bc)) return NULL;
    PyArrayObject* res_a = (PyArrayObject*)PyArray_SimpleNew(array->nd,array->dimensions,PyArray_TYPE(array));
    if (!res_a) return NULL;
    PyArray_FILLWBYTE(res_a, 0);
#define HANDLE(type) \
    erode<type>(numpy::aligned_array<type>(res_a), numpy::aligned_array<type>(array), numpy::aligned_array<type>(Bc));\

        SAFE_SWITCH_ON_INTEGER_TYPES_OF(array)
#undef HANDLE
    ...

这些函数通常包含大量样板代码:读取参数,执行一些健全性检查,可能是一些初始化,然后在 SAFE_SWITCH_ON_INTEGER_TYPES_OF() 和朋友,它们调用执行实际工作的模板的正确专门化。在本例中 erode 实施(二进制)侵蚀:

template<typename T>
void erode(numpy::aligned_array<T> res, numpy::aligned_array<T> array, numpy::aligned_array<T> Bc) {
    gil_release nogil;
    const unsigned N = res.size();
    typename numpy::aligned_array<T>::iterator iter = array.begin();
    filter_iterator<T> filter(res.raw_array(), Bc.raw_array());
    const unsigned N2 = filter.size();
    T* rpos = res.data();

    for (int i = 0; i != N; ++i, ++rpos, filter.iterate_with(iter), ++iter) {
        for (int j = 0; j != N2; ++j) {
            T arr_val = false;
            filter.retrieve(iter, j, arr_val);
            if (filter[j] && !arr_val) goto skip_this_one;
        }
        *rpos = true;
        skip_this_one: continue;
    }
}

模板机构并不是那么复杂,使用它的功能非常简单和易于阅读。唯一的缺点是代码大小有所扩大。然而,考虑到这些函数的规模很小,这不是一个大问题。

在上面的代码片段中,您可以看到一些其他的C++机制:

gil_release

这是一个RAII对象,它在其构造函数中释放GIL,并在其析构函数中将其取回。通常,模板函数将在特定于Python的代码完成后释放GIL。

array

这是一个很薄的封装 PyArrayObject 知道其类型并具有迭代器的。依赖这些对象还有另一个优势,那就是在调试模式下,它检查许多内存访问的边界。虽然这对于日常使用来说非常昂贵,但它可以比其他方法更快地捕捉到错误。

filter_iterator

本文摘自 scipy.ndimage 在图像上迭代并在每个像素周围使用居中过滤器(它跟踪所有边界条件)是很有用的。

内部循环是侵蚀的直接实现,正如人们所希望的那样:对于图像中的每个像素,查看其邻居。如果全部为真,则将相应的输出像素设置为 true (否则,跳过它,因为它已被初始化为零)。

大多数功能都遵循这种体系结构。