#include
#include
#include
int main() {
const std::string yFile = "./Y.yuv";//自己替换
const std::string uvFile = "./UV.yuv";//自己替换
const int width = 1920;//自己替换
const int height = 1080;//自己替换
// 计算帧大小
const int frameSize = width * height * 3 / 2; // YUV420 图像每帧占用的字节数
// 打开 YUV420 图片文件
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;
}
// 读取 YUV 数据
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);
// 提取 Y、U、V 分量
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);
// 显示图像
cv::imshow("YUV420 Image", bgrImage);
cv::waitKey(6000);
cv::destroyAllWindows();
return 0;
}
当然,我会逐行解释你的代码:
#include
#include
#include
这些是 C++ 头文件引入部分。
用于标准输入和输出,
用于文件操作,
是 OpenCV 库的头文件,允许你使用 OpenCV 功能。
int main() {
这是 main
函数的开始。
const std::string yFile = "./Y.yuv";
const std::string uvFile = "./UV.yuv";
这里定义了两个字符串变量 yFile
和 uvFile
,用于存储 Y 和 UV 分量的文件路径。
const int width = 1920;
const int height = 1080;
这里定义了图像的宽度和高度。
const int frameSize = width * height * 3 / 2;
这是计算每帧图像的大小,因为 YUV420 图像中,Y 分量占据一帧的大小,而 U 和 V 分量各占据一帧大小的四分之一。这是根据 YUV420 格式的存储方式计算的。
std::ifstream yfile(yFile, std::ios::binary);
std::ifstream uvfile(uvFile, std::ios::binary);
这里打开了 Y 和 UV 分量的文件,使用了 ifstream
流,打开方式是二进制方式打开。
if (!yfile) {
std::cerr << "Failed to open Y file." << std::endl;
return 1;
}
这个条件语句检查是否成功打开了 Y 分量的文件,如果失败,则输出错误信息并返回 1,表示程序出错。
std::vector<uint8_t> yData(frameSize);
std::vector<uint8_t> uvData(frameSize);
这里定义了两个 std::vector
,分别用于存储 Y 和 UV 分量的数据。它们的大小是一个帧的大小。
yfile.read(reinterpret_cast<char*>(yData.data()), frameSize);
uvfile.read(reinterpret_cast<char*>(uvData.data()), frameSize);
这里使用 read
函数从文件中读取 Y 和 UV 数据,注意通过 reinterpret_cast
进行了类型转换,因为文件操作需要 char*
类型。
std::vector<uint8_t> Y(width * height);
std::vector<uint8_t> U(width * height / 4);
std::vector<uint8_t> V(width * height / 4);
这里定义了三个 std::vector
,分别用于存储提取出的 Y、U 和 V 分量数据。
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());
这三行代码分别从 yData
和 uvData
中复制数据到 Y
、U
和 V
的 std::vector
中,确保正确提取了 Y、U、V 分量。
cv::Mat yuvImage(height * 3 / 2, width, CV_8UC1);
这里定义了一个 cv::Mat
对象 yuvImage
,它将用于存储整个 YUV 图像,大小是 (height * 3 / 2)
行和 width
列,像素类型为 CV_8UC1
(8 位无符号字符)。
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);
这三行代码使用 memcpy
函数将 Y、U、V 分量的数据复制到 yuvImage
中,确保它们按正确的顺序合并在一起。
cv::Mat bgrImage;
cv::cvtColor(yuvImage, bgrImage, cv::COLOR_YUV2BGR_NV12);
这里定义了 bgrImage
,用于存储 BGR 格式的图像。然后,使用 OpenCV 的 cvtColor
函数将 YUV 图像转换为 BGR 格式,cv::COLOR_YUV2BGR_NV12
表示从 NV12 YUV 格式转换到 BGR 格式。
cv::imshow("YUV420 Image", bgrImage);
cv::waitKey(6000);
cv::destroyAllWindows();
最后,这几行代码显示图像。imshow
用于显示图像窗口,waitKey
用于等待按键,destroyAllWindows
用于关闭所有窗口。
这就是你代码的逐行解释。它的主要功能是打开 YUV420 文件,提取 Y、U、V 分量,将它们合并成 YUV 图像,然后将其转换为 BGR 格式并在窗口中显示。如果你有任何问题或需要更多解释,请随时提问。
// ImageConverter.h
#pragma once
#include
class ImageConverter {
public:
ImageConverter(const std::string& yFile, const std::string& 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
#include "ImageConverter.h"
#include
#include
ImageConverter::ImageConverter(const std::string& yFile, const std::string& 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() {
// 可以在这里添加清理资源的代码
}
cv::Mat ImageConverter::convertToBGR() {
return bgrImage_.clone();
}
void ImageConverter::displayBGRImage() {
cv::imshow("BGR Image", bgrImage_);
cv::waitKey(60000);
cv::destroyAllWindows();
}
// main.cpp (用法示例)
#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();
// 设置要保存的文件路径
std::string filePath = "./your_image.png"; // 替换为你的文件夹路径和文件名
// 保存BGR图像
bool success = cv::imwrite(filePath, bgrImage);
if (success) {
std::cout << "图像已成功保存到文件夹:" << filePath << std::endl;
}
else {
std::cerr << "保存图像时出错." << std::endl;
}
converter.displayBGRImage();
return 0;
}