1 ###############################################################################
3 # IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
5 # By downloading, copying, installing or using the software you agree to this license.
6 # If you do not agree to this license, do not download, install,
7 # copy or use the software.
11 # For Open Source Computer Vision Library
13 # Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14 # Third party copyrights are property of their respective owners.
16 # Redistribution and use in source and binary forms, with or without modification,
17 # are permitted provided that the following conditions are met:
19 # * Redistribution's of source code must retain the above copyright notice,
20 # this list of conditions and the following disclaimer.
22 # * Redistribution's in binary form must reproduce the above copyright notice,
23 # this list of conditions and the following disclaimer in the documentation
24 # and/or other materials provided with the distribution.
26 # * The name of the copyright holders may not be used to endorse or promote products
27 # derived from this software without specific prior written permission.
29 # This software is provided by the copyright holders and contributors "as is" and
30 # any express or implied warranties, including, but not limited to, the implied
31 # warranties of merchantability and fitness for a particular purpose are disclaimed.
32 # In no event shall the Intel Corporation or contributors be liable for any direct,
33 # indirect, incidental, special, exemplary, or consequential damages
34 # (including, but not limited to, procurement of substitute goods or services;
35 # loss of use, data, or profits; or business interruption) however caused
36 # and on any theory of liability, whether in contract, strict liability,
37 # or tort (including negligence or otherwise) arising in any way out of
38 # the use of this software, even if advised of the possibility of such damage.
41 ###############################################################################
42 # AUTHOR: Sajjad Taheri, University of California, Irvine. sajjadt[at]uci[dot]edu
45 # Copyright (c) 2015, 2015 The Regents of the University of California (Regents)
47 # Redistribution and use in source and binary forms, with or without
48 # modification, are permitted provided that the following conditions are met:
49 # 1. Redistributions of source code must retain the above copyright
50 # notice, this list of conditions and the following disclaimer.
51 # 2. Redistributions in binary form must reproduce the above copyright
52 # notice, this list of conditions and the following disclaimer in the
53 # documentation and/or other materials provided with the distribution.
54 # 3. Neither the name of the University nor the
55 # names of its contributors may be used to endorse or promote products
56 # derived from this software without specific prior written permission.
58 # THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
59 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
60 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
61 # DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
62 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
63 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
64 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
65 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
66 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
67 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68 ###############################################################################
70 from __future__ import print_function
72 from templates import *
74 if sys.version_info[0] >= 3:
75 from io import StringIO
77 from cStringIO import StringIO
82 # Ignore these functions due to Embind limitations for now
83 ignore_list = ['locate', #int&
84 'minEnclosingCircle', #float&
87 'floodFill', # special case, implemented in core_bindings.cpp
90 'calibrationMatrixValues', #double&
91 'undistortPoints', # global redefinition
96 # Classes and methods whitelist
97 core = {'': ['absdiff', 'add', 'addWeighted', 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor', 'cartToPolar',\
98 'compare', 'convertScaleAbs', 'copyMakeBorder', 'countNonZero', 'determinant', 'dft', 'divide', 'eigen', \
99 'exp', 'flip', 'getOptimalDFTSize','gemm', 'hconcat', 'inRange', 'invert', 'kmeans', 'log', 'magnitude', \
100 'max', 'mean', 'meanStdDev', 'merge', 'min', 'minMaxLoc', 'mixChannels', 'multiply', 'norm', 'normalize', \
101 'perspectiveTransform', 'polarToCart', 'pow', 'randn', 'randu', 'reduce', 'repeat', 'setIdentity', 'setRNGSeed', \
102 'solve', 'solvePoly', 'split', 'sqrt', 'subtract', 'trace', 'transform', 'transpose', 'vconcat'],
105 imgproc = {'': ['Canny', 'GaussianBlur', 'Laplacian', 'HoughLines', 'HoughLinesP', 'HoughCircles', 'Scharr','Sobel', \
106 'adaptiveThreshold','approxPolyDP','arcLength','bilateralFilter','blur','boundingRect','boxFilter',\
107 'calcBackProject','calcHist','circle','compareHist','connectedComponents','connectedComponentsWithStats', \
108 'contourArea', 'convexHull', 'convexityDefects', 'cornerHarris','cornerMinEigenVal','createCLAHE', \
109 'createLineSegmentDetector','cvtColor','demosaicing','dilate', 'distanceTransform','distanceTransformWithLabels', \
110 'drawContours','ellipse','ellipse2Poly','equalizeHist','erode', 'filter2D', 'findContours','fitEllipse', \
111 'fitLine', 'floodFill','getAffineTransform', 'getPerspectiveTransform', 'getRotationMatrix2D', 'getStructuringElement', \
112 'goodFeaturesToTrack','grabCut','initUndistortRectifyMap', 'integral','integral2', 'isContourConvex', 'line', \
113 'matchShapes', 'matchTemplate','medianBlur', 'minAreaRect', 'minEnclosingCircle', 'moments', 'morphologyEx', \
114 'pointPolygonTest', 'putText','pyrDown','pyrUp','rectangle','remap', 'resize','sepFilter2D','threshold', \
115 'undistort','warpAffine','warpPerspective','watershed', \
116 'fillPoly', 'fillConvexPoly'],
117 'CLAHE': ['apply', 'collectGarbage', 'getClipLimit', 'getTilesGridSize', 'setClipLimit', 'setTilesGridSize']}
119 objdetect = {'': ['groupRectangles'],
120 'HOGDescriptor': ['load', 'HOGDescriptor', 'getDefaultPeopleDetector', 'getDaimlerPeopleDetector', 'setSVMDetector', 'detectMultiScale'],
121 'CascadeClassifier': ['load', 'detectMultiScale2', 'CascadeClassifier', 'detectMultiScale3', 'empty', 'detectMultiScale']}
123 video = {'': ['CamShift', 'calcOpticalFlowFarneback', 'calcOpticalFlowPyrLK', 'createBackgroundSubtractorMOG2', \
124 'findTransformECC', 'meanShift'],
125 'BackgroundSubtractorMOG2': ['BackgroundSubtractorMOG2', 'apply'],
126 'BackgroundSubtractor': ['apply', 'getBackgroundImage']}
128 dnn = {'dnn_Net': ['setInput', 'forward'],
129 '': ['readNetFromCaffe', 'readNetFromTensorflow', 'readNetFromTorch', 'readNetFromDarknet',
130 'readNetFromONNX', 'readNet', 'blobFromImage']}
132 features2d = {'Feature2D': ['detect', 'compute', 'detectAndCompute', 'descriptorSize', 'descriptorType', 'defaultNorm', 'empty', 'getDefaultName'],
133 'BRISK': ['create', 'getDefaultName'],
134 'ORB': ['create', 'setMaxFeatures', 'setScaleFactor', 'setNLevels', 'setEdgeThreshold', 'setFirstLevel', 'setWTA_K', 'setScoreType', 'setPatchSize', 'getFastThreshold', 'getDefaultName'],
135 'MSER': ['create', 'detectRegions', 'setDelta', 'getDelta', 'setMinArea', 'getMinArea', 'setMaxArea', 'getMaxArea', 'setPass2Only', 'getPass2Only', 'getDefaultName'],
136 'FastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'],
137 'AgastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'],
138 'GFTTDetector': ['create', 'setMaxFeatures', 'getMaxFeatures', 'setQualityLevel', 'getQualityLevel', 'setMinDistance', 'getMinDistance', 'setBlockSize', 'getBlockSize', 'setHarrisDetector', 'getHarrisDetector', 'setK', 'getK', 'getDefaultName'],
139 # 'SimpleBlobDetector': ['create'],
140 'KAZE': ['create', 'setExtended', 'getExtended', 'setUpright', 'getUpright', 'setThreshold', 'getThreshold', 'setNOctaves', 'getNOctaves', 'setNOctaveLayers', 'getNOctaveLayers', 'setDiffusivity', 'getDiffusivity', 'getDefaultName'],
141 'AKAZE': ['create', 'setDescriptorType', 'getDescriptorType', 'setDescriptorSize', 'getDescriptorSize', 'setDescriptorChannels', 'getDescriptorChannels', 'setThreshold', 'getThreshold', 'setNOctaves', 'getNOctaves', 'setNOctaveLayers', 'getNOctaveLayers', 'setDiffusivity', 'getDiffusivity', 'getDefaultName'],
142 'DescriptorMatcher': ['add', 'clear', 'empty', 'isMaskSupported', 'train', 'match', 'knnMatch', 'radiusMatch', 'clone', 'create'],
143 'BFMatcher': ['isMaskSupported', 'create'],
144 '': ['drawKeypoints', 'drawMatches']}
146 calib3d = {'': ['findHomography']}
148 def makeWhiteList(module_list):
150 for m in module_list:
158 white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, calib3d])
160 # Features to be exported
163 with_wrapped_functions = True
164 with_default_params = True
165 with_vec_from_js_array = True
167 wrapper_namespace = "Wrappers"
169 'InputArray': 'const cv::Mat&',
170 'OutputArray': 'cv::Mat&',
171 'InputOutputArray': 'cv::Mat&',
172 'InputArrayOfArrays': 'const std::vector<cv::Mat>&',
173 'OutputArrayOfArrays': 'std::vector<cv::Mat>&',
174 'String': 'std::string',
175 'const String&':'const std::string&'
178 def normalize_class_name(name):
179 return re.sub(r"^cv\.", "", name).replace(".", "_")
182 class ClassProp(object):
183 def __init__(self, decl):
184 self.tp = decl[0].replace("*", "_ptr").strip()
188 self.readonly = False
191 class ClassInfo(object):
192 def __init__(self, name, decl=None):
193 self.cname = name.replace(".", "::")
194 self.name = self.wname = normalize_class_name(name)
197 self.issimple = False
198 self.isalgorithm = False
200 self.ext_constructors = {}
205 self.constructor_arg_num = set()
207 self.has_smart_ptr = False
210 self.bases = decl[1].split()[1:]
211 if len(self.bases) > 1:
212 self.bases = [self.bases[0].strip(",")]
213 # return sys.exit(-1)
214 if self.bases and self.bases[0].startswith("cv::"):
215 self.bases[0] = self.bases[0][4:]
216 if self.bases and self.bases[0] == "Algorithm":
217 self.isalgorithm = True
219 if m.startswith("="):
226 self.props = [ClassProp(p) for p in decl[3]]
228 if not customname and self.wname.startswith("Cv"):
229 self.wname = self.wname[2:]
233 if tp.startswith('Ptr_'):
234 tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
237 def handle_vector(tp):
238 if tp.startswith('vector_'):
239 tp = handle_vector(tp[tp.find('_') + 1:])
240 tp = 'std::vector<' + "::".join(tp.split('_')) + '>'
244 class ArgInfo(object):
245 def __init__(self, arg_tuple):
246 self.tp = handle_ptr(arg_tuple[0]).strip()
247 self.name = arg_tuple[1]
248 self.defval = arg_tuple[2]
253 self.outputarg = False
254 self.returnarg = False
256 self.reference = False
257 for m in arg_tuple[3]:
259 self.inputarg = False
260 self.outputarg = True
261 self.returnarg = True
264 self.outputarg = True
265 self.returnarg = True
266 elif m.startswith("/A"):
268 self.arraylen = m[2:].strip()
269 elif m.startswith("/CA"):
271 self.arraycvt = m[2:].strip()
275 self.reference = True
280 self.tp = "const cv::Mat&"
281 if self.tp == "vector_Mat":
283 self.tp = "std::vector<cv::Mat>&"
285 self.tp = "const std::vector<cv::Mat>&"
286 self.tp = handle_vector(self.tp).strip()
288 self.tp = "const " + self.tp
290 self.tp = self.tp + "&"
291 self.py_inputarg = False
292 self.py_outputarg = False
294 class FuncVariant(object):
295 def __init__(self, class_name, name, decl, is_constructor, is_class_method, is_const, is_virtual, is_pure_virtual, ref_return, const_return):
296 self.class_name = class_name
297 self.name = self.wname = name
298 self.is_constructor = is_constructor
299 self.is_class_method = is_class_method
300 self.is_const = is_const
301 self.is_virtual = is_virtual
302 self.is_pure_virtual = is_pure_virtual
303 self.refret = ref_return
304 self.constret = const_return
305 self.rettype = handle_vector(handle_ptr(decl[1]).strip()).strip()
306 if self.rettype == "void":
309 self.array_counters = {}
313 if ainfo.isarray and not ainfo.arraycvt:
315 c_arrlist = self.array_counters.get(c, [])
317 c_arrlist.append(ainfo.name)
319 self.array_counters[c] = [ainfo.name]
320 self.args.append(ainfo)
323 class FuncInfo(object):
324 def __init__(self, class_name, name, cname, namespace, isconstructor):
325 self.class_name = class_name
328 self.namespace = namespace
330 self.is_constructor = isconstructor
332 def add_variant(self, variant):
333 self.variants.append(variant)
336 class Namespace(object):
343 class JSWrapperGenerator(object):
347 self.wrapper_funcs = []
353 self.parser = hdr_parser.CppHeaderParser()
356 def add_class(self, stype, name, decl):
357 class_info = ClassInfo(name, decl)
358 class_info.decl_idx = self.class_idx
361 if class_info.name in self.classes:
362 print("Generator error: class %s (cpp_name=%s) already exists" \
363 % (class_info.name, class_info.cname))
365 self.classes[class_info.name] = class_info
368 chunks = class_info.bases[0].split('::')
369 base = '_'.join(chunks)
370 while base not in self.classes and len(chunks) > 1:
372 base = '_'.join(chunks)
373 if base not in self.classes:
374 print("Generator error: unable to resolve base %s for %s"
375 % (class_info.bases[0], class_info.name))
378 class_info.bases[0] = "::".join(chunks)
379 class_info.isalgorithm |= self.classes[base].isalgorithm
381 def split_decl_name(self, name):
382 chunks = name.split('.')
383 namespace = chunks[:-1]
385 while namespace and '.'.join(namespace) not in self.parser.namespaces:
386 classes.insert(0, namespace.pop())
387 return namespace, classes, chunks[-1]
389 def add_enum(self, decl):
390 name = decl[0].rsplit(" ", 1)[1]
391 namespace, classes, val = self.split_decl_name(name)
392 namespace = '.'.join(namespace)
393 ns = self.namespaces.setdefault(namespace, Namespace())
394 if len(name) == 0: name = "<unnamed>"
395 if name.endswith("<unnamed>"):
399 candidate_name = name.replace("<unnamed>", "unnamed_%u" % i)
400 if candidate_name not in ns.enums:
401 name = candidate_name
403 cname = name.replace('.', '::')
404 type_dict[normalize_class_name(name)] = cname
406 print("Generator warning: enum %s (cname=%s) already exists" \
412 ns.enums[name].append(item)
414 const_decls = decl[3]
416 for decl in const_decls:
418 self.add_const(name.replace("const ", "").strip(), decl)
420 def add_const(self, name, decl):
421 cname = name.replace('.','::')
422 namespace, classes, name = self.split_decl_name(name)
423 namespace = '.'.join(namespace)
424 name = '_'.join(classes+[name])
425 ns = self.namespaces.setdefault(namespace, Namespace())
426 if name in ns.consts:
427 print("Generator error: constant %s (cname=%s) already exists" \
430 ns.consts[name] = cname
432 def add_func(self, decl):
433 namespace, classes, barename = self.split_decl_name(decl[0])
434 cpp_name = "::".join(namespace + classes + [barename])
439 class_name = normalize_class_name('.'.join(namespace + classes))
440 bare_class_name = classes[-1]
441 namespace = '.'.join(namespace)
443 is_constructor = name == bare_class_name
444 is_class_method = False
445 is_const_method = False
446 is_virtual_method = False
447 is_pure_virtual_method = False
453 is_class_method = True
455 is_const_method = True
457 is_virtual_method = True
459 is_pure_virtual_method = True
464 elif m.startswith("="):
469 func_map = self.classes[class_name].methods
471 func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
473 func = func_map.setdefault(name, FuncInfo(class_name, name, cpp_name, namespace, is_constructor))
475 variant = FuncVariant(class_name, name, decl, is_constructor, is_class_method, is_const_method,
476 is_virtual_method, is_pure_virtual_method, ref_return, const_return)
477 func.add_variant(variant)
479 def save(self, path, name, buf):
480 f = open(path + "/" + name, "wt")
481 f.write(buf.getvalue())
484 def gen_function_binding_with_wrapper(self, func, class_info):
487 wrapper_func_text = None
492 for index, variant in enumerate(func.variants):
495 if class_info and 'Ptr<' in variant.rettype:
498 base_class_name = variant.rettype
499 base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
500 if base_class_name in self.classes:
501 self.classes[base_class_name].has_smart_ptr = True
503 print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
504 self.classes[class_info.name].has_smart_ptr = True
507 has_def_param = False
510 ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
511 if ret_type.startswith('Ptr'): #smart pointer
512 ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
513 if ptr_type in type_dict:
514 ret_type = type_dict[ptr_type]
515 for key in type_dict:
517 ret_type = ret_type.replace(key, type_dict[key])
520 unwrapped_arg_types = []
521 for arg in variant.args:
523 if arg.tp in type_dict:
524 arg_type = type_dict[arg.tp]
528 if with_default_params and arg.defval != '':
529 def_args.append(arg.defval);
530 arg_types.append(arg_type)
531 unwrapped_arg_types.append(arg_type)
535 if '*' in ''.join(arg_types):
536 func_attribs += ', allow_raw_pointers()'
538 if variant.is_pure_virtual:
539 func_attribs += ', pure_virtual()'
543 wrap_func_name = (func.class_name+"_" if class_info != None else "") + func.name.split("::")[-1] + "_wrapper"
544 js_func_name = func.name
546 # TODO: Name functions based wrap directives or based on arguments list
548 wrap_func_name += str(index)
549 js_func_name += str(index)
551 c_func_name = 'Wrappers::' + wrap_func_name
554 raw_arg_names = ['arg' + str(i + 1) for i in range(0, len(variant.args))]
557 casted_arg_types = []
558 for arg_type, arg_name in zip(arg_types, raw_arg_names):
559 casted_arg_name = arg_name
560 if with_vec_from_js_array:
561 # Only support const vector reference as input parameter
562 match = re.search(r'const std::vector<(.*)>&', arg_type)
564 type_in_vect = match.group(1)
565 if type_in_vect != 'cv::Mat':
566 casted_arg_name = 'emscripten::vecFromJSArray<' + type_in_vect + '>(' + arg_name + ')'
567 arg_type = re.sub(r'std::vector<(.*)>', 'emscripten::val', arg_type)
568 w_signature.append(arg_type + ' ' + arg_name)
569 arg_names.append(casted_arg_name)
570 casted_arg_types.append(arg_type)
572 arg_types = casted_arg_types
574 # Argument list, signature
575 arg_names_casted = [c if a == b else c + '.as<' + a + '>()' for a, b, c in
576 zip(unwrapped_arg_types, arg_types, arg_names)]
578 # Add self object to the parameters
579 if class_info and not factory:
580 arg_types = [class_info.cname + '&'] + arg_types
581 w_signature = [class_info.cname + '& arg0 '] + w_signature
583 for j in range(0, len(def_args) + 1):
586 postfix = '_' + str(j);
588 ###################################
590 if factory: # TODO or static
591 name = class_info.cname+'::' if variant.class_name else ""
592 cpp_call_text = static_class_call_template.substitute(scope=name,
594 args=', '.join(arg_names[:len(arg_names)-j]))
596 cpp_call_text = class_call_template.substitute(obj='arg0',
598 args=', '.join(arg_names[:len(arg_names)-j]))
600 cpp_call_text = call_template.substitute(func=func.cname,
601 args=', '.join(arg_names[:len(arg_names)-j]))
604 wrapper_func_text = wrapper_function_template.substitute(ret_val=ret_type,
605 func=wrap_func_name+postfix,
606 signature=', '.join(w_signature[:len(w_signature)-j]),
607 cpp_call=cpp_call_text,
608 const='' if variant.is_const else '')
610 ###################################
614 # print("Factory Function: ", c_func_name, len(variant.args) - j, class_info.name)
615 if variant.is_pure_virtual:
616 # FIXME: workaround for pure virtual in constructor
617 # e.g. DescriptorMatcher_clone_wrapper
619 # consider the default parameter variants
620 args_num = len(variant.args) - j
621 if args_num in class_info.constructor_arg_num:
622 # FIXME: workaournd for constructor overload with same args number
623 # e.g. DescriptorMatcher
625 class_info.constructor_arg_num.add(args_num)
626 binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
627 cpp_name=c_func_name+postfix,
629 args=','.join(arg_types[:len(arg_types)-j]),
630 optional=func_attribs)
632 binding_template = overload_class_static_function_template if variant.is_class_method else \
633 overload_class_function_template
634 binding_text = binding_template.substitute(js_name=js_func_name,
635 const='' if variant.is_const else '',
636 cpp_name=c_func_name+postfix,
638 args=','.join(arg_types[:len(arg_types)-j]),
639 optional=func_attribs)
641 binding_text = overload_function_template.substitute(js_name=js_func_name,
642 cpp_name=c_func_name+postfix,
643 const='const' if variant.is_const else '',
645 args=', '.join(arg_types[:len(arg_types)-j]),
646 optional=func_attribs)
648 bindings.append(binding_text)
649 wrappers.append(wrapper_func_text)
651 return [bindings, wrappers]
654 def gen_function_binding(self, func, class_info):
656 if not class_info == None :
657 func_name = class_info.cname+'::'+func.cname
659 func_name = func.cname
662 binding_text_list = []
664 for index, variant in enumerate(func.variants):
666 #TODO if variant.is_class_method and variant.rettype == ('Ptr<' + class_info.name + '>'):
667 if (not class_info == None) and variant.rettype == ('Ptr<' + class_info.name + '>') or (func.name.startswith("create") and variant.rettype):
669 base_class_name = variant.rettype
670 base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
671 if base_class_name in self.classes:
672 self.classes[base_class_name].has_smart_ptr = True
674 print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
675 self.classes[class_info.name].has_smart_ptr = True
679 ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
681 ret_type = ret_type.strip()
683 if ret_type.startswith('Ptr'): #smart pointer
684 ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
685 if ptr_type in type_dict:
686 ret_type = type_dict[ptr_type]
687 for key in type_dict:
689 ret_type = ret_type.replace(key, type_dict[key])
691 if variant.constret and ret_type.startswith('const') == False:
692 ret_type = 'const ' + ret_type
693 if variant.refret and ret_type.endswith('&') == False:
699 for arg in variant.args:
700 if arg.tp in type_dict:
701 arg_type = type_dict[arg.tp]
707 orig_arg_types.append(arg_type)
708 if with_default_params and arg.defval != '':
709 def_args.append(arg.defval)
710 arg_types.append(orig_arg_types[-1])
714 if '*' in ''.join(orig_arg_types):
715 func_attribs += ', allow_raw_pointers()'
717 if variant.is_pure_virtual:
718 func_attribs += ', pure_virtual()'
721 #if variant.name in self.jsfunctions:
723 js_func_name = variant.name
726 c_func_name = func.cname if (factory and variant.is_class_method == False) else func_name
729 ################################### Binding
730 for j in range(0, len(def_args) + 1):
733 postfix = '_' + str(j);
735 binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
736 cpp_name=c_func_name+postfix,
738 args=','.join(arg_types[:len(arg_types)-j]),
739 optional=func_attribs)
741 binding_template = overload_class_static_function_template if variant.is_class_method else \
742 overload_function_template if class_info == None else overload_class_function_template
743 binding_text = binding_template.substitute(js_name=js_func_name,
744 const='const' if variant.is_const else '',
745 cpp_name=c_func_name+postfix,
747 args=','.join(arg_types[:len(arg_types)-1]),
748 optional=func_attribs)
750 binding_text_list.append(binding_text)
752 return binding_text_list
754 def print_decls(self, decls):
756 Prints the list of declarations, retrieived by the parse() method
759 print(d[0], d[1], ";".join(d[2]))
761 print(" ", a[0], a[1], a[2], end="")
763 print("; ".join(a[3]))
767 def gen(self, dst_file, src_files, core_bindings):
768 # step 1: scan the headers and extract classes, enums and functions
770 for hdr in src_files:
771 decls = self.parser.parse(hdr)
773 # self.print_decls(decls);
776 headers.append(hdr[hdr.rindex('opencv2/'):])
779 type = name[:name.find(" ")]
780 if type == "struct" or type == "class": # class/structure case
781 name = name[name.find(" ") + 1:].strip()
782 self.add_class(type, name, decl)
783 elif name.startswith("enum"): # enumerations
785 elif name.startswith("const"):
787 self.add_const(name.replace("const ", "").strip(), decl)
788 else: # class/global function
791 # step 2: generate bindings
793 for ns_name, ns in sorted(self.namespaces.items()):
794 if ns_name.split('.')[0] != 'cv':
796 for name, func in sorted(ns.funcs.items()):
797 if name in ignore_list:
799 if not name in white_list['']:
803 # Check if the method is an external constructor
804 for variant in func.variants:
805 if "Ptr<" in variant.rettype:
807 # Register the smart pointer
808 base_class_name = variant.rettype
809 base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
810 self.classes[base_class_name].has_smart_ptr = True
812 # Adds the external constructor
813 class_name = func.name.replace("create", "")
814 if not class_name in self.classes:
815 self.classes[base_class_name].methods[func.cname] = func
817 self.classes[class_name].methods[func.cname] = func
822 if with_wrapped_functions:
823 binding, wrapper = self.gen_function_binding_with_wrapper(func, class_info=None)
824 self.bindings += binding
825 self.wrapper_funcs += wrapper
827 binding = self.gen_function_binding(func, class_info=None)
828 self.bindings+=binding
830 # generate code for the classes and their methods
831 class_list = list(self.classes.items())
833 for name, class_info in class_list:
835 if not name in white_list:
838 # Generate bindings for methods
839 for method_name, method in class_info.methods.items():
840 if method.cname in ignore_list:
842 if not method.name in white_list[method.class_name]:
844 if method.is_constructor:
845 for variant in method.variants:
847 for arg in variant.args:
848 arg_type = type_dict[arg.tp] if arg.tp in type_dict else arg.tp
849 args.append(arg_type)
850 # print('Constructor: ', class_info.name, len(variant.args))
851 args_num = len(variant.args)
852 if args_num in class_info.constructor_arg_num:
854 class_info.constructor_arg_num.add(args_num)
855 class_bindings.append(constructor_template.substitute(signature=', '.join(args)))
857 if with_wrapped_functions and (len(method.variants) > 1 or len(method.variants[0].args)>0 or "String" in method.variants[0].rettype):
858 binding, wrapper = self.gen_function_binding_with_wrapper(method, class_info=class_info)
859 self.wrapper_funcs = self.wrapper_funcs + wrapper
860 class_bindings = class_bindings + binding
862 binding = self.gen_function_binding(method, class_info=class_info)
863 class_bindings = class_bindings + binding
865 # Regiseter Smart pointer
866 if class_info.has_smart_ptr:
867 class_bindings.append(smart_ptr_reg_template.substitute(cname=class_info.cname, name=class_info.name))
869 # Attach external constructors
870 # for method_name, method in class_info.ext_constructors.items():
871 # print("ext constructor", method_name)
872 #if class_info.ext_constructors:
876 # Generate bindings for properties
877 for property in class_info.props:
878 _class_property = class_property_enum_template if property.tp in type_dict else class_property_template
879 class_bindings.append(_class_property.substitute(js_name=property.name, cpp_name='::'.join(
880 [class_info.cname, property.name])))
883 base = Template("""base<$base>""")
885 assert len(class_info.bases) <= 1 , "multiple inheritance not supported"
887 if len(class_info.bases) == 1:
888 dv = "," + base.substitute(base=', '.join(class_info.bases))
890 self.bindings.append(class_template.substitute(cpp_name=class_info.cname,
892 class_templates=''.join(class_bindings),
896 # step 4: generate bindings for enums
897 # TODO anonymous enums are ignored for now.
898 for ns_name, ns in sorted(self.namespaces.items()):
899 if ns_name.split('.')[0] != 'cv':
901 for name, enum in sorted(ns.enums.items()):
902 if not name.endswith('.anonymous'):
903 name = name.replace("cv.", "")
905 for enum_val in enum:
906 value = enum_val[0][enum_val[0].rfind(".")+1:]
907 enum_values.append(enum_item_template.substitute(val=value,
908 cpp_val=name.replace('.', '::')+'::'+value))
910 self.bindings.append(enum_template.substitute(cpp_name=name.replace(".", "::"),
911 js_name=name.replace(".", "_"),
912 enum_items=''.join(enum_values)))
915 #TODO: represent anonymous enums with constants
918 # step 5: generate bindings for consts
919 for ns_name, ns in sorted(self.namespaces.items()):
920 if ns_name.split('.')[0] != 'cv':
922 for name, const in sorted(ns.consts.items()):
923 # print("Gen consts: ", name, const)
924 self.bindings.append(const_template.substitute(js_name=name, value=const))
926 with open(core_bindings) as f:
929 header_includes = '\n'.join(['#include "{}"'.format(hdr) for hdr in headers])
930 ret = ret.replace('@INCLUDES@', header_includes)
932 defis = '\n'.join(self.wrapper_funcs)
933 ret += wrapper_codes_template.substitute(ns=wrapper_namespace, defs=defis)
934 ret += emscripten_binding_template.substitute(binding_name='testBinding', bindings=''.join(self.bindings))
938 text_file = open(dst_file, "w")
943 if __name__ == "__main__":
944 if len(sys.argv) < 4:
946 os.path.basename(sys.argv[0]), \
947 "<full path to hdr_parser.py> <bindings.cpp> <headers.txt> <core_bindings.cpp>")
948 print("Current args are: ", ", ".join(["'"+a+"'" for a in sys.argv]))
952 hdr_parser_path = os.path.abspath(sys.argv[1])
953 if hdr_parser_path.endswith(".py"):
954 hdr_parser_path = os.path.dirname(hdr_parser_path)
955 sys.path.append(hdr_parser_path)
958 bindingsCpp = sys.argv[2]
959 headers = open(sys.argv[3], 'r').read().split(';')
960 coreBindings = sys.argv[4]
961 generator = JSWrapperGenerator()
962 generator.gen(bindingsCpp, headers, coreBindings)