cish.CISH.java Source code

Java tutorial

Introduction

Here is the source code for cish.CISH.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cish;

import TMARKERPluginInterface.PluginManager;
import com.boxysystems.jgoogleanalytics.FocusPoint;
import com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker;
import ij.process.ImageProcessor;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.table.DefaultTableModel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.DefaultXYDataset;
import plugins.TMARKERPluginManager;
import tmarker.TMAspot.TMApoint;
import tmarker.TMAspot.TMAspot;
import tmarker.misc.Misc;
import tmarker.misc.StringToIntConverter;
import weka.classifiers.Classifier;
import weka.classifiers.functions.LibSVM;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.LibSVMLoader;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.NumericToNominal;

/**
 *
 * @author Peter J. Schueffler
 */
public class CISH extends javax.swing.JFrame implements TMARKERPluginInterface.Pluggable {

    // For Plugin handling
    TMARKERPluginManager manager = null;
    private static final String PLUGINNAME = "ISHProfiler";
    private static final String PLUGINVERSION = "1."
            + java.util.ResourceBundle.getBundle("cish/Bundle").getString("build");
    private CISH_Thread thread = null;

    /**
     * 5 classes of Colors for CISH Points
     */
    final static int[][] COLORS = new int[][] { new int[] { 255, 0, 0 }, new int[] { 0, 0, 0 },
            new int[] { 230, 230, 230 }, new int[] { 0, 0, 255 }, new int[] { 0, 255, 0 } };

    LibSVM classifier = null;
    Instances dataset = null;
    StringToIntConverter stic = new StringToIntConverter();
    List<TMAspot> tss = new ArrayList<>();
    double[] gRatios;
    double[] lRatios;
    int[][][] ps;

    /**
     * Creates new form CISH
     */
    public CISH() {
        initComponents();
        try {
            this.setIconImage(ImageIO.read((getClass().getResource("/cish/icon.png"))));
        } catch (IOException ex) {
            Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
        }
        initComponents2();
        drawRatioPlot();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        buttonGroup1 = new javax.swing.ButtonGroup();
        jButton1 = new javax.swing.JButton();
        jTextField1 = new javax.swing.JTextField();
        jTextField2 = new javax.swing.JTextField();
        jSlider1 = new javax.swing.JSlider();
        jSlider2 = new javax.swing.JSlider();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        jCheckBox1 = new javax.swing.JCheckBox();
        jCheckBox2 = new javax.swing.JCheckBox();
        jScrollPane1 = new javax.swing.JScrollPane();
        jXTable1 = new org.jdesktop.swingx.JXTable();
        jPanel2 = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();

        setTitle(PLUGINNAME + " v1." + java.util.ResourceBundle.getBundle("cish/Bundle").getString("build")); // NOI18N
        getContentPane().setLayout(new java.awt.GridBagLayout());

        jButton1.setText("CISH Analysis");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 6;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5);
        getContentPane().add(jButton1, gridBagConstraints);

        jTextField1.setColumns(4);
        jTextField1.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
        jTextField1.setText("3");
        jTextField1.setEnabled(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipadx = 32;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5);
        getContentPane().add(jTextField1, gridBagConstraints);

        jTextField2.setColumns(4);
        jTextField2.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
        jTextField2.setText("300");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipadx = 32;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5);
        getContentPane().add(jTextField2, gridBagConstraints);

        jSlider1.setMajorTickSpacing(5);
        jSlider1.setMaximum(20);
        jSlider1.setMinorTickSpacing(1);
        jSlider1.setPaintLabels(true);
        jSlider1.setPaintTicks(true);
        jSlider1.setSnapToTicks(true);
        jSlider1.setEnabled(false);

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
                org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jTextField1,
                org.jdesktop.beansbinding.ELProperty.create("${text}"), jSlider1,
                org.jdesktop.beansbinding.BeanProperty.create("value"), "jSlider1Binding");
        binding.setSourceNullValue(0);
        binding.setSourceUnreadableValue(0);
        binding.setConverter(stic);
        bindingGroup.addBinding(binding);

        jSlider1.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                jSlider1StateChanged(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.ipadx = 164;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
        getContentPane().add(jSlider1, gridBagConstraints);

        jSlider2.setMajorTickSpacing(50);
        jSlider2.setMaximum(400);
        jSlider2.setMinorTickSpacing(5);
        jSlider2.setPaintLabels(true);
        jSlider2.setPaintTicks(true);
        jSlider2.setSnapToTicks(true);

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
                org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jTextField2,
                org.jdesktop.beansbinding.ELProperty.create("${text}"), jSlider2,
                org.jdesktop.beansbinding.BeanProperty.create("value"), "jSlider2Binding");
        binding.setSourceNullValue(0);
        binding.setSourceUnreadableValue(0);
        binding.setConverter(stic);
        bindingGroup.addBinding(binding);

        jSlider2.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                jSlider2StateChanged(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.ipadx = 164;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
        getContentPane().add(jSlider2, gridBagConstraints);

        jLabel1.setText("Point / Signal Radius: (at the moment only trained and tested on points with radius 3)");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5);
        getContentPane().add(jLabel1, gridBagConstraints);

        jLabel2.setText("Number Random Points for Local Ratio:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5);
        getContentPane().add(jLabel2, gridBagConstraints);

        jPanel1.setLayout(new java.awt.BorderLayout());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 9;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(10, 0, 5, 5);
        getContentPane().add(jPanel1, gridBagConstraints);

        jCheckBox1.setSelected(true);
        jCheckBox1.setText("Display Results");
        jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBox1ActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
        getContentPane().add(jCheckBox1, gridBagConstraints);

        jCheckBox2.setSelected(true);
        jCheckBox2.setText("Selected to find black points, instead of white points.");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
        getContentPane().add(jCheckBox2, gridBagConstraints);

        jScrollPane1.setPreferredSize(new java.awt.Dimension(377, 150));

        jXTable1.setModel(new javax.swing.table.DefaultTableModel(new Object[][] {

        }, new String[] { "Image", "# cep", "# gene", "global ratio", "local ratio" }) {
            Class[] types = new Class[] { java.lang.String.class, java.lang.Integer.class, java.lang.Integer.class,
                    java.lang.Double.class, java.lang.Double.class };
            boolean[] canEdit = new boolean[] { false, false, false, false, false };

            public Class getColumnClass(int columnIndex) {
                return types[columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit[columnIndex];
            }
        });
        jXTable1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jXTable1MouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(jXTable1);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(10, 5, 5, 5);
        getContentPane().add(jScrollPane1, gridBagConstraints);

        jPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 10, 5));

        jLabel3.setText("centromer");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
                org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, jCheckBox1,
                org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel3,
                org.jdesktop.beansbinding.BeanProperty.create("enabled"), "jLabel3Binding");
        bindingGroup.addBinding(binding);

        jPanel2.add(jLabel3);

        jLabel4.setText("gene");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
                org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, jCheckBox1,
                org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel4,
                org.jdesktop.beansbinding.BeanProperty.create("enabled"), "jLabel4Binding");
        bindingGroup.addBinding(binding);

        jPanel2.add(jLabel4);

        jLabel5.setText("both");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
                org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, jCheckBox1,
                org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel5,
                org.jdesktop.beansbinding.BeanProperty.create("enabled"), "jLabel5Binding");
        bindingGroup.addBinding(binding);

        jPanel2.add(jLabel5);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 5, 0, 0);
        getContentPane().add(jPanel2, gridBagConstraints);

        jLabel6.setText(" ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(2, 12, 0, 5);
        getContentPane().add(jLabel6, gridBagConstraints);

        bindingGroup.bind();

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jSlider1StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSlider1StateChanged
        jSlider1.setToolTipText(Integer.toString(jSlider1.getValue()));
    }//GEN-LAST:event_jSlider1StateChanged

    private void jSlider2StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSlider2StateChanged
        jSlider2.setToolTipText(Integer.toString(jSlider2.getValue()));
    }//GEN-LAST:event_jSlider2StateChanged

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        doCISH();
        JGoogleAnalyticsTracker tracker = new JGoogleAnalyticsTracker("TMARKER", "UA-61194283-1");
        FocusPoint focusPoint = new FocusPoint("CISHUsage");
        tracker.trackAsynchronously(focusPoint);
    }//GEN-LAST:event_jButton1ActionPerformed

    private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox1ActionPerformed
        manager.repaintVisibleTMAspot();
    }//GEN-LAST:event_jCheckBox1ActionPerformed

    private void jXTable1MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jXTable1MouseClicked
        if (evt.getClickCount() == 2) {
            TMAspot ts = null;
            String selectedName = jXTable1.getStringAt(jXTable1.getSelectedRow(),
                    jXTable1.convertColumnIndexToView(0));
            for (TMAspot ts_ : manager.getTMAspots()) {
                if (ts_.getName().equals(selectedName)) {
                    ts = ts_;
                    break;
                }
            }
            manager.selectAndShowTMAspot(ts);
        }
    }//GEN-LAST:event_jXTable1MouseClicked

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(CISH.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(CISH.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(CISH.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(CISH.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new CISH().setVisible(true);

            }
        });

    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JButton jButton1;
    private javax.swing.JCheckBox jCheckBox1;
    private javax.swing.JCheckBox jCheckBox2;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSlider jSlider1;
    private javax.swing.JSlider jSlider2;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JTextField jTextField2;
    private org.jdesktop.swingx.JXTable jXTable1;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables

    @Override
    public String getAuthor() {
        return "Qing Zhong, Peter J. Schffler";
    }

    @Override
    public String getVersion() {
        return PLUGINVERSION;
    }

    @Override
    public boolean start() {
        return true;
    }

    @Override
    public boolean stop() {
        this.setVisible(false);
        return true;
    }

    @Override
    public void setPluginManager(PluginManager manager) {
        this.manager = (TMARKERPluginManager) manager;
    }

    @Override
    public Icon getIcon() {
        return new ImageIcon(this.getIconImage());
    }

    @Override
    public String getPluginName() {
        return PLUGINNAME;
    }

    @Override
    public void actionPerformed(ActionEvent evt) {
        updateOptionsToTMAspot(manager.getVisibleTMAspot(), manager.getSelectedTMAspots());
        this.setVisible(true);
        if (classifier == null) {
            trainClassifier();
        }
    }

    @Override
    public void setParameterDefaults() {
        setParam_PointSignalRadius(3);
        setParam_nPts(300);
        setParam_darkpoints(true);
    }

    @Override
    public void setParameters(Properties parameters) {
        String value;
        value = parameters.getProperty("PSR");
        if (value != null) {
            setParam_PointSignalRadius(Integer.parseInt(value));
        }
        value = parameters.getProperty("nPts");
        if (value != null) {
            setParam_nPts(Integer.parseInt(value));
        }
        value = parameters.getProperty("darkpoints");
        if (value != null) {
            setParam_darkpoints(Boolean.parseBoolean(value));
        }
    }

    @Override
    public Properties getParameters() {
        Properties parameters = new Properties();
        parameters.setProperty("PSR", Integer.toString(getParam_PointSignalRadius()));
        parameters.setProperty("nPts", Integer.toString(getParam_nPts()));
        parameters.setProperty("darkpoints", Boolean.toString(getParam_darkpoints()));
        return parameters;
    }

    @Override
    public String getHTMLReport(String HTMLFolderName) {
        String output = "<html>";
        String lb = "\n";

        if (!tss.isEmpty()) {

            // Save the parameters
            output += "<table><tr>" + lb + "  <td colspan=2><i><u>CISH Parameters</u></i></td>" + lb + " </tr>" + lb
                    + " <tr>" + lb + "  <td><b>Point Radius:</b></td>" + lb + "  <td>"
                    + getParam_PointSignalRadius() + "</td>" + lb + " </tr>" + lb + " <tr>" + lb
                    + "  <td><b>Number of Random Points for Local Ratio:</b></td>" + lb + "  <td>" + getParam_nPts()
                    + "</td>" + lb + " </tr>" + lb + " <tr>" + lb + "  <td><b>Polarity:</b></td>" + lb + "  <td>"
                    + (getParam_darkpoints() ? "Detect Dark Points" : "Detect Light Points") + "</td>" + lb
                    + " </tr>" + lb;
            output += "</table><br>" + lb;

            // Save the CISH Table
            output += "<table>" + lb + "  <tr>" + lb;
            for (int i = 0; i < jXTable1.getColumnCount(); i++) {
                output += "    <th>" + jXTable1.getColumnName(i) + "</th>" + lb;
            }
            output += "  </tr>" + lb;
            for (int i = 0; i < jXTable1.getRowCount(); i++) {
                output += "  <tr>" + lb;
                for (int j = 0; j < jXTable1.getColumnCount(); j++) {
                    output += "    <td>" + jXTable1.getStringAt(i, j) + "</td>" + lb;
                }
                output += "  </tr>" + lb;
            }
            output += "</table>" + lb;

            // Save the CISH plot image
            ChartPanel chartpanel = (ChartPanel) jPanel1.getComponent(0);
            if (chartpanel != null) {
                File file_tmp = new File(HTMLFolderName + "CISHPlot.png");
                try {
                    ChartUtilities.saveChartAsPNG(file_tmp, chartpanel.getChart(), chartpanel.getWidth(),
                            chartpanel.getHeight());
                } catch (IOException ex) {
                    Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
                }
                output += "<br><br>" + lb;
                output += "<img src=\"" + HTMLFolderName
                        + "CISHPlot.png\" alt=\"Global vs. Local Ratio Plot\"><br><br>" + lb;
            }

            // Save the TMA spot images with CISH marks
            for (TMAspot ts : tss) {
                try {
                    File file_tmp = new File(HTMLFolderName + ts.getName() + "_CISH.jpg");
                    BufferedImage bi = ts.getBufferedImage();
                    drawInformationPreNuclei(ts, bi.createGraphics(), 1, 0, 0, bi.getWidth(), bi.getHeight());
                    ImageIO.write(bi, "jpg", file_tmp);
                    output += "<a href=\"" + HTMLFolderName + ts.getName() + "_CISH.jpg\">" + "<img src=\""
                            + HTMLFolderName + ts.getName() + "_CISH.jpg\" alt=\"" + ts.getName()
                            + " CISH Points\" width=\"100\">" + "</a>&nbsp;&nbsp;&nbsp;" + lb;
                } catch (IOException ex) {
                    Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            output += "<br>" + "<font color=\"" + Misc.Color2HTML(COLORS[0]) + "\"><b>centromer</b></font> "
                    + "<font color=\"" + Misc.Color2HTML(COLORS[1]) + "\"><b>gene</b></font> " + "<font color=\""
                    + Misc.Color2HTML(COLORS[4]) + "\"><b>both</b></font> " + "<br><br>" + lb;
        }

        output += "</html>";

        return output;
    }

    @Override
    public void updateOptionsToTMAspot(TMAspot visible_TMAspot, List<TMAspot> selected_TMAspots) {
    }

    @Override
    public void drawInformationPreNuclei(TMAspot ts, Graphics g, double z, int x_min, int y_min, int x_max,
            int y_max) {
        if (tss != null && !tss.isEmpty() && jCheckBox1.isSelected()) {
            int i = tss.indexOf(ts);
            if (i >= 0) {
                double x, y;
                int r = getParam_PointSignalRadius();
                for (int[] coords : ps[i]) {
                    x = coords[0];
                    y = coords[1];
                    if (x >= x_min && y >= y_min && x < x_max && y < y_max) {
                        g.setColor(new Color(coords[2], coords[3], coords[4]));
                        g.drawRect((int) ((x - r) * z), (int) ((y - r) * z), (int) (2 * r * z), (int) (2 * r * z));
                    }
                }
            }
        }
    }

    @Override
    public void drawInformationPostNuclei(TMAspot ts, Graphics g, double z, int x_min, int y_min, int x_max,
            int y_max) {
    }

    @Override
    public BufferedImage showAlternativeImage(TMAspot ts) {
        return null;
    }

    @Override
    public void TMAspotMouseClicked(TMAspot ts, TMApoint tp, MouseEvent evt) {

    }

    /**
     * Inits some icons.
     */
    private void initComponents2() {
        BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bi.createGraphics();
        g.setColor(new Color(COLORS[0][0], COLORS[0][1], COLORS[0][2]));
        g.drawRect(0, 0, 7, 7);
        jLabel3.setIcon(new ImageIcon(bi));

        bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
        g = bi.createGraphics();
        g.setColor(new Color(COLORS[1][0], COLORS[1][1], COLORS[1][2]));
        g.drawRect(0, 0, 7, 7);
        jLabel4.setIcon(new ImageIcon(bi));

        bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
        g = bi.createGraphics();
        g.setColor(new Color(COLORS[4][0], COLORS[4][1], COLORS[4][2]));
        g.drawRect(0, 0, 7, 7);
        jLabel5.setIcon(new ImageIcon(bi));
    }

    /**
     * Returns the point / signal radius used for ratio calculation.
     * @return The point / signal radius used for ratio calculation (Default=3).
     */
    int getParam_PointSignalRadius() {
        return jSlider1.getValue();
    }

    /**
     * Sets the point / signal radius used for ratio calculation.
     * @param PSR The point / signal radius used for ratio calculation (Default=3).
     */
    void setParam_PointSignalRadius(int PSR) {
        jSlider1.setValue(PSR);
    }

    /**
     * Returns the number of points used for local ratio calculation.
     * @return The number of points for local ratio calculation (Default=300).
     */
    int getParam_nPts() {
        return jSlider2.getValue();
    }

    /**
     * Sets the number of points used for local ratio calculation.
     * @param nPts The number of points for local ratio calculation (Default=300).
     */
    void setParam_nPts(int nPts) {
        jSlider2.setValue(nPts);
    }

    /**
     * Returns whether black points on light background are found or white points on dark background.
     * @return True, if black points on light bg are found (Default=true).
     */
    boolean getParam_darkpoints() {
        return jCheckBox2.isSelected();
    }

    /**
     * Sets whether black points on light background are found or white points on dark background.
     * @param b True, if black points on light bg are found (Default=true).
     */
    void setParam_darkpoints(boolean b) {
        jCheckBox2.setSelected(b);
    }

    /**
     * Performs the CISH analysis on currently selected TMAspots.
     */
    void doCISH() {
        this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        this.tss = manager.getSelectedTMAspots();
        this.gRatios = new double[tss.size()];
        this.lRatios = new double[tss.size()];
        this.ps = new int[tss.size()][][];

        if (!tss.isEmpty()) {
            if (thread != null) {
                if (thread.isAlive()) {
                    thread.interrupt();
                }
            }
            thread = new CISH_Thread((TMARKERPluginManager) manager, this, this.tss, getParam_PointSignalRadius(),
                    getParam_nPts(), getParam_darkpoints(), gRatios, lRatios, ps, classifier, dataset);
            thread.start();
        }

        this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }

    /**
    * Updates the CISH table in this plugin window.
    */
    void updateCISHTable() {

        DefaultTableModel tm = (DefaultTableModel) jXTable1.getModel();
        tm.setRowCount(0);
        for (int i = 0; i < tss.size(); i++) {
            int n_cep = 0;
            int n_gene = 0;
            for (int[] cp : ps[i]) {
                if (cp[2] == COLORS[0][0] && cp[3] == COLORS[0][1] && cp[4] == COLORS[0][2]) {
                    n_cep++;
                } else if (cp[2] == COLORS[1][0] && cp[3] == COLORS[1][1] && cp[4] == COLORS[1][2]) {
                    n_gene++;
                } else if (cp[2] == COLORS[4][0] && cp[3] == COLORS[4][1] && cp[4] == COLORS[4][2]) {
                    n_cep++;
                    n_gene++;
                }
            }
            tm.addRow(new Object[] { tss.get(i).getName(), n_cep, n_gene, gRatios[i], lRatios[i] });
        }
    }

    /**
     * Draws the global / local ratio plot in this Plugin window.
     */
    void drawRatioPlot() {
        //Set the chartpanel
        DefaultXYDataset dataset = new DefaultXYDataset();
        if (lRatios != null) {
            for (int i = 0; i < lRatios.length; i++) {
                if (gRatios[i] != Double.POSITIVE_INFINITY && lRatios[i] != Double.POSITIVE_INFINITY) {
                    dataset.addSeries(tss.get(i).getName(),
                            new double[][] { new double[] { gRatios[i] }, new double[] { lRatios[i] } });
                }
            }
        }

        JFreeChart chart = ChartFactory.createScatterPlot("CISH Ratio", "Global Ratio", "Local Ratio", dataset,
                PlotOrientation.VERTICAL, true, true, false);
        //chart.getXYPlot().getDomainAxis().setRange(0, 1);
        //chart.getXYPlot().getRangeAxis().setRange(0, 1);
        chart.setBackgroundPaint(null);
        chart.getPlot().setBackgroundPaint(null);
        ChartPanel chartPanel = new ChartPanel(chart);
        setPrecisionRecallPlot(chartPanel);
    }

    /**
     * Sets the given chartpanel on the right position of this dialog window. If the position is already occupied by an old chartpanel, the old chartpanel will be removed.
     * @param chartpanel The chartpanel to be drawn.
     */
    void setPrecisionRecallPlot(ChartPanel chartpanel) {
        if (((java.awt.BorderLayout) (jPanel1.getLayout()))
                .getLayoutComponent(java.awt.BorderLayout.CENTER) != null) {
            jPanel1.remove(((java.awt.BorderLayout) (jPanel1.getLayout()))
                    .getLayoutComponent(java.awt.BorderLayout.CENTER));
        }
        chartpanel.setPreferredSize(new Dimension(jButton1.getPreferredSize().width, 300));
        jPanel1.add(chartpanel, java.awt.BorderLayout.CENTER);
        validate();
        pack();
    }

    /*
    Trains the libsvm classifier to distinguish CISH points. It is trained on a
    fixed CISH dataset given by Qing.
    */
    private void trainClassifier() {
        try {
            LibSVMLoader loader = new LibSVMLoader();
            loader.setSource(getClass().getResource("/cish/traindata.libsvm"));
            Instances traindata = loader.getDataSet();

            // Set the class attribute as nominal
            NumericToNominal filter = new NumericToNominal();
            filter.setAttributeIndices("last");
            filter.setInputFormat(traindata);
            dataset = Filter.useFilter(traindata, filter);

            // Train the LibSVM
            classifier = new LibSVM();
            classifier.setOptions(new String[] { "-C", "8", "-G", "0.0625" });

            System.out.println("CISH classifier has options");
            for (String o : classifier.getOptions()) {
                System.out.print(o + " ");
            }
            System.out.println();

            classifier.buildClassifier(dataset);

        } catch (IOException ex) {
            Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Returns the (only) centromer points (cep), (only) gene points and the points which are classified as 
     * centromer and gene (whenever the gene is too close to the centromer).
     * @param ip The image as image processor on which points are classified.
     * @param circles The points which are classified into the three classes cep, gene and both.
     * @param r A radius r for feature extraction around each point.
     * @param classifier A trained classifier used for classification.
     * @param dataset A dataset used for classification (technically necessary).
     * @return Three lists are returned: ceps, a n x 5 list for n centromer points, each with x coord, y coord, R G and B color value;
     * genes, a m x 5 list for m gene points, each with x coord, y coord, R G and B color value;
     * both, a k x 5 list for k "both" points, each with x coord, y coord, R G and B color value;
     */
    static List<List> getPoints(ImageProcessor ip, Point[] circles, int r, Classifier classifier,
            Instances dataset) {
        List<int[]> ceps = new ArrayList<>();
        List<int[]> genes = new ArrayList<>();
        List<int[]> both = new ArrayList<>();

        int x, y;
        Instance inst;
        double c;
        for (Point p : circles) {
            try {
                x = p.x;
                y = p.y;

                double[] attValues = new double[(2 * r + 1) * (2 * r + 1) * 3 + 1];
                int k = 0;
                int[] rgb = new int[3];
                for (int n = 0; n < 3; n++) {
                    for (int i = x - r; i <= x + r; i++) {
                        for (int j = y - r; j <= y + r; j++) {
                            ip.getPixel(i, j, rgb);
                            attValues[k] = rgb[n] / 255.0;
                            k++;
                        }
                    }
                }
                inst = new Instance(1, attValues);
                inst.setDataset(dataset);
                c = classifier.classifyInstance(inst);
                int[] entry = Misc.concat(new int[] { x, y }, COLORS[(int) c]);
                switch ((int) c) {
                case 0:
                    ceps.add(entry);
                    break;
                case 1:
                    genes.add(entry);
                    break;
                case 4:
                    both.add(entry);
                    break;
                default:
                    break;
                }
            } catch (Exception ex) {
                //Logger.getLogger(CISH.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        List<List> out = new ArrayList<>();
        out.add(ceps);
        out.add(genes);
        out.add(both);
        return out;
    }

    /**
     * Writes the progress numbers and estimated time according to total number 
     * of instances, already processed number of instances and process start time
     * to a JLabel. If total is 0, " " is written (making the progress inforamtion
     * invisible). If startTimeMillis > 0, the estimated time for the remaining
     * instances is added.
     * @param processed Processed number of instances.
     * @param total Total number of instances (if 0, " " will be written).
     * @param startTimeMillis The starting time of the process.
     */
    void setProgressNumber(int processed, int total, long startTimeMillis) {
        if (processed <= 0 || total <= 0) {
            jLabel6.setText(" ");
        } else {
            String text = "Processed  " + processed + "/" + total + "  (" + 100 * processed / total + " %)";
            if (startTimeMillis > 0) {
                long time = (total - processed) * (System.currentTimeMillis() - startTimeMillis) / processed;
                text += "    (est. " + String.format("%d min, %d sec", TimeUnit.MILLISECONDS.toMinutes(time),
                        TimeUnit.MILLISECONDS.toSeconds(time)
                                - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time)))
                        + ")";
            }
            jLabel6.setText(text);
        }
    }

}