Skip to content

Instantly share code, notes, and snippets.

@berak
Created June 3, 2015 05:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save berak/b696020f05043a3e16b2 to your computer and use it in GitHub Desktop.
Save berak/b696020f05043a3e16b2 to your computer and use it in GitHub Desktop.
matlab wrapper
#!/usr/bin/env python
import sys, re, os, time
from string import Template
from parse_tree import ParseTree, todict, constants
from filters import *
class MatlabWrapperGenerator(object):
"""
MatlabWrapperGenerator is a class for generating Matlab mex sources from
a set of C++ headers. MatlabWrapperGenerator objects can be default
constructed. Given an instance, the gen() method performs the translation.
"""
def gen(self, module_roots, modules, extras, output_dir):
"""
Generate a set of Matlab mex source files by parsing exported symbols
in a set of C++ headers. The headers can be input in one (or both) of
two methods:
1. specify module_root and modules
Given a path to the OpenCV module root and a list of module names,
the headers to parse are implicitly constructed.
2. specifiy header locations explicitly in extras
Each element in the list of extras must be of the form:
'namespace=/full/path/to/extra/header.hpp' where 'namespace' is
the namespace in which the definitions should be added.
The output_dir specifies the directory to write the generated sources
to.
"""
# dynamically import the parsers
from jinja2 import Environment, FileSystemLoader
import hdr_parser
# parse each of the files and store in a dictionary
# as a separate "namespace"
parser = hdr_parser.CppHeaderParser()
ns = dict((key, []) for key in modules)
path_template = Template('${module}/include/opencv2/${module}.hpp')
for module in modules:
for module_root in module_roots:
# construct a header path from the module root and a path template
header = os.path.join(module_root, path_template.substitute(module=module))
if os.path.isfile(header):
break
else:
raise Exception('no header found for module %s!' % module)
# parse the definitions
ns[module] = parser.parse(header)
# parse the documentation
for extra in extras:
module = extra.split("=")[0]
header = extra.split("=")[1]
ns[module] = ns[module] + parser.parse(header) if module in ns else parser.parse(header)
# cleanify the parser output
parse_tree = ParseTree()
parse_tree.build(ns)
# setup the template engine
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jtemplate = Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True)
# add the custom filters
jtemplate.filters['formatMatlabConstant'] = formatMatlabConstant
jtemplate.filters['convertibleToInt'] = convertibleToInt
jtemplate.filters['toUpperCamelCase'] = toUpperCamelCase
jtemplate.filters['toLowerCamelCase'] = toLowerCamelCase
jtemplate.filters['toUnderCase'] = toUnderCase
jtemplate.filters['matlabURL'] = matlabURL
jtemplate.filters['stripTags'] = stripTags
jtemplate.filters['filename'] = filename
jtemplate.filters['comment'] = comment
jtemplate.filters['inputs'] = inputs
jtemplate.filters['ninputs'] = ninputs
jtemplate.filters['outputs'] = outputs
jtemplate.filters['noutputs'] = noutputs
jtemplate.filters['qualify'] = qualify
jtemplate.filters['slugify'] = slugify
jtemplate.filters['only'] = only
jtemplate.filters['void'] = void
jtemplate.filters['not'] = flip
# load the templates
tfunction = jtemplate.get_template('template_function_base.cpp')
tclassm = jtemplate.get_template('template_class_base.m')
tclassc = jtemplate.get_template('template_class_base.cpp')
tconst = jtemplate.get_template('template_map_base.m')
# create the build directory
output_source_dir = output_dir+'/src'
output_private_dir = output_source_dir+'/private'
output_class_dir = output_dir+'/+cv'
output_map_dir = output_dir+'/map'
if not os.path.isdir(output_source_dir):
os.makedirs(output_source_dir)
if not os.path.isdir(output_private_dir):
os.makedirs(output_private_dir)
if not os.path.isdir(output_class_dir):
os.makedirs(output_class_dir)
if not os.path.isdir(output_map_dir):
os.makedirs(output_map_dir)
# populate templates
for namespace in parse_tree.namespaces:
# functions
for method in namespace.methods:
populated = tfunction.render(fun=method, time=time, includes=namespace.name)
with open(output_source_dir+'/'+method.name+'.cpp', 'wb') as f:
f.write(populated.encode('utf-8'))
# classes
for clss in namespace.classes:
# cpp converter
populated = tclassc.render(clss=clss, time=time)
with open(output_private_dir+'/'+clss.name+'Bridge.cpp', 'wb') as f:
f.write(populated.encode('utf-8'))
# matlab classdef
populated = tclassm.render(clss=clss, time=time)
with open(output_class_dir+'/'+clss.name+'.m', 'wb') as f:
f.write(populated.encode('utf-8'))
# create a global constants lookup table
const = dict(constants(todict(parse_tree.namespaces)))
populated = tconst.render(constants=const, time=time)
with open(output_dir+'/cv.m', 'wb') as f:
f.write(populated.encode('utf-8'))
if __name__ == "__main__":
"""
Usage: python gen_matlab.py --jinja2 /path/to/jinja2/engine
--hdrparser /path/to/hdr_parser/dir
--moduleroot [ /path/to/opencv/modules /path/to/opencv_contrib/modules etc ]
--modules [core imgproc objdetect etc]
--extra namespace=/path/to/extra/header.hpp
--outdir /path/to/output/generated/srcs
gen_matlab.py is the main control script for generating matlab source
files from given set of headers. Internally, gen_matlab:
1. constructs the headers to parse from the module root and list of modules
2. parses the headers using CppHeaderParser
3. refactors the definitions using ParseTree
5. populates the templates for classes, function, enums and docs from the
definitions
gen_matlab.py requires the following inputs:
--jinja2 the path to the Jinja2 templating engine
e.g. ${CMAKE_SOURCE_DIR}/3rdparty
--hdrparser the path to the header parser directory
(opencv/modules/python/src2)
--moduleroot (optional) paths to the opencv directories containing the modules
--modules (optional - required if --moduleroot specified) the modules
to produce bindings for. The path to the include directories
as well as the namespaces are constructed from the modules
and the moduleroot
--extra extra headers explicitly defined to parse. This must be in
the format "namepsace=/path/to/extra/header.hpp". For example,
the core module requires the extra header:
"core=/opencv/modules/core/include/opencv2/core/core/base.hpp"
--outdir the output directory to put the generated matlab sources. In
the OpenCV build this is "${CMAKE_CURRENT_BUILD_DIR}/src"
"""
# parse the input options
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('--jinja2')
parser.add_argument('--hdrparser')
parser.add_argument('--moduleroot', nargs='*', default=[], required=False)
parser.add_argument('--modules', nargs='*', default=[], required=False)
parser.add_argument('--extra', nargs='*', default=[], required=False)
parser.add_argument('--outdir')
args = parser.parse_args()
# add the hdr_parser and rst_parser modules to the path
sys.path.append(args.jinja2)
sys.path.append(args.hdrparser)
# create the generator
mwg = MatlabWrapperGenerator()
mwg.gen(args.moduleroot, args.modules, args.extra, args.outdir)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment