picamera 1.10 教程及api中文简译(四)picamera的高级使用

5、picamera的高级使用

下面的这些实例包含了picamera的一些高级使用方式,可能需要有一些图像开发经验才能掌握。所以请随时提出改进或更多的实例。

5.1、无损格式图像采集(YUV格式)

如果你不想损失拍摄图像的细节(由于jpeg是有损压缩),那么你可以通过PNG来接收拍摄的图像(PNG为无损压缩格式),然而某些应用需要YUV(YUV是被欧洲电视系统所采用的一种颜色编码方法)这种数字压缩格式的图像,对于这点需求可以用‘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')

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图像的大小将是:

  • 128.0 水平32的倍数
  • 112.0 垂直16的倍数
  • 1.5 1.5bytes的yuv数据(YUV比为4:2:0)
  • =21504.0 bytes

前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的图像,产生的数据如下:

  • 128.0 水平32的倍数
  • 112.0 垂直16的倍数
  • 3 每个像素块占3byte的数据
  • =43008.0 bytes

由此可见,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参数),但是使用这个功能需要注意几点:

  • 当使用video-port来捕捉图像的时候,在某些情况下,所捕捉的图像大小及清晰度可能会不如正常捕获的图片(可以参考相机模式和视频模式的区别)
  • 所捕捉的图像没办法嵌入EXIF信息。
  • 所捕获的图像可能会非常不清晰,噪点很大。若希望能捕获更清晰的图片,可以使用比较慢的获取方式,或者采取更先进的降噪算法。

所有的捕捉方法都支持use_video_port选项,但方法不同所捕捉图像的能力也有所不同。所以虽然capturehe和capture_continuous方法都支持use_video_prot功能,但最好使用capture_continuous来实现快速捕捉图片这个功能(因为capture_continuous不会每次都初始化解码器)。作者在测试时,这个方法最高可以支持在30fps下获取分辨率为1024x768的图片。

通常情况下,capture_continuous方法特别适合与捕获固定帧数的图像,比如下面这个例子:

import time
import picamera

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值