es.ubu.XRayDetector.modelo.ventana.VentanaAbstracta.java Source code

Java tutorial

Introduction

Here is the source code for es.ubu.XRayDetector.modelo.ventana.VentanaAbstracta.java

Source

/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * VentanaAbstracta.java
 * Copyright (C) 2013 Joaqun Bravo Panadero and Adrin Gonzlez Duarte. Spain
 */

package es.ubu.XRayDetector.modelo.ventana;

import ij.ImagePlus;
import ij.gui.Roi;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JProgressBar;

import es.ubu.XRayDetector.datos.GestorArff;
import es.ubu.XRayDetector.modelo.feature.Feature;
import es.ubu.XRayDetector.modelo.feature.Haralick;
import es.ubu.XRayDetector.modelo.feature.Lbp;
import es.ubu.XRayDetector.modelo.feature.Standard;
import es.ubu.XRayDetector.utils.Differentials_;
import es.ubu.XRayDetector.utils.Propiedades;

import weka.classifiers.AbstractClassifier;
import weka.classifiers.Classifier;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;

/**
 * Superclass of the <i>Strategy</i> pattern that controls the types of windows.
 * @author <a href="mailto:jbp0023@alu.ubu.es"> Joaqun Bravo Panadero </a>
 * @author <a href="mailto:agd0048@alu.ubu.es"> Adrin Gonzalez Duarte </a>
 * 
 * @see Thread
 * @version 1.0
 */
public abstract class VentanaAbstracta extends Thread {

    /**
     * Height of the window.
     */
    private int altura;

    /**
     * Width of the window.
     */
    private int anchura;

    /**
     * Number of thread.
     */
    private int getNumHilo;

    /**
     * Original image.
     */
    private ImagePlus img;

    /**
     * Saliency map image.
     */
    private ImagePlus saliency;

    /**
     * Complete image.
     */
    private ImagePlus imgCompleta;

    /**
     * Image with an additional margin to perform the convloution operation.
     */
    private ImagePlus imgConvolucion;

    /**
     * Saliency map image with an additional margin to perform the convloution operation.
     */
    private ImagePlus imgConvolucionSaliency;

    /**
     * Strings containing the names of the grades (Haralick).
     */
    protected String grades[] = { "0 degrees", "90 degrees", "180 degrees", "270 degrees" };

    /**
     * Array with the values of the means (Haralick).
     */
    protected double[] meanVector;

    /**
     * Array with the values of the ranges (Haralick).
     */
    protected double[] rangeVector;

    /**
     * Array with the values of the means (Saliency, Haralick).
     */
    protected double[] meanVectorSaliency;

    /**
     * Array with the values of the ranges (Saliency, Haralick).
     */
    protected double[] rangeVectorSaliency;

    /**
     * Standard features.
     */
    protected Feature ftStandard;

    /**
     * Haralick features.
     */
    protected Feature ftHaralick;

    /**
     * LBP features.
     */
    protected Feature ftLbp;

    /**
     * Standard features, saliency map.
     */
    protected Feature ftStandardSaliency;

    /**
     * Haralick features, saliency.
     */
    protected Feature ftHaralickSaliency;

    /**
     * LBP features, saliency.
     */
    protected Feature ftLbpSaliency;

    /**
     * LBP values.
     */
    protected double[] lbp;

    /**
     * LBP values, saliency.
     */
    protected double[] lbpSaliency;

    /**
     * Means values.
     */
    protected double means[];

    /**
     * Ranges values.
     */
    protected double ranges[];

    /**
     * Means values, saliency.
     */
    protected double meansSaliency[];

    /**
     * Ranges values, saliency.
     */
    protected double rangesSaliency[];

    /**
     * Values for 0 degrees.
     */
    protected double vector0[] = null;

    /**
     * Values for 90 degrees.
     */
    protected double vector90[] = null;

    /**
     * Values for 180 degrees.
     */
    protected double vector180[] = null;

    /**
     * Values for 170 degrees.
     */
    protected double vector270[] = null;

    /**
     * Values for 0 degrees, saliency.
     */
    protected double vector0sal[] = null;

    /**
     * Values for 90 degrees, saliency.
     */
    protected double vector90sal[] = null;

    /**
     * Values for 180 degrees, saliency.
     */
    protected double vector180sal[] = null;

    /**
     * Values for 270 degrees, saliency.
     */
    protected double vector270sal[] = null;

    /**
     * Flag that indicates if the process has been initialized as normal.
     */
    protected boolean initializedNormal = false;

    /**
     * ProgressBar where the process progress is going to be written.
     */
    protected JProgressBar progressBar;

    /**
     * Matrix where the defects are going to be stored.
     */
    protected int[][] defectMatrix;

    /**
     * Application properties.
     */
    private static Propiedades prop;

    /**
     * Class constructor.
     * @param img Original image
     * @param saliency Saliency map image
     * @param convolucion Image to perform the convolution process
     * @param convolucionSaliency salicny image to perform the convolution process
     * @param numHilo Number of this thread
     */
    public VentanaAbstracta(ImagePlus img, ImagePlus saliency, ImagePlus convolucion, ImagePlus convolucionSaliency,
            int numHilo) {
        this.img = img;
        this.getNumHilo = numHilo;
        this.saliency = saliency;
        this.imgConvolucion = convolucion;
        this.imgConvolucionSaliency = convolucionSaliency;
        prop = Propiedades.getInstance();
        altura = anchura = prop.getTamVentana();
    }

    /* (non-Javadoc)
     * @see java.lang.Thread#run()
     */
    public abstract void run();

    /**
     * Method that retrieves the height of the window.
     * @return Height of the window
     */
    public int getAlturaVentana() {
        return altura;
    }

    /**
     * Method that retrieves the width of the window.
     * @return Width of the window
     */
    public int getAnchuraVentana() {
        return anchura;
    }

    /**
     * Method that retrieves the number of this thread.
     * @return Number of thread
     * @see #setNumHilo
     */
    public int getNumHilo() {
        return getNumHilo;
    }

    /**
     * Method that sets the height of the window.
     * @param alt Height of the window
     */
    public void setAltura(int alt) {
        altura = alt;
    }

    /**
     * Method that sets the width of the window.
     * @param anch Width of the window
     */
    public void setAnchura(int anch) {
        anchura = anch;
    }

    /**
     * Method that sets the number of this thread.
     * @param n Number of thread
     * @see #getNumHilo
     */
    public void setNumHilo(int n) {
        getNumHilo = n;
    }

    /**
     * Method that retrieves the image.
     * @return Image
     * @see #setImage
     */
    public ImagePlus getImage() {
        return img;
    }

    /**
     * Method that sets the image.
     * @param im Image
     * @see #getImage
     */
    public void setImage(ImagePlus im) {
        img = im;
    }

    /**
     * Method that sets the complete image.
     * @param im Complete image
     * @see #getImagenCompleta
     */
    public void setImagenCompleta(ImagePlus im) {
        imgCompleta = im;
    }

    /**
     * Method that retrieves the complete image.
     * @return Complete image
     * @see #setImagenCompleta
     */
    public ImagePlus getImagenCompleta() {
        return imgCompleta;
    }

    /**
     * Method that sets the saliency map image.
     * @param im Saliency map image
     * @see #getSaliency
     */
    public void setSaliency(ImagePlus im) {
        saliency = im;
    }

    /**
     * Method that retrieves the saliency map image.
     * @return Saliency map image
     * @see #setSaliency
     */
    public ImagePlus getSaliency() {
        return saliency;
    }

    /**
     * Method that sets the image to perform the convolution process.
     * @param im Image
     * @see #getConvolucion
     */
    public void setConvolucion(ImagePlus im) {
        imgConvolucion = im;
    }

    /**
     * Method that retrieves the image to perform the convolution process.
     * @return Image
     * @see #setConvolucion
     */
    public ImagePlus getConvolucion() {
        return imgConvolucion;
    }

    /**
     * Method that sets the saliency map image to perform the convolution process.
     * @param im Saliency map image
     * @see #getConvolucionSaliency
     */
    public void setConvolucionSaliency(ImagePlus im) {
        imgConvolucionSaliency = im;
    }

    /**
     * Method that retrieves the saliency map image to perform the convolution process.
     * @return Saliency map image
     * @see #setConvolucionSaliency
     */
    public ImagePlus getConvolucionSaliency() {
        return imgConvolucionSaliency;
    }

    /**
     * Method that retrieves the properties instance.
     * @return Properties instance
     */
    public Propiedades getPropiedades() {
        return prop;
    }

    /**
     * Calculation of all the Haralick features, saliency map.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     * @param step Selected step
     * @param w Step
     */
    public void calcularHaralickSaliency(int coordenadaX, int coordenadaY, int step, int w) {
        ftHaralickSaliency = new Haralick(grades[w], step);
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftHaralickSaliency.calcular(roi, getSaliency(), null, null);
    }

    /**
     * Calculation of all the Haralick features.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     * @param step Selected step
     * @param w step
     * @param imagen Image
     */
    public void calcularHaralick(int coordenadaX, int coordenadaY, int step, int w, ImagePlus imagen) {
        ftHaralick = new Haralick(grades[w], step);
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftHaralick.calcular(roi, imagen, null, null);
    }

    /**
     * Calculation of all LBP features, saliency map.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     */
    public void calcularLbpSaliency(int coordenadaX, int coordenadaY) {
        ftLbpSaliency = new Lbp();
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftLbpSaliency.calcular(roi, getSaliency(), null, null);
        lbpSaliency = ftLbpSaliency.getVectorResultados();
    }

    /**
     * Calculation of all LBP features.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     * @param imagen Image
     */
    public void calcularLbp(int coordenadaX, int coordenadaY, ImagePlus imagen) {
        ftLbp = new Lbp();
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftLbp.calcular(roi, imagen, null, null);
        lbp = ftLbp.getVectorResultados();
    }

    /**
     * Calculation of all Standard features, saliency map.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     */
    public void calcularStandardSaliency(int coordenadaX, int coordenadaY) {
        ImagePlus copiaStandardSaliency = getConvolucionSaliency().duplicate();
        ftStandardSaliency = new Standard();
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftStandardSaliency.calcular(roi, getSaliency(), getImageFd(copiaStandardSaliency),
                getImageSd(copiaStandardSaliency));
    }

    /**
     * Calculation of all Standard features.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     * @param imagen Image
     */
    public void calcularStandard(int coordenadaX, int coordenadaY, ImagePlus imagen) {
        ImagePlus copiaStandard = getConvolucion().duplicate();
        ftStandard = new Standard();
        Roi roi = new Roi(coordenadaX, coordenadaY, getAnchuraVentana(), getAlturaVentana());
        ftStandard.calcular(roi, imagen, getImageFd(copiaStandard), getImageSd(copiaStandard));
    }

    /**
     * Calculation of the first derivative filter.
     * @param image Image
     * @return Filtered image
     */
    public ImagePlus getImageFd(ImagePlus image) {
        //NUEVA VERSIN: USANDO DIFFERENTIALS_
        Differentials_ diff = new Differentials_();
        diff.setImp(image.duplicate());
        Differentials_.setOperation(Differentials_.GRADIENT_MAGNITUDE);
        diff.run("");
        return diff.getImp();
    }

    /**
     * Calculation of the second derivative.
     * @param image Image
     * @return Filtered image
     */
    public ImagePlus getImageSd(ImagePlus image) {
        //NUEVA VERSIN: USANDO DIFFERENTIALS_
        Differentials_ diff = new Differentials_();
        diff.setImp(image.duplicate());
        Differentials_.setOperation(Differentials_.LAPLACIAN);
        diff.run("");
        return diff.getImp();
    }

    /**
     * This method calculates the mean of each box from four vectors.
     * 
     * @param vector0
     *            Vector with the features for the 0 grades
     * @param vector90
     *            Vector with the features for the 90 grades
     * @param vector180
     *            Vector with the features for the 180 grades
     * @param vector270
     *            Vector with the features for the 027 grades
     * @return vector with the mean
     */
    public double[] calculateMean(double[] vector0, double[] vector90, double[] vector180, double[] vector270) {
        double[] mean = new double[vector0.length];

        for (int i = 0; i < vector0.length; i++) {
            mean[i] = (vector0[i] + vector90[i] + vector180[i] + vector270[i]) / 4;
        }

        return mean;
    }

    /**
     * Calculates the range.
     * 
     * @param vector0
     *            Vector with the features for the 0 grades
     * @param vector90
     *            Vector with the features for the 90 grades
     * @param vector180
     *            Vector with the features for the 180 grades
     * @param vector270
     *            Vector with the features for the 027 grades
     * @return vector with the range
     */
    public double[] calculateRange(double[] vector0, double[] vector90, double[] vector180, double[] vector270) {
        double[] range = new double[vector0.length];
        double[] compareVector = new double[4];
        double max;

        for (int i = 0; i < vector0.length; i++) {
            compareVector[0] = Math.abs(vector0[i]);
            compareVector[1] = Math.abs(vector90[i]);
            compareVector[2] = Math.abs(vector180[i]);
            compareVector[3] = Math.abs(vector270[i]);
            max = -2000;

            for (int j = 0; j < compareVector.length; j++) {
                if (compareVector[j] > max) {
                    max = compareVector[j];
                }
            }
            range[i] = max;
        }
        return range;
    }

    /**
     * Method that opens a model.
     * @return Classifier contained in a .model file
     */
    protected Classifier abrirModelo() {
        URL url = null;
        File model = new File(getPropiedades().getPathModel());
        try {
            url = model.toURI().toURL();
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
        ObjectInputStream file = null;
        try {
            file = new ObjectInputStream(url.openStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Classifier classifier = null;
        try {
            classifier = (AbstractClassifier) file.readObject();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            file.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return classifier;
    }

    /**
     * Method that creates an instance which can be classified by Weka.
     * @return Instance with the features values
     */
    protected Instance crearInstancia() {
        double newVals[] = new double[407];
        int count = 0;

        if (ftStandard != null) {
            for (int i = 0; i < ftStandard.getVectorResultados().length; i++) {
                newVals[count] = ftStandard.getVectorResultados()[i];
                count++;
            }
        }

        if (ftStandardSaliency != null) {
            for (int i = 0; i < ftStandardSaliency.getVectorResultados().length; i++) {
                newVals[count] = ftStandardSaliency.getVectorResultados()[i];
                count++;
            }
        }

        if (meanVector != null) {
            for (int i = 0; i < meanVector.length; i++) {
                newVals[count] = meanVector[i];
                count++;
            }
        }

        if (rangeVector != null) {
            for (int i = 0; i < rangeVector.length; i++) {
                newVals[count] = rangeVector[i];
                count++;
            }
        }

        if (meanVectorSaliency != null) {
            for (int i = 0; i < meanVectorSaliency.length; i++) {
                newVals[count] = meanVectorSaliency[i];
                count++;
            }
        }

        if (rangeVectorSaliency != null) {
            for (int i = 0; i < rangeVectorSaliency.length; i++) {
                newVals[count] = rangeVectorSaliency[i];
                count++;
            }
        }

        if (ftLbp != null) {
            for (int i = 0; i < ftLbp.getVectorResultados().length; i++) {
                newVals[count] = ftLbp.getVectorResultados()[i];
                count++;
            }
        }

        if (ftLbpSaliency != null) {
            for (int i = 0; i < ftLbpSaliency.getVectorResultados().length; i++) {
                newVals[count] = ftLbpSaliency.getVectorResultados()[i];
                count++;
            }
        }
        // newVals es el vector de doubles donde tienes los datos de las medias etc.
        Instance instance = new DenseInstance(1, newVals);
        List<String> feat;
        if (prop.getTipoCaracteristicas() == 0) { //todas
            feat = null;
        } else { //mejores
            feat = obtainFeatures();
        }
        instance.setDataset(getHeader(feat));
        return instance;
    }

    /**
     * This method gets the headers of the features.
     * @param features  a List of features
     * @return header with features headers
     */
    public Instances getHeader(List<String> features) {
        int capacity = 100000;

        List<String> featuresCopy = null;
        ArrayList<Attribute> atts = new ArrayList<Attribute>();
        ArrayList<String> defect = new ArrayList<String>();

        defect.add("true");
        defect.add("false");

        if (features != null) {
            featuresCopy = new ArrayList<String>(features);

            for (int i = 0; i < featuresCopy.size(); i++) {
                String rest = featuresCopy.get(i).substring(1);
                char first = featuresCopy.get(i).charAt(0);
                first = Character.toLowerCase(first);
                featuresCopy.set(i, (first + rest).replaceAll(" ", ""));
            }
        }

        for (int j = 0; j < ftStandard.getHead().length; j++) {
            if (features == null || featuresCopy.contains(ftStandard.getHead()[j]))
                atts.add(new Attribute(ftStandard.getHead()[j]));
        }

        for (int j = 0; j < ftStandardSaliency.getHead().length; j++) {
            if (features == null || featuresCopy.contains(ftStandard.getHead()[j] + "(S)"))
                atts.add(new Attribute(ftStandardSaliency.getHead()[j] + "(S)"));
        }

        for (int j = 1; j < 6; j++) {
            for (int i = 0; i < ftHaralick.getHead().length; i++) {
                if (features == null || featuresCopy.contains(ftHaralick.getHead()[i]))
                    atts.add(new Attribute(ftHaralick.getHead()[i] + "_mean" + j));
            }
        }

        for (int j = 1; j < 6; j++) {
            for (int i = 0; i < ftHaralick.getHead().length; i++) {
                if (features == null || featuresCopy.contains(ftHaralick.getHead()[i]))
                    atts.add(new Attribute(ftHaralick.getHead()[i] + "_range" + j));
            }
        }

        for (int j = 1; j < 6; j++) {
            for (int i = 0; i < ftHaralickSaliency.getHead().length; i++) {
                if (features == null || featuresCopy.contains(ftHaralick.getHead()[i] + "(S)"))
                    atts.add(new Attribute(ftHaralickSaliency.getHead()[i] + "_mean" + j + "(S)"));
            }
        }

        for (int j = 1; j < 6; j++) {
            for (int i = 0; i < ftHaralickSaliency.getHead().length; i++) {
                if (features == null || featuresCopy.contains(ftHaralick.getHead()[i] + "(S)"))
                    atts.add(new Attribute(ftHaralickSaliency.getHead()[i] + "_range" + j + "(S)"));
            }
        }

        for (int j = 1; j < 60; j++) {
            if (features == null || featuresCopy.contains(ftLbp.getHead() + "_" + j))
                atts.add(new Attribute(ftLbp.getHead() + "(" + j + ")"));
        }

        for (int j = 1; j < 60; j++) {
            if (features == null || featuresCopy.contains(ftLbpSaliency.getHead() + "_" + j + "(S)"))
                atts.add(new Attribute(ftLbpSaliency.getHead() + "(" + j + ")(S)"));
        }

        atts.add(new Attribute("Defecto", defect));

        // Capacidad es el nmero de instancias.
        Instances header = new Instances("NuevaInstancia", atts, capacity);
        // Establecer la clase
        header.setClassIndex(header.numAttributes() - 1);

        return header;
    }

    /**
     * Creates a list of best features.
     * 
     * @return list with the features
     */
    public List<String> obtainFeatures() {
        List<String> features = new ArrayList<String>();
        String[] bestFeatures = null;

        int tam = prop.getTamVentana();
        switch (tam) {
        case 12:
            if (prop.getTipoVentanaDefectuosa() == 0) { //ventana
                bestFeatures = new String[] { "mean", "firstDerivative", "secondDerivative(S)",
                        "differenceVariance_mean1", "imc_1_mean1", "correlation_mean2", "differenceVariance_mean2",
                        "imc_1_mean4", "correlation_mean5", "correlation_range1", "inverseDifferenceMoment_range1",
                        "sumVariance_range1", "contrast_range2", "inverseDifferenceMoment_range2",
                        "inverseDifferenceMoment_range3", "inverseDifferenceMoment_range4", "imc_1_range4",
                        "contrast_range5", "sumOfSquares_range5", "inverseDifferenceMoment_range5",
                        "differenceEntropy_mean1(S)", "sumEntropy_mean2(S)", "differenceEntropy_mean2(S)",
                        "differenceEntropy_mean3(S)", "LBP(7)", "LBP(30)", "LBP(32)", "LBP(38)", "LBP(31)(S)",
                        "LBP(41)(S)" };
            } else { //vecinos
                bestFeatures = new String[] { "standardDeviation", "differenceVariance_mean1", "correlation_mean2",
                        "sumEntropy_mean2", "correlation_mean4", "inverseDifferenceMoment_mean4",
                        "correlation_mean5", "sumOfSquares_mean5", "correlation_range1",
                        "inverseDifferenceMoment_range2", "inverseDifferenceMoment_range3",
                        "angularSecondMoment_range4", "sumOfSquares_range4", "differenceVariance_range4",
                        "sumOfSquares_range5", "inverseDifferenceMoment_range5", "differenceEntropy_range5",
                        "imc_2_range5", "correlation_mean2(S)", "differenceEntropy_mean2(S)",
                        "inverseDifferenceMoment_mean5(S)", "LBP(37)", "LBP(26)(S)", "LBP(27)(S)", "LBP(40)(S)",
                        "LBP(44)(S)", "LBP(47)(S)", "LBP(52)(S)", "LBP(55)(S)" };
            }
            break;

        case 16:
            if (prop.getTipoVentanaDefectuosa() == 0) { //ventana
                bestFeatures = new String[] { "firstDerivative", "secondDerivative", "differenceVariance_mean1",
                        "imc_1_mean1", "correlation_mean2", "differenceEntropy_mean2", "imc_1_mean2",
                        "sumAverage_mean5", "inverseDifferenceMoment_range1", "inverseDifferenceMoment_range2",
                        "inverseDifferenceMoment_range4", "sumVariance_range4", "differenceEntropy_mean1(S)",
                        "inverseDifferenceMoment_mean5(S)", "differenceEntropy_range1(S)",
                        "differenceEntropy_range4(S)", "LBP(4)", "LBP(6)", "LBP(19)", "LBP(30)", "LBP(32)",
                        "LBP(44)", "LBP(15)(S)", "LBP(34)(S)" };
            } else { //vecinos
                bestFeatures = new String[] { "standardDeviation", "imc_1_mean1", "sumAverage_mean2", "imc_2_mean3",
                        "differenceVariance_mean4", "imc_2_mean4", "angularSecondMoment_range1",
                        "correlation_range1", "inverseDifferenceMoment_range1", "inverseDifferenceMoment_range2",
                        "inverseDifferenceMoment_range3", "sumAverage_range3", "sumOfSquares_range4",
                        "inverseDifferenceMoment_range4", "differenceVariance_range4", "sumOfSquares_range5",
                        "inverseDifferenceMoment_range5", "differenceVariance_range5", "correlation_mean2(S)",
                        "differenceEntropy_mean3(S)", "differenceEntropy_mean4(S)", "angularSecondMoment_mean5(S)",
                        "inverseDifferenceMoment_range4(S)", "LBP(7)", "LBP(48)", "LBP(20)(S)", "LBP(26)(S)",
                        "LBP(27)(S)", "LBP(40)(S)", "LBP(47)(S)", "LBP(52)(S)" };
            }
            break;

        case 24:
            if (prop.getTipoVentanaDefectuosa() == 0) { //ventana
                bestFeatures = new String[] { "firstDerivative", "secondDerivative", "firstDerivative(S)",
                        "secondDerivative(S)", "sumOfSquares_mean1", "differenceVariance_mean1",
                        "inverseDifferenceMoment_mean3", "sumAverage_mean3", "inverseDifferenceMoment_mean4",
                        "contrast_range1", "inverseDifferenceMoment_range1", "inverseDifferenceMoment_range2",
                        "inverseDifferenceMoment_range3", "imc_1_range4", "inverseDifferenceMoment_range5",
                        "sumVariance_range5", "angularSecondMoment_mean5(S)", "differenceEntropy_range1(S)",
                        "LBP(4)", "LBP(7)", "LBP(14)", "LBP(17)", "LBP(18)", "LBP(18)", "LBP(19)", "LBP(20)",
                        "LBP(21)", "LBP(26)", "LBP(32)", "LBP(38)", "LBP(39)", "LBP(44)", "LBP(6)(S)", "LBP(14)(S)",
                        "LBP(17)(S)", "LBP(23)(S)", "LBP(39)(S)", "LBP(40)(S)" };
            } else { //vecinos
                bestFeatures = new String[] { "firstDerivative", "correlation_mean1", "imc_1_mean3",
                        "differenceVariance_mean4", "imc_1_mean4", "sumOfSquares_mean5",
                        "inverseDifferenceMoment_range1", "differenceVariance_range1",
                        "inverseDifferenceMoment_range2", "inverseDifferenceMoment_range3", "sumOfSquares_range4",
                        "inverseDifferenceMoment_range4", "angularSecondMoment_range5",
                        "inverseDifferenceMoment_range5", "sumAverage_range5", "differenceEntropy_mean2(S)",
                        "differenceEntropy_mean3(S)", "angularSecondMoment_mean4(S)",
                        "angularSecondMoment_range5(S)", "LBP(7)", "LBP(10)", "LBP(38)", "LBP(43)", "LBP(20)(S)",
                        "LBP(21)(S)", "LBP(27)(S)", "LBP(29)(S)", "LBP(40)(S)", "LBP(52)(S)", "LBP(59)(S)" };
            }
            break;

        case 32:
            if (prop.getTipoVentanaDefectuosa() == 0) { //ventana
                bestFeatures = new String[] { "firstDerivative", "secondDerivative", "firstDerivative(S)",
                        "secondDerivative(S)", "inverseDifferenceMoment_mean1", "inverseDifferenceMoment_range1",
                        "inverseDifferenceMoment_range4", "imc_1_range4", "sumOfSquares_range5", "LBP(17)",
                        "LBP(18)", "LBP(20)", "LBP(30)", "LBP(34)", "LBP(38)", "LBP(39)", "LBP(8)(S)", "LBP(17)(S)",
                        "LBP(40)(S)" };
            } else { //vecinos
                bestFeatures = new String[] { "secondDerivative", "secondDerivative(S)", "sumOfSquares_mean1",
                        "inverseDifferenceMoment_mean1", "differenceVariance_mean1", "correlation_mean2",
                        "differenceVariance_mean2", "differenceVariance_mean4", "inverseDifferenceMoment_range1",
                        "sumAverage_range1", "imc_1_range1", "inverseDifferenceMoment_range2",
                        "inverseDifferenceMoment_range3", "inverseDifferenceMoment_range4", "imc_1_range4",
                        "inverseDifferenceMoment_range5", "entropy_mean1(S)", "differenceEntropy_mean1(S)",
                        "correlation_mean4(S)", "inverseDifferenceMoment_mean5(S)", "angularSecondMoment_range1(S)",
                        "angularSecondMoment_range2(S)", "angularSecondMoment_range5(S)",
                        "inverseDifferenceMoment_range5(S)", "LBP(7)", "LBP(14)", "LBP(15)", "LBP(38)", "LBP(6)(S)",
                        "LBP(13)(S)", "LBP(14)(S)", "LBP(20)(S)", "LBP(21)(S)", "LBP(34)(S)", "LBP(39)(S)",
                        "LBP(40)(S)", "LBP(52)(S)", "LBP(55)(S)" };
            }
            break;
        }

        features = new ArrayList<String>();

        for (int i = 0; i < bestFeatures.length; i++) {
            features.add(bestFeatures[i]);
        }
        return features;
    }

    /**
     * Methods that sets the percentage of the ProgressBar (synchoronized).
     */
    protected synchronized void setPorcentajeBarra() {
        progressBar.setValue(progressBar.getValue() + 1);
        progressBar.repaint();
    }

    /**
     * Method that increases the amount of windows that indicate that this
     * pixel is faulty.
     * @param coordX X coordinate of the pixel
     * @param coordY Y coordinate of the pixel
     */
    public synchronized void rellenarMatrizDefectos(int coordX, int coordY) {
        for (int i = coordY; i < coordY + getAlturaVentana(); i++) {
            for (int j = coordX; j < coordX + getAnchuraVentana(); j++) {
                defectMatrix[j][i]++;
            }
        }
    }

    /**
     * Method that calculates all the features.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the window
     * @param imagen Image
     */
    public void ejecutarCalculos(int coordenadaX, int coordenadaY, ImagePlus imagen) {
        calcularStandard(coordenadaX, coordenadaY, imagen);
        calcularStandardSaliency(coordenadaX, coordenadaY);
        calcularLbp(coordenadaX, coordenadaY, imagen);
        calcularLbpSaliency(coordenadaX, coordenadaY);

        int total = 0;
        for (int step = 1; step < 6; step++) {
            for (int w = 0; w < 4; w++) {

                calcularHaralick(coordenadaX, coordenadaY, step, w, imagen);
                calcularHaralickSaliency(coordenadaX, coordenadaY, step, w);

                switch (w) {
                case 0:
                    vector0 = ftHaralick.getVectorResultados();
                    vector0sal = ftHaralickSaliency.getVectorResultados();
                    break;
                case 1:
                    vector90 = ftHaralick.getVectorResultados();
                    vector90sal = ftHaralickSaliency.getVectorResultados();
                    break;
                case 2:
                    vector180 = ftHaralick.getVectorResultados();
                    vector180sal = ftHaralickSaliency.getVectorResultados();
                    break;
                case 3:
                    vector270 = ftHaralick.getVectorResultados();
                    vector270sal = ftHaralickSaliency.getVectorResultados();
                    break;
                }

            }

            means = calculateMean(vector0, vector90, vector180, vector270);
            ranges = calculateRange(vector0, vector90, vector180, vector270);

            meansSaliency = calculateMean(vector0sal, vector90sal, vector180sal, vector270sal);
            rangesSaliency = calculateRange(vector0sal, vector90sal, vector180sal, vector270sal);

            if (initializedNormal == false) {
                meanVector = new double[ftHaralick.getVectorResultados().length * 5];
                rangeVector = new double[ftHaralick.getVectorResultados().length * 5];
                meanVectorSaliency = new double[ftHaralickSaliency.getVectorResultados().length * 5];
                rangeVectorSaliency = new double[ftHaralickSaliency.getVectorResultados().length * 5];
                initializedNormal = true;
            }
            for (int k = 0; k < means.length; k++) {
                // Sale un vector que contiene los 5 steps de medias
                meanVector[total] = means[k];
                rangeVector[total] = ranges[k];
                meanVectorSaliency[total] = meansSaliency[k];
                rangeVectorSaliency[total] = rangesSaliency[k];
                total++;
            }
        }
    }

    /**
     * Method that uses a classifier to classifiy an instance.
     * @return Value of the classifier
     */
    public double clasificar() {
        Instance instancia = crearInstancia();
        Classifier clas = abrirModelo();
        double clase = 0;
        try {
            clase = clas.classifyInstance(instancia);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return clase;
    }

    /**
     * Method that creates an ARFF file and writes instances in it.
     * @param coordenadaX X coordinate of the window
     * @param coordenadaY Y coordinate of the 
     * @param defect Value of the defect (class)
     */
    public void generarArff(int coordenadaX, int coordenadaY, boolean defect) {
        String featuresString = generateFeatures(null, defect);
        String headerFile = null;
        if (getNumHilo() == 0) {
            headerFile = getHeader(false);
        }
        GestorArff garff = new GestorArff();
        garff.crearArff(getNumHilo(), featuresString, headerFile);
    }

    /**
     * Method that generates a string of features.
     * @param coordinates  the coordinates of the window
     * @param defect if the window has a defect or not
     * @return string of features
     */
    public String generateFeatures(int[] coordinates, boolean defect) {
        String features = new String();
        if (coordinates != null) {
            features += coordinates[0];
            features += ", ";
            features += coordinates[1];
            features += ", ";
        }

        if (ftStandard != null) {
            for (int i = 0; i < ftStandard.getVectorResultados().length; i++) {
                features += ftStandard.getVectorResultados()[i];
                features += ", ";
            }
        }

        if (ftStandardSaliency != null) {
            for (int i = 0; i < ftStandardSaliency.getVectorResultados().length; i++) {
                features += ftStandardSaliency.getVectorResultados()[i];
                features += ", ";
            }
        }

        if (ftHaralick != null) {
            for (int i = 0; i < meanVector.length; i++) {
                features += meanVector[i];
                features += ", ";
            }

            for (int i = 0; i < rangeVector.length; i++) {
                features += rangeVector[i];
                features += ", ";
            }
        }

        if (ftHaralickSaliency != null) {
            for (int i = 0; i < meanVectorSaliency.length; i++) {
                features += meanVectorSaliency[i];
                features += ", ";
            }

            for (int i = 0; i < rangeVectorSaliency.length; i++) {
                features += rangeVectorSaliency[i];
                features += ", ";
            }
        }

        if (ftLbp != null) {
            for (int i = 0; i < lbp.length; i++) {
                features += lbp[i];
                features += ", ";
            }
        }

        if (ftLbpSaliency != null) {
            for (int i = 0; i < lbpSaliency.length; i++) {
                features += lbpSaliency[i];
                features += ", ";
            }
        }

        int opcionClasificacion = prop.getTipoClasificacion();

        switch (opcionClasificacion) {
        case 0:
            //CLASIFICACIN CLASE NOMINAL (TRUE, FALSE)
            features += defect;
            break;
        case 1:
            //REGRESIN (CLASE 1,0)
            if (defect) {
                features += "1";
            } else {
                features += "0";
            }
            break;
        }
        return features;
    }

    /**
     * Method that generates a header for the file that will store the
     * characteristics.
     * @param coordenadas  if the coordinates have to be included
     * @return string with the header
     */
    public String getHeader(boolean coordenadas) {
        String header = new String();
        header += "% 1. Titulo: Deteccion de defectos en piezas metalicas \n%\n";
        header += "% 2. Fuentes:\n";
        header += "%       (a) Creador: Adrian Gonzalez Duarte\n";
        header += "%       (b) Creador: Joaquin Bravo Panadero\n";
        header += "@relation deteccionDefectos \n";
        if (coordenadas) {
            header += "@attribute coordenadasX INTEGER\n";
            header += "@attribute coordenadasY INTEGER\n";
        }
        header += getStandardAttributes();
        header += getHaralickAttributes();
        header += getLbpAttributes();

        int opcionClasificacion = prop.getTipoClasificacion();

        switch (opcionClasificacion) {
        case 0:
            //CLASIFICACIN CON CLASE NOMINAL
            header += "@ATTRIBUTE class {true, false}\n";
            break;
        case 1:
            //REGRESIN (CLASE 1,0)
            header += "@ATTRIBUTE class numeric\n";
            break;
        }
        header += "@data\n";
        return header;
    }

    /**
     * Method that generates a string with the name and type of each standard
     * attribute. In Weka arff format.
     * 
     * @return string with the information of all attributes
     */
    private String getStandardAttributes() {
        String attributesString = new String();
        if (ftStandard != null) {
            for (int i = 0; i < ftStandard.getHead().length; i++) {
                attributesString += "@attribute " + ftStandard.getHead()[i] + " REAL\n";
            }
        }

        if (ftStandardSaliency != null) {
            for (int i = 0; i < ftStandardSaliency.getHead().length; i++) {
                attributesString += "@attribute " + ftStandardSaliency.getHead()[i] + "(S)" + " REAL\n";
            }
        }
        return attributesString;
    }

    /**
     * Method that generates a string with the name and type of each haralick
     * attribute. In Weka arff format.
     * 
     * @return string with the information of all attributes
     */
    private String getHaralickAttributes() {
        String attributesString = new String();

        if (ftHaralick != null) {
            for (int j = 1; j < 6; j++) {
                for (int i = 0; i < ftHaralick.getHead().length; i++) {
                    attributesString += "@attribute " + ftHaralick.getHead()[i] + "_mean" + j + " REAL\n";
                }
            }

            for (int j = 1; j < 6; j++) {
                for (int i = 0; i < ftHaralick.getHead().length; i++) {
                    attributesString += "@attribute " + ftHaralick.getHead()[i] + "_range" + j + " REAL\n";
                }
            }
        }

        if (ftHaralickSaliency != null) {
            for (int j = 1; j < 6; j++) {
                for (int i = 0; i < ftHaralickSaliency.getHead().length; i++) {
                    attributesString += "@attribute " + ftHaralickSaliency.getHead()[i] + "_mean" + j + "(S)"
                            + " REAL\n";
                }
            }

            for (int j = 1; j < 6; j++) {
                for (int i = 0; i < ftHaralickSaliency.getHead().length; i++) {
                    attributesString += "@attribute " + ftHaralickSaliency.getHead()[i] + "_range" + j + "(S)"
                            + " REAL\n";
                }
            }
        }

        return attributesString;
    }

    /**
     * Method that generates a string with the name and type of each lbp
     * attribute. In Weka arff format.
     * 
     * @return string with the information of all attributes
     */
    private String getLbpAttributes() {
        String attributesString = new String();
        int[] lbpHead = new int[59];

        for (int x = 0; x < 59; x++) {
            lbpHead[x] = x + 1;
        }

        if (lbp != null) {
            for (int i = 0; i < lbpHead.length; i++) {
                attributesString += "@attribute " + ftLbp.getHead()[0] + "(" + lbpHead[i] + ") REAL\n";
            }
        }

        if (lbpSaliency != null) {
            for (int i = 0; i < lbpHead.length; i++) {
                attributesString += "@attribute " + ftLbpSaliency.getHead()[0] + "(" + lbpHead[i] + ")(S) REAL\n";
            }
        }

        return attributesString;
    }

    /**
     * This method checks in the mask if the region of interest analyzed has a
     * colored defect. It returns true if the region has defects. If there is no
     * defects, it returns false.
     * 
     * @param img
     *            mask with colored defects
     * @return true if it has defects, false if not
     */
    protected boolean getDefecto(ImagePlus img) {

        int heuristica = prop.getTipoVentanaDefectuosa();

        switch (heuristica) {
        case 0:
            return porcentajeVentanaMal(img);
        case 1:
            return porcentajeVecinosMal(img);
        }

        return false;
    }

    /**
     * One of the heuristics that determines when a window is faulty.
     * The window is faulty if a percentage of the pixels in the window
     * are faulty.
     * @param img Image
     * @return True if this window is faulty, false if not
     */
    public boolean porcentajeVecinosMal(ImagePlus img) {
        int[] defectVector;
        //Pixel Central + Vecinos Malo
        int x = (int) img.getProcessor().getRoi().getCenterX();
        int y = (int) img.getProcessor().getRoi().getCenterY();
        int pixelDef = 0;
        //regin de vecinos de 3x3
        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
                defectVector = img.getPixel(x + j, y + i);
                if ((defectVector[1] != 255 && defectVector[1] != 0)
                        || (defectVector[2] != 255 && defectVector[2] != 0)) {
                    pixelDef++;
                    if (pixelDef > (prop.getPorcentajePixeles() * 9)) { //porcentaje de la regin del centro de la ventana
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * One of the heuristics that determines when a window is faulty.
     * The window is faulty when a percentage of a 3x3 region around
     * the central pixel are faulty.
     * @param img Image
     * @return True if this window is faulty, false if not
     */
    public boolean porcentajeVentanaMal(ImagePlus img) {
        int[] defectVector;
        // Porcentaje de Pixel mal
        int numPixVentana = getAlturaVentana() * getAnchuraVentana();
        int pixelDef = 0;
        for (int y = 0; y < img.getHeight(); y++) {
            for (int x = 0; x < img.getWidth(); x++) {
                defectVector = img.getPixel(x, y);
                // El valor 0 del vector, guarda el valor en escala de grises.
                // Por eso no nos interesa.
                if ((defectVector[1] != 255 && defectVector[1] != 0)
                        || (defectVector[2] != 255 && defectVector[2] != 0)) {
                    pixelDef++;
                    if (pixelDef > (prop.getPorcentajePixeles() * numPixVentana)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

}