video.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #!/usr/bin/env python
  2. '''
  3. Video capture sample.
  4. Sample shows how VideoCapture class can be used to acquire video
  5. frames from a camera of a movie file. Also the sample provides
  6. an example of procedural video generation by an object, mimicking
  7. the VideoCapture interface (see Chess class).
  8. 'create_capture' is a convenience function for capture creation,
  9. falling back to procedural video in case of error.
  10. Usage:
  11. video.py [--shotdir <shot path>] [source0] [source1] ...'
  12. sourceN is an
  13. - integer number for camera capture
  14. - name of video file
  15. - synth:<params> for procedural video
  16. Synth examples:
  17. synth:bg=lena.jpg:noise=0.1
  18. synth:class=chess:bg=lena.jpg:noise=0.1:size=640x480
  19. Keys:
  20. ESC - exit
  21. SPACE - save current frame to <shot path> directory
  22. '''
  23. # Python 2/3 compatibility
  24. from __future__ import print_function
  25. import numpy as np
  26. import cv2 as cv
  27. import re
  28. from numpy import pi, sin, cos
  29. # local modules
  30. from tst_scene_render import TestSceneRender
  31. import common
  32. class VideoSynthBase(object):
  33. def __init__(self, size=None, noise=0.0, bg = None, **params):
  34. self.bg = None
  35. self.frame_size = (640, 480)
  36. if bg is not None:
  37. self.bg = cv.imread(cv.samples.findFile(bg))
  38. h, w = self.bg.shape[:2]
  39. self.frame_size = (w, h)
  40. if size is not None:
  41. w, h = map(int, size.split('x'))
  42. self.frame_size = (w, h)
  43. self.bg = cv.resize(self.bg, self.frame_size)
  44. self.noise = float(noise)
  45. def render(self, dst):
  46. pass
  47. def read(self, dst=None):
  48. w, h = self.frame_size
  49. if self.bg is None:
  50. buf = np.zeros((h, w, 3), np.uint8)
  51. else:
  52. buf = self.bg.copy()
  53. self.render(buf)
  54. if self.noise > 0.0:
  55. noise = np.zeros((h, w, 3), np.int8)
  56. cv.randn(noise, np.zeros(3), np.ones(3)*255*self.noise)
  57. buf = cv.add(buf, noise, dtype=cv.CV_8UC3)
  58. return True, buf
  59. def isOpened(self):
  60. return True
  61. class Book(VideoSynthBase):
  62. def __init__(self, **kw):
  63. super(Book, self).__init__(**kw)
  64. backGr = cv.imread(cv.samples.findFile('graf1.png'))
  65. fgr = cv.imread(cv.samples.findFile('box.png'))
  66. self.render = TestSceneRender(backGr, fgr, speed = 1)
  67. def read(self, dst=None):
  68. noise = np.zeros(self.render.sceneBg.shape, np.int8)
  69. cv.randn(noise, np.zeros(3), np.ones(3)*255*self.noise)
  70. return True, cv.add(self.render.getNextFrame(), noise, dtype=cv.CV_8UC3)
  71. class Cube(VideoSynthBase):
  72. def __init__(self, **kw):
  73. super(Cube, self).__init__(**kw)
  74. self.render = TestSceneRender(cv.imread(cv.samples.findFile('pca_test1.jpg')), deformation = True, speed = 1)
  75. def read(self, dst=None):
  76. noise = np.zeros(self.render.sceneBg.shape, np.int8)
  77. cv.randn(noise, np.zeros(3), np.ones(3)*255*self.noise)
  78. return True, cv.add(self.render.getNextFrame(), noise, dtype=cv.CV_8UC3)
  79. class Chess(VideoSynthBase):
  80. def __init__(self, **kw):
  81. super(Chess, self).__init__(**kw)
  82. w, h = self.frame_size
  83. self.grid_size = sx, sy = 10, 7
  84. white_quads = []
  85. black_quads = []
  86. for i, j in np.ndindex(sy, sx):
  87. q = [[j, i, 0], [j+1, i, 0], [j+1, i+1, 0], [j, i+1, 0]]
  88. [white_quads, black_quads][(i + j) % 2].append(q)
  89. self.white_quads = np.float32(white_quads)
  90. self.black_quads = np.float32(black_quads)
  91. fx = 0.9
  92. self.K = np.float64([[fx*w, 0, 0.5*(w-1)],
  93. [0, fx*w, 0.5*(h-1)],
  94. [0.0,0.0, 1.0]])
  95. self.dist_coef = np.float64([-0.2, 0.1, 0, 0])
  96. self.t = 0
  97. def draw_quads(self, img, quads, color = (0, 255, 0)):
  98. img_quads = cv.projectPoints(quads.reshape(-1, 3), self.rvec, self.tvec, self.K, self.dist_coef) [0]
  99. img_quads.shape = quads.shape[:2] + (2,)
  100. for q in img_quads:
  101. cv.fillConvexPoly(img, np.int32(q*4), color, cv.LINE_AA, shift=2)
  102. def render(self, dst):
  103. t = self.t
  104. self.t += 1.0/30.0
  105. sx, sy = self.grid_size
  106. center = np.array([0.5*sx, 0.5*sy, 0.0])
  107. phi = pi/3 + sin(t*3)*pi/8
  108. c, s = cos(phi), sin(phi)
  109. ofs = np.array([sin(1.2*t), cos(1.8*t), 0]) * sx * 0.2
  110. eye_pos = center + np.array([cos(t)*c, sin(t)*c, s]) * 15.0 + ofs
  111. target_pos = center + ofs
  112. R, self.tvec = common.lookat(eye_pos, target_pos)
  113. self.rvec = common.mtx2rvec(R)
  114. self.draw_quads(dst, self.white_quads, (245, 245, 245))
  115. self.draw_quads(dst, self.black_quads, (10, 10, 10))
  116. classes = dict(chess=Chess, book=Book, cube=Cube)
  117. presets = dict(
  118. empty = 'synth:',
  119. lena = 'synth:bg=lena.jpg:noise=0.1',
  120. chess = 'synth:class=chess:bg=lena.jpg:noise=0.1:size=640x480',
  121. book = 'synth:class=book:bg=graf1.png:noise=0.1:size=640x480',
  122. cube = 'synth:class=cube:bg=pca_test1.jpg:noise=0.0:size=640x480'
  123. )
  124. def create_capture(source = 0, fallback = presets['chess']):
  125. '''source: <int> or '<int>|<filename>|synth [:<param_name>=<value> [:...]]'
  126. '''
  127. source = str(source).strip()
  128. # Win32: handle drive letter ('c:', ...)
  129. source = re.sub(r'(^|=)([a-zA-Z]):([/\\a-zA-Z0-9])', r'\1?disk\2?\3', source)
  130. chunks = source.split(':')
  131. chunks = [re.sub(r'\?disk([a-zA-Z])\?', r'\1:', s) for s in chunks]
  132. source = chunks[0]
  133. try: source = int(source)
  134. except ValueError: pass
  135. params = dict( s.split('=') for s in chunks[1:] )
  136. cap = None
  137. if source == 'synth':
  138. Class = classes.get(params.get('class', None), VideoSynthBase)
  139. try: cap = Class(**params)
  140. except: pass
  141. else:
  142. cap = cv.VideoCapture(source)
  143. if 'size' in params:
  144. w, h = map(int, params['size'].split('x'))
  145. cap.set(cv.CAP_PROP_FRAME_WIDTH, w)
  146. cap.set(cv.CAP_PROP_FRAME_HEIGHT, h)
  147. if cap is None or not cap.isOpened():
  148. print('Warning: unable to open video source: ', source)
  149. if fallback is not None:
  150. return create_capture(fallback, None)
  151. return cap
  152. if __name__ == '__main__':
  153. import sys
  154. import getopt
  155. print(__doc__)
  156. args, sources = getopt.getopt(sys.argv[1:], '', 'shotdir=')
  157. args = dict(args)
  158. shotdir = args.get('--shotdir', '.')
  159. if len(sources) == 0:
  160. sources = [ 0 ]
  161. caps = list(map(create_capture, sources))
  162. shot_idx = 0
  163. while True:
  164. imgs = []
  165. for i, cap in enumerate(caps):
  166. ret, img = cap.read()
  167. imgs.append(img)
  168. cv.imshow('capture %d' % i, img)
  169. ch = cv.waitKey(1)
  170. if ch == 27:
  171. break
  172. if ch == ord(' '):
  173. for i, img in enumerate(imgs):
  174. fn = '%s/shot_%d_%03d.bmp' % (shotdir, i, shot_idx)
  175. cv.imwrite(fn, img)
  176. print(fn, 'saved')
  177. shot_idx += 1
  178. cv.destroyAllWindows()