Skip to content
This repository was archived by the owner on Nov 14, 2023. It is now read-only.

Commit a087f8b

Browse files
committed
0.2.0
1 parent c5cfaa2 commit a087f8b

File tree

8 files changed

+276
-242
lines changed

8 files changed

+276
-242
lines changed

Diff for: setup.cfg

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = camera4kivy
3-
version = 0.1.0
3+
version = 0.2.0
44
author = Robert Flatt
55
description = Yet Another Camera for Kivy
66
long_description = file: README.md
@@ -17,9 +17,9 @@ classifiers =
1717
package_dir =
1818
= src
1919
packages = find:
20-
python_requires = >=3.6
20+
python_requires = >=3.7
2121
install_requires =
22-
gestures4kivy >= 0.1.0
22+
gestures4kivy >= 0.1.1
2323

2424
[options.packages.find]
2525
where = src

Diff for: src/camera4kivy/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
from .preview import Preview
2-
2+
from .preview import CameraProviderInfo

Diff for: src/camera4kivy/based_on_kivy_core/camera/__init__.py

+16-34
Original file line numberDiff line numberDiff line change
@@ -20,48 +20,27 @@
2020
from kivy.utils import platform
2121
from kivy.event import EventDispatcher
2222
from kivy.logger import Logger
23-
from kivy.core import core_select_lib
23+
from .. import core_select_lib
2424

2525

2626
class CameraBase(EventDispatcher):
27-
'''Abstract Camera Widget class.
28-
29-
Concrete camera classes must implement initialization and
30-
frame capturing to a buffer that can be uploaded to the gpu.
31-
32-
:Parameters:
33-
`index`: int
34-
Source index of the camera.
35-
`resolution`: tuple (int, int)
36-
Resolution to try to request from the camera.
37-
Used in the gstreamer pipeline by forcing the appsink caps
38-
to this resolution. If the camera doesn't support the resolution,
39-
a negotiation error might be thrown.
40-
41-
:Events:
42-
`on_load`
43-
Fired when the camera is loaded and the texture has become
44-
available.
45-
`on_texture`
46-
Fired each time the camera texture is updated.
47-
'''
48-
49-
__events__ = ('on_load', 'on_texture')
5027

5128
def __init__(self, **kwargs):
5229
kwargs.setdefault('stopped', False)
5330
kwargs.setdefault('index', 0)
31+
kwargs.setdefault('context', None)
5432
self.stopped = kwargs.get('stopped')
5533
self._resolution = kwargs.get('resolution')
5634
self._index = kwargs.get('index')
35+
self._context = kwargs.get('context')
5736
self._buffer = None
5837
self._format = 'rgb'
5938
self._texture = None
6039
self.capture_device = None
61-
super(CameraBase, self).__init__()
40+
super().__init__()
6241
self.init_camera()
63-
if not self.stopped:
64-
self.start()
42+
#if not self.stopped and not self._context:
43+
# self.start()
6544

6645
def _get_texture(self):
6746
return self._texture
@@ -90,16 +69,18 @@ def _copy_to_gpu(self):
9069
if self._texture is None:
9170
Logger.debug('Camera: copy_to_gpu() failed, _texture is None !')
9271
return
93-
self._texture.blit_buffer(self._buffer, colorfmt=self._format)
72+
self._texture.blit_buffer(self._buffer, colorfmt=self._format)
9473
self._buffer = None
95-
self.dispatch('on_texture')
74+
if self._context:
75+
self._context.on_texture()
76+
else:
77+
self.dispatch('on_texture')
9678

97-
def on_texture(self):
98-
pass
99-
100-
def on_load(self):
101-
pass
79+
#def on_texture(self):
80+
# pass
10281

82+
#def on_load(self):
83+
# pass
10384

10485
# Load the appropriate providers
10586
providers = ()
@@ -115,6 +96,7 @@ def on_load(self):
11596
elif platform == 'android':
11697
pass
11798
else:
99+
#providers += (('picamera2', 'camera_picamera2', 'CameraPiCamera2'), )
118100
providers += (('picamera', 'camera_picamera', 'CameraPiCamera'), )
119101
providers += (('gi', 'camera_gi', 'CameraGi'), )
120102
providers += (('opencv', 'camera_opencv', 'CameraOpenCV'), )
+55-131
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,94 @@
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-
111
__all__ = ('CameraOpenCV')
122

13-
143
from kivy.logger import Logger
154
from kivy.clock import Clock
165
from kivy.graphics.texture import Texture
17-
from kivy.core.camera import CameraBase
186
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
5510

5611
class CameraOpenCV(CameraBase):
57-
'''
58-
Implementation of CameraBase using OpenCV
59-
'''
60-
_update_ev = None
6112

6213
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-
7014
self._device = None
15+
self._update_ev = None
7116
super(CameraOpenCV, self).__init__(**kwargs)
7217

7318
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':
7621
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)
12828
if self.fps == 0 or self.fps == 1:
12929
self.fps = 1.0 / 30
13030
elif self.fps > 1:
13131
self.fps = 1.0 / self.fps
32+
self.crop = self._context.crop_for_aspect_orientation(*self._resolution)
33+
self.stopped = True
13234

133-
if not self.stopped:
134-
self.start()
135-
136-
def _update(self, dt):
35+
def update(self, dt):
13736
if self.stopped:
13837
return
13938
if self._texture is None:
140-
# Create the texture
14139
self._texture = Texture.create(self._resolution)
14240
self._texture.flip_vertical()
143-
self.dispatch('on_load')
41+
self._context.on_load()
14442
try:
14543
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:
15345
self._buffer = frame.reshape(-1)
15446
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:
15860
Logger.exception('OpenCV: Couldn\'t get image from Camera')
15961

16062
def start(self):
161-
super(CameraOpenCV, self).start()
63+
self.stopped = False
64+
self.photo_capture = False
65+
self.video_capture = False
16266
if self._update_ev is not None:
16367
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)
16569

16670
def stop(self):
167-
super(CameraOpenCV, self).stop()
71+
self.stopped = True
72+
self._device = None
16873
if self._update_ev is not None:
16974
self._update_ev.cancel()
17075
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

Comments
 (0)