BGRtoNV12与NV12toBGR互转函数

rt

主要是用于输入输出图像数据的转换

cpp 复制代码
#include <stdbool.h>
#include <string.h>
#include <thread>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <fstream>
#include <iostream>
#include <opencv2/opencv.hpp>

cv::Mat NV12ToBGRUnified(const unsigned char* nv12_data, int width, int height, int input_channels) {
    if (!nv12_data || width <= 0 || height <= 0) 
	{
        std::cerr << "[mw]Error: Invalid NV12 input data or dimensions.\n";
        return cv::Mat(); // Return empty Mat on error
    }
    if (width % 2 != 0 || height % 2 != 0) 
	{
        std::cerr << "[mw]Error: NV12 input dimensions must be even. width=" << width << ", height=" << height << "\n";
        return cv::Mat();
    }
    if (input_channels != 1 && input_channels != 3) 
	{
        std::cerr << "[mw]Error: input_channels must be 1 (grayscale) or 3 (color). Got " << input_channels << "\n";
        return cv::Mat();
    }

    int y_size = width * height;
    int uv_size = y_size / 2;

    cv::Mat bgr_mat(height, width, CV_8UC3);

    const unsigned char* y_plane = nv12_data;
    const unsigned char* uv_plane = nv12_data + y_size;

    const int SCALE_FACTOR = 1000;
    const int R_V_COEFF = static_cast<int>(1.402 * SCALE_FACTOR);      // ~1402
    const int G_U_COEFF = static_cast<int>(0.344136 * SCALE_FACTOR);  // ~344
    const int G_V_COEFF = static_cast<int>(0.714136 * SCALE_FACTOR);  // ~714
    const int B_U_COEFF = static_cast<int>(1.772 * SCALE_FACTOR);     // ~1772

    for (int y = 0; y < height; ++y) {
        unsigned char* bgr_row = bgr_mat.ptr<unsigned char>(y);
        for (int x = 0; x < width; ++x) {
            // Get Y value for current pixel (Always present)
            unsigned char Y = y_plane[y * width + x];

            unsigned char U = 128; // Default U value
            unsigned char V = 128; // Default V value

            if (input_channels == 3) {
                // Calculate corresponding UV block coordinates (since UV is subsampled 2x2)
                int uv_x = x / 2;
                int uv_y = y / 2;
                int uv_offset = uv_y * (width / 2) + uv_x;

                // Get U and V values for the 2x2 block from the NV12 UV plane
                U = uv_plane[uv_offset * 2];     // U value
                V = uv_plane[uv_offset * 2 + 1]; // V value
            }
            int c = Y - 16;
            int d = U - 128;
            int e = V - 128;

            int scaled_R = (298 * c + R_V_COEFF * e) / SCALE_FACTOR;
            int scaled_G = (298 * c - G_U_COEFF * d - G_V_COEFF * e) / SCALE_FACTOR;
            int scaled_B = (298 * c + B_U_COEFF * d) / SCALE_FACTOR;

            unsigned char R = static_cast<unsigned char>(std::max(0, std::min(255, scaled_R)));
            unsigned char G = static_cast<unsigned char>(std::max(0, std::min(255, scaled_G)));
            unsigned char B = static_cast<unsigned char>(std::max(0, std::min(255, scaled_B)));

            int pixel_idx = x * 3;
            bgr_row[pixel_idx + 0] = B; // B
            bgr_row[pixel_idx + 1] = G; // G
            bgr_row[pixel_idx + 2] = R; // R
        }
    }
    return bgr_mat;
}

std::vector<unsigned char> MatBGRToNV12(const cv::Mat& bgr_mat) 
{ 
    if (bgr_mat.empty() || bgr_mat.channels() != 3 || bgr_mat.type() != CV_8UC3) {
        std::cerr << "[mw]Error: Input Mat is empty, not 3-channel, or not CV_8UC3." << std::endl;
        return {};
    }

    int width = bgr_mat.cols;
    int height = bgr_mat.rows;
    int y_plane_size = width * height;
    int uv_plane_size = width * height / 2;
    int nv12_size = y_plane_size + uv_plane_size;

    std::vector<unsigned char> nv12_buffer(nv12_size);
	log::info("[mw]step1");
    // 将 BGR 转换为 全尺寸 YUV (YUV444P-like intermediate)
    std::vector<std::vector<unsigned char>> y_full(height, std::vector<unsigned char>(width));
    std::vector<std::vector<unsigned char>> u_full(height, std::vector<unsigned char>(width));
    std::vector<std::vector<unsigned char>> v_full(height, std::vector<unsigned char>(width));

    for (int y = 0; y < height; ++y) {
        const unsigned char* bgr_row = bgr_mat.ptr<unsigned char>(y);
        for (int x = 0; x < width; ++x) {
            // Get BGR pixel values
            // Remember: OpenCV Mat stores BGR as [B, G, R]
            unsigned char b = bgr_row[x * 3 + 0];
            unsigned char g = bgr_row[x * 3 + 1];
            unsigned char r = bgr_row[x * 3 + 2];

            // Apply YUV conversion formulas
            double Y_val =  0.299 * r + 0.587 * g + 0.114 * b;
            double U_val = -0.147 * r - 0.289 * g + 0.436 * b + 128.0; // Add 128 offset
            double V_val =  0.615 * r - 0.515 * g - 0.100 * b + 128.0; // Add 128 offset

            // Clamp values to [0, 255] and store as unsigned char
            y_full[y][x] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, Y_val)));
            u_full[y][x] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, U_val)));
            v_full[y][x] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, V_val)));
        }
    }
	log::info("[mw]step2");
    // --- Copy Y plane (no downsampling needed) ---
    for (int y = 0; y < height; ++y) {
        unsigned char* dst_y_row = nv12_buffer.data() + y * width;
        for (int x = 0; x < width; ++x) {
            dst_y_row[x] = y_full[y][x];
        }
    }
	log::info("[mw]step3");
    // --- Downsample U/V planes (2x2 average) and interleave into NV12 UV plane ---
    unsigned char* dst_uv_ptr = nv12_buffer.data() + y_plane_size;
    for (int vy = 0; vy < height / 2; ++vy) {
        for (int vx = 0; vx < width / 2; ++vx) {
            int src_y_start = vy * 2;
            int src_x_start = vx * 2;

            // Average 2x2 block for U and V
            int u_sum = 0, v_sum = 0;
            for (int dy = 0; dy < 2; ++dy) {
                for (int dx = 0; dx < 2; ++dx) {
                    int src_y = src_y_start + dy;
                    int src_x = src_x_start + dx;
                    u_sum += u_full[src_y][src_x];
                    v_sum += v_full[src_y][src_x];
                }
            }
            unsigned char avg_u = static_cast<unsigned char>((u_sum + 2) / 4); // Add 2 for rounding
            unsigned char avg_v = static_cast<unsigned char>((v_sum + 2) / 4);

            int uv_index = vy * (width / 2) + vx;
            dst_uv_ptr[uv_index * 2]     = avg_u; // U
            dst_uv_ptr[uv_index * 2 + 1] = avg_v; // V
        }
    }
	log::info("[mw]step4");
    return nv12_buffer;
}

输出的nv12图像数据写入到执行程序目录下

cpp 复制代码
	std::string df_name = "./df_pic_nv12.yuv";
	std::ofstream out_file(df_name, std::ios::binary);
	if (out_file.is_open()) 
	{
		out_file.write(reinterpret_cast<const char*>(dst_img_data_nv12.get()), nv12_size);
		out_file.close();
		log::info("Saved defogged NV12 data to %s", df_name.c_str());
	} 
	else {
		log::warn("Failed to open file for writing NV12 data.");
	}

单纯是为了绕过cvtColor函数,如果遇到自己编的库和对方跑的库opencv版本不同,可以用这种方式转换。

我main这样写

cpp 复制代码
int main(int argc, char* argv[])
{
	if (argc < 2)
    {
        std::cout << "./test_client_demo inputpath Level" << std::endl;
        return -1;
    }
	log::init();
    log::debug_enable(true);
    int Level = 1;
    if (argc >= 3)
    {
        Level = atoi(argv[2]);
    }
    cv::Mat loaded_img = cv::imread(argv[1], cv::IMREAD_COLOR);
    if (loaded_img.empty())
    {
        std::cout << "img read failed!!" << std::endl;
        return -1;
    }
	api_config_t client_config;
	int ret = 0;
	client_config.com_type = COM_ETHNET;
	strcpy(client_config.config_enet.ip_addr, "127.0.0.1");
	client_config.config_enet.port = 7887;
	system_init(client_config);
	system_sync_time();
	auto retframe = 1;
	std::vector<unsigned char> nv12_vector = MatBGRToNV12(loaded_img);
	const uint8_t* nv12_src_ptr = nv12_vector.data();
	size_t nv12_size = nv12_vector.size();
	std::unique_ptr<uint8_t[]> dst_img_data_nv12 = std::make_unique<uint8_t[]>(nv12_size);
	std::unique_ptr<uint8_t[]> dst_img_data_nv12c = std::make_unique<uint8_t[]>(nv12_size);
	log::debug("input size: w = %d ,h = %d ,size = %d", loaded_img.cols, loaded_img.rows,nv12_size);
	log::debug("Buffer Info: src_ptr=%p, dst_ptr=%p", nv12_src_ptr, dst_img_data_nv12.get());

	log::debug("nv12_src_ptr address: %p, aligned: %s", nv12_src_ptr, ((uintptr_t)nv12_src_ptr % 16 == 0) ? "Yes" : "No");
	log::debug("dst_img_data_nv12 address: %p, aligned: %s", dst_img_data_nv12.get(), ((uintptr_t)dst_img_data_nv12.get() % 16 == 0) ? "Yes" : "No");
	





		std::string df_name = "./second_see_df_pic_nv12.yuv";
		std::ofstream out_file(df_name, std::ios::binary);
		if (out_file.is_open()) 
		{
			out_file.write(reinterpret_cast<const char*>(dst_img_data_nv12.get()), nv12_size);
			out_file.close();
			log::info("Saved defogged NV12 data to %s", df_name.c_str());
		} else {
			log::warn("Failed to open file for writing NV12 data.");
		}
	
	log::info("goodbye");
	system_deinit();
  log::deinit();
	return 0;
}
相关推荐
千匠网络1 小时前
破局出海壁垒,千匠网络新能源汽车跨境出海解决方案
人工智能
马丁聊GEO3 小时前
解码AI用户心智,筑牢可信GEO根基——悠易科技深度参与《中国AI用户态度与行为研究报告(2026)》发布会
人工智能·科技
nap-joker3 小时前
Fusion - Mamba用于跨模态目标检测
人工智能·目标检测·计算机视觉·fusion-mamba·可见光-红外成像融合·远距离/伪目标问题
一只幸运猫.3 小时前
2026Java 后端面试完整版|八股简答 + AI 大模型集成技术(最新趋势)
人工智能·面试·职场和发展
Promise微笑3 小时前
2026年国产替代油介损测试仪:油介损全场景解决方案与技术演进
大数据·网络·人工智能
深海鱼在掘金4 小时前
深入浅出 LangChain —— 第三章:模型抽象层
人工智能·langchain·agent
生信碱移4 小时前
PACells:这个方法可以鉴定疾病/预后相关的重要细胞亚群,作者提供的代码流程可以学习起来了,甚至兼容转录组与 ATAC 两种数据类型!
人工智能·学习·算法·机器学习·数据挖掘·数据分析·r语言
workflower4 小时前
具身智能行业应用-生活服务业
大数据·人工智能·机器人·动态规划·生活
GitCode官方4 小时前
基于昇腾 MindSpeed LLM 玩转 DeepSeekV4-Flash 模型的预训练复现部署
人工智能·开源·atomgit
大刘讲IT4 小时前
AI重塑企业信息价值标准:从“系统供给”到“用户定义”的企业数字化新范式
人工智能·经验分享·ai·制造