在http://blog.csdn.net/mikedai/article/details/64215860这篇博文中,采用查表的方式优化YUV转RGB,但是仍然没有发挥计算机硬件性能,对1920x1080的YV12格式,在intel i7-7700上测试120fps,也即是每帧耗时8ms。
采用mmx结合查找表优化,首先创建查找表,跟之前博文里有点不一样:

static  short CoefficientsRGBY[256][4];
static  short CoefficientsRGBU[256][4];
static  short CoefficientsRGBV[256][4];
void init_coefficients()
{
    int i;
    for (i = 0; i < 256; i++)
    {
        CoefficientsRGBY[i][0] = (short)(1.164 * 64 * (i - 16) + 0.5);
        CoefficientsRGBY[i][1] = (short)(1.164 * 64 * (i - 16) + 0.5);
        CoefficientsRGBY[i][2] = (short)(1.164 * 64 * (i - 16) + 0.5);
        CoefficientsRGBY[i][3] = 0x00;        CoefficientsRGBU[i][0] = (short)(2.018 * 64 * (i - 128) + 0.5);
        CoefficientsRGBU[i][1] = (short)(-0.391 * 64 * (i - 128) + 0.5);
        CoefficientsRGBU[i][2] = 0x00;
        CoefficientsRGBU[i][3] = 0x00;        CoefficientsRGBV[i][0] = 0x00;
        CoefficientsRGBV[i][1] = (short)(-0.813 * 64 * (i - 128) + 0.5);
        CoefficientsRGBV[i][2] = (short)(1.596 * 64 * (i - 128) + 0.5);
        CoefficientsRGBV[i][3] = 0x00;
    }
}

这里只是采用了16位整数作为查找表的值,精度上可能比32位稍差,但是对于大多数应用已经足够。
采用mmx实现:

int YUV420_RGB32_mmx(uint32_t* rgb, int width, int height, uint8_t* y, uint8_t* u, uint8_t* v)
{__asm{pushadfinitxor eax, eaxmov ebx, heightmov ecx, widthmov edx, ymov edi, vmov esi, umov ebp, rgbhloop:push ebxmov ebx, ecxwloop :push ebxxor ebx, ebxmov al, [edi]mov bl, [esi]movq mm0, [CoefficientsRGBU + 8*eax]paddw mm0, [CoefficientsRGBV + 8*ebx]mov al, [edx]mov bl, [edx + 1]movq mm1, [CoefficientsRGBY + 8 * eax]movq mm2, [CoefficientsRGBY + 8 * ebx]mov al, [edx + ecx]mov bl, [edx + ecx + 1]movq mm3, [CoefficientsRGBY + 8 * eax]movq mm4, [CoefficientsRGBY + 8 * ebx]paddw mm1, mm0paddw mm2, mm0paddw mm3, mm0paddw mm4, mm0psraw mm1, 6psraw mm2, 6psraw mm3, 6psraw mm4, 6packuswb mm1, mm2packuswb mm3, mm4movq[ebp], mm1movq[ebp + 4 * ecx], mm3add ebp, 8add edx, 2add edi, 1add esi, 1pop ebxsub ebx, 2jnz wlooplea ebp, [ebp + 4*ecx]add edx, ecxpop ebxsub ebx, 2jnz hloopemmspopad}
}

测试结果:1920x1080,YV12,100帧,172ms,将近600fps,跟之前查表实现提升5倍速度。
原因分析:采用mmx一次能读取8字节数据,另外,

            paddw mm1, mm0paddw mm2, mm0paddw mm3, mm0paddw mm4, mm0

相当于一次并行处理处理4个像素。
**缺点:64位下编译不通过,国外网站有说可以单独编写成汇编文件,不要混用,可以在64位下编译,但是没有试过。

使用mmx或者sse指令可以实现一些低度并行计算,姑且称为低度并行计算,但是要实现高度并行计算并获取更高的效率,需要采用GPU。

使用CUDA实现,参考nvidia Video_Codec_SDK和opencv中的代码:

void cudasafe(int error, char* message, char* file, int line) {if (error != cudaSuccess) {fprintf(stderr, "CUDA Error: %s : %i. In %s line %d\n", message, error, file, line);exit(-1);}
}
__host__ __device__ __forceinline__ int divUp(int total, int grain)
{return (total + grain - 1) / grain;
}typedef unsigned char uchar;
typedef unsigned int  uint;__device__ static void YUV2RGB(const uint* yuvi, float* red, float* green, float* blue)
{float luma, chromaCb, chromaCr;// Prepare for hue adjustmentluma = (float)yuvi[0];chromaCb = (float)((int)yuvi[1] - 512.0f);chromaCr = (float)((int)yuvi[2] - 512.0f);// Convert YUV To RGB with hue adjustment*red = (luma     * constHueColorSpaceMat[0]) +(chromaCb * constHueColorSpaceMat[1]) +(chromaCr * constHueColorSpaceMat[2]);*green = (luma     * constHueColorSpaceMat[3]) +(chromaCb * constHueColorSpaceMat[4]) +(chromaCr * constHueColorSpaceMat[5]);*blue = (luma     * constHueColorSpaceMat[6]) +(chromaCb * constHueColorSpaceMat[7]) +(chromaCr * constHueColorSpaceMat[8]);
}__device__ static uint RGBA_pack_10bit(float red, float green, float blue, uint alpha)
{uint ARGBpixel = 0;// Clamp final 10 bit resultsred = min(max(red, 0.0f), 1023.f);green = min(max(green, 0.0f), 1023.f);blue = min(max(blue, 0.0f), 1023.f);// Convert to 8 bit unsigned integers per color componentARGBpixel = (((uint)blue >> 2) |(((uint)green >> 2) << 8) |(((uint)red >> 2) << 16) |(uint)alpha);return ARGBpixel;
}// CUDA kernel for outputing the final ARGB output from NV12#define COLOR_COMPONENT_BIT_SIZE 10
#define COLOR_COMPONENT_MASK     0x3FF__global__ void NV12_to_RGB(const uchar* srcImage, size_t nSourcePitch,uint* dstImage, size_t nDestPitch,uint width, uint height)
{// Pad borders with duplicate pixels, and we multiply by 2 because we process 2 pixels per threadconst int x = blockIdx.x * (blockDim.x << 1) + (threadIdx.x << 1);const int y = blockIdx.y *  blockDim.y + threadIdx.y;if (x >= width || y >= height)return;// Read 2 Luma components at a time, so we don't waste processing since CbCr are decimated this way.// if we move to texture we could read 4 luminance valuesuint yuv101010Pel[2];yuv101010Pel[0] = (srcImage[y * nSourcePitch + x]) << 2;yuv101010Pel[1] = (srcImage[y * nSourcePitch + x + 1]) << 2;const size_t chromaOffset = nSourcePitch * height;const int y_chroma = y >> 1;if (y & 1)  // odd scanline ?{uint chromaCb = srcImage[chromaOffset + y_chroma * nSourcePitch + x];uint chromaCr = srcImage[chromaOffset + y_chroma * nSourcePitch + x + 1];if (y_chroma < ((height >> 1) - 1)) // interpolate chroma vertically{chromaCb = (chromaCb + srcImage[chromaOffset + (y_chroma + 1) * nSourcePitch + x] + 1) >> 1;chromaCr = (chromaCr + srcImage[chromaOffset + (y_chroma + 1) * nSourcePitch + x + 1] + 1) >> 1;}yuv101010Pel[0] |= (chromaCb << (COLOR_COMPONENT_BIT_SIZE + 2));yuv101010Pel[0] |= (chromaCr << ((COLOR_COMPONENT_BIT_SIZE << 1) + 2));yuv101010Pel[1] |= (chromaCb << (COLOR_COMPONENT_BIT_SIZE + 2));yuv101010Pel[1] |= (chromaCr << ((COLOR_COMPONENT_BIT_SIZE << 1) + 2));}else{yuv101010Pel[0] |= ((uint)srcImage[chromaOffset + y_chroma * nSourcePitch + x] << (COLOR_COMPONENT_BIT_SIZE + 2));yuv101010Pel[0] |= ((uint)srcImage[chromaOffset + y_chroma * nSourcePitch + x + 1] << ((COLOR_COMPONENT_BIT_SIZE << 1) + 2));yuv101010Pel[1] |= ((uint)srcImage[chromaOffset + y_chroma * nSourcePitch + x] << (COLOR_COMPONENT_BIT_SIZE + 2));yuv101010Pel[1] |= ((uint)srcImage[chromaOffset + y_chroma * nSourcePitch + x + 1] << ((COLOR_COMPONENT_BIT_SIZE << 1) + 2));}// this steps performs the color conversionuint yuvi[6];float red[2], green[2], blue[2];yuvi[0] = (yuv101010Pel[0] & COLOR_COMPONENT_MASK);yuvi[1] = ((yuv101010Pel[0] >> COLOR_COMPONENT_BIT_SIZE)       & COLOR_COMPONENT_MASK);yuvi[2] = ((yuv101010Pel[0] >> (COLOR_COMPONENT_BIT_SIZE << 1)) & COLOR_COMPONENT_MASK);yuvi[3] = (yuv101010Pel[1] & COLOR_COMPONENT_MASK);yuvi[4] = ((yuv101010Pel[1] >> COLOR_COMPONENT_BIT_SIZE)       & COLOR_COMPONENT_MASK);yuvi[5] = ((yuv101010Pel[1] >> (COLOR_COMPONENT_BIT_SIZE << 1)) & COLOR_COMPONENT_MASK);// YUV to RGB Transformation conversionYUV2RGB(&yuvi[0], &red[0], &green[0], &blue[0]);YUV2RGB(&yuvi[3], &red[1], &green[1], &blue[1]);// Clamp the results to RGBAconst size_t dstImagePitch = nDestPitch >> 2;dstImage[y * dstImagePitch + x] = RGBA_pack_10bit(red[0], green[0], blue[0], ((uint)0xff << 24));dstImage[y * dstImagePitch + x + 1] = RGBA_pack_10bit(red[1], green[1], blue[1], ((uint)0xff << 24));
}void nv12_to_rgb32(uint8_t* yuv, uint8_t* data, uint32_t yuv_pitch, uint32_t data_pitch, int w, int h)
{uint8_t* device_yuv;uint32_t* device_data;uint32_t  yuv_size;uint32_t  data_size;yuv_size = w * h * 3 / 2;data_size = w * 4 * h;if (yuv_size == 0)return;cudasafe(cudaMalloc((void **)&device_yuv, yuv_size), "Original image allocation ", __FILE__, __LINE__);cudasafe(cudaMemcpy(device_yuv, yuv, yuv_size, cudaMemcpyHostToDevice), "Copy original image to device ", __FILE__, __LINE__);cudasafe(cudaMalloc((void **)&device_data, data_size), "Original image allocation ", __FILE__, __LINE__);//dim3 block(32, 16, 1);//dim3 grid((w + (2 * block.x - 1)) / (2 * block.x), (h + (block.y - 1)) / block.y, 1);dim3 block(32, 8);dim3 grid(divUp(w, 2 * block.x), divUp(h, block.y));NV12_to_RGB << <grid, block >> > (device_yuv, yuv_pitch, device_data, (size_t)data_pitch, w, h);cudasafe(cudaMemcpy(data, device_data, data_size, cudaMemcpyDeviceToHost), "from device to host", __FILE__, __LINE__);cudaFree(device_yuv);cudaFree(device_data);
}

以上代码只是nv12转rgb,稍作修改可以实现nv21转rgb、yv12转rgb,或者其他yuv格式。
测试结果,gtx 1050 显卡,i7-7700cpu,1920x1080,1500fps,不包括从内存拷贝数据到GPU的时间。

结论:采用GPU可以实现更快的转换速度,但是从内存将yuv数据拷贝到显存差不多1ms,然后将结构拷贝到内存仍然需要1ms,所以大部分实现消耗在IO上,这样计算下来也就500fps左右,使用mmx可以达到600fps,但是mmx的寄存器也不多,对多个线程同事用mmx,可能会存在一些问题。二者都可以实现更高的转换效率,至于其中选择,需要看具体需求。

YUV转RGB--使用MMX和CUDA优化相关推荐

  1. 图像算法研究---一种简单的YUV转RGB的优化算法

    YUV与RGB的相互转换一直以来都是非常常用的基础算法,如何才能最高效的转换,成为一个难点问题,尤其是目前视频直播火热的时候,这些算法的优化也越发重要. 今天我结合自己的一些经验,给大家介绍一个简单的 ...

  2. 【DSP开发】【VS开发】YUV与RGB格式转换

    [视频处理]YUV与RGB格式转换 YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与 ...

  3. Android视频: YUV转RGB

    Android SDK为Camera预览提供了一个Demo,这个Demo的大致流程是初始化一个Camera和一个SurfaceView,SurfaceView被创建之后可以获取到一个SurfaceHo ...

  4. 最简单的基于FFmpeg的libswscale的示例(YUV转RGB)

    ===================================================== 最简单的基于FFmpeg的libswscale的示例系列文章列表: 最简单的基于FFmpeg ...

  5. ffmpeg 保存图片 将rgb数据_FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)...

    FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...

  6. YUV和RGB的区别(以及YUV444 YUV422 YUV411 YUV420的介绍)

    RGB是构成多种颜色的三基色(红绿蓝),也称为加成色.主要是图像的采集和显示.YUV是优化彩色视频信号的编码和传输,和rgb相比,YUV占用的带宽少. YUV中Y表示的是亮度,是通过RGB输入信号确定 ...

  7. yuv与rgb的转换矩阵推导过程

    文章目录 yuv与rgb的转换矩阵推导过程 yuv与rgb互转公式 yuv420p转rgb888矩阵 优化一下 参考 yuv与rgb的转换矩阵推导过程 在网上经常看到一些shader里用了矩阵就能把y ...

  8. OpenCV YUV 与 RGB的互转(草稿)

    YUV 转 RGB cv::Mat yuvImg; /*//下面两行是我自己代码里用来构成YUV图像的数据,可以忽略 yuvImg.create(pInput->height*3/2, pInp ...

  9. YUV图像实时去雾算法的优化与改进

    YUV图像实时去雾算法的优化与改进 2016-03-02 21:04 摘要:针对已有的基于DSP的实时去雾算法出现的问题进行优化和改进.在降采样倍数过大时,去雾效果会出现一些副作用,包括纹理细节丢失. ...

最新文章

  1. hdu 1312 Red and Black 解题报告
  2. nyoj971爬行的蚂蚁
  3. python车牌识别系统开源代码_天津谁做车牌识别系统供应商,伸缩栅栏门_郑州荣锋科技有限公司...
  4. 如何通过link_to传递一个post请求
  5. android 百度定位 sdk 3.3demo,BaiduMapApi_Sample_Android_1.3.3\demo无法启动
  6. Linux SendMail发送邮件失败诊断案例(四)
  7. zetoro导入csl 软件学报
  8. centos7安装便捷小工具命令
  9. 熟悉VB 6.0界面
  10. 极力推荐--不错的delphi 学习网站与论坛---有待补充
  11. 57、弱电网络管理入门与基础技术
  12. 算法题目打卡:Ques20201007
  13. 电脑IP地址查看及修改
  14. 「LSTM 之父」亲笔万字长文,只为向世人证明:深度学习不是在母语为英语的地方被发明的...
  15. 中国的ruby on rails 视频
  16. NB-IoT和GSM概述
  17. 电脑读取不U盘,在磁盘管理器中显示“无媒体”解决方法
  18. ​LeetCode刷题实战375:猜数字大小 II
  19. 读书笔记--Java虚拟机垃圾收集算法
  20. 2022年山东省职业院校技能大赛中职组“网络搭建与应用”赛项规程

热门文章

  1. 2019年一级消防工程师备考教材梳理指南
  2. python里range什么意思_python中range什么意思
  3. 重温傅里叶--深入篇1--傅里叶级数与傅里叶变换的关系以及频谱图的介绍
  4. matlab中 repmat 函数的用法
  5. jcrop java_[Java教程]Jcrop简单实用
  6. macos安装git命令行_MAC OS git客户端安装及操作
  7. 每天一算法题 之 《剑指offer》 -- 二维数组中的查找
  8. idea 连接远程docker
  9. 元宇宙、web 3.0、NFT的关系
  10. 自考操作系统-----页置换算法