关注

BGRtoNV12与NV12toBGR互转函数

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

#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图像数据写入到执行程序目录下

	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这样写

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

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/kiro_1023/article/details/157650953

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--