一、使用OpenCV库
在C++中,将YUV420数据转换为BMP图片需要一些图像处理的步骤。以下是一个简单的示例代码,使用了C++和OpenCV库来完成这个任务。在运行之前,请确保已经安装了OpenCV库:
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
void YUV420toBGR(const unsigned char *yuv, int width, int height, cv::Mat &output) {
cv::Mat yuvMat(height + height / 2, width, CV_8UC1, (void*)yuv);
cv::cvtColor(yuvMat, output, cv::COLOR_YUV2BGR_I420);
}
int main() {
// 读取YUV文件
std::ifstream file("input.yuv", std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Error opening YUV file." << std::endl;
return -1;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Error reading YUV file." << std::endl;
return -1;
}
file.close();
// 定义图像大小
int width = 640;
int height = 480;
// 将YUV数据转换为BGR图像
cv::Mat bgrImage;
YUV420toBGR(reinterpret_cast<unsigned char*>(buffer.data()), width, height, bgrImage);
// 保存BGR图像为BMP文件
cv::imwrite("output.bmp", bgrImage);
std::cout << "Conversion completed successfully." << std::endl;
return 0;
}
在这个例子中,假设YUV数据的格式是I420。如果你的YUV数据格式不同,你可能需要调整cv::COLOR_YUV2BGR_I420
这一行的参数。此外,请确保你的YUV文件的分辨率与代码中定义的分辨率一致。
二、不使用OpenCV库
如果不想使用OpenCV,可以直接处理YUV420数据并将其转换为BMP格式。以下是一个基本的示例,使用C++和标准库来完成这个任务:
#include <iostream>
#include <fstream>
#include <cstdint>
#include <vector>
// YUV to RGB conversion function
void YUV420toRGB(const unsigned char *yuv, int width, int height, std::vector<unsigned char> &rgb) {
int size = width * height;
int uvSize = size / 4;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int y = yuv[i * width + j];
int u = yuv[size + (i / 2) * (width / 2) + (j / 2)];
int v = yuv[size + uvSize + (i / 2) * (width / 2) + (j / 2)];
int c = y - 16;
int d = u - 128;
int e = v - 128;
int r = (298 * c + 409 * e + 128) >> 8;
int g = (298 * c - 100 * d - 208 * e + 128) >> 8;
int b = (298 * c + 516 * d + 128) >> 8;
r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
rgb.push_back(static_cast<unsigned char>(b));
rgb.push_back(static_cast<unsigned char>(g));
rgb.push_back(static_cast<unsigned char>(r));
}
}
}
// BMP file header structure
#pragma pack(push, 1)
struct BMPHeader {
uint16_t signature;
uint32_t fileSize;
uint16_t reserved1;
uint16_t reserved2;
uint32_t dataOffset;
uint32_t headerSize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t imageSize;
int32_t xPixelsPerMeter;
int32_t yPixelsPerMeter;
uint32_t totalColors;
uint32_t importantColors;
};
#pragma pack(pop)
int main() {
// 读取YUV文件
std::ifstream file("input.yuv", std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Error opening YUV file." << std::endl;
return -1;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Error reading YUV file." << std::endl;
return -1;
}
file.close();
// 定义图像大小
int width = 640;
int height = 480;
// 将YUV数据转换为RGB
std::vector<unsigned char> rgbImage;
YUV420toRGB(reinterpret_cast<unsigned char*>(buffer.data()), width, height, rgbImage);
// 创建BMP文件头
BMPHeader bmpHeader;
bmpHeader.signature = 0x4D42; // BM
bmpHeader.fileSize = sizeof(BMPHeader) + rgbImage.size();
bmpHeader.reserved1 = 0;
bmpHeader.reserved2 = 0;
bmpHeader.dataOffset = sizeof(BMPHeader);
bmpHeader.headerSize = 40;
bmpHeader.width = width;
bmpHeader.height = height;
bmpHeader.planes = 1;
bmpHeader.bitsPerPixel = 24;
bmpHeader.compression = 0;
bmpHeader.imageSize = 0;
bmpHeader.xPixelsPerMeter = 0;
bmpHeader.yPixelsPerMeter = 0;
bmpHeader.totalColors = 0;
bmpHeader.importantColors = 0;
// 写入BMP文件
std::ofstream bmpFile("output.bmp", std::ios::binary);
bmpFile.write(reinterpret_cast<char*>(&bmpHeader), sizeof(BMPHeader));
bmpFile.write(reinterpret_cast<char*>(rgbImage.data()), rgbImage.size());
bmpFile.close();
std::cout << "Conversion completed successfully." << std::endl;
return 0;
}
如果转换出来的图片颜色不正确,可能是因为 YUV420 到 RGB 的转换公式不准确或者颜色通道的顺序不正确。修改代码如下:
void YUV420toRGB(const unsigned char* yuv, int width, int height, std::vector<unsigned char>& rgb) {
int size = width * height;
int uvSize = size / 4;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int y = yuv[i * width + j];
int u = yuv[size + (i / 2) * (width / 2) + (j / 2)];
int v = yuv[size + uvSize + (i / 2) * (width / 2) + (j / 2)];
// YUV to RGB conversion
int c = y - 16;
int d = u - 128;
int e = v - 128;
int r = (298 * c + 409 * e + 128) >> 8;
int g = (298 * c - 100 * d - 208 * e + 128) >> 8;
int b = (298 * c + 516 * d + 128) >> 8;
r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
rgb.push_back(static_cast<unsigned char>(r));
rgb.push_back(static_cast<unsigned char>(g));
rgb.push_back(static_cast<unsigned char>(b));
}
}
}
如果图像是颠倒的,可能是由于 BMP 文件的行顺序与 YUV 数据的行顺序不匹配。在 BMP 文件中,图像的行通常是从底部到顶部排列的,而 YUV 数据的行可能是从顶部到底部排列的。因此,在写入 BMP 文件时,你需要按照适当的顺序写入行。修改代码如下:
// 写入BMP文件
std::ofstream bmpFile("output.bmp", std::ios::binary);
bmpFile.write(reinterpret_cast<char*>(&bmpHeader), sizeof(BMPHeader));
// 写入图像数据(按适当的行顺序)
for (int i = height - 1; i >= 0; i--) {
bmpFile.write(reinterpret_cast<char*>(&rgbImage[i * width * 3]), width * 3);
}
bmpFile.close();
三、非YUV420
上述的YUV到BMP的转换代码是为YUV420格式设计的。在YUV420格式中,图像的亮度(Y分量)是全分辨率的,而色度(U和V分量)是以1/2的水平和垂直分辨率存储的。如果YUV数据格式不是YUV420,而是其他的YUV格式(比如YUV422、YUV444等),需要相应地修改颜色分量的提取和转换公式。以下是对于YUV422格式的简单修改,假设颜色分量的存储顺序为YUYV:
void YUV422toRGB(const unsigned char* yuv, int width, int height, std::vector<unsigned char>& rgb) {
int size = width * height * 2;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j += 2) {
int y1 = yuv[i * width * 2 + j * 2];
int u = yuv[i * width * 2 + j * 2 + 1];
int y2 = yuv[i * width * 2 + j * 2 + 2];
int v = yuv[i * width * 2 + j * 2 + 3];
// YUV to RGB conversion for the first pixel
// (similarly, you can convert the second pixel)
int c1 = y1 - 16;
int d1 = u - 128;
int e1 = v - 128;
int r1 = (298 * c1 + 409 * e1 + 128) >> 8;
int g1 = (298 * c1 - 100 * d1 - 208 * e1 + 128) >> 8;
int b1 = (298 * c1 + 516 * d1 + 128) >> 8;
r1 = std::min(255, std::max(0, r1));
g1 = std::min(255, std::max(0, g1));
b1 = std::min(255, std::max(0, b1));
// YUV to RGB conversion for the second pixel
// (similarly, you can convert the first pixel)
int c2 = y2 - 16;
int d2 = u - 128;
int e2 = v - 128;
int r2 = (298 * c2 + 409 * e2 + 128) >> 8;
int g2 = (298 * c2 - 100 * d2 - 208 * e2 + 128) >> 8;
int b2 = (298 * c2 + 516 * d2 + 128) >> 8;
r2 = std::min(255, std::max(0, r2));
g2 = std::min(255, std::max(0, g2));
b2 = std::min(255, std::max(0, b2));
// Append the RGB values to the vector
rgb.push_back(static_cast<unsigned char>(b1));
rgb.push_back(static_cast<unsigned char>(g1));
rgb.push_back(static_cast<unsigned char>(r1));
rgb.push_back(static_cast<unsigned char>(b2));
rgb.push_back(static_cast<unsigned char>(g2));
rgb.push_back(static_cast<unsigned char>(r2));
}
}
}