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

Commit 0211602

Browse files
add picam2 usb camera
1 parent e27ba99 commit 0211602

3 files changed

Lines changed: 57 additions & 21 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,9 @@ Depends on the Linux flavor, but commonly:
746746
### Picamera
747747
This uses either the Picamera or Picamera2 package, depending on which is installed (they are mutually exclusive).
748748

749-
If Picamera2 is installed there are additional features for native Pi cameras: higher resolution photos, video (optionally with audio), zoom, pan when zoom'd, sensor rotation. However USB cameras are not currently supported.
749+
If Picamera2 is installed there are additional features for native Pi cameras: higher resolution photos, video (optionally with audio), zoom, pan when zoom'd, sensor rotation.
750+
751+
For Picamera2 USB cameras are available with physical rotation support, including for photo and screen shot capture. However zoom and video capture is not available.
750752

751753
### AVFoundation
752754
Pre-installed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = camera4kivy
3-
version = 0.3.0
3+
version = 0.3.1
44
author = Robert Flatt
55
description = Yet Another Camera for Kivy
66
long_description = file: index.md

src/camera4kivy/based_on_kivy_core/camera/camera_picamera2.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
'''
44

55
__all__ = ('CameraPiCamera2', )
6+
import io
67

78
from kivy.logger import Logger
89
from kivy.clock import Clock, mainthread
@@ -39,6 +40,7 @@ def __init__(self):
3940
self.y = None
4041
self.u = None
4142
self.v = None
43+
self.mjpeg = None
4244
self.stream_size = ()
4345

4446
# Sync event loops
@@ -48,6 +50,10 @@ def sync_yuv(self,y,u,v):
4850
self.y = y
4951
self.u = u
5052
self.v = v
53+
54+
@mainthread
55+
def sync_mjpeg(self,mjpeg):
56+
self.mjpeg = mjpeg
5157

5258
# Request Handlers
5359
###################
@@ -75,6 +81,9 @@ def render_request(self, request):
7581
v = bytes(mm[end_u:])
7682
self.sync_yuv(y,u,v)
7783

84+
elif self.stream_fmt == 'MJPEG':
85+
self.sync_mjpeg(bytes(mm))
86+
7887
elif self.stream_fmt and not self.mute:
7988
self.mute = True
8089
Logger.error(
@@ -163,6 +172,7 @@ def __init__(self):
163172
self._rotate = 0
164173
self.video_recording = False
165174
self.audio = False
175+
self.is_usb = False
166176

167177
# Start
168178
# Choose sensor, configure pc2
@@ -184,22 +194,33 @@ def start(self, index):
184194
Logger.warning('C4k Picamera2: Requested camera '+ str(index) +\
185195
' not found, using ' + str(num_cameras -1) + ' .')
186196
index = num_cameras -1
187-
Id = Picamera2.global_camera_info()[index]['Id']
188-
if 'i2c' not in Id.lower():
189-
Logger.error('C4k Picamera2: USB cameras not supported.')
190-
return
191197

192198
# initialize
199+
Id = Picamera2.global_camera_info()[index]['Id']
193200
self.picam2 = Picamera2(index)
194-
self.configure_sensor(index)
201+
if 'i2c' in Id.lower():
202+
self.is_usb = False
203+
self.create_picam_configurations(index)
204+
else:
205+
self.is_usb = True
206+
self.create_usb_configurations()
207+
self.base_scaler_crop = self.crop_limits
208+
self.scaler_crop = self.crop_limits
195209
self.picam2.configure(self.preview_config)
196210
self.sensor= SensorInterface()
197211
self.picam2.start_preview(self.sensor)
198-
self.base_scaler_crop = self.crop_limits
199-
self.scaler_crop = self.crop_limits
200212
self.picam2.start()
201213

202-
def configure_sensor(self, index):
214+
def create_usb_configurations(self):
215+
self.crop_limits = None
216+
self.preview_config = self.picam2.create_preview_configuration(
217+
{"format": "MJPEG"})
218+
self.photo_config = self.picam2.create_still_configuration(
219+
{"format": "MJPEG"})
220+
self.video_config = self.picam2.create_video_configuration(
221+
{"format": "MJPEG"}) # Not supported
222+
223+
def create_picam_configurations(self, index):
203224
# Sensor configuration
204225
size_s = (0,0)
205226
wide = self._context.aspect_ratio == '16:9'
@@ -281,6 +302,12 @@ def update(self):
281302
if ss and ss.y:
282303
return self._yuv_to_rgba('YUV420', ss.y, ss.u, ss.v,
283304
ss.stream_size, self._resolution)
305+
elif ss and ss.mjpeg:
306+
img = Image.open(io.BytesIO(ss.mjpeg))
307+
img = img.convert('RGBA')
308+
img = img.rotate(self._rotate)
309+
img = img.resize(self._resolution)
310+
return img.tobytes()
284311
return None
285312

286313
# Zoom and Drag events
@@ -333,20 +360,19 @@ def capture_file(self, file_output, callback):
333360
request = self.picam2.capture_request()
334361
size = request.config['main']['size']
335362
with _MappedBuffer(request,'main') as pixels:
336-
img = Image.frombytes('RGB', size, bytes(pixels))
363+
if self.is_usb:
364+
img = Image.open(io.BytesIO(pixels))
365+
else:
366+
img = Image.frombytes('RGB', size, bytes(pixels))
337367
request.release()
338368
if self._rotate in [90,270]:
339369
size = size[::-1]
340370
crop = self._context.crop_for_aspect_orientation(size[0],
341371
size[1])
342-
if self._rotate in [90,270]:
343-
t = crop[2]
344-
crop[2] = crop[3]
345-
crop[3] = t
346-
bottom = crop[3] - crop[1]
372+
bottom = crop[3] + crop[1]
347373
right = crop[2] + crop[0]
348-
img = img.crop((crop[0], crop[1], right, bottom))
349374
img = img.rotate(self._rotate, expand = True)
375+
img = img.crop((crop[0], crop[1], right, bottom))
350376
with open(file_output, 'wb') as fp:
351377
img.save(fp)
352378
if callback:
@@ -356,9 +382,12 @@ def capture_file(self, file_output, callback):
356382
def switch_config(self, new_config):
357383
self.picam2.stop()
358384
self.picam2.configure(new_config)
359-
self.picam2.controls.ScalerCrop = self.scaler_crop
360-
self.picam2.start()
361-
self.picam2.controls.ScalerCrop = self.scaler_crop
385+
if self.is_usb:
386+
self.picam2.start()
387+
else:
388+
self.picam2.controls.ScalerCrop = self.scaler_crop
389+
self.picam2.start()
390+
self.picam2.controls.ScalerCrop = self.scaler_crop
362391

363392
def photo(self, path, callback):
364393
if self.picam2 and self.sensor and not self.video_recording:
@@ -369,7 +398,10 @@ def photo(self, path, callback):
369398
# Video start/stop
370399
###############################
371400

372-
def video_start(self, filepath, callback):
401+
def video_start(self, filepath, callback):
402+
if self.is_usb:
403+
Logger.error('Camera4Kivy, USB video recording not supported.')
404+
return
373405
self.video_filepath = filepath
374406
self.video_callback = callback
375407
if self.picam2 and self.sensor:
@@ -381,6 +413,8 @@ def video_start(self, filepath, callback):
381413
self.picam2.start_encoder(encoder, output)
382414

383415
def video_stop(self):
416+
if self.is_usb:
417+
return
384418
self.picam2.stop_encoder()
385419
self.picam2.switch_mode(self.preview_config)
386420
self.video_recording = False

0 commit comments

Comments
 (0)