摘要:在新项目中,需要为上层应用开放几个接口,但又不想让上层应用过多依赖OpenCV。本文将详细介绍如何使用C++和OpenCV,通过加载图片并转换为NV12格式,实现对图像数据的处理,以及如何加载NV12数据并显示。这些步骤对于在相机等设备中处理YUV数据并与OpenCV进行无缝集成非常有用。
新项目需要开放接口给上层应用使用,而相机直接输出的是YUV数据。为了减少上层应用对OpenCV的依赖,需要在函数内部将YUV数据转化为cv::Mat
格式。这样,上层应用就无需调用OpenCV的任何操作。测试接口需要使用YUV数据,因此我们还需要保存图像数据为YUV格式,并加载YUV数据进行进一步处理。
void convert2NV12(const std::string& imagePath, const std::string& outputFilePath)
{
cv::Mat mat = cv::imread(imagePath);
int cropWidth = mat.cols/2*2;
int cropHeight = mat.rows/2*2;
cv::Mat cropImage = mat(cv::Rect(0 , 0, cropWidth, cropHeight));
std::ofstream ofs;
ofs.open(outputFilePath, std::ios::binary);
// 创建一个YUV图像来存储转换后的数据
cv::Mat yuvImage(cropImage.rows *3/2, cropImage.cols, CV_8UC1, cv::Scalar(0)); // Y分量
cv::Mat uvImage(cropImage.rows *3/ 2, cropImage.cols, CV_8UC1, cv::Scalar(0)); // UV分量 (NV12)
// 进行BGR到YUV颜色空间转换
cv::cvtColor(cropImage, yuvImage, cv::COLOR_BGR2YUV_I420);
memcpy(uvImage.data, yuvImage.data, cropImage.cols*cropImage.rows);
int yLen = cropImage.cols * cropImage.rows;
int uvLen = cropImage.cols*cropImage.rows/4;
// 将UV分量(U和V)从I420格式提取并排列为NV12格式
for (int el = 0; el < uvLen; el++) {
uvImage.data[yLen + 2*el] = yuvImage.data[yLen + el];
uvImage.data[yLen + 2*el + 1] = yuvImage.data[yLen + el + uvLen];
}
if(ofs.is_open()){
// 写入YUV数据到文件
ofs.write(reinterpret_cast(uvImage.data), uvImage.total() * uvImage.elemSize());
// ofs.write(reinterpret_cast(uvImage.data), cropImage.cols*cropImage.rows*3/2);
ofs.flush();
ofs.close();
}
}
在这个步骤中,我们加载一张图片,将其转换为NV12格式,并保存为NV12文件。convertToNV12
函数接受图片文件路径和输出NV12文件路径两个参数。
cv::Mat loadNV12(const std::string& filePath, int width, int height) {
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Error: Unable to open NV12 file " << filePath << std::endl;
return cv::Mat();
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector buffer(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Error: Unable to read NV12 data from file " << filePath << std::endl;
return cv::Mat();
}
cv::Mat nv12Mat(height + height / 2, width, CV_8UC1, buffer.data());
return nv12Mat.clone(); // Clone to ensure data ownership
}
这个函数负责加载NV12数据并将其转化为cv::Mat
格式。接受NV12文件路径、图像宽度和高度三个参数,并返回cv::Mat
格式的NV12数据。
int main() {
int width = 640;
int height = 480;
convertToNV12("input_image.jpg", "output_image.nv12");
cv::Mat nv12Image = loadNV12("output_image.nv12", width, height);
if (!nv12Image.empty()) {
// 此时nv12Image包含了NV12格式的数据,你可以对其进行进一步处理
cv::imshow("NV12 Image", nv12Image);
cv::waitKey(0);
}
return 0;
}
主函数演示了如何调用前述两个函数,将图像转换为NV12格式并加载NV12数据进行显示。你可以根据实际需求进一步处理nv12Image
。
通过这些步骤,你可以轻松地在新项目中处理YUV数据并与OpenCV集成,同时提供简便的接口给上层应用使用。