原文地址查看

前言

市面上主流摄像头的图像封装格式一般逃不过这三种:JPEG、MJPG和YUV。其中YUV编码既可以与灰度图像兼容,又利用了人眼对亮度和色度的定量优化,使其可以直接跟三原色RGB进行直接互换而到广泛青睐。但YUV与RGB的转码涉及大量浮点运算,对于高分辨率高速摄像头而言,转码对CPU的负担很重,本文来看看如何巧妙化解这个难点。

摄像头捕获视频数据

摄像头之所以可以捕获图像,使用了很多感光点来采集光线,感光原理就是通过一个个的感光点来对光进行采样和量化,平常所说的 300 万像素的摄像头,指的就是有大约 300 万个感光点。

摄像头的输出数据格式一般有三种:
1,rawRGB
这种格式就是直接输出摄像头的感光点输出,由于每一个像素点包含 RGB 中的一种颜色,因此被称为 rawRGB 而不是 RGB,从 rawRGB 到 RGB 还需要一个“反马赛克”的算法。最后得到的 RGB 就是图像的原生数据了。
2,MJPG/JPEG
由于 rawRGB 或者 RGB 这样的数据没有经过任何加工和压缩,尺寸很大,因此一般市面上的摄像头都不会直接输出这样的格式,最常见的做法是压缩成 JPEG 或者 MJPG 格式来输出。
3,YUV

这是一种更为流行的格式。根据人类眼睛的视觉特征设计——由于人类的眼睛对亮度的敏感度比颜色要高许多,而且在 RGB 三原色中对绿色有尤为敏感,利用这个原理,可以把色度信息减少一点,人眼也无法查觉这一点。YUV三个字母中,其中"Y"表示明亮度(Lumina nce或Luma),也就是灰阶值,而"U"和"V"表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。我们可以通过减少图像中的红色和蓝色分量来达到减少图像尺寸的目的。在很多技术文档中YUV还会写成YCbCr,Y指的是绿色和亮度,C是Component的首字母,b和r分别是blue和red,从这个角度出发可以认为YUV是RGB的变种。

YUV与RGB格式转换

YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式。因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式。

RGB与YUV的变换公式如下:

YUV(256 级别) 可以从8位 RGB 直接计算:

Y = 0.299 R + 0.587 G + 0.114 BU = - 0.1687 R - 0.3313 G + 0.5 B + 128V = 0.5 R - 0.4187 G - 0.0813 B + 128

反过来,RGB 也可以直接从YUV (256级别) 计算:

R = Y + 1.402 (Cr-128)G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)B = Y + 1.772 (Cb-128)

可见,YUV可以看做是RGB的优化变种。并且更进一步,既然U和V对人类的眼睛不敏感,我们可以针对它们做各种变化,来减少整体图像的尺寸。具体情况我们来一个个看。

第一种:YUV422.后面的数字可以理解为代表YUV分量的比例是4:2:2,其原理是在每个像素中删去一个U或者V分量,然后再在还原的时候用相邻的像素的UV分量填充,一图顶千言:

这样做虽然损失了原先的一部分UV信息,但是人眼对此部分信息不敏感,海原就可以得到很好的效果,照这样的思路,可以对YUV进程更激进的裁剪,比如下面这种压缩率大一点的YUV411:

还有这种更没人性的YUV420:

以上各种YUV格式思路大同小异,都是通过裁剪UV信息来达到缩小图像尺寸的目的,因此不再一个个赘述了。我们以最常见的YUV422为例,来看看从摄像头中捕获这种数据之后,怎么极速转化为RGB。

首先,由上面转换公式可知,YUV与RGB存在以下互换公式:

R = Y + 1.042*(V-128);G = Y - 0.344*(U-128)-0.714*(V-128);B = Y + 1.772*(U-128);

有了以上公式,我们自然可以写出如下代码,来计算每一个YUV像素点的等价RGB值,比如我们有原始YUV422的两个像素为:

那根据公式我们很容易写出对应的两个 RGB 像素点的数值为:

R0 = *Y0 + 1.042*(*V1-128);G0 = *Y0 - 0.344*(*U0-128) - 0.714*(*V1-128);B0 = *Y0 + 1.772*(*U0-128);R1 = *Y1 + 1.042*(*V1-128);G1 = *Y1 - 0.344*(*U0-128) - 0.714*(*V1-128);B1 = *Y1 + 1.772*(*U0-128);

以上算式只是其中两个像素点的计算量,假如摄像头的分辨率是1280×720,那么一帧这样的YUV数据就得进行好几百万次浮点运算,而最普通的摄像头一秒可以产生25-30帧数据,高速摄像头每秒可以产生几百到几千帧数据(激光扑捉器每秒200亿帧了解一下),这还不算转换后的RGB写入显存的时间,这种运算量对于嵌入式系统而言简直就是噩梦,如果CPU不支持浮点运算的话还得转化成整型运算,最终的结果是:

牛逼闪闪的摄像头烂成渣渣的视频流

这个问题的解决,可以通过优化以上公式来苟延残喘,比如可以将浮点运算变成整数运算:

R0 = *Y0 + 1042*(*V1-128) / 1000;G0 = *Y0 - (344*(*U0-128) - 714*(*V1-128)) / 1000;B0 = *Y0 + 1772*(*U0-128) / 1000;R1 = *Y1 + 1042*(*V1-128) / 1000;G1 = *Y1 - (344*(*U0-128) - 714*(*V1-128)) / 1000;B1 = *Y1 + 1772*(*U0-128) / 1000;

当然这不够刺激,可以进一步将除法变成右移:

R0 = *Y0 + 4268*(*V1-128) >> 12;G0 = *Y0 - (1409*(*U0-128) - 2924*(*V1-128)) >> 12;B0 = *Y0 + 7258*(*U0-128) >> 12;R1 = *Y1 + 4268*(*V1-128) >> 12;G1 = *Y1 - (1409*(*U0-128) - 2924*(*V1-128)) >> 12;B1 = *Y1 + 7258*(*U0-128) >> 12;

虽然这已经大大提高了运算速度,但公式中依然残留了部分挥之不去的乘法运算,踌躇间,想起一句算法届至圣名言:如果要取得时间,就必须要牺牲空间。时间就是执行效率,空间就是运算内存。这条 IT 公理可简称为算法的时空守恒。

怎么个时空互换?简单讲就是作弊:既然那么难算,我就把答案先算好写纸条里,不仅要写纸条里,为了方便作弊还要画个表格(其实就是数组),一五一十白纸黑字写死,要算某个数的时候直接像查字典那样查就行了。请注意,是要把所有的答案全部算出来,作弊就要做得彻底。 比如这两公式:

R = Y + 1.042*(V-128);B = Y + 1.772*(U-128);

其中Y、U、V是从摄像头获取的图像数值,分别保存在一个字节中,他们的取值无非就是0-255之间,不可能有别的取值,这样一来R、B的取值就从他们中产生,总共256*256中可能。将所有的这些可能统统暴力计算出来保存在一个叫R、B的数组中:

for(int i=0; i<256; i++){for(int j=0; j<256; j++){R[i][j] = i + 1.042 * (j-128);R[i][i] = R[i][j] < 0 ? 0 : R[i][j];R[i][j] = R[i][j] > 255 ? 255 : R[i][j];B[i][j] = i + 1.772 * (j-128);B[i][i] = B[i][j] < 0 ? 0 : B[i][j];B[i][j] = B[i][j] > 255 ? 255 : B[i][j];}}

注意以上代码是在摄像头开启之前就做好了,因此丝毫不需要对浮点公式本身做任何优化,红色部分是保证RGB取值正确。使用完全一样的算法,将具有三个变量的G也暴力计算一下:

for(int i=0; i<256; i++){for(int j=0; j<256; j++){for(int k=0; k<256; k++){G[i][j] = i - 0.344*(j-128) - 0.714*(k-128);G[i][i] = G[i][j] < 0 ? 0 : G[i][j];G[i][j] = G[i][j] > 255 ? 255 : G[i][j];}}}

有了这杨的金光闪闪的数组 R、G、B,进行 YUV 转码 RGB 的时候就易如反掌、快如闪电了:

R = R[*Y][*V];G = G[*Y][*U][*V];B = B[*Y][*U];

简单的例子:比如读取YUV中的原始数据得出:Y0=100,U=200,Y1=30,V=88,代进去数组即可得到R=R[100][88],G=G[100][200][88],B=B[30][200]

代码示例

#include <stdio.h>//YUV 转 RGB 公式:
/*******************************R = Y + 1.042*(V-128);G = Y - 0.344*(U-128)-0.714*(V-128);B = Y + 1.772*(U-128);
*******************************/// 准备RGB数组
int R[256][256];
int G[256][256][256];
int B[256][256];int main(int argc, char **argv)
{// 将RGB的所有可能的取值,都提前算出来(作弊)for(int i=0; i<256; i++){for(int j=0; j<256; j++){R[i][j] = i + 1.042*(j-128);R[i][j] = R[i][j]>255 ? 255 : R[i][j];R[i][j] = R[i][j]<0   ? 0   : R[i][j];B[i][j] = i + 1.772*(j-128);B[i][j] = B[i][j]>255 ? 255 : B[i][j];B[i][j] = B[i][j]<0   ? 0   : B[i][j];for(int k=0; k<256; k++){G[i][j][k] = i - 0.344*(j-128)-0.714*(k-128);G[i][j][k] = G[i][j][k]>255 ? 255 : G[i][j][k];G[i][j][k] = G[i][j][k]<0   ? 0   : G[i][j][k];}}}// 给定一帧YUV图像,及其尺寸char *yuv; // 相当于start[i%nbuf];int height, width;// 这是每次转换前的两个YUV像素: [Y0,U], [Y1 V]uint8_t Y0, U;uint8_t Y1, V;// 每次转换后得到的连个RGB像素: [R0, G0, B0], [R1, G1, B1]int R0, G0, B0;int R1, G1, B1;int yuv_offset;for(int y=0; y<height; y++){for(int x=0; x<width; x+=2) // 每次转换两个像素,四个字节{yuv_offset = ( 640*y + x ) * 2;// 取四个字节Y0 = *(yuv + yuv_offset + 0);U  = *(yuv + yuv_offset + 1);Y1 = *(yuv + yuv_offset + 2);V  = *(yuv + yuv_offset + 3);// 得到六个字节//得到RGB数据就可以妥善放置在LCD上了R0 = R[Y0][V];G0 = G[Y0][U][V];B0 = B[Y0][U];R1 = R[Y1][V];G1 = G[Y1][U][V];B1 = B[Y1][U];}}return 0;
}

摄像头YUV格式极速转码相关推荐

  1. android usb摄像头 抓取一张图片 yuv格式 yuyv(yuy2)

    //#加了点注释   //#Rockie Cheng     #include <stdio.h> #include <stdlib.h> #include <strin ...

  2. 使用v4l2音、视频协议实现USB摄像头的图像、视频YUV格式采集功能(ubuntu16.04LTS)

    第一感觉是首先得了解v4l2协议,它的功能.以及与之对应的实现逻辑,还有与硬件.操作系统的交互等内容.再试着根据功能逻辑和软硬件交互关系,借助硬件设备,实现基础的功能,如查询设备信息.帧类型等.然后, ...

  3. 摄像头YUV图像常见数据格式介绍

    1.YUV模型分类: 是根据一个亮度(Y分量)和两个色度(UV分量)来定义颜色空间,常见的YUV格式描述有YUY2.YUYV.YVYU.UYVY.AYUV.Y41P.Y411.Y211.IF09.IY ...

  4. RGB与YUV格式简介

    (1) RGB格式简介 RGB色彩模式是一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的.图像中每一个像素的RGB分量都分配一个0~25 ...

  5. 使用 8 位 YUV 格式的视频呈现

    发布日期 : 12/9/2004 | 更新日期 : 12/9/2004 Gary Sullivan 和 Stephen Estrop Microsoft Digital Media Division ...

  6. yuv420p 详解_Android中的YUV格式解析

    一.YUV格式 YUV 表示三个分量, Y 表示 亮度(Luminance),即灰度值,UV表示色度(Chrominance),描述图像色彩和饱和度,指定颜色.YUV格式有YUV444. YUV422 ...

  7. yuv格式转换是那个组织定义的_YUV格式

    在前几篇介绍了OpenGL几种2D效果(旋转.平移.缩放.滤镜)后可以看到,GL图像颜色空间是用R.G.B.A,也就是红.绿.蓝 加一个透明度通道来表示的.比如,gl_FragColor 通常在取值的 ...

  8. 关于视频的YUV格式介绍

    本文转载于图文详解YUV420数据格式 YUV格式有两大类:planar和packed. 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V. 对于 ...

  9. V4L2编程代码实现以及YUV格式(V4L2二)

    目录 一.V4L2编程代码实现 1.头文件 2.打开设备 3.获取设备支持格式 4.设置设备采集格式VIDIOC_S_FMT 5.分配内核队列空间(申请内核缓冲区)VIDIOC_REQBUFS 6.映 ...

最新文章

  1. java oracle in 10000_java支持ORACLE的in不能超过1000
  2. 如果有一天 Pytorch / Tensorflow 不开源了,我们该怎么办?
  3. 无法定位软件包_使用Degraph管理软件包依赖关系
  4. OpenResty 通过二级域名做跳转
  5. 稀疏数组与原始数组之间的转换
  6. python pprint_如何美观地打印 Python 对象?这个标准库可以简单实现
  7. java 方式配置ssm,关于SSM以及Spring boot中对于Spring MVC配置的问题
  8. 现实世界的Windows Azure:采访InishTech的销售及市场部主管Andrew O’Connor
  9. 面试官系统精讲Java源码及大厂真题 - 24 举一反三:队列在 Java 其它源码中的应用
  10. 云、AI、5G技术融合,会将移动互联网带到什么新高度?
  11. //变量在scala中没有自增自减的操作
  12. android wear系统源码,android wear5.1怎么样 android wear5.1更新评测
  13. linux dhcp服务器配置及小实验
  14. 【学习】自定义view
  15. Java操作Excel表读取的数字变成科学计数法
  16. linux系统Nginx下limit_req模块burst参数超详细解析
  17. Windows桌面任务栏应用图标变白怎么办?
  18. java ios push_java向IOS设备推送消息
  19. 小功能⭐️Untiy组合键检测
  20. 2021-2027全球及中国M2M应用开发平台行业研究及十四五规划分析报告

热门文章

  1. python地图坐标系转换(bd09,gcj02,wgs84三种投影坐标系相互转化)
  2. 吉林大学超星MOOC学习通高级语言程序设计 C++ 实验02 分支与循环程序设计(2021级)(2)
  3. python计算球坐标系的积分_Python实现将n个点均匀地分布在球面上的方法
  4. 赫美收购再发力 打造线上线下全产业链战略布局
  5. 解决谷歌浏览器安装包双击没有反应
  6. Intellij idea使用Statistic统计代码行数的方法
  7. 【在线网课】Java高性能高并发秒杀系统方案优化实战
  8. 汉字的内码和区位码与显示汉字原理
  9. Titanic - (XGBoost,RF随机森林,Fastai-tabular_learner)总结
  10. 2.FFmpeg5.1下载和使用