13.07.2015 Views

Proceedings of the 8th Annual Python in Science Conference

Proceedings of the 8th Annual Python in Science Conference

Proceedings of the 8th Annual Python in Science Conference

SHOW MORE
SHOW LESS
  • No tags were found...

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Convert-XY: type-safe <strong>in</strong>terchange <strong>of</strong> C++ and <strong>Python</strong> conta<strong>in</strong>ers for NumPy extensionsCopyThis compound type specifies that <strong>the</strong> str<strong>in</strong>g keys andvector elements be copied but <strong>the</strong> buffer po<strong>in</strong>ter to <strong>the</strong>arrays should be reused. Figure 5 illustrates how thisrecursive <strong>in</strong>stantiation works.DefaultToCPPAction::ActionCopyKeyActionDefaultToCPPAction::ActionActionCopyValueActionDefaultToCPPAction::ActionActionCopyElementActionDefaultToCPPAction Action::ActionActionAllocateCopyCopyFigure 5 The process <strong>of</strong> conversion is deducedat compile time via recursive <strong>in</strong>stantiation <strong>of</strong>DefaultToCPPAction.The process <strong>of</strong> conversion can be overridden by pass<strong>in</strong>gas a template argument a different Action type. Forexample, suppose <strong>the</strong> target BasicImage was allocatedfrom elsewhere (e.g. a special malloc function thatproperly aligns buffers), we can override <strong>the</strong> defaultconversion action (Reuse) so Copy is used as <strong>the</strong>Action <strong>in</strong>stead. The ConvertXY::convert_overridefunction can be used for this purpose:void example(PyObject *x, BasicImage &y) {convert_override(x, y);}Suppose a C++ object y <strong>of</strong> a compound conta<strong>in</strong>er typealready exists, e.g. an object <strong>of</strong> type map. In this case, <strong>the</strong> keys shouldnot be copied but we must ensure that <strong>the</strong> C++ mapconta<strong>in</strong>s exactly <strong>the</strong> same set <strong>of</strong> keys as <strong>the</strong> <strong>Python</strong>dictionary. This is done by first check<strong>in</strong>g that <strong>the</strong> size<strong>of</strong> <strong>the</strong> dictionary is <strong>the</strong> same as <strong>the</strong> std::map and <strong>the</strong>nmak<strong>in</strong>g sure each unique key <strong>in</strong> <strong>the</strong> dictionary is equivalentto some unique key <strong>in</strong> <strong>the</strong> map.The contents <strong>of</strong> a dictionary <strong>of</strong> arrays can be copied<strong>in</strong>to a std::map <strong>of</strong> exist<strong>in</strong>g BasicImage buffers as follows:void example(PyObject *x,map &y) {convert_override(x, y);}The CheckSize simple checks that <strong>the</strong> size <strong>of</strong><strong>the</strong> std::map and <strong>Python</strong> map are equivalent.CheckExists ensures each key <strong>in</strong> <strong>the</strong> <strong>Python</strong> map isalso <strong>in</strong> <strong>the</strong> STL map.Reference count<strong>in</strong>g is automatically handled dur<strong>in</strong>gconversion with PyCXX. In cases where <strong>the</strong> flow <strong>of</strong>execution may return to <strong>Python</strong> after conversion, Py-CXX can be used to ensure objects that are <strong>in</strong> use byC++ rout<strong>in</strong>es don’t get prematurely destroyed.:void example(PyObject *x,BasicImage &y) {// The l<strong>in</strong>e below is safe.convert(x,y);// However, if it can’t be guaranteed that program// control flow will never return to <strong>Python</strong>// for <strong>the</strong> rest <strong>of</strong> this function, use PyCXX’s// Py::Object to <strong>in</strong>crement <strong>the</strong> reference count.Py::Object obj(x);// If <strong>the</strong> flow <strong>of</strong> execution returns to <strong>Python</strong> while// execut<strong>in</strong>g <strong>the</strong> function below, y will always po<strong>in</strong>t// to a valid buffer.compute(y);}The O<strong>the</strong>r Way Around: from C++ to <strong>Python</strong>When convert<strong>in</strong>g from C++ to <strong>Python</strong>, <strong>the</strong> typestructure <strong>of</strong> <strong>the</strong> target <strong>Python</strong> object must beprespecified at compile time (e.g. <strong>the</strong> type <strong>of</strong> <strong>the</strong><strong>Python</strong> object to be created is static). This is notstraightforward s<strong>in</strong>ce <strong>the</strong>re is sometimes more thanone compatible <strong>Python</strong> type for a C++ type. Forexample, an STL vector can be converted to a <strong>Python</strong>list, tuple, or NumPy object array. In Convert-XYthis is deduced with a compile-time-only Structuremetatype deduced by recursive <strong>in</strong>stantiation <strong>of</strong>DefaultTo<strong>Python</strong>Structure::Structure.Figure 6illustrates how DefaultTo<strong>Python</strong>Structureis recursively <strong>in</strong>stantiated to yield a default <strong>Python</strong>target type.To<strong>Python</strong>DefaultStructure::StructureStructurePyDictKeyStructureTo<strong>Python</strong>DefaultStructure::StructureStructurePyStr<strong>in</strong>gNon-term<strong>in</strong>al Structure TypeTerm<strong>in</strong>al Structure TypeExpanded typedefValueStructureTo<strong>Python</strong>DefaultStructure::StructureElementStructurePyListElementStructureDefault::StructureStructurePyArrayPyDictFigure 6 The default <strong>Python</strong> type when convert<strong>in</strong>gfrom C++ to <strong>Python</strong> is deduced by recursive <strong>in</strong>stantiation<strong>of</strong> To<strong>Python</strong>DefaultStructure.As <strong>the</strong> figure illustrates, <strong>the</strong> default type for a <strong>Python</strong>target given a source object that’s an STL map mapp<strong>in</strong>gstr<strong>in</strong>g to vectors <strong>of</strong> images is PyDict. If a tuple is preferredover a list, <strong>the</strong> default structure can be overridenas follows:c○2009, D. Eads, E. Rosten 32


<strong>Proceed<strong>in</strong>gs</strong> <strong>of</strong> <strong>the</strong> 8 th <strong>Python</strong> <strong>in</strong> <strong>Science</strong> <strong>Conference</strong> (SciPy 2009)PyObject *y= convert_override(x)BasicImagedata float*PyArrayObject*data float*If an attempt is made to convert a C++ object to a<strong>Python</strong> object that’s <strong>in</strong>compatible, mean<strong>in</strong>gful compilermessages can be generated via a trick <strong>in</strong>volv<strong>in</strong>g<strong>in</strong>complete types:<strong>in</strong> not_found.hpp:88:’unsupport_type_or_structure’has <strong>in</strong>complete typeCannotConvertTo<strong>Python</strong>.Allocat<strong>in</strong>g Result ArraysOne approach to allocat<strong>in</strong>g result arrays is to copy<strong>the</strong> result (e.g. image or matrix) <strong>in</strong>to a new NumPyarray. This overhead can be avoided us<strong>in</strong>g <strong>the</strong>ConvertXY::allocate_numpy_array function. Thisfunction is templated, and pattern match<strong>in</strong>g is performedon <strong>the</strong> C++ type to generate an allocatorat compile time. In this example, when <strong>the</strong>allocate_numpy_array function is called, NumPyarray and BasicImage object are allocated at <strong>the</strong>same time, shar<strong>in</strong>g <strong>the</strong> same buffer po<strong>in</strong>ter. Theimage is smoo<strong>the</strong>d us<strong>in</strong>g <strong>the</strong> LIBCVD functionconvolveGaussian, as illustrated on figure 7:PyObject *smooth_image(PyObject *x, double radius) {}// First convert <strong>the</strong> NumPy array <strong>in</strong>put image// to a BasicImage.Holder y;convert(x, y);// Now allocate enough memory to store <strong>the</strong>// result. Use <strong>the</strong> same buffer po<strong>in</strong>ter for// both <strong>the</strong> NumPy array and BasicImage.Holder result;PyObject *pyResult =allocate_numpy_array(result,y.getReference().size());// Us<strong>in</strong>g LIBCVD, convolve a Gaussian on <strong>the</strong>// converted <strong>in</strong>put, store <strong>the</strong> result <strong>in</strong> <strong>the</strong>// buffer po<strong>in</strong>ted to by both pyResult and// result.CVD::convolveGaussian(y.getReference(),result.getReference(),radius);return pyResult;Libraries SupportedConvert-XY supports conversion between <strong>Python</strong>and objects from several C++ libraries <strong>in</strong>clud<strong>in</strong>gTooN, LIBCVD, and STL. The library is split <strong>in</strong>tothree different headers, all <strong>of</strong> which are optional.• ConvertXY/TooN.hpp: for convert<strong>in</strong>g betweenNumPy arrays and TooN matrices and vectors.• ConvertXY/STL.hpp: for convert<strong>in</strong>g between STLstructures and <strong>Python</strong> objects.C++ Computationfloat buffer<strong>Python</strong>ComputationFigure 7 allocate_numpy_array constructs a new<strong>Python</strong> and C++ array at <strong>the</strong> same time, to wit, aNumPy array and a BasicImage object. The NumPyarray owns <strong>the</strong> buffer allocated so when its referencecount goes to zero, its memory buffer will be freed.• ConvertXY/CVD.hpp: for convert<strong>in</strong>g betweenNumPy arrays and CVD::Image C++ objects.The compiler can convert comb<strong>in</strong>ations<strong>of</strong> STL, CVD, and TooN structures (e.g.vector)only when <strong>the</strong>ir respective headers are <strong>in</strong>cludedtoge<strong>the</strong>r.Tom’s object-oriented Numerics Library (TooN)TooN is a header-only l<strong>in</strong>ear algebra library thatis very fast on small matrices mak<strong>in</strong>g it particularlyuseful for real-time Computer Vision applications[Dru03]. TooN provides templated classes for staticand dynamically-sized vectors and matrices, and usestemplated-based pattern match<strong>in</strong>g to catch for commonl<strong>in</strong>ear algebra errors at compile time. For example,when convert<strong>in</strong>g <strong>the</strong> result <strong>of</strong> an a matrix multiplication<strong>in</strong>volv<strong>in</strong>g two matrices X and Y <strong>of</strong> <strong>in</strong>compatibledimensions:Matrix X;Matrix Y;PyObject *Z;Z = convert(X * Y);an easy-to-understand compiler error results,“dimension_mismatch has <strong>in</strong>complete typeSizeMismatch”.Dimension check<strong>in</strong>g is performed at compile time evenwhen some dimensions are not known beforehand as<strong>the</strong> follow<strong>in</strong>g example shows:Matrix X;Matrix Y;PyObject *Z;Z = convert(X * Y);If a dimension check passes at compile time, it is omittedat run time. A buffer po<strong>in</strong>ter <strong>of</strong> a NumPy arraycan be reused <strong>in</strong> a TooN::Matrix by specify<strong>in</strong>g a layouttype as <strong>the</strong> third template argument to Matrix(which is matched with DefaultAction):33 http://conference.scipy.org/proceed<strong>in</strong>gs/SciPy2009/paper_4


Convert-XY: type-safe <strong>in</strong>terchange <strong>of</strong> C++ and <strong>Python</strong> conta<strong>in</strong>ers for NumPy extensionsvoid example(PyObject *x) {Holder y;convert(x, y);cout I[y-1][x] && ctr > I[y-1][x+1] &&ctr > I[y][x-1] && ctr > I[y][x+1] &&ctr > I[y+1][x-1] && ctr > I[y+1][x] &&ctr > I[y+1][x+1] ) {maxima.push_back(ImageRef(x, y));}}}return convert(maxima);}The example shows that with LIBCVD’s lightweight,simple design, computer vision codes can be written<strong>in</strong> C++ succ<strong>in</strong>ct with good run-time performance.Convert-XY <strong>in</strong> comb<strong>in</strong>ation with an exist<strong>in</strong>g wrapp<strong>in</strong>gtool greatly simplifies <strong>the</strong> task <strong>of</strong> <strong>in</strong>terfac<strong>in</strong>g<strong>Python</strong> with a C++ library.The function is templated so it works on array elements<strong>of</strong> any numeric type. Templated functions are harderto wrap from <strong>Python</strong>. One simple, flexible approach<strong>in</strong>volves writ<strong>in</strong>g a function that walks across a list <strong>of</strong>types specified as a template argument. In this case,<strong>the</strong> list <strong>of</strong> types is called List:template PyObject*large_local_maxima_walk(PyObject *pyImage,PyObject *pyThresh) {typedef List::Head HeadType;typedef List::Tail TailList;const bool listEnd = List::listEnd;if (listEnd) {throw Py::Exception("type not supported!");}try {return large_local_maxima(pyImage,pyThresh);}catch (ConvertXY::TypeMismatch &e) {return large_local_maxima_walk(pyImage,pyThresh);}}Then write a C wrapper function:extern "C"PyObject* large_local_maxima_c(PyObject *pyImage,PyObject *pyThresh) {return large_local_maxima_walk(pyImage, pyThresh);}that calls <strong>the</strong> walk function on a list <strong>of</strong> types. This Cfunction can easily be called with SWIG, <strong>the</strong> <strong>Python</strong> CAPI, PyCXX, boost, or Cython. When debugg<strong>in</strong>g newtemplated functions, each error is usually reported by<strong>the</strong> compiler for each type <strong>in</strong> <strong>the</strong> type list. This can beavoided by first debugg<strong>in</strong>g with a list conta<strong>in</strong><strong>in</strong>g onlya s<strong>in</strong>gle type:extern "C"PyObject* large_local_maxima_c(PyObject *pyImage,PyObject *pyThresh) {return large_local_maxima_walk(pyImage, pyThresh);}Improvements and PyCXX IntegrationConvert-XY has been refactored considerably s<strong>in</strong>ceits first <strong>in</strong>troduction at <strong>the</strong> SciPy conference <strong>in</strong> August2009. The most significant improvements <strong>in</strong>cludecustomizable actions and structures, greatly simplifiedsemantics, as well as be<strong>in</strong>g fully <strong>in</strong>tegrated with <strong>the</strong>mature C++ package PyCXX. It handles <strong>Python</strong> referenc<strong>in</strong>gand dereferenc<strong>in</strong>g <strong>in</strong> Convert-XY is handledto make conversion safe to exceptions and return <strong>of</strong> executioncontrol to <strong>Python</strong>.c○2009, D. Eads, E. Rosten 34


<strong>Proceed<strong>in</strong>gs</strong> <strong>of</strong> <strong>the</strong> 8 th <strong>Python</strong> <strong>in</strong> <strong>Science</strong> <strong>Conference</strong> (SciPy 2009)Packages Us<strong>in</strong>g Convert-XYThe authors have written several packages that alreadyuse Convert-XY .• GGFE: grammar guided feature extraction is a techniquefor generat<strong>in</strong>g image features from generativegrammars. GGFE is a tool for express<strong>in</strong>g generativegrammars <strong>in</strong> pure <strong>Python</strong>. (see http://ggfe.googlecode.com). Some image features are implemented<strong>in</strong> C++ us<strong>in</strong>g LIBCVD with conversion handledby Convert-XY.• ACD: a <strong>Python</strong> package for anomalous change detection<strong>in</strong> hyperspectral imagery. All algorithms arewritten <strong>in</strong> pure <strong>Python</strong> but optional, performanceenhancedC++ versions <strong>of</strong> functions make heavy use<strong>of</strong> Convert-XY. ACD is property <strong>of</strong> <strong>the</strong> Los AlamosNational Laboratory and its release is still be<strong>in</strong>g consideredby management.• ETSE: a package for perform<strong>in</strong>g dynamic time warp<strong>in</strong>gand time series cluster<strong>in</strong>g <strong>in</strong> <strong>Python</strong>. (see http://www.soe.ucsc.edu/~eads/s<strong>of</strong>tware.shtml)• Tesla: a new object localisation system under developmentas part <strong>of</strong> our research. The s<strong>of</strong>tware willbe released upon publication <strong>of</strong> our algorithm.ConclusionConvert-XY is a powerful tool that facilitates automaticconversion <strong>of</strong> arbitrarily structured conta<strong>in</strong>ersbetween C++ and <strong>Python</strong> with a succ<strong>in</strong>ct syntax,convert(x,y). By exploit<strong>in</strong>g template-based patternmatch<strong>in</strong>g <strong>in</strong> C++, dynamic type checkers and converterscan be recursively built based on <strong>the</strong> static structure<strong>of</strong> a C++ object. At run-time, <strong>the</strong> dispatcherclass decodes <strong>the</strong> type <strong>of</strong> <strong>the</strong> <strong>Python</strong> object and deduces<strong>the</strong> protocols it obeys. Additionally, conversionis customizable by specify<strong>in</strong>g different action or structuremeta-types. Large data sets can be converted between<strong>Python</strong> and C++ with m<strong>in</strong>imal copy<strong>in</strong>g. Whenpossible, erroneous conversions are caught at compiletime but o<strong>the</strong>rwise caught at run time. Convert-XY<strong>in</strong>tegrates with PyCXX to improve exception safetydur<strong>in</strong>g conversion. It can also be used to automaticallyfacilitate conversion for SWIG typemaps.LicenseConvert-XY is <strong>of</strong>fered under <strong>the</strong> terms <strong>of</strong> <strong>the</strong> GeneralPublic License version 2.0 with a special exception.As a special exception, you may use <strong>the</strong>se files as part <strong>of</strong> afree s<strong>of</strong>tware library without restriction. Specifically, if o<strong>the</strong>rfiles <strong>in</strong>stantiate templates or use macros or <strong>in</strong>l<strong>in</strong>e functionsfrom this library, or you compile this library and l<strong>in</strong>k it witho<strong>the</strong>r files to produce an executable, this library does not byitself cause <strong>the</strong> result<strong>in</strong>g executable to be covered by <strong>the</strong> GNUGeneral Public License. This exception does not however <strong>in</strong>validateany o<strong>the</strong>r reasons why <strong>the</strong> executable file might becovered by <strong>the</strong> GNU General Public License.Future WorkThe primary focus <strong>of</strong> Convert-XY’s development untilnow has been on greatly improv<strong>in</strong>g <strong>the</strong> safety, simplicity,and flexibility <strong>of</strong> <strong>the</strong> <strong>in</strong>terface. Mov<strong>in</strong>g forward,we plan to focus efforts on improv<strong>in</strong>g <strong>the</strong> documentation,f<strong>in</strong>ish<strong>in</strong>g a regression test suite, and writ<strong>in</strong>g atutorial on how to write custom converters for o<strong>the</strong>rlibraries.References[Abr03] D. Abrahams. Build<strong>in</strong>g Hybrid Systems withBoost <strong>Python</strong>. PyCon 2003. 2003.[Bea95] D. Beazley. Simplified Wrapper and InterfaceGenerator. http://www.swig.org/. 1995--.[Dru03] T. Drummond, E. Rosten, et al. TooN: Tom’sObject-oriented Numerics. http://mi.eng.cam.ac.uk/~twd20/TooNhtml/. 2003.[Ewi08] M. Ew<strong>in</strong>g. Cython. 2008--.[Hel00] T. Heller. ctypes. 2000--.[Jon01]E. Jones, T. Oliphant, P. Peterson, et al. “SciPy:Open Source Scientific tools for <strong>Python</strong>”. 2001--.[GvR92] G. van Rossum. <strong>Python</strong>. 1991--.[Ros04] E. Rosten, et al. LIBCVD. 2004--[Sco04] B. Scott, P. Dubois. Writ<strong>in</strong>g <strong>Python</strong> Extensions<strong>in</strong> C++ with PyCXX. http://cxx.sf.net/. 2004--.[Yak09] R. Yakovenko. Py++: Language B<strong>in</strong>d<strong>in</strong>gProject. 2009.35 http://conference.scipy.org/proceed<strong>in</strong>gs/SciPy2009/paper_4

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!