CMOS概述

     任何CMOS成像系统都由有源像素区和光电探测器部分组成捕获光子并将其转换成非常小的光电流或电子。不同的部分读出数据,包括ADC、模拟信号处理、用户接口数字逻辑、定时、,毫微微放大器范围内的少量光电二极管电流在曝光时间(快门的打开)转换成少量电荷ADC的电压。

电路原理

Example of a 4 Transistor Design:

三维堆叠视图View

CMOS相关技术-小白菜博客

一、CMOS光学参数

1.CMOS元件的CRA参数

     众所周知,sensor的效能与sensor本身的灵敏度与光线入射到sensor的角度有关。而光线入射到sensor pixel的角度是由Lens的CRA和sensor的Micro Lens开口布局(sensor的CRA)决定的。

     CRA是Chief Ray Angle的缩写,意思是主光角。 从镜头的传感器一侧,可以聚焦到像素上的光线的最大角度被定义为一个参数,称为主光角(CRA)。对于主光角的一般性定义是:此角度处的像素响应降低为零度角像素响应(此时,此像素是垂直于光线的)的80%。

     我们在挑选Lens的时候会有一个CRA的参数,在选择sensor的时候同样有一个CRA的参数,一般我们要求Lens的CRA曲线与senosr的CRA曲线完全匹配,如果实在不能匹配,我们也要求在同样的像高位置,CRA相差不能超过3度,而且最好是Lens的CRA比sensor的CRA小。否则将会导致成像照度或色彩问题
image

     拍摄镜头和传感器之间的接口是整个可拍照手机系统中最重要的接口之一。随着镜头的长度变得越来越短,光线到达传感器像素位置的角度也就会变得越来越大。每个像素上都有一个微镜头。微镜头的主要功能就是将来自不同角度的光线聚焦在此像素上。然而,随着像素位置的角度越来越大,某些光线将无法聚焦在像素上,从而导致光线损失和像素响应降低。

     光线进入每一个像素的角度将依赖于该像素所处的位置,镜头轴心线附近的光线将以接近零角度进入像素中随着它与轴心线的距离曾大,角度也变大.CRA与像素在传感器中的位置是相关的 。

     光线进入每个像素的角度将依赖于该像素所处的位置。镜头轴心线附近的光线将以接近零度的角度进入像素中。随着它与轴心线的距离增大,角度也将随之增大。CRA与像素在传感器中的位置是相关的,它们之间的关系与镜头的设计有关。很紧凑的镜头都具有很复杂的CRA模式。如果镜头的CRA与传感器的微镜头设计不匹配,将会出现不理想的透过传感器的光线强度(也就是“阴影”)。通过改变微镜头设计,并对拍摄到的图像进行适当处理,就可以大大降低这种现象。改变微镜头设计可以大大降低阴影现象。然而,在改变微镜头设计时,必须与镜头设计者密切配合,以便为各种拍摄镜头找到适合的CRA模式。
     相机的设计工程师应该确保这种技术合作得以实现,并确保传感器与镜头CRA特性可以很好地匹配。为确保成功实现此目标,美光开发了相关的仿真工具和评价工具。由于光线是沿着不同的角度入射到传感器上的,因此对于各种镜头设计而言,阴影现象都是固有的。 “cos4定律”说明,减少的光线与增大角度余弦值的四次方是成比例关系的。另外,在某些镜头设计中,镜头可能本身就会阻挡一部分光线(称为“晕光”),这也会引起阴影现象。所以,即使微镜头设计可以最小化短镜头的阴影现象,此种现象还是会多多少少地存在。因此后端ISP一般都会有LSC功能。

什么是Sensor的CRA

     Sensor有一个 CRA值,也就是Sensor的Micro Lens与光电二极管的位置存在一个水平误差,并不在一条直线上,做成这样有一定的目的,按通常的做法,因为Sensor的Micro Lens与光电二极管之间存在一定的距离,这样的做的目的也是为了好搭配Lens。因为CAR为0度的Lens还是不好找的。。。。

普通的FSI的sensor都有一个类似光子井的结构用来收集光子:

image

当CRA增加的时候,光线会被金属电路层阻挡掉一部分,导致sensor接受光的效能降低。

那么针对上述存在的接收光效能的问题,解决办法如下:

  1. 增大pixel大小,这种影响会越小:
    image
  2. 对于BSI的sensor,这种影响也会更小:
    image
  3. 对于FSI的sensor来说,通常会通过移动sensor表面的Micro Lens来收集更多的光线:
    image

怎样选择Sensor的CRA

  • 广角镜头:这时一般Lens的CRA比较大,需要选择CRA大于25度的Sensor或者BSI的Sensor;一般用于手机、安防、玩具、网络摄像头等;
  • 超长焦镜头:这时一般Lens的CRA比较小,需要选择CRA为0度的Sensor;一般用于安防、机器视觉等;
  • 变焦镜头:这时Lens的CRA是变化的,一般需要根据实际应用选择,最好采用大pixel,BSI的Sensor;一般用于安防等;

Sony CMOS的CRA对收光的影响(安森美厂家对CRA的横轴描述的是CMOS对角线的长度,OV的横轴使用CMOS对角线的一半的长度作为描述)

垂直方向CRA
CMOS相关技术-小白菜博客
水平方向CRA

2. Lens Shading Correction(LSC) 镜头阴影区域矫正

     由于在CMOS成像过程中,在传感器边缘的像素由于镜头的光学特性,导致边缘像素接受到的光信号强度存在偏差,这种误差的大小取决于接手像素的位置以及光束颜色;为了补偿上述误差,通过设计基于位置、颜色的可控增益矫正模块,从而自动完成上述误差的矫正,如下图所示:

其他参考

CRA参数

二、CMOS动态范围HDR技术

(OverExposed)==>(HDR)

HDR发展史PDF 提取码: 7xtq

1、基于Linlog感光技术的CMOS动态范围扩充(实际为多段式像元-感光方案)

     LinLog感光技术是瑞士Photonfocus公司 在工业CMOS传感器开发中创新的专利技术,将传感器的 像元放大器增益特性设置为多段类对数特性 ,使其动态范围大大提高,最大动态范围可达 120 dB(一般的数字CCD 工业相机的动态范围最高只有 80 dB)12)。CMOS传感器工作在线性响应模式时,和 CCD 一样动态范围较小,传感器很快会饱和。对数响应模式时,CMOS 相机有非常大的动态范围,但其在低照度时灵敏度会降低,致使低照度区域成像质量严重降低,对比度很低,模糊不清。LinLog 感光技术使传感器仅在接近饱和及饱和区域为对数压缩响应,而保留其在低照度区域的线性响应和灵敏度,所以既扩展了整个传感器的动态范围,又保证了低照度区域成像的质量。图1表示出线性响应、传统对数响应与 LinLog 响应曲线的区别。

     运用 LinLog 感光技术后,较暗的熔池中后方又城图像对比度减小,为了增强这个区城的细节信息,在 Linlog 感光技木的基础上配合应用 Skimming 技木,它为低亮度区城提供了一种可调的对比度增强方法,与LinLog 感光技术一起,可实现低亮度区域高增益响应,稍亮区城正常线性响应,高亮度区域对数压缩响应、得到单调平滑的响应曲线、如图3 所示。Skimming技术使熔池图像中后方区域图像局部对比度增强,熔池起伏和气泡等细节信息清晰可见,其数值越大,对低亮度区域的对比度增强效果越明显。

2、基于分段线性的HDR高动态范围CMOS方案

Linear response

Piecewise linear response(PWLR)

Piecewise linear response

The dynamic range can be increased by dividing the integration (exposure time) in two or three phases (slopes), with different maximum saturation levels.
To use this kind of HDR mode the user has to define one/two pair(s) of PWLR parameters:
(T2,SL2) in case of one kneepoint and
(T1,SL1), (T2,SL2) in case of two kneepoints.

  • T1 and T2 define portions of the total exposure time and the length of the three timing phases (XI_PRM_HDR_T1,XI_PRM_HDR_T2).
  • SL1 and SL2 define portions of the sensor saturation, so called kneepoint1 and kneepoint2 (XI_PRM_KNEEPOINT1,XI_PRM_KNEEPOINT2).

image

HDR Mode PDF

链接: https://pan.baidu.com/s/1vhyG5Ji4TKxSnfZzlBplzw?pwd=jz3r 提取码: jz3r

3. 基于单像素偏振极化处理的反光抑制技术

通过在每一个像素成像单元前增加四种不同的极化偏振片,从而对于反射光进行极化采集处理,通过相邻四个像素之间的不同极化RAW图合成为最终图像,完成反射抑制,目前索尼的产品 Sony@IMX250,IMX264,IMX253.

Example

CMOS相关技术-小白菜博客 ==>

IM250MZR DataSheet 链接: https://pan.baidu.com/s/1clqo6IvoSMHdNX9kJ7Cmyw?pwd=gsn8 提取码: gsn8

4. 高动态范围HDR厂家芯片介绍

I. New Imaging Technologies (NIT) @ 法国

型号 厂商 光谱范围 分辨率 像素尺寸 帧频 量子效率 响应模式 动态范围 满阱容量 曝光时间 A/D 接口 OP温度 链接
WiDy SWIR 320 NIT@France 900 nm ~ 1700 nm 320 x 256 25um 200HZ 70% 对数 120dB 接近无限 100μs~200ms/100ns 门控 14Bits USB2.0 / CameraLink -40°C~+70°C
WiDy SenS NIT@France 900 nm ~ 1700 nm 640 x 512 15um 230HZ 70% 对数 & 线性 双模式 120dB 线性模式:高增益70Ke-、低增益400Ke-/对数模式:接近无限 100μs~200ms/100ns 门控 14Bits USB2.0 / CameraLink -40°C~+70°C
MC1105F https://new-imaging-technologies.com/product/mc1105f/
MC1003 https://new-imaging-technologies.com/product/mc1003/
  1. 中国区代理:想像光学

Reference

  1. 链接: https://pan.baidu.com/s/19bpu9OfC6vZAoTr8pt1Yjg?pwd=tvkn 提取码: tvkn
  2. 分段线性HDR:https://www.ximea.com/support/wiki/allprod/HDR_mode:

三、高速CMOS相关技术

1、高速摄像Binning技术介绍

     在通常的CMOS读取方式中,由于像素读取规模的差异,不同的分辨率对应不同的帧率。在通道带宽固定的前提下,想要提高帧率就要考虑是否需要缩小视野(外圈裁切)。若不希望视野缩小,需要减少采样的分辨率。
     常用的减少分辨率的两种方式是下采样,即Skipping(跳采样)和Binning(合并读出)。通过选取视野中的像素点,抽取指定像素点来降低分辨率。在Skipping模式中,并不会对所有行列的像素点进行采样,这样才能获取非原始分辨率的图像(降低的分辨率图像),而行列数据是成对读取的。

如下图为Skipping模式举例:

     Binning是一种图像读出模式,将相邻的像元中感应的电荷被加在一起,以一个像素的模式读出。Binning分为水平方向Binning和垂直方向Binning,水平方向Binning是将相邻的行的电荷加在一起读出;垂直方向Binning是将相邻的列的电荷加在一起读出,Binning这一技术的优点是能将几个像素联合起来作为一个像素使用,在Phantom高速摄像机的应用中,Binning Mode采用的是2x的Binning,即长宽缩短为原本一般,输出分辨率降低为原本的1/4。

Binning模式的优略势:

  • Binning Mode 优势:能够在视野面积和比例不变的前提下提高帧数,同时也可以提高暗处对光感应的灵敏度。
  • Binning Mode 劣势:降低输出图像解析力,只能输出单色图像。

2、卷帘快门(Rolling) VS 全局快门(Global) Shutter

Global Shutter(Total Shutter)

     通过整幅场景在同一时间曝光实现的。Sensor所有像素点同时收集光线,同时曝光。
     即在曝光开始的时候,Sensor开始收集光线;在曝光结束的时候,光线收集电路被切断。然后Sensor值读出即为一幅照片。
     CCD就是Global shutter工作方式。所有像元同时曝光。

CMOS相关技术-小白菜博客
     Sensor的所有行同时开始曝光,并同时结束曝光,在曝光结束后,Sensor将所有电子从感光区转到存储区,之后逐行地读出像素数据。这样曝光的好处是获得图像每一行的曝光时间比较一致,并且在拍摄运动物体时图像不会出现偏移和歪斜。
缺点: Global shutter 曝光时间更短,但会增加读出噪声;

Rolling Shutter

     与Global shutter不同,Rolling Shutter是通过Sensor逐行曝光的方式实现的。
CMOS相关技术-小白菜博客
     在曝光开始的时候,Sensor逐行扫描,逐行进行曝光,直至所有像素点都被曝光。当然,所有的动作在极短的时间内完成。不同行像元的曝光时间不同。

  • 逐行曝光从第一行开始曝光,一个行周期之后第二行才开始曝光。依次类推,经过 N-1 行后第 N 行开始曝光。
  • 第一行曝光结束后开始读出数据,读出一行需要一行周期时间(含行消隐时间)。至第一行完全读出后,第二行刚好开始读出,依次类推,当第 N-1 行读完后,第 N 行开始读出,直到整幅图像完全读出。
  • 逐行曝光的sensor 技术难度较全局曝光sensor 低,价格便宜,且分辨率较大,对于一些静态图像拍摄是不错的选择。

缺点: 对于相机厂家,Rolling shutter可以达到更高的帧率(因为在边曝光边读出行,节约了读取时间),但当曝光不当或物体移动较快时,会出现部分曝光(partial exposure)、斜坡图形(skew)、晃动(wobble) 等现象。这种Rolling shutter方式拍摄出现的现象,就定义为果冻效应
image

四、CMOS电路系统设计

1、CMOS系统框图

I. image sensor core(ASP)

     sensor是摄像头的核心,负责将通过Lens的光信号转换为电信号,再经过内部AD转换为数字信号。每个pixel像素点只能感受R、G、B中的一种,因此每个像素点中存放的数据是单色光,所以我们通常所说的30万像素或者130万像素,表示的就是有30万或130万个感光点,每个感光点只能感应一种光,这些最原始的感光数据我们称为RAW Data。Raw Data数据要经过ISP(应该理解为Image Sensor Processor,是Sensor模块的组成部分,下面有解释)的处理才能还原出三原色,也就是说如果一个像素点感应为R值,那么ISP会根据该感光点周围的G、B的值,通过插值和特效处理等,计算出该R点的G、B值,这样该点的RGB就被还原了,除此之外,ISP还有很多操作,下面有介绍。
    目前常用的sensor有两种,一种是CCD(电荷耦合)原件;一种是CMOS(金属氧化物导体)原件。

  • CCD(Charge Coupled Device),电荷耦合器件传感器:使用一种高感光度的半导体材料制成,能把光线转变成电荷,通过模数转换器芯片转换成电信号。CCD由许多独立的感光单位组成,通常以百万像素为单位。当CCD表面受到光照时,每个感光单位都会将电荷反映在组件上,所有的感光单位产生的信号加在一起,就构成了一幅完整的图像。CCD传感器以日本厂商为主导,全球市场上有90%被日本厂商垄断,索尼、松下、夏普是龙头。
  • CMOS(Complementary Metal-Oxide Semiconductor),互补性氧化金属半导体:主要是利用硅和锗做成的半导体,使其在CMOS上共存着带N(-)和P(+)级的半导体,这两个互补效应所产生的电流可以被处理芯片记录并解读成影像。CMOS传感器主要以美国、韩国和中国台湾为主导,主要生产厂家是美国的OmnVison、Agilent、Micron,中国台湾的锐像、原相、泰视等,韩国的三星、现代。

II. image sensor processor(图像处理芯片 DSP)

     DSP是CCM的重要组成部分,它的作用是将感光芯片获得的数据及时地快速地传递到中央处理器并刷新感光芯片,因此DSP芯片的好坏,直接影响画面品质,如:色彩饱和度、清晰度、流畅度等。如果sensor没有集成DSP,则通过DVP的方式传输到baseband芯片中(可以理解为外挂DSP),进入DSP的数据是RAW Data,采集到的原始数据。如果集成了DSP,则RAW Data会经过AWB、color matrix、lens shading、gamma、sharpness、AE和de-noise处理,最终输出YUV或者RGB格式的数据。如下图中所示,ISP(Image Sensor Processor,不是Image Signal Processor)部分中使用的就是DSP来处理Image sensor的输出数据(Raw Data):

DSP结构框架:

  1. ISP(Image Signal Processor),镜像信号处理器或者图像信号处理器
  2. JPEG encoder,JPEG图像编码器,有软件硬件之分。

III. CMOS数据输出接口 —— DVP

DVP(Digital Video Port) 是传统的sensor输出接口,采用并行输出方式,d数据位宽有8bit、10bit、12bit、16bit,是CMOS电平信号(重点是非差分信号),PCLK最大速率为96MHz,DVP分为三个部分:输出总线;输入总线;电源总线;如下所示:

  1. 输入总线介绍
    • PWDN是camera的使能管脚,有两种配置方式,一种为standby,一种是normal work,设置为standby的时候,一切对camera的操作都是无效的,包括复位。所以在RESET之前,一定要将PWDN管脚设置为normal模式,否则RESET无效。
    • RESET是camera的复位管脚,此方式为硬复位模式,camera的各个IO口恢复到出厂默认状态。只有在XCLK开启后,将RESET置为低,硬复位才有效,否则复位无效。
    • XCLK是camera的工作时钟管脚,此管脚为BB提供camera的工作时钟。
    • IIC是camera和BB通信的总线通道。
  2. 输出总线介绍
    • data为camera的数据管脚。此数据脚可以输出的格式有YUV、RGB、JPEG等。
    • VSYNC为camera的帧同步信号管脚。一个VYSNC信号结束表示一帧(即一个画面)的数据已经输出完毕。
    • HSYNC为camera行同步信号管脚。一个HSYNC信号结束表示一行的数据已经输出完毕。
    • PCLK为像素同步信号管脚。一个PCLK信号结束表示一个像素点的数据已经输出完毕。
  3. Power线介绍
    • AVDD为camera的模拟电压。
    • DOVDD为camera的GPIO口数字电压。
    • DVDD为camera的核工作电压。

一般来说,要求先提供sensor的GPIO口电压,接着提供模拟电压,最后提供工作电压。

IV. CMOS数据输出接口 —— MIPI

V. CMOS数据输出接口 —— LVDS

上述接口详述请参考这里:Camera&视频数据传输接口—目录

2、数字增益控制 Digital Gain Control

     数字增益控制模块作为控制电压放大器放大率的模块,对不同通道的数据分别可配置不同的增益参数,海力士传感器 CMOS@AAA6411PXX 支持了 13bit 的增益配置寄存器, \(bit [12:9]\) 用于配置增益整数部分,\(bit [8:0]\) 用于控制小数部分,计算公式如下:

\[Digital_{Gain} = bit[12:9] + \frac{bit[8:0]}{512}
\]

总增益范围为 1~15.99倍。

3、黑电平 BLACK LEVEL

     黑电平(Black Level),也称作:Optical Black,很多人也称呼为OB,指的是光学暗区矫正,也就是黑色的最低点,指在经过一定校准的显示装置上,没有一行光亮输出的视频信号电平。定义图像数据为0时对应的信号电平。也可以比喻我们平时在称量物品的之前的较0处理。

     因为sensor本身存在暗电流,导致在没有外部光线照射的时候,也有一定的输出电压。最终sensor的输出需要减去这个数值,

     一般我们知道,sensor上会预留了一些完全没有曝光的像素,在上下两端都有一些未曝光的像素行,通过读取这些像素值的大小,可以实时得到optical black level,因此,一般在从sensor厂商那边的说法更称呼为OB,此时sensor的输出RAW = sensor input – optical black level,每个sensor都有OB区域,CCD sensor初期成像效果较差,它会把top OB 和 side OB暴露给isp使用,这样isp可以自己估算不同区域得到不同的black level值,再分别处理。然而ob并不能真实的反映黑电平的值,因为尽管这些black lines已经充分考虑到了不同column的OB不同,但因为在sensor边缘的black lines会受到PCB layout、电源纹波、模组结构设计等等因素的影响,故此时OB扣除的还是可能不准确,有的sensor厂商可能不会使用这个方法,因此目前很少有ISP或sensor去使用ob区域进行计算,每家都会使用sensor内部的isp根据自家设计的算法去动态实时计算optical black level值,然后在sensor输出的时候把它去掉。

     由于考虑到sensor输出的信噪比,故一般sensor输出数据时又会垫上一个基底pedestal,那就是此时sensor的RAW = sensor input – optical black level + pedestal。对于芯片处理来说,一般拿到的就是这个数据,此时需要在ISP处理流程的起始部分减去这个基底。在ISP内部一般叫做BLC,即Black Level Correct黑电平校正。

关于增加pedestal 基底可以增加输出信噪比的缘由:

  • CMOS传感器采集的信息经过一系列转换生成原始RAW格式数据。以8bit数据为例,单个pixel的有效值是0~255,但是实际AD芯片(模数转换芯片)的精度可能无法将电压值很小的一部分转换出来,因此,sensor厂家一般会在AD的输入之前加上一个固定的pedestal,使输出的pixel value在16(每家不同)~255之间,目的是为了让暗部的细节完全保留,当然同时也会损失一些亮部细节,加了基底,高光的信号会有溢出现象,但对于图像来说,但考虑到人眼对于画面暗处更敏感,我们的关注度更倾向于暗部区域,ISP后面会有很多增益模块(LSC、AWB、Gamma等),因此亮区的一点点损失是可以接受的。
  • 因为针对暗部区域,我们有了基底,可以更好的配合raw降噪算法,一般的raw域降噪算法都是双边滤波器,在高通平台上是ABF模块,有了基底,才能更好的计算和过滤暗区的噪声,提高信噪比。

综上,黑电平校正可能是做过两遍的,第一遍做在了sensor内部,第二遍做在了芯片ISP处理的开始。

4、供电电源选择

     PSRR提供了LDO如何抑制纹波或如何阻挡噪声的度量仅在LDO的输入处由供电轨产生。PSRR越高,噪声越大或者可以从电源阻断纹波。这些涟漪的来源可以来自具有50/60 Hz纹波的输入电源,DC−DC的开关频率,或由于共享而产生的纹波不同电路之间的输入电源。

     LDO的反馈回路通常控制系统的PSRR,频率小于100 kHz,因此确保选择了正确的LDO。对于100kHz以上的频率无源元件和PCB布局/放置的适当选择控制PSRR。
     设计PCB时应注意,以减少紧电流回路寄生电感和电源轨和相机轨之间的波纹。在Vin和Vo之间使用干净的偏置轨或更高的净空也可以增加PSRR性能能力。低PSRR性能或模拟轨上的任何噪声都会导致电源上的噪声轨道通过高增益源极跟随器放大器电路进入输出信号路径,导致在捕获的图像中出现不希望的水平波纹。

     正常LDO在高频时具有较低的PSRR,这应足以满足标准相机,但具有50–200 MP范围内的高分辨率和高帧速率图像传感器,确实需要一系列特定的LDO,其高PSRR在较低的频率下大于90 dB频率范围(最高10 kHz),在1−3 MHz频率范围内大于45 dB帧速率和行速率转换期间的波纹。

五、CMOS数据输出格式

数据格式种类:

  1. YUV: luma(Y)+chroma(UV)格式(亮度、色度),一般情况下sensor支持YUV422格式,即Y-U-Y-V次序输出数据。
  2. RGB: 传统的红绿蓝格式,比如RGB565,5bit R + 6bit G + 5bit B,G多一位是因为人眼对绿色比较敏感。
  3. RAW RGB: sensor的每一像素对应一个彩色滤光片,滤光片按Bayer Pattern(拜耳模板)分布,将每一个像素的数据直接输出,即Raw RGB Data。
  4. JPEG: 有些sensor,特别是低分辨率的,其自带JPEG engine,可以直接输出压缩后的jpg格式的数据。

1. MIPI-RAW图像数据 & RAW图像数据

在ISP图像处理中经常需要面对MIPI RAW数据和RAW数据,这里简单说明一下二者的差别。
传感器采集的RAW数据通常为10bit,存储RAW数据需要两个Byte,而其中有6个bit位是空着的,这样就有存储空间浪费。
MIPI RAW数据充分利用了这个特性,采用5个Byte,共40bit存储4个RAW数据。
这里以大端存储方式,给出了从MIPI RAW数据恢复RAW数据的方法:

//b1,b2,b3,b4,b5分别为连续读出的五个uint8类型的数据,p1,p2,p3,p4为四个像素点的像素值,那么
p1 = (b1 << 2) + ((b5     ) & 0x3);
P2 = (b2 << 2) + ((b5 >> 2) & 0x3);
P3 = (b3 << 2) + ((b5 >> 4) & 0x3);
P4 = (b4 << 2) + ((b5 >> 6) & 0x3);

MIPI-RAW=>RAW转换代码:

点击查看
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char raw_array[3840*2160*2];
short pixel_array[3840*2160*2];

static const char *format_array[5] = {"RAW8", "RAW10", "RAW12", "RAW14", "RAW16"};
static const char *help = "help";

int main(int argc, char *argv[]){
    char *file_name = argv[1];
    char *format = argv[2];
    int width = atoi(argv[3]);
    int height = atoi(argv[4]);
    char out_file[256];
    int format_index;
    int byte_index = 0;
    int pixel_index = 0;
    int in_size = 0;
    int out_size = 0;
    printf("Usage: <filename> <format: RAW8/RAW10/RAW12/RAW14/RAW16> <width> <height>\r\n");
    printf("file name: %s \r\n", file_name);
    printf("format: %s \r\n", format);

    printf("width: %d \r\n", width);
    printf("height: %d \r\n", height);

    FILE *raw_fb = fopen(file_name, "rb" );
    if(!raw_fb) {
        printf("open input file faile \r\n");
        return -1;
    }

    sprintf(out_file, "out_%s_%s", format, file_name);
    printf("open input file name: %s\r\n", out_file);
    FILE *pixel_fb = fopen(out_file, "wb");

    if(!pixel_fb) {
        printf("open output file faile \r\n");
        return -1;
    }

    for(int i=0; i<5; i++) {
        if(0 == strcmp(format_array[i], format)) {
            format_index = i;
            break;
        }
        format_index = 5;

    }

    printf("start convertion format_index: %d \r\n", format_index);
    switch(format_index) {
    case 0:
        in_size = width*height;
        out_size = width*height;
        break;
    case 1:
        in_size = width*height*10/8;
        out_size = width*height*2;
        break;
    case 2:
        in_size = width*height*12/8;
        out_size = width*height*2;
        break;
    case 3:
        in_size = width*height*14/8;
        out_size = width*height*2;
        break;
    case 4:
        in_size = width*height*2;
        out_size = width*height*2;
        break;
    default:
        printf("format is unsupport !!! \r\n");
        break;
    }

    fread(raw_array, 1, in_size, raw_fb);

    switch(format_index) {
    case 0:
        // RAW8 don't need convert
        break;
    case 1:
        // RAW10
        for(byte_index=0, pixel_index=0; byte_index < (in_size-5); ) {
            pixel_array[pixel_index+0] = (((short)(raw_array[byte_index+0]))<<2) & 0x03FC;
            pixel_array[pixel_index+0] = pixel_array[pixel_index+0] | (short)((raw_array[byte_index+4]>>0) & 0x0003);
            pixel_array[pixel_index+1] = (((short)(raw_array[byte_index+1]))<<2) & 0x03FC;
            pixel_array[pixel_index+1] = pixel_array[pixel_index+1] | (short)((raw_array[byte_index+4]>>2) & 0x0003);
            pixel_array[pixel_index+2] = (((short)(raw_array[byte_index+2]))<<2) & 0x03FC;
            pixel_array[pixel_index+2] = pixel_array[pixel_index+2] | (short)((raw_array[byte_index+4]>>4) & 0x0003);
            pixel_array[pixel_index+3] = (((short)(raw_array[byte_index+3]))<<2) & 0x03FC;
            pixel_array[pixel_index+3] = pixel_array[pixel_index+3] | (short)((raw_array[byte_index+4]>>6) & 0x0003);
            byte_index = byte_index + 5;
            pixel_index = pixel_index + 4;
        }
        break;

    case 2:
        // RAW12
        for(byte_index=0, pixel_index=0; byte_index < (in_size-3); ) {
            pixel_array[pixel_index+0] = (((short)(raw_array[byte_index+0]))<<4) & 0x0FF0;
            pixel_array[pixel_index+0] = pixel_array[pixel_index+0] | (short)((raw_array[byte_index+2]>>0) & 0x000F);
            pixel_array[pixel_index+1] = (((short)(raw_array[byte_index+1]))<<4) & 0x0FF0;
            pixel_array[pixel_index+1] = pixel_array[pixel_index+1] | (short)((raw_array[byte_index+2]>>4) & 0x000F);
            byte_index = byte_index + 3;
            pixel_index = pixel_index + 2;
        }
        break;

    case 3:
        //RAW14
        for(byte_index=0, pixel_index=0; byte_index < (in_size-7); ) {
            pixel_array[pixel_index+0] = (((short)(raw_array[byte_index+0]))<<6) & 0x3FC0;
            pixel_array[pixel_index+0] = pixel_array[pixel_index+0] | (short)((raw_array[byte_index+4]>>0) & 0x003F);
            pixel_array[pixel_index+1] = (((short)(raw_array[byte_index+1]))<<6) & 0x3FC0;
            pixel_array[pixel_index+1] = pixel_array[pixel_index+1] | (short)((raw_array[byte_index+4]>>6) & 0x0003);
            pixel_array[pixel_index+1] = pixel_array[pixel_index+1] | (short)((raw_array[byte_index+5]>>0) & 0x000F);
            pixel_array[pixel_index+2] = (((short)(raw_array[byte_index+2]))<<6) & 0x3FC0;
            pixel_array[pixel_index+2] = pixel_array[pixel_index+2] | (short)((raw_array[byte_index+5]>>4) & 0x000F);
            pixel_array[pixel_index+2] = pixel_array[pixel_index+2] | (short)((raw_array[byte_index+6]>>0) & 0x0003);
            pixel_array[pixel_index+3] = (((short)(raw_array[byte_index+3]))<<6) & 0x3FC0;
            pixel_array[pixel_index+3] = pixel_array[pixel_index+3] | (short)((raw_array[byte_index+6]>>2) & 0x003F);
            byte_index = byte_index + 7;
            pixel_index = pixel_index + 4;
        }
        break;
    case 4:
        // RAW16 don't need convert
        break;
    default:
        printf("formt is unsupport !!! \r\n");
        break;
    }

    fwrite(pixel_array, 1, out_size, pixel_fb);
    fclose(raw_fb);
    fclose(pixel_fb);
    printf("convert finished !!! \r\n");
    return 0;
}

Reference

Understanding Challenges in Powering High Resolution, High Frame Rate CMOS Image Sensors 密码:v6kj
Image Sensor Handling and Best Practices 密码:bac2
Image Sensor ISO Measurement 密码:r2rw
camera理论基础和工作原理