|
1 | | -''' |
2 | | -OpenCV Camera: Implement CameraBase with OpenCV |
3 | | -''' |
4 | | - |
5 | | -# |
6 | | -# TODO: make usage of thread or multiprocess |
7 | | -# |
8 | | - |
9 | | -from __future__ import division |
10 | | - |
11 | 1 | __all__ = ('CameraOpenCV') |
12 | 2 |
|
13 | | - |
14 | 3 | from kivy.logger import Logger |
15 | 4 | from kivy.clock import Clock |
16 | 5 | from kivy.graphics.texture import Texture |
17 | | -from kivy.core.camera import CameraBase |
18 | 6 | from kivy.utils import platform |
19 | | - |
20 | | -try: |
21 | | - # opencv 1 case |
22 | | - import opencv as cv |
23 | | - |
24 | | - try: |
25 | | - import opencv.highgui as hg |
26 | | - except ImportError: |
27 | | - class Hg(object): |
28 | | - ''' |
29 | | - On OSX, not only are the import names different, |
30 | | - but the API also differs. |
31 | | - There is no module called 'highgui' but the names are |
32 | | - directly available in the 'cv' module. |
33 | | - Some of them even have a different names. |
34 | | -
|
35 | | - Therefore we use this proxy object. |
36 | | - ''' |
37 | | - |
38 | | - def __getattr__(self, attr): |
39 | | - if attr.startswith('cv'): |
40 | | - attr = attr[2:] |
41 | | - got = getattr(cv, attr) |
42 | | - return got |
43 | | - |
44 | | - hg = Hg() |
45 | | - |
46 | | -except ImportError: |
47 | | - # opencv 2 case (and also opencv 3, because it still uses cv2 module name) |
48 | | - try: |
49 | | - import cv2 |
50 | | - # here missing this OSX specific highgui thing. |
51 | | - # I'm not on OSX so don't know if it is still valid in opencv >= 2 |
52 | | - except ImportError: |
53 | | - raise |
54 | | - |
| 7 | +from kivy.graphics import Color, Rectangle, Rotate, Fbo |
| 8 | +import cv2 |
| 9 | +from . import CameraBase |
55 | 10 |
|
56 | 11 | class CameraOpenCV(CameraBase): |
57 | | - ''' |
58 | | - Implementation of CameraBase using OpenCV |
59 | | - ''' |
60 | | - _update_ev = None |
61 | 12 |
|
62 | 13 | def __init__(self, **kwargs): |
63 | | - # we will need it, because constants have |
64 | | - # different access paths between ver. 2 and 3 |
65 | | - try: |
66 | | - self.opencvMajorVersion = int(cv.__version__[0]) |
67 | | - except NameError: |
68 | | - self.opencvMajorVersion = int(cv2.__version__[0]) |
69 | | - |
70 | 14 | self._device = None |
| 15 | + self._update_ev = None |
71 | 16 | super(CameraOpenCV, self).__init__(**kwargs) |
72 | 17 |
|
73 | 18 | def init_camera(self): |
74 | | - # The only change in this file |
75 | | - if platform == 'win' and self.opencvMajorVersion in (2, 3, 4): |
| 19 | + self._format = 'bgr' |
| 20 | + if platform == 'win': |
76 | 21 | self._index = self._index + cv2.CAP_DSHOW |
77 | | - # consts have changed locations between versions 2 and 3 |
78 | | - if self.opencvMajorVersion in (3, 4): |
79 | | - PROPERTY_WIDTH = cv2.CAP_PROP_FRAME_WIDTH |
80 | | - PROPERTY_HEIGHT = cv2.CAP_PROP_FRAME_HEIGHT |
81 | | - PROPERTY_FPS = cv2.CAP_PROP_FPS |
82 | | - elif self.opencvMajorVersion == 2: |
83 | | - PROPERTY_WIDTH = cv2.cv.CV_CAP_PROP_FRAME_WIDTH |
84 | | - PROPERTY_HEIGHT = cv2.cv.CV_CAP_PROP_FRAME_HEIGHT |
85 | | - PROPERTY_FPS = cv2.cv.CV_CAP_PROP_FPS |
86 | | - elif self.opencvMajorVersion == 1: |
87 | | - PROPERTY_WIDTH = cv.CV_CAP_PROP_FRAME_WIDTH |
88 | | - PROPERTY_HEIGHT = cv.CV_CAP_PROP_FRAME_HEIGHT |
89 | | - PROPERTY_FPS = cv.CV_CAP_PROP_FPS |
90 | | - |
91 | | - Logger.debug('Using opencv ver.' + str(self.opencvMajorVersion)) |
92 | | - |
93 | | - if self.opencvMajorVersion == 1: |
94 | | - # create the device |
95 | | - self._device = hg.cvCreateCameraCapture(self._index) |
96 | | - # Set preferred resolution |
97 | | - cv.SetCaptureProperty(self._device, cv.CV_CAP_PROP_FRAME_WIDTH, |
98 | | - self.resolution[0]) |
99 | | - cv.SetCaptureProperty(self._device, cv.CV_CAP_PROP_FRAME_HEIGHT, |
100 | | - self.resolution[1]) |
101 | | - # and get frame to check if it's ok |
102 | | - frame = hg.cvQueryFrame(self._device) |
103 | | - # Just set the resolution to the frame we just got, but don't use |
104 | | - # self.resolution for that as that would cause an infinite |
105 | | - # recursion with self.init_camera (but slowly as we'd have to |
106 | | - # always get a frame). |
107 | | - self._resolution = (int(frame.width), int(frame.height)) |
108 | | - # get fps |
109 | | - self.fps = cv.GetCaptureProperty(self._device, cv.CV_CAP_PROP_FPS) |
110 | | - |
111 | | - elif self.opencvMajorVersion in (2, 3, 4): |
112 | | - # create the device |
113 | | - self._device = cv2.VideoCapture(self._index) |
114 | | - # Set preferred resolution |
115 | | - self._device.set(PROPERTY_WIDTH, |
116 | | - self.resolution[0]) |
117 | | - self._device.set(PROPERTY_HEIGHT, |
118 | | - self.resolution[1]) |
119 | | - # and get frame to check if it's ok |
120 | | - ret, frame = self._device.read() |
121 | | - |
122 | | - # source: |
123 | | - # http://stackoverflow.com/questions/32468371/video-capture-propid-parameters-in-opencv # noqa |
124 | | - self._resolution = (int(frame.shape[1]), int(frame.shape[0])) |
125 | | - # get fps |
126 | | - self.fps = self._device.get(PROPERTY_FPS) |
127 | | - |
| 22 | + self._device = cv2.VideoCapture(self._index) |
| 23 | + self._device.set(cv2.CAP_PROP_FRAME_WIDTH, self._resolution[0]) |
| 24 | + self._device.set(cv2.CAP_PROP_FRAME_HEIGHT, self._resolution[1]) |
| 25 | + ret, frame = self._device.read() |
| 26 | + self._resolution = (int(frame.shape[1]), int(frame.shape[0])) |
| 27 | + self.fps = self._device.get(cv2.CAP_PROP_FPS) |
128 | 28 | if self.fps == 0 or self.fps == 1: |
129 | 29 | self.fps = 1.0 / 30 |
130 | 30 | elif self.fps > 1: |
131 | 31 | self.fps = 1.0 / self.fps |
| 32 | + self.crop = self._context.crop_for_aspect_orientation(*self._resolution) |
| 33 | + self.stopped = True |
132 | 34 |
|
133 | | - if not self.stopped: |
134 | | - self.start() |
135 | | - |
136 | | - def _update(self, dt): |
| 35 | + def update(self, dt): |
137 | 36 | if self.stopped: |
138 | 37 | return |
139 | 38 | if self._texture is None: |
140 | | - # Create the texture |
141 | 39 | self._texture = Texture.create(self._resolution) |
142 | 40 | self._texture.flip_vertical() |
143 | | - self.dispatch('on_load') |
| 41 | + self._context.on_load() |
144 | 42 | try: |
145 | 43 | ret, frame = self._device.read() |
146 | | - self._format = 'bgr' |
147 | | - try: |
148 | | - self._buffer = frame.imageData |
149 | | - self._copy_to_gpu() |
150 | | - except AttributeError: |
151 | | - # frame is already of type ndarray |
152 | | - # which can be reshaped to 1-d. |
| 44 | + if ret: |
153 | 45 | self._buffer = frame.reshape(-1) |
154 | 46 | self._copy_to_gpu() |
155 | | - except: |
156 | | - pass |
157 | | - except: |
| 47 | + if self.photo_capture: |
| 48 | + self.photo_capture = False |
| 49 | + cropped = frame[self.crop[1]: self.crop[1]+self.crop[3], |
| 50 | + self.crop[0]: self.crop[0]+self.crop[2], :] |
| 51 | + cv2.imwrite(self.photo_path, cropped) |
| 52 | + if self.photo_callback: |
| 53 | + self.photo_callback(self.photo_path) |
| 54 | + if self.video_capture: |
| 55 | + cropped = frame[self.crop[1]: self.crop[1]+self.crop[3], |
| 56 | + self.crop[0]: self.crop[0]+self.crop[2], :] |
| 57 | + self.video_stream.write(cropped) |
| 58 | + |
| 59 | + except Exception as e: |
158 | 60 | Logger.exception('OpenCV: Couldn\'t get image from Camera') |
159 | 61 |
|
160 | 62 | def start(self): |
161 | | - super(CameraOpenCV, self).start() |
| 63 | + self.stopped = False |
| 64 | + self.photo_capture = False |
| 65 | + self.video_capture = False |
162 | 66 | if self._update_ev is not None: |
163 | 67 | self._update_ev.cancel() |
164 | | - self._update_ev = Clock.schedule_interval(self._update, self.fps) |
| 68 | + self._update_ev = Clock.schedule_interval(self.update, 1/30) |
165 | 69 |
|
166 | 70 | def stop(self): |
167 | | - super(CameraOpenCV, self).stop() |
| 71 | + self.stopped = True |
| 72 | + self._device = None |
168 | 73 | if self._update_ev is not None: |
169 | 74 | self._update_ev.cancel() |
170 | 75 | self._update_ev = None |
| 76 | + |
| 77 | + def photo(self, path, callback): |
| 78 | + self.photo_capture = True |
| 79 | + self.photo_path = path |
| 80 | + self.photo_callback = callback |
| 81 | + |
| 82 | + def video_start(self, path, callback): |
| 83 | + self.video_capture = True |
| 84 | + self.video_path = path |
| 85 | + self.video_callback = callback |
| 86 | + size = (self.crop[2], self.crop[3]) |
| 87 | + rate = Clock.get_fps() |
| 88 | + fourcc = cv2.VideoWriter_fourcc(*'mp4v') |
| 89 | + self.video_stream = cv2.VideoWriter(path, fourcc, rate, size) |
| 90 | + |
| 91 | + def video_stop(self): |
| 92 | + self.video_capture = False |
| 93 | + self.video_stream.release() |
| 94 | + |
0 commit comments