Search code examples
c++opencvvlcyuv

YUV420 to BGR image from pixel pointers


I am capturing raw output from a decoder which is YUV420. I have got three pointers: Y(1920*1080), U(960*540) and V(960*540) separately.

I want to save the image as JPEG using OpenCV. I tried using cvtcolor of opencv

cv::Mat i_image(cv::Size(columns, rows), CV_8UC3, dataBuffer);
cv::Mat i_image_BGR(cv::Size(columns, rows), CV_8UC3);
cvtColor(i_image, i_image_BGR, cv::COLOR_YCrCb2BGR);
cv::imwrite("/data/data/org.myproject.debug/files/pic1.jpg", i_image_BGR);

But, here is the output image which is saved:

image

Can someone please suggest what is the proper way of saving the image?

YUV Binary files for reference


Solution

  • Following is the process to convert the provided YUV files into RGB image.

    • Read Y, U and V binary files into byte buffers
    • Create OpenCV Mat object from the created buffers.
    • Resize U and V Mats to the size of Y.
    • Merge Y and resized U and V.
    • Convert from YUV to BGR

    Be advised that the resizing step is just an optimized way of repeating the values of U and V. This is only valid in the case where Y has twice the resolution of U and V in both dimensions. This approach should be invalid for arbitrary size images (not tested).

    Here is the code for the above-mentioned process.

    #include <iostream>
    #include <vector>
    #include <opencv2/opencv.hpp>
    
    std::vector<unsigned char> readBytesFromFile(const char* filename)
    {
        std::vector<unsigned char> result;
    
        FILE* f = fopen(filename, "rb");
    
        fseek(f, 0, SEEK_END);  // Jump to the end of the file
        long length = ftell(f); // Get the current byte offset in the file
        rewind(f);              // Jump back to the beginning of the file
    
        result.resize(length);
    
        char* ptr = reinterpret_cast<char*>(&(result[0]));
        fread(ptr, length, 1, f); // Read in the entire file
        fclose(f); // Close the file
    
        return result;
    }
    
    int main(int argc, char** argv)
    {
        cv::Size actual_size(1920, 1080);
        cv::Size half_size(960, 540);
    
        //Read y, u and v in bytes arrays
        auto y_buffer = readBytesFromFile("ypixel.bin");
        auto u_buffer = readBytesFromFile("upixel.bin");
        auto v_buffer = readBytesFromFile("vpixel.bin");
    
    
        cv::Mat y(actual_size, CV_8UC1, y_buffer.data());
        cv::Mat u(half_size, CV_8UC1, u_buffer.data());
        cv::Mat v(half_size, CV_8UC1, v_buffer.data());
    
        cv::Mat u_resized, v_resized;
        cv::resize(u, u_resized, actual_size, 0, 0, cv::INTER_NEAREST); //repeat u values 4 times
        cv::resize(v, v_resized, actual_size, 0, 0, cv::INTER_NEAREST); //repeat v values 4 times
    
        cv::Mat yuv;
    
        std::vector<cv::Mat> yuv_channels = { y, u_resized, v_resized };
        cv::merge(yuv_channels, yuv);
    
        cv::Mat bgr;
        cv::cvtColor(yuv, bgr, cv::COLOR_YUV2BGR);
        cv::imwrite("bgr.jpg", bgr);
    
        return 0;
    }
    

    Compiled and tested with following command:

    g++ -o yuv2rgb -std=c++11 yuv2rgb.cpp -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc

    Following output image is generated by executing the above code:

    YUV To BGR Output