[英]How can I get and set pixel values of an EmguCV Mat image?
我将 EmguCV 3.0.0 包装器用于 OpenCV 3.0 库。 我在几个地方使用Mat
class。 下面是一个由double
值组成的单通道 8x8 图像示例:
Mat image = new Mat(8, 8, DepthType.Cv64F, 1);
Image<>
class 提供了获取和设置像素值的合理方法, Matrix<>
class 的方法相同,但Mat
class 的方法似乎并不明显。我想出的唯一方法是设置单个像素使用遮罩:
// 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);
这感觉应该是两行,而不是六行,所以我觉得我错过了什么。 当Mat
不止一个通道时,事情会变得更加复杂,因为Matrix<>
只是二维的,所以必须使用遮罩来设置每个通道上的像素。
我没有时间或 memory 以这种方式设置像素。 如何使用单个方法调用设置像素?
您可以通过使用 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);
对 200000000 次操作的测试表明,动态类型版本可能比静态版本慢约 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();
}
}
一个更好的。
public static class MatExtension
{
public static T Get<T>(this Mat mat, int row, int col)
{
unsafe
{
var span = new ReadOnlySpan<T>(mat.DataPointer.ToPointer(), mat.Rows * mat.Cols * mat.ElementSize);
return span[row * mat.Cols + col];
}
}
public static ReadOnlySpan<T> Get<T>(this Mat mat, int row, Range cols)
{
unsafe
{
var span = new ReadOnlySpan<T>(mat.DataPointer.ToPointer(), mat.Rows * mat.Cols * mat.ElementSize);
var colOffsets = cols.GetOffsetAndLength(span.Length);
return span.Slice(row * mat.Cols + colOffsets.Offset, colOffsets.Length);
}
}
}
用法:
using var stats = new Mat();
using var centroids = new Mat();
//...
var x = stats.Get<int>(i,(int)ConnectedComponentsTypes.Left);
var cxy = centroids.Get<double>(i, 0..1);
double cxy0 = cxy[0];
//...
此解决方案https://stackoverflow.com/a/32559496/15221325应用户 Quergo 的要求用于三个颜色通道:
起初,很好的答案,对我帮助很大。 但我不明白这种方法如何处理多个渠道。 在您的示例中,您编写了 var mat = new Mat(3, 3, DepthType.Cv64F, 3); 它不应该返回一个大小为 3 的 double[] ,其中包含每个通道的值吗? – Quergo 19 年 11 月 18 日,22:30
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];
}
}
您可以通过两种方式轻松获取和设置 Mat 图像的像素值:
1- 将 Mat 图像转换为 Image<ColorType, DDepth>
2- 直接从 Mat class 使用 SetValue 和 GetValue。
1- 将 Mat 图像转换为 Image<> 格式:
int row = 0;
int col = 1;
int channel = 6;
Mat Image = CvInvoke.Imread("path");
Image<Gray, Byte> ImageFormat = Image.ToImage<Gray, Byte>();
int pixVal = ImageFormat.Data[row, col, channel];
// for set value
int numValue = 165;
ImageFormat.Data[row, col, channel] = numValue;
您可以通过以下方式访问 Image<> 的 Mat 格式:
ImageFormat.Mat;
2- 您可以直接使用 SetValue 和 GetValue 方法设置或获取像素值(这些方法返回 object,您必须将 object 转换为数字):
object pixVal = Image.Data.GetValue(row, col, channel);
float pixValue = Convert.ToInt32(pixVal);
// for set value:
float setPixVal = 159;
Image.Data.SetValue(setPixVal, row, col, channel);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.