// RGB、YUV互换
int rgbToY(int r, int g, int b) {
return (((66 * r + 129 * g + 25 * b + 128) >> 8) + 16);
}
int rgbToU(int r, int g, int b) {
return (((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128);
}
int rgbToV(int r, int g, int b) {
return (((112 * r - 94 * g - 18 * b + 128) >> 8) + 128);
}
int yuvToR(int y, int u, int v) {
return (int) ((y & 0xFF) + 1.4075 * ((v & 0xFF) - 128));
}
int yuvToG(int y, int u, int v) {
return (int) ((y & 0xFF) - 0.3455 * ((u & 0xFF) - 128) - 0.7169 * ((v & 0xFF) - 128));
}
int yuvToB(int y, int u, int v) {
return (int) ((y & 0xFF) + 1.779 * ((u & 0xFF) - 128));
}
int alignIntToByte(int c) {
return c & 0xFF;
}
举个例子,对于4x2的图片,RGBA32
格式内容为:
R1 G1 B1 A1 R2 G2 B2 A2 R3 G3 B3 A3 R4 G4 B4 A4
R5 G5 B5 A5 R6 G6 B6 A6 R7 G7 B7 A7 R8 G8 B8 A8
那么若需要转化为BGR24
,内容将变成:
B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4
B5 G5 R5 B6 G6 R6 B7 G7 R7 B8 G8 R8
BGR24
内容为3个byte
一组,RGBA32
内容为4个byte
一组。因此,对于第一组RGBA32(R1 G1 B1 A1)
和第一组BGR24(B1 G1 R1)
,其对应关系为:
bgr24[0] = Rgba32[2];
bgr24[1] = Rgba32[1];
bgr24[2] = Rgba32[0];
对应的转换代码:
void rgba32ToBgr24(char *rgba32, char *bgr24, int width, int height) {
int groupNum = width * height;
int bgr24Index = 0;
int rgba32Index = 0;
for (int i = 0; i < groupNum; i++) {
*(bgr24 + bgr24Index) = *(rgba32 + rgba32Index + 2);
*(bgr24 + bgr24Index + 1) = *(rgba32 + rgba32Index + 1);
*(bgr24 + bgr24Index + 2) = *(rgba32 + rgba32Index);
bgr24Index += 3;
rgba32Index += 4;
}
}
BGR24
的每一组 B G R
数据对应了一个像素点,而NV21
是一种YUV
数据,YUV
数据的每个Y
会和对应的U
和V
组成一个像素点,NV21
的共用关系是每4个Y(例如下标为:0
,1
,width
,width+1
的四个Y
)会共用一组U
和V
(对应下标:U
:width x height + 1
,V
:width x height
)。所以对于Y
而言,需要计算每一组BGR
对应的Y
值;但是对于NV21
的U
和V
而言,U
和V
的大小都是各只有Y
的一半,所以我们可以在宽度下标和高度下标都为偶数时计算一组U
和V
。
void bgr24ToNv21(char *bgr24, char *nv21, int width, int height) {
int yIndex = 0;
int uvIndex = width * height;
int bgrIndex = 0;
int nv21Length = width * height * 3 / 2;
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
int b = bgr24[bgrIndex++];
int g = bgr24[bgrIndex++];
int r = bgr24[bgrIndex++];
b = alignIntToByte(b);
g = alignIntToByte(g);
r = alignIntToByte®;
int y = rgbToY(r, g, b);
nv21[yIndex++] = static_cast(alignIntToByte(y));
if ((j & 1) == 0 && ((bgrIndex >> 2) & 1) == 0 && uvIndex < nv21Length - 2) {
int u = rgbToU(r, g, b);
int v = rgbToV(r, g, b);
nv21[uvIndex++] = static_cast(alignIntToByte(v));
nv21[uvIndex++] = static_cast(alignIntToByte(u));
}
}
}
}
这是上一个介绍的逆过程,在BGR24
转换为NV21
时,宽度下标和高度下标都为偶数才进行计算U
和V
;那么若要将NV21
转换为BGR24
,则我们需要对每组U
和V
进行一次复用。
void nv21ToBgr24(char *nv21, char *bgr24, int width, int height) {
int bgrLineSize = width * 3;
//偶数行的bgr数据下标
int evenLineBgrIndex = 0;
//奇数行的bgr数据下标
int oddLineBgrIndex = bgrLineSize;
//当前一行y数据最左边的下标
int yLineStart = 0;
//uv数据的下标
int uvIndex = width * height;
//由于NV21的共用关系,每2行做一次转换
for (int i = 0; i < height; i += 2) {
for (int widthOffset = 0; widthOffset < width; widthOffset++) {
char v = nv21[uvIndex];
char u = nv21[uvIndex + 1];
char yEven = nv21[yLineStart + widthOffset];
char yOdd = nv21[yLineStart + width + widthOffset];
int r, g, b;
//偶数行YUV转RGB
r = alignIntToByte(yuvToR(yEven, u, v));
g = alignIntToByte(yuvToG(yEven, u, v));
b = alignIntToByte(yuvToB(yEven, u, v));
bgr24[evenLineBgrIndex++] = (char) b;
bgr24[evenLineBgrIndex++] = (char) g;
bgr24[evenLineBgrIndex++] = (char) r;
//奇数行YUV转RGB
r = alignIntToByte(yuvToR(yOdd, u, v));
g = alignIntToByte(yuvToG(yOdd, u, v));
b = alignIntToByte(yuvToB(yOdd, u, v));
bgr24[oddLineBgrIndex++] = (char) b;
bgr24[oddLineBgrIndex++] = (char) g;
bgr24[oddLineBgrIndex++] = (char) r;
//每两个y将uv下标增1
if ((widthOffset & 1) == 1) {
uvIndex += 2;
}
}
//由于在内层循环中已经做过width * 3次自增,所以外层循环中只需要增加一行
evenLineBgrIndex += bgrLineSize;
oddLineBgrIndex += bgrLineSize;
//y增2行
yLineStart += (width << 1);
}
}
NV21
和NV12
只是U与V的数据位置不同,只需要替换U和V的位置即可。因此,将NV21
转换为NV12
的代码同样适用于NV12
转换为NV21
。可参考如下代码:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
总结
最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
上述【高清技术脑图】以及【配套的架构技术PDF】可以关注我免费获取
是环境来适应我们!**
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
[外链图片转存中…(img-syuzKVjV-1711181744328)]
[外链图片转存中…(img-DfjP1ZLf-1711181744329)]
上述【高清技术脑图】以及【配套的架构技术PDF】可以关注我免费获取
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。