前言

使用zmm220核心板,IFACE102版本的内核等,4300型号的LCD,XC7011_SC1145摄像头,亲测有效。

本文章使用Markdown写法。

源码

需要注意的是,这份代码适用于UYVYUYVYUYVYRGB565,其它类型的转换,看完后面的讲解后自然会举一反三。可以设置一个参数,用于选择数据类型,函数体中用switch case。这里没进行这个处理。

/*
* YUV422打包数据,UYVY,转换为RGB565,
* inBuf -- YUV data
* outBuf -- RGB565 data
* imgWidth,imgHeight -- image width and height
* cvtMethod -- 无效参数
*/
int convert_uyvy_to_rgb(unsigned char *inBuf, unsigned char *outBuf, int imgWidth, int imgHeight, int cvtMethod)
{
int rows ,cols; /* 行列标志 */
int y, u, v, r, g, b; /* yuv rgb 相关分量 */
unsigned char *YUVdata, *RGBdata; /* YUV和RGB数据指针 */
int Ypos, Upos, Vpos; /* Y U V在数据缓存中的偏移 */
unsigned int i = 0; YUVdata = inBuf;
RGBdata = outBuf;
#if 0
/* YUYV */
Ypos = 0;
Upos = Ypos + 1;
Vpos = Upos + 2; /* YVYU */
Ypos = 0;
Vpos = Ypos + 1;
Upos = Vpos + 2;
#endif #if 1 /* UYVY */
Ypos = 1;
Upos = Ypos - 1;
Vpos = Ypos + 1;
#endif /* 每个像素两个字节 */
for(rows = 0; rows < imgHeight; rows++)
{
for(cols = 0; cols < imgWidth; cols++)
{
/* 矩阵推到,百度 */
y = YUVdata[Ypos];
u = YUVdata[Upos] - 128;
v = YUVdata[Vpos] - 128; r = y + v + ((v * 103) >> 8);
g = y - ((u * 88) >> 8) - ((v * 183) >> 8);
b = y + u + ((u * 198) >> 8); r = r > 255?255:(r < 0?0:r);
g = g > 255?255:(g < 0?0:g);
b = b > 255?255:(b < 0?0:b); /* 从低到高r g b */
*(RGBdata ++) = (((g & 0x1c) << 3) | (b >> 3)); /* g低5位,b高5位 */
*(RGBdata ++) = ((r & 0xf8) | (g >> 5)); /* r高5位,g高3位 */ /* 两个字节数据中包含一个Y */
Ypos += 2;
//Ypos++;
i++;
/* 每两个Y更新一次UV */
if(!(i & 0x01))
{
Upos = Ypos - 1;
Vpos = Ypos + 1;
} }
} return 0;
}

这里面有几个关键点,一开始写错了,最好的办法是在纸上画出来,第二次重写的时候,在纸上画出来,编译运行一次性通过。光凭想象非常容易出纰漏或者进入思路误区。

代码分析

YUV三个分量的关系

首先你要确定ISP输出给CPU的控制器接口cim的数据排列方式,也就是上面传入的参数inBuf中Y、U、V三分量的存储方式。如:YUYV、YVYU、UYVY、VYUY等。特别要注意当配置寄存器以便输出黑白图的时候,要确定ISP输出的是YUV400,也就是UV也占字节,只不过都是0,如XC7011_SC1145;还是只单独输出Y,如:gc0308.

这个可以通过工具验证,建议使用海康的YUVplayer,比pYUV好用很多,也准确许多。

这里分析UYVY的存储方式,见下表:

YUV三分量各占一个字节,每两个Y共享一对UV,所以每个像素两个字节,在代码中的表现就是,Y递增两次才刷新一次UV,注意这里Y的递增是2,和网上的版本不同

据此可以很清晰的看出,对于UYVY来说,Y、U、V三个分量的关系如下:

Y=i和Y=i+2时
U=i-1;
V=i+1;

循环遍历

这个很容易搞错,特别是在做图像的裁剪时,一不注意就会数组越界导致段错误。

处理YUV422、RGB888等数据有个很实用的规律:对于遍历图像缓存数据的操作,特别是一个像素点对应多个字节的时候,for循环的遍历参数i、j等参考像素点的宽和高递增;而在循环体中涉及到图像缓存数据的操作,一律按照字节数来操作。RGB565数据处理也可以参考这个规律,只不过增加了数据的裁剪增补而已。

比如分析上面的例子:

图像的像素点宽高是imgHeight、imgWidth,所以for循环的写法就是:

for(rows = 0; rows < imgHeight; rows++)
{
for(cols = 0; cols < imgWidth; cols++)
{
}
}

循环的参考条件是像素点的宽和高,这样能保证大方向不会出错——遍历每个像素点,不会漏数据(这是对每次递增1来说的,还要看循环体的具体操作)。

如果每个像素点都要操作,且一个像素点对应两个字节,那么在循环体中你一次就要处理两个字节的数据,这能明白吧?但是针对UYVY这种YUV数据来说,参见上表,一般理解为每4个字节对应两个像素,因为这样理解包含了YUV数据的特性,不会出现Y占一个字节,UV各占半个字节的错觉。所以上面代码的写法思路就是:保证最里层的循环体执行两次——遍历两个像素点——处理4个字节数据——更新两次Y——更新一次UV,这点很关键,否则循环体代码越写越乱,分析如下:

/* 矩阵推到,百度 */
y = YUVdata[Ypos];
u = YUVdata[Upos] - 128;
v = YUVdata[Vpos] - 128; r = y + v + ((v * 103) >> 8);
g = y - ((u * 88) >> 8) - ((v * 183) >> 8);
b = y + u + ((u * 198) >> 8); r = r > 255?255:(r < 0?0:r);
g = g > 255?255:(g < 0?0:g);
b = b > 255?255:(b < 0?0:b); /* 从低到高r g b */
*(RGBdata ++) = (((g & 0x1c) << 3) | (b >> 3)); /* g低5位,b高5位 */
*(RGBdata ++) = ((r & 0xf8) | (g >> 5)); /* r高5位,g高3位 */

这部分代码是网上提供的YUV和RGB的转换公式,实测证明有效,但是要注意几点

  • 括号不能少

  • 要做数值边界判断和处理

  • RGB的存储方式

    yuv分量转换的来的rgb分量,都是各占一个字节,实际的RGB中,R、B各占5bits,G占6bits,总共是16bits两个字节。在这两个字节中,低字节存放g的低3位(8bits中高6bits中的低3bits)和b的全部(8bits中的高5位),高字节存放r的全部(8bits中的高5位)和g的高3位(8bits中高6bits中的高3bits)

/* 两个字节数据中包含一个Y */
Ypos += 2;
//Ypos++;
i++;
/* 每两个Y更新一次UV */
if(!(i & 0x01))
{
Upos = Ypos - 1;
Vpos = Ypos + 1;
}

这个应该很好懂了,j++两次,循环体执行两次,Y更新了两次,UV更新了一次。

结束语

明白了这种写法的原理,你就可以举一反三了,比如:每次处理两个像素、数据按照YUYV排列等,找一个适合自己的写法,彻底搞懂就不会再卡壳了。

(完-共勉)

YUV422(UYVY)转RGB565源代码及其讲解.md的更多相关文章

  1. 格式转换至yuv422转 yuv420

    //pYUV为422,yuv为420 /*ok! * brief:pyuv is yuv422sp srcIn, and yuv is yuv420p desOut  */ int YUV422To4 ...

  2. Excel催化剂开源第1波-自定义函数的源代码全公开

    Excel催化剂插件从2018年1月1日开始运营,到今天刚好一周年,在过去一年时间里,感谢社区里的许多友人们的关心和鼓励,得以坚持下来,并收获一定的用户量和粉丝数和少量的经济收入回报和个人知名度的提升 ...

  3. 唐巧的iOS技术博客选摘

    1. 那些被遗漏的objective-c保留字:http://blog.devtang.com/blog/2013/04/29/the-missing-objc-keywords/   2. 使用cr ...

  4. 从零开始写一个武侠冒险游戏-8-用GPU提升性能(3)

    从零开始写一个武侠冒险游戏-8-用GPU提升性能(3) ----解决因绘制雷达图导致的帧速下降问题 作者:FreeBlues 修订记录 2016.06.23 初稿完成. 2016.08.07 增加对 ...

  5. 从零开始写一个武侠冒险游戏-7-用GPU提升性能(2)

    从零开始写一个武侠冒险游戏-7-用GPU提升性能(2) ----把地图处理放在GPU上 作者:FreeBlues 修订记录 2016.06.21 初稿完成. 2016.08.06 增加对 XCode ...

  6. 从零开始写一个武侠冒险游戏-6-用GPU提升性能(1)

    从零开始写一个武侠冒险游戏-6-用GPU提升性能(1) ----把帧动画的实现放在GPU上 作者:FreeBlues 修订记录 2016.06.19 初稿完成. 2016.08.05 增加对 XCod ...

  7. Golang源码探索(三) GC的实现原理

    Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短. 停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服 ...

  8. Golang源码探索(三) GC的实现原理(转)

    Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短.停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服务 ...

  9. 从零开始写一个武侠冒险游戏-0-开发框架Codea简介

    从零开始写一个武侠冒险游戏-0-开发框架Codea简介 作者:FreeBlues 修订记录 2016.06.21 初稿完成. 2016.08.03 增加对 XCode 项目文件的说明. 概述 本游戏全 ...

随机推荐

  1. url基础知识

    浏览器通过url访问服务器步骤 ①浏览器解析出url中的服务器名称 ②浏览器将服务器名称解析成ip(DNS解析) ③浏览器解析出url中的服务器端口(如果有端口的话) ④浏览器建立和web服务器的TC ...

  2. TJOI2013数字根

    题面链接 洛谷 sol 我们先不考虑\(0\),发现数字根\(=\)它\(mod 9\). 我们前缀和一波,把区间和变成两数相减. 对于每个\(v\in\{0-8\}\),(这里面的\(mod 9=0 ...

  3. 【BZOJ3534】重建(矩阵树定理)

    [BZOJ3534]重建(矩阵树定理) 题面 BZOJ 洛谷 题解 这.... 矩阵树定理神仙用法???? #include<iostream> #include<cmath> ...

  4. 【hdu3555】 Bomb

    http://acm.hdu.edu.cn/showproblem.php?pid=3555 (题目链接) 题意 求区间${[1,n]}$含有49的数的个数. Solution 数位dp,先求出不含4 ...

  5. 单点登录(十八)----cas4.2.x客户端增加权限控制shiro

    我们在上面章节已经完成了cas4.2.x登录启用mongodb的验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也完成了获取管理员身份属性 ...

  6. 2018.10.20 2018-2019 ICPC,NEERC,Southern Subregional Contest(Online Mirror, ACM-ICPC Rules)

    i207M的“怕不是一个小时就要弃疗的flag”并没有生效,这次居然写到了最后,好评=.= 然而可能是退役前和i207M的最后一场比赛了TAT 不过打得真的好爽啊QAQ 最终结果: 看见那几个罚时没, ...

  7. 【纪中集训2019.3.23】Deadline

    题意 描述 一个二分图\((A,B)\),每个点额外有一个颜色0或者1: 匹配时,只能相同颜色的点匹配: 给出\(A\)中的颜色,问如何分配\(B\)种的颜色使得\((A,B)\)的最大匹配最小: 范 ...

  8. 【bzoj2754】【scoi2012】喵星球上的点名

    题解们: 1.首先可以被很多暴力给搞过去:我以前也是这样水过去的 2.ac自动机 2.1 抽离fail树 对点名建自动机,建$fail$树的时候只保留询问节点: 对于一个喵,子串==在自动机里匹配到的 ...

  9. JAVA中properties基本用法

    转载 源地址不详 java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式, ...

  10. 题解【bzoj1503 [NOI2004]郁闷的出纳员】

    Description 给出一个下限 \(m\) ,要求维护以下操作 插入一个数(如果小于下限就不加) 给每个数加上一个数 给每个数减去一个数,并且删除掉 \(< m\) 的所有数 求目前第 \ ...