本篇博客首先简单介绍树莓派官方摄像头相关的内容,然后介绍树莓派相机的raspistill
、raspiyuv
和raspivid
三个命令,最后介绍如何获取树莓派相机的Raw图进行进一步处理。因为涉及很多技术细节,再加上本人对这一块内容的理解有限,如果有错误欢迎在评论中指出,互相交流。
在之前的这篇博客中,我们已经利用树莓派的相机实现了长时间的延时摄影,取得了不错的效果,感兴趣可以查看。
1.背景
这篇博客的背景与动机是我们想获得树莓派相机的Raw数据,以便进行更高质量的图像处理,如去噪、暗光增强等目的。而对于树莓派官方摄像头,获取Raw数据最简单的办法就是利用自带的raspistill
等命令。之前虽然用过,但没有比较完整的进行记录。所以这篇博客也希望可以把一些常用的功能、命令、参数记录一下,以方便日后查阅。
2.树莓派相机硬件
在本篇博客中,我们使用的是树莓派官方的第二代相机模块(Camera Module 2),发布于2016年,如下图所示。 在官网的介绍中提到,相机采用CSI接口,使用的是Sony IMX219传感器,800万像素。相机可以通过MMAL和V4L等库进行访问,并且也有比较丰富的第三方库进行数据处理。更多详细信息参考官网介绍页面。
2.1 相机的硬件接口
树莓派二代相机采用的是CSI接口,如下图所示。 相机模块的接口长这样。 CSI(Camera Serial Interface)是由MIPI(Mobile Industry Processor Interface)联盟下Camera工作组制定的接口标准,是MIPI联盟发起的为移动应用处理器制定的开放标准,该协议为低成本智能手机细分市场提供了一套灵活的、高性价比的解决方案。CSI接口具有接口少、抗EMI强、速度快、功耗低等优点。CSI-2是MIPI-CSI第二版,CSI-2由应用层、协议层、物理层组成,最大支持4通道数据传输,单线传输速度高达1Gb/s。关于CSI接口更多信息可参考这个网页,说的非常详细。
在拔插相机模块的时候建议动作轻一点,如下面的动图(选取自参考资料5)所示,不然容易损伤接口。
2.2 相机的传感器
树莓派二代相机采用的是Sony IMX219传感器,如下图所示。 我们首先可以去Sony半导体的影像传感器官网查阅相关信息,当然IMX219传感器可能比较老,所以官网没有相关信息了。但在这个网页、这个网页、这个网页、这个网页上依然有比较详细的介绍。此外,这个网页提供了Sony官方2页的概述Datasheet,这个网页则提供了长达96页的详细Datasheet,感兴趣可以读一读。这里就简单列举一些关键参数:
- 传感器(Sensor type):Sony IMX219 Color CMOS 8-megapixel
- 传感器大小(Sensor size): 3.674 x 2.760 mm (1/4″ format, diagonal 4.60mm)
- 传感器基底材料(Substrate material): Silicon
- 传感器总像元个数(Total number of pixels):3296(H) × 2512(V) approx. 8.28M pixels
- 传感器有效像元个数(Number of effective pixels): 3296(H) × 2480(V) approx. 8.17M pixels
- 传感器活动像元个数(Number of active pixels): 3296(H) × 2464(V) approx. 8.08M pixels
- 单个像素尺寸(Pixel size):1.12μm ×1.12μm
- 芯片尺寸(Chip size):5.095mm(H) × 4.930mm(V)
- 快门类型(Shutter type):电子卷帘快门
- 焦距类型(Focus type):固定焦距,3.04mm
- 视角(FoV): 62.2 degrees(H), 48.8 degrees(v)
- 光圈(Aperture): 2.0
- 色度(Chroma):彩色RGB
- 接口类型(Interface type):标准CSI接口
- 模数转换分辨率(ADC resolution):10Bit
另外,在完整版Datasheet里面也有一些比较有意思的图,这里也放一下。下图展示了相机光心与芯片中心之间的差异。 下图展示了像元物理平面之间的布局。 同时这个图也直观的展示了上面提到的三个像元个数(Total number of pixels, Number of effective pixels, Number of active pixels)。下图展示了在芯片上进行的一些图像处理步骤。 下图展示了传感器本身的光谱敏感度特征曲线。 下图展示了IMX219的RGB阵列。 最后,还有一张传感器电路连接示意图。
3.树莓派拍照命令raspistill
关于相机的安装,可以参考这个网页或者这个网页,有视频有动图还有解释,说的很好。如果你还没有配置好硬件的话,可以参考。下面主要介绍raspistill
命令相关的内容。
在树莓派中,最直接、最方便调用相机进行拍照的命令就是raspistill
。我们首先介绍使用raspistill
的一般格式,然后介绍常用参数,最后给出一些常用的参数搭配。
3.1 raspistill使用格式
与其它Linux命令类似,raspistill
的使用遵循“命令+参数+输出文件”的套路。具体而言,命令就是raspistill
,参数就是各种以-
+单个字母或者--
+单词的组合,最后就是输出影像的文件名。一个最简单的raspistill
使用方法如下。
raspistill -o output.jpg
在树莓派终端中执行这行命令后,系统就会调用相机进行拍摄(默认会等待5秒),拍摄的结果会保存在当前目录下,名字就是output.jpg
。
3.2 raspistill常用参数
前面提到了raspistill
有很多参数可以设置,所以这里就把一些我觉得比较重要和常用的参数列出来进行简单的说明。
影像拍摄相关参数:
-t
或-timeout
: 影像拍摄倒计时,单位毫秒,如果没有指定设为5秒(-t 5000
),例如-t 1000
-tl
或--timelapse
: 延时摄影模式,每隔t毫秒拍摄一张,例如-tl 5000
-ISO
或--ISO
: 相机的感光度,例如-ISO 500
-vs
或--vstab
: 打开视频稳像模式-ev
或--ev
: 相机的曝光补偿(-10 ~ 10),例如-ev 0.2
-ex
或--exposure
: 相机的曝光模式(off,auto,night,backlight,snow,verylong,fireworks等),例如-ex night
-fli
或--flicker
: 相机的防闪烁模式(off,auto,50Hz,60Hz)-awb
或--awb
: 相机的白平衡模式(off,auto,sun,cloud,shade,flash等),例如-awb auto
-mm
或--metering
: 相机的测光模式(average,spot,backlit,matrix),例如--mm matrix
-ss
或--shutter
: 相机的快门速度(微秒,1s=10^6微秒,对于Camera Module V2,最长曝光时间约为10s),例如--ss 1000
-awbg
或--awbgains
: 相机的白平衡增益(自动白平衡模式AWB必须关闭)-drc
或--drc
: 相机的动态范围压缩(Dynamic Range Compression)选项(off,low,med,high),例如-drc low
-ag
或--analoggain
: 相机的模拟增益(浮点型)-dg
或--digitalgain
: 相机的数字增益(浮点型)-set
或--settings
: 获取相机的配置参数并且输出-v
或--verbose
: 输出相机相关的运行状态与参数-n
或--nopreview
: 拍摄时不显示预览影像
影像效果相关参数:
-h
或--height
: 影像的高度(像素),例如-h 640
-w
或--width
: 影像的宽度(像素),例如-w 480
-vf
或--vflip
: 影像垂直翻转(vertical flip)-hf
或--hflip
: 影像水平翻转(horzontial flip)-sh
或--sharpness
: 影像的锐度(-100到100),例如-sh 50
-co
或--contrast
: 影像的对比度(-100到100),例如-co 50
-br
或--brightness
: 影像的亮度(-100到100),例如-br 50
-sa
或--saturation
: 影像的饱和度(-100到100),例如-sa 50
-ifx
或--imxfx
: 影像的不同效果(none,negative,sketch,denoise,emboss,watercolour,film等),例如-ifx negative
-cfx
或--colfx
: 影像的色彩效果(U:V)-rot
或--rotation
: 影像的旋转角度(0,90,180,270),例如-rot 90
-roi
或--roi
: 影像中感兴趣的区域(x,y,w,d)归一化坐标,从0.0-1.0
影像输出相关参数:
-o
或--output
: 影像的输出路径,例如-o /root/images/photo.jpg
-e
或--encoding
: 影像的编码格式(jpg,bmp,gif,png),例如-e jpg
-q
或--quality
: 影像的保存质量,从0到100,例如-q 75
-r
或--raw
: 将Bayer原始数据附加到jpg影像中-gps
或--gpsexif
: 将实时GPS信息添加到输出影像中
更完整的参数可以参见官网这个网页,里面介绍的非常详细,当个字典查就可以。
3.3 raspistill常用模式
上面我们介绍了raspistill命令的常用参数,这里也简单给出几个常用的参数组合和模式。
(a) 最简单模式
使用raspistill
最简单的方式就是只指定输出影像名称,如下所示。
raspistill -o output.jpg
这样拍出来的照片所有的参数都是默认或自动指定的,如下图所示。
(b) 手动指定感光度
我们可以使用-ISO
参数来手动指定感光度,其余参数默认或自动,如下所示。
raspistill -ISO 100 -o iso_100.jpg
raspistill -ISO 400 -o iso_400.jpg
raspistill -ISO 800 -o iso_800.jpg
可以看到,相机拍摄了三张图像,ISO是我们设定的值,其余参数为自动。 相机根据ISO的变化自动调整了快门速度。
(c) 手动指定快门速度
我们可以使用-ss
参数来手动指定快门速度(单位为微秒),其余参数默认或自动,如下所示。
raspistill -ss 1 -o ss_1.jpg
raspistill -ss 100 -o ss_100.jpg
raspistill -ss 10000 -o ss_10000.jpg
raspistill -ss 100000 -o ss_100000.jpg
raspistill -ss 1000000 -o ss_1000000.jpg
raspistill -ss 10000000 -o ss_10000000.jpg
可以看到,相机拍摄了六张图像,快门速度是我们设定的值,其余参数为自动。 相机根据曝光时间的长短自动调整了ISO,拍出来的可视效果如下所示。
d. 手动指定感光度与曝光时间
有时对于同一场景,我们希望用不同的感光度、曝光时间来获得内容相似(进光量相似)的影像。这时候就可以同时控制上面提到的感光度与曝光时间。
这里简单介绍一下相机进光量的计算。简单来说,一个相机的进光量主要由三个因素控制:感光度ISO、曝光时间(快门速度)以及光圈大小。显然的,感光度越高,其它条件不变情况下,单位时间内进光量越多;同理,在其它因素不变情况下,光圈越大,单位时间进光量越多;曝光时间越长,进光量越多。这就类似于水龙头向水桶里倒水,可以通过控制水龙头的水流大小、接水时间等来控制最终桶里接多少水。当然,这里也不是说这三个因素可以随意搭配。感光度如果过高,容易产生噪点;快门时间过长,容易发生抖动模糊;光圈太大,容易虚化过度。可以针对具体情况,在一个合理的范围内调整,从而保证进光量的一致。对于这个树莓派镜头而言,其光圈是固定的,所以影响进光量的主要就是感光度和快门速度。比如说我们ISO设为100,曝光时间为1/10秒,我们可以把他们相乘,得到10,可以理解为单位时间内传感器接受100的进光量,现在感光时间为1/10秒,所以一共接收了10进光量。所以比如我们将ISO设为400,为了保证进光量还是10,那么曝光时间就应该设置为1/40。我们可以利用下面的命令进行测试。
raspistill -ISO 100 -ss 100000 -o iso_100_ss_100000.jpg
raspistill -ISO 400 -ss 25000 -o iso_100_ss_25000.jpg
拍摄的影像如下图所示。 可以看到,两张影像的亮度从肉眼上来看基本是完全一致的,这也就是我们控制进光量的目的,尽可能保证影像的亮度一致。而如果你仔细观察的话也会发现,低ISO的影像噪点会比高ISO影像更少一些。
4.树莓派拍照命令raspiyuv
上面我们介绍了raspistill
命令的参数与基本用法,并给出了一些示例。在树莓派中还有一个拍照命令那就是raspiyuv
。一般情况下raspistill
已经可以满足需求了。那么raspiyuv
的作用是什么呢?其实也非常简单,从名字就可以看出来,它的主要用途是输出未经压缩/处理的YUV或RGB格式的影像而非常规的JPG,更准确来说是YUV420或RGB888格式。那么你可能就会有第二个问题了,什么是YUV420,为什么要输出这个,相比于RGB好在哪里。这些问题在下面都会得到答案。
在介绍YUV之前,先介绍一下Raw RGB和普通RGB的区别。简单来说就是传感器输出的Raw RGB是指未经过Bayer插值的原始RGB数据(每个位置只有R、G、B三个颜色中的一个,一般为RGGB格式),而普通RGB则是经过色彩插值之后的数据(每个位置都同时包含R、G、B三个颜色)。然后介绍一下RGB888。其实从字面意思来看,就是Red、Green、Blue三通道,每个通道都是8bit。你可能会觉得,这不就是常规的RGB吗?为什么要特别强调888呢?事实上,在数据实际传输的时候,为了节省带宽,可能出现RGB565的情况。也就是Red用5bit存储,Green用6bit存储,Blue用5bit存储。这里为什么Green多1bit是因为人眼对于绿光相较于其它两种光更为敏感。所以一句话总结这里的Raw RGB888就是传感器输出的数据是未经过插值混色的原始的图像数据(一般为RGGB格式),每个色彩通道都是8bit。插一句题外话(如果现在看不懂也没关系,看完后面就明白了):这和我们通过raspistill
获取的Raw数据还是有区别的。直接获取的Raw数据一般是10bit,是要高于8bit的。相同点是都是未经处理的传感器的感光Bayer数值。
上面用raspistill
保存的彩色图像,可能因为压缩和保存等问题,导致某些通道不是标准的8bit,或者说局部多个像素共用一种色彩的情况(已经经过Bayer插值)。所以简单来说,如果想获得未经压缩或处理的影像数据(YUV或Raw RGB),那么就用raspiyuv
就可以了。不过需要注意的是,此时虽然保存的也是.jpg
格式,但是普通的软件是打不开的。在下面会介绍如何解析这些文件。
4.1 YUV色彩编码简介
首先说色彩空间,色彩空间的表示说白了就是以不同的方式来准确描述色彩,通过多维的描述可以绝对地定位某种色彩。有很多色彩空间,比如RGB色彩空间就是把所有颜色以红(Red)、绿(Green)、蓝(Blue)分量的形式来描述,HSV色彩空间则是将所有颜色以色调(Hue)、饱和度(Saturation)、明度(Value)来表示。这有点像三维空间,对于同样的三维空间,我们可以使用不同的描述方式来定位一个点,例如用空间直角坐标系来表达,也可以用极坐标来表达,这是一个道理。当然这里还要区分的一个概念是RGB、HSV等是一种色彩空间,而JPG、PNG、TIFF等则是文件格式,两者也不是一回事。
然后介绍色彩的编码。有时我们希望以更高效的方式来描述某种某种色彩,而不是“比较傻”的用颜色空间。举个简单的例子,比如有10个红色像素(R:255,G:0,B:0),如果用RGB色彩空间来描述,那就是10个(255,0,0)。但是如果我们设计一种颜色编码方式,比如指定每10个像素共用一个颜色,那么,我们就只要记录一个像素的颜色再加上步长,比如写成(255,0,0,10)就可以了。这显然降低了颜色表达的重复性,之前需要3×10=30个字符,现在只需要4个字符,这极大提高了传输效率。这也就是各类色彩编码系统诞生的原因,各类编码系统也就应运而生了,这其中有一个比较有代表性的就是YUV。所以我们可以这样理解:色彩编码更多的是为了高效传递颜色、减小数据量,而非精准、绝对的表达颜色。这是和色彩空间最显著的区别。
在继续介绍之前,先给出一个人眼生理上的结论:人眼的视觉特点是对亮度更敏感,对位置、色彩相对来说不敏感。基于这个事实,也就是说在压缩编码时,我们可以保存更多的亮度信息(Luminance,luma),保存较少的色差信息(Chrominance,chroma)。事实上,YUV色彩编码也就是这样做的,相比于RGB颜色空间,其在传输时更方便、带宽占用更少,主要用在视频、图形处理流水线中。
YUV编码中的Y表示亮度(luma),U和V表示色度(chroma)。其实Y’UV、YUV、YCbCr、YPbPr这几个概念其实是一样的。由于历史关系,Y’UV、YUV主要用在彩色电视中,用于模拟信号表示。YCbCr用在数字视频、图像的压缩和传输,如MPEG、JPEG。所以YUV就是YCbCr。
在大体明白什么是YUV颜色编码以后,我们再来介绍后面的三个数字。前面提到了,人眼对于亮度信息更加敏感,所以我们保存较多的亮度信息(luma),保存较少的色差信息(chroma)。这被称为色度二次采样(Chrominance Subsampling)。它的原则是:(1) 每个像素都要包含亮度(luma);(2) 几个像素共用一个U、V值(一般是2、4、8个像素)。所以YUV这后面的三个数字就是对应色度二次采样的方式,可以用J:a:b来表示。一般而言,我们在J×2个像素范围内定义色度二次采样。比如J为6的时候就是6×2这个像素矩阵,我们把这个矩阵称为参考块。一般J=4。a表示参考块的第一行包含的色度像素个数,b表示参考块第二行包含的色度像素个数。所以,YUV420就表示参考块大小为4×2,第一行有2个包含色度的像素,第二行没有,画出示意图如下所示。 说了这么多,上面都是偏理论的东西,从实际出发,RGB和YUV的转换公式如下。
Y= 0.299×R+0.587×G+0.114×B
U=-0.147×R-0.289×G+0.463×B
V= 0.615×R-0.515×G-0.100×B
对YUV色彩编码感兴趣可以参考这个网页和这个网页,里面有更详细的说明。
4.2 raspiyuv常用参数
与raspistill
类似的,绝大部分raspistill
的参数都可以在raspiyuv
命令中使用。当然raspiyuv
也有一些独有的参数,这里也简单列举一下。
-rgb
或--rgb
: 以RGB格式保存数据而非YUV-y
或--luma
: 仅输出YUV格式的luma/Y分量-bgr
或--bgr
: 以BGR格式保存数据而非YUV-md
或--mode
: 强制传感器模式,0表示自动,其它模式代码参考这个网页,下图列出了IMX219支持的拍摄模式。
4.3 raspiyuv常用模式
(a) 常规YUV数据
在利用raspiyuv
获取YUV数据的时候,一定要注意一点,就是影像的长和宽都必须要是32的整倍数,如果不是的话,在后续解析的时候就会出问题。所以我们在拍摄YUV的时候,需要显式指定影像的大小(IMX219默认为3280×2464),如下所示。
raspiyuv -w 3200 -h 2400 -o raw_yuv.yuv
这样我们就可以获得一张YUV影像了。
(b) 单独亮度数据
我们也可以使用下面的命令只获取亮度数据。
raspiyuv -w 3200 -h 2400 -y -o raw_luma.yuv
(c) Raw RGB数据
我们也可以获取Raw RGB,如下。
raspiyuv -w 3200 -h 2400 -rgb -o raw_rgb.rgb
4.4 YUV文件的读取与显示
在拍摄好了YUV影像以后,就需要对YUV文件进行读取。有两种比较方便的形式,一种是利用Pyuv库或者libuv库,通过写代码实现读取。另一种是利用PYUV软件进行可视化。当然,在可视化的时候需要指定影像的长宽、YUV采样模式等参数,才可以正常显示。
比如,我们利用PYUV软件打开刚刚拍摄的YUV影像,如下所示。 我们需要指定影像的大小、颜色编码为YUV、采样方式为420,然后点击OK。这样就能打开影像了,如下图所示。 我们也可以打开刚刚拍摄的只包含Y的影像,如下所示。 我们在参数里还是设置影像大小,编码为YUV,但是这里采样方式就变了,为400,因为没有色度信息,所以后两个都为0。点击OK,就能显示图像如下图所示。 最后,我们打开刚刚拍摄的Raw RGB影像,如下图所示。 我们设置图像大小、选择颜色编码为RGB,然后采样方式选择444,同时勾选”Interleaved”,这样就可以正常显示影像了。 如果不勾选”Interleaved”的话,显示的是下面的效果。 关于可视化YUV影像,更多可以参考这个网页。
5.树莓派录像命令raspivid
上面介绍了利用raspistill
和raspiyuv
拍摄图片,下面介绍利用raspivid
拍摄视频。
5.1 raspivid常用参数
与前面两个命令类似的,raspivid
也有一些常用的参数,和上面相同的参数就不列举了,这里列举一些特有的参数如下。
-b
或--bitrate
: 码率设置,每秒多少bits的数据,例如10MBits/s为-b 10000000
-t
或--timeout
: 指定视频拍摄时长,单位为ms,如果没有指定默认为5s,例如-t 7000
-fps
或--framewrate
: 帧率设置,每秒多少帧,例如-fps 30
-qp
或--qp
: 量化参数,一般在10到40之间,默认为0(off)-cd
或--codec
: 指定编码方式:H264(默认)或MPEG,例如-cd MPEG
-pf
或--profile
: 用于编码的H264配置(baseline,main,high)-lev
或--level
: 指定H264编码等级(4, 4.1, 4.2)-r
或--raw
: 输出Raw视频的路径与名称,例如-r /root/out.yuv
-rf
或--raw-format
: Raw视频的保存格式(yuv,rgb,gray),默认为yuv
5.2 raspivid常用模式
(a) 最简单的模式
最简单的模式当然就是直接录制视频并保存,其它参数都默认,如下。
raspivid -o test_video.h264
这样系统就会自动录制一段时长为5s的H264编码的视频。当然这样录制出来的数据一般的播放器可能没法正常播放(比如无法读取总时长、播放速度不对等问题),在官网中也给出了解决办法。那就是在树莓派中输入sudo apt install -y gpac
,安装MP4Box工具,然后利用这个工具将录制的视频转成MP4,就可以正常播放了。转换命令如下。
MP4Box -add test_video.h264 cvt_video.mp4
(b) 自定义模式
比如我们录制一段长为4s、尺寸为640×480、帧率为20、码率为10MBits/s的视频,参数设置如下。
raspivid -w 640 -h 480 -fps 20 -b 10000000 -t 4000 -o test.h264
录制好以后别忘了转换成普通的MP4以方便观看。
MP4Box -add test.h264 test.mp4
(c) Raw模式
我们也可以输出未经压缩的yuv数据进行后续处理,如下所示。需要注意的是对于raspivid
的-r
参数,直接传入输出的视频路径。比如我们录制一段长为3s、尺寸为640×480、帧率为25的Raw视频。
raspivid -w 640 -h 480 -fps 25 -t 3000 -r test.yuv
这样我们就可以获得一段Raw视频,方便后续处理。
5.3 视频查看
对于录制好的普通视频,经过MP4Box的转换,就可以在普通播放器播放了。对于Raw视频,我们还是可以用刚刚看YUV图的工具进行播放,如下所示。 与单张YUV影像类似的,我们还是要指定影像的大小、颜色编码、色度采样方式等,不同的是还要多个帧率的选项,这里我们设置为25,然后就可以正常打开了。
6.树莓派相机获取原始数据
在上面,我们介绍了raspistill
、raspiyuv
和raspivid
三个命令的基本使用。在介绍如何获取原始数据之前,我们应该先明确一下什么是原始数据。一般而言,认为由于原始数据没有经过任何后续处理,所以最大程度记录和保留了拍摄瞬间的场景信息。感兴趣可以参考维基百科对于Raw影像的描述。
事实上,不同的应用场景有着不同的定义,可以参考官方的这个网页。在这个网页中,对拍摄的数据根据是否处理以及是否有损进行了划分,可以用下图表示。
在这里可以看到,“原始数据”主要有两类:经过ISP处理的无损数据以及未经过任何处理的传感器数据。下面分别进行讨论。
6.1 经过处理的无损数据
如果把原始数据定义为经过ISP处理且未压缩的数据,那么,我们有两种方式获得这种“原始数据”:raspistill
和raspiyuv
。
对于raspistill
而言,其默认输出的是经过处理和压缩后(有损)的JPEG影像。但如上面所说,它有一个-e
或--encoding
参数,用于指定输出文件编码。如果想获取经过处理的、未压缩的影像,我们可以直接指定编码为bmp或png,这样获得的就是无损影像。不过需要注意的是,这样需要从原始的YUV数据中直接转换,并且这种转换也不在硬件层面被支持,所以相比于有损影像会更慢(几倍甚至更多)。
raspistill -e jpg -o test1.jpg
raspistill -e png -o test2.png
另一个选择就是利用raspiyuv
。这样可以避免raspistill
命令最终的文件类型转换阶段,将传感器数据直接写为YUV420或RGB888的二进制文件。这样获取到的同样是经过ISP处理后的数据。
6.2 未处理的传感器原始数据
而如果我们把原始数据定义为传感器直接接收的数据(未经ISP处理和压缩),那么我们依然可以通过raspistill
命令获得。如前面所说,raspistill
命令有个-r
或--raw
参数,利用它我们就可以很方便地获取这种“原始数据”。在树莓派官方的这篇博客中,对于Raw文件有这样的描述:
A raw image in this contex is a direct capture of the pixels output from the image sensor, with no addtional processing. Normally this is in a relatively standard format known as a Bayer image, named after Bryce Bayer who pioneered the technique back in 1974 while working for Kodak. The idea is not to let the on-board hardware ISP (Image Signal Processor) turn the raw Bayer image into a viewable picture, but instead to do it offline with an additional piece of software.
简单来说就是Raw数据是传感器直接获得的数据,没有经过任何处理,是以Bayer阵列存储的。我们可以通过下面的命令方便地获取Raw图。
raspistill -o test1.jpg
raspistill -o test2.jpg -r
可以看到,同样的内容,但拍摄的两个图像文件大小差别很大,这就是因为在test2.jpg
中包含了Raw数据。树莓派的策略是并不是直接单独将Raw数据保存成单独文件,而是将其附在JPG数据后面,这样的好处是可以直接知道Raw影像的内容而无需用专门的软件打开Raw图像。当然这样做的结果就是我们还需要额外的步骤才能把Raw数据取出来。关于摄像头Raw数据和YUV数据之间的关系,感兴趣可以看这个网页,仅供参考。
6.3 原始数据后处理
对于6.1部分提到的“原始数据”,其实并不需要额外的处理,而6.2部分的数据,则需要单独提取。显然我们对后者更感兴趣。这一小节主要介绍如何提取出这种“原始数据”。官方也给出了一个教程,点击查看。简单来说,我们可以通过PiDNG这个工具将树莓派拍摄的JPEG+RAW独特文件中的Raw数据分离开来。
(a) PiDNG安装
PiDNG是一个Python的标准库,所以我们可以直接pip install pidng
进行安装。其PyPI主页地址是这里。安装好之后就可以使用了。
(b) 分离Raw数据
我们可以通过编写Python脚本实现JPEG与Raw数据的分离,一个简单的脚本如下。
from pidng.core import RPICAM2DNG
if __name__ == '__main__':
d = RPICAM2DNG()
cvt_raw = d.convert("test.jpg")
通过上面两行代码,就可以提取文件中的Raw数据,并将其保存为test.dng
。在获得了Raw图之后,我们就可以使用之前博客提到过的Rawpy进行一系列后处理以达到我们想要的效果了。比如我们简单利用这篇博客中的代码对分离出来的test.dng
进行解析,就可以得到传感器获取的Bayer阵列数据,如下所示。
局部放大之后就可以看到清晰的Bayer阵列了,图如下所示。
而且不仅如此,可以看到,原始Raw图的量化级数是10bit,最大亮度为1023,如下图所示。
比我们直接获得的8bit RGB图像的动态范围要高得多。这也就是为什么Raw图后期空间更大的原因之一,动态范围更大、记录的信息更真实。
(c) 从Raw到RGB
如何从传感器Raw数据到可视RGB,这其实就是ISP芯片主要做的工作。当然我们也可以像上面这样,把这个工作从ISP手中“抢过来”——直接获取原始Raw数据,我们自己后期处理。那么ISP都做了些什么使得Raw数据一步步处理到可视的RGB影像呢?在这篇博客里就有提到,或者也可以参考这个网页,说的也很不错。按照这个网页里说的,简单来说主要包含两个阶段:Raw Conversion + Editing。Raw conversion简单来说就是把Raw数据读取出来并转换到标准格式,以便后续步骤使用。而Editing则是对标准格式的Raw数据进行一系列调整,使得图像更符合我们的要求与期待。具体而言又包含以下几个步骤:
1.Load linear data from the raw file and subtract Black Levels
这一步的主要操作就是读取Raw数据并将其加载到内存中,以便后续处理。当然,还需要做一次Black Level Compensation。也就是说,把原本应该是纯黑的像素的亮度值变成纯黑的。而这个偏移值可以通过相机标定获得。这一步做完之后,我们就得到了一个相对干净的、未经Bayer插值的传感器数据,我们可以暂且叫它linear Color Filter Array(CFA)数据,如下图所示(下面所有的图片都来自这个网页)。
2.White Balance the data
由于CFA的存在以及感光元件的性能,传感器实际感受的光线的强度是不同的。例如传感器对红色和蓝色光的感知只有绿色光的约48.8%和75.4%。这样的结果就是整幅图像偏绿,而红色和蓝色颜色偏暗。针对这种情况,我们就要进行校正,最直接目的目的就是让图像中原本是白色的区域显示出来也是白色的。知道了事实之后,校正也就非常简单。在刚刚得到的linear CFA数据上,所有红色像素都乘以2.0493(1/48.8%),所有蓝色像素都乘以1.3256(1/75.4%)。这样得到的是相机自然灰度影像(Camera Neutral grayscale image),如下图所示。当然实际情况更加复杂,也有不同的参数配置,但基本原理是相同的。
3.Correct linear brightness
一般而言,传感器获取到的Raw数据是10bit的,也就是说范围在0到1023。但是在存储的时候,一般喜欢用8bit或16bit进行存储。这样导致的一个问题就是原始数据只占了极小一部分。以10bit数据(最大为1023)为例,其保存为16bit(最大值为)65535,仅仅占了约1.6%。所以需要进行灰度缩放,尽可能让原始数据充分利用高bit范围。这个过程被称为Exposure Correction/Compensation(EC)。一个最直接、简单的想法就是把每个像素都乘以一个常数,采用linear brightness correction,如下图所示。这当然是没问题的,但随之而来的就是可能会损失一些原本就很亮的区域的细节(过曝),所以才会有很多更加复杂的非线性算法。
4.Properly Clip image data
经过我们上面的步骤,RGB三个通道可能出现色彩范围不一样的情况。比如绿色在0到1范围之间,而红色和蓝色则超过了1,比如在0到1.3,0到1.2之间。之所以出现这种情况,是因为在白平衡的时候,我们单独对红色和蓝色像素做了处理,乘了一个常数。如果对这种情况不做处理,那么在高光区域,就可能出现偏粉色的情况(因为红色和蓝色分量比绿色多,红搭配蓝是紫色,又因为在高光区域,紫色变淡,最终形成淡粉色),如下图所示。所以为了避免在接近饱和的高亮区域中引入假彩色,一个简单有效的办法就是将R、G、B这三个通道全部裁剪到最低通道的水平。比如绿色范围为0到1,是最低的,我们就把红色和蓝色超过1的部分裁剪掉(全部设为1)。
5.Demosaic it
经过了上面一系列步骤,我们对亮度的处理基本完成了。下面就是根据传感器的Bayer阵列插值得到真正的彩色图像。现在的数据每个像素只有RGB其中的一种,最终我们的目标是每个像素都有RGB三个颜色。关于Demosaic算法的一些介绍,可以参考这篇博客。如下图所示,是我们经过Black Level Substraction、White Balance、Clipping、Brightness Correction、Demosaic的效果。
6.Apply Color Transforms and Corrections
经过上面的步骤,我们可以得到一个看起来还不错的彩色图像,但总感觉有些平淡。简单来说,其原因是这些由相机得到的色彩并非在一个标准的、看图软件或硬件能接受的RGB色彩空间中。所以我们可以在这一步对现有的色彩进行“调教”,使得这些Raw RGB转换到标准RGB色彩空间(如sRGB)中。具体而言,这种转换可以通过Color matrix进行。不同的相机有不同的Color matrix,不同的Color matrix又会带来不同的效果。事实上各个相机厂家都对其各自的相机传感器CFA的调教严格保密,这也导致了不同厂家拍出来的照片“味道”不同的原因。那么下一个问题是,如何获得Color matrix,还是有两种方法,一种是自己测量,另一种则是从网上下载别人测量好的数据,比如DXO网站就提供了很多相机的参数,比如Nikon D160的Color matrix如下。 在有了Color matrix之后,我们只需要将Raw RGB分别乘以对应的系数,就可以得到sRGB下的对应值了,如下图(black levels subtracted, white balanced, clipped, brightness corrected, demosaiced, color corrected and converted to the sRGB color space)。
7.Apply Gamma
最后一步处理就是应用Gamma变换。其原因在于人眼的生物特性,人眼对于光照强度变化的感觉并非是线性的。为了使影像看起来更讨好“人眼”,需要在现有基础上进行Gamma变换,变换到适合人眼的亮度曲线,看起来才会更好看。当然,如果要进一步处理,还可以增加Tone Mapping,使得图片更好看。如下是经过Tone Mapping的效果。
7.总结
至此,这篇“又臭又长”的博客终于结束了。我们介绍了树莓派raspistill
、raspiyuv
、raspivid
三个命令的使用,然后又介绍了如何利用它们在树莓派上获取Raw数据。最后介绍了Raw数据的解析和后处理的一系列流程。
8.参考资料
- [1] https://www.raspberrypi.org/products/camera-module-v2/
- [2] https://blog.csdn.net/yanglei0385/article/details/85258122
- [3] https://www.sony-semicon.co.jp/e/products/IS/automotive/
- [4] http://www.camera-module.com/product/raspberrypicameramod/8mp-raspberry-pi-camera-module-sony-imx219-sensor.html
- [5] https://www.arducam.com/product/arducam-8mp-raspberry-pi-camera-v2-cs-b0102/
- [6] https://www.arducam.com/docs/cameras-for-raspberry-pi/native-raspberry-pi-cameras/8mp-imx219-standard-camera-modules/
- [7] https://zhuanlan.zhihu.com/p/269347352
- [8] https://www.electronicsdatasheets.com/download/5721ed8ce34e24fd697a913a.pdf
- [9] https://www.arducam.com/downloads/modules/RaspberryPi_camera/IMX219DS.PDF
- [10] https://projects.raspberrypi.org/en/projects/getting-started-with-picamera
- [11] https://www.raspberrypi.org/documentation/accessories/camera.html
- [12] https://roboticsbackend.com/raspberry-pi-camera-take-picture/
- [13] https://zhuanlan.zhihu.com/p/85620611
- [14] https://www.cnblogs.com/zebra-bin/p/12882117.html
- [15] https://pypi.org/project/pyuv/
- [16] http://dsplab.diei.unipg.it/software/pyuv_raw_video_sequence_player
- [17] https://blog.csdn.net/lavenderss/article/details/51495648
- [18] https://en.wikipedia.org/wiki/Raw_image_format
- [19] https://www.raspberrypi.org/documentation/accessories/camera.html#shooting-raw-using-the-camera-modules
- [20] https://www.raspberrypi.org/blog/processing-raw-image-files-from-a-raspberry-pi-high-quality-camera/
- [21] http://www.liuwenhao.me/?p=1814
- [22] https://github.com/schoolpost/PiDNG
- [23] https://pypi.org/project/pidng/
- [24] https://www.strollswithmydog.com/raw-file-conversion-steps/
本文作者原创,未经许可不得转载,谢谢配合