yuv420 and converted to bgr

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;
}


syntaxbug.com © 2021 All Rights Reserved.