Article directory
-
- Get the yuv video frame data y and uv from the video channel, read and merge them into a complete yuv and convert it to bgr.
- Detailed explanation
- Further encapsulation
-
- ImageConverter.h
- ImageConverter.cpp
- main.cpp
Obtain yuv video frame data y and uv from the video channel, read and merge them into complete yuv and convert them to bgr.
#include <iostream> #include <fstream> #include <opencv2/opencv.hpp> int main() {<!-- --> const std::string yFile = "./Y.yuv";//Replace by yourself const std::string uvFile = "./UV.yuv";//Replace by yourself const int width = 1920; //Replace by yourself const int height = 1080; //Replace by yourself // Calculate frame size const int frameSize = width * height * 3 / 2; // The number of bytes occupied by each frame of the YUV420 image //Open YUV420 image file std::ifstream yfile(yFile, std::ios::binary); if (!yfile) {<!-- --> std::cerr << "Failed to open Y file." << std::endl; return 1; } std::ifstream uvfile(uvFile, std::ios::binary); if (!uvfile) {<!-- --> std::cerr << "Failed to open UV file." << std::endl; return 1; } //Read YUV data std::vector<uint8_t> yData(frameSize); yfile.read(reinterpret_cast<char*>(yData.data()), frameSize); std::vector<uint8_t> uvData(frameSize); uvfile.read(reinterpret_cast<char*>(uvData.data()), frameSize); //Extract Y, U, V components std::vector<uint8_t> Y(width * height); std::vector<uint8_t> U(width * height / 4); std::vector<uint8_t> V(width * height / 4); std::copy_n(yData.begin(), width * height, Y.begin()); std::copy_n(uvData.begin(), width * height / 4, U.begin()); std::copy_n(uvData.begin() + width * height / 4, width * height / 4, V.begin()); cv::Mat yuvImage(height * 3 / 2, width, CV_8UC1); std::memcpy(yuvImage.data, Y.data(), width * height); std::memcpy(yuvImage.data + width * height, U.data(), width * height / 4); std::memcpy(yuvImage.data + width * height * 5 / 4, V.data(), width * height / 4); cv::Mat bgrImage; cv::cvtColor(yuvImage, bgrImage, cv::COLOR_YUV2BGR_NV12); // display image cv::imshow("YUV420 Image", bgrImage); cv::waitKey(6000); cv::destroyAllWindows(); return 0; }
Detailed explanation
Of course, I’ll explain your code line by line:
#include <iostream> #include <fstream> #include <opencv2/opencv.hpp>
These are the C++ header file import sections.
is used for standard input and output,
is used for file operations,
is for the OpenCV library Header files that allow you to use OpenCV functionality.
int main() {<!-- -->
This is the beginning of the main
function.
const std::string yFile = "./Y.yuv"; const std::string uvFile = "./UV.yuv";
Two string variables yFile
and uvFile
are defined here to store the file paths of the Y and UV components.
const int width = 1920; const int height = 1080;
The width and height of the image are defined here.
const int frameSize = width * height * 3 / 2;
This is calculated as the size of each frame of the image, because in a YUV420 image, the Y component occupies the size of a frame, while the U and V components each occupy a quarter of the size of a frame. This is calculated based on how the YUV420 format is stored.
std::ifstream yfile(yFile, std::ios::binary); std::ifstream uvfile(uvFile, std::ios::binary);
The files of Y and UV components are opened here, using the ifstream
stream, and the opening method is binary mode.
if (!yfile) {<!-- --> std::cerr << "Failed to open Y file." << std::endl; return 1; }
This conditional statement checks whether the file of the Y component is successfully opened. If it fails, an error message is output and 1 is returned, indicating a program error.
std::vector<uint8_t> yData(frameSize); std::vector<uint8_t> uvData(frameSize);
Two std::vector
are defined here, used to store data of Y and UV components respectively. Their size is the size of a frame.
yfile.read(reinterpret_cast<char*>(yData.data()), frameSize); uvfile.read(reinterpret_cast<char*>(uvData.data()), frameSize);
The read
function is used here to read Y and UV data from the file. Note that type conversion is performed through reinterpret_cast
, because the file operation requires the char*
type. .
std::vector<uint8_t> Y(width * height); std::vector<uint8_t> U(width * height / 4); std::vector<uint8_t> V(width * height / 4);
Three std::vector
are defined here, which are used to store the extracted Y, U and V component data respectively.
std::copy_n(yData.begin(), width * height, Y.begin()); std::copy_n(uvData.begin(), width * height / 4, U.begin()); std::copy_n(uvData.begin() + width * height / 4, width * height / 4, V.begin());
These three lines of code copy data from yData
and uvData
to Y
, U
and V< respectively. /code> In
std::vector
, ensure that the Y, U, and V components are correctly extracted.
cv::Mat yuvImage(height * 3 / 2, width, CV_8UC1);
A cv::Mat
object yuvImage
is defined here, which will be used to store the entire YUV image, the size is (height * 3 / 2)
Rows and width
columns, pixel type is CV_8UC1
(8-bit unsigned character).
std::memcpy(yuvImage.data, Y.data(), width * height); std::memcpy(yuvImage.data + width * height, U.data(), width * height / 4); std::memcpy(yuvImage.data + width * height * 5 / 4, V.data(), width * height / 4);
These three lines of code use the memcpy
function to copy the data for the Y, U, and V components into a yuvImage
, ensuring that they are merged together in the correct order.
cv::Mat bgrImage; cv::cvtColor(yuvImage, bgrImage, cv::COLOR_YUV2BGR_NV12);
bgrImage
is defined here, used to store images in BGR format. Then, use OpenCV's cvtColor
function to convert the YUV image to BGR format, cv::COLOR_YUV2BGR_NV12
means converting from NV12 YUV format to BGR format.
cv::imshow("YUV420 Image", bgrImage); cv::waitKey(6000); cv::destroyAllWindows();
Finally, these lines of code display the image. imshow
is used to display the image window, waitKey
is used to wait for the key press, and destroyAllWindows
is used to close all windows.
This is a line-by-line explanation of your code. Its main function is to open YUV420 files, extract Y, U, V components, merge them into YUV images, and then convert them to BGR format and display them in the window. If you have any questions or need more explanation, please feel free to ask.
Further encapsulation
ImageConverter.h
// ImageConverter.h #pragma once #include <opencv2/opencv.hpp> class ImageConverter {<!-- --> public: ImageConverter(const std::string & amp; yFile, const std::string & amp; uvFile, int width, int height); ~ImageConverter(); cv::Mat convertToBGR(); void displayBGRImage(); private: std::string yFile_; std::string uvFile_; int width_; int height_; cv::Mat yuvImage_; cv::Mat bgrImage_; }; #pragma once
ImageConverter.cpp
// ImageConverter.cpp #include "ImageConverter.h" #include <fstream> #include <iostream> ImageConverter::ImageConverter(const std::string & amp; yFile, const std::string & amp; uvFile, int width, int height) : yFile_(yFile), uvFile_(uvFile), width_(width), height_(height) {<!-- --> const int frameSize = width * height * 3 / 2; std::ifstream yfile(yFile_, std::ios::binary); std::ifstream uvfile(uvFile_, std::ios::binary); if (!yfile || !uvfile) {<!-- --> std::cerr << "Failed to open YUV files." << std::endl; return; } std::vector<uint8_t> yData(frameSize); yfile.read(reinterpret_cast<char*>(yData.data()), frameSize); std::vector<uint8_t> uvData(frameSize); uvfile.read(reinterpret_cast<char*>(uvData.data()), frameSize); std::vector<uint8_t> Y(width * height); std::vector<uint8_t> U(width * height / 4); std::vector<uint8_t> V(width * height / 4); std::copy_n(yData.begin(), width * height, Y.begin()); std::copy_n(uvData.begin(), width * height / 4, U.begin()); std::copy_n(uvData.begin() + width * height / 4, width * height / 4, V.begin()); yuvImage_ = cv::Mat(height * 3 / 2, width, CV_8UC1); std::memcpy(yuvImage_.data, Y.data(), width * height); std::memcpy(yuvImage_.data + width * height, U.data(), width * height / 4); std::memcpy(yuvImage_.data + width * height * 5 / 4, V.data(), width * height / 4); bgrImage_ = cv::Mat(height, width, CV_8UC3); cv::cvtColor(yuvImage_, bgrImage_, cv::COLOR_YUV2BGR_NV12); } ImageConverter::~ImageConverter() {<!-- --> // You can add code to clean up resources here } cv::Mat ImageConverter::convertToBGR() {<!-- --> return bgrImage_.clone(); } void ImageConverter::displayBGRImage() {<!-- --> cv::imshow("BGR Image", bgrImage_); cv::waitKey(60000); cv::destroyAllWindows(); }
main.cpp
// main.cpp (usage example) #include "ImageConverter.h" int main() {<!-- --> const std::string yFile = "./Y.yuv"; const std::string uvFile = "./UV.yuv"; const int width = 1920; const int height = 1080; ImageConverter converter(yFile, uvFile, width, height); cv::Mat bgrImage = converter.convertToBGR(); //Set the file path to be saved std::string filePath = "./your_image.png"; // Replace with your folder path and file name //Save BGR image bool success = cv::imwrite(filePath, bgrImage); if (success) { std::cout << "The image was successfully saved to the folder:" << filePath << std::endl; } else { std::cerr << "Error saving image." << std::endl; } converter.displayBGRImage(); return 0; }