Emgu.CV 4.8.1中Mat与Image<,>像素存取的最佳实践咨询
在Emgu.CV 4.8.1中选择Image<,>还是Mat进行高效像素操作
核心结论
优先选择Mat,它不仅性能更优,还提供了多种无需重新分配内存的高效像素存取方式,完全能满足你频繁操作单个像素的需求。
Mat的高效像素存取方法
1. 直接操作非托管内存(性能最优)
通过DataPointer获取图像数据的非托管指针,结合Step(行字节数,包含对齐填充)进行像素读写,这种方式跳过托管层封装,性能最高,适合对性能要求极高的场景。需要启用unsafe代码:
// 示例:操作8位单通道灰度图 using (Mat grayMat = new Mat(480, 640, DepthType.Cv8U, 1)) { unsafe { // 获取数据指针 byte* dataPtr = (byte*)grayMat.DataPointer.ToPointer(); // 获取行步长(每行的总字节数,含对齐) int rowStep = grayMat.Step; // 读取坐标(x=100, y=200)的像素 byte pixelValue = dataPtr[200 * rowStep + 100]; // 写入坐标(x=100, y=200)的像素 dataPtr[200 * rowStep + 100] = 255; } }
对于多通道图像(如BGR),只需根据通道数偏移指针即可:
// 示例:操作8位3通道彩色图 using (Mat bgrMat = new Mat(480, 640, DepthType.Cv8U, 3)) { unsafe { byte* dataPtr = (byte*)bgrMat.DataPointer.ToPointer(); int rowStep = bgrMat.Step; int channels = bgrMat.NumberOfChannels; // 读取(x=100, y=200)的BGR分量 byte blue = dataPtr[200 * rowStep + 100 * channels + 0]; byte green = dataPtr[200 * rowStep + 100 * channels + 1]; byte red = dataPtr[200 * rowStep + 100 * channels + 2]; // 写入绿色像素 dataPtr[200 * rowStep + 100 * channels + 1] = 255; } }
2. 使用Mat.At<T>()方法(类型安全,无需unsafe)
如果不想启用unsafe代码,可以用At<T>()方法,它是类型安全的,且直接操作非托管内存,性能略逊于指针操作,但远优于Image<,>.Data[,,]:
// 单通道图像示例 using (Mat grayMat = new Mat(480, 640, DepthType.Cv8U, 1)) { // 读取(y=200, x=100)的像素 byte pixel = grayMat.At<byte>(200, 100); // 写入像素 grayMat.At<byte>(200, 100) = 255; } // 多通道图像示例 using (Mat bgrMat = new Mat(480, 640, DepthType.Cv8U, 3)) { // 读取BGR分量 MCvScalar color = bgrMat.At<MCvScalar>(200, 100); byte blue = color.V0; byte green = color.V1; byte red = color.V2; // 写入绿色 bgrMat.At<MCvScalar>(200, 100) = new MCvScalar(0, 255, 0); }
Image<,> vs Mat:为什么选Mat
Image<,>.Data[,,]本质是托管数组,每次访问都会在托管内存和内部封装的Mat非托管内存之间进行隐式拷贝,频繁操作时会产生明显的性能损耗。- Mat的所有像素操作都是直接对非托管内存进行读写,无额外内存分配或拷贝,性能更稳定高效。
- 对于你提到的重采样、插值、卷积等操作,Emgu.CV提供了基于Mat的原生方法(如
CvInvoke.Resize、CvInvoke.Filter2D),这些方法调用底层OpenCV优化实现,性能远高于手动像素循环,优先使用这些原生方法而非手动操作像素。
内容的提问来源于stack exchange,提问作者Michele Colle
相关产品推荐
相关产品推荐

