ArCore+Android NDK+OpenCV:AImage转cv::Mat及帧正方形识别咨询
没问题!这两个需求都完全可以实现,我在做ArCore NDK开发的时候刚好处理过类似的场景,给你详细说下具体的做法:
从AImage创建cv::Mat的可行方案
ArCore输出的ArImage转成AImage后,绝大多数场景下是YUV_420_888格式(你可以用AImage_getFormat()确认格式),下面针对这种主流格式给出两种实现思路:
一、直接基于AImage创建可用的cv::Mat
我们可以先拆分AImage的Y/U/V三个平面,分别封装成cv::Mat,再按需转成RGB格式(更适合正方形识别这类视觉算法)。
步骤1:获取AImage的基础信息与平面数据
// 假设你已经拿到了有效的AImage* aImage int32_t format = AImage_getFormat(aImage); assert(format == AIMAGE_FORMAT_YUV_420_888); // 确保是预期的图像格式 int32_t imgWidth = AImage_getWidth(aImage); int32_t imgHeight = AImage_getHeight(aImage); // 获取Y/U/V三个平面的指针与行步长(关键:必须用系统返回的步长,不能直接用图像宽度) const uint8_t* yPlaneData = AImage_getPlaneData(aImage, 0); int32_t yRowStride = AImage_getPlaneRowStride(aImage, 0); const uint8_t* uPlaneData = AImage_getPlaneData(aImage, 1); int32_t uRowStride = AImage_getPlaneRowStride(aImage, 1); const uint8_t* vPlaneData = AImage_getPlaneData(aImage, 2); int32_t vRowStride = AImage_getPlaneRowStride(aImage, 2);
步骤2:创建YUV格式的cv::Mat
Y平面是全尺寸单通道,U/V平面是原图像尺寸的一半:
// 创建Y平面Mat(单通道8位灰度图) cv::Mat yMat(imgHeight, imgWidth, CV_8UC1, (void*)yPlaneData, yRowStride); // 创建U/V平面Mat(尺寸为原图像的1/2) cv::Mat uMat(imgHeight / 2, imgWidth / 2, CV_8UC1, (void*)uPlaneData, uRowStride); cv::Mat vMat(imgHeight / 2, imgWidth / 2, CV_8UC1, (void*)vPlaneData, vRowStride);
步骤3:转成RGB格式(可选,推荐用于正方形识别)
如果你的识别算法基于RGB图像,可以把YUV转成OpenCV默认的BGR格式:
// 合并Y/U/V平面为完整YUV Mat std::vector<cv::Mat> yuvPlanes = {yMat, uMat, vMat}; cv::Mat yuvMat; cv::merge(yuvPlanes, yuvMat); // 转成BGR格式 cv::Mat bgrMat; cv::cvtColor(yuvMat, bgrMat, cv::COLOR_YUV2BGR_I420);
二、从AImage平面数据创建cv::Mat的有效性
当然有效!其实上面的流程已经用到了AImage_getPlaneData()来创建Mat,只要注意以下细节,就能保证创建的Mat是完全可用的:
- 不要提前释放原图像资源:我们创建的cv::Mat是直接引用AImage的内存(未拷贝数据),如果在Mat使用完毕前调用
AImage_delete()或ArImage_release(),会导致Mat内存失效引发崩溃,一定要在处理完Mat后再释放原图像。 - 必须使用行步长(Row Stride):系统为了内存对齐,会在每行像素末尾填充额外字节,直接用图像宽度作为步长会导致图像错乱,必须用
AImage_getPlaneRowStride()返回的值。 - 特殊情况处理:少数设备的U/V平面是交错存储的(pixel stride为2),这时候可以先创建双通道Mat再拆分:
cv::Mat uvMat(imgHeight / 2, imgWidth / 2, CV_8UC2, (void*)uPlaneData, uRowStride); cv::Mat uMat, vMat; cv::split(uvMat, uMat, vMat);
额外提醒
如果需要长期保留图像数据,建议调用cv::Mat::clone()拷贝一份独立数据,避免原图像释放后Mat失效;也可以临时将Mat保存为文件,验证图像是否正确创建。
内容的提问来源于stack exchange,提问作者Sinigr
相关产品推荐
相关产品推荐

