C# 如何获取和设置EmguCV Mat图像的像素值?
我正在使用EmguCV 3.0.0包装器来访问OpenCV 3.0库。我在一些地方使用C# 如何获取和设置EmguCV Mat图像的像素值?,c#,image-processing,mat,emgucv,C#,Image Processing,Mat,Emgucv,我正在使用EmguCV 3.0.0包装器来访问OpenCV 3.0库。我在一些地方使用Mat类。以下是一个由双值构成的单通道8x8图像示例: Mat image = new Mat(8, 8, DepthType.Cv64F, 1); 该类提供了,并且该方法对于该类是相同的,但对于该类来说似乎不那么明显。我知道如何设置单个像素的唯一方法是使用遮罩: // set two pixel values, (0,0) to 9.0, (2, 3) to 42.0 Matrix<byte>
Mat
类。以下是一个由双值构成的单通道8x8图像示例:
Mat image = new Mat(8, 8, DepthType.Cv64F, 1);
该类提供了,并且该方法对于该类是相同的,但对于该类来说似乎不那么明显。我知道如何设置单个像素的唯一方法是使用遮罩:
// set two pixel values, (0,0) to 9.0, (2, 3) to 42.0
Matrix<byte> mask = new Matrix<byte>(8,8);
mask.Data[0, 0] = 1;
image.SetTo(new MCvScalar(9.0), mask);
mask = new Matrix<byte>(8,8);
mask.Data[2, 3] = 1;
image.SetTo(new MCvScalar(42.0), mask);
//将两个像素值(0,0)设置为9.0,(2,3)设置为42.0
矩阵掩码=新矩阵(8,8);
掩码数据[0,0]=1;
image.SetTo(新的MCvScalar(9.0),掩码);
掩模=新矩阵(8,8);
掩码数据[2,3]=1;
image.SetTo(新的MCvScalar(42.0),掩码);
这感觉应该是两行,而不是六行,所以我觉得我遗漏了什么。当Mat
是多个通道时,事情变得更加复杂,因为Matrix
仅为2D,因此必须使用遮罩来设置每个通道上的像素
我没有时间或内存来这样设置像素如何通过单个方法调用设置像素?您可以通过使用DataPointer复制非托管内存块并将托管类型转换为非托管类型,从Mat获取元素。设置值以相反方向编组
例如,您可以使用这样的扩展类
public static class MatExtension
{
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];
}
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);
}
private static dynamic CreateElement(DepthType depthType, dynamic value)
{
var element = CreateElement(depthType);
element[0] = value;
return element;
}
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];
}
}
然后可以通过单个方法调用获取和设置值
var row = 2;
var col = 1;
var mat = new Mat(3, 3, DepthType.Cv64F, 3);
mat.SetValue(row, col, 3.14);
var value = mat.GetValue(row, col);
对20000000次操作的测试表明,动态类型版本比静态版本慢约2.5倍
public static double GetDoubleValue(this Mat mat, int row, int col)
{
var value = new double[1];
Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
return value[0];
}
public static void SetDoubleValue(this Mat mat, int row, int col, double value)
{
var target = new[] { value };
Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
}
基于Bartosz Rachwal的伟大回答,我尝试为OpenCvSharp编写:
public static dynamic GetValue(this Mat mat, int row, int col)
{
var value = CreateElement(mat.Type());
Marshal.Copy(mat.Data + (row * mat.Cols + col) * mat.ElemSize(), value, 0, 1);
return value[0];
}
public static void SetValue(this Mat mat, int row, int col, dynamic value)
{
var target = CreateElement(mat.Type(), value);
Marshal.Copy(target, 0, mat.Data + (row * mat.Cols + col) * mat.ElemSize(), 1);
}
private static dynamic CreateElement(MatType depthType, dynamic value)
{
var element = CreateElement(depthType);
element[0] = value;
return element;
}
private static dynamic CreateElement(MatType depthType)
{
switch (depthType)
{
case MatType.CV_8S:
return new sbyte[1];
case MatType.CV_8U:
return new byte[1];
case MatType.CV_16S:
return new short[1];
case MatType.CV_16U:
return new ushort[1];
case MatType.CV_32S:
return new int[1];
case MatType.CV_32F:
return new float[1];
case MatType.CV_64F:
return new double[1];
default:
throw new NotImplementedException();
}
}
此解决方案适用于用户要求的三种颜色通道:
起初,很好的回答对我帮助很大。但我不明白这种方法如何处理多个通道。在您的示例中,您编写了var mat=new mat(3,3,DepthType.Cv64F,3);它是否应该返回一个大小为3的双[]值,每个通道包含一个值19年11月18日22:30在Quergo
图像[0,0]=9;应该这样做,这也是我所期望的。但它不起作用。这里有一个错误:“无法将带[]的索引应用于'Emgu.CV.Mat'类型的表达式”使用dynamic
,看起来非常好用!我唯一的问题是动态
可能比静态类型的备选方案慢10到100倍。(请参阅和。)您的扩展方法应该处于该频谱的快速端,因为动态
接收类型很少会改变。即使如此,优化的Get/SetValue()
方法也应该可以安全返回/接收double
,因为有一个从任何Emgu支持的DepthType
到double
,谢谢!我没想到。如果EmguCV的维护人员不能更好地支持这一点,我将很快包括一个像您这样的扩展方法。Bartosz,很好的答案。只有一件事,而不是(row*mat.Cols+col)*mat ElementSize,我会使用((row*mat.Step)+(col*mat.ElementSize)),因为row Step可能不等于row*mat.ColumnsAt首先,很好的答案,对我帮助很大。但我不明白这种方法如何处理多个通道。在您的示例中,您编写了var mat=new mat(3,3,DepthType.Cv64F,3)代码>它不应该返回一个大小为3的双[]值,每个通道包含一个值吗?谢谢你的努力,但我认为这不属于这里。OpenCvSharp是一个与我所问的完全不同的库。如果OpenCV夏普上没有一个现有的问题,你可以把它移到,考虑问和回答你自己的问题。两个都是OpenCV包装器,不像你看到的那么大。当我需要某方面的知识时,我不会只寻找OpenCvSharp线程。EMGU、Cpp甚至Phton的例子都给了我很大的帮助。我认为一个新问题是浪费时间。这是相同的问题,几乎是相同的代码。任何像我这样搜索的人都可以利用它。另外,您关于dynamic的讨论也是一本好书,任何使用它的人都应该阅读。谢谢。使用OpenCvSharp,您可以使用Mat::Set(x,y,val)
方法将(x,y)
处的元素设置为val
的值。在Emgu包装中没有这样的访问器。你只是为同一个问题链接了另一个答案吗?我们赞赏扩展的解决方案,但有3个颜色通道对原始问题并不重要。引用另一个答案:“起初,不错的答案,对我帮助很大。但我不明白这种方法如何处理多个通道。在您的示例中,您编写了var mat=new mat(3,3,DepthType.Cv64F,3);它不应该返回一个双[]大小为3,每个频道都有一个值?–Quergo 11月18日19日22:30“所以肯定有人对3个彩色频道感兴趣,而最上面的答案中有一个三色图像,这在本例中是错误的。您好,英语3LS,从您的回答中不清楚您是在回答另一个用户的评论。我建议更新您的答案来解释这一点。注意:对某个内容投票的用户在第二天不能更改投票,除非该帖子被编辑。我编辑了我的解决方案,提到我正在处理另一个用户的评论。因为我没有足够的声誉来评论顶级解决方案,你能在那里写一篇评论吗,Quergo的问题已经得到了回答。。。它已经2年了,所以我认为这并不重要,但你永远不知道。然而,用户PeterBence去年也提出了同样的问题。
public static class MatExtension
{
public static dynamic GetValues(this Mat mat, int row, int col)
{
var value = CreateElement3Channels(mat.Depth);
Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 3);
return value;
}
public static dynamic GetValue(this Mat mat, int channel, int row, int col)
{
var value = CreateElement3Channels(mat.Depth);
Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 3);
return value[channel];
}
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];
}
public static void SetValues(this Mat mat, int row, int col, dynamic value)
{
Marshal.Copy(value, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
}
public static void SetValue(this Mat mat, int channel, int row, int col, dynamic value)
{
var element = GetValues(mat, row, col);
var target = CreateElement(element, value, channel);
Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
}
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);
}
private static dynamic CreateElement(dynamic element, dynamic value, int channel)
{
element[channel] = value;
return element;
}
private static dynamic CreateElement(DepthType depthType, dynamic value)
{
var element = CreateElement(depthType);
element[0] = value;
return element;
}
private static dynamic CreateElement3Channels(DepthType depthType)
{
if (depthType == DepthType.Cv8S)
{
return new sbyte[3];
}
if (depthType == DepthType.Cv8U)
{
return new byte[3];
}
if (depthType == DepthType.Cv16S)
{
return new short[3];
}
if (depthType == DepthType.Cv16U)
{
return new ushort[3];
}
if (depthType == DepthType.Cv32S)
{
return new int[3];
}
if (depthType == DepthType.Cv32F)
{
return new float[3];
}
if (depthType == DepthType.Cv64F)
{
return new double[3];
}
return new float[3];
}
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];
}
}