|
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