转自:点击打开链接
5、picamera的高级使用
下面的这些实例包含了picamera的一些高级使用方式,可能需要有一些图像开发经验才能掌握。所以请随时提出改进或更多的实例。
5.1、无损格式图像采集(YUV格式)
import time
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (100, 100)
camera.start_preview()
time.sleep(2)
camera.capture('image.data', 'yuv')
如果你不想损失拍摄图像的细节(由于jpeg是有损压缩),那么你可以通过PNG来接收拍摄的图像(PNG为无损压缩格式),然而某些应用需要YUV(YUV是被欧洲电视系统所采用的一种颜色编码方法)这种数字压缩格式的图像,对于这点需求可以用‘yuv’格式来压缩这些数据:YUV具体是采用YUV420【还有YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411等】的格式来压缩,这意味着,首先,数据的Y(亮度)值,这个值必须在全分辨率中都包含(用于计算分辨率中每一个像素的Y值),然后是U(色彩饱和度)值,亮度作用于色彩饱和度之上,最后是V(色度)值。每一个色彩与色度之上都包含四分之一的亮度。表格如下:
需要注意的是,输出到未编码格式时,摄像头需要图片的分辨率,水平分辨率上位32位的本书,垂直分辨率为16的倍数。例如,如果请求分辨率为100X100,那么实际捕获到的图像为分辨率128X112的像素数据。
鉴于YUV420格式的每个像素都包含1.5个字节的数据(每个像素包含1个字节的Y值,每四个像素包含一个UV值),并考虑到分辨率,一个100x100的yuv图像的大小将是:
前14336字节的数据为Y值,然后3584字节的数据(128x112/4)为U值,最后3584字节数据为V值。
下面这个实例演示了捕捉YUV图像数据,并将数据加载到numpy,然后将其转换为有效的RGB图像格式:
from __future__ import division
import time
import picamera
import numpy as np
width = 100
height = 100
stream = open('image.data', 'w+b')
# 捕获格式为YUV的图像
with picamera.PiCamera() as camera:
camera.resolution = (width, height)
camera.start_preview()
time.sleep(2)
camera.capture(stream, 'yuv')
# 像流指针指向开始
stream.seek(0)
# 计算实际图像的像素数
fwidth = (width + 31) // 32 * 32
fheight = (height + 15) // 16 * 16
# 然后从流中读出Y的值
Y = np.fromfile(stream, dtype=np.uint8, count=fwidth*fheight).\
reshape((fheight, fwidth))
# 最后将流中UV的值读出
U = np.fromfile(stream, dtype=np.uint8, count=(fwidth//2)*(fheight//2)).\
reshape((fheight//2, fwidth//2)).\
repeat(2, axis=0).repeat(2, axis=1)
V = np.fromfile(stream, dtype=np.uint8, count=(fwidth//2)*(fheight//2)).\
reshape((fheight//2, fwidth//2)).\
repeat(2, axis=0).repeat(2, axis=1)
# 将堆栈中的图像转换为实际的分辨率
YUV = np.dstack((Y, U, V))[:height, :width, :].astype(np.float)
YUV[:, :, 0] = YUV[:, :, 0] - 16 # Offset Y by 16
YUV[:, :, 1:] = YUV[:, :, 1:] - 128 # Offset UV by 128
# 将YUV转换成ITU-R BT.601版本(SDTV)的数据
# Y U V
M = np.array([[1.164, 0.000, 1.596], # R
[1.164, -0.392, -0.813], # G
[1.164, 2.017, 0.000]]) # B
# 最后输出RGB数据
RGB = YUV.dot(M.T).clip(0, 255).astype(np.uint8)
你可能注意到,在实例中我们创建文件使用了open方法,而不是io.open(),这是因为numpy的fromfile()只接受真实的文件对象。
现在这个实例已经封装在PiYUVArray类中,所以代码可以简化成:
import time
import picamera
import picamera.array
with picamera.PiCamera() as camera:
with picamera.array.PiYUVArray(camera) as stream:
camera.resolution = (100, 100)
camera.start_preview()
time.sleep(2)
camera.capture(stream, 'yuv')
# 显示YUV图像大小
print(stream.array.shape)
# 显示转换成RGB图像后文件的大小
print(stream.rgb_array.shape)
最后可以通过camera.capture(stream, 'rgb')
来直接让摄像头输出rgb数据,来替代以上脚本。
注意,在版本1.0中的format格式“raw”现在已经变更为YUV,若使用最新的库,请将格式修改成最新版。
从1.5版以后加入了picamera.array模块
5.2、无损格式图像采集(RGB格式)
RGB格式与YUV格式现在争议比较大,不过都是相当有益的讨论。在picamera上输出RGB格式的数据非常简单,只需要跳动capture函数将捕获的图像格式设置为RGB即可。
import time
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (100, 100)
camera.start_preview()
time.sleep(2)
camera.capture('image.data', 'rgb')
计算RGB图像数据的大小与YUV相同,首先会调整分辨率(参考YUV分辨率调整),其次,每个像素块在RGB上是占用3bytes的数据(红绿蓝三色各占1byte的数据),因此捕获一个100x100的图像,产生的数据如下:
由此可见,RGB的数据是由红绿蓝三色的数据结合产生,其顺序为,第一个字节为红色(0,1)第二个字节为绿色(0,0)最后一个是蓝色的字节。
然后若想将RGB数据转换成Numpy的话如下:
from __future__ import division
width = 100
height = 100
stream = open('image.data', 'w+b')
# 设置捕获类型为RGB
with picamera.PiCamera() as camera:
camera.resolution = (width, height)
camera.start_preview()
time.sleep(2)
camera.capture(stream, 'rgb')
# 将指针指向数据开始
stream.seek(0)
# 计算实际的图片大小
fwidth = (width + 31) // 32 * 32
fheight = (height + 15) // 16 * 16
# 将图像读取进numpy之中
image = np.fromfile(stream, dtype=np.uint8).\
reshape((fheight, fwidth, 3))[:height, :width, :]
# 如果你希望将图像的字节浮点数控制在0到1之间,添加如下代码
image = image.astype(np.float, copy=False)
image = image / 255.0
现在这个实例已经被封装到pirgbarray类中,所以可以简化成如下代码:
import time
import picamera
import picamera.array
with picamera.PiCamera() as camera:
with picamera.array.PiRGBArray(camera) as stream:
camera.resolution = (100, 100)
camera.start_preview()
time.sleep(2)
camera.capture(stream, 'rgb')
# 输出rgb图像的大小
print(stream.array.shape)
注意,在版本1.0中的format格式“raw”现在已经变更为RGB,若使用最新的库,请将格式修改成最新版。
从1.5版以后加入了picamera.array模块。
5.3、图像的快速捕捉及快速处理
树莓派的摄像头可以快速的捕捉一组图像序列,将之解码成jpeg格式(通过设置usb_video_port参数),但是使用这个功能需要注意几点:
所有的捕捉方法都支持use_video_port选项,但方法不同所捕捉图像的能力也有所不同。所以虽然capturehe和capture_continuous方法都支持use_video_prot功能,但最好使用capture_continuous来实现快速捕捉图片这个功能(因为capture_continuous不会每次都初始化解码器)。作者在测试时,这个方法最高可以支持在30fps下获取分辨率为1024x768的图片。
通常情况下,capture_continuous方法特别适合与捕获固定帧数的图像,比如下面这个例子:
import time
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (1024, 768)
camera.framerate = 30
camera.start_preview()
time.sleep(2)
camera.capture_sequence([
'image1.jpg',
'image2.jpg',
'image3.jpg',
'image4.jpg',
'image5.jpg',
])
我们可以细化一下这个例子,可以指定一个循环序列,这样我们就没必要去手动指定每一个文件名了:
import time
import picamera
frames = 60
with picamera.PiCamera() as camera:
camera.resolution = (1024, 768)
camera.framerate = 30
camera.start_preview()
# 摄像头预热
time.sleep(2)
start = time.time()
camera.capture_sequence([
'image%02d.jpg' % i
for i in range(frames)
], use_video_port=True)
finish = time.time()
print('Captured %d frames at %.2ffps' % (
frames,
frames / (finish - start)))
然而,这仍然无法满足我们在一定的条件下捕捉任意帧数的图片,为此我们需要生成一个方法,通过调用方法来获取文件名:
import time
import picamera
frames = 60
def filenames():
frame = 0
while frame < frames:
yield 'image%02d.jpg' % frame
frame += 1
with picamera.PiCamera() as camera:
camera.resolution = (1024, 768)
camera.framerate = 30
camera.start_preview()
# 摄像头预热
time.sleep(2)
start = time.time()
camera.capture_sequence(filenames(), use_video_port=True)
finish = time.time()
print('Captured %d frames at %.2ffps' % (
frames,
frames / (finish - start)))
如果你打算逐帧来处理图片的话,最好是通过拍摄的视频来解码抓取视频中帧组成图片,而不是通过快速抓取的jpeg来分析图片,或者你也可以通过网络将视频或图片传输到另外的设备上来处理,然而,若你不需要快速的完成图像的处理的话,那么你尽可省去上面说的两个步骤。我们可以通过生成一个新的函数,然后通过并行线程来处理我们的图像刘,这样处理的速度和解码都大大的加快了。
import io
import time
import threading
import picamera
# 创建一个图像处理序列
done = False
lock = threading.Lock()
pool = []
class ImageProcessor(threading.Thread):
def __init__(self):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.start()
def run(self):
# 这是一个单独运行的线程
global done
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# 这里去执行图片的处理过程
#done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# 将处理完的图片加载到序列中。
with lock:
pool.append(self)
def streams():
while not done:
with lock:
if pool:
processor = pool.pop()
else:
processor = None
if processor:
yield processor.stream
processor.event.set()
else:
# 当pool序列为空是,我们等待0.1秒
time.sleep(0.1)
with picamera.PiCamera() as camera:
pool = [ImageProcessor() for i in range(4)]
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_preview()
time.sleep(2)
camera.capture_sequence(streams(), use_video_port=True)
# 处理完成,释放所有处理序列
while pool:
with lock:
processor = pool.pop()
processor.terminated = True
processor.join()
5.4、图像的快速捕捉及转换成数据流
下面这个例子是5.5的实例的扩展,我们可以通过摄像头快速捕捉一组图像,并将之转换为网络数据流。服务器的脚本不变,客户端脚本会通过 capture_continuous()方法设置use_video_port来快速获取图片:
import io
import socket
import struct
import time
import picamera
client_socket = socket.socket()
client_socket.connect(('my_server', 8000))
connection = client_socket.makefile('wb')
try:
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 30
time.sleep(2)
start = time.time()
stream = io.BytesIO()
# 将模式设置为video-port
for foo in camera.capture_continuous(stream, 'jpeg',
use_video_port=True):
connection.write(struct.pack(' 30:
break
stream.seek(0)
stream.truncate()
connection.write(struct.pack('
import io
import socket
import struct
import time
import threading
import picamera
client_socket = socket.socket()
client_socket.connect(('spider', 8000))
connection = client_socket.makefile('wb')
try:
connection_lock = threading.Lock()
pool_lock = threading.Lock()
pool = []
class ImageStreamer(threading.Thread):
def __init__(self):
super(ImageStreamer, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.start()
def run(self):
# 这是个独立运行的线程
while not self.terminated:
# 等待图像被写入流
if self.event.wait(1):
try:
with connection_lock:
connection.write(struct.pack('
5.5、拍摄录像同时拍摄图像
树莓派的摄像头支持在录制视频的时候,同时拍摄静态图片。但是如果在拍摄模式中尝试获取静态图像,则有可能造成视频的丢帧现象。这是因为在拍摄视频式由于中途为了拍摄静态图像需要改变模式,从而停止了获取录像而造成丢帧。
然而,如果使用use_video_prot参数从拍摄的视频中直接捕获图像的话,则拍摄的视频将不会丢帧,下面这个例子演示了在拍摄视频的同时获取静态图像:
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (800, 600)
camera.start_preview()
camera.start_recording('foo.h264')
camera.wait_recording(10)
camera.capture('foo.jpg', use_video_port=True)
camera.wait_recording(10)
camera.stop_recording()
上面这段代码演示了,录制20秒的视频,从10秒处捕获一个静态的图像,使用了快速捕捉的参数,所以不会造成视频的丢帧,但若使用非jpeg模式或者使用更高分辨率的话,还是会引起一些丢帧的现象。
5.6、同时录制多种分辨率的视频
树莓派的摄像头支持同时使用多个视频分配器在不同的分辨率进行录制,这个需求可以支持在低分辨率下进行分析,而高分辨率同时进行存储或观看。
下面这个实例演示了利用start_recording()方法的splitter_port参数,开始两个同步线程同时录制图像,每一个图像录制使用不同的分辨率:
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (1024, 768)
camera.framerate = 30
camera.start_recording('highres.h264')
camera.start_recording('lowres.h264', splitter_port=2, resize=(320, 240))
camera.wait_recording(30)
camera.stop_recording(splitter_port=2)
camera.stop_recording()
树莓派最多支持4个视频分配器(端口编号分为0,1,2,3),普通视频录制默认使用了1号端口分配器,而拍摄静态图像则默认使用0号分配器(use_video_port也是使用0号),同一个视频分配器不能同时进行视频的录制和静态图像的捕获,所以你应该尽量避免占用0号分配器,除非你永远不打算在拍摄视频的同时捕获静态图像。
这个功能支持1.3及以后的版本。
5.7、记录运动矢量诗句
树莓派的摄像头能够输出对运动矢量数据的预估值,可以在录制h264视频的同时将运动矢量数据计算出来并输出到文件,输出运动矢量数据可以使用start_recording方法设置motion_output参数来输出一个矢量数据文件。
import picamera
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_recording('motion.h264', motion_output='motion.data')
camera.wait_recording(10)
camera.stop_recording()
运动数据包含在一个宏块数据,(一段MPEG视频宏表示所包含一块16x16的像素区域)并包括一个额外的数据列。因此,如果在640x480分辨率以上就会产生运动数据,数据长度为41列(640/16+1),30行(480/16)。
单个运动数据的大小为4字节,包括1字节的x矢量,1字节的y向量,和2字节的SAD(Sum of Absolute Differences绝对差值)。因此在上面这个例子中,每一帧数据将产生4920字节的运动数据(41*30*4),假设该视频包含300帧,则运动该数据总大小为1476000字节。
下面这个示例演示了加载运动数据,并将其转换为三维numpy阵列,第一维表示帧,后两维表示数据的行列。将数据机构化以后,可以比较简单的分析运动数的x,y和SAD值:
from __future__ import division
import numpy as np
width = 640
height = 480
cols = (width + 15) // 16
cols += 1 # 这里增加一个额外的列
rows = (height + 15) // 16
motion_data = np.fromfile(
'motion.data', dtype=[
('x', 'i1'),
('y', 'i1'),
('sad', 'u2'),
])
frames = motion_data.shape[0] // (cols * rows)
motion_data = motion_data.reshape((frames, rows, cols))
# 得到数据的第一帧
motion_data[0]
# 获取数据第5帧的x矢量值
motion_data[4]['x']
# 获取数据第10帧的sad值
motion_data[9]['sad']
得到数据的值以后,可以通过毕达哥拉斯定理来计算运动幅度。而SAD值可用于确定视频中的初始基准帧。
以下代码演示了使用从上面那个例子中得到的运动数据,然后使用PIL分析每一帧的图像,生成出一组PNG图像。
from __future__ import division
import numpy as np
from PIL import Image
width = 640
height = 480
cols = (width + 15) // 16
cols += 1
rows = (height + 15) // 16
m = np.fromfile(
'motion.data', dtype=[
('x', 'i1'),
('y', 'i1'),
('sad', 'u2'),
])
frames = m.shape[0] // (cols * rows)
m = m.reshape((frames, rows, cols))
for frame in range(frames):
data = np.sqrt(
np.square(m[frame]['x'].astype(np.float)) +
np.square(m[frame]['y'].astype(np.float))
).clip(0, 255).astype(np.uint8)
img = Image.fromarray(data)
filename = 'frame%03d.png' % frame
print('Writing %s' % filename)
img.save(filename)
我们可以利用picamera.array模块中的PiMotionArray类来简化上面的这个例子:
import numpy as np
import picamera
import picamera.array
from PIL import Image
with picamera.PiCamera() as camera:
with picamera.array.PiMotionArray(camera) as stream:
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_recording('/dev/null', format='h264', motion_output=stream)
camera.wait_recording(10)
camera.stop_recording()
for frame in range(stream.array.shape[0]):
data = np.sqrt(
np.square(stream.array[frame]['x'].astype(np.float)) +
np.square(stream.array[frame]['y'].astype(np.float))
).clip(0, 255).astype(np.uint8)
img = Image.fromarray(data)
filename = 'frame%03d.png' % frame
print('Writing %s' % filename)
img.save(filename)
最后,使用下面这个例子,可以利用生成的png图像生成一个ffmpeg的动画。(从树莓派上生成这组数据将会话费大量的时间,所以若你希望能够加快分析速度,则需要将数据移植到更快的机器上来进行分析)
avconv -r 30 -i frame%03d.png -filter:v scale=640:480 -c:v libx264 motion.mp4
这个功能支持1.5及以后的版本。
5.8、分割loop视频流
下面这个例子是简历在录制loop视频流的同时也在捕捉图像的安全应用上。和以前一样,用PiCameraCircularIO来让录制的视频流一直保持是最新的几秒视频。在录像的同时,通过分配器端口同时捕捉运动检测算法的程序(也就是说检测视频中有是否有运动的物体切入)。
一旦检测到有运动的物体,则将最后10秒钟的视频写入磁盘,然后录像通过运动检测算法,分割出运动部分的录像。然后循环检测运这里写代码片
动,直到不再检测到运动:
import io
import random
import picamera
from PIL import Image
prior_image = None
def detect_motion(camera):
global prior_image
stream = io.BytesIO()
camera.capture(stream, format='jpeg', use_video_port=True)
stream.seek(0)
if prior_image is None:
prior_image = Image.open(stream)
return False
else:
current_image = Image.open(stream)
# 从current_image到prior_image来检测两张图片是否有运动的物体切入
# 这一部分作为练习留给读者(哈哈哈)
result = random.randint(0, 10) == 0
# 若没有检测到运动,则更新prior_image为当前秒图像
prior_image = current_image
return result
def write_video(stream):
# 同时指定一个文件为loop循环视频缓冲区,在锁定的时候我们不会写入
with io.open('before.h264', 'wb') as output:
for frame in stream.frames:
if frame.frame_type == picamera.PiVideoFrameType.sps_header:
stream.seek(frame.position)
break
while True:
buf = stream.read1()
if not buf:
break
output.write(buf)
# 指针指向头则完成loop流的擦除
stream.seek(0)
stream.truncate()
with picamera.PiCamera() as camera:
camera.resolution = (1280, 720)
stream = picamera.PiCameraCircularIO(camera, seconds=10)
camera.start_recording(stream, format='h264')
try:
while True:
camera.wait_recording(1)
if detect_motion(camera):
print('Motion detected!')
# 如果我们检测到运动的物体时锁定当前录像,并将loop流分割到after.h264文件中。
camera.split_recording('after.h264')
# 录制10秒的运动数据
write_video(stream)
# 等待运动消失时,将loop流在移回before.h264中进行录制
while detect_motion(camera):
camera.wait_recording(1)
print('Motion stopped!')
camera.split_recording(stream)
finally:
camera.stop_recording()
需要注意的是,read1()方法在读写的时候不会返回读写字节的数量。
这个功能支持1.0及以后的版本。
5.9、自定义输出
在picamera的库中,能够接受接受输出到一个文件,或输出到一个IO流中,所以简历自定义输出是非常容易的,而且在某种情况下也非常有用。比如说我们可以构造一个自定义的简单对象,只要对象包含一个write方法,并且方法接受一个简单参数,和一个flush(没有参数)方法都可以被picamera方法所调用,使之成为一个输出方法。
在自定义输出类中,write方法为自定义的输出流方法,它至少会被调用一次被用来接收picamera所抛出的每一帧动画,而无需自建一个编码器。但是需要记住的是,write中不能包含太复杂的业务逻辑,因为该方法的处理必须要迅速且有效(这个方法必须快速处理并在下一帧捕捉之前返回)。
下面这个例子定义了一个非常简单的自定义输出类,并在flush时打印输出的字节:
from __future__ import print_function
import picamera
class MyOutput(object):
def __init__(self):
self.size = 0
def write(self, s):
self.size += len(s)
def flush(self):
print('%d bytes would have been written' % self.size)
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 60
camera.start_recording(MyOutput(), format='h264')
camera.wait_recording(10)
camera.stop_recording()
rom __future__ import division
import picamera
import numpy as np
motion_dtype = np.dtype([
('x', 'i1'),
('y', 'i1'),
('sad', 'u2'),
])
class MyMotionDetector(object):
def __init__(self, camera):
width, height = camera.resolution
self.cols = (width + 15) // 16
self.cols += 1 # 加入一个额外的列
self.rows = (height + 15) // 16
def write(self, s):
# 将运动数据加载到numpy阵列中
data = np.fromstring(s, dtype=motion_dtype)
# 计算每个向量的数据
data = data.reshape((self.rows, self.cols))
data = np.sqrt(
np.square(data['x'].astype(np.float)) +
np.square(data['y'].astype(np.float))
).clip(0, 255).astype(np.uint8)
# 如果运动幅度超过10向量或幅度更大,且总体大于60次,则证明我们捕捉到了运动中的物体。
if (data > 60).sum() > 10:
print('Motion detected!')
# 返回字节数
return len(s)
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_recording(
# 抛弃掉视频数据,但保证我们的格式为h264
'/dev/null', format='h264',
# Record motion data to our custom output object
motion_output=MyMotionDetector(camera)
)
camera.wait_recording(30)
camera.stop_recording()
import picamera
import picamera.array
import numpy as np
class MyMotionDetector(picamera.array.PiMotionAnalysis):
def analyse(self, a):
a = np.sqrt(
np.square(a['x'].astype(np.float)) +
np.square(a['y'].astype(np.float))
).clip(0, 255).astype(np.uint8)
# 如果运动幅度超过10向量或幅度更大,且总体大于60次,则证明我们捕捉到了运动中的物体。
# than 60, then say we've detected motion
if (a > 60).sum() > 10:
print('Motion detected!')
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_recording(
'/dev/null', format='h264',
motion_output=MyMotionDetector(camera)
)
camera.wait_recording(30)
camera.stop_recording()
5.10、自定义解码器
picamera支持开发者重写或者扩展图像或视频的解码器类,这样在视频的采集解码中可以直接运行您的代码进行解码。
但不同于自定义输出,自定义解码器是相当复杂的,在大多数情况下,我们还是建议使用默认的解码器,因为自定义解码器几乎没有什么好处,可能唯一的好处就是能更改自定义输出时无法更改的缓冲区head(说实话我也没怎么搞懂。。。)。
import picamera
import picamera.mmal as mmal
# 重写PiVideoEncoder类
class MyEncoder(picamera.PiCookedVideoEncoder):
def start(self, output, motion_output=None):
self.parent.i_frames = 0
self.parent.p_frames = 0
super(MyEncoder, self).start(output, motion_output)
def _callback_write(self, buf):
# Only count when buffer indicates it's the end of a frame, and
# it's not an SPS/PPS header (..._CONFIG)
if (
(buf[0].flags & mmal.MMAL_BUFFER_HEADER_FLAG_FRAME_END) and
not (buf[0].flags & mmal.MMAL_BUFFER_HEADER_FLAG_CONFIG)
):
if buf[0].flags & mmal.MMAL_BUFFER_HEADER_FLAG_KEYFRAME:
self.parent.i_frames += 1
else:
self.parent.p_frames += 1
# Remember to return the result of the parent method!
return super(MyEncoder, self)._callback_write(buf)
# Override PiCamera to use our custom encoder for video recording
class MyCamera(picamera.PiCamera):
def __init__(self):
super(MyCamera, self).__init__()
self.i_frames = 0
self.p_frames = 0
def _get_video_encoder(
self, camera_port, output_port, format, resize, **options):
return MyEncoder(
self, camera_port, output_port, format, resize, **options)
with MyCamera() as camera:
camera.start_recording('foo.h264')
camera.wait_recording(10)
camera.stop_recording()
print('Recording contains %d I-frames and %d P-frames' % (
camera.i_frames, camera.p_frames))
5.11、Raw Bayer 数据捕获
以下文档对我来说太复杂,暂时没搞定怎么翻译。。
5.12、摄像头闪光灯的调用
一些树莓派的摄像头模块支持在拍摄时调用LED闪光灯进行场景补光。对于LED灯可以自行配置GPRO的引脚。
树莓派摄像头有两个可操作的led灯
这些引脚可以通过videoCore设备列表进行查看,首选在使用led灯之前需要安装设备支持的库:
$ sudo apt-get install device-tree-compiler
$ wget http://www.raspberrypi.org/documentation/configuration/images/dt-blob.dts
树莓派类型 | 目录 |
---|---|
Raspberry Pi Model B revision 1 | /videocore/pins_rev1 |
Raspberry Pi Model A | /videocore/pins_rev2 |
Raspberry Pi Model B revision 2 | /videocore/pins_rev2 |
Raspberry Pi Model A+ | /videocore/pins_bplus |
Raspberry Pi Model B+ | /videocore/pins_bplus |
Raspberry Pi 2 Model B | /videocore/pins_bplus |
根据不同的树莓派类型,你会发现需要修改pin_config和pin_defines,根据pin_config部分,你需要配置led闪光灯的GPIO针脚,然后在pin_defines关联这些针脚与FLASH_0_ENABLE和FLASH_0_INDICATOR进行关联,来控制摄像头的开启与关闭。
例如,我们配置GPIO17作为闪光灯的针脚。那么我们在树莓派2B+对应的配置文件/videocore/pins_bplus中需要添加如下部分。
pin@p17 { function = "output"; termination = "pull_down"; };
pin-define@FLASH_0_ENABLE {
type = "internal";
number = <17>;
};
dtc -I dts -O dtb dt-blob.dts -o dt-blob.bin
命令行参数解释如下:
执行完以后命令行会输出一下内容:
DTC: dts->dtb on file "dt-blob.dts"
$ sudo mkdir /mnt/recovery
$ sudo mount /dev/mmcblk0p1 /mnt/recovery
$ sudo cp dt-blob.bin /mnt/recovery
$ sudo umount /mnt/recovery
$ sudo rmdir /mnt/recovery
以上就设置完闪光灯的一系列要求,然后就可以在picamera中开启闪光灯参数了。
import picamera
with picamera.PiCamera() as camera:
camera.flash_mode = 'on'
camera.capture('foo.jpg')
相机的LED脚已被定义为使用下拉终止),但你需要设置CAMERA_0_LED和CAMERA_0_LED的参数来手动启动led灯。
pin_define@CAMERA_0_LED {
type = "internal";
number = <5>;
};
pin_define@FLASH_0_ENABLE {
type = "absent";
};
pin_define@CAMERA_0_LED {
type = "absent";
};
pin_define@FLASH_0_ENABLE {
type = "internal";
number = <5>;
};