获取和修改Emgu.CV.Mat的元素值

通过掩膜操作Mat元素

  • 创建Mat变量
using Emgu.CV;
using Emgu.CV.Structure;

Mat B = new Mat(3, 1, Emgu.CV.CvEnum.DepthType.Cv64F, 1);
  • 创建同样大小的掩膜

Matrix作为Emgu3.0版本后取代Mat的类,它具有很多实用的矩阵方法,包括获取和设置矩阵元素值。

//
// 摘要:
//     Get or Set the data for this matrix
public TDepth[,] Data { get; set; }

可以用Matrix创建掩膜

 Matrix<byte> mask = new Matrix<byte>(3, 1);
 mask.Data[0, 0] = 1;
  • 设置B(0,0)、B(1,0)和B(2,0)元素
B.SetTo(new MCvScalar(4), mask);
mask = new Matrix<byte>(3, 1);
mask.Data[1, 0] = 1;
B.SetTo(new MCvScalar(8), mask);
mask = new Matrix<byte>(3, 1);
mask.Data[2, 0] = 1;
B.SetTo(new MCvScalar(32), mask);

其实这里只是通过掩膜设置了B的元素值,我还没有找到直接读取元素值的方法,但是通过观察B的结构,我发现它的数据以byte[]方式保存,而且Mat类有一个获取数据首地址的方法,所以可以考虑操作内存修改/获取Mat元素值。

通过内存拷贝方式操作Mat元素值

  • 首先需要引入命名空间
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.CvEnum;
  • Mat拓展类
namespace _01_MatExtension
{
    /// <summary>
    /// 通过指针操作内存,修改or获取Mat元素值
    /// </summary>
    public static class MatExtension
    {
        /// <summary>
        /// 获取Mat的元素值
        /// </summary>
        /// <param name="mat">需操作的Mat</param>
        /// <param name="row">元素行</param>
        /// <param name="col">元素列</param>
        /// <returns></returns>
        public static dynamic GetValue(this Mat mat, int row, int col)
        {
            var value = CreateElement(mat.Depth);
            Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
            return value[0];
        }
        /// <summary>
        /// 修改Mat的元素值
        /// </summary>
        /// <param name="mat">需操作的Mat</param>
        /// <param name="row">元素行</param>
        /// <param name="col">元素列</param>
        /// <param name="value">修改值</param>
        public static void SetValue(this Mat mat, int row, int col, dynamic value)
        {
            var target = CreateElement(mat.Depth, value);
            Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
        }
        /// <summary>
        /// 根据Mat的类型,动态解析传入数据的类型
        /// </summary>
        /// <param name="depthType"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static dynamic CreateElement(DepthType depthType, dynamic value)
        {
            var element = CreateElement(depthType);
            element[0] = value;
            return element;
        }
        /// <summary>
        /// 获取Mat元素的类型
        /// </summary>
        /// <param name="depthType"></param>
        /// <returns></returns>
        private static dynamic CreateElement(DepthType depthType)
        {
            if (depthType == DepthType.Cv8S)
            {
                return new sbyte[1];
            }
            if (depthType == DepthType.Cv8U)
            {
                return new byte[1];
            }
            if (depthType == DepthType.Cv16S)
            {
                return new short[1];
            }
            if (depthType == DepthType.Cv16U)
            {
                return new ushort[1];
            }
            if (depthType == DepthType.Cv32S)
            {
                return new int[1];
            }
            if (depthType == DepthType.Cv32F)
            {
                return new float[1];
            }
            if (depthType == DepthType.Cv64F)
            {
                return new double[1];
            }
            return new float[1];
        }
    }
}
  • demo
static void Main(string[] args)
        {
           
            Mat m = new Mat(3,1,Emgu.CV.CvEnum.DepthType.Cv64F,1);
            MatExtension.SetValue(m, 0, 0, 3.14);
            MatExtension.SetValue(m, 1, 0, 2.14);
            MatExtension.SetValue(m, 2, 0, 1.14);

            Console.WriteLine(MatExtension.GetValue(m,0,0));
            Console.WriteLine(MatExtension.GetValue(m,1,0));
            Console.WriteLine(MatExtension.GetValue(m,2,0));

            Console.ReadKey();
        }
  • 运行结果

 

<think>我们正在处理一个关于在C#中将Emgu.CV.Mat转换为OpenCvSharp.Mat的问题。根据提供的引用,我们可以总结以下关键点: 1. 引用[1]提到OpenCvSharp在处理Mat时,将C++中的Mat指针传递到C#,并在C#中重新定义Mat类型,将指针作为成员变量。 2. 引用[2]提到Emgucv传递Image类参数给OpenCVMat类,但具体转换方法未明说。 3. 引用[4]提到EmguCV中Image类的Data属性是图像数据的指针,并且由于内存对齐,实际分配的内存可能大于图像数据本身。 因此,我们可以推断,Emgu.CV.MatOpenCvSharp.Mat都是对底层OpenCV Mat的封装,它们内部都持有一个指向OpenCV C++ Mat对象的指针。 转换的关键在于获取Emgu.CV.Mat中的指针,然后用这个指针来初始化OpenCvSharp.Mat。 步骤: 1.Emgu.CV.Mat获取指向C++ Mat的指针(IntPtr类型)。 2. 使用这个指针创建OpenCvSharp.Mat对象。 但是,我们需要注意,Emgu.CVOpenCvSharp是两个不同的封装库,它们的Mat类定义可能不同,但底层都是OpenCVMat。因此,我们可以尝试通过指针来传递。 根据OpenCvSharp的文档,OpenCvSharp.Mat有一个构造函数可以接受一个指针(IntPtr)来创建Mat对象。同样,Emgu.CV.Mat也应该有办法获取其内部的指针。 具体步骤: 1.Emgu.CV中,Mat类有一个属性`Ptr`(或者`MatPtr`)用来获取内部指针。但是,我们需要查阅Emgu.CV的文档或源码确认。 - 引用[4]提到EmguCV中Image类的Data属性是指针,但我们现在用的是Mat类。在Emgu.CV中,Mat类应该也有类似的方法。 实际上,在Emgu.CV中,Mat类有一个`Ptr`属性(类型为`IntPtr`),它表示指向内部cv::Mat的指针。我们可以使用这个属性。 2. 在OpenCvSharp中,Mat类有一个构造函数:`public Mat(IntPtr ptr)`,它接受一个指针来创建Mat。 因此,转换代码可能如下: ```csharp // 假设我们有一个Emgu.CV.Mat对象 emguMat Emgu.CV.Mat emguMat = ...; // 获取Emgu.CV.Mat内部的指针 IntPtr emguMatPtr = emguMat.Ptr; // 使用该指针创建OpenCvSharp.Mat OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(emguMatPtr); ``` 然而,这里有一个重要注意事项:当我们将Emgu.CV.Mat的指针传递给OpenCvSharp.Mat时,这个指针指向的内存是由Emgu.CV管理的。因此,我们必须确保Emgu.CV.Mat对象在OpenCvSharp.Mat使用期间不会被释放,否则会出现访问已释放内存的错误。 为了避免这个问题,我们可以考虑增加引用计数或者克隆数据。但是,直接使用指针创建OpenCvSharp.Mat并不会增加底层Mat的引用计数。因此,我们需要谨慎处理对象的生命周期。 另一种方法是克隆数据,这样两个Mat对象就拥有各自独立的数据,但这样会有性能开销。如果不需要共享数据,克隆是最安全的方式。 使用克隆的转换方法: ```csharp // 将Emgu.CV.Mat转换为OpenCvSharp.Mat(通过复制数据) OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(emguMat.Rows, emguMat.Cols, emguMat.Depth, emguMat.Channels); emguMat.CopyTo(ocvMat); // 但是Emgu.CV.MatOpenCvSharp.Mat没有直接的CopyTo方法,因为它们属于不同的库。 ``` 因此,直接使用指针是更高效的方式,但需要管理好生命周期。 总结: 1. 高效方式(共享内存,注意生命周期): ```csharp IntPtr emguMatPtr = emguMat.Ptr; OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(emguMatPtr); // 注意:确保emguMat在ocvMat使用期间不被释放 ``` 2. 安全方式(复制数据,避免生命周期问题): ```csharp // 首先将Emgu.CV.Mat转换为字节数组,然后用字节数组构建OpenCvSharp.Mat byte[] data = emguMat.ToBytes(); OpenCvSharp.Mat ocvMat = OpenCvSharp.Cv2.ImDecode(data, OpenCvSharp.ImreadModes.Unchanged); // 或者根据具体情况,如果知道图像尺寸类型,可以用Mat的构造函数 // 但是这种方式可能会损失一些信息(如步长),且效率较低。 ``` 但是,第二种方法只适用于图像(因为是ImDecode),对于普通的矩阵数据可能不适用。 对于矩阵数据,另一种复制数据的方法: ```csharp // 获取Emgu.CV.Mat的数据指针,然后复制到OpenCvSharp.Mat IntPtr dataPtr = emguMat.DataPointer; int step = (int)emguMat.Step; OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(emguMat.Rows, emguMat.Cols, OpenCvSharp.MatType.CV_8UC(emguMat.NumberOfChannels), dataPtr, step); ``` 但是,这样创建的OpenCvSharp.Mat仍然共享同一块内存,因为直接使用了dataPtr。如果想要深度复制,可以再调用`Clone()`方法: ```csharp OpenCvSharp.Mat ocvMatCloned = ocvMat.Clone(); ``` 或者,我们可以创建一个新的Mat,然后使用`SetArray`方法复制数据,但这样需要将整个数据块复制到托管数组,然后再复制回去,效率较低。 因此,如果对性能要求高,且能保证生命周期,使用指针直接转换;如果不能保证,则使用深度复制(例如,通过ToBytes或者手动复制数据)的方式。 另外,根据引用[3][4]的内容,我们还需要注意通道数内存对齐问题。在复制数据时,要确保两个库对图像数据的解释是一致的。 结论: 推荐使用指针传递的方式,但必须注意确保Emgu.CV.Mat对象在OpenCvSharp.Mat使用期间保持有效。如果无法保证,则应该使用深度复制。 代码示例(指针方式): ```csharp using Emgu.CV; using OpenCvSharp; // 假设有一个Emgu.CV.Mat实例 Mat emguMat = ...; // Emgu.CV.Mat // 获取指针 IntPtr ptr = emguMat.Ptr; // 创建OpenCvSharp.Mat OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(ptr); // 注意:此时ocvMatemguMat共享同一块内存,对ocvMat修改会影响emguMat,反之亦然。 // 同时,emguMat不能先于ocvMat被释放。 // 如果之后不再需要emguMat,而希望ocvMat独立管理内存,可以克隆: OpenCvSharp.Mat ocvMatCloned = ocvMat.Clone(); ``` 注意:上述代码中,使用`emguMat.Ptr`可能需要确认Emgu.CV的版本。在Emgu.CV 3.x4.x中,Mat类确实有`Ptr`属性(只读)返回IntPtr。 另外,OpenCvSharp的Mat构造函数`public Mat(IntPtr ptr)`在OpenCvSharp 4.x中也是存在的。 因此,这种方法在两个库的最新版本中应该是可行的。 最后,如果遇到问题,还可以考虑通过中间格式(如字节数组)进行转换,但这样会有额外开销。 </think>基于引用内容,以下是 C# 中将 `Emgu.CV.Mat` 转换为 `OpenCvSharp.Mat` 的两种核心方法及注意事项: ### 方法 1:通过指针直接转换(高效) ```csharp // 获取Emgu.CV.Mat的底层指针 IntPtr emguMatPtr = emguMat.Ptr; // 通过指针创建OpenCvSharp.Mat OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(emguMatPtr); // 注意:转换后两个对象共享同一内存 ``` **原理**: 两个库的 `Mat` 底层都是对 OpenCV C++ `Mat` 对象的封装(引用[1][4])。`Emgu.CV.Mat.Ptr` 获取指向 C++ 数据的指针,`OpenCvSharp.Mat` 的指针构造函数直接复用该内存地址。 **注意事项**: 1. ⚠️ **生命周期管理**: `emguMat` 必须保持存活,否则 `ocvMat` 将访问已释放内存 2. ️ **内存共享**: 修改 `ocvMat` 会直接影响 `emguMat` 的数据 3. ⚠️ **版本兼容**: 需确保 OpenCV 底层库版本一致(如都使用 OpenCV 4.x) --- ### 方法 2:通过数据复制转换(安全) ```csharp // 获取Emgu.CV.Mat的数据指针数据 IntPtr dataPtr = emguMat.DataPointer; int rows = emguMat.Rows; int cols = emguMat.Cols; int channels = emguMat.NumberOfChannels; MatType type = emguMat.Depth; // 需转换为OpenCvSharp.MatType对应 // 创建新Mat并复制数据 OpenCvSharp.Mat ocvMat = new OpenCvSharp.Mat(rows, cols, type, dataPtr); OpenCvSharp.Mat clonedMat = ocvMat.Clone(); // 深度复制解除内存关联 ``` **原理**: 利用 `DataPointer` 获取像素数据地址(引用[4]),手动创建新 `Mat` 并复制数据,避免内存共享问题。 **关键点**: 1. 内存对齐处理: OpenCV 默认按 4 字节对齐,需通过 `step` 参数处理行填充(引用[4]): ```csharp int step = (int)emguMat.Step; var ocvMat = new OpenCvSharp.Mat(rows, cols, type, dataPtr, step); ``` 2. 类型映射: `Emgu.CV.CvEnum.DepthType` 需转换为 `OpenCvSharp.MatType`,例如: ```csharp MatType type = emguMat.Depth switch { DepthType.Cv8U => MatType.CV_8UC(channels), DepthType.Cv32F => MatType.CV_32FC(channels), // ...其他类型映射 }; ``` --- ### 完整转换示例 ```csharp using Emgu.CV; using OpenCvSharp; public static OpenCvSharp.Mat ConvertToOpenCvSharpMat(Mat emguMat) { // 方法1:指针直接转换(高效但需管理生命周期) IntPtr ptr = emguMat.Ptr; var ocvMat = new OpenCvSharp.Mat(ptr); // 方法2:安全复制(推荐) OpenCvSharp.MatType matType = GetMappedMatType(emguMat.Depth, emguMat.NumberOfChannels); var safeMat = new OpenCvSharp.Mat( emguMat.Rows, emguMat.Cols, matType, emguMat.DataPointer, (int)emguMat.Step // 处理内存对齐 ).Clone(); // 解除内存依赖 return safeMat; } private static OpenCvSharp.MatType GetMappedMatType(DepthType depth, int channels) { return depth switch { DepthType.Cv8U => MatType.CV_8UC(channels), DepthType.Cv16U => MatType.CV_16UC(channels), DepthType.Cv32F => MatType.CV_32FC(channels), DepthType.Cv64F => MatType.CV_64FC(channels), _ => throw new NotSupportedException($"Unsupported depth type: {depth}") }; } ``` ### 注意事项 1. **内存管理** - 指针转换需手动管理对象生命周期 - 推荐用 `using` 语句确保及时释放: ```csharp using (var emguMat = new Emgu.CV.Mat()) using (var ocvMat = new OpenCvSharp.Mat(emguMat.Ptr)) { // 操作ocvMat } // 自动释放资源 ``` 2. **通道兼容性** 多通道图像(如 BGRA)需保持通道顺序一致(引用[3]) 3. **异常处理** 添加空指针检查异常捕获: ```csharp if (emguMat.Ptr == IntPtr.Zero) throw new ArgumentException("Invalid Emgu.CV.Mat"); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值