daniel lozano
daniel lozano

Reputation: 429

Trying to obtain gray images from webcam using Visual Basic, OpenCV and EmguCV

I am learning about OpenCV and EmguCV. I succeed by trying to obtain images from my webcam and now I'm trying to obtain that images in a gray scale.

My purpose after achieve this, is to recognize and track colors, so im trying to obtain the image in a HVS format to filter the image to display only the color Im looking for in white, and the rest of the image in black (I hope you understand my explanation)

Now, I only want to obtain the image from my webcam in that gray scale using the following code:

Imports Emgu.CV

Imports Emgu.CV.Util

Imports Emgu.CV.Structure

Imports System.Drawing

Public Class Form1

Dim capturez As Capture = New Capture
Dim radius As Decimal = 50.0F
Dim x As Decimal
Dim y As Decimal

Private Sub Timer1_Tick() Handles Timer1.Tick

    'Define x and y to determinate the position of the circle in the PictureBox
    x = PictureBox1.Width / 2 - radius
    y = PictureBox1.Height / 2 - radius

    'PointF type defines the X and Y position of the future circle in the PictureBox
    Dim point1 As New PointF(x, y)
    Dim circle As CircleF = New CircleF(point1, radius)
    Dim img As Image(Of Bgr, Byte) = capturez.QueryFrame()

    'Convert camera capture from BGR to HVS
    Dim hvs_frame As Image(Of Hsv, Byte) = img.Convert(Of Hsv, Byte)()

    'Obtain the three channels that compose the HVS image (hue, saturation and value)
    Dim channels As Image(Of Gray, Byte)() = hvs_frame.Split()


    'Remove all pixels from the hue channel that are not in the range [40, 60]
    CvInvoke.cvInRangeS(channels(0), New Gray(200).MCvScalar, New Gray(255).MCvScalar, channels(0))

    'Display converted frame
    PictureBox1.Image = channels(0).ToBitmap()

    'When commenting the .CopyBlank() property, the back color of the PictureBox remains transparent
    'When uncomment this, the back color of the PictureBox becomes black
    Dim imageCircle As Image(Of Bgr, Byte) = img '.CopyBlank()
    imageCircle.Draw(circle, New Bgr(Color.Brown), 2)
    PictureBox1.Image = imageCircle.ToBitmap()
End Sub

With this code, I am only obtaining the image in BGR, and I don't know why, because I am specifying in the 'PictureBox1.Image' that I want the image in that gray scale.

Please help me =(

I'm using OpenCV 2.4.9 and EmguCV 2.4.0 ... if needed

Thanks a lot!

Upvotes: 1

Views: 2092

Answers (1)

Jurjen
Jurjen

Reputation: 1396

Currently you are capturing the image in color (BGR) so you can't just show one channel in your Picturebox as this won't be a GrayScale image (it's the Blue channel, since it's a BGR image). You can solve this in two ways.

The first option is to specifically retrieve the frame from your camera in GrayScale. You can achieve this like this:

Dim imgGrayscale As Image(Of Gray, Byte) = capturez.RetrieveGrayFrame()

The other option is to convert your image to GrayScale after you've retrieved it from the webcam capture. You can achieve this like this:

Dim img As Image(Of Bgr, Byte) = capturez.QueryFrame()
Dim imgGrayscale As Image(Of Gray, Byte) = img.Convert(Of Gray, Byte)()

Note that from this moment you will be working with a GrayScale image, which will impact the rest of your code (i.e. you will not have 3 channels anymore, etc.). Therefore the second option may be more suitable for you, as you will now have a Bgr and a GrayScale. You can now still use the Bgr image for your HSV conversion etc.

You can now show the GrayScale image like this:

PictureBox1.Image = imgGrayscale.ToBitmap()

EDIT to threshold one color, instead of converting to grayscale:

If you want to extract one color from the image, you can't use Grayscale, even though the result will be a one channel image. This is because you need all channel values to extract the right color. Follow the following steps:

  • Retrieve you Bgr image and convert it to Hsv as you did.
  • Don't split your image into three channels, but just create a new empty image to store your color threshold result in
  • Call the CvInvoke.InRange function, but apply the scalar to all three HSV channels. I recommend the use of InRange and not InRangeS as the latter is from OpenCV 1.X.
  • The InRange function works as follows: the MCvScalars represent the bounds of your color values in HSV range. It checks each pixel and compares if it is within the scalar values you stated. If it is, it will get a 255 pixel value (white) and otherwise 0 (black). That will give you a thresholded image.
  • If you want to extract more ranges of colors, you can add more CvInvoke.Inrange methods and add the different results with a CvInvoke.Add function later.
  • Experiment with the MCvScalar bounds you set to extract your color if they don't give you the right result. Also, if you want to obtain a different color, modify the bounds. You can use color pickers to retrieve the HSV values of certain color. Even a color picker in MS Paint will give you the Hue/Sat/Lum values, but you have to convert these (they are on a different scale, MS Paint 0-239 and OpenCV 0-179). So convert them by multiplying the levels by 180/240.

You will get something like this:

` Get the webcam frame
Dim webcam_image As Mat
webcam_image = capturez.QueryFrame()

` Get an empty HSV image and threshold image
Dim HSV_img As New Mat(webcam_image.Size, DepthType.Cv8U, 3)
Dim threshold_img As New Mat(webcam_image.Size, DepthType.Cv8U, 1)

' Convert camera capture from BGR to HVS
CvInvoke.CvtColor(webcam_image, HSV_img, ColorConversion.Bgr2Hsv)

` Threshold the yellow colors only (please adopt the scalar values to whatever values suit your needs)
CvInvoke.InRange(HSV_img, New ScalarArray(New MCvScalar(20, 100, 100)), New ScalarArray(New MCvScalar(30, 255, 255)), threshold_img)

Upvotes: 2

Related Questions