Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# / EmguCV - Convert an uploaded HttpPostedFileBase to a Emgu.CV.Mat

Tags:

c#

opencv

emgucv

I have an MVC application where one of my controllers receives an uploaded file (image) as an HttpPostedFileBase object.

I am trying to process the image using EmguCV, but I'm having difficulty converting my HttpPostedFileBase to the EmguCV matrix object Emgu.CV.Mat (which is just a C# implementation of a cv::Mat object).

There is a constructor for Mat that looks like:

public Mat(int rows, int cols, DepthType type, int channels, IntPtr data, int step);

but I'm not sure how to get the type, data, and step from my starting HttpPostedFileBase object. Is this possible?

I see here, that I can convert an HttpPostedFileBase to an Image object (I think that's in the System.Drawing namespace), which allows me to see the height and width. But how can I use this information to get the rest of the required parameters to send the the Mat() constructor?

like image 993
Brett Avatar asked Jun 05 '14 02:06

Brett


1 Answers

According to the Emgu specification those parameters mean:

  /// <param name="type">Mat element type

  /// <param name="channels">Number of channels

  /// <param name="data">
  /// Pointer to the user data. Matrix constructors that take data and step parameters do not  
  /// allocate matrix data. Instead, they just initialize the matrix header that points to the 
  /// specified data, which means that no data is copied. This operation is very efficient and 
  /// can be used to process external data using OpenCV functions. The external data is not 
  /// automatically deallocated, so you should take care of it.

  /// <param name="step">
  /// Number of bytes each matrix row occupies. 
  /// The value should include the padding bytes at the end of each row, if any.
  • type is of the type CvEnum.DepthType, which is the depth of the image, you can pass CvEnum.DepthType.Cv32F which stands for 32bit depth images, other possible values are of the form CvEnum.DepthType.Cv{x}{t}, where {x} is any value of the set {8,16,32,64} and {t} can be Sfor Single or F for Float.

  • channels, depend on the type of image but I think you can use 4from ARGB.

For the other 2 parameters, if you don't need the optimization part, you can just use this constructor of the Mat class:

public Mat(int rows, int cols, DepthType type, int channels)

If you really want to use the optimized version, then (continuing):

  • data, you can pass the Bitmap.GetHbitmap() which returns an IntPtr to the user data.

  • step, for this guy, I'll give you an informed guess, if for each pixel you have 4 channels, and each channel ranges from 0 to 255 (8bits), 8*4 = 32, so for every width of unit you need 32 bits. Assuming this is correct, each row haves 32*width bits, converting it to bytes ((8*4)*width)/8 = 4*width, which is the number of channels, multiplied by the image width.

UPDATE

Other way to get the data and step is from BitmapData class, like this (excerpt from the MSDN resource):

Bitmap bmp = new Bitmap(Image.FromStream(httpPostedFileBase.InputStream, true, true));

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

System.Drawing.Imaging.BitmapData bmpData =
    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
        bmp.PixelFormat);

// data = scan0 is a pointer to our memory block.
IntPtr data = bmpData.Scan0;

// step = stride = amount of bytes for a single line of the image
int step = bmpData.Stride;

// So you can try to get you Mat instance like this:
Mat mat = new Mat(bmp.Height, bmp.Width, CvEnum.DepthType.Cv32F, 4, data, step);

// Unlock the bits.
bmp.UnlockBits(bmpData);

Have not tested this solution, but you can give it a try. My answer was based on the Emgu code here., Bitmap IntPtr here and also on this post which help me also acquiring further understanding on this.

I have seen other ways to do it also, and unless you really need to call that complete constructor, I would try this approach, seems cleaner:

HttpPostedFileBase file //your file must be available is this context.

if (file.ContentLength > 0)
{
    string filename = Path.GetFileName(file.FileName);

    // your so wanted Mat!
    Mat img = imread(filename, CV_LOAD_IMAGE_COLOR);
}

NOTE

There are great tutorials in the OpenCV documentation. Just have a look at the available tutorials for the core module. In particular, this one.

like image 123
João Pinho Avatar answered Sep 30 '22 14:09

João Pinho