Lib Bridge | Cvb Image to Emgu Mat

Hi @vizion,

sorry for the late reply. First, I would recommend the object oriented wrappers of :cvb: 13.02.000 and higher. With these it gets much simpler. If you have a :cvb: acquisition loop you can convert them to Cv.Mats 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

Programming Questions.Net API

csharp tutorial

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 :cvb: always treats plane 0 as red. OpenCV loads RGB data as BGR (thus channel 0 is blue). Hope this helps.

Merry Christmas :santa:

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 :wink:.

2 Likes