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