Aplicación para crear retratos de personas
Uso de los eventos Paint y PrintPage

Fecha: 02/Abr/2005 (01 de Abril de 2005)
Autor: Alma Yolanda Díaz Valdez

 


Partes en las que se divide este artículo:

  • Introducción
  • Cómo alimentar los ListViews con las imágenes
  • Cómo se dibujan las imágenes en el PictureBox
  • Cómo se dibuja una imagen sobre otra en el área indicada
  • Cómo imprimir el retrato
  • Código de la aplicación en VB.NET y C#

  • Introducción:

    Existe una técnica policial de identificación visual que por medio de la descripción de los rasgos fisonómicos de una persona, se puede crear el "retrato" de la misma en base a la información facilitada por los testigos.
    Esto mediante el empleo de láminas plásticas transparentes con formas de caras, narices, bocas, orejas, cejas, ojos, peinados, etc., que se superponen unas a otras, hasta componer el retrato.

    Esta aplicación permite recrear esta técnica.

    Para ir formando el retrato, se deben seleccionar cada uno de los siguientes elementos:

  • La forma de la cabeza
  • El tipo de cabello
  • El tipo de cejas
  • El tipo de ojos
  • El tipo de nariz
  • El tipo de boca
  • El tipo de bigote y barba
  • Hasta formar el retrato:



    A continuación se explicarán partes del código utilizado en este ejemplo.

    Cómo alimentar los ListViews con las imágenes:

    Se deben inicializar algunos de los elementos de la aplicación: ImageList y ListViews. Esta inicialización es recomendable que se haga desde el método New de la forma, claro, se debe tener cuidado de colocar este código de inicialización despues del llamado del método InitializeComponent(), como se muestra enseguida:


    Public Sub New()
        MyBase.New()
    
        'El Diseñador de Windows Forms requiere esta llamada.
        InitializeComponent()
    
        'Agregar cualquier inicialización después de la llamada a InitializeComponent()
        Inicializar("cara*.jpg", lvwCara, ilstCaras)
        Inicializar("cabello*.jpg", lvwCabello, ilstCabello)
        Inicializar("ojos*.jpg", lvwOjos, ilstOjos)
        Inicializar("ceja*.jpg", lvwCejas, ilstCejas)
        Inicializar("nariz*.jpg", lvwNariz, ilstNariz)
        Inicializar("boca*.jpg", lvwBoca, ilstBoca)
        Inicializar("bigote*.jpg", lvwBigote, ilstBigote)
        Inicializar("barba*.jpg", lvwBarba, ilstBarba)
    
        lvwCara.Visible = True
        lvwCabello.Visible = False
        lvwCejas.Visible = False
        lvwOjos.Visible = False
        lvwNariz.Visible = False
        lvwBoca.Visible = False
        lvwBigote.Visible = False
        lvwBarba.Visible = False
    End Sub

    El método Inicializar alimenta los ImageLists y ListViews con los elementos que permitirán dibujar el retrato.
    Los ImageLists contendrán las imágenes de: formas de cabeza, tipo de cabello, tipo de cejas, tipos de ojos, etc. Estas imágenes serán utilizadas por los ListViews.
    Los ListViews son los controles que interactuan con el usuario. A cada ListView se le indica que ImageList va a utilizar para ligar a cada uno de sus elementos con la imagen que los representará.


    Private Sub Inicializar(ByVal ps_filtro As String, ByRef po_lista As ListView,ByRef po_listadeimagenes As ImageList)
        ' Relación de archivos a buscar:                             ByVal ps_filtro As String
        ' ListView que contiene la lista de elementos a utilizar:    ByRef po_lista As ListView
        ' ImageList que contiene las imágenes del ListView:          ByRef po_listadeimagenes As ImageList
        Dim ls_archivo As String
        Dim ln_indice_imagen As Integer
        Dim lo_imagenes As String() = System.IO.Directory.GetFiles(sRuta, ps_filtro)
    
        ' Ligando ListView con el ImageList 
        po_lista.LargeImageList = po_listadeimagenes
        For Each ls_archivo In lo_imagenes
            ' Lee la imagen --> Image.FromFile(ls_archivo) 
            ' y la agrega a la colección de imágenes del ImageList 
            po_listadeimagenes.Images.Add(Image.FromFile(ls_archivo))
            ' Indice de la imagen que se relacionará con el elemento del ListView
            ln_indice_imagen = po_listadeimagenes.Images.Count - 1
            ' Texto que aparecerá en el elemento del ListView
            ' nombre de la imagen sin extensión, ejemplo: cara01
            ls_archivo = System.IO.Path.GetFileName(ls_archivo).ToLower
            ' Agrega a la colección de elementos del ListView y lo relaciona con la imagen
            po_lista.Items.Add(ls_archivo.Replace(".jpg", ""), ln_indice_imagen)
        Next
        ' Se le indica al objeto PrintDocument (pdHojas) el evento que se ejecutará al 
        ' previsualizar el retrato en el objeto PrintPreviewDialog ( ppdPrevisualizacion ) 
        ' El evento es PrintPage y está representado por Imprime_Retrato
        AddHandler pdHojas.PrintPage, AddressOf Me.Imprime_Retrato
        ' Se le indica al objeto PrintPreviewDialog (ppdPrevisualizacion) cuál será el 
        ' documento que deberá imprimir, este documento está representado por 
        ' el objeto PrintDocument (pdHojas)
        ppdPrevisualizacion.Document = pdHojas
    End Sub

    Cómo se dibujan las imágenes en el PictureBox:

    Cuando el usuario selecciona de la barra de herramientas el elemento de la cara que desea dibujar, automáticamente aparece un ListView con todas las imágenes disponibles.
    Cuando el usuario selecciona del ListView una imagen, se ejecuta el evento SelectedIndexChanged del ListView; y desde ahí es ejecutado el método Refresh, el cual hace que el evento Paint del PictureBox se ejecute, esto es porque el método Refresh permite que el área del objeto se actualice y es ahí donde el evento Paint entra en acción.


    Private Sub lvwCara_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
        Handles lvwCara.SelectedIndexChanged, lvwCabello.SelectedIndexChanged, _
        lvwCejas.SelectedIndexChanged, lvwOjos.SelectedIndexChanged, _
        lvwNariz.SelectedIndexChanged, lvwBoca.SelectedIndexChanged, _
        lvwBigote.SelectedIndexChanged, lvwBarba.SelectedIndexChanged
    
        pbCaras.Refresh()
    
    End Sub

    El evento Paint se ejecuta siempre que sea necesario repintar o actualizar cualquier área de un formulario o control.
    En esta aplicación el evento Paint, del PictureBox, lo que hace es ejecutar un método llamado Dibuja_Sospechoso el cual recibe como parámetro una referencia al objeto Graphics en el cual se va a dibujar; este objeto viene como argumento del evento Paint.


    Private Sub pbCaras_Paint(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.PaintEventArgs) Handles pbCaras.Paint
    
        Dibuja_Sospechoso(e.Graphics)
    End Sub

    Cómo se dibuja una imagen sobre otra en el área indicada:

    El siguiente paso es dibujar las partes del retrato. El método Dibuja_Sospechoso limpia el área donde se va a dibujar con el método Clear del objeto Graphics, y luego manda dibujar cada una de las partes que forman el retrato con el método Dibuja_Partes.


    Private Sub Dibuja_Sospechoso(ByVal po_sospechoso As Graphics)
        ' Limpiar el área de dibujo en el PictureBox 
        po_sospechoso.Clear(Color.White)
        ' Dibuja todas las partes de la cara
        Dibuja_Partes(po_sospechoso, lvwCara, ilstCaras, New Rectangle(100, 85, 195, 230), False)
        Dibuja_Partes(po_sospechoso, lvwCabello, ilstCabello, New Rectangle(94, 50, 204, 164), False)
        Dibuja_Partes(po_sospechoso, lvwCejas, ilstCejas, New Rectangle(130, 97, 135, 99), True)
        Dibuja_Partes(po_sospechoso, lvwOjos, ilstOjos, New Rectangle(130, 110, 135, 99), True)
        Dibuja_Partes(po_sospechoso, lvwNariz, ilstNariz, New Rectangle(132, 140, 135, 99), True)
        Dibuja_Partes(po_sospechoso, lvwBoca, ilstBoca, New Rectangle(144, 200, 108, 80), True)
        Dibuja_Partes(po_sospechoso, lvwBigote, ilstBigote, New Rectangle(165, 209, 64, 48), True)
        Dibuja_Partes(po_sospechoso, lvwBarba, ilstBarba, New Rectangle(115, 187, 160, 120), True)
    End Sub

    El método Dibuja_Partes recibe los parámetros necesarios para dibujar cada una de las partes del retrato.
    Primero se determina que elemento del ListView fue seleccionado y despues se extrae del ImageList la imagen que representa ese elemento. Al hacer esto tenemos un objeto Image el cual se utiliza para dibujar el retrato. Pero si lo dibujamos tal cual, las partes del retrato se podrían una sobre otra y ocasionaría que el retrato se viera de la siguiente manera:


    Para poder manejar la transparencia fue necesario convertir el objeto Image a Bitmap para poder utilizar el método MakeTransparent y conseguir el efecto de transparencia.


    Utilizando el método MakeTransparent del objeto BitMap


    Private Sub Dibuja_Partes(ByRef po_sospechoso As Graphics, _
        ByVal po_lista As ListView, _
        ByVal po_listadeimagenes As ImageList, _
        ByVal po_areadedibujo As Rectangle, _
        ByVal pb_estransparente As Boolean)
        ' Área del picturebox donde se va a dibujar :   ByRef po_sospechoso As Graphics
        ' ListView con la parte de la cara a dibujar:   ByVal po_lista As ListView
        ' ImageList con la lista de imágenes:           ByVal po_listadeimagenes As ImageList
        ' Área donde será dibujada la parte de la cara: ByVal po_areadedibujo As Rectangle
        ' Indica si la imagen es transparente o no:     ByVal pb_estransparente As Boolean
    
        ' Declara el objeto Bitmap utilizado para dibujar
        Dim lo_trans As Bitmap
        ' Valida si fue seleccionado un elemento de esa parte de la cara
        ' Esto es para ir dibujando solo las partes seleccionadas por el usuario
        If po_lista.SelectedItems.Count = 1 Then
            ' Convierte la imagen del ImageList a un objeto Bitmap
            lo_trans = CType(po_listadeimagenes.Images(po_lista.SelectedItems(0).ImageIndex), Bitmap)
            ' Pregunta si el bitmap será transparente
            If pb_estransparente Then
                lo_trans.MakeTransparent(Color.White)
            End If
            ' Se dibuja en bitmap en el picturebox en el área X,Y,W,H indicada
            po_sospechoso.DrawImage(lo_trans, po_areadedibujo)
        End If
    End Sub

    Cómo imprimir el retrato:

    Para manejar la impresión se utilizaron los objetos: PrintDocument y PrintPreviewDialog.
    Primero asociamos el evento PrintPage del objeto PrintDocument (pdHojas) al controlador del evento Imprime_Retrato por medio de la instrucción AddHandler.

            
        AddHandler pdHojas.PrintPage, AddressOf Me.Imprime_Retrato
    

    Despues se establece el documento del que se desea la vista previa del objeto PrintPreviewDialog (ppdPrevisualizacion).
            
        ppdPrevisualizacion.Document = pdHojas
    

    El controlador del evento, Imprime_Retrato, que representa el evento PrintPage del objeto PrintDocument se produce cuando se necesita el resultado que se va a imprimir para la página actual.

    Al igual que el evento Paint, el evento PrintPage recibe como parámetro una referencia al objeto Graphics utilizado para dibujar en la página. Así que el tratamiento que le podemos dar es el mismo del evento Paint, explicado anteriormente.

    La propiedad HasMorePages se utiliza para indicar si existen o no páginas para imprimir. En la línea e.HasMorePages = False se indica que no hay más página por imprimir.


    Private Sub Imprime_Retrato(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
    
        ' Dibuja en el área del PrintDocument donde se va a dibujar el retrato
        Dibuja_Sospechoso(e.Graphics)
    
        ' Indica al objeto PrintDocument que no hay más páginas que imprimir 
        e.HasMorePages = False
    End Sub

    Al ejecutar el siguiente código :

    ppdPrevisualizacion.ShowDialog(Me)

    Es desplegada la ventana de previsualización para imprimir el retrato:





    Espero que este artículo les sea de utilidad; por favor colabora conmigo votando por este artículo.

    Gracias... ;)


    ir al índice


    Fichero con el código de ejemplo VB.NET: VYDA_Graficos_Caras_VBdotNET.zip - 198 KB

    Fichero con el código de ejemplo C#:
    VYDA_Graficos_Caras_CSharp.zip - 198 KB


    Si te interesa saber más de gráficos puedes ver el siguiente tutorial: Gráficos vectoriales con Visual Basic .NET


    Culiacán, Sinaloa, México