Merge pull request #14849 from thangktran:thangktran/feature/fillPoly_and_fillConvexP...
[platform/upstream/opencv.git] / modules / js / src / embindgen.py
1 ###############################################################################
2 #
3 #  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 #
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.
8 #
9 #
10 #                           License Agreement
11 #                For Open Source Computer Vision Library
12 #
13 # Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14 # Third party copyrights are property of their respective owners.
15 #
16 # Redistribution and use in source and binary forms, with or without modification,
17 # are permitted provided that the following conditions are met:
18 #
19 #   * Redistribution's of source code must retain the above copyright notice,
20 #     this list of conditions and the following disclaimer.
21 #
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.
25 #
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.
28 #
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.
39 #
40
41 ###############################################################################
42 # AUTHOR: Sajjad Taheri, University of California, Irvine. sajjadt[at]uci[dot]edu
43 #
44 #                             LICENSE AGREEMENT
45 # Copyright (c) 2015, 2015 The Regents of the University of California (Regents)
46 #
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.
57 #
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 ###############################################################################
69
70 from __future__ import print_function
71 import sys, re, os
72 from templates import *
73
74 if sys.version_info[0] >= 3:
75     from io import StringIO
76 else:
77     from cStringIO import StringIO
78
79
80 func_table = {}
81
82 # Ignore these functions due to Embind limitations for now
83 ignore_list = ['locate',  #int&
84                'minEnclosingCircle',  #float&
85                'checkRange',
86                'minMaxLoc',   #double*
87                'floodFill', # special case, implemented in core_bindings.cpp
88                'phaseCorrelate',
89                'randShuffle',
90                'calibrationMatrixValues', #double&
91                'undistortPoints', # global redefinition
92                'CamShift', #Rect&
93                'meanShift' #Rect&
94                ]
95
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'],
103         'Algorithm': []}
104
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']}
118
119 objdetect = {'': ['groupRectangles'],
120              'HOGDescriptor': ['load', 'HOGDescriptor', 'getDefaultPeopleDetector', 'getDaimlerPeopleDetector', 'setSVMDetector', 'detectMultiScale'],
121              'CascadeClassifier': ['load', 'detectMultiScale2', 'CascadeClassifier', 'detectMultiScale3', 'empty', 'detectMultiScale']}
122
123 video = {'': ['CamShift', 'calcOpticalFlowFarneback', 'calcOpticalFlowPyrLK', 'createBackgroundSubtractorMOG2', \
124              'findTransformECC', 'meanShift'],
125          'BackgroundSubtractorMOG2': ['BackgroundSubtractorMOG2', 'apply'],
126          'BackgroundSubtractor': ['apply', 'getBackgroundImage']}
127
128 dnn = {'dnn_Net': ['setInput', 'forward'],
129        '': ['readNetFromCaffe', 'readNetFromTensorflow', 'readNetFromTorch', 'readNetFromDarknet',
130             'readNetFromONNX', 'readNet', 'blobFromImage']}
131
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']}
145
146 calib3d = {'': ['findHomography']}
147
148 def makeWhiteList(module_list):
149     wl = {}
150     for m in module_list:
151         for k in m.keys():
152             if k in wl:
153                 wl[k] += m[k]
154             else:
155                 wl[k] = m[k]
156     return wl
157
158 white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, calib3d])
159
160 # Features to be exported
161 export_enums = False
162 export_consts = True
163 with_wrapped_functions = True
164 with_default_params = True
165 with_vec_from_js_array = True
166
167 wrapper_namespace = "Wrappers"
168 type_dict = {
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&'
176 }
177
178 def normalize_class_name(name):
179     return re.sub(r"^cv\.", "", name).replace(".", "_")
180
181
182 class ClassProp(object):
183     def __init__(self, decl):
184         self.tp = decl[0].replace("*", "_ptr").strip()
185         self.name = decl[1]
186         self.readonly = True
187         if "/RW" in decl[3]:
188             self.readonly = False
189
190
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)
195
196         self.ismap = False
197         self.issimple = False
198         self.isalgorithm = False
199         self.methods = {}
200         self.ext_constructors = {}
201         self.props = []
202         self.consts = {}
203         customname = False
204         self.jsfuncs = {}
205         self.constructor_arg_num = set()
206
207         self.has_smart_ptr = False
208
209         if decl:
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
218             for m in decl[2]:
219                 if m.startswith("="):
220                     self.wname = m[1:]
221                     customname = True
222                 elif m == "/Map":
223                     self.ismap = True
224                 elif m == "/Simple":
225                     self.issimple = True
226             self.props = [ClassProp(p) for p in decl[3]]
227
228         if not customname and self.wname.startswith("Cv"):
229             self.wname = self.wname[2:]
230
231
232 def handle_ptr(tp):
233     if tp.startswith('Ptr_'):
234         tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
235     return tp
236
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('_')) + '>'
241     return tp
242
243
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]
249         self.isarray = False
250         self.arraylen = 0
251         self.arraycvt = None
252         self.inputarg = True
253         self.outputarg = False
254         self.returnarg = False
255         self.const = False
256         self.reference = False
257         for m in arg_tuple[3]:
258             if m == "/O":
259                 self.inputarg = False
260                 self.outputarg = True
261                 self.returnarg = True
262             elif m == "/IO":
263                 self.inputarg = True
264                 self.outputarg = True
265                 self.returnarg = True
266             elif m.startswith("/A"):
267                 self.isarray = True
268                 self.arraylen = m[2:].strip()
269             elif m.startswith("/CA"):
270                 self.isarray = True
271                 self.arraycvt = m[2:].strip()
272             elif m == "/C":
273                 self.const = True
274             elif m == "/Ref":
275                 self.reference = True
276         if self.tp == "Mat":
277             if self.outputarg:
278                 self.tp = "cv::Mat&"
279             elif self.inputarg:
280                 self.tp = "const cv::Mat&"
281         if self.tp == "vector_Mat":
282             if self.outputarg:
283                 self.tp = "std::vector<cv::Mat>&"
284             elif self.inputarg:
285                 self.tp = "const std::vector<cv::Mat>&"
286         self.tp = handle_vector(self.tp).strip()
287         if self.const:
288             self.tp = "const " + self.tp
289         if self.reference:
290             self.tp = self.tp + "&"
291         self.py_inputarg = False
292         self.py_outputarg = False
293
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":
307             self.rettype = ""
308         self.args = []
309         self.array_counters = {}
310
311         for a in decl[3]:
312             ainfo = ArgInfo(a)
313             if ainfo.isarray and not ainfo.arraycvt:
314                 c = ainfo.arraylen
315                 c_arrlist = self.array_counters.get(c, [])
316                 if c_arrlist:
317                     c_arrlist.append(ainfo.name)
318                 else:
319                     self.array_counters[c] = [ainfo.name]
320             self.args.append(ainfo)
321
322
323 class FuncInfo(object):
324     def __init__(self, class_name, name, cname, namespace, isconstructor):
325         self.class_name = class_name
326         self.name = name
327         self.cname = cname
328         self.namespace = namespace
329         self.variants = []
330         self.is_constructor = isconstructor
331
332     def add_variant(self, variant):
333         self.variants.append(variant)
334
335
336 class Namespace(object):
337     def __init__(self):
338         self.funcs = {}
339         self.enums = {}
340         self.consts = {}
341
342
343 class JSWrapperGenerator(object):
344     def __init__(self):
345
346         self.bindings = []
347         self.wrapper_funcs = []
348
349         self.classes = {}
350         self.namespaces = {}
351         self.enums = {}
352
353         self.parser = hdr_parser.CppHeaderParser()
354         self.class_idx = 0
355
356     def add_class(self, stype, name, decl):
357         class_info = ClassInfo(name, decl)
358         class_info.decl_idx = self.class_idx
359         self.class_idx += 1
360
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))
364             sys.exit(-1)
365         self.classes[class_info.name] = class_info
366
367         if class_info.bases:
368             chunks = class_info.bases[0].split('::')
369             base = '_'.join(chunks)
370             while base not in self.classes and len(chunks) > 1:
371                 del chunks[-2]
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))
376                 sys.exit(-1)
377             else:
378                 class_info.bases[0] = "::".join(chunks)
379                 class_info.isalgorithm |= self.classes[base].isalgorithm
380
381     def split_decl_name(self, name):
382         chunks = name.split('.')
383         namespace = chunks[:-1]
384         classes = []
385         while namespace and '.'.join(namespace) not in self.parser.namespaces:
386             classes.insert(0, namespace.pop())
387         return namespace, classes, chunks[-1]
388
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>"):
396             i = 0
397             while True:
398                 i += 1
399                 candidate_name = name.replace("<unnamed>", "unnamed_%u" % i)
400                 if candidate_name not in ns.enums:
401                     name = candidate_name
402                     break;
403         cname = name.replace('.', '::')
404         type_dict[normalize_class_name(name)] = cname
405         if name in ns.enums:
406             print("Generator warning: enum %s (cname=%s) already exists" \
407                   % (name, cname))
408             # sys.exit(-1)
409         else:
410             ns.enums[name] = []
411         for item in decl[3]:
412             ns.enums[name].append(item)
413
414         const_decls = decl[3]
415
416         for decl in const_decls:
417             name = decl[0]
418             self.add_const(name.replace("const ", "").strip(), decl)
419
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" \
428                 % (name, cname))
429             sys.exit(-1)
430         ns.consts[name] = cname
431
432     def add_func(self, decl):
433         namespace, classes, barename = self.split_decl_name(decl[0])
434         cpp_name = "::".join(namespace + classes + [barename])
435         name = barename
436         class_name = ''
437         bare_class_name = ''
438         if classes:
439             class_name = normalize_class_name('.'.join(namespace + classes))
440             bare_class_name = classes[-1]
441         namespace = '.'.join(namespace)
442
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
448         const_return = False
449         ref_return = False
450
451         for m in decl[2]:
452             if m == "/S":
453                 is_class_method = True
454             elif m == "/C":
455                 is_const_method = True
456             elif m == "/V":
457                 is_virtual_method = True
458             elif m == "/PV":
459                 is_pure_virtual_method = True
460             elif m == "/Ref":
461                 ref_return = True
462             elif m == "/CRet":
463                 const_return = True
464             elif m.startswith("="):
465                 name = m[1:]
466
467         if class_name:
468             cpp_name = barename
469             func_map = self.classes[class_name].methods
470         else:
471             func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
472
473         func = func_map.setdefault(name, FuncInfo(class_name, name, cpp_name, namespace, is_constructor))
474
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)
478
479     def save(self, path, name, buf):
480         f = open(path + "/" + name, "wt")
481         f.write(buf.getvalue())
482         f.close()
483
484     def gen_function_binding_with_wrapper(self, func, class_info):
485
486         binding_text = None
487         wrapper_func_text = None
488
489         bindings = []
490         wrappers = []
491
492         for index, variant in enumerate(func.variants):
493
494             factory = False
495             if class_info and 'Ptr<' in variant.rettype:
496
497                 factory = True
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
502                 else:
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
505
506             def_args = []
507             has_def_param = False
508
509             # Return type
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:
516                 if key in ret_type:
517                     ret_type = ret_type.replace(key, type_dict[key])
518
519             arg_types = []
520             unwrapped_arg_types = []
521             for arg in variant.args:
522                 arg_type = None
523                 if arg.tp in type_dict:
524                     arg_type = type_dict[arg.tp]
525                 else:
526                     arg_type = arg.tp
527                 # Add default value
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)
532
533             # Function attribure
534             func_attribs = ''
535             if '*' in ''.join(arg_types):
536                 func_attribs += ', allow_raw_pointers()'
537
538             if variant.is_pure_virtual:
539                 func_attribs += ', pure_virtual()'
540
541
542             # Wrapper function
543             wrap_func_name = (func.class_name+"_" if class_info != None else "") + func.name.split("::")[-1] + "_wrapper"
544             js_func_name = func.name
545
546             # TODO: Name functions based wrap directives or based on arguments list
547             if index > 0:
548                 wrap_func_name += str(index)
549                 js_func_name += str(index)
550
551             c_func_name = 'Wrappers::' + wrap_func_name
552
553             # Binding template-
554             raw_arg_names = ['arg' + str(i + 1) for i in range(0, len(variant.args))]
555             arg_names = []
556             w_signature = []
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)
563                     if match:
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)
571
572             arg_types = casted_arg_types
573
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)]
577
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
582
583             for j in range(0, len(def_args) + 1):
584                 postfix = ''
585                 if j > 0:
586                     postfix = '_' + str(j);
587
588                 ###################################
589                 # Wrapper
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,
593                                                                    func=func.cname,
594                                                                    args=', '.join(arg_names[:len(arg_names)-j]))
595                 elif class_info:
596                     cpp_call_text = class_call_template.substitute(obj='arg0',
597                                                                    func=func.cname,
598                                                                    args=', '.join(arg_names[:len(arg_names)-j]))
599                 else:
600                     cpp_call_text = call_template.substitute(func=func.cname,
601                                                              args=', '.join(arg_names[:len(arg_names)-j]))
602
603
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 '')
609
610                 ###################################
611                 # Binding
612                 if class_info:
613                     if factory:
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
618                             continue
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
624                             continue
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,
628                                                            ret=ret_type,
629                                                            args=','.join(arg_types[:len(arg_types)-j]),
630                                                            optional=func_attribs)
631                     else:
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,
637                                                            ret=ret_type,
638                                                            args=','.join(arg_types[:len(arg_types)-j]),
639                                                            optional=func_attribs)
640                 else:
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 '',
644                                                        ret=ret_type,
645                                                        args=', '.join(arg_types[:len(arg_types)-j]),
646                                                        optional=func_attribs)
647
648                 bindings.append(binding_text)
649                 wrappers.append(wrapper_func_text)
650
651         return [bindings, wrappers]
652
653
654     def gen_function_binding(self, func, class_info):
655
656         if not class_info == None :
657             func_name = class_info.cname+'::'+func.cname
658         else :
659             func_name = func.cname
660
661         binding_text = None
662         binding_text_list = []
663
664         for index, variant in enumerate(func.variants):
665             factory = False
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):
668                 factory = True
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
673                 else:
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
676
677
678             # Return type
679             ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
680
681             ret_type = ret_type.strip()
682
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:
688                 if key in ret_type:
689                     ret_type = ret_type.replace(key, type_dict[key])
690
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:
694                 ret_type += '&'
695
696             arg_types = []
697             orig_arg_types = []
698             def_args = []
699             for arg in variant.args:
700                 if arg.tp in type_dict:
701                     arg_type = type_dict[arg.tp]
702                 else:
703                     arg_type = arg.tp
704
705                 #if arg.outputarg:
706                 #    arg_type += '&'
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])
711
712             # Function attribure
713             func_attribs = ''
714             if '*' in ''.join(orig_arg_types):
715                 func_attribs += ', allow_raw_pointers()'
716
717             if variant.is_pure_virtual:
718                 func_attribs += ', pure_virtual()'
719
720             #TODO better naming
721             #if variant.name in self.jsfunctions:
722             #else
723             js_func_name = variant.name
724
725
726             c_func_name = func.cname if (factory and variant.is_class_method == False) else func_name
727
728
729             ################################### Binding
730             for j in range(0, len(def_args) + 1):
731                 postfix = ''
732                 if j > 0:
733                     postfix = '_' + str(j);
734                 if factory:
735                     binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
736                                                            cpp_name=c_func_name+postfix,
737                                                            ret=ret_type,
738                                                            args=','.join(arg_types[:len(arg_types)-j]),
739                                                            optional=func_attribs)
740                 else:
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,
746                                                                ret=ret_type,
747                                                                args=','.join(arg_types[:len(arg_types)-1]),
748                                                                optional=func_attribs)
749
750                 binding_text_list.append(binding_text)
751
752         return binding_text_list
753
754     def print_decls(self, decls):
755         """
756         Prints the list of declarations, retrieived by the parse() method
757         """
758         for d in decls:
759             print(d[0], d[1], ";".join(d[2]))
760             for a in d[3]:
761                 print("   ", a[0], a[1], a[2], end="")
762                 if a[3]:
763                     print("; ".join(a[3]))
764                 else:
765                     print()
766
767     def gen(self, dst_file, src_files, core_bindings):
768         # step 1: scan the headers and extract classes, enums and functions
769         headers = []
770         for hdr in src_files:
771             decls = self.parser.parse(hdr)
772             # print(hdr);
773             # self.print_decls(decls);
774             if len(decls) == 0:
775                 continue
776             headers.append(hdr[hdr.rindex('opencv2/'):])
777             for decl in decls:
778                 name = decl[0]
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
784                     self.add_enum(decl)
785                 elif name.startswith("const"):
786                     # constant
787                     self.add_const(name.replace("const ", "").strip(), decl)
788                 else:  # class/global function
789                     self.add_func(decl)
790
791         # step 2: generate bindings
792         # Global functions
793         for ns_name, ns in sorted(self.namespaces.items()):
794             if ns_name.split('.')[0] != 'cv':
795                 continue
796             for name, func in sorted(ns.funcs.items()):
797                 if name in ignore_list:
798                     continue
799                 if not name in white_list['']:
800                     continue
801
802                 ext_cnst = False
803                 # Check if the method is an external constructor
804                 for variant in func.variants:
805                     if "Ptr<" in variant.rettype:
806
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
811
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
816                         else:
817                             self.classes[class_name].methods[func.cname] = func
818                         ext_cnst = True
819                 if ext_cnst:
820                     continue
821
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
826                 else:
827                     binding = self.gen_function_binding(func, class_info=None)
828                     self.bindings+=binding
829
830         # generate code for the classes and their methods
831         class_list = list(self.classes.items())
832
833         for name, class_info in class_list:
834             class_bindings = []
835             if not name in white_list:
836                 continue
837
838             # Generate bindings for methods
839             for method_name, method in class_info.methods.items():
840                 if method.cname in ignore_list:
841                     continue
842                 if not method.name in white_list[method.class_name]:
843                     continue
844                 if method.is_constructor:
845                     for variant in method.variants:
846                         args = []
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:
853                             continue
854                         class_info.constructor_arg_num.add(args_num)
855                         class_bindings.append(constructor_template.substitute(signature=', '.join(args)))
856                 else:
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
861                     else:
862                         binding = self.gen_function_binding(method, class_info=class_info)
863                         class_bindings = class_bindings + binding
864
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))
868
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:
873
874
875
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])))
881
882             dv = ''
883             base = Template("""base<$base>""")
884
885             assert len(class_info.bases) <= 1 , "multiple inheritance not supported"
886
887             if len(class_info.bases) == 1:
888                 dv = "," + base.substitute(base=', '.join(class_info.bases))
889
890             self.bindings.append(class_template.substitute(cpp_name=class_info.cname,
891                                                            js_name=name,
892                                                            class_templates=''.join(class_bindings),
893                                                            derivation=dv))
894
895         if export_enums:
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':
900                     continue
901                 for name, enum in sorted(ns.enums.items()):
902                     if not name.endswith('.anonymous'):
903                         name = name.replace("cv.", "")
904                         enum_values = []
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))
909
910                         self.bindings.append(enum_template.substitute(cpp_name=name.replace(".", "::"),
911                                                                       js_name=name.replace(".", "_"),
912                                                                       enum_items=''.join(enum_values)))
913                     else:
914                         print(name)
915                         #TODO: represent anonymous enums with constants
916
917         if export_consts:
918             # step 5: generate bindings for consts
919             for ns_name, ns in sorted(self.namespaces.items()):
920                 if ns_name.split('.')[0] != 'cv':
921                     continue
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))
925
926         with open(core_bindings) as f:
927             ret = f.read()
928
929         header_includes = '\n'.join(['#include "{}"'.format(hdr) for hdr in headers])
930         ret = ret.replace('@INCLUDES@', header_includes)
931
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))
935
936
937         # print(ret)
938         text_file = open(dst_file, "w")
939         text_file.write(ret)
940         text_file.close()
941
942
943 if __name__ == "__main__":
944     if len(sys.argv) < 4:
945         print("Usage:\n", \
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]))
949         exit(0)
950
951     dstdir = "."
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)
956     import hdr_parser
957
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)