c语言实现nv21图像数据转化成rgb24数据

前言:因为需要将安卓设备的相机预览数据传到pc,然后在windows pc上显示出来,所以涉及到socket通信用于数据传输,以及预览数据转化问题。由于pc端软件使用c#开发,预览数据转化效率太低,于是采用c语言实现,并转化成dll供C#使用,转化效率提升30倍(测试时采用1280*720图像数据,耗时10ms左右)。

一、nv21转rgb24(rgb888)方法如下:

//将nv21数据转化成rgb24的数据
// input: nv21数据,长度为宽*高*3/2
// output: rgb数据,长度为宽*高*3
// width: 图像宽度
// height:图像高度
int nv21_to_rgb24(unsigned char* input, unsigned char* output, int width, int height) {
    if (width < 1 || height < 1 || input == NULL || output == NULL)
        return 0;
    // bit depth
    int depth = 3;
    int nvOff = width * height;
    int i, j, yIndex = 0;
    int y, u, v;
    int r, g, b, nvIndex = 0;
    unsigned char* yuvData = input;
    unsigned char* rgbData = output;
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++, ++yIndex) {
            nvIndex = (i / 2) * width + j - j % 2;
            y = yuvData[yIndex] & 0xff;
            v = yuvData[nvOff + nvIndex] & 0xff;
            u = yuvData[nvOff + nvIndex + 1] & 0xff;

            // yuv to rgb
            r = y + ((351 * (v - 128)) >> 8);  //r
            g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
            b = y + ((443 * (u - 128)) >> 8); //b

            r = ((r > 255) ? 255 : (r < 0) ? 0 : r);
            g = ((g > 255) ? 255 : (g < 0) ? 0 : g);
            b = ((b > 255) ? 255 : (b < 0) ? 0 : b);
            // RGB格式的图像存储的顺序,并非像字面的顺序,而是以:B、G、R的顺序进行存储。
            *(rgbData + yIndex * depth + 0) = b;
            *(rgbData + yIndex * depth + 1) = g;
            *(rgbData + yIndex * depth + 2) = r;

        }
    }
    return 1;
}

二、由于安卓相机预览数据是逆时针旋转了90度的,所以要在PC上查看得调正,所以自己在上述方法中稍作调整。

以下方法为nv21转rgb24,并顺时针旋转90,消耗时间跟nv21转rgb24方法几乎一样,两步并成一步,节省时间。

// nv21转rgb24,转化完后直接旋转。省得转化后再旋转浪费时间。
//将nv21数据转化成rgb24的数据
// input: nv21数据,长度为宽*高*3/2
// output: rgb数据,长度为宽*高*3
// width: 图像宽度(旋转前)
// height:图像高度(旋转前)
// 旋转后图像宽==height,图像高==width
int nv21_to_rgb24_clockwise_rotate90(unsigned char* input, unsigned char* output, int width, int height) {
    if (width < 1 || height < 1 || input == NULL || output == NULL)
        return 0;
    // bit depth
    int depth = 3;
    int nvOff = width * height;
    int i, j, yIndex = 0;
    int y, u, v;
    int r, g, b, nvIndex = 0;
    unsigned char* yuvData = input;
    unsigned char* rgbData = output;
    // yuv转成rgb后,立马将数据放到旋转后的位置。第一行换到最后一列,第二行换到倒数第二列,以此类推
    // 当前正在做旋转处理的数据所在列
    int currTransColumn = height;
    int currColumnBaseIndex = 0;
    // 转化后的图像中,每一行的数据长度。
    int afterTransLinesLength = height * depth;

    int tmp = 0;
    for (i = 0; i < height; i++) {
        // 转化后图像中每列的第一个rgb数据最大下标+1,从最后一列开始计算
        currColumnBaseIndex = currTransColumn * depth;
        for (j = 0; j < width; j++, ++yIndex) {
            nvIndex = (i / 2) * width + j - j % 2;
            y = yuvData[yIndex] & 0xff;
            v = yuvData[nvOff + nvIndex] & 0xff;
            u = yuvData[nvOff + nvIndex + 1] & 0xff;

            // yuv to rgb
            r = y + ((351 * (v - 128)) >> 8);  //r
            g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
            b = y + ((443 * (u - 128)) >> 8); //b

            r = ((r > 255) ? 255 : (r < 0) ? 0 : r);
            g = ((g > 255) ? 255 : (g < 0) ? 0 : g);
            b = ((b > 255) ? 255 : (b < 0) ? 0 : b);
            // RGB格式的图像存储的顺序,并非像字面的顺序,而是以:B、G、R的顺序进行存储。
            // 旋转后放的位置
            *(rgbData + currColumnBaseIndex - 3) = b;
            *(rgbData + currColumnBaseIndex - 2) = g;
            *(rgbData + currColumnBaseIndex - 1) = r;
            // 下一次复制的下标为上一行下标+每一行的数据长度
            currColumnBaseIndex = currColumnBaseIndex + afterTransLinesLength;
        }
        // 减一列
        currTransColumn--;
    }
    return 1;
}

PS:想要了解安卓和PC进行socket通信的,请查看我的相关博客,附有demo。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值