Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/js/src/embindgen.py
16337 views
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',
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
'CLAHE': ['apply', 'collectGarbage', 'getClipLimit', 'getTilesGridSize', 'setClipLimit', 'setTilesGridSize']}
117
118
objdetect = {'': ['groupRectangles'],
119
'HOGDescriptor': ['load', 'HOGDescriptor', 'getDefaultPeopleDetector', 'getDaimlerPeopleDetector', 'setSVMDetector', 'detectMultiScale'],
120
'CascadeClassifier': ['load', 'detectMultiScale2', 'CascadeClassifier', 'detectMultiScale3', 'empty', 'detectMultiScale']}
121
122
video = {'': ['CamShift', 'calcOpticalFlowFarneback', 'calcOpticalFlowPyrLK', 'createBackgroundSubtractorMOG2', \
123
'findTransformECC', 'meanShift'],
124
'BackgroundSubtractorMOG2': ['BackgroundSubtractorMOG2', 'apply'],
125
'BackgroundSubtractor': ['apply', 'getBackgroundImage']}
126
127
dnn = {'dnn_Net': ['setInput', 'forward'],
128
'': ['readNetFromCaffe', 'readNetFromTensorflow', 'readNetFromTorch', 'readNetFromDarknet',
129
'readNetFromONNX', 'readNet', 'blobFromImage']}
130
131
features2d = {'Feature2D': ['detect', 'compute', 'detectAndCompute', 'descriptorSize', 'descriptorType', 'defaultNorm', 'empty', 'getDefaultName'],
132
'BRISK': ['create', 'getDefaultName'],
133
'ORB': ['create', 'setMaxFeatures', 'setScaleFactor', 'setNLevels', 'setEdgeThreshold', 'setFirstLevel', 'setWTA_K', 'setScoreType', 'setPatchSize', 'getFastThreshold', 'getDefaultName'],
134
'MSER': ['create', 'detectRegions', 'setDelta', 'getDelta', 'setMinArea', 'getMinArea', 'setMaxArea', 'getMaxArea', 'setPass2Only', 'getPass2Only', 'getDefaultName'],
135
'FastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'],
136
'AgastFeatureDetector': ['create', 'setThreshold', 'getThreshold', 'setNonmaxSuppression', 'getNonmaxSuppression', 'setType', 'getType', 'getDefaultName'],
137
'GFTTDetector': ['create', 'setMaxFeatures', 'getMaxFeatures', 'setQualityLevel', 'getQualityLevel', 'setMinDistance', 'getMinDistance', 'setBlockSize', 'getBlockSize', 'setHarrisDetector', 'getHarrisDetector', 'setK', 'getK', 'getDefaultName'],
138
# 'SimpleBlobDetector': ['create'],
139
'KAZE': ['create', 'setExtended', 'getExtended', 'setUpright', 'getUpright', 'setThreshold', 'getThreshold', 'setNOctaves', 'getNOctaves', 'setNOctaveLayers', 'getNOctaveLayers', 'setDiffusivity', 'getDiffusivity', 'getDefaultName'],
140
'AKAZE': ['create', 'setDescriptorType', 'getDescriptorType', 'setDescriptorSize', 'getDescriptorSize', 'setDescriptorChannels', 'getDescriptorChannels', 'setThreshold', 'getThreshold', 'setNOctaves', 'getNOctaves', 'setNOctaveLayers', 'getNOctaveLayers', 'setDiffusivity', 'getDiffusivity', 'getDefaultName'],
141
'DescriptorMatcher': ['add', 'clear', 'empty', 'isMaskSupported', 'train', 'match', 'knnMatch', 'radiusMatch', 'clone', 'create'],
142
'BFMatcher': ['isMaskSupported', 'create'],
143
'': ['FAST', 'AGAST', 'drawKeypoints', 'drawMatches']}
144
145
def makeWhiteList(module_list):
146
wl = {}
147
for m in module_list:
148
for k in m.keys():
149
if k in wl:
150
wl[k] += m[k]
151
else:
152
wl[k] = m[k]
153
return wl
154
155
white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d])
156
157
# Features to be exported
158
export_enums = False
159
export_consts = True
160
with_wrapped_functions = True
161
with_default_params = True
162
with_vec_from_js_array = True
163
164
wrapper_namespace = "Wrappers"
165
type_dict = {
166
'InputArray': 'const cv::Mat&',
167
'OutputArray': 'cv::Mat&',
168
'InputOutputArray': 'cv::Mat&',
169
'InputArrayOfArrays': 'const std::vector<cv::Mat>&',
170
'OutputArrayOfArrays': 'std::vector<cv::Mat>&',
171
'String': 'std::string',
172
'const String&':'const std::string&'
173
}
174
175
def normalize_class_name(name):
176
return re.sub(r"^cv\.", "", name).replace(".", "_")
177
178
179
class ClassProp(object):
180
def __init__(self, decl):
181
self.tp = decl[0].replace("*", "_ptr").strip()
182
self.name = decl[1]
183
self.readonly = True
184
if "/RW" in decl[3]:
185
self.readonly = False
186
187
188
class ClassInfo(object):
189
def __init__(self, name, decl=None):
190
self.cname = name.replace(".", "::")
191
self.name = self.wname = normalize_class_name(name)
192
193
self.ismap = False
194
self.issimple = False
195
self.isalgorithm = False
196
self.methods = {}
197
self.ext_constructors = {}
198
self.props = []
199
self.consts = {}
200
customname = False
201
self.jsfuncs = {}
202
self.constructor_arg_num = set()
203
204
self.has_smart_ptr = False
205
206
if decl:
207
self.bases = decl[1].split()[1:]
208
if len(self.bases) > 1:
209
self.bases = [self.bases[0].strip(",")]
210
# return sys.exit(-1)
211
if self.bases and self.bases[0].startswith("cv::"):
212
self.bases[0] = self.bases[0][4:]
213
if self.bases and self.bases[0] == "Algorithm":
214
self.isalgorithm = True
215
for m in decl[2]:
216
if m.startswith("="):
217
self.wname = m[1:]
218
customname = True
219
elif m == "/Map":
220
self.ismap = True
221
elif m == "/Simple":
222
self.issimple = True
223
self.props = [ClassProp(p) for p in decl[3]]
224
225
if not customname and self.wname.startswith("Cv"):
226
self.wname = self.wname[2:]
227
228
229
def handle_ptr(tp):
230
if tp.startswith('Ptr_'):
231
tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
232
return tp
233
234
def handle_vector(tp):
235
if tp.startswith('vector_'):
236
tp = handle_vector(tp[tp.find('_') + 1:])
237
tp = 'std::vector<' + "::".join(tp.split('_')) + '>'
238
return tp
239
240
241
class ArgInfo(object):
242
def __init__(self, arg_tuple):
243
self.tp = handle_ptr(arg_tuple[0]).strip()
244
self.name = arg_tuple[1]
245
self.defval = arg_tuple[2]
246
self.isarray = False
247
self.arraylen = 0
248
self.arraycvt = None
249
self.inputarg = True
250
self.outputarg = False
251
self.returnarg = False
252
self.const = False
253
self.reference = False
254
for m in arg_tuple[3]:
255
if m == "/O":
256
self.inputarg = False
257
self.outputarg = True
258
self.returnarg = True
259
elif m == "/IO":
260
self.inputarg = True
261
self.outputarg = True
262
self.returnarg = True
263
elif m.startswith("/A"):
264
self.isarray = True
265
self.arraylen = m[2:].strip()
266
elif m.startswith("/CA"):
267
self.isarray = True
268
self.arraycvt = m[2:].strip()
269
elif m == "/C":
270
self.const = True
271
elif m == "/Ref":
272
self.reference = True
273
if self.tp == "Mat":
274
if self.outputarg:
275
self.tp = "cv::Mat&"
276
elif self.inputarg:
277
self.tp = "const cv::Mat&"
278
if self.tp == "vector_Mat":
279
if self.outputarg:
280
self.tp = "std::vector<cv::Mat>&"
281
elif self.inputarg:
282
self.tp = "const std::vector<cv::Mat>&"
283
self.tp = handle_vector(self.tp).strip()
284
if self.const:
285
self.tp = "const " + self.tp
286
if self.reference:
287
self.tp = self.tp + "&"
288
self.py_inputarg = False
289
self.py_outputarg = False
290
291
class FuncVariant(object):
292
def __init__(self, class_name, name, decl, is_constructor, is_class_method, is_const, is_virtual, is_pure_virtual, ref_return, const_return):
293
self.class_name = class_name
294
self.name = self.wname = name
295
self.is_constructor = is_constructor
296
self.is_class_method = is_class_method
297
self.is_const = is_const
298
self.is_virtual = is_virtual
299
self.is_pure_virtual = is_pure_virtual
300
self.refret = ref_return
301
self.constret = const_return
302
self.rettype = handle_vector(handle_ptr(decl[1]).strip()).strip()
303
if self.rettype == "void":
304
self.rettype = ""
305
self.args = []
306
self.array_counters = {}
307
308
for a in decl[3]:
309
ainfo = ArgInfo(a)
310
if ainfo.isarray and not ainfo.arraycvt:
311
c = ainfo.arraylen
312
c_arrlist = self.array_counters.get(c, [])
313
if c_arrlist:
314
c_arrlist.append(ainfo.name)
315
else:
316
self.array_counters[c] = [ainfo.name]
317
self.args.append(ainfo)
318
319
320
class FuncInfo(object):
321
def __init__(self, class_name, name, cname, namespace, isconstructor):
322
self.class_name = class_name
323
self.name = name
324
self.cname = cname
325
self.namespace = namespace
326
self.variants = []
327
self.is_constructor = isconstructor
328
329
def add_variant(self, variant):
330
self.variants.append(variant)
331
332
333
class Namespace(object):
334
def __init__(self):
335
self.funcs = {}
336
self.enums = {}
337
self.consts = {}
338
339
340
class JSWrapperGenerator(object):
341
def __init__(self):
342
343
self.bindings = []
344
self.wrapper_funcs = []
345
346
self.classes = {}
347
self.namespaces = {}
348
self.enums = {}
349
350
self.parser = hdr_parser.CppHeaderParser()
351
self.class_idx = 0
352
353
def add_class(self, stype, name, decl):
354
class_info = ClassInfo(name, decl)
355
class_info.decl_idx = self.class_idx
356
self.class_idx += 1
357
358
if class_info.name in self.classes:
359
print("Generator error: class %s (cpp_name=%s) already exists" \
360
% (class_info.name, class_info.cname))
361
sys.exit(-1)
362
self.classes[class_info.name] = class_info
363
364
if class_info.bases:
365
chunks = class_info.bases[0].split('::')
366
base = '_'.join(chunks)
367
while base not in self.classes and len(chunks) > 1:
368
del chunks[-2]
369
base = '_'.join(chunks)
370
if base not in self.classes:
371
print("Generator error: unable to resolve base %s for %s"
372
% (class_info.bases[0], class_info.name))
373
sys.exit(-1)
374
else:
375
class_info.bases[0] = "::".join(chunks)
376
class_info.isalgorithm |= self.classes[base].isalgorithm
377
378
def split_decl_name(self, name):
379
chunks = name.split('.')
380
namespace = chunks[:-1]
381
classes = []
382
while namespace and '.'.join(namespace) not in self.parser.namespaces:
383
classes.insert(0, namespace.pop())
384
return namespace, classes, chunks[-1]
385
386
def add_enum(self, decl):
387
name = decl[0].rsplit(" ", 1)[1]
388
namespace, classes, val = self.split_decl_name(name)
389
namespace = '.'.join(namespace)
390
ns = self.namespaces.setdefault(namespace, Namespace())
391
if len(name) == 0: name = "<unnamed>"
392
if name.endswith("<unnamed>"):
393
i = 0
394
while True:
395
i += 1
396
candidate_name = name.replace("<unnamed>", "unnamed_%u" % i)
397
if candidate_name not in ns.enums:
398
name = candidate_name
399
break;
400
cname = name.replace('.', '::')
401
type_dict[normalize_class_name(name)] = cname
402
if name in ns.enums:
403
print("Generator warning: enum %s (cname=%s) already exists" \
404
% (name, cname))
405
# sys.exit(-1)
406
else:
407
ns.enums[name] = []
408
for item in decl[3]:
409
ns.enums[name].append(item)
410
411
const_decls = decl[3]
412
413
for decl in const_decls:
414
name = decl[0]
415
self.add_const(name.replace("const ", "").strip(), decl)
416
417
def add_const(self, name, decl):
418
cname = name.replace('.','::')
419
namespace, classes, name = self.split_decl_name(name)
420
namespace = '.'.join(namespace)
421
name = '_'.join(classes+[name])
422
ns = self.namespaces.setdefault(namespace, Namespace())
423
if name in ns.consts:
424
print("Generator error: constant %s (cname=%s) already exists" \
425
% (name, cname))
426
sys.exit(-1)
427
ns.consts[name] = cname
428
429
def add_func(self, decl):
430
namespace, classes, barename = self.split_decl_name(decl[0])
431
cpp_name = "::".join(namespace + classes + [barename])
432
name = barename
433
class_name = ''
434
bare_class_name = ''
435
if classes:
436
class_name = normalize_class_name('.'.join(namespace + classes))
437
bare_class_name = classes[-1]
438
namespace = '.'.join(namespace)
439
440
is_constructor = name == bare_class_name
441
is_class_method = False
442
is_const_method = False
443
is_virtual_method = False
444
is_pure_virtual_method = False
445
const_return = False
446
ref_return = False
447
448
for m in decl[2]:
449
if m == "/S":
450
is_class_method = True
451
elif m == "/C":
452
is_const_method = True
453
elif m == "/V":
454
is_virtual_method = True
455
elif m == "/PV":
456
is_pure_virtual_method = True
457
elif m == "/Ref":
458
ref_return = True
459
elif m == "/CRet":
460
const_return = True
461
elif m.startswith("="):
462
name = m[1:]
463
464
if class_name:
465
cpp_name = barename
466
func_map = self.classes[class_name].methods
467
else:
468
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
469
470
func = func_map.setdefault(name, FuncInfo(class_name, name, cpp_name, namespace, is_constructor))
471
472
variant = FuncVariant(class_name, name, decl, is_constructor, is_class_method, is_const_method,
473
is_virtual_method, is_pure_virtual_method, ref_return, const_return)
474
func.add_variant(variant)
475
476
def save(self, path, name, buf):
477
f = open(path + "/" + name, "wt")
478
f.write(buf.getvalue())
479
f.close()
480
481
def gen_function_binding_with_wrapper(self, func, class_info):
482
483
binding_text = None
484
wrapper_func_text = None
485
486
bindings = []
487
wrappers = []
488
489
for index, variant in enumerate(func.variants):
490
491
factory = False
492
if class_info and 'Ptr<' in variant.rettype:
493
494
factory = True
495
base_class_name = variant.rettype
496
base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
497
if base_class_name in self.classes:
498
self.classes[base_class_name].has_smart_ptr = True
499
else:
500
print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
501
self.classes[class_info.name].has_smart_ptr = True
502
503
def_args = []
504
has_def_param = False
505
506
# Return type
507
ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
508
if ret_type.startswith('Ptr'): #smart pointer
509
ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
510
if ptr_type in type_dict:
511
ret_type = type_dict[ptr_type]
512
for key in type_dict:
513
if key in ret_type:
514
ret_type = ret_type.replace(key, type_dict[key])
515
516
arg_types = []
517
unwrapped_arg_types = []
518
for arg in variant.args:
519
arg_type = None
520
if arg.tp in type_dict:
521
arg_type = type_dict[arg.tp]
522
else:
523
arg_type = arg.tp
524
# Add default value
525
if with_default_params and arg.defval != '':
526
def_args.append(arg.defval);
527
arg_types.append(arg_type)
528
unwrapped_arg_types.append(arg_type)
529
530
# Function attribure
531
func_attribs = ''
532
if '*' in ''.join(arg_types):
533
func_attribs += ', allow_raw_pointers()'
534
535
if variant.is_pure_virtual:
536
func_attribs += ', pure_virtual()'
537
538
539
# Wrapper function
540
wrap_func_name = (func.class_name+"_" if class_info != None else "") + func.name.split("::")[-1] + "_wrapper"
541
js_func_name = func.name
542
543
# TODO: Name functions based wrap directives or based on arguments list
544
if index > 0:
545
wrap_func_name += str(index)
546
js_func_name += str(index)
547
548
c_func_name = 'Wrappers::' + wrap_func_name
549
550
# Binding template-
551
raw_arg_names = ['arg' + str(i + 1) for i in range(0, len(variant.args))]
552
arg_names = []
553
w_signature = []
554
casted_arg_types = []
555
for arg_type, arg_name in zip(arg_types, raw_arg_names):
556
casted_arg_name = arg_name
557
if with_vec_from_js_array:
558
# Only support const vector reference as input parameter
559
match = re.search(r'const std::vector<(.*)>&', arg_type)
560
if match:
561
type_in_vect = match.group(1)
562
if type_in_vect != 'cv::Mat':
563
casted_arg_name = 'emscripten::vecFromJSArray<' + type_in_vect + '>(' + arg_name + ')'
564
arg_type = re.sub(r'std::vector<(.*)>', 'emscripten::val', arg_type)
565
w_signature.append(arg_type + ' ' + arg_name)
566
arg_names.append(casted_arg_name)
567
casted_arg_types.append(arg_type)
568
569
arg_types = casted_arg_types
570
571
# Argument list, signature
572
arg_names_casted = [c if a == b else c + '.as<' + a + '>()' for a, b, c in
573
zip(unwrapped_arg_types, arg_types, arg_names)]
574
575
# Add self object to the parameters
576
if class_info and not factory:
577
arg_types = [class_info.cname + '&'] + arg_types
578
w_signature = [class_info.cname + '& arg0 '] + w_signature
579
580
for j in range(0, len(def_args) + 1):
581
postfix = ''
582
if j > 0:
583
postfix = '_' + str(j);
584
585
###################################
586
# Wrapper
587
if factory: # TODO or static
588
name = class_info.cname+'::' if variant.class_name else ""
589
cpp_call_text = static_class_call_template.substitute(scope=name,
590
func=func.cname,
591
args=', '.join(arg_names[:len(arg_names)-j]))
592
elif class_info:
593
cpp_call_text = class_call_template.substitute(obj='arg0',
594
func=func.cname,
595
args=', '.join(arg_names[:len(arg_names)-j]))
596
else:
597
cpp_call_text = call_template.substitute(func=func.cname,
598
args=', '.join(arg_names[:len(arg_names)-j]))
599
600
601
wrapper_func_text = wrapper_function_template.substitute(ret_val=ret_type,
602
func=wrap_func_name+postfix,
603
signature=', '.join(w_signature[:len(w_signature)-j]),
604
cpp_call=cpp_call_text,
605
const='' if variant.is_const else '')
606
607
###################################
608
# Binding
609
if class_info:
610
if factory:
611
# print("Factory Function: ", c_func_name, len(variant.args) - j, class_info.name)
612
if variant.is_pure_virtual:
613
# FIXME: workaround for pure virtual in constructor
614
# e.g. DescriptorMatcher_clone_wrapper
615
continue
616
# consider the default parameter variants
617
args_num = len(variant.args) - j
618
if args_num in class_info.constructor_arg_num:
619
# FIXME: workaournd for constructor overload with same args number
620
# e.g. DescriptorMatcher
621
continue
622
class_info.constructor_arg_num.add(args_num)
623
binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
624
cpp_name=c_func_name+postfix,
625
ret=ret_type,
626
args=','.join(arg_types[:len(arg_types)-j]),
627
optional=func_attribs)
628
else:
629
binding_template = overload_class_static_function_template if variant.is_class_method else \
630
overload_class_function_template
631
binding_text = binding_template.substitute(js_name=js_func_name,
632
const='' if variant.is_const else '',
633
cpp_name=c_func_name+postfix,
634
ret=ret_type,
635
args=','.join(arg_types[:len(arg_types)-j]),
636
optional=func_attribs)
637
else:
638
binding_text = overload_function_template.substitute(js_name=js_func_name,
639
cpp_name=c_func_name+postfix,
640
const='const' if variant.is_const else '',
641
ret=ret_type,
642
args=', '.join(arg_types[:len(arg_types)-j]),
643
optional=func_attribs)
644
645
bindings.append(binding_text)
646
wrappers.append(wrapper_func_text)
647
648
return [bindings, wrappers]
649
650
651
def gen_function_binding(self, func, class_info):
652
653
if not class_info == None :
654
func_name = class_info.cname+'::'+func.cname
655
else :
656
func_name = func.cname
657
658
binding_text = None
659
binding_text_list = []
660
661
for index, variant in enumerate(func.variants):
662
factory = False
663
#TODO if variant.is_class_method and variant.rettype == ('Ptr<' + class_info.name + '>'):
664
if (not class_info == None) and variant.rettype == ('Ptr<' + class_info.name + '>') or (func.name.startswith("create") and variant.rettype):
665
factory = True
666
base_class_name = variant.rettype
667
base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
668
if base_class_name in self.classes:
669
self.classes[base_class_name].has_smart_ptr = True
670
else:
671
print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
672
self.classes[class_info.name].has_smart_ptr = True
673
674
675
# Return type
676
ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
677
678
ret_type = ret_type.strip()
679
680
if ret_type.startswith('Ptr'): #smart pointer
681
ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
682
if ptr_type in type_dict:
683
ret_type = type_dict[ptr_type]
684
for key in type_dict:
685
if key in ret_type:
686
ret_type = ret_type.replace(key, type_dict[key])
687
688
if variant.constret and ret_type.startswith('const') == False:
689
ret_type = 'const ' + ret_type
690
if variant.refret and ret_type.endswith('&') == False:
691
ret_type += '&'
692
693
arg_types = []
694
orig_arg_types = []
695
def_args = []
696
for arg in variant.args:
697
if arg.tp in type_dict:
698
arg_type = type_dict[arg.tp]
699
else:
700
arg_type = arg.tp
701
702
#if arg.outputarg:
703
# arg_type += '&'
704
orig_arg_types.append(arg_type)
705
if with_default_params and arg.defval != '':
706
def_args.append(arg.defval)
707
arg_types.append(orig_arg_types[-1])
708
709
# Function attribure
710
func_attribs = ''
711
if '*' in ''.join(orig_arg_types):
712
func_attribs += ', allow_raw_pointers()'
713
714
if variant.is_pure_virtual:
715
func_attribs += ', pure_virtual()'
716
717
#TODO better naming
718
#if variant.name in self.jsfunctions:
719
#else
720
js_func_name = variant.name
721
722
723
c_func_name = func.cname if (factory and variant.is_class_method == False) else func_name
724
725
726
################################### Binding
727
for j in range(0, len(def_args) + 1):
728
postfix = ''
729
if j > 0:
730
postfix = '_' + str(j);
731
if factory:
732
binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
733
cpp_name=c_func_name+postfix,
734
ret=ret_type,
735
args=','.join(arg_types[:len(arg_types)-j]),
736
optional=func_attribs)
737
else:
738
binding_template = overload_class_static_function_template if variant.is_class_method else \
739
overload_function_template if class_info == None else overload_class_function_template
740
binding_text = binding_template.substitute(js_name=js_func_name,
741
const='const' if variant.is_const else '',
742
cpp_name=c_func_name+postfix,
743
ret=ret_type,
744
args=','.join(arg_types[:len(arg_types)-1]),
745
optional=func_attribs)
746
747
binding_text_list.append(binding_text)
748
749
return binding_text_list
750
751
def print_decls(self, decls):
752
"""
753
Prints the list of declarations, retrieived by the parse() method
754
"""
755
for d in decls:
756
print(d[0], d[1], ";".join(d[2]))
757
for a in d[3]:
758
print(" ", a[0], a[1], a[2], end="")
759
if a[3]:
760
print("; ".join(a[3]))
761
else:
762
print()
763
764
def gen(self, dst_file, src_files, core_bindings):
765
# step 1: scan the headers and extract classes, enums and functions
766
headers = []
767
for hdr in src_files:
768
decls = self.parser.parse(hdr)
769
# print(hdr);
770
# self.print_decls(decls);
771
if len(decls) == 0:
772
continue
773
headers.append(hdr[hdr.rindex('opencv2/'):])
774
for decl in decls:
775
name = decl[0]
776
type = name[:name.find(" ")]
777
if type == "struct" or type == "class": # class/structure case
778
name = name[name.find(" ") + 1:].strip()
779
self.add_class(type, name, decl)
780
elif name.startswith("enum"): # enumerations
781
self.add_enum(decl)
782
elif name.startswith("const"):
783
# constant
784
self.add_const(name.replace("const ", "").strip(), decl)
785
else: # class/global function
786
self.add_func(decl)
787
788
# step 2: generate bindings
789
# Global functions
790
for ns_name, ns in sorted(self.namespaces.items()):
791
if ns_name.split('.')[0] != 'cv':
792
continue
793
for name, func in sorted(ns.funcs.items()):
794
if name in ignore_list:
795
continue
796
if not name in white_list['']:
797
continue
798
799
ext_cnst = False
800
# Check if the method is an external constructor
801
for variant in func.variants:
802
if "Ptr<" in variant.rettype:
803
804
# Register the smart pointer
805
base_class_name = variant.rettype
806
base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
807
self.classes[base_class_name].has_smart_ptr = True
808
809
# Adds the external constructor
810
class_name = func.name.replace("create", "")
811
if not class_name in self.classes:
812
self.classes[base_class_name].methods[func.cname] = func
813
else:
814
self.classes[class_name].methods[func.cname] = func
815
ext_cnst = True
816
if ext_cnst:
817
continue
818
819
if with_wrapped_functions:
820
binding, wrapper = self.gen_function_binding_with_wrapper(func, class_info=None)
821
self.bindings += binding
822
self.wrapper_funcs += wrapper
823
else:
824
binding = self.gen_function_binding(func, class_info=None)
825
self.bindings+=binding
826
827
# generate code for the classes and their methods
828
class_list = list(self.classes.items())
829
830
for name, class_info in class_list:
831
class_bindings = []
832
if not name in white_list:
833
continue
834
835
# Generate bindings for methods
836
for method_name, method in class_info.methods.items():
837
if method.cname in ignore_list:
838
continue
839
if not method.name in white_list[method.class_name]:
840
continue
841
if method.is_constructor:
842
for variant in method.variants:
843
args = []
844
for arg in variant.args:
845
arg_type = type_dict[arg.tp] if arg.tp in type_dict else arg.tp
846
args.append(arg_type)
847
# print('Constructor: ', class_info.name, len(variant.args))
848
args_num = len(variant.args)
849
if args_num in class_info.constructor_arg_num:
850
continue
851
class_info.constructor_arg_num.add(args_num)
852
class_bindings.append(constructor_template.substitute(signature=', '.join(args)))
853
else:
854
if with_wrapped_functions and (len(method.variants) > 1 or len(method.variants[0].args)>0 or "String" in method.variants[0].rettype):
855
binding, wrapper = self.gen_function_binding_with_wrapper(method, class_info=class_info)
856
self.wrapper_funcs = self.wrapper_funcs + wrapper
857
class_bindings = class_bindings + binding
858
else:
859
binding = self.gen_function_binding(method, class_info=class_info)
860
class_bindings = class_bindings + binding
861
862
# Regiseter Smart pointer
863
if class_info.has_smart_ptr:
864
class_bindings.append(smart_ptr_reg_template.substitute(cname=class_info.cname, name=class_info.name))
865
866
# Attach external constructors
867
# for method_name, method in class_info.ext_constructors.items():
868
# print("ext constructor", method_name)
869
#if class_info.ext_constructors:
870
871
872
873
# Generate bindings for properties
874
for property in class_info.props:
875
_class_property = class_property_enum_template if property.tp in type_dict else class_property_template
876
class_bindings.append(_class_property.substitute(js_name=property.name, cpp_name='::'.join(
877
[class_info.cname, property.name])))
878
879
dv = ''
880
base = Template("""base<$base>""")
881
882
assert len(class_info.bases) <= 1 , "multiple inheritance not supported"
883
884
if len(class_info.bases) == 1:
885
dv = "," + base.substitute(base=', '.join(class_info.bases))
886
887
self.bindings.append(class_template.substitute(cpp_name=class_info.cname,
888
js_name=name,
889
class_templates=''.join(class_bindings),
890
derivation=dv))
891
892
if export_enums:
893
# step 4: generate bindings for enums
894
# TODO anonymous enums are ignored for now.
895
for ns_name, ns in sorted(self.namespaces.items()):
896
if ns_name.split('.')[0] != 'cv':
897
continue
898
for name, enum in sorted(ns.enums.items()):
899
if not name.endswith('.anonymous'):
900
name = name.replace("cv.", "")
901
enum_values = []
902
for enum_val in enum:
903
value = enum_val[0][enum_val[0].rfind(".")+1:]
904
enum_values.append(enum_item_template.substitute(val=value,
905
cpp_val=name.replace('.', '::')+'::'+value))
906
907
self.bindings.append(enum_template.substitute(cpp_name=name.replace(".", "::"),
908
js_name=name.replace(".", "_"),
909
enum_items=''.join(enum_values)))
910
else:
911
print(name)
912
#TODO: represent anonymous enums with constants
913
914
if export_consts:
915
# step 5: generate bindings for consts
916
for ns_name, ns in sorted(self.namespaces.items()):
917
if ns_name.split('.')[0] != 'cv':
918
continue
919
for name, const in sorted(ns.consts.items()):
920
# print("Gen consts: ", name, const)
921
self.bindings.append(const_template.substitute(js_name=name, value=const))
922
923
with open(core_bindings) as f:
924
ret = f.read()
925
926
header_includes = '\n'.join(['#include "{}"'.format(hdr) for hdr in headers])
927
ret = ret.replace('@INCLUDES@', header_includes)
928
929
defis = '\n'.join(self.wrapper_funcs)
930
ret += wrapper_codes_template.substitute(ns=wrapper_namespace, defs=defis)
931
ret += emscripten_binding_template.substitute(binding_name='testBinding', bindings=''.join(self.bindings))
932
933
934
# print(ret)
935
text_file = open(dst_file, "w")
936
text_file.write(ret)
937
text_file.close()
938
939
940
if __name__ == "__main__":
941
if len(sys.argv) < 4:
942
print("Usage:\n", \
943
os.path.basename(sys.argv[0]), \
944
"<full path to hdr_parser.py> <bindings.cpp> <headers.txt> <core_bindings.cpp>")
945
print("Current args are: ", ", ".join(["'"+a+"'" for a in sys.argv]))
946
exit(0)
947
948
dstdir = "."
949
hdr_parser_path = os.path.abspath(sys.argv[1])
950
if hdr_parser_path.endswith(".py"):
951
hdr_parser_path = os.path.dirname(hdr_parser_path)
952
sys.path.append(hdr_parser_path)
953
import hdr_parser
954
955
bindingsCpp = sys.argv[2]
956
headers = open(sys.argv[3], 'r').read().split(';')
957
coreBindings = sys.argv[4]
958
generator = JSWrapperGenerator()
959
generator.gen(bindingsCpp, headers, coreBindings)
960
961