做了快十年 Camera,有不少同学问我:想转行做相机、或者刚毕业想往这走,从哪学起?这篇不是教程,是个过来人给完全没碰过 Camera、但会 Java 和 Android 的人,理的一条能落地的路。但我先不讲 API、不讲架构——我先带你去相机工厂"卧底"一天。
先问你一件你八成从没想过的事:你昨天发朋友圈那张照片,在被你"看到"之前,光,在手机里跑了一场什么样的接力赛?
如果你脱口而出"不就是相机 App 调个接口嘛"——那今天这篇,就是专门写给你看的。
真相是:你按下快门那一下,小光要穿过五层楼、跨两个部门、被十几道车间轮番盘问、被十几种算法挨个收拾,才从"外面那个世界"变成"相册里那张图"。而且这场接力赛,在你按快门之前就偷偷开跑了——你屏幕上那个实时预览,就是小光在跑第一棒。
网上能搜到的教程,两极分化得厉害。一头太浅,通篇"Camera 就是调 Camera2 API 拍照",看完你还是不知道相机为啥卡、竖屏拍出来为啥歪、夜景为啥能拍亮。另一头太深,上来让你啃 CamX 源码、读 Usecase 那堆 C++ 模板,三天基本劝退。
我自己前后写了 57 篇笔记(后来叫"小驰私房菜")。这篇就当个过来人,先把"从哪开始、学什么、以及你手机上那些怪现象到底是哪一层的锅"给你理清楚。文中所有平台代号、sensor 型号、项目路径都按通用写法给出,只讲原理和思路,具体代号你以手头平台为准。
下面这张表建议先扫一眼,后面讲到对应关时你会回来翻它。它也是整篇的目录。
小光要进厂打工,得先过几道岗前体检——不是考它,是考你。很多初学者不是不聪明,是一上来直接扎进 HAL 源码,结果卡在「这行 C++ 在干啥」「这份 datasheet 哪个表是分辨率」。下面这张单子,按「方向 → 要懂啥 → 哪层用得上 → 怎么补」列清楚。先别急着全会,对着勾,知道自己缺哪块、去哪补,就够了。
| ① 图像 / 信号处理 | |||
| ② 硬件 / sensor | |||
| ③ 软件 / C++ | |||
| ④ 我补的三块(你没提但必卡) |
很多人学 Camera 卡住,是因为一开始的认知就歪了:以为相机开发 = 会调 API 就行。其实 Android 相机是分五层的——你写的 App 调用的那点接口,只是顶楼。
咱们把手机相机想成一栋工厂大楼,一共五层:
- 顶楼 · App 办公室
——你写的相机 App,负责"接单"。 - 四楼 · Framework 经理层
——CameraService 坐这,它就住在"厂长"cameraserver 的大办公室里(和 App 不在同一进程,但跟 cameraserver 同住)。 - 三楼 · HAL 外包部
——叫 CameraProvider,注意,它是个独立部门,不在 Framework 那栋楼里(这是 Treble 改革后的事)。 - 二楼 · Kernel 后勤
——驱动、内核,管上电、配时钟这些脏活。 - 一楼 · Hardware 车间
——Sensor 等真硬件,小光就是从这儿"出生"的。
第一步别读源码,先把"从点开相机到第一帧出来"在脑子里走一遍。
新手最爱把 sensor 当黑盒。其实做 Camera 驱动,第一件事就是跟硬件打交道。小光不是孤身一人——它出生在一整个模组团队里。
我笔记第 02 篇讲"新增一个 driver 要准备什么",清单很实在:原理图、sensor 规格书、马达规格、lens 参数、EEPROM/OTP 数据、模组厂给的 golden……这些不是文档套话,是你真要去跟硬件同事和模组厂要的东西。少一样,bring-up 的时候你就知道什么叫寸步难行。
趁早弄懂这几块,不然后面看代码全靠猜:
- facing / orientation / mount angle
。facing 是前后摄,orientation 是旋转角,但真正坑人的是 mount angle 那套 roll/pitch/yaw——它是从 dtsi 里读出来的。第 06 篇专门捋过,因为这几个值散在 camx 代码好几个地方,新手看一个懵一个。 - Remosaic / QuadCFA / Binning
。现在主流 sensor 基本都是 QuadCFA(四合一),不懂这个你看不懂它输出尺寸为啥那么怪,也理解不了"四合一"模式下拍照和预览的区别。第 03 篇讲的。 - OTP / EEPROM 校准
。sensor 出厂每个都不一样,模组厂烧一份 golden,开机读出来用。第 10 篇讲怎么 dump,以及 camx 和老框架开关不一样。 - 3A(AE/AF/AWB)
。先扫个盲:AE 管曝光、AF 管对焦、AWB 管白平衡。AE 还能加人脸权重(Face AE)——逆光人脸不死黑就靠它,第四关讲。别一上来扎进去。
你拿着手机竖拍,但 Sensor 在模组里是固定方向焊死的,它"认为"的上下和你握的方向不一定一致。Framework 拿 orientation 和 dtsi 里的 mount angle(roll/pitch/yaw)来把图转正。这套值配错了,就会出现"预览是正的、存下来是歪的",或者"某些机型旋转怎么都不对"——根因往往就在驱动里这几个角度参数。所以 facing/orientation 看着枯燥,真出问题能让你调一下午。
小光从 Sensor 出来时,是个"黑白盲盒"——咱稍后解释。ISP 干的事,就是把"生"的小光做成能看的图。高通把 ISP 拆成三道车间:IFE、BPS、IPE。但有个点特别容易看漏,也是我当年踩过的坑:
IFE 是流水线第一道,直接吃 Sensor 的 raw,干坏点校正(ABF)、镜头暗角、PDAF 那些前端脏活。BPS 只活在拍照线,做 demosaic(下面细说)和一部分拜耳域处理。IPE 是真正的"精修化妆间":缩放、锐化(ASF)、降噪(WNR/HNR/MFNR)、局部色调映射(LTM)、各种颜色增强,最后送显示或编码。你照片里"通透感""夜景干净程度"大半是 IPE 的功劳。
RAW/DNG 就是小光刚出厂、还没进 BPS 的素颜——没 demosaic、没 ISP 调色,所以平、灰、偏色。但这恰恰是后期空间大的原因:你拿到的是最原始的信号。JPEG 则是小光走完 IFE+BPS+IPE 全套精修后的"妆后照"。理解它,你就懂了"为什么专业模式 RAW+JPG 双出"。
ISP 里那一串缩写 ABF/LTM/ASF/WNR/CC/ACE/MCE/SCE,初学者最容易被劝退。但它们每一个都是"先有毛病,才造出这个工匠"。我把每个 ISP 模块的"病症"和"治法"整理成卡片——理解了"它在修什么",比背名字有用十倍:
你照片里"通透感""夜景干净""肤色好看",拆开看全是上面这些工匠在分别干活;而"逆光人脸不死黑"第一功臣是 3A 里的 Face AE(人脸区域加权测光),LTM 这类局部提亮工匠再搭把手。调图(tuning)工程师一辈子就在调这些工匠的表和强度。
记不住就念——"预览不进 BPS,拍照才进 BPS",这句值一千行代码。
前面铺垫够了,现在回头看你每天用手机拍照那些"玄学现象"。每一个,都能在小光的旅行里找到对应的一站。
逆光人脸不死黑,主要功臣不是 HDR,是 Face AE。普通 AE 对整个画面平均测光,逆光时背景亮得晃眼、人脸却黑乎乎,一平均就按背景曝光——人脸直接糊成剪影。Face AE 不一样:3A 里先做人脸检测框出脸,把测光权重死死压在人脸区域,曝光就按"让人脸正常亮"来定。所以"人脸不死黑"第一负责人是 Face AE。
而"亮处不过曝、暗处不死黑"才是 HDR 的活儿:连拍几张不同曝光的图——一张欠曝保住天空高光、一张正常、一张过曝保住暗部——对齐融合成"亮暗都清"的图;融合完再由 IPE 里的 LTM 做局部色调映射,只把暗部提亮、亮部原样不动,所以不会"为了救暗部把整张图弄得灰扑扑"。后面进阶篇给 HDR / Face AE 的开关和 dump 命令。
Camera2 把格式设成 YUV_420_888,ImageReader 会给你三个 Plane(Y/U/V)。但每个 Plane 在内存里怎么排,厂商实现不一样。我笔记第 04 篇举了个真事:我手头两台不同品牌的手机,一台(华为 P20)getRowStride 等于宽度,另一台(小米 8)不等——比如 width=8,RowStride 可能是 10,后面有补齐。所以你取数据不能直接 memcpy,必须按 RowStride 跳着读,否则出来就是花屏。
怎么算 Stride?width 宽、每像素 N 字节:stride = N * width,不是 4 的倍数再补齐到 4 的倍数。排查时,高通 camx 框架下 logcat 过滤StrideXsliceHeight就能看到 dump 出来的 stride 和 sliceHeight,一帧 1920×1080 用 yuv 工具看时把 size 设成 2048×1536 就能正常显示——多出来的就是对齐 padding。
前面的铺垫都为了这一步不劝退。CamX-CHI 的几层你先记:Usecase(一个具体配置)→ Feature(要开的若干功能)→ Pipeline(实际的数据加工流,驱动层靠它理解数据怎么走)→ Node(流水线上的一个操作块)。这里有个特别容易搞反的点:Pipeline 内部长什么样,是用一张「有向无环图(DAG)」来描述的,这张图就叫 Topology——它不是 Pipeline 上面再叠一层,而是 Pipeline 的"图纸":一串 Node 被 link 串起来、中间用 buffer 传数据,Node 串起来就构成了 Topology。整张图写在 XML 里(这套描述也叫 "Pipeline – XML Topology Graph")。CHI 全称 Camera Hardware Interface。
把它想成一家大公司的汇报链就通了:
- Usecase = 公司接的一个具体单子
,比如"20MP 连拍 ZSL + 2K 预览"算一个 usecase。一个 usecase 可以带好几个 Feature。 - Feature = 单子里的"功能开关"
(ZSL / 夜景 / 人像…),开哪些,决定这条流水线要干哪些活。 - Pipeline = 这条数据加工流水线本身
,驱动层靠它理解数据怎么流。 - Topology = Pipeline 内部的 DAG 图纸
——一串 Node 用 link 连起来、中间用 buffer 传数据,这就是 topology。它不是 Pipeline 的上一层,而是"Pipeline 长什么样"的描述,写在 XML 里(也叫 "Pipeline – XML Topology Graph")。 - Node = 图纸上的一个操作工
,干一件具体事(比如"把水印贴上去")。Node 串起来,就构成了 Topology。
而且——整张图写在 XML 里,等于说这家公司的"组织架构图"是一份可改的配置文件。你要加个新工人(自定义 Node),改改 XML + 写个 Node 类就行。
真要动手,从两件事切入最划算:
- 加一个自己的 Node
。我第 05 篇以"加水印"为例,讲怎么在 camxnode.cpp 里加一个 Node::WatermarkImage。这是理解 Node 机制最快的方式——你改的不是某个神秘配置,而是实实在在地往流水线里塞了一道工序(相当于给车间加一名新工人)。 - 学会 debug
。camx 有个 camxoverridesettings.txt,配合persist.vendor.camera.*那堆属性,能开关各种日志、强制走某条路径。帧率怎么查、EIS 怎么开,我第 07、08、18–22 篇都有具体命令。这些不是理论知识,是每天干活都要用的。
persist.vendor.camera.raw.dump 1(代码在 QCamera3HWI.cpp 附近)。 - 想看 HDR 中间结果:设persist.vendor.camera.imglib.dump 1再persist.vendor.camera.imglib.hdr.dump inout,连输入输出一起 dump。 - 这些命令在不同平台属性名可能略有差异,但套路一样——先把数据落盘,再拿工具一点点看。新一点的平台还有 Feature2 框架(GraphSelector/GraphManager、TBM、FRO 那些)。我的建议是:先把上面老架构逻辑吃透,再去看 Feature2,不然容易被新名词带着跑。
我笔记第 057 篇分析过一个真实的 Feature2 图:输入 3 张 Raw、输出 1 张 Raw 的 RawHDR,它的 feature graph 是
RT → RawHDR → Bayer2YUV → JPEG。意思是:RealTime 实时流先跑,RawHDR 这颗 feature 把 3 张 raw 融成 1 张,再交给 Bayer2YUV 转 YUV、最后 JPEG 编码。你看,Usecase→Feature→Pipeline→Node 那套抽象,到这就能对应上一个真实串接。进代码后,这种图就是你的地图。MTK 是 mtkcam3 那套,imgsensor 的玩法和高通不一样,路径、命名、调优入口全两样。但背后的思想——分层、数据流、3A、ISP 处理——是相通的。这篇先不展开,免得你一开始两头抓,后面单独写。
入门之后,真正拉开差距的是这几块——它们也是如今做相机最吃香的方向:
- 帧率和性能优化
。为什么配了 60fps 实际只有 30?一个很实的思路:看 SOF(sensor 每帧起始)间隔,比如日志里 frame 37、38 间隔约 33ms,那实际就是 30fps;如果看到 requestID=0,说明是无效帧被 Kernel 丢掉了(KMD drop)。还有个坑叫slowJpegMode——一旦被选中,ZSL 就不支持了,整条配置都会变。这些我在第 08、18–20 篇有实战思路。 - 应用层集成 Raw 域算法
。比如用 createReprocessableCaptureSession把 raw 拿出来自己算,再塞回去。第 09 篇讲的,涉及 privapp 权限那套坑:高通平台要在persist.vendor.camera.privapp.list里加你应用的包名(注意 selinux,最好setenforce 0再设)。MTK 和高通思路一致——App 层先查REQUEST_AVAILABLE_CAPABILITIES里有没有 RAW,再看availableStreamConfigurations里 OUTPUT 有没有 32(RAW16)。 - 常见问题怎么查
。没捷径,就是踩出来的。比如 sensor 上电:日志里能看到 Mclk 给到 24MHz、i2c slave address、搜到具体 sensor 名,这些是 bring-up 时确认"硬件通了没"的第一手信号。我笔记后面那些篇幅,大半是踩坑复盘。 - 功耗与发热控制
。相机是整机耗电和发热的大户:sensor 上电、ISP 全速跑、DDR 带宽拉满,再加夜景那种"连拍好几张"的长曝光,电量肉眼可见往下掉。更坑的是发热会触发 thermal throttle(降频),直接表现为"拍一会儿就掉帧、画质也跟着降"——所以功耗和前面的帧率其实是一根绳上的。进阶思路是低功耗流水线:能跳过的不跑、预览分辨率往下降、stream on/off 时序掐准,还得算清楚 ZSL 那批环形 buffer 的内存和功耗代价。 - 多摄协同与一致性
。现在的旗舰卖点大半在"几颗镜头像一个人"。难点不在单摄,而在主摄 / 超广角 / 长焦之间:色彩、白平衡、曝光要对齐,切换要无感,双摄还要算景深做虚化。你前面看的小光"三兄弟接力",到进阶就是"怎么让三兄弟拍出来像同一台机器"。 - 计算摄影与 AI(跑在 DSP / NPU 上)
。夜景、人像、超分、虚化,越来越多不是纯 ISP 硬算,而是把模型丢到 hexagon DSP / NPU 上跑。这意味着进阶工程师得懂算法怎么部署、模型在功耗和性能之间怎么权衡——这也是这几年行业最明显的一个方向。 - 视频能力与防抖
。录像和拍照是两条不同的通路,市场又最看重:4K/8K 能不能稳、EIS/OIS 防抖到不到位、高动态视频(HDR video)能不能压住。稳定性(不卡顿、不掉帧)和首帧延迟(冷启动多快出画面)都是硬指标,后面会单列来讲。 - 画质调优(tuning)与客观评测
。前面那些玩意儿最终都得"落到画面上好看"。每个模组都要做 tuning:AE/AWB/AF 的表、ISP 各模块的参数、镜头 shading,靠 Chromatix7 那类工具一点点调。还要会看 IQ 测试 chart、读客观指标、做竞品对比——这是 Camera 工程师最日常、也最见功力的活。
从 App 到 Kernel 一条线,任何一环你不懂,小光的旅行就卡在那。
顺着小光的脚印走一遍,比背十本手册都管用。
我这些笔记也是踩了好几年坑才攒出来的。不指望你照着看一遍就全会,但按这个顺序走——先建立分层认知(顺手把"每天拍照那些怪事"对上号),再啃硬件和驱动,然后理解 ISP 和数据流(记住预览跳过 BPS、拍照走 BPS),最后进平台代码动手改——至少不会一上来就被源码劝退。
具体每一块怎么往深了挖,后面我按这个路线一篇篇写。这篇先解决"从哪开始、学什么、以及你手机上那些现象到底是哪一层的锅"的问题。
《更多交流,欢迎加入知识星球》
评论 (0)