I wanted to create a wrapper of OpenCV for Node js, I've looked at it
from different angles as I didn't want to write it by hand, since I had an idea
about making it multithreaded and easy to use with typescript bindings.
There's an already existing project called node-opencv
but a lot of its functions are not suitable for multithreading or async
javascript programming, which is what I aimed for.
hdr_parser.py
OpenCV's way of exposing the OpenCV api for python is using hdr_parser.py, which goes over the headers and exports a
list of functions then needed to be made into python code with gen2.py
cv.groupRectangles void
vector_Rect rectList /IO
vector_int weights /O
int groupThreshold
double eps 0.2
const cv.CASCADE_DO_CANNY_PRUNING
1
const cv.CASCADE_SCALE_IMAGE 2
const cv.CASCADE_FIND_BIGGEST_OBJECT
4
const cv.CASCADE_DO_ROUGH_SEARCH 8
class
cv.BaseCascadeClassifier
:
cv::Algorithm
class
cv.CascadeClassifier
cv.CascadeClassifier.CascadeClassifier
cv.CascadeClassifier.CascadeClassifier
String filename
cv.CascadeClassifier.empty bool
cv.CascadeClassifier.load bool
String filename
cv.CascadeClassifier.read bool
FileNode node
cv.CascadeClassifier.detectMultiScale void
Mat image
vector_Rect objects /O
double scaleFactor 1.1
int minNeighbors 3
int flags 0
Size minSize Size()
Size maxSize Size()
Which isn't so much useful if I'd like to do something more complex such
as creating all the functions with their enums as parameters or creating
setters and getters instead of get/set functions, but it could be the way
OpenCV is written.
PEG.JS
So I've decided to try and parse the headers myself, I've found a PEG.js C++ parser written by Pau Fernández
and decided to give it a go, the first execution was terrible, most of the
header files couldn't be parsed and I've spent a few days fixing about 80% of
it to finish parsing, with some days getting so frustrated that I've even tried
to rebuild it myself.
But then I've read somewhere that the problem with C++ parsing
expression grammar is that they lack context, so eventually I had to move on
and try a different approach.
But before I moved on, I ended up with a nice tool to help me work on that grammar
antlr4
In search of a C++ parser, I've met antlr4, it looked nice and it had
many grammars, one of them is C++, while working on that direction, I did not
find it suitable, as the learning curve was stiffer than I wanted to invest at
the moment.
You can compile the grammer to javascript:
antlr4 -Dlanguage=JavaScript CPP14.g4
or if you would like a visitor:
antlr4 -Dlanguage=JavaScript -visitor CPP14.g4
LLVM
LLVM is a compiler, you can dump the AST.
but since it's also directed at full compilation, figuring what you need to
take from it is out of the scope of my needs.
You can dump the ast with this command:
clang -cc1 -ast-dump file.c
castxml
castxml
is a tool that parses C++ files and generates XML AST, before castxml there was GCC-XML, both were written by
Brad King from Kitware who are responsible
for some major open source projects such as VTK, CMake and ITK.
castxml showed the most promise, I got to know it after I saw this brief
presentation
by Matthew MaCormick and looked at the output.
It has llvm includes directory and gave it a shot with this command
arguments:
castxml.exe --castxml-gccxml -o output.xml "header.hpp" -I includefolder
...
and the entire AST was generated and human readable.
After a bit of manipulation with xml2js, I got the following result:
{ id: '_3119',
name: 'Cv_iplAllocateImageData',
type: { id: '_8779', type: [Object], elementtype: 'PointerType' },
context: [Circular],
location: 'f66:1938',
file: 'f66',
line: '1938',
elementtype: 'Typedef' },
{ id: '_3120',
name: 'Cv_iplDeallocate',
type: { id: '_8780', type: [Object], elementtype: 'PointerType' },
context: [Circular],
location: 'f66:1939',
file: 'f66',
line: '1939',
elementtype: 'Typedef' },
from which it was easy to derive:
void filter2D ( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor, double delta, int borderType );
void sepFilter2D ( InputArray src, OutputArray dst, int ddepth, InputArray kernelX, InputArray kernelY, Point anchor, double delta, int borderType );
void Sobel ( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType );
Which was pretty neat!
The best part about it, since it went through the llvm compiler and
preprocessor, the only exported functions, structures and classes were those
that are actually compiled in the real project without any extra work.
please note a few things about castxml:
- it works only with --castxml-gccxml flag, without it, it will compile
but not work.
- llvm relies on file extension to distinguish between c and c++
headers, to force it to use c++ compiler for h extension use the "-c
c++" argument.
- compiling on windows might throw some errors, it could be because ms
vc++ headers rely on a version passed to the compiler, use "-fms-compatibility-version=19.00".
- if you're using exceptions in your code make sure you tell it to llvm
as it has exceptions disabled by default "-fexceptions"
extra bonus: I've created a parser tool to help me out with the xml
file, you can find it here.
swig
Swig actually looks like a perfect tool for this job, it has both V8 and
Node js targets, but I wanted a node js with nan target with async workers and
multithreaded queuing in libuv and safety locks (mutexes), which I'll probably
have to implement into the default swig templates.
At the moment it looks like the best tool for the job but I'm not sure
how easy it's going to be to create nan template over regular macros.
For now I'm exploring a combination of castxml and macros, if you think
there's a better way, I'll be glad to know about it.
0 comments:
Post a Comment