移动端摄像头yuv旋转、裁剪、镜像、格式转换算法的实现及其原因

存在问题

移动端录像在yuv数据上存在如下问题:
1. 无论android还是ios都不能直接从摄像头取出颜色空间为i420的数据,所以在编码前需要进行格式转换。
2. 而且由于所取图像得分辨率必须是摄像头所提供分辨率中得一组,所以有可能需要裁剪。
3. 另外由于
  (1)想让无论用户哪个方向拿手机所录的视频内容永远“头朝上”
  ( 2)摄像头默认返回图像为横屏图像(宽大于长)
  所以需要旋转。
4. 前置摄像头需要镜像。

算法实现

1.格式转换
nv21转成i420。可以通过摄像头设置将所采集数据设置为YUVNV21格式。
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;
    //y
    memcpy(dstyuv,data,imageWidth*imageHeight);
    int tempindex = 0 ;
    int srcindex= 0;
    //u
    for(i= 0 ;i <uHeight;i++)
    {
      for(j = 0;j <uWidth ;j++ )
      {
      dstyuv[Ustart+tempindex+j]= data[Ustart+(srcindex<<1)+1];
      srcindex++;
     }
    tempindex+= uWidth;
    }
    //v
    for (i = 0; i < uHeight;i++)
    {
     for (j = 0; j < uWidth;j++)
     {
      dstyuv[Ustart+tempindex + j] = data[Ustart + (srcindex << 1 )];
      srcindex++;
     }
     tempindex+= uWidth;
    }
}

其实就是改变了uv的位置。
I420: YYYYYYYY UU VV
NV21: YYYYYYYY VUVU

2.裁剪
//crop yuv data
int crop_yuv (char* data, char*dst, intwidth, intheight,
        int goalwidth, int goalheight) {
    int i, j;
    int h_div = 0, w_div = 0;
    w_div= (width - goalwidth) / 2;
    if (w_div % 2)
        w_div--;
    h_div= (height - goalheight) / 2;
    if (h_div % 2)
        h_div--;
    //u_div = (height-goalheight)/4;
    int src_y_length = width *height;
    int dst_y_length =goalwidth * goalheight;
    for (i = 0; i <goalheight; i++)
        for (j = 0; j <goalwidth; j++) {
            dst[i* goalwidth + j] = data[(i + h_div) * width + j + w_div];
        }
    int index = dst_y_length;
    int src_begin =src_y_length + h_div * width / 4;
    int src_u_length =src_y_length / 4;
    int dst_u_length =dst_y_length / 4;
    for (i = 0; i <goalheight / 2; i++)
        for (j = 0; j <goalwidth / 2; j++) {
            int p = src_begin + i *(width >> 1) + (w_div >> 1) + j;
            dst[index]= data[p];
            dst[dst_u_length+ index++] = data[p + src_u_length];
        }
    return 0;
}
3. 旋转
分为四个方向 。以顺时针270度为例作图。 旋转前:

旋转后:

u值的第i 行j列 对应原 数据的下标为:ustart+uw*j-i;

去除index的乘除法运算后算法:

(1) i420 顺时针 270度
int rotateYUV420Degree270(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) {
    int i = 0, j = 0;
    int index = 0;
    int tempindex = 0;
    int div = 0;
    for (i = 0; i <imageHeight; i++) {
        div= i +1;
        tempindex= 0;
        for (j = 0; j <imageWidth; j++) {
            tempindex+= imageWidth;
            dstyuv[index++]= srcdata[tempindex-div];
        }
    }
    int start =imageWidth*imageHeight;
    int udiv = imageWidth *imageHeight / 4;
    int uWidth = imageWidth /2;
    int uHeight = imageHeight /2;
    index= start;
    for (i = 0; i < uHeight;i++) {
        div= i +1;
        tempindex= start;
        for (j = 0; j < uWidth;j++) {
            tempindex += uWidth;
            dstyuv[index]= srcdata[tempindex-div];
            dstyuv[index+udiv]= srcdata[tempindex-div+udiv];
            index++;
        }
    }
    return 0;
}
(2)i420 顺时针旋转 180
int rotateYUV420Degree180(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight)
{
    int i = 0, j = 0;
    int index = 0;
    int tempindex = 0;
    int ustart = imageWidth \*imageHeight;
    tempindex= ustart;
    for (i = 0; i <imageHeight; i++) {
        tempindex-= imageWidth;
        for (j = 0; j <imageWidth; j++) {
            dstyuv[index++] = srcdata[tempindex + j];
        }
    }
    int udiv = imageWidth *imageHeight / 4;
    int uWidth = imageWidth /2;
    int uHeight = imageHeight /2;
    index= ustart;
    tempindex= ustart+udiv;
    for (i = 0; i < uHeight;i++) {
        tempindex-= uWidth;
        for (j = 0; j < uWidth;j++) {
            dstyuv[index]= srcdata[tempindex + j];
            dstyuv[index+ udiv] = srcdata[tempindex + j + udiv];
            index++;
        }
    }
    return 0;
}
(3)顺时针 90度
int rotateYUV420Degree90(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) {
    int i = 0, j = 0;
    int index = 0;
    int tempindex = 0;
    int div = 0;
    int ustart = imageWidth *imageHeight;
    for (i = 0; i <imageHeight; i++) {
        div= i;
        tempindex= ustart;
        for (j = 0; j <imageHeight; j++) {
            tempindex-= imageWidth;
            dstyuv[index++]= srcdata[tempindex + div];
        }
    }
    int udiv = imageWidth *imageHeight / 4;
    int uWidth = imageWidth /2;
    int uHeight = imageHeight /2;
    index= ustart;
    for (i = 0; i < uHeight;i++) {
        div= i ;
        tempindex= ustart+udiv;
        for (j = 0; j < uWidth;j++) {
            tempindex-= uWidth;
            dstyuv[index]= srcdata[tempindex + div];
            dstyuv[index+ udiv] = srcdata[tempindex + div + udiv];
            index++;
        }
    }
    return 0;
}
4. 镜像
//mirro 原址的
void Mirror(uint8_t\* yuv_temp, int nw, int nh, int w,
        int h) {
    int deleteW = (nw - h) / 2;
    int deleteH = (nh - w) / 2;
    int i, j;
    int a, b;
    uint8_ttemp;
    //mirror y
    for (i = 0; i < h; i++){
        a= i \* w;
        b= (i + 1) \* w - 1;
        while (a < b) {
            temp= yuv_temp[a];
            yuv_temp[a]= yuv_temp[b];
            yuv_temp[b]= temp;
            a++;
            b--;
        }
    }
    //mirror u
    int uindex = w * h;
    for (i = 0; i < h / 2;i++) {
        a = i\ * w / 2;
        b= (i + 1) \* w / 2 - 1;
        while (a < b) {
            temp= yuv_temp[a + uindex];
            yuv_temp[a+ uindex] = yuv_temp[b + uindex];
            yuv_temp[b+ uindex] = temp;
            a++;
            b--;
        }
    }
    //mirror v
    uindex= w * h / 4 * 5;
    for (i = 0; i < h / 2;i++) {
        a= i\* w / 2;
        b= (i + 1) \* w / 2 - 1;
        while (a < b) {
            temp= yuv_temp[a + uindex];
            yuv_temp[a+ uindex] = yuv_temp[b + uindex];
            yuv_temp[b+ uindex] = temp;
            a++;
            b--;
        }
    }
}

算法优化

如果从摄像头取出数据,这样一步步的历遍,在低配手机上是满足不了需求的。其实这三个步骤中有很多中间步骤是可以省去的,比如:将a放到b 位置,再将b位置上的数据取出放到c位置,那么可以直接将a放到c位置。
所以将旋转、裁剪、格式转换三个问题所用的算法整合(未整合进去镜像)。结果如下:
(1)处理不用旋转的图像,格式转换加裁剪
void detailPic0(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) {
    int deleteW = (nw - w) / 2;
    int deleteH = (nh - h) / 2;
    //处理y 旋转加裁剪
    int i, j;
    int index = 0;
    for (j = deleteH; j < nh- deleteH; j++) {
        for (i = deleteW; i < nw- deleteW; i++)
            yuv_temp[index++]= d[j \* nw + i];
    }
    //处理u
    index= w \* h;
    for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++)
        for (j = deleteW + 1; j< nw - deleteW; j += 2)
            yuv_temp[index++]= d[i \* nw + j];
    //处理v 旋转裁剪加格式转换
    for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++)
        for (j = deleteW; j < nw- deleteW; j += 2)
            yuv_temp[index++]= d[i \* nw + j];
}

(2)格式转换、裁剪加旋转90度

void detailPic90(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) {
    int deleteW = (nw - h) / 2;
    int deleteH = (nh - w) / 2;
    int i, j;
    for (i = 0; i < h; i++){
        for (j = 0; j < w; j++){
            yuv_temp[(h- i) \* w - 1 - j] = d[nw \* (deleteH + j) + nw - deleteW
                    -i];
        }
    }
    int index = w \* h;
    for (i = deleteW + 1; i< nw - deleteW; i += 2)
        for (j = nh / 2 \* 3 -deleteH / 2; j > nh + deleteH / 2; j--)
            yuv_temp[index++]= d[(j - 1) \* nw + i];
    for (i = deleteW; i < nw- deleteW; i += 2)
        for (j = nh / 2 \* 3 -deleteH / 2; j > nh + deleteH / 2; j--)
            yuv_temp[index++]= d[(j - 1) \* nw + i];
}

(3)格式转换、裁剪加旋转180度

void detailPic180(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) {
    int deleteW = (nw - w) / 2;
    int deleteH = (nh - h) / 2;
    //处理y 旋转加裁剪
    int i, j;
    int index = w \* h;
    for (j = deleteH; j < nh- deleteH; j++) {
        for (i = deleteW; i < nw- deleteW; i++)
            yuv_temp[--index]= d[j \* nw + i];
    }
    //处理u
    index= w \* h \* 5 / 4;
    for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++)
        for (j = deleteW + 1; j< nw - deleteW; j += 2)
            yuv_temp[--index]= d[i \* nw + j];
    //处理v
    index= w \* h \* 3 / 2;
    for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++)
        for (j = deleteW; j < nw- deleteW; j += 2)
            yuv_temp[--index]= d[i \* nw + j];
}

(4)格式转换、裁剪加旋转270度

void detailPic270(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) {
    int deleteW = (nw - h) / 2;
    int deleteH = (nh - w) / 2;
    int i, j;
    //处理y 旋转加裁剪
    for (i = 0; i < h; i++){
        for (j = 0; j < w; j++){
            yuv_temp[i\* w + j] = d[nw \* (deleteH + j) + nw - deleteW - i];
        }
    }
    //处理u 旋转裁剪加格式转换
    int index = w \* h;
    for (i = nw - deleteW - 1;i > deleteW; i -= 2)
        for (j = nh + deleteH / 2;j < nh / 2 \* 3 - deleteH / 2; j++)
            yuv_temp[index++]= d[(j) \* nw + i];
    //处理v 旋转裁剪加格式转换
    for (i = nw - deleteW - 2;i >= deleteW; i -= 2)
        for (j = nh + deleteH / 2;j < nh / 2 \* 3 - deleteH / 2; j++)
            yuv_temp[index++]= d[(j) \* nw + i];
}

注:以上算法消除index的乘法后效果肯定会更好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值