Hi @vizion,
sorry for the late reply. First, I would recommend the object oriented wrappers of 13.02.000 and higher. With these it gets much simpler. If you have a acquisition loop you can convert them to Cv.Mat
s as follows (helper class below):
var stream = device.Stream;
stream.Start();
try
{
while (acquiring)
{
using (var acqImage = stream.Wait())
{
var acqMat = acqImage.ToMat();
// do your OpenCV processing here
}
}
}
finally
{
stream.Abort();
}
See here for a quick tutorial on the new API:
Getting Started with CVB.Net
https://forum.commonvisionblox.com/t/getting-started-with-cvb-net/246?u=parsd
Here is what you need for the .ToMat()
extension method:
public enum SwapChannels
{
No,
Yes
}
public static class CvbEmguExtensions
{
public static Mat ToMat(this Image image, SwapChannels swap = SwapChannels.Yes)
{
if (image == null)
throw new ArgumentNullException(nameof(image));
if (swap == SwapChannels.Yes)
{
using (var swapped = Image.FromPlanes(MappingOption.LinkPixels, image.Planes.Reverse().ToArray()))
{
return swapped.ToMat(SwapChannels.No);
}
}
var mat = new Mat(image.Height, image.Width, image.GetDepthType(), image.Planes.Count);
try
{
image.CopyTo(mat);
return mat;
}
catch
{
mat.Dispose();
throw;
}
}
public static DepthType GetDepthType(this Image image)
{
if (image == null)
throw new ArgumentNullException(nameof(image));
if (!image.Planes.DataTypesIdentical)
throw new ArgumentException("Differing data types per plane not supported");
var dataType = image.Planes[0].DataType;
var bitsPerChannel = dataType.BitsPerPixel;
if (dataType.IsUnsignedInteger)
{
if (bitsPerChannel <= 8)
return DepthType.Cv8U;
else if (bitsPerChannel <= 16)
return DepthType.Cv16U;
}
else if (dataType.IsSignedInteger)
{
if (bitsPerChannel <= 8)
return DepthType.Cv8S;
else if (bitsPerChannel <= 16)
return DepthType.Cv16S;
else if (bitsPerChannel <= 32)
return DepthType.Cv32S;
}
else if (dataType.IsFloat)
{
if (bitsPerChannel == 32)
return DepthType.Cv32F;
else if (bitsPerChannel == 64)
return DepthType.Cv64F;
}
throw new NotSupportedException($"{dataType} not supported by Cv.Mat");
}
public static void CopyTo(this Image image, Mat mat)
{
if (image == null)
throw new ArgumentNullException(nameof(image));
if (mat == null)
throw new ArgumentNullException(nameof(mat));
using (var target = mat.AsWrappedCvbImage())
{
image.CopyTo(target);
}
}
public static WrappedImage AsWrappedCvbImage(this Mat mat)
{
var planeStride = mat.GetBitDepth() / 8;
var pixelStride = planeStride * mat.NumberOfChannels;
return new WrappedImage
(
mat.DataPointer, 0, mat.Width, mat.Height,
mat.GetPixelDataType(), mat.GetBitDepth(),
pixelStride, mat.Step, planeStride, Enumerable.Range(0, mat.NumberOfChannels).ToArray()
);
}
public static PixelDataType GetPixelDataType(this Mat mat)
{
if (mat == null)
throw new ArgumentNullException(nameof(mat));
switch (mat.Depth)
{
default:
case DepthType.Default:
throw new InvalidOperationException("Invalid bit depth");
case DepthType.Cv8U:
case DepthType.Cv16U:
return PixelDataType.UInt;
case DepthType.Cv8S:
case DepthType.Cv16S:
case DepthType.Cv32S:
return PixelDataType.Int;
case DepthType.Cv32F:
case DepthType.Cv64F:
return PixelDataType.Float;
}
}
public static int GetBitDepth(this Mat mat)
{
if (mat == null)
throw new ArgumentNullException(nameof(mat));
switch (mat.Depth)
{
default:
case DepthType.Default:
throw new InvalidOperationException("Invalid bit depth");
case DepthType.Cv8U:
case DepthType.Cv8S:
return 8;
case DepthType.Cv16U:
case DepthType.Cv16S:
return 16;
case DepthType.Cv32S:
case DepthType.Cv32F:
return 32;
case DepthType.Cv64F:
return 64;
}
}
}
The swap is necessary as always treats plane 0 as red. OpenCV loads RGB data as BGR (thus channel 0 is blue). Hope this helps.
Merry Christmas
P.S.: the implementation here always copies. You can also sometimes create a Cv.Mat
view on another buffer. If you are interested in it, I will also write it .