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:
Can someone please suggest what is the proper way of saving the image?
YUV Binary files for reference
Following is the process to convert the provided YUV files into RGB image.
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:
I think OpenCV matrix for your input yuv420 planar image should have 1 channel format instead of 3 channel. Place there Y channel, then U, then V. I found a very similar question HERE Planar YUV420 and NV12 is the same
For those who came here for YUV 420 to BGR/RGB Conversion (First Google search Result), Please refer to this code:
#include "opencv2/opencv.hpp"
#include "opencv2/opencv_modules.hpp"
#include <iostream>
#include <fstream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Size iSize(512,512);
int iYUV_Size = iSize.width * (iSize.height + iSize.height / 2);
Mat mSrc_YUV420(cv::Size(iSize.width,iSize.height+ iSize.height / 2),CV_8UC1);
ifstream FileIn;
FileIn.open("Sample_YUV420_512x512.yuv", ios::binary | ios::in);
if (FileIn.is_open())
{
FileIn.read((char*)mSrc_YUV420.data, iYUV_Size);
FileIn.close();
}
else
{
printf("[Error] Unable to Read the Input File! \n");
}
Mat mSrc_BGR(cv::Size(iSize.width, iSize.height), CV_8UC1);
cv::cvtColor(mSrc_YUV420, mSrc_BGR, COLOR_YUV2BGR_I420);
imshow("Input YUV Image", mSrc_YUV420);
imshow("Input RGB Image", mSrc_BGR);
cv::waitKey();
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With