Seguimiento de Objeto por Color con Vb.Net + WebCam

Hay muchas ocasiones en la robotica que se necesita realizar procesamiento de imagenes para tomar ciertas decisiones, en la mayoria de los casos las entradas corresponden a informacion que es capturada en tiempo real para asi actuar de alguna manera determinada frente a los cambios.

En esta ocasion he decidido elaborar una pequeña aplicacion que permita seguir algun objeto de un color determinado y reconocer el eje coordenado en el que se produjo el movimiento, algo asi como para simular el movimiento de de la cabeza o de los ojos de algun automata desplazando el foco o punto de vision ya sea a la derecha, izquierda, arriba o abajo.

Como es de saber, para desarrollar este tipo de aplicaciones es mejor inclinarse por lenguajes especializados en el tratamiento de imagenes y procesamiento matematico y a muchos lo primero que se nos viene a la cabeza es utilizar matlab; sin embargo, para este proyecto vamos a utilizar el lenguaje de programacion visual basic.net teniendo en cuenta que para funcionar en cualquier computador solo se necesita tener instalado el netframework , ademas que las aplicaciones aqui son mas livianas y si utilizamos la version express no necesitamos adquirir licencia alguna.

Por otra parte haremos uso del conjunto de librerias que nos ofrece AForge.Net para el procesamiento de imagenes el cual se puede descargar del siguiente enlace ( Conseguir AForge.Net ).

Una vez tengamos todo listo podemos pasar a elaborar la aplicacion, un vistazo a lo que vamos a realizar es lo siguiente:

Bien Manos a la obra, inicialmente elaboramos un nuevo proyecto en vb.net (estoy usando la version 2008), agregamos las librerias de Aforge.Net a la carpeta debug del proyecto, en esta ocasion vamos a utilzar las que se ven en la siguiente imagen:

Luego pasamos al entorno de vb y agregamos las referencias (en post anteriores ya se ha explicado este paso), para ello vamos al explorador de soluciones y presionamos clic derecho sobre references y en el cuado que aparece nos pasamos a la pestaña examinar y luego buscamos las librerias en la carpeta debug del proyecto por ultimo presionamos el boton aceptar:

Una vez agregadas las referencias nos pasamos al cuadro de herramientas en la parte derecha de la interfaz, presionamos clic derecho sobre algun control y en el menu desplegable que aparece seleccionamos elegir elementos:

A continuacion en la ventana emergente que aparece estando en la pestaña Componetes de .Net Framework presionamos el boton examinar y en el cuadro que aparece nos desplazamos a la carpeta debug del proyecto y seleccionamos la libreria AForge.Controls.dll luego hacemos clic en el boton abrir y por ultimo en el boton aceptar.

Despues de esto procedemos a agregar los controles al formulario, para ello elaboramos un diseño similar al siguiente agregando varios groupbox, labels, picturebox, botones radiobuttons, 1 colordialog, 1 combobox y 2 controles videosource player tal y como se ve en la siguiente imagen:

El diseño completo se vera asi:

Bien hay que aclarar que cada videosource player realiza una funcion especifica para poder realizar el procesamiento de imagen de una manera coordinada, el cuadro de la derecha en el que se mostraran las coordenadas trazadas corresponde a un plano cartesiano en el que podemos ver la posicion del punto actual ya sea en el eje x (positivo o negativo) asi como en el eje y. En la siguiente imagen se puede ver dicho plano a elaborar y que mas adelante explicaremos la parte del codigo:

Ahora si, pasemos a la parte del codigo de nuestro formulario, hagamos las siguientes importaciones:


Imports System.Collections.Generic

Imports System.ComponentModel

Imports System.Data

Imports System.Drawing

Imports System.Linq

Imports System.Text

Imports System.Windows.Forms

Imports AForge.Imaging

Imports AForge.Imaging.Filters

Imports AForge

Imports AForge.Video.DirectShow

Imports System.Drawing.Imaging

Imports System.Threading

Imports System.IO

Imports AForge.Video

Ahora pasemos a declarar las siguientes variables como globales:

Private ExistenDispositivos As Boolean = False
Private DispositivosDeVideo As FilterInfoCollection
Private WithEvents FuenteDeVideo As VideoCaptureDevice = Nothing
Private WithEvents FuenteDeVideo2 As VideoCaptureDevice = Nothing
Private videoDevices As FilterInfoCollection

Private filter As New EuclideanColorFiltering()
Private color As Color = Color.Black
Private grayscaleFilter As New GrayscaleBT709()

Private blobCounter As New BlobCounter()
Private range As Integer = 120

Dim hebra As Thread
Dim ab As Rectangle

Luego pasemos a establecer los parametros de configuracion para gestionar la webcam, recibir y finalizar la entrada de video, para ello vamos a elaborar las siguientes funciones o subrutinas:

#Region "Preparo webcam"

Public Sub CargarDispositivos()

For i As Integer = 0 To DispositivosDeVideo.Count - 1

combo_camaras.Items.Add(DispositivosDeVideo(i).Name.ToString())

Next

combo_camaras.Text = combo_camaras.Items(0).ToString()

End Sub

Public Sub BuscarDispositivos()

DispositivosDeVideo = New FilterInfoCollection(FilterCategory.VideoInputDevice)

If DispositivosDeVideo.Count = 0 Then

ExistenDispositivos = False

Else

ExistenDispositivos = True

CargarDispositivos()

Button_iniciar.Enabled = True

End If

End Sub

Public Sub TerminarFuenteDeVideo()

If Not (FuenteDeVideo Is Nothing) Then

If FuenteDeVideo.IsRunning Then

FuenteDeVideo.SignalToStop()

FuenteDeVideo = Nothing

End If

End If

End Sub

#End Region

Posteriormente pasamos al evento load del formulario, alli lo que vamos a hacer sera dibujar la cruz que representa al plano cartesiano, para ello debemos escribir lo siguiente:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

CheckForIllegalCrossThreadCalls = False

BuscarDispositivos()

blobCounter.MinWidth = 2

blobCounter.MinHeight = 2

blobCounter.FilterBlobs = True

blobCounter.ObjectsOrder = ObjectsOrder.Size

Dim b As New Bitmap(320, 240)

Dim pen1 As New Pen(color.FromArgb(160, 255, 160), 3)

Dim g2 As Graphics = Graphics.FromImage(b)

pen1 = New Pen(color.FromArgb(0, 0, 0), 3)

g2.Clear(color.White)

g2.DrawLine(pen1, b.Width \ 2, 0, b.Width \ 2, b.Width)

g2.DrawLine(pen1, b.Width, b.Height \ 2, 0, b.Height \ 2)

pictureBox1.Image = DirectCast(b, System.Drawing.Image)

Me.Button_detener.Enabled = False

End Sub

Despues de esto, podemos programar el boton Iniciar que nos permitira cargar la fuente de video que hayamos seleccionado para procesar la imagen:

Private Sub iniciar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_iniciar.Click

Try

If ExistenDispositivos Then

FuenteDeVideo = New VideoCaptureDevice(DispositivosDeVideo(combo_camaras.SelectedIndex).MonikerString)

AddHandler FuenteDeVideo.NewFrame, New NewFrameEventHandler(AddressOf video_FrameEscalaGris)

FuenteDeVideo.Start()

'dispositivos.Enabled = False

FuenteDeVideo2 = New VideoCaptureDevice(DispositivosDeVideo(combo_camaras.SelectedIndex).MonikerString)

AddHandler FuenteDeVideo.NewFrame, New NewFrameEventHandler(AddressOf video_FrameTratamiento)

FuenteDeVideo.Start()

combo_camaras.Enabled = False

Me.Button_iniciar.Enabled = False

Me.Button_detener.Enabled = True

End If

Catch ex As Exception

End Try

End Sub

Luego programaremos el boton detener para finalizar de manera segura la captura de video:

Private Sub Button_detener_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_detener.Click

Try

If FuenteDeVideo.IsRunning Then

TerminarFuenteDeVideo()

combo_camaras.Enabled = True

Me.Button_iniciar.Enabled = True

Me.Button_detener.Enabled = False

End If

Catch ex As Exception

End Try

End Sub

A continuacion vamos a programar el boton pickcolor que basicamente nos permitira seleccionar el color al cual queremos tratar:

Private Sub Button_sel_color_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_sel_color.Click

ColorDialog1.ShowDialog()

color = ColorDialog1.Color

End Sub

Luego vamos a programar la funcion video_FrameTratamiento que se encargara de recibir la imagen de la webcam como parametro y realizar el proceso de identificacion del objeto u objetos por el color asi como dibujar un rectangulo que encierre dicho color, esta funcion nos permitira visualizar la imagen en el primer videosource player:

Private Sub video_FrameTratamiento(ByVal sender As Object, ByVal eventArgs As NewFrameEventArgs)

Dim Imagen As Bitmap = DirectCast(eventArgs.Frame.Clone(), Bitmap)

Dim imagen2 As Bitmap = DirectCast(eventArgs.Frame.Clone(), Bitmap)

Dim objectsImage As Bitmap = Nothing

filter.CenterColor = color.FromArgb(color.ToArgb())

filter.Radius = CShort(range)

objectsImage = Imagen

filter.ApplyInPlace(objectsImage)

Dim objectsData As BitmapData = objectsImage.LockBits(New Rectangle(0, 0, Imagen.Width, Imagen.Height), ImageLockMode.[ReadOnly], Imagen.PixelFormat)

Dim grayImage As UnmanagedImage = grayscaleFilter.Apply(New UnmanagedImage(objectsData))

objectsImage.UnlockBits(objectsData)

blobCounter.ProcessImage(grayImage)

Dim rects As Rectangle() = blobCounter.GetObjectRectangles()

Dim n As Integer

n = rects.Count

If (Me.RadioButton_todos.Checked = True) Then

If rects.Length > 0 Then

For Each objectRect As Rectangle In rects

Dim g As Graphics = Graphics.FromImage(imagen2)

Using pen As New Pen(color.FromArgb(160, 255, 160), 5)

g.DrawRectangle(pen, objectRect)

End Using

g.Dispose()

Next

End If

End If
'............................................

If (Me.RadioButton_suno.Checked = True) Then

If rects.Length > 0 Then

Dim objectRect As Rectangle

objectRect = rects(0)

Dim g As Graphics = Graphics.FromImage(imagen2)

Using pen As New Pen(color.FromArgb(160, 255, 160), 5)

g.DrawRectangle(pen, objectRect)

End Using

g.Dispose()

End If

End If

Player_proimagen.Image = imagen2

End Sub

Ademas aqui se determina si el usuario decide ver todos los objetos detectado o solo uno ya que el proceso de seguimiento del color puede encontrar mas de un objeto con el mismo color, por otra parte si decide solo ver 1 solo se selecciona el radiobutton indicado y se visualizara un solo rectangulo con el objeto con el color mas representativo.

Por otra parte al convertir la imagen a escala de grises y aplicar filtros podremos determinar con mayor facilidad el color que queremos procesar asi que para ello elaboramos la siguiente funcion:

Private Sub video_FrameEscalaGris(ByVal sender As Object, ByVal eventArgs As NewFrameEventArgs)

Dim Imagen As Bitmap = DirectCast(eventArgs.Frame.Clone(), Bitmap)

Dim objectsImage As Bitmap = Nothing

Dim filter As New EuclideanColorFiltering()

' set center colol and radius

filter.CenterColor = color.FromArgb(0, 0, 0)

filter.Radius = 120

' apply the filter

objectsImage = Imagen

filter.ApplyInPlace(Imagen)

' lock image for further processing

Dim objectsData As BitmapData = objectsImage.LockBits(New Rectangle(0, 0, Imagen.Width, Imagen.Height), ImageLockMode.[ReadOnly], Imagen.PixelFormat)

' grayscaling

Dim grayImage As UnmanagedImage = grayscaleFilter.Apply(New UnmanagedImage(objectsData))

' unlock image

objectsImage.UnlockBits(objectsData)

' locate blobs

blobCounter.ProcessImage(grayImage)

Dim rects As Rectangle() = blobCounter.GetObjectRectangles()

If rects.Length > 0 Then

Dim objectRect As Rectangle = rects(0)

' draw rectangle around derected object

Dim g As Graphics = Graphics.FromImage(Imagen)

Using pen As New Pen(color.FromArgb(160, 255, 160), 5)

g.DrawRectangle(pen, objectRect)

End Using

g.Dispose()

Dim objectX As Integer = objectRect.X + objectRect.Width \ 2 - Imagen.Width \ 2

Dim objectY As Integer = Imagen.Height \ 2 - (objectRect.Y + objectRect.Height \ 2)

Dim t As New ParameterizedThreadStart(AddressOf proceso_punto)

Dim aa As New Thread(t)

aa.Start(rects(0))

ab = CType(rects(0), Rectangle)

hebra = New Thread(AddressOf proceso_alternativo)

'ponemos al hilo en marcha

hebra.Start()

End If

Dim g1 As Graphics = Graphics.FromImage(Imagen)

Dim pen1 As New Pen(color.FromArgb(160, 255, 160), 3)

g1.DrawLine(pen1, Imagen.Width \ 2, 0, Imagen.Width \ 2, Imagen.Width)

g1.DrawLine(pen1, Imagen.Width, Imagen.Height \ 2, 0, Imagen.Height \ 2)

g1.Dispose()

'--------------

Player_escala_gris.Image = Imagen

End Sub

Asi mismo cabe indicar que internamente la funcion video_FrameEscalaGris hace un llamado a 2 hebras o hilos de ejecucion para paralelizar la aplicacion y llevar a cabo la ubicacion de los puntos en el plano coordenado y la estraccion de dichos puntos o ubicacion, se realiza esto de esa manera para evitar el bloqueo de la aplicacion por la carga elevada de instrucciones que esta realizando.

El hilo o hebra de ejecucion para dibujar los puntos en el plano corresponde a la siguiente subrutina:

Private Sub proceso_punto(ByVal r As Object)

Try

Dim b As New Bitmap(pictureBox1.Image)

Dim a As Rectangle = CType(r, Rectangle)

Dim pen1 As New Pen(color.FromArgb(160, 255, 160), 3)

Dim g2 As Graphics = Graphics.FromImage(b)

pen1 = New Pen(color, 3)

' Brush b5 = null;

Dim b5 As New SolidBrush(color)

' g2.Clear(Color.Black);

Dim f As New Font(Font, FontStyle.Bold)

g2.DrawString("x", f, b5, a.Location)

g2.Dispose()

pictureBox1.Image = DirectCast(b, System.Drawing.Image)

Catch faa As Exception

Thread.CurrentThread.Abort()

End Try

Thread.CurrentThread.Abort()

End Sub

En la anterior funcion se esta recibiendo como parametro el punto a dibujar.

Luego en la siguiente hebra o hilo de ejecucion vamos a extraer la ubicacion en pixeles del punto y nos permitira saber que tan alejado o cerca se encuentra en relacion a algun eje:

Sub proceso_alternativo()
Me.Label_puntoa.Text = ab.Location.ToString
hebra.Abort()
End Sub

Por ultimo codificamos el boton limpiar que nos permitira volver a dibujar el plano cartesiano y borrar los puntos que ya existen asi como colocar las coordenadas de ubicacion en cero.

Private Sub Button_limpiar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_limpiar.Click

Dim b As New Bitmap(320, 240)

Dim pen1 As New Pen(color.FromArgb(160, 255, 160), 3)

Dim g2 As Graphics = Graphics.FromImage(b)

pen1 = New Pen(color.FromArgb(0, 0, 0), 3)

g2.Clear(color.White)

g2.DrawLine(pen1, b.Width \ 2, 0, b.Width \ 2, b.Width)

g2.DrawLine(pen1, b.Width, b.Height \ 2, 0, b.Height \ 2)

pictureBox1.Image = DirectCast(b, System.Drawing.Image)

Me.Label_puntoa.Text = "{X=0,Y=0}"

End Sub

Bien, despues de terminar de programar podemos pasar a la parte de la ejecucion y las pruebas, al correr nuestro programa se puede ver una imagen como la siguiente:

Primero se selecciona la camara que tengamos conectada, luego seleccionamos el color que queremos seguir y a continuacion se presiona el boton iniciar, inmediatamente se puede observar la imagen de video que esta siendo capturada y procesada asi como los puntos que se van graficando; en esta ocasion el algoritmo identifico varios objetos con el color que hemos establecido que corresponde al “rojo” asi que la ubicacion del punto o el movimiento realizado no sera tan preciso.

Si queremos que la ubicacion del punto y del objeto sea mas preciso seleccionaremos el radioboton solo 1 y de esta manera solo veremos un rectangulo que encierre al color:

Bueno amigos eso es todo, espero que esta aplicacion pueda resultarles util y de interes para cualquier proyecto, como nota adicional se pude complementar estableciendo rangos de coordenadas para cada eje y asi establecer una equivalencia entre pixeles y centimetros y lograr que el cambio de posicion o ubicacion no sea tan repentino (como para simular el movimiento de la cabeza o unos ojos).

a continuacion les dejo el link para que puedan descargar el programa.

Descargar Aplicacion

Anuncios

5 respuestas a “Seguimiento de Objeto por Color con Vb.Net + WebCam

  1. Excelente y gran aporte. Felicitaciones.
    Quisiera por favor, si puede ponerse en contacto a través de mi casilla.
    Saludos desde Argentina.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s