jueves, 27 de octubre de 2016

Eliminación de fondo (Background removal) con Java/ ImageJ


Introducción

La eliminación de fondo (en inglés background removal), es una operación esencial en el tratamiento de imágenes y extracción de características en sistemas de visión de computadoras.

Habitualmente, esta operación es parte de un algoritmo general para la clasificación de objetos y extracción de características a partir de una imagen de entrada [SuryaPrabha2015].

En este artículo presenta en forma práctica la aplicación de operaciones morfológicas y de eliminación de fondo en imágenes de naranjas obtenidas aplicadas a imágenes de naranjas obtenidas desde una base de datos de imágenes pública "Banco de Imágenes: Fotos Libres de Derechos y Vectores".

Se implementó desarrollando la solución con código Java, utilizando ImageJ como librería de procesamiento de imágenes y Netbeans IDE 8.1 como entorno de desarrollo.


¿Para qué sirve la eliminación de fondo?

¿Para qué sirve la eliminación de fondo?, en primera instancia, permite aislar regiones que se desean analizar, calcular áreas de pixeles o formas geométricas en imágenes.

A continuación algunos ejemplos: 
  • Cálculo de área en pixeles. Es posible asociar unidades de medida de distancia con pixeles mediante una escala. Un caso es obtener el calibre de un objeto.
  • Extracción de color en zona en una región a analizar. La extracción hace más fácil el análisis de uno o varios colores en una zona elegida. Un ejemplo sería conocer el color predominante en una fruta para su clasificación.
  • Asociación de varias regiones en una imagen de entrada. Es posible, a partir de una imagen, obtener varios objetos y compararlos con patrones almacenados. En una fotografía con varios tornillos de diferentes tamaños, una tarea sería seleccionarlos, rotarlos y compararlos contra una base de datos.


Un documento interesante que explica la eliminación de fondo es [SuryaPrabha2015], el mismo aplica el proceso para la clasificación de bananas.


Algoritmo e implementación

El código fuente de la solución en lenguaje Java puede ser descargado desde el enlace la cuenta de GitHub "SeparacionFondo".

Se tomaron imágenes de naranjas en buen estado y en estado de descomposición desde una fuente de datos pública "Banco de Imágenes: Fotos Libres de Derechos y Vectores".
Se agruparon las operaciones de procesamiento de imágenes en una clase con las secuencias. Este proceso fue denominado "Secuencias Morfológicas".

Como ayuda visual para la prueba de la aplicación de las operaciones se utilizó Fiji ("Fiji Is Just ImageJ"), el cual cuenta con un grabador de macros para facilitar la conversión a lenguaje Java de las operaciones aplicadas.

Por último, se procedió a copiar los pixeles desde la imagen a color original, utilizando como máscara los datos obtenidos en la imagen binaria. Estas operaciones se agruparon en un proceso llamado "Copiado de Imagen".

Las imágenes elegidas cuentan con un contraste alto, de manera a facilitar la extracción de la fruta. Se obtuvieron buenos resultados con imágenes de fondo oscuro Fig.1 y de fondo blanco Fig.2, Fig.3, no obstante también se probó con una imagen cuyo fondo no era uniforme y los resultados no fueron óptimos comparados con las imágenes anteriores, Fig.4 y Fig.5.

Las imágenes utilizadas, sirven a modo pedagógico, en la elección de las mismas no se tomaron en cuenta ángulos de enfoque y métodos relacionados a la adquisición uniforme de imágenes, aunque se recomienda que este proceso responda a una metodología estandarizada.

Fig. 1


Fig. 2

Fig. 3


Fig. 4

Fig. 5

En el proceso "Secuencia Morfológica", se aplicaron las operaciones:
  • Resaltado de bordes.
  • Binarización. 
  • Dilatación.
  • Relleno de silueta.
  • Cerradura.   
Para el relleno de la silueta, se realizó un barrido del área de la fruta utilizando dos variables apuntadoras para las posiciones de las columnas. Donde el valor 255 representa al color BLANCO y el valor 0 al color NEGRO. Básicamente lo que se hace es establecer valores de tope en un fila y rellenar con un valor correspondiente al color blanco.

El resultado obtenido es una imagen binaria con la silueta, la cual fue utilizada como entrada para el siguiente proceso \textbf{"Copiado de Imagen"}. Los resultados obtenidos pueden visualizarse en las figuras Fig.6, Fig.7, Fig.8, Fig.9, Fig.10.


Fig. 7

Fig. 8

Fig. 9

Fig. 10

Fig. 11

Conclusión

Luego de las pruebas realizadas sobre las imágenes de naranjas, es recomendable utilizar imágenes con un fondo que contraste con la fruta sobre la cual se desea trabajar para eliminar el fondo.

Bibliografía

1. Banco de imágenes: Fotos libres de derechos y vectores. http://es.123rf.com/. [Web; accedido el 26-10-2016].
2. Fiji. http://imagej.net/Fiji. [Web; accedido el 28-09-2016].
3. Imagej. http://imagej.net/ImageJ. [Web; accedido el 28-09-2016].
4 Netbeans ide. http://netbeans.org/. [Web; accedido el 26-10-2016].
5. Proyecto para netbeans SeparacionFondo. https://github.com/freelanceparaguay/javaImageJ/tree/master/SeparacionFondo. [Web; accedido el 26-10-2016].
6. D. Surya Prabha and J. Satheesh Kumar. Assessment of banana fruit maturity by image processing technique. Journal of Food Science and Technology, 52(3):1316–1327, 2015. http://dx.doi.org/10.1007/s13197-013-1188-3

 

lunes, 10 de octubre de 2016

Java:ImageJ: Enlaces a recursos y ejemplos de Java / ImageJ

Se irán agrupando enlaces y ejemplos relacionados al uso de la librería ImageJ para el procesamiento digital de imágenes (PDI).

Java ImageJ: Procesamiento Digital de Imágenes con Java, Ejercicio Cebra

Anteriormente se realizó la programación del ejercicio de la cebra utilizando operaciones morfológicas e implementando el código en Matlab/Octave, el artículo explicativo se encuentra aquí. Descargar desde github el ejemplo Matlab/Octave.

En un artículo anterior, se hizo mención a la librería ImageJ y sus características.
Esta es una versión del ejercicio de procesamiento digital de imágenes (PDI) utilizando Java + Netbeans IDE 8.1. Para acompañar, se ha incluido el código Java utilizando la librería ImageJ.

La idea es obtener las líneas de contorno de la cebra a partir de una imagen original en escala de grises.

Se parte desde una imagen en escala de grises, la cebra contiene las líneas verticales que se desean eliminar.
Cebra en escala de grises
El resultado al cual se desea llegar, es una figura con el contorno de líneas en escala de colores binaria similar a la siguiente imagen.

Contorno de líneas

Secuencias de imágenes obtenidas en según los pasos aplicados.
  • Binarización.
  • Erosión.
  • Erosión.
  • Operación XOR.
Secuencia de imágenes

Operaciones básicas

La aplicación de las operaciones morfológicas básicas se sintetizan en estas intrucciones:
  • IJ.run(IB2, "Make Binary", ""); //convertir a imagen binaria 
  • IJ.run(IB3, "Options...", "iterations=1 count=1 do=Erode"); //erosionar        IJ.run(IB4, "Options...", "iterations=1 count=2 do=Erode"); //erosionar con mayor iteraciones        
  • ImagePlus IB5 = ic.run("XOR create", IB3, IB4);//operacion de calculo XOR entre imagenes

Código Java - ImagenJ

A continuación el código del plugin.

//------------------------------- CORTAR AQUI ---------------------------
package variaspruebasimagej;

import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.io.Opener;
import java.awt.*;
import ij.plugin.*;

/*
www.otroblogdetecnologias.blogspot.com
A partir de una imagen original establece la secuencia de pasos para obtener el
contorno de la cebra quitando las lineas verticales.
Se aplican operaciones para lograr un procesamiento de imágenes.
*/

public class My_secuencia_cebra implements PlugIn {

    public void run(String arg) {
                //apertura de imagen
                Opener opener = new Opener();               
                String directorioActualDefault = System.getProperty("user.dir"); //estableciendo el directorio del proyecto
                String imageFilePathDefault = directorioActualDefault+"/Cebra.png";
                ImagePlus Iorig= opener.openImage(imageFilePathDefault);           
           
    //    ImagePlus Iorig = IJ.openImage("/home/usuario/materialMaestria/2016trabajoTesisMaestria/javaCodigo/variasPruebasImageJ/Cebra.png");
                ImagePlus IB2 = Iorig.crop();
        Prefs.blackBackground = false;
        IJ.run(IB2, "Make Binary", ""); //convertir a imagen binaria

                ImagePlus IB3 = IB2.crop();
        IJ.run(IB3, "Options...", "iterations=1 count=1 do=Erode"); //erosionar

                ImagePlus IB4 = IB2.crop();
        IJ.run(IB4, "Options...", "iterations=1 count=2 do=Erode"); //erosionar con mayor iteraciones
       
               
                ImageCalculator ic = new ImageCalculator();

        ImagePlus IB5 = ic.run("XOR create", IB3, IB4);//operacion de calculo XOR entre imagenes

                //--- Mostrar imagenes ---
        Iorig.show();Iorig.setTitle("1) Original Gris");               
                IB2.show(); IB2.setTitle("2) Binarización");
                IB3.show(); IB3.setTitle("3) Erosión iteración=1 contador=1");              
        IB4.show(); IB4.setTitle("4) Erosión iteración=1 contador=2");                                              
        IB5.show(); IB5.setTitle("3) XOR 4)");

    }//run

}//fin clase

//------------------------------- CORTAR AQUI ---------------------------


//
//------------------------ MAIN CORTAR AQUI ---------------------------
package variaspruebasimagej;

import ij.ImagePlus;
import ij.plugin.filter.PlugInFilter;
import ij.io.Opener;
import ij.process.ImageProcessor;
import ij.IJ;

/*
 este es el programa principal, la lógica que se plantea es la de poder explicar
 con ejemplos las funcionalidades básicas. Los ejemplos se estructuran en plugins
*/
public class PrincipalImageJ {

    public static void main(String[] args) {
        //-----------------------------------
        // Declaracion de Plugins
        //-----------------------------------

        My_secuencia_cebra ej3=new My_secuencia_cebra();
       
        //-----------------------------------------
        // Declaracion de variables iniciales
        //-----------------------------------------
        Opener opener = new Opener();               
        String directorioActualDefault = System.getProperty("user.dir"); //estableciendo el directorio del proyecto
        //String imageFilePathDefault = directorioActualDefault+"/barco.jpg";       
        String imageFilePathDefault = directorioActualDefault+"/Cebra.png";
        ImagePlus imagenDefault= opener.openImage(imageFilePathDefault);
        ImageProcessor imagenPDefault= imagenDefault.getProcessor();
               
        ej3.run("");

    }//main
   
}//FIN clase

//------------------------ MAIN CORTAR AQUI --------------------------- 



jueves, 6 de octubre de 2016

Procesamiento digital de imágenes, Intro a Java + ImageJ

 Contenidos


 Introducción


Durante la década de los 1980’s, el acceso a imágenes era costoso, la situación que se presentaba en aquel entonces era la falta de formatos estándar para la representación de las imágenes. Por otro lado, las librerías de acceso no se encontraban documentadas y abiertas a los programadores. Prácticamente pasaban meses antes de que se pudiera realizar un procesamiento de imágenes con operaciones elementales.
Actualmente existen librerías para C/C++, Java, y otros lenguajes; los cuales cuentan con soporte para trabajar con imágenes. También existen varias herramientas visuales software para manipular y procesar archivos de imágenes.

Las herramientas visuales para manipular archivos de imágenes, se encuentran orientadas a profesionales y diseñadores, posibilitan realizar cambios y manipular imágenes. En algunos casos, se pueden extender las características incluyendo el uso de scripts.

Las herramientas para procesamiento digital de imágenes enfatizan en algoritmos específicos y se encuentran enfocadas a programadores, científicos e ingenieros que trabajan con imágenes, donde se puede decir que la facilidad de uso y la interactividad no son los focos principales. En estos entornos se hace énfasis en librerías bien documentadas para el procesamiento de imágenes.

En base a lo anterior, se hace una distinción en las funcionalidades del software relacionado al área:
  • Software de manipulación de imágenes.
  • Software de procesamiento de imágenes.
El software de manipulación de imágenes, pueden ser utilizado por usuarios que no cuentan con conocimientos de programación, a la vez estas herramientas utilizan algoritmos pertenecientes a la categoría de procesamiento de imágenes. Bajo la funcionalidad de manipulación de imágenes se encuentra Corel Draw, GIMP, InkScape, Photoshop.

El software de procesamiento de imágenes, incluye los algoritmos que luego son utilizados en manipulación de imágenes. En la categoría de procesamiento de imágenes se agrupan opciones como Matlab, ImageJ.


Codificación en Java, ejemplo de un plugin

Las modalidades para escribir programas que procesen una imagen, pueden ser incuso hasta abrumadoras, mas cuando previamente se han trabajado con software como Matlab u Octave en donde el trabajo de elaborar una secuencia simple no representa gran complejidad.

ImageJ posee muchísimas herramientas y varias funcionalidades pueden ser creadas desde cero según la necesidad de la tarea. El objeto de esta sección es orientar desde un ejemplo práctico para codificar un plugin y llamarlo desde una clase main en Java.

Los ejemplos de plugins y su código han sido tomados de http://imagej.net/Introduction_into_Developing_Plugins

Ejemplo para Java utilizando la clase PluginFilter

public class My_Plugin implements PlugInFilter {
  public int setup ( String arg , ImagePlus image ) {
    return DOES_ALL;
  }
  public void run ( ImageProcessorip ) {
    // Aqui secuencias de comandos
  }
}//fin My_Plugin


Ejemplo para Java utilizando la clase Plugin.

public class My_Plugin implements PlugIn {
  public void run ( String arg ) {
    // Aqui secuencias de comandos
  }
}//fin My_Plugin


A continuación un ejemplo de llamada desde una clase main en Java.

import ij.*;
public class VariasPruebasImageJ {
    public static void main(String[] args) {
        //-----------------------------------
        My_Plugin mi = new My_Plugin(); //clase del plugin definida
        Opener opener = new Opener(); 
       
        //estableciendo el directorio del proyecto
        String directorioActual = System.getProperty("user.dir");
        //-----------------------------------------
        //apertura de la imagen segun directorio
        String imageFilePath = directorioActual+"/cebra.png";
        ImagePlus imp2 = opener.openImage(imageFilePath);
        ImageProcessor ip2 = imp2.getProcessor(); // ImageProcessor from ImagePlus
        //-----------------------------------------
       
        //-----------------------------------------
        //ejecucion del plugin
        // ----------------------------------------
        imp2.show(); //ImagePlus es para mostrar la imagen
        mi.run(ip2); //llamada al plugin
        imp2.updateAndDraw();
        imp2.show();
    } //fin main   
}//fin clase


Clases para el desarrollo de plugins. ¿Cuando utilizar PlugIn PlugInFilter?


Existen dos modalidades de herencia para desarrollar plugins:
  • PlugIn, no requiere una imagen abierta para comenzar la ejecución.
  • PlugInFilter, requiere una imagen activa y que la misma sea pasada cuando comienza la ejecución. Esta clase, requiere que se implementen dos métodos setup() y run().
Se utiliza PlugIn para tareas que crean, cargan y graban imágenes, o que realizan operaciones sin imágenes abiertas.

Se utiliza PlugInFilter, es utilizado para procesar imágenes abiertas con la herramienta visual.

La finalidad que busca este documento es poder llegar a estructurar secuencias de operaciones, para este fin ambas clases mencionadas sirven.

Funciones/ tipos básicos en el desarrollo de plugins


A continuación se hace una breve descripción de los métodos y tipos que deben ser definidos en un plugin que utilice ImageJ.
  • setup() , establece las configuraciones para la ejecución. El método int setup(String args, ImagePlus im)
  • posibilita la recepción de parámetros que utilizará el plugin.
  • run() , es el método principal del pluging, similar a un método main en Java. La sentencia de declaración del método void run(ImageProcessor ip), recibe una imagen como parámetros.
Los tipos básicos utilizados son:
  • DOES_ALL, especifica que la función puede trabajar con cualquier tipo de imagen.
  • DOES_8G, especifica que se trabajará con una imagen de escala de grises de 8-bit.
  • ImagePlus, clase utilizada para cargar la imagen desde una ruta específica.
  • ImageProcessor, el elemento contiene la imagen a ser procesada y permite el acceso a funciones de modificación.

¿Conviene utilizar un lenguaje script?


Este es un buen punto para establecer, depende especialmente de la tarea que se desee realizar. Un motivo por el cual se pueden utilizar scripts es para prototipar secuencias de pasos y verificar si el proceso funciona. ImageJ tiene muchos lenguajes que pueden ser utilizados para el desarrollo de scripts, ver http://imagej.net/Scripting.

Una tarea tediosa, es crear las secuencias de operaciones que se aplicarán a una imagen o a un conjunto de imágenes, sin poder ver los resultados, algo que puede solucionarse utilizando una herramienta visual. Para esto, se ha creado la opción visual para grabación de macros, con esta funcionalidad se pueden traspasar secuencias de comandos a clases java.

Otros recursos