Leed las notas antes que nada.
- Archivo principal del algorismo.
- Clase de ejecución por línea de comando que mustra imágenes.
- Clase de la GUI de ejecución (necesita .jar de NetBeans!)
NOTAS:
. CUIDADO:
/! El archivo "swing-layout-1.0.jar" es ESENCIAL para que funcione la GUI, pues es la API de NetBeans para
^^^ hacer ventanas. No se ha de borrar o automaticamente no se podrá ni compilar ni ejecutar la práctica.
Para las tareas de ejecución y compilado de la práctica se han incluído sendos scripts, tanto .sh para operativos UNIX-like como .bat para operativos de Micro$soft.
Compilación Linux:
CLASSPATH=".:swing-layout-1.0.jar" javac *.java
Ejecución Linux:
CLASSPATH=".:swing-layout-1.0.jar" java Menu1
Compilación Windows:
set CLASSPATH=".;swing-layout-1.0.jar"
javac *.java
Ejecución Windows:
set CLASSPATH=".;swing-layout-1.0.jar"
java Menu1
Al ejecutar la práctica aparece una GUI.
También se puede ejecutar el k-menas sin la GUI, mediante la clase detecta.java, en cuyo caso no necesitaremos el .jar adjunto. La sintaxis es:
java detecta ruta_de_la_imagen numero_de_colores
Además, si ponemos cualquier cosa como 3er argumento se ofrecerá muchísima información del proceso.
kmeans.java:/! El archivo "swing-layout-1.0.jar" es ESENCIAL para que funcione la GUI, pues es la API de NetBeans para
^^^ hacer ventanas. No se ha de borrar o automaticamente no se podrá ni compilar ni ejecutar la práctica.
Para las tareas de ejecución y compilado de la práctica se han incluído sendos scripts, tanto .sh para operativos UNIX-like como .bat para operativos de Micro$soft.
Compilación Linux:
CLASSPATH=".:swing-layout-1.0.jar" javac *.java
Ejecución Linux:
CLASSPATH=".:swing-layout-1.0.jar" java Menu1
Compilación Windows:
set CLASSPATH=".;swing-layout-1.0.jar"
javac *.java
Ejecución Windows:
set CLASSPATH=".;swing-layout-1.0.jar"
java Menu1
Al ejecutar la práctica aparece una GUI.
También se puede ejecutar el k-menas sin la GUI, mediante la clase detecta.java, en cuyo caso no necesitaremos el .jar adjunto. La sintaxis es:
java detecta ruta_de_la_imagen numero_de_colores
Además, si ponemos cualquier cosa como 3er argumento se ofrecerá muchísima información del proceso.
import java.io.*;
import java.util.*;
public class kmeans {
/** Numero de clusters */
private int k;
/** Debug por consola */
boolean debug=false;
/** Paleta final, en las mismas posiciones que los clusters */
int[] paleta;
/** Array de clusters */
private int[] clusters;
/** Numero de iteraciones (log) */
private int nIterations;
/** Vector de pixeles */
private int[] pixeles;
/** Tamanyo del vector de pixeles **/
private int tamanyo;
/** Asignacion a cluster de los pixeles; sirve para sacar la imagen recompuesta **/
private int[] asignacion;
/**
* Constructor kmeans: kmeans( k, img, tamanyo, debug)
* k = numero de clases a encontrar
* img = pixeles de la imagen en un int[]
* tamanyo = tamanyo de la imagen en pixeles, igual que tamanyo de img
* debug = bool, activa el debug
*/
public kmeans(int k, int[] pixeles, int tamanyo, boolean debug) {
int ii=0;
this.debug = debug;
this.k = k;
this.nIterations = 0;
this.tamanyo = tamanyo;
this.clusters = new int[k];
this.paleta = new int[k];
this.asignacion = new int[this.tamanyo];
this.pixeles = new int[tamanyo];
/** Inicializamos los clusters con los primeros k pixeles no repetidos **/
for( int i=0; i < tamanyo; i++) this.pixeles[i] = (pixeles[i] & 0x00FFFFFF);
/** Cogemos los 1os k colores diferentes como iniciales de las clases **/
int cluster=0;
for( int i=0; i<this.tamanyo && cluster < this.k; i++) {
boolean continuar=true;
for( ii = 0; ii < cluster && continuar == true; ii++) if( this.pixeles[i] == this.clusters[ii]) continuar=false;
if( continuar == true ) {
this.clusters[cluster] = this.pixeles[i];
if(debug) System.out.println("Cluster inicial " + cluster + ", pixel " + i + ": rojo=" + ( (this.clusters[cluster] & 0xFF0000) / 0x10000) +"; verde=" + ( (this.clusters[cluster] & 0x00FF00) / 0x100) + " azul=" + (this.clusters[cluster] & 0x0000FF) );
cluster++;
}
}
runkmeans();
} // fin del kmeans()
/**
* Runs the k-means algorithm over the data set
*/
private void runkmeans() {
int[] clustersant=new int[this.k];
int[] clusterstmpR = new int[this.k];
int[] clusterstmpG = new int[this.k];
int[] clusterstmpB = new int[this.k];
int[] clusterstmpn = new int[this.k];
int distanciamin,dtmp;
int pixeli,clusteri,indexmin;
boolean mismocluster;
do {
mismocluster = true;
/**
Para cada pixel encontramos su cluster
**/
for(pixeli=0; pixeli < this.tamanyo; pixeli++) {
distanciamin=distancia(this.clusters[0],this.pixeles[pixeli]);
indexmin=0;
/**
Para todos los clusters, calculamos la distáncia y escogemos
la minima como cluster al que pertenece ese pixel
**/
for( clusteri=0; clusteri<this.k; clusteri++) {
dtmp=distancia(this.pixeles[pixeli],this.clusters[clusteri]);
if( dtmp < distanciamin ) {
distanciamin = dtmp;
indexmin = clusteri;
}
}
this.asignacion[pixeli] = indexmin;
} // fin de analisis de pixeles
/**
Ponemos a 0 el acumulador temporal...
**/
for(clusteri=0; clusteri<this.k; clusteri++){
clusterstmpR[clusteri]=0;
clusterstmpG[clusteri]=0;
clusterstmpB[clusteri]=0;
clusterstmpn[clusteri]=0;
}
/**
Para los pixeles reagrupados, ahora encontramos el nuevo 'centro de
gravedad', que sera la nueva ubicacion del cluster
Para ello usamos unos acumuladores donde iremos sumando
**/
for(pixeli=0; pixeli<this.tamanyo; pixeli++){
clusterstmpR[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x00FF0000) / 0x10000;
clusterstmpG[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x0000FF00) / 0x100;
clusterstmpB[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x000000FF);
clusterstmpn[this.asignacion[pixeli]]++;
}
for(clusteri=0; clusteri<this.k; clusteri++){
if( clusterstmpn[clusteri] > 0 ) {
clusterstmpR[clusteri]=clusterstmpR[clusteri]/clusterstmpn[clusteri];
clusterstmpG[clusteri]=clusterstmpG[clusteri]/clusterstmpn[clusteri];
clusterstmpB[clusteri]=clusterstmpB[clusteri]/clusterstmpn[clusteri];
clustersant[clusteri] = this.clusters[clusteri];
this.clusters[clusteri] = clusterstmpR[clusteri]*0x10000 + clusterstmpG[clusteri]*0x100 + clusterstmpB[clusteri];
if( clusters[clusteri] != clustersant[clusteri] ) mismocluster = false;
}
else System.out.println("/! Cluster "+clusteri+" con 0 pixeles!");
}
if( debug ) {
System.out.println("> Iteracion " + nIterations );
for( int ii = 0; ii < this.k ; ii++) System.out.println(" -cluster " + ii + ": rojo=" + ( (this.clusters[ii] & 0xFF0000) / 0x10000) +"; verde=" + ( (this.clusters[ii] & 0x00FF00) / 0x100) + " azul=" + (this.clusters[ii] & 0x0000FF) );
}
this.nIterations++;
} while ( !mismocluster );
if( debug ) System.out.println("-- usadas " + this.nIterations + " iteraciones--");
} // end of runkmeans()
/**
* Para ahorrar tiempo de computo calculamos la distancia al cuadrado
* Esto no afecta a las comparaciones, que es lo que nos interesa
* Asi nos ahorramos hacer la raiz cuadrada
*/
private int distancia(int puntoa, int puntob) {
int resultR = ( (puntoa & 0x00FF0000) - (puntob & 0x00FF0000) ) / 0x10000;
int resultG = ( (puntoa & 0x0000FF00) - (puntob & 0x0000FF00) ) / 0x100;
int resultB = (puntoa & 0x000000FF) - (puntob & 0x000000FF);
return (resultR*resultR + resultG*resultG + resultB*resultB);
} // fin de distancia()
/**
* Retorna el valor de k, el numero de clusters
*/
public int getK() {
return this.k;
} // end of getK()
/**
* Retorna los clusters en un array de int's
*/
public int[] getClusters() {
return this.clusters;
} // end of getCluster()
/**
* Retorna la imagen transformada por kmeans a 'k' colores cualquiera.
*/
public int[] getkImage() {
int i=0;
int[] ret=new int[this.tamanyo];
for( i=0; i<this.tamanyo; i++) ret[i]= 0xFF000000 + this.clusters[this.asignacion[i]];
return ret;
}
/**
* Retorna la imagen transformada por kmeans a 'k' colores (max) de la paleta.
*/
public void setPaleta(int[] paleta, int l) {
int distanciamin,indexmin,dtmp,clusteri,paletai;
/**
* Para todos los clusters, calculamos la distáncia a la paleta dada
* y escogemos la minima como color al que pertenece ese cluster
*/
for( clusteri=0; clusteri<this.k; clusteri++) {
distanciamin=distancia(this.clusters[clusteri],paleta[0]);
indexmin=0;
for( paletai=0; paletai < l; paletai++) {
dtmp=distancia(paleta[paletai],this.clusters[clusteri]);
if( dtmp < distanciamin ) {
distanciamin = dtmp;
indexmin = paletai;
}
}
this.paleta[clusteri] = paleta[indexmin];
}
}
/**
* Retorna la imagen transformada por kmeans a 'k' colores (max) de la paleta
*/
public int[] getkImagep() {
int[] ret=new int[this.tamanyo];
for( int i=0; i<this.tamanyo; i++) ret[i]= 0xFF000000 | this.paleta[this.asignacion[i]];
return ret;
}
/**
* Retorna la paleta que usa la imagen transformada con el kmeans (k colores de los 11)
*/
public int[] getPaleta() {
return this.paleta;
}
} // final de la calse kmeans
import java.util.*;
public class kmeans {
/** Numero de clusters */
private int k;
/** Debug por consola */
boolean debug=false;
/** Paleta final, en las mismas posiciones que los clusters */
int[] paleta;
/** Array de clusters */
private int[] clusters;
/** Numero de iteraciones (log) */
private int nIterations;
/** Vector de pixeles */
private int[] pixeles;
/** Tamanyo del vector de pixeles **/
private int tamanyo;
/** Asignacion a cluster de los pixeles; sirve para sacar la imagen recompuesta **/
private int[] asignacion;
/**
* Constructor kmeans: kmeans( k, img, tamanyo, debug)
* k = numero de clases a encontrar
* img = pixeles de la imagen en un int[]
* tamanyo = tamanyo de la imagen en pixeles, igual que tamanyo de img
* debug = bool, activa el debug
*/
public kmeans(int k, int[] pixeles, int tamanyo, boolean debug) {
int ii=0;
this.debug = debug;
this.k = k;
this.nIterations = 0;
this.tamanyo = tamanyo;
this.clusters = new int[k];
this.paleta = new int[k];
this.asignacion = new int[this.tamanyo];
this.pixeles = new int[tamanyo];
/** Inicializamos los clusters con los primeros k pixeles no repetidos **/
for( int i=0; i < tamanyo; i++) this.pixeles[i] = (pixeles[i] & 0x00FFFFFF);
/** Cogemos los 1os k colores diferentes como iniciales de las clases **/
int cluster=0;
for( int i=0; i<this.tamanyo && cluster < this.k; i++) {
boolean continuar=true;
for( ii = 0; ii < cluster && continuar == true; ii++) if( this.pixeles[i] == this.clusters[ii]) continuar=false;
if( continuar == true ) {
this.clusters[cluster] = this.pixeles[i];
if(debug) System.out.println("Cluster inicial " + cluster + ", pixel " + i + ": rojo=" + ( (this.clusters[cluster] & 0xFF0000) / 0x10000) +"; verde=" + ( (this.clusters[cluster] & 0x00FF00) / 0x100) + " azul=" + (this.clusters[cluster] & 0x0000FF) );
cluster++;
}
}
runkmeans();
} // fin del kmeans()
/**
* Runs the k-means algorithm over the data set
*/
private void runkmeans() {
int[] clustersant=new int[this.k];
int[] clusterstmpR = new int[this.k];
int[] clusterstmpG = new int[this.k];
int[] clusterstmpB = new int[this.k];
int[] clusterstmpn = new int[this.k];
int distanciamin,dtmp;
int pixeli,clusteri,indexmin;
boolean mismocluster;
do {
mismocluster = true;
/**
Para cada pixel encontramos su cluster
**/
for(pixeli=0; pixeli < this.tamanyo; pixeli++) {
distanciamin=distancia(this.clusters[0],this.pixeles[pixeli]);
indexmin=0;
/**
Para todos los clusters, calculamos la distáncia y escogemos
la minima como cluster al que pertenece ese pixel
**/
for( clusteri=0; clusteri<this.k; clusteri++) {
dtmp=distancia(this.pixeles[pixeli],this.clusters[clusteri]);
if( dtmp < distanciamin ) {
distanciamin = dtmp;
indexmin = clusteri;
}
}
this.asignacion[pixeli] = indexmin;
} // fin de analisis de pixeles
/**
Ponemos a 0 el acumulador temporal...
**/
for(clusteri=0; clusteri<this.k; clusteri++){
clusterstmpR[clusteri]=0;
clusterstmpG[clusteri]=0;
clusterstmpB[clusteri]=0;
clusterstmpn[clusteri]=0;
}
/**
Para los pixeles reagrupados, ahora encontramos el nuevo 'centro de
gravedad', que sera la nueva ubicacion del cluster
Para ello usamos unos acumuladores donde iremos sumando
**/
for(pixeli=0; pixeli<this.tamanyo; pixeli++){
clusterstmpR[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x00FF0000) / 0x10000;
clusterstmpG[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x0000FF00) / 0x100;
clusterstmpB[this.asignacion[pixeli]] += (this.pixeles[pixeli] & 0x000000FF);
clusterstmpn[this.asignacion[pixeli]]++;
}
for(clusteri=0; clusteri<this.k; clusteri++){
if( clusterstmpn[clusteri] > 0 ) {
clusterstmpR[clusteri]=clusterstmpR[clusteri]/clusterstmpn[clusteri];
clusterstmpG[clusteri]=clusterstmpG[clusteri]/clusterstmpn[clusteri];
clusterstmpB[clusteri]=clusterstmpB[clusteri]/clusterstmpn[clusteri];
clustersant[clusteri] = this.clusters[clusteri];
this.clusters[clusteri] = clusterstmpR[clusteri]*0x10000 + clusterstmpG[clusteri]*0x100 + clusterstmpB[clusteri];
if( clusters[clusteri] != clustersant[clusteri] ) mismocluster = false;
}
else System.out.println("/! Cluster "+clusteri+" con 0 pixeles!");
}
if( debug ) {
System.out.println("> Iteracion " + nIterations );
for( int ii = 0; ii < this.k ; ii++) System.out.println(" -cluster " + ii + ": rojo=" + ( (this.clusters[ii] & 0xFF0000) / 0x10000) +"; verde=" + ( (this.clusters[ii] & 0x00FF00) / 0x100) + " azul=" + (this.clusters[ii] & 0x0000FF) );
}
this.nIterations++;
} while ( !mismocluster );
if( debug ) System.out.println("-- usadas " + this.nIterations + " iteraciones--");
} // end of runkmeans()
/**
* Para ahorrar tiempo de computo calculamos la distancia al cuadrado
* Esto no afecta a las comparaciones, que es lo que nos interesa
* Asi nos ahorramos hacer la raiz cuadrada
*/
private int distancia(int puntoa, int puntob) {
int resultR = ( (puntoa & 0x00FF0000) - (puntob & 0x00FF0000) ) / 0x10000;
int resultG = ( (puntoa & 0x0000FF00) - (puntob & 0x0000FF00) ) / 0x100;
int resultB = (puntoa & 0x000000FF) - (puntob & 0x000000FF);
return (resultR*resultR + resultG*resultG + resultB*resultB);
} // fin de distancia()
/**
* Retorna el valor de k, el numero de clusters
*/
public int getK() {
return this.k;
} // end of getK()
/**
* Retorna los clusters en un array de int's
*/
public int[] getClusters() {
return this.clusters;
} // end of getCluster()
/**
* Retorna la imagen transformada por kmeans a 'k' colores cualquiera.
*/
public int[] getkImage() {
int i=0;
int[] ret=new int[this.tamanyo];
for( i=0; i<this.tamanyo; i++) ret[i]= 0xFF000000 + this.clusters[this.asignacion[i]];
return ret;
}
/**
* Retorna la imagen transformada por kmeans a 'k' colores (max) de la paleta.
*/
public void setPaleta(int[] paleta, int l) {
int distanciamin,indexmin,dtmp,clusteri,paletai;
/**
* Para todos los clusters, calculamos la distáncia a la paleta dada
* y escogemos la minima como color al que pertenece ese cluster
*/
for( clusteri=0; clusteri<this.k; clusteri++) {
distanciamin=distancia(this.clusters[clusteri],paleta[0]);
indexmin=0;
for( paletai=0; paletai < l; paletai++) {
dtmp=distancia(paleta[paletai],this.clusters[clusteri]);
if( dtmp < distanciamin ) {
distanciamin = dtmp;
indexmin = paletai;
}
}
this.paleta[clusteri] = paleta[indexmin];
}
}
/**
* Retorna la imagen transformada por kmeans a 'k' colores (max) de la paleta
*/
public int[] getkImagep() {
int[] ret=new int[this.tamanyo];
for( int i=0; i<this.tamanyo; i++) ret[i]= 0xFF000000 | this.paleta[this.asignacion[i]];
return ret;
}
/**
* Retorna la paleta que usa la imagen transformada con el kmeans (k colores de los 11)
*/
public int[] getPaleta() {
return this.paleta;
}
} // final de la calse kmeans
detecta.java:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
class detecta extends Frame {
Image imagenFuente; // Imagen cargada del disco
int iniAncho;
int iniAlto;
// Imagen modificada
// Valores del borde para el objeto contenedor
int insetArriba;
int insetIzqda;
// Método de control del programa
public static void main( String[] args ) {
// Se instancia un objeto de esta clase
detecta obj = new detecta(args);
}
// Constructor de la clase
public detecta(String[] args) {
kmeans kluster;
int k = Integer.parseInt(args[1]);
boolean debug=false;
if( args.length > 2 ) debug = true;
// Se carga la imagen desde el fichero que se indique, que se
// supone situado en el directorio actual del disco duro
imagenFuente = Toolkit.getDefaultToolkit().getImage( args[0] );
// Se utilzia un objeto MediaTracker para bloquear la tarea hasta
// que la imagen se haya cargado o hayan transcurrido 10 segundos
// desde que se inicia la carga
MediaTracker tracker = new MediaTracker( this );
tracker.addImage( imagenFuente,1 );
try {
if( !tracker.waitForID( 1,10000 ) ) {
System.out.println( "Error en la carga de la imagen" );
System.exit( 1 );
}
} catch( InterruptedException e ) {
System.out.println( e );
}
// La imagen ya está cargada, así que se establece la anchura y
// altura de la ventana, para que sus dimensiones se adecúen a la
// imagen
iniAncho = imagenFuente.getWidth( this );
iniAlto = imagenFuente.getHeight( this );
// Se hace visible el Frame
this.setVisible( true );
//Get and store inset data for the Frame object so
// that it can be easily avoided.
insetArriba = this.getInsets().top;
insetIzqda = this.getInsets().left;
// Se usan las dimensiones de insets y el tamaño de la imagen
// fuente para establecer el tamaño total del Frame. La altura se
// hace doble, para que se puedan presentar la imagen original y
// debajo de ella, la imagen modificada. Exactamente no es doble,
// para permitir que la imagen modificada se superponga un poco
// sobre la original.
this.setSize( insetIzqda+iniAncho,insetArriba+iniAlto );
this.setTitle( "IA1 - Practica 1" );
this.setBackground( Color.black );
// Se declara un array para guardar la representación de la imagen
// en pixels individuales
int[] pix = new int[iniAncho * iniAlto];
// Se convierte la "imagenFuente" a representación numérica que
// corresponde a sus pixels, de forma que se puedan manipular
// Esto hay que colocarlo en un bloque try-catch, porque tenemos
// que estar prevenidos par recoger las excepciones de tipo
// "InterrruptedException" que puede lanzar el método grabPixels()
try {
// Se instancia un objeto de tipo PixelGrabber, pasándole como
// parámetro el array de pixels en donde queremos guardar la
// representación numérica de la imagen que vamos manipular
PixelGrabber pgObj = new PixelGrabber( imagenFuente,
0,0,iniAncho,iniAlto,pix,0,iniAncho );
// Se invoca ahora el método grabPixels() sobre el objeto de tipo
// PixelGrabber que se acaba de instanciar, para la imagen se
// convierta en un array de pixels. también se comprueba que el
// proceso se realiza satisfactoriamente
if( pgObj.grabPixels() &&
( (pgObj.getStatus() & ImageObserver.ALLBITS ) != 0 ) ) {
for( int i=0; i < (iniAncho*iniAlto); i++ ) {
pix[i] = pix[i] & 0xFFFFFFFF;
}
}
else {
System.out.println( "Problemas al descomponer la imagen" );
}
} catch( InterruptedException e ) {
System.out.println( e );
}
/**
* Constructor kmeans: kmeans( k, img, tamanyo, debug)
* k = numero de clases a encontrar
* img = pixeles de la imagen en un int[]
* tamanyo = tamanyo de la imagen en pixeles, igual que tamanyo de img
* debug = bool, activa el debug
*/
kluster=new kmeans(k,pix,(iniAncho*iniAlto),debug);
int[] colores=kluster.getClusters();
if(debug) for( int i=0; i < k; i++) System.out.println("Color k-means " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );
// Ahora se utiliza el método createImage() para obtener una nueva
// imagen a partir del array de pixels que hemos alterado
imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImage(),0,iniAncho ) );
// Se llama directamente al método que va a presentar la imagen
this.repaint();
/**
* Ahora adaptamos la imágen a la nueva paleta, la del ejercicio.
*/
int[] paleta = new int[11];
paleta[0] = Color.white.getRGB() & 0x00FFFFFF;
paleta[1] = Color.gray.getRGB() & 0x00FFFFFF;
paleta[2] = Color.black.getRGB() & 0x00FFFFFF;
paleta[3] = Color.red.getRGB() & 0x00FFFFFF;
paleta[4] = Color.green.getRGB() & 0x00FFFFFF;
paleta[5] = Color.blue.getRGB() & 0x00FFFFFF;
paleta[6] = Color.yellow.getRGB() & 0x00FFFFFF;
paleta[7] = Color.orange.getRGB() & 0x00FFFFFF;
paleta[8] = 0xA52A2A; // brown
paleta[9] = 0x800080; // purple
paleta[10] = Color.pink.getRGB() & 0x00FFFFFF;
kluster=new kmeans(k,pix,(iniAncho*iniAlto),false);
kluster.setPaleta(paleta,11);
colores = kluster.getPaleta();
for( int i=0; i<k; i++) System.out.println("Color k-paleta " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );
imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImagep(),0,iniAncho ) );
// Se llama directamente al método que va a presentar la imagen
this.repaint();
System.out.println("----fin-----");
// Clase anidada que permite terminar la ejecución de la animación
this.addWindowListener(
// Definición de la clase anónima para controlar el cierre de
// la ventana
new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
// Se concluye el programa
System.exit( 0 );
}
}
);
}
// Sobrecargamos el método paint() para presentar las dos imágenes
// del ejemplo, la original leida del fichero, y la modificada que se
// ha creado tras la manipulación de los pixels de la original
public void paint( Graphics g ) {
if( imagenFuente != null ) {
g.drawImage( imagenFuente,insetIzqda,insetArriba,this );
}
}
}
import java.awt.event.*;
import java.awt.image.*;
class detecta extends Frame {
Image imagenFuente; // Imagen cargada del disco
int iniAncho;
int iniAlto;
// Imagen modificada
// Valores del borde para el objeto contenedor
int insetArriba;
int insetIzqda;
// Método de control del programa
public static void main( String[] args ) {
// Se instancia un objeto de esta clase
detecta obj = new detecta(args);
}
// Constructor de la clase
public detecta(String[] args) {
kmeans kluster;
int k = Integer.parseInt(args[1]);
boolean debug=false;
if( args.length > 2 ) debug = true;
// Se carga la imagen desde el fichero que se indique, que se
// supone situado en el directorio actual del disco duro
imagenFuente = Toolkit.getDefaultToolkit().getImage( args[0] );
// Se utilzia un objeto MediaTracker para bloquear la tarea hasta
// que la imagen se haya cargado o hayan transcurrido 10 segundos
// desde que se inicia la carga
MediaTracker tracker = new MediaTracker( this );
tracker.addImage( imagenFuente,1 );
try {
if( !tracker.waitForID( 1,10000 ) ) {
System.out.println( "Error en la carga de la imagen" );
System.exit( 1 );
}
} catch( InterruptedException e ) {
System.out.println( e );
}
// La imagen ya está cargada, así que se establece la anchura y
// altura de la ventana, para que sus dimensiones se adecúen a la
// imagen
iniAncho = imagenFuente.getWidth( this );
iniAlto = imagenFuente.getHeight( this );
// Se hace visible el Frame
this.setVisible( true );
//Get and store inset data for the Frame object so
// that it can be easily avoided.
insetArriba = this.getInsets().top;
insetIzqda = this.getInsets().left;
// Se usan las dimensiones de insets y el tamaño de la imagen
// fuente para establecer el tamaño total del Frame. La altura se
// hace doble, para que se puedan presentar la imagen original y
// debajo de ella, la imagen modificada. Exactamente no es doble,
// para permitir que la imagen modificada se superponga un poco
// sobre la original.
this.setSize( insetIzqda+iniAncho,insetArriba+iniAlto );
this.setTitle( "IA1 - Practica 1" );
this.setBackground( Color.black );
// Se declara un array para guardar la representación de la imagen
// en pixels individuales
int[] pix = new int[iniAncho * iniAlto];
// Se convierte la "imagenFuente" a representación numérica que
// corresponde a sus pixels, de forma que se puedan manipular
// Esto hay que colocarlo en un bloque try-catch, porque tenemos
// que estar prevenidos par recoger las excepciones de tipo
// "InterrruptedException" que puede lanzar el método grabPixels()
try {
// Se instancia un objeto de tipo PixelGrabber, pasándole como
// parámetro el array de pixels en donde queremos guardar la
// representación numérica de la imagen que vamos manipular
PixelGrabber pgObj = new PixelGrabber( imagenFuente,
0,0,iniAncho,iniAlto,pix,0,iniAncho );
// Se invoca ahora el método grabPixels() sobre el objeto de tipo
// PixelGrabber que se acaba de instanciar, para la imagen se
// convierta en un array de pixels. también se comprueba que el
// proceso se realiza satisfactoriamente
if( pgObj.grabPixels() &&
( (pgObj.getStatus() & ImageObserver.ALLBITS ) != 0 ) ) {
for( int i=0; i < (iniAncho*iniAlto); i++ ) {
pix[i] = pix[i] & 0xFFFFFFFF;
}
}
else {
System.out.println( "Problemas al descomponer la imagen" );
}
} catch( InterruptedException e ) {
System.out.println( e );
}
/**
* Constructor kmeans: kmeans( k, img, tamanyo, debug)
* k = numero de clases a encontrar
* img = pixeles de la imagen en un int[]
* tamanyo = tamanyo de la imagen en pixeles, igual que tamanyo de img
* debug = bool, activa el debug
*/
kluster=new kmeans(k,pix,(iniAncho*iniAlto),debug);
int[] colores=kluster.getClusters();
if(debug) for( int i=0; i < k; i++) System.out.println("Color k-means " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );
// Ahora se utiliza el método createImage() para obtener una nueva
// imagen a partir del array de pixels que hemos alterado
imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImage(),0,iniAncho ) );
// Se llama directamente al método que va a presentar la imagen
this.repaint();
/**
* Ahora adaptamos la imágen a la nueva paleta, la del ejercicio.
*/
int[] paleta = new int[11];
paleta[0] = Color.white.getRGB() & 0x00FFFFFF;
paleta[1] = Color.gray.getRGB() & 0x00FFFFFF;
paleta[2] = Color.black.getRGB() & 0x00FFFFFF;
paleta[3] = Color.red.getRGB() & 0x00FFFFFF;
paleta[4] = Color.green.getRGB() & 0x00FFFFFF;
paleta[5] = Color.blue.getRGB() & 0x00FFFFFF;
paleta[6] = Color.yellow.getRGB() & 0x00FFFFFF;
paleta[7] = Color.orange.getRGB() & 0x00FFFFFF;
paleta[8] = 0xA52A2A; // brown
paleta[9] = 0x800080; // purple
paleta[10] = Color.pink.getRGB() & 0x00FFFFFF;
kluster=new kmeans(k,pix,(iniAncho*iniAlto),false);
kluster.setPaleta(paleta,11);
colores = kluster.getPaleta();
for( int i=0; i<k; i++) System.out.println("Color k-paleta " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );
imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImagep(),0,iniAncho ) );
// Se llama directamente al método que va a presentar la imagen
this.repaint();
System.out.println("----fin-----");
// Clase anidada que permite terminar la ejecución de la animación
this.addWindowListener(
// Definición de la clase anónima para controlar el cierre de
// la ventana
new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
// Se concluye el programa
System.exit( 0 );
}
}
);
}
// Sobrecargamos el método paint() para presentar las dos imágenes
// del ejemplo, la original leida del fichero, y la modificada que se
// ha creado tras la manipulación de los pixels de la original
public void paint( Graphics g ) {
if( imagenFuente != null ) {
g.drawImage( imagenFuente,insetIzqda,insetArriba,this );
}
}
}
Editado por Daniel el 22/11/2007 a las 06:21:57h.
Editado por Daniel el 18/12/2007 a las 11:28:11h.