cpp rgb to yuv422 conversion

僤鯓⒐⒋嵵緔 提交于 2020-07-07 11:48:05

问题


I'm trying to convert an image (originally from QImage) in a RGB/RGBA format (can be changed) to a YUV422 format. My initial intention was to use OpenCV cvtColor to do the work but it does not enable the conversion of RGB/RGBA to 422 format.

I searched for alternatives and even considered to write my own conversion according to this but it would not work fast enough.

I searched for another library to use and found this post but it is relay old and not so relevant.

So my question is what good options do I have for RGB->YUV422 conversions? It would be better if they perform conversions on the GPU instead of the CPU.

Thanks in advance


回答1:


In this somewhat related answer, they suggest to use Intel Performance Primitives and the OP seemed to achieve the expected results (conversion of many PAL streams in realtime).




回答2:


I solved my problem using OpenCL, following this: Tutorial: Simple start with OpenCL and C++

I changed the conversion to be Format_ARGB32_Premultiplied to YUV422 but it can be easily changed to any format.

openclwrapper.h:

class OpenClWrapper
{
public:
    OpenClWrapper(size_t width, size_t height);
    ~OpenClWrapper();

    void RGB2YUV422(unsigned int * yuvImg, unsigned char * rgbImg);

private:
    std::vector<cl::Platform> m_all_platforms;
    std::vector<cl::Device> m_all_devices;
    cl::Platform m_default_platform;
    cl::Device m_default_device;
    cl::Context m_context;
    cl::Program::Sources m_sources;
    cl::Program m_program;
    cl::CommandQueue m_queue;
    cl::Buffer m_buffer_yuv;
    cl::Buffer m_buffer_rgb;
    std::string m_kernel_code;

    size_t m_width;
    size_t m_height;

};

openclwrapper.cpp:

#include "openclwrapper.h"
#include <iostream>
#include <sstream>

OpenClWrapper::OpenClWrapper(size_t width, size_t height) :
    m_height(height),
    m_width(width)
{
    //get all platforms (drivers)
       cl::Platform::get(&m_all_platforms);
       if(m_all_platforms.size()==0){
           std::cout<<" No platforms found. Check OpenCL installation!\n";
           exit(1);
       }
       m_default_platform=m_all_platforms[0];

       //get default device of the default platform
       m_default_platform.getDevices(CL_DEVICE_TYPE_ALL, &m_all_devices);
       if(m_all_devices.size()==0){
           std::cout<<" No devices found. Check OpenCL installation!\n";
           exit(1);
       }
       m_default_device=m_all_devices[0];


       m_context = *(new cl::Context({m_default_device}));

       std::ostringstream oss;

       oss <<
               "   void kernel RGB2YUV422(global const unsigned char rgbImg[" << m_height << "][" << m_width << "*4], global unsigned int yuvImg[" << m_height << "][" << m_width << "/2]){       \n"
               "       int x_idx = get_global_id(0);                                                                                        \n"
               "       int y_idx = get_global_id(1)*8;                                                                                      \n"
               "       int alpha1 = rgbImg[x_idx][y_idx+3];                                                                                 \n"
               "       int alpha2 = rgbImg[x_idx][y_idx+7];                                                                                 \n"
               "       unsigned char R1 = rgbImg[x_idx][y_idx+2]  * (255 / alpha1);                                                         \n"
               "       unsigned char G1 = rgbImg[x_idx][y_idx+1]  * (255 / alpha1);                                                         \n"
               "       unsigned char B1 = rgbImg[x_idx][y_idx] * (255 / alpha1);                                                            \n"
               "       unsigned char R2 = rgbImg[x_idx][y_idx+6] * (255 / alpha2);                                                          \n"
               "       unsigned char G2 = rgbImg[x_idx][y_idx+5] * (255 / alpha2);                                                          \n"
               "       unsigned char B2 = rgbImg[x_idx][y_idx+4] * (255 / alpha2);                                                          \n"

               "       unsigned char Y1 = (unsigned char)(0.299000*R1 + 0.587000*G1 + 0.114000*B1);                                         \n"
               "       unsigned char Y2 = (unsigned char)(0.299000*R2 + 0.587000*G2 + 0.114000*B2);                                         \n"
               "       unsigned char U = (unsigned char)(-0.168736*R1-0.331264*G1+0.500000*B1+128);//(0.492*(B1-Y1));                       \n"
               "       unsigned char V = (unsigned char)(0.500000*R1-0.418688*G1-0.081312*B1+128);//(0.877*(R1-Y1));                        \n"
               "       yuvImg[get_global_id(0)][get_global_id(1)] = (unsigned int)(Y2 << 24 | V << 16 | Y1 << 8 | U);                       \n"
               "   }                                                                                                                        ";

       m_kernel_code = oss.str();

       m_sources.push_back({m_kernel_code.c_str(),m_kernel_code.length()});

       m_program = *(new cl::Program(m_context,m_sources));
       if(m_program.build({m_default_device})!=CL_SUCCESS){
           std::cout<<" Error building: "<<m_program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(m_default_device)<<"\n";
           exit(1);
       }


       // create buffers on the device
       m_buffer_yuv = *(new cl::Buffer(m_context,CL_MEM_READ_WRITE,sizeof(unsigned int)*(m_width*m_height/2))); //each cell is int so it is 4 times the mem nedded, but each pixel is represented by 16 bits
       m_buffer_rgb = *(new cl::Buffer(m_context,CL_MEM_READ_WRITE,sizeof(unsigned char)*(m_width*m_height*4))); // each pixel is represented by 4 bytes (alpha, RGB)

}

OpenClWrapper::~OpenClWrapper(){
    free(&m_buffer_rgb);
    free(&m_buffer_yuv);
}

void OpenClWrapper::RGB2YUV422(unsigned int * yuvImg, unsigned char * rgbImg){


    cl::CommandQueue queue(m_context,m_default_device);
       //write rgb image to the OpenCl buffer
       queue.enqueueWriteBuffer(m_buffer_rgb,CL_TRUE,0,sizeof(unsigned char)*(m_width*m_height*4),rgbImg);


       //run the kernel
       cl::Kernel kernel_yuv2rgb=cl::Kernel(m_program,"RGB2YUV422");
       kernel_yuv2rgb.setArg(0,m_buffer_rgb);
       kernel_yuv2rgb.setArg(1,m_buffer_yuv);
       queue.enqueueNDRangeKernel(kernel_yuv2rgb,cl::NullRange,cl::NDRange(m_height,(m_width/2)),cl::NullRange); //range is divided by 2 because we have width is represented in integers instead of 16bit (as needed in yuv422).
       queue.finish();

       //read result yuv Image from the device to yuv Image pointer
       queue.enqueueReadBuffer(m_buffer_yuv,CL_TRUE,0,sizeof(unsigned int)*(m_width*m_height/2),yuvImg);

}


来源:https://stackoverflow.com/questions/49964259/cpp-rgb-to-yuv422-conversion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!