https://blog.csdn.net/dangxw_/article/details/50903693
移动端录像在yuv数据上存在如下问题:
1.无论android还是ios都不能直接从摄像头取出颜色空间为i420的数据,所以在编码前需要进行格式转换。
2.而且由于所取图像得分辨率必须是摄像头所提供分辨率中得一组,所以有可能需要裁剪。
3.另外由于1)想让无论用户哪个方向拿手机所录的视频内容永远“头朝上”,
2)摄像头默认返回图像为横屏图像(宽大于长)所以需要旋转。
4.前置摄像头需要镜像。
YUV 颜色空间分类:https://zh.wikipedia.org/wiki/YUV
yuv 420 又分为:
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
下面给出解决这四个问题所需要得算法:
1 格式转换:
nv21 转成i420
-
- void NV21ToI420(uint8_t* dstyuv,uint8_t* data, int imageWidth, int imageHeight)
- {
- int Ustart =imageWidth*imageHeight;
- int i,j;
- int uWidth = imageWidth/2;
- int uHeight = imageWidth/2;
-
- memcpy(dstyuv,data,imageWidth*imageHeight);
- int tempindex = 0 ;
- int srcindex= 0;
-
- for(i= 0 ;i
- {
-
-
- for(j = 0;j
- {
- dstyuv[Ustart&#43;tempindex&#43;j]&#61; data[Ustart&#43;(srcindex<<1)&#43;1];
- srcindex&#43;&#43;;
- }
- tempindex&#43;&#61; uWidth;
- }
-
-
-
- for (i &#61; 0; i < uHeight;i&#43;&#43;)
- {
-
- for (j &#61; 0; j < uWidth;j&#43;&#43;)
- {
- dstyuv[Ustart&#43;tempindex &#43; j] &#61; data[Ustart &#43; (srcindex << 1 )];
- srcindex&#43;&#43;;
- }
- tempindex&#43;&#61; uWidth;
- }
- }
其实就是改变了uv的位置。
2 裁剪&#xff1a;
-
- int crop_yuv (char* data, char*dst, intwidth, intheight,
- int goalwidth, int goalheight) {
-
- int i, j;
- int h_div &#61; 0, w_div &#61; 0;
- w_div&#61; (width - goalwidth) / 2;
- if (w_div % 2)
- w_div--;
- h_div&#61; (height - goalheight) / 2;
- if (h_div % 2)
- h_div--;
-
- int src_y_length &#61; width *height;
- int dst_y_length &#61;goalwidth * goalheight;
- for (i &#61; 0; i
- for (j &#61; 0; j
- dst[i* goalwidth &#43; j] &#61; data[(i &#43; h_div) * width &#43; j &#43; w_div];
- }
- int index &#61; dst_y_length;
- int src_begin &#61;src_y_length &#43; h_div * width / 4;
- int src_u_length &#61;src_y_length / 4;
- int dst_u_length &#61;dst_y_length / 4;
- for (i &#61; 0; i
- for (j &#61; 0; j
- int p &#61; src_begin &#43; i *(width >> 1) &#43; (w_div >> 1) &#43; j;
- dst[index]&#61; data[p];
- dst[dst_u_length&#43; index&#43;&#43;] &#61; data[p &#43; src_u_length];
- }
-
- return 0;
- }
3 旋转&#xff1a;
分为四个方向
旋转&#xff1a;
以顺时针旋转270度为例作图&#xff1a;
Y1 | Y2 | Y3 | Y4 |
Y5 | Y6 | Y7 | Y8 |
Y9 | Y10 | Y11 | Y12 |
Y13 | Y14 | Y15 | Y16 |
U1 | U2 | U3 | U4 |
V1 | V2 | V3 | V4 |
原图
Y4 | Y8 | Y12 | Y16 |
Y3 | Y7 | Y11 | Y15 |
Y2 | Y6 | Y10 | Y14 |
Y1 | Y5 | Y9 | Y13 |
U2 | U4 | U1 | U3 |
V2 | V4 | V1 | V3 |
旋转后
u值的第i 行j列 对应原 数据的下标为&#xff1a; ustart&#43;uw*j-i;
去除index的乘除法运算后&#xff1a;
//i420 顺时针 270度
- int rotateYUV420Degree270(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) {
-
- int i &#61; 0, j &#61; 0;
-
- int index &#61; 0;
- int tempindex &#61; 0;
- int div &#61; 0;
- for (i &#61; 0; i
- div&#61; i &#43;1;
- tempindex&#61; 0;
- for (j &#61; 0; j
-
- tempindex&#43;&#61; imageWidth;
- dstyuv[index&#43;&#43;]&#61; srcdata[tempindex-div];
- }
- }
-
- int start &#61;imageWidth*imageHeight;
- int udiv &#61; imageWidth *imageHeight / 4;
-
- int uWidth &#61; imageWidth /2;
- int uHeight &#61; imageHeight /2;
- index&#61; start;
- for (i &#61; 0; i < uHeight;i&#43;&#43;) {
- div&#61; i &#43;1;
- tempindex&#61; start;
- for (j &#61; 0; j < uWidth;j&#43;&#43;) {
- tempindex &#43;&#61; uWidth;
- dstyuv[index]&#61; srcdata[tempindex-div];
- dstyuv[index&#43;udiv]&#61; srcdata[tempindex-div&#43;udiv];
- index&#43;&#43;;
- }
- }
-
- return 0;
-
- }
//i420 顺时针旋转 180&#xff1b;
- int rotateYUV420Degree180(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight)
- {
-
- int i &#61; 0, j &#61; 0;
-
- int index &#61; 0;
- int tempindex &#61; 0;
-
- int ustart &#61; imageWidth *imageHeight;
- tempindex&#61; ustart;
- for (i &#61; 0; i
-
- tempindex-&#61; imageWidth;
- for (j &#61; 0; j
-
- dstyuv[index&#43;&#43;] &#61; srcdata[tempindex &#43; j];
- }
- }
-
- int udiv &#61; imageWidth *imageHeight / 4;
-
- int uWidth &#61; imageWidth /2;
- int uHeight &#61; imageHeight /2;
- index&#61; ustart;
- tempindex&#61; ustart&#43;udiv;
- for (i &#61; 0; i < uHeight;i&#43;&#43;) {
-
- tempindex-&#61; uWidth;
- for (j &#61; 0; j < uWidth;j&#43;&#43;) {
-
- dstyuv[index]&#61; srcdata[tempindex &#43; j];
- dstyuv[index&#43; udiv] &#61; srcdata[tempindex &#43; j &#43; udiv];
- index&#43;&#43;;
- }
- }
- return 0;
- }
顺时针 90度&#xff1a;
-
- int rotateYUV420Degree90(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) {
-
- int i &#61; 0, j &#61; 0;
-
- int index &#61; 0;
- int tempindex &#61; 0;
- int div &#61; 0;
- int ustart &#61; imageWidth *imageHeight;
- for (i &#61; 0; i
- div&#61; i;
- tempindex&#61; ustart;
- for (j &#61; 0; j
-
- tempindex-&#61; imageWidth;
-
- dstyuv[index&#43;&#43;]&#61; srcdata[tempindex &#43; div];
- }
- }
-
-
- int udiv &#61; imageWidth *imageHeight / 4;
-
- int uWidth &#61; imageWidth /2;
- int uHeight &#61; imageHeight /2;
- index&#61; ustart;
- for (i &#61; 0; i < uHeight;i&#43;&#43;) {
- div&#61; i ;
- tempindex&#61; ustart&#43;udiv;
- for (j &#61; 0; j < uWidth;j&#43;&#43;) {
- tempindex-&#61; uWidth;
- dstyuv[index]&#61; srcdata[tempindex &#43; div];
- dstyuv[index&#43; udiv] &#61; srcdata[tempindex &#43; div &#43; udiv];
- index&#43;&#43;;
- }
- }
- return 0;
-
- }
如果从摄像头取出数据&#xff0c;这样一步步的历遍&#xff0c;在低配手机上是满足不了需求的。其实这三个步骤中有很多中间步骤是可以省去的&#xff0c;比如&#xff1a;将a放到b 位置&#xff0c;再将b位置上的数据取出放到c位置&#xff0c;那么可以直接将a放到c位置。
所以需要优化以上三类问题所用的算法将其整合。结果如下&#xff1a;
- void detailPic0(uint8_t* d, uint8_t* yuv_temp, int nw, int nh, int w, int h) {
- int deleteW &#61; (nw - w) / 2;
- int deleteH &#61; (nh - h) / 2;
-
- int i, j;
- int index &#61; 0;
- for (j &#61; deleteH; j < nh- deleteH; j&#43;&#43;) {
- for (i &#61; deleteW; i < nw- deleteW; i&#43;&#43;)
- yuv_temp[index&#43;&#43;]&#61; d[j * nw &#43; i];
- }
-
-
- index&#61; w * h;
-
- for (i &#61; nh &#43; deleteH / 2;i < nh / 2 * 3 - deleteH / 2; i&#43;&#43;)
- for (j &#61; deleteW &#43; 1; j< nw - deleteW; j &#43;&#61; 2)
- yuv_temp[index&#43;&#43;]&#61; d[i * nw &#43; j];
-
-
- for (i &#61; nh &#43; deleteH / 2;i < nh / 2 * 3 - deleteH / 2; i&#43;&#43;)
- for (j &#61; deleteW; j < nw- deleteW; j &#43;&#61; 2)
- yuv_temp[index&#43;&#43;]&#61; d[i * nw &#43; j];
-
- }
-
- void detailPic180(uint8_t* d, uint8_t* yuv_temp, int nw, int nh, int w, int h) {
- int deleteW &#61; (nw - w) / 2;
- int deleteH &#61; (nh - h) / 2;
-
- int i, j;
- int index &#61; w * h;
- for (j &#61; deleteH; j < nh- deleteH; j&#43;&#43;) {
- for (i &#61; deleteW; i < nw- deleteW; i&#43;&#43;)
- yuv_temp[--index]&#61; d[j * nw &#43; i];
- }
-
-
- index&#61; w * h * 5 / 4;
-
- for (i &#61; nh &#43; deleteH / 2;i < nh / 2 * 3 - deleteH / 2; i&#43;&#43;)
- for (j &#61; deleteW &#43; 1; j< nw - deleteW; j &#43;&#61; 2)
- yuv_temp[--index]&#61; d[i * nw &#43; j];
-
-
- index&#61; w * h * 3 / 2;
- for (i &#61; nh &#43; deleteH / 2;i < nh / 2 * 3 - deleteH / 2; i&#43;&#43;)
- for (j &#61; deleteW; j < nw- deleteW; j &#43;&#61; 2)
- yuv_temp[--index]&#61; d[i * nw &#43; j];
-
- }
- void detailPic90(uint8_t* d, uint8_t* yuv_temp, int nw, int nh, int w, int h) {
-
-
-
-
-
- int deleteW &#61; (nw - h) / 2;
- int deleteH &#61; (nh - w) / 2;
-
- int i, j;
- }*/
- for (i &#61; 0; i < h; i&#43;&#43;){
- for (j &#61; 0; j < w; j&#43;&#43;){
- yuv_temp[(h- i) * w - 1 - j] &#61; d[nw * (deleteH &#43; j) &#43; nw - deleteW
- -i];
- }
- }
-
- int index &#61; w * h;
- for (i &#61; deleteW &#43; 1; i< nw - deleteW; i &#43;&#61; 2)
- for (j &#61; nh / 2 * 3 -deleteH / 2; j > nh &#43; deleteH / 2; j--)
- yuv_temp[index&#43;&#43;]&#61; d[(j - 1) * nw &#43; i];
-
- for (i &#61; deleteW; i < nw- deleteW; i &#43;&#61; 2)
- for (j &#61; nh / 2 * 3 -deleteH / 2; j > nh &#43; deleteH / 2; j--)
- yuv_temp[index&#43;&#43;]&#61; d[(j - 1) * nw &#43; i];
-
- }
-
- void detailPic270(uint8_t* d, uint8_t* yuv_temp, int nw, int nh, int w, int h) {
- int deleteW &#61; (nw - h) / 2;
- int deleteH &#61; (nh - w) / 2;
- int i, j;
-
- for (i &#61; 0; i < h; i&#43;&#43;){
- for (j &#61; 0; j < w; j&#43;&#43;){
- yuv_temp[i* w &#43; j] &#61; d[nw * (deleteH &#43; j) &#43; nw - deleteW - i];
- }
- }
-
-
- int index &#61; w * h;
- for (i &#61; nw - deleteW - 1;i > deleteW; i -&#61; 2)
- for (j &#61; nh &#43; deleteH / 2;j < nh / 2 * 3 - deleteH / 2; j&#43;&#43;)
- yuv_temp[index&#43;&#43;]&#61; d[(j) * nw &#43; i];
-
-
-
- for (i &#61; nw - deleteW - 2;i >&#61; deleteW; i -&#61; 2)
- for (j &#61; nh &#43; deleteH / 2;j < nh / 2 * 3 - deleteH / 2; j&#43;&#43;)
- yuv_temp[index&#43;&#43;]&#61; d[(j) * nw &#43; i];
-
- }
-
注&#xff1a;没有优化&#xff0c;消除index的乘法后效果肯定会更好。
4 镜像&#xff1a;
-
- void Mirror(uint8_t* yuv_temp, int nw, int nh, int w,
- int h) {
- int deleteW &#61; (nw - h) / 2;
- int deleteH &#61; (nh - w) / 2;
- int i, j;
-
- int a, b;
- uint8_ttemp;
-
- for (i &#61; 0; i < h; i&#43;&#43;){
- a&#61; i * w;
- b&#61; (i &#43; 1) * w - 1;
- while (a < b) {
- temp&#61; yuv_temp[a];
- yuv_temp[a]&#61; yuv_temp[b];
- yuv_temp[b]&#61; temp;
- a&#43;&#43;;
- b--;
- }
- }
-
- int uindex &#61; w * h;
- for (i &#61; 0; i < h / 2;i&#43;&#43;) {
- a &#61; i * w / 2;
- b&#61; (i &#43; 1) * w / 2 - 1;
- while (a < b) {
- temp&#61; yuv_temp[a &#43; uindex];
- yuv_temp[a&#43; uindex] &#61; yuv_temp[b &#43; uindex];
- yuv_temp[b&#43; uindex] &#61; temp;
- a&#43;&#43;;
- b--;
- }
- }
-
- uindex&#61; w * h / 4 * 5;
- for (i &#61; 0; i < h / 2;i&#43;&#43;) {
- a&#61; i * w / 2;
- b&#61; (i &#43; 1) * w / 2 - 1;
- while (a < b) {
- temp&#61; yuv_temp[a &#43; uindex];
- yuv_temp[a&#43; uindex] &#61; yuv_temp[b &#43; uindex];
- yuv_temp[b&#43; uindex] &#61; temp;
- a&#43;&#43;;
- b--;
- }
- }
-
- }
由于当初忽略了镜像&#xff0c;所以并没有把镜像也和其他三个算法和并到一起。不过测试还是通过的。
如果集成ffmpeg或者opencv&#xff0c;可以使用ffmpeg的sws&#xff0c;filter 或者opencv的cvtcolor都可以轻松实现部分功能&#xff0c;不过跟踪过ffmpeg sws的源码&#xff0c;发现效率较低&#xff0c;代码实现较差。如果追求处理效率&#xff0c;并且以上优化的算法仍不能满足&#xff0c;建议使用libyuv&#xff0c;其中试用了很多汇编指令加速&#xff0c;效率惊人。