Update to 2.0.0 tree from current Fremantle build
[opencv] / interfaces / python / gen.py
1 import sys
2
3 class argument:
4   def __init__(self, fields):
5     self.ty = fields[0]
6     self.nm = fields[1]
7     self.flags = ""
8     self.init = None
9
10     if len(fields) > 2:
11       if fields[2][0] == '/':
12         self.flags = fields[2][1:].split(",")
13       else:
14         self.init = fields[2]
15
16 api = []
17 for l in open("%s/api" % sys.argv[1]):
18   if l[0] == '#':
19     continue
20   l = l.rstrip()
21   f = l.split()
22   if len(f) != 0:
23     if l[0] != ' ':
24       if len(f) > 1:
25         ty = f[1]
26       else:
27         ty = None
28       api.append((f[0], [], ty))
29     else:
30       api[-1][1].append(argument(f))
31
32 def cname(n):
33   if n.startswith("CV"):
34     return '_' + n
35   elif n[0].isdigit():
36     return '_' + n
37   else:
38     return n
39
40 # RHS is how the aggregate gets expanded in the C call
41 aggregate = {
42   'pts_npts_contours' :  '!.pts,!.npts,!.contours',
43   'cvarr_count' :        '!.cvarr,!.count',
44   'cvarr_plane_count' :  '!.cvarr,!.count',
45   'floats' :             '!.f',
46   'ints' :               '!.i',
47   'CvPoints' :           '!.p,!.count',
48   'CvPoint2D32fs' :      '!.p,!.count',
49   'CvPoint3D32fs' :      '!.p,!.count',
50   'cvarrseq' :           '!.v',
51   'CvArrs' :             '!.ims',
52   'IplImages' :          '!.ims',
53   'intpair' :            '!.pairs,!.count',
54   'cvpoint2d32f_count' : '!.points,&!.count'
55 }
56 conversion_types = [
57 'char',
58 'CvArr',
59 'CvArrSeq',
60 'CvBox2D', # '((ff)(ff)f)',
61 'CvBox2D*',
62 'CvCapture*',
63 'CvVideoWriter*',
64 'CvContourTree*',
65 'CvFont',
66 'CvFont*',
67 'CvHaarClassifierCascade*',
68 'CvHistogram',
69 'CvMat',
70 'CvMatND',
71 'CvMemStorage',
72 'CvMoments',
73 'CvMoments*',
74 'CvNextEdgeType',
75 'CvPoint',
76 'CvPoint*',
77 'CvPoint2D32f', # '(ff)',
78 'CvPoint2D32f*',
79 'CvPoint3D32f*',
80 'CvPOSITObject*',
81 'CvRect',
82 'CvRect*',
83 'CvRNG*',
84 'CvScalar',
85 'CvSeq',
86 'CvSeqOfCvConvexityDefect',
87 'CvSize',
88 'CvSlice',
89 'CvStarDetectorParams',
90 'CvSubdiv2D*',
91 'CvSubdiv2DEdge',
92 'CvTermCriteria',
93 'generic',
94 'IplConvKernel*',
95 'IplImage',
96 ]
97
98 def safename(s):
99   return s.replace('*', 'PTR').replace('[', '_').replace(']', '_')
100
101 def has_optional(al):
102     """ return true if any argument is optional """
103     return any([a.init for a in al])
104
105 def gen(name, args, ty):
106   yield ""
107   if has_optional(args):
108       yield "static PyObject *pycv%s(PyObject *self, PyObject *args, PyObject *kw)" % cname(name) 
109   else:
110       yield "static PyObject *pycv%s(PyObject *self, PyObject *args)" % cname(name)
111   yield "{"
112
113   destinations = []
114   for a in args:
115     remap = {
116      'CvArr' : 'CvArr*',
117      'CvMat' : 'CvMat*',
118      'CvMatND' : 'CvMatND*',
119      'IplImage' : 'IplImage*',
120      'CvMemStorage' : 'CvMemStorage*',
121      'CvHistogram':'CvHistogram*',
122      'CvSeq':'CvSeq*',
123      'CvHaarClassifierCascade' : 'CvHaarClassifierCascade*'
124     }
125     ctype = remap.get(a.ty, a.ty)
126     if a.init:
127       init = " = %s" % a.init
128     else:
129       init = ''
130     yield "  %s %s%s;" % (ctype, a.nm, init)
131     if 'O' in a.flags:
132       continue
133     if a.ty in (conversion_types + aggregate.keys()):
134       yield '  PyObject *pyobj_%s = NULL;' % (a.nm)
135       destinations.append('&pyobj_%s' % (a.nm))
136     elif a.ty in [ 'CvPoint2D32f' ]:
137       destinations.append('&%s.x, &%s.y' % (a.nm, a.nm))
138     elif a.ty in [ 'CvTermCriteria' ]:
139       destinations.append('&%s.type, &%s.max_iter, &%s.epsilon' % ((a.nm,)*3))
140     elif a.ty in [ 'CvSURFParams' ]:
141       destinations.append('&%s.extended, &%s.hessianThreshold, &%s.nOctaves, &%s.nOctaveLayers' % ((a.nm,)*4))
142     elif a.nm in [ 'CvBox2D' ]:
143       s = ", ".join([('&' + a.nm +'.' + fld) for fld in [ 'center.x', 'center.y', 'size.width', 'size.height', 'angle' ] ])
144       destinations.append(s)
145     else:
146       destinations.append('&%s' % a.nm)
147   fmap = {
148     'CvSURFParams' : '(idii)',
149     'double' : 'd',
150     'float' : 'f',
151     'int' : 'i',
152     'int64' : 'L',
153     'char*' : 's',
154   }
155   for k in (conversion_types + aggregate.keys()):
156     fmap[k] = 'O'
157   in_args = [ a for a in args if not 'O' in a.flags ]
158   fmt0 = "".join([ fmap[a.ty] for a in in_args if not a.init])
159   fmt1 = "".join([ fmap[a.ty] for a in in_args if a.init])
160       
161   yield ''
162   if len(fmt0 + fmt1) > 0:
163     if len(fmt1) > 0:
164       yield '  const char *keywords[] = { %s };' % (", ".join([ '"%s"' % arg.nm for arg in args if not 'O' in arg.flags ] + ['NULL']))
165       yield '  if (!PyArg_ParseTupleAndKeywords(args, kw, "%s|%s", %s))' % (fmt0, fmt1, ", ".join(['(char**)keywords'] + destinations))
166       if '(' in (fmt0 + fmt1):
167         print "Tuple with kwargs is not allowed, function", name
168         sys.exit(1)
169     else:
170       yield '  if (!PyArg_ParseTuple(args, "%s", %s))' % (fmt0, ", ".join(destinations))
171     yield '    return NULL;'
172
173   # Do the conversions:
174   for a in args:
175     joinwith = [f[2:] for f in a.flags if f.startswith("J:")]
176     if len(joinwith) > 0:
177       yield 'preShareData(%s, &%s);' % (joinwith[0], a.nm)
178     if 'O' in a.flags:
179       continue
180     if a.ty in (conversion_types + aggregate.keys()):
181       if a.init:
182         pred = '(pyobj_%s != NULL) && ' % a.nm
183       else:
184         pred = ''
185       yield '  if (%s!convert_to_%s(pyobj_%s, &%s, "%s")) return NULL;' % (pred, safename(a.ty), a.nm, a.nm, a.nm)
186
187   def invokename(a):
188     if 'K' in a.flags:
189       prefix = "(const CvArr **)"
190     elif 'O' in a.flags and not 'A' in a.flags:
191       prefix = "&"
192     else:
193       prefix = ""
194     if a.ty in aggregate:
195       return prefix + aggregate[a.ty].replace('!', a.nm)
196     else:
197       return prefix + a.nm
198
199   def funcname(s):
200     # The name by which the function is called, in C
201     if s.startswith("CV"):
202       return s
203     else:
204       return "cv" + s
205   tocall = '%s(%s)' % (funcname(name), ", ".join(invokename(a) for a in args))
206   if ty == None:
207     yield '  ERRWRAP(%s);' % tocall
208     yield '  Py_RETURN_NONE;'
209   else:
210     Rtypes = [
211       'int',
212       'double',
213       'CvCapture*',
214       'CvVideoWriter*',
215       'CvPOSITObject*',
216       'CvScalar',
217       'CvSize',
218       'CvRect',
219       'CvSeq*',
220       'CvBox2D',
221       'CvSeqOfCvAvgComp*',
222       'CvSeqOfCvConvexityDefect*',
223       'CvSeqOfCvStarKeypoint*',
224       'CvSeqOfCvSURFPoint*',
225       'CvSeqOfCvSURFDescriptor*',
226       'CvContourTree*',
227       'IplConvKernel*',
228       'IplImage*',
229       'CvMat*',
230       'CvMatND*',
231       'CvPoint2D32f_4',
232       'CvRNG',
233       'CvSubdiv2D*',
234       'CvSubdiv2DPoint*',
235       'CvSubdiv2DEdge',
236       'ROIplImage*',
237       'float',
238       'generic',
239       'unsigned' ]
240
241     if ty in Rtypes:
242       yield '  %s r;' % (ty)
243       yield '  ERRWRAP(r = %s);' % (tocall)
244       yield '  return FROM_%s(r);' % safename(ty)
245     else:
246       all_returns = ty.split(",")
247       return_value_from_call = len(set(Rtypes) & set(all_returns)) != 0
248       if return_value_from_call:
249         yield '  %s r;' % list(set(Rtypes) & set(all_returns))[0]
250         yield '  ERRWRAP(r = %s);' % (tocall)
251       else:
252         yield '  ERRWRAP(%s);' % (tocall)
253       typed = dict([ (a.nm,a.ty) for a in args])
254       for i in range(len(all_returns)):
255         if all_returns[i] in Rtypes:
256           typed['r'] = all_returns[i]
257           all_returns[i] = "r"
258       if len(all_returns) == 1:
259         af = dict([ (a.nm,a.flags) for a in args])
260         joinwith = [f[2:] for f in af.get(all_returns[0], []) if f.startswith("J:")]
261         if len(joinwith) > 0:
262             yield '  return shareData(pyobj_%s, %s, %s);' % (joinwith[0], joinwith[0], all_returns[0])
263         else:
264             yield '  return FROM_%s(%s);' % (safename(typed[all_returns[0]]), all_returns[0])
265       else:
266         yield '  return Py_BuildValue("%s", %s);' % ("N" * len(all_returns), ", ".join(["FROM_%s(%s)" % (safename(typed[n]), n) for n in all_returns]))
267
268   yield '}'
269
270 gen_c = [ open("generated%d.i" % i, "w") for i in range(3) ]
271
272 print "Generated %d functions" % len(api)
273 for nm,args,ty in sorted(api):
274
275   # Figure out docstring into ds_*
276   ds_args = []
277   mandatory = [a.nm for a in args if not ('O' in a.flags) and not a.init]
278   optional = [a.nm for a in args if not ('O' in a.flags) and a.init]
279   ds_args = ", ".join(mandatory)
280   def o2s(o):
281     if o == []:
282         return ""
283     else:
284         return ' [, %s%s]' % (o[0], o2s(o[1:]))
285   ds_args += o2s(optional)
286
287   ds = "%s(%s) -> %s" % (nm, ds_args, str(ty))
288   print ds
289
290   if has_optional(args):
291       entry = '{"%%s", (PyCFunction)pycv%s, METH_KEYWORDS, "%s"},' % (cname(nm), ds)
292   else:
293       entry = '{"%%s", pycv%s, METH_VARARGS, "%s"},' % (cname(nm), ds)
294   print >>gen_c[1], entry % (nm)
295   if nm.startswith('CV_'):
296     print >>gen_c[1], entry % (nm[3:])
297   for l in gen(nm,args,ty):
298     print >>gen_c[0], l
299
300 for l in open("%s/defs" % sys.argv[1]):
301   print >>gen_c[2], "PUBLISH(%s);" % l.split()[1]
302
303 for f in gen_c:
304   f.close()