C++实现YUV420转成BMP

一、使用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));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值