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
- 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