Mahotas内部结构¶
如果您试图了解mahotas是如何工作的,以便修复、扩展它(补丁总是受欢迎的),或者在您的项目中使用它的一些技术,这一节是很有意义的。
哲理¶
Mahotas不应该一团糟。
这是我的主要开发目标,如果我实现了这一目标,仅此一项就应该会使Mahotas进入软件包的前10%到1%。
Mahotas应该没有虫子。没有。永远不会。
当然,有些人悄悄地进来了。所以,我们满足于下一个最好的事情: Mahotas should have no * *known bugs **。无论何时发现漏洞,当务之急都是消除它。
C++/Python分部¶
Mahotas在很大程度上是用C++编写的,但几乎所有的情况下,您都会调用一个检查类型的Python函数,然后调用内部函数。这稍微慢了一点,但以这种方式显影更容易(而且,除了最小的图像外,对所有图像来说,这都不重要)。
所以每个人 module.py
将与其关联的 _module.cpp
。
C++模板¶
Mahotas使用C++(而不是纯C)的主要原因是使用模板。几乎所有的C++函数实际上都是2个函数:
A
py_function
它使用PythonC/API来获取参数&c。这几乎总是纯C。一个模板
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
(否则,跳过它,因为它已被初始化为零)。
大多数功能都遵循这种体系结构。