Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connected Components python interface #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Expand Up @@ -14,3 +14,5 @@ before_script:
- "export PYTHONPATH=$VIRTUAL_ENV/lib/python2.7/site-packages:$PYTHONPATH"
script:
- make test
- cd ..
- nosetests blockedarray
3 changes: 2 additions & 1 deletion .travis_scripts/requirements/development-stage2.txt
@@ -1 +1,2 @@
h5py==2.1.3
h5py==2.2.1
nose
1 change: 1 addition & 0 deletions blockedarray/.gitignore
@@ -1,2 +1,3 @@
_blockedarray.so
*.pyc
*.h5
3 changes: 2 additions & 1 deletion blockedarray/CMakeLists.txt
Expand Up @@ -24,6 +24,7 @@ add_library(_blockedarray SHARED
module_py.cxx
blockedarray_py.cxx
blockwisecc_py.cxx
adapters_py.cxx
${EXTRA_SRCS}
)

Expand All @@ -34,9 +35,9 @@ else()
endif()

target_link_libraries(_blockedarray
${Boost_PYTHON_LIBRARIES}
snappy
${PYTHON_LIBRARY}
${Boost_PYTHON_LIBRARIES}
${HDF5_LIBRARY}
${HDF5_HL_LIBRARY}
${VIGRA_IMPEX_LIBRARY}
Expand Down
2 changes: 1 addition & 1 deletion blockedarray/__init__.py
@@ -1,3 +1,3 @@
from _blockedarray import *


from opBlockedConnectedComponents import OpBlockedConnectedComponents
148 changes: 148 additions & 0 deletions blockedarray/adapters.py
@@ -0,0 +1,148 @@

import vigra

import numpy as np
from _blockedarray import *


# This file contains examples for using the Source and Sink classes from
# blockedarray. These should be viewed as guidelines on how to use the exported
# interfaces
# dim[2|3].Source[U|S][8|16|32|64]
# dim[2|3].Sink[U|S][8|16|32|64]
# The ABC classes are for reference on what methods are exposed, the Example
# classes show how the base classes can be used. There is a complete workflow
# for blockwise connected components in the test file
# test_connectedcomponents.py


# examples deal with 8bit image input and 32bit image output
_Source = dim3.PySourceU8
_Sink = dim3.PySinkU32


## Source Interface
#
# This class provides the python interface to the C++ class `BW::Source`. If
# you inherit from this class, be sure to implement *all* abstract methods.
class SourceABC(_Source):

## Constructor
#
# Be sure to call super().__init__ if you override the constructor!
def __init__(self):
super(SourceABC, self).__init__()

## Set a custom ROI
# selects only the region of interest given from the
# underlying data source. When readBlock() is used, the coordinates
# are relative to roi[0]
#
# @param roi a tuple containing 2 lists of length 3 (incl. start, excl. stop)
def pySetRoi(self, roi):
raise NotImplementedError
#pass

## Volume shape getter
#
# @return must be a tuple of (python) integers
def pyShape(self):
raise NotImplementedError
#return (10, 10, 10)

## Block getter
#
# read a block of data into a 3d numpy array
#
# @param roi a tuple containing 2 lists of length 3 (incl. start, excl. stop)
# @param output a 3d numpy.ndarray with shape roi[1]-roi[0] and dtype uint8
def pyReadBlock(self, roi, output):
raise NotImplementedError
#return True


## Sink Interface
#
# This class provides the python interface to the C++ class `BW::Sink`. If
# you inherit from this class, be sure to implement *all* abstract methods.
class SinkABC(_Sink):

## Constructor
#
# Be sure to call super().__init__ if you override the constructor!
def __init__(self):
super(SinkABC, self).__init__()

## Write a block of data
#
# @param roi a tuple containing 2 lists of length 3 (incl. start, excl. stop)
# @param block a 3d numpy.ndarray with shape roi[1]-roi[0] and dtype int32
def pyWriteBlock(self, roi, block):
raise NotImplementedError
#return True


class DummySource(SourceABC):
def __init__(self):
super(DummySource, self).__init__()

def pySetRoi(self, roi):
pass

def pyShape(self):
return (100, 100, 10)

def pyReadBlock(self, roi, output):
output[...] = 0
return True


class ExampleSource(SourceABC):
def __init__(self, vol):
# the call to super() is super-important!
super(ExampleSource, self).__init__()
self._vol = vol
self._p = np.zeros((len(vol.shape),), dtype=np.long)
self._q = np.asarray(vol.shape, dtype=np.long)

def pySetRoi(self, roi):
self._p = np.asarray(roi.p, dtype=np.long)
self._q = np.asarray(roi.q, dtype=np.long)

def pyShape(self):
return self._vol.shape

def pyReadBlock(self, roi, output):
roiP = np.asarray(roi.p)
roiQ = np.asarray(roi.q)
p = self._p + roiP
q = p + roiQ - roiP
if np.any(q > self._q):
raise IndexError("Requested roi is too large for selected "
"roi (previous call to setRoi)")
s = _roi2slice(p, q)
output[...] = self._vol[s]
return True


class ExampleSink(SinkABC):
def __init__(self):
super(ExampleSink, self).__init__()
self.vol = None

def pyWriteBlock(self, roi, block):
if self.vol is None:
shape = self.shape
shape = _v2tup(shape)
self.vol = np.zeros(shape, dtype=np.uint8)
s = _roi2slice(roi.p, roi.q)
self.vol[s] = block


def _v2tup(v, d=3):
return tuple([v[i] for i in range(d)])


def _roi2slice(p, q):
s = [slice(p[i], q[i]) for i in range(len(p))]
return tuple(s)
140 changes: 140 additions & 0 deletions blockedarray/adapters_py.cxx
@@ -0,0 +1,140 @@
#define PY_ARRAY_UNIQUE_SYMBOL blockedarray_PyArray_API
#define NO_IMPORT_ARRAY

#include "Python.h"

#include <boost/python.hpp>
#include <boost/python/slice.hpp>
#include <boost/tuple/tuple.hpp>

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>

#include <bw/connectedcomponents.h>
#include <bw/thresholding.h>
#include <bw/channelselector.h>

#include "adapters_py.h"

#include <bw/extern_templates.h>

using namespace BW;


template<int N, class T>
struct PySourceABC : Source<N,T>, boost::python::wrapper<Source<N,T> >
{
public:

typedef typename Source<N,T>::V TinyVec;

PySourceABC() {}
virtual ~PySourceABC() {}

void setRoi(Roi<N> roi) {
this->pySetRoi(roi);
}

TinyVec shape() const {
return this->pyShape();
}

bool readBlock(Roi<N> roi, vigra::MultiArrayView<N,T>& block) const {
bool ret;

//temporary NumpyArray, because MultiArrayView is not convertible to python
vigra::NumpyArray<N,T> tempArray(roi.q-roi.p);

if (!this->pyReadBlock(roi, tempArray))
return false;

block.copy(tempArray);

return true;
}

void pySetRoi(Roi<N> roi) {
this->get_override("pySetRoi")(roi);
}

TinyVec pyShape() const {
return this->get_override("pyShape")();
};

bool pyReadBlock(Roi<N> roi, vigra::NumpyArray<N,T>& block) const {
return this->get_override("pyReadBlock")(roi, block);
};
};

template<int N, class T>
struct PySinkABC : Sink<N,T>, boost::python::wrapper<Sink<N,T> > {
public:
typedef typename Roi<N>::V V;

PySinkABC() {};
virtual ~PySinkABC() {};

bool writeBlock(Roi<N> roi, const vigra::MultiArrayView<N,T>& block) {
vigra::NumpyArray<N,T> tempArray(block);
return this->pyWriteBlock(roi, tempArray);
}

bool pyWriteBlock(Roi<N> roi, const vigra::NumpyArray<N,T>& block) {
return this->get_override("pyWriteBlock")(roi, block);
};


V getShape() const {
return this->shape_;
}

void setShape(V shape) {
this->shape_ = shape;
}

V getBlockShape() const {
return this->blockShape_;
}

void setBlockShape(V shape) {
this->blockShape_ = shape;
}
};



template<int N, class T>
void exportSpecificSourceAdapter(std::string suffix) {
using namespace boost::python;

class_<PySourceABC<N,T>, boost::noncopyable>(("PySource" + suffix).c_str())
.def("pySetRoi", pure_virtual(&PySourceABC<N,T>::pySetRoi))
.def("pyShape", pure_virtual(&PySourceABC<N,T>::pyShape))
.def("pyReadBlock", pure_virtual(&PySourceABC<N,T>::pyReadBlock))
;

class_<PySinkABC<N,T>, boost::noncopyable>(("PySink" + suffix).c_str())
.def("pyWriteBlock", pure_virtual(&PySinkABC<N,T>::pyWriteBlock))
.add_property("shape", &PySinkABC<N,T>::getShape, &PySinkABC<N,T>::setShape)
.add_property("blockShape", &PySinkABC<N,T>::getBlockShape, &PySinkABC<N,T>::setBlockShape)
;
}

template <int N>
void exportSourceAdaptersForDim() {

exportSpecificSourceAdapter<N,vigra::UInt8>("U8");
//exportSpecificSourceAdapter<N,vigra::UInt16>("U16");
exportSpecificSourceAdapter<N,vigra::UInt32>("U32");

exportSpecificSourceAdapter<N,vigra::Int8>("S8");
//exportSpecificSourceAdapter<N,vigra::Int16>("S16");
exportSpecificSourceAdapter<N,vigra::Int32>("S32");

exportSpecificSourceAdapter<N,float>("F");
exportSpecificSourceAdapter<N,double>("D");
}

template void exportSourceAdaptersForDim<2>();
template void exportSourceAdaptersForDim<3>();

7 changes: 7 additions & 0 deletions blockedarray/adapters_py.h
@@ -0,0 +1,7 @@
#ifndef ADAPTERS_PY_H
#define ADAPTERS_PY_H

template <int N>
void exportSourceAdaptersForDim();

#endif /* ADAPTERS_PY_H */